@elie-laloum/outpost 11.1.2 → 11.2.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 (596) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +2 -1
  3. package/SECURITY.md +5 -1
  4. package/dist/adapters/agents/antigravity/antigravity-adapter.js +9 -0
  5. package/dist/adapters/agents/antigravity/antigravity-adapter.js.map +1 -1
  6. package/dist/adapters/agents/antigravity/antigravity-hook-plan.d.ts +3 -0
  7. package/dist/adapters/agents/antigravity/antigravity-hook-plan.js +39 -0
  8. package/dist/adapters/agents/antigravity/antigravity-hook-plan.js.map +1 -0
  9. package/dist/adapters/agents/antigravity/antigravity-hooks.constants.d.ts +3 -0
  10. package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js +394 -0
  11. package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js.map +1 -0
  12. package/dist/adapters/agents/antigravity/antigravity-hooks.d.ts +2 -0
  13. package/dist/adapters/agents/antigravity/antigravity-hooks.js +6 -0
  14. package/dist/adapters/agents/antigravity/antigravity-hooks.js.map +1 -0
  15. package/dist/adapters/agents/antigravity/antigravity-hooks.types.d.ts +114 -0
  16. package/dist/adapters/agents/antigravity/antigravity-hooks.types.js +2 -0
  17. package/dist/adapters/agents/antigravity/antigravity-hooks.types.js.map +1 -0
  18. package/dist/adapters/agents/antigravity/antigravity.types.d.ts +1 -0
  19. package/dist/adapters/agents/antigravity/antigravity.types.js.map +1 -1
  20. package/dist/adapters/agents/authentication.js +1 -0
  21. package/dist/adapters/agents/authentication.js.map +1 -1
  22. package/dist/adapters/agents/claude/claude-adapter.js +15 -0
  23. package/dist/adapters/agents/claude/claude-adapter.js.map +1 -1
  24. package/dist/adapters/agents/claude/claude-hook-plan.constants.d.ts +1 -0
  25. package/dist/adapters/agents/claude/claude-hook-plan.constants.js +20 -0
  26. package/dist/adapters/agents/claude/claude-hook-plan.constants.js.map +1 -0
  27. package/dist/adapters/agents/claude/claude-hook-plan.d.ts +4 -0
  28. package/dist/adapters/agents/claude/claude-hook-plan.js +60 -0
  29. package/dist/adapters/agents/claude/claude-hook-plan.js.map +1 -0
  30. package/dist/adapters/agents/claude/claude-hooks.constants.d.ts +3 -0
  31. package/dist/adapters/agents/claude/claude-hooks.constants.js +2968 -0
  32. package/dist/adapters/agents/claude/claude-hooks.constants.js.map +1 -0
  33. package/dist/adapters/agents/claude/claude-hooks.d.ts +2 -0
  34. package/dist/adapters/agents/claude/claude-hooks.js +6 -0
  35. package/dist/adapters/agents/claude/claude-hooks.js.map +1 -0
  36. package/dist/adapters/agents/claude/claude-hooks.types.d.ts +900 -0
  37. package/dist/adapters/agents/claude/claude-hooks.types.js +2 -0
  38. package/dist/adapters/agents/claude/claude-hooks.types.js.map +1 -0
  39. package/dist/adapters/agents/claude/claude-profile.d.ts +1 -0
  40. package/dist/adapters/agents/claude/claude-profile.js +23 -11
  41. package/dist/adapters/agents/claude/claude-profile.js.map +1 -1
  42. package/dist/adapters/agents/codex/codex-adapter.js +17 -1
  43. package/dist/adapters/agents/codex/codex-adapter.js.map +1 -1
  44. package/dist/adapters/agents/codex/codex-authentication.js +1 -0
  45. package/dist/adapters/agents/codex/codex-authentication.js.map +1 -1
  46. package/dist/adapters/agents/codex/codex-conversations.constants.d.ts +1 -0
  47. package/dist/adapters/agents/codex/codex-conversations.constants.js +2 -0
  48. package/dist/adapters/agents/codex/codex-conversations.constants.js.map +1 -0
  49. package/dist/adapters/agents/codex/codex-conversations.js +8 -5
  50. package/dist/adapters/agents/codex/codex-conversations.js.map +1 -1
  51. package/dist/adapters/agents/codex/codex-hook-plan.d.ts +3 -0
  52. package/dist/adapters/agents/codex/codex-hook-plan.js +72 -0
  53. package/dist/adapters/agents/codex/codex-hook-plan.js.map +1 -0
  54. package/dist/adapters/agents/codex/codex-hooks.constants.d.ts +3 -0
  55. package/dist/adapters/agents/codex/codex-hooks.constants.js +942 -0
  56. package/dist/adapters/agents/codex/codex-hooks.constants.js.map +1 -0
  57. package/dist/adapters/agents/codex/codex-hooks.d.ts +2 -0
  58. package/dist/adapters/agents/codex/codex-hooks.js +6 -0
  59. package/dist/adapters/agents/codex/codex-hooks.js.map +1 -0
  60. package/dist/adapters/agents/codex/codex-hooks.types.d.ts +255 -0
  61. package/dist/adapters/agents/codex/codex-hooks.types.js +2 -0
  62. package/dist/adapters/agents/codex/codex-hooks.types.js.map +1 -0
  63. package/dist/adapters/agents/codex/codex-provider.js +2 -2
  64. package/dist/adapters/agents/codex/codex-provider.js.map +1 -1
  65. package/dist/adapters/agents/copilot/copilot-adapter.js +14 -0
  66. package/dist/adapters/agents/copilot/copilot-adapter.js.map +1 -1
  67. package/dist/adapters/agents/copilot/copilot-hook-plan.d.ts +3 -0
  68. package/dist/adapters/agents/copilot/copilot-hook-plan.js +26 -0
  69. package/dist/adapters/agents/copilot/copilot-hook-plan.js.map +1 -0
  70. package/dist/adapters/agents/copilot/copilot-hooks.constants.d.ts +3 -0
  71. package/dist/adapters/agents/copilot/copilot-hooks.constants.js +528 -0
  72. package/dist/adapters/agents/copilot/copilot-hooks.constants.js.map +1 -0
  73. package/dist/adapters/agents/copilot/copilot-hooks.d.ts +2 -0
  74. package/dist/adapters/agents/copilot/copilot-hooks.js +6 -0
  75. package/dist/adapters/agents/copilot/copilot-hooks.js.map +1 -0
  76. package/dist/adapters/agents/copilot/copilot-hooks.types.d.ts +166 -0
  77. package/dist/adapters/agents/copilot/copilot-hooks.types.js +2 -0
  78. package/dist/adapters/agents/copilot/copilot-hooks.types.js.map +1 -0
  79. package/dist/adapters/agents/copilot/copilot.types.d.ts +1 -0
  80. package/dist/adapters/agents/copilot/copilot.types.js.map +1 -1
  81. package/dist/adapters/agents/hook-groups.d.ts +2 -0
  82. package/dist/adapters/agents/hook-groups.js +17 -0
  83. package/dist/adapters/agents/hook-groups.js.map +1 -0
  84. package/dist/adapters/agents/kimi/kimi-adapter.js +16 -1
  85. package/dist/adapters/agents/kimi/kimi-adapter.js.map +1 -1
  86. package/dist/adapters/agents/kimi/kimi-hook-plan.d.ts +3 -0
  87. package/dist/adapters/agents/kimi/kimi-hook-plan.js +33 -0
  88. package/dist/adapters/agents/kimi/kimi-hook-plan.js.map +1 -0
  89. package/dist/adapters/agents/kimi/kimi-hooks.constants.d.ts +3 -0
  90. package/dist/adapters/agents/kimi/kimi-hooks.constants.js +747 -0
  91. package/dist/adapters/agents/kimi/kimi-hooks.constants.js.map +1 -0
  92. package/dist/adapters/agents/kimi/kimi-hooks.d.ts +2 -0
  93. package/dist/adapters/agents/kimi/kimi-hooks.js +6 -0
  94. package/dist/adapters/agents/kimi/kimi-hooks.js.map +1 -0
  95. package/dist/adapters/agents/kimi/kimi-hooks.types.d.ts +226 -0
  96. package/dist/adapters/agents/kimi/kimi-hooks.types.js +2 -0
  97. package/dist/adapters/agents/kimi/kimi-hooks.types.js.map +1 -0
  98. package/dist/adapters/agents/kimi/kimi-request.js +2 -0
  99. package/dist/adapters/agents/kimi/kimi-request.js.map +1 -1
  100. package/dist/adapters/agents/kimi/kimi.constants.d.ts +1 -0
  101. package/dist/adapters/agents/kimi/kimi.constants.js +1 -0
  102. package/dist/adapters/agents/kimi/kimi.constants.js.map +1 -1
  103. package/dist/adapters/agents/kimi/kimi.types.d.ts +1 -0
  104. package/dist/adapters/agents/kimi/kimi.types.js.map +1 -1
  105. package/dist/adapters/agents/native-hooks.d.ts +4 -0
  106. package/dist/adapters/agents/native-hooks.js +63 -0
  107. package/dist/adapters/agents/native-hooks.js.map +1 -0
  108. package/dist/adapters/agents/native-hooks.types.d.ts +22 -0
  109. package/dist/adapters/agents/native-hooks.types.js +2 -0
  110. package/dist/adapters/agents/native-hooks.types.js.map +1 -0
  111. package/dist/adapters/agents/settings.types.d.ts +2 -0
  112. package/dist/adapters/agents/settings.types.js.map +1 -1
  113. package/dist/adapters/decisions/system-one-provider.constants.d.ts +1 -1
  114. package/dist/adapters/decisions/system-one-provider.constants.js +1 -1
  115. package/dist/adapters/decisions/system-one-provider.constants.js.map +1 -1
  116. package/dist/adapters/decisions/system-one-provider.js +8 -21
  117. package/dist/adapters/decisions/system-one-provider.js.map +1 -1
  118. package/dist/adapters/models/anthropic-model-provider.constants.d.ts +1 -0
  119. package/dist/adapters/models/anthropic-model-provider.constants.js +4 -0
  120. package/dist/adapters/models/anthropic-model-provider.constants.js.map +1 -1
  121. package/dist/adapters/models/anthropic-model-provider.js +7 -1
  122. package/dist/adapters/models/anthropic-model-provider.js.map +1 -1
  123. package/dist/adapters/models/http-failure.d.ts +3 -0
  124. package/dist/adapters/models/http-failure.js +43 -0
  125. package/dist/adapters/models/http-failure.js.map +1 -0
  126. package/dist/adapters/models/http-provider.js +14 -20
  127. package/dist/adapters/models/http-provider.js.map +1 -1
  128. package/dist/adapters/models/model-protocol.types.d.ts +1 -0
  129. package/dist/adapters/models/model-protocol.types.js.map +1 -1
  130. package/dist/adapters/models/model.constants.d.ts +2 -0
  131. package/dist/adapters/models/model.constants.js +17 -0
  132. package/dist/adapters/models/model.constants.js.map +1 -1
  133. package/dist/adapters/models/openai-chat-stream.js +3 -1
  134. package/dist/adapters/models/openai-chat-stream.js.map +1 -1
  135. package/dist/adapters/models/openai-responses.js +4 -0
  136. package/dist/adapters/models/openai-responses.js.map +1 -1
  137. package/dist/adapters/secrets/aws.js +5 -4
  138. package/dist/adapters/secrets/aws.js.map +1 -1
  139. package/dist/adapters/secrets/azure.constants.d.ts +1 -0
  140. package/dist/adapters/secrets/azure.constants.js +2 -0
  141. package/dist/adapters/secrets/azure.constants.js.map +1 -0
  142. package/dist/adapters/secrets/azure.js +2 -1
  143. package/dist/adapters/secrets/azure.js.map +1 -1
  144. package/dist/adapters/secrets/gcp.constants.d.ts +1 -0
  145. package/dist/adapters/secrets/gcp.constants.js +2 -0
  146. package/dist/adapters/secrets/gcp.constants.js.map +1 -0
  147. package/dist/adapters/secrets/gcp.js +11 -9
  148. package/dist/adapters/secrets/gcp.js.map +1 -1
  149. package/dist/adapters/secrets/infisical.js +3 -2
  150. package/dist/adapters/secrets/infisical.js.map +1 -1
  151. package/dist/adapters/secrets/onepassword.constants.d.ts +1 -0
  152. package/dist/adapters/secrets/onepassword.constants.js +2 -0
  153. package/dist/adapters/secrets/onepassword.constants.js.map +1 -0
  154. package/dist/adapters/secrets/onepassword.js +2 -1
  155. package/dist/adapters/secrets/onepassword.js.map +1 -1
  156. package/dist/adapters/secrets/secret-source.js +3 -1
  157. package/dist/adapters/secrets/secret-source.js.map +1 -1
  158. package/dist/adapters/secrets/vault-response.js +5 -5
  159. package/dist/adapters/secrets/vault-response.js.map +1 -1
  160. package/dist/adapters/secrets/vault.js +4 -3
  161. package/dist/adapters/secrets/vault.js.map +1 -1
  162. package/dist/adapters/testing/scripted-agent.d.ts +3 -1
  163. package/dist/adapters/testing/scripted-agent.js +3 -1
  164. package/dist/adapters/testing/scripted-agent.js.map +1 -1
  165. package/dist/adapters/tools/edit-tools.js +3 -3
  166. package/dist/adapters/tools/edit-tools.js.map +1 -1
  167. package/dist/adapters/tools/git-tools.js +16 -1
  168. package/dist/adapters/tools/git-tools.js.map +1 -1
  169. package/dist/adapters/tools/mcp-launcher.constants.d.ts +1 -1
  170. package/dist/adapters/tools/mcp-launcher.constants.js +29 -7
  171. package/dist/adapters/tools/mcp-launcher.constants.js.map +1 -1
  172. package/dist/adapters/tools/mcp-resources.js +5 -1
  173. package/dist/adapters/tools/mcp-resources.js.map +1 -1
  174. package/dist/adapters/tools/mcp.constants.d.ts +0 -6
  175. package/dist/adapters/tools/mcp.constants.js +0 -6
  176. package/dist/adapters/tools/mcp.constants.js.map +1 -1
  177. package/dist/adapters/tools/mcp.js +1 -0
  178. package/dist/adapters/tools/mcp.js.map +1 -1
  179. package/dist/adapters/tools/sandbox-files.constants.d.ts +1 -0
  180. package/dist/adapters/tools/sandbox-files.constants.js +17 -0
  181. package/dist/adapters/tools/sandbox-files.constants.js.map +1 -0
  182. package/dist/adapters/tools/sandbox-files.d.ts +1 -0
  183. package/dist/adapters/tools/sandbox-files.js +14 -1
  184. package/dist/adapters/tools/sandbox-files.js.map +1 -1
  185. package/dist/adapters/tools/tools.constants.js +1 -0
  186. package/dist/adapters/tools/tools.constants.js.map +1 -1
  187. package/dist/adapters/triggers/github-webhook.js +14 -11
  188. package/dist/adapters/triggers/github-webhook.js.map +1 -1
  189. package/dist/adapters/triggers/gitlab-webhook.js +16 -13
  190. package/dist/adapters/triggers/gitlab-webhook.js.map +1 -1
  191. package/dist/adapters/triggers/slack-request.js +10 -7
  192. package/dist/adapters/triggers/slack-request.js.map +1 -1
  193. package/dist/adapters/triggers/standard-webhook.js +10 -7
  194. package/dist/adapters/triggers/standard-webhook.js.map +1 -1
  195. package/dist/application/agent-authentication.js +4 -1
  196. package/dist/application/agent-authentication.js.map +1 -1
  197. package/dist/application/agent-turn.js +20 -5
  198. package/dist/application/agent-turn.js.map +1 -1
  199. package/dist/application/cli-hooks.constants.d.ts +2 -0
  200. package/dist/application/cli-hooks.constants.js +69 -0
  201. package/dist/application/cli-hooks.constants.js.map +1 -0
  202. package/dist/application/cli-hooks.d.ts +3 -0
  203. package/dist/application/cli-hooks.js +168 -0
  204. package/dist/application/cli-hooks.js.map +1 -0
  205. package/dist/application/cli-steering.js +33 -2
  206. package/dist/application/cli-steering.js.map +1 -1
  207. package/dist/application/custom-turn.js +10 -1
  208. package/dist/application/custom-turn.js.map +1 -1
  209. package/dist/application/diagnostic-probe.d.ts +1 -1
  210. package/dist/application/diagnostic-probe.js +7 -3
  211. package/dist/application/diagnostic-probe.js.map +1 -1
  212. package/dist/application/dispatch-observation.js +4 -0
  213. package/dist/application/dispatch-observation.js.map +1 -1
  214. package/dist/application/doctor-agent.d.ts +1 -1
  215. package/dist/application/doctor-agent.js +2 -2
  216. package/dist/application/doctor-agent.js.map +1 -1
  217. package/dist/application/doctor-deadline.d.ts +1 -0
  218. package/dist/application/doctor-deadline.js +10 -0
  219. package/dist/application/doctor-deadline.js.map +1 -0
  220. package/dist/application/doctor-image.js +9 -7
  221. package/dist/application/doctor-image.js.map +1 -1
  222. package/dist/application/doctor-sandbox.js +4 -4
  223. package/dist/application/doctor-sandbox.js.map +1 -1
  224. package/dist/application/doctor.constants.d.ts +2 -0
  225. package/dist/application/doctor.constants.js +2 -0
  226. package/dist/application/doctor.constants.js.map +1 -1
  227. package/dist/application/doctor.js +12 -8
  228. package/dist/application/doctor.js.map +1 -1
  229. package/dist/application/doctor.types.d.ts +2 -0
  230. package/dist/application/doctor.types.js.map +1 -1
  231. package/dist/application/file-sandbox.js +49 -11
  232. package/dist/application/file-sandbox.js.map +1 -1
  233. package/dist/application/harness-context.js +4 -2
  234. package/dist/application/harness-context.js.map +1 -1
  235. package/dist/application/harness-hook-transforms.d.ts +8 -0
  236. package/dist/application/harness-hook-transforms.js +115 -0
  237. package/dist/application/harness-hook-transforms.js.map +1 -0
  238. package/dist/application/harness-hooks.d.ts +5 -3
  239. package/dist/application/harness-hooks.js +37 -50
  240. package/dist/application/harness-hooks.js.map +1 -1
  241. package/dist/application/harness-loop.d.ts +2 -1
  242. package/dist/application/harness-loop.js +95 -21
  243. package/dist/application/harness-loop.js.map +1 -1
  244. package/dist/application/harness-subagent.d.ts +1 -1
  245. package/dist/application/harness-subagent.js +63 -12
  246. package/dist/application/harness-subagent.js.map +1 -1
  247. package/dist/application/harness.types.d.ts +3 -0
  248. package/dist/application/harness.types.js.map +1 -1
  249. package/dist/application/hook-invocation.d.ts +4 -0
  250. package/dist/application/hook-invocation.js +59 -0
  251. package/dist/application/hook-invocation.js.map +1 -0
  252. package/dist/application/queue-worker.js +51 -9
  253. package/dist/application/queue-worker.js.map +1 -1
  254. package/dist/application/queue-worker.types.d.ts +1 -0
  255. package/dist/application/queue-worker.types.js.map +1 -1
  256. package/dist/application/recipes/native-schemas.constants.js +1439 -218
  257. package/dist/application/recipes/native-schemas.constants.js.map +1 -1
  258. package/dist/application/recovery-inspection.js +4 -1
  259. package/dist/application/recovery-inspection.js.map +1 -1
  260. package/dist/application/remote-download.js +15 -5
  261. package/dist/application/remote-download.js.map +1 -1
  262. package/dist/application/remote-paths.d.ts +1 -0
  263. package/dist/application/remote-paths.js +7 -0
  264. package/dist/application/remote-paths.js.map +1 -0
  265. package/dist/application/remote-validation.js +40 -9
  266. package/dist/application/remote-validation.js.map +1 -1
  267. package/dist/application/remote-workspace.js +17 -2
  268. package/dist/application/remote-workspace.js.map +1 -1
  269. package/dist/application/repetition-watchdog.js +3 -1
  270. package/dist/application/repetition-watchdog.js.map +1 -1
  271. package/dist/application/sandbox-attach.js +38 -9
  272. package/dist/application/sandbox-attach.js.map +1 -1
  273. package/dist/application/sandbox-dispatch.js +7 -1
  274. package/dist/application/sandbox-dispatch.js.map +1 -1
  275. package/dist/application/schedules.js +2 -0
  276. package/dist/application/schedules.js.map +1 -1
  277. package/dist/application/secrets.js +4 -0
  278. package/dist/application/secrets.js.map +1 -1
  279. package/dist/application/tool-execution.js +28 -8
  280. package/dist/application/tool-execution.js.map +1 -1
  281. package/dist/application/transport-retention.js +3 -3
  282. package/dist/application/transport-retention.js.map +1 -1
  283. package/dist/application/workspace-fingerprint.d.ts +1 -0
  284. package/dist/application/workspace-fingerprint.js +19 -4
  285. package/dist/application/workspace-fingerprint.js.map +1 -1
  286. package/dist/cli/doctor-command.js +12 -2
  287. package/dist/cli/doctor-command.js.map +1 -1
  288. package/dist/cli/image.js +2 -0
  289. package/dist/cli/image.js.map +1 -1
  290. package/dist/cli/main.constants.d.ts +14 -2
  291. package/dist/cli/main.constants.js +27 -1
  292. package/dist/cli/main.constants.js.map +1 -1
  293. package/dist/cli/project-settings.js +7 -1
  294. package/dist/cli/project-settings.js.map +1 -1
  295. package/dist/cli/recipe-yaml.js +8 -0
  296. package/dist/cli/recipe-yaml.js.map +1 -1
  297. package/dist/cli/recovery-command.js +3 -0
  298. package/dist/cli/recovery-command.js.map +1 -1
  299. package/dist/cli/recovery-unlock-command.d.ts +2 -0
  300. package/dist/cli/recovery-unlock-command.js +19 -0
  301. package/dist/cli/recovery-unlock-command.js.map +1 -0
  302. package/dist/cli/scaffold-files.js +4 -1
  303. package/dist/cli/scaffold-files.js.map +1 -1
  304. package/dist/cli/scaffold-validation.js +3 -0
  305. package/dist/cli/scaffold-validation.js.map +1 -1
  306. package/dist/cli/scaffold.js +2 -0
  307. package/dist/cli/scaffold.js.map +1 -1
  308. package/dist/domain/agent-hook.types.d.ts +28 -0
  309. package/dist/domain/agent-hook.types.js +2 -0
  310. package/dist/domain/agent-hook.types.js.map +1 -0
  311. package/dist/domain/agent-profile-hooks.d.ts +2 -0
  312. package/dist/domain/agent-profile-hooks.js +52 -0
  313. package/dist/domain/agent-profile-hooks.js.map +1 -0
  314. package/dist/domain/agent-profile-permissions.js +11 -1
  315. package/dist/domain/agent-profile-permissions.js.map +1 -1
  316. package/dist/domain/agent-profile.constants.d.ts +1 -0
  317. package/dist/domain/agent-profile.constants.js +9 -0
  318. package/dist/domain/agent-profile.constants.js.map +1 -1
  319. package/dist/domain/agent-profile.js +6 -1
  320. package/dist/domain/agent-profile.js.map +1 -1
  321. package/dist/domain/agent-profile.types.d.ts +11 -0
  322. package/dist/domain/agent-profile.types.js.map +1 -1
  323. package/dist/domain/agent.types.d.ts +2 -0
  324. package/dist/domain/agent.types.js.map +1 -1
  325. package/dist/domain/container-image.d.ts +1 -0
  326. package/dist/domain/container-image.js +8 -0
  327. package/dist/domain/container-image.js.map +1 -0
  328. package/dist/domain/context.constants.d.ts +1 -0
  329. package/dist/domain/context.constants.js +1 -0
  330. package/dist/domain/context.constants.js.map +1 -1
  331. package/dist/domain/context.js +6 -1
  332. package/dist/domain/context.js.map +1 -1
  333. package/dist/domain/egress.js +6 -1
  334. package/dist/domain/egress.js.map +1 -1
  335. package/dist/domain/glob.js +1 -1
  336. package/dist/domain/glob.js.map +1 -1
  337. package/dist/domain/harness.js +4 -1
  338. package/dist/domain/harness.js.map +1 -1
  339. package/dist/domain/hook-command.d.ts +2 -0
  340. package/dist/domain/hook-command.js +26 -0
  341. package/dist/domain/hook-command.js.map +1 -0
  342. package/dist/domain/hook-command.types.d.ts +7 -0
  343. package/dist/domain/hook-command.types.js +2 -0
  344. package/dist/domain/hook-command.types.js.map +1 -0
  345. package/dist/domain/hook-decision.d.ts +2 -0
  346. package/dist/domain/hook-decision.js +38 -0
  347. package/dist/domain/hook-decision.js.map +1 -0
  348. package/dist/domain/hook.constants.d.ts +3 -0
  349. package/dist/domain/hook.constants.js +31 -1
  350. package/dist/domain/hook.constants.js.map +1 -1
  351. package/dist/domain/hook.js +49 -5
  352. package/dist/domain/hook.js.map +1 -1
  353. package/dist/domain/hook.types.d.ts +82 -5
  354. package/dist/domain/hook.types.js.map +1 -1
  355. package/dist/domain/mcp-server.constants.d.ts +6 -0
  356. package/dist/domain/mcp-server.constants.js +6 -0
  357. package/dist/domain/mcp-server.constants.js.map +1 -1
  358. package/dist/domain/mcp-server.js +1 -0
  359. package/dist/domain/mcp-server.js.map +1 -1
  360. package/dist/domain/permissions.types.d.ts +1 -0
  361. package/dist/domain/permissions.types.js.map +1 -1
  362. package/dist/domain/pricing.js +4 -2
  363. package/dist/domain/pricing.js.map +1 -1
  364. package/dist/domain/redaction.js +6 -3
  365. package/dist/domain/redaction.js.map +1 -1
  366. package/dist/domain/secret-fault.d.ts +4 -0
  367. package/dist/domain/secret-fault.js +11 -0
  368. package/dist/domain/secret-fault.js.map +1 -0
  369. package/dist/domain/secrets.js +3 -2
  370. package/dist/domain/secrets.js.map +1 -1
  371. package/dist/domain/steering.js +2 -0
  372. package/dist/domain/steering.js.map +1 -1
  373. package/dist/domain/steering.types.d.ts +1 -1
  374. package/dist/domain/steering.types.js.map +1 -1
  375. package/dist/domain/transport.js +5 -1
  376. package/dist/domain/transport.js.map +1 -1
  377. package/dist/domain/trigger-payload.d.ts +4 -0
  378. package/dist/domain/trigger-payload.js +15 -0
  379. package/dist/domain/trigger-payload.js.map +1 -0
  380. package/dist/domain/workflow/checkpoint.js +19 -2
  381. package/dist/domain/workflow/checkpoint.js.map +1 -1
  382. package/dist/domain/workflow/retry.constants.d.ts +1 -0
  383. package/dist/domain/workflow/retry.constants.js +1 -0
  384. package/dist/domain/workflow/retry.constants.js.map +1 -1
  385. package/dist/domain/workflow/retry.js +6 -2
  386. package/dist/domain/workflow/retry.js.map +1 -1
  387. package/dist/domain/workflow/task-runner.js +5 -3
  388. package/dist/domain/workflow/task-runner.js.map +1 -1
  389. package/dist/domain/workflow/workflow-state.js +2 -1
  390. package/dist/domain/workflow/workflow-state.js.map +1 -1
  391. package/dist/domain/workflow.types.d.ts +1 -0
  392. package/dist/domain/workflow.types.js.map +1 -1
  393. package/dist/index.d.ts +15 -1
  394. package/dist/index.js +6 -0
  395. package/dist/index.js.map +1 -1
  396. package/dist/infrastructure/conversations/transcript-store.js +32 -5
  397. package/dist/infrastructure/conversations/transcript-store.js.map +1 -1
  398. package/dist/infrastructure/files.d.ts +1 -0
  399. package/dist/infrastructure/files.js +8 -4
  400. package/dist/infrastructure/files.js.map +1 -1
  401. package/dist/infrastructure/git/integration-candidate.js +3 -2
  402. package/dist/infrastructure/git/integration-candidate.js.map +1 -1
  403. package/dist/infrastructure/git/lock-recovery.d.ts +3 -0
  404. package/dist/infrastructure/git/lock-recovery.js +58 -0
  405. package/dist/infrastructure/git/lock-recovery.js.map +1 -0
  406. package/dist/infrastructure/git/lock-recovery.types.d.ts +11 -0
  407. package/dist/infrastructure/git/lock-recovery.types.js +2 -0
  408. package/dist/infrastructure/git/lock-recovery.types.js.map +1 -0
  409. package/dist/infrastructure/git/managed-worktree.js +5 -8
  410. package/dist/infrastructure/git/managed-worktree.js.map +1 -1
  411. package/dist/infrastructure/git/verification-command.js +35 -30
  412. package/dist/infrastructure/git/verification-command.js.map +1 -1
  413. package/dist/infrastructure/git/workspace-lease.js +12 -5
  414. package/dist/infrastructure/git/workspace-lease.js.map +1 -1
  415. package/dist/infrastructure/hook-configuration.constants.d.ts +1 -0
  416. package/dist/infrastructure/hook-configuration.constants.js +59 -0
  417. package/dist/infrastructure/hook-configuration.constants.js.map +1 -0
  418. package/dist/infrastructure/hook-configuration.d.ts +3 -0
  419. package/dist/infrastructure/hook-configuration.js +16 -0
  420. package/dist/infrastructure/hook-configuration.js.map +1 -0
  421. package/dist/infrastructure/hook-configuration.types.d.ts +14 -0
  422. package/dist/infrastructure/hook-configuration.types.js +2 -0
  423. package/dist/infrastructure/hook-configuration.types.js.map +1 -0
  424. package/dist/infrastructure/http-transport.constants.d.ts +1 -0
  425. package/dist/infrastructure/http-transport.constants.js +14 -0
  426. package/dist/infrastructure/http-transport.constants.js.map +1 -0
  427. package/dist/infrastructure/http-transport.d.ts +7 -0
  428. package/dist/infrastructure/http-transport.js +19 -0
  429. package/dist/infrastructure/http-transport.js.map +1 -0
  430. package/dist/infrastructure/input-failure.d.ts +4 -0
  431. package/dist/infrastructure/input-failure.js +14 -0
  432. package/dist/infrastructure/input-failure.js.map +1 -0
  433. package/dist/infrastructure/inspection-file.d.ts +1 -0
  434. package/dist/infrastructure/inspection-file.js +8 -2
  435. package/dist/infrastructure/inspection-file.js.map +1 -1
  436. package/dist/infrastructure/local-transport.js +16 -11
  437. package/dist/infrastructure/local-transport.js.map +1 -1
  438. package/dist/infrastructure/process.constants.d.ts +1 -0
  439. package/dist/infrastructure/process.constants.js +1 -0
  440. package/dist/infrastructure/process.constants.js.map +1 -1
  441. package/dist/infrastructure/process.d.ts +1 -0
  442. package/dist/infrastructure/process.js +48 -8
  443. package/dist/infrastructure/process.js.map +1 -1
  444. package/dist/infrastructure/reservation-ledger.d.ts +2 -0
  445. package/dist/infrastructure/reservation-ledger.js +84 -0
  446. package/dist/infrastructure/reservation-ledger.js.map +1 -0
  447. package/dist/infrastructure/reservation-recovery.d.ts +4 -0
  448. package/dist/infrastructure/reservation-recovery.js +28 -0
  449. package/dist/infrastructure/reservation-recovery.js.map +1 -0
  450. package/dist/infrastructure/s3-tombstones.d.ts +3 -0
  451. package/dist/infrastructure/s3-tombstones.js +46 -0
  452. package/dist/infrastructure/s3-tombstones.js.map +1 -0
  453. package/dist/infrastructure/s3-tombstones.types.d.ts +6 -0
  454. package/dist/infrastructure/s3-tombstones.types.js +2 -0
  455. package/dist/infrastructure/s3-tombstones.types.js.map +1 -0
  456. package/dist/infrastructure/s3-transport.d.ts +2 -0
  457. package/dist/infrastructure/s3-transport.js +4 -2
  458. package/dist/infrastructure/s3-transport.js.map +1 -1
  459. package/dist/infrastructure/sse.js +6 -3
  460. package/dist/infrastructure/sse.js.map +1 -1
  461. package/dist/infrastructure/task-queue-bullmq-connection.d.ts +3 -0
  462. package/dist/infrastructure/task-queue-bullmq-connection.js +32 -0
  463. package/dist/infrastructure/task-queue-bullmq-connection.js.map +1 -0
  464. package/dist/infrastructure/task-queue-bullmq-connection.types.d.ts +8 -0
  465. package/dist/infrastructure/task-queue-bullmq-connection.types.js +2 -0
  466. package/dist/infrastructure/task-queue-bullmq-connection.types.js.map +1 -0
  467. package/dist/infrastructure/task-queue-bullmq-resources.js +21 -15
  468. package/dist/infrastructure/task-queue-bullmq-resources.js.map +1 -1
  469. package/dist/infrastructure/task-queue-bullmq-state.d.ts +1 -0
  470. package/dist/infrastructure/task-queue-bullmq-state.js +1 -0
  471. package/dist/infrastructure/task-queue-bullmq-state.js.map +1 -1
  472. package/dist/infrastructure/task-queue-bullmq.constants.d.ts +2 -1
  473. package/dist/infrastructure/task-queue-bullmq.constants.js +2 -1
  474. package/dist/infrastructure/task-queue-bullmq.constants.js.map +1 -1
  475. package/dist/infrastructure/task-queue-bullmq.js +10 -1
  476. package/dist/infrastructure/task-queue-bullmq.js.map +1 -1
  477. package/dist/infrastructure/task-queue.js +13 -4
  478. package/dist/infrastructure/task-queue.js.map +1 -1
  479. package/dist/infrastructure/transfer-copy.d.ts +1 -0
  480. package/dist/infrastructure/transfer-copy.js +16 -1
  481. package/dist/infrastructure/transfer-copy.js.map +1 -1
  482. package/dist/infrastructure/transport-archive.d.ts +3 -3
  483. package/dist/infrastructure/transport-archive.js +71 -48
  484. package/dist/infrastructure/transport-archive.js.map +1 -1
  485. package/dist/infrastructure/transport-archive.types.d.ts +3 -0
  486. package/dist/infrastructure/transport-archive.types.js.map +1 -1
  487. package/dist/infrastructure/transport-checkpoint.js +14 -4
  488. package/dist/infrastructure/transport-checkpoint.js.map +1 -1
  489. package/dist/infrastructure/transport-envelope.d.ts +1 -0
  490. package/dist/infrastructure/transport-envelope.js +22 -5
  491. package/dist/infrastructure/transport-envelope.js.map +1 -1
  492. package/dist/infrastructure/transport-reservations.constants.d.ts +2 -0
  493. package/dist/infrastructure/transport-reservations.constants.js +3 -0
  494. package/dist/infrastructure/transport-reservations.constants.js.map +1 -0
  495. package/dist/infrastructure/transport-reservations.d.ts +1 -2
  496. package/dist/infrastructure/transport-reservations.js +18 -22
  497. package/dist/infrastructure/transport-reservations.js.map +1 -1
  498. package/dist/infrastructure/transport-reservations.types.d.ts +23 -2
  499. package/dist/infrastructure/transport-reservations.types.js.map +1 -1
  500. package/dist/infrastructure/trigger-server.js +12 -9
  501. package/dist/infrastructure/trigger-server.js.map +1 -1
  502. package/dist/infrastructure/trigger-server.types.d.ts +1 -1
  503. package/dist/infrastructure/trigger-server.types.js.map +1 -1
  504. package/dist/infrastructure/workflow-checkpoint.constants.d.ts +1 -0
  505. package/dist/infrastructure/workflow-checkpoint.constants.js +1 -0
  506. package/dist/infrastructure/workflow-checkpoint.constants.js.map +1 -1
  507. package/dist/infrastructure/workspace-lock.constants.d.ts +2 -1
  508. package/dist/infrastructure/workspace-lock.constants.js +2 -1
  509. package/dist/infrastructure/workspace-lock.constants.js.map +1 -1
  510. package/dist/infrastructure/workspace-lock.js +48 -10
  511. package/dist/infrastructure/workspace-lock.js.map +1 -1
  512. package/dist/providers/cloud-cache.js +14 -9
  513. package/dist/providers/cloud-cache.js.map +1 -1
  514. package/dist/providers/cloud-cache.types.d.ts +1 -0
  515. package/dist/providers/cloud-cache.types.js.map +1 -1
  516. package/dist/providers/cloud-files.js +3 -4
  517. package/dist/providers/cloud-files.js.map +1 -1
  518. package/dist/providers/cloud-input.constants.d.ts +2 -2
  519. package/dist/providers/cloud-input.constants.js +2 -1
  520. package/dist/providers/cloud-input.constants.js.map +1 -1
  521. package/dist/providers/cloud-input.d.ts +2 -2
  522. package/dist/providers/cloud-input.js +14 -3
  523. package/dist/providers/cloud-input.js.map +1 -1
  524. package/dist/providers/container-command.js +30 -5
  525. package/dist/providers/container-command.js.map +1 -1
  526. package/dist/providers/container-egress.constants.d.ts +7 -0
  527. package/dist/providers/container-egress.constants.js +8 -0
  528. package/dist/providers/container-egress.constants.js.map +1 -0
  529. package/dist/providers/container-egress.d.ts +7 -0
  530. package/dist/providers/container-egress.js +101 -0
  531. package/dist/providers/container-egress.js.map +1 -0
  532. package/dist/providers/container-files.js +10 -11
  533. package/dist/providers/container-files.js.map +1 -1
  534. package/dist/providers/container-mounts.js +3 -0
  535. package/dist/providers/container-mounts.js.map +1 -1
  536. package/dist/providers/container-network.js +16 -1
  537. package/dist/providers/container-network.js.map +1 -1
  538. package/dist/providers/container-plan.js +7 -10
  539. package/dist/providers/container-plan.js.map +1 -1
  540. package/dist/providers/container-recovery.js +8 -3
  541. package/dist/providers/container-recovery.js.map +1 -1
  542. package/dist/providers/container.js +36 -5
  543. package/dist/providers/container.js.map +1 -1
  544. package/dist/providers/container.types.d.ts +1 -0
  545. package/dist/providers/container.types.js.map +1 -1
  546. package/dist/providers/daytona-command.constants.js +4 -1
  547. package/dist/providers/daytona-command.constants.js.map +1 -1
  548. package/dist/providers/daytona-command.js +39 -16
  549. package/dist/providers/daytona-command.js.map +1 -1
  550. package/dist/providers/daytona-files.js +5 -5
  551. package/dist/providers/daytona-files.js.map +1 -1
  552. package/dist/providers/daytona-output.js +11 -3
  553. package/dist/providers/daytona-output.js.map +1 -1
  554. package/dist/providers/daytona.js +11 -9
  555. package/dist/providers/daytona.js.map +1 -1
  556. package/dist/providers/egress/address.d.ts +3 -0
  557. package/dist/providers/egress/address.js +26 -0
  558. package/dist/providers/egress/address.js.map +1 -0
  559. package/dist/providers/egress/egress.constants.d.ts +22 -0
  560. package/dist/providers/egress/egress.constants.js +23 -0
  561. package/dist/providers/egress/egress.constants.js.map +1 -0
  562. package/dist/providers/egress/egress.types.d.ts +11 -0
  563. package/dist/providers/egress/egress.types.js +2 -0
  564. package/dist/providers/egress/egress.types.js.map +1 -0
  565. package/dist/providers/egress/firewall.d.ts +3 -0
  566. package/dist/providers/egress/firewall.js +29 -0
  567. package/dist/providers/egress/firewall.js.map +1 -0
  568. package/dist/providers/egress/main.d.ts +1 -0
  569. package/dist/providers/egress/main.js +90 -0
  570. package/dist/providers/egress/main.js.map +1 -0
  571. package/dist/providers/egress/policy.d.ts +5 -0
  572. package/dist/providers/egress/policy.js +54 -0
  573. package/dist/providers/egress/policy.js.map +1 -0
  574. package/dist/providers/egress/proxy.d.ts +2 -0
  575. package/dist/providers/egress/proxy.js +129 -0
  576. package/dist/providers/egress/proxy.js.map +1 -0
  577. package/dist/providers/egress/tls-hello.d.ts +1 -0
  578. package/dist/providers/egress/tls-hello.js +75 -0
  579. package/dist/providers/egress/tls-hello.js.map +1 -0
  580. package/dist/providers/firecracker-command.js +12 -6
  581. package/dist/providers/firecracker-command.js.map +1 -1
  582. package/dist/providers/local-files.d.ts +2 -0
  583. package/dist/providers/local-files.js +24 -0
  584. package/dist/providers/local-files.js.map +1 -0
  585. package/dist/providers/local.js +2 -20
  586. package/dist/providers/local.js.map +1 -1
  587. package/dist/providers/vercel-command.js +33 -9
  588. package/dist/providers/vercel-command.js.map +1 -1
  589. package/dist/providers/vercel.js +11 -9
  590. package/dist/providers/vercel.js.map +1 -1
  591. package/dist/testing.d.ts +1 -1
  592. package/dist/testing.js +1 -1
  593. package/dist/testing.js.map +1 -1
  594. package/package.json +2 -2
  595. package/recipe-configuration.schema.json +2164 -35
  596. package/recipe.schema.json +2512 -404
