@ai-hero/sandcastle 0.6.5 → 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 (229) hide show
  1. package/README.md +137 -62
  2. package/dist/{MountConfig.d.ts → MountConfig-CmXclHA5.d.ts} +3 -2
  3. package/dist/{SandboxProvider.d.ts → SandboxProvider-EkSMuBp8.d.ts} +25 -39
  4. package/dist/chunk-52CIJF45.js +25569 -0
  5. package/dist/chunk-52CIJF45.js.map +1 -0
  6. package/dist/chunk-5VM5QZ26.js +26988 -0
  7. package/dist/chunk-5VM5QZ26.js.map +1 -0
  8. package/dist/chunk-72UVAC7B.js +99 -0
  9. package/dist/chunk-72UVAC7B.js.map +1 -0
  10. package/dist/chunk-BIWNFKGV.js +22 -0
  11. package/dist/chunk-BIWNFKGV.js.map +1 -0
  12. package/dist/chunk-NGBM7T3E.js +76 -0
  13. package/dist/chunk-NGBM7T3E.js.map +1 -0
  14. package/dist/chunk-NSFQW6ML.js +362 -0
  15. package/dist/chunk-NSFQW6ML.js.map +1 -0
  16. package/dist/index.d.ts +920 -22
  17. package/dist/index.js +3212 -9
  18. package/dist/index.js.map +1 -1
  19. package/dist/main.d.ts +0 -2
  20. package/dist/main.js +19349 -13
  21. package/dist/main.js.map +1 -1
  22. package/dist/mountUtils-CCA-bbpK.d.ts +25 -0
  23. package/dist/sandboxes/daytona.d.ts +8 -5
  24. package/dist/sandboxes/daytona.js +118 -124
  25. package/dist/sandboxes/daytona.js.map +1 -1
  26. package/dist/sandboxes/docker.d.ts +10 -8
  27. package/dist/sandboxes/docker.js +8 -255
  28. package/dist/sandboxes/docker.js.map +1 -1
  29. package/dist/sandboxes/no-sandbox.d.ts +7 -4
  30. package/dist/sandboxes/no-sandbox.js +6 -114
  31. package/dist/sandboxes/no-sandbox.js.map +1 -1
  32. package/dist/sandboxes/podman.d.ts +10 -8
  33. package/dist/sandboxes/podman.js +287 -297
  34. package/dist/sandboxes/podman.js.map +1 -1
  35. package/dist/sandboxes/vercel.d.ts +7 -4
  36. package/dist/sandboxes/vercel.js +144 -165
  37. package/dist/sandboxes/vercel.js.map +1 -1
  38. package/dist/templates/sequential-reviewer/implement-prompt.md +2 -2
  39. package/dist/templates/simple-loop/prompt.md +2 -2
  40. package/package.json +15 -14
  41. package/dist/AgentProvider.d.ts +0 -134
  42. package/dist/AgentProvider.d.ts.map +0 -1
  43. package/dist/AgentProvider.js +0 -647
  44. package/dist/AgentProvider.js.map +0 -1
  45. package/dist/AgentStreamEmitter.d.ts +0 -36
  46. package/dist/AgentStreamEmitter.d.ts.map +0 -1
  47. package/dist/AgentStreamEmitter.js +0 -21
  48. package/dist/AgentStreamEmitter.js.map +0 -1
  49. package/dist/CopyToWorktree.d.ts +0 -15
  50. package/dist/CopyToWorktree.d.ts.map +0 -1
  51. package/dist/CopyToWorktree.js +0 -60
  52. package/dist/CopyToWorktree.js.map +0 -1
  53. package/dist/Display.d.ts +0 -58
  54. package/dist/Display.d.ts.map +0 -1
  55. package/dist/Display.js +0 -142
  56. package/dist/Display.js.map +0 -1
  57. package/dist/DockerLifecycle.d.ts +0 -54
  58. package/dist/DockerLifecycle.d.ts.map +0 -1
  59. package/dist/DockerLifecycle.js +0 -123
  60. package/dist/DockerLifecycle.js.map +0 -1
  61. package/dist/EnvResolver.d.ts +0 -11
  62. package/dist/EnvResolver.d.ts.map +0 -1
  63. package/dist/EnvResolver.js +0 -63
  64. package/dist/EnvResolver.js.map +0 -1
  65. package/dist/ErrorHandler.d.ts +0 -15
  66. package/dist/ErrorHandler.d.ts.map +0 -1
  67. package/dist/ErrorHandler.js +0 -85
  68. package/dist/ErrorHandler.js.map +0 -1
  69. package/dist/InitService.d.ts +0 -92
  70. package/dist/InitService.d.ts.map +0 -1
  71. package/dist/InitService.js +0 -836
  72. package/dist/InitService.js.map +0 -1
  73. package/dist/MountConfig.d.ts.map +0 -1
  74. package/dist/MountConfig.js +0 -7
  75. package/dist/MountConfig.js.map +0 -1
  76. package/dist/Orchestrator.d.ts +0 -56
  77. package/dist/Orchestrator.d.ts.map +0 -1
  78. package/dist/Orchestrator.js +0 -293
  79. package/dist/Orchestrator.js.map +0 -1
  80. package/dist/Output.d.ts +0 -107
  81. package/dist/Output.d.ts.map +0 -1
  82. package/dist/Output.js +0 -95
  83. package/dist/Output.js.map +0 -1
  84. package/dist/PodmanLifecycle.d.ts +0 -17
  85. package/dist/PodmanLifecycle.d.ts.map +0 -1
  86. package/dist/PodmanLifecycle.js +0 -45
  87. package/dist/PodmanLifecycle.js.map +0 -1
  88. package/dist/PromptArgumentSubstitution.d.ts +0 -32
  89. package/dist/PromptArgumentSubstitution.d.ts.map +0 -1
  90. package/dist/PromptArgumentSubstitution.js +0 -104
  91. package/dist/PromptArgumentSubstitution.js.map +0 -1
  92. package/dist/PromptPreprocessor.d.ts +0 -15
  93. package/dist/PromptPreprocessor.d.ts.map +0 -1
  94. package/dist/PromptPreprocessor.js +0 -55
  95. package/dist/PromptPreprocessor.js.map +0 -1
  96. package/dist/PromptResolver.d.ts +0 -21
  97. package/dist/PromptResolver.d.ts.map +0 -1
  98. package/dist/PromptResolver.js +0 -27
  99. package/dist/PromptResolver.js.map +0 -1
  100. package/dist/RecoveryMessage.d.ts +0 -15
  101. package/dist/RecoveryMessage.d.ts.map +0 -1
  102. package/dist/RecoveryMessage.js +0 -81
  103. package/dist/RecoveryMessage.js.map +0 -1
  104. package/dist/SandboxFactory.d.ts +0 -90
  105. package/dist/SandboxFactory.d.ts.map +0 -1
  106. package/dist/SandboxFactory.js +0 -324
  107. package/dist/SandboxFactory.js.map +0 -1
  108. package/dist/SandboxLifecycle.d.ts +0 -65
  109. package/dist/SandboxLifecycle.d.ts.map +0 -1
  110. package/dist/SandboxLifecycle.js +0 -296
  111. package/dist/SandboxLifecycle.js.map +0 -1
  112. package/dist/SandboxProvider.d.ts.map +0 -1
  113. package/dist/SandboxProvider.js +0 -28
  114. package/dist/SandboxProvider.js.map +0 -1
  115. package/dist/SessionStore.d.ts +0 -110
  116. package/dist/SessionStore.d.ts.map +0 -1
  117. package/dist/SessionStore.js +0 -330
  118. package/dist/SessionStore.js.map +0 -1
  119. package/dist/TextDeltaBuffer.d.ts +0 -24
  120. package/dist/TextDeltaBuffer.d.ts.map +0 -1
  121. package/dist/TextDeltaBuffer.js +0 -68
  122. package/dist/TextDeltaBuffer.js.map +0 -1
  123. package/dist/WorktreeManager.d.ts +0 -79
  124. package/dist/WorktreeManager.d.ts.map +0 -1
  125. package/dist/WorktreeManager.js +0 -283
  126. package/dist/WorktreeManager.js.map +0 -1
  127. package/dist/boundedTail.d.ts +0 -48
  128. package/dist/boundedTail.d.ts.map +0 -1
  129. package/dist/boundedTail.js +0 -64
  130. package/dist/boundedTail.js.map +0 -1
  131. package/dist/cli.d.ts +0 -30
  132. package/dist/cli.d.ts.map +0 -1
  133. package/dist/cli.js +0 -340
  134. package/dist/cli.js.map +0 -1
  135. package/dist/createSandbox.d.ts +0 -154
  136. package/dist/createSandbox.d.ts.map +0 -1
  137. package/dist/createSandbox.js +0 -476
  138. package/dist/createSandbox.js.map +0 -1
  139. package/dist/createWorktree.d.ts +0 -154
  140. package/dist/createWorktree.d.ts.map +0 -1
  141. package/dist/createWorktree.js +0 -391
  142. package/dist/createWorktree.js.map +0 -1
  143. package/dist/errors.d.ts +0 -227
  144. package/dist/errors.d.ts.map +0 -1
  145. package/dist/errors.js +0 -81
  146. package/dist/errors.js.map +0 -1
  147. package/dist/extractStructuredOutput.d.ts +0 -23
  148. package/dist/extractStructuredOutput.d.ts.map +0 -1
  149. package/dist/extractStructuredOutput.js +0 -102
  150. package/dist/extractStructuredOutput.js.map +0 -1
  151. package/dist/index.d.ts.map +0 -1
  152. package/dist/interactive.d.ts +0 -74
  153. package/dist/interactive.d.ts.map +0 -1
  154. package/dist/interactive.js +0 -279
  155. package/dist/interactive.js.map +0 -1
  156. package/dist/main.d.ts.map +0 -1
  157. package/dist/mergeProviderEnv.d.ts +0 -13
  158. package/dist/mergeProviderEnv.d.ts.map +0 -1
  159. package/dist/mergeProviderEnv.js +0 -23
  160. package/dist/mergeProviderEnv.js.map +0 -1
  161. package/dist/mountUtils.d.ts +0 -146
  162. package/dist/mountUtils.d.ts.map +0 -1
  163. package/dist/mountUtils.js +0 -301
  164. package/dist/mountUtils.js.map +0 -1
  165. package/dist/raceAbortSignal.d.ts +0 -18
  166. package/dist/raceAbortSignal.d.ts.map +0 -1
  167. package/dist/raceAbortSignal.js +0 -32
  168. package/dist/raceAbortSignal.js.map +0 -1
  169. package/dist/resolveCwd.d.ts +0 -24
  170. package/dist/resolveCwd.d.ts.map +0 -1
  171. package/dist/resolveCwd.js +0 -32
  172. package/dist/resolveCwd.js.map +0 -1
  173. package/dist/resumePrecheck.d.ts +0 -26
  174. package/dist/resumePrecheck.d.ts.map +0 -1
  175. package/dist/resumePrecheck.js +0 -40
  176. package/dist/resumePrecheck.js.map +0 -1
  177. package/dist/run.d.ts +0 -216
  178. package/dist/run.d.ts.map +0 -1
  179. package/dist/run.js +0 -313
  180. package/dist/run.js.map +0 -1
  181. package/dist/sandboxExec.d.ts +0 -12
  182. package/dist/sandboxExec.d.ts.map +0 -1
  183. package/dist/sandboxExec.js +0 -26
  184. package/dist/sandboxExec.js.map +0 -1
  185. package/dist/sandboxes/daytona.d.ts.map +0 -1
  186. package/dist/sandboxes/docker.d.ts.map +0 -1
  187. package/dist/sandboxes/no-sandbox.d.ts.map +0 -1
  188. package/dist/sandboxes/podman.d.ts.map +0 -1
  189. package/dist/sandboxes/test-bind-mount.d.ts +0 -17
  190. package/dist/sandboxes/test-bind-mount.d.ts.map +0 -1
  191. package/dist/sandboxes/test-bind-mount.js +0 -92
  192. package/dist/sandboxes/test-bind-mount.js.map +0 -1
  193. package/dist/sandboxes/test-isolated.d.ts +0 -17
  194. package/dist/sandboxes/test-isolated.d.ts.map +0 -1
  195. package/dist/sandboxes/test-isolated.js +0 -98
  196. package/dist/sandboxes/test-isolated.js.map +0 -1
  197. package/dist/sandboxes/vercel.d.ts.map +0 -1
  198. package/dist/shutdownRegistry.d.ts +0 -30
  199. package/dist/shutdownRegistry.d.ts.map +0 -1
  200. package/dist/shutdownRegistry.js +0 -73
  201. package/dist/shutdownRegistry.js.map +0 -1
  202. package/dist/startSandbox.d.ts +0 -50
  203. package/dist/startSandbox.d.ts.map +0 -1
  204. package/dist/startSandbox.js +0 -117
  205. package/dist/startSandbox.js.map +0 -1
  206. package/dist/syncIn.d.ts +0 -24
  207. package/dist/syncIn.d.ts.map +0 -1
  208. package/dist/syncIn.js +0 -107
  209. package/dist/syncIn.js.map +0 -1
  210. package/dist/syncOut.d.ts +0 -27
  211. package/dist/syncOut.d.ts.map +0 -1
  212. package/dist/syncOut.js +0 -271
  213. package/dist/syncOut.js.map +0 -1
  214. package/dist/templates.d.ts +0 -2
  215. package/dist/templates.d.ts.map +0 -1
  216. package/dist/templates.js +0 -26
  217. package/dist/templates.js.map +0 -1
  218. package/dist/terminalCleanup.d.ts +0 -30
  219. package/dist/terminalCleanup.d.ts.map +0 -1
  220. package/dist/terminalCleanup.js +0 -37
  221. package/dist/terminalCleanup.js.map +0 -1
  222. package/dist/testSandbox.d.ts +0 -8
  223. package/dist/testSandbox.d.ts.map +0 -1
  224. package/dist/testSandbox.js +0 -109
  225. package/dist/testSandbox.js.map +0 -1
  226. package/dist/testSetup.d.ts +0 -2
  227. package/dist/testSetup.d.ts.map +0 -1
  228. package/dist/testSetup.js +0 -29
  229. package/dist/testSetup.js.map +0 -1
