@machina.ai/cell-cli 1.59.0-rc2 → 1.64.0-rc1

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 (327) hide show
  1. package/dist/index.js +11 -14
  2. package/dist/index.js.map +1 -1
  3. package/dist/package.json +3 -3
  4. package/dist/src/acp/acpResume.test.js +50 -0
  5. package/dist/src/acp/acpResume.test.js.map +1 -1
  6. package/dist/src/acp/acpRpcDispatcher.d.ts +1 -1
  7. package/dist/src/acp/acpRpcDispatcher.js +2 -2
  8. package/dist/src/acp/acpRpcDispatcher.js.map +1 -1
  9. package/dist/src/acp/acpRpcDispatcher.test.js +9 -0
  10. package/dist/src/acp/acpRpcDispatcher.test.js.map +1 -1
  11. package/dist/src/acp/acpSession.d.ts +1 -1
  12. package/dist/src/acp/acpSession.js +90 -80
  13. package/dist/src/acp/acpSession.js.map +1 -1
  14. package/dist/src/acp/acpSession.test.js +279 -2
  15. package/dist/src/acp/acpSession.test.js.map +1 -1
  16. package/dist/src/acp/acpSessionManager.d.ts +2 -2
  17. package/dist/src/acp/acpSessionManager.js +165 -73
  18. package/dist/src/acp/acpSessionManager.js.map +1 -1
  19. package/dist/src/acp/acpSessionManager.test.js +204 -1
  20. package/dist/src/acp/acpSessionManager.test.js.map +1 -1
  21. package/dist/src/acp/acpUtils.js +5 -3
  22. package/dist/src/acp/acpUtils.js.map +1 -1
  23. package/dist/src/commands/extensions/validate.js +7 -3
  24. package/dist/src/commands/extensions/validate.js.map +1 -1
  25. package/dist/src/commands/extensions/validate.test.js +51 -0
  26. package/dist/src/commands/extensions/validate.test.js.map +1 -1
  27. package/dist/src/config/config.js +3 -0
  28. package/dist/src/config/config.js.map +1 -1
  29. package/dist/src/config/config.test.js +6 -1
  30. package/dist/src/config/config.test.js.map +1 -1
  31. package/dist/src/config/extension-manager.d.ts +25 -0
  32. package/dist/src/config/extension-manager.js +96 -15
  33. package/dist/src/config/extension-manager.js.map +1 -1
  34. package/dist/src/config/extension-manager.test.js +263 -1
  35. package/dist/src/config/extension-manager.test.js.map +1 -1
  36. package/dist/src/config/extension.test.js +10 -7
  37. package/dist/src/config/extension.test.js.map +1 -1
  38. package/dist/src/config/extensionRegistryClient.js +1 -1
  39. package/dist/src/config/extensionRegistryClient.js.map +1 -1
  40. package/dist/src/config/extensions/consent.js +98 -1
  41. package/dist/src/config/extensions/consent.js.map +1 -1
  42. package/dist/src/config/extensions/consent.test.js +89 -0
  43. package/dist/src/config/extensions/consent.test.js.map +1 -1
  44. package/dist/src/config/extensions/update.js +7 -2
  45. package/dist/src/config/extensions/update.js.map +1 -1
  46. package/dist/src/config/extensions/update.test.js +21 -0
  47. package/dist/src/config/extensions/update.test.js.map +1 -1
  48. package/dist/src/config/mcp/mcpServerEnablement.d.ts +5 -0
  49. package/dist/src/config/mcp/mcpServerEnablement.js +26 -3
  50. package/dist/src/config/mcp/mcpServerEnablement.js.map +1 -1
  51. package/dist/src/config/mcp/mcpServerEnablement.test.js +27 -0
  52. package/dist/src/config/mcp/mcpServerEnablement.test.js.map +1 -1
  53. package/dist/src/config/settings.js +20 -4
  54. package/dist/src/config/settings.js.map +1 -1
  55. package/dist/src/config/settings.test.js +121 -0
  56. package/dist/src/config/settings.test.js.map +1 -1
  57. package/dist/src/config/settingsSchema.d.ts +27 -0
  58. package/dist/src/config/settingsSchema.js +55 -1
  59. package/dist/src/config/settingsSchema.js.map +1 -1
  60. package/dist/src/core/auth.test.js +1 -1
  61. package/dist/src/core/auth.test.js.map +1 -1
  62. package/dist/src/gemini.js +110 -18
  63. package/dist/src/gemini.js.map +1 -1
  64. package/dist/src/gemini_uncaught.test.d.ts +6 -0
  65. package/dist/src/gemini_uncaught.test.js +91 -0
  66. package/dist/src/gemini_uncaught.test.js.map +1 -0
  67. package/dist/src/generated/git-commit.d.ts +2 -2
  68. package/dist/src/generated/git-commit.js +2 -2
  69. package/dist/src/patches/http-proxy-agent.d.ts +4 -1
  70. package/dist/src/patches/http-proxy-agent.js +38 -2
  71. package/dist/src/patches/http-proxy-agent.js.map +1 -1
  72. package/dist/src/patches/https-proxy-agent.d.ts +4 -1
  73. package/dist/src/patches/https-proxy-agent.js +38 -2
  74. package/dist/src/patches/https-proxy-agent.js.map +1 -1
  75. package/dist/src/services/FeedbackService.integration.test.d.ts +6 -0
  76. package/dist/src/services/FeedbackService.integration.test.js +212 -0
  77. package/dist/src/services/FeedbackService.integration.test.js.map +1 -0
  78. package/dist/src/services/FeedbackService.js +62 -6
  79. package/dist/src/services/FeedbackService.js.map +1 -1
  80. package/dist/src/services/FeedbackService.test.d.ts +6 -0
  81. package/dist/src/services/FeedbackService.test.js +282 -0
  82. package/dist/src/services/FeedbackService.test.js.map +1 -0
  83. package/dist/src/services/FileCommandLoader.js +3 -5
  84. package/dist/src/services/FileCommandLoader.js.map +1 -1
  85. package/dist/src/services/FileCommandLoader.test.js +68 -6
  86. package/dist/src/services/FileCommandLoader.test.js.map +1 -1
  87. package/dist/src/test-utils/createExtension.d.ts +1 -1
  88. package/dist/src/test-utils/createExtension.js +15 -1
  89. package/dist/src/test-utils/createExtension.js.map +1 -1
  90. package/dist/src/ui/AppContainer.js +42 -39
  91. package/dist/src/ui/AppContainer.js.map +1 -1
  92. package/dist/src/ui/AppContainer.test.js +30 -0
  93. package/dist/src/ui/AppContainer.test.js.map +1 -1
  94. package/dist/src/ui/auth/AuthDialog.js +11 -3
  95. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  96. package/dist/src/ui/auth/useAuth.test.js +1 -1
  97. package/dist/src/ui/auth/useAuth.test.js.map +1 -1
  98. package/dist/src/ui/commands/bugCommand.js +3 -3
  99. package/dist/src/ui/commands/bugCommand.js.map +1 -1
  100. package/dist/src/ui/commands/bugCommand.test.js +44 -0
  101. package/dist/src/ui/commands/bugCommand.test.js.map +1 -1
  102. package/dist/src/ui/commands/bugMemoryCommand.js +2 -2
  103. package/dist/src/ui/commands/bugMemoryCommand.js.map +1 -1
  104. package/dist/src/ui/commands/bugMemoryCommand.test.js +12 -0
  105. package/dist/src/ui/commands/bugMemoryCommand.test.js.map +1 -1
  106. package/dist/src/ui/commands/statsCommand.js +17 -2
  107. package/dist/src/ui/commands/statsCommand.js.map +1 -1
  108. package/dist/src/ui/commands/statsCommand.test.js +2 -0
  109. package/dist/src/ui/commands/statsCommand.test.js.map +1 -1
  110. package/dist/src/ui/components/AboutBox.js +1 -1
  111. package/dist/src/ui/components/AboutBox.js.map +1 -1
  112. package/dist/src/ui/components/AsciiArt.d.ts +6 -6
  113. package/dist/src/ui/components/AsciiArt.js +6 -6
  114. package/dist/src/ui/components/AskUserDialog.js +7 -2
  115. package/dist/src/ui/components/AskUserDialog.js.map +1 -1
  116. package/dist/src/ui/components/AskUserDialog.test.js +33 -0
  117. package/dist/src/ui/components/AskUserDialog.test.js.map +1 -1
  118. package/dist/src/ui/components/Composer.js +4 -4
  119. package/dist/src/ui/components/Composer.js.map +1 -1
  120. package/dist/src/ui/components/Composer.test.js +50 -2
  121. package/dist/src/ui/components/Composer.test.js.map +1 -1
  122. package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
  123. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  124. package/dist/src/ui/components/InputPrompt.js +16 -10
  125. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  126. package/dist/src/ui/components/InputPrompt.test.js +103 -7
  127. package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
  128. package/dist/src/ui/components/LoadingIndicator.d.ts +1 -0
  129. package/dist/src/ui/components/LoadingIndicator.js +13 -11
  130. package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
  131. package/dist/src/ui/components/LoadingIndicator.test.js +29 -0
  132. package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
  133. package/dist/src/ui/components/MainContent.js +79 -14
  134. package/dist/src/ui/components/MainContent.js.map +1 -1
  135. package/dist/src/ui/components/MainContent.test.js +218 -5
  136. package/dist/src/ui/components/MainContent.test.js.map +1 -1
  137. package/dist/src/ui/components/ModelDialog.js +11 -13
  138. package/dist/src/ui/components/ModelDialog.js.map +1 -1
  139. package/dist/src/ui/components/ModelDialog.test.js +28 -24
  140. package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
  141. package/dist/src/ui/components/ModelStatsDisplay.test.js +2 -2
  142. package/dist/src/ui/components/ProgressBar.js +4 -3
  143. package/dist/src/ui/components/ProgressBar.js.map +1 -1
  144. package/dist/src/ui/components/ProgressBar.test.js +12 -0
  145. package/dist/src/ui/components/ProgressBar.test.js.map +1 -1
  146. package/dist/src/ui/components/RewindViewer.js +2 -1
  147. package/dist/src/ui/components/RewindViewer.js.map +1 -1
  148. package/dist/src/ui/components/RewindViewer.test.js +38 -0
  149. package/dist/src/ui/components/RewindViewer.test.js.map +1 -1
  150. package/dist/src/ui/components/SessionSummaryDisplay.js +28 -2
  151. package/dist/src/ui/components/SessionSummaryDisplay.js.map +1 -1
  152. package/dist/src/ui/components/SessionSummaryDisplay.test.js +41 -2
  153. package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -1
  154. package/dist/src/ui/components/StatsDisplay.d.ts +2 -1
  155. package/dist/src/ui/components/StatsDisplay.js +66 -4
  156. package/dist/src/ui/components/StatsDisplay.js.map +1 -1
  157. package/dist/src/ui/components/StatsDisplay.test.js +120 -3
  158. package/dist/src/ui/components/StatsDisplay.test.js.map +1 -1
  159. package/dist/src/ui/components/StatusRow.d.ts +2 -0
  160. package/dist/src/ui/components/StatusRow.js +11 -6
  161. package/dist/src/ui/components/StatusRow.js.map +1 -1
  162. package/dist/src/ui/components/StatusRow.test.js +158 -0
  163. package/dist/src/ui/components/StatusRow.test.js.map +1 -1
  164. package/dist/src/ui/components/ToolConfirmationQueue.js +4 -2
  165. package/dist/src/ui/components/ToolConfirmationQueue.js.map +1 -1
  166. package/dist/src/ui/components/ToolConfirmationQueue.test.js +30 -0
  167. package/dist/src/ui/components/ToolConfirmationQueue.test.js.map +1 -1
  168. package/dist/src/ui/components/messages/DenseToolMessage.js +5 -5
  169. package/dist/src/ui/components/messages/DenseToolMessage.js.map +1 -1
  170. package/dist/src/ui/components/messages/DenseToolMessage.test.js +13 -0
  171. package/dist/src/ui/components/messages/DenseToolMessage.test.js.map +1 -1
  172. package/dist/src/ui/components/messages/DiffRenderer.d.ts +1 -0
  173. package/dist/src/ui/components/messages/DiffRenderer.js +16 -8
  174. package/dist/src/ui/components/messages/DiffRenderer.js.map +1 -1
  175. package/dist/src/ui/components/messages/DiffRenderer.test.js +42 -0
  176. package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -1
  177. package/dist/src/ui/components/messages/ShellToolMessage.js +4 -3
  178. package/dist/src/ui/components/messages/ShellToolMessage.js.map +1 -1
  179. package/dist/src/ui/components/messages/ToolConfirmationMessage.js +74 -16
  180. package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
  181. package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js +80 -7
  182. package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js.map +1 -1
  183. package/dist/src/ui/components/messages/ToolGroupMessage.js +6 -5
  184. package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
  185. package/dist/src/ui/components/messages/ToolMessage.js +2 -1
  186. package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
  187. package/dist/src/ui/components/messages/ToolResultDisplay.js +11 -8
  188. package/dist/src/ui/components/messages/ToolResultDisplay.js.map +1 -1
  189. package/dist/src/ui/components/messages/ToolShared.js +5 -4
  190. package/dist/src/ui/components/messages/ToolShared.js.map +1 -1
  191. package/dist/src/ui/components/shared/HalfLinePaddedBox.js +4 -2
  192. package/dist/src/ui/components/shared/HalfLinePaddedBox.js.map +1 -1
  193. package/dist/src/ui/components/shared/HalfLinePaddedBox.test.js +8 -0
  194. package/dist/src/ui/components/shared/HalfLinePaddedBox.test.js.map +1 -1
  195. package/dist/src/ui/components/shared/SearchableList.js +1 -1
  196. package/dist/src/ui/components/shared/SearchableList.js.map +1 -1
  197. package/dist/src/ui/components/shared/SearchableList.test.js +13 -0
  198. package/dist/src/ui/components/shared/SearchableList.test.js.map +1 -1
  199. package/dist/src/ui/components/shared/TextInput.js +2 -2
  200. package/dist/src/ui/components/shared/TextInput.js.map +1 -1
  201. package/dist/src/ui/components/views/ExtensionRegistryView.test.js +2 -0
  202. package/dist/src/ui/components/views/ExtensionRegistryView.test.js.map +1 -1
  203. package/dist/src/ui/contexts/KeypressContext.js +20 -2
  204. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  205. package/dist/src/ui/contexts/KeypressContext.test.js +24 -0
  206. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  207. package/dist/src/ui/contexts/SessionContext.js +12 -0
  208. package/dist/src/ui/contexts/SessionContext.js.map +1 -1
  209. package/dist/src/ui/contexts/SessionContext.test.js +5 -0
  210. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -1
  211. package/dist/src/ui/contexts/UIStateContext.d.ts +1 -0
  212. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  213. package/dist/src/ui/emergency-stop.test.d.ts +6 -0
  214. package/dist/src/ui/emergency-stop.test.js +116 -0
  215. package/dist/src/ui/emergency-stop.test.js.map +1 -0
  216. package/dist/src/ui/hooks/atCommandProcessor.d.ts +8 -5
  217. package/dist/src/ui/hooks/atCommandProcessor.js +47 -16
  218. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  219. package/dist/src/ui/hooks/atCommandProcessor.test.js +160 -0
  220. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  221. package/dist/src/ui/hooks/useComposerStatus.d.ts +1 -0
  222. package/dist/src/ui/hooks/useComposerStatus.js +1 -0
  223. package/dist/src/ui/hooks/useComposerStatus.js.map +1 -1
  224. package/dist/src/ui/hooks/useFocus.js +1 -0
  225. package/dist/src/ui/hooks/useFocus.js.map +1 -1
  226. package/dist/src/ui/hooks/useFolderTrust.js +16 -16
  227. package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
  228. package/dist/src/ui/hooks/useFolderTrust.test.js +186 -3
  229. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
  230. package/dist/src/ui/hooks/useGeminiStream.js +21 -15
  231. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  232. package/dist/src/ui/hooks/useGeminiStream.test.js +141 -3
  233. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
  234. package/dist/src/ui/hooks/useLoadingIndicator.d.ts +1 -0
  235. package/dist/src/ui/hooks/useLoadingIndicator.js +1 -0
  236. package/dist/src/ui/hooks/useLoadingIndicator.js.map +1 -1
  237. package/dist/src/ui/hooks/useLoadingIndicator.test.js +2 -0
  238. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  239. package/dist/src/ui/hooks/useQuotaAndFallback.js +2 -2
  240. package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
  241. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +3 -3
  242. package/dist/src/ui/hooks/useSelectionList.js +24 -1
  243. package/dist/src/ui/hooks/useSelectionList.js.map +1 -1
  244. package/dist/src/ui/hooks/useSelectionList.test.js +63 -1
  245. package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
  246. package/dist/src/ui/types.d.ts +2 -1
  247. package/dist/src/ui/types.js.map +1 -1
  248. package/dist/src/ui/utils/CodeColorizer.d.ts +1 -0
  249. package/dist/src/ui/utils/CodeColorizer.js +13 -7
  250. package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
  251. package/dist/src/ui/utils/TableRenderer.js +3 -2
  252. package/dist/src/ui/utils/TableRenderer.js.map +1 -1
  253. package/dist/src/ui/utils/borderStyles.d.ts +1 -0
  254. package/dist/src/ui/utils/borderStyles.js +1 -0
  255. package/dist/src/ui/utils/borderStyles.js.map +1 -1
  256. package/dist/src/ui/utils/fileUtils.d.ts +4 -0
  257. package/dist/src/ui/utils/fileUtils.js +13 -0
  258. package/dist/src/ui/utils/fileUtils.js.map +1 -1
  259. package/dist/src/ui/utils/renderBorder.d.ts +27 -0
  260. package/dist/src/ui/utils/renderBorder.js +46 -0
  261. package/dist/src/ui/utils/renderBorder.js.map +1 -0
  262. package/dist/src/ui/utils/renderBorder.test.d.ts +6 -0
  263. package/dist/src/ui/utils/renderBorder.test.js +74 -0
  264. package/dist/src/ui/utils/renderBorder.test.js.map +1 -0
  265. package/dist/src/ui/utils/rewindFileOps.d.ts +9 -0
  266. package/dist/src/ui/utils/rewindFileOps.js +25 -1
  267. package/dist/src/ui/utils/rewindFileOps.js.map +1 -1
  268. package/dist/src/ui/utils/rewindFileOps.test.js +94 -1
  269. package/dist/src/ui/utils/rewindFileOps.test.js.map +1 -1
  270. package/dist/src/ui/utils/terminalCapabilityManager.js +7 -0
  271. package/dist/src/ui/utils/terminalCapabilityManager.js.map +1 -1
  272. package/dist/src/ui/utils/terminalCapabilityManager.stdin-flow.test.d.ts +6 -0
  273. package/dist/src/ui/utils/terminalCapabilityManager.stdin-flow.test.js +105 -0
  274. package/dist/src/ui/utils/terminalCapabilityManager.stdin-flow.test.js.map +1 -0
  275. package/dist/src/utils/commentJson.js +66 -5
  276. package/dist/src/utils/commentJson.js.map +1 -1
  277. package/dist/src/utils/commentJson.test.js +21 -0
  278. package/dist/src/utils/commentJson.test.js.map +1 -1
  279. package/dist/src/utils/handleAutoUpdate.test.js +2 -2
  280. package/dist/src/utils/persistentState.js +84 -15
  281. package/dist/src/utils/persistentState.js.map +1 -1
  282. package/dist/src/utils/persistentState.test.js +66 -9
  283. package/dist/src/utils/persistentState.test.js.map +1 -1
  284. package/dist/src/utils/persistentStateRecovery.test.d.ts +6 -0
  285. package/dist/src/utils/persistentStateRecovery.test.js +84 -0
  286. package/dist/src/utils/persistentStateRecovery.test.js.map +1 -0
  287. package/dist/src/utils/processUtils.d.ts +10 -1
  288. package/dist/src/utils/processUtils.js +55 -1
  289. package/dist/src/utils/processUtils.js.map +1 -1
  290. package/dist/src/utils/processUtils.test.js +90 -2
  291. package/dist/src/utils/processUtils.test.js.map +1 -1
  292. package/dist/src/utils/relaunch.js +7 -0
  293. package/dist/src/utils/relaunch.js.map +1 -1
  294. package/dist/src/utils/relaunch.test.js +16 -0
  295. package/dist/src/utils/relaunch.test.js.map +1 -1
  296. package/dist/src/utils/retry.d.ts +22 -0
  297. package/dist/src/utils/retry.js +54 -0
  298. package/dist/src/utils/retry.js.map +1 -0
  299. package/dist/src/utils/retry.test.d.ts +6 -0
  300. package/dist/src/utils/retry.test.js +141 -0
  301. package/dist/src/utils/retry.test.js.map +1 -0
  302. package/dist/src/utils/sandbox-macos-permissive-open.sb +36 -1
  303. package/dist/src/utils/sandbox-macos-permissive-proxied.sb +36 -1
  304. package/dist/src/utils/sandbox-macos-profiles.test.js +93 -0
  305. package/dist/src/utils/sandbox-macos-profiles.test.js.map +1 -1
  306. package/dist/src/utils/sandbox-macos-restrictive-open.sb +39 -1
  307. package/dist/src/utils/sandbox-macos-restrictive-proxied.sb +40 -1
  308. package/dist/src/utils/sandbox-macos-strict-open.sb +38 -2
  309. package/dist/src/utils/sandbox-macos-strict-proxied.sb +38 -2
  310. package/dist/src/utils/sandbox.js +149 -36
  311. package/dist/src/utils/sandbox.js.map +1 -1
  312. package/dist/src/utils/sandbox.test.js +410 -2
  313. package/dist/src/utils/sandbox.test.js.map +1 -1
  314. package/dist/src/utils/sandboxBuiltinProfiles.js +198 -8
  315. package/dist/src/utils/sandboxBuiltinProfiles.js.map +1 -1
  316. package/dist/src/utils/sandboxUtils.d.ts +25 -0
  317. package/dist/src/utils/sandboxUtils.js +225 -1
  318. package/dist/src/utils/sandboxUtils.js.map +1 -1
  319. package/dist/src/utils/sandboxUtils.test.js +341 -1
  320. package/dist/src/utils/sandboxUtils.test.js.map +1 -1
  321. package/dist/src/utils/sessionUtils.d.ts +21 -1
  322. package/dist/src/utils/sessionUtils.js +66 -2
  323. package/dist/src/utils/sessionUtils.js.map +1 -1
  324. package/dist/src/utils/sessionUtils.test.js +143 -0
  325. package/dist/src/utils/sessionUtils.test.js.map +1 -1
  326. package/dist/tsconfig.tsbuildinfo +1 -1
  327. package/package.json +3 -3
