@rockclaver/sandcastle 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1355 -0
- package/dist/MountConfig-CmXclHA5.d.ts +26 -0
- package/dist/SandboxProvider-EkSMuBp8.d.ts +243 -0
- package/dist/chunk-72UVAC7B.js +99 -0
- package/dist/chunk-72UVAC7B.js.map +1 -0
- package/dist/chunk-BIWNFKGV.js +22 -0
- package/dist/chunk-BIWNFKGV.js.map +1 -0
- package/dist/chunk-FKX3DRTL.js +362 -0
- package/dist/chunk-FKX3DRTL.js.map +1 -0
- package/dist/chunk-NGBM7T3E.js +76 -0
- package/dist/chunk-NGBM7T3E.js.map +1 -0
- package/dist/chunk-QCLZLPJ7.js +26431 -0
- package/dist/chunk-QCLZLPJ7.js.map +1 -0
- package/dist/chunk-VAKEM3U2.js +26997 -0
- package/dist/chunk-VAKEM3U2.js.map +1 -0
- package/dist/index.d.ts +943 -0
- package/dist/index.js +2393 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +19268 -0
- package/dist/main.js.map +1 -0
- package/dist/mountUtils-CCA-bbpK.d.ts +25 -0
- package/dist/sandboxes/daytona.d.ts +60 -0
- package/dist/sandboxes/daytona.js +122 -0
- package/dist/sandboxes/daytona.js.map +1 -0
- package/dist/sandboxes/docker.d.ts +110 -0
- package/dist/sandboxes/docker.js +9 -0
- package/dist/sandboxes/docker.js.map +1 -0
- package/dist/sandboxes/no-sandbox.d.ts +38 -0
- package/dist/sandboxes/no-sandbox.js +7 -0
- package/dist/sandboxes/no-sandbox.js.map +1 -0
- package/dist/sandboxes/podman.d.ts +124 -0
- package/dist/sandboxes/podman.js +299 -0
- package/dist/sandboxes/podman.js.map +1 -0
- package/dist/sandboxes/vercel.d.ts +104 -0
- package/dist/sandboxes/vercel.js +148 -0
- package/dist/sandboxes/vercel.js.map +1 -0
- package/dist/templates/blank/main.mts +14 -0
- package/dist/templates/blank/prompt.md +12 -0
- package/dist/templates/blank/template.json +4 -0
- package/dist/templates/parallel-planner/implement-prompt.md +62 -0
- package/dist/templates/parallel-planner/main.mts +204 -0
- package/dist/templates/parallel-planner/merge-prompt.md +26 -0
- package/dist/templates/parallel-planner/plan-prompt.md +37 -0
- package/dist/templates/parallel-planner/template.json +4 -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 +226 -0
- package/dist/templates/parallel-planner-with-review/merge-prompt.md +26 -0
- package/dist/templates/parallel-planner-with-review/plan-prompt.md +37 -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/CODING_STANDARDS.md +27 -0
- package/dist/templates/sequential-reviewer/implement-prompt.md +53 -0
- package/dist/templates/sequential-reviewer/main.mts +119 -0
- package/dist/templates/sequential-reviewer/review-prompt.md +55 -0
- package/dist/templates/sequential-reviewer/template.json +4 -0
- package/dist/templates/simple-loop/main.mts +49 -0
- package/dist/templates/simple-loop/prompt.md +53 -0
- package/dist/templates/simple-loop/template.json +4 -0
- package/package.json +104 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared mount utilities for Docker and Podman sandbox providers.
|
|
3
|
+
*
|
|
4
|
+
* Handles host/sandbox path resolution, tilde expansion, user mount
|
|
5
|
+
* validation, image naming, and Windows path normalization.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* SELinux volume label suffix applied to bind mounts.
|
|
10
|
+
*
|
|
11
|
+
* - `"z"` — shared label. No-op on non-SELinux systems.
|
|
12
|
+
* - `"Z"` — private label; only this container can access the mount.
|
|
13
|
+
* - `false` — disable labeling entirely.
|
|
14
|
+
*/
|
|
15
|
+
type SelinuxLabel = "z" | "Z" | false;
|
|
16
|
+
/**
|
|
17
|
+
* Derive the default image name from the repo directory.
|
|
18
|
+
* Returns `sandcastle:<dir-name>` where dir-name is the last path segment,
|
|
19
|
+
* lowercased and sanitized for image tag rules.
|
|
20
|
+
*
|
|
21
|
+
* Handles both POSIX (`/`) and Windows (`\`) path separators.
|
|
22
|
+
*/
|
|
23
|
+
declare const defaultImageName: (repoDir: string) => string;
|
|
24
|
+
|
|
25
|
+
export { type SelinuxLabel as S, defaultImageName as d };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { I as IsolatedSandboxProvider } from '../SandboxProvider-EkSMuBp8.js';
|
|
2
|
+
import { CreateSandboxFromImageParams, CreateSandboxFromSnapshotParams } from '@daytona/sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Daytona isolated sandbox provider.
|
|
6
|
+
*
|
|
7
|
+
* Creates ephemeral Daytona sandboxes via `@daytona/sdk`.
|
|
8
|
+
* Requires `@daytona/sdk` as a peer dependency.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Options for the Daytona sandbox provider. */
|
|
12
|
+
interface DaytonaOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Daytona API key for authentication.
|
|
15
|
+
* Falls back to the `DAYTONA_API_KEY` environment variable if not provided.
|
|
16
|
+
*/
|
|
17
|
+
readonly apiKey?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Daytona API URL.
|
|
20
|
+
* Falls back to the `DAYTONA_API_URL` environment variable if not provided.
|
|
21
|
+
*/
|
|
22
|
+
readonly apiUrl?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Target environment for sandboxes.
|
|
25
|
+
* Falls back to the `DAYTONA_TARGET` environment variable if not provided.
|
|
26
|
+
*/
|
|
27
|
+
readonly target?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Options passed through to the Daytona SDK when creating a sandbox.
|
|
30
|
+
* Supports both image-based and snapshot-based creation.
|
|
31
|
+
*/
|
|
32
|
+
readonly create?: CreateSandboxFromImageParams | CreateSandboxFromSnapshotParams;
|
|
33
|
+
/** Environment variables injected by this provider. Merged at launch time with env resolver and agent provider env. */
|
|
34
|
+
readonly env?: Record<string, string>;
|
|
35
|
+
/**
|
|
36
|
+
* Maximum number of characters of streamed `exec` output retained per stream
|
|
37
|
+
* (stdout and stderr) when an `onLine` callback is supplied (default: 64KiB).
|
|
38
|
+
*
|
|
39
|
+
* Output is delivered live to `onLine` regardless; this only bounds the tail
|
|
40
|
+
* returned in `ExecResult`, preventing a long-running agent's output from
|
|
41
|
+
* overflowing V8's max string length and crashing the run.
|
|
42
|
+
*/
|
|
43
|
+
readonly maxOutputTailChars?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a Daytona isolated sandbox provider.
|
|
47
|
+
*
|
|
48
|
+
* Sandboxes are ephemeral — each `create()` call spins up a new Daytona
|
|
49
|
+
* sandbox and `close()` destroys it.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* import { daytona } from "@ai-hero/sandcastle/sandboxes/daytona";
|
|
54
|
+
*
|
|
55
|
+
* const provider = daytona({ apiKey: "dyt_my_key" });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare const daytona: (options?: DaytonaOptions) => IsolatedSandboxProvider;
|
|
59
|
+
|
|
60
|
+
export { type DaytonaOptions, daytona };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { createIsolatedSandboxProvider } from '../chunk-BIWNFKGV.js';
|
|
3
|
+
import { MAX_TAIL_CHARS, BoundedTail } from '../chunk-NGBM7T3E.js';
|
|
4
|
+
import { stat, readdir } from 'fs/promises';
|
|
5
|
+
import { relative, join } from 'path';
|
|
6
|
+
|
|
7
|
+
createRequire(import.meta.url);
|
|
8
|
+
var daytona = (options) => createIsolatedSandboxProvider({
|
|
9
|
+
name: "daytona",
|
|
10
|
+
env: options?.env,
|
|
11
|
+
create: async () => {
|
|
12
|
+
const maxOutputTailChars = options?.maxOutputTailChars ?? MAX_TAIL_CHARS;
|
|
13
|
+
const { Daytona } = await import('@daytona/sdk');
|
|
14
|
+
const config = {};
|
|
15
|
+
if (options?.apiKey) config.apiKey = options.apiKey;
|
|
16
|
+
if (options?.apiUrl) config.apiUrl = options.apiUrl;
|
|
17
|
+
if (options?.target) config.target = options.target;
|
|
18
|
+
const client = new Daytona(config);
|
|
19
|
+
const sandbox = await client.create(options?.create);
|
|
20
|
+
const worktreePath = await sandbox.getWorkDir() ?? await sandbox.getUserHomeDir() ?? "/home/daytona";
|
|
21
|
+
return {
|
|
22
|
+
worktreePath,
|
|
23
|
+
exec: async (command, opts) => {
|
|
24
|
+
const effectiveCommand = opts?.sudo ? `sudo ${command}` : command;
|
|
25
|
+
if (opts?.onLine) {
|
|
26
|
+
const onLine = opts.onLine;
|
|
27
|
+
const sessionId = `sandcastle-${crypto.randomUUID()}`;
|
|
28
|
+
await sandbox.process.createSession(sessionId);
|
|
29
|
+
try {
|
|
30
|
+
const execResponse = await sandbox.process.executeSessionCommand(
|
|
31
|
+
sessionId,
|
|
32
|
+
{
|
|
33
|
+
command: `cd ${opts?.cwd ?? worktreePath} && ${effectiveCommand}`,
|
|
34
|
+
async: true
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
const cmdId = execResponse.cmdId;
|
|
38
|
+
const stdoutTail = new BoundedTail(maxOutputTailChars, "\n");
|
|
39
|
+
const stderrTail = new BoundedTail(maxOutputTailChars, "");
|
|
40
|
+
let partial = "";
|
|
41
|
+
await sandbox.process.getSessionCommandLogs(
|
|
42
|
+
sessionId,
|
|
43
|
+
cmdId,
|
|
44
|
+
(chunk) => {
|
|
45
|
+
const text = partial + chunk;
|
|
46
|
+
const lines = text.split("\n");
|
|
47
|
+
partial = lines.pop() ?? "";
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
stdoutTail.push(line);
|
|
50
|
+
onLine(line);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
(chunk) => {
|
|
54
|
+
stderrTail.push(chunk);
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
if (partial) {
|
|
58
|
+
stdoutTail.push(partial);
|
|
59
|
+
onLine(partial);
|
|
60
|
+
}
|
|
61
|
+
const cmdInfo = await sandbox.process.getSessionCommand(
|
|
62
|
+
sessionId,
|
|
63
|
+
cmdId
|
|
64
|
+
);
|
|
65
|
+
return {
|
|
66
|
+
stdout: stdoutTail.toString(),
|
|
67
|
+
stderr: stderrTail.toString(),
|
|
68
|
+
exitCode: cmdInfo.exitCode ?? 0
|
|
69
|
+
};
|
|
70
|
+
} finally {
|
|
71
|
+
await sandbox.process.deleteSession(sessionId).catch(() => {
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const response = await sandbox.process.executeCommand(
|
|
76
|
+
effectiveCommand,
|
|
77
|
+
opts?.cwd ?? worktreePath
|
|
78
|
+
);
|
|
79
|
+
return {
|
|
80
|
+
stdout: response.result,
|
|
81
|
+
stderr: "",
|
|
82
|
+
exitCode: response.exitCode
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
copyIn: async (hostPath, sandboxPath) => {
|
|
86
|
+
const info = await stat(hostPath);
|
|
87
|
+
if (info.isDirectory()) {
|
|
88
|
+
const walk = async (dir) => {
|
|
89
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
90
|
+
const files2 = [];
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
const full = join(dir, entry.name);
|
|
93
|
+
if (entry.isDirectory()) {
|
|
94
|
+
files2.push(...await walk(full));
|
|
95
|
+
} else {
|
|
96
|
+
files2.push(full);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return files2;
|
|
100
|
+
};
|
|
101
|
+
const files = await walk(hostPath);
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
const rel = relative(hostPath, file);
|
|
104
|
+
await sandbox.fs.uploadFile(file, join(sandboxPath, rel));
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
await sandbox.fs.uploadFile(hostPath, sandboxPath);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
copyFileOut: async (sandboxPath, hostPath) => {
|
|
111
|
+
await sandbox.fs.downloadFile(sandboxPath, hostPath);
|
|
112
|
+
},
|
|
113
|
+
close: async () => {
|
|
114
|
+
await client.delete(sandbox);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export { daytona };
|
|
121
|
+
//# sourceMappingURL=daytona.js.map
|
|
122
|
+
//# sourceMappingURL=daytona.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sandboxes/daytona.ts"],"names":["files"],"mappings":"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","file":"daytona.js","sourcesContent":["/**\n * Daytona isolated sandbox provider.\n *\n * Creates ephemeral Daytona sandboxes via `@daytona/sdk`.\n * Requires `@daytona/sdk` as a peer dependency.\n */\n\nimport { readdir, stat } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport {\n createIsolatedSandboxProvider,\n type ExecResult,\n type IsolatedSandboxHandle,\n type IsolatedSandboxProvider,\n} from \"../SandboxProvider.js\";\nimport { BoundedTail, MAX_TAIL_CHARS } from \"../boundedTail.js\";\n\nimport type {\n Daytona as DaytonaClient,\n DaytonaConfig,\n CreateSandboxFromImageParams,\n CreateSandboxFromSnapshotParams,\n} from \"@daytona/sdk\";\n\n/** Options for the Daytona sandbox provider. */\nexport interface DaytonaOptions {\n /**\n * Daytona API key for authentication.\n * Falls back to the `DAYTONA_API_KEY` environment variable if not provided.\n */\n readonly apiKey?: string;\n\n /**\n * Daytona API URL.\n * Falls back to the `DAYTONA_API_URL` environment variable if not provided.\n */\n readonly apiUrl?: string;\n\n /**\n * Target environment for sandboxes.\n * Falls back to the `DAYTONA_TARGET` environment variable if not provided.\n */\n readonly target?: string;\n\n /**\n * Options passed through to the Daytona SDK when creating a sandbox.\n * Supports both image-based and snapshot-based creation.\n */\n readonly create?:\n | CreateSandboxFromImageParams\n | CreateSandboxFromSnapshotParams;\n\n /** Environment variables injected by this provider. Merged at launch time with env resolver and agent provider env. */\n readonly env?: Record<string, string>;\n\n /**\n * Maximum number of characters of streamed `exec` output retained per stream\n * (stdout and stderr) when an `onLine` callback is supplied (default: 64KiB).\n *\n * Output is delivered live to `onLine` regardless; this only bounds the tail\n * returned in `ExecResult`, preventing a long-running agent's output from\n * overflowing V8's max string length and crashing the run.\n */\n readonly maxOutputTailChars?: number;\n}\n\n/**\n * Create a Daytona isolated sandbox provider.\n *\n * Sandboxes are ephemeral — each `create()` call spins up a new Daytona\n * sandbox and `close()` destroys it.\n *\n * @example\n * ```ts\n * import { daytona } from \"@ai-hero/sandcastle/sandboxes/daytona\";\n *\n * const provider = daytona({ apiKey: \"dyt_my_key\" });\n * ```\n */\nexport const daytona = (options?: DaytonaOptions): IsolatedSandboxProvider =>\n createIsolatedSandboxProvider({\n name: \"daytona\",\n env: options?.env,\n create: async (): Promise<IsolatedSandboxHandle> => {\n const maxOutputTailChars = options?.maxOutputTailChars ?? MAX_TAIL_CHARS;\n const { Daytona } =\n (await import(\"@daytona/sdk\")) as typeof import(\"@daytona/sdk\");\n\n const config: DaytonaConfig = {};\n if (options?.apiKey) config.apiKey = options.apiKey;\n if (options?.apiUrl) config.apiUrl = options.apiUrl;\n if (options?.target) config.target = options.target;\n\n const client: DaytonaClient = new Daytona(config);\n const sandbox = await client.create(options?.create as any);\n\n const worktreePath =\n (await sandbox.getWorkDir()) ??\n (await sandbox.getUserHomeDir()) ??\n \"/home/daytona\";\n\n return {\n worktreePath,\n\n exec: async (\n command: string,\n opts?: {\n onLine?: (line: string) => void;\n cwd?: string;\n sudo?: boolean;\n },\n ): Promise<ExecResult> => {\n const effectiveCommand = opts?.sudo ? `sudo ${command}` : command;\n if (opts?.onLine) {\n const onLine = opts.onLine;\n const sessionId = `sandcastle-${crypto.randomUUID()}`;\n await sandbox.process.createSession(sessionId);\n\n try {\n const execResponse = await sandbox.process.executeSessionCommand(\n sessionId,\n {\n command: `cd ${opts?.cwd ?? worktreePath} && ${effectiveCommand}`,\n async: true,\n },\n );\n\n const cmdId = execResponse.cmdId!;\n\n const stdoutTail = new BoundedTail(maxOutputTailChars, \"\\n\");\n const stderrTail = new BoundedTail(maxOutputTailChars, \"\");\n let partial = \"\";\n\n await sandbox.process.getSessionCommandLogs(\n sessionId,\n cmdId,\n (chunk: string) => {\n const text = partial + chunk;\n const lines = text.split(\"\\n\");\n partial = lines.pop() ?? \"\";\n for (const line of lines) {\n stdoutTail.push(line);\n onLine(line);\n }\n },\n (chunk: string) => {\n stderrTail.push(chunk);\n },\n );\n\n if (partial) {\n stdoutTail.push(partial);\n onLine(partial);\n }\n\n const cmdInfo = await sandbox.process.getSessionCommand(\n sessionId,\n cmdId,\n );\n\n return {\n stdout: stdoutTail.toString(),\n stderr: stderrTail.toString(),\n exitCode: cmdInfo.exitCode ?? 0,\n };\n } finally {\n await sandbox.process.deleteSession(sessionId).catch(() => {});\n }\n }\n\n const response = await sandbox.process.executeCommand(\n effectiveCommand,\n opts?.cwd ?? worktreePath,\n );\n return {\n stdout: response.result,\n stderr: \"\",\n exitCode: response.exitCode,\n };\n },\n\n copyIn: async (\n hostPath: string,\n sandboxPath: string,\n ): Promise<void> => {\n const info = await stat(hostPath);\n if (info.isDirectory()) {\n const walk = async (dir: string): Promise<string[]> => {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: string[] = [];\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await walk(full)));\n } else {\n files.push(full);\n }\n }\n return files;\n };\n const files = await walk(hostPath);\n for (const file of files) {\n const rel = relative(hostPath, file);\n await sandbox.fs.uploadFile(file, join(sandboxPath, rel));\n }\n } else {\n await sandbox.fs.uploadFile(hostPath, sandboxPath);\n }\n },\n\n copyFileOut: async (\n sandboxPath: string,\n hostPath: string,\n ): Promise<void> => {\n await sandbox.fs.downloadFile(sandboxPath, hostPath);\n },\n\n close: async (): Promise<void> => {\n await client.delete(sandbox);\n },\n };\n },\n });\n"]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { S as SandboxProvider } from '../SandboxProvider-EkSMuBp8.js';
|
|
2
|
+
import { M as MountConfig } from '../MountConfig-CmXclHA5.js';
|
|
3
|
+
import { S as SelinuxLabel } from '../mountUtils-CCA-bbpK.js';
|
|
4
|
+
export { d as defaultImageName } from '../mountUtils-CCA-bbpK.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Docker sandbox provider — wraps DockerLifecycle into a SandboxProvider.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { docker } from "sandcastle/sandboxes/docker";
|
|
11
|
+
* await run({ agent: claudeCode("claude-opus-4-7"), sandbox: docker() });
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface DockerOptions {
|
|
15
|
+
/** Docker image name (default: derived from repo directory name). */
|
|
16
|
+
readonly imageName?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The UID of the `agent` user inside the container image (default: host UID via `process.getuid()`, or 1000).
|
|
19
|
+
*
|
|
20
|
+
* Must match the UID baked into the image at build time. Used as the `--user` flag value
|
|
21
|
+
* and checked against the image's configured UID in the pre-flight diagnostic.
|
|
22
|
+
*/
|
|
23
|
+
readonly containerUid?: number;
|
|
24
|
+
/**
|
|
25
|
+
* The GID of the `agent` user inside the container image (default: host GID via `process.getgid()`, or 1000).
|
|
26
|
+
*
|
|
27
|
+
* Must match the GID baked into the image at build time. Used as the `--user` flag value.
|
|
28
|
+
*/
|
|
29
|
+
readonly containerGid?: number;
|
|
30
|
+
/**
|
|
31
|
+
* SELinux volume label suffix applied to bind mounts.
|
|
32
|
+
*
|
|
33
|
+
* - `"z"` — shared label (default). No-op on non-SELinux systems.
|
|
34
|
+
* - `"Z"` — private label; only this container can access the mount.
|
|
35
|
+
* - `false` — disable labeling entirely.
|
|
36
|
+
*/
|
|
37
|
+
readonly selinuxLabel?: SelinuxLabel;
|
|
38
|
+
/**
|
|
39
|
+
* Additional host directories to bind-mount into the sandbox.
|
|
40
|
+
*
|
|
41
|
+
* Each entry specifies a `hostPath` (tilde-expanded) and `sandboxPath`.
|
|
42
|
+
* If `hostPath` does not exist, sandbox creation fails with a clear error.
|
|
43
|
+
*/
|
|
44
|
+
readonly mounts?: readonly MountConfig[];
|
|
45
|
+
/** Environment variables injected by this provider. Merged at launch time with env resolver and agent provider env. */
|
|
46
|
+
readonly env?: Record<string, string>;
|
|
47
|
+
/**
|
|
48
|
+
* Docker network(s) to attach the container to.
|
|
49
|
+
*
|
|
50
|
+
* - `"my-network"` → `--network my-network`
|
|
51
|
+
* - `["net1", "net2"]` → `--network net1 --network net2`
|
|
52
|
+
*
|
|
53
|
+
* When omitted, Docker's default bridge network is used.
|
|
54
|
+
*/
|
|
55
|
+
readonly network?: string | readonly string[];
|
|
56
|
+
/**
|
|
57
|
+
* Supplementary groups to add the container user to, via `--group-add`.
|
|
58
|
+
*
|
|
59
|
+
* Accepts group names or numeric GIDs:
|
|
60
|
+
*
|
|
61
|
+
* - `["docker"]` → `--group-add docker`
|
|
62
|
+
* - `[999]` → `--group-add 999`
|
|
63
|
+
* - `["docker", 999]` → `--group-add docker --group-add 999`
|
|
64
|
+
*
|
|
65
|
+
* Useful for granting access to a bind-mounted Docker socket (Docker-outside-of-Docker).
|
|
66
|
+
* When omitted, no `--group-add` flags are added.
|
|
67
|
+
*/
|
|
68
|
+
readonly groups?: readonly (string | number)[];
|
|
69
|
+
/**
|
|
70
|
+
* Host devices to expose to the container, via `--device`.
|
|
71
|
+
*
|
|
72
|
+
* Each entry is a full device spec in `host[:container[:permissions]]` form:
|
|
73
|
+
*
|
|
74
|
+
* - `["/dev/kvm"]` → `--device /dev/kvm`
|
|
75
|
+
* - `["/dev/sda:/dev/xvda:rwm"]` → `--device /dev/sda:/dev/xvda:rwm`
|
|
76
|
+
* - `["/dev/kvm", "/dev/fuse"]` → `--device /dev/kvm --device /dev/fuse`
|
|
77
|
+
*
|
|
78
|
+
* When omitted, no `--device` flags are added.
|
|
79
|
+
*/
|
|
80
|
+
readonly devices?: readonly string[];
|
|
81
|
+
/**
|
|
82
|
+
* Maximum number of characters of streamed `exec` output retained per stream
|
|
83
|
+
* (stdout and stderr) when an `onLine` callback is supplied (default: 64KiB).
|
|
84
|
+
*
|
|
85
|
+
* Output is delivered live to `onLine` regardless; this only bounds the tail
|
|
86
|
+
* returned in `ExecResult`, preventing a long-running agent's output from
|
|
87
|
+
* overflowing V8's max string length and crashing the run.
|
|
88
|
+
*/
|
|
89
|
+
readonly maxOutputTailChars?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Limit the CPU resources available to the container, via `--cpus`.
|
|
92
|
+
*
|
|
93
|
+
* Maps directly to `docker run --cpus`. Accepts fractional values:
|
|
94
|
+
*
|
|
95
|
+
* - `2` → `--cpus 2` (at most 2 CPUs)
|
|
96
|
+
* - `1.5` → `--cpus 1.5` (at most 1.5 CPUs)
|
|
97
|
+
*
|
|
98
|
+
* When omitted, no `--cpus` flag is added and the container is unconstrained.
|
|
99
|
+
*/
|
|
100
|
+
readonly cpus?: number;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create a Docker sandbox provider.
|
|
104
|
+
*
|
|
105
|
+
* The returned provider creates Docker containers with bind-mounts
|
|
106
|
+
* for the worktree and git directories.
|
|
107
|
+
*/
|
|
108
|
+
declare const docker: (options?: DockerOptions) => SandboxProvider;
|
|
109
|
+
|
|
110
|
+
export { type DockerOptions, docker };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
export { docker } from '../chunk-FKX3DRTL.js';
|
|
3
|
+
export { defaultImageName } from '../chunk-VAKEM3U2.js';
|
|
4
|
+
import '../chunk-BIWNFKGV.js';
|
|
5
|
+
import '../chunk-NGBM7T3E.js';
|
|
6
|
+
|
|
7
|
+
createRequire(import.meta.url);
|
|
8
|
+
//# sourceMappingURL=docker.js.map
|
|
9
|
+
//# sourceMappingURL=docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"docker.js"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { N as NoSandboxProvider } from '../SandboxProvider-EkSMuBp8.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* No-sandbox provider — runs the agent directly on the host with no container isolation.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* import { noSandbox } from "sandcastle/sandboxes/no-sandbox";
|
|
8
|
+
* await interactive({ agent: claudeCode("claude-opus-4-7"), sandbox: noSandbox() });
|
|
9
|
+
*
|
|
10
|
+
* Accepted by `run()`, `interactive()`, and `createSandbox()`. Skips
|
|
11
|
+
* container isolation entirely — the agent executes on the host. Does not
|
|
12
|
+
* pass `--dangerously-skip-permissions` to the agent — the user manages
|
|
13
|
+
* permissions themselves.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface NoSandboxOptions {
|
|
17
|
+
/** Environment variables injected by this provider. Merged at launch time. */
|
|
18
|
+
readonly env?: Record<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* Maximum number of characters of streamed `exec` output retained per stream
|
|
21
|
+
* (stdout and stderr) when an `onLine` callback is supplied (default: 64KiB).
|
|
22
|
+
*
|
|
23
|
+
* Output is delivered live to `onLine` regardless; this only bounds the tail
|
|
24
|
+
* returned in `ExecResult`, preventing a long-running agent's output from
|
|
25
|
+
* overflowing V8's max string length and crashing the run.
|
|
26
|
+
*/
|
|
27
|
+
readonly maxOutputTailChars?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create a no-sandbox provider.
|
|
31
|
+
*
|
|
32
|
+
* The returned provider runs the agent directly on the host. All three
|
|
33
|
+
* branch strategies are supported (head, merge-to-head, branch),
|
|
34
|
+
* defaulting to head.
|
|
35
|
+
*/
|
|
36
|
+
declare const noSandbox: (options?: NoSandboxOptions) => NoSandboxProvider;
|
|
37
|
+
|
|
38
|
+
export { type NoSandboxOptions, noSandbox };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"no-sandbox.js"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { S as SandboxProvider } from '../SandboxProvider-EkSMuBp8.js';
|
|
2
|
+
import { M as MountConfig } from '../MountConfig-CmXclHA5.js';
|
|
3
|
+
import { S as SelinuxLabel } from '../mountUtils-CCA-bbpK.js';
|
|
4
|
+
export { d as defaultImageName } from '../mountUtils-CCA-bbpK.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Podman sandbox provider — creates Podman containers with bind-mounts.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { podman } from "sandcastle/sandboxes/podman";
|
|
11
|
+
* await run({ agent: claudeCode("claude-opus-4-7"), sandbox: podman() });
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface PodmanOptions {
|
|
15
|
+
/** Podman image name (default: derived from repo directory name). */
|
|
16
|
+
readonly imageName?: string;
|
|
17
|
+
/**
|
|
18
|
+
* SELinux volume label suffix applied to bind mounts.
|
|
19
|
+
*
|
|
20
|
+
* - `"z"` — shared label (default). No-op on non-SELinux systems.
|
|
21
|
+
* - `"Z"` — private label; only this container can access the mount.
|
|
22
|
+
* - `false` — disable labeling entirely.
|
|
23
|
+
*/
|
|
24
|
+
readonly selinuxLabel?: SelinuxLabel;
|
|
25
|
+
/**
|
|
26
|
+
* User namespace mode for rootless Podman.
|
|
27
|
+
*
|
|
28
|
+
* - `"keep-id"` (default) — maps host UID to `containerUid` inside the
|
|
29
|
+
* container via `--userns=keep-id:uid=N,gid=N`, so both bind-mounted
|
|
30
|
+
* files and image-built files have correct ownership without chown.
|
|
31
|
+
* - `false` — disable; use for rootful Podman setups.
|
|
32
|
+
*/
|
|
33
|
+
readonly userns?: "keep-id" | false;
|
|
34
|
+
/**
|
|
35
|
+
* The UID of the `agent` user inside the container image (default: 1000).
|
|
36
|
+
*
|
|
37
|
+
* Must match the UID set in the Containerfile. Used with `--userns=keep-id`
|
|
38
|
+
* to map the host user to this UID inside the container.
|
|
39
|
+
*/
|
|
40
|
+
readonly containerUid?: number;
|
|
41
|
+
/**
|
|
42
|
+
* The GID of the `agent` user inside the container image (default: 1000).
|
|
43
|
+
*
|
|
44
|
+
* Must match the GID set in the Containerfile. Used with `--userns=keep-id`
|
|
45
|
+
* to map the host group to this GID inside the container.
|
|
46
|
+
*/
|
|
47
|
+
readonly containerGid?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Additional host directories to bind-mount into the sandbox.
|
|
50
|
+
*
|
|
51
|
+
* Each entry specifies a `hostPath` (tilde-expanded) and `sandboxPath`.
|
|
52
|
+
* If `hostPath` does not exist, sandbox creation fails with a clear error.
|
|
53
|
+
*/
|
|
54
|
+
readonly mounts?: readonly MountConfig[];
|
|
55
|
+
/** Environment variables injected by this provider. Merged at launch time with env resolver and agent provider env. */
|
|
56
|
+
readonly env?: Record<string, string>;
|
|
57
|
+
/**
|
|
58
|
+
* Podman network(s) to attach the container to.
|
|
59
|
+
*
|
|
60
|
+
* - `"my-network"` → `--network my-network`
|
|
61
|
+
* - `["net1", "net2"]` → `--network net1 --network net2`
|
|
62
|
+
*
|
|
63
|
+
* When omitted, Podman's default network is used.
|
|
64
|
+
*/
|
|
65
|
+
readonly network?: string | readonly string[];
|
|
66
|
+
/**
|
|
67
|
+
* Supplementary groups to add the container user to, via `--group-add`.
|
|
68
|
+
*
|
|
69
|
+
* Accepts group names or numeric GIDs:
|
|
70
|
+
*
|
|
71
|
+
* - `["docker"]` → `--group-add docker`
|
|
72
|
+
* - `[999]` → `--group-add 999`
|
|
73
|
+
* - `["docker", 999]` → `--group-add docker --group-add 999`
|
|
74
|
+
*
|
|
75
|
+
* Useful for granting access to a bind-mounted Docker socket (Docker-outside-of-Docker).
|
|
76
|
+
* When omitted, no `--group-add` flags are added.
|
|
77
|
+
*/
|
|
78
|
+
readonly groups?: readonly (string | number)[];
|
|
79
|
+
/**
|
|
80
|
+
* Host devices to expose to the container, via `--device`.
|
|
81
|
+
*
|
|
82
|
+
* Each entry is a full device spec in `host[:container[:permissions]]` form:
|
|
83
|
+
*
|
|
84
|
+
* - `["/dev/kvm"]` → `--device /dev/kvm`
|
|
85
|
+
* - `["/dev/sda:/dev/xvda:rwm"]` → `--device /dev/sda:/dev/xvda:rwm`
|
|
86
|
+
* - `["/dev/kvm", "/dev/fuse"]` → `--device /dev/kvm --device /dev/fuse`
|
|
87
|
+
*
|
|
88
|
+
* Under rootless Podman, exposing a host device often requires host-side
|
|
89
|
+
* group/permission setup and may interact with `--userns=keep-id`.
|
|
90
|
+
* When omitted, no `--device` flags are added.
|
|
91
|
+
*/
|
|
92
|
+
readonly devices?: readonly string[];
|
|
93
|
+
/**
|
|
94
|
+
* Maximum number of characters of streamed `exec` output retained per stream
|
|
95
|
+
* (stdout and stderr) when an `onLine` callback is supplied (default: 64KiB).
|
|
96
|
+
*
|
|
97
|
+
* Output is delivered live to `onLine` regardless; this only bounds the tail
|
|
98
|
+
* returned in `ExecResult`, preventing a long-running agent's output from
|
|
99
|
+
* overflowing V8's max string length and crashing the run.
|
|
100
|
+
*/
|
|
101
|
+
readonly maxOutputTailChars?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Limit the CPU resources available to the container, via `--cpus`.
|
|
104
|
+
*
|
|
105
|
+
* Maps directly to `podman run --cpus`. Accepts fractional values:
|
|
106
|
+
*
|
|
107
|
+
* - `2` → `--cpus 2` (at most 2 CPUs)
|
|
108
|
+
* - `1.5` → `--cpus 1.5` (at most 1.5 CPUs)
|
|
109
|
+
*
|
|
110
|
+
* When omitted, no `--cpus` flag is added and the container is unconstrained.
|
|
111
|
+
*/
|
|
112
|
+
readonly cpus?: number;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Create a Podman sandbox provider.
|
|
116
|
+
*
|
|
117
|
+
* The returned provider creates Podman containers with bind-mounts
|
|
118
|
+
* for the worktree and git directories. Calls the `podman` binary
|
|
119
|
+
* on PATH directly. On macOS/Windows, verifies that a Podman Machine
|
|
120
|
+
* is running before container creation.
|
|
121
|
+
*/
|
|
122
|
+
declare const podman: (options?: PodmanOptions) => SandboxProvider;
|
|
123
|
+
|
|
124
|
+
export { type PodmanOptions, podman };
|