windows-development-sandbox 1.7.0 → 1.10.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 (432) hide show
  1. package/config.example.json +12 -1
  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 +49 -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 +20 -3
  82. package/dist/cli/commands/web.d.ts +5 -0
  83. package/dist/cli/commands/web.js +40 -0
  84. package/dist/cli/index.js +2 -1
  85. package/dist/cli/manifest.d.ts +72 -104
  86. package/dist/cli/manifest.js +64 -39
  87. package/dist/cli/menu/add-agent.d.ts +1 -1
  88. package/dist/cli/menu/add-agent.js +1 -1
  89. package/dist/cli/menu/apps.d.ts +7 -1
  90. package/dist/cli/menu/apps.js +36 -0
  91. package/dist/cli/menu/command-fields.d.ts +1 -1
  92. package/dist/cli/menu/command-form.js +2 -2
  93. package/dist/cli/menu/io-menu.d.ts +29 -0
  94. package/dist/cli/menu/io-menu.js +126 -0
  95. package/dist/cli/menu/live.d.ts +27 -0
  96. package/dist/cli/menu/live.js +69 -0
  97. package/dist/cli/menu/memory.d.ts +19 -0
  98. package/dist/cli/menu/memory.js +53 -0
  99. package/dist/cli/menu/menu.d.ts +10 -1
  100. package/dist/cli/menu/menu.js +27 -9
  101. package/dist/cli/menu/pages/agents-page.d.ts +3 -1
  102. package/dist/cli/menu/pages/agents-page.js +12 -5
  103. package/dist/cli/menu/pages/apps-page.d.ts +3 -1
  104. package/dist/cli/menu/pages/apps-page.js +19 -2
  105. package/dist/cli/menu/pages/list-page.d.ts +27 -0
  106. package/dist/cli/menu/pages/list-page.js +109 -7
  107. package/dist/cli/menu/pages/maintenance-page.d.ts +0 -5
  108. package/dist/cli/menu/pages/maintenance-page.js +17 -19
  109. package/dist/cli/menu/pages/sandbox-page.d.ts +4 -6
  110. package/dist/cli/menu/pages/sandbox-page.js +26 -118
  111. package/dist/cli/menu/pages/scopes.d.ts +1 -1
  112. package/dist/cli/menu/pages/sessions-page.d.ts +5 -11
  113. package/dist/cli/menu/pages/sessions-page.js +19 -88
  114. package/dist/cli/menu/pages/settings-page.d.ts +2 -10
  115. package/dist/cli/menu/pages/settings-page.js +64 -94
  116. package/dist/cli/menu/render-view.d.ts +28 -0
  117. package/dist/cli/menu/render-view.js +112 -0
  118. package/dist/cli/menu/settings/{path-kind.d.ts → ask-path.d.ts} +1 -7
  119. package/dist/cli/menu/settings/ask-path.js +14 -0
  120. package/dist/cli/menu/settings/collections/collection-editor.d.ts +1 -1
  121. package/dist/cli/menu/settings/collections/string-list-editor.d.ts +1 -2
  122. package/dist/cli/menu/settings/collections/string-list-editor.js +1 -1
  123. package/dist/cli/menu/settings/draft.d.ts +1 -1
  124. package/dist/cli/menu/settings/format.d.ts +1 -3
  125. package/dist/cli/menu/settings/format.js +6 -36
  126. package/dist/cli/menu/settings/index.d.ts +1 -3
  127. package/dist/cli/menu/settings/index.js +3 -3
  128. package/dist/cli/menu/settings/object-editor.d.ts +1 -1
  129. package/dist/cli/menu/settings/share.d.ts +36 -0
  130. package/dist/cli/menu/settings/share.js +131 -0
  131. package/dist/cli/menu/settings/value-editor.d.ts +5 -1
  132. package/dist/cli/menu/settings/value-editor.js +57 -54
  133. package/dist/cli/menu/several.js +1 -1
  134. package/dist/cli/menu/share-settings.d.ts +21 -0
  135. package/dist/cli/menu/share-settings.js +112 -0
  136. package/dist/cli/menu/shell.d.ts +21 -5
  137. package/dist/cli/menu/shell.js +31 -6
  138. package/dist/cli/menu/views.d.ts +7 -0
  139. package/dist/cli/menu/views.js +11 -0
  140. package/dist/cli/menu/vpn-login.js +8 -0
  141. package/dist/cli/operations/handler.d.ts +6 -0
  142. package/dist/cli/operations/handler.js +61 -0
  143. package/dist/cli/operations/index.d.ts +4 -0
  144. package/dist/cli/operations/index.js +5 -0
  145. package/dist/cli/operations/input.d.ts +6 -0
  146. package/dist/cli/operations/input.js +37 -0
  147. package/dist/cli/operations/io-cli.d.ts +7 -0
  148. package/dist/cli/operations/io-cli.js +62 -0
  149. package/dist/cli/operations/spec.d.ts +18 -0
  150. package/dist/cli/operations/spec.js +108 -0
  151. package/dist/cli/output/colors.d.ts +2 -2
  152. package/dist/cli/output/colors.js +2 -11
  153. package/dist/cli/output/index.d.ts +1 -0
  154. package/dist/cli/output/index.js +1 -0
  155. package/dist/cli/output/print.d.ts +3 -1
  156. package/dist/cli/output/print.js +7 -3
  157. package/dist/cli/output/progress.d.ts +15 -0
  158. package/dist/cli/output/progress.js +44 -0
  159. package/dist/cli/output/render-view.d.ts +4 -0
  160. package/dist/cli/output/render-view.js +63 -0
  161. package/dist/cli/services/agent-commands-service.d.ts +2 -0
  162. package/dist/cli/services/agent-commands-service.js +11 -2
  163. package/dist/cli/services/logs-service.js +4 -3
  164. package/dist/cli/tui/app.d.ts +6 -1
  165. package/dist/cli/tui/app.js +26 -4
  166. package/dist/cli/tui/list/list-screen.d.ts +14 -0
  167. package/dist/cli/tui/list/list-screen.js +64 -36
  168. package/dist/cli/tui/list/list-types.d.ts +2 -0
  169. package/dist/cli/tui/terminal.d.ts +7 -0
  170. package/dist/cli/tui/terminal.js +30 -10
  171. package/dist/core/config/defaults.js +9 -1
  172. package/dist/core/schema/config.d.ts +14 -0
  173. package/dist/core/schema/config.js +16 -1
  174. package/dist/core/schema/harness.d.ts +1 -0
  175. package/dist/core/schema/harness.js +5 -0
  176. package/dist/core/schema/sandbox.d.ts +6 -0
  177. package/dist/core/schema/sandbox.js +20 -3
  178. package/dist/core/schema/vpn.d.ts +2 -1
  179. package/dist/core/schema/vpn.js +4 -4
  180. package/dist/core/schema/web.d.ts +1 -0
  181. package/dist/core/schema/web.js +4 -1
  182. package/dist/core/types.d.ts +23 -5
  183. package/dist/core/util/fs.d.ts +2 -0
  184. package/dist/core/util/fs.js +9 -0
  185. package/dist/core/util/index.d.ts +1 -1
  186. package/dist/core/util/index.js +1 -1
  187. package/dist/guest/agents/agent-files.d.ts +5 -0
  188. package/dist/guest/agents/agent-files.js +4 -0
  189. package/dist/guest/agents/local-state.d.ts +2 -0
  190. package/dist/guest/agents/local-state.js +21 -1
  191. package/dist/guest/boot/boot.js +5 -2
  192. package/dist/guest/daemon/daemon.js +4 -0
  193. package/dist/guest/daemon/sessions.d.ts +5 -0
  194. package/dist/guest/daemon/sessions.js +17 -3
  195. package/dist/guest/entries/daemon.js +3 -0
  196. package/dist/guest/governor/decide.d.ts +71 -0
  197. package/dist/guest/governor/decide.js +211 -0
  198. package/dist/guest/governor/governor.d.ts +45 -0
  199. package/dist/guest/governor/governor.js +230 -0
  200. package/dist/guest/governor/index.d.ts +2 -0
  201. package/dist/guest/governor/index.js +2 -0
  202. package/dist/guest/network/firewall.d.ts +1 -0
  203. package/dist/guest/network/firewall.js +65 -23
  204. package/dist/guest/runtime/csc.d.ts +1 -0
  205. package/dist/guest/runtime/csc.js +1 -0
  206. package/dist/guest/runtime/process.js +53 -5
  207. package/dist/guest/sessions/viewer.js +3 -1
  208. package/dist/guest/setup/claude-mem.js +12 -4
  209. package/dist/guest/toolchains/languages/node.js +29 -3
  210. package/dist/guest/toolchains/native/rust-extras.js +4 -2
  211. package/dist/guest/vpn/vpn.js +69 -21
  212. package/dist/guest/vpn/wireguard.d.ts +32 -0
  213. package/dist/guest/vpn/wireguard.js +132 -0
  214. package/dist/host/agent-commands/commands.d.ts +4 -0
  215. package/dist/host/agent-commands/commands.js +31 -7
  216. package/dist/host/audit/log.d.ts +1 -1
  217. package/dist/host/channel/attach.d.ts +3 -1
  218. package/dist/host/channel/attach.js +5 -2
  219. package/dist/host/channel/channel.d.ts +2 -0
  220. package/dist/host/channel/channel.js +4 -0
  221. package/dist/host/engines/engine.d.ts +12 -0
  222. package/dist/host/engines/engine.js +1 -0
  223. package/dist/host/engines/hcs/check.d.ts +25 -0
  224. package/dist/host/engines/hcs/check.js +95 -0
  225. package/dist/host/engines/hcs/documents.d.ts +44 -0
  226. package/dist/host/engines/hcs/documents.js +155 -0
  227. package/dist/host/engines/hcs/engine.d.ts +39 -0
  228. package/dist/host/engines/hcs/engine.js +171 -0
  229. package/dist/host/engines/hcs/helper.d.ts +43 -0
  230. package/dist/host/engines/hcs/helper.js +173 -0
  231. package/dist/host/engines/hcs/images.d.ts +37 -0
  232. package/dist/host/engines/hcs/images.js +152 -0
  233. package/dist/host/engines/index.d.ts +7 -0
  234. package/dist/host/engines/index.js +5 -0
  235. package/dist/host/engines/registry.d.ts +16 -0
  236. package/dist/host/engines/registry.js +30 -0
  237. package/dist/host/engines/types.d.ts +58 -0
  238. package/dist/host/engines/types.js +1 -0
  239. package/dist/host/engines/windows-sandbox/engine.d.ts +16 -0
  240. package/dist/host/engines/windows-sandbox/engine.js +63 -0
  241. package/dist/host/engines/windows-sandbox/wsb-text.d.ts +2 -0
  242. package/dist/host/engines/windows-sandbox/wsb-text.js +29 -0
  243. package/dist/host/launch/generate.d.ts +5 -1
  244. package/dist/host/launch/generate.js +50 -8
  245. package/dist/host/launch/index.d.ts +3 -1
  246. package/dist/host/launch/index.js +3 -1
  247. package/dist/host/launch/launch-plan.d.ts +13 -0
  248. package/dist/host/launch/launch-plan.js +26 -0
  249. package/dist/host/launch/mappings.js +2 -1
  250. package/dist/host/launch/risk-notes.d.ts +10 -0
  251. package/dist/host/launch/risk-notes.js +18 -0
  252. package/dist/host/launch/session-file.d.ts +2 -0
  253. package/dist/host/launch/session-file.js +7 -1
  254. package/dist/host/launch/wsb-file.d.ts +28 -4
  255. package/dist/host/launch/wsb-file.js +61 -42
  256. package/dist/host/life/crash-report.d.ts +27 -1
  257. package/dist/host/life/crash-report.js +173 -7
  258. package/dist/host/life/host-memory.d.ts +41 -0
  259. package/dist/host/life/host-memory.js +140 -0
  260. package/dist/host/life/index.d.ts +2 -0
  261. package/dist/host/life/index.js +2 -0
  262. package/dist/host/life/stop-events.d.ts +2 -0
  263. package/dist/host/life/stop-events.js +20 -3
  264. package/dist/host/life/stop-warning.js +1 -1
  265. package/dist/host/life/watchdog.d.ts +56 -0
  266. package/dist/host/life/watchdog.js +124 -0
  267. package/dist/host/native.d.ts +3 -2
  268. package/dist/host/native.js +22 -3
  269. package/dist/host/profile/claude-login.js +3 -2
  270. package/dist/host/sandbox/contained-deps.d.ts +2 -0
  271. package/dist/host/sandbox/contained-deps.js +17 -0
  272. package/dist/host/sandbox/index.d.ts +2 -0
  273. package/dist/host/sandbox/index.js +2 -0
  274. package/dist/host/sandbox/platform.d.ts +2 -0
  275. package/dist/host/sandbox/platform.js +7 -7
  276. package/dist/host/sandbox/tasklist.d.ts +13 -0
  277. package/dist/host/sandbox/tasklist.js +47 -0
  278. package/dist/host/secrets/launch-secrets.js +2 -1
  279. package/dist/host/sharing/current.js +2 -6
  280. package/dist/host/storage/contained/check.d.ts +9 -0
  281. package/dist/host/storage/contained/check.js +37 -0
  282. package/dist/host/storage/contained/contained-storage.d.ts +66 -0
  283. package/dist/host/storage/contained/contained-storage.js +168 -0
  284. package/dist/host/storage/contained/disk-port.d.ts +43 -0
  285. package/dist/host/storage/contained/disk-port.js +122 -0
  286. package/dist/host/storage/contained/index.d.ts +9 -0
  287. package/dist/host/storage/contained/index.js +8 -0
  288. package/dist/host/storage/contained/layout.d.ts +28 -0
  289. package/dist/host/storage/contained/layout.js +45 -0
  290. package/dist/host/storage/contained/resolve.d.ts +4 -0
  291. package/dist/host/storage/contained/resolve.js +30 -0
  292. package/dist/host/storage/contained/setting.d.ts +1 -0
  293. package/dist/host/storage/contained/setting.js +5 -0
  294. package/dist/host/storage/contained/state.d.ts +89 -0
  295. package/dist/host/storage/contained/state.js +166 -0
  296. package/dist/host/storage/contained/transfer.d.ts +24 -0
  297. package/dist/host/storage/contained/transfer.js +121 -0
  298. package/dist/host/storage/index.d.ts +2 -0
  299. package/dist/host/storage/index.js +2 -0
  300. package/dist/host/storage/providers/contained.d.ts +15 -0
  301. package/dist/host/storage/providers/contained.js +39 -0
  302. package/dist/host/storage/providers/folder.d.ts +12 -0
  303. package/dist/host/storage/providers/folder.js +20 -0
  304. package/dist/host/storage/providers/index.d.ts +4 -0
  305. package/dist/host/storage/providers/index.js +3 -0
  306. package/dist/host/storage/providers/registry.d.ts +9 -0
  307. package/dist/host/storage/providers/registry.js +14 -0
  308. package/dist/host/storage/providers/types.d.ts +33 -0
  309. package/dist/host/storage/providers/types.js +1 -0
  310. package/dist/host/tools/conpty.js +3 -2
  311. package/dist/host/tools/windows-terminal.js +8 -7
  312. package/dist/host/vpn/index.d.ts +1 -1
  313. package/dist/host/vpn/index.js +1 -1
  314. package/dist/host/vpn/prepare.d.ts +2 -0
  315. package/dist/host/vpn/prepare.js +63 -2
  316. package/dist/host/web/browser.d.ts +2 -0
  317. package/dist/host/web/browser.js +14 -1
  318. package/dist/host/web/index.d.ts +3 -2
  319. package/dist/host/web/index.js +3 -2
  320. package/dist/host/web/rpc.d.ts +24 -0
  321. package/dist/host/web/rpc.js +149 -0
  322. package/dist/host/web/runner.d.ts +2 -0
  323. package/dist/host/web/runner.js +1 -2
  324. package/dist/host/web/server.d.ts +7 -0
  325. package/dist/host/web/server.js +23 -3
  326. package/dist/runtime/agent.js +83 -18
  327. package/dist/runtime/daemon.js +1805 -1221
  328. package/dist/runtime/db.js +83 -18
  329. package/dist/runtime/main.js +779 -219
  330. package/dist/runtime/netproxy.js +73 -11
  331. package/dist/runtime/view.js +91 -16
  332. package/dist/selftest/agent-paths.test.js +11 -1
  333. package/dist/selftest/app.test.d.ts +2 -0
  334. package/dist/selftest/app.test.js +324 -0
  335. package/dist/selftest/channel.test.js +13 -0
  336. package/dist/selftest/cli.test.js +2 -1
  337. package/dist/selftest/config.test.js +59 -0
  338. package/dist/selftest/contained.test.d.ts +2 -0
  339. package/dist/selftest/contained.test.js +544 -0
  340. package/dist/selftest/credentials.test.js +1 -1
  341. package/dist/selftest/engine.test.d.ts +2 -0
  342. package/dist/selftest/engine.test.js +127 -0
  343. package/dist/selftest/guest-boot.test.js +47 -1
  344. package/dist/selftest/hcs.test.d.ts +2 -0
  345. package/dist/selftest/hcs.test.js +482 -0
  346. package/dist/selftest/heavy-load.test.d.ts +2 -0
  347. package/dist/selftest/heavy-load.test.js +355 -0
  348. package/dist/selftest/index.js +12 -0
  349. package/dist/selftest/layout.test.js +12 -0
  350. package/dist/selftest/menu-perf.test.d.ts +2 -0
  351. package/dist/selftest/menu-perf.test.js +248 -0
  352. package/dist/selftest/menu-screens.js +4 -2
  353. package/dist/selftest/menu.test.js +36 -8
  354. package/dist/selftest/network.test.js +20 -1
  355. package/dist/selftest/support/engine-cases.d.ts +8 -0
  356. package/dist/selftest/support/engine-cases.js +65 -0
  357. package/dist/selftest/support/engine-golden.d.ts +3 -0
  358. package/dist/selftest/support/engine-golden.js +66 -0
  359. package/dist/selftest/support/fixtures.d.ts +8 -1
  360. package/dist/selftest/support/hcs-probes.d.ts +17 -0
  361. package/dist/selftest/support/hcs-probes.js +41 -0
  362. package/dist/selftest/vpn.test.js +136 -3
  363. package/dist/selftest/web.test.js +101 -3
  364. package/dist/shared/channel/run-dir.d.ts +4 -1
  365. package/dist/shared/channel/run-dir.js +7 -1
  366. package/dist/shared/channel/schema.d.ts +35 -1
  367. package/dist/shared/channel/schema.js +27 -0
  368. package/dist/shared/channel/secrets.d.ts +1 -0
  369. package/dist/shared/channel/secrets.js +6 -0
  370. package/dist/shared/io/copy-tree.d.ts +7 -0
  371. package/dist/shared/io/copy-tree.js +45 -0
  372. package/dist/shared/io/index.d.ts +2 -0
  373. package/dist/shared/io/index.js +2 -0
  374. package/dist/shared/io/robocopy-result.d.ts +6 -0
  375. package/dist/shared/io/robocopy-result.js +23 -0
  376. package/dist/shared/memory/index.d.ts +1 -0
  377. package/dist/shared/memory/index.js +1 -0
  378. package/dist/shared/memory/policy.d.ts +38 -0
  379. package/dist/shared/memory/policy.js +63 -0
  380. package/dist/shared/network/firewall-rules.d.ts +8 -0
  381. package/dist/shared/network/firewall-rules.js +28 -0
  382. package/dist/shared/pty/output-filter.d.ts +14 -1
  383. package/dist/shared/pty/output-filter.js +22 -4
  384. package/dist/shared/pty/terminal.d.ts +4 -1
  385. package/dist/shared/pty/terminal.js +4 -2
  386. package/dist/shared/system.d.ts +1 -0
  387. package/dist/shared/system.js +1 -0
  388. package/dist/shared/vpn/download.d.ts +16 -0
  389. package/dist/shared/vpn/download.js +16 -0
  390. package/dist/shared/vpn/wireguard.d.ts +51 -0
  391. package/dist/shared/vpn/wireguard.js +227 -0
  392. package/dist/sync/sync-cycle.js +5 -5
  393. package/dist/sync/watcher.d.ts +13 -0
  394. package/dist/sync/watcher.js +92 -3
  395. package/dist/web/index.html +4 -3
  396. package/dist/web/main-NRRS5O6Z.js +157 -0
  397. package/dist/web/main-T5DMCHYV.css +1 -0
  398. package/native/csharp/HcsHost/AssemblyInfo.cs +14 -0
  399. package/native/csharp/HcsHost/Guard.cs +100 -0
  400. package/native/csharp/HcsHost/Hcs.cs +256 -0
  401. package/native/csharp/HcsHost/Json.cs +78 -0
  402. package/native/csharp/HcsHost/Program.cs +185 -0
  403. package/native/csharp/HcsHost/VirtualDisk.cs +95 -0
  404. package/native/csharp/HostTools/AssemblyInfo.cs +14 -0
  405. package/native/csharp/HostTools/MemoryStatus.cs +128 -0
  406. package/native/csharp/HostTools/Program.cs +58 -17
  407. package/native/scripts/check-contained.ps1 +28 -0
  408. package/native/scripts/contained-common.ps1 +241 -0
  409. package/native/scripts/dismount-contained.ps1 +66 -0
  410. package/native/scripts/memory-status.ps1 +42 -0
  411. package/native/scripts/mount-contained.ps1 +139 -0
  412. package/native/scripts/run-elevated.ps1 +17 -4
  413. package/native/scripts/stop-events.ps1 +23 -8
  414. package/package.json +9 -2
  415. package/sandbox/csharp/MemGov.cs +466 -0
  416. package/sandbox/csharp/PtyHost.cs +111 -5
  417. package/sandbox/scripts/new-shortcuts.ps1 +3 -1
  418. package/schemas/config.schema.json +66 -8
  419. package/schemas/sandbox-config.schema.json +3 -3
  420. package/dist/cli/commands/sessions/control.d.ts +0 -4
  421. package/dist/cli/commands/sessions/control.js +0 -66
  422. package/dist/cli/commands/sessions/list.d.ts +0 -2
  423. package/dist/cli/commands/sessions/list.js +0 -48
  424. package/dist/cli/commands/sessions/status.d.ts +0 -4
  425. package/dist/cli/commands/sessions/status.js +0 -114
  426. package/dist/cli/commands/sessions/window.d.ts +0 -3
  427. package/dist/cli/commands/sessions/window.js +0 -18
  428. package/dist/cli/menu/settings/path-kind.js +0 -44
  429. package/dist/web/main-CXESL347.js +0 -45
  430. package/dist/web/main-VLR7DVQ4.css +0 -1
  431. /package/dist/{cli/menu/settings → app/kernel}/schema-node.d.ts +0 -0
  432. /package/dist/{cli/menu/settings → app/kernel}/schema-node.js +0 -0
