@ai-hero/sandcastle 0.3.0 → 0.4.1
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 +384 -58
- package/dist/AgentProvider.d.ts +22 -12
- package/dist/AgentProvider.d.ts.map +1 -1
- package/dist/AgentProvider.js +46 -47
- package/dist/AgentProvider.js.map +1 -1
- package/dist/DockerLifecycle.d.ts +5 -1
- package/dist/DockerLifecycle.d.ts.map +1 -1
- package/dist/DockerLifecycle.js +8 -1
- package/dist/DockerLifecycle.js.map +1 -1
- package/dist/InitService.d.ts.map +1 -1
- package/dist/InitService.js +57 -6
- package/dist/InitService.js.map +1 -1
- package/dist/MountConfig.d.ts +15 -0
- package/dist/MountConfig.d.ts.map +1 -0
- package/dist/MountConfig.js +7 -0
- package/dist/MountConfig.js.map +1 -0
- package/dist/Orchestrator.d.ts +0 -1
- package/dist/Orchestrator.d.ts.map +1 -1
- package/dist/Orchestrator.js +28 -29
- package/dist/Orchestrator.js.map +1 -1
- package/dist/SandboxFactory.d.ts +21 -17
- package/dist/SandboxFactory.d.ts.map +1 -1
- package/dist/SandboxFactory.js +48 -50
- package/dist/SandboxFactory.js.map +1 -1
- package/dist/SandboxLifecycle.d.ts +1 -1
- package/dist/SandboxLifecycle.d.ts.map +1 -1
- package/dist/SandboxLifecycle.js +2 -2
- package/dist/SandboxLifecycle.js.map +1 -1
- package/dist/SandboxProvider.d.ts +50 -13
- package/dist/SandboxProvider.d.ts.map +1 -1
- package/dist/SandboxProvider.js +2 -0
- package/dist/SandboxProvider.js.map +1 -1
- package/dist/TextDeltaBuffer.d.ts +24 -0
- package/dist/TextDeltaBuffer.d.ts.map +1 -0
- package/dist/TextDeltaBuffer.js +68 -0
- package/dist/TextDeltaBuffer.js.map +1 -0
- package/dist/WorktreeManager.d.ts +2 -0
- package/dist/WorktreeManager.d.ts.map +1 -1
- package/dist/WorktreeManager.js +3 -0
- package/dist/WorktreeManager.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -6
- package/dist/cli.js.map +1 -1
- package/dist/createSandbox.d.ts +6 -5
- package/dist/createSandbox.d.ts.map +1 -1
- package/dist/createSandbox.js +14 -6
- package/dist/createSandbox.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mergeProviderEnv.d.ts +13 -0
- package/dist/mergeProviderEnv.d.ts.map +1 -0
- package/dist/mergeProviderEnv.js +23 -0
- package/dist/mergeProviderEnv.js.map +1 -0
- package/dist/run.d.ts +7 -18
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +35 -22
- package/dist/run.js.map +1 -1
- package/dist/sandboxes/daytona.d.ts +48 -0
- package/dist/sandboxes/daytona.d.ts.map +1 -0
- package/dist/sandboxes/daytona.js +125 -0
- package/dist/sandboxes/daytona.js.map +1 -0
- package/dist/sandboxes/docker.d.ts +10 -0
- package/dist/sandboxes/docker.d.ts.map +1 -1
- package/dist/sandboxes/docker.js +69 -42
- package/dist/sandboxes/docker.js.map +1 -1
- package/dist/sandboxes/podman.d.ts +46 -0
- package/dist/sandboxes/podman.d.ts.map +1 -0
- package/dist/sandboxes/podman.js +195 -0
- package/dist/sandboxes/podman.js.map +1 -0
- package/dist/sandboxes/test-isolated.d.ts +1 -1
- package/dist/sandboxes/test-isolated.d.ts.map +1 -1
- package/dist/sandboxes/test-isolated.js +56 -45
- package/dist/sandboxes/test-isolated.js.map +1 -1
- package/dist/sandboxes/vercel.d.ts +92 -0
- package/dist/sandboxes/vercel.d.ts.map +1 -0
- package/dist/sandboxes/vercel.js +165 -0
- package/dist/sandboxes/vercel.js.map +1 -0
- package/dist/syncIn.d.ts +4 -2
- package/dist/syncIn.d.ts.map +1 -1
- package/dist/syncIn.js +72 -22
- package/dist/syncIn.js.map +1 -1
- package/dist/syncOut.d.ts +4 -2
- package/dist/syncOut.d.ts.map +1 -1
- package/dist/syncOut.js +156 -77
- package/dist/syncOut.js.map +1 -1
- package/dist/templates/blank/.env.example +1 -0
- package/dist/templates/parallel-planner/.env.example +1 -0
- package/dist/templates/parallel-planner/main.mts +3 -3
- package/dist/templates/parallel-planner-with-review/.env.example +5 -0
- package/dist/templates/parallel-planner-with-review/CODING_STANDARDS.md +27 -0
- package/dist/templates/parallel-planner-with-review/implement-prompt.md +62 -0
- package/dist/templates/parallel-planner-with-review/main.mts +249 -0
- package/dist/templates/parallel-planner-with-review/merge-prompt.md +22 -0
- package/dist/templates/parallel-planner-with-review/plan-prompt.md +33 -0
- package/dist/templates/parallel-planner-with-review/review-prompt.md +55 -0
- package/dist/templates/parallel-planner-with-review/template.json +4 -0
- package/dist/templates/sequential-reviewer/.env.example +1 -0
- package/dist/templates/sequential-reviewer/CODING_STANDARDS.md +27 -0
- package/dist/templates/sequential-reviewer/implement-prompt.md +34 -45
- package/dist/templates/sequential-reviewer/main.mts +2 -2
- package/dist/templates/sequential-reviewer/review-prompt.md +1 -1
- package/dist/templates/simple-loop/.env.example +1 -0
- package/dist/testSandbox.d.ts.map +1 -1
- package/dist/testSandbox.js +58 -53
- package/dist/testSandbox.js.map +1 -1
- package/package.json +25 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Podman sandbox provider — creates Podman containers with bind-mounts.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { podman } from "sandcastle/sandboxes/podman";
|
|
6
|
+
* await run({ agent: claudeCode("claude-opus-4-6"), sandbox: podman() });
|
|
7
|
+
*/
|
|
8
|
+
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
import { createInterface } from "node:readline";
|
|
11
|
+
import { createBindMountSandboxProvider, } from "../SandboxProvider.js";
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
/**
|
|
15
|
+
* Create a Podman sandbox provider.
|
|
16
|
+
*
|
|
17
|
+
* The returned provider creates Podman containers with bind-mounts
|
|
18
|
+
* for the worktree and git directories. Calls the `podman` binary
|
|
19
|
+
* on PATH directly — no Podman Machine detection or special
|
|
20
|
+
* macOS/Windows handling.
|
|
21
|
+
*/
|
|
22
|
+
export const podman = (options) => {
|
|
23
|
+
const configuredImageName = options?.imageName;
|
|
24
|
+
const selinuxLabel = options?.selinuxLabel ?? "z";
|
|
25
|
+
const userMounts = options?.mounts ? resolveUserMounts(options.mounts) : [];
|
|
26
|
+
return createBindMountSandboxProvider({
|
|
27
|
+
name: "podman",
|
|
28
|
+
env: options?.env,
|
|
29
|
+
create: async (createOptions) => {
|
|
30
|
+
const containerName = `sandcastle-${randomUUID()}`;
|
|
31
|
+
const workspacePath = createOptions.mounts.find((m) => m.hostPath === createOptions.worktreePath)?.sandboxPath ?? "/home/agent/workspace";
|
|
32
|
+
// Build volume mount strings with optional SELinux label (internal + user mounts)
|
|
33
|
+
const labelSuffix = selinuxLabel ? `:${selinuxLabel}` : "";
|
|
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
|
+
});
|
|
41
|
+
// Resolve image name
|
|
42
|
+
const imageName = configuredImageName ?? defaultImageName(createOptions.hostRepoPath);
|
|
43
|
+
const hostUid = process.getuid?.() ?? 1000;
|
|
44
|
+
const hostGid = process.getgid?.() ?? 1000;
|
|
45
|
+
const env = { ...createOptions.env, HOME: "/home/agent" };
|
|
46
|
+
const envArgs = Object.entries(env).flatMap(([key, value]) => [
|
|
47
|
+
"-e",
|
|
48
|
+
`${key}=${value}`,
|
|
49
|
+
]);
|
|
50
|
+
const volumeArgs = volumeMounts.flatMap((v) => ["-v", v]);
|
|
51
|
+
// Start container via podman run
|
|
52
|
+
await new Promise((resolve, reject) => {
|
|
53
|
+
execFile("podman", [
|
|
54
|
+
"run",
|
|
55
|
+
"-d",
|
|
56
|
+
"--name",
|
|
57
|
+
containerName,
|
|
58
|
+
"--user",
|
|
59
|
+
`${hostUid}:${hostGid}`,
|
|
60
|
+
"-w",
|
|
61
|
+
workspacePath,
|
|
62
|
+
...envArgs,
|
|
63
|
+
...volumeArgs,
|
|
64
|
+
imageName,
|
|
65
|
+
"sleep",
|
|
66
|
+
"infinity",
|
|
67
|
+
], (error) => {
|
|
68
|
+
if (error) {
|
|
69
|
+
reject(new Error(`podman run failed: ${error.message}`));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
resolve();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
// Set up signal handlers for cleanup
|
|
77
|
+
const onExit = () => {
|
|
78
|
+
try {
|
|
79
|
+
execFileSync("podman", ["rm", "-f", containerName], {
|
|
80
|
+
stdio: "ignore",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
/* best-effort */
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const onSignal = () => {
|
|
88
|
+
onExit();
|
|
89
|
+
process.exit(1);
|
|
90
|
+
};
|
|
91
|
+
process.on("exit", onExit);
|
|
92
|
+
process.on("SIGINT", onSignal);
|
|
93
|
+
process.on("SIGTERM", onSignal);
|
|
94
|
+
const handle = {
|
|
95
|
+
workspacePath,
|
|
96
|
+
exec: (command, opts) => {
|
|
97
|
+
const args = ["exec"];
|
|
98
|
+
if (opts?.cwd)
|
|
99
|
+
args.push("-w", opts.cwd);
|
|
100
|
+
args.push(containerName, "sh", "-c", command);
|
|
101
|
+
if (opts?.onLine) {
|
|
102
|
+
const onLine = opts.onLine;
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
const proc = spawn("podman", args, {
|
|
105
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
106
|
+
});
|
|
107
|
+
const stdoutChunks = [];
|
|
108
|
+
const stderrChunks = [];
|
|
109
|
+
const rl = createInterface({ input: proc.stdout });
|
|
110
|
+
rl.on("line", (line) => {
|
|
111
|
+
stdoutChunks.push(line);
|
|
112
|
+
onLine(line);
|
|
113
|
+
});
|
|
114
|
+
proc.stderr.on("data", (chunk) => {
|
|
115
|
+
stderrChunks.push(chunk.toString());
|
|
116
|
+
});
|
|
117
|
+
proc.on("error", (error) => {
|
|
118
|
+
reject(new Error(`podman exec failed: ${error.message}`));
|
|
119
|
+
});
|
|
120
|
+
proc.on("close", (code) => {
|
|
121
|
+
resolve({
|
|
122
|
+
stdout: stdoutChunks.join("\n"),
|
|
123
|
+
stderr: stderrChunks.join(""),
|
|
124
|
+
exitCode: code ?? 0,
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
execFile("podman", args, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
131
|
+
if (error && error.code === undefined) {
|
|
132
|
+
reject(new Error(`podman exec failed: ${error.message}`));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
resolve({
|
|
136
|
+
stdout: stdout.toString(),
|
|
137
|
+
stderr: stderr.toString(),
|
|
138
|
+
exitCode: typeof error?.code === "number" ? error.code : 0,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
close: async () => {
|
|
145
|
+
process.removeListener("exit", onExit);
|
|
146
|
+
process.removeListener("SIGINT", onSignal);
|
|
147
|
+
process.removeListener("SIGTERM", onSignal);
|
|
148
|
+
await new Promise((resolve, reject) => {
|
|
149
|
+
execFile("podman", ["rm", "-f", containerName], (error) => {
|
|
150
|
+
if (error) {
|
|
151
|
+
reject(new Error(`podman rm failed: ${error.message}`));
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
resolve();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
return handle;
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Derive the default Podman image name from the repo directory.
|
|
166
|
+
* Returns `sandcastle:<dir-name>` where dir-name is the last path segment,
|
|
167
|
+
* lowercased and sanitized for image tag rules.
|
|
168
|
+
*/
|
|
169
|
+
export const defaultImageName = (repoDir) => {
|
|
170
|
+
const dirName = repoDir.replace(/\/+$/, "").split("/").pop() || "local";
|
|
171
|
+
const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
|
|
172
|
+
return `sandcastle:${sanitized}`;
|
|
173
|
+
};
|
|
174
|
+
const expandTilde = (p) => {
|
|
175
|
+
if (p === "~")
|
|
176
|
+
return homedir();
|
|
177
|
+
if (p.startsWith("~/"))
|
|
178
|
+
return homedir() + p.slice(1);
|
|
179
|
+
return p;
|
|
180
|
+
};
|
|
181
|
+
const resolveUserMounts = (mounts) => mounts.map((m) => {
|
|
182
|
+
const resolvedHostPath = expandTilde(m.hostPath);
|
|
183
|
+
if (!existsSync(resolvedHostPath)) {
|
|
184
|
+
throw new Error(`Mount hostPath does not exist: ${m.hostPath}` +
|
|
185
|
+
(m.hostPath !== resolvedHostPath
|
|
186
|
+
? ` (resolved to ${resolvedHostPath})`
|
|
187
|
+
: ""));
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
hostPath: resolvedHostPath,
|
|
191
|
+
sandboxPath: m.sandboxPath,
|
|
192
|
+
...(m.readonly ? { readonly: true } : {}),
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
//# sourceMappingURL=podman.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"podman.js","sourceRoot":"","sources":["../../src/sandboxes/podman.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ import { type IsolatedSandboxProvider } from "../SandboxProvider.js";
|
|
|
10
10
|
* Create a filesystem-based test isolated sandbox provider.
|
|
11
11
|
*
|
|
12
12
|
* The "sandbox" is a temp directory. `exec` runs shell commands in it,
|
|
13
|
-
* `copyIn`/`
|
|
13
|
+
* `copyIn`/`copyFileOut` copy files between host and the temp dir,
|
|
14
14
|
* and `close` removes the temp dir.
|
|
15
15
|
*/
|
|
16
16
|
export declare const testIsolated: () => IsolatedSandboxProvider;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-isolated.d.ts","sourceRoot":"","sources":["../../src/sandboxes/test-isolated.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,EAIL,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAC;AAE/B;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,+
|
|
1
|
+
{"version":3,"file":"test-isolated.d.ts","sourceRoot":"","sources":["../../src/sandboxes/test-isolated.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,EAIL,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAC;AAE/B;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,+BAmGrB,CAAC"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* requiring a real remote environment.
|
|
7
7
|
*/
|
|
8
8
|
import { execFile, spawn } from "node:child_process";
|
|
9
|
-
import { copyFile, mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
9
|
+
import { copyFile, cp, mkdir, mkdtemp, rm, stat } from "node:fs/promises";
|
|
10
10
|
import { tmpdir } from "node:os";
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
12
12
|
import { createInterface } from "node:readline";
|
|
@@ -15,7 +15,7 @@ import { createIsolatedSandboxProvider, } from "../SandboxProvider.js";
|
|
|
15
15
|
* Create a filesystem-based test isolated sandbox provider.
|
|
16
16
|
*
|
|
17
17
|
* The "sandbox" is a temp directory. `exec` runs shell commands in it,
|
|
18
|
-
* `copyIn`/`
|
|
18
|
+
* `copyIn`/`copyFileOut` copy files between host and the temp dir,
|
|
19
19
|
* and `close` removes the temp dir.
|
|
20
20
|
*/
|
|
21
21
|
export const testIsolated = () => createIsolatedSandboxProvider({
|
|
@@ -26,54 +26,65 @@ export const testIsolated = () => createIsolatedSandboxProvider({
|
|
|
26
26
|
await mkdir(workspacePath, { recursive: true });
|
|
27
27
|
return {
|
|
28
28
|
workspacePath,
|
|
29
|
-
exec: (command, options) =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
exec: (command, options) => {
|
|
30
|
+
if (options?.onLine) {
|
|
31
|
+
const onLine = options.onLine;
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const proc = spawn("sh", ["-c", command], {
|
|
34
|
+
cwd: options?.cwd ?? workspacePath,
|
|
35
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
36
|
+
});
|
|
37
|
+
const stdoutChunks = [];
|
|
38
|
+
const stderrChunks = [];
|
|
39
|
+
const rl = createInterface({ input: proc.stdout });
|
|
40
|
+
rl.on("line", (line) => {
|
|
41
|
+
stdoutChunks.push(line);
|
|
42
|
+
onLine(line);
|
|
43
|
+
});
|
|
44
|
+
proc.stderr.on("data", (chunk) => {
|
|
45
|
+
stderrChunks.push(chunk.toString());
|
|
46
|
+
});
|
|
47
|
+
proc.on("error", (error) => {
|
|
48
|
+
reject(new Error(`exec failed: ${error.message}`));
|
|
49
|
+
});
|
|
50
|
+
proc.on("close", (code) => {
|
|
51
|
+
resolve({
|
|
52
|
+
stdout: stdoutChunks.join("\n"),
|
|
53
|
+
stderr: stderrChunks.join(""),
|
|
54
|
+
exitCode: code ?? 0,
|
|
55
|
+
});
|
|
42
56
|
});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
61
|
-
proc.on("error", (error) => {
|
|
62
|
-
reject(new Error(`exec streaming failed: ${error.message}`));
|
|
63
|
-
});
|
|
64
|
-
proc.on("close", (code) => {
|
|
65
|
-
resolve({
|
|
66
|
-
stdout: stdoutChunks.join("\n"),
|
|
67
|
-
stderr: stderrChunks.join(""),
|
|
68
|
-
exitCode: code ?? 0,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
execFile("sh", ["-c", command], {
|
|
61
|
+
cwd: options?.cwd ?? workspacePath,
|
|
62
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
63
|
+
}, (error, stdout, stderr) => {
|
|
64
|
+
if (error && error.code === undefined) {
|
|
65
|
+
reject(new Error(`exec failed: ${error.message}`));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
resolve({
|
|
69
|
+
stdout: stdout.toString(),
|
|
70
|
+
stderr: stderr.toString(),
|
|
71
|
+
exitCode: typeof error?.code === "number" ? error.code : 0,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
69
74
|
});
|
|
70
75
|
});
|
|
71
|
-
}
|
|
76
|
+
},
|
|
72
77
|
copyIn: async (hostPath, sandboxPath) => {
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
const info = await stat(hostPath);
|
|
79
|
+
if (info.isDirectory()) {
|
|
80
|
+
await cp(hostPath, sandboxPath, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await mkdir(dirname(sandboxPath), { recursive: true });
|
|
84
|
+
await copyFile(hostPath, sandboxPath);
|
|
85
|
+
}
|
|
75
86
|
},
|
|
76
|
-
|
|
87
|
+
copyFileOut: async (sandboxPath, hostPath) => {
|
|
77
88
|
await mkdir(dirname(hostPath), { recursive: true });
|
|
78
89
|
await copyFile(sandboxPath, hostPath);
|
|
79
90
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-isolated.js","sourceRoot":"","sources":["../../src/sandboxes/test-isolated.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"test-isolated.js","sourceRoot":"","sources":["../../src/sandboxes/test-isolated.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel isolated sandbox provider — wraps `@vercel/sandbox` into a SandboxProvider.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { vercel } from "sandcastle/sandboxes/vercel";
|
|
6
|
+
* await run({ agent: claudeCode("claude-opus-4-6"), sandbox: vercel() });
|
|
7
|
+
*/
|
|
8
|
+
import { type IsolatedSandboxProvider } from "../SandboxProvider.js";
|
|
9
|
+
/**
|
|
10
|
+
* Options for creating a Vercel sandbox provider.
|
|
11
|
+
*
|
|
12
|
+
* All `@vercel/sandbox` `Sandbox.create()` options are accepted as pass-through,
|
|
13
|
+
* plus Sandcastle-specific options for auth and branch strategy.
|
|
14
|
+
*/
|
|
15
|
+
export interface VercelOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Vercel access token.
|
|
18
|
+
*
|
|
19
|
+
* Falls back to the SDK's default auth behavior, which reads
|
|
20
|
+
* `VERCEL_OIDC_TOKEN` (recommended for Vercel-hosted environments) or
|
|
21
|
+
* `VERCEL_TOKEN` from the environment.
|
|
22
|
+
*/
|
|
23
|
+
readonly token?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The source of the sandbox (git repo, tarball, or snapshot).
|
|
26
|
+
* Omit to start an empty sandbox.
|
|
27
|
+
*/
|
|
28
|
+
readonly source?: {
|
|
29
|
+
type: "git";
|
|
30
|
+
url: string;
|
|
31
|
+
depth?: number;
|
|
32
|
+
revision?: string;
|
|
33
|
+
username?: string;
|
|
34
|
+
password?: string;
|
|
35
|
+
} | {
|
|
36
|
+
type: "tarball";
|
|
37
|
+
url: string;
|
|
38
|
+
} | {
|
|
39
|
+
type: "snapshot";
|
|
40
|
+
snapshotId: string;
|
|
41
|
+
};
|
|
42
|
+
/** Array of port numbers to expose from the sandbox (up to 4). */
|
|
43
|
+
readonly ports?: number[];
|
|
44
|
+
/** Timeout in milliseconds before the sandbox auto-terminates. */
|
|
45
|
+
readonly timeout?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Resources to allocate to the sandbox.
|
|
48
|
+
* Each vCPU gets 2048 MB of memory.
|
|
49
|
+
*/
|
|
50
|
+
readonly resources?: {
|
|
51
|
+
vcpus: number;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The runtime of the sandbox (e.g. `"node24"`, `"node22"`, `"python3.13"`).
|
|
55
|
+
* Defaults to `"node24"`.
|
|
56
|
+
*/
|
|
57
|
+
readonly runtime?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Network policy for the sandbox.
|
|
60
|
+
* Defaults to full internet access if not specified.
|
|
61
|
+
*/
|
|
62
|
+
readonly networkPolicy?: Record<string, unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* Vercel project ID to associate sandbox operations with.
|
|
65
|
+
*/
|
|
66
|
+
readonly projectId?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Vercel team ID to associate sandbox operations with.
|
|
69
|
+
*/
|
|
70
|
+
readonly teamId?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Timeout in milliseconds (alias for `timeout`, kept for discoverability).
|
|
73
|
+
*/
|
|
74
|
+
readonly timeoutMs?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Sandbox template shorthand (e.g. `"node-22"`).
|
|
77
|
+
* Maps to the `runtime` option.
|
|
78
|
+
*/
|
|
79
|
+
readonly template?: string;
|
|
80
|
+
/** Environment variables injected by this provider. Merged at launch time with env resolver and agent provider env. */
|
|
81
|
+
readonly env?: Record<string, string>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Create a Vercel isolated sandbox provider.
|
|
85
|
+
*
|
|
86
|
+
* The returned provider creates Vercel Firecracker microVM sandboxes via
|
|
87
|
+
* the `@vercel/sandbox` SDK. Each sandbox is ephemeral — one sandbox per run.
|
|
88
|
+
*
|
|
89
|
+
* Requires `@vercel/sandbox` to be installed as a peer dependency.
|
|
90
|
+
*/
|
|
91
|
+
export declare const vercel: (options?: VercelOptions | undefined) => IsolatedSandboxProvider;
|
|
92
|
+
//# sourceMappingURL=vercel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../src/sandboxes/vercel.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,EAIL,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAC;AAK/B;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAIxB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EACZ;QACE,IAAI,EAAE,KAAK,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GACD;QACE,IAAI,EAAE,SAAS,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;KACb,GACD;QACE,IAAI,EAAE,UAAU,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAEN,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE;QACnB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IAEF;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEjD;;OAEG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,uHAAuH;IACvH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,kEAqKf,CAAC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel isolated sandbox provider — wraps `@vercel/sandbox` into a SandboxProvider.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { vercel } from "sandcastle/sandboxes/vercel";
|
|
6
|
+
* await run({ agent: claudeCode("claude-opus-4-6"), sandbox: vercel() });
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
|
+
import { readFile, unlink, writeFile, mkdir, stat } from "node:fs/promises";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { Writable } from "node:stream";
|
|
13
|
+
import { createIsolatedSandboxProvider, } from "../SandboxProvider.js";
|
|
14
|
+
/** Workspace path inside the Vercel sandbox. */
|
|
15
|
+
const VERCEL_WORKSPACE_PATH = "/vercel/sandbox/workspace";
|
|
16
|
+
/**
|
|
17
|
+
* Create a Vercel isolated sandbox provider.
|
|
18
|
+
*
|
|
19
|
+
* The returned provider creates Vercel Firecracker microVM sandboxes via
|
|
20
|
+
* the `@vercel/sandbox` SDK. Each sandbox is ephemeral — one sandbox per run.
|
|
21
|
+
*
|
|
22
|
+
* Requires `@vercel/sandbox` to be installed as a peer dependency.
|
|
23
|
+
*/
|
|
24
|
+
export const vercel = (options) => createIsolatedSandboxProvider({
|
|
25
|
+
name: "vercel",
|
|
26
|
+
env: options?.env,
|
|
27
|
+
create: async (createOptions) => {
|
|
28
|
+
// Dynamic import so the peer dependency is only loaded at runtime
|
|
29
|
+
const { Sandbox } = await import("@vercel/sandbox");
|
|
30
|
+
const createParams = {};
|
|
31
|
+
// Pass through SDK options
|
|
32
|
+
if (options?.source)
|
|
33
|
+
createParams.source = options.source;
|
|
34
|
+
if (options?.ports)
|
|
35
|
+
createParams.ports = options.ports;
|
|
36
|
+
if (options?.resources)
|
|
37
|
+
createParams.resources = options.resources;
|
|
38
|
+
if (options?.networkPolicy)
|
|
39
|
+
createParams.networkPolicy = options.networkPolicy;
|
|
40
|
+
// runtime takes precedence over the template convenience alias
|
|
41
|
+
const resolvedRuntime = options?.runtime ?? options?.template;
|
|
42
|
+
if (resolvedRuntime)
|
|
43
|
+
createParams.runtime = resolvedRuntime;
|
|
44
|
+
// Timeout: prefer explicit timeout, fall back to timeoutMs alias
|
|
45
|
+
const timeoutValue = options?.timeout ?? options?.timeoutMs;
|
|
46
|
+
if (timeoutValue !== undefined)
|
|
47
|
+
createParams.timeout = timeoutValue;
|
|
48
|
+
// Merge provider env with Sandcastle env
|
|
49
|
+
createParams.env = createOptions.env;
|
|
50
|
+
// Auth: pass token and team/project IDs if provided
|
|
51
|
+
if (options?.token)
|
|
52
|
+
createParams.token = options.token;
|
|
53
|
+
if (options?.projectId)
|
|
54
|
+
createParams.projectId = options.projectId;
|
|
55
|
+
if (options?.teamId)
|
|
56
|
+
createParams.teamId = options.teamId;
|
|
57
|
+
const sandbox = await Sandbox.create(createParams);
|
|
58
|
+
// Ensure workspace directory exists
|
|
59
|
+
await sandbox.mkDir(VERCEL_WORKSPACE_PATH);
|
|
60
|
+
const handle = {
|
|
61
|
+
workspacePath: VERCEL_WORKSPACE_PATH,
|
|
62
|
+
exec: async (command, opts) => {
|
|
63
|
+
if (opts?.onLine) {
|
|
64
|
+
const onLine = opts.onLine;
|
|
65
|
+
const stdoutLines = [];
|
|
66
|
+
const stderrChunks = [];
|
|
67
|
+
let partial = "";
|
|
68
|
+
const stdoutWritable = new Writable({
|
|
69
|
+
write(chunk, _encoding, callback) {
|
|
70
|
+
const text = partial + chunk.toString();
|
|
71
|
+
const lines = text.split("\n");
|
|
72
|
+
partial = lines.pop() ?? "";
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
stdoutLines.push(line);
|
|
75
|
+
onLine(line);
|
|
76
|
+
}
|
|
77
|
+
callback();
|
|
78
|
+
},
|
|
79
|
+
final(callback) {
|
|
80
|
+
if (partial) {
|
|
81
|
+
stdoutLines.push(partial);
|
|
82
|
+
onLine(partial);
|
|
83
|
+
partial = "";
|
|
84
|
+
}
|
|
85
|
+
callback();
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
const stderrWritable = new Writable({
|
|
89
|
+
write(chunk, _encoding, callback) {
|
|
90
|
+
stderrChunks.push(chunk.toString());
|
|
91
|
+
callback();
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
const result = await sandbox.runCommand({
|
|
95
|
+
cmd: "sh",
|
|
96
|
+
args: ["-c", command],
|
|
97
|
+
cwd: opts?.cwd ?? VERCEL_WORKSPACE_PATH,
|
|
98
|
+
stdout: stdoutWritable,
|
|
99
|
+
stderr: stderrWritable,
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
stdout: stdoutLines.join("\n"),
|
|
103
|
+
stderr: stderrChunks.join(""),
|
|
104
|
+
exitCode: result.exitCode,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const result = await sandbox.runCommand({
|
|
108
|
+
cmd: "sh",
|
|
109
|
+
args: ["-c", command],
|
|
110
|
+
cwd: opts?.cwd ?? VERCEL_WORKSPACE_PATH,
|
|
111
|
+
});
|
|
112
|
+
const stdout = await result.stdout();
|
|
113
|
+
const stderr = await result.stderr();
|
|
114
|
+
return {
|
|
115
|
+
stdout,
|
|
116
|
+
stderr,
|
|
117
|
+
exitCode: result.exitCode,
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
copyIn: async (hostPath, sandboxPath) => {
|
|
121
|
+
const info = await stat(hostPath);
|
|
122
|
+
if (info.isDirectory()) {
|
|
123
|
+
const tarPath = join(tmpdir(), `sandcastle-copyin-${Date.now()}.tar.gz`);
|
|
124
|
+
execSync(`tar -czf "${tarPath}" -C "${hostPath}" .`);
|
|
125
|
+
try {
|
|
126
|
+
const tarContent = await readFile(tarPath);
|
|
127
|
+
const sandboxTarPath = `/tmp/sandcastle-copyin-${Date.now()}.tar.gz`;
|
|
128
|
+
await sandbox.writeFiles([
|
|
129
|
+
{ path: sandboxTarPath, content: tarContent },
|
|
130
|
+
]);
|
|
131
|
+
await sandbox.runCommand({
|
|
132
|
+
cmd: "sh",
|
|
133
|
+
args: [
|
|
134
|
+
"-c",
|
|
135
|
+
`mkdir -p "${sandboxPath}" && tar -xzf "${sandboxTarPath}" -C "${sandboxPath}" && rm -f "${sandboxTarPath}"`,
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
await unlink(tarPath).catch(() => { });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const content = await readFile(hostPath);
|
|
145
|
+
await sandbox.writeFiles([{ path: sandboxPath, content }]);
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
copyFileOut: async (sandboxPath, hostPath) => {
|
|
149
|
+
const buffer = await sandbox.readFileToBuffer({
|
|
150
|
+
path: sandboxPath,
|
|
151
|
+
});
|
|
152
|
+
if (!buffer) {
|
|
153
|
+
throw new Error(`File not found in Vercel sandbox: ${sandboxPath}`);
|
|
154
|
+
}
|
|
155
|
+
await mkdir(dirname(hostPath), { recursive: true });
|
|
156
|
+
await writeFile(hostPath, buffer);
|
|
157
|
+
},
|
|
158
|
+
close: async () => {
|
|
159
|
+
await sandbox.stop();
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
return handle;
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
//# sourceMappingURL=vercel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../src/sandboxes/vercel.ts"],"names":[],"mappings":"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"}
|