@elie-laloum/outpost 11.1.3 → 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 +25 -0
  2. package/README.md +1 -0
  3. package/SECURITY.md +5 -1
  4. package/dist/adapters/agents/antigravity/antigravity-adapter.js +9 -0
  5. package/dist/adapters/agents/antigravity/antigravity-adapter.js.map +1 -1
  6. package/dist/adapters/agents/antigravity/antigravity-hook-plan.d.ts +3 -0
  7. package/dist/adapters/agents/antigravity/antigravity-hook-plan.js +39 -0
  8. package/dist/adapters/agents/antigravity/antigravity-hook-plan.js.map +1 -0
  9. package/dist/adapters/agents/antigravity/antigravity-hooks.constants.d.ts +3 -0
  10. package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js +394 -0
  11. package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js.map +1 -0
  12. package/dist/adapters/agents/antigravity/antigravity-hooks.d.ts +2 -0
  13. package/dist/adapters/agents/antigravity/antigravity-hooks.js +6 -0
  14. package/dist/adapters/agents/antigravity/antigravity-hooks.js.map +1 -0
  15. package/dist/adapters/agents/antigravity/antigravity-hooks.types.d.ts +114 -0
  16. package/dist/adapters/agents/antigravity/antigravity-hooks.types.js +2 -0
  17. package/dist/adapters/agents/antigravity/antigravity-hooks.types.js.map +1 -0
  18. package/dist/adapters/agents/antigravity/antigravity.types.d.ts +1 -0
  19. package/dist/adapters/agents/antigravity/antigravity.types.js.map +1 -1
  20. package/dist/adapters/agents/authentication.js +1 -0
  21. package/dist/adapters/agents/authentication.js.map +1 -1
  22. package/dist/adapters/agents/claude/claude-adapter.js +15 -0
  23. package/dist/adapters/agents/claude/claude-adapter.js.map +1 -1
  24. package/dist/adapters/agents/claude/claude-hook-plan.constants.d.ts +1 -0
  25. package/dist/adapters/agents/claude/claude-hook-plan.constants.js +20 -0
  26. package/dist/adapters/agents/claude/claude-hook-plan.constants.js.map +1 -0
  27. package/dist/adapters/agents/claude/claude-hook-plan.d.ts +4 -0
  28. package/dist/adapters/agents/claude/claude-hook-plan.js +60 -0
  29. package/dist/adapters/agents/claude/claude-hook-plan.js.map +1 -0
  30. package/dist/adapters/agents/claude/claude-hooks.constants.d.ts +3 -0
  31. package/dist/adapters/agents/claude/claude-hooks.constants.js +2968 -0
  32. package/dist/adapters/agents/claude/claude-hooks.constants.js.map +1 -0
  33. package/dist/adapters/agents/claude/claude-hooks.d.ts +2 -0
  34. package/dist/adapters/agents/claude/claude-hooks.js +6 -0
  35. package/dist/adapters/agents/claude/claude-hooks.js.map +1 -0
  36. package/dist/adapters/agents/claude/claude-hooks.types.d.ts +900 -0
  37. package/dist/adapters/agents/claude/claude-hooks.types.js +2 -0
  38. package/dist/adapters/agents/claude/claude-hooks.types.js.map +1 -0
  39. package/dist/adapters/agents/claude/claude-profile.d.ts +1 -0
  40. package/dist/adapters/agents/claude/claude-profile.js +23 -11
  41. package/dist/adapters/agents/claude/claude-profile.js.map +1 -1
  42. package/dist/adapters/agents/codex/codex-adapter.js +17 -1
  43. package/dist/adapters/agents/codex/codex-adapter.js.map +1 -1
  44. package/dist/adapters/agents/codex/codex-authentication.js +1 -0
  45. package/dist/adapters/agents/codex/codex-authentication.js.map +1 -1
  46. package/dist/adapters/agents/codex/codex-conversations.constants.d.ts +1 -0
  47. package/dist/adapters/agents/codex/codex-conversations.constants.js +2 -0
  48. package/dist/adapters/agents/codex/codex-conversations.constants.js.map +1 -0
  49. package/dist/adapters/agents/codex/codex-conversations.js +8 -5
  50. package/dist/adapters/agents/codex/codex-conversations.js.map +1 -1
  51. package/dist/adapters/agents/codex/codex-hook-plan.d.ts +3 -0
  52. package/dist/adapters/agents/codex/codex-hook-plan.js +72 -0
  53. package/dist/adapters/agents/codex/codex-hook-plan.js.map +1 -0
  54. package/dist/adapters/agents/codex/codex-hooks.constants.d.ts +3 -0
  55. package/dist/adapters/agents/codex/codex-hooks.constants.js +942 -0
  56. package/dist/adapters/agents/codex/codex-hooks.constants.js.map +1 -0
  57. package/dist/adapters/agents/codex/codex-hooks.d.ts +2 -0
  58. package/dist/adapters/agents/codex/codex-hooks.js +6 -0
  59. package/dist/adapters/agents/codex/codex-hooks.js.map +1 -0
  60. package/dist/adapters/agents/codex/codex-hooks.types.d.ts +255 -0
  61. package/dist/adapters/agents/codex/codex-hooks.types.js +2 -0
  62. package/dist/adapters/agents/codex/codex-hooks.types.js.map +1 -0
  63. package/dist/adapters/agents/codex/codex-provider.js +2 -2
  64. package/dist/adapters/agents/codex/codex-provider.js.map +1 -1
  65. package/dist/adapters/agents/copilot/copilot-adapter.js +14 -0
  66. package/dist/adapters/agents/copilot/copilot-adapter.js.map +1 -1
  67. package/dist/adapters/agents/copilot/copilot-hook-plan.d.ts +3 -0
  68. package/dist/adapters/agents/copilot/copilot-hook-plan.js +26 -0
  69. package/dist/adapters/agents/copilot/copilot-hook-plan.js.map +1 -0
  70. package/dist/adapters/agents/copilot/copilot-hooks.constants.d.ts +3 -0
  71. package/dist/adapters/agents/copilot/copilot-hooks.constants.js +528 -0
  72. package/dist/adapters/agents/copilot/copilot-hooks.constants.js.map +1 -0
  73. package/dist/adapters/agents/copilot/copilot-hooks.d.ts +2 -0
  74. package/dist/adapters/agents/copilot/copilot-hooks.js +6 -0
  75. package/dist/adapters/agents/copilot/copilot-hooks.js.map +1 -0
  76. package/dist/adapters/agents/copilot/copilot-hooks.types.d.ts +166 -0
  77. package/dist/adapters/agents/copilot/copilot-hooks.types.js +2 -0
  78. package/dist/adapters/agents/copilot/copilot-hooks.types.js.map +1 -0
  79. package/dist/adapters/agents/copilot/copilot.types.d.ts +1 -0
  80. package/dist/adapters/agents/copilot/copilot.types.js.map +1 -1
  81. package/dist/adapters/agents/hook-groups.d.ts +2 -0
  82. package/dist/adapters/agents/hook-groups.js +17 -0
  83. package/dist/adapters/agents/hook-groups.js.map +1 -0
  84. package/dist/adapters/agents/kimi/kimi-adapter.js +16 -1
  85. package/dist/adapters/agents/kimi/kimi-adapter.js.map +1 -1
  86. package/dist/adapters/agents/kimi/kimi-hook-plan.d.ts +3 -0
  87. package/dist/adapters/agents/kimi/kimi-hook-plan.js +33 -0
  88. package/dist/adapters/agents/kimi/kimi-hook-plan.js.map +1 -0
  89. package/dist/adapters/agents/kimi/kimi-hooks.constants.d.ts +3 -0
  90. package/dist/adapters/agents/kimi/kimi-hooks.constants.js +747 -0
  91. package/dist/adapters/agents/kimi/kimi-hooks.constants.js.map +1 -0
  92. package/dist/adapters/agents/kimi/kimi-hooks.d.ts +2 -0
  93. package/dist/adapters/agents/kimi/kimi-hooks.js +6 -0
  94. package/dist/adapters/agents/kimi/kimi-hooks.js.map +1 -0
  95. package/dist/adapters/agents/kimi/kimi-hooks.types.d.ts +226 -0
  96. package/dist/adapters/agents/kimi/kimi-hooks.types.js +2 -0
  97. package/dist/adapters/agents/kimi/kimi-hooks.types.js.map +1 -0
  98. package/dist/adapters/agents/kimi/kimi-request.js +2 -0
  99. package/dist/adapters/agents/kimi/kimi-request.js.map +1 -1
  100. package/dist/adapters/agents/kimi/kimi.constants.d.ts +1 -0
  101. package/dist/adapters/agents/kimi/kimi.constants.js +1 -0
  102. package/dist/adapters/agents/kimi/kimi.constants.js.map +1 -1
  103. package/dist/adapters/agents/kimi/kimi.types.d.ts +1 -0
  104. package/dist/adapters/agents/kimi/kimi.types.js.map +1 -1
  105. package/dist/adapters/agents/native-hooks.d.ts +4 -0
  106. package/dist/adapters/agents/native-hooks.js +63 -0
  107. package/dist/adapters/agents/native-hooks.js.map +1 -0
  108. package/dist/adapters/agents/native-hooks.types.d.ts +22 -0
  109. package/dist/adapters/agents/native-hooks.types.js +2 -0
  110. package/dist/adapters/agents/native-hooks.types.js.map +1 -0
  111. package/dist/adapters/agents/settings.types.d.ts +2 -0
  112. package/dist/adapters/agents/settings.types.js.map +1 -1
  113. package/dist/adapters/decisions/system-one-provider.constants.d.ts +1 -1
  114. package/dist/adapters/decisions/system-one-provider.constants.js +1 -1
  115. package/dist/adapters/decisions/system-one-provider.constants.js.map +1 -1
  116. package/dist/adapters/decisions/system-one-provider.js +8 -21
  117. package/dist/adapters/decisions/system-one-provider.js.map +1 -1
  118. package/dist/adapters/models/anthropic-model-provider.constants.d.ts +1 -0
  119. package/dist/adapters/models/anthropic-model-provider.constants.js +4 -0
  120. package/dist/adapters/models/anthropic-model-provider.constants.js.map +1 -1
  121. package/dist/adapters/models/anthropic-model-provider.js +7 -1
  122. package/dist/adapters/models/anthropic-model-provider.js.map +1 -1
  123. package/dist/adapters/models/http-failure.d.ts +3 -0
  124. package/dist/adapters/models/http-failure.js +43 -0
  125. package/dist/adapters/models/http-failure.js.map +1 -0
  126. package/dist/adapters/models/http-provider.js +14 -20
  127. package/dist/adapters/models/http-provider.js.map +1 -1
  128. package/dist/adapters/models/model-protocol.types.d.ts +1 -0
  129. package/dist/adapters/models/model-protocol.types.js.map +1 -1
  130. package/dist/adapters/models/model.constants.d.ts +2 -0
  131. package/dist/adapters/models/model.constants.js +17 -0
  132. package/dist/adapters/models/model.constants.js.map +1 -1
  133. package/dist/adapters/models/openai-chat-stream.js +3 -1
  134. package/dist/adapters/models/openai-chat-stream.js.map +1 -1
  135. package/dist/adapters/models/openai-responses.js +4 -0
  136. package/dist/adapters/models/openai-responses.js.map +1 -1
  137. package/dist/adapters/secrets/aws.js +5 -4
  138. package/dist/adapters/secrets/aws.js.map +1 -1
  139. package/dist/adapters/secrets/azure.constants.d.ts +1 -0
  140. package/dist/adapters/secrets/azure.constants.js +2 -0
  141. package/dist/adapters/secrets/azure.constants.js.map +1 -0
  142. package/dist/adapters/secrets/azure.js +2 -1
  143. package/dist/adapters/secrets/azure.js.map +1 -1
  144. package/dist/adapters/secrets/gcp.constants.d.ts +1 -0
  145. package/dist/adapters/secrets/gcp.constants.js +2 -0
  146. package/dist/adapters/secrets/gcp.constants.js.map +1 -0
  147. package/dist/adapters/secrets/gcp.js +11 -9
  148. package/dist/adapters/secrets/gcp.js.map +1 -1
  149. package/dist/adapters/secrets/infisical.js +3 -2
  150. package/dist/adapters/secrets/infisical.js.map +1 -1
  151. package/dist/adapters/secrets/onepassword.constants.d.ts +1 -0
  152. package/dist/adapters/secrets/onepassword.constants.js +2 -0
  153. package/dist/adapters/secrets/onepassword.constants.js.map +1 -0
  154. package/dist/adapters/secrets/onepassword.js +2 -1
  155. package/dist/adapters/secrets/onepassword.js.map +1 -1
  156. package/dist/adapters/secrets/secret-source.js +3 -1
  157. package/dist/adapters/secrets/secret-source.js.map +1 -1
  158. package/dist/adapters/secrets/vault-response.js +5 -5
  159. package/dist/adapters/secrets/vault-response.js.map +1 -1
  160. package/dist/adapters/secrets/vault.js +4 -3
  161. package/dist/adapters/secrets/vault.js.map +1 -1
  162. package/dist/adapters/testing/scripted-agent.d.ts +3 -1
  163. package/dist/adapters/testing/scripted-agent.js +3 -1
  164. package/dist/adapters/testing/scripted-agent.js.map +1 -1
  165. package/dist/adapters/tools/edit-tools.js +3 -3
  166. package/dist/adapters/tools/edit-tools.js.map +1 -1
  167. package/dist/adapters/tools/git-tools.js +16 -1
  168. package/dist/adapters/tools/git-tools.js.map +1 -1
  169. package/dist/adapters/tools/mcp-launcher.constants.d.ts +1 -1
  170. package/dist/adapters/tools/mcp-launcher.constants.js +29 -7
  171. package/dist/adapters/tools/mcp-launcher.constants.js.map +1 -1
  172. package/dist/adapters/tools/mcp-resources.js +5 -1
  173. package/dist/adapters/tools/mcp-resources.js.map +1 -1
  174. package/dist/adapters/tools/mcp.constants.d.ts +0 -6
  175. package/dist/adapters/tools/mcp.constants.js +0 -6
  176. package/dist/adapters/tools/mcp.constants.js.map +1 -1
  177. package/dist/adapters/tools/mcp.js +1 -0
  178. package/dist/adapters/tools/mcp.js.map +1 -1
  179. package/dist/adapters/tools/sandbox-files.constants.d.ts +1 -0
  180. package/dist/adapters/tools/sandbox-files.constants.js +17 -0
  181. package/dist/adapters/tools/sandbox-files.constants.js.map +1 -0
  182. package/dist/adapters/tools/sandbox-files.d.ts +1 -0
  183. package/dist/adapters/tools/sandbox-files.js +14 -1
  184. package/dist/adapters/tools/sandbox-files.js.map +1 -1
  185. package/dist/adapters/tools/tools.constants.js +1 -0
  186. package/dist/adapters/tools/tools.constants.js.map +1 -1
  187. package/dist/adapters/triggers/github-webhook.js +14 -11
  188. package/dist/adapters/triggers/github-webhook.js.map +1 -1
  189. package/dist/adapters/triggers/gitlab-webhook.js +16 -13
  190. package/dist/adapters/triggers/gitlab-webhook.js.map +1 -1
  191. package/dist/adapters/triggers/slack-request.js +10 -7
  192. package/dist/adapters/triggers/slack-request.js.map +1 -1
  193. package/dist/adapters/triggers/standard-webhook.js +10 -7
  194. package/dist/adapters/triggers/standard-webhook.js.map +1 -1
  195. package/dist/application/agent-authentication.js +4 -1
  196. package/dist/application/agent-authentication.js.map +1 -1
  197. package/dist/application/agent-turn.js +20 -5
  198. package/dist/application/agent-turn.js.map +1 -1
  199. package/dist/application/cli-hooks.constants.d.ts +2 -0
  200. package/dist/application/cli-hooks.constants.js +69 -0
  201. package/dist/application/cli-hooks.constants.js.map +1 -0
  202. package/dist/application/cli-hooks.d.ts +3 -0
  203. package/dist/application/cli-hooks.js +168 -0
  204. package/dist/application/cli-hooks.js.map +1 -0
  205. package/dist/application/cli-steering.js +33 -2
  206. package/dist/application/cli-steering.js.map +1 -1
  207. package/dist/application/custom-turn.js +10 -1
  208. package/dist/application/custom-turn.js.map +1 -1
  209. package/dist/application/diagnostic-probe.d.ts +1 -1
  210. package/dist/application/diagnostic-probe.js +7 -3
  211. package/dist/application/diagnostic-probe.js.map +1 -1
  212. package/dist/application/dispatch-observation.js +4 -0
  213. package/dist/application/dispatch-observation.js.map +1 -1
  214. package/dist/application/doctor-agent.d.ts +1 -1
  215. package/dist/application/doctor-agent.js +2 -2
  216. package/dist/application/doctor-agent.js.map +1 -1
  217. package/dist/application/doctor-deadline.d.ts +1 -0
  218. package/dist/application/doctor-deadline.js +10 -0
  219. package/dist/application/doctor-deadline.js.map +1 -0
  220. package/dist/application/doctor-image.js +9 -7
  221. package/dist/application/doctor-image.js.map +1 -1
  222. package/dist/application/doctor-sandbox.js +4 -4
  223. package/dist/application/doctor-sandbox.js.map +1 -1
  224. package/dist/application/doctor.constants.d.ts +2 -0
  225. package/dist/application/doctor.constants.js +2 -0
  226. package/dist/application/doctor.constants.js.map +1 -1
  227. package/dist/application/doctor.js +12 -8
  228. package/dist/application/doctor.js.map +1 -1
  229. package/dist/application/doctor.types.d.ts +2 -0
  230. package/dist/application/doctor.types.js.map +1 -1
  231. package/dist/application/file-sandbox.js +49 -11
  232. package/dist/application/file-sandbox.js.map +1 -1
  233. package/dist/application/harness-context.js +4 -2
  234. package/dist/application/harness-context.js.map +1 -1
  235. package/dist/application/harness-hook-transforms.d.ts +8 -0
  236. package/dist/application/harness-hook-transforms.js +115 -0
  237. package/dist/application/harness-hook-transforms.js.map +1 -0
  238. package/dist/application/harness-hooks.d.ts +5 -3
  239. package/dist/application/harness-hooks.js +37 -50
  240. package/dist/application/harness-hooks.js.map +1 -1
  241. package/dist/application/harness-loop.d.ts +2 -1
  242. package/dist/application/harness-loop.js +95 -21
  243. package/dist/application/harness-loop.js.map +1 -1
  244. package/dist/application/harness-subagent.d.ts +1 -1
  245. package/dist/application/harness-subagent.js +63 -12
  246. package/dist/application/harness-subagent.js.map +1 -1
  247. package/dist/application/harness.types.d.ts +3 -0
  248. package/dist/application/harness.types.js.map +1 -1
  249. package/dist/application/hook-invocation.d.ts +4 -0
  250. package/dist/application/hook-invocation.js +59 -0
  251. package/dist/application/hook-invocation.js.map +1 -0
  252. package/dist/application/queue-worker.js +51 -9
  253. package/dist/application/queue-worker.js.map +1 -1
  254. package/dist/application/queue-worker.types.d.ts +1 -0
  255. package/dist/application/queue-worker.types.js.map +1 -1
  256. package/dist/application/recipes/native-schemas.constants.js +1439 -218
  257. package/dist/application/recipes/native-schemas.constants.js.map +1 -1
  258. package/dist/application/recovery-inspection.js +4 -1
  259. package/dist/application/recovery-inspection.js.map +1 -1
  260. package/dist/application/remote-download.js +15 -5
  261. package/dist/application/remote-download.js.map +1 -1
  262. package/dist/application/remote-paths.d.ts +1 -0
  263. package/dist/application/remote-paths.js +7 -0
  264. package/dist/application/remote-paths.js.map +1 -0
  265. package/dist/application/remote-validation.js +40 -9
  266. package/dist/application/remote-validation.js.map +1 -1
  267. package/dist/application/remote-workspace.js +17 -2
  268. package/dist/application/remote-workspace.js.map +1 -1
  269. package/dist/application/repetition-watchdog.js +3 -1
  270. package/dist/application/repetition-watchdog.js.map +1 -1
  271. package/dist/application/sandbox-attach.js +38 -9
  272. package/dist/application/sandbox-attach.js.map +1 -1
  273. package/dist/application/sandbox-dispatch.js +7 -1
  274. package/dist/application/sandbox-dispatch.js.map +1 -1
  275. package/dist/application/schedules.js +2 -0
  276. package/dist/application/schedules.js.map +1 -1
  277. package/dist/application/secrets.js +4 -0
  278. package/dist/application/secrets.js.map +1 -1
  279. package/dist/application/tool-execution.js +28 -8
  280. package/dist/application/tool-execution.js.map +1 -1
  281. package/dist/application/transport-retention.js +3 -3
  282. package/dist/application/transport-retention.js.map +1 -1
  283. package/dist/application/workspace-fingerprint.d.ts +1 -0
  284. package/dist/application/workspace-fingerprint.js +19 -4
  285. package/dist/application/workspace-fingerprint.js.map +1 -1
  286. package/dist/cli/doctor-command.js +12 -2
  287. package/dist/cli/doctor-command.js.map +1 -1
  288. package/dist/cli/image.js +2 -0
  289. package/dist/cli/image.js.map +1 -1
  290. package/dist/cli/main.constants.d.ts +14 -2
  291. package/dist/cli/main.constants.js +27 -1
  292. package/dist/cli/main.constants.js.map +1 -1
  293. package/dist/cli/project-settings.js +7 -1
  294. package/dist/cli/project-settings.js.map +1 -1
  295. package/dist/cli/recipe-yaml.js +8 -0
  296. package/dist/cli/recipe-yaml.js.map +1 -1
  297. package/dist/cli/recovery-command.js +3 -0
  298. package/dist/cli/recovery-command.js.map +1 -1
  299. package/dist/cli/recovery-unlock-command.d.ts +2 -0
  300. package/dist/cli/recovery-unlock-command.js +19 -0
  301. package/dist/cli/recovery-unlock-command.js.map +1 -0
  302. package/dist/cli/scaffold-files.js +4 -1
  303. package/dist/cli/scaffold-files.js.map +1 -1
  304. package/dist/cli/scaffold-validation.js +3 -0
  305. package/dist/cli/scaffold-validation.js.map +1 -1
  306. package/dist/cli/scaffold.js +2 -0
  307. package/dist/cli/scaffold.js.map +1 -1
  308. package/dist/domain/agent-hook.types.d.ts +28 -0
  309. package/dist/domain/agent-hook.types.js +2 -0
  310. package/dist/domain/agent-hook.types.js.map +1 -0
  311. package/dist/domain/agent-profile-hooks.d.ts +2 -0
  312. package/dist/domain/agent-profile-hooks.js +52 -0
  313. package/dist/domain/agent-profile-hooks.js.map +1 -0
  314. package/dist/domain/agent-profile-permissions.js +11 -1
  315. package/dist/domain/agent-profile-permissions.js.map +1 -1
  316. package/dist/domain/agent-profile.constants.d.ts +1 -0
  317. package/dist/domain/agent-profile.constants.js +9 -0
  318. package/dist/domain/agent-profile.constants.js.map +1 -1
  319. package/dist/domain/agent-profile.js +6 -1
  320. package/dist/domain/agent-profile.js.map +1 -1
  321. package/dist/domain/agent-profile.types.d.ts +11 -0
  322. package/dist/domain/agent-profile.types.js.map +1 -1
  323. package/dist/domain/agent.types.d.ts +2 -0
  324. package/dist/domain/agent.types.js.map +1 -1
  325. package/dist/domain/container-image.d.ts +1 -0
  326. package/dist/domain/container-image.js +8 -0
  327. package/dist/domain/container-image.js.map +1 -0
  328. package/dist/domain/context.constants.d.ts +1 -0
  329. package/dist/domain/context.constants.js +1 -0
  330. package/dist/domain/context.constants.js.map +1 -1
  331. package/dist/domain/context.js +6 -1
  332. package/dist/domain/context.js.map +1 -1
  333. package/dist/domain/egress.js +6 -1
  334. package/dist/domain/egress.js.map +1 -1
  335. package/dist/domain/glob.js +1 -1
  336. package/dist/domain/glob.js.map +1 -1
  337. package/dist/domain/harness.js +4 -1
  338. package/dist/domain/harness.js.map +1 -1
  339. package/dist/domain/hook-command.d.ts +2 -0
  340. package/dist/domain/hook-command.js +26 -0
  341. package/dist/domain/hook-command.js.map +1 -0
  342. package/dist/domain/hook-command.types.d.ts +7 -0
  343. package/dist/domain/hook-command.types.js +2 -0
  344. package/dist/domain/hook-command.types.js.map +1 -0
  345. package/dist/domain/hook-decision.d.ts +2 -0
  346. package/dist/domain/hook-decision.js +38 -0
  347. package/dist/domain/hook-decision.js.map +1 -0
  348. package/dist/domain/hook.constants.d.ts +3 -0
  349. package/dist/domain/hook.constants.js +31 -1
  350. package/dist/domain/hook.constants.js.map +1 -1
  351. package/dist/domain/hook.js +49 -5
  352. package/dist/domain/hook.js.map +1 -1
  353. package/dist/domain/hook.types.d.ts +82 -5
  354. package/dist/domain/hook.types.js.map +1 -1
  355. package/dist/domain/mcp-server.constants.d.ts +6 -0
  356. package/dist/domain/mcp-server.constants.js +6 -0
  357. package/dist/domain/mcp-server.constants.js.map +1 -1
  358. package/dist/domain/mcp-server.js +1 -0
  359. package/dist/domain/mcp-server.js.map +1 -1
  360. package/dist/domain/permissions.types.d.ts +1 -0
  361. package/dist/domain/permissions.types.js.map +1 -1
  362. package/dist/domain/pricing.js +4 -2
  363. package/dist/domain/pricing.js.map +1 -1
  364. package/dist/domain/redaction.js +6 -3
  365. package/dist/domain/redaction.js.map +1 -1
  366. package/dist/domain/secret-fault.d.ts +4 -0
  367. package/dist/domain/secret-fault.js +11 -0
  368. package/dist/domain/secret-fault.js.map +1 -0
  369. package/dist/domain/secrets.js +3 -2
  370. package/dist/domain/secrets.js.map +1 -1
  371. package/dist/domain/steering.js +2 -0
  372. package/dist/domain/steering.js.map +1 -1
  373. package/dist/domain/steering.types.d.ts +1 -1
  374. package/dist/domain/steering.types.js.map +1 -1
  375. package/dist/domain/transport.js +5 -1
  376. package/dist/domain/transport.js.map +1 -1
  377. package/dist/domain/trigger-payload.d.ts +4 -0
  378. package/dist/domain/trigger-payload.js +15 -0
  379. package/dist/domain/trigger-payload.js.map +1 -0
  380. package/dist/domain/workflow/checkpoint.js +19 -2
  381. package/dist/domain/workflow/checkpoint.js.map +1 -1
  382. package/dist/domain/workflow/retry.constants.d.ts +1 -0
  383. package/dist/domain/workflow/retry.constants.js +1 -0
  384. package/dist/domain/workflow/retry.constants.js.map +1 -1
  385. package/dist/domain/workflow/retry.js +6 -2
  386. package/dist/domain/workflow/retry.js.map +1 -1
  387. package/dist/domain/workflow/task-runner.js +5 -3
  388. package/dist/domain/workflow/task-runner.js.map +1 -1
  389. package/dist/domain/workflow/workflow-state.js +2 -1
  390. package/dist/domain/workflow/workflow-state.js.map +1 -1
  391. package/dist/domain/workflow.types.d.ts +1 -0
  392. package/dist/domain/workflow.types.js.map +1 -1
  393. package/dist/index.d.ts +15 -1
  394. package/dist/index.js +6 -0
  395. package/dist/index.js.map +1 -1
  396. package/dist/infrastructure/conversations/transcript-store.js +32 -5
  397. package/dist/infrastructure/conversations/transcript-store.js.map +1 -1
  398. package/dist/infrastructure/files.d.ts +1 -0
  399. package/dist/infrastructure/files.js +8 -4
  400. package/dist/infrastructure/files.js.map +1 -1
  401. package/dist/infrastructure/git/integration-candidate.js +3 -2
  402. package/dist/infrastructure/git/integration-candidate.js.map +1 -1
  403. package/dist/infrastructure/git/lock-recovery.d.ts +3 -0
  404. package/dist/infrastructure/git/lock-recovery.js +58 -0
  405. package/dist/infrastructure/git/lock-recovery.js.map +1 -0
  406. package/dist/infrastructure/git/lock-recovery.types.d.ts +11 -0
  407. package/dist/infrastructure/git/lock-recovery.types.js +2 -0
  408. package/dist/infrastructure/git/lock-recovery.types.js.map +1 -0
  409. package/dist/infrastructure/git/managed-worktree.js +5 -8
  410. package/dist/infrastructure/git/managed-worktree.js.map +1 -1
  411. package/dist/infrastructure/git/verification-command.js +35 -30
  412. package/dist/infrastructure/git/verification-command.js.map +1 -1
  413. package/dist/infrastructure/git/workspace-lease.js +12 -5
  414. package/dist/infrastructure/git/workspace-lease.js.map +1 -1
  415. package/dist/infrastructure/hook-configuration.constants.d.ts +1 -0
  416. package/dist/infrastructure/hook-configuration.constants.js +59 -0
  417. package/dist/infrastructure/hook-configuration.constants.js.map +1 -0
  418. package/dist/infrastructure/hook-configuration.d.ts +3 -0
  419. package/dist/infrastructure/hook-configuration.js +16 -0
  420. package/dist/infrastructure/hook-configuration.js.map +1 -0
  421. package/dist/infrastructure/hook-configuration.types.d.ts +14 -0
  422. package/dist/infrastructure/hook-configuration.types.js +2 -0
  423. package/dist/infrastructure/hook-configuration.types.js.map +1 -0
  424. package/dist/infrastructure/http-transport.constants.d.ts +1 -0
  425. package/dist/infrastructure/http-transport.constants.js +14 -0
  426. package/dist/infrastructure/http-transport.constants.js.map +1 -0
  427. package/dist/infrastructure/http-transport.d.ts +7 -0
  428. package/dist/infrastructure/http-transport.js +19 -0
  429. package/dist/infrastructure/http-transport.js.map +1 -0
  430. package/dist/infrastructure/input-failure.d.ts +4 -0
  431. package/dist/infrastructure/input-failure.js +14 -0
  432. package/dist/infrastructure/input-failure.js.map +1 -0
  433. package/dist/infrastructure/inspection-file.d.ts +1 -0
  434. package/dist/infrastructure/inspection-file.js +8 -2
  435. package/dist/infrastructure/inspection-file.js.map +1 -1
  436. package/dist/infrastructure/local-transport.js +16 -11
  437. package/dist/infrastructure/local-transport.js.map +1 -1
  438. package/dist/infrastructure/process.constants.d.ts +1 -0
  439. package/dist/infrastructure/process.constants.js +1 -0
  440. package/dist/infrastructure/process.constants.js.map +1 -1
  441. package/dist/infrastructure/process.d.ts +1 -0
  442. package/dist/infrastructure/process.js +48 -8
  443. package/dist/infrastructure/process.js.map +1 -1
  444. package/dist/infrastructure/reservation-ledger.d.ts +2 -0
  445. package/dist/infrastructure/reservation-ledger.js +84 -0
  446. package/dist/infrastructure/reservation-ledger.js.map +1 -0
  447. package/dist/infrastructure/reservation-recovery.d.ts +4 -0
  448. package/dist/infrastructure/reservation-recovery.js +28 -0
  449. package/dist/infrastructure/reservation-recovery.js.map +1 -0
  450. package/dist/infrastructure/s3-tombstones.d.ts +3 -0
  451. package/dist/infrastructure/s3-tombstones.js +46 -0
  452. package/dist/infrastructure/s3-tombstones.js.map +1 -0
  453. package/dist/infrastructure/s3-tombstones.types.d.ts +6 -0
  454. package/dist/infrastructure/s3-tombstones.types.js +2 -0
  455. package/dist/infrastructure/s3-tombstones.types.js.map +1 -0
  456. package/dist/infrastructure/s3-transport.d.ts +2 -0
  457. package/dist/infrastructure/s3-transport.js +4 -2
  458. package/dist/infrastructure/s3-transport.js.map +1 -1
  459. package/dist/infrastructure/sse.js +6 -3
  460. package/dist/infrastructure/sse.js.map +1 -1
  461. package/dist/infrastructure/task-queue-bullmq-connection.d.ts +3 -0
  462. package/dist/infrastructure/task-queue-bullmq-connection.js +32 -0
  463. package/dist/infrastructure/task-queue-bullmq-connection.js.map +1 -0
  464. package/dist/infrastructure/task-queue-bullmq-connection.types.d.ts +8 -0
  465. package/dist/infrastructure/task-queue-bullmq-connection.types.js +2 -0
  466. package/dist/infrastructure/task-queue-bullmq-connection.types.js.map +1 -0
  467. package/dist/infrastructure/task-queue-bullmq-resources.js +21 -15
  468. package/dist/infrastructure/task-queue-bullmq-resources.js.map +1 -1
  469. package/dist/infrastructure/task-queue-bullmq-state.d.ts +1 -0
  470. package/dist/infrastructure/task-queue-bullmq-state.js +1 -0
  471. package/dist/infrastructure/task-queue-bullmq-state.js.map +1 -1
  472. package/dist/infrastructure/task-queue-bullmq.constants.d.ts +2 -1
  473. package/dist/infrastructure/task-queue-bullmq.constants.js +2 -1
  474. package/dist/infrastructure/task-queue-bullmq.constants.js.map +1 -1
  475. package/dist/infrastructure/task-queue-bullmq.js +10 -1
  476. package/dist/infrastructure/task-queue-bullmq.js.map +1 -1
  477. package/dist/infrastructure/task-queue.js +13 -4
  478. package/dist/infrastructure/task-queue.js.map +1 -1
  479. package/dist/infrastructure/transfer-copy.d.ts +1 -0
  480. package/dist/infrastructure/transfer-copy.js +16 -1
  481. package/dist/infrastructure/transfer-copy.js.map +1 -1
  482. package/dist/infrastructure/transport-archive.d.ts +3 -3
  483. package/dist/infrastructure/transport-archive.js +71 -48
  484. package/dist/infrastructure/transport-archive.js.map +1 -1
  485. package/dist/infrastructure/transport-archive.types.d.ts +3 -0
  486. package/dist/infrastructure/transport-archive.types.js.map +1 -1
  487. package/dist/infrastructure/transport-checkpoint.js +14 -4
  488. package/dist/infrastructure/transport-checkpoint.js.map +1 -1
  489. package/dist/infrastructure/transport-envelope.d.ts +1 -0
  490. package/dist/infrastructure/transport-envelope.js +22 -5
  491. package/dist/infrastructure/transport-envelope.js.map +1 -1
  492. package/dist/infrastructure/transport-reservations.constants.d.ts +2 -0
  493. package/dist/infrastructure/transport-reservations.constants.js +3 -0
  494. package/dist/infrastructure/transport-reservations.constants.js.map +1 -0
  495. package/dist/infrastructure/transport-reservations.d.ts +1 -2
  496. package/dist/infrastructure/transport-reservations.js +18 -22
  497. package/dist/infrastructure/transport-reservations.js.map +1 -1
  498. package/dist/infrastructure/transport-reservations.types.d.ts +23 -2
  499. package/dist/infrastructure/transport-reservations.types.js.map +1 -1
  500. package/dist/infrastructure/trigger-server.js +12 -9
  501. package/dist/infrastructure/trigger-server.js.map +1 -1
  502. package/dist/infrastructure/trigger-server.types.d.ts +1 -1
  503. package/dist/infrastructure/trigger-server.types.js.map +1 -1
  504. package/dist/infrastructure/workflow-checkpoint.constants.d.ts +1 -0
  505. package/dist/infrastructure/workflow-checkpoint.constants.js +1 -0
  506. package/dist/infrastructure/workflow-checkpoint.constants.js.map +1 -1
  507. package/dist/infrastructure/workspace-lock.constants.d.ts +2 -1
  508. package/dist/infrastructure/workspace-lock.constants.js +2 -1
  509. package/dist/infrastructure/workspace-lock.constants.js.map +1 -1
  510. package/dist/infrastructure/workspace-lock.js +48 -10
  511. package/dist/infrastructure/workspace-lock.js.map +1 -1
  512. package/dist/providers/cloud-cache.js +14 -9
  513. package/dist/providers/cloud-cache.js.map +1 -1
  514. package/dist/providers/cloud-cache.types.d.ts +1 -0
  515. package/dist/providers/cloud-cache.types.js.map +1 -1
  516. package/dist/providers/cloud-files.js +3 -4
  517. package/dist/providers/cloud-files.js.map +1 -1
  518. package/dist/providers/cloud-input.constants.d.ts +2 -2
  519. package/dist/providers/cloud-input.constants.js +2 -1
  520. package/dist/providers/cloud-input.constants.js.map +1 -1
  521. package/dist/providers/cloud-input.d.ts +2 -2
  522. package/dist/providers/cloud-input.js +14 -3
  523. package/dist/providers/cloud-input.js.map +1 -1
  524. package/dist/providers/container-command.js +30 -5
  525. package/dist/providers/container-command.js.map +1 -1
  526. package/dist/providers/container-egress.constants.d.ts +7 -0
  527. package/dist/providers/container-egress.constants.js +8 -0
  528. package/dist/providers/container-egress.constants.js.map +1 -0
  529. package/dist/providers/container-egress.d.ts +7 -0
  530. package/dist/providers/container-egress.js +101 -0
  531. package/dist/providers/container-egress.js.map +1 -0
  532. package/dist/providers/container-files.js +10 -11
  533. package/dist/providers/container-files.js.map +1 -1
  534. package/dist/providers/container-mounts.js +3 -0
  535. package/dist/providers/container-mounts.js.map +1 -1
  536. package/dist/providers/container-network.js +16 -1
  537. package/dist/providers/container-network.js.map +1 -1
  538. package/dist/providers/container-plan.js +7 -10
  539. package/dist/providers/container-plan.js.map +1 -1
  540. package/dist/providers/container-recovery.js +8 -3
  541. package/dist/providers/container-recovery.js.map +1 -1
  542. package/dist/providers/container.js +36 -5
  543. package/dist/providers/container.js.map +1 -1
  544. package/dist/providers/container.types.d.ts +1 -0
  545. package/dist/providers/container.types.js.map +1 -1
  546. package/dist/providers/daytona-command.constants.js +4 -1
  547. package/dist/providers/daytona-command.constants.js.map +1 -1
  548. package/dist/providers/daytona-command.js +39 -16
  549. package/dist/providers/daytona-command.js.map +1 -1
  550. package/dist/providers/daytona-files.js +5 -5
  551. package/dist/providers/daytona-files.js.map +1 -1
  552. package/dist/providers/daytona-output.js +11 -3
  553. package/dist/providers/daytona-output.js.map +1 -1
  554. package/dist/providers/daytona.js +11 -9
  555. package/dist/providers/daytona.js.map +1 -1
  556. package/dist/providers/egress/address.d.ts +3 -0
  557. package/dist/providers/egress/address.js +26 -0
  558. package/dist/providers/egress/address.js.map +1 -0
  559. package/dist/providers/egress/egress.constants.d.ts +22 -0
  560. package/dist/providers/egress/egress.constants.js +23 -0
  561. package/dist/providers/egress/egress.constants.js.map +1 -0
  562. package/dist/providers/egress/egress.types.d.ts +11 -0
  563. package/dist/providers/egress/egress.types.js +2 -0
  564. package/dist/providers/egress/egress.types.js.map +1 -0
  565. package/dist/providers/egress/firewall.d.ts +3 -0
  566. package/dist/providers/egress/firewall.js +29 -0
  567. package/dist/providers/egress/firewall.js.map +1 -0
  568. package/dist/providers/egress/main.d.ts +1 -0
  569. package/dist/providers/egress/main.js +90 -0
  570. package/dist/providers/egress/main.js.map +1 -0
  571. package/dist/providers/egress/policy.d.ts +5 -0
  572. package/dist/providers/egress/policy.js +54 -0
  573. package/dist/providers/egress/policy.js.map +1 -0
  574. package/dist/providers/egress/proxy.d.ts +2 -0
  575. package/dist/providers/egress/proxy.js +129 -0
  576. package/dist/providers/egress/proxy.js.map +1 -0
  577. package/dist/providers/egress/tls-hello.d.ts +1 -0
  578. package/dist/providers/egress/tls-hello.js +75 -0
  579. package/dist/providers/egress/tls-hello.js.map +1 -0
  580. package/dist/providers/firecracker-command.js +12 -6
  581. package/dist/providers/firecracker-command.js.map +1 -1
  582. package/dist/providers/local-files.d.ts +2 -0
  583. package/dist/providers/local-files.js +24 -0
  584. package/dist/providers/local-files.js.map +1 -0
  585. package/dist/providers/local.js +2 -20
  586. package/dist/providers/local.js.map +1 -1
  587. package/dist/providers/vercel-command.js +33 -9
  588. package/dist/providers/vercel-command.js.map +1 -1
  589. package/dist/providers/vercel.js +11 -9
  590. package/dist/providers/vercel.js.map +1 -1
  591. package/dist/testing.d.ts +1 -1
  592. package/dist/testing.js +1 -1
  593. package/dist/testing.js.map +1 -1
  594. package/package.json +2 -2
  595. package/recipe-configuration.schema.json +2164 -35
  596. package/recipe.schema.json +2512 -404
