windows-development-sandbox 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (413) hide show
  1. package/config.example.json +10 -0
  2. package/dist/app/context.d.ts +8 -0
  3. package/dist/app/context.js +12 -0
  4. package/dist/app/features/defs.d.ts +322 -0
  5. package/dist/app/features/defs.js +28 -0
  6. package/dist/app/features/sandbox/index.d.ts +2 -0
  7. package/dist/app/features/sandbox/index.js +3 -0
  8. package/dist/app/features/sandbox/sandbox.def.d.ts +193 -0
  9. package/dist/app/features/sandbox/sandbox.def.js +245 -0
  10. package/dist/app/features/sandbox/sandbox.run.d.ts +72 -0
  11. package/dist/app/features/sandbox/sandbox.run.js +98 -0
  12. package/dist/app/features/sessions/index.d.ts +2 -0
  13. package/dist/app/features/sessions/index.js +3 -0
  14. package/dist/app/features/sessions/sessions.def.d.ts +141 -0
  15. package/dist/app/features/sessions/sessions.def.js +176 -0
  16. package/dist/app/features/sessions/sessions.run.d.ts +39 -0
  17. package/dist/app/features/sessions/sessions.run.js +58 -0
  18. package/dist/{cli/menu/settings/categories.d.ts → app/features/settings/categories.def.d.ts} +2 -0
  19. package/dist/{cli/menu/settings/categories.js → app/features/settings/categories.def.js} +24 -5
  20. package/dist/app/features/settings/index.d.ts +5 -0
  21. package/dist/app/features/settings/index.js +6 -0
  22. package/dist/{cli/menu/settings/config-file.d.ts → app/features/settings/settings-file.d.ts} +1 -1
  23. package/dist/{cli/menu/settings/config-file.js → app/features/settings/settings-file.js} +1 -1
  24. package/dist/app/features/settings/settings-rows.d.ts +35 -0
  25. package/dist/app/features/settings/settings-rows.js +94 -0
  26. package/dist/app/features/settings/settings.def.d.ts +157 -0
  27. package/dist/app/features/settings/settings.def.js +179 -0
  28. package/dist/app/features/settings/settings.run.d.ts +67 -0
  29. package/dist/app/features/settings/settings.run.js +84 -0
  30. package/dist/app/kernel/define.d.ts +143 -0
  31. package/dist/app/kernel/define.js +42 -0
  32. package/dist/app/kernel/errors.d.ts +34 -0
  33. package/dist/app/kernel/errors.js +45 -0
  34. package/dist/app/kernel/fields.d.ts +70 -0
  35. package/dist/app/kernel/fields.js +243 -0
  36. package/dist/app/kernel/index.d.ts +8 -0
  37. package/dist/app/kernel/index.js +10 -0
  38. package/dist/app/kernel/io.d.ts +193 -0
  39. package/dist/app/kernel/io.js +126 -0
  40. package/dist/app/kernel/rpc.d.ts +156 -0
  41. package/dist/app/kernel/rpc.js +47 -0
  42. package/dist/app/kernel/text.d.ts +6 -0
  43. package/dist/app/kernel/text.js +57 -0
  44. package/dist/app/kernel/view.d.ts +146 -0
  45. package/dist/app/kernel/view.js +28 -0
  46. package/dist/app/registry.d.ts +10 -0
  47. package/dist/app/registry.js +50 -0
  48. package/dist/cli/args.d.ts +5 -0
  49. package/dist/cli/args.js +6 -0
  50. package/dist/cli/commands/engine.d.ts +2 -0
  51. package/dist/cli/commands/engine.js +60 -0
  52. package/dist/cli/commands/index.d.ts +1 -1
  53. package/dist/cli/commands/index.js +1 -1
  54. package/dist/cli/commands/launch/launch.js +47 -7
  55. package/dist/cli/commands/launch/prepare.js +3 -3
  56. package/dist/cli/commands/launch/running.js +2 -1
  57. package/dist/cli/commands/launch/summary.d.ts +2 -0
  58. package/dist/cli/commands/maintenance/config-share.d.ts +12 -0
  59. package/dist/cli/commands/maintenance/config-share.js +80 -0
  60. package/dist/cli/commands/maintenance/config.js +8 -82
  61. package/dist/cli/commands/maintenance/logs.js +14 -0
  62. package/dist/cli/commands/registry.js +3 -7
  63. package/dist/cli/commands/repair/checks/contained.d.ts +2 -0
  64. package/dist/cli/commands/repair/checks/contained.js +139 -0
  65. package/dist/cli/commands/repair/checks/hcs.d.ts +7 -0
  66. package/dist/cli/commands/repair/checks/hcs.js +28 -0
  67. package/dist/cli/commands/repair/checks/index.js +4 -0
  68. package/dist/cli/commands/repair/checks/pc.js +32 -1
  69. package/dist/cli/commands/repair/checks/sandbox.js +16 -1
  70. package/dist/cli/commands/repair/checks/vpn.js +34 -9
  71. package/dist/cli/commands/repair/env.js +2 -1
  72. package/dist/cli/commands/sessions/index.d.ts +0 -4
  73. package/dist/cli/commands/sessions/index.js +3 -7
  74. package/dist/cli/commands/shared-use.d.ts +5 -2
  75. package/dist/cli/commands/shared-use.js +11 -8
  76. package/dist/cli/commands/storage/contained.d.ts +3 -0
  77. package/dist/cli/commands/storage/contained.js +179 -0
  78. package/dist/cli/commands/storage/offer.js +7 -1
  79. package/dist/cli/commands/storage/storage.js +10 -2
  80. package/dist/cli/commands/user-config.js +1 -1
  81. package/dist/cli/commands/vpn.js +19 -3
  82. package/dist/cli/commands/web.js +2 -0
  83. package/dist/cli/index.js +2 -1
  84. package/dist/cli/manifest.d.ts +72 -104
  85. package/dist/cli/manifest.js +64 -39
  86. package/dist/cli/menu/add-agent.d.ts +1 -1
  87. package/dist/cli/menu/add-agent.js +1 -1
  88. package/dist/cli/menu/apps.d.ts +7 -1
  89. package/dist/cli/menu/apps.js +36 -0
  90. package/dist/cli/menu/command-fields.d.ts +1 -1
  91. package/dist/cli/menu/command-form.js +2 -2
  92. package/dist/cli/menu/io-menu.d.ts +29 -0
  93. package/dist/cli/menu/io-menu.js +126 -0
  94. package/dist/cli/menu/live.d.ts +27 -0
  95. package/dist/cli/menu/live.js +69 -0
  96. package/dist/cli/menu/memory.d.ts +19 -0
  97. package/dist/cli/menu/memory.js +53 -0
  98. package/dist/cli/menu/menu.d.ts +10 -1
  99. package/dist/cli/menu/menu.js +27 -9
  100. package/dist/cli/menu/pages/agents-page.d.ts +3 -1
  101. package/dist/cli/menu/pages/agents-page.js +12 -5
  102. package/dist/cli/menu/pages/apps-page.d.ts +3 -1
  103. package/dist/cli/menu/pages/apps-page.js +19 -2
  104. package/dist/cli/menu/pages/list-page.d.ts +27 -0
  105. package/dist/cli/menu/pages/list-page.js +109 -7
  106. package/dist/cli/menu/pages/maintenance-page.d.ts +0 -5
  107. package/dist/cli/menu/pages/maintenance-page.js +17 -19
  108. package/dist/cli/menu/pages/sandbox-page.d.ts +4 -6
  109. package/dist/cli/menu/pages/sandbox-page.js +26 -118
  110. package/dist/cli/menu/pages/scopes.d.ts +1 -1
  111. package/dist/cli/menu/pages/sessions-page.d.ts +5 -11
  112. package/dist/cli/menu/pages/sessions-page.js +19 -88
  113. package/dist/cli/menu/pages/settings-page.d.ts +2 -10
  114. package/dist/cli/menu/pages/settings-page.js +64 -94
  115. package/dist/cli/menu/render-view.d.ts +28 -0
  116. package/dist/cli/menu/render-view.js +112 -0
  117. package/dist/cli/menu/settings/{path-kind.d.ts → ask-path.d.ts} +1 -7
  118. package/dist/cli/menu/settings/ask-path.js +14 -0
  119. package/dist/cli/menu/settings/collections/collection-editor.d.ts +1 -1
  120. package/dist/cli/menu/settings/collections/string-list-editor.d.ts +1 -2
  121. package/dist/cli/menu/settings/collections/string-list-editor.js +1 -1
  122. package/dist/cli/menu/settings/draft.d.ts +1 -1
  123. package/dist/cli/menu/settings/format.d.ts +1 -3
  124. package/dist/cli/menu/settings/format.js +6 -36
  125. package/dist/cli/menu/settings/index.d.ts +1 -3
  126. package/dist/cli/menu/settings/index.js +3 -3
  127. package/dist/cli/menu/settings/object-editor.d.ts +1 -1
  128. package/dist/cli/menu/settings/share.d.ts +36 -0
  129. package/dist/cli/menu/settings/share.js +131 -0
  130. package/dist/cli/menu/settings/value-editor.d.ts +5 -1
  131. package/dist/cli/menu/settings/value-editor.js +57 -54
  132. package/dist/cli/menu/several.js +1 -1
  133. package/dist/cli/menu/share-settings.d.ts +21 -0
  134. package/dist/cli/menu/share-settings.js +112 -0
  135. package/dist/cli/menu/shell.d.ts +21 -5
  136. package/dist/cli/menu/shell.js +31 -6
  137. package/dist/cli/menu/views.d.ts +7 -0
  138. package/dist/cli/menu/views.js +11 -0
  139. package/dist/cli/menu/vpn-login.js +8 -0
  140. package/dist/cli/operations/handler.d.ts +6 -0
  141. package/dist/cli/operations/handler.js +61 -0
  142. package/dist/cli/operations/index.d.ts +4 -0
  143. package/dist/cli/operations/index.js +5 -0
  144. package/dist/cli/operations/input.d.ts +6 -0
  145. package/dist/cli/operations/input.js +37 -0
  146. package/dist/cli/operations/io-cli.d.ts +7 -0
  147. package/dist/cli/operations/io-cli.js +62 -0
  148. package/dist/cli/operations/spec.d.ts +18 -0
  149. package/dist/cli/operations/spec.js +108 -0
  150. package/dist/cli/output/colors.d.ts +2 -2
  151. package/dist/cli/output/colors.js +2 -11
  152. package/dist/cli/output/index.d.ts +1 -0
  153. package/dist/cli/output/index.js +1 -0
  154. package/dist/cli/output/print.d.ts +3 -1
  155. package/dist/cli/output/print.js +7 -3
  156. package/dist/cli/output/progress.d.ts +15 -0
  157. package/dist/cli/output/progress.js +44 -0
  158. package/dist/cli/output/render-view.d.ts +4 -0
  159. package/dist/cli/output/render-view.js +63 -0
  160. package/dist/cli/services/agent-commands-service.d.ts +2 -0
  161. package/dist/cli/services/agent-commands-service.js +11 -2
  162. package/dist/cli/services/logs-service.js +4 -3
  163. package/dist/cli/tui/app.d.ts +6 -1
  164. package/dist/cli/tui/app.js +26 -4
  165. package/dist/cli/tui/list/list-screen.d.ts +14 -0
  166. package/dist/cli/tui/list/list-screen.js +64 -36
  167. package/dist/cli/tui/list/list-types.d.ts +2 -0
  168. package/dist/cli/tui/terminal.d.ts +7 -0
  169. package/dist/cli/tui/terminal.js +30 -10
  170. package/dist/core/config/defaults.js +8 -0
  171. package/dist/core/schema/config.d.ts +13 -0
  172. package/dist/core/schema/config.js +16 -1
  173. package/dist/core/schema/harness.d.ts +1 -0
  174. package/dist/core/schema/harness.js +5 -0
  175. package/dist/core/schema/sandbox.d.ts +6 -0
  176. package/dist/core/schema/sandbox.js +20 -3
  177. package/dist/core/schema/vpn.d.ts +2 -1
  178. package/dist/core/schema/vpn.js +3 -3
  179. package/dist/core/types.d.ts +23 -5
  180. package/dist/core/util/fs.d.ts +2 -0
  181. package/dist/core/util/fs.js +9 -0
  182. package/dist/core/util/index.d.ts +1 -1
  183. package/dist/core/util/index.js +1 -1
  184. package/dist/guest/agents/agent-files.d.ts +5 -0
  185. package/dist/guest/agents/agent-files.js +4 -0
  186. package/dist/guest/agents/local-state.d.ts +2 -0
  187. package/dist/guest/agents/local-state.js +21 -1
  188. package/dist/guest/boot/boot.js +2 -1
  189. package/dist/guest/daemon/daemon.js +4 -0
  190. package/dist/guest/daemon/sessions.d.ts +5 -0
  191. package/dist/guest/daemon/sessions.js +17 -3
  192. package/dist/guest/entries/daemon.js +3 -0
  193. package/dist/guest/governor/decide.d.ts +61 -0
  194. package/dist/guest/governor/decide.js +182 -0
  195. package/dist/guest/governor/governor.d.ts +42 -0
  196. package/dist/guest/governor/governor.js +214 -0
  197. package/dist/guest/governor/index.d.ts +2 -0
  198. package/dist/guest/governor/index.js +2 -0
  199. package/dist/guest/runtime/csc.d.ts +1 -0
  200. package/dist/guest/runtime/csc.js +1 -0
  201. package/dist/guest/sessions/viewer.js +3 -1
  202. package/dist/guest/toolchains/native/rust-extras.js +4 -2
  203. package/dist/guest/vpn/vpn.js +69 -21
  204. package/dist/guest/vpn/wireguard.d.ts +32 -0
  205. package/dist/guest/vpn/wireguard.js +132 -0
  206. package/dist/host/agent-commands/commands.d.ts +4 -0
  207. package/dist/host/agent-commands/commands.js +31 -7
  208. package/dist/host/audit/log.d.ts +1 -1
  209. package/dist/host/channel/attach.d.ts +3 -1
  210. package/dist/host/channel/attach.js +5 -2
  211. package/dist/host/channel/channel.d.ts +2 -0
  212. package/dist/host/channel/channel.js +4 -0
  213. package/dist/host/engines/engine.d.ts +12 -0
  214. package/dist/host/engines/engine.js +1 -0
  215. package/dist/host/engines/hcs/check.d.ts +25 -0
  216. package/dist/host/engines/hcs/check.js +95 -0
  217. package/dist/host/engines/hcs/documents.d.ts +44 -0
  218. package/dist/host/engines/hcs/documents.js +155 -0
  219. package/dist/host/engines/hcs/engine.d.ts +39 -0
  220. package/dist/host/engines/hcs/engine.js +171 -0
  221. package/dist/host/engines/hcs/helper.d.ts +43 -0
  222. package/dist/host/engines/hcs/helper.js +173 -0
  223. package/dist/host/engines/hcs/images.d.ts +37 -0
  224. package/dist/host/engines/hcs/images.js +152 -0
  225. package/dist/host/engines/index.d.ts +7 -0
  226. package/dist/host/engines/index.js +5 -0
  227. package/dist/host/engines/registry.d.ts +16 -0
  228. package/dist/host/engines/registry.js +30 -0
  229. package/dist/host/engines/types.d.ts +58 -0
  230. package/dist/host/engines/types.js +1 -0
  231. package/dist/host/engines/windows-sandbox/engine.d.ts +16 -0
  232. package/dist/host/engines/windows-sandbox/engine.js +63 -0
  233. package/dist/host/engines/windows-sandbox/wsb-text.d.ts +2 -0
  234. package/dist/host/engines/windows-sandbox/wsb-text.js +29 -0
  235. package/dist/host/launch/generate.d.ts +5 -1
  236. package/dist/host/launch/generate.js +50 -8
  237. package/dist/host/launch/index.d.ts +3 -1
  238. package/dist/host/launch/index.js +3 -1
  239. package/dist/host/launch/launch-plan.d.ts +13 -0
  240. package/dist/host/launch/launch-plan.js +26 -0
  241. package/dist/host/launch/mappings.js +2 -1
  242. package/dist/host/launch/risk-notes.d.ts +10 -0
  243. package/dist/host/launch/risk-notes.js +18 -0
  244. package/dist/host/launch/session-file.d.ts +2 -0
  245. package/dist/host/launch/session-file.js +7 -1
  246. package/dist/host/launch/wsb-file.d.ts +28 -4
  247. package/dist/host/launch/wsb-file.js +61 -42
  248. package/dist/host/life/crash-report.d.ts +27 -1
  249. package/dist/host/life/crash-report.js +173 -7
  250. package/dist/host/life/host-memory.d.ts +40 -0
  251. package/dist/host/life/host-memory.js +137 -0
  252. package/dist/host/life/index.d.ts +2 -0
  253. package/dist/host/life/index.js +2 -0
  254. package/dist/host/life/stop-events.d.ts +2 -0
  255. package/dist/host/life/stop-events.js +20 -3
  256. package/dist/host/life/stop-warning.js +1 -1
  257. package/dist/host/life/watchdog.d.ts +54 -0
  258. package/dist/host/life/watchdog.js +108 -0
  259. package/dist/host/native.d.ts +3 -2
  260. package/dist/host/native.js +22 -3
  261. package/dist/host/profile/claude-login.js +3 -2
  262. package/dist/host/sandbox/contained-deps.d.ts +2 -0
  263. package/dist/host/sandbox/contained-deps.js +17 -0
  264. package/dist/host/sandbox/index.d.ts +2 -0
  265. package/dist/host/sandbox/index.js +2 -0
  266. package/dist/host/sandbox/platform.d.ts +2 -0
  267. package/dist/host/sandbox/platform.js +7 -7
  268. package/dist/host/sandbox/tasklist.d.ts +13 -0
  269. package/dist/host/sandbox/tasklist.js +47 -0
  270. package/dist/host/secrets/launch-secrets.js +2 -1
  271. package/dist/host/sharing/current.js +2 -6
  272. package/dist/host/storage/contained/check.d.ts +9 -0
  273. package/dist/host/storage/contained/check.js +37 -0
  274. package/dist/host/storage/contained/contained-storage.d.ts +66 -0
  275. package/dist/host/storage/contained/contained-storage.js +168 -0
  276. package/dist/host/storage/contained/disk-port.d.ts +43 -0
  277. package/dist/host/storage/contained/disk-port.js +122 -0
  278. package/dist/host/storage/contained/index.d.ts +9 -0
  279. package/dist/host/storage/contained/index.js +8 -0
  280. package/dist/host/storage/contained/layout.d.ts +28 -0
  281. package/dist/host/storage/contained/layout.js +45 -0
  282. package/dist/host/storage/contained/resolve.d.ts +4 -0
  283. package/dist/host/storage/contained/resolve.js +30 -0
  284. package/dist/host/storage/contained/setting.d.ts +1 -0
  285. package/dist/host/storage/contained/setting.js +5 -0
  286. package/dist/host/storage/contained/state.d.ts +89 -0
  287. package/dist/host/storage/contained/state.js +166 -0
  288. package/dist/host/storage/contained/transfer.d.ts +24 -0
  289. package/dist/host/storage/contained/transfer.js +121 -0
  290. package/dist/host/storage/index.d.ts +2 -0
  291. package/dist/host/storage/index.js +2 -0
  292. package/dist/host/storage/providers/contained.d.ts +15 -0
  293. package/dist/host/storage/providers/contained.js +39 -0
  294. package/dist/host/storage/providers/folder.d.ts +12 -0
  295. package/dist/host/storage/providers/folder.js +20 -0
  296. package/dist/host/storage/providers/index.d.ts +4 -0
  297. package/dist/host/storage/providers/index.js +3 -0
  298. package/dist/host/storage/providers/registry.d.ts +9 -0
  299. package/dist/host/storage/providers/registry.js +14 -0
  300. package/dist/host/storage/providers/types.d.ts +33 -0
  301. package/dist/host/storage/providers/types.js +1 -0
  302. package/dist/host/tools/conpty.js +3 -2
  303. package/dist/host/tools/windows-terminal.js +8 -7
  304. package/dist/host/vpn/index.d.ts +1 -1
  305. package/dist/host/vpn/index.js +1 -1
  306. package/dist/host/vpn/prepare.d.ts +2 -0
  307. package/dist/host/vpn/prepare.js +63 -2
  308. package/dist/host/web/browser.d.ts +2 -0
  309. package/dist/host/web/browser.js +14 -1
  310. package/dist/host/web/index.d.ts +3 -2
  311. package/dist/host/web/index.js +3 -2
  312. package/dist/host/web/rpc.d.ts +24 -0
  313. package/dist/host/web/rpc.js +149 -0
  314. package/dist/host/web/runner.d.ts +2 -0
  315. package/dist/host/web/runner.js +1 -2
  316. package/dist/host/web/server.d.ts +7 -0
  317. package/dist/host/web/server.js +23 -3
  318. package/dist/runtime/agent.js +69 -8
  319. package/dist/runtime/daemon.js +1058 -624
  320. package/dist/runtime/db.js +69 -8
  321. package/dist/runtime/main.js +624 -182
  322. package/dist/runtime/netproxy.js +69 -8
  323. package/dist/runtime/view.js +87 -13
  324. package/dist/selftest/agent-paths.test.js +11 -1
  325. package/dist/selftest/app.test.d.ts +2 -0
  326. package/dist/selftest/app.test.js +324 -0
  327. package/dist/selftest/channel.test.js +13 -0
  328. package/dist/selftest/cli.test.js +2 -1
  329. package/dist/selftest/config.test.js +59 -0
  330. package/dist/selftest/contained.test.d.ts +2 -0
  331. package/dist/selftest/contained.test.js +544 -0
  332. package/dist/selftest/credentials.test.js +1 -1
  333. package/dist/selftest/engine.test.d.ts +2 -0
  334. package/dist/selftest/engine.test.js +127 -0
  335. package/dist/selftest/hcs.test.d.ts +2 -0
  336. package/dist/selftest/hcs.test.js +482 -0
  337. package/dist/selftest/heavy-load.test.d.ts +2 -0
  338. package/dist/selftest/heavy-load.test.js +329 -0
  339. package/dist/selftest/index.js +12 -0
  340. package/dist/selftest/layout.test.js +12 -0
  341. package/dist/selftest/menu-perf.test.d.ts +2 -0
  342. package/dist/selftest/menu-perf.test.js +248 -0
  343. package/dist/selftest/menu-screens.js +4 -2
  344. package/dist/selftest/menu.test.js +36 -8
  345. package/dist/selftest/support/engine-cases.d.ts +8 -0
  346. package/dist/selftest/support/engine-cases.js +65 -0
  347. package/dist/selftest/support/engine-golden.d.ts +3 -0
  348. package/dist/selftest/support/engine-golden.js +66 -0
  349. package/dist/selftest/support/fixtures.d.ts +7 -1
  350. package/dist/selftest/support/hcs-probes.d.ts +17 -0
  351. package/dist/selftest/support/hcs-probes.js +41 -0
  352. package/dist/selftest/vpn.test.js +136 -3
  353. package/dist/selftest/web.test.js +101 -3
  354. package/dist/shared/channel/run-dir.d.ts +4 -1
  355. package/dist/shared/channel/run-dir.js +7 -1
  356. package/dist/shared/channel/schema.d.ts +35 -1
  357. package/dist/shared/channel/schema.js +27 -0
  358. package/dist/shared/channel/secrets.d.ts +1 -0
  359. package/dist/shared/channel/secrets.js +6 -0
  360. package/dist/shared/memory/index.d.ts +1 -0
  361. package/dist/shared/memory/index.js +1 -0
  362. package/dist/shared/memory/policy.d.ts +33 -0
  363. package/dist/shared/memory/policy.js +55 -0
  364. package/dist/shared/pty/output-filter.d.ts +14 -1
  365. package/dist/shared/pty/output-filter.js +22 -4
  366. package/dist/shared/pty/terminal.d.ts +4 -1
  367. package/dist/shared/pty/terminal.js +4 -2
  368. package/dist/shared/system.d.ts +1 -0
  369. package/dist/shared/system.js +1 -0
  370. package/dist/shared/vpn/download.d.ts +16 -0
  371. package/dist/shared/vpn/download.js +16 -0
  372. package/dist/shared/vpn/wireguard.d.ts +51 -0
  373. package/dist/shared/vpn/wireguard.js +227 -0
  374. package/dist/sync/sync-cycle.js +5 -5
  375. package/dist/sync/watcher.d.ts +13 -0
  376. package/dist/sync/watcher.js +92 -3
  377. package/dist/web/index.html +4 -3
  378. package/dist/web/main-NRRS5O6Z.js +157 -0
  379. package/dist/web/main-T5DMCHYV.css +1 -0
  380. package/native/csharp/HcsHost/AssemblyInfo.cs +14 -0
  381. package/native/csharp/HcsHost/Guard.cs +100 -0
  382. package/native/csharp/HcsHost/Hcs.cs +256 -0
  383. package/native/csharp/HcsHost/Json.cs +78 -0
  384. package/native/csharp/HcsHost/Program.cs +185 -0
  385. package/native/csharp/HcsHost/VirtualDisk.cs +95 -0
  386. package/native/csharp/HostTools/AssemblyInfo.cs +14 -0
  387. package/native/csharp/HostTools/MemoryStatus.cs +107 -0
  388. package/native/csharp/HostTools/Program.cs +58 -17
  389. package/native/scripts/check-contained.ps1 +28 -0
  390. package/native/scripts/contained-common.ps1 +241 -0
  391. package/native/scripts/dismount-contained.ps1 +66 -0
  392. package/native/scripts/memory-status.ps1 +34 -0
  393. package/native/scripts/mount-contained.ps1 +139 -0
  394. package/native/scripts/run-elevated.ps1 +17 -4
  395. package/native/scripts/stop-events.ps1 +23 -8
  396. package/package.json +9 -2
  397. package/sandbox/csharp/MemGov.cs +398 -0
  398. package/sandbox/csharp/PtyHost.cs +111 -5
  399. package/schemas/config.schema.json +60 -6
  400. package/schemas/sandbox-config.schema.json +3 -3
  401. package/dist/cli/commands/sessions/control.d.ts +0 -4
  402. package/dist/cli/commands/sessions/control.js +0 -66
  403. package/dist/cli/commands/sessions/list.d.ts +0 -2
  404. package/dist/cli/commands/sessions/list.js +0 -48
  405. package/dist/cli/commands/sessions/status.d.ts +0 -4
  406. package/dist/cli/commands/sessions/status.js +0 -114
  407. package/dist/cli/commands/sessions/window.d.ts +0 -3
  408. package/dist/cli/commands/sessions/window.js +0 -18
  409. package/dist/cli/menu/settings/path-kind.js +0 -44
  410. package/dist/web/main-CXESL347.js +0 -45
  411. package/dist/web/main-VLR7DVQ4.css +0 -1
  412. /package/dist/{cli/menu/settings → app/kernel}/schema-node.d.ts +0 -0
  413. /package/dist/{cli/menu/settings → app/kernel}/schema-node.js +0 -0
