toolcraft 0.0.12 → 0.0.14
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/node_modules/@poe-code/process-runner/README.md +41 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.js +40 -0
- package/node_modules/@poe-code/process-runner/dist/docker/context.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/docker/context.js +30 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.d.ts +28 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +428 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +131 -0
- package/node_modules/@poe-code/process-runner/dist/docker/engine.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/docker/engine.js +24 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.js +48 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +74 -0
- package/node_modules/@poe-code/process-runner/dist/index.d.ts +8 -0
- package/node_modules/@poe-code/process-runner/dist/index.js +7 -0
- package/node_modules/@poe-code/process-runner/dist/testing/index.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/testing/index.js +1 -0
- package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.js +115 -0
- package/node_modules/@poe-code/process-runner/dist/testing/verify.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/testing/verify.js +359 -0
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +180 -0
- package/node_modules/@poe-code/process-runner/dist/types.js +1 -0
- package/node_modules/@poe-code/process-runner/package.json +27 -0
- package/package.json +10 -4
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { strict as assert } from "node:assert";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { createDockerRunner, createHostRunner } from "../index.js";
|
|
7
|
+
const dockerPortHost = 18923;
|
|
8
|
+
async function verifyHostPiped() {
|
|
9
|
+
const runner = createHostRunner();
|
|
10
|
+
const handle = runner.exec({
|
|
11
|
+
command: "echo",
|
|
12
|
+
args: ["hello"],
|
|
13
|
+
stdout: "pipe",
|
|
14
|
+
stderr: "pipe"
|
|
15
|
+
});
|
|
16
|
+
const stdoutPromise = readStream(handle.stdout, "host piped stdout");
|
|
17
|
+
const { exitCode } = await handle.result;
|
|
18
|
+
const stdout = await stdoutPromise;
|
|
19
|
+
assert.equal(exitCode, 0, "host piped: exit code 0");
|
|
20
|
+
assert.equal(stdout.trim(), "hello", "host piped: stdout captured");
|
|
21
|
+
console.log("✓ host runner — piped mode");
|
|
22
|
+
}
|
|
23
|
+
async function verifyHostStdin() {
|
|
24
|
+
const runner = createHostRunner();
|
|
25
|
+
const handle = runner.exec({
|
|
26
|
+
command: "cat",
|
|
27
|
+
stdin: "pipe",
|
|
28
|
+
stdout: "pipe"
|
|
29
|
+
});
|
|
30
|
+
const stdoutPromise = readStream(handle.stdout, "host stdin stdout");
|
|
31
|
+
const stdin = assertWritable(handle.stdin, "host stdin: stdin available");
|
|
32
|
+
stdin.write("ping");
|
|
33
|
+
stdin.end();
|
|
34
|
+
const { exitCode } = await handle.result;
|
|
35
|
+
const stdout = await stdoutPromise;
|
|
36
|
+
assert.equal(exitCode, 0, "host stdin: exit code 0");
|
|
37
|
+
assert.equal(stdout, "ping", "host stdin: stdin echoed to stdout");
|
|
38
|
+
console.log("✓ host runner — stdin pipe");
|
|
39
|
+
}
|
|
40
|
+
async function verifyHostKill() {
|
|
41
|
+
const runner = createHostRunner();
|
|
42
|
+
const handle = runner.exec({
|
|
43
|
+
command: "sleep",
|
|
44
|
+
args: ["60"],
|
|
45
|
+
stdout: "pipe",
|
|
46
|
+
stderr: "pipe"
|
|
47
|
+
});
|
|
48
|
+
await delay(100);
|
|
49
|
+
handle.kill("SIGTERM");
|
|
50
|
+
const { exitCode } = await handle.result;
|
|
51
|
+
assert.notEqual(exitCode, 0, "host kill: non-zero exit after SIGTERM");
|
|
52
|
+
console.log("✓ host runner — kill");
|
|
53
|
+
}
|
|
54
|
+
async function verifyHostAbort() {
|
|
55
|
+
const runner = createHostRunner();
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const handle = runner.exec({
|
|
58
|
+
command: "sleep",
|
|
59
|
+
args: ["60"],
|
|
60
|
+
stdout: "pipe",
|
|
61
|
+
stderr: "pipe",
|
|
62
|
+
signal: controller.signal
|
|
63
|
+
});
|
|
64
|
+
await delay(100);
|
|
65
|
+
controller.abort();
|
|
66
|
+
const { exitCode } = await handle.result;
|
|
67
|
+
assert.notEqual(exitCode, 0, "host abort: non-zero exit after abort");
|
|
68
|
+
console.log("✓ host runner — abort signal");
|
|
69
|
+
}
|
|
70
|
+
async function verifyHostInherit() {
|
|
71
|
+
const runner = createHostRunner();
|
|
72
|
+
const handle = runner.exec({
|
|
73
|
+
command: "echo",
|
|
74
|
+
args: ["inherit-mode-output"],
|
|
75
|
+
stdin: "inherit",
|
|
76
|
+
stdout: "inherit",
|
|
77
|
+
stderr: "inherit"
|
|
78
|
+
});
|
|
79
|
+
assert.equal(handle.stdout, null, "host inherit: stdout is null");
|
|
80
|
+
assert.equal(handle.stderr, null, "host inherit: stderr is null");
|
|
81
|
+
assert.equal(handle.stdin, null, "host inherit: stdin is null");
|
|
82
|
+
const { exitCode } = await handle.result;
|
|
83
|
+
assert.equal(exitCode, 0, "host inherit: exit code 0");
|
|
84
|
+
console.log("✓ host runner — inherit mode");
|
|
85
|
+
}
|
|
86
|
+
async function verifyHostExitCode() {
|
|
87
|
+
const runner = createHostRunner();
|
|
88
|
+
const handle = runner.exec({
|
|
89
|
+
command: "sh",
|
|
90
|
+
args: ["-c", "exit 42"],
|
|
91
|
+
stdout: "pipe",
|
|
92
|
+
stderr: "pipe"
|
|
93
|
+
});
|
|
94
|
+
const { exitCode } = await handle.result;
|
|
95
|
+
assert.equal(exitCode, 42, "host exit code: 42");
|
|
96
|
+
console.log("✓ host runner — non-zero exit code");
|
|
97
|
+
}
|
|
98
|
+
async function verifyHostEnv() {
|
|
99
|
+
const runner = createHostRunner();
|
|
100
|
+
const handle = runner.exec({
|
|
101
|
+
command: "sh",
|
|
102
|
+
args: ["-c", "printf %s \"$MY_TEST_VAR\""],
|
|
103
|
+
stdout: "pipe",
|
|
104
|
+
env: { ...process.env, MY_TEST_VAR: "runner-works" }
|
|
105
|
+
});
|
|
106
|
+
const stdoutPromise = readStream(handle.stdout, "host env stdout");
|
|
107
|
+
const { exitCode } = await handle.result;
|
|
108
|
+
const stdout = await stdoutPromise;
|
|
109
|
+
assert.equal(exitCode, 0, "host env: exit code 0");
|
|
110
|
+
assert.equal(stdout, "runner-works", "host env: env var passed");
|
|
111
|
+
console.log("✓ host runner — env vars");
|
|
112
|
+
}
|
|
113
|
+
async function verifyDockerPiped(engine) {
|
|
114
|
+
const runner = createDockerRunner({ image: "alpine:latest", engine, context: "" });
|
|
115
|
+
const handle = runner.exec({
|
|
116
|
+
command: "echo",
|
|
117
|
+
args: ["hello from docker"],
|
|
118
|
+
stdout: "pipe",
|
|
119
|
+
stderr: "pipe"
|
|
120
|
+
});
|
|
121
|
+
const stdoutPromise = readStream(handle.stdout, "docker piped stdout");
|
|
122
|
+
const { exitCode } = await handle.result;
|
|
123
|
+
const stdout = await stdoutPromise;
|
|
124
|
+
assert.equal(exitCode, 0, "docker piped: exit code 0");
|
|
125
|
+
assert.equal(stdout.trim(), "hello from docker", "docker piped: stdout captured");
|
|
126
|
+
console.log("✓ docker runner — piped mode");
|
|
127
|
+
}
|
|
128
|
+
async function verifyDockerStdin(engine) {
|
|
129
|
+
const runner = createDockerRunner({ image: "alpine:latest", engine, context: "" });
|
|
130
|
+
const handle = runner.exec({
|
|
131
|
+
command: "cat",
|
|
132
|
+
stdin: "pipe",
|
|
133
|
+
stdout: "pipe"
|
|
134
|
+
});
|
|
135
|
+
const stdoutPromise = readStream(handle.stdout, "docker stdin stdout");
|
|
136
|
+
const stdin = assertWritable(handle.stdin, "docker stdin: stdin available");
|
|
137
|
+
stdin.write("docker-ping");
|
|
138
|
+
stdin.end();
|
|
139
|
+
const { exitCode } = await handle.result;
|
|
140
|
+
const stdout = await stdoutPromise;
|
|
141
|
+
assert.equal(exitCode, 0, "docker stdin: exit code 0");
|
|
142
|
+
assert.equal(stdout, "docker-ping", "docker stdin: stdin echoed to stdout");
|
|
143
|
+
console.log("✓ docker runner — stdin pipe");
|
|
144
|
+
}
|
|
145
|
+
async function verifyDockerKill(engine) {
|
|
146
|
+
const runner = createDockerRunner({ image: "alpine:latest", engine, context: "" });
|
|
147
|
+
const handle = runner.exec({
|
|
148
|
+
command: "sleep",
|
|
149
|
+
args: ["60"],
|
|
150
|
+
stdout: "pipe",
|
|
151
|
+
stderr: "pipe"
|
|
152
|
+
});
|
|
153
|
+
await delay(1000);
|
|
154
|
+
handle.kill("SIGTERM");
|
|
155
|
+
const { exitCode } = await handle.result;
|
|
156
|
+
assert.notEqual(exitCode, 0, "docker kill: non-zero exit after SIGTERM");
|
|
157
|
+
console.log("✓ docker runner — kill");
|
|
158
|
+
}
|
|
159
|
+
async function verifyDockerExitCode(engine) {
|
|
160
|
+
const runner = createDockerRunner({ image: "alpine:latest", engine, context: "" });
|
|
161
|
+
const handle = runner.exec({
|
|
162
|
+
command: "sh",
|
|
163
|
+
args: ["-c", "exit 42"],
|
|
164
|
+
stdout: "pipe",
|
|
165
|
+
stderr: "pipe"
|
|
166
|
+
});
|
|
167
|
+
const { exitCode } = await handle.result;
|
|
168
|
+
assert.equal(exitCode, 42, "docker exit code: 42");
|
|
169
|
+
console.log("✓ docker runner — non-zero exit code");
|
|
170
|
+
}
|
|
171
|
+
async function verifyDockerEnv(engine) {
|
|
172
|
+
const runner = createDockerRunner({ image: "alpine:latest", engine, context: "" });
|
|
173
|
+
const handle = runner.exec({
|
|
174
|
+
command: "sh",
|
|
175
|
+
args: ["-c", "printf %s \"$MY_TEST_VAR\""],
|
|
176
|
+
stdout: "pipe",
|
|
177
|
+
env: { MY_TEST_VAR: "docker-runner-works" }
|
|
178
|
+
});
|
|
179
|
+
const stdoutPromise = readStream(handle.stdout, "docker env stdout");
|
|
180
|
+
const { exitCode } = await handle.result;
|
|
181
|
+
const stdout = await stdoutPromise;
|
|
182
|
+
assert.equal(exitCode, 0, "docker env: exit code 0");
|
|
183
|
+
assert.equal(stdout, "docker-runner-works", "docker env: env var passed");
|
|
184
|
+
console.log("✓ docker runner — env vars");
|
|
185
|
+
}
|
|
186
|
+
async function verifyDockerMount(engine) {
|
|
187
|
+
const mountRoot = mkdtempSync(path.join(tmpdir(), "process-runner-verify-"));
|
|
188
|
+
const mountFile = path.join(mountRoot, "mounted.txt");
|
|
189
|
+
writeFileSync(mountFile, "mounted-ok\n", "utf8");
|
|
190
|
+
try {
|
|
191
|
+
const runner = createDockerRunner({
|
|
192
|
+
image: "alpine:latest",
|
|
193
|
+
engine,
|
|
194
|
+
context: "",
|
|
195
|
+
mounts: [{ source: mountRoot, target: "/host-data", readonly: true }]
|
|
196
|
+
});
|
|
197
|
+
const handle = runner.exec({
|
|
198
|
+
command: "cat",
|
|
199
|
+
args: ["/host-data/mounted.txt"],
|
|
200
|
+
stdout: "pipe",
|
|
201
|
+
stderr: "pipe"
|
|
202
|
+
});
|
|
203
|
+
const stdoutPromise = readStream(handle.stdout, "docker mount stdout");
|
|
204
|
+
const { exitCode } = await handle.result;
|
|
205
|
+
const stdout = await stdoutPromise;
|
|
206
|
+
assert.equal(exitCode, 0, "docker mount: exit code 0");
|
|
207
|
+
assert.equal(stdout.trim(), "mounted-ok", "docker mount: mounted file available");
|
|
208
|
+
console.log("✓ docker runner — bind mount");
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
rmSync(mountRoot, { recursive: true, force: true });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function verifyDockerPort(engine) {
|
|
215
|
+
const runner = createDockerRunner({
|
|
216
|
+
image: "alpine:latest",
|
|
217
|
+
engine,
|
|
218
|
+
context: "",
|
|
219
|
+
ports: [{ host: dockerPortHost, container: 8080 }]
|
|
220
|
+
});
|
|
221
|
+
const handle = runner.exec({
|
|
222
|
+
command: "sh",
|
|
223
|
+
args: [
|
|
224
|
+
"-c",
|
|
225
|
+
"mkdir -p /srv/http && printf ok > /srv/http/index.html && exec busybox httpd -f -p 8080 -h /srv/http"
|
|
226
|
+
],
|
|
227
|
+
stdout: "pipe",
|
|
228
|
+
stderr: "pipe"
|
|
229
|
+
});
|
|
230
|
+
try {
|
|
231
|
+
await waitForHttpOk(`http://127.0.0.1:${dockerPortHost}`);
|
|
232
|
+
console.log("✓ docker runner — port mapping");
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
handle.kill("SIGTERM");
|
|
236
|
+
await handle.result;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async function verifyDockerInteractiveContract(engine) {
|
|
240
|
+
const runner = createDockerRunner({ image: "alpine:latest", engine, context: "" });
|
|
241
|
+
const handle = runner.exec({
|
|
242
|
+
command: "sh",
|
|
243
|
+
args: [
|
|
244
|
+
"-c",
|
|
245
|
+
"if [ -t 0 ]; then printf stdin-is-tty; else printf stdin-not-tty; fi; printf '\\n'; if [ -t 1 ]; then printf stdout-is-tty; else printf stdout-not-tty; fi"
|
|
246
|
+
],
|
|
247
|
+
stdin: "pipe",
|
|
248
|
+
stdout: "pipe",
|
|
249
|
+
tty: true
|
|
250
|
+
});
|
|
251
|
+
const stdoutPromise = readStream(handle.stdout, "docker interactive stdout");
|
|
252
|
+
const stdin = assertWritable(handle.stdin, "docker interactive: stdin available");
|
|
253
|
+
stdin.end();
|
|
254
|
+
const { exitCode } = await handle.result;
|
|
255
|
+
const stdout = await stdoutPromise;
|
|
256
|
+
assert.equal(exitCode, 0, "docker interactive: exit code 0");
|
|
257
|
+
assert.ok(stdout.includes("stdin-is-tty"), "docker interactive: stdin is tty");
|
|
258
|
+
assert.ok(stdout.includes("stdout-is-tty"), "docker interactive: stdout is tty");
|
|
259
|
+
console.log("✓ docker runner — tty contract");
|
|
260
|
+
}
|
|
261
|
+
async function main() {
|
|
262
|
+
console.log("\n=== Host Runner ===\n");
|
|
263
|
+
await verifyHostPiped();
|
|
264
|
+
await verifyHostStdin();
|
|
265
|
+
await verifyHostKill();
|
|
266
|
+
await verifyHostAbort();
|
|
267
|
+
await verifyHostInherit();
|
|
268
|
+
await verifyHostExitCode();
|
|
269
|
+
await verifyHostEnv();
|
|
270
|
+
console.log("\n=== Docker Runner ===\n");
|
|
271
|
+
const engine = resolveAvailableEngine();
|
|
272
|
+
if (engine !== null) {
|
|
273
|
+
await verifyDockerPiped(engine);
|
|
274
|
+
await verifyDockerStdin(engine);
|
|
275
|
+
await verifyDockerKill(engine);
|
|
276
|
+
await verifyDockerExitCode(engine);
|
|
277
|
+
await verifyDockerEnv(engine);
|
|
278
|
+
await verifyDockerMount(engine);
|
|
279
|
+
await verifyDockerPort(engine);
|
|
280
|
+
await verifyDockerInteractiveContract(engine);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
console.log("⏭ Docker not available — skipping docker runner tests");
|
|
284
|
+
}
|
|
285
|
+
console.log("\n=== All verifications passed ===\n");
|
|
286
|
+
}
|
|
287
|
+
async function readStream(stream, label) {
|
|
288
|
+
const readable = assertReadable(stream, `${label}: stream is available`);
|
|
289
|
+
return await new Promise((resolve, reject) => {
|
|
290
|
+
let output = "";
|
|
291
|
+
readable.setEncoding("utf8");
|
|
292
|
+
readable.on("data", (chunk) => {
|
|
293
|
+
output += chunk;
|
|
294
|
+
});
|
|
295
|
+
readable.once("end", () => {
|
|
296
|
+
resolve(output);
|
|
297
|
+
});
|
|
298
|
+
readable.once("error", reject);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function assertReadable(stream, message) {
|
|
302
|
+
if (stream === null) {
|
|
303
|
+
assert.fail(message);
|
|
304
|
+
}
|
|
305
|
+
return stream;
|
|
306
|
+
}
|
|
307
|
+
function assertWritable(stream, message) {
|
|
308
|
+
if (stream === null) {
|
|
309
|
+
assert.fail(message);
|
|
310
|
+
}
|
|
311
|
+
return stream;
|
|
312
|
+
}
|
|
313
|
+
function resolveAvailableEngine() {
|
|
314
|
+
for (const engine of ["docker", "podman"]) {
|
|
315
|
+
if (!isEngineResponsive(engine, ["--version"])) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (isEngineResponsive(engine, ["info"])) {
|
|
319
|
+
return engine;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
function isEngineResponsive(engine, args) {
|
|
325
|
+
const result = spawnSync(engine, args, {
|
|
326
|
+
stdio: "ignore",
|
|
327
|
+
timeout: 2000
|
|
328
|
+
});
|
|
329
|
+
if (result.error !== undefined) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
return result.status === 0;
|
|
333
|
+
}
|
|
334
|
+
async function waitForHttpOk(url) {
|
|
335
|
+
const startedAt = Date.now();
|
|
336
|
+
while (Date.now() - startedAt < 15000) {
|
|
337
|
+
try {
|
|
338
|
+
const response = await fetch(url);
|
|
339
|
+
const body = await response.text();
|
|
340
|
+
if (response.ok && body.includes("ok")) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
void error;
|
|
346
|
+
}
|
|
347
|
+
await delay(250);
|
|
348
|
+
}
|
|
349
|
+
throw new Error(`Timed out waiting for ${url}`);
|
|
350
|
+
}
|
|
351
|
+
function delay(ms) {
|
|
352
|
+
return new Promise((resolve) => {
|
|
353
|
+
setTimeout(resolve, ms);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
main().catch((error) => {
|
|
357
|
+
console.error(error);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
export interface RunHandle {
|
|
3
|
+
readonly pid: number | null;
|
|
4
|
+
readonly stdout: Readable | null;
|
|
5
|
+
readonly stderr: Readable | null;
|
|
6
|
+
readonly stdin: Writable | null;
|
|
7
|
+
readonly result: Promise<RunResult>;
|
|
8
|
+
kill(signal?: NodeJS.Signals): void;
|
|
9
|
+
}
|
|
10
|
+
export interface RunResult {
|
|
11
|
+
exitCode: number;
|
|
12
|
+
}
|
|
13
|
+
export interface RunSpec {
|
|
14
|
+
command: string;
|
|
15
|
+
args?: string[];
|
|
16
|
+
cwd?: string;
|
|
17
|
+
env?: Record<string, string>;
|
|
18
|
+
stdin?: "pipe" | "inherit" | "ignore";
|
|
19
|
+
stdout?: "pipe" | "inherit";
|
|
20
|
+
stderr?: "pipe" | "inherit";
|
|
21
|
+
tty?: boolean;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
}
|
|
24
|
+
export interface Runner {
|
|
25
|
+
exec(spec: RunSpec): RunHandle;
|
|
26
|
+
readonly name: string;
|
|
27
|
+
}
|
|
28
|
+
export interface HostRunnerOptions {
|
|
29
|
+
detached?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export type ExecutionEnvType = "host" | "docker" | "e2b";
|
|
32
|
+
export type JobStatus = "running" | "exited" | "killed" | "lost";
|
|
33
|
+
export interface ExecutionEnvFactory {
|
|
34
|
+
readonly type: ExecutionEnvType;
|
|
35
|
+
readonly supportsDetach?: boolean;
|
|
36
|
+
open(spec: OpenSpec): Promise<OpenedEnv>;
|
|
37
|
+
attach(envId: string, context?: AttachedJobContext): Promise<OpenedEnv>;
|
|
38
|
+
}
|
|
39
|
+
export interface OpenSpec {
|
|
40
|
+
cwd: string;
|
|
41
|
+
runtime: unknown;
|
|
42
|
+
runner?: unknown;
|
|
43
|
+
state?: ExecutionState;
|
|
44
|
+
hostRunner?: Runner;
|
|
45
|
+
env: Record<string, string>;
|
|
46
|
+
uploadIgnoreFiles: string[];
|
|
47
|
+
jobLabel: {
|
|
48
|
+
tool: string;
|
|
49
|
+
argv: string[];
|
|
50
|
+
};
|
|
51
|
+
execution?: {
|
|
52
|
+
wrapForLogTee?: boolean;
|
|
53
|
+
stdin?: RunSpec["stdin"];
|
|
54
|
+
stdout?: RunSpec["stdout"];
|
|
55
|
+
stderr?: RunSpec["stderr"];
|
|
56
|
+
env?: RunSpec["env"];
|
|
57
|
+
tty?: boolean;
|
|
58
|
+
input?: string | Buffer;
|
|
59
|
+
captureOutput?: boolean;
|
|
60
|
+
activityTimeoutMs?: number;
|
|
61
|
+
onStdout?(chunk: string): void;
|
|
62
|
+
onStderr?(chunk: string): void;
|
|
63
|
+
};
|
|
64
|
+
shellSpec?: RunSpec;
|
|
65
|
+
}
|
|
66
|
+
export interface ExecutionState {
|
|
67
|
+
templates: {
|
|
68
|
+
get(backend: "docker" | "e2b", hash: string): Promise<TemplateEntry | null>;
|
|
69
|
+
put(backend: "docker" | "e2b", entry: TemplateEntry): Promise<void>;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export interface TemplateEntry {
|
|
73
|
+
hash: string;
|
|
74
|
+
template_id?: string;
|
|
75
|
+
image?: string;
|
|
76
|
+
runtime_type: string;
|
|
77
|
+
dockerfile_path: string;
|
|
78
|
+
built_at: string;
|
|
79
|
+
}
|
|
80
|
+
export interface UploadResult {
|
|
81
|
+
files: number;
|
|
82
|
+
bytes: number;
|
|
83
|
+
skipped: {
|
|
84
|
+
path: string;
|
|
85
|
+
bytes: number;
|
|
86
|
+
reason: "max_size";
|
|
87
|
+
}[];
|
|
88
|
+
}
|
|
89
|
+
export interface DownloadResult {
|
|
90
|
+
files: number;
|
|
91
|
+
bytes: number;
|
|
92
|
+
conflicts: {
|
|
93
|
+
path: string;
|
|
94
|
+
reason: "local_modified";
|
|
95
|
+
}[];
|
|
96
|
+
}
|
|
97
|
+
export interface LogChunk {
|
|
98
|
+
byteOffset: number;
|
|
99
|
+
data: string;
|
|
100
|
+
}
|
|
101
|
+
export interface AttachedJobContext {
|
|
102
|
+
jobId: string;
|
|
103
|
+
tool: string;
|
|
104
|
+
argv: string[];
|
|
105
|
+
cwd: string;
|
|
106
|
+
}
|
|
107
|
+
export interface OpenedEnv {
|
|
108
|
+
readonly id: string;
|
|
109
|
+
readonly job: JobHandle | null;
|
|
110
|
+
uploadWorkspace(): Promise<UploadResult>;
|
|
111
|
+
downloadWorkspace(opts: {
|
|
112
|
+
conflictPolicy: "refuse" | "overwrite";
|
|
113
|
+
}): Promise<DownloadResult>;
|
|
114
|
+
exec(spec: RunSpec): RunHandle;
|
|
115
|
+
detach(): Promise<JobHandle>;
|
|
116
|
+
shell(): RunHandle;
|
|
117
|
+
close(): Promise<void>;
|
|
118
|
+
}
|
|
119
|
+
export interface JobHandle {
|
|
120
|
+
readonly id: string;
|
|
121
|
+
readonly envId: string;
|
|
122
|
+
readonly tool: string;
|
|
123
|
+
readonly argv: string[];
|
|
124
|
+
status(): Promise<JobStatus>;
|
|
125
|
+
stream(opts?: {
|
|
126
|
+
sinceByte?: number;
|
|
127
|
+
since?: Date;
|
|
128
|
+
}): AsyncIterable<LogChunk>;
|
|
129
|
+
wait(): Promise<{
|
|
130
|
+
exitCode: number;
|
|
131
|
+
}>;
|
|
132
|
+
kill(signal?: NodeJS.Signals): Promise<void>;
|
|
133
|
+
}
|
|
134
|
+
export type Engine = "docker" | "podman";
|
|
135
|
+
export interface DockerMount {
|
|
136
|
+
source: string;
|
|
137
|
+
target: string;
|
|
138
|
+
readonly?: boolean;
|
|
139
|
+
}
|
|
140
|
+
export interface DockerPortMapping {
|
|
141
|
+
host: number;
|
|
142
|
+
container: number;
|
|
143
|
+
protocol?: "tcp" | "udp";
|
|
144
|
+
}
|
|
145
|
+
export interface DockerRunnerOptions {
|
|
146
|
+
image: string;
|
|
147
|
+
engine?: Engine;
|
|
148
|
+
context?: string;
|
|
149
|
+
mounts?: DockerMount[];
|
|
150
|
+
ports?: DockerPortMapping[];
|
|
151
|
+
network?: string;
|
|
152
|
+
extraArgs?: string[];
|
|
153
|
+
containerName?: string;
|
|
154
|
+
}
|
|
155
|
+
export interface DockerRunArgs {
|
|
156
|
+
engine: Engine;
|
|
157
|
+
context: string | null;
|
|
158
|
+
image: string;
|
|
159
|
+
command: string;
|
|
160
|
+
args: string[];
|
|
161
|
+
cwd?: string;
|
|
162
|
+
env?: Record<string, string>;
|
|
163
|
+
mounts: DockerMount[];
|
|
164
|
+
ports: DockerPortMapping[];
|
|
165
|
+
network?: string;
|
|
166
|
+
containerName: string;
|
|
167
|
+
detached: boolean;
|
|
168
|
+
interactive: boolean;
|
|
169
|
+
tty: boolean;
|
|
170
|
+
rm: boolean;
|
|
171
|
+
extraArgs: string[];
|
|
172
|
+
}
|
|
173
|
+
export interface MockRunBehavior {
|
|
174
|
+
pid?: number;
|
|
175
|
+
exitCode: number;
|
|
176
|
+
exitAfterMs?: number;
|
|
177
|
+
stdout?: string[];
|
|
178
|
+
stderr?: string[];
|
|
179
|
+
stdoutInterval?: number;
|
|
180
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@poe-code/process-runner",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./testing": {
|
|
14
|
+
"types": "./dist/testing/index.d.ts",
|
|
15
|
+
"import": "./dist/testing/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"test": "cd ../.. && vitest run --config vitest.config.ts packages/process-runner/src/**/*.test.ts",
|
|
21
|
+
"test:unit": "cd ../.. && vitest run --config vitest.config.ts packages/process-runner/src/**/*.test.ts",
|
|
22
|
+
"lint": "cd ../.. && eslint packages/process-runner/src --ext ts && tsc -p packages/process-runner/tsconfig.json --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
]
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"test": "cd ../.. && vitest run packages/toolcraft/src",
|
|
36
36
|
"test:unit": "cd ../.. && vitest run packages/toolcraft/src",
|
|
37
37
|
"lint": "cd ../.. && eslint packages/toolcraft/src --ext ts && tsc -p packages/toolcraft/tsconfig.json --noEmit",
|
|
38
|
-
"prepack": "node ../../scripts/manage-bundled-workspace-deps.mjs prepare . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations tiny-mcp-client mcp-oauth auth-store",
|
|
39
|
-
"postpack": "node ../../scripts/manage-bundled-workspace-deps.mjs cleanup . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations tiny-mcp-client mcp-oauth auth-store"
|
|
38
|
+
"prepack": "node ../../scripts/manage-bundled-workspace-deps.mjs prepare . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations @poe-code/process-runner tiny-mcp-client mcp-oauth auth-store",
|
|
39
|
+
"postpack": "node ../../scripts/manage-bundled-workspace-deps.mjs cleanup . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations @poe-code/process-runner tiny-mcp-client mcp-oauth auth-store"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@clack/core": "^1.0.0",
|
|
@@ -44,8 +44,12 @@
|
|
|
44
44
|
"chalk": "^5.6.2",
|
|
45
45
|
"commander": "^14.0.3",
|
|
46
46
|
"console-table-printer": "^2.15.0",
|
|
47
|
+
"jose": "^6.1.2",
|
|
48
|
+
"jsonc-parser": "^3.3.1",
|
|
49
|
+
"mustache": "^4.2.0",
|
|
50
|
+
"smol-toml": "^1.3.0",
|
|
47
51
|
"tiny-stdio-mcp-server": "^0.1.0",
|
|
48
|
-
"toolcraft-schema": "^0.0.
|
|
52
|
+
"toolcraft-schema": "^0.0.14",
|
|
49
53
|
"yaml": "^2.8.2"
|
|
50
54
|
},
|
|
51
55
|
"files": [
|
|
@@ -67,6 +71,7 @@
|
|
|
67
71
|
"@poe-code/file-lock",
|
|
68
72
|
"@poe-code/agent-defs",
|
|
69
73
|
"@poe-code/config-mutations",
|
|
74
|
+
"@poe-code/process-runner",
|
|
70
75
|
"tiny-mcp-client",
|
|
71
76
|
"mcp-oauth",
|
|
72
77
|
"auth-store"
|
|
@@ -78,6 +83,7 @@
|
|
|
78
83
|
"@poe-code/config-mutations": "*",
|
|
79
84
|
"@poe-code/design-system": "^0.0.1",
|
|
80
85
|
"@poe-code/file-lock": "*",
|
|
86
|
+
"@poe-code/process-runner": "*",
|
|
81
87
|
"@poe-code/task-list": "*",
|
|
82
88
|
"auth-store": "*",
|
|
83
89
|
"mcp-oauth": "*",
|