pidnap 0.0.0-dev.3 → 0.0.0-dev.5

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { _ as ProcessDefinitionSchema, a as TaskStateSchema, c as CronProcessStateSchema, h as RestartingProcessStateSchema, i as TaskList, m as RestartingProcessOptionsSchema, n as EnvManager, o as CronProcess, p as RestartingProcess, s as CronProcessOptionsSchema, t as logger, y as treeKill } from "./logger-BF3KrCIK.mjs";
2
+ import { _ as ProcessDefinition, a as TaskStateSchema, c as CronProcessState, h as RestartingProcessState, i as TaskList, m as RestartingProcessOptions, n as EnvManager, o as CronProcess, p as RestartingProcess, s as CronProcessOptions, t as logger } from "./logger-BU2RmetS.mjs";
3
3
  import { createClient } from "./client.mjs";
4
4
  import { Command } from "commander";
5
5
  import { createServer } from "node:http";
@@ -9,11 +9,8 @@ import { RPCHandler } from "@orpc/server/node";
9
9
  import { implement, onError } from "@orpc/server";
10
10
  import * as v from "valibot";
11
11
  import { oc } from "@orpc/contract";
12
- import { exec } from "node:child_process";
13
12
  import { cwd } from "node:process";
14
13
  import { mkdirSync } from "node:fs";
15
- import { promisify } from "node:util";
16
- import { tsImport } from "tsx/esm/api";
17
14
  import Table from "cli-table3";
18
15
 
19
16
  //#region src/api/contract.ts
@@ -34,12 +31,12 @@ const ManagerStatusSchema = v.object({
34
31
  });
35
32
  const RestartingProcessInfoSchema = v.object({
36
33
  name: v.string(),
37
- state: RestartingProcessStateSchema,
34
+ state: RestartingProcessState,
38
35
  restarts: v.number()
39
36
  });
40
37
  const CronProcessInfoSchema = v.object({
41
38
  name: v.string(),
42
- state: CronProcessStateSchema,
39
+ state: CronProcessState,
43
40
  runCount: v.number(),
44
41
  failCount: v.number(),
45
42
  nextRun: v.nullable(v.string())
@@ -50,12 +47,12 @@ const TaskEntryInfoSchema = v.object({
50
47
  processNames: v.array(v.string())
51
48
  });
52
49
  const manager = { status: oc$1.output(ManagerStatusSchema) };
53
- const processes$1 = {
50
+ const processes = {
54
51
  get: oc$1.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),
55
52
  list: oc$1.output(v.array(RestartingProcessInfoSchema)),
56
53
  add: oc$1.input(v.object({
57
54
  name: v.string(),
58
- definition: ProcessDefinitionSchema
55
+ definition: ProcessDefinition
59
56
  })).output(RestartingProcessInfoSchema),
60
57
  start: oc$1.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),
61
58
  stop: oc$1.input(v.object({ target: ResourceTarget })).output(RestartingProcessInfoSchema),
@@ -65,7 +62,7 @@ const processes$1 = {
65
62
  })).output(RestartingProcessInfoSchema),
66
63
  reload: oc$1.input(v.object({
67
64
  target: ResourceTarget,
68
- definition: ProcessDefinitionSchema,
65
+ definition: ProcessDefinition,
69
66
  restartImmediately: v.optional(v.boolean())
70
67
  })).output(RestartingProcessInfoSchema),
71
68
  remove: oc$1.input(v.object({ target: ResourceTarget })).output(v.object({ success: v.boolean() }))
@@ -75,7 +72,7 @@ const tasks$1 = {
75
72
  list: oc$1.output(v.array(TaskEntryInfoSchema)),
76
73
  add: oc$1.input(v.object({
77
74
  name: v.string(),
78
- definition: ProcessDefinitionSchema
75
+ definition: ProcessDefinition
79
76
  })).output(TaskEntryInfoSchema),
80
77
  remove: oc$1.input(v.object({ target: ResourceTarget })).output(TaskEntryInfoSchema)
81
78
  };
@@ -88,7 +85,7 @@ const crons$1 = {
88
85
  };
89
86
  const api = {
90
87
  manager,
91
- processes: processes$1,
88
+ processes,
92
89
  tasks: tasks$1,
93
90
  crons: crons$1
94
91
  };
@@ -213,76 +210,52 @@ const router = os.router({
213
210
  }
214
211
  });
215
212
 
