pid1 0.0.0-dev.0 → 0.0.0-dev.1
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/dist/cli.mjs +1 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-Bdt88RU-.d.mts → client-B02fC2Sv.d.mts} +1 -2
- package/dist/{client-Bdt88RU-.d.mts.map → client-B02fC2Sv.d.mts.map} +1 -1
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs.map +1 -1
- package/dist/config-BgRb4pSG.mjs.map +1 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{process-manager-gO56266Q.mjs → process-manager-Ddk5dALx.mjs} +1 -7
- package/dist/process-manager-Ddk5dALx.mjs.map +1 -0
- package/package.json +11 -9
- package/src/api/client.ts +1 -3
- package/src/api/router.ts +6 -10
- package/src/api/server.ts +1 -3
- package/src/cli.ts +2 -6
- package/src/config.ts +5 -10
- package/src/env.ts +3 -12
- package/src/exec.ts +2 -9
- package/src/process-manager.ts +18 -49
- package/dist/process-manager-gO56266Q.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { i as globalLogger } from "./config-BgRb4pSG.mjs";
|
|
3
|
-
import { t as ProcessManager } from "./process-manager-
|
|
3
|
+
import { t as ProcessManager } from "./process-manager-Ddk5dALx.mjs";
|
|
4
4
|
import { createClient } from "./client.mjs";
|
|
5
5
|
import Table from "cli-table3";
|
|
6
6
|
import { Command } from "commander";
|
|
@@ -51,7 +51,6 @@ async function runInit(argv) {
|
|
|
51
51
|
pm.serve();
|
|
52
52
|
await pm.init();
|
|
53
53
|
pm.startAll();
|
|
54
|
-
await pm.wait();
|
|
55
54
|
}
|
|
56
55
|
function createRpcClient(argv) {
|
|
57
56
|
return createClient({ url: `http://${argv.host ?? envHost ?? "127.0.0.1"}:${argv.port ?? defaultPort}/rpc` });
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport Table from \"cli-table3\";\nimport { Command } from \"commander\";\nimport { ProcessManager } from \"./process-manager.ts\";\nimport { createClient } from \"./api/client.ts\";\nimport { globalLogger } from \"./logger.ts\";\n\nconst logger = globalLogger.child(\"cli\");\nconst defaultPort = Number(process.env.PID1_PORT ?? process.env.PORT ?? 3000);\nconst envHost = process.env.PID1_HOST ?? process.env.HOST;\n\nfunction formatTable(rows: Array<Record<string, unknown>>): void {\n if (rows.length === 0) {\n console.log(\"No processes registered\");\n return;\n }\n\n const table = new Table({\n head: [\"ID\", \"NAME\", \"STATUS\", \"PID\", \"RESTARTS\", \"STARTED\"],\n style: { head: [], border: [] },\n });\n\n for (const row of rows) {\n table.push([\n String(row.id),\n String(row.name),\n String(row.status),\n row.pid ? String(row.pid) : \"-\",\n String(row.restarts),\n row.startedAt ? new Date(row.startedAt as string).toLocaleString() : \"-\",\n ]);\n }\n\n console.log(table.toString());\n}\n\ntype CommonArgs = {\n port?: number;\n host?: string;\n};\ntype InitArgs = CommonArgs & {\n cwd?: string;\n config?: string;\n};\n\nasync function runInit(argv: InitArgs) {\n const host = argv.host ?? envHost ?? \"0.0.0.0\";\n const pm = new ProcessManager({\n cwd: argv.cwd,\n configPath: argv.config,\n server: { port: argv.port ?? defaultPort, host },\n });\n pm.serve();\n await pm.init();\n pm.startAll();\n
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport Table from \"cli-table3\";\nimport { Command } from \"commander\";\nimport { ProcessManager } from \"./process-manager.ts\";\nimport { createClient } from \"./api/client.ts\";\nimport { globalLogger } from \"./logger.ts\";\n\nconst logger = globalLogger.child(\"cli\");\nconst defaultPort = Number(process.env.PID1_PORT ?? process.env.PORT ?? 3000);\nconst envHost = process.env.PID1_HOST ?? process.env.HOST;\n\nfunction formatTable(rows: Array<Record<string, unknown>>): void {\n if (rows.length === 0) {\n console.log(\"No processes registered\");\n return;\n }\n\n const table = new Table({\n head: [\"ID\", \"NAME\", \"STATUS\", \"PID\", \"RESTARTS\", \"STARTED\"],\n style: { head: [], border: [] },\n });\n\n for (const row of rows) {\n table.push([\n String(row.id),\n String(row.name),\n String(row.status),\n row.pid ? String(row.pid) : \"-\",\n String(row.restarts),\n row.startedAt ? new Date(row.startedAt as string).toLocaleString() : \"-\",\n ]);\n }\n\n console.log(table.toString());\n}\n\ntype CommonArgs = {\n port?: number;\n host?: string;\n};\ntype InitArgs = CommonArgs & {\n cwd?: string;\n config?: string;\n};\n\nasync function runInit(argv: InitArgs) {\n const host = argv.host ?? envHost ?? \"0.0.0.0\";\n const pm = new ProcessManager({\n cwd: argv.cwd,\n configPath: argv.config,\n server: { port: argv.port ?? defaultPort, host },\n });\n pm.serve();\n await pm.init();\n pm.startAll();\n}\n\nfunction createRpcClient(argv: CommonArgs) {\n const host = argv.host ?? envHost ?? \"127.0.0.1\";\n const port = argv.port ?? defaultPort;\n return createClient({ url: `http://${host}:${port}/rpc` });\n}\n\nasync function main() {\n const program = new Command();\n program\n .name(\"pid1\")\n .description(\"Process manager daemon and controller\")\n .option(\"--port <number>\", \"Port for the HTTP server\", (value) => Number(value))\n .option(\"--host <host>\", \"Host for the HTTP server\");\n\n const commonOptions = (): CommonArgs => {\n const opts = program.opts<CommonArgs>();\n return {\n port: opts.port,\n host: opts.host,\n };\n };\n\n program\n .command(\"init\")\n .description(\"Start the daemon, run tasks, and start all processes\")\n .option(\"--cwd <path>\", \"Working directory for config lookup\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (options: InitArgs) => {\n await runInit({ ...commonOptions(), ...options });\n });\n\n program\n .command(\"start [name]\")\n .description(\"Start all processes, or a specific process by name or id\")\n .action(async (name?: string) => {\n const client = createRpcClient(commonOptions());\n const result = await client.process.start({ name });\n console.log(result.message);\n });\n\n program\n .command(\"stop [name]\")\n .description(\"Stop all processes, or a specific process by name or id\")\n .action(async (name?: string) => {\n const client = createRpcClient(commonOptions());\n const result = await client.process.stop({ name });\n console.log(result.message);\n });\n\n program\n .command(\"restart [name]\")\n .description(\"Restart all processes, or a specific process by name or id\")\n .action(async (name?: string) => {\n const client = createRpcClient(commonOptions());\n const result = await client.process.restart({ name });\n console.log(result.message);\n });\n\n program\n .command(\"delete [name]\")\n .description(\"Delete all processes, or a specific process by name or id\")\n .action(async (name?: string) => {\n const client = createRpcClient(commonOptions());\n const result = await client.process.delete({ name });\n console.log(result.message);\n });\n\n program\n .command(\"list\")\n .description(\"List all managed processes\")\n .action(async () => {\n const client = createRpcClient(commonOptions());\n const processes = await client.process.list({});\n formatTable(processes);\n });\n\n program\n .command(\"get <name>\")\n .description(\"Get a process by name or id\")\n .action(async (name: string) => {\n const client = createRpcClient(commonOptions());\n const process = await client.process.get({ name });\n if (!process) {\n console.log(`Process not found: ${name}`);\n return;\n }\n formatTable([process]);\n });\n\n if (process.argv.length <= 2) {\n program.outputHelp();\n return;\n }\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((error) => {\n if (error.cause?.code === \"ECONNREFUSED\") {\n console.error(\n \"Error: Could not connect to pid1 daemon. Is it running? Start it with: pid1 init\",\n );\n process.exit(1);\n }\n logger.error(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.exit(1);\n});\n"],"mappings":";;;;;;;;AAOA,MAAM,SAAS,aAAa,MAAM,MAAM;AACxC,MAAM,cAAc,OAAO,QAAQ,IAAI,aAAa,QAAQ,IAAI,QAAQ,IAAK;AAC7E,MAAM,UAAU,QAAQ,IAAI,aAAa,QAAQ,IAAI;AAErD,SAAS,YAAY,MAA4C;AAC/D,KAAI,KAAK,WAAW,GAAG;AACrB,UAAQ,IAAI,0BAA0B;AACtC;;CAGF,MAAM,QAAQ,IAAI,MAAM;EACtB,MAAM;GAAC;GAAM;GAAQ;GAAU;GAAO;GAAY;GAAU;EAC5D,OAAO;GAAE,MAAM,EAAE;GAAE,QAAQ,EAAE;GAAE;EAChC,CAAC;AAEF,MAAK,MAAM,OAAO,KAChB,OAAM,KAAK;EACT,OAAO,IAAI,GAAG;EACd,OAAO,IAAI,KAAK;EAChB,OAAO,IAAI,OAAO;EAClB,IAAI,MAAM,OAAO,IAAI,IAAI,GAAG;EAC5B,OAAO,IAAI,SAAS;EACpB,IAAI,YAAY,IAAI,KAAK,IAAI,UAAoB,CAAC,gBAAgB,GAAG;EACtE,CAAC;AAGJ,SAAQ,IAAI,MAAM,UAAU,CAAC;;AAY/B,eAAe,QAAQ,MAAgB;CACrC,MAAM,OAAO,KAAK,QAAQ,WAAW;CACrC,MAAM,KAAK,IAAI,eAAe;EAC5B,KAAK,KAAK;EACV,YAAY,KAAK;EACjB,QAAQ;GAAE,MAAM,KAAK,QAAQ;GAAa;GAAM;EACjD,CAAC;AACF,IAAG,OAAO;AACV,OAAM,GAAG,MAAM;AACf,IAAG,UAAU;;AAGf,SAAS,gBAAgB,MAAkB;AAGzC,QAAO,aAAa,EAAE,KAAK,UAFd,KAAK,QAAQ,WAAW,YAEK,GAD7B,KAAK,QAAQ,YACwB,OAAO,CAAC;;AAG5D,eAAe,OAAO;CACpB,MAAM,UAAU,IAAI,SAAS;AAC7B,SACG,KAAK,OAAO,CACZ,YAAY,wCAAwC,CACpD,OAAO,mBAAmB,6BAA6B,UAAU,OAAO,MAAM,CAAC,CAC/E,OAAO,iBAAiB,2BAA2B;CAEtD,MAAM,sBAAkC;EACtC,MAAM,OAAO,QAAQ,MAAkB;AACvC,SAAO;GACL,MAAM,KAAK;GACX,MAAM,KAAK;GACZ;;AAGH,SACG,QAAQ,OAAO,CACf,YAAY,uDAAuD,CACnE,OAAO,gBAAgB,sCAAsC,CAC7D,OAAO,mBAAmB,sBAAsB,CAChD,OAAO,OAAO,YAAsB;AACnC,QAAM,QAAQ;GAAE,GAAG,eAAe;GAAE,GAAG;GAAS,CAAC;GACjD;AAEJ,SACG,QAAQ,eAAe,CACvB,YAAY,2DAA2D,CACvE,OAAO,OAAO,SAAkB;EAE/B,MAAM,SAAS,MADA,gBAAgB,eAAe,CAAC,CACnB,QAAQ,MAAM,EAAE,MAAM,CAAC;AACnD,UAAQ,IAAI,OAAO,QAAQ;GAC3B;AAEJ,SACG,QAAQ,cAAc,CACtB,YAAY,0DAA0D,CACtE,OAAO,OAAO,SAAkB;EAE/B,MAAM,SAAS,MADA,gBAAgB,eAAe,CAAC,CACnB,QAAQ,KAAK,EAAE,MAAM,CAAC;AAClD,UAAQ,IAAI,OAAO,QAAQ;GAC3B;AAEJ,SACG,QAAQ,iBAAiB,CACzB,YAAY,6DAA6D,CACzE,OAAO,OAAO,SAAkB;EAE/B,MAAM,SAAS,MADA,gBAAgB,eAAe,CAAC,CACnB,QAAQ,QAAQ,EAAE,MAAM,CAAC;AACrD,UAAQ,IAAI,OAAO,QAAQ;GAC3B;AAEJ,SACG,QAAQ,gBAAgB,CACxB,YAAY,4DAA4D,CACxE,OAAO,OAAO,SAAkB;EAE/B,MAAM,SAAS,MADA,gBAAgB,eAAe,CAAC,CACnB,QAAQ,OAAO,EAAE,MAAM,CAAC;AACpD,UAAQ,IAAI,OAAO,QAAQ;GAC3B;AAEJ,SACG,QAAQ,OAAO,CACf,YAAY,6BAA6B,CACzC,OAAO,YAAY;AAGlB,cADkB,MADH,gBAAgB,eAAe,CAAC,CAChB,QAAQ,KAAK,EAAE,CAAC,CACzB;GACtB;AAEJ,SACG,QAAQ,aAAa,CACrB,YAAY,8BAA8B,CAC1C,OAAO,OAAO,SAAiB;EAE9B,MAAM,UAAU,MADD,gBAAgB,eAAe,CAAC,CAClB,QAAQ,IAAI,EAAE,MAAM,CAAC;AAClD,MAAI,CAAC,SAAS;AACZ,WAAQ,IAAI,sBAAsB,OAAO;AACzC;;AAEF,cAAY,CAAC,QAAQ,CAAC;GACtB;AAEJ,KAAI,QAAQ,KAAK,UAAU,GAAG;AAC5B,UAAQ,YAAY;AACpB;;AAGF,OAAM,QAAQ,WAAW,QAAQ,KAAK;;AAGxC,MAAM,CAAC,OAAO,UAAU;AACtB,KAAI,MAAM,OAAO,SAAS,gBAAgB;AACxC,UAAQ,MACN,mFACD;AACD,UAAQ,KAAK,EAAE;;AAEjB,QAAO,MAAM,iBAAiB,QAAS,MAAM,SAAS,MAAM,UAAW,OAAO,MAAM,CAAC;AACrF,SAAQ,KAAK,EAAE;EACf"}
|
|
@@ -130,7 +130,6 @@ declare class ProcessManager {
|
|
|
130
130
|
delete(name: string): Promise<void>;
|
|
131
131
|
deleteAll(): Promise<void>;
|
|
132
132
|
get(name: string): ProcessInfo | undefined;
|
|
133
|
-
wait(): Promise<void>;
|
|
134
133
|
shutdown(): Promise<void>;
|
|
135
134
|
private spawnProcess;
|
|
136
135
|
private runTasks;
|
|
@@ -214,4 +213,4 @@ declare function createClient(options?: ClientOptions): RouterClient<Router>;
|
|
|
214
213
|
type Client = RouterClient<Router>;
|
|
215
214
|
//#endregion
|
|
216
215
|
export { router as a, ProcessInfo as c, ProcessStatus as d, ServerOptions as f, spawnWithLogging as h, Router as i, ProcessManager as l, SpawnedProcess as m, ClientOptions as n, ManagedProcess as o, createServer as p, createClient as r, ProcessConfig as s, Client as t, ProcessManagerOptions as u };
|
|
217
|
-
//# sourceMappingURL=client-
|
|
216
|
+
//# sourceMappingURL=client-B02fC2Sv.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-
|
|
1
|
+
{"version":3,"file":"client-B02fC2Sv.d.mts","names":[],"sources":["../src/exec.ts","../src/api/server.ts","../src/process-manager.ts","../src/api/router.ts","../src/api/client.ts"],"mappings":"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"}
|
package/dist/client.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as ClientOptions, r as createClient, t as Client } from "./client-
|
|
1
|
+
import { n as ClientOptions, r as createClient, t as Client } from "./client-B02fC2Sv.mjs";
|
|
2
2
|
export { Client, ClientOptions, createClient };
|
package/dist/client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../src/api/client.ts"],"sourcesContent":["import type { RouterClient } from \"@orpc/server\";\nimport { createORPCClient } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nimport type { Router } from \"./router.ts\";\n\nexport type ClientOptions = {\n url?: string;\n};\n\nexport function createClient(
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/api/client.ts"],"sourcesContent":["import type { RouterClient } from \"@orpc/server\";\nimport { createORPCClient } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nimport type { Router } from \"./router.ts\";\n\nexport type ClientOptions = {\n url?: string;\n};\n\nexport function createClient(options: ClientOptions = {}): RouterClient<Router> {\n const { url = \"http://127.0.0.1:3000/rpc\" } = options;\n\n const link = new RPCLink({ url });\n\n return createORPCClient(link);\n}\n\nexport type Client = RouterClient<Router>;\n"],"mappings":";;;;AASA,SAAgB,aAAa,UAAyB,EAAE,EAAwB;CAC9E,MAAM,EAAE,MAAM,gCAAgC;AAI9C,QAAO,iBAFM,IAAI,QAAQ,EAAE,KAAK,CAAC,CAEJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-BgRb4pSG.mjs","names":[],"sources":["../src/logger.ts","../src/config.ts"],"sourcesContent":["import { appendFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nconst ANSI_COLORS = {\n GREEN: \"\\x1b[32m\",\n CYAN: \"\\x1b[36m\",\n YELLOW: \"\\x1b[33m\",\n RED: \"\\x1b[31m\",\n RESET: \"\\x1b[0m\",\n BOLD: \"\\x1b[1m\",\n GRAY: \"\\x1b[90m\",\n};\n\nconst COLORS = {\n debug: (text: string) => `${ANSI_COLORS.GREEN}${text}${ANSI_COLORS.RESET}`,\n info: (text: string) => `${ANSI_COLORS.CYAN}${text}${ANSI_COLORS.RESET}`,\n warn: (text: string) => `${ANSI_COLORS.YELLOW}${text}${ANSI_COLORS.RESET}`,\n error: (text: string) => `${ANSI_COLORS.RED}${text}${ANSI_COLORS.RESET}`,\n gray: (text: string) => `${ANSI_COLORS.GRAY}${text}${ANSI_COLORS.RESET}`,\n bold: (text: string) => `${ANSI_COLORS.BOLD}${text}${ANSI_COLORS.RESET}`,\n};\n\nconst LOG_LEVELS = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n} as const;\n\ntype LogLevel = keyof typeof LOG_LEVELS;\n\nfunction formatTime() {\n return Intl.DateTimeFormat(\"en-US\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n timeZone: \"UTC\",\n hourCycle: \"h23\",\n }).format(new Date());\n}\n\nfunction formatLevel(level: LogLevel, color: boolean): string {\n const upper = level.toUpperCase().padStart(5);\n return color ? `${COLORS[level](upper)}` : upper;\n}\n\nexport type LoggerOptions = {\n name: string;\n level?: LogLevel;\n prettyLogFile?: string | null;\n jsonLogFile?: string | null;\n stdout?: boolean;\n};\n\nexport class Logger {\n private name: string;\n private level: number;\n private prettyLogFile?: string | null;\n private jsonLogFile?: string | null;\n private stdout: boolean;\n\n constructor(options: LoggerOptions) {\n this.name = options.name;\n this.level = LOG_LEVELS[options.level ?? \"info\"];\n this.prettyLogFile = options.prettyLogFile;\n this.jsonLogFile = options.jsonLogFile;\n this.stdout = options.stdout ?? true;\n\n if (this.prettyLogFile) {\n mkdirSync(dirname(this.prettyLogFile), { recursive: true });\n }\n if (this.jsonLogFile) {\n mkdirSync(dirname(this.jsonLogFile), { recursive: true });\n }\n }\n\n private log(level: LogLevel, message: string): void {\n if (LOG_LEVELS[level] < this.level) return;\n\n const time = formatTime();\n\n if (this.stdout) {\n const colorLevel = formatLevel(level, true);\n console.log(\n `${COLORS.gray(`[${time}]`)} ${colorLevel} ${COLORS.bold(`(${this.name})`)} ${message}`,\n );\n }\n\n if (this.prettyLogFile) {\n const plainLevel = formatLevel(level, false);\n const line = `[${time}] ${plainLevel} (${this.name}) ${message}\\n`;\n appendFileSync(this.prettyLogFile, line);\n }\n\n if (this.jsonLogFile) {\n const logObj = {\n time: new Date().toISOString(),\n level,\n name: this.name,\n msg: message,\n };\n appendFileSync(this.jsonLogFile, JSON.stringify(logObj) + \"\\n\");\n }\n }\n\n debug(message: string): void {\n this.log(\"debug\", message);\n }\n\n info(message: string): void {\n this.log(\"info\", message);\n }\n\n warn(message: string): void {\n this.log(\"warn\", message);\n }\n\n error(message: string): void {\n this.log(\"error\", message);\n }\n\n child(name: string, options: Partial<LoggerOptions> = {}): Logger {\n const hasPrettyLogFile = \"prettyLogFile\" in options;\n const hasJsonLogFile = \"jsonLogFile\" in options;\n const hasStdout = \"stdout\" in options;\n\n return new Logger({\n name: `${this.name}:${name}`,\n level: options.level ?? (Object.keys(LOG_LEVELS)[this.level] as LogLevel),\n prettyLogFile: hasPrettyLogFile ? options.prettyLogFile : this.prettyLogFile,\n jsonLogFile: hasJsonLogFile ? options.jsonLogFile : this.jsonLogFile,\n stdout: hasStdout ? options.stdout : this.stdout,\n });\n }\n}\n\nmkdirSync(\"logs\", { recursive: true });\nexport const globalLogger = new Logger({\n name: \"pid1\",\n level: \"debug\",\n jsonLogFile: \"logs/pid1.log\",\n});\n\ntype ProcessLoggerParams = {\n name: string;\n logFile: string;\n};\n\nexport function processLogger({ name, logFile }: ProcessLoggerParams): Logger {\n mkdirSync(dirname(logFile), { recursive: true });\n return globalLogger.child(name, {\n prettyLogFile: logFile,\n jsonLogFile: null,\n });\n}\n","import { globSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tsImport } from \"tsx/esm/api\";\nimport * as v from \"valibot\";\nimport { globalLogger } from \"./logger\";\n\nconst logger = globalLogger.child(\"config\");\nconst EnvFileOption = v.optional(v.union([v.string(), v.literal(false)]));\nconst RestartPolicySchema = v.picklist([\"never\", \"always\", \"on-failure\"]);\n\nconst ProcessConfigSchema = v.object({\n name: v.string(),\n command: v.string(),\n args: v.optional(v.array(v.string()), []),\n env: v.optional(v.record(v.string(), v.string()), {}),\n cwd: v.optional(v.string()),\n envFile: EnvFileOption,\n restartOnEnvChange: v.optional(v.boolean(), true),\n restartPolicy: v.optional(RestartPolicySchema, \"on-failure\"),\n maxRestarts: v.optional(v.number(), 10),\n restartMinDelayMs: v.optional(v.number(), 5000),\n restartMaxDelayMs: v.optional(v.number(), 60000),\n restartResetSeconds: v.optional(v.number(), 300),\n});\n\nconst Config = v.object({\n envFile: EnvFileOption,\n restartOnEnvChange: v.optional(v.boolean(), true),\n restartMinDelayMs: v.optional(v.number(), 5000),\n restartMaxDelayMs: v.optional(v.number(), 60000),\n tasks: v.optional(v.array(ProcessConfigSchema), []),\n processes: v.optional(v.array(ProcessConfigSchema), []),\n});\n\ntype ConfigInput = v.InferInput<typeof Config>;\n\nexport type LoadedConfig = v.InferOutput<typeof Config> & {\n configPath: string;\n};\n\nexport async function loadConfigFile(\n path?: string,\n cwd?: string,\n): Promise<LoadedConfig> {\n const configFile = path ?? findConfigFile(cwd ?? process.cwd());\n if (!configFile) {\n logger.error(\n `Failed to find a config file in ${cwd ?? process.cwd()} or any of its parent directories`,\n );\n logger.error(\"Exiting...\");\n process.exit(1);\n }\n\n logger.info(`Trying to load config from ${configFile}`);\n const config = await tsImport(configFile, {\n parentURL: import.meta.url,\n })\n .then((m) =>\n \"default\" in m.default ? m.default.default : m.default,\n )\n .catch((error) => {\n logger.error(`Failed to load config from ${configFile}`);\n logger.error(error instanceof Error ? error.stack ?? error.message : String(error));\n logger.error(\"Exiting...\");\n process.exit(1);\n });\n\n const res = await v.safeParseAsync(Config, config);\n if (!res.success) {\n logger.error(`Config validation failed`);\n logger.error(v.summarize(res.issues));\n logger.error(\"Exiting...\");\n process.exit(1);\n }\n\n return { ...res.output, configPath: configFile };\n}\n\nexport function findConfigFile(cwd: string) {\n const files = globSync(\"pid1.config.{ts,js,mts,mjs}\", { cwd });\n if (files.length === 0 && join(cwd) !== \"/\") {\n return findConfigFile(join(cwd, \"..\"));\n }\n return files[0] ? join(cwd, files[0]) : null;\n}\n\nexport function defineConfig(config: ConfigInput) {\n return config;\n}\n"],"mappings":";;;;;;AAGA,MAAM,cAAc;CAClB,OAAO;CACP,MAAM;CACN,QAAQ;CACR,KAAK;CACL,OAAO;CACP,MAAM;CACN,MAAM;CACP;AAED,MAAM,SAAS;CACb,QAAQ,SAAiB,GAAG,YAAY,QAAQ,OAAO,YAAY;CACnE,OAAO,SAAiB,GAAG,YAAY,OAAO,OAAO,YAAY;CACjE,OAAO,SAAiB,GAAG,YAAY,SAAS,OAAO,YAAY;CACnE,QAAQ,SAAiB,GAAG,YAAY,MAAM,OAAO,YAAY;CACjE,OAAO,SAAiB,GAAG,YAAY,OAAO,OAAO,YAAY;CACjE,OAAO,SAAiB,GAAG,YAAY,OAAO,OAAO,YAAY;CAClE;AAED,MAAM,aAAa;CACjB,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR;AAID,SAAS,aAAa;AACpB,QAAO,KAAK,eAAe,SAAS;EAClC,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,UAAU;EACV,WAAW;EACZ,CAAC,CAAC,uBAAO,IAAI,MAAM,CAAC;;AAGvB,SAAS,YAAY,OAAiB,OAAwB;CAC5D,MAAM,QAAQ,MAAM,aAAa,CAAC,SAAS,EAAE;AAC7C,QAAO,QAAQ,GAAG,OAAO,OAAO,MAAM,KAAK;;AAW7C,IAAa,SAAb,MAAa,OAAO;CAClB,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,SAAwB;AAClC,OAAK,OAAO,QAAQ;AACpB,OAAK,QAAQ,WAAW,QAAQ,SAAS;AACzC,OAAK,gBAAgB,QAAQ;AAC7B,OAAK,cAAc,QAAQ;AAC3B,OAAK,SAAS,QAAQ,UAAU;AAEhC,MAAI,KAAK,cACP,WAAU,QAAQ,KAAK,cAAc,EAAE,EAAE,WAAW,MAAM,CAAC;AAE7D,MAAI,KAAK,YACP,WAAU,QAAQ,KAAK,YAAY,EAAE,EAAE,WAAW,MAAM,CAAC;;CAI7D,AAAQ,IAAI,OAAiB,SAAuB;AAClD,MAAI,WAAW,SAAS,KAAK,MAAO;EAEpC,MAAM,OAAO,YAAY;AAEzB,MAAI,KAAK,QAAQ;GACf,MAAM,aAAa,YAAY,OAAO,KAAK;AAC3C,WAAQ,IACN,GAAG,OAAO,KAAK,IAAI,KAAK,GAAG,CAAC,GAAG,WAAW,GAAG,OAAO,KAAK,IAAI,KAAK,KAAK,GAAG,CAAC,IAAI,UAChF;;AAGH,MAAI,KAAK,eAAe;GAEtB,MAAM,OAAO,IAAI,KAAK,IADH,YAAY,OAAO,MAAM,CACP,IAAI,KAAK,KAAK,KAAK,QAAQ;AAChE,kBAAe,KAAK,eAAe,KAAK;;AAG1C,MAAI,KAAK,aAAa;GACpB,MAAM,SAAS;IACb,uBAAM,IAAI,MAAM,EAAC,aAAa;IAC9B;IACA,MAAM,KAAK;IACX,KAAK;IACN;AACD,kBAAe,KAAK,aAAa,KAAK,UAAU,OAAO,GAAG,KAAK;;;CAInE,MAAM,SAAuB;AAC3B,OAAK,IAAI,SAAS,QAAQ;;CAG5B,KAAK,SAAuB;AAC1B,OAAK,IAAI,QAAQ,QAAQ;;CAG3B,KAAK,SAAuB;AAC1B,OAAK,IAAI,QAAQ,QAAQ;;CAG3B,MAAM,SAAuB;AAC3B,OAAK,IAAI,SAAS,QAAQ;;CAG5B,MAAM,MAAc,UAAkC,EAAE,EAAU;EAChE,MAAM,mBAAmB,mBAAmB;EAC5C,MAAM,iBAAiB,iBAAiB;EACxC,MAAM,YAAY,YAAY;AAE9B,SAAO,IAAI,OAAO;GAChB,MAAM,GAAG,KAAK,KAAK,GAAG;GACtB,OAAO,QAAQ,SAAU,OAAO,KAAK,WAAW,CAAC,KAAK;GACtD,eAAe,mBAAmB,QAAQ,gBAAgB,KAAK;GAC/D,aAAa,iBAAiB,QAAQ,cAAc,KAAK;GACzD,QAAQ,YAAY,QAAQ,SAAS,KAAK;GAC3C,CAAC;;;AAIN,UAAU,QAAQ,EAAE,WAAW,MAAM,CAAC;AACtC,MAAa,eAAe,IAAI,OAAO;CACrC,MAAM;CACN,OAAO;CACP,aAAa;CACd,CAAC;AAOF,SAAgB,cAAc,EAAE,MAAM,WAAwC;AAC5E,WAAU,QAAQ,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;AAChD,QAAO,aAAa,MAAM,MAAM;EAC9B,eAAe;EACf,aAAa;EACd,CAAC;;;;;ACnJJ,MAAM,SAAS,aAAa,MAAM,SAAS;AAC3C,MAAM,gBAAgB,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC;AACzE,MAAM,sBAAsB,EAAE,SAAS;CAAC;CAAS;CAAU;CAAa,CAAC;AAEzE,MAAM,sBAAsB,EAAE,OAAO;CACnC,MAAM,EAAE,QAAQ;CAChB,SAAS,EAAE,QAAQ;CACnB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC;CACzC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC;CACrD,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC3B,SAAS;CACT,oBAAoB,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK;CACjD,eAAe,EAAE,SAAS,qBAAqB,aAAa;CAC5D,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG;CACvC,mBAAmB,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAK;CAC/C,mBAAmB,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAM;CAChD,qBAAqB,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI;CACjD,CAAC;AAEF,MAAM,SAAS,EAAE,OAAO;CACtB,SAAS;CACT,oBAAoB,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK;CACjD,mBAAmB,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAK;CAC/C,mBAAmB,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAM;CAChD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,EAAE,EAAE,CAAC;CACnD,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,EAAE,EAAE,CAAC;CACxD,CAAC;AAQF,eAAsB,eACpB,MACA,KACuB;CACvB,MAAM,aAAa,QAAQ,eAAe,OAAO,QAAQ,KAAK,CAAC;AAC/D,KAAI,CAAC,YAAY;AACjB,SAAO,MACH,mCAAmC,OAAO,QAAQ,KAAK,CAAC,mCACzD;AACH,SAAO,MAAM,aAAa;AACxB,UAAQ,KAAK,EAAE;;AAGjB,QAAO,KAAK,8BAA8B,aAAa;CACvD,MAAM,SAAS,MAAM,SAAS,YAAY,EACxC,WAAW,OAAO,KAAK,KACxB,CAAC,CACC,MAAM,MACL,aAAa,EAAE,UAAU,EAAE,QAAQ,UAAU,EAAE,QAChD,CACA,OAAO,UAAU;AAChB,SAAO,MAAM,8BAA8B,aAAa;AACxD,SAAO,MAAM,iBAAiB,QAAQ,MAAM,SAAS,MAAM,UAAU,OAAO,MAAM,CAAC;AACnF,SAAO,MAAM,aAAa;AAC1B,UAAQ,KAAK,EAAE;GACf;CAEJ,MAAM,MAAM,MAAM,EAAE,eAAe,QAAQ,OAAO;AAClD,KAAI,CAAC,IAAI,SAAS;AAChB,SAAO,MAAM,2BAA2B;AACxC,SAAO,MAAM,EAAE,UAAU,IAAI,OAAO,CAAC;AACrC,SAAO,MAAM,aAAa;AAC1B,UAAQ,KAAK,EAAE;;AAGjB,QAAO;EAAE,GAAG,IAAI;EAAQ,YAAY;EAAY;;AAGlD,SAAgB,eAAe,KAAa;CAC1C,MAAM,QAAQ,SAAS,+BAA+B,EAAE,KAAK,CAAC;AAC9D,KAAI,MAAM,WAAW,KAAK,KAAK,IAAI,KAAK,IACtC,QAAO,eAAe,KAAK,KAAK,KAAK,CAAC;AAExC,QAAO,MAAM,KAAK,KAAK,KAAK,MAAM,GAAG,GAAG;;AAG1C,SAAgB,aAAa,QAAqB;AAChD,QAAO"}
|
|
1
|
+
{"version":3,"file":"config-BgRb4pSG.mjs","names":[],"sources":["../src/logger.ts","../src/config.ts"],"sourcesContent":["import { appendFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nconst ANSI_COLORS = {\n GREEN: \"\\x1b[32m\",\n CYAN: \"\\x1b[36m\",\n YELLOW: \"\\x1b[33m\",\n RED: \"\\x1b[31m\",\n RESET: \"\\x1b[0m\",\n BOLD: \"\\x1b[1m\",\n GRAY: \"\\x1b[90m\",\n};\n\nconst COLORS = {\n debug: (text: string) => `${ANSI_COLORS.GREEN}${text}${ANSI_COLORS.RESET}`,\n info: (text: string) => `${ANSI_COLORS.CYAN}${text}${ANSI_COLORS.RESET}`,\n warn: (text: string) => `${ANSI_COLORS.YELLOW}${text}${ANSI_COLORS.RESET}`,\n error: (text: string) => `${ANSI_COLORS.RED}${text}${ANSI_COLORS.RESET}`,\n gray: (text: string) => `${ANSI_COLORS.GRAY}${text}${ANSI_COLORS.RESET}`,\n bold: (text: string) => `${ANSI_COLORS.BOLD}${text}${ANSI_COLORS.RESET}`,\n};\n\nconst LOG_LEVELS = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n} as const;\n\ntype LogLevel = keyof typeof LOG_LEVELS;\n\nfunction formatTime() {\n return Intl.DateTimeFormat(\"en-US\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n timeZone: \"UTC\",\n hourCycle: \"h23\",\n }).format(new Date());\n}\n\nfunction formatLevel(level: LogLevel, color: boolean): string {\n const upper = level.toUpperCase().padStart(5);\n return color ? `${COLORS[level](upper)}` : upper;\n}\n\nexport type LoggerOptions = {\n name: string;\n level?: LogLevel;\n prettyLogFile?: string | null;\n jsonLogFile?: string | null;\n stdout?: boolean;\n};\n\nexport class Logger {\n private name: string;\n private level: number;\n private prettyLogFile?: string | null;\n private jsonLogFile?: string | null;\n private stdout: boolean;\n\n constructor(options: LoggerOptions) {\n this.name = options.name;\n this.level = LOG_LEVELS[options.level ?? \"info\"];\n this.prettyLogFile = options.prettyLogFile;\n this.jsonLogFile = options.jsonLogFile;\n this.stdout = options.stdout ?? true;\n\n if (this.prettyLogFile) {\n mkdirSync(dirname(this.prettyLogFile), { recursive: true });\n }\n if (this.jsonLogFile) {\n mkdirSync(dirname(this.jsonLogFile), { recursive: true });\n }\n }\n\n private log(level: LogLevel, message: string): void {\n if (LOG_LEVELS[level] < this.level) return;\n\n const time = formatTime();\n\n if (this.stdout) {\n const colorLevel = formatLevel(level, true);\n console.log(\n `${COLORS.gray(`[${time}]`)} ${colorLevel} ${COLORS.bold(`(${this.name})`)} ${message}`,\n );\n }\n\n if (this.prettyLogFile) {\n const plainLevel = formatLevel(level, false);\n const line = `[${time}] ${plainLevel} (${this.name}) ${message}\\n`;\n appendFileSync(this.prettyLogFile, line);\n }\n\n if (this.jsonLogFile) {\n const logObj = {\n time: new Date().toISOString(),\n level,\n name: this.name,\n msg: message,\n };\n appendFileSync(this.jsonLogFile, JSON.stringify(logObj) + \"\\n\");\n }\n }\n\n debug(message: string): void {\n this.log(\"debug\", message);\n }\n\n info(message: string): void {\n this.log(\"info\", message);\n }\n\n warn(message: string): void {\n this.log(\"warn\", message);\n }\n\n error(message: string): void {\n this.log(\"error\", message);\n }\n\n child(name: string, options: Partial<LoggerOptions> = {}): Logger {\n const hasPrettyLogFile = \"prettyLogFile\" in options;\n const hasJsonLogFile = \"jsonLogFile\" in options;\n const hasStdout = \"stdout\" in options;\n\n return new Logger({\n name: `${this.name}:${name}`,\n level: options.level ?? (Object.keys(LOG_LEVELS)[this.level] as LogLevel),\n prettyLogFile: hasPrettyLogFile ? options.prettyLogFile : this.prettyLogFile,\n jsonLogFile: hasJsonLogFile ? options.jsonLogFile : this.jsonLogFile,\n stdout: hasStdout ? options.stdout : this.stdout,\n });\n }\n}\n\nmkdirSync(\"logs\", { recursive: true });\nexport const globalLogger = new Logger({\n name: \"pid1\",\n level: \"debug\",\n jsonLogFile: \"logs/pid1.log\",\n});\n\ntype ProcessLoggerParams = {\n name: string;\n logFile: string;\n};\n\nexport function processLogger({ name, logFile }: ProcessLoggerParams): Logger {\n mkdirSync(dirname(logFile), { recursive: true });\n return globalLogger.child(name, {\n prettyLogFile: logFile,\n jsonLogFile: null,\n });\n}\n","import { globSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tsImport } from \"tsx/esm/api\";\nimport * as v from \"valibot\";\nimport { globalLogger } from \"./logger\";\n\nconst logger = globalLogger.child(\"config\");\nconst EnvFileOption = v.optional(v.union([v.string(), v.literal(false)]));\nconst RestartPolicySchema = v.picklist([\"never\", \"always\", \"on-failure\"]);\n\nconst ProcessConfigSchema = v.object({\n name: v.string(),\n command: v.string(),\n args: v.optional(v.array(v.string()), []),\n env: v.optional(v.record(v.string(), v.string()), {}),\n cwd: v.optional(v.string()),\n envFile: EnvFileOption,\n restartOnEnvChange: v.optional(v.boolean(), true),\n restartPolicy: v.optional(RestartPolicySchema, \"on-failure\"),\n maxRestarts: v.optional(v.number(), 10),\n restartMinDelayMs: v.optional(v.number(), 5000),\n restartMaxDelayMs: v.optional(v.number(), 60000),\n restartResetSeconds: v.optional(v.number(), 300),\n});\n\nconst Config = v.object({\n envFile: EnvFileOption,\n restartOnEnvChange: v.optional(v.boolean(), true),\n restartMinDelayMs: v.optional(v.number(), 5000),\n restartMaxDelayMs: v.optional(v.number(), 60000),\n tasks: v.optional(v.array(ProcessConfigSchema), []),\n processes: v.optional(v.array(ProcessConfigSchema), []),\n});\n\ntype ConfigInput = v.InferInput<typeof Config>;\n\nexport type LoadedConfig = v.InferOutput<typeof Config> & {\n configPath: string;\n};\n\nexport async function loadConfigFile(path?: string, cwd?: string): Promise<LoadedConfig> {\n const configFile = path ?? findConfigFile(cwd ?? process.cwd());\n if (!configFile) {\n logger.error(\n `Failed to find a config file in ${cwd ?? process.cwd()} or any of its parent directories`,\n );\n logger.error(\"Exiting...\");\n process.exit(1);\n }\n\n logger.info(`Trying to load config from ${configFile}`);\n const config = await tsImport(configFile, {\n parentURL: import.meta.url,\n })\n .then((m) => (\"default\" in m.default ? m.default.default : m.default))\n .catch((error) => {\n logger.error(`Failed to load config from ${configFile}`);\n logger.error(error instanceof Error ? (error.stack ?? error.message) : String(error));\n logger.error(\"Exiting...\");\n process.exit(1);\n });\n\n const res = await v.safeParseAsync(Config, config);\n if (!res.success) {\n logger.error(`Config validation failed`);\n logger.error(v.summarize(res.issues));\n logger.error(\"Exiting...\");\n process.exit(1);\n }\n\n return { ...res.output, configPath: configFile };\n}\n\nexport function findConfigFile(cwd: string) {\n const files = globSync(\"pid1.config.{ts,js,mts,mjs}\", { cwd });\n if (files.length === 0 && join(cwd) !== \"/\") {\n return findConfigFile(join(cwd, \"..\"));\n }\n return files[0] ? join(cwd, files[0]) : null;\n}\n\nexport function defineConfig(config: ConfigInput) {\n return config;\n}\n"],"mappings":"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"}
|
package/dist/config.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":";;;cAyBM,MAAA,EAAM,CAAA,CAAA,YAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KASP,WAAA,GAAc,CAAA,CAAE,UAAA,QAAkB,MAAA;AAAA,KAE3B,YAAA,GAAe,CAAA,CAAE,WAAA,QAAmB,MAAA;EAC9C,UAAA;AAAA;AAAA,iBAGoB,cAAA,
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":";;;cAyBM,MAAA,EAAM,CAAA,CAAA,YAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KASP,WAAA,GAAc,CAAA,CAAE,UAAA,QAAkB,MAAA;AAAA,KAE3B,YAAA,GAAe,CAAA,CAAE,WAAA,QAAmB,MAAA;EAC9C,UAAA;AAAA;AAAA,iBAGoB,cAAA,CAAe,IAAA,WAAe,GAAA,YAAe,OAAA,CAAQ,YAAA;AAAA,iBAiC3D,cAAA,CAAe,GAAA;AAAA,iBAQf,YAAA,CAAa,MAAA,EAAQ,WAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as router, c as ProcessInfo, d as ProcessStatus, f as ServerOptions, h as spawnWithLogging, i as Router, l as ProcessManager, m as SpawnedProcess, n as ClientOptions, o as ManagedProcess, p as createServer, r as createClient, s as ProcessConfig, t as Client, u as ProcessManagerOptions } from "./client-
|
|
1
|
+
import { a as router, c as ProcessInfo, d as ProcessStatus, f as ServerOptions, h as spawnWithLogging, i as Router, l as ProcessManager, m as SpawnedProcess, n as ClientOptions, o as ManagedProcess, p as createServer, r as createClient, s as ProcessConfig, t as Client, u as ProcessManagerOptions } from "./client-B02fC2Sv.mjs";
|
|
2
2
|
import { defineConfig, loadConfigFile } from "./config.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/logger.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as globalLogger, r as loadConfigFile, t as defineConfig } from "./config-BgRb4pSG.mjs";
|
|
2
|
-
import { i as spawnWithLogging, n as createServer, r as router, t as ProcessManager } from "./process-manager-
|
|
2
|
+
import { i as spawnWithLogging, n as createServer, r as router, t as ProcessManager } from "./process-manager-Ddk5dALx.mjs";
|
|
3
3
|
import { createClient } from "./client.mjs";
|
|
4
4
|
|
|
5
5
|
export { ProcessManager, createClient, createServer, defineConfig, globalLogger, loadConfigFile, router, spawnWithLogging };
|
|
@@ -236,7 +236,6 @@ function createServer(pm, options = {}) {
|
|
|
236
236
|
hostname: host
|
|
237
237
|
});
|
|
238
238
|
logger$2.info(`HTTP server listening on http://${host}:${port}`);
|
|
239
|
-
logger$2.info(`RPC endpoint: http://${host}:${port}/rpc`);
|
|
240
239
|
return server;
|
|
241
240
|
}
|
|
242
241
|
};
|
|
@@ -413,11 +412,6 @@ var ProcessManager = class {
|
|
|
413
412
|
args: resolved.managed.config.args
|
|
414
413
|
};
|
|
415
414
|
}
|
|
416
|
-
async wait() {
|
|
417
|
-
const donePromises = [...this.processes.values()].filter((m) => m.spawned).map((m) => m.spawned.done);
|
|
418
|
-
await Promise.allSettled(donePromises);
|
|
419
|
-
this.logger.info("All processes exited");
|
|
420
|
-
}
|
|
421
415
|
async shutdown() {
|
|
422
416
|
await this.stopActiveTask();
|
|
423
417
|
await this.stopAll();
|
|
@@ -640,4 +634,4 @@ var ProcessManager = class {
|
|
|
640
634
|
|
|
641
635
|
//#endregion
|
|
642
636
|
export { spawnWithLogging as i, createServer as n, router as r, ProcessManager as t };
|
|
643
|
-
//# sourceMappingURL=process-manager-
|
|
637
|
+
//# sourceMappingURL=process-manager-Ddk5dALx.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-manager-Ddk5dALx.mjs","names":["logger","logger","honoLogger"],"sources":["../src/env.ts","../src/exec.ts","../src/api/router.ts","../src/api/server.ts","../src/process-manager.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { parse } from \"dotenv\";\nimport { globalLogger } from \"./logger\";\n\nexport type EnvFileConfig = {\n envFile?: string | false;\n};\n\nconst logger = globalLogger.child(\"env\");\n\nfunction loadEnvFile(path: string): Record<string, string> {\n if (!existsSync(path)) {\n return {};\n }\n\n try {\n const content = readFileSync(path, \"utf-8\");\n const parsed = parse(content);\n logger.debug(`Loaded env file: ${path}`);\n return parsed;\n } catch (error) {\n logger.warn(`Failed to parse env file ${path}: ${error}`);\n return {};\n }\n}\n\nexport type LoadEnvOptions = {\n configDir: string;\n processName: string;\n globalEnvFile?: string | false;\n processEnvFile?: string | false;\n configEnv?: Record<string, string>;\n};\n\nexport function loadEnvForProcess(options: LoadEnvOptions): Record<string, string> {\n const { configDir, processName, globalEnvFile, processEnvFile, configEnv = {} } = options;\n\n const baseEnv = Object.fromEntries(\n Object.entries(process.env).filter(([, value]) => value !== undefined),\n ) as Record<string, string>;\n\n let globalEnv: Record<string, string> = {};\n if (globalEnvFile !== false) {\n const globalPath = globalEnvFile ?? join(configDir, \".env\");\n globalEnv = loadEnvFile(globalPath);\n }\n\n let processEnv: Record<string, string> = {};\n if (processEnvFile !== false) {\n const processPath = processEnvFile ?? join(configDir, `.env.${processName}`);\n processEnv = loadEnvFile(processPath);\n }\n\n return {\n ...baseEnv,\n ...globalEnv,\n ...configEnv,\n ...processEnv,\n };\n}\n\nexport function getConfigDir(configPath: string): string {\n return dirname(configPath);\n}\n","import { processLogger } from \"./logger\";\nimport { x } from \"tinyexec\";\n\ntype ExecType = \"task\" | \"process\";\n\ntype ExecWithLoggingParams = {\n name: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n cwd: string;\n logFile: string;\n abortSignal?: AbortSignal;\n type: ExecType;\n};\n\ntype TinyExecProcess = ReturnType<typeof x>;\n\nexport type SpawnedProcess = {\n proc: TinyExecProcess;\n done: Promise<number | null>;\n kill: (signal?: NodeJS.Signals) => boolean;\n};\n\nexport function spawnWithLogging({\n name,\n command,\n args,\n env,\n cwd,\n logFile,\n abortSignal,\n type,\n}: ExecWithLoggingParams): SpawnedProcess {\n const proc = x(command, args, {\n nodeOptions: { cwd, env },\n signal: abortSignal,\n throwOnError: true,\n });\n\n const kill = (signal: NodeJS.Signals = \"SIGTERM\"): boolean => {\n if (proc.pid) {\n try {\n process.kill(proc.pid, signal);\n return true;\n } catch {\n return false;\n }\n }\n return false;\n };\n\n const done = (async () => {\n const logger = processLogger({ name: `${type}:${name}`, logFile });\n\n logger.info(`Starting: command=${command}, args=[${args.join(\", \")}], cwd=${cwd}`);\n\n for await (const line of proc) {\n logger.info(line);\n }\n\n await proc;\n\n const exitCode = proc.exitCode;\n\n if (proc.aborted || proc.killed || exitCode === null || exitCode === undefined) {\n logger.info(`${name} was terminated`);\n } else if (exitCode === 0) {\n logger.info(`${name} completed successfully`);\n } else {\n logger.info(`${name} exited with code ${exitCode}`);\n }\n\n return proc.exitCode ?? null;\n })();\n\n return { proc, done, kill };\n}\n","import { os, ORPCError } from \"@orpc/server\";\nimport * as v from \"valibot\";\nimport type { ProcessManager, ProcessInfo } from \"../process-manager.ts\";\n\nconst ProcessInfoSchema = v.object({\n id: v.number(),\n name: v.string(),\n status: v.picklist([\"stopped\", \"running\", \"errored\"]),\n pid: v.optional(v.number()),\n exitCode: v.nullable(v.number()),\n restarts: v.number(),\n startedAt: v.nullable(v.date()),\n command: v.string(),\n args: v.array(v.string()),\n});\n\nconst ProcessNameSchema = v.object({\n name: v.string(),\n});\n\ntype RouterContext = {\n pm: ProcessManager;\n};\n\nconst base = os.$context<RouterContext>();\n\nexport const list = base.output(v.array(ProcessInfoSchema)).handler(async ({ context }) => {\n return context.pm.list();\n});\n\nexport const get = base\n .input(ProcessNameSchema)\n .output(v.nullable(ProcessInfoSchema))\n .handler(async ({ input, context }) => {\n return context.pm.get(input.name) ?? null;\n });\n\nexport const start = base\n .input(\n v.object({\n name: v.optional(v.string()),\n }),\n )\n .output(v.object({ success: v.boolean(), message: v.string() }))\n .handler(async ({ input, context }) => {\n try {\n if (input.name) {\n context.pm.start(input.name);\n return { success: true, message: `Started process: ${input.name}` };\n } else {\n context.pm.startAll();\n return { success: true, message: \"Started all processes\" };\n }\n } catch (error) {\n throw new ORPCError(\"BAD_REQUEST\", {\n message: error instanceof Error ? error.message : \"Failed to start\",\n });\n }\n });\n\nexport const stop = base\n .input(\n v.object({\n name: v.optional(v.string()),\n }),\n )\n .output(v.object({ success: v.boolean(), message: v.string() }))\n .handler(async ({ input, context }) => {\n try {\n if (input.name) {\n await context.pm.stop(input.name);\n return { success: true, message: `Stopped process: ${input.name}` };\n } else {\n await context.pm.stopAll();\n return { success: true, message: \"Stopped all processes\" };\n }\n } catch (error) {\n throw new ORPCError(\"BAD_REQUEST\", {\n message: error instanceof Error ? error.message : \"Failed to stop\",\n });\n }\n });\n\nexport const restart = base\n .input(\n v.object({\n name: v.optional(v.string()),\n }),\n )\n .output(v.object({ success: v.boolean(), message: v.string() }))\n .handler(async ({ input, context }) => {\n try {\n if (input.name) {\n await context.pm.restart(input.name);\n return { success: true, message: `Restarted process: ${input.name}` };\n } else {\n await context.pm.restartAll();\n return { success: true, message: \"Restarted all processes\" };\n }\n } catch (error) {\n throw new ORPCError(\"BAD_REQUEST\", {\n message: error instanceof Error ? error.message : \"Failed to restart\",\n });\n }\n });\n\nexport const deleteProcess = base\n .input(\n v.object({\n name: v.optional(v.string()),\n }),\n )\n .output(v.object({ success: v.boolean(), message: v.string() }))\n .handler(async ({ input, context }) => {\n try {\n if (input.name) {\n await context.pm.delete(input.name);\n return { success: true, message: `Deleted process: ${input.name}` };\n } else {\n await context.pm.deleteAll();\n return { success: true, message: \"Deleted all processes\" };\n }\n } catch (error) {\n throw new ORPCError(\"BAD_REQUEST\", {\n message: error instanceof Error ? error.message : \"Failed to delete\",\n });\n }\n });\n\nexport const health = os.output(v.object({ status: v.literal(\"ok\") })).handler(async () => {\n return { status: \"ok\" as const };\n});\n\nexport const router = {\n health,\n process: {\n list,\n get,\n start,\n stop,\n restart,\n delete: deleteProcess,\n },\n};\n\nexport type Router = typeof router;\n","import { Hono } from \"hono\";\nimport { serve } from \"@hono/node-server\";\nimport { RPCHandler } from \"@orpc/server/fetch\";\nimport { onError } from \"@orpc/server\";\nimport { router } from \"./router.ts\";\nimport type { ProcessManager } from \"../process-manager.ts\";\nimport { globalLogger } from \"../logger.ts\";\nimport { logger as honoLogger } from \"hono/logger\";\n\nexport type ServerOptions = {\n port?: number;\n host?: string;\n};\n\nexport function createServer(pm: ProcessManager, options: ServerOptions = {}) {\n const { port = 3000, host = \"127.0.0.1\" } = options;\n const logger = globalLogger.child(\"server\");\n\n const app = new Hono();\n\n const handler = new RPCHandler(router, {\n interceptors: [\n onError((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(`RPC Error: ${message}`);\n }),\n ],\n });\n\n app.use(honoLogger((msg, ...args) => logger.debug(`${msg} ${args.join(\" \")}`)));\n\n app.get(\"/health\", (c) => c.json({ status: \"ok\" }));\n\n app.use(\"/rpc/*\", async (c, next) => {\n const { matched, response } = await handler.handle(c.req.raw, {\n prefix: \"/rpc\",\n context: { pm },\n });\n\n if (matched) {\n return c.newResponse(response.body, response);\n }\n\n await next();\n });\n\n app.notFound((c) => c.json({ error: \"Not found\" }, 404));\n\n return {\n app,\n start: () => {\n const server = serve({\n fetch: app.fetch,\n port,\n hostname: host,\n });\n\n logger.info(`HTTP server listening on http://${host}:${port}`);\n return server;\n },\n };\n}\n\nexport type { Router } from \"./router.ts\";\n","import { dirname, join } from \"node:path\";\nimport { watch, type FSWatcher } from \"node:fs\";\nimport { loadConfigFile } from \"./config.ts\";\nimport { loadEnvForProcess } from \"./env.ts\";\nimport { spawnWithLogging, type SpawnedProcess } from \"./exec.ts\";\nimport { globalLogger } from \"./logger.ts\";\nimport { createServer, type ServerOptions } from \"./api/server.ts\";\n\nexport type ProcessManagerOptions = {\n cwd?: string;\n configPath?: string;\n server?: ServerOptions & { enabled?: boolean };\n};\n\nexport type ProcessStatus = \"stopped\" | \"running\" | \"errored\";\nexport type RestartPolicy = \"never\" | \"always\" | \"on-failure\";\n\nexport type ProcessConfig = {\n name: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n cwd?: string;\n envFile?: string | false;\n restartOnEnvChange?: boolean;\n restartPolicy?: RestartPolicy;\n maxRestarts?: number;\n restartMinDelayMs?: number;\n restartMaxDelayMs?: number;\n restartResetSeconds?: number;\n};\n\nexport type ManagedProcess = {\n id: number;\n config: ProcessConfig;\n status: ProcessStatus;\n spawned: SpawnedProcess | null;\n exitCode: number | null;\n restarts: number;\n restartAttempts: number;\n restartTimer: NodeJS.Timeout | null;\n stopRequested: boolean;\n startedAt: Date | null;\n};\n\nconst DEFAULT_STOP_TIMEOUT_MS = 10000;\nconst DEFAULT_RESTART_RESET_SECONDS = 300;\nconst DEFAULT_RESTART_MAX = 10;\nconst DEFAULT_RESTART_MIN_DELAY_MS = 5000;\nconst DEFAULT_RESTART_MAX_DELAY_MS = 60000;\n\nexport type ProcessInfo = {\n id: number;\n name: string;\n status: ProcessStatus;\n pid: number | undefined;\n exitCode: number | null;\n restarts: number;\n startedAt: Date | null;\n command: string;\n args: string[];\n};\n\nexport class ProcessManager {\n private logger = globalLogger.child(\"pm\");\n private cwd: string;\n private configPath?: string;\n private configDir?: string;\n private globalEnvFile?: string | false;\n private processes: Map<string, ManagedProcess>;\n private processIds: Map<number, string>;\n private nextProcessId = 0;\n private signalHandlersSetup = false;\n private serverOptions?: ServerOptions & { enabled?: boolean };\n private httpServer?: ReturnType<ReturnType<typeof createServer>[\"start\"]>;\n private envWatcher: FSWatcher | null = null;\n private envChangeTimers = new Map<string, NodeJS.Timeout>();\n private envRestartTimers = new Map<string, NodeJS.Timeout>();\n private restartOnEnvChangeGlobal = true;\n private restartMinDelayMsGlobal = DEFAULT_RESTART_MIN_DELAY_MS;\n private restartMaxDelayMsGlobal = DEFAULT_RESTART_MAX_DELAY_MS;\n private currentTask: SpawnedProcess | null = null;\n private currentTaskName: string | null = null;\n private taskAbortController: AbortController | null = null;\n\n constructor(options: ProcessManagerOptions = {}) {\n this.cwd = options.cwd ?? process.cwd();\n this.configPath = options.configPath;\n this.processes = new Map();\n this.processIds = new Map();\n this.serverOptions = options.server;\n }\n\n async init(): Promise<void> {\n const config = await loadConfigFile(this.configPath, this.cwd);\n\n this.configDir = dirname(config.configPath);\n this.globalEnvFile = config.envFile;\n this.restartOnEnvChangeGlobal = config.restartOnEnvChange ?? true;\n this.restartMinDelayMsGlobal = config.restartMinDelayMs ?? DEFAULT_RESTART_MIN_DELAY_MS;\n this.restartMaxDelayMsGlobal = config.restartMaxDelayMs ?? DEFAULT_RESTART_MAX_DELAY_MS;\n\n this.logger.info(\n `Executing ${config.tasks.length} tasks and registering ${config.processes.length} processes`,\n );\n\n this.setupSignalHandlers();\n this.setupEnvWatchers();\n await this.runTasks(config.tasks);\n\n for (const p of config.processes) {\n this.register(p);\n }\n }\n\n register(config: ProcessConfig): void {\n if (this.processes.has(config.name)) {\n throw new Error(`Process ${config.name} already registered`);\n }\n\n const id = this.nextProcessId++;\n const managed: ManagedProcess = {\n id,\n config,\n status: \"stopped\",\n spawned: null,\n exitCode: null,\n restarts: 0,\n restartAttempts: 0,\n restartTimer: null,\n stopRequested: false,\n startedAt: null,\n };\n\n this.processes.set(config.name, managed);\n this.processIds.set(id, config.name);\n this.logger.info(`Registered process: ${config.name} (id: ${id})`);\n }\n\n list(): ProcessInfo[] {\n return [...this.processes.values()].map((m) => ({\n id: m.id,\n name: m.config.name,\n status: m.status,\n pid: m.spawned?.proc.pid,\n exitCode: m.exitCode,\n restarts: m.restarts,\n startedAt: m.startedAt,\n command: m.config.command,\n args: m.config.args,\n }));\n }\n\n start(name: string): void {\n const { name: resolvedName, managed } = this.resolveProcessIdentifier(name);\n\n if (managed.status === \"running\") {\n this.logger.warn(`Process ${resolvedName} is already running`);\n return;\n }\n\n this.spawnProcess(managed);\n }\n\n startAll(): void {\n this.setupSignalHandlers();\n\n for (const [_, managed] of this.processes) {\n if (managed.status !== \"running\") {\n this.spawnProcess(managed);\n }\n }\n }\n\n serve(options?: ServerOptions & { enabled?: boolean }): void {\n const serverOpts = { ...this.serverOptions, ...options };\n if (serverOpts.enabled === false) return;\n const { enabled: _enabled, ...serverOptions } = serverOpts;\n const server = createServer(this, serverOptions);\n this.httpServer = server.start();\n }\n\n async stop(name: string, timeoutMs = DEFAULT_STOP_TIMEOUT_MS): Promise<void> {\n const { name: resolvedName, managed } = this.resolveProcessIdentifier(name);\n\n managed.stopRequested = true;\n this.clearRestartTimer(managed);\n\n if (managed.status !== \"running\" || !managed.spawned) {\n this.logger.warn(`Process ${resolvedName} is not running`);\n return;\n }\n\n const { spawned } = managed;\n\n this.logger.info(`Stopping process: ${resolvedName} (SIGTERM)`);\n spawned.kill(\"SIGTERM\");\n\n const exitedGracefully = await Promise.race([\n spawned.done.then(() => true).catch(() => true),\n new Promise<false>((resolve) => setTimeout(() => resolve(false), timeoutMs)),\n ]);\n\n if (!exitedGracefully && managed.status === \"running\") {\n this.logger.warn(`Process ${resolvedName} did not stop gracefully, sending SIGKILL`);\n spawned.kill(\"SIGKILL\");\n await spawned.done.catch(() => {});\n }\n }\n\n async stopAll(): Promise<void> {\n this.logger.info(\"Stopping all processes...\");\n\n for (const managed of this.processes.values()) {\n managed.stopRequested = true;\n this.clearRestartTimer(managed);\n }\n\n const stopPromises = [...this.processes.entries()]\n .filter(([, m]) => m.status === \"running\")\n .map(([name]) => this.stop(name));\n\n await Promise.allSettled(stopPromises);\n }\n\n async restart(name: string): Promise<void> {\n const { name: resolvedName, managed } = this.resolveProcessIdentifier(name);\n\n this.logger.info(`Restarting process: ${resolvedName}`);\n\n if (managed.status === \"running\") {\n await this.stop(resolvedName);\n }\n\n managed.restarts++;\n this.spawnProcess(managed);\n }\n\n async restartAll(): Promise<void> {\n this.logger.info(\"Restarting all processes...\");\n\n for (const [name] of this.processes) {\n await this.restart(name);\n }\n }\n\n async delete(name: string): Promise<void> {\n const { name: resolvedName, managed } = this.resolveProcessIdentifier(name);\n\n managed.stopRequested = true;\n this.clearRestartTimer(managed);\n\n if (managed.status === \"running\") {\n await this.stop(resolvedName);\n }\n\n this.processes.delete(resolvedName);\n this.processIds.delete(managed.id);\n this.logger.info(`Deleted process: ${resolvedName}`);\n }\n\n async deleteAll(): Promise<void> {\n await this.stopAll();\n this.processes.clear();\n this.logger.info(\"Deleted all processes\");\n }\n\n get(name: string): ProcessInfo | undefined {\n let resolved: { name: string; managed: ManagedProcess };\n try {\n resolved = this.resolveProcessIdentifier(name);\n } catch {\n return undefined;\n }\n\n return {\n id: resolved.managed.id,\n name: resolved.managed.config.name,\n status: resolved.managed.status,\n pid: resolved.managed.spawned?.proc.pid,\n exitCode: resolved.managed.exitCode,\n restarts: resolved.managed.restarts,\n startedAt: resolved.managed.startedAt,\n command: resolved.managed.config.command,\n args: resolved.managed.config.args,\n };\n }\n\n async shutdown(): Promise<void> {\n await this.stopActiveTask();\n await this.stopAll();\n this.closeEnvWatcher();\n if (this.httpServer) {\n this.httpServer.close();\n this.logger.info(\"HTTP server closed\");\n }\n }\n\n private spawnProcess(managed: ManagedProcess): void {\n const { config } = managed;\n\n managed.stopRequested = false;\n this.clearRestartTimer(managed);\n\n const env = loadEnvForProcess({\n configDir: this.configDir ?? this.cwd,\n processName: config.name,\n globalEnvFile: this.globalEnvFile,\n processEnvFile: config.envFile,\n configEnv: config.env,\n });\n\n const spawned = spawnWithLogging({\n cwd: config.cwd ?? this.cwd,\n logFile: join(this.cwd, \"logs\", \"processes\", `${config.name}.log`),\n type: \"process\",\n name: config.name,\n command: config.command,\n args: config.args,\n env,\n });\n\n managed.spawned = spawned;\n managed.status = \"running\";\n managed.startedAt = new Date();\n managed.exitCode = null;\n\n this.logger.info(`Started process: ${config.name} (pid: ${spawned.proc.pid})`);\n\n const handleExit = (exitCode: number | null, error?: unknown) => {\n managed.exitCode = exitCode;\n managed.status =\n exitCode === null || exitCode === undefined || exitCode === 0 ? \"stopped\" : \"errored\";\n const now = Date.now();\n const startedAt = managed.startedAt?.getTime() ?? now;\n const uptimeMs = Math.max(0, now - startedAt);\n const resetMs = (config.restartResetSeconds ?? DEFAULT_RESTART_RESET_SECONDS) * 1000;\n\n if (uptimeMs >= resetMs) {\n managed.restartAttempts = 0;\n }\n\n if (exitCode === null || exitCode === undefined) {\n this.logger.info(`Process ${config.name} stopped`);\n } else if (exitCode !== 0) {\n this.logger.info(`Process ${config.name} exited with code ${exitCode}`);\n } else {\n this.logger.info(`Process ${config.name} exited successfully`);\n }\n\n if (error) {\n this.logger.error(`Process ${config.name} failed with error: ${error}`);\n }\n\n const policy = config.restartPolicy ?? \"on-failure\";\n const isFailure = exitCode === null || exitCode === undefined || exitCode !== 0;\n const shouldRestart =\n !managed.stopRequested && (policy === \"always\" || (policy === \"on-failure\" && isFailure));\n\n if (!shouldRestart) {\n return;\n }\n\n const maxRestarts = config.maxRestarts ?? DEFAULT_RESTART_MAX;\n if (managed.restartAttempts >= maxRestarts) {\n managed.status = \"errored\";\n this.logger.warn(\n `Process ${config.name} reached restart limit (${maxRestarts}), not restarting`,\n );\n return;\n }\n\n managed.restartAttempts += 1;\n managed.restarts += 1;\n const minDelay = config.restartMinDelayMs ?? this.restartMinDelayMsGlobal;\n const maxDelay = config.restartMaxDelayMs ?? this.restartMaxDelayMsGlobal;\n const delay = Math.min(maxDelay, minDelay * Math.pow(2, managed.restartAttempts - 1));\n\n this.logger.warn(\n `Restarting process ${config.name} in ${delay}ms (attempt ${managed.restartAttempts}/${maxRestarts})`,\n );\n\n managed.restartTimer = setTimeout(() => {\n managed.restartTimer = null;\n if (!managed.stopRequested) {\n this.spawnProcess(managed);\n }\n }, delay);\n };\n\n spawned.done\n .then((exitCode) => handleExit(exitCode))\n .catch((error) => {\n const exitCode = spawned.proc.exitCode ?? 1;\n handleExit(exitCode, error);\n });\n }\n\n private async runTasks(tasks: Array<ProcessConfig>): Promise<void> {\n for (const task of tasks) {\n const env = loadEnvForProcess({\n configDir: this.configDir ?? this.cwd,\n processName: task.name,\n globalEnvFile: this.globalEnvFile,\n processEnvFile: task.envFile,\n configEnv: task.env,\n });\n\n const taskAbortController = new AbortController();\n this.taskAbortController = taskAbortController;\n\n const spawned = spawnWithLogging({\n cwd: task.cwd ?? this.cwd,\n logFile: join(this.cwd, \"logs\", \"tasks\", `${task.name}.log`),\n type: \"task\",\n name: task.name,\n command: task.command,\n args: task.args,\n env,\n abortSignal: taskAbortController.signal,\n });\n\n this.currentTask = spawned;\n this.currentTaskName = task.name;\n\n let exitCode: number | null;\n try {\n exitCode = await spawned.done;\n } finally {\n this.currentTask = null;\n this.currentTaskName = null;\n this.taskAbortController = null;\n }\n\n if (exitCode !== 0) {\n this.logger.error(`Task ${task.name} failed with exit code ${exitCode}`);\n throw new Error(`Task ${task.name} failed`);\n }\n }\n\n if (tasks.length > 0) {\n this.logger.info(\"All tasks completed\");\n }\n }\n\n private clearRestartTimer(managed: ManagedProcess): void {\n if (managed.restartTimer) {\n clearTimeout(managed.restartTimer);\n managed.restartTimer = null;\n }\n }\n\n private async stopActiveTask(timeoutMs = DEFAULT_STOP_TIMEOUT_MS): Promise<void> {\n if (!this.currentTask) return;\n\n const taskName = this.currentTaskName ?? \"unknown\";\n this.logger.info(`Stopping task: ${taskName} (SIGTERM)`);\n this.currentTask.kill(\"SIGTERM\");\n\n const exitedGracefully = await Promise.race([\n this.currentTask.done.then(() => true).catch(() => true),\n new Promise<false>((resolve) => setTimeout(() => resolve(false), timeoutMs)),\n ]);\n\n if (!exitedGracefully) {\n this.logger.warn(`Task ${taskName} did not stop gracefully, sending SIGKILL`);\n this.currentTask.kill(\"SIGKILL\");\n await this.currentTask.done.catch(() => {});\n }\n }\n\n private abortActiveTask(signal: NodeJS.Signals): void {\n if (this.taskAbortController && !this.taskAbortController.signal.aborted) {\n this.taskAbortController.abort();\n }\n if (this.currentTask) {\n this.currentTask.kill(signal);\n }\n }\n\n private setupSignalHandlers(): void {\n if (this.signalHandlersSetup) return;\n this.signalHandlersSetup = true;\n\n const signals = [\"SIGINT\", \"SIGTERM\"] as const;\n for (const signal of signals) {\n process.on(signal, () => {\n process.stdout.write(\"\\r\\x1b[K\");\n this.logger.info(`Received ${signal}, shutting down...`);\n this.abortActiveTask(signal);\n this.shutdown().then(() => process.exit(0));\n });\n }\n }\n\n private setupEnvWatchers(): void {\n if (this.envWatcher) return;\n if (!this.restartOnEnvChangeGlobal) return;\n\n const watchDir = this.configDir ?? this.cwd;\n this.envWatcher = watch(watchDir, (event, filename) => {\n if (!filename) return;\n const envFile = filename.toString();\n if (!envFile.startsWith(\".env\")) return;\n if (event !== \"change\" && event !== \"rename\") return;\n\n if (envFile === \".env\") {\n this.scheduleEnvRestart(\"global\", () => {\n this.logger.info(\"Detected .env change, scheduling restarts\");\n for (const managed of this.processes.values()) {\n this.scheduleEnvRestartForProcess(managed);\n }\n });\n return;\n }\n\n if (!envFile.startsWith(\".env.\")) return;\n const processName = envFile.slice(\".env.\".length);\n if (!processName) return;\n\n this.scheduleEnvRestart(processName, () => {\n const managed = this.processes.get(processName);\n if (!managed) return;\n this.logger.info(`Detected ${envFile} change, scheduling restart for ${processName}`);\n this.scheduleEnvRestartForProcess(managed);\n });\n });\n }\n\n private closeEnvWatcher(): void {\n if (this.envWatcher) {\n this.envWatcher.close();\n this.envWatcher = null;\n }\n for (const timer of this.envChangeTimers.values()) {\n clearTimeout(timer);\n }\n this.envChangeTimers.clear();\n for (const timer of this.envRestartTimers.values()) {\n clearTimeout(timer);\n }\n this.envRestartTimers.clear();\n }\n\n private scheduleEnvRestart(key: string, restart: () => void): void {\n const existing = this.envChangeTimers.get(key);\n if (existing) clearTimeout(existing);\n const timer = setTimeout(() => {\n this.envChangeTimers.delete(key);\n restart();\n }, 250);\n this.envChangeTimers.set(key, timer);\n }\n\n private scheduleEnvRestartForProcess(managed: ManagedProcess): void {\n if (!this.restartOnEnvChangeGlobal) return;\n if (managed.config.restartOnEnvChange === false) return;\n if (managed.status !== \"running\") return;\n const name = managed.config.name;\n const existing = this.envRestartTimers.get(name);\n if (existing) clearTimeout(existing);\n\n const delayMs = managed.config.restartMinDelayMs ?? this.restartMinDelayMsGlobal;\n\n const timer = setTimeout(\n () => {\n this.envRestartTimers.delete(name);\n this.restart(name).catch((error) => {\n this.logger.error(`Failed to restart ${name} after env change: ${error}`);\n });\n },\n Math.max(0, delayMs),\n );\n\n this.envRestartTimers.set(name, timer);\n this.logger.info(`Restarting ${name} after env change in ${Math.max(0, delayMs)}ms`);\n }\n\n private resolveProcessIdentifier(nameOrId: string): {\n name: string;\n managed: ManagedProcess;\n } {\n const byName = this.processes.get(nameOrId);\n if (byName) {\n return { name: nameOrId, managed: byName };\n }\n\n if (/^\\d+$/.test(nameOrId)) {\n const id = Number(nameOrId);\n const name = this.processIds.get(id);\n if (name) {\n const managed = this.processes.get(name);\n if (managed) {\n return { name, managed };\n }\n }\n }\n\n throw new Error(`Process ${nameOrId} not found`);\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pid1",
|
|
3
|
-
"
|
|
4
|
-
"version": "0.0.0-dev.0",
|
|
3
|
+
"version": "0.0.0-dev.1",
|
|
5
4
|
"bin": "./dist/cli.mjs",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist",
|
|
7
|
+
"src"
|
|
8
|
+
],
|
|
9
|
+
"type": "module",
|
|
6
10
|
"exports": {
|
|
7
11
|
".": {
|
|
8
12
|
"import": "./dist/index.mjs",
|
|
@@ -17,10 +21,6 @@
|
|
|
17
21
|
"types": "./dist/client.d.mts"
|
|
18
22
|
}
|
|
19
23
|
},
|
|
20
|
-
"files": [
|
|
21
|
-
"src",
|
|
22
|
-
"dist"
|
|
23
|
-
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@hono/node-server": "^1.19.9",
|
|
26
26
|
"@orpc/client": "^1.13.4",
|
|
@@ -38,15 +38,17 @@
|
|
|
38
38
|
"@types/node": "^25.0.10",
|
|
39
39
|
"oxfmt": "^0.26.0",
|
|
40
40
|
"tsdown": "^0.20.1",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"vitest": "^3.2.4"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsdown",
|
|
46
46
|
"dev": "tsx src/cli.ts init",
|
|
47
47
|
"dev:docker": "docker build -t pid1 . && docker run --rm pid1",
|
|
48
|
+
"prepublish": "pnpm build",
|
|
48
49
|
"test": "vitest run",
|
|
49
50
|
"test:watch": "vitest",
|
|
50
|
-
"typecheck": "tsc --noEmit"
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"format": "oxfmt"
|
|
51
53
|
}
|
|
52
54
|
}
|
package/src/api/client.ts
CHANGED
|
@@ -7,9 +7,7 @@ export type ClientOptions = {
|
|
|
7
7
|
url?: string;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
export function createClient(
|
|
11
|
-
options: ClientOptions = {},
|
|
12
|
-
): RouterClient<Router> {
|
|
10
|
+
export function createClient(options: ClientOptions = {}): RouterClient<Router> {
|
|
13
11
|
const { url = "http://127.0.0.1:3000/rpc" } = options;
|
|
14
12
|
|
|
15
13
|
const link = new RPCLink({ url });
|
package/src/api/router.ts
CHANGED
|
@@ -24,11 +24,9 @@ type RouterContext = {
|
|
|
24
24
|
|
|
25
25
|
const base = os.$context<RouterContext>();
|
|
26
26
|
|
|
27
|
-
export const list = base
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
return context.pm.list();
|
|
31
|
-
});
|
|
27
|
+
export const list = base.output(v.array(ProcessInfoSchema)).handler(async ({ context }) => {
|
|
28
|
+
return context.pm.list();
|
|
29
|
+
});
|
|
32
30
|
|
|
33
31
|
export const get = base
|
|
34
32
|
.input(ProcessNameSchema)
|
|
@@ -129,11 +127,9 @@ export const deleteProcess = base
|
|
|
129
127
|
}
|
|
130
128
|
});
|
|
131
129
|
|
|
132
|
-
export const health = os
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return { status: "ok" as const };
|
|
136
|
-
});
|
|
130
|
+
export const health = os.output(v.object({ status: v.literal("ok") })).handler(async () => {
|
|
131
|
+
return { status: "ok" as const };
|
|
132
|
+
});
|
|
137
133
|
|
|
138
134
|
export const router = {
|
|
139
135
|
health,
|
package/src/api/server.ts
CHANGED
|
@@ -27,9 +27,7 @@ export function createServer(pm: ProcessManager, options: ServerOptions = {}) {
|
|
|
27
27
|
],
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
app.use(
|
|
31
|
-
honoLogger((msg, ...args) => logger.debug(`${msg} ${args.join(" ")}`)),
|
|
32
|
-
);
|
|
30
|
+
app.use(honoLogger((msg, ...args) => logger.debug(`${msg} ${args.join(" ")}`)));
|
|
33
31
|
|
|
34
32
|
app.get("/health", (c) => c.json({ status: "ok" }));
|
|
35
33
|
|
package/src/cli.ts
CHANGED
|
@@ -66,9 +66,7 @@ async function main() {
|
|
|
66
66
|
program
|
|
67
67
|
.name("pid1")
|
|
68
68
|
.description("Process manager daemon and controller")
|
|
69
|
-
.option("--port <number>", "Port for the HTTP server", (value) =>
|
|
70
|
-
Number(value),
|
|
71
|
-
)
|
|
69
|
+
.option("--port <number>", "Port for the HTTP server", (value) => Number(value))
|
|
72
70
|
.option("--host <host>", "Host for the HTTP server");
|
|
73
71
|
|
|
74
72
|
const commonOptions = (): CommonArgs => {
|
|
@@ -161,8 +159,6 @@ main().catch((error) => {
|
|
|
161
159
|
);
|
|
162
160
|
process.exit(1);
|
|
163
161
|
}
|
|
164
|
-
logger.error(
|
|
165
|
-
error instanceof Error ? (error.stack ?? error.message) : String(error),
|
|
166
|
-
);
|
|
162
|
+
logger.error(error instanceof Error ? (error.stack ?? error.message) : String(error));
|
|
167
163
|
process.exit(1);
|
|
168
164
|
});
|
package/src/config.ts
CHANGED
|
@@ -38,16 +38,13 @@ export type LoadedConfig = v.InferOutput<typeof Config> & {
|
|
|
38
38
|
configPath: string;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
export async function loadConfigFile(
|
|
42
|
-
path?: string,
|
|
43
|
-
cwd?: string,
|
|
44
|
-
): Promise<LoadedConfig> {
|
|
41
|
+
export async function loadConfigFile(path?: string, cwd?: string): Promise<LoadedConfig> {
|
|
45
42
|
const configFile = path ?? findConfigFile(cwd ?? process.cwd());
|
|
46
43
|
if (!configFile) {
|
|
47
|
-
|
|
44
|
+
logger.error(
|
|
48
45
|
`Failed to find a config file in ${cwd ?? process.cwd()} or any of its parent directories`,
|
|
49
46
|
);
|
|
50
|
-
|
|
47
|
+
logger.error("Exiting...");
|
|
51
48
|
process.exit(1);
|
|
52
49
|
}
|
|
53
50
|
|
|
@@ -55,12 +52,10 @@ export async function loadConfigFile(
|
|
|
55
52
|
const config = await tsImport(configFile, {
|
|
56
53
|
parentURL: import.meta.url,
|
|
57
54
|
})
|
|
58
|
-
.then((m) =>
|
|
59
|
-
"default" in m.default ? m.default.default : m.default,
|
|
60
|
-
)
|
|
55
|
+
.then((m) => ("default" in m.default ? m.default.default : m.default))
|
|
61
56
|
.catch((error) => {
|
|
62
57
|
logger.error(`Failed to load config from ${configFile}`);
|
|
63
|
-
logger.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
58
|
+
logger.error(error instanceof Error ? (error.stack ?? error.message) : String(error));
|
|
64
59
|
logger.error("Exiting...");
|
|
65
60
|
process.exit(1);
|
|
66
61
|
});
|
package/src/env.ts
CHANGED
|
@@ -33,16 +33,8 @@ export type LoadEnvOptions = {
|
|
|
33
33
|
configEnv?: Record<string, string>;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
export function loadEnvForProcess(
|
|
37
|
-
|
|
38
|
-
): Record<string, string> {
|
|
39
|
-
const {
|
|
40
|
-
configDir,
|
|
41
|
-
processName,
|
|
42
|
-
globalEnvFile,
|
|
43
|
-
processEnvFile,
|
|
44
|
-
configEnv = {},
|
|
45
|
-
} = options;
|
|
36
|
+
export function loadEnvForProcess(options: LoadEnvOptions): Record<string, string> {
|
|
37
|
+
const { configDir, processName, globalEnvFile, processEnvFile, configEnv = {} } = options;
|
|
46
38
|
|
|
47
39
|
const baseEnv = Object.fromEntries(
|
|
48
40
|
Object.entries(process.env).filter(([, value]) => value !== undefined),
|
|
@@ -56,8 +48,7 @@ export function loadEnvForProcess(
|
|
|
56
48
|
|
|
57
49
|
let processEnv: Record<string, string> = {};
|
|
58
50
|
if (processEnvFile !== false) {
|
|
59
|
-
const processPath =
|
|
60
|
-
processEnvFile ?? join(configDir, `.env.${processName}`);
|
|
51
|
+
const processPath = processEnvFile ?? join(configDir, `.env.${processName}`);
|
|
61
52
|
processEnv = loadEnvFile(processPath);
|
|
62
53
|
}
|
|
63
54
|
|
package/src/exec.ts
CHANGED
|
@@ -53,9 +53,7 @@ export function spawnWithLogging({
|
|
|
53
53
|
const done = (async () => {
|
|
54
54
|
const logger = processLogger({ name: `${type}:${name}`, logFile });
|
|
55
55
|
|
|
56
|
-
logger.info(
|
|
57
|
-
`Starting: command=${command}, args=[${args.join(", ")}], cwd=${cwd}`,
|
|
58
|
-
);
|
|
56
|
+
logger.info(`Starting: command=${command}, args=[${args.join(", ")}], cwd=${cwd}`);
|
|
59
57
|
|
|
60
58
|
for await (const line of proc) {
|
|
61
59
|
logger.info(line);
|
|
@@ -65,12 +63,7 @@ export function spawnWithLogging({
|
|
|
65
63
|
|
|
66
64
|
const exitCode = proc.exitCode;
|
|
67
65
|
|
|
68
|
-
if (
|
|
69
|
-
proc.aborted ||
|
|
70
|
-
proc.killed ||
|
|
71
|
-
exitCode === null ||
|
|
72
|
-
exitCode === undefined
|
|
73
|
-
) {
|
|
66
|
+
if (proc.aborted || proc.killed || exitCode === null || exitCode === undefined) {
|
|
74
67
|
logger.info(`${name} was terminated`);
|
|
75
68
|
} else if (exitCode === 0) {
|
|
76
69
|
logger.info(`${name} completed successfully`);
|
package/src/process-manager.ts
CHANGED
|
@@ -97,10 +97,8 @@ export class ProcessManager {
|
|
|
97
97
|
this.configDir = dirname(config.configPath);
|
|
98
98
|
this.globalEnvFile = config.envFile;
|
|
99
99
|
this.restartOnEnvChangeGlobal = config.restartOnEnvChange ?? true;
|
|
100
|
-
this.restartMinDelayMsGlobal =
|
|
101
|
-
|
|
102
|
-
this.restartMaxDelayMsGlobal =
|
|
103
|
-
config.restartMaxDelayMs ?? DEFAULT_RESTART_MAX_DELAY_MS;
|
|
100
|
+
this.restartMinDelayMsGlobal = config.restartMinDelayMs ?? DEFAULT_RESTART_MIN_DELAY_MS;
|
|
101
|
+
this.restartMaxDelayMsGlobal = config.restartMaxDelayMs ?? DEFAULT_RESTART_MAX_DELAY_MS;
|
|
104
102
|
|
|
105
103
|
this.logger.info(
|
|
106
104
|
`Executing ${config.tasks.length} tasks and registering ${config.processes.length} processes`,
|
|
@@ -200,15 +198,11 @@ export class ProcessManager {
|
|
|
200
198
|
|
|
201
199
|
const exitedGracefully = await Promise.race([
|
|
202
200
|
spawned.done.then(() => true).catch(() => true),
|
|
203
|
-
new Promise<false>((resolve) =>
|
|
204
|
-
setTimeout(() => resolve(false), timeoutMs),
|
|
205
|
-
),
|
|
201
|
+
new Promise<false>((resolve) => setTimeout(() => resolve(false), timeoutMs)),
|
|
206
202
|
]);
|
|
207
203
|
|
|
208
204
|
if (!exitedGracefully && managed.status === "running") {
|
|
209
|
-
this.logger.warn(
|
|
210
|
-
`Process ${resolvedName} did not stop gracefully, sending SIGKILL`,
|
|
211
|
-
);
|
|
205
|
+
this.logger.warn(`Process ${resolvedName} did not stop gracefully, sending SIGKILL`);
|
|
212
206
|
spawned.kill("SIGKILL");
|
|
213
207
|
await spawned.done.catch(() => {});
|
|
214
208
|
}
|
|
@@ -331,21 +325,16 @@ export class ProcessManager {
|
|
|
331
325
|
managed.startedAt = new Date();
|
|
332
326
|
managed.exitCode = null;
|
|
333
327
|
|
|
334
|
-
this.logger.info(
|
|
335
|
-
`Started process: ${config.name} (pid: ${spawned.proc.pid})`,
|
|
336
|
-
);
|
|
328
|
+
this.logger.info(`Started process: ${config.name} (pid: ${spawned.proc.pid})`);
|
|
337
329
|
|
|
338
330
|
const handleExit = (exitCode: number | null, error?: unknown) => {
|
|
339
331
|
managed.exitCode = exitCode;
|
|
340
332
|
managed.status =
|
|
341
|
-
exitCode === null || exitCode === undefined || exitCode === 0
|
|
342
|
-
? "stopped"
|
|
343
|
-
: "errored";
|
|
333
|
+
exitCode === null || exitCode === undefined || exitCode === 0 ? "stopped" : "errored";
|
|
344
334
|
const now = Date.now();
|
|
345
335
|
const startedAt = managed.startedAt?.getTime() ?? now;
|
|
346
336
|
const uptimeMs = Math.max(0, now - startedAt);
|
|
347
|
-
const resetMs =
|
|
348
|
-
(config.restartResetSeconds ?? DEFAULT_RESTART_RESET_SECONDS) * 1000;
|
|
337
|
+
const resetMs = (config.restartResetSeconds ?? DEFAULT_RESTART_RESET_SECONDS) * 1000;
|
|
349
338
|
|
|
350
339
|
if (uptimeMs >= resetMs) {
|
|
351
340
|
managed.restartAttempts = 0;
|
|
@@ -364,11 +353,9 @@ export class ProcessManager {
|
|
|
364
353
|
}
|
|
365
354
|
|
|
366
355
|
const policy = config.restartPolicy ?? "on-failure";
|
|
367
|
-
const isFailure =
|
|
368
|
-
exitCode === null || exitCode === undefined || exitCode !== 0;
|
|
356
|
+
const isFailure = exitCode === null || exitCode === undefined || exitCode !== 0;
|
|
369
357
|
const shouldRestart =
|
|
370
|
-
!managed.stopRequested &&
|
|
371
|
-
(policy === "always" || (policy === "on-failure" && isFailure));
|
|
358
|
+
!managed.stopRequested && (policy === "always" || (policy === "on-failure" && isFailure));
|
|
372
359
|
|
|
373
360
|
if (!shouldRestart) {
|
|
374
361
|
return;
|
|
@@ -387,10 +374,7 @@ export class ProcessManager {
|
|
|
387
374
|
managed.restarts += 1;
|
|
388
375
|
const minDelay = config.restartMinDelayMs ?? this.restartMinDelayMsGlobal;
|
|
389
376
|
const maxDelay = config.restartMaxDelayMs ?? this.restartMaxDelayMsGlobal;
|
|
390
|
-
const delay = Math.min(
|
|
391
|
-
maxDelay,
|
|
392
|
-
minDelay * Math.pow(2, managed.restartAttempts - 1),
|
|
393
|
-
);
|
|
377
|
+
const delay = Math.min(maxDelay, minDelay * Math.pow(2, managed.restartAttempts - 1));
|
|
394
378
|
|
|
395
379
|
this.logger.warn(
|
|
396
380
|
`Restarting process ${config.name} in ${delay}ms (attempt ${managed.restartAttempts}/${maxRestarts})`,
|
|
@@ -449,9 +433,7 @@ export class ProcessManager {
|
|
|
449
433
|
}
|
|
450
434
|
|
|
451
435
|
if (exitCode !== 0) {
|
|
452
|
-
this.logger.error(
|
|
453
|
-
`Task ${task.name} failed with exit code ${exitCode}`,
|
|
454
|
-
);
|
|
436
|
+
this.logger.error(`Task ${task.name} failed with exit code ${exitCode}`);
|
|
455
437
|
throw new Error(`Task ${task.name} failed`);
|
|
456
438
|
}
|
|
457
439
|
}
|
|
@@ -468,9 +450,7 @@ export class ProcessManager {
|
|
|
468
450
|
}
|
|
469
451
|
}
|
|
470
452
|
|
|
471
|
-
private async stopActiveTask(
|
|
472
|
-
timeoutMs = DEFAULT_STOP_TIMEOUT_MS,
|
|
473
|
-
): Promise<void> {
|
|
453
|
+
private async stopActiveTask(timeoutMs = DEFAULT_STOP_TIMEOUT_MS): Promise<void> {
|
|
474
454
|
if (!this.currentTask) return;
|
|
475
455
|
|
|
476
456
|
const taskName = this.currentTaskName ?? "unknown";
|
|
@@ -479,15 +459,11 @@ export class ProcessManager {
|
|
|
479
459
|
|
|
480
460
|
const exitedGracefully = await Promise.race([
|
|
481
461
|
this.currentTask.done.then(() => true).catch(() => true),
|
|
482
|
-
new Promise<false>((resolve) =>
|
|
483
|
-
setTimeout(() => resolve(false), timeoutMs),
|
|
484
|
-
),
|
|
462
|
+
new Promise<false>((resolve) => setTimeout(() => resolve(false), timeoutMs)),
|
|
485
463
|
]);
|
|
486
464
|
|
|
487
465
|
if (!exitedGracefully) {
|
|
488
|
-
this.logger.warn(
|
|
489
|
-
`Task ${taskName} did not stop gracefully, sending SIGKILL`,
|
|
490
|
-
);
|
|
466
|
+
this.logger.warn(`Task ${taskName} did not stop gracefully, sending SIGKILL`);
|
|
491
467
|
this.currentTask.kill("SIGKILL");
|
|
492
468
|
await this.currentTask.done.catch(() => {});
|
|
493
469
|
}
|
|
@@ -545,9 +521,7 @@ export class ProcessManager {
|
|
|
545
521
|
this.scheduleEnvRestart(processName, () => {
|
|
546
522
|
const managed = this.processes.get(processName);
|
|
547
523
|
if (!managed) return;
|
|
548
|
-
this.logger.info(
|
|
549
|
-
`Detected ${envFile} change, scheduling restart for ${processName}`,
|
|
550
|
-
);
|
|
524
|
+
this.logger.info(`Detected ${envFile} change, scheduling restart for ${processName}`);
|
|
551
525
|
this.scheduleEnvRestartForProcess(managed);
|
|
552
526
|
});
|
|
553
527
|
});
|
|
@@ -586,25 +560,20 @@ export class ProcessManager {
|
|
|
586
560
|
const existing = this.envRestartTimers.get(name);
|
|
587
561
|
if (existing) clearTimeout(existing);
|
|
588
562
|
|
|
589
|
-
const delayMs =
|
|
590
|
-
managed.config.restartMinDelayMs ?? this.restartMinDelayMsGlobal;
|
|
563
|
+
const delayMs = managed.config.restartMinDelayMs ?? this.restartMinDelayMsGlobal;
|
|
591
564
|
|
|
592
565
|
const timer = setTimeout(
|
|
593
566
|
() => {
|
|
594
567
|
this.envRestartTimers.delete(name);
|
|
595
568
|
this.restart(name).catch((error) => {
|
|
596
|
-
this.logger.error(
|
|
597
|
-
`Failed to restart ${name} after env change: ${error}`,
|
|
598
|
-
);
|
|
569
|
+
this.logger.error(`Failed to restart ${name} after env change: ${error}`);
|
|
599
570
|
});
|
|
600
571
|
},
|
|
601
572
|
Math.max(0, delayMs),
|
|
602
573
|
);
|
|
603
574
|
|
|
604
575
|
this.envRestartTimers.set(name, timer);
|
|
605
|
-
this.logger.info(
|
|
606
|
-
`Restarting ${name} after env change in ${Math.max(0, delayMs)}ms`,
|
|
607
|
-
);
|
|
576
|
+
this.logger.info(`Restarting ${name} after env change in ${Math.max(0, delayMs)}ms`);
|
|
608
577
|
}
|
|
609
578
|
|
|
610
579
|
private resolveProcessIdentifier(nameOrId: string): {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"process-manager-gO56266Q.mjs","names":["logger","logger","honoLogger"],"sources":["../src/env.ts","../src/exec.ts","../src/api/router.ts","../src/api/server.ts","../src/process-manager.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { parse } from \"dotenv\";\nimport { globalLogger } from \"./logger\";\n\nexport type EnvFileConfig = {\n envFile?: string | false;\n};\n\nconst logger = globalLogger.child(\"env\");\n\nfunction loadEnvFile(path: string): Record<string, string> {\n if (!existsSync(path)) {\n return {};\n }\n\n try {\n const content = readFileSync(path, \"utf-8\");\n const parsed = parse(content);\n logger.debug(`Loaded env file: ${path}`);\n return parsed;\n } catch (error) {\n logger.warn(`Failed to parse env file ${path}: ${error}`);\n return {};\n }\n}\n\nexport type LoadEnvOptions = {\n configDir: string;\n processName: string;\n globalEnvFile?: string | false;\n processEnvFile?: string | false;\n configEnv?: Record<string, string>;\n};\n\nexport function loadEnvForProcess(\n options: LoadEnvOptions,\n): Record<string, string> {\n const {\n configDir,\n processName,\n globalEnvFile,\n processEnvFile,\n configEnv = {},\n } = options;\n\n const baseEnv = Object.fromEntries(\n Object.entries(process.env).filter(([, value]) => value !== undefined),\n ) as Record<string, string>;\n\n let globalEnv: Record<string, string> = {};\n if (globalEnvFile !== false) {\n const globalPath = globalEnvFile ?? join(configDir, \".env\");\n globalEnv = loadEnvFile(globalPath);\n }\n\n let processEnv: Record<string, string> = {};\n if (processEnvFile !== false) {\n const processPath =\n processEnvFile ?? join(configDir, `.env.${processName}`);\n processEnv = loadEnvFile(processPath);\n }\n\n return {\n ...baseEnv,\n ...globalEnv,\n ...configEnv,\n ...processEnv,\n };\n}\n\nexport function getConfigDir(configPath: string): string {\n return dirname(configPath);\n}\n","import { processLogger } from \"./logger\";\nimport { x } from \"tinyexec\";\n\ntype ExecType = \"task\" | \"process\";\n\ntype ExecWithLoggingParams = {\n name: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n cwd: string;\n logFile: string;\n abortSignal?: AbortSignal;\n type: ExecType;\n};\n\ntype TinyExecProcess = ReturnType<typeof x>;\n\nexport type SpawnedProcess = {\n proc: TinyExecProcess;\n done: Promise<number | null>;\n kill: (signal?: NodeJS.Signals) => boolean;\n};\n\nexport function spawnWithLogging({\n name,\n command,\n args,\n env,\n cwd,\n logFile,\n abortSignal,\n type,\n}: ExecWithLoggingParams): SpawnedProcess {\n const proc = x(command, args, {\n nodeOptions: { cwd, env },\n signal: abortSignal,\n throwOnError: true,\n });\n\n const kill = (signal: NodeJS.Signals = \"SIGTERM\"): boolean => {\n if (proc.pid) {\n try {\n process.kill(proc.pid, signal);\n return true;\n } catch {\n return false;\n }\n }\n return false;\n };\n\n const done = (async () => {\n const logger = processLogger({ name: `${type}:${name}`, logFile });\n\n logger.info(\n `Starting: command=${command}, args=[${args.join(\", \")}], cwd=${cwd}`,\n );\n\n for await (const line of proc) {\n logger.info(line);\n }\n\n await proc;\n\n const exitCode = proc.exitCode;\n\n if (\n proc.aborted ||\n proc.killed ||\n exitCode === null ||\n exitCode === undefined\n ) {\n logger.info(`${name} was terminated`);\n } else if (exitCode === 0) {\n logger.info(`${name} completed successfully`);\n } else {\n logger.info(`${name} exited with code ${exitCode}`);\n }\n\n return proc.exitCode ?? null;\n })();\n\n return { proc, done, kill };\n}\n","import { os, ORPCError } from \"@orpc/server\";\nimport * as v from \"valibot\";\nimport type { ProcessManager, ProcessInfo } from \"../process-manager.ts\";\n\nconst ProcessInfoSchema = v.object({\n id: v.number(),\n name: v.string(),\n status: v.picklist([\"stopped\", \"running\", \"errored\"]),\n pid: v.optional(v.number()),\n exitCode: v.nullable(v.number()),\n restarts: v.number(),\n startedAt: v.nullable(v.date()),\n command: v.string(),\n args: v.array(v.string()),\n});\n\nconst ProcessNameSchema = v.object({\n name: v.string(),\n});\n\ntype RouterContext = {\n pm: ProcessManager;\n};\n\nconst base = os.$context<RouterContext>();\n\nexport const list = base\n .output(v.array(ProcessInfoSchema))\n .handler(async ({ context }) => {\n return context.pm.list();\n });\n\nexport const get = base\n .input(ProcessNameSchema)\n .output(v.nullable(ProcessInfoSchema))\n .handler(async ({ input, context }) => {\n return context.pm.get(input.name) ?? null;\n });\n\nexport const start = base\n .input(\n v.object({\n name: v.optional(v.string()),\n }),\n )\n .output(v.object({ success: v.boolean(), message: v.string() }))\n .handler(async ({ input, context }) => {\n try {\n if (input.name) {\n context.pm.start(input.name);\n return { success: true, message: `Started process: ${input.name}` };\n } else {\n context.pm.startAll();\n return { success: true, message: \"Started all processes\" };\n }\n } catch (error) {\n throw new ORPCError(\"BAD_REQUEST\", {\n message: error instanceof Error ? error.message : \"Failed to start\",\n });\n }\n });\n\nexport const stop = base\n .input(\n v.object({\n name: v.optional(v.string()),\n }),\n )\n .output(v.object({ success: v.boolean(), message: v.string() }))\n .handler(async ({ input, context }) => {\n try {\n if (input.name) {\n await context.pm.stop(input.name);\n return { success: true, message: `Stopped process: ${input.name}` };\n } else {\n await context.pm.stopAll();\n return { success: true, message: \"Stopped all processes\" };\n }\n } catch (error) {\n throw new ORPCError(\"BAD_REQUEST\", {\n message: error instanceof Error ? error.message : \"Failed to stop\",\n });\n }\n });\n\nexport const restart = base\n .input(\n v.object({\n name: v.optional(v.string()),\n }),\n )\n .output(v.object({ success: v.boolean(), message: v.string() }))\n .handler(async ({ input, context }) => {\n try {\n if (input.name) {\n await context.pm.restart(input.name);\n return { success: true, message: `Restarted process: ${input.name}` };\n } else {\n await context.pm.restartAll();\n return { success: true, message: \"Restarted all processes\" };\n }\n } catch (error) {\n throw new ORPCError(\"BAD_REQUEST\", {\n message: error instanceof Error ? error.message : \"Failed to restart\",\n });\n }\n });\n\nexport const deleteProcess = base\n .input(\n v.object({\n name: v.optional(v.string()),\n }),\n )\n .output(v.object({ success: v.boolean(), message: v.string() }))\n .handler(async ({ input, context }) => {\n try {\n if (input.name) {\n await context.pm.delete(input.name);\n return { success: true, message: `Deleted process: ${input.name}` };\n } else {\n await context.pm.deleteAll();\n return { success: true, message: \"Deleted all processes\" };\n }\n } catch (error) {\n throw new ORPCError(\"BAD_REQUEST\", {\n message: error instanceof Error ? error.message : \"Failed to delete\",\n });\n }\n });\n\nexport const health = os\n .output(v.object({ status: v.literal(\"ok\") }))\n .handler(async () => {\n return { status: \"ok\" as const };\n });\n\nexport const router = {\n health,\n process: {\n list,\n get,\n start,\n stop,\n restart,\n delete: deleteProcess,\n },\n};\n\nexport type Router = typeof router;\n","import { Hono } from \"hono\";\nimport { serve } from \"@hono/node-server\";\nimport { RPCHandler } from \"@orpc/server/fetch\";\nimport { onError } from \"@orpc/server\";\nimport { router } from \"./router.ts\";\nimport type { ProcessManager } from \"../process-manager.ts\";\nimport { globalLogger } from \"../logger.ts\";\nimport { logger as honoLogger } from \"hono/logger\";\n\nexport type ServerOptions = {\n port?: number;\n host?: string;\n};\n\nexport function createServer(pm: ProcessManager, options: ServerOptions = {}) {\n const { port = 3000, host = \"127.0.0.1\" } = options;\n const logger = globalLogger.child(\"server\");\n\n const app = new Hono();\n\n const handler = new RPCHandler(router, {\n interceptors: [\n onError((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(`RPC Error: ${message}`);\n }),\n ],\n });\n\n app.use(\n honoLogger((msg, ...args) => logger.debug(`${msg} ${args.join(\" \")}`)),\n );\n\n app.get(\"/health\", (c) => c.json({ status: \"ok\" }));\n\n app.use(\"/rpc/*\", async (c, next) => {\n const { matched, response } = await handler.handle(c.req.raw, {\n prefix: \"/rpc\",\n context: { pm },\n });\n\n if (matched) {\n return c.newResponse(response.body, response);\n }\n\n await next();\n });\n\n app.notFound((c) => c.json({ error: \"Not found\" }, 404));\n\n return {\n app,\n start: () => {\n const server = serve({\n fetch: app.fetch,\n port,\n hostname: host,\n });\n\n logger.info(`HTTP server listening on http://${host}:${port}`);\n logger.info(`RPC endpoint: http://${host}:${port}/rpc`);\n\n return server;\n },\n };\n}\n\nexport type { Router } from \"./router.ts\";\n","import { dirname, join } from \"node:path\";\nimport { watch, type FSWatcher } from \"node:fs\";\nimport { loadConfigFile } from \"./config.ts\";\nimport { loadEnvForProcess } from \"./env.ts\";\nimport { spawnWithLogging, type SpawnedProcess } from \"./exec.ts\";\nimport { globalLogger } from \"./logger.ts\";\nimport { createServer, type ServerOptions } from \"./api/server.ts\";\n\nexport type ProcessManagerOptions = {\n cwd?: string;\n configPath?: string;\n server?: ServerOptions & { enabled?: boolean };\n};\n\nexport type ProcessStatus = \"stopped\" | \"running\" | \"errored\";\nexport type RestartPolicy = \"never\" | \"always\" | \"on-failure\";\n\nexport type ProcessConfig = {\n name: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n cwd?: string;\n envFile?: string | false;\n restartOnEnvChange?: boolean;\n restartPolicy?: RestartPolicy;\n maxRestarts?: number;\n restartMinDelayMs?: number;\n restartMaxDelayMs?: number;\n restartResetSeconds?: number;\n};\n\nexport type ManagedProcess = {\n id: number;\n config: ProcessConfig;\n status: ProcessStatus;\n spawned: SpawnedProcess | null;\n exitCode: number | null;\n restarts: number;\n restartAttempts: number;\n restartTimer: NodeJS.Timeout | null;\n stopRequested: boolean;\n startedAt: Date | null;\n};\n\nconst DEFAULT_STOP_TIMEOUT_MS = 10000;\nconst DEFAULT_RESTART_RESET_SECONDS = 300;\nconst DEFAULT_RESTART_MAX = 10;\nconst DEFAULT_RESTART_MIN_DELAY_MS = 5000;\nconst DEFAULT_RESTART_MAX_DELAY_MS = 60000;\n\nexport type ProcessInfo = {\n id: number;\n name: string;\n status: ProcessStatus;\n pid: number | undefined;\n exitCode: number | null;\n restarts: number;\n startedAt: Date | null;\n command: string;\n args: string[];\n};\n\nexport class ProcessManager {\n private logger = globalLogger.child(\"pm\");\n private cwd: string;\n private configPath?: string;\n private configDir?: string;\n private globalEnvFile?: string | false;\n private processes: Map<string, ManagedProcess>;\n private processIds: Map<number, string>;\n private nextProcessId = 0;\n private signalHandlersSetup = false;\n private serverOptions?: ServerOptions & { enabled?: boolean };\n private httpServer?: ReturnType<ReturnType<typeof createServer>[\"start\"]>;\n private envWatcher: FSWatcher | null = null;\n private envChangeTimers = new Map<string, NodeJS.Timeout>();\n private envRestartTimers = new Map<string, NodeJS.Timeout>();\n private restartOnEnvChangeGlobal = true;\n private restartMinDelayMsGlobal = DEFAULT_RESTART_MIN_DELAY_MS;\n private restartMaxDelayMsGlobal = DEFAULT_RESTART_MAX_DELAY_MS;\n private currentTask: SpawnedProcess | null = null;\n private currentTaskName: string | null = null;\n private taskAbortController: AbortController | null = null;\n\n constructor(options: ProcessManagerOptions = {}) {\n this.cwd = options.cwd ?? process.cwd();\n this.configPath = options.configPath;\n this.processes = new Map();\n this.processIds = new Map();\n this.serverOptions = options.server;\n }\n\n async init(): Promise<void> {\n const config = await loadConfigFile(this.configPath, this.cwd);\n\n this.configDir = dirname(config.configPath);\n this.globalEnvFile = config.envFile;\n this.restartOnEnvChangeGlobal = config.restartOnEnvChange ?? true;\n this.restartMinDelayMsGlobal =\n config.restartMinDelayMs ?? DEFAULT_RESTART_MIN_DELAY_MS;\n this.restartMaxDelayMsGlobal =\n config.restartMaxDelayMs ?? DEFAULT_RESTART_MAX_DELAY_MS;\n\n this.logger.info(\n `Executing ${config.tasks.length} tasks and registering ${config.processes.length} processes`,\n );\n\n this.setupSignalHandlers();\n this.setupEnvWatchers();\n await this.runTasks(config.tasks);\n\n for (const p of config.processes) {\n this.register(p);\n }\n }\n\n register(config: ProcessConfig): void {\n if (this.processes.has(config.name)) {\n throw new Error(`Process ${config.name} already registered`);\n }\n\n const id = this.nextProcessId++;\n const managed: ManagedProcess = {\n id,\n config,\n status: \"stopped\",\n spawned: null,\n exitCode: null,\n restarts: 0,\n restartAttempts: 0,\n restartTimer: null,\n stopRequested: false,\n startedAt: null,\n };\n\n this.processes.set(config.name, managed);\n this.processIds.set(id, config.name);\n this.logger.info(`Registered process: ${config.name} (id: ${id})`);\n }\n\n list(): ProcessInfo[] {\n return [...this.processes.values()].map((m) => ({\n id: m.id,\n name: m.config.name,\n status: m.status,\n pid: m.spawned?.proc.pid,\n exitCode: m.exitCode,\n restarts: m.restarts,\n startedAt: m.startedAt,\n command: m.config.command,\n args: m.config.args,\n }));\n }\n\n start(name: string): void {\n const { name: resolvedName, managed } = this.resolveProcessIdentifier(name);\n\n if (managed.status === \"running\") {\n this.logger.warn(`Process ${resolvedName} is already running`);\n return;\n }\n\n this.spawnProcess(managed);\n }\n\n startAll(): void {\n this.setupSignalHandlers();\n\n for (const [_, managed] of this.processes) {\n if (managed.status !== \"running\") {\n this.spawnProcess(managed);\n }\n }\n }\n\n serve(options?: ServerOptions & { enabled?: boolean }): void {\n const serverOpts = { ...this.serverOptions, ...options };\n if (serverOpts.enabled === false) return;\n const { enabled: _enabled, ...serverOptions } = serverOpts;\n const server = createServer(this, serverOptions);\n this.httpServer = server.start();\n }\n\n async stop(name: string, timeoutMs = DEFAULT_STOP_TIMEOUT_MS): Promise<void> {\n const { name: resolvedName, managed } = this.resolveProcessIdentifier(name);\n\n managed.stopRequested = true;\n this.clearRestartTimer(managed);\n\n if (managed.status !== \"running\" || !managed.spawned) {\n this.logger.warn(`Process ${resolvedName} is not running`);\n return;\n }\n\n const { spawned } = managed;\n\n this.logger.info(`Stopping process: ${resolvedName} (SIGTERM)`);\n spawned.kill(\"SIGTERM\");\n\n const exitedGracefully = await Promise.race([\n spawned.done.then(() => true).catch(() => true),\n new Promise<false>((resolve) =>\n setTimeout(() => resolve(false), timeoutMs),\n ),\n ]);\n\n if (!exitedGracefully && managed.status === \"running\") {\n this.logger.warn(\n `Process ${resolvedName} did not stop gracefully, sending SIGKILL`,\n );\n spawned.kill(\"SIGKILL\");\n await spawned.done.catch(() => {});\n }\n }\n\n async stopAll(): Promise<void> {\n this.logger.info(\"Stopping all processes...\");\n\n for (const managed of this.processes.values()) {\n managed.stopRequested = true;\n this.clearRestartTimer(managed);\n }\n\n const stopPromises = [...this.processes.entries()]\n .filter(([, m]) => m.status === \"running\")\n .map(([name]) => this.stop(name));\n\n await Promise.allSettled(stopPromises);\n }\n\n async restart(name: string): Promise<void> {\n const { name: resolvedName, managed } = this.resolveProcessIdentifier(name);\n\n this.logger.info(`Restarting process: ${resolvedName}`);\n\n if (managed.status === \"running\") {\n await this.stop(resolvedName);\n }\n\n managed.restarts++;\n this.spawnProcess(managed);\n }\n\n async restartAll(): Promise<void> {\n this.logger.info(\"Restarting all processes...\");\n\n for (const [name] of this.processes) {\n await this.restart(name);\n }\n }\n\n async delete(name: string): Promise<void> {\n const { name: resolvedName, managed } = this.resolveProcessIdentifier(name);\n\n managed.stopRequested = true;\n this.clearRestartTimer(managed);\n\n if (managed.status === \"running\") {\n await this.stop(resolvedName);\n }\n\n this.processes.delete(resolvedName);\n this.processIds.delete(managed.id);\n this.logger.info(`Deleted process: ${resolvedName}`);\n }\n\n async deleteAll(): Promise<void> {\n await this.stopAll();\n this.processes.clear();\n this.logger.info(\"Deleted all processes\");\n }\n\n get(name: string): ProcessInfo | undefined {\n let resolved: { name: string; managed: ManagedProcess };\n try {\n resolved = this.resolveProcessIdentifier(name);\n } catch {\n return undefined;\n }\n\n return {\n id: resolved.managed.id,\n name: resolved.managed.config.name,\n status: resolved.managed.status,\n pid: resolved.managed.spawned?.proc.pid,\n exitCode: resolved.managed.exitCode,\n restarts: resolved.managed.restarts,\n startedAt: resolved.managed.startedAt,\n command: resolved.managed.config.command,\n args: resolved.managed.config.args,\n };\n }\n\n async wait(): Promise<void> {\n const donePromises = [...this.processes.values()]\n .filter((m) => m.spawned)\n .map((m) => m.spawned!.done);\n\n await Promise.allSettled(donePromises);\n this.logger.info(\"All processes exited\");\n }\n\n async shutdown(): Promise<void> {\n await this.stopActiveTask();\n await this.stopAll();\n this.closeEnvWatcher();\n if (this.httpServer) {\n this.httpServer.close();\n this.logger.info(\"HTTP server closed\");\n }\n }\n\n private spawnProcess(managed: ManagedProcess): void {\n const { config } = managed;\n\n managed.stopRequested = false;\n this.clearRestartTimer(managed);\n\n const env = loadEnvForProcess({\n configDir: this.configDir ?? this.cwd,\n processName: config.name,\n globalEnvFile: this.globalEnvFile,\n processEnvFile: config.envFile,\n configEnv: config.env,\n });\n\n const spawned = spawnWithLogging({\n cwd: config.cwd ?? this.cwd,\n logFile: join(this.cwd, \"logs\", \"processes\", `${config.name}.log`),\n type: \"process\",\n name: config.name,\n command: config.command,\n args: config.args,\n env,\n });\n\n managed.spawned = spawned;\n managed.status = \"running\";\n managed.startedAt = new Date();\n managed.exitCode = null;\n\n this.logger.info(\n `Started process: ${config.name} (pid: ${spawned.proc.pid})`,\n );\n\n const handleExit = (exitCode: number | null, error?: unknown) => {\n managed.exitCode = exitCode;\n managed.status =\n exitCode === null || exitCode === undefined || exitCode === 0\n ? \"stopped\"\n : \"errored\";\n const now = Date.now();\n const startedAt = managed.startedAt?.getTime() ?? now;\n const uptimeMs = Math.max(0, now - startedAt);\n const resetMs =\n (config.restartResetSeconds ?? DEFAULT_RESTART_RESET_SECONDS) * 1000;\n\n if (uptimeMs >= resetMs) {\n managed.restartAttempts = 0;\n }\n\n if (exitCode === null || exitCode === undefined) {\n this.logger.info(`Process ${config.name} stopped`);\n } else if (exitCode !== 0) {\n this.logger.info(\n `Process ${config.name} exited with code ${exitCode}`,\n );\n } else {\n this.logger.info(`Process ${config.name} exited successfully`);\n }\n\n if (error) {\n this.logger.error(`Process ${config.name} failed with error: ${error}`);\n }\n\n const policy = config.restartPolicy ?? \"on-failure\";\n const isFailure =\n exitCode === null || exitCode === undefined || exitCode !== 0;\n const shouldRestart =\n !managed.stopRequested &&\n (policy === \"always\" || (policy === \"on-failure\" && isFailure));\n\n if (!shouldRestart) {\n return;\n }\n\n const maxRestarts = config.maxRestarts ?? DEFAULT_RESTART_MAX;\n if (managed.restartAttempts >= maxRestarts) {\n managed.status = \"errored\";\n this.logger.warn(\n `Process ${config.name} reached restart limit (${maxRestarts}), not restarting`,\n );\n return;\n }\n\n managed.restartAttempts += 1;\n managed.restarts += 1;\n const minDelay =\n config.restartMinDelayMs ?? this.restartMinDelayMsGlobal;\n const maxDelay =\n config.restartMaxDelayMs ?? this.restartMaxDelayMsGlobal;\n const delay = Math.min(\n maxDelay,\n minDelay * Math.pow(2, managed.restartAttempts - 1),\n );\n\n this.logger.warn(\n `Restarting process ${config.name} in ${delay}ms (attempt ${managed.restartAttempts}/${maxRestarts})`,\n );\n\n managed.restartTimer = setTimeout(() => {\n managed.restartTimer = null;\n if (!managed.stopRequested) {\n this.spawnProcess(managed);\n }\n }, delay);\n };\n\n spawned.done\n .then((exitCode) => handleExit(exitCode))\n .catch((error) => {\n const exitCode = spawned.proc.exitCode ?? 1;\n handleExit(exitCode, error);\n });\n }\n\n private async runTasks(tasks: Array<ProcessConfig>): Promise<void> {\n for (const task of tasks) {\n const env = loadEnvForProcess({\n configDir: this.configDir ?? this.cwd,\n processName: task.name,\n globalEnvFile: this.globalEnvFile,\n processEnvFile: task.envFile,\n configEnv: task.env,\n });\n\n const taskAbortController = new AbortController();\n this.taskAbortController = taskAbortController;\n\n const spawned = spawnWithLogging({\n cwd: task.cwd ?? this.cwd,\n logFile: join(this.cwd, \"logs\", \"tasks\", `${task.name}.log`),\n type: \"task\",\n name: task.name,\n command: task.command,\n args: task.args,\n env,\n abortSignal: taskAbortController.signal,\n });\n\n this.currentTask = spawned;\n this.currentTaskName = task.name;\n\n let exitCode: number | null;\n try {\n exitCode = await spawned.done;\n } finally {\n this.currentTask = null;\n this.currentTaskName = null;\n this.taskAbortController = null;\n }\n\n if (exitCode !== 0) {\n this.logger.error(\n `Task ${task.name} failed with exit code ${exitCode}`,\n );\n throw new Error(`Task ${task.name} failed`);\n }\n }\n\n if (tasks.length > 0) {\n this.logger.info(\"All tasks completed\");\n }\n }\n\n private clearRestartTimer(managed: ManagedProcess): void {\n if (managed.restartTimer) {\n clearTimeout(managed.restartTimer);\n managed.restartTimer = null;\n }\n }\n\n private async stopActiveTask(\n timeoutMs = DEFAULT_STOP_TIMEOUT_MS,\n ): Promise<void> {\n if (!this.currentTask) return;\n\n const taskName = this.currentTaskName ?? \"unknown\";\n this.logger.info(`Stopping task: ${taskName} (SIGTERM)`);\n this.currentTask.kill(\"SIGTERM\");\n\n const exitedGracefully = await Promise.race([\n this.currentTask.done.then(() => true).catch(() => true),\n new Promise<false>((resolve) =>\n setTimeout(() => resolve(false), timeoutMs),\n ),\n ]);\n\n if (!exitedGracefully) {\n this.logger.warn(\n `Task ${taskName} did not stop gracefully, sending SIGKILL`,\n );\n this.currentTask.kill(\"SIGKILL\");\n await this.currentTask.done.catch(() => {});\n }\n }\n\n private abortActiveTask(signal: NodeJS.Signals): void {\n if (this.taskAbortController && !this.taskAbortController.signal.aborted) {\n this.taskAbortController.abort();\n }\n if (this.currentTask) {\n this.currentTask.kill(signal);\n }\n }\n\n private setupSignalHandlers(): void {\n if (this.signalHandlersSetup) return;\n this.signalHandlersSetup = true;\n\n const signals = [\"SIGINT\", \"SIGTERM\"] as const;\n for (const signal of signals) {\n process.on(signal, () => {\n process.stdout.write(\"\\r\\x1b[K\");\n this.logger.info(`Received ${signal}, shutting down...`);\n this.abortActiveTask(signal);\n this.shutdown().then(() => process.exit(0));\n });\n }\n }\n\n private setupEnvWatchers(): void {\n if (this.envWatcher) return;\n if (!this.restartOnEnvChangeGlobal) return;\n\n const watchDir = this.configDir ?? this.cwd;\n this.envWatcher = watch(watchDir, (event, filename) => {\n if (!filename) return;\n const envFile = filename.toString();\n if (!envFile.startsWith(\".env\")) return;\n if (event !== \"change\" && event !== \"rename\") return;\n\n if (envFile === \".env\") {\n this.scheduleEnvRestart(\"global\", () => {\n this.logger.info(\"Detected .env change, scheduling restarts\");\n for (const managed of this.processes.values()) {\n this.scheduleEnvRestartForProcess(managed);\n }\n });\n return;\n }\n\n if (!envFile.startsWith(\".env.\")) return;\n const processName = envFile.slice(\".env.\".length);\n if (!processName) return;\n\n this.scheduleEnvRestart(processName, () => {\n const managed = this.processes.get(processName);\n if (!managed) return;\n this.logger.info(\n `Detected ${envFile} change, scheduling restart for ${processName}`,\n );\n this.scheduleEnvRestartForProcess(managed);\n });\n });\n }\n\n private closeEnvWatcher(): void {\n if (this.envWatcher) {\n this.envWatcher.close();\n this.envWatcher = null;\n }\n for (const timer of this.envChangeTimers.values()) {\n clearTimeout(timer);\n }\n this.envChangeTimers.clear();\n for (const timer of this.envRestartTimers.values()) {\n clearTimeout(timer);\n }\n this.envRestartTimers.clear();\n }\n\n private scheduleEnvRestart(key: string, restart: () => void): void {\n const existing = this.envChangeTimers.get(key);\n if (existing) clearTimeout(existing);\n const timer = setTimeout(() => {\n this.envChangeTimers.delete(key);\n restart();\n }, 250);\n this.envChangeTimers.set(key, timer);\n }\n\n private scheduleEnvRestartForProcess(managed: ManagedProcess): void {\n if (!this.restartOnEnvChangeGlobal) return;\n if (managed.config.restartOnEnvChange === false) return;\n if (managed.status !== \"running\") return;\n const name = managed.config.name;\n const existing = this.envRestartTimers.get(name);\n if (existing) clearTimeout(existing);\n\n const delayMs =\n managed.config.restartMinDelayMs ?? this.restartMinDelayMsGlobal;\n\n const timer = setTimeout(() => {\n this.envRestartTimers.delete(name);\n this.restart(name).catch((error) => {\n this.logger.error(\n `Failed to restart ${name} after env change: ${error}`,\n );\n });\n }, Math.max(0, delayMs));\n\n this.envRestartTimers.set(name, timer);\n this.logger.info(\n `Restarting ${name} after env change in ${Math.max(0, delayMs)}ms`,\n );\n }\n\n private resolveProcessIdentifier(nameOrId: string): {\n name: string;\n managed: ManagedProcess;\n } {\n const byName = this.processes.get(nameOrId);\n if (byName) {\n return { name: nameOrId, managed: byName };\n }\n\n if (/^\\d+$/.test(nameOrId)) {\n const id = Number(nameOrId);\n const name = this.processIds.get(id);\n if (name) {\n const managed = this.processes.get(name);\n if (managed) {\n return { name, managed };\n }\n }\n }\n\n throw new Error(`Process ${nameOrId} not found`);\n }\n}\n"],"mappings":"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"}
|