windows-development-sandbox 1.9.0 → 1.11.3

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 (398) hide show
  1. package/README.md +64 -17
  2. package/config.example.json +7 -2
  3. package/dist/app/features/accounts/accounts.def.d.ts +122 -0
  4. package/dist/app/features/accounts/accounts.def.js +243 -0
  5. package/dist/app/features/accounts/accounts.run.d.ts +51 -0
  6. package/dist/app/features/accounts/accounts.run.js +125 -0
  7. package/dist/app/features/accounts/index.d.ts +2 -0
  8. package/dist/app/features/accounts/index.js +3 -0
  9. package/dist/app/features/agents/agent-entry.d.ts +17 -0
  10. package/dist/app/features/agents/agent-entry.js +50 -0
  11. package/dist/app/features/agents/agents.def.d.ts +65 -0
  12. package/dist/app/features/agents/agents.def.js +142 -0
  13. package/dist/app/features/agents/agents.run.d.ts +31 -0
  14. package/dist/app/features/agents/agents.run.js +189 -0
  15. package/dist/app/features/agents/index.d.ts +3 -0
  16. package/dist/app/features/agents/index.js +4 -0
  17. package/dist/app/features/apps/apps-rows.d.ts +31 -0
  18. package/dist/app/features/apps/apps-rows.js +73 -0
  19. package/dist/app/features/apps/apps.def.d.ts +181 -0
  20. package/dist/app/features/apps/apps.def.js +264 -0
  21. package/dist/app/features/apps/apps.run.d.ts +58 -0
  22. package/dist/app/features/apps/apps.run.js +76 -0
  23. package/dist/app/features/apps/index.d.ts +3 -0
  24. package/dist/app/features/apps/index.js +4 -0
  25. package/dist/app/features/data/data.def.d.ts +154 -0
  26. package/dist/app/features/data/data.def.js +201 -0
  27. package/dist/app/features/data/data.run.d.ts +46 -0
  28. package/dist/app/features/data/data.run.js +141 -0
  29. package/dist/app/features/data/index.d.ts +2 -0
  30. package/dist/app/features/data/index.js +3 -0
  31. package/dist/app/features/defs.d.ts +952 -4
  32. package/dist/app/features/defs.js +147 -10
  33. package/dist/app/features/engine/engine.def.d.ts +228 -0
  34. package/dist/app/features/engine/engine.def.js +194 -0
  35. package/dist/app/features/engine/engine.run.d.ts +72 -0
  36. package/dist/app/features/engine/engine.run.js +77 -0
  37. package/dist/app/features/engine/index.d.ts +2 -0
  38. package/dist/app/features/engine/index.js +4 -0
  39. package/dist/app/features/files/files.def.d.ts +51 -0
  40. package/dist/app/features/files/files.def.js +38 -0
  41. package/dist/app/features/files/files.run.d.ts +20 -0
  42. package/dist/app/features/files/files.run.js +59 -0
  43. package/dist/app/features/files/index.d.ts +2 -0
  44. package/dist/app/features/files/index.js +3 -0
  45. package/dist/app/features/github/github.def.d.ts +80 -0
  46. package/dist/app/features/github/github.def.js +142 -0
  47. package/dist/app/features/github/github.run.d.ts +36 -0
  48. package/dist/app/features/github/github.run.js +59 -0
  49. package/dist/app/features/github/index.d.ts +2 -0
  50. package/dist/app/features/github/index.js +3 -0
  51. package/dist/app/features/health/health.def.d.ts +275 -0
  52. package/dist/app/features/health/health.def.js +281 -0
  53. package/dist/app/features/health/health.run.d.ts +125 -0
  54. package/dist/app/features/health/health.run.js +204 -0
  55. package/dist/app/features/health/index.d.ts +2 -0
  56. package/dist/app/features/health/index.js +3 -0
  57. package/dist/app/features/logs/index.d.ts +2 -0
  58. package/dist/app/features/logs/index.js +3 -0
  59. package/dist/app/features/logs/logs.def.d.ts +78 -0
  60. package/dist/app/features/logs/logs.def.js +115 -0
  61. package/dist/app/features/logs/logs.run.d.ts +20 -0
  62. package/dist/app/features/logs/logs.run.js +42 -0
  63. package/dist/app/features/projects/index.d.ts +2 -0
  64. package/dist/app/features/projects/index.js +3 -0
  65. package/dist/app/features/projects/projects.def.d.ts +101 -0
  66. package/dist/app/features/projects/projects.def.js +208 -0
  67. package/dist/app/features/projects/projects.run.d.ts +56 -0
  68. package/dist/app/features/projects/projects.run.js +155 -0
  69. package/dist/app/features/sandbox/sandbox.def.js +11 -5
  70. package/dist/app/features/sessions/sessions.def.js +3 -0
  71. package/dist/app/features/settings/categories.def.js +3 -2
  72. package/dist/{cli/menu/settings/share.d.ts → app/features/settings/settings-share.d.ts} +1 -1
  73. package/dist/{cli/menu/settings/share.js → app/features/settings/settings-share.js} +3 -2
  74. package/dist/app/features/settings/settings.def.d.ts +22 -5
  75. package/dist/app/features/settings/settings.def.js +81 -6
  76. package/dist/app/features/settings/settings.run.d.ts +17 -0
  77. package/dist/app/features/settings/settings.run.js +89 -4
  78. package/dist/app/features/storage/index.d.ts +2 -0
  79. package/dist/app/features/storage/index.js +3 -0
  80. package/dist/app/features/storage/storage.def.d.ts +133 -0
  81. package/dist/app/features/storage/storage.def.js +225 -0
  82. package/dist/app/features/storage/storage.run.d.ts +62 -0
  83. package/dist/app/features/storage/storage.run.js +370 -0
  84. package/dist/app/features/updates/index.d.ts +3 -0
  85. package/dist/app/features/updates/index.js +4 -0
  86. package/dist/app/features/updates/update-lines.d.ts +13 -0
  87. package/dist/app/features/updates/update-lines.js +61 -0
  88. package/dist/app/features/updates/updates.def.d.ts +89 -0
  89. package/dist/app/features/updates/updates.def.js +225 -0
  90. package/dist/app/features/updates/updates.run.d.ts +48 -0
  91. package/dist/app/features/updates/updates.run.js +154 -0
  92. package/dist/app/features/vpn/index.d.ts +2 -0
  93. package/dist/app/features/vpn/index.js +3 -0
  94. package/dist/app/features/vpn/vpn.def.d.ts +112 -0
  95. package/dist/app/features/vpn/vpn.def.js +174 -0
  96. package/dist/app/features/vpn/vpn.run.d.ts +37 -0
  97. package/dist/app/features/vpn/vpn.run.js +54 -0
  98. package/dist/app/features/web/index.d.ts +2 -0
  99. package/dist/app/features/web/index.js +3 -0
  100. package/dist/app/features/web/web.def.d.ts +33 -0
  101. package/dist/app/features/web/web.def.js +90 -0
  102. package/dist/app/features/web/web.run.d.ts +24 -0
  103. package/dist/app/features/web/web.run.js +93 -0
  104. package/dist/app/kernel/define.d.ts +10 -1
  105. package/dist/app/kernel/io.d.ts +34 -0
  106. package/dist/app/kernel/io.js +9 -0
  107. package/dist/app/kernel/rpc.d.ts +17 -0
  108. package/dist/app/kernel/text.d.ts +2 -0
  109. package/dist/app/kernel/text.js +7 -0
  110. package/dist/app/kernel/view.d.ts +24 -1
  111. package/dist/app/kernel/view.js +1 -1
  112. package/dist/app/registry.js +74 -1
  113. package/dist/cli/args.js +1 -1
  114. package/dist/cli/commands/context.d.ts +7 -0
  115. package/dist/cli/commands/context.js +29 -2
  116. package/dist/cli/commands/engine.js +5 -58
  117. package/dist/cli/commands/github.d.ts +2 -0
  118. package/dist/cli/commands/github.js +11 -0
  119. package/dist/cli/commands/index.d.ts +3 -5
  120. package/dist/cli/commands/index.js +3 -5
  121. package/dist/cli/commands/launch/launch.js +23 -4
  122. package/dist/cli/commands/launch/running.js +4 -1
  123. package/dist/cli/commands/maintenance/config.js +1 -6
  124. package/dist/cli/commands/maintenance/index.d.ts +0 -1
  125. package/dist/cli/commands/maintenance/index.js +1 -2
  126. package/dist/cli/commands/maintenance/logs.js +27 -32
  127. package/dist/cli/commands/maintenance/migrate.d.ts +0 -8
  128. package/dist/cli/commands/maintenance/migrate.js +2 -44
  129. package/dist/cli/commands/maintenance/update.d.ts +0 -19
  130. package/dist/cli/commands/maintenance/update.js +1 -99
  131. package/dist/cli/commands/projects/index.d.ts +0 -1
  132. package/dist/cli/commands/projects/index.js +0 -1
  133. package/dist/cli/commands/registry.d.ts +1 -1
  134. package/dist/cli/commands/registry.js +29 -11
  135. package/dist/cli/commands/repair/checks/data.js +1 -1
  136. package/dist/cli/commands/repair/checks/hcs.js +28 -1
  137. package/dist/cli/commands/repair/checks/sandbox.js +1 -1
  138. package/dist/cli/commands/repair/health-run.d.ts +30 -0
  139. package/dist/cli/commands/repair/health-run.js +32 -0
  140. package/dist/cli/commands/repair/printer.d.ts +2 -2
  141. package/dist/cli/commands/repair/printer.js +2 -11
  142. package/dist/cli/commands/repair/run.d.ts +1 -21
  143. package/dist/cli/commands/repair/run.js +13 -70
  144. package/dist/cli/commands/storage/index.d.ts +0 -1
  145. package/dist/cli/commands/storage/index.js +0 -1
  146. package/dist/cli/commands/storage/offer.js +6 -2
  147. package/dist/cli/commands/vpn.d.ts +1 -3
  148. package/dist/cli/commands/vpn.js +8 -88
  149. package/dist/cli/commands/web.d.ts +7 -1
  150. package/dist/cli/commands/web.js +45 -71
  151. package/dist/cli/index.js +3 -2
  152. package/dist/cli/manifest.d.ts +16 -153
  153. package/dist/cli/manifest.js +20 -50
  154. package/dist/cli/menu/command-form.js +1 -2
  155. package/dist/cli/menu/io-menu.d.ts +2 -2
  156. package/dist/cli/menu/io-menu.js +6 -4
  157. package/dist/cli/menu/menu.js +1 -1
  158. package/dist/cli/menu/pages/accounts-page.d.ts +5 -17
  159. package/dist/cli/menu/pages/accounts-page.js +28 -170
  160. package/dist/cli/menu/pages/agents-page.d.ts +17 -18
  161. package/dist/cli/menu/pages/agents-page.js +67 -124
  162. package/dist/cli/menu/pages/apps-page.d.ts +15 -8
  163. package/dist/cli/menu/pages/apps-page.js +121 -29
  164. package/dist/cli/menu/pages/list-page.js +10 -1
  165. package/dist/cli/menu/pages/maintenance-page.d.ts +51 -52
  166. package/dist/cli/menu/pages/maintenance-page.js +246 -427
  167. package/dist/cli/menu/pages/sandbox-page.d.ts +3 -5
  168. package/dist/cli/menu/pages/sandbox-page.js +34 -112
  169. package/dist/cli/menu/pages/settings-page.d.ts +21 -3
  170. package/dist/cli/menu/pages/settings-page.js +89 -17
  171. package/dist/cli/menu/render-view.js +2 -1
  172. package/dist/cli/menu/settings/index.d.ts +0 -1
  173. package/dist/cli/menu/settings/index.js +3 -3
  174. package/dist/cli/menu/views.js +28 -0
  175. package/dist/cli/operations/handler.js +7 -3
  176. package/dist/cli/operations/input.d.ts +1 -1
  177. package/dist/cli/operations/input.js +29 -5
  178. package/dist/cli/operations/io-cli.js +9 -1
  179. package/dist/cli/operations/spec.js +8 -2
  180. package/dist/cli/output/render-view.js +2 -0
  181. package/dist/cli/{commands/data/collect.js → services/data-collect.js} +2 -2
  182. package/dist/cli/{commands/data/remove.js → services/data-remove.js} +2 -2
  183. package/dist/cli/services/data-service.d.ts +1 -1
  184. package/dist/cli/services/data-service.js +2 -2
  185. package/dist/cli/services/databases-service.d.ts +5 -0
  186. package/dist/cli/services/databases-service.js +15 -0
  187. package/dist/cli/services/health-service.d.ts +8 -4
  188. package/dist/cli/services/health-service.js +10 -8
  189. package/dist/cli/services/index.d.ts +4 -1
  190. package/dist/cli/services/index.js +4 -1
  191. package/dist/cli/services/logs-service.d.ts +7 -0
  192. package/dist/cli/services/logs-service.js +13 -0
  193. package/dist/cli/services/storage-service.d.ts +19 -0
  194. package/dist/cli/services/storage-service.js +71 -0
  195. package/dist/cli/services/vpn-service.d.ts +6 -4
  196. package/dist/cli/services/vpn-service.js +5 -11
  197. package/dist/cli/services/web-service.d.ts +4 -3
  198. package/dist/cli/services/web-service.js +12 -32
  199. package/dist/core/config/defaults.js +3 -2
  200. package/dist/core/schema/config.d.ts +6 -0
  201. package/dist/core/schema/config.js +2 -0
  202. package/dist/core/schema/github.d.ts +4 -0
  203. package/dist/core/schema/github.js +7 -0
  204. package/dist/core/schema/index.d.ts +1 -0
  205. package/dist/core/schema/index.js +1 -0
  206. package/dist/core/schema/profile.d.ts +1 -0
  207. package/dist/core/schema/profile.js +3 -0
  208. package/dist/core/schema/sandbox.js +3 -3
  209. package/dist/core/schema/toolchains.js +2 -2
  210. package/dist/core/schema/vpn.js +1 -1
  211. package/dist/core/schema/web.d.ts +1 -0
  212. package/dist/core/schema/web.js +4 -1
  213. package/dist/core/types.d.ts +6 -3
  214. package/dist/guest/boot/boot.js +11 -1
  215. package/dist/guest/daemon/daemon.js +1 -1
  216. package/dist/guest/daemon/sessions.d.ts +3 -2
  217. package/dist/guest/daemon/sessions.js +38 -7
  218. package/dist/guest/governor/core.d.ts +44 -0
  219. package/dist/guest/governor/core.js +124 -0
  220. package/dist/guest/governor/decide.d.ts +53 -15
  221. package/dist/guest/governor/decide.js +155 -72
  222. package/dist/guest/governor/governor.d.ts +2 -35
  223. package/dist/guest/governor/governor.js +35 -99
  224. package/dist/guest/governor/index.d.ts +1 -0
  225. package/dist/guest/governor/index.js +1 -0
  226. package/dist/guest/network/firewall.d.ts +3 -0
  227. package/dist/guest/network/firewall.js +65 -23
  228. package/dist/guest/runtime/process.js +53 -5
  229. package/dist/guest/sessions/viewer.js +5 -1
  230. package/dist/guest/setup/claude-mem.js +12 -4
  231. package/dist/guest/setup/github-token.d.ts +7 -0
  232. package/dist/guest/setup/github-token.js +65 -0
  233. package/dist/guest/toolchains/languages/node.js +40 -4
  234. package/dist/guest/toolchains/native/rust-extras.js +2 -2
  235. package/dist/guest/toolchains/tools/cli.d.ts +8 -0
  236. package/dist/guest/toolchains/tools/cli.js +16 -2
  237. package/dist/guest/vpn/openvpn.d.ts +5 -0
  238. package/dist/guest/vpn/openvpn.js +14 -1
  239. package/dist/guest/vpn/vpn.js +55 -16
  240. package/dist/guest/vpn/wireguard.js +2 -0
  241. package/dist/host/channel/attach.d.ts +1 -0
  242. package/dist/host/channel/attach.js +1 -0
  243. package/dist/host/channel/channel.d.ts +1 -1
  244. package/dist/host/channel/channel.js +2 -2
  245. package/dist/host/engines/hcs/check.d.ts +1 -0
  246. package/dist/host/engines/hcs/check.js +1 -1
  247. package/dist/host/engines/hcs/engine.d.ts +1 -1
  248. package/dist/host/engines/hcs/engine.js +6 -4
  249. package/dist/host/engines/hcs/helper.d.ts +11 -0
  250. package/dist/host/engines/hcs/helper.js +31 -2
  251. package/dist/host/engines/hcs/leftovers.d.ts +42 -0
  252. package/dist/host/engines/hcs/leftovers.js +101 -0
  253. package/dist/host/engines/index.d.ts +1 -0
  254. package/dist/host/engines/index.js +1 -0
  255. package/dist/host/github/deliver.d.ts +13 -0
  256. package/dist/host/github/deliver.js +51 -0
  257. package/dist/host/github/index.d.ts +2 -0
  258. package/dist/host/github/index.js +3 -0
  259. package/dist/host/github/token.d.ts +21 -0
  260. package/dist/host/github/token.js +74 -0
  261. package/dist/host/launch/generate.js +11 -5
  262. package/dist/host/launch/index.d.ts +1 -1
  263. package/dist/host/launch/index.js +1 -1
  264. package/dist/host/launch/session-file.js +1 -2
  265. package/dist/host/launch/wsb-file.d.ts +11 -7
  266. package/dist/host/launch/wsb-file.js +30 -12
  267. package/dist/host/life/host-memory.d.ts +1 -0
  268. package/dist/host/life/host-memory.js +4 -1
  269. package/dist/host/life/watchdog.d.ts +11 -0
  270. package/dist/host/life/watchdog.js +36 -4
  271. package/dist/host/sandbox/tasklist.d.ts +6 -0
  272. package/dist/host/sandbox/tasklist.js +37 -0
  273. package/dist/host/secrets/launch-secrets.d.ts +2 -1
  274. package/dist/host/secrets/launch-secrets.js +4 -2
  275. package/dist/host/vpn/status.js +13 -3
  276. package/dist/host/web/backend.d.ts +1 -3
  277. package/dist/host/web/index.d.ts +2 -1
  278. package/dist/host/web/index.js +2 -1
  279. package/dist/host/web/live.d.ts +23 -0
  280. package/dist/host/web/live.js +107 -0
  281. package/dist/host/web/rpc.d.ts +6 -1
  282. package/dist/host/web/rpc.js +23 -31
  283. package/dist/host/web/runner.d.ts +4 -0
  284. package/dist/host/web/runner.js +12 -1
  285. package/dist/host/web/server.d.ts +30 -2
  286. package/dist/host/web/server.js +87 -29
  287. package/dist/host/web/state.d.ts +1 -0
  288. package/dist/host/web/state.js +2 -0
  289. package/dist/runtime/agent.js +44 -16
  290. package/dist/runtime/daemon.js +1154 -857
  291. package/dist/runtime/db.js +44 -16
  292. package/dist/runtime/main.js +532 -115
  293. package/dist/runtime/netproxy.js +34 -9
  294. package/dist/runtime/view.js +499 -149
  295. package/dist/selftest/app.test.js +620 -12
  296. package/dist/selftest/channel.test.js +18 -1
  297. package/dist/selftest/cli.test.js +1 -1
  298. package/dist/selftest/config.test.js +1 -1
  299. package/dist/selftest/contained.test.js +1 -1
  300. package/dist/selftest/data.test.js +1 -2
  301. package/dist/selftest/engine.test.js +3 -1
  302. package/dist/selftest/github.test.d.ts +2 -0
  303. package/dist/selftest/github.test.js +243 -0
  304. package/dist/selftest/guest-boot.test.js +47 -1
  305. package/dist/selftest/hcs.test.js +105 -8
  306. package/dist/selftest/health.test.js +71 -0
  307. package/dist/selftest/heavy-load.test.js +152 -66
  308. package/dist/selftest/index.js +4 -0
  309. package/dist/selftest/layout.test.js +8 -0
  310. package/dist/selftest/live-terminal.test.js +74 -0
  311. package/dist/selftest/menu-screens.js +171 -56
  312. package/dist/selftest/menu.test.js +54 -37
  313. package/dist/selftest/migrate.test.js +14 -12
  314. package/dist/selftest/network.test.js +20 -1
  315. package/dist/selftest/powershell-variables.test.d.ts +2 -0
  316. package/dist/selftest/powershell-variables.test.js +61 -0
  317. package/dist/selftest/support/engine-golden.js +1 -0
  318. package/dist/selftest/support/fixtures.d.ts +5 -0
  319. package/dist/selftest/support/web-preview.d.ts +1 -0
  320. package/dist/selftest/support/web-preview.js +856 -0
  321. package/dist/selftest/update-service.test.js +15 -8
  322. package/dist/selftest/vpn.test.js +86 -2
  323. package/dist/selftest/web.test.js +58 -11
  324. package/dist/shared/channel/run-dir.d.ts +2 -0
  325. package/dist/shared/channel/run-dir.js +3 -0
  326. package/dist/shared/channel/schema.d.ts +7 -0
  327. package/dist/shared/channel/schema.js +9 -1
  328. package/dist/shared/channel/secrets.d.ts +1 -0
  329. package/dist/shared/channel/secrets.js +3 -0
  330. package/dist/shared/github/index.d.ts +1 -0
  331. package/dist/shared/github/index.js +2 -0
  332. package/dist/shared/github/token.d.ts +29 -0
  333. package/dist/shared/github/token.js +89 -0
  334. package/dist/shared/io/copy-tree.d.ts +7 -0
  335. package/dist/shared/io/copy-tree.js +45 -0
  336. package/dist/shared/io/index.d.ts +2 -0
  337. package/dist/shared/io/index.js +2 -0
  338. package/dist/shared/io/robocopy-result.d.ts +6 -0
  339. package/dist/shared/io/robocopy-result.js +23 -0
  340. package/dist/shared/memory/policy.d.ts +38 -12
  341. package/dist/shared/memory/policy.js +58 -19
  342. package/dist/shared/network/firewall-rules.d.ts +8 -0
  343. package/dist/shared/network/firewall-rules.js +28 -0
  344. package/dist/shared/pty/ctrl-c.d.ts +18 -0
  345. package/dist/shared/pty/ctrl-c.js +36 -0
  346. package/dist/shared/pty/index.d.ts +1 -0
  347. package/dist/shared/pty/index.js +1 -0
  348. package/dist/shared/pty/terminal.d.ts +2 -0
  349. package/dist/shared/pty/terminal.js +21 -2
  350. package/dist/shared/vpn/kill-switch.d.ts +13 -2
  351. package/dist/shared/vpn/kill-switch.js +88 -8
  352. package/dist/shared/web/protocol.d.ts +0 -4
  353. package/dist/sync/watcher.js +1 -1
  354. package/dist/web/index.html +2 -2
  355. package/dist/web/{main-T5DMCHYV.css → main-6JF3ASB5.css} +1 -1
  356. package/dist/web/main-Z645W45E.js +219 -0
  357. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  358. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  359. package/native/csharp/HostTools/MemoryStatus.cs +128 -107
  360. package/native/scripts/memory-status.ps1 +10 -2
  361. package/package.json +10 -3
  362. package/sandbox/csharp/MemGov.cs +668 -398
  363. package/sandbox/csharp/PtyHost.cs +104 -15
  364. package/sandbox/csharp/SandboxTools/CpuCap.cs +50 -1
  365. package/sandbox/csharp/SandboxTools/Program.cs +1 -1
  366. package/sandbox/scripts/new-shortcuts.ps1 +3 -1
  367. package/sandbox/scripts/repair-wmi-services.ps1 +26 -0
  368. package/schemas/config.schema.json +25 -7
  369. package/schemas/sandbox-config.schema.json +7 -3
  370. package/dist/cli/commands/accounts.d.ts +0 -2
  371. package/dist/cli/commands/accounts.js +0 -96
  372. package/dist/cli/commands/data/clean.d.ts +0 -3
  373. package/dist/cli/commands/data/clean.js +0 -60
  374. package/dist/cli/commands/data/index.d.ts +0 -4
  375. package/dist/cli/commands/data/index.js +0 -62
  376. package/dist/cli/commands/maintenance/clean.d.ts +0 -2
  377. package/dist/cli/commands/maintenance/clean.js +0 -24
  378. package/dist/cli/commands/maintenance/config-share.d.ts +0 -12
  379. package/dist/cli/commands/maintenance/config-share.js +0 -80
  380. package/dist/cli/commands/projects/projects.d.ts +0 -2
  381. package/dist/cli/commands/projects/projects.js +0 -162
  382. package/dist/cli/commands/storage/contained.d.ts +0 -3
  383. package/dist/cli/commands/storage/contained.js +0 -179
  384. package/dist/cli/commands/storage/storage.d.ts +0 -8
  385. package/dist/cli/commands/storage/storage.js +0 -179
  386. package/dist/cli/menu/add-agent.d.ts +0 -47
  387. package/dist/cli/menu/add-agent.js +0 -168
  388. package/dist/cli/menu/apps.d.ts +0 -69
  389. package/dist/cli/menu/apps.js +0 -214
  390. package/dist/cli/menu/data-folder.d.ts +0 -18
  391. package/dist/cli/menu/data-folder.js +0 -99
  392. package/dist/cli/menu/share-settings.d.ts +0 -21
  393. package/dist/cli/menu/share-settings.js +0 -112
  394. package/dist/cli/menu/vpn-login.d.ts +0 -16
  395. package/dist/cli/menu/vpn-login.js +0 -74
  396. package/dist/web/main-NRRS5O6Z.js +0 -157
  397. /package/dist/cli/{commands/data/collect.d.ts → services/data-collect.d.ts} +0 -0
  398. /package/dist/cli/{commands/data/remove.d.ts → services/data-remove.d.ts} +0 -0
