@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
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-lock.js","sourceRoot":"","sources":["../../src/infrastructure/workspace-lock.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n lstat,\n mkdir,\n readdir,\n realpath,\n rm,\n rmdir,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { invariant } from \"../domain/errors.ts\";\nimport { inside } from \"./files.ts\";\nimport {\n workspaceLockAttempts,\n workspaceLocksDirectory,\n} from \"./workspace-lock.constants.ts\";\nimport type {\n WorkspacePathLock,\n WorkspacePathRelease,\n WorkspacePathRecoveryOptions,\n} from \"./workspace-lock.types.ts\";\nimport type { WorkspacePathGate } from \"./workspace-lock.types.ts\";\nimport { readInspectionFile } from \"./inspection-file.ts\";\n\nexport async function canonicalWorkspacePath(value: string): Promise<string> {\n const path = resolve(value);\n try {\n return await realpath(path);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\"))\n throw error;\n invariant(dirname(path) !== path, \"Workspace path cannot be resolved\");\n return join(\n await canonicalWorkspacePath(dirname(path)),\n path.slice(dirname(path).length + 1),\n );\n }\n}\n\nasync function withRegistry<T>(action: () => Promise<T>): Promise<T> {\n await mkdir(workspaceLocksDirectory, { recursive: true, mode: 0o700 });\n const info = await lstat(workspaceLocksDirectory);\n invariant(\n info.isDirectory() &&\n !info.isSymbolicLink() &&\n (process.getuid === undefined || info.uid === process.getuid()),\n \"Unsafe workspace lock registry\",\n );\n const gate = join(workspaceLocksDirectory, \"gate\");\n for (let attempt = 0; attempt < workspaceLockAttempts; attempt++) {\n try {\n await mkdir(gate, { mode: 0o700 });\n } catch (error) {\n if (!(\n error instanceof Error &&\n \"code\" in error &&\n error.code === \"EEXIST\"\n ))\n throw error;\n await setTimeout(10);\n continue;\n }\n try {\n return await action();\n } finally {\n await rm(gate, { recursive: true });\n }\n }\n throw new Error(\n \"Workspace lock registry is busy; inspect abandoned ownership before recovery\",\n );\n}\n\nfunction parseLock(value: unknown): WorkspacePathLock {\n invariant(\n value &&\n typeof value === \"object\" &&\n \"id\" in value &&\n typeof value.id === \"string\" &&\n \"directory\" in value &&\n typeof value.directory === \"string\" &&\n \"writable\" in value &&\n typeof value.writable === \"boolean\",\n \"Invalid workspace path lock\",\n );\n const identity =\n \"identity\" in value ? parseIdentity(value.identity) : undefined;\n const ancestors =\n \"ancestors\" in value\n ? (() => {\n invariant(\n Array.isArray(value.ancestors) && value.ancestors.length <= 1024,\n \"Invalid workspace lock ancestors\",\n );\n return value.ancestors.map(parseIdentity);\n })()\n : undefined;\n const lockDirectory = value.directory;\n const excludedDirectories =\n \"excludedDirectories\" in value\n ? (() => {\n invariant(\n !value.writable &&\n Array.isArray(value.excludedDirectories) &&\n value.excludedDirectories.length <= 128,\n \"Invalid workspace lock exclusions\",\n );\n return value.excludedDirectories.map((path: unknown) => {\n invariant(\n typeof path === \"string\" &&\n path === resolve(path) &&\n path !== lockDirectory &&\n inside(lockDirectory, path),\n \"Invalid workspace lock exclusion path\",\n );\n return path;\n });\n })()\n : undefined;\n return {\n id: value.id,\n directory: value.directory,\n writable: value.writable,\n ...(identity ? { identity } : {}),\n ...(ancestors ? { ancestors } : {}),\n ...(excludedDirectories ? { excludedDirectories } : {}),\n };\n}\n\nfunction parseIdentity(value: unknown): WorkspacePathGate {\n invariant(\n value &&\n typeof value === \"object\" &&\n \"device\" in value &&\n typeof value.device === \"number\" &&\n \"inode\" in value &&\n typeof value.inode === \"number\",\n \"Invalid workspace lock filesystem identity\",\n );\n return { device: value.device, inode: value.inode };\n}\n\nasync function readLock(path: string): Promise<WorkspacePathLock> {\n return parseLock(\n JSON.parse((await readInspectionFile(path, 256 * 1024)).toString(\"utf8\")),\n );\n}\n\nasync function pathIdentities(path: string) {\n const ancestors: WorkspacePathGate[] = [];\n let identity: WorkspacePathGate | undefined;\n for (let current = path; ; current = dirname(current)) {\n const info = await lstat(current).catch((error) => {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n return undefined;\n throw error;\n });\n if (info) {\n const entry = { device: info.dev, inode: info.ino };\n ancestors.push(entry);\n if (current === path) identity = entry;\n }\n invariant(ancestors.length <= 1024, \"Workspace path is too deep\");\n if (dirname(current) === current) break;\n }\n return { ...(identity ? { identity } : {}), ancestors };\n}\n\nfunction containsIdentity(\n identities: readonly WorkspacePathGate[] | undefined,\n target: WorkspacePathGate | undefined,\n): boolean {\n return (\n !!target &&\n !!identities?.some(\n (identity) =>\n identity.device === target.device && identity.inode === target.inode,\n )\n );\n}\n\nexport async function lockWorkspacePath(\n value: string,\n writable: boolean,\n): Promise<WorkspacePathRelease> {\n return acquireWorkspacePath(value, writable);\n}\n\nexport async function lockWorkspaceCopySource(\n value: string,\n runtimeDirectory: string,\n): Promise<WorkspacePathRelease> {\n const source = await canonicalWorkspacePath(value);\n const runtime = await canonicalWorkspacePath(runtimeDirectory);\n return acquireWorkspacePath(\n source,\n false,\n inside(source, runtime) && source !== runtime ? [runtime] : [],\n );\n}\n\nasync function acquireWorkspacePath(\n value: string,\n writable: boolean,\n excludedDirectories: readonly string[] = [],\n): Promise<WorkspacePathRelease> {\n const directory = await canonicalWorkspacePath(value);\n const id = randomUUID();\n const identities = await pathIdentities(directory);\n const path = join(workspaceLocksDirectory, `${id}.json`);\n await withRegistry(async () => {\n for (const name of await readdir(workspaceLocksDirectory)) {\n if (!name.endsWith(\".json\")) continue;\n const existing = await readLock(join(workspaceLocksDirectory, name));\n if (\n existing.excludedDirectories?.some((excluded) =>\n inside(excluded, directory),\n ) ||\n excludedDirectories.some((excluded) =>\n inside(excluded, existing.directory),\n )\n )\n continue;\n invariant(\n !(writable || existing.writable) ||\n !(\n inside(directory, existing.directory) ||\n inside(existing.directory, directory) ||\n containsIdentity(identities.ancestors, existing.identity) ||\n containsIdentity(existing.ancestors, identities.identity)\n ),\n \"Workspace path overlaps an active writer\",\n );\n }\n await writeFile(\n path,\n JSON.stringify({\n id,\n directory,\n writable,\n ...identities,\n ...(excludedDirectories.length ? { excludedDirectories } : {}),\n }),\n { flag: \"wx\", mode: 0o600 },\n );\n });\n let released = false;\n return Object.assign(\n async () => {\n if (released) return;\n await withRegistry(async () => {\n const current = await readLock(path);\n invariant(current.id === id, \"Workspace path lock ownership changed\");\n await rm(path);\n });\n released = true;\n },\n { id, directory },\n );\n}\n\nexport async function inspectWorkspacePathLocks(): Promise<\n readonly WorkspacePathLock[]\n> {\n return withRegistry(async () => {\n const locks = [];\n for (const name of await readdir(workspaceLocksDirectory)) {\n if (name.endsWith(\".json\"))\n locks.push(await readLock(join(workspaceLocksDirectory, name)));\n }\n return locks;\n });\n}\n\nexport async function recoverWorkspacePathLock(\n id: string,\n expectedDirectory: string,\n options: WorkspacePathRecoveryOptions,\n): Promise<void> {\n invariant(\n options.processesStopped === true && /^[a-f0-9-]{36}$/.test(id),\n \"Workspace lock recovery requires explicit stopped-process authorization\",\n );\n const directory = await canonicalWorkspacePath(expectedDirectory);\n await withRegistry(async () => {\n const path = join(workspaceLocksDirectory, `${id}.json`);\n const lock = await readLock(path);\n invariant(\n lock.id === id && lock.directory === directory,\n \"Workspace lock recovery identity mismatch\",\n );\n await rm(path);\n });\n}\n\nexport async function inspectWorkspaceRegistryGate(): Promise<\n WorkspacePathGate | undefined\n> {\n const info = await lstat(join(workspaceLocksDirectory, \"gate\")).catch(\n (error) => {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n return undefined;\n throw error;\n },\n );\n invariant(\n !info || (info.isDirectory() && !info.isSymbolicLink()),\n \"Unsafe workspace registry gate\",\n );\n return info ? { device: info.dev, inode: info.ino } : undefined;\n}\n\nexport async function recoverWorkspaceRegistryGate(\n expected: WorkspacePathGate,\n options: WorkspacePathRecoveryOptions,\n): Promise<void> {\n invariant(\n options.processesStopped === true,\n \"Registry recovery requires explicit stopped-process authorization\",\n );\n const current = await inspectWorkspaceRegistryGate();\n invariant(\n current?.device === expected.device && current.inode === expected.inode,\n \"Workspace registry gate changed\",\n );\n await rmdir(join(workspaceLocksDirectory, \"gate\"));\n}\n"]}
1
+ {"version":3,"file":"workspace-lock.js","sourceRoot":"","sources":["../../src/infrastructure/workspace-lock.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n lstat,\n mkdir,\n readdir,\n realpath,\n rm,\n rmdir,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { setTimeout } from \"node:timers/promises\";\nimport { invariant } from \"../domain/errors.ts\";\nimport { inside } from \"./files.ts\";\nimport {\n workspaceLocksDirectory,\n workspaceRegistryPendingDeleteAttempts,\n workspaceRegistryStaleMs,\n} from \"./workspace-lock.constants.ts\";\nimport type {\n WorkspacePathLock,\n WorkspacePathRelease,\n WorkspacePathRecoveryOptions,\n} from \"./workspace-lock.types.ts\";\nimport type { WorkspacePathGate } from \"./workspace-lock.types.ts\";\nimport { readInspectionFile } from \"./inspection-file.ts\";\n\nexport async function canonicalWorkspacePath(value: string): Promise<string> {\n const path = resolve(value);\n try {\n return await realpath(path);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\"))\n throw error;\n invariant(dirname(path) !== path, \"Workspace path cannot be resolved\");\n return join(\n await canonicalWorkspacePath(dirname(path)),\n path.slice(dirname(path).length + 1),\n );\n }\n}\n\nasync function withRegistry<T>(action: () => Promise<T>): Promise<T> {\n await mkdir(workspaceLocksDirectory, { recursive: true, mode: 0o700 });\n const info = await lstat(workspaceLocksDirectory);\n invariant(\n info.isDirectory() &&\n !info.isSymbolicLink() &&\n (process.getuid === undefined || info.uid === process.getuid()),\n \"Unsafe workspace lock registry\",\n );\n const gate = join(workspaceLocksDirectory, \"gate\");\n let observed: string | undefined;\n let since = Date.now();\n let pendingDeletes = 0;\n for (;;) {\n try {\n await mkdir(gate, { mode: 0o700 });\n } catch (error) {\n if (\n pendingDeletion(error) &&\n ++pendingDeletes < workspaceRegistryPendingDeleteAttempts\n ) {\n await setTimeout(10);\n continue;\n }\n if (!(\n error instanceof Error &&\n \"code\" in error &&\n error.code === \"EEXIST\"\n ))\n throw error;\n pendingDeletes = 0;\n // A gate that keeps changing belongs to live holders; only an unchanged one is reported.\n const current = await gateInstance(gate);\n if (current !== observed) since = Date.now();\n observed = current;\n if (current && Date.now() - since >= workspaceRegistryStaleMs)\n throw new Error(\n \"Workspace lock registry is busy; inspect abandoned ownership before recovery\",\n );\n await setTimeout(5 + Math.random() * 20);\n continue;\n }\n try {\n return await action();\n } finally {\n await rm(gate, { recursive: true, maxRetries: 5 });\n }\n }\n}\n\nasync function gateInstance(gate: string): Promise<string | undefined> {\n const info = await lstat(gate, { bigint: true }).catch((error) => {\n if (\n (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") ||\n pendingDeletion(error)\n )\n return undefined;\n throw error;\n });\n return info && `${info.dev}:${info.ino}:${info.birthtimeNs}:${info.ctimeNs}`;\n}\n\n// Windows reports EPERM while a just-released gate is pending deletion.\nfunction pendingDeletion(error: unknown): boolean {\n return (\n process.platform === \"win32\" &&\n error instanceof Error &&\n \"code\" in error &&\n error.code === \"EPERM\"\n );\n}\n\nfunction parseLock(value: unknown): WorkspacePathLock {\n invariant(\n value &&\n typeof value === \"object\" &&\n \"id\" in value &&\n typeof value.id === \"string\" &&\n \"directory\" in value &&\n typeof value.directory === \"string\" &&\n \"writable\" in value &&\n typeof value.writable === \"boolean\",\n \"Invalid workspace path lock\",\n );\n const identity =\n \"identity\" in value ? parseIdentity(value.identity) : undefined;\n const ancestors =\n \"ancestors\" in value\n ? (() => {\n invariant(\n Array.isArray(value.ancestors) && value.ancestors.length <= 1024,\n \"Invalid workspace lock ancestors\",\n );\n return value.ancestors.map(parseIdentity);\n })()\n : undefined;\n const lockDirectory = value.directory;\n const excludedDirectories =\n \"excludedDirectories\" in value\n ? (() => {\n invariant(\n !value.writable &&\n Array.isArray(value.excludedDirectories) &&\n value.excludedDirectories.length <= 128,\n \"Invalid workspace lock exclusions\",\n );\n return value.excludedDirectories.map((path: unknown) => {\n invariant(\n typeof path === \"string\" &&\n path === resolve(path) &&\n path !== lockDirectory &&\n inside(lockDirectory, path),\n \"Invalid workspace lock exclusion path\",\n );\n return path;\n });\n })()\n : undefined;\n return {\n id: value.id,\n directory: value.directory,\n writable: value.writable,\n ...(identity ? { identity } : {}),\n ...(ancestors ? { ancestors } : {}),\n ...(excludedDirectories ? { excludedDirectories } : {}),\n };\n}\n\nfunction parseIdentity(value: unknown): WorkspacePathGate {\n invariant(\n value &&\n typeof value === \"object\" &&\n \"device\" in value &&\n typeof value.device === \"number\" &&\n \"inode\" in value &&\n typeof value.inode === \"number\",\n \"Invalid workspace lock filesystem identity\",\n );\n return { device: value.device, inode: value.inode };\n}\n\nasync function readLock(path: string): Promise<WorkspacePathLock> {\n return parseLock(\n JSON.parse((await readInspectionFile(path, 256 * 1024)).toString(\"utf8\")),\n );\n}\n\nasync function pathIdentities(path: string) {\n const ancestors: WorkspacePathGate[] = [];\n let identity: WorkspacePathGate | undefined;\n for (let current = path; ; current = dirname(current)) {\n const info = await lstat(current).catch((error) => {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n return undefined;\n throw error;\n });\n if (info) {\n const entry = { device: info.dev, inode: info.ino };\n ancestors.push(entry);\n if (current === path) identity = entry;\n }\n invariant(ancestors.length <= 1024, \"Workspace path is too deep\");\n if (dirname(current) === current) break;\n }\n return { ...(identity ? { identity } : {}), ancestors };\n}\n\nfunction containsIdentity(\n identities: readonly WorkspacePathGate[] | undefined,\n target: WorkspacePathGate | undefined,\n): boolean {\n return (\n !!target &&\n !!identities?.some(\n (identity) =>\n identity.device === target.device && identity.inode === target.inode,\n )\n );\n}\n\nexport async function lockWorkspacePath(\n value: string,\n writable: boolean,\n): Promise<WorkspacePathRelease> {\n return acquireWorkspacePath(value, writable);\n}\n\nexport async function lockWorkspaceCopySource(\n value: string,\n runtimeDirectory: string,\n): Promise<WorkspacePathRelease> {\n const source = await canonicalWorkspacePath(value);\n const runtime = await canonicalWorkspacePath(runtimeDirectory);\n return acquireWorkspacePath(\n source,\n false,\n inside(source, runtime) && source !== runtime ? [runtime] : [],\n );\n}\n\nasync function acquireWorkspacePath(\n value: string,\n writable: boolean,\n excludedDirectories: readonly string[] = [],\n): Promise<WorkspacePathRelease> {\n const directory = await canonicalWorkspacePath(value);\n const id = randomUUID();\n const identities = await pathIdentities(directory);\n const path = join(workspaceLocksDirectory, `${id}.json`);\n await withRegistry(async () => {\n for (const name of await readdir(workspaceLocksDirectory)) {\n if (!name.endsWith(\".json\")) continue;\n const existing = await readLock(join(workspaceLocksDirectory, name));\n if (\n existing.excludedDirectories?.some((excluded) =>\n inside(excluded, directory),\n ) ||\n excludedDirectories.some((excluded) =>\n inside(excluded, existing.directory),\n )\n )\n continue;\n if (!(writable || existing.writable)) continue;\n // Recorded inodes count only while the locked path still resolves to them; deleted ones get reused.\n const current = await pathIdentities(existing.directory);\n const identity = containsIdentity(\n current.identity ? [current.identity] : [],\n existing.identity,\n )\n ? existing.identity\n : undefined;\n const ancestors = existing.ancestors?.filter((entry) =>\n containsIdentity(current.ancestors, entry),\n );\n invariant(\n !(\n inside(directory, existing.directory) ||\n inside(existing.directory, directory) ||\n containsIdentity(identities.ancestors, identity) ||\n containsIdentity(ancestors, identities.identity)\n ),\n \"Workspace path overlaps an active writer\",\n );\n }\n await writeFile(\n path,\n JSON.stringify({\n id,\n directory,\n writable,\n ...identities,\n ...(excludedDirectories.length ? { excludedDirectories } : {}),\n }),\n { flag: \"wx\", mode: 0o600 },\n );\n });\n let released = false;\n return Object.assign(\n async () => {\n if (released) return;\n await withRegistry(async () => {\n const current = await readLock(path);\n invariant(current.id === id, \"Workspace path lock ownership changed\");\n await rm(path);\n });\n released = true;\n },\n { id, directory },\n );\n}\n\nexport async function inspectWorkspacePathLocks(): Promise<\n readonly WorkspacePathLock[]\n> {\n return withRegistry(async () => {\n const locks = [];\n for (const name of await readdir(workspaceLocksDirectory)) {\n if (name.endsWith(\".json\"))\n locks.push(await readLock(join(workspaceLocksDirectory, name)));\n }\n return locks;\n });\n}\n\nexport async function recoverWorkspacePathLock(\n id: string,\n expectedDirectory: string,\n options: WorkspacePathRecoveryOptions,\n): Promise<void> {\n invariant(\n options.processesStopped === true && /^[a-f0-9-]{36}$/.test(id),\n \"Workspace lock recovery requires explicit stopped-process authorization\",\n );\n const directory = await canonicalWorkspacePath(expectedDirectory);\n await withRegistry(async () => {\n const path = join(workspaceLocksDirectory, `${id}.json`);\n const lock = await readLock(path);\n invariant(\n lock.id === id && lock.directory === directory,\n \"Workspace lock recovery identity mismatch\",\n );\n await rm(path);\n });\n}\n\nexport async function inspectWorkspaceRegistryGate(): Promise<\n WorkspacePathGate | undefined\n> {\n const info = await lstat(join(workspaceLocksDirectory, \"gate\")).catch(\n (error) => {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n return undefined;\n throw error;\n },\n );\n invariant(\n !info || (info.isDirectory() && !info.isSymbolicLink()),\n \"Unsafe workspace registry gate\",\n );\n return info ? { device: info.dev, inode: info.ino } : undefined;\n}\n\nexport async function recoverWorkspaceRegistryGate(\n expected: WorkspacePathGate,\n options: WorkspacePathRecoveryOptions,\n): Promise<void> {\n invariant(\n options.processesStopped === true,\n \"Registry recovery requires explicit stopped-process authorization\",\n );\n const current = await inspectWorkspaceRegistryGate();\n invariant(\n current?.device === expected.device && current.inode === expected.inode,\n \"Workspace registry gate changed\",\n );\n await rmdir(join(workspaceLocksDirectory, \"gate\"));\n}\n"]}
@@ -11,9 +11,14 @@ import { cacheMounts, validateCacheDeclarations } from "./container-cache.js";
11
11
  import { cloudCacheDefaults as defaults } from "./cloud-cache.constants.js";
