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
@@ -1,5 +1,6 @@
1
- // Tab 3: the sandbox as a whole. Status (a card, the window, stopping it), Projects (roots and known projects) and
1
+ // Section 3: the sandbox as a whole. Status (a card, the window, stopping it), Projects (roots and known projects) and
2
2
  // Databases (start and stop), each a category on the left.
3
+ import fs from 'node:fs';
3
4
  import path from 'node:path';
4
5
  import { errMsg, safeText } from '../../../core/util/index.js';
5
6
  import { RunDir } from '../../../shared/channel/index.js';
@@ -29,9 +30,9 @@ export function sandboxOptions(m) {
29
30
  section: 'Sandbox',
30
31
  ...extra,
31
32
  });
32
- const off = running ? {} : { disabled: 'No sandbox is running.' };
33
33
  const sessions = m.report.projects.reduce((n, p) => n + p.sessions.length, 0) + m.report.otherSessions.length;
34
34
  const status = [
35
+ ...(running ? [] : startRows(m.defaultAgent, 'status')),
35
36
  row('info:state', 'State', running ? Theme.ok(Theme.glyph.on + ' ' + sandbox.phase) : Theme.muted(Theme.glyph.off + ' stopped'), { description: sandbox.message ? safeText(sandbox.message) : undefined }),
36
37
  row('info:uptime', 'Up for', sandbox.startedAt && running ? ago(sandbox.startedAt).replace(' ago', '') : '-'),
37
38
  row('info:daemon', 'Sandbox agent', running ? safeText(sandbox.daemon) : '-'),
@@ -44,29 +45,37 @@ export function sandboxOptions(m) {
44
45
  (repoGuard.findings.length ? ', ' + repoGuard.findings.length + ' change(s) neutralised' : ''), {
45
46
  details: repoGuard.findings.slice(-8).map((f) => Theme.warn(safeText(f))),
46
47
  }),
47
- row('act:show', 'Show the window', '', {
48
- section: 'Actions',
49
- description: 'Brings the sandbox window to the front.',
50
- ...off,
51
- }),
52
- row('act:hide', 'Hide the window', '', {
53
- section: 'Actions',
54
- description: 'Hides the window; sbx-show inside the sandbox (or Show the window here) brings it back.',
55
- ...off,
56
- }),
57
- row('act:stop', 'Stop the sandbox…', '', {
58
- section: 'Actions',
59
- description: 'Shuts the sandbox down; history and claude-mem sync back when it closes.',
60
- ...off,
61
- }),
48
+ ...(running
49
+ ? [
50
+ row('act:show', 'Show the window', '', {
51
+ section: 'Actions',
52
+ description: 'Brings the sandbox window to the front.',
53
+ }),
54
+ row('act:hide', 'Hide the window', '', {
55
+ section: 'Actions',
56
+ description: 'Hides the window. Show the window brings it back.',
57
+ }),
58
+ row('act:stop', 'Stop the sandbox…', '', {
59
+ section: 'Actions',
60
+ description: 'Shuts the sandbox down; your history syncs back when it closes.',
61
+ }),
62
+ ]
63
+ : []),
62
64
  ];
65
+ const addRoot = {
66
+ value: ADD_ROOT,
67
+ label: '+ Add a project root…',
68
+ category: 'projects',
69
+ section: 'Project roots',
70
+ description: 'Pick a folder that holds your projects: every project in it (also new ones) opens without a restart.',
71
+ };
63
72
  const roots = m.projects.roots.map((r) => ({
64
73
  value: 'root:' + r,
65
74
  label: Text.middle(r, 60),
66
75
  hint: Theme.muted('root'),
67
76
  category: 'projects',
68
77
  section: 'Project roots',
69
- description: 'Every project in this folder (also new ones) opens without a restart.',
78
+ description: 'Every project in this folder (also new ones) opens without a restart. f removes the root.',
70
79
  }));
71
80
  const projects = m.projects.projects.map((p) => ({
72
81
  value: 'project:' + p.folder,
@@ -80,6 +89,20 @@ export function sandboxOptions(m) {
80
89
  Theme.muted('In sandbox ') + (p.running ?? '-'),
81
90
  ],
82
91
  }));