@@ -1,18 +1,20 @@
1
1
  // Section 7: keeping things in shape. Updates (the program and your saved settings), Data & storage (the data folder,
2
2
  // what is stored, clean-up), Health (doctor, repair and this PC's setup together), the logs, and the less common
3
3
  // commands. Each is a category on the left and loads when you open it.
4
- import _ from 'lodash';
5
- import { humanBytes } from '../../../core/disk.js';
6
- import { userDir } from '../../../core/config/index.js';
7
- import { errMsg, fail } from '../../../core/util/index.js';
8
- import { cleanTargetOf } from '../../commands/data/collect.js';
4
+ import { dataPage, showData } from '../../../app/features/data/index.js';
5
+ import { readQuery } from '../../../app/registry.js';
6
+ import { errMsg } from '../../../core/util/index.js';
7
+ import { enginePage, engineLeftovers, engineStatus, } from '../../../app/features/engine/index.js';
8
+ import { healthPage, lastHealth } from '../../../app/features/health/index.js';
9
+ import { containedStatus, showStorage, storagePage, } from '../../../app/features/storage/index.js';
10
+ import { CRASH, listLogs, logsPage } from '../../../app/features/logs/index.js';
11
+ import { updatesPage, updatesStatus } from '../../../app/features/updates/updates.def.js';
12
+ import { webPage, webStatus } from '../../../app/features/web/index.js';
9
13
  import { commandSpec } from '../../manifest.js';
