windows-development-sandbox 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (413) hide show
  1. package/config.example.json +10 -0
  2. package/dist/app/context.d.ts +8 -0
  3. package/dist/app/context.js +12 -0
  4. package/dist/app/features/defs.d.ts +322 -0
  5. package/dist/app/features/defs.js +28 -0
  6. package/dist/app/features/sandbox/index.d.ts +2 -0
  7. package/dist/app/features/sandbox/index.js +3 -0
  8. package/dist/app/features/sandbox/sandbox.def.d.ts +193 -0
  9. package/dist/app/features/sandbox/sandbox.def.js +245 -0
  10. package/dist/app/features/sandbox/sandbox.run.d.ts +72 -0
  11. package/dist/app/features/sandbox/sandbox.run.js +98 -0
  12. package/dist/app/features/sessions/index.d.ts +2 -0
  13. package/dist/app/features/sessions/index.js +3 -0
  14. package/dist/app/features/sessions/sessions.def.d.ts +141 -0
  15. package/dist/app/features/sessions/sessions.def.js +176 -0
  16. package/dist/app/features/sessions/sessions.run.d.ts +39 -0
  17. package/dist/app/features/sessions/sessions.run.js +58 -0
  18. package/dist/{cli/menu/settings/categories.d.ts → app/features/settings/categories.def.d.ts} +2 -0
  19. package/dist/{cli/menu/settings/categories.js → app/features/settings/categories.def.js} +24 -5
  20. package/dist/app/features/settings/index.d.ts +5 -0
  21. package/dist/app/features/settings/index.js +6 -0
  22. package/dist/{cli/menu/settings/config-file.d.ts → app/features/settings/settings-file.d.ts} +1 -1
  23. package/dist/{cli/menu/settings/config-file.js → app/features/settings/settings-file.js} +1 -1
  24. package/dist/app/features/settings/settings-rows.d.ts +35 -0
  25. package/dist/app/features/settings/settings-rows.js +94 -0
  26. package/dist/app/features/settings/settings.def.d.ts +157 -0
  27. package/dist/app/features/settings/settings.def.js +179 -0
  28. package/dist/app/features/settings/settings.run.d.ts +67 -0
  29. package/dist/app/features/settings/settings.run.js +84 -0
  30. package/dist/app/kernel/define.d.ts +143 -0
  31. package/dist/app/kernel/define.js +42 -0
  32. package/dist/app/kernel/errors.d.ts +34 -0
  33. package/dist/app/kernel/errors.js +45 -0
  34. package/dist/app/kernel/fields.d.ts +70 -0
  35. package/dist/app/kernel/fields.js +243 -0
  36. package/dist/app/kernel/index.d.ts +8 -0
  37. package/dist/app/kernel/index.js +10 -0
  38. package/dist/app/kernel/io.d.ts +193 -0
  39. package/dist/app/kernel/io.js +126 -0
  40. package/dist/app/kernel/rpc.d.ts +156 -0
  41. package/dist/app/kernel/rpc.js +47 -0
  42. package/dist/app/kernel/text.d.ts +6 -0
  43. package/dist/app/kernel/text.js +57 -0
  44. package/dist/app/kernel/view.d.ts +146 -0
  45. package/dist/app/kernel/view.js +28 -0
  46. package/dist/app/registry.d.ts +10 -0
  47. package/dist/app/registry.js +50 -0
  48. package/dist/cli/args.d.ts +5 -0
  49. package/dist/cli/args.js +6 -0
  50. package/dist/cli/commands/engine.d.ts +2 -0
  51. package/dist/cli/commands/engine.js +60 -0
  52. package/dist/cli/commands/index.d.ts +1 -1
  53. package/dist/cli/commands/index.js +1 -1
  54. package/dist/cli/commands/launch/launch.js +47 -7
  55. package/dist/cli/commands/launch/prepare.js +3 -3
  56. package/dist/cli/commands/launch/running.js +2 -1
  57. package/dist/cli/commands/launch/summary.d.ts +2 -0
  58. package/dist/cli/commands/maintenance/config-share.d.ts +12 -0
  59. package/dist/cli/commands/maintenance/config-share.js +80 -0
  60. package/dist/cli/commands/maintenance/config.js +8 -82
  61. package/dist/cli/commands/maintenance/logs.js +14 -0
  62. package/dist/cli/commands/registry.js +3 -7
  63. package/dist/cli/commands/repair/checks/contained.d.ts +2 -0
  64. package/dist/cli/commands/repair/checks/contained.js +139 -0
  65. package/dist/cli/commands/repair/checks/hcs.d.ts +7 -0
  66. package/dist/cli/commands/repair/checks/hcs.js +28 -0
  67. package/dist/cli/commands/repair/checks/index.js +4 -0
  68. package/dist/cli/commands/repair/checks/pc.js +32 -1
  69. package/dist/cli/commands/repair/checks/sandbox.js +16 -1
  70. package/dist/cli/commands/repair/checks/vpn.js +34 -9
  71. package/dist/cli/commands/repair/env.js +2 -1
  72. package/dist/cli/commands/sessions/index.d.ts +0 -4
  73. package/dist/cli/commands/sessions/index.js +3 -7
  74. package/dist/cli/commands/shared-use.d.ts +5 -2
  75. package/dist/cli/commands/shared-use.js +11 -8
  76. package/dist/cli/commands/storage/contained.d.ts +3 -0
  77. package/dist/cli/commands/storage/contained.js +179 -0
  78. package/dist/cli/commands/storage/offer.js +7 -1
  79. package/dist/cli/commands/storage/storage.js +10 -2
  80. package/dist/cli/commands/user-config.js +1 -1
  81. package/dist/cli/commands/vpn.js +19 -3
  82. package/dist/cli/commands/web.js +2 -0
  83. package/dist/cli/index.js +2 -1
  84. package/dist/cli/manifest.d.ts +72 -104
  85. package/dist/cli/manifest.js +64 -39
  86. package/dist/cli/menu/add-agent.d.ts +1 -1
  87. package/dist/cli/menu/add-agent.js +1 -1
  88. package/dist/cli/menu/apps.d.ts +7 -1
  89. package/dist/cli/menu/apps.js +36 -0
  90. package/dist/cli/menu/command-fields.d.ts +1 -1
  91. package/dist/cli/menu/command-form.js +2 -2
  92. package/dist/cli/menu/io-menu.d.ts +29 -0
  93. package/dist/cli/menu/io-menu.js +126 -0
  94. package/dist/cli/menu/live.d.ts +27 -0
  95. package/dist/cli/menu/live.js +69 -0
  96. package/dist/cli/menu/memory.d.ts +19 -0
  97. package/dist/cli/menu/memory.js +53 -0
  98. package/dist/cli/menu/menu.d.ts +10 -1
  99. package/dist/cli/menu/menu.js +27 -9
  100. package/dist/cli/menu/pages/agents-page.d.ts +3 -1
  101. package/dist/cli/menu/pages/agents-page.js +12 -5
  102. package/dist/cli/menu/pages/apps-page.d.ts +3 -1
  103. package/dist/cli/menu/pages/apps-page.js +19 -2
  104. package/dist/cli/menu/pages/list-page.d.ts +27 -0
  105. package/dist/cli/menu/pages/list-page.js +109 -7
  106. package/dist/cli/menu/pages/maintenance-page.d.ts +0 -5
  107. package/dist/cli/menu/pages/maintenance-page.js +17 -19
  108. package/dist/cli/menu/pages/sandbox-page.d.ts +4 -6
  109. package/dist/cli/menu/pages/sandbox-page.js +26 -118
  110. package/dist/cli/menu/pages/scopes.d.ts +1 -1
  111. package/dist/cli/menu/pages/sessions-page.d.ts +5 -11
  112. package/dist/cli/menu/pages/sessions-page.js +19 -88
  113. package/dist/cli/menu/pages/settings-page.d.ts +2 -10
  114. package/dist/cli/menu/pages/settings-page.js +64 -94
  115. package/dist/cli/menu/render-view.d.ts +28 -0
  116. package/dist/cli/menu/render-view.js +112 -0
  117. package/dist/cli/menu/settings/{path-kind.d.ts → ask-path.d.ts} +1 -7
  118. package/dist/cli/menu/settings/ask-path.js +14 -0
  119. package/dist/cli/menu/settings/collections/collection-editor.d.ts +1 -1
  120. package/dist/cli/menu/settings/collections/string-list-editor.d.ts +1 -2
  121. package/dist/cli/menu/settings/collections/string-list-editor.js +1 -1
  122. package/dist/cli/menu/settings/draft.d.ts +1 -1
  123. package/dist/cli/menu/settings/format.d.ts +1 -3
  124. package/dist/cli/menu/settings/format.js +6 -36
  125. package/dist/cli/menu/settings/index.d.ts +1 -3
  126. package/dist/cli/menu/settings/index.js +3 -3
  127. package/dist/cli/menu/settings/object-editor.d.ts +1 -1
  128. package/dist/cli/menu/settings/share.d.ts +36 -0
  129. package/dist/cli/menu/settings/share.js +131 -0
  130. package/dist/cli/menu/settings/value-editor.d.ts +5 -1
  131. package/dist/cli/menu/settings/value-editor.js +57 -54
  132. package/dist/cli/menu/several.js +1 -1
  133. package/dist/cli/menu/share-settings.d.ts +21 -0
  134. package/dist/cli/menu/share-settings.js +112 -0
  135. package/dist/cli/menu/shell.d.ts +21 -5
  136. package/dist/cli/menu/shell.js +31 -6
  137. package/dist/cli/menu/views.d.ts +7 -0
  138. package/dist/cli/menu/views.js +11 -0
  139. package/dist/cli/menu/vpn-login.js +8 -0
  140. package/dist/cli/operations/handler.d.ts +6 -0
  141. package/dist/cli/operations/handler.js +61 -0
  142. package/dist/cli/operations/index.d.ts +4 -0
  143. package/dist/cli/operations/index.js +5 -0
  144. package/dist/cli/operations/input.d.ts +6 -0
  145. package/dist/cli/operations/input.js +37 -0
  146. package/dist/cli/operations/io-cli.d.ts +7 -0
  147. package/dist/cli/operations/io-cli.js +62 -0
  148. package/dist/cli/operations/spec.d.ts +18 -0
  149. package/dist/cli/operations/spec.js +108 -0
  150. package/dist/cli/output/colors.d.ts +2 -2
  151. package/dist/cli/output/colors.js +2 -11
  152. package/dist/cli/output/index.d.ts +1 -0
  153. package/dist/cli/output/index.js +1 -0
  154. package/dist/cli/output/print.d.ts +3 -1
  155. package/dist/cli/output/print.js +7 -3
  156. package/dist/cli/output/progress.d.ts +15 -0
  157. package/dist/cli/output/progress.js +44 -0
  158. package/dist/cli/output/render-view.d.ts +4 -0
  159. package/dist/cli/output/render-view.js +63 -0
  160. package/dist/cli/services/agent-commands-service.d.ts +2 -0
  161. package/dist/cli/services/agent-commands-service.js +11 -2
  162. package/dist/cli/services/logs-service.js +4 -3
  163. package/dist/cli/tui/app.d.ts +6 -1
  164. package/dist/cli/tui/app.js +26 -4
  165. package/dist/cli/tui/list/list-screen.d.ts +14 -0
  166. package/dist/cli/tui/list/list-screen.js +64 -36
  167. package/dist/cli/tui/list/list-types.d.ts +2 -0
  168. package/dist/cli/tui/terminal.d.ts +7 -0
  169. package/dist/cli/tui/terminal.js +30 -10
  170. package/dist/core/config/defaults.js +8 -0
  171. package/dist/core/schema/config.d.ts +13 -0
  172. package/dist/core/schema/config.js +16 -1
  173. package/dist/core/schema/harness.d.ts +1 -0
  174. package/dist/core/schema/harness.js +5 -0
  175. package/dist/core/schema/sandbox.d.ts +6 -0
  176. package/dist/core/schema/sandbox.js +20 -3
  177. package/dist/core/schema/vpn.d.ts +2 -1
  178. package/dist/core/schema/vpn.js +3 -3
  179. package/dist/core/types.d.ts +23 -5
  180. package/dist/core/util/fs.d.ts +2 -0
  181. package/dist/core/util/fs.js +9 -0
  182. package/dist/core/util/index.d.ts +1 -1
  183. package/dist/core/util/index.js +1 -1
  184. package/dist/guest/agents/agent-files.d.ts +5 -0
  185. package/dist/guest/agents/agent-files.js +4 -0
  186. package/dist/guest/agents/local-state.d.ts +2 -0
  187. package/dist/guest/agents/local-state.js +21 -1
  188. package/dist/guest/boot/boot.js +2 -1
  189. package/dist/guest/daemon/daemon.js +4 -0
  190. package/dist/guest/daemon/sessions.d.ts +5 -0
  191. package/dist/guest/daemon/sessions.js +17 -3
  192. package/dist/guest/entries/daemon.js +3 -0
  193. package/dist/guest/governor/decide.d.ts +61 -0
  194. package/dist/guest/governor/decide.js +182 -0
  195. package/dist/guest/governor/governor.d.ts +42 -0
  196. package/dist/guest/governor/governor.js +214 -0
  197. package/dist/guest/governor/index.d.ts +2 -0
  198. package/dist/guest/governor/index.js +2 -0
  199. package/dist/guest/runtime/csc.d.ts +1 -0
  200. package/dist/guest/runtime/csc.js +1 -0
  201. package/dist/guest/sessions/viewer.js +3 -1
  202. package/dist/guest/toolchains/native/rust-extras.js +4 -2
  203. package/dist/guest/vpn/vpn.js +69 -21
  204. package/dist/guest/vpn/wireguard.d.ts +32 -0
  205. package/dist/guest/vpn/wireguard.js +132 -0
  206. package/dist/host/agent-commands/commands.d.ts +4 -0
  207. package/dist/host/agent-commands/commands.js +31 -7
  208. package/dist/host/audit/log.d.ts +1 -1
  209. package/dist/host/channel/attach.d.ts +3 -1
  210. package/dist/host/channel/attach.js +5 -2
  211. package/dist/host/channel/channel.d.ts +2 -0
  212. package/dist/host/channel/channel.js +4 -0
  213. package/dist/host/engines/engine.d.ts +12 -0
  214. package/dist/host/engines/engine.js +1 -0
  215. package/dist/host/engines/hcs/check.d.ts +25 -0
  216. package/dist/host/engines/hcs/check.js +95 -0
  217. package/dist/host/engines/hcs/documents.d.ts +44 -0
  218. package/dist/host/engines/hcs/documents.js +155 -0
  219. package/dist/host/engines/hcs/engine.d.ts +39 -0
  220. package/dist/host/engines/hcs/engine.js +171 -0
  221. package/dist/host/engines/hcs/helper.d.ts +43 -0
  222. package/dist/host/engines/hcs/helper.js +173 -0
  223. package/dist/host/engines/hcs/images.d.ts +37 -0
  224. package/dist/host/engines/hcs/images.js +152 -0
  225. package/dist/host/engines/index.d.ts +7 -0
  226. package/dist/host/engines/index.js +5 -0
  227. package/dist/host/engines/registry.d.ts +16 -0
  228. package/dist/host/engines/registry.js +30 -0
  229. package/dist/host/engines/types.d.ts +58 -0
  230. package/dist/host/engines/types.js +1 -0
  231. package/dist/host/engines/windows-sandbox/engine.d.ts +16 -0
  232. package/dist/host/engines/windows-sandbox/engine.js +63 -0
  233. package/dist/host/engines/windows-sandbox/wsb-text.d.ts +2 -0
  234. package/dist/host/engines/windows-sandbox/wsb-text.js +29 -0
  235. package/dist/host/launch/generate.d.ts +5 -1
  236. package/dist/host/launch/generate.js +50 -8
  237. package/dist/host/launch/index.d.ts +3 -1
  238. package/dist/host/launch/index.js +3 -1
  239. package/dist/host/launch/launch-plan.d.ts +13 -0
  240. package/dist/host/launch/launch-plan.js +26 -0
  241. package/dist/host/launch/mappings.js +2 -1
  242. package/dist/host/launch/risk-notes.d.ts +10 -0
  243. package/dist/host/launch/risk-notes.js +18 -0
  244. package/dist/host/launch/session-file.d.ts +2 -0
  245. package/dist/host/launch/session-file.js +7 -1
  246. package/dist/host/launch/wsb-file.d.ts +28 -4
  247. package/dist/host/launch/wsb-file.js +61 -42
  248. package/dist/host/life/crash-report.d.ts +27 -1
  249. package/dist/host/life/crash-report.js +173 -7
  250. package/dist/host/life/host-memory.d.ts +40 -0
  251. package/dist/host/life/host-memory.js +137 -0
  252. package/dist/host/life/index.d.ts +2 -0
  253. package/dist/host/life/index.js +2 -0
  254. package/dist/host/life/stop-events.d.ts +2 -0
  255. package/dist/host/life/stop-events.js +20 -3
  256. package/dist/host/life/stop-warning.js +1 -1
  257. package/dist/host/life/watchdog.d.ts +54 -0
  258. package/dist/host/life/watchdog.js +108 -0
  259. package/dist/host/native.d.ts +3 -2
  260. package/dist/host/native.js +22 -3
  261. package/dist/host/profile/claude-login.js +3 -2
  262. package/dist/host/sandbox/contained-deps.d.ts +2 -0
  263. package/dist/host/sandbox/contained-deps.js +17 -0
  264. package/dist/host/sandbox/index.d.ts +2 -0
  265. package/dist/host/sandbox/index.js +2 -0
  266. package/dist/host/sandbox/platform.d.ts +2 -0
  267. package/dist/host/sandbox/platform.js +7 -7
  268. package/dist/host/sandbox/tasklist.d.ts +13 -0
  269. package/dist/host/sandbox/tasklist.js +47 -0
  270. package/dist/host/secrets/launch-secrets.js +2 -1
  271. package/dist/host/sharing/current.js +2 -6
  272. package/dist/host/storage/contained/check.d.ts +9 -0
  273. package/dist/host/storage/contained/check.js +37 -0
  274. package/dist/host/storage/contained/contained-storage.d.ts +66 -0
  275. package/dist/host/storage/contained/contained-storage.js +168 -0
  276. package/dist/host/storage/contained/disk-port.d.ts +43 -0
  277. package/dist/host/storage/contained/disk-port.js +122 -0
  278. package/dist/host/storage/contained/index.d.ts +9 -0
  279. package/dist/host/storage/contained/index.js +8 -0
  280. package/dist/host/storage/contained/layout.d.ts +28 -0
  281. package/dist/host/storage/contained/layout.js +45 -0
  282. package/dist/host/storage/contained/resolve.d.ts +4 -0
  283. package/dist/host/storage/contained/resolve.js +30 -0
  284. package/dist/host/storage/contained/setting.d.ts +1 -0
  285. package/dist/host/storage/contained/setting.js +5 -0
  286. package/dist/host/storage/contained/state.d.ts +89 -0
  287. package/dist/host/storage/contained/state.js +166 -0
  288. package/dist/host/storage/contained/transfer.d.ts +24 -0
  289. package/dist/host/storage/contained/transfer.js +121 -0
  290. package/dist/host/storage/index.d.ts +2 -0
  291. package/dist/host/storage/index.js +2 -0
  292. package/dist/host/storage/providers/contained.d.ts +15 -0
  293. package/dist/host/storage/providers/contained.js +39 -0
  294. package/dist/host/storage/providers/folder.d.ts +12 -0
  295. package/dist/host/storage/providers/folder.js +20 -0
  296. package/dist/host/storage/providers/index.d.ts +4 -0
  297. package/dist/host/storage/providers/index.js +3 -0
  298. package/dist/host/storage/providers/registry.d.ts +9 -0
  299. package/dist/host/storage/providers/registry.js +14 -0
  300. package/dist/host/storage/providers/types.d.ts +33 -0
  301. package/dist/host/storage/providers/types.js +1 -0
  302. package/dist/host/tools/conpty.js +3 -2
  303. package/dist/host/tools/windows-terminal.js +8 -7
  304. package/dist/host/vpn/index.d.ts +1 -1
  305. package/dist/host/vpn/index.js +1 -1
  306. package/dist/host/vpn/prepare.d.ts +2 -0
  307. package/dist/host/vpn/prepare.js +63 -2
  308. package/dist/host/web/browser.d.ts +2 -0
  309. package/dist/host/web/browser.js +14 -1
  310. package/dist/host/web/index.d.ts +3 -2
  311. package/dist/host/web/index.js +3 -2
  312. package/dist/host/web/rpc.d.ts +24 -0
  313. package/dist/host/web/rpc.js +149 -0
  314. package/dist/host/web/runner.d.ts +2 -0
  315. package/dist/host/web/runner.js +1 -2
  316. package/dist/host/web/server.d.ts +7 -0
  317. package/dist/host/web/server.js +23 -3
  318. package/dist/runtime/agent.js +69 -8
  319. package/dist/runtime/daemon.js +1058 -624
  320. package/dist/runtime/db.js +69 -8
  321. package/dist/runtime/main.js +624 -182
  322. package/dist/runtime/netproxy.js +69 -8
  323. package/dist/runtime/view.js +87 -13
  324. package/dist/selftest/agent-paths.test.js +11 -1
  325. package/dist/selftest/app.test.d.ts +2 -0
  326. package/dist/selftest/app.test.js +324 -0
  327. package/dist/selftest/channel.test.js +13 -0
  328. package/dist/selftest/cli.test.js +2 -1
  329. package/dist/selftest/config.test.js +59 -0
  330. package/dist/selftest/contained.test.d.ts +2 -0
  331. package/dist/selftest/contained.test.js +544 -0
  332. package/dist/selftest/credentials.test.js +1 -1
  333. package/dist/selftest/engine.test.d.ts +2 -0
  334. package/dist/selftest/engine.test.js +127 -0
  335. package/dist/selftest/hcs.test.d.ts +2 -0
  336. package/dist/selftest/hcs.test.js +482 -0
  337. package/dist/selftest/heavy-load.test.d.ts +2 -0
  338. package/dist/selftest/heavy-load.test.js +329 -0
  339. package/dist/selftest/index.js +12 -0
  340. package/dist/selftest/layout.test.js +12 -0
  341. package/dist/selftest/menu-perf.test.d.ts +2 -0
  342. package/dist/selftest/menu-perf.test.js +248 -0
  343. package/dist/selftest/menu-screens.js +4 -2
  344. package/dist/selftest/menu.test.js +36 -8
  345. package/dist/selftest/support/engine-cases.d.ts +8 -0
  346. package/dist/selftest/support/engine-cases.js +65 -0
  347. package/dist/selftest/support/engine-golden.d.ts +3 -0
  348. package/dist/selftest/support/engine-golden.js +66 -0
  349. package/dist/selftest/support/fixtures.d.ts +7 -1
  350. package/dist/selftest/support/hcs-probes.d.ts +17 -0
  351. package/dist/selftest/support/hcs-probes.js +41 -0
  352. package/dist/selftest/vpn.test.js +136 -3
  353. package/dist/selftest/web.test.js +101 -3
  354. package/dist/shared/channel/run-dir.d.ts +4 -1
  355. package/dist/shared/channel/run-dir.js +7 -1
  356. package/dist/shared/channel/schema.d.ts +35 -1
  357. package/dist/shared/channel/schema.js +27 -0
  358. package/dist/shared/channel/secrets.d.ts +1 -0
  359. package/dist/shared/channel/secrets.js +6 -0
  360. package/dist/shared/memory/index.d.ts +1 -0
  361. package/dist/shared/memory/index.js +1 -0
  362. package/dist/shared/memory/policy.d.ts +33 -0
  363. package/dist/shared/memory/policy.js +55 -0
  364. package/dist/shared/pty/output-filter.d.ts +14 -1
  365. package/dist/shared/pty/output-filter.js +22 -4
  366. package/dist/shared/pty/terminal.d.ts +4 -1
  367. package/dist/shared/pty/terminal.js +4 -2
  368. package/dist/shared/system.d.ts +1 -0
  369. package/dist/shared/system.js +1 -0
  370. package/dist/shared/vpn/download.d.ts +16 -0
  371. package/dist/shared/vpn/download.js +16 -0
  372. package/dist/shared/vpn/wireguard.d.ts +51 -0
  373. package/dist/shared/vpn/wireguard.js +227 -0
  374. package/dist/sync/sync-cycle.js +5 -5
  375. package/dist/sync/watcher.d.ts +13 -0
  376. package/dist/sync/watcher.js +92 -3
  377. package/dist/web/index.html +4 -3
  378. package/dist/web/main-NRRS5O6Z.js +157 -0
  379. package/dist/web/main-T5DMCHYV.css +1 -0
  380. package/native/csharp/HcsHost/AssemblyInfo.cs +14 -0
  381. package/native/csharp/HcsHost/Guard.cs +100 -0
  382. package/native/csharp/HcsHost/Hcs.cs +256 -0
  383. package/native/csharp/HcsHost/Json.cs +78 -0
  384. package/native/csharp/HcsHost/Program.cs +185 -0
  385. package/native/csharp/HcsHost/VirtualDisk.cs +95 -0
  386. package/native/csharp/HostTools/AssemblyInfo.cs +14 -0
  387. package/native/csharp/HostTools/MemoryStatus.cs +107 -0
  388. package/native/csharp/HostTools/Program.cs +58 -17
  389. package/native/scripts/check-contained.ps1 +28 -0
  390. package/native/scripts/contained-common.ps1 +241 -0
  391. package/native/scripts/dismount-contained.ps1 +66 -0
  392. package/native/scripts/memory-status.ps1 +34 -0
  393. package/native/scripts/mount-contained.ps1 +139 -0
  394. package/native/scripts/run-elevated.ps1 +17 -4
  395. package/native/scripts/stop-events.ps1 +23 -8
  396. package/package.json +9 -2
  397. package/sandbox/csharp/MemGov.cs +398 -0
  398. package/sandbox/csharp/PtyHost.cs +111 -5
  399. package/schemas/config.schema.json +60 -6
  400. package/schemas/sandbox-config.schema.json +3 -3
  401. package/dist/cli/commands/sessions/control.d.ts +0 -4
  402. package/dist/cli/commands/sessions/control.js +0 -66
  403. package/dist/cli/commands/sessions/list.d.ts +0 -2
  404. package/dist/cli/commands/sessions/list.js +0 -48
  405. package/dist/cli/commands/sessions/status.d.ts +0 -4
  406. package/dist/cli/commands/sessions/status.js +0 -114
  407. package/dist/cli/commands/sessions/window.d.ts +0 -3
  408. package/dist/cli/commands/sessions/window.js +0 -18
  409. package/dist/cli/menu/settings/path-kind.js +0 -44
  410. package/dist/web/main-CXESL347.js +0 -45
  411. package/dist/web/main-VLR7DVQ4.css +0 -1
  412. /package/dist/{cli/menu/settings → app/kernel}/schema-node.d.ts +0 -0
  413. /package/dist/{cli/menu/settings → app/kernel}/schema-node.js +0 -0