@@ -1,309 +1,299 @@
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-7"), 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 { defaultImageName, resolveUserMounts, formatVolumeMount, processFileMountParents, } from "../mountUtils.js";
13
- import { BoundedTail, MAX_TAIL_CHARS } from "../boundedTail.js";
14
- import { registerShutdown } from "../shutdownRegistry.js";
15
- /**
16
- * Create a Podman sandbox provider.
17
- *
18
- * The returned provider creates Podman containers with bind-mounts
19
- * for the worktree and git directories. Calls the `podman` binary
20
- * on PATH directly. On macOS/Windows, verifies that a Podman Machine
21
- * is running before container creation.
22
- */
23
- export const podman = (options) => {
24
- const configuredImageName = options?.imageName;
25
- const selinuxLabel = options?.selinuxLabel ?? "z";
26
- const userns = options?.userns ?? "keep-id";
27
- const containerUid = options?.containerUid ?? 1000;
28
- const containerGid = options?.containerGid ?? 1000;
29
- const maxOutputTailChars = options?.maxOutputTailChars ?? MAX_TAIL_CHARS;
30
- const sandboxHomedir = "/home/agent";
31
- const userMounts = options?.mounts
32
- ? resolveUserMounts(options.mounts, sandboxHomedir)
33
- : [];
34
- // Validate file mounts and collect parent dirs to create at container start.
35
- // Throws at construction time if any file mount parent is outside sandboxHomedir.
36
- const parentDirsToCreate = processFileMountParents(userMounts, sandboxHomedir);
37
- return createBindMountSandboxProvider({
38
- name: "podman",
39
- env: options?.env,
40
- sandboxHomedir,
41
- create: async (createOptions) => {
42
- const containerName = `sandcastle-${randomUUID()}`;
43
- const worktreePath = createOptions.mounts.find((m) => m.hostPath === createOptions.worktreePath)?.sandboxPath ?? "/home/agent/workspace";
44
- // Build volume mount strings with optional SELinux label (internal + user mounts)
45
- const allMounts = [...createOptions.mounts, ...userMounts];
46
- const volumeMounts = allMounts.map((m) => formatVolumeMount(m, selinuxLabel));
47
- // Resolve image name
48
- const imageName = configuredImageName ?? defaultImageName(createOptions.hostRepoPath);
49
- // Pre-flight: check Podman Machine on macOS/Windows
50
- if (process.platform === "darwin" || process.platform === "win32") {
51
- await checkPodmanMachine();
1
+ import { createRequire } from 'node:module';
2
+ import { resolveUserMounts, processFileMountParents, formatVolumeMount, defaultImageName, registerShutdown } from '../chunk-5VM5QZ26.js';
3
+ export { defaultImageName } from '../chunk-5VM5QZ26.js';
4
+ import { createBindMountSandboxProvider } from '../chunk-BIWNFKGV.js';
5
+ import { MAX_TAIL_CHARS, BoundedTail } from '../chunk-NGBM7T3E.js';
6
+ import { execFile, execFileSync, spawn } from 'child_process';
7
+ import { randomUUID } from 'crypto';
8
+ import { createInterface } from 'readline';
9
+
10
+ createRequire(import.meta.url);
11
+ var podman = (options) => {
12
+ const configuredImageName = options?.imageName;
13
+ const selinuxLabel = options?.selinuxLabel ?? "z";
14
+ const userns = options?.userns ?? "keep-id";
15
+ const containerUid = options?.containerUid ?? 1e3;
16
+ const containerGid = options?.containerGid ?? 1e3;
17
+ const maxOutputTailChars = options?.maxOutputTailChars ?? MAX_TAIL_CHARS;
18
+ const sandboxHomedir = "/home/agent";
19
+ const userMounts = options?.mounts ? resolveUserMounts(options.mounts, sandboxHomedir) : [];
20
+ const parentDirsToCreate = processFileMountParents(
21
+ userMounts,
22
+ sandboxHomedir
23
+ );
24
+ return createBindMountSandboxProvider({
25
+ name: "podman",
26
+ env: options?.env,
27
+ sandboxHomedir,
28
+ create: async (createOptions) => {
29
+ const containerName = `sandcastle-${randomUUID()}`;
30
+ const worktreePath = createOptions.mounts.find(
31
+ (m) => m.hostPath === createOptions.worktreePath
32
+ )?.sandboxPath ?? "/home/agent/workspace";
33
+ const allMounts = [...createOptions.mounts, ...userMounts];
34
+ const volumeMounts = allMounts.map(
35
+ (m) => formatVolumeMount(m, selinuxLabel)
36
+ );
37
+ const imageName = configuredImageName ?? defaultImageName(createOptions.hostRepoPath);
38
+ if (process.platform === "darwin" || process.platform === "win32") {
39
+ await checkPodmanMachine();
40
+ }
41
+ await checkImageExists(imageName);
42
+ const env = { ...createOptions.env, HOME: "/home/agent" };
43
+ const envArgs = Object.entries(env).flatMap(([key, value]) => [
44
+ "-e",
45
+ `${key}=${value}`
46
+ ]);
47
+ const volumeArgs = volumeMounts.flatMap((v) => ["-v", v]);
48
+ const usernsArgs = userns ? [`--userns=keep-id:uid=${containerUid},gid=${containerGid}`] : [];
49
+ const userArgs = ["--user", `${containerUid}:${containerGid}`];
50
+ const networks = options?.network ? Array.isArray(options.network) ? options.network : [options.network] : [];
51
+ const networkArgs = networks.flatMap((n) => ["--network", n]);
52
+ const groupArgs = (options?.groups ?? []).flatMap((g) => [
53
+ "--group-add",
54
+ String(g)
55
+ ]);
56
+ const deviceArgs = (options?.devices ?? []).flatMap((d) => [
57
+ "--device",
58
+ d
59
+ ]);
60
+ const cpusArgs = options?.cpus !== void 0 ? ["--cpus", String(options.cpus)] : [];
61
+ await new Promise((resolve, reject) => {
62
+ execFile(
63
+ "podman",
64
+ [
65
+ "run",
66
+ "-d",
67
+ "--name",
68
+ containerName,
69
+ ...userArgs,
70
+ ...usernsArgs,
71
+ ...networkArgs,
72
+ ...groupArgs,
73
+ ...deviceArgs,
74
+ ...cpusArgs,
75
+ "-w",
76
+ worktreePath,
77
+ ...envArgs,
78
+ ...volumeArgs,
79
+ "--entrypoint",
80
+ "sleep",
81
+ imageName,
82
+ "infinity"
83
+ ],
84
+ (error) => {
85
+ if (error) {
86
+ reject(new Error(`podman run failed: ${error.message}`));
87
+ } else {
88
+ resolve();
52
89
  }
53
- // Pre-flight: verify image exists locally
54
- await checkImageExists(imageName);
55
- const env = { ...createOptions.env, HOME: "/home/agent" };
56
- const envArgs = Object.entries(env).flatMap(([key, value]) => [
57
- "-e",
58
- `${key}=${value}`,
59
- ]);
60
- const volumeArgs = volumeMounts.flatMap((v) => ["-v", v]);
61
- const usernsArgs = userns
62
- ? [`--userns=keep-id:uid=${containerUid},gid=${containerGid}`]
63
- : [];
64
- const userArgs = ["--user", `${containerUid}:${containerGid}`];
65
- const networks = options?.network
66
- ? Array.isArray(options.network)
67
- ? options.network
68
- : [options.network]
69
- : [];
70
- const networkArgs = networks.flatMap((n) => ["--network", n]);
71
- const groupArgs = (options?.groups ?? []).flatMap((g) => [
72
- "--group-add",
73
- String(g),
74
- ]);
75
- const deviceArgs = (options?.devices ?? []).flatMap((d) => [
76
- "--device",
77
- d,
78
- ]);
79
- const cpusArgs = options?.cpus !== undefined ? ["--cpus", String(options.cpus)] : [];
80
- // Start container via podman run
81
- await new Promise((resolve, reject) => {
82
- execFile("podman", [
83
- "run",
84
- "-d",
85
- "--name",
86
- containerName,
87
- ...userArgs,
88
- ...usernsArgs,
89
- ...networkArgs,
90
- ...groupArgs,
91
- ...deviceArgs,
92
- ...cpusArgs,
93
- "-w",
94
- worktreePath,
95
- ...envArgs,
96
- ...volumeArgs,
97
- "--entrypoint",
98
- "sleep",
99
- imageName,
100
- "infinity",
101
- ], (error) => {
102
- if (error) {
103
- reject(new Error(`podman run failed: ${error.message}`));
104
- }
105
- else {
106
- resolve();
107
- }
108
- });
90
+ }
91
+ );
92
+ });
93
+ for (const dir of parentDirsToCreate) {
94
+ await new Promise((resolve, reject) => {
95
+ execFile(
96
+ "podman",
97
+ [
98
+ "exec",
99
+ "--user",
100
+ "0:0",
101
+ containerName,
102
+ "sh",
103
+ "-c",
104
+ `mkdir -p "$1" && chown "$2" "$1"`,
105
+ "sh",
106
+ dir,
107
+ `${containerUid}:${containerGid}`
108
+ ],
109
+ (error) => {
110
+ if (error) {
111
+ reject(
112
+ new Error(
113
+ `Failed to create parent directory '${dir}' in container: ${error.message}`
114
+ )
115
+ );
116
+ } else {
117
+ resolve();
118
+ }
119
+ }
120
+ );
121
+ });
122
+ }
123
+ const removeContainerSync = () => {
124
+ try {
125
+ execFileSync("podman", ["rm", "-f", containerName], {
126
+ stdio: "ignore",
127
+ timeout: 5e3
128
+ });
129
+ } catch {
130
+ }
131
+ };
132
+ const unregisterShutdown = registerShutdown(removeContainerSync);
133
+ const handle = {
134
+ worktreePath,
135
+ exec: (command, opts) => {
136
+ const effectiveCommand = opts?.sudo ? `sudo ${command}` : command;
137
+ const args = ["exec"];
138
+ if (opts?.stdin !== void 0) args.push("-i");
139
+ if (opts?.cwd) args.push("-w", opts.cwd);
140
+ args.push(containerName, "sh", "-c", effectiveCommand);
141
+ return new Promise((resolve, reject) => {
142
+ const proc = spawn("podman", args, {
143
+ stdio: [
144
+ opts?.stdin !== void 0 ? "pipe" : "ignore",
145
+ "pipe",
146
+ "pipe"
147
+ ]
148
+ });
149
+ if (opts?.stdin !== void 0) {
150
+ proc.stdin.write(opts.stdin);
151
+ proc.stdin.end();
152
+ }
153
+ proc.on("error", (error) => {
154
+ reject(new Error(`podman exec failed: ${error.message}`));
109
155
  });
110
- // Create parent directories for file mounts and chown to the container user
111
- for (const dir of parentDirsToCreate) {
112
- await new Promise((resolve, reject) => {
113
- execFile("podman", [
114
- "exec",
115
- "--user",
116
- "0:0",
117
- containerName,
118
- "sh",
119
- "-c",
120
- `mkdir -p "$1" && chown "$2" "$1"`,
121
- "sh",
122
- dir,
123
- `${containerUid}:${containerGid}`,
124
- ], (error) => {
125
- if (error) {
126
- reject(new Error(`Failed to create parent directory '${dir}' in container: ${error.message}`));
127
- }
128
- else {
129
- resolve();
130
- }
131
- });
156
+ if (opts?.onLine) {
157
+ const onLine = opts.onLine;
158
+ const stdoutTail = new BoundedTail(maxOutputTailChars, "\n");
159
+ const stderrTail = new BoundedTail(maxOutputTailChars, "");
160
+ const rl = createInterface({ input: proc.stdout });
161
+ rl.on("line", (line) => {
162
+ stdoutTail.push(line);
163
+ onLine(line);
164
+ });
165
+ proc.stderr.on("data", (chunk) => {
166
+ stderrTail.push(chunk.toString());
167
+ });
168
+ proc.on("close", (code) => {
169
+ resolve({
170
+ stdout: stdoutTail.toString(),
171
+ stderr: stderrTail.toString(),
172
+ exitCode: code ?? 0
173
+ });
174
+ });
175
+ } else {
176
+ const stdoutChunks = [];
177
+ const stderrChunks = [];
178
+ proc.stdout.on("data", (chunk) => {
179
+ stdoutChunks.push(chunk.toString());
180
+ });
181
+ proc.stderr.on("data", (chunk) => {
182
+ stderrChunks.push(chunk.toString());
183
+ });
184
+ proc.on("close", (code) => {
185
+ resolve({
186
+ stdout: stdoutChunks.join(""),
187
+ stderr: stderrChunks.join(""),
188
+ exitCode: code ?? 0
132
189
  });
190
+ });
133
191
  }
134
- // Register synchronous container cleanup via the shared shutdown registry
135
- // so concurrent sandboxes share a single exit/SIGINT/SIGTERM listener
136
- // instead of tripping Node's MaxListenersExceededWarning.
137
- const removeContainerSync = () => {
138
- try {
139
- execFileSync("podman", ["rm", "-f", containerName], {
140
- stdio: "ignore",
141
- timeout: 5000,
142
- });
143
- }
144
- catch {
145
- /* best-effort */
146
- }
147
- };
148
- const unregisterShutdown = registerShutdown(removeContainerSync);
149
- const handle = {
150
- worktreePath,
151
- exec: (command, opts) => {
152
- const effectiveCommand = opts?.sudo ? `sudo ${command}` : command;
153
- const args = ["exec"];
154
- if (opts?.stdin !== undefined)
155
- args.push("-i");
156
- if (opts?.cwd)
157
- args.push("-w", opts.cwd);
158
- args.push(containerName, "sh", "-c", effectiveCommand);
159
- return new Promise((resolve, reject) => {
160
- const proc = spawn("podman", args, {
161
- stdio: [
162
- opts?.stdin !== undefined ? "pipe" : "ignore",
163
- "pipe",
164
- "pipe",
165
- ],
166
- });
167
- if (opts?.stdin !== undefined) {
168
- proc.stdin.write(opts.stdin);
169
- proc.stdin.end();
170
- }
171
- proc.on("error", (error) => {
172
- reject(new Error(`podman exec failed: ${error.message}`));
173
- });
174
- if (opts?.onLine) {
175
- const onLine = opts.onLine;
176
- const stdoutTail = new BoundedTail(maxOutputTailChars, "\n");
177
- const stderrTail = new BoundedTail(maxOutputTailChars, "");
178
- const rl = createInterface({ input: proc.stdout });
179
- rl.on("line", (line) => {
180
- stdoutTail.push(line);
181
- onLine(line);
182
- });
183
- proc.stderr.on("data", (chunk) => {
184
- stderrTail.push(chunk.toString());
185
- });
186
- proc.on("close", (code) => {
187
- resolve({
188
- stdout: stdoutTail.toString(),
189
- stderr: stderrTail.toString(),
190
- exitCode: code ?? 0,
191
- });
192
- });
193
- }
194
- else {
195
- const stdoutChunks = [];
196
- const stderrChunks = [];
197
- proc.stdout.on("data", (chunk) => {
198
- stdoutChunks.push(chunk.toString());
199
- });
200
- proc.stderr.on("data", (chunk) => {
201
- stderrChunks.push(chunk.toString());
202
- });
203
- proc.on("close", (code) => {
204
- resolve({
205
- stdout: stdoutChunks.join(""),
206
- stderr: stderrChunks.join(""),
207
- exitCode: code ?? 0,
208
- });
209
- });
210
- }
211
- });
212
- },
213
- interactiveExec: (args, opts) => {
214
- return new Promise((resolve, reject) => {
215
- const podmanArgs = ["exec"];
216
- // Allocate a pseudo-terminal when stdin looks like a TTY
217
- if ("isTTY" in opts.stdin &&
218
- opts.stdin.isTTY) {
219
- podmanArgs.push("-it");
220
- }
221
- else {
222
- podmanArgs.push("-i");
223
- }
224
- if (opts.cwd)
225
- podmanArgs.push("-w", opts.cwd);
226
- podmanArgs.push(containerName, ...args);
227
- const proc = spawn("podman", podmanArgs, {
228
- stdio: [opts.stdin, opts.stdout, opts.stderr],
229
- });
230
- proc.on("error", (error) => {
231
- reject(new Error(`podman exec failed: ${error.message}`));
232
- });
233
- proc.on("close", (code) => {
234
- resolve({ exitCode: code ?? 0 });
235
- });
236
- });
237
- },
238
- copyFileIn: (hostPath, sandboxPath) => new Promise((resolve, reject) => {
239
- execFile("podman", ["cp", hostPath, `${containerName}:${sandboxPath}`], (error) => {
240
- if (error) {
241
- reject(new Error(`podman cp (in) failed: ${error.message}`));
242
- }
243
- else {
244
- resolve();
245
- }
246
- });
247
- }),
248
- copyFileOut: (sandboxPath, hostPath) => new Promise((resolve, reject) => {
249
- execFile("podman", ["cp", `${containerName}:${sandboxPath}`, hostPath], (error) => {
250
- if (error) {
251
- reject(new Error(`podman cp (out) failed: ${error.message}`));
252
- }
253
- else {
254
- resolve();
255
- }
256
- });
257
- }),
258
- close: async () => {
259
- unregisterShutdown();
260
- await new Promise((resolve, reject) => {
261
- execFile("podman", ["rm", "-f", containerName], (error) => {
262
- if (error) {
263
- reject(new Error(`podman rm failed: ${error.message}`));
264
- }
265
- else {
266
- resolve();
267
- }
268
- });
269
- });
270
- },
271
- };
272
- return handle;
192
+ });
273
193
  },
274
- });
275
- };
276
- // Re-export for backwards compatibility
277
- export { defaultImageName };
278
- const checkImageExists = (imageName) => new Promise((resolve, reject) => {
279
- execFile("podman", ["image", "inspect", imageName], (error) => {
280
- if (error) {
281
- reject(new Error(`Image '${imageName}' not found locally. Build it first with 'podman build -t ${imageName} .'`));
282
- }
283
- else {
284
- resolve();
285
- }
286
- });
287
- });
288
- const podmanMachineError = () => new Error("Podman Machine is not running. Run 'podman machine init && podman machine start' first.");
289
- const checkPodmanMachine = () => new Promise((resolve, reject) => {
290
- execFile("podman", ["machine", "list", "--format", "json"], (error, stdout) => {
291
- if (error) {
292
- reject(podmanMachineError());
293
- return;
294
- }
295
- try {
296
- const machines = JSON.parse(stdout.toString());
297
- if (machines.some((m) => m.Running)) {
194
+ interactiveExec: (args, opts) => {
195
+ return new Promise((resolve, reject) => {
196
+ const podmanArgs = ["exec"];
197
+ if ("isTTY" in opts.stdin && opts.stdin.isTTY) {
198
+ podmanArgs.push("-it");
199
+ } else {
200
+ podmanArgs.push("-i");
201
+ }
202
+ if (opts.cwd) podmanArgs.push("-w", opts.cwd);
203
+ podmanArgs.push(containerName, ...args);
204
+ const proc = spawn("podman", podmanArgs, {
205
+ stdio: [opts.stdin, opts.stdout, opts.stderr]
206
+ });
207
+ proc.on("error", (error) => {
208
+ reject(new Error(`podman exec failed: ${error.message}`));
209
+ });
210
+ proc.on("close", (code) => {
211
+ resolve({ exitCode: code ?? 0 });
212
+ });
213
+ });
214
+ },
215
+ copyFileIn: (hostPath, sandboxPath) => new Promise((resolve, reject) => {
216
+ execFile(
217
+ "podman",
218
+ ["cp", hostPath, `${containerName}:${sandboxPath}`],
219
+ (error) => {
220
+ if (error) {
221
+ reject(new Error(`podman cp (in) failed: ${error.message}`));
222
+ } else {
298
223
  resolve();
224
+ }
299
225
  }
300
- else {
301
- reject(podmanMachineError());
226
+ );
227
+ }),
228
+ copyFileOut: (sandboxPath, hostPath) => new Promise((resolve, reject) => {
229
+ execFile(
230
+ "podman",
231
+ ["cp", `${containerName}:${sandboxPath}`, hostPath],
232
+ (error) => {
233
+ if (error) {
234
+ reject(new Error(`podman cp (out) failed: ${error.message}`));
235
+ } else {
236
+ resolve();
237
+ }
302
238
  }
239
+ );
240
+ }),
241
+ close: async () => {
242
+ unregisterShutdown();
243
+ await new Promise((resolve, reject) => {
244
+ execFile("podman", ["rm", "-f", containerName], (error) => {
245
+ if (error) {
246
+ reject(new Error(`podman rm failed: ${error.message}`));
247
+ } else {
248
+ resolve();
249
+ }
250
+ });
251
+ });
303
252
  }
304
- catch {
305
- reject(podmanMachineError());
253
+ };
254
+ return handle;
255
+ }
256
+ });
257
+ };
258
+ var checkImageExists = (imageName) => new Promise((resolve, reject) => {
259
+ execFile("podman", ["image", "inspect", imageName], (error) => {
260
+ if (error) {
261
+ reject(
262
+ new Error(
263
+ `Image '${imageName}' not found locally. Build it first with 'podman build -t ${imageName} .'`
264
+ )
265
+ );
266
+ } else {
267
+ resolve();
268
+ }
269
+ });
270
+ });
271
+ var podmanMachineError = () => new Error(
272
+ "Podman Machine is not running. Run 'podman machine init && podman machine start' first."
273
+ );
274
+ var checkPodmanMachine = () => new Promise((resolve, reject) => {
275
+ execFile(
276
+ "podman",
277
+ ["machine", "list", "--format", "json"],
278
+ (error, stdout) => {
279
+ if (error) {
280
+ reject(podmanMachineError());
281
+ return;
282
+ }
283
+ try {
284
+ const machines = JSON.parse(stdout.toString());
285
+ if (machines.some((m) => m.Running)) {
286
+ resolve();
287
+ } else {
288
+ reject(podmanMachineError());
306
289
  }
307
- });
290
+ } catch {
291
+ reject(podmanMachineError());
292
+ }
293
+ }
294
+ );
308
295
  });
296
+
297
+ export { podman };
298
+ //# sourceMappingURL=podman.js.map
309
299
  //# sourceMappingURL=podman.js.map