@agimon-ai/po-runner 0.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +257 -0
- package/dist/ansiScrub-CsqTSaVT.mjs +71 -0
- package/dist/ansiScrub-CsqTSaVT.mjs.map +1 -0
- package/dist/ansiScrub-D5gpHDch.cjs +100 -0
- package/dist/ansiScrub-D5gpHDch.cjs.map +1 -0
- package/dist/ansiScrub.cjs +7 -0
- package/dist/ansiScrub.d.mts +26 -0
- package/dist/ansiScrub.d.mts.map +1 -0
- package/dist/ansiScrub.mjs +2 -0
- package/dist/apiContracts.cjs +162 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +157 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/bashResult-DNgtahGR.cjs +629 -0
- package/dist/bashResult-DNgtahGR.cjs.map +1 -0
- package/dist/bashResult-DcrRaBLv.d.mts +49 -0
- package/dist/bashResult-DcrRaBLv.d.mts.map +1 -0
- package/dist/bashResult-bIVSzoj-.mjs +546 -0
- package/dist/bashResult-bIVSzoj-.mjs.map +1 -0
- package/dist/bashRunService-CJ02XaAq.mjs +440 -0
- package/dist/bashRunService-CJ02XaAq.mjs.map +1 -0
- package/dist/bashRunService-Ci0cGdYO.d.mts +71 -0
- package/dist/bashRunService-Ci0cGdYO.d.mts.map +1 -0
- package/dist/bashRunService-DyqxOSlx.cjs +445 -0
- package/dist/bashRunService-DyqxOSlx.cjs.map +1 -0
- package/dist/bashRunService.cjs +3 -0
- package/dist/bashRunService.d.mts +36 -0
- package/dist/bashRunService.d.mts.map +1 -0
- package/dist/bashRunService.mjs +2 -0
- package/dist/bashSchema.cjs +5 -0
- package/dist/bashSchema.d.mts +7 -0
- package/dist/bashSchema.d.mts.map +1 -0
- package/dist/bashSchema.mjs +2 -0
- package/dist/bashTool-B1EwZLo_.d.mts +20 -0
- package/dist/bashTool-B1EwZLo_.d.mts.map +1 -0
- package/dist/bashTool-C-k7fmyh.mjs +24 -0
- package/dist/bashTool-C-k7fmyh.mjs.map +1 -0
- package/dist/bashTool-CdgtqVFD.cjs +41 -0
- package/dist/bashTool-CdgtqVFD.cjs.map +1 -0
- package/dist/bashTool.cjs +13 -0
- package/dist/bashTool.cjs.map +1 -0
- package/dist/bashTool.d.mts +8 -0
- package/dist/bashTool.d.mts.map +1 -0
- package/dist/bashTool.mjs +11 -0
- package/dist/bashTool.mjs.map +1 -0
- package/dist/bin/cli.cjs +78 -0
- package/dist/bin/cli.cjs.map +1 -0
- package/dist/bin/cli.d.mts +4 -0
- package/dist/bin/cli.d.mts.map +1 -0
- package/dist/bin/cli.mjs +77 -0
- package/dist/bin/cli.mjs.map +1 -0
- package/dist/bin/logSink.cjs +102 -0
- package/dist/bin/logSink.cjs.map +1 -0
- package/dist/bin/logSink.d.mts +4 -0
- package/dist/bin/logSink.d.mts.map +1 -0
- package/dist/bin/logSink.mjs +99 -0
- package/dist/bin/logSink.mjs.map +1 -0
- package/dist/bin/runnerHost.cjs +131 -0
- package/dist/bin/runnerHost.cjs.map +1 -0
- package/dist/bin/runnerHost.d.mts +4 -0
- package/dist/bin/runnerHost.d.mts.map +1 -0
- package/dist/bin/runnerHost.mjs +127 -0
- package/dist/bin/runnerHost.mjs.map +1 -0
- package/dist/cliApp-Dj9nwaOv.cjs +244 -0
- package/dist/cliApp-Dj9nwaOv.cjs.map +1 -0
- package/dist/cliApp-v8Z3rMCi.mjs +237 -0
- package/dist/cliApp-v8Z3rMCi.mjs.map +1 -0
- package/dist/cliApp.cjs +3 -0
- package/dist/cliApp.d.mts +18 -0
- package/dist/cliApp.d.mts.map +1 -0
- package/dist/cliApp.mjs +2 -0
- package/dist/clock-Cv2pddgo.mjs +16 -0
- package/dist/clock-Cv2pddgo.mjs.map +1 -0
- package/dist/clock-euJkweLq.d.mts +11 -0
- package/dist/clock-euJkweLq.d.mts.map +1 -0
- package/dist/clock-jqnLWziT.cjs +21 -0
- package/dist/clock-jqnLWziT.cjs.map +1 -0
- package/dist/clock.cjs +3 -0
- package/dist/clock.d.mts +10 -0
- package/dist/clock.d.mts.map +1 -0
- package/dist/clock.mjs +2 -0
- package/dist/compaction-4QSUsBxq.cjs +40 -0
- package/dist/compaction-4QSUsBxq.cjs.map +1 -0
- package/dist/compaction-DCw80VZj.mjs +29 -0
- package/dist/compaction-DCw80VZj.mjs.map +1 -0
- package/dist/compaction.cjs +4 -0
- package/dist/compaction.d.mts +10 -0
- package/dist/compaction.d.mts.map +1 -0
- package/dist/compaction.mjs +2 -0
- package/dist/config.cjs +41 -0
- package/dist/config.d.mts +2 -0
- package/dist/config.mjs +2 -0
- package/dist/extensions/mcp-ui.mjs +23 -0
- package/dist/extensions/mcp-ui.mjs.map +1 -0
- package/dist/extensions/mcp.cjs +142 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +114 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/extensions/server.cjs +1156 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +1146 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +1785 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/headless-BGYfIlNk.mjs +169 -0
- package/dist/headless-BGYfIlNk.mjs.map +1 -0
- package/dist/headless-CHUsBGhg.cjs +180 -0
- package/dist/headless-CHUsBGhg.cjs.map +1 -0
- package/dist/index-BZL7keVW.d.mts +31 -0
- package/dist/index-BZL7keVW.d.mts.map +1 -0
- package/dist/index-BwB_jTI9.d.mts +12 -0
- package/dist/index-BwB_jTI9.d.mts.map +1 -0
- package/dist/index-D9-IXax4.d.mts +43 -0
- package/dist/index-D9-IXax4.d.mts.map +1 -0
- package/dist/index-DBz0DxnL.d.mts +39 -0
- package/dist/index-DBz0DxnL.d.mts.map +1 -0
- package/dist/index-DM7yJpMs.d.mts +25 -0
- package/dist/index-DM7yJpMs.d.mts.map +1 -0
- package/dist/index-Dhi8asLH.d.mts +22 -0
- package/dist/index-Dhi8asLH.d.mts.map +1 -0
- package/dist/index.cjs +19 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +6 -0
- package/dist/launcher-BDm6-DiQ.mjs +150 -0
- package/dist/launcher-BDm6-DiQ.mjs.map +1 -0
- package/dist/launcher-BG5t9nvG.d.mts +39 -0
- package/dist/launcher-BG5t9nvG.d.mts.map +1 -0
- package/dist/launcher-SlyE8R3b.cjs +157 -0
- package/dist/launcher-SlyE8R3b.cjs.map +1 -0
- package/dist/launcher.cjs +3 -0
- package/dist/launcher.d.mts +33 -0
- package/dist/launcher.d.mts.map +1 -0
- package/dist/launcher.mjs +2 -0
- package/dist/lifeline-BZaQA0rm.mjs +88 -0
- package/dist/lifeline-BZaQA0rm.mjs.map +1 -0
- package/dist/lifeline-BuV_erib.cjs +98 -0
- package/dist/lifeline-BuV_erib.cjs.map +1 -0
- package/dist/lifeline-eJdZV0nN.d.mts +19 -0
- package/dist/lifeline-eJdZV0nN.d.mts.map +1 -0
- package/dist/lifeline.cjs +6 -0
- package/dist/lifeline.d.mts +21 -0
- package/dist/lifeline.d.mts.map +1 -0
- package/dist/lifeline.mjs +3 -0
- package/dist/logFile-C7oQ_v-g.d.mts +16 -0
- package/dist/logFile-C7oQ_v-g.d.mts.map +1 -0
- package/dist/logFile-CNvepZh0.cjs +67 -0
- package/dist/logFile-CNvepZh0.cjs.map +1 -0
- package/dist/logFile-KKPbkC-a.mjs +60 -0
- package/dist/logFile-KKPbkC-a.mjs.map +1 -0
- package/dist/logFile.cjs +3 -0
- package/dist/logFile.d.mts +11 -0
- package/dist/logFile.d.mts.map +1 -0
- package/dist/logFile.mjs +2 -0
- package/dist/logReader-B6J7L7Ua.cjs +282 -0
- package/dist/logReader-B6J7L7Ua.cjs.map +1 -0
- package/dist/logReader-MdW9ffvV.d.mts +43 -0
- package/dist/logReader-MdW9ffvV.d.mts.map +1 -0
- package/dist/logReader-UfbDbwQl.mjs +257 -0
- package/dist/logReader-UfbDbwQl.mjs.map +1 -0
- package/dist/logReader.cjs +4 -0
- package/dist/logReader.d.mts +3 -0
- package/dist/logReader.mjs +2 -0
- package/dist/processControl-BAOa3oo_.d.mts +14 -0
- package/dist/processControl-BAOa3oo_.d.mts.map +1 -0
- package/dist/processControl-DApTpV16.mjs +26 -0
- package/dist/processControl-DApTpV16.mjs.map +1 -0
- package/dist/processControl-DNJ1WZ9N.cjs +31 -0
- package/dist/processControl-DNJ1WZ9N.cjs.map +1 -0
- package/dist/processControl.cjs +3 -0
- package/dist/processControl.d.mts +10 -0
- package/dist/processControl.d.mts.map +1 -0
- package/dist/processControl.mjs +2 -0
- package/dist/ptyHost-DWWtRrGn.d.mts +38 -0
- package/dist/ptyHost-DWWtRrGn.d.mts.map +1 -0
- package/dist/ptyHost-DlQtTB8F.mjs +150 -0
- package/dist/ptyHost-DlQtTB8F.mjs.map +1 -0
- package/dist/ptyHost-MI2Z6WTG.cjs +157 -0
- package/dist/ptyHost-MI2Z6WTG.cjs.map +1 -0
- package/dist/ptyHost.cjs +3 -0
- package/dist/ptyHost.d.mts +26 -0
- package/dist/ptyHost.d.mts.map +1 -0
- package/dist/ptyHost.mjs +2 -0
- package/dist/ptySpawner-B6GsueZd.d.mts +28 -0
- package/dist/ptySpawner-B6GsueZd.d.mts.map +1 -0
- package/dist/ptySpawner-DEi1KOnd.mjs +17 -0
- package/dist/ptySpawner-DEi1KOnd.mjs.map +1 -0
- package/dist/ptySpawner-ijw0g5_T.cjs +22 -0
- package/dist/ptySpawner-ijw0g5_T.cjs.map +1 -0
- package/dist/ptySpawner.cjs +3 -0
- package/dist/ptySpawner.d.mts +14 -0
- package/dist/ptySpawner.d.mts.map +1 -0
- package/dist/ptySpawner.mjs +2 -0
- package/dist/reconcile-BugwYFbV.cjs +133 -0
- package/dist/reconcile-BugwYFbV.cjs.map +1 -0
- package/dist/reconcile-DMeNSxwn.mjs +114 -0
- package/dist/reconcile-DMeNSxwn.mjs.map +1 -0
- package/dist/reconcile.cjs +5 -0
- package/dist/reconcile.d.mts +36 -0
- package/dist/reconcile.d.mts.map +1 -0
- package/dist/reconcile.mjs +2 -0
- package/dist/responseEnvelope.cjs +19 -0
- package/dist/responseEnvelope.d.mts +50 -0
- package/dist/responseEnvelope.d.mts.map +1 -0
- package/dist/responseEnvelope.mjs +3 -0
- package/dist/rmuxBackend-CaEWooWn.d.mts +36 -0
- package/dist/rmuxBackend-CaEWooWn.d.mts.map +1 -0
- package/dist/rmuxBackend-DfyQwJrc.mjs +509 -0
- package/dist/rmuxBackend-DfyQwJrc.mjs.map +1 -0
- package/dist/rmuxBackend-DlxgqRZ1.cjs +523 -0
- package/dist/rmuxBackend-DlxgqRZ1.cjs.map +1 -0
- package/dist/rmuxBackend.cjs +4 -0
- package/dist/rmuxBackend.d.mts +3 -0
- package/dist/rmuxBackend.mjs +2 -0
- package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
- package/dist/rtkProcessor-B4_urXQr.mjs +10 -0
- package/dist/rtkProcessor-B4_urXQr.mjs.map +1 -0
- package/dist/rtkProcessor-B9KPdyzr.mjs +309 -0
- package/dist/rtkProcessor-B9KPdyzr.mjs.map +1 -0
- package/dist/rtkProcessor-Bc4L4o_8.cjs +335 -0
- package/dist/rtkProcessor-Bc4L4o_8.cjs.map +1 -0
- package/dist/rtkProcessor-CVZvNhkJ.d.mts +35 -0
- package/dist/rtkProcessor-CVZvNhkJ.d.mts.map +1 -0
- package/dist/rtkProcessor-CsrCPyTQ.cjs +33 -0
- package/dist/rtkProcessor-CsrCPyTQ.cjs.map +1 -0
- package/dist/rtkProcessor.cjs +11 -0
- package/dist/rtkProcessor.d.mts +7 -0
- package/dist/rtkProcessor.d.mts.map +1 -0
- package/dist/rtkProcessor.mjs +3 -0
- package/dist/runnerConfig-BQBlVAXn.mjs +134 -0
- package/dist/runnerConfig-BQBlVAXn.mjs.map +1 -0
- package/dist/runnerConfig-BjtulXBI.d.mts +57 -0
- package/dist/runnerConfig-BjtulXBI.d.mts.map +1 -0
- package/dist/runnerConfig-D2tGq5ZM.cjs +367 -0
- package/dist/runnerConfig-D2tGq5ZM.cjs.map +1 -0
- package/dist/runnerDependencies-DruByBGS.mjs +647 -0
- package/dist/runnerDependencies-DruByBGS.mjs.map +1 -0
- package/dist/runnerDependencies-KMZigIxB.cjs +667 -0
- package/dist/runnerDependencies-KMZigIxB.cjs.map +1 -0
- package/dist/runnerDependencies.cjs +3 -0
- package/dist/runnerDependencies.d.mts +2 -0
- package/dist/runnerDependencies.mjs +2 -0
- package/dist/runnerNamer-B8FH7n2v.mjs +44 -0
- package/dist/runnerNamer-B8FH7n2v.mjs.map +1 -0
- package/dist/runnerNamer-we1XVYfM.cjs +49 -0
- package/dist/runnerNamer-we1XVYfM.cjs.map +1 -0
- package/dist/runnerNamer.cjs +3 -0
- package/dist/runnerNamer.d.mts +11 -0
- package/dist/runnerNamer.d.mts.map +1 -0
- package/dist/runnerNamer.mjs +2 -0
- package/dist/runnerPaths-BTEuzxw0.cjs +471 -0
- package/dist/runnerPaths-BTEuzxw0.cjs.map +1 -0
- package/dist/runnerPaths-B_dAAeco.mjs +451 -0
- package/dist/runnerPaths-B_dAAeco.mjs.map +1 -0
- package/dist/runnerPaths.cjs +3 -0
- package/dist/runnerPaths.d.mts +50 -0
- package/dist/runnerPaths.d.mts.map +1 -0
- package/dist/runnerPaths.mjs +2 -0
- package/dist/runnerRegistry-BmkZTJHi.cjs +371 -0
- package/dist/runnerRegistry-BmkZTJHi.cjs.map +1 -0
- package/dist/runnerRegistry-CeOHEjBv.mjs +339 -0
- package/dist/runnerRegistry-CeOHEjBv.mjs.map +1 -0
- package/dist/runnerRegistry-CtJiD02R.d.mts +88 -0
- package/dist/runnerRegistry-CtJiD02R.d.mts.map +1 -0
- package/dist/runnerRegistry.cjs +4 -0
- package/dist/runnerRegistry.d.mts +3 -0
- package/dist/runnerRegistry.mjs +2 -0
- package/dist/runnerSettings-DiV1lvzx.d.mts +34 -0
- package/dist/runnerSettings-DiV1lvzx.d.mts.map +1 -0
- package/dist/services/lifeline/client.cjs +52 -0
- package/dist/services/lifeline/client.cjs.map +1 -0
- package/dist/services/lifeline/client.d.mts +22 -0
- package/dist/services/lifeline/client.d.mts.map +1 -0
- package/dist/services/lifeline/client.mjs +48 -0
- package/dist/services/lifeline/client.mjs.map +1 -0
- package/dist/services/runnerSupervisor/index.cjs +207 -0
- package/dist/services/runnerSupervisor/index.cjs.map +1 -0
- package/dist/services/runnerSupervisor/index.d.mts +71 -0
- package/dist/services/runnerSupervisor/index.d.mts.map +1 -0
- package/dist/services/runnerSupervisor/index.mjs +195 -0
- package/dist/services/runnerSupervisor/index.mjs.map +1 -0
- package/dist/session-BC43uEyG.cjs +29 -0
- package/dist/session-BC43uEyG.cjs.map +1 -0
- package/dist/session-Chsb_Nh2.mjs +18 -0
- package/dist/session-Chsb_Nh2.mjs.map +1 -0
- package/dist/session-DE2Acesp.mjs +6 -0
- package/dist/session-DE2Acesp.mjs.map +1 -0
- package/dist/session-SfIPtdZO.cjs +11 -0
- package/dist/session-SfIPtdZO.cjs.map +1 -0
- package/dist/session.cjs +6 -0
- package/dist/session.d.mts +11 -0
- package/dist/session.d.mts.map +1 -0
- package/dist/session.mjs +3 -0
- package/dist/spawner-BxFsINXP.cjs +68 -0
- package/dist/spawner-BxFsINXP.cjs.map +1 -0
- package/dist/spawner-CdrN_HNk.d.mts +35 -0
- package/dist/spawner-CdrN_HNk.d.mts.map +1 -0
- package/dist/spawner-LoVsn55-.mjs +63 -0
- package/dist/spawner-LoVsn55-.mjs.map +1 -0
- package/dist/spawner.cjs +3 -0
- package/dist/spawner.d.mts +9 -0
- package/dist/spawner.d.mts.map +1 -0
- package/dist/spawner.mjs +2 -0
- package/dist/type-BXM03qSw.d.mts +44 -0
- package/dist/type-BXM03qSw.d.mts.map +1 -0
- package/dist/type-C31e1gla.d.mts +43 -0
- package/dist/type-C31e1gla.d.mts.map +1 -0
- package/dist/type-vWQNTahw.d.mts +12 -0
- package/dist/type-vWQNTahw.d.mts.map +1 -0
- package/dist/types.cjs +0 -0
- package/dist/types.d.mts +3 -0
- package/dist/types.mjs +1 -0
- package/llms.txt +9 -0
- package/package.json +303 -0
- package/skills/doom-runner/SKILL.md +78 -0
- package/src/prompts/po-use-runner/SKILL.md +47 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_rolldown_runtime = require("../rolldown-runtime-VH7oDXx4.cjs");
|
|
3
|
+
const require_services_lifeline_client = require("../services/lifeline/client.cjs");
|
|
4
|
+
let node_fs = require("node:fs");
|
|
5
|
+
node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
|
|
6
|
+
let node_url = require("node:url");
|
|
7
|
+
let node_child_process = require("node:child_process");
|
|
8
|
+
let node_os = require("node:os");
|
|
9
|
+
node_os = require_rolldown_runtime.__toESM(node_os, 1);
|
|
10
|
+
let _agimon_ai_po_telemetry = require("@agimon-ai/po-telemetry");
|
|
11
|
+
//#region src/bin/runnerHost.ts
|
|
12
|
+
const POLL_MS = 10;
|
|
13
|
+
const FORWARDED_SIGNAL_GRACE_MS = 2e3;
|
|
14
|
+
const HOST_FAILED_EVENT = "doom_runner.host_failed";
|
|
15
|
+
const INVALID_COMMAND_SPEC_ERROR = "invalid runner command spec";
|
|
16
|
+
async function main(argv = process.argv.slice(2)) {
|
|
17
|
+
const startedAt = Date.now();
|
|
18
|
+
const telemetry = (0, _agimon_ai_po_telemetry.createDoomTelemetry)({
|
|
19
|
+
serviceName: "doom-runner-host",
|
|
20
|
+
packageName: "@agimon-ai/po-runner",
|
|
21
|
+
env: process.env,
|
|
22
|
+
enableLogs: true,
|
|
23
|
+
enableTraces: true
|
|
24
|
+
});
|
|
25
|
+
const [specPath, gatePath, exitPath] = argv;
|
|
26
|
+
let spec;
|
|
27
|
+
try {
|
|
28
|
+
if (!specPath || !gatePath || !exitPath) throw new Error("runnerHost requires spec, gate, and exit paths");
|
|
29
|
+
spec = readSpec(specPath);
|
|
30
|
+
await waitForGate(gatePath);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });
|
|
33
|
+
await telemetry.shutdown();
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
let child;
|
|
37
|
+
let forwardedSignal = null;
|
|
38
|
+
let escalationTimer;
|
|
39
|
+
let ownerLost = false;
|
|
40
|
+
const signalChild = (signal) => {
|
|
41
|
+
if (!child?.pid) return;
|
|
42
|
+
try {
|
|
43
|
+
process.kill(-child.pid, signal);
|
|
44
|
+
} catch {
|
|
45
|
+
child.kill(signal);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const forward = (signal) => {
|
|
49
|
+
forwardedSignal = signal;
|
|
50
|
+
signalChild(signal);
|
|
51
|
+
escalationTimer ??= setTimeout(() => signalChild("SIGKILL"), FORWARDED_SIGNAL_GRACE_MS);
|
|
52
|
+
};
|
|
53
|
+
for (const signal of [
|
|
54
|
+
"SIGTERM",
|
|
55
|
+
"SIGINT",
|
|
56
|
+
"SIGHUP"
|
|
57
|
+
]) process.once(signal, () => forward(signal));
|
|
58
|
+
child = (0, node_child_process.spawn)(spec.command, {
|
|
59
|
+
cwd: spec.cwd,
|
|
60
|
+
env: spec.env,
|
|
61
|
+
shell: "/bin/bash",
|
|
62
|
+
stdio: "inherit",
|
|
63
|
+
detached: true
|
|
64
|
+
});
|
|
65
|
+
if (forwardedSignal) forward(forwardedSignal);
|
|
66
|
+
const detachLifeline = require_services_lifeline_client.watchOwner(() => {
|
|
67
|
+
ownerLost = true;
|
|
68
|
+
forward("SIGTERM");
|
|
69
|
+
}, spec.env[require_services_lifeline_client.LIFELINE_ENV]);
|
|
70
|
+
try {
|
|
71
|
+
const result = await new Promise((resolve, reject) => {
|
|
72
|
+
child?.once("error", reject);
|
|
73
|
+
child?.once("exit", (code, signal) => resolve({
|
|
74
|
+
code,
|
|
75
|
+
signal: signal ?? forwardedSignal
|
|
76
|
+
}));
|
|
77
|
+
});
|
|
78
|
+
writeAtomic(exitPath, {
|
|
79
|
+
...result,
|
|
80
|
+
...ownerLost ? {
|
|
81
|
+
reason: "stopped",
|
|
82
|
+
terminationReason: "owner_lost",
|
|
83
|
+
stopReason: "owner session ended"
|
|
84
|
+
} : {}
|
|
85
|
+
});
|
|
86
|
+
const exitCode = result.code !== null ? result.code : result.signal ? 128 + node_os.default.constants.signals[result.signal] : 1;
|
|
87
|
+
await telemetry.recordEvent("doom_runner.host_finished", {
|
|
88
|
+
outcome: result.signal || forwardedSignal ? "signaled" : exitCode === 0 ? "completed" : "failed",
|
|
89
|
+
"runner.exit_code": exitCode,
|
|
90
|
+
...result.signal || forwardedSignal ? { "runner.signal": result.signal ?? forwardedSignal ?? "" } : {},
|
|
91
|
+
duration_ms: Date.now() - startedAt
|
|
92
|
+
});
|
|
93
|
+
return exitCode;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });
|
|
96
|
+
throw error;
|
|
97
|
+
} finally {
|
|
98
|
+
detachLifeline();
|
|
99
|
+
if (escalationTimer) clearTimeout(escalationTimer);
|
|
100
|
+
await telemetry.shutdown();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function readSpec(target) {
|
|
104
|
+
const value = JSON.parse(node_fs.default.readFileSync(target, "utf8"));
|
|
105
|
+
if (typeof value !== "object" || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);
|
|
106
|
+
const spec = value;
|
|
107
|
+
if (typeof spec.command !== "string" || typeof spec.cwd !== "string" || !spec.env) throw new Error(INVALID_COMMAND_SPEC_ERROR);
|
|
108
|
+
return {
|
|
109
|
+
command: spec.command,
|
|
110
|
+
cwd: spec.cwd,
|
|
111
|
+
env: spec.env
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function waitForGate(target) {
|
|
115
|
+
while (!node_fs.default.existsSync(target)) await new Promise((resolve) => setTimeout(resolve, POLL_MS));
|
|
116
|
+
}
|
|
117
|
+
function writeAtomic(target, value) {
|
|
118
|
+
const temporary = `${target}.${process.pid}.tmp`;
|
|
119
|
+
node_fs.default.writeFileSync(temporary, `${JSON.stringify(value)}\n`, { mode: 384 });
|
|
120
|
+
node_fs.default.renameSync(temporary, target);
|
|
121
|
+
}
|
|
122
|
+
if (process.argv[1] && require("url").pathToFileURL(__filename).href === (0, node_url.pathToFileURL)(process.argv[1]).href) main().then((code) => {
|
|
123
|
+
process.exitCode = code;
|
|
124
|
+
}, (error) => {
|
|
125
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
});
|
|
128
|
+
//#endregion
|
|
129
|
+
exports.main = main;
|
|
130
|
+
|
|
131
|
+
//# sourceMappingURL=runnerHost.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runnerHost.cjs","names":["createDoomTelemetry","spawn","watchOwner","LIFELINE_ENV","os","fs","pathToFileURL"],"sources":["../../src/bin/runnerHost.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport { createDoomTelemetry } from '@agimon-ai/po-telemetry';\n\nimport { LIFELINE_ENV, watchOwner } from '../services/lifeline/client';\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 = 'doom_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 const startedAt = Date.now();\n const telemetry = createDoomTelemetry({\n serviceName: 'doom-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 = 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 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('doom_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\nfunction readSpec(target: string): CommandSpec {\n const value: unknown = JSON.parse(fs.readFileSync(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 while (!fs.existsSync(target)) await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n}\n\nfunction writeAtomic(target: string, value: unknown): void {\n const temporary = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n fs.renameSync(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"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runnerHost.d.mts","names":[],"sources":["../../src/bin/runnerHost.ts"],"mappings":";wBAoBsB,KAAK,2BAAkD"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { LIFELINE_ENV, watchOwner } from "../services/lifeline/client.mjs";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import { createDoomTelemetry } from "@agimon-ai/po-telemetry";
|
|
7
|
+
//#region src/bin/runnerHost.ts
|
|
8
|
+
const POLL_MS = 10;
|
|
9
|
+
const FORWARDED_SIGNAL_GRACE_MS = 2e3;
|
|
10
|
+
const HOST_FAILED_EVENT = "doom_runner.host_failed";
|
|
11
|
+
const INVALID_COMMAND_SPEC_ERROR = "invalid runner command spec";
|
|
12
|
+
async function main(argv = process.argv.slice(2)) {
|
|
13
|
+
const startedAt = Date.now();
|
|
14
|
+
const telemetry = createDoomTelemetry({
|
|
15
|
+
serviceName: "doom-runner-host",
|
|
16
|
+
packageName: "@agimon-ai/po-runner",
|
|
17
|
+
env: process.env,
|
|
18
|
+
enableLogs: true,
|
|
19
|
+
enableTraces: true
|
|
20
|
+
});
|
|
21
|
+
const [specPath, gatePath, exitPath] = argv;
|
|
22
|
+
let spec;
|
|
23
|
+
try {
|
|
24
|
+
if (!specPath || !gatePath || !exitPath) throw new Error("runnerHost requires spec, gate, and exit paths");
|
|
25
|
+
spec = readSpec(specPath);
|
|
26
|
+
await waitForGate(gatePath);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });
|
|
29
|
+
await telemetry.shutdown();
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
let child;
|
|
33
|
+
let forwardedSignal = null;
|
|
34
|
+
let escalationTimer;
|
|
35
|
+
let ownerLost = false;
|
|
36
|
+
const signalChild = (signal) => {
|
|
37
|
+
if (!child?.pid) return;
|
|
38
|
+
try {
|
|
39
|
+
process.kill(-child.pid, signal);
|
|
40
|
+
} catch {
|
|
41
|
+
child.kill(signal);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const forward = (signal) => {
|
|
45
|
+
forwardedSignal = signal;
|
|
46
|
+
signalChild(signal);
|
|
47
|
+
escalationTimer ??= setTimeout(() => signalChild("SIGKILL"), FORWARDED_SIGNAL_GRACE_MS);
|
|
48
|
+
};
|
|
49
|
+
for (const signal of [
|
|
50
|
+
"SIGTERM",
|
|
51
|
+
"SIGINT",
|
|
52
|
+
"SIGHUP"
|
|
53
|
+
]) process.once(signal, () => forward(signal));
|
|
54
|
+
child = spawn(spec.command, {
|
|
55
|
+
cwd: spec.cwd,
|
|
56
|
+
env: spec.env,
|
|
57
|
+
shell: "/bin/bash",
|
|
58
|
+
stdio: "inherit",
|
|
59
|
+
detached: true
|
|
60
|
+
});
|
|
61
|
+
if (forwardedSignal) forward(forwardedSignal);
|
|
62
|
+
const detachLifeline = watchOwner(() => {
|
|
63
|
+
ownerLost = true;
|
|
64
|
+
forward("SIGTERM");
|
|
65
|
+
}, spec.env[LIFELINE_ENV]);
|
|
66
|
+
try {
|
|
67
|
+
const result = await new Promise((resolve, reject) => {
|
|
68
|
+
child?.once("error", reject);
|
|
69
|
+
child?.once("exit", (code, signal) => resolve({
|
|
70
|
+
code,
|
|
71
|
+
signal: signal ?? forwardedSignal
|
|
72
|
+
}));
|
|
73
|
+
});
|
|
74
|
+
writeAtomic(exitPath, {
|
|
75
|
+
...result,
|
|
76
|
+
...ownerLost ? {
|
|
77
|
+
reason: "stopped",
|
|
78
|
+
terminationReason: "owner_lost",
|
|
79
|
+
stopReason: "owner session ended"
|
|
80
|
+
} : {}
|
|
81
|
+
});
|
|
82
|
+
const exitCode = result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;
|
|
83
|
+
await telemetry.recordEvent("doom_runner.host_finished", {
|
|
84
|
+
outcome: result.signal || forwardedSignal ? "signaled" : exitCode === 0 ? "completed" : "failed",
|
|
85
|
+
"runner.exit_code": exitCode,
|
|
86
|
+
...result.signal || forwardedSignal ? { "runner.signal": result.signal ?? forwardedSignal ?? "" } : {},
|
|
87
|
+
duration_ms: Date.now() - startedAt
|
|
88
|
+
});
|
|
89
|
+
return exitCode;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });
|
|
92
|
+
throw error;
|
|
93
|
+
} finally {
|
|
94
|
+
detachLifeline();
|
|
95
|
+
if (escalationTimer) clearTimeout(escalationTimer);
|
|
96
|
+
await telemetry.shutdown();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function readSpec(target) {
|
|
100
|
+
const value = JSON.parse(fs.readFileSync(target, "utf8"));
|
|
101
|
+
if (typeof value !== "object" || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);
|
|
102
|
+
const spec = value;
|
|
103
|
+
if (typeof spec.command !== "string" || typeof spec.cwd !== "string" || !spec.env) throw new Error(INVALID_COMMAND_SPEC_ERROR);
|
|
104
|
+
return {
|
|
105
|
+
command: spec.command,
|
|
106
|
+
cwd: spec.cwd,
|
|
107
|
+
env: spec.env
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
async function waitForGate(target) {
|
|
111
|
+
while (!fs.existsSync(target)) await new Promise((resolve) => setTimeout(resolve, POLL_MS));
|
|
112
|
+
}
|
|
113
|
+
function writeAtomic(target, value) {
|
|
114
|
+
const temporary = `${target}.${process.pid}.tmp`;
|
|
115
|
+
fs.writeFileSync(temporary, `${JSON.stringify(value)}\n`, { mode: 384 });
|
|
116
|
+
fs.renameSync(temporary, target);
|
|
117
|
+
}
|
|
118
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) main().then((code) => {
|
|
119
|
+
process.exitCode = code;
|
|
120
|
+
}, (error) => {
|
|
121
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
});
|
|
124
|
+
//#endregion
|
|
125
|
+
export { main };
|
|
126
|
+
|
|
127
|
+
//# sourceMappingURL=runnerHost.mjs.map
|
|
@@ -0,0 +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';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport { createDoomTelemetry } from '@agimon-ai/po-telemetry';\n\nimport { LIFELINE_ENV, watchOwner } from '../services/lifeline/client';\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 = 'doom_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 const startedAt = Date.now();\n const telemetry = createDoomTelemetry({\n serviceName: 'doom-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 = 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 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('doom_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\nfunction readSpec(target: string): CommandSpec {\n const value: unknown = JSON.parse(fs.readFileSync(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 while (!fs.existsSync(target)) await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n}\n\nfunction writeAtomic(target: string, value: unknown): void {\n const temporary = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n fs.renameSync(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"],"mappings":"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"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
|
|
2
|
+
const require_bashResult = require("./bashResult-DNgtahGR.cjs");
|
|
3
|
+
const require_session = require("./session-BC43uEyG.cjs");
|
|
4
|
+
let node_fs = require("node:fs");
|
|
5
|
+
node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
|
|
6
|
+
const STOPPED_REASON = "stopped";
|
|
7
|
+
const TERMINATION_SIGNAL = "SIGTERM";
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/services/cliApp/index.ts
|
|
10
|
+
const MULTIPLEXER_BACKENDS = /* @__PURE__ */ new Set(["rmux", "tmux"]);
|
|
11
|
+
async function runCli(argv, dependencies) {
|
|
12
|
+
const [command, ...args] = argv;
|
|
13
|
+
if (command !== void 0 && command !== "--help" && command !== "-h" && !require_session.inheritedSessionId(dependencies.env)) {
|
|
14
|
+
dependencies.stderr("PI_SESSION_ID is required for doom-runner commands");
|
|
15
|
+
return 1;
|
|
16
|
+
}
|
|
17
|
+
switch (command) {
|
|
18
|
+
case "list": return list(args, dependencies);
|
|
19
|
+
case "status": return status(args, dependencies);
|
|
20
|
+
case "logs": return logs(args, dependencies);
|
|
21
|
+
case "stop": return stop(args, dependencies);
|
|
22
|
+
case "stop-all": return stopAll(args, dependencies);
|
|
23
|
+
case "input": return input(args, dependencies);
|
|
24
|
+
case "--help":
|
|
25
|
+
case "-h":
|
|
26
|
+
case void 0:
|
|
27
|
+
dependencies.stdout(help());
|
|
28
|
+
return 0;
|
|
29
|
+
default:
|
|
30
|
+
dependencies.stderr(`Unknown command: ${command}\n\n${help()}`);
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function list(args, dependencies) {
|
|
35
|
+
const sessionId = require_session.requiredSessionId(dependencies.env);
|
|
36
|
+
const records = args.includes("--all") ? await dependencies.registry.listAll(sessionId) : await dependencies.registry.listBySession(sessionId);
|
|
37
|
+
dependencies.stdout(records.length > 0 ? records.map(formatRecord).join("\n") : "No runners");
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
async function status(args, dependencies) {
|
|
41
|
+
const record = await requiredRecord(args[0], dependencies);
|
|
42
|
+
if (!record) return 1;
|
|
43
|
+
dependencies.stdout([
|
|
44
|
+
`ID: ${record.id}`,
|
|
45
|
+
`Name: ${record.name}`,
|
|
46
|
+
`State: ${record.state}`,
|
|
47
|
+
`Backend: ${record.backend}`,
|
|
48
|
+
...record.backendTarget ? [`Backend target: ${record.backendTarget}`] : [],
|
|
49
|
+
`PID: ${record.pid}`,
|
|
50
|
+
`Command: ${record.command}`,
|
|
51
|
+
`CWD: ${record.cwd}`,
|
|
52
|
+
`Session: ${record.sessionId}`,
|
|
53
|
+
`Log: ${record.logPath}`,
|
|
54
|
+
...record.exit ? [`Exit: ${formatExit(record)}`] : []
|
|
55
|
+
].join("\n"));
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
async function logs(args, dependencies) {
|
|
59
|
+
const record = await requiredRecord(args[0], dependencies);
|
|
60
|
+
if (!record) return 1;
|
|
61
|
+
const lines = positiveInteger(option(args, "--lines")) ?? 200;
|
|
62
|
+
const result = dependencies.logReader.read(record.logPath, { lines });
|
|
63
|
+
if (!result.exists) {
|
|
64
|
+
dependencies.stderr(`Runner ${record.id} has not written a log yet`);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
if (result.text) dependencies.stdout(result.text);
|
|
68
|
+
if (!args.includes("--follow")) return 0;
|
|
69
|
+
return follow(record, result.fileSize, dependencies);
|
|
70
|
+
}
|
|
71
|
+
async function stop(args, dependencies) {
|
|
72
|
+
const record = await requiredRecord(args[0], dependencies);
|
|
73
|
+
if (!record) return 1;
|
|
74
|
+
if (record.state !== "running") {
|
|
75
|
+
dependencies.stderr(`Runner ${record.id} is already ${record.state}`);
|
|
76
|
+
return 1;
|
|
77
|
+
}
|
|
78
|
+
const reason = option(args, "--reason");
|
|
79
|
+
if (!await stopRecord(record, dependencies, reason)) {
|
|
80
|
+
dependencies.stderr(`Could not stop runner ${record.id}`);
|
|
81
|
+
return 1;
|
|
82
|
+
}
|
|
83
|
+
await dependencies.registry.complete(record.id, {
|
|
84
|
+
reason: STOPPED_REASON,
|
|
85
|
+
code: null,
|
|
86
|
+
signal: TERMINATION_SIGNAL,
|
|
87
|
+
...reason ? { stopReason: reason } : {}
|
|
88
|
+
}, record.sessionId);
|
|
89
|
+
dependencies.stdout(`Stopped ${record.id}\nLog: ${record.logPath}`);
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
async function stopAll(args, dependencies) {
|
|
93
|
+
const sessionId = require_session.requiredSessionId(dependencies.env);
|
|
94
|
+
const records = await dependencies.registry.listBySession(sessionId);
|
|
95
|
+
const reason = option(args, "--reason");
|
|
96
|
+
const stopped = [];
|
|
97
|
+
for (const record of records) {
|
|
98
|
+
if (!await stopRecord(record, dependencies, reason)) continue;
|
|
99
|
+
await dependencies.registry.complete(record.id, {
|
|
100
|
+
reason: STOPPED_REASON,
|
|
101
|
+
code: null,
|
|
102
|
+
signal: TERMINATION_SIGNAL,
|
|
103
|
+
...reason ? { stopReason: reason } : {}
|
|
104
|
+
}, record.sessionId);
|
|
105
|
+
stopped.push(record);
|
|
106
|
+
}
|
|
107
|
+
dependencies.stdout(records.length === 0 ? `No active runners for session ${sessionId}` : `Stopped ${stopped.length} runner(s) for session ${sessionId}: ${stopped.map((record) => record.id).join(", ")}`);
|
|
108
|
+
if (stopped.length !== records.length) {
|
|
109
|
+
dependencies.stderr(`Could not stop ${records.length - stopped.length} runner(s) for session ${sessionId}`);
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
async function input(args, dependencies) {
|
|
115
|
+
const record = await requiredRecord(args[0], dependencies);
|
|
116
|
+
if (!record) return 1;
|
|
117
|
+
if (record.state === "completed") {
|
|
118
|
+
dependencies.stderr(`Runner ${record.id} is already completed; use doom-runner logs ${record.id}`);
|
|
119
|
+
return 1;
|
|
120
|
+
}
|
|
121
|
+
if (!record.interactive) {
|
|
122
|
+
dependencies.stderr(`Runner ${record.id} was not started with interactive: true`);
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
const text = option(args, "--text") ?? await dependencies.readStdin();
|
|
126
|
+
const payload = args.includes("--enter") && !text.endsWith("\n") ? `${text}\n` : text;
|
|
127
|
+
if (!MULTIPLEXER_BACKENDS.has(record.backend) || !record.backendTarget) {
|
|
128
|
+
dependencies.stderr(`Runner ${record.id} uses the ${record.backend} backend, which does not expose CLI input`);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
if (!await dependencies.rmuxBackend.input(record.backendTarget, payload)) {
|
|
132
|
+
dependencies.stderr(`Runner ${record.id} is no longer accepting RMUX input`);
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
dependencies.stdout(`Sent input to ${record.id}`);
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
async function requiredRecord(id, dependencies) {
|
|
139
|
+
if (!id) {
|
|
140
|
+
dependencies.stderr("A runner ID is required");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const sessionId = require_session.requiredSessionId(dependencies.env);
|
|
144
|
+
const scopedRecord = await dependencies.registry.get(id, sessionId);
|
|
145
|
+
if (!scopedRecord) {
|
|
146
|
+
dependencies.stderr(`No runner with id ${id}`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
return scopedRecord;
|
|
150
|
+
}
|
|
151
|
+
function formatRecord(record) {
|
|
152
|
+
const active = require_bashResult.formatRunnerLine(record, Date.now());
|
|
153
|
+
return `${record.id} ${record.state} ${record.backend} ${active}`;
|
|
154
|
+
}
|
|
155
|
+
function formatExit(record) {
|
|
156
|
+
const exit = record.exit;
|
|
157
|
+
if (!exit) return "unknown";
|
|
158
|
+
const detail = exit.code !== null ? ` code ${exit.code}` : exit.signal ? ` ${exit.signal}` : "";
|
|
159
|
+
const attribution = exit.terminationReason ? ` [${exit.terminationReason}]` : "";
|
|
160
|
+
const note = exit.stopReason ? ` (${exit.stopReason})` : "";
|
|
161
|
+
return `${exit.reason}${detail}${attribution}${note}`;
|
|
162
|
+
}
|
|
163
|
+
function option(args, name) {
|
|
164
|
+
const index = args.indexOf(name);
|
|
165
|
+
return index >= 0 ? args[index + 1] : void 0;
|
|
166
|
+
}
|
|
167
|
+
function positiveInteger(value) {
|
|
168
|
+
if (value === void 0) return void 0;
|
|
169
|
+
const parsed = Number(value);
|
|
170
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
171
|
+
}
|
|
172
|
+
async function follow(record, initialSize, dependencies) {
|
|
173
|
+
let offset = initialSize;
|
|
174
|
+
return new Promise((resolve) => {
|
|
175
|
+
let stopped = false;
|
|
176
|
+
let stateTimer;
|
|
177
|
+
const listener = (current) => {
|
|
178
|
+
if (current.size < offset) offset = 0;
|
|
179
|
+
if (current.size === offset) return;
|
|
180
|
+
const length = current.size - offset;
|
|
181
|
+
const buffer = Buffer.alloc(length);
|
|
182
|
+
const descriptor = node_fs.default.openSync(record.logPath, "r");
|
|
183
|
+
try {
|
|
184
|
+
const bytesRead = node_fs.default.readSync(descriptor, buffer, 0, length, offset);
|
|
185
|
+
if (bytesRead > 0) dependencies.stdout(buffer.subarray(0, bytesRead).toString("utf8"));
|
|
186
|
+
} finally {
|
|
187
|
+
node_fs.default.closeSync(descriptor);
|
|
188
|
+
}
|
|
189
|
+
offset = current.size;
|
|
190
|
+
};
|
|
191
|
+
const stopFollowing = () => {
|
|
192
|
+
if (stopped) return;
|
|
193
|
+
stopped = true;
|
|
194
|
+
try {
|
|
195
|
+
listener(node_fs.default.statSync(record.logPath));
|
|
196
|
+
} catch (error) {
|
|
197
|
+
if (error.code !== "ENOENT") dependencies.stderr(`Runner ${record.id} failed to flush its final log output: ${error.message}`);
|
|
198
|
+
}
|
|
199
|
+
node_fs.default.unwatchFile(record.logPath, listener);
|
|
200
|
+
clearInterval(stateTimer);
|
|
201
|
+
process.off("SIGINT", stopFollowing);
|
|
202
|
+
resolve(0);
|
|
203
|
+
};
|
|
204
|
+
process.once("SIGINT", stopFollowing);
|
|
205
|
+
node_fs.default.watchFile(record.logPath, { interval: 250 }, listener);
|
|
206
|
+
stateTimer = setInterval(() => {
|
|
207
|
+
dependencies.registry.get(record.id).then((current) => {
|
|
208
|
+
if (current?.state === "completed") stopFollowing();
|
|
209
|
+
});
|
|
210
|
+
}, 250);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
async function stopRecord(record, dependencies, reason) {
|
|
214
|
+
await dependencies.registry.requestTermination(record.id, {
|
|
215
|
+
reason: STOPPED_REASON,
|
|
216
|
+
terminationReason: "user_stop",
|
|
217
|
+
...reason ? { stopReason: reason } : {}
|
|
218
|
+
}, record.sessionId);
|
|
219
|
+
const stopped = MULTIPLEXER_BACKENDS.has(record.backend) && record.backendTarget ? await dependencies.rmuxBackend.stop(record.backendTarget, record.pid) : await dependencies.launcher.stop(record.pid);
|
|
220
|
+
if (!stopped) await dependencies.registry.requestTermination(record.id, void 0, record.sessionId);
|
|
221
|
+
return stopped;
|
|
222
|
+
}
|
|
223
|
+
function help() {
|
|
224
|
+
return [
|
|
225
|
+
"Usage: doom-runner <command>",
|
|
226
|
+
"",
|
|
227
|
+
"Commands:",
|
|
228
|
+
" list [--all]",
|
|
229
|
+
" status <id>",
|
|
230
|
+
" logs <id> [--lines N] [--follow]",
|
|
231
|
+
" stop <id> [--reason <text>]",
|
|
232
|
+
" stop-all [--reason <text>]",
|
|
233
|
+
" input <id> [--text <text>] [--enter]"
|
|
234
|
+
].join("\n");
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
237
|
+
Object.defineProperty(exports, "runCli", {
|
|
238
|
+
enumerable: true,
|
|
239
|
+
get: function() {
|
|
240
|
+
return runCli;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
//# sourceMappingURL=cliApp-Dj9nwaOv.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliApp-Dj9nwaOv.cjs","names":["inheritedSessionId","requiredSessionId","formatRunnerLine","fs"],"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';\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';\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 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 doom-runner commands');\n return 1;\n }\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(help());\n return 0;\n default:\n dependencies.stderr(`Unknown command: ${command}\\n\\n${help()}`);\n return 1;\n }\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 dependencies.stdout(records.length > 0 ? records.map(formatRecord).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 = 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 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 doom-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 const listener = (current: fs.Stats): 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 = fs.openSync(record.logPath, 'r');\n try {\n const bytesRead = fs.readSync(descriptor, buffer, 0, length, offset);\n if (bytesRead > 0) dependencies.stdout(buffer.subarray(0, bytesRead).toString('utf8'));\n } finally {\n fs.closeSync(descriptor);\n }\n offset = current.size;\n };\n const stopFollowing = (): void => {\n if (stopped) return;\n stopped = true;\n try {\n listener(fs.statSync(record.logPath));\n } catch (error) {\n // A missing log file was already reported by the initial read.\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n dependencies.stderr(`Runner ${record.id} failed to flush its final log output: ${(error as Error).message}`);\n }\n }\n fs.unwatchFile(record.logPath, listener);\n clearInterval(stateTimer);\n process.off('SIGINT', stopFollowing);\n 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 await dependencies.registry.requestTermination(\n record.id,\n {\n reason: STOPPED_REASON,\n terminationReason: 'user_stop',\n ...(reason ? { stopReason: reason } : {}),\n },\n record.sessionId,\n );\n const stopped =\n MULTIPLEXER_BACKENDS.has(record.backend) && record.backendTarget\n ? await dependencies.rmuxBackend.stop(record.backendTarget, record.pid)\n : await dependencies.launcher.stop(record.pid);\n if (!stopped) await dependencies.registry.requestTermination(record.id, undefined, record.sessionId);\n return stopped;\n}\n\nfunction help(): string {\n return [\n 'Usage: doom-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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"}
|