@@ -1,110 +1,41 @@
1
- // Section 2: the agent sessions of the running sandbox. Enter shows one in this terminal (the one thing that leaves the
2
- // menu), r restarts the agents.
3
- import { errMsg, safeText } from '../../../core/util/index.js';
4
- import { isSandboxRunning } from '../../../host/sandbox/index.js';
5
- import { ago } from '../../output/index.js';
6
- import { SessionsService, viewerText } from '../../services/index.js';
7
- import { Theme } from '../../tui/index.js';
1
+ // Section 2: the agent sessions of the running sandbox, as the sessions view shows them (src/app/features/sessions).
2
+ // Enter shows one in this terminal (the one thing that leaves the menu), r restarts the agents.
3
+ import { listSessions, sessionsPage } from '../../../app/features/sessions/index.js';
4
+ import { readQuery } from '../../../app/registry.js';
5
+ import { MenuOperations } from '../io-menu.js';
6
+ import { menuView } from '../render-view.js';
8
7
  import { ListPage } from './list-page.js';
9
- const START = '@start';
8
+ function view(m) {
9
+ return menuView(sessionsPage({ ...m, wide: false }));
10
+ }
10
11
  /** The list for the sessions tab. Pure. */
11
12
  export function sessionsOptions(m) {
12
- return {
13
- path: [],
14
- items: m.sessions.length
15
- ? m.sessions.map((s) => ({
16
- value: s.id,
17
- label: safeText(s.harness) + ' ' + safeText(s.projectName ?? '-'),
18
- hint: s.phase + (s.exitCode !== undefined ? ' (' + s.exitCode + ')' : '') + ' ' + ago(s.startedAt),
19
- section: s.here ? "This folder's project" : 'Other sessions',
20
- description: s.phase === 'running'
21
- ? 'Enter shows this session in this terminal (Ctrl+] detaches; the session keeps running).'
22
- : undefined,
23
- details: [
24
- Theme.muted('Session ') + s.id,
25
- Theme.muted('Folder ') + safeText(s.cwd),
26
- Theme.muted('Attached ') + viewerText(s.viewers),
27
- ],
28
- ...(s.phase === 'running' ? {} : { disabled: 'This session has ended.' }),
29
- }))
30
- : [
31
- {
32
- value: START,
33
- label: 'Start ' + m.defaultAgent,
34
- hint: Theme.muted('default agent'),
35
- section: m.sandboxRunning ? 'No sessions' : 'No sandbox is running',
36
- description: m.sandboxRunning
37
- ? 'No agent is running. Enter starts ' + m.defaultAgent + ' in this folder.'
38
- : 'Enter starts the sandbox and ' + m.defaultAgent + ' in this folder; its session is listed here.',
39
- },
40
- ],
41
- actions: [
42
- { key: 'r', label: 'restart agents', importance: 1, applies: (id) => id !== START },
43
- { key: 'o', label: 'reopen last sessions', importance: 2 },
44
- { key: 'l', label: 'refresh', importance: 3 },
45
- ],
46
- enterLabel: (_category, id) => (id === START ? 'start' : 'attach'),
47
- };
13
+ const v = view(m);
14
+ return { path: [], items: v.items, actions: v.actions, enterLabel: v.enterLabel };
48
15
  }
