@agimon-ai/doompi-runner 0.0.1-alpha.76 → 0.0.1-alpha.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/extensions/mcp.cjs +105 -0
  2. package/dist/extensions/mcp.cjs.map +1 -0
  3. package/dist/extensions/mcp.d.cts +5 -0
  4. package/dist/extensions/mcp.d.cts.map +1 -0
  5. package/dist/extensions/mcp.d.mts +5 -0
  6. package/dist/extensions/mcp.d.mts.map +1 -0
  7. package/dist/extensions/mcp.mjs +77 -0
  8. package/dist/extensions/mcp.mjs.map +1 -0
  9. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.cjs +13 -5
  10. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.cjs.map +1 -1
  11. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.mjs +13 -6
  12. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.mjs.map +1 -1
  13. package/dist/src/services/launcher/index.cjs +8 -3
  14. package/dist/src/services/launcher/index.cjs.map +1 -1
  15. package/dist/src/services/launcher/index.d.cts +3 -1
  16. package/dist/src/services/launcher/index.d.cts.map +1 -1
  17. package/dist/src/services/launcher/index.d.mts +3 -1
  18. package/dist/src/services/launcher/index.d.mts.map +1 -1
  19. package/dist/src/services/launcher/index.mjs +8 -3
  20. package/dist/src/services/launcher/index.mjs.map +1 -1
  21. package/dist/src/services/lifeline/index.cjs +0 -3
  22. package/dist/src/services/lifeline/index.cjs.map +1 -1
  23. package/dist/src/services/lifeline/index.d.cts.map +1 -1
  24. package/dist/src/services/lifeline/index.d.mts.map +1 -1
  25. package/dist/src/services/lifeline/index.mjs +0 -3
  26. package/dist/src/services/lifeline/index.mjs.map +1 -1
  27. package/dist/src/services/runnerDependencies/index.cjs +1 -1
  28. package/dist/src/services/runnerDependencies/index.cjs.map +1 -1
  29. package/dist/src/services/runnerDependencies/index.mjs +1 -1
  30. package/dist/src/services/runnerDependencies/index.mjs.map +1 -1
  31. package/dist/src/services/runnerServerRuntime/index.cjs +17 -19
  32. package/dist/src/services/runnerServerRuntime/index.cjs.map +1 -1
  33. package/dist/src/services/runnerServerRuntime/index.mjs +17 -19
  34. package/dist/src/services/runnerServerRuntime/index.mjs.map +1 -1
  35. package/dist/src/tui/runnerRuntime.cjs +1 -1
  36. package/dist/src/tui/runnerRuntime.cjs.map +1 -1
  37. package/dist/src/tui/runnerRuntime.mjs +1 -1
  38. package/dist/src/tui/runnerRuntime.mjs.map +1 -1
  39. package/package.json +28 -20
@@ -2,6 +2,7 @@ import { getResultMaxBytes } from "../runnerConfig/index.mjs";
2
2
  import { PI_SESSION_ID_ENV } from "../../constants/session.mjs";
3
3
  import { NO_TERMINAL_INPUT_ENV, cleanupSupervisorFiles, readExitMetadata, supervisorCommand, supervisorPaths, writeCommandSpec } from "../../../services/runnerSupervisor/index.mjs";
4
4
  import { NON_INTERACTIVE_ENV, TERM_GRACE_MS } from "../../constants/launcher.mjs";
5
+ import { LIFELINE_ENV } from "../../../services/lifeline/client.mjs";
5
6
  import fs from "node:fs";
6
7
  //#region src/services/launcher/index.ts