@@ -118,7 +118,11 @@ describe('sandbox', () => {
118
118
  vi.mocked(os.tmpdir).mockReturnValue('/tmp');
119
119
  vi.mocked(fs.existsSync).mockReturnValue(true);
120
120
  vi.mocked(fs.realpathSync).mockImplementation((p) => p);
121
+ vi.mocked(fs.mkdtempSync).mockImplementation((prefix) => `${prefix}test-tmp`);
122
+ vi.mocked(fs.chmodSync).mockImplementation(() => { });
123
+ vi.mocked(fs.rmSync).mockImplementation(() => { });
121
124
  vi.mocked(execSync).mockReturnValue(Buffer.from(''));
125
+ mockedHomedir.mockReturnValue('/home/user');
122
126
  });
123
127
  afterEach(() => {
124
128
  process.env = originalEnv;
@@ -146,6 +150,73 @@ describe('sandbox', () => {
146
150
  expect.stringContaining('sandbox-macos-permissive-open.sb'),
147
151
  ]), expect.objectContaining({ stdio: 'inherit' }));
148
152
  });
153
+ it('should isolate temporary directory for macOS seatbelt (sandbox-exec)', async () => {
154
+ vi.mocked(os.platform).mockReturnValue('darwin');
155
+ vi.mocked(os.tmpdir).mockReturnValue('/var/folders/test/T');
156
+ vi.mocked(fs.realpathSync).mockImplementation((p) => p);
157
+ vi.mocked(fs.mkdtempSync).mockReturnValue('/var/folders/test/T/cell-sandbox-tmp-123456');
158
+ const config = createMockSandboxConfig({
159
+ command: 'sandbox-exec',
160
+ image: 'some-image',
161
+ });
162
+ const mockSpawnProcess = new EventEmitter();
163
+ mockSpawnProcess.stdout = new EventEmitter();
164
+ mockSpawnProcess.stderr = new EventEmitter();
165
+ vi.mocked(spawn).mockReturnValue(mockSpawnProcess);
166
+ const onSpy = vi.spyOn(process, 'on');
167
+ const offSpy = vi.spyOn(process, 'off');
168
+ const promise = start_sandbox(config, [], undefined, ['arg1']);
169
+ setTimeout(() => {
170
+ mockSpawnProcess.emit('close', 0);
171
+ }, 10);
172
+ await expect(promise).resolves.toBe(0);
173
+ // Verify that an isolated temporary directory was created
174
+ expect(fs.mkdtempSync).toHaveBeenCalledWith(expect.stringContaining(path.join('/var/folders/test/T', 'cell-sandbox-')));
175
+ const spawnCalls = vi.mocked(spawn).mock.calls;
176
+ const spawnArgs = spawnCalls[0]?.[1];
177
+ // Verify that TMP_DIR argument passed to seatbelt is the isolated temp directory, NOT host tmpdir
178
+ expect(spawnArgs).toContain('TMP_DIR=/var/folders/test/T/cell-sandbox-tmp-123456');
179
+ expect(spawnArgs).not.toContain('TMP_DIR=/var/folders/test/T');
180
+ // Verify that TMPDIR environment variable is set in the sandboxed shell execution
181
+ const shCommand = spawnArgs[spawnArgs.indexOf('sh') + 2];
182
+ expect(shCommand).toContain('TMPDIR=');
183
+ expect(shCommand).toContain('/var/folders/test/T/cell-sandbox-tmp-123456');
184
+ // Verify cleanup of the isolated temporary directory
185
+ expect(fs.rmSync).toHaveBeenCalledWith('/var/folders/test/T/cell-sandbox-tmp-123456', expect.objectContaining({ recursive: true, force: true }));
186
+ // Verify that exit and signal cleanup hooks are registered and unregistered
187
+ expect(onSpy).toHaveBeenCalledWith('exit', expect.any(Function));
188
+ expect(onSpy).toHaveBeenCalledWith('SIGINT', expect.any(Function));
189
+ expect(onSpy).toHaveBeenCalledWith('SIGTERM', expect.any(Function));
190
+ expect(offSpy).toHaveBeenCalledWith('exit', expect.any(Function));
191
+ expect(offSpy).toHaveBeenCalledWith('SIGINT', expect.any(Function));
192
+ expect(offSpy).toHaveBeenCalledWith('SIGTERM', expect.any(Function));
193
+ onSpy.mockRestore();
194
+ offSpy.mockRestore();
195
+ });
196
+ it('should safely swallow errors if fs.rmSync fails during cleanup', async () => {
197
+ vi.mocked(os.platform).mockReturnValue('darwin');
198
+ vi.mocked(os.tmpdir).mockReturnValue('/var/folders/test/T');
199
+ vi.mocked(fs.realpathSync).mockImplementation((p) => p);
200
+ vi.mocked(fs.mkdtempSync).mockReturnValue('/var/folders/test/T/cell-sandbox-tmp-error');
201
+ vi.mocked(fs.rmSync).mockImplementation(() => {
202
+ throw new Error('EPERM: operation not permitted');
203
+ });
204
+ const config = createMockSandboxConfig({
205
+ command: 'sandbox-exec',
206
+ image: 'some-image',
207
+ });
208
+ const mockSpawnProcess = new EventEmitter();
209
+ mockSpawnProcess.stdout = new EventEmitter();
210
+ mockSpawnProcess.stderr = new EventEmitter();
211
+ vi.mocked(spawn).mockReturnValue(mockSpawnProcess);
212
+ const promise = start_sandbox(config, [], undefined, ['arg1']);
213
+ setTimeout(() => {
214
+ mockSpawnProcess.emit('close', 0);
215
+ }, 10);
216
+ // Even if fs.rmSync throws, start_sandbox should resolve successfully and not crash
217
+ await expect(promise).resolves.toBe(0);
218
+ expect(fs.rmSync).toHaveBeenCalled();
219
+ });
149
220
  it('should resolve custom seatbelt profile from user home directory', async () => {
150
221
  vi.mocked(os.platform).mockReturnValue('darwin');
151
222
  vi.stubEnv('SEATBELT_PROFILE', 'custom-test');
@@ -330,6 +401,122 @@ describe('sandbox', () => {
330
401
  `SANDBOX=${containerName}`,
331
402
  ]), expect.objectContaining({ stdio: 'inherit' }));
332
403
  });