49
16
  export class SessionsPage extends ListPage {
50
17
  ctx;
51
18
  label = 'Sessions';
52
19
  model;
53
- service;
20
+ operations;
54
21
  constructor(app, ctx) {
55
22
  super(app);
56
23
  this.ctx = ctx;
57
- this.service = new SessionsService(ctx);
58
- this.model = { sandboxRunning: false, sessions: [], defaultAgent: this.defaultAgent() };
59
- }
60
- defaultAgent() {
61
- return this.ctx.loaded.config.defaultHarness;
24
+ this.operations = new MenuOperations(app, ctx);
25
+ this.model = { sandboxRunning: false, sessions: [], defaultAgent: ctx.loaded.config.defaultHarness };
62
26
  }
63
27
  async load() {
64
- const sandboxRunning = isSandboxRunning();
65
- try {
66
- const list = sandboxRunning ? await this.service.list() : [];
67
- this.model = { sandboxRunning, sessions: this.service.ordered(list), defaultAgent: this.defaultAgent() };
68
- }
69
- catch (e) {
70
- this.model = { sandboxRunning, sessions: [], defaultAgent: this.defaultAgent() };
71
- this.say(Theme.error(Theme.glyph.cross + ' ' + errMsg(e)));
72
- }
28
+ this.model = await readQuery(listSessions, this.ctx);
73
29
  }
74
30
  options() {
75
31
  return sessionsOptions(this.model);
76
32
  }
77
33
  async act(result) {
78
- if (result.kind === 'pick' && result.value === START)
79
- return { exit: { kind: 'run', argv: ['launch', '--harness', this.model.defaultAgent] } };
80
- if (result.kind === 'pick')
81
- return { exit: { kind: 'run', argv: ['attach', result.value] } };
82
- if (result.kind !== 'action')
34
+ const chosen = view(this.model).resolve(result);
35
+ if (!chosen)
83
36
  return {};
84
- if (result.key === 'l')
37
+ if (chosen.kind === 'refresh')
85
38
  return { reload: true };
86
- if (result.key === 'o')
87
- return { exit: { kind: 'run', argv: ['resume'] } };
88
- if (!this.model.sandboxRunning)
89
- return { notice: Theme.warn('No sandbox is running.') };
90
- const scope = this.service.restartScope(false);
91
- const what = scope.guest ? 'the agents of ' + scope.projectName : 'every agent';
92
- const ok = await this.app.ask('Restart ' + what + '?', [
93
- 'The agents are started again and continue their conversations where they can.',
94
- ...(scope.notAProject
95
- ? [scope.folder + ' is not a project of the running sandbox: every session restarts.']
96
- : []),
97
- ], 'Restart');
98
- if (!ok)
99
- return {};
100
- try {
101
- return {
102
- reload: true,
103
- notice: Theme.ok(Theme.glyph.ok + ' ' + safeText(await this.service.restart(null, scope.guest))),
104
- };
105
- }
106
- catch (e) {
107
- return { notice: Theme.error(Theme.glyph.cross + ' ' + errMsg(e)) };
108
- }
39
+ return this.operations.perform(chosen.action, [this.label], (text) => this.busy(text));
109
40
  }
110
41
  }
