windows-development-sandbox 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (448) hide show
  1. package/README.md +1 -0
  2. package/config.example.json +17 -1
  3. package/dist/cli/args.d.ts +13 -2
  4. package/dist/cli/args.js +95 -24
  5. package/dist/cli/commands/accounts.js +45 -17
  6. package/dist/cli/commands/context.d.ts +2 -0
  7. package/dist/cli/commands/context.js +9 -2
  8. package/dist/cli/commands/data/clean.js +32 -9
  9. package/dist/cli/commands/index.d.ts +1 -0
  10. package/dist/cli/commands/index.js +1 -0
  11. package/dist/cli/commands/launch/add-root.d.ts +3 -2
  12. package/dist/cli/commands/launch/add-root.js +11 -10
  13. package/dist/cli/commands/launch/launch.js +57 -4
  14. package/dist/cli/commands/launch/prepare.js +3 -1
  15. package/dist/cli/commands/maintenance/audit.js +1 -1
  16. package/dist/cli/commands/maintenance/clean.d.ts +1 -1
  17. package/dist/cli/commands/maintenance/clean.js +17 -3
  18. package/dist/cli/commands/maintenance/config.d.ts +1 -1
  19. package/dist/cli/commands/maintenance/config.js +112 -10
  20. package/dist/cli/commands/maintenance/doctor.d.ts +1 -1
  21. package/dist/cli/commands/maintenance/doctor.js +2 -4
  22. package/dist/cli/commands/maintenance/migrate.d.ts +2 -1
  23. package/dist/cli/commands/maintenance/migrate.js +19 -4
  24. package/dist/cli/commands/maintenance/sync.js +2 -0
  25. package/dist/cli/commands/maintenance/update.d.ts +2 -0
  26. package/dist/cli/commands/maintenance/update.js +27 -4
  27. package/dist/cli/commands/projects/here.js +12 -7
  28. package/dist/cli/commands/projects/projects.js +26 -17
  29. package/dist/cli/commands/registry.js +7 -0
  30. package/dist/cli/commands/repair/checks/agents.d.ts +2 -0
  31. package/dist/cli/commands/repair/checks/agents.js +70 -0
  32. package/dist/cli/commands/repair/checks/common.d.ts +8 -0
  33. package/dist/cli/commands/repair/checks/common.js +22 -0
  34. package/dist/cli/commands/repair/checks/data.d.ts +2 -0
  35. package/dist/cli/commands/repair/checks/data.js +241 -0
  36. package/dist/cli/commands/repair/checks/index.d.ts +2 -0
  37. package/dist/cli/commands/repair/checks/index.js +20 -0
  38. package/dist/cli/commands/repair/checks/install.d.ts +5 -0
  39. package/dist/cli/commands/repair/checks/install.js +318 -0
  40. package/dist/cli/commands/repair/checks/machine.d.ts +2 -0
  41. package/dist/cli/commands/repair/checks/machine.js +232 -0
  42. package/dist/cli/commands/repair/checks/network.d.ts +6 -0
  43. package/dist/cli/commands/repair/checks/network.js +60 -0
  44. package/dist/cli/commands/repair/checks/pc.d.ts +4 -0
  45. package/dist/cli/commands/repair/checks/pc.js +55 -0
  46. package/dist/cli/commands/repair/checks/sandbox.d.ts +2 -0
  47. package/dist/cli/commands/repair/checks/sandbox.js +267 -0
  48. package/dist/cli/commands/repair/checks/settings.d.ts +2 -0
  49. package/dist/cli/commands/repair/checks/settings.js +169 -0
  50. package/dist/cli/commands/repair/checks/vpn.d.ts +2 -0
  51. package/dist/cli/commands/repair/checks/vpn.js +53 -0
  52. package/dist/cli/commands/repair/engine.d.ts +32 -0
  53. package/dist/cli/commands/repair/engine.js +127 -0
  54. package/dist/cli/commands/repair/env.d.ts +7 -0
  55. package/dist/cli/commands/repair/env.js +49 -0
  56. package/dist/cli/commands/repair/index.d.ts +1 -9
  57. package/dist/cli/commands/repair/index.js +2 -54
  58. package/dist/cli/commands/repair/log.d.ts +17 -0
  59. package/dist/cli/commands/repair/log.js +82 -0
  60. package/dist/cli/commands/repair/printer.d.ts +32 -0
  61. package/dist/cli/commands/repair/printer.js +132 -0
  62. package/dist/cli/commands/repair/run.d.ts +33 -0
  63. package/dist/cli/commands/repair/run.js +133 -0
  64. package/dist/cli/commands/repair/types.d.ts +97 -9
  65. package/dist/cli/commands/repair/types.js +11 -7
  66. package/dist/cli/commands/resume.js +2 -2
  67. package/dist/cli/commands/sessions/list.js +5 -2
  68. package/dist/cli/commands/sessions/status.d.ts +2 -0
  69. package/dist/cli/commands/sessions/status.js +27 -6
  70. package/dist/cli/commands/setup/index.d.ts +5 -0
  71. package/dist/cli/commands/setup/index.js +25 -1
  72. package/dist/cli/commands/storage/index.d.ts +2 -0
  73. package/dist/cli/commands/storage/index.js +2 -0
  74. package/dist/cli/commands/storage/offer.d.ts +3 -0
  75. package/dist/cli/commands/storage/offer.js +85 -0
  76. package/dist/cli/commands/storage/storage.d.ts +8 -0
  77. package/dist/cli/commands/storage/storage.js +171 -0
  78. package/dist/cli/commands/user-config.js +1 -1
  79. package/dist/cli/commands/vpn.d.ts +4 -0
  80. package/dist/cli/commands/vpn.js +75 -0
  81. package/dist/cli/commands/web.d.ts +4 -0
  82. package/dist/cli/commands/web.js +95 -0
  83. package/dist/cli/history.js +1 -1
  84. package/dist/cli/index.js +46 -3
  85. package/dist/cli/manifest.d.ts +303 -29
  86. package/dist/cli/manifest.js +173 -43
  87. package/dist/cli/menu/add-agent.d.ts +47 -0
  88. package/dist/cli/menu/add-agent.js +168 -0
  89. package/dist/cli/menu/apps.d.ts +31 -2
  90. package/dist/cli/menu/apps.js +44 -5
  91. package/dist/cli/menu/command-fields.d.ts +18 -0
  92. package/dist/cli/menu/command-fields.js +61 -0
  93. package/dist/cli/menu/command-form.d.ts +22 -0
  94. package/dist/cli/menu/command-form.js +169 -46
  95. package/dist/cli/menu/data-folder.d.ts +18 -0
  96. package/dist/cli/menu/data-folder.js +99 -0
  97. package/dist/cli/menu/menu.d.ts +1 -1
  98. package/dist/cli/menu/menu.js +11 -8
  99. package/dist/cli/menu/pages/accounts-page.js +55 -24
  100. package/dist/cli/menu/pages/agents-page.d.ts +66 -0
  101. package/dist/cli/menu/pages/agents-page.js +338 -0
  102. package/dist/cli/menu/pages/apps-page.js +1 -2
  103. package/dist/cli/menu/pages/list-page.d.ts +16 -3
  104. package/dist/cli/menu/pages/list-page.js +40 -8
  105. package/dist/cli/menu/pages/maintenance-page.d.ts +9 -0
  106. package/dist/cli/menu/pages/maintenance-page.js +161 -72
  107. package/dist/cli/menu/pages/sandbox-page.d.ts +3 -0
  108. package/dist/cli/menu/pages/sandbox-page.js +132 -43
  109. package/dist/cli/menu/pages/sessions-page.d.ts +4 -0
  110. package/dist/cli/menu/pages/sessions-page.js +41 -25
  111. package/dist/cli/menu/pages/settings-page.d.ts +8 -4
  112. package/dist/cli/menu/pages/settings-page.js +36 -13
  113. package/dist/cli/menu/settings/categories.d.ts +2 -0
  114. package/dist/cli/menu/settings/categories.js +19 -44
  115. package/dist/cli/menu/settings/collections/collection-editor.d.ts +7 -1
  116. package/dist/cli/menu/settings/collections/collection-editor.js +12 -8
  117. package/dist/cli/menu/settings/collections/map-editor.js +5 -1
  118. package/dist/cli/menu/settings/collections/object-list-editor.js +2 -2
  119. package/dist/cli/menu/settings/collections/object-map-editor.js +2 -2
  120. package/dist/cli/menu/settings/collections/string-list-editor.d.ts +11 -1
  121. package/dist/cli/menu/settings/collections/string-list-editor.js +23 -0
  122. package/dist/cli/menu/settings/index.d.ts +1 -1
  123. package/dist/cli/menu/settings/index.js +1 -1
  124. package/dist/cli/menu/settings/object-editor.d.ts +5 -1
  125. package/dist/cli/menu/settings/object-editor.js +15 -4
  126. package/dist/cli/menu/settings/path-kind.d.ts +20 -0
  127. package/dist/cli/menu/settings/path-kind.js +44 -0
  128. package/dist/cli/menu/settings/schema-node.d.ts +9 -0
  129. package/dist/cli/menu/settings/schema-node.js +26 -0
  130. package/dist/cli/menu/settings/value-editor.d.ts +5 -0
  131. package/dist/cli/menu/settings/value-editor.js +51 -18
  132. package/dist/cli/menu/several.js +7 -5
  133. package/dist/cli/menu/shell.d.ts +37 -10
  134. package/dist/cli/menu/shell.js +90 -10
  135. package/dist/cli/menu/vpn-login.d.ts +16 -0
  136. package/dist/cli/menu/vpn-login.js +66 -0
  137. package/dist/cli/output/print.d.ts +2 -0
  138. package/dist/cli/output/print.js +8 -1
  139. package/dist/cli/services/accounts-service.d.ts +2 -0
  140. package/dist/cli/services/accounts-service.js +2 -2
  141. package/dist/cli/services/health-service.d.ts +0 -6
  142. package/dist/cli/services/health-service.js +5 -42
  143. package/dist/cli/services/index.d.ts +3 -1
  144. package/dist/cli/services/index.js +3 -1
  145. package/dist/cli/services/migrate-service.js +1 -1
  146. package/dist/cli/services/update-service.d.ts +2 -0
  147. package/dist/cli/services/update-service.js +13 -6
  148. package/dist/cli/services/vpn-service.d.ts +28 -0
  149. package/dist/cli/services/vpn-service.js +46 -0
  150. package/dist/cli/services/web-service.d.ts +22 -0
  151. package/dist/cli/services/web-service.js +111 -0
  152. package/dist/cli/tui/app.d.ts +6 -3
  153. package/dist/cli/tui/app.js +12 -5
  154. package/dist/cli/tui/dialogs/path-listing.d.ts +43 -0
  155. package/dist/cli/tui/dialogs/path-listing.js +127 -0
  156. package/dist/cli/tui/dialogs/path-picker.d.ts +67 -0
  157. package/dist/cli/tui/dialogs/path-picker.js +423 -0
  158. package/dist/cli/tui/dialogs/text-screen.d.ts +6 -0
  159. package/dist/cli/tui/dialogs/text-screen.js +10 -3
  160. package/dist/cli/tui/frame.d.ts +7 -10
  161. package/dist/cli/tui/frame.js +19 -10
  162. package/dist/cli/tui/index.d.ts +4 -3
  163. package/dist/cli/tui/index.js +2 -1
  164. package/dist/cli/tui/list/list-screen.d.ts +14 -2
  165. package/dist/cli/tui/list/list-screen.js +68 -32
  166. package/dist/cli/tui/list/list-types.d.ts +8 -4
  167. package/dist/cli/tui/nav.d.ts +29 -0
  168. package/dist/cli/tui/nav.js +67 -0
  169. package/dist/cli/validate.d.ts +1 -1
  170. package/dist/cli/validate.js +3 -3
  171. package/dist/cli/version.d.ts +2 -0
  172. package/dist/cli/version.js +11 -0
  173. package/dist/core/config/defaults.js +5 -1
  174. package/dist/core/config/loader.js +12 -2
  175. package/dist/core/local-config/overrides.js +1 -0
  176. package/dist/core/prompt.d.ts +19 -0
  177. package/dist/core/prompt.js +43 -3
  178. package/dist/core/schema/common.d.ts +6 -0
  179. package/dist/core/schema/common.js +8 -0
  180. package/dist/core/schema/config.d.ts +23 -0
  181. package/dist/core/schema/config.js +10 -1
  182. package/dist/core/schema/harness.d.ts +1 -0
  183. package/dist/core/schema/harness.js +8 -2
  184. package/dist/core/schema/index.d.ts +3 -1
  185. package/dist/core/schema/index.js +3 -1
  186. package/dist/core/schema/profile.d.ts +1 -0
  187. package/dist/core/schema/profile.js +3 -0
  188. package/dist/core/schema/redact.d.ts +13 -0
  189. package/dist/core/schema/redact.js +75 -0
  190. package/dist/core/schema/sandbox.js +1 -0
  191. package/dist/core/schema/vpn.d.ts +17 -0
  192. package/dist/core/schema/vpn.js +22 -0
  193. package/dist/core/schema/web.d.ts +6 -0
  194. package/dist/core/schema/web.js +14 -0
  195. package/dist/core/types.d.ts +26 -0
  196. package/dist/guest/agents/agent-files.d.ts +49 -0
  197. package/dist/guest/agents/agent-files.js +63 -0
  198. package/dist/guest/agents/agent-installer.js +2 -9
  199. package/dist/guest/agents/local-state.d.ts +12 -0
  200. package/dist/guest/agents/local-state.js +198 -0
  201. package/dist/guest/agents/sbx-agent.d.ts +2 -0
  202. package/dist/guest/agents/sbx-agent.js +35 -0
  203. package/dist/guest/boot/boot.d.ts +4 -0
  204. package/dist/guest/boot/boot.js +50 -11
  205. package/dist/guest/daemon/daemon.js +4 -1
  206. package/dist/guest/daemon/exec.js +2 -1
  207. package/dist/guest/daemon/idle-shutdown.js +2 -0
  208. package/dist/guest/daemon/sessions.js +4 -1
  209. package/dist/guest/entries/agent.d.ts +1 -0
  210. package/dist/guest/entries/agent.js +10 -0
  211. package/dist/guest/entries/daemon.js +2 -0
  212. package/dist/guest/runtime/csc.d.ts +1 -0
  213. package/dist/guest/runtime/csc.js +1 -0
  214. package/dist/guest/runtime/secrets.d.ts +12 -0
  215. package/dist/guest/runtime/secrets.js +63 -0
  216. package/dist/guest/setup/claude-profile.d.ts +2 -1
  217. package/dist/guest/setup/claude-profile.js +12 -1
  218. package/dist/guest/setup/environment.d.ts +6 -0
  219. package/dist/guest/setup/environment.js +56 -6
  220. package/dist/guest/setup/path-plan.d.ts +32 -0
  221. package/dist/guest/setup/path-plan.js +67 -0
  222. package/dist/guest/setup/shell-path.d.ts +1 -0
  223. package/dist/guest/setup/shell-path.js +22 -0
  224. package/dist/guest/toolchains/languages/python.js +1 -1
  225. package/dist/guest/toolchains/runtimes.js +2 -2
  226. package/dist/guest/vpn/index.d.ts +1 -0
  227. package/dist/guest/vpn/index.js +1 -0
  228. package/dist/guest/vpn/openvpn.d.ts +9 -0
  229. package/dist/guest/vpn/openvpn.js +47 -0
  230. package/dist/guest/vpn/tunnel.d.ts +17 -0
  231. package/dist/guest/vpn/tunnel.js +78 -0
  232. package/dist/guest/vpn/vpn.d.ts +6 -0
  233. package/dist/guest/vpn/vpn.js +126 -0
  234. package/dist/host/channel/attach.d.ts +3 -1
  235. package/dist/host/channel/attach.js +1 -1
  236. package/dist/host/channel/channel.d.ts +7 -0
  237. package/dist/host/channel/channel.js +26 -2
  238. package/dist/host/health/acl-read.d.ts +17 -0
  239. package/dist/host/health/acl-read.js +45 -0
  240. package/dist/host/health/config-backup.d.ts +11 -0
  241. package/dist/host/health/config-backup.js +73 -0
  242. package/dist/host/health/index.d.ts +6 -0
  243. package/dist/host/health/index.js +7 -0
  244. package/dist/host/health/install-state.d.ts +33 -0
  245. package/dist/host/health/install-state.js +110 -0
  246. package/dist/host/health/path-copies.d.ts +32 -0
  247. package/dist/host/health/path-copies.js +82 -0
  248. package/dist/host/health/stale.d.ts +45 -0
  249. package/dist/host/health/stale.js +120 -0
  250. package/dist/host/health/system.d.ts +37 -0
  251. package/dist/host/health/system.js +87 -0
  252. package/dist/host/install/copies.d.ts +13 -0
  253. package/dist/host/install/copies.js +63 -0
  254. package/dist/host/install/index.d.ts +1 -0
  255. package/dist/host/install/index.js +1 -0
  256. package/dist/host/launch/generate.js +25 -1
  257. package/dist/host/launch/session-file.js +4 -1
  258. package/dist/host/launch/stage.js +1 -1
  259. package/dist/host/migrate/backup.js +3 -0
  260. package/dist/host/migrate/migrations/config-file.d.ts +2 -0
  261. package/dist/host/migrate/migrations/config-file.js +7 -0
  262. package/dist/host/migrate/migrations/follow-machine-storage.js +9 -1
  263. package/dist/host/migrate/migrations/pin-storage-root.d.ts +4 -0
  264. package/dist/host/migrate/migrations/pin-storage-root.js +46 -0
  265. package/dist/host/migrate/migrations/unpin-config-defaults.js +4 -1
  266. package/dist/host/migrate/registry.js +2 -1
  267. package/dist/host/profile/accounts.d.ts +25 -1
  268. package/dist/host/profile/accounts.js +96 -13
  269. package/dist/host/profile/agent-accounts.d.ts +5 -1
  270. package/dist/host/profile/agent-accounts.js +35 -5
  271. package/dist/host/profile/index.d.ts +2 -1
  272. package/dist/host/profile/index.js +1 -0
  273. package/dist/host/profile/plugin-share.d.ts +26 -0
  274. package/dist/host/profile/plugin-share.js +146 -0
  275. package/dist/host/projects/join.d.ts +7 -1
  276. package/dist/host/projects/join.js +21 -4
  277. package/dist/host/projects/registry.d.ts +16 -10
  278. package/dist/host/projects/registry.js +74 -6
  279. package/dist/host/sandbox/platform.d.ts +2 -0
  280. package/dist/host/sandbox/platform.js +21 -2
  281. package/dist/host/secrets/index.d.ts +2 -0
  282. package/dist/host/secrets/index.js +2 -0
  283. package/dist/host/secrets/launch-secrets.d.ts +17 -0
  284. package/dist/host/secrets/launch-secrets.js +93 -0
  285. package/dist/host/secrets/secret-store.d.ts +25 -0
  286. package/dist/host/secrets/secret-store.js +89 -0
  287. package/dist/host/sharing/acl.d.ts +16 -2
  288. package/dist/host/sharing/acl.js +35 -10
  289. package/dist/host/sharing/current.js +11 -2
  290. package/dist/host/sharing/doctor.d.ts +2 -0
  291. package/dist/host/sharing/doctor.js +44 -0
  292. package/dist/host/sharing/group-access.d.ts +6 -0
  293. package/dist/host/sharing/group-access.js +24 -0
  294. package/dist/host/sharing/index.d.ts +2 -1
  295. package/dist/host/sharing/index.js +2 -1
  296. package/dist/host/sharing/lifecycle.d.ts +8 -2
  297. package/dist/host/sharing/lifecycle.js +16 -7
  298. package/dist/host/storage/cleanup.d.ts +2 -0
  299. package/dist/host/storage/cleanup.js +2 -1
  300. package/dist/host/storage/history.d.ts +21 -0
  301. package/dist/host/storage/history.js +49 -0
  302. package/dist/host/storage/index.d.ts +4 -0
  303. package/dist/host/storage/index.js +4 -0
  304. package/dist/host/storage/move.d.ts +95 -0
  305. package/dist/host/storage/move.js +384 -0
  306. package/dist/host/storage/offer.d.ts +11 -0
  307. package/dist/host/storage/offer.js +36 -0
  308. package/dist/host/storage/root.d.ts +18 -0
  309. package/dist/host/storage/root.js +77 -0
  310. package/dist/host/tools/windows-terminal.d.ts +2 -0
  311. package/dist/host/tools/windows-terminal.js +23 -5
  312. package/dist/host/update/index.d.ts +1 -1
  313. package/dist/host/update/index.js +1 -1
  314. package/dist/host/update/notice.d.ts +2 -0
  315. package/dist/host/update/notice.js +13 -1
  316. package/dist/host/update/update-state.d.ts +6 -0
  317. package/dist/host/update/update-state.js +2 -0
  318. package/dist/host/update/updater.d.ts +19 -0
  319. package/dist/host/update/updater.js +88 -12
  320. package/dist/host/vpn/index.d.ts +4 -0
  321. package/dist/host/vpn/index.js +4 -0
  322. package/dist/host/vpn/login.d.ts +24 -0
  323. package/dist/host/vpn/login.js +79 -0
  324. package/dist/host/vpn/nordvpn.d.ts +21 -0
  325. package/dist/host/vpn/nordvpn.js +77 -0
  326. package/dist/host/vpn/prepare.d.ts +17 -0
  327. package/dist/host/vpn/prepare.js +77 -0
  328. package/dist/host/vpn/status.d.ts +12 -0
  329. package/dist/host/vpn/status.js +53 -0
  330. package/dist/host/web/auth.d.ts +38 -0
  331. package/dist/host/web/auth.js +96 -0
  332. package/dist/host/web/backend.d.ts +14 -0
  333. package/dist/host/web/backend.js +1 -0
  334. package/dist/host/web/bridge.d.ts +27 -0
  335. package/dist/host/web/bridge.js +144 -0
  336. package/dist/host/web/browser.d.ts +1 -0
  337. package/dist/host/web/browser.js +10 -0
  338. package/dist/host/web/control.d.ts +17 -0
  339. package/dist/host/web/control.js +67 -0
  340. package/dist/host/web/index.d.ts +9 -0
  341. package/dist/host/web/index.js +8 -0
  342. package/dist/host/web/runner.d.ts +14 -0
  343. package/dist/host/web/runner.js +45 -0
  344. package/dist/host/web/server.d.ts +40 -0
  345. package/dist/host/web/server.js +218 -0
  346. package/dist/host/web/state.d.ts +20 -0
  347. package/dist/host/web/state.js +41 -0
  348. package/dist/host/web/static-files.d.ts +13 -0
  349. package/dist/host/web/static-files.js +57 -0
  350. package/dist/runtime/agent.js +27614 -0
  351. package/dist/runtime/daemon.js +1012 -618
  352. package/dist/runtime/db.js +236 -106
  353. package/dist/runtime/main.js +1093 -250
  354. package/dist/runtime/netproxy.js +102 -3
  355. package/dist/runtime/view.js +102 -3
  356. package/dist/selftest/accounts.test.js +40 -0
  357. package/dist/selftest/agent-paths.test.d.ts +2 -0
  358. package/dist/selftest/agent-paths.test.js +115 -0
  359. package/dist/selftest/channel.test.js +5 -0
  360. package/dist/selftest/cli.test.js +34 -4
  361. package/dist/selftest/credentials.test.d.ts +2 -0
  362. package/dist/selftest/credentials.test.js +198 -0
  363. package/dist/selftest/generate.test.js +1 -1
  364. package/dist/selftest/health.test.d.ts +2 -0
  365. package/dist/selftest/health.test.js +470 -0
  366. package/dist/selftest/host-sim.test.js +2 -0
  367. package/dist/selftest/index.js +19 -0
  368. package/dist/selftest/layout.test.js +2 -0
  369. package/dist/selftest/menu-screens.d.ts +7 -0
  370. package/dist/selftest/menu-screens.js +65 -16
  371. package/dist/selftest/menu.test.js +574 -21
  372. package/dist/selftest/migrate.test.js +1 -1
  373. package/dist/selftest/personalization.test.js +3 -1
  374. package/dist/selftest/projects.test.js +39 -1
  375. package/dist/selftest/shared-use.test.js +64 -2
  376. package/dist/selftest/sharing.test.js +50 -1
  377. package/dist/selftest/storage.test.d.ts +2 -0
  378. package/dist/selftest/storage.test.js +363 -0
  379. package/dist/selftest/support/fixtures.d.ts +14 -0
  380. package/dist/selftest/support/releases.js +1 -1
  381. package/dist/selftest/toolchains.test.js +21 -0
  382. package/dist/selftest/update-service.test.js +87 -3
  383. package/dist/selftest/vpn.test.d.ts +2 -0
  384. package/dist/selftest/vpn.test.js +279 -0
  385. package/dist/selftest/web.test.d.ts +2 -0
  386. package/dist/selftest/web.test.js +304 -0
  387. package/dist/shared/apps/catalogue.js +31 -0
  388. package/dist/shared/channel/index.d.ts +1 -0
  389. package/dist/shared/channel/index.js +1 -0
  390. package/dist/shared/channel/run-dir.d.ts +6 -1
  391. package/dist/shared/channel/run-dir.js +12 -1
  392. package/dist/shared/channel/schema.d.ts +19 -0
  393. package/dist/shared/channel/schema.js +11 -0
  394. package/dist/shared/channel/secrets.d.ts +28 -0
  395. package/dist/shared/channel/secrets.js +44 -0
  396. package/dist/shared/vpn/download.d.ts +13 -0
  397. package/dist/shared/vpn/download.js +14 -0
  398. package/dist/shared/vpn/kill-switch.d.ts +18 -0
  399. package/dist/shared/vpn/kill-switch.js +81 -0
  400. package/dist/shared/vpn/ovpn.d.ts +23 -0
  401. package/dist/shared/vpn/ovpn.js +244 -0
  402. package/dist/shared/web/index.d.ts +1 -0
  403. package/dist/shared/web/index.js +1 -0
  404. package/dist/shared/web/protocol.d.ts +61 -0
  405. package/dist/shared/web/protocol.js +35 -0
  406. package/dist/sync/sync-cycle.js +5 -1
  407. package/dist/sync/sync-state.d.ts +1 -0
  408. package/dist/sync/sync-state.js +1 -0
  409. package/dist/sync/watcher.js +10 -0
  410. package/dist/web/index.html +13 -0
  411. package/dist/web/main-CXESL347.js +45 -0
  412. package/dist/web/main-VLR7DVQ4.css +1 -0
  413. package/native/csharp/FolderSecurity.cs +29 -0
  414. package/native/scripts/protect-data.ps1 +29 -0
  415. package/native/scripts/read-folder-sddl.ps1 +10 -0
  416. package/native/scripts/set-folder-access.ps1 +49 -7
  417. package/node_modules/ws/LICENSE +20 -0
  418. package/node_modules/ws/README.md +548 -0
  419. package/node_modules/ws/browser.js +8 -0
  420. package/node_modules/ws/index.js +22 -0
  421. package/node_modules/ws/lib/buffer-util.js +131 -0
  422. package/node_modules/ws/lib/constants.js +19 -0
  423. package/node_modules/ws/lib/event-target.js +292 -0
  424. package/node_modules/ws/lib/extension.js +203 -0
  425. package/node_modules/ws/lib/limiter.js +55 -0
  426. package/node_modules/ws/lib/permessage-deflate.js +530 -0
  427. package/node_modules/ws/lib/receiver.js +743 -0
  428. package/node_modules/ws/lib/sender.js +607 -0
  429. package/node_modules/ws/lib/stream.js +161 -0
  430. package/node_modules/ws/lib/subprotocol.js +62 -0
  431. package/node_modules/ws/lib/validation.js +152 -0
  432. package/node_modules/ws/lib/websocket-server.js +562 -0
  433. package/node_modules/ws/lib/websocket.js +1421 -0
  434. package/node_modules/ws/package.json +74 -0
  435. package/node_modules/ws/wrapper.mjs +21 -0
  436. package/package.json +15 -5
  437. package/sandbox/csharp/EnvBroadcast.cs +33 -0
  438. package/sandbox/csharp/PathSync.cs +90 -0
  439. package/sandbox/scripts/install-shell-path.ps1 +73 -0
  440. package/sandbox/scripts/set-machine-environment.ps1 +37 -4
  441. package/schemas/config.schema.json +79 -4
  442. package/schemas/sandbox-config.schema.json +14 -1
  443. package/dist/cli/commands/repair/checks.d.ts +0 -2
  444. package/dist/cli/commands/repair/checks.js +0 -246
  445. package/dist/cli/menu/pages/start-page.d.ts +0 -43
  446. package/dist/cli/menu/pages/start-page.js +0 -192
  447. package/dist/cli/tui/tab-bar.d.ts +0 -9
  448. package/dist/cli/tui/tab-bar.js +0 -33
