@parall/daemon 1.36.0 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/bundle/manifest.json +11 -11
  2. package/bundle/parall-browser-pod.js +384 -32
  3. package/bundle/parall-claude-agent.js +426 -51
  4. package/bundle/parall-codex-agent.js +425 -49
  5. package/bundle/parall-daemon.js +33336 -31520
  6. package/dist/browser-pod.d.ts +7 -0
  7. package/dist/browser-pod.d.ts.map +1 -1
  8. package/dist/browser-pod.js +73 -3
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +2 -2
  11. package/dist/clip-runtime/browser-daemon-env.d.ts +29 -0
  12. package/dist/clip-runtime/browser-daemon-env.d.ts.map +1 -0
  13. package/dist/clip-runtime/browser-daemon-env.js +70 -0
  14. package/dist/clip-runtime/browser-profile-manager.d.ts +34 -5
  15. package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
  16. package/dist/clip-runtime/browser-profile-manager.js +166 -24
  17. package/dist/clip-runtime/browser-profile-pool.d.ts +250 -0
  18. package/dist/clip-runtime/browser-profile-pool.d.ts.map +1 -0
  19. package/dist/clip-runtime/browser-profile-pool.js +581 -0
  20. package/dist/clip-runtime/index.d.ts +2 -1
  21. package/dist/clip-runtime/index.d.ts.map +1 -1
  22. package/dist/clip-runtime/index.js +2 -1
  23. package/dist/clip-runtime/process-manager.d.ts +5 -3
  24. package/dist/clip-runtime/process-manager.d.ts.map +1 -1
  25. package/dist/clip-runtime/subprocess.d.ts +14 -0
  26. package/dist/clip-runtime/subprocess.d.ts.map +1 -1
  27. package/dist/clip-runtime/subprocess.js +49 -0
  28. package/dist/config.d.ts +14 -15
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +8 -26
  31. package/dist/daemon-main.d.ts +8 -0
  32. package/dist/daemon-main.d.ts.map +1 -0
  33. package/dist/daemon-main.js +165 -0
  34. package/dist/daemon-paths.d.ts +14 -0
  35. package/dist/daemon-paths.d.ts.map +1 -0
  36. package/dist/daemon-paths.js +26 -0
  37. package/dist/daemon-update-mode.d.ts +8 -0
  38. package/dist/daemon-update-mode.d.ts.map +1 -0
  39. package/dist/daemon-update-mode.js +18 -0
  40. package/dist/index.js +41 -167
  41. package/dist/runtime-bin-resolver.d.ts +4 -0
  42. package/dist/runtime-bin-resolver.d.ts.map +1 -1
  43. package/dist/runtime-bin-resolver.js +40 -7
  44. package/dist/runtime-detector.d.ts +30 -0
  45. package/dist/runtime-detector.d.ts.map +1 -0
  46. package/dist/runtime-detector.js +100 -0
  47. package/dist/supervisor.d.ts +64 -2
  48. package/dist/supervisor.d.ts.map +1 -1
  49. package/dist/supervisor.js +629 -67
  50. package/dist/update-health-gate.d.ts +66 -0
  51. package/dist/update-health-gate.d.ts.map +1 -0
  52. package/dist/update-health-gate.js +93 -0
  53. package/dist/updater-manifest.d.ts +2 -1
  54. package/dist/updater-manifest.d.ts.map +1 -1
  55. package/dist/updater-manifest.js +38 -7
  56. package/dist/updater.d.ts +13 -2
  57. package/dist/updater.d.ts.map +1 -1
  58. package/dist/updater.js +126 -17
  59. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -1,188 +1,62 @@
1
1
  #!/usr/bin/env node
2
+ import * as fs from 'node:fs';
2
3
  import * as path from 'node:path';
3
- import { createLogger, createOtelLogger, initAgentTelemetry, } from '@parall/agent-core';
4
- import { ParallClient } from '@parall/sdk';
5
- import { runCLI } from './cli.js';
6
- import { resolveBundleDir, resolveClaudeDaemonConfig, resolveWsUrl, } from './config.js';
7
- import { DaemonSupervisor, sleepCancellable } from './supervisor.js';
8
- import { DaemonUpdater } from './updater.js';
4
+ import { resolveBundleDir } from './daemon-paths.js';
5
+ import { isSelfUpdateDisabledByEnv, isSelfUpdateManaged } from './daemon-update-mode.js';
9
6
  const UPDATE_EXIT_CODE = 42;
10
- let log = createLogger('daemon');
11
7
  function formatError(reason) {
12
8
  if (reason instanceof Error) {
13
9
  return reason.stack ?? reason.message;
14
10
  }
15
11
  return String(reason);
16
12
  }