@@ -1,18 +1,10 @@
1
+ import { settingsOf, type ConfigFile, type Setting } from '../../../app/features/settings/index.js';
1
2
  import type { Context } from '../../commands/context.js';
2
3
  import { type App, type ListItem, type ListOptions, type ListResult } from '../../tui/index.js';
3
- import type { ConfigFile } from '../settings/config-file.js';
4
4
  import type { Draft } from '../settings/draft.js';
5
- import type { SchemaNode } from '../settings/schema-node.js';
6
5
  import { type Scope } from './scopes.js';
7
6
  import { ListPage, type Outcome } from './list-page.js';
8
- export interface Setting {
9
- readonly key: string[];
10
- readonly node: SchemaNode;
11
- readonly category: string;
12
- readonly section?: string;
13
- }
14
- /** Every editable setting with the category and heading it is shown under. */
15
- export declare function settingsOf(file: ConfigFile): Setting[];
7
+ export { settingsOf, type Setting };
16
8
  /** The row in General that opens the data folder flow (your own settings only). */
17
9
  export declare const DATA_FOLDER = "@data-folder";
18
10
  /** The list for the settings section. Pure (reads the files it is given). `extra` are rows that are not settings. */
@@ -1,125 +1,86 @@
1
1
  // Section 5: every setting, for all projects or just this project (two scopes), grouped into categories on the left and
