@almyty/runner 1.2.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/README.md +72 -0
- package/dist/binaries.d.ts +30 -0
- package/dist/binaries.js +99 -0
- package/dist/binaries.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +128 -0
- package/dist/cli.js.map +1 -0
- package/dist/coding-agents/detect.d.ts +12 -0
- package/dist/coding-agents/detect.js +35 -0
- package/dist/coding-agents/detect.js.map +1 -0
- package/dist/coding-agents/index.d.ts +15 -0
- package/dist/coding-agents/index.js +15 -0
- package/dist/coding-agents/index.js.map +1 -0
- package/dist/coding-agents/registry.d.ts +9 -0
- package/dist/coding-agents/registry.js +260 -0
- package/dist/coding-agents/registry.js.map +1 -0
- package/dist/coding-agents/spawn-spec.d.ts +47 -0
- package/dist/coding-agents/spawn-spec.js +58 -0
- package/dist/coding-agents/spawn-spec.js.map +1 -0
- package/dist/coding-agents/status.d.ts +38 -0
- package/dist/coding-agents/status.js +111 -0
- package/dist/coding-agents/status.js.map +1 -0
- package/dist/coding-agents/types.d.ts +104 -0
- package/dist/coding-agents/types.js +18 -0
- package/dist/coding-agents/types.js.map +1 -0
- package/dist/coding-sessions.d.ts +80 -0
- package/dist/coding-sessions.js +121 -0
- package/dist/coding-sessions.js.map +1 -0
- package/dist/config.d.ts +49 -0
- package/dist/config.js +180 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon.d.ts +71 -0
- package/dist/daemon.js +353 -0
- package/dist/daemon.js.map +1 -0
- package/dist/handlers.d.ts +39 -0
- package/dist/handlers.js +299 -0
- package/dist/handlers.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/policy.d.ts +16 -0
- package/dist/policy.js +134 -0
- package/dist/policy.js.map +1 -0
- package/dist/process-manager.d.ts +107 -0
- package/dist/process-manager.js +334 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/protocol.d.ts +53 -0
- package/dist/protocol.js +39 -0
- package/dist/protocol.js.map +1 -0
- package/dist/runtime-info.d.ts +23 -0
- package/dist/runtime-info.js +35 -0
- package/dist/runtime-info.js.map +1 -0
- package/dist/streamable-client.d.ts +69 -0
- package/dist/streamable-client.js +204 -0
- package/dist/streamable-client.js.map +1 -0
- package/dist/types.d.ts +112 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { spawn as cpSpawn } from 'child_process';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { execFile } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { RunnerError, RUNNER_ERROR_CODES, } from './types.js';
|
|
7
|
+
import { lastFrameResetIndex } from './coding-agents/index.js';
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
/** Cap on the retained status tail (~ a couple of screenfuls). */
|
|
10
|
+
const RECENT_TAIL_CAP = 8192;
|
|
11
|
+
/**
|
|
12
|
+
* Per-runner process orchestrator.
|
|
13
|
+
*
|
|
14
|
+
* Bookkeeping: every process is namespaced by workspaceId. Cross-
|
|
15
|
+
* workspace lookups error loudly (process_cross_workspace) — this is
|
|
16
|
+
* cheap insurance against an agent in workspace A handing a stale
|
|
17
|
+
* process_id to a tool that runs in workspace B.
|
|
18
|
+
*
|
|
19
|
+
* Buffering: stdout/stderr accumulate in a per-process string buffer.
|
|
20
|
+
* read() drains it; wait_for_idle() reads with idle-detection. We
|
|
21
|
+
* don't try to interpret encodings or framing; the runner's job is
|
|
22
|
+
* to deliver bytes faithfully, not to reason about what they mean.
|
|
23
|
+
*/
|
|
24
|
+
export class ProcessManager {
|
|
25
|
+
adapter;
|
|
26
|
+
maxConcurrent;
|
|
27
|
+
processes = new Map();
|
|
28
|
+
constructor(adapter, maxConcurrent) {
|
|
29
|
+
this.adapter = adapter;
|
|
30
|
+
this.maxConcurrent = maxConcurrent;
|
|
31
|
+
}
|
|
32
|
+
// ── snapshots ───────────────────────────────────────────────────────
|
|
33
|
+
inUse() {
|
|
34
|
+
let n = 0;
|
|
35
|
+
for (const p of this.processes.values())
|
|
36
|
+
if (p.handle.status === 'running')
|
|
37
|
+
n++;
|
|
38
|
+
return n;
|
|
39
|
+
}
|
|
40
|
+
list(workspaceId) {
|
|
41
|
+
const handles = [];
|
|
42
|
+
for (const p of this.processes.values()) {
|
|
43
|
+
if (workspaceId && p.handle.workspaceId !== workspaceId)
|
|
44
|
+
continue;
|
|
45
|
+
handles.push(p.handle);
|
|
46
|
+
}
|
|
47
|
+
return handles;
|
|
48
|
+
}
|
|
49
|
+
// ── lifecycle ───────────────────────────────────────────────────────
|
|
50
|
+
async spawn(workspaceId, opts) {
|
|
51
|
+
if (this.inUse() >= this.maxConcurrent) {
|
|
52
|
+
throw new RunnerError(`runner at capacity (${this.maxConcurrent} concurrent processes)`, RUNNER_ERROR_CODES.CAPACITY_EXCEEDED);
|
|
53
|
+
}
|
|
54
|
+
const usePty = opts.pty !== false;
|
|
55
|
+
const adapter = usePty
|
|
56
|
+
? await this.adapter.spawnPty(opts)
|
|
57
|
+
: await this.adapter.spawnPipe(opts);
|
|
58
|
+
const processId = `proc_${randomUUID()}`;
|
|
59
|
+
const handle = {
|
|
60
|
+
processId,
|
|
61
|
+
binary: opts.binary,
|
|
62
|
+
startedAt: new Date(),
|
|
63
|
+
status: 'running',
|
|
64
|
+
workspaceId,
|
|
65
|
+
};
|
|
66
|
+
let resolveExit;
|
|
67
|
+
const exitPromise = new Promise(r => { resolveExit = r; });
|
|
68
|
+
const running = {
|
|
69
|
+
handle,
|
|
70
|
+
adapter,
|
|
71
|
+
buffer: '',
|
|
72
|
+
recentTail: '',
|
|
73
|
+
lastOutputAt: Date.now(),
|
|
74
|
+
exitPromise,
|
|
75
|
+
exitInfo: null,
|
|
76
|
+
};
|
|
77
|
+
adapter.on('data', (chunk) => {
|
|
78
|
+
const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
79
|
+
running.buffer += text;
|
|
80
|
+
// Maintain the status tail as the latest repaint frame. If this chunk
|
|
81
|
+
// contains a frame-reset (erase-display / cursor-home), the prior frame
|
|
82
|
+
// is stale — keep only from the last reset so a since-cleared status line
|
|
83
|
+
// can't falsely read as busy.
|
|
84
|
+
const reset = lastFrameResetIndex(text);
|
|
85
|
+
running.recentTail =
|
|
86
|
+
reset >= 0
|
|
87
|
+
? text.slice(reset).slice(-RECENT_TAIL_CAP)
|
|
88
|
+
: (running.recentTail + text).slice(-RECENT_TAIL_CAP);
|
|
89
|
+
running.lastOutputAt = Date.now();
|
|
90
|
+
});
|
|
91
|
+
adapter.on('exit', (info) => {
|
|
92
|
+
running.handle.status = info.signal ? 'killed' : 'exited';
|
|
93
|
+
const exit = {
|
|
94
|
+
exitCode: info.exitCode,
|
|
95
|
+
signal: info.signal,
|
|
96
|
+
};
|
|
97
|
+
running.exitInfo = exit;
|
|
98
|
+
resolveExit(exit);
|
|
99
|
+
});
|
|
100
|
+
this.processes.set(processId, running);
|
|
101
|
+
return handle;
|
|
102
|
+
}
|
|
103
|
+
/** Look up a process; enforces workspace ownership. */
|
|
104
|
+
getOrThrow(workspaceId, processId) {
|
|
105
|
+
const proc = this.processes.get(processId);
|
|
106
|
+
if (!proc) {
|
|
107
|
+
throw new RunnerError(`unknown process ${processId}`, RUNNER_ERROR_CODES.PROCESS_NOT_FOUND);
|
|
108
|
+
}
|
|
109
|
+
if (proc.handle.workspaceId !== workspaceId) {
|
|
110
|
+
// Match the spec: "Calls referencing a resource from a
|
|
111
|
+
// different workspace error loudly with a clear message."
|
|
112
|
+
throw new RunnerError(`process ${processId} is not in workspace ${workspaceId}`, RUNNER_ERROR_CODES.PROCESS_CROSS_WORKSPACE);
|
|
113
|
+
}
|
|
114
|
+
return proc;
|
|
115
|
+
}
|
|
116
|
+
// ── I/O ─────────────────────────────────────────────────────────────
|
|
117
|
+
write(workspaceId, processId, data) {
|
|
118
|
+
const proc = this.getOrThrow(workspaceId, processId);
|
|
119
|
+
if (proc.handle.status !== 'running') {
|
|
120
|
+
throw new RunnerError(`process ${processId} has already exited`, RUNNER_ERROR_CODES.PROCESS_ALREADY_EXITED);
|
|
121
|
+
}
|
|
122
|
+
proc.adapter.write(data);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Drain the output buffer. Returns whatever has accumulated since
|
|
126
|
+
* the last call; future calls return only what's new.
|
|
127
|
+
*/
|
|
128
|
+
read(workspaceId, processId) {
|
|
129
|
+
const proc = this.getOrThrow(workspaceId, processId);
|
|
130
|
+
const data = proc.buffer;
|
|
131
|
+
proc.buffer = '';
|
|
132
|
+
return { data, moreAvailable: false };
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Non-destructive peek at the recent output tail. Unlike read(), this does
|
|
136
|
+
* NOT drain anything — it's for status classification, which must work even
|
|
137
|
+
* after the agent has drained `buffer` via read(). Returns the rolling tail
|
|
138
|
+
* (capped at RECENT_TAIL_CAP) plus the process status and idle interval.
|
|
139
|
+
*/
|
|
140
|
+
snapshot(workspaceId, processId) {
|
|
141
|
+
const proc = this.getOrThrow(workspaceId, processId);
|
|
142
|
+
return {
|
|
143
|
+
tail: proc.recentTail,
|
|
144
|
+
binary: proc.handle.binary,
|
|
145
|
+
status: proc.handle.status,
|
|
146
|
+
idleMs: Date.now() - proc.lastOutputAt,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Subscribe to a process's live output and exit. Push-based counterpart to
|
|
151
|
+
* read(): the coding-session layer streams chunks to the backend as they
|
|
152
|
+
* arrive instead of polling. Does NOT drain the pull buffer — a caller can
|
|
153
|
+
* observe and read() the same process without losing bytes in either place.
|
|
154
|
+
* Returns an unsubscribe function.
|
|
155
|
+
*/
|
|
156
|
+
observe(workspaceId, processId, handlers) {
|
|
157
|
+
const proc = this.getOrThrow(workspaceId, processId);
|
|
158
|
+
const onData = (chunk) => {
|
|
159
|
+
handlers.onData?.(typeof chunk === 'string' ? chunk : chunk.toString('utf8'));
|
|
160
|
+
};
|
|
161
|
+
const onExit = (info) => {
|
|
162
|
+
handlers.onExit?.({ exitCode: info.exitCode, signal: info.signal });
|
|
163
|
+
};
|
|
164
|
+
if (handlers.onData)
|
|
165
|
+
proc.adapter.on('data', onData);
|
|
166
|
+
if (handlers.onExit)
|
|
167
|
+
proc.adapter.on('exit', onExit);
|
|
168
|
+
return () => {
|
|
169
|
+
proc.adapter.off('data', onData);
|
|
170
|
+
proc.adapter.off('exit', onExit);
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async waitForIdle(workspaceId, processId, opts) {
|
|
174
|
+
const proc = this.getOrThrow(workspaceId, processId);
|
|
175
|
+
const start = Date.now();
|
|
176
|
+
let captured = '';
|
|
177
|
+
return new Promise(resolve => {
|
|
178
|
+
const drain = () => {
|
|
179
|
+
captured += proc.buffer;
|
|
180
|
+
proc.buffer = '';
|
|
181
|
+
};
|
|
182
|
+
const tick = () => {
|
|
183
|
+
const now = Date.now();
|
|
184
|
+
const sinceLast = now - proc.lastOutputAt;
|
|
185
|
+
const sinceStart = now - start;
|
|
186
|
+
drain();
|
|
187
|
+
if (sinceLast >= opts.idleMs) {
|
|
188
|
+
clearInterval(timer);
|
|
189
|
+
resolve({ data: captured, idle: true });
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (sinceStart >= opts.maxWaitMs) {
|
|
193
|
+
clearInterval(timer);
|
|
194
|
+
resolve({ data: captured, idle: false });
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (proc.handle.status !== 'running') {
|
|
198
|
+
// Drain whatever's queued, then return idle: true since
|
|
199
|
+
// the process is done producing.
|
|
200
|
+
clearInterval(timer);
|
|
201
|
+
resolve({ data: captured, idle: true });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
// Poll cadence: 1/4 of idleMs, capped at 250ms. Polling instead
|
|
206
|
+
// of subscribing because we want to wake up at the deadline
|
|
207
|
+
// even if no output is arriving.
|
|
208
|
+
const intervalMs = Math.max(20, Math.min(250, Math.floor(opts.idleMs / 4)));
|
|
209
|
+
const timer = setInterval(tick, intervalMs);
|
|
210
|
+
timer.unref?.();
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
closeInput(workspaceId, processId) {
|
|
214
|
+
const proc = this.getOrThrow(workspaceId, processId);
|
|
215
|
+
if (proc.handle.status === 'running')
|
|
216
|
+
proc.adapter.closeInput();
|
|
217
|
+
}
|
|
218
|
+
signal(workspaceId, processId, sig) {
|
|
219
|
+
const proc = this.getOrThrow(workspaceId, processId);
|
|
220
|
+
if (proc.handle.status !== 'running')
|
|
221
|
+
return;
|
|
222
|
+
const mapped = `SIG${sig}`;
|
|
223
|
+
proc.adapter.kill(mapped);
|
|
224
|
+
}
|
|
225
|
+
async wait(workspaceId, processId, timeoutMs) {
|
|
226
|
+
const proc = this.getOrThrow(workspaceId, processId);
|
|
227
|
+
if (proc.exitInfo)
|
|
228
|
+
return proc.exitInfo;
|
|
229
|
+
if (timeoutMs === undefined)
|
|
230
|
+
return proc.exitPromise;
|
|
231
|
+
return Promise.race([
|
|
232
|
+
proc.exitPromise,
|
|
233
|
+
new Promise((_, reject) => {
|
|
234
|
+
const t = setTimeout(() => reject(new RunnerError(`wait timed out after ${timeoutMs}ms`, RUNNER_ERROR_CODES.TIMEOUT)), timeoutMs);
|
|
235
|
+
t.unref?.();
|
|
236
|
+
}),
|
|
237
|
+
]);
|
|
238
|
+
}
|
|
239
|
+
/** Kill all processes for a workspace and forget them. Used on workspace release. */
|
|
240
|
+
async killWorkspace(workspaceId) {
|
|
241
|
+
let killed = 0;
|
|
242
|
+
for (const [id, proc] of this.processes) {
|
|
243
|
+
if (proc.handle.workspaceId !== workspaceId)
|
|
244
|
+
continue;
|
|
245
|
+
if (proc.handle.status === 'running') {
|
|
246
|
+
try {
|
|
247
|
+
proc.adapter.kill('SIGKILL');
|
|
248
|
+
}
|
|
249
|
+
catch { /* */ }
|
|
250
|
+
}
|
|
251
|
+
this.processes.delete(id);
|
|
252
|
+
killed++;
|
|
253
|
+
}
|
|
254
|
+
return killed;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// ── Default adapter (real subprocess via node-pty / child_process) ──
|
|
258
|
+
export function createDefaultAdapterFactory() {
|
|
259
|
+
let nodePtyMod = null;
|
|
260
|
+
return {
|
|
261
|
+
async spawnPty(opts) {
|
|
262
|
+
if (!nodePtyMod) {
|
|
263
|
+
// Native dep: load on first use so tests that never request
|
|
264
|
+
// a PTY don't pay the cost or hit the build-toolchain
|
|
265
|
+
// requirement.
|
|
266
|
+
nodePtyMod = await import('node-pty');
|
|
267
|
+
}
|
|
268
|
+
const pty = nodePtyMod.spawn(opts.binary, opts.args, {
|
|
269
|
+
name: 'xterm-color',
|
|
270
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
271
|
+
env: { ...process.env, ...(opts.env ?? {}) },
|
|
272
|
+
cols: 120,
|
|
273
|
+
rows: 30,
|
|
274
|
+
});
|
|
275
|
+
const emitter = new EventEmitter();
|
|
276
|
+
Object.defineProperty(emitter, 'pid', { get: () => pty.pid });
|
|
277
|
+
emitter.write = (data) => pty.write(data);
|
|
278
|
+
emitter.resize = (cols, rows) => pty.resize(cols, rows);
|
|
279
|
+
emitter.kill = (signal) => pty.kill(signal);
|
|
280
|
+
emitter.closeInput = () => {
|
|
281
|
+
// Send EOT (^D); cleanest cross-platform "close stdin" for a PTY.
|
|
282
|
+
pty.write('\x04');
|
|
283
|
+
};
|
|
284
|
+
pty.onData((d) => emitter.emit('data', d));
|
|
285
|
+
pty.onExit(({ exitCode, signal }) => {
|
|
286
|
+
emitter.emit('exit', {
|
|
287
|
+
exitCode: exitCode ?? null,
|
|
288
|
+
signal: signal !== undefined && signal !== 0 ? String(signal) : null,
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
return emitter;
|
|
292
|
+
},
|
|
293
|
+
async spawnPipe(opts) {
|
|
294
|
+
const child = cpSpawn(opts.binary, opts.args, {
|
|
295
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
296
|
+
cwd: opts.cwd,
|
|
297
|
+
env: { ...process.env, ...(opts.env ?? {}) },
|
|
298
|
+
});
|
|
299
|
+
const emitter = new EventEmitter();
|
|
300
|
+
Object.defineProperty(emitter, 'pid', { get: () => child.pid });
|
|
301
|
+
emitter.write = (data) => { child.stdin?.write(data); };
|
|
302
|
+
emitter.kill = (signal) => { child.kill(signal); };
|
|
303
|
+
emitter.closeInput = () => { child.stdin?.end(); };
|
|
304
|
+
child.stdout?.on('data', d => emitter.emit('data', d.toString('utf8')));
|
|
305
|
+
child.stderr?.on('data', d => emitter.emit('data', d.toString('utf8')));
|
|
306
|
+
child.on('exit', (code, signal) => {
|
|
307
|
+
emitter.emit('exit', { exitCode: code, signal: signal ?? null });
|
|
308
|
+
});
|
|
309
|
+
child.on('error', () => {
|
|
310
|
+
emitter.emit('exit', { exitCode: 127, signal: null });
|
|
311
|
+
});
|
|
312
|
+
return emitter;
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
// ── Standalone shell.exec (no PTY, no buffer; one-shot) ─────────────
|
|
317
|
+
export async function shellExec(cmd, env, timeoutMs = 60_000) {
|
|
318
|
+
try {
|
|
319
|
+
const { stdout, stderr } = await execFileAsync('/bin/sh', ['-c', cmd], {
|
|
320
|
+
env: { ...process.env, ...(env ?? {}) },
|
|
321
|
+
timeout: timeoutMs,
|
|
322
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
323
|
+
});
|
|
324
|
+
return { stdout, stderr, exitCode: 0 };
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
return {
|
|
328
|
+
stdout: err.stdout?.toString?.() ?? '',
|
|
329
|
+
stderr: err.stderr?.toString?.() ?? '',
|
|
330
|
+
exitCode: err.code ?? null,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
//# sourceMappingURL=process-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../src/process-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker-protocol envelope, mirroring backend/src/modules/mcp/types/
|
|
3
|
+
* worker-protocol.types.ts. Duplicated rather than imported because
|
|
4
|
+
* the runner can't depend on the backend package; we keep the two
|
|
5
|
+
* files structurally identical and bump the version constant in
|
|
6
|
+
* lockstep when the shape changes.
|
|
7
|
+
*/
|
|
8
|
+
export declare const WORKER_PROTOCOL_VERSION: 1;
|
|
9
|
+
export type WorkerEnvelopeType = 'request' | 'response' | 'event' | 'heartbeat' | 'error';
|
|
10
|
+
export interface WorkerEnvelope<T = unknown> {
|
|
11
|
+
v: typeof WORKER_PROTOCOL_VERSION;
|
|
12
|
+
type: WorkerEnvelopeType;
|
|
13
|
+
id: string;
|
|
14
|
+
seq?: number;
|
|
15
|
+
ts: number;
|
|
16
|
+
payload: T;
|
|
17
|
+
}
|
|
18
|
+
export interface WorkerErrorPayload {
|
|
19
|
+
code: number;
|
|
20
|
+
message: string;
|
|
21
|
+
data?: unknown;
|
|
22
|
+
}
|
|
23
|
+
export declare const WORKER_ERROR_CODES: {
|
|
24
|
+
readonly MALFORMED_ENVELOPE: -32700;
|
|
25
|
+
readonly UNKNOWN_SESSION: -32001;
|
|
26
|
+
readonly REPLAY_UNAVAILABLE: -32002;
|
|
27
|
+
readonly INTERNAL: -32603;
|
|
28
|
+
};
|
|
29
|
+
export declare function isWorkerEnvelope(value: unknown): value is WorkerEnvelope;
|
|
30
|
+
/**
|
|
31
|
+
* Request payload shape: a method name + arbitrary params, just like
|
|
32
|
+
* JSON-RPC. The backend dispatches by method to one of the runner's
|
|
33
|
+
* registered handlers (process.spawn, runner.info, etc.).
|
|
34
|
+
*/
|
|
35
|
+
export interface RequestPayload {
|
|
36
|
+
method: string;
|
|
37
|
+
params: unknown;
|
|
38
|
+
/**
|
|
39
|
+
* Optional workspace scope. Most calls require it; runner.info()
|
|
40
|
+
* is the exception (returns global metadata, not workspace-scoped).
|
|
41
|
+
*/
|
|
42
|
+
workspaceId?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface ResponsePayload {
|
|
45
|
+
ok: boolean;
|
|
46
|
+
result?: unknown;
|
|
47
|
+
error?: WorkerErrorPayload;
|
|
48
|
+
}
|
|
49
|
+
export interface HeartbeatPayload {
|
|
50
|
+
ts: number;
|
|
51
|
+
/** Number of running processes the runner is currently hosting. */
|
|
52
|
+
inUse?: number;
|
|
53
|
+
}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker-protocol envelope, mirroring backend/src/modules/mcp/types/
|
|
3
|
+
* worker-protocol.types.ts. Duplicated rather than imported because
|
|
4
|
+
* the runner can't depend on the backend package; we keep the two
|
|
5
|
+
* files structurally identical and bump the version constant in
|
|
6
|
+
* lockstep when the shape changes.
|
|
7
|
+
*/
|
|
8
|
+
export const WORKER_PROTOCOL_VERSION = 1;
|
|
9
|
+
export const WORKER_ERROR_CODES = {
|
|
10
|
+
MALFORMED_ENVELOPE: -32700,
|
|
11
|
+
UNKNOWN_SESSION: -32001,
|
|
12
|
+
REPLAY_UNAVAILABLE: -32002,
|
|
13
|
+
INTERNAL: -32603,
|
|
14
|
+
};
|
|
15
|
+
export function isWorkerEnvelope(value) {
|
|
16
|
+
if (!value || typeof value !== 'object')
|
|
17
|
+
return false;
|
|
18
|
+
const e = value;
|
|
19
|
+
if (e.v !== WORKER_PROTOCOL_VERSION)
|
|
20
|
+
return false;
|
|
21
|
+
if (typeof e.id !== 'string' || e.id.length === 0)
|
|
22
|
+
return false;
|
|
23
|
+
if (typeof e.ts !== 'number' || !Number.isFinite(e.ts))
|
|
24
|
+
return false;
|
|
25
|
+
if (e.seq !== undefined && (typeof e.seq !== 'number' || !Number.isFinite(e.seq)))
|
|
26
|
+
return false;
|
|
27
|
+
switch (e.type) {
|
|
28
|
+
case 'request':
|
|
29
|
+
case 'response':
|
|
30
|
+
case 'event':
|
|
31
|
+
case 'heartbeat':
|
|
32
|
+
case 'error': break;
|
|
33
|
+
default: return false;
|
|
34
|
+
}
|
|
35
|
+
if (!('payload' in e))
|
|
36
|
+
return false;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAU,CAAC;AAmBlD,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,kBAAkB,EAAE,CAAC,KAAK;IAC1B,eAAe,EAAE,CAAC,KAAK;IACvB,kBAAkB,EAAE,CAAC,KAAK;IAC1B,QAAQ,EAAE,CAAC,KAAK;CACR,CAAC;AAEX,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,IAAI,CAAC,CAAC,CAAC,KAAK,uBAAuB;QAAE,OAAO,KAAK,CAAC;IAClD,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAChE,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IACrE,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAChG,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,SAAS,CAAC;QAAC,KAAK,UAAU,CAAC;QAAC,KAAK,OAAO,CAAC;QAAC,KAAK,WAAW,CAAC;QAAC,KAAK,OAAO,CAAC,CAAC,MAAM;QACrF,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC;IACxB,CAAC;IACD,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ProbeExec } from './binaries.js';
|
|
2
|
+
import { RunnerRuntimeInfo } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Snapshot the runner's runtime characteristics.
|
|
5
|
+
*
|
|
6
|
+
* Every field here is detected, never settable from config. This is
|
|
7
|
+
* the read-only twin of the user's RunnerConfig: routing matches on
|
|
8
|
+
* either, but only the user can change RunnerConfig values, and
|
|
9
|
+
* only the runtime can produce these values.
|
|
10
|
+
*
|
|
11
|
+
* RUNNER_VERSION should track packages/runner/package.json. Bumping
|
|
12
|
+
* the package version without bumping this constant is a benign
|
|
13
|
+
* mismatch; the constant is what the backend records in the runner
|
|
14
|
+
* row's runtimeInfo.runnerVersion.
|
|
15
|
+
*/
|
|
16
|
+
export declare const RUNNER_VERSION = "0.1.0";
|
|
17
|
+
export interface DetectInputs {
|
|
18
|
+
/** Override binary list. Defaults to the resolved config's list. */
|
|
19
|
+
binaries: string[];
|
|
20
|
+
/** Test injection point: stub child_process.spawn. */
|
|
21
|
+
exec?: ProbeExec;
|
|
22
|
+
}
|
|
23
|
+
export declare function detectRuntimeInfo(inputs: DetectInputs): Promise<RunnerRuntimeInfo>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { hostname, arch as osArch, platform as osPlatform, cpus, totalmem } from 'os';
|
|
2
|
+
import { probeAll, realExec } from './binaries.js';
|
|
3
|
+
import { detectCodingAgents } from './coding-agents/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Snapshot the runner's runtime characteristics.
|
|
6
|
+
*
|
|
7
|
+
* Every field here is detected, never settable from config. This is
|
|
8
|
+
* the read-only twin of the user's RunnerConfig: routing matches on
|
|
9
|
+
* either, but only the user can change RunnerConfig values, and
|
|
10
|
+
* only the runtime can produce these values.
|
|
11
|
+
*
|
|
12
|
+
* RUNNER_VERSION should track packages/runner/package.json. Bumping
|
|
13
|
+
* the package version without bumping this constant is a benign
|
|
14
|
+
* mismatch; the constant is what the backend records in the runner
|
|
15
|
+
* row's runtimeInfo.runnerVersion.
|
|
16
|
+
*/
|
|
17
|
+
export const RUNNER_VERSION = '0.1.0';
|
|
18
|
+
export async function detectRuntimeInfo(inputs) {
|
|
19
|
+
const exec = inputs.exec ?? realExec;
|
|
20
|
+
const [binaries, codingAgents] = await Promise.all([
|
|
21
|
+
probeAll(inputs.binaries, exec),
|
|
22
|
+
detectCodingAgents(exec),
|
|
23
|
+
]);
|
|
24
|
+
return {
|
|
25
|
+
os: osPlatform(),
|
|
26
|
+
arch: osArch(),
|
|
27
|
+
hostname: hostname(),
|
|
28
|
+
cpuCount: cpus().length,
|
|
29
|
+
memoryMb: Math.round(totalmem() / (1024 * 1024)),
|
|
30
|
+
runnerVersion: RUNNER_VERSION,
|
|
31
|
+
binaries,
|
|
32
|
+
codingAgents,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=runtime-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-info.js","sourceRoot":"","sources":["../src/runtime-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,IAAI,MAAM,EAAE,QAAQ,IAAI,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAEtF,OAAO,EAAE,QAAQ,EAAa,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG9D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AAStC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAoB;IAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC;IACrC,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACjD,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC;QAC/B,kBAAkB,CAAC,IAAI,CAAC;KACzB,CAAC,CAAC;IACH,OAAO;QACL,EAAE,EAAE,UAAU,EAAE;QAChB,IAAI,EAAE,MAAM,EAAE;QACd,QAAQ,EAAE,QAAQ,EAAE;QACpB,QAAQ,EAAE,IAAI,EAAE,CAAC,MAAM;QACvB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QAChD,aAAa,EAAE,cAAc;QAC7B,QAAQ;QACR,YAAY;KACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { WorkerEnvelope } from './protocol.js';
|
|
3
|
+
/**
|
|
4
|
+
* Client for the backend's Streamable HTTP endpoint at /mcp/streamable.
|
|
5
|
+
*
|
|
6
|
+
* Two responsibilities:
|
|
7
|
+
*
|
|
8
|
+
* 1. POST envelopes to the backend (client -> server). Used for
|
|
9
|
+
* heartbeats, responses to dispatched requests, and unsolicited
|
|
10
|
+
* events the runner wants to emit.
|
|
11
|
+
* 2. Maintain a long-lived GET stream (server -> client) that
|
|
12
|
+
* reconnects on disconnect, replaying via Last-Event-ID. Each
|
|
13
|
+
* received envelope is emitted as a typed `envelope` event the
|
|
14
|
+
* handler registry subscribes to.
|
|
15
|
+
*
|
|
16
|
+
* Reconnect policy:
|
|
17
|
+
* - Exponential backoff capped at 30s (1s, 2s, 4s, 8s, 16s, 30s, 30s).
|
|
18
|
+
* - We track the last event id we successfully parsed; on reconnect
|
|
19
|
+
* we send `Last-Event-ID` so the backend can replay anything we
|
|
20
|
+
* missed.
|
|
21
|
+
* - Reconnect attempts emit `reconnect` events for observability;
|
|
22
|
+
* persistent failure emits `fatal` and the daemon exits.
|
|
23
|
+
*
|
|
24
|
+
* Auth: Bearer JWT in the Authorization header. The token comes from
|
|
25
|
+
* @almyty/client's resolveCredentials and is passed in at construct
|
|
26
|
+
* time; rotation isn't a v1.0 concern (the daemon restarts on token
|
|
27
|
+
* rotation anyway because credentials.json was overwritten).
|
|
28
|
+
*/
|
|
29
|
+
export interface StreamableClientOptions {
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
token: string;
|
|
32
|
+
/** Test injection: replace the global fetch with a stub. */
|
|
33
|
+
fetch?: typeof globalThis.fetch;
|
|
34
|
+
/** Test injection: replace setTimeout with a controllable timer. */
|
|
35
|
+
setTimeoutFn?: typeof setTimeout;
|
|
36
|
+
}
|
|
37
|
+
export declare class StreamableClient extends EventEmitter {
|
|
38
|
+
private readonly opts;
|
|
39
|
+
private sessionId;
|
|
40
|
+
private lastEventId;
|
|
41
|
+
private streamAbort;
|
|
42
|
+
private stopped;
|
|
43
|
+
private reconnectAttempt;
|
|
44
|
+
private readonly fetchImpl;
|
|
45
|
+
private readonly setTimeoutFn;
|
|
46
|
+
constructor(opts: StreamableClientOptions);
|
|
47
|
+
/** Returns the session id once the first POST has assigned one. */
|
|
48
|
+
getSessionId(): string | null;
|
|
49
|
+
/** Send an envelope. Returns the parsed response envelope when the
|
|
50
|
+
* backend hands one back inline (for unary requests); otherwise null. */
|
|
51
|
+
send<T>(env: WorkerEnvelope<T>): Promise<WorkerEnvelope | null>;
|
|
52
|
+
/** Open the GET stream and dispatch envelopes via emit('envelope'). */
|
|
53
|
+
openStream(): Promise<void>;
|
|
54
|
+
stop(): void;
|
|
55
|
+
/**
|
|
56
|
+
* SSE parser. The MCP Streamable HTTP wire format follows the SSE
|
|
57
|
+
* spec: events are separated by blank lines, fields are `id:`,
|
|
58
|
+
* `event:`, `data:`. We accept arbitrary header order and ignore
|
|
59
|
+
* unknown fields.
|
|
60
|
+
*
|
|
61
|
+
* Buffers across chunk boundaries because TCP doesn't respect SSE
|
|
62
|
+
* frame boundaries; one fetch chunk often contains a half-event.
|
|
63
|
+
*/
|
|
64
|
+
private consumeStream;
|
|
65
|
+
private handleFrame;
|
|
66
|
+
private scheduleReconnect;
|
|
67
|
+
}
|
|
68
|
+
/** Helper to mint an envelope on the runner side. */
|
|
69
|
+
export declare function envelope<T>(type: WorkerEnvelope['type'], payload: T, correlationId?: string): WorkerEnvelope<T>;
|