@@ -0,0 +1,3 @@
1
+ import type { Allowlist } from "./egress.types.ts";
2
+ export declare function egressFirewall(policy: Allowlist): string;
3
+ export declare function egressHeartbeat(): string;
@@ -0,0 +1,29 @@
1
+ import { isIP } from "node:net";
2
+ import { egressRuntime } from "./egress.constants.js";
3
+ import { egressCidr } from "./address.js";
4
+ export function egressFirewall(policy) {
5
+ const destination = (value) => {
6
+ const cidr = egressCidr(value);
7
+ return `${isIP(cidr.split("/")[0]) === 4 ? "ip" : "ip6"} daddr ${cidr}`;
8
+ };
9
+ return `table inet outpost_egress {
10
+ set live { type nf_proto; flags timeout; timeout 3s; }
11
+ chain permitted {
12
+ ${(policy.denyCidrs ?? []).map((cidr) => `${destination(cidr)} drop`).join("\n ")}
13
+ meta skuid ${egressRuntime.uid} meta l4proto { tcp, udp } accept
14
+ ${(policy.allowCidrs ?? []).map((cidr) => `${destination(cidr)} meta l4proto { tcp, udp } accept`).join("\n ")}
15
+ }
16
+ chain output {
17
+ type filter hook output priority -150; policy drop;
18
+ oifname "lo" ip daddr 127.0.0.1 accept
19
+ oifname "lo" ip6 daddr ::1 accept
20
+ ip6 hoplimit 255 icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit } accept
21
+ meta nfproto @live jump permitted
22
+ }
23
+ chain forward { type filter hook forward priority 0; policy drop; }
24
+ }`;
25
+ }
26
+ export function egressHeartbeat() {
27
+ return "flush set inet outpost_egress live\nadd element inet outpost_egress live { ipv4 timeout 3s, ipv6 timeout 3s }";
28
+ }
29
+ //# sourceMappingURL=firewall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firewall.js","sourceRoot":"","sources":["../../../src/providers/egress/firewall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,UAAU,cAAc,CAAC,MAAiB;IAC9C,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC/B,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,IAAI,EAAE,CAAC;IAC3E,CAAC,CAAC;IACF,OAAO;;;IAGL,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;eACrE,aAAa,CAAC,GAAG;IAC5B,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;EAU/G,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,+GAA+G,CAAC;AACzH,CAAC","sourcesContent":["import { isIP } from \"node:net\";\nimport { egressRuntime } from \"./egress.constants.ts\";\nimport type { Allowlist } from \"./egress.types.ts\";\nimport { egressCidr } from \"./address.ts\";\n\nexport function egressFirewall(policy: Allowlist): string {\n const destination = (value: string) => {\n const cidr = egressCidr(value);\n return `${isIP(cidr.split(\"/\")[0]!) === 4 ? \"ip\" : \"ip6\"} daddr ${cidr}`;\n };\n return `table inet outpost_egress {\n set live { type nf_proto; flags timeout; timeout 3s; }\n chain permitted {\n ${(policy.denyCidrs ?? []).map((cidr) => `${destination(cidr)} drop`).join(\"\\n \")}\n meta skuid ${egressRuntime.uid} meta l4proto { tcp, udp } accept\n ${(policy.allowCidrs ?? []).map((cidr) => `${destination(cidr)} meta l4proto { tcp, udp } accept`).join(\"\\n \")}\n }\n chain output {\n type filter hook output priority -150; policy drop;\n oifname \"lo\" ip daddr 127.0.0.1 accept\n oifname \"lo\" ip6 daddr ::1 accept\n ip6 hoplimit 255 icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit } accept\n meta nfproto @live jump permitted\n }\n chain forward { type filter hook forward priority 0; policy drop; }\n}`;\n}\n\nexport function egressHeartbeat(): string {\n return \"flush set inet outpost_egress live\\nadd element inet outpost_egress live { ipv4 timeout 3s, ipv6 timeout 3s }\";\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,90 @@
1
+ import { spawn, execFileSync } from "node:child_process";
2
+ import { writeFileSync, readFileSync, rmSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import { validateEgress } from "../../domain/egress.js";
5
+ import { egressRuntime } from "./egress.constants.js";
6
+ import { egressFirewall, egressHeartbeat } from "./firewall.js";
7
+ import { createEgressProxy } from "./proxy.js";
8
+ function run() {
9
+ if (process.argv[2] === "health") {
10
+ if (process.argv[3] !== String(egressRuntime.version) ||
11
+ readFileSync(egressRuntime.ready, "utf8") !==
12
+ String(egressRuntime.version))
13
+ throw new Error("Egress not ready");
14
+ return;
15
+ }
16
+ const policy = validateEgress(JSON.parse(process.env.OUTPOST_EGRESS_POLICY ?? "null"));
17
+ if (policy?.mode !== "allowlist")
18
+ throw new Error("Allowlist required");
19
+ if (process.argv[2] === "proxy") {
20
+ process.setgroups([]);
21
+ process.setgid(egressRuntime.uid);
22
+ process.setuid(egressRuntime.uid);
23
+ const server = createEgressProxy(policy);
24
+ server.listen(egressRuntime.port, "127.0.0.1", () => process.send?.("ready"));
25
+ return;
26
+ }
27
+ const nft = (input) => execFileSync("nft", ["-f", "-"], {
28
+ input,
29
+ timeout: 2_000,
30
+ stdio: ["pipe", "ignore", "pipe"],
31
+ });
32
+ nft(egressFirewall(policy));
33
+ const child = spawn(process.execPath, [fileURLToPath(import.meta.url), "proxy"], { stdio: ["ignore", "inherit", "inherit", "ipc"] });
34
+ let heartbeat;
35
+ const stop = () => {
36
+ clearInterval(heartbeat);
37
+ rmSync(egressRuntime.ready, { force: true });
38
+ try {
39
+ nft("flush set inet outpost_egress live");
40
+ }
41
+ finally {
42
+ child.kill("SIGKILL");
43
+ }
44
+ };
45
+ child.once("message", (message) => {
46
+ if (message !== "ready") {
47
+ stop();
48
+ return;
49
+ }
50
+ const refresh = () => {
51
+ try {
52
+ nft(egressHeartbeat());
53
+ }
54
+ catch {
55
+ stop();
56
+ process.exitCode = 1;
57
+ }
58
+ };
59
+ try {
60
+ nft(egressHeartbeat());
61
+ }
62
+ catch {
63
+ stop();
64
+ process.exitCode = 1;
65
+ return;
66
+ }
67
+ writeFileSync(egressRuntime.ready, String(egressRuntime.version), {
68
+ mode: 0o600,
69
+ });
70
+ heartbeat = setInterval(refresh, egressRuntime.heartbeatMs);
71
+ });
72
+ child.once("exit", () => {
73
+ stop();
74
+ process.exitCode = 1;
75
+ });
76
+ child.once("error", () => {
77
+ stop();
78
+ process.exitCode = 1;
79
+ });
80
+ process.once("SIGTERM", stop);
81
+ process.once("SIGINT", stop);
82
+ }
83
+ try {
84
+ run();
85
+ }
86
+ catch {
87
+ process.stderr.write("Outpost egress startup or health check failed\n");
88
+ process.exitCode = 1;
89
+ }
90
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/providers/egress/main.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, execFileSync } from \"node:child_process\";\nimport { writeFileSync, readFileSync, rmSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { validateEgress } from \"../../domain/egress.ts\";\nimport { egressRuntime } from \"./egress.constants.ts\";\nimport { egressFirewall, egressHeartbeat } from \"./firewall.ts\";\nimport { createEgressProxy } from \"./proxy.ts\";\n\nfunction run(): void {\n if (process.argv[2] === \"health\") {\n if (\n process.argv[3] !== String(egressRuntime.version) ||\n readFileSync(egressRuntime.ready, \"utf8\") !==\n String(egressRuntime.version)\n )\n throw new Error(\"Egress not ready\");\n return;\n }\n const policy = validateEgress(\n JSON.parse(process.env.OUTPOST_EGRESS_POLICY ?? \"null\"),\n );\n if (policy?.mode !== \"allowlist\") throw new Error(\"Allowlist required\");\n if (process.argv[2] === \"proxy\") {\n process.setgroups!([]);\n process.setgid!(egressRuntime.uid);\n process.setuid!(egressRuntime.uid);\n const server = createEgressProxy(policy);\n server.listen(egressRuntime.port, \"127.0.0.1\", () =>\n process.send?.(\"ready\"),\n );\n return;\n }\n const nft = (input: string) =>\n execFileSync(\"nft\", [\"-f\", \"-\"], {\n input,\n timeout: 2_000,\n stdio: [\"pipe\", \"ignore\", \"pipe\"],\n });\n nft(egressFirewall(policy));\n const child = spawn(\n process.execPath,\n [fileURLToPath(import.meta.url), \"proxy\"],\n { stdio: [\"ignore\", \"inherit\", \"inherit\", \"ipc\"] },\n );\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n const stop = () => {\n clearInterval(heartbeat);\n rmSync(egressRuntime.ready, { force: true });\n try {\n nft(\"flush set inet outpost_egress live\");\n } finally {\n child.kill(\"SIGKILL\");\n }\n };\n child.once(\"message\", (message: unknown) => {\n if (message !== \"ready\") {\n stop();\n return;\n }\n const refresh = () => {\n try {\n nft(egressHeartbeat());\n } catch {\n stop();\n process.exitCode = 1;\n }\n };\n try {\n nft(egressHeartbeat());\n } catch {\n stop();\n process.exitCode = 1;\n return;\n }\n writeFileSync(egressRuntime.ready, String(egressRuntime.version), {\n mode: 0o600,\n });\n heartbeat = setInterval(refresh, egressRuntime.heartbeatMs);\n });\n child.once(\"exit\", () => {\n stop();\n process.exitCode = 1;\n });\n child.once(\"error\", () => {\n stop();\n process.exitCode = 1;\n });\n process.once(\"SIGTERM\", stop);\n process.once(\"SIGINT\", stop);\n}\n\ntry {\n run();\n} catch {\n process.stderr.write(\"Outpost egress startup or health check failed\\n\");\n process.exitCode = 1;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { Allowlist, EgressAddress, EgressResolver } from "./egress.types.ts";
2
+ export declare function egressAccess(policy: Allowlist): {
3
+ domain: (hostname: string) => boolean;
4
+ resolve(hostname: string, resolver?: EgressResolver): Promise<EgressAddress>;
5
+ };
@@ -0,0 +1,54 @@
1
+ import { lookup } from "node:dns/promises";
2
+ import { BlockList, isIP } from "node:net";
3
+ import { egressAddress, egressCidr } from "./address.js";
4
+ function subnets(cidrs) {
5
+ const result = { ipv4: new BlockList(), ipv6: new BlockList() };
6
+ for (const cidr of cidrs.map(egressCidr)) {
7
+ const [address, prefix] = cidr.split("/");
8
+ const family = isIP(address) === 4 ? "ipv4" : "ipv6";
9
+ result[family].addSubnet(address, Number(prefix), isIP(address) === 4 ? "ipv4" : "ipv6");
10
+ }
11
+ return result;
12
+ }
13
+ export function egressAccess(policy) {
14
+ const denied = subnets(policy.denyCidrs ?? []);
15
+ const allowed = subnets(policy.allowCidrs ?? []);
16
+ const local = subnets(["127.0.0.0/8", "::1/128", "0.0.0.0/8", "::/128"]);
17
+ const domains = (policy.domains ?? []).map((name) => name.toLowerCase());
18
+ const matches = (list, address) => {
19
+ const family = address.family === 4 ? "ipv4" : "ipv6";
20
+ return list[family].check(address.address, family);
21
+ };
22
+ const domain = (hostname) => {
23
+ if (hostname.length > 253 ||
24
+ !hostname
25
+ .split(".")
26
+ .every((label) => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i.test(label)))
27
+ return false;
28
+ return domains.some((pattern) => pattern.startsWith("*.")
29
+ ? hostname.toLowerCase().endsWith(pattern.slice(1))
30
+ : hostname.toLowerCase() === pattern);
31
+ };
32
+ return {
33
+ domain,
34
+ async resolve(hostname, resolver = (name) => lookup(name, { all: true })) {
35
+ const version = isIP(hostname);
36
+ if (!version && !domain(hostname))
37
+ throw new Error("Egress destination denied");
38
+ const addresses = version
39
+ ? [{ address: hostname, family: version }]
40
+ : await resolver(hostname);
41
+ const selected = addresses
42
+ .filter((item) => isIP(item.address) === item.family)
43
+ .map((item) => egressAddress(item.address))
44
+ .find((address) => isIP(address.address) === address.family &&
45
+ !matches(local, address) &&
46
+ !matches(denied, address) &&
47
+ (domain(hostname) || matches(allowed, address)));
48
+ if (!selected)
49
+ throw new Error("Egress destination denied");
50
+ return selected;
51
+ },
52
+ };
53
+ }
54
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/providers/egress/policy.ts"],"names":[],"mappings":"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","sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { BlockList, isIP } from \"node:net\";\nimport { egressAddress, egressCidr } from \"./address.ts\";\nimport type {\n Allowlist,\n EgressAddress,\n EgressResolver,\n} from \"./egress.types.ts\";\n\nfunction subnets(cidrs: readonly string[]) {\n const result = { ipv4: new BlockList(), ipv6: new BlockList() };\n for (const cidr of cidrs.map(egressCidr)) {\n const [address, prefix] = cidr.split(\"/\");\n const family = isIP(address!) === 4 ? \"ipv4\" : \"ipv6\";\n result[family].addSubnet(\n address!,\n Number(prefix),\n isIP(address!) === 4 ? \"ipv4\" : \"ipv6\",\n );\n }\n return result;\n}\n\nexport function egressAccess(policy: Allowlist) {\n const denied = subnets(policy.denyCidrs ?? []);\n const allowed = subnets(policy.allowCidrs ?? []);\n const local = subnets([\"127.0.0.0/8\", \"::1/128\", \"0.0.0.0/8\", \"::/128\"]);\n const domains = (policy.domains ?? []).map((name) => name.toLowerCase());\n const matches = (\n list: ReturnType<typeof subnets>,\n address: EgressAddress,\n ) => {\n const family = address.family === 4 ? \"ipv4\" : \"ipv6\";\n return list[family].check(address.address, family);\n };\n const domain = (hostname: string) => {\n if (\n hostname.length > 253 ||\n !hostname\n .split(\".\")\n .every((label) => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i.test(label))\n )\n return false;\n return domains.some((pattern) =>\n pattern.startsWith(\"*.\")\n ? hostname.toLowerCase().endsWith(pattern.slice(1))\n : hostname.toLowerCase() === pattern,\n );\n };\n return {\n domain,\n async resolve(\n hostname: string,\n resolver: EgressResolver = (name) => lookup(name, { all: true }),\n ): Promise<EgressAddress> {\n const version = isIP(hostname);\n if (!version && !domain(hostname))\n throw new Error(\"Egress destination denied\");\n const addresses = version\n ? [{ address: hostname, family: version }]\n : await resolver(hostname);\n const selected = addresses\n .filter((item) => isIP(item.address) === item.family)\n .map((item) => egressAddress(item.address))\n .find(\n (address) =>\n isIP(address.address) === address.family &&\n !matches(local, address) &&\n !matches(denied, address) &&\n (domain(hostname) || matches(allowed, address)),\n );\n if (!selected) throw new Error(\"Egress destination denied\");\n return selected;\n },\n };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { Allowlist, EgressResolver } from "./egress.types.ts";
2
+ export declare function createEgressProxy(policy: Allowlist, resolver?: EgressResolver): import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
@@ -0,0 +1,129 @@
1
+ import { createServer, request } from "node:http";
2
+ import { connect, isIP } from "node:net";
3
+ import { egressRuntime } from "./egress.constants.js";
4
+ import { egressAccess } from "./policy.js";
5
+ import { tlsServerName } from "./tls-hello.js";
6
+ function reject(socket) {
7
+ socket.end("HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
8
+ }
9
+ export function createEgressProxy(policy, resolver) {
10
+ const access = egressAccess(policy);
11
+ const server = createServer({ maxHeaderSize: egressRuntime.maxHeaderBytes }, (incoming, outgoing) => {
12
+ void (async () => {
13
+ const target = new URL(incoming.url ?? "");
14
+ if (target.protocol !== "http:" ||
15
+ (target.port && target.port !== "80") ||
16
+ target.username ||
17
+ target.password)
18
+ throw new Error("HTTP destination required");
19
+ const hostname = target.hostname.replace(/^\[|\]$/g, "");
20
+ const address = await access.resolve(hostname, resolver);
21
+ if (incoming.destroyed || outgoing.destroyed)
22
+ return;
23
+ const headers = {
24
+ ...incoming.headers,
25
+ host: target.host,
26
+ connection: "close",
27
+ };
28
+ for (const key of [
29
+ "proxy-authorization",
30
+ "proxy-connection",
31
+ "upgrade",
32
+ ...(incoming.headers.connection ?? "")
33
+ .toLowerCase()
34
+ .split(",")
35
+ .map((value) => value.trim()),
36
+ ])
37
+ delete headers[key];
38
+ headers.host = target.host;
39
+ headers.connection = "close";
40
+ const upstream = request({
41
+ hostname: address.address,
42
+ family: address.family,
43
+ port: 80,
44
+ method: incoming.method,
45
+ path: target.pathname + target.search,
46
+ headers,
47
+ agent: false,
48
+ }, (response) => {
49
+ outgoing.writeHead(response.statusCode ?? 502, response.headers);
50
+ response.pipe(outgoing);
51
+ response.on("error", () => outgoing.destroy());
52
+ });
53
+ upstream.setTimeout(egressRuntime.idleMs, () => upstream.destroy());
54
+ upstream.on("error", () => {
55
+ if (outgoing.headersSent) {
56
+ outgoing.destroy();
57
+ return;
58
+ }
59
+ outgoing.writeHead(502).end();
60
+ });
61
+ incoming.on("aborted", () => upstream.destroy());
62
+ outgoing.on("close", () => upstream.destroy());
63
+ incoming.pipe(upstream);
64
+ })().catch(() => outgoing.writeHead(403).end());
65
+ });
66
+ server.on("connect", (request, socket, head) => {
67
+ socket.on("error", () => socket.destroy());
68
+ void (async () => {
69
+ const authority = request.url ?? "";
70
+ const target = new URL(`https://${authority}`);
71
+ if (target.username ||
72
+ target.password ||
73
+ target.pathname !== "/" ||
74
+ target.search ||
75
+ target.hash ||
76
+ !authority.endsWith(":443"))
77
+ throw new Error("HTTPS destination required");
78
+ const hostname = target.hostname.replace(/^\[|\]$/g, "");
79
+ const address = await access.resolve(hostname, resolver);
80
+ if (socket.destroyed)
81
+ return;
82
+ socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
83
+ let buffered = head;
84
+ const timeout = setTimeout(() => socket.destroy(), egressRuntime.connectMs);
85
+ socket.once("close", () => clearTimeout(timeout));
86
+ const receive = (chunk) => {
87
+ try {
88
+ buffered = Buffer.concat([buffered, chunk]);
89
+ if (!isIP(hostname)) {
90
+ const name = tlsServerName(buffered);
91
+ if (name === undefined)
92
+ return;
93
+ if (name !== hostname.toLowerCase())
94
+ throw new Error("TLS server name mismatch");
95
+ }
96
+ socket.pause();
97
+ socket.removeListener("data", receive);
98
+ const upstream = connect({
99
+ host: address.address,
100
+ family: address.family,
101
+ port: 443,
102
+ });
103
+ upstream.on("error", () => socket.destroy());
104
+ socket.once("close", () => upstream.destroy());
105
+ upstream.once("close", () => socket.end());
106
+ upstream.setTimeout(egressRuntime.idleMs, () => upstream.destroy());
107
+ upstream.once("connect", () => {
108
+ clearTimeout(timeout);
109
+ upstream.write(buffered);
110
+ socket.pipe(upstream).pipe(socket);
111
+ socket.resume();
112
+ });
113
+ }
114
+ catch {
115
+ socket.destroy();
116
+ }
117
+ };
118
+ socket.on("data", receive);
119
+ receive(Buffer.alloc(0));
120
+ })().catch(() => reject(socket));
121
+ });
122
+ server.on("upgrade", (_request, socket) => reject(socket));
123
+ server.on("clientError", (_error, socket) => reject(socket));
124
+ server.requestTimeout = egressRuntime.idleMs;
125
+ server.headersTimeout = egressRuntime.connectMs;
126
+ server.maxConnections = egressRuntime.maxConnections;
127
+ return server;
128
+ }
129
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../../src/providers/egress/proxy.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, request } from \"node:http\";\nimport { connect, isIP } from \"node:net\";\nimport type { Duplex } from \"node:stream\";\nimport { egressRuntime } from \"./egress.constants.ts\";\nimport type { Allowlist, EgressResolver } from \"./egress.types.ts\";\nimport { egressAccess } from \"./policy.ts\";\nimport { tlsServerName } from \"./tls-hello.ts\";\n\nfunction reject(socket: Duplex): void {\n socket.end(\n \"HTTP/1.1 403 Forbidden\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n\",\n );\n}\n\nexport function createEgressProxy(\n policy: Allowlist,\n resolver?: EgressResolver,\n) {\n const access = egressAccess(policy);\n const server = createServer(\n { maxHeaderSize: egressRuntime.maxHeaderBytes },\n (incoming, outgoing) => {\n void (async () => {\n const target = new URL(incoming.url ?? \"\");\n if (\n target.protocol !== \"http:\" ||\n (target.port && target.port !== \"80\") ||\n target.username ||\n target.password\n )\n throw new Error(\"HTTP destination required\");\n const hostname = target.hostname.replace(/^\\[|\\]$/g, \"\");\n const address = await access.resolve(hostname, resolver);\n if (incoming.destroyed || outgoing.destroyed) return;\n const headers = {\n ...incoming.headers,\n host: target.host,\n connection: \"close\",\n };\n for (const key of [\n \"proxy-authorization\",\n \"proxy-connection\",\n \"upgrade\",\n ...(incoming.headers.connection ?? \"\")\n .toLowerCase()\n .split(\",\")\n .map((value) => value.trim()),\n ])\n delete headers[key as keyof typeof headers];\n headers.host = target.host;\n headers.connection = \"close\";\n const upstream = request(\n {\n hostname: address.address,\n family: address.family,\n port: 80,\n method: incoming.method,\n path: target.pathname + target.search,\n headers,\n agent: false,\n },\n (response) => {\n outgoing.writeHead(response.statusCode ?? 502, response.headers);\n response.pipe(outgoing);\n response.on(\"error\", () => outgoing.destroy());\n },\n );\n upstream.setTimeout(egressRuntime.idleMs, () => upstream.destroy());\n upstream.on(\"error\", () => {\n if (outgoing.headersSent) {\n outgoing.destroy();\n return;\n }\n outgoing.writeHead(502).end();\n });\n incoming.on(\"aborted\", () => upstream.destroy());\n outgoing.on(\"close\", () => upstream.destroy());\n incoming.pipe(upstream);\n })().catch(() => outgoing.writeHead(403).end());\n },\n );\n server.on(\"connect\", (request, socket, head) => {\n socket.on(\"error\", () => socket.destroy());\n void (async () => {\n const authority = request.url ?? \"\";\n const target = new URL(`https://${authority}`);\n if (\n target.username ||\n target.password ||\n target.pathname !== \"/\" ||\n target.search ||\n target.hash ||\n !authority.endsWith(\":443\")\n )\n throw new Error(\"HTTPS destination required\");\n const hostname = target.hostname.replace(/^\\[|\\]$/g, \"\");\n const address = await access.resolve(hostname, resolver);\n if (socket.destroyed) return;\n socket.write(\"HTTP/1.1 200 Connection Established\\r\\n\\r\\n\");\n let buffered = head;\n const timeout = setTimeout(\n () => socket.destroy(),\n egressRuntime.connectMs,\n );\n socket.once(\"close\", () => clearTimeout(timeout));\n const receive = (chunk: Buffer) => {\n try {\n buffered = Buffer.concat([buffered, chunk]);\n if (!isIP(hostname)) {\n const name = tlsServerName(buffered);\n if (name === undefined) return;\n if (name !== hostname.toLowerCase())\n throw new Error(\"TLS server name mismatch\");\n }\n socket.pause();\n socket.removeListener(\"data\", receive);\n const upstream = connect({\n host: address.address,\n family: address.family,\n port: 443,\n });\n upstream.on(\"error\", () => socket.destroy());\n socket.once(\"close\", () => upstream.destroy());\n upstream.once(\"close\", () => socket.end());\n upstream.setTimeout(egressRuntime.idleMs, () => upstream.destroy());\n upstream.once(\"connect\", () => {\n clearTimeout(timeout);\n upstream.write(buffered);\n socket.pipe(upstream).pipe(socket);\n socket.resume();\n });\n } catch {\n socket.destroy();\n }\n };\n socket.on(\"data\", receive);\n receive(Buffer.alloc(0));\n })().catch(() => reject(socket));\n });\n server.on(\"upgrade\", (_request, socket) => reject(socket));\n server.on(\"clientError\", (_error, socket) => reject(socket));\n server.requestTimeout = egressRuntime.idleMs;\n server.headersTimeout = egressRuntime.connectMs;\n server.maxConnections = egressRuntime.maxConnections;\n return server;\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function tlsServerName(input: Buffer): string | undefined;
@@ -0,0 +1,75 @@
1
+ import { egressRuntime } from "./egress.constants.js";
2
+ export function tlsServerName(input) {
3
+ if (input.length > egressRuntime.maxHelloBytes)
4
+ throw new Error("TLS hello too large");
5
+ const fragments = [];
6
+ let offset = 0;
7
+ while (offset < input.length) {
8
+ if (input.length - offset < 5)
9
+ return undefined;
10
+ if (input[offset] !== 22 || input[offset + 1] !== 3)
11
+ throw new Error("TLS ClientHello required");
12
+ const end = offset + 5 + input.readUInt16BE(offset + 3);
13
+ if (end > input.length)
14
+ return undefined;
15
+ fragments.push(input.subarray(offset + 5, end));
16
+ if (fragments.length > egressRuntime.maxHelloRecords)
17
+ throw new Error("Too many TLS hello records");
18
+ offset = end;
19
+ const hello = Buffer.concat(fragments);
20
+ if (hello.length < 4)
21
+ continue;
22
+ if (hello[0] !== 1)
23
+ throw new Error("TLS ClientHello required");
24
+ const length = hello.readUIntBE(1, 3) + 4;
25
+ if (length > egressRuntime.maxHelloBytes)
26
+ throw new Error("TLS hello too large");
27
+ if (hello.length < length)
28
+ continue;
29
+ const data = hello.subarray(0, length);
30
+ let cursor = 38;
31
+ const take = (count) => {
32
+ if (count < 0 || cursor + count > data.length)
33
+ throw new Error("Malformed TLS hello");
34
+ const part = data.subarray(cursor, cursor + count);
35
+ cursor += count;
36
+ return part;
37
+ };
38
+ take(take(1)[0]);
39
+ take(take(2).readUInt16BE());
40
+ take(take(1)[0]);
41
+ const extensions = take(take(2).readUInt16BE());
42
+ if (cursor !== data.length)
43
+ throw new Error("Malformed TLS extensions");
44
+ let position = 0;
45
+ let name;
46
+ while (position < extensions.length) {
47
+ if (position + 4 > extensions.length)
48
+ throw new Error("Malformed TLS extension");
49
+ const kind = extensions.readUInt16BE(position);
50
+ const size = extensions.readUInt16BE(position + 2);
51
+ position += 4;
52
+ if (position + size > extensions.length)
53
+ throw new Error("Malformed TLS extension");
54
+ const extension = extensions.subarray(position, position + size);
55
+ position += size;
56
+ if (kind !== 0)
57
+ continue;
58
+ if (name !== undefined)
59
+ throw new Error("Duplicate TLS server name");
60
+ if (size < 5 ||
61
+ extension.readUInt16BE(0) !== size - 2 ||
62
+ extension[2] !== 0 ||
63
+ extension.readUInt16BE(3) !== size - 5)
64
+ throw new Error("Malformed TLS server name");
65
+ name = extension.subarray(5).toString("utf8");
66
+ if (!/^[a-z0-9.-]+$/i.test(name))
67
+ throw new Error("Invalid TLS server name");
68
+ }
69
+ if (name !== undefined)
70
+ return name.toLowerCase();
71
+ throw new Error("TLS server name required");
72
+ }
73
+ return undefined;
74
+ }
75
+ //# sourceMappingURL=tls-hello.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tls-hello.js","sourceRoot":"","sources":["../../../src/providers/egress/tls-hello.ts"],"names":[],"mappings":"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","sourcesContent":["import { egressRuntime } from \"./egress.constants.ts\";\n\nexport function tlsServerName(input: Buffer): string | undefined {\n if (input.length > egressRuntime.maxHelloBytes)\n throw new Error(\"TLS hello too large\");\n const fragments: Buffer[] = [];\n let offset = 0;\n while (offset < input.length) {\n if (input.length - offset < 5) return undefined;\n if (input[offset] !== 22 || input[offset + 1] !== 3)\n throw new Error(\"TLS ClientHello required\");\n const end = offset + 5 + input.readUInt16BE(offset + 3);\n if (end > input.length) return undefined;\n fragments.push(input.subarray(offset + 5, end));\n if (fragments.length > egressRuntime.maxHelloRecords)\n throw new Error(\"Too many TLS hello records\");\n offset = end;\n const hello = Buffer.concat(fragments);\n if (hello.length < 4) continue;\n if (hello[0] !== 1) throw new Error(\"TLS ClientHello required\");\n const length = hello.readUIntBE(1, 3) + 4;\n if (length > egressRuntime.maxHelloBytes)\n throw new Error(\"TLS hello too large\");\n if (hello.length < length) continue;\n const data = hello.subarray(0, length);\n let cursor = 38;\n const take = (count: number) => {\n if (count < 0 || cursor + count > data.length)\n throw new Error(\"Malformed TLS hello\");\n const part = data.subarray(cursor, cursor + count);\n cursor += count;\n return part;\n };\n take(take(1)[0]!);\n take(take(2).readUInt16BE());\n take(take(1)[0]!);\n const extensions = take(take(2).readUInt16BE());\n if (cursor !== data.length) throw new Error(\"Malformed TLS extensions\");\n let position = 0;\n let name: string | undefined;\n while (position < extensions.length) {\n if (position + 4 > extensions.length)\n throw new Error(\"Malformed TLS extension\");\n const kind = extensions.readUInt16BE(position);\n const size = extensions.readUInt16BE(position + 2);\n position += 4;\n if (position + size > extensions.length)\n throw new Error(\"Malformed TLS extension\");\n const extension = extensions.subarray(position, position + size);\n position += size;\n if (kind !== 0) continue;\n if (name !== undefined) throw new Error(\"Duplicate TLS server name\");\n if (\n size < 5 ||\n extension.readUInt16BE(0) !== size - 2 ||\n extension[2] !== 0 ||\n extension.readUInt16BE(3) !== size - 5\n )\n throw new Error(\"Malformed TLS server name\");\n name = extension.subarray(5).toString(\"utf8\");\n if (!/^[a-z0-9.-]+$/i.test(name))\n throw new Error(\"Invalid TLS server name\");\n }\n if (name !== undefined) return name.toLowerCase();\n throw new Error(\"TLS server name required\");\n }\n return undefined;\n}\n"]}
@@ -59,14 +59,13 @@ export function firecrackerCommand(runtime) {
59
59
  let cancellation;
60
60
  const cancel = () => {
61
61
  cancellation ??= requireSuccess({
62
- ...firecrackerSsh(runtime.options, `umask 077; touch ${quote(id + ".cancel")}; if [ -f ${quote(id)} ]; then node -e ${quote("const fs=require('node:fs');const pid=Number(fs.readFileSync(process.argv[1],'utf8'));if(!Number.isSafeInteger(pid)||pid<2)process.exit(1);const kill=s=>{try{process.kill(-pid,s)}catch(e){if(e.code!=='ESRCH')throw e}};kill('SIGTERM');setTimeout(()=>kill('SIGKILL'),100)")} ${quote(id)}; fi`),
62
+ ...firecrackerSsh(runtime.options, `umask 077; touch ${quote(id + ".cancel")}; if [ -f ${quote(id)} ]; then node -e ${quote("const fs=require('node:fs');const pid=Number(fs.readFileSync(process.argv[1],'utf8'));if(!Number.isSafeInteger(pid)||pid<2)process.exit(1);const kill=s=>{try{process.kill(-pid,s)}catch(e){if(e.code!=='ESRCH')throw e}};kill('SIGTERM');setTimeout(()=>kill('SIGKILL'),500)")} ${quote(id)}; fi`),
63
63
  deadlineMs: firecrackerDefaults.cleanupMs,
64
64
  }, executeProcess);
65
65
  void cancellation.catch(() => undefined);
66
66
  };
67
67
  signal.addEventListener("abort", cancel, { once: true });
68
68
  const timer = setTimeout(() => stop.abort(new OutpostError("timeout", `Command exceeded ${deadlineMs} ms`)), deadlineMs);
69
- let interrupted = false;
70
69
  try {
71
70
  const { directory: _directory, variables: _variables, ...invocation } = command;
72
71
  const result = await runtime.executor({
@@ -84,13 +83,20 @@ export function firecrackerCommand(runtime) {
84
83
  }
85
84
  : {}),
86
85
  signal,
87
- ...firecrackerSsh(runtime.options, `setsid --wait sh -c ${quote(wrapper)}`),
86
+ ...firecrackerSsh(runtime.options, `setsid --wait sh -c ${quote(wrapper)}; status=$?; printf '%s' "$status" > ${quote(id + ".status")}; exit "$status"`),
88
87
  });
89
88
  signal.throwIfAborted();
89
+ if (result.status === 255) {
90
+ const status = await executeProcess({
91
+ ...firecrackerSsh(runtime.options, `cat ${quote(id + ".status")}`),
92
+ deadlineMs: firecrackerDefaults.cleanupMs,
93
+ });
94
+ if (status.status !== 0 || status.stdout.trim() !== "255")
95
+ throw new OutpostError("provider", "Guest SSH connection failed before command completion", { unavailable: "SSH connection failure" });
96
+ }
90
97
  return result;
91
98
  }
