@agimon-ai/po-runner 0.0.0-alpha.46 → 0.0.0-alpha.48

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.
Files changed (92) hide show
  1. package/dist/bashTool.d.mts +1 -1
  2. package/dist/bin/cli.cjs +3 -3
  3. package/dist/bin/cli.mjs +3 -3
  4. package/dist/bin/runnerHost.cjs +6 -34
  5. package/dist/bin/runnerHost.cjs.map +1 -1
  6. package/dist/bin/runnerHost.mjs +6 -34
  7. package/dist/bin/runnerHost.mjs.map +1 -1
  8. package/dist/{cliApp-DkhnAFR1.cjs → cliApp-GKcqD6CT.cjs} +2 -2
  9. package/dist/{cliApp-DkhnAFR1.cjs.map → cliApp-GKcqD6CT.cjs.map} +1 -1
  10. package/dist/{cliApp-DjU_M0ho.mjs → cliApp-g7APOwcU.mjs} +2 -2
  11. package/dist/{cliApp-DjU_M0ho.mjs.map → cliApp-g7APOwcU.mjs.map} +1 -1
  12. package/dist/cliApp.cjs +1 -1
  13. package/dist/cliApp.mjs +1 -1
  14. package/dist/extensions/mcp.cjs +2 -2
  15. package/dist/extensions/mcp.cjs.map +1 -1
  16. package/dist/extensions/mcp.mjs +2 -2
  17. package/dist/extensions/mcp.mjs.map +1 -1
  18. package/dist/extensions/server.cjs +18 -22
  19. package/dist/extensions/server.cjs.map +1 -1
  20. package/dist/extensions/server.mjs +18 -22
  21. package/dist/extensions/server.mjs.map +1 -1
  22. package/dist/{index-CuYtXV5V.d.mts → index-CoBOAigd.d.mts} +5 -2
  23. package/dist/index-CoBOAigd.d.mts.map +1 -0
  24. package/dist/{index-DyxtAbaK.d.mts → index-DoeHCVHQ.d.mts} +2 -2
  25. package/dist/{index-DyxtAbaK.d.mts.map → index-DoeHCVHQ.d.mts.map} +1 -1
  26. package/dist/{index-CKc2Fi5k.d.mts → index-EFeluIMR.d.mts} +8 -2
  27. package/dist/index-EFeluIMR.d.mts.map +1 -0
  28. package/dist/index.cjs +1 -1
  29. package/dist/index.d.mts +2 -2
  30. package/dist/index.mjs +1 -1
  31. package/dist/{launcher-rqDsTfQZ.cjs → launcher-7Ho1Rp7S.cjs} +2 -2
  32. package/dist/{launcher-rqDsTfQZ.cjs.map → launcher-7Ho1Rp7S.cjs.map} +1 -1
  33. package/dist/{launcher-Cl-4uv52.mjs → launcher-Dw4pXVq9.mjs} +2 -2
  34. package/dist/{launcher-Cl-4uv52.mjs.map → launcher-Dw4pXVq9.mjs.map} +1 -1
  35. package/dist/launcher.cjs +1 -1
  36. package/dist/launcher.mjs +1 -1
  37. package/dist/{ptyHost-BJp7tYhN.mjs → ptyHost-D8zJaYg3.mjs} +2 -2
  38. package/dist/{ptyHost-BJp7tYhN.mjs.map → ptyHost-D8zJaYg3.mjs.map} +1 -1
  39. package/dist/{ptyHost-DJTgxbjn.cjs → ptyHost-DfXM9RCJ.cjs} +2 -2
  40. package/dist/{ptyHost-DJTgxbjn.cjs.map → ptyHost-DfXM9RCJ.cjs.map} +1 -1
  41. package/dist/ptyHost.cjs +1 -1
  42. package/dist/ptyHost.mjs +1 -1
  43. package/dist/{runnerDependencies-s6vttB3n.cjs → runnerDependencies-k4qqvHxN.cjs} +6 -6
  44. package/dist/{runnerDependencies-s6vttB3n.cjs.map → runnerDependencies-k4qqvHxN.cjs.map} +1 -1
  45. package/dist/{runnerDependencies-Ca9jPi1A.mjs → runnerDependencies-nf5jRCfc.mjs} +6 -6
  46. package/dist/{runnerDependencies-Ca9jPi1A.mjs.map → runnerDependencies-nf5jRCfc.mjs.map} +1 -1
  47. package/dist/runnerDependencies.cjs +1 -1
  48. package/dist/runnerDependencies.d.mts +1 -1
  49. package/dist/runnerDependencies.mjs +1 -1
  50. package/dist/{runnerPaths-DMYG6DZ3.mjs → runnerPaths-BiO-WuKm.mjs} +4 -4
  51. package/dist/runnerPaths-BiO-WuKm.mjs.map +1 -0
  52. package/dist/{runnerPaths-BW-ebQfP.cjs → runnerPaths-Dkl43cmS.cjs} +5 -5
  53. package/dist/runnerPaths-Dkl43cmS.cjs.map +1 -0
  54. package/dist/runnerPaths.cjs +1 -1
  55. package/dist/runnerPaths.d.mts.map +1 -1
  56. package/dist/runnerPaths.mjs +1 -1
  57. package/dist/{runnerRegistry-sjOISoLL.cjs → runnerRegistry-BucrRJsG.cjs} +23 -4
  58. package/dist/runnerRegistry-BucrRJsG.cjs.map +1 -0
  59. package/dist/{runnerRegistry-B434F5lR.mjs → runnerRegistry-_B6LXMhS.mjs} +22 -3
  60. package/dist/runnerRegistry-_B6LXMhS.mjs.map +1 -0
  61. package/dist/runnerRegistry.cjs +1 -1
  62. package/dist/runnerRegistry.d.mts +1 -1
  63. package/dist/runnerRegistry.mjs +1 -1
  64. package/dist/session-3oSU3QjI.mjs +26 -0
  65. package/dist/session-3oSU3QjI.mjs.map +1 -0
  66. package/dist/session-CRql5E2T.cjs +25 -0
  67. package/dist/session-CRql5E2T.cjs.map +1 -0
  68. package/dist/{session-BvqzhXZY.cjs → session-D_nH4XhL.cjs} +16 -2
  69. package/dist/session-D_nH4XhL.cjs.map +1 -0
  70. package/dist/session-IgtrocZP.mjs +8 -0
  71. package/dist/session-IgtrocZP.mjs.map +1 -0
  72. package/dist/session.cjs +3 -2
  73. package/dist/session.d.mts +2 -0
  74. package/dist/session.d.mts.map +1 -1
  75. package/dist/session.mjs +3 -3
  76. package/dist/{type-COYoMif1.d.mts → type-CRhqDus6.d.mts} +2 -2
  77. package/dist/{type-COYoMif1.d.mts.map → type-CRhqDus6.d.mts.map} +1 -1
  78. package/dist/types.d.mts +1 -1
  79. package/package.json +16 -16
  80. package/dist/index-CKc2Fi5k.d.mts.map +0 -1
  81. package/dist/index-CuYtXV5V.d.mts.map +0 -1
  82. package/dist/runnerPaths-BW-ebQfP.cjs.map +0 -1
  83. package/dist/runnerPaths-DMYG6DZ3.mjs.map +0 -1
  84. package/dist/runnerRegistry-B434F5lR.mjs.map +0 -1
  85. package/dist/runnerRegistry-sjOISoLL.cjs.map +0 -1
  86. package/dist/session-BKYnH4R6.mjs +0 -18
  87. package/dist/session-BKYnH4R6.mjs.map +0 -1
  88. package/dist/session-BvqzhXZY.cjs.map +0 -1
  89. package/dist/session-DE2Acesp.mjs +0 -6
  90. package/dist/session-DE2Acesp.mjs.map +0 -1
  91. package/dist/session-SfIPtdZO.cjs +0 -11
  92. package/dist/session-SfIPtdZO.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import { n as BashRunResult } from "./bashRunService-CmdDhaTn.mjs";
2
2
  import { a as ResultBudget, s as ToolResult } from "./bashResult-CfOPpOHN.mjs";
3
- import { t as createHeadlessBashTool } from "./index-DyxtAbaK.mjs";
3
+ import { t as createHeadlessBashTool } from "./index-DoeHCVHQ.mjs";
4
4
  //#region src/services/runnerResult/index.d.ts
5
5
  declare function formatRunnerResult(result: BashRunResult, budget?: ResultBudget): Promise<ToolResult>;
6
6
  //#endregion
package/dist/bin/cli.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_runnerSequenceStore = require("../runnerSequenceStore-Ck6M-8r_.cjs");
4
- const require_cliApp = require("../cliApp-DkhnAFR1.cjs");
5
- const require_session = require("../session-BvqzhXZY.cjs");
6
- const require_runnerDependencies = require("../runnerDependencies-s6vttB3n.cjs");
4
+ const require_cliApp = require("../cliApp-GKcqD6CT.cjs");
5
+ const require_session = require("../session-D_nH4XhL.cjs");
6
+ const require_runnerDependencies = require("../runnerDependencies-k4qqvHxN.cjs");
7
7
  let node_fs_promises = require("node:fs/promises");
8
8
  let node_url = require("node:url");
9
9
  let _agimon_ai_po_core_telemetry = require("@agimon-ai/po-core/telemetry");
package/dist/bin/cli.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as createRunnerSequenceStore } from "../runnerSequenceStore-CaY7S_Sd.mjs";
3
- import { n as runnerCliHelp, t as runCli } from "../cliApp-DjU_M0ho.mjs";
4
- import { t as inheritedSessionId } from "../session-BKYnH4R6.mjs";
5
- import { t as createRunnerDependencies } from "../runnerDependencies-Ca9jPi1A.mjs";
3
+ import { n as runnerCliHelp, t as runCli } from "../cliApp-g7APOwcU.mjs";
4
+ import { t as inheritedSessionId } from "../session-3oSU3QjI.mjs";
5
+ import { t as createRunnerDependencies } from "../runnerDependencies-nf5jRCfc.mjs";
6
6
  import { realpath } from "node:fs/promises";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { createPoTelemetry } from "@agimon-ai/po-core/telemetry";
@@ -12,33 +12,16 @@ node_os = require_rolldown_runtime.__toESM(node_os, 1);
12
12
  //#region src/bin/runnerHost.ts
13
13
  const POLL_MS = 10;
14
14
  const FORWARDED_SIGNAL_GRACE_MS = 2e3;
15
- const HOST_FAILED_EVENT = "po_runner.host_failed";
16
15
  const INVALID_COMMAND_SPEC_ERROR = "invalid runner command spec";
17
16
  async function main(argv = process.argv.slice(2)) {
18
17
  if (argv[0] === "--observe-sessions") {
19
18
  require_services_lifeline_client.observeSessions(cleanupSessions);
20
19
  return 0;
21
20
  }
22
- const startedAt = Date.now();
23
- const { createPoTelemetry } = await import("@agimon-ai/po-core/telemetry");
24
- const telemetry = createPoTelemetry({
25
- serviceName: "po-runner-host",
26
- packageName: "@agimon-ai/po-runner",
27
- env: process.env,
28
- enableLogs: true,
29
- enableTraces: true
30
- });
31
21
  const [specPath, gatePath, exitPath] = argv;
32
- let spec;
33
- try {
34
- if (!specPath || !gatePath || !exitPath) throw new Error("runnerHost requires spec, gate, and exit paths");
35
- spec = await readSpec(specPath);
36
- await waitForGate(gatePath);
37
- } catch (error) {
38
- await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });
39
- await telemetry.shutdown();
40
- throw error;
41
- }
22
+ if (!specPath || !gatePath || !exitPath) throw new Error("runnerHost requires spec, gate, and exit paths");
23
+ const spec = await readSpec(specPath);
24
+ await waitForGate(gatePath);
42
25
  let child;
43
26
  let forwardedSignal = null;
44
27
  let escalationTimer;
@@ -89,21 +72,10 @@ async function main(argv = process.argv.slice(2)) {
89
72
  stopReason: "owner session ended"
90
73
  } : {}
91
74
  });
92
- const exitCode = result.code !== null ? result.code : result.signal ? 128 + node_os.default.constants.signals[result.signal] : 1;
93
- await telemetry.recordEvent("po_runner.host_finished", {
94
- outcome: result.signal || forwardedSignal ? "signaled" : exitCode === 0 ? "completed" : "failed",
95
- "runner.exit_code": exitCode,
96
- ...result.signal || forwardedSignal ? { "runner.signal": result.signal ?? forwardedSignal ?? "" } : {},
97
- duration_ms: Date.now() - startedAt
98
- });
99
- return exitCode;
100
- } catch (error) {
101
- await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });
102
- throw error;
75
+ return result.code !== null ? result.code : result.signal ? 128 + node_os.default.constants.signals[result.signal] : 1;
103
76
  } finally {
104
77
  detachLifeline();
105
78
  if (escalationTimer) clearTimeout(escalationTimer);
106
- await telemetry.shutdown();
107
79
  }
108
80
  }
