windows-development-sandbox 1.5.0 → 1.7.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 (448) hide show
  1. package/README.md +1 -0
  2. package/config.example.json +17 -1
  3. package/dist/cli/args.d.ts +13 -2
  4. package/dist/cli/args.js +95 -24
  5. package/dist/cli/commands/accounts.js +45 -17
  6. package/dist/cli/commands/context.d.ts +2 -0
  7. package/dist/cli/commands/context.js +9 -2
  8. package/dist/cli/commands/data/clean.js +32 -9
  9. package/dist/cli/commands/index.d.ts +1 -0
  10. package/dist/cli/commands/index.js +1 -0
  11. package/dist/cli/commands/launch/add-root.d.ts +3 -2
  12. package/dist/cli/commands/launch/add-root.js +11 -10
  13. package/dist/cli/commands/launch/launch.js +57 -4
  14. package/dist/cli/commands/launch/prepare.js +3 -1
  15. package/dist/cli/commands/maintenance/audit.js +1 -1
  16. package/dist/cli/commands/maintenance/clean.d.ts +1 -1
  17. package/dist/cli/commands/maintenance/clean.js +17 -3
  18. package/dist/cli/commands/maintenance/config.d.ts +1 -1
  19. package/dist/cli/commands/maintenance/config.js +112 -10
  20. package/dist/cli/commands/maintenance/doctor.d.ts +1 -1
  21. package/dist/cli/commands/maintenance/doctor.js +2 -4
  22. package/dist/cli/commands/maintenance/migrate.d.ts +2 -1
  23. package/dist/cli/commands/maintenance/migrate.js +19 -4
  24. package/dist/cli/commands/maintenance/sync.js +2 -0
  25. package/dist/cli/commands/maintenance/update.d.ts +2 -0
  26. package/dist/cli/commands/maintenance/update.js +27 -4
  27. package/dist/cli/commands/projects/here.js +12 -7
  28. package/dist/cli/commands/projects/projects.js +26 -17
  29. package/dist/cli/commands/registry.js +7 -0
  30. package/dist/cli/commands/repair/checks/agents.d.ts +2 -0
  31. package/dist/cli/commands/repair/checks/agents.js +70 -0
  32. package/dist/cli/commands/repair/checks/common.d.ts +8 -0
  33. package/dist/cli/commands/repair/checks/common.js +22 -0
  34. package/dist/cli/commands/repair/checks/data.d.ts +2 -0
  35. package/dist/cli/commands/repair/checks/data.js +241 -0
  36. package/dist/cli/commands/repair/checks/index.d.ts +2 -0
  37. package/dist/cli/commands/repair/checks/index.js +20 -0
  38. package/dist/cli/commands/repair/checks/install.d.ts +5 -0
  39. package/dist/cli/commands/repair/checks/install.js +318 -0
  40. package/dist/cli/commands/repair/checks/machine.d.ts +2 -0
  41. package/dist/cli/commands/repair/checks/machine.js +232 -0
  42. package/dist/cli/commands/repair/checks/network.d.ts +6 -0
  43. package/dist/cli/commands/repair/checks/network.js +60 -0
  44. package/dist/cli/commands/repair/checks/pc.d.ts +4 -0
  45. package/dist/cli/commands/repair/checks/pc.js +55 -0
  46. package/dist/cli/commands/repair/checks/sandbox.d.ts +2 -0
  47. package/dist/cli/commands/repair/checks/sandbox.js +267 -0
  48. package/dist/cli/commands/repair/checks/settings.d.ts +2 -0
  49. package/dist/cli/commands/repair/checks/settings.js +169 -0
  50. package/dist/cli/commands/repair/checks/vpn.d.ts +2 -0
  51. package/dist/cli/commands/repair/checks/vpn.js +53 -0
  52. package/dist/cli/commands/repair/engine.d.ts +32 -0
  53. package/dist/cli/commands/repair/engine.js +127 -0
  54. package/dist/cli/commands/repair/env.d.ts +7 -0
  55. package/dist/cli/commands/repair/env.js +49 -0
  56. package/dist/cli/commands/repair/index.d.ts +1 -9
  57. package/dist/cli/commands/repair/index.js +2 -54
  58. package/dist/cli/commands/repair/log.d.ts +17 -0
  59. package/dist/cli/commands/repair/log.js +82 -0
  60. package/dist/cli/commands/repair/printer.d.ts +32 -0
  61. package/dist/cli/commands/repair/printer.js +132 -0
  62. package/dist/cli/commands/repair/run.d.ts +33 -0
  63. package/dist/cli/commands/repair/run.js +133 -0
  64. package/dist/cli/commands/repair/types.d.ts +97 -9
  65. package/dist/cli/commands/repair/types.js +11 -7
  66. package/dist/cli/commands/resume.js +2 -2
  67. package/dist/cli/commands/sessions/list.js +5 -2
  68. package/dist/cli/commands/sessions/status.d.ts +2 -0
  69. package/dist/cli/commands/sessions/status.js +27 -6
  70. package/dist/cli/commands/setup/index.d.ts +5 -0
  71. package/dist/cli/commands/setup/index.js +25 -1
  72. package/dist/cli/commands/storage/index.d.ts +2 -0
  73. package/dist/cli/commands/storage/index.js +2 -0
  74. package/dist/cli/commands/storage/offer.d.ts +3 -0
  75. package/dist/cli/commands/storage/offer.js +85 -0
  76. package/dist/cli/commands/storage/storage.d.ts +8 -0
  77. package/dist/cli/commands/storage/storage.js +171 -0
  78. package/dist/cli/commands/user-config.js +1 -1
  79. package/dist/cli/commands/vpn.d.ts +4 -0
  80. package/dist/cli/commands/vpn.js +75 -0
  81. package/dist/cli/commands/web.d.ts +4 -0
  82. package/dist/cli/commands/web.js +95 -0
  83. package/dist/cli/history.js +1 -1
  84. package/dist/cli/index.js +46 -3
  85. package/dist/cli/manifest.d.ts +303 -29
  86. package/dist/cli/manifest.js +173 -43
  87. package/dist/cli/menu/add-agent.d.ts +47 -0
  88. package/dist/cli/menu/add-agent.js +168 -0
  89. package/dist/cli/menu/apps.d.ts +31 -2
  90. package/dist/cli/menu/apps.js +44 -5
  91. package/dist/cli/menu/command-fields.d.ts +18 -0
  92. package/dist/cli/menu/command-fields.js +61 -0
  93. package/dist/cli/menu/command-form.d.ts +22 -0
  94. package/dist/cli/menu/command-form.js +169 -46
  95. package/dist/cli/menu/data-folder.d.ts +18 -0
  96. package/dist/cli/menu/data-folder.js +99 -0
  97. package/dist/cli/menu/menu.d.ts +1 -1
  98. package/dist/cli/menu/menu.js +11 -8
  99. package/dist/cli/menu/pages/accounts-page.js +55 -24
  100. package/dist/cli/menu/pages/agents-page.d.ts +66 -0
  101. package/dist/cli/menu/pages/agents-page.js +338 -0
  102. package/dist/cli/menu/pages/apps-page.js +1 -2
  103. package/dist/cli/menu/pages/list-page.d.ts +16 -3
  104. package/dist/cli/menu/pages/list-page.js +40 -8
  105. package/dist/cli/menu/pages/maintenance-page.d.ts +9 -0
  106. package/dist/cli/menu/pages/maintenance-page.js +161 -72
  107. package/dist/cli/menu/pages/sandbox-page.d.ts +3 -0
  108. package/dist/cli/menu/pages/sandbox-page.js +132 -43
  109. package/dist/cli/menu/pages/sessions-page.d.ts +4 -0
  110. package/dist/cli/menu/pages/sessions-page.js +41 -25
  111. package/dist/cli/menu/pages/settings-page.d.ts +8 -4
  112. package/dist/cli/menu/pages/settings-page.js +36 -13
  113. package/dist/cli/menu/settings/categories.d.ts +2 -0
  114. package/dist/cli/menu/settings/categories.js +19 -44
  115. package/dist/cli/menu/settings/collections/collection-editor.d.ts +7 -1
  116. package/dist/cli/menu/settings/collections/collection-editor.js +12 -8
  117. package/dist/cli/menu/settings/collections/map-editor.js +5 -1
  118. package/dist/cli/menu/settings/collections/object-list-editor.js +2 -2
  119. package/dist/cli/menu/settings/collections/object-map-editor.js +2 -2
  120. package/dist/cli/menu/settings/collections/string-list-editor.d.ts +11 -1
  121. package/dist/cli/menu/settings/collections/string-list-editor.js +23 -0
  122. package/dist/cli/menu/settings/index.d.ts +1 -1
  123. package/dist/cli/menu/settings/index.js +1 -1
  124. package/dist/cli/menu/settings/object-editor.d.ts +5 -1
  125. package/dist/cli/menu/settings/object-editor.js +15 -4
  126. package/dist/cli/menu/settings/path-kind.d.ts +20 -0
  127. package/dist/cli/menu/settings/path-kind.js +44 -0
  128. package/dist/cli/menu/settings/schema-node.d.ts +9 -0
  129. package/dist/cli/menu/settings/schema-node.js +26 -0
  130. package/dist/cli/menu/settings/value-editor.d.ts +5 -0
  131. package/dist/cli/menu/settings/value-editor.js +51 -18
  132. package/dist/cli/menu/several.js +7 -5
  133. package/dist/cli/menu/shell.d.ts +37 -10
  134. package/dist/cli/menu/shell.js +90 -10
  135. package/dist/cli/menu/vpn-login.d.ts +16 -0
  136. package/dist/cli/menu/vpn-login.js +66 -0
  137. package/dist/cli/output/print.d.ts +2 -0
  138. package/dist/cli/output/print.js +8 -1
  139. package/dist/cli/services/accounts-service.d.ts +2 -0
  140. package/dist/cli/services/accounts-service.js +2 -2
  141. package/dist/cli/services/health-service.d.ts +0 -6
  142. package/dist/cli/services/health-service.js +5 -42
  143. package/dist/cli/services/index.d.ts +3 -1
  144. package/dist/cli/services/index.js +3 -1
  145. package/dist/cli/services/migrate-service.js +1 -1
  146. package/dist/cli/services/update-service.d.ts +2 -0
  147. package/dist/cli/services/update-service.js +13 -6
  148. package/dist/cli/services/vpn-service.d.ts +28 -0
  149. package/dist/cli/services/vpn-service.js +46 -0
  150. package/dist/cli/services/web-service.d.ts +22 -0
  151. package/dist/cli/services/web-service.js +111 -0
  152. package/dist/cli/tui/app.d.ts +6 -3
  153. package/dist/cli/tui/app.js +12 -5
  154. package/dist/cli/tui/dialogs/path-listing.d.ts +43 -0
  155. package/dist/cli/tui/dialogs/path-listing.js +127 -0
  156. package/dist/cli/tui/dialogs/path-picker.d.ts +67 -0
  157. package/dist/cli/tui/dialogs/path-picker.js +423 -0
  158. package/dist/cli/tui/dialogs/text-screen.d.ts +6 -0
  159. package/dist/cli/tui/dialogs/text-screen.js +10 -3
  160. package/dist/cli/tui/frame.d.ts +7 -10
  161. package/dist/cli/tui/frame.js +19 -10
  162. package/dist/cli/tui/index.d.ts +4 -3
  163. package/dist/cli/tui/index.js +2 -1
  164. package/dist/cli/tui/list/list-screen.d.ts +14 -2
  165. package/dist/cli/tui/list/list-screen.js +68 -32
  166. package/dist/cli/tui/list/list-types.d.ts +8 -4
  167. package/dist/cli/tui/nav.d.ts +29 -0
  168. package/dist/cli/tui/nav.js +67 -0
  169. package/dist/cli/validate.d.ts +1 -1
  170. package/dist/cli/validate.js +3 -3
  171. package/dist/cli/version.d.ts +2 -0
  172. package/dist/cli/version.js +11 -0
  173. package/dist/core/config/defaults.js +5 -1
  174. package/dist/core/config/loader.js +12 -2
  175. package/dist/core/local-config/overrides.js +1 -0
  176. package/dist/core/prompt.d.ts +19 -0
  177. package/dist/core/prompt.js +43 -3
  178. package/dist/core/schema/common.d.ts +6 -0
  179. package/dist/core/schema/common.js +8 -0
  180. package/dist/core/schema/config.d.ts +23 -0
  181. package/dist/core/schema/config.js +10 -1
  182. package/dist/core/schema/harness.d.ts +1 -0
  183. package/dist/core/schema/harness.js +8 -2
  184. package/dist/core/schema/index.d.ts +3 -1
  185. package/dist/core/schema/index.js +3 -1
  186. package/dist/core/schema/profile.d.ts +1 -0
  187. package/dist/core/schema/profile.js +3 -0
  188. package/dist/core/schema/redact.d.ts +13 -0
  189. package/dist/core/schema/redact.js +75 -0
  190. package/dist/core/schema/sandbox.js +1 -0
  191. package/dist/core/schema/vpn.d.ts +17 -0
  192. package/dist/core/schema/vpn.js +22 -0
  193. package/dist/core/schema/web.d.ts +6 -0
  194. package/dist/core/schema/web.js +14 -0
  195. package/dist/core/types.d.ts +26 -0
  196. package/dist/guest/agents/agent-files.d.ts +49 -0
  197. package/dist/guest/agents/agent-files.js +63 -0
  198. package/dist/guest/agents/agent-installer.js +2 -9
  199. package/dist/guest/agents/local-state.d.ts +12 -0
  200. package/dist/guest/agents/local-state.js +198 -0
  201. package/dist/guest/agents/sbx-agent.d.ts +2 -0
  202. package/dist/guest/agents/sbx-agent.js +35 -0
  203. package/dist/guest/boot/boot.d.ts +4 -0
  204. package/dist/guest/boot/boot.js +50 -11
  205. package/dist/guest/daemon/daemon.js +4 -1
  206. package/dist/guest/daemon/exec.js +2 -1
  207. package/dist/guest/daemon/idle-shutdown.js +2 -0
  208. package/dist/guest/daemon/sessions.js +4 -1
  209. package/dist/guest/entries/agent.d.ts +1 -0
  210. package/dist/guest/entries/agent.js +10 -0
  211. package/dist/guest/entries/daemon.js +2 -0
  212. package/dist/guest/runtime/csc.d.ts +1 -0
  213. package/dist/guest/runtime/csc.js +1 -0
  214. package/dist/guest/runtime/secrets.d.ts +12 -0
  215. package/dist/guest/runtime/secrets.js +63 -0
  216. package/dist/guest/setup/claude-profile.d.ts +2 -1
  217. package/dist/guest/setup/claude-profile.js +12 -1
  218. package/dist/guest/setup/environment.d.ts +6 -0
  219. package/dist/guest/setup/environment.js +56 -6
  220. package/dist/guest/setup/path-plan.d.ts +32 -0
  221. package/dist/guest/setup/path-plan.js +67 -0
  222. package/dist/guest/setup/shell-path.d.ts +1 -0
  223. package/dist/guest/setup/shell-path.js +22 -0
  224. package/dist/guest/toolchains/languages/python.js +1 -1
  225. package/dist/guest/toolchains/runtimes.js +2 -2
  226. package/dist/guest/vpn/index.d.ts +1 -0
  227. package/dist/guest/vpn/index.js +1 -0
  228. package/dist/guest/vpn/openvpn.d.ts +9 -0
  229. package/dist/guest/vpn/openvpn.js +47 -0
  230. package/dist/guest/vpn/tunnel.d.ts +17 -0
  231. package/dist/guest/vpn/tunnel.js +78 -0
  232. package/dist/guest/vpn/vpn.d.ts +6 -0
  233. package/dist/guest/vpn/vpn.js +126 -0
  234. package/dist/host/channel/attach.d.ts +3 -1
  235. package/dist/host/channel/attach.js +1 -1
  236. package/dist/host/channel/channel.d.ts +7 -0
  237. package/dist/host/channel/channel.js +26 -2
  238. package/dist/host/health/acl-read.d.ts +17 -0
  239. package/dist/host/health/acl-read.js +45 -0
  240. package/dist/host/health/config-backup.d.ts +11 -0
  241. package/dist/host/health/config-backup.js +73 -0
  242. package/dist/host/health/index.d.ts +6 -0
  243. package/dist/host/health/index.js +7 -0
  244. package/dist/host/health/install-state.d.ts +33 -0
  245. package/dist/host/health/install-state.js +110 -0
  246. package/dist/host/health/path-copies.d.ts +32 -0
  247. package/dist/host/health/path-copies.js +82 -0
  248. package/dist/host/health/stale.d.ts +45 -0
  249. package/dist/host/health/stale.js +120 -0
  250. package/dist/host/health/system.d.ts +37 -0
  251. package/dist/host/health/system.js +87 -0
  252. package/dist/host/install/copies.d.ts +13 -0
  253. package/dist/host/install/copies.js +63 -0
  254. package/dist/host/install/index.d.ts +1 -0
  255. package/dist/host/install/index.js +1 -0
  256. package/dist/host/launch/generate.js +25 -1
  257. package/dist/host/launch/session-file.js +4 -1
  258. package/dist/host/launch/stage.js +1 -1
  259. package/dist/host/migrate/backup.js +3 -0
  260. package/dist/host/migrate/migrations/config-file.d.ts +2 -0
  261. package/dist/host/migrate/migrations/config-file.js +7 -0
  262. package/dist/host/migrate/migrations/follow-machine-storage.js +9 -1
  263. package/dist/host/migrate/migrations/pin-storage-root.d.ts +4 -0
  264. package/dist/host/migrate/migrations/pin-storage-root.js +46 -0
  265. package/dist/host/migrate/migrations/unpin-config-defaults.js +4 -1
  266. package/dist/host/migrate/registry.js +2 -1
  267. package/dist/host/profile/accounts.d.ts +25 -1
  268. package/dist/host/profile/accounts.js +96 -13
  269. package/dist/host/profile/agent-accounts.d.ts +5 -1
  270. package/dist/host/profile/agent-accounts.js +35 -5
  271. package/dist/host/profile/index.d.ts +2 -1
  272. package/dist/host/profile/index.js +1 -0
  273. package/dist/host/profile/plugin-share.d.ts +26 -0
  274. package/dist/host/profile/plugin-share.js +146 -0
  275. package/dist/host/projects/join.d.ts +7 -1
  276. package/dist/host/projects/join.js +21 -4
  277. package/dist/host/projects/registry.d.ts +16 -10
  278. package/dist/host/projects/registry.js +74 -6
  279. package/dist/host/sandbox/platform.d.ts +2 -0
  280. package/dist/host/sandbox/platform.js +21 -2
  281. package/dist/host/secrets/index.d.ts +2 -0
  282. package/dist/host/secrets/index.js +2 -0
  283. package/dist/host/secrets/launch-secrets.d.ts +17 -0
  284. package/dist/host/secrets/launch-secrets.js +93 -0
  285. package/dist/host/secrets/secret-store.d.ts +25 -0
  286. package/dist/host/secrets/secret-store.js +89 -0
  287. package/dist/host/sharing/acl.d.ts +16 -2
  288. package/dist/host/sharing/acl.js +35 -10
  289. package/dist/host/sharing/current.js +11 -2
  290. package/dist/host/sharing/doctor.d.ts +2 -0
  291. package/dist/host/sharing/doctor.js +44 -0
  292. package/dist/host/sharing/group-access.d.ts +6 -0
  293. package/dist/host/sharing/group-access.js +24 -0
  294. package/dist/host/sharing/index.d.ts +2 -1
  295. package/dist/host/sharing/index.js +2 -1
  296. package/dist/host/sharing/lifecycle.d.ts +8 -2
  297. package/dist/host/sharing/lifecycle.js +16 -7
  298. package/dist/host/storage/cleanup.d.ts +2 -0
  299. package/dist/host/storage/cleanup.js +2 -1
  300. package/dist/host/storage/history.d.ts +21 -0
  301. package/dist/host/storage/history.js +49 -0
  302. package/dist/host/storage/index.d.ts +4 -0
  303. package/dist/host/storage/index.js +4 -0
  304. package/dist/host/storage/move.d.ts +95 -0
  305. package/dist/host/storage/move.js +384 -0
  306. package/dist/host/storage/offer.d.ts +11 -0
  307. package/dist/host/storage/offer.js +36 -0
  308. package/dist/host/storage/root.d.ts +18 -0
  309. package/dist/host/storage/root.js +77 -0
  310. package/dist/host/tools/windows-terminal.d.ts +2 -0
  311. package/dist/host/tools/windows-terminal.js +23 -5
  312. package/dist/host/update/index.d.ts +1 -1
  313. package/dist/host/update/index.js +1 -1
  314. package/dist/host/update/notice.d.ts +2 -0
  315. package/dist/host/update/notice.js +13 -1
  316. package/dist/host/update/update-state.d.ts +6 -0
  317. package/dist/host/update/update-state.js +2 -0
  318. package/dist/host/update/updater.d.ts +19 -0
  319. package/dist/host/update/updater.js +88 -12
  320. package/dist/host/vpn/index.d.ts +4 -0
  321. package/dist/host/vpn/index.js +4 -0
  322. package/dist/host/vpn/login.d.ts +24 -0
  323. package/dist/host/vpn/login.js +79 -0
  324. package/dist/host/vpn/nordvpn.d.ts +21 -0
  325. package/dist/host/vpn/nordvpn.js +77 -0
  326. package/dist/host/vpn/prepare.d.ts +17 -0
  327. package/dist/host/vpn/prepare.js +77 -0
  328. package/dist/host/vpn/status.d.ts +12 -0
  329. package/dist/host/vpn/status.js +53 -0
  330. package/dist/host/web/auth.d.ts +38 -0
  331. package/dist/host/web/auth.js +96 -0
  332. package/dist/host/web/backend.d.ts +14 -0
  333. package/dist/host/web/backend.js +1 -0
  334. package/dist/host/web/bridge.d.ts +27 -0
  335. package/dist/host/web/bridge.js +144 -0
  336. package/dist/host/web/browser.d.ts +1 -0
  337. package/dist/host/web/browser.js +10 -0
  338. package/dist/host/web/control.d.ts +17 -0
  339. package/dist/host/web/control.js +67 -0
  340. package/dist/host/web/index.d.ts +9 -0
  341. package/dist/host/web/index.js +8 -0
  342. package/dist/host/web/runner.d.ts +14 -0
  343. package/dist/host/web/runner.js +45 -0
  344. package/dist/host/web/server.d.ts +40 -0
  345. package/dist/host/web/server.js +218 -0
  346. package/dist/host/web/state.d.ts +20 -0
  347. package/dist/host/web/state.js +41 -0
  348. package/dist/host/web/static-files.d.ts +13 -0
  349. package/dist/host/web/static-files.js +57 -0
  350. package/dist/runtime/agent.js +27614 -0
  351. package/dist/runtime/daemon.js +1012 -618
  352. package/dist/runtime/db.js +236 -106
  353. package/dist/runtime/main.js +1093 -250
  354. package/dist/runtime/netproxy.js +102 -3
  355. package/dist/runtime/view.js +102 -3
  356. package/dist/selftest/accounts.test.js +40 -0
  357. package/dist/selftest/agent-paths.test.d.ts +2 -0
  358. package/dist/selftest/agent-paths.test.js +115 -0
  359. package/dist/selftest/channel.test.js +5 -0
  360. package/dist/selftest/cli.test.js +34 -4
  361. package/dist/selftest/credentials.test.d.ts +2 -0
  362. package/dist/selftest/credentials.test.js +198 -0
  363. package/dist/selftest/generate.test.js +1 -1
  364. package/dist/selftest/health.test.d.ts +2 -0
  365. package/dist/selftest/health.test.js +470 -0
  366. package/dist/selftest/host-sim.test.js +2 -0
  367. package/dist/selftest/index.js +19 -0
  368. package/dist/selftest/layout.test.js +2 -0
  369. package/dist/selftest/menu-screens.d.ts +7 -0
  370. package/dist/selftest/menu-screens.js +65 -16
  371. package/dist/selftest/menu.test.js +574 -21
  372. package/dist/selftest/migrate.test.js +1 -1
  373. package/dist/selftest/personalization.test.js +3 -1
  374. package/dist/selftest/projects.test.js +39 -1
  375. package/dist/selftest/shared-use.test.js +64 -2
  376. package/dist/selftest/sharing.test.js +50 -1
  377. package/dist/selftest/storage.test.d.ts +2 -0
  378. package/dist/selftest/storage.test.js +363 -0
  379. package/dist/selftest/support/fixtures.d.ts +14 -0
  380. package/dist/selftest/support/releases.js +1 -1
  381. package/dist/selftest/toolchains.test.js +21 -0
  382. package/dist/selftest/update-service.test.js +87 -3
  383. package/dist/selftest/vpn.test.d.ts +2 -0
  384. package/dist/selftest/vpn.test.js +279 -0
  385. package/dist/selftest/web.test.d.ts +2 -0
  386. package/dist/selftest/web.test.js +304 -0
  387. package/dist/shared/apps/catalogue.js +31 -0
  388. package/dist/shared/channel/index.d.ts +1 -0
  389. package/dist/shared/channel/index.js +1 -0
  390. package/dist/shared/channel/run-dir.d.ts +6 -1
  391. package/dist/shared/channel/run-dir.js +12 -1
  392. package/dist/shared/channel/schema.d.ts +19 -0
  393. package/dist/shared/channel/schema.js +11 -0
  394. package/dist/shared/channel/secrets.d.ts +28 -0
  395. package/dist/shared/channel/secrets.js +44 -0
  396. package/dist/shared/vpn/download.d.ts +13 -0
  397. package/dist/shared/vpn/download.js +14 -0
  398. package/dist/shared/vpn/kill-switch.d.ts +18 -0
  399. package/dist/shared/vpn/kill-switch.js +81 -0
  400. package/dist/shared/vpn/ovpn.d.ts +23 -0
  401. package/dist/shared/vpn/ovpn.js +244 -0
  402. package/dist/shared/web/index.d.ts +1 -0
  403. package/dist/shared/web/index.js +1 -0
  404. package/dist/shared/web/protocol.d.ts +61 -0
  405. package/dist/shared/web/protocol.js +35 -0
  406. package/dist/sync/sync-cycle.js +5 -1
  407. package/dist/sync/sync-state.d.ts +1 -0
  408. package/dist/sync/sync-state.js +1 -0
  409. package/dist/sync/watcher.js +10 -0
  410. package/dist/web/index.html +13 -0
  411. package/dist/web/main-CXESL347.js +45 -0
  412. package/dist/web/main-VLR7DVQ4.css +1 -0
  413. package/native/csharp/FolderSecurity.cs +29 -0
  414. package/native/scripts/protect-data.ps1 +29 -0
  415. package/native/scripts/read-folder-sddl.ps1 +10 -0
  416. package/native/scripts/set-folder-access.ps1 +49 -7
  417. package/node_modules/ws/LICENSE +20 -0
  418. package/node_modules/ws/README.md +548 -0
  419. package/node_modules/ws/browser.js +8 -0
  420. package/node_modules/ws/index.js +22 -0
  421. package/node_modules/ws/lib/buffer-util.js +131 -0
  422. package/node_modules/ws/lib/constants.js +19 -0
  423. package/node_modules/ws/lib/event-target.js +292 -0
  424. package/node_modules/ws/lib/extension.js +203 -0
  425. package/node_modules/ws/lib/limiter.js +55 -0
  426. package/node_modules/ws/lib/permessage-deflate.js +530 -0
  427. package/node_modules/ws/lib/receiver.js +743 -0
  428. package/node_modules/ws/lib/sender.js +607 -0
  429. package/node_modules/ws/lib/stream.js +161 -0
  430. package/node_modules/ws/lib/subprotocol.js +62 -0
  431. package/node_modules/ws/lib/validation.js +152 -0
  432. package/node_modules/ws/lib/websocket-server.js +562 -0
  433. package/node_modules/ws/lib/websocket.js +1421 -0
  434. package/node_modules/ws/package.json +74 -0
  435. package/node_modules/ws/wrapper.mjs +21 -0
  436. package/package.json +15 -5
  437. package/sandbox/csharp/EnvBroadcast.cs +33 -0
  438. package/sandbox/csharp/PathSync.cs +90 -0
  439. package/sandbox/scripts/install-shell-path.ps1 +73 -0
  440. package/sandbox/scripts/set-machine-environment.ps1 +37 -4
  441. package/schemas/config.schema.json +79 -4
  442. package/schemas/sandbox-config.schema.json +14 -1
  443. package/dist/cli/commands/repair/checks.d.ts +0 -2
  444. package/dist/cli/commands/repair/checks.js +0 -246
  445. package/dist/cli/menu/pages/start-page.d.ts +0 -43
  446. package/dist/cli/menu/pages/start-page.js +0 -192
  447. package/dist/cli/tui/tab-bar.d.ts +0 -9
  448. package/dist/cli/tui/tab-bar.js +0 -33
