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,6 +1,7 @@
1
- import type { Context } from '../../commands/context.js';
2
- import { type App, type Category, type ListAction, type ListOptions, type ListResult } from '../../tui/index.js';
1
+ import { type AgentsList } from '../../../app/features/agents/index.js';
3
2
  import type { ConfigFile } from '../../../app/features/settings/index.js';
3
+ import type { Context } from '../../commands/context.js';
4
+ import { type App, type Category, type ListOptions, type ListResult } from '../../tui/index.js';
4
5
  import type { Draft } from '../settings/draft.js';
5
6
  import { ListPage, type Outcome } from './list-page.js';
6
7
  export type WindowChoice = 'config' | 'hide' | 'show';
@@ -19,10 +20,8 @@ export interface AgentsModel {
19
20
  /** The agents you added yourself: only these can be removed (the built-in ones stay). */
20
21
  readonly removable?: readonly string[];
21
22
  }
22
- /** The agents of `harnesses` that are not built in. Pure. */
23
- export declare function addedAgents(names: readonly string[]): string[];
24
- /** The keys of the Start category: make an agent the default, remove one you added. Pure. */
25
- export declare function agentActions(m: Pick<AgentsModel, 'defaultAgent' | 'removable'>): ListAction<string>[];
23
+ /** The agents page's data as the agents view takes it. Pure. */
24
+ export declare function agentsList(m: AgentsModel): AgentsList;
26
25
  /**
27
26
  * The "not sandboxed" entries: only while agent commands are on (typing the agent then starts the sandbox, so a way to
28
27
  * run the real one is needed), one per agent found installed on this PC.
@@ -32,37 +31,37 @@ export declare function hostAgentsToShow(enabled: boolean, found: readonly {
32
31
  }[]): string[];
33
32
  /** The command line a start stands for. Pure. */
34
33
  export declare function startArgv(m: AgentsModel, agent: string, cwd: string): string[];
34
+ /** Settings has unsaved changes to the agents (their list or the default): the agent operations wait for them. */
35
+ export declare function agentsHaveStagedChanges(file: ConfigFile, draft: Draft): boolean;
35
36
  /** The categories of the agents section; "On this PC" only when there is something in it. Pure. */
36
37
  export declare function agentCategories(m: Pick<AgentsModel, 'folder' | 'hostAgents'>): Category[];
37
38
  /** The list for the agents section. Pure. */
38
39
  export declare function agentsOptions(m: AgentsModel, cwd?: string): ListOptions<string>;
39
40
  export declare class AgentsPage extends ListPage<string> {
40
41
  private readonly ctx;
41
- /** Your own config file and its unsaved changes: where "Add an agent" writes to. */
42
+ /** Your own config file: the default agent's name for where the cursor starts. */
42
43
  private readonly file;
44
+ /** Unsaved changes in Settings: while there are some to the agents, the agent operations wait for them. */
43
45
  private readonly draft;
44
46
  readonly label = "Agents";
45
47
  private attach;
46
48
  private window;
47
49
  private folder;
48
50
  private model;
49
- /** What load() looked up (programs on PATH, the history file): not asked again for every list that is built. */
51
+ private readonly operations;
52
+ /** What load() looked up: the agents view, programs on PATH, the history file. */
50
53
  private facts;
51
54
  constructor(app: App, ctx: Context,
52
- /** Your own config file and its unsaved changes: where "Add an agent" writes to. */
53
- file: ConfigFile, draft: Draft);
54
- /** Every agent: the configured ones, and any added in Settings that is not saved yet. */
55
- private harnesses;
56
- /** Is this agent already in the sandbox's persistent storage (so starting it needs no install)? */
57
- private installed;
55
+ /** Your own config file: the default agent's name for where the cursor starts. */
56
+ file: ConfigFile,
57
+ /** Unsaved changes in Settings: while there are some to the agents, the agent operations wait for them. */
58
+ draft: Draft);
58
59
  private current;
59
60
  /** The cursor starts on the default agent. */
60
61
  protected initialFocus(): string;
61
62
  protected load(): Promise<void>;
62
63
  protected options(): ListOptions<string>;