109
81
  async function readSpec(target) {
@@ -177,8 +149,8 @@ async function readMarker(marker) {
177
149
  async function loadCleaner() {
178
150
  const [{ cleanupRunnerSession }, { RunnerPaths }, { createDefaultProcessRegistry }] = await Promise.all([
179
151
  Promise.resolve().then(() => require("../runnerHost-BXk4sPBP.cjs")).then((n) => n.runnerHost_exports),
180
- Promise.resolve().then(() => require("../runnerPaths-BW-ebQfP.cjs")).then((n) => n.runnerPaths_exports),
181
- Promise.resolve().then(() => require("../runnerRegistry-sjOISoLL.cjs")).then((n) => n.runnerRegistry_exports)
152
+ Promise.resolve().then(() => require("../runnerPaths-Dkl43cmS.cjs")).then((n) => n.runnerPaths_exports),
153
+ Promise.resolve().then(() => require("../runnerRegistry-BucrRJsG.cjs")).then((n) => n.runnerRegistry_exports)
182
154
  ]);
183
155
  const registries = /* @__PURE__ */ new Map();
184
156
  process.once("beforeExit", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"runnerHost.cjs","names":["spawn","watchOwner","LIFELINE_ENV","os","fs","pathToFileURL","REGISTRY_PATH_ENV"],"sources":["../../src/bin/runnerHost.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport type { PoTelemetry } from '@agimon-ai/po-core/telemetry';\n\nimport { OBSERVE_SESSIONS_FLAG } from '../constants/lifeline';\nimport { REGISTRY_PATH_ENV } from '../constants/runnerRegistry';\nimport { LIFELINE_ENV, observeSessions, watchOwner } from '../services/lifeline/client';\nimport type { ProcessRegistryPort } from '../services/runnerRegistry';\nimport type { LifelineWatch } from '../types/lifeline';\n\n// Everything heavier loads on first use: the observer idles for its owner's whole life and\n// these imports alone would more than double its memory.\n\ninterface CommandSpec {\n command: string;\n cwd: string;\n env: Record<string, string>;\n}\n\nconst POLL_MS = 10;\nconst FORWARDED_SIGNAL_GRACE_MS = 2_000;\nconst HOST_FAILED_EVENT = 'po_runner.host_failed';\nconst INVALID_COMMAND_SPEC_ERROR = 'invalid runner command spec';\n\nexport async function main(argv: readonly string[] = process.argv.slice(2)): Promise<number> {\n if (argv[0] === OBSERVE_SESSIONS_FLAG) {\n observeSessions(cleanupSessions);\n return 0;\n }\n const startedAt = Date.now();\n const { createPoTelemetry } = await import('@agimon-ai/po-core/telemetry');\n const telemetry = createPoTelemetry({\n serviceName: 'po-runner-host',\n packageName: '@agimon-ai/po-runner',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const [specPath, gatePath, exitPath] = argv;\n let spec: CommandSpec;\n try {\n if (!specPath || !gatePath || !exitPath) throw new Error('runnerHost requires spec, gate, and exit paths');\n spec = await readSpec(specPath);\n await waitForGate(gatePath);\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n await telemetry.shutdown();\n throw error;\n }\n let child: ChildProcess | undefined;\n let forwardedSignal: NodeJS.Signals | null = null;\n let escalationTimer: NodeJS.Timeout | undefined;\n let ownerLost = false;\n const signalChild = (signal: NodeJS.Signals): void => {\n if (!child?.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch {\n child.kill(signal);\n }\n };\n const forward = (signal: NodeJS.Signals): void => {\n forwardedSignal = signal;\n signalChild(signal);\n escalationTimer ??= setTimeout(() => signalChild('SIGKILL'), FORWARDED_SIGNAL_GRACE_MS);\n };\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP'];\n for (const signal of signals) process.once(signal, () => forward(signal));\n\n child = spawn(spec.command, {\n cwd: spec.cwd,\n env: spec.env,\n shell: '/bin/bash',\n stdio: 'inherit',\n detached: true,\n });\n if (forwardedSignal) forward(forwardedSignal);\n\n // This process supervises the command for its whole life, so it is also the\n // right place to notice an owner that died without stopping it.\n //\n // The lifeline comes from the spec rather than this process's environment: an\n // rmux server outlives the session that started it, so its own environment can\n // still name a previous session's socket.\n const detachLifeline = watchOwner(() => {\n ownerLost = true;\n forward('SIGTERM');\n }, spec.env[LIFELINE_ENV]);\n\n try {\n const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {\n child?.once('error', reject);\n child?.once('exit', (code, signal) => resolve({ code, signal: signal ?? forwardedSignal }));\n });\n await writeAtomic(exitPath, {\n ...result,\n ...(ownerLost ? { reason: 'stopped', terminationReason: 'owner_lost', stopReason: 'owner session ended' } : {}),\n });\n const exitCode = result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;\n await telemetry.recordEvent('po_runner.host_finished', {\n outcome: result.signal || forwardedSignal ? 'signaled' : exitCode === 0 ? 'completed' : 'failed',\n 'runner.exit_code': exitCode,\n ...(result.signal || forwardedSignal ? { 'runner.signal': result.signal ?? forwardedSignal ?? '' } : {}),\n duration_ms: Date.now() - startedAt,\n });\n return exitCode;\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n throw error;\n } finally {\n detachLifeline();\n if (escalationTimer) clearTimeout(escalationTimer);\n await telemetry.shutdown();\n }\n}\n\nasync function readSpec(target: string): Promise<CommandSpec> {\n const value: unknown = JSON.parse(await fs.readFile(target, 'utf8'));\n if (typeof value !== 'object' || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);\n const spec = value as Partial<CommandSpec>;\n if (typeof spec.command !== 'string' || typeof spec.cwd !== 'string' || !spec.env) {\n throw new Error(INVALID_COMMAND_SPEC_ERROR);\n }\n return { command: spec.command, cwd: spec.cwd, env: spec.env };\n}\n\nasync function waitForGate(target: string): Promise<void> {\n for (;;) {\n try {\n await fs.access(target);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n }\n}\n\nasync function writeAtomic(target: string, value: unknown): Promise<void> {\n const temporary = `${target}.${process.pid}.tmp`;\n await fs.writeFile(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n await fs.rename(temporary, target);\n}\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n },\n );\n}\n\n/** Loaded on the first session an owner leaves behind, then shared by every later one. */\nlet cleaner: Promise<(watch: LifelineWatch) => Promise<void>> | undefined;\nlet telemetry: Promise<PoTelemetry> | undefined;\n\n/**\n * A separate process survives SIGKILL of the owner, even if that session has no active runners.\n * The owner cleans up each session it ends with its own shared services and removes its marker,\n * so only a session still marked with its generation is left here; the rest load nothing.\n */\nasync function cleanupSessions(watches: readonly LifelineWatch[]): Promise<void> {\n const failures: { watch: LifelineWatch; error: unknown }[] = [];\n // One session failing to clean up must not stop the others.\n for (const watch of watches) {\n try {\n // The same fence cleanupRunnerSession applies, checked before anything loads for it.\n if ((await readMarker(watch.marker)) !== watch.generation) continue;\n const clean = await (cleaner ??= loadCleaner());\n await clean(watch);\n } catch (error) {\n failures.push({ watch, error });\n }\n }\n const [first] = failures;\n if (!first) return;\n const log = await (telemetry ??= loadTelemetry(first.watch.environment));\n for (const { watch, error } of failures) {\n await log.recordError('po_runner.observer_cleanup_failed', error, { 'session.id': watch.sessionId });\n }\n await log.flush();\n throw new AggregateError(\n failures.map(({ error }) => error),\n `Could not clean up runner sessions ${failures.map(({ watch }) => watch.sessionId).join(', ')}`,\n );\n}\n\nasync function readMarker(marker: string): Promise<string | undefined> {\n try {\n return await fs.readFile(marker, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\n/** One registry per registry path, held until the observer runs out of work. */\nasync function loadCleaner(): Promise<(watch: LifelineWatch) => Promise<void>> {\n const [{ cleanupRunnerSession }, { RunnerPaths }, { createDefaultProcessRegistry }] = await Promise.all([\n import('../services/runnerHost'),\n import('../services/runnerPaths'),\n import('../services/runnerRegistry'),\n ]);\n const registries = new Map<string | undefined, ProcessRegistryPort>();\n process.once('beforeExit', () => {\n for (const registry of registries.values()) registry.close();\n });\n return (watch) => {\n const registryPath = watch.environment[REGISTRY_PATH_ENV];\n let registry = registries.get(registryPath);\n if (!registry) {\n registry = createDefaultProcessRegistry(watch.environment);\n registries.set(registryPath, registry);\n }\n return cleanupRunnerSession({\n sessionId: watch.sessionId,\n paths: new RunnerPaths(watch.repository, watch.environment),\n processRegistry: registry,\n ownerPid: watch.ownerPid,\n generation: watch.generation,\n });\n };\n}\n\nasync function loadTelemetry(environment: LifelineWatch['environment']): Promise<PoTelemetry> {\n const { createPoTelemetry } = await import('@agimon-ai/po-core/telemetry');\n const shared = createPoTelemetry({\n serviceName: 'po-runner-host',\n packageName: '@agimon-ai/po-runner',\n env: environment,\n enableLogs: true,\n });\n process.once('beforeExit', () => void shared.shutdown());\n return shared;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runnerHost.cjs","names":["spawn","watchOwner","LIFELINE_ENV","os","fs","pathToFileURL","REGISTRY_PATH_ENV"],"sources":["../../src/bin/runnerHost.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport type { PoTelemetry } from '@agimon-ai/po-core/telemetry';\n\nimport { OBSERVE_SESSIONS_FLAG } from '../constants/lifeline';\nimport { REGISTRY_PATH_ENV } from '../constants/runnerRegistry';\nimport { LIFELINE_ENV, observeSessions, watchOwner } from '../services/lifeline/client';\nimport type { ProcessRegistryPort } from '../services/runnerRegistry';\nimport type { LifelineWatch } from '../types/lifeline';\n\n// Everything heavier loads on first use: the observer idles for its owner's whole life and\n// these imports alone would more than double its memory. A command's host loads none of it:\n// the server records the runner when it completes the record from this host's exit file.\n\ninterface CommandSpec {\n command: string;\n cwd: string;\n env: Record<string, string>;\n}\n\nconst POLL_MS = 10;\nconst FORWARDED_SIGNAL_GRACE_MS = 2_000;\nconst INVALID_COMMAND_SPEC_ERROR = 'invalid runner command spec';\n\nexport async function main(argv: readonly string[] = process.argv.slice(2)): Promise<number> {\n if (argv[0] === OBSERVE_SESSIONS_FLAG) {\n observeSessions(cleanupSessions);\n return 0;\n }\n const [specPath, gatePath, exitPath] = argv;\n if (!specPath || !gatePath || !exitPath) throw new Error('runnerHost requires spec, gate, and exit paths');\n const spec = await readSpec(specPath);\n await waitForGate(gatePath);\n let child: ChildProcess | undefined;\n let forwardedSignal: NodeJS.Signals | null = null;\n let escalationTimer: NodeJS.Timeout | undefined;\n let ownerLost = false;\n const signalChild = (signal: NodeJS.Signals): void => {\n if (!child?.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch {\n child.kill(signal);\n }\n };\n const forward = (signal: NodeJS.Signals): void => {\n forwardedSignal = signal;\n signalChild(signal);\n escalationTimer ??= setTimeout(() => signalChild('SIGKILL'), FORWARDED_SIGNAL_GRACE_MS);\n };\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP'];\n for (const signal of signals) process.once(signal, () => forward(signal));\n\n child = spawn(spec.command, {\n cwd: spec.cwd,\n env: spec.env,\n shell: '/bin/bash',\n stdio: 'inherit',\n detached: true,\n });\n if (forwardedSignal) forward(forwardedSignal);\n\n // This process supervises the command for its whole life, so it is also the\n // right place to notice an owner that died without stopping it.\n //\n // The lifeline comes from the spec rather than this process's environment: an\n // rmux server outlives the session that started it, so its own environment can\n // still name a previous session's socket.\n const detachLifeline = watchOwner(() => {\n ownerLost = true;\n forward('SIGTERM');\n }, spec.env[LIFELINE_ENV]);\n\n try {\n const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {\n child?.once('error', reject);\n child?.once('exit', (code, signal) => resolve({ code, signal: signal ?? forwardedSignal }));\n });\n await writeAtomic(exitPath, {\n ...result,\n ...(ownerLost ? { reason: 'stopped', terminationReason: 'owner_lost', stopReason: 'owner session ended' } : {}),\n });\n return result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;\n } finally {\n detachLifeline();\n if (escalationTimer) clearTimeout(escalationTimer);\n }\n}\n\nasync function readSpec(target: string): Promise<CommandSpec> {\n const value: unknown = JSON.parse(await fs.readFile(target, 'utf8'));\n if (typeof value !== 'object' || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);\n const spec = value as Partial<CommandSpec>;\n if (typeof spec.command !== 'string' || typeof spec.cwd !== 'string' || !spec.env) {\n throw new Error(INVALID_COMMAND_SPEC_ERROR);\n }\n return { command: spec.command, cwd: spec.cwd, env: spec.env };\n}\n\nasync function waitForGate(target: string): Promise<void> {\n for (;;) {\n try {\n await fs.access(target);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n }\n}\n\nasync function writeAtomic(target: string, value: unknown): Promise<void> {\n const temporary = `${target}.${process.pid}.tmp`;\n await fs.writeFile(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n await fs.rename(temporary, target);\n}\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n },\n );\n}\n\n/** Loaded on the first session an owner leaves behind, then shared by every later one. */\nlet cleaner: Promise<(watch: LifelineWatch) => Promise<void>> | undefined;\nlet telemetry: Promise<PoTelemetry> | undefined;\n\n/**\n * A separate process survives SIGKILL of the owner, even if that session has no active runners.\n * The owner cleans up each session it ends with its own shared services and removes its marker,\n * so only a session still marked with its generation is left here; the rest load nothing.\n */\nasync function cleanupSessions(watches: readonly LifelineWatch[]): Promise<void> {\n const failures: { watch: LifelineWatch; error: unknown }[] = [];\n // One session failing to clean up must not stop the others.\n for (const watch of watches) {\n try {\n // The same fence cleanupRunnerSession applies, checked before anything loads for it.\n if ((await readMarker(watch.marker)) !== watch.generation) continue;\n const clean = await (cleaner ??= loadCleaner());\n await clean(watch);\n } catch (error) {\n failures.push({ watch, error });\n }\n }\n const [first] = failures;\n if (!first) return;\n const log = await (telemetry ??= loadTelemetry(first.watch.environment));\n for (const { watch, error } of failures) {\n await log.recordError('po_runner.observer_cleanup_failed', error, { 'session.id': watch.sessionId });\n }\n await log.flush();\n throw new AggregateError(\n failures.map(({ error }) => error),\n `Could not clean up runner sessions ${failures.map(({ watch }) => watch.sessionId).join(', ')}`,\n );\n}\n\nasync function readMarker(marker: string): Promise<string | undefined> {\n try {\n return await fs.readFile(marker, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\n/** One registry per registry path, held until the observer runs out of work. */\nasync function loadCleaner(): Promise<(watch: LifelineWatch) => Promise<void>> {\n const [{ cleanupRunnerSession }, { RunnerPaths }, { createDefaultProcessRegistry }] = await Promise.all([\n import('../services/runnerHost'),\n import('../services/runnerPaths'),\n import('../services/runnerRegistry'),\n ]);\n const registries = new Map<string | undefined, ProcessRegistryPort>();\n process.once('beforeExit', () => {\n for (const registry of registries.values()) registry.close();\n });\n return (watch) => {\n const registryPath = watch.environment[REGISTRY_PATH_ENV];\n let registry = registries.get(registryPath);\n if (!registry) {\n registry = createDefaultProcessRegistry(watch.environment);\n registries.set(registryPath, registry);\n }\n return cleanupRunnerSession({\n sessionId: watch.sessionId,\n paths: new RunnerPaths(watch.repository, watch.environment),\n processRegistry: registry,\n ownerPid: watch.ownerPid,\n generation: watch.generation,\n });\n };\n}\n\nasync function loadTelemetry(environment: LifelineWatch['environment']): Promise<PoTelemetry> {\n const { createPoTelemetry } = await import('@agimon-ai/po-core/telemetry');\n const shared = createPoTelemetry({\n serviceName: 'po-runner-host',\n packageName: '@agimon-ai/po-runner',\n env: environment,\n enableLogs: true,\n });\n process.once('beforeExit', () => void shared.shutdown());\n return shared;\n}\n"],"mappings":"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"}
@@ -8,33 +8,16 @@ import os from "node:os";
8
8
  //#region src/bin/runnerHost.ts
9
9
  const POLL_MS = 10;
10
10
  const FORWARDED_SIGNAL_GRACE_MS = 2e3;
11
- const HOST_FAILED_EVENT = "po_runner.host_failed";
12
11
  const INVALID_COMMAND_SPEC_ERROR = "invalid runner command spec";
13
12
  async function main(argv = process.argv.slice(2)) {
14
13
  if (argv[0] === "--observe-sessions") {
15
14
  observeSessions(cleanupSessions);
16
15
  return 0;
17
16
  }
18
- const startedAt = Date.now();
19
- const { createPoTelemetry } = await import("@agimon-ai/po-core/telemetry");
20
- const telemetry = createPoTelemetry({
21
- serviceName: "po-runner-host",
22
- packageName: "@agimon-ai/po-runner",
23
- env: process.env,
24
- enableLogs: true,
25
- enableTraces: true
26
- });
27
17
  const [specPath, gatePath, exitPath] = argv;
28
- let spec;
29
- try {
30
- if (!specPath || !gatePath || !exitPath) throw new Error("runnerHost requires spec, gate, and exit paths");
31
- spec = await readSpec(specPath);
32
- await waitForGate(gatePath);
33
- } catch (error) {
34
- await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });
35
- await telemetry.shutdown();
36
- throw error;
37
- }
18
+ if (!specPath || !gatePath || !exitPath) throw new Error("runnerHost requires spec, gate, and exit paths");
19
+ const spec = await readSpec(specPath);
20
+ await waitForGate(gatePath);
38
21
  let child;
39
22
  let forwardedSignal = null;
40
23
  let escalationTimer;
@@ -85,21 +68,10 @@ async function main(argv = process.argv.slice(2)) {
85
68
  stopReason: "owner session ended"
86
69
  } : {}
87
70
  });
88
- const exitCode = result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;
89
- await telemetry.recordEvent("po_runner.host_finished", {
90
- outcome: result.signal || forwardedSignal ? "signaled" : exitCode === 0 ? "completed" : "failed",
91
- "runner.exit_code": exitCode,
92
- ...result.signal || forwardedSignal ? { "runner.signal": result.signal ?? forwardedSignal ?? "" } : {},
93
- duration_ms: Date.now() - startedAt
94
- });
95
- return exitCode;
96
- } catch (error) {
97
- await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });
98
- throw error;
71
+ return result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;
99
72
  } finally {
100
73
  detachLifeline();
101
74
  if (escalationTimer) clearTimeout(escalationTimer);
102
- await telemetry.shutdown();
103
75
  }