2
- // searchable, with each value, its default and its description. Changes are staged in the Draft (marked *) and
3
- // written with Ctrl+S; Enter edits, r resets to the default, z undoes an unsaved change.
2
+ // searchable, with each value, its default and its description. The rows (which settings, their values, secrets as
3
+ // ***) are the settings feature's; this page lays the unsaved changes over them: changes are staged in the Draft
4
+ // (marked *) and written with Ctrl+S; Enter edits, r resets to the default, z undoes an unsaved change.
4
5
  import _ from 'lodash';
5
- import { NEVER_LOOSER } from '../../../core/local-config/index.js';
6
+ import { CATEGORIES, labelOf, nameOf, settingRow, settingsOf, } from '../../../app/features/settings/index.js';
7
+ import { valueText } from '../../../app/kernel/index.js';
6
8
  import { errMsg } from '../../../core/util/index.js';
7
9
  import { Text, Theme, } from '../../tui/index.js';
8
10
  import { dataFolderRow, DataFolderFlow } from '../data-folder.js';
11
+ import { EXPORT_SETTINGS, IMPORT_SETTINGS, ShareSettingsFlow, shareSettingsRows } from '../share-settings.js';
9
12
  import { VpnLoginFlow, vpnLoginRow, VPN_LOGIN } from '../vpn-login.js';
10
- import { CATEGORIES, categoryOf, nameOf } from '../settings/categories.js';
11
- import { formatValue } from '../settings/format.js';
12
13
  import { ValueEditor } from '../settings/value-editor.js';
13
14
  import { SCOPES, scopeStatus } from './scopes.js';
14
15
  import { ListPage } from './list-page.js';