92
+ const noDatabases = m.databases
93
+ ? []
94
+ : running
95
+ ? [
96
+ {
97
+ value: 'info:db',
98
+ label: 'The sandbox is not ready yet',
99
+ category: 'databases',
100
+ section: 'Databases',
101
+ description: 'Its databases are listed once it is ready. l looks again.',
102
+ disabled: 'Not ready yet: l looks again.',
103
+ },
104
+ ]
105
+ : startRows(m.defaultAgent, 'databases');
83
106
  const databases = (m.databases ?? []).map((d) => ({
84
107
  value: 'db:' + d.name,
85
108
  label: d.name,
@@ -90,27 +113,69 @@ export function sandboxOptions(m) {
90
113
  }));
91
114
  return {
92
115
  path: [],
93
- items: [...status, ...roots, ...projects, ...databases],
116
+ items: [...status, addRoot, ...roots, ...projects, ...databases, ...noDatabases],
94
117
  categories: CATEGORIES,
95
- empty: running ? 'Nothing here yet.' : 'No sandbox is running.',
96
- actions: sandboxActions,
97
- enterLabel: 'run',
98
- escapeLabel: 'quit',
118
+ empty: 'Nothing here yet.',
119
+ actions: (category) => sandboxActions(category, running),
120
+ enterLabel: (_category, id) => enterLabel(id ?? '', m),
99
121
  };
100
122
  }
101
- function sandboxActions(category) {
123
+ const ADD_ROOT = '@add-root';
124
+ const REFRESH = { key: 'l', label: 'refresh', importance: 3 };
125
+ /** The rows that start a sandbox, shown where the sandbox has to run for anything to be there. */
126
+ function startRows(agent, category) {
127
+ return [
128
+ {
129
+ value: 'act:start',
130
+ label: 'Start the sandbox with ' + agent,
131
+ hint: Theme.muted('default agent'),
132
+ category,
133
+ section: 'No sandbox is running',
134
+ description: 'Starts the sandbox and ' + agent + ' in this folder.',
135
+ },
136
+ {
137
+ value: 'act:start-bare',
138
+ label: 'Start the sandbox without an agent',
139
+ category,
140
+ section: 'No sandbox is running',
141
+ description: 'Tools and databases only.',
142
+ },
143
+ ];
144
+ }
145
+ /** What Enter does on a row ("" = the row only shows information). */
146
+ function enterLabel(id, m) {
147
+ if (id === 'act:show')
148
+ return 'show';
149
+ if (id === 'act:hide')
150
+ return 'hide';
151
+ if (id === 'act:stop')
152
+ return 'stop';
153
+ if (id.startsWith('act:start'))
154
+ return 'start';
155
+ if (id === ADD_ROOT)
156
+ return 'choose folder';
157
+ if (id.startsWith('db:'))
158
+ return m.databases?.find((d) => 'db:' + d.name === id)?.running ? 'stop' : 'start';
159
+ return '';
160
+ }
161
+ function sandboxActions(category, running) {
102
162
  if (category === 'projects')
103
163
  return [
104
- { key: 'a', label: 'add root' },
105
- { key: 'f', label: 'forget / remove' },
106
- ];
107
- return category === 'databases'
108
- ? []
109
- : [
110
- { key: 'w', label: 'show window' },
111
- { key: 'h', label: 'hide window' },
112
- { key: 'x', label: 'stop' },
164
+ { key: 'a', label: 'add root', importance: 1 },
165
+ { key: 'f', label: 'remove root', importance: 1, applies: (id) => id.startsWith('root:') },
166
+ { key: 'f', label: 'forget project', importance: 1, applies: (id) => id.startsWith('project:') },
167
+ REFRESH,
113
168
  ];
169
+ if (category === 'databases')
170
+ return [REFRESH];
171
+ return running
172
+ ? [
173
+ { key: 'w', label: 'show window', importance: 3 },
174
+ { key: 'h', label: 'hide window', importance: 3 },
175
+ { key: 'x', label: 'stop', importance: 1 },
176
+ REFRESH,
177
+ ]
178
+ : [REFRESH];
114
179
  }
115
180
  export class SandboxPage extends ListPage {
116
181
  ctx;
@@ -136,7 +201,7 @@ export class SandboxPage extends ListPage {
136
201
  /* the sandbox did not answer: the list stays empty */
137
202
  }
138
203
  }
139
- this.model = { report, projects: this.projects.list(), databases };
204
+ this.model = { report, projects: this.projects.list(), databases, defaultAgent: this.defaultAgent() };
140
205
  }
