apex-code 0.0.1-alpha.7 → 0.0.1-alpha.9

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 (393) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/README.md +5 -0
  3. package/dist/cli/args.d.ts +4 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +44 -9
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/concurrent-session.d.ts +3 -0
  8. package/dist/cli/concurrent-session.d.ts.map +1 -0
  9. package/dist/cli/concurrent-session.js +13 -0
  10. package/dist/cli/concurrent-session.js.map +1 -0
  11. package/dist/cli/file-processor.d.ts.map +1 -1
  12. package/dist/cli/file-processor.js +2 -1
  13. package/dist/cli/file-processor.js.map +1 -1
  14. package/dist/cli.d.ts.map +1 -1
  15. package/dist/cli.js +1 -2
  16. package/dist/cli.js.map +1 -1
  17. package/dist/config.d.ts +3 -2
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +21 -13
  20. package/dist/config.js.map +1 -1
  21. package/dist/core/agent-session-services.d.ts.map +1 -1
  22. package/dist/core/agent-session-services.js +20 -0
  23. package/dist/core/agent-session-services.js.map +1 -1
  24. package/dist/core/agent-session.d.ts +41 -9
  25. package/dist/core/agent-session.d.ts.map +1 -1
  26. package/dist/core/agent-session.js +172 -101
  27. package/dist/core/agent-session.js.map +1 -1
  28. package/dist/core/auth-storage.d.ts +4 -1
  29. package/dist/core/auth-storage.d.ts.map +1 -1
  30. package/dist/core/auth-storage.js +12 -9
  31. package/dist/core/auth-storage.js.map +1 -1
  32. package/dist/core/bash-executor.d.ts.map +1 -1
  33. package/dist/core/bash-executor.js +10 -9
  34. package/dist/core/bash-executor.js.map +1 -1
  35. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  36. package/dist/core/compaction/branch-summarization.js +3 -0
  37. package/dist/core/compaction/branch-summarization.js.map +1 -1
  38. package/dist/core/compaction/compaction.d.ts +4 -3
  39. package/dist/core/compaction/compaction.d.ts.map +1 -1
  40. package/dist/core/compaction/compaction.js +41 -32
  41. package/dist/core/compaction/compaction.js.map +1 -1
  42. package/dist/core/defaults.d.ts +1 -0
  43. package/dist/core/defaults.d.ts.map +1 -1
  44. package/dist/core/defaults.js +9 -0
  45. package/dist/core/defaults.js.map +1 -1
  46. package/dist/core/durable-state/sqlite.d.ts.map +1 -1
  47. package/dist/core/durable-state/sqlite.js +26 -2
  48. package/dist/core/durable-state/sqlite.js.map +1 -1
  49. package/dist/core/experimental.d.ts +4 -0
  50. package/dist/core/experimental.d.ts.map +1 -1
  51. package/dist/core/experimental.js +4 -0
  52. package/dist/core/experimental.js.map +1 -1
  53. package/dist/core/extensions/index.d.ts +2 -2
  54. package/dist/core/extensions/index.d.ts.map +1 -1
  55. package/dist/core/extensions/index.js +1 -1
  56. package/dist/core/extensions/index.js.map +1 -1
  57. package/dist/core/extensions/loader.d.ts.map +1 -1
  58. package/dist/core/extensions/loader.js +115 -48
  59. package/dist/core/extensions/loader.js.map +1 -1
  60. package/dist/core/extensions/types.d.ts +42 -9
  61. package/dist/core/extensions/types.d.ts.map +1 -1
  62. package/dist/core/extensions/types.js +3 -0
  63. package/dist/core/extensions/types.js.map +1 -1
  64. package/dist/core/footer-data-provider.d.ts +1 -1
  65. package/dist/core/footer-data-provider.d.ts.map +1 -1
  66. package/dist/core/footer-data-provider.js +1 -1
  67. package/dist/core/footer-data-provider.js.map +1 -1
  68. package/dist/core/http-dispatcher.d.ts +1 -2
  69. package/dist/core/http-dispatcher.d.ts.map +1 -1
  70. package/dist/core/http-dispatcher.js +2 -17
  71. package/dist/core/http-dispatcher.js.map +1 -1
  72. package/dist/core/http-idle-timeout.d.ts +12 -0
  73. package/dist/core/http-idle-timeout.d.ts.map +1 -0
  74. package/dist/core/http-idle-timeout.js +27 -0
  75. package/dist/core/http-idle-timeout.js.map +1 -0
  76. package/dist/core/keybindings.d.ts +38 -13
  77. package/dist/core/keybindings.d.ts.map +1 -1
  78. package/dist/core/keybindings.js +26 -5
  79. package/dist/core/keybindings.js.map +1 -1
  80. package/dist/core/lsp/client.d.ts +2 -0
  81. package/dist/core/lsp/client.d.ts.map +1 -1
  82. package/dist/core/lsp/client.js +27 -2
  83. package/dist/core/lsp/client.js.map +1 -1
  84. package/dist/core/model-config.d.ts +10 -0
  85. package/dist/core/model-config.d.ts.map +1 -1
  86. package/dist/core/model-config.js +4 -1
  87. package/dist/core/model-config.js.map +1 -1
  88. package/dist/core/model-resolver.d.ts +1 -0
  89. package/dist/core/model-resolver.d.ts.map +1 -1
  90. package/dist/core/model-resolver.js +14 -8
  91. package/dist/core/model-resolver.js.map +1 -1
  92. package/dist/core/models-store.d.ts.map +1 -1
  93. package/dist/core/models-store.js +2 -1
  94. package/dist/core/models-store.js.map +1 -1
  95. package/dist/core/package-manager.d.ts.map +1 -1
  96. package/dist/core/package-manager.js +22 -14
  97. package/dist/core/package-manager.js.map +1 -1
  98. package/dist/core/pi-manifest.d.ts.map +1 -1
  99. package/dist/core/pi-manifest.js +2 -1
  100. package/dist/core/pi-manifest.js.map +1 -1
  101. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  102. package/dist/core/remote-catalog-provider.js +2 -1
  103. package/dist/core/remote-catalog-provider.js.map +1 -1
  104. package/dist/core/resource-loader.d.ts.map +1 -1
  105. package/dist/core/resource-loader.js +3 -2
  106. package/dist/core/resource-loader.js.map +1 -1
  107. package/dist/core/sandbox/child-entry.d.ts.map +1 -1
  108. package/dist/core/sandbox/child-entry.js +8 -1
  109. package/dist/core/sandbox/child-entry.js.map +1 -1
  110. package/dist/core/sandbox/cli-launch.d.ts +3 -0
  111. package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
  112. package/dist/core/sandbox/cli-launch.js +25 -4
  113. package/dist/core/sandbox/cli-launch.js.map +1 -1
  114. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
  115. package/dist/core/sandbox/cli-supervisor.js +53 -13
  116. package/dist/core/sandbox/cli-supervisor.js.map +1 -1
  117. package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
  118. package/dist/core/sandbox/linux-backend.js +15 -0
  119. package/dist/core/sandbox/linux-backend.js.map +1 -1
  120. package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
  121. package/dist/core/sandbox/macos-backend.js +24 -1
  122. package/dist/core/sandbox/macos-backend.js.map +1 -1
  123. package/dist/core/sandbox/rpc/credential-client.d.ts +20 -0
  124. package/dist/core/sandbox/rpc/credential-client.d.ts.map +1 -0
  125. package/dist/core/sandbox/rpc/credential-client.js +170 -0
  126. package/dist/core/sandbox/rpc/credential-client.js.map +1 -0
  127. package/dist/core/sandbox/rpc/credential-proxy.d.ts +44 -0
  128. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -0
  129. package/dist/core/sandbox/rpc/credential-proxy.js +381 -0
  130. package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -0
  131. package/dist/core/sandbox/supervisor.d.ts +11 -0
  132. package/dist/core/sandbox/supervisor.d.ts.map +1 -1
  133. package/dist/core/sandbox/supervisor.js.map +1 -1
  134. package/dist/core/sandbox/terminal-size.d.ts +40 -0
  135. package/dist/core/sandbox/terminal-size.d.ts.map +1 -0
  136. package/dist/core/sandbox/terminal-size.js +105 -0
  137. package/dist/core/sandbox/terminal-size.js.map +1 -0
  138. package/dist/core/sdk.d.ts +8 -5
  139. package/dist/core/sdk.d.ts.map +1 -1
  140. package/dist/core/sdk.js +26 -7
  141. package/dist/core/sdk.js.map +1 -1
  142. package/dist/core/session-export.d.ts +4 -0
  143. package/dist/core/session-export.d.ts.map +1 -0
  144. package/dist/core/session-export.js +32 -0
  145. package/dist/core/session-export.js.map +1 -0
  146. package/dist/core/session-lease.d.ts +31 -0
  147. package/dist/core/session-lease.d.ts.map +1 -0
  148. package/dist/core/session-lease.js +138 -0
  149. package/dist/core/session-lease.js.map +1 -0
  150. package/dist/core/session-manager.d.ts +5 -0
  151. package/dist/core/session-manager.d.ts.map +1 -1
  152. package/dist/core/session-manager.js +5 -4
  153. package/dist/core/session-manager.js.map +1 -1
  154. package/dist/core/settings-diagnostics.d.ts +9 -0
  155. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  156. package/dist/core/settings-diagnostics.js +21 -0
  157. package/dist/core/settings-diagnostics.js.map +1 -0
  158. package/dist/core/settings-manager.d.ts +35 -0
  159. package/dist/core/settings-manager.d.ts.map +1 -1
  160. package/dist/core/settings-manager.js +93 -12
  161. package/dist/core/settings-manager.js.map +1 -1
  162. package/dist/core/skills.d.ts.map +1 -1
  163. package/dist/core/skills.js +51 -33
  164. package/dist/core/skills.js.map +1 -1
  165. package/dist/core/slash-commands.d.ts.map +1 -1
  166. package/dist/core/slash-commands.js +4 -1
  167. package/dist/core/slash-commands.js.map +1 -1
  168. package/dist/core/system-prompt.d.ts +2 -2
  169. package/dist/core/system-prompt.d.ts.map +1 -1
  170. package/dist/core/system-prompt.js +53 -62
  171. package/dist/core/system-prompt.js.map +1 -1
  172. package/dist/core/tools/bash.d.ts +14 -1
  173. package/dist/core/tools/bash.d.ts.map +1 -1
  174. package/dist/core/tools/bash.js +38 -20
  175. package/dist/core/tools/bash.js.map +1 -1
  176. package/dist/core/tools/delegate.d.ts.map +1 -1
  177. package/dist/core/tools/delegate.js +24 -0
  178. package/dist/core/tools/delegate.js.map +1 -1
  179. package/dist/core/tools/edit-diff.d.ts +0 -5
  180. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  181. package/dist/core/tools/edit-diff.js +2 -5
  182. package/dist/core/tools/edit-diff.js.map +1 -1
  183. package/dist/core/tools/edit.d.ts.map +1 -1
  184. package/dist/core/tools/edit.js +22 -4
  185. package/dist/core/tools/edit.js.map +1 -1
  186. package/dist/core/tools/find.d.ts.map +1 -1
  187. package/dist/core/tools/find.js +1 -1
  188. package/dist/core/tools/find.js.map +1 -1
  189. package/dist/core/tools/grep.d.ts +2 -0
  190. package/dist/core/tools/grep.d.ts.map +1 -1
  191. package/dist/core/tools/grep.js +35 -10
  192. package/dist/core/tools/grep.js.map +1 -1
  193. package/dist/core/tools/index.d.ts +4 -1
  194. package/dist/core/tools/index.d.ts.map +1 -1
  195. package/dist/core/tools/index.js +9 -0
  196. package/dist/core/tools/index.js.map +1 -1
  197. package/dist/core/tools/ls.d.ts +2 -0
  198. package/dist/core/tools/ls.d.ts.map +1 -1
  199. package/dist/core/tools/ls.js +5 -1
  200. package/dist/core/tools/ls.js.map +1 -1
  201. package/dist/core/tools/powershell.d.ts +16 -0
  202. package/dist/core/tools/powershell.d.ts.map +1 -0
  203. package/dist/core/tools/powershell.js +39 -0
  204. package/dist/core/tools/powershell.js.map +1 -0
  205. package/dist/core/tools/read.d.ts.map +1 -1
  206. package/dist/core/tools/read.js +2 -0
  207. package/dist/core/tools/read.js.map +1 -1
  208. package/dist/core/tools/web-search-exa.d.ts +33 -0
  209. package/dist/core/tools/web-search-exa.d.ts.map +1 -0
  210. package/dist/core/tools/web-search-exa.js +128 -0
  211. package/dist/core/tools/web-search-exa.js.map +1 -0
  212. package/dist/core/tools/write.d.ts.map +1 -1
  213. package/dist/core/tools/write.js +2 -0
  214. package/dist/core/tools/write.js.map +1 -1
  215. package/dist/core/trust-manager.d.ts.map +1 -1
  216. package/dist/core/trust-manager.js +2 -1
  217. package/dist/core/trust-manager.js.map +1 -1
  218. package/dist/core/web-search-provider.d.ts +64 -0
  219. package/dist/core/web-search-provider.d.ts.map +1 -0
  220. package/dist/core/web-search-provider.js +149 -0
  221. package/dist/core/web-search-provider.js.map +1 -0
  222. package/dist/extensions/llama/index.d.ts.map +1 -1
  223. package/dist/extensions/llama/index.js +2 -0
  224. package/dist/extensions/llama/index.js.map +1 -1
  225. package/dist/extensions/llama/provider.d.ts.map +1 -1
  226. package/dist/extensions/llama/provider.js +6 -2
  227. package/dist/extensions/llama/provider.js.map +1 -1
  228. package/dist/index.d.ts +6 -6
  229. package/dist/index.d.ts.map +1 -1
  230. package/dist/index.js +4 -4
  231. package/dist/index.js.map +1 -1
  232. package/dist/main.d.ts +5 -0
  233. package/dist/main.d.ts.map +1 -1
  234. package/dist/main.js +38 -16
  235. package/dist/main.js.map +1 -1
  236. package/dist/migrations.d.ts.map +1 -1
  237. package/dist/migrations.js +4 -3
  238. package/dist/migrations.js.map +1 -1
  239. package/dist/modes/interactive/components/custom-editor.d.ts +23 -0
  240. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  241. package/dist/modes/interactive/components/custom-editor.js +105 -2
  242. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  243. package/dist/modes/interactive/components/dynamic-border.d.ts +7 -1
  244. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  245. package/dist/modes/interactive/components/dynamic-border.js +11 -2
  246. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  247. package/dist/modes/interactive/components/extension-selector.d.ts +4 -0
  248. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  249. package/dist/modes/interactive/components/extension-selector.js +24 -6
  250. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  251. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  252. package/dist/modes/interactive/components/first-time-setup.js +6 -3
  253. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  254. package/dist/modes/interactive/components/first-use-hints.d.ts +8 -0
  255. package/dist/modes/interactive/components/first-use-hints.d.ts.map +1 -0
  256. package/dist/modes/interactive/components/first-use-hints.js +22 -0
  257. package/dist/modes/interactive/components/first-use-hints.js.map +1 -0
  258. package/dist/modes/interactive/components/footer.d.ts +12 -0
  259. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  260. package/dist/modes/interactive/components/footer.js +128 -9
  261. package/dist/modes/interactive/components/footer.js.map +1 -1
  262. package/dist/modes/interactive/components/index.d.ts +1 -0
  263. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  264. package/dist/modes/interactive/components/index.js +1 -0
  265. package/dist/modes/interactive/components/index.js.map +1 -1
  266. package/dist/modes/interactive/components/model-selector.d.ts +12 -3
  267. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  268. package/dist/modes/interactive/components/model-selector.js +71 -14
  269. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  270. package/dist/modes/interactive/components/settings-selector.d.ts +10 -3
  271. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  272. package/dist/modes/interactive/components/settings-selector.js +128 -56
  273. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  274. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  275. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  276. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  277. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  278. package/dist/modes/interactive/components/splash-header.d.ts +29 -4
  279. package/dist/modes/interactive/components/splash-header.d.ts.map +1 -1
  280. package/dist/modes/interactive/components/splash-header.js +58 -23
  281. package/dist/modes/interactive/components/splash-header.js.map +1 -1
  282. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  283. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  284. package/dist/modes/interactive/components/thinking-selector.js +74 -17
  285. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  286. package/dist/modes/interactive/components/tool-execution.d.ts +14 -0
  287. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/tool-execution.js +113 -35
  289. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  290. package/dist/modes/interactive/components/tool-panel.d.ts +30 -0
  291. package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -0
  292. package/dist/modes/interactive/components/tool-panel.js +130 -0
  293. package/dist/modes/interactive/components/tool-panel.js.map +1 -0
  294. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  295. package/dist/modes/interactive/external-editor.js +2 -1
  296. package/dist/modes/interactive/external-editor.js.map +1 -1
  297. package/dist/modes/interactive/interactive-mode.d.ts +55 -9
  298. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  299. package/dist/modes/interactive/interactive-mode.js +446 -87
  300. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  301. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  302. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  303. package/dist/modes/interactive/model-catalog-refresh.js +34 -0
  304. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  305. package/dist/modes/interactive/theme/apex.json +62 -63
  306. package/dist/modes/interactive/theme/dark.json +2 -0
  307. package/dist/modes/interactive/theme/light.json +2 -0
  308. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  309. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  310. package/dist/modes/interactive/theme/theme-controller.js +25 -11
  311. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  312. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  313. package/dist/modes/interactive/theme/theme.d.ts +14 -3
  314. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  315. package/dist/modes/interactive/theme/theme.js +60 -10
  316. package/dist/modes/interactive/theme/theme.js.map +1 -1
  317. package/dist/modes/json-event.d.ts +18 -12
  318. package/dist/modes/json-event.d.ts.map +1 -1
  319. package/dist/modes/json-event.js +22 -5
  320. package/dist/modes/json-event.js.map +1 -1
  321. package/dist/package-manager-cli.d.ts +1 -0
  322. package/dist/package-manager-cli.d.ts.map +1 -1
  323. package/dist/package-manager-cli.js +197 -5
  324. package/dist/package-manager-cli.js.map +1 -1
  325. package/dist/server/create-harness.d.ts.map +1 -1
  326. package/dist/server/create-harness.js +2 -0
  327. package/dist/server/create-harness.js.map +1 -1
  328. package/dist/themes/apex-logo.d.ts +36 -16
  329. package/dist/themes/apex-logo.d.ts.map +1 -1
  330. package/dist/themes/apex-logo.js +32 -28
  331. package/dist/themes/apex-logo.js.map +1 -1
  332. package/dist/utils/frontmatter.d.ts.map +1 -1
  333. package/dist/utils/frontmatter.js +2 -1
  334. package/dist/utils/frontmatter.js.map +1 -1
  335. package/dist/utils/management-http.d.ts +5 -3
  336. package/dist/utils/management-http.d.ts.map +1 -1
  337. package/dist/utils/management-http.js +14 -10
  338. package/dist/utils/management-http.js.map +1 -1
  339. package/dist/utils/shell.d.ts +3 -0
  340. package/dist/utils/shell.d.ts.map +1 -1
  341. package/dist/utils/shell.js +17 -5
  342. package/dist/utils/shell.js.map +1 -1
  343. package/dist/utils/syntax-highlight.d.ts +1 -0
  344. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  345. package/dist/utils/syntax-highlight.js +60 -1
  346. package/dist/utils/syntax-highlight.js.map +1 -1
  347. package/dist/utils/text.d.ts +8 -0
  348. package/dist/utils/text.d.ts.map +1 -0
  349. package/dist/utils/text.js +9 -0
  350. package/dist/utils/text.js.map +1 -0
  351. package/dist/utils/tools-manager.d.ts +10 -1
  352. package/dist/utils/tools-manager.d.ts.map +1 -1
  353. package/dist/utils/tools-manager.js +15 -20
  354. package/dist/utils/tools-manager.js.map +1 -1
  355. package/docs/compaction.md +18 -3
  356. package/docs/custom-provider.md +6 -3
  357. package/docs/environment-variables.md +1 -0
  358. package/docs/extensions.md +21 -7
  359. package/docs/json.md +12 -5
  360. package/docs/keybindings.md +15 -9
  361. package/docs/llama-cpp.md +2 -0
  362. package/docs/models.md +8 -2
  363. package/docs/packages.md +2 -2
  364. package/docs/rpc.md +24 -7
  365. package/docs/sdk.md +16 -2
  366. package/docs/settings.md +73 -2
  367. package/docs/skills.md +4 -3
  368. package/docs/terminal-setup.md +34 -9
  369. package/docs/themes.md +24 -3
  370. package/docs/tui.md +2 -2
  371. package/docs/usage.md +9 -4
  372. package/docs/windows.md +24 -2
  373. package/examples/extensions/border-status-editor.ts +1 -1
  374. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  375. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  376. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  377. package/examples/extensions/git-checkpoint.ts +2 -2
  378. package/examples/extensions/gondolin/package-lock.json +2 -2
  379. package/examples/extensions/gondolin/package.json +1 -1
  380. package/examples/extensions/notify.ts +3 -1
  381. package/examples/extensions/sandbox/package-lock.json +2 -2
  382. package/examples/extensions/sandbox/package.json +1 -1
  383. package/examples/extensions/subagent/README.md +3 -1
  384. package/examples/extensions/subagent/agents.ts +40 -9
  385. package/examples/extensions/subagent/index.ts +27 -4
  386. package/examples/extensions/titlebar-spinner.ts +1 -1
  387. package/examples/extensions/with-deps/package-lock.json +2 -2
  388. package/examples/extensions/with-deps/package.json +1 -1
  389. package/examples/rpc-extension-ui.ts +1 -1
  390. package/examples/sdk/06-extensions.ts +1 -1
  391. package/examples/sdk/README.md +1 -1
  392. package/npm-shrinkwrap.json +280 -445
  393. package/package.json +9 -11
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Supervisor-owned credential mutation service for sandboxed sessions.
3
+ *
4
+ * Reads stay on the read-only auth.json projection. Mutations cross this Unix socket
5
+ * and execute under the host AuthStorage lock. The child can therefore implement the
6
+ * CredentialStore serialized read-modify-write contract without gaining filesystem
7
+ * write access to the credential file.
8
+ */
9
+ import { chmodSync, lstatSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
10
+ import * as net from "node:net";
11
+ import { join } from "node:path";
12
+ import { AuthStorage } from "../../auth-storage.js";
13
+ import { getConfigValueEnvVarNames, isCommandConfigValue } from "../../resolve-config-value.js";
14
+ /** AF_UNIX `sun_path` is 108 bytes on Linux, including the terminating NUL. */
15
+ const SUN_PATH_LIMIT = 108;
16
+ /** Credentials are small; reject larger frames before UTF-8 decoding or JSON parsing. */
17
+ const MAX_FRAME_BYTES = 64 * 1024;
18
+ const MAX_CONNECTIONS = 32;
19
+ const CONNECTION_IDLE_TIMEOUT_MS = 30_000;
20
+ function processIsAlive(pid) {
21
+ try {
22
+ process.kill(pid, 0);
23
+ return true;
24
+ }
25
+ catch (error) {
26
+ return error.code === "EPERM";
27
+ }
28
+ }
29
+ /** Reclaim only same-user directories whose encoded supervisor PID is dead. */
30
+ function reclaimStaleCredentialChannelDirectories() {
31
+ let entries;
32
+ try {
33
+ entries = readdirSync("/tmp");
34
+ }
35
+ catch {
36
+ return;
37
+ }
38
+ for (const entry of entries) {
39
+ const match = /^apex-cred-(\d+)-[A-Za-z0-9_-]+$/.exec(entry);
40
+ if (!match)
41
+ continue;
42
+ const pid = Number(match[1]);
43
+ if (!Number.isSafeInteger(pid) || pid < 1 || processIsAlive(pid))
44
+ continue;
45
+ const path = join("/tmp", entry);
46
+ try {
47
+ const stat = lstatSync(path);
48
+ if (!stat.isDirectory() || stat.uid !== process.getuid?.())
49
+ continue;
50
+ rmSync(path, { force: true, recursive: true });
51
+ }
52
+ catch {
53
+ // Another launch may have reclaimed it first.
54
+ }
55
+ }
56
+ }
57
+ /**
58
+ * Allocate the endpoint beneath a private fixed-root directory.
59
+ *
60
+ * Do not honor TMPDIR here. It can point inside the workspace on macOS, where the
61
+ * sandbox may write it. A fresh 0700 directory under /tmp also removes the
62
+ * listen-before-chmod authorization race: no other account can reach the socket while
63
+ * Node applies its initial umask-derived mode.
64
+ */
65
+ export function resolveCredentialChannelPaths() {
66
+ reclaimStaleCredentialChannelDirectories();
67
+ const hostSocketDirectory = mkdtempSync(`/tmp/apex-cred-${process.pid}-`, { encoding: "utf8" });
68
+ chmodSync(hostSocketDirectory, 0o700);
69
+ const hostSocketPath = join(hostSocketDirectory, "channel.sock");
70
+ if (Buffer.byteLength(hostSocketPath) + 1 > SUN_PATH_LIMIT) {
71
+ rmSync(hostSocketDirectory, { force: true, recursive: true });
72
+ throw new Error("Credential channel path exceeds the Unix socket path limit.");
73
+ }
74
+ const childSocketPath = process.platform === "linux" ? "/home/apex-credential-channel.sock" : hostSocketPath;
75
+ return { hostSocketDirectory, hostSocketPath, childSocketPath };
76
+ }
77
+ /** Find a config-value reference without recursively walking attacker-controlled input. */
78
+ export function findCredentialConfigReference(credential) {
79
+ const pending = [{ value: credential, field: "" }];
80
+ while (pending.length > 0) {
81
+ const current = pending.pop();
82
+ if (!current)
83
+ break;
84
+ if (typeof current.value === "string") {
85
+ if (isCommandConfigValue(current.value))
86
+ return { field: current.field, kind: "command" };
87
+ if (getConfigValueEnvVarNames(current.value).length > 0) {
88
+ return { field: current.field, kind: "environment" };
89
+ }
90
+ continue;
91
+ }
92
+ if (Array.isArray(current.value)) {
93
+ for (let index = current.value.length - 1; index >= 0; index--) {
94
+ pending.push({ value: current.value[index], field: `${current.field}[${index}]` });
95
+ }
96
+ continue;
97
+ }
98
+ if (current.value !== null && typeof current.value === "object") {
99
+ const entries = Object.entries(current.value);
100
+ for (let index = entries.length - 1; index >= 0; index--) {
101
+ const [key, value] = entries[index];
102
+ pending.push({ value, field: current.field ? `${current.field}.${key}` : key });
103
+ }
104
+ }
105
+ }
106
+ return undefined;
107
+ }
108
+ function isStringRecord(value) {
109
+ return (typeof value === "object" &&
110
+ value !== null &&
111
+ !Array.isArray(value) &&
112
+ Object.values(value).every((entry) => typeof entry === "string"));
113
+ }
114
+ /** Runtime validation at the untrusted child-to-host boundary. */
115
+ export function isCredential(value) {
116
+ if (typeof value !== "object" || value === null || Array.isArray(value))
117
+ return false;
118
+ const credential = value;
119
+ if (credential.type === "api_key") {
120
+ return ((credential.key === undefined || typeof credential.key === "string") &&
121
+ (credential.env === undefined || isStringRecord(credential.env)));
122
+ }
123
+ return (credential.type === "oauth" &&
124
+ typeof credential.access === "string" &&
125
+ typeof credential.refresh === "string" &&
126
+ typeof credential.expires === "number" &&
127
+ Number.isFinite(credential.expires));
128
+ }
129
+ function referenceRefusal(reference) {
130
+ return (`Credential values written from a sandboxed session must be literal secrets: field ` +
131
+ `"${reference.field}" contains a ${reference.kind} reference. To store a reference, ` +
132
+ `edit the credential file on the host directly or use the provider environment variable.`);
133
+ }
134
+ function writeFrame(socket, response) {
135
+ if (socket.destroyed)
136
+ return;
137
+ const frame = Buffer.from(`${JSON.stringify(response)}\n`, "utf8");
138
+ if (frame.length > MAX_FRAME_BYTES) {
139
+ throw new Error("Credential channel response exceeded its byte limit.");
140
+ }
141
+ socket.write(frame);
142
+ }
143
+ class FrameReader {
144
+ socket;
145
+ onProtocolRefusal;
146
+ buffer = Buffer.alloc(0);
147
+ queued = [];
148
+ waiting = [];
149
+ terminalError;
150
+ constructor(socket, onProtocolRefusal) {
151
+ this.socket = socket;
152
+ this.onProtocolRefusal = onProtocolRefusal;
153
+ socket.on("data", (chunk) => this.push(chunk));
154
+ socket.on("close", () => this.fail(new Error("Credential channel client disconnected.")));
155
+ socket.on("error", (error) => this.fail(error));
156
+ }
157
+ next() {
158
+ const queued = this.queued.shift();
159
+ if (queued !== undefined)
160
+ return Promise.resolve(queued);
161
+ if (this.terminalError)
162
+ return Promise.reject(this.terminalError);
163
+ return new Promise((resolve, reject) => this.waiting.push({ resolve, reject }));
164
+ }
165
+ push(chunk) {
166
+ if (this.terminalError)
167
+ return;
168
+ this.buffer = Buffer.concat([this.buffer, chunk]);
169
+ while (true) {
170
+ const newline = this.buffer.indexOf(0x0a);
171
+ if (newline < 0) {
172
+ if (this.buffer.length > MAX_FRAME_BYTES)
173
+ this.rejectOversizedFrame();
174
+ return;
175
+ }
176
+ if (newline > MAX_FRAME_BYTES) {
177
+ this.rejectOversizedFrame();
178
+ return;
179
+ }
180
+ const frame = this.buffer.subarray(0, newline);
181
+ this.buffer = this.buffer.subarray(newline + 1);
182
+ if (frame.length === 0)
183
+ continue;
184
+ let value;
185
+ try {
186
+ value = JSON.parse(frame.toString("utf8"));
187
+ }
188
+ catch {
189
+ this.onProtocolRefusal("Invalid request frame: not JSON.");
190
+ writeFrame(this.socket, { ok: false, error: "Invalid request frame: not JSON." });
191
+ this.fail(new Error("Invalid credential channel JSON frame."));
192
+ this.socket.end();
193
+ return;
194
+ }
195
+ const waiter = this.waiting.shift();
196
+ if (waiter) {
197
+ waiter.resolve(value);
198
+ }
199
+ else if (this.queued.length === 0) {
200
+ this.queued.push(value);
201
+ }
202
+ else {
203
+ this.onProtocolRefusal("Unexpected pipelined request frame.");
204
+ writeFrame(this.socket, { ok: false, error: "Unexpected pipelined request frame." });
205
+ this.fail(new Error("Credential channel received an unexpected pipelined frame."));
206
+ this.socket.end();
207
+ return;
208
+ }
209
+ }
210
+ }
211
+ rejectOversizedFrame() {
212
+ this.onProtocolRefusal("Request frame exceeded the 64 KiB byte limit.");
213
+ writeFrame(this.socket, { ok: false, error: "Request frame is too large." });
214
+ this.fail(new Error("Credential channel frame exceeded its byte limit."));
215
+ this.socket.end();
216
+ }
217
+ fail(error) {
218
+ if (this.terminalError)
219
+ return;
220
+ this.terminalError = error;
221
+ for (const waiter of this.waiting.splice(0))
222
+ waiter.reject(error);
223
+ }
224
+ }
225
+ class ChannelRefusal extends Error {
226
+ command;
227
+ constructor(command, message) {
228
+ super(message);
229
+ this.name = "ChannelRefusal";
230
+ this.command = command;
231
+ }
232
+ }
233
+ function isRequestObject(value) {
234
+ return typeof value === "object" && value !== null && !Array.isArray(value);
235
+ }
236
+ /** Serve serialized credential mutations on one private Unix socket. */
237
+ export function createCredentialProxy(options) {
238
+ return new Promise((resolve, reject) => {
239
+ const authStorage = AuthStorage.create(options.authPath);
240
+ const sockets = new Set();
241
+ const operations = new Set();
242
+ let settled = false;
243
+ let closing;
244
+ const audit = (command, detail) => {
245
+ options.violationStore?.add({ kind: "unknown", command, detail, timestamp: new Date() });
246
+ };
247
+ const refuse = (socket, command, error, detail = error) => {
248
+ audit(command, `Refused: ${detail}`);
249
+ writeFrame(socket, { ok: false, error });
250
+ };
251
+ const handleConnection = async (socket, reader, signal) => {
252
+ const raw = await reader.next();
253
+ if (!isRequestObject(raw)) {
254
+ refuse(socket, "credential-channel", "Invalid request: expected an object.");
255
+ return;
256
+ }
257
+ const request = raw;
258
+ const providerId = request.providerId;
259
+ if (typeof providerId !== "string" ||
260
+ providerId.length === 0 ||
261
+ Buffer.byteLength(providerId, "utf8") > 256 ||
262
+ /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u.test(providerId)) {
263
+ refuse(socket, "credential-channel", "Invalid request: providerId contains unsafe characters or is too long.");
264
+ return;
265
+ }
266
+ if (request.action === "delete") {
267
+ await authStorage.delete(providerId, { signal });
268
+ audit(`credential-delete ${providerId}`, "Accepted: credential deleted through the sandbox channel.");
269
+ writeFrame(socket, { ok: true });
270
+ return;
271
+ }
272
+ if (request.action !== "modify") {
273
+ refuse(socket, "credential-channel", 'Invalid request: action must be "modify" or "delete".');
274
+ return;
275
+ }
276
+ let changed = false;
277
+ const result = await authStorage.modify(providerId, async (current) => {
278
+ writeFrame(socket, { type: "current", ...(current === undefined ? {} : { credential: current }) });
279
+ const rawCommit = await reader.next();
280
+ if (!isRequestObject(rawCommit)) {
281
+ throw new ChannelRefusal(`credential-write ${providerId}`, "Invalid modify response: expected an object.");
282
+ }
283
+ const commit = rawCommit;
284
+ if (commit.action === "abort") {
285
+ throw new ChannelRefusal(`credential-write ${providerId}`, "Credential modification was aborted by the client.");
286
+ }
287
+ if (commit.action === "no_change")
288
+ return undefined;
289
+ if (commit.action !== "commit") {
290
+ throw new ChannelRefusal(`credential-write ${providerId}`, "Invalid modify response action.");
291
+ }
292
+ if (!isCredential(commit.credential)) {
293
+ throw new ChannelRefusal(`credential-write ${providerId}`, "Invalid credential shape.");
294
+ }
295
+ const reference = findCredentialConfigReference(commit.credential);
296
+ if (reference) {
297
+ throw new ChannelRefusal(`credential-write ${providerId}`, referenceRefusal(reference));
298
+ }
299
+ changed = true;
300
+ return commit.credential;
301
+ }, { signal });
302
+ if (changed) {
303
+ audit(`credential-write ${providerId}`, "Accepted: credential written through the sandbox channel.");
304
+ }
305
+ writeFrame(socket, { ok: true, ...(result === undefined ? {} : { credential: result }) });
306
+ };
307
+ const server = net.createServer((socket) => {
308
+ if (sockets.size >= MAX_CONNECTIONS) {
309
+ refuse(socket, "credential-channel", "Credential channel connection limit reached.");
310
+ socket.end();
311
+ return;
312
+ }
313
+ sockets.add(socket);
314
+ const controller = new AbortController();
315
+ socket.setTimeout(CONNECTION_IDLE_TIMEOUT_MS, () => {
316
+ audit("credential-channel", "Refused: credential channel connection timed out.");
317
+ socket.destroy(new Error("Credential channel connection timed out."));
318
+ });
319
+ socket.on("close", () => {
320
+ sockets.delete(socket);
321
+ controller.abort();
322
+ });
323
+ const reader = new FrameReader(socket, (detail) => audit("credential-channel", `Refused: ${detail}`));
324
+ const operation = handleConnection(socket, reader, controller.signal)
325
+ .catch((error) => {
326
+ if (error instanceof ChannelRefusal) {
327
+ refuse(socket, error.command, error.message);
328
+ return;
329
+ }
330
+ refuse(socket, "credential-channel", "The host credential operation failed.", "Host credential operation failed without exposing credential data.");
331
+ })
332
+ .finally(() => {
333
+ operations.delete(operation);
334
+ socket.end();
335
+ });
336
+ operations.add(operation);
337
+ });
338
+ const cleanup = () => {
339
+ rmSync(options.socketPath, { force: true });
340
+ if (options.cleanupDirectory)
341
+ rmSync(options.cleanupDirectory, { force: true, recursive: true });
342
+ };
343
+ const failStartup = (error) => {
344
+ if (settled)
345
+ return;
346
+ settled = true;
347
+ server.close();
348
+ for (const socket of sockets)
349
+ socket.destroy();
350
+ cleanup();
351
+ reject(error);
352
+ };
353
+ server.once("error", failStartup);
354
+ server.listen(options.socketPath, () => {
355
+ try {
356
+ chmodSync(options.socketPath, 0o600);
357
+ }
358
+ catch (error) {
359
+ failStartup(error instanceof Error ? error : new Error(String(error)));
360
+ return;
361
+ }
362
+ settled = true;
363
+ server.off("error", failStartup);
364
+ resolve({
365
+ close: () => {
366
+ if (closing)
367
+ return closing;
368
+ closing = (async () => {
369
+ for (const socket of sockets)
370
+ socket.destroy();
371
+ await new Promise((resolveClose) => server.close(() => resolveClose()));
372
+ await Promise.allSettled([...operations]);
373
+ cleanup();
374
+ })();
375
+ return closing;
376
+ },
377
+ });
378
+ });
379
+ });
380
+ }
381
+ //# sourceMappingURL=credential-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-proxy.js","sourceRoot":"","sources":["../../../../src/core/sandbox/rpc/credential-proxy.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Supervisor-owned credential mutation service for sandboxed sessions.\n *\n * Reads stay on the read-only auth.json projection. Mutations cross this Unix socket\n * and execute under the host AuthStorage lock. The child can therefore implement the\n * CredentialStore serialized read-modify-write contract without gaining filesystem\n * write access to the credential file.\n */\n\nimport { chmodSync, lstatSync, mkdtempSync, readdirSync, rmSync } from \"node:fs\";\nimport * as net from \"node:net\";\nimport { join } from \"node:path\";\nimport type { Credential } from \"@earendil-works/pi-ai\";\nimport { AuthStorage } from \"../../auth-storage.ts\";\nimport { getConfigValueEnvVarNames, isCommandConfigValue } from \"../../resolve-config-value.ts\";\nimport type { SandboxViolationStore } from \"../violations.ts\";\n\n/** AF_UNIX `sun_path` is 108 bytes on Linux, including the terminating NUL. */\nconst SUN_PATH_LIMIT = 108;\n/** Credentials are small; reject larger frames before UTF-8 decoding or JSON parsing. */\nconst MAX_FRAME_BYTES = 64 * 1024;\nconst MAX_CONNECTIONS = 32;\nconst CONNECTION_IDLE_TIMEOUT_MS = 30_000;\n\nfunction processIsAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\treturn (error as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\n/** Reclaim only same-user directories whose encoded supervisor PID is dead. */\nfunction reclaimStaleCredentialChannelDirectories(): void {\n\tlet entries: string[];\n\ttry {\n\t\tentries = readdirSync(\"/tmp\");\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const entry of entries) {\n\t\tconst match = /^apex-cred-(\\d+)-[A-Za-z0-9_-]+$/.exec(entry);\n\t\tif (!match) continue;\n\t\tconst pid = Number(match[1]);\n\t\tif (!Number.isSafeInteger(pid) || pid < 1 || processIsAlive(pid)) continue;\n\t\tconst path = join(\"/tmp\", entry);\n\t\ttry {\n\t\t\tconst stat = lstatSync(path);\n\t\t\tif (!stat.isDirectory() || stat.uid !== process.getuid?.()) continue;\n\t\t\trmSync(path, { force: true, recursive: true });\n\t\t} catch {\n\t\t\t// Another launch may have reclaimed it first.\n\t\t}\n\t}\n}\n\nexport interface CredentialChannelPaths {\n\treadonly hostSocketDirectory: string;\n\treadonly hostSocketPath: string;\n\treadonly childSocketPath: string;\n}\n\n/**\n * Allocate the endpoint beneath a private fixed-root directory.\n *\n * Do not honor TMPDIR here. It can point inside the workspace on macOS, where the\n * sandbox may write it. A fresh 0700 directory under /tmp also removes the\n * listen-before-chmod authorization race: no other account can reach the socket while\n * Node applies its initial umask-derived mode.\n */\nexport function resolveCredentialChannelPaths(): CredentialChannelPaths {\n\treclaimStaleCredentialChannelDirectories();\n\tconst hostSocketDirectory = mkdtempSync(`/tmp/apex-cred-${process.pid}-`, { encoding: \"utf8\" });\n\tchmodSync(hostSocketDirectory, 0o700);\n\tconst hostSocketPath = join(hostSocketDirectory, \"channel.sock\");\n\tif (Buffer.byteLength(hostSocketPath) + 1 > SUN_PATH_LIMIT) {\n\t\trmSync(hostSocketDirectory, { force: true, recursive: true });\n\t\tthrow new Error(\"Credential channel path exceeds the Unix socket path limit.\");\n\t}\n\tconst childSocketPath = process.platform === \"linux\" ? \"/home/apex-credential-channel.sock\" : hostSocketPath;\n\treturn { hostSocketDirectory, hostSocketPath, childSocketPath };\n}\n\nexport interface CredentialProxy {\n\tclose(): Promise<void>;\n}\n\nexport interface CredentialConfigReference {\n\treadonly field: string;\n\treadonly kind: \"command\" | \"environment\";\n}\n\n/** Find a config-value reference without recursively walking attacker-controlled input. */\nexport function findCredentialConfigReference(credential: unknown): CredentialConfigReference | undefined {\n\tconst pending: Array<{ value: unknown; field: string }> = [{ value: credential, field: \"\" }];\n\twhile (pending.length > 0) {\n\t\tconst current = pending.pop();\n\t\tif (!current) break;\n\t\tif (typeof current.value === \"string\") {\n\t\t\tif (isCommandConfigValue(current.value)) return { field: current.field, kind: \"command\" };\n\t\t\tif (getConfigValueEnvVarNames(current.value).length > 0) {\n\t\t\t\treturn { field: current.field, kind: \"environment\" };\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (Array.isArray(current.value)) {\n\t\t\tfor (let index = current.value.length - 1; index >= 0; index--) {\n\t\t\t\tpending.push({ value: current.value[index], field: `${current.field}[${index}]` });\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (current.value !== null && typeof current.value === \"object\") {\n\t\t\tconst entries = Object.entries(current.value);\n\t\t\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\t\t\tconst [key, value] = entries[index]!;\n\t\t\t\tpending.push({ value, field: current.field ? `${current.field}.${key}` : key });\n\t\t\t}\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction isStringRecord(value: unknown): value is Record<string, string> {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t!Array.isArray(value) &&\n\t\tObject.values(value).every((entry) => typeof entry === \"string\")\n\t);\n}\n\n/** Runtime validation at the untrusted child-to-host boundary. */\nexport function isCredential(value: unknown): value is Credential {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n\tconst credential = value as Record<string, unknown>;\n\tif (credential.type === \"api_key\") {\n\t\treturn (\n\t\t\t(credential.key === undefined || typeof credential.key === \"string\") &&\n\t\t\t(credential.env === undefined || isStringRecord(credential.env))\n\t\t);\n\t}\n\treturn (\n\t\tcredential.type === \"oauth\" &&\n\t\ttypeof credential.access === \"string\" &&\n\t\ttypeof credential.refresh === \"string\" &&\n\t\ttypeof credential.expires === \"number\" &&\n\t\tNumber.isFinite(credential.expires)\n\t);\n}\n\nfunction referenceRefusal(reference: CredentialConfigReference): string {\n\treturn (\n\t\t`Credential values written from a sandboxed session must be literal secrets: field ` +\n\t\t`\"${reference.field}\" contains a ${reference.kind} reference. To store a reference, ` +\n\t\t`edit the credential file on the host directly or use the provider environment variable.`\n\t);\n}\n\ninterface StartRequest {\n\treadonly action?: unknown;\n\treadonly providerId?: unknown;\n}\n\ninterface CommitRequest {\n\treadonly action?: unknown;\n\treadonly credential?: unknown;\n}\n\nfunction writeFrame(socket: net.Socket, response: object): void {\n\tif (socket.destroyed) return;\n\tconst frame = Buffer.from(`${JSON.stringify(response)}\\n`, \"utf8\");\n\tif (frame.length > MAX_FRAME_BYTES) {\n\t\tthrow new Error(\"Credential channel response exceeded its byte limit.\");\n\t}\n\tsocket.write(frame);\n}\n\nclass FrameReader {\n\tprivate readonly socket: net.Socket;\n\tprivate readonly onProtocolRefusal: (detail: string) => void;\n\tprivate buffer = Buffer.alloc(0);\n\tprivate readonly queued: unknown[] = [];\n\tprivate readonly waiting: Array<{ resolve(value: unknown): void; reject(error: Error): void }> = [];\n\tprivate terminalError: Error | undefined;\n\n\tconstructor(socket: net.Socket, onProtocolRefusal: (detail: string) => void) {\n\t\tthis.socket = socket;\n\t\tthis.onProtocolRefusal = onProtocolRefusal;\n\t\tsocket.on(\"data\", (chunk: Buffer) => this.push(chunk));\n\t\tsocket.on(\"close\", () => this.fail(new Error(\"Credential channel client disconnected.\")));\n\t\tsocket.on(\"error\", (error) => this.fail(error));\n\t}\n\n\tnext(): Promise<unknown> {\n\t\tconst queued = this.queued.shift();\n\t\tif (queued !== undefined) return Promise.resolve(queued);\n\t\tif (this.terminalError) return Promise.reject(this.terminalError);\n\t\treturn new Promise((resolve, reject) => this.waiting.push({ resolve, reject }));\n\t}\n\n\tprivate push(chunk: Buffer): void {\n\t\tif (this.terminalError) return;\n\t\tthis.buffer = Buffer.concat([this.buffer, chunk]);\n\t\twhile (true) {\n\t\t\tconst newline = this.buffer.indexOf(0x0a);\n\t\t\tif (newline < 0) {\n\t\t\t\tif (this.buffer.length > MAX_FRAME_BYTES) this.rejectOversizedFrame();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (newline > MAX_FRAME_BYTES) {\n\t\t\t\tthis.rejectOversizedFrame();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst frame = this.buffer.subarray(0, newline);\n\t\t\tthis.buffer = this.buffer.subarray(newline + 1);\n\t\t\tif (frame.length === 0) continue;\n\t\t\tlet value: unknown;\n\t\t\ttry {\n\t\t\t\tvalue = JSON.parse(frame.toString(\"utf8\")) as unknown;\n\t\t\t} catch {\n\t\t\t\tthis.onProtocolRefusal(\"Invalid request frame: not JSON.\");\n\t\t\t\twriteFrame(this.socket, { ok: false, error: \"Invalid request frame: not JSON.\" });\n\t\t\t\tthis.fail(new Error(\"Invalid credential channel JSON frame.\"));\n\t\t\t\tthis.socket.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst waiter = this.waiting.shift();\n\t\t\tif (waiter) {\n\t\t\t\twaiter.resolve(value);\n\t\t\t} else if (this.queued.length === 0) {\n\t\t\t\tthis.queued.push(value);\n\t\t\t} else {\n\t\t\t\tthis.onProtocolRefusal(\"Unexpected pipelined request frame.\");\n\t\t\t\twriteFrame(this.socket, { ok: false, error: \"Unexpected pipelined request frame.\" });\n\t\t\t\tthis.fail(new Error(\"Credential channel received an unexpected pipelined frame.\"));\n\t\t\t\tthis.socket.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate rejectOversizedFrame(): void {\n\t\tthis.onProtocolRefusal(\"Request frame exceeded the 64 KiB byte limit.\");\n\t\twriteFrame(this.socket, { ok: false, error: \"Request frame is too large.\" });\n\t\tthis.fail(new Error(\"Credential channel frame exceeded its byte limit.\"));\n\t\tthis.socket.end();\n\t}\n\n\tprivate fail(error: Error): void {\n\t\tif (this.terminalError) return;\n\t\tthis.terminalError = error;\n\t\tfor (const waiter of this.waiting.splice(0)) waiter.reject(error);\n\t}\n}\n\nclass ChannelRefusal extends Error {\n\treadonly command: string;\n\n\tconstructor(command: string, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ChannelRefusal\";\n\t\tthis.command = command;\n\t}\n}\n\nfunction isRequestObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Serve serialized credential mutations on one private Unix socket. */\nexport function createCredentialProxy(options: {\n\tauthPath: string;\n\tviolationStore?: SandboxViolationStore;\n\tsocketPath: string;\n\t/** Private directory allocated by resolveCredentialChannelPaths; removed on close. */\n\tcleanupDirectory?: string;\n}): Promise<CredentialProxy> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst authStorage = AuthStorage.create(options.authPath);\n\t\tconst sockets = new Set<net.Socket>();\n\t\tconst operations = new Set<Promise<void>>();\n\t\tlet settled = false;\n\t\tlet closing: Promise<void> | undefined;\n\n\t\tconst audit = (command: string, detail: string): void => {\n\t\t\toptions.violationStore?.add({ kind: \"unknown\", command, detail, timestamp: new Date() });\n\t\t};\n\t\tconst refuse = (socket: net.Socket, command: string, error: string, detail = error): void => {\n\t\t\taudit(command, `Refused: ${detail}`);\n\t\t\twriteFrame(socket, { ok: false, error });\n\t\t};\n\n\t\tconst handleConnection = async (socket: net.Socket, reader: FrameReader, signal: AbortSignal): Promise<void> => {\n\t\t\tconst raw = await reader.next();\n\t\t\tif (!isRequestObject(raw)) {\n\t\t\t\trefuse(socket, \"credential-channel\", \"Invalid request: expected an object.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst request = raw as StartRequest;\n\t\t\tconst providerId = request.providerId;\n\t\t\tif (\n\t\t\t\ttypeof providerId !== \"string\" ||\n\t\t\t\tproviderId.length === 0 ||\n\t\t\t\tBuffer.byteLength(providerId, \"utf8\") > 256 ||\n\t\t\t\t/[\\u0000-\\u001f\\u007f-\\u009f\\u202a-\\u202e\\u2066-\\u2069]/u.test(providerId)\n\t\t\t) {\n\t\t\t\trefuse(\n\t\t\t\t\tsocket,\n\t\t\t\t\t\"credential-channel\",\n\t\t\t\t\t\"Invalid request: providerId contains unsafe characters or is too long.\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (request.action === \"delete\") {\n\t\t\t\tawait authStorage.delete(providerId, { signal });\n\t\t\t\taudit(`credential-delete ${providerId}`, \"Accepted: credential deleted through the sandbox channel.\");\n\t\t\t\twriteFrame(socket, { ok: true });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (request.action !== \"modify\") {\n\t\t\t\trefuse(socket, \"credential-channel\", 'Invalid request: action must be \"modify\" or \"delete\".');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet changed = false;\n\t\t\tconst result = await authStorage.modify(\n\t\t\t\tproviderId,\n\t\t\t\tasync (current) => {\n\t\t\t\t\twriteFrame(socket, { type: \"current\", ...(current === undefined ? {} : { credential: current }) });\n\t\t\t\t\tconst rawCommit = await reader.next();\n\t\t\t\t\tif (!isRequestObject(rawCommit)) {\n\t\t\t\t\t\tthrow new ChannelRefusal(\n\t\t\t\t\t\t\t`credential-write ${providerId}`,\n\t\t\t\t\t\t\t\"Invalid modify response: expected an object.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst commit = rawCommit as CommitRequest;\n\t\t\t\t\tif (commit.action === \"abort\") {\n\t\t\t\t\t\tthrow new ChannelRefusal(\n\t\t\t\t\t\t\t`credential-write ${providerId}`,\n\t\t\t\t\t\t\t\"Credential modification was aborted by the client.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (commit.action === \"no_change\") return undefined;\n\t\t\t\t\tif (commit.action !== \"commit\") {\n\t\t\t\t\t\tthrow new ChannelRefusal(`credential-write ${providerId}`, \"Invalid modify response action.\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!isCredential(commit.credential)) {\n\t\t\t\t\t\tthrow new ChannelRefusal(`credential-write ${providerId}`, \"Invalid credential shape.\");\n\t\t\t\t\t}\n\t\t\t\t\tconst reference = findCredentialConfigReference(commit.credential);\n\t\t\t\t\tif (reference) {\n\t\t\t\t\t\tthrow new ChannelRefusal(`credential-write ${providerId}`, referenceRefusal(reference));\n\t\t\t\t\t}\n\t\t\t\t\tchanged = true;\n\t\t\t\t\treturn commit.credential;\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t\tif (changed) {\n\t\t\t\taudit(`credential-write ${providerId}`, \"Accepted: credential written through the sandbox channel.\");\n\t\t\t}\n\t\t\twriteFrame(socket, { ok: true, ...(result === undefined ? {} : { credential: result }) });\n\t\t};\n\n\t\tconst server = net.createServer((socket) => {\n\t\t\tif (sockets.size >= MAX_CONNECTIONS) {\n\t\t\t\trefuse(socket, \"credential-channel\", \"Credential channel connection limit reached.\");\n\t\t\t\tsocket.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsockets.add(socket);\n\t\t\tconst controller = new AbortController();\n\t\t\tsocket.setTimeout(CONNECTION_IDLE_TIMEOUT_MS, () => {\n\t\t\t\taudit(\"credential-channel\", \"Refused: credential channel connection timed out.\");\n\t\t\t\tsocket.destroy(new Error(\"Credential channel connection timed out.\"));\n\t\t\t});\n\t\t\tsocket.on(\"close\", () => {\n\t\t\t\tsockets.delete(socket);\n\t\t\t\tcontroller.abort();\n\t\t\t});\n\t\t\tconst reader = new FrameReader(socket, (detail) => audit(\"credential-channel\", `Refused: ${detail}`));\n\t\t\tconst operation = handleConnection(socket, reader, controller.signal)\n\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\tif (error instanceof ChannelRefusal) {\n\t\t\t\t\t\trefuse(socket, error.command, error.message);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\trefuse(\n\t\t\t\t\t\tsocket,\n\t\t\t\t\t\t\"credential-channel\",\n\t\t\t\t\t\t\"The host credential operation failed.\",\n\t\t\t\t\t\t\"Host credential operation failed without exposing credential data.\",\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\toperations.delete(operation);\n\t\t\t\t\tsocket.end();\n\t\t\t\t});\n\t\t\toperations.add(operation);\n\t\t});\n\n\t\tconst cleanup = (): void => {\n\t\t\trmSync(options.socketPath, { force: true });\n\t\t\tif (options.cleanupDirectory) rmSync(options.cleanupDirectory, { force: true, recursive: true });\n\t\t};\n\t\tconst failStartup = (error: Error): void => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tserver.close();\n\t\t\tfor (const socket of sockets) socket.destroy();\n\t\t\tcleanup();\n\t\t\treject(error);\n\t\t};\n\t\tserver.once(\"error\", failStartup);\n\t\tserver.listen(options.socketPath, () => {\n\t\t\ttry {\n\t\t\t\tchmodSync(options.socketPath, 0o600);\n\t\t\t} catch (error) {\n\t\t\t\tfailStartup(error instanceof Error ? error : new Error(String(error)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tserver.off(\"error\", failStartup);\n\t\t\tresolve({\n\t\t\t\tclose: () => {\n\t\t\t\t\tif (closing) return closing;\n\t\t\t\t\tclosing = (async () => {\n\t\t\t\t\t\tfor (const socket of sockets) socket.destroy();\n\t\t\t\t\t\tawait new Promise<void>((resolveClose) => server.close(() => resolveClose()));\n\t\t\t\t\t\tawait Promise.allSettled([...operations]);\n\t\t\t\t\t\tcleanup();\n\t\t\t\t\t})();\n\t\t\t\t\treturn closing;\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t});\n}\n"]}
@@ -17,6 +17,17 @@ export interface SandboxLaunch {
17
17
  readonly source: string;
18
18
  readonly destination: string;
19
19
  }[];
20
+ /**
21
+ * The supervisor-owned credential write channel, when one was opened for this
22
+ * launch. `hostSocketPath` is where the supervisor's writer listens; the backend
23
+ * projects it to `childSocketPath`, which is also what the child is told through
24
+ * `APEX_CREDENTIAL_PROXY_PATH`. Absent means the session has no credential write
25
+ * path at all -- the mount stays read-only with no exception.
26
+ */
27
+ readonly credentialChannel?: {
28
+ readonly hostSocketPath: string;
29
+ readonly childSocketPath: string;
30
+ };
20
31
  }
