agent-mcp-hub 0.5.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/LICENSE +373 -0
- package/README.md +246 -0
- package/dist/adapters/claude.d.ts +2 -0
- package/dist/adapters/claude.js +15 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex.d.ts +2 -0
- package/dist/adapters/codex.js +16 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/cursor.d.ts +2 -0
- package/dist/adapters/cursor.js +33 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/opencode.d.ts +2 -0
- package/dist/adapters/opencode.js +23 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/ansi.d.ts +5 -0
- package/dist/ansi.js +15 -0
- package/dist/ansi.js.map +1 -0
- package/dist/confirm.d.ts +36 -0
- package/dist/confirm.js +65 -0
- package/dist/confirm.js.map +1 -0
- package/dist/exec.d.ts +146 -0
- package/dist/exec.js +494 -0
- package/dist/exec.js.map +1 -0
- package/dist/failure.d.ts +27 -0
- package/dist/failure.js +210 -0
- package/dist/failure.js.map +1 -0
- package/dist/git.d.ts +59 -0
- package/dist/git.js +127 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +70 -0
- package/dist/registry.js +159 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +509 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +75 -0
package/dist/exec.js
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { stripAnsi } from "./ansi.js";
|
|
4
|
+
/** Positive finite integer only; NaN/0/negative/non-integer all fall back to `fallback`. */
|
|
5
|
+
function parsePositiveInt(raw, fallback) {
|
|
6
|
+
const n = Number(raw);
|
|
7
|
+
return Number.isInteger(n) && n > 0 ? n : fallback;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* TOTAL runtime cap (never reset). Parsed once at module load; override via
|
|
11
|
+
* MCP_AGENT_TIMEOUT_MS. Raised to 30 min so long PRODUCTIVE runs survive — the
|
|
12
|
+
* idle timeout, not this, is the fast hung-agent detector.
|
|
13
|
+
*/
|
|
14
|
+
export const DEFAULT_TIMEOUT_MS = parsePositiveInt(process.env.MCP_AGENT_TIMEOUT_MS, 1_800_000);
|
|
15
|
+
/**
|
|
16
|
+
* IDLE (inactivity) cap: reset on every accepted output chunk. Parsed once at
|
|
17
|
+
* module load; override via MCP_AGENT_IDLE_TIMEOUT_MS. Generous default so a
|
|
18
|
+
* slow-but-streaming CLI is not killed mid-work.
|
|
19
|
+
*/
|
|
20
|
+
export const DEFAULT_IDLE_TIMEOUT_MS = parsePositiveInt(process.env.MCP_AGENT_IDLE_TIMEOUT_MS, 300_000);
|
|
21
|
+
export const DEFAULT_MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
22
|
+
export const DEFAULT_STALL_ATTEMPT_LIMIT = 2;
|
|
23
|
+
export const DEFAULT_STALL_STRIKE_LIMIT = 4;
|
|
24
|
+
/**
|
|
25
|
+
* Hard ceiling on any single timer (total or idle). Two jobs:
|
|
26
|
+
* 1. Correctness — `setTimeout` truncates a delay > 2^31-1 ms (~24.8 days) to
|
|
27
|
+
* 1ms and fires it IMMEDIATELY, so an unclamped huge `timeoutMs` would kill
|
|
28
|
+
* the child the instant it starts. 24h is safely under that boundary.
|
|
29
|
+
* 2. Load-shedding — a single run can hold a concurrency slot for at most 24h,
|
|
30
|
+
* so a caller cannot pin all slots for weeks with an enormous `timeoutMs`.
|
|
31
|
+
*/
|
|
32
|
+
export const MAX_TIMEOUT_MS = 24 * 60 * 60 * 1000;
|
|
33
|
+
/** Clamp a caller-supplied timer to [1, MAX_TIMEOUT_MS]; floors non-integers. */
|
|
34
|
+
export function clampTimer(ms) {
|
|
35
|
+
if (!Number.isFinite(ms))
|
|
36
|
+
return MAX_TIMEOUT_MS;
|
|
37
|
+
return Math.min(Math.max(1, Math.floor(ms)), MAX_TIMEOUT_MS);
|
|
38
|
+
}
|
|
39
|
+
/** Positive finite integer only; NaN/0/negative/non-integer all fall back to 4. */
|
|
40
|
+
function parseConcurrency(raw) {
|
|
41
|
+
return parsePositiveInt(raw, 4);
|
|
42
|
+
}
|
|
43
|
+
/** Non-negative finite integer only; NaN/negative/non-integer all fall back to 100. */
|
|
44
|
+
function parseMaxQueue(raw) {
|
|
45
|
+
const n = Number(raw);
|
|
46
|
+
return Number.isInteger(n) && n >= 0 ? n : 100;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Cap on children spawned concurrently across the process. Parsed once at module
|
|
50
|
+
* load; override via MCP_MAX_CONCURRENT_AGENTS.
|
|
51
|
+
*/
|
|
52
|
+
export const MAX_CONCURRENT_AGENTS = parseConcurrency(process.env.MCP_MAX_CONCURRENT_AGENTS);
|
|
53
|
+
/**
|
|
54
|
+
* Thrown when every permit is busy AND the wait queue is already full. Overload
|
|
55
|
+
* sheds load here instead of growing latency unbounded — the caller retries
|
|
56
|
+
* later. Surfaces through runCommand → runAdapter → the tool handler's catch as
|
|
57
|
+
* an `isError` result (the 503-equivalent in the stateless per-request model).
|
|
58
|
+
*/
|
|
59
|
+
export class ServerBusyError extends Error {
|
|
60
|
+
code = "SERVER_BUSY";
|
|
61
|
+
constructor() {
|
|
62
|
+
super("server busy: agent queue full, retry later");
|
|
63
|
+
this.name = "ServerBusyError";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Thrown when the child process fails to spawn (binary missing / not on PATH). */
|
|
67
|
+
export class SpawnError extends Error {
|
|
68
|
+
cause;
|
|
69
|
+
code = "spawn";
|
|
70
|
+
constructor(message, cause) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.cause = cause;
|
|
73
|
+
this.name = "SpawnError";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Thrown when `cwd` does not exist (or is not a directory). Checked BEFORE spawn
|
|
78
|
+
* because both a missing binary and a missing cwd surface as bare ENOENT from
|
|
79
|
+
* spawn, and collapsing them is actively misleading: a caller passing a repo path
|
|
80
|
+
* the server cannot see was told "codex was not found on PATH" while codex was
|
|
81
|
+
* installed and healthy. Distinguishing them up front makes the real fault legible.
|
|
82
|
+
*/
|
|
83
|
+
export class InvalidCwdError extends Error {
|
|
84
|
+
cwd;
|
|
85
|
+
code = "invalid_cwd";
|
|
86
|
+
constructor(cwd) {
|
|
87
|
+
super(`cwd does not exist or is not a directory: ${cwd}`);
|
|
88
|
+
this.cwd = cwd;
|
|
89
|
+
this.name = "InvalidCwdError";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Thrown when the child is killed for exceeding a timeout. Carries the bound and
|
|
94
|
+
* WHICH cap fired: `"idle"` (no output for idleTimeoutMs — likely hung/unreachable)
|
|
95
|
+
* or `"total"` (exceeded the total runtime cap). `kind` is additive and defaults
|
|
96
|
+
* to `"total"`, so `new TimeoutError(msg, ms)` call sites are unchanged.
|
|
97
|
+
*/
|
|
98
|
+
export class TimeoutError extends Error {
|
|
99
|
+
timeoutMs;
|
|
100
|
+
kind;
|
|
101
|
+
code = "timeout";
|
|
102
|
+
constructor(message, timeoutMs, kind = "total") {
|
|
103
|
+
super(message);
|
|
104
|
+
this.timeoutMs = timeoutMs;
|
|
105
|
+
this.kind = kind;
|
|
106
|
+
this.name = "TimeoutError";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Thrown when the child is killed for exceeding `maxOutputBytes`. Carries the cap. */
|
|
110
|
+
export class OutputLimitError extends Error {
|
|
111
|
+
maxOutputBytes;
|
|
112
|
+
code = "output_limit";
|
|
113
|
+
constructor(message, maxOutputBytes) {
|
|
114
|
+
super(message);
|
|
115
|
+
this.maxOutputBytes = maxOutputBytes;
|
|
116
|
+
this.name = "OutputLimitError";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Thrown when the child is detected as stalled: it prints diagnostic reconnect
|
|
121
|
+
* phrases on stderr (the adapter's `stallSignatures`) but never produces stdout,
|
|
122
|
+
* and the attempt/strike counters corroborate that it will not recover. The
|
|
123
|
+
* process group is killed; the rejection surfaces on `close` so the semaphore
|
|
124
|
+
* permit is released only after the tree is actually gone.
|
|
125
|
+
*/
|
|
126
|
+
export class AgentStalledError extends Error {
|
|
127
|
+
signature;
|
|
128
|
+
strikes;
|
|
129
|
+
code = "stream_stalled";
|
|
130
|
+
constructor(message, signature, strikes) {
|
|
131
|
+
super(message);
|
|
132
|
+
this.signature = signature;
|
|
133
|
+
this.strikes = strikes;
|
|
134
|
+
this.name = "AgentStalledError";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Kill an entire process group. Children are spawned `detached`, making each the
|
|
139
|
+
* leader of its own group, so `process.kill(-pid, …)` reaps the child and every
|
|
140
|
+
* grandchild it forked. ESRCH (group already gone) is benign. On EPERM or any
|
|
141
|
+
* other group-kill failure we fall back to killing just the child PID. The whole
|
|
142
|
+
* kill path is best-effort: it never throws, so it can never mask the
|
|
143
|
+
* timeout-or-output-limit error being surfaced to the caller.
|
|
144
|
+
*/
|
|
145
|
+
function killGroup(pid, signal, fallback) {
|
|
146
|
+
if (pid == null)
|
|
147
|
+
return;
|
|
148
|
+
try {
|
|
149
|
+
process.kill(-pid, signal);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
if (err.code === "ESRCH")
|
|
153
|
+
return;
|
|
154
|
+
try {
|
|
155
|
+
fallback();
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
/* swallow: a failed fallback must not mask the original error */
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Live children spawned by this module. Children are `detached` (their own group
|
|
164
|
+
* leaders), so if the server process dies without reaping them, their whole
|
|
165
|
+
* process tree is orphaned and keeps running. We track every live child and, on
|
|
166
|
+
* server shutdown, SIGKILL each group.
|
|
167
|
+
*
|
|
168
|
+
* Reaping uses the same POSIX `process.kill(-pid)` group kill as the rest of the
|
|
169
|
+
* module. On a platform without process groups (Windows) that falls back to
|
|
170
|
+
* killing only the direct child, so a grandchild there could still leak — a
|
|
171
|
+
* pre-existing limitation of the codebase's POSIX-oriented kill strategy, not of
|
|
172
|
+
* this reaper. The runtime target is POSIX (unix agent CLIs).
|
|
173
|
+
*/
|
|
174
|
+
const liveChildren = new Set();
|
|
175
|
+
/** SIGKILL a child's whole process group, falling back to the bare child. */
|
|
176
|
+
function killChildGroup(child) {
|
|
177
|
+
killGroup(child.pid, "SIGKILL", () => child.kill("SIGKILL"));
|
|
178
|
+
}
|
|
179
|
+
/** Best-effort: kill every still-live child's process group. Never throws. */
|
|
180
|
+
export function reapAllChildren() {
|
|
181
|
+
for (const child of liveChildren) {
|
|
182
|
+
killChildGroup(child);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Install the shutdown reaper exactly once, lazily on first spawn (so importing
|
|
186
|
+
// this module has no global side effect). `exit` runs synchronously on any exit;
|
|
187
|
+
// the SIGINT/SIGTERM/SIGHUP handlers reap first, remove themselves, then re-raise
|
|
188
|
+
// the signal so the process still terminates with the default disposition.
|
|
189
|
+
let shutdownHandlersInstalled = false;
|
|
190
|
+
function installShutdownReaper() {
|
|
191
|
+
if (shutdownHandlersInstalled)
|
|
192
|
+
return;
|
|
193
|
+
shutdownHandlersInstalled = true;
|
|
194
|
+
process.once("exit", reapAllChildren);
|
|
195
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
196
|
+
/* v8 ignore start -- the handler re-raises the signal, which would terminate
|
|
197
|
+
the test runner; the reap it performs IS covered directly via
|
|
198
|
+
reapAllChildren() in exec.test.ts. */
|
|
199
|
+
const handler = () => {
|
|
200
|
+
reapAllChildren();
|
|
201
|
+
process.removeListener(sig, handler);
|
|
202
|
+
process.kill(process.pid, sig);
|
|
203
|
+
};
|
|
204
|
+
/* v8 ignore stop */
|
|
205
|
+
process.on(sig, handler);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* FIFO async semaphore. `acquire` resolves to a release token that hands the
|
|
210
|
+
* permit straight to the next waiter (never bumping the count while anyone is
|
|
211
|
+
* queued) and is idempotent so a double release cannot leak an extra permit.
|
|
212
|
+
*
|
|
213
|
+
* The wait queue is bounded: when no permit is free and the queue is already at
|
|
214
|
+
* `maxQueue()`, `acquire` rejects with `ServerBusyError` BEFORE enqueuing rather
|
|
215
|
+
* than letting the backlog grow without limit. `maxQueue` is a thunk so the
|
|
216
|
+
* bound is read per-acquire (keeps the class free of env reads and lets it be
|
|
217
|
+
* overridden without reloading the module).
|
|
218
|
+
*/
|
|
219
|
+
class Semaphore {
|
|
220
|
+
permits;
|
|
221
|
+
waiters = [];
|
|
222
|
+
maxQueue;
|
|
223
|
+
constructor(permits, maxQueue) {
|
|
224
|
+
this.permits = permits;
|
|
225
|
+
this.maxQueue = maxQueue;
|
|
226
|
+
}
|
|
227
|
+
async acquire() {
|
|
228
|
+
if (this.permits > 0) {
|
|
229
|
+
this.permits -= 1;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
if (this.waiters.length >= this.maxQueue()) {
|
|
233
|
+
throw new ServerBusyError();
|
|
234
|
+
}
|
|
235
|
+
await new Promise((resolve) => this.waiters.push(resolve));
|
|
236
|
+
}
|
|
237
|
+
let released = false;
|
|
238
|
+
return () => {
|
|
239
|
+
if (released)
|
|
240
|
+
return;
|
|
241
|
+
released = true;
|
|
242
|
+
const next = this.waiters.shift();
|
|
243
|
+
if (next)
|
|
244
|
+
next();
|
|
245
|
+
else
|
|
246
|
+
this.permits += 1;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const sem = new Semaphore(MAX_CONCURRENT_AGENTS, () => parseMaxQueue(process.env.MCP_MAX_QUEUE));
|
|
251
|
+
/** Run `fn` holding one semaphore slot; the slot is released on every exit path. */
|
|
252
|
+
async function withSlot(fn) {
|
|
253
|
+
const release = await sem.acquire();
|
|
254
|
+
try {
|
|
255
|
+
return await fn();
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
release();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function isDirectory(path) {
|
|
262
|
+
try {
|
|
263
|
+
return statSync(path).isDirectory();
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const runCommandInner = (binary, args, opts = {}) => {
|
|
270
|
+
const timeoutMs = clampTimer(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
271
|
+
const idleTimeoutMs = clampTimer(opts.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS);
|
|
272
|
+
const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
273
|
+
const stallSignatures = opts.stallSignatures ?? null;
|
|
274
|
+
const stallAttemptLimit = opts.stallAttemptLimit ?? DEFAULT_STALL_ATTEMPT_LIMIT;
|
|
275
|
+
const stallStrikeLimit = opts.stallStrikeLimit ?? DEFAULT_STALL_STRIKE_LIMIT;
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
// Pre-flight the cwd so a missing directory is never mistaken for a missing
|
|
278
|
+
// binary (spawn reports ENOENT for both). See InvalidCwdError.
|
|
279
|
+
if (opts.cwd !== undefined && !isDirectory(opts.cwd)) {
|
|
280
|
+
reject(new InvalidCwdError(opts.cwd));
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// Build an explicit env only when there is something to strip OR overlay;
|
|
284
|
+
// otherwise omit the option so the child inherits process.env verbatim
|
|
285
|
+
// (unchanged behavior). Strip first, then overlay — an `env` override always
|
|
286
|
+
// wins and is never removed by a later strip key.
|
|
287
|
+
const stripEnvKeys = opts.stripEnvKeys;
|
|
288
|
+
const envOverlay = opts.env;
|
|
289
|
+
let childEnv;
|
|
290
|
+
if ((stripEnvKeys && stripEnvKeys.length > 0) || envOverlay) {
|
|
291
|
+
childEnv = { ...process.env };
|
|
292
|
+
if (stripEnvKeys)
|
|
293
|
+
for (const key of stripEnvKeys)
|
|
294
|
+
delete childEnv[key];
|
|
295
|
+
if (envOverlay)
|
|
296
|
+
Object.assign(childEnv, envOverlay);
|
|
297
|
+
}
|
|
298
|
+
const child = spawn(binary, args, {
|
|
299
|
+
cwd: opts.cwd,
|
|
300
|
+
detached: true,
|
|
301
|
+
stdio: [opts.input === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
302
|
+
...(childEnv ? { env: childEnv } : {}),
|
|
303
|
+
});
|
|
304
|
+
liveChildren.add(child);
|
|
305
|
+
installShutdownReaper();
|
|
306
|
+
const killTree = () => killChildGroup(child);
|
|
307
|
+
const stdoutChunks = [];
|
|
308
|
+
const stderrChunks = [];
|
|
309
|
+
let outputBytes = 0;
|
|
310
|
+
// Single terminal cause: first to fire wins, recorded exactly once by
|
|
311
|
+
// markTerminal so a losing timer can never overwrite it or double-kill.
|
|
312
|
+
let terminalCause;
|
|
313
|
+
let settled = false;
|
|
314
|
+
// Stall detector state (only armed when stallSignatures is non-empty).
|
|
315
|
+
let stallStrikes = 0;
|
|
316
|
+
let stallMaxAttempt = 0;
|
|
317
|
+
let stallNoAttemptStrikes = 0;
|
|
318
|
+
let stallSignatureLast = "";
|
|
319
|
+
// Rolling buffer: a data chunk may split a line mid-byte, so we keep the
|
|
320
|
+
// trailing partial across chunks and only test complete lines.
|
|
321
|
+
let stderrRemainder = "";
|
|
322
|
+
// Only stdout bytes count as "productive" — stderr reconnect phrases are
|
|
323
|
+
// diagnostic noise, not progress toward a model answer.
|
|
324
|
+
let productiveStdoutBytes = 0;
|
|
325
|
+
// The TOTAL timer bounds runtime and is NEVER reset. The IDLE timer is reset
|
|
326
|
+
// on every accepted chunk. Both start after the semaphore slot is acquired,
|
|
327
|
+
// so the caps bound the child's runtime, not time spent queued behind the cap.
|
|
328
|
+
let idleTimer;
|
|
329
|
+
const totalTimer = setTimeout(() => markTerminal({ kind: "total", windowMs: timeoutMs }), timeoutMs);
|
|
330
|
+
const clearTimers = () => {
|
|
331
|
+
clearTimeout(totalTimer);
|
|
332
|
+
clearTimeout(idleTimer);
|
|
333
|
+
};
|
|
334
|
+
// Single guarded terminal path: first cause to fire wins, records the cause,
|
|
335
|
+
// stops BOTH timers, then kills the tree. Idempotent — a second call (losing
|
|
336
|
+
// timer, or a race with settle) is a no-op, so no double-kill / overwrite.
|
|
337
|
+
function markTerminal(cause) {
|
|
338
|
+
/* v8 ignore next -- unreachable: clearTimers() cancels the losing timer before it can fire, so markTerminal runs at most once (settled/terminalCause never true on entry) */
|
|
339
|
+
if (settled || terminalCause)
|
|
340
|
+
return;
|
|
341
|
+
terminalCause = cause;
|
|
342
|
+
clearTimers();
|
|
343
|
+
killTree();
|
|
344
|
+
}
|
|
345
|
+
idleTimer = setTimeout(() => markTerminal({ kind: "idle", windowMs: idleTimeoutMs }), idleTimeoutMs);
|
|
346
|
+
if (opts.input !== undefined) {
|
|
347
|
+
// Swallow EPIPE if the child exits before reading its stdin.
|
|
348
|
+
child.stdin?.on("error", () => { });
|
|
349
|
+
child.stdin?.write(opts.input);
|
|
350
|
+
child.stdin?.end();
|
|
351
|
+
}
|
|
352
|
+
// Test a complete stderr line against the stall signatures. Returns the
|
|
353
|
+
// matched signature text (already stripped/trimmed) or undefined.
|
|
354
|
+
const testStallLine = (line) => {
|
|
355
|
+
if (!stallSignatures || stallSignatures.length === 0)
|
|
356
|
+
return undefined;
|
|
357
|
+
const stripped = stripAnsi(line).trim();
|
|
358
|
+
if (stripped.length === 0)
|
|
359
|
+
return undefined;
|
|
360
|
+
for (const re of stallSignatures) {
|
|
361
|
+
if (re.test(stripped))
|
|
362
|
+
return stripped;
|
|
363
|
+
}
|
|
364
|
+
return undefined;
|
|
365
|
+
};
|
|
366
|
+
// Parse an attempt number out of a stall line, e.g. "… (attempt 1)…" → 1.
|
|
367
|
+
// Returns undefined when no attempt number is present.
|
|
368
|
+
const parseAttempt = (line) => {
|
|
369
|
+
const m = /attempt\D{0,10}(\d+)/i.exec(line);
|
|
370
|
+
return m ? Number(m[1]) : undefined;
|
|
371
|
+
};
|
|
372
|
+
const track = (chunk, sink, isStdout) => {
|
|
373
|
+
if (terminalCause)
|
|
374
|
+
return;
|
|
375
|
+
outputBytes += chunk.length;
|
|
376
|
+
if (outputBytes > maxOutputBytes) {
|
|
377
|
+
// Stop accumulating and drop what we captured: memory must not keep
|
|
378
|
+
// growing between the breach and the child actually dying. The fatal
|
|
379
|
+
// breach chunk is NOT activity — it neither re-arms idle nor fires a
|
|
380
|
+
// heartbeat right before the kill.
|
|
381
|
+
markTerminal({ kind: "output", maxOutputBytes });
|
|
382
|
+
stdoutChunks.length = 0;
|
|
383
|
+
stderrChunks.length = 0;
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (isStdout) {
|
|
387
|
+
sink.push(chunk);
|
|
388
|
+
productiveStdoutBytes += chunk.length;
|
|
389
|
+
// ACCEPTED stdout chunk: this counts as progress. Fire the (synchronous)
|
|
390
|
+
// activity hook and reset the idle window from now.
|
|
391
|
+
opts.onActivity?.();
|
|
392
|
+
clearTimeout(idleTimer);
|
|
393
|
+
idleTimer = setTimeout(() => markTerminal({ kind: "idle", windowMs: idleTimeoutMs }), idleTimeoutMs);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
// stderr path.
|
|
397
|
+
if (stallSignatures && stallSignatures.length > 0) {
|
|
398
|
+
// Append to the rolling buffer and split on newlines. Keep the trailing
|
|
399
|
+
// partial so a line straddling two chunks is reassembled intact.
|
|
400
|
+
stderrRemainder += chunk.toString("utf8");
|
|
401
|
+
const lines = stderrRemainder.split("\n");
|
|
402
|
+
// The last element is the partial (may be empty if chunk ended on \n).
|
|
403
|
+
stderrRemainder = lines.pop() ?? "";
|
|
404
|
+
let matchedInBatch = false;
|
|
405
|
+
for (const line of lines) {
|
|
406
|
+
const sig = testStallLine(line);
|
|
407
|
+
if (sig === undefined)
|
|
408
|
+
continue;
|
|
409
|
+
matchedInBatch = true;
|
|
410
|
+
stallStrikes += 1;
|
|
411
|
+
stallSignatureLast = sig;
|
|
412
|
+
const attempt = parseAttempt(sig);
|
|
413
|
+
if (attempt !== undefined) {
|
|
414
|
+
stallMaxAttempt = Math.max(stallMaxAttempt, attempt);
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
// Only attempt-less signatures feed the raw-strike fallback, so a
|
|
418
|
+
// legitimate reconnect that keeps resetting to "attempt 1" cannot
|
|
419
|
+
// masquerade as escalating failure.
|
|
420
|
+
stallNoAttemptStrikes += 1;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (matchedInBatch) {
|
|
424
|
+
// A stall line is NOT activity: sink the bytes but never re-arm idle.
|
|
425
|
+
sink.push(chunk);
|
|
426
|
+
if (productiveStdoutBytes === 0 &&
|
|
427
|
+
(stallMaxAttempt >= stallAttemptLimit || stallNoAttemptStrikes >= stallStrikeLimit)) {
|
|
428
|
+
markTerminal({
|
|
429
|
+
kind: "stall",
|
|
430
|
+
signature: stallSignatureLast,
|
|
431
|
+
strikes: stallStrikes,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
// No stall line matched — treat as ordinary activity (existing behavior).
|
|
437
|
+
sink.push(chunk);
|
|
438
|
+
opts.onActivity?.();
|
|
439
|
+
clearTimeout(idleTimer);
|
|
440
|
+
idleTimer = setTimeout(() => markTerminal({ kind: "idle", windowMs: idleTimeoutMs }), idleTimeoutMs);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
// No stall signatures armed: stderr behaves like stdout for activity.
|
|
444
|
+
sink.push(chunk);
|
|
445
|
+
opts.onActivity?.();
|
|
446
|
+
clearTimeout(idleTimer);
|
|
447
|
+
idleTimer = setTimeout(() => markTerminal({ kind: "idle", windowMs: idleTimeoutMs }), idleTimeoutMs);
|
|
448
|
+
};
|
|
449
|
+
child.stdout?.on("data", (chunk) => track(chunk, stdoutChunks, true));
|
|
450
|
+
child.stderr?.on("data", (chunk) => track(chunk, stderrChunks, false));
|
|
451
|
+
child.on("error", (err) => {
|
|
452
|
+
clearTimers();
|
|
453
|
+
liveChildren.delete(child);
|
|
454
|
+
if (settled)
|
|
455
|
+
return;
|
|
456
|
+
settled = true;
|
|
457
|
+
reject(new SpawnError(`Failed to start "${binary}": ${err.message}. Is it installed and on PATH?`, err));
|
|
458
|
+
});
|
|
459
|
+
child.on("close", (code) => {
|
|
460
|
+
clearTimers();
|
|
461
|
+
liveChildren.delete(child);
|
|
462
|
+
if (settled)
|
|
463
|
+
return;
|
|
464
|
+
settled = true;
|
|
465
|
+
if (terminalCause) {
|
|
466
|
+
if (terminalCause.kind === "output") {
|
|
467
|
+
// Never echo the captured bytes back — only the limit that was breached.
|
|
468
|
+
reject(new OutputLimitError(`"${binary}" exceeded output limit of ${terminalCause.maxOutputBytes} bytes`, terminalCause.maxOutputBytes));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (terminalCause.kind === "idle") {
|
|
472
|
+
reject(new TimeoutError(`"${binary}" produced no output for ${terminalCause.windowMs}ms (idle) — it may be hung or its model/backend is unreachable`, terminalCause.windowMs, "idle"));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (terminalCause.kind === "total") {
|
|
476
|
+
reject(new TimeoutError(`"${binary}" timed out after ${timeoutMs}ms (total runtime cap)`, timeoutMs, "total"));
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (terminalCause.kind === "stall") {
|
|
480
|
+
reject(new AgentStalledError(`"${binary}" stalled: detected diagnostic reconnect pattern — "${terminalCause.signature}" (strike ${terminalCause.strikes}). The agent cannot complete a run in this environment (common cause: TLS-intercepting proxy). Treat this agent as unavailable until the network path is fixed.`, terminalCause.signature, terminalCause.strikes));
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
resolve({
|
|
486
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
487
|
+
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
488
|
+
exitCode: code,
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
};
|
|
493
|
+
export const runCommand = (binary, args, opts) => withSlot(() => runCommandInner(binary, args, opts));
|
|
494
|
+
//# sourceMappingURL=exec.js.map
|
package/dist/exec.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ExecResult } from "./exec.js";
|
|
2
|
+
export { stripAnsi } from "./ansi.js";
|
|
3
|
+
/** Lowercased, ANSI-free view of `s` used for phrase matching (never shown). */
|
|
4
|
+
export declare function normalize(s: string): string;
|
|
5
|
+
export type FailureCode = "not_installed" | "invalid_cwd" | "not_authenticated" | "not_configured" | "timed_out" | "output_limit" | "stream_stalled" | "server_busy" | "tool_failure";
|
|
6
|
+
/** Static per-adapter remediation metadata (the pure subset classification needs). */
|
|
7
|
+
interface AdapterMeta {
|
|
8
|
+
name: string;
|
|
9
|
+
loginCommand: string;
|
|
10
|
+
apiKeyEnv?: string;
|
|
11
|
+
}
|
|
12
|
+
/** What the caller supplies: exactly one of a rejected error OR a resolved result. */
|
|
13
|
+
interface Outcome {
|
|
14
|
+
result?: ExecResult;
|
|
15
|
+
error?: unknown;
|
|
16
|
+
}
|
|
17
|
+
export interface FailureClassification {
|
|
18
|
+
code: FailureCode;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Map any agent failure to a stable `{ code, message }` with clean, ANSI-free,
|
|
23
|
+
* actionable text. Pure — no I/O. Precedence (R3): the error branch (classified
|
|
24
|
+
* by TYPE) takes priority over the result branch; within the result branch,
|
|
25
|
+
* overload → auth → config → generic tool_failure.
|
|
26
|
+
*/
|
|
27
|
+
export declare function classifyFailure(adapter: AdapterMeta, outcome: Outcome): FailureClassification;
|