@@ -3,13 +3,15 @@
3
3
  // restart that adds it is offered (restart.ts).
4
4
  import path from 'node:path';
5
5
  import _ from 'lodash';
6
- import { generateSandbox } from '../../../host/launch/index.js';
6
+ import { openEngine } from '../../../host/engines/index.js';
7
+ import { containedMappings, containedPaths, providerFor, setNameOf, } from '../../../host/storage/index.js';
8
+ import { generateSandbox, GUEST, vgpuSource, vgpuWarning } from '../../../host/launch/index.js';
7
9
  import { findSecretFiles } from '../../../host/profile/index.js';
8
10
  import { RepoGuardStore } from '../../../host/guard/index.js';
9
11
  import { hostPublicIp, prepareVpn, readVpnLine, vpnNotes } from '../../../host/vpn/index.js';
10
- import { assertCanLaunch, isSandboxRunning, spawnDetached, startSandbox, confirmSandboxStarted, probeHost, hideSandboxWindow, } from '../../../host/sandbox/index.js';
12
+ import { assertCanLaunch, isSandboxRunning, spawnDetached, probeHost, hideSandboxWindow, realContainedDeps, } from '../../../host/sandbox/index.js';
11
13
  import { KnownProjects, ProjectRegistry } from '../../../host/projects/index.js';
