FleetManager API Reference
The FleetManager class is the primary entry point for using herdctl programmatically. It provides a simple, high-level API to initialize and run a fleet of agents with minimal configuration.
Installation
Section titled “Installation”npm install @herdctl/core# orpnpm add @herdctl/coreQuick Start
Section titled “Quick Start”import { FleetManager } from '@herdctl/core';
const manager = new FleetManager({ configPath: './herdctl.yaml', stateDir: './.herdctl',});
await manager.initialize();await manager.start();
// Subscribe to eventsmanager.on('job:created', (payload) => { console.log(`Job ${payload.job.id} created for ${payload.agentName}`);});
// Graceful shutdownprocess.on('SIGINT', async () => { await manager.stop(); process.exit(0);});Constructor
Section titled “Constructor”new FleetManager(options)
Section titled “new FleetManager(options)”Creates a new FleetManager instance.
const manager = new FleetManager(options: FleetManagerOptions);Parameters
Section titled “Parameters”| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.stateDir | string | Yes | — | Path to the state directory (e.g., .herdctl). Created if it doesn’t exist. Stores job artifacts, session state, and logs. |
options.configPath | string | No | Auto-discover | Path to herdctl.yaml. Can be absolute, relative, or a directory path. If not provided, searches up from cwd. |
options.logger | FleetManagerLogger | No | Console logger | Custom logger with debug, info, warn, error methods. |
options.checkInterval | number | No | 1000 | Interval in milliseconds between scheduler checks. |
Example
Section titled “Example”import { FleetManager } from '@herdctl/core';
// Minimal configurationconst manager = new FleetManager({ stateDir: './.herdctl',});
// Full configurationconst manager = new FleetManager({ configPath: './config/herdctl.yaml', stateDir: './.herdctl', checkInterval: 5000, // 5 seconds logger: { debug: (msg) => console.debug(`[DEBUG] ${msg}`), info: (msg) => console.info(`[INFO] ${msg}`), warn: (msg) => console.warn(`[WARN] ${msg}`), error: (msg) => console.error(`[ERROR] ${msg}`), },});Lifecycle Methods
Section titled “Lifecycle Methods”initialize()
Section titled “initialize()”Initializes the fleet manager by loading configuration and preparing the state directory.
await manager.initialize(): Promise<void>Description
Section titled “Description”This method:
- Loads and validates the configuration file
- Initializes the state directory structure
- Prepares the scheduler (but does not start it)
After initialization, the fleet manager is ready to start.
Throws
Section titled “Throws”| Error | Condition |
|---|---|
FleetManagerStateError | Already initialized or running |
FleetManagerConfigError | Configuration is invalid or not found |
FleetManagerStateDirError | State directory cannot be created |
Example
Section titled “Example”const manager = new FleetManager({ configPath: './herdctl.yaml', stateDir: './.herdctl',});
try { await manager.initialize(); console.log(`Loaded ${manager.state.agentCount} agents`);} catch (error) { if (error instanceof FleetManagerConfigError) { console.error('Configuration error:', error.message); }}start()
Section titled “start()”Starts the fleet manager scheduler, which begins processing agent schedules.
await manager.start(): Promise<void>Description
Section titled “Description”This begins the scheduler, which will:
- Check agent schedules at the configured interval
- Trigger agents when their schedules are due
- Track schedule state in the state directory
Throws
Section titled “Throws”| Error | Condition |
|---|---|
FleetManagerStateError | Not initialized |
Example
Section titled “Example”await manager.initialize();await manager.start();
// The manager is now running and processing schedulesmanager.on('schedule:triggered', (payload) => { console.log(`Triggered ${payload.agentName}/${payload.scheduleName}`);});stop(options?)
Section titled “stop(options?)”Gracefully stops the fleet manager.
await manager.stop(options?: FleetManagerStopOptions): Promise<void>Parameters
Section titled “Parameters”| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.waitForJobs | boolean | No | true | Wait for running jobs to complete before stopping |
options.timeout | number | No | 30000 | Maximum time in ms to wait for jobs to complete |
options.cancelOnTimeout | boolean | No | false | Cancel jobs that don’t complete within timeout |
options.cancelTimeout | number | No | 10000 | Time in ms for each job to respond to SIGTERM before SIGKILL |
Description
Section titled “Description”This will:
- Signal the scheduler to stop accepting new triggers
- Wait for running jobs to complete (with timeout)
- If timeout is reached and
cancelOnTimeoutis true, cancel remaining jobs - Persist all state before shutdown completes
- Emit
'stopped'event when complete
Throws
Section titled “Throws”| Error | Condition |
|---|---|
FleetManagerShutdownError | Shutdown times out and cancelOnTimeout is false |
Example
Section titled “Example”// Normal shutdown - wait for jobs with default 30s timeoutawait manager.stop();
// Shutdown with custom timeoutawait manager.stop({ timeout: 60000 });
// Shutdown without waiting for jobs (not recommended)await manager.stop({ waitForJobs: false });
// Cancel jobs if they don't complete in timeawait manager.stop({ timeout: 30000, cancelOnTimeout: true, cancelTimeout: 10000,});reload()
Section titled “reload()”Hot-reloads configuration without restarting the fleet.
await manager.reload(): Promise<ConfigReloadedPayload>Returns
Section titled “Returns”interface ConfigReloadedPayload { agentCount: number; // Number of agents in new config agentNames: string[]; // Names of agents in new config configPath: string; // Path to reloaded config file changes: ConfigChange[]; // List of detected changes timestamp: string; // ISO timestamp of reload}
interface ConfigChange { type: 'added' | 'removed' | 'modified'; category: 'agent' | 'schedule' | 'defaults'; name: string; // Agent name or "agent/schedule" details?: string; // What changed (for modifications)}Description
Section titled “Description”This method provides hot configuration reload capability:
- Loads and validates the new configuration
- If validation fails, keeps the old configuration (fails gracefully)
- Running jobs continue with their original configuration
- New jobs will use the new configuration
- Updates the scheduler with new agent definitions
- Emits a
'config:reloaded'event with change details
Throws
Section titled “Throws”| Error | Condition |
|---|---|
InvalidStateError | Fleet manager is not initialized |
FleetManagerConfigError | New configuration is invalid |
Example
Section titled “Example”// Reload configurationconst result = await manager.reload();console.log(`Reloaded with ${result.changes.length} changes`);
for (const change of result.changes) { console.log(` ${change.type} ${change.category}: ${change.name}`);}
// Subscribe to reload eventsmanager.on('config:reloaded', (payload) => { console.log(`Config reloaded: ${payload.agentCount} agents`);});Query Methods
Section titled “Query Methods”getFleetStatus()
Section titled “getFleetStatus()”Returns a comprehensive snapshot of fleet state.
await manager.getFleetStatus(): Promise<FleetStatus>Returns
Section titled “Returns”interface FleetStatus { state: FleetManagerStatus; // Current state uptimeSeconds: number | null; // Time since started initializedAt: string | null; // ISO timestamp startedAt: string | null; // ISO timestamp stoppedAt: string | null; // ISO timestamp counts: FleetCounts; // Summary counts scheduler: { status: 'stopped' | 'running' | 'stopping'; checkCount: number; // Total checks performed triggerCount: number; // Total triggers fired lastCheckAt: string | null; // ISO timestamp checkIntervalMs: number; }; lastError: string | null;}
interface FleetCounts { totalAgents: number; idleAgents: number; runningAgents: number; errorAgents: number; totalSchedules: number; runningSchedules: number; runningJobs: number;}Example
Section titled “Example”const status = await manager.getFleetStatus();console.log(`Fleet: ${status.state}`);console.log(`Uptime: ${status.uptimeSeconds}s`);console.log(`Agents: ${status.counts.totalAgents} total, ${status.counts.runningAgents} running`);console.log(`Jobs: ${status.counts.runningJobs} running`);getAgentInfo()
Section titled “getAgentInfo()”Returns information about all configured agents.
await manager.getAgentInfo(): Promise<AgentInfo[]>Returns
Section titled “Returns”interface AgentInfo { name: string; // Agent local name qualifiedName: string; // Dot-separated qualified name (e.g., "myfleet.agent-name") fleetPath: string[]; // Fleet hierarchy path (empty for root agents) description?: string; // From configuration status: 'idle' | 'running' | 'error'; currentJobId: string | null; // Currently running job lastJobId: string | null; // Last completed job maxConcurrent: number; // Max concurrent instances runningCount: number; // Currently running instances errorMessage: string | null; // Error if status is 'error' scheduleCount: number; // Number of schedules schedules: ScheduleInfo[]; // Schedule details model?: string; // Model from config working_directory?: string; // Working directory path chat?: Record<string, AgentChatStatus>; // Chat connector statuses by platform}
interface AgentChatStatus { configured: boolean; connectionStatus?: 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'disconnecting' | 'error'; botUsername?: string; lastError?: string;}Example
Section titled “Example”const agents = await manager.getAgentInfo();for (const agent of agents) { console.log(`${agent.name}: ${agent.status}`); console.log(` Running: ${agent.runningCount}/${agent.maxConcurrent}`); console.log(` Schedules: ${agent.scheduleCount}`);}getAgentInfoByName(name)
Section titled “getAgentInfoByName(name)”Returns information about a specific agent.
await manager.getAgentInfoByName(name: string): Promise<AgentInfo>Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The agent name to look up |
Throws
Section titled “Throws”| Error | Condition |
|---|---|
AgentNotFoundError | No agent with that name exists |
Example
Section titled “Example”try { const agent = await manager.getAgentInfoByName('my-agent'); console.log(`Status: ${agent.status}`); console.log(`Running: ${agent.runningCount}/${agent.maxConcurrent}`);} catch (error) { if (error instanceof AgentNotFoundError) { console.log(`Agent "${error.agentName}" not found`); console.log(`Available: ${error.availableAgents?.join(', ')}`); }}getSchedules()
Section titled “getSchedules()”Returns all schedules across all agents.
await manager.getSchedules(): Promise<ScheduleInfo[]>Returns
Section titled “Returns”interface ScheduleInfo { name: string; // Schedule name agentName: string; // Owning agent type: string; // 'interval', 'cron', etc. interval?: string; // e.g., "5m", "1h" expression?: string; // Cron expression status: 'idle' | 'running' | 'disabled'; lastRunAt: string | null; // ISO timestamp nextRunAt: string | null; // ISO timestamp lastError: string | null;}Example
Section titled “Example”const schedules = await manager.getSchedules();for (const schedule of schedules) { console.log(`${schedule.agentName}/${schedule.name}: ${schedule.status}`); console.log(` Next run: ${schedule.nextRunAt}`);}getSchedule(agentName, scheduleName)
Section titled “getSchedule(agentName, scheduleName)”Returns a specific schedule by agent and schedule name.
await manager.getSchedule(agentName: string, scheduleName: string): Promise<ScheduleInfo>Throws
Section titled “Throws”| Error | Condition |
|---|---|
AgentNotFoundError | Agent doesn’t exist |
ScheduleNotFoundError | Schedule doesn’t exist for agent |
Example
Section titled “Example”const schedule = await manager.getSchedule('my-agent', 'hourly');console.log(`Status: ${schedule.status}`);console.log(`Last run: ${schedule.lastRunAt}`);console.log(`Next run: ${schedule.nextRunAt}`);enableSchedule(agentName, scheduleName)
Section titled “enableSchedule(agentName, scheduleName)”Enables a previously disabled schedule.
await manager.enableSchedule(agentName: string, scheduleName: string): Promise<ScheduleInfo>Description
Section titled “Description”Enables a schedule that was previously disabled, allowing it to trigger again on its configured interval. The enabled state is persisted and survives restarts.
Example
Section titled “Example”const schedule = await manager.enableSchedule('my-agent', 'hourly');console.log(`Schedule status: ${schedule.status}`); // 'idle'disableSchedule(agentName, scheduleName)
Section titled “disableSchedule(agentName, scheduleName)”Disables a schedule temporarily.
await manager.disableSchedule(agentName: string, scheduleName: string): Promise<ScheduleInfo>Description
Section titled “Description”Disables a schedule, preventing it from triggering on its configured interval. The schedule remains in the configuration but won’t run until re-enabled. The disabled state is persisted and survives restarts.
Example
Section titled “Example”// Disable a schedule temporarilyconst schedule = await manager.disableSchedule('my-agent', 'hourly');console.log(`Schedule status: ${schedule.status}`); // 'disabled'
// Later, re-enable itawait manager.enableSchedule('my-agent', 'hourly');Action Methods
Section titled “Action Methods”trigger(agentName, scheduleName?, options?)
Section titled “trigger(agentName, scheduleName?, options?)”Manually triggers an agent outside its normal schedule.
await manager.trigger( agentName: string, scheduleName?: string, options?: TriggerOptions): Promise<TriggerResult>Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
agentName | string | Yes | Name of the agent to trigger |
scheduleName | string | No | Schedule to use for configuration (prompt, work source) |
options.prompt | string | No | Override the prompt for this trigger |
options.workItems | WorkItem[] | No | Custom work items instead of fetching from work source |
options.bypassConcurrencyLimit | boolean | No | Force trigger even if agent is at capacity (default: false) |
Returns
Section titled “Returns”interface TriggerResult { jobId: string; // Unique job identifier agentName: string; scheduleName: string | null; startedAt: string; // ISO timestamp prompt?: string; // Prompt used for trigger}Throws
Section titled “Throws”| Error | Condition |
|---|---|
InvalidStateError | Fleet manager not initialized |
AgentNotFoundError | Agent doesn’t exist |
ScheduleNotFoundError | Specified schedule doesn’t exist |
ConcurrencyLimitError | Agent at capacity and bypass not enabled |
Example
Section titled “Example”// Trigger with agent defaultsconst job = await manager.trigger('my-agent');console.log(`Created job: ${job.jobId}`);
// Trigger a specific scheduleconst job = await manager.trigger('my-agent', 'hourly');
// Trigger with custom promptconst job = await manager.trigger('my-agent', undefined, { prompt: 'Review the latest security updates',});
// Force trigger even at capacityconst job = await manager.trigger('my-agent', undefined, { bypassConcurrencyLimit: true,});cancelJob(jobId, options?)
Section titled “cancelJob(jobId, options?)”Cancels a running job gracefully.
await manager.cancelJob( jobId: string, options?: { timeout?: number }): Promise<CancelJobResult>Parameters
Section titled “Parameters”| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
jobId | string | Yes | — | ID of the job to cancel |
options.timeout | number | No | 10000 | Time in ms to wait for graceful shutdown before SIGKILL |
Returns
Section titled “Returns”interface CancelJobResult { jobId: string; success: boolean; terminationType: 'graceful' | 'forced' | 'already_stopped'; canceledAt: string; // ISO timestamp}Description
Section titled “Description”Cancels a running job by first sending SIGTERM to allow graceful shutdown. If the job doesn’t terminate within the timeout, it will be forcefully killed with SIGKILL.
Throws
Section titled “Throws”| Error | Condition |
|---|---|
InvalidStateError | Fleet manager not initialized |
JobNotFoundError | Job doesn’t exist |
Example
Section titled “Example”// Cancel with default timeout (10s)const result = await manager.cancelJob('job-2024-01-15-abc123');console.log(`Cancelled: ${result.terminationType}`);
// Cancel with custom timeoutconst result = await manager.cancelJob('job-2024-01-15-abc123', { timeout: 30000, // 30 seconds});forkJob(jobId, modifications?)
Section titled “forkJob(jobId, modifications?)”Creates a new job based on an existing job’s configuration.
await manager.forkJob( jobId: string, modifications?: JobModifications): Promise<ForkJobResult>Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
jobId | string | Yes | ID of the job to fork |
modifications.prompt | string | No | Override the prompt |
modifications.schedule | string | No | Override the schedule name |
modifications.workItems | WorkItem[] | No | Replace work items |
Returns
Section titled “Returns”interface ForkJobResult { jobId: string; // New job ID forkedFromJobId: string; // Original job ID agentName: string; startedAt: string; // ISO timestamp prompt?: string;}Description
Section titled “Description”Creates a new job based on an existing job’s configuration. The new job will have the same agent and can optionally have modifications applied. If the original job has a session ID, the new job will fork from that session, preserving conversation context.
Throws
Section titled “Throws”| Error | Condition |
|---|---|
InvalidStateError | Fleet manager not initialized |
JobNotFoundError | Original job doesn’t exist |
JobForkError | Job cannot be forked |
Example
Section titled “Example”// Fork with same configurationconst result = await manager.forkJob('job-2024-01-15-abc123');console.log(`Forked to: ${result.jobId}`);
// Fork with modified promptconst result = await manager.forkJob('job-2024-01-15-abc123', { prompt: 'Continue the previous task but focus on testing',});
// Fork with different scheduleconst result = await manager.forkJob('job-2024-01-15-abc123', { schedule: 'nightly',});Log Streaming Methods
Section titled “Log Streaming Methods”streamLogs(options?)
Section titled “streamLogs(options?)”Streams all fleet logs as an async iterable.
manager.streamLogs(options?: LogStreamOptions): AsyncIterable<LogEntry>Parameters
Section titled “Parameters”| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.level | LogLevel | No | 'info' | Minimum log level ('debug', 'info', 'warn', 'error') |
options.agentName | string | No | — | Filter to specific agent |
options.jobId | string | No | — | Filter to specific job |
options.includeHistory | boolean | No | true | Replay history before streaming |
options.historyLimit | number | No | 1000 | Max historical entries |
Returns
Section titled “Returns”interface LogEntry { timestamp: string; // ISO timestamp level: 'debug' | 'info' | 'warn' | 'error'; source: 'fleet' | 'agent' | 'job' | 'scheduler'; agentName?: string; jobId?: string; scheduleName?: string; message: string; data?: Record<string, unknown>;}Example
Section titled “Example”// Stream all info+ logsfor await (const log of manager.streamLogs()) { console.log(`[${log.level}] ${log.message}`);}
// Stream only errors for a specific agentfor await (const log of manager.streamLogs({ level: 'error', agentName: 'my-agent',})) { console.error(log.message);}streamJobOutput(jobId)
Section titled “streamJobOutput(jobId)”Streams output from a specific job.
manager.streamJobOutput(jobId: string): AsyncIterable<LogEntry>Description
Section titled “Description”For completed jobs, this will replay the job’s history and then complete. For running jobs, it will continue streaming until the job completes.
Throws
Section titled “Throws”| Error | Condition |
|---|---|
JobNotFoundError | Job doesn’t exist |
Example
Section titled “Example”for await (const log of manager.streamJobOutput('job-2024-01-15-abc123')) { console.log(`[${log.level}] ${log.message}`);}streamAgentLogs(agentName)
Section titled “streamAgentLogs(agentName)”Streams logs for a specific agent.
manager.streamAgentLogs(agentName: string): AsyncIterable<LogEntry>Throws
Section titled “Throws”| Error | Condition |
|---|---|
AgentNotFoundError | Agent doesn’t exist |
Example
Section titled “Example”for await (const log of manager.streamAgentLogs('my-agent')) { console.log(`[${log.jobId}] ${log.message}`);}getJobFinalOutput(jobId)
Section titled “getJobFinalOutput(jobId)”Returns the final output of a completed job as a string.
await manager.getJobFinalOutput(jobId: string): Promise<string>Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
jobId | string | Yes | ID of the job to get output from |
Returns
Section titled “Returns”Returns a Promise<string> containing the job’s final output. Throws JobNotFoundError if the job doesn’t exist.
Example
Section titled “Example”const output = await manager.getJobFinalOutput('job-2024-01-15-abc123');console.log(output);// "I checked the prices for office chairs..."State Accessors
Section titled “State Accessors”Returns the current fleet manager state (read-only property).
manager.state: FleetManagerStateReturns
Section titled “Returns”interface FleetManagerState { status: FleetManagerStatus; initializedAt: string | null; startedAt: string | null; stoppedAt: string | null; agentCount: number; lastError: string | null;}
type FleetManagerStatus = | 'uninitialized' // Initial state | 'initialized' // After initialize() | 'starting' // During start() | 'running' // Scheduler active | 'stopping' // During stop() | 'stopped' // After stop() | 'error'; // Error occurredExample
Section titled “Example”console.log(`Status: ${manager.state.status}`);console.log(`Agents: ${manager.state.agentCount}`);console.log(`Started at: ${manager.state.startedAt}`);getConfig()
Section titled “getConfig()”Returns the loaded configuration.
manager.getConfig(): ResolvedConfig | nullReturns null if not initialized.
getAgents()
Section titled “getAgents()”Returns the loaded agents array.
manager.getAgents(): ResolvedAgent[]Returns empty array if not initialized.
Events
Section titled “Events”The FleetManager extends EventEmitter and provides strongly-typed events.
Event Types
Section titled “Event Types”interface FleetManagerEventMap { // Lifecycle 'initialized': []; 'started': []; 'stopped': []; 'error': [error: Error]; 'config:reloaded': [payload: ConfigReloadedPayload];
// Agent events 'agent:started': [payload: AgentStartedPayload]; 'agent:stopped': [payload: AgentStoppedPayload];
// Schedule events 'schedule:triggered': [payload: ScheduleTriggeredPayload]; 'schedule:skipped': [payload: ScheduleSkippedPayload];
// Job events 'job:created': [payload: JobCreatedPayload]; 'job:output': [payload: JobOutputPayload]; 'job:completed': [payload: JobCompletedPayload]; 'job:failed': [payload: JobFailedPayload]; 'job:cancelled': [payload: JobCancelledPayload]; 'job:forked': [payload: JobForkedPayload];}Event Payloads
Section titled “Event Payloads”interface JobCreatedPayload { job: JobMetadata; agentName: string; scheduleName?: string; timestamp: string;}
interface JobOutputPayload { jobId: string; agentName: string; output: string; outputType: 'stdout' | 'stderr' | 'assistant' | 'tool' | 'system'; timestamp: string;}
interface JobCompletedPayload { job: JobMetadata; agentName: string; exitReason: ExitReason; durationSeconds: number; timestamp: string;}
interface JobFailedPayload { job: JobMetadata; agentName: string; error: Error; exitReason: ExitReason; durationSeconds?: number; timestamp: string;}
interface JobCancelledPayload { job: JobMetadata; agentName: string; terminationType: 'graceful' | 'forced' | 'already_stopped'; durationSeconds?: number; timestamp: string;}
interface JobForkedPayload { job: JobMetadata; originalJob: JobMetadata; agentName: string; timestamp: string;}interface ScheduleTriggeredPayload { agentName: string; scheduleName: string; schedule: Schedule; timestamp: string;}
interface ScheduleSkippedPayload { agentName: string; scheduleName: string; reason: 'already_running' | 'disabled' | 'max_concurrent' | 'work_source_empty'; timestamp: string;}interface ConfigReloadedPayload { agentCount: number; agentNames: string[]; configPath: string; changes: ConfigChange[]; timestamp: string;}
interface ConfigChange { type: 'added' | 'removed' | 'modified'; category: 'agent' | 'schedule' | 'defaults'; name: string; details?: string;}Example: Event Subscription
Section titled “Example: Event Subscription”const manager = new FleetManager({ stateDir: './.herdctl' });
// Lifecycle eventsmanager.on('initialized', () => { console.log('Fleet initialized');});
manager.on('started', () => { console.log('Fleet started');});
manager.on('stopped', () => { console.log('Fleet stopped');});
manager.on('error', (error) => { console.error('Fleet error:', error.message);});
// Job eventsmanager.on('job:created', (payload) => { console.log(`Job ${payload.job.id} created for ${payload.agentName}`);});
manager.on('job:output', (payload) => { process.stdout.write(payload.output);});
manager.on('job:completed', (payload) => { console.log(`Job completed in ${payload.durationSeconds}s`);});
manager.on('job:failed', (payload) => { console.error(`Job failed: ${payload.error.message}`);});
// Schedule eventsmanager.on('schedule:triggered', (payload) => { console.log(`${payload.agentName}/${payload.scheduleName} triggered`);});
manager.on('schedule:skipped', (payload) => { console.log(`${payload.agentName}/${payload.scheduleName} skipped: ${payload.reason}`);});
// Config reloadmanager.on('config:reloaded', (payload) => { console.log(`Config reloaded with ${payload.changes.length} changes`);});Error Classes
Section titled “Error Classes”All errors extend FleetManagerError and include error codes for programmatic handling.
Error Hierarchy
Section titled “Error Hierarchy”FleetManagerError (base)├── ConfigurationError // Config invalid/not found├── AgentNotFoundError // Agent not in config├── JobNotFoundError // Job doesn't exist├── ScheduleNotFoundError // Schedule not configured├── InvalidStateError // Operation invalid for state├── ConcurrencyLimitError // Agent at max capacity├── JobCancelError // Job cancellation failed├── JobForkError // Job fork failed├── FleetManagerConfigError // Legacy config error├── FleetManagerStateError // Legacy state error├── FleetManagerStateDirError // State directory init failed└── FleetManagerShutdownError // Shutdown failedError Codes
Section titled “Error Codes”const FleetManagerErrorCode = { FLEET_MANAGER_ERROR: 'FLEET_MANAGER_ERROR', CONFIGURATION_ERROR: 'CONFIGURATION_ERROR', CONFIG_LOAD_ERROR: 'CONFIG_LOAD_ERROR', AGENT_NOT_FOUND: 'AGENT_NOT_FOUND', JOB_NOT_FOUND: 'JOB_NOT_FOUND', SCHEDULE_NOT_FOUND: 'SCHEDULE_NOT_FOUND', INVALID_STATE: 'INVALID_STATE', STATE_DIR_ERROR: 'STATE_DIR_ERROR', CONCURRENCY_LIMIT: 'CONCURRENCY_LIMIT', SHUTDOWN_ERROR: 'SHUTDOWN_ERROR', JOB_CANCEL_ERROR: 'JOB_CANCEL_ERROR', JOB_FORK_ERROR: 'JOB_FORK_ERROR',};Type Guards
Section titled “Type Guards”import { isFleetManagerError, isConfigurationError, isAgentNotFoundError, isJobNotFoundError, isScheduleNotFoundError, isInvalidStateError, isConcurrencyLimitError, isJobCancelError, isJobForkError,} from '@herdctl/core';
try { await manager.trigger('unknown-agent');} catch (error) { if (isAgentNotFoundError(error)) { console.log(`Agent "${error.agentName}" not found`); console.log(`Available: ${error.availableAgents?.join(', ')}`); } else if (isConcurrencyLimitError(error)) { console.log(`At capacity: ${error.currentJobs}/${error.limit}`); }}Error Properties
Section titled “Error Properties”class AgentNotFoundError extends FleetManagerError { agentName: string; // The agent that wasn't found availableAgents?: string[]; // List of valid agents}class ScheduleNotFoundError extends FleetManagerError { agentName: string; scheduleName: string; // The schedule that wasn't found availableSchedules?: string[]; // List of valid schedules}class ConcurrencyLimitError extends FleetManagerError { agentName: string; currentJobs: number; // Currently running limit: number; // Max allowed
isAtLimit(): boolean; // Helper method}class InvalidStateError extends FleetManagerError { operation: string; // The attempted operation currentState: string; // Current fleet state expectedState: string | string[]; // Required state(s)}Complete Example
Section titled “Complete Example”Here’s a complete example showing typical usage patterns:
import { FleetManager, isAgentNotFoundError } from '@herdctl/core';
async function main() { const manager = new FleetManager({ configPath: './herdctl.yaml', stateDir: './.herdctl', checkInterval: 5000, });
// Set up event handlers manager.on('initialized', () => console.log('Fleet initialized')); manager.on('started', () => console.log('Fleet started')); manager.on('stopped', () => console.log('Fleet stopped'));
manager.on('job:created', (payload) => { console.log(`Job ${payload.job.id} created for ${payload.agentName}`); });
manager.on('job:output', (payload) => { process.stdout.write(payload.output); });
manager.on('job:completed', (payload) => { console.log(`Job ${payload.job.id} completed in ${payload.durationSeconds}s`); });
manager.on('job:failed', (payload) => { console.error(`Job ${payload.job.id} failed: ${payload.error.message}`); });
// Initialize and start await manager.initialize(); await manager.start();
// Get fleet status const status = await manager.getFleetStatus(); console.log(`Running ${status.counts.totalAgents} agents`);
// List all agents const agents = await manager.getAgentInfo(); for (const agent of agents) { console.log(`${agent.name}: ${agent.status}`); }
// Manually trigger an agent try { const result = await manager.trigger('my-agent', 'check-issues', { prompt: 'Check for urgent issues only', }); console.log(`Triggered job: ${result.jobId}`); } catch (error) { if (isAgentNotFoundError(error)) { console.error(`Agent not found: ${error.agentName}`); } }
// Handle shutdown process.on('SIGINT', async () => { console.log('Shutting down...'); await manager.stop({ timeout: 30000, cancelOnTimeout: true, }); process.exit(0); });}
main().catch(console.error);TypeScript Support
Section titled “TypeScript Support”The @herdctl/core package is written in TypeScript and provides full type definitions. All types are exported from the main package:
import type { // Options and configuration FleetManagerOptions, FleetManagerLogger,
// State types FleetManagerState, FleetManagerStatus,
// Query result types FleetStatus, FleetCounts, AgentInfo, ScheduleInfo,
// Action types TriggerOptions, TriggerResult, JobModifications, CancelJobResult, ForkJobResult,
// Stop options FleetManagerStopOptions,
// Log streaming types LogLevel, LogSource, LogEntry, LogStreamOptions,
// Event types FleetManagerEventMap, FleetManagerEventName, ConfigReloadedPayload, ConfigChange, JobCreatedPayload, JobOutputPayload, JobCompletedPayload, JobFailedPayload, JobCancelledPayload, JobForkedPayload, ScheduleTriggeredPayload, ScheduleSkippedPayload,} from '@herdctl/core';