@@ -1,7 +1,8 @@
1
1
  // This PC: Windows itself, what Windows Sandbox needs of it, and whether Windows wants a restart. The hardware and
2
2
  // edition answers come from the same look `sandbox setup` takes (once per run).
3
3
  import { isWin } from '../../../../core/util/index.js';
4
- import { updateRestartPending } from '../../../../host/life/index.js';
4
+ import { pageFileRisk, readHostMemory, updateRestartPending } from '../../../../host/life/index.js';
5
+ import { decideMemory } from '../../../../host/launch/index.js';
5
6
  import { buildChecks } from '../../setup/checks.js';
6
7
  import { ok } from './common.js';
7
8
  const STATUS = { ok: 'ok', warn: 'warn', action: 'needs-you' };
@@ -38,6 +39,36 @@ export const PC_CHECKS = [
38
39
  fromFacts('pc-virtualization', 'This PC', 'CPU virtualization', 'CPU virtualization'),
39
40
  fromFacts('pc-memory', 'This PC', 'Memory', 'Memory'),
40
41
  fromFacts('pc-node', 'This PC', 'Node.js', 'Node.js'),
42
+ {
43
+ id: 'pc-commit',
44
+ group: 'This PC',
45
+ name: 'Memory for the sandbox',
46
+ timeoutMs: 40_000,
47
+ run: (env) => {
48
+ const memory = readHostMemory(env.storageRoot);
49
+ if (!memory)
50
+ return { status: 'skipped', detail: 'the memory of this PC could not be read' };
51
+ const sandbox = env.ctx.loaded.config.sandbox;
52
+ const planned = sandbox.memoryMb === 'auto'
53
+ ? decideMemory(sandbox, memory.availableMb, memory.totalMb, memory.commitAvailableMb).mb
54
+ : sandbox.memoryMb;
55
+ const detail = Math.floor(memory.commitAvailableMb / 1024) +
56
+ ' GB of RAM plus paging file free, page file ' +
57
+ memory.pageFile +
58
+ ', the next sandbox gets ' +
59
+ Math.floor(planned / 1024) +
60
+ ' GB';
61
+ const risk = pageFileRisk(memory, planned);
62
+ return risk
63
+ ? {
64
+ status: 'warn',
65
+ detail,
66
+ means: risk.text,
67
+ fix: 'Nothing is changed for you: set the page file as above (Windows keeps working either way).',
68
+ }
69
+ : ok(detail);
70
+ },
71
+ },
41
72
  {
42
73
  id: 'pc-restart-pending',
43
74
  group: 'This PC',
@@ -6,6 +6,7 @@ import path from 'node:path';
6
6
  import { dirExists, fileExists, isLink } from '../../../../core/util/index.js';
7
7
  import { SandboxChannel } from '../../../../host/channel/index.js';
8
8
  import { orphanWatchers, runStateProblems, staleLaunchFolders, watcherRecords } from '../../../../host/health/index.js';
9
+ import { vgpuSource, vgpuWarning } from '../../../../host/launch/index.js';
9
10
  import { hostToolsExe } from '../../../../host/native.js';
10
11
  import { sandboxFeatureInstalled } from '../../../../host/sandbox/index.js';
11
12
  import { assertTestWritable, refuseInSelfTest } from '../../../../host/test-guard.js';
@@ -19,6 +20,19 @@ import { ok, plural, rel } from './common.js';
19
20
  const LAUNCH_DAYS = 7;
20
21
  const hiddenWindowsFile = (env) => path.join(env.storageRoot, 'run', 'hidden-windows.json');
21
22
  export const SANDBOX_CHECKS = [
23
+ {
24
+ id: 'sandbox-vgpu',
25
+ group: 'Sandbox',
26
+ name: 'vGPU',
27
+ run: (env) => env.ctx.loaded.config.sandbox.vGpu
28
+ ? {
29
+ status: 'warn',
30
+ detail: 'vGPU is on in your settings',
31
+ means: vgpuWarning(vgpuSource(true)),
32
+ fix: 'Run: sandbox config set sandbox.vGpu false',
33
+ }
34
+ : ok('off'),
35
+ },
22
36
  {
23
37
  id: 'sandbox-exe',
24
38
  group: 'Sandbox',
@@ -198,7 +212,8 @@ export const SANDBOX_CHECKS = [
198
212
  timeoutMs: 150_000,
199
213
  run: (env) => {
200
214
  const bin = path.join(env.storageRoot, 'bin');
201
- const built = dirExists(bin) && fs.readdirSync(bin).some((n) => /^HostTools-[0-9a-f]+\.exe$/i.test(n));
215
+ const built = dirExists(bin) &&
216
+ fs.readdirSync(bin).some((n) => /^[0-9a-f]{12}$/i.test(n) && fs.existsSync(path.join(bin, n, 'HostTools.exe')));
202
217
  if (built)
203
218
  return ok('built in ' + bin);
204
219
  return {
@@ -1,17 +1,20 @@
1
1
  // The VPN, when it is on: is the login saved and readable, can the OpenVPN download be reached. Not shown when the
2
2
  // VPN is off. (That the storage folder is private is checked with the data folder.)
3
- import { VpnLogin } from '../../../../host/vpn/index.js';
4
- import { OPENVPN_MSI } from '../../../../shared/vpn/download.js';
3
+ import { VpnLogin, wireguardFileProblem } from '../../../../host/vpn/index.js';
4
+ import { OPENVPN_MSI, WIREGUARD_MSI } from '../../../../shared/vpn/download.js';
5
5
  import { ok } from './common.js';
6
6
  const REACH_MS = 6000;
7
7
  const vpnOn = (env) => env.ctx.loaded.config.vpn.enabled;
8
+ const wireguard = (env) => env.ctx.loaded.config.vpn.provider === 'wireguard-file';
9
+ const loginNeeded = (env) => vpnOn(env) && !wireguard(env);
10
+ const wireguardOn = (env) => vpnOn(env) && wireguard(env);
8
11
  export const VPN_CHECKS = [
9
12
  {
10
13
  id: 'vpn-login',
11
14
  group: 'Network',
12
15
  name: 'VPN login',
13
16
  checking: 'the saved VPN login',
14
- applies: vpnOn,
17
+ applies: loginNeeded,
15
18
  run: (env) => {
16
19
  const { provider } = env.ctx.loaded.config.vpn;
17
20
  const state = new VpnLogin(env.storageRoot).state(provider);
@@ -32,20 +35,42 @@ export const VPN_CHECKS = [
32
35
  };
33
36
  },
34
37
  },
38
+ {
39
+ id: 'vpn-config',
40
+ group: 'Network',
41
+ name: 'WireGuard config',
42
+ checking: 'the WireGuard config file',
43
+ applies: wireguardOn,
44
+ run: (env) => {
45
+ const problem = wireguardFileProblem(env.ctx.loaded.config.vpn.configFile, process.cwd());
46
+ return problem
47
+ ? {
48
+ status: 'needs-you',
49
+ detail: problem,
50
+ means: 'The sandbox cannot start its VPN, so a launch stops.',
51
+ fix: 'Set vpn.configFile to a plain WireGuard .conf (no PostUp, PreUp or other script lines).',
52
+ }
53
+ : ok('readable and plain (keys are not shown)');
54
+ },
55
+ },
35
56
  {
36
57
  id: 'vpn-download',
37
58
  group: 'Network',
38
- name: 'OpenVPN download',
39
- checking: 'whether the OpenVPN download can be reached',
59
+ name: 'VPN download',
60
+ checking: 'whether the VPN software download can be reached',
40
61
  applies: vpnOn,
41
62
  timeoutMs: REACH_MS + 4000,
42
63
  run: async (env) => {
43
- if (await env.system.reachable(OPENVPN_MSI.base, REACH_MS))
44
- return ok('swupdate.openvpn.org answers');
64
+ const [name, base] = wireguard(env)
65
+ ? ['WireGuard', WIREGUARD_MSI.base]
66
+ : ['OpenVPN', OPENVPN_MSI.base];
67
+ const host = new URL(base).host;
68
+ if (await env.system.reachable(base, REACH_MS))
69
+ return ok(host + ' answers');
45
70
  return {
46
71
  status: 'warn',
47
- detail: 'swupdate.openvpn.org did not answer within ' + REACH_MS / 1000 + ' seconds',
48
- means: 'The sandbox downloads OpenVPN there at its first VPN start (it is kept afterwards).',
72
+ detail: host + ' did not answer within ' + REACH_MS / 1000 + ' seconds',
73
+ means: 'The sandbox downloads ' + name + ' there at its first VPN start (it is kept afterwards).',
49
74
  fix: 'Check your internet connection or proxy, then run this again.',
50
75
  };
51
76
  },
@@ -6,6 +6,7 @@ import { WindowsProbe } from '../../../host/health/index.js';
6
6
  import { InstallLayout, InstallRecordFile } from '../../../host/install/index.js';
7
7
  import { MIGRATIONS } from '../../../host/migrate/index.js';
8
8
  import { isSandboxRunning } from '../../../host/sandbox/index.js';
9
+ import { hostDataFolder } from '../../../host/storage/index.js';
9
10
  import { gatherFacts } from '../setup/facts.js';
10
11
  /** The settings file in use: --config, AGENT_SANDBOX_CONFIG, else the user's config.json. */
11
12
  export function configFileOf(ctx) {
@@ -33,7 +34,7 @@ export function buildEnv(ctx, configError = null, system = new WindowsProbe()) {
33
34
  running: isSandboxRunning(),
34
35
  dataPath: ctx.dataPath,
35
36
  storageRoot: ctx.storageRoot,
36
- toolsDir: path.join(ctx.dataPath, 'tools'),
37
+ toolsDir: hostDataFolder(ctx.dataPath, 'tools'),
37
38
  configError: configProblem(ctx, configError),
38
39
  layout,
39
40
  record: layout ? new InstallRecordFile(layout.root).read() : null,
@@ -1,6 +1,2 @@
1
1
  export * from './attach.js';
2
- export * from './list.js';
3
- export * from './status.js';
4
2
  export * from './exec.js';
5
- export * from './control.js';
6
- export * from './window.js';
@@ -1,9 +1,5 @@
1
- // Working with a running sandbox: attach, sessions, status, exec, restart, stop, show/hide. Each knows which project
2
- // you run it from (project.ts): attach and sessions put that project's sessions first, exec and db run in it, restart
3
- // restarts its sessions, stop warns about the other projects' sessions.
1
+ // Working with a running sandbox: attach and exec. Each knows which project you run it from (projects/here.ts): attach
2
+ // puts that project's sessions first, exec and db run in it. Status, sessions, restart, stop and show/hide are the
3
+ // shared core's (src/app/features/sandbox and sessions).
4
4
  export * from './attach.js';
5
- export * from './list.js';
6
- export * from './status.js';
7
5
  export * from './exec.js';
8
- export * from './control.js';
9
- export * from './window.js';
@@ -21,9 +21,12 @@ export interface SandboxView {
21
21
  }
22
22
  /** For status and sessions: they work for anyone, and say whose sandbox this is. */
23
23
  export declare function lookAtSandbox(ctx: Context): SandboxView;
24
+ /** Asks for a typed confirmation ("Type alice to confirm"); true when it was typed. */
25
+ export type TypedConfirm = (prompt: string, expected: string) => Promise<boolean>;
24
26
  /**
25
27
  * For a command that acts on the running sandbox: refuses with the reason when this user may not, asks for the typed
26
- * confirmation when the rule wants one (--yes in a script), and returns the context to use.
28
+ * confirmation when the rule wants one (--yes in a script; `confirm` asks it, by default on the terminal), and returns
29
+ * the context to use.
27
30
  */
28
- export declare function useSharedSandbox(ctx: Context, action: OwnerAction | ((shared: Context) => OwnerAction)): Promise<Context>;
31
+ export declare function useSharedSandbox(ctx: Context, action: OwnerAction | ((shared: Context) => OwnerAction), confirm?: TypedConfirm): Promise<Context>;
29
32
  export {};
@@ -47,11 +47,18 @@ export function lookAtSandbox(ctx) {
47
47
  return { ctx: borrowedContext(ctx, owner), line, limited: false, owner };
48
48
  return { ctx, line, limited: owner.kind !== 'mine' && owner.kind !== 'none', owner };
49
49
  }
50
+ /** On the terminal: no terminal fails and says how to do it in a script. */
51
+ const terminalTypedConfirm = async (prompt, expected) => {
52
+ if (!(process.stdin.isTTY && process.stdout.isTTY))
53
+ fail(prompt + ' It was not stopped.', 'In a script, add --yes to do it anyway.');
54
+ return askTyped(prompt, expected);
55
+ };
50
56
  /**
51
57
  * For a command that acts on the running sandbox: refuses with the reason when this user may not, asks for the typed
52
- * confirmation when the rule wants one (--yes in a script), and returns the context to use.
58
+ * confirmation when the rule wants one (--yes in a script; `confirm` asks it, by default on the terminal), and returns
59
+ * the context to use.
53
60
  */
54
- export async function useSharedSandbox(ctx, action) {
61
+ export async function useSharedSandbox(ctx, action, confirm = terminalTypedConfirm) {
55
62
  const owner = ownerNow();
56
63
  if (!owner || owner.kind === 'mine' || owner.kind === 'none')
57
64
  return ctx;
@@ -59,11 +66,7 @@ export async function useSharedSandbox(ctx, action) {
59
66
  const rule = ownerRule(owner, typeof action === 'function' ? action(target) : action);
60
67
  if (rule.kind === 'refuse')
61
68
  return fail(rule.reason, rule.fix);
62
- if (rule.kind === 'confirm' && !ctx.options.yes) {
63
- if (!(process.stdin.isTTY && process.stdout.isTTY))
64
- fail(rule.prompt + ' It was not stopped.', 'In a script, add --yes to do it anyway.');
65
- if (!(await askTyped(rule.prompt, rule.expected)))
66
- fail('Nothing changed.');
67
- }
69
+ if (rule.kind === 'confirm' && !ctx.options.yes && !(await confirm(rule.prompt, rule.expected)))
70
+ fail('Nothing changed.');
68
71
  return target;
69
72
  }
@@ -0,0 +1,3 @@
1
+ import type { Context } from '../context.js';
2
+ /** `sandbox storage contained [status|on|off]`. */
3
+ export declare function contained(ctx: Context): Promise<number>;
@@ -0,0 +1,179 @@
1
+ // `sandbox storage contained on|off|status` (beta): keep the data set's tools, state and logs in a
2
+ // disk image that is mounted only while a sandbox runs. The work is in host/storage/contained; this file asks, shows
3
+ // size and time, and prints.
4
+ import fs from 'node:fs';
5
+ import { userConfigPath } from '../../../core/config/index.js';
6
+ import { humanBytes } from '../../../core/disk.js';
7
+ import { askRequired } from '../../../core/prompt.js';
8
+ import { fail } from '../../../core/util/index.js';
9
+ import { isSandboxRunning, realContainedDeps } from '../../../host/sandbox/index.js';
10
+ import { assertTargetsFree, containedAvailability, ContainedStorage, describeDuration, describeSize, planTransfer, runTransfer, setStateOf, StateFile, writeContainedSetting, } from '../../../host/storage/index.js';
11
+ import { c, printJson, working } from '../../output/index.js';
12
+ const BETA = 'Beta, and not yet tested on a real PC. ';
13
+ function storageFor(ctx) {
14
+ const { config } = ctx.loaded;
15
+ return new ContainedStorage(realContainedDeps(ctx.storageRoot, config.storage.containedMaxGb), config.storage.dataName);
16
+ }
17
+ function requireAvailable() {
18
+ const check = containedAvailability();
19
+ if (!check.available)
20
+ fail('Contained storage cannot be used: ' + check.reasons.join(' '), check.fixes.join(' '));
21
+ }
22
+ function requireNoSandbox() {
23
+ if (isSandboxRunning())
24
+ fail('A sandbox is running.', 'Close it (sandbox stop) first: the folders in the image are in use.');
25
+ }
26
+ async function status(ctx) {
27
+ const storage = storageFor(ctx);
28
+ const check = containedAvailability();
29
+ const state = new StateFile(storage.paths).read();
30
+ const record = setStateOf(state, storage.set);
31
+ const reality = check.available && storage.imageExists() ? await storage.reality() : null;
32
+ const answer = {
33
+ setting: ctx.loaded.config.storage.contained,
34
+ maxGb: ctx.loaded.config.storage.containedMaxGb,
35
+ available: check.available,
36
+ reasons: check.reasons,
37
+ image: storage.paths.image,
38
+ imageExists: storage.imageExists(),
39
+ imageBytes: reality?.imageBytes ?? null,
40
+ mountFolder: storage.paths.mount,
41
+ recordedMounted: record.mounted,
42
+ holders: record.holders.length,
43
+ mountedNow: reality?.mountIsMountPoint ?? null,
44
+ };
45
+ if (printJson(ctx, answer))
46
+ return 0;
47
+ console.log(c.yellow('Contained storage (beta, untested on a real PC)'));
48
+ console.log(' setting: storage.contained = ' + answer.setting + ' (limit ' + answer.maxGb + ' GB)');
49
+ console.log(' this PC: ' +
50
+ (check.available ? 'can use it' : 'cannot use it: ' + check.reasons.join(' ') + ' ' + check.fixes.join(' ')));
51
+ console.log(' image: ' +
52
+ answer.image +
53
+ (answer.imageExists ? ' (' + humanBytes(answer.imageBytes ?? 0) + ' on disk)' : ' (not made yet)'));
54
+ console.log(' mount folder: ' + answer.mountFolder);
55
+ if (reality)
56
+ console.log(' mounted now: ' +
57
+ (reality.mountIsMountPoint ? 'yes' : 'no') +
58
+ (record.mounted !== reality.mountIsMountPoint
59
+ ? ' (the record says ' + (record.mounted ? 'yes' : 'no') + '; sandbox doctor looks into it)'
60
+ : ''));
61
+ console.log('\nTurn it on with: sandbox storage contained on Turn it off with: sandbox storage contained off');
62
+ return 0;
63
+ }
64
+ function printPlan(plan, to) {
65
+ for (const f of plan.folders)
66
+ console.log(' ' + f.name + ': ' + describeSize(f.measured));
67
+ console.log(' total: ' + describeSize(plan.total) + ' to ' + to);
68
+ console.log(' time: about ' + describeDuration(plan.estimate.seconds) + ' (copied, then the originals are removed)');
69
+ if (!plan.estimate.fits)
70
+ fail('Not enough free space for the copy.', 'Free some space on that drive and try again.');
71
+ }
72
+ async function turnOn(ctx) {
73
+ requireAvailable();
74
+ requireNoSandbox();
75
+ const storage = storageFor(ctx);
76
+ const { config } = ctx.loaded;
77
+ const configFile = ctx.loaded.configPath ?? userConfigPath();
78
+ console.log(c.yellow(BETA + 'Contained storage keeps the sandbox tools, state and logs in one disk image file.'));
79
+ console.log(' image: ' + storage.paths.image + (storage.imageExists() ? ' (exists, it is used as it is)' : ''));
80
+ console.log(' limit: ' + config.storage.containedMaxGb + ' GB (storage.containedMaxGb; the file grows only as it fills)');
81
+ console.log(' Windows asks for permission to mount it each time a sandbox starts (and once when it closes).');
82
+ if (!ctx.options.yes &&
83
+ !(await askRequired('Turn contained storage on?', 'Add --yes to turn it on without asking.', true)))
84
+ return fail('Nothing was changed.', 'Run sandbox storage contained on when you want it.');
85
+ const mount = await working('Making and mounting the image (Windows asks for permission)', () => storage.acquire({ id: 'cli', pid: process.pid }, { create: true }));
86
+ try {
87
+ const plan = planTransfer(ctx.dataPath, mount, ctx.storageRoot);
88
+ if (plan.folders.length > 0) {
89
+ console.log('\nThese folders hold your tools, state and logs now:');
90
+ printPlan(plan, 'the image');
91
+ assertTargetsFree(plan);
92
+ const move = ctx.options.yes || (await askRequired('Move them into the image?', 'Add --yes to move without asking.', true));
93
+ if (move) {
94
+ let line = 'Copying';
95
+ const result = await working(() => line, () => runTransfer(plan, ctx.dataPath, (p) => {
96
+ line = 'Copying: ' + p.files.toLocaleString('en-US') + ' files, ' + humanBytes(p.bytes);
97
+ }));
98
+ console.log('Moved ' + result.files.toLocaleString('en-US') + ' files into the image.');
99
+ for (const note of result.notes)
100
+ console.log(c.yellow(note));
101
+ }
102
+ else
103
+ console.log('They stay where they are and are not used: tools are downloaded again into the image. Delete them later with sandbox data clean.');
104
+ }
105
+ writeContainedSetting(configFile, 'vhdx');
106
+ }
107
+ finally {
108
+ const done = await working('Dismounting the image (Windows asks for permission)', () => storage.release('cli'));
109
+ if (!done.dismounted)
110
+ console.log(c.yellow('The image stays mounted (' + (done.note ?? '') + '). sandbox repair dismounts it.'));
111
+ }
112
+ console.log('Contained storage is on. sandbox launch mounts the image first.');
113
+ return 0;
114
+ }
115
+ async function turnOff(ctx) {
116
+ requireAvailable();
117
+ requireNoSandbox();
118
+ const storage = storageFor(ctx);
119
+ const configFile = ctx.loaded.configPath ?? userConfigPath();
120
+ console.log(c.yellow(BETA + 'Turning contained storage off moves the files back into plain folders.'));
121
+ if (!storage.imageExists()) {
122
+ writeContainedSetting(configFile, 'off');
123
+ console.log('There is no image, so there was nothing to move. Contained storage is off.');
124
+ return 0;
125
+ }
126
+ const mount = await working('Mounting the image (Windows asks for permission)', () => storage.acquire({ id: 'cli', pid: process.pid }));
127
+ let moved = false;
128
+ try {
129
+ const plan = planTransfer(mount, ctx.dataPath, ctx.dataPath);
130
+ if (plan.folders.length > 0) {
131
+ console.log('\nIn the image:');
132
+ printPlan(plan, ctx.dataPath);
133
+ assertTargetsFree(plan);
134
+ if (!ctx.options.yes &&
135
+ !(await askRequired('Move them back out of the image?', 'Add --yes to move without asking.', true)))
136
+ return fail('Nothing was moved.', 'Contained storage is still on.');
137
+ let line = 'Copying';
138
+ const result = await working(() => line, () => runTransfer(plan, mount, (p) => {
139
+ line = 'Copying: ' + p.files.toLocaleString('en-US') + ' files, ' + humanBytes(p.bytes);
140
+ }));
141
+ console.log('Moved ' + result.files.toLocaleString('en-US') + ' files back.');
142
+ for (const note of result.notes)
143
+ console.log(c.yellow(note));
144
+ }
145
+ writeContainedSetting(configFile, 'off');
146
+ moved = true;
147
+ }
148
+ finally {
149
+ const done = await working('Dismounting the image (Windows asks for permission)', () => storage.release('cli'));
150
+ if (!done.dismounted)
151
+ console.log(c.yellow('The image stays mounted (' + (done.note ?? '') + '). sandbox repair dismounts it.'));
152
+ else if (!moved)
153
+ console.log('The image was dismounted; contained storage is still on.');
154
+ }
155
+ console.log('Contained storage is off: tools, state and logs are plain folders in ' + ctx.dataPath + '.');
156
+ const bytes = fs.statSync(storage.paths.image).size;
157
+ if (!ctx.options.yes &&
158
+ (await askRequired('Delete the image file now (' + humanBytes(bytes) + ')? Everything in it was moved out.', 'Answer no to keep it.', false))) {
159
+ await storage.deleteImage();
160
+ console.log('Deleted ' + storage.paths.image + '.');
161
+ }
162
+ else
163
+ console.log('The image file is kept: ' + storage.paths.image + ' (delete it yourself when you are sure).');
164
+ return 0;
165
+ }
166
+ /** `sandbox storage contained [status|on|off]`. */
167
+ export function contained(ctx) {
168
+ const [, mode = 'status'] = ctx.options.positionals;
169
+ switch (mode) {
170
+ case 'status':
171
+ return status(ctx);
172
+ case 'on':
173
+ return turnOn(ctx);
174
+ case 'off':
175
+ return turnOff(ctx);
176
+ default:
177
+ return Promise.resolve(fail('Unknown action "' + mode + '" for contained storage.', 'Use: sandbox storage contained on, off or status'));
178
+ }
179
+ }
@@ -5,7 +5,7 @@ import { userDir } from '../../../core/config/index.js';
5
5
  import { errMsg } from '../../../core/util/index.js';
6
6
  import { isSandboxRunning, insideSandbox } from '../../../host/sandbox/index.js';
7
7
  import { protectPrivateFolder } from '../../../host/sharing/index.js';
8
- import { describeDuration, describeSize, ensureStorageRoot, estimate, measureFolder, moveWarning, planOffer, StorageHistory, } from '../../../host/storage/index.js';
8
+ import { describeDuration, describeSize, ensureStorageRoot, estimate, measureFolder, moveWarning, planOffer, staleMountNote, StorageHistory, } from '../../../host/storage/index.js';
9
9
  import { c, working } from '../../output/index.js';
10
10
  import { refreshStorage } from '../context.js';
11
11
  import { performMove } from './storage.js';
@@ -82,4 +82,10 @@ export async function storageBeforeCommand(ctx, command) {
82
82
  }
83
83
  }
84
84
  ensureStorageRoot(ctx.storageRoot, protectPrivateFolder);
85
+ // Contained storage (beta): a mount an earlier sandbox left behind is only mentioned here; repair fixes it.
86
+ if (command !== 'launch' && !ctx.options.json) {
87
+ const stale = staleMountNote(ctx.storageRoot, ctx.loaded.config.storage.dataName, isSandboxRunning());
88
+ if (stale)
89
+ console.error(c.yellow(stale));
90
+ }
85
91
  }
@@ -6,14 +6,20 @@ import { userConfigPath, userDir } from '../../../core/config/index.js';
6
6
  import { diskUsage, humanBytes } from '../../../core/disk.js';
7
7
  import { askChoice, askName, askRequired } from '../../../core/prompt.js';
8
8
  import { expandPath, fail } from '../../../core/util/index.js';
9
- import { describeDuration, describeSize, estimate, holdsData, measureFolder, moveWarning, otherDataFolders, sharedDefaultRoot, StorageHistory, StorageMove, } from '../../../host/storage/index.js';
9
+ import { describeDuration, describeSize, estimate, holdsData, measureFolder, mountedSets, moveWarning, otherDataFolders, sharedDefaultRoot, StorageHistory, StorageMove, } from '../../../host/storage/index.js';
10
10
  import { isSandboxRunning } from '../../../host/sandbox/index.js';
11
11
  import { protectPrivateFolder } from '../../../host/sharing/index.js';
12
12
  import { watcherRunning } from '../../../sync/index.js';
13
13
  import { printJson, working } from '../../output/index.js';
14
14
  import { requireOnPc } from '../context.js';
15
+ import { contained } from './contained.js';
15
16
  /** Why a move cannot start (a background sync is still on a data folder in the old storage), or null. */
16
17
  export function storageBusy(root) {
18
+ const mounted = mountedSets(root);
19
+ if (mounted.length > 0)
20
+ return ('The contained storage of "' +
21
+ mounted.join('", "') +
22
+ '" is mounted (a disk image; moving now would reach into it).');
17
23
  const data = path.join(root, 'data');
18
24
  try {
19
25
  for (const name of fs.readdirSync(data))
@@ -165,7 +171,9 @@ export function storage(ctx) {
165
171
  return move(ctx);
166
172
  case 'use':
167
173
  return use(ctx);
174
+ case 'contained':
175
+ return contained(ctx);
168
176
  default:
169
- return fail('Unknown storage action "' + action + '".', 'Use: sandbox storage, sandbox storage move <folder> or sandbox storage use <folder>');
177
+ return fail('Unknown storage action "' + action + '".', 'Use: sandbox storage, sandbox storage move <folder>, sandbox storage use <folder> or sandbox storage contained on|off|status');
170
178
  }
171
179
  }
@@ -6,7 +6,7 @@ import { parse } from 'jsonc-parser';
6
6
  import _ from 'lodash';
7
7
  import { userConfigPath } from '../../core/config/index.js';
8
8
  import { fail } from '../../core/util/index.js';
9
- import { editConfigText } from '../menu/settings/index.js';
9
+ import { editConfigText } from '../../app/features/settings/index.js';
10
10
  export function configFile(ctx) {
11
11
  return ctx.loaded.configPath ?? userConfigPath();
12
12
  }
@@ -12,6 +12,7 @@ const LOGIN_TEXT = {
12
12
  };
13
13
  /** The warnings every VPN user must see. */
14
14
  export const VPN_WARNINGS = [
15
+ 'The VPN is an alpha: not yet tried on a real PC (docs/vpn.md lists what still needs checking).',
15
16
  'The agent in the sandbox can read the VPN login (a service login you can revoke) and can turn the tunnel off, because it is the administrator there.',
16
17
  'Free VPNs see all your traffic: use one you trust.',
17
18
  ];
@@ -21,14 +22,26 @@ async function status(ctx) {
21
22
  return 0;
22
23
  table([
23
24
  ['VPN', report.enabled ? c.green('on') : 'off (turn on: sandbox config set vpn.enabled true)'],
24
- ['Provider', report.provider === 'nordvpn' ? 'NordVPN' : 'your own .ovpn file'],
25
+ [
26
+ 'Provider',
27
+ report.provider === 'nordvpn'
28
+ ? 'NordVPN'
29
+ : report.provider === 'wireguard-file'
30
+ ? 'your own WireGuard .conf'
31
+ : 'your own .ovpn file',
32
+ ],
25
33
  ['Protocol', report.protocol],
26
34
  ['Country', report.country || 'best available'],
27
35
  ['Kill switch', report.killSwitch ? 'on (no internet if the tunnel fails)' : c.yellow('off')],
28
- ['Login', LOGIN_TEXT[report.login] + (report.loginSavedAt ? c.dim(' ' + report.loginSavedAt.slice(0, 10)) : '')],
36
+ [
37
+ 'Login',
38
+ report.provider === 'wireguard-file'
39
+ ? c.dim('not needed (the keys are in your config)')
40
+ : LOGIN_TEXT[report.login] + (report.loginSavedAt ? c.dim(' ' + report.loginSavedAt.slice(0, 10)) : ''),
41
+ ],
29
42
  ...(report.running ? [['Running now', report.running.text]] : []),
30
43
  ]);
31
- if (report.login === 'none')
44
+ if (report.provider !== 'wireguard-file' && report.login === 'none')
32
45
  nextStep('sandbox vpn login');
33
46
  if (report.login === 'unreadable')
34
47
  console.log(c.dim('Only the Windows user that saved it can read it. Save it again: sandbox vpn login'));
@@ -39,6 +52,10 @@ async function status(ctx) {
39
52
  async function login(ctx) {
40
53
  requireOnPc('vpn login');
41
54
  const service = new VpnService(ctx);
55
+ if (service.provider === 'wireguard-file') {
56
+ console.log('A WireGuard config needs no login: the keys are in the file vpn.configFile points to.');
57
+ return 0;
58
+ }
42
59
  const nord = service.provider === 'nordvpn';
43
60
  console.log(nord
44
61
  ? 'NordVPN needs its service credentials, not your usual login. To get them:'
@@ -1,4 +1,9 @@
1
1
  import { type Context } from './context.js';
2
+ /**
3
+ * At a sandbox start, with web.enabled: the web UI is started in the background when it is not running, and with
4
+ * web.openOnLaunch opened in the browser, signed in. Never stops the launch: a problem is one line.
5
+ */
6
+ export declare function startWebOnLaunch(ctx: Context): Promise<void>;
2
7
  export declare function web(ctx: Context): Promise<number>;
3
8
  /** Body of the background process (`sandbox _web`): runs until stopped. */
4
9
  export declare function webServer(ctx: Context): Promise<void>;
@@ -6,6 +6,7 @@ import { fail, sleep } from '../../core/util/index.js';
6
6
  import { spawnDetached } from '../../host/sandbox/index.js';
7
7
  import { openBrowser, WebControl, WebRunner, WebStore } from '../../host/web/index.js';
8
8
  import { printJson } from '../output/index.js';
9
+ import { createRpcHost } from '../../app/registry.js';
9
10
  import { WebService } from '../services/index.js';
10
11
  import { requireOnPc } from './context.js';
11
12
  const START_WAIT_MS = 15000;
@@ -36,6 +37,44 @@ async function startServer(ctx, control) {
36
37
  }
37
38
  return fail('The web UI did not start.', 'Run sandbox _web to see why (the port ' + config.web.port + ' may be taken: change web.port).');
38
39
  }
40
+ /**
41
+ * At a sandbox start, with web.enabled: the web UI is started in the background when it is not running, and with
42
+ * web.openOnLaunch opened in the browser, signed in. Never stops the launch: a problem is one line.
43
+ */
44
+ export async function startWebOnLaunch(ctx) {
45
+ const { web: settings } = ctx.loaded.config;
46
+ if (!settings.enabled)
47
+ return;
48
+ try {
49
+ const control = controlOf(ctx);
50
+ const before = await control.status();
51
+ let state = before.running ? before.state : null;
52
+ if (!state) {
53
+ if (before.state)
54
+ control.clear();
55
+ const configFile = ctx.options.config;
56
+ spawnDetached(ctx.entry, ['_web', ...(configFile ? ['--config', path.resolve(configFile)] : [])]);
57
+ for (const started = Date.now(); !state && Date.now() - started < START_WAIT_MS; await sleep(300)) {
58
+ const now = await control.status();
59
+ if (now.running && now.state)
60
+ state = now.state;
61
+ }
62
+ }
63
+ if (!state) {
64
+ console.log('The web UI did not start (port ' + settings.port + ' may be taken: change web.port). Try: sandbox web start');
65
+ return;
66
+ }
67
+ if (settings.openOnLaunch) {
68
+ openBrowser(control.url(state));
69
+ console.log('The web UI is open in your browser (http://127.0.0.1:' + state.port + '/).');
70
+ }
71
+ else
72
+ console.log('The web UI is at http://127.0.0.1:' + state.port + '/ (open it signed in with: sandbox web open).');
73
+ }
74
+ catch (e) {
75
+ console.log('The web UI could not be started: ' + (e instanceof Error ? e.message : String(e)));
76
+ }
77
+ }
39
78
  export async function web(ctx) {
40
79
  requireOnPc('web');
41
80
  const action = ctx.options.positionals[0] ?? 'status';
@@ -89,6 +128,7 @@ export async function webServer(ctx) {
89
128
  storageRoot: ctx.storageRoot,
90
129
  port: ctx.loaded.config.web.port,
91
130
  backend: new WebService(ctx),
131
+ rpc: createRpcHost(ctx),
92
132
  staticRoot,
93
133
  }).run();
94
134
  process.exit(0);
package/dist/cli/index.js CHANGED
@@ -15,6 +15,7 @@ import { withConfig, hoistConfig, withoutConfig } from './argv.js';
15
15
  import { report } from './report.js';
16
16
  import { versionText } from './version.js';
17
17
  import { HANDLERS } from './commands/index.js';
18
+ import { operationHandler } from './operations/index.js';
18
19
  const entry = path.join(PACKAGE_ROOT, 'dist', 'bin', 'agent-sandbox.js');
19
20
  /** A first word that is no command and no agent, but close to one: "Did you mean: sandbox status?". */
20
21
  function failOnTypo(word, agents) {
@@ -103,7 +104,7 @@ export async function main(rawArgv, preset = {}) {
103
104
  // update works on the install, not on a sandbox: it needs no settings (and a broken file must not stop it).
104
105
  if (options.command === 'update')
105
106
  return commands.update(options);
106
- const handler = HANDLERS[options.command];
107
+ const handler = operationHandler(options.command, options.positionals) ?? HANDLERS[options.command];
107
108
  if (!handler)
108
109
  return fail('Unknown command "' + options.command + '".', 'Run sandbox --help for the list of commands, or sandbox for the menu.');
109
110
  const ctx = commands.createContext(PACKAGE_ROOT, entry, options);