12
12
  export function validateCloudCaches(caches) {
13
13
  validateCacheDeclarations(caches);
14
- for (const cache of caches)
14
+ for (const cache of caches) {
15
+ invariant(cache.timeoutMs === undefined ||
16
+ (Number.isSafeInteger(cache.timeoutMs) &&
17
+ cache.timeoutMs > 0 &&
18
+ cache.timeoutMs <= 2 ** 31 - 1), "Cloud cache timeoutMs must be a positive supported timer duration");
15
19
  invariant(cache.transport &&
16
20
  ["read", "write", "remove", "list"].every((method) => typeof Reflect.get(cache.transport, method) === "function"), "Cloud dependency caches require a Transport");
21
+ }
17
22
  }
18
23
  function cancellableTransport(transport, signal) {
19
24
  return {
@@ -33,12 +38,10 @@ function cancellableTransport(transport, signal) {
33
38
  export async function prepareCloudCaches(lease, caches, context, image) {
34
39
  if (!caches.length)
35
40
  return async () => { };
41
+ const timeoutMs = Math.max(...caches.map((cache) => cache.timeoutMs ?? defaults.deadlineMs));
36
42
  const signal = context.signal
37
- ? AbortSignal.any([
38
- context.signal,
39
- AbortSignal.timeout(defaults.deadlineMs),
40
- ])
41
- : AbortSignal.timeout(defaults.deadlineMs);
43
+ ? AbortSignal.any([context.signal, AbortSignal.timeout(timeoutMs)])
44
+ : AbortSignal.timeout(timeoutMs);
42
45
  const call = async (script, elevated = false) => {
43
46
  const result = await lease.invoke({
44
47
  executable: "sh",
@@ -76,7 +79,9 @@ export async function prepareCloudCaches(lease, caches, context, image) {
76
79
  const staging = await mkdtemp(join(tmpdir(), "outpost-cache-"));
77
80
  try {
78
81
  const restored = join(staging, "restored");
79
- await restoreArchiveFiles(transport, reference, restored);
82
+ await restoreArchiveFiles(transport, reference, restored, undefined, {
83
+ allowGitMetadata: true,
84
+ });
80
85
  await lease.upload(restored, mount.target, { signal });
81
86
  }
82
87
  finally {
@@ -85,7 +90,7 @@ export async function prepareCloudCaches(lease, caches, context, image) {
85
90
  }
86
91
  saves.push(async () => {
87
92
  // Cleanup has its own deadline so an aborted run can still publish downloads.
88
- const signal = AbortSignal.timeout(defaults.deadlineMs);
93
+ const signal = AbortSignal.timeout(cache.timeoutMs ?? defaults.deadlineMs);
89
94
  const transport = cancellableTransport(cache.transport, signal);
90
95
  const staging = await mkdtemp(join(tmpdir(), "outpost-cache-"));
91
96
  try {
@@ -99,7 +104,7 @@ export async function prepareCloudCaches(lease, caches, context, image) {
99
104
  const paths = entries
100
105
  .filter((entry) => !entry.isDirectory())
101
106
  .map((entry) => relative(downloaded, join(entry.parentPath, entry.name)).replaceAll("\\", "/"));
102
- const reference = await archiveFiles(transport, downloaded, paths, `${prefix}/snapshots/${randomUUID()}`);
107
+ const reference = await archiveFiles(transport, downloaded, paths, `${prefix}/snapshots/${randomUUID()}`, undefined, { allowGitMetadata: true });
103
108
  try {
104
109
  await transport.write(key, jsonBytes(reference), {
105
110
  ifRevision: current?.revision ?? null,
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-cache.js","sourceRoot":"","sources":["../../src/providers/cloud-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { lstat, mkdtemp, readdir, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative } from \"node:path\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport { TransportConflict } from \"../domain/transport.ts\";\nimport type { Transport } from \"../domain/transport.types.ts\";\nimport type { SandboxContext, SandboxLease } from \"../domain/sandbox.types.ts\";\nimport {\n archiveFiles,\n restoreArchiveFiles,\n} from \"../infrastructure/transport-archive.ts\";\nimport {\n jsonBytes,\n jsonObject,\n transportReference,\n} from \"../infrastructure/transport-json.ts\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport { cacheMounts, validateCacheDeclarations } from \"./container-cache.ts\";\nimport { cloudCacheDefaults as defaults } from \"./cloud-cache.constants.ts\";\nimport type { CloudDependencyCache } from \"./cloud-cache.types.ts\";\n\nexport function validateCloudCaches(\n caches: readonly CloudDependencyCache[],\n): void {\n validateCacheDeclarations(caches);\n for (const cache of caches)\n invariant(\n cache.transport &&\n [\"read\", \"write\", \"remove\", \"list\"].every(\n (method) =>\n typeof Reflect.get(cache.transport, method) === \"function\",\n ),\n \"Cloud dependency caches require a Transport\",\n );\n}\n\nfunction cancellableTransport(\n transport: Transport,\n signal: AbortSignal,\n): Transport {\n return {\n name: transport.name,\n read: (key, options) => {\n signal.throwIfAborted();\n return transport.read(key, { ...options, signal });\n },\n write: (key, bytes, options) => {\n signal.throwIfAborted();\n return transport.write(key, bytes, { ...options, signal });\n },\n remove: (key, options) => transport.remove(key, { ...options, signal }),\n list: (prefix, options) => transport.list(prefix, { ...options, signal }),\n };\n}\n\nexport async function prepareCloudCaches(\n lease: SandboxLease,\n caches: readonly CloudDependencyCache[],\n context: SandboxContext,\n image: string,\n): Promise<() => Promise<void>> {\n if (!caches.length) return async () => {};\n const signal = context.signal\n ? AbortSignal.any([\n context.signal,\n AbortSignal.timeout(defaults.deadlineMs),\n ])\n : AbortSignal.timeout(defaults.deadlineMs);\n const call = async (script: string, elevated = false) => {\n const result = await lease.invoke({\n executable: \"sh\",\n arguments: [\"-c\", script],\n elevated,\n signal,\n retain: defaults.userBytes,\n });\n if (result.status !== 0)\n throw new OutpostError(\n \"provider\",\n \"Cloud dependency cache setup failed\",\n {},\n result.stderr,\n );\n return result.stdout;\n };\n const ids = (await call('printf \"%s:%s\" \"$(id -u)\" \"$(id -g)\"')).trim();\n invariant(\n /^\\d+:\\d+$/.test(ids),\n \"Cloud sandbox returned an invalid cache user\",\n );\n const [uid, gid] = ids.split(\":\").map(Number);\n const mounts = await cacheMounts(\n caches,\n context.repository,\n image,\n {\n uid: uid!,\n gid: gid!,\n },\n context.workspaceIdentity,\n );\n const saves: (() => Promise<void>)[] = [];\n for (const [index, cache] of caches.entries()) {\n signal.throwIfAborted();\n const mount = mounts[index]!;\n const prefix = `${defaults.prefix}/${mount.volume}`;\n const key = `${prefix}/current`;\n const transport = cancellableTransport(cache.transport, signal);\n const current = await transport.read(key, {\n maxBytes: defaults.pointerBytes,\n });\n await call(\n `mkdir -p ${quote(mount.target)} && chown ${ids} ${quote(mount.target)} && chmod 700 ${quote(mount.target)}`,\n uid !== 0,\n );\n if (current) {\n const reference = transportReference(jsonObject(current));\n invariant(\n reference.key.startsWith(`${prefix}/snapshots/`) &&\n reference.key.endsWith(\"/manifest\"),\n \"Invalid dependency cache archive reference\",\n );\n const staging = await mkdtemp(join(tmpdir(), \"outpost-cache-\"));\n try {\n const restored = join(staging, \"restored\");\n await restoreArchiveFiles(transport, reference, restored);\n await lease.upload(restored, mount.target, { signal });\n } finally {\n await rm(staging, { recursive: true, force: true });\n }\n }\n saves.push(async () => {\n // Cleanup has its own deadline so an aborted run can still publish downloads.\n const signal = AbortSignal.timeout(defaults.deadlineMs);\n const transport = cancellableTransport(cache.transport, signal);\n const staging = await mkdtemp(join(tmpdir(), \"outpost-cache-\"));\n try {\n const downloaded = join(staging, \"downloaded\");\n await lease.download(mount.target, downloaded, { signal });\n invariant(\n (await lstat(downloaded)).isDirectory(),\n \"Dependency cache must remain a directory\",\n );\n const entries = await readdir(downloaded, {\n recursive: true,\n withFileTypes: true,\n });\n const paths = entries\n .filter((entry) => !entry.isDirectory())\n .map((entry) =>\n relative(downloaded, join(entry.parentPath, entry.name)).replaceAll(\n \"\\\\\",\n \"/\",\n ),\n );\n const reference = await archiveFiles(\n transport,\n downloaded,\n paths,\n `${prefix}/snapshots/${randomUUID()}`,\n );\n try {\n await transport.write(key, jsonBytes(reference), {\n ifRevision: current?.revision ?? null,\n });\n } catch (cause) {\n if (!(cause instanceof TransportConflict)) throw cause;\n }\n } finally {\n await rm(staging, { recursive: true, force: true });\n }\n });\n }\n return async () => {\n const failures: unknown[] = [];\n for (const save of saves) {\n try {\n await save();\n } catch (cause) {\n failures.push(cause);\n }\n }\n if (failures.length)\n throw new AggregateError(failures, \"Cloud dependency cache save failed\");\n };\n}\n\nexport async function releaseCloudSandbox(\n save: () => Promise<void>,\n stop: () => Promise<void>,\n): Promise<void> {\n const failures: unknown[] = [];\n try {\n await save();\n } catch (cause) {\n failures.push(cause);\n }\n try {\n await stop();\n } catch (cause) {\n failures.push(cause);\n }\n if (failures.length === 1) throw failures[0];\n if (failures.length)\n throw new AggregateError(\n failures,\n \"Cloud cache save or sandbox cleanup failed\",\n );\n}\n"]}
1
+ {"version":3,"file":"cloud-cache.js","sourceRoot":"","sources":["../../src/providers/cloud-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { lstat, mkdtemp, readdir, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative } from \"node:path\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport { TransportConflict } from \"../domain/transport.ts\";\nimport type { Transport } from \"../domain/transport.types.ts\";\nimport type { SandboxContext, SandboxLease } from \"../domain/sandbox.types.ts\";\nimport {\n archiveFiles,\n restoreArchiveFiles,\n} from \"../infrastructure/transport-archive.ts\";\nimport {\n jsonBytes,\n jsonObject,\n transportReference,\n} from \"../infrastructure/transport-json.ts\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport { cacheMounts, validateCacheDeclarations } from \"./container-cache.ts\";\nimport { cloudCacheDefaults as defaults } from \"./cloud-cache.constants.ts\";\nimport type { CloudDependencyCache } from \"./cloud-cache.types.ts\";\n\nexport function validateCloudCaches(\n caches: readonly CloudDependencyCache[],\n): void {\n validateCacheDeclarations(caches);\n for (const cache of caches) {\n invariant(\n cache.timeoutMs === undefined ||\n (Number.isSafeInteger(cache.timeoutMs) &&\n cache.timeoutMs > 0 &&\n cache.timeoutMs <= 2 ** 31 - 1),\n \"Cloud cache timeoutMs must be a positive supported timer duration\",\n );\n invariant(\n cache.transport &&\n [\"read\", \"write\", \"remove\", \"list\"].every(\n (method) =>\n typeof Reflect.get(cache.transport, method) === \"function\",\n ),\n \"Cloud dependency caches require a Transport\",\n );\n }\n}\n\nfunction cancellableTransport(\n transport: Transport,\n signal: AbortSignal,\n): Transport {\n return {\n name: transport.name,\n read: (key, options) => {\n signal.throwIfAborted();\n return transport.read(key, { ...options, signal });\n },\n write: (key, bytes, options) => {\n signal.throwIfAborted();\n return transport.write(key, bytes, { ...options, signal });\n },\n remove: (key, options) => transport.remove(key, { ...options, signal }),\n list: (prefix, options) => transport.list(prefix, { ...options, signal }),\n };\n}\n\nexport async function prepareCloudCaches(\n lease: SandboxLease,\n caches: readonly CloudDependencyCache[],\n context: SandboxContext,\n image: string,\n): Promise<() => Promise<void>> {\n if (!caches.length) return async () => {};\n const timeoutMs = Math.max(\n ...caches.map((cache) => cache.timeoutMs ?? defaults.deadlineMs),\n );\n const signal = context.signal\n ? AbortSignal.any([context.signal, AbortSignal.timeout(timeoutMs)])\n : AbortSignal.timeout(timeoutMs);\n const call = async (script: string, elevated = false) => {\n const result = await lease.invoke({\n executable: \"sh\",\n arguments: [\"-c\", script],\n elevated,\n signal,\n retain: defaults.userBytes,\n });\n if (result.status !== 0)\n throw new OutpostError(\n \"provider\",\n \"Cloud dependency cache setup failed\",\n {},\n result.stderr,\n );\n return result.stdout;\n };\n const ids = (await call('printf \"%s:%s\" \"$(id -u)\" \"$(id -g)\"')).trim();\n invariant(\n /^\\d+:\\d+$/.test(ids),\n \"Cloud sandbox returned an invalid cache user\",\n );\n const [uid, gid] = ids.split(\":\").map(Number);\n const mounts = await cacheMounts(\n caches,\n context.repository,\n image,\n {\n uid: uid!,\n gid: gid!,\n },\n context.workspaceIdentity,\n );\n const saves: (() => Promise<void>)[] = [];\n for (const [index, cache] of caches.entries()) {\n signal.throwIfAborted();\n const mount = mounts[index]!;\n const prefix = `${defaults.prefix}/${mount.volume}`;\n const key = `${prefix}/current`;\n const transport = cancellableTransport(cache.transport, signal);\n const current = await transport.read(key, {\n maxBytes: defaults.pointerBytes,\n });\n await call(\n `mkdir -p ${quote(mount.target)} && chown ${ids} ${quote(mount.target)} && chmod 700 ${quote(mount.target)}`,\n uid !== 0,\n );\n if (current) {\n const reference = transportReference(jsonObject(current));\n invariant(\n reference.key.startsWith(`${prefix}/snapshots/`) &&\n reference.key.endsWith(\"/manifest\"),\n \"Invalid dependency cache archive reference\",\n );\n const staging = await mkdtemp(join(tmpdir(), \"outpost-cache-\"));\n try {\n const restored = join(staging, \"restored\");\n await restoreArchiveFiles(transport, reference, restored, undefined, {\n allowGitMetadata: true,\n });\n await lease.upload(restored, mount.target, { signal });\n } finally {\n await rm(staging, { recursive: true, force: true });\n }\n }\n saves.push(async () => {\n // Cleanup has its own deadline so an aborted run can still publish downloads.\n const signal = AbortSignal.timeout(\n cache.timeoutMs ?? defaults.deadlineMs,\n );\n const transport = cancellableTransport(cache.transport, signal);\n const staging = await mkdtemp(join(tmpdir(), \"outpost-cache-\"));\n try {\n const downloaded = join(staging, \"downloaded\");\n await lease.download(mount.target, downloaded, { signal });\n invariant(\n (await lstat(downloaded)).isDirectory(),\n \"Dependency cache must remain a directory\",\n );\n const entries = await readdir(downloaded, {\n recursive: true,\n withFileTypes: true,\n });\n const paths = entries\n .filter((entry) => !entry.isDirectory())\n .map((entry) =>\n relative(downloaded, join(entry.parentPath, entry.name)).replaceAll(\n \"\\\\\",\n \"/\",\n ),\n );\n const reference = await archiveFiles(\n transport,\n downloaded,\n paths,\n `${prefix}/snapshots/${randomUUID()}`,\n undefined,\n { allowGitMetadata: true },\n );\n try {\n await transport.write(key, jsonBytes(reference), {\n ifRevision: current?.revision ?? null,\n });\n } catch (cause) {\n if (!(cause instanceof TransportConflict)) throw cause;\n }\n } finally {\n await rm(staging, { recursive: true, force: true });\n }\n });\n }\n return async () => {\n const failures: unknown[] = [];\n for (const save of saves) {\n try {\n await save();\n } catch (cause) {\n failures.push(cause);\n }\n }\n if (failures.length)\n throw new AggregateError(failures, \"Cloud dependency cache save failed\");\n };\n}\n\nexport async function releaseCloudSandbox(\n save: () => Promise<void>,\n stop: () => Promise<void>,\n): Promise<void> {\n const failures: unknown[] = [];\n try {\n await save();\n } catch (cause) {\n failures.push(cause);\n }\n try {\n await stop();\n } catch (cause) {\n failures.push(cause);\n }\n if (failures.length === 1) throw failures[0];\n if (failures.length)\n throw new AggregateError(\n failures,\n \"Cloud cache save or sandbox cleanup failed\",\n );\n}\n"]}
@@ -2,4 +2,5 @@ import type { Transport } from "../domain/transport.types.ts";
2
2
  import type { DependencyCache } from "./container-cache.types.ts";
3
3
  export interface CloudDependencyCache extends DependencyCache {
4
4
  readonly transport: Transport;
5
+ readonly timeoutMs?: number;
5
6
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-cache.types.js","sourceRoot":"","sources":["../../src/providers/cloud-cache.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Transport } from \"../domain/transport.types.ts\";\nimport type { DependencyCache } from \"./container-cache.types.ts\";\n\nexport interface CloudDependencyCache extends DependencyCache {\n readonly transport: Transport;\n}\n"]}
1
+ {"version":3,"file":"cloud-cache.types.js","sourceRoot":"","sources":["../../src/providers/cloud-cache.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Transport } from \"../domain/transport.types.ts\";\nimport type { DependencyCache } from \"./container-cache.types.ts\";\n\nexport interface CloudDependencyCache extends DependencyCache {\n readonly transport: Transport;\n readonly timeoutMs?: number;\n}\n"]}
@@ -1,13 +1,12 @@
1
- import { chmod, lstat, mkdir, readdir, readFile, symlink, writeFile, } from "node:fs/promises";
1
+ import { chmod, lstat, mkdir, readdir, readFile, readlink, symlink, writeFile, } from "node:fs/promises";
2
2
  import { dirname, join, posix } from "node:path";
3
3
  import { OutpostError } from "../domain/errors.js";
4
- import { safeDestination } from "../infrastructure/files.js";
4
+ import { fileDestination } from "../infrastructure/files.js";
5
5
  export { manifestScript } from "./cloud-files.constants.js";
6
6
  export async function uploadTree(source, destination, upload, link, metadata, signal) {
7
7
  signal?.throwIfAborted();
8
8
  const info = await lstat(source);
9
9
  if (info.isSymbolicLink()) {
10
- const { readlink } = await import("node:fs/promises");
11
10
  await link(await readlink(source), destination);
12
11
  return;
13
12
  }
@@ -42,7 +41,7 @@ export async function downloadTree(source, destination, manifest, read, signal)
42
41
  !Number.isSafeInteger(entry.mode))
43
42
  throw new OutpostError("provider", "Invalid transfer entry");
44
43
  const target = entry.path
45
- ? await safeDestination(destination, entry.path)
44
+ ? await fileDestination(destination, entry.path)
46
45
  : destination;
47
46
  if ((await lstat(target).catch(() => undefined))?.isSymbolicLink())
48
47
  throw new OutpostError("provider", "Refusing to overwrite a local symlink");
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-files.js","sourceRoot":"","sources":["../../src/providers/cloud-files.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,KAAK,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,OAAO,EACP,SAAS,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,WAAmB,EACnB,MAAqD,EACrD,IAAqD,EACrD,QAA4E,EAC5E,MAAoB;IAEpB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;QAC1B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACtD,MAAM,IAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;QAChD,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACvB,MAAM,QAAQ,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;QACvD,KAAK,MAAM,KAAK,IAAI,MAAM,OAAO,CAAC,MAAM,CAAC;YACvC,MAAM,UAAU,CACd,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,EAC9B,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,MAAM,CACP,CAAC;QACJ,OAAO;IACT,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAChB,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,6BAA6B,MAAM,EAAE,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,MAAM,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAChC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,MAAM,QAAQ,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,WAAmB,EACnB,IAAY;IAEZ,MAAM,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,cAAc,EAAE;QACrE,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,uCAAuC,CAAC,CAAC;IAC9E,MAAM,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,WAAmB,EACnB,QAAgB,EAChB,IAAuC,EACvC,MAAoB;IAEpB,MAAM,OAAO,GAAY,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QACzB,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,2BAA2B,CAAC,CAAC;IAClE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,IACE,CAAC,KAAK;YACN,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;YAC9B,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC;YAEjC,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI;YACvB,CAAC,CAAC,MAAM,eAAe,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC;YAChD,CAAC,CAAC,WAAW,CAAC;QAChB,IAAI,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,cAAc,EAAE;YAChE,MAAM,IAAI,YAAY,CACpB,UAAU,EACV,uCAAuC,CACxC,CAAC;QACJ,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAC/B,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACzC,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACxD,MAAM,EAAE,cAAc,EAAE,CAAC;YACzB,MAAM,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACjC,MAAM,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;YACxC,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC9D,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACpC,SAAS;QACX,CAAC;QACD,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,8BAA8B,CAAC,CAAC;IACrE,CAAC;AACH,CAAC","sourcesContent":["import {\n chmod,\n lstat,\n mkdir,\n readdir,\n readFile,\n symlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, posix } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport { safeDestination } from \"../infrastructure/files.ts\";\n\nexport { manifestScript } from \"./cloud-files.constants.ts\";\n\nexport async function uploadTree(\n source: string,\n destination: string,\n upload: (path: string, data: Buffer) => Promise<void>,\n link: (target: string, path: string) => Promise<void>,\n metadata?: (path: string, directory: boolean, mode: number) => Promise<void>,\n signal?: AbortSignal,\n): Promise<void> {\n signal?.throwIfAborted();\n const info = await lstat(source);\n if (info.isSymbolicLink()) {\n const { readlink } = await import(\"node:fs/promises\");\n await link(await readlink(source), destination);\n return;\n }\n if (info.isDirectory()) {\n await metadata?.(destination, true, info.mode & 0o777);\n for (const entry of await readdir(source))\n await uploadTree(\n join(source, entry),\n posix.join(destination, entry),\n upload,\n link,\n metadata,\n signal,\n );\n return;\n }\n if (!info.isFile())\n throw new OutpostError(\"provider\", `Unsupported upload input: ${source}`);\n const data = await readFile(source);\n signal?.throwIfAborted();\n await upload(destination, data);\n signal?.throwIfAborted();\n await metadata?.(destination, false, info.mode & 0o777);\n}\n\nexport async function saveDownload(\n destination: string,\n data: Buffer,\n): Promise<void> {\n await mkdir(dirname(destination), { recursive: true });\n if ((await lstat(destination).catch(() => undefined))?.isSymbolicLink())\n throw new OutpostError(\"provider\", \"Refusing to overwrite a local symlink\");\n await writeFile(destination, data);\n}\n\nexport async function downloadTree(\n source: string,\n destination: string,\n manifest: string,\n read: (path: string) => Promise<Buffer>,\n signal?: AbortSignal,\n): Promise<void> {\n const entries: unknown = JSON.parse(manifest);\n if (!Array.isArray(entries))\n throw new OutpostError(\"provider\", \"Invalid transfer manifest\");\n for (const entry of entries) {\n signal?.throwIfAborted();\n if (\n !entry ||\n typeof entry.path !== \"string\" ||\n !Number.isSafeInteger(entry.mode)\n )\n throw new OutpostError(\"provider\", \"Invalid transfer entry\");\n const target = entry.path\n ? await safeDestination(destination, entry.path)\n : destination;\n if ((await lstat(target).catch(() => undefined))?.isSymbolicLink())\n throw new OutpostError(\n \"provider\",\n \"Refusing to overwrite a local symlink\",\n );\n if (entry.kind === \"directory\") {\n await mkdir(target, { recursive: true });\n continue;\n }\n if (entry.kind === \"file\") {\n const data = await read(posix.join(source, entry.path));\n signal?.throwIfAborted();\n await saveDownload(target, data);\n await chmod(target, entry.mode & 0o777);\n continue;\n }\n if (entry.kind === \"link\" && typeof entry.target === \"string\") {\n await mkdir(dirname(target), { recursive: true });\n await symlink(entry.target, target);\n continue;\n }\n throw new OutpostError(\"provider\", \"Unsupported remote file type\");\n }\n}\n"]}
1
+ {"version":3,"file":"cloud-files.js","sourceRoot":"","sources":["../../src/providers/cloud-files.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n chmod,\n lstat,\n mkdir,\n readdir,\n readFile,\n readlink,\n symlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, posix } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport { fileDestination } from \"../infrastructure/files.ts\";\n\nexport { manifestScript } from \"./cloud-files.constants.ts\";\n\nexport async function uploadTree(\n source: string,\n destination: string,\n upload: (path: string, data: Buffer) => Promise<void>,\n link: (target: string, path: string) => Promise<void>,\n metadata?: (path: string, directory: boolean, mode: number) => Promise<void>,\n signal?: AbortSignal,\n): Promise<void> {\n signal?.throwIfAborted();\n const info = await lstat(source);\n if (info.isSymbolicLink()) {\n await link(await readlink(source), destination);\n return;\n }\n if (info.isDirectory()) {\n await metadata?.(destination, true, info.mode & 0o777);\n for (const entry of await readdir(source))\n await uploadTree(\n join(source, entry),\n posix.join(destination, entry),\n upload,\n link,\n metadata,\n signal,\n );\n return;\n }\n if (!info.isFile())\n throw new OutpostError(\"provider\", `Unsupported upload input: ${source}`);\n const data = await readFile(source);\n signal?.throwIfAborted();\n await upload(destination, data);\n signal?.throwIfAborted();\n await metadata?.(destination, false, info.mode & 0o777);\n}\n\nexport async function saveDownload(\n destination: string,\n data: Buffer,\n): Promise<void> {\n await mkdir(dirname(destination), { recursive: true });\n if ((await lstat(destination).catch(() => undefined))?.isSymbolicLink())\n throw new OutpostError(\"provider\", \"Refusing to overwrite a local symlink\");\n await writeFile(destination, data);\n}\n\nexport async function downloadTree(\n source: string,\n destination: string,\n manifest: string,\n read: (path: string) => Promise<Buffer>,\n signal?: AbortSignal,\n): Promise<void> {\n const entries: unknown = JSON.parse(manifest);\n if (!Array.isArray(entries))\n throw new OutpostError(\"provider\", \"Invalid transfer manifest\");\n for (const entry of entries) {\n signal?.throwIfAborted();\n if (\n !entry ||\n typeof entry.path !== \"string\" ||\n !Number.isSafeInteger(entry.mode)\n )\n throw new OutpostError(\"provider\", \"Invalid transfer entry\");\n const target = entry.path\n ? await fileDestination(destination, entry.path)\n : destination;\n if ((await lstat(target).catch(() => undefined))?.isSymbolicLink())\n throw new OutpostError(\n \"provider\",\n \"Refusing to overwrite a local symlink\",\n );\n if (entry.kind === \"directory\") {\n await mkdir(target, { recursive: true });\n continue;\n }\n if (entry.kind === \"file\") {\n const data = await read(posix.join(source, entry.path));\n signal?.throwIfAborted();\n await saveDownload(target, data);\n await chmod(target, entry.mode & 0o777);\n continue;\n }\n if (entry.kind === \"link\" && typeof entry.target === \"string\") {\n await mkdir(dirname(target), { recursive: true });\n await symlink(entry.target, target);\n continue;\n }\n throw new OutpostError(\"provider\", \"Unsupported remote file type\");\n }\n}\n"]}
@@ -1,2 +1,2 @@
1
- /** Runs argv[2..] with stdin fed from base64 lines appended to argv[1]; an empty line ends stdin. */
2
- export declare const cloudInputScript = "const fs=require(\"node:fs\");\nconst {spawn}=require(\"node:child_process\");\nconst {constants}=require(\"node:os\");\nconst [file,program,...args]=process.argv.slice(1);\nconst child=spawn(program,args,{stdio:[\"pipe\",\"inherit\",\"inherit\"]});\nlet offset=0,pending=\"\",ended=false;\nchild.stdin.on(\"error\",()=>{});\nconst poll=()=>{\nif(ended)return;\nconst data=fs.readFileSync(file);\nif(data.length>offset){pending+=data.subarray(offset).toString(\"utf8\");offset=data.length}\nlet end;\nwhile((end=pending.indexOf(\"\\n\"))>=0){\nconst line=pending.slice(0,end);pending=pending.slice(end+1);\nif(!line){ended=true;child.stdin.end();return}\nchild.stdin.write(Buffer.from(line,\"base64\"));\n}\nsetTimeout(poll,50);\n};\npoll();\nchild.on(\"error\",error=>{process.stderr.write(error.message+\"\\n\");process.exit(error.code===\"ENOENT\"?127:126)});\nchild.on(\"close\",(code,signal)=>process.exit(code??(signal?128+(constants.signals[signal]??0):127)));";
1
+ /** Runs argv[2..] with stdin fed from base64 lines appended to argv[1]; an empty line or closed marker ends stdin. */
2
+ export declare const cloudInputScript = "const fs=require(\"node:fs\");\nconst {spawn}=require(\"node:child_process\");\nconst {constants}=require(\"node:os\");\nconst [file,program,...args]=process.argv.slice(1);\nconst child=spawn(program,args,{stdio:[\"pipe\",\"inherit\",\"inherit\"]});\nlet offset=0,pending=\"\",ended=false;\nchild.stdin.on(\"error\",()=>{});\nconst poll=()=>{\nif(ended)return;\nconst data=fs.readFileSync(file);\nif(data.length>offset){pending+=data.subarray(offset).toString(\"utf8\");offset=data.length}\nlet end;\nwhile((end=pending.indexOf(\"\\n\"))>=0){\nconst line=pending.slice(0,end);pending=pending.slice(end+1);\nif(!line){ended=true;child.stdin.end();return}\nchild.stdin.write(Buffer.from(line,\"base64\"));\n}\nif(fs.existsSync(file+\".closed\")){ended=true;child.stdin.end();return}\nsetTimeout(poll,50);\n};\npoll();\nchild.on(\"error\",error=>{process.stderr.write(error.message+\"\\n\");process.exit(error.code===\"ENOENT\"?127:126)});\nchild.on(\"close\",(code,signal)=>process.exit(code??(signal?128+(constants.signals[signal]??0):127)));";
@@ -1,4 +1,4 @@
1
- /** Runs argv[2..] with stdin fed from base64 lines appended to argv[1]; an empty line ends stdin. */
1
+ /** Runs argv[2..] with stdin fed from base64 lines appended to argv[1]; an empty line or closed marker ends stdin. */
2
2
  export const cloudInputScript = `const fs=require("node:fs");
3
3
  const {spawn}=require("node:child_process");
4
4
  const {constants}=require("node:os");
@@ -16,6 +16,7 @@ const line=pending.slice(0,end);pending=pending.slice(end+1);
16
16
  if(!line){ended=true;child.stdin.end();return}
17
17
  child.stdin.write(Buffer.from(line,"base64"));
18
18
  }
19
+ if(fs.existsSync(file+".closed")){ended=true;child.stdin.end();return}
19
20
  setTimeout(poll,50);
20
21
  };
21
22
  poll();
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-input.constants.js","sourceRoot":"","sources":["../../src/providers/cloud-input.constants.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;sGAqBsE,CAAC","sourcesContent":["/** Runs argv[2..] with stdin fed from base64 lines appended to argv[1]; an empty line ends stdin. */\nexport const cloudInputScript = `const fs=require(\"node:fs\");\nconst {spawn}=require(\"node:child_process\");\nconst {constants}=require(\"node:os\");\nconst [file,program,...args]=process.argv.slice(1);\nconst child=spawn(program,args,{stdio:[\"pipe\",\"inherit\",\"inherit\"]});\nlet offset=0,pending=\"\",ended=false;\nchild.stdin.on(\"error\",()=>{});\nconst poll=()=>{\nif(ended)return;\nconst data=fs.readFileSync(file);\nif(data.length>offset){pending+=data.subarray(offset).toString(\"utf8\");offset=data.length}\nlet end;\nwhile((end=pending.indexOf(\"\\\\n\"))>=0){\nconst line=pending.slice(0,end);pending=pending.slice(end+1);\nif(!line){ended=true;child.stdin.end();return}\nchild.stdin.write(Buffer.from(line,\"base64\"));\n}\nsetTimeout(poll,50);\n};\npoll();\nchild.on(\"error\",error=>{process.stderr.write(error.message+\"\\\\n\");process.exit(error.code===\"ENOENT\"?127:126)});\nchild.on(\"close\",(code,signal)=>process.exit(code??(signal?128+(constants.signals[signal]??0):127)));`;\n"]}
1
+ {"version":3,"file":"cloud-input.constants.js","sourceRoot":"","sources":["../../src/providers/cloud-input.constants.ts"],"names":[],"mappings":"AAAA,sHAAsH;AACtH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;sGAsBsE,CAAC","sourcesContent":["/** Runs argv[2..] with stdin fed from base64 lines appended to argv[1]; an empty line or closed marker ends stdin. */\nexport const cloudInputScript = `const fs=require(\"node:fs\");\nconst {spawn}=require(\"node:child_process\");\nconst {constants}=require(\"node:os\");\nconst [file,program,...args]=process.argv.slice(1);\nconst child=spawn(program,args,{stdio:[\"pipe\",\"inherit\",\"inherit\"]});\nlet offset=0,pending=\"\",ended=false;\nchild.stdin.on(\"error\",()=>{});\nconst poll=()=>{\nif(ended)return;\nconst data=fs.readFileSync(file);\nif(data.length>offset){pending+=data.subarray(offset).toString(\"utf8\");offset=data.length}\nlet end;\nwhile((end=pending.indexOf(\"\\\\n\"))>=0){\nconst line=pending.slice(0,end);pending=pending.slice(end+1);\nif(!line){ended=true;child.stdin.end();return}\nchild.stdin.write(Buffer.from(line,\"base64\"));\n}\nif(fs.existsSync(file+\".closed\")){ended=true;child.stdin.end();return}\nsetTimeout(poll,50);\n};\npoll();\nchild.on(\"error\",error=>{process.stderr.write(error.message+\"\\\\n\");process.exit(error.code===\"ENOENT\"?127:126)});\nchild.on(\"close\",(code,signal)=>process.exit(code??(signal?128+(constants.signals[signal]??0):127)));`;\n"]}
@@ -4,5 +4,5 @@ import type { CloudInputFeed } from "./cloud-input.types.ts";
4
4
  export declare function cloudInputFrames(stdin: string | undefined): string;
5
5
  /** Shell words that run a quoted program with stdin fed from the input file. */
6
6
  export declare function cloudInputProgram(file: string, program: string): string;
7
- /** Appends each live chunk, then the end frame, in order; the first failure stops the feed. */
8
- export declare function cloudInputFeed(input: Readable, append: (line: string) => Promise<void>, fail: (cause: unknown) => void): CloudInputFeed;
7
+ /** Appends each live chunk, then the end frame, in order; the first failure stops data but still permits EOF. */
8
+ export declare function cloudInputFeed(input: Readable, append: (line: string) => Promise<void>, fail: (cause: unknown) => void, close?: () => Promise<void>): CloudInputFeed;
@@ -9,8 +9,8 @@ export function cloudInputFrames(stdin) {
9
9
  export function cloudInputProgram(file, program) {
10
10
  return `node -e ${quote(cloudInputScript)} -- ${quote(file)} ${program}`;
11
11
  }
12
- /** Appends each live chunk, then the end frame, in order; the first failure stops the feed. */
13
- export function cloudInputFeed(input, append, fail) {
12
+ /** Appends each live chunk, then the end frame, in order; the first failure stops data but still permits EOF. */
13
+ export function cloudInputFeed(input, append, fail, close = () => append("\n")) {
14
14
  let queue = Promise.resolve();
15
15
  let failed = false;
16
16
  const push = (line) => {
@@ -27,7 +27,18 @@ export function cloudInputFeed(input, append, fail) {
27
27
  });
28
28
  };
29
29
  const data = (chunk) => push(frame(chunk));
30
- const end = () => push("\n");
30
+ const end = () => {
31
+ queue = queue.then(async () => {
32
+ try {
33
+ await close();
34
+ }
35
+ catch (cause) {
36
+ if (!failed)
37
+ fail(cause);
38
+ failed = true;
39
+ }
40
+ });
41
+ };
31
42
  input.on("data", data);
32
43
  input.once("end", end);
33
44
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-input.js","sourceRoot":"","sources":["../../src/providers/cloud-input.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG9D,MAAM,KAAK,GAAG,CAAC,KAAsB,EAAE,EAAE,CACvC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;AAE/C,qEAAqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,KAAyB;IACxD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAe;IAC7D,OAAO,WAAW,KAAK,CAAC,gBAAgB,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC;AAC3E,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,cAAc,CAC5B,KAAe,EACf,MAAuC,EACvC,IAA8B;IAE9B,IAAI,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC9B,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE;QAC5B,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;YAC5B,IAAI,MAAM;gBAAE,OAAO;YACnB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,GAAG,IAAI,CAAC;gBACd,IAAI,CAAC,KAAK,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,CAAC,KAAsB,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5D,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvB,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACvB,OAAO;QACL,KAAK,CAAC,MAAM;YACV,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACxB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACtB,MAAM,GAAG,IAAI,CAAC;YACd,MAAM,KAAK,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { Readable } from \"node:stream\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport { cloudInputScript } from \"./cloud-input.constants.ts\";\nimport type { CloudInputFeed } from \"./cloud-input.types.ts\";\n\nconst frame = (chunk: string | Buffer) =>\n `${Buffer.from(chunk).toString(\"base64\")}\\n`;\n\n/** Initial content of the input file: framed stdin, when present. */\nexport function cloudInputFrames(stdin: string | undefined): string {\n return stdin ? frame(stdin) : \"\";\n}\n\n/** Shell words that run a quoted program with stdin fed from the input file. */\nexport function cloudInputProgram(file: string, program: string): string {\n return `node -e ${quote(cloudInputScript)} -- ${quote(file)} ${program}`;\n}\n\n/** Appends each live chunk, then the end frame, in order; the first failure stops the feed. */\nexport function cloudInputFeed(\n input: Readable,\n append: (line: string) => Promise<void>,\n fail: (cause: unknown) => void,\n): CloudInputFeed {\n let queue = Promise.resolve();\n let failed = false;\n const push = (line: string) => {\n queue = queue.then(async () => {\n if (failed) return;\n try {\n await append(line);\n } catch (cause) {\n failed = true;\n fail(cause);\n }\n });\n };\n const data = (chunk: string | Buffer) => push(frame(chunk));\n const end = () => push(\"\\n\");\n input.on(\"data\", data);\n input.once(\"end\", end);\n return {\n async finish() {\n input.off(\"data\", data);\n input.off(\"end\", end);\n failed = true;\n await queue;\n },\n };\n}\n"]}
1
+ {"version":3,"file":"cloud-input.js","sourceRoot":"","sources":["../../src/providers/cloud-input.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG9D,MAAM,KAAK,GAAG,CAAC,KAAsB,EAAE,EAAE,CACvC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;AAE/C,qEAAqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,KAAyB;IACxD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAe;IAC7D,OAAO,WAAW,KAAK,CAAC,gBAAgB,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC;AAC3E,CAAC;AAED,iHAAiH;AACjH,MAAM,UAAU,cAAc,CAC5B,KAAe,EACf,MAAuC,EACvC,IAA8B,EAC9B,QAA6B,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;IAE/C,IAAI,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC9B,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE;QAC5B,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;YAC5B,IAAI,MAAM;gBAAE,OAAO;YACnB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,GAAG,IAAI,CAAC;gBACd,IAAI,CAAC,KAAK,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,CAAC,KAAsB,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5D,MAAM,GAAG,GAAG,GAAG,EAAE;QACf,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;YAC5B,IAAI,CAAC;gBACH,MAAM,KAAK,EAAE,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM;oBAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBACzB,MAAM,GAAG,IAAI,CAAC;YAChB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvB,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACvB,OAAO;QACL,KAAK,CAAC,MAAM;YACV,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACxB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACtB,MAAM,GAAG,IAAI,CAAC;YACd,MAAM,KAAK,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { Readable } from \"node:stream\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport { cloudInputScript } from \"./cloud-input.constants.ts\";\nimport type { CloudInputFeed } from \"./cloud-input.types.ts\";\n\nconst frame = (chunk: string | Buffer) =>\n `${Buffer.from(chunk).toString(\"base64\")}\\n`;\n\n/** Initial content of the input file: framed stdin, when present. */\nexport function cloudInputFrames(stdin: string | undefined): string {\n return stdin ? frame(stdin) : \"\";\n}\n\n/** Shell words that run a quoted program with stdin fed from the input file. */\nexport function cloudInputProgram(file: string, program: string): string {\n return `node -e ${quote(cloudInputScript)} -- ${quote(file)} ${program}`;\n}\n\n/** Appends each live chunk, then the end frame, in order; the first failure stops data but still permits EOF. */\nexport function cloudInputFeed(\n input: Readable,\n append: (line: string) => Promise<void>,\n fail: (cause: unknown) => void,\n close: () => Promise<void> = () => append(\"\\n\"),\n): CloudInputFeed {\n let queue = Promise.resolve();\n let failed = false;\n const push = (line: string) => {\n queue = queue.then(async () => {\n if (failed) return;\n try {\n await append(line);\n } catch (cause) {\n failed = true;\n fail(cause);\n }\n });\n };\n const data = (chunk: string | Buffer) => push(frame(chunk));\n const end = () => {\n queue = queue.then(async () => {\n try {\n await close();\n } catch (cause) {\n if (!failed) fail(cause);\n failed = true;\n }\n });\n };\n input.on(\"data\", data);\n input.once(\"end\", end);\n return {\n async finish() {\n input.off(\"data\", data);\n input.off(\"end\", end);\n failed = true;\n await queue;\n },\n };\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { registerCleanup } from "../infrastructure/shutdown.js";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import { invariant, OutpostError } from "../domain/errors.js";
3
4
  import { quote } from "../infrastructure/process.js";
@@ -30,10 +31,22 @@ export function containerCommand(runtime) {
30
31
  ];
31
32
  const wrapper = `${exports} echo $$ > ${quote(pidFile)}; test ! -f ${quote(pidFile + ".cancel")} || exit 130; exec "$@"`;
32
33
  const { directory: _directory, ...invocation } = command;
33
- let interrupted = false;
34
+ const shutdown = new AbortController();
35
+ const signal = command.signal
36
+ ? AbortSignal.any([command.signal, shutdown.signal])
37
+ : shutdown.signal;
38
+ let settled;
39
+ const completion = new Promise((resolve) => {
40
+ settled = resolve;
41
+ });
42
+ const unregister = registerCleanup(async () => {
43
+ shutdown.abort(new OutpostError("aborted", "Container command interrupted"));
44
+ await completion;
45
+ });
34
46
  try {
35
47
  return await executor({
36
48
  ...invocation,
49
+ signal,
37
50
  executable: engine,
38
51
  arguments: [
39
52
  ...flags,
@@ -52,7 +65,6 @@ export function containerCommand(runtime) {
52
65
  });
53
66
  }
54
67
  catch (cause) {
55
- interrupted = true;
56
68
  try {
57
69
  await call([
58
70
  "exec",
@@ -60,7 +72,7 @@ export function containerCommand(runtime) {
60
72
  name,
61
73
  "sh",
62
74
  "-c",
63
- `touch ${quote(pidFile + ".cancel")}; if [ -f ${quote(pidFile)} ]; then p=$(cat ${quote(pidFile)}); kill -TERM -"$p" 2>/dev/null || true; sleep 0.1; kill -KILL -"$p" 2>/dev/null || true; fi`,
75
+ `touch ${quote(pidFile + ".cancel")}; if [ -f ${quote(pidFile)} ]; then p=$(cat ${quote(pidFile)}); kill -TERM -"$p" 2>/dev/null || true; sleep 0.5; kill -KILL -"$p" 2>/dev/null || true; fi`,
64
76
  ]);
65
77
  }
66
78
  catch (cleanup) {
@@ -69,8 +81,21 @@ export function containerCommand(runtime) {
69
81
  throw cause;
70
82
  }
71
83
  finally {
72
- if (!isClosed() && !interrupted)
73
- await call(["exec", name, "rm", "-f", pidFile]).catch(() => undefined);
84
+ try {
85
+ if (!isClosed())
86
+ await call([
87
+ "exec",
88
+ name,
89
+ "rm",
90
+ "-f",
91
+ pidFile,
92
+ pidFile + ".cancel",
93
+ ]).catch(() => undefined);
94
+ }
95
+ finally {
96
+ unregister();
97
+ settled();
98
+ }
74
99
  }
75
100
  };
76
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"container-command.js","sourceRoot":"","sources":["../../src/providers/container-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport { containerDefaults } from \"./container.constants.ts\";\nimport type { ContainerRuntime } from \"./container.types.ts\";\n\nexport function containerCommand(\n runtime: ContainerRuntime,\n): SandboxLease[\"invoke\"] {\n const { engine, config, executor, root, name, env, call, isClosed } = runtime;\n return async (command) => {\n if (isClosed())\n throw new OutpostError(\"provider\", \"Container sandbox is closed\");\n command.signal?.throwIfAborted();\n const id = randomUUID();\n const pidFile = `/tmp/outpost-${id}.pid`;\n const variables = { ...env, ...command.variables };\n invariant(\n Object.keys(variables).every((key) =>\n /^[A-Za-z_][A-Za-z0-9_]*$/.test(key),\n ),\n \"Invalid environment key\",\n );\n const environment = Object.fromEntries(\n Object.entries(variables).map(([, value], index) => [\n `OUTPOST_VALUE_${index}`,\n value,\n ]),\n );\n const exports = Object.keys(variables)\n .map(\n (key, index) =>\n `export ${key}=\"$OUTPOST_VALUE_${index}\"; unset OUTPOST_VALUE_${index};`,\n )\n .join(\" \");\n const flags = [\n \"exec\",\n command.interactive && process.stdin.isTTY ? \"-it\" : \"-i\",\n ...(command.elevated ? [\"--user\", \"0:0\"] : []),\n \"--workdir\",\n command.directory ?? root,\n ...Object.keys(environment).flatMap((key) => [\"--env\", key]),\n name,\n ];\n const wrapper = `${exports} echo $$ > ${quote(pidFile)}; test ! -f ${quote(pidFile + \".cancel\")} || exit 130; exec \"$@\"`;\n const { directory: _directory, ...invocation } = command;\n let interrupted = false;\n try {\n return await executor({\n ...invocation,\n executable: engine,\n arguments: [\n ...flags,\n ...(command.interactive && process.stdin.isTTY\n ? []\n : [\"setsid\", \"--wait\"]),\n \"sh\",\n \"-c\",\n wrapper,\n \"outpost\",\n command.executable,\n ...(command.arguments ?? []),\n ],\n variables: environment,\n retain:\n command.retain ?? config.retain ?? containerDefaults.retainBytes,\n });\n } catch (cause) {\n interrupted = true;\n try {\n await call([\n \"exec\",\n ...(command.elevated ? [\"--user\", \"0:0\"] : []),\n name,\n \"sh\",\n \"-c\",\n `touch ${quote(pidFile + \".cancel\")}; if [ -f ${quote(pidFile)} ]; then p=$(cat ${quote(pidFile)}); kill -TERM -\"$p\" 2>/dev/null || true; sleep 0.1; kill -KILL -\"$p\" 2>/dev/null || true; fi`,\n ]);\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Command cancellation could not be confirmed\",\n );\n }\n throw cause;\n } finally {\n if (!isClosed() && !interrupted)\n await call([\"exec\", name, \"rm\", \"-f\", pidFile]).catch(() => undefined);\n }\n };\n}\n"]}
1
+ {"version":3,"file":"container-command.js","sourceRoot":"","sources":["../../src/providers/container-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerCleanup } from \"../infrastructure/shutdown.ts\";\nimport { randomUUID } from \"node:crypto\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport { containerDefaults } from \"./container.constants.ts\";\nimport type { ContainerRuntime } from \"./container.types.ts\";\n\nexport function containerCommand(\n runtime: ContainerRuntime,\n): SandboxLease[\"invoke\"] {\n const { engine, config, executor, root, name, env, call, isClosed } = runtime;\n return async (command) => {\n if (isClosed())\n throw new OutpostError(\"provider\", \"Container sandbox is closed\");\n command.signal?.throwIfAborted();\n const id = randomUUID();\n const pidFile = `/tmp/outpost-${id}.pid`;\n const variables = { ...env, ...command.variables };\n invariant(\n Object.keys(variables).every((key) =>\n /^[A-Za-z_][A-Za-z0-9_]*$/.test(key),\n ),\n \"Invalid environment key\",\n );\n const environment = Object.fromEntries(\n Object.entries(variables).map(([, value], index) => [\n `OUTPOST_VALUE_${index}`,\n value,\n ]),\n );\n const exports = Object.keys(variables)\n .map(\n (key, index) =>\n `export ${key}=\"$OUTPOST_VALUE_${index}\"; unset OUTPOST_VALUE_${index};`,\n )\n .join(\" \");\n const flags = [\n \"exec\",\n command.interactive && process.stdin.isTTY ? \"-it\" : \"-i\",\n ...(command.elevated ? [\"--user\", \"0:0\"] : []),\n \"--workdir\",\n command.directory ?? root,\n ...Object.keys(environment).flatMap((key) => [\"--env\", key]),\n name,\n ];\n const wrapper = `${exports} echo $$ > ${quote(pidFile)}; test ! -f ${quote(pidFile + \".cancel\")} || exit 130; exec \"$@\"`;\n const { directory: _directory, ...invocation } = command;\n const shutdown = new AbortController();\n const signal = command.signal\n ? AbortSignal.any([command.signal, shutdown.signal])\n : shutdown.signal;\n let settled!: () => void;\n const completion = new Promise<void>((resolve) => {\n settled = resolve;\n });\n const unregister = registerCleanup(async () => {\n shutdown.abort(\n new OutpostError(\"aborted\", \"Container command interrupted\"),\n );\n await completion;\n });\n try {\n return await executor({\n ...invocation,\n signal,\n executable: engine,\n arguments: [\n ...flags,\n ...(command.interactive && process.stdin.isTTY\n ? []\n : [\"setsid\", \"--wait\"]),\n \"sh\",\n \"-c\",\n wrapper,\n \"outpost\",\n command.executable,\n ...(command.arguments ?? []),\n ],\n variables: environment,\n retain:\n command.retain ?? config.retain ?? containerDefaults.retainBytes,\n });\n } catch (cause) {\n try {\n await call([\n \"exec\",\n ...(command.elevated ? [\"--user\", \"0:0\"] : []),\n name,\n \"sh\",\n \"-c\",\n `touch ${quote(pidFile + \".cancel\")}; if [ -f ${quote(pidFile)} ]; then p=$(cat ${quote(pidFile)}); kill -TERM -\"$p\" 2>/dev/null || true; sleep 0.5; kill -KILL -\"$p\" 2>/dev/null || true; fi`,\n ]);\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Command cancellation could not be confirmed\",\n );\n }\n throw cause;\n } finally {\n try {\n if (!isClosed())\n await call([\n \"exec\",\n name,\n \"rm\",\n \"-f\",\n pidFile,\n pidFile + \".cancel\",\n ]).catch(() => undefined);\n } finally {\n unregister();\n settled();\n }\n }\n };\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export declare const defaultEgressImage: string | undefined;
2
+ export declare const containerEgress: Readonly<{
3
+ label: "io.outpost.egress.owner";
4
+ entrypoint: "/opt/outpost/providers/egress/main.js";
5
+ startupMs: 15000;
6
+ pollMs: 100;
7
+ }>;
@@ -0,0 +1,8 @@
1
+ export const defaultEgressImage = "ghcr.io/elie-laloum/outpost/egress@sha256:f56c1272514e65a3a65d7663c5f9c025abad11278577c36d5d699010dc8713a8";
2
+ export const containerEgress = Object.freeze({
3
+ label: "io.outpost.egress.owner",
4
+ entrypoint: "/opt/outpost/providers/egress/main.js",
5
+ startupMs: 15_000,
6
+ pollMs: 100,
7
+ });
8
+ //# sourceMappingURL=container-egress.constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-egress.constants.js","sourceRoot":"","sources":["../../src/providers/container-egress.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kBAAkB,GAC7B,4GAA4G,CAAC;AAE/G,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3C,KAAK,EAAE,yBAAyB;IAChC,UAAU,EAAE,uCAAuC;IACnD,SAAS,EAAE,MAAM;IACjB,MAAM,EAAE,GAAG;CACZ,CAAC,CAAC","sourcesContent":["export const defaultEgressImage: string | undefined =\n \"ghcr.io/elie-laloum/outpost/egress@sha256:f56c1272514e65a3a65d7663c5f9c025abad11278577c36d5d699010dc8713a8\";\n\nexport const containerEgress = Object.freeze({\n label: \"io.outpost.egress.owner\",\n entrypoint: \"/opt/outpost/providers/egress/main.js\",\n startupMs: 15_000,\n pollMs: 100,\n});\n"]}
@@ -0,0 +1,7 @@
1
+ import type { ContainerCall, ContainerEngine, ContainerOptions, ContainerUser } from "./container.types.ts";
2
+ export declare function containerUserNamespace(engine: ContainerEngine, config: ContainerOptions, user: ContainerUser): string[];
3
+ export declare function createContainerEgress(call: ContainerCall, name: string, config: ContainerOptions, engine: ContainerEngine, user: ContainerUser): {
4
+ name: string;
5
+ start(signal?: AbortSignal): Promise<void>;
6
+ release(): Promise<void>;
7
+ };