15
- /** Every editable setting with the category and heading it is shown under. */
16
- export function settingsOf(file) {
17
- return file.schema.children().flatMap(([top, node]) => {
18
- const category = categoryOf(top);
19
- if (!category)
20
- return [];
21
- const several = category.keys.filter((k) => file.schema.child(k)).length > 1;
22
- if (!node.isSection)
23
- return [
24
- {
25
- key: [top],
26
- node,
27
- category: category.id,
28
- section: several && category.id !== 'general' ? nameOf(top) : undefined,
29
- },
30
- ];
31
- return node.leaves([top]).map(({ key, node: leaf }) => {
32
- const heading = [
33
- several ? nameOf(top) : undefined,
34
- key.length > 2 ? key.slice(1, -1).map(nameOf).join(' › ') : undefined,
35
- ]
36
- .filter(Boolean)
37
- .join(' › ');
38
- return { key, node: leaf, category: category.id, section: heading || undefined };
39
- });
40
- });
41
- }
42
- /** What the file will say for a setting once the staged changes are saved (undefined = not set there). */
43
- function written(file, draft, key) {
44
- return draft.has(file, key) ? draft.value(file, key) : file.valueInFile(key);
45
- }
46
- /** What is in effect for a setting once the staged changes are saved. */
47
- function effective(file, draft, key, config) {
48
- if (file.isProject)
49
- return written(file, draft, key);
50
- return draft.has(file, key) ? (draft.value(file, key) ?? file.defaultOf(key)) : _.get(config, key);
16
+ export { settingsOf };
17
+ /** The files with the menu's unsaved changes laid over them. */
18
+ function draftSource(draft) {
19
+ const written = (file, key) => draft.has(file, key) ? draft.value(file, key) : file.valueInFile(key);
20
+ return {
21
+ written,
22
+ effective: (file, key, config) => file.isProject
23
+ ? written(file, key)
24
+ : draft.has(file, key)
25
+ ? (draft.value(file, key) ?? file.defaultOf(key))
26
+ : _.get(config, key),
27
+ };
51
28
  }
52
- /** Plain words for settings whose key is jargon (the key itself stays in the details pane). */
53
- const LABELS = {
54
- mappings: 'Extra folders from this PC',
55
- 'sandbox.vGpu': 'Graphics acceleration',
56
- 'sandbox.headless': 'Hide the sandbox window',
57
- 'sandbox.memoryMb': 'Memory (MB)',
58
- 'sandbox.pageFileMb': 'Swap file (MB)',
59
- 'network.dns': 'DNS servers',
60
- 'projects.wsbShare': 'Add folders while the sandbox runs',
61
- 'projects.all': 'Map all known projects at start',
62
- 'claudeProfile.hostDir': 'Claude folder on this PC',
63
- 'claudeProfile.globalStateFile': 'Claude state file on this PC',
64
- };
65
- function labelOf(key) {
66
- return LABELS[key.join('.')] ?? Text.humanize(key[key.length - 1]);
29
+ /** A value of a row in one short line, painted like every value in the menu. */
30
+ function shown(row, value, max = 60) {
31
+ const v = valueText(value, row.field, max);
32
+ return v.tone === 'ok' ? Theme.ok(v.text) : v.tone === 'muted' ? Theme.muted(v.text) : v.text;
67
33
  }
68
- function item(s, files, scope, draft, config) {
69
- const file = files[scope];
70
- const id = s.key.join('.');
71
- const set = written(file, draft, s.key) !== undefined;
72
- const unsaved = draft.has(file, s.key);
73
- const yours = effective(files.user, draft, s.key, config);
74
- const project = files.project;
75
- const inProject = project.schema.leaves().some((l) => l.key.join('.') === id)
76
- ? written(project, draft, s.key)
77
- : undefined;
34
+ function item(row, file, draft) {
35
+ const unsaved = draft.has(file, row.key);
78
36
  const paint = file.isProject ? Theme.project : Theme.changed;
79
- const value = file.isProject && !set
80
- ? Theme.muted('all projects: ') + formatValue(yours, 40)
81
- : formatValue(effective(file, draft, s.key, config));
37
+ const value = file.isProject && !row.set ? Theme.muted('all projects: ') + shown(row, row.yours, 40) : shown(row, row.value);
82
38
  const details = [
83
- Theme.muted('Setting ') + id,
39
+ Theme.muted('Setting ') + row.id,
84
40
  Theme.muted('All projects ') +
85
- formatValue(yours, 40) +
86
- Theme.muted(written(files.user, draft, s.key) === undefined ? ' (built-in default)' : ' (your file)'),
41
+ shown(row, row.yours, 40) +
42
+ Theme.muted(row.yoursSet ? ' (your file)' : ' (built-in default)'),
87
43
  Theme.muted('This project ') +
88
- (inProject === undefined ? Theme.muted('not set') : Theme.project(Text.strip(formatValue(inProject, 40)))),
89
- ...(!file.isProject ? [Theme.muted('Default ') + formatValue(file.defaultOf(s.key), 40)] : []),
44
+ (row.inProject === undefined ? Theme.muted('not set') : Theme.project(Text.strip(shown(row, row.inProject, 40)))),
45
+ ...(!file.isProject ? [Theme.muted('Default ') + shown(row, row.default, 40)] : []),
90
46
  ...(unsaved ? [Theme.warn('Unsaved: Ctrl+S saves, z undoes this change.')] : []),
91
- ...(file.isProject && NEVER_LOOSER.includes(id)
47
+ ...(file.isProject && row.locked
92
48
  ? [Theme.warn(Theme.glyph.lock + ' A project can only make this stricter than your own setting.')]
93
49
  : []),
94
50
  ];
95
51
  return {
96
- value: id,
97
- label: labelOf(s.key),
52
+ value: row.id,
53
+ label: row.label,
98
54
  hint: unsaved
99
55
  ? paint(Text.strip(value)) + ' ' + Theme.warn('*')
100
- : set
56
+ : row.set
101
57
  ? paint(Text.strip(value)) + ' ' + paint(Theme.glyph.changed)
102
58
  : value,
103
- description: s.node.description,
59
+ description: row.field.help,
104
60
  details,
105
- section: s.section,
106
- category: s.category,
107
- keywords: id,
108
- flip: s.node.editor === 'boolean',
61
+ section: row.section,
62
+ category: row.category,
63
+ keywords: row.id,
64
+ flip: row.field.kind === 'boolean',
109
65
  };
110
66
  }
67
+ /** What a setting is now for editing: the staged value, else the file's (a project), else what is in effect. */
68
+ function current(file, draft, key, config) {
69
+ const source = draftSource(draft);
70
+ return file.isProject ? (source.written(file, key) ?? _.get(config, key)) : source.effective(file, key, config);
71
+ }
111
72
  /** The row in General that opens the data folder flow (your own settings only). */
112
73
  export const DATA_FOLDER = '@data-folder';
113
74
  /** The list for the settings section. Pure (reads the files it is given). `extra` are rows that are not settings. */
114
75
  export function settingsOptions(files, scope, draft, extra = []) {
115
76
  const file = files[scope];
116
- const config = file.userConfig();
77
+ const config = files.user.userConfig();
117
78
  // The Data folder row stands in for the storage folder setting (changing it moves the data, which is asked about).
118
79
  const hideRoot = !file.isProject && extra.some((e) => e.value === DATA_FOLDER);
119
80
  const settings = settingsOf(file).filter((s) => !(hideRoot && s.key.join('.') === 'storage.root'));
120
- const ids = (test) => new Set(settings.filter(test).map((s) => s.key.join('.')));
121
- const set = ids((s) => written(file, draft, s.key) !== undefined);
122
- const unsaved = ids((s) => draft.has(file, s.key));
81
+ const rows = settings.map((s) => settingRow(s, files, scope, draftSource(draft), config));
82
+ const set = new Set(rows.filter((r) => r.set).map((r) => r.id));
83
+ const unsaved = new Set(rows.filter((r) => draft.has(file, r.key)).map((r) => r.id));
123
84
  const categories = CATEGORIES.filter((c) => settings.some((s) => s.category === c.id)).map((c) => ({
124
85
  id: c.id,
125
86
  label: c.label,
@@ -129,13 +90,17 @@ export function settingsOptions(files, scope, draft, extra = []) {
129
90
  path: [],
130
91
  status: scopeStatus(file),
131
92
  tabs: { items: SCOPES.map((x) => x.tab), active: SCOPES.findIndex((x) => x.scope === scope) },
132
- items: [...settings.map((s) => item(s, files, scope, draft, config)), ...(file.isProject ? [] : extra)],
93
+ items: [...rows.map((r) => item(r, file, draft)), ...(file.isProject ? [] : extra)],
133
94
  categories,
134
95
  actions: [
135
96
  { key: 'r', label: file.isProject ? 'remove' : 'reset to default', applies: (id) => set.has(id) },
136
97
  { key: 'z', label: 'undo', applies: (id) => unsaved.has(id) },
137
98
  ],
138
- enterLabel: (_category, id) => (id === DATA_FOLDER || id === VPN_LOGIN ? 'change' : 'edit'),
99
+ enterLabel: (_category, id) => id === DATA_FOLDER || id === VPN_LOGIN
100
+ ? 'change'
101
+ : id === EXPORT_SETTINGS || id === IMPORT_SETTINGS
102
+ ? 'open'
103
+ : 'edit',
139
104
  };
140
105
  }
141
106
  export class SettingsPage extends ListPage {
@@ -168,6 +133,7 @@ export class SettingsPage extends ListPage {
168
133
  return settingsOptions(this.files, this.scope, this.draft, [
169
134
  { value: DATA_FOLDER, category: 'general', ...row },
170
135
  { value: VPN_LOGIN, category: 'network', ...vpnLoginRow(this.ctx) },
136
+ ...shareSettingsRows().map((r) => ({ ...r, category: 'general' })),
171
137
  ]);
172
138
  }
173
139
  /** Stages a value; your own file never keeps a line that only repeats the default. */
@@ -196,6 +162,13 @@ export class SettingsPage extends ListPage {
196
162
  const exit = await new DataFolderFlow(this.app, this.ctx).run();
197
163
  return exit ? { exit } : { reload: true };
198
164
  }
165
+ if (result.value === EXPORT_SETTINGS || result.value === IMPORT_SETTINGS) {
166
+ if (result.kind !== 'pick')
167
+ return {};
168
+ const flow = new ShareSettingsFlow(this.app, this.ctx);
169
+ const notice = await (result.value === EXPORT_SETTINGS ? flow.export() : flow.import());
170
+ return { reload: true, notice };
171
+ }
199
172
  if (result.value === VPN_LOGIN) {
200
173
  if (result.kind !== 'pick')
201
174
  return {};
@@ -213,12 +186,9 @@ export class SettingsPage extends ListPage {
213
186
  return {};
214
187
  }
215
188
  try {
216
- const config = this.file.userConfig();
217
- const current = this.file.isProject
218
- ? (written(this.file, this.draft, setting.key) ?? _.get(config, setting.key))
219
- : effective(this.file, this.draft, setting.key, config);
189
+ const now = current(this.file, this.draft, setting.key, this.file.userConfig());
220
190
  const path = ['Settings', ...setting.key.slice(0, -1).map(nameOf), labelOf(setting.key)];
221
- const edited = await this.editor.edit(path, setting.key, setting.node, current);
191
+ const edited = await this.editor.edit(path, setting.key, setting.node, now);
222
192
  if (edited)
223
193
  this.stage(setting.key, edited.value);
224
194
  }
@@ -0,0 +1,28 @@
1
+ import type { Action, Page } from '../../app/kernel/index.js';
2
+ import { type ListAction, type ListItem, type ListResult } from '../tui/index.js';
3
+ /** The row that stands for the empty state. */
4
+ export declare const EMPTY_ROW = "@empty";
5
+ /** What the list returned, as the page means it. */
6
+ export type Chosen = {
7
+ readonly kind: 'refresh';
8
+ } | {
9
+ readonly kind: 'action';
10
+ readonly action: Action;
11
+ };
12
+ export interface MenuView {
13
+ readonly items: ListItem<string>[];
14
+ readonly actions: ListAction<string>[];
15
+ /** What Enter does on a row ("" = it only shows information). */
16
+ enterLabel(category: string | undefined, id: string | undefined): string;
17
+ resolve(result: ListResult<string>): Chosen | undefined;
18
+ }
19
+ export interface MenuViewOptions {
20
+ /** Every row goes in this category (a view that is one category of a page). */
21
+ readonly category?: string;
22
+ /** Put before every row's value, so the rows cannot clash with the page's own rows. */
23
+ readonly prefix?: string;
24
+ /** Offer l to load the data again (default: yes). */
25
+ readonly refresh?: boolean;
26
+ }
27
+ /** The menu's rows, keys and Enter labels for a page. */
28
+ export declare function menuView(page: Page, o?: MenuViewOptions): MenuView;
@@ -0,0 +1,112 @@
1
+ import { pageHasContent } from '../../app/kernel/index.js';
2
+ import { safeText } from '../../core/util/index.js';
3
+ import { Theme } from '../tui/index.js';
4
+ /** The row that stands for the empty state. */
5
+ export const EMPTY_ROW = '@empty';
6
+ const FACT = 'fact:';
7
+ const ACTION = 'action:';
8
+ const REFRESH_KEY = 'l';
9
+ const PAINT = {
10
+ ok: Theme.ok,
11
+ warn: Theme.warn,
12
+ danger: Theme.error,
13
+ muted: Theme.muted,
14
+ accent: Theme.accent,
15
+ };
16
+ const paint = (text, tone) => text === undefined ? undefined : tone ? PAINT[tone](safeText(text)) : safeText(text);
17
+ /** The menu's rows, keys and Enter labels for a page. */
18
+ export function menuView(page, o = {}) {
19
+ const category = o.category ? { category: o.category } : {};
20
+ const prefix = o.prefix ?? '';
21
+ const items = [];
22
+ /** The action Enter runs, by row. */
23
+ const enter = new Map();
24
+ /** Key actions that apply to one row, by row. */
25
+ const keyed = new Map();
26
+ /** Key actions that apply everywhere. */
27
+ const everywhere = [...(page.actions ?? [])];
28
+ if (!pageHasContent(page) && page.empty) {
29
+ const e = page.empty;
30
+ items.push({
31
+ value: prefix + EMPTY_ROW,
32
+ label: e.action?.label ?? e.text,
33
+ section: e.title,
34
+ description: e.text,
35
+ ...category,
36
+ });
37
+ if (e.action)
38
+ enter.set(prefix + EMPTY_ROW, e.action);
39
+ }
40
+ for (const section of page.sections) {
41
+ const heading = section.title ?? page.title;
42
+ for (const f of section.facts ?? [])
43
+ items.push({
44
+ value: prefix + FACT + f.id,
45
+ label: f.label,
46
+ hint: paint(f.value, f.tone),
47
+ description: f.description,
48
+ details: f.details?.map(safeText),
49
+ section: heading,
50
+ ...category,
51
+ });
52
+ for (const item of section.items ?? []) {
53
+ const id = prefix + item.id;
54
+ items.push({
55
+ value: id,
56
+ label: safeText(item.label),
57
+ hint: paint(item.hint, item.tone),
58
+ description: item.description,
59
+ details: item.details?.map(safeText),
60
+ section: item.group ?? heading,
61
+ keywords: item.keywords,
62
+ ...(item.disabled ? { disabled: item.disabled } : {}),
63
+ ...category,
64
+ });
65
+ if (item.primary && !item.disabled)
66
+ enter.set(id, item.primary);
67
+ if (item.actions?.length)
68
+ keyed.set(id, [...item.actions]);
69
+ }
70
+ (section.actions ?? []).forEach((a, i) => {
71
+ const id = prefix + ACTION + section.id + ':' + i;
72
+ items.push({ value: id, label: a.label, hint: '', description: a.description, section: 'Actions', ...category });
73
+ enter.set(id, a);
74
+ everywhere.push(a);
75
+ });
76
+ }
77
+ const keys = [];
78
+ const addKey = (a, applies) => {
79
+ if (!a.key || keys.some((k) => k.key === a.key && k.label === (a.verb ?? a.label)))
80
+ return;
81
+ keys.push({ key: a.key, label: a.verb ?? a.label, importance: a.importance ?? 2, ...(applies ? { applies } : {}) });
82
+ };
83
+ for (const a of everywhere)
84
+ addKey(a);
85
+ for (const list of keyed.values())
86
+ for (const a of list)
87
+ addKey(a, (row) => keyed.get(row)?.some((x) => x.key === a.key) ?? false);
88
+ if (o.refresh !== false)
89
+ keys.push({ key: REFRESH_KEY, label: 'refresh', importance: 3 });
90
+ return {
91
+ items,
92
+ actions: keys,
93
+ enterLabel: (_category, id) => {
94
+ const a = id === undefined ? undefined : enter.get(id);
95
+ return a ? (a.verb ?? a.label.toLowerCase()) : '';
96
+ },
97
+ resolve: (result) => {
98
+ if (result.kind === 'pick') {
99
+ const a = enter.get(result.value);
100
+ return a ? { kind: 'action', action: a } : undefined;
101
+ }
102
+ if (result.kind !== 'action')
103
+ return undefined;
104
+ if (result.key === REFRESH_KEY && o.refresh !== false)
105
+ return { kind: 'refresh' };
106
+ const own = result.value === undefined ? undefined : keyed.get(result.value)?.find((a) => a.key === result.key);
107
+ const shared = everywhere.find((a) => a.key === result.key);
108
+ const a = own ?? shared;
109
+ return a ? { kind: 'action', action: a } : undefined;
110
+ },
111
+ };
112
+ }
@@ -1,11 +1,5 @@
1
+ import type { PathKind } from '../../../app/kernel/index.js';
1
2
  import type { App } from '../../tui/index.js';
2
- import type { SchemaNode } from './schema-node.js';
3
- /** A folder or a file. */
4
- export type PathKind = 'dir' | 'file';
5
- /** The kind of path a setting holds (its key without list positions), or undefined when it is not a path. */
6
- export declare function pathKindOf(key: readonly string[]): PathKind | undefined;
7
- /** Words that are fine instead of a path ("auto"), read from the setting's description. */
8
- export declare function pathLiterals(node: SchemaNode): string[];
9
3
  export interface AskPathOptions {
10
4
  readonly path: string[];
11
5
  readonly title: string;
@@ -0,0 +1,14 @@
1
+ /** Picks a folder or file in the path picker; typing a path (or an allowed word) still works. */
2
+ export function askPath(app, o) {
3
+ return app.pickPath({
4
+ path: o.path,
5
+ title: o.title,
6
+ about: o.about,
7
+ initial: o.initial,
8
+ files: o.kind === 'file',
9
+ use: o.kind === 'file' ? 'use this file' : 'use this folder',
10
+ literals: o.literals,
11
+ relative: true,
12
+ validate: o.validate,
13
+ });
14
+ }
@@ -1,5 +1,5 @@
1
1
  import { type App, type ListItem } from '../../../tui/index.js';
2
- import { type SchemaNode } from '../schema-node.js';
2
+ import type { SchemaNode } from '../../../../app/kernel/index.js';
3
3
  import type { ValueEditor, Edited } from '../value-editor.js';
4
4
  /**
5
5
  * The shared shape of the list and map editors: a working copy shown as a list with "Add" at the top, Enter to change
@@ -1,7 +1,6 @@
1
1
  import type { App, ListItem } from '../../../tui/index.js';
2
2
  import { CollectionEditor } from './collection-editor.js';
3
- import { type PathKind } from '../path-kind.js';
4
- import type { SchemaNode } from '../schema-node.js';
3
+ import type { PathKind, SchemaNode } from '../../../../app/kernel/index.js';
5
4
  import type { ValueEditor } from '../value-editor.js';
6
5
  /** A list of strings (project roots, scripts, extra packages ...). */
7
6
  export declare class StringListEditor extends CollectionEditor<string[]> {
@@ -1,6 +1,6 @@
1
1
  import { Theme } from '../../../tui/index.js';
2
2
  import { CollectionEditor } from './collection-editor.js';
3
- import { askPath } from '../path-kind.js';
3
+ import { askPath } from '../ask-path.js';
4
4
  /** A list of strings (project roots, scripts, extra packages ...). */
5
5
  export class StringListEditor extends CollectionEditor {
6
6
  pathKind;
@@ -1,4 +1,4 @@
1
- import type { ConfigFile, SettingChange } from './config-file.js';
1
+ import type { ConfigFile, SettingChange } from '../../../app/features/settings/index.js';
2
2
  /** What saving did: how many settings were written and why a file was refused. */
3
3
  export interface DraftResult {
4
4
  readonly saved: number;
@@ -1,5 +1,3 @@
1
+ export { choiceLabel, summary } from '../../../app/kernel/index.js';
1
2
  /** A value in one short line, for the right-hand column. */
2
3
  export declare function formatValue(value: unknown, max?: number): string;
3
- /** A one-line name for an object in a list (a mapping's folders, an agent's package ...). */
4
- export declare function summary(value: unknown): string;
5
- export declare function choiceLabel(v: unknown): string;
@@ -1,39 +1,9 @@
1
- import _ from 'lodash';
2
- import { Text, Theme } from '../../tui/index.js';
1
+ // A value in one short line for the menu's right-hand column: the kernel's valueText, painted with the theme.
2
+ import { valueText } from '../../../app/kernel/index.js';
3
+ import { Theme } from '../../tui/index.js';
4
+ export { choiceLabel, summary } from '../../../app/kernel/index.js';
3
5
  /** A value in one short line, for the right-hand column. */
4
6
  export function formatValue(value, max = 60) {
5
- if (value === undefined)
6
- return Theme.muted('not set');
7
- if (value === true)
8
- return Theme.ok('On');
9
- if (value === false)
10
- return Theme.muted('Off');
11
- if (value === null)
12
- return Theme.muted('none');
13
- if (typeof value === 'string')
14
- return value === '' ? Theme.muted('(empty)') : Text.truncate(value, max);
15
- if (typeof value === 'number')
16
- return String(value);
17
- if (Array.isArray(value))
18
- return value.length === 0
19
- ? Theme.muted('none')
20
- : Text.truncate(value.length + ' · ' + value.map((v) => (typeof v === 'object' ? summary(v) : String(v))).join(', '), max);
21
- const keys = Object.keys(value);
22
- return keys.length ? Text.truncate(keys.length + ' · ' + keys.join(', '), max) : Theme.muted('none');
23
- }
24
- /** A one-line name for an object in a list (a mapping's folders, an agent's package ...). */
25
- export function summary(value) {
26
- if (!_.isPlainObject(value))
27
- return String(value);
28
- const o = value;
29
- const strings = Object.entries(o).filter(([, v]) => typeof v === 'string' && v);
30
- if (typeof o.host === 'string' && typeof o.guest === 'string')
31
- return o.host + ' → ' + o.guest + (o.readOnly ? ' (read-only)' : '');
32
- return (strings
33
- .slice(0, 2)
34
- .map(([, v]) => v)
35
- .join(' · ') || '{ ' + Object.keys(o).join(', ') + ' }');
36
- }
37
- export function choiceLabel(v) {
38
- return v === true ? 'On' : v === false ? 'Off' : String(v);
7
+ const shown = valueText(value, undefined, max);
8
+ return shown.tone === 'ok' ? Theme.ok(shown.text) : shown.tone === 'muted' ? Theme.muted(shown.text) : shown.text;
39
9
  }