63
64
  protected act(result: ListResult<string>): Promise<Outcome<string>>;
64
- /** Make default (d) and Remove (x) on the highlighted agent. */
65
- private action;
66
- /** "Add an agent…": the new agent shows in the list, with the cursor on it. */
67
- private add;
65
+ /** Add, make default and remove run through the menu's dialogs, unless Settings has unsaved changes to the agents. */
66
+ private operate;
68
67
  }
@@ -1,39 +1,38 @@
1
1
  // Section 1: the agents. "Start" lists the agents you can start here (Enter starts one) after "Repeat last", and ends
2
- // with "Add an agent…" (the new one appears in the list); "Options" holds how a start goes (attach, window, folder) and
3
- // the other ways to start; "On this PC" has the not-sandboxed entries while agent commands are on.
2
+ // with "Add an agent…" (the agents view's rows and keys: src/app/features/agents); "Options" holds how a start goes
3
+ // (attach, window, folder) and the other ways to start; "On this PC" has the not-sandboxed entries while agent commands
4
+ // are on.
4
5
  import fs from 'node:fs';
5
6
  import path from 'node:path';
6
- import { DEFAULTS } from '../../../core/config/index.js';
7
- import { errMsg } from '../../../core/util/index.js';
7
+ import { agentsPage, listAgents } from '../../../app/features/agents/index.js';
8
+ import { readQuery } from '../../../app/registry.js';
8
9
  import { commandLine, readHistory } from '../../history.js';
9
10
  import { AgentCommandsService } from '../../services/index.js';
10
11
  import { commandSpec } from '../../manifest.js';
11
12
  import { Text, Theme, } from '../../tui/index.js';
12
- import { AddAgent, makeDefaultAgent, removeAgent } from '../add-agent.js';
13
13
  import { CommandForm } from '../command-form.js';
14
+ import { MenuOperations } from '../io-menu.js';
15
+ import { menuView } from '../render-view.js';
14
16
  import { askPath } from '../settings/ask-path.js';
15
17
  import { SeveralAgents } from '../several.js';
16
18
  import { ListPage } from './list-page.js';
