@cascade-flow/backend-filesystem 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/dist/index.d.ts +353 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1381 -0
- package/dist/index.js.map +10 -0
- package/package.json +52 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { mkdir, writeFile, readFile, readdir, access, rename, unlink, open as openFile } from \"node:fs/promises\";\nimport type { FileHandle } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport {\n Backend,\n type StepStartMetadata,\n type StepCompleteMetadata,\n type StepError,\n type StepRecord,\n type StepState,\n type StateChange,\n type LogEntry,\n type Event,\n type StepEvent,\n type WorkflowEvent,\n type StepStartedEvent,\n type StepCompletedEvent,\n type StepFailedEvent,\n type StepSkippedEvent,\n type StepScheduledEvent,\n type StepHeartbeatEvent,\n type StepReclaimedEvent,\n type StepRetryingEvent,\n type LogEntryEvent,\n type WorkflowStartedEvent,\n type WorkflowInputValidationEvent,\n type WorkflowCompletedEvent,\n type WorkflowFailedEvent,\n type WorkflowResumedEvent,\n type WorkflowCancelledEvent,\n type WorkflowRetryStartedEvent,\n type RunSubmittedEvent,\n type RunSubmission,\n type RunState,\n type WorkflowMetadata,\n type WorkflowRegistration,\n type StepDefinition,\n type AnalyticsOptions,\n type ErrorAnalysis,\n type RetryAnalysis,\n type SchedulingLatency,\n type StepDuration,\n type WorkflowDuration,\n type WorkerStability,\n type Throughput,\n type QueueDepth,\n type QueueDepthByWorkflow,\n type SuccessRate,\n type AnalyticsSummary,\n StepRecordSchema,\n RunStateSchema,\n eventSchema,\n stepEventSchema,\n workflowEventSchema,\n safeSerialize,\n projectStepRecord,\n projectStepState,\n projectRunStateFromEvents,\n extractLogsFromEvents,\n getCurrentAttemptNumber,\n getMicrosecondTimestamp,\n computeErrorAnalysis,\n computeRetryAnalysis,\n computeSchedulingLatency,\n computeStepDuration,\n computeWorkflowDuration,\n computeErrorFingerprints,\n computeWorkerStability,\n computeThroughput,\n computeSuccessRate,\n} from \"@cascade-flow/backend-interface\";\n\n/**\n * FileSystem backend implementation\n * Stores workflow execution state in the local filesystem using event sourcing\n *\n * Directory structure:\n * ./.runs/{workflow-slug}/{runId}/workflow-events/{timestamp}-{eventType}.json\n * ./.runs/{workflow-slug}/{runId}/step-events/{timestamp}-{stepId}-{eventType}.json\n */\nexport class FileSystemBackend extends Backend {\n private baseDir: string;\n\n /**\n * Create a new FileSystem backend\n *\n * @param baseDir - Base directory for storing runs (defaults to \"./.runs\")\n */\n constructor(baseDir: string = \"./.runs\") {\n super();\n this.baseDir = baseDir;\n }\n\n /**\n * Initialize the backend (no-op for filesystem - directories created as needed)\n */\n async initialize(): Promise<void> {\n // No initialization needed - directories are created on-demand\n }\n\n /**\n * Get the directory path for a run\n */\n private getRunDir(workflowSlug: string, runId: string): string {\n return join(this.baseDir, workflowSlug, runId);\n }\n\n /**\n * Get the directory path for workflow events within a run\n */\n private getWorkflowEventsDir(workflowSlug: string, runId: string): string {\n return join(this.getRunDir(workflowSlug, runId), \"workflow-events\");\n }\n\n /**\n * Get the directory path for step events within a run\n */\n private getStepEventsDir(workflowSlug: string, runId: string): string {\n return join(this.getRunDir(workflowSlug, runId), \"step-events\");\n }\n\n /**\n * Get the directory path for step outputs within a run\n */\n private getStepOutputsDir(workflowSlug: string, runId: string): string {\n return join(this.getRunDir(workflowSlug, runId), \"step-outputs\");\n }\n\n /**\n * Get the file path for a step's output file\n */\n public getStepOutputPath(workflowSlug: string, runId: string, stepId: string, attemptNumber: number): string {\n return join(this.getStepOutputsDir(workflowSlug, runId), `${stepId}-attempt-${attemptNumber}.json`);\n }\n\n /**\n * Get the directory path for steps within a run\n * @deprecated Old format - kept for reference only\n */\n private getStepsDir(workflowSlug: string, runId: string): string {\n return join(this.getRunDir(workflowSlug, runId), \"steps\");\n }\n\n /**\n * Get the file path for a specific step\n * @deprecated Old format - kept for reference only\n */\n private getStepFile(workflowSlug: string, runId: string, stepName: string): string {\n return join(this.getStepsDir(workflowSlug, runId), `${stepName}.json`);\n }\n\n /**\n * Get the file path for a step's logs\n * @deprecated Old format - kept for reference only\n */\n private getStepLogsFile(workflowSlug: string, runId: string, stepName: string): string {\n return join(this.getStepsDir(workflowSlug, runId), `${stepName}.logs.json`);\n }\n\n /**\n * Get the idempotency directory path (moved to top-level)\n */\n private getIdempotencyDir(): string {\n return join(this.baseDir, \".idempotency\");\n }\n\n /**\n * Generate a unique run ID\n */\n private generateRunId(): string {\n return `run_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;\n }\n\n /**\n * Hash an idempotency key\n */\n private hashIdempotencyKey(key: string): string {\n return createHash(\"sha256\").update(key).digest(\"hex\");\n }\n\n /**\n * Generate a unique event ID using microsecond timestamp\n * Format: {timestamp}\n * Example: 1762916097401523\n *\n * The microsecond timestamp provides ~1000x better precision than milliseconds,\n * making collisions extremely unlikely even with parallel step execution.\n *\n * @param timestamp - Optional timestamp in microseconds (defaults to current time)\n */\n private generateEventId(timestamp?: number): string {\n const ts = timestamp ?? getMicrosecondTimestamp();\n return `${ts}`;\n }\n\n /**\n * Get the filename for a workflow event\n * Format: {timestamp}-{eventType}.json\n * Example: 1762916097401523-WorkflowStarted.json\n */\n private getWorkflowEventFilename(event: WorkflowEvent): string {\n return `${event.eventId}-${event.type}.json`;\n }\n\n /**\n * Get the filename for a step event\n * Format: {timestamp}-{stepId}-{eventType}.json\n * Example: 1762916097401523-ai-step-1-StepStarted.json\n */\n private getStepEventFilename(event: StepEvent): string {\n return `${event.eventId}-${event.stepId}-${event.type}.json`;\n }\n\n /**\n * Get the filesystem lock path used to claim a step attempt\n */\n private getStepClaimLockPath(\n workflowSlug: string,\n runId: string,\n stepId: string,\n attemptNumber: number\n ): string {\n return join(this.getRunDir(workflowSlug, runId), \"locks\", `${stepId}-attempt-${attemptNumber}.lock`);\n }\n\n /**\n * Atomically write JSON data to a file\n * Uses a temporary file and rename for atomicity\n */\n private async writeJsonAtomic(filePath: string, data: unknown): Promise<void> {\n const dir = dirname(filePath);\n await mkdir(dir, { recursive: true });\n\n const jsonString = JSON.stringify(data, null, 2);\n await writeFile(filePath, jsonString, \"utf-8\");\n }\n\n /**\n * Read and validate a step record from disk\n */\n private async readStepRecord(workflowSlug: string, runId: string, stepName: string): Promise<StepRecord | null> {\n try {\n const filePath = this.getStepFile(workflowSlug, runId, stepName);\n const content = await readFile(filePath, \"utf-8\");\n const parsed = JSON.parse(content);\n return StepRecordSchema.parse(parsed);\n } catch {\n return null;\n }\n }\n\n /**\n * Create a state change record\n */\n private createStateChange(\n status: StateChange[\"status\"],\n message?: string\n ): StateChange {\n return {\n status,\n timestamp: getMicrosecondTimestamp(),\n message,\n };\n }\n\n async initializeRun(workflowSlug: string, runId: string): Promise<void> {\n const workflowEventsDir = this.getWorkflowEventsDir(workflowSlug, runId);\n const stepEventsDir = this.getStepEventsDir(workflowSlug, runId);\n await mkdir(workflowEventsDir, { recursive: true });\n await mkdir(stepEventsDir, { recursive: true });\n }\n\n async appendEvent(workflowSlug: string, runId: string, event: Event): Promise<void> {\n // Ensure event has ID and timestamp if not provided\n if (!event.eventId) {\n (event as any).eventId = this.generateEventId();\n }\n if (!event.timestampUs) {\n (event as any).timestampUs = getMicrosecondTimestamp();\n }\n\n // Validate event\n eventSchema.parse(event);\n\n // Determine directory and filename based on event category\n const eventsDir =\n event.category === \"workflow\"\n ? this.getWorkflowEventsDir(workflowSlug, runId)\n : this.getStepEventsDir(workflowSlug, runId);\n\n const getFilename = (evt: Event): string => {\n if (evt.category === \"workflow\") {\n return this.getWorkflowEventFilename(evt as WorkflowEvent);\n } else {\n return this.getStepEventFilename(evt as StepEvent);\n }\n };\n\n await mkdir(eventsDir, { recursive: true });\n\n // Handle timestamp collisions by incrementing the timestamp\n let timestamp = parseInt(event.eventId);\n let filename = getFilename(event);\n let filePath = join(eventsDir, filename);\n\n // Check for collision and increment timestamp until we find a unique one\n while (true) {\n try {\n await access(filePath);\n // File exists, increment timestamp and try again\n timestamp++;\n (event as any).eventId = `${timestamp}`;\n (event as any).timestampUs = timestamp;\n filename = getFilename(event);\n filePath = join(eventsDir, filename);\n } catch {\n // File doesn't exist, we can use this timestamp\n break;\n }\n }\n\n await this.writeJsonAtomic(filePath, event);\n }\n\n async loadEvents(\n workflowSlug: string,\n runId: string,\n options: { category: \"step\"; stepId?: string }\n ): Promise<StepEvent[]>;\n async loadEvents(\n workflowSlug: string,\n runId: string,\n options: { category: \"workflow\" }\n ): Promise<WorkflowEvent[]>;\n async loadEvents(\n workflowSlug: string,\n runId: string,\n options?: { category?: \"workflow\" | \"step\"; stepId?: string }\n ): Promise<Event[]>;\n async loadEvents(\n workflowSlug: string,\n runId: string,\n options?: { category?: \"workflow\" | \"step\"; stepId?: string }\n ): Promise<Event[]> {\n try {\n const events: Event[] = [];\n\n // Determine which directories to read from\n const directories: Array<{ dir: string; category: \"workflow\" | \"step\" }> = [];\n\n if (!options?.category || options.category === \"workflow\") {\n directories.push({\n dir: this.getWorkflowEventsDir(workflowSlug, runId),\n category: \"workflow\",\n });\n }\n\n if (!options?.category || options.category === \"step\") {\n directories.push({\n dir: this.getStepEventsDir(workflowSlug, runId),\n category: \"step\",\n });\n }\n\n // Load events from each directory\n for (const { dir, category } of directories) {\n try {\n const files = await readdir(dir);\n\n // Filter JSON files only\n const eventFiles = files.filter((f) => f.endsWith(\".json\"));\n\n // Load and parse events\n for (const file of eventFiles) {\n const content = await readFile(join(dir, file), \"utf-8\");\n const event = eventSchema.parse(JSON.parse(content));\n\n // Apply filters\n if (category === \"step\" && options?.stepId) {\n // Filter by stepId if provided (only for step events)\n if (event.category === \"step\" && event.stepId === options.stepId) {\n events.push(event);\n }\n } else {\n events.push(event);\n }\n }\n } catch (err) {\n // Directory might not exist yet, continue with other directories\n continue;\n }\n }\n\n // Sort events by timestamp, then by eventId for deterministic ordering\n events.sort((a, b) => {\n if (a.timestampUs !== b.timestampUs) {\n return a.timestampUs - b.timestampUs;\n }\n return a.eventId.localeCompare(b.eventId);\n });\n\n return events;\n } catch {\n return [];\n }\n }\n\n async saveStepStart(\n workflowSlug: string,\n runId: string,\n stepId: string,\n workerId: string,\n metadata: StepStartMetadata\n ): Promise<void> {\n // Get current attempt number by counting previous StepStarted events\n const events = await this.loadEvents(workflowSlug, runId, { category: \"step\", stepId });\n const attemptNumber = getCurrentAttemptNumber(events) + 1;\n\n // Use high-resolution timestamp instead of metadata timestamp\n const timestamp = getMicrosecondTimestamp();\n\n const event: StepStartedEvent = {\n category: \"step\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n stepId,\n type: \"StepStarted\",\n workerId,\n dependencies: metadata.dependencies,\n attemptNumber,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveStepComplete(\n workflowSlug: string,\n runId: string,\n stepId: string,\n output: unknown,\n metadata: StepCompleteMetadata,\n exportOutput: boolean = false\n ): Promise<void> {\n // Get current attempt number\n const events = await this.loadEvents(workflowSlug, runId, { category: \"step\", stepId });\n const attemptNumber = getCurrentAttemptNumber(events);\n\n if (attemptNumber === 0) {\n throw new Error(`Cannot complete step that hasn't started: ${stepId}`);\n }\n\n // Append log events first (if any)\n if (metadata.logs && metadata.logs.length > 0) {\n for (const log of metadata.logs) {\n // Log timestamp is already in microseconds\n const logTimestamp = log.timestamp;\n const logEvent: LogEntryEvent = {\n category: \"step\",\n eventId: this.generateEventId(logTimestamp),\n timestampUs: logTimestamp,\n workflowSlug,\n runId,\n stepId,\n type: \"LogEntry\",\n stream: log.stream,\n message: log.message,\n attemptNumber,\n };\n await this.appendEvent(workflowSlug, runId, logEvent);\n }\n }\n\n // Serialize the output\n const serialized = safeSerialize(output);\n const outputString = serialized.success ? serialized.data : serialized.fallback;\n\n // Use high-resolution timestamp\n const timestamp = getMicrosecondTimestamp();\n\n const event: StepCompletedEvent = {\n category: \"step\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n stepId,\n type: \"StepCompleted\",\n output: outputString,\n durationUs: metadata.duration,\n attemptNumber,\n exportOutput,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveStepFailed(\n workflowSlug: string,\n runId: string,\n stepId: string,\n error: StepError,\n metadata: {\n duration: number;\n attemptNumber: number;\n terminal: boolean;\n nextRetryAt?: number;\n failureReason: \"exhausted-retries\" | \"worker-crash\" | \"timeout\" | \"cancelled\" | \"execution-error\";\n }\n ): Promise<void> {\n const now = getMicrosecondTimestamp();\n\n const event: StepFailedEvent = {\n category: \"step\",\n eventId: this.generateEventId(now),\n timestampUs: now,\n workflowSlug,\n runId,\n stepId,\n type: \"StepFailed\",\n error,\n errorFingerprints: computeErrorFingerprints(error, error.stack),\n durationUs: metadata.duration,\n attemptNumber: metadata.attemptNumber,\n terminal: metadata.terminal,\n nextRetryAtUs: metadata.nextRetryAt,\n failureReason: metadata.failureReason,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveStepFailedAndScheduleRetry(\n workflowSlug: string,\n runId: string,\n stepId: string,\n error: StepError,\n failureMetadata: {\n duration: number;\n attemptNumber: number;\n nextRetryAt: number;\n failureReason: \"execution-error\" | \"timeout\";\n },\n scheduleMetadata: {\n availableAt: number;\n nextAttemptNumber: number;\n retryDelayMs: number;\n maxRetries: number;\n }\n ): Promise<void> {\n // For filesystem backend, we write events sequentially\n // This is less atomic than the Postgres version, but:\n // 1. File system operations are generally reliable\n // 2. The recovery logic will handle any edge cases\n // 3. Each appendEvent is atomic at the file level\n\n // Generate timestamps for all three events\n // We need distinct timestamps to ensure proper event ordering\n const failedTimestamp = getMicrosecondTimestamp();\n const retryingTimestamp = failedTimestamp + 1;\n const scheduledTimestamp = failedTimestamp + 2;\n\n // Event 1: StepFailed (terminal: false)\n const failedEvent: StepFailedEvent = {\n category: \"step\",\n type: \"StepFailed\",\n eventId: this.generateEventId(failedTimestamp),\n timestampUs: failedTimestamp,\n workflowSlug,\n runId,\n stepId,\n error,\n errorFingerprints: computeErrorFingerprints(error, error.stack),\n durationUs: failureMetadata.duration,\n attemptNumber: failureMetadata.attemptNumber,\n terminal: false,\n nextRetryAtUs: failureMetadata.nextRetryAt,\n failureReason: failureMetadata.failureReason,\n };\n\n await this.appendEvent(workflowSlug, runId, failedEvent);\n\n // Event 2: StepRetrying (informational)\n const retryingEvent: StepRetryingEvent = {\n category: \"step\",\n type: \"StepRetrying\",\n eventId: this.generateEventId(retryingTimestamp),\n timestampUs: retryingTimestamp,\n workflowSlug,\n runId,\n stepId,\n attemptNumber: failureMetadata.attemptNumber,\n nextAttempt: scheduleMetadata.nextAttemptNumber,\n maxRetries: scheduleMetadata.maxRetries,\n error,\n };\n\n await this.appendEvent(workflowSlug, runId, retryingEvent);\n\n // Event 3: StepScheduled (for retry)\n const scheduledEvent: StepScheduledEvent = {\n category: \"step\",\n type: \"StepScheduled\",\n eventId: this.generateEventId(scheduledTimestamp),\n timestampUs: scheduledTimestamp,\n workflowSlug,\n runId,\n stepId,\n availableAtUs: scheduleMetadata.availableAt,\n reason: \"retry\",\n attemptNumber: scheduleMetadata.nextAttemptNumber,\n retryDelayMs: scheduleMetadata.retryDelayMs,\n };\n\n await this.appendEvent(workflowSlug, runId, scheduledEvent);\n }\n\n async saveStepSkipped(\n workflowSlug: string,\n runId: string,\n stepId: string,\n metadata: {\n skipType: \"primary\" | \"cascade\";\n reason: string;\n metadata?: Record<string, any>;\n duration: number;\n attemptNumber: number;\n cascadedFrom?: string;\n }\n ): Promise<void> {\n const now = getMicrosecondTimestamp();\n\n const event: StepSkippedEvent = {\n category: \"step\",\n eventId: this.generateEventId(now),\n timestampUs: now,\n workflowSlug,\n runId,\n stepId,\n type: \"StepSkipped\",\n skipType: metadata.skipType,\n reason: metadata.reason,\n metadata: metadata.metadata,\n durationUs: metadata.duration,\n attemptNumber: metadata.attemptNumber,\n cascadedFrom: metadata.cascadedFrom,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveStepScheduled(\n workflowSlug: string,\n runId: string,\n stepId: string,\n metadata: {\n availableAt: number;\n reason: \"initial\" | \"retry\" | \"dependency-satisfied\";\n attemptNumber: number;\n retryDelayMs?: number;\n }\n ): Promise<void> {\n const now = getMicrosecondTimestamp();\n\n const event: StepScheduledEvent = {\n category: \"step\",\n eventId: this.generateEventId(now),\n timestampUs: now,\n workflowSlug,\n runId,\n stepId,\n type: \"StepScheduled\",\n availableAtUs: metadata.availableAt,\n reason: metadata.reason,\n attemptNumber: metadata.attemptNumber,\n retryDelayMs: metadata.retryDelayMs,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveStepHeartbeat(\n workflowSlug: string,\n runId: string,\n stepId: string,\n workerId: string,\n attemptNumber: number\n ): Promise<void> {\n const now = getMicrosecondTimestamp();\n\n const event: StepHeartbeatEvent = {\n category: \"step\",\n eventId: this.generateEventId(now),\n timestampUs: now,\n workflowSlug,\n runId,\n stepId,\n type: \"StepHeartbeat\",\n workerId,\n attemptNumber,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveStepReclaimed(\n workflowSlug: string,\n runId: string,\n stepId: string,\n metadata: {\n originalWorkerId: string;\n reclaimedBy: string;\n lastHeartbeat: number;\n staleThreshold: number;\n staleDuration: number;\n attemptNumber: number;\n }\n ): Promise<void> {\n const now = getMicrosecondTimestamp();\n\n const event: StepReclaimedEvent = {\n category: \"step\",\n eventId: this.generateEventId(now),\n timestampUs: now,\n workflowSlug,\n runId,\n stepId,\n type: \"StepReclaimed\",\n originalWorkerId: metadata.originalWorkerId,\n reclaimedBy: metadata.reclaimedBy,\n lastHeartbeatUs: metadata.lastHeartbeat,\n staleThresholdUs: metadata.staleThreshold,\n staleDurationUs: metadata.staleDuration,\n attemptNumber: metadata.attemptNumber,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveWorkflowComplete(\n workflowSlug: string,\n runId: string,\n output: unknown,\n metadata: { workflowAttemptNumber: number; timestamp: number; duration: number; totalSteps: number }\n ): Promise<void> {\n // Serialize the output\n const serialized = safeSerialize(output);\n const outputString = serialized.success ? serialized.data : serialized.fallback;\n\n // Use high-resolution timestamp\n const timestamp = getMicrosecondTimestamp();\n\n const event: WorkflowCompletedEvent = {\n category: \"workflow\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n type: \"WorkflowCompleted\",\n workflowAttemptNumber: metadata.workflowAttemptNumber,\n output: outputString,\n durationUs: metadata.duration,\n totalSteps: metadata.totalSteps,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveStepLogs(\n workflowSlug: string,\n runId: string,\n stepId: string,\n logs: LogEntry[]\n ): Promise<void> {\n // Logs are now saved as LogEntry events during saveStepComplete\n // This method is kept for backward compatibility but is a no-op\n // The logs are already appended as events in saveStepComplete\n }\n\n async loadStepLogs(\n workflowSlug: string,\n runId: string,\n stepId: string,\n attemptNumber?: number\n ): Promise<LogEntry[] | null> {\n // Load logs from events\n const events = await this.loadEvents(workflowSlug, runId, { category: \"step\", stepId });\n let logs = extractLogsFromEvents(events);\n\n // Filter by attempt number if specified\n if (attemptNumber !== undefined) {\n logs = logs.filter(log => log.attemptNumber === attemptNumber);\n }\n\n return logs.length > 0 ? logs : null;\n }\n\n async loadRun(workflowSlug: string, runId: string): Promise<StepRecord[]> {\n try {\n // Load only step events (workflow events don't represent step execution)\n const events = await this.loadEvents(workflowSlug, runId, { category: \"step\" });\n\n if (events.length === 0) {\n return [];\n }\n\n // Group events by step ID\n const eventsByStep = new Map<string, Event[]>();\n for (const event of events) {\n // Type guard: we know these are step events\n if (event.category === \"step\") {\n if (!eventsByStep.has(event.stepId)) {\n eventsByStep.set(event.stepId, []);\n }\n eventsByStep.get(event.stepId)!.push(event);\n }\n }\n\n // Project each step's events to StepRecord\n const records: StepRecord[] = [];\n for (const [stepId, stepEvents] of eventsByStep) {\n const record = projectStepRecord(stepEvents);\n records.push(record);\n }\n\n return records;\n } catch {\n return [];\n }\n }\n\n async runExists(workflowSlug: string, runId: string): Promise<boolean> {\n try {\n const runDir = this.getRunDir(workflowSlug, runId);\n await access(runDir);\n return true;\n } catch {\n return false;\n }\n }\n\n async saveWorkflowStart(\n workflowSlug: string,\n runId: string,\n metadata: { workflowAttemptNumber: number; hasInputSchema: boolean; hasInput: boolean }\n ): Promise<void> {\n const timestamp = getMicrosecondTimestamp();\n\n const event: WorkflowStartedEvent = {\n category: \"workflow\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n type: \"WorkflowStarted\",\n workflowAttemptNumber: metadata.workflowAttemptNumber,\n hasInputSchema: metadata.hasInputSchema,\n hasInput: metadata.hasInput,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveWorkflowInputValidation(\n workflowSlug: string,\n runId: string,\n result: {\n workflowAttemptNumber: number;\n hasSchema: boolean;\n success: boolean;\n error?: StepError;\n validationErrors?: Array<{ path: string; message: string }>;\n }\n ): Promise<void> {\n const timestamp = getMicrosecondTimestamp();\n\n const event: WorkflowInputValidationEvent = {\n category: \"workflow\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n type: \"WorkflowInputValidation\",\n ...result,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveWorkflowFailed(\n workflowSlug: string,\n runId: string,\n error: StepError,\n metadata: { workflowAttemptNumber: number; duration: number; completedSteps: number; failedStep?: string },\n failureReason: \"step-failed\" | \"worker-crash\" | \"timeout\" | \"cancelled\"\n ): Promise<void> {\n const timestamp = getMicrosecondTimestamp();\n\n const event: WorkflowFailedEvent = {\n category: \"workflow\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n type: \"WorkflowFailed\",\n workflowAttemptNumber: metadata.workflowAttemptNumber,\n error,\n durationUs: metadata.duration,\n completedSteps: metadata.completedSteps,\n failedStep: metadata.failedStep,\n failureReason,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveWorkflowResumed(\n workflowSlug: string,\n runId: string,\n metadata: { originalRunId: string; resumedSteps: number; pendingSteps: number }\n ): Promise<void> {\n const timestamp = getMicrosecondTimestamp();\n\n const event: WorkflowResumedEvent = {\n category: \"workflow\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n type: \"WorkflowResumed\",\n originalRunId: metadata.originalRunId,\n resumedSteps: metadata.resumedSteps,\n pendingSteps: metadata.pendingSteps,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveWorkflowCancelled(\n workflowSlug: string,\n runId: string,\n metadata: { workflowAttemptNumber: number; reason?: string; duration: number; completedSteps: number }\n ): Promise<void> {\n const timestamp = getMicrosecondTimestamp();\n\n const event: WorkflowCancelledEvent = {\n category: \"workflow\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n type: \"WorkflowCancelled\",\n workflowAttemptNumber: metadata.workflowAttemptNumber,\n reason: metadata.reason,\n durationUs: metadata.duration,\n completedSteps: metadata.completedSteps,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async saveWorkflowRetryStarted(\n workflowSlug: string,\n runId: string,\n metadata: {\n workflowAttemptNumber: number;\n previousAttemptNumber: number;\n retriedSteps: string[];\n reason?: string;\n }\n ): Promise<void> {\n const timestamp = getMicrosecondTimestamp();\n\n const event: WorkflowRetryStartedEvent = {\n category: \"workflow\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n type: \"WorkflowRetryStarted\",\n workflowAttemptNumber: metadata.workflowAttemptNumber,\n previousAttemptNumber: metadata.previousAttemptNumber,\n retriedSteps: metadata.retriedSteps,\n reason: metadata.reason,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n async getFailedSteps(\n workflowSlug: string,\n runId: string\n ): Promise<\n Array<{\n stepId: string;\n error: StepError;\n attemptNumber: number;\n }>\n > {\n // Load all step events for this run\n const events = await this.loadEvents(workflowSlug, runId, {\n category: \"step\",\n });\n\n if (events.length === 0 || !(\"category\" in events[0]!)) {\n return [];\n }\n\n // Group events by step ID\n const eventsByStep = new Map<string, StepEvent[]>();\n for (const event of events) {\n if (event.category === \"step\") {\n const stepEvents = eventsByStep.get(event.stepId) || [];\n stepEvents.push(event as StepEvent);\n eventsByStep.set(event.stepId, stepEvents);\n }\n }\n\n // Project each step's state and filter for failed steps\n const failedSteps: Array<{\n stepId: string;\n error: StepError;\n attemptNumber: number;\n }> = [];\n\n for (const [stepId, stepEvents] of eventsByStep) {\n const state = projectStepState(stepEvents, workflowSlug);\n if (state.status === \"failed\" && state.terminal && state.error) {\n failedSteps.push({\n stepId,\n error: state.error,\n attemptNumber: state.attemptNumber,\n });\n }\n }\n\n return failedSteps;\n }\n\n // ============================================================================\n // Queue Event Methods (Events-as-Queue Pattern)\n // ============================================================================\n\n async saveRunSubmitted(\n workflowSlug: string,\n runId: string,\n metadata: {\n availableAt: number;\n priority: number;\n input?: string; // Serialized JSON\n hasInputSchema: boolean;\n timeout?: number;\n idempotencyKey?: string;\n metadata?: Record<string, unknown>;\n tags?: string[];\n }\n ): Promise<void> {\n const timestamp = getMicrosecondTimestamp();\n\n const event: RunSubmittedEvent = {\n category: \"workflow\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n type: \"RunSubmitted\",\n availableAtUs: metadata.availableAt,\n priority: metadata.priority,\n input: metadata.input,\n hasInputSchema: metadata.hasInputSchema,\n timeoutUs: metadata.timeout,\n idempotencyKey: metadata.idempotencyKey,\n metadata: metadata.metadata,\n tags: metadata.tags,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n }\n\n\n // ============================================================================\n // Queue Management Methods (Events-as-Queue Implementation)\n // ============================================================================\n\n async submitRun(submission: RunSubmission): Promise<{ runId: string; isNew: boolean }> {\n // Check idempotency first\n if (submission.idempotencyKey) {\n const hash = this.hashIdempotencyKey(submission.idempotencyKey);\n const idempotencyFile = join(this.getIdempotencyDir(), `${hash}.json`);\n\n try {\n const content = await readFile(idempotencyFile, \"utf-8\");\n const existing = JSON.parse(content);\n return { runId: existing.runId, isNew: false };\n } catch {\n // Idempotency key not found, continue with submission\n }\n }\n\n // Generate runId if not provided\n const runId = submission.runId || this.generateRunId();\n const now = getMicrosecondTimestamp();\n const availableAt = submission.availableAt || now;\n const priority = submission.priority || 0;\n\n // Initialize run directory\n await this.initializeRun(submission.workflowSlug, runId);\n\n // Emit RunSubmitted event (this IS the queue entry!)\n // Input is stored directly in the event (no separate file)\n await this.saveRunSubmitted(submission.workflowSlug, runId, {\n availableAt,\n priority,\n input: submission.input !== undefined ? JSON.stringify(submission.input) : undefined,\n hasInputSchema: false, // TODO: detect from workflow metadata\n timeout: submission.timeout,\n idempotencyKey: submission.idempotencyKey,\n metadata: submission.metadata,\n tags: submission.tags,\n });\n\n // Save idempotency mapping if key provided\n if (submission.idempotencyKey) {\n const hash = this.hashIdempotencyKey(submission.idempotencyKey);\n const idempotencyDir = this.getIdempotencyDir();\n await mkdir(idempotencyDir, { recursive: true });\n const idempotencyFile = join(idempotencyDir, `${hash}.json`);\n await this.writeJsonAtomic(idempotencyFile, { runId, createdAt: now });\n }\n\n return { runId, isNew: true };\n }\n\n\n async listRuns(options?: {\n workflowSlug?: string;\n status?: RunState[\"status\"][];\n tags?: string[];\n limit?: number;\n }): Promise<RunState[]> {\n try {\n const allRuns: RunState[] = [];\n\n // Scan all workflow directories\n const workflows = await readdir(this.baseDir);\n\n for (const workflowSlug of workflows) {\n // Skip non-workflow directories\n if (workflowSlug.startsWith(\".\")) continue;\n\n // Apply workflow filter\n if (options?.workflowSlug && workflowSlug !== options.workflowSlug) continue;\n\n const workflowDir = join(this.baseDir, workflowSlug);\n const runDirs = await readdir(workflowDir);\n\n for (const runId of runDirs) {\n try {\n // Load workflow events and project to RunState\n const events = await this.loadEvents(workflowSlug, runId, { category: \"workflow\" });\n\n if (events.length === 0) continue;\n\n const state = projectRunStateFromEvents(events, workflowSlug);\n\n // Apply status filter (OR logic - match any)\n if (options?.status && options.status.length > 0) {\n if (!options.status.includes(state.status)) continue;\n }\n\n // Apply tag filter (AND logic - must have all)\n if (options?.tags && options.tags.length > 0) {\n const stateTags = state.tags || [];\n const hasAllTags = options.tags.every((tag) => stateTags.includes(tag));\n if (!hasAllTags) continue;\n }\n\n allRuns.push(state);\n } catch {\n // Skip runs that fail to project (corrupted/incomplete)\n continue;\n }\n }\n }\n\n // Sort by createdAt (descending - newest first)\n allRuns.sort((a, b) => b.createdAt - a.createdAt);\n\n // Apply limit\n return options?.limit ? allRuns.slice(0, options.limit) : allRuns;\n } catch {\n return [];\n }\n }\n\n\n async cancelRun(runId: string, reason?: string): Promise<void> {\n // Find the run across all workflows\n const workflows = await readdir(this.baseDir);\n\n for (const workflow of workflows) {\n if (workflow.startsWith(\".\")) continue;\n\n const runDir = this.getRunDir(workflow, runId);\n\n try {\n await access(runDir);\n } catch {\n continue;\n }\n\n // Found the run, load events and get created time\n const events = await this.loadEvents(workflow, runId, { category: \"workflow\" });\n if (events.length === 0) continue;\n\n const state = projectRunStateFromEvents(events, workflow);\n\n // Calculate duration\n const duration = getMicrosecondTimestamp() - state.createdAt;\n\n // Count completed steps\n const stepRecords = await this.loadRun(workflow, runId);\n const completedSteps = stepRecords.filter((r) => r.status === \"completed\").length;\n\n // Emit WorkflowCancelled event (this IS the cancellation)\n await this.saveWorkflowCancelled(workflow, runId, {\n workflowAttemptNumber: state.workflowAttemptNumber || 1,\n reason,\n duration,\n completedSteps,\n });\n\n return;\n }\n }\n\n async getRun(runId: string): Promise<RunState | null> {\n try {\n // Search all workflow directories\n const workflows = await readdir(this.baseDir);\n\n for (const workflowSlug of workflows) {\n if (workflowSlug.startsWith(\".\")) continue;\n\n const runDir = this.getRunDir(workflowSlug, runId);\n\n try {\n await access(runDir);\n } catch {\n continue;\n }\n\n // Found the run, project state from events\n const events = await this.loadEvents(workflowSlug, runId, { category: \"workflow\" });\n\n if (events.length === 0) return null;\n\n return projectRunStateFromEvents(events, workflowSlug);\n }\n\n return null;\n } catch {\n return null;\n }\n }\n\n\n // ============================================================================\n // Step-Level Distribution Methods\n // ============================================================================\n\n async listActiveWorkflows(): Promise<string[]> {\n const activeWorkflows: string[] = [];\n\n try {\n // Scan all workflow directories\n const workflows = await readdir(this.baseDir);\n\n for (const workflowSlug of workflows) {\n if (workflowSlug.startsWith(\".\")) continue;\n\n const workflowDir = join(this.baseDir, workflowSlug);\n const runDirs = await readdir(workflowDir);\n\n // Check if any run is in a non-terminal state\n for (const runId of runDirs) {\n try {\n const workflowEvents = await this.loadEvents(workflowSlug, runId, { category: \"workflow\" });\n\n if (workflowEvents.length === 0) continue;\n\n const runState = projectRunStateFromEvents(workflowEvents, workflowSlug);\n\n // Non-terminal states: pending, claimed, running\n if (runState.status === \"pending\" || runState.status === \"claimed\" || runState.status === \"running\") {\n activeWorkflows.push(workflowSlug);\n break; // Found active run, move to next workflow\n }\n } catch {\n continue;\n }\n }\n }\n\n return Array.from(new Set(activeWorkflows)); // Remove duplicates\n } catch {\n return [];\n }\n }\n\n async listScheduledSteps(options?: {\n availableBefore?: number;\n workflowSlug?: string;\n limit?: number;\n }): Promise<Array<{ workflowSlug: string; runId: string; stepId: string }>> {\n const now = getMicrosecondTimestamp();\n const availableBefore = options?.availableBefore || now;\n const scheduledSteps: Array<{ workflowSlug: string; runId: string; stepId: string; availableAt: number }> = [];\n\n try {\n // Scan all workflow directories\n const workflows = await readdir(this.baseDir);\n\n for (const workflowSlug of workflows) {\n if (workflowSlug.startsWith(\".\")) continue;\n if (options?.workflowSlug && workflowSlug !== options.workflowSlug) continue;\n\n const workflowDir = join(this.baseDir, workflowSlug);\n const runDirs = await readdir(workflowDir);\n\n for (const runId of runDirs) {\n try {\n const stepEventsDir = this.getStepEventsDir(workflowSlug, runId);\n\n // Check if step-events directory exists\n try {\n await access(stepEventsDir);\n } catch {\n continue; // No step events yet\n }\n\n // Get all step IDs by scanning event files\n const eventFiles = await readdir(stepEventsDir);\n const stepIds = new Set<string>();\n\n for (const file of eventFiles) {\n // Event files: {timestamp}-{stepId}-{eventType}.json\n const parts = file.replace('.json', '').split('-');\n if (parts.length >= 3) {\n // Step ID is everything between timestamp and event type\n const stepId = parts.slice(1, -1).join('-');\n stepIds.add(stepId);\n }\n }\n\n // For each unique step, load events and check if scheduled\n for (const stepId of stepIds) {\n const events = await this.loadEvents(workflowSlug, runId, { category: \"step\", stepId });\n\n if (events.length === 0) continue;\n\n const state = projectStepState(events as StepEvent[], workflowSlug);\n\n // Only include steps that are scheduled and available\n if (state.status === \"scheduled\" && state.availableAt && state.availableAt <= availableBefore) {\n scheduledSteps.push({\n workflowSlug,\n runId,\n stepId,\n availableAt: state.availableAt,\n });\n }\n }\n } catch {\n continue;\n }\n }\n }\n\n // Sort by availableAt (earliest first)\n scheduledSteps.sort((a, b) => a.availableAt - b.availableAt);\n\n // Apply limit\n const limited = options?.limit ? scheduledSteps.slice(0, options.limit) : scheduledSteps;\n\n // Return without availableAt field\n return limited.map(({ workflowSlug, runId, stepId }) => ({ workflowSlug, runId, stepId }));\n } catch {\n return [];\n }\n }\n\n async isStepClaimable(\n workflowSlug: string,\n runId: string,\n stepId: string\n ): Promise<boolean> {\n try {\n const events = await this.loadEvents(workflowSlug, runId, { category: \"step\", stepId });\n\n if (events.length === 0) return false;\n\n const state = projectStepState(events as StepEvent[], workflowSlug);\n const now = getMicrosecondTimestamp();\n\n return state.status === \"scheduled\" &&\n state.availableAt !== undefined &&\n state.availableAt <= now;\n } catch {\n return false;\n }\n }\n\n async claimScheduledStep(\n workflowSlug: string,\n runId: string,\n stepId: string,\n workerId: string,\n metadata: StepStartMetadata\n ): Promise<{ attemptNumber: number } | null> {\n const initialEvents = await this.loadEvents(workflowSlug, runId, { category: \"step\", stepId });\n\n if (initialEvents.length === 0) {\n return null;\n }\n\n const now = getMicrosecondTimestamp();\n const initialState = projectStepState(initialEvents as StepEvent[], workflowSlug);\n\n if (\n initialState.status !== \"scheduled\" ||\n initialState.availableAt === undefined ||\n initialState.availableAt > now\n ) {\n return null;\n }\n\n const attemptNumber = initialState.attemptNumber;\n const lockPath = this.getStepClaimLockPath(workflowSlug, runId, stepId, attemptNumber);\n await mkdir(dirname(lockPath), { recursive: true });\n\n let lockHandle: FileHandle | null = null;\n try {\n lockHandle = await openFile(lockPath, \"wx\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n return null;\n }\n throw error;\n }\n\n try {\n const currentEvents = await this.loadEvents(workflowSlug, runId, { category: \"step\", stepId });\n\n if (currentEvents.length === 0) {\n return null;\n }\n\n const currentState = projectStepState(currentEvents as StepEvent[], workflowSlug);\n const claimable =\n currentState.status === \"scheduled\" &&\n currentState.availableAt !== undefined &&\n currentState.availableAt <= getMicrosecondTimestamp() &&\n currentState.attemptNumber === attemptNumber;\n\n if (!claimable) {\n return null;\n }\n\n const timestamp = getMicrosecondTimestamp();\n\n const event: StepStartedEvent = {\n category: \"step\",\n eventId: this.generateEventId(timestamp),\n timestampUs: timestamp,\n workflowSlug,\n runId,\n stepId,\n type: \"StepStarted\",\n workerId,\n dependencies: metadata.dependencies,\n attemptNumber,\n };\n\n await this.appendEvent(workflowSlug, runId, event);\n\n return { attemptNumber };\n } finally {\n if (lockHandle) {\n try {\n await lockHandle.close();\n } catch {\n // ignore close errors\n }\n try {\n await unlink(lockPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw error;\n }\n }\n }\n }\n }\n\n async reclaimStaleSteps(\n staleThreshold: number,\n reclaimedBy: string\n ): Promise<Array<{ workflowSlug: string; runId: string; stepId: string }>> {\n const reclaimed: Array<{ workflowSlug: string; runId: string; stepId: string }> = [];\n const now = getMicrosecondTimestamp();\n\n try {\n // Scan all workflow directories\n const workflows = await readdir(this.baseDir);\n\n for (const workflowSlug of workflows) {\n if (workflowSlug.startsWith(\".\")) continue;\n\n const workflowDir = join(this.baseDir, workflowSlug);\n const runDirs = await readdir(workflowDir);\n\n for (const runId of runDirs) {\n try {\n const stepEventsDir = this.getStepEventsDir(workflowSlug, runId);\n\n // Check if step-events directory exists\n try {\n await access(stepEventsDir);\n } catch {\n continue;\n }\n\n // Get all step IDs\n const eventFiles = await readdir(stepEventsDir);\n const stepIds = new Set<string>();\n\n for (const file of eventFiles) {\n const parts = file.replace('.json', '').split('-');\n if (parts.length >= 3) {\n const stepId = parts.slice(1, -1).join('-');\n stepIds.add(stepId);\n }\n }\n\n // Check each step for stale heartbeat\n for (const stepId of stepIds) {\n const events = await this.loadEvents(workflowSlug, runId, { category: \"step\", stepId });\n\n if (events.length === 0) continue;\n\n const state = projectStepState(events as StepEvent[], workflowSlug);\n\n // Only reclaim running steps\n if (state.status !== \"running\") continue;\n\n // Check if heartbeat is stale\n const lastHeartbeat = state.lastHeartbeat || state.startTime || 0;\n const staleDuration = now - lastHeartbeat;\n\n if (staleDuration > staleThreshold) {\n // Emit StepReclaimed event\n await this.saveStepReclaimed(workflowSlug, runId, stepId, {\n originalWorkerId: state.claimedBy || \"unknown\",\n reclaimedBy,\n lastHeartbeat,\n staleThreshold,\n staleDuration,\n attemptNumber: state.attemptNumber,\n });\n\n // Re-schedule the step\n await this.saveStepScheduled(workflowSlug, runId, stepId, {\n availableAt: now, // Immediately available\n reason: \"retry\",\n attemptNumber: state.attemptNumber, // Same attempt, since it was interrupted\n retryDelayMs: 0,\n });\n\n reclaimed.push({ workflowSlug, runId, stepId });\n }\n }\n } catch {\n continue;\n }\n }\n }\n\n return reclaimed;\n } catch {\n return reclaimed;\n }\n }\n\n // ============================================================================\n // Workflow Discovery Methods\n // ============================================================================\n\n /**\n * List all available workflows\n */\n // ============================================================================\n // Workflow Registration Methods\n // ============================================================================\n\n private getRegistryDir(): string {\n return join(this.baseDir, \".registry\");\n }\n\n private getWorkflowRegistryDir(slug: string): string {\n return join(this.getRegistryDir(), slug);\n }\n\n /**\n * Register a workflow with the backend\n */\n async registerWorkflow(registration: WorkflowRegistration): Promise<void> {\n const registryDir = this.getWorkflowRegistryDir(registration.slug);\n\n try {\n // Create registry directory\n await mkdir(registryDir, { recursive: true });\n\n // Store metadata\n const metadataPath = join(registryDir, \"metadata.json\");\n const metadata: WorkflowMetadata = {\n slug: registration.slug,\n name: registration.name,\n location: registration.location,\n inputSchemaJSON: registration.inputSchemaJSON,\n };\n await writeFile(metadataPath, JSON.stringify(metadata, null, 2), \"utf-8\");\n\n // Store steps\n const stepsPath = join(registryDir, \"steps.json\");\n await writeFile(stepsPath, JSON.stringify(registration.steps, null, 2), \"utf-8\");\n } catch (error) {\n console.error(`Failed to register workflow ${registration.slug}:`, error);\n throw new Error(`Failed to register workflow: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Get metadata for a specific workflow\n */\n async getWorkflowMetadata(slug: string): Promise<WorkflowMetadata | null> {\n const metadataPath = join(this.getWorkflowRegistryDir(slug), \"metadata.json\");\n\n try {\n const content = await readFile(metadataPath, \"utf-8\");\n return JSON.parse(content) as WorkflowMetadata;\n } catch (error) {\n if (error && typeof error === 'object' && 'code' in error && error.code === \"ENOENT\") {\n return null;\n }\n console.error(`Failed to get workflow metadata for ${slug}:`, error);\n throw new Error(`Failed to load workflow metadata: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * List all registered workflows\n */\n async listWorkflowMetadata(): Promise<WorkflowMetadata[]> {\n const registryDir = this.getRegistryDir();\n\n try {\n // Check if registry exists\n try {\n await access(registryDir);\n } catch {\n // Registry doesn't exist yet\n return [];\n }\n\n const entries = await readdir(registryDir, { withFileTypes: true });\n const workflows: WorkflowMetadata[] = [];\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const metadata = await this.getWorkflowMetadata(entry.name);\n if (metadata) {\n workflows.push(metadata);\n }\n }\n }\n\n return workflows;\n } catch (error) {\n console.error('Failed to list workflows:', error);\n throw new Error(`Failed to list workflows: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Get step definitions for a workflow\n */\n async getWorkflowSteps(slug: string): Promise<StepDefinition[]> {\n const stepsPath = join(this.getWorkflowRegistryDir(slug), \"steps.json\");\n\n try {\n const content = await readFile(stepsPath, \"utf-8\");\n return JSON.parse(content) as StepDefinition[];\n } catch (error) {\n if (error && typeof error === 'object' && 'code' in error && error.code === \"ENOENT\") {\n return [];\n }\n console.error(`Failed to get workflow steps for ${slug}:`, error);\n throw new Error(`Failed to load workflow steps: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * List all run IDs for a workflow\n */\n async listRunIds(workflowSlug: string): Promise<string[]> {\n const workflowDir = join(this.baseDir, workflowSlug);\n\n try {\n // Check if workflow directory exists\n try {\n await access(workflowDir);\n } catch {\n // Workflow has no runs yet\n return [];\n }\n\n const entries = await readdir(workflowDir, { withFileTypes: true });\n const runIds: string[] = [];\n\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== \".registry\") {\n runIds.push(entry.name);\n }\n }\n\n return runIds;\n } catch (error) {\n console.error(`Failed to list run IDs for workflow ${workflowSlug}:`, error);\n throw new Error(`Failed to list run IDs: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Close the backend and clean up resources\n * For filesystem backend, this is a no-op as there are no persistent connections\n */\n async close(): Promise<void> {\n // No cleanup needed for filesystem backend\n }\n\n // ============================================================================\n // Analytics Methods\n // ============================================================================\n\n /**\n * Helper to load all events within a time range and optional filters\n */\n private async loadEventsForAnalytics(\n options?: AnalyticsOptions\n ): Promise<{ stepEvents: StepEvent[]; workflowEvents: WorkflowEvent[] }> {\n const now = getMicrosecondTimestamp();\n const startUs = options?.startUs ?? now - 24 * 60 * 60 * 1000 * 1000; // Default: 24 hours ago\n const endUs = options?.endUs ?? now;\n\n const allStepEvents: StepEvent[] = [];\n const allWorkflowEvents: WorkflowEvent[] = [];\n\n // Determine which workflows to scan\n const workflowsToScan: string[] = [];\n if (options?.workflowSlug) {\n workflowsToScan.push(options.workflowSlug);\n } else {\n // Scan all workflows\n try {\n const entries = await readdir(this.baseDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== \".registry\") {\n workflowsToScan.push(entry.name);\n }\n }\n } catch (error) {\n // baseDir doesn't exist yet, no workflows\n return { stepEvents: [], workflowEvents: [] };\n }\n }\n\n // Load events from each workflow\n for (const workflowSlug of workflowsToScan) {\n const runIds = options?.runIds ?? (await this.listRunIds(workflowSlug));\n\n for (const runId of runIds) {\n try {\n // Load step events\n const stepEvents = await this.loadEvents(workflowSlug, runId, {\n category: \"step\",\n stepId: options?.stepId,\n });\n\n // Filter by time range\n const filteredStepEvents = stepEvents.filter(\n (e) => e.timestampUs >= startUs && e.timestampUs <= endUs\n );\n allStepEvents.push(...filteredStepEvents);\n\n // Load workflow events (unless filtering by stepId)\n if (!options?.stepId) {\n const workflowEvents = await this.loadEvents(workflowSlug, runId, {\n category: \"workflow\",\n });\n\n const filteredWorkflowEvents = workflowEvents.filter(\n (e) => e.timestampUs >= startUs && e.timestampUs <= endUs\n );\n allWorkflowEvents.push(...filteredWorkflowEvents);\n }\n } catch (error) {\n // Skip runs that don't exist or have errors\n continue;\n }\n }\n }\n\n return { stepEvents: allStepEvents, workflowEvents: allWorkflowEvents };\n }\n\n async getErrorAnalysis(options?: AnalyticsOptions): Promise<ErrorAnalysis> {\n const { stepEvents } = await this.loadEventsForAnalytics(options);\n return computeErrorAnalysis(\n stepEvents,\n options?.workflowSlug,\n options?.stepId\n );\n }\n\n async getErrorsList(options?: {\n timeRange?: { start: number; end: number };\n workflowSlug?: string;\n groupingStrategy?: 'exact' | 'normalized' | 'portable';\n limit?: number;\n offset?: number;\n }): Promise<{\n errors: Array<{\n fingerprint: string;\n errorMessage: string;\n errorName: string;\n sampleStack: string;\n count: number;\n affectedRuns: number;\n firstSeen: number;\n lastSeen: number;\n }>;\n total: number;\n }> {\n const { stepEvents } = await this.loadEventsForAnalytics({\n startUs: options?.timeRange?.start,\n endUs: options?.timeRange?.end,\n workflowSlug: options?.workflowSlug,\n });\n\n const strategy = options?.groupingStrategy || 'exact';\n const limit = options?.limit || 50;\n const offset = options?.offset || 0;\n\n // Filter for StepFailed events only\n const failedEvents = stepEvents.filter(\n (e): e is StepFailedEvent => e.type === 'StepFailed'\n );\n\n // Group by composable fingerprint\n const errorGroups = new Map<\n string,\n {\n errorMessage: string;\n errorName: string;\n sampleStack: string;\n count: number;\n affectedRuns: Set<string>;\n firstSeen: number;\n lastSeen: number;\n }\n >();\n\n for (const event of failedEvents) {\n // Compose fingerprint based on strategy\n const stackHash =\n strategy === 'exact'\n ? event.errorFingerprints.stackExactHash\n : strategy === 'normalized'\n ? event.errorFingerprints.stackNormalizedHash\n : event.errorFingerprints.stackPortableHash;\n\n const fingerprint = `${event.errorFingerprints.nameHash}:${event.errorFingerprints.messageHash}:${stackHash}`;\n\n const existing = errorGroups.get(fingerprint);\n if (existing) {\n existing.count++;\n existing.affectedRuns.add(event.runId);\n existing.firstSeen = Math.min(existing.firstSeen, event.timestampUs);\n existing.lastSeen = Math.max(existing.lastSeen, event.timestampUs);\n } else {\n errorGroups.set(fingerprint, {\n errorMessage: event.error.message,\n errorName: event.error.name || 'Error',\n sampleStack: event.error.stack || '',\n count: 1,\n affectedRuns: new Set([event.runId]),\n firstSeen: event.timestampUs,\n lastSeen: event.timestampUs,\n });\n }\n }\n\n // Convert to array and sort by count\n const allErrors = Array.from(errorGroups.entries()).map(([fingerprint, data]) => ({\n fingerprint,\n errorMessage: data.errorMessage,\n errorName: data.errorName,\n sampleStack: data.sampleStack,\n count: data.count,\n affectedRuns: data.affectedRuns.size,\n firstSeen: data.firstSeen,\n lastSeen: data.lastSeen,\n }));\n\n allErrors.sort((a, b) => b.count - a.count);\n\n const total = allErrors.length;\n const errors = allErrors.slice(offset, offset + limit);\n\n return { errors, total };\n }\n\n async getErrorDetail(\n fingerprint: string,\n groupingStrategy: 'exact' | 'normalized' | 'portable',\n options?: {\n timeRange?: { start: number; end: number };\n limit?: number;\n offset?: number;\n }\n ): Promise<{\n fingerprint: string;\n errorMessage: string;\n errorName: string;\n sampleStack: string;\n totalCount: number;\n affectedRuns: number;\n firstSeen: number;\n lastSeen: number;\n occurrences: Array<{\n workflowSlug: string;\n runId: string;\n stepId: string;\n attemptNumber: number;\n timestampUs: number;\n }>;\n total: number;\n }> {\n // Parse fingerprint\n const parts = fingerprint.split(':');\n if (parts.length !== 3) {\n throw new Error(`Invalid fingerprint format: ${fingerprint}`);\n }\n\n const [nameHash, messageHash, stackHash] = parts;\n\n const { stepEvents } = await this.loadEventsForAnalytics({\n startUs: options?.timeRange?.start,\n endUs: options?.timeRange?.end,\n });\n\n const limit = options?.limit || 100;\n const offset = options?.offset || 0;\n\n // Filter for matching StepFailed events\n const matchingEvents = stepEvents.filter((e): e is StepFailedEvent => {\n if (e.type !== 'StepFailed') return false;\n\n // Check if fingerprints match\n if (e.errorFingerprints.nameHash !== nameHash) return false;\n if (e.errorFingerprints.messageHash !== messageHash) return false;\n\n const eventStackHash =\n groupingStrategy === 'exact'\n ? e.errorFingerprints.stackExactHash\n : groupingStrategy === 'normalized'\n ? e.errorFingerprints.stackNormalizedHash\n : e.errorFingerprints.stackPortableHash;\n\n return eventStackHash === stackHash;\n });\n\n if (matchingEvents.length === 0) {\n return {\n fingerprint,\n errorMessage: '',\n errorName: '',\n sampleStack: '',\n totalCount: 0,\n affectedRuns: 0,\n firstSeen: 0,\n lastSeen: 0,\n occurrences: [],\n total: 0,\n };\n }\n\n // Calculate stats\n const affectedRunsSet = new Set(matchingEvents.map((e) => e.runId));\n const firstSeen = Math.min(...matchingEvents.map((e) => e.timestampUs));\n const lastSeen = Math.max(...matchingEvents.map((e) => e.timestampUs));\n\n // Sort by timestamp desc and paginate\n matchingEvents.sort((a, b) => b.timestampUs - a.timestampUs);\n const paginatedEvents = matchingEvents.slice(offset, offset + limit);\n\n const occurrences = paginatedEvents.map((e) => ({\n workflowSlug: e.workflowSlug,\n runId: e.runId,\n stepId: e.stepId,\n attemptNumber: e.attemptNumber,\n timestampUs: e.timestampUs,\n }));\n\n const sampleEvent = matchingEvents[0]!; // Safe: empty array check at line 2000\n\n return {\n fingerprint,\n errorMessage: sampleEvent.error.message,\n errorName: sampleEvent.error.name || 'Error',\n sampleStack: sampleEvent.error.stack || '',\n totalCount: matchingEvents.length,\n affectedRuns: affectedRunsSet.size,\n firstSeen,\n lastSeen,\n occurrences,\n total: matchingEvents.length,\n };\n }\n\n async getRetryAnalysis(options?: AnalyticsOptions): Promise<RetryAnalysis> {\n const { stepEvents } = await this.loadEventsForAnalytics(options);\n return computeRetryAnalysis(stepEvents);\n }\n\n async getSchedulingLatency(\n options?: AnalyticsOptions\n ): Promise<SchedulingLatency> {\n const { stepEvents } = await this.loadEventsForAnalytics(options);\n return computeSchedulingLatency(\n stepEvents,\n options?.workflowSlug,\n options?.stepId\n );\n }\n\n async getStepDuration(options?: AnalyticsOptions): Promise<StepDuration> {\n const { stepEvents } = await this.loadEventsForAnalytics(options);\n return computeStepDuration(\n stepEvents,\n options?.workflowSlug,\n options?.stepId\n );\n }\n\n async getWorkflowDuration(\n options?: AnalyticsOptions\n ): Promise<WorkflowDuration> {\n const { workflowEvents } = await this.loadEventsForAnalytics(options);\n return computeWorkflowDuration(workflowEvents, options?.workflowSlug);\n }\n\n async getWorkerStability(options?: AnalyticsOptions): Promise<WorkerStability> {\n const { stepEvents } = await this.loadEventsForAnalytics(options);\n return computeWorkerStability(stepEvents);\n }\n\n async getThroughput(options?: AnalyticsOptions): Promise<Throughput> {\n const { stepEvents, workflowEvents } = await this.loadEventsForAnalytics(options);\n\n const now = getMicrosecondTimestamp();\n const startUs = options?.startUs ?? now - 24 * 60 * 60 * 1000 * 1000;\n const endUs = options?.endUs ?? now;\n const timeRangeUs = endUs - startUs;\n\n return computeThroughput(\n stepEvents,\n workflowEvents,\n timeRangeUs,\n options?.workflowSlug\n );\n }\n\n async getQueueDepth(\n options?: Pick<AnalyticsOptions, \"workflowSlug\">\n ): Promise<QueueDepth> {\n const runs = await this.listRuns({\n workflowSlug: options?.workflowSlug,\n status: [\"pending\", \"running\"],\n });\n\n let pendingRuns = 0;\n let runningRuns = 0;\n let scheduledSteps = 0;\n let runningSteps = 0;\n let oldestScheduledStepUs: number | undefined;\n let oldestPendingRunUs: number | undefined;\n\n for (const run of runs) {\n if (run.status === \"pending\") {\n pendingRuns++;\n if (\n !oldestPendingRunUs ||\n run.createdAt < oldestPendingRunUs\n ) {\n oldestPendingRunUs = run.createdAt;\n }\n } else if (run.status === \"running\") {\n runningRuns++;\n\n // Count scheduled and running steps in this run\n try {\n const stepEvents = await this.loadEvents(\n run.workflowSlug,\n run.runId,\n { category: \"step\" }\n );\n\n // Group events by step\n const eventsByStep = new Map<string, StepEvent[]>();\n for (const event of stepEvents) {\n const events = eventsByStep.get(event.stepId) || [];\n events.push(event);\n eventsByStep.set(event.stepId, events);\n }\n\n // Project state for each step\n for (const [stepId, events] of eventsByStep.entries()) {\n const state = projectStepState(events, run.workflowSlug);\n\n if (state.status === \"scheduled\") {\n scheduledSteps++;\n if (\n state.availableAt &&\n (!oldestScheduledStepUs || state.availableAt < oldestScheduledStepUs)\n ) {\n oldestScheduledStepUs = state.availableAt;\n }\n } else if (state.status === \"running\") {\n runningSteps++;\n }\n }\n } catch (error) {\n // Skip if can't load step events\n continue;\n }\n }\n }\n\n return {\n workflowSlug: options?.workflowSlug,\n pendingRuns,\n runningRuns,\n scheduledSteps,\n runningSteps,\n oldestScheduledStepUs,\n oldestPendingRunUs,\n };\n }\n\n async getQueueDepthByWorkflow(): Promise<QueueDepthByWorkflow> {\n const runs = await this.listRuns({ status: [\"pending\", \"running\"] });\n\n // Group runs by workflow\n const workflowMap = new Map<\n string,\n {\n pendingRuns: number;\n scheduledSteps: number;\n oldestPendingItemUs?: number;\n }\n >();\n\n for (const run of runs) {\n const existing = workflowMap.get(run.workflowSlug) || {\n pendingRuns: 0,\n scheduledSteps: 0,\n };\n\n if (run.status === \"pending\") {\n existing.pendingRuns++;\n if (\n !existing.oldestPendingItemUs ||\n run.createdAt < existing.oldestPendingItemUs\n ) {\n existing.oldestPendingItemUs = run.createdAt;\n }\n } else if (run.status === \"running\") {\n // Count scheduled steps in this run\n try {\n const stepEvents = await this.loadEvents(run.workflowSlug, run.runId, {\n category: \"step\",\n });\n\n // Group events by step\n const eventsByStep = new Map<string, StepEvent[]>();\n for (const event of stepEvents) {\n const events = eventsByStep.get(event.stepId) || [];\n events.push(event);\n eventsByStep.set(event.stepId, events);\n }\n\n // Project state for each step\n for (const [stepId, events] of eventsByStep.entries()) {\n const state = projectStepState(events, run.workflowSlug);\n\n if (state.status === \"scheduled\") {\n existing.scheduledSteps++;\n if (\n state.availableAt &&\n (!existing.oldestPendingItemUs ||\n state.availableAt < existing.oldestPendingItemUs)\n ) {\n existing.oldestPendingItemUs = state.availableAt;\n }\n }\n }\n } catch (error) {\n // Skip if can't load step events\n continue;\n }\n }\n\n workflowMap.set(run.workflowSlug, existing);\n }\n\n // Get workflow metadata for names\n const allMetadata = await this.listWorkflowMetadata();\n const metadataMap = new Map(allMetadata.map((m) => [m.slug, m]));\n\n // Convert to array format\n const result: QueueDepthByWorkflow = Array.from(workflowMap.entries())\n .map(([workflowSlug, data]) => ({\n workflowSlug,\n workflowName: metadataMap.get(workflowSlug)?.name,\n pendingRuns: data.pendingRuns,\n scheduledSteps: data.scheduledSteps,\n oldestPendingItemUs: data.oldestPendingItemUs,\n }))\n .filter((item) => item.pendingRuns > 0 || item.scheduledSteps > 0) // Only include workflows with pending work\n .sort((a, b) => {\n // Sort by total pending work (desc)\n const aTotal = a.pendingRuns + a.scheduledSteps;\n const bTotal = b.pendingRuns + b.scheduledSteps;\n return bTotal - aTotal;\n });\n\n return result;\n }\n\n async getSuccessRate(options?: AnalyticsOptions): Promise<SuccessRate> {\n const { stepEvents, workflowEvents } = await this.loadEventsForAnalytics(options);\n return computeSuccessRate(\n stepEvents,\n workflowEvents,\n options?.workflowSlug,\n options?.stepId\n );\n }\n\n async getAnalyticsSummary(\n options?: AnalyticsOptions\n ): Promise<AnalyticsSummary> {\n const now = getMicrosecondTimestamp();\n const startUs = options?.startUs ?? now - 24 * 60 * 60 * 1000 * 1000;\n const endUs = options?.endUs ?? now;\n\n // Run all analytics in parallel\n const [\n errorAnalysis,\n retryAnalysis,\n schedulingLatency,\n stepDuration,\n workflowDuration,\n workerStability,\n throughput,\n queueDepth,\n successRate,\n ] = await Promise.all([\n this.getErrorAnalysis(options),\n this.getRetryAnalysis(options),\n this.getSchedulingLatency(options),\n this.getStepDuration(options),\n this.getWorkflowDuration(options),\n this.getWorkerStability(options),\n this.getThroughput(options),\n this.getQueueDepth(options),\n this.getSuccessRate(options),\n ]);\n\n return {\n timeRange: {\n startUs,\n endUs,\n durationUs: endUs - startUs,\n },\n errorAnalysis,\n retryAnalysis,\n schedulingLatency,\n stepDuration,\n workflowDuration,\n workerStability,\n throughput,\n queueDepth,\n successRate,\n };\n }\n}\n\n// Re-export projection functions for use by workers\nexport { projectStepState, projectRunStateFromEvents, projectStepRecord } from \"@cascade-flow/backend-interface\";\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "0253EE6D5A19700564756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cascade-flow/backend-filesystem",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"module": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "rm -rf dist && bun build src/index.ts --outdir dist --target node --sourcemap=external --external=@cascade-flow/* && tsc -p tsconfig.build.json",
|
|
17
|
+
"prepublishOnly": "bun run build",
|
|
18
|
+
"test": "bun test",
|
|
19
|
+
"test:integration": "bun test tests/integration",
|
|
20
|
+
"test:coverage": "bun test --coverage"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@cascade-flow/backend-interface": "0.1.0",
|
|
24
|
+
"@cascade-flow/runner": "0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/bun": "latest"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"typescript": "^5"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/cascadeflow/cascadeflow.git",
|
|
41
|
+
"directory": "packages/backend-filesystem"
|
|
42
|
+
},
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"description": "Filesystem-based backend implementation for CascadeFlow workflow orchestrator",
|
|
45
|
+
"keywords": [
|
|
46
|
+
"workflow",
|
|
47
|
+
"orchestrator",
|
|
48
|
+
"backend",
|
|
49
|
+
"filesystem",
|
|
50
|
+
"persistence"
|
|
51
|
+
]
|
|
52
|
+
}
|