12
- import { LifeRecord, SessionLedger } from '../../../host/life/index.js';
14
+ import { LifeRecord, pageFileRisk, readHostMemory, SessionLedger, takePageFileHint } from '../../../host/life/index.js';
13
15
  import { isWithin } from '../../../shared/paths/index.js';
14
16
  import { watcherRunning } from '../../../sync/index.js';
15
17
  import { protectionsOf } from '../../../core/local-config/index.js';
@@ -118,11 +120,39 @@ class Launcher {
118
120
  }
119
121
  /** One sandbox for this folder: resolve, prepare, write the launch files, then (unless generate) start it. */
120
122
  async launchHere() {
121
- const { ctx, generateOnly, extras } = this;
123
+ const { ctx, generateOnly } = this;
122
124
  const run = await this.resolve();
123
125
  const { config } = run;
126
+ // An engine or storage kind that is not available yet stops here, before anything is prepared.
127
+ const engine = await openEngine(config);
128
+ const provider = providerFor(config, config.storage.contained === 'off'
129
+ ? {}
130
+ : { contained: realContainedDeps(ctx.storageRoot, config.storage.containedMaxGb) });
124
131
  if (!generateOnly)
125
132
  assertCanLaunch(isSandboxRunning() ? runningOwner() : undefined);
133
+ // Contained storage: the image is mounted now (before anything reads or writes the data set), and let go of again
134
+ // when the launch fails before the sandbox starts. The background watcher holds it from then on.
135
+ const dataMappings = await this.prepareStorage(provider, run);
136
+ try {
137
+ return await this.launchPrepared(run, engine, dataMappings);
138
+ }
139
+ catch (e) {
140
+ await provider.release();
141
+ throw e;
142
+ }
143
+ }
144
+ /** The data set's mappings from a contained-storage provider (mounting it), or undefined for plain folders. */
145
+ async prepareStorage(provider, run) {
146
+ if (provider.id === 'folder')
147
+ return undefined;
148
+ if (this.generateOnly)
149
+ return containedMappings(run.dataPath, containedPaths(run.storageRoot, setNameOf(run.dataPath)).mount, GUEST.data);
150
+ console.log('Mounting the contained storage (Windows asks for permission when it is not mounted yet)...');
151
+ return [...(await provider.prepare({ dataPath: run.dataPath, guest: GUEST.data })).mappings];
152
+ }
153
+ async launchPrepared(run, engine, dataMappings) {
154
+ const { ctx, generateOnly, extras } = this;
155
+ const { config } = run;
126
156
  if (!generateOnly && !extras.extraProjects)
127
157
  await maybeAddRoot(ctx, run);
128
158
  const prepared = generateOnly ? null : await prepare(ctx, run);
@@ -136,8 +166,11 @@ class Launcher {
136
166
  sandboxRunning: isSandboxRunning(),
137
167
  base: run.workspaceHost,
138
168
  });