92
99
  catch (cause) {
93
- interrupted = true;
94
100
  try {
95
101
  cancel();
96
102
  await cancellation;
@@ -103,9 +109,9 @@ export function firecrackerCommand(runtime) {
103
109
  finally {
104
110
  clearTimeout(timer);
105
111
  signal.removeEventListener("abort", cancel);
106
- if (!interrupted && !runtime.isClosed())
112
+ if (!runtime.isClosed())
107
113
  await executeProcess({
108
- ...firecrackerSsh(runtime.options, `rm -f -- ${quote(id)}`),
114
+ ...firecrackerSsh(runtime.options, `rm -f -- ${quote(id)} ${quote(id + ".cancel")} ${quote(id + ".status")}`),
109
115
  deadlineMs: firecrackerDefaults.cleanupMs,
110
116
  }).catch(() => undefined);
111
117
  }
@@ -1 +1 @@
1
- {"version":3,"file":"firecracker-command.js","sourceRoot":"","sources":["../../src/providers/firecracker-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { invariant, OutpostError, positive } from \"../domain/errors.ts\";\nimport type { Command } from \"../domain/command.types.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport {\n executeProcess,\n quote,\n requireSuccess,\n} from \"../infrastructure/process.ts\";\nimport { firecrackerDefaults } from \"./firecracker.constants.ts\";\nimport type {\n FirecrackerOptions,\n FirecrackerRuntime,\n} from \"./firecracker.types.ts\";\n\nexport function firecrackerSsh(\n options: FirecrackerOptions,\n script: string,\n): Command {\n const ssh = options.ssh;\n return {\n executable: ssh.binary ?? (options.jailer ? \"/usr/bin/ssh\" : \"ssh\"),\n arguments: [\n \"-F\",\n \"/dev/null\",\n \"-T\",\n \"-o\",\n \"BatchMode=yes\",\n \"-o\",\n \"IdentitiesOnly=yes\",\n \"-o\",\n \"StrictHostKeyChecking=yes\",\n \"-o\",\n `UserKnownHostsFile=${ssh.knownHosts}`,\n \"-o\",\n \"GlobalKnownHostsFile=/dev/null\",\n \"-o\",\n \"ConnectTimeout=2\",\n \"-o\",\n \"ServerAliveInterval=2\",\n \"-o\",\n \"ServerAliveCountMax=2\",\n \"-i\",\n ssh.identity,\n \"-p\",\n String(ssh.port ?? 22),\n \"-l\",\n ssh.user,\n \"--\",\n ssh.host,\n script,\n ],\n };\n}\n\nexport function firecrackerCommand(\n runtime: FirecrackerRuntime,\n): SandboxLease[\"invoke\"] {\n return async (command) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Firecracker sandbox is closed\");\n invariant(\n !command.interactive && !command.elevated,\n \"Firecracker research provider does not support interactive or elevated commands\",\n );\n command.signal?.throwIfAborted();\n const variables = { ...runtime.variables, ...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 id = `/tmp/outpost-command-${randomUUID()}`;\n const wrapper = `umask 077; echo $$ > ${quote(id)}; test ! -e ${quote(id + \".cancel\")} || exit 130; cd ${quote(command.directory ?? runtime.root)} || exit; exec env ${Object.entries(\n variables,\n )\n .map(([key, value]) => quote(`${key}=${value}`))\n .join(\n \" \",\n )} ${[command.executable, ...(command.arguments ?? [])].map(quote).join(\" \")}`;\n const stop = new AbortController();\n const signal = command.signal\n ? AbortSignal.any([command.signal, stop.signal])\n : stop.signal;\n const deadlineMs = command.deadlineMs ?? firecrackerDefaults.commandMs;\n positive(deadlineMs, \"deadlineMs\");\n let cancellation: Promise<unknown> | undefined;\n const cancel = () => {\n cancellation ??= requireSuccess(\n {\n ...firecrackerSsh(\n runtime.options,\n `umask 077; touch ${quote(id + \".cancel\")}; if [ -f ${quote(id)} ]; then node -e ${quote(\"const fs=require('node:fs');const pid=Number(fs.readFileSync(process.argv[1],'utf8'));if(!Number.isSafeInteger(pid)||pid<2)process.exit(1);const kill=s=>{try{process.kill(-pid,s)}catch(e){if(e.code!=='ESRCH')throw e}};kill('SIGTERM');setTimeout(()=>kill('SIGKILL'),100)\")} ${quote(id)}; fi`,\n ),\n deadlineMs: firecrackerDefaults.cleanupMs,\n },\n executeProcess,\n );\n void cancellation.catch(() => undefined);\n };\n signal.addEventListener(\"abort\", cancel, { once: true });\n const timer = setTimeout(\n () =>\n stop.abort(\n new OutpostError(\"timeout\", `Command exceeded ${deadlineMs} ms`),\n ),\n deadlineMs,\n );\n let interrupted = false;\n try {\n const {\n directory: _directory,\n variables: _variables,\n ...invocation\n } = command;\n const result = await runtime.executor({\n ...invocation,\n ...(command.observe\n ? {\n observe: (channel, text) => {\n try {\n command.observe!(channel, text);\n } catch (cause) {\n stop.abort(cause);\n }\n },\n }\n : {}),\n signal,\n ...firecrackerSsh(\n runtime.options,\n `setsid --wait sh -c ${quote(wrapper)}`,\n ),\n });\n signal.throwIfAborted();\n return result;\n } catch (cause) {\n interrupted = true;\n try {\n cancel();\n await cancellation;\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Guest cancellation could not be confirmed; release the VM\",\n );\n }\n throw cause;\n } finally {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", cancel);\n if (!interrupted && !runtime.isClosed())\n await executeProcess({\n ...firecrackerSsh(runtime.options, `rm -f -- ${quote(id)}`),\n deadlineMs: firecrackerDefaults.cleanupMs,\n }).catch(() => undefined);\n }\n };\n}\n"]}
1
+ {"version":3,"file":"firecracker-command.js","sourceRoot":"","sources":["../../src/providers/firecracker-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGxE,OAAO,EACL,cAAc,EACd,KAAK,EACL,cAAc,GACf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAMjE,MAAM,UAAU,cAAc,CAC5B,OAA2B,EAC3B,MAAc;IAEd,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACxB,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC;QACnE,SAAS,EAAE;YACT,IAAI;YACJ,WAAW;YACX,IAAI;YACJ,IAAI;YACJ,eAAe;YACf,IAAI;YACJ,oBAAoB;YACpB,IAAI;YACJ,2BAA2B;YAC3B,IAAI;YACJ,sBAAsB,GAAG,CAAC,UAAU,EAAE;YACtC,IAAI;YACJ,gCAAgC;YAChC,IAAI;YACJ,kBAAkB;YAClB,IAAI;YACJ,uBAAuB;YACvB,IAAI;YACJ,uBAAuB;YACvB,IAAI;YACJ,GAAG,CAAC,QAAQ;YACZ,IAAI;YACJ,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;YACtB,IAAI;YACJ,GAAG,CAAC,IAAI;YACR,IAAI;YACJ,GAAG,CAAC,IAAI;YACR,MAAM;SACP;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,OAA2B;IAE3B,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,OAAO,CAAC,QAAQ,EAAE;YACpB,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,+BAA+B,CAAC,CAAC;QACtE,SAAS,CACP,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,QAAQ,EACzC,iFAAiF,CAClF,CAAC;QACF,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QACjE,SAAS,CACP,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACnC,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC,CACrC,EACD,yBAAyB,CAC1B,CAAC;QACF,MAAM,EAAE,GAAG,wBAAwB,UAAU,EAAE,EAAE,CAAC;QAClD,MAAM,OAAO,GAAG,wBAAwB,KAAK,CAAC,EAAE,CAAC,eAAe,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,oBAAoB,KAAK,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,sBAAsB,MAAM,CAAC,OAAO,CACnL,SAAS,CACV;aACE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC;aAC/C,IAAI,CACH,GAAG,CACJ,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACjF,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;YAC3B,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAChD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QAChB,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC,SAAS,CAAC;QACvE,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACnC,IAAI,YAA0C,CAAC;QAC/C,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,YAAY,KAAK,cAAc,CAC7B;gBACE,GAAG,cAAc,CACf,OAAO,CAAC,OAAO,EACf,oBAAoB,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,aAAa,KAAK,CAAC,EAAE,CAAC,oBAAoB,KAAK,CAAC,+QAA+Q,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,MAAM,CAC7X;gBACD,UAAU,EAAE,mBAAmB,CAAC,SAAS;aAC1C,EACD,cAAc,CACf,CAAC;YACF,KAAK,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC3C,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CACH,IAAI,CAAC,KAAK,CACR,IAAI,YAAY,CAAC,SAAS,EAAE,oBAAoB,UAAU,KAAK,CAAC,CACjE,EACH,UAAU,CACX,CAAC;QACF,IAAI,CAAC;YACH,MAAM,EACJ,SAAS,EAAE,UAAU,EACrB,SAAS,EAAE,UAAU,EACrB,GAAG,UAAU,EACd,GAAG,OAAO,CAAC;YACZ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC;gBACpC,GAAG,UAAU;gBACb,GAAG,CAAC,OAAO,CAAC,OAAO;oBACjB,CAAC,CAAC;wBACE,OAAO,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;4BACzB,IAAI,CAAC;gCACH,OAAO,CAAC,OAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;4BAClC,CAAC;4BAAC,OAAO,KAAK,EAAE,CAAC;gCACf,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;4BACpB,CAAC;wBACH,CAAC;qBACF;oBACH,CAAC,CAAC,EAAE,CAAC;gBACP,MAAM;gBACN,GAAG,cAAc,CACf,OAAO,CAAC,OAAO,EACf,uBAAuB,KAAK,CAAC,OAAO,CAAC,wCAAwC,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,kBAAkB,CACrH;aACF,CAAC,CAAC;YACH,MAAM,CAAC,cAAc,EAAE,CAAC;YACxB,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC1B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;oBAClC,GAAG,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC;oBAClE,UAAU,EAAE,mBAAmB,CAAC,SAAS;iBAC1C,CAAC,CAAC;gBACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,KAAK;oBACvD,MAAM,IAAI,YAAY,CACpB,UAAU,EACV,uDAAuD,EACvD,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAC1C,CAAC;YACN,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC;gBACT,MAAM,YAAY,CAAC;YACrB,CAAC;YAAC,OAAO,OAAO,EAAE,CAAC;gBACjB,MAAM,IAAI,cAAc,CACtB,CAAC,KAAK,EAAE,OAAO,CAAC,EAChB,2DAA2D,CAC5D,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;gBACrB,MAAM,cAAc,CAAC;oBACnB,GAAG,cAAc,CACf,OAAO,CAAC,OAAO,EACf,YAAY,KAAK,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAC1E;oBACD,UAAU,EAAE,mBAAmB,CAAC,SAAS;iBAC1C,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { invariant, OutpostError, positive } from \"../domain/errors.ts\";\nimport type { Command } from \"../domain/command.types.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport {\n executeProcess,\n quote,\n requireSuccess,\n} from \"../infrastructure/process.ts\";\nimport { firecrackerDefaults } from \"./firecracker.constants.ts\";\nimport type {\n FirecrackerOptions,\n FirecrackerRuntime,\n} from \"./firecracker.types.ts\";\n\nexport function firecrackerSsh(\n options: FirecrackerOptions,\n script: string,\n): Command {\n const ssh = options.ssh;\n return {\n executable: ssh.binary ?? (options.jailer ? \"/usr/bin/ssh\" : \"ssh\"),\n arguments: [\n \"-F\",\n \"/dev/null\",\n \"-T\",\n \"-o\",\n \"BatchMode=yes\",\n \"-o\",\n \"IdentitiesOnly=yes\",\n \"-o\",\n \"StrictHostKeyChecking=yes\",\n \"-o\",\n `UserKnownHostsFile=${ssh.knownHosts}`,\n \"-o\",\n \"GlobalKnownHostsFile=/dev/null\",\n \"-o\",\n \"ConnectTimeout=2\",\n \"-o\",\n \"ServerAliveInterval=2\",\n \"-o\",\n \"ServerAliveCountMax=2\",\n \"-i\",\n ssh.identity,\n \"-p\",\n String(ssh.port ?? 22),\n \"-l\",\n ssh.user,\n \"--\",\n ssh.host,\n script,\n ],\n };\n}\n\nexport function firecrackerCommand(\n runtime: FirecrackerRuntime,\n): SandboxLease[\"invoke\"] {\n return async (command) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Firecracker sandbox is closed\");\n invariant(\n !command.interactive && !command.elevated,\n \"Firecracker research provider does not support interactive or elevated commands\",\n );\n command.signal?.throwIfAborted();\n const variables = { ...runtime.variables, ...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 id = `/tmp/outpost-command-${randomUUID()}`;\n const wrapper = `umask 077; echo $$ > ${quote(id)}; test ! -e ${quote(id + \".cancel\")} || exit 130; cd ${quote(command.directory ?? runtime.root)} || exit; exec env ${Object.entries(\n variables,\n )\n .map(([key, value]) => quote(`${key}=${value}`))\n .join(\n \" \",\n )} ${[command.executable, ...(command.arguments ?? [])].map(quote).join(\" \")}`;\n const stop = new AbortController();\n const signal = command.signal\n ? AbortSignal.any([command.signal, stop.signal])\n : stop.signal;\n const deadlineMs = command.deadlineMs ?? firecrackerDefaults.commandMs;\n positive(deadlineMs, \"deadlineMs\");\n let cancellation: Promise<unknown> | undefined;\n const cancel = () => {\n cancellation ??= requireSuccess(\n {\n ...firecrackerSsh(\n runtime.options,\n `umask 077; touch ${quote(id + \".cancel\")}; if [ -f ${quote(id)} ]; then node -e ${quote(\"const fs=require('node:fs');const pid=Number(fs.readFileSync(process.argv[1],'utf8'));if(!Number.isSafeInteger(pid)||pid<2)process.exit(1);const kill=s=>{try{process.kill(-pid,s)}catch(e){if(e.code!=='ESRCH')throw e}};kill('SIGTERM');setTimeout(()=>kill('SIGKILL'),500)\")} ${quote(id)}; fi`,\n ),\n deadlineMs: firecrackerDefaults.cleanupMs,\n },\n executeProcess,\n );\n void cancellation.catch(() => undefined);\n };\n signal.addEventListener(\"abort\", cancel, { once: true });\n const timer = setTimeout(\n () =>\n stop.abort(\n new OutpostError(\"timeout\", `Command exceeded ${deadlineMs} ms`),\n ),\n deadlineMs,\n );\n try {\n const {\n directory: _directory,\n variables: _variables,\n ...invocation\n } = command;\n const result = await runtime.executor({\n ...invocation,\n ...(command.observe\n ? {\n observe: (channel, text) => {\n try {\n command.observe!(channel, text);\n } catch (cause) {\n stop.abort(cause);\n }\n },\n }\n : {}),\n signal,\n ...firecrackerSsh(\n runtime.options,\n `setsid --wait sh -c ${quote(wrapper)}; status=$?; printf '%s' \"$status\" > ${quote(id + \".status\")}; exit \"$status\"`,\n ),\n });\n signal.throwIfAborted();\n if (result.status === 255) {\n const status = await executeProcess({\n ...firecrackerSsh(runtime.options, `cat ${quote(id + \".status\")}`),\n deadlineMs: firecrackerDefaults.cleanupMs,\n });\n if (status.status !== 0 || status.stdout.trim() !== \"255\")\n throw new OutpostError(\n \"provider\",\n \"Guest SSH connection failed before command completion\",\n { unavailable: \"SSH connection failure\" },\n );\n }\n return result;\n } catch (cause) {\n try {\n cancel();\n await cancellation;\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Guest cancellation could not be confirmed; release the VM\",\n );\n }\n throw cause;\n } finally {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", cancel);\n if (!runtime.isClosed())\n await executeProcess({\n ...firecrackerSsh(\n runtime.options,\n `rm -f -- ${quote(id)} ${quote(id + \".cancel\")} ${quote(id + \".status\")}`,\n ),\n deadlineMs: firecrackerDefaults.cleanupMs,\n }).catch(() => undefined);\n }\n };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { SandboxLease } from "../domain/sandbox.types.ts";
2
+ export declare function localFiles(root: string, isClosed: () => boolean): Pick<SandboxLease, "upload" | "download">;
@@ -0,0 +1,24 @@
1
+ import { basename, resolve } from "node:path";
2
+ import { invariant, OutpostError } from "../domain/errors.js";
3
+ import { inside } from "../infrastructure/files.js";
4
+ import { copyTransfer, transferDestination, transferTarget, } from "../infrastructure/transfer-copy.js";
5
+ import { transfer } from "../infrastructure/transfer.js";
6
+ export function localFiles(root, isClosed) {
7
+ const copy = (source, destination, contents, options) => transfer(options, async (signal) => {
8
+ if (isClosed())
9
+ throw new OutpostError("provider", "Local sandbox is closed");
10
+ await transferDestination(destination);
11
+ if (source === destination)
12
+ return;
13
+ const target = await transferTarget(basename(source), destination, contents);
14
+ if (source === target)
15
+ return;
16
+ invariant(!inside(source, target), "Cannot copy a directory into itself");
17
+ await copyTransfer(source, target, signal);
18
+ });
19
+ return {
20
+ upload: (source, destination, options = {}) => copy(resolve(source), resolve(root, destination), /[\\/]\.$/.test(source), options),
21
+ download: (source, destination, options = {}) => copy(resolve(root, source), resolve(destination), /[\\/]\.$/.test(source), options),
22
+ };
23
+ }
24
+ //# sourceMappingURL=local-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-files.js","sourceRoot":"","sources":["../../src/providers/local-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,cAAc,GACf,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAEzD,MAAM,UAAU,UAAU,CACxB,IAAY,EACZ,QAAuB;IAEvB,MAAM,IAAI,GAAG,CACX,MAAc,EACd,WAAmB,EACnB,QAAiB,EACjB,OAAwB,EACxB,EAAE,CACF,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACjC,IAAI,QAAQ,EAAE;YACZ,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,yBAAyB,CAAC,CAAC;QAChE,MAAM,mBAAmB,CAAC,WAAW,CAAC,CAAC;QACvC,IAAI,MAAM,KAAK,WAAW;YAAE,OAAO;QACnC,MAAM,MAAM,GAAG,MAAM,cAAc,CACjC,QAAQ,CAAC,MAAM,CAAC,EAChB,WAAW,EACX,QAAQ,CACT,CAAC;QACF,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO;QAC9B,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,qCAAqC,CAAC,CAAC;QAC1E,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IACL,OAAO;QACL,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAC5C,IAAI,CACF,OAAO,CAAC,MAAM,CAAC,EACf,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,EAC1B,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EACvB,OAAO,CACR;QACH,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAC9C,IAAI,CACF,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,EACrB,OAAO,CAAC,WAAW,CAAC,EACpB,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EACvB,OAAO,CACR;KACJ,CAAC;AACJ,CAAC","sourcesContent":["import { basename, resolve } from \"node:path\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease, TransferOptions } from \"../domain/sandbox.types.ts\";\nimport { inside } from \"../infrastructure/files.ts\";\nimport {\n copyTransfer,\n transferDestination,\n transferTarget,\n} from \"../infrastructure/transfer-copy.ts\";\nimport { transfer } from \"../infrastructure/transfer.ts\";\n\nexport function localFiles(\n root: string,\n isClosed: () => boolean,\n): Pick<SandboxLease, \"upload\" | \"download\"> {\n const copy = (\n source: string,\n destination: string,\n contents: boolean,\n options: TransferOptions,\n ) =>\n transfer(options, async (signal) => {\n if (isClosed())\n throw new OutpostError(\"provider\", \"Local sandbox is closed\");\n await transferDestination(destination);\n if (source === destination) return;\n const target = await transferTarget(\n basename(source),\n destination,\n contents,\n );\n if (source === target) return;\n invariant(!inside(source, target), \"Cannot copy a directory into itself\");\n await copyTransfer(source, target, signal);\n });\n return {\n upload: (source, destination, options = {}) =>\n copy(\n resolve(source),\n resolve(root, destination),\n /[\\\\/]\\.$/.test(source),\n options,\n ),\n download: (source, destination, options = {}) =>\n copy(\n resolve(root, source),\n resolve(destination),\n /[\\\\/]\\.$/.test(source),\n options,\n ),\n };\n}\n"]}