17
- /**
18
- * Self-update should run only when a service manager supervises the daemon —
19
- * `parall-daemon service install` (launchd/systemd) or the hosted entrypoint.sh
20
- * keepalive loop. Those wrappers re-exec the overlay bundle on restart, and
21
- * exit(UPDATE_EXIT_CODE) is precisely what triggers that restart so the new code
22
- * loads.
23
- *
24
- * A bare `npx @parall/daemon` foreground run has neither property: it never
25
- * exec's the overlay (so a downloaded bundle is dead weight on disk — this very
26
- * process keeps running the npm launcher code), and nothing restarts it after
27
- * exit(42). Self-updating there would just kill the daemon with no recovery. So
28
- * we gate on two positive "managed" signals:
29
- * - PRLL_DAEMON_MANAGED=1 — injected into the plist / systemd unit by
30
- * `service install`; the authoritative marker.
31
- * - launched from the overlay bundle dir — a service wrapper already exec'd
32
- * `…/.parall-daemon/bundle/current/parall-daemon.js`. Covers service
33
- * installs predating the PRLL_DAEMON_MANAGED marker.
34
- * Foreground users stay current via `npx @parall/daemon@latest`, which pulls the
35
- * newest launcher on every run.
36
- */
37
- function isSelfUpdateManaged(env, bundleDir) {
38
- if (env.PRLL_DAEMON_MANAGED === '1' || env.PRLL_DAEMON_MANAGED === 'true')
39
- return true;
40
- const entry = process.argv[1];
41
- if (entry) {
42
- const resolvedEntry = path.resolve(entry);
43
- const resolvedBundle = path.resolve(bundleDir);
44
- if (resolvedEntry === resolvedBundle || resolvedEntry.startsWith(resolvedBundle + path.sep)) {
45
- return true;
46
- }
13
+ function errnoCode(err) {
14
+ if (!err || typeof err !== 'object' || !('code' in err))
15
+ return undefined;
16
+ const code = err.code;
17
+ return typeof code === 'string' ? code : undefined;
18
+ }
19
+ function clearRunningMarker(markerPath) {
20
+ try {
21
+ fs.unlinkSync(markerPath);
22
+ }
23
+ catch (err) {
24
+ if (errnoCode(err) === 'ENOENT')
25
+ return;
26
+ console.warn(`failed to clear daemon running marker: ${formatError(err)}`);
47
27
  }
48
- return false;
49
28
  }
50
29
  /**
51
- * Outer keepalive: runs `supervisor.run()` in a loop, restarting it with
52
- * exponential backoff if it rejects. The daemon process only exits when
53
- * the abort signal fires (SIGINT/SIGTERM) anything else is treated as
54
- * a transient failure that we recover from in-process.
55
- *
56
- * Why this exists in addition to entrypoint.sh's `while :; do parall-daemon; done`:
57
- * - In-process restart preserves the `client` (= same TCP/TLS pool) and
58
- * skips the ~hundreds-of-ms cost of Node startup + module loading per
59
- * cycle, which matters when the API is flapping.
60
- * - The shell wrapper is the "process really crashed" backstop —
61
- * segfault, OOM, uncaughtException, etc. The two layers are
62
- * complementary: in-process for soft failures, shell for hard failures.
63
- *
64
- * Operators can disable the in-process loop by setting
65
- * PRLL_DAEMON_SUPERVISOR_RESTART_BACKOFF_MS=0 — main() will then exit on
66
- * any supervisor.run() rejection and rely entirely on the shell wrapper +
67
- * K8s for restart.
30
+ * Bootstrap owns the process-lifecycle fact for rollback: "a daemon boot was
31
+ * entered and did not exit cleanly." Keep this before the app import so config,
32
+ * telemetry, updater, or other app-startup failures still leave evidence for
33
+ * the next boot's rollback accounting. CLI subcommands are not daemon boots.
68
34
  */
69
- async function runForever(config, client, log, signal, updater) {
70
- let attempt = 0;
71
- while (!signal.aborted) {
72
- const supervisor = new DaemonSupervisor(config, client, log);
73
- if (updater)
74
- supervisor.setUpdater(updater);
75
- try {
76
- await supervisor.run(signal);
77
- // Clean exit (signal aborted) — done.
78
- await supervisor.stop();
79
- return;
80
- }
81
- catch (err) {
82
- // supervisor.run() rejected. The supervisor's per-tick handlers
83
- // already swallow individual failures, so the only paths that reach
84
- // here are (a) bootstrap fail-fast mode and (b) genuine programmer
85
- // bugs. Restart anyway — operators rely on this daemon as the only
86
- // thing keeping per-agent children alive on this host.
87
- log.error(`supervisor crashed: ${String(err)}`);
88
- try {
89
- await supervisor.stop();
90
- }
91
- catch (stopErr) {
92
- log.warn(`supervisor.stop() after crash threw: ${String(stopErr)}`);
93
- }
94
- if (config.supervisorRestartBackoffMs === 0) {
95
- log.error('supervisor keepalive disabled (PRLL_DAEMON_SUPERVISOR_RESTART_BACKOFF_MS=0) — exiting');
96
- throw err;
97
- }
98
- const delay = Math.min(config.supervisorRestartBackoffMs * 2 ** attempt, config.supervisorRestartBackoffMaxMs);
99
- attempt += 1;
100
- log.warn(`restarting supervisor in ${delay}ms (attempt ${attempt})`);
101
- const slept = await sleepCancellable(delay, signal);
102
- if (!slept)
103
- return;
104
- }
35
+ function prepareDaemonBootstrap(env = process.env, args = process.argv.slice(2)) {
36
+ const bundleDir = resolveBundleDir(env);
37
+ const runningMarker = path.join(bundleDir, 'daemon-running');
38
+ const lifecycleMarkerEnabled = args.length === 0 && !isSelfUpdateDisabledByEnv(env) && isSelfUpdateManaged(bundleDir, env);
39
+ if (!lifecycleMarkerEnabled) {
40
+ return { lifecycleMarkerEnabled: false, runningMarker, uncleanPrevExit: false };
105
41
  }
106
- }
107
- async function main() {
108
- const config = resolveClaudeDaemonConfig(process.env);
109
- const telemetry = await initAgentTelemetry('parall-daemon', 'daemon');
110
- log = createOtelLogger('daemon', 'daemon');
111
- log.info(`boot: api=${config.apiUrl} pollMs=${config.pollIntervalMs} heartbeatMs=${config.heartbeatIntervalMs} agentBin=${config.agentBin}`);
112
- log.info(`keepalive: bootstrapBackoffMs=${config.bootstrapBackoffMs} supervisorRestartBackoffMs=${config.supervisorRestartBackoffMs}`);
113
- // --- Self-update: rollback check (before any network calls) ---
114
- // Only run when a service manager supervises us (see isSelfUpdateManaged):
115
- // a bare `npx` foreground run can't load the overlay and won't be restarted
116
- // after exit(42), so self-update there would just kill the daemon.
117
- let updater = null;
118
- const bundleDir = resolveBundleDir(process.env);
119
- const selfUpdateEnabled = !config.updateDisabled && isSelfUpdateManaged(process.env, bundleDir);
120
- if (selfUpdateEnabled) {
121
- updater = new DaemonUpdater(bundleDir, config.updateCdnUrl, log, true);
122
- if (updater.checkRollback()) {
123
- log.info('rollback applied — exiting for service manager restart');
124
- await telemetry.shutdown();
125
- process.exit(UPDATE_EXIT_CODE);
126
- }
127
- log.info(`update: bundleDir=${bundleDir} cdn=${config.updateCdnUrl} interval=${config.updateIntervalMs}ms`);
42
+ const uncleanPrevExit = fs.existsSync(runningMarker);
43
+ try {
44
+ fs.mkdirSync(bundleDir, { recursive: true });
45
+ fs.writeFileSync(runningMarker, String(process.pid));
128
46
  }
129
- else if (!config.updateDisabled) {
130
- log.info('update: skipped (foreground mode) — run `parall-daemon service install` for managed auto-update');
47
+ catch (err) {
48
+ console.warn(`failed to write daemon running marker: ${formatError(err)}`);
131
49
  }
132
- // The daemon talks to the API as a Machine — the bearer is mck_*.
133
- // No orgId is configured here; per-agent subprocesses get their
134
- // own org_id via the spawn env.
135
- const client = new ParallClient({
136
- baseUrl: config.apiUrl,
137
- token: config.apiKey,
138
- swimlaneName: config.swimlaneName,
139
- });
140
- const abortController = new AbortController();
141
- const onSignal = (sig) => {
142
- log.info(`received ${sig} — initiating shutdown`);
143
- abortController.abort();
144
- };
145
- process.on('SIGINT', () => onSignal('SIGINT'));
146
- process.on('SIGTERM', () => onSignal('SIGTERM'));
147
- // Defensive: unexpected async failures are logged explicitly. Rejections
148
- // stay in-process so the supervisor loop can recover; uncaught exceptions
149
- // exit so entrypoint.sh's shell-level keepalive restarts from a clean VM.
150
- process.on('unhandledRejection', (reason) => {
151
- log.error(`unhandledRejection: ${formatError(reason)}`);
152
- });
153
- process.on('uncaughtException', (err) => {
154
- log.error(`uncaughtException: ${formatError(err)}`);
155
- process.exitCode = 1;
156
- process.exit(1);
50
+ process.on('exit', (code) => {
51
+ if (code === 0 || code === UPDATE_EXIT_CODE)
52
+ clearRunningMarker(runningMarker);
157
53
  });
158
- config.wsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
159
- // --- Self-update: boot check + periodic timer ---
160
- if (updater) {
161
- const applied = await updater.checkAndApply().catch((err) => {
162
- log.warn(`boot update check failed: ${String(err)}`);
163
- return false;
164
- });
165
- if (applied) {
166
- log.info('boot update applied — exiting for restart');
167
- await telemetry.shutdown();
168
- process.exit(UPDATE_EXIT_CODE);
169
- }
170
- updater.startPeriodicCheck(config.updateIntervalMs);
171
- }
172
- await runForever(config, client, log, abortController.signal, updater);
173
- await telemetry.shutdown();
54
+ return { lifecycleMarkerEnabled: true, runningMarker, uncleanPrevExit };
174
55
  }
175
- const cliArgs = process.argv.slice(2);
176
- runCLI(cliArgs)
177
- .then((result) => {
178
- if (result === 'handled')
179
- return;
180
- main().catch((err) => {
181
- log.error(`fatal: ${formatError(err)}`);
182
- process.exitCode = 1;
183
- });
184
- })
56
+ const bootstrap = prepareDaemonBootstrap();
57
+ void import('./daemon-main.js')
58
+ .then((mod) => mod.runDaemonEntry(bootstrap))
185
59
  .catch((err) => {
186
- log.error(`fatal: ${formatError(err)}`);
60
+ console.error(`fatal: ${formatError(err)}`);
187
61
  process.exitCode = 1;
188
62
  });
@@ -2,6 +2,10 @@ type RuntimeBinLogger = {
2
2
  info: (msg: string) => void;
3
3
  warn: (msg: string) => void;
4
4
  };
5
+ /** Runtime types whose host CLI the resolver knows how to locate. */
6
+ export declare const DETECTABLE_RUNTIME_TYPES: readonly string[];
7
+ /** The env var `applyRuntimeBinaryEnv` publishes the resolved binary under. */
8
+ export declare function runtimeBinaryEnvVar(runtimeType: string): string | undefined;
5
9
  export declare function applyRuntimeBinaryEnv(runtimeType: string, baseEnv: NodeJS.ProcessEnv, log: RuntimeBinLogger): NodeJS.ProcessEnv;
6
10
  export {};
7
11
  //# sourceMappingURL=runtime-bin-resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-bin-resolver.d.ts","sourceRoot":"","sources":["../src/runtime-bin-resolver.ts"],"names":[],"mappings":"AAUA,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B,CAAC;AA0BF,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,CAAC,UAAU,EAC1B,GAAG,EAAE,gBAAgB,GACpB,MAAM,CAAC,UAAU,CAmCnB"}
1
+ {"version":3,"file":"runtime-bin-resolver.d.ts","sourceRoot":"","sources":["../src/runtime-bin-resolver.ts"],"names":[],"mappings":"AAUA,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B,CAAC;AAgCF,qEAAqE;AACrE,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAAkC,CAAC;AAEzF,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE3E;AAKD,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,CAAC,UAAU,EAC1B,GAAG,EAAE,gBAAgB,GACpB,MAAM,CAAC,UAAU,CAmCnB"}
@@ -7,12 +7,18 @@ const RUNTIME_BINARIES = {
7
7
  codex: { command: 'codex', envVar: 'PRLL_CODEX_BIN' },
8
8
  openclaw: { command: 'openclaw', envVar: 'PRLL_OPENCLAW_BIN' },
9
9
  };
10
+ /** Runtime types whose host CLI the resolver knows how to locate. */
11
+ export const DETECTABLE_RUNTIME_TYPES = Object.keys(RUNTIME_BINARIES);
12
+ /** The env var `applyRuntimeBinaryEnv` publishes the resolved binary under. */
13
+ export function runtimeBinaryEnvVar(runtimeType) {
14
+ return RUNTIME_BINARIES[runtimeType]?.envVar;
15
+ }
10
16
  const RESOLUTION_CACHE_TTL_MS = 5 * 60_000;
11
17
  const resolutionCache = new Map();
12
18
  export function applyRuntimeBinaryEnv(runtimeType, baseEnv, log) {
13
19
  const env = { ...baseEnv };
14
20
  const originalPath = env.PATH;
15
- const pathPlan = candidatePathPlan(env);
21
+ const pathPlan = cachedCandidatePathPlan(env);
16
22
  const primaryPath = mergePath(pathPlan.primaryDirs, originalPath);
17
23
  env.PATH = mergePath([...pathPlan.primaryDirs, ...pathPlan.versionedFallbackDirs], originalPath);
18
24
  const spec = RUNTIME_BINARIES[runtimeType];
@@ -48,6 +54,9 @@ function resolveRuntimeCommand(command, env, inheritedPath, primaryPath, fallbac
48
54
  const cached = getCachedResolution(cacheKey);
49
55
  if (cached)
50
56
  return cached;
57
+ // A cached miss (cached === null) skips ONLY the login-shell step — see
58
+ // ResolutionCacheEntry. The cheap fs scans below always run fresh.
59
+ const runLoginShell = cached === undefined;
51
60
  const direct = resolveDirectPath(command);
52
61
  if (direct) {
53
62
  const resolved = { binaryPath: direct };
@@ -59,10 +68,12 @@ function resolveRuntimeCommand(command, env, inheritedPath, primaryPath, fallbac
59
68
  setCachedResolution(cacheKey, fromInheritedPath);
60
69
  return fromInheritedPath;
61
70
  }
62
- const fromShell = resolveFromLoginShell(command, { ...env, PATH: inheritedPath });
63
- if (fromShell) {
64
- setCachedResolution(cacheKey, fromShell);
65
- return fromShell;
71
+ if (runLoginShell) {
72
+ const fromShell = resolveFromLoginShell(command, { ...env, PATH: inheritedPath });
73
+ if (fromShell) {
74
+ setCachedResolution(cacheKey, fromShell);
75
+ return fromShell;
76
+ }
66
77
  }
67
78
  const fromPrimaryPath = resolveFromPath(command, primaryPath, env);
68
79
  if (fromPrimaryPath) {
@@ -74,6 +85,12 @@ function resolveRuntimeCommand(command, env, inheritedPath, primaryPath, fallbac
74
85
  setCachedResolution(cacheKey, fromFallbackPath);
75
86
  return fromFallbackPath;
76
87
  }
88
+ // Record the miss only on a round that actually ran the login-shell —
89
+ // re-recording it on a cached-miss round would keep refreshing the TTL and
90
+ // the login-shell would never get retried.
91
+ if (runLoginShell) {
92
+ setCachedResolution(cacheKey, null);
93
+ }
77
94
  return null;
78
95
  }
79
96
  function resolveDirectPath(command) {
@@ -135,6 +152,21 @@ function resolveFromLoginShell(command, env) {
135
152
  }
136
153
  return null;
137
154
  }
155
+ // The plan involves fs stats over dozens of candidate dirs plus readdirSync
156
+ // over nvm/fnm version roots; since the periodic runtime detector re-resolves
157
+ // every 5 min (×3 runtimes) on top of agent spawns, memoize it on the same
158
+ // TTL as resolutions. A freshly installed version manager dir therefore shows
159
+ // up within one detection interval, same as everything else here.
160
+ const pathPlanCache = new Map();
161
+ function cachedCandidatePathPlan(env) {
162
+ const key = `${env.HOME ?? ''}\0${env.PRLL_DAEMON_RUNTIME_PATH ?? ''}\0${env.PRLL_DAEMON_EXTRA_PATH ?? ''}`;
163
+ const hit = pathPlanCache.get(key);
164
+ if (hit && hit.expiresAt > Date.now())
165
+ return hit.value;
166
+ const value = candidatePathPlan(env);
167
+ pathPlanCache.set(key, { value, expiresAt: Date.now() + RESOLUTION_CACHE_TTL_MS });
168
+ return value;
169
+ }
138
170
  function candidatePathPlan(env) {
139
171
  const home = env.HOME || os.homedir();
140
172
  const primaryDirs = [
@@ -243,13 +275,14 @@ function commandCandidates(command, env) {
243
275
  .map((ext) => ext.toLowerCase());
244
276
  return [command, ...exts.map((ext) => `${command}${ext}`)];
245
277
  }
278
+ /** undefined = no cache entry; null = cached miss (see ResolutionCacheEntry). */
246
279
  function getCachedResolution(cacheKey) {
247
280
  const entry = resolutionCache.get(cacheKey);
248
281
  if (!entry)
249
- return null;
282
+ return undefined;
250
283
  if (entry.expiresAt <= Date.now()) {
251
284
  resolutionCache.delete(cacheKey);
252
- return null;
285
+ return undefined;
253
286
  }
254
287
  return entry.value;
255
288
  }
@@ -0,0 +1,30 @@
1
+ import type { DetectedRuntime } from '@parall/sdk';
2
+ export type { DetectedRuntime };
3
+ type DetectorLogger = {
4
+ info: (msg: string) => void;
5
+ warn: (msg: string) => void;
6
+ };
7
+ /**
8
+ * Resolve a runtime CLI binary the same way agent spawn does. Exposed for
9
+ * tests to inject a fake resolver into `detectRuntimes`.
10
+ */
11
+ export declare function resolveRuntimeBinary(runtimeType: string, baseEnv: NodeJS.ProcessEnv): {
12
+ binaryPath: string;
13
+ env: NodeJS.ProcessEnv;
14
+ } | null;
15
+ /**
16
+ * Detect which runtime CLIs exist on this host and whether they actually run.
17
+ * Absent runtimes are omitted; a binary that resolves but fails `--version`
18
+ * (exit != 0, timeout, spawn error) reports status 'broken' — a
19
+ * present-but-unusable install is a stronger signal than absence.
20
+ *
21
+ * Resolution reuses the exact spawn-time path (`applyRuntimeBinaryEnv`), so
22
+ * "detected" and "what an agent child would launch" can't drift apart. The
23
+ * probe runs with the resolver's anchored PATH because npm-shim CLIs
24
+ * (`#!/usr/bin/env node`) need their sibling node on PATH to execute.
25
+ */
26
+ export declare function detectRuntimes(baseEnv: NodeJS.ProcessEnv, log: DetectorLogger, resolveBin?: typeof resolveRuntimeBinary): Promise<DetectedRuntime[]>;
27
+ /** Order-sensitive compare — detection emits entries in stable runtime order. */
28
+ export declare function detectedRuntimesEqual(a: DetectedRuntime[], b: DetectedRuntime[]): boolean;
29
+ export declare function summarizeDetectedRuntimes(detected: DetectedRuntime[]): string;
30
+ //# sourceMappingURL=runtime-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-detector.d.ts","sourceRoot":"","sources":["../src/runtime-detector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAOnD,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B,CAAC;AAuBF;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,CAAC,UAAU,GACzB;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,GAAG,IAAI,CAMvD;AA0BD;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,CAAC,UAAU,EAC1B,GAAG,EAAE,cAAc,EACnB,UAAU,GAAE,OAAO,oBAA2C,GAC7D,OAAO,CAAC,eAAe,EAAE,CAAC,CA4B5B;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,eAAe,EAAE,GAAG,OAAO,CAQzF;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAK7E"}
@@ -0,0 +1,100 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { applyRuntimeBinaryEnv, DETECTABLE_RUNTIME_TYPES, runtimeBinaryEnvVar, } from './runtime-bin-resolver.js';
3
+ // The resolver logs per-runtime resolution lines meant for spawn time; during
4
+ // periodic detection they would repeat every interval, so detection summarizes
5
+ // in ONE line instead and hands the resolver a muted logger.
6
+ const MUTED_LOG = { info: () => { }, warn: () => { } };
7
+ const VERSION_PROBE_TIMEOUT_MS = 10_000;
8
+ // Server-side column cap is 64 (heartbeat validation rejects longer).
9
+ const VERSION_MAX_LEN = 64;
10
+ const IS_WIN32 = process.platform === 'win32';
11
+ // Mirrors the bridges' spawn quoting (claude-agent/codex-agent dispatch.ts):
12
+ // on Windows the resolved binary is usually a `.cmd` npm shim, which execFile
13
+ // can only launch through a shell — and shell mode requires manual quoting.
14
+ function quoteWin32Arg(arg) {
15
+ if (!/[\s"&|^<>()]/.test(arg))
16
+ return arg;
17
+ return `"${arg.replace(/"/g, '""')}"`;
18
+ }
19
+ /**
20
+ * Resolve a runtime CLI binary the same way agent spawn does. Exposed for
21
+ * tests to inject a fake resolver into `detectRuntimes`.
22
+ */
23
+ export function resolveRuntimeBinary(runtimeType, baseEnv) {
24
+ const envVar = runtimeBinaryEnvVar(runtimeType);
25
+ if (!envVar)
26
+ return null;
27
+ const env = applyRuntimeBinaryEnv(runtimeType, baseEnv, MUTED_LOG);
28
+ const binaryPath = env[envVar];
29
+ return binaryPath ? { binaryPath, env } : null;
30
+ }
31
+ function probeVersion(binaryPath, env) {
32
+ return new Promise((resolve) => {
33
+ execFile(IS_WIN32 ? quoteWin32Arg(binaryPath) : binaryPath, ['--version'],
34
+ // shell on Windows: same as the bridge spawn path — a `.cmd` shim can't
35
+ // be exec'd directly, and without it a healthy install probes 'broken'.
36
+ { env, timeout: VERSION_PROBE_TIMEOUT_MS, windowsHide: true, shell: IS_WIN32 }, (err, stdout) => {
37
+ if (err) {
38
+ resolve({ ok: false });
39
+ return;
40
+ }
41
+ const line = String(stdout)
42
+ .split(/\r?\n/)
43
+ .map((l) => l.trim())
44
+ .find((l) => l.length > 0) ?? '';
45
+ resolve(line ? { ok: true, version: line.slice(0, VERSION_MAX_LEN) } : { ok: true });
46
+ });
47
+ });
48
+ }
49
+ /**
50
+ * Detect which runtime CLIs exist on this host and whether they actually run.
51
+ * Absent runtimes are omitted; a binary that resolves but fails `--version`
52
+ * (exit != 0, timeout, spawn error) reports status 'broken' — a
53
+ * present-but-unusable install is a stronger signal than absence.
54
+ *
55
+ * Resolution reuses the exact spawn-time path (`applyRuntimeBinaryEnv`), so
56
+ * "detected" and "what an agent child would launch" can't drift apart. The
57
+ * probe runs with the resolver's anchored PATH because npm-shim CLIs
58
+ * (`#!/usr/bin/env node`) need their sibling node on PATH to execute.
59
+ */
60
+ export async function detectRuntimes(baseEnv, log, resolveBin = resolveRuntimeBinary) {
61
+ // Resolution is synchronous by nature (fs scans + possible login-shell), so
62
+ // it stays a plain loop; the probes are independent subprocesses and run in
63
+ // parallel — serial probing would stack their latencies (worst case one
64
+ // 10s timeout per broken CLI). Promise.all preserves the stable runtime
65
+ // order the change-comparison relies on.
66
+ const resolved = DETECTABLE_RUNTIME_TYPES.map((runtimeType) => ({
67
+ runtimeType,
68
+ bin: resolveBin(runtimeType, baseEnv),
69
+ }));
70
+ const entries = await Promise.all(resolved.map(async ({ runtimeType, bin }) => {
71
+ if (!bin)
72
+ return null;
73
+ const probe = await probeVersion(bin.binaryPath, bin.env);
74
+ if (probe.ok) {
75
+ return {
76
+ runtime_type: runtimeType,
77
+ ...(probe.version ? { version: probe.version } : {}),
78
+ status: 'ok',
79
+ };
80
+ }
81
+ log.warn(`runtime detection: ${runtimeType} resolved at ${bin.binaryPath} but --version failed`);
82
+ return { runtime_type: runtimeType, status: 'broken' };
83
+ }));
84
+ return entries.filter((e) => e !== null);
85
+ }
86
+ /** Order-sensitive compare — detection emits entries in stable runtime order. */
87
+ export function detectedRuntimesEqual(a, b) {
88
+ if (a.length !== b.length)
89
+ return false;
90
+ return a.every((x, i) => x.runtime_type === b[i].runtime_type &&
91
+ x.version === b[i].version &&
92
+ x.status === b[i].status);
93
+ }
94
+ export function summarizeDetectedRuntimes(detected) {
95
+ if (detected.length === 0)
96
+ return 'none found';
97
+ return detected
98
+ .map((d) => `${d.runtime_type}=${d.status === 'ok' ? (d.version ?? 'ok') : 'broken'}`)
99
+ .join(' ');
100
+ }
@@ -2,6 +2,7 @@ import { type GatewayLogger } from '@parall/agent-core';
2
2
  import { type ParallClient } from '@parall/sdk';
3
3
  import { type ClaudeDaemonConfig } from './config.js';
4
4
  import type { DaemonUpdater } from './updater.js';
5
+ import type { PendingUpdateHealthGate } from './update-health-gate.js';
5
6
  /**
6
7
  * Sleep that wakes early on abort. Returns true if the full delay elapsed,
7
8
  * false if aborted. Used by bootstrap retry and the outer keepalive in
@@ -28,8 +29,12 @@ export declare class DaemonSupervisor {
28
29
  private readonly spawningAgents;
29
30
  private readonly pendingWorkspaceSetup;
30
31
  private readonly browserProfileOpQueues;
32
+ private readonly browserProfilePendingRevives;
31
33
  private readonly workspaceSetupRetryTimers;
34
+ private readonly agentConfigRefreshRetryTimers;
32
35
  private readonly cancelledSpawns;
36
+ private readonly pendingConfigRefresh;
37
+ private readonly restartingStates;
33
38
  private ws;
34
39
  private running;
35
40
  private machineId;
@@ -40,23 +45,42 @@ export declare class DaemonSupervisor {
40
45
  private connectedClipServiceUrl;
41
46
  private stopResolve;
42
47
  private updater;
43
- private healthConfirmed;
44
- private browserProfileManager;
48
+ private healthGate;
49
+ private browserProfilePool;
45
50
  private clipManager;
46
51
  private clipProvider;
47
52
  private clipReconcileTimer;
48
53
  private clipReconcileInFlight;
54
+ private detectedRuntimes;
55
+ private runtimeDetectTimer;
56
+ private runtimeDetectInFlight;
57
+ private detectRuntimesFn;
49
58
  private hubClient;
50
59
  private hubClientUrl;
51
60
  constructor(config: ClaudeDaemonConfig, client: ParallClient, log: GatewayLogger);
52
61
  setUpdater(updater: DaemonUpdater): void;
62
+ /** Inject the process-level update health gate. The supervisor only feeds it
63
+ * fact signals (machine.hello, supervisor-ended); the gate owns confirm policy. */
64
+ setHealthGate(gate: PendingUpdateHealthGate): void;
53
65
  /** Start the supervisor. Returns a promise that resolves on `stop()`. */
54
66
  run(signal: AbortSignal): Promise<void>;
55
67
  /** Disconnect WS, cancel timers, SIGTERM all children, await exit. */
56
68
  stop(): Promise<void>;
57
69
  private bootstrapWithRetry;
58
70
  private fullReconcile;
71
+ private wipeBeforeRevive;
59
72
  private reconcileBrowserProfiles;
73
+ /**
74
+ * Resolve a profile's outbound proxy for bb-browser account creation (BYOC).
75
+ * Fetched fresh — account creation is rare (once per profile until reset), so a
76
+ * proxy edit applies on the next account_create. The mck_-authed machine list is
77
+ * the only path that carries proxy_password (for replay to bb-browser).
78
+ *
79
+ * Fails closed: a fetch failure or a vanished profile THROWS, so the manager never
80
+ * creates an account that would egress from the host's real IP for a
81
+ * proxy-configured profile (the caller reports `error` and retries next tick).
82
+ */
83
+ private resolveBrowserProfileProxy;
60
84
  /**
61
85
  * Detects a legacy flat state layout (no agents/ subdir) and migrates it
62
86
  * into the per-agent directory for the owning agent. Ownership is determined
@@ -70,6 +94,7 @@ export declare class DaemonSupervisor {
70
94
  private handleBrowserProfileLifecycle;
71
95
  private enqueueBrowserProfileLifecycle;
72
96
  private enqueueBrowserProfileOp;
97
+ private enqueueBrowserProfileRevive;
73
98
  /**
74
99
  * Handle one viewer control command and reply via REST. Always answers the
75
100
  * request/reply bridge exactly once ({result} on success, {error:{message}}
@@ -83,6 +108,15 @@ export declare class DaemonSupervisor {
83
108
  private reconcileMachineClipsNow;
84
109
  private startClipReconcileTimer;
85
110
  private stopClipReconcileTimer;
111
+ /**
112
+ * Detect runtime CLIs on this host and heartbeat the result. `initial`
113
+ * always reports (it also carries daemon version + self-update capability,
114
+ * replacing the old version-only startup heartbeat); periodic runs report
115
+ * only when the detection result changed, keeping steady state write-free.
116
+ * Serialized via `runtimeDetectInFlight` so a slow probe can't overlap the
117
+ * next interval tick and race the change comparison.
118
+ */
119
+ private detectAndReportRuntimes;
86
120
  private machineClipToConfig;
87
121
  private manifestFromMachineClip;
88
122
  private dependencyMap;
@@ -121,10 +155,38 @@ export declare class DaemonSupervisor {
121
155
  */
122
156
  private applyClipProviderState;
123
157
  private respawnAllChildren;
158
+ /**
159
+ * Per-agent counterpart of the machine.config.updated respawn: ONE agent's
160
+ * explicit llm_source changed, so refresh that agent's cached
161
+ * provider_config from the server and respawn only its child — every other
162
+ * child keeps running. The event payload carries only the EFFECTIVE source;
163
+ * the cache must hold the RAW per-agent config (see ChildState
164
+ * .providerConfig) so startChild keeps resolving inherit-vs-override
165
+ * against the CURRENT machine default — caching the effective value would
166
+ * pin this agent across future machine-level default changes.
167
+ */
168
+ private handleAgentConfigUpdated;
169
+ /**
170
+ * Intentional single-child respawn for a config change. Marks the state
171
+ * shuttingDown around the terminate so settleChild doesn't schedule a
172
+ * competing crash-backoff restart (which would also bump restartAttempts
173
+ * and delay the new route), clears any pending crash timer so
174
+ * restartChildNow isn't blocked by it, and bails if the agent detached
175
+ * while the child was terminating.
176
+ */
177
+ private respawnChildForConfig;
178
+ private scheduleAgentConfigRefreshRetry;
179
+ private clearAgentConfigRefreshRetry;
124
180
  private restartChildNow;
125
181
  private spawnAgent;
126
182
  private spawnAgentOnce;
127
183
  private startChild;
184
+ /**
185
+ * SIGTERM the child (SIGKILL after 10s) and resolve once it has exited.
186
+ * settleChild's 'exit' listener is registered before this one, so
187
+ * state.child is guaranteed cleared by the time this resolves — callers
188
+ * may start a replacement child immediately after awaiting.
189
+ */
128
190
  private terminateChild;
129
191
  private ensureSharedCredentialLink;
130
192
  }
@@ -1 +1 @@
1
- {"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAgBL,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAUrB,OAAO,EAKL,KAAK,kBAAkB,EAExB,MAAM,aAAa,CAAC;AAUrB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAsBvE;;;;GAIG;AACH,iBAAS,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAa3E;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAyB5B;;;;;;;;;;GAUG;AACH,qBAAa,gBAAgB;IAyEzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IA1EtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAM3D,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAoC;IAM3E,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAA6B;IAC1E,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAqC;IAC/E,OAAO,CAAC,QAAQ,CAAC,6BAA6B,CAAqC;IACnF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IAKrD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;IAQ1D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAyB;IAC1D,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,gBAAgB,CAAoB;IAG5C,OAAO,CAAC,sBAAsB,CAAQ;IAItC,OAAO,CAAC,sBAAsB,CAAuB;IAGrD,OAAO,CAAC,uBAAuB,CAAuB;IACtD,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,OAAO,CAA8B;IAI7C,OAAO,CAAC,UAAU,CAAwC;IAC1D,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,kBAAkB,CAA+B;IACzD,OAAO,CAAC,qBAAqB,CAA8B;IAI3D,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,kBAAkB,CAA+B;IACzD,OAAO,CAAC,qBAAqB,CAA8B;IAE3D,OAAO,CAAC,gBAAgB,CAAyC;IAIjE,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,YAAY,CAAuB;gBAGxB,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,aAAa;IAGrC,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAIxC;wFACoF;IACpF,aAAa,CAAC,IAAI,EAAE,uBAAuB,GAAG,IAAI;IAIlD,yEAAyE;IACnE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkN7C,sEAAsE;IAChE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YA8Db,kBAAkB;YAwClB,aAAa;YA6Eb,gBAAgB;YAiBhB,wBAAwB;IAiMtC;;;;;;;;;OASG;YACW,0BAA0B;IAkBxC;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;YA6CX,kBAAkB;YAqBlB,mBAAmB;YAgBnB,mBAAmB;YAqBnB,sBAAsB;YAwBtB,6BAA6B;IA6E3C,OAAO,CAAC,8BAA8B;IAkCtC,OAAO,CAAC,uBAAuB;IA4B/B,OAAO,CAAC,2BAA2B;IAYnC;;;;;OAKG;YACW,0BAA0B;IAiCxC,OAAO,CAAC,2BAA2B;YAIrB,cAAc;YAId,qBAAqB;YAgBrB,wBAAwB;IAoDtC,OAAO,CAAC,uBAAuB;IAU/B,OAAO,CAAC,sBAAsB;IAM9B;;;;;;;OAOG;IACH,OAAO,CAAC,uBAAuB;IA0B/B,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,uBAAuB;IAyB/B,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,mBAAmB;YAab,mBAAmB;IAyCjC,OAAO,CAAC,wBAAwB;YAelB,6BAA6B;IAqC3C,OAAO,CAAC,2BAA2B;IAYnC,OAAO,CAAC,wBAAwB;YAUlB,oBAAoB;IAmClC;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IASpB;;;;;;;;OAQG;YACW,sBAAsB;YA4CtB,kBAAkB;IAahC;;;;;;;;;OASG;YACW,wBAAwB;IAwDtC;;;;;;;OAOG;YACW,qBAAqB;IAcnC,OAAO,CAAC,+BAA+B;IAYvC,OAAO,CAAC,4BAA4B;YAQtB,eAAe;YAgDf,UAAU;YA+BV,cAAc;IAkE5B,OAAO,CAAC,UAAU;IA0HlB;;;;;OAKG;YACW,cAAc;IAyB5B,OAAO,CAAC,0BAA0B;CAkCnC"}