404
+ it('should not attempt to create a temporary settings directory when homedir is empty', async () => {
405
+ mockedHomedir.mockReturnValue('');
406
+ vi.mocked(os.tmpdir).mockReturnValue('/mock/tmp');
407
+ const config = createMockSandboxConfig({
408
+ command: 'docker',
409
+ image: 'cell-cli-sandbox',
410
+ });
411
+ const mockImageCheckProcess = new EventEmitter();
412
+ mockImageCheckProcess.stdout = new EventEmitter();
413
+ vi.mocked(spawn).mockImplementationOnce(() => {
414
+ setTimeout(() => {
415
+ mockImageCheckProcess.stdout.emit('data', Buffer.from('image-id'));
416
+ mockImageCheckProcess.emit('close', 0);
417
+ }, 1);
418
+ return mockImageCheckProcess;
419
+ });
420
+ const mockSpawnProcess = new EventEmitter();
421
+ mockSpawnProcess.on = vi.fn().mockImplementation((event, cb) => {
422
+ if (event === 'close') {
423
+ setTimeout(() => cb(0), 10);
424
+ }
425
+ return mockSpawnProcess;
426
+ });
427
+ vi.mocked(spawn).mockImplementationOnce(() => mockSpawnProcess);
428
+ await expect(start_sandbox(config, [], undefined, ['arg1'])).resolves.toBe(0);
429
+ // Should only create the sandbox temp directory, not the settings fallback directory
430
+ expect(fs.mkdtempSync).toHaveBeenCalledTimes(1);
431
+ expect(fs.mkdtempSync).toHaveBeenCalledWith(path.join('/mock/tmp', 'cell-sandbox-'));
432
+ expect(fs.writeFileSync).toHaveBeenCalledWith(expect.stringMatching(/settings\.json$/), expect.any(String), { mode: 0o600 });
433
+ const dockerCall = vi.mocked(spawn).mock.calls.find((call) => {
434
+ const args = call[1];
435
+ return args && args.includes('run');
436
+ });
437
+ expect(dockerCall).toBeDefined();
438
+ const dockerArgs = dockerCall[1];
439
+ expect(dockerArgs.some((arg) => arg.includes('.config/gcloud'))).toBe(false);
440
+ });
441
+ it('should not mount gcloud config directory when homedir is empty even if relative path exists', async () => {
442
+ mockedHomedir.mockReturnValue('');
443
+ vi.mocked(os.tmpdir).mockReturnValue('/mock/tmp');
444
+ vi.mocked(fs.existsSync).mockImplementation((p) => {
445
+ if (typeof p === 'string' && p.includes('.config')) {
446
+ return true;
447
+ }
448
+ return false;
449
+ });
450
+ const config = createMockSandboxConfig({
451
+ command: 'docker',
452
+ image: 'cell-cli-sandbox',
453
+ });
454
+ const mockImageCheckProcess = new EventEmitter();
455
+ mockImageCheckProcess.stdout = new EventEmitter();
456
+ vi.mocked(spawn).mockImplementationOnce(() => {
457
+ setTimeout(() => {
458
+ mockImageCheckProcess.stdout.emit('data', Buffer.from('image-id'));
459
+ mockImageCheckProcess.emit('close', 0);
460
+ }, 1);
461
+ return mockImageCheckProcess;
462
+ });
463
+ const mockSpawnProcess = new EventEmitter();
464
+ mockSpawnProcess.on = vi.fn().mockImplementation((event, cb) => {
465
+ if (event === 'close') {
466
+ setTimeout(() => cb(0), 10);
467
+ }
468
+ return mockSpawnProcess;
469
+ });
470
+ vi.mocked(spawn).mockImplementationOnce(() => mockSpawnProcess);
471
+ await expect(start_sandbox(config, [], undefined, ['arg1'])).resolves.toBe(0);
472
+ const dockerCall = vi.mocked(spawn).mock.calls.find((call) => {
473
+ const args = call[1];
474
+ return args && args.includes('run');
475
+ });
476
+ expect(dockerCall).toBeDefined();
477
+ const dockerArgs = dockerCall[1];
478
+ expect(dockerArgs.some((arg) => arg.includes('.config/gcloud'))).toBe(false);
479
+ });
480
+ it('should mount gcloud config directory when homedir is defined and config exists', async () => {
481
+ mockedHomedir.mockReturnValue('/home/user');
482
+ vi.mocked(os.tmpdir).mockReturnValue('/mock/tmp');
483
+ const expectedConfigDir = path.join('/home/user', '.config', 'gcloud');
484
+ vi.mocked(fs.existsSync).mockImplementation((p) => {
485
+ if (typeof p === 'string' && p === expectedConfigDir) {
486
+ return true;
487
+ }
488
+ return false;
489
+ });
490
+ const config = createMockSandboxConfig({
491
+ command: 'docker',
492
+ image: 'cell-cli-sandbox',
493
+ });
494
+ const mockImageCheckProcess = new EventEmitter();
495
+ mockImageCheckProcess.stdout = new EventEmitter();
496
+ vi.mocked(spawn).mockImplementationOnce(() => {
497
+ setTimeout(() => {
498
+ mockImageCheckProcess.stdout.emit('data', Buffer.from('image-id'));
499
+ mockImageCheckProcess.emit('close', 0);
500
+ }, 1);
501
+ return mockImageCheckProcess;
502
+ });
503
+ const mockSpawnProcess = new EventEmitter();
504
+ mockSpawnProcess.on = vi.fn().mockImplementation((event, cb) => {
505
+ if (event === 'close') {
506
+ setTimeout(() => cb(0), 10);
507
+ }
508
+ return mockSpawnProcess;
509
+ });
510
+ vi.mocked(spawn).mockImplementationOnce(() => mockSpawnProcess);
511
+ await expect(start_sandbox(config, [], undefined, ['arg1'])).resolves.toBe(0);
512
+ const dockerCall = vi.mocked(spawn).mock.calls.find((call) => {
513
+ const args = call[1];
514
+ return args && args.includes('run');
515
+ });
516
+ expect(dockerCall).toBeDefined();
517
+ const dockerArgs = dockerCall[1];
518
+ expect(dockerArgs.some((arg) => arg.includes(expectedConfigDir) && arg.endsWith(':ro'))).toBe(true);
519
+ });
333
520
  it('should preserve the integration-test prefix for random container names', async () => {
334
521
  const config = createMockSandboxConfig({
335
522
  command: 'docker',
@@ -464,12 +651,78 @@ describe('sandbox', () => {
464
651
  // The second call is 'docker run ...'
465
652
  expect(spawn).toHaveBeenNthCalledWith(2, 'docker', expect.arrayContaining([
466
653
  'run',
654
+ '--env',
655
+ 'HOME=/home/node',
467
656
  '--volume',
468
657
  '/host/path:/container/path:ro',
469
658
  '--volume',
470
- expect.stringMatching(/[\\/]home[\\/]user[\\/]\.cell-cli/),
659
+ expect.stringMatching(/[\\/]home[\\/]node[\\/]\.cell-cli:rw$/),
471
660
  ]), expect.any(Object));
472
661
  });
662
+ it('should mount an isolated settings directory rather than exposing host credentials', async () => {
663
+ const config = createMockSandboxConfig({
664
+ command: 'docker',
665
+ image: 'cell-cli-sandbox',
666
+ });
667
+ vi.mocked(fs.existsSync).mockReturnValue(true);
668
+ const mockImageCheckProcess = new EventEmitter();
669
+ mockImageCheckProcess.stdout = new EventEmitter();
670
+ vi.mocked(spawn).mockImplementationOnce(() => {
671
+ setTimeout(() => {
672
+ mockImageCheckProcess.stdout.emit('data', Buffer.from('image-id'));
673
+ mockImageCheckProcess.emit('close', 0);
674
+ }, 1);
675
+ return mockImageCheckProcess;
676
+ });
677
+ const mockSpawnProcess = new EventEmitter();
678
+ mockSpawnProcess.on = vi.fn().mockImplementation((event, cb) => {
679
+ if (event === 'close') {
680
+ setTimeout(() => cb(0), 10);
681
+ }
682
+ return mockSpawnProcess;
683
+ });
684
+ vi.mocked(spawn).mockImplementationOnce(() => mockSpawnProcess);
685
+ await start_sandbox(config);
686
+ // Verify that docker run does NOT mount the raw host settings directory directly
687
+ expect(spawn).toHaveBeenNthCalledWith(2, 'docker', expect.not.arrayContaining([
688
+ '/home/user/.cell-cli:/home/node/.cell-cli',
689
+ ]), expect.any(Object));
690
+ // Verify that docker run mounts the isolated sanitized settings directory
691
+ expect(spawn).toHaveBeenNthCalledWith(2, 'docker', expect.arrayContaining([
692
+ '--volume',
693
+ expect.stringMatching(/cell-sandbox.*:[\\/]home[\\/]node[\\/]\.cell-cli:rw$/),
694
+ ]), expect.any(Object));
695
+ // Verify that ephemeral sandbox directory permissions are restricted to owner (0o700)
696
+ expect(fs.chmodSync).toHaveBeenCalledWith(expect.stringContaining('cell-sandbox-'), 0o700);
697
+ });
698
+ it('should tolerate chmod errors on non-POSIX filesystems without crashing', async () => {
699
+ const config = createMockSandboxConfig({
700
+ command: 'docker',
701
+ image: 'cell-cli-sandbox',
702
+ });
703
+ vi.mocked(fs.existsSync).mockReturnValue(true);
704
+ vi.mocked(fs.chmodSync).mockImplementationOnce(() => {
705
+ throw new Error('EPERM: operation not permitted');
706
+ });
707
+ const mockImageCheckProcess = new EventEmitter();
708
+ mockImageCheckProcess.stdout = new EventEmitter();
709
+ vi.mocked(spawn).mockImplementationOnce(() => {
710
+ setTimeout(() => {
711
+ mockImageCheckProcess.stdout.emit('data', Buffer.from('image-id'));
712
+ mockImageCheckProcess.emit('close', 0);
713
+ }, 1);
714
+ return mockImageCheckProcess;
715
+ });
716
+ const mockSpawnProcess = new EventEmitter();
717
+ mockSpawnProcess.on = vi.fn().mockImplementation((event, cb) => {
718
+ if (event === 'close') {
719
+ setTimeout(() => cb(0), 10);
720
+ }
721
+ return mockSpawnProcess;
722
+ });
723
+ vi.mocked(spawn).mockImplementationOnce(() => mockSpawnProcess);
724
+ await expect(start_sandbox(config)).resolves.toBe(0);
725
+ });
473
726
  it('should handle allowedPaths in Docker', async () => {
474
727
  const config = createMockSandboxConfig({
475
728
  command: 'docker',
@@ -497,6 +750,161 @@ describe('sandbox', () => {
497
750
  await start_sandbox(config);
498
751
  expect(spawn).toHaveBeenCalledWith('docker', expect.arrayContaining(['--volume', '/extra/path:/extra/path:ro']), expect.any(Object));
499
752
  });
753
+ it('should reject sensitive host mounts in SANDBOX_MOUNTS with FatalSandboxError', async () => {
754
+ const config = createMockSandboxConfig({
755
+ command: 'docker',
756
+ image: 'cell-cli-sandbox',
757
+ });
758
+ vi.stubEnv('SANDBOX_MOUNTS', '/home/user/.cell-cli:/mnt:rw');
759
+ const mockImageCheckProcess = new EventEmitter();
760
+ mockImageCheckProcess.stdout = new EventEmitter();
761
+ vi.mocked(spawn).mockImplementationOnce(() => {
762
+ setTimeout(() => {
763
+ mockImageCheckProcess.stdout.emit('data', Buffer.from('image-id'));
764
+ mockImageCheckProcess.emit('close', 0);
765
+ }, 1);
766
+ return mockImageCheckProcess;
767
+ });
768
+ await expect(start_sandbox(config)).rejects.toThrow(FatalSandboxError);
769
+ });
770
+ it('should reject running container sandbox from a sensitive working directory with FatalSandboxError', async () => {
771
+ const config = createMockSandboxConfig({
772
+ command: 'docker',
773
+ image: 'cell-cli-sandbox',
774
+ });
775
+ const cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue('/home/user');
776
+ try {
777
+ const mockImageCheckProcess = new EventEmitter();
778
+ mockImageCheckProcess.stdout = new EventEmitter();
779
+ vi.mocked(spawn).mockImplementationOnce(() => {
780
+ setTimeout(() => {
781
+ mockImageCheckProcess.stdout.emit('data', Buffer.from('image-id'));
782
+ mockImageCheckProcess.emit('close', 0);
783
+ }, 1);
784
+ return mockImageCheckProcess;
785
+ });
786
+ const targetWorkdir = path.resolve('/home/user');
787
+ await expect(start_sandbox(config)).rejects.toThrow(new RegExp(`Running sandbox from a sensitive host directory '${targetWorkdir.replace(/\\/g, '\\\\')}' is strictly prohibited`));
788
+ }
789
+ finally {
790
+ cwdSpy.mockRestore();
791
+ }
792
+ });
793
+ it('should reject running container sandbox when cliConfig targetDir is sensitive with FatalSandboxError', async () => {
794
+ const config = createMockSandboxConfig({
795
+ command: 'docker',
796
+ image: 'cell-cli-sandbox',
797
+ });
798
+ const targetDir = path.resolve('/home/user/.cell-cli');
799
+ const mockCliConfig = {
800
+ getTargetDir: vi.fn().mockReturnValue(targetDir),
801
+ getDebugMode: vi.fn().mockReturnValue(false),
802
+ };
803
+ const mockImageCheckProcess = new EventEmitter();
804
+ mockImageCheckProcess.stdout = new EventEmitter();
805
+ vi.mocked(spawn).mockImplementationOnce(() => {
806
+ setTimeout(() => {
807
+ mockImageCheckProcess.stdout.emit('data', Buffer.from('image-id'));
808
+ mockImageCheckProcess.emit('close', 0);
809
+ }, 1);
810
+ return mockImageCheckProcess;
811
+ });
812
+ await expect(start_sandbox(config, [], mockCliConfig)).rejects.toThrow(new RegExp(`Running sandbox from a sensitive host directory '${targetDir.replace(/\\/g, '\\\\')}' is strictly prohibited`));
813
+ });
814
+ it('should reject running macOS seatbelt sandbox from a sensitive working directory with FatalSandboxError', async () => {
815
+ vi.mocked(os.platform).mockReturnValue('darwin');
816
+ const config = createMockSandboxConfig({
817
+ command: 'sandbox-exec',
818
+ image: 'some-image',
819
+ });
820
+ const targetDir = path.resolve('/home/user');
821
+ const cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(targetDir);
822
+ try {
823
+ await expect(start_sandbox(config)).rejects.toThrow(new RegExp(`Running sandbox from a sensitive host directory '${targetDir.replace(/\\/g, '\\\\')}' is strictly prohibited`));
824
+ }
825
+ finally {
826
+ cwdSpy.mockRestore();
827
+ }
828
+ });
829
+ it('should reject running LXC sandbox from a sensitive working directory with FatalSandboxError', async () => {
830
+ const config = createMockSandboxConfig({
831
+ command: 'lxc',
832
+ image: 'cell-sandbox',
833
+ });
834
+ const targetDir = path.resolve('/home/user');
835
+ const cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(targetDir);
836
+ try {
837
+ await expect(start_sandbox(config)).rejects.toThrow(new RegExp(`Running sandbox from a sensitive host directory '${targetDir.replace(/\\/g, '\\\\')}' is strictly prohibited`));
838
+ }
839
+ finally {
840
+ cwdSpy.mockRestore();
841
+ }
842
+ });
843
+ it('should skip sensitive workspace directory in macOS seatbelt includedDirs', async () => {
844
+ vi.mocked(os.platform).mockReturnValue('darwin');
845
+ const config = createMockSandboxConfig({
846
+ command: 'sandbox-exec',
847
+ image: 'some-image',
848
+ });
849
+ const safeWorkspace = path.resolve('/safe/workspace');
850
+ const safeOther = path.resolve('/safe/other');
851
+ const sensitiveUser = path.resolve('/home/user');
852
+ const mockCliConfig = {
853
+ getTargetDir: vi.fn().mockReturnValue(safeWorkspace),
854
+ getDebugMode: vi.fn().mockReturnValue(false),
855
+ getWorkspaceContext: vi.fn().mockReturnValue({
856
+ getDirectories: vi
857
+ .fn()
858
+ .mockReturnValue([safeWorkspace, sensitiveUser, safeOther]),
859
+ }),
860
+ };
861
+ const mockSpawnProcess = new EventEmitter();
862
+ mockSpawnProcess.stdout = new EventEmitter();
863
+ mockSpawnProcess.stderr = new EventEmitter();
864
+ vi.mocked(spawn).mockReturnValue(mockSpawnProcess);
865
+ const promise = start_sandbox(config, [], mockCliConfig);
866
+ setTimeout(() => mockSpawnProcess.emit('close', 0), 10);
867
+ await promise;
868
+ const spawnArgs = vi.mocked(spawn).mock.calls[0][1];
869
+ expect(spawnArgs).toContain(`INCLUDE_DIR_0=${safeOther}`);
870
+ const includeDirs = spawnArgs.filter((arg) => arg.startsWith('INCLUDE_DIR_'));
871
+ expect(includeDirs.some((arg) => arg.includes(sensitiveUser))).toBe(false);
872
+ });
873
+ it('should ignore sensitive paths in allowedPaths', async () => {
874
+ const config = createMockSandboxConfig({
875
+ command: 'docker',
876
+ image: 'cell-cli-sandbox',
877
+ allowedPaths: ['/home/user/.cell-cli', '/safe/extra/path'],
878
+ });
879
+ vi.mocked(fs.existsSync).mockReturnValue(true);
880
+ const mockImageCheckProcess = new EventEmitter();
881
+ mockImageCheckProcess.stdout = new EventEmitter();
882
+ vi.mocked(spawn).mockImplementationOnce(() => {
883
+ setTimeout(() => {
884
+ mockImageCheckProcess.stdout.emit('data', Buffer.from('image-id'));
885
+ mockImageCheckProcess.emit('close', 0);
886
+ }, 1);
887
+ return mockImageCheckProcess;
888
+ });
889
+ const mockSpawnProcess = new EventEmitter();
890
+ mockSpawnProcess.on = vi.fn().mockImplementation((event, cb) => {
891
+ if (event === 'close') {
892
+ setTimeout(() => cb(0), 10);
893
+ }
894
+ return mockSpawnProcess;
895
+ });
896
+ vi.mocked(spawn).mockImplementationOnce(() => mockSpawnProcess);
897
+ await start_sandbox(config);
898
+ const runCalls = vi
899
+ .mocked(spawn)
900
+ .mock.calls.filter((call) => call[0] === 'docker' &&
901
+ Array.isArray(call[1]) &&
902
+ call[1].includes('run'));
903
+ expect(runCalls.length).toBe(1);
904
+ const runArgs = runCalls[0][1];
905
+ expect(runArgs).toContain('/safe/extra/path:/safe/extra/path:ro');
906
+ expect(runArgs.some((arg) => typeof arg === 'string' && arg.includes('/home/user/.cell-cli:'))).toBe(false);
907
+ });
500
908
  it('should handle networkAccess: false in Docker', async () => {
501
909
  const config = createMockSandboxConfig({
502
910
  command: 'docker',
@@ -606,7 +1014,7 @@ describe('sandbox', () => {
606
1014
  });
607
1015
  vi.mocked(spawn).mockImplementationOnce(() => mockSpawnProcess);
608
1016
  await start_sandbox(config);
609
- expect(spawn).toHaveBeenCalledWith('docker', expect.arrayContaining(['--user', 'root', '--env', 'HOME=/home/user']), expect.any(Object));
1017
+ expect(spawn).toHaveBeenCalledWith('docker', expect.arrayContaining(['--user', 'root', '--env', 'HOME=/home/node']), expect.any(Object));
610
1018
  // Check that the entrypoint command includes the defensive useradd check
611
1019
  const args = vi.mocked(spawn).mock.calls[1][1];
612
1020
  const entrypointCmd = args[args.length - 1];