pidnap 0.0.0-dev.0 → 0.0.0-dev.2

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.
@@ -9,6 +9,91 @@ import { parse } from "dotenv";
9
9
  import { readFile } from "node:fs/promises";
10
10
  import { format } from "node:util";
11
11
 
12
+ //#region src/tree-kill.ts
13
+ /**
14
+ * Kill a process and all its descendants (children, grandchildren, etc.)
15
+ * @param pid - The process ID to kill
16
+ * @param signal - The signal to send (default: SIGTERM)
17
+ * @returns Promise that resolves when all processes have been signaled
18
+ */
19
+ async function treeKill(pid, signal = "SIGTERM") {
20
+ if (Number.isNaN(pid)) throw new Error("pid must be a number");
21
+ const tree = { [pid]: [] };
22
+ await buildProcessTree(pid, tree, new Set([pid]));
23
+ killAll(tree, signal);
24
+ }
25
+ /**
26
+ * Build a tree of all child processes recursively
27
+ */
28
+ async function buildProcessTree(parentPid, tree, pidsToProcess) {
29
+ return new Promise((resolve) => {
30
+ const ps = spawn("ps", [
31
+ "-o",
32
+ "pid",
33
+ "--no-headers",
34
+ "--ppid",
35
+ String(parentPid)
36
+ ]);
37
+ let allData = "";
38
+ ps.stdout.on("data", (data) => {
39
+ allData += data.toString("ascii");
40
+ });
41
+ ps.on("close", async (code) => {
42
+ pidsToProcess.delete(parentPid);
43
+ if (code !== 0) {
44
+ if (pidsToProcess.size === 0) resolve();
45
+ return;
46
+ }
47
+ const childPids = allData.match(/\d+/g);
48
+ if (!childPids) {
49
+ if (pidsToProcess.size === 0) resolve();
50
+ return;
51
+ }
52
+ const promises = [];
53
+ for (const pidStr of childPids) {
54
+ const childPid = parseInt(pidStr, 10);
55
+ tree[parentPid].push(childPid);
56
+ tree[childPid] = [];
57
+ pidsToProcess.add(childPid);
58
+ promises.push(buildProcessTree(childPid, tree, pidsToProcess));
59
+ }
60
+ await Promise.all(promises);
61
+ if (pidsToProcess.size === 0) resolve();
62
+ });
63
+ ps.on("error", () => {
64
+ pidsToProcess.delete(parentPid);
65
+ if (pidsToProcess.size === 0) resolve();
66
+ });
67
+ });
68
+ }
69
+ /**
70
+ * Kill all processes in the tree
71
+ * Kills children before parents to ensure clean shutdown
72
+ */
73
+ function killAll(tree, signal) {
74
+ const killed = /* @__PURE__ */ new Set();
75
+ const allPids = Object.keys(tree).map(Number);
76
+ for (const pid of allPids) for (const childPid of tree[pid]) if (!killed.has(childPid)) {
77
+ killPid(childPid, signal);
78
+ killed.add(childPid);
79
+ }
80
+ for (const pid of allPids) if (!killed.has(pid)) {
81
+ killPid(pid, signal);
82
+ killed.add(pid);
83
+ }
84
+ }
85
+ /**
86
+ * Kill a single process, ignoring ESRCH errors (process already dead)
87
+ */
88
+ function killPid(pid, signal) {
89
+ try {
90
+ process.kill(pid, signal);
91
+ } catch (err) {
92
+ if (err.code !== "ESRCH") throw err;
93
+ }
94
+ }
95
+
96
+ //#endregion
12
97
  //#region src/lazy-process.ts