7
8
  /**
@@ -17,12 +18,14 @@ var Launcher = class {
17
18
  logFile;
18
19
  clock;
19
20
  paths;
20
- constructor(spawner, processControl, logFile, clock, paths) {
21
+ lifeline;
22
+ constructor(spawner, processControl, logFile, clock, paths, lifeline) {
21
23
  this.spawner = spawner;
22
24
  this.processControl = processControl;
23
25
  this.logFile = logFile;
24
26
  this.clock = clock;
25
27
  this.paths = paths;
28
+ this.lifeline = lifeline;
26
29
  }
27
30
  launch(request) {
28
31
  const writer = this.logFile.open(request.id);
@@ -38,11 +41,13 @@ var Launcher = class {
38
41
  });
39
42
  this.paths.ensureDirectories(request.sessionId);
40
43
  const supervisor = supervisorPaths(this.paths.stateDirectory(request.sessionId), request.id);
44
+ const { [LIFELINE_ENV]: _inheritedLifeline, ...environment } = process.env;
41
45
  const env = {
42
- ...process.env,
46
+ ...environment,
43
47
  ...NON_INTERACTIVE_ENV,
44
48
  ...NO_TERMINAL_INPUT_ENV,
45
- [PI_SESSION_ID_ENV]: request.sessionId
49
+ [PI_SESSION_ID_ENV]: request.sessionId,
50
+ [LIFELINE_ENV]: this.lifeline?.path()
46
51
  };
47
52
  cleanupSupervisorFiles(supervisor);
48
53
  writeCommandSpec(supervisor.spec, {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/launcher/index.ts"],"sourcesContent":["import fs from 'node:fs';\n\nimport {\n GATE_MODE,\n LIVENESS_POLL_MS,\n MEMORY_BUFFER_FACTOR,\n NON_INTERACTIVE_ENV,\n TERM_GRACE_MS,\n} from '../../constants/launcher';\nimport { PI_SESSION_ID_ENV } from '../../constants/session';\nimport type { IClock } from '../../types/clock';\nimport type { ILauncher, LaunchRequest, RunHandle } from '../../types/launcher';\nimport type { ILogFile } from '../../types/logFile';\nimport type { IProcessControl } from '../../types/processControl';\nimport type { ExitResult, ISpawner } from '../../types/spawner';\nimport { getResultMaxBytes } from '../runnerConfig';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport {\n cleanupSupervisorFiles,\n NO_TERMINAL_INPUT_ENV,\n readExitMetadata,\n supervisorCommand,\n supervisorPaths,\n writeCommandSpec,\n} from '../runnerSupervisor';\n\n/**\n * Prompts belong on a screen, not in a log file, so `CI` stays set.\n *\n * Colour is kept: the log is replayed into the transcript, and the scrub keeps\n * SGR while still removing the cursor moves that made raw output unreadable.\n */\n/** In-memory output is a multiple of the result ceiling; the log holds the rest. */\n\nexport class Launcher implements ILauncher {\n constructor(\n private readonly spawner: ISpawner,\n private readonly processControl: IProcessControl,\n private readonly logFile: ILogFile,\n private readonly clock: IClock,\n private readonly paths: IRunnerPaths,\n ) {}\n\n launch(request: LaunchRequest): RunHandle {\n const writer = this.logFile.open(request.id);\n const memoryLimit = getResultMaxBytes() * MEMORY_BUFFER_FACTOR;\n\n let buffer = '';\n let buffering = true;\n let settled = false;\n let resolveCompletion: (result: ExitResult) => void = () => undefined;\n let rejectCompletion: (error: Error) => void = () => undefined;\n const completion = new Promise<ExitResult>((resolve, reject) => {\n resolveCompletion = resolve;\n rejectCompletion = reject;\n });\n\n this.paths.ensureDirectories(request.sessionId);\n const supervisor = supervisorPaths(this.paths.stateDirectory(request.sessionId), request.id);\n const env = {\n ...process.env,\n ...NON_INTERACTIVE_ENV,\n ...NO_TERMINAL_INPUT_ENV,\n [PI_SESSION_ID_ENV]: request.sessionId,\n };\n cleanupSupervisorFiles(supervisor);\n writeCommandSpec(supervisor.spec, { command: request.command, cwd: request.cwd, env: defined(env) });\n // Nothing has to be attached before this runner starts, so its gate opens at once.\n fs.writeFileSync(supervisor.gate, '', { mode: GATE_MODE });\n\n const child = this.spawner.spawn({\n // runnerHost supervises the command: it forwards signals and, if the owning\n // pi dies without running its shutdown handler, kills the group itself.\n command: supervisorCommand(supervisor),\n cwd: request.cwd,\n env,\n // Its own process group, so stopping a runner takes the whole tree with it.\n detached: true,\n });\n\n const failLaunch = (error: unknown): void => {\n if (settled) return;\n settled = true;\n writer.close();\n cleanupSupervisorFiles(supervisor);\n try {\n child.kill('SIGTERM');\n } catch (killError) {\n process.emitWarning(`Could not stop runner after log failure: ${String(killError)}`);\n }\n rejectCompletion(error instanceof Error ? error : new Error(String(error)));\n };\n child.onOutput((chunk) => {\n if (settled) return;\n try {\n writer.append(chunk);\n } catch (error) {\n failLaunch(error);\n return;\n }\n if (!buffering) return;\n buffer += chunk;\n if (buffer.length > memoryLimit) buffer = buffer.slice(-memoryLimit);\n });\n\n child.onError(failLaunch);\n\n child.onExit((result) => {\n if (settled) return;\n settled = true;\n writer.close();\n // The supervisor collapses a signalled command into an exit code, so its\n // own sidecar is the only place the original signal survives.\n const outcome = readExitMetadata(supervisor.exit) ?? result;\n cleanupSupervisorFiles(supervisor);\n resolveCompletion(outcome);\n });\n\n // The launcher must never hold the pi process open on a runner's behalf.\n child.unref();\n\n const stop = async (): Promise<boolean> => (child.pid === undefined ? false : this.stop(child.pid));\n\n return {\n id: request.id,\n name: request.name,\n pid: child.pid,\n logPath: writer.path,\n backend: 'native',\n output: () => buffer,\n completion: () => completion,\n detach: () => {\n buffering = false;\n buffer = '';\n },\n stop,\n };\n }\n\n async stop(pid: number): Promise<boolean> {\n if (!this.processControl.isAlive(pid)) return false;\n\n this.processControl.signalGroup(pid, 'SIGTERM');\n const deadline = this.clock.now() + TERM_GRACE_MS;\n while (this.clock.now() < deadline) {\n if (!this.processControl.isAlive(pid)) return true;\n await this.sleep(LIVENESS_POLL_MS);\n }\n\n this.processControl.signalGroup(pid, 'SIGKILL');\n return true;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n this.clock.after(ms, resolve);\n });\n }\n}\n\n/** The spec is JSON, which has no way to carry an unset variable. */\nfunction defined(env: NodeJS.ProcessEnv): Record<string, string> {\n return Object.fromEntries(Object.entries(env).filter((entry): entry is [string, string] => entry[1] !== undefined));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/launcher/index.ts"],"sourcesContent":["import fs from 'node:fs';\n\nimport {\n GATE_MODE,\n LIVENESS_POLL_MS,\n MEMORY_BUFFER_FACTOR,\n NON_INTERACTIVE_ENV,\n TERM_GRACE_MS,\n} from '../../constants/launcher';\nimport { PI_SESSION_ID_ENV } from '../../constants/session';\nimport type { IClock } from '../../types/clock';\nimport type { ILauncher, LaunchRequest, RunHandle } from '../../types/launcher';\nimport type { ILifeline } from '../../types/lifeline';\nimport type { ILogFile } from '../../types/logFile';\nimport type { IProcessControl } from '../../types/processControl';\nimport type { ExitResult, ISpawner } from '../../types/spawner';\nimport { LIFELINE_ENV } from '../lifeline/client';\nimport { getResultMaxBytes } from '../runnerConfig';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport {\n cleanupSupervisorFiles,\n NO_TERMINAL_INPUT_ENV,\n readExitMetadata,\n supervisorCommand,\n supervisorPaths,\n writeCommandSpec,\n} from '../runnerSupervisor';\n\n/**\n * Prompts belong on a screen, not in a log file, so `CI` stays set.\n *\n * Colour is kept: the log is replayed into the transcript, and the scrub keeps\n * SGR while still removing the cursor moves that made raw output unreadable.\n */\n/** In-memory output is a multiple of the result ceiling; the log holds the rest. */\n\nexport class Launcher implements ILauncher {\n constructor(\n private readonly spawner: ISpawner,\n private readonly processControl: IProcessControl,\n private readonly logFile: ILogFile,\n private readonly clock: IClock,\n private readonly paths: IRunnerPaths,\n private readonly lifeline?: ILifeline,\n ) {}\n\n launch(request: LaunchRequest): RunHandle {\n const writer = this.logFile.open(request.id);\n const memoryLimit = getResultMaxBytes() * MEMORY_BUFFER_FACTOR;\n\n let buffer = '';\n let buffering = true;\n let settled = false;\n let resolveCompletion: (result: ExitResult) => void = () => undefined;\n let rejectCompletion: (error: Error) => void = () => undefined;\n const completion = new Promise<ExitResult>((resolve, reject) => {\n resolveCompletion = resolve;\n rejectCompletion = reject;\n });\n\n this.paths.ensureDirectories(request.sessionId);\n const supervisor = supervisorPaths(this.paths.stateDirectory(request.sessionId), request.id);\n const { [LIFELINE_ENV]: _inheritedLifeline, ...environment } = process.env;\n const env = {\n ...environment,\n ...NON_INTERACTIVE_ENV,\n ...NO_TERMINAL_INPUT_ENV,\n [PI_SESSION_ID_ENV]: request.sessionId,\n [LIFELINE_ENV]: this.lifeline?.path(),\n };\n cleanupSupervisorFiles(supervisor);\n writeCommandSpec(supervisor.spec, { command: request.command, cwd: request.cwd, env: defined(env) });\n // Nothing has to be attached before this runner starts, so its gate opens at once.\n fs.writeFileSync(supervisor.gate, '', { mode: GATE_MODE });\n\n const child = this.spawner.spawn({\n // runnerHost supervises the command: it forwards signals and, if the owning\n // pi dies without running its shutdown handler, kills the group itself.\n command: supervisorCommand(supervisor),\n cwd: request.cwd,\n env,\n // Its own process group, so stopping a runner takes the whole tree with it.\n detached: true,\n });\n\n const failLaunch = (error: unknown): void => {\n if (settled) return;\n settled = true;\n writer.close();\n cleanupSupervisorFiles(supervisor);\n try {\n child.kill('SIGTERM');\n } catch (killError) {\n process.emitWarning(`Could not stop runner after log failure: ${String(killError)}`);\n }\n rejectCompletion(error instanceof Error ? error : new Error(String(error)));\n };\n child.onOutput((chunk) => {\n if (settled) return;\n try {\n writer.append(chunk);\n } catch (error) {\n failLaunch(error);\n return;\n }\n if (!buffering) return;\n buffer += chunk;\n if (buffer.length > memoryLimit) buffer = buffer.slice(-memoryLimit);\n });\n\n child.onError(failLaunch);\n\n child.onExit((result) => {\n if (settled) return;\n settled = true;\n writer.close();\n // The supervisor collapses a signalled command into an exit code, so its\n // own sidecar is the only place the original signal survives.\n const outcome = readExitMetadata(supervisor.exit) ?? result;\n cleanupSupervisorFiles(supervisor);\n resolveCompletion(outcome);\n });\n\n // The launcher must never hold the pi process open on a runner's behalf.\n child.unref();\n\n const stop = async (): Promise<boolean> => (child.pid === undefined ? false : this.stop(child.pid));\n\n return {\n id: request.id,\n name: request.name,\n pid: child.pid,\n logPath: writer.path,\n backend: 'native',\n output: () => buffer,\n completion: () => completion,\n detach: () => {\n buffering = false;\n buffer = '';\n },\n stop,\n };\n }\n\n async stop(pid: number): Promise<boolean> {\n if (!this.processControl.isAlive(pid)) return false;\n\n this.processControl.signalGroup(pid, 'SIGTERM');\n const deadline = this.clock.now() + TERM_GRACE_MS;\n while (this.clock.now() < deadline) {\n if (!this.processControl.isAlive(pid)) return true;\n await this.sleep(LIVENESS_POLL_MS);\n }\n\n this.processControl.signalGroup(pid, 'SIGKILL');\n return true;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n this.clock.after(ms, resolve);\n });\n }\n}\n\n/** The spec is JSON, which has no way to carry an unset variable. */\nfunction defined(env: NodeJS.ProcessEnv): Record<string, string> {\n return Object.fromEntries(Object.entries(env).filter((entry): entry is [string, string] => entry[1] !== undefined));\n}\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
2
2
  const require_lifeline = require("../../constants/lifeline.cjs");
3
- const require_services_lifeline_client = require("../../../services/lifeline/client.cjs");
4
3
  let node_os = require("node:os");
5
4
  node_os = require_runtime.__toESM(node_os, 1);
6
5
  let node_crypto = require("node:crypto");
@@ -38,7 +37,6 @@ var NodeLifeline = class {
38
37
  server.unref();
39
38
  this.server = server;
40
39
  this.target = target;
41
- process.env[require_services_lifeline_client.LIFELINE_ENV] = target;
42
40
  return target;
43
41
  } catch (error) {
44
42
  process.emitWarning(`Could not arm the doom-runner lifeline: ${String(error)}`);
@@ -67,7 +65,6 @@ var NodeLifeline = class {
67
65
  }
68
66
  this.target = void 0;
69
67
  }
70
- if (process.env["DOOM_RUNNER_LIFELINE"]) delete process.env[require_services_lifeline_client.LIFELINE_ENV];
71
68
  }
72
69
  /** Keeps the socket beside the session it belongs to unless that path is too long to bind. */
73
70
  socketPath(sessionId) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["net","LIFELINE_ENV","path","SOCKET_NAME","createHash","os"],"sources":["../../../../src/services/lifeline/index.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport net from 'node:net';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { SOCKET_HASH_LENGTH, SOCKET_MODE, SOCKET_NAME, SOCKET_PATH_LIMIT } from '../../constants/lifeline';\nimport type { ILifeline } from '../../types/lifeline';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport { LIFELINE_ENV } from './client';\n\n/** Unix socket paths are capped at 104 bytes, so a long agent directory falls back to the temp dir. */\n\nexport class NodeLifeline implements ILifeline {\n private server: net.Server | undefined;\n private target: string | undefined;\n /** Closing a server stops it accepting; only the connections carry the death signal. */\n private readonly connections = new Set<net.Socket>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async arm(sessionId: string): Promise<string | undefined> {\n if (this.target) return this.target;\n try {\n this.paths.ensureDirectories(sessionId);\n const target = this.socketPath(sessionId);\n // A socket left behind by a SIGKILLed predecessor refuses connections, never accepts them.\n fs.rmSync(target, { force: true });\n const server = net.createServer((connection) => {\n // Runners need only the close edge, so their liveness and their data are equally irrelevant.\n connection.unref();\n connection.resume();\n this.connections.add(connection);\n connection.on('close', () => this.connections.delete(connection));\n });\n await listen(server, target);\n fs.chmodSync(target, SOCKET_MODE);\n // The lifeline must never be the reason pi stays alive.\n server.unref();\n this.server = server;\n this.target = target;\n process.env[LIFELINE_ENV] = target;\n return target;\n } catch (error) {\n // Runners stay unwatched rather than unlaunchable, so a session without a\n // lifeline degrades to running them unsupervised instead of failing.\n process.emitWarning(`Could not arm the doom-runner lifeline: ${String(error)}`);\n return undefined;\n }\n }\n\n path(): string | undefined {\n return this.target;\n }\n\n dispose(): void {\n // Dropping the connections is what a runner sees, and it is the whole point\n // of disposing: closing the server alone would leave every runner waiting.\n for (const connection of this.connections) connection.destroy();\n this.connections.clear();\n if (this.server) {\n try {\n this.server.close();\n } catch (error) {\n process.emitWarning(`Could not close the doom-runner lifeline: ${String(error)}`);\n }\n this.server = undefined;\n }\n if (this.target) {\n try {\n fs.rmSync(this.target, { force: true });\n } catch (error) {\n process.emitWarning(`Could not remove the doom-runner lifeline: ${String(error)}`);\n }\n this.target = undefined;\n }\n if (process.env[LIFELINE_ENV]) delete process.env[LIFELINE_ENV];\n }\n\n /** Keeps the socket beside the session it belongs to unless that path is too long to bind. */\n private socketPath(sessionId: string): string {\n const preferred = path.join(this.paths.stateDirectory(sessionId), SOCKET_NAME);\n if (Buffer.byteLength(preferred) <= SOCKET_PATH_LIMIT) return preferred;\n const hash = createHash('sha256').update(preferred).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return path.join(os.tmpdir(), `doom-runner-${hash}.sock`);\n }\n}\n\n/** Binding must complete before the first runner launches, so arming waits for it. */\nfunction listen(server: net.Server, target: string): Promise<void> {\n return new Promise((resolve, reject) => {\n server.once('error', reject);\n server.listen(target, () => {\n server.removeListener('error', reject);\n // Later errors would otherwise reach the process as unhandled.\n server.on('error', (error: Error) => process.emitWarning(`doom-runner lifeline failed: ${error.message}`));\n resolve();\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,IAAa,eAAb,MAA+C;CAMhB;CAL7B;CACA;;CAEA,8BAA+B,IAAI,IAAgB;CAEnD,YAAY,OAAsC;EAArB,KAAA,QAAA;CAAsB;CAEnD,MAAM,IAAI,WAAgD;EACxD,IAAI,KAAK,QAAQ,OAAO,KAAK;EAC7B,IAAI;GACF,KAAK,MAAM,kBAAkB,SAAS;GACtC,MAAM,SAAS,KAAK,WAAW,SAAS;GAExC,QAAA,QAAG,OAAO,QAAQ,EAAE,OAAO,KAAK,CAAC;GACjC,MAAM,SAASA,SAAAA,QAAI,cAAc,eAAe;IAE9C,WAAW,MAAM;IACjB,WAAW,OAAO;IAClB,KAAK,YAAY,IAAI,UAAU;IAC/B,WAAW,GAAG,eAAe,KAAK,YAAY,OAAO,UAAU,CAAC;GAClE,CAAC;GACD,MAAM,OAAO,QAAQ,MAAM;GAC3B,QAAA,QAAG,UAAU,QAAA,GAAmB;GAEhC,OAAO,MAAM;GACb,KAAK,SAAS;GACd,KAAK,SAAS;GACd,QAAQ,IAAIC,iCAAAA,gBAAgB;GAC5B,OAAO;EACT,SAAS,OAAO;GAGd,QAAQ,YAAY,2CAA2C,OAAO,KAAK,GAAG;GAC9E;EACF;CACF;CAEA,OAA2B;EACzB,OAAO,KAAK;CACd;CAEA,UAAgB;EAGd,KAAK,MAAM,cAAc,KAAK,aAAa,WAAW,QAAQ;EAC9D,KAAK,YAAY,MAAM;EACvB,IAAI,KAAK,QAAQ;GACf,IAAI;IACF,KAAK,OAAO,MAAM;GACpB,SAAS,OAAO;IACd,QAAQ,YAAY,6CAA6C,OAAO,KAAK,GAAG;GAClF;GACA,KAAK,SAAS,KAAA;EAChB;EACA,IAAI,KAAK,QAAQ;GACf,IAAI;IACF,QAAA,QAAG,OAAO,KAAK,QAAQ,EAAE,OAAO,KAAK,CAAC;GACxC,SAAS,OAAO;IACd,QAAQ,YAAY,8CAA8C,OAAO,KAAK,GAAG;GACnF;GACA,KAAK,SAAS,KAAA;EAChB;EACA,IAAI,QAAQ,IAAA,yBAAmB,OAAO,QAAQ,IAAIA,iCAAAA;CACpD;;CAGA,WAAmB,WAA2B;EAC5C,MAAM,YAAYC,UAAAA,QAAK,KAAK,KAAK,MAAM,eAAe,SAAS,GAAGC,iBAAAA,WAAW;EAC7E,IAAI,OAAO,WAAW,SAAS,KAAA,KAAwB,OAAO;EAC9D,MAAM,QAAA,GAAOC,YAAAA,WAAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAA,EAAqB;EAC7F,OAAOF,UAAAA,QAAK,KAAKG,QAAAA,QAAG,OAAO,GAAG,eAAe,KAAK,MAAM;CAC1D;AACF;;AAGA,SAAS,OAAO,QAAoB,QAA+B;CACjE,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,OAAO,cAAc;GAC1B,OAAO,eAAe,SAAS,MAAM;GAErC,OAAO,GAAG,UAAU,UAAiB,QAAQ,YAAY,gCAAgC,MAAM,SAAS,CAAC;GACzG,QAAQ;EACV,CAAC;CACH,CAAC;AACH"}
1
+ {"version":3,"file":"index.cjs","names":["net","path","SOCKET_NAME","createHash","os"],"sources":["../../../../src/services/lifeline/index.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport net from 'node:net';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { SOCKET_HASH_LENGTH, SOCKET_MODE, SOCKET_NAME, SOCKET_PATH_LIMIT } from '../../constants/lifeline';\nimport type { ILifeline } from '../../types/lifeline';\nimport type { IRunnerPaths } from '../runnerPaths/type';\n\n/** Unix socket paths are capped at 104 bytes, so a long agent directory falls back to the temp dir. */\n\nexport class NodeLifeline implements ILifeline {\n private server: net.Server | undefined;\n private target: string | undefined;\n /** Closing a server stops it accepting; only the connections carry the death signal. */\n private readonly connections = new Set<net.Socket>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async arm(sessionId: string): Promise<string | undefined> {\n if (this.target) return this.target;\n try {\n this.paths.ensureDirectories(sessionId);\n const target = this.socketPath(sessionId);\n // A socket left behind by a SIGKILLed predecessor refuses connections, never accepts them.\n fs.rmSync(target, { force: true });\n const server = net.createServer((connection) => {\n // Runners need only the close edge, so their liveness and their data are equally irrelevant.\n connection.unref();\n connection.resume();\n this.connections.add(connection);\n connection.on('close', () => this.connections.delete(connection));\n });\n await listen(server, target);\n fs.chmodSync(target, SOCKET_MODE);\n // The lifeline must never be the reason pi stays alive.\n server.unref();\n this.server = server;\n this.target = target;\n return target;\n } catch (error) {\n // Runners stay unwatched rather than unlaunchable, so a session without a\n // lifeline degrades to running them unsupervised instead of failing.\n process.emitWarning(`Could not arm the doom-runner lifeline: ${String(error)}`);\n return undefined;\n }\n }\n\n path(): string | undefined {\n return this.target;\n }\n\n dispose(): void {\n // Dropping the connections is what a runner sees, and it is the whole point\n // of disposing: closing the server alone would leave every runner waiting.\n for (const connection of this.connections) connection.destroy();\n this.connections.clear();\n if (this.server) {\n try {\n this.server.close();\n } catch (error) {\n process.emitWarning(`Could not close the doom-runner lifeline: ${String(error)}`);\n }\n this.server = undefined;\n }\n if (this.target) {\n try {\n fs.rmSync(this.target, { force: true });\n } catch (error) {\n process.emitWarning(`Could not remove the doom-runner lifeline: ${String(error)}`);\n }\n this.target = undefined;\n }\n }\n\n /** Keeps the socket beside the session it belongs to unless that path is too long to bind. */\n private socketPath(sessionId: string): string {\n const preferred = path.join(this.paths.stateDirectory(sessionId), SOCKET_NAME);\n if (Buffer.byteLength(preferred) <= SOCKET_PATH_LIMIT) return preferred;\n const hash = createHash('sha256').update(preferred).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return path.join(os.tmpdir(), `doom-runner-${hash}.sock`);\n }\n}\n\n/** Binding must complete before the first runner launches, so arming waits for it. */\nfunction listen(server: net.Server, target: string): Promise<void> {\n return new Promise((resolve, reject) => {\n server.once('error', reject);\n server.listen(target, () => {\n server.removeListener('error', reject);\n // Later errors would otherwise reach the process as unhandled.\n server.on('error', (error: Error) => process.emitWarning(`doom-runner lifeline failed: ${error.message}`));\n resolve();\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,IAAa,eAAb,MAA+C;CAMhB;CAL7B;CACA;;CAEA,8BAA+B,IAAI,IAAgB;CAEnD,YAAY,OAAsC;EAArB,KAAA,QAAA;CAAsB;CAEnD,MAAM,IAAI,WAAgD;EACxD,IAAI,KAAK,QAAQ,OAAO,KAAK;EAC7B,IAAI;GACF,KAAK,MAAM,kBAAkB,SAAS;GACtC,MAAM,SAAS,KAAK,WAAW,SAAS;GAExC,QAAA,QAAG,OAAO,QAAQ,EAAE,OAAO,KAAK,CAAC;GACjC,MAAM,SAASA,SAAAA,QAAI,cAAc,eAAe;IAE9C,WAAW,MAAM;IACjB,WAAW,OAAO;IAClB,KAAK,YAAY,IAAI,UAAU;IAC/B,WAAW,GAAG,eAAe,KAAK,YAAY,OAAO,UAAU,CAAC;GAClE,CAAC;GACD,MAAM,OAAO,QAAQ,MAAM;GAC3B,QAAA,QAAG,UAAU,QAAA,GAAmB;GAEhC,OAAO,MAAM;GACb,KAAK,SAAS;GACd,KAAK,SAAS;GACd,OAAO;EACT,SAAS,OAAO;GAGd,QAAQ,YAAY,2CAA2C,OAAO,KAAK,GAAG;GAC9E;EACF;CACF;CAEA,OAA2B;EACzB,OAAO,KAAK;CACd;CAEA,UAAgB;EAGd,KAAK,MAAM,cAAc,KAAK,aAAa,WAAW,QAAQ;EAC9D,KAAK,YAAY,MAAM;EACvB,IAAI,KAAK,QAAQ;GACf,IAAI;IACF,KAAK,OAAO,MAAM;GACpB,SAAS,OAAO;IACd,QAAQ,YAAY,6CAA6C,OAAO,KAAK,GAAG;GAClF;GACA,KAAK,SAAS,KAAA;EAChB;EACA,IAAI,KAAK,QAAQ;GACf,IAAI;IACF,QAAA,QAAG,OAAO,KAAK,QAAQ,EAAE,OAAO,KAAK,CAAC;GACxC,SAAS,OAAO;IACd,QAAQ,YAAY,8CAA8C,OAAO,KAAK,GAAG;GACnF;GACA,KAAK,SAAS,KAAA;EAChB;CACF;;CAGA,WAAmB,WAA2B;EAC5C,MAAM,YAAYC,UAAAA,QAAK,KAAK,KAAK,MAAM,eAAe,SAAS,GAAGC,iBAAAA,WAAW;EAC7E,IAAI,OAAO,WAAW,SAAS,KAAA,KAAwB,OAAO;EAC9D,MAAM,QAAA,GAAOC,YAAAA,WAAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAA,EAAqB;EAC7F,OAAOF,UAAAA,QAAK,KAAKG,QAAAA,QAAG,OAAO,GAAG,eAAe,KAAK,MAAM;CAC1D;AACF;;AAGA,SAAS,OAAO,QAAoB,QAA+B;CACjE,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,OAAO,cAAc;GAC1B,OAAO,eAAe,SAAS,MAAM;GAErC,OAAO,GAAG,UAAU,UAAiB,QAAQ,YAAY,gCAAgC,MAAM,SAAS,CAAC;GACzG,QAAQ;EACV,CAAC;CACH,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../../src/services/lifeline/index.ts"],"mappings":";;;;qBAaa,wBAAwB;mBAMN;UALrB;UACA;;mBAES;EAEjB,YAA6B,OAAO;EAE9B,IAAI,oBAAoB;EA8B9B;EAIA;;UAyBQ"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../../../src/services/lifeline/index.ts"],"mappings":";;;;qBAYa,wBAAwB;mBAMN;UALrB;UACA;;mBAES;EAEjB,YAA6B,OAAO;EAE9B,IAAI,oBAAoB;EA6B9B;EAIA;;UAwBQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/services/lifeline/index.ts"],"mappings":";;;;qBAaa,wBAAwB;mBAMN;UALrB;UACA;;mBAES;EAEjB,YAA6B,OAAO;EAE9B,IAAI,oBAAoB;EA8B9B;EAIA;;UAyBQ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/services/lifeline/index.ts"],"mappings":";;;;qBAYa,wBAAwB;mBAMN;UALrB;UACA;;mBAES;EAEjB,YAA6B,OAAO;EAE9B,IAAI,oBAAoB;EA6B9B;EAIA;;UAwBQ"}
@@ -1,5 +1,4 @@
1
1
  import { SOCKET_NAME } from "../../constants/lifeline.mjs";
2
- import { LIFELINE_ENV } from "../../../services/lifeline/client.mjs";
3
2
  import os from "node:os";
4
3
  import { createHash } from "node:crypto";
5
4
  import fs from "node:fs";
@@ -33,7 +32,6 @@ var NodeLifeline = class {
33
32
  server.unref();
34
33
  this.server = server;
35
34
  this.target = target;
36
- process.env[LIFELINE_ENV] = target;
37
35
  return target;
38
36
  } catch (error) {
39
37
  process.emitWarning(`Could not arm the doom-runner lifeline: ${String(error)}`);
@@ -62,7 +60,6 @@ var NodeLifeline = class {
62
60
  }
63
61
  this.target = void 0;
64
62
  }
65
- if (process.env["DOOM_RUNNER_LIFELINE"]) delete process.env[LIFELINE_ENV];
66
63
  }
67
64
  /** Keeps the socket beside the session it belongs to unless that path is too long to bind. */
68
65
  socketPath(sessionId) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/lifeline/index.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport net from 'node:net';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { SOCKET_HASH_LENGTH, SOCKET_MODE, SOCKET_NAME, SOCKET_PATH_LIMIT } from '../../constants/lifeline';\nimport type { ILifeline } from '../../types/lifeline';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport { LIFELINE_ENV } from './client';\n\n/** Unix socket paths are capped at 104 bytes, so a long agent directory falls back to the temp dir. */\n\nexport class NodeLifeline implements ILifeline {\n private server: net.Server | undefined;\n private target: string | undefined;\n /** Closing a server stops it accepting; only the connections carry the death signal. */\n private readonly connections = new Set<net.Socket>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async arm(sessionId: string): Promise<string | undefined> {\n if (this.target) return this.target;\n try {\n this.paths.ensureDirectories(sessionId);\n const target = this.socketPath(sessionId);\n // A socket left behind by a SIGKILLed predecessor refuses connections, never accepts them.\n fs.rmSync(target, { force: true });\n const server = net.createServer((connection) => {\n // Runners need only the close edge, so their liveness and their data are equally irrelevant.\n connection.unref();\n connection.resume();\n this.connections.add(connection);\n connection.on('close', () => this.connections.delete(connection));\n });\n await listen(server, target);\n fs.chmodSync(target, SOCKET_MODE);\n // The lifeline must never be the reason pi stays alive.\n server.unref();\n this.server = server;\n this.target = target;\n process.env[LIFELINE_ENV] = target;\n return target;\n } catch (error) {\n // Runners stay unwatched rather than unlaunchable, so a session without a\n // lifeline degrades to running them unsupervised instead of failing.\n process.emitWarning(`Could not arm the doom-runner lifeline: ${String(error)}`);\n return undefined;\n }\n }\n\n path(): string | undefined {\n return this.target;\n }\n\n dispose(): void {\n // Dropping the connections is what a runner sees, and it is the whole point\n // of disposing: closing the server alone would leave every runner waiting.\n for (const connection of this.connections) connection.destroy();\n this.connections.clear();\n if (this.server) {\n try {\n this.server.close();\n } catch (error) {\n process.emitWarning(`Could not close the doom-runner lifeline: ${String(error)}`);\n }\n this.server = undefined;\n }\n if (this.target) {\n try {\n fs.rmSync(this.target, { force: true });\n } catch (error) {\n process.emitWarning(`Could not remove the doom-runner lifeline: ${String(error)}`);\n }\n this.target = undefined;\n }\n if (process.env[LIFELINE_ENV]) delete process.env[LIFELINE_ENV];\n }\n\n /** Keeps the socket beside the session it belongs to unless that path is too long to bind. */\n private socketPath(sessionId: string): string {\n const preferred = path.join(this.paths.stateDirectory(sessionId), SOCKET_NAME);\n if (Buffer.byteLength(preferred) <= SOCKET_PATH_LIMIT) return preferred;\n const hash = createHash('sha256').update(preferred).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return path.join(os.tmpdir(), `doom-runner-${hash}.sock`);\n }\n}\n\n/** Binding must complete before the first runner launches, so arming waits for it. */\nfunction listen(server: net.Server, target: string): Promise<void> {\n return new Promise((resolve, reject) => {\n server.once('error', reject);\n server.listen(target, () => {\n server.removeListener('error', reject);\n // Later errors would otherwise reach the process as unhandled.\n server.on('error', (error: Error) => process.emitWarning(`doom-runner lifeline failed: ${error.message}`));\n resolve();\n });\n });\n}\n"],"mappings":";;;;;;;;;AAaA,IAAa,eAAb,MAA+C;CAMhB;CAL7B;CACA;;CAEA,8BAA+B,IAAI,IAAgB;CAEnD,YAAY,OAAsC;EAArB,KAAA,QAAA;CAAsB;CAEnD,MAAM,IAAI,WAAgD;EACxD,IAAI,KAAK,QAAQ,OAAO,KAAK;EAC7B,IAAI;GACF,KAAK,MAAM,kBAAkB,SAAS;GACtC,MAAM,SAAS,KAAK,WAAW,SAAS;GAExC,GAAG,OAAO,QAAQ,EAAE,OAAO,KAAK,CAAC;GACjC,MAAM,SAAS,IAAI,cAAc,eAAe;IAE9C,WAAW,MAAM;IACjB,WAAW,OAAO;IAClB,KAAK,YAAY,IAAI,UAAU;IAC/B,WAAW,GAAG,eAAe,KAAK,YAAY,OAAO,UAAU,CAAC;GAClE,CAAC;GACD,MAAM,OAAO,QAAQ,MAAM;GAC3B,GAAG,UAAU,QAAA,GAAmB;GAEhC,OAAO,MAAM;GACb,KAAK,SAAS;GACd,KAAK,SAAS;GACd,QAAQ,IAAI,gBAAgB;GAC5B,OAAO;EACT,SAAS,OAAO;GAGd,QAAQ,YAAY,2CAA2C,OAAO,KAAK,GAAG;GAC9E;EACF;CACF;CAEA,OAA2B;EACzB,OAAO,KAAK;CACd;CAEA,UAAgB;EAGd,KAAK,MAAM,cAAc,KAAK,aAAa,WAAW,QAAQ;EAC9D,KAAK,YAAY,MAAM;EACvB,IAAI,KAAK,QAAQ;GACf,IAAI;IACF,KAAK,OAAO,MAAM;GACpB,SAAS,OAAO;IACd,QAAQ,YAAY,6CAA6C,OAAO,KAAK,GAAG;GAClF;GACA,KAAK,SAAS,KAAA;EAChB;EACA,IAAI,KAAK,QAAQ;GACf,IAAI;IACF,GAAG,OAAO,KAAK,QAAQ,EAAE,OAAO,KAAK,CAAC;GACxC,SAAS,OAAO;IACd,QAAQ,YAAY,8CAA8C,OAAO,KAAK,GAAG;GACnF;GACA,KAAK,SAAS,KAAA;EAChB;EACA,IAAI,QAAQ,IAAA,yBAAmB,OAAO,QAAQ,IAAI;CACpD;;CAGA,WAAmB,WAA2B;EAC5C,MAAM,YAAY,KAAK,KAAK,KAAK,MAAM,eAAe,SAAS,GAAG,WAAW;EAC7E,IAAI,OAAO,WAAW,SAAS,KAAA,KAAwB,OAAO;EAC9D,MAAM,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAA,EAAqB;EAC7F,OAAO,KAAK,KAAK,GAAG,OAAO,GAAG,eAAe,KAAK,MAAM;CAC1D;AACF;;AAGA,SAAS,OAAO,QAAoB,QAA+B;CACjE,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,OAAO,cAAc;GAC1B,OAAO,eAAe,SAAS,MAAM;GAErC,OAAO,GAAG,UAAU,UAAiB,QAAQ,YAAY,gCAAgC,MAAM,SAAS,CAAC;GACzG,QAAQ;EACV,CAAC;CACH,CAAC;AACH"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/lifeline/index.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport net from 'node:net';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { SOCKET_HASH_LENGTH, SOCKET_MODE, SOCKET_NAME, SOCKET_PATH_LIMIT } from '../../constants/lifeline';\nimport type { ILifeline } from '../../types/lifeline';\nimport type { IRunnerPaths } from '../runnerPaths/type';\n\n/** Unix socket paths are capped at 104 bytes, so a long agent directory falls back to the temp dir. */\n\nexport class NodeLifeline implements ILifeline {\n private server: net.Server | undefined;\n private target: string | undefined;\n /** Closing a server stops it accepting; only the connections carry the death signal. */\n private readonly connections = new Set<net.Socket>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async arm(sessionId: string): Promise<string | undefined> {\n if (this.target) return this.target;\n try {\n this.paths.ensureDirectories(sessionId);\n const target = this.socketPath(sessionId);\n // A socket left behind by a SIGKILLed predecessor refuses connections, never accepts them.\n fs.rmSync(target, { force: true });\n const server = net.createServer((connection) => {\n // Runners need only the close edge, so their liveness and their data are equally irrelevant.\n connection.unref();\n connection.resume();\n this.connections.add(connection);\n connection.on('close', () => this.connections.delete(connection));\n });\n await listen(server, target);\n fs.chmodSync(target, SOCKET_MODE);\n // The lifeline must never be the reason pi stays alive.\n server.unref();\n this.server = server;\n this.target = target;\n return target;\n } catch (error) {\n // Runners stay unwatched rather than unlaunchable, so a session without a\n // lifeline degrades to running them unsupervised instead of failing.\n process.emitWarning(`Could not arm the doom-runner lifeline: ${String(error)}`);\n return undefined;\n }\n }\n\n path(): string | undefined {\n return this.target;\n }\n\n dispose(): void {\n // Dropping the connections is what a runner sees, and it is the whole point\n // of disposing: closing the server alone would leave every runner waiting.\n for (const connection of this.connections) connection.destroy();\n this.connections.clear();\n if (this.server) {\n try {\n this.server.close();\n } catch (error) {\n process.emitWarning(`Could not close the doom-runner lifeline: ${String(error)}`);\n }\n this.server = undefined;\n }\n if (this.target) {\n try {\n fs.rmSync(this.target, { force: true });\n } catch (error) {\n process.emitWarning(`Could not remove the doom-runner lifeline: ${String(error)}`);\n }\n this.target = undefined;\n }\n }\n\n /** Keeps the socket beside the session it belongs to unless that path is too long to bind. */\n private socketPath(sessionId: string): string {\n const preferred = path.join(this.paths.stateDirectory(sessionId), SOCKET_NAME);\n if (Buffer.byteLength(preferred) <= SOCKET_PATH_LIMIT) return preferred;\n const hash = createHash('sha256').update(preferred).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return path.join(os.tmpdir(), `doom-runner-${hash}.sock`);\n }\n}\n\n/** Binding must complete before the first runner launches, so arming waits for it. */\nfunction listen(server: net.Server, target: string): Promise<void> {\n return new Promise((resolve, reject) => {\n server.once('error', reject);\n server.listen(target, () => {\n server.removeListener('error', reject);\n // Later errors would otherwise reach the process as unhandled.\n server.on('error', (error: Error) => process.emitWarning(`doom-runner lifeline failed: ${error.message}`));\n resolve();\n });\n });\n}\n"],"mappings":";;;;;;;;AAYA,IAAa,eAAb,MAA+C;CAMhB;CAL7B;CACA;;CAEA,8BAA+B,IAAI,IAAgB;CAEnD,YAAY,OAAsC;EAArB,KAAA,QAAA;CAAsB;CAEnD,MAAM,IAAI,WAAgD;EACxD,IAAI,KAAK,QAAQ,OAAO,KAAK;EAC7B,IAAI;GACF,KAAK,MAAM,kBAAkB,SAAS;GACtC,MAAM,SAAS,KAAK,WAAW,SAAS;GAExC,GAAG,OAAO,QAAQ,EAAE,OAAO,KAAK,CAAC;GACjC,MAAM,SAAS,IAAI,cAAc,eAAe;IAE9C,WAAW,MAAM;IACjB,WAAW,OAAO;IAClB,KAAK,YAAY,IAAI,UAAU;IAC/B,WAAW,GAAG,eAAe,KAAK,YAAY,OAAO,UAAU,CAAC;GAClE,CAAC;GACD,MAAM,OAAO,QAAQ,MAAM;GAC3B,GAAG,UAAU,QAAA,GAAmB;GAEhC,OAAO,MAAM;GACb,KAAK,SAAS;GACd,KAAK,SAAS;GACd,OAAO;EACT,SAAS,OAAO;GAGd,QAAQ,YAAY,2CAA2C,OAAO,KAAK,GAAG;GAC9E;EACF;CACF;CAEA,OAA2B;EACzB,OAAO,KAAK;CACd;CAEA,UAAgB;EAGd,KAAK,MAAM,cAAc,KAAK,aAAa,WAAW,QAAQ;EAC9D,KAAK,YAAY,MAAM;EACvB,IAAI,KAAK,QAAQ;GACf,IAAI;IACF,KAAK,OAAO,MAAM;GACpB,SAAS,OAAO;IACd,QAAQ,YAAY,6CAA6C,OAAO,KAAK,GAAG;GAClF;GACA,KAAK,SAAS,KAAA;EAChB;EACA,IAAI,KAAK,QAAQ;GACf,IAAI;IACF,GAAG,OAAO,KAAK,QAAQ,EAAE,OAAO,KAAK,CAAC;GACxC,SAAS,OAAO;IACd,QAAQ,YAAY,8CAA8C,OAAO,KAAK,GAAG;GACnF;GACA,KAAK,SAAS,KAAA;EAChB;CACF;;CAGA,WAAmB,WAA2B;EAC5C,MAAM,YAAY,KAAK,KAAK,KAAK,MAAM,eAAe,SAAS,GAAG,WAAW;EAC7E,IAAI,OAAO,WAAW,SAAS,KAAA,KAAwB,OAAO;EAC9D,MAAM,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAA,EAAqB;EAC7F,OAAO,KAAK,KAAK,GAAG,OAAO,GAAG,eAAe,KAAK,MAAM;CAC1D;AACF;;AAGA,SAAS,OAAO,QAAoB,QAA+B;CACjE,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,OAAO,cAAc;GAC1B,OAAO,eAAe,SAAS,MAAM;GAErC,OAAO,GAAG,UAAU,UAAiB,QAAQ,YAAY,gCAAgC,MAAM,SAAS,CAAC;GACzG,QAAQ;EACV,CAAC;CACH,CAAC;AACH"}
@@ -52,7 +52,7 @@ function createRunnerDependencies(overrides) {
52
52
  const namer = memoize(() => overrides.namer ?? new require_index$12.RunnerNamer(runnerRegistry()));
53
53
  const logFile = memoize(() => overrides.logFile ?? new require_index$7.LogFile(paths()));
54
54
  const logReader = memoize(() => overrides.logReader ?? new require_index$1.LogReader());
55
- const launcher = memoize(() => overrides.launcher ?? new require_index$5.Launcher(spawner(), processControl(), logFile(), clock(), paths()));
55
+ const launcher = memoize(() => overrides.launcher ?? new require_index$5.Launcher(spawner(), processControl(), logFile(), clock(), paths(), lifeline()));
56
56
  const rmuxBackend = memoize(() => overrides.rmuxBackend ?? new require_index$9.PtyBackendChain(new require_index$3.RmuxBackend(paths()), new require_index$16.TmuxBackend(paths())));
57
57
  const rtkProcessor = memoize(() => overrides.rtkProcessor ?? new require_index$4.RtkProcessor());
58
58
  const ptyHost = memoize(() => overrides.ptyHost ?? new require_index$10.PtyHost(ptySpawner(), logFile(), processControl(), clock()));
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["SystemClock","NodeSpawner","NodeProcessControl","NodePtySpawner","RunnerPaths","NodeLifeline","createDefaultProcessRegistry","RunnerRegistry","RunnerNamer","LogFile","LogReader","Launcher","PtyBackendChain","RmuxBackend","TmuxBackend","RtkProcessor","PtyHost","BashRunService"],"sources":["../../../../src/services/runnerDependencies/index.ts"],"sourcesContent":["import { BashRunService } from '../bashRunService';\nimport { SystemClock } from '../clock';\nimport { Launcher } from '../launcher';\nimport { NodeLifeline } from '../lifeline';\nimport { LogFile } from '../logFile';\nimport { LogReader } from '../logReader';\nimport { NodeProcessControl } from '../processControl';\nimport { PtyBackendChain } from '../ptyBackendChain';\nimport { PtyHost } from '../ptyHost';\nimport { NodePtySpawner } from '../ptySpawner';\nimport { RmuxBackend } from '../rmuxBackend';\nimport { RtkProcessor } from '../rtkProcessor';\nimport { RunnerNamer } from '../runnerNamer';\nimport { RunnerPaths } from '../runnerPaths';\nimport { RunnerRegistry, createDefaultProcessRegistry } from '../runnerRegistry';\nimport { NodeSpawner } from '../spawner';\nimport { TmuxBackend } from '../tmuxBackend';\nimport type { RunnerDependencies } from './type';\n/** Build once on first access, then hand back the same instance. */\nfunction memoize<T>(build: () => T): () => T {\n let value: T | undefined;\n let built = false;\n return () => {\n if (!built) {\n value = build();\n built = true;\n }\n return value as T;\n };\n}\n\n/**\n * Builds the extension's service graph.\n *\n * A factory rather than a module-level singleton: the extension owns one graph\n * per session, and tests get a fresh one they can substitute ports on through\n * `overrides` without leaking state between cases.\n *\n * Every slot resolves lazily and memoizes, which keeps the previous container's\n * behaviour: nothing is constructed until it is asked for, so opening the SQLite\n * process registry stays deferred until something actually needs a runner.\n */\nexport function createRunnerDependencies(\n overrides: Partial<RunnerDependencies> & {\n environment: Readonly<Record<string, string | undefined>>;\n },\n): RunnerDependencies {\n const environment = overrides.environment;\n const clock = memoize(() => overrides.clock ?? new SystemClock());\n const spawner = memoize(() => overrides.spawner ?? new NodeSpawner());\n const processControl = memoize(() => overrides.processControl ?? new NodeProcessControl());\n const ptySpawner = memoize(() => overrides.ptySpawner ?? new NodePtySpawner());\n const paths = memoize(() => overrides.paths ?? new RunnerPaths());\n const lifeline = memoize(() => overrides.lifeline ?? new NodeLifeline(paths()));\n const processRegistry = memoize(() => overrides.processRegistry ?? createDefaultProcessRegistry());\n const runnerRegistry = memoize(\n () => overrides.runnerRegistry ?? new RunnerRegistry(paths(), processControl(), processRegistry(), environment),\n );\n const namer = memoize(() => overrides.namer ?? new RunnerNamer(runnerRegistry()));\n const logFile = memoize(() => overrides.logFile ?? new LogFile(paths()));\n const logReader = memoize(() => overrides.logReader ?? new LogReader());\n const launcher = memoize(\n () => overrides.launcher ?? new Launcher(spawner(), processControl(), logFile(), clock(), paths()),\n );\n const rmuxBackend = memoize(\n () => overrides.rmuxBackend ?? new PtyBackendChain(new RmuxBackend(paths()), new TmuxBackend(paths())),\n );\n const rtkProcessor = memoize(() => overrides.rtkProcessor ?? new RtkProcessor());\n const ptyHost = memoize(() => overrides.ptyHost ?? new PtyHost(ptySpawner(), logFile(), processControl(), clock()));\n const bashRunService = memoize(\n () =>\n overrides.bashRunService ??\n new BashRunService(launcher(), rmuxBackend(), namer(), runnerRegistry(), clock(), rtkProcessor()),\n );\n\n return {\n environment,\n get clock() {\n return clock();\n },\n get spawner() {\n return spawner();\n },\n get processControl() {\n return processControl();\n },\n get ptySpawner() {\n return ptySpawner();\n },\n get paths() {\n return paths();\n },\n get lifeline() {\n return lifeline();\n },\n get processRegistry() {\n return processRegistry();\n },\n get runnerRegistry() {\n return runnerRegistry();\n },\n get namer() {\n return namer();\n },\n get logFile() {\n return logFile();\n },\n get logReader() {\n return logReader();\n },\n get launcher() {\n return launcher();\n },\n get rmuxBackend() {\n return rmuxBackend();\n },\n get rtkProcessor() {\n return rtkProcessor();\n },\n get ptyHost() {\n return ptyHost();\n },\n get bashRunService() {\n return bashRunService();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmBA,SAAS,QAAW,OAAyB;CAC3C,IAAI;CACJ,IAAI,QAAQ;CACZ,aAAa;EACX,IAAI,CAAC,OAAO;GACV,QAAQ,MAAM;GACd,QAAQ;EACV;EACA,OAAO;CACT;AACF;;;;;;;;;;;;AAaA,SAAgB,yBACd,WAGoB;CACpB,MAAM,cAAc,UAAU;CAC9B,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAIA,gBAAAA,YAAY,CAAC;CAChE,MAAM,UAAU,cAAc,UAAU,WAAW,IAAIC,iBAAAA,YAAY,CAAC;CACpE,MAAM,iBAAiB,cAAc,UAAU,kBAAkB,IAAIC,gBAAAA,mBAAmB,CAAC;CACzF,MAAM,aAAa,cAAc,UAAU,cAAc,IAAIC,iBAAAA,eAAe,CAAC;CAC7E,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAIC,iBAAAA,YAAY,CAAC;CAChE,MAAM,WAAW,cAAc,UAAU,YAAY,IAAIC,gBAAAA,aAAa,MAAM,CAAC,CAAC;CAC9E,MAAM,kBAAkB,cAAc,UAAU,mBAAmBC,iBAAAA,6BAA6B,CAAC;CACjG,MAAM,iBAAiB,cACf,UAAU,kBAAkB,IAAIC,iBAAAA,eAAe,MAAM,GAAG,eAAe,GAAG,gBAAgB,GAAG,WAAW,CAChH;CACA,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAIC,iBAAAA,YAAY,eAAe,CAAC,CAAC;CAChF,MAAM,UAAU,cAAc,UAAU,WAAW,IAAIC,gBAAAA,QAAQ,MAAM,CAAC,CAAC;CACvE,MAAM,YAAY,cAAc,UAAU,aAAa,IAAIC,gBAAAA,UAAU,CAAC;CACtE,MAAM,WAAW,cACT,UAAU,YAAY,IAAIC,gBAAAA,SAAS,QAAQ,GAAG,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC,CACnG;CACA,MAAM,cAAc,cACZ,UAAU,eAAe,IAAIC,gBAAAA,gBAAgB,IAAIC,gBAAAA,YAAY,MAAM,CAAC,GAAG,IAAIC,iBAAAA,YAAY,MAAM,CAAC,CAAC,CACvG;CACA,MAAM,eAAe,cAAc,UAAU,gBAAgB,IAAIC,gBAAAA,aAAa,CAAC;CAC/E,MAAM,UAAU,cAAc,UAAU,WAAW,IAAIC,iBAAAA,QAAQ,WAAW,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,CAAC,CAAC;CAClH,MAAM,iBAAiB,cAEnB,UAAU,kBACV,IAAIC,cAAAA,eAAe,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,aAAa,CAAC,CACpG;CAEA,OAAO;EACL;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;EACA,IAAI,aAAa;GACf,OAAO,WAAW;EACpB;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,WAAW;GACb,OAAO,SAAS;EAClB;EACA,IAAI,kBAAkB;GACpB,OAAO,gBAAgB;EACzB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,YAAY;GACd,OAAO,UAAU;EACnB;EACA,IAAI,WAAW;GACb,OAAO,SAAS;EAClB;EACA,IAAI,cAAc;GAChB,OAAO,YAAY;EACrB;EACA,IAAI,eAAe;GACjB,OAAO,aAAa;EACtB;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;CACF;AACF"}
1
+ {"version":3,"file":"index.cjs","names":["SystemClock","NodeSpawner","NodeProcessControl","NodePtySpawner","RunnerPaths","NodeLifeline","createDefaultProcessRegistry","RunnerRegistry","RunnerNamer","LogFile","LogReader","Launcher","PtyBackendChain","RmuxBackend","TmuxBackend","RtkProcessor","PtyHost","BashRunService"],"sources":["../../../../src/services/runnerDependencies/index.ts"],"sourcesContent":["import { BashRunService } from '../bashRunService';\nimport { SystemClock } from '../clock';\nimport { Launcher } from '../launcher';\nimport { NodeLifeline } from '../lifeline';\nimport { LogFile } from '../logFile';\nimport { LogReader } from '../logReader';\nimport { NodeProcessControl } from '../processControl';\nimport { PtyBackendChain } from '../ptyBackendChain';\nimport { PtyHost } from '../ptyHost';\nimport { NodePtySpawner } from '../ptySpawner';\nimport { RmuxBackend } from '../rmuxBackend';\nimport { RtkProcessor } from '../rtkProcessor';\nimport { RunnerNamer } from '../runnerNamer';\nimport { RunnerPaths } from '../runnerPaths';\nimport { RunnerRegistry, createDefaultProcessRegistry } from '../runnerRegistry';\nimport { NodeSpawner } from '../spawner';\nimport { TmuxBackend } from '../tmuxBackend';\nimport type { RunnerDependencies } from './type';\n/** Build once on first access, then hand back the same instance. */\nfunction memoize<T>(build: () => T): () => T {\n let value: T | undefined;\n let built = false;\n return () => {\n if (!built) {\n value = build();\n built = true;\n }\n return value as T;\n };\n}\n\n/**\n * Builds the extension's service graph.\n *\n * A factory rather than a module-level singleton: the extension owns one graph\n * per session, and tests get a fresh one they can substitute ports on through\n * `overrides` without leaking state between cases.\n *\n * Every slot resolves lazily and memoizes, which keeps the previous container's\n * behaviour: nothing is constructed until it is asked for, so opening the SQLite\n * process registry stays deferred until something actually needs a runner.\n */\nexport function createRunnerDependencies(\n overrides: Partial<RunnerDependencies> & {\n environment: Readonly<Record<string, string | undefined>>;\n },\n): RunnerDependencies {\n const environment = overrides.environment;\n const clock = memoize(() => overrides.clock ?? new SystemClock());\n const spawner = memoize(() => overrides.spawner ?? new NodeSpawner());\n const processControl = memoize(() => overrides.processControl ?? new NodeProcessControl());\n const ptySpawner = memoize(() => overrides.ptySpawner ?? new NodePtySpawner());\n const paths = memoize(() => overrides.paths ?? new RunnerPaths());\n const lifeline = memoize(() => overrides.lifeline ?? new NodeLifeline(paths()));\n const processRegistry = memoize(() => overrides.processRegistry ?? createDefaultProcessRegistry());\n const runnerRegistry = memoize(\n () => overrides.runnerRegistry ?? new RunnerRegistry(paths(), processControl(), processRegistry(), environment),\n );\n const namer = memoize(() => overrides.namer ?? new RunnerNamer(runnerRegistry()));\n const logFile = memoize(() => overrides.logFile ?? new LogFile(paths()));\n const logReader = memoize(() => overrides.logReader ?? new LogReader());\n const launcher = memoize(\n () => overrides.launcher ?? new Launcher(spawner(), processControl(), logFile(), clock(), paths(), lifeline()),\n );\n const rmuxBackend = memoize(\n () => overrides.rmuxBackend ?? new PtyBackendChain(new RmuxBackend(paths()), new TmuxBackend(paths())),\n );\n const rtkProcessor = memoize(() => overrides.rtkProcessor ?? new RtkProcessor());\n const ptyHost = memoize(() => overrides.ptyHost ?? new PtyHost(ptySpawner(), logFile(), processControl(), clock()));\n const bashRunService = memoize(\n () =>\n overrides.bashRunService ??\n new BashRunService(launcher(), rmuxBackend(), namer(), runnerRegistry(), clock(), rtkProcessor()),\n );\n\n return {\n environment,\n get clock() {\n return clock();\n },\n get spawner() {\n return spawner();\n },\n get processControl() {\n return processControl();\n },\n get ptySpawner() {\n return ptySpawner();\n },\n get paths() {\n return paths();\n },\n get lifeline() {\n return lifeline();\n },\n get processRegistry() {\n return processRegistry();\n },\n get runnerRegistry() {\n return runnerRegistry();\n },\n get namer() {\n return namer();\n },\n get logFile() {\n return logFile();\n },\n get logReader() {\n return logReader();\n },\n get launcher() {\n return launcher();\n },\n get rmuxBackend() {\n return rmuxBackend();\n },\n get rtkProcessor() {\n return rtkProcessor();\n },\n get ptyHost() {\n return ptyHost();\n },\n get bashRunService() {\n return bashRunService();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmBA,SAAS,QAAW,OAAyB;CAC3C,IAAI;CACJ,IAAI,QAAQ;CACZ,aAAa;EACX,IAAI,CAAC,OAAO;GACV,QAAQ,MAAM;GACd,QAAQ;EACV;EACA,OAAO;CACT;AACF;;;;;;;;;;;;AAaA,SAAgB,yBACd,WAGoB;CACpB,MAAM,cAAc,UAAU;CAC9B,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAIA,gBAAAA,YAAY,CAAC;CAChE,MAAM,UAAU,cAAc,UAAU,WAAW,IAAIC,iBAAAA,YAAY,CAAC;CACpE,MAAM,iBAAiB,cAAc,UAAU,kBAAkB,IAAIC,gBAAAA,mBAAmB,CAAC;CACzF,MAAM,aAAa,cAAc,UAAU,cAAc,IAAIC,iBAAAA,eAAe,CAAC;CAC7E,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAIC,iBAAAA,YAAY,CAAC;CAChE,MAAM,WAAW,cAAc,UAAU,YAAY,IAAIC,gBAAAA,aAAa,MAAM,CAAC,CAAC;CAC9E,MAAM,kBAAkB,cAAc,UAAU,mBAAmBC,iBAAAA,6BAA6B,CAAC;CACjG,MAAM,iBAAiB,cACf,UAAU,kBAAkB,IAAIC,iBAAAA,eAAe,MAAM,GAAG,eAAe,GAAG,gBAAgB,GAAG,WAAW,CAChH;CACA,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAIC,iBAAAA,YAAY,eAAe,CAAC,CAAC;CAChF,MAAM,UAAU,cAAc,UAAU,WAAW,IAAIC,gBAAAA,QAAQ,MAAM,CAAC,CAAC;CACvE,MAAM,YAAY,cAAc,UAAU,aAAa,IAAIC,gBAAAA,UAAU,CAAC;CACtE,MAAM,WAAW,cACT,UAAU,YAAY,IAAIC,gBAAAA,SAAS,QAAQ,GAAG,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAC/G;CACA,MAAM,cAAc,cACZ,UAAU,eAAe,IAAIC,gBAAAA,gBAAgB,IAAIC,gBAAAA,YAAY,MAAM,CAAC,GAAG,IAAIC,iBAAAA,YAAY,MAAM,CAAC,CAAC,CACvG;CACA,MAAM,eAAe,cAAc,UAAU,gBAAgB,IAAIC,gBAAAA,aAAa,CAAC;CAC/E,MAAM,UAAU,cAAc,UAAU,WAAW,IAAIC,iBAAAA,QAAQ,WAAW,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,CAAC,CAAC;CAClH,MAAM,iBAAiB,cAEnB,UAAU,kBACV,IAAIC,cAAAA,eAAe,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,aAAa,CAAC,CACpG;CAEA,OAAO;EACL;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;EACA,IAAI,aAAa;GACf,OAAO,WAAW;EACpB;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,WAAW;GACb,OAAO,SAAS;EAClB;EACA,IAAI,kBAAkB;GACpB,OAAO,gBAAgB;EACzB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,YAAY;GACd,OAAO,UAAU;EACnB;EACA,IAAI,WAAW;GACb,OAAO,SAAS;EAClB;EACA,IAAI,cAAc;GAChB,OAAO,YAAY;EACrB;EACA,IAAI,eAAe;GACjB,OAAO,aAAa;EACtB;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;CACF;AACF"}
@@ -52,7 +52,7 @@ function createRunnerDependencies(overrides) {
52
52
  const namer = memoize(() => overrides.namer ?? new RunnerNamer(runnerRegistry()));
53
53
  const logFile = memoize(() => overrides.logFile ?? new LogFile(paths()));
54
54
  const logReader = memoize(() => overrides.logReader ?? new LogReader());
55
- const launcher = memoize(() => overrides.launcher ?? new Launcher(spawner(), processControl(), logFile(), clock(), paths()));
55
+ const launcher = memoize(() => overrides.launcher ?? new Launcher(spawner(), processControl(), logFile(), clock(), paths(), lifeline()));
56
56
  const rmuxBackend = memoize(() => overrides.rmuxBackend ?? new PtyBackendChain(new RmuxBackend(paths()), new TmuxBackend(paths())));
57
57
  const rtkProcessor = memoize(() => overrides.rtkProcessor ?? new RtkProcessor());
58
58
  const ptyHost = memoize(() => overrides.ptyHost ?? new PtyHost(ptySpawner(), logFile(), processControl(), clock()));
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/runnerDependencies/index.ts"],"sourcesContent":["import { BashRunService } from '../bashRunService';\nimport { SystemClock } from '../clock';\nimport { Launcher } from '../launcher';\nimport { NodeLifeline } from '../lifeline';\nimport { LogFile } from '../logFile';\nimport { LogReader } from '../logReader';\nimport { NodeProcessControl } from '../processControl';\nimport { PtyBackendChain } from '../ptyBackendChain';\nimport { PtyHost } from '../ptyHost';\nimport { NodePtySpawner } from '../ptySpawner';\nimport { RmuxBackend } from '../rmuxBackend';\nimport { RtkProcessor } from '../rtkProcessor';\nimport { RunnerNamer } from '../runnerNamer';\nimport { RunnerPaths } from '../runnerPaths';\nimport { RunnerRegistry, createDefaultProcessRegistry } from '../runnerRegistry';\nimport { NodeSpawner } from '../spawner';\nimport { TmuxBackend } from '../tmuxBackend';\nimport type { RunnerDependencies } from './type';\n/** Build once on first access, then hand back the same instance. */\nfunction memoize<T>(build: () => T): () => T {\n let value: T | undefined;\n let built = false;\n return () => {\n if (!built) {\n value = build();\n built = true;\n }\n return value as T;\n };\n}\n\n/**\n * Builds the extension's service graph.\n *\n * A factory rather than a module-level singleton: the extension owns one graph\n * per session, and tests get a fresh one they can substitute ports on through\n * `overrides` without leaking state between cases.\n *\n * Every slot resolves lazily and memoizes, which keeps the previous container's\n * behaviour: nothing is constructed until it is asked for, so opening the SQLite\n * process registry stays deferred until something actually needs a runner.\n */\nexport function createRunnerDependencies(\n overrides: Partial<RunnerDependencies> & {\n environment: Readonly<Record<string, string | undefined>>;\n },\n): RunnerDependencies {\n const environment = overrides.environment;\n const clock = memoize(() => overrides.clock ?? new SystemClock());\n const spawner = memoize(() => overrides.spawner ?? new NodeSpawner());\n const processControl = memoize(() => overrides.processControl ?? new NodeProcessControl());\n const ptySpawner = memoize(() => overrides.ptySpawner ?? new NodePtySpawner());\n const paths = memoize(() => overrides.paths ?? new RunnerPaths());\n const lifeline = memoize(() => overrides.lifeline ?? new NodeLifeline(paths()));\n const processRegistry = memoize(() => overrides.processRegistry ?? createDefaultProcessRegistry());\n const runnerRegistry = memoize(\n () => overrides.runnerRegistry ?? new RunnerRegistry(paths(), processControl(), processRegistry(), environment),\n );\n const namer = memoize(() => overrides.namer ?? new RunnerNamer(runnerRegistry()));\n const logFile = memoize(() => overrides.logFile ?? new LogFile(paths()));\n const logReader = memoize(() => overrides.logReader ?? new LogReader());\n const launcher = memoize(\n () => overrides.launcher ?? new Launcher(spawner(), processControl(), logFile(), clock(), paths()),\n );\n const rmuxBackend = memoize(\n () => overrides.rmuxBackend ?? new PtyBackendChain(new RmuxBackend(paths()), new TmuxBackend(paths())),\n );\n const rtkProcessor = memoize(() => overrides.rtkProcessor ?? new RtkProcessor());\n const ptyHost = memoize(() => overrides.ptyHost ?? new PtyHost(ptySpawner(), logFile(), processControl(), clock()));\n const bashRunService = memoize(\n () =>\n overrides.bashRunService ??\n new BashRunService(launcher(), rmuxBackend(), namer(), runnerRegistry(), clock(), rtkProcessor()),\n );\n\n return {\n environment,\n get clock() {\n return clock();\n },\n get spawner() {\n return spawner();\n },\n get processControl() {\n return processControl();\n },\n get ptySpawner() {\n return ptySpawner();\n },\n get paths() {\n return paths();\n },\n get lifeline() {\n return lifeline();\n },\n get processRegistry() {\n return processRegistry();\n },\n get runnerRegistry() {\n return runnerRegistry();\n },\n get namer() {\n return namer();\n },\n get logFile() {\n return logFile();\n },\n get logReader() {\n return logReader();\n },\n get launcher() {\n return launcher();\n },\n get rmuxBackend() {\n return rmuxBackend();\n },\n get rtkProcessor() {\n return rtkProcessor();\n },\n get ptyHost() {\n return ptyHost();\n },\n get bashRunService() {\n return bashRunService();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmBA,SAAS,QAAW,OAAyB;CAC3C,IAAI;CACJ,IAAI,QAAQ;CACZ,aAAa;EACX,IAAI,CAAC,OAAO;GACV,QAAQ,MAAM;GACd,QAAQ;EACV;EACA,OAAO;CACT;AACF;;;;;;;;;;;;AAaA,SAAgB,yBACd,WAGoB;CACpB,MAAM,cAAc,UAAU;CAC9B,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAI,YAAY,CAAC;CAChE,MAAM,UAAU,cAAc,UAAU,WAAW,IAAI,YAAY,CAAC;CACpE,MAAM,iBAAiB,cAAc,UAAU,kBAAkB,IAAI,mBAAmB,CAAC;CACzF,MAAM,aAAa,cAAc,UAAU,cAAc,IAAI,eAAe,CAAC;CAC7E,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAI,YAAY,CAAC;CAChE,MAAM,WAAW,cAAc,UAAU,YAAY,IAAI,aAAa,MAAM,CAAC,CAAC;CAC9E,MAAM,kBAAkB,cAAc,UAAU,mBAAmB,6BAA6B,CAAC;CACjG,MAAM,iBAAiB,cACf,UAAU,kBAAkB,IAAI,eAAe,MAAM,GAAG,eAAe,GAAG,gBAAgB,GAAG,WAAW,CAChH;CACA,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAI,YAAY,eAAe,CAAC,CAAC;CAChF,MAAM,UAAU,cAAc,UAAU,WAAW,IAAI,QAAQ,MAAM,CAAC,CAAC;CACvE,MAAM,YAAY,cAAc,UAAU,aAAa,IAAI,UAAU,CAAC;CACtE,MAAM,WAAW,cACT,UAAU,YAAY,IAAI,SAAS,QAAQ,GAAG,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC,CACnG;CACA,MAAM,cAAc,cACZ,UAAU,eAAe,IAAI,gBAAgB,IAAI,YAAY,MAAM,CAAC,GAAG,IAAI,YAAY,MAAM,CAAC,CAAC,CACvG;CACA,MAAM,eAAe,cAAc,UAAU,gBAAgB,IAAI,aAAa,CAAC;CAC/E,MAAM,UAAU,cAAc,UAAU,WAAW,IAAI,QAAQ,WAAW,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,CAAC,CAAC;CAClH,MAAM,iBAAiB,cAEnB,UAAU,kBACV,IAAI,eAAe,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,aAAa,CAAC,CACpG;CAEA,OAAO;EACL;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;EACA,IAAI,aAAa;GACf,OAAO,WAAW;EACpB;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,WAAW;GACb,OAAO,SAAS;EAClB;EACA,IAAI,kBAAkB;GACpB,OAAO,gBAAgB;EACzB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,YAAY;GACd,OAAO,UAAU;EACnB;EACA,IAAI,WAAW;GACb,OAAO,SAAS;EAClB;EACA,IAAI,cAAc;GAChB,OAAO,YAAY;EACrB;EACA,IAAI,eAAe;GACjB,OAAO,aAAa;EACtB;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;CACF;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/runnerDependencies/index.ts"],"sourcesContent":["import { BashRunService } from '../bashRunService';\nimport { SystemClock } from '../clock';\nimport { Launcher } from '../launcher';\nimport { NodeLifeline } from '../lifeline';\nimport { LogFile } from '../logFile';\nimport { LogReader } from '../logReader';\nimport { NodeProcessControl } from '../processControl';\nimport { PtyBackendChain } from '../ptyBackendChain';\nimport { PtyHost } from '../ptyHost';\nimport { NodePtySpawner } from '../ptySpawner';\nimport { RmuxBackend } from '../rmuxBackend';\nimport { RtkProcessor } from '../rtkProcessor';\nimport { RunnerNamer } from '../runnerNamer';\nimport { RunnerPaths } from '../runnerPaths';\nimport { RunnerRegistry, createDefaultProcessRegistry } from '../runnerRegistry';\nimport { NodeSpawner } from '../spawner';\nimport { TmuxBackend } from '../tmuxBackend';\nimport type { RunnerDependencies } from './type';\n/** Build once on first access, then hand back the same instance. */\nfunction memoize<T>(build: () => T): () => T {\n let value: T | undefined;\n let built = false;\n return () => {\n if (!built) {\n value = build();\n built = true;\n }\n return value as T;\n };\n}\n\n/**\n * Builds the extension's service graph.\n *\n * A factory rather than a module-level singleton: the extension owns one graph\n * per session, and tests get a fresh one they can substitute ports on through\n * `overrides` without leaking state between cases.\n *\n * Every slot resolves lazily and memoizes, which keeps the previous container's\n * behaviour: nothing is constructed until it is asked for, so opening the SQLite\n * process registry stays deferred until something actually needs a runner.\n */\nexport function createRunnerDependencies(\n overrides: Partial<RunnerDependencies> & {\n environment: Readonly<Record<string, string | undefined>>;\n },\n): RunnerDependencies {\n const environment = overrides.environment;\n const clock = memoize(() => overrides.clock ?? new SystemClock());\n const spawner = memoize(() => overrides.spawner ?? new NodeSpawner());\n const processControl = memoize(() => overrides.processControl ?? new NodeProcessControl());\n const ptySpawner = memoize(() => overrides.ptySpawner ?? new NodePtySpawner());\n const paths = memoize(() => overrides.paths ?? new RunnerPaths());\n const lifeline = memoize(() => overrides.lifeline ?? new NodeLifeline(paths()));\n const processRegistry = memoize(() => overrides.processRegistry ?? createDefaultProcessRegistry());\n const runnerRegistry = memoize(\n () => overrides.runnerRegistry ?? new RunnerRegistry(paths(), processControl(), processRegistry(), environment),\n );\n const namer = memoize(() => overrides.namer ?? new RunnerNamer(runnerRegistry()));\n const logFile = memoize(() => overrides.logFile ?? new LogFile(paths()));\n const logReader = memoize(() => overrides.logReader ?? new LogReader());\n const launcher = memoize(\n () => overrides.launcher ?? new Launcher(spawner(), processControl(), logFile(), clock(), paths(), lifeline()),\n );\n const rmuxBackend = memoize(\n () => overrides.rmuxBackend ?? new PtyBackendChain(new RmuxBackend(paths()), new TmuxBackend(paths())),\n );\n const rtkProcessor = memoize(() => overrides.rtkProcessor ?? new RtkProcessor());\n const ptyHost = memoize(() => overrides.ptyHost ?? new PtyHost(ptySpawner(), logFile(), processControl(), clock()));\n const bashRunService = memoize(\n () =>\n overrides.bashRunService ??\n new BashRunService(launcher(), rmuxBackend(), namer(), runnerRegistry(), clock(), rtkProcessor()),\n );\n\n return {\n environment,\n get clock() {\n return clock();\n },\n get spawner() {\n return spawner();\n },\n get processControl() {\n return processControl();\n },\n get ptySpawner() {\n return ptySpawner();\n },\n get paths() {\n return paths();\n },\n get lifeline() {\n return lifeline();\n },\n get processRegistry() {\n return processRegistry();\n },\n get runnerRegistry() {\n return runnerRegistry();\n },\n get namer() {\n return namer();\n },\n get logFile() {\n return logFile();\n },\n get logReader() {\n return logReader();\n },\n get launcher() {\n return launcher();\n },\n get rmuxBackend() {\n return rmuxBackend();\n },\n get rtkProcessor() {\n return rtkProcessor();\n },\n get ptyHost() {\n return ptyHost();\n },\n get bashRunService() {\n return bashRunService();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmBA,SAAS,QAAW,OAAyB;CAC3C,IAAI;CACJ,IAAI,QAAQ;CACZ,aAAa;EACX,IAAI,CAAC,OAAO;GACV,QAAQ,MAAM;GACd,QAAQ;EACV;EACA,OAAO;CACT;AACF;;;;;;;;;;;;AAaA,SAAgB,yBACd,WAGoB;CACpB,MAAM,cAAc,UAAU;CAC9B,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAI,YAAY,CAAC;CAChE,MAAM,UAAU,cAAc,UAAU,WAAW,IAAI,YAAY,CAAC;CACpE,MAAM,iBAAiB,cAAc,UAAU,kBAAkB,IAAI,mBAAmB,CAAC;CACzF,MAAM,aAAa,cAAc,UAAU,cAAc,IAAI,eAAe,CAAC;CAC7E,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAI,YAAY,CAAC;CAChE,MAAM,WAAW,cAAc,UAAU,YAAY,IAAI,aAAa,MAAM,CAAC,CAAC;CAC9E,MAAM,kBAAkB,cAAc,UAAU,mBAAmB,6BAA6B,CAAC;CACjG,MAAM,iBAAiB,cACf,UAAU,kBAAkB,IAAI,eAAe,MAAM,GAAG,eAAe,GAAG,gBAAgB,GAAG,WAAW,CAChH;CACA,MAAM,QAAQ,cAAc,UAAU,SAAS,IAAI,YAAY,eAAe,CAAC,CAAC;CAChF,MAAM,UAAU,cAAc,UAAU,WAAW,IAAI,QAAQ,MAAM,CAAC,CAAC;CACvE,MAAM,YAAY,cAAc,UAAU,aAAa,IAAI,UAAU,CAAC;CACtE,MAAM,WAAW,cACT,UAAU,YAAY,IAAI,SAAS,QAAQ,GAAG,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAC/G;CACA,MAAM,cAAc,cACZ,UAAU,eAAe,IAAI,gBAAgB,IAAI,YAAY,MAAM,CAAC,GAAG,IAAI,YAAY,MAAM,CAAC,CAAC,CACvG;CACA,MAAM,eAAe,cAAc,UAAU,gBAAgB,IAAI,aAAa,CAAC;CAC/E,MAAM,UAAU,cAAc,UAAU,WAAW,IAAI,QAAQ,WAAW,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,CAAC,CAAC;CAClH,MAAM,iBAAiB,cAEnB,UAAU,kBACV,IAAI,eAAe,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,aAAa,CAAC,CACpG;CAEA,OAAO;EACL;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;EACA,IAAI,aAAa;GACf,OAAO,WAAW;EACpB;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,WAAW;GACb,OAAO,SAAS;EAClB;EACA,IAAI,kBAAkB;GACpB,OAAO,gBAAgB;EACzB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;EACA,IAAI,QAAQ;GACV,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,YAAY;GACd,OAAO,UAAU;EACnB;EACA,IAAI,WAAW;GACb,OAAO,SAAS;EAClB;EACA,IAAI,cAAc;GAChB,OAAO,YAAY;EACrB;EACA,IAAI,eAAe;GACjB,OAAO,aAAa;EACtB;EACA,IAAI,UAAU;GACZ,OAAO,QAAQ;EACjB;EACA,IAAI,iBAAiB;GACnB,OAAO,eAAe;EACxB;CACF;AACF"}
@@ -10,30 +10,27 @@ function createRunnerServerRuntime(container, directEvents) {
10
10
  let runtimeDisposal;
11
11
  let backgroundWorkItems = [];
12
12
  let backgroundWorkProvider;
13
- const startSupervision = (executionContext) => {
14
- sessionId ??= executionContext.sessionId;
13
+ const ensureSession = (requestedSessionId) => {
14
+ if (sessionId !== void 0 && sessionId !== requestedSessionId) throw new Error(`Runner runtime belongs to session ${sessionId}.`);
15
+ sessionId ??= requestedSessionId;
15
16
  supervision ??= (async () => {
16
- try {
17
- container.paths.setSessionId(executionContext.sessionId);
18
- await container.lifeline.arm(executionContext.sessionId);
19
- const reconciled = await require_index.reconcileActiveRunners({
20
- registry: container.runnerRegistry,
21
- launcher: container.launcher,
22
- rmuxBackend: container.rmuxBackend,
23
- processControl: container.processControl,
24
- currentHostPid: process.pid,
25
- startup: true
26
- });
27
- for (const error of reconciled.errors) process.emitWarning(error);
28
- } catch (error) {
29
- process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`);
30
- }
17
+ container.paths.setSessionId(requestedSessionId);
18
+ if (await container.lifeline.arm(requestedSessionId) === void 0) throw new Error("Could not arm runner supervision lifeline.");
19
+ const reconciled = await require_index.reconcileActiveRunners({
20
+ registry: container.runnerRegistry,
21
+ launcher: container.launcher,
22
+ rmuxBackend: container.rmuxBackend,
23
+ processControl: container.processControl,
24
+ currentHostPid: process.pid,
25
+ startup: true
26
+ });
27
+ for (const error of reconciled.errors) process.emitWarning(error);
31
28
  })();
32
29
  return supervision;
33
30
  };
34
31
  const disposeRuntime = () => {
35
32
  runtimeDisposal ??= (async () => {
36
- await supervision;
33
+ await supervision?.catch((error) => process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`));
37
34
  const ownedSessionId = sessionId;
38
35
  if (ownedSessionId === void 0) return;
39
36
  const records = await container.runnerRegistry.listBySession(ownedSessionId).catch(() => []);
@@ -99,7 +96,7 @@ function createRunnerServerRuntime(container, directEvents) {
99
96
  activity: {
100
97
  name: "runner",
101
98
  start: async (executionContext) => {
102
- await startSupervision(executionContext);
99
+ await ensureSession(executionContext.sessionId);
103
100
  const unsubscribe = container.runnerRegistry.subscribe(() => requestPublish(executionContext.sessionId), executionContext.sessionId);
104
101
  unsubscribeRunnerUpdates = unsubscribe;
105
102
  requestPublish(executionContext.sessionId);
@@ -110,6 +107,7 @@ function createRunnerServerRuntime(container, directEvents) {
110
107
  };
111
108
  }
112
109
  },
110
+ ensureSession,
113
111
  backgroundWorkPlugin: (context) => {
114
112
  context.inject([_agimon_ai_doompi_core_background_work.DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {
115
113
  const service = (0, _agimon_ai_doompi_core_background_work.readDoomBackgroundWorkService)(serviceContext);
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["reconcileActiveRunners","stopRunnerProcess","RUNNER_RUNS_TYPE","presentRunnerRuns","DOOM_BACKGROUND_WORK_SERVICE","readDoomBackgroundWorkService","BACKGROUND_WORK_PROVIDER"],"sources":["../../../../src/services/runnerServerRuntime/index.ts"],"sourcesContent":["import {\n DOOM_BACKGROUND_WORK_SERVICE,\n readDoomBackgroundWorkService,\n type BackgroundProviderWorkItem,\n type BackgroundWorkProviderHandle,\n} from '@agimon-ai/doompi-core/background-work';\nimport type { DoomHeadlessActivity, DoomHeadlessExecutionContext } from '@agimon-ai/doompi-core/headless';\nimport type { DoomServerHostService } from '@agimon-ai/doompi-core/server-facet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { BACKGROUND_WORK_PROVIDER } from '../../constants/runnerRuntime';\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport { reconcileActiveRunners, stopRunnerProcess } from '../reconcile';\nimport type { RunnerDependencies } from '../runnerDependencies/type';\nimport { presentRunnerRuns } from '../webRunnerRuns';\n\nexport interface RunnerServerRuntime {\n readonly container: RunnerDependencies;\n readonly activity: DoomHeadlessActivity;\n readonly backgroundWorkPlugin: (context: Context) => void;\n dispose(this: void): Promise<void>;\n}\nexport function createRunnerServerRuntime(\n container: RunnerDependencies,\n directEvents: NonNullable<DoomServerHostService['context']['directEvents']>,\n): RunnerServerRuntime {\n let sessionId: string | undefined;\n let supervision: Promise<void> | undefined;\n let runtimeDisposal: Promise<void> | undefined;\n let backgroundWorkItems: BackgroundProviderWorkItem[] = [];\n let backgroundWorkProvider: BackgroundWorkProviderHandle | undefined;\n\n // The activity is optional and source-gated. The retained facet owns the\n // session runtime, so disabling the activity must not tear down its jobs.\n const startSupervision = (executionContext: DoomHeadlessExecutionContext): Promise<void> => {\n sessionId ??= executionContext.sessionId;\n supervision ??= (async () => {\n try {\n container.paths.setSessionId(executionContext.sessionId);\n await container.lifeline.arm(executionContext.sessionId);\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: true,\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n } catch (error) {\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`);\n }\n })();\n return supervision;\n };\n\n const disposeRuntime = (): Promise<void> => {\n runtimeDisposal ??= (async () => {\n await supervision;\n const ownedSessionId = sessionId;\n if (ownedSessionId === undefined) return;\n const records = await container.runnerRegistry.listBySession(ownedSessionId).catch(() => []);\n await Promise.all(\n records.map(async (record) => {\n try {\n await stopRunnerProcess(record, container.launcher, container.rmuxBackend);\n await container.runnerRegistry.complete(\n record.id,\n { reason: 'stopped', code: null, signal: null, stopReason: 'session ended' },\n ownedSessionId,\n );\n } catch (error) {\n process.emitWarning(`Could not clean up runner ${record.id}: ${String(error)}`);\n }\n }),\n );\n try {\n await container.ptyHost.disposeAll();\n } finally {\n container.lifeline.dispose();\n container.runnerRegistry.close();\n }\n })();\n return runtimeDisposal;\n };\n\n const publishRuns = async (ownedSessionId: string): Promise<void> => {\n try {\n const records = await container.runnerRegistry.listAll(ownedSessionId);\n const nextBackgroundWork = records\n .filter((record) => record.state === 'running')\n .map((record) => ({\n id: record.id,\n sessionId: record.sessionId,\n label: record.name,\n status: record.state,\n }));\n if (JSON.stringify(nextBackgroundWork) !== JSON.stringify(backgroundWorkItems)) {\n backgroundWorkItems = nextBackgroundWork;\n backgroundWorkProvider?.update();\n }\n directEvents.publish(RUNNER_RUNS_TYPE, ownedSessionId, {\n runs: presentRunnerRuns(records, Date.now()),\n });\n } catch (error) {\n process.emitWarning(`Could not publish runner state: ${String(error)}`);\n }\n };\n let publishInFlight: Promise<void> | undefined;\n let publishAgain = false;\n let unsubscribeRunnerUpdates: (() => void) | undefined;\n const requestPublish = (ownedSessionId: string): void => {\n publishAgain = true;\n if (publishInFlight !== undefined) return;\n publishInFlight = (async () => {\n do {\n publishAgain = false;\n await publishRuns(ownedSessionId);\n } while (publishAgain);\n })()\n .catch((error: unknown) => {\n process.emitWarning(`Could not schedule runner state publication: ${String(error)}`);\n })\n .finally(() => {\n publishInFlight = undefined;\n });\n };\n\n const activity: DoomHeadlessActivity = {\n name: 'runner',\n start: async (executionContext: DoomHeadlessExecutionContext) => {\n await startSupervision(executionContext);\n const unsubscribe = container.runnerRegistry.subscribe(\n () => requestPublish(executionContext.sessionId),\n executionContext.sessionId,\n );\n unsubscribeRunnerUpdates = unsubscribe;\n requestPublish(executionContext.sessionId);\n return () => {\n unsubscribe();\n if (unsubscribeRunnerUpdates === unsubscribe) unsubscribeRunnerUpdates = undefined;\n // Let the next optional activation reconcile again, while keeping\n // the retained runtime and all child job supervisors alive.\n supervision = undefined;\n };\n },\n };\n return {\n container,\n activity,\n backgroundWorkPlugin: (context) => {\n context.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => backgroundWorkItems,\n });\n backgroundWorkProvider = registration;\n return () => {\n registration.dispose();\n if (backgroundWorkProvider === registration) backgroundWorkProvider = undefined;\n };\n });\n },\n async dispose() {\n unsubscribeRunnerUpdates?.();\n unsubscribeRunnerUpdates = undefined;\n await disposeRuntime();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["reconcileActiveRunners","stopRunnerProcess","RUNNER_RUNS_TYPE","presentRunnerRuns","DOOM_BACKGROUND_WORK_SERVICE","readDoomBackgroundWorkService","BACKGROUND_WORK_PROVIDER"],"sources":["../../../../src/services/runnerServerRuntime/index.ts"],"sourcesContent":["import {\n DOOM_BACKGROUND_WORK_SERVICE,\n readDoomBackgroundWorkService,\n type BackgroundProviderWorkItem,\n type BackgroundWorkProviderHandle,\n} from '@agimon-ai/doompi-core/background-work';\nimport type { DoomHeadlessActivity, DoomHeadlessExecutionContext } from '@agimon-ai/doompi-core/headless';\nimport type { DoomServerHostService } from '@agimon-ai/doompi-core/server-facet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { BACKGROUND_WORK_PROVIDER } from '../../constants/runnerRuntime';\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport { reconcileActiveRunners, stopRunnerProcess } from '../reconcile';\nimport type { RunnerDependencies } from '../runnerDependencies/type';\nimport { presentRunnerRuns } from '../webRunnerRuns';\n\nexport interface RunnerServerRuntime {\n readonly container: RunnerDependencies;\n readonly activity: DoomHeadlessActivity;\n readonly backgroundWorkPlugin: (context: Context) => void;\n ensureSession(sessionId: string): Promise<void>;\n dispose(this: void): Promise<void>;\n}\nexport function createRunnerServerRuntime(\n container: RunnerDependencies,\n directEvents: NonNullable<DoomServerHostService['context']['directEvents']>,\n): RunnerServerRuntime {\n let sessionId: string | undefined;\n let supervision: Promise<void> | undefined;\n let runtimeDisposal: Promise<void> | undefined;\n let backgroundWorkItems: BackgroundProviderWorkItem[] = [];\n let backgroundWorkProvider: BackgroundWorkProviderHandle | undefined;\n\n // The activity is optional and source-gated. The retained facet owns the\n // session runtime, so disabling the activity must not tear down its jobs.\n const ensureSession = (requestedSessionId: string): Promise<void> => {\n if (sessionId !== undefined && sessionId !== requestedSessionId) {\n throw new Error(`Runner runtime belongs to session ${sessionId}.`);\n }\n sessionId ??= requestedSessionId;\n supervision ??= (async () => {\n container.paths.setSessionId(requestedSessionId);\n if ((await container.lifeline.arm(requestedSessionId)) === undefined) {\n throw new Error('Could not arm runner supervision lifeline.');\n }\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: true,\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n })();\n return supervision;\n };\n\n const disposeRuntime = (): Promise<void> => {\n runtimeDisposal ??= (async () => {\n await supervision?.catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n const ownedSessionId = sessionId;\n if (ownedSessionId === undefined) return;\n const records = await container.runnerRegistry.listBySession(ownedSessionId).catch(() => []);\n await Promise.all(\n records.map(async (record) => {\n try {\n await stopRunnerProcess(record, container.launcher, container.rmuxBackend);\n await container.runnerRegistry.complete(\n record.id,\n { reason: 'stopped', code: null, signal: null, stopReason: 'session ended' },\n ownedSessionId,\n );\n } catch (error) {\n process.emitWarning(`Could not clean up runner ${record.id}: ${String(error)}`);\n }\n }),\n );\n try {\n await container.ptyHost.disposeAll();\n } finally {\n container.lifeline.dispose();\n container.runnerRegistry.close();\n }\n })();\n return runtimeDisposal;\n };\n\n const publishRuns = async (ownedSessionId: string): Promise<void> => {\n try {\n const records = await container.runnerRegistry.listAll(ownedSessionId);\n const nextBackgroundWork = records\n .filter((record) => record.state === 'running')\n .map((record) => ({\n id: record.id,\n sessionId: record.sessionId,\n label: record.name,\n status: record.state,\n }));\n if (JSON.stringify(nextBackgroundWork) !== JSON.stringify(backgroundWorkItems)) {\n backgroundWorkItems = nextBackgroundWork;\n backgroundWorkProvider?.update();\n }\n directEvents.publish(RUNNER_RUNS_TYPE, ownedSessionId, {\n runs: presentRunnerRuns(records, Date.now()),\n });\n } catch (error) {\n process.emitWarning(`Could not publish runner state: ${String(error)}`);\n }\n };\n let publishInFlight: Promise<void> | undefined;\n let publishAgain = false;\n let unsubscribeRunnerUpdates: (() => void) | undefined;\n const requestPublish = (ownedSessionId: string): void => {\n publishAgain = true;\n if (publishInFlight !== undefined) return;\n publishInFlight = (async () => {\n do {\n publishAgain = false;\n await publishRuns(ownedSessionId);\n } while (publishAgain);\n })()\n .catch((error: unknown) => {\n process.emitWarning(`Could not schedule runner state publication: ${String(error)}`);\n })\n .finally(() => {\n publishInFlight = undefined;\n });\n };\n\n const activity: DoomHeadlessActivity = {\n name: 'runner',\n start: async (executionContext: DoomHeadlessExecutionContext) => {\n await ensureSession(executionContext.sessionId);\n const unsubscribe = container.runnerRegistry.subscribe(\n () => requestPublish(executionContext.sessionId),\n executionContext.sessionId,\n );\n unsubscribeRunnerUpdates = unsubscribe;\n requestPublish(executionContext.sessionId);\n return () => {\n unsubscribe();\n if (unsubscribeRunnerUpdates === unsubscribe) unsubscribeRunnerUpdates = undefined;\n // Let the next optional activation reconcile again, while keeping\n // the retained runtime and all child job supervisors alive.\n supervision = undefined;\n };\n },\n };\n return {\n container,\n activity,\n ensureSession,\n backgroundWorkPlugin: (context) => {\n context.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => backgroundWorkItems,\n });\n backgroundWorkProvider = registration;\n return () => {\n registration.dispose();\n if (backgroundWorkProvider === registration) backgroundWorkProvider = undefined;\n };\n });\n },\n async dispose() {\n unsubscribeRunnerUpdates?.();\n unsubscribeRunnerUpdates = undefined;\n await disposeRuntime();\n },\n };\n}\n"],"mappings":"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"}
@@ -10,30 +10,27 @@ function createRunnerServerRuntime(container, directEvents) {
10
10
  let runtimeDisposal;
11
11
  let backgroundWorkItems = [];
12
12
  let backgroundWorkProvider;
13
- const startSupervision = (executionContext) => {
14
- sessionId ??= executionContext.sessionId;
13
+ const ensureSession = (requestedSessionId) => {
14
+ if (sessionId !== void 0 && sessionId !== requestedSessionId) throw new Error(`Runner runtime belongs to session ${sessionId}.`);
15
+ sessionId ??= requestedSessionId;
15
16
  supervision ??= (async () => {
16
- try {
17
- container.paths.setSessionId(executionContext.sessionId);
18
- await container.lifeline.arm(executionContext.sessionId);
19
- const reconciled = await reconcileActiveRunners({
20
- registry: container.runnerRegistry,
21
- launcher: container.launcher,
22
- rmuxBackend: container.rmuxBackend,
23
- processControl: container.processControl,
24
- currentHostPid: process.pid,
25
- startup: true
26
- });
27
- for (const error of reconciled.errors) process.emitWarning(error);
28
- } catch (error) {
29
- process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`);
30
- }
17
+ container.paths.setSessionId(requestedSessionId);
18
+ if (await container.lifeline.arm(requestedSessionId) === void 0) throw new Error("Could not arm runner supervision lifeline.");
19
+ const reconciled = await reconcileActiveRunners({
20
+ registry: container.runnerRegistry,
21
+ launcher: container.launcher,
22
+ rmuxBackend: container.rmuxBackend,
23
+ processControl: container.processControl,
24
+ currentHostPid: process.pid,
25
+ startup: true
26
+ });
27
+ for (const error of reconciled.errors) process.emitWarning(error);
31
28
  })();
32
29
  return supervision;
33
30
  };
34
31
  const disposeRuntime = () => {
35
32
  runtimeDisposal ??= (async () => {
36
- await supervision;
33
+ await supervision?.catch((error) => process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`));
37
34
  const ownedSessionId = sessionId;
38
35
  if (ownedSessionId === void 0) return;
39
36
  const records = await container.runnerRegistry.listBySession(ownedSessionId).catch(() => []);
@@ -99,7 +96,7 @@ function createRunnerServerRuntime(container, directEvents) {
99
96
  activity: {
100
97
  name: "runner",
101
98
  start: async (executionContext) => {
102
- await startSupervision(executionContext);
99
+ await ensureSession(executionContext.sessionId);
103
100
  const unsubscribe = container.runnerRegistry.subscribe(() => requestPublish(executionContext.sessionId), executionContext.sessionId);
104
101
  unsubscribeRunnerUpdates = unsubscribe;
105
102
  requestPublish(executionContext.sessionId);
@@ -110,6 +107,7 @@ function createRunnerServerRuntime(container, directEvents) {
110
107
  };
111
108
  }
112
109
  },
110
+ ensureSession,
113
111
  backgroundWorkPlugin: (context) => {
114
112
  context.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {
115
113
  const service = readDoomBackgroundWorkService(serviceContext);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/runnerServerRuntime/index.ts"],"sourcesContent":["import {\n DOOM_BACKGROUND_WORK_SERVICE,\n readDoomBackgroundWorkService,\n type BackgroundProviderWorkItem,\n type BackgroundWorkProviderHandle,\n} from '@agimon-ai/doompi-core/background-work';\nimport type { DoomHeadlessActivity, DoomHeadlessExecutionContext } from '@agimon-ai/doompi-core/headless';\nimport type { DoomServerHostService } from '@agimon-ai/doompi-core/server-facet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { BACKGROUND_WORK_PROVIDER } from '../../constants/runnerRuntime';\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport { reconcileActiveRunners, stopRunnerProcess } from '../reconcile';\nimport type { RunnerDependencies } from '../runnerDependencies/type';\nimport { presentRunnerRuns } from '../webRunnerRuns';\n\nexport interface RunnerServerRuntime {\n readonly container: RunnerDependencies;\n readonly activity: DoomHeadlessActivity;\n readonly backgroundWorkPlugin: (context: Context) => void;\n dispose(this: void): Promise<void>;\n}\nexport function createRunnerServerRuntime(\n container: RunnerDependencies,\n directEvents: NonNullable<DoomServerHostService['context']['directEvents']>,\n): RunnerServerRuntime {\n let sessionId: string | undefined;\n let supervision: Promise<void> | undefined;\n let runtimeDisposal: Promise<void> | undefined;\n let backgroundWorkItems: BackgroundProviderWorkItem[] = [];\n let backgroundWorkProvider: BackgroundWorkProviderHandle | undefined;\n\n // The activity is optional and source-gated. The retained facet owns the\n // session runtime, so disabling the activity must not tear down its jobs.\n const startSupervision = (executionContext: DoomHeadlessExecutionContext): Promise<void> => {\n sessionId ??= executionContext.sessionId;\n supervision ??= (async () => {\n try {\n container.paths.setSessionId(executionContext.sessionId);\n await container.lifeline.arm(executionContext.sessionId);\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: true,\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n } catch (error) {\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`);\n }\n })();\n return supervision;\n };\n\n const disposeRuntime = (): Promise<void> => {\n runtimeDisposal ??= (async () => {\n await supervision;\n const ownedSessionId = sessionId;\n if (ownedSessionId === undefined) return;\n const records = await container.runnerRegistry.listBySession(ownedSessionId).catch(() => []);\n await Promise.all(\n records.map(async (record) => {\n try {\n await stopRunnerProcess(record, container.launcher, container.rmuxBackend);\n await container.runnerRegistry.complete(\n record.id,\n { reason: 'stopped', code: null, signal: null, stopReason: 'session ended' },\n ownedSessionId,\n );\n } catch (error) {\n process.emitWarning(`Could not clean up runner ${record.id}: ${String(error)}`);\n }\n }),\n );\n try {\n await container.ptyHost.disposeAll();\n } finally {\n container.lifeline.dispose();\n container.runnerRegistry.close();\n }\n })();\n return runtimeDisposal;\n };\n\n const publishRuns = async (ownedSessionId: string): Promise<void> => {\n try {\n const records = await container.runnerRegistry.listAll(ownedSessionId);\n const nextBackgroundWork = records\n .filter((record) => record.state === 'running')\n .map((record) => ({\n id: record.id,\n sessionId: record.sessionId,\n label: record.name,\n status: record.state,\n }));\n if (JSON.stringify(nextBackgroundWork) !== JSON.stringify(backgroundWorkItems)) {\n backgroundWorkItems = nextBackgroundWork;\n backgroundWorkProvider?.update();\n }\n directEvents.publish(RUNNER_RUNS_TYPE, ownedSessionId, {\n runs: presentRunnerRuns(records, Date.now()),\n });\n } catch (error) {\n process.emitWarning(`Could not publish runner state: ${String(error)}`);\n }\n };\n let publishInFlight: Promise<void> | undefined;\n let publishAgain = false;\n let unsubscribeRunnerUpdates: (() => void) | undefined;\n const requestPublish = (ownedSessionId: string): void => {\n publishAgain = true;\n if (publishInFlight !== undefined) return;\n publishInFlight = (async () => {\n do {\n publishAgain = false;\n await publishRuns(ownedSessionId);\n } while (publishAgain);\n })()\n .catch((error: unknown) => {\n process.emitWarning(`Could not schedule runner state publication: ${String(error)}`);\n })\n .finally(() => {\n publishInFlight = undefined;\n });\n };\n\n const activity: DoomHeadlessActivity = {\n name: 'runner',\n start: async (executionContext: DoomHeadlessExecutionContext) => {\n await startSupervision(executionContext);\n const unsubscribe = container.runnerRegistry.subscribe(\n () => requestPublish(executionContext.sessionId),\n executionContext.sessionId,\n );\n unsubscribeRunnerUpdates = unsubscribe;\n requestPublish(executionContext.sessionId);\n return () => {\n unsubscribe();\n if (unsubscribeRunnerUpdates === unsubscribe) unsubscribeRunnerUpdates = undefined;\n // Let the next optional activation reconcile again, while keeping\n // the retained runtime and all child job supervisors alive.\n supervision = undefined;\n };\n },\n };\n return {\n container,\n activity,\n backgroundWorkPlugin: (context) => {\n context.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => backgroundWorkItems,\n });\n backgroundWorkProvider = registration;\n return () => {\n registration.dispose();\n if (backgroundWorkProvider === registration) backgroundWorkProvider = undefined;\n };\n });\n },\n async dispose() {\n unsubscribeRunnerUpdates?.();\n unsubscribeRunnerUpdates = undefined;\n await disposeRuntime();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/runnerServerRuntime/index.ts"],"sourcesContent":["import {\n DOOM_BACKGROUND_WORK_SERVICE,\n readDoomBackgroundWorkService,\n type BackgroundProviderWorkItem,\n type BackgroundWorkProviderHandle,\n} from '@agimon-ai/doompi-core/background-work';\nimport type { DoomHeadlessActivity, DoomHeadlessExecutionContext } from '@agimon-ai/doompi-core/headless';\nimport type { DoomServerHostService } from '@agimon-ai/doompi-core/server-facet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { BACKGROUND_WORK_PROVIDER } from '../../constants/runnerRuntime';\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport { reconcileActiveRunners, stopRunnerProcess } from '../reconcile';\nimport type { RunnerDependencies } from '../runnerDependencies/type';\nimport { presentRunnerRuns } from '../webRunnerRuns';\n\nexport interface RunnerServerRuntime {\n readonly container: RunnerDependencies;\n readonly activity: DoomHeadlessActivity;\n readonly backgroundWorkPlugin: (context: Context) => void;\n ensureSession(sessionId: string): Promise<void>;\n dispose(this: void): Promise<void>;\n}\nexport function createRunnerServerRuntime(\n container: RunnerDependencies,\n directEvents: NonNullable<DoomServerHostService['context']['directEvents']>,\n): RunnerServerRuntime {\n let sessionId: string | undefined;\n let supervision: Promise<void> | undefined;\n let runtimeDisposal: Promise<void> | undefined;\n let backgroundWorkItems: BackgroundProviderWorkItem[] = [];\n let backgroundWorkProvider: BackgroundWorkProviderHandle | undefined;\n\n // The activity is optional and source-gated. The retained facet owns the\n // session runtime, so disabling the activity must not tear down its jobs.\n const ensureSession = (requestedSessionId: string): Promise<void> => {\n if (sessionId !== undefined && sessionId !== requestedSessionId) {\n throw new Error(`Runner runtime belongs to session ${sessionId}.`);\n }\n sessionId ??= requestedSessionId;\n supervision ??= (async () => {\n container.paths.setSessionId(requestedSessionId);\n if ((await container.lifeline.arm(requestedSessionId)) === undefined) {\n throw new Error('Could not arm runner supervision lifeline.');\n }\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: true,\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n })();\n return supervision;\n };\n\n const disposeRuntime = (): Promise<void> => {\n runtimeDisposal ??= (async () => {\n await supervision?.catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n const ownedSessionId = sessionId;\n if (ownedSessionId === undefined) return;\n const records = await container.runnerRegistry.listBySession(ownedSessionId).catch(() => []);\n await Promise.all(\n records.map(async (record) => {\n try {\n await stopRunnerProcess(record, container.launcher, container.rmuxBackend);\n await container.runnerRegistry.complete(\n record.id,\n { reason: 'stopped', code: null, signal: null, stopReason: 'session ended' },\n ownedSessionId,\n );\n } catch (error) {\n process.emitWarning(`Could not clean up runner ${record.id}: ${String(error)}`);\n }\n }),\n );\n try {\n await container.ptyHost.disposeAll();\n } finally {\n container.lifeline.dispose();\n container.runnerRegistry.close();\n }\n })();\n return runtimeDisposal;\n };\n\n const publishRuns = async (ownedSessionId: string): Promise<void> => {\n try {\n const records = await container.runnerRegistry.listAll(ownedSessionId);\n const nextBackgroundWork = records\n .filter((record) => record.state === 'running')\n .map((record) => ({\n id: record.id,\n sessionId: record.sessionId,\n label: record.name,\n status: record.state,\n }));\n if (JSON.stringify(nextBackgroundWork) !== JSON.stringify(backgroundWorkItems)) {\n backgroundWorkItems = nextBackgroundWork;\n backgroundWorkProvider?.update();\n }\n directEvents.publish(RUNNER_RUNS_TYPE, ownedSessionId, {\n runs: presentRunnerRuns(records, Date.now()),\n });\n } catch (error) {\n process.emitWarning(`Could not publish runner state: ${String(error)}`);\n }\n };\n let publishInFlight: Promise<void> | undefined;\n let publishAgain = false;\n let unsubscribeRunnerUpdates: (() => void) | undefined;\n const requestPublish = (ownedSessionId: string): void => {\n publishAgain = true;\n if (publishInFlight !== undefined) return;\n publishInFlight = (async () => {\n do {\n publishAgain = false;\n await publishRuns(ownedSessionId);\n } while (publishAgain);\n })()\n .catch((error: unknown) => {\n process.emitWarning(`Could not schedule runner state publication: ${String(error)}`);\n })\n .finally(() => {\n publishInFlight = undefined;\n });\n };\n\n const activity: DoomHeadlessActivity = {\n name: 'runner',\n start: async (executionContext: DoomHeadlessExecutionContext) => {\n await ensureSession(executionContext.sessionId);\n const unsubscribe = container.runnerRegistry.subscribe(\n () => requestPublish(executionContext.sessionId),\n executionContext.sessionId,\n );\n unsubscribeRunnerUpdates = unsubscribe;\n requestPublish(executionContext.sessionId);\n return () => {\n unsubscribe();\n if (unsubscribeRunnerUpdates === unsubscribe) unsubscribeRunnerUpdates = undefined;\n // Let the next optional activation reconcile again, while keeping\n // the retained runtime and all child job supervisors alive.\n supervision = undefined;\n };\n },\n };\n return {\n container,\n activity,\n ensureSession,\n backgroundWorkPlugin: (context) => {\n context.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => backgroundWorkItems,\n });\n backgroundWorkProvider = registration;\n return () => {\n registration.dispose();\n if (backgroundWorkProvider === registration) backgroundWorkProvider = undefined;\n };\n });\n },\n async dispose() {\n unsubscribeRunnerUpdates?.();\n unsubscribeRunnerUpdates = undefined;\n await disposeRuntime();\n },\n };\n}\n"],"mappings":"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"}
@@ -108,7 +108,7 @@ function createRunnerRuntime(pi) {
108
108
  };
109
109
  const publishBackgroundWork = () => {
110
110
  const activeSessionId = sessionId;
111
- const representedIds = /* @__PURE__ */ new Set([...runners.map((record) => record.id), ...terminalRunners.keys()]);
111
+ const representedIds = /* @__PURE__ */ new Set([...runners.filter((record) => record.promoted).map((record) => record.id), ...terminalRunners.keys()]);
112
112
  const next = activeSessionId ? [
113
113
  ...runners.filter((record) => record.sessionId === activeSessionId && record.promoted).map((record) => ({
114
114
  id: record.id,