169
+ // This PC's commit (RAM plus paging file) bounds the sandbox's memory as much as free RAM does.
170
+ const hostMemory = generateOnly ? null : readHostMemory(ctx.storageRoot);
139
171
  const generated = generateSandbox({
140
172
  packageRoot: ctx.packageRoot,
173
+ ...(hostMemory ? { commitAvailableMb: hostMemory.commitAvailableMb } : {}),
141
174
  run,
142
175
  harnessArgs: this.options.passthrough,
143
176
  probe: probeHost(config, run.harnessName),
@@ -145,10 +178,18 @@ class Launcher {
145
178
  autoSession: extras.autoSession,
146
179
  bootWants: bootWants.wants,
147
180
  ...(vpn ? { vpn } : {}),
148
- });
181
+ ...(dataMappings ? { dataMappings } : {}),
182
+ }, engine);
149
183
  const notes = [...run.notes, ...bootWants.notes, ...generated.projects.notes, ...generated.mappingNotes];
150
184
  for (const note of notes)
151
185
  console.log(note);
186
+ if (generated.memoryNote)
187
+ console.log(c.yellow(generated.memoryNote));
188
+ if (generated.bootConfig.sandbox.vGpu)
189
+ console.log(c.yellow(vgpuWarning(vgpuSource(ctx.loaded.config.sandbox.vGpu))));
190
+ const risk = hostMemory ? pageFileRisk(hostMemory, generated.memoryMb) : null;
191
+ if (risk && takePageFileHint(ctx.storageRoot))
192
+ console.log(c.yellow(risk.text));
152
193
  this.warnSecretFiles(run);
