@parall/daemon 1.29.0 → 1.29.2
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 +8 -8
- package/bundle/parall-claude-agent.js +129 -25
- package/bundle/parall-codex-agent.js +205 -28
- package/bundle/parall-daemon.js +726 -106
- package/dist/config.d.ts +3 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -2
- package/dist/index.js +2 -1
- package/dist/runtimes.d.ts +1 -0
- package/dist/runtimes.d.ts.map +1 -1
- package/dist/runtimes.js +1 -1
- package/dist/supervisor.d.ts +9 -0
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +130 -16
- package/dist/workspace.d.ts +11 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +439 -0
- package/package.json +6 -6
package/dist/config.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export type ClaudeDaemonConfig = {
|
|
|
22
22
|
rootClaudeHome: string;
|
|
23
23
|
/** Optional WS URL override for the machine control-plane WebSocket. */
|
|
24
24
|
wsUrl?: string;
|
|
25
|
+
/** Optional swimlane routing name, e.g. pr-1191. */
|
|
26
|
+
swimlaneName?: string;
|
|
25
27
|
/** @deprecated Superseded by WS event-driven model; kept for backward compat. */
|
|
26
28
|
pollIntervalMs: number;
|
|
27
29
|
/** @deprecated Superseded by WS event-driven model; kept for backward compat. */
|
|
@@ -61,5 +63,5 @@ export declare function sharedClaudeCredentialsFileFor(rootClaudeHome: string):
|
|
|
61
63
|
export declare function agentClaudeCredentialsFileFor(agentClaudeHome: string): string;
|
|
62
64
|
/** Per-agent workspace dir where the agent runs git commands. */
|
|
63
65
|
export declare function agentWorkspaceDirFor(rootStateDir: string, agentId: string): string;
|
|
64
|
-
export declare function resolveWsUrl(apiUrl: string, explicitWsUrl?: string): string;
|
|
66
|
+
export declare function resolveWsUrl(apiUrl: string, explicitWsUrl?: string, swimlaneName?: string): string;
|
|
65
67
|
//# sourceMappingURL=config.d.ts.map
|
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,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;CACvC,CAAC;AA2BF,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE5E;AAED,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE7E;AAyBD,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,kBAAkB,
|
|
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;CACvC,CAAC;AA2BF,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE5E;AAED,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE7E;AAyBD,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,kBAAkB,CA6ClG;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,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlG"}
|
package/dist/config.js
CHANGED
|
@@ -82,6 +82,7 @@ export function resolveClaudeDaemonConfig(env = process.env) {
|
|
|
82
82
|
rootStateDir,
|
|
83
83
|
rootClaudeHome,
|
|
84
84
|
wsUrl: env.PRLL_WS_URL?.trim() || undefined,
|
|
85
|
+
swimlaneName: env.PRLL_SWIMLANE_NAME?.trim() || undefined,
|
|
85
86
|
pollIntervalMs: parseMs(env.PRLL_DAEMON_POLL_INTERVAL_MS, 30_000),
|
|
86
87
|
heartbeatIntervalMs: parseMs(env.PRLL_DAEMON_HEARTBEAT_INTERVAL_MS, 30_000),
|
|
87
88
|
restartBackoffMs: parseMs(env.PRLL_DAEMON_RESTART_BACKOFF_MS, 5_000),
|
|
@@ -120,6 +121,11 @@ export function agentClaudeCredentialsFileFor(agentClaudeHome) {
|
|
|
120
121
|
export function agentWorkspaceDirFor(rootStateDir, agentId) {
|
|
121
122
|
return path.join(rootStateDir, "agents", assertSafeAgentId(agentId), "workspace");
|
|
122
123
|
}
|
|
123
|
-
export function resolveWsUrl(apiUrl, explicitWsUrl) {
|
|
124
|
-
|
|
124
|
+
export function resolveWsUrl(apiUrl, explicitWsUrl, swimlaneName) {
|
|
125
|
+
const base = explicitWsUrl || `${apiUrl.replace(/\/$/, "").replace(/^http/, "ws")}/ws`;
|
|
126
|
+
if (!swimlaneName)
|
|
127
|
+
return base;
|
|
128
|
+
const url = new URL(base);
|
|
129
|
+
url.searchParams.set("swimlane", swimlaneName);
|
|
130
|
+
return url.toString();
|
|
125
131
|
}
|
package/dist/index.js
CHANGED
|
@@ -76,6 +76,7 @@ async function main() {
|
|
|
76
76
|
const client = new ParallClient({
|
|
77
77
|
baseUrl: config.apiUrl,
|
|
78
78
|
token: config.apiKey,
|
|
79
|
+
swimlaneName: config.swimlaneName,
|
|
79
80
|
});
|
|
80
81
|
const abortController = new AbortController();
|
|
81
82
|
const onSignal = (sig) => {
|
|
@@ -95,7 +96,7 @@ async function main() {
|
|
|
95
96
|
process.exitCode = 1;
|
|
96
97
|
process.exit(1);
|
|
97
98
|
});
|
|
98
|
-
config.wsUrl = resolveWsUrl(config.apiUrl, config.wsUrl);
|
|
99
|
+
config.wsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
|
|
99
100
|
await runForever(config, client, log, abortController.signal);
|
|
100
101
|
}
|
|
101
102
|
const cliArgs = process.argv.slice(2);
|
package/dist/runtimes.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface AgentDirs {
|
|
|
14
14
|
workspaceDir: string;
|
|
15
15
|
claudeHome: string;
|
|
16
16
|
}
|
|
17
|
+
export declare function clearAllProviderCreds(env: NodeJS.ProcessEnv): void;
|
|
17
18
|
export declare function getRuntimeAdapter(runtimeType: string): RuntimeAdapter;
|
|
18
19
|
export declare function assertAgentKey(apiKey: string): void;
|
|
19
20
|
//# sourceMappingURL=runtimes.d.ts.map
|
package/dist/runtimes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtimes.d.ts","sourceRoot":"","sources":["../src/runtimes.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC;CAC/I;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"runtimes.d.ts","sourceRoot":"","sources":["../src/runtimes.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC;CAC/I;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAeD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,IAAI,CAOlE;AAgGD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAErE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAInD"}
|
package/dist/runtimes.js
CHANGED
package/dist/supervisor.d.ts
CHANGED
|
@@ -24,6 +24,10 @@ export declare class DaemonSupervisor {
|
|
|
24
24
|
private readonly client;
|
|
25
25
|
private readonly log;
|
|
26
26
|
private readonly children;
|
|
27
|
+
private readonly spawningAgents;
|
|
28
|
+
private readonly pendingWorkspaceSetup;
|
|
29
|
+
private readonly workspaceSetupRetryTimers;
|
|
30
|
+
private readonly cancelledSpawns;
|
|
27
31
|
private ws;
|
|
28
32
|
private running;
|
|
29
33
|
private machineOrgId;
|
|
@@ -42,12 +46,17 @@ export declare class DaemonSupervisor {
|
|
|
42
46
|
* by parsing session state files which embed the agent ID in the runtimeKey.
|
|
43
47
|
*/
|
|
44
48
|
private migrateFlatLayout;
|
|
49
|
+
private fetchAttachedAgent;
|
|
45
50
|
private handleAgentAttached;
|
|
46
51
|
private handleAgentDetached;
|
|
52
|
+
private handleWorkspaceSetupRequested;
|
|
53
|
+
private scheduleWorkspaceSetupRetry;
|
|
54
|
+
private clearWorkspaceSetupRetry;
|
|
47
55
|
private refreshMachineConfig;
|
|
48
56
|
private respawnAllChildren;
|
|
49
57
|
private restartChildNow;
|
|
50
58
|
private spawnAgent;
|
|
59
|
+
private spawnAgentOnce;
|
|
51
60
|
private startChild;
|
|
52
61
|
private terminateChild;
|
|
53
62
|
private ensureSharedCredentialLink;
|
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,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,EAA2N,MAAM,aAAa,CAAC;AACpQ,OAAO,EACL,KAAK,kBAAkB,EAMxB,MAAM,aAAa,CAAC;AAiBrB;;;;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;IAazB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAdtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAqC;IAC/E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,WAAW,CAA6B;gBAG7B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,aAAa;IAGrC,yEAAyE;IACnE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiF7C,sEAAsE;IAChE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAiCb,kBAAkB;YAqClB,aAAa;IAqD3B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;YA6CX,kBAAkB;YAqBlB,mBAAmB;YAgBnB,mBAAmB;YAmBnB,6BAA6B;IAqC3C,OAAO,CAAC,2BAA2B;IAYnC,OAAO,CAAC,wBAAwB;YAUlB,oBAAoB;YAcpB,kBAAkB;YAYlB,eAAe;YAcf,UAAU;YAoBV,cAAc;IA2D5B,OAAO,CAAC,UAAU;YA0EJ,cAAc;IAyB5B,OAAO,CAAC,0BAA0B;CA8BnC"}
|
package/dist/supervisor.js
CHANGED
|
@@ -4,11 +4,13 @@ import * as path from "node:path";
|
|
|
4
4
|
import { ParallWs } from "@parall/sdk";
|
|
5
5
|
import { agentClaudeCredentialsFileFor, agentClaudeHomeFor, agentStateDirFor, agentWorkspaceDirFor, sharedClaudeCredentialsFileFor, } from "./config.js";
|
|
6
6
|
import { assertAgentKey, getRuntimeAdapter } from "./runtimes.js";
|
|
7
|
+
import { prepareWorkspace } from "./workspace.js";
|
|
7
8
|
const RUNTIME_PACKAGES = {
|
|
8
9
|
'claude-code': '@parall/claude-agent',
|
|
9
10
|
'codex': '@parall/codex-agent',
|
|
10
11
|
'openclaw': '@parall/openclaw-agent',
|
|
11
12
|
};
|
|
13
|
+
const WORKSPACE_SETUP_RETRY_DELAY_MS = 5_000;
|
|
12
14
|
/**
|
|
13
15
|
* Sleep that wakes early on abort. Returns true if the full delay elapsed,
|
|
14
16
|
* false if aborted. Used by bootstrap retry and the outer keepalive in
|
|
@@ -46,6 +48,10 @@ export class DaemonSupervisor {
|
|
|
46
48
|
client;
|
|
47
49
|
log;
|
|
48
50
|
children = new Map();
|
|
51
|
+
spawningAgents = new Set();
|
|
52
|
+
pendingWorkspaceSetup = new Set();
|
|
53
|
+
workspaceSetupRetryTimers = new Map();
|
|
54
|
+
cancelledSpawns = new Set();
|
|
49
55
|
ws = null;
|
|
50
56
|
running = false;
|
|
51
57
|
machineOrgId = null;
|
|
@@ -107,6 +113,10 @@ export class DaemonSupervisor {
|
|
|
107
113
|
void this.respawnAllChildren();
|
|
108
114
|
}
|
|
109
115
|
});
|
|
116
|
+
this.ws.on("machine.workspace.setup.requested", (data) => {
|
|
117
|
+
this.log.info(`WS: workspace setup requested for agent ${data.agent_id}`);
|
|
118
|
+
void this.handleWorkspaceSetupRequested(data.agent_id);
|
|
119
|
+
});
|
|
110
120
|
this.ws.on("machine.stop", (data) => {
|
|
111
121
|
this.log.info(`WS: machine.stop received (reason=${data.reason ?? "none"})`);
|
|
112
122
|
void this.stop();
|
|
@@ -132,6 +142,10 @@ export class DaemonSupervisor {
|
|
|
132
142
|
this.ws = null;
|
|
133
143
|
}
|
|
134
144
|
const exits = [];
|
|
145
|
+
for (const timer of this.workspaceSetupRetryTimers.values()) {
|
|
146
|
+
clearTimeout(timer);
|
|
147
|
+
}
|
|
148
|
+
this.workspaceSetupRetryTimers.clear();
|
|
135
149
|
for (const state of this.children.values()) {
|
|
136
150
|
state.shuttingDown = true;
|
|
137
151
|
if (state.restartTimer) {
|
|
@@ -277,24 +291,31 @@ export class DaemonSupervisor {
|
|
|
277
291
|
}
|
|
278
292
|
}
|
|
279
293
|
// ---- WS event handlers (incremental) ----
|
|
280
|
-
async
|
|
281
|
-
if (this.children.has(agentId))
|
|
282
|
-
return;
|
|
294
|
+
async fetchAttachedAgent(agentId) {
|
|
283
295
|
let attached;
|
|
284
296
|
try {
|
|
285
297
|
attached = await this.client.listAttachedAgents();
|
|
286
298
|
}
|
|
287
299
|
catch (err) {
|
|
288
|
-
this.log.warn(`
|
|
289
|
-
return;
|
|
300
|
+
this.log.warn(`fetchAttachedAgent: listAttachedAgents failed: ${String(err)}`);
|
|
301
|
+
return { kind: "retryable" };
|
|
290
302
|
}
|
|
291
303
|
const entry = attached.find((a) => (a.user?.id ?? a.profile.user_id) === agentId);
|
|
292
304
|
if (!entry) {
|
|
293
|
-
this.log.warn(`
|
|
294
|
-
return;
|
|
305
|
+
this.log.warn(`fetchAttachedAgent: agent ${agentId} not found in attached list`);
|
|
306
|
+
return { kind: "skip" };
|
|
295
307
|
}
|
|
296
308
|
if (entry.user && entry.user.status !== "active") {
|
|
297
309
|
this.log.info(`agent ${agentId} not active (status=${entry.user.status}) — skipping`);
|
|
310
|
+
return { kind: "skip" };
|
|
311
|
+
}
|
|
312
|
+
return { kind: "found", entry };
|
|
313
|
+
}
|
|
314
|
+
async handleAgentAttached(agentId) {
|
|
315
|
+
if (this.children.has(agentId) || this.spawningAgents.has(agentId))
|
|
316
|
+
return;
|
|
317
|
+
const result = await this.fetchAttachedAgent(agentId);
|
|
318
|
+
if (result.kind !== "found") {
|
|
298
319
|
return;
|
|
299
320
|
}
|
|
300
321
|
const orgId = this.machineOrgId;
|
|
@@ -302,9 +323,14 @@ export class DaemonSupervisor {
|
|
|
302
323
|
this.log.warn(`agent ${agentId}: no org_id available yet; skipping`);
|
|
303
324
|
return;
|
|
304
325
|
}
|
|
305
|
-
await this.spawnAgent(agentId, orgId, entry);
|
|
326
|
+
await this.spawnAgent(agentId, orgId, result.entry);
|
|
306
327
|
}
|
|
307
328
|
async handleAgentDetached(agentId) {
|
|
329
|
+
this.pendingWorkspaceSetup.delete(agentId);
|
|
330
|
+
this.clearWorkspaceSetupRetry(agentId);
|
|
331
|
+
if (this.spawningAgents.has(agentId)) {
|
|
332
|
+
this.cancelledSpawns.add(agentId);
|
|
333
|
+
}
|
|
308
334
|
const state = this.children.get(agentId);
|
|
309
335
|
if (!state)
|
|
310
336
|
return;
|
|
@@ -317,6 +343,61 @@ export class DaemonSupervisor {
|
|
|
317
343
|
await this.terminateChild(state);
|
|
318
344
|
this.children.delete(agentId);
|
|
319
345
|
}
|
|
346
|
+
async handleWorkspaceSetupRequested(agentId) {
|
|
347
|
+
if (this.spawningAgents.has(agentId)) {
|
|
348
|
+
this.log.info(`agent ${agentId}: workspace setup already in progress; queueing one restart`);
|
|
349
|
+
this.pendingWorkspaceSetup.add(agentId);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const result = await this.fetchAttachedAgent(agentId);
|
|
353
|
+
if (result.kind === "retryable") {
|
|
354
|
+
this.pendingWorkspaceSetup.add(agentId);
|
|
355
|
+
this.scheduleWorkspaceSetupRetry(agentId);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (result.kind === "skip") {
|
|
359
|
+
this.pendingWorkspaceSetup.delete(agentId);
|
|
360
|
+
this.clearWorkspaceSetupRetry(agentId);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
this.clearWorkspaceSetupRetry(agentId);
|
|
364
|
+
const state = this.children.get(agentId);
|
|
365
|
+
if (state) {
|
|
366
|
+
this.log.info(`agent ${agentId}: restarting for workspace setup`);
|
|
367
|
+
state.shuttingDown = true;
|
|
368
|
+
if (state.restartTimer) {
|
|
369
|
+
clearTimeout(state.restartTimer);
|
|
370
|
+
state.restartTimer = null;
|
|
371
|
+
}
|
|
372
|
+
await this.terminateChild(state);
|
|
373
|
+
this.children.delete(agentId);
|
|
374
|
+
}
|
|
375
|
+
const orgId = this.machineOrgId;
|
|
376
|
+
if (!orgId) {
|
|
377
|
+
this.log.warn(`agent ${agentId}: no org_id available yet; skipping`);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
await this.spawnAgent(agentId, orgId, result.entry);
|
|
381
|
+
}
|
|
382
|
+
scheduleWorkspaceSetupRetry(agentId) {
|
|
383
|
+
if (!this.running || this.workspaceSetupRetryTimers.has(agentId))
|
|
384
|
+
return;
|
|
385
|
+
const timer = setTimeout(() => {
|
|
386
|
+
this.workspaceSetupRetryTimers.delete(agentId);
|
|
387
|
+
if (this.running && this.pendingWorkspaceSetup.has(agentId)) {
|
|
388
|
+
void this.handleWorkspaceSetupRequested(agentId);
|
|
389
|
+
}
|
|
390
|
+
}, WORKSPACE_SETUP_RETRY_DELAY_MS);
|
|
391
|
+
timer.unref?.();
|
|
392
|
+
this.workspaceSetupRetryTimers.set(agentId, timer);
|
|
393
|
+
}
|
|
394
|
+
clearWorkspaceSetupRetry(agentId) {
|
|
395
|
+
const timer = this.workspaceSetupRetryTimers.get(agentId);
|
|
396
|
+
if (timer) {
|
|
397
|
+
clearTimeout(timer);
|
|
398
|
+
this.workspaceSetupRetryTimers.delete(agentId);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
320
401
|
// ---- Spawn / restart ----
|
|
321
402
|
async refreshMachineConfig() {
|
|
322
403
|
try {
|
|
@@ -358,6 +439,26 @@ export class DaemonSupervisor {
|
|
|
358
439
|
}
|
|
359
440
|
}
|
|
360
441
|
async spawnAgent(agentId, orgId, attached) {
|
|
442
|
+
if (this.children.has(agentId) || this.spawningAgents.has(agentId)) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
this.spawningAgents.add(agentId);
|
|
446
|
+
let shouldReplaySetupRequest = false;
|
|
447
|
+
try {
|
|
448
|
+
await this.spawnAgentOnce(agentId, orgId, attached);
|
|
449
|
+
}
|
|
450
|
+
finally {
|
|
451
|
+
this.spawningAgents.delete(agentId);
|
|
452
|
+
this.cancelledSpawns.delete(agentId);
|
|
453
|
+
shouldReplaySetupRequest = this.pendingWorkspaceSetup.delete(agentId);
|
|
454
|
+
}
|
|
455
|
+
if (shouldReplaySetupRequest && this.running) {
|
|
456
|
+
queueMicrotask(() => {
|
|
457
|
+
void this.handleWorkspaceSetupRequested(agentId);
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async spawnAgentOnce(agentId, orgId, attached) {
|
|
361
462
|
let credential;
|
|
362
463
|
try {
|
|
363
464
|
credential = await this.client.mintLaunchCredential(agentId);
|
|
@@ -367,19 +468,13 @@ export class DaemonSupervisor {
|
|
|
367
468
|
return;
|
|
368
469
|
}
|
|
369
470
|
const stateDir = agentStateDirFor(this.config.rootStateDir, agentId);
|
|
370
|
-
const
|
|
371
|
-
|| agentWorkspaceDirFor(this.config.rootStateDir, agentId);
|
|
471
|
+
const defaultWorkspaceDir = agentWorkspaceDirFor(this.config.rootStateDir, agentId);
|
|
372
472
|
const isK8s = !!process.env.KUBERNETES_SERVICE_HOST;
|
|
373
473
|
const claudeHome = isK8s
|
|
374
474
|
? agentClaudeHomeFor(this.config.rootClaudeHome, agentId)
|
|
375
475
|
: this.config.rootClaudeHome;
|
|
376
476
|
try {
|
|
377
477
|
fs.mkdirSync(stateDir, { recursive: true });
|
|
378
|
-
// Only create workspace dir when using the default isolated path —
|
|
379
|
-
// user-specified workspace_path should already exist.
|
|
380
|
-
if (!attached.daemon_config?.workspace_path) {
|
|
381
|
-
fs.mkdirSync(workspaceDir, { recursive: true });
|
|
382
|
-
}
|
|
383
478
|
if (isK8s) {
|
|
384
479
|
fs.mkdirSync(claudeHome, { recursive: true });
|
|
385
480
|
this.ensureSharedCredentialLink(claudeHome, agentId);
|
|
@@ -389,6 +484,24 @@ export class DaemonSupervisor {
|
|
|
389
484
|
this.log.warn(`mkdir agent dirs (${agentId}) failed: ${String(err)}`);
|
|
390
485
|
}
|
|
391
486
|
const runtimeType = attached.profile.runtime_type ?? "claude-code";
|
|
487
|
+
let workspaceDir;
|
|
488
|
+
try {
|
|
489
|
+
workspaceDir = await prepareWorkspace({
|
|
490
|
+
client: this.client,
|
|
491
|
+
attached,
|
|
492
|
+
agentId,
|
|
493
|
+
defaultWorkspaceDir,
|
|
494
|
+
log: this.log,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
catch (err) {
|
|
498
|
+
this.log.error(`agent ${agentId}: workspace setup failed: ${String(err)}`);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
if (this.cancelledSpawns.delete(agentId)) {
|
|
502
|
+
this.log.info(`agent ${agentId}: spawn cancelled after detach during workspace setup`);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
392
505
|
const state = {
|
|
393
506
|
agentId,
|
|
394
507
|
orgId,
|
|
@@ -460,11 +573,12 @@ export class DaemonSupervisor {
|
|
|
460
573
|
settleChild("error", null, null, err);
|
|
461
574
|
});
|
|
462
575
|
child.once("close", (code, signal) => settleChild("close", code, signal));
|
|
463
|
-
setTimeout(() => {
|
|
576
|
+
const stableTimer = setTimeout(() => {
|
|
464
577
|
if (state.child === child) {
|
|
465
578
|
state.restartAttempts = 0;
|
|
466
579
|
}
|
|
467
580
|
}, Math.max(this.config.restartBackoffMs, 30_000));
|
|
581
|
+
stableTimer.unref?.();
|
|
468
582
|
}
|
|
469
583
|
async terminateChild(state) {
|
|
470
584
|
const child = state.child;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { GatewayLogger } from "@parall/agent-core";
|
|
2
|
+
import type { AttachedAgent, ParallClient } from "@parall/sdk";
|
|
3
|
+
export interface PrepareWorkspaceOpts {
|
|
4
|
+
client: ParallClient;
|
|
5
|
+
attached: AttachedAgent;
|
|
6
|
+
agentId: string;
|
|
7
|
+
defaultWorkspaceDir: string;
|
|
8
|
+
log: GatewayLogger;
|
|
9
|
+
}
|
|
10
|
+
export declare function prepareWorkspace(opts: PrepareWorkspaceOpts): Promise<string>;
|
|
11
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAA4C,YAAY,EAAE,MAAM,aAAa,CAAC;AAiBzG,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,aAAa,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6ClF"}
|