17
- /** The agents of `harnesses` that are not built in. Pure. */
18
- export function addedAgents(names) {
19
- return names.filter((n) => !(n in DEFAULTS.harnesses));
20
- }
21
- const agentOf = (id) => (id.startsWith('agent:') ? id.slice(6) : undefined);
22
- /** The keys of the Start category: make an agent the default, remove one you added. Pure. */
23
- export function agentActions(m) {
24
- const isOther = (id) => {
25
- const name = agentOf(id);
26
- return name !== undefined && name !== m.defaultAgent;
19
+ /** The agents page's data as the agents view takes it. Pure. */
20
+ export function agentsList(m) {
21
+ const removable = m.removable ?? [];
22
+ return {
23
+ folder: m.folder,
24
+ defaultAgent: m.defaultAgent,
25
+ agents: m.agents.map((a) => ({
26
+ name: a.name,
27
+ ready: a.ready,
28
+ isDefault: a.name === m.defaultAgent,
29
+ removable: removable.includes(a.name),
30
+ })),
27
31
  };
28
- return [
29
- { key: 'd', label: 'make default', importance: 1, applies: isOther },
30
- {
31
- key: 'x',
32
- label: 'remove',
33
- importance: 1,
34
- applies: (id) => isOther(id) && (m.removable ?? []).includes(agentOf(id) ?? ''),
35
- },
36
- ];
32
+ }
33
+ /** The Start category: the agents view's rows and keys, as the menu draws them. Pure. */
34
+ function startView(m) {
35
+ return menuView(agentsPage(agentsList(m)), { category: 'start', refresh: false });
37
36
  }
38
37
  /**
39
38
  * The "not sandboxed" entries: only while agent commands are on (typing the agent then starts the sandbox, so a way to
@@ -54,6 +53,10 @@ export function startArgv(m, agent, cwd) {
54
53
  ...(path.resolve(m.folder).toLowerCase() === path.resolve(cwd).toLowerCase() ? [] : ['--workspace', m.folder]),
55
54
  ];
56
55
  }
56
+ /** Settings has unsaved changes to the agents (their list or the default): the agent operations wait for them. */
57
+ export function agentsHaveStagedChanges(file, draft) {
58
+ return draft.has(file, ['harnesses']) || draft.has(file, ['defaultHarness']);
59
+ }
57
60
  /** The categories of the agents section; "On this PC" only when there is something in it. Pure. */
58
61
  export function agentCategories(m) {
59
62
  return [
@@ -74,10 +77,20 @@ export function agentCategories(m) {
74
77
  : []),
75
78
  ];
76
79
  }
80
+ /** A Start row of the view with the menu's own lines: the default's accent and the command a start runs. Pure. */
81
+ function startRow(item, m, cwd) {
82
+ const name = item.value.startsWith('agent:') ? item.value.slice(6) : undefined;
83
+ if (name === undefined)
84
+ return item;
85
+ return {
86
+ ...item,
87
+ hint: (name === m.defaultAgent ? Theme.accent('default ') : '') + (item.hint ?? ''),
88
+ details: [Theme.muted('Command ') + commandLine(startArgv(m, name, cwd))],
89
+ };
90
+ }
77
91
  /** The list for the agents section. Pure. */
78
92
  export function agentsOptions(m, cwd = process.cwd()) {
79
- const section = 'Start in ' + path.basename(m.folder);
80
- const agents = [...m.agents].sort((a, b) => Number(b.name === m.defaultAgent) - Number(a.name === m.defaultAgent));
93
+ const view = startView(m);
81
94
  return {
82
95
  path: [],
83
96
  categories: agentCategories(m),
@@ -94,33 +107,7 @@ export function agentsOptions(m, cwd = process.cwd()) {
94
107
  },
95
108
  ]
96
109
  : []),
97
- ...agents.map((a) => ({
98
- value: 'agent:' + a.name,
99
- label: a.name,
100
- category: 'start',
101
- section,
102
- hint: (a.name === m.defaultAgent ? Theme.accent('default ') : '') +
103
- (a.ready ? Theme.ok('ready') : Theme.muted('installs on first start')),
104
- description: 'Starts ' +
105
- a.name +
106
- ' in ' +
107
- path.basename(m.folder) +
108
- '.' +
109
- (a.name === m.defaultAgent ? ' It is the default agent.' : '') +
110
- ((m.removable ?? []).includes(a.name) && a.name !== m.defaultAgent
111
- ? ' You added it: x removes it.'
112
- : (m.removable ?? []).includes(a.name)
113
- ? ' You added it. Make another agent the default to remove it.'
114
- : ''),
115
- details: [Theme.muted('Command ') + commandLine(startArgv(m, a.name, cwd))],
116
- })),
117
- {
118
- value: 'add',
119
- label: 'Add an agent…',
120
- category: 'start',
121
- section,
122
- description: 'Add another agent by its npm or Python package, or a program on this PC. It is saved in your settings and appears in this list.',
123
- },
110
+ ...view.items.map((item) => startRow(item, m, cwd)),
124
111
  {
125
112
  value: 'opt:attach',
126
113
  label: 'Attach this terminal',
@@ -175,7 +162,7 @@ export function agentsOptions(m, cwd = process.cwd()) {
175
162
  })),
176
163
  ],
177
164
  enterLabel: (_category, id) => ENTER[id ?? ''] ?? (id?.startsWith('agent:') ? 'start' : id?.startsWith('host:') ? 'run' : 'open'),
178
- actions: (category) => (category === 'start' ? agentActions(m) : []),
165
+ actions: (category) => (category === 'start' ? view.actions : []),
179
166
  };
180
167
  }
181
168
  /** What Enter does on the rows that are not an agent. */
