@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.
- package/bundle/manifest.json +11 -11
- package/bundle/parall-browser-pod.js +384 -32
- package/bundle/parall-claude-agent.js +426 -51
- package/bundle/parall-codex-agent.js +425 -49
- package/bundle/parall-daemon.js +33336 -31520
- package/dist/browser-pod.d.ts +7 -0
- package/dist/browser-pod.d.ts.map +1 -1
- package/dist/browser-pod.js +73 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/clip-runtime/browser-daemon-env.d.ts +29 -0
- package/dist/clip-runtime/browser-daemon-env.d.ts.map +1 -0
- package/dist/clip-runtime/browser-daemon-env.js +70 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts +34 -5
- package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
- package/dist/clip-runtime/browser-profile-manager.js +166 -24
- package/dist/clip-runtime/browser-profile-pool.d.ts +250 -0
- package/dist/clip-runtime/browser-profile-pool.d.ts.map +1 -0
- package/dist/clip-runtime/browser-profile-pool.js +581 -0
- package/dist/clip-runtime/index.d.ts +2 -1
- package/dist/clip-runtime/index.d.ts.map +1 -1
- package/dist/clip-runtime/index.js +2 -1
- package/dist/clip-runtime/process-manager.d.ts +5 -3
- package/dist/clip-runtime/process-manager.d.ts.map +1 -1
- package/dist/clip-runtime/subprocess.d.ts +14 -0
- package/dist/clip-runtime/subprocess.d.ts.map +1 -1
- package/dist/clip-runtime/subprocess.js +49 -0
- package/dist/config.d.ts +14 -15
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -26
- package/dist/daemon-main.d.ts +8 -0
- package/dist/daemon-main.d.ts.map +1 -0
- package/dist/daemon-main.js +165 -0
- package/dist/daemon-paths.d.ts +14 -0
- package/dist/daemon-paths.d.ts.map +1 -0
- package/dist/daemon-paths.js +26 -0
- package/dist/daemon-update-mode.d.ts +8 -0
- package/dist/daemon-update-mode.d.ts.map +1 -0
- package/dist/daemon-update-mode.js +18 -0
- package/dist/index.js +41 -167
- package/dist/runtime-bin-resolver.d.ts +4 -0
- package/dist/runtime-bin-resolver.d.ts.map +1 -1
- package/dist/runtime-bin-resolver.js +40 -7
- package/dist/runtime-detector.d.ts +30 -0
- package/dist/runtime-detector.d.ts.map +1 -0
- package/dist/runtime-detector.js +100 -0
- package/dist/supervisor.d.ts +64 -2
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +629 -67
- package/dist/update-health-gate.d.ts +66 -0
- package/dist/update-health-gate.d.ts.map +1 -0
- package/dist/update-health-gate.js +93 -0
- package/dist/updater-manifest.d.ts +2 -1
- package/dist/updater-manifest.d.ts.map +1 -1
- package/dist/updater-manifest.js +38 -7
- package/dist/updater.d.ts +13 -2
- package/dist/updater.d.ts.map +1 -1
- package/dist/updater.js +126 -17
- 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 {
|
|
4
|
-
import {
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
130
|
-
|
|
47
|
+
catch (err) {
|
|
48
|
+
console.warn(`failed to write daemon running marker: ${formatError(err)}`);
|
|
131
49
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
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
|
|
176
|
-
|
|
177
|
-
.then((
|
|
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
|
-
|
|
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;
|
|
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 =
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
282
|
+
return undefined;
|
|
250
283
|
if (entry.expiresAt <= Date.now()) {
|
|
251
284
|
resolutionCache.delete(cacheKey);
|
|
252
|
-
return
|
|
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
|
+
}
|
package/dist/supervisor.d.ts
CHANGED
|
@@ -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
|
|
44
|
-
private
|
|
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
|
}
|
package/dist/supervisor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"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"}
|