104
76
  }
105
77
  async function readSpec(target) {
@@ -173,8 +145,8 @@ async function readMarker(marker) {
173
145
  async function loadCleaner() {
174
146
  const [{ cleanupRunnerSession }, { RunnerPaths }, { createDefaultProcessRegistry }] = await Promise.all([
175
147
  import("../runnerHost-B2Jiba1U.mjs").then((n) => n.r),
176
- import("../runnerPaths-DMYG6DZ3.mjs").then((n) => n.r),
177
- import("../runnerRegistry-B434F5lR.mjs").then((n) => n.r)
148
+ import("../runnerPaths-BiO-WuKm.mjs").then((n) => n.r),
149
+ import("../runnerRegistry-_B6LXMhS.mjs").then((n) => n.r)
178
150
  ]);
179
151
  const registries = /* @__PURE__ */ new Map();
180
152
  process.once("beforeExit", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"runnerHost.mjs","names":[],"sources":["../../src/bin/runnerHost.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport type { PoTelemetry } from '@agimon-ai/po-core/telemetry';\n\nimport { OBSERVE_SESSIONS_FLAG } from '../constants/lifeline';\nimport { REGISTRY_PATH_ENV } from '../constants/runnerRegistry';\nimport { LIFELINE_ENV, observeSessions, watchOwner } from '../services/lifeline/client';\nimport type { ProcessRegistryPort } from '../services/runnerRegistry';\nimport type { LifelineWatch } from '../types/lifeline';\n\n// Everything heavier loads on first use: the observer idles for its owner's whole life and\n// these imports alone would more than double its memory.\n\ninterface CommandSpec {\n command: string;\n cwd: string;\n env: Record<string, string>;\n}\n\nconst POLL_MS = 10;\nconst FORWARDED_SIGNAL_GRACE_MS = 2_000;\nconst HOST_FAILED_EVENT = 'po_runner.host_failed';\nconst INVALID_COMMAND_SPEC_ERROR = 'invalid runner command spec';\n\nexport async function main(argv: readonly string[] = process.argv.slice(2)): Promise<number> {\n if (argv[0] === OBSERVE_SESSIONS_FLAG) {\n observeSessions(cleanupSessions);\n return 0;\n }\n const startedAt = Date.now();\n const { createPoTelemetry } = await import('@agimon-ai/po-core/telemetry');\n const telemetry = createPoTelemetry({\n serviceName: 'po-runner-host',\n packageName: '@agimon-ai/po-runner',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const [specPath, gatePath, exitPath] = argv;\n let spec: CommandSpec;\n try {\n if (!specPath || !gatePath || !exitPath) throw new Error('runnerHost requires spec, gate, and exit paths');\n spec = await readSpec(specPath);\n await waitForGate(gatePath);\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n await telemetry.shutdown();\n throw error;\n }\n let child: ChildProcess | undefined;\n let forwardedSignal: NodeJS.Signals | null = null;\n let escalationTimer: NodeJS.Timeout | undefined;\n let ownerLost = false;\n const signalChild = (signal: NodeJS.Signals): void => {\n if (!child?.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch {\n child.kill(signal);\n }\n };\n const forward = (signal: NodeJS.Signals): void => {\n forwardedSignal = signal;\n signalChild(signal);\n escalationTimer ??= setTimeout(() => signalChild('SIGKILL'), FORWARDED_SIGNAL_GRACE_MS);\n };\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP'];\n for (const signal of signals) process.once(signal, () => forward(signal));\n\n child = spawn(spec.command, {\n cwd: spec.cwd,\n env: spec.env,\n shell: '/bin/bash',\n stdio: 'inherit',\n detached: true,\n });\n if (forwardedSignal) forward(forwardedSignal);\n\n // This process supervises the command for its whole life, so it is also the\n // right place to notice an owner that died without stopping it.\n //\n // The lifeline comes from the spec rather than this process's environment: an\n // rmux server outlives the session that started it, so its own environment can\n // still name a previous session's socket.\n const detachLifeline = watchOwner(() => {\n ownerLost = true;\n forward('SIGTERM');\n }, spec.env[LIFELINE_ENV]);\n\n try {\n const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {\n child?.once('error', reject);\n child?.once('exit', (code, signal) => resolve({ code, signal: signal ?? forwardedSignal }));\n });\n await writeAtomic(exitPath, {\n ...result,\n ...(ownerLost ? { reason: 'stopped', terminationReason: 'owner_lost', stopReason: 'owner session ended' } : {}),\n });\n const exitCode = result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;\n await telemetry.recordEvent('po_runner.host_finished', {\n outcome: result.signal || forwardedSignal ? 'signaled' : exitCode === 0 ? 'completed' : 'failed',\n 'runner.exit_code': exitCode,\n ...(result.signal || forwardedSignal ? { 'runner.signal': result.signal ?? forwardedSignal ?? '' } : {}),\n duration_ms: Date.now() - startedAt,\n });\n return exitCode;\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n throw error;\n } finally {\n detachLifeline();\n if (escalationTimer) clearTimeout(escalationTimer);\n await telemetry.shutdown();\n }\n}\n\nasync function readSpec(target: string): Promise<CommandSpec> {\n const value: unknown = JSON.parse(await fs.readFile(target, 'utf8'));\n if (typeof value !== 'object' || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);\n const spec = value as Partial<CommandSpec>;\n if (typeof spec.command !== 'string' || typeof spec.cwd !== 'string' || !spec.env) {\n throw new Error(INVALID_COMMAND_SPEC_ERROR);\n }\n return { command: spec.command, cwd: spec.cwd, env: spec.env };\n}\n\nasync function waitForGate(target: string): Promise<void> {\n for (;;) {\n try {\n await fs.access(target);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n }\n}\n\nasync function writeAtomic(target: string, value: unknown): Promise<void> {\n const temporary = `${target}.${process.pid}.tmp`;\n await fs.writeFile(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n await fs.rename(temporary, target);\n}\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n },\n );\n}\n\n/** Loaded on the first session an owner leaves behind, then shared by every later one. */\nlet cleaner: Promise<(watch: LifelineWatch) => Promise<void>> | undefined;\nlet telemetry: Promise<PoTelemetry> | undefined;\n\n/**\n * A separate process survives SIGKILL of the owner, even if that session has no active runners.\n * The owner cleans up each session it ends with its own shared services and removes its marker,\n * so only a session still marked with its generation is left here; the rest load nothing.\n */\nasync function cleanupSessions(watches: readonly LifelineWatch[]): Promise<void> {\n const failures: { watch: LifelineWatch; error: unknown }[] = [];\n // One session failing to clean up must not stop the others.\n for (const watch of watches) {\n try {\n // The same fence cleanupRunnerSession applies, checked before anything loads for it.\n if ((await readMarker(watch.marker)) !== watch.generation) continue;\n const clean = await (cleaner ??= loadCleaner());\n await clean(watch);\n } catch (error) {\n failures.push({ watch, error });\n }\n }\n const [first] = failures;\n if (!first) return;\n const log = await (telemetry ??= loadTelemetry(first.watch.environment));\n for (const { watch, error } of failures) {\n await log.recordError('po_runner.observer_cleanup_failed', error, { 'session.id': watch.sessionId });\n }\n await log.flush();\n throw new AggregateError(\n failures.map(({ error }) => error),\n `Could not clean up runner sessions ${failures.map(({ watch }) => watch.sessionId).join(', ')}`,\n );\n}\n\nasync function readMarker(marker: string): Promise<string | undefined> {\n try {\n return await fs.readFile(marker, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\n/** One registry per registry path, held until the observer runs out of work. */\nasync function loadCleaner(): Promise<(watch: LifelineWatch) => Promise<void>> {\n const [{ cleanupRunnerSession }, { RunnerPaths }, { createDefaultProcessRegistry }] = await Promise.all([\n import('../services/runnerHost'),\n import('../services/runnerPaths'),\n import('../services/runnerRegistry'),\n ]);\n const registries = new Map<string | undefined, ProcessRegistryPort>();\n process.once('beforeExit', () => {\n for (const registry of registries.values()) registry.close();\n });\n return (watch) => {\n const registryPath = watch.environment[REGISTRY_PATH_ENV];\n let registry = registries.get(registryPath);\n if (!registry) {\n registry = createDefaultProcessRegistry(watch.environment);\n registries.set(registryPath, registry);\n }\n return cleanupRunnerSession({\n sessionId: watch.sessionId,\n paths: new RunnerPaths(watch.repository, watch.environment),\n processRegistry: registry,\n ownerPid: watch.ownerPid,\n generation: watch.generation,\n });\n };\n}\n\nasync function loadTelemetry(environment: LifelineWatch['environment']): Promise<PoTelemetry> {\n const { createPoTelemetry } = await import('@agimon-ai/po-core/telemetry');\n const shared = createPoTelemetry({\n serviceName: 'po-runner-host',\n packageName: '@agimon-ai/po-runner',\n env: environment,\n enableLogs: true,\n });\n process.once('beforeExit', () => void shared.shutdown());\n return shared;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runnerHost.mjs","names":[],"sources":["../../src/bin/runnerHost.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport type { PoTelemetry } from '@agimon-ai/po-core/telemetry';\n\nimport { OBSERVE_SESSIONS_FLAG } from '../constants/lifeline';\nimport { REGISTRY_PATH_ENV } from '../constants/runnerRegistry';\nimport { LIFELINE_ENV, observeSessions, watchOwner } from '../services/lifeline/client';\nimport type { ProcessRegistryPort } from '../services/runnerRegistry';\nimport type { LifelineWatch } from '../types/lifeline';\n\n// Everything heavier loads on first use: the observer idles for its owner's whole life and\n// these imports alone would more than double its memory. A command's host loads none of it:\n// the server records the runner when it completes the record from this host's exit file.\n\ninterface CommandSpec {\n command: string;\n cwd: string;\n env: Record<string, string>;\n}\n\nconst POLL_MS = 10;\nconst FORWARDED_SIGNAL_GRACE_MS = 2_000;\nconst INVALID_COMMAND_SPEC_ERROR = 'invalid runner command spec';\n\nexport async function main(argv: readonly string[] = process.argv.slice(2)): Promise<number> {\n if (argv[0] === OBSERVE_SESSIONS_FLAG) {\n observeSessions(cleanupSessions);\n return 0;\n }\n const [specPath, gatePath, exitPath] = argv;\n if (!specPath || !gatePath || !exitPath) throw new Error('runnerHost requires spec, gate, and exit paths');\n const spec = await readSpec(specPath);\n await waitForGate(gatePath);\n let child: ChildProcess | undefined;\n let forwardedSignal: NodeJS.Signals | null = null;\n let escalationTimer: NodeJS.Timeout | undefined;\n let ownerLost = false;\n const signalChild = (signal: NodeJS.Signals): void => {\n if (!child?.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch {\n child.kill(signal);\n }\n };\n const forward = (signal: NodeJS.Signals): void => {\n forwardedSignal = signal;\n signalChild(signal);\n escalationTimer ??= setTimeout(() => signalChild('SIGKILL'), FORWARDED_SIGNAL_GRACE_MS);\n };\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP'];\n for (const signal of signals) process.once(signal, () => forward(signal));\n\n child = spawn(spec.command, {\n cwd: spec.cwd,\n env: spec.env,\n shell: '/bin/bash',\n stdio: 'inherit',\n detached: true,\n });\n if (forwardedSignal) forward(forwardedSignal);\n\n // This process supervises the command for its whole life, so it is also the\n // right place to notice an owner that died without stopping it.\n //\n // The lifeline comes from the spec rather than this process's environment: an\n // rmux server outlives the session that started it, so its own environment can\n // still name a previous session's socket.\n const detachLifeline = watchOwner(() => {\n ownerLost = true;\n forward('SIGTERM');\n }, spec.env[LIFELINE_ENV]);\n\n try {\n const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {\n child?.once('error', reject);\n child?.once('exit', (code, signal) => resolve({ code, signal: signal ?? forwardedSignal }));\n });\n await writeAtomic(exitPath, {\n ...result,\n ...(ownerLost ? { reason: 'stopped', terminationReason: 'owner_lost', stopReason: 'owner session ended' } : {}),\n });\n return result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;\n } finally {\n detachLifeline();\n if (escalationTimer) clearTimeout(escalationTimer);\n }\n}\n\nasync function readSpec(target: string): Promise<CommandSpec> {\n const value: unknown = JSON.parse(await fs.readFile(target, 'utf8'));\n if (typeof value !== 'object' || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);\n const spec = value as Partial<CommandSpec>;\n if (typeof spec.command !== 'string' || typeof spec.cwd !== 'string' || !spec.env) {\n throw new Error(INVALID_COMMAND_SPEC_ERROR);\n }\n return { command: spec.command, cwd: spec.cwd, env: spec.env };\n}\n\nasync function waitForGate(target: string): Promise<void> {\n for (;;) {\n try {\n await fs.access(target);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n }\n}\n\nasync function writeAtomic(target: string, value: unknown): Promise<void> {\n const temporary = `${target}.${process.pid}.tmp`;\n await fs.writeFile(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n await fs.rename(temporary, target);\n}\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n },\n );\n}\n\n/** Loaded on the first session an owner leaves behind, then shared by every later one. */\nlet cleaner: Promise<(watch: LifelineWatch) => Promise<void>> | undefined;\nlet telemetry: Promise<PoTelemetry> | undefined;\n\n/**\n * A separate process survives SIGKILL of the owner, even if that session has no active runners.\n * The owner cleans up each session it ends with its own shared services and removes its marker,\n * so only a session still marked with its generation is left here; the rest load nothing.\n */\nasync function cleanupSessions(watches: readonly LifelineWatch[]): Promise<void> {\n const failures: { watch: LifelineWatch; error: unknown }[] = [];\n // One session failing to clean up must not stop the others.\n for (const watch of watches) {\n try {\n // The same fence cleanupRunnerSession applies, checked before anything loads for it.\n if ((await readMarker(watch.marker)) !== watch.generation) continue;\n const clean = await (cleaner ??= loadCleaner());\n await clean(watch);\n } catch (error) {\n failures.push({ watch, error });\n }\n }\n const [first] = failures;\n if (!first) return;\n const log = await (telemetry ??= loadTelemetry(first.watch.environment));\n for (const { watch, error } of failures) {\n await log.recordError('po_runner.observer_cleanup_failed', error, { 'session.id': watch.sessionId });\n }\n await log.flush();\n throw new AggregateError(\n failures.map(({ error }) => error),\n `Could not clean up runner sessions ${failures.map(({ watch }) => watch.sessionId).join(', ')}`,\n );\n}\n\nasync function readMarker(marker: string): Promise<string | undefined> {\n try {\n return await fs.readFile(marker, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\n/** One registry per registry path, held until the observer runs out of work. */\nasync function loadCleaner(): Promise<(watch: LifelineWatch) => Promise<void>> {\n const [{ cleanupRunnerSession }, { RunnerPaths }, { createDefaultProcessRegistry }] = await Promise.all([\n import('../services/runnerHost'),\n import('../services/runnerPaths'),\n import('../services/runnerRegistry'),\n ]);\n const registries = new Map<string | undefined, ProcessRegistryPort>();\n process.once('beforeExit', () => {\n for (const registry of registries.values()) registry.close();\n });\n return (watch) => {\n const registryPath = watch.environment[REGISTRY_PATH_ENV];\n let registry = registries.get(registryPath);\n if (!registry) {\n registry = createDefaultProcessRegistry(watch.environment);\n registries.set(registryPath, registry);\n }\n return cleanupRunnerSession({\n sessionId: watch.sessionId,\n paths: new RunnerPaths(watch.repository, watch.environment),\n processRegistry: registry,\n ownerPid: watch.ownerPid,\n generation: watch.generation,\n });\n };\n}\n\nasync function loadTelemetry(environment: LifelineWatch['environment']): Promise<PoTelemetry> {\n const { createPoTelemetry } = await import('@agimon-ai/po-core/telemetry');\n const shared = createPoTelemetry({\n serviceName: 'po-runner-host',\n packageName: '@agimon-ai/po-runner',\n env: environment,\n enableLogs: true,\n });\n process.once('beforeExit', () => void shared.shutdown());\n return shared;\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
2
  const require_bashResult = require("./bashResult-B1w7qkQW.cjs");
3
3
  const require_reconcile = require("./reconcile-DjF9T1hN.cjs");
4
4
  const require_runnerSequenceStore = require("./runnerSequenceStore-Ck6M-8r_.cjs");
5
- const require_session = require("./session-BvqzhXZY.cjs");
5
+ const require_session = require("./session-D_nH4XhL.cjs");
6
6
  let node_fs_promises = require("node:fs/promises");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
@@ -304,4 +304,4 @@ Object.defineProperty(exports, "runnerCliHelp", {
304
304
  }
305
305
  });
306
306
 
307
- //# sourceMappingURL=cliApp-DkhnAFR1.cjs.map
307
+ //# sourceMappingURL=cliApp-GKcqD6CT.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cliApp-DkhnAFR1.cjs","names":["inheritedSessionId","isSequenceId","requiredSessionId","formatRunnerLine","open","stat","stopRunnerProcess"],"sources":["../src/constants/cliApp.ts","../src/services/cliApp/index.ts"],"sourcesContent":["export const DEFAULT_LOG_LINES = 200;\nexport const FOLLOW_INTERVAL_MS = 250;\nexport const COMPLETED_STATE = 'completed';\nexport const STOPPED_REASON = 'stopped';\nexport const TERMINATION_SIGNAL = 'SIGTERM';\n","import fs from 'node:fs';\nimport { open, stat } from 'node:fs/promises';\n\nimport {\n COMPLETED_STATE,\n DEFAULT_LOG_LINES,\n FOLLOW_INTERVAL_MS,\n STOPPED_REASON,\n TERMINATION_SIGNAL,\n} from '../../constants/cliApp';\nimport { formatRunnerLine } from '../../services/bashResult';\nimport { inheritedSessionId, requiredSessionId } from '../../services/session';\nimport type { ILauncher } from '../../types/launcher';\nimport type { ILogReader } from '../../types/logReader';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport type { IRunnerRegistry, RunnerRecord } from '../../types/runnerRegistry';\nimport { stopRunnerProcess } from '../reconcile';\nimport { isSequenceId, type RunnerSequenceStore } from '../runnerSequenceStore';\n\nconst MULTIPLEXER_BACKENDS = new Set(['rmux', 'tmux']);\n\nexport interface CliDependencies {\n registry: IRunnerRegistry;\n launcher: ILauncher;\n rmuxBackend: IRmuxBackend;\n logReader: ILogReader;\n env?: NodeJS.ProcessEnv;\n sequences?: RunnerSequenceStore;\n stdout(text: string): void;\n stderr(text: string): void;\n readStdin(): Promise<string>;\n}\n\nexport async function runCli(argv: readonly string[], dependencies: CliDependencies): Promise<number> {\n const [command, ...args] = argv;\n if (command !== undefined && command !== '--help' && command !== '-h' && !inheritedSessionId(dependencies.env)) {\n dependencies.stderr('PI_SESSION_ID is required for po-runner commands');\n return 1;\n }\n if (command && args[0] && isSequenceId(args[0])) return sequenceCommand(command, args, dependencies);\n switch (command) {\n case 'list':\n return list(args, dependencies);\n case 'status':\n return status(args, dependencies);\n case 'logs':\n return logs(args, dependencies);\n case 'stop':\n return stop(args, dependencies);\n case 'stop-all':\n return stopAll(args, dependencies);\n case 'input':\n return input(args, dependencies);\n case '--help':\n case '-h':\n case undefined:\n dependencies.stdout(runnerCliHelp());\n return 0;\n default:\n dependencies.stderr(`Unknown command: ${command}\\n\\n${runnerCliHelp()}`);\n return 1;\n }\n}\n\nasync function sequenceCommand(\n command: string,\n args: readonly string[],\n dependencies: CliDependencies,\n): Promise<number> {\n const store = dependencies.sequences;\n if (!store) {\n dependencies.stderr('Sequence controls are unavailable.');\n return 1;\n }\n const record = await store.get(args[0]!);\n if (!record) {\n dependencies.stderr(`No sequence with id ${args[0]}`);\n return 1;\n }\n if (command === 'stop') {\n if (record.state !== 'running') {\n dependencies.stderr(`Sequence ${record.id} is already completed`);\n return 1;\n }\n await store.requestStop(record.id, option(args, '--reason'));\n dependencies.stdout(`Stop requested for sequence ${record.id}`);\n return 0;\n }\n if (command === 'status') {\n dependencies.stdout(\n [\n `ID: ${record.id}`,\n `Name: ${record.name}`,\n `State: ${record.state}`,\n `Session: ${record.sessionId}`,\n ...(record.outcome ? [`Outcome: ${record.outcome}`] : []),\n ...record.stages.map((stage) => `Stage ${stage.index + 1}: ${stage.state}${stage.id ? ` (${stage.id})` : ''}`),\n ].join('\\n'),\n );\n return 0;\n }\n if (command === 'logs') {\n if (args.includes('--follow')) {\n dependencies.stderr('Sequence logs are finite snapshots; inspect status before reading again.');\n return 1;\n }\n if (record.result)\n dependencies.stdout(\n record.result.content.flatMap((part) => (part.type === 'text' ? [part.text] : [])).join('\\n'),\n );\n else\n dependencies.stdout(\n [\n `Sequence ${record.id} is running.`,\n ...record.stages.filter((stage) => stage.logPath).map((stage) => `${stage.index + 1}. ${stage.logPath}`),\n ].join('\\n'),\n );\n return 0;\n }\n dependencies.stderr(\n command === 'input' ? 'Command sequences are noninteractive.' : `Unknown sequence command: ${command}`,\n );\n return 1;\n}\n\nasync function list(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const sessionId = requiredSessionId(dependencies.env);\n const records = args.includes('--all')\n ? await dependencies.registry.listAll(sessionId)\n : await dependencies.registry.listBySession(sessionId);\n const sequences = (await dependencies.sequences?.list(args.includes('--all'))) ?? [];\n const rows = [\n ...records.map(formatRecord),\n ...sequences.map((record) => `${record.id} ${record.state} sequence ${record.name}`),\n ];\n dependencies.stdout(rows.length > 0 ? rows.join('\\n') : 'No runners');\n return 0;\n}\n\nasync function status(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n dependencies.stdout(\n [\n `ID: ${record.id}`,\n `Name: ${record.name}`,\n `State: ${record.state}`,\n `Backend: ${record.backend}`,\n ...(record.backendTarget ? [`Backend target: ${record.backendTarget}`] : []),\n `PID: ${record.pid}`,\n `Command: ${record.command}`,\n `CWD: ${record.cwd}`,\n `Session: ${record.sessionId}`,\n `Log: ${record.logPath}`,\n ...(record.exit ? [`Exit: ${formatExit(record)}`] : []),\n ].join('\\n'),\n );\n return 0;\n}\n\nasync function logs(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n const lines = positiveInteger(option(args, '--lines')) ?? DEFAULT_LOG_LINES;\n const result = await dependencies.logReader.read(record.logPath, { lines });\n if (!result.exists) {\n dependencies.stderr(`Runner ${record.id} has not written a log yet`);\n return 1;\n }\n if (result.text) dependencies.stdout(result.text);\n if (!args.includes('--follow')) return 0;\n return follow(record, result.fileSize, dependencies);\n}\n\nasync function stop(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n if (record.state !== 'running') {\n dependencies.stderr(`Runner ${record.id} is already ${record.state}`);\n return 1;\n }\n const reason = option(args, '--reason');\n if (!(await stopRecord(record, dependencies, reason))) {\n dependencies.stderr(`Could not stop runner ${record.id}`);\n return 1;\n }\n await dependencies.registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: TERMINATION_SIGNAL,\n ...(reason ? { stopReason: reason } : {}),\n },\n record.sessionId,\n );\n dependencies.stdout(`Stopped ${record.id}\\nLog: ${record.logPath}`);\n return 0;\n}\n\nasync function stopAll(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const sessionId = requiredSessionId(dependencies.env);\n const records = await dependencies.registry.listBySession(sessionId);\n const reason = option(args, '--reason');\n for (const sequence of (await dependencies.sequences?.list()) ?? []) {\n await dependencies.sequences!.requestStop(sequence.id, reason);\n dependencies.stdout(`Stop requested for sequence ${sequence.id}`);\n }\n const stopped: RunnerRecord[] = [];\n for (const record of records) {\n if (!(await stopRecord(record, dependencies, reason))) continue;\n await dependencies.registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: TERMINATION_SIGNAL,\n ...(reason ? { stopReason: reason } : {}),\n },\n record.sessionId,\n );\n stopped.push(record);\n }\n dependencies.stdout(\n records.length === 0\n ? `No active runners for session ${sessionId}`\n : `Stopped ${stopped.length} runner(s) for session ${sessionId}: ${stopped.map((record) => record.id).join(', ')}`,\n );\n if (stopped.length !== records.length) {\n dependencies.stderr(`Could not stop ${records.length - stopped.length} runner(s) for session ${sessionId}`);\n return 1;\n }\n return 0;\n}\n\nasync function input(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n if (record.state === COMPLETED_STATE) {\n dependencies.stderr(`Runner ${record.id} is already completed; use po-runner logs ${record.id}`);\n return 1;\n }\n if (!record.interactive) {\n dependencies.stderr(`Runner ${record.id} was not started with interactive: true`);\n return 1;\n }\n const supplied = option(args, '--text');\n const text = supplied ?? (await dependencies.readStdin());\n const payload = args.includes('--enter') && !text.endsWith('\\n') ? `${text}\\n` : text;\n if (!MULTIPLEXER_BACKENDS.has(record.backend) || !record.backendTarget) {\n dependencies.stderr(`Runner ${record.id} uses the ${record.backend} backend, which does not expose CLI input`);\n return 1;\n }\n if (!(await dependencies.rmuxBackend.input(record.backendTarget, payload))) {\n dependencies.stderr(`Runner ${record.id} is no longer accepting RMUX input`);\n return 1;\n }\n dependencies.stdout(`Sent input to ${record.id}`);\n return 0;\n}\n\nasync function requiredRecord(\n id: string | undefined,\n dependencies: CliDependencies,\n): Promise<RunnerRecord | undefined> {\n if (!id) {\n dependencies.stderr('A runner ID is required');\n return undefined;\n }\n const sessionId = requiredSessionId(dependencies.env);\n const scopedRecord = await dependencies.registry.get(id, sessionId);\n if (!scopedRecord) {\n dependencies.stderr(`No runner with id ${id}`);\n return undefined;\n }\n return scopedRecord;\n}\n\nfunction formatRecord(record: RunnerRecord): string {\n const active = formatRunnerLine(record, Date.now());\n return `${record.id} ${record.state} ${record.backend} ${active}`;\n}\n\nfunction formatExit(record: RunnerRecord): string {\n const exit = record.exit;\n if (!exit) return 'unknown';\n const detail = exit.code !== null ? ` code ${exit.code}` : exit.signal ? ` ${exit.signal}` : '';\n const attribution = exit.terminationReason ? ` [${exit.terminationReason}]` : '';\n const note = exit.stopReason ? ` (${exit.stopReason})` : '';\n return `${exit.reason}${detail}${attribution}${note}`;\n}\n\nfunction option(args: readonly string[], name: string): string | undefined {\n const index = args.indexOf(name);\n return index >= 0 ? args[index + 1] : undefined;\n}\n\nfunction positiveInteger(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Number(value);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;\n}\n\nasync function follow(record: RunnerRecord, initialSize: number, dependencies: CliDependencies): Promise<number> {\n let offset = initialSize;\n return new Promise<number>((resolve) => {\n let stopped = false;\n let stateTimer: NodeJS.Timeout;\n let reads = Promise.resolve();\n const drain = async (current: fs.Stats): Promise<void> => {\n if (current.size < offset) offset = 0;\n if (current.size === offset) return;\n const length = current.size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = await open(record.logPath, 'r');\n try {\n const { bytesRead } = await descriptor.read(buffer, 0, length, offset);\n if (bytesRead > 0) dependencies.stdout(buffer.subarray(0, bytesRead).toString('utf8'));\n offset += bytesRead;\n } finally {\n await descriptor.close();\n }\n };\n const report = (error: unknown): void => {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n dependencies.stderr(`Runner ${record.id} failed to flush its final log output: ${String(error)}`);\n }\n };\n const listener = (current: fs.Stats): void => {\n if (stopped) return;\n reads = reads.then(() => drain(current)).catch(report);\n };\n const stopFollowing = (): void => {\n if (stopped) return;\n stopped = true;\n fs.unwatchFile(record.logPath, listener);\n clearInterval(stateTimer);\n process.off('SIGINT', stopFollowing);\n reads = reads.then(async () => drain(await stat(record.logPath))).catch(report);\n void reads.then(() => resolve(0));\n };\n process.once('SIGINT', stopFollowing);\n fs.watchFile(record.logPath, { interval: FOLLOW_INTERVAL_MS }, listener);\n stateTimer = setInterval(() => {\n void dependencies.registry.get(record.id).then((current) => {\n if (current?.state === COMPLETED_STATE) stopFollowing();\n });\n }, FOLLOW_INTERVAL_MS);\n });\n}\n\nasync function stopRecord(record: RunnerRecord, dependencies: CliDependencies, reason?: string): Promise<boolean> {\n return stopRunnerProcess(record, dependencies.launcher, dependencies.rmuxBackend, {\n registry: dependencies.registry,\n intent: { reason: STOPPED_REASON, terminationReason: 'user_stop', ...(reason ? { stopReason: reason } : {}) },\n });\n}\n\nexport function runnerCliHelp(): string {\n return [\n 'Usage: po-runner <command>',\n '',\n 'Commands:',\n ' list [--all]',\n ' status <id>',\n ' logs <id> [--lines N] [--follow]',\n ' stop <id> [--reason <text>]',\n ' stop-all [--reason <text>]',\n ' input <id> [--text <text>] [--enter]',\n ].join('\\n');\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cliApp-GKcqD6CT.cjs","names":["inheritedSessionId","isSequenceId","requiredSessionId","formatRunnerLine","open","stat","stopRunnerProcess"],"sources":["../src/constants/cliApp.ts","../src/services/cliApp/index.ts"],"sourcesContent":["export const DEFAULT_LOG_LINES = 200;\nexport const FOLLOW_INTERVAL_MS = 250;\nexport const COMPLETED_STATE = 'completed';\nexport const STOPPED_REASON = 'stopped';\nexport const TERMINATION_SIGNAL = 'SIGTERM';\n","import fs from 'node:fs';\nimport { open, stat } from 'node:fs/promises';\n\nimport {\n COMPLETED_STATE,\n DEFAULT_LOG_LINES,\n FOLLOW_INTERVAL_MS,\n STOPPED_REASON,\n TERMINATION_SIGNAL,\n} from '../../constants/cliApp';\nimport { formatRunnerLine } from '../../services/bashResult';\nimport { inheritedSessionId, requiredSessionId } from '../../services/session';\nimport type { ILauncher } from '../../types/launcher';\nimport type { ILogReader } from '../../types/logReader';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport type { IRunnerRegistry, RunnerRecord } from '../../types/runnerRegistry';\nimport { stopRunnerProcess } from '../reconcile';\nimport { isSequenceId, type RunnerSequenceStore } from '../runnerSequenceStore';\n\nconst MULTIPLEXER_BACKENDS = new Set(['rmux', 'tmux']);\n\nexport interface CliDependencies {\n registry: IRunnerRegistry;\n launcher: ILauncher;\n rmuxBackend: IRmuxBackend;\n logReader: ILogReader;\n env?: NodeJS.ProcessEnv;\n sequences?: RunnerSequenceStore;\n stdout(text: string): void;\n stderr(text: string): void;\n readStdin(): Promise<string>;\n}\n\nexport async function runCli(argv: readonly string[], dependencies: CliDependencies): Promise<number> {\n const [command, ...args] = argv;\n if (command !== undefined && command !== '--help' && command !== '-h' && !inheritedSessionId(dependencies.env)) {\n dependencies.stderr('PI_SESSION_ID is required for po-runner commands');\n return 1;\n }\n if (command && args[0] && isSequenceId(args[0])) return sequenceCommand(command, args, dependencies);\n switch (command) {\n case 'list':\n return list(args, dependencies);\n case 'status':\n return status(args, dependencies);\n case 'logs':\n return logs(args, dependencies);\n case 'stop':\n return stop(args, dependencies);\n case 'stop-all':\n return stopAll(args, dependencies);\n case 'input':\n return input(args, dependencies);\n case '--help':\n case '-h':\n case undefined:\n dependencies.stdout(runnerCliHelp());\n return 0;\n default:\n dependencies.stderr(`Unknown command: ${command}\\n\\n${runnerCliHelp()}`);\n return 1;\n }\n}\n\nasync function sequenceCommand(\n command: string,\n args: readonly string[],\n dependencies: CliDependencies,\n): Promise<number> {\n const store = dependencies.sequences;\n if (!store) {\n dependencies.stderr('Sequence controls are unavailable.');\n return 1;\n }\n const record = await store.get(args[0]!);\n if (!record) {\n dependencies.stderr(`No sequence with id ${args[0]}`);\n return 1;\n }\n if (command === 'stop') {\n if (record.state !== 'running') {\n dependencies.stderr(`Sequence ${record.id} is already completed`);\n return 1;\n }\n await store.requestStop(record.id, option(args, '--reason'));\n dependencies.stdout(`Stop requested for sequence ${record.id}`);\n return 0;\n }\n if (command === 'status') {\n dependencies.stdout(\n [\n `ID: ${record.id}`,\n `Name: ${record.name}`,\n `State: ${record.state}`,\n `Session: ${record.sessionId}`,\n ...(record.outcome ? [`Outcome: ${record.outcome}`] : []),\n ...record.stages.map((stage) => `Stage ${stage.index + 1}: ${stage.state}${stage.id ? ` (${stage.id})` : ''}`),\n ].join('\\n'),\n );\n return 0;\n }\n if (command === 'logs') {\n if (args.includes('--follow')) {\n dependencies.stderr('Sequence logs are finite snapshots; inspect status before reading again.');\n return 1;\n }\n if (record.result)\n dependencies.stdout(\n record.result.content.flatMap((part) => (part.type === 'text' ? [part.text] : [])).join('\\n'),\n );\n else\n dependencies.stdout(\n [\n `Sequence ${record.id} is running.`,\n ...record.stages.filter((stage) => stage.logPath).map((stage) => `${stage.index + 1}. ${stage.logPath}`),\n ].join('\\n'),\n );\n return 0;\n }\n dependencies.stderr(\n command === 'input' ? 'Command sequences are noninteractive.' : `Unknown sequence command: ${command}`,\n );\n return 1;\n}\n\nasync function list(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const sessionId = requiredSessionId(dependencies.env);\n const records = args.includes('--all')\n ? await dependencies.registry.listAll(sessionId)\n : await dependencies.registry.listBySession(sessionId);\n const sequences = (await dependencies.sequences?.list(args.includes('--all'))) ?? [];\n const rows = [\n ...records.map(formatRecord),\n ...sequences.map((record) => `${record.id} ${record.state} sequence ${record.name}`),\n ];\n dependencies.stdout(rows.length > 0 ? rows.join('\\n') : 'No runners');\n return 0;\n}\n\nasync function status(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n dependencies.stdout(\n [\n `ID: ${record.id}`,\n `Name: ${record.name}`,\n `State: ${record.state}`,\n `Backend: ${record.backend}`,\n ...(record.backendTarget ? [`Backend target: ${record.backendTarget}`] : []),\n `PID: ${record.pid}`,\n `Command: ${record.command}`,\n `CWD: ${record.cwd}`,\n `Session: ${record.sessionId}`,\n `Log: ${record.logPath}`,\n ...(record.exit ? [`Exit: ${formatExit(record)}`] : []),\n ].join('\\n'),\n );\n return 0;\n}\n\nasync function logs(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n const lines = positiveInteger(option(args, '--lines')) ?? DEFAULT_LOG_LINES;\n const result = await dependencies.logReader.read(record.logPath, { lines });\n if (!result.exists) {\n dependencies.stderr(`Runner ${record.id} has not written a log yet`);\n return 1;\n }\n if (result.text) dependencies.stdout(result.text);\n if (!args.includes('--follow')) return 0;\n return follow(record, result.fileSize, dependencies);\n}\n\nasync function stop(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n if (record.state !== 'running') {\n dependencies.stderr(`Runner ${record.id} is already ${record.state}`);\n return 1;\n }\n const reason = option(args, '--reason');\n if (!(await stopRecord(record, dependencies, reason))) {\n dependencies.stderr(`Could not stop runner ${record.id}`);\n return 1;\n }\n await dependencies.registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: TERMINATION_SIGNAL,\n ...(reason ? { stopReason: reason } : {}),\n },\n record.sessionId,\n );\n dependencies.stdout(`Stopped ${record.id}\\nLog: ${record.logPath}`);\n return 0;\n}\n\nasync function stopAll(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const sessionId = requiredSessionId(dependencies.env);\n const records = await dependencies.registry.listBySession(sessionId);\n const reason = option(args, '--reason');\n for (const sequence of (await dependencies.sequences?.list()) ?? []) {\n await dependencies.sequences!.requestStop(sequence.id, reason);\n dependencies.stdout(`Stop requested for sequence ${sequence.id}`);\n }\n const stopped: RunnerRecord[] = [];\n for (const record of records) {\n if (!(await stopRecord(record, dependencies, reason))) continue;\n await dependencies.registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: TERMINATION_SIGNAL,\n ...(reason ? { stopReason: reason } : {}),\n },\n record.sessionId,\n );\n stopped.push(record);\n }\n dependencies.stdout(\n records.length === 0\n ? `No active runners for session ${sessionId}`\n : `Stopped ${stopped.length} runner(s) for session ${sessionId}: ${stopped.map((record) => record.id).join(', ')}`,\n );\n if (stopped.length !== records.length) {\n dependencies.stderr(`Could not stop ${records.length - stopped.length} runner(s) for session ${sessionId}`);\n return 1;\n }\n return 0;\n}\n\nasync function input(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n if (record.state === COMPLETED_STATE) {\n dependencies.stderr(`Runner ${record.id} is already completed; use po-runner logs ${record.id}`);\n return 1;\n }\n if (!record.interactive) {\n dependencies.stderr(`Runner ${record.id} was not started with interactive: true`);\n return 1;\n }\n const supplied = option(args, '--text');\n const text = supplied ?? (await dependencies.readStdin());\n const payload = args.includes('--enter') && !text.endsWith('\\n') ? `${text}\\n` : text;\n if (!MULTIPLEXER_BACKENDS.has(record.backend) || !record.backendTarget) {\n dependencies.stderr(`Runner ${record.id} uses the ${record.backend} backend, which does not expose CLI input`);\n return 1;\n }\n if (!(await dependencies.rmuxBackend.input(record.backendTarget, payload))) {\n dependencies.stderr(`Runner ${record.id} is no longer accepting RMUX input`);\n return 1;\n }\n dependencies.stdout(`Sent input to ${record.id}`);\n return 0;\n}\n\nasync function requiredRecord(\n id: string | undefined,\n dependencies: CliDependencies,\n): Promise<RunnerRecord | undefined> {\n if (!id) {\n dependencies.stderr('A runner ID is required');\n return undefined;\n }\n const sessionId = requiredSessionId(dependencies.env);\n const scopedRecord = await dependencies.registry.get(id, sessionId);\n if (!scopedRecord) {\n dependencies.stderr(`No runner with id ${id}`);\n return undefined;\n }\n return scopedRecord;\n}\n\nfunction formatRecord(record: RunnerRecord): string {\n const active = formatRunnerLine(record, Date.now());\n return `${record.id} ${record.state} ${record.backend} ${active}`;\n}\n\nfunction formatExit(record: RunnerRecord): string {\n const exit = record.exit;\n if (!exit) return 'unknown';\n const detail = exit.code !== null ? ` code ${exit.code}` : exit.signal ? ` ${exit.signal}` : '';\n const attribution = exit.terminationReason ? ` [${exit.terminationReason}]` : '';\n const note = exit.stopReason ? ` (${exit.stopReason})` : '';\n return `${exit.reason}${detail}${attribution}${note}`;\n}\n\nfunction option(args: readonly string[], name: string): string | undefined {\n const index = args.indexOf(name);\n return index >= 0 ? args[index + 1] : undefined;\n}\n\nfunction positiveInteger(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Number(value);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;\n}\n\nasync function follow(record: RunnerRecord, initialSize: number, dependencies: CliDependencies): Promise<number> {\n let offset = initialSize;\n return new Promise<number>((resolve) => {\n let stopped = false;\n let stateTimer: NodeJS.Timeout;\n let reads = Promise.resolve();\n const drain = async (current: fs.Stats): Promise<void> => {\n if (current.size < offset) offset = 0;\n if (current.size === offset) return;\n const length = current.size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = await open(record.logPath, 'r');\n try {\n const { bytesRead } = await descriptor.read(buffer, 0, length, offset);\n if (bytesRead > 0) dependencies.stdout(buffer.subarray(0, bytesRead).toString('utf8'));\n offset += bytesRead;\n } finally {\n await descriptor.close();\n }\n };\n const report = (error: unknown): void => {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n dependencies.stderr(`Runner ${record.id} failed to flush its final log output: ${String(error)}`);\n }\n };\n const listener = (current: fs.Stats): void => {\n if (stopped) return;\n reads = reads.then(() => drain(current)).catch(report);\n };\n const stopFollowing = (): void => {\n if (stopped) return;\n stopped = true;\n fs.unwatchFile(record.logPath, listener);\n clearInterval(stateTimer);\n process.off('SIGINT', stopFollowing);\n reads = reads.then(async () => drain(await stat(record.logPath))).catch(report);\n void reads.then(() => resolve(0));\n };\n process.once('SIGINT', stopFollowing);\n fs.watchFile(record.logPath, { interval: FOLLOW_INTERVAL_MS }, listener);\n stateTimer = setInterval(() => {\n void dependencies.registry.get(record.id).then((current) => {\n if (current?.state === COMPLETED_STATE) stopFollowing();\n });\n }, FOLLOW_INTERVAL_MS);\n });\n}\n\nasync function stopRecord(record: RunnerRecord, dependencies: CliDependencies, reason?: string): Promise<boolean> {\n return stopRunnerProcess(record, dependencies.launcher, dependencies.rmuxBackend, {\n registry: dependencies.registry,\n intent: { reason: STOPPED_REASON, terminationReason: 'user_stop', ...(reason ? { stopReason: reason } : {}) },\n });\n}\n\nexport function runnerCliHelp(): string {\n return [\n 'Usage: po-runner <command>',\n '',\n 'Commands:',\n ' list [--all]',\n ' status <id>',\n ' logs <id> [--lines N] [--follow]',\n ' stop <id> [--reason <text>]',\n ' stop-all [--reason <text>]',\n ' input <id> [--text <text>] [--enter]',\n ].join('\\n');\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { l as formatRunnerLine } from "./bashResult-ntL4Umz3.mjs";
2
2
  import { n as stopRunnerProcess } from "./reconcile-DUaX_vKZ.mjs";
3
3
  import { n as isSequenceId } from "./runnerSequenceStore-CaY7S_Sd.mjs";
4
- import { n as requiredSessionId, t as inheritedSessionId } from "./session-BKYnH4R6.mjs";
4
+ import { n as requiredSessionId, t as inheritedSessionId } from "./session-3oSU3QjI.mjs";
5
5
  import { open, stat } from "node:fs/promises";
6
6
  import fs$1 from "node:fs";
7
7
  const STOPPED_REASON = "stopped";
@@ -291,4 +291,4 @@ function runnerCliHelp() {
291
291
  //#endregion
292
292
  export { runnerCliHelp as n, runCli as t };
293
293
 
294
- //# sourceMappingURL=cliApp-DjU_M0ho.mjs.map
294
+ //# sourceMappingURL=cliApp-g7APOwcU.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cliApp-DjU_M0ho.mjs","names":[],"sources":["../src/constants/cliApp.ts","../src/services/cliApp/index.ts"],"sourcesContent":["export const DEFAULT_LOG_LINES = 200;\nexport const FOLLOW_INTERVAL_MS = 250;\nexport const COMPLETED_STATE = 'completed';\nexport const STOPPED_REASON = 'stopped';\nexport const TERMINATION_SIGNAL = 'SIGTERM';\n","import fs from 'node:fs';\nimport { open, stat } from 'node:fs/promises';\n\nimport {\n COMPLETED_STATE,\n DEFAULT_LOG_LINES,\n FOLLOW_INTERVAL_MS,\n STOPPED_REASON,\n TERMINATION_SIGNAL,\n} from '../../constants/cliApp';\nimport { formatRunnerLine } from '../../services/bashResult';\nimport { inheritedSessionId, requiredSessionId } from '../../services/session';\nimport type { ILauncher } from '../../types/launcher';\nimport type { ILogReader } from '../../types/logReader';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport type { IRunnerRegistry, RunnerRecord } from '../../types/runnerRegistry';\nimport { stopRunnerProcess } from '../reconcile';\nimport { isSequenceId, type RunnerSequenceStore } from '../runnerSequenceStore';\n\nconst MULTIPLEXER_BACKENDS = new Set(['rmux', 'tmux']);\n\nexport interface CliDependencies {\n registry: IRunnerRegistry;\n launcher: ILauncher;\n rmuxBackend: IRmuxBackend;\n logReader: ILogReader;\n env?: NodeJS.ProcessEnv;\n sequences?: RunnerSequenceStore;\n stdout(text: string): void;\n stderr(text: string): void;\n readStdin(): Promise<string>;\n}\n\nexport async function runCli(argv: readonly string[], dependencies: CliDependencies): Promise<number> {\n const [command, ...args] = argv;\n if (command !== undefined && command !== '--help' && command !== '-h' && !inheritedSessionId(dependencies.env)) {\n dependencies.stderr('PI_SESSION_ID is required for po-runner commands');\n return 1;\n }\n if (command && args[0] && isSequenceId(args[0])) return sequenceCommand(command, args, dependencies);\n switch (command) {\n case 'list':\n return list(args, dependencies);\n case 'status':\n return status(args, dependencies);\n case 'logs':\n return logs(args, dependencies);\n case 'stop':\n return stop(args, dependencies);\n case 'stop-all':\n return stopAll(args, dependencies);\n case 'input':\n return input(args, dependencies);\n case '--help':\n case '-h':\n case undefined:\n dependencies.stdout(runnerCliHelp());\n return 0;\n default:\n dependencies.stderr(`Unknown command: ${command}\\n\\n${runnerCliHelp()}`);\n return 1;\n }\n}\n\nasync function sequenceCommand(\n command: string,\n args: readonly string[],\n dependencies: CliDependencies,\n): Promise<number> {\n const store = dependencies.sequences;\n if (!store) {\n dependencies.stderr('Sequence controls are unavailable.');\n return 1;\n }\n const record = await store.get(args[0]!);\n if (!record) {\n dependencies.stderr(`No sequence with id ${args[0]}`);\n return 1;\n }\n if (command === 'stop') {\n if (record.state !== 'running') {\n dependencies.stderr(`Sequence ${record.id} is already completed`);\n return 1;\n }\n await store.requestStop(record.id, option(args, '--reason'));\n dependencies.stdout(`Stop requested for sequence ${record.id}`);\n return 0;\n }\n if (command === 'status') {\n dependencies.stdout(\n [\n `ID: ${record.id}`,\n `Name: ${record.name}`,\n `State: ${record.state}`,\n `Session: ${record.sessionId}`,\n ...(record.outcome ? [`Outcome: ${record.outcome}`] : []),\n ...record.stages.map((stage) => `Stage ${stage.index + 1}: ${stage.state}${stage.id ? ` (${stage.id})` : ''}`),\n ].join('\\n'),\n );\n return 0;\n }\n if (command === 'logs') {\n if (args.includes('--follow')) {\n dependencies.stderr('Sequence logs are finite snapshots; inspect status before reading again.');\n return 1;\n }\n if (record.result)\n dependencies.stdout(\n record.result.content.flatMap((part) => (part.type === 'text' ? [part.text] : [])).join('\\n'),\n );\n else\n dependencies.stdout(\n [\n `Sequence ${record.id} is running.`,\n ...record.stages.filter((stage) => stage.logPath).map((stage) => `${stage.index + 1}. ${stage.logPath}`),\n ].join('\\n'),\n );\n return 0;\n }\n dependencies.stderr(\n command === 'input' ? 'Command sequences are noninteractive.' : `Unknown sequence command: ${command}`,\n );\n return 1;\n}\n\nasync function list(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const sessionId = requiredSessionId(dependencies.env);\n const records = args.includes('--all')\n ? await dependencies.registry.listAll(sessionId)\n : await dependencies.registry.listBySession(sessionId);\n const sequences = (await dependencies.sequences?.list(args.includes('--all'))) ?? [];\n const rows = [\n ...records.map(formatRecord),\n ...sequences.map((record) => `${record.id} ${record.state} sequence ${record.name}`),\n ];\n dependencies.stdout(rows.length > 0 ? rows.join('\\n') : 'No runners');\n return 0;\n}\n\nasync function status(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n dependencies.stdout(\n [\n `ID: ${record.id}`,\n `Name: ${record.name}`,\n `State: ${record.state}`,\n `Backend: ${record.backend}`,\n ...(record.backendTarget ? [`Backend target: ${record.backendTarget}`] : []),\n `PID: ${record.pid}`,\n `Command: ${record.command}`,\n `CWD: ${record.cwd}`,\n `Session: ${record.sessionId}`,\n `Log: ${record.logPath}`,\n ...(record.exit ? [`Exit: ${formatExit(record)}`] : []),\n ].join('\\n'),\n );\n return 0;\n}\n\nasync function logs(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n const lines = positiveInteger(option(args, '--lines')) ?? DEFAULT_LOG_LINES;\n const result = await dependencies.logReader.read(record.logPath, { lines });\n if (!result.exists) {\n dependencies.stderr(`Runner ${record.id} has not written a log yet`);\n return 1;\n }\n if (result.text) dependencies.stdout(result.text);\n if (!args.includes('--follow')) return 0;\n return follow(record, result.fileSize, dependencies);\n}\n\nasync function stop(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n if (record.state !== 'running') {\n dependencies.stderr(`Runner ${record.id} is already ${record.state}`);\n return 1;\n }\n const reason = option(args, '--reason');\n if (!(await stopRecord(record, dependencies, reason))) {\n dependencies.stderr(`Could not stop runner ${record.id}`);\n return 1;\n }\n await dependencies.registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: TERMINATION_SIGNAL,\n ...(reason ? { stopReason: reason } : {}),\n },\n record.sessionId,\n );\n dependencies.stdout(`Stopped ${record.id}\\nLog: ${record.logPath}`);\n return 0;\n}\n\nasync function stopAll(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const sessionId = requiredSessionId(dependencies.env);\n const records = await dependencies.registry.listBySession(sessionId);\n const reason = option(args, '--reason');\n for (const sequence of (await dependencies.sequences?.list()) ?? []) {\n await dependencies.sequences!.requestStop(sequence.id, reason);\n dependencies.stdout(`Stop requested for sequence ${sequence.id}`);\n }\n const stopped: RunnerRecord[] = [];\n for (const record of records) {\n if (!(await stopRecord(record, dependencies, reason))) continue;\n await dependencies.registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: TERMINATION_SIGNAL,\n ...(reason ? { stopReason: reason } : {}),\n },\n record.sessionId,\n );\n stopped.push(record);\n }\n dependencies.stdout(\n records.length === 0\n ? `No active runners for session ${sessionId}`\n : `Stopped ${stopped.length} runner(s) for session ${sessionId}: ${stopped.map((record) => record.id).join(', ')}`,\n );\n if (stopped.length !== records.length) {\n dependencies.stderr(`Could not stop ${records.length - stopped.length} runner(s) for session ${sessionId}`);\n return 1;\n }\n return 0;\n}\n\nasync function input(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n if (record.state === COMPLETED_STATE) {\n dependencies.stderr(`Runner ${record.id} is already completed; use po-runner logs ${record.id}`);\n return 1;\n }\n if (!record.interactive) {\n dependencies.stderr(`Runner ${record.id} was not started with interactive: true`);\n return 1;\n }\n const supplied = option(args, '--text');\n const text = supplied ?? (await dependencies.readStdin());\n const payload = args.includes('--enter') && !text.endsWith('\\n') ? `${text}\\n` : text;\n if (!MULTIPLEXER_BACKENDS.has(record.backend) || !record.backendTarget) {\n dependencies.stderr(`Runner ${record.id} uses the ${record.backend} backend, which does not expose CLI input`);\n return 1;\n }\n if (!(await dependencies.rmuxBackend.input(record.backendTarget, payload))) {\n dependencies.stderr(`Runner ${record.id} is no longer accepting RMUX input`);\n return 1;\n }\n dependencies.stdout(`Sent input to ${record.id}`);\n return 0;\n}\n\nasync function requiredRecord(\n id: string | undefined,\n dependencies: CliDependencies,\n): Promise<RunnerRecord | undefined> {\n if (!id) {\n dependencies.stderr('A runner ID is required');\n return undefined;\n }\n const sessionId = requiredSessionId(dependencies.env);\n const scopedRecord = await dependencies.registry.get(id, sessionId);\n if (!scopedRecord) {\n dependencies.stderr(`No runner with id ${id}`);\n return undefined;\n }\n return scopedRecord;\n}\n\nfunction formatRecord(record: RunnerRecord): string {\n const active = formatRunnerLine(record, Date.now());\n return `${record.id} ${record.state} ${record.backend} ${active}`;\n}\n\nfunction formatExit(record: RunnerRecord): string {\n const exit = record.exit;\n if (!exit) return 'unknown';\n const detail = exit.code !== null ? ` code ${exit.code}` : exit.signal ? ` ${exit.signal}` : '';\n const attribution = exit.terminationReason ? ` [${exit.terminationReason}]` : '';\n const note = exit.stopReason ? ` (${exit.stopReason})` : '';\n return `${exit.reason}${detail}${attribution}${note}`;\n}\n\nfunction option(args: readonly string[], name: string): string | undefined {\n const index = args.indexOf(name);\n return index >= 0 ? args[index + 1] : undefined;\n}\n\nfunction positiveInteger(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Number(value);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;\n}\n\nasync function follow(record: RunnerRecord, initialSize: number, dependencies: CliDependencies): Promise<number> {\n let offset = initialSize;\n return new Promise<number>((resolve) => {\n let stopped = false;\n let stateTimer: NodeJS.Timeout;\n let reads = Promise.resolve();\n const drain = async (current: fs.Stats): Promise<void> => {\n if (current.size < offset) offset = 0;\n if (current.size === offset) return;\n const length = current.size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = await open(record.logPath, 'r');\n try {\n const { bytesRead } = await descriptor.read(buffer, 0, length, offset);\n if (bytesRead > 0) dependencies.stdout(buffer.subarray(0, bytesRead).toString('utf8'));\n offset += bytesRead;\n } finally {\n await descriptor.close();\n }\n };\n const report = (error: unknown): void => {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n dependencies.stderr(`Runner ${record.id} failed to flush its final log output: ${String(error)}`);\n }\n };\n const listener = (current: fs.Stats): void => {\n if (stopped) return;\n reads = reads.then(() => drain(current)).catch(report);\n };\n const stopFollowing = (): void => {\n if (stopped) return;\n stopped = true;\n fs.unwatchFile(record.logPath, listener);\n clearInterval(stateTimer);\n process.off('SIGINT', stopFollowing);\n reads = reads.then(async () => drain(await stat(record.logPath))).catch(report);\n void reads.then(() => resolve(0));\n };\n process.once('SIGINT', stopFollowing);\n fs.watchFile(record.logPath, { interval: FOLLOW_INTERVAL_MS }, listener);\n stateTimer = setInterval(() => {\n void dependencies.registry.get(record.id).then((current) => {\n if (current?.state === COMPLETED_STATE) stopFollowing();\n });\n }, FOLLOW_INTERVAL_MS);\n });\n}\n\nasync function stopRecord(record: RunnerRecord, dependencies: CliDependencies, reason?: string): Promise<boolean> {\n return stopRunnerProcess(record, dependencies.launcher, dependencies.rmuxBackend, {\n registry: dependencies.registry,\n intent: { reason: STOPPED_REASON, terminationReason: 'user_stop', ...(reason ? { stopReason: reason } : {}) },\n });\n}\n\nexport function runnerCliHelp(): string {\n return [\n 'Usage: po-runner <command>',\n '',\n 'Commands:',\n ' list [--all]',\n ' status <id>',\n ' logs <id> [--lines N] [--follow]',\n ' stop <id> [--reason <text>]',\n ' stop-all [--reason <text>]',\n ' input <id> [--text <text>] [--enter]',\n ].join('\\n');\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cliApp-g7APOwcU.mjs","names":[],"sources":["../src/constants/cliApp.ts","../src/services/cliApp/index.ts"],"sourcesContent":["export const DEFAULT_LOG_LINES = 200;\nexport const FOLLOW_INTERVAL_MS = 250;\nexport const COMPLETED_STATE = 'completed';\nexport const STOPPED_REASON = 'stopped';\nexport const TERMINATION_SIGNAL = 'SIGTERM';\n","import fs from 'node:fs';\nimport { open, stat } from 'node:fs/promises';\n\nimport {\n COMPLETED_STATE,\n DEFAULT_LOG_LINES,\n FOLLOW_INTERVAL_MS,\n STOPPED_REASON,\n TERMINATION_SIGNAL,\n} from '../../constants/cliApp';\nimport { formatRunnerLine } from '../../services/bashResult';\nimport { inheritedSessionId, requiredSessionId } from '../../services/session';\nimport type { ILauncher } from '../../types/launcher';\nimport type { ILogReader } from '../../types/logReader';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport type { IRunnerRegistry, RunnerRecord } from '../../types/runnerRegistry';\nimport { stopRunnerProcess } from '../reconcile';\nimport { isSequenceId, type RunnerSequenceStore } from '../runnerSequenceStore';\n\nconst MULTIPLEXER_BACKENDS = new Set(['rmux', 'tmux']);\n\nexport interface CliDependencies {\n registry: IRunnerRegistry;\n launcher: ILauncher;\n rmuxBackend: IRmuxBackend;\n logReader: ILogReader;\n env?: NodeJS.ProcessEnv;\n sequences?: RunnerSequenceStore;\n stdout(text: string): void;\n stderr(text: string): void;\n readStdin(): Promise<string>;\n}\n\nexport async function runCli(argv: readonly string[], dependencies: CliDependencies): Promise<number> {\n const [command, ...args] = argv;\n if (command !== undefined && command !== '--help' && command !== '-h' && !inheritedSessionId(dependencies.env)) {\n dependencies.stderr('PI_SESSION_ID is required for po-runner commands');\n return 1;\n }\n if (command && args[0] && isSequenceId(args[0])) return sequenceCommand(command, args, dependencies);\n switch (command) {\n case 'list':\n return list(args, dependencies);\n case 'status':\n return status(args, dependencies);\n case 'logs':\n return logs(args, dependencies);\n case 'stop':\n return stop(args, dependencies);\n case 'stop-all':\n return stopAll(args, dependencies);\n case 'input':\n return input(args, dependencies);\n case '--help':\n case '-h':\n case undefined:\n dependencies.stdout(runnerCliHelp());\n return 0;\n default:\n dependencies.stderr(`Unknown command: ${command}\\n\\n${runnerCliHelp()}`);\n return 1;\n }\n}\n\nasync function sequenceCommand(\n command: string,\n args: readonly string[],\n dependencies: CliDependencies,\n): Promise<number> {\n const store = dependencies.sequences;\n if (!store) {\n dependencies.stderr('Sequence controls are unavailable.');\n return 1;\n }\n const record = await store.get(args[0]!);\n if (!record) {\n dependencies.stderr(`No sequence with id ${args[0]}`);\n return 1;\n }\n if (command === 'stop') {\n if (record.state !== 'running') {\n dependencies.stderr(`Sequence ${record.id} is already completed`);\n return 1;\n }\n await store.requestStop(record.id, option(args, '--reason'));\n dependencies.stdout(`Stop requested for sequence ${record.id}`);\n return 0;\n }\n if (command === 'status') {\n dependencies.stdout(\n [\n `ID: ${record.id}`,\n `Name: ${record.name}`,\n `State: ${record.state}`,\n `Session: ${record.sessionId}`,\n ...(record.outcome ? [`Outcome: ${record.outcome}`] : []),\n ...record.stages.map((stage) => `Stage ${stage.index + 1}: ${stage.state}${stage.id ? ` (${stage.id})` : ''}`),\n ].join('\\n'),\n );\n return 0;\n }\n if (command === 'logs') {\n if (args.includes('--follow')) {\n dependencies.stderr('Sequence logs are finite snapshots; inspect status before reading again.');\n return 1;\n }\n if (record.result)\n dependencies.stdout(\n record.result.content.flatMap((part) => (part.type === 'text' ? [part.text] : [])).join('\\n'),\n );\n else\n dependencies.stdout(\n [\n `Sequence ${record.id} is running.`,\n ...record.stages.filter((stage) => stage.logPath).map((stage) => `${stage.index + 1}. ${stage.logPath}`),\n ].join('\\n'),\n );\n return 0;\n }\n dependencies.stderr(\n command === 'input' ? 'Command sequences are noninteractive.' : `Unknown sequence command: ${command}`,\n );\n return 1;\n}\n\nasync function list(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const sessionId = requiredSessionId(dependencies.env);\n const records = args.includes('--all')\n ? await dependencies.registry.listAll(sessionId)\n : await dependencies.registry.listBySession(sessionId);\n const sequences = (await dependencies.sequences?.list(args.includes('--all'))) ?? [];\n const rows = [\n ...records.map(formatRecord),\n ...sequences.map((record) => `${record.id} ${record.state} sequence ${record.name}`),\n ];\n dependencies.stdout(rows.length > 0 ? rows.join('\\n') : 'No runners');\n return 0;\n}\n\nasync function status(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n dependencies.stdout(\n [\n `ID: ${record.id}`,\n `Name: ${record.name}`,\n `State: ${record.state}`,\n `Backend: ${record.backend}`,\n ...(record.backendTarget ? [`Backend target: ${record.backendTarget}`] : []),\n `PID: ${record.pid}`,\n `Command: ${record.command}`,\n `CWD: ${record.cwd}`,\n `Session: ${record.sessionId}`,\n `Log: ${record.logPath}`,\n ...(record.exit ? [`Exit: ${formatExit(record)}`] : []),\n ].join('\\n'),\n );\n return 0;\n}\n\nasync function logs(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n const lines = positiveInteger(option(args, '--lines')) ?? DEFAULT_LOG_LINES;\n const result = await dependencies.logReader.read(record.logPath, { lines });\n if (!result.exists) {\n dependencies.stderr(`Runner ${record.id} has not written a log yet`);\n return 1;\n }\n if (result.text) dependencies.stdout(result.text);\n if (!args.includes('--follow')) return 0;\n return follow(record, result.fileSize, dependencies);\n}\n\nasync function stop(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n if (record.state !== 'running') {\n dependencies.stderr(`Runner ${record.id} is already ${record.state}`);\n return 1;\n }\n const reason = option(args, '--reason');\n if (!(await stopRecord(record, dependencies, reason))) {\n dependencies.stderr(`Could not stop runner ${record.id}`);\n return 1;\n }\n await dependencies.registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: TERMINATION_SIGNAL,\n ...(reason ? { stopReason: reason } : {}),\n },\n record.sessionId,\n );\n dependencies.stdout(`Stopped ${record.id}\\nLog: ${record.logPath}`);\n return 0;\n}\n\nasync function stopAll(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const sessionId = requiredSessionId(dependencies.env);\n const records = await dependencies.registry.listBySession(sessionId);\n const reason = option(args, '--reason');\n for (const sequence of (await dependencies.sequences?.list()) ?? []) {\n await dependencies.sequences!.requestStop(sequence.id, reason);\n dependencies.stdout(`Stop requested for sequence ${sequence.id}`);\n }\n const stopped: RunnerRecord[] = [];\n for (const record of records) {\n if (!(await stopRecord(record, dependencies, reason))) continue;\n await dependencies.registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: TERMINATION_SIGNAL,\n ...(reason ? { stopReason: reason } : {}),\n },\n record.sessionId,\n );\n stopped.push(record);\n }\n dependencies.stdout(\n records.length === 0\n ? `No active runners for session ${sessionId}`\n : `Stopped ${stopped.length} runner(s) for session ${sessionId}: ${stopped.map((record) => record.id).join(', ')}`,\n );\n if (stopped.length !== records.length) {\n dependencies.stderr(`Could not stop ${records.length - stopped.length} runner(s) for session ${sessionId}`);\n return 1;\n }\n return 0;\n}\n\nasync function input(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n const record = await requiredRecord(args[0], dependencies);\n if (!record) return 1;\n if (record.state === COMPLETED_STATE) {\n dependencies.stderr(`Runner ${record.id} is already completed; use po-runner logs ${record.id}`);\n return 1;\n }\n if (!record.interactive) {\n dependencies.stderr(`Runner ${record.id} was not started with interactive: true`);\n return 1;\n }\n const supplied = option(args, '--text');\n const text = supplied ?? (await dependencies.readStdin());\n const payload = args.includes('--enter') && !text.endsWith('\\n') ? `${text}\\n` : text;\n if (!MULTIPLEXER_BACKENDS.has(record.backend) || !record.backendTarget) {\n dependencies.stderr(`Runner ${record.id} uses the ${record.backend} backend, which does not expose CLI input`);\n return 1;\n }\n if (!(await dependencies.rmuxBackend.input(record.backendTarget, payload))) {\n dependencies.stderr(`Runner ${record.id} is no longer accepting RMUX input`);\n return 1;\n }\n dependencies.stdout(`Sent input to ${record.id}`);\n return 0;\n}\n\nasync function requiredRecord(\n id: string | undefined,\n dependencies: CliDependencies,\n): Promise<RunnerRecord | undefined> {\n if (!id) {\n dependencies.stderr('A runner ID is required');\n return undefined;\n }\n const sessionId = requiredSessionId(dependencies.env);\n const scopedRecord = await dependencies.registry.get(id, sessionId);\n if (!scopedRecord) {\n dependencies.stderr(`No runner with id ${id}`);\n return undefined;\n }\n return scopedRecord;\n}\n\nfunction formatRecord(record: RunnerRecord): string {\n const active = formatRunnerLine(record, Date.now());\n return `${record.id} ${record.state} ${record.backend} ${active}`;\n}\n\nfunction formatExit(record: RunnerRecord): string {\n const exit = record.exit;\n if (!exit) return 'unknown';\n const detail = exit.code !== null ? ` code ${exit.code}` : exit.signal ? ` ${exit.signal}` : '';\n const attribution = exit.terminationReason ? ` [${exit.terminationReason}]` : '';\n const note = exit.stopReason ? ` (${exit.stopReason})` : '';\n return `${exit.reason}${detail}${attribution}${note}`;\n}\n\nfunction option(args: readonly string[], name: string): string | undefined {\n const index = args.indexOf(name);\n return index >= 0 ? args[index + 1] : undefined;\n}\n\nfunction positiveInteger(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const parsed = Number(value);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;\n}\n\nasync function follow(record: RunnerRecord, initialSize: number, dependencies: CliDependencies): Promise<number> {\n let offset = initialSize;\n return new Promise<number>((resolve) => {\n let stopped = false;\n let stateTimer: NodeJS.Timeout;\n let reads = Promise.resolve();\n const drain = async (current: fs.Stats): Promise<void> => {\n if (current.size < offset) offset = 0;\n if (current.size === offset) return;\n const length = current.size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = await open(record.logPath, 'r');\n try {\n const { bytesRead } = await descriptor.read(buffer, 0, length, offset);\n if (bytesRead > 0) dependencies.stdout(buffer.subarray(0, bytesRead).toString('utf8'));\n offset += bytesRead;\n } finally {\n await descriptor.close();\n }\n };\n const report = (error: unknown): void => {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n dependencies.stderr(`Runner ${record.id} failed to flush its final log output: ${String(error)}`);\n }\n };\n const listener = (current: fs.Stats): void => {\n if (stopped) return;\n reads = reads.then(() => drain(current)).catch(report);\n };\n const stopFollowing = (): void => {\n if (stopped) return;\n stopped = true;\n fs.unwatchFile(record.logPath, listener);\n clearInterval(stateTimer);\n process.off('SIGINT', stopFollowing);\n reads = reads.then(async () => drain(await stat(record.logPath))).catch(report);\n void reads.then(() => resolve(0));\n };\n process.once('SIGINT', stopFollowing);\n fs.watchFile(record.logPath, { interval: FOLLOW_INTERVAL_MS }, listener);\n stateTimer = setInterval(() => {\n void dependencies.registry.get(record.id).then((current) => {\n if (current?.state === COMPLETED_STATE) stopFollowing();\n });\n }, FOLLOW_INTERVAL_MS);\n });\n}\n\nasync function stopRecord(record: RunnerRecord, dependencies: CliDependencies, reason?: string): Promise<boolean> {\n return stopRunnerProcess(record, dependencies.launcher, dependencies.rmuxBackend, {\n registry: dependencies.registry,\n intent: { reason: STOPPED_REASON, terminationReason: 'user_stop', ...(reason ? { stopReason: reason } : {}) },\n });\n}\n\nexport function runnerCliHelp(): string {\n return [\n 'Usage: po-runner <command>',\n '',\n 'Commands:',\n ' list [--all]',\n ' status <id>',\n ' logs <id> [--lines N] [--follow]',\n ' stop <id> [--reason <text>]',\n ' stop-all [--reason <text>]',\n ' input <id> [--text <text>] [--enter]',\n ].join('\\n');\n}\n"],"mappings":";;;;;;AAGA,MAAa,iBAAiB;AAC9B,MAAa,qBAAqB;;;ACelC,MAAM,uCAAuB,IAAI,IAAI,CAAC,QAAQ,MAAM,CAAC;AAcrD,eAAsB,OAAO,MAAyB,cAAgD;CACpG,MAAM,CAAC,SAAS,GAAG,QAAQ;CAC3B,IAAI,YAAY,KAAA,KAAa,YAAY,YAAY,YAAY,QAAQ,CAAC,mBAAmB,aAAa,GAAG,GAAG;EAC9G,aAAa,OAAO,kDAAkD;EACtE,OAAO;CACT;CACA,IAAI,WAAW,KAAK,MAAM,aAAa,KAAK,EAAE,GAAG,OAAO,gBAAgB,SAAS,MAAM,YAAY;CACnG,QAAQ,SAAR;EACE,KAAK,QACH,OAAO,KAAK,MAAM,YAAY;EAChC,KAAK,UACH,OAAO,OAAO,MAAM,YAAY;EAClC,KAAK,QACH,OAAO,KAAK,MAAM,YAAY;EAChC,KAAK,QACH,OAAO,KAAK,MAAM,YAAY;EAChC,KAAK,YACH,OAAO,QAAQ,MAAM,YAAY;EACnC,KAAK,SACH,OAAO,MAAM,MAAM,YAAY;EACjC,KAAK;EACL,KAAK;EACL,KAAK,KAAA;GACH,aAAa,OAAO,cAAc,CAAC;GACnC,OAAO;EACT;GACE,aAAa,OAAO,oBAAoB,QAAQ,MAAM,cAAc,GAAG;GACvE,OAAO;CACX;AACF;AAEA,eAAe,gBACb,SACA,MACA,cACiB;CACjB,MAAM,QAAQ,aAAa;CAC3B,IAAI,CAAC,OAAO;EACV,aAAa,OAAO,oCAAoC;EACxD,OAAO;CACT;CACA,MAAM,SAAS,MAAM,MAAM,IAAI,KAAK,EAAG;CACvC,IAAI,CAAC,QAAQ;EACX,aAAa,OAAO,uBAAuB,KAAK,IAAI;EACpD,OAAO;CACT;CACA,IAAI,YAAY,QAAQ;EACtB,IAAI,OAAO,UAAU,WAAW;GAC9B,aAAa,OAAO,YAAY,OAAO,GAAG,sBAAsB;GAChE,OAAO;EACT;EACA,MAAM,MAAM,YAAY,OAAO,IAAI,OAAO,MAAM,UAAU,CAAC;EAC3D,aAAa,OAAO,+BAA+B,OAAO,IAAI;EAC9D,OAAO;CACT;CACA,IAAI,YAAY,UAAU;EACxB,aAAa,OACX;GACE,OAAO,OAAO;GACd,SAAS,OAAO;GAChB,UAAU,OAAO;GACjB,YAAY,OAAO;GACnB,GAAI,OAAO,UAAU,CAAC,YAAY,OAAO,SAAS,IAAI,CAAC;GACvD,GAAG,OAAO,OAAO,KAAK,UAAU,SAAS,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,GAAG,KAAK,IAAI;EAC/G,CAAC,CAAC,KAAK,IAAI,CACb;EACA,OAAO;CACT;CACA,IAAI,YAAY,QAAQ;EACtB,IAAI,KAAK,SAAS,UAAU,GAAG;GAC7B,aAAa,OAAO,0EAA0E;GAC9F,OAAO;EACT;EACA,IAAI,OAAO,QACT,aAAa,OACX,OAAO,OAAO,QAAQ,SAAS,SAAU,KAAK,SAAS,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAE,CAAC,CAAC,KAAK,IAAI,CAC9F;OAEA,aAAa,OACX,CACE,YAAY,OAAO,GAAG,eACtB,GAAG,OAAO,OAAO,QAAQ,UAAU,MAAM,OAAO,CAAC,CAAC,KAAK,UAAU,GAAG,MAAM,QAAQ,EAAE,IAAI,MAAM,SAAS,CACzG,CAAC,CAAC,KAAK,IAAI,CACb;EACF,OAAO;CACT;CACA,aAAa,OACX,YAAY,UAAU,0CAA0C,6BAA6B,SAC/F;CACA,OAAO;AACT;AAEA,eAAe,KAAK,MAAyB,cAAgD;CAC3F,MAAM,YAAY,kBAAkB,aAAa,GAAG;CACpD,MAAM,UAAU,KAAK,SAAS,OAAO,IACjC,MAAM,aAAa,SAAS,QAAQ,SAAS,IAC7C,MAAM,aAAa,SAAS,cAAc,SAAS;CACvD,MAAM,YAAa,MAAM,aAAa,WAAW,KAAK,KAAK,SAAS,OAAO,CAAC,KAAM,CAAC;CACnF,MAAM,OAAO,CACX,GAAG,QAAQ,IAAI,YAAY,GAC3B,GAAG,UAAU,KAAK,WAAW,GAAG,OAAO,GAAG,IAAI,OAAO,MAAM,cAAc,OAAO,MAAM,CACxF;CACA,aAAa,OAAO,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,YAAY;CACpE,OAAO;AACT;AAEA,eAAe,OAAO,MAAyB,cAAgD;CAC7F,MAAM,SAAS,MAAM,eAAe,KAAK,IAAI,YAAY;CACzD,IAAI,CAAC,QAAQ,OAAO;CACpB,aAAa,OACX;EACE,OAAO,OAAO;EACd,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,YAAY,OAAO;EACnB,GAAI,OAAO,gBAAgB,CAAC,mBAAmB,OAAO,eAAe,IAAI,CAAC;EAC1E,QAAQ,OAAO;EACf,YAAY,OAAO;EACnB,QAAQ,OAAO;EACf,YAAY,OAAO;EACnB,QAAQ,OAAO;EACf,GAAI,OAAO,OAAO,CAAC,SAAS,WAAW,MAAM,GAAG,IAAI,CAAC;CACvD,CAAC,CAAC,KAAK,IAAI,CACb;CACA,OAAO;AACT;AAEA,eAAe,KAAK,MAAyB,cAAgD;CAC3F,MAAM,SAAS,MAAM,eAAe,KAAK,IAAI,YAAY;CACzD,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,QAAQ,gBAAgB,OAAO,MAAM,SAAS,CAAC,KAAA;CACrD,MAAM,SAAS,MAAM,aAAa,UAAU,KAAK,OAAO,SAAS,EAAE,MAAM,CAAC;CAC1E,IAAI,CAAC,OAAO,QAAQ;EAClB,aAAa,OAAO,UAAU,OAAO,GAAG,2BAA2B;EACnE,OAAO;CACT;CACA,IAAI,OAAO,MAAM,aAAa,OAAO,OAAO,IAAI;CAChD,IAAI,CAAC,KAAK,SAAS,UAAU,GAAG,OAAO;CACvC,OAAO,OAAO,QAAQ,OAAO,UAAU,YAAY;AACrD;AAEA,eAAe,KAAK,MAAyB,cAAgD;CAC3F,MAAM,SAAS,MAAM,eAAe,KAAK,IAAI,YAAY;CACzD,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,OAAO,UAAU,WAAW;EAC9B,aAAa,OAAO,UAAU,OAAO,GAAG,cAAc,OAAO,OAAO;EACpE,OAAO;CACT;CACA,MAAM,SAAS,OAAO,MAAM,UAAU;CACtC,IAAI,CAAE,MAAM,WAAW,QAAQ,cAAc,MAAM,GAAI;EACrD,aAAa,OAAO,yBAAyB,OAAO,IAAI;EACxD,OAAO;CACT;CACA,MAAM,aAAa,SAAS,SAC1B,OAAO,IACP;EACE,QAAQ;EACR,MAAM;EACN,QAAQ;EACR,GAAI,SAAS,EAAE,YAAY,OAAO,IAAI,CAAC;CACzC,GACA,OAAO,SACT;CACA,aAAa,OAAO,WAAW,OAAO,GAAG,SAAS,OAAO,SAAS;CAClE,OAAO;AACT;AAEA,eAAe,QAAQ,MAAyB,cAAgD;CAC9F,MAAM,YAAY,kBAAkB,aAAa,GAAG;CACpD,MAAM,UAAU,MAAM,aAAa,SAAS,cAAc,SAAS;CACnE,MAAM,SAAS,OAAO,MAAM,UAAU;CACtC,KAAK,MAAM,YAAa,MAAM,aAAa,WAAW,KAAK,KAAM,CAAC,GAAG;EACnE,MAAM,aAAa,UAAW,YAAY,SAAS,IAAI,MAAM;EAC7D,aAAa,OAAO,+BAA+B,SAAS,IAAI;CAClE;CACA,MAAM,UAA0B,CAAC;CACjC,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,CAAE,MAAM,WAAW,QAAQ,cAAc,MAAM,GAAI;EACvD,MAAM,aAAa,SAAS,SAC1B,OAAO,IACP;GACE,QAAQ;GACR,MAAM;GACN,QAAQ;GACR,GAAI,SAAS,EAAE,YAAY,OAAO,IAAI,CAAC;EACzC,GACA,OAAO,SACT;EACA,QAAQ,KAAK,MAAM;CACrB;CACA,aAAa,OACX,QAAQ,WAAW,IACf,iCAAiC,cACjC,WAAW,QAAQ,OAAO,yBAAyB,UAAU,IAAI,QAAQ,KAAK,WAAW,OAAO,EAAE,CAAC,CAAC,KAAK,IAAI,GACnH;CACA,IAAI,QAAQ,WAAW,QAAQ,QAAQ;EACrC,aAAa,OAAO,kBAAkB,QAAQ,SAAS,QAAQ,OAAO,yBAAyB,WAAW;EAC1G,OAAO;CACT;CACA,OAAO;AACT;AAEA,eAAe,MAAM,MAAyB,cAAgD;CAC5F,MAAM,SAAS,MAAM,eAAe,KAAK,IAAI,YAAY;CACzD,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,OAAO,UAAA,aAA2B;EACpC,aAAa,OAAO,UAAU,OAAO,GAAG,4CAA4C,OAAO,IAAI;EAC/F,OAAO;CACT;CACA,IAAI,CAAC,OAAO,aAAa;EACvB,aAAa,OAAO,UAAU,OAAO,GAAG,wCAAwC;EAChF,OAAO;CACT;CAEA,MAAM,OADW,OAAO,MAAM,QACV,KAAM,MAAM,aAAa,UAAU;CACvD,MAAM,UAAU,KAAK,SAAS,SAAS,KAAK,CAAC,KAAK,SAAS,IAAI,IAAI,GAAG,KAAK,MAAM;CACjF,IAAI,CAAC,qBAAqB,IAAI,OAAO,OAAO,KAAK,CAAC,OAAO,eAAe;EACtE,aAAa,OAAO,UAAU,OAAO,GAAG,YAAY,OAAO,QAAQ,0CAA0C;EAC7G,OAAO;CACT;CACA,IAAI,CAAE,MAAM,aAAa,YAAY,MAAM,OAAO,eAAe,OAAO,GAAI;EAC1E,aAAa,OAAO,UAAU,OAAO,GAAG,mCAAmC;EAC3E,OAAO;CACT;CACA,aAAa,OAAO,iBAAiB,OAAO,IAAI;CAChD,OAAO;AACT;AAEA,eAAe,eACb,IACA,cACmC;CACnC,IAAI,CAAC,IAAI;EACP,aAAa,OAAO,yBAAyB;EAC7C;CACF;CACA,MAAM,YAAY,kBAAkB,aAAa,GAAG;CACpD,MAAM,eAAe,MAAM,aAAa,SAAS,IAAI,IAAI,SAAS;CAClE,IAAI,CAAC,cAAc;EACjB,aAAa,OAAO,qBAAqB,IAAI;EAC7C;CACF;CACA,OAAO;AACT;AAEA,SAAS,aAAa,QAA8B;CAClD,MAAM,SAAS,iBAAiB,QAAQ,KAAK,IAAI,CAAC;CAClD,OAAO,GAAG,OAAO,GAAG,IAAI,OAAO,MAAM,IAAI,OAAO,QAAQ,IAAI;AAC9D;AAEA,SAAS,WAAW,QAA8B;CAChD,MAAM,OAAO,OAAO;CACpB,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,SAAS,KAAK,SAAS,OAAO,SAAS,KAAK,SAAS,KAAK,SAAS,IAAI,KAAK,WAAW;CAC7F,MAAM,cAAc,KAAK,oBAAoB,KAAK,KAAK,kBAAkB,KAAK;CAC9E,MAAM,OAAO,KAAK,aAAa,KAAK,KAAK,WAAW,KAAK;CACzD,OAAO,GAAG,KAAK,SAAS,SAAS,cAAc;AACjD;AAEA,SAAS,OAAO,MAAyB,MAAkC;CACzE,MAAM,QAAQ,KAAK,QAAQ,IAAI;CAC/B,OAAO,SAAS,IAAI,KAAK,QAAQ,KAAK,KAAA;AACxC;AAEA,SAAS,gBAAgB,OAA+C;CACtE,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,SAAS,OAAO,KAAK;CAC3B,OAAO,OAAO,UAAU,MAAM,KAAK,SAAS,IAAI,SAAS,KAAA;AAC3D;AAEA,eAAe,OAAO,QAAsB,aAAqB,cAAgD;CAC/G,IAAI,SAAS;CACb,OAAO,IAAI,SAAiB,YAAY;EACtC,IAAI,UAAU;EACd,IAAI;EACJ,IAAI,QAAQ,QAAQ,QAAQ;EAC5B,MAAM,QAAQ,OAAO,YAAqC;GACxD,IAAI,QAAQ,OAAO,QAAQ,SAAS;GACpC,IAAI,QAAQ,SAAS,QAAQ;GAC7B,MAAM,SAAS,QAAQ,OAAO;GAC9B,MAAM,SAAS,OAAO,MAAM,MAAM;GAClC,MAAM,aAAa,MAAM,KAAK,OAAO,SAAS,GAAG;GACjD,IAAI;IACF,MAAM,EAAE,cAAc,MAAM,WAAW,KAAK,QAAQ,GAAG,QAAQ,MAAM;IACrE,IAAI,YAAY,GAAG,aAAa,OAAO,OAAO,SAAS,GAAG,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC;IACrF,UAAU;GACZ,UAAU;IACR,MAAM,WAAW,MAAM;GACzB;EACF;EACA,MAAM,UAAU,UAAyB;GACvC,IAAK,MAAgC,SAAS,UAC5C,aAAa,OAAO,UAAU,OAAO,GAAG,yCAAyC,OAAO,KAAK,GAAG;EAEpG;EACA,MAAM,YAAY,YAA4B;GAC5C,IAAI,SAAS;GACb,QAAQ,MAAM,WAAW,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM;EACvD;EACA,MAAM,sBAA4B;GAChC,IAAI,SAAS;GACb,UAAU;GACV,KAAG,YAAY,OAAO,SAAS,QAAQ;GACvC,cAAc,UAAU;GACxB,QAAQ,IAAI,UAAU,aAAa;GACnC,QAAQ,MAAM,KAAK,YAAY,MAAM,MAAM,KAAK,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,MAAM;GAC9E,MAAW,WAAW,QAAQ,CAAC,CAAC;EAClC;EACA,QAAQ,KAAK,UAAU,aAAa;EACpC,KAAG,UAAU,OAAO,SAAS,EAAE,UAAA,IAA6B,GAAG,QAAQ;EACvE,aAAa,kBAAkB;GAC7B,aAAkB,SAAS,IAAI,OAAO,EAAE,CAAC,CAAC,MAAM,YAAY;IAC1D,IAAI,SAAS,UAAA,aAA2B,cAAc;GACxD,CAAC;EACH,GAAA,GAAqB;CACvB,CAAC;AACH;AAEA,eAAe,WAAW,QAAsB,cAA+B,QAAmC;CAChH,OAAO,kBAAkB,QAAQ,aAAa,UAAU,aAAa,aAAa;EAChF,UAAU,aAAa;EACvB,QAAQ;GAAE,QAAQ;GAAgB,mBAAmB;GAAa,GAAI,SAAS,EAAE,YAAY,OAAO,IAAI,CAAC;EAAG;CAC9G,CAAC;AACH;AAEA,SAAgB,gBAAwB;CACtC,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI;AACb"}
package/dist/cliApp.cjs CHANGED
@@ -1,4 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_cliApp = require("./cliApp-DkhnAFR1.cjs");
2
+ const require_cliApp = require("./cliApp-GKcqD6CT.cjs");
3
3
  exports.runCli = require_cliApp.runCli;
4
4
  exports.runnerCliHelp = require_cliApp.runnerCliHelp;
package/dist/cliApp.mjs CHANGED
@@ -1 +1 @@
1
- export { t as runCli, n as runnerCliHelp } from "./cliApp-DjU_M0ho.mjs";
1
+ export { t as runCli, n as runnerCliHelp } from "./cliApp-g7APOwcU.mjs";
@@ -28,6 +28,7 @@ let _agimon_ai_po_core_mcpFacet = require("@agimon-ai/po-core/mcpFacet");
28
28
  let _agimon_ai_po_core_serverPlugin = require("@agimon-ai/po-core/serverPlugin");
29
29
  require("@agimon-ai/po-core/codemode");
30
30
  require("@agimon-ai/po-core/platform");
31
+ require("@agimon-ai/po-core/telemetry");
31
32
  let typebox = require("typebox");
32
33
  let node_child_process = require("node:child_process");
33
34
  require("@agimon-ai/po-core/packageApi");
@@ -36,11 +37,10 @@ _xterm_headless = __toESM(_xterm_headless, 1);
36
37
  require("@rmux/sdk");
37
38
  let node_util = require("node:util");
38
39
  require("@agimon-ai/foundation-process-registry");
39
- require("@agimon-ai/po-core/childProcess");
40
40
  require("typebox/value");
41
41
  require("@agimon-ai/po-core/backgroundWork");
42
+ require("@agimon-ai/po-core/childProcess");
42
43
  require("@agimon-ai/po-core/extensionFile");
43
- require("@agimon-ai/po-core/telemetry");
44
44
  require("@earendil-works/pi-coding-agent");
45
45
  //#region src/extensions/workspaces/sessions/(backend)/skill/help-how-to-runner.mcp.ts
46
46
  var help_how_to_runner_mcp_default = (0, _agimon_ai_po_core_mcpFacet.defineMcpSkill)({