@@ -0,0 +1,171 @@
1
+ // `sandbox storage`: where everything kept between sandboxes lives, moving it to another folder (with the size, an
2
+ // estimate and a warning first) and switching to a folder without moving. The work is in host/storage.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { userConfigPath, userDir } from '../../../core/config/index.js';
6
+ import { diskUsage, humanBytes } from '../../../core/disk.js';
7
+ import { askChoice, askName, askRequired } from '../../../core/prompt.js';
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';
10
+ import { isSandboxRunning } from '../../../host/sandbox/index.js';
11
+ import { protectPrivateFolder } from '../../../host/sharing/index.js';
12
+ import { watcherRunning } from '../../../sync/index.js';
13
+ import { printJson, working } from '../../output/index.js';
14
+ import { requireOnPc } from '../context.js';
15
+ /** Why a move cannot start (a background sync is still on a data folder in the old storage), or null. */
16
+ export function storageBusy(root) {
17
+ const data = path.join(root, 'data');
18
+ try {
19
+ for (const name of fs.readdirSync(data))
20
+ if (watcherRunning(path.join(data, name)))
21
+ return 'The background sync of "' + name + '" is still finishing.';
22
+ }
23
+ catch {
24
+ // no data folder: nothing runs
25
+ }
26
+ return null;
27
+ }
28
+ function mover(ctx) {
29
+ return new StorageMove(ctx.loaded.configPath ?? userConfigPath(), () => storageBusy(ctx.storageRoot), isSandboxRunning, protectPrivateFolder);
30
+ }
31
+ /** The path as typed (variables and ~ expand, relative to the current folder). */
32
+ function typedPath(value) {
33
+ return expandPath(value, process.cwd());
34
+ }
35
+ /** The folder typed, or picked from the default folder, folders that hold data and the drives (or typed in). */
36
+ async function folderOf(ctx, value, what) {
37
+ if (value)
38
+ return typedPath(value);
39
+ const fix = 'Use: sandbox storage ' + what + ' <folder>';
40
+ const typed = 'type';
41
+ const known = [sharedDefaultRoot(), ...otherDataFolders(ctx.storageRoot, new StorageHistory(userDir()))];
42
+ const drives = diskUsage(ctx.storageRoot)
43
+ .filter((d) => !d.holdsStorage)
44
+ .map((d) => path.join(d.drive, 'AgentSandbox'));
45
+ const choices = [...new Set([...known, ...drives])]
46
+ .filter((f) => f.toLowerCase() !== ctx.storageRoot.toLowerCase())
47
+ .map((f) => ({ name: f, value: f }));
48
+ const picked = await askChoice('Which folder?', [...choices, { name: 'Type a path...', value: typed }], fix);
49
+ if (picked !== typed)
50
+ return picked;
51
+ const answer = await askName('Folder:', fix, (v) => v.length > 0 || 'Type a folder path.');
52
+ return typedPath(answer);
53
+ }
54
+ async function show(ctx) {
55
+ const history = new StorageHistory(userDir());
56
+ const measured = await working('Measuring your saved data (big folders take a moment)', () => measureFolder(ctx.storageRoot), !ctx.options.json);
57
+ const others = otherDataFolders(ctx.storageRoot, history);
58
+ const shared = sharedDefaultRoot();
59
+ if (printJson(ctx, {
60
+ root: ctx.storageRoot,
61
+ exists: fs.existsSync(ctx.storageRoot),
62
+ ...measured,
63
+ defaultRoot: shared,
64
+ otherFoldersWithData: others,
65
+ }))
66
+ return 0;
67
+ const inUse = shared.toLowerCase() === ctx.storageRoot.toLowerCase() ? ' (in use)' : '';
68
+ console.log('Your saved data is in: ' + ctx.storageRoot);
69
+ console.log(' ' + (holdsData(ctx.storageRoot) ? describeSize(measured) : 'nothing in it yet'));
70
+ console.log('The default folder is: ' + shared + inUse);
71
+ for (const other of others)
72
+ console.log('Another folder with data: ' + other);
73
+ console.log('\nMove it with: sandbox storage move <folder> (or switch without moving: sandbox storage use <folder>)');
74
+ console.log('What takes the space: sandbox data');
75
+ return 0;
76
+ }
77
+ function printPlan(from, to, measured, copyOnly) {
78
+ const est = estimate(from, to, measured, copyOnly);
79
+ const time = est.sameVolume
80
+ ? 'a few seconds (same drive, nothing is copied)'
81
+ : describeDuration(est.seconds) + ' (copied to another drive)';
82
+ console.log('Move from: ' + from);
83
+ console.log(' to: ' + to);
84
+ console.log(' size: ' + describeSize(measured));
85
+ console.log(' time: ' + time);
86
+ const warning = moveWarning(measured, est);
87
+ if (warning)
88
+ console.log('Warning: ' + warning);
89
+ if (!est.fits)
90
+ fail('Not enough free space on the target drive.', 'Free some space or pick another drive.');
91
+ }
92
+ /** Moves the data with progress on the terminal; remembers both folders. Used by the command and the start offer. */
93
+ export async function performMove(ctx, to, keepOld) {
94
+ const from = ctx.storageRoot;
95
+ const history = new StorageHistory(userDir());
96
+ let line = 'Moving your data';
97
+ const result = await working(() => line, () => mover(ctx).move(from, to, {
98
+ keepOld,
99
+ onProgress: (p) => {
100
+ line = 'Copying: ' + p.files.toLocaleString('en-US') + ' files, ' + humanBytes(p.bytes);
101
+ },
102
+ }));
103
+ history.remember(from);
104
+ history.remember(to);
105
+ return result;
106
+ }
107
+ async function move(ctx) {
108
+ const to = await folderOf(ctx, ctx.options.positionals[1], 'move');
109
+ const from = ctx.storageRoot;
110
+ if (!fs.existsSync(from))
111
+ fail('There is nothing to move: ' + from + ' does not exist.', 'Use sandbox storage use ' + to + ' to switch.');
112
+ mover(ctx).validate(from, to);
113
+ const measured = await working('Measuring your saved data', () => measureFolder(from), !ctx.options.json);
114
+ const keepOld = ctx.options.keepOld;
115
+ printPlan(from, to, measured, keepOld);
116
+ if (!ctx.options.yes) {
117
+ if (!(await askRequired('Move your data to ' + to + '?', 'Add --yes to move without asking.', true))) {
118
+ console.log('Nothing was moved.');
119
+ return 1;
120
+ }
121
+ }
122
+ const result = await performMove(ctx, to, keepOld);
123
+ if (printJson(ctx, result))
124
+ return 0;
125
+ const left = result.removedOld ? '' : '; the old folder is still at ' + result.from;
126
+ console.log('Moved ' + result.files.toLocaleString('en-US') + ' files. Your data is now in ' + result.to + left + '.');
127
+ for (const note of result.notes)
128
+ console.log(note);
129
+ console.log('Your settings now point to ' + result.pinned + ', so older versions find the data too.');
130
+ return 0;
131
+ }
132
+ async function use(ctx) {
133
+ const to = await folderOf(ctx, ctx.options.positionals[1], 'use');
134
+ if (to.toLowerCase() === ctx.storageRoot.toLowerCase()) {
135
+ console.log('That folder is in use already.');
136
+ return 0;
137
+ }
138
+ const busy = storageBusy(ctx.storageRoot);
139
+ if (busy)
140
+ fail(busy, 'Wait a minute, then try again.');
141
+ const state = holdsData(to)
142
+ ? to + ' is used as it is.'
143
+ : to + ' starts empty: tools, logins and history are set up again.';
144
+ console.log('Warning: your data stays in ' + ctx.storageRoot + '. ' + state);
145
+ if (!ctx.options.yes) {
146
+ if (!(await askRequired('Use ' + to + ' from now on, without moving your data?', 'Add --yes to switch without asking.'))) {
147
+ console.log('Nothing was changed.');
148
+ return 1;
149
+ }
150
+ }
151
+ new StorageHistory(userDir()).remember(ctx.storageRoot);
152
+ mover(ctx).pin(to);
153
+ console.log('Your data is now looked for in ' + to + '. Move it later with: sandbox storage move <folder>');
154
+ return 0;
155
+ }
156
+ /** `sandbox storage [show|move <folder>|use <folder>]`. */
157
+ export function storage(ctx) {
158
+ requireOnPc('storage');
159
+ const [action = 'show'] = ctx.options.positionals;
160
+ switch (action) {
161
+ case 'show':
162
+ case 'info':
163
+ return show(ctx);
164
+ case 'move':
165
+ return move(ctx);
166
+ case 'use':
167
+ return use(ctx);
168
+ default:
169
+ return fail('Unknown storage action "' + action + '".', 'Use: sandbox storage, sandbox storage move <folder> or sandbox storage use <folder>');
170
+ }
171
+ }
@@ -21,7 +21,7 @@ export function setSetting(ctx, key, value) {
21
21
  const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
22
22
  const result = editConfigText(text, key, value);
23
23
  if ('problems' in result)
24
- fail('Could not change ' + key.join('.') + ':\n ' + result.problems.join('\n '));
24
+ fail('Could not change ' + key.join('.') + ':\n ' + result.problems.join('\n '), 'Nothing was written. Fix the value, or the settings file: ' + file);
25
25
  fs.mkdirSync(path.dirname(file), { recursive: true });
26
26
  fs.writeFileSync(file, result.text, 'utf8');
27
27
  }