@@ -195,45 +182,33 @@ export class AgentsPage extends ListPage {
195
182
  window = 'config';
196
183
  folder = process.cwd();
197
184
  model;
198
- /** What load() looked up (programs on PATH, the history file): not asked again for every list that is built. */
185
+ operations;
186
+ /** What load() looked up: the agents view, programs on PATH, the history file. */
199
187
  facts;
200
188
  constructor(app, ctx,
201
- /** Your own config file and its unsaved changes: where "Add an agent" writes to. */
202
- file, draft) {
189
+ /** Your own config file: the default agent's name for where the cursor starts. */
190
+ file,
191
+ /** Unsaved changes in Settings: while there are some to the agents, the agent operations wait for them. */
192
+ draft) {
203
193
  super(app);
204
194
  this.ctx = ctx;
205
195
  this.file = file;
206
196
  this.draft = draft;
207
- }
208
- /** Every agent: the configured ones, and any added in Settings that is not saved yet. */
209
- harnesses() {
210
- const config = this.file.userConfig();
211
- const staged = this.draft.has(this.file, ['harnesses'])
212
- ? this.draft.value(this.file, ['harnesses'])
213
- : undefined;
214
- return { ...config.harnesses, ...staged };
215
- }
216
- /** Is this agent already in the sandbox's persistent storage (so starting it needs no install)? */
217
- installed(name, harnesses) {
218
- const h = harnesses[name];
219
- const dir = path.join(this.ctx.dataPath, 'tools', 'agents', name);
220
- return (Boolean(h) &&
221
- [h.hostExecutable, h.npmBin && h.npmBin + '.cmd', h.uvBin && h.uvBin + '.exe'].some((f) => f && fs.existsSync(path.join(dir, f))));
197
+ this.operations = new MenuOperations(app, ctx);
222
198
  }
223
199
  current() {
224
- const harnesses = this.harnesses();
225
200
  if (!this.facts)
226
201
  throw new Error('The agents tab was not loaded');
227
- const { commands, last } = this.facts;
202
+ const { agents, commands, last } = this.facts;
228
203
  return {
229
204
  folder: this.folder,
230
- defaultAgent: this.file.userConfig().defaultHarness,
231
- agents: Object.keys(harnesses).map((name) => ({ name, ready: this.installed(name, harnesses) })),
205
+ defaultAgent: agents.defaultAgent,
206
+ agents: agents.agents.map((a) => ({ name: a.name, ready: a.ready })),
232
207
  last,
233
208
  attach: this.attach,
234
209
  window: this.window,
235
210
  hostAgents: hostAgentsToShow(commands.enabled, commands.found),
236
- removable: addedAgents(Object.keys(harnesses)),
211
+ removable: agents.agents.filter((a) => a.removable).map((a) => a.name),
237
212
  };
238
213
  }
239
214
  /** The cursor starts on the default agent. */
@@ -242,6 +217,7 @@ export class AgentsPage extends ListPage {
242
217
  }
243
218
  async load() {
244
219
  this.facts = {
220
+ agents: await readQuery(listAgents, this.ctx),
245
221
  commands: await new AgentCommandsService(this.ctx).statusAsync(),
246
222
  last: readHistory()[0]?.argv,
247
223
  };
@@ -252,21 +228,23 @@ export class AgentsPage extends ListPage {
252
228
  return agentsOptions(this.model);
253
229
  }
254
230
  async act(result) {
255
- if (result.kind === 'action')
256
- return this.action(result.key, result.value);
231
+ const model = this.current();
232
+ const chosen = startView(model).resolve(result);
233
+ if (chosen?.kind === 'action') {
234
+ const { action } = chosen;
235
+ // A start keeps the options of this page (attach, window, folder), which the plain link does not know.
236
+ if (action.kind === 'link' && action.target.kind === 'start-agent')
237
+ return { exit: { kind: 'run', argv: startArgv(model, action.target.agent, process.cwd()) } };
238
+ return this.operate(action);
239
+ }
257
240
  if (result.kind !== 'pick')
258
241
  return {};
259
242
  const id = result.value;
260
- const model = this.current();
261
243
  if (id.startsWith('host:'))
262
244
  return { exit: { kind: 'run', argv: ['host', id.slice(5)] } };
263
- if (id.startsWith('agent:'))
264
- return { exit: { kind: 'run', argv: startArgv(model, id.slice(6), process.cwd()) } };
265
245
  switch (id) {
266
246
  case 'repeat':
267
247
  return model.last ? { exit: { kind: 'run', argv: model.last } } : {};
268
- case 'add':
269
- return this.add(model);
270
248
  case 'opt:attach':
271
249
  this.attach = !this.attach;
272
250
  return {};
@@ -301,45 +279,10 @@ export class AgentsPage extends ListPage {
301
279
  return {};
302
280
  }
303
281
  }
304
- /** Make default (d) and Remove (x) on the highlighted agent. */
305
- async action(key, id) {
306
- const name = id?.startsWith('agent:') ? id.slice(6) : undefined;
307
- if (!name)
308
- return {};
309
- try {
310
- if (key === 'd') {
311
- makeDefaultAgent(this.file, this.draft, name);
312
- return { focus: id, notice: Theme.ok(Theme.glyph.ok + ' ' + name + ' is the default agent.') };
313
- }
314
- if (key !== 'x')
315
- return {};
316
- const ok = await this.app.ask('Remove ' + name + '?', ['It is taken out of your settings. Its files in the sandbox stay until you clean them up.'], 'Remove', true);
317
- if (!ok)
318
- return {};
319
- removeAgent(this.file, this.draft, name);
320
- return {
321
- focus: 'agent:' + this.file.userConfig().defaultHarness,
322
- notice: Theme.ok(Theme.glyph.ok + ' Removed ' + name + '.'),
323
- };
324
- }
325
- catch (e) {
326
- return { notice: Theme.error(Theme.glyph.cross + ' ' + errMsg(e)) };
327
- }
328
- }
329
- /** "Add an agent…": the new agent shows in the list, with the cursor on it. */
330
- async add(model) {
331
- try {
332
- const added = await new AddAgent(this.app, this.file, this.draft, model.agents.map((a) => a.name)).run();
333
- if (!added)
334
- return {};
335
- return {
336
- focus: 'agent:' + added.name,
337
- notice: Theme.ok(Theme.glyph.ok + ' Added ' + added.name + '.') +
338
- Theme.muted(added.saved ? ' Enter starts it.' : ' Ctrl+S saves it.'),
339
- };
340
- }
341
- catch (e) {
342
- return { notice: Theme.error(Theme.glyph.cross + ' ' + errMsg(e)) };
343
- }
282
+ /** Add, make default and remove run through the menu's dialogs, unless Settings has unsaved changes to the agents. */
283
+ async operate(action) {
284
+ if (agentsHaveStagedChanges(this.file, this.draft))
285
+ return { notice: Theme.warn('Agents have unsaved changes in Settings: save them (Ctrl+S) or undo them first.') };
286
+ return this.operations.perform(action, [this.label], (text) => this.busy(text));
344
287
  }
345
288
  }
@@ -1,28 +1,35 @@
1
- import { type App, type ListOptions, type ListResult } from '../../tui/index.js';
2
- import { AppsModel } from '../apps.js';
1
+ import { type AppsSource } from '../../../app/features/apps/index.js';
3
2
  import type { ConfigFile } from '../../../app/features/settings/index.js';
3
+ import { type App, type Category, type ListItem, type ListOptions, type ListResult } from '../../tui/index.js';
4
+ import type { Context } from '../../commands/context.js';
4
5
  import type { Draft } from '../settings/draft.js';
5
6
  import { type Scope } from './scopes.js';
6
7
  import { ListPage, type Outcome } from './list-page.js';
7
8
  import type { ShellExit } from '../shell.js';
8
9
  import type { Step } from '../../tui/index.js';
10
+ /** The apps as the menu shows them: the saved files with this page's staged ticks laid over them. */
11
+ export declare function draftSource(draft: Draft): AppsSource;
12
+ /** The menu's groups: the feature's groups, and in your own scope "Your own programs". */
13
+ export declare function appsCategories(file: ConfigFile): Category[];
14
+ /** The menu's rows for a scope, with the staged ticks. */
15
+ export declare function appsItems(file: ConfigFile, dataPath: string, draft: Draft): ListItem<string>[];
9
16
  /** The list for the apps tab. Pure. */
10
- export declare function appsOptions(model: AppsModel, file: ConfigFile, draft: Draft, scope: Scope): ListOptions<string>;
17
+ export declare function appsOptions(file: ConfigFile, dataPath: string, draft: Draft, scope: Scope): ListOptions<string>;
18
+ /** Stages the ticks of the list: only what differs from the saved file is kept in the draft. */
19
+ export declare function stageTicks(file: ConfigFile, draft: Draft, ticked: readonly string[]): void;
11
20
  export declare class AppsPage extends ListPage<string> {
21
+ private readonly ctx;
12
22
  private readonly files;
13
23
  private readonly draft;
14
- private readonly dataPath;
15
24
  readonly label = "Apps";
16
25
  readonly stages = true;
17
26
  private scope;
18
- constructor(app: App, files: Record<Scope, ConfigFile>, draft: Draft, dataPath: string);
27
+ private readonly operations;
28
+ constructor(app: App, ctx: Context, files: Record<Scope, ConfigFile>, draft: Draft);
19
29
  private get file();
20
- private get model();
21
30
  protected load(): Promise<void>;
22
31
  protected options(): ListOptions<string>;
23
32
  /** Every tick is staged as it happens, so leaving or saving always sees it. */
24
33
  protected afterKey(): Step<ShellExit>;
25
- /** Opens the GitHub request form in the browser and says where it is. */
26
- private request;
27
34
  protected act(result: ListResult<string>): Promise<Outcome<string>>;
28
35
  }
@@ -1,14 +1,100 @@
1
1
  // Section 4: the apps catalogue. Groups on the left, space ticks, and the ticks are staged until Ctrl+S saves them. Two
2
- // scopes like Settings: apps for every project, or for this project only.
2
+ // scopes like Settings: apps for every project, or for this project only. The rows come from the apps feature
3
+ // (features/apps/apps-rows.ts); the menu only lays its staged ticks over them. The web and the feature write at once
4
+ // (apps.toggle), so the staging stays here: "r" runs the feature's request operation, "c" stages your own programs.
5
+ import _ from 'lodash';
3
6
  import { errMsg } from '../../../core/util/index.js';
7
+ import { APP_GROUPS, appIsOn, appRows, customRows, CUSTOM_KEY, entriesOf, INSTALL, ISSUES_URL, requestApp, REQUEST_APP, SAVED, } from '../../../app/features/apps/index.js';
8
+ import { action } from '../../../app/kernel/index.js';
4
9
  import { Theme } from '../../tui/index.js';
5
- import { openIssuePage } from '../../../host/web/index.js';
6
- import { AppsModel, requestAppUrl } from '../apps.js';
7
10
  import { ValueEditor } from '../settings/value-editor.js';
11
+ import { MenuOperations } from '../io-menu.js';
8
12
  import { SCOPES, scopeStatus } from './scopes.js';
9
13
  import { ListPage } from './list-page.js';
14
+ /** The apps as the menu shows them: the saved files with this page's staged ticks laid over them. */
15
+ export function draftSource(draft) {
16
+ return {
17
+ install: (file) => draft.has(file, INSTALL) ? draft.value(file, INSTALL) : SAVED.install(file),
18
+ setting: (file, key) => (draft.has(file, key) ? draft.value(file, key) : SAVED.setting(file, key)),
19
+ custom: (file) => draft.has(file, CUSTOM_KEY)
20
+ ? draft.value(file, CUSTOM_KEY)
21
+ : SAVED.custom(file),
22
+ };
23
+ }
24
+ /** The menu's groups: the feature's groups, and in your own scope "Your own programs". */
25
+ export function appsCategories(file) {
26
+ return [
27
+ ...APP_GROUPS.map(({ id, label, description }) => ({ id, label, description })),
28
+ ...(file.isProject
29
+ ? []
30
+ : [
31
+ {
32
+ id: 'custom',
33
+ label: 'Your own programs',
34
+ description: 'Programs you added yourself (c to add or change them).',
35
+ },
36
+ ]),
37
+ ];
38
+ }
39
+ function appItem(row) {
40
+ const notes = [
41
+ Theme.muted('Download ') +
42
+ '~' +
43
+ row.size +
44
+ ' once, kept in C:\\AgentData\\tools\\' +
45
+ (row.setting ? '' : 'apps\\') +
46
+ row.id,
47
+ Theme.muted('Starts ') + (row.cli ? 'from a terminal (on PATH)' : 'from the desktop or Start > Sandbox apps'),
48
+ ...(row.mesa ? [Theme.muted('Graphics ') + 'OpenGL on the CPU (Mesa) is added for it'] : []),
49
+ ...(row.fullGitOnly
50
+ ? [Theme.warn('Needs full git access (Settings > Projects & setup > Git): it pushes with its own git.')]
51
+ : []),
52
+ ...(row.setting ? [Theme.muted('Setting ') + row.setting] : []),
53
+ ];
54
+ return {
55
+ value: row.id,
56
+ label: row.name,
57
+ category: row.group,
58
+ description: row.about,
59
+ details: notes,
60
+ keywords: row.id,
61
+ hint: (row.installed ? Theme.ok('installed ') : '') + Theme.muted(row.size),
62
+ checked: row.on,
63
+ };
64
+ }
65
+ function customItem(row) {
66
+ return {
67
+ value: '@custom:' + row.id,
68
+ label: row.name,
69
+ category: 'custom',
70
+ description: row.from,
71
+ hint: Theme.muted(row.cli ? 'command line' : 'desktop'),
72
+ checked: true,
73
+ disabled: 'Your own programs are always installed; c to change or remove them.',
74
+ };
75
+ }
76
+ /** The menu's rows for a scope, with the staged ticks. */
77
+ export function appsItems(file, dataPath, draft) {
78
+ const source = draftSource(draft);
79
+ return [
80
+ ...appRows(file, dataPath, source).map(appItem),
81
+ ...(file.isProject
82
+ ? []
83
+ : [
84
+ {
85
+ value: REQUEST_APP,
86
+ label: 'Request an app…',
87
+ category: 'custom',
88
+ description: 'Not in the list? Press r: your browser opens a prefilled request on GitHub (' + ISSUES_URL + ').',
89
+ hint: Theme.muted('press r'),
90
+ disabled: 'Press r to open the request form on GitHub.',
91
+ },
92
+ ]),
93
+ ...customRows(file, source).map(customItem),
94
+ ];
95
+ }
10
96
  /** The list for the apps tab. Pure. */
11
- export function appsOptions(model, file, draft, scope) {
97
+ export function appsOptions(file, dataPath, draft, scope) {
12
98
  return {
13
99
  path: [],
14
100
  status: scopeStatus(file),
@@ -16,72 +102,78 @@ export function appsOptions(model, file, draft, scope) {
16
102
  tabs: { items: SCOPES.map((s) => s.tab), active: SCOPES.findIndex((s) => s.scope === scope) },
17
103
  multi: true,
18
104
  enterTicks: true,
19
- categories: model.categories(),
20
- items: model.items(draft),
105
+ categories: appsCategories(file),
106
+ items: appsItems(file, dataPath, draft),
21
107
  actions: [
22
108
  ...(file.isProject ? [] : [{ key: 'c', label: 'your own programs' }]),
23
109
  { key: 'r', label: 'request an app' },
24
110
  ],
25
111
  };
26
112
  }
113
+ /** Stages the ticks of the list: only what differs from the saved file is kept in the draft. */
114
+ export function stageTicks(file, draft, ticked) {
115
+ const entries = entriesOf(file);
116
+ for (const entry of entries.filter((a) => a.setting)) {
117
+ const key = entry.setting.split('.');
118
+ const on = ticked.includes(entry.id);
119
+ if (on === appIsOn(entry, file, SAVED))
120
+ draft.unstage(file, key);
121
+ else
122
+ draft.stage(file, key, on);
123
+ }
124
+ const wanted = entries.filter((a) => !a.setting && ticked.includes(a.id)).map((a) => a.id);
125
+ const savedInstall = SAVED.install(file) ?? [];
126
+ if (_.isEqual([...wanted].sort(), [...savedInstall].sort()))
127
+ draft.unstage(file, INSTALL);
128
+ else
129
+ draft.stage(file, INSTALL, wanted.length || file.isProject ? wanted : undefined);
130
+ }
27
131
  export class AppsPage extends ListPage {
132
+ ctx;
28
133
  files;
29
134
  draft;
30
- dataPath;
31
135
  label = 'Apps';
32
136
  stages = true;
33
137
  scope = 'user';
34
- constructor(app, files, draft, dataPath) {
138
+ operations;
139
+ constructor(app, ctx, files, draft) {
35
140
  super(app);
141
+ this.ctx = ctx;
36
142
  this.files = files;
37
143
  this.draft = draft;
38
- this.dataPath = dataPath;
144
+ this.operations = new MenuOperations(app, ctx);
39
145
  }
40
146
  get file() {
41
147
  return this.files[this.scope];
42
148
  }
43
- get model() {
44
- return new AppsModel(this.file, this.dataPath);
45
- }
46
149
  load() {
47
150
  return Promise.resolve();
48
151
  }
49
152
  options() {
50
- return appsOptions(this.model, this.file, this.draft, this.scope);
153
+ return appsOptions(this.file, this.ctx.dataPath, this.draft, this.scope);
51
154
  }
52
155
  /** Every tick is staged as it happens, so leaving or saving always sees it. */
53
156
  afterKey() {
54
- this.model.stageTicks(this.draft, this.ticked());
157
+ stageTicks(this.file, this.draft, this.ticked());
55
158
  return;
56
159
  }
57
- /** Opens the GitHub request form in the browser and says where it is. */
58
- request() {
59
- const url = requestAppUrl();
60
- try {
61
- openIssuePage(url);
62
- return Theme.ok(Theme.glyph.ok + ' Opened the request form in your browser: ') + Theme.muted(url);
63
- }
64
- catch (e) {
65
- return Theme.error(Theme.glyph.cross + ' ' + errMsg(e)) + Theme.muted(' Open it yourself: ' + url);
66
- }
67
- }
68
160
  async act(result) {
69
- this.model.stageTicks(this.draft, this.ticked());
161
+ stageTicks(this.file, this.draft, this.ticked());
70
162
  if (result.kind === 'tab') {
71
163
  this.scope = SCOPES[result.index].scope;
72
164
  return {};
73
165
  }
74
166
  if (result.kind === 'action' && result.key === 'r')
75
- return { notice: this.request() };
167
+ return this.operations.perform(action(requestApp, {}), [this.label]);
76
168
  if (result.kind !== 'action' || result.key !== 'c')
77
169
  return {};
78
170
  const node = this.file.schema.child('apps')?.child('custom');
79
171
  if (!node)
80
172
  return {};
81
173
  try {
82
- const edited = await new ValueEditor(this.app).edit(['Apps', 'Your own programs'], ['apps', 'custom'], node, this.model.custom(this.draft));
174
+ const edited = await new ValueEditor(this.app).edit(['Apps', 'Your own programs'], CUSTOM_KEY, node, draftSource(this.draft).custom(this.file));
83
175
  if (edited)
84
- this.draft.stage(this.file, ['apps', 'custom'], edited.value);
176
+ this.draft.stage(this.file, CUSTOM_KEY, edited.value);
85
177
  }
86
178
  catch (e) {
87
179
  return { notice: Theme.error(Theme.glyph.cross + ' ' + errMsg(e)) };
@@ -178,7 +178,16 @@ export class ListPage {
178
178
  if (!step)
179
179
  return this.afterKey();
180
180
  const result = step.done;
181
- const outcome = result.kind === 'back' ? { exit: { kind: 'quit' } } : await this.act(result);
181
+ let outcome;
182
+ try {
183
+ outcome = result.kind === 'back' ? { exit: { kind: 'quit' } } : await this.act(result);
184
+ }
185
+ catch (e) {
186
+ // Whatever an action does wrong, the menu stays: the problem is shown where the action was chosen.
187
+ if (e instanceof Error && (e.name === 'ExitRequested' || e.name === 'ExitPromptError'))
188
+ throw e;
189
+ outcome = { notice: Theme.error(Theme.glyph.cross + ' ' + errMsg(e)) };
190
+ }
182
191
  if (outcome.exit)
183
192
  return { done: outcome.exit };
184
193
  // What the action changed (a stop, a restart) is known to the menu before the page loads again. An action that