216
- //#endregion
217
- //#region src/port-utils.ts
218
- const execAsync = promisify(exec);
219
- /**
220
- * Kill any process listening on the specified port, including all descendants.
221
- */
222
- async function killProcessOnPort(port) {
223
- try {
224
- const { stdout } = await execAsync(`lsof -ti tcp:${port}`);
225
- const pids = stdout.trim().split("\n").filter(Boolean).map((p) => parseInt(p, 10)).filter((p) => !isNaN(p));
226
- if (pids.length === 0) return false;
227
- for (const pid of pids) try {
228
- await treeKill(pid, "SIGKILL");
229
- } catch {}
230
- return true;
231
- } catch {
232
- return false;
233
- }
234
- }
235
-
236
213
  //#endregion
237
214
  //#region src/manager.ts
238
- const HttpServerConfigSchema = v.object({
215
+ const HttpServerConfig = v.object({
239
216
  host: v.optional(v.string()),
240
217
  port: v.optional(v.number()),
241
218
  authToken: v.optional(v.string())
242
219
  });
243
- const CronProcessEntrySchema = v.object({
220
+ const CronProcessEntry = v.object({
244
221
  name: v.string(),
245
- definition: ProcessDefinitionSchema,
246
- options: CronProcessOptionsSchema,
222
+ definition: ProcessDefinition,
223
+ options: CronProcessOptions,
247
224
  envFile: v.optional(v.string())
248
225
  });
249
- const EnvReloadDelaySchema = v.union([
226
+ const EnvReloadDelay = v.union([
250
227
  v.number(),
251
228
  v.boolean(),
252
229
  v.literal("immediately")
253
230
  ]);
254
- const RestartingProcessEntrySchema = v.object({
231
+ const RestartingProcessEntry = v.object({
255
232
  name: v.string(),
256
- definition: ProcessDefinitionSchema,
257
- options: v.optional(RestartingProcessOptionsSchema),
233
+ definition: ProcessDefinition,
234
+ options: v.optional(RestartingProcessOptions),
258
235
  envFile: v.optional(v.string()),
259
- envReloadDelay: v.optional(EnvReloadDelaySchema),
260
- port: v.optional(v.number())
236
+ envReloadDelay: v.optional(EnvReloadDelay)
261
237
  });
262
- const TaskEntrySchema = v.object({
238
+ const TaskEntry = v.object({
263
239
  name: v.string(),
264
- definition: ProcessDefinitionSchema,
240
+ definition: ProcessDefinition,
265
241
  envFile: v.optional(v.string())
266
242
  });
267
- const ManagerConfigSchema = v.object({
268
- http: v.optional(HttpServerConfigSchema),
243
+ const ManagerConfig = v.object({
244
+ http: v.optional(HttpServerConfig),
269
245
  cwd: v.optional(v.string()),
270
246
  logDir: v.optional(v.string()),
271
247
  env: v.optional(v.record(v.string(), v.string())),
272
- envFiles: v.optional(v.record(v.string(), v.string())),
273
- tasks: v.optional(v.array(TaskEntrySchema)),
274
- crons: v.optional(v.array(CronProcessEntrySchema)),
275
- processes: v.optional(v.array(RestartingProcessEntrySchema))
248
+ envFile: v.optional(v.string()),
249
+ tasks: v.optional(v.array(TaskEntry)),
250
+ crons: v.optional(v.array(CronProcessEntry)),
251
+ processes: v.optional(v.array(RestartingProcessEntry))
276
252
  });
277
253
  const DEFAULT_RESTART_OPTIONS = { restartPolicy: "always" };
278
254
  const SHUTDOWN_TIMEOUT_MS = 15e3;
279
- const SHUTDOWN_SIGNALS = ["SIGINT", "SIGTERM"];
280
- const SHUTDOWN_EXIT_CODE = 0;
281
255
  var Manager = class {
282
256
  config;
283
257
  logger;
284
258
  envManager;
285
- envFileKeyByProcess = /* @__PURE__ */ new Map();
286
259
  _state = "idle";
287
260
  taskList = null;
288
261
  cronProcesses = /* @__PURE__ */ new Map();
@@ -291,48 +264,93 @@ var Manager = class {
291
264
  processEnvReloadConfig = /* @__PURE__ */ new Map();
292
265
  envReloadTimers = /* @__PURE__ */ new Map();
293
266
  envChangeUnsubscribe = null;
294
- processPortConfig = /* @__PURE__ */ new Map();
295
267
  signalHandlers = /* @__PURE__ */ new Map();
296
268
  shutdownPromise = null;
297
269
  isShuttingDown = false;
298
270
  constructor(config, logger) {
271
+ const cwd$1 = config.cwd ?? cwd();
299
272
  this.config = config;
300
273
  this.logger = logger;
301
- this.logDir = config.logDir ?? join(cwd(), "logs");
274
+ this.logDir = config.logDir ?? join(cwd$1, "logs");
302
275
  this.ensureLogDirs();
276
+ this.validateConfigNames();
277
+ const customEnvFiles = {};
278
+ for (const task of config.tasks ?? []) if (task.envFile) customEnvFiles[task.name] = task.envFile;
279
+ for (const cron of config.crons ?? []) if (cron.envFile) customEnvFiles[cron.name] = cron.envFile;
280
+ for (const proc of config.processes ?? []) if (proc.envFile) customEnvFiles[proc.name] = proc.envFile;
303
281
  this.envManager = new EnvManager({
304
- cwd: config.cwd,
305
- files: config.envFiles,
306
- watch: true
282
+ cwd: cwd$1,
283
+ globalEnvFile: config.envFile,
284
+ customEnvFiles
285
+ });
286
+ this.envChangeUnsubscribe = this.envManager.onChange((event) => this.handleEnvChange(event));
287
+ for (const signal of ["SIGINT", "SIGTERM"]) {
288
+ const handler = () => this.handleSignal(signal);
289
+ this.signalHandlers.set(signal, handler);
290
+ process.on(signal, handler);
291
+ }
292
+ }
293
+ validateConfigNames() {
294
+ const allNames = [];
295
+ for (const task of this.config.tasks ?? []) allNames.push({
296
+ name: task.name,
297
+ type: "task"
307
298
  });
308
- this.envChangeUnsubscribe = this.envManager.onChange((changedKeys) => {
309
- this.handleEnvChange(changedKeys);
299
+ for (const cron of this.config.crons ?? []) allNames.push({
300
+ name: cron.name,
301
+ type: "cron"
310
302
  });
311
- this.registerShutdownHandlers();
303
+ for (const proc of this.config.processes ?? []) allNames.push({
304
+ name: proc.name,
305
+ type: "process"
306
+ });
307
+ const seen = /* @__PURE__ */ new Map();
308
+ for (const { name, type } of allNames) {
309
+ const existingType = seen.get(name);
310
+ if (existingType) throw new Error(`Duplicate name "${name}" found: already used as ${existingType}, cannot use as ${type}. Names must be globally unique across tasks, crons, and processes.`);
311
+ seen.set(name, type);
312
+ }
312
313
  }
313
314
  /**
314
- * Merge global env with process-specific env and apply cwd inheritance
315
- * Merge order: .env (global), config.env (global), .env.<processName>, processEnvFile, definition.env
315
+ * Check if a name is already used by any task, cron, or process
316
316
  */
317
- applyDefaults(processName, definition, envFile) {
318
- const envFromFiles = this.envManager.getEnvVars(processName);
319
- let envFromCustomFile = {};
320
- if (envFile) {
321
- let key = this.envFileKeyByProcess.get(processName);
322
- if (!key) {
323
- key = `custom:${processName}`;
324
- this.envFileKeyByProcess.set(processName, key);
325
- this.envManager.registerFile(key, envFile);
326
- }
327
- envFromCustomFile = this.envManager.getEnvForKey(key);
317
+ isNameUsed(name) {
318
+ for (const task of this.config.tasks ?? []) if (task.name === name) return {
319
+ used: true,
320
+ type: "task"
321
+ };
322
+ if (this.taskList) {
323
+ for (const task of this.taskList.tasks) for (const proc of task.processes) if (proc.name === name) return {
324
+ used: true,
325
+ type: "task"
326
+ };
328
327
  }
328
+ if (this.cronProcesses.has(name)) return {
329
+ used: true,
330
+ type: "cron"
331
+ };
332
+ for (const cron of this.config.crons ?? []) if (cron.name === name) return {
333
+ used: true,
334
+ type: "cron"
335
+ };
336
+ if (this.restartingProcesses.has(name)) return {
337
+ used: true,
338
+ type: "process"
339
+ };
340
+ for (const proc of this.config.processes ?? []) if (proc.name === name) return {
341
+ used: true,
342
+ type: "process"
343
+ };
344
+ return { used: false };
345
+ }
346
+ applyDefaults(processName, definition) {
347
+ const envVarsFromManager = this.envManager.getEnvVars(processName);
329
348
  return {
330
349
  ...definition,
331
350
  cwd: definition.cwd ?? this.config.cwd,
332
351
  env: {
333
- ...envFromFiles,
352
+ ...envVarsFromManager,
334
353
  ...this.config.env,
335
- ...envFromCustomFile,
336
354
  ...definition.env
337
355
  }
338
356
  };
@@ -346,14 +364,6 @@ var Manager = class {
346
364
  cronLogFile(name) {
347
365
  return join(this.logDir, "cron", `${name}.log`);
348
366
  }
349
- /**
350
- * Kill any process running on the configured port for a process
351
- */
352
- async killPortForProcess(processName) {
353
- const port = this.processPortConfig.get(processName);
354
- if (port === void 0) return;
355
- if (await killProcessOnPort(port)) this.logger.info(`Killed process on port ${port} before starting "${processName}"`);
356
- }
357
367
  ensureLogDirs() {
358
368
  mkdirSync(this.logDir, { recursive: true });
359
369
  mkdirSync(join(this.logDir, "process"), { recursive: true });
@@ -363,18 +373,21 @@ var Manager = class {
363
373
  /**
364
374
  * Handle env file changes
365
375
  */
366
- handleEnvChange(changedKeys) {
376
+ handleEnvChange(event) {
367
377
  if (this._state !== "running") return;
368
- this.logger.info(`Env files changed for keys: ${changedKeys.join(", ")}`);
369
- const affectedProcesses = /* @__PURE__ */ new Set();
370
- for (const key of changedKeys) if (key === "global") for (const processName of this.restartingProcesses.keys()) affectedProcesses.add(processName);
371
- else {
372
- if (this.restartingProcesses.has(key)) affectedProcesses.add(key);
373
- for (const [processName, customKey] of this.envFileKeyByProcess.entries()) if (customKey === key && this.restartingProcesses.has(processName)) affectedProcesses.add(processName);
378
+ if (event.type === "global") {
379
+ this.logger.info("Global env file changed, reloading all processes as per their policies");
380
+ for (const processName of this.restartingProcesses.keys()) {
381
+ const reloadDelay = this.processEnvReloadConfig.get(processName);
382
+ if (reloadDelay === false) continue;
383
+ this.scheduleProcessReload(processName, reloadDelay);
384
+ }
385
+ return;
374
386
  }
375
- for (const processName of affectedProcesses) {
387
+ if (event.type === "process") {
388
+ const processName = event.key;
376
389
  const reloadDelay = this.processEnvReloadConfig.get(processName);
377
- if (reloadDelay === false) continue;
390
+ if (reloadDelay === false) return;
378
391
  this.scheduleProcessReload(processName, reloadDelay);
379
392
  }
380
393
  }
@@ -411,7 +424,7 @@ var Manager = class {
411
424
  this.logger.warn(`Process config for "${processName}" not found`);
412
425
  return;
413
426
  }
414
- const updatedDefinition = this.applyDefaults(processName, processConfig.definition, processConfig.envFile);
427
+ const updatedDefinition = this.applyDefaults(processName, processConfig.definition);
415
428
  await proc.reload(updatedDefinition, true);
416
429
  }
417
430
  get state() {
@@ -490,7 +503,6 @@ var Manager = class {
490
503
  async startProcessByTarget(target) {
491
504
  const proc = this.getProcessByTarget(target);
492
505
  if (!proc) throw new Error(`Process not found: ${target}`);
493
- await this.killPortForProcess(proc.name);
494
506
  proc.start();
495
507
  return proc;
496
508
  }
@@ -509,7 +521,6 @@ var Manager = class {
509
521
  async restartProcessByTarget(target, force = false) {
510
522
  const proc = this.getProcessByTarget(target);
511
523
  if (!proc) throw new Error(`Process not found: ${target}`);
512
- await this.killPortForProcess(proc.name);
513
524
  await proc.restart(force);
514
525
  return proc;
515
526
  }
@@ -539,7 +550,10 @@ var Manager = class {
539
550
  * Add a task to the task list
540
551
  * Creates the task list if it doesn't exist and starts it
541
552
  */
542
- addTask(name, definition) {
553
+ addTask(name, definition, envFile) {
554
+ const nameCheck = this.isNameUsed(name);
555
+ if (nameCheck.used) throw new Error(`Name "${name}" is already used as ${nameCheck.type}. Names must be globally unique across tasks, crons, and processes.`);
556
+ if (envFile) this.envManager.registerFile(name, envFile);
543
557
  if (!this.taskList) this.taskList = new TaskList("runtime", this.logger.child("tasks", { logFile: this.taskLogFile("tasks") }), void 0, (processName) => {
544
558
  return this.taskLogFile(processName);
545
559
  });
@@ -567,15 +581,13 @@ var Manager = class {
567
581
  /**
568
582
  * Add a restarting process at runtime
569
583
  */
570
- async addProcess(name, definition, options, envReloadDelay, port) {
571
- if (this.restartingProcesses.has(name)) throw new Error(`Process "${name}" already exists`);
584
+ async addProcess(name, definition, options, envReloadDelay, envFile) {
585
+ const nameCheck = this.isNameUsed(name);
586
+ if (nameCheck.used) throw new Error(`Name "${name}" is already used as ${nameCheck.type}. Names must be globally unique across tasks, crons, and processes.`);
587
+ if (envFile) this.envManager.registerFile(name, envFile);
572
588
  const processLogger = this.logger.child(name, { logFile: this.processLogFile(name) });
573
589
  const restartingProcess = new RestartingProcess(name, this.applyDefaults(name, definition), options ?? DEFAULT_RESTART_OPTIONS, processLogger);
574
590
  this.restartingProcesses.set(name, restartingProcess);
575
- if (port !== void 0) {
576
- this.processPortConfig.set(name, port);
577
- await this.killPortForProcess(name);
578
- }
579
591
  restartingProcess.start();
580
592
  this.processEnvReloadConfig.set(name, envReloadDelay ?? 5e3);
581
593
  this.logger.info(`Added and started restarting process: ${name}`);
@@ -621,7 +633,7 @@ var Manager = class {
621
633
  this.logger.info(`Running initialization tasks`);
622
634
  this.taskList = new TaskList("init", this.logger.child("tasks"), this.config.tasks.map((task) => ({
623
635
  name: task.name,
624
- process: this.applyDefaults(task.name, task.definition, task.envFile)
636
+ process: this.applyDefaults(task.name, task.definition)
625
637
  })), (processName) => {
626
638
  return this.taskLogFile(processName);
627
639
  });
@@ -637,19 +649,15 @@ var Manager = class {
637
649
  }
638
650
  if (this.config.crons) for (const entry of this.config.crons) {
639
651
  const processLogger = this.logger.child(entry.name, { logFile: this.cronLogFile(entry.name) });
640
- const cronProcess = new CronProcess(entry.name, this.applyDefaults(entry.name, entry.definition, entry.envFile), entry.options, processLogger);
652
+ const cronProcess = new CronProcess(entry.name, this.applyDefaults(entry.name, entry.definition), entry.options, processLogger);
641
653
  this.cronProcesses.set(entry.name, cronProcess);
642
654
  cronProcess.start();
643
655
  this.logger.info(`Started cron process: ${entry.name}`);
644
656
  }
645
657
  if (this.config.processes) for (const entry of this.config.processes) {
646
658
  const processLogger = this.logger.child(entry.name, { logFile: this.processLogFile(entry.name) });
647
- const restartingProcess = new RestartingProcess(entry.name, this.applyDefaults(entry.name, entry.definition, entry.envFile), entry.options ?? DEFAULT_RESTART_OPTIONS, processLogger);
659
+ const restartingProcess = new RestartingProcess(entry.name, this.applyDefaults(entry.name, entry.definition), entry.options ?? DEFAULT_RESTART_OPTIONS, processLogger);
648
660
  this.restartingProcesses.set(entry.name, restartingProcess);
649
- if (entry.port !== void 0) {
650
- this.processPortConfig.set(entry.name, entry.port);
651
- await this.killPortForProcess(entry.name);
652
- }
653
661
  restartingProcess.start();
654
662
  this.processEnvReloadConfig.set(entry.name, entry.envReloadDelay ?? 5e3);
655
663
  this.logger.info(`Started restarting process: ${entry.name}`);
@@ -663,7 +671,6 @@ var Manager = class {
663
671
  async stop(timeout) {
664
672
  if (this._state === "idle" || this._state === "stopped") {
665
673
  this._state = "stopped";
666
- this.unregisterShutdownHandlers();
667
674
  return;
668
675
  }
669
676
  this._state = "stopping";
@@ -674,42 +681,13 @@ var Manager = class {
674
681
  this.envChangeUnsubscribe();
675
682
  this.envChangeUnsubscribe = null;
676
683
  }
677
- this.envManager.dispose();
684
+ this.envManager.close();
678
685
  if (this.taskList) await this.taskList.stop(timeout);
679
686
  const cronStopPromises = Array.from(this.cronProcesses.values()).map((p) => p.stop(timeout));
680
687
  const restartingStopPromises = Array.from(this.restartingProcesses.values()).map((p) => p.stop(timeout));
681
688
  await Promise.all([...cronStopPromises, ...restartingStopPromises]);
682
689
  this._state = "stopped";
683
690
  this.logger.info(`Manager stopped`);
684
- this.unregisterShutdownHandlers();
685
- }
686
- /**
687
- * Register signal handlers for graceful shutdown
688
- * Called automatically by constructor
689
- */
690
- registerShutdownHandlers() {
691
- if (this.signalHandlers.size > 0) {
692
- this.logger.warn(`Shutdown handlers already registered`);
693
- return;
694
- }
695
- for (const signal of SHUTDOWN_SIGNALS) {
696
- const handler = () => this.handleSignal(signal);
697
- this.signalHandlers.set(signal, handler);
698
- process.on(signal, handler);
699
- this.logger.debug(`Registered handler for ${signal}`);
700
- }
701
- this.logger.info(`Shutdown handlers registered for signals: ${SHUTDOWN_SIGNALS.join(", ")}`);
702
- }
703
- /**
704
- * Unregister signal handlers for graceful shutdown
705
- */
706
- unregisterShutdownHandlers() {
707
- if (this.signalHandlers.size === 0) return;
708
- for (const [signal, handler] of this.signalHandlers.entries()) {
709
- process.off(signal, handler);
710
- this.logger.debug(`Unregistered handler for ${signal}`);
711
- }
712
- this.signalHandlers.clear();
713
691
  }
714
692
  /**
715
693
  * Wait for shutdown to complete (useful for keeping process alive)
@@ -753,8 +731,8 @@ var Manager = class {
753
731
  this.isShuttingDown = true;
754
732
  this.shutdownPromise = this.performShutdown();
755
733
  this.shutdownPromise.then(() => {
756
- this.logger.info(`Exiting with code ${SHUTDOWN_EXIT_CODE}`);
757
- process.exit(SHUTDOWN_EXIT_CODE);
734
+ this.logger.info(`Exiting with code 0`);
735
+ process.exit();
758
736
  }).catch((err) => {
759
737
  this.logger.error(`Shutdown error:`, err);
760
738
  process.exit(1);
@@ -780,19 +758,31 @@ var Manager = class {
780
758
  }
781
759
  };
782
760
 
761
+ //#endregion
762
+ //#region src/utils.ts
763
+ async function tImport(path) {
764
+ try {
765
+ return await import(path);
766
+ } catch {
767
+ const { tsImport } = await import("tsx/esm/api");
768
+ return await tsImport(path, { parentURL: import.meta.url });
769
+ }
770
+ }
771
+
783
772
  //#endregion
784
773
  //#region src/cli.ts
785
774
  const program = new Command();
786
- program.name("pidnap").description("Process manager with init system capabilities").version("0.0.0-dev.1");
775
+ program.name("pidnap").description("Process manager with init system capabilities").version("0.0.0-dev.5");
787
776
  program.command("init").description("Initialize and run the process manager with config file").option("-c, --config [path]", "Path to config file", "pidnap.config.ts").action(async (options) => {
777
+ process.title = "pidnap";
788
778
  const initLogger = logger({ name: "pidnap" });
789
779
  try {
790
780
  const configUrl = pathToFileURL(resolve(process.cwd(), options.config)).href;
791
- const configModule = await tsImport(configUrl, { parentURL: import.meta.url });
781
+ const configModule = await tImport(configUrl);
792
782
  const rawConfig = configModule.default.default || configModule.default || configModule.config || {};
793
- const config = v.parse(ManagerConfigSchema, rawConfig);
794
- const host = config.http?.host ?? "localhost";
795
- const port = config.http?.port ?? 3e3;
783
+ const config = v.parse(ManagerConfig, rawConfig);
784
+ const host = config.http?.host ?? "127.0.0.1";
785
+ const port = config.http?.port ?? 9876;
796
786
  const authToken = config.http?.authToken;
797
787
  const managerLogger = logger({
798
788
  name: "pidnap",
@@ -816,7 +806,7 @@ program.command("init").description("Initialize and run the process manager with
816
806
  });
817
807
  if (matched) return;
818
808
  res.statusCode = 404;
819
- res.end("Not found");
809
+ res.end("Not found\n");
820
810
  });
821
811
  server.listen(port, host, async () => {
822
812
  managerLogger.info(`pidnap RPC server running on http://${host}:${port}`);
@@ -836,7 +826,7 @@ program.command("init").description("Initialize and run the process manager with
836
826
  process.exit(1);
837
827
  }
838
828
  });
839
- program.command("status").description("Show manager status").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (options) => {
829
+ program.command("status").description("Show manager status").option("-u, --url <url>", "RPC server URL").action(async (options) => {
840
830
  try {
841
831
  const status = await createClient(options.url).manager.status();
842
832
  const table = new Table({ head: [
@@ -857,8 +847,8 @@ program.command("status").description("Show manager status").option("-u, --url <
857
847
  process.exit(1);
858
848
  }
859
849
  });
860
- const processes = program.command("processes").description("Manage restarting processes");
861
- processes.command("list").description("List restarting processes").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (options) => {
850
+ const processGroup = program.command("process").description("Manage restarting processes");
851
+ processGroup.command("list").description("List restarting processes").option("-u, --url <url>", "RPC server URL").action(async (options) => {
862
852
  try {
863
853
  const processes = await createClient(options.url).processes.list();
864
854
  const table = new Table({ head: [
@@ -877,7 +867,7 @@ processes.command("list").description("List restarting processes").option("-u, -
877
867
  process.exit(1);
878
868
  }
879
869
  });
880
- processes.command("get").description("Get a restarting process by name or index").argument("<target>", "Process name or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
870
+ processGroup.command("get").description("Get a restarting process by name or index").argument("<target>", "Process name or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
881
871
  try {
882
872
  const proc = await createClient(options.url).processes.get({ target: parseTarget(target) });
883
873
  const table = new Table({ head: [
@@ -896,7 +886,7 @@ processes.command("get").description("Get a restarting process by name or index"
896
886
  process.exit(1);
897
887
  }
898
888
  });
899
- processes.command("add").description("Add a restarting process").requiredOption("-n, --name <name>", "Process name").requiredOption("-d, --definition <json>", "Process definition JSON").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (options) => {
889
+ processGroup.command("add").description("Add a restarting process").requiredOption("-n, --name <name>", "Process name").requiredOption("-d, --definition <json>", "Process definition JSON").option("-u, --url <url>", "RPC server URL").action(async (options) => {
900
890
  try {
901
891
  const client = createClient(options.url);
902
892
  const definition = parseDefinition(options.definition);
@@ -920,7 +910,7 @@ processes.command("add").description("Add a restarting process").requiredOption(
920
910
  process.exit(1);
921
911
  }
922
912
  });
923
- processes.command("start").description("Start a restarting process").argument("<target>", "Process name or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
913
+ processGroup.command("start").description("Start a restarting process").argument("<target>", "Process name or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
924
914
  try {
925
915
  const proc = await createClient(options.url).processes.start({ target: parseTarget(target) });
926
916
  const table = new Table({ head: [
@@ -939,7 +929,7 @@ processes.command("start").description("Start a restarting process").argument("<
939
929
  process.exit(1);
940
930
  }
941
931
  });
942
- processes.command("stop").description("Stop a restarting process").argument("<target>", "Process name or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
932
+ processGroup.command("stop").description("Stop a restarting process").argument("<target>", "Process name or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
943
933
  try {
944
934
  const proc = await createClient(options.url).processes.stop({ target: parseTarget(target) });
945
935
  const table = new Table({ head: [
@@ -958,7 +948,7 @@ processes.command("stop").description("Stop a restarting process").argument("<ta
958
948
  process.exit(1);
959
949
  }
960
950
  });
961
- processes.command("restart").description("Restart a restarting process").argument("<target>", "Process name or index").option("-f, --force", "Force restart").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
951
+ processGroup.command("restart").description("Restart a restarting process").argument("<target>", "Process name or index").option("-f, --force", "Force restart").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
962
952
  try {
963
953
  const proc = await createClient(options.url).processes.restart({
964
954
  target: parseTarget(target),
@@ -980,7 +970,7 @@ processes.command("restart").description("Restart a restarting process").argumen
980
970
  process.exit(1);
981
971
  }
982
972
  });
983
- processes.command("remove").description("Remove a restarting process").argument("<target>", "Process name or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
973
+ processGroup.command("remove").description("Remove a restarting process").argument("<target>", "Process name or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
984
974
  try {
985
975
  await createClient(options.url).processes.remove({ target: parseTarget(target) });
986
976
  console.log("Process removed");
@@ -990,7 +980,7 @@ processes.command("remove").description("Remove a restarting process").argument(
990
980
  }
991
981
  });
992
982
  const crons = program.command("crons").description("Manage cron processes");
993
- crons.command("list").description("List cron processes").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (options) => {
983
+ crons.command("list").description("List cron processes").option("-u, --url <url>", "RPC server URL").action(async (options) => {
994
984
  try {
995
985
  const crons = await createClient(options.url).crons.list();
996
986
  const table = new Table({ head: [
@@ -1013,7 +1003,7 @@ crons.command("list").description("List cron processes").option("-u, --url <url>
1013
1003
  process.exit(1);
1014
1004
  }
1015
1005
  });
1016
- crons.command("get").description("Get a cron process by name or index").argument("<target>", "Cron name or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
1006
+ crons.command("get").description("Get a cron process by name or index").argument("<target>", "Cron name or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
1017
1007
  try {
1018
1008
  const cron = await createClient(options.url).crons.get({ target: parseTarget(target) });
1019
1009
  const table = new Table({ head: [
@@ -1036,7 +1026,7 @@ crons.command("get").description("Get a cron process by name or index").argument
1036
1026
  process.exit(1);
1037
1027
  }
1038
1028
  });
1039
- crons.command("trigger").description("Trigger a cron process").argument("<target>", "Cron name or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
1029
+ crons.command("trigger").description("Trigger a cron process").argument("<target>", "Cron name or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
1040
1030
  try {
1041
1031
  const cron = await createClient(options.url).crons.trigger({ target: parseTarget(target) });
1042
1032
  const table = new Table({ head: [
@@ -1059,7 +1049,7 @@ crons.command("trigger").description("Trigger a cron process").argument("<target
1059
1049
  process.exit(1);
1060
1050
  }
1061
1051
  });
1062
- crons.command("start").description("Start a cron process").argument("<target>", "Cron name or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
1052
+ crons.command("start").description("Start a cron process").argument("<target>", "Cron name or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
1063
1053
  try {
1064
1054
  const cron = await createClient(options.url).crons.start({ target: parseTarget(target) });
1065
1055
  const table = new Table({ head: [
@@ -1082,7 +1072,7 @@ crons.command("start").description("Start a cron process").argument("<target>",
1082
1072
  process.exit(1);
1083
1073
  }
1084
1074
  });
1085
- crons.command("stop").description("Stop a cron process").argument("<target>", "Cron name or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
1075
+ crons.command("stop").description("Stop a cron process").argument("<target>", "Cron name or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
1086
1076
  try {
1087
1077
  const cron = await createClient(options.url).crons.stop({ target: parseTarget(target) });
1088
1078
  const table = new Table({ head: [
@@ -1106,7 +1096,7 @@ crons.command("stop").description("Stop a cron process").argument("<target>", "C
1106
1096
  }
1107
1097
  });
1108
1098
  const tasks = program.command("tasks").description("Manage tasks");
1109
- tasks.command("list").description("List tasks").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (options) => {
1099
+ tasks.command("list").description("List tasks").option("-u, --url <url>", "RPC server URL").action(async (options) => {
1110
1100
  try {
1111
1101
  const tasks = await createClient(options.url).tasks.list();
1112
1102
  const table = new Table({ head: [
@@ -1125,7 +1115,7 @@ tasks.command("list").description("List tasks").option("-u, --url <url>", "RPC s
1125
1115
  process.exit(1);
1126
1116
  }
1127
1117
  });
1128
- tasks.command("get").description("Get a task by id or index").argument("<target>", "Task id or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
1118
+ tasks.command("get").description("Get a task by id or index").argument("<target>", "Task id or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
1129
1119
  try {
1130
1120
  const task = await createClient(options.url).tasks.get({ target: parseTarget(target) });
1131
1121
  const table = new Table({ head: [
@@ -1144,7 +1134,7 @@ tasks.command("get").description("Get a task by id or index").argument("<target>
1144
1134
  process.exit(1);
1145
1135
  }
1146
1136
  });
1147
- tasks.command("add").description("Add a task").requiredOption("-n, --name <name>", "Task name").requiredOption("-d, --definition <json>", "Process definition JSON").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (options) => {
1137
+ tasks.command("add").description("Add a task").requiredOption("-n, --name <name>", "Task name").requiredOption("-d, --definition <json>", "Process definition JSON").option("-u, --url <url>", "RPC server URL").action(async (options) => {
1148
1138
  try {
1149
1139
  const client = createClient(options.url);
1150
1140
  const definition = parseDefinition(options.definition);
@@ -1168,7 +1158,7 @@ tasks.command("add").description("Add a task").requiredOption("-n, --name <name>
1168
1158
  process.exit(1);
1169
1159
  }
1170
1160
  });
1171
- tasks.command("remove").description("Remove a task by id or index").argument("<target>", "Task id or index").option("-u, --url <url>", "RPC server URL", "http://localhost:3000/rpc").action(async (target, options) => {
1161
+ tasks.command("remove").description("Remove a task by id or index").argument("<target>", "Task id or index").option("-u, --url <url>", "RPC server URL").action(async (target, options) => {
1172
1162
  try {
1173
1163
  const task = await createClient(options.url).tasks.remove({ target: parseTarget(target) });
1174
1164
  const table = new Table({ head: [
@@ -1195,7 +1185,7 @@ function parseTarget(value) {
1195
1185
  function parseDefinition(raw) {
1196
1186
  try {
1197
1187
  const parsed = JSON.parse(raw);
1198
- return v.parse(ProcessDefinitionSchema, parsed);
1188
+ return v.parse(ProcessDefinition, parsed);
1199
1189
  } catch (error) {
1200
1190
  console.error("Invalid --definition JSON. Expected a ProcessDefinition.");
1201
1191
  throw error;