10
- import { ago } from '../../output/index.js';
11
- import { DataService, HealthService, LogsService, MigrateService, NPM_UPDATE_COMMAND, UpdateService, } from '../../services/index.js';
12
- import { describeResult } from '../../commands/maintenance/update.js';
13
14
  import { Text, Theme, } from '../../tui/index.js';
14
15
  import { CommandForm } from '../command-form.js';
15
- import { DataFolderFlow, dataFolderRow } from '../data-folder.js';
16
+ import { MenuOperations } from '../io-menu.js';
17
+ import { menuView } from '../render-view.js';
16
18
  import { ListPage } from './list-page.js';
17
19
  const CATEGORIES = [
18
20
  {
@@ -31,9 +33,22 @@ const CATEGORIES = [
31
33
  description: 'Checks this PC and your settings, repairs problems, and sets up Windows Sandbox.',
32
34
  },
33
35
  { id: 'logs', label: 'Logs', description: "The sandbox's own logs." },
36
+ {
37
+ id: 'web',
38
+ label: 'Web UI',
39
+ description: 'The web UI server on this PC: start it, open it in your browser, stop it.',
40
+ },
41
+ {
42
+ id: 'engine',
43
+ label: 'Engine',
44
+ description: 'What runs your sandbox, and what each engine can do on this PC.',
45
+ },
34
46
  { id: 'more', label: 'More commands', description: 'Less common commands.' },
35
47
  ];
36
48
  const MORE_COMMANDS = ['sync', 'audit', 'config', 'generate', 'exec', 'agent-commands'];
49
+ const NO_LOGS = { folder: '', files: [], crash: false };
50
+ /** Until the status is read: the server counts as not running. */
51
+ const NO_WEB = { running: false, port: null, address: null };
37
52
  /** Plain names for the commands under More commands. */
38
53
  const MORE_LABELS = {
39
54
  sync: 'Copy history back now',
@@ -43,33 +58,21 @@ const MORE_LABELS = {
43
58
  exec: 'Run a command in the sandbox',
44
59
  'agent-commands': 'Agent commands (Beta)',
45
60
  };
46
- const REPAIR_LABEL = {
47
- ok: ['OK', Theme.ok],
48
- fixed: ['FIXED', Theme.accent],
49
- 'needs-you': ['NEEDS YOU', Theme.error],
50
- warn: ['WARNING', Theme.warn],
51
- skipped: ['SKIPPED', Theme.muted],
52
- };
53
- const AREA_LABEL = {
54
- tools: 'Toolchains',
55
- databases: 'Databases',
56
- state: 'Agent state',
57
- logs: 'Logs',
58
- data: 'Other',
59
- host: 'On this PC',
60
- };
61
+ /** The Data & storage category's rows and keys once measured: the data view (src/app/features/data) as menu rows. */
62
+ function dataMenuView(data) {
63
+ return data ? menuView(dataPage(data), { category: 'data', prefix: 'data:', refresh: false }) : undefined;
64
+ }
65
+ /** The storage view (the data folder, contained storage) as menu rows, drawn above the data rows. */
66
+ function storageMenu(s) {
67
+ return s
68
+ ? menuView(storagePage(s.show, s.contained), { category: 'data', prefix: 'storage:', refresh: false })
69
+ : undefined;
70
+ }
61
71
  function dataItems(m) {
62
- const folder = { value: 'data:folder', category: 'data', section: 'Data folder', ...m.dataFolder };
63
- const clean = {
64
- value: 'data:clean',
65
- label: 'Clean up…',
66
- category: 'data',
67
- section: 'Clean up',
68
- description: 'Delete caches, old sandboxes and other leftovers (the clean command, with its options).',
69
- };
72
+ const storage = storageMenu(m.storage)?.items ?? [];
70
73
  if (!m.data)
71
74
  return [
72
- folder,
75
+ ...storage,
73
76
  {
74
77
  value: 'data:measure',
75
78
  label: 'Measure what is stored',
@@ -77,157 +80,46 @@ function dataItems(m) {
77
80
  section: 'Stored data',
78
81
  description: 'Adds up the size of every folder. A big data folder takes a minute or more.',
79
82
  },
80
- clean,
81
83
  ];
82
- return [
83
- folder,
84
- ...m.data.disks.map((d) => ({
85
- value: 'disk:' + d.drive,
86
- label: d.drive,
87
- category: 'data',
88
- section: 'Disks',
89
- hint: humanBytes(d.usedBytes) + ' used · ' + humanBytes(d.freeBytes) + ' free of ' + humanBytes(d.totalBytes),
90
- description: d.holdsStorage ? 'The sandbox keeps its files on this drive.' : undefined,
91
- })),
92
- ...m.data.entries.map((e, i) => ({
93
- value: 'entry:' + i,
94
- label: e.name,
95
- category: 'data',
96
- section: AREA_LABEL[e.area],
97
- hint: humanBytes(e.bytes),
98
- description: cleanTargetOf(e) ? 'x deletes it.' : 'Kept on this PC on purpose; it is not deleted here.',
99
- details: [Theme.muted('Folder ') + e.path],
100
- })),
101
- clean,
102
- ];
84
+ return [...storage, ...(dataMenuView(m.data)?.items ?? [])];
103
85
  }
104
- function updateItems(m) {
105
- const row = (id, label, hint, description, details) => ({
106
- value: 'updates:' + id,
107
- label,
108
- category: 'updates',
109
- section: 'This program',
110
- hint,
111
- description,
112
- details,
113
- });
114
- if ('error' in m.updates)
115
- return [row('error', m.updates.error, '')];
116
- const u = m.updates;
117
- if (!u.managed)
118
- return [
119
- row('version', 'Version', u.version, 'Installed with npm, so npm updates it: ' + NPM_UPDATE_COMMAND),
120
- row('npm', 'Update', Theme.muted('with npm'), 'Run ' + NPM_UPDATE_COMMAND + ' in a terminal.'),
121
- ];
122
- const scope = u.scope === 'machine' ? 'for everyone on this PC' : 'for you';
123
- const rows = [
124
- row('version', 'Version', u.version, 'Installed ' + scope + '.'),
125
- row('latest', 'Newest version', m.latest ? Theme.warn(m.latest) : Theme.muted('not looked yet'), m.latest ? 'Enter installs it.' : 'Enter looks on GitHub for a newer version.'),
126
- row('auto', 'Automatic updates', u.autoUpdate ? Theme.ok('on') : Theme.muted('off'), 'Enter switches them. ' +
127
- (u.scope === 'machine'
128
- ? 'For everyone: needs an admin terminal.'
129
- : 'A new version installs when no sandbox runs.')),
130
- ];
131
- if (u.autoOffText)
132
- rows.push(row('autooff', 'Automatic updates turned off', Theme.warn('off'), u.autoOffText, [u.autoOffText]));
133
- if (u.state.previous)
134
- rows.push(row('previous', 'Previous version', u.state.previous, 'Enter goes back to it.'));
135
- if (u.state.failed)
136
- rows.push(row('failed', 'Last failed update', Theme.error(u.state.failed.version), u.state.failed.reason, [
137
- u.state.failed.reason,
138
- 'It is not tried again until a newer version is released.',
139
- ]));
140
- return rows;
86
+ /** The Updates view (src/app/features/updates) as the menu's rows, keys and Enter labels, for the Updates category. */
87
+ function updatesMenu(m) {
88
+ return menuView(updatesPage(m.updates.status, m.updates.latest), { category: 'updates', refresh: false });
89
+ }
90
+ /** A value the page shows before the first read (the status is read when the page loads). */
91
+ const NO_UPDATES = {
92
+ managed: false,
93
+ version: '',
94
+ scope: null,
95
+ autoUpdate: false,
96
+ autoOffText: null,
97
+ previous: null,
98
+ failed: null,
99
+ npmCommand: '',
100
+ error: null,
101
+ settings: { pending: [], error: null },
102
+ };
103
+ /**
104
+ * The logs view's rows and keys for the Logs category (its own empty row is left out: the category shows the page's
105
+ * "Nothing here yet.").
106
+ */
107
+ function logsMenu(files) {
108
+ return menuView({ ...logsPage(files), empty: undefined }, { category: 'logs', refresh: false });
109
+ }
110
+ /** The Web UI view (src/app/features/web) as the menu's rows, keys and Enter labels, for the Web UI category. */
111
+ function webMenu(m) {
112
+ return menuView(webPage(m.web), { category: 'web', prefix: 'web:', refresh: false });
141
113
  }
142
114
  /** The list for the maintenance tab. Pure. */
143
115
  export function maintenanceOptions(m) {
144
- const logs = m.logs.map((f) => ({
145
- value: 'log:' + f.name,
146
- label: f.name,
147
- category: 'logs',
148
- hint: humanBytes(f.bytes) + ' ' + ago(f.modified),
149
- description: 'Enter shows the end of this log in the terminal.',
150
- }));
151
- // The checks take seconds (programs are run), so they are not made while you move through the categories.
152
- const check = m.doctor === null && m.setup === null
153
- ? [
154
- {
155
- value: 'health:check',
156
- label: 'Check this PC',
157
- category: 'health',
158
- section: 'Doctor',
159
- description: 'Looks at this PC: Windows features, the install, the folders and the sandbox. It takes a few seconds, so it only runs when you ask.',
160
- },
161
- ]
162
- : [];
163
- const doctor = (m.doctor ?? []).map(([name, detail], i) => ({
164
- value: 'doctor:' + i,
165
- label: name,
166
- category: 'health',
167
- section: 'Doctor',
168
- hint: Text.truncate(detail, 50),
169
- details: [detail],
170
- }));
171
- const repair = m.repair
172
- ? m.repair.map((r, i) => {
173
- const [text, paint] = REPAIR_LABEL[r.status];
174
- return {
175
- value: 'repair:' + i,
176
- label: r.name,
177
- category: 'health',
178
- section: 'Repair',
179
- hint: paint(text),
180
- description: r.detail,
181
- };
182
- })
183
- : [
184
- {
185
- value: 'repair:run',
186
- label: 'Run the checks and fix what can be fixed',
187
- category: 'health',
188
- section: 'Repair',
189
- description: 'Checks the install, data folder, settings and sandbox, lists what it would change, and asks before it fixes anything.',
190
- },
191
- ];
192
- const setup = m.setup === null
193
- ? []
194
- : 'error' in m.setup
195
- ? [{ value: 'setup:error', label: m.setup.error, category: 'health', section: 'Set up this PC' }]
196
- : m.setup.checks.map((c, i) => ({
197
- value: 'setup:' + i,
198
- label: c.name,
199
- category: 'health',
200
- section: 'Set up this PC',
201
- hint: c.status === 'ok'
202
- ? Theme.ok('OK')
203
- : c.status === 'warn'
204
- ? Theme.warn('WARNING')
205
- : Theme.error('ACTION NEEDED'),
206
- description: c.detail,
207
- }));
208
- const settings = 'error' in m.settings
209
- ? [{ value: 'settings:error', label: m.settings.error, category: 'updates', section: 'Saved settings' }]
210
- : m.settings.pending.length
211
- ? m.settings.pending.map((line, i) => ({
212
- value: 'settings:' + i,
213
- label: line.split(':')[0],
214
- category: 'updates',
215
- section: 'Saved settings',
216
- hint: Theme.warn('to update'),
217
- description: 'Enter updates your saved settings (a backup is kept first).',
218
- details: [line],
219
- }))
220
- : [
221
- {
222
- value: 'settings:none',
223
- label: 'Up to date',
224
- category: 'updates',
225
- section: 'Saved settings',
226
- hint: Theme.ok('OK'),
227
- description: 'Nothing to update.',
228
- },
229
- ];
230
- const updates = updateItems(m);
116
+ // The logs rows have no section heading of their own: the category is their heading.
117
+ const logs = logsMenu(m.logs).items.map(({ section: _section, ...row }) => row);
118
+ // The checks take seconds (programs are run), so they only run when you ask: the page shows the saved results.
119
+ const health = healthMenu(m);
120
+ const updates = updatesMenu(m);
121
+ const engine = engineMenu(m);
122
+ const web = webMenu(m);
231
123
  const more = m.more.map((c) => ({
232
124
  value: 'more:' + c.name,
233
125
  label: MORE_LABELS[c.name] ?? Text.humanize(c.name),
@@ -237,32 +129,53 @@ export function maintenanceOptions(m) {
237
129
  return {
238
130
  path: [],
239
131
  categories: CATEGORIES,
240
- items: [...updates, ...settings, ...dataItems(m), ...check, ...doctor, ...repair, ...setup, ...logs, ...more],
132
+ items: [...updates.items, ...dataItems(m), ...health.items, ...logs, ...web.items, ...engine.items, ...more],
241
133
  empty: 'Nothing here yet.',
242
134
  actions: maintenanceActions(m),
243
- enterLabel: (_category, id) => enterLabel(id ?? '', m),
135
+ enterLabel: (category, id) => category === 'health'
136
+ ? health.enterLabel(category, id)
137
+ : category === 'updates'
138
+ ? updates.enterLabel(category, id)
139
+ : category === 'engine'
140
+ ? engine.enterLabel(category, id)
141
+ : category === 'web'
142
+ ? web.enterLabel(category, id)
143
+ : (category === 'data'
144
+ ? storageMenu(m.storage)?.enterLabel(category, id) || dataMenuView(m.data)?.enterLabel(category, id)
145
+ : undefined) || enterLabel(id ?? ''),
244
146
  };
245
147
  }
148
+ /**
149
+ * The Engine category: before the first read, one row that reads the engines on Enter (the checks run programs, so
150
+ * they are not made just by opening the category); after it, the engine view's rows.
151
+ */
152
+ function engineMenu(m) {
153
+ if (!m.engine) {
154
+ const load = {
155
+ value: 'engine:load',
156
+ label: 'Look at the engines',
157
+ category: 'engine',
158
+ section: 'Engines',
159
+ description: 'Checks what each engine needs on this PC. It runs a few Windows programs, so it only runs when asked.',
160
+ };
161
+ return { items: [load], actions: [], enterLabel: () => 'check', resolve: () => undefined };
162
+ }
163
+ return menuView(enginePage(m.engine, m.leftovers ?? undefined), {
164
+ category: 'engine',
165
+ prefix: 'engine:',
166
+ refresh: false,
167
+ });
168
+ }
169
+ /** The Health category: the health view's rows and keys, drawn only while that category is open. */
170
+ function healthMenu(m) {
171
+ return menuView(healthPage({ last: m.health.last }), { category: 'health', refresh: false });
172
+ }
246
173
  /** What Enter does on a row ("" = the row only shows information). */
247
- function enterLabel(id, m) {
248
- if (id === 'data:folder')
249
- return 'change';
174
+ function enterLabel(id) {
250
175
  if (id === 'data:measure')
251
176
  return 'measure';
252
- if (id === 'data:clean')
253
- return 'choose what';
254
- if (id === 'repair:run')
255
- return 'check and fix';
256
- if (id === 'health:check')
177
+ if (id === 'engine:load')
257
178
  return 'check';
258
- if (id === 'updates:latest')
259
- return m.latest ? 'install' : 'look';
260
- if (id === 'updates:auto')
261
- return 'switch';
262
- if (id === 'updates:previous')
263
- return 'go back';
264
- if (id.startsWith('settings:') && id !== 'settings:none' && id !== 'settings:error')
265
- return 'update';
266
179
  if (id.startsWith('log:'))
267
180
  return 'show';
268
181
  if (id.startsWith('more:'))
@@ -272,48 +185,22 @@ function enterLabel(id, m) {
272
185
  function maintenanceActions(m) {
273
186
  return (category) => {
274
187
  switch (category) {
275
- case 'data': {
276
- const deletable = new Set((m.data?.entries ?? []).flatMap((e, i) => (cleanTargetOf(e) ? ['entry:' + i] : [])));
188
+ case 'data':
277
189
  return [
278
190
  { key: 'm', label: 'measure again' },
279
- { key: 'x', label: 'delete', applies: (id) => deletable.has(id) },
191
+ ...(storageMenu(m.storage)?.actions ?? []),
192
+ ...(dataMenuView(m.data)?.actions ?? []),
280
193
  ];
281
- }
282
194
  case 'logs':
283
- return [{ key: 'f', label: 'follow in the terminal' }];
284
- case 'health': {
285
- const canEnable = m.setup !== null && !('error' in m.setup) && m.setup.canEnable;
286
- return [
287
- { key: 'f', label: 'check and fix' },
288
- { key: 'l', label: m.doctor ? 'check again' : 'check' },
289
- ...(canEnable ? [{ key: 'e', label: 'turn on Windows Sandbox' }] : []),
290
- ];
291
- }
292
- case 'updates': {
293
- const managed = !('error' in m.updates) && m.updates.managed;
294
- const previous = !('error' in m.updates) && Boolean(m.updates.state.previous);
295
- return [
296
- ...(managed
297
- ? [
298
- { key: 'c', label: 'check now' },
299
- { key: 'u', label: 'update now' },
300
- { key: 'a', label: 'automatic on/off', applies: (id) => id === 'updates:auto' },
301
- ...(previous
302
- ? [{ key: 'r', label: 'roll back', applies: (id) => id === 'updates:previous' }]
303
- : []),
304
- ]
305
- : []),
306
- ...('pending' in m.settings && m.settings.pending.length
307
- ? [
308
- {
309
- key: 's',
310
- label: 'update saved settings',
311
- applies: (id) => id.startsWith('settings:') && id !== 'settings:none',
312
- },
313
- ]
314
- : []),
315
- ];
316
- }
195
+ return logsMenu(m.logs).actions;
196
+ case 'health':
197
+ return healthMenu(m).actions;
198
+ case 'updates':
199
+ return updatesMenu(m).actions;
200
+ case 'engine':
201
+ return [{ key: 'l', label: m.engine ? 'check again' : 'check' }, ...engineMenu(m).actions];
202
+ case 'web':
203
+ return webMenu(m).actions;
317
204
  default:
318
205
  return [];
319
206
  }
@@ -323,181 +210,122 @@ export class MaintenancePage extends ListPage {
323
210
  ctx;
324
211
  label = 'Maintenance';
325
212
  data = null;
326
- logs = [];
327
- doctorRows = null;
328
- repairRows = null;
329
- setupReport = null;
330
- settings = { pending: [] };
331
- migrate;
332
- health;
333
- dataService;
334
- updateService;
213
+ storage = null;
214
+ logs = NO_LOGS;
215
+ last = null;
216
+ updates = NO_UPDATES;
217
+ engine = null;
218
+ leftovers = null;
219
+ web = NO_WEB;
220
+ operations;
221
+ /** The newer version the last check found (null: none, or not looked yet). */
335
222
  latest = null;
336
223
  constructor(app, ctx) {
337
224
  super(app);
338
225
  this.ctx = ctx;
339
- this.updateService = UpdateService.forPackage(ctx.packageRoot, undefined, { userDir: userDir() });
340
- this.health = new HealthService(ctx);
341
- this.migrate = new MigrateService({
342
- storageRoot: ctx.storageRoot,
343
- userDir: userDir(),
344
- configPath: ctx.loaded.configPath,
345
- });
346
- this.dataService = new DataService(ctx);
226
+ this.operations = new MenuOperations(app, ctx);
347
227
  }
348
228
  model() {
349
229
  return {
350
- dataFolder: dataFolderRow(this.ctx),
230
+ storage: this.storage,
351
231
  data: this.data,
352
232
  logs: this.logs,
353
- doctor: this.doctorRows,
354
- repair: this.repairRows,
355
- setup: this.setupReport,
356
- settings: this.settings,
357
- updates: this.updateStatus(),
358
- latest: this.latest,
233
+ health: { last: this.last },
234
+ updates: { status: this.updates, latest: this.latest },
235
+ engine: this.engine,
236
+ leftovers: this.leftovers,
237
+ web: this.web,
359
238
  more: MORE_COMMANDS.flatMap((name) => {
360
239
  const spec = commandSpec(name);
361
240
  return spec ? [{ name, summary: spec.summary }] : [];
362
241
  }),
363
242
  };
364
243
  }
365
- load() {
244
+ async load() {
366
245
  try {
367
- this.logs = new LogsService(this.ctx).files();
246
+ this.storage = {
247
+ show: await readQuery(showStorage, this.ctx, { measure: false }),
248
+ contained: await readQuery(containedStatus, this.ctx),
249
+ };
368
250
  }
369
251
  catch {
370
- this.logs = [];
252
+ this.storage = null;
371
253
  }
372
- this.readSettings();
373
- return Promise.resolve();
374
- }
375
- options() {
376
- return maintenanceOptions(this.model());
377
- }
378
- readSettings() {
379
- try {
380
- this.settings = { pending: this.migrate.plan() };
381
- }
382
- catch (e) {
383
- this.settings = { error: errMsg(e) };
384
- }
385
- }
386
- updateStatus() {
387
254
  try {
388
- return this.updateService.status();
255
+ this.logs = await readQuery(listLogs, this.ctx);
389
256
  }
390
- catch (e) {
391
- return { error: errMsg(e) };
392
- }
393
- }
394
- /** The Updates category: look, install, go back, switch automatic updates. */
395
- async updates(key) {
396
- const service = this.updateService;
397
- if (!service.managed)
398
- return { notice: Theme.muted('Installed with npm: update it with ' + NPM_UPDATE_COMMAND + '.') };
399
- if (key === 'a') {
400
- const on = !service.status().autoUpdate;
401
- this.busy(on ? 'Turning automatic updates on…' : 'Turning automatic updates off…');
402
- service.setAuto(on);
403
- return { notice: Theme.ok(Theme.glyph.ok + ' Automatic updates are ' + (on ? 'on.' : 'off.')) };
404
- }
405
- if (key === 'r') {
406
- const target = service.status().state.previous;
407
- if (!target)
408
- return { notice: Theme.muted('There is no earlier version to go back to.') };
409
- if (!(await this.app.ask('Go back to version ' + target + '?', ['Takes effect the next time you start it.'], 'Go back')))
410
- return {};
411
- return { notice: Theme.ok(Theme.glyph.ok + ' Went back to version ' + service.rollback() + '.') };
412
- }
413
- if (key === 'c') {
414
- this.busy('Looking for a newer version…');
415
- const found = await service.check();
416
- this.latest = found.available && found.latest ? found.latest.version : null;
417
- return {
418
- notice: this.latest
419
- ? Theme.warn('Version ' + this.latest + ' is available.')
420
- : Theme.ok(Theme.glyph.ok + ' You have the newest version.'),
421
- };
257
+ catch {
258
+ this.logs = NO_LOGS;
422
259
  }
423
- if (key !== 'u')
424
- return {};
425
- this.busy('Installing the newest version…');
426
- const done = describeResult(await service.run(), false);
427
- this.latest = null;
428
- return {
429
- notice: done.code === 0 ? Theme.ok(Theme.glyph.ok + ' ' + done.line) : Theme.error(Theme.glyph.cross + ' ' + done.line),
430
- };
431
- }
432
- async updateSettings() {
433
- if (!('pending' in this.settings) || !this.settings.pending.length)
434
- return { notice: Theme.muted('Your saved settings are up to date.') };
435
- this.busy('Updating your saved settings…');
436
- const notes = [];
437
- const result = await this.migrate.run((line) => notes.push(line));
438
- this.readSettings();
439
- return {
440
- notice: result.postponed.length
441
- ? Theme.warn('Waiting until no sandbox runs: ' + result.postponed.join(', ') + '.')
442
- : Theme.ok(Theme.glyph.ok + ' Saved settings updated.'),
443
- };
444
- }
445
- async loadDoctor() {
446
- this.busy('Checking this PC…');
260
+ this.last = (await readQuery(lastHealth, this.ctx)).last;
261
+ this.updates = await readQuery(updatesStatus, this.ctx);
447
262
  try {
448
- this.doctorRows = this.health.doctor();
263
+ this.web = await readQuery(webStatus, this.ctx);
449
264
  }
450
- catch (e) {
451
- this.doctorRows = [['Doctor', errMsg(e)]];
265
+ catch {
266
+ // Not on this PC (a sandbox cannot start the web UI): the category shows it as not running.
267
+ this.web = NO_WEB;
452
268
  }
453
269
  }
454
- loadSetup() {
455
- this.busy('Looking at this PC…');
456
- try {
457
- this.health.assertWindows();
458
- this.setupReport = this.health.setupReport();
459
- }
460
- catch (e) {
461
- this.setupReport = { error: errMsg(e) };
462
- }
463
- return Promise.resolve();
270
+ options() {
271
+ return maintenanceOptions(this.model());
464
272
  }
465
273
  async act(result) {
466
274
  const id = result.kind === 'pick' || result.kind === 'action' ? result.value : undefined;
467
275
  const key = result.kind === 'action' ? result.key : undefined;
468
276
  try {
469
- if (key === 'm' || id === 'data:measure')
470
- return this.measure();
471
- if (id === 'data:folder' && result.kind === 'pick') {
472
- const exit = await new DataFolderFlow(this.app, this.ctx).run();
473
- return exit ? { exit } : { reload: true };
277
+ // The open category's rows and keys only (a key of one category must not fire in another).
278
+ if (this.category === 'data') {
279
+ const stored = storageMenu(this.storage)?.resolve(result);
280
+ if (stored?.kind === 'action')
281
+ return await this.dataAction(stored.action);
282
+ const chosen = dataMenuView(this.data)?.resolve(result);
283
+ if (chosen?.kind === 'action')
284
+ return await this.dataAction(chosen.action);
474
285
  }
475
- if (id === 'data:clean' && result.kind === 'pick') {
476
- const argv = await new CommandForm(this.app, this.ctx, commandSpec('clean')).run();
477
- return argv ? { exit: { kind: 'run', argv } } : {};
286
+ // The Engine category reads the engines on Enter on its row, or on l (check again) once they were read.
287
+ if (this.category === 'engine' && ((result.kind === 'pick' && id === 'engine:load') || key === 'l')) {
288
+ this.busy('Checking the engines…');
289
+ this.engine = await readQuery(engineStatus, this.ctx);
290
+ this.leftovers = await readQuery(engineLeftovers, this.ctx);
291
+ return {};
292
+ }
293
+ // Stopping the leftover HCS systems (x): the operation lists them and asks first, then they are looked up again.
294
+ if (this.category === 'engine') {
295
+ const chosen = engineMenu(this.model()).resolve(result);
296
+ if (chosen?.kind !== 'action')
297
+ return {};
298
+ const done = await this.operations.perform(chosen.action, [this.label], (text) => this.busy(text));
299
+ this.leftovers = await readQuery(engineLeftovers, this.ctx);
300
+ return done;
478
301
  }
479
- if (key === 'x')
480
- return await this.remove(id);
481
- if (key === 'f' && this.category === 'logs')
482
- return { exit: { kind: 'run', argv: ['logs', '--follow'] } };
483
- if (id?.startsWith('log:') && result.kind === 'pick') {
484
- const file = this.logs.find((l) => l.name === id.slice(4));
485
- return file ? { exit: { kind: 'view', file: file.path } } : {};
302
+ // The Web UI's keys (s, o, x) only in its category; the operation reports and the page reloads its facts.
303
+ if (this.category === 'web') {
304
+ const chosen = webMenu(this.model()).resolve(result);
305
+ if (chosen?.kind === 'action')
306
+ return await this.operations.perform(chosen.action, [this.label], (text) => this.busy(text));
307
+ return {};
308
+ }
309
+ if (key === 'm' || id === 'data:measure')
310
+ return await this.measure();
311
+ // Only the open category's rows and keys: `f` and Enter mean something else in Health.
312
+ if (this.category === 'logs') {
313
+ const chosen = logsMenu(this.logs).resolve(result);
314
+ if (chosen?.kind === 'action')
315
+ return this.showLog(chosen.action);
316
+ }
317
+ if (this.category === 'updates') {
318
+ const chosen = updatesMenu(this.model()).resolve(result);
319
+ if (chosen?.kind === 'action')
320
+ return await this.runUpdates(chosen.action);
321
+ return {};
486
322
  }
487
- if ((result.kind === 'pick' && id?.startsWith('settings:')) || (key === 's' && this.category === 'updates'))
488
- return await this.updateSettings();
489
- if (this.category === 'updates')
490
- return await this.updates(key ?? this.updateKey(id));
323
+ // Only the open category's rows and keys: a key of one category must not fire in another.
491
324
  if (this.category === 'health') {
492
- if (key === 'f' || id === 'repair:run')
493
- return this.repair();
494
- if (key === 'l' || id === 'health:check') {
495
- await this.loadDoctor();
496
- await this.loadSetup();
497
- return {};
498
- }
499
- if (key === 'e')
500
- return await this.enable();
325
+ const chosen = healthMenu(this.model()).resolve(result);
326
+ if (chosen?.kind === 'action')
327
+ return await this.operations.perform(chosen.action, [this.label], (text) => this.busy(text));
328
+ return {};
501
329
  }
502
330
  if (id?.startsWith('more:') && result.kind === 'pick') {
503
331
  const spec = commandSpec(id.slice(5));
@@ -510,61 +338,52 @@ export class MaintenancePage extends ListPage {
510
338
  }
511
339
  return {};
512
340
  }
513
- /** The key an Enter on an Updates row stands for. */
514
- updateKey(id) {
515
- if (id === 'updates:latest')
516
- return this.latest ? 'u' : 'c';
517
- if (id === 'updates:auto')
518
- return 'a';
519
- if (id === 'updates:previous')
520
- return 'r';
521
- return undefined;
522
- }
523
- measure() {
524
- this.busy('Measuring what is stored… this can take a minute or more');
525
- const report = this.dataService.report();
526
- this.data = { entries: report.entries, disks: report.disks, total: report.totalBytes };
527
- return { notice: Theme.ok(Theme.glyph.ok + ' Total stored: ' + humanBytes(report.totalBytes) + '.') };
528
- }
529
- async remove(id) {
530
- const entry = id?.startsWith('entry:') ? this.data?.entries[Number(id.slice(6))] : undefined;
531
- if (!entry || !this.data)
341
+ /**
342
+ * A Logs row or key: the crash report or the end of a log, on the normal screen (the menu's own view shows a log
343
+ * there). Only names from the listing are looked up, never a path of their own.
344
+ */
345
+ showLog(action) {
346
+ if (action.kind !== 'link')
532
347
  return {};
533
- const target = cleanTargetOf(entry) ?? fail('"' + entry.name + '" is kept on this PC on purpose.', 'It is not deleted here.');
534
- const plan = this.dataService.plan([target]);
535
- if (!plan.present.length)
536
- return { notice: Theme.muted(target + ' does not exist.') };
537
- const ok = await this.app.ask('Delete ' + target + '?', [
538
- humanBytes(plan.totalBytes) + ' in ' + entry.path + '.',
539
- ...plan.present.flatMap((t) => (t.warning ? [t.target + ' ' + t.warning + '.'] : [])),
540
- 'This cannot be undone.',
541
- ], 'Delete', true);
542
- if (!ok)
348
+ const target = action.target;
349
+ if (target.kind === 'command')
350
+ return { exit: { kind: 'run', argv: [...target.argv] } };
351
+ if (target.kind !== 'view')
543
352
  return {};
544
- const outcome = this.dataService.clean(plan);
545
- if (outcome.failed.length)
546
- return { notice: Theme.error(Theme.glyph.cross + ' Could not remove everything: ' + outcome.failed.join('; ')) };
547
- const gone = this.data.entries.filter((e) => e !== entry);
548
- this.data = { ...this.data, entries: gone, total: _.sumBy(gone, 'bytes') };
549
- return { notice: Theme.ok(Theme.glyph.ok + ' Freed ' + humanBytes(outcome.freedBytes) + '.') };
353
+ const name = target.params?.name;
354
+ if (name === CRASH)
355
+ return { exit: { kind: 'run', argv: ['logs', '--crash'] } };
356
+ const file = this.logs.files.find((f) => f.name === name);
357
+ return file ? { exit: { kind: 'view', file: file.path } } : {};
550
358
  }
551
- /** Repair shows what it does as it goes and asks before it changes anything, so it runs on the normal screen. */
552
- repair() {
553
- return { exit: { kind: 'run', argv: ['repair'] } };
359
+ /**
360
+ * An Updates row or key: runs its operation as the command line does (the operation asks what it must). A check and
361
+ * an install tell the page the newer version they found (or that none is left), the rest leave it alone.
362
+ */
363
+ runUpdates(action) {
364
+ const input = action.kind === 'operation' ? action.input : {};
365
+ const checks = input.check === true;
366
+ const installs = !checks && input.rollback === undefined && input.auto === undefined;
367
+ const remember = checks || installs
368
+ ? (output) => {
369
+ this.latest = output.latest;
370
+ }
371
+ : undefined;
372
+ return this.operations.perform(action, [this.label], (text) => this.busy(text), remember);
554
373
  }
555
- async enable() {
556
- const ok = await this.app.ask('Turn on Windows Sandbox?', ['Windows asks for admin permission, then you restart Windows to finish.'], 'Turn on');
557
- if (!ok)
558
- return {};
559
- // The admin prompt and its output need the normal screen.
560
- this.app.terminal.stop();
561
- const worked = this.health.enableFeature();
562
- this.app.terminal.start();
563
- await this.loadSetup();
564
- return {
565
- notice: worked
566
- ? Theme.ok(Theme.glyph.ok + ' Done. Restart Windows to finish.')
567
- : Theme.error(Theme.glyph.cross + ' Windows Sandbox could not be turned on (or the admin prompt was declined).'),
568
- };
374
+ async measure() {
375
+ this.busy('Measuring what is stored… this can take a minute or more');
376
+ this.data = await readQuery(showData, this.ctx);
377
+ return { notice: Theme.ok(Theme.glyph.ok + ' Total stored: ' + this.data.total + '.') };
378
+ }
379
+ /**
380
+ * Delete one stored folder, or clean up: the operation asks and reports as on the command line. After it ran, the
381
+ * sizes shown are out of date, so the list goes back to "Measure what is stored" (a declined question keeps it).
382
+ */
383
+ async dataAction(action) {
384
+ const outcome = await this.operations.perform(action, [this.label], (text) => this.busy(text));
385
+ if (outcome.reload)
386
+ this.data = null;
387
+ return outcome;
569
388
  }
570
389
  }