@agimon-ai/doompi-runner 0.0.1-alpha.94 → 0.0.1-alpha.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apiContracts.cjs +8 -0
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.d.mts.map +1 -1
- package/dist/apiContracts.mjs +8 -0
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/{bashRunService-DVCoTO2o.mjs → bashRunService-74kDU2Dl.mjs} +21 -7
- package/dist/bashRunService-74kDU2Dl.mjs.map +1 -0
- package/dist/{bashRunService-DbUkhN8y.cjs → bashRunService-CbzHiXkh.cjs} +21 -7
- package/dist/bashRunService-CbzHiXkh.cjs.map +1 -0
- package/dist/bashRunService.cjs +1 -1
- package/dist/bashRunService.d.mts +3 -3
- package/dist/bashRunService.d.mts.map +1 -1
- package/dist/bashRunService.mjs +1 -1
- package/dist/bin/cli.cjs +2 -2
- package/dist/bin/cli.mjs +2 -2
- package/dist/bin/runnerHost.cjs +13 -2
- package/dist/bin/runnerHost.cjs.map +1 -1
- package/dist/bin/runnerHost.mjs +13 -2
- package/dist/bin/runnerHost.mjs.map +1 -1
- package/dist/{cliApp-bWkTg1Q-.cjs → cliApp-CP2WKUG_.cjs} +24 -8
- package/dist/cliApp-CP2WKUG_.cjs.map +1 -0
- package/dist/{cliApp-4T2TCpwp.mjs → cliApp-dVY9mnBz.mjs} +24 -8
- package/dist/cliApp-dVY9mnBz.mjs.map +1 -0
- package/dist/cliApp.cjs +1 -1
- package/dist/cliApp.d.mts +3 -3
- package/dist/cliApp.mjs +1 -1
- package/dist/compaction.d.mts +1 -1
- package/dist/extensions/mcp.cjs.map +1 -1
- package/dist/extensions/mcp.mjs.map +1 -1
- package/dist/extensions/pi.cjs +19 -6
- package/dist/extensions/pi.cjs.map +1 -1
- package/dist/extensions/pi.mjs +19 -6
- package/dist/extensions/pi.mjs.map +1 -1
- package/dist/extensions/server.cjs +17 -6
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.mjs +17 -6
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/{headless-BDvWJKzm.mjs → headless-CsjrWGKk.mjs} +10 -3
- package/dist/{headless-BDvWJKzm.mjs.map → headless-CsjrWGKk.mjs.map} +1 -1
- package/dist/{headless-Ch9darGJ.cjs → headless-D2UKFWVg.cjs} +10 -3
- package/dist/{headless-Ch9darGJ.cjs.map → headless-D2UKFWVg.cjs.map} +1 -1
- package/dist/{index-BWxvlYO8.d.mts → index-BCa_jNXG.d.mts} +5 -5
- package/dist/{index-BWxvlYO8.d.mts.map → index-BCa_jNXG.d.mts.map} +1 -1
- package/dist/{index-IRH2NxgG.d.mts → index-BjSmZc3m.d.mts} +2 -2
- package/dist/index-BjSmZc3m.d.mts.map +1 -0
- package/dist/{index-C5LvdST6.d.mts → index-DBz0DxnL.d.mts} +3 -2
- package/dist/{index-C5LvdST6.d.mts.map → index-DBz0DxnL.d.mts.map} +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +2 -2
- package/dist/{launcher-CkTZm7KT.d.mts → launcher-BG5t9nvG.d.mts} +2 -2
- package/dist/{launcher-CkTZm7KT.d.mts.map → launcher-BG5t9nvG.d.mts.map} +1 -1
- package/dist/launcher.d.mts +2 -2
- package/dist/{ptyHost-C3geHUQx.d.mts → ptyHost-DWWtRrGn.d.mts} +2 -2
- package/dist/{ptyHost-C3geHUQx.d.mts.map → ptyHost-DWWtRrGn.d.mts.map} +1 -1
- package/dist/ptyHost.d.mts +1 -1
- package/dist/{reconcile-Cv_M9ctf.mjs → reconcile-DgBwJoP6.mjs} +18 -11
- package/dist/reconcile-DgBwJoP6.mjs.map +1 -0
- package/dist/{reconcile-B109E0xL.cjs → reconcile-y5CcOmWz.cjs} +18 -11
- package/dist/reconcile-y5CcOmWz.cjs.map +1 -0
- package/dist/reconcile.cjs +1 -1
- package/dist/reconcile.d.mts +7 -4
- package/dist/reconcile.d.mts.map +1 -1
- package/dist/reconcile.mjs +1 -1
- package/dist/responseEnvelope.d.mts +1 -1
- package/dist/{rmuxBackend-DslAqB7-.d.mts → rmuxBackend-CaEWooWn.d.mts} +4 -4
- package/dist/{rmuxBackend-DslAqB7-.d.mts.map → rmuxBackend-CaEWooWn.d.mts.map} +1 -1
- package/dist/rmuxBackend.d.mts +2 -2
- package/dist/{runnerDependencies-Dq8oIvGv.cjs → runnerDependencies-CKWU0Mxa.cjs} +3 -3
- package/dist/{runnerDependencies-Dq8oIvGv.cjs.map → runnerDependencies-CKWU0Mxa.cjs.map} +1 -1
- package/dist/{runnerDependencies-CsizryxY.mjs → runnerDependencies-CTbinc_6.mjs} +3 -3
- package/dist/{runnerDependencies-CsizryxY.mjs.map → runnerDependencies-CTbinc_6.mjs.map} +1 -1
- package/dist/runnerDependencies.cjs +1 -1
- package/dist/runnerDependencies.d.mts +1 -1
- package/dist/runnerDependencies.mjs +1 -1
- package/dist/runnerFormat.d.mts +1 -1
- package/dist/runnerNamer.d.mts +1 -1
- package/dist/{runnerRegistry-CpIH63zQ.mjs → runnerRegistry-BouzlG6h.mjs} +16 -1
- package/dist/runnerRegistry-BouzlG6h.mjs.map +1 -0
- package/dist/{runnerRegistry-DcN71T16.cjs → runnerRegistry-BvWqBcaM.cjs} +16 -1
- package/dist/runnerRegistry-BvWqBcaM.cjs.map +1 -0
- package/dist/{runnerRegistry-Cy6rjbel.d.mts → runnerRegistry-CtJiD02R.d.mts} +13 -2
- package/dist/runnerRegistry-CtJiD02R.d.mts.map +1 -0
- package/dist/runnerRegistry.cjs +1 -1
- package/dist/runnerRegistry.d.mts +2 -2
- package/dist/runnerRegistry.mjs +1 -1
- package/dist/{runnerRuntime-DHgXAtWs.cjs → runnerRuntime-Cm9-GYrp.cjs} +1 -8
- package/dist/{runnerRuntime-C_eT68L4.mjs.map → runnerRuntime-Cm9-GYrp.cjs.map} +1 -1
- package/dist/{runnerRuntime-C_eT68L4.mjs → runnerRuntime-fNMw1XFo.mjs} +2 -3
- package/dist/{runnerRuntime-DHgXAtWs.cjs.map → runnerRuntime-fNMw1XFo.mjs.map} +1 -1
- package/dist/runnerSpace.d.mts +2 -2
- package/dist/services/runnerSupervisor/index.cjs +4 -1
- package/dist/services/runnerSupervisor/index.cjs.map +1 -1
- package/dist/services/runnerSupervisor/index.d.mts +2 -4
- package/dist/services/runnerSupervisor/index.d.mts.map +1 -1
- package/dist/services/runnerSupervisor/index.mjs +4 -1
- package/dist/services/runnerSupervisor/index.mjs.map +1 -1
- package/dist/{spawner-Cv8jkawW.d.mts → spawner-CdrN_HNk.d.mts} +5 -1
- package/dist/spawner-CdrN_HNk.d.mts.map +1 -0
- package/dist/spawner.d.mts +1 -1
- package/dist/{type-U7Y6NN2B.d.mts → type-WwYXtEDs.d.mts} +7 -7
- package/dist/{type-U7Y6NN2B.d.mts.map → type-WwYXtEDs.d.mts.map} +1 -1
- package/dist/types.d.mts +1 -1
- package/package.json +16 -16
- package/dist/bashRunService-DVCoTO2o.mjs.map +0 -1
- package/dist/bashRunService-DbUkhN8y.cjs.map +0 -1
- package/dist/cliApp-4T2TCpwp.mjs.map +0 -1
- package/dist/cliApp-bWkTg1Q-.cjs.map +0 -1
- package/dist/index-IRH2NxgG.d.mts.map +0 -1
- package/dist/reconcile-B109E0xL.cjs.map +0 -1
- package/dist/reconcile-Cv_M9ctf.mjs.map +0 -1
- package/dist/runnerRegistry-CpIH63zQ.mjs.map +0 -1
- package/dist/runnerRegistry-Cy6rjbel.d.mts.map +0 -1
- package/dist/runnerRegistry-DcN71T16.cjs.map +0 -1
- package/dist/spawner-Cv8jkawW.d.mts.map +0 -1
package/dist/apiContracts.cjs
CHANGED
|
@@ -38,6 +38,14 @@ const Run = typebox.Type.Object({
|
|
|
38
38
|
]),
|
|
39
39
|
code: typebox.Type.Union([N, typebox.Type.Null()]),
|
|
40
40
|
signal: typebox.Type.Union([S, typebox.Type.Null()]),
|
|
41
|
+
terminationReason: O(literals([
|
|
42
|
+
"timeout",
|
|
43
|
+
"user_stop",
|
|
44
|
+
"request_cancelled",
|
|
45
|
+
"parent_session_cleanup",
|
|
46
|
+
"owner_lost",
|
|
47
|
+
"external_signal"
|
|
48
|
+
])),
|
|
41
49
|
stopReason: O(S),
|
|
42
50
|
finishedAt: S
|
|
43
51
|
}))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiContracts.cjs","names":["Type","defineApiContract","jsonApiResponses"],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses } from '@agimon-ai/doompi-core/apiContracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst literals = <T extends string>(values: T[]) => Type.Union(values.map((value) => Type.Literal(value)));\nconst Run = Type.Object({\n id: S,\n name: S,\n pid: N,\n command: S,\n cwd: S,\n interactive: B,\n backend: literals(['rmux', 'tmux', 'native']),\n state: literals(['running', 'completed']),\n promoted: B,\n startedAt: S,\n logPath: S,\n exit: O(\n Type.Object({\n reason: literals(['completed', 'failed', 'signaled', 'stopped', 'timed_out', 'launcher_error', 'backend_lost']),\n code: Type.Union([N, Type.Null()]),\n signal: Type.Union([S, Type.Null()]),\n stopReason: O(S),\n finishedAt: S,\n }),\n ),\n});\nexport const RunnerPayloadSchema = Type.Object({ runs: Type.Array(Run) });\nexport const RunnerLogSchema = Type.Object({\n runId: S,\n running: B,\n text: S,\n lineCount: N,\n totalLines: N,\n fileSize: N,\n completeBytes: N,\n path: S,\n exists: B,\n lineNumbers: O(Type.Array(N)),\n});\nexport const RunnerAppendSchema = Type.Object({ lines: Type.Array(S), offset: O(N), ended: O(B) });\nexport const RunnerScreenSchema = Type.Object({ chunk: S, offset: O(N), ended: O(B) });\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: [\n {\n id: 'runner.log',\n scope: 'session',\n basePath: 'runner',\n path: '/runners/{runId}/log',\n method: 'GET',\n authentication: 'owner',\n description: 'Read a runner log tail or literal substring search.',\n parameters: ['lines', 'grep', 'ignoreCase', 'contextLines'].map((name) => ({\n name,\n in: 'query',\n required: false,\n schema: S,\n })),\n responses: jsonApiResponses(RunnerLogSchema),\n },\n ...(['log', 'screen'] as const).map((kind) => ({\n id: `runner.${kind}.stream`,\n scope: 'session' as const,\n basePath: 'runner',\n path: `/runners/{runId}/${kind}/stream`,\n method: 'GET' as const,\n authentication: 'owner' as const,\n description: `Follow runner ${kind}. SSE data is JSON; ping events carry empty data.`,\n parameters: [{ name: 'from', in: 'query' as const, required: false, schema: N }],\n responses: {\n '200': {\n description: 'Event stream.',\n contentType: 'text/event-stream',\n schema: S,\n events: {\n [kind === 'log' ? 'append' : 'screen']: kind === 'log' ? RunnerAppendSchema : RunnerScreenSchema,\n ping: Type.Literal(''),\n },\n },\n '404': { description: 'Runner unavailable.', schema: Type.Object({ error: S }) },\n },\n })),\n {\n id: 'runner.input',\n scope: 'session',\n basePath: 'runner',\n path: '/runners/{runId}/screen/input',\n method: 'POST',\n authentication: 'owner',\n description: 'Send text to an interactive runner.',\n body: {\n required: true,\n contentType: 'application/json',\n schema: Type.Object({ text: Type.String({ minLength: 1 }) }),\n },\n responses: jsonApiResponses(Type.Object({ delivered: Type.Literal(true) })),\n },\n ],\n sockets: (['global', 'workspace'] as const).map((scope) => ({\n id: 'runner.channel',\n scope,\n service: 'doompi.hub.v1',\n member: 'runner_runs',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'Runner channel payload in the hub frame payload property.',\n input: RunnerPayloadSchema,\n })),\n});\nexport default apiContracts;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"apiContracts.cjs","names":["Type","defineApiContract","jsonApiResponses"],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses } from '@agimon-ai/doompi-core/apiContracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst literals = <T extends string>(values: T[]) => Type.Union(values.map((value) => Type.Literal(value)));\nconst Run = Type.Object({\n id: S,\n name: S,\n pid: N,\n command: S,\n cwd: S,\n interactive: B,\n backend: literals(['rmux', 'tmux', 'native']),\n state: literals(['running', 'completed']),\n promoted: B,\n startedAt: S,\n logPath: S,\n exit: O(\n Type.Object({\n reason: literals(['completed', 'failed', 'signaled', 'stopped', 'timed_out', 'launcher_error', 'backend_lost']),\n code: Type.Union([N, Type.Null()]),\n signal: Type.Union([S, Type.Null()]),\n terminationReason: O(\n literals([\n 'timeout',\n 'user_stop',\n 'request_cancelled',\n 'parent_session_cleanup',\n 'owner_lost',\n 'external_signal',\n ]),\n ),\n stopReason: O(S),\n finishedAt: S,\n }),\n ),\n});\nexport const RunnerPayloadSchema = Type.Object({ runs: Type.Array(Run) });\nexport const RunnerLogSchema = Type.Object({\n runId: S,\n running: B,\n text: S,\n lineCount: N,\n totalLines: N,\n fileSize: N,\n completeBytes: N,\n path: S,\n exists: B,\n lineNumbers: O(Type.Array(N)),\n});\nexport const RunnerAppendSchema = Type.Object({ lines: Type.Array(S), offset: O(N), ended: O(B) });\nexport const RunnerScreenSchema = Type.Object({ chunk: S, offset: O(N), ended: O(B) });\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: [\n {\n id: 'runner.log',\n scope: 'session',\n basePath: 'runner',\n path: '/runners/{runId}/log',\n method: 'GET',\n authentication: 'owner',\n description: 'Read a runner log tail or literal substring search.',\n parameters: ['lines', 'grep', 'ignoreCase', 'contextLines'].map((name) => ({\n name,\n in: 'query',\n required: false,\n schema: S,\n })),\n responses: jsonApiResponses(RunnerLogSchema),\n },\n ...(['log', 'screen'] as const).map((kind) => ({\n id: `runner.${kind}.stream`,\n scope: 'session' as const,\n basePath: 'runner',\n path: `/runners/{runId}/${kind}/stream`,\n method: 'GET' as const,\n authentication: 'owner' as const,\n description: `Follow runner ${kind}. SSE data is JSON; ping events carry empty data.`,\n parameters: [{ name: 'from', in: 'query' as const, required: false, schema: N }],\n responses: {\n '200': {\n description: 'Event stream.',\n contentType: 'text/event-stream',\n schema: S,\n events: {\n [kind === 'log' ? 'append' : 'screen']: kind === 'log' ? RunnerAppendSchema : RunnerScreenSchema,\n ping: Type.Literal(''),\n },\n },\n '404': { description: 'Runner unavailable.', schema: Type.Object({ error: S }) },\n },\n })),\n {\n id: 'runner.input',\n scope: 'session',\n basePath: 'runner',\n path: '/runners/{runId}/screen/input',\n method: 'POST',\n authentication: 'owner',\n description: 'Send text to an interactive runner.',\n body: {\n required: true,\n contentType: 'application/json',\n schema: Type.Object({ text: Type.String({ minLength: 1 }) }),\n },\n responses: jsonApiResponses(Type.Object({ delivered: Type.Literal(true) })),\n },\n ],\n sockets: (['global', 'workspace'] as const).map((scope) => ({\n id: 'runner.channel',\n scope,\n service: 'doompi.hub.v1',\n member: 'runner_runs',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'Runner channel payload in the hub frame payload property.',\n input: RunnerPayloadSchema,\n })),\n});\nexport default apiContracts;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../src/schemas/apiContracts.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../src/schemas/apiContracts.ts"],"mappings":";;qBAuDa"}
|
package/dist/apiContracts.mjs
CHANGED
|
@@ -34,6 +34,14 @@ const Run = Type.Object({
|
|
|
34
34
|
]),
|
|
35
35
|
code: Type.Union([N, Type.Null()]),
|
|
36
36
|
signal: Type.Union([S, Type.Null()]),
|
|
37
|
+
terminationReason: O(literals([
|
|
38
|
+
"timeout",
|
|
39
|
+
"user_stop",
|
|
40
|
+
"request_cancelled",
|
|
41
|
+
"parent_session_cleanup",
|
|
42
|
+
"owner_lost",
|
|
43
|
+
"external_signal"
|
|
44
|
+
])),
|
|
37
45
|
stopReason: O(S),
|
|
38
46
|
finishedAt: S
|
|
39
47
|
}))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiContracts.mjs","names":[],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses } from '@agimon-ai/doompi-core/apiContracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst literals = <T extends string>(values: T[]) => Type.Union(values.map((value) => Type.Literal(value)));\nconst Run = Type.Object({\n id: S,\n name: S,\n pid: N,\n command: S,\n cwd: S,\n interactive: B,\n backend: literals(['rmux', 'tmux', 'native']),\n state: literals(['running', 'completed']),\n promoted: B,\n startedAt: S,\n logPath: S,\n exit: O(\n Type.Object({\n reason: literals(['completed', 'failed', 'signaled', 'stopped', 'timed_out', 'launcher_error', 'backend_lost']),\n code: Type.Union([N, Type.Null()]),\n signal: Type.Union([S, Type.Null()]),\n stopReason: O(S),\n finishedAt: S,\n }),\n ),\n});\nexport const RunnerPayloadSchema = Type.Object({ runs: Type.Array(Run) });\nexport const RunnerLogSchema = Type.Object({\n runId: S,\n running: B,\n text: S,\n lineCount: N,\n totalLines: N,\n fileSize: N,\n completeBytes: N,\n path: S,\n exists: B,\n lineNumbers: O(Type.Array(N)),\n});\nexport const RunnerAppendSchema = Type.Object({ lines: Type.Array(S), offset: O(N), ended: O(B) });\nexport const RunnerScreenSchema = Type.Object({ chunk: S, offset: O(N), ended: O(B) });\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: [\n {\n id: 'runner.log',\n scope: 'session',\n basePath: 'runner',\n path: '/runners/{runId}/log',\n method: 'GET',\n authentication: 'owner',\n description: 'Read a runner log tail or literal substring search.',\n parameters: ['lines', 'grep', 'ignoreCase', 'contextLines'].map((name) => ({\n name,\n in: 'query',\n required: false,\n schema: S,\n })),\n responses: jsonApiResponses(RunnerLogSchema),\n },\n ...(['log', 'screen'] as const).map((kind) => ({\n id: `runner.${kind}.stream`,\n scope: 'session' as const,\n basePath: 'runner',\n path: `/runners/{runId}/${kind}/stream`,\n method: 'GET' as const,\n authentication: 'owner' as const,\n description: `Follow runner ${kind}. SSE data is JSON; ping events carry empty data.`,\n parameters: [{ name: 'from', in: 'query' as const, required: false, schema: N }],\n responses: {\n '200': {\n description: 'Event stream.',\n contentType: 'text/event-stream',\n schema: S,\n events: {\n [kind === 'log' ? 'append' : 'screen']: kind === 'log' ? RunnerAppendSchema : RunnerScreenSchema,\n ping: Type.Literal(''),\n },\n },\n '404': { description: 'Runner unavailable.', schema: Type.Object({ error: S }) },\n },\n })),\n {\n id: 'runner.input',\n scope: 'session',\n basePath: 'runner',\n path: '/runners/{runId}/screen/input',\n method: 'POST',\n authentication: 'owner',\n description: 'Send text to an interactive runner.',\n body: {\n required: true,\n contentType: 'application/json',\n schema: Type.Object({ text: Type.String({ minLength: 1 }) }),\n },\n responses: jsonApiResponses(Type.Object({ delivered: Type.Literal(true) })),\n },\n ],\n sockets: (['global', 'workspace'] as const).map((scope) => ({\n id: 'runner.channel',\n scope,\n service: 'doompi.hub.v1',\n member: 'runner_runs',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'Runner channel payload in the hub frame payload property.',\n input: RunnerPayloadSchema,\n })),\n});\nexport default apiContracts;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"apiContracts.mjs","names":[],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses } from '@agimon-ai/doompi-core/apiContracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst literals = <T extends string>(values: T[]) => Type.Union(values.map((value) => Type.Literal(value)));\nconst Run = Type.Object({\n id: S,\n name: S,\n pid: N,\n command: S,\n cwd: S,\n interactive: B,\n backend: literals(['rmux', 'tmux', 'native']),\n state: literals(['running', 'completed']),\n promoted: B,\n startedAt: S,\n logPath: S,\n exit: O(\n Type.Object({\n reason: literals(['completed', 'failed', 'signaled', 'stopped', 'timed_out', 'launcher_error', 'backend_lost']),\n code: Type.Union([N, Type.Null()]),\n signal: Type.Union([S, Type.Null()]),\n terminationReason: O(\n literals([\n 'timeout',\n 'user_stop',\n 'request_cancelled',\n 'parent_session_cleanup',\n 'owner_lost',\n 'external_signal',\n ]),\n ),\n stopReason: O(S),\n finishedAt: S,\n }),\n ),\n});\nexport const RunnerPayloadSchema = Type.Object({ runs: Type.Array(Run) });\nexport const RunnerLogSchema = Type.Object({\n runId: S,\n running: B,\n text: S,\n lineCount: N,\n totalLines: N,\n fileSize: N,\n completeBytes: N,\n path: S,\n exists: B,\n lineNumbers: O(Type.Array(N)),\n});\nexport const RunnerAppendSchema = Type.Object({ lines: Type.Array(S), offset: O(N), ended: O(B) });\nexport const RunnerScreenSchema = Type.Object({ chunk: S, offset: O(N), ended: O(B) });\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: [\n {\n id: 'runner.log',\n scope: 'session',\n basePath: 'runner',\n path: '/runners/{runId}/log',\n method: 'GET',\n authentication: 'owner',\n description: 'Read a runner log tail or literal substring search.',\n parameters: ['lines', 'grep', 'ignoreCase', 'contextLines'].map((name) => ({\n name,\n in: 'query',\n required: false,\n schema: S,\n })),\n responses: jsonApiResponses(RunnerLogSchema),\n },\n ...(['log', 'screen'] as const).map((kind) => ({\n id: `runner.${kind}.stream`,\n scope: 'session' as const,\n basePath: 'runner',\n path: `/runners/{runId}/${kind}/stream`,\n method: 'GET' as const,\n authentication: 'owner' as const,\n description: `Follow runner ${kind}. SSE data is JSON; ping events carry empty data.`,\n parameters: [{ name: 'from', in: 'query' as const, required: false, schema: N }],\n responses: {\n '200': {\n description: 'Event stream.',\n contentType: 'text/event-stream',\n schema: S,\n events: {\n [kind === 'log' ? 'append' : 'screen']: kind === 'log' ? RunnerAppendSchema : RunnerScreenSchema,\n ping: Type.Literal(''),\n },\n },\n '404': { description: 'Runner unavailable.', schema: Type.Object({ error: S }) },\n },\n })),\n {\n id: 'runner.input',\n scope: 'session',\n basePath: 'runner',\n path: '/runners/{runId}/screen/input',\n method: 'POST',\n authentication: 'owner',\n description: 'Send text to an interactive runner.',\n body: {\n required: true,\n contentType: 'application/json',\n schema: Type.Object({ text: Type.String({ minLength: 1 }) }),\n },\n responses: jsonApiResponses(Type.Object({ delivered: Type.Literal(true) })),\n },\n ],\n sockets: (['global', 'workspace'] as const).map((scope) => ({\n id: 'runner.channel',\n scope,\n service: 'doompi.hub.v1',\n member: 'runner_runs',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'Runner channel payload in the hub frame payload property.',\n input: RunnerPayloadSchema,\n })),\n});\nexport default apiContracts;\n"],"mappings":"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"}
|
|
@@ -5,7 +5,6 @@ import { randomBytes } from "node:crypto";
|
|
|
5
5
|
const COMPLETED = "completed";
|
|
6
6
|
const FAILED = "failed";
|
|
7
7
|
const LAUNCHER_ERROR = "launcher_error";
|
|
8
|
-
const SIGNALED = "signaled";
|
|
9
8
|
//#endregion
|
|
10
9
|
//#region src/services/bashRunService/index.ts
|
|
11
10
|
/** The command outlived the background threshold and should be promoted. */
|
|
@@ -122,6 +121,10 @@ var BashRunService = class {
|
|
|
122
121
|
outputUpdates?.flush();
|
|
123
122
|
if (outcome === TIMED_OUT) return this.promote(handle, "threshold", request.signal);
|
|
124
123
|
if (outcome === ABORTED) {
|
|
124
|
+
await this.registry.requestTermination(id, {
|
|
125
|
+
reason: "stopped",
|
|
126
|
+
terminationReason: "request_cancelled"
|
|
127
|
+
});
|
|
125
128
|
await handle.stop();
|
|
126
129
|
outputUpdates?.flush();
|
|
127
130
|
await this.registry.complete(id, {
|
|
@@ -142,6 +145,10 @@ var BashRunService = class {
|
|
|
142
145
|
};
|
|
143
146
|
}
|
|
144
147
|
if (outcome === DEADLINE) {
|
|
148
|
+
await this.registry.requestTermination(id, {
|
|
149
|
+
reason: "timed_out",
|
|
150
|
+
terminationReason: "timeout"
|
|
151
|
+
});
|
|
145
152
|
await handle.stop();
|
|
146
153
|
outputUpdates?.flush();
|
|
147
154
|
await this.registry.complete(id, {
|
|
@@ -175,9 +182,11 @@ var BashRunService = class {
|
|
|
175
182
|
};
|
|
176
183
|
}
|
|
177
184
|
await this.registry.complete(id, {
|
|
178
|
-
reason: outcome.signal ?
|
|
185
|
+
reason: outcome.reason ?? (outcome.signal ? "signaled" : outcome.code === 0 ? "completed" : "failed"),
|
|
179
186
|
code: outcome.code,
|
|
180
|
-
signal: outcome.signal
|
|
187
|
+
signal: outcome.signal,
|
|
188
|
+
...outcome.terminationReason ? { terminationReason: outcome.terminationReason } : {},
|
|
189
|
+
...outcome.stopReason ? { stopReason: outcome.stopReason } : {}
|
|
181
190
|
});
|
|
182
191
|
return {
|
|
183
192
|
kind: COMPLETED,
|
|
@@ -275,7 +284,10 @@ var BashRunService = class {
|
|
|
275
284
|
handle.detach();
|
|
276
285
|
let abortStop;
|
|
277
286
|
const abort = () => {
|
|
278
|
-
abortStop =
|
|
287
|
+
abortStop = this.registry.requestTermination(handle.id, {
|
|
288
|
+
reason: "stopped",
|
|
289
|
+
terminationReason: "request_cancelled"
|
|
290
|
+
}).then(() => handle.stop()).then(async () => {
|
|
279
291
|
await this.registry.complete(handle.id, {
|
|
280
292
|
reason: "stopped",
|
|
281
293
|
code: null,
|
|
@@ -295,9 +307,11 @@ var BashRunService = class {
|
|
|
295
307
|
handle.completion().then(async (outcome) => {
|
|
296
308
|
if (await abortStop) return;
|
|
297
309
|
await this.registry.complete(handle.id, {
|
|
298
|
-
reason: outcome.signal ?
|
|
310
|
+
reason: outcome.reason ?? (outcome.signal ? "signaled" : outcome.code === 0 ? "completed" : "failed"),
|
|
299
311
|
code: outcome.code,
|
|
300
|
-
signal: outcome.signal
|
|
312
|
+
signal: outcome.signal,
|
|
313
|
+
...outcome.terminationReason ? { terminationReason: outcome.terminationReason } : {},
|
|
314
|
+
...outcome.stopReason ? { stopReason: outcome.stopReason } : {}
|
|
301
315
|
});
|
|
302
316
|
}, async () => {
|
|
303
317
|
if (await abortStop) return;
|
|
@@ -324,4 +338,4 @@ var BashRunService = class {
|
|
|
324
338
|
//#endregion
|
|
325
339
|
export { BashRunService as t };
|
|
326
340
|
|
|
327
|
-
//# sourceMappingURL=bashRunService-
|
|
341
|
+
//# sourceMappingURL=bashRunService-74kDU2Dl.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bashRunService-74kDU2Dl.mjs","names":[],"sources":["../src/constants/bashRunService.ts","../src/services/bashRunService/index.ts"],"sourcesContent":["export const RUNNER_ID_RANDOM_BYTES = 3;\nexport const OUTPUT_UPDATE_POLL_MS = 100;\nexport const COMPLETED = 'completed';\nexport const FAILED = 'failed';\nexport const LAUNCHER_ERROR = 'launcher_error';\nexport const SIGNALED = 'signaled';\n","import { randomBytes } from 'node:crypto';\n\nimport {\n COMPLETED,\n FAILED,\n LAUNCHER_ERROR,\n OUTPUT_UPDATE_POLL_MS,\n RUNNER_ID_RANDOM_BYTES,\n SIGNALED,\n} from '../../constants/bashRunService';\nimport type {\n BashRunRequest,\n BashRunResult,\n CompletedRun,\n IBashRunService,\n PromotedRun,\n} from '../../types/bashRunService';\nimport type { IClock } from '../../types/clock';\nimport type { ILauncher, RunHandle } from '../../types/launcher';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport { type IRtkProcessor, RTK_FAILED_WARNING } from '../../types/rtkProcessor';\nimport type { IRunnerRegistry } from '../../types/runnerRegistry';\nimport { getBackgroundThresholdMs } from '../runnerConfig';\nimport type { IRunnerNamer } from '../runnerNamer/type';\n\n/** The command outlived the background threshold and should be promoted. */\nconst TIMED_OUT = Symbol('threshold-reached');\n/** The command outlived the caller's explicit timeout and should be stopped. */\nconst DEADLINE = Symbol('deadline-reached');\n/** The caller cancelled the command and its owned runner should be stopped. */\nconst ABORTED = Symbol('aborted');\n\nfunction runnerId(): string {\n const timestamp = Date.now().toString(36);\n const random = randomBytes(RUNNER_ID_RANDOM_BYTES).toString('base64url');\n return `${timestamp}-${random}`;\n}\n\nexport class BashRunService implements IBashRunService {\n constructor(\n private readonly launcher: ILauncher,\n private readonly rmuxBackend: IRmuxBackend,\n private readonly namer: IRunnerNamer,\n private readonly registry: IRunnerRegistry,\n private readonly clock: IClock,\n private readonly rtkProcessor: IRtkProcessor,\n private readonly cwd: string = process.cwd(),\n private readonly env: Readonly<Record<string, string | undefined>> = process.env,\n ) {}\n\n async run(request: BashRunRequest): Promise<BashRunResult> {\n const id = runnerId();\n const name = await this.namer.allocate(request.command, request.sessionId, request.name);\n const cwd = request.cwd ?? this.cwd;\n const interactive = request.interactive === true;\n if (request.signal?.aborted) return { kind: FAILED, id, name, error: 'Operation aborted' };\n\n let handle: RunHandle;\n try {\n const rmuxHandle = await this.rmuxBackend.launch({\n id,\n name,\n command: request.command,\n cwd,\n sessionId: request.sessionId,\n interactive,\n });\n if (interactive && !rmuxHandle) {\n return { kind: FAILED, id, name, error: 'RMUX is required for interactive commands but is unavailable' };\n }\n handle =\n rmuxHandle ?? this.launcher.launch({ id, name, command: request.command, cwd, sessionId: request.sessionId });\n } catch (error) {\n return { kind: FAILED, id, name, error: error instanceof Error ? error.message : String(error) };\n }\n\n if (handle.pid === undefined) {\n return { kind: FAILED, id, name, error: 'The command did not start, so it cannot be supervised' };\n }\n if (request.signal?.aborted) {\n await handle.stop();\n return { kind: FAILED, id, name, error: 'Operation aborted' };\n }\n\n try {\n await this.registry.register({\n id,\n name,\n pid: handle.pid,\n command: request.command,\n cwd,\n logPath: handle.logPath,\n interactive,\n sessionId: request.sessionId,\n backend: handle.backend,\n ...(handle.backendTarget ? { backendTarget: handle.backendTarget } : {}),\n });\n } catch (error) {\n await handle.stop();\n return { kind: FAILED, id, name, error: error instanceof Error ? error.message : String(error) };\n }\n\n // An interactive run has nothing to wait for: it is going to prompt, so\n // the model needs the runner name before it can answer.\n if (request.background === true || interactive) {\n return this.promote(handle, request.background === true ? 'requested' : 'interactive', request.signal);\n }\n\n const outputUpdates = request.onOutput ? this.followOutput(handle, request.onOutput) : undefined;\n try {\n const outcome = await this.race(handle, request.timeoutMs, request.signal);\n outputUpdates?.flush();\n if (outcome === TIMED_OUT) {\n return this.promote(handle, 'threshold', request.signal);\n }\n if (outcome === ABORTED) {\n await this.registry.requestTermination(id, { reason: 'stopped', terminationReason: 'request_cancelled' });\n await handle.stop();\n outputUpdates?.flush();\n await this.registry.complete(id, { reason: 'stopped', code: null, signal: 'SIGTERM' });\n return {\n kind: COMPLETED,\n id,\n name,\n output: handle.output(),\n exitCode: null,\n signal: 'SIGTERM',\n logPath: handle.logPath,\n backend: handle.backend,\n aborted: true,\n };\n }\n if (outcome === DEADLINE) {\n // An explicit timeout means the caller wants the command dead, not\n // supervised, so it is stopped rather than promoted.\n await this.registry.requestTermination(id, { reason: 'timed_out', terminationReason: 'timeout' });\n await handle.stop();\n outputUpdates?.flush();\n await this.registry.complete(id, { reason: 'timed_out', code: null, signal: 'SIGTERM' });\n return {\n kind: COMPLETED,\n id,\n name,\n output: handle.output(),\n exitCode: null,\n signal: 'SIGTERM',\n logPath: handle.logPath,\n backend: handle.backend,\n timedOut: true,\n };\n }\n if (outcome instanceof Error) {\n await this.registry.complete(id, { reason: LAUNCHER_ERROR, code: null, signal: null });\n return { kind: FAILED, id, name, error: outcome.message };\n }\n\n await this.registry.complete(id, {\n reason: outcome.reason ?? (outcome.signal ? SIGNALED : outcome.code === 0 ? COMPLETED : FAILED),\n code: outcome.code,\n signal: outcome.signal,\n ...(outcome.terminationReason ? { terminationReason: outcome.terminationReason } : {}),\n ...(outcome.stopReason ? { stopReason: outcome.stopReason } : {}),\n });\n\n return {\n kind: COMPLETED,\n id,\n name,\n output: handle.output(),\n exitCode: outcome.code,\n signal: outcome.signal,\n logPath: handle.logPath,\n backend: handle.backend,\n ...(await this.processLog(request.command, handle.logPath)),\n };\n } finally {\n outputUpdates?.dispose();\n }\n }\n\n private async processLog(command: string, logPath: string): Promise<Pick<CompletedRun, 'rtkOutput' | 'rtkWarning'>> {\n try {\n const result = await this.rtkProcessor.process({ command, logPath });\n if (result.kind === 'processed') return { rtkOutput: result.result };\n if (result.kind === 'fallback') return { rtkWarning: result.warning };\n return {};\n } catch {\n return { rtkWarning: RTK_FAILED_WARNING };\n }\n }\n\n private followOutput(\n handle: RunHandle,\n onOutput: NonNullable<BashRunRequest['onOutput']>,\n ): { flush(): void; dispose(): void } {\n let active = true;\n let previous = '';\n let cancelPoll: (() => void) | undefined;\n const flush = (): void => {\n if (!active) return;\n const output = handle.output();\n if (output === previous) return;\n previous = output;\n onOutput(output);\n };\n const poll = (): void => {\n if (!active) return;\n flush();\n if (active) cancelPoll = this.clock.after(OUTPUT_UPDATE_POLL_MS, poll);\n };\n cancelPoll = this.clock.after(OUTPUT_UPDATE_POLL_MS, poll);\n return {\n flush,\n dispose: () => {\n active = false;\n cancelPoll?.();\n cancelPoll = undefined;\n },\n };\n }\n\n /**\n * Waits for the command, giving up at the background threshold or at the\n * caller's timeout, whichever comes first.\n *\n * A rejected completion is returned rather than thrown so the caller can\n * report a start failure as a tool result instead of an exception.\n */\n private async race(\n handle: RunHandle,\n timeoutMs: number | undefined,\n signal: AbortSignal | undefined,\n ): Promise<\n Awaited<ReturnType<RunHandle['completion']>> | Error | typeof TIMED_OUT | typeof DEADLINE | typeof ABORTED\n > {\n const cancels: Array<() => void> = [];\n const racers: Array<Promise<unknown>> = [handle.completion().catch((error: Error) => error)];\n\n racers.push(\n new Promise((resolve) => {\n cancels.push(this.clock.after(getBackgroundThresholdMs(this.env), () => resolve(TIMED_OUT)));\n }),\n );\n if (timeoutMs !== undefined && timeoutMs > 0) {\n racers.push(\n new Promise((resolve) => {\n cancels.push(this.clock.after(timeoutMs, () => resolve(DEADLINE)));\n }),\n );\n }\n if (signal) {\n racers.push(\n new Promise((resolve) => {\n if (signal.aborted) {\n resolve(ABORTED);\n return;\n }\n const abort = (): void => resolve(ABORTED);\n signal.addEventListener('abort', abort, { once: true });\n cancels.push(() => signal.removeEventListener('abort', abort));\n }),\n );\n }\n\n try {\n return (await Promise.race(racers)) as\n | Awaited<ReturnType<RunHandle['completion']>>\n | Error\n | typeof TIMED_OUT\n | typeof DEADLINE\n | typeof ABORTED;\n } finally {\n for (const cancel of cancels) cancel();\n }\n }\n\n private async promote(\n handle: RunHandle,\n reason: PromotedRun['reason'],\n signal: AbortSignal | undefined,\n ): Promise<BashRunResult> {\n if (handle.pid === undefined)\n return { kind: FAILED, id: handle.id, name: handle.name, error: 'The command did not start' };\n await this.registry.markPromoted(handle.id);\n // Registration first: an unregistered runner is invisible, and the handle\n // stops buffering the moment it is detached.\n handle.detach();\n let abortStop: Promise<boolean> | undefined;\n const abort = (): void => {\n abortStop = this.registry\n .requestTermination(handle.id, { reason: 'stopped', terminationReason: 'request_cancelled' })\n .then(() => handle.stop())\n .then(async () => {\n await this.registry.complete(handle.id, { reason: 'stopped', code: null, signal: 'SIGTERM' });\n return true;\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.emitWarning(`Failed to stop aborted runner ${handle.id}: ${message}`);\n return false;\n });\n };\n if (signal) {\n if (signal.aborted) abort();\n else signal.addEventListener('abort', abort, { once: true });\n }\n void handle\n .completion()\n .then(\n async (outcome) => {\n if (await abortStop) return;\n await this.registry.complete(handle.id, {\n reason: outcome.reason ?? (outcome.signal ? SIGNALED : outcome.code === 0 ? COMPLETED : FAILED),\n code: outcome.code,\n signal: outcome.signal,\n ...(outcome.terminationReason ? { terminationReason: outcome.terminationReason } : {}),\n ...(outcome.stopReason ? { stopReason: outcome.stopReason } : {}),\n });\n },\n async () => {\n if (await abortStop) return;\n await this.registry.complete(handle.id, { reason: LAUNCHER_ERROR, code: null, signal: null });\n },\n )\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.emitWarning(`Failed to complete runner ${handle.id}: ${message}`);\n })\n .finally(() => signal?.removeEventListener('abort', abort));\n\n return {\n kind: 'promoted',\n id: handle.id,\n name: handle.name,\n pid: handle.pid,\n logPath: handle.logPath,\n backend: handle.backend,\n reason,\n };\n }\n}\n"],"mappings":";;;;AAEA,MAAa,YAAY;AACzB,MAAa,SAAS;AACtB,MAAa,iBAAiB;;;;ACsB9B,MAAM,YAAY,OAAO,mBAAmB;;AAE5C,MAAM,WAAW,OAAO,kBAAkB;;AAE1C,MAAM,UAAU,OAAO,SAAS;AAEhC,SAAS,WAAmB;CAG1B,OAAO,GAFW,KAAK,IAAI,CAAC,CAAC,SAAS,EAEpB,EAAE,GADL,YAAA,CAAkC,CAAC,CAAC,SAAS,WAChC;AAC9B;AAEA,IAAa,iBAAb,MAAuD;CAElC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CARnB,YACE,UACA,aACA,OACA,UACA,OACA,cACA,MAA+B,QAAQ,IAAI,GAC3C,MAAqE,QAAQ,KAC7E;EARiB,KAAA,WAAA;EACA,KAAA,cAAA;EACA,KAAA,QAAA;EACA,KAAA,WAAA;EACA,KAAA,QAAA;EACA,KAAA,eAAA;EACA,KAAA,MAAA;EACA,KAAA,MAAA;CAChB;CAEH,MAAM,IAAI,SAAiD;EACzD,MAAM,KAAK,SAAS;EACpB,MAAM,OAAO,MAAM,KAAK,MAAM,SAAS,QAAQ,SAAS,QAAQ,WAAW,QAAQ,IAAI;EACvF,MAAM,MAAM,QAAQ,OAAO,KAAK;EAChC,MAAM,cAAc,QAAQ,gBAAgB;EAC5C,IAAI,QAAQ,QAAQ,SAAS,OAAO;GAAE,MAAM;GAAQ;GAAI;GAAM,OAAO;EAAoB;EAEzF,IAAI;EACJ,IAAI;GACF,MAAM,aAAa,MAAM,KAAK,YAAY,OAAO;IAC/C;IACA;IACA,SAAS,QAAQ;IACjB;IACA,WAAW,QAAQ;IACnB;GACF,CAAC;GACD,IAAI,eAAe,CAAC,YAClB,OAAO;IAAE,MAAM;IAAQ;IAAI;IAAM,OAAO;GAA+D;GAEzG,SACE,cAAc,KAAK,SAAS,OAAO;IAAE;IAAI;IAAM,SAAS,QAAQ;IAAS;IAAK,WAAW,QAAQ;GAAU,CAAC;EAChH,SAAS,OAAO;GACd,OAAO;IAAE,MAAM;IAAQ;IAAI;IAAM,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE;EACjG;EAEA,IAAI,OAAO,QAAQ,KAAA,GACjB,OAAO;GAAE,MAAM;GAAQ;GAAI;GAAM,OAAO;EAAwD;EAElG,IAAI,QAAQ,QAAQ,SAAS;GAC3B,MAAM,OAAO,KAAK;GAClB,OAAO;IAAE,MAAM;IAAQ;IAAI;IAAM,OAAO;GAAoB;EAC9D;EAEA,IAAI;GACF,MAAM,KAAK,SAAS,SAAS;IAC3B;IACA;IACA,KAAK,OAAO;IACZ,SAAS,QAAQ;IACjB;IACA,SAAS,OAAO;IAChB;IACA,WAAW,QAAQ;IACnB,SAAS,OAAO;IAChB,GAAI,OAAO,gBAAgB,EAAE,eAAe,OAAO,cAAc,IAAI,CAAC;GACxE,CAAC;EACH,SAAS,OAAO;GACd,MAAM,OAAO,KAAK;GAClB,OAAO;IAAE,MAAM;IAAQ;IAAI;IAAM,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE;EACjG;EAIA,IAAI,QAAQ,eAAe,QAAQ,aACjC,OAAO,KAAK,QAAQ,QAAQ,QAAQ,eAAe,OAAO,cAAc,eAAe,QAAQ,MAAM;EAGvG,MAAM,gBAAgB,QAAQ,WAAW,KAAK,aAAa,QAAQ,QAAQ,QAAQ,IAAI,KAAA;EACvF,IAAI;GACF,MAAM,UAAU,MAAM,KAAK,KAAK,QAAQ,QAAQ,WAAW,QAAQ,MAAM;GACzE,eAAe,MAAM;GACrB,IAAI,YAAY,WACd,OAAO,KAAK,QAAQ,QAAQ,aAAa,QAAQ,MAAM;GAEzD,IAAI,YAAY,SAAS;IACvB,MAAM,KAAK,SAAS,mBAAmB,IAAI;KAAE,QAAQ;KAAW,mBAAmB;IAAoB,CAAC;IACxG,MAAM,OAAO,KAAK;IAClB,eAAe,MAAM;IACrB,MAAM,KAAK,SAAS,SAAS,IAAI;KAAE,QAAQ;KAAW,MAAM;KAAM,QAAQ;IAAU,CAAC;IACrF,OAAO;KACL,MAAM;KACN;KACA;KACA,QAAQ,OAAO,OAAO;KACtB,UAAU;KACV,QAAQ;KACR,SAAS,OAAO;KAChB,SAAS,OAAO;KAChB,SAAS;IACX;GACF;GACA,IAAI,YAAY,UAAU;IAGxB,MAAM,KAAK,SAAS,mBAAmB,IAAI;KAAE,QAAQ;KAAa,mBAAmB;IAAU,CAAC;IAChG,MAAM,OAAO,KAAK;IAClB,eAAe,MAAM;IACrB,MAAM,KAAK,SAAS,SAAS,IAAI;KAAE,QAAQ;KAAa,MAAM;KAAM,QAAQ;IAAU,CAAC;IACvF,OAAO;KACL,MAAM;KACN;KACA;KACA,QAAQ,OAAO,OAAO;KACtB,UAAU;KACV,QAAQ;KACR,SAAS,OAAO;KAChB,SAAS,OAAO;KAChB,UAAU;IACZ;GACF;GACA,IAAI,mBAAmB,OAAO;IAC5B,MAAM,KAAK,SAAS,SAAS,IAAI;KAAE,QAAQ;KAAgB,MAAM;KAAM,QAAQ;IAAK,CAAC;IACrF,OAAO;KAAE,MAAM;KAAQ;KAAI;KAAM,OAAO,QAAQ;IAAQ;GAC1D;GAEA,MAAM,KAAK,SAAS,SAAS,IAAI;IAC/B,QAAQ,QAAQ,WAAW,QAAQ,SAAA,aAAoB,QAAQ,SAAS,IAAA,cAAA;IACxE,MAAM,QAAQ;IACd,QAAQ,QAAQ;IAChB,GAAI,QAAQ,oBAAoB,EAAE,mBAAmB,QAAQ,kBAAkB,IAAI,CAAC;IACpF,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;GACjE,CAAC;GAED,OAAO;IACL,MAAM;IACN;IACA;IACA,QAAQ,OAAO,OAAO;IACtB,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,SAAS,OAAO;IAChB,SAAS,OAAO;IAChB,GAAI,MAAM,KAAK,WAAW,QAAQ,SAAS,OAAO,OAAO;GAC3D;EACF,UAAU;GACR,eAAe,QAAQ;EACzB;CACF;CAEA,MAAc,WAAW,SAAiB,SAA0E;EAClH,IAAI;GACF,MAAM,SAAS,MAAM,KAAK,aAAa,QAAQ;IAAE;IAAS;GAAQ,CAAC;GACnE,IAAI,OAAO,SAAS,aAAa,OAAO,EAAE,WAAW,OAAO,OAAO;GACnE,IAAI,OAAO,SAAS,YAAY,OAAO,EAAE,YAAY,OAAO,QAAQ;GACpE,OAAO,CAAC;EACV,QAAQ;GACN,OAAO,EAAE,YAAY,mBAAmB;EAC1C;CACF;CAEA,aACE,QACA,UACoC;EACpC,IAAI,SAAS;EACb,IAAI,WAAW;EACf,IAAI;EACJ,MAAM,cAAoB;GACxB,IAAI,CAAC,QAAQ;GACb,MAAM,SAAS,OAAO,OAAO;GAC7B,IAAI,WAAW,UAAU;GACzB,WAAW;GACX,SAAS,MAAM;EACjB;EACA,MAAM,aAAmB;GACvB,IAAI,CAAC,QAAQ;GACb,MAAM;GACN,IAAI,QAAQ,aAAa,KAAK,MAAM,MAAA,KAA6B,IAAI;EACvE;EACA,aAAa,KAAK,MAAM,MAAA,KAA6B,IAAI;EACzD,OAAO;GACL;GACA,eAAe;IACb,SAAS;IACT,aAAa;IACb,aAAa,KAAA;GACf;EACF;CACF;;;;;;;;CASA,MAAc,KACZ,QACA,WACA,QAGA;EACA,MAAM,UAA6B,CAAC;EACpC,MAAM,SAAkC,CAAC,OAAO,WAAW,CAAC,CAAC,OAAO,UAAiB,KAAK,CAAC;EAE3F,OAAO,KACL,IAAI,SAAS,YAAY;GACvB,QAAQ,KAAK,KAAK,MAAM,MAAM,yBAAyB,KAAK,GAAG,SAAS,QAAQ,SAAS,CAAC,CAAC;EAC7F,CAAC,CACH;EACA,IAAI,cAAc,KAAA,KAAa,YAAY,GACzC,OAAO,KACL,IAAI,SAAS,YAAY;GACvB,QAAQ,KAAK,KAAK,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,CAAC,CAAC;EACnE,CAAC,CACH;EAEF,IAAI,QACF,OAAO,KACL,IAAI,SAAS,YAAY;GACvB,IAAI,OAAO,SAAS;IAClB,QAAQ,OAAO;IACf;GACF;GACA,MAAM,cAAoB,QAAQ,OAAO;GACzC,OAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;GACtD,QAAQ,WAAW,OAAO,oBAAoB,SAAS,KAAK,CAAC;EAC/D,CAAC,CACH;EAGF,IAAI;GACF,OAAQ,MAAM,QAAQ,KAAK,MAAM;EAMnC,UAAU;GACR,KAAK,MAAM,UAAU,SAAS,OAAO;EACvC;CACF;CAEA,MAAc,QACZ,QACA,QACA,QACwB;EACxB,IAAI,OAAO,QAAQ,KAAA,GACjB,OAAO;GAAE,MAAM;GAAQ,IAAI,OAAO;GAAI,MAAM,OAAO;GAAM,OAAO;EAA4B;EAC9F,MAAM,KAAK,SAAS,aAAa,OAAO,EAAE;EAG1C,OAAO,OAAO;EACd,IAAI;EACJ,MAAM,cAAoB;GACxB,YAAY,KAAK,SACd,mBAAmB,OAAO,IAAI;IAAE,QAAQ;IAAW,mBAAmB;GAAoB,CAAC,CAAC,CAC5F,WAAW,OAAO,KAAK,CAAC,CAAC,CACzB,KAAK,YAAY;IAChB,MAAM,KAAK,SAAS,SAAS,OAAO,IAAI;KAAE,QAAQ;KAAW,MAAM;KAAM,QAAQ;IAAU,CAAC;IAC5F,OAAO;GACT,CAAC,CAAC,CACD,OAAO,UAAmB;IACzB,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IACrE,QAAQ,YAAY,iCAAiC,OAAO,GAAG,IAAI,SAAS;IAC5E,OAAO;GACT,CAAC;EACL;EACA,IAAI,QAAQ;GACV,IAAI,OAAO,SAAS,MAAM;QACrB,OAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;EAC7D;EACA,OACG,WAAW,CAAC,CACZ,KACC,OAAO,YAAY;GACjB,IAAI,MAAM,WAAW;GACrB,MAAM,KAAK,SAAS,SAAS,OAAO,IAAI;IACtC,QAAQ,QAAQ,WAAW,QAAQ,SAAA,aAAoB,QAAQ,SAAS,IAAA,cAAA;IACxE,MAAM,QAAQ;IACd,QAAQ,QAAQ;IAChB,GAAI,QAAQ,oBAAoB,EAAE,mBAAmB,QAAQ,kBAAkB,IAAI,CAAC;IACpF,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;GACjE,CAAC;EACH,GACA,YAAY;GACV,IAAI,MAAM,WAAW;GACrB,MAAM,KAAK,SAAS,SAAS,OAAO,IAAI;IAAE,QAAQ;IAAgB,MAAM;IAAM,QAAQ;GAAK,CAAC;EAC9F,CACF,CAAC,CACA,OAAO,UAAmB;GACzB,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACrE,QAAQ,YAAY,6BAA6B,OAAO,GAAG,IAAI,SAAS;EAC1E,CAAC,CAAC,CACD,cAAc,QAAQ,oBAAoB,SAAS,KAAK,CAAC;EAE5D,OAAO;GACL,MAAM;GACN,IAAI,OAAO;GACX,MAAM,OAAO;GACb,KAAK,OAAO;GACZ,SAAS,OAAO;GAChB,SAAS,OAAO;GAChB;EACF;CACF;AACF"}
|
|
@@ -5,7 +5,6 @@ let node_crypto = require("node:crypto");
|
|
|
5
5
|
const COMPLETED = "completed";
|
|
6
6
|
const FAILED = "failed";
|
|
7
7
|
const LAUNCHER_ERROR = "launcher_error";
|
|
8
|
-
const SIGNALED = "signaled";
|
|
9
8
|
//#endregion
|
|
10
9
|
//#region src/services/bashRunService/index.ts
|
|
11
10
|
/** The command outlived the background threshold and should be promoted. */
|
|
@@ -122,6 +121,10 @@ var BashRunService = class {
|
|
|
122
121
|
outputUpdates?.flush();
|
|
123
122
|
if (outcome === TIMED_OUT) return this.promote(handle, "threshold", request.signal);
|
|
124
123
|
if (outcome === ABORTED) {
|
|
124
|
+
await this.registry.requestTermination(id, {
|
|
125
|
+
reason: "stopped",
|
|
126
|
+
terminationReason: "request_cancelled"
|
|
127
|
+
});
|
|
125
128
|
await handle.stop();
|
|
126
129
|
outputUpdates?.flush();
|
|
127
130
|
await this.registry.complete(id, {
|
|
@@ -142,6 +145,10 @@ var BashRunService = class {
|
|
|
142
145
|
};
|
|
143
146
|
}
|
|
144
147
|
if (outcome === DEADLINE) {
|
|
148
|
+
await this.registry.requestTermination(id, {
|
|
149
|
+
reason: "timed_out",
|
|
150
|
+
terminationReason: "timeout"
|
|
151
|
+
});
|
|
145
152
|
await handle.stop();
|
|
146
153
|
outputUpdates?.flush();
|
|
147
154
|
await this.registry.complete(id, {
|
|
@@ -175,9 +182,11 @@ var BashRunService = class {
|
|
|
175
182
|
};
|
|
176
183
|
}
|
|
177
184
|
await this.registry.complete(id, {
|
|
178
|
-
reason: outcome.signal ?
|
|
185
|
+
reason: outcome.reason ?? (outcome.signal ? "signaled" : outcome.code === 0 ? "completed" : "failed"),
|
|
179
186
|
code: outcome.code,
|
|
180
|
-
signal: outcome.signal
|
|
187
|
+
signal: outcome.signal,
|
|
188
|
+
...outcome.terminationReason ? { terminationReason: outcome.terminationReason } : {},
|
|
189
|
+
...outcome.stopReason ? { stopReason: outcome.stopReason } : {}
|
|
181
190
|
});
|
|
182
191
|
return {
|
|
183
192
|
kind: COMPLETED,
|
|
@@ -275,7 +284,10 @@ var BashRunService = class {
|
|
|
275
284
|
handle.detach();
|
|
276
285
|
let abortStop;
|
|
277
286
|
const abort = () => {
|
|
278
|
-
abortStop =
|
|
287
|
+
abortStop = this.registry.requestTermination(handle.id, {
|
|
288
|
+
reason: "stopped",
|
|
289
|
+
terminationReason: "request_cancelled"
|
|
290
|
+
}).then(() => handle.stop()).then(async () => {
|
|
279
291
|
await this.registry.complete(handle.id, {
|
|
280
292
|
reason: "stopped",
|
|
281
293
|
code: null,
|
|
@@ -295,9 +307,11 @@ var BashRunService = class {
|
|
|
295
307
|
handle.completion().then(async (outcome) => {
|
|
296
308
|
if (await abortStop) return;
|
|
297
309
|
await this.registry.complete(handle.id, {
|
|
298
|
-
reason: outcome.signal ?
|
|
310
|
+
reason: outcome.reason ?? (outcome.signal ? "signaled" : outcome.code === 0 ? "completed" : "failed"),
|
|
299
311
|
code: outcome.code,
|
|
300
|
-
signal: outcome.signal
|
|
312
|
+
signal: outcome.signal,
|
|
313
|
+
...outcome.terminationReason ? { terminationReason: outcome.terminationReason } : {},
|
|
314
|
+
...outcome.stopReason ? { stopReason: outcome.stopReason } : {}
|
|
301
315
|
});
|
|
302
316
|
}, async () => {
|
|
303
317
|
if (await abortStop) return;
|
|
@@ -329,4 +343,4 @@ Object.defineProperty(exports, "BashRunService", {
|
|
|
329
343
|
}
|
|
330
344
|
});
|
|
331
345
|
|
|
332
|
-
//# sourceMappingURL=bashRunService-
|
|
346
|
+
//# sourceMappingURL=bashRunService-CbzHiXkh.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bashRunService-CbzHiXkh.cjs","names":["randomBytes","RTK_FAILED_WARNING","getBackgroundThresholdMs"],"sources":["../src/constants/bashRunService.ts","../src/services/bashRunService/index.ts"],"sourcesContent":["export const RUNNER_ID_RANDOM_BYTES = 3;\nexport const OUTPUT_UPDATE_POLL_MS = 100;\nexport const COMPLETED = 'completed';\nexport const FAILED = 'failed';\nexport const LAUNCHER_ERROR = 'launcher_error';\nexport const SIGNALED = 'signaled';\n","import { randomBytes } from 'node:crypto';\n\nimport {\n COMPLETED,\n FAILED,\n LAUNCHER_ERROR,\n OUTPUT_UPDATE_POLL_MS,\n RUNNER_ID_RANDOM_BYTES,\n SIGNALED,\n} from '../../constants/bashRunService';\nimport type {\n BashRunRequest,\n BashRunResult,\n CompletedRun,\n IBashRunService,\n PromotedRun,\n} from '../../types/bashRunService';\nimport type { IClock } from '../../types/clock';\nimport type { ILauncher, RunHandle } from '../../types/launcher';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport { type IRtkProcessor, RTK_FAILED_WARNING } from '../../types/rtkProcessor';\nimport type { IRunnerRegistry } from '../../types/runnerRegistry';\nimport { getBackgroundThresholdMs } from '../runnerConfig';\nimport type { IRunnerNamer } from '../runnerNamer/type';\n\n/** The command outlived the background threshold and should be promoted. */\nconst TIMED_OUT = Symbol('threshold-reached');\n/** The command outlived the caller's explicit timeout and should be stopped. */\nconst DEADLINE = Symbol('deadline-reached');\n/** The caller cancelled the command and its owned runner should be stopped. */\nconst ABORTED = Symbol('aborted');\n\nfunction runnerId(): string {\n const timestamp = Date.now().toString(36);\n const random = randomBytes(RUNNER_ID_RANDOM_BYTES).toString('base64url');\n return `${timestamp}-${random}`;\n}\n\nexport class BashRunService implements IBashRunService {\n constructor(\n private readonly launcher: ILauncher,\n private readonly rmuxBackend: IRmuxBackend,\n private readonly namer: IRunnerNamer,\n private readonly registry: IRunnerRegistry,\n private readonly clock: IClock,\n private readonly rtkProcessor: IRtkProcessor,\n private readonly cwd: string = process.cwd(),\n private readonly env: Readonly<Record<string, string | undefined>> = process.env,\n ) {}\n\n async run(request: BashRunRequest): Promise<BashRunResult> {\n const id = runnerId();\n const name = await this.namer.allocate(request.command, request.sessionId, request.name);\n const cwd = request.cwd ?? this.cwd;\n const interactive = request.interactive === true;\n if (request.signal?.aborted) return { kind: FAILED, id, name, error: 'Operation aborted' };\n\n let handle: RunHandle;\n try {\n const rmuxHandle = await this.rmuxBackend.launch({\n id,\n name,\n command: request.command,\n cwd,\n sessionId: request.sessionId,\n interactive,\n });\n if (interactive && !rmuxHandle) {\n return { kind: FAILED, id, name, error: 'RMUX is required for interactive commands but is unavailable' };\n }\n handle =\n rmuxHandle ?? this.launcher.launch({ id, name, command: request.command, cwd, sessionId: request.sessionId });\n } catch (error) {\n return { kind: FAILED, id, name, error: error instanceof Error ? error.message : String(error) };\n }\n\n if (handle.pid === undefined) {\n return { kind: FAILED, id, name, error: 'The command did not start, so it cannot be supervised' };\n }\n if (request.signal?.aborted) {\n await handle.stop();\n return { kind: FAILED, id, name, error: 'Operation aborted' };\n }\n\n try {\n await this.registry.register({\n id,\n name,\n pid: handle.pid,\n command: request.command,\n cwd,\n logPath: handle.logPath,\n interactive,\n sessionId: request.sessionId,\n backend: handle.backend,\n ...(handle.backendTarget ? { backendTarget: handle.backendTarget } : {}),\n });\n } catch (error) {\n await handle.stop();\n return { kind: FAILED, id, name, error: error instanceof Error ? error.message : String(error) };\n }\n\n // An interactive run has nothing to wait for: it is going to prompt, so\n // the model needs the runner name before it can answer.\n if (request.background === true || interactive) {\n return this.promote(handle, request.background === true ? 'requested' : 'interactive', request.signal);\n }\n\n const outputUpdates = request.onOutput ? this.followOutput(handle, request.onOutput) : undefined;\n try {\n const outcome = await this.race(handle, request.timeoutMs, request.signal);\n outputUpdates?.flush();\n if (outcome === TIMED_OUT) {\n return this.promote(handle, 'threshold', request.signal);\n }\n if (outcome === ABORTED) {\n await this.registry.requestTermination(id, { reason: 'stopped', terminationReason: 'request_cancelled' });\n await handle.stop();\n outputUpdates?.flush();\n await this.registry.complete(id, { reason: 'stopped', code: null, signal: 'SIGTERM' });\n return {\n kind: COMPLETED,\n id,\n name,\n output: handle.output(),\n exitCode: null,\n signal: 'SIGTERM',\n logPath: handle.logPath,\n backend: handle.backend,\n aborted: true,\n };\n }\n if (outcome === DEADLINE) {\n // An explicit timeout means the caller wants the command dead, not\n // supervised, so it is stopped rather than promoted.\n await this.registry.requestTermination(id, { reason: 'timed_out', terminationReason: 'timeout' });\n await handle.stop();\n outputUpdates?.flush();\n await this.registry.complete(id, { reason: 'timed_out', code: null, signal: 'SIGTERM' });\n return {\n kind: COMPLETED,\n id,\n name,\n output: handle.output(),\n exitCode: null,\n signal: 'SIGTERM',\n logPath: handle.logPath,\n backend: handle.backend,\n timedOut: true,\n };\n }\n if (outcome instanceof Error) {\n await this.registry.complete(id, { reason: LAUNCHER_ERROR, code: null, signal: null });\n return { kind: FAILED, id, name, error: outcome.message };\n }\n\n await this.registry.complete(id, {\n reason: outcome.reason ?? (outcome.signal ? SIGNALED : outcome.code === 0 ? COMPLETED : FAILED),\n code: outcome.code,\n signal: outcome.signal,\n ...(outcome.terminationReason ? { terminationReason: outcome.terminationReason } : {}),\n ...(outcome.stopReason ? { stopReason: outcome.stopReason } : {}),\n });\n\n return {\n kind: COMPLETED,\n id,\n name,\n output: handle.output(),\n exitCode: outcome.code,\n signal: outcome.signal,\n logPath: handle.logPath,\n backend: handle.backend,\n ...(await this.processLog(request.command, handle.logPath)),\n };\n } finally {\n outputUpdates?.dispose();\n }\n }\n\n private async processLog(command: string, logPath: string): Promise<Pick<CompletedRun, 'rtkOutput' | 'rtkWarning'>> {\n try {\n const result = await this.rtkProcessor.process({ command, logPath });\n if (result.kind === 'processed') return { rtkOutput: result.result };\n if (result.kind === 'fallback') return { rtkWarning: result.warning };\n return {};\n } catch {\n return { rtkWarning: RTK_FAILED_WARNING };\n }\n }\n\n private followOutput(\n handle: RunHandle,\n onOutput: NonNullable<BashRunRequest['onOutput']>,\n ): { flush(): void; dispose(): void } {\n let active = true;\n let previous = '';\n let cancelPoll: (() => void) | undefined;\n const flush = (): void => {\n if (!active) return;\n const output = handle.output();\n if (output === previous) return;\n previous = output;\n onOutput(output);\n };\n const poll = (): void => {\n if (!active) return;\n flush();\n if (active) cancelPoll = this.clock.after(OUTPUT_UPDATE_POLL_MS, poll);\n };\n cancelPoll = this.clock.after(OUTPUT_UPDATE_POLL_MS, poll);\n return {\n flush,\n dispose: () => {\n active = false;\n cancelPoll?.();\n cancelPoll = undefined;\n },\n };\n }\n\n /**\n * Waits for the command, giving up at the background threshold or at the\n * caller's timeout, whichever comes first.\n *\n * A rejected completion is returned rather than thrown so the caller can\n * report a start failure as a tool result instead of an exception.\n */\n private async race(\n handle: RunHandle,\n timeoutMs: number | undefined,\n signal: AbortSignal | undefined,\n ): Promise<\n Awaited<ReturnType<RunHandle['completion']>> | Error | typeof TIMED_OUT | typeof DEADLINE | typeof ABORTED\n > {\n const cancels: Array<() => void> = [];\n const racers: Array<Promise<unknown>> = [handle.completion().catch((error: Error) => error)];\n\n racers.push(\n new Promise((resolve) => {\n cancels.push(this.clock.after(getBackgroundThresholdMs(this.env), () => resolve(TIMED_OUT)));\n }),\n );\n if (timeoutMs !== undefined && timeoutMs > 0) {\n racers.push(\n new Promise((resolve) => {\n cancels.push(this.clock.after(timeoutMs, () => resolve(DEADLINE)));\n }),\n );\n }\n if (signal) {\n racers.push(\n new Promise((resolve) => {\n if (signal.aborted) {\n resolve(ABORTED);\n return;\n }\n const abort = (): void => resolve(ABORTED);\n signal.addEventListener('abort', abort, { once: true });\n cancels.push(() => signal.removeEventListener('abort', abort));\n }),\n );\n }\n\n try {\n return (await Promise.race(racers)) as\n | Awaited<ReturnType<RunHandle['completion']>>\n | Error\n | typeof TIMED_OUT\n | typeof DEADLINE\n | typeof ABORTED;\n } finally {\n for (const cancel of cancels) cancel();\n }\n }\n\n private async promote(\n handle: RunHandle,\n reason: PromotedRun['reason'],\n signal: AbortSignal | undefined,\n ): Promise<BashRunResult> {\n if (handle.pid === undefined)\n return { kind: FAILED, id: handle.id, name: handle.name, error: 'The command did not start' };\n await this.registry.markPromoted(handle.id);\n // Registration first: an unregistered runner is invisible, and the handle\n // stops buffering the moment it is detached.\n handle.detach();\n let abortStop: Promise<boolean> | undefined;\n const abort = (): void => {\n abortStop = this.registry\n .requestTermination(handle.id, { reason: 'stopped', terminationReason: 'request_cancelled' })\n .then(() => handle.stop())\n .then(async () => {\n await this.registry.complete(handle.id, { reason: 'stopped', code: null, signal: 'SIGTERM' });\n return true;\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.emitWarning(`Failed to stop aborted runner ${handle.id}: ${message}`);\n return false;\n });\n };\n if (signal) {\n if (signal.aborted) abort();\n else signal.addEventListener('abort', abort, { once: true });\n }\n void handle\n .completion()\n .then(\n async (outcome) => {\n if (await abortStop) return;\n await this.registry.complete(handle.id, {\n reason: outcome.reason ?? (outcome.signal ? SIGNALED : outcome.code === 0 ? COMPLETED : FAILED),\n code: outcome.code,\n signal: outcome.signal,\n ...(outcome.terminationReason ? { terminationReason: outcome.terminationReason } : {}),\n ...(outcome.stopReason ? { stopReason: outcome.stopReason } : {}),\n });\n },\n async () => {\n if (await abortStop) return;\n await this.registry.complete(handle.id, { reason: LAUNCHER_ERROR, code: null, signal: null });\n },\n )\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.emitWarning(`Failed to complete runner ${handle.id}: ${message}`);\n })\n .finally(() => signal?.removeEventListener('abort', abort));\n\n return {\n kind: 'promoted',\n id: handle.id,\n name: handle.name,\n pid: handle.pid,\n logPath: handle.logPath,\n backend: handle.backend,\n reason,\n };\n }\n}\n"],"mappings":"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"}
|
package/dist/bashRunService.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_bashRunService = require("./bashRunService-
|
|
2
|
+
const require_bashRunService = require("./bashRunService-CbzHiXkh.cjs");
|
|
3
3
|
exports.BashRunService = require_bashRunService.BashRunService;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as IRtkProcessor } from "./rtkProcessor-CVZvNhkJ.mjs";
|
|
2
2
|
import { a as IBashRunService, i as FailedRun, n as BashRunResult, o as PromotedRun, r as CompletedRun, t as BashRunRequest } from "./bashRunService-Ci0cGdYO.mjs";
|
|
3
3
|
import { t as IClock } from "./clock-euJkweLq.mjs";
|
|
4
|
-
import { t as ILauncher } from "./launcher-
|
|
5
|
-
import { t as IRmuxBackend } from "./rmuxBackend-
|
|
6
|
-
import { n as IRunnerRegistry } from "./runnerRegistry-
|
|
4
|
+
import { t as ILauncher } from "./launcher-BG5t9nvG.mjs";
|
|
5
|
+
import { t as IRmuxBackend } from "./rmuxBackend-CaEWooWn.mjs";
|
|
6
|
+
import { n as IRunnerRegistry } from "./runnerRegistry-CtJiD02R.mjs";
|
|
7
7
|
import { t as IRunnerNamer } from "./type-vWQNTahw.mjs";
|
|
8
8
|
//#region src/services/bashRunService/index.d.ts
|
|
9
9
|
export declare class BashRunService implements IBashRunService {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bashRunService.d.mts","names":[],"sources":["../src/services/bashRunService/index.ts"],"mappings":";;;;;;;;qBAsCa,0BAA0B;mBAElB;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;EARnB,YACmB,UAAU,WACV,aAAa,cACb,OAAO,cACP,UAAU,iBACV,OAAO,QACP,cAAc,eACd,cACA,MAAK,SAAS;EAG3B,IAAI,SAAS,iBAAiB,QAAQ;
|
|
1
|
+
{"version":3,"file":"bashRunService.d.mts","names":[],"sources":["../src/services/bashRunService/index.ts"],"mappings":";;;;;;;;qBAsCa,0BAA0B;mBAElB;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;EARnB,YACmB,UAAU,WACV,aAAa,cACb,OAAO,cACP,UAAU,iBACV,OAAO,QACP,cAAc,eACd,cACA,MAAK,SAAS;EAG3B,IAAI,SAAS,iBAAiB,QAAQ;UAkI9B;UAWN;;;;;;;;UAqCM;UAgDA"}
|
package/dist/bashRunService.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as BashRunService } from "./bashRunService-
|
|
1
|
+
import { t as BashRunService } from "./bashRunService-74kDU2Dl.mjs";
|
|
2
2
|
export { BashRunService };
|
package/dist/bin/cli.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_cliApp = require("../cliApp-
|
|
4
|
-
const require_runnerDependencies = require("../runnerDependencies-
|
|
3
|
+
const require_cliApp = require("../cliApp-CP2WKUG_.cjs");
|
|
4
|
+
const require_runnerDependencies = require("../runnerDependencies-CKWU0Mxa.cjs");
|
|
5
5
|
let node_fs = require("node:fs");
|
|
6
6
|
let node_url = require("node:url");
|
|
7
7
|
let _agimon_ai_doompi_telemetry = require("@agimon-ai/doompi-telemetry");
|
package/dist/bin/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as runCli } from "../cliApp-
|
|
3
|
-
import { t as createRunnerDependencies } from "../runnerDependencies-
|
|
2
|
+
import { t as runCli } from "../cliApp-dVY9mnBz.mjs";
|
|
3
|
+
import { t as createRunnerDependencies } from "../runnerDependencies-CTbinc_6.mjs";
|
|
4
4
|
import { existsSync, realpathSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { createDoomTelemetry } from "@agimon-ai/doompi-telemetry";
|
package/dist/bin/runnerHost.cjs
CHANGED
|
@@ -36,6 +36,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
36
36
|
let child;
|
|
37
37
|
let forwardedSignal = null;
|
|
38
38
|
let escalationTimer;
|
|
39
|
+
let ownerLost = false;
|
|
39
40
|
const signalChild = (signal) => {
|
|
40
41
|
if (!child?.pid) return;
|
|
41
42
|
try {
|
|
@@ -62,7 +63,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
62
63
|
detached: true
|
|
63
64
|
});
|
|
64
65
|
if (forwardedSignal) forward(forwardedSignal);
|
|
65
|
-
const detachLifeline = require_services_lifeline_client.watchOwner(() =>
|
|
66
|
+
const detachLifeline = require_services_lifeline_client.watchOwner(() => {
|
|
67
|
+
ownerLost = true;
|
|
68
|
+
forward("SIGTERM");
|
|
69
|
+
}, spec.env[require_services_lifeline_client.LIFELINE_ENV]);
|
|
66
70
|
try {
|
|
67
71
|
const result = await new Promise((resolve, reject) => {
|
|
68
72
|
child?.once("error", reject);
|
|
@@ -71,7 +75,14 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
71
75
|
signal: signal ?? forwardedSignal
|
|
72
76
|
}));
|
|
73
77
|
});
|
|
74
|
-
writeAtomic(exitPath,
|
|
78
|
+
writeAtomic(exitPath, {
|
|
79
|
+
...result,
|
|
80
|
+
...ownerLost ? {
|
|
81
|
+
reason: "stopped",
|
|
82
|
+
terminationReason: "owner_lost",
|
|
83
|
+
stopReason: "owner session ended"
|
|
84
|
+
} : {}
|
|
85
|
+
});
|
|
75
86
|
const exitCode = result.code !== null ? result.code : result.signal ? 128 + node_os.default.constants.signals[result.signal] : 1;
|
|
76
87
|
await telemetry.recordEvent("doom_runner.host_finished", {
|
|
77
88
|
outcome: result.signal || forwardedSignal ? "signaled" : exitCode === 0 ? "completed" : "failed",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runnerHost.cjs","names":["createDoomTelemetry","spawn","watchOwner","LIFELINE_ENV","os","fs","pathToFileURL"],"sources":["../../src/bin/runnerHost.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport { createDoomTelemetry } from '@agimon-ai/doompi-telemetry';\n\nimport { LIFELINE_ENV, watchOwner } from '../services/lifeline/client';\n\ninterface CommandSpec {\n command: string;\n cwd: string;\n env: Record<string, string>;\n}\n\nconst POLL_MS = 10;\nconst FORWARDED_SIGNAL_GRACE_MS = 2_000;\nconst HOST_FAILED_EVENT = 'doom_runner.host_failed';\nconst INVALID_COMMAND_SPEC_ERROR = 'invalid runner command spec';\n\nexport async function main(argv: readonly string[] = process.argv.slice(2)): Promise<number> {\n const startedAt = Date.now();\n const telemetry = createDoomTelemetry({\n serviceName: 'doom-runner-host',\n packageName: '@agimon-ai/doompi-runner',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const [specPath, gatePath, exitPath] = argv;\n let spec: CommandSpec;\n try {\n if (!specPath || !gatePath || !exitPath) throw new Error('runnerHost requires spec, gate, and exit paths');\n spec = readSpec(specPath);\n await waitForGate(gatePath);\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n await telemetry.shutdown();\n throw error;\n }\n let child: ChildProcess | undefined;\n let forwardedSignal: NodeJS.Signals | null = null;\n let escalationTimer: NodeJS.Timeout | undefined;\n const signalChild = (signal: NodeJS.Signals): void => {\n if (!child?.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch {\n child.kill(signal);\n }\n };\n const forward = (signal: NodeJS.Signals): void => {\n forwardedSignal = signal;\n signalChild(signal);\n escalationTimer ??= setTimeout(() => signalChild('SIGKILL'), FORWARDED_SIGNAL_GRACE_MS);\n };\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP'];\n for (const signal of signals) process.once(signal, () => forward(signal));\n\n child = spawn(spec.command, {\n cwd: spec.cwd,\n env: spec.env,\n shell: '/bin/bash',\n stdio: 'inherit',\n detached: true,\n });\n if (forwardedSignal) forward(forwardedSignal);\n\n // This process supervises the command for its whole life, so it is also the\n // right place to notice an owner that died without stopping it.\n //\n // The lifeline comes from the spec rather than this process's environment: an\n // rmux server outlives the session that started it, so its own environment can\n // still name a previous session's socket.\n const detachLifeline = watchOwner(() => forward('SIGTERM'), spec.env[LIFELINE_ENV]);\n\n try {\n const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {\n child?.once('error', reject);\n child?.once('exit', (code, signal) => resolve({ code, signal: signal ?? forwardedSignal }));\n });\n writeAtomic(exitPath, result);\n const exitCode = result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;\n await telemetry.recordEvent('doom_runner.host_finished', {\n outcome: result.signal || forwardedSignal ? 'signaled' : exitCode === 0 ? 'completed' : 'failed',\n 'runner.exit_code': exitCode,\n ...(result.signal || forwardedSignal ? { 'runner.signal': result.signal ?? forwardedSignal ?? '' } : {}),\n duration_ms: Date.now() - startedAt,\n });\n return exitCode;\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n throw error;\n } finally {\n detachLifeline();\n if (escalationTimer) clearTimeout(escalationTimer);\n await telemetry.shutdown();\n }\n}\n\nfunction readSpec(target: string): CommandSpec {\n const value: unknown = JSON.parse(fs.readFileSync(target, 'utf8'));\n if (typeof value !== 'object' || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);\n const spec = value as Partial<CommandSpec>;\n if (typeof spec.command !== 'string' || typeof spec.cwd !== 'string' || !spec.env) {\n throw new Error(INVALID_COMMAND_SPEC_ERROR);\n }\n return { command: spec.command, cwd: spec.cwd, env: spec.env };\n}\n\nasync function waitForGate(target: string): Promise<void> {\n while (!fs.existsSync(target)) await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n}\n\nfunction writeAtomic(target: string, value: unknown): void {\n const temporary = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n fs.renameSync(temporary, target);\n}\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n },\n );\n}\n"],"mappings":";;;;;;;;;;;AAeA,MAAM,UAAU;AAChB,MAAM,4BAA4B;AAClC,MAAM,oBAAoB;AAC1B,MAAM,6BAA6B;AAEnC,eAAsB,KAAK,OAA0B,QAAQ,KAAK,MAAM,CAAC,GAAoB;CAC3F,MAAM,YAAY,KAAK,IAAI;CAC3B,MAAM,aAAA,GAAYA,4BAAAA,oBAAAA,CAAoB;EACpC,aAAa;EACb,aAAa;EACb,KAAK,QAAQ;EACb,YAAY;EACZ,cAAc;CAChB,CAAC;CACD,MAAM,CAAC,UAAU,UAAU,YAAY;CACvC,IAAI;CACJ,IAAI;EACF,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,UAAU,MAAM,IAAI,MAAM,gDAAgD;EACzG,OAAO,SAAS,QAAQ;EACxB,MAAM,YAAY,QAAQ;CAC5B,SAAS,OAAO;EACd,MAAM,UAAU,YAAY,mBAAmB,OAAO,EAAE,aAAa,KAAK,IAAI,IAAI,UAAU,CAAC;EAC7F,MAAM,UAAU,SAAS;EACzB,MAAM;CACR;CACA,IAAI;CACJ,IAAI,kBAAyC;CAC7C,IAAI;CACJ,MAAM,eAAe,WAAiC;EACpD,IAAI,CAAC,OAAO,KAAK;EACjB,IAAI;GACF,QAAQ,KAAK,CAAC,MAAM,KAAK,MAAM;EACjC,QAAQ;GACN,MAAM,KAAK,MAAM;EACnB;CACF;CACA,MAAM,WAAW,WAAiC;EAChD,kBAAkB;EAClB,YAAY,MAAM;EAClB,oBAAoB,iBAAiB,YAAY,SAAS,GAAG,yBAAyB;CACxF;CAEA,KAAK,MAAM,UAAU;EADc;EAAW;EAAU;CAC7B,GAAG,QAAQ,KAAK,cAAc,QAAQ,MAAM,CAAC;CAExE,SAAA,GAAQC,mBAAAA,MAAAA,CAAM,KAAK,SAAS;EAC1B,KAAK,KAAK;EACV,KAAK,KAAK;EACV,OAAO;EACP,OAAO;EACP,UAAU;CACZ,CAAC;CACD,IAAI,iBAAiB,QAAQ,eAAe;CAQ5C,MAAM,iBAAiBC,iCAAAA,iBAAiB,QAAQ,SAAS,GAAG,KAAK,IAAIC,iCAAAA,aAAa;
|
|
1
|
+
{"version":3,"file":"runnerHost.cjs","names":["createDoomTelemetry","spawn","watchOwner","LIFELINE_ENV","os","fs","pathToFileURL"],"sources":["../../src/bin/runnerHost.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport { createDoomTelemetry } from '@agimon-ai/doompi-telemetry';\n\nimport { LIFELINE_ENV, watchOwner } from '../services/lifeline/client';\n\ninterface CommandSpec {\n command: string;\n cwd: string;\n env: Record<string, string>;\n}\n\nconst POLL_MS = 10;\nconst FORWARDED_SIGNAL_GRACE_MS = 2_000;\nconst HOST_FAILED_EVENT = 'doom_runner.host_failed';\nconst INVALID_COMMAND_SPEC_ERROR = 'invalid runner command spec';\n\nexport async function main(argv: readonly string[] = process.argv.slice(2)): Promise<number> {\n const startedAt = Date.now();\n const telemetry = createDoomTelemetry({\n serviceName: 'doom-runner-host',\n packageName: '@agimon-ai/doompi-runner',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const [specPath, gatePath, exitPath] = argv;\n let spec: CommandSpec;\n try {\n if (!specPath || !gatePath || !exitPath) throw new Error('runnerHost requires spec, gate, and exit paths');\n spec = readSpec(specPath);\n await waitForGate(gatePath);\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n await telemetry.shutdown();\n throw error;\n }\n let child: ChildProcess | undefined;\n let forwardedSignal: NodeJS.Signals | null = null;\n let escalationTimer: NodeJS.Timeout | undefined;\n let ownerLost = false;\n const signalChild = (signal: NodeJS.Signals): void => {\n if (!child?.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch {\n child.kill(signal);\n }\n };\n const forward = (signal: NodeJS.Signals): void => {\n forwardedSignal = signal;\n signalChild(signal);\n escalationTimer ??= setTimeout(() => signalChild('SIGKILL'), FORWARDED_SIGNAL_GRACE_MS);\n };\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP'];\n for (const signal of signals) process.once(signal, () => forward(signal));\n\n child = spawn(spec.command, {\n cwd: spec.cwd,\n env: spec.env,\n shell: '/bin/bash',\n stdio: 'inherit',\n detached: true,\n });\n if (forwardedSignal) forward(forwardedSignal);\n\n // This process supervises the command for its whole life, so it is also the\n // right place to notice an owner that died without stopping it.\n //\n // The lifeline comes from the spec rather than this process's environment: an\n // rmux server outlives the session that started it, so its own environment can\n // still name a previous session's socket.\n const detachLifeline = watchOwner(() => {\n ownerLost = true;\n forward('SIGTERM');\n }, spec.env[LIFELINE_ENV]);\n\n try {\n const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {\n child?.once('error', reject);\n child?.once('exit', (code, signal) => resolve({ code, signal: signal ?? forwardedSignal }));\n });\n writeAtomic(exitPath, {\n ...result,\n ...(ownerLost ? { reason: 'stopped', terminationReason: 'owner_lost', stopReason: 'owner session ended' } : {}),\n });\n const exitCode = result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;\n await telemetry.recordEvent('doom_runner.host_finished', {\n outcome: result.signal || forwardedSignal ? 'signaled' : exitCode === 0 ? 'completed' : 'failed',\n 'runner.exit_code': exitCode,\n ...(result.signal || forwardedSignal ? { 'runner.signal': result.signal ?? forwardedSignal ?? '' } : {}),\n duration_ms: Date.now() - startedAt,\n });\n return exitCode;\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n throw error;\n } finally {\n detachLifeline();\n if (escalationTimer) clearTimeout(escalationTimer);\n await telemetry.shutdown();\n }\n}\n\nfunction readSpec(target: string): CommandSpec {\n const value: unknown = JSON.parse(fs.readFileSync(target, 'utf8'));\n if (typeof value !== 'object' || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);\n const spec = value as Partial<CommandSpec>;\n if (typeof spec.command !== 'string' || typeof spec.cwd !== 'string' || !spec.env) {\n throw new Error(INVALID_COMMAND_SPEC_ERROR);\n }\n return { command: spec.command, cwd: spec.cwd, env: spec.env };\n}\n\nasync function waitForGate(target: string): Promise<void> {\n while (!fs.existsSync(target)) await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n}\n\nfunction writeAtomic(target: string, value: unknown): void {\n const temporary = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n fs.renameSync(temporary, target);\n}\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n },\n );\n}\n"],"mappings":"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"}
|
package/dist/bin/runnerHost.mjs
CHANGED
|
@@ -32,6 +32,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
32
32
|
let child;
|
|
33
33
|
let forwardedSignal = null;
|
|
34
34
|
let escalationTimer;
|
|
35
|
+
let ownerLost = false;
|
|
35
36
|
const signalChild = (signal) => {
|
|
36
37
|
if (!child?.pid) return;
|
|
37
38
|
try {
|
|
@@ -58,7 +59,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
58
59
|
detached: true
|
|
59
60
|
});
|
|
60
61
|
if (forwardedSignal) forward(forwardedSignal);
|
|
61
|
-
const detachLifeline = watchOwner(() =>
|
|
62
|
+
const detachLifeline = watchOwner(() => {
|
|
63
|
+
ownerLost = true;
|
|
64
|
+
forward("SIGTERM");
|
|
65
|
+
}, spec.env[LIFELINE_ENV]);
|
|
62
66
|
try {
|
|
63
67
|
const result = await new Promise((resolve, reject) => {
|
|
64
68
|
child?.once("error", reject);
|
|
@@ -67,7 +71,14 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
67
71
|
signal: signal ?? forwardedSignal
|
|
68
72
|
}));
|
|
69
73
|
});
|
|
70
|
-
writeAtomic(exitPath,
|
|
74
|
+
writeAtomic(exitPath, {
|
|
75
|
+
...result,
|
|
76
|
+
...ownerLost ? {
|
|
77
|
+
reason: "stopped",
|
|
78
|
+
terminationReason: "owner_lost",
|
|
79
|
+
stopReason: "owner session ended"
|
|
80
|
+
} : {}
|
|
81
|
+
});
|
|
71
82
|
const exitCode = result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;
|
|
72
83
|
await telemetry.recordEvent("doom_runner.host_finished", {
|
|
73
84
|
outcome: result.signal || forwardedSignal ? "signaled" : exitCode === 0 ? "completed" : "failed",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runnerHost.mjs","names":[],"sources":["../../src/bin/runnerHost.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport { createDoomTelemetry } from '@agimon-ai/doompi-telemetry';\n\nimport { LIFELINE_ENV, watchOwner } from '../services/lifeline/client';\n\ninterface CommandSpec {\n command: string;\n cwd: string;\n env: Record<string, string>;\n}\n\nconst POLL_MS = 10;\nconst FORWARDED_SIGNAL_GRACE_MS = 2_000;\nconst HOST_FAILED_EVENT = 'doom_runner.host_failed';\nconst INVALID_COMMAND_SPEC_ERROR = 'invalid runner command spec';\n\nexport async function main(argv: readonly string[] = process.argv.slice(2)): Promise<number> {\n const startedAt = Date.now();\n const telemetry = createDoomTelemetry({\n serviceName: 'doom-runner-host',\n packageName: '@agimon-ai/doompi-runner',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const [specPath, gatePath, exitPath] = argv;\n let spec: CommandSpec;\n try {\n if (!specPath || !gatePath || !exitPath) throw new Error('runnerHost requires spec, gate, and exit paths');\n spec = readSpec(specPath);\n await waitForGate(gatePath);\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n await telemetry.shutdown();\n throw error;\n }\n let child: ChildProcess | undefined;\n let forwardedSignal: NodeJS.Signals | null = null;\n let escalationTimer: NodeJS.Timeout | undefined;\n const signalChild = (signal: NodeJS.Signals): void => {\n if (!child?.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch {\n child.kill(signal);\n }\n };\n const forward = (signal: NodeJS.Signals): void => {\n forwardedSignal = signal;\n signalChild(signal);\n escalationTimer ??= setTimeout(() => signalChild('SIGKILL'), FORWARDED_SIGNAL_GRACE_MS);\n };\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP'];\n for (const signal of signals) process.once(signal, () => forward(signal));\n\n child = spawn(spec.command, {\n cwd: spec.cwd,\n env: spec.env,\n shell: '/bin/bash',\n stdio: 'inherit',\n detached: true,\n });\n if (forwardedSignal) forward(forwardedSignal);\n\n // This process supervises the command for its whole life, so it is also the\n // right place to notice an owner that died without stopping it.\n //\n // The lifeline comes from the spec rather than this process's environment: an\n // rmux server outlives the session that started it, so its own environment can\n // still name a previous session's socket.\n const detachLifeline = watchOwner(() => forward('SIGTERM'), spec.env[LIFELINE_ENV]);\n\n try {\n const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {\n child?.once('error', reject);\n child?.once('exit', (code, signal) => resolve({ code, signal: signal ?? forwardedSignal }));\n });\n writeAtomic(exitPath, result);\n const exitCode = result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;\n await telemetry.recordEvent('doom_runner.host_finished', {\n outcome: result.signal || forwardedSignal ? 'signaled' : exitCode === 0 ? 'completed' : 'failed',\n 'runner.exit_code': exitCode,\n ...(result.signal || forwardedSignal ? { 'runner.signal': result.signal ?? forwardedSignal ?? '' } : {}),\n duration_ms: Date.now() - startedAt,\n });\n return exitCode;\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n throw error;\n } finally {\n detachLifeline();\n if (escalationTimer) clearTimeout(escalationTimer);\n await telemetry.shutdown();\n }\n}\n\nfunction readSpec(target: string): CommandSpec {\n const value: unknown = JSON.parse(fs.readFileSync(target, 'utf8'));\n if (typeof value !== 'object' || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);\n const spec = value as Partial<CommandSpec>;\n if (typeof spec.command !== 'string' || typeof spec.cwd !== 'string' || !spec.env) {\n throw new Error(INVALID_COMMAND_SPEC_ERROR);\n }\n return { command: spec.command, cwd: spec.cwd, env: spec.env };\n}\n\nasync function waitForGate(target: string): Promise<void> {\n while (!fs.existsSync(target)) await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n}\n\nfunction writeAtomic(target: string, value: unknown): void {\n const temporary = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n fs.renameSync(temporary, target);\n}\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n },\n );\n}\n"],"mappings":";;;;;;;AAeA,MAAM,UAAU;AAChB,MAAM,4BAA4B;AAClC,MAAM,oBAAoB;AAC1B,MAAM,6BAA6B;AAEnC,eAAsB,KAAK,OAA0B,QAAQ,KAAK,MAAM,CAAC,GAAoB;CAC3F,MAAM,YAAY,KAAK,IAAI;CAC3B,MAAM,YAAY,oBAAoB;EACpC,aAAa;EACb,aAAa;EACb,KAAK,QAAQ;EACb,YAAY;EACZ,cAAc;CAChB,CAAC;CACD,MAAM,CAAC,UAAU,UAAU,YAAY;CACvC,IAAI;CACJ,IAAI;EACF,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,UAAU,MAAM,IAAI,MAAM,gDAAgD;EACzG,OAAO,SAAS,QAAQ;EACxB,MAAM,YAAY,QAAQ;CAC5B,SAAS,OAAO;EACd,MAAM,UAAU,YAAY,mBAAmB,OAAO,EAAE,aAAa,KAAK,IAAI,IAAI,UAAU,CAAC;EAC7F,MAAM,UAAU,SAAS;EACzB,MAAM;CACR;CACA,IAAI;CACJ,IAAI,kBAAyC;CAC7C,IAAI;CACJ,MAAM,eAAe,WAAiC;EACpD,IAAI,CAAC,OAAO,KAAK;EACjB,IAAI;GACF,QAAQ,KAAK,CAAC,MAAM,KAAK,MAAM;EACjC,QAAQ;GACN,MAAM,KAAK,MAAM;EACnB;CACF;CACA,MAAM,WAAW,WAAiC;EAChD,kBAAkB;EAClB,YAAY,MAAM;EAClB,oBAAoB,iBAAiB,YAAY,SAAS,GAAG,yBAAyB;CACxF;CAEA,KAAK,MAAM,UAAU;EADc;EAAW;EAAU;CAC7B,GAAG,QAAQ,KAAK,cAAc,QAAQ,MAAM,CAAC;CAExE,QAAQ,MAAM,KAAK,SAAS;EAC1B,KAAK,KAAK;EACV,KAAK,KAAK;EACV,OAAO;EACP,OAAO;EACP,UAAU;CACZ,CAAC;CACD,IAAI,iBAAiB,QAAQ,eAAe;CAQ5C,MAAM,iBAAiB,iBAAiB,QAAQ,SAAS,GAAG,KAAK,IAAI,aAAa;
|
|
1
|
+
{"version":3,"file":"runnerHost.mjs","names":[],"sources":["../../src/bin/runnerHost.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport { pathToFileURL } from 'node:url';\n\nimport { createDoomTelemetry } from '@agimon-ai/doompi-telemetry';\n\nimport { LIFELINE_ENV, watchOwner } from '../services/lifeline/client';\n\ninterface CommandSpec {\n command: string;\n cwd: string;\n env: Record<string, string>;\n}\n\nconst POLL_MS = 10;\nconst FORWARDED_SIGNAL_GRACE_MS = 2_000;\nconst HOST_FAILED_EVENT = 'doom_runner.host_failed';\nconst INVALID_COMMAND_SPEC_ERROR = 'invalid runner command spec';\n\nexport async function main(argv: readonly string[] = process.argv.slice(2)): Promise<number> {\n const startedAt = Date.now();\n const telemetry = createDoomTelemetry({\n serviceName: 'doom-runner-host',\n packageName: '@agimon-ai/doompi-runner',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n const [specPath, gatePath, exitPath] = argv;\n let spec: CommandSpec;\n try {\n if (!specPath || !gatePath || !exitPath) throw new Error('runnerHost requires spec, gate, and exit paths');\n spec = readSpec(specPath);\n await waitForGate(gatePath);\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n await telemetry.shutdown();\n throw error;\n }\n let child: ChildProcess | undefined;\n let forwardedSignal: NodeJS.Signals | null = null;\n let escalationTimer: NodeJS.Timeout | undefined;\n let ownerLost = false;\n const signalChild = (signal: NodeJS.Signals): void => {\n if (!child?.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch {\n child.kill(signal);\n }\n };\n const forward = (signal: NodeJS.Signals): void => {\n forwardedSignal = signal;\n signalChild(signal);\n escalationTimer ??= setTimeout(() => signalChild('SIGKILL'), FORWARDED_SIGNAL_GRACE_MS);\n };\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP'];\n for (const signal of signals) process.once(signal, () => forward(signal));\n\n child = spawn(spec.command, {\n cwd: spec.cwd,\n env: spec.env,\n shell: '/bin/bash',\n stdio: 'inherit',\n detached: true,\n });\n if (forwardedSignal) forward(forwardedSignal);\n\n // This process supervises the command for its whole life, so it is also the\n // right place to notice an owner that died without stopping it.\n //\n // The lifeline comes from the spec rather than this process's environment: an\n // rmux server outlives the session that started it, so its own environment can\n // still name a previous session's socket.\n const detachLifeline = watchOwner(() => {\n ownerLost = true;\n forward('SIGTERM');\n }, spec.env[LIFELINE_ENV]);\n\n try {\n const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {\n child?.once('error', reject);\n child?.once('exit', (code, signal) => resolve({ code, signal: signal ?? forwardedSignal }));\n });\n writeAtomic(exitPath, {\n ...result,\n ...(ownerLost ? { reason: 'stopped', terminationReason: 'owner_lost', stopReason: 'owner session ended' } : {}),\n });\n const exitCode = result.code !== null ? result.code : result.signal ? 128 + os.constants.signals[result.signal] : 1;\n await telemetry.recordEvent('doom_runner.host_finished', {\n outcome: result.signal || forwardedSignal ? 'signaled' : exitCode === 0 ? 'completed' : 'failed',\n 'runner.exit_code': exitCode,\n ...(result.signal || forwardedSignal ? { 'runner.signal': result.signal ?? forwardedSignal ?? '' } : {}),\n duration_ms: Date.now() - startedAt,\n });\n return exitCode;\n } catch (error) {\n await telemetry.recordError(HOST_FAILED_EVENT, error, { duration_ms: Date.now() - startedAt });\n throw error;\n } finally {\n detachLifeline();\n if (escalationTimer) clearTimeout(escalationTimer);\n await telemetry.shutdown();\n }\n}\n\nfunction readSpec(target: string): CommandSpec {\n const value: unknown = JSON.parse(fs.readFileSync(target, 'utf8'));\n if (typeof value !== 'object' || value === null) throw new Error(INVALID_COMMAND_SPEC_ERROR);\n const spec = value as Partial<CommandSpec>;\n if (typeof spec.command !== 'string' || typeof spec.cwd !== 'string' || !spec.env) {\n throw new Error(INVALID_COMMAND_SPEC_ERROR);\n }\n return { command: spec.command, cwd: spec.cwd, env: spec.env };\n}\n\nasync function waitForGate(target: string): Promise<void> {\n while (!fs.existsSync(target)) await new Promise((resolve) => setTimeout(resolve, POLL_MS));\n}\n\nfunction writeAtomic(target: string, value: unknown): void {\n const temporary = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(temporary, `${JSON.stringify(value)}\\n`, { mode: 0o600 });\n fs.renameSync(temporary, target);\n}\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error: unknown) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n },\n );\n}\n"],"mappings":";;;;;;;AAeA,MAAM,UAAU;AAChB,MAAM,4BAA4B;AAClC,MAAM,oBAAoB;AAC1B,MAAM,6BAA6B;AAEnC,eAAsB,KAAK,OAA0B,QAAQ,KAAK,MAAM,CAAC,GAAoB;CAC3F,MAAM,YAAY,KAAK,IAAI;CAC3B,MAAM,YAAY,oBAAoB;EACpC,aAAa;EACb,aAAa;EACb,KAAK,QAAQ;EACb,YAAY;EACZ,cAAc;CAChB,CAAC;CACD,MAAM,CAAC,UAAU,UAAU,YAAY;CACvC,IAAI;CACJ,IAAI;EACF,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,UAAU,MAAM,IAAI,MAAM,gDAAgD;EACzG,OAAO,SAAS,QAAQ;EACxB,MAAM,YAAY,QAAQ;CAC5B,SAAS,OAAO;EACd,MAAM,UAAU,YAAY,mBAAmB,OAAO,EAAE,aAAa,KAAK,IAAI,IAAI,UAAU,CAAC;EAC7F,MAAM,UAAU,SAAS;EACzB,MAAM;CACR;CACA,IAAI;CACJ,IAAI,kBAAyC;CAC7C,IAAI;CACJ,IAAI,YAAY;CAChB,MAAM,eAAe,WAAiC;EACpD,IAAI,CAAC,OAAO,KAAK;EACjB,IAAI;GACF,QAAQ,KAAK,CAAC,MAAM,KAAK,MAAM;EACjC,QAAQ;GACN,MAAM,KAAK,MAAM;EACnB;CACF;CACA,MAAM,WAAW,WAAiC;EAChD,kBAAkB;EAClB,YAAY,MAAM;EAClB,oBAAoB,iBAAiB,YAAY,SAAS,GAAG,yBAAyB;CACxF;CAEA,KAAK,MAAM,UAAU;EADc;EAAW;EAAU;CAC7B,GAAG,QAAQ,KAAK,cAAc,QAAQ,MAAM,CAAC;CAExE,QAAQ,MAAM,KAAK,SAAS;EAC1B,KAAK,KAAK;EACV,KAAK,KAAK;EACV,OAAO;EACP,OAAO;EACP,UAAU;CACZ,CAAC;CACD,IAAI,iBAAiB,QAAQ,eAAe;CAQ5C,MAAM,iBAAiB,iBAAiB;EACtC,YAAY;EACZ,QAAQ,SAAS;CACnB,GAAG,KAAK,IAAI,aAAa;CAEzB,IAAI;EACF,MAAM,SAAS,MAAM,IAAI,SAAiE,SAAS,WAAW;GAC5G,OAAO,KAAK,SAAS,MAAM;GAC3B,OAAO,KAAK,SAAS,MAAM,WAAW,QAAQ;IAAE;IAAM,QAAQ,UAAU;GAAgB,CAAC,CAAC;EAC5F,CAAC;EACD,YAAY,UAAU;GACpB,GAAG;GACH,GAAI,YAAY;IAAE,QAAQ;IAAW,mBAAmB;IAAc,YAAY;GAAsB,IAAI,CAAC;EAC/G,CAAC;EACD,MAAM,WAAW,OAAO,SAAS,OAAO,OAAO,OAAO,OAAO,SAAS,MAAM,GAAG,UAAU,QAAQ,OAAO,UAAU;EAClH,MAAM,UAAU,YAAY,6BAA6B;GACvD,SAAS,OAAO,UAAU,kBAAkB,aAAa,aAAa,IAAI,cAAc;GACxF,oBAAoB;GACpB,GAAI,OAAO,UAAU,kBAAkB,EAAE,iBAAiB,OAAO,UAAU,mBAAmB,GAAG,IAAI,CAAC;GACtG,aAAa,KAAK,IAAI,IAAI;EAC5B,CAAC;EACD,OAAO;CACT,SAAS,OAAO;EACd,MAAM,UAAU,YAAY,mBAAmB,OAAO,EAAE,aAAa,KAAK,IAAI,IAAI,UAAU,CAAC;EAC7F,MAAM;CACR,UAAU;EACR,eAAe;EACf,IAAI,iBAAiB,aAAa,eAAe;EACjD,MAAM,UAAU,SAAS;CAC3B;AACF;AAEA,SAAS,SAAS,QAA6B;CAC7C,MAAM,QAAiB,KAAK,MAAM,GAAG,aAAa,QAAQ,MAAM,CAAC;CACjE,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,MAAM,IAAI,MAAM,0BAA0B;CAC3F,MAAM,OAAO;CACb,IAAI,OAAO,KAAK,YAAY,YAAY,OAAO,KAAK,QAAQ,YAAY,CAAC,KAAK,KAC5E,MAAM,IAAI,MAAM,0BAA0B;CAE5C,OAAO;EAAE,SAAS,KAAK;EAAS,KAAK,KAAK;EAAK,KAAK,KAAK;CAAI;AAC/D;AAEA,eAAe,YAAY,QAA+B;CACxD,OAAO,CAAC,GAAG,WAAW,MAAM,GAAG,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,OAAO,CAAC;AAC5F;AAEA,SAAS,YAAY,QAAgB,OAAsB;CACzD,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,IAAI;CAC3C,GAAG,cAAc,WAAW,GAAG,KAAK,UAAU,KAAK,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;CACzE,GAAG,WAAW,WAAW,MAAM;AACjC;AAEA,IAAI,QAAQ,KAAK,MAAM,YAAY,QAAQ,cAAc,QAAQ,KAAK,EAAE,CAAC,CAAC,MACxE,KAAK,CAAC,CAAC,MACJ,SAAS;CACR,QAAQ,WAAW;AACrB,IACC,UAAmB;CAClB,QAAQ,OAAO,MAAM,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAAG;CAClF,QAAQ,WAAW;AACrB,CACF"}
|