@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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1355 -0
  3. package/dist/MountConfig-CmXclHA5.d.ts +26 -0
  4. package/dist/SandboxProvider-EkSMuBp8.d.ts +243 -0
  5. package/dist/chunk-72UVAC7B.js +99 -0
  6. package/dist/chunk-72UVAC7B.js.map +1 -0
  7. package/dist/chunk-BIWNFKGV.js +22 -0
  8. package/dist/chunk-BIWNFKGV.js.map +1 -0
  9. package/dist/chunk-FKX3DRTL.js +362 -0
  10. package/dist/chunk-FKX3DRTL.js.map +1 -0
  11. package/dist/chunk-NGBM7T3E.js +76 -0
  12. package/dist/chunk-NGBM7T3E.js.map +1 -0
  13. package/dist/chunk-QCLZLPJ7.js +26431 -0
  14. package/dist/chunk-QCLZLPJ7.js.map +1 -0
  15. package/dist/chunk-VAKEM3U2.js +26997 -0
  16. package/dist/chunk-VAKEM3U2.js.map +1 -0
  17. package/dist/index.d.ts +943 -0
  18. package/dist/index.js +2393 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/main.d.ts +1 -0
  21. package/dist/main.js +19268 -0
  22. package/dist/main.js.map +1 -0
  23. package/dist/mountUtils-CCA-bbpK.d.ts +25 -0
  24. package/dist/sandboxes/daytona.d.ts +60 -0
  25. package/dist/sandboxes/daytona.js +122 -0
  26. package/dist/sandboxes/daytona.js.map +1 -0
  27. package/dist/sandboxes/docker.d.ts +110 -0
  28. package/dist/sandboxes/docker.js +9 -0
  29. package/dist/sandboxes/docker.js.map +1 -0
  30. package/dist/sandboxes/no-sandbox.d.ts +38 -0
  31. package/dist/sandboxes/no-sandbox.js +7 -0
  32. package/dist/sandboxes/no-sandbox.js.map +1 -0
  33. package/dist/sandboxes/podman.d.ts +124 -0
  34. package/dist/sandboxes/podman.js +299 -0
  35. package/dist/sandboxes/podman.js.map +1 -0
  36. package/dist/sandboxes/vercel.d.ts +104 -0
  37. package/dist/sandboxes/vercel.js +148 -0
  38. package/dist/sandboxes/vercel.js.map +1 -0
  39. package/dist/templates/blank/main.mts +14 -0
  40. package/dist/templates/blank/prompt.md +12 -0
  41. package/dist/templates/blank/template.json +4 -0
  42. package/dist/templates/parallel-planner/implement-prompt.md +62 -0
  43. package/dist/templates/parallel-planner/main.mts +204 -0
  44. package/dist/templates/parallel-planner/merge-prompt.md +26 -0
  45. package/dist/templates/parallel-planner/plan-prompt.md +37 -0
  46. package/dist/templates/parallel-planner/template.json +4 -0
  47. package/dist/templates/parallel-planner-with-review/CODING_STANDARDS.md +27 -0
  48. package/dist/templates/parallel-planner-with-review/implement-prompt.md +62 -0
  49. package/dist/templates/parallel-planner-with-review/main.mts +226 -0
  50. package/dist/templates/parallel-planner-with-review/merge-prompt.md +26 -0
  51. package/dist/templates/parallel-planner-with-review/plan-prompt.md +37 -0
  52. package/dist/templates/parallel-planner-with-review/review-prompt.md +55 -0
  53. package/dist/templates/parallel-planner-with-review/template.json +4 -0
  54. package/dist/templates/sequential-reviewer/CODING_STANDARDS.md +27 -0
  55. package/dist/templates/sequential-reviewer/implement-prompt.md +53 -0
  56. package/dist/templates/sequential-reviewer/main.mts +119 -0
  57. package/dist/templates/sequential-reviewer/review-prompt.md +55 -0
  58. package/dist/templates/sequential-reviewer/template.json +4 -0
  59. package/dist/templates/simple-loop/main.mts +49 -0
  60. package/dist/templates/simple-loop/prompt.md +53 -0
  61. package/dist/templates/simple-loop/template.json +4 -0
  62. 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,7 @@
1
+ import { createRequire } from 'node:module';
2
+ export { noSandbox } from '../chunk-72UVAC7B.js';
3
+ import '../chunk-NGBM7T3E.js';
4
+
5
+ createRequire(import.meta.url);
6
+ //# sourceMappingURL=no-sandbox.js.map
7
+ //# sourceMappingURL=no-sandbox.js.map
@@ -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 };