@parall/daemon 1.32.0 → 1.33.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/bb-browser-daemon.js +15628 -0
- package/bundle/buildDomTree.js +1501 -0
- package/bundle/manifest.json +19 -11
- package/bundle/parall-claude-agent.js +224 -58
- package/bundle/parall-codex-agent.js +224 -58
- package/bundle/parall-daemon.js +4507 -2668
- package/bundle/parall-openclaw-agent.js +4 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -2
- package/dist/clip-runtime/browser-dependency.d.ts +20 -0
- package/dist/clip-runtime/browser-dependency.d.ts.map +1 -0
- package/dist/clip-runtime/browser-dependency.js +52 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts +67 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -0
- package/dist/clip-runtime/browser-profile-manager.js +595 -0
- package/dist/clip-runtime/bun-resolver.d.ts +24 -0
- package/dist/clip-runtime/bun-resolver.d.ts.map +1 -0
- package/dist/clip-runtime/bun-resolver.js +58 -0
- package/dist/clip-runtime/clip-installer.d.ts.map +1 -1
- package/dist/clip-runtime/clip-installer.js +59 -18
- package/dist/clip-runtime/clip-provider.d.ts +13 -2
- package/dist/clip-runtime/clip-provider.d.ts.map +1 -1
- package/dist/clip-runtime/clip-provider.js +106 -36
- package/dist/clip-runtime/hub-client.d.ts +79 -0
- package/dist/clip-runtime/hub-client.d.ts.map +1 -0
- package/dist/clip-runtime/hub-client.js +320 -0
- package/dist/clip-runtime/index.d.ts +2 -0
- package/dist/clip-runtime/index.d.ts.map +1 -1
- package/dist/clip-runtime/index.js +2 -0
- package/dist/clip-runtime/ipc.d.ts +6 -0
- package/dist/clip-runtime/ipc.d.ts.map +1 -1
- package/dist/clip-runtime/manifest.d.ts +16 -8
- package/dist/clip-runtime/manifest.d.ts.map +1 -1
- package/dist/clip-runtime/manifest.js +13 -0
- package/dist/clip-runtime/process-manager.d.ts +55 -3
- package/dist/clip-runtime/process-manager.d.ts.map +1 -1
- package/dist/clip-runtime/process-manager.js +233 -76
- package/dist/clip-runtime/process.d.ts +15 -1
- package/dist/clip-runtime/process.d.ts.map +1 -1
- package/dist/clip-runtime/process.js +73 -10
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -0
- package/dist/index.js +46 -5
- package/dist/runtime-bin-resolver.d.ts +7 -0
- package/dist/runtime-bin-resolver.d.ts.map +1 -0
- package/dist/runtime-bin-resolver.js +292 -0
- package/dist/supervisor.d.ts +53 -4
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +449 -117
- package/package.json +7 -6
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,2GAA2G;IAC3G,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,YAAY,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,cAAc,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,0BAA0B,EAAE,MAAM,CAAC;IACnC,6BAA6B,EAAE,MAAM,CAAC;IACtC,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,gBAAgB,EAAE,MAAM,CAAC;IACzB,4DAA4D;IAC5D,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AA2BF,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,2GAA2G;IAC3G,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,YAAY,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,cAAc,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,0BAA0B,EAAE,MAAM,CAAC;IACnC,6BAA6B,EAAE,MAAM,CAAC;IACtC,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,gBAAgB,EAAE,MAAM,CAAC;IACzB,4DAA4D;IAC5D,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AA2BF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAI5E;AAED,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE7E;AAyBD,wBAAgB,yBAAyB,CACvC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,kBAAkB,CA0DpB;AASD,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED;;mEAEmE;AACnE,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAElF;AAED,+EAA+E;AAC/E,wBAAgB,8BAA8B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,uEAAuE;AACvE,wBAAgB,6BAA6B,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,iEAAiE;AACjE,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAElF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAG7E;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAMR"}
|
package/dist/config.js
CHANGED
|
@@ -24,7 +24,19 @@ function parseMsAllowZero(value, fallback) {
|
|
|
24
24
|
const n = Number(value);
|
|
25
25
|
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Root dir for all on-disk daemon state: `config.json`, the self-update
|
|
29
|
+
* `bundle/` overlay (versions + current symlink + update-state.json), and
|
|
30
|
+
* bridge-bin resolution all derive from this single path. `PRLL_DAEMON_CONFIG_DIR`
|
|
31
|
+
* overrides it so multiple daemons can coexist on one host without colliding —
|
|
32
|
+
* e.g. a staging desktop install points its launchd agent at
|
|
33
|
+
* `~/.parall-daemon-staging` while prod keeps the default `~/.parall-daemon`.
|
|
34
|
+
* Unset → default (no behavior change for existing single-env installs).
|
|
35
|
+
*/
|
|
27
36
|
export function daemonConfigDir(env = process.env) {
|
|
37
|
+
const override = env.PRLL_DAEMON_CONFIG_DIR?.trim();
|
|
38
|
+
if (override)
|
|
39
|
+
return resolvePath(override);
|
|
28
40
|
return path.join(env.HOME || os.homedir(), '.parall-daemon');
|
|
29
41
|
}
|
|
30
42
|
export function daemonConfigPath(env = process.env) {
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import * as path from 'node:path';
|
|
2
3
|
import { createLogger, createOtelLogger, initAgentTelemetry, } from '@parall/agent-core';
|
|
3
4
|
import { ParallClient } from '@parall/sdk';
|
|
4
|
-
import {
|
|
5
|
+
import { runCLI } from './cli.js';
|
|
6
|
+
import { resolveBundleDir, resolveClaudeDaemonConfig, resolveWsUrl, } from './config.js';
|
|
5
7
|
import { DaemonSupervisor, sleepCancellable } from './supervisor.js';
|
|
6
8
|
import { DaemonUpdater } from './updater.js';
|
|
7
|
-
import { runCLI } from './cli.js';
|
|
8
9
|
const UPDATE_EXIT_CODE = 42;
|
|
9
10
|
let log = createLogger('daemon');
|
|
10
11
|
function formatError(reason) {
|
|
@@ -13,6 +14,39 @@ function formatError(reason) {
|
|
|
13
14
|
}
|
|
14
15
|
return String(reason);
|
|
15
16
|
}
|
|
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
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
16
50
|
/**
|
|
17
51
|
* Outer keepalive: runs `supervisor.run()` in a loop, restarting it with
|
|
18
52
|
* exponential backoff if it rejects. The daemon process only exits when
|
|
@@ -61,7 +95,7 @@ async function runForever(config, client, log, signal, updater) {
|
|
|
61
95
|
log.error('supervisor keepalive disabled (PRLL_DAEMON_SUPERVISOR_RESTART_BACKOFF_MS=0) — exiting');
|
|
62
96
|
throw err;
|
|
63
97
|
}
|
|
64
|
-
const delay = Math.min(config.supervisorRestartBackoffMs *
|
|
98
|
+
const delay = Math.min(config.supervisorRestartBackoffMs * 2 ** attempt, config.supervisorRestartBackoffMaxMs);
|
|
65
99
|
attempt += 1;
|
|
66
100
|
log.warn(`restarting supervisor in ${delay}ms (attempt ${attempt})`);
|
|
67
101
|
const slept = await sleepCancellable(delay, signal);
|
|
@@ -77,9 +111,13 @@ async function main() {
|
|
|
77
111
|
log.info(`boot: api=${config.apiUrl} pollMs=${config.pollIntervalMs} heartbeatMs=${config.heartbeatIntervalMs} agentBin=${config.agentBin}`);
|
|
78
112
|
log.info(`keepalive: bootstrapBackoffMs=${config.bootstrapBackoffMs} supervisorRestartBackoffMs=${config.supervisorRestartBackoffMs}`);
|
|
79
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.
|
|
80
117
|
let updater = null;
|
|
81
|
-
|
|
82
|
-
|
|
118
|
+
const bundleDir = resolveBundleDir(process.env);
|
|
119
|
+
const selfUpdateEnabled = !config.updateDisabled && isSelfUpdateManaged(process.env, bundleDir);
|
|
120
|
+
if (selfUpdateEnabled) {
|
|
83
121
|
updater = new DaemonUpdater(bundleDir, config.updateCdnUrl, log, true);
|
|
84
122
|
if (updater.checkRollback()) {
|
|
85
123
|
log.info('rollback applied — exiting for service manager restart');
|
|
@@ -88,6 +126,9 @@ async function main() {
|
|
|
88
126
|
}
|
|
89
127
|
log.info(`update: bundleDir=${bundleDir} cdn=${config.updateCdnUrl} interval=${config.updateIntervalMs}ms`);
|
|
90
128
|
}
|
|
129
|
+
else if (!config.updateDisabled) {
|
|
130
|
+
log.info('update: skipped (foreground mode) — run `parall-daemon service install` for managed auto-update');
|
|
131
|
+
}
|
|
91
132
|
// The daemon talks to the API as a Machine — the bearer is mck_*.
|
|
92
133
|
// No orgId is configured here; per-agent subprocesses get their
|
|
93
134
|
// own org_id via the spawn env.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type RuntimeBinLogger = {
|
|
2
|
+
info: (msg: string) => void;
|
|
3
|
+
warn: (msg: string) => void;
|
|
4
|
+
};
|
|
5
|
+
export declare function applyRuntimeBinaryEnv(runtimeType: string, baseEnv: NodeJS.ProcessEnv, log: RuntimeBinLogger): NodeJS.ProcessEnv;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=runtime-bin-resolver.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
const RUNTIME_BINARIES = {
|
|
6
|
+
'claude-code': { command: 'claude', envVar: 'PRLL_CLAUDE_BIN' },
|
|
7
|
+
codex: { command: 'codex', envVar: 'PRLL_CODEX_BIN' },
|
|
8
|
+
openclaw: { command: 'openclaw', envVar: 'PRLL_OPENCLAW_BIN' },
|
|
9
|
+
};
|
|
10
|
+
const RESOLUTION_CACHE_TTL_MS = 5 * 60_000;
|
|
11
|
+
const resolutionCache = new Map();
|
|
12
|
+
export function applyRuntimeBinaryEnv(runtimeType, baseEnv, log) {
|
|
13
|
+
const env = { ...baseEnv };
|
|
14
|
+
const originalPath = env.PATH;
|
|
15
|
+
const pathPlan = candidatePathPlan(env);
|
|
16
|
+
const primaryPath = mergePath(pathPlan.primaryDirs, originalPath);
|
|
17
|
+
env.PATH = mergePath([...pathPlan.primaryDirs, ...pathPlan.versionedFallbackDirs], originalPath);
|
|
18
|
+
const spec = RUNTIME_BINARIES[runtimeType];
|
|
19
|
+
if (!spec)
|
|
20
|
+
return env;
|
|
21
|
+
const configured = env[spec.envVar]?.trim();
|
|
22
|
+
if (configured) {
|
|
23
|
+
const resolved = resolveRuntimeCommand(configured, env, originalPath, primaryPath, env.PATH);
|
|
24
|
+
if (resolved) {
|
|
25
|
+
env[spec.envVar] = resolved.binaryPath;
|
|
26
|
+
env.PATH = anchorResolvedPath(env.PATH, resolved);
|
|
27
|
+
log.info(`runtime ${runtimeType}: using ${spec.envVar}=${resolved.binaryPath}`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
log.warn(`runtime ${runtimeType}: configured ${spec.envVar}=${configured} is not executable`);
|
|
31
|
+
}
|
|
32
|
+
return env;
|
|
33
|
+
}
|
|
34
|
+
const resolved = resolveRuntimeCommand(spec.command, env, originalPath, primaryPath, env.PATH);
|
|
35
|
+
if (resolved) {
|
|
36
|
+
env[spec.envVar] = resolved.binaryPath;
|
|
37
|
+
env.PATH = anchorResolvedPath(env.PATH, resolved);
|
|
38
|
+
log.info(`runtime ${runtimeType}: resolved ${spec.command} at ${resolved.binaryPath}`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
log.warn(`runtime ${runtimeType}: could not resolve ${spec.command}; child will rely on PATH ` +
|
|
42
|
+
`(${env.PATH ?? '<empty>'})`);
|
|
43
|
+
}
|
|
44
|
+
return env;
|
|
45
|
+
}
|
|
46
|
+
function resolveRuntimeCommand(command, env, inheritedPath, primaryPath, fallbackPath) {
|
|
47
|
+
const cacheKey = `runtime\0${command}\0${primaryPath}\0${fallbackPath}\0${env.SHELL ?? ''}\0${env.HOME ?? ''}`;
|
|
48
|
+
const cached = getCachedResolution(cacheKey);
|
|
49
|
+
if (cached)
|
|
50
|
+
return cached;
|
|
51
|
+
const direct = resolveDirectPath(command);
|
|
52
|
+
if (direct) {
|
|
53
|
+
const resolved = { binaryPath: direct };
|
|
54
|
+
setCachedResolution(cacheKey, resolved);
|
|
55
|
+
return resolved;
|
|
56
|
+
}
|
|
57
|
+
const fromInheritedPath = resolveFromPath(command, inheritedPath, env);
|
|
58
|
+
if (fromInheritedPath) {
|
|
59
|
+
setCachedResolution(cacheKey, fromInheritedPath);
|
|
60
|
+
return fromInheritedPath;
|
|
61
|
+
}
|
|
62
|
+
const fromShell = resolveFromLoginShell(command, { ...env, PATH: inheritedPath });
|
|
63
|
+
if (fromShell) {
|
|
64
|
+
setCachedResolution(cacheKey, fromShell);
|
|
65
|
+
return fromShell;
|
|
66
|
+
}
|
|
67
|
+
const fromPrimaryPath = resolveFromPath(command, primaryPath, env);
|
|
68
|
+
if (fromPrimaryPath) {
|
|
69
|
+
setCachedResolution(cacheKey, fromPrimaryPath);
|
|
70
|
+
return fromPrimaryPath;
|
|
71
|
+
}
|
|
72
|
+
const fromFallbackPath = fallbackPath === primaryPath ? null : resolveFromPath(command, fallbackPath, env);
|
|
73
|
+
if (fromFallbackPath) {
|
|
74
|
+
setCachedResolution(cacheKey, fromFallbackPath);
|
|
75
|
+
return fromFallbackPath;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function resolveDirectPath(command) {
|
|
80
|
+
if (!command.includes('/') && !command.includes('\\'))
|
|
81
|
+
return null;
|
|
82
|
+
const abs = path.isAbsolute(command) ? command : path.resolve(process.cwd(), command);
|
|
83
|
+
return isExecutable(abs) ? abs : null;
|
|
84
|
+
}
|
|
85
|
+
function resolveFromPath(command, pathValue, env) {
|
|
86
|
+
if (!pathValue || command.includes('/') || command.includes('\\'))
|
|
87
|
+
return null;
|
|
88
|
+
const dirs = pathValue.split(path.delimiter).filter(Boolean);
|
|
89
|
+
for (const dir of dirs) {
|
|
90
|
+
for (const file of commandCandidates(command, env)) {
|
|
91
|
+
const candidate = path.join(dir, file);
|
|
92
|
+
if (isExecutable(candidate))
|
|
93
|
+
return { binaryPath: candidate, pathValue };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function resolveFromLoginShell(command, env) {
|
|
99
|
+
if (command.includes('/') || command.includes('\\'))
|
|
100
|
+
return null;
|
|
101
|
+
const shells = unique([
|
|
102
|
+
env.SHELL?.trim(),
|
|
103
|
+
process.platform === 'darwin' ? '/bin/zsh' : undefined,
|
|
104
|
+
'/bin/bash',
|
|
105
|
+
'/bin/sh',
|
|
106
|
+
]);
|
|
107
|
+
for (const shell of shells) {
|
|
108
|
+
if (!shell || !isExecutable(shell))
|
|
109
|
+
continue;
|
|
110
|
+
try {
|
|
111
|
+
const out = execFileSync(shell, [
|
|
112
|
+
'-lic',
|
|
113
|
+
`resolved=$(command -v ${shellQuote(command)}) || exit $?; printf '__PRLL_BIN__%s\n__PRLL_PATH__%s\n' "$resolved" "$PATH"`,
|
|
114
|
+
], {
|
|
115
|
+
encoding: 'utf8',
|
|
116
|
+
env,
|
|
117
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
118
|
+
timeout: 2_500,
|
|
119
|
+
});
|
|
120
|
+
let binaryPath = '';
|
|
121
|
+
let pathValue = '';
|
|
122
|
+
for (const line of out.split(/\r?\n/)) {
|
|
123
|
+
if (line.startsWith('__PRLL_BIN__'))
|
|
124
|
+
binaryPath = line.slice('__PRLL_BIN__'.length).trim();
|
|
125
|
+
if (line.startsWith('__PRLL_PATH__'))
|
|
126
|
+
pathValue = line.slice('__PRLL_PATH__'.length);
|
|
127
|
+
}
|
|
128
|
+
if (path.isAbsolute(binaryPath) && isExecutable(binaryPath)) {
|
|
129
|
+
return { binaryPath, pathValue: pathValue || undefined };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Shell startup files may be noisy or slow. Keep resolution best-effort.
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
function candidatePathPlan(env) {
|
|
139
|
+
const home = env.HOME || os.homedir();
|
|
140
|
+
const primaryDirs = [
|
|
141
|
+
...splitPath(env.PRLL_DAEMON_RUNTIME_PATH),
|
|
142
|
+
...splitPath(env.PRLL_DAEMON_EXTRA_PATH),
|
|
143
|
+
path.dirname(process.execPath),
|
|
144
|
+
path.join(path.dirname(process.execPath), 'bin'),
|
|
145
|
+
path.resolve(path.dirname(process.execPath), '..', 'Resources', 'bin'),
|
|
146
|
+
path.join(home, '.local', 'bin'),
|
|
147
|
+
path.join(home, 'bin'),
|
|
148
|
+
path.join(home, '.npm-global', 'bin'),
|
|
149
|
+
path.join(home, 'Library', 'pnpm'),
|
|
150
|
+
path.join(home, '.local', 'share', 'pnpm'),
|
|
151
|
+
path.join(home, '.volta', 'bin'),
|
|
152
|
+
path.join(home, '.bun', 'bin'),
|
|
153
|
+
path.join(home, '.asdf', 'shims'),
|
|
154
|
+
path.join(home, '.local', 'share', 'mise', 'shims'),
|
|
155
|
+
path.join(home, '.mise', 'shims'),
|
|
156
|
+
path.join(home, '.fnm', 'aliases', 'default', 'bin'),
|
|
157
|
+
path.join(home, 'Library', 'Application Support', 'fnm', 'aliases', 'default', 'bin'),
|
|
158
|
+
'/opt/homebrew/bin',
|
|
159
|
+
'/usr/local/bin',
|
|
160
|
+
'/usr/bin',
|
|
161
|
+
'/bin',
|
|
162
|
+
'/usr/sbin',
|
|
163
|
+
'/sbin',
|
|
164
|
+
'/snap/bin',
|
|
165
|
+
];
|
|
166
|
+
const versionedFallbackDirs = [
|
|
167
|
+
...nvmVersionBinDirs(home),
|
|
168
|
+
...fnmVersionBinDirs(home),
|
|
169
|
+
];
|
|
170
|
+
return {
|
|
171
|
+
primaryDirs: unique(primaryDirs).filter((dir) => !!dir && isDirectory(dir)),
|
|
172
|
+
versionedFallbackDirs: unique(versionedFallbackDirs).filter((dir) => !!dir && isDirectory(dir)),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function nvmVersionBinDirs(home) {
|
|
176
|
+
const root = path.join(home, '.nvm', 'versions', 'node');
|
|
177
|
+
let versions;
|
|
178
|
+
try {
|
|
179
|
+
versions = fs.readdirSync(root);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
return sortVersionNamesDesc(versions).map((version) => path.join(root, version, 'bin'));
|
|
185
|
+
}
|
|
186
|
+
function fnmVersionBinDirs(home) {
|
|
187
|
+
const roots = [
|
|
188
|
+
path.join(home, '.fnm', 'node-versions'),
|
|
189
|
+
path.join(home, 'Library', 'Application Support', 'fnm', 'node-versions'),
|
|
190
|
+
];
|
|
191
|
+
const dirs = [];
|
|
192
|
+
for (const root of roots) {
|
|
193
|
+
let versions;
|
|
194
|
+
try {
|
|
195
|
+
versions = fs.readdirSync(root);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
dirs.push(...sortVersionNamesDesc(versions).map((version) => path.join(root, version, 'installation', 'bin')));
|
|
201
|
+
}
|
|
202
|
+
return dirs;
|
|
203
|
+
}
|
|
204
|
+
function sortVersionNamesDesc(values) {
|
|
205
|
+
return [...values].sort((a, b) => compareVersionName(b, a));
|
|
206
|
+
}
|
|
207
|
+
function compareVersionName(a, b) {
|
|
208
|
+
const av = parseVersionName(a);
|
|
209
|
+
const bv = parseVersionName(b);
|
|
210
|
+
const max = Math.max(av.length, bv.length);
|
|
211
|
+
for (let i = 0; i < max; i += 1) {
|
|
212
|
+
const diff = (av[i] ?? 0) - (bv[i] ?? 0);
|
|
213
|
+
if (diff !== 0)
|
|
214
|
+
return diff;
|
|
215
|
+
}
|
|
216
|
+
return a.localeCompare(b);
|
|
217
|
+
}
|
|
218
|
+
function parseVersionName(value) {
|
|
219
|
+
return value
|
|
220
|
+
.replace(/^v/i, '')
|
|
221
|
+
.split('.')
|
|
222
|
+
.map((part) => Number.parseInt(part, 10))
|
|
223
|
+
.filter((part) => Number.isFinite(part));
|
|
224
|
+
}
|
|
225
|
+
function splitPath(value) {
|
|
226
|
+
return value?.split(path.delimiter).filter(Boolean) ?? [];
|
|
227
|
+
}
|
|
228
|
+
function mergePath(prependDirs, existing) {
|
|
229
|
+
return unique([...prependDirs, ...splitPath(existing)]).join(path.delimiter);
|
|
230
|
+
}
|
|
231
|
+
function anchorResolvedPath(pathValue, resolution) {
|
|
232
|
+
return mergePath([path.dirname(resolution.binaryPath), ...splitPath(resolution.pathValue)], pathValue);
|
|
233
|
+
}
|
|
234
|
+
function commandCandidates(command, env) {
|
|
235
|
+
if (process.platform !== 'win32')
|
|
236
|
+
return [command];
|
|
237
|
+
const hasExt = /\.[^\\/]+$/.test(command);
|
|
238
|
+
if (hasExt)
|
|
239
|
+
return [command];
|
|
240
|
+
const exts = (env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
|
|
241
|
+
.split(';')
|
|
242
|
+
.filter(Boolean)
|
|
243
|
+
.map((ext) => ext.toLowerCase());
|
|
244
|
+
return [command, ...exts.map((ext) => `${command}${ext}`)];
|
|
245
|
+
}
|
|
246
|
+
function getCachedResolution(cacheKey) {
|
|
247
|
+
const entry = resolutionCache.get(cacheKey);
|
|
248
|
+
if (!entry)
|
|
249
|
+
return null;
|
|
250
|
+
if (entry.expiresAt <= Date.now()) {
|
|
251
|
+
resolutionCache.delete(cacheKey);
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
return entry.value;
|
|
255
|
+
}
|
|
256
|
+
function setCachedResolution(cacheKey, value) {
|
|
257
|
+
resolutionCache.set(cacheKey, { value, expiresAt: Date.now() + RESOLUTION_CACHE_TTL_MS });
|
|
258
|
+
}
|
|
259
|
+
function isExecutable(file) {
|
|
260
|
+
try {
|
|
261
|
+
const stat = fs.statSync(file);
|
|
262
|
+
if (!stat.isFile())
|
|
263
|
+
return false;
|
|
264
|
+
fs.accessSync(file, fs.constants.X_OK);
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function isDirectory(dir) {
|
|
272
|
+
try {
|
|
273
|
+
return fs.statSync(dir).isDirectory();
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function unique(values) {
|
|
280
|
+
const seen = new Set();
|
|
281
|
+
const out = [];
|
|
282
|
+
for (const value of values) {
|
|
283
|
+
if (!value || seen.has(value))
|
|
284
|
+
continue;
|
|
285
|
+
seen.add(value);
|
|
286
|
+
out.push(value);
|
|
287
|
+
}
|
|
288
|
+
return out;
|
|
289
|
+
}
|
|
290
|
+
function shellQuote(value) {
|
|
291
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
292
|
+
}
|
package/dist/supervisor.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type GatewayLogger } from '@parall/agent-core';
|
|
2
|
-
import { ParallClient } from '@parall/sdk';
|
|
3
|
-
import type { DaemonUpdater } from './updater.js';
|
|
2
|
+
import { type ParallClient } from '@parall/sdk';
|
|
4
3
|
import { type ClaudeDaemonConfig } from './config.js';
|
|
4
|
+
import type { DaemonUpdater } from './updater.js';
|
|
5
5
|
/**
|
|
6
6
|
* Sleep that wakes early on abort. Returns true if the full delay elapsed,
|
|
7
7
|
* false if aborted. Used by bootstrap retry and the outer keepalive in
|
|
@@ -27,6 +27,7 @@ export declare class DaemonSupervisor {
|
|
|
27
27
|
private readonly children;
|
|
28
28
|
private readonly spawningAgents;
|
|
29
29
|
private readonly pendingWorkspaceSetup;
|
|
30
|
+
private readonly browserProfileLifecycleQueues;
|
|
30
31
|
private readonly workspaceSetupRetryTimers;
|
|
31
32
|
private readonly cancelledSpawns;
|
|
32
33
|
private ws;
|
|
@@ -34,11 +35,19 @@ export declare class DaemonSupervisor {
|
|
|
34
35
|
private machineId;
|
|
35
36
|
private machineOrgId;
|
|
36
37
|
private machineLlmSource;
|
|
38
|
+
private machineProviderEnabled;
|
|
39
|
+
private machineClipProviderUrl;
|
|
40
|
+
private connectedClipServiceUrl;
|
|
37
41
|
private stopResolve;
|
|
38
42
|
private updater;
|
|
39
43
|
private healthConfirmed;
|
|
44
|
+
private browserProfileManager;
|
|
40
45
|
private clipManager;
|
|
41
46
|
private clipProvider;
|
|
47
|
+
private clipReconcileTimer;
|
|
48
|
+
private clipReconcileInFlight;
|
|
49
|
+
private hubClient;
|
|
50
|
+
private hubClientUrl;
|
|
42
51
|
constructor(config: ClaudeDaemonConfig, client: ParallClient, log: GatewayLogger);
|
|
43
52
|
setUpdater(updater: DaemonUpdater): void;
|
|
44
53
|
/** Start the supervisor. Returns a promise that resolves on `stop()`. */
|
|
@@ -47,6 +56,7 @@ export declare class DaemonSupervisor {
|
|
|
47
56
|
stop(): Promise<void>;
|
|
48
57
|
private bootstrapWithRetry;
|
|
49
58
|
private fullReconcile;
|
|
59
|
+
private reconcileBrowserProfiles;
|
|
50
60
|
/**
|
|
51
61
|
* Detects a legacy flat state layout (no agents/ subdir) and migrates it
|
|
52
62
|
* into the per-agent directory for the owning agent. Ownership is determined
|
|
@@ -57,12 +67,51 @@ export declare class DaemonSupervisor {
|
|
|
57
67
|
private handleAgentAttached;
|
|
58
68
|
private handleAgentDetached;
|
|
59
69
|
private handleFilesystemBrowse;
|
|
60
|
-
private
|
|
61
|
-
private
|
|
70
|
+
private handleBrowserProfileLifecycle;
|
|
71
|
+
private enqueueBrowserProfileLifecycle;
|
|
72
|
+
private handleClipSync;
|
|
73
|
+
private reconcileMachineClips;
|
|
74
|
+
private reconcileMachineClipsNow;
|
|
75
|
+
private startClipReconcileTimer;
|
|
76
|
+
private stopClipReconcileTimer;
|
|
77
|
+
private machineClipToConfig;
|
|
78
|
+
private manifestFromMachineClip;
|
|
79
|
+
private dependencyMap;
|
|
80
|
+
private clipConfigSignature;
|
|
81
|
+
private ensureClipInstalled;
|
|
82
|
+
private readInstalledClipVersion;
|
|
83
|
+
private reconnectClipProvider;
|
|
62
84
|
private handleWorkspaceSetupRequested;
|
|
63
85
|
private scheduleWorkspaceSetupRetry;
|
|
64
86
|
private clearWorkspaceSetupRetry;
|
|
65
87
|
private refreshMachineConfig;
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the ProviderStream endpoint the daemon registers clips against.
|
|
90
|
+
* Priority: PRLL_CLIP_SERVICE_URL (hosted/cluster DNS or explicit operator
|
|
91
|
+
* override) > machine.clip_provider_url (server-delivered grey-cloud
|
|
92
|
+
* clip-rpc host for BYOC) > config.apiUrl (legacy fallback for servers
|
|
93
|
+
* predating clip_provider_url). The grey-cloud host bypasses Cloudflare's
|
|
94
|
+
* proxy, which does not pass Connect-RPC bidi streams (#1430).
|
|
95
|
+
*/
|
|
96
|
+
private resolveClipServiceUrl;
|
|
97
|
+
/**
|
|
98
|
+
* Execution-side hub client used to resolve a Clip's dependency
|
|
99
|
+
* binding (GetBindings) and forward the dependency invoke (Invoke). It targets
|
|
100
|
+
* the same grey-cloud clip-rpc endpoint as the provider stream and auths with
|
|
101
|
+
* the daemon's mck_ (the hub scope-gates calls to instances this machine
|
|
102
|
+
* executes). Rebuilt transparently when the resolved endpoint changes.
|
|
103
|
+
*/
|
|
104
|
+
private getHubClient;
|
|
105
|
+
/**
|
|
106
|
+
* Connect, reconnect, or disconnect the clip Provider to match
|
|
107
|
+
* machineProviderEnabled and the resolved ProviderStream endpoint. An org
|
|
108
|
+
* admin toggles provider_enabled to withdraw the machine from the org clip
|
|
109
|
+
* provider pool without uninstalling clips; a changed clip_provider_url
|
|
110
|
+
* triggers a transparent reconnect to the new endpoint. No-op when the clip
|
|
111
|
+
* runtime is disabled or identity is not yet resolved. See
|
|
112
|
+
* docs/engineering-design/clip-provider-sharing-design.md.
|
|
113
|
+
*/
|
|
114
|
+
private applyClipProviderState;
|
|
66
115
|
private respawnAllChildren;
|
|
67
116
|
private restartChildNow;
|
|
68
117
|
private spawnAgent;
|
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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"}
|