pidnap 0.0.0-dev.1 → 0.0.0-dev.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/cli.mjs +9 -28
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{logger-uliALovz.mjs → logger-BF3KrCIK.mjs} +5 -6
- package/dist/logger-BF3KrCIK.mjs.map +1 -0
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/port-utils.ts +19 -56
- package/src/tree-kill.ts +3 -10
- package/dist/logger-uliALovz.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -11,7 +11,13 @@ pnpm install
|
|
|
11
11
|
## Run
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
pidnap init
|
|
14
|
+
pidnap init
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
By default, this looks for `pidnap.config.ts` in the current directory. To use a different config file:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pidnap init --config path/to/config.ts
|
|
15
21
|
```
|
|
16
22
|
|
|
17
23
|
## CLI
|
|
@@ -19,7 +25,7 @@ pidnap init -c pidnap.config.ts
|
|
|
19
25
|
```bash
|
|
20
26
|
pidnap status
|
|
21
27
|
pidnap processes list
|
|
22
|
-
pidnap processes add
|
|
28
|
+
pidnap processes add --name api --definition '{"command":"node","args":["server.js"]}'
|
|
23
29
|
pidnap processes restart api
|
|
24
30
|
pidnap crons list
|
|
25
31
|
pidnap tasks list
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { _ as ProcessDefinitionSchema, a as TaskStateSchema, c as CronProcessStateSchema, h as RestartingProcessStateSchema, i as TaskList, m as RestartingProcessOptionsSchema, n as EnvManager, o as CronProcess, p as RestartingProcess, s as CronProcessOptionsSchema, t as logger, y as treeKill } from "./logger-
|
|
2
|
+
import { _ as ProcessDefinitionSchema, a as TaskStateSchema, c as CronProcessStateSchema, h as RestartingProcessStateSchema, i as TaskList, m as RestartingProcessOptionsSchema, n as EnvManager, o as CronProcess, p as RestartingProcess, s as CronProcessOptionsSchema, t as logger, y as treeKill } from "./logger-BF3KrCIK.mjs";
|
|
3
3
|
import { createClient } from "./client.mjs";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { createServer } from "node:http";
|
|
@@ -218,35 +218,16 @@ const router = os.router({
|
|
|
218
218
|
const execAsync = promisify(exec);
|
|
219
219
|
/**
|
|
220
220
|
* Kill any process listening on the specified port, including all descendants.
|
|
221
|
-
* Works on macOS, Linux, and Windows.
|
|
222
221
|
*/
|
|
223
222
|
async function killProcessOnPort(port) {
|
|
224
223
|
try {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
} catch {}
|
|
233
|
-
return true;
|
|
234
|
-
} else if (platform === "win32") {
|
|
235
|
-
const { stdout } = await execAsync(`netstat -ano | findstr :${port} | findstr LISTENING`);
|
|
236
|
-
const lines = stdout.trim().split("\n").filter(Boolean);
|
|
237
|
-
if (lines.length === 0) return false;
|
|
238
|
-
const pids = /* @__PURE__ */ new Set();
|
|
239
|
-
for (const line of lines) {
|
|
240
|
-
const parts = line.trim().split(/\s+/);
|
|
241
|
-
const pid = parseInt(parts[parts.length - 1], 10);
|
|
242
|
-
if (!isNaN(pid) && pid !== 0) pids.add(pid);
|
|
243
|
-
}
|
|
244
|
-
for (const pid of pids) try {
|
|
245
|
-
await treeKill(pid, "SIGKILL");
|
|
246
|
-
} catch {}
|
|
247
|
-
return pids.size > 0;
|
|
248
|
-
}
|
|
249
|
-
return false;
|
|
224
|
+
const { stdout } = await execAsync(`lsof -ti tcp:${port}`);
|
|
225
|
+
const pids = stdout.trim().split("\n").filter(Boolean).map((p) => parseInt(p, 10)).filter((p) => !isNaN(p));
|
|
226
|
+
if (pids.length === 0) return false;
|
|
227
|
+
for (const pid of pids) try {
|
|
228
|
+
await treeKill(pid, "SIGKILL");
|
|
229
|
+
} catch {}
|
|
230
|
+
return true;
|
|
250
231
|
} catch {
|
|
251
232
|
return false;
|
|
252
233
|
}
|
|
@@ -803,7 +784,7 @@ var Manager = class {
|
|
|
803
784
|
//#region src/cli.ts
|
|
804
785
|
const program = new Command();
|
|
805
786
|
program.name("pidnap").description("Process manager with init system capabilities").version("0.0.0-dev.1");
|
|
806
|
-
program.command("init").description("Initialize and run the process manager with config file").option("-c, --config
|
|
787
|
+
program.command("init").description("Initialize and run the process manager with config file").option("-c, --config [path]", "Path to config file", "pidnap.config.ts").action(async (options) => {
|
|
807
788
|
const initLogger = logger({ name: "pidnap" });
|
|
808
789
|
try {
|
|
809
790
|
const configUrl = pathToFileURL(resolve(process.cwd(), options.config)).href;
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["oc","ocBase","processes","tasks","crons","getCwd"],"sources":["../src/api/contract.ts","../src/api/server.ts","../src/port-utils.ts","../src/manager.ts","../src/cli.ts"],"sourcesContent":["import { oc as ocBase } from \"@orpc/contract\";\nimport * as v from \"valibot\";\nimport { ProcessDefinitionSchema } from \"../lazy-process.ts\";\nimport { RestartingProcessStateSchema } from \"../restarting-process.ts\";\nimport { CronProcessStateSchema } from \"../cron-process.ts\";\nimport { TaskStateSchema } from \"../task-list.ts\";\n\n// Re-export schemas for use in other modules\nexport { ProcessDefinitionSchema } from \"../lazy-process.ts\";\nexport { RestartingProcessStateSchema } from \"../restarting-process.ts\";\nexport { CronProcessStateSchema } from \"../cron-process.ts\";\nexport { TaskStateSchema } from \"../task-list.ts\";\n\nconst oc = ocBase.$input(v.void());\n\n// Resource target (name or index)\nconst ResourceTarget = v.union([v.string(), v.number()]);\n\n// Manager state schema\nexport const ManagerStateSchema = v.picklist([\n \"idle\",\n \"initializing\",\n \"running\",\n \"stopping\",\n \"stopped\",\n]);\n\nexport type ManagerState = v.InferOutput<typeof ManagerStateSchema>;\n\n// Manager status response\nexport const ManagerStatusSchema = v.object({\n state: ManagerStateSchema,\n processCount: v.number(),\n cronCount: v.number(),\n taskCount: v.number(),\n});\n\nexport type ManagerStatus = v.InferOutput<typeof ManagerStatusSchema>;\n\n// API response schemas\nexport const RestartingProcessInfoSchema = v.object({\n name: v.string(),\n state: RestartingProcessStateSchema,\n restarts: v.number(),\n});\n\nexport type RestartingProcessInfo = v.InferOutput<typeof RestartingProcessInfoSchema>;\n\nexport const CronProcessInfoSchema = v.object({\n name: v.string(),\n state: CronProcessStateSchema,\n runCount: v.number(),\n failCount: v.number(),\n nextRun: v.nullable(v.string()), // ISO date string\n});\n\nexport type CronProcessInfo = v.InferOutput<typeof CronProcessInfoSchema>;\n\nexport const TaskEntryInfoSchema = v.object({\n id: v.string(),\n state: TaskStateSchema,\n processNames: v.array(v.string()),\n});\n\nexport type TaskEntryInfo = v.InferOutput<typeof TaskEntryInfoSchema>;\n\n// API contract\nexport const manager = {\n status: oc.output(ManagerStatusSchema),\n};\n\nexport const processes = {\n get: oc.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),\n list: oc.output(v.array(RestartingProcessInfoSchema)),\n add: oc\n .input(v.object({ name: v.string(), definition: ProcessDefinitionSchema }))\n .output(RestartingProcessInfoSchema),\n start: oc.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),\n stop: oc.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),\n restart: oc\n .input(v.object({ target: ResourceTarget, force: v.optional(v.boolean()) }))\n .output(RestartingProcessInfoSchema),\n reload: oc\n .input(\n v.object({\n target: ResourceTarget,\n definition: ProcessDefinitionSchema,\n restartImmediately: v.optional(v.boolean()),\n }),\n )\n .output(RestartingProcessInfoSchema),\n remove: oc.input(v.object({ target: ResourceTarget })).output(v.object({ success: v.boolean() })),\n};\n\nexport const tasks = {\n get: oc.input(v.object({ target: ResourceTarget })).output(TaskEntryInfoSchema),\n list: oc.output(v.array(TaskEntryInfoSchema)),\n add: oc\n .input(v.object({ name: v.string(), definition: ProcessDefinitionSchema }))\n .output(TaskEntryInfoSchema),\n remove: oc.input(v.object({ target: ResourceTarget })).output(TaskEntryInfoSchema),\n};\n\nexport const crons = {\n get: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),\n list: oc.output(v.array(CronProcessInfoSchema)),\n trigger: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),\n start: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),\n stop: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),\n};\n\nexport const api = {\n manager,\n processes,\n tasks,\n crons,\n};\n","import { implement } from \"@orpc/server\";\nimport {\n api,\n type RestartingProcessInfo,\n type CronProcessInfo,\n type TaskEntryInfo,\n type ManagerStatus,\n} from \"./contract.ts\";\nimport type { Manager } from \"../manager.ts\";\nimport type { RestartingProcess } from \"../restarting-process.ts\";\nimport type { CronProcess } from \"../cron-process.ts\";\n\nconst os = implement(api).$context<{ manager: Manager }>();\n\n// Helper to serialize a RestartingProcess to API response\nfunction serializeProcess(proc: RestartingProcess): RestartingProcessInfo {\n return {\n name: proc.name,\n state: proc.state,\n restarts: proc.restarts,\n };\n}\n\n// Helper to serialize a CronProcess to API response\nfunction serializeCron(cron: CronProcess): CronProcessInfo {\n return {\n name: cron.name,\n state: cron.state,\n runCount: cron.runCount,\n failCount: cron.failCount,\n nextRun: cron.nextRun?.toISOString() ?? null,\n };\n}\n\n// Manager handlers\nconst managerStatus = os.manager.status.handler(async ({ context }): Promise<ManagerStatus> => {\n const manager = context.manager;\n const taskList = manager.getTaskList();\n return {\n state: manager.state,\n processCount: manager.getRestartingProcesses().size,\n cronCount: manager.getCronProcesses().size,\n taskCount: taskList?.tasks.length ?? 0,\n };\n});\n\n// Processes handlers\nconst getProcess = os.processes.get.handler(async ({ input, context }) => {\n const proc = context.manager.getProcessByTarget(input.target);\n if (!proc) {\n throw new Error(`Process not found: ${input.target}`);\n }\n return serializeProcess(proc);\n});\n\nconst listProcesses = os.processes.list.handler(async ({ context }) => {\n const processes = Array.from(context.manager.getRestartingProcesses().values());\n return processes.map(serializeProcess);\n});\n\nconst addProcess = os.processes.add.handler(async ({ input, context }) => {\n const proc = await context.manager.addProcess(input.name, input.definition);\n return serializeProcess(proc);\n});\n\nconst startProcess = os.processes.start.handler(async ({ input, context }) => {\n const proc = await context.manager.startProcessByTarget(input.target);\n return serializeProcess(proc);\n});\n\nconst stopProcess = os.processes.stop.handler(async ({ input, context }) => {\n const proc = await context.manager.stopProcessByTarget(input.target);\n return serializeProcess(proc);\n});\n\nconst restartProcess = os.processes.restart.handler(async ({ input, context }) => {\n const proc = await context.manager.restartProcessByTarget(input.target, input.force);\n return serializeProcess(proc);\n});\n\nconst reloadProcess = os.processes.reload.handler(async ({ input, context }) => {\n const proc = await context.manager.reloadProcessByTarget(input.target, input.definition, {\n restartImmediately: input.restartImmediately,\n });\n return serializeProcess(proc);\n});\n\nconst removeProcess = os.processes.remove.handler(async ({ input, context }) => {\n await context.manager.removeProcessByTarget(input.target);\n return { success: true };\n});\n\n// Crons handlers\nconst getCron = os.crons.get.handler(async ({ input, context }) => {\n const cron = context.manager.getCronByTarget(input.target);\n if (!cron) {\n throw new Error(`Cron not found: ${input.target}`);\n }\n return serializeCron(cron);\n});\n\nconst listCrons = os.crons.list.handler(async ({ context }) => {\n const crons = Array.from(context.manager.getCronProcesses().values());\n return crons.map(serializeCron);\n});\n\nconst triggerCron = os.crons.trigger.handler(async ({ input, context }) => {\n const cron = await context.manager.triggerCronByTarget(input.target);\n return serializeCron(cron);\n});\n\nconst startCron = os.crons.start.handler(async ({ input, context }) => {\n const cron = context.manager.startCronByTarget(input.target);\n return serializeCron(cron);\n});\n\nconst stopCron = os.crons.stop.handler(async ({ input, context }) => {\n const cron = await context.manager.stopCronByTarget(input.target);\n return serializeCron(cron);\n});\n\n// Tasks handlers\nconst getTask = os.tasks.get.handler(async ({ input, context }) => {\n const task = context.manager.getTaskByTarget(input.target);\n if (!task) {\n throw new Error(`Task not found: ${input.target}`);\n }\n return task as TaskEntryInfo;\n});\n\nconst listTasks = os.tasks.list.handler(async ({ context }) => {\n const taskList = context.manager.getTaskList();\n if (!taskList) {\n return [];\n }\n return taskList.tasks.map((t) => ({\n id: t.id,\n state: t.state,\n processNames: t.processes.map((p) => p.name),\n })) as TaskEntryInfo[];\n});\n\nconst addTask = os.tasks.add.handler(async ({ input, context }) => {\n const task = context.manager.addTask(input.name, input.definition);\n return task as TaskEntryInfo;\n});\n\nconst removeTask = os.tasks.remove.handler(async ({ input, context }) => {\n const task = context.manager.removeTaskByTarget(input.target);\n return task as TaskEntryInfo;\n});\n\nexport const router = os.router({\n manager: {\n status: managerStatus,\n },\n processes: {\n add: addProcess,\n get: getProcess,\n list: listProcesses,\n start: startProcess,\n stop: stopProcess,\n restart: restartProcess,\n reload: reloadProcess,\n remove: removeProcess,\n },\n crons: {\n get: getCron,\n list: listCrons,\n trigger: triggerCron,\n start: startCron,\n stop: stopCron,\n },\n tasks: {\n get: getTask,\n list: listTasks,\n add: addTask,\n remove: removeTask,\n },\n});\n","import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { treeKill } from \"./tree-kill.ts\";\n\nconst execAsync = promisify(exec);\n\n/**\n * Kill any process listening on the specified port, including all descendants.\n * Works on macOS, Linux, and Windows.\n */\nexport async function killProcessOnPort(port: number): Promise<boolean> {\n try {\n const platform = process.platform;\n\n if (platform === \"darwin\" || platform === \"linux\") {\n // Use lsof to find the process ID listening on the port\n const { stdout } = await execAsync(`lsof -ti tcp:${port}`);\n const pids = stdout\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .map((p) => parseInt(p, 10))\n .filter((p) => !isNaN(p));\n\n if (pids.length === 0) {\n return false; // No process found on port\n }\n\n // Tree kill all processes found on the port (kills children/grandchildren too)\n for (const pid of pids) {\n try {\n await treeKill(pid, \"SIGKILL\");\n } catch {\n // Process may have already exited\n }\n }\n\n return true;\n } else if (platform === \"win32\") {\n // Windows: use netstat to find PIDs\n const { stdout } = await execAsync(\n `netstat -ano | findstr :${port} | findstr LISTENING`,\n );\n const lines = stdout.trim().split(\"\\n\").filter(Boolean);\n\n if (lines.length === 0) {\n return false;\n }\n\n const pids = new Set<number>();\n for (const line of lines) {\n const parts = line.trim().split(/\\s+/);\n const pid = parseInt(parts[parts.length - 1], 10);\n if (!isNaN(pid) && pid !== 0) {\n pids.add(pid);\n }\n }\n\n // treeKill handles /T flag on Windows\n for (const pid of pids) {\n try {\n await treeKill(pid, \"SIGKILL\");\n } catch {\n // Process may have already exited\n }\n }\n\n return pids.size > 0;\n }\n\n return false;\n } catch {\n // Command failed (e.g., no process on port)\n return false;\n }\n}\n","import * as v from \"valibot\";\nimport { join } from \"node:path\";\nimport { cwd as getCwd } from \"node:process\";\nimport { mkdirSync } from \"node:fs\";\nimport { ProcessDefinitionSchema, type ProcessDefinition } from \"./lazy-process.ts\";\nimport type { Logger } from \"./logger.ts\";\nimport { TaskList, type NamedProcessDefinition } from \"./task-list.ts\";\nimport { CronProcess, CronProcessOptionsSchema } from \"./cron-process.ts\";\nimport {\n RestartingProcess,\n RestartingProcessOptionsSchema,\n type RestartingProcessOptions,\n} from \"./restarting-process.ts\";\nimport { EnvManager } from \"./env-manager.ts\";\nimport { killProcessOnPort } from \"./port-utils.ts\";\n\n// Valibot schemas\n\n// HTTP server configuration schema\nexport const HttpServerConfigSchema = v.object({\n host: v.optional(v.string()),\n port: v.optional(v.number()),\n authToken: v.optional(v.string()),\n});\n\nexport type HttpServerConfig = v.InferOutput<typeof HttpServerConfigSchema>;\n\n// Cron process entry schema\nexport const CronProcessEntrySchema = v.object({\n name: v.string(),\n definition: ProcessDefinitionSchema,\n options: CronProcessOptionsSchema,\n envFile: v.optional(v.string()),\n});\n\nexport type CronProcessEntry = v.InferOutput<typeof CronProcessEntrySchema>;\n\n// Env reload delay schema - can be number (ms), \"immediately\"/true, or false for disabled\nexport const EnvReloadDelaySchema = v.union([v.number(), v.boolean(), v.literal(\"immediately\")]);\n\nexport type EnvReloadDelay = v.InferOutput<typeof EnvReloadDelaySchema>;\n\n// Restarting process entry schema\nexport const RestartingProcessEntrySchema = v.object({\n name: v.string(),\n definition: ProcessDefinitionSchema,\n options: v.optional(RestartingProcessOptionsSchema),\n envFile: v.optional(v.string()),\n envReloadDelay: v.optional(EnvReloadDelaySchema), // Default 5000ms\n port: v.optional(v.number()), // Kill any process on this port before spawning\n});\n\nexport type RestartingProcessEntry = v.InferOutput<typeof RestartingProcessEntrySchema>;\n\n// Task entry schema with envFile\nexport const TaskEntrySchema = v.object({\n name: v.string(),\n definition: ProcessDefinitionSchema,\n envFile: v.optional(v.string()),\n});\n\nexport type TaskEntry = v.InferOutput<typeof TaskEntrySchema>;\n\n// Main manager configuration schema\nexport const ManagerConfigSchema = v.object({\n http: v.optional(HttpServerConfigSchema),\n cwd: v.optional(v.string()),\n logDir: v.optional(v.string()),\n env: v.optional(v.record(v.string(), v.string())),\n envFiles: v.optional(v.record(v.string(), v.string())),\n tasks: v.optional(v.array(TaskEntrySchema)),\n crons: v.optional(v.array(CronProcessEntrySchema)),\n processes: v.optional(v.array(RestartingProcessEntrySchema)),\n});\n\nexport type ManagerConfig = v.InferOutput<typeof ManagerConfigSchema>;\n\n// Default restart policy when not specified\nconst DEFAULT_RESTART_OPTIONS = {\n restartPolicy: \"always\" as const,\n};\n\n// Hardcoded shutdown configuration\nconst SHUTDOWN_TIMEOUT_MS = 15000;\nconst SHUTDOWN_SIGNALS: NodeJS.Signals[] = [\"SIGINT\", \"SIGTERM\"];\nconst SHUTDOWN_EXIT_CODE = 0;\n\n// Manager state\nexport type ManagerState =\n | \"idle\" // Not started\n | \"initializing\" // Running task list\n | \"running\" // All processes running\n | \"stopping\" // Stopping all processes\n | \"stopped\"; // Fully stopped\n\nexport class Manager {\n private config: ManagerConfig;\n private logger: Logger;\n private envManager: EnvManager;\n private envFileKeyByProcess: Map<string, string> = new Map();\n\n private _state: ManagerState = \"idle\";\n private taskList: TaskList | null = null;\n private cronProcesses: Map<string, CronProcess> = new Map();\n private restartingProcesses: Map<string, RestartingProcess> = new Map();\n private logDir: string;\n\n // Env reload tracking\n private processEnvReloadConfig: Map<string, EnvReloadDelay> = new Map();\n private envReloadTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();\n private envChangeUnsubscribe: (() => void) | null = null;\n\n // Port tracking for processes\n private processPortConfig: Map<string, number> = new Map();\n\n // Shutdown handling\n private signalHandlers: Map<NodeJS.Signals, () => void> = new Map();\n private shutdownPromise: Promise<void> | null = null;\n private isShuttingDown: boolean = false;\n\n constructor(config: ManagerConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n this.logDir = config.logDir ?? join(getCwd(), \"logs\");\n this.ensureLogDirs();\n // Initialize EnvManager with watching enabled\n this.envManager = new EnvManager({\n cwd: config.cwd,\n files: config.envFiles,\n watch: true,\n });\n\n // Register env change handler\n this.envChangeUnsubscribe = this.envManager.onChange((changedKeys) => {\n this.handleEnvChange(changedKeys);\n });\n\n // Automatically register shutdown handlers\n this.registerShutdownHandlers();\n }\n\n /**\n * Merge global env with process-specific env and apply cwd inheritance\n * Merge order: .env (global), config.env (global), .env.<processName>, processEnvFile, definition.env\n */\n private applyDefaults(\n processName: string,\n definition: ProcessDefinition,\n envFile?: string,\n ): ProcessDefinition {\n // Start with env from .env file (global)\n const envFromFiles = this.envManager.getEnvVars(processName);\n\n // Build the env file map for this specific process if envFile is provided\n let envFromCustomFile: Record<string, string> = {};\n if (envFile) {\n let key = this.envFileKeyByProcess.get(processName);\n if (!key) {\n key = `custom:${processName}`;\n this.envFileKeyByProcess.set(processName, key);\n this.envManager.registerFile(key, envFile);\n }\n envFromCustomFile = this.envManager.getEnvForKey(key);\n }\n\n return {\n ...definition,\n cwd: definition.cwd ?? this.config.cwd,\n env: {\n ...envFromFiles, // .env (global) + .env.<processName>\n ...this.config.env, // Global env from config\n ...envFromCustomFile, // Custom env file if specified\n ...definition.env, // Process-specific env overrides\n },\n };\n }\n\n private processLogFile(name: string): string {\n return join(this.logDir, \"process\", `${name}.log`);\n }\n\n private taskLogFile(name: string): string {\n return join(this.logDir, \"tasks\", `${name}.log`);\n }\n\n private cronLogFile(name: string): string {\n return join(this.logDir, \"cron\", `${name}.log`);\n }\n\n /**\n * Kill any process running on the configured port for a process\n */\n private async killPortForProcess(processName: string): Promise<void> {\n const port = this.processPortConfig.get(processName);\n if (port === undefined) return;\n\n const killed = await killProcessOnPort(port);\n if (killed) {\n this.logger.info(`Killed process on port ${port} before starting \"${processName}\"`);\n }\n }\n\n private ensureLogDirs(): void {\n mkdirSync(this.logDir, { recursive: true });\n mkdirSync(join(this.logDir, \"process\"), { recursive: true });\n mkdirSync(join(this.logDir, \"tasks\"), { recursive: true });\n mkdirSync(join(this.logDir, \"cron\"), { recursive: true });\n }\n\n /**\n * Handle env file changes\n */\n private handleEnvChange(changedKeys: string[]): void {\n if (this._state !== \"running\") {\n return; // Only handle changes when manager is running\n }\n\n this.logger.info(`Env files changed for keys: ${changedKeys.join(\", \")}`);\n\n // Check which processes are affected\n const affectedProcesses = new Set<string>();\n\n for (const key of changedKeys) {\n // Global key affects all processes\n if (key === \"global\") {\n for (const processName of this.restartingProcesses.keys()) {\n affectedProcesses.add(processName);\n }\n } else {\n // Specific key affects matching process\n if (this.restartingProcesses.has(key)) {\n affectedProcesses.add(key);\n }\n\n for (const [processName, customKey] of this.envFileKeyByProcess.entries()) {\n if (customKey === key && this.restartingProcesses.has(processName)) {\n affectedProcesses.add(processName);\n }\n }\n }\n }\n\n // Schedule restarts for affected processes\n for (const processName of affectedProcesses) {\n const reloadDelay = this.processEnvReloadConfig.get(processName);\n\n // Skip if reload is disabled for this process\n if (reloadDelay === false) {\n continue;\n }\n\n this.scheduleProcessReload(processName, reloadDelay);\n }\n }\n\n /**\n * Schedule a process reload with debouncing\n */\n private scheduleProcessReload(processName: string, reloadDelay?: EnvReloadDelay): void {\n // Clear existing timer if any\n const existingTimer = this.envReloadTimers.get(processName);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Determine delay in ms\n let delayMs: number;\n if (reloadDelay === false) {\n return; // Should not happen, but guard anyway\n } else if (reloadDelay === true || reloadDelay === \"immediately\") {\n delayMs = 0;\n } else if (typeof reloadDelay === \"number\") {\n delayMs = reloadDelay;\n } else {\n delayMs = 5000; // Default 5 seconds\n }\n\n this.logger.info(`Scheduling reload for process \"${processName}\" in ${delayMs}ms`);\n\n const timer = setTimeout(async () => {\n await this.reloadProcessEnv(processName);\n this.envReloadTimers.delete(processName);\n }, delayMs);\n\n this.envReloadTimers.set(processName, timer);\n }\n\n /**\n * Reload a process with updated env vars\n */\n private async reloadProcessEnv(processName: string): Promise<void> {\n const proc = this.restartingProcesses.get(processName);\n if (!proc) {\n this.logger.warn(`Process \"${processName}\" not found for env reload`);\n return;\n }\n\n this.logger.info(`Reloading process \"${processName}\" due to env change`);\n\n // Get the original config for this process\n const processConfig = this.config.processes?.find((p) => p.name === processName);\n if (!processConfig) {\n this.logger.warn(`Process config for \"${processName}\" not found`);\n return;\n }\n\n // Rebuild definition with updated env\n const updatedDefinition = this.applyDefaults(\n processName,\n processConfig.definition,\n processConfig.envFile,\n );\n\n // Reload the process (graceful restart)\n await proc.reload(updatedDefinition, true);\n }\n\n get state(): ManagerState {\n return this._state;\n }\n\n /**\n * Get all cron processes (read-only access)\n */\n getCronProcesses(): ReadonlyMap<string, CronProcess> {\n return this.cronProcesses;\n }\n\n /**\n * Get a specific cron process by name\n */\n getCronProcess(name: string): CronProcess | undefined {\n return this.cronProcesses.get(name);\n }\n\n /**\n * Get all restarting processes (read-only access)\n */\n getRestartingProcesses(): ReadonlyMap<string, RestartingProcess> {\n return this.restartingProcesses;\n }\n\n /**\n * Get a specific restarting process by name\n */\n getRestartingProcess(name: string): RestartingProcess | undefined {\n return this.restartingProcesses.get(name);\n }\n\n /**\n * Get the task list (read-only access)\n */\n getTaskList(): TaskList | null {\n return this.taskList;\n }\n\n /**\n * Get a restarting process by name or index\n */\n getProcessByTarget(target: string | number): RestartingProcess | undefined {\n if (typeof target === \"string\") {\n return this.restartingProcesses.get(target);\n }\n const entries = Array.from(this.restartingProcesses.values());\n return entries[target];\n }\n\n /**\n * Get a cron process by name or index\n */\n getCronByTarget(target: string | number): CronProcess | undefined {\n if (typeof target === \"string\") {\n return this.cronProcesses.get(target);\n }\n const entries = Array.from(this.cronProcesses.values());\n return entries[target];\n }\n\n /**\n * Get a task by id or index\n */\n getTaskByTarget(\n target: string | number,\n ): { id: string; state: string; processNames: string[] } | undefined {\n if (!this.taskList) return undefined;\n const tasks = this.taskList.tasks;\n\n if (typeof target === \"string\") {\n const task = tasks.find((t) => t.id === target);\n if (!task) return undefined;\n return {\n id: task.id,\n state: task.state,\n processNames: task.processes.map((p) => p.name),\n };\n }\n\n const task = tasks[target];\n if (!task) return undefined;\n return {\n id: task.id,\n state: task.state,\n processNames: task.processes.map((p) => p.name),\n };\n }\n\n /**\n * Start a restarting process by target\n */\n async startProcessByTarget(target: string | number): Promise<RestartingProcess> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n // Kill any process on the configured port before starting\n await this.killPortForProcess(proc.name);\n proc.start();\n return proc;\n }\n\n /**\n * Stop a restarting process by target\n */\n async stopProcessByTarget(target: string | number, timeout?: number): Promise<RestartingProcess> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n await proc.stop(timeout);\n return proc;\n }\n\n /**\n * Restart a restarting process by target\n */\n async restartProcessByTarget(\n target: string | number,\n force: boolean = false,\n ): Promise<RestartingProcess> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n // Kill any process on the configured port before restarting\n await this.killPortForProcess(proc.name);\n await proc.restart(force);\n return proc;\n }\n\n /**\n * Reload a restarting process with new definition\n */\n async reloadProcessByTarget(\n target: string | number,\n newDefinition: ProcessDefinition,\n options?: {\n restartImmediately?: boolean;\n updateOptions?: Partial<RestartingProcessOptions>;\n },\n ): Promise<RestartingProcess> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n\n // Apply global defaults to new definition\n const definitionWithDefaults = this.applyDefaults(proc.name, newDefinition);\n\n // Update options if provided\n if (options?.updateOptions) {\n proc.updateOptions(options.updateOptions);\n }\n\n // Reload with new definition\n await proc.reload(definitionWithDefaults, options?.restartImmediately ?? true);\n this.logger.info(`Reloaded process: ${proc.name}`);\n return proc;\n }\n\n /**\n * Remove a restarting process by target\n */\n async removeProcessByTarget(target: string | number, timeout?: number): Promise<void> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n\n // Stop the process first\n await proc.stop(timeout);\n\n // Remove from the map\n this.restartingProcesses.delete(proc.name);\n this.logger.info(`Removed process: ${proc.name}`);\n }\n\n /**\n * Add a task to the task list\n * Creates the task list if it doesn't exist and starts it\n */\n addTask(\n name: string,\n definition: ProcessDefinition,\n ): { id: string; state: string; processNames: string[] } {\n if (!this.taskList) {\n const taskListLogger = this.logger.child(\"tasks\", {\n logFile: this.taskLogFile(\"tasks\"),\n });\n this.taskList = new TaskList(\"runtime\", taskListLogger, undefined, (processName) => {\n return this.taskLogFile(processName);\n });\n }\n\n const namedProcess: NamedProcessDefinition = {\n name,\n process: this.applyDefaults(name, definition),\n };\n const id = this.taskList.addTask(namedProcess);\n\n // Start the task list if it's idle so the task runs immediately\n if (this.taskList.state === \"idle\") {\n this.taskList.start();\n }\n\n return {\n id,\n state: \"pending\",\n processNames: [name],\n };\n }\n\n removeTaskByTarget(target: string | number): {\n id: string;\n state: string;\n processNames: string[];\n } {\n if (!this.taskList) {\n throw new Error(`Task list not initialized`);\n }\n\n const removed = this.taskList.removeTaskByTarget(target);\n return {\n id: removed.id,\n state: removed.state,\n processNames: removed.processes.map((p) => p.name),\n };\n }\n\n /**\n * Add a restarting process at runtime\n */\n async addProcess(\n name: string,\n definition: ProcessDefinition,\n options?: RestartingProcessOptions,\n envReloadDelay?: EnvReloadDelay,\n port?: number,\n ): Promise<RestartingProcess> {\n if (this.restartingProcesses.has(name)) {\n throw new Error(`Process \"${name}\" already exists`);\n }\n\n const processLogger = this.logger.child(name, { logFile: this.processLogFile(name) });\n const restartingProcess = new RestartingProcess(\n name,\n this.applyDefaults(name, definition),\n options ?? DEFAULT_RESTART_OPTIONS,\n processLogger,\n );\n this.restartingProcesses.set(name, restartingProcess);\n\n // Track port config and kill any process on that port before starting\n if (port !== undefined) {\n this.processPortConfig.set(name, port);\n await this.killPortForProcess(name);\n }\n\n restartingProcess.start();\n\n // Track env reload config for this process\n this.processEnvReloadConfig.set(name, envReloadDelay ?? 5000);\n\n this.logger.info(`Added and started restarting process: ${name}`);\n return restartingProcess;\n }\n\n /**\n * Trigger a cron process by target\n */\n async triggerCronByTarget(target: string | number): Promise<CronProcess> {\n const cron = this.getCronByTarget(target);\n if (!cron) {\n throw new Error(`Cron not found: ${target}`);\n }\n await cron.trigger();\n return cron;\n }\n\n /**\n * Start a cron process by target\n */\n startCronByTarget(target: string | number): CronProcess {\n const cron = this.getCronByTarget(target);\n if (!cron) {\n throw new Error(`Cron not found: ${target}`);\n }\n cron.start();\n return cron;\n }\n\n /**\n * Stop a cron process by target\n */\n async stopCronByTarget(target: string | number, timeout?: number): Promise<CronProcess> {\n const cron = this.getCronByTarget(target);\n if (!cron) {\n throw new Error(`Cron not found: ${target}`);\n }\n await cron.stop(timeout);\n return cron;\n }\n\n /**\n * Start the manager:\n * 1. Run task list (if configured) and wait for completion\n * 2. Create and start all cron/restarting processes\n */\n async start(): Promise<void> {\n if (this._state !== \"idle\" && this._state !== \"stopped\") {\n throw new Error(`Manager is already ${this._state}`);\n }\n\n this.logger.info(`Starting manager`);\n\n // Phase 1: Run initialization tasks\n if (this.config.tasks && this.config.tasks.length > 0) {\n this._state = \"initializing\";\n this.logger.info(`Running initialization tasks`);\n\n const taskListLogger = this.logger.child(\"tasks\");\n // Apply defaults to all tasks\n const tasksWithDefaults = this.config.tasks.map((task) => ({\n name: task.name,\n process: this.applyDefaults(task.name, task.definition, task.envFile),\n }));\n this.taskList = new TaskList(\"init\", taskListLogger, tasksWithDefaults, (processName) => {\n return this.taskLogFile(processName);\n });\n\n this.taskList.start();\n await this.taskList.waitUntilIdle();\n\n // Check if any tasks failed\n const failedTasks = this.taskList.tasks.filter((t) => t.state === \"failed\");\n if (failedTasks.length > 0) {\n this._state = \"stopped\";\n const failedNames = failedTasks.map((t) => t.id).join(\", \");\n throw new Error(`Initialization failed: tasks [${failedNames}] failed`);\n }\n\n this.logger.info(`Initialization tasks completed`);\n }\n\n // Phase 2: Create and start cron processes\n if (this.config.crons) {\n for (const entry of this.config.crons) {\n const processLogger = this.logger.child(entry.name, {\n logFile: this.cronLogFile(entry.name),\n });\n const cronProcess = new CronProcess(\n entry.name,\n this.applyDefaults(entry.name, entry.definition, entry.envFile),\n entry.options,\n processLogger,\n );\n this.cronProcesses.set(entry.name, cronProcess);\n cronProcess.start();\n this.logger.info(`Started cron process: ${entry.name}`);\n }\n }\n\n // Phase 3: Create and start restarting processes\n if (this.config.processes) {\n for (const entry of this.config.processes) {\n const processLogger = this.logger.child(entry.name, {\n logFile: this.processLogFile(entry.name),\n });\n const restartingProcess = new RestartingProcess(\n entry.name,\n this.applyDefaults(entry.name, entry.definition, entry.envFile),\n entry.options ?? DEFAULT_RESTART_OPTIONS,\n processLogger,\n );\n this.restartingProcesses.set(entry.name, restartingProcess);\n\n // Track port config and kill any process on that port before starting\n if (entry.port !== undefined) {\n this.processPortConfig.set(entry.name, entry.port);\n await this.killPortForProcess(entry.name);\n }\n\n restartingProcess.start();\n\n // Track env reload config for this process\n this.processEnvReloadConfig.set(\n entry.name,\n entry.envReloadDelay ?? 5000, // Default to 5000ms\n );\n\n this.logger.info(`Started restarting process: ${entry.name}`);\n }\n }\n\n this._state = \"running\";\n this.logger.info(\n `Manager started with ${this.cronProcesses.size} cron process(es) and ${this.restartingProcesses.size} restarting process(es)`,\n );\n }\n\n /**\n * Stop all managed processes\n */\n async stop(timeout?: number): Promise<void> {\n if (this._state === \"idle\" || this._state === \"stopped\") {\n this._state = \"stopped\";\n this.unregisterShutdownHandlers();\n return;\n }\n\n this._state = \"stopping\";\n this.logger.info(`Stopping manager`);\n\n // Clear all env reload timers\n for (const timer of this.envReloadTimers.values()) {\n clearTimeout(timer);\n }\n this.envReloadTimers.clear();\n\n // Unsubscribe from env changes\n if (this.envChangeUnsubscribe) {\n this.envChangeUnsubscribe();\n this.envChangeUnsubscribe = null;\n }\n\n // Dispose env manager\n this.envManager.dispose();\n\n // Stop task list if still running\n if (this.taskList) {\n await this.taskList.stop(timeout);\n }\n\n // Stop all cron processes in parallel\n const cronStopPromises = Array.from(this.cronProcesses.values()).map((p) => p.stop(timeout));\n\n // Stop all restarting processes in parallel\n const restartingStopPromises = Array.from(this.restartingProcesses.values()).map((p) =>\n p.stop(timeout),\n );\n\n await Promise.all([...cronStopPromises, ...restartingStopPromises]);\n\n this._state = \"stopped\";\n this.logger.info(`Manager stopped`);\n\n this.unregisterShutdownHandlers();\n }\n\n /**\n * Register signal handlers for graceful shutdown\n * Called automatically by constructor\n */\n private registerShutdownHandlers(): void {\n if (this.signalHandlers.size > 0) {\n this.logger.warn(`Shutdown handlers already registered`);\n return;\n }\n\n for (const signal of SHUTDOWN_SIGNALS) {\n const handler = () => this.handleSignal(signal);\n this.signalHandlers.set(signal, handler);\n process.on(signal, handler);\n this.logger.debug(`Registered handler for ${signal}`);\n }\n\n this.logger.info(`Shutdown handlers registered for signals: ${SHUTDOWN_SIGNALS.join(\", \")}`);\n }\n\n /**\n * Unregister signal handlers for graceful shutdown\n */\n private unregisterShutdownHandlers(): void {\n if (this.signalHandlers.size === 0) {\n return;\n }\n\n for (const [signal, handler] of this.signalHandlers.entries()) {\n process.off(signal, handler);\n this.logger.debug(`Unregistered handler for ${signal}`);\n }\n\n this.signalHandlers.clear();\n }\n\n /**\n * Wait for shutdown to complete (useful for keeping process alive)\n * Resolves when the manager has fully stopped\n */\n async waitForShutdown(): Promise<void> {\n // If already stopped, return immediately\n if (this._state === \"stopped\") {\n return;\n }\n\n // If shutdown is in progress, wait for it\n if (this.shutdownPromise) {\n await this.shutdownPromise;\n return;\n }\n\n // Wait for state to become stopped\n return new Promise((resolve) => {\n const checkInterval = setInterval(() => {\n if (this._state === \"stopped\") {\n clearInterval(checkInterval);\n resolve();\n }\n }, 100);\n });\n }\n\n /**\n * Trigger graceful shutdown programmatically\n */\n async shutdown(): Promise<void> {\n if (this.isShuttingDown) {\n this.logger.warn(`Shutdown already in progress`);\n if (this.shutdownPromise) {\n await this.shutdownPromise;\n }\n return;\n }\n\n this.isShuttingDown = true;\n this.logger.info(`Initiating graceful shutdown (timeout: ${SHUTDOWN_TIMEOUT_MS}ms)`);\n\n this.shutdownPromise = this.performShutdown();\n await this.shutdownPromise;\n }\n\n private handleSignal(signal: NodeJS.Signals): void {\n this.logger.info(`Received ${signal}, initiating graceful shutdown...`);\n\n // Prevent handling multiple signals\n if (this.isShuttingDown) {\n this.logger.warn(`Shutdown already in progress, ignoring ${signal}`);\n return;\n }\n\n this.isShuttingDown = true;\n this.shutdownPromise = this.performShutdown();\n\n this.shutdownPromise\n .then(() => {\n this.logger.info(`Exiting with code ${SHUTDOWN_EXIT_CODE}`);\n process.exit(SHUTDOWN_EXIT_CODE);\n })\n .catch((err) => {\n this.logger.error(`Shutdown error:`, err);\n process.exit(1);\n });\n }\n\n private async performShutdown(): Promise<void> {\n // Create a timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`Shutdown timed out after ${SHUTDOWN_TIMEOUT_MS}ms`));\n }, SHUTDOWN_TIMEOUT_MS);\n });\n\n try {\n // Race between graceful stop and timeout\n await Promise.race([this.stop(SHUTDOWN_TIMEOUT_MS), timeoutPromise]);\n this.logger.info(`Graceful shutdown completed`);\n } catch (err) {\n this.logger.error(`Shutdown error:`, err);\n // Force stop on timeout\n this._state = \"stopped\";\n throw err;\n } finally {\n this.isShuttingDown = false;\n this.shutdownPromise = null;\n }\n }\n}\n","#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { createServer } from \"node:http\";\nimport { pathToFileURL } from \"node:url\";\nimport { resolve } from \"node:path\";\nimport { RPCHandler } from \"@orpc/server/node\";\nimport { onError } from \"@orpc/server\";\nimport * as v from \"valibot\";\nimport { router } from \"./api/server.ts\";\nimport { Manager, ManagerConfigSchema } from \"./manager.ts\";\nimport { logger } from \"./logger.ts\";\nimport { tsImport } from \"tsx/esm/api\";\nimport Table from \"cli-table3\";\nimport { createClient } from \"./api/client.ts\";\nimport { ProcessDefinitionSchema } from \"./api/contract.ts\";\n\nconst program = new Command();\n\nprogram\n .name(\"pidnap\")\n .description(\"Process manager with init system capabilities\")\n .version(\"0.0.0-dev.1\");\n\nprogram\n .command(\"init\")\n .description(\"Initialize and run the process manager with config file\")\n .option(\"-c, --config <path>\", \"Path to config file\", \"pidnap.config.ts\")\n .action(async (options) => {\n const initLogger = logger({ name: \"pidnap\" });\n try {\n // Resolve config file path\n const configPath = resolve(process.cwd(), options.config);\n const configUrl = pathToFileURL(configPath).href;\n\n // Import the config file\n const configModule = await tsImport(configUrl, { parentURL: import.meta.url });\n const rawConfig =\n configModule.default.default || configModule.default || configModule.config || {};\n\n // Parse and validate the config with Valibot\n const config = v.parse(ManagerConfigSchema, rawConfig);\n\n // Extract HTTP config with defaults\n const host = config.http?.host ?? \"localhost\";\n const port = config.http?.port ?? 3000;\n const authToken = config.http?.authToken;\n\n // Create manager with config\n const logDir = config.logDir ?? resolve(process.cwd(), \"logs\");\n const managerLogger = logger({ name: \"pidnap\", logFile: resolve(logDir, \"pidnap.log\") });\n const manager = new Manager(config, managerLogger);\n\n // Setup ORPC server with optional auth token middleware\n const handler = new RPCHandler(router, {\n interceptors: [\n onError((error) => {\n managerLogger.error(error);\n }),\n ],\n });\n\n const server = createServer(async (req, res) => {\n // Check auth token if configured\n if (authToken) {\n const providedToken = req.headers[\"authorization\"]?.replace(\"Bearer \", \"\");\n if (providedToken !== authToken) {\n res.statusCode = 401;\n res.end(\"Unauthorized\");\n return;\n }\n }\n\n const { matched } = await handler.handle(req, res, {\n prefix: \"/rpc\",\n context: { manager },\n });\n if (matched) return;\n res.statusCode = 404;\n res.end(\"Not found\");\n });\n\n server.listen(port, host, async () => {\n managerLogger.info(`pidnap RPC server running on http://${host}:${port}`);\n if (authToken) {\n managerLogger.info(\"Auth token required for API access\");\n }\n\n try {\n await manager.start();\n } catch (err) {\n managerLogger.error(\"Failed to start manager:\", err);\n server.close();\n process.exit(1);\n }\n });\n\n // Wait for shutdown\n await manager.waitForShutdown();\n\n // Close server on shutdown\n server.close();\n } catch (error) {\n initLogger.error(\"Failed to start pidnap:\", error);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"status\")\n .description(\"Show manager status\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const status = await client.manager.status();\n\n const table = new Table({ head: [\"State\", \"Processes\", \"Crons\", \"Tasks\"] });\n table.push([status.state, status.processCount, status.cronCount, status.taskCount]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to fetch status:\", error);\n process.exit(1);\n }\n });\n\nconst processes = program.command(\"processes\").description(\"Manage restarting processes\");\n\nprocesses\n .command(\"list\")\n .description(\"List restarting processes\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const processes = await client.processes.list();\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n for (const proc of processes) {\n table.push([proc.name, proc.state, proc.restarts]);\n }\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to list processes:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"get\")\n .description(\"Get a restarting process by name or index\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const proc = await client.processes.get({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to get process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"add\")\n .description(\"Add a restarting process\")\n .requiredOption(\"-n, --name <name>\", \"Process name\")\n .requiredOption(\"-d, --definition <json>\", \"Process definition JSON\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const definition = parseDefinition(options.definition);\n const proc = await client.processes.add({ name: options.name, definition });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to add process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"start\")\n .description(\"Start a restarting process\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const proc = await client.processes.start({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to start process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"stop\")\n .description(\"Stop a restarting process\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const proc = await client.processes.stop({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to stop process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"restart\")\n .description(\"Restart a restarting process\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-f, --force\", \"Force restart\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const proc = await client.processes.restart({\n target: parseTarget(target),\n force: options.force,\n });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to restart process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"remove\")\n .description(\"Remove a restarting process\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n await client.processes.remove({ target: parseTarget(target) });\n console.log(\"Process removed\");\n } catch (error) {\n console.error(\"Failed to remove process:\", error);\n process.exit(1);\n }\n });\n\nconst crons = program.command(\"crons\").description(\"Manage cron processes\");\n\ncrons\n .command(\"list\")\n .description(\"List cron processes\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const crons = await client.crons.list();\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n for (const cron of crons) {\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n }\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to list crons:\", error);\n process.exit(1);\n }\n });\n\ncrons\n .command(\"get\")\n .description(\"Get a cron process by name or index\")\n .argument(\"<target>\", \"Cron name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const cron = await client.crons.get({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to get cron:\", error);\n process.exit(1);\n }\n });\n\ncrons\n .command(\"trigger\")\n .description(\"Trigger a cron process\")\n .argument(\"<target>\", \"Cron name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const cron = await client.crons.trigger({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to trigger cron:\", error);\n process.exit(1);\n }\n });\n\ncrons\n .command(\"start\")\n .description(\"Start a cron process\")\n .argument(\"<target>\", \"Cron name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const cron = await client.crons.start({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to start cron:\", error);\n process.exit(1);\n }\n });\n\ncrons\n .command(\"stop\")\n .description(\"Stop a cron process\")\n .argument(\"<target>\", \"Cron name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const cron = await client.crons.stop({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to stop cron:\", error);\n process.exit(1);\n }\n });\n\nconst tasks = program.command(\"tasks\").description(\"Manage tasks\");\n\ntasks\n .command(\"list\")\n .description(\"List tasks\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const tasks = await client.tasks.list();\n\n const table = new Table({ head: [\"Id\", \"State\", \"Processes\"] });\n for (const task of tasks) {\n table.push([task.id, task.state, task.processNames.join(\", \")]);\n }\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to list tasks:\", error);\n process.exit(1);\n }\n });\n\ntasks\n .command(\"get\")\n .description(\"Get a task by id or index\")\n .argument(\"<target>\", \"Task id or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const task = await client.tasks.get({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Id\", \"State\", \"Processes\"] });\n table.push([task.id, task.state, task.processNames.join(\", \")]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to get task:\", error);\n process.exit(1);\n }\n });\n\ntasks\n .command(\"add\")\n .description(\"Add a task\")\n .requiredOption(\"-n, --name <name>\", \"Task name\")\n .requiredOption(\"-d, --definition <json>\", \"Process definition JSON\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const definition = parseDefinition(options.definition);\n const task = await client.tasks.add({ name: options.name, definition });\n\n const table = new Table({ head: [\"Id\", \"State\", \"Processes\"] });\n table.push([task.id, task.state, task.processNames.join(\", \")]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to add task:\", error);\n process.exit(1);\n }\n });\n\ntasks\n .command(\"remove\")\n .description(\"Remove a task by id or index\")\n .argument(\"<target>\", \"Task id or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const task = await client.tasks.remove({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Id\", \"State\", \"Processes\"] });\n table.push([task.id, task.state, task.processNames.join(\", \")]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to remove task:\", error);\n process.exit(1);\n }\n });\n\nconst TargetSchema = v.union([v.string(), v.number()]);\ntype ProcessDefinition = v.InferOutput<typeof ProcessDefinitionSchema>;\n\nfunction parseTarget(value: string): string | number {\n const asNumber = Number(value);\n return v.parse(TargetSchema, Number.isNaN(asNumber) ? value : asNumber);\n}\n\nfunction parseDefinition(raw: string): ProcessDefinition {\n try {\n const parsed = JSON.parse(raw);\n return v.parse(ProcessDefinitionSchema, parsed);\n } catch (error) {\n console.error(\"Invalid --definition JSON. Expected a ProcessDefinition.\");\n throw error;\n }\n}\n\nprogram.parse();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["oc","ocBase","processes","tasks","crons","getCwd"],"sources":["../src/api/contract.ts","../src/api/server.ts","../src/port-utils.ts","../src/manager.ts","../src/cli.ts"],"sourcesContent":["import { oc as ocBase } from \"@orpc/contract\";\nimport * as v from \"valibot\";\nimport { ProcessDefinitionSchema } from \"../lazy-process.ts\";\nimport { RestartingProcessStateSchema } from \"../restarting-process.ts\";\nimport { CronProcessStateSchema } from \"../cron-process.ts\";\nimport { TaskStateSchema } from \"../task-list.ts\";\n\n// Re-export schemas for use in other modules\nexport { ProcessDefinitionSchema } from \"../lazy-process.ts\";\nexport { RestartingProcessStateSchema } from \"../restarting-process.ts\";\nexport { CronProcessStateSchema } from \"../cron-process.ts\";\nexport { TaskStateSchema } from \"../task-list.ts\";\n\nconst oc = ocBase.$input(v.void());\n\n// Resource target (name or index)\nconst ResourceTarget = v.union([v.string(), v.number()]);\n\n// Manager state schema\nexport const ManagerStateSchema = v.picklist([\n \"idle\",\n \"initializing\",\n \"running\",\n \"stopping\",\n \"stopped\",\n]);\n\nexport type ManagerState = v.InferOutput<typeof ManagerStateSchema>;\n\n// Manager status response\nexport const ManagerStatusSchema = v.object({\n state: ManagerStateSchema,\n processCount: v.number(),\n cronCount: v.number(),\n taskCount: v.number(),\n});\n\nexport type ManagerStatus = v.InferOutput<typeof ManagerStatusSchema>;\n\n// API response schemas\nexport const RestartingProcessInfoSchema = v.object({\n name: v.string(),\n state: RestartingProcessStateSchema,\n restarts: v.number(),\n});\n\nexport type RestartingProcessInfo = v.InferOutput<typeof RestartingProcessInfoSchema>;\n\nexport const CronProcessInfoSchema = v.object({\n name: v.string(),\n state: CronProcessStateSchema,\n runCount: v.number(),\n failCount: v.number(),\n nextRun: v.nullable(v.string()), // ISO date string\n});\n\nexport type CronProcessInfo = v.InferOutput<typeof CronProcessInfoSchema>;\n\nexport const TaskEntryInfoSchema = v.object({\n id: v.string(),\n state: TaskStateSchema,\n processNames: v.array(v.string()),\n});\n\nexport type TaskEntryInfo = v.InferOutput<typeof TaskEntryInfoSchema>;\n\n// API contract\nexport const manager = {\n status: oc.output(ManagerStatusSchema),\n};\n\nexport const processes = {\n get: oc.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),\n list: oc.output(v.array(RestartingProcessInfoSchema)),\n add: oc\n .input(v.object({ name: v.string(), definition: ProcessDefinitionSchema }))\n .output(RestartingProcessInfoSchema),\n start: oc.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),\n stop: oc.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),\n restart: oc\n .input(v.object({ target: ResourceTarget, force: v.optional(v.boolean()) }))\n .output(RestartingProcessInfoSchema),\n reload: oc\n .input(\n v.object({\n target: ResourceTarget,\n definition: ProcessDefinitionSchema,\n restartImmediately: v.optional(v.boolean()),\n }),\n )\n .output(RestartingProcessInfoSchema),\n remove: oc.input(v.object({ target: ResourceTarget })).output(v.object({ success: v.boolean() })),\n};\n\nexport const tasks = {\n get: oc.input(v.object({ target: ResourceTarget })).output(TaskEntryInfoSchema),\n list: oc.output(v.array(TaskEntryInfoSchema)),\n add: oc\n .input(v.object({ name: v.string(), definition: ProcessDefinitionSchema }))\n .output(TaskEntryInfoSchema),\n remove: oc.input(v.object({ target: ResourceTarget })).output(TaskEntryInfoSchema),\n};\n\nexport const crons = {\n get: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),\n list: oc.output(v.array(CronProcessInfoSchema)),\n trigger: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),\n start: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),\n stop: oc.input(v.object({ target: ResourceTarget })).output(CronProcessInfoSchema),\n};\n\nexport const api = {\n manager,\n processes,\n tasks,\n crons,\n};\n","import { implement } from \"@orpc/server\";\nimport {\n api,\n type RestartingProcessInfo,\n type CronProcessInfo,\n type TaskEntryInfo,\n type ManagerStatus,\n} from \"./contract.ts\";\nimport type { Manager } from \"../manager.ts\";\nimport type { RestartingProcess } from \"../restarting-process.ts\";\nimport type { CronProcess } from \"../cron-process.ts\";\n\nconst os = implement(api).$context<{ manager: Manager }>();\n\n// Helper to serialize a RestartingProcess to API response\nfunction serializeProcess(proc: RestartingProcess): RestartingProcessInfo {\n return {\n name: proc.name,\n state: proc.state,\n restarts: proc.restarts,\n };\n}\n\n// Helper to serialize a CronProcess to API response\nfunction serializeCron(cron: CronProcess): CronProcessInfo {\n return {\n name: cron.name,\n state: cron.state,\n runCount: cron.runCount,\n failCount: cron.failCount,\n nextRun: cron.nextRun?.toISOString() ?? null,\n };\n}\n\n// Manager handlers\nconst managerStatus = os.manager.status.handler(async ({ context }): Promise<ManagerStatus> => {\n const manager = context.manager;\n const taskList = manager.getTaskList();\n return {\n state: manager.state,\n processCount: manager.getRestartingProcesses().size,\n cronCount: manager.getCronProcesses().size,\n taskCount: taskList?.tasks.length ?? 0,\n };\n});\n\n// Processes handlers\nconst getProcess = os.processes.get.handler(async ({ input, context }) => {\n const proc = context.manager.getProcessByTarget(input.target);\n if (!proc) {\n throw new Error(`Process not found: ${input.target}`);\n }\n return serializeProcess(proc);\n});\n\nconst listProcesses = os.processes.list.handler(async ({ context }) => {\n const processes = Array.from(context.manager.getRestartingProcesses().values());\n return processes.map(serializeProcess);\n});\n\nconst addProcess = os.processes.add.handler(async ({ input, context }) => {\n const proc = await context.manager.addProcess(input.name, input.definition);\n return serializeProcess(proc);\n});\n\nconst startProcess = os.processes.start.handler(async ({ input, context }) => {\n const proc = await context.manager.startProcessByTarget(input.target);\n return serializeProcess(proc);\n});\n\nconst stopProcess = os.processes.stop.handler(async ({ input, context }) => {\n const proc = await context.manager.stopProcessByTarget(input.target);\n return serializeProcess(proc);\n});\n\nconst restartProcess = os.processes.restart.handler(async ({ input, context }) => {\n const proc = await context.manager.restartProcessByTarget(input.target, input.force);\n return serializeProcess(proc);\n});\n\nconst reloadProcess = os.processes.reload.handler(async ({ input, context }) => {\n const proc = await context.manager.reloadProcessByTarget(input.target, input.definition, {\n restartImmediately: input.restartImmediately,\n });\n return serializeProcess(proc);\n});\n\nconst removeProcess = os.processes.remove.handler(async ({ input, context }) => {\n await context.manager.removeProcessByTarget(input.target);\n return { success: true };\n});\n\n// Crons handlers\nconst getCron = os.crons.get.handler(async ({ input, context }) => {\n const cron = context.manager.getCronByTarget(input.target);\n if (!cron) {\n throw new Error(`Cron not found: ${input.target}`);\n }\n return serializeCron(cron);\n});\n\nconst listCrons = os.crons.list.handler(async ({ context }) => {\n const crons = Array.from(context.manager.getCronProcesses().values());\n return crons.map(serializeCron);\n});\n\nconst triggerCron = os.crons.trigger.handler(async ({ input, context }) => {\n const cron = await context.manager.triggerCronByTarget(input.target);\n return serializeCron(cron);\n});\n\nconst startCron = os.crons.start.handler(async ({ input, context }) => {\n const cron = context.manager.startCronByTarget(input.target);\n return serializeCron(cron);\n});\n\nconst stopCron = os.crons.stop.handler(async ({ input, context }) => {\n const cron = await context.manager.stopCronByTarget(input.target);\n return serializeCron(cron);\n});\n\n// Tasks handlers\nconst getTask = os.tasks.get.handler(async ({ input, context }) => {\n const task = context.manager.getTaskByTarget(input.target);\n if (!task) {\n throw new Error(`Task not found: ${input.target}`);\n }\n return task as TaskEntryInfo;\n});\n\nconst listTasks = os.tasks.list.handler(async ({ context }) => {\n const taskList = context.manager.getTaskList();\n if (!taskList) {\n return [];\n }\n return taskList.tasks.map((t) => ({\n id: t.id,\n state: t.state,\n processNames: t.processes.map((p) => p.name),\n })) as TaskEntryInfo[];\n});\n\nconst addTask = os.tasks.add.handler(async ({ input, context }) => {\n const task = context.manager.addTask(input.name, input.definition);\n return task as TaskEntryInfo;\n});\n\nconst removeTask = os.tasks.remove.handler(async ({ input, context }) => {\n const task = context.manager.removeTaskByTarget(input.target);\n return task as TaskEntryInfo;\n});\n\nexport const router = os.router({\n manager: {\n status: managerStatus,\n },\n processes: {\n add: addProcess,\n get: getProcess,\n list: listProcesses,\n start: startProcess,\n stop: stopProcess,\n restart: restartProcess,\n reload: reloadProcess,\n remove: removeProcess,\n },\n crons: {\n get: getCron,\n list: listCrons,\n trigger: triggerCron,\n start: startCron,\n stop: stopCron,\n },\n tasks: {\n get: getTask,\n list: listTasks,\n add: addTask,\n remove: removeTask,\n },\n});\n","import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { treeKill } from \"./tree-kill.ts\";\n\nconst execAsync = promisify(exec);\n\n/**\n * Kill any process listening on the specified port, including all descendants.\n */\nexport async function killProcessOnPort(port: number): Promise<boolean> {\n try {\n // Use lsof to find the process ID listening on the port\n const { stdout } = await execAsync(`lsof -ti tcp:${port}`);\n const pids = stdout\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .map((p) => parseInt(p, 10))\n .filter((p) => !isNaN(p));\n\n if (pids.length === 0) {\n return false; // No process found on port\n }\n\n // Tree kill all processes found on the port (kills children/grandchildren too)\n for (const pid of pids) {\n try {\n await treeKill(pid, \"SIGKILL\");\n } catch {\n // Process may have already exited\n }\n }\n\n return true;\n } catch {\n // Command failed (e.g., no process on port)\n return false;\n }\n}\n","import * as v from \"valibot\";\nimport { join } from \"node:path\";\nimport { cwd as getCwd } from \"node:process\";\nimport { mkdirSync } from \"node:fs\";\nimport { ProcessDefinitionSchema, type ProcessDefinition } from \"./lazy-process.ts\";\nimport type { Logger } from \"./logger.ts\";\nimport { TaskList, type NamedProcessDefinition } from \"./task-list.ts\";\nimport { CronProcess, CronProcessOptionsSchema } from \"./cron-process.ts\";\nimport {\n RestartingProcess,\n RestartingProcessOptionsSchema,\n type RestartingProcessOptions,\n} from \"./restarting-process.ts\";\nimport { EnvManager } from \"./env-manager.ts\";\nimport { killProcessOnPort } from \"./port-utils.ts\";\n\n// Valibot schemas\n\n// HTTP server configuration schema\nexport const HttpServerConfigSchema = v.object({\n host: v.optional(v.string()),\n port: v.optional(v.number()),\n authToken: v.optional(v.string()),\n});\n\nexport type HttpServerConfig = v.InferOutput<typeof HttpServerConfigSchema>;\n\n// Cron process entry schema\nexport const CronProcessEntrySchema = v.object({\n name: v.string(),\n definition: ProcessDefinitionSchema,\n options: CronProcessOptionsSchema,\n envFile: v.optional(v.string()),\n});\n\nexport type CronProcessEntry = v.InferOutput<typeof CronProcessEntrySchema>;\n\n// Env reload delay schema - can be number (ms), \"immediately\"/true, or false for disabled\nexport const EnvReloadDelaySchema = v.union([v.number(), v.boolean(), v.literal(\"immediately\")]);\n\nexport type EnvReloadDelay = v.InferOutput<typeof EnvReloadDelaySchema>;\n\n// Restarting process entry schema\nexport const RestartingProcessEntrySchema = v.object({\n name: v.string(),\n definition: ProcessDefinitionSchema,\n options: v.optional(RestartingProcessOptionsSchema),\n envFile: v.optional(v.string()),\n envReloadDelay: v.optional(EnvReloadDelaySchema), // Default 5000ms\n port: v.optional(v.number()), // Kill any process on this port before spawning\n});\n\nexport type RestartingProcessEntry = v.InferOutput<typeof RestartingProcessEntrySchema>;\n\n// Task entry schema with envFile\nexport const TaskEntrySchema = v.object({\n name: v.string(),\n definition: ProcessDefinitionSchema,\n envFile: v.optional(v.string()),\n});\n\nexport type TaskEntry = v.InferOutput<typeof TaskEntrySchema>;\n\n// Main manager configuration schema\nexport const ManagerConfigSchema = v.object({\n http: v.optional(HttpServerConfigSchema),\n cwd: v.optional(v.string()),\n logDir: v.optional(v.string()),\n env: v.optional(v.record(v.string(), v.string())),\n envFiles: v.optional(v.record(v.string(), v.string())),\n tasks: v.optional(v.array(TaskEntrySchema)),\n crons: v.optional(v.array(CronProcessEntrySchema)),\n processes: v.optional(v.array(RestartingProcessEntrySchema)),\n});\n\nexport type ManagerConfig = v.InferOutput<typeof ManagerConfigSchema>;\n\n// Default restart policy when not specified\nconst DEFAULT_RESTART_OPTIONS = {\n restartPolicy: \"always\" as const,\n};\n\n// Hardcoded shutdown configuration\nconst SHUTDOWN_TIMEOUT_MS = 15000;\nconst SHUTDOWN_SIGNALS: NodeJS.Signals[] = [\"SIGINT\", \"SIGTERM\"];\nconst SHUTDOWN_EXIT_CODE = 0;\n\n// Manager state\nexport type ManagerState =\n | \"idle\" // Not started\n | \"initializing\" // Running task list\n | \"running\" // All processes running\n | \"stopping\" // Stopping all processes\n | \"stopped\"; // Fully stopped\n\nexport class Manager {\n private config: ManagerConfig;\n private logger: Logger;\n private envManager: EnvManager;\n private envFileKeyByProcess: Map<string, string> = new Map();\n\n private _state: ManagerState = \"idle\";\n private taskList: TaskList | null = null;\n private cronProcesses: Map<string, CronProcess> = new Map();\n private restartingProcesses: Map<string, RestartingProcess> = new Map();\n private logDir: string;\n\n // Env reload tracking\n private processEnvReloadConfig: Map<string, EnvReloadDelay> = new Map();\n private envReloadTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();\n private envChangeUnsubscribe: (() => void) | null = null;\n\n // Port tracking for processes\n private processPortConfig: Map<string, number> = new Map();\n\n // Shutdown handling\n private signalHandlers: Map<NodeJS.Signals, () => void> = new Map();\n private shutdownPromise: Promise<void> | null = null;\n private isShuttingDown: boolean = false;\n\n constructor(config: ManagerConfig, logger: Logger) {\n this.config = config;\n this.logger = logger;\n this.logDir = config.logDir ?? join(getCwd(), \"logs\");\n this.ensureLogDirs();\n // Initialize EnvManager with watching enabled\n this.envManager = new EnvManager({\n cwd: config.cwd,\n files: config.envFiles,\n watch: true,\n });\n\n // Register env change handler\n this.envChangeUnsubscribe = this.envManager.onChange((changedKeys) => {\n this.handleEnvChange(changedKeys);\n });\n\n // Automatically register shutdown handlers\n this.registerShutdownHandlers();\n }\n\n /**\n * Merge global env with process-specific env and apply cwd inheritance\n * Merge order: .env (global), config.env (global), .env.<processName>, processEnvFile, definition.env\n */\n private applyDefaults(\n processName: string,\n definition: ProcessDefinition,\n envFile?: string,\n ): ProcessDefinition {\n // Start with env from .env file (global)\n const envFromFiles = this.envManager.getEnvVars(processName);\n\n // Build the env file map for this specific process if envFile is provided\n let envFromCustomFile: Record<string, string> = {};\n if (envFile) {\n let key = this.envFileKeyByProcess.get(processName);\n if (!key) {\n key = `custom:${processName}`;\n this.envFileKeyByProcess.set(processName, key);\n this.envManager.registerFile(key, envFile);\n }\n envFromCustomFile = this.envManager.getEnvForKey(key);\n }\n\n return {\n ...definition,\n cwd: definition.cwd ?? this.config.cwd,\n env: {\n ...envFromFiles, // .env (global) + .env.<processName>\n ...this.config.env, // Global env from config\n ...envFromCustomFile, // Custom env file if specified\n ...definition.env, // Process-specific env overrides\n },\n };\n }\n\n private processLogFile(name: string): string {\n return join(this.logDir, \"process\", `${name}.log`);\n }\n\n private taskLogFile(name: string): string {\n return join(this.logDir, \"tasks\", `${name}.log`);\n }\n\n private cronLogFile(name: string): string {\n return join(this.logDir, \"cron\", `${name}.log`);\n }\n\n /**\n * Kill any process running on the configured port for a process\n */\n private async killPortForProcess(processName: string): Promise<void> {\n const port = this.processPortConfig.get(processName);\n if (port === undefined) return;\n\n const killed = await killProcessOnPort(port);\n if (killed) {\n this.logger.info(`Killed process on port ${port} before starting \"${processName}\"`);\n }\n }\n\n private ensureLogDirs(): void {\n mkdirSync(this.logDir, { recursive: true });\n mkdirSync(join(this.logDir, \"process\"), { recursive: true });\n mkdirSync(join(this.logDir, \"tasks\"), { recursive: true });\n mkdirSync(join(this.logDir, \"cron\"), { recursive: true });\n }\n\n /**\n * Handle env file changes\n */\n private handleEnvChange(changedKeys: string[]): void {\n if (this._state !== \"running\") {\n return; // Only handle changes when manager is running\n }\n\n this.logger.info(`Env files changed for keys: ${changedKeys.join(\", \")}`);\n\n // Check which processes are affected\n const affectedProcesses = new Set<string>();\n\n for (const key of changedKeys) {\n // Global key affects all processes\n if (key === \"global\") {\n for (const processName of this.restartingProcesses.keys()) {\n affectedProcesses.add(processName);\n }\n } else {\n // Specific key affects matching process\n if (this.restartingProcesses.has(key)) {\n affectedProcesses.add(key);\n }\n\n for (const [processName, customKey] of this.envFileKeyByProcess.entries()) {\n if (customKey === key && this.restartingProcesses.has(processName)) {\n affectedProcesses.add(processName);\n }\n }\n }\n }\n\n // Schedule restarts for affected processes\n for (const processName of affectedProcesses) {\n const reloadDelay = this.processEnvReloadConfig.get(processName);\n\n // Skip if reload is disabled for this process\n if (reloadDelay === false) {\n continue;\n }\n\n this.scheduleProcessReload(processName, reloadDelay);\n }\n }\n\n /**\n * Schedule a process reload with debouncing\n */\n private scheduleProcessReload(processName: string, reloadDelay?: EnvReloadDelay): void {\n // Clear existing timer if any\n const existingTimer = this.envReloadTimers.get(processName);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Determine delay in ms\n let delayMs: number;\n if (reloadDelay === false) {\n return; // Should not happen, but guard anyway\n } else if (reloadDelay === true || reloadDelay === \"immediately\") {\n delayMs = 0;\n } else if (typeof reloadDelay === \"number\") {\n delayMs = reloadDelay;\n } else {\n delayMs = 5000; // Default 5 seconds\n }\n\n this.logger.info(`Scheduling reload for process \"${processName}\" in ${delayMs}ms`);\n\n const timer = setTimeout(async () => {\n await this.reloadProcessEnv(processName);\n this.envReloadTimers.delete(processName);\n }, delayMs);\n\n this.envReloadTimers.set(processName, timer);\n }\n\n /**\n * Reload a process with updated env vars\n */\n private async reloadProcessEnv(processName: string): Promise<void> {\n const proc = this.restartingProcesses.get(processName);\n if (!proc) {\n this.logger.warn(`Process \"${processName}\" not found for env reload`);\n return;\n }\n\n this.logger.info(`Reloading process \"${processName}\" due to env change`);\n\n // Get the original config for this process\n const processConfig = this.config.processes?.find((p) => p.name === processName);\n if (!processConfig) {\n this.logger.warn(`Process config for \"${processName}\" not found`);\n return;\n }\n\n // Rebuild definition with updated env\n const updatedDefinition = this.applyDefaults(\n processName,\n processConfig.definition,\n processConfig.envFile,\n );\n\n // Reload the process (graceful restart)\n await proc.reload(updatedDefinition, true);\n }\n\n get state(): ManagerState {\n return this._state;\n }\n\n /**\n * Get all cron processes (read-only access)\n */\n getCronProcesses(): ReadonlyMap<string, CronProcess> {\n return this.cronProcesses;\n }\n\n /**\n * Get a specific cron process by name\n */\n getCronProcess(name: string): CronProcess | undefined {\n return this.cronProcesses.get(name);\n }\n\n /**\n * Get all restarting processes (read-only access)\n */\n getRestartingProcesses(): ReadonlyMap<string, RestartingProcess> {\n return this.restartingProcesses;\n }\n\n /**\n * Get a specific restarting process by name\n */\n getRestartingProcess(name: string): RestartingProcess | undefined {\n return this.restartingProcesses.get(name);\n }\n\n /**\n * Get the task list (read-only access)\n */\n getTaskList(): TaskList | null {\n return this.taskList;\n }\n\n /**\n * Get a restarting process by name or index\n */\n getProcessByTarget(target: string | number): RestartingProcess | undefined {\n if (typeof target === \"string\") {\n return this.restartingProcesses.get(target);\n }\n const entries = Array.from(this.restartingProcesses.values());\n return entries[target];\n }\n\n /**\n * Get a cron process by name or index\n */\n getCronByTarget(target: string | number): CronProcess | undefined {\n if (typeof target === \"string\") {\n return this.cronProcesses.get(target);\n }\n const entries = Array.from(this.cronProcesses.values());\n return entries[target];\n }\n\n /**\n * Get a task by id or index\n */\n getTaskByTarget(\n target: string | number,\n ): { id: string; state: string; processNames: string[] } | undefined {\n if (!this.taskList) return undefined;\n const tasks = this.taskList.tasks;\n\n if (typeof target === \"string\") {\n const task = tasks.find((t) => t.id === target);\n if (!task) return undefined;\n return {\n id: task.id,\n state: task.state,\n processNames: task.processes.map((p) => p.name),\n };\n }\n\n const task = tasks[target];\n if (!task) return undefined;\n return {\n id: task.id,\n state: task.state,\n processNames: task.processes.map((p) => p.name),\n };\n }\n\n /**\n * Start a restarting process by target\n */\n async startProcessByTarget(target: string | number): Promise<RestartingProcess> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n // Kill any process on the configured port before starting\n await this.killPortForProcess(proc.name);\n proc.start();\n return proc;\n }\n\n /**\n * Stop a restarting process by target\n */\n async stopProcessByTarget(target: string | number, timeout?: number): Promise<RestartingProcess> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n await proc.stop(timeout);\n return proc;\n }\n\n /**\n * Restart a restarting process by target\n */\n async restartProcessByTarget(\n target: string | number,\n force: boolean = false,\n ): Promise<RestartingProcess> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n // Kill any process on the configured port before restarting\n await this.killPortForProcess(proc.name);\n await proc.restart(force);\n return proc;\n }\n\n /**\n * Reload a restarting process with new definition\n */\n async reloadProcessByTarget(\n target: string | number,\n newDefinition: ProcessDefinition,\n options?: {\n restartImmediately?: boolean;\n updateOptions?: Partial<RestartingProcessOptions>;\n },\n ): Promise<RestartingProcess> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n\n // Apply global defaults to new definition\n const definitionWithDefaults = this.applyDefaults(proc.name, newDefinition);\n\n // Update options if provided\n if (options?.updateOptions) {\n proc.updateOptions(options.updateOptions);\n }\n\n // Reload with new definition\n await proc.reload(definitionWithDefaults, options?.restartImmediately ?? true);\n this.logger.info(`Reloaded process: ${proc.name}`);\n return proc;\n }\n\n /**\n * Remove a restarting process by target\n */\n async removeProcessByTarget(target: string | number, timeout?: number): Promise<void> {\n const proc = this.getProcessByTarget(target);\n if (!proc) {\n throw new Error(`Process not found: ${target}`);\n }\n\n // Stop the process first\n await proc.stop(timeout);\n\n // Remove from the map\n this.restartingProcesses.delete(proc.name);\n this.logger.info(`Removed process: ${proc.name}`);\n }\n\n /**\n * Add a task to the task list\n * Creates the task list if it doesn't exist and starts it\n */\n addTask(\n name: string,\n definition: ProcessDefinition,\n ): { id: string; state: string; processNames: string[] } {\n if (!this.taskList) {\n const taskListLogger = this.logger.child(\"tasks\", {\n logFile: this.taskLogFile(\"tasks\"),\n });\n this.taskList = new TaskList(\"runtime\", taskListLogger, undefined, (processName) => {\n return this.taskLogFile(processName);\n });\n }\n\n const namedProcess: NamedProcessDefinition = {\n name,\n process: this.applyDefaults(name, definition),\n };\n const id = this.taskList.addTask(namedProcess);\n\n // Start the task list if it's idle so the task runs immediately\n if (this.taskList.state === \"idle\") {\n this.taskList.start();\n }\n\n return {\n id,\n state: \"pending\",\n processNames: [name],\n };\n }\n\n removeTaskByTarget(target: string | number): {\n id: string;\n state: string;\n processNames: string[];\n } {\n if (!this.taskList) {\n throw new Error(`Task list not initialized`);\n }\n\n const removed = this.taskList.removeTaskByTarget(target);\n return {\n id: removed.id,\n state: removed.state,\n processNames: removed.processes.map((p) => p.name),\n };\n }\n\n /**\n * Add a restarting process at runtime\n */\n async addProcess(\n name: string,\n definition: ProcessDefinition,\n options?: RestartingProcessOptions,\n envReloadDelay?: EnvReloadDelay,\n port?: number,\n ): Promise<RestartingProcess> {\n if (this.restartingProcesses.has(name)) {\n throw new Error(`Process \"${name}\" already exists`);\n }\n\n const processLogger = this.logger.child(name, { logFile: this.processLogFile(name) });\n const restartingProcess = new RestartingProcess(\n name,\n this.applyDefaults(name, definition),\n options ?? DEFAULT_RESTART_OPTIONS,\n processLogger,\n );\n this.restartingProcesses.set(name, restartingProcess);\n\n // Track port config and kill any process on that port before starting\n if (port !== undefined) {\n this.processPortConfig.set(name, port);\n await this.killPortForProcess(name);\n }\n\n restartingProcess.start();\n\n // Track env reload config for this process\n this.processEnvReloadConfig.set(name, envReloadDelay ?? 5000);\n\n this.logger.info(`Added and started restarting process: ${name}`);\n return restartingProcess;\n }\n\n /**\n * Trigger a cron process by target\n */\n async triggerCronByTarget(target: string | number): Promise<CronProcess> {\n const cron = this.getCronByTarget(target);\n if (!cron) {\n throw new Error(`Cron not found: ${target}`);\n }\n await cron.trigger();\n return cron;\n }\n\n /**\n * Start a cron process by target\n */\n startCronByTarget(target: string | number): CronProcess {\n const cron = this.getCronByTarget(target);\n if (!cron) {\n throw new Error(`Cron not found: ${target}`);\n }\n cron.start();\n return cron;\n }\n\n /**\n * Stop a cron process by target\n */\n async stopCronByTarget(target: string | number, timeout?: number): Promise<CronProcess> {\n const cron = this.getCronByTarget(target);\n if (!cron) {\n throw new Error(`Cron not found: ${target}`);\n }\n await cron.stop(timeout);\n return cron;\n }\n\n /**\n * Start the manager:\n * 1. Run task list (if configured) and wait for completion\n * 2. Create and start all cron/restarting processes\n */\n async start(): Promise<void> {\n if (this._state !== \"idle\" && this._state !== \"stopped\") {\n throw new Error(`Manager is already ${this._state}`);\n }\n\n this.logger.info(`Starting manager`);\n\n // Phase 1: Run initialization tasks\n if (this.config.tasks && this.config.tasks.length > 0) {\n this._state = \"initializing\";\n this.logger.info(`Running initialization tasks`);\n\n const taskListLogger = this.logger.child(\"tasks\");\n // Apply defaults to all tasks\n const tasksWithDefaults = this.config.tasks.map((task) => ({\n name: task.name,\n process: this.applyDefaults(task.name, task.definition, task.envFile),\n }));\n this.taskList = new TaskList(\"init\", taskListLogger, tasksWithDefaults, (processName) => {\n return this.taskLogFile(processName);\n });\n\n this.taskList.start();\n await this.taskList.waitUntilIdle();\n\n // Check if any tasks failed\n const failedTasks = this.taskList.tasks.filter((t) => t.state === \"failed\");\n if (failedTasks.length > 0) {\n this._state = \"stopped\";\n const failedNames = failedTasks.map((t) => t.id).join(\", \");\n throw new Error(`Initialization failed: tasks [${failedNames}] failed`);\n }\n\n this.logger.info(`Initialization tasks completed`);\n }\n\n // Phase 2: Create and start cron processes\n if (this.config.crons) {\n for (const entry of this.config.crons) {\n const processLogger = this.logger.child(entry.name, {\n logFile: this.cronLogFile(entry.name),\n });\n const cronProcess = new CronProcess(\n entry.name,\n this.applyDefaults(entry.name, entry.definition, entry.envFile),\n entry.options,\n processLogger,\n );\n this.cronProcesses.set(entry.name, cronProcess);\n cronProcess.start();\n this.logger.info(`Started cron process: ${entry.name}`);\n }\n }\n\n // Phase 3: Create and start restarting processes\n if (this.config.processes) {\n for (const entry of this.config.processes) {\n const processLogger = this.logger.child(entry.name, {\n logFile: this.processLogFile(entry.name),\n });\n const restartingProcess = new RestartingProcess(\n entry.name,\n this.applyDefaults(entry.name, entry.definition, entry.envFile),\n entry.options ?? DEFAULT_RESTART_OPTIONS,\n processLogger,\n );\n this.restartingProcesses.set(entry.name, restartingProcess);\n\n // Track port config and kill any process on that port before starting\n if (entry.port !== undefined) {\n this.processPortConfig.set(entry.name, entry.port);\n await this.killPortForProcess(entry.name);\n }\n\n restartingProcess.start();\n\n // Track env reload config for this process\n this.processEnvReloadConfig.set(\n entry.name,\n entry.envReloadDelay ?? 5000, // Default to 5000ms\n );\n\n this.logger.info(`Started restarting process: ${entry.name}`);\n }\n }\n\n this._state = \"running\";\n this.logger.info(\n `Manager started with ${this.cronProcesses.size} cron process(es) and ${this.restartingProcesses.size} restarting process(es)`,\n );\n }\n\n /**\n * Stop all managed processes\n */\n async stop(timeout?: number): Promise<void> {\n if (this._state === \"idle\" || this._state === \"stopped\") {\n this._state = \"stopped\";\n this.unregisterShutdownHandlers();\n return;\n }\n\n this._state = \"stopping\";\n this.logger.info(`Stopping manager`);\n\n // Clear all env reload timers\n for (const timer of this.envReloadTimers.values()) {\n clearTimeout(timer);\n }\n this.envReloadTimers.clear();\n\n // Unsubscribe from env changes\n if (this.envChangeUnsubscribe) {\n this.envChangeUnsubscribe();\n this.envChangeUnsubscribe = null;\n }\n\n // Dispose env manager\n this.envManager.dispose();\n\n // Stop task list if still running\n if (this.taskList) {\n await this.taskList.stop(timeout);\n }\n\n // Stop all cron processes in parallel\n const cronStopPromises = Array.from(this.cronProcesses.values()).map((p) => p.stop(timeout));\n\n // Stop all restarting processes in parallel\n const restartingStopPromises = Array.from(this.restartingProcesses.values()).map((p) =>\n p.stop(timeout),\n );\n\n await Promise.all([...cronStopPromises, ...restartingStopPromises]);\n\n this._state = \"stopped\";\n this.logger.info(`Manager stopped`);\n\n this.unregisterShutdownHandlers();\n }\n\n /**\n * Register signal handlers for graceful shutdown\n * Called automatically by constructor\n */\n private registerShutdownHandlers(): void {\n if (this.signalHandlers.size > 0) {\n this.logger.warn(`Shutdown handlers already registered`);\n return;\n }\n\n for (const signal of SHUTDOWN_SIGNALS) {\n const handler = () => this.handleSignal(signal);\n this.signalHandlers.set(signal, handler);\n process.on(signal, handler);\n this.logger.debug(`Registered handler for ${signal}`);\n }\n\n this.logger.info(`Shutdown handlers registered for signals: ${SHUTDOWN_SIGNALS.join(\", \")}`);\n }\n\n /**\n * Unregister signal handlers for graceful shutdown\n */\n private unregisterShutdownHandlers(): void {\n if (this.signalHandlers.size === 0) {\n return;\n }\n\n for (const [signal, handler] of this.signalHandlers.entries()) {\n process.off(signal, handler);\n this.logger.debug(`Unregistered handler for ${signal}`);\n }\n\n this.signalHandlers.clear();\n }\n\n /**\n * Wait for shutdown to complete (useful for keeping process alive)\n * Resolves when the manager has fully stopped\n */\n async waitForShutdown(): Promise<void> {\n // If already stopped, return immediately\n if (this._state === \"stopped\") {\n return;\n }\n\n // If shutdown is in progress, wait for it\n if (this.shutdownPromise) {\n await this.shutdownPromise;\n return;\n }\n\n // Wait for state to become stopped\n return new Promise((resolve) => {\n const checkInterval = setInterval(() => {\n if (this._state === \"stopped\") {\n clearInterval(checkInterval);\n resolve();\n }\n }, 100);\n });\n }\n\n /**\n * Trigger graceful shutdown programmatically\n */\n async shutdown(): Promise<void> {\n if (this.isShuttingDown) {\n this.logger.warn(`Shutdown already in progress`);\n if (this.shutdownPromise) {\n await this.shutdownPromise;\n }\n return;\n }\n\n this.isShuttingDown = true;\n this.logger.info(`Initiating graceful shutdown (timeout: ${SHUTDOWN_TIMEOUT_MS}ms)`);\n\n this.shutdownPromise = this.performShutdown();\n await this.shutdownPromise;\n }\n\n private handleSignal(signal: NodeJS.Signals): void {\n this.logger.info(`Received ${signal}, initiating graceful shutdown...`);\n\n // Prevent handling multiple signals\n if (this.isShuttingDown) {\n this.logger.warn(`Shutdown already in progress, ignoring ${signal}`);\n return;\n }\n\n this.isShuttingDown = true;\n this.shutdownPromise = this.performShutdown();\n\n this.shutdownPromise\n .then(() => {\n this.logger.info(`Exiting with code ${SHUTDOWN_EXIT_CODE}`);\n process.exit(SHUTDOWN_EXIT_CODE);\n })\n .catch((err) => {\n this.logger.error(`Shutdown error:`, err);\n process.exit(1);\n });\n }\n\n private async performShutdown(): Promise<void> {\n // Create a timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`Shutdown timed out after ${SHUTDOWN_TIMEOUT_MS}ms`));\n }, SHUTDOWN_TIMEOUT_MS);\n });\n\n try {\n // Race between graceful stop and timeout\n await Promise.race([this.stop(SHUTDOWN_TIMEOUT_MS), timeoutPromise]);\n this.logger.info(`Graceful shutdown completed`);\n } catch (err) {\n this.logger.error(`Shutdown error:`, err);\n // Force stop on timeout\n this._state = \"stopped\";\n throw err;\n } finally {\n this.isShuttingDown = false;\n this.shutdownPromise = null;\n }\n }\n}\n","#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { createServer } from \"node:http\";\nimport { pathToFileURL } from \"node:url\";\nimport { resolve } from \"node:path\";\nimport { RPCHandler } from \"@orpc/server/node\";\nimport { onError } from \"@orpc/server\";\nimport * as v from \"valibot\";\nimport { router } from \"./api/server.ts\";\nimport { Manager, ManagerConfigSchema } from \"./manager.ts\";\nimport { logger } from \"./logger.ts\";\nimport { tsImport } from \"tsx/esm/api\";\nimport Table from \"cli-table3\";\nimport { createClient } from \"./api/client.ts\";\nimport { ProcessDefinitionSchema } from \"./api/contract.ts\";\n\nconst program = new Command();\n\nprogram\n .name(\"pidnap\")\n .description(\"Process manager with init system capabilities\")\n .version(\"0.0.0-dev.1\");\n\nprogram\n .command(\"init\")\n .description(\"Initialize and run the process manager with config file\")\n .option(\"-c, --config [path]\", \"Path to config file\", \"pidnap.config.ts\")\n .action(async (options) => {\n const initLogger = logger({ name: \"pidnap\" });\n try {\n // Resolve config file path\n const configPath = resolve(process.cwd(), options.config);\n const configUrl = pathToFileURL(configPath).href;\n\n // Import the config file\n const configModule = await tsImport(configUrl, { parentURL: import.meta.url });\n const rawConfig =\n configModule.default.default || configModule.default || configModule.config || {};\n\n // Parse and validate the config with Valibot\n const config = v.parse(ManagerConfigSchema, rawConfig);\n\n // Extract HTTP config with defaults\n const host = config.http?.host ?? \"localhost\";\n const port = config.http?.port ?? 3000;\n const authToken = config.http?.authToken;\n\n // Create manager with config\n const logDir = config.logDir ?? resolve(process.cwd(), \"logs\");\n const managerLogger = logger({ name: \"pidnap\", logFile: resolve(logDir, \"pidnap.log\") });\n const manager = new Manager(config, managerLogger);\n\n // Setup ORPC server with optional auth token middleware\n const handler = new RPCHandler(router, {\n interceptors: [\n onError((error) => {\n managerLogger.error(error);\n }),\n ],\n });\n\n const server = createServer(async (req, res) => {\n // Check auth token if configured\n if (authToken) {\n const providedToken = req.headers[\"authorization\"]?.replace(\"Bearer \", \"\");\n if (providedToken !== authToken) {\n res.statusCode = 401;\n res.end(\"Unauthorized\");\n return;\n }\n }\n\n const { matched } = await handler.handle(req, res, {\n prefix: \"/rpc\",\n context: { manager },\n });\n if (matched) return;\n res.statusCode = 404;\n res.end(\"Not found\");\n });\n\n server.listen(port, host, async () => {\n managerLogger.info(`pidnap RPC server running on http://${host}:${port}`);\n if (authToken) {\n managerLogger.info(\"Auth token required for API access\");\n }\n\n try {\n await manager.start();\n } catch (err) {\n managerLogger.error(\"Failed to start manager:\", err);\n server.close();\n process.exit(1);\n }\n });\n\n // Wait for shutdown\n await manager.waitForShutdown();\n\n // Close server on shutdown\n server.close();\n } catch (error) {\n initLogger.error(\"Failed to start pidnap:\", error);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"status\")\n .description(\"Show manager status\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const status = await client.manager.status();\n\n const table = new Table({ head: [\"State\", \"Processes\", \"Crons\", \"Tasks\"] });\n table.push([status.state, status.processCount, status.cronCount, status.taskCount]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to fetch status:\", error);\n process.exit(1);\n }\n });\n\nconst processes = program.command(\"processes\").description(\"Manage restarting processes\");\n\nprocesses\n .command(\"list\")\n .description(\"List restarting processes\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const processes = await client.processes.list();\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n for (const proc of processes) {\n table.push([proc.name, proc.state, proc.restarts]);\n }\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to list processes:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"get\")\n .description(\"Get a restarting process by name or index\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const proc = await client.processes.get({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to get process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"add\")\n .description(\"Add a restarting process\")\n .requiredOption(\"-n, --name <name>\", \"Process name\")\n .requiredOption(\"-d, --definition <json>\", \"Process definition JSON\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const definition = parseDefinition(options.definition);\n const proc = await client.processes.add({ name: options.name, definition });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to add process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"start\")\n .description(\"Start a restarting process\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const proc = await client.processes.start({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to start process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"stop\")\n .description(\"Stop a restarting process\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const proc = await client.processes.stop({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to stop process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"restart\")\n .description(\"Restart a restarting process\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-f, --force\", \"Force restart\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const proc = await client.processes.restart({\n target: parseTarget(target),\n force: options.force,\n });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Restarts\"] });\n table.push([proc.name, proc.state, proc.restarts]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to restart process:\", error);\n process.exit(1);\n }\n });\n\nprocesses\n .command(\"remove\")\n .description(\"Remove a restarting process\")\n .argument(\"<target>\", \"Process name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n await client.processes.remove({ target: parseTarget(target) });\n console.log(\"Process removed\");\n } catch (error) {\n console.error(\"Failed to remove process:\", error);\n process.exit(1);\n }\n });\n\nconst crons = program.command(\"crons\").description(\"Manage cron processes\");\n\ncrons\n .command(\"list\")\n .description(\"List cron processes\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const crons = await client.crons.list();\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n for (const cron of crons) {\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n }\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to list crons:\", error);\n process.exit(1);\n }\n });\n\ncrons\n .command(\"get\")\n .description(\"Get a cron process by name or index\")\n .argument(\"<target>\", \"Cron name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const cron = await client.crons.get({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to get cron:\", error);\n process.exit(1);\n }\n });\n\ncrons\n .command(\"trigger\")\n .description(\"Trigger a cron process\")\n .argument(\"<target>\", \"Cron name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const cron = await client.crons.trigger({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to trigger cron:\", error);\n process.exit(1);\n }\n });\n\ncrons\n .command(\"start\")\n .description(\"Start a cron process\")\n .argument(\"<target>\", \"Cron name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const cron = await client.crons.start({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to start cron:\", error);\n process.exit(1);\n }\n });\n\ncrons\n .command(\"stop\")\n .description(\"Stop a cron process\")\n .argument(\"<target>\", \"Cron name or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const cron = await client.crons.stop({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Name\", \"State\", \"Runs\", \"Fails\", \"Next Run\"] });\n table.push([cron.name, cron.state, cron.runCount, cron.failCount, cron.nextRun ?? \"-\"]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to stop cron:\", error);\n process.exit(1);\n }\n });\n\nconst tasks = program.command(\"tasks\").description(\"Manage tasks\");\n\ntasks\n .command(\"list\")\n .description(\"List tasks\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const tasks = await client.tasks.list();\n\n const table = new Table({ head: [\"Id\", \"State\", \"Processes\"] });\n for (const task of tasks) {\n table.push([task.id, task.state, task.processNames.join(\", \")]);\n }\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to list tasks:\", error);\n process.exit(1);\n }\n });\n\ntasks\n .command(\"get\")\n .description(\"Get a task by id or index\")\n .argument(\"<target>\", \"Task id or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const task = await client.tasks.get({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Id\", \"State\", \"Processes\"] });\n table.push([task.id, task.state, task.processNames.join(\", \")]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to get task:\", error);\n process.exit(1);\n }\n });\n\ntasks\n .command(\"add\")\n .description(\"Add a task\")\n .requiredOption(\"-n, --name <name>\", \"Task name\")\n .requiredOption(\"-d, --definition <json>\", \"Process definition JSON\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (options) => {\n try {\n const client = createClient(options.url);\n const definition = parseDefinition(options.definition);\n const task = await client.tasks.add({ name: options.name, definition });\n\n const table = new Table({ head: [\"Id\", \"State\", \"Processes\"] });\n table.push([task.id, task.state, task.processNames.join(\", \")]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to add task:\", error);\n process.exit(1);\n }\n });\n\ntasks\n .command(\"remove\")\n .description(\"Remove a task by id or index\")\n .argument(\"<target>\", \"Task id or index\")\n .option(\"-u, --url <url>\", \"RPC server URL\", \"http://localhost:3000/rpc\")\n .action(async (target, options) => {\n try {\n const client = createClient(options.url);\n const task = await client.tasks.remove({ target: parseTarget(target) });\n\n const table = new Table({ head: [\"Id\", \"State\", \"Processes\"] });\n table.push([task.id, task.state, task.processNames.join(\", \")]);\n console.log(table.toString());\n } catch (error) {\n console.error(\"Failed to remove task:\", error);\n process.exit(1);\n }\n });\n\nconst TargetSchema = v.union([v.string(), v.number()]);\ntype ProcessDefinition = v.InferOutput<typeof ProcessDefinitionSchema>;\n\nfunction parseTarget(value: string): string | number {\n const asNumber = Number(value);\n return v.parse(TargetSchema, Number.isNaN(asNumber) ? value : asNumber);\n}\n\nfunction parseDefinition(raw: string): ProcessDefinition {\n try {\n const parsed = JSON.parse(raw);\n return v.parse(ProcessDefinitionSchema, parsed);\n } catch (error) {\n console.error(\"Invalid --definition JSON. Expected a ProcessDefinition.\");\n throw error;\n }\n}\n\nprogram.parse();\n"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as ProcessDefinitionSchema, a as TaskStateSchema, c as CronProcessStateSchema, d as CrashLoopConfigSchema, f as RestartPolicySchema, g as LazyProcess, h as RestartingProcessStateSchema, i as TaskList, l as RetryConfigSchema, m as RestartingProcessOptionsSchema, n as EnvManager, o as CronProcess, p as RestartingProcess, r as NamedProcessDefinitionSchema, s as CronProcessOptionsSchema, t as logger, u as BackoffStrategySchema, v as ProcessStateSchema, y as treeKill } from "./logger-
|
|
1
|
+
import { _ as ProcessDefinitionSchema, a as TaskStateSchema, c as CronProcessStateSchema, d as CrashLoopConfigSchema, f as RestartPolicySchema, g as LazyProcess, h as RestartingProcessStateSchema, i as TaskList, l as RetryConfigSchema, m as RestartingProcessOptionsSchema, n as EnvManager, o as CronProcess, p as RestartingProcess, r as NamedProcessDefinitionSchema, s as CronProcessOptionsSchema, t as logger, u as BackoffStrategySchema, v as ProcessStateSchema, y as treeKill } from "./logger-BF3KrCIK.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/index.ts
|
|
4
4
|
function defineConfig(config) {
|
|
@@ -18,17 +18,16 @@ import { format } from "node:util";
|
|
|
18
18
|
*/
|
|
19
19
|
async function treeKill(pid, signal = "SIGTERM") {
|
|
20
20
|
if (Number.isNaN(pid)) throw new Error("pid must be a number");
|
|
21
|
-
const platform = process.platform;
|
|
22
21
|
const tree = { [pid]: [] };
|
|
23
|
-
await buildProcessTree(pid, tree, new Set([pid])
|
|
22
|
+
await buildProcessTree(pid, tree, new Set([pid]));
|
|
24
23
|
killAll(tree, signal);
|
|
25
24
|
}
|
|
26
25
|
/**
|
|
27
26
|
* Build a tree of all child processes recursively
|
|
28
27
|
*/
|
|
29
|
-
async function buildProcessTree(parentPid, tree, pidsToProcess
|
|
28
|
+
async function buildProcessTree(parentPid, tree, pidsToProcess) {
|
|
30
29
|
return new Promise((resolve) => {
|
|
31
|
-
const ps =
|
|
30
|
+
const ps = spawn("ps", [
|
|
32
31
|
"-o",
|
|
33
32
|
"pid",
|
|
34
33
|
"--no-headers",
|
|
@@ -56,7 +55,7 @@ async function buildProcessTree(parentPid, tree, pidsToProcess, platform) {
|
|
|
56
55
|
tree[parentPid].push(childPid);
|
|
57
56
|
tree[childPid] = [];
|
|
58
57
|
pidsToProcess.add(childPid);
|
|
59
|
-
promises.push(buildProcessTree(childPid, tree, pidsToProcess
|
|
58
|
+
promises.push(buildProcessTree(childPid, tree, pidsToProcess));
|
|
60
59
|
}
|
|
61
60
|
await Promise.all(promises);
|
|
62
61
|
if (pidsToProcess.size === 0) resolve();
|
|
@@ -1056,4 +1055,4 @@ const logger = (input) => {
|
|
|
1056
1055
|
|
|
1057
1056
|
//#endregion
|
|
1058
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 };
|
|
1059
|
-
//# sourceMappingURL=logger-
|
|
1058
|
+
//# sourceMappingURL=logger-BF3KrCIK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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":";;;;;;;;;;;;;;;;;;AAUA,eAAsB,SAAS,KAAa,SAAyB,WAA0B;AAC7F,KAAI,OAAO,MAAM,IAAI,CACnB,OAAM,IAAI,MAAM,uBAAuB;CAGzC,MAAM,OAAoB,GAAG,MAAM,EAAE,EAAE;AAIvC,OAAM,iBAAiB,KAAK,MAHN,IAAI,IAAY,CAAC,IAAI,CAAC,CAGI;AAGhD,SAAQ,MAAM,OAAO;;;;;AAMvB,eAAe,iBACb,WACA,MACA,eACe;AACf,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,KAAK,MAAM,MAAM;GAAC;GAAM;GAAO;GAAgB;GAAU,OAAO,UAAU;GAAC,CAAC;EAElF,IAAI,UAAU;AAEd,KAAG,OAAO,GAAG,SAAS,SAAiB;AACrC,cAAW,KAAK,SAAS,QAAQ;IACjC;AAEF,KAAG,GAAG,SAAS,OAAO,SAAS;AAC7B,iBAAc,OAAO,UAAU;AAE/B,OAAI,SAAS,GAAG;AAEd,QAAI,cAAc,SAAS,EACzB,UAAS;AAEX;;GAGF,MAAM,YAAY,QAAQ,MAAM,OAAO;AACvC,OAAI,CAAC,WAAW;AACd,QAAI,cAAc,SAAS,EACzB,UAAS;AAEX;;GAIF,MAAM,WAA4B,EAAE;AAEpC,QAAK,MAAM,UAAU,WAAW;IAC9B,MAAM,WAAW,SAAS,QAAQ,GAAG;AACrC,SAAK,WAAW,KAAK,SAAS;AAC9B,SAAK,YAAY,EAAE;AACnB,kBAAc,IAAI,SAAS;AAC3B,aAAS,KAAK,iBAAiB,UAAU,MAAM,cAAc,CAAC;;AAGhE,SAAM,QAAQ,IAAI,SAAS;AAE3B,OAAI,cAAc,SAAS,EACzB,UAAS;IAEX;AAEF,KAAG,GAAG,eAAe;AACnB,iBAAc,OAAO,UAAU;AAC/B,OAAI,cAAc,SAAS,EACzB,UAAS;IAEX;GACF;;;;;;AAOJ,SAAS,QAAQ,MAAmB,QAA8B;CAChE,MAAM,yBAAS,IAAI,KAAa;CAGhC,MAAM,UAAU,OAAO,KAAK,KAAK,CAAC,IAAI,OAAO;AAG7C,MAAK,MAAM,OAAO,QAEhB,MAAK,MAAM,YAAY,KAAK,KAC1B,KAAI,CAAC,OAAO,IAAI,SAAS,EAAE;AACzB,UAAQ,UAAU,OAAO;AACzB,SAAO,IAAI,SAAS;;AAM1B,MAAK,MAAM,OAAO,QAChB,KAAI,CAAC,OAAO,IAAI,IAAI,EAAE;AACpB,UAAQ,KAAK,OAAO;AACpB,SAAO,IAAI,IAAI;;;;;;AAQrB,SAAS,QAAQ,KAAa,QAA8B;AAC1D,KAAI;AACF,UAAQ,KAAK,KAAK,OAAO;UAClB,KAAK;AAEZ,MAAK,IAA8B,SAAS,QAC1C,OAAM;;;;;;ACxHZ,MAAa,0BAA0B,EAAE,OAAO;CAC9C,SAAS,EAAE,QAAQ;CACnB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;CACrC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC3B,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC;CAClD,CAAC;AAIF,MAAa,qBAAqB,EAAE,SAAS;CAC3C;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;AAQF,eAAe,gBACb,OACA,QACkB;CAClB,MAAM,MAAM,MAAM;AAClB,KAAI,QAAQ,OACV,QAAO;AAGT,KAAI;AACF,QAAM,SAAS,KAAK,OAAO;AAC3B,SAAO;SACD;AAEN,MAAI;AACF,UAAO,MAAM,KAAK,OAAO;UACnB;AACN,UAAO;;;;AAKb,IAAa,cAAb,MAAyB;CACvB,AAAS;CACT,AAAQ;CACR,AAAQ;CACR,AAAQ,eAAoC;CAC5C,AAAQ,SAAuB;CAC/B,AAAQ,cAAc,QAAQ,eAAqB;CACnD,AAAO,WAA0B;CAEjC,YAAY,MAAc,YAA+B,QAAgB;AACvE,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,SAAS;;CAGhB,IAAI,QAAsB;AACxB,SAAO,KAAK;;CAGd,QAAc;AACZ,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,WAC/C,OAAM,IAAI,MAAM,YAAY,KAAK,KAAK,eAAe,KAAK,SAAS;AAGrE,MAAI,KAAK,WAAW,WAClB,OAAM,IAAI,MAAM,YAAY,KAAK,KAAK,yBAAyB;AAGjE,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,qBAAqB,KAAK,WAAW,UAAU;AAEhE,MAAI;GACF,MAAM,MAAM,KAAK,WAAW,MAAM;IAAE,GAAG,QAAQ;IAAK,GAAG,KAAK,WAAW;IAAK,GAAG,QAAQ;AAEvF,QAAK,eAAe,MAAM,KAAK,WAAW,SAAS,KAAK,WAAW,QAAQ,EAAE,EAAE;IAC7E,KAAK,KAAK,WAAW;IACrB;IACA,OAAO;KAAC;KAAU;KAAQ;KAAO;IAClC,CAAC;AAEF,QAAK,SAAS;GAGd,MAAM,WAAW,IAAI,aAAa;GAClC,IAAI,cAAc;AAElB,OAAI,KAAK,aAAa,QAAQ;AAC5B;AACA,SAAK,aAAa,OAAO,KAAK,UAAU,EAAE,KAAK,OAAO,CAAC;AACvD,SAAK,aAAa,OAAO,GAAG,aAAa;AACvC,SAAI,EAAE,gBAAgB,EAAG,UAAS,KAAK;MACvC;;AAGJ,OAAI,KAAK,aAAa,QAAQ;AAC5B;AACA,SAAK,aAAa,OAAO,KAAK,UAAU,EAAE,KAAK,OAAO,CAAC;AACvD,SAAK,aAAa,OAAO,GAAG,aAAa;AACvC,SAAI,EAAE,gBAAgB,EAAG,UAAS,KAAK;MACvC;;AAKJ,GADW,SAAS,gBAAgB,EAAE,OAAO,UAAU,CAAC,CACrD,GAAG,SAAS,SAAS;AACtB,SAAK,OAAO,KAAK,KAAK;KACtB;AAGF,QAAK,aAAa,GAAG,SAAS,MAAM,WAAW;AAC7C,SAAK,WAAW;AAEhB,QAAI,KAAK,WAAW,UAClB,KAAI,SAAS,GAAG;AACd,UAAK,SAAS;AACd,UAAK,OAAO,KAAK,4BAA4B,OAAO;eAC3C,QAAQ;AACjB,UAAK,SAAS;AACd,UAAK,OAAO,KAAK,8BAA8B,SAAS;WACnD;AACL,UAAK,SAAS;AACd,UAAK,OAAO,MAAM,4BAA4B,OAAO;;AAIzD,SAAK,YAAY,SAAS;KAC1B;AAGF,QAAK,aAAa,GAAG,UAAU,QAAQ;AACrC,QAAI,KAAK,WAAW,cAAc,KAAK,WAAW,WAAW;AAC3D,UAAK,SAAS;AACd,UAAK,OAAO,MAAM,kBAAkB,IAAI;;AAE1C,SAAK,YAAY,SAAS;KAC1B;WACK,KAAK;AACZ,QAAK,SAAS;AACd,QAAK,OAAO,MAAM,4BAA4B,IAAI;AAClD,SAAM;;;CAIV,MAAM,KAAK,SAAiC;AAC1C,MAAI,KAAK,WAAW,UAAU,KAAK,WAAW,aAAa,KAAK,WAAW,QACzE;AAGF,MAAI,KAAK,WAAW,YAAY;AAE9B,SAAM,KAAK,YAAY;AACvB;;AAGF,MAAI,CAAC,KAAK,cAAc;AACtB,QAAK,SAAS;AACd;;AAGF,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,gCAAgC;AAGjD,QAAM,gBAAgB,KAAK,cAAc,UAAU;EAEnD,MAAM,YAAY,WAAW;EAG7B,MAAM,iBAAiB,IAAI,SAAoB,YAC7C,iBAAiB,QAAQ,UAAU,EAAE,UAAU,CAChD;AAOD,MALe,MAAM,QAAQ,KAAK,CAChC,KAAK,YAAY,QAAQ,WAAW,SAAkB,EACtD,eACD,CAAC,KAEa,aAAa,KAAK,cAAc;AAC7C,QAAK,OAAO,KAAK,+BAA+B,UAAU,qBAAqB;AAC/E,SAAM,gBAAgB,KAAK,cAAc,UAAU;GAGnD,MAAM,cAAc,IAAI,SAAe,YAAY,WAAW,SAAS,IAAK,CAAC;AAC7E,SAAM,QAAQ,KAAK,CAAC,KAAK,YAAY,SAAS,YAAY,CAAC;;AAG7D,OAAK,SAAS;AACd,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,kBAAkB;;CAGrC,MAAM,QAAuB;AAC3B,MAAI,KAAK,cAAc;AAErB,SAAM,gBAAgB,KAAK,cAAc,UAAU;AACnD,SAAM,KAAK,YAAY;AACvB,QAAK,SAAS;;AAGhB,OAAK,SAAS;AAEd,OAAK,cAAc,QAAQ,eAAqB;AAChD,OAAK,OAAO,KAAK,wBAAwB;;CAG3C,iBAAiB,YAAqC;AACpD,OAAK,aAAa;;CAGpB,MAAM,cAAqC;AACzC,MAAI,CAAC,KAAK,aACR,QAAO,KAAK;AAGd,QAAM,KAAK,YAAY;AACvB,SAAO,KAAK;;CAGd,AAAQ,UAAgB;AACtB,MAAI,KAAK,cAAc;AAErB,QAAK,aAAa,QAAQ,oBAAoB;AAC9C,QAAK,aAAa,QAAQ,oBAAoB;AAC9C,QAAK,aAAa,oBAAoB;AACtC,QAAK,eAAe;;AAGtB,OAAK,WAAW;;;;;;AC3OpB,MAAa,sBAAsB,EAAE,SAAS;CAC5C;CACA;CACA;CACA;CACA;CACD,CAAC;AAKF,MAAa,wBAAwB,EAAE,MAAM,CAC3C,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,QAAQ;CACxB,SAAS,EAAE,QAAQ;CACpB,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,cAAc;CAC9B,gBAAgB,EAAE,QAAQ;CAC1B,YAAY,EAAE,QAAQ;CACtB,YAAY,EAAE,SAAS,EAAE,QAAQ,CAAC;CACnC,CAAC,CACH,CAAC;AAKF,MAAa,wBAAwB,EAAE,OAAO;CAC5C,aAAa,EAAE,QAAQ;CACvB,UAAU,EAAE,QAAQ;CACpB,WAAW,EAAE,QAAQ;CACtB,CAAC;AAKF,MAAa,iCAAiC,EAAE,OAAO;CACrD,eAAe;CACf,SAAS,EAAE,SAAS,sBAAsB;CAC1C,WAAW,EAAE,SAAS,sBAAsB;CAC5C,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC;CACnC,kBAAkB,EAAE,SAAS,EAAE,QAAQ,CAAC;CACzC,CAAC;AAKF,MAAa,+BAA+B,EAAE,SAAS;CACrD;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAIF,MAAM,kBAAmC;CAAE,MAAM;CAAS,SAAS;CAAM;AACzE,MAAM,qBAAsC;CAAE,aAAa;CAAG,UAAU;CAAO,WAAW;CAAO;AAEjG,IAAa,oBAAb,MAA+B;CAC7B,AAAS;CACT,AAAQ;CACR,AAAQ;CACR,AAAQ;CAGR,AAAQ;CAGR,AAAQ,SAAiC;CACzC,AAAQ,gBAAwB;CAChC,AAAQ,oBAA8B,EAAE;CACxC,AAAQ,sBAA8B;CACtC,AAAQ,gBAA+B;CACvC,AAAQ,gBAAyB;CACjC,AAAQ,sBAA4D;CAEpE,YACE,MACA,YACA,SACA,QACA;AACA,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,SAAS;AACd,OAAK,UAAU;GACb,eAAe,QAAQ;GACvB,SAAS,QAAQ,WAAW;GAC5B,WAAW,QAAQ,aAAa;GAChC,aAAa,QAAQ,eAAe;GACpC,kBAAkB,QAAQ;GAC3B;AACD,OAAK,cAAc,IAAI,YAAY,MAAM,YAAY,OAAO;;CAG9D,IAAI,QAAgC;AAClC,SAAO,KAAK;;CAGd,IAAI,WAAmB;AACrB,SAAO,KAAK;;CAGd,QAAc;AACZ,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,aAC/C,OAAM,IAAI,MAAM,YAAY,KAAK,KAAK,eAAe,KAAK,SAAS;AAGrE,MAAI,KAAK,WAAW,WAClB,OAAM,IAAI,MAAM,YAAY,KAAK,KAAK,yBAAyB;AAIjE,MACE,KAAK,WAAW,aAChB,KAAK,WAAW,UAChB,KAAK,WAAW,uBAEhB,MAAK,eAAe;AAGtB,OAAK,gBAAgB;AACrB,OAAK,cAAc;;CAGrB,MAAM,KAAK,SAAiC;AAC1C,OAAK,gBAAgB;AAGrB,MAAI,KAAK,qBAAqB;AAC5B,gBAAa,KAAK,oBAAoB;AACtC,QAAK,sBAAsB;;AAG7B,MACE,KAAK,WAAW,UAChB,KAAK,WAAW,aAChB,KAAK,WAAW,wBAChB;AACA,QAAK,SAAS;AACd;;AAGF,OAAK,SAAS;AACd,QAAM,KAAK,YAAY,KAAK,QAAQ;AACpC,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,4BAA4B;;CAG/C,MAAM,QAAQ,QAAiB,OAAsB;AAEnD,MACE,KAAK,WAAW,aAChB,KAAK,WAAW,UAChB,KAAK,WAAW,wBAChB;AACA,QAAK,eAAe;AACpB,QAAK,gBAAgB;AACrB,QAAK,cAAc;AACnB;;AAIF,QAAM,KAAK,MAAM;AAEjB,OAAK,gBAAgB;AAErB,MAAI,MAEF,MAAK,cAAc;OACd;GAEL,MAAM,QAAQ,KAAK,gBAAgB;AACnC,OAAI,QAAQ,GAAG;AACb,SAAK,SAAS;AACd,SAAK,OAAO,KAAK,iBAAiB,MAAM,IAAI;AAC5C,UAAM,KAAK,MAAM,MAAM;AACvB,QAAI,KAAK,cAAe;;AAE1B,QAAK,cAAc;;;;;;CAOvB,MAAM,OACJ,eACA,qBAA8B,MACf;AACf,OAAK,OAAO,KAAK,wCAAwC;AACzD,OAAK,aAAa;AAClB,OAAK,YAAY,iBAAiB,cAAc;AAEhD,MAAI,mBAEF,OAAM,KAAK,QAAQ,KAAK;;;;;CAO5B,cAAc,YAAqD;AACjE,OAAK,OAAO,KAAK,2BAA2B;AAC5C,OAAK,UAAU;GACb,GAAG,KAAK;GACR,eAAe,WAAW,iBAAiB,KAAK,QAAQ;GACxD,SAAS,WAAW,WAAW,KAAK,QAAQ;GAC5C,WAAW,WAAW,aAAa,KAAK,QAAQ;GAChD,aAAa,WAAW,eAAe,KAAK,QAAQ;GACpD,kBAAkB,WAAW,oBAAoB,KAAK,QAAQ;GAC/D;;CAGH,AAAQ,gBAAsB;AAC5B,OAAK,gBAAgB;AACrB,OAAK,sBAAsB;AAC3B,OAAK,oBAAoB,EAAE;;CAG7B,AAAQ,eAAqB;AAC3B,OAAK,gBAAgB,KAAK,KAAK;AAC/B,OAAK,SAAS;AAEd,OAAK,YACF,OAAO,CACP,WAAW;AACV,OAAI,KAAK,cAAe;AACxB,QAAK,YAAY,OAAO;AACxB,UAAO,KAAK,YAAY,aAAa;IACrC,CACD,MAAM,cAAc;AACnB,OAAI,CAAC,UAAW;AAChB,OAAI,KAAK,iBAAiB,cAAc,SAAS;AAC/C,SAAK,SAAS;AACd;;AAEF,OAAI,cAAc,aAAa,cAAc,QAC3C,MAAK,kBAAkB,UAAU;IAEnC,CACD,OAAO,QAAQ;AACd,OAAI,KAAK,cAAe;AACxB,QAAK,SAAS;AACd,QAAK,OAAO,MAAM,4BAA4B,IAAI;IAClD;;CAGN,AAAQ,kBAAkB,WAA+B;AACvD,MAAI,KAAK,eAAe;AACtB,QAAK,SAAS;AACd;;EAIF,MAAM,cADS,KAAK,gBAAgB,KAAK,KAAK,GAAG,KAAK,gBAAgB,MACzC,KAAK,QAAQ;EAC1C,MAAM,kBAAkB,cAAc;AAGtC,MAAI,WACF,MAAK,sBAAsB;MAE3B,MAAK;AAIP,MAAI,CAAC,KAAK,cAAc,gBAAgB,EAAE;AACxC,QAAK,SAAS;AACd,QAAK,OAAO,KACV,2BAA2B,KAAK,QAAQ,cAAc,0BACvD;AACD;;AAIF,MACE,KAAK,QAAQ,qBAAqB,UAClC,KAAK,iBAAiB,KAAK,QAAQ,kBACnC;AACA,QAAK,SAAS;AACd,QAAK,OAAO,KAAK,uBAAuB,KAAK,QAAQ,iBAAiB,WAAW;AACjF;;EAIF,MAAM,MAAM,KAAK,KAAK;AACtB,OAAK,kBAAkB,KAAK,IAAI;AAGhC,MAAI,KAAK,eAAe,EAAE;AACxB,QAAK,SAAS;AACd,QAAK,OAAO,KACV,wBAAwB,KAAK,QAAQ,UAAU,YAAY,eAAe,KAAK,QAAQ,UAAU,SAAS,uBAAuB,KAAK,QAAQ,UAAU,UAAU,IACnK;AACD,QAAK,2BAA2B;AAChC;;AAIF,OAAK;AACL,OAAK,iBAAiB;;CAGxB,AAAQ,cAAc,iBAAmC;AACvD,UAAQ,KAAK,QAAQ,eAArB;GACE,KAAK,SACH,QAAO;GACT,KAAK,QACH,QAAO;GACT,KAAK,aACH,QAAO;GACT,KAAK,aACH,QAAO,CAAC;GACV,KAAK,iBACH,QAAO,CAAC,KAAK;GACf,QACE,QAAO;;;CAIb,AAAQ,gBAAyB;EAC/B,MAAM,EAAE,aAAa,aAAa,KAAK,QAAQ;EAE/C,MAAM,SADM,KAAK,KAAK,GACD;AAGrB,OAAK,oBAAoB,KAAK,kBAAkB,QAAQ,OAAO,KAAK,OAAO;AAE3E,SAAO,KAAK,kBAAkB,UAAU;;CAG1C,AAAQ,iBAAyB;EAC/B,MAAM,EAAE,YAAY,KAAK;AAEzB,MAAI,QAAQ,SAAS,QACnB,QAAO,QAAQ;EAIjB,MAAM,aAAa,QAAQ,cAAc;EACzC,MAAM,QAAQ,QAAQ,iBAAiB,KAAK,IAAI,YAAY,KAAK,oBAAoB;AACrF,SAAO,KAAK,IAAI,OAAO,QAAQ,WAAW;;CAG5C,AAAQ,kBAAwB;AAC9B,OAAK,SAAS;EACd,MAAM,QAAQ,KAAK,gBAAgB;AAEnC,OAAK,OAAO,KAAK,iBAAiB,MAAM,eAAe,KAAK,cAAc,GAAG;AAE7E,OAAK,sBAAsB,iBAAiB;AAC1C,QAAK,sBAAsB;AAC3B,OAAI,KAAK,eAAe;AACtB,SAAK,SAAS;AACd;;AAEF,QAAK,cAAc;KAClB,MAAM;;CAGX,AAAQ,4BAAkC;EACxC,MAAM,EAAE,cAAc,KAAK,QAAQ;AAEnC,OAAK,sBAAsB,iBAAiB;AAC1C,QAAK,sBAAsB;AAC3B,OAAI,KAAK,eAAe;AACtB,SAAK,SAAS;AACd;;AAIF,QAAK,oBAAoB,EAAE;AAC3B,QAAK;AACL,QAAK,OAAO,KAAK,qDAAqD,KAAK,cAAc,GAAG;AAC5F,QAAK,cAAc;KAClB,UAAU;;CAGf,AAAQ,MAAM,IAA2B;AACvC,SAAO,IAAI,SAAS,YAAY;AAC9B,QAAK,sBAAsB,iBAAiB;AAC1C,SAAK,sBAAsB;AAC3B,aAAS;MACR,GAAG;IACN;;;;;;ACpYN,MAAa,oBAAoB,EAAE,OAAO;CACxC,YAAY,EAAE,QAAQ;CACtB,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC;CAChC,CAAC;AAKF,MAAa,2BAA2B,EAAE,OAAO;CAC/C,UAAU,EAAE,QAAQ;CACpB,OAAO,EAAE,SAAS,kBAAkB;CACpC,YAAY,EAAE,SAAS,EAAE,SAAS,CAAC;CACpC,CAAC;AAKF,MAAa,yBAAyB,EAAE,SAAS;CAC/C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAIF,MAAM,sBAAsB;AAE5B,IAAa,cAAb,MAAyB;CACvB,AAAS;CACT,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ,UAAuB;CAG/B,AAAQ,SAA2B;CACnC,AAAQ,YAAoB;CAC5B,AAAQ,aAAqB;CAC7B,AAAQ,sBAA8B;CACtC,AAAQ,YAAqB;CAC7B,AAAQ,gBAAyB;CACjC,AAAQ,eAAqD;CAE7D,YACE,MACA,YACA,SACA,QACA;AACA,OAAK,OAAO;AACZ,OAAK,UAAU;AACf,OAAK,SAAS;AACd,OAAK,cAAc,IAAI,YAAY,MAAM,YAAY,OAAO;;CAG9D,IAAI,QAA0B;AAC5B,SAAO,KAAK;;CAGd,IAAI,WAAmB;AACrB,SAAO,KAAK;;CAGd,IAAI,YAAoB;AACtB,SAAO,KAAK;;CAGd,IAAI,UAAuB;AACzB,MAAI,CAAC,KAAK,QAAS,QAAO;AAE1B,SADa,KAAK,QAAQ,SAAS,IACpB;;CAGjB,QAAc;AACZ,MAAI,KAAK,WAAW,eAAe,KAAK,WAAW,aAAa,KAAK,WAAW,SAC9E,OAAM,IAAI,MAAM,gBAAgB,KAAK,KAAK,eAAe,KAAK,SAAS;AAGzE,MAAI,KAAK,WAAW,WAClB,OAAM,IAAI,MAAM,gBAAgB,KAAK,KAAK,yBAAyB;AAGrE,OAAK,gBAAgB;AACrB,OAAK,OAAO,KAAK,2BAA2B,KAAK,QAAQ,WAAW;AAGpE,OAAK,UAAU,IAAI,KAAK,KAAK,QAAQ,UAAU,EAAE,UAAU,OAAO,QAAQ;AACxE,QAAK,YAAY;IACjB;AAEF,OAAK,SAAS;AAGd,MAAI,KAAK,QAAQ,WACf,MAAK,YAAY;;CAIrB,MAAM,KAAK,SAAiC;AAC1C,OAAK,gBAAgB;AAGrB,MAAI,KAAK,SAAS;AAChB,QAAK,QAAQ,MAAM;AACnB,QAAK,UAAU;;AAIjB,MAAI,KAAK,cAAc;AACrB,gBAAa,KAAK,aAAa;AAC/B,QAAK,eAAe;;AAGtB,MAAI,KAAK,WAAW,UAAU,KAAK,WAAW,WAAW;AACvD,QAAK,SAAS;AACd;;AAIF,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,cAAc,KAAK,WAAW,UAAU;AACvF,QAAK,SAAS;AACd,SAAM,KAAK,YAAY,KAAK,QAAQ;;AAGtC,OAAK,SAAS;AACd,OAAK,YAAY;AACjB,OAAK,OAAO,KAAK,sBAAsB;;CAGzC,MAAM,UAAyB;AAC7B,MAAI,KAAK,cACP,OAAM,IAAI,MAAM,gBAAgB,KAAK,KAAK,cAAc;AAI1D,MAAI,KAAK,WAAW,SAClB;AAIF,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,YAAY;AAC3D,QAAK,YAAY;AACjB,QAAK,SAAS;AACd,QAAK,OAAO,KAAK,yCAAyC;AAC1D;;AAGF,QAAM,KAAK,YAAY;;CAGzB,AAAQ,aAAmB;AACzB,MAAI,KAAK,cAAe;AAGxB,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,cAAc,KAAK,WAAW,UAAU;AACvF,QAAK,YAAY;AACjB,OAAI,KAAK,WAAW,SAClB,MAAK,SAAS;AAEhB,QAAK,OAAO,KAAK,oDAAoD;AACrE;;AAGF,OAAK,YAAY;;CAGnB,MAAc,aAA4B;AACxC,MAAI,KAAK,cAAe;AAExB,OAAK,SAAS;AACd,OAAK,sBAAsB;AAC3B,OAAK,OAAO,KAAK,gBAAgB;AAEjC,QAAM,KAAK,iBAAiB;;CAG9B,MAAc,kBAAiC;AAC7C,MAAI,KAAK,cAAe;AAGxB,QAAM,KAAK,YAAY,OAAO;AAC9B,OAAK,YAAY,OAAO;EAExB,MAAM,YAAY,MAAM,KAAK,YAAY,aAAa;AACtD,MAAI,KAAK,iBAAiB,cAAc,SAAS;AAC/C,QAAK,SAAS;AACd;;AAEF,OAAK,kBAAkB,cAAc,QAAQ;;CAG/C,AAAQ,kBAAkB,QAAuB;AAC/C,MAAI,KAAK,eAAe;AACtB,QAAK,SAAS;AACd;;AAGF,MAAI,QAAQ;GACV,MAAM,aAAa,KAAK,QAAQ,OAAO,cAAc;AAErD,OAAI,KAAK,sBAAsB,YAAY;AAEzC,SAAK;AACL,SAAK,SAAS;IACd,MAAM,UAAU,KAAK,QAAQ,OAAO,WAAW;AAE/C,SAAK,OAAO,KACV,2BAA2B,QAAQ,cAAc,KAAK,oBAAoB,GAAG,WAAW,GACzF;AAED,SAAK,eAAe,iBAAiB;AACnC,UAAK,eAAe;AACpB,SAAI,KAAK,eAAe;AACtB,WAAK,SAAS;AACd;;AAEF,UAAK,iBAAiB;OACrB,QAAQ;AACX;;AAIF,QAAK;AACL,QAAK,OAAO,MAAM,oBAAoB,KAAK,oBAAoB,UAAU;SACpE;AACL,QAAK;AACL,QAAK,OAAO,KAAK,6BAA6B;;AAIhD,MAAI,KAAK,WAAW;AAClB,QAAK,YAAY;AACjB,QAAK,OAAO,KAAK,sBAAsB;AACvC,QAAK,YAAY;AACjB;;AAIF,MAAI,KAAK,QACP,MAAK,SAAS;MAEd,MAAK,SAAS;;;;;;ACtPpB,MAAa,kBAAkB,EAAE,SAAS;CAAC;CAAW;CAAW;CAAa;CAAU;CAAU,CAAC;AAKnG,MAAa,+BAA+B,EAAE,OAAO;CACnD,MAAM,EAAE,QAAQ;CAChB,SAAS;CACV,CAAC;AAcF,IAAa,WAAb,MAAsB;CACpB,AAAS;CACT,AAAQ,SAAsB,EAAE;CAChC,AAAQ,SAAwB;CAChC,AAAQ;CACR,AAAQ;CACR,AAAQ,gBAAwB;CAChC,AAAQ,mBAAkC,EAAE;CAC5C,AAAQ,gBAAyB;CACjC,AAAQ,iBAAuC;CAE/C,YACE,MACA,QACA,cACA,iBACA;AACA,OAAK,OAAO;AACZ,OAAK,SAAS;AACd,OAAK,kBAAkB;AAGvB,MAAI,aACF,MAAK,MAAM,QAAQ,aACjB,MAAK,QAAQ,KAAK;;CAKxB,IAAI,QAAuB;AACzB,SAAO,KAAK;;CAGd,IAAI,QAAkC;AACpC,SAAO,KAAK;;CAGd,mBAAmB,QAAoC;EACrD,MAAM,QACJ,OAAO,WAAW,WAAW,SAAS,KAAK,OAAO,WAAW,MAAM,EAAE,OAAO,OAAO;AACrF,MAAI,QAAQ,KAAK,SAAS,KAAK,OAAO,OACpC,OAAM,IAAI,MAAM,mBAAmB,SAAS;EAG9C,MAAM,OAAO,KAAK,OAAO;AACzB,MAAI,KAAK,UAAU,UACjB,OAAM,IAAI,MAAM,+BAA+B,KAAK,KAAK;AAG3D,OAAK,OAAO,OAAO,OAAO,EAAE;AAC5B,OAAK,OAAO,KAAK,SAAS,KAAK,GAAG,WAAW;AAC7C,SAAO;;;;;;CAOT,QAAQ,MAAiE;EACvE,MAAM,KAAK,QAAQ,EAAE,KAAK;EAC1B,MAAM,YAAY,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC,KAAK;EAErD,MAAM,QAAmB;GACvB;GACA;GACA,OAAO;GACR;AAED,OAAK,OAAO,KAAK,MAAM;AACvB,OAAK,OAAO,KAAK,SAAS,GAAG,eAAe,UAAU,OAAO,cAAc;AAE3E,SAAO;;;;;CAMT,QAAc;AACZ,MAAI,KAAK,WAAW,UAClB,OAAM,IAAI,MAAM,aAAa,KAAK,KAAK,sBAAsB;AAG/D,OAAK,gBAAgB;AACrB,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,mBAAmB;AAGpC,OAAK,iBAAiB,KAAK,SAAS;;;;;CAMtC,MAAM,gBAA+B;AACnC,MAAI,KAAK,WAAW,UAAU,KAAK,WAAW,UAC5C;AAIF,MAAI,KAAK,eACP,OAAM,KAAK;;;;;CAOf,MAAM,KAAK,SAAiC;AAC1C,MAAI,KAAK,WAAW,UAAU,KAAK,WAAW,WAAW;AACvD,QAAK,SAAS;AACd;;AAGF,OAAK,gBAAgB;AACrB,OAAK,OAAO,KAAK,uBAAuB;EAGxC,MAAM,eAAe,KAAK,iBAAiB,KAAK,MAAM,EAAE,KAAK,QAAQ,CAAC;AACtE,QAAM,QAAQ,IAAI,aAAa;AAC/B,OAAK,mBAAmB,EAAE;AAG1B,OAAK,MAAM,QAAQ,KAAK,OACtB,KAAI,KAAK,UAAU,UACjB,MAAK,QAAQ;AAKjB,MAAI,KAAK,gBAAgB;AACvB,SAAM,KAAK;AACX,QAAK,iBAAiB;;AAGxB,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,mBAAmB;;CAGtC,MAAc,UAAyB;AACrC,SAAO,KAAK,WAAW,aAAa,CAAC,KAAK,eAAe;GAEvD,MAAM,WAAW,KAAK,OAAO,MAAM,MAAM,EAAE,UAAU,UAAU;AAE/D,OAAI,CAAC,UAAU;AAEb,SAAK,SAAS;AACd,SAAK,OAAO,KAAK,wCAAwC;AACzD;;AAGF,SAAM,KAAK,YAAY,SAAS;;;CAIpC,MAAc,YAAY,MAAgC;AACxD,MAAI,KAAK,eAAe;AACtB,QAAK,QAAQ;AACb;;AAGF,OAAK,QAAQ;EACb,MAAM,YAAY,KAAK,UAAU,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK;AAC9D,OAAK,OAAO,KAAK,mBAAmB,KAAK,GAAG,MAAM,UAAU,GAAG;EAG/D,MAAM,gBAA+B,KAAK,UAAU,KAAK,MAAM;GAC7D,MAAM,UAAU,KAAK,kBAAkB,EAAE,KAAK;GAC9C,MAAM,cAAc,UAChB,KAAK,OAAO,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,GACtC,KAAK,OAAO,MAAM,EAAE,KAAK;AAC7B,UAAO,IAAI,YAAY,EAAE,MAAM,EAAE,SAAS,YAAY;IACtD;AAEF,OAAK,mBAAmB;AAExB,MAAI;AAEF,QAAK,MAAM,MAAM,cACf,IAAG,OAAO;GAOZ,MAAM,aAHU,MAAM,QAAQ,IAAI,cAAc,KAAK,OAAO,KAAK,eAAe,GAAG,CAAC,CAAC,EAG3D,MAAM,MAAM,MAAM,QAAQ;AAEpD,OAAI,KAAK,cACP,MAAK,QAAQ;YACJ,WAAW;AACpB,SAAK,QAAQ;AACb,SAAK,OAAO,KAAK,SAAS,KAAK,GAAG,UAAU;UACvC;AACL,SAAK,QAAQ;AACb,SAAK,OAAO,KAAK,SAAS,KAAK,GAAG,aAAa;;WAE1C,KAAK;AACZ,QAAK,QAAQ;AACb,QAAK,OAAO,MAAM,SAAS,KAAK,GAAG,WAAW,IAAI;YAC1C;AACR,QAAK,mBAAmB,EAAE;;;CAI9B,MAAc,eAAe,IAA+C;AAE1E,SADc,MAAM,GAAG,aAAa,KACnB,UAAU,UAAU;;;;;;AC7MzC,IAAa,aAAb,MAAwB;CACtB,AAAQ,sBAA2C,IAAI,KAAK;CAC5D,AAAQ;CACR,AAAQ;CACR,AAAQ,2BAAkD,IAAI,KAAK;CACnE,AAAQ,6BAAuC,IAAI,KAAK;CACxD,AAAQ,kCAA0C,IAAI,KAAK;CAC3D,AAAQ,uCAAmE,IAAI,KAAK;CAEpF,YAAY,SAA2B,EAAE,EAAE;AACzC,OAAK,MAAM,OAAO,OAAO,QAAQ,KAAK;AACtC,OAAK,eAAe,OAAO,SAAS;AAGpC,OAAK,qBAAqB;AAG1B,MAAI,OAAO,MACT,MAAK,MAAM,CAAC,KAAK,aAAa,OAAO,QAAQ,OAAO,MAAM,CACxD,MAAK,YAAY,KAAK,SAAS;;CAKrC,aAAa,KAAa,UAAwB;AAChD,OAAK,YAAY,KAAK,SAAS;;CAGjC,aAAa,KAAqC;AAChD,SAAO,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE;;;;;CAMhC,AAAQ,sBAA4B;EAElC,MAAM,aAAa,QAAQ,KAAK,KAAK,OAAO;AAC5C,MAAI,WAAW,WAAW,CACxB,MAAK,YAAY,UAAU,WAAW;AAIxC,MAAI;GAEF,MAAM,WAAW,SADD,KAAK,KAAK,KAAK,SAAS,CACN;AAElC,QAAK,MAAM,YAAY,UAAU;IAG/B,MAAM,QADW,SAAS,SAAS,CACZ,MAAM,gBAAgB;AAC7C,QAAI,OAAO;KACT,MAAM,SAAS,MAAM;AACrB,UAAK,YAAY,QAAQ,SAAS;;;WAG/B,KAAK;AACZ,WAAQ,KAAK,6BAA6B,IAAI;;;;;;CAOlD,AAAQ,YAAY,KAAa,UAAwB;EACvD,MAAM,eAAe,QAAQ,KAAK,KAAK,SAAS;AAEhD,MAAI,CAAC,WAAW,aAAa,CAC3B;AAGF,MAAI;GAEF,MAAM,SAAS,MADC,aAAa,cAAc,QAAQ,CACtB;AAC7B,QAAK,IAAI,IAAI,KAAK,OAAO;AAGzB,OAAI,CAAC,KAAK,WAAW,IAAI,aAAa,CACpC,MAAK,WAAW,IAAI,8BAAc,IAAI,KAAK,CAAC;AAE9C,QAAK,WAAW,IAAI,aAAa,CAAE,IAAI,IAAI;AAG3C,OAAI,KAAK,gBAAgB,CAAC,KAAK,SAAS,IAAI,aAAa,CACvD,MAAK,UAAU,aAAa;WAEvB,KAAK;AACZ,WAAQ,KAAK,4BAA4B,gBAAgB,IAAI;;;;;;CAOjE,AAAQ,UAAU,cAA4B;AAC5C,MAAI;GACF,MAAM,UAAU,MAAM,eAAe,cAAc;AACjD,QAAI,cAAc,SAChB,MAAK,iBAAiB,aAAa;KAErC;AAEF,QAAK,SAAS,IAAI,cAAc,QAAQ;WACjC,KAAK;AACZ,WAAQ,KAAK,6BAA6B,gBAAgB,IAAI;;;;;;CAOlE,AAAQ,iBAAiB,cAA4B;EAEnD,MAAM,gBAAgB,KAAK,qBAAqB,IAAI,aAAa;AACjE,MAAI,cACF,cAAa,cAAc;EAI7B,MAAM,QAAQ,iBAAiB;AAC7B,QAAK,WAAW,aAAa;AAC7B,QAAK,qBAAqB,OAAO,aAAa;KAC7C,IAAI;AAEP,OAAK,qBAAqB,IAAI,cAAc,MAAM;;;;;CAMpD,AAAQ,WAAW,cAA4B;EAC7C,MAAM,OAAO,KAAK,WAAW,IAAI,aAAa;AAC9C,MAAI,CAAC,KAAM;AAEX,WAAS,cAAc,QAAQ,CAC5B,MAAM,YAAY,MAAM,QAAQ,CAAC,CACjC,MAAM,WAAW;GAChB,MAAM,cAAwB,EAAE;AAChC,QAAK,MAAM,OAAO,MAAM;AACtB,SAAK,IAAI,IAAI,KAAK,OAAO;AACzB,gBAAY,KAAK,IAAI;;AAIvB,OAAI,YAAY,SAAS,EACvB,MAAK,MAAM,YAAY,KAAK,gBAC1B,UAAS,YAAY;IAGzB,CACD,OAAO,QAAQ;AACd,WAAQ,KAAK,8BAA8B,gBAAgB,IAAI;IAC/D;;;;;;CAON,SAAS,UAAyC;AAChD,OAAK,gBAAgB,IAAI,SAAS;AAClC,eAAa;AACX,QAAK,gBAAgB,OAAO,SAAS;;;;;;CAOzC,UAAgB;AAEd,OAAK,MAAM,SAAS,KAAK,qBAAqB,QAAQ,CACpD,cAAa,MAAM;AAErB,OAAK,qBAAqB,OAAO;AAGjC,OAAK,MAAM,WAAW,KAAK,SAAS,QAAQ,CAC1C,SAAQ,OAAO;AAEjB,OAAK,SAAS,OAAO;AAGrB,OAAK,gBAAgB,OAAO;;;;;;;CAQ9B,WAAW,YAA6C;EACtD,MAAM,YAAY,KAAK,IAAI,IAAI,SAAS,IAAI,EAAE;AAE9C,MAAI,CAAC,WACH,QAAO,EAAE,GAAG,WAAW;EAGzB,MAAM,aAAa,KAAK,IAAI,IAAI,WAAW,IAAI,EAAE;AACjD,SAAO;GAAE,GAAG;GAAW,GAAG;GAAY;;;;;CAMxC,YAAyD;AACvD,SAAO,KAAK;;;;;;ACvOhB,MAAM,SAAS;CACb,OAAO;CACP,MAAM;CACN,OAAO;CACP,OAAO;CACP,QAAQ;CACR,KAAK;CACL,MAAM;CACP;AAED,MAAM,cAAc;CAClB,OAAO,OAAO;CACd,MAAM,OAAO;CACb,MAAM,OAAO;CACb,OAAO,OAAO;CACf;AAED,MAAM,cAAc,SAClB,KAAK,eAAe,SAAS;CAC3B,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,wBAAwB;CACxB,WAAW;CACZ,CAAC,CAAC,OAAO,KAAK;AAEjB,MAAM,uBAAuB,OAAe,MAAc,SAAe;CACvE,MAAM,iBAAiB,MAAM,aAAa,CAAC,SAAS,EAAE;AAEtD,QAAO,IADW,WAAW,KAAK,CACb,IAAI,eAAe,IAAI,KAAK;;AAGnD,MAAM,yBAAyB,OAAe,MAAc,SAAe;CACzE,MAAM,iBAAiB,MAAM,aAAa,CAAC,SAAS,EAAE;CACtD,MAAM,YAAY,WAAW,KAAK;CAClC,MAAM,YAAY,YAAY,UAAsC;AACpE,QAAO,GAAG,OAAO,KAAK,GAAG,UAAU,GAAG,OAAO,MAAM,GAAG,YAAY,iBAAiB,OAAO,MAAM,IAAI,KAAK;;AAe3G,MAAM,gBAAgB,SAA6B,SAAiB;AAClE,KAAI,CAAC,QAAS;AACd,gBAAe,SAAS,GAAG,KAAK,IAAI;;AAGtC,MAAM,WAAW,QAAsB,OAA4C,SAAgB;CACjG,MAAM,UAAU,KAAK,SAAS,IAAI,OAAO,GAAG,KAAK,GAAG;CACpD,MAAM,uBAAO,IAAI,MAAM;CAEvB,MAAM,YAAY,GADE,oBAAoB,OAAO,OAAO,MAAM,KAAK,CAChC,IAAI;AAErC,cAAa,OAAO,SAAS,UAAU;AAEvC,KAAI,CAAC,OAAO,OAAQ;CAEpB,MAAM,cAAc,GADE,sBAAsB,OAAO,OAAO,MAAM,KAAK,CAChC,IAAI;AAEzC,SAAQ,OAAR;EACE,KAAK;AACH,WAAQ,MAAM,YAAY;AAC1B;EACF,KAAK;AACH,WAAQ,KAAK,YAAY;AACzB;EACF,KAAK;AACH,WAAQ,KAAK,YAAY;AACzB;EACF;AACE,WAAQ,MAAM,YAAY;AAC1B;;;AAIN,MAAa,UAAU,UAAuB;CAC5C,MAAM,SAAuB;EAC3B,QAAQ;EACR,GAAG;EACJ;AAED,QAAO;EACL,OAAO,GAAG,SAAgB,QAAQ,QAAQ,QAAQ,KAAK;EACvD,QAAQ,GAAG,SAAgB,QAAQ,QAAQ,SAAS,KAAK;EACzD,OAAO,GAAG,SAAgB,QAAQ,QAAQ,QAAQ,KAAK;EACvD,QAAQ,GAAG,SAAgB,QAAQ,QAAQ,SAAS,KAAK;EACzD,QAAQ,QAAgB,YAAiD,EAAE,KACzE,OAAO;GACL,GAAG;GACH,GAAG;GACH,MAAM,GAAG,OAAO,KAAK,GAAG;GACzB,CAAC;EACL"}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -24,7 +24,7 @@ program
|
|
|
24
24
|
program
|
|
25
25
|
.command("init")
|
|
26
26
|
.description("Initialize and run the process manager with config file")
|
|
27
|
-
.option("-c, --config
|
|
27
|
+
.option("-c, --config [path]", "Path to config file", "pidnap.config.ts")
|
|
28
28
|
.action(async (options) => {
|
|
29
29
|
const initLogger = logger({ name: "pidnap" });
|
|
30
30
|
try {
|
package/src/port-utils.ts
CHANGED
|
@@ -6,69 +6,32 @@ const execAsync = promisify(exec);
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Kill any process listening on the specified port, including all descendants.
|
|
9
|
-
* Works on macOS, Linux, and Windows.
|
|
10
9
|
*/
|
|
11
10
|
export async function killProcessOnPort(port: number): Promise<boolean> {
|
|
12
11
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (pids.length === 0) {
|
|
26
|
-
return false; // No process found on port
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Tree kill all processes found on the port (kills children/grandchildren too)
|
|
30
|
-
for (const pid of pids) {
|
|
31
|
-
try {
|
|
32
|
-
await treeKill(pid, "SIGKILL");
|
|
33
|
-
} catch {
|
|
34
|
-
// Process may have already exited
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return true;
|
|
39
|
-
} else if (platform === "win32") {
|
|
40
|
-
// Windows: use netstat to find PIDs
|
|
41
|
-
const { stdout } = await execAsync(
|
|
42
|
-
`netstat -ano | findstr :${port} | findstr LISTENING`,
|
|
43
|
-
);
|
|
44
|
-
const lines = stdout.trim().split("\n").filter(Boolean);
|
|
45
|
-
|
|
46
|
-
if (lines.length === 0) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const pids = new Set<number>();
|
|
51
|
-
for (const line of lines) {
|
|
52
|
-
const parts = line.trim().split(/\s+/);
|
|
53
|
-
const pid = parseInt(parts[parts.length - 1], 10);
|
|
54
|
-
if (!isNaN(pid) && pid !== 0) {
|
|
55
|
-
pids.add(pid);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
12
|
+
// Use lsof to find the process ID listening on the port
|
|
13
|
+
const { stdout } = await execAsync(`lsof -ti tcp:${port}`);
|
|
14
|
+
const pids = stdout
|
|
15
|
+
.trim()
|
|
16
|
+
.split("\n")
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.map((p) => parseInt(p, 10))
|
|
19
|
+
.filter((p) => !isNaN(p));
|
|
20
|
+
|
|
21
|
+
if (pids.length === 0) {
|
|
22
|
+
return false; // No process found on port
|
|
23
|
+
}
|
|
58
24
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
25
|
+
// Tree kill all processes found on the port (kills children/grandchildren too)
|
|
26
|
+
for (const pid of pids) {
|
|
27
|
+
try {
|
|
28
|
+
await treeKill(pid, "SIGKILL");
|
|
29
|
+
} catch {
|
|
30
|
+
// Process may have already exited
|
|
66
31
|
}
|
|
67
|
-
|
|
68
|
-
return pids.size > 0;
|
|
69
32
|
}
|
|
70
33
|
|
|
71
|
-
return
|
|
34
|
+
return true;
|
|
72
35
|
} catch {
|
|
73
36
|
// Command failed (e.g., no process on port)
|
|
74
37
|
return false;
|
package/src/tree-kill.ts
CHANGED
|
@@ -13,14 +13,11 @@ export async function treeKill(pid: number, signal: NodeJS.Signals = "SIGTERM"):
|
|
|
13
13
|
throw new Error("pid must be a number");
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const platform = process.platform;
|
|
17
|
-
|
|
18
|
-
// Unix-like systems (darwin, linux, etc.)
|
|
19
16
|
const tree: ProcessTree = { [pid]: [] };
|
|
20
17
|
const pidsToProcess = new Set<number>([pid]);
|
|
21
18
|
|
|
22
19
|
// Build the process tree
|
|
23
|
-
await buildProcessTree(pid, tree, pidsToProcess
|
|
20
|
+
await buildProcessTree(pid, tree, pidsToProcess);
|
|
24
21
|
|
|
25
22
|
// Kill all processes in the tree (children first, then parents)
|
|
26
23
|
killAll(tree, signal);
|
|
@@ -33,13 +30,9 @@ async function buildProcessTree(
|
|
|
33
30
|
parentPid: number,
|
|
34
31
|
tree: ProcessTree,
|
|
35
32
|
pidsToProcess: Set<number>,
|
|
36
|
-
platform: string,
|
|
37
33
|
): Promise<void> {
|
|
38
34
|
return new Promise((resolve) => {
|
|
39
|
-
const ps =
|
|
40
|
-
platform === "darwin"
|
|
41
|
-
? spawn("pgrep", ["-P", String(parentPid)])
|
|
42
|
-
: spawn("ps", ["-o", "pid", "--no-headers", "--ppid", String(parentPid)]);
|
|
35
|
+
const ps = spawn("ps", ["-o", "pid", "--no-headers", "--ppid", String(parentPid)]);
|
|
43
36
|
|
|
44
37
|
let allData = "";
|
|
45
38
|
|
|
@@ -74,7 +67,7 @@ async function buildProcessTree(
|
|
|
74
67
|
tree[parentPid].push(childPid);
|
|
75
68
|
tree[childPid] = [];
|
|
76
69
|
pidsToProcess.add(childPid);
|
|
77
|
-
promises.push(buildProcessTree(childPid, tree, pidsToProcess
|
|
70
|
+
promises.push(buildProcessTree(childPid, tree, pidsToProcess));
|
|
78
71
|
}
|
|
79
72
|
|
|
80
73
|
await Promise.all(promises);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"logger-uliALovz.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 platform = process.platform;\n\n // Unix-like systems (darwin, linux, etc.)\n const tree: ProcessTree = { [pid]: [] };\n const pidsToProcess = new Set<number>([pid]);\n\n // Build the process tree\n await buildProcessTree(pid, tree, pidsToProcess, platform);\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 platform: string,\n): Promise<void> {\n return new Promise((resolve) => {\n const ps =\n platform === \"darwin\"\n ? spawn(\"pgrep\", [\"-P\", String(parentPid)])\n : 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, platform));\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":";;;;;;;;;;;;;;;;;;AAUA,eAAsB,SAAS,KAAa,SAAyB,WAA0B;AAC7F,KAAI,OAAO,MAAM,IAAI,CACnB,OAAM,IAAI,MAAM,uBAAuB;CAGzC,MAAM,WAAW,QAAQ;CAGzB,MAAM,OAAoB,GAAG,MAAM,EAAE,EAAE;AAIvC,OAAM,iBAAiB,KAAK,MAHN,IAAI,IAAY,CAAC,IAAI,CAAC,EAGK,SAAS;AAG1D,SAAQ,MAAM,OAAO;;;;;AAMvB,eAAe,iBACb,WACA,MACA,eACA,UACe;AACf,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,KACJ,aAAa,WACT,MAAM,SAAS,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC,GACzC,MAAM,MAAM;GAAC;GAAM;GAAO;GAAgB;GAAU,OAAO,UAAU;GAAC,CAAC;EAE7E,IAAI,UAAU;AAEd,KAAG,OAAO,GAAG,SAAS,SAAiB;AACrC,cAAW,KAAK,SAAS,QAAQ;IACjC;AAEF,KAAG,GAAG,SAAS,OAAO,SAAS;AAC7B,iBAAc,OAAO,UAAU;AAE/B,OAAI,SAAS,GAAG;AAEd,QAAI,cAAc,SAAS,EACzB,UAAS;AAEX;;GAGF,MAAM,YAAY,QAAQ,MAAM,OAAO;AACvC,OAAI,CAAC,WAAW;AACd,QAAI,cAAc,SAAS,EACzB,UAAS;AAEX;;GAIF,MAAM,WAA4B,EAAE;AAEpC,QAAK,MAAM,UAAU,WAAW;IAC9B,MAAM,WAAW,SAAS,QAAQ,GAAG;AACrC,SAAK,WAAW,KAAK,SAAS;AAC9B,SAAK,YAAY,EAAE;AACnB,kBAAc,IAAI,SAAS;AAC3B,aAAS,KAAK,iBAAiB,UAAU,MAAM,eAAe,SAAS,CAAC;;AAG1E,SAAM,QAAQ,IAAI,SAAS;AAE3B,OAAI,cAAc,SAAS,EACzB,UAAS;IAEX;AAEF,KAAG,GAAG,eAAe;AACnB,iBAAc,OAAO,UAAU;AAC/B,OAAI,cAAc,SAAS,EACzB,UAAS;IAEX;GACF;;;;;;AAOJ,SAAS,QAAQ,MAAmB,QAA8B;CAChE,MAAM,yBAAS,IAAI,KAAa;CAGhC,MAAM,UAAU,OAAO,KAAK,KAAK,CAAC,IAAI,OAAO;AAG7C,MAAK,MAAM,OAAO,QAEhB,MAAK,MAAM,YAAY,KAAK,KAC1B,KAAI,CAAC,OAAO,IAAI,SAAS,EAAE;AACzB,UAAQ,UAAU,OAAO;AACzB,SAAO,IAAI,SAAS;;AAM1B,MAAK,MAAM,OAAO,QAChB,KAAI,CAAC,OAAO,IAAI,IAAI,EAAE;AACpB,UAAQ,KAAK,OAAO;AACpB,SAAO,IAAI,IAAI;;;;;;AAQrB,SAAS,QAAQ,KAAa,QAA8B;AAC1D,KAAI;AACF,UAAQ,KAAK,KAAK,OAAO;UAClB,KAAK;AAEZ,MAAK,IAA8B,SAAS,QAC1C,OAAM;;;;;;AC/HZ,MAAa,0BAA0B,EAAE,OAAO;CAC9C,SAAS,EAAE,QAAQ;CACnB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;CACrC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC3B,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC;CAClD,CAAC;AAIF,MAAa,qBAAqB,EAAE,SAAS;CAC3C;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;AAQF,eAAe,gBACb,OACA,QACkB;CAClB,MAAM,MAAM,MAAM;AAClB,KAAI,QAAQ,OACV,QAAO;AAGT,KAAI;AACF,QAAM,SAAS,KAAK,OAAO;AAC3B,SAAO;SACD;AAEN,MAAI;AACF,UAAO,MAAM,KAAK,OAAO;UACnB;AACN,UAAO;;;;AAKb,IAAa,cAAb,MAAyB;CACvB,AAAS;CACT,AAAQ;CACR,AAAQ;CACR,AAAQ,eAAoC;CAC5C,AAAQ,SAAuB;CAC/B,AAAQ,cAAc,QAAQ,eAAqB;CACnD,AAAO,WAA0B;CAEjC,YAAY,MAAc,YAA+B,QAAgB;AACvE,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,SAAS;;CAGhB,IAAI,QAAsB;AACxB,SAAO,KAAK;;CAGd,QAAc;AACZ,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,WAC/C,OAAM,IAAI,MAAM,YAAY,KAAK,KAAK,eAAe,KAAK,SAAS;AAGrE,MAAI,KAAK,WAAW,WAClB,OAAM,IAAI,MAAM,YAAY,KAAK,KAAK,yBAAyB;AAGjE,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,qBAAqB,KAAK,WAAW,UAAU;AAEhE,MAAI;GACF,MAAM,MAAM,KAAK,WAAW,MAAM;IAAE,GAAG,QAAQ;IAAK,GAAG,KAAK,WAAW;IAAK,GAAG,QAAQ;AAEvF,QAAK,eAAe,MAAM,KAAK,WAAW,SAAS,KAAK,WAAW,QAAQ,EAAE,EAAE;IAC7E,KAAK,KAAK,WAAW;IACrB;IACA,OAAO;KAAC;KAAU;KAAQ;KAAO;IAClC,CAAC;AAEF,QAAK,SAAS;GAGd,MAAM,WAAW,IAAI,aAAa;GAClC,IAAI,cAAc;AAElB,OAAI,KAAK,aAAa,QAAQ;AAC5B;AACA,SAAK,aAAa,OAAO,KAAK,UAAU,EAAE,KAAK,OAAO,CAAC;AACvD,SAAK,aAAa,OAAO,GAAG,aAAa;AACvC,SAAI,EAAE,gBAAgB,EAAG,UAAS,KAAK;MACvC;;AAGJ,OAAI,KAAK,aAAa,QAAQ;AAC5B;AACA,SAAK,aAAa,OAAO,KAAK,UAAU,EAAE,KAAK,OAAO,CAAC;AACvD,SAAK,aAAa,OAAO,GAAG,aAAa;AACvC,SAAI,EAAE,gBAAgB,EAAG,UAAS,KAAK;MACvC;;AAKJ,GADW,SAAS,gBAAgB,EAAE,OAAO,UAAU,CAAC,CACrD,GAAG,SAAS,SAAS;AACtB,SAAK,OAAO,KAAK,KAAK;KACtB;AAGF,QAAK,aAAa,GAAG,SAAS,MAAM,WAAW;AAC7C,SAAK,WAAW;AAEhB,QAAI,KAAK,WAAW,UAClB,KAAI,SAAS,GAAG;AACd,UAAK,SAAS;AACd,UAAK,OAAO,KAAK,4BAA4B,OAAO;eAC3C,QAAQ;AACjB,UAAK,SAAS;AACd,UAAK,OAAO,KAAK,8BAA8B,SAAS;WACnD;AACL,UAAK,SAAS;AACd,UAAK,OAAO,MAAM,4BAA4B,OAAO;;AAIzD,SAAK,YAAY,SAAS;KAC1B;AAGF,QAAK,aAAa,GAAG,UAAU,QAAQ;AACrC,QAAI,KAAK,WAAW,cAAc,KAAK,WAAW,WAAW;AAC3D,UAAK,SAAS;AACd,UAAK,OAAO,MAAM,kBAAkB,IAAI;;AAE1C,SAAK,YAAY,SAAS;KAC1B;WACK,KAAK;AACZ,QAAK,SAAS;AACd,QAAK,OAAO,MAAM,4BAA4B,IAAI;AAClD,SAAM;;;CAIV,MAAM,KAAK,SAAiC;AAC1C,MAAI,KAAK,WAAW,UAAU,KAAK,WAAW,aAAa,KAAK,WAAW,QACzE;AAGF,MAAI,KAAK,WAAW,YAAY;AAE9B,SAAM,KAAK,YAAY;AACvB;;AAGF,MAAI,CAAC,KAAK,cAAc;AACtB,QAAK,SAAS;AACd;;AAGF,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,gCAAgC;AAGjD,QAAM,gBAAgB,KAAK,cAAc,UAAU;EAEnD,MAAM,YAAY,WAAW;EAG7B,MAAM,iBAAiB,IAAI,SAAoB,YAC7C,iBAAiB,QAAQ,UAAU,EAAE,UAAU,CAChD;AAOD,MALe,MAAM,QAAQ,KAAK,CAChC,KAAK,YAAY,QAAQ,WAAW,SAAkB,EACtD,eACD,CAAC,KAEa,aAAa,KAAK,cAAc;AAC7C,QAAK,OAAO,KAAK,+BAA+B,UAAU,qBAAqB;AAC/E,SAAM,gBAAgB,KAAK,cAAc,UAAU;GAGnD,MAAM,cAAc,IAAI,SAAe,YAAY,WAAW,SAAS,IAAK,CAAC;AAC7E,SAAM,QAAQ,KAAK,CAAC,KAAK,YAAY,SAAS,YAAY,CAAC;;AAG7D,OAAK,SAAS;AACd,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,kBAAkB;;CAGrC,MAAM,QAAuB;AAC3B,MAAI,KAAK,cAAc;AAErB,SAAM,gBAAgB,KAAK,cAAc,UAAU;AACnD,SAAM,KAAK,YAAY;AACvB,QAAK,SAAS;;AAGhB,OAAK,SAAS;AAEd,OAAK,cAAc,QAAQ,eAAqB;AAChD,OAAK,OAAO,KAAK,wBAAwB;;CAG3C,iBAAiB,YAAqC;AACpD,OAAK,aAAa;;CAGpB,MAAM,cAAqC;AACzC,MAAI,CAAC,KAAK,aACR,QAAO,KAAK;AAGd,QAAM,KAAK,YAAY;AACvB,SAAO,KAAK;;CAGd,AAAQ,UAAgB;AACtB,MAAI,KAAK,cAAc;AAErB,QAAK,aAAa,QAAQ,oBAAoB;AAC9C,QAAK,aAAa,QAAQ,oBAAoB;AAC9C,QAAK,aAAa,oBAAoB;AACtC,QAAK,eAAe;;AAGtB,OAAK,WAAW;;;;;;AC3OpB,MAAa,sBAAsB,EAAE,SAAS;CAC5C;CACA;CACA;CACA;CACA;CACD,CAAC;AAKF,MAAa,wBAAwB,EAAE,MAAM,CAC3C,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,QAAQ;CACxB,SAAS,EAAE,QAAQ;CACpB,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,cAAc;CAC9B,gBAAgB,EAAE,QAAQ;CAC1B,YAAY,EAAE,QAAQ;CACtB,YAAY,EAAE,SAAS,EAAE,QAAQ,CAAC;CACnC,CAAC,CACH,CAAC;AAKF,MAAa,wBAAwB,EAAE,OAAO;CAC5C,aAAa,EAAE,QAAQ;CACvB,UAAU,EAAE,QAAQ;CACpB,WAAW,EAAE,QAAQ;CACtB,CAAC;AAKF,MAAa,iCAAiC,EAAE,OAAO;CACrD,eAAe;CACf,SAAS,EAAE,SAAS,sBAAsB;CAC1C,WAAW,EAAE,SAAS,sBAAsB;CAC5C,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC;CACnC,kBAAkB,EAAE,SAAS,EAAE,QAAQ,CAAC;CACzC,CAAC;AAKF,MAAa,+BAA+B,EAAE,SAAS;CACrD;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAIF,MAAM,kBAAmC;CAAE,MAAM;CAAS,SAAS;CAAM;AACzE,MAAM,qBAAsC;CAAE,aAAa;CAAG,UAAU;CAAO,WAAW;CAAO;AAEjG,IAAa,oBAAb,MAA+B;CAC7B,AAAS;CACT,AAAQ;CACR,AAAQ;CACR,AAAQ;CAGR,AAAQ;CAGR,AAAQ,SAAiC;CACzC,AAAQ,gBAAwB;CAChC,AAAQ,oBAA8B,EAAE;CACxC,AAAQ,sBAA8B;CACtC,AAAQ,gBAA+B;CACvC,AAAQ,gBAAyB;CACjC,AAAQ,sBAA4D;CAEpE,YACE,MACA,YACA,SACA,QACA;AACA,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,SAAS;AACd,OAAK,UAAU;GACb,eAAe,QAAQ;GACvB,SAAS,QAAQ,WAAW;GAC5B,WAAW,QAAQ,aAAa;GAChC,aAAa,QAAQ,eAAe;GACpC,kBAAkB,QAAQ;GAC3B;AACD,OAAK,cAAc,IAAI,YAAY,MAAM,YAAY,OAAO;;CAG9D,IAAI,QAAgC;AAClC,SAAO,KAAK;;CAGd,IAAI,WAAmB;AACrB,SAAO,KAAK;;CAGd,QAAc;AACZ,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,aAC/C,OAAM,IAAI,MAAM,YAAY,KAAK,KAAK,eAAe,KAAK,SAAS;AAGrE,MAAI,KAAK,WAAW,WAClB,OAAM,IAAI,MAAM,YAAY,KAAK,KAAK,yBAAyB;AAIjE,MACE,KAAK,WAAW,aAChB,KAAK,WAAW,UAChB,KAAK,WAAW,uBAEhB,MAAK,eAAe;AAGtB,OAAK,gBAAgB;AACrB,OAAK,cAAc;;CAGrB,MAAM,KAAK,SAAiC;AAC1C,OAAK,gBAAgB;AAGrB,MAAI,KAAK,qBAAqB;AAC5B,gBAAa,KAAK,oBAAoB;AACtC,QAAK,sBAAsB;;AAG7B,MACE,KAAK,WAAW,UAChB,KAAK,WAAW,aAChB,KAAK,WAAW,wBAChB;AACA,QAAK,SAAS;AACd;;AAGF,OAAK,SAAS;AACd,QAAM,KAAK,YAAY,KAAK,QAAQ;AACpC,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,4BAA4B;;CAG/C,MAAM,QAAQ,QAAiB,OAAsB;AAEnD,MACE,KAAK,WAAW,aAChB,KAAK,WAAW,UAChB,KAAK,WAAW,wBAChB;AACA,QAAK,eAAe;AACpB,QAAK,gBAAgB;AACrB,QAAK,cAAc;AACnB;;AAIF,QAAM,KAAK,MAAM;AAEjB,OAAK,gBAAgB;AAErB,MAAI,MAEF,MAAK,cAAc;OACd;GAEL,MAAM,QAAQ,KAAK,gBAAgB;AACnC,OAAI,QAAQ,GAAG;AACb,SAAK,SAAS;AACd,SAAK,OAAO,KAAK,iBAAiB,MAAM,IAAI;AAC5C,UAAM,KAAK,MAAM,MAAM;AACvB,QAAI,KAAK,cAAe;;AAE1B,QAAK,cAAc;;;;;;CAOvB,MAAM,OACJ,eACA,qBAA8B,MACf;AACf,OAAK,OAAO,KAAK,wCAAwC;AACzD,OAAK,aAAa;AAClB,OAAK,YAAY,iBAAiB,cAAc;AAEhD,MAAI,mBAEF,OAAM,KAAK,QAAQ,KAAK;;;;;CAO5B,cAAc,YAAqD;AACjE,OAAK,OAAO,KAAK,2BAA2B;AAC5C,OAAK,UAAU;GACb,GAAG,KAAK;GACR,eAAe,WAAW,iBAAiB,KAAK,QAAQ;GACxD,SAAS,WAAW,WAAW,KAAK,QAAQ;GAC5C,WAAW,WAAW,aAAa,KAAK,QAAQ;GAChD,aAAa,WAAW,eAAe,KAAK,QAAQ;GACpD,kBAAkB,WAAW,oBAAoB,KAAK,QAAQ;GAC/D;;CAGH,AAAQ,gBAAsB;AAC5B,OAAK,gBAAgB;AACrB,OAAK,sBAAsB;AAC3B,OAAK,oBAAoB,EAAE;;CAG7B,AAAQ,eAAqB;AAC3B,OAAK,gBAAgB,KAAK,KAAK;AAC/B,OAAK,SAAS;AAEd,OAAK,YACF,OAAO,CACP,WAAW;AACV,OAAI,KAAK,cAAe;AACxB,QAAK,YAAY,OAAO;AACxB,UAAO,KAAK,YAAY,aAAa;IACrC,CACD,MAAM,cAAc;AACnB,OAAI,CAAC,UAAW;AAChB,OAAI,KAAK,iBAAiB,cAAc,SAAS;AAC/C,SAAK,SAAS;AACd;;AAEF,OAAI,cAAc,aAAa,cAAc,QAC3C,MAAK,kBAAkB,UAAU;IAEnC,CACD,OAAO,QAAQ;AACd,OAAI,KAAK,cAAe;AACxB,QAAK,SAAS;AACd,QAAK,OAAO,MAAM,4BAA4B,IAAI;IAClD;;CAGN,AAAQ,kBAAkB,WAA+B;AACvD,MAAI,KAAK,eAAe;AACtB,QAAK,SAAS;AACd;;EAIF,MAAM,cADS,KAAK,gBAAgB,KAAK,KAAK,GAAG,KAAK,gBAAgB,MACzC,KAAK,QAAQ;EAC1C,MAAM,kBAAkB,cAAc;AAGtC,MAAI,WACF,MAAK,sBAAsB;MAE3B,MAAK;AAIP,MAAI,CAAC,KAAK,cAAc,gBAAgB,EAAE;AACxC,QAAK,SAAS;AACd,QAAK,OAAO,KACV,2BAA2B,KAAK,QAAQ,cAAc,0BACvD;AACD;;AAIF,MACE,KAAK,QAAQ,qBAAqB,UAClC,KAAK,iBAAiB,KAAK,QAAQ,kBACnC;AACA,QAAK,SAAS;AACd,QAAK,OAAO,KAAK,uBAAuB,KAAK,QAAQ,iBAAiB,WAAW;AACjF;;EAIF,MAAM,MAAM,KAAK,KAAK;AACtB,OAAK,kBAAkB,KAAK,IAAI;AAGhC,MAAI,KAAK,eAAe,EAAE;AACxB,QAAK,SAAS;AACd,QAAK,OAAO,KACV,wBAAwB,KAAK,QAAQ,UAAU,YAAY,eAAe,KAAK,QAAQ,UAAU,SAAS,uBAAuB,KAAK,QAAQ,UAAU,UAAU,IACnK;AACD,QAAK,2BAA2B;AAChC;;AAIF,OAAK;AACL,OAAK,iBAAiB;;CAGxB,AAAQ,cAAc,iBAAmC;AACvD,UAAQ,KAAK,QAAQ,eAArB;GACE,KAAK,SACH,QAAO;GACT,KAAK,QACH,QAAO;GACT,KAAK,aACH,QAAO;GACT,KAAK,aACH,QAAO,CAAC;GACV,KAAK,iBACH,QAAO,CAAC,KAAK;GACf,QACE,QAAO;;;CAIb,AAAQ,gBAAyB;EAC/B,MAAM,EAAE,aAAa,aAAa,KAAK,QAAQ;EAE/C,MAAM,SADM,KAAK,KAAK,GACD;AAGrB,OAAK,oBAAoB,KAAK,kBAAkB,QAAQ,OAAO,KAAK,OAAO;AAE3E,SAAO,KAAK,kBAAkB,UAAU;;CAG1C,AAAQ,iBAAyB;EAC/B,MAAM,EAAE,YAAY,KAAK;AAEzB,MAAI,QAAQ,SAAS,QACnB,QAAO,QAAQ;EAIjB,MAAM,aAAa,QAAQ,cAAc;EACzC,MAAM,QAAQ,QAAQ,iBAAiB,KAAK,IAAI,YAAY,KAAK,oBAAoB;AACrF,SAAO,KAAK,IAAI,OAAO,QAAQ,WAAW;;CAG5C,AAAQ,kBAAwB;AAC9B,OAAK,SAAS;EACd,MAAM,QAAQ,KAAK,gBAAgB;AAEnC,OAAK,OAAO,KAAK,iBAAiB,MAAM,eAAe,KAAK,cAAc,GAAG;AAE7E,OAAK,sBAAsB,iBAAiB;AAC1C,QAAK,sBAAsB;AAC3B,OAAI,KAAK,eAAe;AACtB,SAAK,SAAS;AACd;;AAEF,QAAK,cAAc;KAClB,MAAM;;CAGX,AAAQ,4BAAkC;EACxC,MAAM,EAAE,cAAc,KAAK,QAAQ;AAEnC,OAAK,sBAAsB,iBAAiB;AAC1C,QAAK,sBAAsB;AAC3B,OAAI,KAAK,eAAe;AACtB,SAAK,SAAS;AACd;;AAIF,QAAK,oBAAoB,EAAE;AAC3B,QAAK;AACL,QAAK,OAAO,KAAK,qDAAqD,KAAK,cAAc,GAAG;AAC5F,QAAK,cAAc;KAClB,UAAU;;CAGf,AAAQ,MAAM,IAA2B;AACvC,SAAO,IAAI,SAAS,YAAY;AAC9B,QAAK,sBAAsB,iBAAiB;AAC1C,SAAK,sBAAsB;AAC3B,aAAS;MACR,GAAG;IACN;;;;;;ACpYN,MAAa,oBAAoB,EAAE,OAAO;CACxC,YAAY,EAAE,QAAQ;CACtB,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC;CAChC,CAAC;AAKF,MAAa,2BAA2B,EAAE,OAAO;CAC/C,UAAU,EAAE,QAAQ;CACpB,OAAO,EAAE,SAAS,kBAAkB;CACpC,YAAY,EAAE,SAAS,EAAE,SAAS,CAAC;CACpC,CAAC;AAKF,MAAa,yBAAyB,EAAE,SAAS;CAC/C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAIF,MAAM,sBAAsB;AAE5B,IAAa,cAAb,MAAyB;CACvB,AAAS;CACT,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ,UAAuB;CAG/B,AAAQ,SAA2B;CACnC,AAAQ,YAAoB;CAC5B,AAAQ,aAAqB;CAC7B,AAAQ,sBAA8B;CACtC,AAAQ,YAAqB;CAC7B,AAAQ,gBAAyB;CACjC,AAAQ,eAAqD;CAE7D,YACE,MACA,YACA,SACA,QACA;AACA,OAAK,OAAO;AACZ,OAAK,UAAU;AACf,OAAK,SAAS;AACd,OAAK,cAAc,IAAI,YAAY,MAAM,YAAY,OAAO;;CAG9D,IAAI,QAA0B;AAC5B,SAAO,KAAK;;CAGd,IAAI,WAAmB;AACrB,SAAO,KAAK;;CAGd,IAAI,YAAoB;AACtB,SAAO,KAAK;;CAGd,IAAI,UAAuB;AACzB,MAAI,CAAC,KAAK,QAAS,QAAO;AAE1B,SADa,KAAK,QAAQ,SAAS,IACpB;;CAGjB,QAAc;AACZ,MAAI,KAAK,WAAW,eAAe,KAAK,WAAW,aAAa,KAAK,WAAW,SAC9E,OAAM,IAAI,MAAM,gBAAgB,KAAK,KAAK,eAAe,KAAK,SAAS;AAGzE,MAAI,KAAK,WAAW,WAClB,OAAM,IAAI,MAAM,gBAAgB,KAAK,KAAK,yBAAyB;AAGrE,OAAK,gBAAgB;AACrB,OAAK,OAAO,KAAK,2BAA2B,KAAK,QAAQ,WAAW;AAGpE,OAAK,UAAU,IAAI,KAAK,KAAK,QAAQ,UAAU,EAAE,UAAU,OAAO,QAAQ;AACxE,QAAK,YAAY;IACjB;AAEF,OAAK,SAAS;AAGd,MAAI,KAAK,QAAQ,WACf,MAAK,YAAY;;CAIrB,MAAM,KAAK,SAAiC;AAC1C,OAAK,gBAAgB;AAGrB,MAAI,KAAK,SAAS;AAChB,QAAK,QAAQ,MAAM;AACnB,QAAK,UAAU;;AAIjB,MAAI,KAAK,cAAc;AACrB,gBAAa,KAAK,aAAa;AAC/B,QAAK,eAAe;;AAGtB,MAAI,KAAK,WAAW,UAAU,KAAK,WAAW,WAAW;AACvD,QAAK,SAAS;AACd;;AAIF,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,cAAc,KAAK,WAAW,UAAU;AACvF,QAAK,SAAS;AACd,SAAM,KAAK,YAAY,KAAK,QAAQ;;AAGtC,OAAK,SAAS;AACd,OAAK,YAAY;AACjB,OAAK,OAAO,KAAK,sBAAsB;;CAGzC,MAAM,UAAyB;AAC7B,MAAI,KAAK,cACP,OAAM,IAAI,MAAM,gBAAgB,KAAK,KAAK,cAAc;AAI1D,MAAI,KAAK,WAAW,SAClB;AAIF,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,YAAY;AAC3D,QAAK,YAAY;AACjB,QAAK,SAAS;AACd,QAAK,OAAO,KAAK,yCAAyC;AAC1D;;AAGF,QAAM,KAAK,YAAY;;CAGzB,AAAQ,aAAmB;AACzB,MAAI,KAAK,cAAe;AAGxB,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,cAAc,KAAK,WAAW,UAAU;AACvF,QAAK,YAAY;AACjB,OAAI,KAAK,WAAW,SAClB,MAAK,SAAS;AAEhB,QAAK,OAAO,KAAK,oDAAoD;AACrE;;AAGF,OAAK,YAAY;;CAGnB,MAAc,aAA4B;AACxC,MAAI,KAAK,cAAe;AAExB,OAAK,SAAS;AACd,OAAK,sBAAsB;AAC3B,OAAK,OAAO,KAAK,gBAAgB;AAEjC,QAAM,KAAK,iBAAiB;;CAG9B,MAAc,kBAAiC;AAC7C,MAAI,KAAK,cAAe;AAGxB,QAAM,KAAK,YAAY,OAAO;AAC9B,OAAK,YAAY,OAAO;EAExB,MAAM,YAAY,MAAM,KAAK,YAAY,aAAa;AACtD,MAAI,KAAK,iBAAiB,cAAc,SAAS;AAC/C,QAAK,SAAS;AACd;;AAEF,OAAK,kBAAkB,cAAc,QAAQ;;CAG/C,AAAQ,kBAAkB,QAAuB;AAC/C,MAAI,KAAK,eAAe;AACtB,QAAK,SAAS;AACd;;AAGF,MAAI,QAAQ;GACV,MAAM,aAAa,KAAK,QAAQ,OAAO,cAAc;AAErD,OAAI,KAAK,sBAAsB,YAAY;AAEzC,SAAK;AACL,SAAK,SAAS;IACd,MAAM,UAAU,KAAK,QAAQ,OAAO,WAAW;AAE/C,SAAK,OAAO,KACV,2BAA2B,QAAQ,cAAc,KAAK,oBAAoB,GAAG,WAAW,GACzF;AAED,SAAK,eAAe,iBAAiB;AACnC,UAAK,eAAe;AACpB,SAAI,KAAK,eAAe;AACtB,WAAK,SAAS;AACd;;AAEF,UAAK,iBAAiB;OACrB,QAAQ;AACX;;AAIF,QAAK;AACL,QAAK,OAAO,MAAM,oBAAoB,KAAK,oBAAoB,UAAU;SACpE;AACL,QAAK;AACL,QAAK,OAAO,KAAK,6BAA6B;;AAIhD,MAAI,KAAK,WAAW;AAClB,QAAK,YAAY;AACjB,QAAK,OAAO,KAAK,sBAAsB;AACvC,QAAK,YAAY;AACjB;;AAIF,MAAI,KAAK,QACP,MAAK,SAAS;MAEd,MAAK,SAAS;;;;;;ACtPpB,MAAa,kBAAkB,EAAE,SAAS;CAAC;CAAW;CAAW;CAAa;CAAU;CAAU,CAAC;AAKnG,MAAa,+BAA+B,EAAE,OAAO;CACnD,MAAM,EAAE,QAAQ;CAChB,SAAS;CACV,CAAC;AAcF,IAAa,WAAb,MAAsB;CACpB,AAAS;CACT,AAAQ,SAAsB,EAAE;CAChC,AAAQ,SAAwB;CAChC,AAAQ;CACR,AAAQ;CACR,AAAQ,gBAAwB;CAChC,AAAQ,mBAAkC,EAAE;CAC5C,AAAQ,gBAAyB;CACjC,AAAQ,iBAAuC;CAE/C,YACE,MACA,QACA,cACA,iBACA;AACA,OAAK,OAAO;AACZ,OAAK,SAAS;AACd,OAAK,kBAAkB;AAGvB,MAAI,aACF,MAAK,MAAM,QAAQ,aACjB,MAAK,QAAQ,KAAK;;CAKxB,IAAI,QAAuB;AACzB,SAAO,KAAK;;CAGd,IAAI,QAAkC;AACpC,SAAO,KAAK;;CAGd,mBAAmB,QAAoC;EACrD,MAAM,QACJ,OAAO,WAAW,WAAW,SAAS,KAAK,OAAO,WAAW,MAAM,EAAE,OAAO,OAAO;AACrF,MAAI,QAAQ,KAAK,SAAS,KAAK,OAAO,OACpC,OAAM,IAAI,MAAM,mBAAmB,SAAS;EAG9C,MAAM,OAAO,KAAK,OAAO;AACzB,MAAI,KAAK,UAAU,UACjB,OAAM,IAAI,MAAM,+BAA+B,KAAK,KAAK;AAG3D,OAAK,OAAO,OAAO,OAAO,EAAE;AAC5B,OAAK,OAAO,KAAK,SAAS,KAAK,GAAG,WAAW;AAC7C,SAAO;;;;;;CAOT,QAAQ,MAAiE;EACvE,MAAM,KAAK,QAAQ,EAAE,KAAK;EAC1B,MAAM,YAAY,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC,KAAK;EAErD,MAAM,QAAmB;GACvB;GACA;GACA,OAAO;GACR;AAED,OAAK,OAAO,KAAK,MAAM;AACvB,OAAK,OAAO,KAAK,SAAS,GAAG,eAAe,UAAU,OAAO,cAAc;AAE3E,SAAO;;;;;CAMT,QAAc;AACZ,MAAI,KAAK,WAAW,UAClB,OAAM,IAAI,MAAM,aAAa,KAAK,KAAK,sBAAsB;AAG/D,OAAK,gBAAgB;AACrB,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,mBAAmB;AAGpC,OAAK,iBAAiB,KAAK,SAAS;;;;;CAMtC,MAAM,gBAA+B;AACnC,MAAI,KAAK,WAAW,UAAU,KAAK,WAAW,UAC5C;AAIF,MAAI,KAAK,eACP,OAAM,KAAK;;;;;CAOf,MAAM,KAAK,SAAiC;AAC1C,MAAI,KAAK,WAAW,UAAU,KAAK,WAAW,WAAW;AACvD,QAAK,SAAS;AACd;;AAGF,OAAK,gBAAgB;AACrB,OAAK,OAAO,KAAK,uBAAuB;EAGxC,MAAM,eAAe,KAAK,iBAAiB,KAAK,MAAM,EAAE,KAAK,QAAQ,CAAC;AACtE,QAAM,QAAQ,IAAI,aAAa;AAC/B,OAAK,mBAAmB,EAAE;AAG1B,OAAK,MAAM,QAAQ,KAAK,OACtB,KAAI,KAAK,UAAU,UACjB,MAAK,QAAQ;AAKjB,MAAI,KAAK,gBAAgB;AACvB,SAAM,KAAK;AACX,QAAK,iBAAiB;;AAGxB,OAAK,SAAS;AACd,OAAK,OAAO,KAAK,mBAAmB;;CAGtC,MAAc,UAAyB;AACrC,SAAO,KAAK,WAAW,aAAa,CAAC,KAAK,eAAe;GAEvD,MAAM,WAAW,KAAK,OAAO,MAAM,MAAM,EAAE,UAAU,UAAU;AAE/D,OAAI,CAAC,UAAU;AAEb,SAAK,SAAS;AACd,SAAK,OAAO,KAAK,wCAAwC;AACzD;;AAGF,SAAM,KAAK,YAAY,SAAS;;;CAIpC,MAAc,YAAY,MAAgC;AACxD,MAAI,KAAK,eAAe;AACtB,QAAK,QAAQ;AACb;;AAGF,OAAK,QAAQ;EACb,MAAM,YAAY,KAAK,UAAU,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK;AAC9D,OAAK,OAAO,KAAK,mBAAmB,KAAK,GAAG,MAAM,UAAU,GAAG;EAG/D,MAAM,gBAA+B,KAAK,UAAU,KAAK,MAAM;GAC7D,MAAM,UAAU,KAAK,kBAAkB,EAAE,KAAK;GAC9C,MAAM,cAAc,UAChB,KAAK,OAAO,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,GACtC,KAAK,OAAO,MAAM,EAAE,KAAK;AAC7B,UAAO,IAAI,YAAY,EAAE,MAAM,EAAE,SAAS,YAAY;IACtD;AAEF,OAAK,mBAAmB;AAExB,MAAI;AAEF,QAAK,MAAM,MAAM,cACf,IAAG,OAAO;GAOZ,MAAM,aAHU,MAAM,QAAQ,IAAI,cAAc,KAAK,OAAO,KAAK,eAAe,GAAG,CAAC,CAAC,EAG3D,MAAM,MAAM,MAAM,QAAQ;AAEpD,OAAI,KAAK,cACP,MAAK,QAAQ;YACJ,WAAW;AACpB,SAAK,QAAQ;AACb,SAAK,OAAO,KAAK,SAAS,KAAK,GAAG,UAAU;UACvC;AACL,SAAK,QAAQ;AACb,SAAK,OAAO,KAAK,SAAS,KAAK,GAAG,aAAa;;WAE1C,KAAK;AACZ,QAAK,QAAQ;AACb,QAAK,OAAO,MAAM,SAAS,KAAK,GAAG,WAAW,IAAI;YAC1C;AACR,QAAK,mBAAmB,EAAE;;;CAI9B,MAAc,eAAe,IAA+C;AAE1E,SADc,MAAM,GAAG,aAAa,KACnB,UAAU,UAAU;;;;;;AC7MzC,IAAa,aAAb,MAAwB;CACtB,AAAQ,sBAA2C,IAAI,KAAK;CAC5D,AAAQ;CACR,AAAQ;CACR,AAAQ,2BAAkD,IAAI,KAAK;CACnE,AAAQ,6BAAuC,IAAI,KAAK;CACxD,AAAQ,kCAA0C,IAAI,KAAK;CAC3D,AAAQ,uCAAmE,IAAI,KAAK;CAEpF,YAAY,SAA2B,EAAE,EAAE;AACzC,OAAK,MAAM,OAAO,OAAO,QAAQ,KAAK;AACtC,OAAK,eAAe,OAAO,SAAS;AAGpC,OAAK,qBAAqB;AAG1B,MAAI,OAAO,MACT,MAAK,MAAM,CAAC,KAAK,aAAa,OAAO,QAAQ,OAAO,MAAM,CACxD,MAAK,YAAY,KAAK,SAAS;;CAKrC,aAAa,KAAa,UAAwB;AAChD,OAAK,YAAY,KAAK,SAAS;;CAGjC,aAAa,KAAqC;AAChD,SAAO,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE;;;;;CAMhC,AAAQ,sBAA4B;EAElC,MAAM,aAAa,QAAQ,KAAK,KAAK,OAAO;AAC5C,MAAI,WAAW,WAAW,CACxB,MAAK,YAAY,UAAU,WAAW;AAIxC,MAAI;GAEF,MAAM,WAAW,SADD,KAAK,KAAK,KAAK,SAAS,CACN;AAElC,QAAK,MAAM,YAAY,UAAU;IAG/B,MAAM,QADW,SAAS,SAAS,CACZ,MAAM,gBAAgB;AAC7C,QAAI,OAAO;KACT,MAAM,SAAS,MAAM;AACrB,UAAK,YAAY,QAAQ,SAAS;;;WAG/B,KAAK;AACZ,WAAQ,KAAK,6BAA6B,IAAI;;;;;;CAOlD,AAAQ,YAAY,KAAa,UAAwB;EACvD,MAAM,eAAe,QAAQ,KAAK,KAAK,SAAS;AAEhD,MAAI,CAAC,WAAW,aAAa,CAC3B;AAGF,MAAI;GAEF,MAAM,SAAS,MADC,aAAa,cAAc,QAAQ,CACtB;AAC7B,QAAK,IAAI,IAAI,KAAK,OAAO;AAGzB,OAAI,CAAC,KAAK,WAAW,IAAI,aAAa,CACpC,MAAK,WAAW,IAAI,8BAAc,IAAI,KAAK,CAAC;AAE9C,QAAK,WAAW,IAAI,aAAa,CAAE,IAAI,IAAI;AAG3C,OAAI,KAAK,gBAAgB,CAAC,KAAK,SAAS,IAAI,aAAa,CACvD,MAAK,UAAU,aAAa;WAEvB,KAAK;AACZ,WAAQ,KAAK,4BAA4B,gBAAgB,IAAI;;;;;;CAOjE,AAAQ,UAAU,cAA4B;AAC5C,MAAI;GACF,MAAM,UAAU,MAAM,eAAe,cAAc;AACjD,QAAI,cAAc,SAChB,MAAK,iBAAiB,aAAa;KAErC;AAEF,QAAK,SAAS,IAAI,cAAc,QAAQ;WACjC,KAAK;AACZ,WAAQ,KAAK,6BAA6B,gBAAgB,IAAI;;;;;;CAOlE,AAAQ,iBAAiB,cAA4B;EAEnD,MAAM,gBAAgB,KAAK,qBAAqB,IAAI,aAAa;AACjE,MAAI,cACF,cAAa,cAAc;EAI7B,MAAM,QAAQ,iBAAiB;AAC7B,QAAK,WAAW,aAAa;AAC7B,QAAK,qBAAqB,OAAO,aAAa;KAC7C,IAAI;AAEP,OAAK,qBAAqB,IAAI,cAAc,MAAM;;;;;CAMpD,AAAQ,WAAW,cAA4B;EAC7C,MAAM,OAAO,KAAK,WAAW,IAAI,aAAa;AAC9C,MAAI,CAAC,KAAM;AAEX,WAAS,cAAc,QAAQ,CAC5B,MAAM,YAAY,MAAM,QAAQ,CAAC,CACjC,MAAM,WAAW;GAChB,MAAM,cAAwB,EAAE;AAChC,QAAK,MAAM,OAAO,MAAM;AACtB,SAAK,IAAI,IAAI,KAAK,OAAO;AACzB,gBAAY,KAAK,IAAI;;AAIvB,OAAI,YAAY,SAAS,EACvB,MAAK,MAAM,YAAY,KAAK,gBAC1B,UAAS,YAAY;IAGzB,CACD,OAAO,QAAQ;AACd,WAAQ,KAAK,8BAA8B,gBAAgB,IAAI;IAC/D;;;;;;CAON,SAAS,UAAyC;AAChD,OAAK,gBAAgB,IAAI,SAAS;AAClC,eAAa;AACX,QAAK,gBAAgB,OAAO,SAAS;;;;;;CAOzC,UAAgB;AAEd,OAAK,MAAM,SAAS,KAAK,qBAAqB,QAAQ,CACpD,cAAa,MAAM;AAErB,OAAK,qBAAqB,OAAO;AAGjC,OAAK,MAAM,WAAW,KAAK,SAAS,QAAQ,CAC1C,SAAQ,OAAO;AAEjB,OAAK,SAAS,OAAO;AAGrB,OAAK,gBAAgB,OAAO;;;;;;;CAQ9B,WAAW,YAA6C;EACtD,MAAM,YAAY,KAAK,IAAI,IAAI,SAAS,IAAI,EAAE;AAE9C,MAAI,CAAC,WACH,QAAO,EAAE,GAAG,WAAW;EAGzB,MAAM,aAAa,KAAK,IAAI,IAAI,WAAW,IAAI,EAAE;AACjD,SAAO;GAAE,GAAG;GAAW,GAAG;GAAY;;;;;CAMxC,YAAyD;AACvD,SAAO,KAAK;;;;;;ACvOhB,MAAM,SAAS;CACb,OAAO;CACP,MAAM;CACN,OAAO;CACP,OAAO;CACP,QAAQ;CACR,KAAK;CACL,MAAM;CACP;AAED,MAAM,cAAc;CAClB,OAAO,OAAO;CACd,MAAM,OAAO;CACb,MAAM,OAAO;CACb,OAAO,OAAO;CACf;AAED,MAAM,cAAc,SAClB,KAAK,eAAe,SAAS;CAC3B,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,wBAAwB;CACxB,WAAW;CACZ,CAAC,CAAC,OAAO,KAAK;AAEjB,MAAM,uBAAuB,OAAe,MAAc,SAAe;CACvE,MAAM,iBAAiB,MAAM,aAAa,CAAC,SAAS,EAAE;AAEtD,QAAO,IADW,WAAW,KAAK,CACb,IAAI,eAAe,IAAI,KAAK;;AAGnD,MAAM,yBAAyB,OAAe,MAAc,SAAe;CACzE,MAAM,iBAAiB,MAAM,aAAa,CAAC,SAAS,EAAE;CACtD,MAAM,YAAY,WAAW,KAAK;CAClC,MAAM,YAAY,YAAY,UAAsC;AACpE,QAAO,GAAG,OAAO,KAAK,GAAG,UAAU,GAAG,OAAO,MAAM,GAAG,YAAY,iBAAiB,OAAO,MAAM,IAAI,KAAK;;AAe3G,MAAM,gBAAgB,SAA6B,SAAiB;AAClE,KAAI,CAAC,QAAS;AACd,gBAAe,SAAS,GAAG,KAAK,IAAI;;AAGtC,MAAM,WAAW,QAAsB,OAA4C,SAAgB;CACjG,MAAM,UAAU,KAAK,SAAS,IAAI,OAAO,GAAG,KAAK,GAAG;CACpD,MAAM,uBAAO,IAAI,MAAM;CAEvB,MAAM,YAAY,GADE,oBAAoB,OAAO,OAAO,MAAM,KAAK,CAChC,IAAI;AAErC,cAAa,OAAO,SAAS,UAAU;AAEvC,KAAI,CAAC,OAAO,OAAQ;CAEpB,MAAM,cAAc,GADE,sBAAsB,OAAO,OAAO,MAAM,KAAK,CAChC,IAAI;AAEzC,SAAQ,OAAR;EACE,KAAK;AACH,WAAQ,MAAM,YAAY;AAC1B;EACF,KAAK;AACH,WAAQ,KAAK,YAAY;AACzB;EACF,KAAK;AACH,WAAQ,KAAK,YAAY;AACzB;EACF;AACE,WAAQ,MAAM,YAAY;AAC1B;;;AAIN,MAAa,UAAU,UAAuB;CAC5C,MAAM,SAAuB;EAC3B,QAAQ;EACR,GAAG;EACJ;AAED,QAAO;EACL,OAAO,GAAG,SAAgB,QAAQ,QAAQ,QAAQ,KAAK;EACvD,QAAQ,GAAG,SAAgB,QAAQ,QAAQ,SAAS,KAAK;EACzD,OAAO,GAAG,SAAgB,QAAQ,QAAQ,QAAQ,KAAK;EACvD,QAAQ,GAAG,SAAgB,QAAQ,QAAQ,SAAS,KAAK;EACzD,QAAQ,QAAgB,YAAiD,EAAE,KACzE,OAAO;GACL,GAAG;GACH,GAAG;GACH,MAAM,GAAG,OAAO,KAAK,GAAG;GACzB,CAAC;EACL"}
|