@@ -0,0 +1,46 @@
1
+ import { isSandboxRunning } from '../../host/sandbox/index.js';
2
+ import { hostPublicIp, readVpnLine, VpnLogin, } from '../../host/vpn/index.js';
3
+ /** The steps to get the NordVPN service credentials, in the words of Nord Account (shown before asking). */
4
+ export const NORD_STEPS = [
5
+ 'Sign in at https://my.nordaccount.com with your usual NordVPN e-mail and password.',
6
+ 'Open NordVPN, then "Set up NordVPN manually" (under Advanced settings).',
7
+ 'Verify your e-mail if it asks, then copy the "Service credentials": a user name and a password.',
8
+ 'These are NOT your account password. You can revoke them in the same place whenever you like.',
9
+ ];
10
+ export class VpnService {
11
+ ctx;
12
+ login;
13
+ constructor(ctx) {
14
+ this.ctx = ctx;
15
+ this.login = new VpnLogin(ctx.storageRoot);
16
+ }
17
+ get config() {
18
+ return this.ctx.loaded.config.vpn;
19
+ }
20
+ get provider() {
21
+ return this.config.provider;
22
+ }
23
+ async report() {
24
+ const c = this.config;
25
+ const running = isSandboxRunning();
26
+ return {
27
+ enabled: c.enabled,
28
+ provider: c.provider,
29
+ protocol: c.protocol,
30
+ country: c.country,
31
+ killSwitch: c.killSwitch,
32
+ login: this.login.state(c.provider),
33
+ loginSavedAt: this.login.savedAt(c.provider),
34
+ running: running ? readVpnLine(this.ctx.dataPath, await hostPublicIp()) : null,
35
+ };
36
+ }
37
+ loginState() {
38
+ return this.login.state(this.provider);
39
+ }
40
+ save(credentials) {
41
+ this.login.save(this.provider, credentials);
42
+ }
43
+ remove() {
44
+ return this.login.remove(this.provider);
45
+ }
46
+ }
@@ -0,0 +1,22 @@
1
+ import type { SessionStatus } from '../../core/types.js';
2
+ import type { WebBackend } from '../../host/web/index.js';
3
+ import type { Endpoint } from '../../shared/pty/index.js';
4
+ import type { NewSessionRequest, WebInfo, WebSession } from '../../shared/web/index.js';
5
+ import type { Context } from '../commands/context.js';
6
+ import { type ListedSession } from './sessions-service.js';
7
+ /** A session as the browser may see it: no attach address or token, text made safe. */
8
+ export declare function webSession(s: SessionStatus | ListedSession, viewers: number | null | undefined): WebSession;
9
+ export declare class WebService implements WebBackend {
10
+ private readonly ctx;
11
+ private cached;
12
+ constructor(ctx: Context);
13
+ /** The running sandbox as this user sees it, or null when none runs. */
14
+ private view;
15
+ /** The sandbox is this user's, or another user's and shared with them. */
16
+ private mayUse;
17
+ private projects;
18
+ info(): Promise<WebInfo>;
19
+ sessions(): Promise<WebSession[]>;
20
+ endpoint(sessionId: string): Endpoint | null;
21
+ create(request: NewSessionRequest): Promise<WebSession>;
22
+ }
@@ -0,0 +1,111 @@
1
+ // What the web UI's server may do with the running sandbox: the same sessions, projects and rules as the CLI (another
2
+ // user's sandbox is shown and used only as far as the sharing rules allow). Returns data, never prints.
3
+ import path from 'node:path';
4
+ import { fail, safeText } from '../../core/util/index.js';
5
+ import { hostEndpoint, SandboxChannel } from '../../host/channel/index.js';
6
+ import { insideSandbox, isSandboxRunning } from '../../host/sandbox/index.js';
7
+ import { ownerRule } from '../../host/sharing/index.js';
8
+ import { RunDir } from '../../shared/channel/index.js';
9
+ import { lookAtSandbox } from '../commands/shared-use.js';
10
+ import { registryOf } from '../commands/projects/here.js';
11
+ import { SessionsService } from './sessions-service.js';
12
+ /** The owner is asked (a process listing) at most this often: the browser polls. */
13
+ const VIEW_TTL_MS = 3000;
14
+ const SESSION_START_MS = 15 * 60 * 1000;
15
+ const MAX_SESSIONS = 50;
16
+ /** A session as the browser may see it: no attach address or token, text made safe. */
17
+ export function webSession(s, viewers) {
18
+ return {
19
+ id: s.id,
20
+ harness: safeText(s.harness),
21
+ project: safeText(('projectName' in s ? s.projectName : undefined) ?? s.project ?? path.win32.basename(s.cwd)),
22
+ cwd: safeText(s.cwd),
23
+ phase: s.phase,
24
+ message: safeText(s.message).slice(0, 300),
25
+ startedAt: s.startedAt,
26
+ viewers: viewers ?? null,
27
+ canOpen: s.phase === 'running' && hostEndpoint(s) !== null,
28
+ };
29
+ }
30
+ export class WebService {
31
+ ctx;
32
+ cached = null;
33
+ constructor(ctx) {
34
+ this.ctx = ctx;
35
+ }
36
+ /** The running sandbox as this user sees it, or null when none runs. */
37
+ view() {
38
+ const now = Date.now();
39
+ if (this.cached && now - this.cached.at < VIEW_TTL_MS)
40
+ return this.cached.view;
41
+ const view = !insideSandbox() && isSandboxRunning() ? lookAtSandbox(this.ctx) : null;
42
+ this.cached = { at: now, view };
43
+ return view;
44
+ }
45
+ /** The sandbox is this user's, or another user's and shared with them. */
46
+ mayUse(view) {
47
+ return !view.owner || ownerRule(view.owner, { kind: 'use' }).kind === 'allow';
48
+ }
49
+ projects(view) {
50
+ const known = registryOf(view.ctx).read()?.projects ?? [];
51
+ if (known.length)
52
+ return known.map((p) => ({ name: p.name, guest: safeText(p.guest) }));
53
+ // A sandbox from before projects existed has only its launch folder.
54
+ const status = new RunDir(view.ctx.dataPath).readSandboxStatus();
55
+ return status ? [{ name: 'workspace', guest: safeText(status.guestWorkspace) }] : [];
56
+ }
57
+ info() {
58
+ const config = this.ctx.loaded.config;
59
+ const view = this.view();
60
+ return Promise.resolve({
61
+ sandbox: view ? 'running' : 'stopped',
62
+ owner: view?.line ?? null,
63
+ canUse: view !== null && this.mayUse(view),
64
+ harnesses: Object.keys(config.harnesses),
65
+ defaultHarness: config.defaultHarness,
66
+ projects: view && this.mayUse(view) ? this.projects(view) : [],
67
+ });
68
+ }
69
+ async sessions() {
70
+ const view = this.view();
71
+ if (!view || !this.mayUse(view))
72
+ return [];
73
+ const launchId = new RunDir(view.ctx.dataPath).readSandboxStatus()?.launchId;
74
+ const listed = await new SessionsService(view.ctx).list();
75
+ return listed
76
+ .filter((s) => s.launchId === launchId && (s.phase === 'running' || s.phase === 'starting'))
77
+ .reverse()
78
+ .slice(0, MAX_SESSIONS)
79
+ .map((s) => webSession(s, s.viewers === undefined ? undefined : (s.viewers?.viewers ?? null)));
80
+ }
81
+ endpoint(sessionId) {
82
+ const view = this.view();
83
+ if (!view || !this.mayUse(view))
84
+ return null;
85
+ const run = new RunDir(view.ctx.dataPath);
86
+ const session = run.readSession(sessionId);
87
+ if (!session || session.phase !== 'running' || session.launchId !== run.readSandboxStatus()?.launchId)
88
+ return null;
89
+ return hostEndpoint(session);
90
+ }
91
+ async create(request) {
92
+ const view = this.view();
93
+ if (!view)
94
+ return fail('No Windows Sandbox is running.', 'Start one with sandbox claude, then try again.');
95
+ if (view.owner && view.owner.kind !== 'none' && view.owner.kind !== 'mine') {
96
+ const rule = ownerRule(view.owner, { kind: 'use' });
97
+ if (rule.kind === 'refuse')
98
+ return fail(rule.reason, rule.fix);
99
+ }
100
+ const project = this.projects(view).find((p) => p.name === request.project);
101
+ if (!project)
102
+ return fail('The running sandbox has no project "' + request.project + '".');
103
+ const harness = view.ctx.loaded.config.harnesses[request.harness];
104
+ if (!harness)
105
+ return fail('There is no agent called "' + request.harness + '".');
106
+ const channel = new SandboxChannel(view.ctx.dataPath);
107
+ const id = channel.requestSession(request.harness, [], project.guest, { harnessSpec: harness });
108
+ const session = await channel.waitForSession(id, SESSION_START_MS, () => undefined);
109
+ return webSession(session, null);
110
+ }
111
+ }
@@ -1,8 +1,9 @@
1
1
  import { type ConfirmOptions } from './dialogs/confirm-screen.js';