13
98
  const ProcessDefinitionSchema = v.object({
14
99
  command: v.string(),
@@ -25,14 +110,21 @@ const ProcessStateSchema = v.picklist([
25
110
  "error"
26
111
  ]);
27
112
  /**
28
- * Kill a process. Tries to kill the process group first (if available),
29
- * then falls back to killing just the process.
113
+ * Kill a process and all its descendants (children, grandchildren, etc.)
114
+ * Falls back to simple child.kill() if tree kill fails.
30
115
  */
31
- function killProcess(child, signal) {
116
+ async function killProcessTree(child, signal) {
117
+ const pid = child.pid;
118
+ if (pid === void 0) return false;
32
119
  try {
33
- return child.kill(signal);
120
+ await treeKill(pid, signal);
121
+ return true;
34
122
  } catch {
35
- return false;
123
+ try {
124
+ return child.kill(signal);
125
+ } catch {
126
+ return false;
127
+ }
36
128
  }
37
129
  }
38
130
  var LazyProcess = class {
@@ -129,12 +221,12 @@ var LazyProcess = class {
129
221
  }
130
222
  this._state = "stopping";
131
223
  this.logger.info(`Stopping process with SIGTERM`);
132
- killProcess(this.childProcess, "SIGTERM");
224
+ await killProcessTree(this.childProcess, "SIGTERM");
133
225
  const timeoutMs = timeout ?? 5e3;
134
226
  const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve("timeout"), timeoutMs));
135
227
  if (await Promise.race([this.processExit.promise.then(() => "exited"), timeoutPromise]) === "timeout" && this.childProcess) {
136
228
  this.logger.warn(`Process did not exit within ${timeoutMs}ms, sending SIGKILL`);
137
- killProcess(this.childProcess, "SIGKILL");
229
+ await killProcessTree(this.childProcess, "SIGKILL");
138
230
  const killTimeout = new Promise((resolve) => setTimeout(resolve, 1e3));
139
231
  await Promise.race([this.processExit.promise, killTimeout]);
140
232
  }
@@ -144,7 +236,7 @@ var LazyProcess = class {
144
236
  }
145
237
  async reset() {
146
238
  if (this.childProcess) {
147
- killProcess(this.childProcess, "SIGKILL");
239
+ await killProcessTree(this.childProcess, "SIGKILL");
148
240
  await this.processExit.promise;
149
241
  this.cleanup();
150
242
  }
@@ -962,5 +1054,5 @@ const logger = (input) => {
962
1054
  };
963
1055
 
964
1056
  //#endregion
965
- export { ProcessDefinitionSchema as _, TaskStateSchema as a, CronProcessStateSchema as c, CrashLoopConfigSchema as d, RestartPolicySchema as f, LazyProcess as g, RestartingProcessStateSchema as h, TaskList as i, RetryConfigSchema as l, RestartingProcessOptionsSchema as m, EnvManager as n, CronProcess as o, RestartingProcess as p, NamedProcessDefinitionSchema as r, CronProcessOptionsSchema as s, logger as t, BackoffStrategySchema as u, ProcessStateSchema as v };
966
- //# sourceMappingURL=logger-crc5neL8.mjs.map
1057
+ export { ProcessDefinitionSchema as _, TaskStateSchema as a, CronProcessStateSchema as c, CrashLoopConfigSchema as d, RestartPolicySchema as f, LazyProcess as g, RestartingProcessStateSchema as h, TaskList as i, RetryConfigSchema as l, RestartingProcessOptionsSchema as m, EnvManager as n, CronProcess as o, RestartingProcess as p, NamedProcessDefinitionSchema as r, CronProcessOptionsSchema as s, logger as t, BackoffStrategySchema as u, ProcessStateSchema as v, treeKill as y };
1058
+ //# sourceMappingURL=logger-BF3KrCIK.mjs.map
@@ -1 +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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"}
1
+ {"version":3,"file":"logger-BF3KrCIK.mjs","names":[],"sources":["../src/tree-kill.ts","../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 } from \"node:child_process\";\n\ntype ProcessTree = Record<number, number[]>;\n\n/**\n * Kill a process and all its descendants (children, grandchildren, etc.)\n * @param pid - The process ID to kill\n * @param signal - The signal to send (default: SIGTERM)\n * @returns Promise that resolves when all processes have been signaled\n */\nexport async function treeKill(pid: number, signal: NodeJS.Signals = \"SIGTERM\"): Promise<void> {\n if (Number.isNaN(pid)) {\n throw new Error(\"pid must be a number\");\n }\n\n const tree: ProcessTree = { [pid]: [] };\n const pidsToProcess = new Set<number>([pid]);\n\n // Build the process tree\n await buildProcessTree(pid, tree, pidsToProcess);\n\n // Kill all processes in the tree (children first, then parents)\n killAll(tree, signal);\n}\n\n/**\n * Build a tree of all child processes recursively\n */\nasync function buildProcessTree(\n parentPid: number,\n tree: ProcessTree,\n pidsToProcess: Set<number>,\n): Promise<void> {\n return new Promise((resolve) => {\n const ps = spawn(\"ps\", [\"-o\", \"pid\", \"--no-headers\", \"--ppid\", String(parentPid)]);\n\n let allData = \"\";\n\n ps.stdout.on(\"data\", (data: Buffer) => {\n allData += data.toString(\"ascii\");\n });\n\n ps.on(\"close\", async (code) => {\n pidsToProcess.delete(parentPid);\n\n if (code !== 0) {\n // No child processes found for this parent\n if (pidsToProcess.size === 0) {\n resolve();\n }\n return;\n }\n\n const childPids = allData.match(/\\d+/g);\n if (!childPids) {\n if (pidsToProcess.size === 0) {\n resolve();\n }\n return;\n }\n\n // Process all child PIDs concurrently\n const promises: Promise<void>[] = [];\n\n for (const pidStr of childPids) {\n const childPid = parseInt(pidStr, 10);\n tree[parentPid].push(childPid);\n tree[childPid] = [];\n pidsToProcess.add(childPid);\n promises.push(buildProcessTree(childPid, tree, pidsToProcess));\n }\n\n await Promise.all(promises);\n\n if (pidsToProcess.size === 0) {\n resolve();\n }\n });\n\n ps.on(\"error\", () => {\n pidsToProcess.delete(parentPid);\n if (pidsToProcess.size === 0) {\n resolve();\n }\n });\n });\n}\n\n/**\n * Kill all processes in the tree\n * Kills children before parents to ensure clean shutdown\n */\nfunction killAll(tree: ProcessTree, signal: NodeJS.Signals): void {\n const killed = new Set<number>();\n\n // Get all PIDs and sort by depth (deepest first)\n const allPids = Object.keys(tree).map(Number);\n\n // Kill children first, then parents\n for (const pid of allPids) {\n // Kill all children of this pid\n for (const childPid of tree[pid]) {\n if (!killed.has(childPid)) {\n killPid(childPid, signal);\n killed.add(childPid);\n }\n }\n }\n\n // Kill all parent pids\n for (const pid of allPids) {\n if (!killed.has(pid)) {\n killPid(pid, signal);\n killed.add(pid);\n }\n }\n}\n\n/**\n * Kill a single process, ignoring ESRCH errors (process already dead)\n */\nfunction killPid(pid: number, signal: NodeJS.Signals): void {\n try {\n process.kill(pid, signal);\n } catch (err) {\n // ESRCH = No such process (already dead) - ignore this\n if ((err as NodeJS.ErrnoException).code !== \"ESRCH\") {\n throw err;\n }\n }\n}\n","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\";\nimport { treeKill } from \"./tree-kill.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 and all its descendants (children, grandchildren, etc.)\n * Falls back to simple child.kill() if tree kill fails.\n */\nasync function killProcessTree(\n child: ChildProcess,\n signal: NodeJS.Signals,\n): Promise<boolean> {\n const pid = child.pid;\n if (pid === undefined) {\n return false;\n }\n\n try {\n await treeKill(pid, signal);\n return true;\n } catch {\n // Fallback to simple kill if tree kill fails\n try {\n return child.kill(signal);\n } catch {\n return false;\n }\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 tree)\n await killProcessTree(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 await killProcessTree(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 entire process tree if running\n await killProcessTree(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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"}
@@ -1 +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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"}
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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pidnap",
3
- "version": "0.0.0-dev.0",
3
+ "version": "0.0.0-dev.2",
4
4
  "bin": {
5
5
  "pidnap": "./dist/cli.mjs"
6
6
  },
package/src/api/server.ts CHANGED
@@ -59,12 +59,12 @@ const listProcesses = os.processes.list.handler(async ({ context }) => {
59
59
  });
60
60
 
61
61
  const addProcess = os.processes.add.handler(async ({ input, context }) => {
62
- const proc = context.manager.addProcess(input.name, input.definition);
62
+ const proc = await context.manager.addProcess(input.name, input.definition);
63
63
  return serializeProcess(proc);
64
64
  });
65
65
 
66
66
  const startProcess = os.processes.start.handler(async ({ input, context }) => {
67
- const proc = context.manager.startProcessByTarget(input.target);
67
+ const proc = await context.manager.startProcessByTarget(input.target);
68
68
  return serializeProcess(proc);
69
69
  });
70
70
 
package/src/index.ts CHANGED
@@ -10,3 +10,4 @@ export * from "./task-list.ts";
10
10
  export * from "./lazy-process.ts";
11
11
  export * from "./env-manager.ts";
12
12
  export * from "./logger.ts";
13
+ export * from "./tree-kill.ts";
@@ -3,6 +3,7 @@ import readline from "node:readline";
3
3
  import { PassThrough } from "node:stream";
4
4
  import * as v from "valibot";
5
5
  import type { Logger } from "./logger.ts";
6
+ import { treeKill } from "./tree-kill.ts";
6
7
 
7
8
  export const ProcessDefinitionSchema = v.object({
8
9
  command: v.string(),
@@ -25,14 +26,28 @@ export const ProcessStateSchema = v.picklist([
25
26
  export type ProcessState = v.InferOutput<typeof ProcessStateSchema>;
26
27
 
27
28
  /**
28
- * Kill a process. Tries to kill the process group first (if available),
29
- * then falls back to killing just the process.
29
+ * Kill a process and all its descendants (children, grandchildren, etc.)
30
+ * Falls back to simple child.kill() if tree kill fails.
30
31
  */
31
- function killProcess(child: ChildProcess, signal: NodeJS.Signals): boolean {
32
+ async function killProcessTree(
33
+ child: ChildProcess,
34
+ signal: NodeJS.Signals,
35
+ ): Promise<boolean> {
36
+ const pid = child.pid;
37
+ if (pid === undefined) {
38
+ return false;
39
+ }
40
+
32
41
  try {
33
- return child.kill(signal);
42
+ await treeKill(pid, signal);
43
+ return true;
34
44
  } catch {
35
- return false;
45
+ // Fallback to simple kill if tree kill fails
46
+ try {
47
+ return child.kill(signal);
48
+ } catch {
49
+ return false;
50
+ }
36
51
  }
37
52
  }
38
53
 
@@ -158,8 +173,8 @@ export class LazyProcess {
158
173
  this._state = "stopping";
159
174
  this.logger.info(`Stopping process with SIGTERM`);
160
175
 
161
- // Send SIGTERM for graceful shutdown (to entire process group)
162
- killProcess(this.childProcess, "SIGTERM");
176
+ // Send SIGTERM for graceful shutdown (to entire process tree)
177
+ await killProcessTree(this.childProcess, "SIGTERM");
163
178
 
164
179
  const timeoutMs = timeout ?? 5000;
165
180
 
@@ -175,7 +190,7 @@ export class LazyProcess {
175
190
 
176
191
  if (result === "timeout" && this.childProcess) {
177
192
  this.logger.warn(`Process did not exit within ${timeoutMs}ms, sending SIGKILL`);
178
- killProcess(this.childProcess, "SIGKILL");
193
+ await killProcessTree(this.childProcess, "SIGKILL");
179
194
 
180
195
  // Give SIGKILL a short timeout
181
196
  const killTimeout = new Promise<void>((resolve) => setTimeout(resolve, 1000));
@@ -189,8 +204,8 @@ export class LazyProcess {
189
204
 
190
205
  async reset(): Promise<void> {
191
206
  if (this.childProcess) {
192
- // Kill the process group if running
193
- killProcess(this.childProcess, "SIGKILL");
207
+ // Kill the entire process tree if running
208
+ await killProcessTree(this.childProcess, "SIGKILL");
194
209
  await this.processExit.promise;
195
210
  this.cleanup();
196
211
  }