pidnap 0.0.0-dev.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger-crc5neL8.mjs","names":[],"sources":["../src/lazy-process.ts","../src/restarting-process.ts","../src/cron-process.ts","../src/task-list.ts","../src/env-manager.ts","../src/logger.ts"],"sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\";\nimport readline from \"node:readline\";\nimport { PassThrough } from \"node:stream\";\nimport * as v from \"valibot\";\nimport type { Logger } from \"./logger.ts\";\n\nexport const ProcessDefinitionSchema = v.object({\n command: v.string(),\n args: v.optional(v.array(v.string())),\n cwd: v.optional(v.string()),\n env: v.optional(v.record(v.string(), v.string())),\n});\n\nexport type ProcessDefinition = v.InferOutput<typeof ProcessDefinitionSchema>;\n\nexport const ProcessStateSchema = v.picklist([\n \"idle\",\n \"starting\",\n \"running\",\n \"stopping\",\n \"stopped\",\n \"error\",\n]);\n\nexport type ProcessState = v.InferOutput<typeof ProcessStateSchema>;\n\n/**\n * Kill a process. Tries to kill the process group first (if available),\n * then falls back to killing just the process.\n */\nfunction killProcess(child: ChildProcess, signal: NodeJS.Signals): boolean {\n try {\n return child.kill(signal);\n } catch {\n return false;\n }\n}\n\nexport class LazyProcess {\n readonly name: string;\n private definition: ProcessDefinition;\n private logger: Logger;\n private childProcess: ChildProcess | null = null;\n private _state: ProcessState = \"idle\";\n private processExit = Promise.withResolvers<void>();\n public exitCode: number | null = null;\n\n constructor(name: string, definition: ProcessDefinition, logger: Logger) {\n this.name = name;\n this.definition = definition;\n this.logger = logger;\n }\n\n get state(): ProcessState {\n return this._state;\n }\n\n start(): void {\n if (this._state === \"running\" || this._state === \"starting\") {\n throw new Error(`Process \"${this.name}\" is already ${this._state}`);\n }\n\n if (this._state === \"stopping\") {\n throw new Error(`Process \"${this.name}\" is currently stopping`);\n }\n\n this._state = \"starting\";\n this.logger.info(`Starting process: ${this.definition.command}`);\n\n try {\n const env = this.definition.env ? { ...process.env, ...this.definition.env } : process.env;\n\n this.childProcess = spawn(this.definition.command, this.definition.args ?? [], {\n cwd: this.definition.cwd,\n env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n this._state = \"running\";\n\n // Combine stdout and stderr into a single stream for unified logging\n const combined = new PassThrough();\n let streamCount = 0;\n\n if (this.childProcess.stdout) {\n streamCount++;\n this.childProcess.stdout.pipe(combined, { end: false });\n this.childProcess.stdout.on(\"end\", () => {\n if (--streamCount === 0) combined.end();\n });\n }\n\n if (this.childProcess.stderr) {\n streamCount++;\n this.childProcess.stderr.pipe(combined, { end: false });\n this.childProcess.stderr.on(\"end\", () => {\n if (--streamCount === 0) combined.end();\n });\n }\n\n // Use readline to handle line-by-line output properly\n const rl = readline.createInterface({ input: combined });\n rl.on(\"line\", (line) => {\n this.logger.info(line);\n });\n\n // Handle process exit\n this.childProcess.on(\"exit\", (code, signal) => {\n this.exitCode = code;\n\n if (this._state === \"running\") {\n if (code === 0) {\n this._state = \"stopped\";\n this.logger.info(`Process exited with code ${code}`);\n } else if (signal) {\n this._state = \"stopped\";\n this.logger.info(`Process killed with signal ${signal}`);\n } else {\n this._state = \"error\";\n this.logger.error(`Process exited with code ${code}`);\n }\n }\n\n this.processExit.resolve();\n });\n\n // Handle spawn errors\n this.childProcess.on(\"error\", (err) => {\n if (this._state !== \"stopping\" && this._state !== \"stopped\") {\n this._state = \"error\";\n this.logger.error(`Process error:`, err);\n }\n this.processExit.resolve();\n });\n } catch (err) {\n this._state = \"error\";\n this.logger.error(`Failed to start process:`, err);\n throw err;\n }\n }\n\n async stop(timeout?: number): Promise<void> {\n if (this._state === \"idle\" || this._state === \"stopped\" || this._state === \"error\") {\n return;\n }\n\n if (this._state === \"stopping\") {\n // Already stopping, wait for completion\n await this.processExit.promise;\n return;\n }\n\n if (!this.childProcess) {\n this._state = \"stopped\";\n return;\n }\n\n this._state = \"stopping\";\n this.logger.info(`Stopping process with SIGTERM`);\n\n // Send SIGTERM for graceful shutdown (to entire process group)\n killProcess(this.childProcess, \"SIGTERM\");\n\n const timeoutMs = timeout ?? 5000;\n\n // Wait for process to exit or timeout\n const timeoutPromise = new Promise<\"timeout\">((resolve) =>\n setTimeout(() => resolve(\"timeout\"), timeoutMs),\n );\n\n const result = await Promise.race([\n this.processExit.promise.then(() => \"exited\" as const),\n timeoutPromise,\n ]);\n\n if (result === \"timeout\" && this.childProcess) {\n this.logger.warn(`Process did not exit within ${timeoutMs}ms, sending SIGKILL`);\n killProcess(this.childProcess, \"SIGKILL\");\n\n // Give SIGKILL a short timeout\n const killTimeout = new Promise<void>((resolve) => setTimeout(resolve, 1000));\n await Promise.race([this.processExit.promise, killTimeout]);\n }\n\n this._state = \"stopped\";\n this.cleanup();\n this.logger.info(`Process stopped`);\n }\n\n async reset(): Promise<void> {\n if (this.childProcess) {\n // Kill the process group if running\n killProcess(this.childProcess, \"SIGKILL\");\n await this.processExit.promise;\n this.cleanup();\n }\n\n this._state = \"idle\";\n // Create a fresh promise for the next process lifecycle\n this.processExit = Promise.withResolvers<void>();\n this.logger.info(`Process reset to idle`);\n }\n\n updateDefinition(definition: ProcessDefinition): void {\n this.definition = definition;\n }\n\n async waitForExit(): Promise<ProcessState> {\n if (!this.childProcess) {\n return this._state;\n }\n\n await this.processExit.promise;\n return this._state;\n }\n\n private cleanup(): void {\n if (this.childProcess) {\n // Remove all listeners to prevent memory leaks\n this.childProcess.stdout?.removeAllListeners();\n this.childProcess.stderr?.removeAllListeners();\n this.childProcess.removeAllListeners();\n this.childProcess = null;\n }\n\n this.exitCode = null;\n }\n}\n","import * as v from \"valibot\";\nimport { LazyProcess, type ProcessDefinition, type ProcessState } from \"./lazy-process.ts\";\nimport type { Logger } from \"./logger.ts\";\n\n// Restart policies\nexport const RestartPolicySchema = v.picklist([\n \"always\",\n \"on-failure\",\n \"never\",\n \"unless-stopped\",\n \"on-success\",\n]);\n\nexport type RestartPolicy = v.InferOutput<typeof RestartPolicySchema>;\n\n// Backoff strategy schema\nexport const BackoffStrategySchema = v.union([\n v.object({\n type: v.literal(\"fixed\"),\n delayMs: v.number(),\n }),\n v.object({\n type: v.literal(\"exponential\"),\n initialDelayMs: v.number(),\n maxDelayMs: v.number(),\n multiplier: v.optional(v.number()),\n }),\n]);\n\nexport type BackoffStrategy = v.InferOutput<typeof BackoffStrategySchema>;\n\n// Crash loop detection config schema\nexport const CrashLoopConfigSchema = v.object({\n maxRestarts: v.number(),\n windowMs: v.number(),\n backoffMs: v.number(),\n});\n\nexport type CrashLoopConfig = v.InferOutput<typeof CrashLoopConfigSchema>;\n\n// Restarting process options schema\nexport const RestartingProcessOptionsSchema = v.object({\n restartPolicy: RestartPolicySchema,\n backoff: v.optional(BackoffStrategySchema),\n crashLoop: v.optional(CrashLoopConfigSchema),\n minUptimeMs: v.optional(v.number()),\n maxTotalRestarts: v.optional(v.number()),\n});\n\nexport type RestartingProcessOptions = v.InferOutput<typeof RestartingProcessOptionsSchema>;\n\n// State\nexport const RestartingProcessStateSchema = v.picklist([\n \"idle\",\n \"running\",\n \"restarting\",\n \"stopping\",\n \"stopped\",\n \"crash-loop-backoff\",\n \"max-restarts-reached\",\n]);\n\nexport type RestartingProcessState = v.InferOutput<typeof RestartingProcessStateSchema>;\n\nconst DEFAULT_BACKOFF: BackoffStrategy = { type: \"fixed\", delayMs: 1000 };\nconst DEFAULT_CRASH_LOOP: CrashLoopConfig = { maxRestarts: 5, windowMs: 60000, backoffMs: 60000 };\n\nexport class RestartingProcess {\n readonly name: string;\n private lazyProcess: LazyProcess;\n private definition: ProcessDefinition;\n private options: Required<Omit<RestartingProcessOptions, \"maxTotalRestarts\">> & {\n maxTotalRestarts?: number;\n };\n private logger: Logger;\n\n // State tracking\n private _state: RestartingProcessState = \"idle\";\n private _restartCount: number = 0;\n private restartTimestamps: number[] = []; // For crash loop detection\n private consecutiveFailures: number = 0; // For exponential backoff\n private lastStartTime: number | null = null;\n private stopRequested: boolean = false;\n private pendingDelayTimeout: ReturnType<typeof setTimeout> | null = null;\n\n constructor(\n name: string,\n definition: ProcessDefinition,\n options: RestartingProcessOptions,\n logger: Logger,\n ) {\n this.name = name;\n this.definition = definition;\n this.logger = logger;\n this.options = {\n restartPolicy: options.restartPolicy,\n backoff: options.backoff ?? DEFAULT_BACKOFF,\n crashLoop: options.crashLoop ?? DEFAULT_CRASH_LOOP,\n minUptimeMs: options.minUptimeMs ?? 0,\n maxTotalRestarts: options.maxTotalRestarts,\n };\n this.lazyProcess = new LazyProcess(name, definition, logger);\n }\n\n get state(): RestartingProcessState {\n return this._state;\n }\n\n get restarts(): number {\n return this._restartCount;\n }\n\n start(): void {\n if (this._state === \"running\" || this._state === \"restarting\") {\n throw new Error(`Process \"${this.name}\" is already ${this._state}`);\n }\n\n if (this._state === \"stopping\") {\n throw new Error(`Process \"${this.name}\" is currently stopping`);\n }\n\n // Fresh start from terminal states - reset counters\n if (\n this._state === \"stopped\" ||\n this._state === \"idle\" ||\n this._state === \"max-restarts-reached\"\n ) {\n this.resetCounters();\n }\n\n this.stopRequested = false;\n this.startProcess();\n }\n\n async stop(timeout?: number): Promise<void> {\n this.stopRequested = true;\n\n // Clear any pending delays\n if (this.pendingDelayTimeout) {\n clearTimeout(this.pendingDelayTimeout);\n this.pendingDelayTimeout = null;\n }\n\n if (\n this._state === \"idle\" ||\n this._state === \"stopped\" ||\n this._state === \"max-restarts-reached\"\n ) {\n this._state = \"stopped\";\n return;\n }\n\n this._state = \"stopping\";\n await this.lazyProcess.stop(timeout);\n this._state = \"stopped\";\n this.logger.info(`RestartingProcess stopped`);\n }\n\n async restart(force: boolean = false): Promise<void> {\n // Fresh start from terminal states - reset counters and no delay\n if (\n this._state === \"stopped\" ||\n this._state === \"idle\" ||\n this._state === \"max-restarts-reached\"\n ) {\n this.resetCounters();\n this.stopRequested = false;\n this.startProcess();\n return;\n }\n\n // Stop the current process first\n await this.stop();\n\n this.stopRequested = false;\n\n if (force) {\n // Force restart - no delay\n this.startProcess();\n } else {\n // Follow normal delay strategy\n const delay = this.calculateDelay();\n if (delay > 0) {\n this._state = \"restarting\";\n this.logger.info(`Restarting in ${delay}ms`);\n await this.delay(delay);\n if (this.stopRequested) return;\n }\n this.startProcess();\n }\n }\n\n /**\n * Update process definition and optionally restart with new config\n */\n async reload(\n newDefinition: ProcessDefinition,\n restartImmediately: boolean = true,\n ): Promise<void> {\n this.logger.info(`Reloading process with new definition`);\n this.definition = newDefinition;\n this.lazyProcess.updateDefinition(newDefinition);\n\n if (restartImmediately) {\n // Restart with force=true to apply changes immediately\n await this.restart(true);\n }\n }\n\n /**\n * Update restart options\n */\n updateOptions(newOptions: Partial<RestartingProcessOptions>): void {\n this.logger.info(`Updating restart options`);\n this.options = {\n ...this.options,\n restartPolicy: newOptions.restartPolicy ?? this.options.restartPolicy,\n backoff: newOptions.backoff ?? this.options.backoff,\n crashLoop: newOptions.crashLoop ?? this.options.crashLoop,\n minUptimeMs: newOptions.minUptimeMs ?? this.options.minUptimeMs,\n maxTotalRestarts: newOptions.maxTotalRestarts ?? this.options.maxTotalRestarts,\n };\n }\n\n private resetCounters(): void {\n this._restartCount = 0;\n this.consecutiveFailures = 0;\n this.restartTimestamps = [];\n }\n\n private startProcess(): void {\n this.lastStartTime = Date.now();\n this._state = \"running\";\n\n this.lazyProcess\n .reset()\n .then(() => {\n if (this.stopRequested) return;\n this.lazyProcess.start();\n return this.lazyProcess.waitForExit();\n })\n .then((exitState) => {\n if (!exitState) return;\n if (this.stopRequested && exitState === \"error\") {\n this._state = \"stopped\";\n return;\n }\n if (exitState === \"stopped\" || exitState === \"error\") {\n this.handleProcessExit(exitState);\n }\n })\n .catch((err) => {\n if (this.stopRequested) return;\n this._state = \"stopped\";\n this.logger.error(`Failed to start process:`, err);\n });\n }\n\n private handleProcessExit(exitState: ProcessState): void {\n if (this.stopRequested) {\n this._state = \"stopped\";\n return;\n }\n\n const uptime = this.lastStartTime ? Date.now() - this.lastStartTime : 0;\n const wasHealthy = uptime >= this.options.minUptimeMs;\n const exitedWithError = exitState === \"error\";\n\n // Reset consecutive failures if the process ran long enough\n if (wasHealthy) {\n this.consecutiveFailures = 0;\n } else {\n this.consecutiveFailures++;\n }\n\n // Check if policy allows restart\n if (!this.shouldRestart(exitedWithError)) {\n this._state = \"stopped\";\n this.logger.info(\n `Process exited, policy \"${this.options.restartPolicy}\" does not allow restart`,\n );\n return;\n }\n\n // Check max total restarts\n if (\n this.options.maxTotalRestarts !== undefined &&\n this._restartCount >= this.options.maxTotalRestarts\n ) {\n this._state = \"max-restarts-reached\";\n this.logger.warn(`Max total restarts (${this.options.maxTotalRestarts}) reached`);\n return;\n }\n\n // Record restart timestamp for crash loop detection\n const now = Date.now();\n this.restartTimestamps.push(now);\n\n // Check for crash loop\n if (this.isInCrashLoop()) {\n this._state = \"crash-loop-backoff\";\n this.logger.warn(\n `Crash loop detected (${this.options.crashLoop.maxRestarts} restarts in ${this.options.crashLoop.windowMs}ms), backing off for ${this.options.crashLoop.backoffMs}ms`,\n );\n this.scheduleCrashLoopRecovery();\n return;\n }\n\n // Schedule restart with delay\n this._restartCount++;\n this.scheduleRestart();\n }\n\n private shouldRestart(exitedWithError: boolean): boolean {\n switch (this.options.restartPolicy) {\n case \"always\":\n return true;\n case \"never\":\n return false;\n case \"on-failure\":\n return exitedWithError;\n case \"on-success\":\n return !exitedWithError;\n case \"unless-stopped\":\n return !this.stopRequested;\n default:\n return false;\n }\n }\n\n private isInCrashLoop(): boolean {\n const { maxRestarts, windowMs } = this.options.crashLoop;\n const now = Date.now();\n const cutoff = now - windowMs;\n\n // Clean up old timestamps\n this.restartTimestamps = this.restartTimestamps.filter((ts) => ts > cutoff);\n\n return this.restartTimestamps.length >= maxRestarts;\n }\n\n private calculateDelay(): number {\n const { backoff } = this.options;\n\n if (backoff.type === \"fixed\") {\n return backoff.delayMs;\n }\n\n // Exponential backoff\n const multiplier = backoff.multiplier ?? 2;\n const delay = backoff.initialDelayMs * Math.pow(multiplier, this.consecutiveFailures);\n return Math.min(delay, backoff.maxDelayMs);\n }\n\n private scheduleRestart(): void {\n this._state = \"restarting\";\n const delay = this.calculateDelay();\n\n this.logger.info(`Restarting in ${delay}ms (restart #${this._restartCount})`);\n\n this.pendingDelayTimeout = setTimeout(() => {\n this.pendingDelayTimeout = null;\n if (this.stopRequested) {\n this._state = \"stopped\";\n return;\n }\n this.startProcess();\n }, delay);\n }\n\n private scheduleCrashLoopRecovery(): void {\n const { backoffMs } = this.options.crashLoop;\n\n this.pendingDelayTimeout = setTimeout(() => {\n this.pendingDelayTimeout = null;\n if (this.stopRequested) {\n this._state = \"stopped\";\n return;\n }\n\n // Reset crash loop timestamps after backoff\n this.restartTimestamps = [];\n this._restartCount++;\n this.logger.info(`Crash loop backoff complete, restarting (restart #${this._restartCount})`);\n this.startProcess();\n }, backoffMs);\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n this.pendingDelayTimeout = setTimeout(() => {\n this.pendingDelayTimeout = null;\n resolve();\n }, ms);\n });\n }\n}\n","import { Cron } from \"croner\";\nimport * as v from \"valibot\";\nimport { LazyProcess, type ProcessDefinition } from \"./lazy-process.ts\";\nimport type { Logger } from \"./logger.ts\";\n\n// Retry configuration schema\nexport const RetryConfigSchema = v.object({\n maxRetries: v.number(),\n delayMs: v.optional(v.number()),\n});\n\nexport type RetryConfig = v.InferOutput<typeof RetryConfigSchema>;\n\n// Cron process options schema\nexport const CronProcessOptionsSchema = v.object({\n schedule: v.string(),\n retry: v.optional(RetryConfigSchema),\n runOnStart: v.optional(v.boolean()),\n});\n\nexport type CronProcessOptions = v.InferOutput<typeof CronProcessOptionsSchema>;\n\n// State\nexport const CronProcessStateSchema = v.picklist([\n \"idle\",\n \"scheduled\",\n \"running\",\n \"retrying\",\n \"queued\",\n \"stopping\",\n \"stopped\",\n]);\n\nexport type CronProcessState = v.InferOutput<typeof CronProcessStateSchema>;\n\nconst DEFAULT_RETRY_DELAY = 1000;\n\nexport class CronProcess {\n readonly name: string;\n private lazyProcess: LazyProcess;\n private options: CronProcessOptions;\n private logger: Logger;\n private cronJob: Cron | null = null;\n\n // State tracking\n private _state: CronProcessState = \"idle\";\n private _runCount: number = 0;\n private _failCount: number = 0;\n private currentRetryAttempt: number = 0;\n private queuedRun: boolean = false;\n private stopRequested: boolean = false;\n private retryTimeout: ReturnType<typeof setTimeout> | null = null;\n\n constructor(\n name: string,\n definition: ProcessDefinition,\n options: CronProcessOptions,\n logger: Logger,\n ) {\n this.name = name;\n this.options = options;\n this.logger = logger;\n this.lazyProcess = new LazyProcess(name, definition, logger);\n }\n\n get state(): CronProcessState {\n return this._state;\n }\n\n get runCount(): number {\n return this._runCount;\n }\n\n get failCount(): number {\n return this._failCount;\n }\n\n get nextRun(): Date | null {\n if (!this.cronJob) return null;\n const next = this.cronJob.nextRun();\n return next ?? null;\n }\n\n start(): void {\n if (this._state === \"scheduled\" || this._state === \"running\" || this._state === \"queued\") {\n throw new Error(`CronProcess \"${this.name}\" is already ${this._state}`);\n }\n\n if (this._state === \"stopping\") {\n throw new Error(`CronProcess \"${this.name}\" is currently stopping`);\n }\n\n this.stopRequested = false;\n this.logger.info(`Starting cron schedule: ${this.options.schedule}`);\n\n // Create cron job with UTC timezone\n this.cronJob = new Cron(this.options.schedule, { timezone: \"UTC\" }, () => {\n this.onCronTick();\n });\n\n this._state = \"scheduled\";\n\n // Run immediately if configured\n if (this.options.runOnStart) {\n this.executeJob();\n }\n }\n\n async stop(timeout?: number): Promise<void> {\n this.stopRequested = true;\n\n // Stop the cron job\n if (this.cronJob) {\n this.cronJob.stop();\n this.cronJob = null;\n }\n\n // Clear any pending retry timeout\n if (this.retryTimeout) {\n clearTimeout(this.retryTimeout);\n this.retryTimeout = null;\n }\n\n if (this._state === \"idle\" || this._state === \"stopped\") {\n this._state = \"stopped\";\n return;\n }\n\n // If running, stop the current job\n if (this._state === \"running\" || this._state === \"retrying\" || this._state === \"queued\") {\n this._state = \"stopping\";\n await this.lazyProcess.stop(timeout);\n }\n\n this._state = \"stopped\";\n this.queuedRun = false;\n this.logger.info(`CronProcess stopped`);\n }\n\n async trigger(): Promise<void> {\n if (this.stopRequested) {\n throw new Error(`CronProcess \"${this.name}\" is stopped`);\n }\n\n // If already queued, just return (already have a run pending)\n if (this._state === \"queued\") {\n return;\n }\n\n // If already running, queue this trigger\n if (this._state === \"running\" || this._state === \"retrying\") {\n this.queuedRun = true;\n this._state = \"queued\";\n this.logger.info(`Run queued (current job still running)`);\n return;\n }\n\n await this.executeJob();\n }\n\n private onCronTick(): void {\n if (this.stopRequested) return;\n\n // If already running, queue the next run\n if (this._state === \"running\" || this._state === \"retrying\" || this._state === \"queued\") {\n this.queuedRun = true;\n if (this._state !== \"queued\") {\n this._state = \"queued\";\n }\n this.logger.info(`Cron tick: run queued (current job still running)`);\n return;\n }\n\n this.executeJob();\n }\n\n private async executeJob(): Promise<void> {\n if (this.stopRequested) return;\n\n this._state = \"running\";\n this.currentRetryAttempt = 0;\n this.logger.info(`Executing job`);\n\n await this.runJobWithRetry();\n }\n\n private async runJobWithRetry(): Promise<void> {\n if (this.stopRequested) return;\n\n // Reset and start the process\n await this.lazyProcess.reset();\n this.lazyProcess.start();\n\n const exitState = await this.lazyProcess.waitForExit();\n if (this.stopRequested && exitState === \"error\") {\n this._state = \"stopped\";\n return;\n }\n this.handleJobComplete(exitState === \"error\");\n }\n\n private handleJobComplete(failed: boolean): void {\n if (this.stopRequested) {\n this._state = \"stopped\";\n return;\n }\n\n if (failed) {\n const maxRetries = this.options.retry?.maxRetries ?? 0;\n\n if (this.currentRetryAttempt < maxRetries) {\n // Retry\n this.currentRetryAttempt++;\n this._state = \"retrying\";\n const delayMs = this.options.retry?.delayMs ?? DEFAULT_RETRY_DELAY;\n\n this.logger.warn(\n `Job failed, retrying in ${delayMs}ms (attempt ${this.currentRetryAttempt}/${maxRetries})`,\n );\n\n this.retryTimeout = setTimeout(() => {\n this.retryTimeout = null;\n if (this.stopRequested) {\n this._state = \"stopped\";\n return;\n }\n this.runJobWithRetry();\n }, delayMs);\n return;\n }\n\n // All retries exhausted\n this._failCount++;\n this.logger.error(`Job failed after ${this.currentRetryAttempt} retries`);\n } else {\n this._runCount++;\n this.logger.info(`Job completed successfully`);\n }\n\n // Check for queued run\n if (this.queuedRun) {\n this.queuedRun = false;\n this.logger.info(`Starting queued run`);\n this.executeJob();\n return;\n }\n\n // Back to scheduled state\n if (this.cronJob) {\n this._state = \"scheduled\";\n } else {\n this._state = \"stopped\";\n }\n }\n}\n","import * as v from \"valibot\";\nimport { LazyProcess, ProcessDefinitionSchema } from \"./lazy-process.ts\";\nimport type { Logger } from \"./logger.ts\";\n\n// Per-task state\nexport const TaskStateSchema = v.picklist([\"pending\", \"running\", \"completed\", \"failed\", \"skipped\"]);\n\nexport type TaskState = v.InferOutput<typeof TaskStateSchema>;\n\n// Schema for named process definition\nexport const NamedProcessDefinitionSchema = v.object({\n name: v.string(),\n process: ProcessDefinitionSchema,\n});\n\nexport type NamedProcessDefinition = v.InferOutput<typeof NamedProcessDefinitionSchema>;\n\n// A task entry (single or parallel processes) with its state\nexport interface TaskEntry {\n id: string; // Unique task ID\n processes: NamedProcessDefinition[]; // Array (length 1 = sequential, >1 = parallel)\n state: TaskState;\n}\n\n// Simple TaskList state (just running or not)\nexport type TaskListState = \"idle\" | \"running\" | \"stopped\";\n\nexport class TaskList {\n readonly name: string;\n private _tasks: TaskEntry[] = [];\n private _state: TaskListState = \"idle\";\n private logger: Logger;\n private logFileResolver?: (processName: string) => string | undefined;\n private taskIdCounter: number = 0;\n private runningProcesses: LazyProcess[] = [];\n private stopRequested: boolean = false;\n private runLoopPromise: Promise<void> | null = null;\n\n constructor(\n name: string,\n logger: Logger,\n initialTasks?: (NamedProcessDefinition | NamedProcessDefinition[])[],\n logFileResolver?: (processName: string) => string | undefined,\n ) {\n this.name = name;\n this.logger = logger;\n this.logFileResolver = logFileResolver;\n\n // Add initial tasks if provided\n if (initialTasks) {\n for (const task of initialTasks) {\n this.addTask(task);\n }\n }\n }\n\n get state(): TaskListState {\n return this._state;\n }\n\n get tasks(): ReadonlyArray<TaskEntry> {\n return this._tasks;\n }\n\n removeTaskByTarget(target: string | number): TaskEntry {\n const index =\n typeof target === \"number\" ? target : this._tasks.findIndex((t) => t.id === target);\n if (index < 0 || index >= this._tasks.length) {\n throw new Error(`Task not found: ${target}`);\n }\n\n const task = this._tasks[index];\n if (task.state === \"running\") {\n throw new Error(`Cannot remove running task: ${task.id}`);\n }\n\n this._tasks.splice(index, 1);\n this.logger.info(`Task \"${task.id}\" removed`);\n return task;\n }\n\n /**\n * Add a single process or parallel processes as a new task\n * @returns The unique task ID\n */\n addTask(task: NamedProcessDefinition | NamedProcessDefinition[]): string {\n const id = `task-${++this.taskIdCounter}`;\n const processes = Array.isArray(task) ? task : [task];\n\n const entry: TaskEntry = {\n id,\n processes,\n state: \"pending\",\n };\n\n this._tasks.push(entry);\n this.logger.info(`Task \"${id}\" added with ${processes.length} process(es)`);\n\n return id;\n }\n\n /**\n * Begin executing pending tasks\n */\n start(): void {\n if (this._state === \"running\") {\n throw new Error(`TaskList \"${this.name}\" is already running`);\n }\n\n this.stopRequested = false;\n this._state = \"running\";\n this.logger.info(`TaskList started`);\n\n // Start the run loop (non-blocking)\n this.runLoopPromise = this.runLoop();\n }\n\n /**\n * Wait until the TaskList becomes idle (all pending tasks completed)\n */\n async waitUntilIdle(): Promise<void> {\n if (this._state === \"idle\" || this._state === \"stopped\") {\n return;\n }\n\n // Wait for the run loop to complete\n if (this.runLoopPromise) {\n await this.runLoopPromise;\n }\n }\n\n /**\n * Stop execution and mark remaining tasks as skipped\n */\n async stop(timeout?: number): Promise<void> {\n if (this._state === \"idle\" || this._state === \"stopped\") {\n this._state = \"stopped\";\n return;\n }\n\n this.stopRequested = true;\n this.logger.info(`Stopping TaskList...`);\n\n // Stop all currently running processes\n const stopPromises = this.runningProcesses.map((p) => p.stop(timeout));\n await Promise.all(stopPromises);\n this.runningProcesses = [];\n\n // Mark all pending tasks as skipped\n for (const task of this._tasks) {\n if (task.state === \"pending\") {\n task.state = \"skipped\";\n }\n }\n\n // Wait for run loop to finish\n if (this.runLoopPromise) {\n await this.runLoopPromise;\n this.runLoopPromise = null;\n }\n\n this._state = \"stopped\";\n this.logger.info(`TaskList stopped`);\n }\n\n private async runLoop(): Promise<void> {\n while (this._state === \"running\" && !this.stopRequested) {\n // Find the next pending task\n const nextTask = this._tasks.find((t) => t.state === \"pending\");\n\n if (!nextTask) {\n // No more pending tasks, go back to idle\n this._state = \"idle\";\n this.logger.info(`All tasks completed, TaskList is idle`);\n break;\n }\n\n await this.executeTask(nextTask);\n }\n }\n\n private async executeTask(task: TaskEntry): Promise<void> {\n if (this.stopRequested) {\n task.state = \"skipped\";\n return;\n }\n\n task.state = \"running\";\n const taskNames = task.processes.map((p) => p.name).join(\", \");\n this.logger.info(`Executing task \"${task.id}\": [${taskNames}]`);\n\n // Create LazyProcess instances for each process in the task\n const lazyProcesses: LazyProcess[] = task.processes.map((p) => {\n const logFile = this.logFileResolver?.(p.name);\n const childLogger = logFile\n ? this.logger.child(p.name, { logFile })\n : this.logger.child(p.name);\n return new LazyProcess(p.name, p.process, childLogger);\n });\n\n this.runningProcesses = lazyProcesses;\n\n try {\n // Start all processes (parallel if multiple)\n for (const lp of lazyProcesses) {\n lp.start();\n }\n\n // Wait for all processes to complete\n const results = await Promise.all(lazyProcesses.map((lp) => this.waitForProcess(lp)));\n\n // Check if any failed\n const anyFailed = results.some((r) => r === \"error\");\n\n if (this.stopRequested) {\n task.state = \"skipped\";\n } else if (anyFailed) {\n task.state = \"failed\";\n this.logger.warn(`Task \"${task.id}\" failed`);\n } else {\n task.state = \"completed\";\n this.logger.info(`Task \"${task.id}\" completed`);\n }\n } catch (err) {\n task.state = \"failed\";\n this.logger.error(`Task \"${task.id}\" error:`, err);\n } finally {\n this.runningProcesses = [];\n }\n }\n\n private async waitForProcess(lp: LazyProcess): Promise<\"stopped\" | \"error\"> {\n const state = await lp.waitForExit();\n return state === \"error\" ? \"error\" : \"stopped\";\n }\n}\n","import { parse } from \"dotenv\";\nimport { existsSync, globSync, watch, readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve, join, basename } from \"node:path\";\n\nexport type EnvChangeCallback = (changedKeys: string[]) => void;\n\nexport interface EnvManagerConfig {\n /**\n * Directory to search for .env files\n * Defaults to process.cwd()\n */\n cwd?: string;\n\n /**\n * Explicit env file paths to load\n * Key is the identifier (e.g., \"global\", \"app1\")\n * Value is the file path relative to cwd or absolute\n */\n files?: Record<string, string>;\n\n /**\n * Enable file watching for env files\n * Defaults to false\n */\n watch?: boolean;\n}\n\nexport class EnvManager {\n private env: Map<string, Record<string, string>> = new Map();\n private cwd: string;\n private watchEnabled: boolean;\n private watchers: Map<string, ReturnType<typeof watch>> = new Map();\n private fileToKeys: Map<string, Set<string>> = new Map();\n private changeCallbacks: Set<EnvChangeCallback> = new Set();\n private reloadDebounceTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();\n\n constructor(config: EnvManagerConfig = {}) {\n this.cwd = config.cwd ?? process.cwd();\n this.watchEnabled = config.watch ?? false;\n\n // Load .env and .env.* files from cwd\n this.loadEnvFilesFromCwd();\n\n // Load explicitly specified files\n if (config.files) {\n for (const [key, filePath] of Object.entries(config.files)) {\n this.loadEnvFile(key, filePath);\n }\n }\n }\n\n registerFile(key: string, filePath: string): void {\n this.loadEnvFile(key, filePath);\n }\n\n getEnvForKey(key: string): Record<string, string> {\n return this.env.get(key) ?? {};\n }\n\n /**\n * Load .env and .env.* files from the cwd\n */\n private loadEnvFilesFromCwd(): void {\n // Load .env file as global\n const dotEnvPath = resolve(this.cwd, \".env\");\n if (existsSync(dotEnvPath)) {\n this.loadEnvFile(\"global\", dotEnvPath);\n }\n\n // Load .env.* files\n try {\n const pattern = join(this.cwd, \".env.*\");\n const envFiles = globSync(pattern);\n\n for (const filePath of envFiles) {\n // Extract the suffix after .env.\n const fileName = basename(filePath);\n const match = fileName.match(/^\\.env\\.(.+)$/);\n if (match) {\n const suffix = match[1];\n this.loadEnvFile(suffix, filePath);\n }\n }\n } catch (err) {\n console.warn(\"Failed to scan env files:\", err);\n }\n }\n\n /**\n * Load a single env file and store it in the map\n */\n private loadEnvFile(key: string, filePath: string): void {\n const absolutePath = resolve(this.cwd, filePath);\n\n if (!existsSync(absolutePath)) {\n return; // Silently skip non-existent files\n }\n\n try {\n const content = readFileSync(absolutePath, \"utf-8\");\n const parsed = parse(content);\n this.env.set(key, parsed);\n\n // Track which file maps to which key\n if (!this.fileToKeys.has(absolutePath)) {\n this.fileToKeys.set(absolutePath, new Set());\n }\n this.fileToKeys.get(absolutePath)!.add(key);\n\n // Start watching if enabled and not already watching\n if (this.watchEnabled && !this.watchers.has(absolutePath)) {\n this.watchFile(absolutePath);\n }\n } catch (err) {\n console.warn(`Failed to load env file: ${absolutePath}`, err);\n }\n }\n\n /**\n * Watch a file for changes\n */\n private watchFile(absolutePath: string): void {\n try {\n const watcher = watch(absolutePath, (eventType) => {\n if (eventType === \"change\") {\n this.handleFileChange(absolutePath);\n }\n });\n\n this.watchers.set(absolutePath, watcher);\n } catch (err) {\n console.warn(`Failed to watch env file: ${absolutePath}`, err);\n }\n }\n\n /**\n * Handle file change with debouncing\n */\n private handleFileChange(absolutePath: string): void {\n // Clear existing timer if any\n const existingTimer = this.reloadDebounceTimers.get(absolutePath);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Debounce reload by 100ms to avoid multiple rapid reloads\n const timer = setTimeout(() => {\n this.reloadFile(absolutePath);\n this.reloadDebounceTimers.delete(absolutePath);\n }, 100);\n\n this.reloadDebounceTimers.set(absolutePath, timer);\n }\n\n /**\n * Reload a file and notify callbacks\n */\n private reloadFile(absolutePath: string): void {\n const keys = this.fileToKeys.get(absolutePath);\n if (!keys) return;\n\n readFile(absolutePath, \"utf-8\")\n .then((content) => parse(content))\n .then((parsed) => {\n const changedKeys: string[] = [];\n for (const key of keys) {\n this.env.set(key, parsed);\n changedKeys.push(key);\n }\n\n // Notify all callbacks\n if (changedKeys.length > 0) {\n for (const callback of this.changeCallbacks) {\n callback(changedKeys);\n }\n }\n })\n .catch((err) => {\n console.warn(`Failed to reload env file: ${absolutePath}`, err);\n });\n }\n\n /**\n * Register a callback to be called when env files change\n * Returns a function to unregister the callback\n */\n onChange(callback: EnvChangeCallback): () => void {\n this.changeCallbacks.add(callback);\n return () => {\n this.changeCallbacks.delete(callback);\n };\n }\n\n /**\n * Stop watching all files and cleanup\n */\n dispose(): void {\n // Clear all timers\n for (const timer of this.reloadDebounceTimers.values()) {\n clearTimeout(timer);\n }\n this.reloadDebounceTimers.clear();\n\n // Close all watchers\n for (const watcher of this.watchers.values()) {\n watcher.close();\n }\n this.watchers.clear();\n\n // Clear callbacks\n this.changeCallbacks.clear();\n }\n\n /**\n * Get environment variables for a specific process\n * Merges global env with process-specific env\n * Process-specific env variables override global ones\n */\n getEnvVars(processKey?: string): Record<string, string> {\n const globalEnv = this.env.get(\"global\") ?? {};\n\n if (!processKey) {\n return { ...globalEnv };\n }\n\n const processEnv = this.env.get(processKey) ?? {};\n return { ...globalEnv, ...processEnv };\n }\n\n /**\n * Get all loaded env maps (for debugging/inspection)\n */\n getAllEnv(): ReadonlyMap<string, Record<string, string>> {\n return this.env;\n }\n}\n","import { appendFileSync } from \"node:fs\";\nimport { format } from \"node:util\";\n\nconst colors = {\n reset: \"\\x1b[0m\",\n gray: \"\\x1b[90m\",\n white: \"\\x1b[37m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n red: \"\\x1b[31m\",\n bold: \"\\x1b[1m\",\n} as const;\n\nconst levelColors = {\n debug: colors.gray,\n info: colors.green,\n warn: colors.yellow,\n error: colors.red,\n} as const;\n\nconst formatTime = (date: Date) =>\n Intl.DateTimeFormat(\"en-US\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n fractionalSecondDigits: 3,\n hourCycle: \"h23\",\n }).format(date);\n\nconst formatPrefixNoColor = (level: string, name: string, time: Date) => {\n const levelFormatted = level.toUpperCase().padStart(5);\n const timestamp = formatTime(time);\n return `[${timestamp}] ${levelFormatted} (${name})`;\n};\n\nconst formatPrefixWithColor = (level: string, name: string, time: Date) => {\n const levelFormatted = level.toUpperCase().padStart(5);\n const timestamp = formatTime(time);\n const levelTint = levelColors[level as keyof typeof levelColors] ?? \"\";\n return `${colors.gray}[${timestamp}]${colors.reset} ${levelTint}${levelFormatted}${colors.reset} (${name})`;\n};\n\ntype LoggerConfig = {\n name: string;\n stdout: boolean;\n logFile?: string;\n};\n\ntype LoggerInput = {\n name: string;\n stdout?: boolean;\n logFile?: string;\n};\n\nconst writeLogFile = (logFile: string | undefined, line: string) => {\n if (!logFile) return;\n appendFileSync(logFile, `${line}\\n`);\n};\n\nconst logLine = (config: LoggerConfig, level: \"debug\" | \"info\" | \"warn\" | \"error\", args: any[]) => {\n const message = args.length > 0 ? format(...args) : \"\";\n const time = new Date();\n const plainPrefix = formatPrefixNoColor(level, config.name, time);\n const plainLine = `${plainPrefix} ${message}`;\n\n writeLogFile(config.logFile, plainLine);\n\n if (!config.stdout) return;\n const coloredPrefix = formatPrefixWithColor(level, config.name, time);\n const coloredLine = `${coloredPrefix} ${message}`;\n\n switch (level) {\n case \"error\":\n console.error(coloredLine);\n break;\n case \"warn\":\n console.warn(coloredLine);\n break;\n case \"info\":\n console.info(coloredLine);\n break;\n default:\n console.debug(coloredLine);\n break;\n }\n};\n\nexport const logger = (input: LoggerInput) => {\n const config: LoggerConfig = {\n stdout: true,\n ...input,\n };\n\n return {\n info: (...args: any[]) => logLine(config, \"info\", args),\n error: (...args: any[]) => logLine(config, \"error\", args),\n warn: (...args: any[]) => logLine(config, \"warn\", args),\n debug: (...args: any[]) => logLine(config, \"debug\", args),\n child: (suffix: string, overrides: Partial<Omit<LoggerConfig, \"name\">> = {}) =>\n logger({\n ...config,\n ...overrides,\n name: `${config.name}:${suffix}`,\n }),\n };\n};\n\nexport type Logger = ReturnType<typeof logger>;\n"],"mappings":"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"}
@@ -0,0 +1,230 @@
1
+ import * as v from "valibot";
2
+
3
+ //#region src/logger.d.ts
4
+ type LoggerConfig = {
5
+ name: string;
6
+ stdout: boolean;
7
+ logFile?: string;
8
+ };
9
+ type LoggerInput = {
10
+ name: string;
11
+ stdout?: boolean;
12
+ logFile?: string;
13
+ };
14
+ declare const logger: (input: LoggerInput) => {
15
+ info: (...args: any[]) => void;
16
+ error: (...args: any[]) => void;
17
+ warn: (...args: any[]) => void;
18
+ debug: (...args: any[]) => void;
19
+ child: (suffix: string, overrides?: Partial<Omit<LoggerConfig, "name">>) => /*elided*/any;
20
+ };
21
+ type Logger = ReturnType<typeof logger>;
22
+ //#endregion
23
+ //#region src/lazy-process.d.ts
24
+ declare const ProcessDefinitionSchema: v.ObjectSchema<{
25
+ readonly command: v.StringSchema<undefined>;
26
+ readonly args: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
27
+ readonly cwd: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
28
+ readonly env: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.StringSchema<undefined>, undefined>, undefined>;
29
+ }, undefined>;
30
+ type ProcessDefinition = v.InferOutput<typeof ProcessDefinitionSchema>;
31
+ declare const ProcessStateSchema: v.PicklistSchema<["idle", "starting", "running", "stopping", "stopped", "error"], undefined>;
32
+ type ProcessState = v.InferOutput<typeof ProcessStateSchema>;
33
+ declare class LazyProcess {
34
+ readonly name: string;
35
+ private definition;
36
+ private logger;
37
+ private childProcess;
38
+ private _state;
39
+ private processExit;
40
+ exitCode: number | null;
41
+ constructor(name: string, definition: ProcessDefinition, logger: Logger);
42
+ get state(): ProcessState;
43
+ start(): void;
44
+ stop(timeout?: number): Promise<void>;
45
+ reset(): Promise<void>;
46
+ updateDefinition(definition: ProcessDefinition): void;
47
+ waitForExit(): Promise<ProcessState>;
48
+ private cleanup;
49
+ }
50
+ //#endregion
51
+ //#region src/restarting-process.d.ts
52
+ declare const RestartPolicySchema: v.PicklistSchema<["always", "on-failure", "never", "unless-stopped", "on-success"], undefined>;
53
+ type RestartPolicy = v.InferOutput<typeof RestartPolicySchema>;
54
+ declare const BackoffStrategySchema: v.UnionSchema<[v.ObjectSchema<{
55
+ readonly type: v.LiteralSchema<"fixed", undefined>;
56
+ readonly delayMs: v.NumberSchema<undefined>;
57
+ }, undefined>, v.ObjectSchema<{
58
+ readonly type: v.LiteralSchema<"exponential", undefined>;
59
+ readonly initialDelayMs: v.NumberSchema<undefined>;
60
+ readonly maxDelayMs: v.NumberSchema<undefined>;
61
+ readonly multiplier: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
62
+ }, undefined>], undefined>;
63
+ type BackoffStrategy = v.InferOutput<typeof BackoffStrategySchema>;
64
+ declare const CrashLoopConfigSchema: v.ObjectSchema<{
65
+ readonly maxRestarts: v.NumberSchema<undefined>;
66
+ readonly windowMs: v.NumberSchema<undefined>;
67
+ readonly backoffMs: v.NumberSchema<undefined>;
68
+ }, undefined>;
69
+ type CrashLoopConfig = v.InferOutput<typeof CrashLoopConfigSchema>;
70
+ declare const RestartingProcessOptionsSchema: v.ObjectSchema<{
71
+ readonly restartPolicy: v.PicklistSchema<["always", "on-failure", "never", "unless-stopped", "on-success"], undefined>;
72
+ readonly backoff: v.OptionalSchema<v.UnionSchema<[v.ObjectSchema<{
73
+ readonly type: v.LiteralSchema<"fixed", undefined>;
74
+ readonly delayMs: v.NumberSchema<undefined>;
75
+ }, undefined>, v.ObjectSchema<{
76
+ readonly type: v.LiteralSchema<"exponential", undefined>;
77
+ readonly initialDelayMs: v.NumberSchema<undefined>;
78
+ readonly maxDelayMs: v.NumberSchema<undefined>;
79
+ readonly multiplier: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
80
+ }, undefined>], undefined>, undefined>;
81
+ readonly crashLoop: v.OptionalSchema<v.ObjectSchema<{
82
+ readonly maxRestarts: v.NumberSchema<undefined>;
83
+ readonly windowMs: v.NumberSchema<undefined>;
84
+ readonly backoffMs: v.NumberSchema<undefined>;
85
+ }, undefined>, undefined>;
86
+ readonly minUptimeMs: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
87
+ readonly maxTotalRestarts: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
88
+ }, undefined>;
89
+ type RestartingProcessOptions = v.InferOutput<typeof RestartingProcessOptionsSchema>;
90
+ declare const RestartingProcessStateSchema: v.PicklistSchema<["idle", "running", "restarting", "stopping", "stopped", "crash-loop-backoff", "max-restarts-reached"], undefined>;
91
+ type RestartingProcessState = v.InferOutput<typeof RestartingProcessStateSchema>;
92
+ declare class RestartingProcess {
93
+ readonly name: string;
94
+ private lazyProcess;
95
+ private definition;
96
+ private options;
97
+ private logger;
98
+ private _state;
99
+ private _restartCount;
100
+ private restartTimestamps;
101
+ private consecutiveFailures;
102
+ private lastStartTime;
103
+ private stopRequested;
104
+ private pendingDelayTimeout;
105
+ constructor(name: string, definition: ProcessDefinition, options: RestartingProcessOptions, logger: Logger);
106
+ get state(): RestartingProcessState;
107
+ get restarts(): number;
108
+ start(): void;
109
+ stop(timeout?: number): Promise<void>;
110
+ restart(force?: boolean): Promise<void>;
111
+ /**
112
+ * Update process definition and optionally restart with new config
113
+ */
114
+ reload(newDefinition: ProcessDefinition, restartImmediately?: boolean): Promise<void>;
115
+ /**
116
+ * Update restart options
117
+ */
118
+ updateOptions(newOptions: Partial<RestartingProcessOptions>): void;
119
+ private resetCounters;
120
+ private startProcess;
121
+ private handleProcessExit;
122
+ private shouldRestart;
123
+ private isInCrashLoop;
124
+ private calculateDelay;
125
+ private scheduleRestart;
126
+ private scheduleCrashLoopRecovery;
127
+ private delay;
128
+ }
129
+ //#endregion
130
+ //#region src/cron-process.d.ts
131
+ declare const RetryConfigSchema: v.ObjectSchema<{
132
+ readonly maxRetries: v.NumberSchema<undefined>;
133
+ readonly delayMs: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
134
+ }, undefined>;
135
+ type RetryConfig = v.InferOutput<typeof RetryConfigSchema>;
136
+ declare const CronProcessOptionsSchema: v.ObjectSchema<{
137
+ readonly schedule: v.StringSchema<undefined>;
138
+ readonly retry: v.OptionalSchema<v.ObjectSchema<{
139
+ readonly maxRetries: v.NumberSchema<undefined>;
140
+ readonly delayMs: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
141
+ }, undefined>, undefined>;
142
+ readonly runOnStart: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
143
+ }, undefined>;
144
+ type CronProcessOptions = v.InferOutput<typeof CronProcessOptionsSchema>;
145
+ declare const CronProcessStateSchema: v.PicklistSchema<["idle", "scheduled", "running", "retrying", "queued", "stopping", "stopped"], undefined>;
146
+ type CronProcessState = v.InferOutput<typeof CronProcessStateSchema>;
147
+ declare class CronProcess {
148
+ readonly name: string;
149
+ private lazyProcess;
150
+ private options;
151
+ private logger;
152
+ private cronJob;
153
+ private _state;
154
+ private _runCount;
155
+ private _failCount;
156
+ private currentRetryAttempt;
157
+ private queuedRun;
158
+ private stopRequested;
159
+ private retryTimeout;
160
+ constructor(name: string, definition: ProcessDefinition, options: CronProcessOptions, logger: Logger);
161
+ get state(): CronProcessState;
162
+ get runCount(): number;
163
+ get failCount(): number;
164
+ get nextRun(): Date | null;
165
+ start(): void;
166
+ stop(timeout?: number): Promise<void>;
167
+ trigger(): Promise<void>;
168
+ private onCronTick;
169
+ private executeJob;
170
+ private runJobWithRetry;
171
+ private handleJobComplete;
172
+ }
173
+ //#endregion
174
+ //#region src/task-list.d.ts
175
+ declare const TaskStateSchema: v.PicklistSchema<["pending", "running", "completed", "failed", "skipped"], undefined>;
176
+ type TaskState = v.InferOutput<typeof TaskStateSchema>;
177
+ declare const NamedProcessDefinitionSchema: v.ObjectSchema<{
178
+ readonly name: v.StringSchema<undefined>;
179
+ readonly process: v.ObjectSchema<{
180
+ readonly command: v.StringSchema<undefined>;
181
+ readonly args: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
182
+ readonly cwd: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
183
+ readonly env: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.StringSchema<undefined>, undefined>, undefined>;
184
+ }, undefined>;
185
+ }, undefined>;
186
+ type NamedProcessDefinition = v.InferOutput<typeof NamedProcessDefinitionSchema>;
187
+ interface TaskEntry {
188
+ id: string;
189
+ processes: NamedProcessDefinition[];
190
+ state: TaskState;
191
+ }
192
+ type TaskListState = "idle" | "running" | "stopped";
193
+ declare class TaskList {
194
+ readonly name: string;
195
+ private _tasks;
196
+ private _state;
197
+ private logger;
198
+ private logFileResolver?;
199
+ private taskIdCounter;
200
+ private runningProcesses;
201
+ private stopRequested;
202
+ private runLoopPromise;
203
+ constructor(name: string, logger: Logger, initialTasks?: (NamedProcessDefinition | NamedProcessDefinition[])[], logFileResolver?: (processName: string) => string | undefined);
204
+ get state(): TaskListState;
205
+ get tasks(): ReadonlyArray<TaskEntry>;
206
+ removeTaskByTarget(target: string | number): TaskEntry;
207
+ /**
208
+ * Add a single process or parallel processes as a new task
209
+ * @returns The unique task ID
210
+ */
211
+ addTask(task: NamedProcessDefinition | NamedProcessDefinition[]): string;
212
+ /**
213
+ * Begin executing pending tasks
214
+ */
215
+ start(): void;
216
+ /**
217
+ * Wait until the TaskList becomes idle (all pending tasks completed)
218
+ */
219
+ waitUntilIdle(): Promise<void>;
220
+ /**
221
+ * Stop execution and mark remaining tasks as skipped
222
+ */
223
+ stop(timeout?: number): Promise<void>;
224
+ private runLoop;
225
+ private executeTask;
226
+ private waitForProcess;
227
+ }
228
+ //#endregion
229
+ export { ProcessStateSchema as A, RestartingProcessOptionsSchema as C, ProcessDefinition as D, LazyProcess as E, logger as M, ProcessDefinitionSchema as O, RestartingProcessOptions as S, RestartingProcessStateSchema as T, CrashLoopConfig as _, TaskListState as a, RestartPolicySchema as b, CronProcess as c, CronProcessState as d, CronProcessStateSchema as f, BackoffStrategySchema as g, BackoffStrategy as h, TaskList as i, Logger as j, ProcessState as k, CronProcessOptions as l, RetryConfigSchema as m, NamedProcessDefinitionSchema as n, TaskState as o, RetryConfig as p, TaskEntry as r, TaskStateSchema as s, NamedProcessDefinition as t, CronProcessOptionsSchema as u, CrashLoopConfigSchema as v, RestartingProcessState as w, RestartingProcess as x, RestartPolicy as y };
230
+ //# sourceMappingURL=task-list-CIdbB3wM.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-list-CIdbB3wM.d.mts","names":[],"sources":["../src/logger.ts","../src/lazy-process.ts","../src/restarting-process.ts","../src/cron-process.ts","../src/task-list.ts"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "pidnap",
3
+ "version": "0.0.0-dev.0",
4
+ "bin": {
5
+ "pidnap": "./dist/cli.mjs"
6
+ },
7
+ "files": [
8
+ "dist",
9
+ "src"
10
+ ],
11
+ "type": "module",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.mjs",
15
+ "types": "./dist/index.d.mts"
16
+ },
17
+ "./client": {
18
+ "import": "./dist/client.mjs",
19
+ "types": "./dist/client.d.mts"
20
+ }
21
+ },
22
+ "dependencies": {
23
+ "@orpc/client": "^1.13.4",
24
+ "@orpc/contract": "^1.13.4",
25
+ "@orpc/server": "^1.13.4",
26
+ "cli-table3": "^0.6.5",
27
+ "commander": "^14.0.2",
28
+ "croner": "^9.1.0",
29
+ "dotenv": "^17.2.3",
30
+ "tsx": "^4.21.0",
31
+ "valibot": "^1.2.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^25.0.10",
35
+ "oxfmt": "^0.26.0",
36
+ "oxlint": "^1.41.0",
37
+ "tsdown": "^0.20.1",
38
+ "typescript": "^5.9.3",
39
+ "vitest": "^4.0.18"
40
+ },
41
+ "scripts": {
42
+ "dev": "tsx src/cli.ts",
43
+ "build": "tsdown",
44
+ "typecheck": "tsc --noEmit",
45
+ "format": "oxfmt",
46
+ "lint": "oxlint",
47
+ "test": "vitest",
48
+ "docker:dev": "docker build -t pidnap -f Dockerfile.example . && docker run --rm pidnap"
49
+ }
50
+ }
@@ -0,0 +1,13 @@
1
+ import { createORPCClient } from "@orpc/client";
2
+ import { RPCLink } from "@orpc/client/fetch";
3
+ import type { ContractRouterClient } from "@orpc/contract";
4
+ import type { api } from "./contract.ts";
5
+
6
+ export function createClient(
7
+ url: string = "http://localhost:3000/rpc",
8
+ ): ContractRouterClient<typeof api> {
9
+ const authToken = process.env.PID1_AUTH_TOKEN;
10
+ const headers = authToken ? { Authorization: `Bearer ${authToken}` } : undefined;
11
+ const link = new RPCLink({ url, headers });
12
+ return createORPCClient(link);
13
+ }
@@ -0,0 +1,117 @@
1
+ import { oc as ocBase } from "@orpc/contract";
2
+ import * as v from "valibot";
3
+ import { ProcessDefinitionSchema } from "../lazy-process.ts";
4
+ import { RestartingProcessStateSchema } from "../restarting-process.ts";
5
+ import { CronProcessStateSchema } from "../cron-process.ts";
6
+ import { TaskStateSchema } from "../task-list.ts";
7
+
8
+ // Re-export schemas for use in other modules
9
+ export { ProcessDefinitionSchema } from "../lazy-process.ts";
10
+ export { RestartingProcessStateSchema } from "../restarting-process.ts";
11
+ export { CronProcessStateSchema } from "../cron-process.ts";
12
+ export { TaskStateSchema } from "../task-list.ts";
13
+
14
+ const oc = ocBase.$input(v.void());
15
+
16
+ // Resource target (name or index)
17
+ const ResourceTarget = v.union([v.string(), v.number()]);
18
+
19
+ // Manager state schema
20
+ export const ManagerStateSchema = v.picklist([
21
+ "idle",
22
+ "initializing",
23
+ "running",
24
+ "stopping",
25
+ "stopped",
26
+ ]);
27
+
28
+ export type ManagerState = v.InferOutput<typeof ManagerStateSchema>;
29
+
30
+ // Manager status response
31
+ export const ManagerStatusSchema = v.object({
32
+ state: ManagerStateSchema,
33
+ processCount: v.number(),
34
+ cronCount: v.number(),
35
+ taskCount: v.number(),
36
+ });
37
+
38
+ export type ManagerStatus = v.InferOutput<typeof ManagerStatusSchema>;
39
+
40
+ // API response schemas
41
+ export const RestartingProcessInfoSchema = v.object({
42
+ name: v.string(),
43
+ state: RestartingProcessStateSchema,
44
+ restarts: v.number(),
45
+ });
46
+
47
+ export type RestartingProcessInfo = v.InferOutput<typeof RestartingProcessInfoSchema>;
48
+
49
+ export const CronProcessInfoSchema = v.object({
50
+ name: v.string(),
51
+ state: CronProcessStateSchema,
52
+ runCount: v.number(),
53
+ failCount: v.number(),
54
+ nextRun: v.nullable(v.string()), // ISO date string
55
+ });
56
+
57
+ export type CronProcessInfo = v.InferOutput<typeof CronProcessInfoSchema>;
58
+
59
+ export const TaskEntryInfoSchema = v.object({
60
+ id: v.string(),
61
+ state: TaskStateSchema,
62
+ processNames: v.array(v.string()),
63
+ });
64
+
65
+ export type TaskEntryInfo = v.InferOutput<typeof TaskEntryInfoSchema>;
66
+
67
+ // API contract
68
+ export const manager = {
69
+ status: oc.output(ManagerStatusSchema),
70
+ };
71
+
72
+ export const processes = {
73
+ get: oc.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),
74
+ list: oc.output(v.array(RestartingProcessInfoSchema)),
75
+ add: oc
76
+ .input(v.object({ name: v.string(), definition: ProcessDefinitionSchema }))
77
+ .output(RestartingProcessInfoSchema),
78
+ start: oc.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),
79
+ stop: oc.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),
80
+ restart: oc
81
+ .input(v.object({ target: ResourceTarget, force: v.optional(v.boolean()) }))
82
+ .output(RestartingProcessInfoSchema),
83
+ reload: oc
84
+ .input(
85
+ v.object({
86
+ target: ResourceTarget,
87
+ definition: ProcessDefinitionSchema,
88
+ restartImmediately: v.optional(v.boolean()),
89
+ }),
90
+ )
91
+ .output(RestartingProcessInfoSchema),
92
+ remove: oc.input(v.object({ target: ResourceTarget })).output(v.object({ success: v.boolean() })),
93
+ };
94
+
95
+ export const tasks = {
96
+ get: oc.input(v.object({ target: ResourceTarget })).output(TaskEntryInfoSchema),
97
+ list: oc.output(v.array(TaskEntryInfoSchema)),
98
+ add: oc
99
+ .input(v.object({ name: v.string(), definition: ProcessDefinitionSchema }))
100
+ .output(TaskEntryInfoSchema),
101
+ remove: oc.input(v.object({ target: ResourceTarget })).output(TaskEntryInfoSchema),
102
+ };
103
+
104
+ export const crons = {
105
+ get: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),
106
+ list: oc.output(v.array(CronProcessInfoSchema)),
107
+ trigger: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),
108
+ start: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),
109
+ stop: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),
110
+ };
111
+
112
+ export const api = {
113
+ manager,
114
+ processes,
115
+ tasks,
116
+ crons,
117
+ };