2
2
  import { MessageScreen } from './dialogs/message-screen.js';
3
3
  import { type TextOptions } from './dialogs/text-screen.js';
4
+ import { type PathPickerOptions } from './dialogs/path-picker.js';
4
5
  import { type OverlayOptions } from './dialogs/overlay.js';
5
- import { type TabsState } from './frame.js';
6
+ import type { NavState } from './nav.js';
6
7
  import { type ListOptions, type ListResult } from './list/list-types.js';
7
8
  import { Terminal } from './terminal.js';
8
9
  import type { Screen } from './screen.js';
@@ -10,8 +11,8 @@ export declare class App {
10
11
  readonly terminal: Terminal;
11
12
  /** The status line for screens that do not set their own (refreshed on every draw). */
12
13
  status: () => string;
13
- /** The tabs shown across the top of every screen (none: no tab row). */
14
- tabs: () => TabsState | undefined;
14
+ /** The navigation on the left of every screen (none: no navigation column). */
15
+ nav: () => NavState | undefined;
15
16
  /** The screens being shown, innermost last: a dialog is drawn over the one below it. */
16
17
  private readonly shown;
17
18
  private redrawNow;
@@ -26,6 +27,8 @@ export declare class App {
26
27
  ask(title: string, lines: readonly string[], yes?: string, danger?: boolean): Promise<boolean>;
27
28
  list<T>(o: ListOptions<T>): Promise<ListResult<T>>;
28
29
  text(o: TextOptions): Promise<string | undefined>;
30
+ /** Choose a folder (or a file): the path on top, the folders to browse below. Undefined = cancelled. */
31
+ pickPath(o: PathPickerOptions): Promise<string | undefined>;
29
32
  confirm(o: ConfirmOptions): Promise<boolean>;
30
33
  message(o: ConstructorParameters<typeof MessageScreen>[0]): Promise<void>;
31
34
  /** One of a few values (undefined = back). */
@@ -3,6 +3,7 @@
3
3
  import { ConfirmScreen } from './dialogs/confirm-screen.js';
4
4
  import { MessageScreen } from './dialogs/message-screen.js';
5
5
  import { TextScreen } from './dialogs/text-screen.js';
6
+ import { PathPickerScreen } from './dialogs/path-picker.js';
6
7
  import { OverlayScreen } from './dialogs/overlay.js';
7
8
  import { Frame } from './frame.js';
8
9
  import { ListScreen } from './list/list-screen.js';
@@ -12,8 +13,8 @@ export class App {
12
13
  terminal;
13
14
  /** The status line for screens that do not set their own (refreshed on every draw). */
14
15
  status = () => '';
15
- /** The tabs shown across the top of every screen (none: no tab row). */
16
- tabs = () => undefined;
16
+ /** The navigation on the left of every screen (none: no navigation column). */
17
+ nav = () => undefined;
17
18
  /** The screens being shown, innermost last: a dialog is drawn over the one below it. */
18
19
  shown = [];
19
20
  redrawNow = null;
@@ -31,9 +32,11 @@ export class App {
31
32
  const outer = this.redrawNow;
32
33
  const draw = () => {
33
34
  const { width, height } = this.terminal;
34
- const tabs = this.tabs();
35
- const parts = screen.render({ width, height: Frame.bodyHeight(height, Boolean(tabs)) });
36
- this.terminal.render(Frame.render({ ...parts, status: parts.status ?? this.status(), tabs: parts.tabs ?? tabs }, width, height));
35
+ const nav = this.nav();
36
+ // A screen over the menu (a dialog, an editor) leaves the navigation visible but grey.
37
+ const covered = this.shown.length > 1;
38
+ const parts = screen.render({ width: Frame.bodyWidth(width, nav), height: Frame.bodyHeight(height) });
39
+ this.terminal.render(Frame.render({ ...parts, status: parts.status ?? this.status(), nav: nav && { ...nav, dimmed: nav.dimmed || covered } }, width, height));
37
40
  };
38
41
  this.redrawNow = draw;
39
42
  this.terminal.onResized(draw);
@@ -75,6 +78,10 @@ export class App {
75
78
  text(o) {
76
79
  return this.show(new TextScreen(o));
77
80
  }
81
+ /** Choose a folder (or a file): the path on top, the folders to browse below. Undefined = cancelled. */
82
+ pickPath(o) {
83
+ return this.show(new PathPickerScreen(o));
84
+ }
78
85
  confirm(o) {
79
86
  return this.show(new ConfirmScreen(o));
80
87
  }
@@ -0,0 +1,43 @@
1
+ export type EntryKind = 'drive' | 'up' | 'dir' | 'file' | 'link';
2
+ export interface PickEntry {
3
+ readonly kind: EntryKind;
4
+ readonly name: string;
5
+ /** The full path it opens or picks ('' for the "This PC" level above a drive). */
6
+ readonly path: string;
7
+ /** Extra words: free space of a drive, "link". */
8
+ readonly detail?: string;
9
+ }
10
+ export interface Listing {
11
+ readonly entries: readonly PickEntry[];
12
+ /** Entries left out because of the cap. */
13
+ readonly more: number;
14
+ /** Why the folder could not be read. */
15
+ readonly error?: string;
16
+ }
17
+ /** Entries listed at most; the rest is counted ("... more"). */
18
+ export declare const LIST_CAP = 2000;
19
+ /** What was typed, split into the folder it names (or is in) and the start of a name in it. '' = This PC. */
20
+ export declare function splitInput(input: string): {
21
+ dir: string;
22
+ prefix: string;
23
+ };
24
+ /** The folder above, or '' (This PC) above a drive's root. */
25
+ export declare function parentOf(dir: string): string;
26
+ /** `dir` with exactly one trailing backslash (for the input line). */
27
+ export declare function withSeparator(dir: string): string;
28
+ /**
29
+ * The folders (and files when asked) in `dir`. Links and junctions are listed, marked, never opened; unreadable
30
+ * folders give an error text; at most `cap` entries come back.
31
+ */
32
+ export declare function listFolder(dir: string, files?: boolean, cap?: number): Listing;
33
+ /** The drives of this PC with their free space ("This PC"). */
34
+ export declare function listDrives(): PickEntry[];
35
+ /** Entries whose name starts with `prefix` (no case), or contains `text` when filtering. */
36
+ export declare function narrow(entries: readonly PickEntry[], prefix: string, text?: string): PickEntry[];
37
+ /**
38
+ * Completes the last name of `input` from the folders listed: the longest start they share, and a backslash when
39
+ * exactly one folder fits. The input is returned unchanged when nothing fits.
40
+ */
41
+ export declare function completeInput(input: string, folders: readonly PickEntry[]): string;
42
+ /** A name for a new folder: not empty, no path characters, not . or .., not reserved. */
43
+ export declare function folderNameProblem(name: string): string | null;
@@ -0,0 +1,127 @@
1
+ // What the path picker needs from the disk and from text, as plain functions (tested): splitting what was typed,
2
+ // listing a folder without ever following links, completing a name, filtering, and the drives of "This PC".
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { diskUsage } from '../../../core/disk.js';
6
+ const win = path.win32;
7
+ /** Entries listed at most; the rest is counted ("... more"). */
8
+ export const LIST_CAP = 2000;
9
+ const SEPARATOR = /[\\/]$/;
10
+ /** What was typed, split into the folder it names (or is in) and the start of a name in it. '' = This PC. */
11
+ export function splitInput(input) {
12
+ const text = input.trim();
13
+ if (text === '')
14
+ return { dir: '', prefix: '' };
15
+ if (/^[A-Za-z]:$/.test(text))
16
+ return { dir: text + '\\', prefix: '' };
17
+ if (SEPARATOR.test(text))
18
+ return { dir: text, prefix: '' };
19
+ const dir = win.dirname(text);
20
+ const base = win.basename(text);
21
+ // A bare name (no folder) has no folder to look in.
22
+ return dir === '.' ? { dir: '', prefix: base } : { dir, prefix: base };
23
+ }
24
+ /** The folder above, or '' (This PC) above a drive's root. */
25
+ export function parentOf(dir) {
26
+ if (dir === '')
27
+ return '';
28
+ const parent = win.dirname(win.resolve(dir));
29
+ return parent.toLowerCase() === win.resolve(dir).toLowerCase() ? '' : parent;
30
+ }
31
+ /** `dir` with exactly one trailing backslash (for the input line). */
32
+ export function withSeparator(dir) {
33
+ return dir === '' ? '' : dir.replace(/[\\/]+$/, '') + '\\';
34
+ }
35
+ /** Folders first, then files, each by name without regard to case. */
36
+ function byKindThenName(a, b) {
37
+ const rank = (e) => (e.kind === 'file' ? 1 : 0);
38
+ return rank(a) - rank(b) || a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
39
+ }
40
+ /**
41
+ * The folders (and files when asked) in `dir`. Links and junctions are listed, marked, never opened; unreadable
42
+ * folders give an error text; at most `cap` entries come back.
43
+ */
44
+ export function listFolder(dir, files = false, cap = LIST_CAP) {
45
+ let found;
46
+ try {
47
+ found = fs.readdirSync(dir, { withFileTypes: true });
48
+ }
49
+ catch (e) {
50
+ const code = e.code;
51
+ return {
52
+ entries: [],
53
+ more: 0,
54
+ error: code === 'ENOENT' || code === 'ENOTDIR' ? 'No such folder' : 'Cannot open it',
55
+ };
56
+ }
57
+ const entries = [];
58
+ for (const d of found) {
59
+ const full = win.join(dir, d.name);
60
+ if (d.isSymbolicLink())
61
+ entries.push({ kind: 'link', name: d.name, path: full, detail: 'link' });
62
+ else if (d.isDirectory())
63
+ entries.push({ kind: 'dir', name: d.name, path: full });
64
+ else if (files)
65
+ entries.push({ kind: 'file', name: d.name, path: full });
66
+ }
67
+ entries.sort(byKindThenName);
68
+ return { entries: entries.slice(0, cap), more: Math.max(0, entries.length - cap) };
69
+ }
70
+ /** The drives of this PC with their free space ("This PC"). */
71
+ export function listDrives() {
72
+ return diskUsage('C:\\').map((d) => ({
73
+ kind: 'drive',
74
+ name: d.drive,
75
+ path: d.drive,
76
+ detail: freeText(d.freeBytes, d.totalBytes),
77
+ }));
78
+ }
79
+ function freeText(free, total) {
80
+ const gb = (n) => (n / 1024 ** 3 >= 10 ? Math.round(n / 1024 ** 3) : (n / 1024 ** 3).toFixed(1)) + ' GB';
81
+ return gb(free) + ' free of ' + gb(total);
82
+ }
83
+ /** Entries whose name starts with `prefix` (no case), or contains `text` when filtering. */
84
+ export function narrow(entries, prefix, text = '') {
85
+ const start = prefix.toLowerCase();
86
+ const inside = text.toLowerCase();
87
+ return entries.filter((e) => {
88
+ const name = e.name.toLowerCase();
89
+ return name.startsWith(start) && name.includes(inside);
90
+ });
91
+ }
92
+ /**
93
+ * Completes the last name of `input` from the folders listed: the longest start they share, and a backslash when
94
+ * exactly one folder fits. The input is returned unchanged when nothing fits.
95
+ */
96
+ export function completeInput(input, folders) {
97
+ const { dir, prefix } = splitInput(input);
98
+ const names = folders
99
+ .filter((f) => f.kind === 'dir' && f.name.toLowerCase().startsWith(prefix.toLowerCase()))
100
+ .map((f) => f.name);
101
+ if (names.length === 0)
102
+ return input;
103
+ let common = names[0];
104
+ for (const name of names.slice(1)) {
105
+ let n = 0;
106
+ while (n < common.length && n < name.length && common[n].toLowerCase() === name[n].toLowerCase())
107
+ n++;
108
+ common = common.slice(0, n);
109
+ }
110
+ // Never shorten what was typed (the typed spelling wins when the listing only differs in case).
111
+ const completed = common.length >= prefix.length ? common : prefix;
112
+ const base = dir === '' ? '' : withSeparator(dir);
113
+ return base + completed + (names.length === 1 && completed.length === names[0].length ? '\\' : '');
114
+ }
115
+ /** A name for a new folder: not empty, no path characters, not . or .., not reserved. */
116
+ export function folderNameProblem(name) {
117
+ const trimmed = name.trim();
118
+ if (trimmed === '')
119
+ return 'Type a name.';
120
+ if (/[<>:"/\\|?*\x00-\x1f]/.test(trimmed))
121
+ return 'A folder name cannot contain < > : " / \\ | ? *';
122
+ if (/^\.+$/.test(trimmed) || /[. ]$/.test(trimmed))
123
+ return 'A folder name cannot end with a dot or a space.';
124
+ if (/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\..*)?$/i.test(trimmed))
125
+ return '"' + trimmed + '" is a reserved name in Windows.';
126
+ return null;
127
+ }
@@ -0,0 +1,67 @@
1
+ import type { FrameParts } from '../frame.js';
2
+ import type { Screen, Size, Step } from '../screen.js';
3
+ import type { Key } from '../terminal.js';
4
+ export interface PathPickerOptions {
5
+ path: string[];
6
+ title: string;
7
+ about?: string[];
8
+ /** The path in the input at first (also where the browser starts). */
9
+ initial?: string;
10
+ /** Show files (dim) and let them be picked; folders only by default. */
11
+ files?: boolean;
12
+ /** Label of the choose key, e.g. "use this folder". */
13
+ use?: string;
14
+ /** true = fine, a string = why this path cannot be chosen. */
15
+ validate?: (path: string) => true | string;
16
+ /** Typed words that are fine as they are (a setting that also takes "auto"). */
17
+ literals?: readonly string[];
18
+ /** A relative path may be typed too (it is kept as typed, not made absolute). */
19
+ relative?: boolean;
20
+ }
21
+ /** Resolves to the chosen path, or undefined when you press Esc. */
22
+ export declare class PathPickerScreen implements Screen<string | undefined> {
23
+ private readonly o;
24
+ private mode;
25
+ private input;
26
+ private cursor;
27
+ private dir;
28
+ private listing;
29
+ private loadedDir;
30
+ private selected;
31
+ private scroll;
32
+ private filter;
33
+ private newName;
34
+ private problem;
35
+ private notice;
36
+ private rows;
37
+ constructor(o: PathPickerOptions);
38
+ render(size: Size): FrameParts;
39
+ handle(key: Key): Promise<Step<string | undefined>>;
40
+ /** The entries on screen: the folder's, narrowed by what is typed after the last backslash and by the filter. */
41
+ private visible;
42
+ private dirIsTyped;
43
+ private row;
44
+ private where;
45
+ private verdict;
46
+ private verdictLine;
47
+ private keys;
48
+ /** What choosing now would pick. In the browser a highlighted folder (or file) wins over the folder being shown. */
49
+ private candidate;
50
+ private choose;
51
+ /** Is this typed text kept as typed: one of the allowed words, or a relative path where those are allowed? */
52
+ private kept;
53
+ /** Reads the folder the input names (only when it changed), keeping what was typed as a filter. */
54
+ private syncListing;
55
+ private openDir;
56
+ private go;
57
+ private keepSelectedInView;
58
+ private typing;
59
+ private edit;
60
+ /** A path ending in a backslash is opened to look inside; any other typed path is chosen. */
61
+ private enterInput;
62
+ private browsing;
63
+ private open;
64
+ private filtering;
65
+ private startName;
66
+ private naming;
67
+ }