@@ -0,0 +1,4 @@
1
+ import { type Context } from './context.js';
2
+ /** The warnings every VPN user must see. */
3
+ export declare const VPN_WARNINGS: readonly string[];
4
+ export declare function vpn(ctx: Context): Promise<number> | number;
@@ -0,0 +1,75 @@
1
+ // `sandbox vpn [status|login|logout]`: the VPN login (kept encrypted on this PC, never printed) and what the VPN is
2
+ // doing.
3
+ import { askSecret } from '../../core/prompt.js';
4
+ import { fail } from '../../core/util/index.js';
5
+ import { c, nextStep, printJson, table } from '../output/index.js';
6
+ import { NORD_STEPS, VpnService } from '../services/vpn-service.js';
7
+ import { requireOnPc } from './context.js';
8
+ const LOGIN_TEXT = {
9
+ none: c.yellow('not saved'),
10
+ saved: c.green('saved (encrypted)'),
11
+ unreadable: c.red('cannot be read'),
12
+ };
13
+ /** The warnings every VPN user must see. */
14
+ export const VPN_WARNINGS = [
15
+ '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
+ 'Free VPNs see all your traffic: use one you trust.',
17
+ ];
18
+ async function status(ctx) {
19
+ const report = await new VpnService(ctx).report();
20
+ if (printJson(ctx, report))
21
+ return 0;
22
+ table([
23
+ ['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
+ ['Protocol', report.protocol],
26
+ ['Country', report.country || 'best available'],
27
+ ['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)) : '')],
29
+ ...(report.running ? [['Running now', report.running.text]] : []),
30
+ ]);
31
+ if (report.login === 'none')
32
+ nextStep('sandbox vpn login');
33
+ if (report.login === 'unreadable')
34
+ console.log(c.dim('Only the Windows user that saved it can read it. Save it again: sandbox vpn login'));
35
+ for (const w of VPN_WARNINGS)
36
+ console.log(c.dim(w));
37
+ return 0;
38
+ }
39
+ async function login(ctx) {
40
+ requireOnPc('vpn login');
41
+ const service = new VpnService(ctx);
42
+ const nord = service.provider === 'nordvpn';
43
+ console.log(nord
44
+ ? 'NordVPN needs its service credentials, not your usual login. To get them:'
45
+ : 'Type the user name and password your VPN profile asks for.');
46
+ if (nord)
47
+ NORD_STEPS.forEach((s, i) => console.log(' ' + (i + 1) + '. ' + s));
48
+ console.log(c.dim('What you type is not shown. It is kept encrypted for your Windows user and never printed.\n'));
49
+ const fix = 'Run sandbox vpn login in a terminal.';
50
+ const username = await askSecret(nord ? 'Service user name' : 'User name', fix);
51
+ const password = await askSecret(nord ? 'Service password' : 'Password', fix);
52
+ service.save({ username, password });
53
+ console.log(c.green('Saved.') + ' Turn the VPN on with: sandbox config set vpn.enabled true');
54
+ for (const w of VPN_WARNINGS)
55
+ console.log(c.dim(w));
56
+ return 0;
57
+ }
58
+ function logout(ctx) {
59
+ requireOnPc('vpn logout');
60
+ console.log(new VpnService(ctx).remove() ? 'The saved VPN login is deleted.' : 'No VPN login was saved.');
61
+ return 0;
62
+ }
63
+ export function vpn(ctx) {
64
+ const action = ctx.options.positionals[0] ?? 'status';
65
+ switch (action) {
66
+ case 'status':
67
+ return status(ctx);
68
+ case 'login':
69
+ return login(ctx);
70
+ case 'logout':
71
+ return logout(ctx);
72
+ default:
73
+ return fail('Unknown VPN action "' + action + '".', 'Use: sandbox vpn status | login | logout');
74
+ }
75
+ }
@@ -0,0 +1,4 @@
1
+ import { type Context } from './context.js';
2
+ export declare function web(ctx: Context): Promise<number>;
3
+ /** Body of the background process (`sandbox _web`): runs until stopped. */
4
+ export declare function webServer(ctx: Context): Promise<void>;
@@ -0,0 +1,95 @@
1
+ // `sandbox web start | stop | status | open`: the web UI's server runs in the background (as `_web`, like the sync
2
+ // watcher) and keeps running when no sandbox does. The work is in host/web.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { fail, sleep } from '../../core/util/index.js';
6
+ import { spawnDetached } from '../../host/sandbox/index.js';
7
+ import { openBrowser, WebControl, WebRunner, WebStore } from '../../host/web/index.js';
8
+ import { printJson } from '../output/index.js';
9
+ import { WebService } from '../services/index.js';
10
+ import { requireOnPc } from './context.js';
11
+ const START_WAIT_MS = 15000;
12
+ function controlOf(ctx) {
13
+ return new WebControl(new WebStore(ctx.storageRoot));
14
+ }
15
+ /** Starts the background server (when it is not running) and waits until it answers. */
16
+ async function startServer(ctx, control) {
17
+ const before = await control.status();
18
+ if (before.running && before.state) {
19
+ console.log('The web UI is already running on port ' + before.state.port + '.');
20
+ return before.state.port;
21
+ }
22
+ if (before.state)
23
+ control.clear();
24
+ const { config } = ctx.loaded;
25
+ if (!config.web.enabled)
26
+ console.log('web.enabled is off: this starts the web UI for now only. Set it to true in Settings to keep it allowed.');
27
+ const configFile = ctx.options.config;
28
+ spawnDetached(ctx.entry, ['_web', ...(configFile ? ['--config', path.resolve(configFile)] : [])]);
29
+ for (const started = Date.now(); Date.now() - started < START_WAIT_MS; await sleep(300)) {
30
+ const now = await control.status();
31
+ if (now.running && now.state) {
32
+ console.log('The web UI is running at http://127.0.0.1:' + now.state.port + '/ (this PC only).');
33
+ console.log('Open it already signed in with: sandbox web open');
34
+ return now.state.port;
35
+ }
36
+ }
37
+ 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
+ export async function web(ctx) {
40
+ requireOnPc('web');
41
+ const action = ctx.options.positionals[0] ?? 'status';
42
+ const control = controlOf(ctx);
43
+ switch (action) {
44
+ case 'start':
45
+ await startServer(ctx, control);
46
+ return 0;
47
+ case 'open': {
48
+ await startServer(ctx, control);
49
+ const { state } = await control.status();
50
+ if (!state)
51
+ return fail('The web UI stopped right away.', 'Run sandbox web start again.');
52
+ openBrowser(control.url(state));
53
+ console.log('Opened in your browser.');
54
+ return 0;
55
+ }
56
+ case 'stop': {
57
+ const { running, state } = await control.status();
58
+ if (!state) {
59
+ console.log('The web UI is not running.');
60
+ return 0;
61
+ }
62
+ if (!running || (await control.stop(state)))
63
+ control.clear();
64
+ else
65
+ return fail('The web UI did not stop.', 'End the process ' + state.pid + ' in Task Manager.');
66
+ console.log('The web UI stopped.');
67
+ return 0;
68
+ }
69
+ case 'status': {
70
+ const { running, state } = await control.status();
71
+ const port = running && state ? state.port : null;
72
+ if (printJson(ctx, { running, port, address: port ? 'http://127.0.0.1:' + port + '/' : null }))
73
+ return 0;
74
+ console.log(running && port
75
+ ? 'The web UI is running at http://127.0.0.1:' + port + '/ (sandbox web open signs you in).'
76
+ : 'The web UI is not running (sandbox web start).');
77
+ return 0;
78
+ }
79
+ default:
80
+ return fail('Unknown action "' + action + '".', 'Use start, stop, status or open.');
81
+ }
82
+ }
83
+ /** Body of the background process (`sandbox _web`): runs until stopped. */
84
+ export async function webServer(ctx) {
85
+ const staticRoot = path.join(ctx.packageRoot, 'dist', 'web');
86
+ if (!fs.existsSync(path.join(staticRoot, 'index.html')))
87
+ fail('The web app is not built (' + staticRoot + ').', 'Run npm run build in the install folder.');
88
+ await new WebRunner({
89
+ storageRoot: ctx.storageRoot,
90
+ port: ctx.loaded.config.web.port,
91
+ backend: new WebService(ctx),
92
+ staticRoot,
93
+ }).run();
94
+ process.exit(0);
95
+ }
@@ -7,7 +7,7 @@ const HistorySchema = z.array(z.strictObject({ argv: z.array(z.string()).max(200
7
7
  function historyFile() {
8
8
  return new JsonFile(HistorySchema, path.join(userDir(), 'history.json'));
9
9
  }
10
- const HIDDEN = new Set(['menu', '_watch', 'self-test']);
10
+ const HIDDEN = new Set(['menu', '_watch', '_web', 'self-test']);
11
11
  export function readHistory() {
12
12
  return historyFile().read() ?? [];
13
13
  }
package/dist/cli/index.js CHANGED
@@ -1,18 +1,39 @@
1
1
  import path from 'node:path';
2
2
  import { buildProgram, parseCli } from './args.js';
3
- import { commandSpec } from './manifest.js';
3
+ import { COMMANDS, commandSpec } from './manifest.js';
4
4
  import { recordHistory } from './history.js';
5
5
  import { menu } from './menu/index.js';
6
- import { validateOptions } from './validate.js';
6
+ import { closest, validateOptions } from './validate.js';
7
7
  import { loadConfig } from '../core/config/index.js';
8
8
  import * as commands from './commands/index.js';
9
9
  import { fail } from '../core/util/index.js';
10
- import { PACKAGE_ROOT } from '../core/package-info.js';
10
+ import { PACKAGE_ROOT, packageVersion } from '../core/package-info.js';
11
+ import { takeHiddenInstallNotice } from '../host/install/index.js';
12
+ import { c } from './output/index.js';
11
13
  import { dropInheritedModulePath } from '../shared/system.js';
12
14
  import { withConfig, hoistConfig, withoutConfig } from './argv.js';
13
15
  import { report } from './report.js';
16
+ import { versionText } from './version.js';
14
17
  import { HANDLERS } from './commands/index.js';
15
18
  const entry = path.join(PACKAGE_ROOT, 'dist', 'bin', 'agent-sandbox.js');
19
+ /** A first word that is no command and no agent, but close to one: "Did you mean: sandbox status?". */
20
+ function failOnTypo(word, agents) {
21
+ const names = [...COMMANDS.filter((c) => !c.hidden).map((c) => c.name), ...agents];
22
+ const hint = closest(word, names);
23
+ if (hint)
24
+ fail('Unknown command "' + word + '". Did you mean: sandbox ' + hint + '?', 'Run sandbox --help for the list of commands, or sandbox for the menu.');
25
+ }
26
+ /** One yellow line, once per version, when this is not the managed install but one exists (it is hidden). */
27
+ function hiddenInstallNotice(storageRoot) {
28
+ try {
29
+ const line = takeHiddenInstallNotice(storageRoot, PACKAGE_ROOT, packageVersion());
30
+ if (line)
31
+ console.log(c.yellow(line));
32
+ }
33
+ catch {
34
+ // A note that cannot be saved is not worth stopping a start for.
35
+ }
36
+ }
16
37
  /** Runs an argv from the menu exactly as if it had been typed, reporting errors instead of throwing. */
17
38
  const runFromMenu = (config) => (argv) => main(withConfig(argv, config)).catch((e) => report(e));
18
39
  /**
@@ -33,10 +54,27 @@ export async function main(rawArgv, preset = {}) {
33
54
  }
34
55
  };
35
56
  if (!preset.command && !preset.harness) {
57
+ const version = versionText(withoutConfig(argv), PACKAGE_ROOT);
58
+ if (version !== null) {
59
+ console.log(version);
60
+ return 0;
61
+ }
36
62
  if (argv.length === 0)
37
63
  return process.stdin.isTTY ? main(['menu']) : (buildProgram(() => undefined).outputHelp(), 0);
38
64
  if (isAgent(argv[0]))
39
65
  return main(argv.slice(1), { harness: argv[0] });
66
+ const first = argv.slice(0, 1)[0];
67
+ if (first && !first.startsWith('-') && !commandSpec(first) && first !== 'help') {
68
+ const at = argv.indexOf('--config');
69
+ let agents = [];
70
+ try {
71
+ agents = Object.keys(loadConfig(at >= 0 ? argv[at + 1] : undefined).config.harnesses);
72
+ }
73
+ catch {
74
+ // A broken settings file: the commands alone are suggested.
75
+ }
76
+ failOnTypo(first, agents);
77
+ }
40
78
  }
41
79
  const options = parseCli(preset.command ? [preset.command, ...argv] : argv, Boolean(preset.harness));
42
80
  if (preset.harness)
@@ -48,8 +86,10 @@ export async function main(rawArgv, preset = {}) {
48
86
  : argv;
49
87
  if (options.command === 'menu') {
50
88
  const menuCtx = commands.createContext(PACKAGE_ROOT, entry, options);
89
+ hiddenInstallNotice(menuCtx.storageRoot);
51
90
  await commands.migrateBeforeCommand(menuCtx, options.command);
52
91
  commands.updatesBeforeCommand(entry, options.command);
92
+ await commands.storageBeforeCommand(menuCtx, options.command);
53
93
  return menu(menuCtx, runFromMenu(options.config));
54
94
  }
55
95
  // Loaded only when asked for: normal commands never load test code.
@@ -68,8 +108,11 @@ export async function main(rawArgv, preset = {}) {
68
108
  return fail('Unknown command "' + options.command + '".', 'Run sandbox --help for the list of commands, or sandbox for the menu.');
69
109
  const ctx = commands.createContext(PACKAGE_ROOT, entry, options);
70
110
  validateOptions(ctx);
111
+ if (options.command === 'launch')
112
+ hiddenInstallNotice(ctx.storageRoot);
71
113
  await commands.migrateBeforeCommand(ctx, options.command);
72
114
  commands.updatesBeforeCommand(entry, options.command);
115
+ await commands.storageBeforeCommand(ctx, options.command);
73
116
  // Only what parsed and validated is offered again by the menu, without the --config (it is the same every time).
74
117
  recordHistory(withoutConfig(typed));
75
118
  return (await handler(ctx)) ?? 0;