@@ -0,0 +1,101 @@
1
+ import { setTimeout } from "node:timers/promises";
2
+ import { invariant, OutpostError } from "../domain/errors.js";
3
+ import { containerEgress, defaultEgressImage, } from "./container-egress.constants.js";
4
+ import { egressRuntime } from "./egress/egress.constants.js";
5
+ export function containerUserNamespace(engine, config, user) {
6
+ if (engine !== "podman" ||
7
+ config.userns === false ||
8
+ (process.getuid?.() === 0 && config.userns !== "keep-id"))
9
+ return [];
10
+ return [
11
+ "--userns",
12
+ config.user ? `keep-id:uid=${user.uid},gid=${user.gid}` : "keep-id",
13
+ ];
14
+ }
15
+ export function createContainerEgress(call, name, config, engine, user) {
16
+ const companion = `${name}-egress`;
17
+ let attempted = false;
18
+ return {
19
+ name: companion,
20
+ async start(signal) {
21
+ invariant(config.egress?.mode === "allowlist", "Container egress requires an allowlist");
22
+ invariant(user.uid !== egressRuntime.uid, `UID ${egressRuntime.uid} is reserved for the egress proxy`);
23
+ const image = config.egressImage ?? defaultEgressImage;
24
+ invariant(image, "The default Outpost egress image has not been published; build images/egress and set egressImage");
25
+ const extra = signal ? { signal } : {};
26
+ attempted = true;
27
+ await call([
28
+ "create",
29
+ "--name",
30
+ companion,
31
+ "--network",
32
+ "bridge",
33
+ "--label",
34
+ `${containerEgress.label}=${name}`,
35
+ "--user",
36
+ "0:0",
37
+ ...containerUserNamespace(engine, config, user),
38
+ "--cap-drop",
39
+ "ALL",
40
+ "--cap-add",
41
+ "NET_ADMIN",
42
+ "--cap-add",
43
+ "SETUID",
44
+ "--cap-add",
45
+ "SETGID",
46
+ "--security-opt",
47
+ "no-new-privileges",
48
+ "--read-only",
49
+ "--tmpfs",
50
+ "/run/outpost:rw,noexec,nosuid,size=1m,mode=0700",
51
+ "--env",
52
+ `OUTPOST_EGRESS_POLICY=${JSON.stringify(config.egress)}`,
53
+ "--entrypoint",
54
+ "node",
55
+ image,
56
+ containerEgress.entrypoint,
57
+ ], extra);
58
+ await call(["start", companion], extra);
59
+ const deadline = Date.now() + containerEgress.startupMs;
60
+ while (true) {
61
+ signal?.throwIfAborted();
62
+ try {
63
+ await call([
64
+ "exec",
65
+ companion,
66
+ "node",
67
+ containerEgress.entrypoint,
68
+ "health",
69
+ String(egressRuntime.version),
70
+ ], { ...extra, deadlineMs: 2_000 });
71
+ return;
72
+ }
73
+ catch (cause) {
74
+ signal?.throwIfAborted();
75
+ const running = await call(["inspect", "--format", "{{.State.Running}}", companion], extra);
76
+ if (running.stdout.trim() !== "true" || Date.now() >= deadline)
77
+ throw new OutpostError("provider", "Container egress could not establish its firewall and proxy; sandbox execution refused", { engine }, cause);
78
+ await setTimeout(containerEgress.pollMs, undefined, extra);
79
+ }
80
+ }
81
+ },
82
+ async release() {
83
+ if (!attempted)
84
+ return;
85
+ const found = await call([
86
+ "container",
87
+ "ls",
88
+ "--all",
89
+ "--filter",
90
+ `name=^${companion}$`,
91
+ "--format",
92
+ "{{.Names}}",
93
+ ]);
94
+ if (!found.stdout.trim())
95
+ return;
96
+ invariant(found.stdout.trim() === companion, "Egress container identity mismatch");
97
+ await call(["rm", "--force", companion]);
98
+ },
99
+ };
100
+ }
101
+ //# sourceMappingURL=container-egress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-egress.js","sourceRoot":"","sources":["../../src/providers/container-egress.ts"],"names":[],"mappings":"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","sourcesContent":["import { setTimeout } from \"node:timers/promises\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport {\n containerEgress,\n defaultEgressImage,\n} from \"./container-egress.constants.ts\";\nimport { egressRuntime } from \"./egress/egress.constants.ts\";\nimport type {\n ContainerCall,\n ContainerEngine,\n ContainerOptions,\n ContainerUser,\n} from \"./container.types.ts\";\n\nexport function containerUserNamespace(\n engine: ContainerEngine,\n config: ContainerOptions,\n user: ContainerUser,\n): string[] {\n if (\n engine !== \"podman\" ||\n config.userns === false ||\n (process.getuid?.() === 0 && config.userns !== \"keep-id\")\n )\n return [];\n return [\n \"--userns\",\n config.user ? `keep-id:uid=${user.uid},gid=${user.gid}` : \"keep-id\",\n ];\n}\n\nexport function createContainerEgress(\n call: ContainerCall,\n name: string,\n config: ContainerOptions,\n engine: ContainerEngine,\n user: ContainerUser,\n) {\n const companion = `${name}-egress`;\n let attempted = false;\n return {\n name: companion,\n async start(signal?: AbortSignal): Promise<void> {\n invariant(\n config.egress?.mode === \"allowlist\",\n \"Container egress requires an allowlist\",\n );\n invariant(\n user.uid !== egressRuntime.uid,\n `UID ${egressRuntime.uid} is reserved for the egress proxy`,\n );\n const image = config.egressImage ?? defaultEgressImage;\n invariant(\n image,\n \"The default Outpost egress image has not been published; build images/egress and set egressImage\",\n );\n const extra = signal ? { signal } : {};\n attempted = true;\n await call(\n [\n \"create\",\n \"--name\",\n companion,\n \"--network\",\n \"bridge\",\n \"--label\",\n `${containerEgress.label}=${name}`,\n \"--user\",\n \"0:0\",\n ...containerUserNamespace(engine, config, user),\n \"--cap-drop\",\n \"ALL\",\n \"--cap-add\",\n \"NET_ADMIN\",\n \"--cap-add\",\n \"SETUID\",\n \"--cap-add\",\n \"SETGID\",\n \"--security-opt\",\n \"no-new-privileges\",\n \"--read-only\",\n \"--tmpfs\",\n \"/run/outpost:rw,noexec,nosuid,size=1m,mode=0700\",\n \"--env\",\n `OUTPOST_EGRESS_POLICY=${JSON.stringify(config.egress)}`,\n \"--entrypoint\",\n \"node\",\n image,\n containerEgress.entrypoint,\n ],\n extra,\n );\n await call([\"start\", companion], extra);\n const deadline = Date.now() + containerEgress.startupMs;\n while (true) {\n signal?.throwIfAborted();\n try {\n await call(\n [\n \"exec\",\n companion,\n \"node\",\n containerEgress.entrypoint,\n \"health\",\n String(egressRuntime.version),\n ],\n { ...extra, deadlineMs: 2_000 },\n );\n return;\n } catch (cause) {\n signal?.throwIfAborted();\n const running = await call(\n [\"inspect\", \"--format\", \"{{.State.Running}}\", companion],\n extra,\n );\n if (running.stdout.trim() !== \"true\" || Date.now() >= deadline)\n throw new OutpostError(\n \"provider\",\n \"Container egress could not establish its firewall and proxy; sandbox execution refused\",\n { engine },\n cause,\n );\n await setTimeout(containerEgress.pollMs, undefined, extra);\n }\n }\n },\n async release(): Promise<void> {\n if (!attempted) return;\n const found = await call([\n \"container\",\n \"ls\",\n \"--all\",\n \"--filter\",\n `name=^${companion}$`,\n \"--format\",\n \"{{.Names}}\",\n ]);\n if (!found.stdout.trim()) return;\n invariant(\n found.stdout.trim() === companion,\n \"Egress container identity mismatch\",\n );\n await call([\"rm\", \"--force\", companion]);\n },\n };\n}\n"]}
@@ -1,11 +1,12 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { lstat, mkdir, mkdtemp } from "node:fs/promises";
2
+ import { mkdir, mkdtemp } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { basename, dirname, join, posix, resolve } from "node:path";
5
5
  import { invariant, OutpostError } from "../domain/errors.js";
