@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
package/dist/handlers.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { shellExec } from './process-manager.js';
|
|
2
|
+
import { enforceSpawnPolicy, enforceShellPolicy } from './policy.js';
|
|
3
|
+
import { detectRuntimeInfo, RUNNER_VERSION } from './runtime-info.js';
|
|
4
|
+
import { WORKER_ERROR_CODES } from './protocol.js';
|
|
5
|
+
import { buildAgentSpawn, classifyStatus, detectCodingAgents, findByBinary, getCodingAgent, listCodingAgents, stripVtEscapes, } from './coding-agents/index.js';
|
|
6
|
+
import { realExec } from './binaries.js';
|
|
7
|
+
import { RUNNER_ERROR_CODES, RunnerError, } from './types.js';
|
|
8
|
+
export async function dispatchHandler(ctx, req) {
|
|
9
|
+
try {
|
|
10
|
+
switch (req.method) {
|
|
11
|
+
case 'process.spawn': return ok(await spawn(ctx, req));
|
|
12
|
+
case 'process.write': return ok(await write(ctx, req));
|
|
13
|
+
case 'process.read': return ok(await read(ctx, req));
|
|
14
|
+
case 'process.wait_for_idle': return ok(await waitForIdle(ctx, req));
|
|
15
|
+
case 'process.signal': return ok(await signal(ctx, req));
|
|
16
|
+
case 'process.close_input': return ok(await closeInput(ctx, req));
|
|
17
|
+
case 'process.list': return ok(await list(ctx, req));
|
|
18
|
+
case 'process.wait': return ok(await waitH(ctx, req));
|
|
19
|
+
case 'shell.exec': return ok(await shell(ctx, req));
|
|
20
|
+
case 'runner.info': return ok(await info(ctx));
|
|
21
|
+
case 'agent.list': return ok(agentList());
|
|
22
|
+
case 'agent.spawn': return ok(await agentSpawn(ctx, req));
|
|
23
|
+
case 'agent.status': return ok(agentStatus(ctx, req));
|
|
24
|
+
case 'coding.list': return ok(await codingList(ctx));
|
|
25
|
+
case 'coding.start': return ok(await codingStart(ctx, req));
|
|
26
|
+
case 'coding.input': return ok(codingInput(ctx, req));
|
|
27
|
+
case 'coding.status': return ok(codingStatus(ctx, req));
|
|
28
|
+
case 'coding.stop': return ok(codingStop(ctx, req));
|
|
29
|
+
default:
|
|
30
|
+
return err(WORKER_ERROR_CODES.MALFORMED_ENVELOPE, `unknown method ${req.method}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
if (e instanceof RunnerError) {
|
|
35
|
+
return err(WORKER_ERROR_CODES.INTERNAL, e.message, { code: e.code, data: e.data });
|
|
36
|
+
}
|
|
37
|
+
return err(WORKER_ERROR_CODES.INTERNAL, e?.message ?? String(e));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// ── handlers ────────────────────────────────────────────────────────
|
|
41
|
+
async function spawn(ctx, req) {
|
|
42
|
+
const ws = requireWorkspace(req);
|
|
43
|
+
const p = req.params;
|
|
44
|
+
if (typeof p.binary !== 'string' || p.binary.length === 0) {
|
|
45
|
+
throw new RunnerError('binary is required', RUNNER_ERROR_CODES.PATH_DENIED);
|
|
46
|
+
}
|
|
47
|
+
const args = Array.isArray(p.args) ? p.args.map(String) : [];
|
|
48
|
+
const env = isStringMap(p.env) ? p.env : undefined;
|
|
49
|
+
const opts = {
|
|
50
|
+
binary: p.binary,
|
|
51
|
+
args,
|
|
52
|
+
env,
|
|
53
|
+
pty: p.pty !== false,
|
|
54
|
+
cwd: typeof p.cwd === 'string' ? p.cwd : undefined,
|
|
55
|
+
};
|
|
56
|
+
// Enforce the runner execution policy (isolation/deny/cwd/install) and
|
|
57
|
+
// use the sanitized env — refuses, rather than silently running, anything
|
|
58
|
+
// the config forbids.
|
|
59
|
+
opts.env = enforceSpawnPolicy(ctx.config, opts).env;
|
|
60
|
+
const handle = await ctx.processes.spawn(ws, opts);
|
|
61
|
+
return { processId: handle.processId };
|
|
62
|
+
}
|
|
63
|
+
async function write(ctx, req) {
|
|
64
|
+
const ws = requireWorkspace(req);
|
|
65
|
+
const p = req.params;
|
|
66
|
+
ctx.processes.write(ws, requireString(p.processId, 'processId'), requireString(p.data, 'data'));
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
async function read(ctx, req) {
|
|
70
|
+
const ws = requireWorkspace(req);
|
|
71
|
+
const p = req.params;
|
|
72
|
+
return ctx.processes.read(ws, requireString(p.processId, 'processId'));
|
|
73
|
+
}
|
|
74
|
+
async function waitForIdle(ctx, req) {
|
|
75
|
+
const ws = requireWorkspace(req);
|
|
76
|
+
const p = req.params;
|
|
77
|
+
const idleMs = typeof p.idleMs === 'number' ? p.idleMs : 250;
|
|
78
|
+
const maxWaitMs = typeof p.maxWaitMs === 'number' ? p.maxWaitMs : 30_000;
|
|
79
|
+
return ctx.processes.waitForIdle(ws, requireString(p.processId, 'processId'), { idleMs, maxWaitMs });
|
|
80
|
+
}
|
|
81
|
+
async function signal(ctx, req) {
|
|
82
|
+
const ws = requireWorkspace(req);
|
|
83
|
+
const p = req.params;
|
|
84
|
+
const sig = p.signal ?? 'TERM';
|
|
85
|
+
if (sig !== 'TERM' && sig !== 'INT' && sig !== 'KILL') {
|
|
86
|
+
throw new RunnerError(`unsupported signal ${sig}`, RUNNER_ERROR_CODES.PATH_DENIED);
|
|
87
|
+
}
|
|
88
|
+
ctx.processes.signal(ws, requireString(p.processId, 'processId'), sig);
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
async function closeInput(ctx, req) {
|
|
92
|
+
const ws = requireWorkspace(req);
|
|
93
|
+
const p = req.params;
|
|
94
|
+
ctx.processes.closeInput(ws, requireString(p.processId, 'processId'));
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
async function list(ctx, req) {
|
|
98
|
+
const ws = req.workspaceId; // optional: list all when undefined
|
|
99
|
+
return { processes: ctx.processes.list(ws) };
|
|
100
|
+
}
|
|
101
|
+
async function waitH(ctx, req) {
|
|
102
|
+
const ws = requireWorkspace(req);
|
|
103
|
+
const p = req.params;
|
|
104
|
+
return ctx.processes.wait(ws, requireString(p.processId, 'processId'), p.timeoutMs);
|
|
105
|
+
}
|
|
106
|
+
async function shell(ctx, req) {
|
|
107
|
+
// shell.exec is workspace-scoped for audit but doesn't depend on
|
|
108
|
+
// the process manager's bookkeeping; one-shot execution returns
|
|
109
|
+
// stdout+stderr+exit and forgets.
|
|
110
|
+
requireWorkspace(req);
|
|
111
|
+
const p = req.params;
|
|
112
|
+
const cmd = requireString(p.cmd, 'cmd');
|
|
113
|
+
// Enforce the same execution policy as process.spawn — isolation
|
|
114
|
+
// fail-closed, denyPatterns, installBlocked — and run with the
|
|
115
|
+
// sanitized env. Without this, shell.exec was a hole straight past
|
|
116
|
+
// every protection spawn honours.
|
|
117
|
+
const { env } = enforceShellPolicy(ctx.config, cmd, p.env);
|
|
118
|
+
return shellExec(cmd, env, p.timeoutMs);
|
|
119
|
+
}
|
|
120
|
+
async function info(ctx) {
|
|
121
|
+
// We re-detect on every call; binaries can be installed/removed at
|
|
122
|
+
// runtime and the user expects the snapshot to be fresh. Cheap when
|
|
123
|
+
// probeAll runs in parallel.
|
|
124
|
+
const runtime = ctx.cachedRuntimeInfo
|
|
125
|
+
?? await detectRuntimeInfo({ binaries: [] });
|
|
126
|
+
return {
|
|
127
|
+
name: ctx.runnerName,
|
|
128
|
+
labels: ctx.labels,
|
|
129
|
+
runtime,
|
|
130
|
+
runnerVersion: RUNNER_VERSION,
|
|
131
|
+
binaries: runtime.binaries,
|
|
132
|
+
capacity: { maxConcurrent: ctx.maxConcurrent, inUse: ctx.processes.inUse() },
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// ── coding-agent surface ────────────────────────────────────────────
|
|
136
|
+
/**
|
|
137
|
+
* Catalog of coding-agent platforms this runner knows how to drive. Static —
|
|
138
|
+
* what's actually installed is in runner.info's runtime.codingAgents. The
|
|
139
|
+
* catalog tells an orchestrator what it COULD ask for and the levers per CLI.
|
|
140
|
+
*/
|
|
141
|
+
function agentList() {
|
|
142
|
+
return {
|
|
143
|
+
platforms: listCodingAgents().map((s) => ({
|
|
144
|
+
id: s.id,
|
|
145
|
+
displayName: s.displayName,
|
|
146
|
+
binary: s.binary,
|
|
147
|
+
providerFamily: s.providerFamily,
|
|
148
|
+
apiKeyEnvVars: s.apiKeyEnvVars,
|
|
149
|
+
configDirEnvVar: s.configDirEnvVar,
|
|
150
|
+
supportsMcp: s.supportsMcp,
|
|
151
|
+
canManage: s.canManage,
|
|
152
|
+
resume: s.session.kind,
|
|
153
|
+
})),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Launch a coding-agent CLI as an unattended member. Builds the platform's
|
|
158
|
+
* spawn spec (headless auth + isolated config home + auto-approve + resume),
|
|
159
|
+
* then runs it through the SAME execution policy as process.spawn — the coding
|
|
160
|
+
* agent gets no privilege the generic surface wouldn't.
|
|
161
|
+
*/
|
|
162
|
+
async function agentSpawn(ctx, req) {
|
|
163
|
+
const ws = requireWorkspace(req);
|
|
164
|
+
const p = req.params;
|
|
165
|
+
const spec = getCodingAgent(requireString(p.platform, 'platform'));
|
|
166
|
+
if (!spec) {
|
|
167
|
+
throw new RunnerError(`unknown coding-agent platform ${p.platform}`, RUNNER_ERROR_CODES.PATH_DENIED);
|
|
168
|
+
}
|
|
169
|
+
const opts = buildAgentSpawn(spec, {
|
|
170
|
+
apiKey: p.apiKey,
|
|
171
|
+
apiKeyEnvVar: p.apiKeyEnvVar,
|
|
172
|
+
configDir: p.configDir,
|
|
173
|
+
autoApprove: p.autoApprove,
|
|
174
|
+
model: p.model,
|
|
175
|
+
resumeSessionId: p.resumeSessionId,
|
|
176
|
+
extraArgs: Array.isArray(p.extraArgs) ? p.extraArgs.map(String) : undefined,
|
|
177
|
+
cwd: p.cwd,
|
|
178
|
+
});
|
|
179
|
+
// Same policy gate as process.spawn — isolation/deny/cwd/install all apply.
|
|
180
|
+
opts.env = enforceSpawnPolicy(ctx.config, opts).env;
|
|
181
|
+
const handle = await ctx.processes.spawn(ws, opts);
|
|
182
|
+
return { processId: handle.processId, platform: spec.id, binary: spec.binary, args: opts.args };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Non-destructively classify a spawned agent's pane: busy / idle /
|
|
186
|
+
* awaiting_input / awaiting_auth / error. Resolves the platform from the
|
|
187
|
+
* process's binary (or an explicit `platform` override), strips VT escapes
|
|
188
|
+
* from the recent tail, and runs the per-CLI status table. Does NOT drain the
|
|
189
|
+
* agent's own read() buffer.
|
|
190
|
+
*/
|
|
191
|
+
function agentStatus(ctx, req) {
|
|
192
|
+
const ws = requireWorkspace(req);
|
|
193
|
+
const p = req.params;
|
|
194
|
+
const snap = ctx.processes.snapshot(ws, requireString(p.processId, 'processId'));
|
|
195
|
+
const spec = p.platform ? getCodingAgent(p.platform) : findByBinary(snap.binary);
|
|
196
|
+
if (!spec) {
|
|
197
|
+
throw new RunnerError(`cannot resolve coding-agent platform for binary ${snap.binary}`, RUNNER_ERROR_CODES.PATH_DENIED);
|
|
198
|
+
}
|
|
199
|
+
const screen = stripVtEscapes(snap.tail);
|
|
200
|
+
const agentStatusValue = snap.status !== 'running' ? 'exited' : classifyStatus(spec.status, screen);
|
|
201
|
+
return {
|
|
202
|
+
platform: spec.id,
|
|
203
|
+
processStatus: snap.status,
|
|
204
|
+
status: agentStatusValue,
|
|
205
|
+
idleMs: snap.idleMs,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
// ── coding-session surface (chat-to-runner bridge) ─────────────────
|
|
209
|
+
//
|
|
210
|
+
// Unlike agent.* (workspace-scoped unattended members), coding.* sessions
|
|
211
|
+
// are daemon-global interactive sessions driven from the chat REPL. The
|
|
212
|
+
// CodingSessionManager namespaces each one under a synthetic workspace and
|
|
213
|
+
// streams output upstream as coding.output/coding.exit event envelopes.
|
|
214
|
+
function requireCoding(ctx) {
|
|
215
|
+
if (!ctx.coding) {
|
|
216
|
+
throw new RunnerError('coding sessions are not available on this runner', RUNNER_ERROR_CODES.PATH_DENIED);
|
|
217
|
+
}
|
|
218
|
+
return ctx.coding;
|
|
219
|
+
}
|
|
220
|
+
/** Coding CLIs actually installed on this machine (fresh probe). */
|
|
221
|
+
async function codingList(ctx) {
|
|
222
|
+
const agents = await detectCodingAgents(ctx.probeExec ?? realExec);
|
|
223
|
+
return { agents };
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Start a coding session: resolve the platform spec, build the spawn spec
|
|
227
|
+
* (auto-approve, task as final positional arg, pipe stdio), run it through
|
|
228
|
+
* the SAME execution policy as process.spawn, then hand it to the session
|
|
229
|
+
* registry which streams output back as events.
|
|
230
|
+
*/
|
|
231
|
+
async function codingStart(ctx, req) {
|
|
232
|
+
const coding = requireCoding(ctx);
|
|
233
|
+
const p = req.params;
|
|
234
|
+
const spec = getCodingAgent(requireString(p.agent, 'agent'));
|
|
235
|
+
if (!spec) {
|
|
236
|
+
throw new RunnerError(`unknown coding agent ${p.agent}`, RUNNER_ERROR_CODES.PATH_DENIED);
|
|
237
|
+
}
|
|
238
|
+
const input = {
|
|
239
|
+
task: requireString(p.task, 'task'),
|
|
240
|
+
cwd: typeof p.cwd === 'string' && p.cwd.length > 0 ? p.cwd : undefined,
|
|
241
|
+
model: typeof p.model === 'string' ? p.model : undefined,
|
|
242
|
+
extraArgs: Array.isArray(p.extraArgs) ? p.extraArgs.map(String) : undefined,
|
|
243
|
+
};
|
|
244
|
+
const opts = coding.buildStartOptions(spec, input);
|
|
245
|
+
// Same policy gate as process.spawn — isolation/deny/cwd/install all apply.
|
|
246
|
+
opts.env = enforceSpawnPolicy(ctx.config, opts).env;
|
|
247
|
+
return coding.start(spec, input, opts);
|
|
248
|
+
}
|
|
249
|
+
/** Route a line of user input to the session's stdin. */
|
|
250
|
+
function codingInput(ctx, req) {
|
|
251
|
+
const coding = requireCoding(ctx);
|
|
252
|
+
const p = req.params;
|
|
253
|
+
coding.input(requireString(p.sessionId, 'sessionId'), requireString(p.data, 'data'));
|
|
254
|
+
return {};
|
|
255
|
+
}
|
|
256
|
+
/** One session's status, or the full session list when sessionId is omitted. */
|
|
257
|
+
function codingStatus(ctx, req) {
|
|
258
|
+
const coding = requireCoding(ctx);
|
|
259
|
+
const p = req.params;
|
|
260
|
+
if (typeof p.sessionId === 'string' && p.sessionId.length > 0) {
|
|
261
|
+
return coding.status(p.sessionId);
|
|
262
|
+
}
|
|
263
|
+
return { sessions: coding.list() };
|
|
264
|
+
}
|
|
265
|
+
/** Stop a session (TERM; KILL with force). */
|
|
266
|
+
function codingStop(ctx, req) {
|
|
267
|
+
const coding = requireCoding(ctx);
|
|
268
|
+
const p = req.params;
|
|
269
|
+
return coding.stop(requireString(p.sessionId, 'sessionId'), p.force === true);
|
|
270
|
+
}
|
|
271
|
+
// ── helpers ─────────────────────────────────────────────────────────
|
|
272
|
+
function requireWorkspace(req) {
|
|
273
|
+
if (!req.workspaceId) {
|
|
274
|
+
throw new RunnerError('workspaceId is required', RUNNER_ERROR_CODES.PATH_DENIED);
|
|
275
|
+
}
|
|
276
|
+
return req.workspaceId;
|
|
277
|
+
}
|
|
278
|
+
function requireString(v, name) {
|
|
279
|
+
if (typeof v !== 'string' || v.length === 0) {
|
|
280
|
+
throw new RunnerError(`${name} is required`, RUNNER_ERROR_CODES.PATH_DENIED);
|
|
281
|
+
}
|
|
282
|
+
return v;
|
|
283
|
+
}
|
|
284
|
+
function isStringMap(v) {
|
|
285
|
+
if (!v || typeof v !== 'object')
|
|
286
|
+
return false;
|
|
287
|
+
for (const val of Object.values(v)) {
|
|
288
|
+
if (typeof val !== 'string')
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
function ok(result) {
|
|
294
|
+
return { ok: true, result };
|
|
295
|
+
}
|
|
296
|
+
function err(code, message, data) {
|
|
297
|
+
return { ok: false, error: { code, message, data } };
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic exports for the runner package.
|
|
3
|
+
*
|
|
4
|
+
* Most consumers run the CLI (`almyty-runner start`); programmatic
|
|
5
|
+
* access exists so backend tests and the integration spec can drive
|
|
6
|
+
* the runner without spawning a subprocess.
|
|
7
|
+
*/
|
|
8
|
+
export { RunnerDaemon, readStatus, stopDaemon, type DaemonStatus } from './daemon.js';
|
|
9
|
+
export { ProcessManager, shellExec, createDefaultAdapterFactory, type ProcessAdapter, type AdapterFactory, } from './process-manager.js';
|
|
10
|
+
export { CodingSessionManager, type CodingEmitter, type CodingEventPayload, type CodingSessionRecord, type CodingStartInput, } from './coding-sessions.js';
|
|
11
|
+
export { loadConfig, DEFAULTS, DEFAULT_BINARY_PROBE_LIST, GLOBAL_CONFIG_PATH, PROJECT_CONFIG_PATH } from './config.js';
|
|
12
|
+
export { detectRuntimeInfo, RUNNER_VERSION } from './runtime-info.js';
|
|
13
|
+
export { probe, probeAll, realExec, type ProbeExec } from './binaries.js';
|
|
14
|
+
export { dispatchHandler, type HandlerContext } from './handlers.js';
|
|
15
|
+
export { StreamableClient, envelope, type StreamableClientOptions, } from './streamable-client.js';
|
|
16
|
+
export { WORKER_PROTOCOL_VERSION, WORKER_ERROR_CODES, isWorkerEnvelope, type WorkerEnvelope, type RequestPayload, type ResponsePayload, type HeartbeatPayload, } from './protocol.js';
|
|
17
|
+
export { RunnerError, RUNNER_ERROR_CODES, type ResolvedConfig, type RunnerConfig, type RunnerInfo, type RunnerRuntimeInfo, type ProcessHandle, type ProcessSignal, type SpawnOptions, type ReadResult, type WaitForIdleOptions, type WaitForIdleResult, type WaitResult, type ShellExecResult, } from './types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic exports for the runner package.
|
|
3
|
+
*
|
|
4
|
+
* Most consumers run the CLI (`almyty-runner start`); programmatic
|
|
5
|
+
* access exists so backend tests and the integration spec can drive
|
|
6
|
+
* the runner without spawning a subprocess.
|
|
7
|
+
*/
|
|
8
|
+
export { RunnerDaemon, readStatus, stopDaemon } from './daemon.js';
|
|
9
|
+
export { ProcessManager, shellExec, createDefaultAdapterFactory, } from './process-manager.js';
|
|
10
|
+
export { CodingSessionManager, } from './coding-sessions.js';
|
|
11
|
+
export { loadConfig, DEFAULTS, DEFAULT_BINARY_PROBE_LIST, GLOBAL_CONFIG_PATH, PROJECT_CONFIG_PATH } from './config.js';
|
|
12
|
+
export { detectRuntimeInfo, RUNNER_VERSION } from './runtime-info.js';
|
|
13
|
+
export { probe, probeAll, realExec } from './binaries.js';
|
|
14
|
+
export { dispatchHandler } from './handlers.js';
|
|
15
|
+
export { StreamableClient, envelope, } from './streamable-client.js';
|
|
16
|
+
export { WORKER_PROTOCOL_VERSION, WORKER_ERROR_CODES, isWorkerEnvelope, } from './protocol.js';
|
|
17
|
+
export { RunnerError, RUNNER_ERROR_CODES, } from './types.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAC;AACtF,OAAO,EACL,cAAc,EACd,SAAS,EACT,2BAA2B,GAG5B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,oBAAoB,GAKrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvH,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAkB,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAuB,MAAM,eAAe,CAAC;AACrE,OAAO,EACL,gBAAgB,EAChB,QAAQ,GAET,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,gBAAgB,GAKjB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,WAAW,EACX,kBAAkB,GAanB,MAAM,YAAY,CAAC"}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RunnerConfig } from './types.js';
|
|
2
|
+
/** Drop env keys a payload must not be allowed to set. */
|
|
3
|
+
export declare function sanitizeEnv(env?: Record<string, string>): Record<string, string> | undefined;
|
|
4
|
+
/** Enforce policy for a process.spawn; returns the sanitized env to use. */
|
|
5
|
+
export declare function enforceSpawnPolicy(config: RunnerConfig, opts: {
|
|
6
|
+
binary: string;
|
|
7
|
+
args: string[];
|
|
8
|
+
cwd?: string;
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
}): {
|
|
11
|
+
env?: Record<string, string>;
|
|
12
|
+
};
|
|
13
|
+
/** Enforce policy for a shell.exec; returns the sanitized env to use. */
|
|
14
|
+
export declare function enforceShellPolicy(config: RunnerConfig, cmd: string, env?: Record<string, string>): {
|
|
15
|
+
env?: Record<string, string>;
|
|
16
|
+
};
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner execution policy enforcement.
|
|
3
|
+
*
|
|
4
|
+
* The runner config advertises an isolation tier, a cwd allowlist, deny
|
|
5
|
+
* patterns, a network-blocked flag, and an install-blocked flag. Until
|
|
6
|
+
* this module these were *documented but never enforced* — every
|
|
7
|
+
* `process.spawn` / `shell.exec` ran any backend-supplied command on the
|
|
8
|
+
* host, with attacker-controllable env, regardless of config. That is host
|
|
9
|
+
* RCE behind a config that lies about protecting the user.
|
|
10
|
+
*
|
|
11
|
+
* This enforces the contract before any spawn:
|
|
12
|
+
* - Fail closed when `defaultIsolation: 'container'` is configured but no
|
|
13
|
+
* container runtime is implemented (the default) — refuse rather than
|
|
14
|
+
* silently run on the host. Host execution is opt-in (`isolation: host`).
|
|
15
|
+
* - Fail closed when `networkBlocked` is requested (can't be honoured on
|
|
16
|
+
* the host).
|
|
17
|
+
* - Reject commands matching `denyPatterns`, and package installs when
|
|
18
|
+
* `installBlocked`.
|
|
19
|
+
* - Constrain `cwd` to `allowedCwdRoots` (realpath-canonicalized to block
|
|
20
|
+
* symlink/`..` escape).
|
|
21
|
+
* - Strip env keys a payload must never set (PATH, LD_PRELOAD, …).
|
|
22
|
+
*/
|
|
23
|
+
import { realpathSync } from 'fs';
|
|
24
|
+
import { resolve, sep } from 'path';
|
|
25
|
+
import { RunnerError, RUNNER_ERROR_CODES } from './types.js';
|
|
26
|
+
// Env vars a payload must never override: they redirect which binary runs
|
|
27
|
+
// (PATH), preload code into the process (LD_PRELOAD and friends), or change
|
|
28
|
+
// interpreter behaviour. The daemon's own values are kept by stripping
|
|
29
|
+
// these from the inbound env before it's merged over process.env.
|
|
30
|
+
const BLOCKED_ENV_KEYS = new Set([
|
|
31
|
+
'PATH',
|
|
32
|
+
'LD_PRELOAD',
|
|
33
|
+
'LD_LIBRARY_PATH',
|
|
34
|
+
'LD_AUDIT',
|
|
35
|
+
'DYLD_INSERT_LIBRARIES',
|
|
36
|
+
'DYLD_LIBRARY_PATH',
|
|
37
|
+
'NODE_OPTIONS',
|
|
38
|
+
'PYTHONPATH',
|
|
39
|
+
'PYTHONSTARTUP',
|
|
40
|
+
'BASH_ENV',
|
|
41
|
+
'ENV',
|
|
42
|
+
'IFS',
|
|
43
|
+
'GIT_SSH',
|
|
44
|
+
'GIT_SSH_COMMAND',
|
|
45
|
+
'GIT_EXEC_PATH',
|
|
46
|
+
'PROMPT_COMMAND',
|
|
47
|
+
].map((k) => k.toUpperCase()));
|
|
48
|
+
const INSTALL_RE = /\b(?:npm\s+(?:i|install|ci|add)|yarn\s+add|pnpm\s+(?:add|install|i)|pip3?\s+install|gem\s+install|cargo\s+install|go\s+install|apt(?:-get)?\s+install|brew\s+install|nix-env|conda\s+install)\b/i;
|
|
49
|
+
/** Drop env keys a payload must not be allowed to set. */
|
|
50
|
+
export function sanitizeEnv(env) {
|
|
51
|
+
if (!env)
|
|
52
|
+
return env;
|
|
53
|
+
const out = {};
|
|
54
|
+
for (const [k, v] of Object.entries(env)) {
|
|
55
|
+
if (BLOCKED_ENV_KEYS.has(k.toUpperCase()))
|
|
56
|
+
continue;
|
|
57
|
+
out[k] = v;
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
function assertIsolationSupported(config) {
|
|
62
|
+
if (config.defaultIsolation === 'container') {
|
|
63
|
+
throw new RunnerError('container isolation is configured but not implemented by this runner build; ' +
|
|
64
|
+
"refusing to run on the host. Set isolation to 'host' to explicitly allow host execution.", RUNNER_ERROR_CODES.COMMAND_DENIED);
|
|
65
|
+
}
|
|
66
|
+
if (config.networkBlocked) {
|
|
67
|
+
throw new RunnerError('networkBlocked is set but cannot be enforced under host isolation; refusing to run.', RUNNER_ERROR_CODES.COMMAND_DENIED);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function matchesDeny(command, patterns) {
|
|
71
|
+
for (const pat of patterns) {
|
|
72
|
+
if (!pat)
|
|
73
|
+
continue;
|
|
74
|
+
try {
|
|
75
|
+
if (new RegExp(pat).test(command))
|
|
76
|
+
return pat;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Invalid regex — fall back to a literal substring match so a
|
|
80
|
+
// malformed pattern still denies rather than silently allowing.
|
|
81
|
+
if (command.includes(pat))
|
|
82
|
+
return pat;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
function assertCommandAllowed(config, command) {
|
|
88
|
+
const deny = matchesDeny(command, config.denyPatterns ?? []);
|
|
89
|
+
if (deny) {
|
|
90
|
+
throw new RunnerError(`command blocked by denyPattern: ${deny}`, RUNNER_ERROR_CODES.COMMAND_DENIED);
|
|
91
|
+
}
|
|
92
|
+
if (config.installBlocked && INSTALL_RE.test(command)) {
|
|
93
|
+
throw new RunnerError('package installation is blocked by runner policy (installBlocked)', RUNNER_ERROR_CODES.COMMAND_DENIED);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function canonical(p) {
|
|
97
|
+
const abs = resolve(p);
|
|
98
|
+
try {
|
|
99
|
+
return realpathSync(abs); // resolve symlinks to block escape via a link
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return abs; // path may legitimately not exist yet
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function assertCwdAllowed(config, cwd) {
|
|
106
|
+
const roots = config.allowedCwdRoots ?? [];
|
|
107
|
+
if (roots.length === 0)
|
|
108
|
+
return; // no restriction configured
|
|
109
|
+
if (!cwd) {
|
|
110
|
+
throw new RunnerError('cwd is required when allowedCwdRoots is configured', RUNNER_ERROR_CODES.PATH_DENIED);
|
|
111
|
+
}
|
|
112
|
+
const real = canonical(cwd);
|
|
113
|
+
const allowed = roots.some((root) => {
|
|
114
|
+
const r = canonical(root);
|
|
115
|
+
return real === r || real.startsWith(r + sep);
|
|
116
|
+
});
|
|
117
|
+
if (!allowed) {
|
|
118
|
+
throw new RunnerError(`cwd is outside allowedCwdRoots: ${cwd}`, RUNNER_ERROR_CODES.PATH_DENIED);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Enforce policy for a process.spawn; returns the sanitized env to use. */
|
|
122
|
+
export function enforceSpawnPolicy(config, opts) {
|
|
123
|
+
assertIsolationSupported(config);
|
|
124
|
+
assertCwdAllowed(config, opts.cwd);
|
|
125
|
+
assertCommandAllowed(config, [opts.binary, ...opts.args].join(' '));
|
|
126
|
+
return { env: sanitizeEnv(opts.env) };
|
|
127
|
+
}
|
|
128
|
+
/** Enforce policy for a shell.exec; returns the sanitized env to use. */
|
|
129
|
+
export function enforceShellPolicy(config, cmd, env) {
|
|
130
|
+
assertIsolationSupported(config);
|
|
131
|
+
assertCommandAllowed(config, cmd);
|
|
132
|
+
return { env: sanitizeEnv(env) };
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { ProcessHandle, ProcessSignal, ProcessStatus, ReadResult, ShellExecResult, SpawnOptions, WaitForIdleOptions, WaitForIdleResult, WaitResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Adapter interface around the actual PTY/pipe implementation.
|
|
5
|
+
*
|
|
6
|
+
* The runtime uses node-pty for PTY mode (the default) and node's
|
|
7
|
+
* built-in child_process for raw pipe mode. Both behind one interface
|
|
8
|
+
* so tests can swap in a synthetic child without spinning subprocesses.
|
|
9
|
+
*
|
|
10
|
+
* node-pty isn't loaded at module-init time; it's a native dep that
|
|
11
|
+
* we don't want to require for unit tests. The default factory below
|
|
12
|
+
* lazy-loads it on first PTY spawn, so non-PTY tests never touch it.
|
|
13
|
+
*/
|
|
14
|
+
export interface ProcessAdapter extends EventEmitter {
|
|
15
|
+
readonly pid: number | undefined;
|
|
16
|
+
write(data: string): void;
|
|
17
|
+
resize?(cols: number, rows: number): void;
|
|
18
|
+
kill(signal?: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Best-effort EOF on stdin. PTY mode sends ^D; pipe mode closes
|
|
21
|
+
* the writable side of the child's stdin stream.
|
|
22
|
+
*/
|
|
23
|
+
closeInput(): void;
|
|
24
|
+
}
|
|
25
|
+
export interface AdapterFactory {
|
|
26
|
+
spawnPty(opts: SpawnOptions): Promise<ProcessAdapter>;
|
|
27
|
+
spawnPipe(opts: SpawnOptions): Promise<ProcessAdapter>;
|
|
28
|
+
}
|
|
29
|
+
interface RunningProcess {
|
|
30
|
+
handle: ProcessHandle;
|
|
31
|
+
adapter: ProcessAdapter;
|
|
32
|
+
/** Output buffer; bytes accumulate here between read() calls. */
|
|
33
|
+
buffer: string;
|
|
34
|
+
/**
|
|
35
|
+
* Rolling tail of recent output, capped and NOT drained by read(). Used for
|
|
36
|
+
* non-destructive status snapshots (coding-agent status classification needs
|
|
37
|
+
* to see the current screen even after the agent has drained `buffer`).
|
|
38
|
+
*/
|
|
39
|
+
recentTail: string;
|
|
40
|
+
/** Last time we observed any output activity from the child. */
|
|
41
|
+
lastOutputAt: number;
|
|
42
|
+
/** Resolved when the child exits, with the final exit info. */
|
|
43
|
+
exitPromise: Promise<WaitResult>;
|
|
44
|
+
exitInfo: WaitResult | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Per-runner process orchestrator.
|
|
48
|
+
*
|
|
49
|
+
* Bookkeeping: every process is namespaced by workspaceId. Cross-
|
|
50
|
+
* workspace lookups error loudly (process_cross_workspace) — this is
|
|
51
|
+
* cheap insurance against an agent in workspace A handing a stale
|
|
52
|
+
* process_id to a tool that runs in workspace B.
|
|
53
|
+
*
|
|
54
|
+
* Buffering: stdout/stderr accumulate in a per-process string buffer.
|
|
55
|
+
* read() drains it; wait_for_idle() reads with idle-detection. We
|
|
56
|
+
* don't try to interpret encodings or framing; the runner's job is
|
|
57
|
+
* to deliver bytes faithfully, not to reason about what they mean.
|
|
58
|
+
*/
|
|
59
|
+
export declare class ProcessManager {
|
|
60
|
+
private readonly adapter;
|
|
61
|
+
private readonly maxConcurrent;
|
|
62
|
+
private readonly processes;
|
|
63
|
+
constructor(adapter: AdapterFactory, maxConcurrent: number);
|
|
64
|
+
inUse(): number;
|
|
65
|
+
list(workspaceId?: string): ProcessHandle[];
|
|
66
|
+
spawn(workspaceId: string, opts: SpawnOptions): Promise<ProcessHandle>;
|
|
67
|
+
/** Look up a process; enforces workspace ownership. */
|
|
68
|
+
getOrThrow(workspaceId: string, processId: string): RunningProcess;
|
|
69
|
+
write(workspaceId: string, processId: string, data: string): void;
|
|
70
|
+
/**
|
|
71
|
+
* Drain the output buffer. Returns whatever has accumulated since
|
|
72
|
+
* the last call; future calls return only what's new.
|
|
73
|
+
*/
|
|
74
|
+
read(workspaceId: string, processId: string): ReadResult;
|
|
75
|
+
/**
|
|
76
|
+
* Non-destructive peek at the recent output tail. Unlike read(), this does
|
|
77
|
+
* NOT drain anything — it's for status classification, which must work even
|
|
78
|
+
* after the agent has drained `buffer` via read(). Returns the rolling tail
|
|
79
|
+
* (capped at RECENT_TAIL_CAP) plus the process status and idle interval.
|
|
80
|
+
*/
|
|
81
|
+
snapshot(workspaceId: string, processId: string): {
|
|
82
|
+
tail: string;
|
|
83
|
+
binary: string;
|
|
84
|
+
status: ProcessStatus;
|
|
85
|
+
idleMs: number;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Subscribe to a process's live output and exit. Push-based counterpart to
|
|
89
|
+
* read(): the coding-session layer streams chunks to the backend as they
|
|
90
|
+
* arrive instead of polling. Does NOT drain the pull buffer — a caller can
|
|
91
|
+
* observe and read() the same process without losing bytes in either place.
|
|
92
|
+
* Returns an unsubscribe function.
|
|
93
|
+
*/
|
|
94
|
+
observe(workspaceId: string, processId: string, handlers: {
|
|
95
|
+
onData?: (chunk: string) => void;
|
|
96
|
+
onExit?: (info: WaitResult) => void;
|
|
97
|
+
}): () => void;
|
|
98
|
+
waitForIdle(workspaceId: string, processId: string, opts: WaitForIdleOptions): Promise<WaitForIdleResult>;
|
|
99
|
+
closeInput(workspaceId: string, processId: string): void;
|
|
100
|
+
signal(workspaceId: string, processId: string, sig: ProcessSignal): void;
|
|
101
|
+
wait(workspaceId: string, processId: string, timeoutMs?: number): Promise<WaitResult>;
|
|
102
|
+
/** Kill all processes for a workspace and forget them. Used on workspace release. */
|
|
103
|
+
killWorkspace(workspaceId: string): Promise<number>;
|
|
104
|
+
}
|
|
105
|
+
export declare function createDefaultAdapterFactory(): AdapterFactory;
|
|
106
|
+
export declare function shellExec(cmd: string, env?: Record<string, string>, timeoutMs?: number): Promise<ShellExecResult>;
|
|
107
|
+
export {};
|