toolcraft 0.0.23 → 0.0.25
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 +2 -2
- package/dist/cli.compile-check.js +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +50 -13
- package/dist/error-report.js +32 -3
- package/dist/human-in-loop/approval-tasks.d.ts +1 -0
- package/dist/human-in-loop/approval-tasks.js +7 -5
- package/dist/human-in-loop/approvals-commands.js +51 -8
- package/dist/human-in-loop/runner.js +24 -19
- package/dist/human-in-loop/state-machine.d.ts +3 -3
- package/dist/human-in-loop/state-machine.js +13 -5
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -1
- package/dist/mcp-proxy.js +85 -19
- package/dist/mcp.compile-check.js +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +50 -8
- package/dist/renderer.js +119 -13
- package/dist/sdk.compile-check.js +1 -0
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.js +56 -11
- package/node_modules/@poe-code/agent-defs/dist/registry.d.ts +1 -1
- package/node_modules/@poe-code/agent-defs/dist/registry.js +22 -11
- package/node_modules/@poe-code/agent-defs/package.json +1 -1
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript-script.js +5 -1
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript.js +1 -1
- package/node_modules/@poe-code/agent-human-in-loop/package.json +1 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/apply.d.ts +1 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/apply.js +41 -92
- package/node_modules/@poe-code/agent-mcp-config/dist/configs.js +4 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/shapes.d.ts +14 -2
- package/node_modules/@poe-code/agent-mcp-config/dist/shapes.js +11 -4
- package/node_modules/@poe-code/agent-mcp-config/package.json +1 -1
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +200 -22
- package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.js +7 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/index.js +1 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/json.js +11 -7
- package/node_modules/@poe-code/config-mutations/dist/formats/object.d.ts +4 -0
- package/node_modules/@poe-code/config-mutations/dist/formats/object.js +27 -0
- package/node_modules/@poe-code/config-mutations/dist/formats/toml.js +12 -9
- package/node_modules/@poe-code/config-mutations/dist/formats/yaml.js +12 -9
- package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.d.ts +11 -1
- package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.js +10 -1
- package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.js +25 -1
- package/node_modules/@poe-code/config-mutations/dist/types.d.ts +12 -2
- package/node_modules/@poe-code/config-mutations/package.json +1 -1
- package/node_modules/@poe-code/design-system/dist/acp/components.js +3 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.js +6 -1
- package/node_modules/@poe-code/design-system/dist/components/color.js +9 -8
- package/node_modules/@poe-code/design-system/dist/components/command-errors.js +3 -2
- package/node_modules/@poe-code/design-system/dist/components/detail-card.d.ts +22 -0
- package/node_modules/@poe-code/design-system/dist/components/detail-card.js +69 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +88 -11
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
- package/node_modules/@poe-code/design-system/dist/components/table.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/components/table.js +82 -5
- package/node_modules/@poe-code/design-system/dist/components/template.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/components/template.js +198 -32
- package/node_modules/@poe-code/design-system/dist/components/text.js +29 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +2 -2
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +77 -32
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +28 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +45 -28
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.js +71 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +32 -10
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +3 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +57 -6
- package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.js +12 -15
- package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/design-system/dist/index.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +8 -5
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +1 -1
- package/node_modules/@poe-code/design-system/dist/static/menu.js +8 -2
- package/node_modules/@poe-code/design-system/dist/static/spinner.js +10 -4
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +9 -2
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +19 -2
- package/node_modules/@poe-code/design-system/package.json +2 -1
- package/node_modules/@poe-code/process-runner/dist/docker/args.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.js +11 -3
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +377 -130
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +78 -10
- package/node_modules/@poe-code/process-runner/dist/docker/env-file.d.ts +6 -0
- package/node_modules/@poe-code/process-runner/dist/docker/env-file.js +49 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.js +3 -2
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +21 -5
- package/node_modules/@poe-code/process-runner/dist/index.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/index.js +1 -0
- package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.js +30 -8
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +6 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.d.ts +61 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +503 -0
- package/node_modules/@poe-code/process-runner/package.json +1 -1
- package/node_modules/@poe-code/task-list/README.md +0 -2
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +3 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-sync.js +89 -59
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.d.ts +9 -3
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +460 -99
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +156 -154
- package/node_modules/@poe-code/task-list/dist/backends/utils.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/backends/utils.js +79 -0
- package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +120 -132
- package/node_modules/@poe-code/task-list/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/task-list/dist/index.js +2 -0
- package/node_modules/@poe-code/task-list/dist/move.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/move.js +215 -0
- package/node_modules/@poe-code/task-list/dist/open.js +3 -4
- package/node_modules/@poe-code/task-list/dist/state-machine.js +3 -1
- package/node_modules/@poe-code/task-list/dist/state.js +9 -0
- package/node_modules/@poe-code/task-list/dist/types.d.ts +48 -13
- package/node_modules/@poe-code/task-list/package.json +1 -2
- package/node_modules/auth-store/dist/create-secret-store.js +4 -1
- package/node_modules/auth-store/dist/encrypted-file-store.d.ts +8 -0
- package/node_modules/auth-store/dist/encrypted-file-store.js +104 -8
- package/node_modules/auth-store/dist/index.d.ts +1 -1
- package/node_modules/auth-store/dist/keychain-store.d.ts +4 -1
- package/node_modules/auth-store/dist/keychain-store.js +18 -16
- package/node_modules/auth-store/dist/provider-store.d.ts +5 -1
- package/node_modules/auth-store/dist/provider-store.js +55 -7
- package/node_modules/auth-store/dist/types.d.ts +3 -1
- package/node_modules/auth-store/package.json +2 -1
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +46 -15
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +49 -12
- package/node_modules/mcp-oauth/dist/client/token-endpoint.js +6 -1
- package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +1 -1
- package/node_modules/mcp-oauth/package.json +1 -0
- package/node_modules/tiny-mcp-client/.turbo/turbo-build.log +1 -1
- package/node_modules/tiny-mcp-client/dist/internal.d.ts +9 -4
- package/node_modules/tiny-mcp-client/dist/internal.js +244 -66
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.d.ts +1 -1
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.js +4 -7
- package/node_modules/tiny-mcp-client/package.json +2 -1
- package/node_modules/tiny-mcp-client/src/http-oauth.integration.test.ts +1 -1
- package/node_modules/tiny-mcp-client/src/http-oauth.test.ts +46 -0
- package/node_modules/tiny-mcp-client/src/internal.ts +287 -76
- package/node_modules/tiny-mcp-client/src/mcp-client-sdk.test.ts +32 -0
- package/node_modules/tiny-mcp-client/src/mcp-client-tiny-stdio-test-server-tools.test.ts +1 -1
- package/node_modules/tiny-mcp-client/src/oauth-discovery.ts +5 -10
- package/node_modules/tiny-mcp-client/src/transports.test.ts +588 -6
- package/package.json +10 -12
- package/node_modules/@poe-code/file-lock/README.md +0 -52
- package/node_modules/@poe-code/file-lock/dist/index.d.ts +0 -1
- package/node_modules/@poe-code/file-lock/dist/index.js +0 -1
- package/node_modules/@poe-code/file-lock/dist/lock.d.ts +0 -27
- package/node_modules/@poe-code/file-lock/dist/lock.js +0 -203
- package/node_modules/@poe-code/file-lock/package.json +0 -23
|
@@ -3,12 +3,25 @@ import { randomBytes } from "node:crypto";
|
|
|
3
3
|
import { buildDockerRunArgs } from "./args.js";
|
|
4
4
|
import { buildContextArgs, detectContext } from "./context.js";
|
|
5
5
|
import { detectEngine } from "./engine.js";
|
|
6
|
+
import { createDockerEnvFile } from "./env-file.js";
|
|
7
|
+
const DOCKER_ABORT_GRACE_MS = 10_000;
|
|
8
|
+
const DOCKER_ABORT_FORCE_GRACE_MS = 5_000;
|
|
6
9
|
export function createDockerRunner(options) {
|
|
7
10
|
const engine = options.engine ?? detectEngine();
|
|
8
11
|
const context = options.context ?? detectContext();
|
|
9
12
|
return {
|
|
10
13
|
name: "docker",
|
|
11
14
|
exec(spec) {
|
|
15
|
+
if (spec.signal?.aborted === true) {
|
|
16
|
+
return {
|
|
17
|
+
pid: null,
|
|
18
|
+
stdin: null,
|
|
19
|
+
stdout: null,
|
|
20
|
+
stderr: null,
|
|
21
|
+
result: Promise.resolve({ exitCode: 1 }),
|
|
22
|
+
kill() { }
|
|
23
|
+
};
|
|
24
|
+
}
|
|
12
25
|
const stdinMode = spec.stdin ?? "ignore";
|
|
13
26
|
const stdoutMode = spec.stdout ?? "pipe";
|
|
14
27
|
const stderrMode = spec.stderr ?? "pipe";
|
|
@@ -17,6 +30,7 @@ export function createDockerRunner(options) {
|
|
|
17
30
|
stderrMode === "inherit" &&
|
|
18
31
|
spec.tty === true;
|
|
19
32
|
const containerName = buildContainerName(options.containerName ?? spec.command);
|
|
33
|
+
const envFile = createDockerEnvFile(spec.env);
|
|
20
34
|
const runArgs = buildDockerRunArgs({
|
|
21
35
|
engine,
|
|
22
36
|
context,
|
|
@@ -25,6 +39,7 @@ export function createDockerRunner(options) {
|
|
|
25
39
|
args: spec.args ?? [],
|
|
26
40
|
cwd: spec.cwd,
|
|
27
41
|
env: spec.env,
|
|
42
|
+
envFilePath: envFile?.path,
|
|
28
43
|
mounts: options.mounts ?? [],
|
|
29
44
|
ports: options.ports ?? [],
|
|
30
45
|
network: options.network,
|
|
@@ -36,25 +51,56 @@ export function createDockerRunner(options) {
|
|
|
36
51
|
extraArgs: options.extraArgs ?? []
|
|
37
52
|
});
|
|
38
53
|
const [command, ...args] = runArgs;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
let child;
|
|
55
|
+
try {
|
|
56
|
+
child = childProcess.spawn(command, args, {
|
|
57
|
+
stdio: interactiveMode ? "inherit" : [stdinMode, stdoutMode, stderrMode]
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
envFile?.cleanup();
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
42
64
|
let isResultSettled = false;
|
|
65
|
+
let exitCodeOverride = null;
|
|
43
66
|
let resolveResult = null;
|
|
67
|
+
let abortEscalationTimers = [];
|
|
44
68
|
const result = new Promise((resolve) => {
|
|
45
69
|
resolveResult = resolve;
|
|
46
70
|
});
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
71
|
+
const clearAbortEscalation = () => {
|
|
72
|
+
for (const timer of abortEscalationTimers) {
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
}
|
|
75
|
+
abortEscalationTimers = [];
|
|
76
|
+
};
|
|
50
77
|
const settleResult = (exitCode) => {
|
|
51
78
|
if (isResultSettled) {
|
|
52
79
|
return;
|
|
53
80
|
}
|
|
54
81
|
isResultSettled = true;
|
|
55
82
|
cleanupAbort();
|
|
56
|
-
|
|
83
|
+
clearAbortEscalation();
|
|
84
|
+
envFile?.cleanup();
|
|
85
|
+
resolveResult?.({ exitCode: exitCodeOverride ?? exitCode });
|
|
86
|
+
};
|
|
87
|
+
const scheduleAbortEscalation = () => {
|
|
88
|
+
const terminateTimer = setTimeout(() => {
|
|
89
|
+
killHostDockerChild(child, "SIGTERM");
|
|
90
|
+
const forceTimer = setTimeout(() => {
|
|
91
|
+
killHostDockerChild(child, "SIGKILL");
|
|
92
|
+
}, DOCKER_ABORT_FORCE_GRACE_MS);
|
|
93
|
+
unrefTimer(forceTimer);
|
|
94
|
+
abortEscalationTimers.push(forceTimer);
|
|
95
|
+
}, DOCKER_ABORT_GRACE_MS);
|
|
96
|
+
unrefTimer(terminateTimer);
|
|
97
|
+
abortEscalationTimers.push(terminateTimer);
|
|
57
98
|
};
|
|
99
|
+
const cleanupAbort = bindAbortSignal(spec.signal, () => {
|
|
100
|
+
exitCodeOverride = 1;
|
|
101
|
+
spawnControlCommand(engine, context, ["stop", containerName]);
|
|
102
|
+
scheduleAbortEscalation();
|
|
103
|
+
});
|
|
58
104
|
child.once("error", () => {
|
|
59
105
|
settleResult(1);
|
|
60
106
|
});
|
|
@@ -82,6 +128,14 @@ export function createDockerRunner(options) {
|
|
|
82
128
|
}
|
|
83
129
|
};
|
|
84
130
|
}
|
|
131
|
+
function killHostDockerChild(child, signal) {
|
|
132
|
+
try {
|
|
133
|
+
child.kill(signal);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
85
139
|
function buildContainerName(name) {
|
|
86
140
|
const suffix = randomBytes(3).toString("hex").slice(0, 6);
|
|
87
141
|
const sanitizedName = sanitizeContainerName(name);
|
|
@@ -112,9 +166,15 @@ function isContainerNameCharacter(char) {
|
|
|
112
166
|
return char === "." || char === "_" || char === "-";
|
|
113
167
|
}
|
|
114
168
|
function spawnControlCommand(engine, context, args) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
169
|
+
try {
|
|
170
|
+
const child = childProcess.spawn(engine, [...buildContextArgs(engine, context), ...args], {
|
|
171
|
+
stdio: "ignore"
|
|
172
|
+
});
|
|
173
|
+
child.once("error", () => undefined);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
118
178
|
}
|
|
119
179
|
function bindAbortSignal(signal, onAbort) {
|
|
120
180
|
if (signal === undefined) {
|
|
@@ -129,3 +189,11 @@ function bindAbortSignal(signal, onAbort) {
|
|
|
129
189
|
signal.removeEventListener("abort", onAbort);
|
|
130
190
|
};
|
|
131
191
|
}
|
|
192
|
+
function unrefTimer(timer) {
|
|
193
|
+
if (typeof timer === "object" &&
|
|
194
|
+
timer !== null &&
|
|
195
|
+
"unref" in timer &&
|
|
196
|
+
typeof timer.unref === "function") {
|
|
197
|
+
timer.unref();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface DockerEnvFile {
|
|
2
|
+
path: string;
|
|
3
|
+
cleanup(): void;
|
|
4
|
+
}
|
|
5
|
+
export declare function createDockerEnvFile(env: Record<string, string> | undefined): DockerEnvFile | null;
|
|
6
|
+
export declare function serializeDockerEnvFile(entries: Array<[string, string]>): string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export function createDockerEnvFile(env) {
|
|
5
|
+
const entries = Object.entries(env ?? {});
|
|
6
|
+
if (entries.length === 0) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const directory = mkdtempSync(path.join(tmpdir(), "poe-docker-env-"));
|
|
10
|
+
const filePath = path.join(directory, "env");
|
|
11
|
+
let active = true;
|
|
12
|
+
try {
|
|
13
|
+
writeFileSync(filePath, serializeDockerEnvFile(entries), {
|
|
14
|
+
encoding: "utf8",
|
|
15
|
+
flag: "wx",
|
|
16
|
+
mode: 0o600
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
rmSync(directory, { recursive: true, force: true });
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
path: filePath,
|
|
25
|
+
cleanup() {
|
|
26
|
+
if (!active) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
active = false;
|
|
30
|
+
rmSync(directory, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function serializeDockerEnvFile(entries) {
|
|
35
|
+
return (entries.map(([key, value]) => `${formatDockerEnvKey(key)}=${formatDockerEnvValue(value)}`).join("\n") +
|
|
36
|
+
"\n");
|
|
37
|
+
}
|
|
38
|
+
function formatDockerEnvKey(key) {
|
|
39
|
+
if (key.length === 0 || key.includes("=") || key.includes("\n") || key.includes("\r")) {
|
|
40
|
+
throw new Error(`Invalid Docker environment variable name: ${JSON.stringify(key)}`);
|
|
41
|
+
}
|
|
42
|
+
return key;
|
|
43
|
+
}
|
|
44
|
+
function formatDockerEnvValue(value) {
|
|
45
|
+
if (value.includes("\n") || value.includes("\r")) {
|
|
46
|
+
throw new Error("Docker env-file values cannot contain newline characters.");
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
@@ -31,12 +31,13 @@ export const hostExecutionEnvFactory = {
|
|
|
31
31
|
return createHostRunner().exec({
|
|
32
32
|
command: shellSpec?.command ?? openSpec.env.SHELL ?? process.env.SHELL ?? "sh",
|
|
33
33
|
...(shellSpec?.args ? { args: shellSpec.args } : {}),
|
|
34
|
-
cwd: openSpec.cwd,
|
|
34
|
+
cwd: shellSpec?.cwd ?? openSpec.cwd,
|
|
35
35
|
env: shellSpec && "env" in shellSpec ? shellSpec.env : openSpec.env,
|
|
36
36
|
stdin: "inherit",
|
|
37
37
|
stdout: "inherit",
|
|
38
38
|
stderr: "inherit",
|
|
39
|
-
tty: true
|
|
39
|
+
tty: true,
|
|
40
|
+
signal: shellSpec?.signal
|
|
40
41
|
});
|
|
41
42
|
},
|
|
42
43
|
async close() { }
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import { spawn as spawnChildProcess } from "node:child_process";
|
|
2
2
|
export function createHostRunner(options = {}) {
|
|
3
|
-
const
|
|
3
|
+
const detachedByDefault = options.detached === true;
|
|
4
4
|
return {
|
|
5
5
|
name: "host",
|
|
6
6
|
exec(spec) {
|
|
7
|
+
if (spec.signal?.aborted === true) {
|
|
8
|
+
return {
|
|
9
|
+
pid: null,
|
|
10
|
+
stdin: null,
|
|
11
|
+
stdout: null,
|
|
12
|
+
stderr: null,
|
|
13
|
+
result: Promise.resolve({ exitCode: 1 }),
|
|
14
|
+
kill() { }
|
|
15
|
+
};
|
|
16
|
+
}
|
|
7
17
|
const stdinMode = spec.stdin ?? "ignore";
|
|
8
18
|
const stdoutMode = spec.stdout ?? "pipe";
|
|
9
19
|
const stderrMode = spec.stderr ?? "pipe";
|
|
20
|
+
const killProcessGroup = detachedByDefault || spec.killProcessGroup === true;
|
|
10
21
|
const stdio = stdinMode === "inherit" && stdoutMode === "inherit" && stderrMode === "inherit"
|
|
11
22
|
? "inherit"
|
|
12
23
|
: [stdinMode, stdoutMode, stderrMode];
|
|
@@ -14,13 +25,13 @@ export function createHostRunner(options = {}) {
|
|
|
14
25
|
cwd: spec.cwd,
|
|
15
26
|
env: spec.env,
|
|
16
27
|
stdio,
|
|
17
|
-
...(
|
|
28
|
+
...(killProcessGroup ? { detached: true } : {})
|
|
18
29
|
});
|
|
19
|
-
if (
|
|
30
|
+
if (killProcessGroup) {
|
|
20
31
|
child.unref();
|
|
21
32
|
}
|
|
22
33
|
const kill = (signal) => {
|
|
23
|
-
if (
|
|
34
|
+
if (killProcessGroup && process.platform !== "win32" && child.pid !== undefined) {
|
|
24
35
|
process.kill(-child.pid, signal);
|
|
25
36
|
return;
|
|
26
37
|
}
|
|
@@ -32,7 +43,12 @@ export function createHostRunner(options = {}) {
|
|
|
32
43
|
resolveResult = resolve;
|
|
33
44
|
});
|
|
34
45
|
const cleanupAbort = bindAbortSignal(spec.signal, () => {
|
|
35
|
-
|
|
46
|
+
try {
|
|
47
|
+
kill("SIGTERM");
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
36
52
|
});
|
|
37
53
|
child.once("close", (code) => {
|
|
38
54
|
if (settled)
|
|
@@ -5,4 +5,5 @@ export { buildDockerRuntimeTemplate, dockerExecutionEnvFactory } from "./docker/
|
|
|
5
5
|
export { hostExecutionEnvFactory } from "./host/host-execution-env.js";
|
|
6
6
|
export { createHostRunner } from "./host/host-runner.js";
|
|
7
7
|
export { createMockRunner, createMockRunnerByCommand } from "./testing/index.js";
|
|
8
|
+
export { downloadWorkspace, uploadWorkspace, type WorkspaceDownloadOptions, type WorkspaceTransferDirent, type WorkspaceTransferEnv, type WorkspaceTransferFileSystem, type WorkspaceTransferOptions, type WorkspaceTransferRunnerOptions, type WorkspaceTransferStats } from "./workspace-transfer.js";
|
|
8
9
|
export type { DownloadResult, DockerMount, DockerPortMapping, DockerRunArgs, DockerRunnerOptions, Engine, ExecutionState, ExecutionEnvFactory, ExecutionEnvType, HostRunnerOptions, JobHandle, JobStatus, LogChunk, MockRunBehavior, OpenedEnv, OpenSpec, RunHandle, RunResult, Runner, RunSpec, TemplateEntry, UploadResult } from "./types.js";
|
|
@@ -5,3 +5,4 @@ export { buildDockerRuntimeTemplate, dockerExecutionEnvFactory } from "./docker/
|
|
|
5
5
|
export { hostExecutionEnvFactory } from "./host/host-execution-env.js";
|
|
6
6
|
export { createHostRunner } from "./host/host-runner.js";
|
|
7
7
|
export { createMockRunner, createMockRunnerByCommand } from "./testing/index.js";
|
|
8
|
+
export { downloadWorkspace, uploadWorkspace } from "./workspace-transfer.js";
|
|
@@ -16,7 +16,9 @@ export function createMockRunnerByCommand(behaviorsByCommand) {
|
|
|
16
16
|
return {
|
|
17
17
|
name: "mock",
|
|
18
18
|
exec(spec) {
|
|
19
|
-
const behavior = behaviorsByCommand
|
|
19
|
+
const behavior = Object.prototype.hasOwnProperty.call(behaviorsByCommand, spec.command)
|
|
20
|
+
? behaviorsByCommand[spec.command]
|
|
21
|
+
: undefined;
|
|
20
22
|
if (behavior === undefined) {
|
|
21
23
|
throw new Error(`No mock run behavior found for command "${spec.command}"`);
|
|
22
24
|
}
|
|
@@ -29,12 +31,19 @@ function createRunHandle(spec, behavior) {
|
|
|
29
31
|
const stderrMode = spec.stderr ?? "pipe";
|
|
30
32
|
const stdinMode = spec.stdin ?? "ignore";
|
|
31
33
|
const interval = behavior.stdoutInterval ?? 10;
|
|
34
|
+
if (behavior.exitAfterMs !== undefined && (!Number.isFinite(behavior.exitAfterMs) || behavior.exitAfterMs < 0)) {
|
|
35
|
+
throw new Error("Mock run exitAfterMs must be a finite non-negative number.");
|
|
36
|
+
}
|
|
32
37
|
const stdoutController = stdoutMode === "pipe" && behavior.stdout !== undefined
|
|
33
38
|
? createReadableStream(behavior.stdout, interval)
|
|
34
39
|
: null;
|
|
35
40
|
const stderrController = stderrMode === "pipe" && behavior.stderr !== undefined
|
|
36
41
|
? createReadableStream(behavior.stderr, interval)
|
|
37
42
|
: null;
|
|
43
|
+
const outputDone = Promise.all([
|
|
44
|
+
...(stdoutController === null ? [] : [stdoutController.done]),
|
|
45
|
+
...(stderrController === null ? [] : [stderrController.done])
|
|
46
|
+
]);
|
|
38
47
|
let resolveResult = null;
|
|
39
48
|
const result = new Promise((resolve) => {
|
|
40
49
|
resolveResult = resolve;
|
|
@@ -51,10 +60,18 @@ function createRunHandle(spec, behavior) {
|
|
|
51
60
|
stdoutController?.stop();
|
|
52
61
|
stderrController?.stop();
|
|
53
62
|
};
|
|
54
|
-
const exitAfterMs = behavior.exitAfterMs
|
|
55
|
-
const exitTimer = exitAfterMs
|
|
56
|
-
?
|
|
57
|
-
:
|
|
63
|
+
const exitAfterMs = behavior.exitAfterMs;
|
|
64
|
+
const exitTimer = exitAfterMs === undefined
|
|
65
|
+
? undefined
|
|
66
|
+
: exitAfterMs > 0
|
|
67
|
+
? setTimeout(complete, exitAfterMs)
|
|
68
|
+
: undefined;
|
|
69
|
+
if (exitAfterMs === undefined) {
|
|
70
|
+
void outputDone.then(complete);
|
|
71
|
+
}
|
|
72
|
+
else if (exitAfterMs === 0) {
|
|
73
|
+
queueMicrotask(complete);
|
|
74
|
+
}
|
|
58
75
|
return {
|
|
59
76
|
pid: behavior.pid ?? null,
|
|
60
77
|
stdout: stdoutController?.stream ?? null,
|
|
@@ -62,7 +79,7 @@ function createRunHandle(spec, behavior) {
|
|
|
62
79
|
stdin: stdinMode === "pipe" ? createWritableStream() : null,
|
|
63
80
|
result,
|
|
64
81
|
kill() {
|
|
65
|
-
if (
|
|
82
|
+
if (exitTimer !== undefined) {
|
|
66
83
|
clearTimeout(exitTimer);
|
|
67
84
|
}
|
|
68
85
|
stopStreams();
|
|
@@ -76,6 +93,10 @@ function createReadableStream(lines, interval) {
|
|
|
76
93
|
});
|
|
77
94
|
const timers = new Set();
|
|
78
95
|
let stopped = false;
|
|
96
|
+
let resolveDone;
|
|
97
|
+
const done = new Promise((resolve) => {
|
|
98
|
+
resolveDone = resolve;
|
|
99
|
+
});
|
|
79
100
|
const stop = () => {
|
|
80
101
|
if (stopped) {
|
|
81
102
|
return;
|
|
@@ -86,10 +107,11 @@ function createReadableStream(lines, interval) {
|
|
|
86
107
|
}
|
|
87
108
|
timers.clear();
|
|
88
109
|
stream.push(null);
|
|
110
|
+
resolveDone?.();
|
|
89
111
|
};
|
|
90
112
|
if (lines.length === 0) {
|
|
91
113
|
queueMicrotask(stop);
|
|
92
|
-
return { stream, stop };
|
|
114
|
+
return { done, stream, stop };
|
|
93
115
|
}
|
|
94
116
|
for (const [index, line] of lines.entries()) {
|
|
95
117
|
const timer = setTimeout(() => {
|
|
@@ -104,7 +126,7 @@ function createReadableStream(lines, interval) {
|
|
|
104
126
|
}, interval * (index + 1));
|
|
105
127
|
timers.add(timer);
|
|
106
128
|
}
|
|
107
|
-
return { stream, stop };
|
|
129
|
+
return { done, stream, stop };
|
|
108
130
|
}
|
|
109
131
|
function createWritableStream() {
|
|
110
132
|
return new Writable({
|
|
@@ -20,6 +20,8 @@ export interface RunSpec {
|
|
|
20
20
|
stderr?: "pipe" | "inherit";
|
|
21
21
|
tty?: boolean;
|
|
22
22
|
signal?: AbortSignal;
|
|
23
|
+
/** Start in a separate process group so kill() can signal the full group where supported. */
|
|
24
|
+
killProcessGroup?: boolean;
|
|
23
25
|
}
|
|
24
26
|
export interface Runner {
|
|
25
27
|
exec(spec: RunSpec): RunHandle;
|
|
@@ -103,10 +105,12 @@ export interface AttachedJobContext {
|
|
|
103
105
|
tool: string;
|
|
104
106
|
argv: string[];
|
|
105
107
|
cwd: string;
|
|
108
|
+
reattachContext?: Record<string, unknown>;
|
|
106
109
|
}
|
|
107
110
|
export interface OpenedEnv {
|
|
108
111
|
readonly id: string;
|
|
109
112
|
readonly job: JobHandle | null;
|
|
113
|
+
readonly reattachContext?: Record<string, unknown>;
|
|
110
114
|
uploadWorkspace(): Promise<UploadResult>;
|
|
111
115
|
downloadWorkspace(opts: {
|
|
112
116
|
conflictPolicy: "refuse" | "overwrite";
|
|
@@ -125,6 +129,7 @@ export interface JobHandle {
|
|
|
125
129
|
stream(opts?: {
|
|
126
130
|
sinceByte?: number;
|
|
127
131
|
since?: Date;
|
|
132
|
+
follow?: boolean;
|
|
128
133
|
}): AsyncIterable<LogChunk>;
|
|
129
134
|
wait(): Promise<{
|
|
130
135
|
exitCode: number;
|
|
@@ -160,6 +165,7 @@ export interface DockerRunArgs {
|
|
|
160
165
|
args: string[];
|
|
161
166
|
cwd?: string;
|
|
162
167
|
env?: Record<string, string>;
|
|
168
|
+
envFilePath?: string;
|
|
163
169
|
mounts: DockerMount[];
|
|
164
170
|
ports: DockerPortMapping[];
|
|
165
171
|
network?: string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { DownloadResult, UploadResult } from "./types.js";
|
|
2
|
+
export type { DownloadResult, UploadResult } from "./types.js";
|
|
3
|
+
export interface WorkspaceTransferDirent {
|
|
4
|
+
name: string;
|
|
5
|
+
isFile(): boolean;
|
|
6
|
+
isDirectory(): boolean;
|
|
7
|
+
isSymbolicLink?(): boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface WorkspaceTransferStats {
|
|
10
|
+
isFile(): boolean;
|
|
11
|
+
isDirectory(): boolean;
|
|
12
|
+
isSymbolicLink?(): boolean;
|
|
13
|
+
size: number;
|
|
14
|
+
}
|
|
15
|
+
export interface WorkspaceTransferFileSystem {
|
|
16
|
+
mkdir(path: string, options?: {
|
|
17
|
+
recursive?: boolean;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
readdir(path: string, options: {
|
|
20
|
+
withFileTypes: true;
|
|
21
|
+
}): Promise<WorkspaceTransferDirent[]>;
|
|
22
|
+
readFile(path: string): Promise<Buffer>;
|
|
23
|
+
readFile(path: string, encoding: BufferEncoding): Promise<string>;
|
|
24
|
+
writeFile(path: string, data: string | Buffer, options?: {
|
|
25
|
+
flag?: string;
|
|
26
|
+
mode?: number;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
stat(path: string): Promise<WorkspaceTransferStats>;
|
|
29
|
+
lstat?(path: string): Promise<WorkspaceTransferStats>;
|
|
30
|
+
rename?(oldPath: string, newPath: string): Promise<void>;
|
|
31
|
+
rm?(path: string, options?: {
|
|
32
|
+
recursive?: boolean;
|
|
33
|
+
force?: boolean;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
unlink?(path: string): Promise<void>;
|
|
36
|
+
rmdir?(path: string): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export interface WorkspaceTransferEnv {
|
|
39
|
+
cwd: string;
|
|
40
|
+
uploadDir: string;
|
|
41
|
+
workspaceDir?: string;
|
|
42
|
+
fs?: WorkspaceTransferFileSystem;
|
|
43
|
+
remoteFs?: WorkspaceTransferFileSystem;
|
|
44
|
+
}
|
|
45
|
+
export interface WorkspaceTransferOptions {
|
|
46
|
+
runner?: WorkspaceTransferRunnerOptions;
|
|
47
|
+
uploadMaxFileMb?: number;
|
|
48
|
+
workspaceExclude?: string[];
|
|
49
|
+
warn?: (message: string) => void;
|
|
50
|
+
}
|
|
51
|
+
export interface WorkspaceTransferRunnerOptions {
|
|
52
|
+
upload_max_file_mb?: number;
|
|
53
|
+
workspace?: {
|
|
54
|
+
exclude?: string[];
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface WorkspaceDownloadOptions {
|
|
58
|
+
conflictPolicy: "refuse" | "overwrite";
|
|
59
|
+
}
|
|
60
|
+
export declare function uploadWorkspace(env: WorkspaceTransferEnv, opts: WorkspaceTransferOptions): Promise<UploadResult>;
|
|
61
|
+
export declare function downloadWorkspace(env: WorkspaceTransferEnv, opts: WorkspaceDownloadOptions): Promise<DownloadResult>;
|