@ai-hero/sandcastle 0.4.5 → 0.4.6
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 +35 -19
- package/dist/AgentProvider.d.ts +7 -2
- package/dist/AgentProvider.d.ts.map +1 -1
- package/dist/AgentProvider.js +28 -15
- package/dist/AgentProvider.js.map +1 -1
- package/dist/{CopyToSandbox.d.ts → CopyToWorkspace.d.ts} +2 -2
- package/dist/CopyToWorkspace.d.ts.map +1 -0
- package/dist/{CopyToSandbox.js → CopyToWorkspace.js} +2 -2
- package/dist/CopyToWorkspace.js.map +1 -0
- package/dist/ErrorHandler.d.ts.map +1 -1
- package/dist/ErrorHandler.js +3 -0
- package/dist/ErrorHandler.js.map +1 -1
- package/dist/InitService.d.ts +24 -0
- package/dist/InitService.d.ts.map +1 -1
- package/dist/InitService.js +117 -11
- package/dist/InitService.js.map +1 -1
- package/dist/Orchestrator.d.ts.map +1 -1
- package/dist/Orchestrator.js +4 -1
- package/dist/Orchestrator.js.map +1 -1
- package/dist/PodmanLifecycle.d.ts +17 -0
- package/dist/PodmanLifecycle.d.ts.map +1 -0
- package/dist/PodmanLifecycle.js +45 -0
- package/dist/PodmanLifecycle.js.map +1 -0
- package/dist/PromptArgumentSubstitution.d.ts +5 -0
- package/dist/PromptArgumentSubstitution.d.ts.map +1 -1
- package/dist/PromptArgumentSubstitution.js +22 -0
- package/dist/PromptArgumentSubstitution.js.map +1 -1
- package/dist/PromptPreprocessor.d.ts.map +1 -1
- package/dist/PromptPreprocessor.js +6 -0
- package/dist/PromptPreprocessor.js.map +1 -1
- package/dist/SandboxFactory.d.ts +3 -3
- package/dist/SandboxFactory.d.ts.map +1 -1
- package/dist/SandboxFactory.js +3 -3
- package/dist/SandboxFactory.js.map +1 -1
- package/dist/SandboxLifecycle.d.ts.map +1 -1
- package/dist/SandboxLifecycle.js +7 -7
- package/dist/SandboxLifecycle.js.map +1 -1
- package/dist/SandboxProvider.d.ts +73 -1
- package/dist/SandboxProvider.d.ts.map +1 -1
- package/dist/SandboxProvider.js.map +1 -1
- package/dist/cli.d.ts +10 -6
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +113 -113
- package/dist/cli.js.map +1 -1
- package/dist/createSandbox.d.ts +23 -1
- package/dist/createSandbox.d.ts.map +1 -1
- package/dist/createSandbox.js +63 -6
- package/dist/createSandbox.js.map +1 -1
- package/dist/errors.d.ts +9 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interactive.d.ts +52 -0
- package/dist/interactive.d.ts.map +1 -0
- package/dist/interactive.js +231 -0
- package/dist/interactive.js.map +1 -0
- package/dist/run.d.ts +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +5 -5
- package/dist/run.js.map +1 -1
- package/dist/sandboxes/docker.d.ts.map +1 -1
- package/dist/sandboxes/docker.js +26 -1
- package/dist/sandboxes/docker.js.map +1 -1
- package/dist/sandboxes/no-sandbox.d.ts +25 -0
- package/dist/sandboxes/no-sandbox.d.ts.map +1 -0
- package/dist/sandboxes/no-sandbox.js +91 -0
- package/dist/sandboxes/no-sandbox.js.map +1 -0
- package/dist/sandboxes/podman.d.ts +10 -2
- package/dist/sandboxes/podman.d.ts.map +1 -1
- package/dist/sandboxes/podman.js +79 -11
- package/dist/sandboxes/podman.js.map +1 -1
- package/dist/templates/blank/prompt.md +1 -1
- package/dist/templates/parallel-planner/implement-prompt.md +2 -2
- package/dist/templates/parallel-planner/main.mts +12 -12
- package/dist/templates/parallel-planner/plan-prompt.md +3 -3
- package/dist/templates/parallel-planner-with-review/implement-prompt.md +2 -2
- package/dist/templates/parallel-planner-with-review/main.mts +11 -11
- package/dist/templates/parallel-planner-with-review/plan-prompt.md +3 -3
- package/dist/templates/sequential-reviewer/implement-prompt.md +2 -2
- package/dist/templates/sequential-reviewer/main.mts +3 -3
- package/dist/templates/simple-loop/main.mts +2 -2
- package/dist/templates/simple-loop/prompt.md +2 -2
- package/package.json +8 -2
- package/dist/CopyToSandbox.d.ts.map +0 -1
- package/dist/CopyToSandbox.js.map +0 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* No-sandbox provider — runs the agent directly on the host with no container isolation.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { noSandbox } from "sandcastle/sandboxes/no-sandbox";
|
|
6
|
+
* await interactive({ agent: claudeCode("claude-opus-4-6"), sandbox: noSandbox() });
|
|
7
|
+
*
|
|
8
|
+
* Only valid for `interactive()` — not accepted by `run()` or `createSandbox()`.
|
|
9
|
+
* Does not pass `--dangerously-skip-permissions` to the agent — the user manages
|
|
10
|
+
* permissions themselves.
|
|
11
|
+
*/
|
|
12
|
+
import type { NoSandboxProvider } from "../SandboxProvider.js";
|
|
13
|
+
export interface NoSandboxOptions {
|
|
14
|
+
/** Environment variables injected by this provider. Merged at launch time. */
|
|
15
|
+
readonly env?: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a no-sandbox provider.
|
|
19
|
+
*
|
|
20
|
+
* The returned provider runs the agent directly on the host. All three
|
|
21
|
+
* branch strategies are supported (head, merge-to-head, branch),
|
|
22
|
+
* defaulting to head.
|
|
23
|
+
*/
|
|
24
|
+
export declare const noSandbox: (options?: NoSandboxOptions | undefined) => NoSandboxProvider;
|
|
25
|
+
//# sourceMappingURL=no-sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-sandbox.d.ts","sourceRoot":"","sources":["../../src/sandboxes/no-sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EACV,iBAAiB,EAIlB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,+DA2FpB,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* No-sandbox provider — runs the agent directly on the host with no container isolation.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { noSandbox } from "sandcastle/sandboxes/no-sandbox";
|
|
6
|
+
* await interactive({ agent: claudeCode("claude-opus-4-6"), sandbox: noSandbox() });
|
|
7
|
+
*
|
|
8
|
+
* Only valid for `interactive()` — not accepted by `run()` or `createSandbox()`.
|
|
9
|
+
* Does not pass `--dangerously-skip-permissions` to the agent — the user manages
|
|
10
|
+
* permissions themselves.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from "node:child_process";
|
|
13
|
+
import { createInterface } from "node:readline";
|
|
14
|
+
/**
|
|
15
|
+
* Create a no-sandbox provider.
|
|
16
|
+
*
|
|
17
|
+
* The returned provider runs the agent directly on the host. All three
|
|
18
|
+
* branch strategies are supported (head, merge-to-head, branch),
|
|
19
|
+
* defaulting to head.
|
|
20
|
+
*/
|
|
21
|
+
export const noSandbox = (options) => ({
|
|
22
|
+
tag: "none",
|
|
23
|
+
name: "no-sandbox",
|
|
24
|
+
env: options?.env ?? {},
|
|
25
|
+
create: async (createOptions) => {
|
|
26
|
+
const workspacePath = createOptions.workspacePath;
|
|
27
|
+
const processEnv = { ...process.env, ...createOptions.env };
|
|
28
|
+
const handle = {
|
|
29
|
+
workspacePath,
|
|
30
|
+
exec: (command, opts) => {
|
|
31
|
+
// sudo is a no-op for no-sandbox — the user is already on the host
|
|
32
|
+
const cwd = opts?.cwd ?? workspacePath;
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const proc = spawn("sh", ["-c", command], {
|
|
35
|
+
cwd,
|
|
36
|
+
env: processEnv,
|
|
37
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
38
|
+
});
|
|
39
|
+
const stdoutChunks = [];
|
|
40
|
+
const stderrChunks = [];
|
|
41
|
+
if (opts?.onLine) {
|
|
42
|
+
const rl = createInterface({ input: proc.stdout });
|
|
43
|
+
rl.on("line", (line) => {
|
|
44
|
+
stdoutChunks.push(line);
|
|
45
|
+
opts.onLine(line);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
proc.stdout.on("data", (chunk) => {
|
|
50
|
+
stdoutChunks.push(chunk.toString());
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
proc.stderr.on("data", (chunk) => {
|
|
54
|
+
stderrChunks.push(chunk.toString());
|
|
55
|
+
});
|
|
56
|
+
proc.on("error", (error) => {
|
|
57
|
+
reject(new Error(`exec failed: ${error.message}`));
|
|
58
|
+
});
|
|
59
|
+
proc.on("close", (code) => {
|
|
60
|
+
resolve({
|
|
61
|
+
stdout: stdoutChunks.join(opts?.onLine ? "\n" : ""),
|
|
62
|
+
stderr: stderrChunks.join(""),
|
|
63
|
+
exitCode: code ?? 0,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
interactiveExec: (args, opts) => {
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const [cmd, ...rest] = args;
|
|
71
|
+
const proc = spawn(cmd, rest, {
|
|
72
|
+
cwd: opts.cwd ?? workspacePath,
|
|
73
|
+
env: processEnv,
|
|
74
|
+
stdio: [opts.stdin, opts.stdout, opts.stderr],
|
|
75
|
+
});
|
|
76
|
+
proc.on("error", (error) => {
|
|
77
|
+
reject(new Error(`exec failed: ${error.message}`));
|
|
78
|
+
});
|
|
79
|
+
proc.on("close", (code) => {
|
|
80
|
+
resolve({ exitCode: code ?? 0 });
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
close: async () => {
|
|
85
|
+
// No-op — no container to tear down
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
return handle;
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=no-sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-sandbox.js","sourceRoot":"","sources":["../../src/sandboxes/no-sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,14 @@ export interface PodmanOptions {
|
|
|
18
18
|
* - `false` — disable labeling entirely.
|
|
19
19
|
*/
|
|
20
20
|
readonly selinuxLabel?: "z" | "Z" | false;
|
|
21
|
+
/**
|
|
22
|
+
* User namespace mode for rootless Podman.
|
|
23
|
+
*
|
|
24
|
+
* - `"keep-id"` (default) — maps host UID 1:1 into the container,
|
|
25
|
+
* so bind-mounted files have correct ownership. Required for rootless Podman.
|
|
26
|
+
* - `false` — disable; use for rootful Podman setups.
|
|
27
|
+
*/
|
|
28
|
+
readonly userns?: "keep-id" | false;
|
|
21
29
|
/**
|
|
22
30
|
* Additional host directories to bind-mount into the sandbox.
|
|
23
31
|
*
|
|
@@ -33,8 +41,8 @@ export interface PodmanOptions {
|
|
|
33
41
|
*
|
|
34
42
|
* The returned provider creates Podman containers with bind-mounts
|
|
35
43
|
* for the worktree and git directories. Calls the `podman` binary
|
|
36
|
-
* on PATH directly
|
|
37
|
-
*
|
|
44
|
+
* on PATH directly. On macOS/Windows, verifies that a Podman Machine
|
|
45
|
+
* is running before container creation.
|
|
38
46
|
*/
|
|
39
47
|
export declare const podman: (options?: PodmanOptions | undefined) => SandboxProvider;
|
|
40
48
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"podman.d.ts","sourceRoot":"","sources":["../../src/sandboxes/podman.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"podman.d.ts","sourceRoot":"","sources":["../../src/sandboxes/podman.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,EAEL,KAAK,eAAe,EAKrB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC;IAC1C;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,uHAAuH;IACvH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,0DA2NlB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,6BAI5B,CAAC"}
|
package/dist/sandboxes/podman.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* import { podman } from "sandcastle/sandboxes/podman";
|
|
6
6
|
* await run({ agent: claudeCode("claude-opus-4-6"), sandbox: podman() });
|
|
7
7
|
*/
|
|
8
|
-
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
8
|
+
import { execFile, execFileSync, spawn, } from "node:child_process";
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
10
|
import { createInterface } from "node:readline";
|
|
11
11
|
import { createBindMountSandboxProvider, } from "../SandboxProvider.js";
|
|
@@ -16,12 +16,13 @@ import { homedir } from "node:os";
|
|
|
16
16
|
*
|
|
17
17
|
* The returned provider creates Podman containers with bind-mounts
|
|
18
18
|
* for the worktree and git directories. Calls the `podman` binary
|
|
19
|
-
* on PATH directly
|
|
20
|
-
*
|
|
19
|
+
* on PATH directly. On macOS/Windows, verifies that a Podman Machine
|
|
20
|
+
* is running before container creation.
|
|
21
21
|
*/
|
|
22
22
|
export const podman = (options) => {
|
|
23
23
|
const configuredImageName = options?.imageName;
|
|
24
24
|
const selinuxLabel = options?.selinuxLabel ?? "z";
|
|
25
|
+
const userns = options?.userns ?? "keep-id";
|
|
25
26
|
const userMounts = options?.mounts ? resolveUserMounts(options.mounts) : [];
|
|
26
27
|
return createBindMountSandboxProvider({
|
|
27
28
|
name: "podman",
|
|
@@ -30,16 +31,16 @@ export const podman = (options) => {
|
|
|
30
31
|
const containerName = `sandcastle-${randomUUID()}`;
|
|
31
32
|
const workspacePath = createOptions.mounts.find((m) => m.hostPath === createOptions.worktreePath)?.sandboxPath ?? "/home/agent/workspace";
|
|
32
33
|
// Build volume mount strings with optional SELinux label (internal + user mounts)
|
|
33
|
-
const labelSuffix = selinuxLabel ? `:${selinuxLabel}` : "";
|
|
34
34
|
const allMounts = [...createOptions.mounts, ...userMounts];
|
|
35
|
-
const volumeMounts = allMounts.map((m) =>
|
|
36
|
-
const base = `${m.hostPath}:${m.sandboxPath}`;
|
|
37
|
-
if (m.readonly)
|
|
38
|
-
return `${base}:ro${labelSuffix}`;
|
|
39
|
-
return `${base}${labelSuffix}`;
|
|
40
|
-
});
|
|
35
|
+
const volumeMounts = allMounts.map((m) => formatVolumeMount(m, selinuxLabel));
|
|
41
36
|
// Resolve image name
|
|
42
37
|
const imageName = configuredImageName ?? defaultImageName(createOptions.hostRepoPath);
|
|
38
|
+
// Pre-flight: check Podman Machine on macOS/Windows
|
|
39
|
+
if (process.platform === "darwin" || process.platform === "win32") {
|
|
40
|
+
await checkPodmanMachine();
|
|
41
|
+
}
|
|
42
|
+
// Pre-flight: verify image exists locally
|
|
43
|
+
await checkImageExists(imageName);
|
|
43
44
|
const hostUid = process.getuid?.() ?? 1000;
|
|
44
45
|
const hostGid = process.getgid?.() ?? 1000;
|
|
45
46
|
const env = { ...createOptions.env, HOME: "/home/agent" };
|
|
@@ -48,6 +49,7 @@ export const podman = (options) => {
|
|
|
48
49
|
`${key}=${value}`,
|
|
49
50
|
]);
|
|
50
51
|
const volumeArgs = volumeMounts.flatMap((v) => ["-v", v]);
|
|
52
|
+
const usernsArgs = userns ? [`--userns=${userns}`] : [];
|
|
51
53
|
// Start container via podman run
|
|
52
54
|
await new Promise((resolve, reject) => {
|
|
53
55
|
execFile("podman", [
|
|
@@ -57,12 +59,14 @@ export const podman = (options) => {
|
|
|
57
59
|
containerName,
|
|
58
60
|
"--user",
|
|
59
61
|
`${hostUid}:${hostGid}`,
|
|
62
|
+
...usernsArgs,
|
|
60
63
|
"-w",
|
|
61
64
|
workspacePath,
|
|
62
65
|
...envArgs,
|
|
63
66
|
...volumeArgs,
|
|
64
|
-
|
|
67
|
+
"--entrypoint",
|
|
65
68
|
"sleep",
|
|
69
|
+
imageName,
|
|
66
70
|
"infinity",
|
|
67
71
|
], (error) => {
|
|
68
72
|
if (error) {
|
|
@@ -78,6 +82,7 @@ export const podman = (options) => {
|
|
|
78
82
|
try {
|
|
79
83
|
execFileSync("podman", ["rm", "-f", containerName], {
|
|
80
84
|
stdio: "ignore",
|
|
85
|
+
timeout: 5000,
|
|
81
86
|
});
|
|
82
87
|
}
|
|
83
88
|
catch {
|
|
@@ -142,6 +147,31 @@ export const podman = (options) => {
|
|
|
142
147
|
});
|
|
143
148
|
});
|
|
144
149
|
},
|
|
150
|
+
interactiveExec: (args, opts) => {
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
const podmanArgs = ["exec"];
|
|
153
|
+
// Allocate a pseudo-terminal when stdin looks like a TTY
|
|
154
|
+
if ("isTTY" in opts.stdin &&
|
|
155
|
+
opts.stdin.isTTY) {
|
|
156
|
+
podmanArgs.push("-it");
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
podmanArgs.push("-i");
|
|
160
|
+
}
|
|
161
|
+
if (opts.cwd)
|
|
162
|
+
podmanArgs.push("-w", opts.cwd);
|
|
163
|
+
podmanArgs.push(containerName, ...args);
|
|
164
|
+
const proc = spawn("podman", podmanArgs, {
|
|
165
|
+
stdio: [opts.stdin, opts.stdout, opts.stderr],
|
|
166
|
+
});
|
|
167
|
+
proc.on("error", (error) => {
|
|
168
|
+
reject(new Error(`podman exec failed: ${error.message}`));
|
|
169
|
+
});
|
|
170
|
+
proc.on("close", (code) => {
|
|
171
|
+
resolve({ exitCode: code ?? 0 });
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
},
|
|
145
175
|
close: async () => {
|
|
146
176
|
process.removeListener("exit", onExit);
|
|
147
177
|
process.removeListener("SIGINT", onSignal);
|
|
@@ -193,4 +223,42 @@ const resolveUserMounts = (mounts) => mounts.map((m) => {
|
|
|
193
223
|
...(m.readonly ? { readonly: true } : {}),
|
|
194
224
|
};
|
|
195
225
|
});
|
|
226
|
+
const checkImageExists = (imageName) => new Promise((resolve, reject) => {
|
|
227
|
+
execFile("podman", ["image", "inspect", imageName], (error) => {
|
|
228
|
+
if (error) {
|
|
229
|
+
reject(new Error(`Image '${imageName}' not found locally. Build it first with 'podman build -t ${imageName} .'`));
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
resolve();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
const podmanMachineError = () => new Error("Podman Machine is not running. Run 'podman machine init && podman machine start' first.");
|
|
237
|
+
const checkPodmanMachine = () => new Promise((resolve, reject) => {
|
|
238
|
+
execFile("podman", ["machine", "list", "--format", "json"], (error, stdout) => {
|
|
239
|
+
if (error) {
|
|
240
|
+
reject(podmanMachineError());
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const machines = JSON.parse(stdout.toString());
|
|
245
|
+
if (machines.some((m) => m.Running)) {
|
|
246
|
+
resolve();
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
reject(podmanMachineError());
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
reject(podmanMachineError());
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
const formatVolumeMount = (mount, selinuxLabel) => {
|
|
258
|
+
const base = `${mount.hostPath}:${mount.sandboxPath}`;
|
|
259
|
+
const options = [mount.readonly ? "ro" : undefined, selinuxLabel || undefined]
|
|
260
|
+
.filter((option) => option !== undefined)
|
|
261
|
+
.join(",");
|
|
262
|
+
return options ? `${base}:${options}` : base;
|
|
263
|
+
};
|
|
196
264
|
//# sourceMappingURL=podman.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"podman.js","sourceRoot":"","sources":["../../src/sandboxes/podman.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"podman.js","sourceRoot":"","sources":["../../src/sandboxes/podman.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,KAAK,GAEN,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EACL,8BAA8B,GAM/B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAiClC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAuB,EAAmB,EAAE;IACjE,MAAM,mBAAmB,GAAG,OAAO,EAAE,SAAS,CAAC;IAC/C,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,GAAG,CAAC;IAClD,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,SAAS,CAAC;IAC5C,MAAM,UAAU,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE5E,OAAO,8BAA8B,CAAC;QACpC,IAAI,EAAE,QAAQ;QACd,GAAG,EAAE,OAAO,EAAE,GAAG;QACjB,MAAM,EAAE,KAAK,EACX,aAAqC,EACJ,EAAE;YACnC,MAAM,aAAa,GAAG,cAAc,UAAU,EAAE,EAAE,CAAC;YAEnD,MAAM,aAAa,GACjB,aAAa,CAAC,MAAM,CAAC,IAAI,CACvB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,aAAa,CAAC,YAAY,CACjD,EAAE,WAAW,IAAI,uBAAuB,CAAC;YAE5C,kFAAkF;YAClF,MAAM,SAAS,GAAG,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC;YAC3D,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACvC,iBAAiB,CAAC,CAAC,EAAE,YAAY,CAAC,CACnC,CAAC;YAEF,qBAAqB;YACrB,MAAM,SAAS,GACb,mBAAmB,IAAI,gBAAgB,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;YAEtE,oDAAoD;YACpD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;gBAClE,MAAM,kBAAkB,EAAE,CAAC;YAC7B,CAAC;YAED,0CAA0C;YAC1C,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAElC,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,IAAI,CAAC;YAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,IAAI,CAAC;YAE3C,MAAM,GAAG,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;YAC1D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;gBAC5D,IAAI;gBACJ,GAAG,GAAG,IAAI,KAAK,EAAE;aAClB,CAAC,CAAC;YACH,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1D,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAExD,iCAAiC;YACjC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,QAAQ,CACN,QAAQ,EACR;oBACE,KAAK;oBACL,IAAI;oBACJ,QAAQ;oBACR,aAAa;oBACb,QAAQ;oBACR,GAAG,OAAO,IAAI,OAAO,EAAE;oBACvB,GAAG,UAAU;oBACb,IAAI;oBACJ,aAAa;oBACb,GAAG,OAAO;oBACV,GAAG,UAAU;oBACb,cAAc;oBACd,OAAO;oBACP,SAAS;oBACT,UAAU;iBACX,EACD,CAAC,KAAK,EAAE,EAAE;oBACR,IAAI,KAAK,EAAE,CAAC;wBACV,MAAM,CAAC,IAAI,KAAK,CAAC,sBAAsB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;oBAC3D,CAAC;yBAAM,CAAC;wBACN,OAAO,EAAE,CAAC;oBACZ,CAAC;gBACH,CAAC,CACF,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,qCAAqC;YACrC,MAAM,MAAM,GAAG,GAAG,EAAE;gBAClB,IAAI,CAAC;oBACH,YAAY,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,CAAC,EAAE;wBAClD,KAAK,EAAE,QAAQ;wBACf,OAAO,EAAE,IAAI;qBACd,CAAC,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACP,iBAAiB;gBACnB,CAAC;YACH,CAAC,CAAC;YACF,MAAM,QAAQ,GAAG,GAAG,EAAE;gBACpB,MAAM,EAAE,CAAC;gBACT,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC;YACF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3B,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAEhC,MAAM,MAAM,GAA2B;gBACrC,aAAa;gBAEb,IAAI,EAAE,CACJ,OAAe,EACf,IAIC,EACoB,EAAE;oBACvB,MAAM,gBAAgB,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;oBAClE,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;oBACtB,IAAI,IAAI,EAAE,GAAG;wBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;oBACzC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC;oBAEvD,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;wBACjB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;wBAC3B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;4BACrC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE;gCACjC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;6BAClC,CAAC,CAAC;4BAEH,MAAM,YAAY,GAAa,EAAE,CAAC;4BAClC,MAAM,YAAY,GAAa,EAAE,CAAC;4BAElC,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAO,EAAE,CAAC,CAAC;4BACpD,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gCACrB,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gCACxB,MAAM,CAAC,IAAI,CAAC,CAAC;4BACf,CAAC,CAAC,CAAC;4BAEH,IAAI,CAAC,MAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gCACxC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;4BACtC,CAAC,CAAC,CAAC;4BAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gCACzB,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;4BAC5D,CAAC,CAAC,CAAC;4BAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gCACxB,OAAO,CAAC;oCACN,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;oCAC/B,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;oCAC7B,QAAQ,EAAE,IAAI,IAAI,CAAC;iCACpB,CAAC,CAAC;4BACL,CAAC,CAAC,CAAC;wBACL,CAAC,CAAC,CAAC;oBACL,CAAC;oBAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;wBACrC,QAAQ,CACN,QAAQ,EACR,IAAI,EACJ,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EAC/B,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;4BACxB,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gCACtC,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;4BAC5D,CAAC;iCAAM,CAAC;gCACN,OAAO,CAAC;oCACN,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;oCACzB,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;oCACzB,QAAQ,EAAE,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;iCAC3D,CAAC,CAAC;4BACL,CAAC;wBACH,CAAC,CACF,CAAC;oBACJ,CAAC,CAAC,CAAC;gBACL,CAAC;gBAED,eAAe,EAAE,CACf,IAAc,EACd,IAA4B,EACG,EAAE;oBACjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;wBACrC,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,CAAC;wBAC5B,yDAAyD;wBACzD,IACE,OAAO,IAAI,IAAI,CAAC,KAAK;4BACpB,IAAI,CAAC,KAA6B,CAAC,KAAK,EACzC,CAAC;4BACD,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;wBACzB,CAAC;6BAAM,CAAC;4BACN,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBACxB,CAAC;wBACD,IAAI,IAAI,CAAC,GAAG;4BAAE,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;wBAC9C,UAAU,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAAC;wBAExC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,EAAE;4BACvC,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAiB;yBAC9D,CAAC,CAAC;wBAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;4BAChC,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;wBAC5D,CAAC,CAAC,CAAC;wBAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAmB,EAAE,EAAE;4BACvC,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;wBACnC,CAAC,CAAC,CAAC;oBACL,CAAC,CAAC,CAAC;gBACL,CAAC;gBAED,KAAK,EAAE,KAAK,IAAmB,EAAE;oBAC/B,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;oBACvC,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;oBAC3C,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;oBAC5C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;wBAC1C,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;4BACxD,IAAI,KAAK,EAAE,CAAC;gCACV,MAAM,CAAC,IAAI,KAAK,CAAC,qBAAqB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;4BAC1D,CAAC;iCAAM,CAAC;gCACN,OAAO,EAAE,CAAC;4BACZ,CAAC;wBACH,CAAC,CAAC,CAAC;oBACL,CAAC,CAAC,CAAC;gBACL,CAAC;aACF,CAAC;YAEF,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAe,EAAU,EAAE;IAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,OAAO,CAAC;IACxE,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;IACtE,OAAO,cAAc,SAAS,EAAE,CAAC;AACnC,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,CAAS,EAAU,EAAE;IACxC,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,OAAO,EAAE,CAAC;IAChC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CACxB,MAA8B,EACwC,EAAE,CACxE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;IACf,MAAM,gBAAgB,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAEjD,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,kCAAkC,CAAC,CAAC,QAAQ,EAAE;YAC5C,CAAC,CAAC,CAAC,QAAQ,KAAK,gBAAgB;gBAC9B,CAAC,CAAC,iBAAiB,gBAAgB,GAAG;gBACtC,CAAC,CAAC,EAAE,CAAC,CACV,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,gBAAgB;QAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1C,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL,MAAM,gBAAgB,GAAG,CAAC,SAAiB,EAAiB,EAAE,CAC5D,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IACpC,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;QAC5D,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CACJ,IAAI,KAAK,CACP,UAAU,SAAS,6DAA6D,SAAS,KAAK,CAC/F,CACF,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,MAAM,kBAAkB,GAAG,GAAG,EAAE,CAC9B,IAAI,KAAK,CACP,yFAAyF,CAC1F,CAAC;AAEJ,MAAM,kBAAkB,GAAG,GAAkB,EAAE,CAC7C,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IACpC,QAAQ,CACN,QAAQ,EACR,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,EACvC,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QAChB,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC;YAC7B,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAE3C,CAAC;YACH,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpC,OAAO,EAAE,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL,MAAM,iBAAiB,GAAG,CACxB,KAAoE,EACpE,YAA2C,EACnC,EAAE;IACV,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;IACtD,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,IAAI,SAAS,CAAC;SAC3E,MAAM,CAAC,CAAC,MAAM,EAAoB,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC;SAC1D,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Context
|
|
2
2
|
|
|
3
3
|
<!-- Use !`command` to pull in dynamic context. Commands run inside the sandbox. -->
|
|
4
|
-
<!-- Example: !`git log --oneline -10` or !`
|
|
4
|
+
<!-- Example: !`git log --oneline -10` or !`{{LIST_TASKS_COMMAND}}` -->
|
|
5
5
|
|
|
6
6
|
# Task
|
|
7
7
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# TASK
|
|
2
2
|
|
|
3
|
-
Fix issue
|
|
3
|
+
Fix issue {{TASK_ID}}: {{ISSUE_TITLE}}
|
|
4
4
|
|
|
5
|
-
Pull in the issue using `
|
|
5
|
+
Pull in the issue using `{{VIEW_TASK_COMMAND}}`. If it has a parent PRD, pull that in too.
|
|
6
6
|
|
|
7
7
|
Only work on the issue specified.
|
|
8
8
|
|
|
@@ -36,7 +36,7 @@ const hooks = {
|
|
|
36
36
|
// Copy node_modules from the host into the worktree before each sandbox
|
|
37
37
|
// starts. Avoids a full npm install from scratch; the hook above handles
|
|
38
38
|
// platform-specific binaries and any packages added since the last copy.
|
|
39
|
-
const
|
|
39
|
+
const copyToWorkspace = ["node_modules"];
|
|
40
40
|
|
|
41
41
|
// ---------------------------------------------------------------------------
|
|
42
42
|
// Main loop
|
|
@@ -56,7 +56,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
56
56
|
// -------------------------------------------------------------------------
|
|
57
57
|
const plan = await sandcastle.run({
|
|
58
58
|
hooks,
|
|
59
|
-
|
|
59
|
+
copyToWorkspace,
|
|
60
60
|
sandbox: docker(),
|
|
61
61
|
branchStrategy: { type: "merge-to-head" },
|
|
62
62
|
name: "planner",
|
|
@@ -76,9 +76,9 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// The plan JSON contains an array of issues, each with
|
|
79
|
+
// The plan JSON contains an array of issues, each with id, title, branch.
|
|
80
80
|
const { issues } = JSON.parse(planMatch[1]!) as {
|
|
81
|
-
issues: {
|
|
81
|
+
issues: { id: string; title: string; branch: string }[];
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
if (issues.length === 0) {
|
|
@@ -91,7 +91,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
91
91
|
`Planning complete. ${issues.length} issue(s) to work in parallel:`,
|
|
92
92
|
);
|
|
93
93
|
for (const issue of issues) {
|
|
94
|
-
console.log(`
|
|
94
|
+
console.log(` ${issue.id}: ${issue.title} → ${issue.branch}`);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
// -------------------------------------------------------------------------
|
|
@@ -107,7 +107,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
107
107
|
issues.map((issue) =>
|
|
108
108
|
sandcastle.run({
|
|
109
109
|
hooks,
|
|
110
|
-
|
|
110
|
+
copyToWorkspace,
|
|
111
111
|
// Each agent starts on its own branch via branchStrategy on run().
|
|
112
112
|
sandbox: docker(),
|
|
113
113
|
branchStrategy: { type: "branch", branch: issue.branch },
|
|
@@ -117,11 +117,11 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
117
117
|
// Sonnet for execution: fast and capable enough for typical issue work.
|
|
118
118
|
agent: sandcastle.claudeCode("claude-sonnet-4-6"),
|
|
119
119
|
promptFile: "./.sandcastle/implement-prompt.md",
|
|
120
|
-
// Prompt arguments substitute {{
|
|
120
|
+
// Prompt arguments substitute {{TASK_ID}}, {{ISSUE_TITLE}},
|
|
121
121
|
// and {{BRANCH}} placeholders in implement-prompt.md before the
|
|
122
122
|
// agent sees the prompt.
|
|
123
123
|
promptArgs: {
|
|
124
|
-
|
|
124
|
+
TASK_ID: issue.id,
|
|
125
125
|
ISSUE_TITLE: issue.title,
|
|
126
126
|
BRANCH: issue.branch,
|
|
127
127
|
},
|
|
@@ -133,7 +133,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
133
133
|
for (const [i, outcome] of settled.entries()) {
|
|
134
134
|
if (outcome.status === "rejected") {
|
|
135
135
|
console.error(
|
|
136
|
-
` ✗
|
|
136
|
+
` ✗ ${issues[i]!.id} (${issues[i]!.branch}) failed: ${outcome.reason}`,
|
|
137
137
|
);
|
|
138
138
|
}
|
|
139
139
|
}
|
|
@@ -182,7 +182,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
182
182
|
// -------------------------------------------------------------------------
|
|
183
183
|
await sandcastle.run({
|
|
184
184
|
hooks,
|
|
185
|
-
|
|
185
|
+
copyToWorkspace,
|
|
186
186
|
sandbox: docker(),
|
|
187
187
|
branchStrategy: { type: "merge-to-head" },
|
|
188
188
|
name: "merger",
|
|
@@ -193,9 +193,9 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
193
193
|
promptArgs: {
|
|
194
194
|
// A markdown list of branch names, one per line.
|
|
195
195
|
BRANCHES: completedBranches.map((b) => `- ${b}`).join("\n"),
|
|
196
|
-
// A markdown list of issue
|
|
196
|
+
// A markdown list of issue IDs and titles, one per line.
|
|
197
197
|
ISSUES: completedIssues
|
|
198
|
-
.map((i) => `-
|
|
198
|
+
.map((i) => `- ${i.id}: ${i.title}`)
|
|
199
199
|
.join("\n"),
|
|
200
200
|
},
|
|
201
201
|
});
|
|
@@ -4,7 +4,7 @@ Here are the open issues in the repo:
|
|
|
4
4
|
|
|
5
5
|
<issues-json>
|
|
6
6
|
|
|
7
|
-
!`
|
|
7
|
+
!`{{LIST_TASKS_COMMAND}}`
|
|
8
8
|
|
|
9
9
|
</issues-json>
|
|
10
10
|
|
|
@@ -20,14 +20,14 @@ An issue B is **blocked by** issue A if:
|
|
|
20
20
|
|
|
21
21
|
An issue is **unblocked** if it has zero blocking dependencies on other open issues.
|
|
22
22
|
|
|
23
|
-
For each unblocked issue, assign a branch name using the format `sandcastle/issue-{
|
|
23
|
+
For each unblocked issue, assign a branch name using the format `sandcastle/issue-{id}-{slug}`.
|
|
24
24
|
|
|
25
25
|
# OUTPUT
|
|
26
26
|
|
|
27
27
|
Output your plan as a JSON object wrapped in `<plan>` tags:
|
|
28
28
|
|
|
29
29
|
<plan>
|
|
30
|
-
{"issues": [{"
|
|
30
|
+
{"issues": [{"id": "42", "title": "Fix auth bug", "branch": "sandcastle/issue-42-fix-auth-bug"}]}
|
|
31
31
|
</plan>
|
|
32
32
|
|
|
33
33
|
Include only unblocked issues. If every issue is blocked, include the single highest-priority candidate (the one with the fewest or weakest dependencies).
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# TASK
|
|
2
2
|
|
|
3
|
-
Fix issue
|
|
3
|
+
Fix issue {{TASK_ID}}: {{ISSUE_TITLE}}
|
|
4
4
|
|
|
5
|
-
Pull in the issue using `
|
|
5
|
+
Pull in the issue using `{{VIEW_TASK_COMMAND}}`. If it has a parent PRD, pull that in too.
|
|
6
6
|
|
|
7
7
|
Only work on the issue specified.
|
|
8
8
|
|
|
@@ -41,7 +41,7 @@ const hooks = {
|
|
|
41
41
|
// Copy node_modules from the host into the worktree before each sandbox
|
|
42
42
|
// starts. Avoids a full npm install from scratch; the hook above handles
|
|
43
43
|
// platform-specific binaries and any packages added since the last copy.
|
|
44
|
-
const
|
|
44
|
+
const copyToWorkspace = ["node_modules"];
|
|
45
45
|
|
|
46
46
|
// Cap the number of concurrent sandboxes to avoid resource exhaustion.
|
|
47
47
|
const MAX_CONCURRENCY = 4;
|
|
@@ -64,7 +64,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
64
64
|
// -------------------------------------------------------------------------
|
|
65
65
|
const plan = await sandcastle.run({
|
|
66
66
|
hooks,
|
|
67
|
-
|
|
67
|
+
copyToWorkspace,
|
|
68
68
|
sandbox: docker(),
|
|
69
69
|
branchStrategy: { type: "merge-to-head" },
|
|
70
70
|
name: "planner",
|
|
@@ -84,9 +84,9 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
84
84
|
);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
// The plan JSON contains an array of issues, each with
|
|
87
|
+
// The plan JSON contains an array of issues, each with id, title, branch.
|
|
88
88
|
const { issues } = JSON.parse(planMatch[1]!) as {
|
|
89
|
-
issues: {
|
|
89
|
+
issues: { id: string; title: string; branch: string }[];
|
|
90
90
|
};
|
|
91
91
|
|
|
92
92
|
if (issues.length === 0) {
|
|
@@ -99,7 +99,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
99
99
|
`Planning complete. ${issues.length} issue(s) to work in parallel:`,
|
|
100
100
|
);
|
|
101
101
|
for (const issue of issues) {
|
|
102
|
-
console.log(`
|
|
102
|
+
console.log(` ${issue.id}: ${issue.title} → ${issue.branch}`);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// -------------------------------------------------------------------------
|
|
@@ -142,7 +142,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
142
142
|
branch: issue.branch,
|
|
143
143
|
sandbox: docker(),
|
|
144
144
|
hooks,
|
|
145
|
-
|
|
145
|
+
copyToWorkspace,
|
|
146
146
|
});
|
|
147
147
|
|
|
148
148
|
try {
|
|
@@ -153,7 +153,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
153
153
|
agent: sandcastle.claudeCode("claude-sonnet-4-6"),
|
|
154
154
|
promptFile: "./.sandcastle/implement-prompt.md",
|
|
155
155
|
promptArgs: {
|
|
156
|
-
|
|
156
|
+
TASK_ID: issue.id,
|
|
157
157
|
ISSUE_TITLE: issue.title,
|
|
158
158
|
BRANCH: issue.branch,
|
|
159
159
|
},
|
|
@@ -186,7 +186,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
186
186
|
for (const [i, outcome] of settled.entries()) {
|
|
187
187
|
if (outcome.status === "rejected") {
|
|
188
188
|
console.error(
|
|
189
|
-
` ✗
|
|
189
|
+
` ✗ ${issues[i]!.id} (${issues[i]!.branch}) failed: ${outcome.reason}`,
|
|
190
190
|
);
|
|
191
191
|
}
|
|
192
192
|
}
|
|
@@ -228,7 +228,7 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
228
228
|
// -------------------------------------------------------------------------
|
|
229
229
|
await sandcastle.run({
|
|
230
230
|
hooks,
|
|
231
|
-
|
|
231
|
+
copyToWorkspace,
|
|
232
232
|
sandbox: docker(),
|
|
233
233
|
branchStrategy: { type: "merge-to-head" },
|
|
234
234
|
name: "merger",
|
|
@@ -238,9 +238,9 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
|
|
238
238
|
promptArgs: {
|
|
239
239
|
// A markdown list of branch names, one per line.
|
|
240
240
|
BRANCHES: completedBranches.map((b) => `- ${b}`).join("\n"),
|
|
241
|
-
// A markdown list of issue
|
|
241
|
+
// A markdown list of issue IDs and titles, one per line.
|
|
242
242
|
ISSUES: completedIssues
|
|
243
|
-
.map((i) => `-
|
|
243
|
+
.map((i) => `- ${i.id}: ${i.title}`)
|
|
244
244
|
.join("\n"),
|
|
245
245
|
},
|
|
246
246
|
});
|
|
@@ -4,7 +4,7 @@ Here are the open issues in the repo:
|
|
|
4
4
|
|
|
5
5
|
<issues-json>
|
|
6
6
|
|
|
7
|
-
!`
|
|
7
|
+
!`{{LIST_TASKS_COMMAND}}`
|
|
8
8
|
|
|
9
9
|
</issues-json>
|
|
10
10
|
|
|
@@ -20,14 +20,14 @@ An issue B is **blocked by** issue A if:
|
|
|
20
20
|
|
|
21
21
|
An issue is **unblocked** if it has zero blocking dependencies on other open issues.
|
|
22
22
|
|
|
23
|
-
For each unblocked issue, assign a branch name using the format `sandcastle/issue-{
|
|
23
|
+
For each unblocked issue, assign a branch name using the format `sandcastle/issue-{id}-{slug}`.
|
|
24
24
|
|
|
25
25
|
# OUTPUT
|
|
26
26
|
|
|
27
27
|
Output your plan as a JSON object wrapped in `<plan>` tags:
|
|
28
28
|
|
|
29
29
|
<plan>
|
|
30
|
-
{"issues": [{"
|
|
30
|
+
{"issues": [{"id": "42", "title": "Fix auth bug", "branch": "sandcastle/issue-42-fix-auth-bug"}]}
|
|
31
31
|
</plan>
|
|
32
32
|
|
|
33
33
|
Include only unblocked issues. If every issue is blocked, include the single highest-priority candidate (the one with the fewest or weakest dependencies).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Open issues
|
|
4
4
|
|
|
5
|
-
!`
|
|
5
|
+
!`{{LIST_TASKS_COMMAND}}`
|
|
6
6
|
|
|
7
7
|
## Recent RALPH commits (last 10)
|
|
8
8
|
|
|
@@ -35,7 +35,7 @@ Pick the highest-priority open issue that is not blocked by another open issue.
|
|
|
35
35
|
- List key decisions made
|
|
36
36
|
- List files changed
|
|
37
37
|
- Note any blockers for the next iteration
|
|
38
|
-
6. **Close** — close the issue with `
|
|
38
|
+
6. **Close** — close the issue with `{{CLOSE_TASK_COMMAND}}` explaining what was done.
|
|
39
39
|
|
|
40
40
|
## Rules
|
|
41
41
|
|