6
6
  import { quote } from "../infrastructure/process.js";
7
- import { copyTransfer, removeTransferDirectory, transferDestination, } from "../infrastructure/transfer-copy.js";
7
+ import { copyTransfer, removeTransferDirectory, transferDestination, transferTarget, } from "../infrastructure/transfer-copy.js";
8
8
  import { transfer } from "../infrastructure/transfer.js";
9
+ import { transferDefaults } from "../infrastructure/transfer.constants.js";
9
10
  import { containerArchive } from "./container-archive.js";
10
11
  export function containerFiles(runtime, archive = containerArchive) {
11
12
  const { call, name } = runtime;
@@ -15,6 +16,7 @@ export function containerFiles(runtime, archive = containerArchive) {
15
16
  if (runtime.isClosed())
16
17
  throw new OutpostError("provider", "Container sandbox is closed");
17
18
  const absolute = resolve(source);
19
+ const target = posix.resolve(runtime.root, destination);
18
20
  const scratch = `/tmp/outpost-transfer-${randomUUID()}`;
19
21
  const entry = basename(absolute);
20
22
  invariant(entry, "Upload a directory's contents using a non-root path");
@@ -23,9 +25,11 @@ export function containerFiles(runtime, archive = containerArchive) {
23
25
  try {
24
26
  await archive(runtime, {
25
27
  executable: "tar",
28
+ deadlineMs: options.deadlineMs ?? transferDefaults.deadlineMs,
26
29
  arguments: ["-cf", "-", "-C", dirname(absolute), "--", entry],
27
30
  }, {
28
31
  executable: "tar",
32
+ deadlineMs: options.deadlineMs ?? transferDefaults.deadlineMs,
29
33
  arguments: [
30
34
  "-xpf",
31
35
  "-",
@@ -40,7 +44,7 @@ export function containerFiles(runtime, archive = containerArchive) {
40
44
  name,
41
45
  "sh",
42
46
  "-c",
43
- `mkdir -p ${quote(posix.dirname(destination))} && cp -a -- ${quote(posix.join(scratch, entry) + (contents ? "/." : ""))} ${quote(destination)}`,
47
+ `mkdir -p ${quote(posix.dirname(target))} && cp -a -- ${quote(posix.join(scratch, entry) + (contents ? "/." : ""))} ${quote(target)}`,
44
48
  ], { signal });
45
49
  }
46
50
  finally {
@@ -66,6 +70,7 @@ export function containerFiles(runtime, archive = containerArchive) {
66
70
  try {
67
71
  await archive(runtime, {
68
72
  executable: "tar",
73
+ deadlineMs: options.deadlineMs ?? transferDefaults.deadlineMs,
69
74
  arguments: [
70
75
  "-xpf",
71
76
  "-",
@@ -76,6 +81,7 @@ export function containerFiles(runtime, archive = containerArchive) {
76
81
  ],
77
82
  }, {
78
83
  executable: "tar",
84
+ deadlineMs: options.deadlineMs ?? transferDefaults.deadlineMs,
79
85
  arguments: [
80
86
  "-cf",
81
87
  "-",
@@ -85,14 +91,7 @@ export function containerFiles(runtime, archive = containerArchive) {
85
91
  entry,
86
92
  ],
87
93
  }, false, signal);
88
- const existing = await lstat(destination).catch((cause) => {
89
- if (cause.code === "ENOENT")
90
- return undefined;
91
- throw cause;
92
- });
93
- const target = existing?.isDirectory() && !source.endsWith("/.")
94
- ? join(destination, entry)
95
- : destination;
94
+ const target = await transferTarget(entry, destination, source.endsWith("/."));
96
95
  await mkdir(dirname(target), { recursive: true });
97
96
  await copyTransfer(join(scratch, entry), target, signal);
98
97
  }
@@ -1 +1 @@
1
- {"version":3,"file":"container-files.js","sourceRoot":"","sources":["../../src/providers/container-files.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { lstat, mkdir, mkdtemp } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, join, posix, resolve } from \"node:path\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport {\n copyTransfer,\n removeTransferDirectory,\n transferDestination,\n} from \"../infrastructure/transfer-copy.ts\";\nimport { transfer } from \"../infrastructure/transfer.ts\";\nimport { containerArchive } from \"./container-archive.ts\";\nimport type { ContainerRuntime } from \"./container.types.ts\";\n\nexport function containerFiles(\n runtime: ContainerRuntime,\n archive = containerArchive,\n): Pick<SandboxLease, \"upload\" | \"download\"> {\n const { call, name } = runtime;\n return {\n upload(source, destination, options = {}) {\n return transfer(options, async (signal) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Container sandbox is closed\");\n const absolute = resolve(source);\n const scratch = `/tmp/outpost-transfer-${randomUUID()}`;\n const entry = basename(absolute);\n invariant(entry, \"Upload a directory's contents using a non-root path\");\n const contents = /[\\\\/]\\.$/.test(source);\n await call([\"exec\", name, \"mkdir\", \"-m\", \"700\", scratch], { signal });\n try {\n await archive(\n runtime,\n {\n executable: \"tar\",\n arguments: [\"-cf\", \"-\", \"-C\", dirname(absolute), \"--\", entry],\n },\n {\n executable: \"tar\",\n arguments: [\n \"-xpf\",\n \"-\",\n \"--ignore-zeros\",\n \"--no-same-owner\",\n \"-C\",\n scratch,\n ],\n },\n true,\n signal,\n );\n await call(\n [\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n `mkdir -p ${quote(posix.dirname(destination))} && cp -a -- ${quote(posix.join(scratch, entry) + (contents ? \"/.\" : \"\"))} ${quote(destination)}`,\n ],\n { signal },\n );\n } finally {\n await call([\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n `chmod -R u+rwX ${quote(scratch)} && rm -rf -- ${quote(scratch)}`,\n ]);\n }\n });\n },\n download(source, destination, options = {}) {\n return transfer(options, async (signal) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Container sandbox is closed\");\n await transferDestination(destination);\n const absolute = posix.resolve(runtime.root, source);\n const entry = posix.basename(absolute);\n invariant(\n entry,\n \"Download a directory's contents using a non-root path\",\n );\n const scratch = await mkdtemp(join(tmpdir(), \"outpost-transfer-\"));\n try {\n await archive(\n runtime,\n {\n executable: \"tar\",\n arguments: [\n \"-xpf\",\n \"-\",\n \"--ignore-zeros\",\n \"--no-same-owner\",\n \"-C\",\n scratch,\n ],\n },\n {\n executable: \"tar\",\n arguments: [\n \"-cf\",\n \"-\",\n \"-C\",\n posix.dirname(absolute),\n \"--\",\n entry,\n ],\n },\n false,\n signal,\n );\n const existing = await lstat(destination).catch(\n (cause: NodeJS.ErrnoException) => {\n if (cause.code === \"ENOENT\") return undefined;\n throw cause;\n },\n );\n const target =\n existing?.isDirectory() && !source.endsWith(\"/.\")\n ? join(destination, entry)\n : destination;\n await mkdir(dirname(target), { recursive: true });\n await copyTransfer(join(scratch, entry), target, signal);\n } finally {\n await removeTransferDirectory(scratch);\n }\n });\n },\n };\n}\n"]}
1
+ {"version":3,"file":"container-files.js","sourceRoot":"","sources":["../../src/providers/container-files.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, mkdtemp } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, join, posix, resolve } from \"node:path\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport {\n copyTransfer,\n removeTransferDirectory,\n transferDestination,\n transferTarget,\n} from \"../infrastructure/transfer-copy.ts\";\nimport { transfer } from \"../infrastructure/transfer.ts\";\nimport { transferDefaults } from \"../infrastructure/transfer.constants.ts\";\nimport { containerArchive } from \"./container-archive.ts\";\nimport type { ContainerRuntime } from \"./container.types.ts\";\n\nexport function containerFiles(\n runtime: ContainerRuntime,\n archive = containerArchive,\n): Pick<SandboxLease, \"upload\" | \"download\"> {\n const { call, name } = runtime;\n return {\n upload(source, destination, options = {}) {\n return transfer(options, async (signal) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Container sandbox is closed\");\n const absolute = resolve(source);\n const target = posix.resolve(runtime.root, destination);\n const scratch = `/tmp/outpost-transfer-${randomUUID()}`;\n const entry = basename(absolute);\n invariant(entry, \"Upload a directory's contents using a non-root path\");\n const contents = /[\\\\/]\\.$/.test(source);\n await call([\"exec\", name, \"mkdir\", \"-m\", \"700\", scratch], { signal });\n try {\n await archive(\n runtime,\n {\n executable: \"tar\",\n deadlineMs: options.deadlineMs ?? transferDefaults.deadlineMs,\n arguments: [\"-cf\", \"-\", \"-C\", dirname(absolute), \"--\", entry],\n },\n {\n executable: \"tar\",\n deadlineMs: options.deadlineMs ?? transferDefaults.deadlineMs,\n arguments: [\n \"-xpf\",\n \"-\",\n \"--ignore-zeros\",\n \"--no-same-owner\",\n \"-C\",\n scratch,\n ],\n },\n true,\n signal,\n );\n await call(\n [\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n `mkdir -p ${quote(posix.dirname(target))} && cp -a -- ${quote(posix.join(scratch, entry) + (contents ? \"/.\" : \"\"))} ${quote(target)}`,\n ],\n { signal },\n );\n } finally {\n await call([\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n `chmod -R u+rwX ${quote(scratch)} && rm -rf -- ${quote(scratch)}`,\n ]);\n }\n });\n },\n download(source, destination, options = {}) {\n return transfer(options, async (signal) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Container sandbox is closed\");\n await transferDestination(destination);\n const absolute = posix.resolve(runtime.root, source);\n const entry = posix.basename(absolute);\n invariant(\n entry,\n \"Download a directory's contents using a non-root path\",\n );\n const scratch = await mkdtemp(join(tmpdir(), \"outpost-transfer-\"));\n try {\n await archive(\n runtime,\n {\n executable: \"tar\",\n deadlineMs: options.deadlineMs ?? transferDefaults.deadlineMs,\n arguments: [\n \"-xpf\",\n \"-\",\n \"--ignore-zeros\",\n \"--no-same-owner\",\n \"-C\",\n scratch,\n ],\n },\n {\n executable: \"tar\",\n deadlineMs: options.deadlineMs ?? transferDefaults.deadlineMs,\n arguments: [\n \"-cf\",\n \"-\",\n \"-C\",\n posix.dirname(absolute),\n \"--\",\n entry,\n ],\n },\n false,\n signal,\n );\n const target = await transferTarget(\n entry,\n destination,\n source.endsWith(\"/.\"),\n );\n await mkdir(dirname(target), { recursive: true });\n await copyTransfer(join(scratch, entry), target, signal);\n } finally {\n await removeTransferDirectory(scratch);\n }\n });\n },\n };\n}\n"]}
@@ -70,6 +70,9 @@ export async function containerMounts(context, config, platform, root, home) {
70
70
  fileParents.add(folder);
71
71
  }
72
72
  invariant(!source.includes(",") && !target.includes(","), "Mount paths cannot contain commas");
73
+ invariant(config.label === false ||
74
+ platform !== "linux" ||
75
+ (!source.includes(":") && !target.includes(":")), "SELinux-labelled mount paths cannot contain colons; use label: false for --mount syntax");
73
76
  if (config.label === false || platform !== "linux")
74
77
  volumes.push("--mount", `type=bind,source=${source},target=${target}${volume.readOnly ? ",readonly" : ""}`);
75
78
  else
@@ -1 +1 @@
1
- {"version":3,"file":"container-mounts.js","sourceRoot":"","sources":["../../src/providers/container-mounts.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat } from \"node:fs/promises\";\nimport { isAbsolute, posix, relative } from \"node:path\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxContext, Volume } from \"../domain/sandbox.types.ts\";\nimport { expandPath } from \"../infrastructure/files.ts\";\nimport { inside } from \"../infrastructure/files.ts\";\nimport { canonicalWorkspacePath } from \"../infrastructure/workspace-lock.ts\";\nimport { validateIsolatedMount } from \"./container-isolation.ts\";\nimport type { ContainerMounts, ContainerOptions } from \"./container.types.ts\";\n\nexport async function containerMounts(\n context: SandboxContext,\n config: ContainerOptions,\n platform: NodeJS.Platform,\n root: string,\n home: string,\n): Promise<ContainerMounts> {\n const env: Record<string, string> = {\n ...context.variables,\n HOME: home,\n ...(context.workspaceIdentity === undefined\n ? {\n GIT_CONFIG_COUNT: \"1\",\n GIT_CONFIG_KEY_0: \"safe.directory\",\n GIT_CONFIG_VALUE_0: \"*\",\n }\n : {}),\n };\n const internal: Volume[] =\n config.repositoryMode === \"isolated\"\n ? []\n : [{ source: context.directory, target: root }];\n if (config.repositoryMode !== \"isolated\" && context.gitDirectories.length) {\n const common = context.gitDirectories.at(-1)!;\n const offset = relative(common, context.gitDirectories[0]!);\n const nested =\n !isAbsolute(offset) &&\n offset !== \"..\" &&\n !offset.startsWith(\"..\\\\\") &&\n !offset.startsWith(\"../\");\n internal.push({\n source: common,\n target: \"/outpost/git/common\",\n });\n if (!nested)\n internal.push({\n source: context.gitDirectories[0]!,\n target: \"/outpost/git/worktree\",\n });\n env.GIT_DIR = nested\n ? posix.join(\"/outpost/git/common\", offset.replaceAll(\"\\\\\", \"/\"))\n : \"/outpost/git/worktree\";\n env.GIT_COMMON_DIR = \"/outpost/git/common\";\n env.GIT_WORK_TREE = root;\n }\n const volumes: string[] = [];\n const fileParents = new Set<string>();\n for (const volume of [...internal, ...(config.volumes ?? [])]) {\n const source = expandPath(volume.source, context.repository);\n const info = await stat(source).catch(() => undefined);\n if (!info)\n throw new OutpostError(\n \"provider\",\n `Mount source is unavailable: ${source}`,\n );\n const destination = volume.target.replaceAll(\"\\\\\", \"/\");\n const target =\n destination === \"~\"\n ? home\n : destination.startsWith(\"~/\")\n ? posix.resolve(home, destination.slice(2))\n : posix.resolve(root, destination);\n if (context.workspaceIdentity !== undefined && !internal.includes(volume)) {\n const canonicalSource = await canonicalWorkspacePath(source);\n const control = await canonicalWorkspacePath(context.repository);\n invariant(\n !inside(canonicalSource, control) && !inside(control, canonicalSource),\n \"File workspace mounts cannot expose runtime control storage\",\n );\n invariant(\n target !== root && !root.startsWith(`${target}/`),\n \"Custom mounts cannot replace the owned workspace root\",\n );\n }\n if (config.repositoryMode === \"isolated\")\n await validateIsolatedMount(context, source, target);\n if (info.isFile() && !internal.includes(volume)) {\n const parent = posix.dirname(target);\n invariant(\n parent === home || parent.startsWith(home + \"/\"),\n \"File mounts must live inside the agent home; mount the containing directory for other destinations\",\n );\n for (let folder = parent; folder !== home; folder = posix.dirname(folder))\n fileParents.add(folder);\n }\n invariant(\n !source.includes(\",\") && !target.includes(\",\"),\n \"Mount paths cannot contain commas\",\n );\n if (config.label === false || platform !== \"linux\")\n volumes.push(\n \"--mount\",\n `type=bind,source=${source},target=${target}${volume.readOnly ? \",readonly\" : \"\"}`,\n );\n else\n volumes.push(\n \"--volume\",\n `${source}:${target}:${volume.readOnly ? \"ro,\" : \"\"}${config.label ?? \"z\"}`,\n );\n }\n\n return { env, volumes, fileParents };\n}\n"]}
1
+ {"version":3,"file":"container-mounts.js","sourceRoot":"","sources":["../../src/providers/container-mounts.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat } from \"node:fs/promises\";\nimport { isAbsolute, posix, relative } from \"node:path\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxContext, Volume } from \"../domain/sandbox.types.ts\";\nimport { expandPath } from \"../infrastructure/files.ts\";\nimport { inside } from \"../infrastructure/files.ts\";\nimport { canonicalWorkspacePath } from \"../infrastructure/workspace-lock.ts\";\nimport { validateIsolatedMount } from \"./container-isolation.ts\";\nimport type { ContainerMounts, ContainerOptions } from \"./container.types.ts\";\n\nexport async function containerMounts(\n context: SandboxContext,\n config: ContainerOptions,\n platform: NodeJS.Platform,\n root: string,\n home: string,\n): Promise<ContainerMounts> {\n const env: Record<string, string> = {\n ...context.variables,\n HOME: home,\n ...(context.workspaceIdentity === undefined\n ? {\n GIT_CONFIG_COUNT: \"1\",\n GIT_CONFIG_KEY_0: \"safe.directory\",\n GIT_CONFIG_VALUE_0: \"*\",\n }\n : {}),\n };\n const internal: Volume[] =\n config.repositoryMode === \"isolated\"\n ? []\n : [{ source: context.directory, target: root }];\n if (config.repositoryMode !== \"isolated\" && context.gitDirectories.length) {\n const common = context.gitDirectories.at(-1)!;\n const offset = relative(common, context.gitDirectories[0]!);\n const nested =\n !isAbsolute(offset) &&\n offset !== \"..\" &&\n !offset.startsWith(\"..\\\\\") &&\n !offset.startsWith(\"../\");\n internal.push({\n source: common,\n target: \"/outpost/git/common\",\n });\n if (!nested)\n internal.push({\n source: context.gitDirectories[0]!,\n target: \"/outpost/git/worktree\",\n });\n env.GIT_DIR = nested\n ? posix.join(\"/outpost/git/common\", offset.replaceAll(\"\\\\\", \"/\"))\n : \"/outpost/git/worktree\";\n env.GIT_COMMON_DIR = \"/outpost/git/common\";\n env.GIT_WORK_TREE = root;\n }\n const volumes: string[] = [];\n const fileParents = new Set<string>();\n for (const volume of [...internal, ...(config.volumes ?? [])]) {\n const source = expandPath(volume.source, context.repository);\n const info = await stat(source).catch(() => undefined);\n if (!info)\n throw new OutpostError(\n \"provider\",\n `Mount source is unavailable: ${source}`,\n );\n const destination = volume.target.replaceAll(\"\\\\\", \"/\");\n const target =\n destination === \"~\"\n ? home\n : destination.startsWith(\"~/\")\n ? posix.resolve(home, destination.slice(2))\n : posix.resolve(root, destination);\n if (context.workspaceIdentity !== undefined && !internal.includes(volume)) {\n const canonicalSource = await canonicalWorkspacePath(source);\n const control = await canonicalWorkspacePath(context.repository);\n invariant(\n !inside(canonicalSource, control) && !inside(control, canonicalSource),\n \"File workspace mounts cannot expose runtime control storage\",\n );\n invariant(\n target !== root && !root.startsWith(`${target}/`),\n \"Custom mounts cannot replace the owned workspace root\",\n );\n }\n if (config.repositoryMode === \"isolated\")\n await validateIsolatedMount(context, source, target);\n if (info.isFile() && !internal.includes(volume)) {\n const parent = posix.dirname(target);\n invariant(\n parent === home || parent.startsWith(home + \"/\"),\n \"File mounts must live inside the agent home; mount the containing directory for other destinations\",\n );\n for (let folder = parent; folder !== home; folder = posix.dirname(folder))\n fileParents.add(folder);\n }\n invariant(\n !source.includes(\",\") && !target.includes(\",\"),\n \"Mount paths cannot contain commas\",\n );\n invariant(\n config.label === false ||\n platform !== \"linux\" ||\n (!source.includes(\":\") && !target.includes(\":\")),\n \"SELinux-labelled mount paths cannot contain colons; use label: false for --mount syntax\",\n );\n if (config.label === false || platform !== \"linux\")\n volumes.push(\n \"--mount\",\n `type=bind,source=${source},target=${target}${volume.readOnly ? \",readonly\" : \"\"}`,\n );\n else\n volumes.push(\n \"--volume\",\n `${source}:${target}:${volume.readOnly ? \"ro,\" : \"\"}${config.label ?? \"z\"}`,\n );\n }\n\n return { env, volumes, fileParents };\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { validateEgress } from "../domain/egress.js";
2
2
  import { invariant } from "../domain/errors.js";
3
+ import { egressCidr } from "./egress/address.js";
3
4
  export function containerNetworks(options) {
4
5
  const policy = validateEgress(options.egress);
5
6
  const networks = typeof options.networks === "string"
@@ -7,7 +8,21 @@ export function containerNetworks(options) {
7
8
  : [...(options.networks ?? [])];
8
9
  if (!policy)
9
10
  return networks;
10
- invariant(policy.mode === "deny-all", "Docker/Podman cannot enforce egress allowlists; use Vercel or an externally managed firewall");
11
+ if (policy.mode === "allowlist") {
12
+ for (const cidr of [
13
+ ...(policy.allowCidrs ?? []),
14
+ ...(policy.denyCidrs ?? []),
15
+ ]) {
16
+ try {
17
+ egressCidr(cidr);
18
+ }
19
+ catch {
20
+ invariant(false, "IPv4-mapped egress CIDRs require a prefix of at least 96");
21
+ }
22
+ }
23
+ invariant(networks.length === 0, "egress allowlist conflicts with configured networks");
24
+ return [];
25
+ }
11
26
  invariant(networks.every((network) => network === "none"), "egress deny-all conflicts with configured networks");
12
27
  return ["none"];
13
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"container-network.js","sourceRoot":"","sources":["../../src/providers/container-network.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,MAAM,UAAU,iBAAiB,CAC/B,OAAyB;IAEzB,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,QAAQ,GACZ,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAClC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;QACpB,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,SAAS,CACP,MAAM,CAAC,IAAI,KAAK,UAAU,EAC1B,8FAA8F,CAC/F,CAAC;IACF,SAAS,CACP,QAAQ,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,MAAM,CAAC,EAC/C,oDAAoD,CACrD,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC","sourcesContent":["import { validateEgress } from \"../domain/egress.ts\";\nimport { invariant } from \"../domain/errors.ts\";\nimport type { ContainerOptions } from \"./container.types.ts\";\n\nexport function containerNetworks(\n options: ContainerOptions,\n): readonly string[] {\n const policy = validateEgress(options.egress);\n const networks =\n typeof options.networks === \"string\"\n ? [options.networks]\n : [...(options.networks ?? [])];\n if (!policy) return networks;\n invariant(\n policy.mode === \"deny-all\",\n \"Docker/Podman cannot enforce egress allowlists; use Vercel or an externally managed firewall\",\n );\n invariant(\n networks.every((network) => network === \"none\"),\n \"egress deny-all conflicts with configured networks\",\n );\n return [\"none\"];\n}\n"]}
1
+ {"version":3,"file":"container-network.js","sourceRoot":"","sources":["../../src/providers/container-network.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,UAAU,iBAAiB,CAC/B,OAAyB;IAEzB,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,QAAQ,GACZ,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAClC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;QACpB,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,KAAK,MAAM,IAAI,IAAI;YACjB,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;YAC5B,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;SAC5B,EAAE,CAAC;YACF,IAAI,CAAC;gBACH,UAAU,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,CACP,KAAK,EACL,0DAA0D,CAC3D,CAAC;YACJ,CAAC;QACH,CAAC;QACD,SAAS,CACP,QAAQ,CAAC,MAAM,KAAK,CAAC,EACrB,qDAAqD,CACtD,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,SAAS,CACP,QAAQ,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,MAAM,CAAC,EAC/C,oDAAoD,CACrD,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC","sourcesContent":["import { validateEgress } from \"../domain/egress.ts\";\nimport { invariant } from \"../domain/errors.ts\";\nimport type { ContainerOptions } from \"./container.types.ts\";\nimport { egressCidr } from \"./egress/address.ts\";\n\nexport function containerNetworks(\n options: ContainerOptions,\n): readonly string[] {\n const policy = validateEgress(options.egress);\n const networks =\n typeof options.networks === \"string\"\n ? [options.networks]\n : [...(options.networks ?? [])];\n if (!policy) return networks;\n if (policy.mode === \"allowlist\") {\n for (const cidr of [\n ...(policy.allowCidrs ?? []),\n ...(policy.denyCidrs ?? []),\n ]) {\n try {\n egressCidr(cidr);\n } catch {\n invariant(\n false,\n \"IPv4-mapped egress CIDRs require a prefix of at least 96\",\n );\n }\n }\n invariant(\n networks.length === 0,\n \"egress allowlist conflicts with configured networks\",\n );\n return [];\n }\n invariant(\n networks.every((network) => network === \"none\"),\n \"egress deny-all conflicts with configured networks\",\n );\n return [\"none\"];\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { containerUserNamespace } from "./container-egress.js";
1
2
  export function containerPlan({ config, engine, user, root, name, image, volumes, fileParents, }) {
2
3
  const networks = typeof config.networks === "string"
3
4
  ? [config.networks]
@@ -9,19 +10,15 @@ export function containerPlan({ config, engine, user, root, name, image, volumes
9
10
  "--init",
10
11
  "--user",
11
12
  `${user.uid}:${user.gid}`,
12
- ...(engine === "podman" &&
13
- config.userns !== false &&
14
- (process.getuid?.() !== 0 || config.userns === "keep-id")
15
- ? [
16
- "--userns",
17
- config.user ? `keep-id:uid=${user.uid},gid=${user.gid}` : "keep-id",
18
- ]
19
- : []),
13
+ ...(engine === "podman" && config.egress?.mode === "allowlist"
14
+ ? ["--userns", `container:${name}-egress`]
15
+ : containerUserNamespace(engine, config, user)),
20
16
  "--workdir",
21
17
  config.repositoryMode === "isolated" ? "/" : root,
22
18
  "--cap-drop",
23
19
  "ALL",
24
- ...(fileParents.size ||
20
+ ...(engine === "podman" ||
21
+ fileParents.size ||
25
22
  config.caches?.length ||
26
23
  config.repositoryMode === "isolated"
27
24
  ? ["--cap-add", "CHOWN"]
@@ -30,7 +27,7 @@ export function containerPlan({ config, engine, user, root, name, image, volumes
30
27
  "no-new-privileges",
31
28
  "--tmpfs",
32
29
  engine === "podman"
33
- ? "/home/agent:rw,mode=1777"
30
+ ? "/home/agent:rw,mode=0700"
34
31
  : `/home/agent:rw,uid=${fileParents.size ? 0 : user.uid},gid=${fileParents.size ? 0 : user.gid},mode=0700`,
35
32
  ...volumes,
36
33
  ...networks.flatMap((network) => ["--network", network]),
@@ -1 +1 @@
1
- {"version":3,"file":"container-plan.js","sourceRoot":"","sources":["../../src/providers/container-plan.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,OAAO,EACP,WAAW,GACU;IACrB,MAAM,QAAQ,GACZ,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;QACjC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QACnB,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG;QACX,QAAQ;QACR,QAAQ;QACR,IAAI;QACJ,QAAQ;QACR,QAAQ;QACR,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;QACzB,GAAG,CAAC,MAAM,KAAK,QAAQ;YACvB,MAAM,CAAC,MAAM,KAAK,KAAK;YACvB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC;YACvD,CAAC,CAAC;gBACE,UAAU;gBACV,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,GAAG,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;aACpE;YACH,CAAC,CAAC,EAAE,CAAC;QACP,WAAW;QACX,MAAM,CAAC,cAAc,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI;QACjD,YAAY;QACZ,KAAK;QACL,GAAG,CAAC,WAAW,CAAC,IAAI;YACpB,MAAM,CAAC,MAAM,EAAE,MAAM;YACrB,MAAM,CAAC,cAAc,KAAK,UAAU;YAClC,CAAC,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC;YACxB,CAAC,CAAC,EAAE,CAAC;QACP,gBAAgB;QAChB,mBAAmB;QACnB,SAAS;QACT,MAAM,KAAK,QAAQ;YACjB,CAAC,CAAC,0BAA0B;YAC5B,CAAC,CAAC,sBAAsB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,QAAQ,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,YAAY;QAC5G,GAAG,OAAO;QACV,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACxD,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3E,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACnE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,cAAc;QACd,OAAO;QACP,KAAK;QACL,UAAU;KACX,CAAC;IAEF,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import type { ContainerPlanOptions } from \"./container.types.ts\";\n\nexport function containerPlan({\n config,\n engine,\n user,\n root,\n name,\n image,\n volumes,\n fileParents,\n}: ContainerPlanOptions): string[] {\n const networks =\n typeof config.networks === \"string\"\n ? [config.networks]\n : (config.networks ?? []);\n const args = [\n \"create\",\n \"--name\",\n name,\n \"--init\",\n \"--user\",\n `${user.uid}:${user.gid}`,\n ...(engine === \"podman\" &&\n config.userns !== false &&\n (process.getuid?.() !== 0 || config.userns === \"keep-id\")\n ? [\n \"--userns\",\n config.user ? `keep-id:uid=${user.uid},gid=${user.gid}` : \"keep-id\",\n ]\n : []),\n \"--workdir\",\n config.repositoryMode === \"isolated\" ? \"/\" : root,\n \"--cap-drop\",\n \"ALL\",\n ...(fileParents.size ||\n config.caches?.length ||\n config.repositoryMode === \"isolated\"\n ? [\"--cap-add\", \"CHOWN\"]\n : []),\n \"--security-opt\",\n \"no-new-privileges\",\n \"--tmpfs\",\n engine === \"podman\"\n ? \"/home/agent:rw,mode=1777\"\n : `/home/agent:rw,uid=${fileParents.size ? 0 : user.uid},gid=${fileParents.size ? 0 : user.gid},mode=0700`,\n ...volumes,\n ...networks.flatMap((network) => [\"--network\", network]),\n ...(config.groups ?? []).flatMap((group) => [\"--group-add\", String(group)]),\n ...(config.devices ?? []).flatMap((device) => [\"--device\", device]),\n ...(config.cpus ? [\"--cpus\", String(config.cpus)] : []),\n ...(config.memoryMb ? [\"--memory\", `${config.memoryMb}m`] : []),\n \"--entrypoint\",\n \"sleep\",\n image,\n \"infinity\",\n ];\n\n return args;\n}\n"]}
1
+ {"version":3,"file":"container-plan.js","sourceRoot":"","sources":["../../src/providers/container-plan.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,MAAM,UAAU,aAAa,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,OAAO,EACP,WAAW,GACU;IACrB,MAAM,QAAQ,GACZ,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;QACjC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QACnB,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG;QACX,QAAQ;QACR,QAAQ;QACR,IAAI;QACJ,QAAQ;QACR,QAAQ;QACR,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;QACzB,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,KAAK,WAAW;YAC5D,CAAC,CAAC,CAAC,UAAU,EAAE,aAAa,IAAI,SAAS,CAAC;YAC1C,CAAC,CAAC,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACjD,WAAW;QACX,MAAM,CAAC,cAAc,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI;QACjD,YAAY;QACZ,KAAK;QACL,GAAG,CAAC,MAAM,KAAK,QAAQ;YACvB,WAAW,CAAC,IAAI;YAChB,MAAM,CAAC,MAAM,EAAE,MAAM;YACrB,MAAM,CAAC,cAAc,KAAK,UAAU;YAClC,CAAC,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC;YACxB,CAAC,CAAC,EAAE,CAAC;QACP,gBAAgB;QAChB,mBAAmB;QACnB,SAAS;QACT,MAAM,KAAK,QAAQ;YACjB,CAAC,CAAC,0BAA0B;YAC5B,CAAC,CAAC,sBAAsB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,QAAQ,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,YAAY;QAC5G,GAAG,OAAO;QACV,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACxD,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3E,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACnE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,cAAc;QACd,OAAO;QACP,KAAK;QACL,UAAU;KACX,CAAC;IAEF,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import type { ContainerPlanOptions } from \"./container.types.ts\";\nimport { containerUserNamespace } from \"./container-egress.ts\";\n\nexport function containerPlan({\n config,\n engine,\n user,\n root,\n name,\n image,\n volumes,\n fileParents,\n}: ContainerPlanOptions): string[] {\n const networks =\n typeof config.networks === \"string\"\n ? [config.networks]\n : (config.networks ?? []);\n const args = [\n \"create\",\n \"--name\",\n name,\n \"--init\",\n \"--user\",\n `${user.uid}:${user.gid}`,\n ...(engine === \"podman\" && config.egress?.mode === \"allowlist\"\n ? [\"--userns\", `container:${name}-egress`]\n : containerUserNamespace(engine, config, user)),\n \"--workdir\",\n config.repositoryMode === \"isolated\" ? \"/\" : root,\n \"--cap-drop\",\n \"ALL\",\n ...(engine === \"podman\" ||\n fileParents.size ||\n config.caches?.length ||\n config.repositoryMode === \"isolated\"\n ? [\"--cap-add\", \"CHOWN\"]\n : []),\n \"--security-opt\",\n \"no-new-privileges\",\n \"--tmpfs\",\n engine === \"podman\"\n ? \"/home/agent:rw,mode=0700\"\n : `/home/agent:rw,uid=${fileParents.size ? 0 : user.uid},gid=${fileParents.size ? 0 : user.gid},mode=0700`,\n ...volumes,\n ...networks.flatMap((network) => [\"--network\", network]),\n ...(config.groups ?? []).flatMap((group) => [\"--group-add\", String(group)]),\n ...(config.devices ?? []).flatMap((device) => [\"--device\", device]),\n ...(config.cpus ? [\"--cpus\", String(config.cpus)] : []),\n ...(config.memoryMb ? [\"--memory\", `${config.memoryMb}m`] : []),\n \"--entrypoint\",\n \"sleep\",\n image,\n \"infinity\",\n ];\n\n return args;\n}\n"]}
@@ -14,7 +14,7 @@ export async function recoverContainer(engine, executor, resourceId, options = {
14
14
  "ls",
15
15
  "--all",
16
16
  "--filter",
17
- `name=^${resourceId}$`,
17
+ `name=^${resourceId}(-egress)?$`,
18
18
  "--format",
19
19
  "{{.Names}}",
20
20
  ],
@@ -22,7 +22,12 @@ export async function recoverContainer(engine, executor, resourceId, options = {
22
22
  }, executor);
23
23
  if (!result.stdout.trim())
24
24
  return;
25
- invariant(result.stdout.trim() === resourceId, "Recoverable container identity mismatch");
26
- await requireSuccess({ executable: engine, arguments: ["rm", "--force", resourceId], ...limits }, executor);
25
+ const names = result.stdout.trim().split(/\r?\n/);
26
+ invariant(names.every((name) => name === resourceId || name === `${resourceId}-egress`), "Recoverable container identity mismatch");
27
+ for (const name of [resourceId, `${resourceId}-egress`]) {
28
+ if (!names.includes(name))
29
+ continue;
30
+ await requireSuccess({ executable: engine, arguments: ["rm", "--force", name], ...limits }, executor);
31
+ }
27
32
  }
28
33
  //# sourceMappingURL=container-recovery.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"container-recovery.js","sourceRoot":"","sources":["../../src/providers/container-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAuB,EACvB,QAAkB,EAClB,UAAkB,EAClB,UAA2B,EAAE;IAE7B,SAAS,CACP,yBAAyB,CAAC,IAAI,CAAC,UAAU,CAAC,EAC1C,wCAAwC,CACzC,CAAC;IACF,MAAM,MAAM,GAAG;QACb,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,iBAAiB,CAAC,SAAS;QAC7D,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,cAAc,CACjC;QACE,UAAU,EAAE,MAAM;QAClB,SAAS,EAAE;YACT,WAAW;YACX,IAAI;YACJ,OAAO;YACP,UAAU;YACV,SAAS,UAAU,GAAG;YACtB,UAAU;YACV,YAAY;SACb;QACD,GAAG,MAAM;KACV,EACD,QAAQ,CACT,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAAE,OAAO;IAClC,SAAS,CACP,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,UAAU,EACnC,yCAAyC,CAC1C,CAAC;IACF,MAAM,cAAc,CAClB,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,GAAG,MAAM,EAAE,EAC3E,QAAQ,CACT,CAAC;AACJ,CAAC","sourcesContent":["import { invariant } from \"../domain/errors.ts\";\nimport type { TransferOptions } from \"../domain/sandbox.types.ts\";\nimport { requireSuccess } from \"../infrastructure/process.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport { containerDefaults } from \"./container.constants.ts\";\nimport type { ContainerEngine } from \"./container.types.ts\";\n\nexport async function recoverContainer(\n engine: ContainerEngine,\n executor: Executor,\n resourceId: string,\n options: TransferOptions = {},\n): Promise<void> {\n invariant(\n /^outpost-[a-f0-9-]{36}$/.test(resourceId),\n \"Invalid recoverable container identity\",\n );\n const limits = {\n deadlineMs: options.deadlineMs ?? containerDefaults.cleanupMs,\n ...(options.signal ? { signal: options.signal } : {}),\n };\n const result = await requireSuccess(\n {\n executable: engine,\n arguments: [\n \"container\",\n \"ls\",\n \"--all\",\n \"--filter\",\n `name=^${resourceId}$`,\n \"--format\",\n \"{{.Names}}\",\n ],\n ...limits,\n },\n executor,\n );\n if (!result.stdout.trim()) return;\n invariant(\n result.stdout.trim() === resourceId,\n \"Recoverable container identity mismatch\",\n );\n await requireSuccess(\n { executable: engine, arguments: [\"rm\", \"--force\", resourceId], ...limits },\n executor,\n );\n}\n"]}
1
+ {"version":3,"file":"container-recovery.js","sourceRoot":"","sources":["../../src/providers/container-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAuB,EACvB,QAAkB,EAClB,UAAkB,EAClB,UAA2B,EAAE;IAE7B,SAAS,CACP,yBAAyB,CAAC,IAAI,CAAC,UAAU,CAAC,EAC1C,wCAAwC,CACzC,CAAC;IACF,MAAM,MAAM,GAAG;QACb,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,iBAAiB,CAAC,SAAS;QAC7D,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,cAAc,CACjC;QACE,UAAU,EAAE,MAAM;QAClB,SAAS,EAAE;YACT,WAAW;YACX,IAAI;YACJ,OAAO;YACP,UAAU;YACV,SAAS,UAAU,aAAa;YAChC,UAAU;YACV,YAAY;SACb;QACD,GAAG,MAAM;KACV,EACD,QAAQ,CACT,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAAE,OAAO;IAClC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClD,SAAS,CACP,KAAK,CAAC,KAAK,CACT,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,GAAG,UAAU,SAAS,CACjE,EACD,yCAAyC,CAC1C,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,GAAG,UAAU,SAAS,CAAC,EAAE,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,SAAS;QACpC,MAAM,cAAc,CAClB,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,EACrE,QAAQ,CACT,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["import { invariant } from \"../domain/errors.ts\";\nimport type { TransferOptions } from \"../domain/sandbox.types.ts\";\nimport { requireSuccess } from \"../infrastructure/process.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport { containerDefaults } from \"./container.constants.ts\";\nimport type { ContainerEngine } from \"./container.types.ts\";\n\nexport async function recoverContainer(\n engine: ContainerEngine,\n executor: Executor,\n resourceId: string,\n options: TransferOptions = {},\n): Promise<void> {\n invariant(\n /^outpost-[a-f0-9-]{36}$/.test(resourceId),\n \"Invalid recoverable container identity\",\n );\n const limits = {\n deadlineMs: options.deadlineMs ?? containerDefaults.cleanupMs,\n ...(options.signal ? { signal: options.signal } : {}),\n };\n const result = await requireSuccess(\n {\n executable: engine,\n arguments: [\n \"container\",\n \"ls\",\n \"--all\",\n \"--filter\",\n `name=^${resourceId}(-egress)?$`,\n \"--format\",\n \"{{.Names}}\",\n ],\n ...limits,\n },\n executor,\n );\n if (!result.stdout.trim()) return;\n const names = result.stdout.trim().split(/\\r?\\n/);\n invariant(\n names.every(\n (name) => name === resourceId || name === `${resourceId}-egress`,\n ),\n \"Recoverable container identity mismatch\",\n );\n for (const name of [resourceId, `${resourceId}-egress`]) {\n if (!names.includes(name)) continue;\n await requireSuccess(\n { executable: engine, arguments: [\"rm\", \"--force\", name], ...limits },\n executor,\n );\n }\n}\n"]}
@@ -1,8 +1,12 @@
1
+ import { validateContainerImage } from "../domain/container-image.js";
1
2
  import { recoverContainer } from "./container-recovery.js";
2
3
  import { execFileSync } from "node:child_process";
3
4
  import { randomUUID } from "node:crypto";
4
5
  import { basename } from "node:path";
5
6
  import { containerNetworks } from "./container-network.js";
7
+ import { validateEgress } from "../domain/egress.js";
8
+ import { createContainerEgress } from "./container-egress.js";
9
+ import { egressVariables } from "./egress/egress.constants.js";
6
10
  import { invariant } from "../domain/errors.js";
7
11
  import { executeProcess, quote, requireSuccess, } from "../infrastructure/process.js";
8
12
  import { registerCleanup } from "../infrastructure/shutdown.js";
@@ -21,11 +25,22 @@ export function imageName(repository) {
21
25
  .replace(/[^a-z0-9_.-]/g, "-") || "workspace"}`;
22
26
  }
23
27
  export function containerProvider(engine, options = {}, executor = executeProcess, platform = process.platform) {
28
+ if (options.image !== undefined)
29
+ validateContainerImage(options.image);
30
+ const policy = validateEgress(options.egress);
24
31
  const config = {
25
32
  ...options,
33
+ ...(policy ? { egress: policy } : {}),
26
34
  networks: containerNetworks(options),
27
35
  caches: (options.caches ?? []).map((cache) => ({ ...cache })),
28
36
  };
37
+ if (config.egressImage !== undefined) {
38
+ invariant(config.egress?.mode === "allowlist", "egressImage requires an egress allowlist");
39
+ invariant(typeof config.egressImage === "string" &&
40
+ config.egressImage.length > 0 &&
41
+ !config.egressImage.startsWith("-") &&
42
+ !/\s/.test(config.egressImage), "Invalid egressImage reference");
43
+ }
29
44
  invariant(config.repositoryMode === undefined ||
30
45
  config.repositoryMode === "mounted" ||
31
46
  config.repositoryMode === "isolated", "repositoryMode must be mounted or isolated");
@@ -67,11 +82,18 @@ export function containerProvider(engine, options = {}, executor = executeProces
67
82
  ? containerDefaults.isolatedRoot
68
83
  : containerDefaults.root, home = containerDefaults.home;
69
84
  const { env, volumes, fileParents } = await containerMounts(context, config, platform, root, home);
85
+ const egress = config.egress?.mode === "allowlist"
86
+ ? createContainerEgress(call, name, config, engine, user)
87
+ : undefined;
88
+ if (egress)
89
+ Object.assign(env, egressVariables);
70
90
  const caches = await cacheMounts(config.caches ?? [], context.repository, image, user, context.workspaceIdentity);
71
91
  for (const cache of caches)
72
92
  volumes.push("--volume", `${cache.volume}:${cache.target}:nocopy`);
73
93
  const args = containerPlan({
74
- config,
94
+ config: egress
95
+ ? { ...config, networks: [`container:${egress.name}`] }
96
+ : config,
75
97
  engine,
76
98
  user,
77
99
  root,
@@ -81,10 +103,17 @@ export function containerProvider(engine, options = {}, executor = executeProces
81
103
  fileParents,
82
104
  });
83
105
  let closed = false, releasing;
106
+ let attempted = false;
84
107
  let unregister = () => { };
85
108
  const release = () => {
86
109
  closed = true;
87
- releasing ??= call(["rm", "--force", name])
110
+ releasing ??= (async () => {
111
+ if (attempted) {
112
+ await call(["rm", "--force", name]);
113
+ attempted = false;
114
+ }
115
+ await egress?.release();
116
+ })()
88
117
  .then(() => {
89
118
  unregister();
90
119
  })
@@ -106,6 +135,8 @@ export function containerProvider(engine, options = {}, executor = executeProces
106
135
  ], context.signal ? { signal: context.signal } : {});
107
136
  await context.registerRecovery?.(name);
108
137
  context.signal?.throwIfAborted();
138
+ await egress?.start(context.signal);
139
+ attempted = true;
109
140
  await call(args, {
110
141
  ...(context.signal ? { signal: context.signal } : {}),
111
142
  });
@@ -132,7 +163,7 @@ export function containerProvider(engine, options = {}, executor = executeProces
132
163
  "-c",
133
164
  `mkdir -p ${quote(parent)} && chown ${user.uid}:${user.gid} ${quote(parent)}`,
134
165
  ]);
135
- if (fileParents.size)
166
+ if (engine === "podman" || fileParents.size)
136
167
  await call([
137
168
  "exec",
138
169
  "--user",
@@ -170,8 +201,8 @@ export function containerProvider(engine, options = {}, executor = executeProces
170
201
  throw cause;
171
202
  }
172
203
  unregister = registerCleanup(release, () => {
173
- if (!closed)
174
- execFileSync(engine, ["rm", "--force", name], {
204
+ if (attempted)
205
+ execFileSync(engine, ["rm", "--force", name, ...(egress ? [egress.name] : [])], {
175
206
  timeout: containerDefaults.cleanupMs,
176
207
  stdio: "ignore",
177
208
  windowsHide: true,