@ctrl-spc/cli 1.1.9 → 1.1.10
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/dist/spawner.js +26 -12
- package/package.json +1 -1
package/dist/spawner.js
CHANGED
|
@@ -6,26 +6,31 @@ function isRemote(agentType) {
|
|
|
6
6
|
return agentType === 'claude-code' || agentType === 'codex';
|
|
7
7
|
}
|
|
8
8
|
export function makeSpawner(deps) {
|
|
9
|
-
const timeoutMs = deps.readinessTimeoutMs ??
|
|
9
|
+
const timeoutMs = deps.readinessTimeoutMs ?? 45_000;
|
|
10
10
|
const backoffMs = deps.retryBackoffMs ?? 30_000;
|
|
11
11
|
const lastAttempt = new Map(); // requestId -> timestamp; in-memory back-off
|
|
12
12
|
async function awaitReadiness(handle) {
|
|
13
13
|
return new Promise((resolve) => {
|
|
14
14
|
let buf = '';
|
|
15
|
+
let errBuf = '';
|
|
15
16
|
let done = false;
|
|
16
|
-
const finish = (
|
|
17
|
-
done
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const finish = (ready) => {
|
|
18
|
+
if (!done) {
|
|
19
|
+
done = true;
|
|
20
|
+
clearTimeout(timer);
|
|
21
|
+
resolve({ ready, stdout: buf, stderr: errBuf });
|
|
22
|
+
}
|
|
23
|
+
};
|
|
21
24
|
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
22
25
|
handle.stdout.on('data', (chunk) => {
|
|
23
26
|
buf += chunk.toString();
|
|
24
27
|
if (buf.includes(READINESS_REPLY))
|
|
25
28
|
finish(true);
|
|
26
29
|
});
|
|
30
|
+
handle.stderr?.on('data', (chunk) => { errBuf += chunk.toString(); });
|
|
27
31
|
handle.stdout.on('end', () => finish(buf.includes(READINESS_REPLY)));
|
|
28
32
|
handle.stdout.on('error', () => finish(false));
|
|
33
|
+
handle.stderr?.on('error', () => { });
|
|
29
34
|
});
|
|
30
35
|
}
|
|
31
36
|
async function resolveLocalPath(projectId) {
|
|
@@ -66,10 +71,10 @@ export function makeSpawner(deps) {
|
|
|
66
71
|
}
|
|
67
72
|
const { cmd, args } = assembleCommand(row);
|
|
68
73
|
const handle = deps.spawnAgent(cmd, args, cwd);
|
|
69
|
-
const
|
|
70
|
-
if (!ready) {
|
|
74
|
+
const result = await awaitReadiness(handle);
|
|
75
|
+
if (!result.ready) {
|
|
71
76
|
handle.kill();
|
|
72
|
-
deps.log(`Readiness gate failed for ${row.id}; will retry`);
|
|
77
|
+
deps.log(`Readiness gate failed for ${row.id}; will retry. stdout=${quoteSnippet(result.stdout)} stderr=${quoteSnippet(result.stderr)}`);
|
|
73
78
|
return;
|
|
74
79
|
}
|
|
75
80
|
await fulfil(row, handle.pid);
|
|
@@ -97,9 +102,18 @@ export function makeSpawner(deps) {
|
|
|
97
102
|
}
|
|
98
103
|
// Real spawn handle backed by node:child_process, used by the daemon (not in unit tests).
|
|
99
104
|
export function nodeSpawnAgent(cmd, args, cwd) {
|
|
100
|
-
const child = spawn(cmd, args, { cwd, stdio: ['ignore', 'pipe', '
|
|
105
|
+
const child = spawn(cmd, args, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
101
106
|
const stdout = new PassThrough();
|
|
107
|
+
const stderr = new PassThrough();
|
|
102
108
|
child.stdout?.pipe(stdout);
|
|
103
|
-
child.
|
|
104
|
-
|
|
109
|
+
child.stderr?.pipe(stderr);
|
|
110
|
+
child.on('error', (err) => {
|
|
111
|
+
stdout.destroy(err);
|
|
112
|
+
stderr.end();
|
|
113
|
+
});
|
|
114
|
+
return { pid: child.pid ?? -1, stdout, stderr, kill: () => child.kill() };
|
|
115
|
+
}
|
|
116
|
+
function quoteSnippet(value) {
|
|
117
|
+
const trimmed = value.replace(/\s+/g, ' ').trim().slice(0, 500);
|
|
118
|
+
return JSON.stringify(trimmed);
|
|
105
119
|
}
|