153
194
  for (const line of vpnNotes(config.vpn))
154
195
  console.log(c.yellow(line));
@@ -159,8 +200,7 @@ class Launcher {
159
200
  const reopen = extras.extraProjects ? [] : await sessionsToResume(ctx, run);
160
201
  this.record(run, generated);
161
202
  this.share(generated);
162
- startSandbox(generated.wsbPath);
163
- await confirmSandboxStarted(generated.wsbPath);
203
+ await engine.start(generated.enginePlan);
164
204
  this.startWatcher();
165
205
  if (reopen.length)
166
206
  await this.reopenAfterBoot(generated.launchId, reopen);
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
  import fs from 'node:fs';
3
3
  import { AccountStore } from '../../../host/profile/index.js';
4
4
  import { SandboxChannel } from '../../../host/channel/index.js';
5
- import { describePrune, pruneStorage } from '../../../host/storage/index.js';
5
+ import { describePrune, hostDataFolder, pruneStorage } from '../../../host/storage/index.js';
6
6
  import { RepoGuardStore, readDataSize, saveMarkedProjects } from '../../../host/guard/index.js';
7
7
  import { ensureConpty, ensureTerminal } from '../../../host/tools/index.js';
8
8
  import { planView, viewSkipped, saveActiveViews, WorkspaceView } from '../../../host/workspace-view/index.js';
@@ -16,7 +16,7 @@ import { firstStartNotice, pendingDownloads } from './first-start.js';
16
16
  import { LifeRecord, UPDATE_RESTART_WARNING, updateRestartPending } from '../../../host/life/index.js';
17
17
  /** Nothing has been installed into this data set yet: the first start downloads every toolchain once. */
18
18
  export function firstBoot(dataPath) {
19
- return !fs.existsSync(path.join(dataPath, 'tools', 'resolved-versions.json'));
19
+ return !fs.existsSync(path.join(hostDataFolder(dataPath, 'tools'), 'resolved-versions.json'));
20
20
  }
21
21
  /** The first-start heads-up (also when this project needs tools not downloaded yet), or null. */
22
22
  export function firstBootNotice(dataPath, generated) {
@@ -27,7 +27,7 @@ export function firstBootNotice(dataPath, generated) {
27
27
  .map(([name]) => name),
28
28
  ...generated.databases.map((d) => d.database),
29
29
  ];
30
- return firstStartNotice(firstBoot(dataPath), pendingDownloads(wanted, (dir) => fs.existsSync(path.join(dataPath, 'tools', dir))));
30
+ return firstStartNotice(firstBoot(dataPath), pendingDownloads(wanted, (dir) => fs.existsSync(path.join(hostDataFolder(dataPath, 'tools'), dir))));
31
31
  }
32
32
  function syncSummary(config, dataPath, workspaceHost) {
33
33
  try {
@@ -17,7 +17,8 @@ const SESSION_START_MS = 60 * 60 * 1000;
17
17
  export function attachHere(ctx, session) {
18
18
  if (ctx.loaded.config.attach.autoHide && !insideSandbox())
19
19
  void hideSandboxWindow(ctx.storageRoot, 120).catch(() => false);
20
- return attachTerminal(session);
20
+ // A copy in the agent reaches this PC's clipboard only when sandbox.clipboard already shares it with the sandbox.
21
+ return attachTerminal(session, { clipboardWrites: ctx.loaded.config.sandbox.clipboard });
21
22
  }
22
23
  /** The sandbox writes sandbox.json a few seconds into its boot; a second launch right after the first waits for it. */
23
24
  export async function runningSandboxStatus(dataPath) {
@@ -1,8 +1,10 @@
1
1
  import type { Generated, ResolvedRun } from '../../../core/types.js';
2
+ import type { EnginePlan } from '../../../host/engines/index.js';
2
3
  import type { Prepared } from './prepare.js';
3
4
  /** What generateSandbox returned. */
4
5
  export type GeneratedSandbox = Generated & {
5
6
  readonly personalization: string;
7
+ readonly enginePlan: EnginePlan;
6
8
  };
7
9
  /** The launch summary rows, in the order they are printed. */
8
10
  export declare function launchSummary(run: ResolvedRun, generated: GeneratedSandbox, prepared: Prepared | null): string[][];
@@ -0,0 +1,12 @@
1
+ import { type ImportPlan } from '../../menu/settings/index.js';
2
+ import type { Context } from '../context.js';
3
+ /** The file `config export` writes when none is named. */
4
+ export declare const DEFAULT_EXPORT = "sandbox-settings.json";
5
+ /** Writes the settings that differ from the defaults (no secrets, no storage, no paths unless asked) to `file`. */
6
+ export declare function writeExport(ctx: Context, file: string, withPaths: boolean, overwrite: boolean): string[];
7
+ /** Reads a shared file and plans the import. Fails with the reason when it cannot be read. */
8
+ export declare function readImport(ctx: Context, file: string): ImportPlan;
9
+ /** Writes the planned changes to your settings file (comments stay); returns how many settings changed. */
10
+ export declare function applyImport(ctx: Context, plan: ImportPlan): number;
11
+ export declare function exportCommand(ctx: Context, file: string | undefined): number;
12
+ export declare function importCommand(ctx: Context, file: string | undefined): Promise<number>;
@@ -0,0 +1,80 @@
1
+ // `sandbox config export` and `import`: your settings as a shareable .json, and a shared file read back in.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { askRequired } from '../../../core/prompt.js';
5
+ import { fail, safeText, writeJson } from '../../../core/util/index.js';
6
+ import { c } from '../../output/index.js';
7
+ import { ConfigFile } from '../../../app/features/settings/index.js';
8
+ import { diffText, exportSettings, planImport, skippedLine } from '../../menu/settings/index.js';
9
+ /** The file `config export` writes when none is named. */
10
+ export const DEFAULT_EXPORT = 'sandbox-settings.json';
11
+ /** A shared file is a few KB; anything bigger is not one. */
12
+ const MAX_BYTES = 1024 * 1024;
13
+ /** Writes the settings that differ from the defaults (no secrets, no storage, no paths unless asked) to `file`. */
14
+ export function writeExport(ctx, file, withPaths, overwrite) {
15
+ const target = path.resolve(file);
16
+ if (!overwrite && fs.existsSync(target))
17
+ fail(target + ' exists already.', 'Pick another name, or delete it first.');
18
+ const { data, skipped } = exportSettings(ctx.loaded.config, withPaths);
19
+ fs.mkdirSync(path.dirname(target), { recursive: true });
20
+ writeJson(target, data);
21
+ return skipped.map(skippedLine);
22
+ }
23
+ /** Reads a shared file and plans the import. Fails with the reason when it cannot be read. */
24
+ export function readImport(ctx, file) {
25
+ const source = path.resolve(file);
26
+ if (!fs.existsSync(source))
27
+ fail('There is no file ' + source + '.', 'Give the path of a file made by sandbox config export.');
28
+ if (fs.statSync(source).size > MAX_BYTES)
29
+ fail(source + ' is too big to be a settings file.');
30
+ const plan = planImport(fs.readFileSync(source, 'utf8'), ctx.loaded.config);
31
+ if (plan.problems.length)
32
+ fail(source + ' is not valid settings:\n ' + plan.problems.map(safeText).join('\n '), 'Nothing was changed. Fix the file, or export a new one with: sandbox config export');
33
+ return plan;
34
+ }
35
+ /** Writes the planned changes to your settings file (comments stay); returns how many settings changed. */
36
+ export function applyImport(ctx, plan) {
37
+ const outcome = ConfigFile.user(ctx.loaded.configPath).setMany(plan.changes);
38
+ if (typeof outcome === 'object')
39
+ fail('Could not import: ' + outcome.problems.join('; '), 'Nothing was written. Your settings are unchanged.');
40
+ return plan.changes.length;
41
+ }
42
+ export function exportCommand(ctx, file) {
43
+ const target = file ?? DEFAULT_EXPORT;
44
+ const skipped = writeExport(ctx, target, ctx.options.withPaths, false);
45
+ console.log('Wrote ' + path.resolve(target) + ' (only what differs from the defaults).');
46
+ if (skipped.length) {
47
+ console.log('Left out:');
48
+ for (const line of skipped)
49
+ console.log(' ' + line);
50
+ }
51
+ if (!ctx.options.withPaths && skipped.some((s) => s.includes('folder or file')))
52
+ console.log("Keep this PC's folders and files in the file with --with-paths.");
53
+ return 0;
54
+ }
55
+ export async function importCommand(ctx, file) {
56
+ if (!file)
57
+ fail('Which file?', 'Usage: sandbox config import <file>');
58
+ const plan = readImport(ctx, file);
59
+ if (plan.skipped.length) {
60
+ console.log('Not imported (never taken from a file):');
61
+ for (const s of plan.skipped)
62
+ console.log(' ' + safeText(skippedLine(s)));
63
+ }
64
+ if (!plan.changes.length) {
65
+ console.log('Nothing to change: your settings already match the file.');
66
+ return 0;
67
+ }
68
+ console.log('This would change ' + plan.changes.length + ' setting' + (plan.changes.length === 1 ? '' : 's') + ':');
69
+ for (const line of diffText(plan.lines))
70
+ console.log(' ' + safeText(line));
71
+ const yes = ctx.options.yes ||
72
+ (await askRequired('Change your settings like this?', 'Add --yes to change them without asking.'));
73
+ if (!yes) {
74
+ console.log('Nothing was changed.');
75
+ return 0;
76
+ }
77
+ const n = applyImport(ctx, plan);
78
+ console.log(c.green('Imported ' + n + ' setting' + (n === 1 ? '' : 's') + '.') + ' New sandboxes use them.');
79
+ return 0;
80
+ }
@@ -2,7 +2,6 @@ import { spawn } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { pathToFileURL } from 'node:url';
5
- import _ from 'lodash';
6
5
  import { DEFAULTS, userConfigPath, userDir } from '../../../core/config/index.js';
7
6
  import { redactSecrets } from '../../../core/schema/index.js';
8
7
  import { allowedAnswer, allowStorePath, applyOverrides, describeOverrides, isTrusted, LOCAL_CONFIG_NAME, loosenedRequests, normalizeOverrides, OUTSIDE_FOLDER, outsideKey, outsideMappings, readLocalConfig, trustStorePath, } from '../../../core/local-config/index.js';
@@ -11,9 +10,8 @@ import { askChoice } from '../../../core/prompt.js';
11
10
  import { dirExists, fail, safeText, writeJson } from '../../../core/util/index.js';
12
11
  import { system32 } from '../../../shared/system.js';
13
12
  import { refuseInSelfTest } from '../../../host/test-guard.js';
14
- import { closest } from '../../validate.js';
15
13
  import { c, nextStep, table } from '../../output/index.js';
16
- import { ConfigFile } from '../../menu/settings/index.js';
14
+ import { exportCommand, importCommand } from './config-share.js';
17
15
  /** The settings file exists already: open it, start over, or leave it (no terminal: say how). */
18
16
  async function existingFile(file) {
19
17
  return askChoice(file + ' exists already.', [
@@ -43,79 +41,6 @@ async function initConfig() {
43
41
  nextStep("sandbox config schema (editor help in every project's .sandbox-config.json)");
44
42
  return 0;
45
43
  }
46
- /** The setting a key names, as the schema knows it; an unknown key says what is close. */
47
- function settingNode(key) {
48
- const root = ConfigFile.user(null).schema;
49
- const keyPath = key.split('.');
50
- let node = root;
51
- for (const part of keyPath)
52
- node = node?.child(part);
53
- if (node)
54
- return { path: keyPath, node };
55
- const known = root.leaves().map((l) => l.key.join('.'));
56
- const hint = closest(key, known);
57
- return fail('There is no setting "' + key + '".' + (hint ? ' Did you mean ' + hint + '?' : ''), 'Settings are named like sandbox.memoryMb. See them all with: sandbox config show');
58
- }
59
- /** What was typed for a setting as the value it stands for: true/false/on/off, a number, JSON, or plain text. */
60
- function settingValue(node, text) {
61
- const word = text.trim().toLowerCase();
62
- if (node.type === 'string')
63
- return text;
64
- if (node.type === 'boolean' || node.options.some((o) => o.type === 'boolean')) {
65
- if (['true', 'on', 'yes'].includes(word))
66
- return true;
67
- if (['false', 'off', 'no'].includes(word))
68
- return false;
69
- }
70
- try {
71
- return JSON.parse(text);
72
- }
73
- catch {
74
- // Not JSON: a bare word such as auto or a path is the text itself.
75
- return node.type === 'array' ? text.split(',').map((t) => t.trim()) : text;
76
- }
77
- }
78
- /** What a setting accepts, in words: its fixed values, a number and its range, true or false. */
79
- function allowedText(node) {
80
- const values = node.choiceValues.map((v) => String(v));
81
- const open = node.openOption ?? (node.type === 'number' ? node : undefined);
82
- const range = (n) => (n.rangeText() ? ' (' + n.rangeText() + ')' : '');
83
- const more = open ? (open.type === 'number' ? 'a number' + range(open) : 'text') : '';
84
- if (node.type === 'boolean')
85
- return 'Use true or false.';
86
- const parts = [...values, ...(more ? [more] : [])];
87
- return parts.length ? 'Allowed: ' + parts.join(', or ') + '.' : 'Check the value.';
88
- }
89
- function setSettingCommand(ctx, key, text) {
90
- if (!key || text === undefined)
91
- fail('Which setting and value?', 'Usage: sandbox config set <setting> <value>');
92
- const { path: keyPath, node } = settingNode(key);
93
- if (node.isSection)
94
- fail('"' + key + '" is a group of settings.', 'Set one of them, e.g. ' + node.leaves(keyPath)[0]?.key.join('.'));
95
- const value = settingValue(node, text);
96
- const problem = node.check(value);
97
- if (problem !== true) {
98
- fail('"' + text + '" does not work for ' + key + ': ' + problem, allowedText(node));
99
- }
100
- const outcome = ConfigFile.user(ctx.loaded.configPath).set(keyPath, value);
101
- if (typeof outcome === 'object')
102
- fail('Could not change ' + key + ': ' + outcome.problems.join('; '), 'Nothing was written.');
103
- console.log(key +
104
- ' is ' +
105
- JSON.stringify(_.get(redactSecrets(_.set({}, keyPath, value)), keyPath)) +
106
- (outcome === 'unchanged' ? ' (it was already)' : '') +
107
- '.');
108
- return 0;
109
- }
110
- function getSettingCommand(ctx, key) {
111
- if (!key)
112
- fail('Which setting?', 'Usage: sandbox config get <setting> (all of them: sandbox config show)');
113
- const { path: keyPath } = settingNode(key);
114
- // Secret values (an agent's API keys) are never printed.
115
- const value = _.get(redactSecrets(ctx.loaded.config), keyPath);
116
- console.log(typeof value === 'string' ? value : JSON.stringify(value, null, 2));
117
- return 0;
118
- }
119
44
  /**
120
45
  * VS Code setting that maps every .sandbox-config.json (and the user config) to the local schema files, so project
121
46
  * files need no "$schema" line and nothing is fetched from the internet (remote schemas need trusting).
@@ -208,7 +133,8 @@ function previewFolders(ctx, dir, filePath, overrides) {
208
133
  }
209
134
  }
210
135
  export function config(ctx) {
211
- const [action = 'summary', key, value] = ctx.options.positionals;
136
+ // get, set, reset and list are the settings feature's operations (cli/operations runs them before this).
137
+ const [action = 'summary', key] = ctx.options.positionals;
212
138
  switch (action) {
213
139
  case 'summary':
214
140
  table([
@@ -233,15 +159,15 @@ export function config(ctx) {
233
159
  case 'show':
234
160
  console.log(JSON.stringify(redactSecrets(ctx.loaded.config), null, 2));
235
161
  return 0;
236
- case 'get':
237
- return getSettingCommand(ctx, key);
238
- case 'set':
239
- return setSettingCommand(ctx, key, value);
162
+ case 'export':
163
+ return exportCommand(ctx, key);
164
+ case 'import':
165
+ return importCommand(ctx, key);
240
166
  case 'schema':
241
167
  return printSchemaSettings();
242
168
  case 'check':
243
169
  return checkLocalConfig(ctx);
244
170
  default:
245
- fail('Unknown config action "' + action + '".', 'Use one of: sandbox config show | get | set | path | init | schema | check');
171
+ fail('Unknown config action "' + action + '".', 'Use one of: sandbox config show | list | get | set | reset | path | init | schema | check | export | import');
246
172
  }
247
173
  }
@@ -1,6 +1,20 @@
1
1
  import { sleep } from '../../../core/util/index.js';
2
+ import { newestCrashReport } from '../../../host/life/index.js';
2
3
  import { LogsService } from '../../services/index.js';
4
+ /** The newest crash report (written by the background sync when the sandbox or this PC went down). */
5
+ function printCrashReport(ctx) {
6
+ const report = newestCrashReport(ctx.storageRoot);
7
+ if (!report) {
8
+ console.log('No crash report yet: no sandbox has ended in a crash since this was installed.');
9
+ return 0;
10
+ }
11
+ console.log('== ' + report.file + ' ==');
12
+ console.log(report.text);
13
+ return 0;
14
+ }
3
15
  export async function logs(ctx) {
16
+ if (ctx.options.crash)
17
+ return printCrashReport(ctx);
4
18
  const service = new LogsService(ctx);
5
19
  const file = service.newest();
6
20
  console.log('== ' + file + ' ==');
@@ -2,10 +2,11 @@ import { launch } from './launch/index.js';
2
2
  import { setup } from './setup/index.js';
3
3
  import { data } from './data/index.js';
4
4
  import { storage } from './storage/index.js';
5
- import { attach, db, exec, hide, restart, sessions, show, status, stop } from './sessions/index.js';
5
+ import { attach, db, exec } from './sessions/index.js';
6
6
  import { account } from './accounts.js';
7
7
  import { projects } from './projects/index.js';
8
8
  import { audit, claudeHost, clean, config, doctor, hostCommand, logs, migrate, sync, watcher, } from './maintenance/index.js';
9
+ import { engine } from './engine.js';
9
10
  import { resume } from './resume.js';
10
11
  import { agentCommandsCommand } from './agent-commands.js';
11
12
  import { vpn } from './vpn.js';
@@ -14,16 +15,10 @@ export const HANDLERS = {
14
15
  launch: (ctx) => launch(ctx),
15
16
  generate: (ctx) => launch(ctx, true),
16
17
  attach: attach,
17
- sessions: sessions,
18
- status: status,
19
18
  exec: exec,
20
19
  'agent-commands': agentCommandsCommand,
21
20
  db: db,
22
- restart: restart,
23
21
  resume: resume,
24
- stop: stop,
25
- show: show,
26
- hide: hide,
27
22
  account: account,
28
23
  projects: projects,
29
24
  sync: sync,
@@ -36,6 +31,7 @@ export const HANDLERS = {
36
31
  setup: setup,
37
32
  data: data,
38
33
  storage: storage,
34
+ engine: engine,
39
35
  'host-claude': claudeHost,
40
36
  host: hostCommand,
41
37
  _watch: watcher,
@@ -0,0 +1,2 @@
1
+ import type { Check } from '../types.js';
2
+ export declare const CONTAINED_CHECKS: Check[];
@@ -0,0 +1,139 @@
1
+ // Contained storage (beta): the image is there, what the record says is mounted matches what is really mounted, and
2
+ // the folders around the image are private. A mount left behind (a sandbox that ended without letting go, a crash) is
3
+ // dismounted here, which needs the Windows permission prompt.
4
+ import fs from 'node:fs';
5
+ import { isWin } from '../../../../core/util/index.js';
6
+ import { parseIcacls, privacyProblems, SHARING_GROUP } from '../../../../host/health/index.js';
7
+ import { realContainedDeps } from '../../../../host/sandbox/index.js';
8
+ import { protectPrivateFolder } from '../../../../host/sharing/index.js';
9
+ import { containedAvailability, ContainedStorage, containedPaths } from '../../../../host/storage/index.js';
10
+ import { ok } from './common.js';
11
+ function storageOf(env) {
12
+ const { config } = env.ctx.loaded;
13
+ return new ContainedStorage(realContainedDeps(env.storageRoot, config.storage.containedMaxGb), config.storage.dataName);
14
+ }
15
+ /** Only when contained storage is on, or an image or a record of one exists. */
16
+ const inUse = (env) => {
17
+ if (!isWin)
18
+ return false;
19
+ const { config } = env.ctx.loaded;
20
+ const paths = containedPaths(env.storageRoot, config.storage.dataName);
21
+ return config.storage.contained === 'vhdx' || fs.existsSync(paths.image) || fs.existsSync(paths.stateFile);
22
+ };
23
+ export const CONTAINED_CHECKS = [
24
+ {
25
+ id: 'data-contained',
26
+ group: 'Data folder',
27
+ name: 'Contained storage',
28
+ checking: 'the contained storage image (beta)',
29
+ applies: inUse,
30
+ timeoutMs: 60_000,
31
+ run: async (env) => {
32
+ const wanted = env.ctx.loaded.config.storage.contained === 'vhdx';
33
+ const available = containedAvailability();
34
+ if (!available.available)
35
+ return wanted
36
+ ? {
37
+ status: 'needs-you',
38
+ detail: 'storage.contained is "vhdx", but ' + available.reasons.join(' '),
39
+ means: 'A launch cannot mount the image on this PC.',
40
+ fix: available.fixes.join(' '),
41
+ }
42
+ : {
43
+ status: 'skipped',
44
+ detail: 'an image exists, but this PC cannot look at it: ' + available.reasons.join(' '),
45
+ };
46
+ const storage = storageOf(env);
47
+ const diagnosis = await storage.diagnose();
48
+ switch (diagnosis.kind) {
49
+ case 'image-missing':
50
+ return wanted
51
+ ? {
52
+ status: 'needs-you',
53
+ detail: 'the image is missing: ' + storage.paths.image,
54
+ means: 'A launch stops until it is there (the tools, state and logs in it are gone with it).',
55
+ fix: 'Restore the file, or run: sandbox storage contained off (and then on again for a new, empty image).',
56
+ }
57
+ : ok('no image (contained storage is off)');
58
+ case 'in-use':
59
+ return ok('mounted, in use by a running sandbox');
60
+ case 'stale-mount':
61
+ return {
62
+ status: 'needs-you',
63
+ detail: 'the image is still mounted at ' + storage.paths.mount + ', and nothing uses it',
64
+ means: 'It stays attached to this PC until it is dismounted; a launch would reuse it.',
65
+ plan: 'dismount the image (Windows asks for permission)',
66
+ data: 'dismount',
67
+ };
68
+ case 'stale-state':
69
+ return {
70
+ status: 'warn',
71
+ detail: 'the record says the image is mounted, but it is not',
72
+ means: 'Nothing is wrong with the image; the record is out of date.',
73
+ plan: 'correct the record',
74
+ data: 'record',
75
+ };
76
+ default:
77
+ return ok('not mounted' + (wanted ? '' : ' (contained storage is off; the image is kept)'));
78
+ }
79
+ },
80
+ fix: async (env, finding) => {
81
+ const storage = storageOf(env);
82
+ if (finding.data === 'record') {
83
+ storage.clearRecord();
84
+ return { done: 'corrected the record' };
85
+ }
86
+ const result = await storage.dismountIfIdle();
87
+ if (!result.dismounted)
88
+ throw new Error('Could not dismount the image: ' + (result.note ?? 'unknown reason'));
89
+ return { done: 'dismounted the image' };
90
+ },
91
+ needsIdle: 'fix',
92
+ },
93
+ {
94
+ id: 'data-contained-privacy',
95
+ group: 'Data folder',
96
+ name: 'Contained storage folders',
97
+ checking: 'who can open the contained storage folders',
98
+ applies: inUse,
99
+ run: (env) => {
100
+ const { config } = env.ctx.loaded;
101
+ const paths = containedPaths(env.storageRoot, config.storage.dataName);
102
+ // While mounted, the mount folder shows the image's own root, and nothing is changed under a mount point.
103
+ if (env.running)
104
+ return ok('a sandbox is running; not looked at now');
105
+ const loose = [];
106
+ for (const dir of [paths.imagesDir, paths.mountsDir, paths.mount]) {
107
+ if (!fs.existsSync(dir))
108
+ continue;
109
+ const text = env.system.icacls(dir);
110
+ if (text === null)
111
+ return { status: 'skipped', detail: 'could not read the permissions of ' + dir };
112
+ const problems = privacyProblems(parseIcacls(text, dir), SHARING_GROUP);
113
+ if (problems.length > 0)
114
+ loose.push(dir + ' (' + problems.join(', ') + ')');
115
+ }
116
+ if (loose.length === 0)
117
+ return ok('private (you, SYSTEM and administrators)');
118
+ return {
119
+ status: 'warn',
120
+ detail: 'open to more than you: ' + loose.join('; '),
121
+ means: 'Other users of this PC could open the image or the folders it is mounted in.',
122
+ plan: 'make them private',
123
+ data: 'private',
124
+ };
125
+ },
126
+ fix: (env) => {
127
+ const { config } = env.ctx.loaded;
128
+ const paths = containedPaths(env.storageRoot, config.storage.dataName);
129
+ // Never with an image mounted below: a folder with files is walked, and a mount point must not be.
130
+ if (env.running)
131
+ throw new Error('a sandbox is running; run repair again once it is closed');
132
+ for (const dir of [paths.imagesDir, paths.mountsDir, paths.mount])
133
+ if (fs.existsSync(dir))
134
+ protectPrivateFolder(dir);
135
+ return { done: 'made the folders private' };
136
+ },
137
+ needsIdle: 'fix',
138
+ },
139
+ ];
@@ -0,0 +1,7 @@
1
+ import { type HcsProbes } from '../../../../host/engines/index.js';
2
+ import type { Check } from '../types.js';
3
+ /** Replaceable by tests: the probes the row looks through. */
4
+ export declare const HCS_PROBES: {
5
+ current: HcsProbes | null;
6
+ };
7
+ export declare const HCS_CHECKS: Check[];
@@ -0,0 +1,28 @@
1
+ // The HCS engine (alpha), only when sandbox.engine is "hcs": can this PC run it. Looks only; the fixes are commands for
2
+ // you to run (they change Windows), never run by repair.
3
+ import { checkHcs, realHcsProbes } from '../../../../host/engines/index.js';
4
+ import { ok } from './common.js';
5
+ const hcsInUse = (env) => env.ctx.loaded.config.sandbox.engine === 'hcs';
6
+ /** Replaceable by tests: the probes the row looks through. */
7
+ export const HCS_PROBES = { current: null };
8
+ export const HCS_CHECKS = [
9
+ {
10
+ id: 'hcs-engine',
11
+ group: 'Sandbox',
12
+ name: 'HCS engine (alpha)',
13
+ checking: 'the HCS engine',
14
+ applies: hcsInUse,
15
+ timeoutMs: 60000,
16
+ run: () => {
17
+ const check = checkHcs(HCS_PROBES.current ?? realHcsProbes());
18
+ if (check.available)
19
+ return ok('this PC can run it (starting it is not enabled yet in this alpha)');
20
+ return {
21
+ status: 'needs-you',
22
+ detail: check.reasons.length + ' thing(s) missing: ' + check.reasons[0],
23
+ means: check.reasons.join(' '),
24
+ fix: check.fixes.join(' | '),
25
+ };
26
+ },
27
+ },
28
+ ];
@@ -1,5 +1,7 @@
1
1
  import { AGENT_CHECKS } from './agents.js';
2
+ import { CONTAINED_CHECKS } from './contained.js';
2
3
  import { DATA_CHECKS } from './data.js';
4
+ import { HCS_CHECKS } from './hcs.js';
3
5
  import { INSTALL_CHECKS } from './install.js';
4
6
  import { MACHINE_CHECKS } from './machine.js';
5
7
  import { NETWORK_CHECKS } from './network.js';
@@ -12,8 +14,10 @@ export const ALL_CHECKS = [
12
14
  ...INSTALL_CHECKS,
13
15
  ...MACHINE_CHECKS,
14
16
  ...DATA_CHECKS,
17
+ ...CONTAINED_CHECKS,
15
18
  ...SETTINGS_CHECKS,
16
19
  ...SANDBOX_CHECKS,
20
+ ...HCS_CHECKS,
17
21
  ...AGENT_CHECKS,
18
22
  ...NETWORK_CHECKS,
19
23
  ...VPN_CHECKS,