141
206
  /** The folder `sbx-db` runs in: the sandbox's own workspace. */
142
207
  guestWorkspace() {
@@ -151,6 +216,14 @@ export class SandboxPage extends ListPage {
151
216
  const id = result.kind === 'pick' || result.kind === 'action' ? result.value : undefined;
152
217
  const key = result.kind === 'action' ? result.key : undefined;
153
218
  try {
219
+ if (key === 'l')
220
+ return { reload: true };
221
+ if (id === 'act:start' && !key)
222
+ return { exit: { kind: 'run', argv: ['launch', '--harness', this.defaultAgent()] } };
223
+ if (id === 'act:start-bare' && !key)
224
+ return { exit: { kind: 'run', argv: ['start'] } };
225
+ if (id === ADD_ROOT && !key)
226
+ return await this.addRoot();
154
227
  if (id === 'act:show' || key === 'w')
155
228
  return this.show();
156
229
  if (id === 'act:hide' || key === 'h')
@@ -160,7 +233,7 @@ export class SandboxPage extends ListPage {
160
233
  if (key === 'a')
161
234
  return await this.addRoot();
162
235
  if (key === 'f')
163
- return this.forget(id);
236
+ return await this.forget(id);
164
237
  if (id?.startsWith('db:') && result.kind === 'pick')
165
238
  return await this.toggleDatabase(id.slice(3));
166
239
  }
@@ -169,6 +242,9 @@ export class SandboxPage extends ListPage {
169
242
  }
170
243
  return {};
171
244
  }
245
+ defaultAgent() {
246
+ return this.ctx.loaded.config.defaultHarness;
247
+ }
172
248
  noSandbox() {
173
249
  return this.model?.report.sandbox.running ? null : { notice: Theme.warn('No sandbox is running.') };
174
250
  }
@@ -186,7 +262,7 @@ export class SandboxPage extends ListPage {
186
262
  await this.sandbox.hideWindow();
187
263
  return {
188
264
  reload: true,
189
- notice: Theme.ok(Theme.glyph.ok + ' Window hidden.') + Theme.muted(' sbx-show brings it back.'),
265
+ notice: Theme.ok(Theme.glyph.ok + ' Window hidden.'),
190
266
  };
191
267
  }
192
268
  async stop() {
@@ -214,17 +290,18 @@ export class SandboxPage extends ListPage {
214
290
  return { reload: true, notice: Theme.ok(Theme.glyph.ok + ' The sandbox is shutting down.') };
215
291
  }
216
292
  async addRoot() {
217
- const typed = await this.app.text({
293
+ const picked = await this.app.pickPath({
218
294
  path: ['Sandbox', 'Add a project root'],
219
- label: 'Folder that holds your projects',
295
+ title: 'Choose the folder that holds your projects',
220
296
  about: [
221
297
  'Every project inside it (also new ones) opens without a restart. Every agent in the sandbox can see them.',
222
298
  ],
223
- validate: (v) => (v.trim() ? true : 'Type a folder.'),
299
+ use: 'add this root',
300
+ validate: (v) => (fs.existsSync(v) ? true : 'That folder does not exist.'),
224
301
  });
225
- if (!typed)
302
+ if (!picked)
226
303
  return {};
227
- const { dir, added } = this.projects.addRoot(typed.trim());
304
+ const { dir, added } = this.projects.addRoot(picked);
228
305
  return {
229
306
  reload: true,
230
307
  notice: added
@@ -232,8 +309,20 @@ export class SandboxPage extends ListPage {
232
309
  : Theme.muted(dir + ' is a root already.'),
233
310
  };
234
311
  }
235
- forget(id) {
236
- if (id?.startsWith('root:')) {
312
+ async forget(id) {
313
+ const root = id?.startsWith('root:');
314
+ if (!id || (!root && !id.startsWith('project:')))
315
+ return {};
316
+ const folder = id.slice(root ? 5 : 8);
317
+ const ok = await this.app.ask((root ? 'Remove the root ' : 'Forget ') + path.basename(folder) + '?', [
318
+ root
319
+ ? 'Its projects are no longer opened without a restart.'
320
+ : 'The project is dropped from your list of projects.',
321
+ 'Nothing in ' + folder + ' is touched.',
322
+ ], root ? 'Remove' : 'Forget', true);
323
+ if (!ok)
324
+ return {};
325
+ if (id.startsWith('root:')) {
237
326
  const { dir, removed } = this.projects.removeRoot(id.slice(5));
238
327
  return {
239
328
  reload: true,
@@ -6,14 +6,18 @@ export interface SessionsModel {
6
6
  readonly sandboxRunning: boolean;
7
7
  /** Newest first, this folder's project first. */
8
8
  readonly sessions: readonly ListedSession[];
9
+ /** The agent "Start" offers while there is no session. */
10
+ readonly defaultAgent: string;
9
11
  }
10
12
  /** The list for the sessions tab. Pure. */
11
13
  export declare function sessionsOptions(m: SessionsModel): ListOptions<string>;
12
14
  export declare class SessionsPage extends ListPage<string> {
15
+ private readonly ctx;
13
16
  readonly label = "Sessions";
14
17
  private model;
15
18
  private readonly service;
16
19
  constructor(app: App, ctx: Context);
20
+ private defaultAgent;
17
21
  protected load(): Promise<void>;
18
22
  protected options(): ListOptions<string>;
19
23
  protected act(result: ListResult<string>): Promise<Outcome<string>>;
@@ -1,4 +1,4 @@
1
- // Tab 2: the agent sessions of the running sandbox. Enter shows one in this terminal (the one thing that leaves the
1
+ // Section 2: the agent sessions of the running sandbox. Enter shows one in this terminal (the one thing that leaves the
2
2
  // menu), r restarts the agents.
3
3
  import { errMsg, safeText } from '../../../core/util/index.js';
4
4
  import { isSandboxRunning } from '../../../host/sandbox/index.js';
@@ -6,54 +6,68 @@ import { ago } from '../../output/index.js';
6
6
  import { SessionsService, viewerText } from '../../services/index.js';
7
7
  import { Theme } from '../../tui/index.js';
8
8
  import { ListPage } from './list-page.js';
9
+ const START = '@start';
9
10
  /** The list for the sessions tab. Pure. */
10
11
  export function sessionsOptions(m) {
11
12
  return {
12
13
  path: [],
13
- intro: m.sandboxRunning
14
- ? undefined
15
- : 'No sandbox is running. Start one on the Start tab (1); its sessions are listed here.',
16
- items: m.sessions.map((s) => ({
17
- value: s.id,
18
- label: safeText(s.harness) + ' ' + safeText(s.projectName ?? '-'),
19
- hint: s.phase + (s.exitCode !== undefined ? ' (' + s.exitCode + ')' : '') + ' ' + ago(s.startedAt),
20
- section: s.here ? "This folder's project" : 'Other sessions',
21
- description: s.phase === 'running'
22
- ? 'Enter shows this session in this terminal (Ctrl+] detaches; the session keeps running).'
23
- : undefined,
24
- details: [
25
- Theme.muted('Session ') + s.id,
26
- Theme.muted('Folder ') + safeText(s.cwd),
27
- Theme.muted('Attached ') + viewerText(s.viewers),
14
+ items: m.sessions.length
15
+ ? m.sessions.map((s) => ({
16
+ value: s.id,
17
+ label: safeText(s.harness) + ' ' + safeText(s.projectName ?? '-'),
18
+ hint: s.phase + (s.exitCode !== undefined ? ' (' + s.exitCode + ')' : '') + ' ' + ago(s.startedAt),
19
+ section: s.here ? "This folder's project" : 'Other sessions',
20
+ description: s.phase === 'running'
21
+ ? 'Enter shows this session in this terminal (Ctrl+] detaches; the session keeps running).'
22
+ : undefined,
23
+ details: [
24
+ Theme.muted('Session ') + s.id,
25
+ Theme.muted('Folder ') + safeText(s.cwd),
26
+ Theme.muted('Attached ') + viewerText(s.viewers),
27
+ ],
28
+ ...(s.phase === 'running' ? {} : { disabled: 'This session has ended.' }),
29
+ }))
30
+ : [
31
+ {
32
+ value: START,
33
+ label: 'Start ' + m.defaultAgent,
34
+ hint: Theme.muted('default agent'),
35
+ section: m.sandboxRunning ? 'No sessions' : 'No sandbox is running',
36
+ description: m.sandboxRunning
37
+ ? 'No agent is running. Enter starts ' + m.defaultAgent + ' in this folder.'
38
+ : 'Enter starts the sandbox and ' + m.defaultAgent + ' in this folder; its session is listed here.',
39
+ },
28
40
  ],
29
- ...(s.phase === 'running' ? {} : { disabled: 'This session has ended.' }),
30
- })),
31
- empty: 'No sessions yet.',
32
41
  actions: [
33
- { key: 'r', label: 'restart agents', importance: 1 },
42
+ { key: 'r', label: 'restart agents', importance: 1, applies: (id) => id !== START },
34
43
  { key: 'o', label: 'reopen last sessions', importance: 2 },
35
44
  { key: 'l', label: 'refresh', importance: 3 },
36
45
  ],
37
- enterLabel: 'attach',
38
- escapeLabel: 'quit',
46
+ enterLabel: (_category, id) => (id === START ? 'start' : 'attach'),
39
47
  };
40
48
  }
41
49
  export class SessionsPage extends ListPage {
50
+ ctx;
42
51
  label = 'Sessions';
43
- model = { sandboxRunning: false, sessions: [] };
52
+ model;
44
53
  service;
45
54
  constructor(app, ctx) {
46
55
  super(app);
56
+ this.ctx = ctx;
47
57
  this.service = new SessionsService(ctx);
58
+ this.model = { sandboxRunning: false, sessions: [], defaultAgent: this.defaultAgent() };
59
+ }
60
+ defaultAgent() {
61
+ return this.ctx.loaded.config.defaultHarness;
48
62
  }
49
63
  async load() {
50
64
  const sandboxRunning = isSandboxRunning();
51
65
  try {
52
66
  const list = sandboxRunning ? await this.service.list() : [];
53
- this.model = { sandboxRunning, sessions: this.service.ordered(list) };
67
+ this.model = { sandboxRunning, sessions: this.service.ordered(list), defaultAgent: this.defaultAgent() };
54
68
  }
55
69
  catch (e) {
56
- this.model = { sandboxRunning, sessions: [] };
70
+ this.model = { sandboxRunning, sessions: [], defaultAgent: this.defaultAgent() };
57
71
  this.say(Theme.error(Theme.glyph.cross + ' ' + errMsg(e)));
58
72
  }
59
73
  }
@@ -61,6 +75,8 @@ export class SessionsPage extends ListPage {
61
75
  return sessionsOptions(this.model);
62
76
  }
63
77
  async act(result) {
78
+ if (result.kind === 'pick' && result.value === START)
79
+ return { exit: { kind: 'run', argv: ['launch', '--harness', this.model.defaultAgent] } };
64
80
  if (result.kind === 'pick')
65
81
  return { exit: { kind: 'run', argv: ['attach', result.value] } };
66
82
  if (result.kind !== 'action')
@@ -1,4 +1,5 @@
1
- import { type App, type ListOptions, type ListResult } from '../../tui/index.js';
1
+ import type { Context } from '../../commands/context.js';
2
+ import { type App, type ListItem, type ListOptions, type ListResult } from '../../tui/index.js';
2
3
  import type { ConfigFile } from '../settings/config-file.js';
3
4
  import type { Draft } from '../settings/draft.js';
4
5
  import type { SchemaNode } from '../settings/schema-node.js';
@@ -12,16 +13,19 @@ export interface Setting {
12
13
  }
13
14
  /** Every editable setting with the category and heading it is shown under. */
14
15
  export declare function settingsOf(file: ConfigFile): Setting[];
15
- /** The list for the settings tab. Pure (reads the files it is given). */
16
- export declare function settingsOptions(files: Record<Scope, ConfigFile>, scope: Scope, draft: Draft): ListOptions<string>;
16
+ /** The row in General that opens the data folder flow (your own settings only). */
17
+ export declare const DATA_FOLDER = "@data-folder";
18
+ /** The list for the settings section. Pure (reads the files it is given). `extra` are rows that are not settings. */
19
+ export declare function settingsOptions(files: Record<Scope, ConfigFile>, scope: Scope, draft: Draft, extra?: readonly ListItem<string>[]): ListOptions<string>;
17
20
  export declare class SettingsPage extends ListPage<string> {
18
21
  private readonly files;
19
22
  private readonly draft;
23
+ private readonly ctx;
20
24
  readonly label = "Settings";
21
25
  readonly stages = true;
22
26
  private scope;
23
27
  private readonly editor;
24
- constructor(app: App, files: Record<Scope, ConfigFile>, draft: Draft);
28
+ constructor(app: App, files: Record<Scope, ConfigFile>, draft: Draft, ctx: Context);
25
29
  private get file();
26
30
  protected load(): Promise<void>;
27
31
  protected options(): ListOptions<string>;
@@ -1,10 +1,12 @@
1
- // Tab 5: every setting, for all projects or just this project (two scopes), grouped into categories on the left and
1
+ // Section 5: every setting, for all projects or just this project (two scopes), grouped into categories on the left and
2
2
  // searchable, with each value, its default and its description. Changes are staged in the Draft (marked *) and
3
3
  // written with Ctrl+S; Enter edits, r resets to the default, z undoes an unsaved change.
4
4
  import _ from 'lodash';
5
5
  import { NEVER_LOOSER } from '../../../core/local-config/index.js';
6
6
  import { errMsg } from '../../../core/util/index.js';
7
7
  import { Text, Theme, } from '../../tui/index.js';
8
+ import { dataFolderRow, DataFolderFlow } from '../data-folder.js';
9
+ import { VpnLoginFlow, vpnLoginRow, VPN_LOGIN } from '../vpn-login.js';
8
10
  import { CATEGORIES, categoryOf, nameOf } from '../settings/categories.js';
9
11
  import { formatValue } from '../settings/format.js';
10
12
  import { ValueEditor } from '../settings/value-editor.js';
@@ -106,11 +108,18 @@ function item(s, files, scope, draft, config) {
106
108
  flip: s.node.editor === 'boolean',
107
109
  };
108
110
  }
109
- /** The list for the settings tab. Pure (reads the files it is given). */
110
- export function settingsOptions(files, scope, draft) {
111
+ /** The row in General that opens the data folder flow (your own settings only). */
112
+ export const DATA_FOLDER = '@data-folder';
113
+ /** The list for the settings section. Pure (reads the files it is given). `extra` are rows that are not settings. */
114
+ export function settingsOptions(files, scope, draft, extra = []) {
111
115
  const file = files[scope];
112
116
  const config = file.userConfig();
113
- const settings = settingsOf(file);
117
+ // The Data folder row stands in for the storage folder setting (changing it moves the data, which is asked about).
118
+ const hideRoot = !file.isProject && extra.some((e) => e.value === DATA_FOLDER);
119
+ const settings = settingsOf(file).filter((s) => !(hideRoot && s.key.join('.') === 'storage.root'));
120
+ const ids = (test) => new Set(settings.filter(test).map((s) => s.key.join('.')));
121
+ const set = ids((s) => written(file, draft, s.key) !== undefined);
122
+ const unsaved = ids((s) => draft.has(file, s.key));
114
123
  const categories = CATEGORIES.filter((c) => settings.some((s) => s.category === c.id)).map((c) => ({
115
124
  id: c.id,
116
125
  label: c.label,
@@ -120,27 +129,28 @@ export function settingsOptions(files, scope, draft) {
120
129
  path: [],
121
130
  status: scopeStatus(file),
122
131
  tabs: { items: SCOPES.map((x) => x.tab), active: SCOPES.findIndex((x) => x.scope === scope) },
123
- items: settings.map((s) => item(s, files, scope, draft, config)),
132
+ items: [...settings.map((s) => item(s, files, scope, draft, config)), ...(file.isProject ? [] : extra)],
124
133
  categories,
125
134
  actions: [
126
- { key: 'r', label: file.isProject ? 'remove' : 'reset' },
127
- { key: 'z', label: 'undo' },
135
+ { key: 'r', label: file.isProject ? 'remove' : 'reset to default', applies: (id) => set.has(id) },
136
+ { key: 'z', label: 'undo', applies: (id) => unsaved.has(id) },
128
137
  ],
129
- enterLabel: 'edit',
130
- escapeLabel: 'quit',
138
+ enterLabel: (_category, id) => (id === DATA_FOLDER || id === VPN_LOGIN ? 'change' : 'edit'),
131
139
  };
132
140
  }
133
141
  export class SettingsPage extends ListPage {
134
142
  files;
135
143
  draft;
144
+ ctx;
136
145
  label = 'Settings';
137
146
  stages = true;
138
147
  scope = 'user';
139
148
  editor;
140
- constructor(app, files, draft) {
149
+ constructor(app, files, draft, ctx) {
141
150
  super(app);
142
151
  this.files = files;
143
152
  this.draft = draft;
153
+ this.ctx = ctx;
144
154
  this.editor = new ValueEditor(app, {
145
155
  suggestions: (key) => ['defaultHarness', 'harness'].includes(key.join('.'))
146
156
  ? Object.keys(this.files.user.userConfig().harnesses)
@@ -154,7 +164,11 @@ export class SettingsPage extends ListPage {
154
164
  return Promise.resolve();
155
165
  }
156
166
  options() {
157
- return settingsOptions(this.files, this.scope, this.draft);
167
+ const row = dataFolderRow(this.ctx);
168
+ return settingsOptions(this.files, this.scope, this.draft, [
169
+ { value: DATA_FOLDER, category: 'general', ...row },
170
+ { value: VPN_LOGIN, category: 'network', ...vpnLoginRow(this.ctx) },
171
+ ]);
158
172
  }
159
173
  /** Stages a value; your own file never keeps a line that only repeats the default. */
160
174
  stage(key, value) {
@@ -176,6 +190,17 @@ export class SettingsPage extends ListPage {
176
190
  return {};
177
191
  if (result.value === undefined)
178
192
  return {};
193
+ if (result.value === DATA_FOLDER) {
194
+ if (result.kind !== 'pick')
195
+ return {};
196
+ const exit = await new DataFolderFlow(this.app, this.ctx).run();
197
+ return exit ? { exit } : { reload: true };
198
+ }
199
+ if (result.value === VPN_LOGIN) {
200
+ if (result.kind !== 'pick')
201
+ return {};
202
+ return { reload: true, notice: await new VpnLoginFlow(this.app, this.ctx).run() };
203
+ }
179
204
  const setting = settingsOf(this.file).find((s) => s.key.join('.') === result.value);
180
205
  if (!setting)
181
206
  return {};
@@ -187,8 +212,6 @@ export class SettingsPage extends ListPage {
187
212
  this.stage(setting.key, undefined);
188
213
  return {};
189
214
  }
190
- if (result.value === 'apps.install')
191
- return { notice: Theme.muted('Choose apps on the Apps tab (press 4); the list is picked there, not typed.') };
192
215
  try {
193
216
  const config = this.file.userConfig();
194
217
  const current = this.file.isProject
@@ -5,5 +5,7 @@ export interface SettingsCategory {
5
5
  keys: string[];
6
6
  }
7
7
  export declare const CATEGORIES: SettingsCategory[];
8
+ /** Settings not listed here because their own section covers them (Apps: the catalogue and your own programs). */
9
+ export declare const SETTINGS_ELSEWHERE: readonly string[];
8
10
  export declare function nameOf(key: string): string;
9
11
  export declare function categoryOf(topKey: string): SettingsCategory | undefined;