@agimon-ai/doompi-runner 0.0.1-alpha.2 → 0.0.1-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -16
- package/dist/adapters/RunnerPaths.cjs +2 -0
- package/dist/adapters/RunnerPaths.cjs.map +1 -0
- package/dist/adapters/RunnerPaths.d.cts +35 -0
- package/dist/adapters/RunnerPaths.d.cts.map +1 -0
- package/dist/adapters/RunnerPaths.d.mts +35 -0
- package/dist/adapters/RunnerPaths.d.mts.map +1 -0
- package/dist/adapters/RunnerPaths.mjs +2 -0
- package/dist/adapters/RunnerPaths.mjs.map +1 -0
- package/dist/adapters/pi/extension.cjs +3 -0
- package/dist/adapters/pi/extension.cjs.map +1 -0
- package/dist/adapters/pi/extension.d.cts +10 -0
- package/dist/adapters/pi/extension.d.cts.map +1 -0
- package/dist/adapters/pi/extension.d.mts +10 -0
- package/dist/adapters/pi/extension.d.mts.map +1 -0
- package/dist/adapters/pi/extension.mjs +3 -0
- package/dist/adapters/pi/extension.mjs.map +1 -0
- package/dist/adapters/pty/ptyProvider.cjs +2 -0
- package/dist/adapters/pty/ptyProvider.cjs.map +1 -0
- package/dist/adapters/pty/ptyProvider.d.cts +13 -0
- package/dist/adapters/pty/ptyProvider.d.cts.map +1 -0
- package/dist/adapters/pty/ptyProvider.d.mts +13 -0
- package/dist/adapters/pty/ptyProvider.d.mts.map +1 -0
- package/dist/adapters/pty/ptyProvider.mjs +2 -0
- package/dist/adapters/pty/ptyProvider.mjs.map +1 -0
- package/dist/bin/cli.cjs +1 -1
- package/dist/bin/cli.cjs.map +1 -1
- package/dist/bin/cli.mjs +1 -1
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/bin/logSink.cjs.map +1 -1
- package/dist/bin/logSink.mjs.map +1 -1
- package/dist/bin/runnerHost.cjs.map +1 -1
- package/dist/bin/runnerHost.mjs.map +1 -1
- package/dist/cli/cliApp.cjs +1 -6
- package/dist/cli/cliApp.d.cts +2 -23
- package/dist/cli/cliApp.d.mts +2 -23
- package/dist/cli/cliApp.mjs +1 -6
- package/dist/commands/bash/bashTool.cjs +7 -0
- package/dist/commands/bash/bashTool.cjs.map +1 -0
- package/dist/commands/bash/bashTool.d.cts +25 -0
- package/dist/commands/bash/bashTool.d.cts.map +1 -0
- package/dist/commands/bash/bashTool.d.mts +25 -0
- package/dist/commands/bash/bashTool.d.mts.map +1 -0
- package/dist/commands/bash/bashTool.mjs +7 -0
- package/dist/commands/bash/bashTool.mjs.map +1 -0
- package/dist/commands/bash/responseEnvelope.cjs +10 -0
- package/dist/commands/bash/responseEnvelope.cjs.map +1 -0
- package/dist/commands/bash/responseEnvelope.d.cts +45 -0
- package/dist/commands/bash/responseEnvelope.d.cts.map +1 -0
- package/dist/commands/bash/responseEnvelope.d.mts +45 -0
- package/dist/commands/bash/responseEnvelope.d.mts.map +1 -0
- package/dist/commands/bash/responseEnvelope.mjs +10 -0
- package/dist/{tool/responseEnvelope.cjs.map → commands/bash/responseEnvelope.mjs.map} +1 -1
- package/dist/commands/cli/cliApp.cjs +6 -0
- package/dist/commands/cli/cliApp.cjs.map +1 -0
- package/dist/commands/cli/cliApp.d.cts +23 -0
- package/dist/commands/cli/cliApp.d.cts.map +1 -0
- package/dist/commands/cli/cliApp.d.mts +23 -0
- package/dist/commands/cli/cliApp.d.mts.map +1 -0
- package/dist/commands/cli/cliApp.mjs +6 -0
- package/dist/commands/cli/cliApp.mjs.map +1 -0
- package/dist/compaction.cjs +1 -4
- package/dist/compaction.d.cts +2 -14
- package/dist/compaction.d.mts +2 -14
- package/dist/compaction.mjs +1 -4
- package/dist/config.cjs +1 -2
- package/dist/config.d.cts +2 -32
- package/dist/config.d.mts +2 -32
- package/dist/config.mjs +1 -2
- package/dist/container/index.cjs +1 -1
- package/dist/container/index.cjs.map +1 -1
- package/dist/container/index.mjs +1 -1
- package/dist/container/index.mjs.map +1 -1
- package/dist/extension.cjs +1 -3
- package/dist/extension.d.cts +2 -10
- package/dist/extension.d.mts +2 -10
- package/dist/extension.mjs +1 -3
- package/dist/extensions/pi.cjs +1 -1
- package/dist/extensions/pi.cjs.map +1 -1
- package/dist/extensions/pi.d.cts +1 -1
- package/dist/extensions/pi.d.mts +1 -1
- package/dist/extensions/pi.mjs +1 -1
- package/dist/extensions/pi.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/ptyProvider.cjs +1 -2
- package/dist/ptyProvider.d.cts +2 -13
- package/dist/ptyProvider.d.mts +2 -13
- package/dist/ptyProvider.mjs +1 -2
- package/dist/reconcile.cjs +1 -2
- package/dist/reconcile.d.cts +2 -37
- package/dist/reconcile.d.mts +2 -37
- package/dist/reconcile.mjs +1 -2
- package/dist/runnerSpec.cjs +1 -2
- package/dist/runnerSpec.d.cts +2 -50
- package/dist/runnerSpec.d.mts +2 -50
- package/dist/runnerSpec.mjs +1 -2
- package/dist/schemas/bashTool.cjs +2 -0
- package/dist/schemas/bashTool.cjs.map +1 -0
- package/dist/schemas/bashTool.d.cts +25 -0
- package/dist/schemas/bashTool.d.cts.map +1 -0
- package/dist/schemas/bashTool.d.mts +25 -0
- package/dist/schemas/bashTool.d.mts.map +1 -0
- package/dist/schemas/bashTool.mjs +2 -0
- package/dist/schemas/bashTool.mjs.map +1 -0
- package/dist/schemas/runnerSpec.cjs +2 -0
- package/dist/schemas/runnerSpec.cjs.map +1 -0
- package/dist/schemas/runnerSpec.d.cts +50 -0
- package/dist/schemas/runnerSpec.d.cts.map +1 -0
- package/dist/schemas/runnerSpec.d.mts +50 -0
- package/dist/schemas/runnerSpec.d.mts.map +1 -0
- package/dist/schemas/runnerSpec.mjs +2 -0
- package/dist/schemas/runnerSpec.mjs.map +1 -0
- package/dist/services/AnsiScrub/ansiScrub.cjs +1 -1
- package/dist/services/AnsiScrub/ansiScrub.cjs.map +1 -1
- package/dist/services/AnsiScrub/ansiScrub.mjs +1 -1
- package/dist/services/AnsiScrub/ansiScrub.mjs.map +1 -1
- package/dist/services/BashRunService/BashRunService.cjs +1 -1
- package/dist/services/BashRunService/BashRunService.cjs.map +1 -1
- package/dist/services/BashRunService/BashRunService.mjs +1 -1
- package/dist/services/BashRunService/BashRunService.mjs.map +1 -1
- package/dist/services/Launcher/Launcher.cjs +1 -1
- package/dist/services/Launcher/Launcher.cjs.map +1 -1
- package/dist/services/Launcher/Launcher.mjs +1 -1
- package/dist/services/Launcher/Launcher.mjs.map +1 -1
- package/dist/services/Lifeline/NodeLifeline.cjs +1 -1
- package/dist/services/Lifeline/NodeLifeline.cjs.map +1 -1
- package/dist/services/Lifeline/NodeLifeline.mjs +1 -1
- package/dist/services/Lifeline/NodeLifeline.mjs.map +1 -1
- package/dist/services/LogFile/LogFile.cjs +1 -1
- package/dist/services/LogFile/LogFile.cjs.map +1 -1
- package/dist/services/LogFile/LogFile.mjs +1 -1
- package/dist/services/LogFile/LogFile.mjs.map +1 -1
- package/dist/services/PtyHost/PtyHost.cjs +1 -1
- package/dist/services/PtyHost/PtyHost.cjs.map +1 -1
- package/dist/services/PtyHost/PtyHost.mjs +1 -1
- package/dist/services/PtyHost/PtyHost.mjs.map +1 -1
- package/dist/services/RmuxBackend/RmuxBackend.cjs +1 -1
- package/dist/services/RmuxBackend/RmuxBackend.cjs.map +1 -1
- package/dist/services/RmuxBackend/RmuxBackend.mjs +1 -1
- package/dist/services/RmuxBackend/RmuxBackend.mjs.map +1 -1
- package/dist/services/RunnerNamer/RunnerNamer.cjs +1 -1
- package/dist/services/RunnerNamer/RunnerNamer.cjs.map +1 -1
- package/dist/services/RunnerNamer/RunnerNamer.mjs +1 -1
- package/dist/services/RunnerNamer/RunnerNamer.mjs.map +1 -1
- package/dist/services/RunnerPaths/RunnerPaths.cjs +1 -2
- package/dist/services/RunnerPaths/RunnerPaths.d.cts +2 -31
- package/dist/services/RunnerPaths/RunnerPaths.d.mts +2 -31
- package/dist/services/RunnerPaths/RunnerPaths.mjs +1 -2
- package/dist/services/RunnerPaths/index.cjs +1 -1
- package/dist/services/RunnerPaths/index.d.cts +2 -1
- package/dist/services/RunnerPaths/index.d.mts +2 -1
- package/dist/services/RunnerPaths/index.mjs +1 -1
- package/dist/services/RunnerPaths/types.d.cts +2 -0
- package/dist/services/RunnerPaths/types.d.cts.map +1 -1
- package/dist/services/RunnerPaths/types.d.mts +2 -0
- package/dist/services/RunnerPaths/types.d.mts.map +1 -1
- package/dist/services/RunnerRegistry/RunnerRegistry.cjs +1 -1
- package/dist/services/RunnerRegistry/RunnerRegistry.cjs.map +1 -1
- package/dist/services/RunnerRegistry/RunnerRegistry.mjs +1 -1
- package/dist/services/RunnerRegistry/RunnerRegistry.mjs.map +1 -1
- package/dist/services/runs/compaction.cjs +4 -0
- package/dist/services/runs/compaction.cjs.map +1 -0
- package/dist/services/runs/compaction.d.cts +14 -0
- package/dist/services/runs/compaction.d.cts.map +1 -0
- package/dist/services/runs/compaction.d.mts +14 -0
- package/dist/services/runs/compaction.d.mts.map +1 -0
- package/dist/services/runs/compaction.mjs +4 -0
- package/dist/services/runs/compaction.mjs.map +1 -0
- package/dist/services/runs/reconcile.cjs +2 -0
- package/dist/services/runs/reconcile.cjs.map +1 -0
- package/dist/services/runs/reconcile.d.cts +37 -0
- package/dist/services/runs/reconcile.d.cts.map +1 -0
- package/dist/services/runs/reconcile.d.mts +37 -0
- package/dist/services/runs/reconcile.d.mts.map +1 -0
- package/dist/services/runs/reconcile.mjs +2 -0
- package/dist/services/runs/reconcile.mjs.map +1 -0
- package/dist/services/runs/session.cjs +2 -0
- package/dist/services/runs/session.cjs.map +1 -0
- package/dist/services/runs/session.d.cts +10 -0
- package/dist/services/runs/session.d.cts.map +1 -0
- package/dist/services/runs/session.d.mts +10 -0
- package/dist/services/runs/session.d.mts.map +1 -0
- package/dist/services/runs/session.mjs +2 -0
- package/dist/services/runs/session.mjs.map +1 -0
- package/dist/session.cjs +1 -2
- package/dist/session.d.cts +2 -10
- package/dist/session.d.mts +2 -10
- package/dist/session.mjs +1 -2
- package/dist/tool/bashTool.cjs +1 -7
- package/dist/tool/bashTool.d.cts +2 -25
- package/dist/tool/bashTool.d.mts +2 -25
- package/dist/tool/bashTool.mjs +1 -7
- package/dist/tool/responseEnvelope.cjs +1 -10
- package/dist/tool/responseEnvelope.d.cts +2 -45
- package/dist/tool/responseEnvelope.d.mts +2 -45
- package/dist/tool/responseEnvelope.mjs +1 -10
- package/dist/tool/schema.cjs +1 -2
- package/dist/tool/schema.d.cts +2 -25
- package/dist/tool/schema.d.mts +2 -25
- package/dist/tool/schema.mjs +1 -2
- package/dist/tui/bashRender.cjs +4 -7
- package/dist/tui/bashRender.cjs.map +1 -1
- package/dist/tui/bashRender.d.cts +5 -5
- package/dist/tui/bashRender.d.cts.map +1 -1
- package/dist/tui/bashRender.d.mts +5 -5
- package/dist/tui/bashRender.d.mts.map +1 -1
- package/dist/tui/bashRender.mjs +4 -7
- package/dist/tui/bashRender.mjs.map +1 -1
- package/dist/tui/format.cjs +1 -1
- package/dist/tui/format.cjs.map +1 -1
- package/dist/tui/format.mjs +1 -1
- package/dist/tui/format.mjs.map +1 -1
- package/dist/tui/runnerSpace.cjs +4 -4
- package/dist/tui/runnerSpace.cjs.map +1 -1
- package/dist/tui/runnerSpace.d.cts.map +1 -1
- package/dist/tui/runnerSpace.d.mts.map +1 -1
- package/dist/tui/runnerSpace.mjs +4 -4
- package/dist/tui/runnerSpace.mjs.map +1 -1
- package/dist/types/config.cjs +2 -0
- package/dist/types/config.cjs.map +1 -0
- package/dist/types/config.d.cts +32 -0
- package/dist/types/config.d.cts.map +1 -0
- package/dist/types/config.d.mts +32 -0
- package/dist/types/config.d.mts.map +1 -0
- package/dist/types/config.mjs +2 -0
- package/dist/types/config.mjs.map +1 -0
- package/dist/types/index.cjs +2 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +28 -0
- package/dist/types/index.d.cts.map +1 -0
- package/dist/types/index.d.mts +28 -0
- package/dist/types/index.d.mts.map +1 -0
- package/dist/types/index.mjs +2 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/types.cjs +1 -2
- package/dist/types.d.cts +2 -28
- package/dist/types.d.mts +2 -28
- package/dist/types.mjs +1 -2
- package/package.json +20 -13
- package/dist/cli/cliApp.cjs.map +0 -1
- package/dist/cli/cliApp.d.cts.map +0 -1
- package/dist/cli/cliApp.d.mts.map +0 -1
- package/dist/cli/cliApp.mjs.map +0 -1
- package/dist/compaction.cjs.map +0 -1
- package/dist/compaction.d.cts.map +0 -1
- package/dist/compaction.d.mts.map +0 -1
- package/dist/compaction.mjs.map +0 -1
- package/dist/config.cjs.map +0 -1
- package/dist/config.d.cts.map +0 -1
- package/dist/config.d.mts.map +0 -1
- package/dist/config.mjs.map +0 -1
- package/dist/extension.cjs.map +0 -1
- package/dist/extension.d.cts.map +0 -1
- package/dist/extension.d.mts.map +0 -1
- package/dist/extension.mjs.map +0 -1
- package/dist/ptyProvider.cjs.map +0 -1
- package/dist/ptyProvider.d.cts.map +0 -1
- package/dist/ptyProvider.d.mts.map +0 -1
- package/dist/ptyProvider.mjs.map +0 -1
- package/dist/reconcile.cjs.map +0 -1
- package/dist/reconcile.d.cts.map +0 -1
- package/dist/reconcile.d.mts.map +0 -1
- package/dist/reconcile.mjs.map +0 -1
- package/dist/runnerSpec.cjs.map +0 -1
- package/dist/runnerSpec.d.cts.map +0 -1
- package/dist/runnerSpec.d.mts.map +0 -1
- package/dist/runnerSpec.mjs.map +0 -1
- package/dist/services/RunnerPaths/RunnerPaths.cjs.map +0 -1
- package/dist/services/RunnerPaths/RunnerPaths.d.cts.map +0 -1
- package/dist/services/RunnerPaths/RunnerPaths.d.mts.map +0 -1
- package/dist/services/RunnerPaths/RunnerPaths.mjs.map +0 -1
- package/dist/session.cjs.map +0 -1
- package/dist/session.d.cts.map +0 -1
- package/dist/session.d.mts.map +0 -1
- package/dist/session.mjs.map +0 -1
- package/dist/tool/bashTool.cjs.map +0 -1
- package/dist/tool/bashTool.d.cts.map +0 -1
- package/dist/tool/bashTool.d.mts.map +0 -1
- package/dist/tool/bashTool.mjs.map +0 -1
- package/dist/tool/responseEnvelope.d.cts.map +0 -1
- package/dist/tool/responseEnvelope.d.mts.map +0 -1
- package/dist/tool/responseEnvelope.mjs.map +0 -1
- package/dist/tool/schema.cjs.map +0 -1
- package/dist/tool/schema.d.cts.map +0 -1
- package/dist/tool/schema.d.mts.map +0 -1
- package/dist/tool/schema.mjs.map +0 -1
- package/dist/types.cjs.map +0 -1
- package/dist/types.d.cts.map +0 -1
- package/dist/types.d.mts.map +0 -1
- package/dist/types.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# @agimon-ai/doompi-runner
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**A shell command should not hold the session hostage.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Doompi Runner replaces Pi's built-in bash tool with a supervised one. Short commands behave
|
|
6
|
+
normally. Long commands move into the background, keep writing to a saved log, and remain
|
|
7
|
+
controllable through the process tool and TUI instead of blocking the agent until a timeout.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
Detached runs reconcile after compaction and other context rewrites. The transcript can
|
|
10
|
+
change; the process does not lose its identity.
|
|
11
|
+
|
|
12
|
+
This is part of [Doompi](https://www.npmjs.com/package/@agimon-ai/doompi). Most users load
|
|
13
|
+
it through a major-mode layer.
|
|
12
14
|
|
|
13
15
|
## Install
|
|
14
16
|
|
|
@@ -18,29 +20,36 @@ npm install @agimon-ai/doompi-runner
|
|
|
18
20
|
|
|
19
21
|
## How it loads
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
Put the bare package name in a layer, then add that layer to a major mode:
|
|
22
24
|
|
|
23
25
|
```yaml
|
|
24
26
|
layers:
|
|
25
27
|
runner:
|
|
26
|
-
packages: ['@agimon-ai/doompi-runner
|
|
28
|
+
packages: ['@agimon-ai/doompi-runner']
|
|
27
29
|
|
|
28
30
|
majorMode:
|
|
29
|
-
dev:
|
|
31
|
+
dev:
|
|
32
|
+
description: Development mode with supervised long-running commands.
|
|
33
|
+
layers: [runner]
|
|
30
34
|
```
|
|
31
35
|
|
|
36
|
+
The package's Pi manifest selects the Pi adapter. It replaces bash only in modes that name
|
|
37
|
+
the layer.
|
|
38
|
+
|
|
32
39
|
## Leader
|
|
33
40
|
|
|
34
|
-
`SPC r r` opens background processes
|
|
41
|
+
`SPC r r` opens Runner Space, where background processes can be inspected and controlled.
|
|
35
42
|
|
|
36
|
-
##
|
|
43
|
+
## Runtime
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
optional dependencies.
|
|
45
|
+
The package ships the `doom-runner` CLI and chooses a prebuilt `rmux` binary from its
|
|
46
|
+
platform-specific optional dependencies. npm installs the matching binary package; users
|
|
47
|
+
do not pick one by hand.
|
|
40
48
|
|
|
41
|
-
##
|
|
49
|
+
## Public API
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
The root export and declared subpaths expose the runner protocol, process supervision,
|
|
52
|
+
state, tools, and TUI. Use supported exports rather than reaching into `dist/`.
|
|
44
53
|
|
|
45
54
|
## License
|
|
46
55
|
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../types/config.cjs"),n=require("../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs"),r=require("../services/runs/session.cjs");let i=require("inversify");require("reflect-metadata/lite");let a=require("node:fs");a=e.__toESM(a,1);let o=require("node:path");o=e.__toESM(o,1);let s=require("node:os");s=e.__toESM(s,1);let c=require("node:child_process");const l=`doom-runner`,u=`runs`,d=`.json`,f=`utf8`,p=`ENOENT`;function m(e,t){try{let n=(0,c.execFileSync)(`git`,e,{cwd:t,encoding:f,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return n.length>0?n:void 0}catch{return}}function h(e){try{return a.default.realpathSync(e)}catch{return e}}function g(e){let t=e.PI_CODING_AGENT_DIR?.trim();return t===`~`?s.default.homedir():t?.startsWith(`~/`)?o.default.join(s.default.homedir(),t.slice(2)):t?o.default.resolve(t):o.default.join(s.default.homedir(),`.pi`,`agent`)}function _(e){let t=a.default.statSync(e).mtimeMs;for(let n of a.default.readdirSync(e,{withFileTypes:!0})){let r=o.default.join(e,n.name);if(t=Math.max(t,a.default.statSync(r).mtimeMs),n.isDirectory())for(let e of a.default.readdirSync(r))t=Math.max(t,a.default.statSync(o.default.join(r,e)).mtimeMs)}return t}async function v(e){let t=(await a.default.promises.stat(e)).mtimeMs;for(let n of await a.default.promises.readdir(e,{withFileTypes:!0})){let r=o.default.join(e,n.name);if(t=Math.max(t,(await a.default.promises.stat(r)).mtimeMs),n.isDirectory())for(let e of await a.default.promises.readdir(r))t=Math.max(t,(await a.default.promises.stat(o.default.join(r,e))).mtimeMs)}return t}function y(e){let t;try{t=a.default.readdirSync(e)}catch{return!1}for(let n of t)if(n.endsWith(d))try{let t=JSON.parse(a.default.readFileSync(o.default.join(e,n),f)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}async function b(e){let t;try{t=await a.default.promises.readdir(e)}catch{return!1}for(let n of t)if(n.endsWith(d))try{let t=JSON.parse(await a.default.promises.readFile(o.default.join(e,n),f)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}function x(e){if(typeof e!=`object`||!e)return;let t=e;if(t.state!==`completed`||typeof t.exit?.finishedAt!=`string`)return;let n=Date.parse(t.exit.finishedAt);return Number.isFinite(n)?n:void 0}let S=class{adoptedSessionId;repository;repositoryPath(){return this.repository??=h(m([`rev-parse`,`--show-toplevel`],process.cwd())??process.cwd()),this.repository}setSessionId(e){let t=e.trim();if(!t)throw Error(`doom-runner session id cannot be blank`);this.adoptedSessionId=t}logDirectory(e){let n=process.env[t.LOG_DIR_ENV]?.trim();return n?o.default.resolve(n):o.default.join(this.sessionDirectory(e),`logs`)}stateDirectory(e){let n=process.env[t.LOG_DIR_ENV]?.trim();return n?o.default.join(o.default.resolve(n),`..`,u):o.default.join(this.sessionDirectory(e),u)}logPathFor(e,t){return o.default.join(this.logDirectory(t),`${e}.log`)}rotatedLogPathFor(e,t){return`${this.logPathFor(e,t)}.1`}statePathFor(e,t){return o.default.join(this.stateDirectory(t),`${e}${d}`)}ensureDirectories(e){a.default.mkdirSync(this.logDirectory(e),{recursive:!0}),a.default.mkdirSync(this.stateDirectory(e),{recursive:!0})}sweepHistory(e,t=Date.now()){let n={removed:[],errors:[]};if(process.env.DOOM_RUNNER_LOG_DIR?.trim())return this.sweepStateDirectory(this.stateDirectory(),this.sessionId(),e,t,n),n;let r=this.storeDirectory(),i;try{i=a.default.readdirSync(r)}catch(e){return e.code===p||n.errors.push(`Could not read runner history directory ${r}: ${String(e)}`),n}let s=this.currentSessionId();for(let a of i)a!==s&&this.sweepSessionDirectory(o.default.join(r,a),e,t,n);return n}async sweepHistoryAsync(e,t=Date.now()){let n={removed:[],errors:[]};if(process.env.DOOM_RUNNER_LOG_DIR?.trim())return await this.sweepStateDirectoryAsync(this.stateDirectory(),this.sessionId(),e,t,n),n;let r=this.storeDirectory(),i;try{i=await a.default.promises.readdir(r)}catch(e){return e.code===p||n.errors.push(`Could not read runner history directory ${r}: ${String(e)}`),n}let s=this.currentSessionId();for(let a of i)a!==s&&await this.sweepSessionDirectoryAsync(o.default.join(r,a),e,t,n);return n}legacyDirectory(){let e=m([`rev-parse`,`--path-format=absolute`,`--git-common-dir`],process.cwd());return e?o.default.join(e,l):void 0}removeLegacyStore(){let e=this.legacyDirectory();if(!(!e||o.default.basename(e)!==l||!a.default.existsSync(e)))return a.default.rmSync(e,{recursive:!0,force:!0}),e}currentSessionId(){try{return this.sessionId()}catch{return}}sweepSessionDirectory(e,t,n,r){try{if(n-_(e)<=t||y(o.default.join(e,u)))return;a.default.rmSync(e,{recursive:!0,force:!0}),r.removed.push(e)}catch(t){t.code!==p&&r.errors.push(`Could not sweep runner session ${e}: ${String(t)}`)}}async sweepSessionDirectoryAsync(e,t,n,r){try{if(n-await v(e)<=t||await b(o.default.join(e,u)))return;await a.default.promises.rm(e,{recursive:!0,force:!0}),r.removed.push(e)}catch(t){t.code!==p&&r.errors.push(`Could not sweep runner session ${e}: ${String(t)}`)}}sessionId(e){let t=e?.trim()||this.adoptedSessionId||r.inheritedSessionId();if(!t)throw Error(`PI_SESSION_ID is required for doom-runner storage`);return t}storeDirectory(){let e=process.env[t.LOG_DIR_ENV]?.trim();return e?o.default.dirname(o.default.resolve(e)):o.default.join(g(process.env),l)}sessionDirectory(e){return o.default.join(this.storeDirectory(),this.sessionId(e))}sweepStateDirectory(e,t,n,r,i){let s;try{s=a.default.readdirSync(e)}catch(t){t.code!==p&&i.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}for(let c of s){if(!c.endsWith(d))continue;let s=o.default.join(e,c);try{let e=x(JSON.parse(a.default.readFileSync(s,f)));if(e===void 0||r-e<=n)continue;let o=c.slice(0,-5);this.removeHistoryFiles(t,o,s,i)}catch(e){i.errors.push(`Could not sweep runner metadata ${s}: ${String(e)}`)}}}async sweepStateDirectoryAsync(e,t,n,r,i){let s;try{s=await a.default.promises.readdir(e)}catch(t){t.code!==p&&i.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}for(let c of s){if(!c.endsWith(d))continue;let s=o.default.join(e,c);try{let e=x(JSON.parse(await a.default.promises.readFile(s,f)));if(e===void 0||r-e<=n)continue;let o=c.slice(0,-5);await this.removeHistoryFilesAsync(t,o,s,i)}catch(e){i.errors.push(`Could not sweep runner metadata ${s}: ${String(e)}`)}}}removeHistoryFiles(e,t,n,r){let i=[n,this.logPathFor(t,e),this.rotatedLogPathFor(t,e)];for(let e of i)try{a.default.unlinkSync(e),r.removed.push(e)}catch(t){t.code!==p&&r.errors.push(`Could not remove runner history ${e}: ${String(t)}`)}}async removeHistoryFilesAsync(e,t,n,r){let i=[n,this.logPathFor(t,e),this.rotatedLogPathFor(t,e)];for(let e of i)try{await a.default.promises.unlink(e),r.removed.push(e)}catch(t){t.code!==p&&r.errors.push(`Could not remove runner history ${e}: ${String(t)}`)}}};S=n.default([(0,i.injectable)()],S),Object.defineProperty(exports,"RunnerPaths",{enumerable:!0,get:function(){return S}});
|
|
2
|
+
//# sourceMappingURL=RunnerPaths.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RunnerPaths.cjs","names":["execFileSync","fs","os","path","LOG_DIR_ENV","inheritedSessionId","injectable"],"sources":["../../src/adapters/RunnerPaths.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { injectable } from 'inversify';\nimport 'reflect-metadata/lite';\nimport type { IRunnerPaths, LogSweepResult } from '../services/RunnerPaths/types.ts';\nimport { inheritedSessionId } from '../services/runs/session.ts';\nimport { LOG_DIR_ENV } from '../types/config.ts';\n\nconst STORE_DIR_NAME = 'doom-runner';\nconst DEFAULT_CONFIG_DIR_NAME = '.pi';\nconst AGENT_DIR_NAME = 'agent';\nconst LOG_DIR_NAME = 'logs';\nconst STATE_DIR_NAME = 'runs';\nconst LOG_EXTENSION = '.log';\nconst STATE_EXTENSION = '.json';\nconst ROTATED_SUFFIX = '.1';\nconst PI_CODING_AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\nconst HOME_ALIAS = '~';\nconst HOME_ALIAS_PREFIX = '~/';\nconst FILE_ENCODING = 'utf8';\nconst NOT_FOUND_ERROR_CODE = 'ENOENT';\n\nfunction git(args: string[], cwd: string): string | undefined {\n try {\n const output = execFileSync('git', args, { cwd, encoding: FILE_ENCODING, stdio: ['ignore', 'pipe', 'ignore'] });\n const trimmed = output.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction realPath(target: string): string {\n try {\n return fs.realpathSync(target);\n } catch {\n return target;\n }\n}\n\nfunction resolveAgentDirectory(env: NodeJS.ProcessEnv): string {\n const configured = env[PI_CODING_AGENT_DIR_ENV]?.trim();\n if (configured === HOME_ALIAS) return os.homedir();\n if (configured?.startsWith(HOME_ALIAS_PREFIX)) {\n return path.join(os.homedir(), configured.slice(HOME_ALIAS_PREFIX.length));\n }\n return configured ? path.resolve(configured) : path.join(os.homedir(), DEFAULT_CONFIG_DIR_NAME, AGENT_DIR_NAME);\n}\n\n/**\n * Newest mtime anywhere one level inside the session, falling back to the\n * directory itself. A session still being written to therefore always looks\n * recent, whatever state its individual records are in.\n */\nfunction newestEntryTime(directory: string): number {\n let newest = fs.statSync(directory).mtimeMs;\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n const target = path.join(directory, entry.name);\n newest = Math.max(newest, fs.statSync(target).mtimeMs);\n if (!entry.isDirectory()) continue;\n for (const child of fs.readdirSync(target)) {\n newest = Math.max(newest, fs.statSync(path.join(target, child)).mtimeMs);\n }\n }\n return newest;\n}\n\nasync function newestEntryTimeAsync(directory: string): Promise<number> {\n let newest = (await fs.promises.stat(directory)).mtimeMs;\n for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {\n const target = path.join(directory, entry.name);\n newest = Math.max(newest, (await fs.promises.stat(target)).mtimeMs);\n if (!entry.isDirectory()) continue;\n for (const child of await fs.promises.readdir(target)) {\n newest = Math.max(newest, (await fs.promises.stat(path.join(target, child))).mtimeMs);\n }\n }\n return newest;\n}\n\n/**\n * Whether any run in this session names a pi process that is still running.\n *\n * One live record is enough: records are only ever written by their owner, so a\n * live host pid means the session is still in use however old its files look.\n */\nfunction ownerAlive(stateDirectory: string): boolean {\n let entries: string[];\n try {\n entries = fs.readdirSync(stateDirectory);\n } catch {\n // No records left to consult, so nothing claims the directory.\n return false;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n try {\n const value: unknown = JSON.parse(fs.readFileSync(path.join(stateDirectory, entry), FILE_ENCODING));\n const hostPid = (value as { hostPid?: unknown }).hostPid;\n if (typeof hostPid !== 'number') continue;\n process.kill(hostPid, 0);\n return true;\n } catch {\n // An unreadable record and a dead pid mean the same thing here: no claim.\n }\n }\n return false;\n}\n\nasync function ownerAliveAsync(stateDirectory: string): Promise<boolean> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(stateDirectory);\n } catch {\n return false;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(path.join(stateDirectory, entry), FILE_ENCODING));\n const hostPid = (value as { hostPid?: unknown }).hostPid;\n if (typeof hostPid !== 'number') continue;\n process.kill(hostPid, 0);\n return true;\n } catch {\n // An unreadable record and a dead pid mean the same thing here: no claim.\n }\n }\n return false;\n}\n\nfunction completedAt(value: unknown): number | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = value as { state?: unknown; exit?: { finishedAt?: unknown } };\n if (record.state !== 'completed' || typeof record.exit?.finishedAt !== 'string') return undefined;\n const timestamp = Date.parse(record.exit.finishedAt);\n return Number.isFinite(timestamp) ? timestamp : undefined;\n}\n\n/** Session-scoped runner storage under the Pi agent directory. */\n@injectable()\nexport class RunnerPaths implements IRunnerPaths {\n private adoptedSessionId: string | undefined;\n private repository: string | undefined;\n\n repositoryPath(): string {\n this.repository ??= realPath(git(['rev-parse', '--show-toplevel'], process.cwd()) ?? process.cwd());\n return this.repository;\n }\n\n setSessionId(sessionId: string): void {\n const trimmed = sessionId.trim();\n if (!trimmed) throw new Error('doom-runner session id cannot be blank');\n this.adoptedSessionId = trimmed;\n }\n\n logDirectory(sessionId?: string): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.resolve(override);\n return path.join(this.sessionDirectory(sessionId), LOG_DIR_NAME);\n }\n\n stateDirectory(sessionId?: string): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.join(path.resolve(override), '..', STATE_DIR_NAME);\n return path.join(this.sessionDirectory(sessionId), STATE_DIR_NAME);\n }\n\n logPathFor(id: string, sessionId?: string): string {\n return path.join(this.logDirectory(sessionId), `${id}${LOG_EXTENSION}`);\n }\n\n rotatedLogPathFor(id: string, sessionId?: string): string {\n return `${this.logPathFor(id, sessionId)}${ROTATED_SUFFIX}`;\n }\n\n statePathFor(id: string, sessionId?: string): string {\n return path.join(this.stateDirectory(sessionId), `${id}${STATE_EXTENSION}`);\n }\n\n ensureDirectories(sessionId?: string): void {\n fs.mkdirSync(this.logDirectory(sessionId), { recursive: true });\n fs.mkdirSync(this.stateDirectory(sessionId), { recursive: true });\n }\n\n sweepHistory(ttlMs: number, now: number = Date.now()): LogSweepResult {\n const result: LogSweepResult = { removed: [], errors: [] };\n // A pinned log directory belongs to whoever pinned it, so that branch only\n // ever removes the files this package put there.\n if (process.env[LOG_DIR_ENV]?.trim()) {\n this.sweepStateDirectory(this.stateDirectory(), this.sessionId(), ttlMs, now, result);\n return result;\n }\n const root = this.storeDirectory();\n let sessions: string[];\n try {\n sessions = fs.readdirSync(root);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return result;\n result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);\n return result;\n }\n\n // Sweeping whole session directories rather than the three files a completed\n // record names is what keeps sockets, sidecars, runs that never reached a\n // terminal state, and the emptied directories themselves from accumulating.\n const active = this.currentSessionId();\n for (const sessionId of sessions) {\n if (sessionId === active) continue;\n this.sweepSessionDirectory(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n async sweepHistoryAsync(ttlMs: number, now: number = Date.now()): Promise<LogSweepResult> {\n const result: LogSweepResult = { removed: [], errors: [] };\n if (process.env[LOG_DIR_ENV]?.trim()) {\n await this.sweepStateDirectoryAsync(this.stateDirectory(), this.sessionId(), ttlMs, now, result);\n return result;\n }\n\n const root = this.storeDirectory();\n let sessions: string[];\n try {\n sessions = await fs.promises.readdir(root);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return result;\n result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);\n return result;\n }\n\n const active = this.currentSessionId();\n for (const sessionId of sessions) {\n if (sessionId === active) continue;\n await this.sweepSessionDirectoryAsync(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n legacyDirectory(): string | undefined {\n const commonDirectory = git(['rev-parse', '--path-format=absolute', '--git-common-dir'], process.cwd());\n return commonDirectory ? path.join(commonDirectory, STORE_DIR_NAME) : undefined;\n }\n\n removeLegacyStore(): string | undefined {\n const target = this.legacyDirectory();\n if (!target || path.basename(target) !== STORE_DIR_NAME || !fs.existsSync(target)) return undefined;\n fs.rmSync(target, { recursive: true, force: true });\n return target;\n }\n\n /** The live session, if one is known, so a sweep never deletes underneath itself. */\n private currentSessionId(): string | undefined {\n try {\n return this.sessionId();\n } catch {\n // A CLI invocation outside a session still sweeps; it just has nothing to spare.\n return undefined;\n }\n }\n\n /** Removes a session's whole directory once its newest entry is past the TTL. */\n private sweepSessionDirectory(directory: string, ttlMs: number, now: number, result: LogSweepResult): void {\n try {\n if (now - newestEntryTime(directory) <= ttlMs) return;\n // An idle session can go quiet for longer than the TTL, and deleting one\n // takes its lifeline socket with it, which would read to its own runners\n // as an owner that had died. Its records name the owner, so ask.\n if (ownerAlive(path.join(directory, STATE_DIR_NAME))) return;\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private async sweepSessionDirectoryAsync(\n directory: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n try {\n if (now - (await newestEntryTimeAsync(directory)) <= ttlMs) return;\n if (await ownerAliveAsync(path.join(directory, STATE_DIR_NAME))) return;\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private sessionId(explicit?: string): string {\n const sessionId = explicit?.trim() || this.adoptedSessionId || inheritedSessionId();\n if (!sessionId) throw new Error('PI_SESSION_ID is required for doom-runner storage');\n return sessionId;\n }\n\n private storeDirectory(): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.dirname(path.resolve(override));\n return path.join(resolveAgentDirectory(process.env), STORE_DIR_NAME);\n }\n\n private sessionDirectory(sessionId?: string): string {\n return path.join(this.storeDirectory(), this.sessionId(sessionId));\n }\n\n private sweepStateDirectory(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): void {\n let entries: string[];\n try {\n entries = fs.readdirSync(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(fs.readFileSync(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined || now - finishedAt <= ttlMs) continue;\n const id = entry.slice(0, -STATE_EXTENSION.length);\n this.removeHistoryFiles(sessionId, id, statePath, result);\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n }\n\n private async sweepStateDirectoryAsync(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined || now - finishedAt <= ttlMs) continue;\n const id = entry.slice(0, -STATE_EXTENSION.length);\n await this.removeHistoryFilesAsync(sessionId, id, statePath, result);\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n }\n\n private removeHistoryFiles(sessionId: string, id: string, statePath: string, result: LogSweepResult): void {\n const candidates = [statePath, this.logPathFor(id, sessionId), this.rotatedLogPathFor(id, sessionId)];\n for (const candidate of candidates) {\n try {\n fs.unlinkSync(candidate);\n result.removed.push(candidate);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);\n }\n }\n }\n }\n\n private async removeHistoryFilesAsync(\n sessionId: string,\n id: string,\n statePath: string,\n result: LogSweepResult,\n ): Promise<void> {\n const candidates = [statePath, this.logPathFor(id, sessionId), this.rotatedLogPathFor(id, sessionId)];\n for (const candidate of candidates) {\n try {\n await fs.promises.unlink(candidate);\n result.removed.push(candidate);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);\n }\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { IRunnerPaths, LogSweepResult } from "../services/RunnerPaths/types.cjs";
|
|
2
|
+
import "reflect-metadata/lite";
|
|
3
|
+
//#region src/adapters/RunnerPaths.d.ts
|
|
4
|
+
/** Session-scoped runner storage under the Pi agent directory. */
|
|
5
|
+
declare class RunnerPaths implements IRunnerPaths {
|
|
6
|
+
private adoptedSessionId;
|
|
7
|
+
private repository;
|
|
8
|
+
repositoryPath(): string;
|
|
9
|
+
setSessionId(sessionId: string): void;
|
|
10
|
+
logDirectory(sessionId?: string): string;
|
|
11
|
+
stateDirectory(sessionId?: string): string;
|
|
12
|
+
logPathFor(id: string, sessionId?: string): string;
|
|
13
|
+
rotatedLogPathFor(id: string, sessionId?: string): string;
|
|
14
|
+
statePathFor(id: string, sessionId?: string): string;
|
|
15
|
+
ensureDirectories(sessionId?: string): void;
|
|
16
|
+
sweepHistory(ttlMs: number, now?: number): LogSweepResult;
|
|
17
|
+
sweepHistoryAsync(ttlMs: number, now?: number): Promise<LogSweepResult>;
|
|
18
|
+
legacyDirectory(): string | undefined;
|
|
19
|
+
removeLegacyStore(): string | undefined;
|
|
20
|
+
/** The live session, if one is known, so a sweep never deletes underneath itself. */
|
|
21
|
+
private currentSessionId;
|
|
22
|
+
/** Removes a session's whole directory once its newest entry is past the TTL. */
|
|
23
|
+
private sweepSessionDirectory;
|
|
24
|
+
private sweepSessionDirectoryAsync;
|
|
25
|
+
private sessionId;
|
|
26
|
+
private storeDirectory;
|
|
27
|
+
private sessionDirectory;
|
|
28
|
+
private sweepStateDirectory;
|
|
29
|
+
private sweepStateDirectoryAsync;
|
|
30
|
+
private removeHistoryFiles;
|
|
31
|
+
private removeHistoryFilesAsync;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { RunnerPaths };
|
|
35
|
+
//# sourceMappingURL=RunnerPaths.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RunnerPaths.d.cts","names":[],"sources":["../../src/adapters/RunnerPaths.ts"],"mappings":";;;;cAiJa,uBAAuB;UAC1B;UACA;EAER;EAKA,aAAa;EAMb,aAAa;EAMb,eAAe;EAMf,WAAW,YAAY;EAIvB,kBAAkB,YAAY;EAI9B,aAAa,YAAY;EAIzB,kBAAkB;EAKlB,aAAa,eAAe,eAA2B;EA6BjD,kBAAkB,eAAe,eAA2B,QAAQ;EAyB1E;EAKA;;UAQQ;;UAUA;UAgBM;UAkBN;UAMA;UAMA;UAIA;UAgCM;UAgCN;UAcM"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { IRunnerPaths, LogSweepResult } from "../services/RunnerPaths/types.mjs";
|
|
2
|
+
import "reflect-metadata/lite";
|
|
3
|
+
//#region src/adapters/RunnerPaths.d.ts
|
|
4
|
+
/** Session-scoped runner storage under the Pi agent directory. */
|
|
5
|
+
declare class RunnerPaths implements IRunnerPaths {
|
|
6
|
+
private adoptedSessionId;
|
|
7
|
+
private repository;
|
|
8
|
+
repositoryPath(): string;
|
|
9
|
+
setSessionId(sessionId: string): void;
|
|
10
|
+
logDirectory(sessionId?: string): string;
|
|
11
|
+
stateDirectory(sessionId?: string): string;
|
|
12
|
+
logPathFor(id: string, sessionId?: string): string;
|
|
13
|
+
rotatedLogPathFor(id: string, sessionId?: string): string;
|
|
14
|
+
statePathFor(id: string, sessionId?: string): string;
|
|
15
|
+
ensureDirectories(sessionId?: string): void;
|
|
16
|
+
sweepHistory(ttlMs: number, now?: number): LogSweepResult;
|
|
17
|
+
sweepHistoryAsync(ttlMs: number, now?: number): Promise<LogSweepResult>;
|
|
18
|
+
legacyDirectory(): string | undefined;
|
|
19
|
+
removeLegacyStore(): string | undefined;
|
|
20
|
+
/** The live session, if one is known, so a sweep never deletes underneath itself. */
|
|
21
|
+
private currentSessionId;
|
|
22
|
+
/** Removes a session's whole directory once its newest entry is past the TTL. */
|
|
23
|
+
private sweepSessionDirectory;
|
|
24
|
+
private sweepSessionDirectoryAsync;
|
|
25
|
+
private sessionId;
|
|
26
|
+
private storeDirectory;
|
|
27
|
+
private sessionDirectory;
|
|
28
|
+
private sweepStateDirectory;
|
|
29
|
+
private sweepStateDirectoryAsync;
|
|
30
|
+
private removeHistoryFiles;
|
|
31
|
+
private removeHistoryFilesAsync;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { RunnerPaths };
|
|
35
|
+
//# sourceMappingURL=RunnerPaths.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RunnerPaths.d.mts","names":[],"sources":["../../src/adapters/RunnerPaths.ts"],"mappings":";;;;cAiJa,uBAAuB;UAC1B;UACA;EAER;EAKA,aAAa;EAMb,aAAa;EAMb,eAAe;EAMf,WAAW,YAAY;EAIvB,kBAAkB,YAAY;EAI9B,aAAa,YAAY;EAIzB,kBAAkB;EAKlB,aAAa,eAAe,eAA2B;EA6BjD,kBAAkB,eAAe,eAA2B,QAAQ;EAyB1E;EAKA;;UAQQ;;UAUA;UAgBM;UAkBN;UAMA;UAMA;UAIA;UAgCM;UAgCN;UAcM"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{LOG_DIR_ENV as e}from"../types/config.mjs";import t from"../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs";import{inheritedSessionId as n}from"../services/runs/session.mjs";import{injectable as r}from"inversify";import"reflect-metadata/lite";import i from"node:fs";import a from"node:path";import o from"node:os";import{execFileSync as s}from"node:child_process";const c=`doom-runner`,l=`runs`,u=`.json`,d=`utf8`,f=`ENOENT`;function p(e,t){try{let n=s(`git`,e,{cwd:t,encoding:d,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return n.length>0?n:void 0}catch{return}}function m(e){try{return i.realpathSync(e)}catch{return e}}function h(e){let t=e.PI_CODING_AGENT_DIR?.trim();return t===`~`?o.homedir():t?.startsWith(`~/`)?a.join(o.homedir(),t.slice(2)):t?a.resolve(t):a.join(o.homedir(),`.pi`,`agent`)}function g(e){let t=i.statSync(e).mtimeMs;for(let n of i.readdirSync(e,{withFileTypes:!0})){let r=a.join(e,n.name);if(t=Math.max(t,i.statSync(r).mtimeMs),n.isDirectory())for(let e of i.readdirSync(r))t=Math.max(t,i.statSync(a.join(r,e)).mtimeMs)}return t}async function _(e){let t=(await i.promises.stat(e)).mtimeMs;for(let n of await i.promises.readdir(e,{withFileTypes:!0})){let r=a.join(e,n.name);if(t=Math.max(t,(await i.promises.stat(r)).mtimeMs),n.isDirectory())for(let e of await i.promises.readdir(r))t=Math.max(t,(await i.promises.stat(a.join(r,e))).mtimeMs)}return t}function v(e){let t;try{t=i.readdirSync(e)}catch{return!1}for(let n of t)if(n.endsWith(u))try{let t=JSON.parse(i.readFileSync(a.join(e,n),d)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}async function y(e){let t;try{t=await i.promises.readdir(e)}catch{return!1}for(let n of t)if(n.endsWith(u))try{let t=JSON.parse(await i.promises.readFile(a.join(e,n),d)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}function b(e){if(typeof e!=`object`||!e)return;let t=e;if(t.state!==`completed`||typeof t.exit?.finishedAt!=`string`)return;let n=Date.parse(t.exit.finishedAt);return Number.isFinite(n)?n:void 0}let x=class{adoptedSessionId;repository;repositoryPath(){return this.repository??=m(p([`rev-parse`,`--show-toplevel`],process.cwd())??process.cwd()),this.repository}setSessionId(e){let t=e.trim();if(!t)throw Error(`doom-runner session id cannot be blank`);this.adoptedSessionId=t}logDirectory(t){let n=process.env[e]?.trim();return n?a.resolve(n):a.join(this.sessionDirectory(t),`logs`)}stateDirectory(t){let n=process.env[e]?.trim();return n?a.join(a.resolve(n),`..`,l):a.join(this.sessionDirectory(t),l)}logPathFor(e,t){return a.join(this.logDirectory(t),`${e}.log`)}rotatedLogPathFor(e,t){return`${this.logPathFor(e,t)}.1`}statePathFor(e,t){return a.join(this.stateDirectory(t),`${e}${u}`)}ensureDirectories(e){i.mkdirSync(this.logDirectory(e),{recursive:!0}),i.mkdirSync(this.stateDirectory(e),{recursive:!0})}sweepHistory(e,t=Date.now()){let n={removed:[],errors:[]};if(process.env.DOOM_RUNNER_LOG_DIR?.trim())return this.sweepStateDirectory(this.stateDirectory(),this.sessionId(),e,t,n),n;let r=this.storeDirectory(),o;try{o=i.readdirSync(r)}catch(e){return e.code===f||n.errors.push(`Could not read runner history directory ${r}: ${String(e)}`),n}let s=this.currentSessionId();for(let i of o)i!==s&&this.sweepSessionDirectory(a.join(r,i),e,t,n);return n}async sweepHistoryAsync(e,t=Date.now()){let n={removed:[],errors:[]};if(process.env.DOOM_RUNNER_LOG_DIR?.trim())return await this.sweepStateDirectoryAsync(this.stateDirectory(),this.sessionId(),e,t,n),n;let r=this.storeDirectory(),o;try{o=await i.promises.readdir(r)}catch(e){return e.code===f||n.errors.push(`Could not read runner history directory ${r}: ${String(e)}`),n}let s=this.currentSessionId();for(let i of o)i!==s&&await this.sweepSessionDirectoryAsync(a.join(r,i),e,t,n);return n}legacyDirectory(){let e=p([`rev-parse`,`--path-format=absolute`,`--git-common-dir`],process.cwd());return e?a.join(e,c):void 0}removeLegacyStore(){let e=this.legacyDirectory();if(!(!e||a.basename(e)!==c||!i.existsSync(e)))return i.rmSync(e,{recursive:!0,force:!0}),e}currentSessionId(){try{return this.sessionId()}catch{return}}sweepSessionDirectory(e,t,n,r){try{if(n-g(e)<=t||v(a.join(e,l)))return;i.rmSync(e,{recursive:!0,force:!0}),r.removed.push(e)}catch(t){t.code!==f&&r.errors.push(`Could not sweep runner session ${e}: ${String(t)}`)}}async sweepSessionDirectoryAsync(e,t,n,r){try{if(n-await _(e)<=t||await y(a.join(e,l)))return;await i.promises.rm(e,{recursive:!0,force:!0}),r.removed.push(e)}catch(t){t.code!==f&&r.errors.push(`Could not sweep runner session ${e}: ${String(t)}`)}}sessionId(e){let t=e?.trim()||this.adoptedSessionId||n();if(!t)throw Error(`PI_SESSION_ID is required for doom-runner storage`);return t}storeDirectory(){let t=process.env[e]?.trim();return t?a.dirname(a.resolve(t)):a.join(h(process.env),c)}sessionDirectory(e){return a.join(this.storeDirectory(),this.sessionId(e))}sweepStateDirectory(e,t,n,r,o){let s;try{s=i.readdirSync(e)}catch(t){t.code!==f&&o.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}for(let c of s){if(!c.endsWith(u))continue;let s=a.join(e,c);try{let e=b(JSON.parse(i.readFileSync(s,d)));if(e===void 0||r-e<=n)continue;let a=c.slice(0,-5);this.removeHistoryFiles(t,a,s,o)}catch(e){o.errors.push(`Could not sweep runner metadata ${s}: ${String(e)}`)}}}async sweepStateDirectoryAsync(e,t,n,r,o){let s;try{s=await i.promises.readdir(e)}catch(t){t.code!==f&&o.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}for(let c of s){if(!c.endsWith(u))continue;let s=a.join(e,c);try{let e=b(JSON.parse(await i.promises.readFile(s,d)));if(e===void 0||r-e<=n)continue;let a=c.slice(0,-5);await this.removeHistoryFilesAsync(t,a,s,o)}catch(e){o.errors.push(`Could not sweep runner metadata ${s}: ${String(e)}`)}}}removeHistoryFiles(e,t,n,r){let a=[n,this.logPathFor(t,e),this.rotatedLogPathFor(t,e)];for(let e of a)try{i.unlinkSync(e),r.removed.push(e)}catch(t){t.code!==f&&r.errors.push(`Could not remove runner history ${e}: ${String(t)}`)}}async removeHistoryFilesAsync(e,t,n,r){let a=[n,this.logPathFor(t,e),this.rotatedLogPathFor(t,e)];for(let e of a)try{await i.promises.unlink(e),r.removed.push(e)}catch(t){t.code!==f&&r.errors.push(`Could not remove runner history ${e}: ${String(t)}`)}}};x=t([r()],x);export{x as RunnerPaths};
|
|
2
|
+
//# sourceMappingURL=RunnerPaths.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RunnerPaths.mjs","names":[],"sources":["../../src/adapters/RunnerPaths.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { injectable } from 'inversify';\nimport 'reflect-metadata/lite';\nimport type { IRunnerPaths, LogSweepResult } from '../services/RunnerPaths/types.ts';\nimport { inheritedSessionId } from '../services/runs/session.ts';\nimport { LOG_DIR_ENV } from '../types/config.ts';\n\nconst STORE_DIR_NAME = 'doom-runner';\nconst DEFAULT_CONFIG_DIR_NAME = '.pi';\nconst AGENT_DIR_NAME = 'agent';\nconst LOG_DIR_NAME = 'logs';\nconst STATE_DIR_NAME = 'runs';\nconst LOG_EXTENSION = '.log';\nconst STATE_EXTENSION = '.json';\nconst ROTATED_SUFFIX = '.1';\nconst PI_CODING_AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\nconst HOME_ALIAS = '~';\nconst HOME_ALIAS_PREFIX = '~/';\nconst FILE_ENCODING = 'utf8';\nconst NOT_FOUND_ERROR_CODE = 'ENOENT';\n\nfunction git(args: string[], cwd: string): string | undefined {\n try {\n const output = execFileSync('git', args, { cwd, encoding: FILE_ENCODING, stdio: ['ignore', 'pipe', 'ignore'] });\n const trimmed = output.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction realPath(target: string): string {\n try {\n return fs.realpathSync(target);\n } catch {\n return target;\n }\n}\n\nfunction resolveAgentDirectory(env: NodeJS.ProcessEnv): string {\n const configured = env[PI_CODING_AGENT_DIR_ENV]?.trim();\n if (configured === HOME_ALIAS) return os.homedir();\n if (configured?.startsWith(HOME_ALIAS_PREFIX)) {\n return path.join(os.homedir(), configured.slice(HOME_ALIAS_PREFIX.length));\n }\n return configured ? path.resolve(configured) : path.join(os.homedir(), DEFAULT_CONFIG_DIR_NAME, AGENT_DIR_NAME);\n}\n\n/**\n * Newest mtime anywhere one level inside the session, falling back to the\n * directory itself. A session still being written to therefore always looks\n * recent, whatever state its individual records are in.\n */\nfunction newestEntryTime(directory: string): number {\n let newest = fs.statSync(directory).mtimeMs;\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n const target = path.join(directory, entry.name);\n newest = Math.max(newest, fs.statSync(target).mtimeMs);\n if (!entry.isDirectory()) continue;\n for (const child of fs.readdirSync(target)) {\n newest = Math.max(newest, fs.statSync(path.join(target, child)).mtimeMs);\n }\n }\n return newest;\n}\n\nasync function newestEntryTimeAsync(directory: string): Promise<number> {\n let newest = (await fs.promises.stat(directory)).mtimeMs;\n for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {\n const target = path.join(directory, entry.name);\n newest = Math.max(newest, (await fs.promises.stat(target)).mtimeMs);\n if (!entry.isDirectory()) continue;\n for (const child of await fs.promises.readdir(target)) {\n newest = Math.max(newest, (await fs.promises.stat(path.join(target, child))).mtimeMs);\n }\n }\n return newest;\n}\n\n/**\n * Whether any run in this session names a pi process that is still running.\n *\n * One live record is enough: records are only ever written by their owner, so a\n * live host pid means the session is still in use however old its files look.\n */\nfunction ownerAlive(stateDirectory: string): boolean {\n let entries: string[];\n try {\n entries = fs.readdirSync(stateDirectory);\n } catch {\n // No records left to consult, so nothing claims the directory.\n return false;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n try {\n const value: unknown = JSON.parse(fs.readFileSync(path.join(stateDirectory, entry), FILE_ENCODING));\n const hostPid = (value as { hostPid?: unknown }).hostPid;\n if (typeof hostPid !== 'number') continue;\n process.kill(hostPid, 0);\n return true;\n } catch {\n // An unreadable record and a dead pid mean the same thing here: no claim.\n }\n }\n return false;\n}\n\nasync function ownerAliveAsync(stateDirectory: string): Promise<boolean> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(stateDirectory);\n } catch {\n return false;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(path.join(stateDirectory, entry), FILE_ENCODING));\n const hostPid = (value as { hostPid?: unknown }).hostPid;\n if (typeof hostPid !== 'number') continue;\n process.kill(hostPid, 0);\n return true;\n } catch {\n // An unreadable record and a dead pid mean the same thing here: no claim.\n }\n }\n return false;\n}\n\nfunction completedAt(value: unknown): number | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = value as { state?: unknown; exit?: { finishedAt?: unknown } };\n if (record.state !== 'completed' || typeof record.exit?.finishedAt !== 'string') return undefined;\n const timestamp = Date.parse(record.exit.finishedAt);\n return Number.isFinite(timestamp) ? timestamp : undefined;\n}\n\n/** Session-scoped runner storage under the Pi agent directory. */\n@injectable()\nexport class RunnerPaths implements IRunnerPaths {\n private adoptedSessionId: string | undefined;\n private repository: string | undefined;\n\n repositoryPath(): string {\n this.repository ??= realPath(git(['rev-parse', '--show-toplevel'], process.cwd()) ?? process.cwd());\n return this.repository;\n }\n\n setSessionId(sessionId: string): void {\n const trimmed = sessionId.trim();\n if (!trimmed) throw new Error('doom-runner session id cannot be blank');\n this.adoptedSessionId = trimmed;\n }\n\n logDirectory(sessionId?: string): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.resolve(override);\n return path.join(this.sessionDirectory(sessionId), LOG_DIR_NAME);\n }\n\n stateDirectory(sessionId?: string): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.join(path.resolve(override), '..', STATE_DIR_NAME);\n return path.join(this.sessionDirectory(sessionId), STATE_DIR_NAME);\n }\n\n logPathFor(id: string, sessionId?: string): string {\n return path.join(this.logDirectory(sessionId), `${id}${LOG_EXTENSION}`);\n }\n\n rotatedLogPathFor(id: string, sessionId?: string): string {\n return `${this.logPathFor(id, sessionId)}${ROTATED_SUFFIX}`;\n }\n\n statePathFor(id: string, sessionId?: string): string {\n return path.join(this.stateDirectory(sessionId), `${id}${STATE_EXTENSION}`);\n }\n\n ensureDirectories(sessionId?: string): void {\n fs.mkdirSync(this.logDirectory(sessionId), { recursive: true });\n fs.mkdirSync(this.stateDirectory(sessionId), { recursive: true });\n }\n\n sweepHistory(ttlMs: number, now: number = Date.now()): LogSweepResult {\n const result: LogSweepResult = { removed: [], errors: [] };\n // A pinned log directory belongs to whoever pinned it, so that branch only\n // ever removes the files this package put there.\n if (process.env[LOG_DIR_ENV]?.trim()) {\n this.sweepStateDirectory(this.stateDirectory(), this.sessionId(), ttlMs, now, result);\n return result;\n }\n const root = this.storeDirectory();\n let sessions: string[];\n try {\n sessions = fs.readdirSync(root);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return result;\n result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);\n return result;\n }\n\n // Sweeping whole session directories rather than the three files a completed\n // record names is what keeps sockets, sidecars, runs that never reached a\n // terminal state, and the emptied directories themselves from accumulating.\n const active = this.currentSessionId();\n for (const sessionId of sessions) {\n if (sessionId === active) continue;\n this.sweepSessionDirectory(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n async sweepHistoryAsync(ttlMs: number, now: number = Date.now()): Promise<LogSweepResult> {\n const result: LogSweepResult = { removed: [], errors: [] };\n if (process.env[LOG_DIR_ENV]?.trim()) {\n await this.sweepStateDirectoryAsync(this.stateDirectory(), this.sessionId(), ttlMs, now, result);\n return result;\n }\n\n const root = this.storeDirectory();\n let sessions: string[];\n try {\n sessions = await fs.promises.readdir(root);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return result;\n result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);\n return result;\n }\n\n const active = this.currentSessionId();\n for (const sessionId of sessions) {\n if (sessionId === active) continue;\n await this.sweepSessionDirectoryAsync(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n legacyDirectory(): string | undefined {\n const commonDirectory = git(['rev-parse', '--path-format=absolute', '--git-common-dir'], process.cwd());\n return commonDirectory ? path.join(commonDirectory, STORE_DIR_NAME) : undefined;\n }\n\n removeLegacyStore(): string | undefined {\n const target = this.legacyDirectory();\n if (!target || path.basename(target) !== STORE_DIR_NAME || !fs.existsSync(target)) return undefined;\n fs.rmSync(target, { recursive: true, force: true });\n return target;\n }\n\n /** The live session, if one is known, so a sweep never deletes underneath itself. */\n private currentSessionId(): string | undefined {\n try {\n return this.sessionId();\n } catch {\n // A CLI invocation outside a session still sweeps; it just has nothing to spare.\n return undefined;\n }\n }\n\n /** Removes a session's whole directory once its newest entry is past the TTL. */\n private sweepSessionDirectory(directory: string, ttlMs: number, now: number, result: LogSweepResult): void {\n try {\n if (now - newestEntryTime(directory) <= ttlMs) return;\n // An idle session can go quiet for longer than the TTL, and deleting one\n // takes its lifeline socket with it, which would read to its own runners\n // as an owner that had died. Its records name the owner, so ask.\n if (ownerAlive(path.join(directory, STATE_DIR_NAME))) return;\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private async sweepSessionDirectoryAsync(\n directory: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n try {\n if (now - (await newestEntryTimeAsync(directory)) <= ttlMs) return;\n if (await ownerAliveAsync(path.join(directory, STATE_DIR_NAME))) return;\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private sessionId(explicit?: string): string {\n const sessionId = explicit?.trim() || this.adoptedSessionId || inheritedSessionId();\n if (!sessionId) throw new Error('PI_SESSION_ID is required for doom-runner storage');\n return sessionId;\n }\n\n private storeDirectory(): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.dirname(path.resolve(override));\n return path.join(resolveAgentDirectory(process.env), STORE_DIR_NAME);\n }\n\n private sessionDirectory(sessionId?: string): string {\n return path.join(this.storeDirectory(), this.sessionId(sessionId));\n }\n\n private sweepStateDirectory(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): void {\n let entries: string[];\n try {\n entries = fs.readdirSync(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(fs.readFileSync(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined || now - finishedAt <= ttlMs) continue;\n const id = entry.slice(0, -STATE_EXTENSION.length);\n this.removeHistoryFiles(sessionId, id, statePath, result);\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n }\n\n private async sweepStateDirectoryAsync(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined || now - finishedAt <= ttlMs) continue;\n const id = entry.slice(0, -STATE_EXTENSION.length);\n await this.removeHistoryFilesAsync(sessionId, id, statePath, result);\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n }\n\n private removeHistoryFiles(sessionId: string, id: string, statePath: string, result: LogSweepResult): void {\n const candidates = [statePath, this.logPathFor(id, sessionId), this.rotatedLogPathFor(id, sessionId)];\n for (const candidate of candidates) {\n try {\n fs.unlinkSync(candidate);\n result.removed.push(candidate);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);\n }\n }\n }\n }\n\n private async removeHistoryFilesAsync(\n sessionId: string,\n id: string,\n statePath: string,\n result: LogSweepResult,\n ): Promise<void> {\n const candidates = [statePath, this.logPathFor(id, sessionId), this.rotatedLogPathFor(id, sessionId)];\n for (const candidate of candidates) {\n try {\n await fs.promises.unlink(candidate);\n result.removed.push(candidate);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);\n }\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const e=require("../../types/config.cjs"),t=require("../../types/index.cjs"),n=require("../../container/index.cjs"),r=require("../../services/runs/compaction.cjs"),i=require("../../commands/bash/bashTool.cjs"),a=require("../../services/runs/reconcile.cjs"),o=require("../../tui/format.cjs"),s=require("../../tui/runnerSpace.cjs"),c=require("../pty/ptyProvider.cjs");let l=require("@agimon-ai/doompi-extension-contracts/child-process"),u=require("@agimon-ai/doompi-telemetry"),d=require("@agimon-ai/doompi-extension-contracts/protocol"),f=require("@agimon-ai/doompi-ui/footer"),p=require("@agimon-ai/doompi-ui/leader");const m=`@agimon-ai/doompi-runner`,h=`runners`,g=`completed`,_=`stopped`,v=`doom-runner-runners`;function y(y){let b=n.createRunnerContainer(),x=b.get(t.TYPES.RunnerRegistry),S=b.get(t.TYPES.Launcher),C=b.get(t.TYPES.RmuxBackend),w=b.get(t.TYPES.LogReader),T=b.get(t.TYPES.PtyHost),E=b.get(t.TYPES.BashRunService),D=b.get(t.TYPES.RunnerPaths),O=b.get(t.TYPES.ProcessControl),k=b.get(t.TYPES.Lifeline),A,j,M=[],N,P,F,I=!1,L=!1,R=!1,z,B,V,H=!1,U=!1,W,G=new Set,K=new Set,q=e=>(F??=(0,u.createDoomTelemetry)({serviceName:`doom-runner`,packageName:`@agimon-ai/doompi-runner`,cwd:e.cwd,env:process.env,enableLogs:!0,enableTraces:!0}),F),J=(0,f.registerDoomFooterContribution)(y,{source:m,id:`runner-count`,order:10}),Y=async e=>{let t=A;if(!t||L)return;let n=await x.list();if(L)return;let r=n;if(e){let e=await a.reconcileActiveRunners({registry:x,launcher:S,rmuxBackend:C,processControl:O,currentHostPid:process.pid,startup:!1,active:n});for(let t of e.errors)process.emitWarning(t);if(e.reclaimed.length>0){let t=new Set(e.reclaimed);r=n.filter(e=>!t.has(e.id))}}if(L)return;M=r.filter(e=>e.sessionId===t);let i=(j?r.filter(e=>(e.rootSessionId??e.sessionId)===j):[]).length;W!==i&&(W=i,J.update(o.formatRunnerFooterContribution(i)),N?.hasUI&&N.ui.setStatus(v,o.formatRunnerStatus(i)));let s=[...K],c=await Promise.all(s.map(e=>x.get(e,t)));if(!L)for(let[e,n]of c.entries()){let r=s[e];if(!r)continue;if(!n||n.sessionId!==t){K.delete(r);continue}if(n.state!==g||(K.delete(n.id),G.has(n.id)))continue;G.add(n.id);let i=n.exit?.reason??g,a=n.exit?.code===null||n.exit?.code===void 0?``:`, exit code ${n.exit.code}`,o=[`Background runner ${n.name} exited: ${i}${a}.`,`Runner ID: ${n.id}`,`Log: ${n.logPath}`,`Inspect: doom-runner logs ${n.id}`].join(`
|
|
2
|
+
`);y.sendMessage({customType:`doom-runner-finished`,content:o,display:!0},{triggerTurn:!0,deliverAs:`steer`}),N&&q(N).recordEvent(`doom_runner.process_finished`,{outcome:`completed`,"runner.exit_code":n.exit?.code??0,"runner.backend":n.backend,...Number.isFinite(Date.parse(n.startedAt))?{duration_ms:Math.max(0,Date.now()-Date.parse(n.startedAt))}:{}})}},X=(e=!0)=>{if(!A||!I||L)return Promise.resolve();if(z)return H=!0,U||=e,z;let t=(async()=>{let t=e;do H=!1,U=!1,await Y(t),t=U;while(H&&!L)})().finally(()=>{z===t&&(z=void 0)});return z=t,t},Z=e=>{L||(N&&q(N).recordError(`doom_runner.refresh_failed`,e),process.emitWarning(`Could not refresh doom-runner: ${String(e)}`))},Q=e=>{X(e).catch(Z)},ee=()=>{V||B||L||(V=setImmediate(()=>{if(V=void 0,L)return;let t=(async()=>{let t=D.sweepHistoryAsync?await D.sweepHistoryAsync(e.getLogTtlMs()):D.sweepHistory(e.getLogTtlMs());for(let e of t.errors)process.emitWarning(e)})().catch(e=>process.emitWarning(`Could not sweep doom-runner history: ${String(e)}`)).finally(()=>{B===t&&(B=void 0)});B=t}),V.unref?.())},te=x.subscribe(()=>Q(!1)),$=setInterval(()=>Q(!0),500);$.unref?.(),i.registerBashTool(y,{bashRunService:E,getSessionId:()=>{if(!A)throw Error(`doom-runner requires an active Pi session`);return A},onRunnerStarted:e=>{K.add(e),Q(!1)}}),r.registerRunnerCompactionRecovery(y,{getSessionId:()=>A,listBySession:e=>x.listBySession(e)}),y.registerCommand(h,{description:`Open Runner Space: background processes started by bash`,handler:async(e,t)=>{if(!t.hasUI){t.ui.notify(`/runners requires interactive mode`,`error`);return}await X(),await s.openRunnerSpace(t,{getRunners:()=>M,getPtyRun:e=>C.get(e)??T.get(e),readLog:e=>w.read(e,{lines:1e3}).text,stopRunner:async(e,t)=>{let n=M.find(t=>t.id===e);n&&(n.backend===`rmux`&&n.backendTarget?await C.stop(n.backendTarget,n.pid):await S.stop(n.pid),await x.complete(n.id,{reason:_,code:null,signal:null,stopReason:t},n.sessionId),await X())}})}}),(0,p.registerDoomLeaderContribution)(y,{source:m,bindings:[{id:`runners.open`,path:[{key:`r`,label:`runners`,order:67},{key:`r`,label:`open`,detail:`background processes`}],command:{name:h}}]}),y.on(`session_start`,async(e,t)=>{let n=t,r=q(n);r.recordEvent(`doom_runner.session_started`,{outcome:`started`}),A=n.sessionManager.getSessionId(),D.setSessionId(A),j=(0,l.resolveRootSessionId)(A),N=n,I=!1,W=void 0,G.clear(),K.clear(),await k.arm(A),P?.(),P=c.provideRunnerPty((0,d.createProtocolRuntime)({bus:y.events,source:m,sessionId:A}),{ptyHost:T,sessionId:()=>A,onError:(e,t)=>void r.recordError(`doom_runner.pty_launch_failed`,e,{"run.id":t})});let i=await x.listAll(A);for(let e of i)e.state===g?G.add(e.id):e.promoted&&K.add(e.id);let o=await a.cleanupLegacyRunnerStore({registry:x,launcher:S,rmuxBackend:C,processControl:O,currentHostPid:process.pid,paths:D}),s=await a.reconcileActiveRunners({registry:x,launcher:S,rmuxBackend:C,processControl:O,currentHostPid:process.pid,startup:!0});I=!0,await X(!1),ee();let u=[...o.errors,...s.errors];for(let e of u)process.emitWarning(e);let f=o.reclaimed.length+s.reclaimed.length;f>0&&n.hasUI&&n.ui.notify(`Reclaimed ${f} stale runner record(s)`,`warning`),r.recordEvent(`doom_runner.reconciled`,{"runner.reclaimed_count":f,"runner.error_count":u.length,outcome:u.length===0?`completed`:`degraded`})}),y.on(`session_shutdown`,async()=>{if(R)return;R=!0,I=!1,L=!0,clearInterval($),V&&=(clearImmediate(V),void 0),te();let e=z;if(e)try{await e}catch(e){process.emitWarning(`Could not settle doom-runner refresh during shutdown: ${String(e)}`)}let t=B;t&&await t;try{try{P?.(),P=void 0,await T.disposeAll()}catch(e){process.emitWarning(`Could not dispose doom-runner PTYs: ${String(e)}`)}let e=A?await x.listBySession(A):[];for(let t of e)try{let e=await a.stopRunnerProcess(t,S,C);if(!e&&O.isAlive(t.pid)){process.emitWarning(`Could not stop runner ${t.id} during session shutdown`);continue}await x.complete(t.id,{reason:_,code:null,signal:e?`SIGTERM`:null,stopReason:`session ended`},t.sessionId)}catch(e){process.emitWarning(`Could not clean up runner ${t.id}: ${String(e)}`)}}finally{k.dispose(),J.dispose(),N?.hasUI&&N.ui.setStatus(v,void 0),x.close(),await F?.recordEvent(`doom_runner.session_finished`,{outcome:`stopped`}),await F?.shutdown(),F=void 0}})}exports.runnerExtension=y;
|
|
3
|
+
//# sourceMappingURL=extension.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.cjs","names":["createRunnerContainer","TYPES","createDoomTelemetry","registerDoomFooterContribution","reconcileActiveRunners","formatRunnerFooterContribution","formatRunnerStatus","getLogTtlMs","openRunnerSpace","resolveRootSessionId","provideRunnerPty","createProtocolRuntime","cleanupLegacyRunnerStore","stopRunnerProcess"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { resolveRootSessionId } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { createProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport { registerDoomFooterContribution } from '@agimon-ai/doompi-ui/footer';\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerBashTool } from '../../commands/bash/bashTool.ts';\nimport { createRunnerContainer } from '../../container/index.ts';\nimport type { IBashRunService } from '../../services/BashRunService/index.ts';\nimport type { ILauncher } from '../../services/Launcher/index.ts';\nimport type { ILifeline } from '../../services/Lifeline/index.ts';\nimport type { ILogReader } from '../../services/LogReader/index.ts';\nimport type { IProcessControl } from '../../services/ProcessControl/index.ts';\nimport type { IPtyHost } from '../../services/PtyHost/index.ts';\nimport type { IRmuxBackend } from '../../services/RmuxBackend/index.ts';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/index.ts';\nimport type { IRunnerRegistry, RunnerRecord } from '../../services/RunnerRegistry/index.ts';\nimport { registerRunnerCompactionRecovery } from '../../services/runs/compaction.ts';\nimport { cleanupLegacyRunnerStore, reconcileActiveRunners, stopRunnerProcess } from '../../services/runs/reconcile.ts';\nimport { formatRunnerFooterContribution, formatRunnerStatus } from '../../tui/format.ts';\nimport { openRunnerSpace } from '../../tui/runnerSpace.ts';\nimport { getLogTtlMs } from '../../types/config.ts';\nimport { TYPES } from '../../types/index.ts';\nimport { provideRunnerPty } from '../pty/ptyProvider.ts';\n\nconst LEADER_SOURCE = '@agimon-ai/doompi-runner';\n/** After doom-task's `t` (65) and before the core help group (70). */\nconst LEADER_GROUP_ORDER = 67;\nconst COMMAND_NAME = 'runners';\nconst ERR_REQUIRES_INTERACTIVE = '/runners requires interactive mode';\n\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\nconst RUNNER_FINISHED_MESSAGE = 'doom-runner-finished';\nconst COMPLETED_STATE = 'completed';\nconst RMUX_BACKEND = 'rmux';\nconst STOPPED_REASON = 'stopped';\nconst RUNNER_STATUS_KEY = 'doom-runner-runners';\nconst RUNNER_FOOTER_ORDER = 10;\nconst RUNNER_STATUS_POLL_MS = 500;\n\n/**\n * doom-runner: replaces pi's `bash` tool with a supervised one and provides a\n * CLI for anything it leaves running.\n */\nexport function runnerExtension(pi: ExtensionAPI): void {\n const container = createRunnerContainer();\n const registry = container.get<IRunnerRegistry>(TYPES.RunnerRegistry);\n const launcher = container.get<ILauncher>(TYPES.Launcher);\n const rmuxBackend = container.get<IRmuxBackend>(TYPES.RmuxBackend);\n const logReader = container.get<ILogReader>(TYPES.LogReader);\n const ptyHost = container.get<IPtyHost>(TYPES.PtyHost);\n const bashRunService = container.get<IBashRunService>(TYPES.BashRunService);\n const paths = container.get<IRunnerPaths>(TYPES.RunnerPaths);\n const processControl = container.get<IProcessControl>(TYPES.ProcessControl);\n const lifeline = container.get<ILifeline>(TYPES.Lifeline);\n\n let sessionId: string | undefined;\n let rootSessionId: string | undefined;\n let runners: RunnerRecord[] = [];\n let sessionContext: ExtensionContext | undefined;\n let disposePtyProvider: (() => void) | undefined;\n let telemetry: DoomTelemetry | undefined;\n let sessionReady = false;\n let disposed = false;\n let shutdownStarted = false;\n let refreshInFlight: Promise<void> | undefined;\n let historySweepInFlight: Promise<void> | undefined;\n let historySweepTimer: ReturnType<typeof setImmediate> | undefined;\n let refreshQueued = false;\n let queuedReconciliation = false;\n let lastRunnerCount: number | undefined;\n const notifiedRunnerIds = new Set<string>();\n const pendingPromotedRunnerIds = new Set<string>();\n const getTelemetry = (ctx: ExtensionContext): DoomTelemetry => {\n telemetry ??= createDoomTelemetry({\n serviceName: 'doom-runner',\n packageName: '@agimon-ai/doompi-runner',\n cwd: ctx.cwd,\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n return telemetry;\n };\n const footerContribution = registerDoomFooterContribution(pi, {\n source: LEADER_SOURCE,\n id: 'runner-count',\n order: RUNNER_FOOTER_ORDER,\n });\n\n /** Performs one bounded pass over active state and explicitly monitored runners. */\n const refreshNow = async (shouldReconcile: boolean): Promise<void> => {\n const activeSessionId = sessionId;\n if (!activeSessionId || disposed) return;\n\n const active = await registry.list();\n if (disposed) return;\n let visibleActive = active;\n if (shouldReconcile) {\n const reconciled = await reconcileActiveRunners({\n registry,\n launcher,\n rmuxBackend,\n processControl,\n currentHostPid: process.pid,\n startup: false,\n active,\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n if (reconciled.reclaimed.length > 0) {\n const reclaimedIds = new Set(reconciled.reclaimed);\n visibleActive = active.filter((record) => !reclaimedIds.has(record.id));\n }\n }\n if (disposed) return;\n\n runners = visibleActive.filter((record) => record.sessionId === activeSessionId);\n const rootRunners = rootSessionId\n ? visibleActive.filter((record) => (record.rootSessionId ?? record.sessionId) === rootSessionId)\n : [];\n const runnerCount = rootRunners.length;\n if (lastRunnerCount !== runnerCount) {\n lastRunnerCount = runnerCount;\n footerContribution.update(formatRunnerFooterContribution(runnerCount));\n if (sessionContext?.hasUI) sessionContext.ui.setStatus(RUNNER_STATUS_KEY, formatRunnerStatus(runnerCount));\n }\n\n const pendingIds = [...pendingPromotedRunnerIds];\n const monitored = await Promise.all(pendingIds.map((id) => registry.get(id, activeSessionId)));\n if (disposed) return;\n for (const [index, record] of monitored.entries()) {\n const id = pendingIds[index];\n if (!id) continue;\n if (!record || record.sessionId !== activeSessionId) {\n pendingPromotedRunnerIds.delete(id);\n continue;\n }\n if (record.state !== COMPLETED_STATE) continue;\n pendingPromotedRunnerIds.delete(record.id);\n if (notifiedRunnerIds.has(record.id)) continue;\n notifiedRunnerIds.add(record.id);\n const outcome = record.exit?.reason ?? COMPLETED_STATE;\n const code =\n record.exit?.code === null || record.exit?.code === undefined ? '' : `, exit code ${record.exit.code}`;\n const content = [\n `Background runner ${record.name} exited: ${outcome}${code}.`,\n `Runner ID: ${record.id}`,\n `Log: ${record.logPath}`,\n `Inspect: doom-runner logs ${record.id}`,\n ].join('\\n');\n pi.sendMessage(\n { customType: RUNNER_FINISHED_MESSAGE, content, display: true },\n { triggerTurn: true, deliverAs: 'steer' },\n );\n if (sessionContext) {\n void getTelemetry(sessionContext).recordEvent('doom_runner.process_finished', {\n outcome: 'completed',\n 'runner.exit_code': record.exit?.code ?? 0,\n 'runner.backend': record.backend,\n ...(Number.isFinite(Date.parse(record.startedAt))\n ? { duration_ms: Math.max(0, Date.now() - Date.parse(record.startedAt)) }\n : {}),\n });\n }\n }\n };\n\n /** Coalesces timer and event triggers without losing a requested reconciliation pass. */\n const refresh = (shouldReconcile = true): Promise<void> => {\n if (!sessionId || !sessionReady || disposed) return Promise.resolve();\n if (refreshInFlight) {\n refreshQueued = true;\n queuedReconciliation ||= shouldReconcile;\n return refreshInFlight;\n }\n\n const execution = (async () => {\n let reconcileNext = shouldReconcile;\n do {\n refreshQueued = false;\n queuedReconciliation = false;\n await refreshNow(reconcileNext);\n reconcileNext = queuedReconciliation;\n } while (refreshQueued && !disposed);\n })().finally(() => {\n if (refreshInFlight === execution) refreshInFlight = undefined;\n });\n refreshInFlight = execution;\n return execution;\n };\n\n const reportRefreshError = (error: unknown): void => {\n if (disposed) return;\n if (sessionContext) void getTelemetry(sessionContext).recordError('doom_runner.refresh_failed', error);\n process.emitWarning(`Could not refresh doom-runner: ${String(error)}`);\n };\n const scheduleRefresh = (shouldReconcile: boolean): void => {\n void refresh(shouldReconcile).catch(reportRefreshError);\n };\n const scheduleHistorySweep = (): void => {\n if (historySweepTimer || historySweepInFlight || disposed) return;\n historySweepTimer = setImmediate(() => {\n historySweepTimer = undefined;\n if (disposed) return;\n const execution = (async () => {\n const sweep = paths.sweepHistoryAsync\n ? await paths.sweepHistoryAsync(getLogTtlMs())\n : paths.sweepHistory(getLogTtlMs());\n for (const error of sweep.errors) process.emitWarning(error);\n })()\n .catch((error) => process.emitWarning(`Could not sweep doom-runner history: ${String(error)}`))\n .finally(() => {\n if (historySweepInFlight === execution) historySweepInFlight = undefined;\n });\n historySweepInFlight = execution;\n });\n historySweepTimer.unref?.();\n };\n const unsubscribeRegistry = registry.subscribe(() => scheduleRefresh(false));\n const statusPoll = setInterval(() => scheduleRefresh(true), RUNNER_STATUS_POLL_MS);\n statusPoll.unref?.();\n\n registerBashTool(pi, {\n bashRunService,\n getSessionId: () => {\n if (!sessionId) throw new Error('doom-runner requires an active Pi session');\n return sessionId;\n },\n onRunnerStarted: (id) => {\n pendingPromotedRunnerIds.add(id);\n scheduleRefresh(false);\n },\n });\n\n registerRunnerCompactionRecovery(pi, {\n getSessionId: () => sessionId,\n listBySession: (activeSessionId) => registry.listBySession(activeSessionId),\n });\n\n pi.registerCommand(COMMAND_NAME, {\n description: 'Open Runner Space: background processes started by bash',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI) {\n ctx.ui.notify(ERR_REQUIRES_INTERACTIVE, 'error');\n return;\n }\n await refresh();\n await openRunnerSpace(ctx, {\n getRunners: () => runners,\n getPtyRun: (name) => rmuxBackend.get(name) ?? ptyHost.get(name),\n readLog: (logPath) => logReader.read(logPath, { lines: 1_000 }).text,\n stopRunner: async (id, reason) => {\n const record = runners.find((candidate) => candidate.id === id);\n if (!record) return;\n if (record.backend === RMUX_BACKEND && record.backendTarget) {\n await rmuxBackend.stop(record.backendTarget, record.pid);\n } else await launcher.stop(record.pid);\n await registry.complete(\n record.id,\n { reason: STOPPED_REASON, code: null, signal: null, stopReason: reason },\n record.sessionId,\n );\n await refresh();\n },\n });\n },\n });\n\n registerDoomLeaderContribution(pi, {\n source: LEADER_SOURCE,\n bindings: [\n {\n id: 'runners.open',\n path: [\n { key: 'r', label: 'runners', order: LEADER_GROUP_ORDER },\n { key: 'r', label: 'open', detail: 'background processes' },\n ],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n\n pi.on(SESSION_START_EVENT, async (_event, ctx) => {\n const context = ctx as ExtensionContext;\n const activeTelemetry = getTelemetry(context);\n void activeTelemetry.recordEvent('doom_runner.session_started', { outcome: 'started' });\n sessionId = context.sessionManager.getSessionId();\n paths.setSessionId(sessionId);\n rootSessionId = resolveRootSessionId(sessionId);\n sessionContext = context;\n sessionReady = false;\n lastRunnerCount = undefined;\n notifiedRunnerIds.clear();\n pendingPromotedRunnerIds.clear();\n // Awaited before anything can launch, so every runner this session starts\n // finds a lifeline it can connect to rather than a socket that is not\n // listening yet, which would read as an owner that is already gone.\n await lifeline.arm(sessionId);\n\n // This package owns the only pty in the workspace, so it lends one to any\n // extension that needs a command the user can watch and answer.\n disposePtyProvider?.();\n disposePtyProvider = provideRunnerPty(createProtocolRuntime({ bus: pi.events, source: LEADER_SOURCE, sessionId }), {\n ptyHost,\n sessionId: () => sessionId,\n onError: (error, id) =>\n void activeTelemetry.recordError('doom_runner.pty_launch_failed', error, { 'run.id': id }),\n });\n\n const retained = await registry.listAll(sessionId);\n for (const record of retained) {\n if (record.state === COMPLETED_STATE) notifiedRunnerIds.add(record.id);\n else if (record.promoted) pendingPromotedRunnerIds.add(record.id);\n }\n const legacy = await cleanupLegacyRunnerStore({\n registry,\n launcher,\n rmuxBackend,\n processControl,\n currentHostPid: process.pid,\n paths,\n });\n const reconciled = await reconcileActiveRunners({\n registry,\n launcher,\n rmuxBackend,\n processControl,\n currentHostPid: process.pid,\n startup: true,\n });\n sessionReady = true;\n await refresh(false);\n scheduleHistorySweep();\n const errors = [...legacy.errors, ...reconciled.errors];\n for (const error of errors) process.emitWarning(error);\n const reclaimedCount = legacy.reclaimed.length + reconciled.reclaimed.length;\n if (reclaimedCount > 0 && context.hasUI) {\n context.ui.notify(`Reclaimed ${reclaimedCount} stale runner record(s)`, 'warning');\n }\n void activeTelemetry.recordEvent('doom_runner.reconciled', {\n 'runner.reclaimed_count': reclaimedCount,\n 'runner.error_count': errors.length,\n outcome: errors.length === 0 ? 'completed' : 'degraded',\n });\n });\n\n pi.on(SESSION_SHUTDOWN_EVENT, async () => {\n if (shutdownStarted) return;\n shutdownStarted = true;\n sessionReady = false;\n disposed = true;\n clearInterval(statusPoll);\n if (historySweepTimer) {\n clearImmediate(historySweepTimer);\n historySweepTimer = undefined;\n }\n unsubscribeRegistry();\n const activeRefresh = refreshInFlight;\n if (activeRefresh) {\n try {\n await activeRefresh;\n } catch (error) {\n process.emitWarning(`Could not settle doom-runner refresh during shutdown: ${String(error)}`);\n }\n }\n const activeHistorySweep = historySweepInFlight;\n if (activeHistorySweep) await activeHistorySweep;\n try {\n try {\n disposePtyProvider?.();\n disposePtyProvider = undefined;\n await ptyHost.disposeAll();\n } catch (error) {\n process.emitWarning(`Could not dispose doom-runner PTYs: ${String(error)}`);\n }\n const owned = sessionId ? await registry.listBySession(sessionId) : [];\n for (const record of owned) {\n try {\n const stopped = await stopRunnerProcess(record, launcher, rmuxBackend);\n if (!stopped && processControl.isAlive(record.pid)) {\n process.emitWarning(`Could not stop runner ${record.id} during session shutdown`);\n continue;\n }\n await registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: stopped ? 'SIGTERM' : null,\n stopReason: 'session ended',\n },\n record.sessionId,\n );\n } catch (error) {\n process.emitWarning(`Could not clean up runner ${record.id}: ${String(error)}`);\n }\n }\n } finally {\n // Last, and in `finally`: closing the lifeline reaps anything the loop\n // above failed to stop, so a partial cleanup still leaves nothing behind.\n lifeline.dispose();\n footerContribution.dispose();\n if (sessionContext?.hasUI) sessionContext.ui.setStatus(RUNNER_STATUS_KEY, undefined);\n registry.close();\n await telemetry?.recordEvent('doom_runner.session_finished', { outcome: 'stopped' });\n await telemetry?.shutdown();\n telemetry = undefined;\n }\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
//#region src/adapters/pi/extension.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* doom-runner: replaces pi's `bash` tool with a supervised one and provides a
|
|
5
|
+
* CLI for anything it leaves running.
|
|
6
|
+
*/
|
|
7
|
+
declare function runnerExtension(pi: ExtensionAPI): void;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { runnerExtension };
|
|
10
|
+
//# sourceMappingURL=extension.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;iBA6CgB,gBAAgB,IAAI"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
//#region src/adapters/pi/extension.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* doom-runner: replaces pi's `bash` tool with a supervised one and provides a
|
|
5
|
+
* CLI for anything it leaves running.
|
|
6
|
+
*/
|
|
7
|
+
declare function runnerExtension(pi: ExtensionAPI): void;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { runnerExtension };
|
|
10
|
+
//# sourceMappingURL=extension.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;iBA6CgB,gBAAgB,IAAI"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{getLogTtlMs as e}from"../../types/config.mjs";import{TYPES as t}from"../../types/index.mjs";import{createRunnerContainer as n}from"../../container/index.mjs";import{registerRunnerCompactionRecovery as r}from"../../services/runs/compaction.mjs";import{registerBashTool as i}from"../../commands/bash/bashTool.mjs";import{cleanupLegacyRunnerStore as a,reconcileActiveRunners as o,stopRunnerProcess as s}from"../../services/runs/reconcile.mjs";import{formatRunnerFooterContribution as c,formatRunnerStatus as l}from"../../tui/format.mjs";import{openRunnerSpace as u}from"../../tui/runnerSpace.mjs";import{provideRunnerPty as d}from"../pty/ptyProvider.mjs";import{resolveRootSessionId as f}from"@agimon-ai/doompi-extension-contracts/child-process";import{createDoomTelemetry as p}from"@agimon-ai/doompi-telemetry";import{createProtocolRuntime as ee}from"@agimon-ai/doompi-extension-contracts/protocol";import{registerDoomFooterContribution as m}from"@agimon-ai/doompi-ui/footer";import{registerDoomLeaderContribution as h}from"@agimon-ai/doompi-ui/leader";const g=`@agimon-ai/doompi-runner`,_=`runners`,v=`completed`,y=`stopped`,b=`doom-runner-runners`;function x(x){let S=n(),C=S.get(t.RunnerRegistry),w=S.get(t.Launcher),T=S.get(t.RmuxBackend),E=S.get(t.LogReader),D=S.get(t.PtyHost),O=S.get(t.BashRunService),k=S.get(t.RunnerPaths),A=S.get(t.ProcessControl),j=S.get(t.Lifeline),M,N,P=[],F,I,L,R=!1,z=!1,B=!1,V,H,U,W=!1,G=!1,K,q=new Set,J=new Set,Y=e=>(L??=p({serviceName:`doom-runner`,packageName:`@agimon-ai/doompi-runner`,cwd:e.cwd,env:process.env,enableLogs:!0,enableTraces:!0}),L),X=m(x,{source:g,id:`runner-count`,order:10}),te=async e=>{let t=M;if(!t||z)return;let n=await C.list();if(z)return;let r=n;if(e){let e=await o({registry:C,launcher:w,rmuxBackend:T,processControl:A,currentHostPid:process.pid,startup:!1,active:n});for(let t of e.errors)process.emitWarning(t);if(e.reclaimed.length>0){let t=new Set(e.reclaimed);r=n.filter(e=>!t.has(e.id))}}if(z)return;P=r.filter(e=>e.sessionId===t);let i=(N?r.filter(e=>(e.rootSessionId??e.sessionId)===N):[]).length;K!==i&&(K=i,X.update(c(i)),F?.hasUI&&F.ui.setStatus(b,l(i)));let a=[...J],s=await Promise.all(a.map(e=>C.get(e,t)));if(!z)for(let[e,n]of s.entries()){let r=a[e];if(!r)continue;if(!n||n.sessionId!==t){J.delete(r);continue}if(n.state!==v||(J.delete(n.id),q.has(n.id)))continue;q.add(n.id);let i=n.exit?.reason??v,o=n.exit?.code===null||n.exit?.code===void 0?``:`, exit code ${n.exit.code}`,s=[`Background runner ${n.name} exited: ${i}${o}.`,`Runner ID: ${n.id}`,`Log: ${n.logPath}`,`Inspect: doom-runner logs ${n.id}`].join(`
|
|
2
|
+
`);x.sendMessage({customType:`doom-runner-finished`,content:s,display:!0},{triggerTurn:!0,deliverAs:`steer`}),F&&Y(F).recordEvent(`doom_runner.process_finished`,{outcome:`completed`,"runner.exit_code":n.exit?.code??0,"runner.backend":n.backend,...Number.isFinite(Date.parse(n.startedAt))?{duration_ms:Math.max(0,Date.now()-Date.parse(n.startedAt))}:{}})}},Z=(e=!0)=>{if(!M||!R||z)return Promise.resolve();if(V)return W=!0,G||=e,V;let t=(async()=>{let t=e;do W=!1,G=!1,await te(t),t=G;while(W&&!z)})().finally(()=>{V===t&&(V=void 0)});return V=t,t},ne=e=>{z||(F&&Y(F).recordError(`doom_runner.refresh_failed`,e),process.emitWarning(`Could not refresh doom-runner: ${String(e)}`))},Q=e=>{Z(e).catch(ne)},re=()=>{U||H||z||(U=setImmediate(()=>{if(U=void 0,z)return;let t=(async()=>{let t=k.sweepHistoryAsync?await k.sweepHistoryAsync(e()):k.sweepHistory(e());for(let e of t.errors)process.emitWarning(e)})().catch(e=>process.emitWarning(`Could not sweep doom-runner history: ${String(e)}`)).finally(()=>{H===t&&(H=void 0)});H=t}),U.unref?.())},ie=C.subscribe(()=>Q(!1)),$=setInterval(()=>Q(!0),500);$.unref?.(),i(x,{bashRunService:O,getSessionId:()=>{if(!M)throw Error(`doom-runner requires an active Pi session`);return M},onRunnerStarted:e=>{J.add(e),Q(!1)}}),r(x,{getSessionId:()=>M,listBySession:e=>C.listBySession(e)}),x.registerCommand(_,{description:`Open Runner Space: background processes started by bash`,handler:async(e,t)=>{if(!t.hasUI){t.ui.notify(`/runners requires interactive mode`,`error`);return}await Z(),await u(t,{getRunners:()=>P,getPtyRun:e=>T.get(e)??D.get(e),readLog:e=>E.read(e,{lines:1e3}).text,stopRunner:async(e,t)=>{let n=P.find(t=>t.id===e);n&&(n.backend===`rmux`&&n.backendTarget?await T.stop(n.backendTarget,n.pid):await w.stop(n.pid),await C.complete(n.id,{reason:y,code:null,signal:null,stopReason:t},n.sessionId),await Z())}})}}),h(x,{source:g,bindings:[{id:`runners.open`,path:[{key:`r`,label:`runners`,order:67},{key:`r`,label:`open`,detail:`background processes`}],command:{name:_}}]}),x.on(`session_start`,async(e,t)=>{let n=t,r=Y(n);r.recordEvent(`doom_runner.session_started`,{outcome:`started`}),M=n.sessionManager.getSessionId(),k.setSessionId(M),N=f(M),F=n,R=!1,K=void 0,q.clear(),J.clear(),await j.arm(M),I?.(),I=d(ee({bus:x.events,source:g,sessionId:M}),{ptyHost:D,sessionId:()=>M,onError:(e,t)=>void r.recordError(`doom_runner.pty_launch_failed`,e,{"run.id":t})});let i=await C.listAll(M);for(let e of i)e.state===v?q.add(e.id):e.promoted&&J.add(e.id);let s=await a({registry:C,launcher:w,rmuxBackend:T,processControl:A,currentHostPid:process.pid,paths:k}),c=await o({registry:C,launcher:w,rmuxBackend:T,processControl:A,currentHostPid:process.pid,startup:!0});R=!0,await Z(!1),re();let l=[...s.errors,...c.errors];for(let e of l)process.emitWarning(e);let u=s.reclaimed.length+c.reclaimed.length;u>0&&n.hasUI&&n.ui.notify(`Reclaimed ${u} stale runner record(s)`,`warning`),r.recordEvent(`doom_runner.reconciled`,{"runner.reclaimed_count":u,"runner.error_count":l.length,outcome:l.length===0?`completed`:`degraded`})}),x.on(`session_shutdown`,async()=>{if(B)return;B=!0,R=!1,z=!0,clearInterval($),U&&=(clearImmediate(U),void 0),ie();let e=V;if(e)try{await e}catch(e){process.emitWarning(`Could not settle doom-runner refresh during shutdown: ${String(e)}`)}let t=H;t&&await t;try{try{I?.(),I=void 0,await D.disposeAll()}catch(e){process.emitWarning(`Could not dispose doom-runner PTYs: ${String(e)}`)}let e=M?await C.listBySession(M):[];for(let t of e)try{let e=await s(t,w,T);if(!e&&A.isAlive(t.pid)){process.emitWarning(`Could not stop runner ${t.id} during session shutdown`);continue}await C.complete(t.id,{reason:y,code:null,signal:e?`SIGTERM`:null,stopReason:`session ended`},t.sessionId)}catch(e){process.emitWarning(`Could not clean up runner ${t.id}: ${String(e)}`)}}finally{j.dispose(),X.dispose(),F?.hasUI&&F.ui.setStatus(b,void 0),C.close(),await L?.recordEvent(`doom_runner.session_finished`,{outcome:`stopped`}),await L?.shutdown(),L=void 0}})}export{x as runnerExtension};
|
|
3
|
+
//# sourceMappingURL=extension.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { resolveRootSessionId } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport { createProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport { registerDoomFooterContribution } from '@agimon-ai/doompi-ui/footer';\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-ui/leader';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { registerBashTool } from '../../commands/bash/bashTool.ts';\nimport { createRunnerContainer } from '../../container/index.ts';\nimport type { IBashRunService } from '../../services/BashRunService/index.ts';\nimport type { ILauncher } from '../../services/Launcher/index.ts';\nimport type { ILifeline } from '../../services/Lifeline/index.ts';\nimport type { ILogReader } from '../../services/LogReader/index.ts';\nimport type { IProcessControl } from '../../services/ProcessControl/index.ts';\nimport type { IPtyHost } from '../../services/PtyHost/index.ts';\nimport type { IRmuxBackend } from '../../services/RmuxBackend/index.ts';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/index.ts';\nimport type { IRunnerRegistry, RunnerRecord } from '../../services/RunnerRegistry/index.ts';\nimport { registerRunnerCompactionRecovery } from '../../services/runs/compaction.ts';\nimport { cleanupLegacyRunnerStore, reconcileActiveRunners, stopRunnerProcess } from '../../services/runs/reconcile.ts';\nimport { formatRunnerFooterContribution, formatRunnerStatus } from '../../tui/format.ts';\nimport { openRunnerSpace } from '../../tui/runnerSpace.ts';\nimport { getLogTtlMs } from '../../types/config.ts';\nimport { TYPES } from '../../types/index.ts';\nimport { provideRunnerPty } from '../pty/ptyProvider.ts';\n\nconst LEADER_SOURCE = '@agimon-ai/doompi-runner';\n/** After doom-task's `t` (65) and before the core help group (70). */\nconst LEADER_GROUP_ORDER = 67;\nconst COMMAND_NAME = 'runners';\nconst ERR_REQUIRES_INTERACTIVE = '/runners requires interactive mode';\n\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\nconst RUNNER_FINISHED_MESSAGE = 'doom-runner-finished';\nconst COMPLETED_STATE = 'completed';\nconst RMUX_BACKEND = 'rmux';\nconst STOPPED_REASON = 'stopped';\nconst RUNNER_STATUS_KEY = 'doom-runner-runners';\nconst RUNNER_FOOTER_ORDER = 10;\nconst RUNNER_STATUS_POLL_MS = 500;\n\n/**\n * doom-runner: replaces pi's `bash` tool with a supervised one and provides a\n * CLI for anything it leaves running.\n */\nexport function runnerExtension(pi: ExtensionAPI): void {\n const container = createRunnerContainer();\n const registry = container.get<IRunnerRegistry>(TYPES.RunnerRegistry);\n const launcher = container.get<ILauncher>(TYPES.Launcher);\n const rmuxBackend = container.get<IRmuxBackend>(TYPES.RmuxBackend);\n const logReader = container.get<ILogReader>(TYPES.LogReader);\n const ptyHost = container.get<IPtyHost>(TYPES.PtyHost);\n const bashRunService = container.get<IBashRunService>(TYPES.BashRunService);\n const paths = container.get<IRunnerPaths>(TYPES.RunnerPaths);\n const processControl = container.get<IProcessControl>(TYPES.ProcessControl);\n const lifeline = container.get<ILifeline>(TYPES.Lifeline);\n\n let sessionId: string | undefined;\n let rootSessionId: string | undefined;\n let runners: RunnerRecord[] = [];\n let sessionContext: ExtensionContext | undefined;\n let disposePtyProvider: (() => void) | undefined;\n let telemetry: DoomTelemetry | undefined;\n let sessionReady = false;\n let disposed = false;\n let shutdownStarted = false;\n let refreshInFlight: Promise<void> | undefined;\n let historySweepInFlight: Promise<void> | undefined;\n let historySweepTimer: ReturnType<typeof setImmediate> | undefined;\n let refreshQueued = false;\n let queuedReconciliation = false;\n let lastRunnerCount: number | undefined;\n const notifiedRunnerIds = new Set<string>();\n const pendingPromotedRunnerIds = new Set<string>();\n const getTelemetry = (ctx: ExtensionContext): DoomTelemetry => {\n telemetry ??= createDoomTelemetry({\n serviceName: 'doom-runner',\n packageName: '@agimon-ai/doompi-runner',\n cwd: ctx.cwd,\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n return telemetry;\n };\n const footerContribution = registerDoomFooterContribution(pi, {\n source: LEADER_SOURCE,\n id: 'runner-count',\n order: RUNNER_FOOTER_ORDER,\n });\n\n /** Performs one bounded pass over active state and explicitly monitored runners. */\n const refreshNow = async (shouldReconcile: boolean): Promise<void> => {\n const activeSessionId = sessionId;\n if (!activeSessionId || disposed) return;\n\n const active = await registry.list();\n if (disposed) return;\n let visibleActive = active;\n if (shouldReconcile) {\n const reconciled = await reconcileActiveRunners({\n registry,\n launcher,\n rmuxBackend,\n processControl,\n currentHostPid: process.pid,\n startup: false,\n active,\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n if (reconciled.reclaimed.length > 0) {\n const reclaimedIds = new Set(reconciled.reclaimed);\n visibleActive = active.filter((record) => !reclaimedIds.has(record.id));\n }\n }\n if (disposed) return;\n\n runners = visibleActive.filter((record) => record.sessionId === activeSessionId);\n const rootRunners = rootSessionId\n ? visibleActive.filter((record) => (record.rootSessionId ?? record.sessionId) === rootSessionId)\n : [];\n const runnerCount = rootRunners.length;\n if (lastRunnerCount !== runnerCount) {\n lastRunnerCount = runnerCount;\n footerContribution.update(formatRunnerFooterContribution(runnerCount));\n if (sessionContext?.hasUI) sessionContext.ui.setStatus(RUNNER_STATUS_KEY, formatRunnerStatus(runnerCount));\n }\n\n const pendingIds = [...pendingPromotedRunnerIds];\n const monitored = await Promise.all(pendingIds.map((id) => registry.get(id, activeSessionId)));\n if (disposed) return;\n for (const [index, record] of monitored.entries()) {\n const id = pendingIds[index];\n if (!id) continue;\n if (!record || record.sessionId !== activeSessionId) {\n pendingPromotedRunnerIds.delete(id);\n continue;\n }\n if (record.state !== COMPLETED_STATE) continue;\n pendingPromotedRunnerIds.delete(record.id);\n if (notifiedRunnerIds.has(record.id)) continue;\n notifiedRunnerIds.add(record.id);\n const outcome = record.exit?.reason ?? COMPLETED_STATE;\n const code =\n record.exit?.code === null || record.exit?.code === undefined ? '' : `, exit code ${record.exit.code}`;\n const content = [\n `Background runner ${record.name} exited: ${outcome}${code}.`,\n `Runner ID: ${record.id}`,\n `Log: ${record.logPath}`,\n `Inspect: doom-runner logs ${record.id}`,\n ].join('\\n');\n pi.sendMessage(\n { customType: RUNNER_FINISHED_MESSAGE, content, display: true },\n { triggerTurn: true, deliverAs: 'steer' },\n );\n if (sessionContext) {\n void getTelemetry(sessionContext).recordEvent('doom_runner.process_finished', {\n outcome: 'completed',\n 'runner.exit_code': record.exit?.code ?? 0,\n 'runner.backend': record.backend,\n ...(Number.isFinite(Date.parse(record.startedAt))\n ? { duration_ms: Math.max(0, Date.now() - Date.parse(record.startedAt)) }\n : {}),\n });\n }\n }\n };\n\n /** Coalesces timer and event triggers without losing a requested reconciliation pass. */\n const refresh = (shouldReconcile = true): Promise<void> => {\n if (!sessionId || !sessionReady || disposed) return Promise.resolve();\n if (refreshInFlight) {\n refreshQueued = true;\n queuedReconciliation ||= shouldReconcile;\n return refreshInFlight;\n }\n\n const execution = (async () => {\n let reconcileNext = shouldReconcile;\n do {\n refreshQueued = false;\n queuedReconciliation = false;\n await refreshNow(reconcileNext);\n reconcileNext = queuedReconciliation;\n } while (refreshQueued && !disposed);\n })().finally(() => {\n if (refreshInFlight === execution) refreshInFlight = undefined;\n });\n refreshInFlight = execution;\n return execution;\n };\n\n const reportRefreshError = (error: unknown): void => {\n if (disposed) return;\n if (sessionContext) void getTelemetry(sessionContext).recordError('doom_runner.refresh_failed', error);\n process.emitWarning(`Could not refresh doom-runner: ${String(error)}`);\n };\n const scheduleRefresh = (shouldReconcile: boolean): void => {\n void refresh(shouldReconcile).catch(reportRefreshError);\n };\n const scheduleHistorySweep = (): void => {\n if (historySweepTimer || historySweepInFlight || disposed) return;\n historySweepTimer = setImmediate(() => {\n historySweepTimer = undefined;\n if (disposed) return;\n const execution = (async () => {\n const sweep = paths.sweepHistoryAsync\n ? await paths.sweepHistoryAsync(getLogTtlMs())\n : paths.sweepHistory(getLogTtlMs());\n for (const error of sweep.errors) process.emitWarning(error);\n })()\n .catch((error) => process.emitWarning(`Could not sweep doom-runner history: ${String(error)}`))\n .finally(() => {\n if (historySweepInFlight === execution) historySweepInFlight = undefined;\n });\n historySweepInFlight = execution;\n });\n historySweepTimer.unref?.();\n };\n const unsubscribeRegistry = registry.subscribe(() => scheduleRefresh(false));\n const statusPoll = setInterval(() => scheduleRefresh(true), RUNNER_STATUS_POLL_MS);\n statusPoll.unref?.();\n\n registerBashTool(pi, {\n bashRunService,\n getSessionId: () => {\n if (!sessionId) throw new Error('doom-runner requires an active Pi session');\n return sessionId;\n },\n onRunnerStarted: (id) => {\n pendingPromotedRunnerIds.add(id);\n scheduleRefresh(false);\n },\n });\n\n registerRunnerCompactionRecovery(pi, {\n getSessionId: () => sessionId,\n listBySession: (activeSessionId) => registry.listBySession(activeSessionId),\n });\n\n pi.registerCommand(COMMAND_NAME, {\n description: 'Open Runner Space: background processes started by bash',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI) {\n ctx.ui.notify(ERR_REQUIRES_INTERACTIVE, 'error');\n return;\n }\n await refresh();\n await openRunnerSpace(ctx, {\n getRunners: () => runners,\n getPtyRun: (name) => rmuxBackend.get(name) ?? ptyHost.get(name),\n readLog: (logPath) => logReader.read(logPath, { lines: 1_000 }).text,\n stopRunner: async (id, reason) => {\n const record = runners.find((candidate) => candidate.id === id);\n if (!record) return;\n if (record.backend === RMUX_BACKEND && record.backendTarget) {\n await rmuxBackend.stop(record.backendTarget, record.pid);\n } else await launcher.stop(record.pid);\n await registry.complete(\n record.id,\n { reason: STOPPED_REASON, code: null, signal: null, stopReason: reason },\n record.sessionId,\n );\n await refresh();\n },\n });\n },\n });\n\n registerDoomLeaderContribution(pi, {\n source: LEADER_SOURCE,\n bindings: [\n {\n id: 'runners.open',\n path: [\n { key: 'r', label: 'runners', order: LEADER_GROUP_ORDER },\n { key: 'r', label: 'open', detail: 'background processes' },\n ],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n\n pi.on(SESSION_START_EVENT, async (_event, ctx) => {\n const context = ctx as ExtensionContext;\n const activeTelemetry = getTelemetry(context);\n void activeTelemetry.recordEvent('doom_runner.session_started', { outcome: 'started' });\n sessionId = context.sessionManager.getSessionId();\n paths.setSessionId(sessionId);\n rootSessionId = resolveRootSessionId(sessionId);\n sessionContext = context;\n sessionReady = false;\n lastRunnerCount = undefined;\n notifiedRunnerIds.clear();\n pendingPromotedRunnerIds.clear();\n // Awaited before anything can launch, so every runner this session starts\n // finds a lifeline it can connect to rather than a socket that is not\n // listening yet, which would read as an owner that is already gone.\n await lifeline.arm(sessionId);\n\n // This package owns the only pty in the workspace, so it lends one to any\n // extension that needs a command the user can watch and answer.\n disposePtyProvider?.();\n disposePtyProvider = provideRunnerPty(createProtocolRuntime({ bus: pi.events, source: LEADER_SOURCE, sessionId }), {\n ptyHost,\n sessionId: () => sessionId,\n onError: (error, id) =>\n void activeTelemetry.recordError('doom_runner.pty_launch_failed', error, { 'run.id': id }),\n });\n\n const retained = await registry.listAll(sessionId);\n for (const record of retained) {\n if (record.state === COMPLETED_STATE) notifiedRunnerIds.add(record.id);\n else if (record.promoted) pendingPromotedRunnerIds.add(record.id);\n }\n const legacy = await cleanupLegacyRunnerStore({\n registry,\n launcher,\n rmuxBackend,\n processControl,\n currentHostPid: process.pid,\n paths,\n });\n const reconciled = await reconcileActiveRunners({\n registry,\n launcher,\n rmuxBackend,\n processControl,\n currentHostPid: process.pid,\n startup: true,\n });\n sessionReady = true;\n await refresh(false);\n scheduleHistorySweep();\n const errors = [...legacy.errors, ...reconciled.errors];\n for (const error of errors) process.emitWarning(error);\n const reclaimedCount = legacy.reclaimed.length + reconciled.reclaimed.length;\n if (reclaimedCount > 0 && context.hasUI) {\n context.ui.notify(`Reclaimed ${reclaimedCount} stale runner record(s)`, 'warning');\n }\n void activeTelemetry.recordEvent('doom_runner.reconciled', {\n 'runner.reclaimed_count': reclaimedCount,\n 'runner.error_count': errors.length,\n outcome: errors.length === 0 ? 'completed' : 'degraded',\n });\n });\n\n pi.on(SESSION_SHUTDOWN_EVENT, async () => {\n if (shutdownStarted) return;\n shutdownStarted = true;\n sessionReady = false;\n disposed = true;\n clearInterval(statusPoll);\n if (historySweepTimer) {\n clearImmediate(historySweepTimer);\n historySweepTimer = undefined;\n }\n unsubscribeRegistry();\n const activeRefresh = refreshInFlight;\n if (activeRefresh) {\n try {\n await activeRefresh;\n } catch (error) {\n process.emitWarning(`Could not settle doom-runner refresh during shutdown: ${String(error)}`);\n }\n }\n const activeHistorySweep = historySweepInFlight;\n if (activeHistorySweep) await activeHistorySweep;\n try {\n try {\n disposePtyProvider?.();\n disposePtyProvider = undefined;\n await ptyHost.disposeAll();\n } catch (error) {\n process.emitWarning(`Could not dispose doom-runner PTYs: ${String(error)}`);\n }\n const owned = sessionId ? await registry.listBySession(sessionId) : [];\n for (const record of owned) {\n try {\n const stopped = await stopRunnerProcess(record, launcher, rmuxBackend);\n if (!stopped && processControl.isAlive(record.pid)) {\n process.emitWarning(`Could not stop runner ${record.id} during session shutdown`);\n continue;\n }\n await registry.complete(\n record.id,\n {\n reason: STOPPED_REASON,\n code: null,\n signal: stopped ? 'SIGTERM' : null,\n stopReason: 'session ended',\n },\n record.sessionId,\n );\n } catch (error) {\n process.emitWarning(`Could not clean up runner ${record.id}: ${String(error)}`);\n }\n }\n } finally {\n // Last, and in `finally`: closing the lifeline reaps anything the loop\n // above failed to stop, so a partial cleanup still leaves nothing behind.\n lifeline.dispose();\n footerContribution.dispose();\n if (sessionContext?.hasUI) sessionContext.ui.setStatus(RUNNER_STATUS_KEY, undefined);\n registry.close();\n await telemetry?.recordEvent('doom_runner.session_finished', { outcome: 'stopped' });\n await telemetry?.shutdown();\n telemetry = undefined;\n }\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require("@agimon-ai/doompi-extension-contracts/runner-pty");function t(e,t){return`ext:${e}:${t}`.replaceAll(/[^A-Za-z0-9._-]/gu,`-`)}function n(n,r){let i=new Map,a=e=>{let t=i.get(e);t&&(i.delete(e),t.dispose())},o=[n.provide(e.RunnerPtyLaunchProtocol,async(o,s)=>{let c=r.sessionId();if(!c)return{launched:!1,reason:`No runner session is active.`};let l=t(s.source,o.id);if(i.has(l))return{launched:!1,reason:`A run named ${o.id} is already active.`};try{let t=await r.ptyHost.launch({id:l,name:l,command:o.command,cwd:o.cwd,sessionId:c,...o.cols===void 0?{}:{cols:o.cols},...o.rows===void 0?{}:{rows:o.rows}}),s=t.onData(t=>n.notify(e.RunnerPtyData,{id:o.id,chunk:t}));return i.set(l,{run:t,dispose:s}),t.completion().then(t=>n.notify(e.RunnerPtyExit,{id:o.id,...typeof t.code==`number`?{code:t.code}:{},...t.signal?{signal:String(t.signal)}:{}})).catch(()=>n.notify(e.RunnerPtyExit,{id:o.id,code:-1})).finally(()=>a(l)),{launched:!0}}catch(e){return r.onError?.(e,o.id),{launched:!1,reason:e instanceof Error?e.message:String(e)}}}),n.onNotification(e.RunnerPtyInput,({id:e,text:n},r)=>{i.get(t(r.source,e))?.run.write(`${n}\n`)}),n.onNotification(e.RunnerPtyStop,async({id:e},n)=>{let r=t(n.source,e),o=i.get(r);o&&(a(r),await o.run.stop())})],s=!1;return()=>{if(!s){s=!0;for(let e of o)e();for(let e of i.values())e.dispose();i.clear()}}}exports.provideRunnerPty=n;
|
|
2
|
+
//# sourceMappingURL=ptyProvider.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ptyProvider.cjs","names":["RunnerPtyLaunchProtocol","RunnerPtyData","RunnerPtyExit","RunnerPtyInput","RunnerPtyStop"],"sources":["../../../src/adapters/pty/ptyProvider.ts"],"sourcesContent":["/**\n * Lends this package's pseudo terminals to other extensions.\n *\n * doom-runner is the only package that carries `node-pty`, and the only one that\n * knows how to keep a terminal alive for a session and tear it down with one.\n * Rather than have every extension that needs an answerable command grow its own\n * copy of that, they ask over the bus and the terminal stays here.\n *\n * Output is forwarded raw, chunk by chunk, because the caller is usually\n * rendering a tail and needs the partial line a prompt sits on. Reassembling\n * lines is the caller's problem, not the terminal's.\n *\n * AVOID:\n * - Keeping a run past the session that asked for it\n * - Swallowing an exit; a caller waiting on one would hang forever\n */\n\nimport type { ProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport {\n RunnerPtyData,\n RunnerPtyExit,\n RunnerPtyInput,\n RunnerPtyLaunchProtocol,\n RunnerPtyStop,\n} from '@agimon-ai/doompi-extension-contracts/runner-pty';\nimport type { IPtyHost, PtyRun } from '../../services/PtyHost/index.ts';\n\n/** Namespaced so a borrowed terminal is distinguishable in the runner list. */\nconst RUN_NAME_PREFIX = 'ext';\n\n/**\n * Collapses a caller and its run id into one safe path segment.\n *\n * The id becomes a log file name, and package-scoped sources carry a `/`, which\n * `path.join` would turn into a directory nobody created.\n */\nfunction runKey(source: string, id: string): string {\n return `${RUN_NAME_PREFIX}:${source}:${id}`.replaceAll(/[^A-Za-z0-9._-]/gu, '-');\n}\n\nexport interface PtyProviderOptions {\n ptyHost: IPtyHost;\n sessionId: () => string | undefined;\n onError?: (error: unknown, id: string) => void;\n}\n\nexport function provideRunnerPty(runtime: ProtocolRuntime, options: PtyProviderOptions): () => void {\n const runs = new Map<string, { run: PtyRun; dispose: () => void }>();\n\n const release = (id: string): void => {\n const entry = runs.get(id);\n if (!entry) return;\n runs.delete(id);\n entry.dispose();\n };\n\n const disposers = [\n runtime.provide(RunnerPtyLaunchProtocol, async (request, identity) => {\n const sessionId = options.sessionId();\n if (!sessionId) return { launched: false, reason: 'No runner session is active.' };\n // Keyed by the caller too, so two extensions choosing the same id cannot\n // collide or read each other's output.\n const key = runKey(identity.source, request.id);\n if (runs.has(key)) return { launched: false, reason: `A run named ${request.id} is already active.` };\n try {\n const run = await options.ptyHost.launch({\n id: key,\n // The host keys its runs by name, so this is the unique one rather\n // than the caller's label; two callers naming a run `install` would\n // otherwise clobber each other.\n name: key,\n command: request.command,\n cwd: request.cwd,\n sessionId,\n ...(request.cols === undefined ? {} : { cols: request.cols }),\n ...(request.rows === undefined ? {} : { rows: request.rows }),\n });\n const unsubscribe = run.onData((chunk) => runtime.notify(RunnerPtyData, { id: request.id, chunk }));\n runs.set(key, { run, dispose: unsubscribe });\n void run\n .completion()\n .then((result) =>\n runtime.notify(RunnerPtyExit, {\n id: request.id,\n ...(typeof result.code === 'number' ? { code: result.code } : {}),\n ...(result.signal ? { signal: String(result.signal) } : {}),\n }),\n )\n // A caller blocked on the exit would wait forever otherwise, so a\n // failure to observe one is still reported as an exit.\n .catch(() => runtime.notify(RunnerPtyExit, { id: request.id, code: -1 }))\n .finally(() => release(key));\n return { launched: true };\n } catch (error) {\n options.onError?.(error, request.id);\n return { launched: false, reason: error instanceof Error ? error.message : String(error) };\n }\n }),\n runtime.onNotification(RunnerPtyInput, ({ id, text }, identity) => {\n runs.get(runKey(identity.source, id))?.run.write(`${text}\\n`);\n }),\n runtime.onNotification(RunnerPtyStop, async ({ id }, identity) => {\n const key = runKey(identity.source, id);\n const entry = runs.get(key);\n if (!entry) return;\n release(key);\n await entry.run.stop();\n }),\n ];\n\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n for (const dispose of disposers) dispose();\n for (const entry of runs.values()) entry.dispose();\n runs.clear();\n };\n}\n"],"mappings":"kEAoCA,SAAS,EAAO,EAAgB,EAAoB,CAClD,MAAO,OAAsB,EAAO,GAAG,IAAK,WAAW,oBAAqB,GAAG,CACjF,CAQA,SAAgB,EAAiB,EAA0B,EAAyC,CAClG,IAAM,EAAO,IAAI,IAEX,EAAW,GAAqB,CACpC,IAAM,EAAQ,EAAK,IAAI,CAAE,EACpB,IACL,EAAK,OAAO,CAAE,EACd,EAAM,QAAQ,EAChB,EAEM,EAAY,CAChB,EAAQ,QAAQA,EAAAA,wBAAyB,MAAO,EAAS,IAAa,CACpE,IAAM,EAAY,EAAQ,UAAU,EACpC,GAAI,CAAC,EAAW,MAAO,CAAE,SAAU,GAAO,OAAQ,8BAA+B,EAGjF,IAAM,EAAM,EAAO,EAAS,OAAQ,EAAQ,EAAE,EAC9C,GAAI,EAAK,IAAI,CAAG,EAAG,MAAO,CAAE,SAAU,GAAO,OAAQ,eAAe,EAAQ,GAAG,oBAAqB,EACpG,GAAI,CACF,IAAM,EAAM,MAAM,EAAQ,QAAQ,OAAO,CACvC,GAAI,EAIJ,KAAM,EACN,QAAS,EAAQ,QACjB,IAAK,EAAQ,IACb,YACA,GAAI,EAAQ,OAAS,IAAA,GAAY,CAAC,EAAI,CAAE,KAAM,EAAQ,IAAK,EAC3D,GAAI,EAAQ,OAAS,IAAA,GAAY,CAAC,EAAI,CAAE,KAAM,EAAQ,IAAK,CAC7D,CAAC,EACK,EAAc,EAAI,OAAQ,GAAU,EAAQ,OAAOC,EAAAA,cAAe,CAAE,GAAI,EAAQ,GAAI,OAAM,CAAC,CAAC,EAelG,OAdA,EAAK,IAAI,EAAK,CAAE,MAAK,QAAS,CAAY,CAAC,EAC3C,EACG,WAAW,CAAC,CACZ,KAAM,GACL,EAAQ,OAAOC,EAAAA,cAAe,CAC5B,GAAI,EAAQ,GACZ,GAAI,OAAO,EAAO,MAAS,SAAW,CAAE,KAAM,EAAO,IAAK,EAAI,CAAC,EAC/D,GAAI,EAAO,OAAS,CAAE,OAAQ,OAAO,EAAO,MAAM,CAAE,EAAI,CAAC,CAC3D,CAAC,CACH,CAAC,CAGA,UAAY,EAAQ,OAAOA,EAAAA,cAAe,CAAE,GAAI,EAAQ,GAAI,KAAM,EAAG,CAAC,CAAC,CAAC,CACxE,YAAc,EAAQ,CAAG,CAAC,EACtB,CAAE,SAAU,EAAK,CAC1B,OAAS,EAAO,CAEd,OADA,EAAQ,UAAU,EAAO,EAAQ,EAAE,EAC5B,CAAE,SAAU,GAAO,OAAQ,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAAE,CAC3F,CACF,CAAC,EACD,EAAQ,eAAeC,EAAAA,gBAAiB,CAAE,KAAI,QAAQ,IAAa,CACjE,EAAK,IAAI,EAAO,EAAS,OAAQ,CAAE,CAAC,CAAC,EAAE,IAAI,MAAM,GAAG,EAAK,GAAG,CAC9D,CAAC,EACD,EAAQ,eAAeC,EAAAA,cAAe,MAAO,CAAE,MAAM,IAAa,CAChE,IAAM,EAAM,EAAO,EAAS,OAAQ,CAAE,EAChC,EAAQ,EAAK,IAAI,CAAG,EACrB,IACL,EAAQ,CAAG,EACX,MAAM,EAAM,IAAI,KAAK,EACvB,CAAC,CACH,EAEI,EAAW,GACf,UAAa,CACP,MACJ,GAAW,GACX,IAAK,IAAM,KAAW,EAAW,EAAQ,EACzC,IAAK,IAAM,KAAS,EAAK,OAAO,EAAG,EAAM,QAAQ,EACjD,EAAK,MAAM,CAHA,CAIb,CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IPtyHost } from "../../services/PtyHost/types.cjs";
|
|
2
|
+
import "../../services/PtyHost/index.cjs";
|
|
3
|
+
import { ProtocolRuntime } from "@agimon-ai/doompi-extension-contracts/protocol";
|
|
4
|
+
//#region src/adapters/pty/ptyProvider.d.ts
|
|
5
|
+
interface PtyProviderOptions {
|
|
6
|
+
ptyHost: IPtyHost;
|
|
7
|
+
sessionId: () => string | undefined;
|
|
8
|
+
onError?: (error: unknown, id: string) => void;
|
|
9
|
+
}
|
|
10
|
+
declare function provideRunnerPty(runtime: ProtocolRuntime, options: PtyProviderOptions): () => void;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { PtyProviderOptions, provideRunnerPty };
|
|
13
|
+
//# sourceMappingURL=ptyProvider.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ptyProvider.d.cts","names":[],"sources":["../../../src/adapters/pty/ptyProvider.ts"],"mappings":";;;;UAwCiB;EACf,SAAS;EACT;EACA,WAAW,gBAAgB;;iBAGb,iBAAiB,SAAS,iBAAiB,SAAS"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IPtyHost } from "../../services/PtyHost/types.mjs";
|
|
2
|
+
import "../../services/PtyHost/index.mjs";
|
|
3
|
+
import { ProtocolRuntime } from "@agimon-ai/doompi-extension-contracts/protocol";
|
|
4
|
+
//#region src/adapters/pty/ptyProvider.d.ts
|
|
5
|
+
interface PtyProviderOptions {
|
|
6
|
+
ptyHost: IPtyHost;
|
|
7
|
+
sessionId: () => string | undefined;
|
|
8
|
+
onError?: (error: unknown, id: string) => void;
|
|
9
|
+
}
|
|
10
|
+
declare function provideRunnerPty(runtime: ProtocolRuntime, options: PtyProviderOptions): () => void;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { PtyProviderOptions, provideRunnerPty };
|
|
13
|
+
//# sourceMappingURL=ptyProvider.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ptyProvider.d.mts","names":[],"sources":["../../../src/adapters/pty/ptyProvider.ts"],"mappings":";;;;UAwCiB;EACf,SAAS;EACT;EACA,WAAW,gBAAgB;;iBAGb,iBAAiB,SAAS,iBAAiB,SAAS"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{RunnerPtyData as e,RunnerPtyExit as t,RunnerPtyInput as n,RunnerPtyLaunchProtocol as r,RunnerPtyStop as i}from"@agimon-ai/doompi-extension-contracts/runner-pty";function a(e,t){return`ext:${e}:${t}`.replaceAll(/[^A-Za-z0-9._-]/gu,`-`)}function o(o,s){let c=new Map,l=e=>{let t=c.get(e);t&&(c.delete(e),t.dispose())},u=[o.provide(r,async(n,r)=>{let i=s.sessionId();if(!i)return{launched:!1,reason:`No runner session is active.`};let u=a(r.source,n.id);if(c.has(u))return{launched:!1,reason:`A run named ${n.id} is already active.`};try{let r=await s.ptyHost.launch({id:u,name:u,command:n.command,cwd:n.cwd,sessionId:i,...n.cols===void 0?{}:{cols:n.cols},...n.rows===void 0?{}:{rows:n.rows}}),a=r.onData(t=>o.notify(e,{id:n.id,chunk:t}));return c.set(u,{run:r,dispose:a}),r.completion().then(e=>o.notify(t,{id:n.id,...typeof e.code==`number`?{code:e.code}:{},...e.signal?{signal:String(e.signal)}:{}})).catch(()=>o.notify(t,{id:n.id,code:-1})).finally(()=>l(u)),{launched:!0}}catch(e){return s.onError?.(e,n.id),{launched:!1,reason:e instanceof Error?e.message:String(e)}}}),o.onNotification(n,({id:e,text:t},n)=>{c.get(a(n.source,e))?.run.write(`${t}\n`)}),o.onNotification(i,async({id:e},t)=>{let n=a(t.source,e),r=c.get(n);r&&(l(n),await r.run.stop())})],d=!1;return()=>{if(!d){d=!0;for(let e of u)e();for(let e of c.values())e.dispose();c.clear()}}}export{o as provideRunnerPty};
|
|
2
|
+
//# sourceMappingURL=ptyProvider.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ptyProvider.mjs","names":[],"sources":["../../../src/adapters/pty/ptyProvider.ts"],"sourcesContent":["/**\n * Lends this package's pseudo terminals to other extensions.\n *\n * doom-runner is the only package that carries `node-pty`, and the only one that\n * knows how to keep a terminal alive for a session and tear it down with one.\n * Rather than have every extension that needs an answerable command grow its own\n * copy of that, they ask over the bus and the terminal stays here.\n *\n * Output is forwarded raw, chunk by chunk, because the caller is usually\n * rendering a tail and needs the partial line a prompt sits on. Reassembling\n * lines is the caller's problem, not the terminal's.\n *\n * AVOID:\n * - Keeping a run past the session that asked for it\n * - Swallowing an exit; a caller waiting on one would hang forever\n */\n\nimport type { ProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport {\n RunnerPtyData,\n RunnerPtyExit,\n RunnerPtyInput,\n RunnerPtyLaunchProtocol,\n RunnerPtyStop,\n} from '@agimon-ai/doompi-extension-contracts/runner-pty';\nimport type { IPtyHost, PtyRun } from '../../services/PtyHost/index.ts';\n\n/** Namespaced so a borrowed terminal is distinguishable in the runner list. */\nconst RUN_NAME_PREFIX = 'ext';\n\n/**\n * Collapses a caller and its run id into one safe path segment.\n *\n * The id becomes a log file name, and package-scoped sources carry a `/`, which\n * `path.join` would turn into a directory nobody created.\n */\nfunction runKey(source: string, id: string): string {\n return `${RUN_NAME_PREFIX}:${source}:${id}`.replaceAll(/[^A-Za-z0-9._-]/gu, '-');\n}\n\nexport interface PtyProviderOptions {\n ptyHost: IPtyHost;\n sessionId: () => string | undefined;\n onError?: (error: unknown, id: string) => void;\n}\n\nexport function provideRunnerPty(runtime: ProtocolRuntime, options: PtyProviderOptions): () => void {\n const runs = new Map<string, { run: PtyRun; dispose: () => void }>();\n\n const release = (id: string): void => {\n const entry = runs.get(id);\n if (!entry) return;\n runs.delete(id);\n entry.dispose();\n };\n\n const disposers = [\n runtime.provide(RunnerPtyLaunchProtocol, async (request, identity) => {\n const sessionId = options.sessionId();\n if (!sessionId) return { launched: false, reason: 'No runner session is active.' };\n // Keyed by the caller too, so two extensions choosing the same id cannot\n // collide or read each other's output.\n const key = runKey(identity.source, request.id);\n if (runs.has(key)) return { launched: false, reason: `A run named ${request.id} is already active.` };\n try {\n const run = await options.ptyHost.launch({\n id: key,\n // The host keys its runs by name, so this is the unique one rather\n // than the caller's label; two callers naming a run `install` would\n // otherwise clobber each other.\n name: key,\n command: request.command,\n cwd: request.cwd,\n sessionId,\n ...(request.cols === undefined ? {} : { cols: request.cols }),\n ...(request.rows === undefined ? {} : { rows: request.rows }),\n });\n const unsubscribe = run.onData((chunk) => runtime.notify(RunnerPtyData, { id: request.id, chunk }));\n runs.set(key, { run, dispose: unsubscribe });\n void run\n .completion()\n .then((result) =>\n runtime.notify(RunnerPtyExit, {\n id: request.id,\n ...(typeof result.code === 'number' ? { code: result.code } : {}),\n ...(result.signal ? { signal: String(result.signal) } : {}),\n }),\n )\n // A caller blocked on the exit would wait forever otherwise, so a\n // failure to observe one is still reported as an exit.\n .catch(() => runtime.notify(RunnerPtyExit, { id: request.id, code: -1 }))\n .finally(() => release(key));\n return { launched: true };\n } catch (error) {\n options.onError?.(error, request.id);\n return { launched: false, reason: error instanceof Error ? error.message : String(error) };\n }\n }),\n runtime.onNotification(RunnerPtyInput, ({ id, text }, identity) => {\n runs.get(runKey(identity.source, id))?.run.write(`${text}\\n`);\n }),\n runtime.onNotification(RunnerPtyStop, async ({ id }, identity) => {\n const key = runKey(identity.source, id);\n const entry = runs.get(key);\n if (!entry) return;\n release(key);\n await entry.run.stop();\n }),\n ];\n\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n for (const dispose of disposers) dispose();\n for (const entry of runs.values()) entry.dispose();\n runs.clear();\n };\n}\n"],"mappings":"wKAoCA,SAAS,EAAO,EAAgB,EAAoB,CAClD,MAAO,OAAsB,EAAO,GAAG,IAAK,WAAW,oBAAqB,GAAG,CACjF,CAQA,SAAgB,EAAiB,EAA0B,EAAyC,CAClG,IAAM,EAAO,IAAI,IAEX,EAAW,GAAqB,CACpC,IAAM,EAAQ,EAAK,IAAI,CAAE,EACpB,IACL,EAAK,OAAO,CAAE,EACd,EAAM,QAAQ,EAChB,EAEM,EAAY,CAChB,EAAQ,QAAQ,EAAyB,MAAO,EAAS,IAAa,CACpE,IAAM,EAAY,EAAQ,UAAU,EACpC,GAAI,CAAC,EAAW,MAAO,CAAE,SAAU,GAAO,OAAQ,8BAA+B,EAGjF,IAAM,EAAM,EAAO,EAAS,OAAQ,EAAQ,EAAE,EAC9C,GAAI,EAAK,IAAI,CAAG,EAAG,MAAO,CAAE,SAAU,GAAO,OAAQ,eAAe,EAAQ,GAAG,oBAAqB,EACpG,GAAI,CACF,IAAM,EAAM,MAAM,EAAQ,QAAQ,OAAO,CACvC,GAAI,EAIJ,KAAM,EACN,QAAS,EAAQ,QACjB,IAAK,EAAQ,IACb,YACA,GAAI,EAAQ,OAAS,IAAA,GAAY,CAAC,EAAI,CAAE,KAAM,EAAQ,IAAK,EAC3D,GAAI,EAAQ,OAAS,IAAA,GAAY,CAAC,EAAI,CAAE,KAAM,EAAQ,IAAK,CAC7D,CAAC,EACK,EAAc,EAAI,OAAQ,GAAU,EAAQ,OAAO,EAAe,CAAE,GAAI,EAAQ,GAAI,OAAM,CAAC,CAAC,EAelG,OAdA,EAAK,IAAI,EAAK,CAAE,MAAK,QAAS,CAAY,CAAC,EAC3C,EACG,WAAW,CAAC,CACZ,KAAM,GACL,EAAQ,OAAO,EAAe,CAC5B,GAAI,EAAQ,GACZ,GAAI,OAAO,EAAO,MAAS,SAAW,CAAE,KAAM,EAAO,IAAK,EAAI,CAAC,EAC/D,GAAI,EAAO,OAAS,CAAE,OAAQ,OAAO,EAAO,MAAM,CAAE,EAAI,CAAC,CAC3D,CAAC,CACH,CAAC,CAGA,UAAY,EAAQ,OAAO,EAAe,CAAE,GAAI,EAAQ,GAAI,KAAM,EAAG,CAAC,CAAC,CAAC,CACxE,YAAc,EAAQ,CAAG,CAAC,EACtB,CAAE,SAAU,EAAK,CAC1B,OAAS,EAAO,CAEd,OADA,EAAQ,UAAU,EAAO,EAAQ,EAAE,EAC5B,CAAE,SAAU,GAAO,OAAQ,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAAE,CAC3F,CACF,CAAC,EACD,EAAQ,eAAe,GAAiB,CAAE,KAAI,QAAQ,IAAa,CACjE,EAAK,IAAI,EAAO,EAAS,OAAQ,CAAE,CAAC,CAAC,EAAE,IAAI,MAAM,GAAG,EAAK,GAAG,CAC9D,CAAC,EACD,EAAQ,eAAe,EAAe,MAAO,CAAE,MAAM,IAAa,CAChE,IAAM,EAAM,EAAO,EAAS,OAAQ,CAAE,EAChC,EAAQ,EAAK,IAAI,CAAG,EACrB,IACL,EAAQ,CAAG,EACX,MAAM,EAAM,IAAI,KAAK,EACvB,CAAC,CACH,EAEI,EAAW,GACf,UAAa,CACP,MACJ,GAAW,GACX,IAAK,IAAM,KAAW,EAAW,EAAQ,EACzC,IAAK,IAAM,KAAS,EAAK,OAAO,EAAG,EAAM,QAAQ,EACjD,EAAK,MAAM,CAHA,CAIb,CACF"}
|
package/dist/bin/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../types.cjs"),t=require("../container/index.cjs"),n=require("../cli/cliApp.cjs");let r=require("node:url"),i=require("@agimon-ai/doompi-telemetry");async function a(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(t));return Buffer.concat(e).toString(`utf8`)}async function o(r=process.argv.slice(2)){let o=Date.now(),s=(0,i.createDoomTelemetry)({serviceName:`doom-runner-cli`,packageName:`@agimon-ai/doompi-runner`,env:process.env,enableLogs:!0,enableTraces:!0}),c=t.createRunnerContainer(),l=c.get(e.TYPES.RunnerRegistry),u=0,d=0;try{let t=await n.runCli(r,{registry:l,launcher:c.get(e.TYPES.Launcher),rmuxBackend:c.get(e.TYPES.RmuxBackend),logReader:c.get(e.TYPES.LogReader),env:process.env,stdout:e=>{let t=e.endsWith(`
|
|
2
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../types/index.cjs"),t=require("../container/index.cjs"),n=require("../commands/cli/cliApp.cjs");let r=require("node:url"),i=require("@agimon-ai/doompi-telemetry");async function a(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(t));return Buffer.concat(e).toString(`utf8`)}async function o(r=process.argv.slice(2)){let o=Date.now(),s=(0,i.createDoomTelemetry)({serviceName:`doom-runner-cli`,packageName:`@agimon-ai/doompi-runner`,env:process.env,enableLogs:!0,enableTraces:!0}),c=t.createRunnerContainer(),l=c.get(e.TYPES.RunnerRegistry),u=0,d=0;try{let t=await n.runCli(r,{registry:l,launcher:c.get(e.TYPES.Launcher),rmuxBackend:c.get(e.TYPES.RmuxBackend),logReader:c.get(e.TYPES.LogReader),env:process.env,stdout:e=>{let t=e.endsWith(`
|
|
3
3
|
`)?e:`${e}\n`;u+=Buffer.byteLength(t,`utf8`),process.stdout.write(t)},stderr:e=>{let t=e.endsWith(`
|
|
4
4
|
`)?e:`${e}\n`;d+=Buffer.byteLength(t,`utf8`),process.stderr.write(t)},readStdin:a});return await s.recordEvent(`doom_runner.cli_finished`,{outcome:t===0?`completed`:`failed`,"runner.exit_code":t,duration_ms:Date.now()-o,"runner.argument_count":r.length,"runner.stdout_bytes":u,"runner.stderr_bytes":d}),t}catch(e){throw await s.recordError(`doom_runner.cli_failed`,e,{duration_ms:Date.now()-o,"runner.argument_count":r.length,"runner.stdout_bytes":u,"runner.stderr_bytes":d}),e}finally{l.close(),await s.shutdown()}}process.argv[1]&&require("url").pathToFileURL(__filename).href===(0,r.pathToFileURL)(process.argv[1]).href&&o().then(e=>{process.exitCode=e},e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1}),exports.main=o;
|
|
5
5
|
//# sourceMappingURL=cli.cjs.map
|