21
32
  /** A platform adapter. Its sole job is to launch a normal Apex child inside an OS boundary. */
22
33
  export interface SandboxBackend {
@@ -1 +1 @@
1
- {"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEhG,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACzC,oFAAoF;IACpF,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,wEAAwE;IACxE,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjG;AAED,+FAA+F;AAC/F,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;CACtB,GAAG,iBAAiB,CAWpB","sourcesContent":["import { requireSandboxEnforcement, type SandboxPolicy, type SandboxStatus } from \"./policy.ts\";\n\nexport interface SandboxLaunch {\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly policy: SandboxPolicy;\n\treadonly environment?: NodeJS.ProcessEnv;\n\t/** Application/runtime directories needed by the child but never writable by it. */\n\treadonly readOnlyPaths?: readonly string[];\n\t/** Individual read-only files, such as a host-owned credential file. */\n\treadonly readOnlyFiles?: readonly string[];\n\t/**\n\t * Host executables projected read-only at an exact path inside the child. The\n\t * destination is chosen by the caller because only it knows where the child will\n\t * look; the backend just places the file there without write access.\n\t */\n\treadonly readOnlyBinaries?: readonly { readonly source: string; readonly destination: string }[];\n}\n\n/** A platform adapter. Its sole job is to launch a normal Apex child inside an OS boundary. */\nexport interface SandboxBackend {\n\treadonly status: SandboxStatus;\n\tlaunch(launch: SandboxLaunch): Promise<number>;\n\tclose(): Promise<void>;\n}\n\nexport interface SandboxSupervisor {\n\treadonly status: SandboxStatus;\n\tlaunch(options: Omit<SandboxLaunch, \"policy\">): Promise<number>;\n\tclose(): Promise<void>;\n}\n\n/**\n * Owns fail-closed policy propagation and lifecycle; OS details remain behind the\n * backend so all testable security semantics are Apex-owned.\n */\nexport function createSandboxSupervisor(options: {\n\tbackend: SandboxBackend;\n\tpolicy: SandboxPolicy;\n}): SandboxSupervisor {\n\treturn {\n\t\tstatus: options.backend.status,\n\t\tasync launch(launch) {\n\t\t\trequireSandboxEnforcement(options.backend.status);\n\t\t\treturn options.backend.launch({ ...launch, policy: options.policy });\n\t\t},\n\t\tasync close() {\n\t\t\tawait options.backend.close();\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEhG,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACzC,oFAAoF;IACpF,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,wEAAwE;IACxE,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjG;;;;;;OAMG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE;QAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;KACjC,CAAC;CACF;AAED,+FAA+F;AAC/F,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;CACtB,GAAG,iBAAiB,CAWpB","sourcesContent":["import { requireSandboxEnforcement, type SandboxPolicy, type SandboxStatus } from \"./policy.ts\";\n\nexport interface SandboxLaunch {\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly policy: SandboxPolicy;\n\treadonly environment?: NodeJS.ProcessEnv;\n\t/** Application/runtime directories needed by the child but never writable by it. */\n\treadonly readOnlyPaths?: readonly string[];\n\t/** Individual read-only files, such as a host-owned credential file. */\n\treadonly readOnlyFiles?: readonly string[];\n\t/**\n\t * Host executables projected read-only at an exact path inside the child. The\n\t * destination is chosen by the caller because only it knows where the child will\n\t * look; the backend just places the file there without write access.\n\t */\n\treadonly readOnlyBinaries?: readonly { readonly source: string; readonly destination: string }[];\n\t/**\n\t * The supervisor-owned credential write channel, when one was opened for this\n\t * launch. `hostSocketPath` is where the supervisor's writer listens; the backend\n\t * projects it to `childSocketPath`, which is also what the child is told through\n\t * `APEX_CREDENTIAL_PROXY_PATH`. Absent means the session has no credential write\n\t * path at all -- the mount stays read-only with no exception.\n\t */\n\treadonly credentialChannel?: {\n\t\treadonly hostSocketPath: string;\n\t\treadonly childSocketPath: string;\n\t};\n}\n\n/** A platform adapter. Its sole job is to launch a normal Apex child inside an OS boundary. */\nexport interface SandboxBackend {\n\treadonly status: SandboxStatus;\n\tlaunch(launch: SandboxLaunch): Promise<number>;\n\tclose(): Promise<void>;\n}\n\nexport interface SandboxSupervisor {\n\treadonly status: SandboxStatus;\n\tlaunch(options: Omit<SandboxLaunch, \"policy\">): Promise<number>;\n\tclose(): Promise<void>;\n}\n\n/**\n * Owns fail-closed policy propagation and lifecycle; OS details remain behind the\n * backend so all testable security semantics are Apex-owned.\n */\nexport function createSandboxSupervisor(options: {\n\tbackend: SandboxBackend;\n\tpolicy: SandboxPolicy;\n}): SandboxSupervisor {\n\treturn {\n\t\tstatus: options.backend.status,\n\t\tasync launch(launch) {\n\t\t\trequireSandboxEnforcement(options.backend.status);\n\t\t\treturn options.backend.launch({ ...launch, policy: options.policy });\n\t\t},\n\t\tasync close() {\n\t\t\tawait options.backend.close();\n\t\t},\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../../src/core/sandbox/supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAA0C,MAAM,aAAa,CAAC;AAgChG;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAGvC,EAAqB;IACrB,OAAO;QACN,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM;QAC9B,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE;YACpB,yBAAyB,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClD,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAAA,CACrE;QACD,KAAK,CAAC,KAAK,GAAG;YACb,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAAA,CAC9B;KACD,CAAC;AAAA,CACF","sourcesContent":["import { requireSandboxEnforcement, type SandboxPolicy, type SandboxStatus } from \"./policy.ts\";\n\nexport interface SandboxLaunch {\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly policy: SandboxPolicy;\n\treadonly environment?: NodeJS.ProcessEnv;\n\t/** Application/runtime directories needed by the child but never writable by it. */\n\treadonly readOnlyPaths?: readonly string[];\n\t/** Individual read-only files, such as a host-owned credential file. */\n\treadonly readOnlyFiles?: readonly string[];\n\t/**\n\t * Host executables projected read-only at an exact path inside the child. The\n\t * destination is chosen by the caller because only it knows where the child will\n\t * look; the backend just places the file there without write access.\n\t */\n\treadonly readOnlyBinaries?: readonly { readonly source: string; readonly destination: string }[];\n}\n\n/** A platform adapter. Its sole job is to launch a normal Apex child inside an OS boundary. */\nexport interface SandboxBackend {\n\treadonly status: SandboxStatus;\n\tlaunch(launch: SandboxLaunch): Promise<number>;\n\tclose(): Promise<void>;\n}\n\nexport interface SandboxSupervisor {\n\treadonly status: SandboxStatus;\n\tlaunch(options: Omit<SandboxLaunch, \"policy\">): Promise<number>;\n\tclose(): Promise<void>;\n}\n\n/**\n * Owns fail-closed policy propagation and lifecycle; OS details remain behind the\n * backend so all testable security semantics are Apex-owned.\n */\nexport function createSandboxSupervisor(options: {\n\tbackend: SandboxBackend;\n\tpolicy: SandboxPolicy;\n}): SandboxSupervisor {\n\treturn {\n\t\tstatus: options.backend.status,\n\t\tasync launch(launch) {\n\t\t\trequireSandboxEnforcement(options.backend.status);\n\t\t\treturn options.backend.launch({ ...launch, policy: options.policy });\n\t\t},\n\t\tasync close() {\n\t\t\tawait options.backend.close();\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../../src/core/sandbox/supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAA0C,MAAM,aAAa,CAAC;AA2ChG;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAGvC,EAAqB;IACrB,OAAO;QACN,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM;QAC9B,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE;YACpB,yBAAyB,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClD,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAAA,CACrE;QACD,KAAK,CAAC,KAAK,GAAG;YACb,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAAA,CAC9B;KACD,CAAC;AAAA,CACF","sourcesContent":["import { requireSandboxEnforcement, type SandboxPolicy, type SandboxStatus } from \"./policy.ts\";\n\nexport interface SandboxLaunch {\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly policy: SandboxPolicy;\n\treadonly environment?: NodeJS.ProcessEnv;\n\t/** Application/runtime directories needed by the child but never writable by it. */\n\treadonly readOnlyPaths?: readonly string[];\n\t/** Individual read-only files, such as a host-owned credential file. */\n\treadonly readOnlyFiles?: readonly string[];\n\t/**\n\t * Host executables projected read-only at an exact path inside the child. The\n\t * destination is chosen by the caller because only it knows where the child will\n\t * look; the backend just places the file there without write access.\n\t */\n\treadonly readOnlyBinaries?: readonly { readonly source: string; readonly destination: string }[];\n\t/**\n\t * The supervisor-owned credential write channel, when one was opened for this\n\t * launch. `hostSocketPath` is where the supervisor's writer listens; the backend\n\t * projects it to `childSocketPath`, which is also what the child is told through\n\t * `APEX_CREDENTIAL_PROXY_PATH`. Absent means the session has no credential write\n\t * path at all -- the mount stays read-only with no exception.\n\t */\n\treadonly credentialChannel?: {\n\t\treadonly hostSocketPath: string;\n\t\treadonly childSocketPath: string;\n\t};\n}\n\n/** A platform adapter. Its sole job is to launch a normal Apex child inside an OS boundary. */\nexport interface SandboxBackend {\n\treadonly status: SandboxStatus;\n\tlaunch(launch: SandboxLaunch): Promise<number>;\n\tclose(): Promise<void>;\n}\n\nexport interface SandboxSupervisor {\n\treadonly status: SandboxStatus;\n\tlaunch(options: Omit<SandboxLaunch, \"policy\">): Promise<number>;\n\tclose(): Promise<void>;\n}\n\n/**\n * Owns fail-closed policy propagation and lifecycle; OS details remain behind the\n * backend so all testable security semantics are Apex-owned.\n */\nexport function createSandboxSupervisor(options: {\n\tbackend: SandboxBackend;\n\tpolicy: SandboxPolicy;\n}): SandboxSupervisor {\n\treturn {\n\t\tstatus: options.backend.status,\n\t\tasync launch(launch) {\n\t\t\trequireSandboxEnforcement(options.backend.status);\n\t\t\treturn options.backend.launch({ ...launch, policy: options.policy });\n\t\t},\n\t\tasync close() {\n\t\t\tawait options.backend.close();\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Carries the host terminal's size across the sandbox boundary.
3
+ *
4
+ * The sandboxed child runs under `bwrap --new-session`, so it has no
5
+ * controlling terminal: the kernel never sends it SIGWINCH, and the window size
6
+ * its stdout reports is frozen at whatever it was when the sandbox was created.
7
+ * Everything downstream reads `process.stdout.columns`, so a resize left the TUI
8
+ * rendering to a width the terminal no longer had.
9
+ *
10
+ * The supervisor owns a real TTY and does get the resize event. It publishes the
11
+ * new size to a file inside the workspace, which is already bind-mounted into
12
+ * the sandbox; the child watches that file and republishes it as a `resize` on
13
+ * its own stdout. Signals are deliberately not used as the transport: bwrap sits
14
+ * between the two processes as PID 1 and does not forward SIGWINCH.
15
+ */
16
+ /** Env var naming the file both sides agree on. */
17
+ export declare const TERMINAL_SIZE_PATH_VARIABLE = "APEX_TERMINAL_SIZE_PATH";
18
+ export interface TerminalSize {
19
+ columns: number;
20
+ rows: number;
21
+ }
22
+ export declare function parseTerminalSize(contents: string): TerminalSize | undefined;
23
+ /**
24
+ * Host side. Publishes the current size, then republishes on every resize.
25
+ * Returns a stop function; the caller must run it on every launch exit path.
26
+ */
27
+ export declare function publishTerminalSize(path: string, stdout?: NodeJS.WriteStream): () => void;
28
+ /**
29
+ * Child side. Applies the published size to `process.stdout` and keeps applying
30
+ * it as the file changes.
31
+ *
32
+ * `columns` and `rows` are redefined as getters because Node recomputes them
33
+ * from the sandbox's own console on SIGWINCH, which is exactly the stale value
34
+ * this exists to override. The setters swallow those writes rather than throwing.
35
+ */
36
+ export declare function applyTerminalSize(path: string, stdout?: NodeJS.WriteStream): {
37
+ stop: () => void;
38
+ refresh: () => void;
39
+ };
40
+ //# sourceMappingURL=terminal-size.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-size.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/terminal-size.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH,mDAAmD;AACnD,eAAO,MAAM,2BAA2B,4BAA4B,CAAC;AAErE,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACb;AAMD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAK5E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,WAA4B,GAAG,MAAM,IAAI,CAmBzG;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,MAAM,CAAC,WAA4B,GACzC;IAAE,IAAI,EAAE,MAAM,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAyC3C","sourcesContent":["import { type FSWatcher, readFileSync, watch, writeFileSync } from \"node:fs\";\n\n/**\n * Carries the host terminal's size across the sandbox boundary.\n *\n * The sandboxed child runs under `bwrap --new-session`, so it has no\n * controlling terminal: the kernel never sends it SIGWINCH, and the window size\n * its stdout reports is frozen at whatever it was when the sandbox was created.\n * Everything downstream reads `process.stdout.columns`, so a resize left the TUI\n * rendering to a width the terminal no longer had.\n *\n * The supervisor owns a real TTY and does get the resize event. It publishes the\n * new size to a file inside the workspace, which is already bind-mounted into\n * the sandbox; the child watches that file and republishes it as a `resize` on\n * its own stdout. Signals are deliberately not used as the transport: bwrap sits\n * between the two processes as PID 1 and does not forward SIGWINCH.\n */\n\n/** Env var naming the file both sides agree on. */\nexport const TERMINAL_SIZE_PATH_VARIABLE = \"APEX_TERMINAL_SIZE_PATH\";\n\nexport interface TerminalSize {\n\tcolumns: number;\n\trows: number;\n}\n\nfunction formatSize(size: TerminalSize): string {\n\treturn `${size.columns} ${size.rows}\\n`;\n}\n\nexport function parseTerminalSize(contents: string): TerminalSize | undefined {\n\tconst [columns, rows] = contents.trim().split(/\\s+/).map(Number);\n\tif (!Number.isInteger(columns) || !Number.isInteger(rows)) return undefined;\n\tif (columns <= 0 || rows <= 0) return undefined;\n\treturn { columns, rows };\n}\n\n/**\n * Host side. Publishes the current size, then republishes on every resize.\n * Returns a stop function; the caller must run it on every launch exit path.\n */\nexport function publishTerminalSize(path: string, stdout: NodeJS.WriteStream = process.stdout): () => void {\n\tif (!stdout.isTTY) return () => {};\n\n\tconst write = () => {\n\t\tconst columns = stdout.columns;\n\t\tconst rows = stdout.rows;\n\t\tif (!Number.isInteger(columns) || !Number.isInteger(rows)) return;\n\t\ttry {\n\t\t\twriteFileSync(path, formatSize({ columns, rows }));\n\t\t} catch {\n\t\t\t// A size we cannot publish is not worth failing a launch over.\n\t\t}\n\t};\n\n\twrite();\n\tstdout.on(\"resize\", write);\n\treturn () => {\n\t\tstdout.off(\"resize\", write);\n\t};\n}\n\n/**\n * Child side. Applies the published size to `process.stdout` and keeps applying\n * it as the file changes.\n *\n * `columns` and `rows` are redefined as getters because Node recomputes them\n * from the sandbox's own console on SIGWINCH, which is exactly the stale value\n * this exists to override. The setters swallow those writes rather than throwing.\n */\nexport function applyTerminalSize(\n\tpath: string,\n\tstdout: NodeJS.WriteStream = process.stdout,\n): { stop: () => void; refresh: () => void } {\n\tlet current: TerminalSize | undefined;\n\n\tconst refresh = () => {\n\t\tlet size: TerminalSize | undefined;\n\t\ttry {\n\t\t\tsize = parseTerminalSize(readFileSync(path, \"utf8\"));\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (!size) return;\n\t\tif (current && current.columns === size.columns && current.rows === size.rows) return;\n\t\tcurrent = size;\n\t\tObject.defineProperty(stdout, \"columns\", {\n\t\t\tconfigurable: true,\n\t\t\tget: () => current?.columns,\n\t\t\tset: () => {},\n\t\t});\n\t\tObject.defineProperty(stdout, \"rows\", {\n\t\t\tconfigurable: true,\n\t\t\tget: () => current?.rows,\n\t\t\tset: () => {},\n\t\t});\n\t\tstdout.emit(\"resize\");\n\t};\n\n\trefresh();\n\n\tlet watcher: FSWatcher | undefined;\n\ttry {\n\t\twatcher = watch(path, refresh);\n\t} catch {\n\t\t// Without a watcher the initial size still applies; only live resize is lost.\n\t}\n\n\treturn {\n\t\trefresh,\n\t\tstop: () => {\n\t\t\twatcher?.close();\n\t\t},\n\t};\n}\n"]}