windows-development-sandbox 1.20.0 → 1.23.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 (331) hide show
  1. package/config.example.json +8 -1
  2. package/dist/app/features/access/access.def.js +5 -2
  3. package/dist/app/features/access/access.run.js +7 -3
  4. package/dist/app/features/accounts/accounts.def.d.ts +29 -1
  5. package/dist/app/features/accounts/accounts.def.js +10 -4
  6. package/dist/app/features/accounts/accounts.run.d.ts +1 -0
  7. package/dist/app/features/accounts/accounts.run.js +7 -2
  8. package/dist/app/features/accounts/services.def.d.ts +4 -1
  9. package/dist/app/features/accounts/services.def.js +15 -14
  10. package/dist/app/features/accounts/services.run.d.ts +1 -0
  11. package/dist/app/features/accounts/services.run.js +41 -15
  12. package/dist/app/features/defs.d.ts +39 -2
  13. package/dist/app/features/engine/engine.run.js +1 -1
  14. package/dist/app/features/github/github.def.d.ts +44 -3
  15. package/dist/app/features/github/github.def.js +145 -63
  16. package/dist/app/features/github/github.run.d.ts +9 -2
  17. package/dist/app/features/github/github.run.js +43 -5
  18. package/dist/app/features/health/health.def.js +8 -2
  19. package/dist/app/features/logs/logs.def.js +0 -8
  20. package/dist/app/features/projects/projects.def.js +2 -2
  21. package/dist/app/features/projects/projects.run.js +5 -5
  22. package/dist/app/features/sandbox/sandbox.def.js +7 -3
  23. package/dist/app/features/sessions/sessions.def.d.ts +4 -4
  24. package/dist/app/features/sessions/sessions.def.js +0 -5
  25. package/dist/app/features/sessions/sessions.run.d.ts +1 -1
  26. package/dist/app/features/sessions/sessions.run.js +2 -2
  27. package/dist/app/features/settings/categories.def.js +2 -0
  28. package/dist/app/features/settings/settings-rows.js +1 -1
  29. package/dist/app/features/settings/settings.def.js +1 -1
  30. package/dist/app/features/settings/settings.run.js +3 -3
  31. package/dist/app/features/storage/storage.run.js +18 -9
  32. package/dist/app/features/updates/update-lines.js +2 -2
  33. package/dist/app/features/updates/updates.def.js +1 -1
  34. package/dist/app/features/updates/updates.run.js +1 -1
  35. package/dist/app/features/web/web.def.js +2 -2
  36. package/dist/app/features/web/web.run.js +12 -4
  37. package/dist/app/kernel/errors.d.ts +7 -0
  38. package/dist/app/kernel/errors.js +18 -3
  39. package/dist/app/kernel/rpc.d.ts +5 -0
  40. package/dist/app/kernel/view.d.ts +11 -1
  41. package/dist/bin/agent-sandbox.js +2 -2
  42. package/dist/bin/{chunk-DK4NTBUF.js → chunk-JETGP2C3.js} +172 -48
  43. package/dist/bin/{chunk-2IMT7ZCT.js → chunk-XZKOPGNH.js} +1 -1
  44. package/dist/bin/{chunk-Z2P5OGKL.js → chunk-ZTTOF5V3.js} +2430 -1688
  45. package/dist/bin/claude-host.js +2 -2
  46. package/dist/bin/claude-sbx.js +2 -2
  47. package/dist/bin/codex-sbx.js +2 -2
  48. package/dist/bin/gemini-sbx.js +2 -2
  49. package/dist/bin/sandbox.js +2 -2
  50. package/dist/bin/sbx-hide.js +2 -2
  51. package/dist/bin/sbx-show.js +2 -2
  52. package/dist/cli/manifest.d.ts +2 -2
  53. package/dist/cli/manifest.js +2 -1
  54. package/dist/cli/menu/coverage-exempt.d.ts +17 -0
  55. package/dist/cli/menu/coverage-exempt.js +61 -0
  56. package/dist/cli/menu/io-menu.d.ts +2 -0
  57. package/dist/cli/menu/io-menu.js +50 -5
  58. package/dist/cli/menu/pages/accounts-page.js +1 -1
  59. package/dist/cli/menu/pages/agents-page.js +8 -2
  60. package/dist/cli/menu/pages/maintenance-page.d.ts +6 -0
  61. package/dist/cli/menu/pages/maintenance-page.js +9 -2
  62. package/dist/cli/menu/pages/sessions-page.d.ts +2 -0
  63. package/dist/cli/menu/pages/sessions-page.js +12 -4
  64. package/dist/cli/menu/pages/settings-page.js +12 -1
  65. package/dist/cli/menu/remote-menu.js +1 -1
  66. package/dist/cli/menu/shell.d.ts +5 -0
  67. package/dist/cli/menu/shell.js +13 -0
  68. package/dist/cli/report.js +2 -0
  69. package/dist/cli/tui/app.d.ts +5 -0
  70. package/dist/cli/tui/app.js +2 -0
  71. package/dist/core/config/defaults.js +3 -1
  72. package/dist/core/schema/accounts.d.ts +6 -0
  73. package/dist/core/schema/accounts.js +13 -11
  74. package/dist/core/schema/config.d.ts +20 -8
  75. package/dist/core/schema/config.js +4 -4
  76. package/dist/core/schema/github.js +1 -1
  77. package/dist/core/schema/profile.js +8 -4
  78. package/dist/core/schema/resume.js +1 -1
  79. package/dist/core/schema/sandbox.js +3 -3
  80. package/dist/core/schema/vpn.js +1 -1
  81. package/dist/core/schema/web.js +3 -3
  82. package/dist/core/types.d.ts +6 -0
  83. package/dist/core/util/errors.d.ts +11 -1
  84. package/dist/core/util/errors.js +7 -1
  85. package/dist/core/util/index.d.ts +1 -1
  86. package/dist/core/util/index.js +1 -1
  87. package/dist/guest/accounts/adapter.d.ts +2 -0
  88. package/dist/guest/accounts/adapters.js +4 -0
  89. package/dist/guest/accounts/apply.d.ts +6 -2
  90. package/dist/guest/accounts/kinds/cfx.d.ts +7 -0
  91. package/dist/guest/accounts/kinds/cfx.js +33 -3
  92. package/dist/guest/accounts/kinds/comet.d.ts +7 -0
  93. package/dist/guest/accounts/kinds/comet.js +50 -0
  94. package/dist/guest/accounts/kinds/perplexity.d.ts +2 -0
  95. package/dist/guest/accounts/kinds/perplexity.js +12 -0
  96. package/dist/guest/accounts/watch.d.ts +2 -0
  97. package/dist/guest/accounts/watch.js +12 -2
  98. package/dist/guest/agents/agent-installer.js +1 -1
  99. package/dist/guest/agents/profile-links.d.ts +2 -0
  100. package/dist/guest/agents/profile-links.js +18 -4
  101. package/dist/guest/apps/after-install.js +19 -0
  102. package/dist/guest/apps/app-installer.d.ts +11 -0
  103. package/dist/guest/apps/app-installer.js +69 -6
  104. package/dist/guest/apps/comet-unpack.d.ts +20 -0
  105. package/dist/guest/apps/comet-unpack.js +127 -0
  106. package/dist/guest/apps/index.d.ts +1 -1
  107. package/dist/guest/apps/index.js +1 -1
  108. package/dist/guest/apps/resolvers-agents-web.d.ts +2 -0
  109. package/dist/guest/apps/resolvers-agents-web.js +7 -0
  110. package/dist/guest/apps/resolvers.js +2 -0
  111. package/dist/guest/apps/shortcuts.d.ts +2 -0
  112. package/dist/guest/apps/shortcuts.js +1 -0
  113. package/dist/guest/apps/unpack.js +14 -4
  114. package/dist/guest/boot/boot.d.ts +45 -16
  115. package/dist/guest/boot/boot.js +316 -145
  116. package/dist/guest/boot/graph.d.ts +252 -0
  117. package/dist/guest/boot/graph.js +257 -0
  118. package/dist/guest/boot/guide-lines.d.ts +25 -0
  119. package/dist/guest/boot/guide-lines.js +41 -0
  120. package/dist/guest/boot/guide.d.ts +1 -4
  121. package/dist/guest/boot/guide.js +6 -8
  122. package/dist/guest/boot/runner.d.ts +77 -0
  123. package/dist/guest/boot/runner.js +215 -0
  124. package/dist/guest/browser/cdp.d.ts +71 -0
  125. package/dist/guest/browser/cdp.js +306 -0
  126. package/dist/guest/browser/comet.d.ts +28 -0
  127. package/dist/guest/browser/comet.js +155 -0
  128. package/dist/guest/daemon/project-provisioner.js +6 -1
  129. package/dist/guest/devtools/fivem.d.ts +1 -1
  130. package/dist/guest/devtools/fivem.js +243 -3
  131. package/dist/guest/entries/daemon.js +2 -0
  132. package/dist/guest/runtime/log.d.ts +12 -0
  133. package/dist/guest/runtime/log.js +29 -5
  134. package/dist/guest/runtime/pools.d.ts +24 -0
  135. package/dist/guest/runtime/pools.js +60 -0
  136. package/dist/guest/runtime/process.d.ts +4 -0
  137. package/dist/guest/runtime/process.js +38 -1
  138. package/dist/guest/runtime/status.d.ts +5 -1
  139. package/dist/guest/runtime/status.js +19 -0
  140. package/dist/guest/setup/addon-mcp.d.ts +1 -1
  141. package/dist/guest/setup/addon-mcp.js +52 -12
  142. package/dist/guest/setup/agent-addons.d.ts +3 -2
  143. package/dist/guest/setup/agent-addons.js +12 -3
  144. package/dist/guest/setup/environment.d.ts +1 -1
  145. package/dist/guest/setup/environment.js +25 -12
  146. package/dist/guest/setup/headroom.d.ts +8 -3
  147. package/dist/guest/setup/headroom.js +44 -15
  148. package/dist/guest/setup/path-plan.d.ts +28 -0
  149. package/dist/guest/setup/path-plan.js +69 -2
  150. package/dist/guest/setup/shell-path.d.ts +1 -1
  151. package/dist/guest/setup/shell-path.js +3 -3
  152. package/dist/guest/toolchains/fivem/artifacts.d.ts +48 -1
  153. package/dist/guest/toolchains/fivem/artifacts.js +97 -4
  154. package/dist/guest/toolchains/fivem/fivem.d.ts +32 -1
  155. package/dist/guest/toolchains/fivem/fivem.js +168 -3
  156. package/dist/guest/toolchains/fivem/scaffold.d.ts +35 -0
  157. package/dist/guest/toolchains/fivem/scaffold.js +105 -0
  158. package/dist/guest/toolchains/fivem/serve-plan.d.ts +34 -0
  159. package/dist/guest/toolchains/fivem/serve-plan.js +61 -0
  160. package/dist/guest/toolchains/languages/go.js +1 -1
  161. package/dist/guest/toolchains/languages/node.js +2 -1
  162. package/dist/guest/toolchains/languages/php.d.ts +2 -0
  163. package/dist/guest/toolchains/languages/php.js +13 -1
  164. package/dist/guest/toolchains/languages/python-tools.js +1 -1
  165. package/dist/guest/toolchains/languages/ruby.js +1 -1
  166. package/dist/guest/toolchains/native/binstall.js +1 -1
  167. package/dist/guest/toolchains/native/msvc/packages.js +10 -4
  168. package/dist/guest/toolchains/native/rust.d.ts +2 -0
  169. package/dist/guest/toolchains/native/rust.js +11 -4
  170. package/dist/guest/toolchains/native/unreal.d.ts +2 -0
  171. package/dist/guest/toolchains/native/unreal.js +16 -6
  172. package/dist/guest/toolchains/native/vcruntime.d.ts +2 -0
  173. package/dist/guest/toolchains/native/vcruntime.js +10 -2
  174. package/dist/guest/toolchains/native/visual-studio.d.ts +3 -0
  175. package/dist/guest/toolchains/native/visual-studio.js +44 -26
  176. package/dist/guest/toolchains/native/vs-request.d.ts +2 -0
  177. package/dist/guest/toolchains/native/vs-request.js +2 -0
  178. package/dist/guest/toolchains/plan.js +19 -8
  179. package/dist/guest/toolchains/scheduler.d.ts +2 -21
  180. package/dist/guest/toolchains/scheduler.js +7 -121
  181. package/dist/guest/toolchains/setup.d.ts +15 -2
  182. package/dist/guest/toolchains/setup.js +39 -20
  183. package/dist/guest/toolchains/split.d.ts +28 -0
  184. package/dist/guest/toolchains/split.js +60 -0
  185. package/dist/guest/toolchains/steam/client.js +2 -1
  186. package/dist/guest/toolchains/step-scheduler.d.ts +51 -0
  187. package/dist/guest/toolchains/step-scheduler.js +147 -0
  188. package/dist/guest/toolchains/step.d.ts +13 -0
  189. package/dist/guest/toolchains/tools/git.js +18 -0
  190. package/dist/guest/vpn/openvpn.js +3 -4
  191. package/dist/guest/vpn/wireguard.js +11 -4
  192. package/dist/host/accounts/deliver.js +7 -0
  193. package/dist/host/accounts/handoff.js +2 -6
  194. package/dist/host/accounts/index.d.ts +1 -1
  195. package/dist/host/accounts/index.js +1 -1
  196. package/dist/host/accounts/kinds/cfx.d.ts +1 -1
  197. package/dist/host/accounts/kinds/cfx.js +47 -4
  198. package/dist/host/accounts/kinds/comet.d.ts +2 -0
  199. package/dist/host/accounts/kinds/comet.js +6 -0
  200. package/dist/host/accounts/kinds/perplexity.d.ts +2 -0
  201. package/dist/host/accounts/kinds/perplexity.js +30 -0
  202. package/dist/host/accounts/leases.d.ts +7 -1
  203. package/dist/host/accounts/leases.js +13 -2
  204. package/dist/host/accounts/source.d.ts +7 -1
  205. package/dist/host/accounts/source.js +27 -0
  206. package/dist/host/accounts/sources.js +4 -0
  207. package/dist/host/github/deliver.d.ts +1 -1
  208. package/dist/host/github/deliver.js +20 -1
  209. package/dist/host/github/index.d.ts +2 -1
  210. package/dist/host/github/index.js +1 -0
  211. package/dist/host/github/token.d.ts +13 -1
  212. package/dist/host/github/token.js +19 -2
  213. package/dist/host/github/verify.d.ts +27 -0
  214. package/dist/host/github/verify.js +61 -0
  215. package/dist/host/launch/hints/fivem.d.ts +3 -1
  216. package/dist/host/launch/hints/fivem.js +68 -2
  217. package/dist/host/launch/session-file.js +4 -0
  218. package/dist/host/launch/toolchains.d.ts +2 -1
  219. package/dist/host/launch/toolchains.js +5 -3
  220. package/dist/host/vpn/nordvpn.d.ts +2 -4
  221. package/dist/host/vpn/nordvpn.js +2 -24
  222. package/dist/runtime/agent.js +405 -90
  223. package/dist/runtime/daemon.js +3010 -1383
  224. package/dist/runtime/db.js +333 -90
  225. package/dist/runtime/main.js +4720 -1954
  226. package/dist/runtime/netproxy.js +286 -69
  227. package/dist/runtime/tool.js +2852 -1780
  228. package/dist/runtime/view.js +286 -69
  229. package/dist/selftest/agent-paths.test.js +39 -1
  230. package/dist/selftest/agents-web.test.d.ts +2 -0
  231. package/dist/selftest/agents-web.test.js +647 -0
  232. package/dist/selftest/app.test.js +5 -2
  233. package/dist/selftest/boot-runner.test.d.ts +2 -0
  234. package/dist/selftest/boot-runner.test.js +456 -0
  235. package/dist/selftest/channel.test.js +15 -0
  236. package/dist/selftest/fivem.test.js +249 -3
  237. package/dist/selftest/github.test.js +94 -9
  238. package/dist/selftest/guest-boot.test.js +3 -0
  239. package/dist/selftest/health.test.js +1 -1
  240. package/dist/selftest/index.js +8 -0
  241. package/dist/selftest/layout.test.js +5 -0
  242. package/dist/selftest/menu-coverage.test.d.ts +2 -0
  243. package/dist/selftest/menu-coverage.test.js +46 -0
  244. package/dist/selftest/menu-screens.js +37 -1
  245. package/dist/selftest/persisted-installs.test.js +6 -3
  246. package/dist/selftest/service-accounts.test.js +3 -2
  247. package/dist/selftest/support/engine-golden.js +27 -0
  248. package/dist/selftest/support/fixtures.d.ts +6 -0
  249. package/dist/selftest/support/web-preview.d.ts +34 -1
  250. package/dist/selftest/support/web-preview.js +21 -18
  251. package/dist/selftest/toolchains.test.js +23 -1
  252. package/dist/selftest/ui-text.test.d.ts +7 -0
  253. package/dist/selftest/ui-text.test.js +300 -0
  254. package/dist/shared/accounts/index.d.ts +2 -0
  255. package/dist/shared/accounts/index.js +2 -0
  256. package/dist/shared/accounts/kinds/comet.d.ts +12 -0
  257. package/dist/shared/accounts/kinds/comet.js +64 -0
  258. package/dist/shared/accounts/kinds/perplexity.d.ts +8 -0
  259. package/dist/shared/accounts/kinds/perplexity.js +37 -0
  260. package/dist/shared/accounts/kinds.d.ts +24 -5
  261. package/dist/shared/accounts/kinds.js +86 -4
  262. package/dist/shared/accounts/payloads.d.ts +81 -0
  263. package/dist/shared/accounts/payloads.js +39 -1
  264. package/dist/shared/apps/agent-addons.d.ts +33 -5
  265. package/dist/shared/apps/agent-addons.js +95 -3
  266. package/dist/shared/apps/catalogue-agents-web.d.ts +14 -0
  267. package/dist/shared/apps/catalogue-agents-web.js +112 -0
  268. package/dist/shared/apps/catalogue-modding.js +32 -1
  269. package/dist/shared/apps/catalogue.d.ts +27 -3
  270. package/dist/shared/apps/catalogue.js +2 -1
  271. package/dist/shared/apps/index.d.ts +3 -1
  272. package/dist/shared/apps/index.js +3 -1
  273. package/dist/shared/channel/schema.d.ts +21 -3
  274. package/dist/shared/channel/schema.js +7 -0
  275. package/dist/shared/channel/secrets.d.ts +19 -0
  276. package/dist/shared/concurrency/index.d.ts +3 -0
  277. package/dist/shared/concurrency/index.js +3 -0
  278. package/dist/shared/concurrency/installer-retry.d.ts +6 -0
  279. package/dist/shared/concurrency/installer-retry.js +10 -0
  280. package/dist/shared/concurrency/limiter.d.ts +19 -0
  281. package/dist/shared/concurrency/limiter.js +67 -0
  282. package/dist/shared/concurrency/pools.d.ts +7 -0
  283. package/dist/shared/concurrency/pools.js +9 -0
  284. package/dist/shared/download/archive.d.ts +2 -0
  285. package/dist/shared/download/archive.js +11 -1
  286. package/dist/shared/download/bounded-fetch.d.ts +13 -0
  287. package/dist/shared/download/bounded-fetch.js +40 -0
  288. package/dist/shared/download/downloader.d.ts +5 -0
  289. package/dist/shared/download/downloader.js +50 -38
  290. package/dist/shared/download/index.d.ts +2 -1
  291. package/dist/shared/download/index.js +2 -1
  292. package/dist/shared/readiness.d.ts +2 -2
  293. package/dist/shared/readiness.js +2 -2
  294. package/dist/web/index.html +1 -1
  295. package/dist/web/{main-OUUQYNK4.js → main-CCILLX2M.js} +67 -67
  296. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  297. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  298. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  299. package/package.json +1 -1
  300. package/sandbox/addons/browser-use-mcp/requirements.in +1 -0
  301. package/sandbox/addons/browser-use-mcp/requirements.txt +2350 -0
  302. package/sandbox/addons/chrome-devtools-mcp/package-lock.json +38 -0
  303. package/sandbox/addons/chrome-devtools-mcp/package.json +7 -0
  304. package/sandbox/addons/comet-mcp/package-lock.json +1277 -0
  305. package/sandbox/addons/comet-mcp/package.json +7 -0
  306. package/sandbox/addons/perplexity-mcp/package-lock.json +1655 -0
  307. package/sandbox/addons/perplexity-mcp/package.json +7 -0
  308. package/sandbox/scripts/add-firewall-port.ps1 +18 -4
  309. package/sandbox/scripts/set-machine-environment.ps1 +30 -15
  310. package/sandbox/templates/fivem/README.txt +6 -2
  311. package/sandbox/templates/fivem/cs/Client/Client.csproj +15 -0
  312. package/sandbox/templates/fivem/cs/Client/ClientMain.cs +20 -0
  313. package/sandbox/templates/fivem/cs/Server/Server.csproj +14 -0
  314. package/sandbox/templates/fivem/cs/Server/ServerMain.cs +20 -0
  315. package/sandbox/templates/fivem/cs/fxmanifest.lua +11 -0
  316. package/sandbox/templates/fivem/js/client.js +12 -0
  317. package/sandbox/templates/fivem/js/fxmanifest.lua +9 -0
  318. package/sandbox/templates/fivem/js/server.js +6 -0
  319. package/sandbox/templates/fivem/lua/client.lua +8 -0
  320. package/sandbox/templates/fivem/lua/fxmanifest.lua +10 -0
  321. package/sandbox/templates/fivem/lua/server.lua +6 -0
  322. package/sandbox/templates/fivem/server.cfg +37 -0
  323. package/sandbox/templates/fivem/ts/build.js +17 -0
  324. package/sandbox/templates/fivem/ts/fxmanifest.lua +9 -0
  325. package/sandbox/templates/fivem/ts/package.json +16 -0
  326. package/sandbox/templates/fivem/ts/src/client/client.ts +12 -0
  327. package/sandbox/templates/fivem/ts/src/client/tsconfig.json +13 -0
  328. package/sandbox/templates/fivem/ts/src/server/server.ts +6 -0
  329. package/sandbox/templates/fivem/ts/src/server/tsconfig.json +13 -0
  330. package/schemas/config.schema.json +56 -27
  331. package/schemas/sandbox-config.schema.json +37 -14
@@ -1,7 +1,21 @@
1
1
  import { z } from 'zod';
2
- import { type Page } from '../../kernel/index.js';
2
+ import { type Item, type Page, type Section } from '../../kernel/index.js';
3
3
  /** Where GitHub makes a fine-grained token. */
4
4
  export declare const NEW_TOKEN_URL = "https://github.com/settings/personal-access-tokens/new";
5
+ /** What a token for sandboxes needs (GitHub's names for the permissions), asked for in the pre-filled form. */
6
+ export declare const TOKEN_PERMISSIONS: Readonly<Record<string, 'read' | 'write'>>;
7
+ /**
8
+ * The link that opens GitHub's "new fine-grained token" form already filled in (name, description, days until it
9
+ * expires, permissions): docs.github.com, "Creating a fine-grained personal access token". Pure.
10
+ */
11
+ export declare function newTokenUrl(o: {
12
+ readonly name: string;
13
+ readonly description?: string;
14
+ readonly days: number;
15
+ readonly permissions: Readonly<Record<string, 'read' | 'write'>>;
16
+ }): string;
17
+ /** The pre-filled link for this PC (the name says which PC the token is for). */
18
+ export declare function tokenUrlFor(host: string): string;
5
19
  /** The steps to make a token for sandboxes (shown before asking for it). */
6
20
  export declare const GITHUB_STEPS: readonly string[];
7
21
  /** What everyone who shares a token must know: on every surface. */
@@ -23,6 +37,11 @@ export declare const GithubStatusSchema: z.ZodObject<{
23
37
  classic: "classic";
24
38
  }>>;
25
39
  savedAt: z.ZodNullable<z.ZodString>;
40
+ login: z.ZodNullable<z.ZodString>;
41
+ expiresAt: z.ZodNullable<z.ZodString>;
42
+ repoCount: z.ZodNullable<z.ZodNumber>;
43
+ checkedAt: z.ZodNullable<z.ZodString>;
44
+ tokenUrl: z.ZodString;
26
45
  }, z.core.$strip>;
27
46
  export type GithubStatus = z.output<typeof GithubStatusSchema>;
28
47
  export declare const githubStatus: import("../../kernel/define.js").QueryDef<z.ZodObject<{}, z.core.$strict>, z.ZodObject<{
@@ -42,6 +61,11 @@ export declare const githubStatus: import("../../kernel/define.js").QueryDef<z.Z
42
61
  classic: "classic";
43
62
  }>>;
44
63
  savedAt: z.ZodNullable<z.ZodString>;
64
+ login: z.ZodNullable<z.ZodString>;
65
+ expiresAt: z.ZodNullable<z.ZodString>;
66
+ repoCount: z.ZodNullable<z.ZodNumber>;
67
+ checkedAt: z.ZodNullable<z.ZodString>;
68
+ tokenUrl: z.ZodString;
45
69
  }, z.core.$strip>, "github.status">;
46
70
  export declare const githubLogin: import("../../kernel/define.js").OperationDef<z.ZodObject<{}, z.core.$strict>, z.ZodObject<{
47
71
  message: z.ZodString;
@@ -56,8 +80,20 @@ export declare function githubTokenRowOf(s: GithubStatus): {
56
80
  hint: string;
57
81
  description: string;
58
82
  };
59
- /** The status page: the facts, the next step (command line only), the warnings and the token actions. */
60
- export declare function githubPage(s: GithubStatus): Page;
83
+ /** A token that expires within this many days is flagged. */
84
+ export declare const EXPIRY_WARN_DAYS = 14;
85
+ /** How the expiry reads: "Expires 3 Feb 2027 (in 12 days)", "Expired 3 Feb 2027", and its tone. */
86
+ export declare function expiryOf(expiresAt: string | null, now?: number): {
87
+ readonly text: string;
88
+ readonly tone: 'ok' | 'warn' | 'danger';
89
+ readonly days: number;
90
+ } | null;
91
+ /** The GitHub card (the "thing you connect" pattern): used by the GitHub page and the Accounts page. */
92
+ export declare function githubItem(s: GithubStatus, now?: number): Item;
93
+ /** The GitHub section (the card, the git-access fact, the warnings and the link that opens GitHub's form). */
94
+ export declare function githubSection(s: GithubStatus, now?: number): Section;
95
+ /** The status page: the card, the git access it needs, the warnings and how to make a new token. */
96
+ export declare function githubPage(s: GithubStatus, now?: number): Page;
61
97
  export declare const githubView: import("../../kernel/define.js").ViewDef<{
62
98
  status: import("../../kernel/define.js").QueryDef<z.ZodObject<{}, z.core.$strict>, z.ZodObject<{
63
99
  shareToken: z.ZodBoolean;
@@ -76,5 +112,10 @@ export declare const githubView: import("../../kernel/define.js").ViewDef<{
76
112
  classic: "classic";
77
113
  }>>;
78
114
  savedAt: z.ZodNullable<z.ZodString>;
115
+ login: z.ZodNullable<z.ZodString>;
116
+ expiresAt: z.ZodNullable<z.ZodString>;
117
+ repoCount: z.ZodNullable<z.ZodNumber>;
118
+ checkedAt: z.ZodNullable<z.ZodString>;
119
+ tokenUrl: z.ZodString;
79
120
  }, z.core.$strip>, "github.status">;
80
121
  }, Record<string, never>, "github">;
@@ -2,12 +2,44 @@
2
2
  // and saving or deleting it. Pure: schemas, labels and the view; github.run.ts does the work. The token itself is never
3
3
  // an input or an output.
4
4
  import { z } from 'zod';
5
- import { action, defineOperation, defineQuery, defineView, } from '../../kernel/index.js';
5
+ import { action, defineOperation, defineQuery, defineView, link, plural, } from '../../kernel/index.js';
6
+ import { setSetting } from '../settings/settings.def.js';
6
7
  /** Where GitHub makes a fine-grained token. */
7
8
  export const NEW_TOKEN_URL = 'https://github.com/settings/personal-access-tokens/new';
9
+ /** What a token for sandboxes needs (GitHub's names for the permissions), asked for in the pre-filled form. */
10
+ export const TOKEN_PERMISSIONS = {
11
+ contents: 'write',
12
+ pull_requests: 'write',
13
+ metadata: 'read',
14
+ workflows: 'write',
15
+ };
16
+ /**
17
+ * The link that opens GitHub's "new fine-grained token" form already filled in (name, description, days until it
18
+ * expires, permissions): docs.github.com, "Creating a fine-grained personal access token". Pure.
19
+ */
20
+ export function newTokenUrl(o) {
21
+ const query = new URLSearchParams();
22
+ query.set('name', o.name.slice(0, 40));
23
+ if (o.description)
24
+ query.set('description', o.description.slice(0, 1024));
25
+ query.set('expires_in', String(Math.max(1, Math.min(366, Math.round(o.days)))));
26
+ for (const [permission, level] of Object.entries(o.permissions))
27
+ query.set(permission, level);
28
+ // GitHub reads %20 as well as +; spaces are written %20 so the link reads the same everywhere.
29
+ return NEW_TOKEN_URL + '?' + query.toString().replace(/\+/g, '%20');
30
+ }
31
+ /** The pre-filled link for this PC (the name says which PC the token is for). */
32
+ export function tokenUrlFor(host) {
33
+ return newTokenUrl({
34
+ name: 'Windows Sandbox ' + host,
35
+ description: 'For the agents in my Windows Sandbox. Limit it to the repositories they need.',
36
+ days: 90,
37
+ permissions: TOKEN_PERMISSIONS,
38
+ });
39
+ }
8
40
  /** The steps to make a token for sandboxes (shown before asking for it). */
9
41
  export const GITHUB_STEPS = [
10
- 'Open ' + NEW_TOKEN_URL + ' (a fine-grained personal access token).',
42
+ 'Make a fine-grained personal access token (the "Make a new token" button opens GitHub with the form filled in).',
11
43
  'Under "Repository access" choose "Only select repositories" and pick only the ones the agent should reach.',
12
44
  'Give only the permissions it needs, for example Contents (read and write), Pull requests (read and write) and Actions (read).',
13
45
  'Set an expiry date, then generate the token and copy it.',
@@ -26,6 +58,11 @@ export const GithubStatusSchema = z.object({
26
58
  token: TokenStateSchema,
27
59
  kind: z.enum(['fine-grained', 'classic']).nullable().describe('What kind of token is saved (never the token)'),
28
60
  savedAt: z.string().nullable().describe('When the token was saved (the date and time, never the token)'),
61
+ login: z.string().nullable().describe('The GitHub user the token belongs to, as GitHub said when it was saved'),
62
+ expiresAt: z.string().nullable().describe('When the token expires (ISO time), when GitHub said'),
63
+ repoCount: z.number().nullable().describe('How many repositories it reaches (at most 100 are counted)'),
64
+ checkedAt: z.string().nullable().describe('When GitHub was asked about it'),
65
+ tokenUrl: z.string().describe("GitHub's new-token form for this PC, filled in"),
29
66
  });
30
67
  export const githubStatus = defineQuery({
31
68
  id: 'github.status',
@@ -57,20 +94,6 @@ export const githubLogout = defineOperation({
57
94
  text: (o) => o.message,
58
95
  cli: { name: 'github', action: 'logout' },
59
96
  });
60
- const TOKEN_TEXT = {
61
- none: 'not saved',
62
- saved: 'saved (encrypted)',
63
- unreadable: 'cannot be read',
64
- };
65
- const TOKEN_TONE = {
66
- none: 'warn',
67
- saved: 'ok',
68
- unreadable: 'danger',
69
- };
70
- const KIND_TEXT = {
71
- 'fine-grained': 'fine-grained',
72
- classic: 'classic (reaches every repository you can: a fine-grained token is safer)',
73
- };
74
97
  /** The Settings row of the menu (label, hint, description), from the status. Pure. */
75
98
  export function githubTokenRowOf(s) {
76
99
  return {
@@ -80,63 +103,122 @@ export function githubTokenRowOf(s) {
80
103
  GITHUB_WARNINGS.join(' '),
81
104
  };
82
105
  }
83
- /** The status page: the facts, the next step (command line only), the warnings and the token actions. */
84
- export function githubPage(s) {
85
- const savedDate = s.savedAt ? ' ' + s.savedAt.slice(0, 10) : '';
86
- const facts = [
87
- s.shareToken
88
- ? { id: 'share', label: 'Share with sandboxes', value: 'on', tone: 'ok' }
89
- : {
90
- id: 'share',
91
- label: 'Share with sandboxes',
92
- value: 'off (turn on: sandbox config set github.shareToken true)',
93
- },
94
- s.gitAccess === 'full'
95
- ? { id: 'gitAccess', label: 'Git access', value: 'full' }
96
- : {
106
+ const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
107
+ const DAY_MS = 86_400_000;
108
+ /** A token that expires within this many days is flagged. */
109
+ export const EXPIRY_WARN_DAYS = 14;
110
+ /** "3 Feb 2027". */
111
+ function dateText(iso) {
112
+ const d = new Date(iso);
113
+ return d.getUTCDate() + ' ' + MONTHS[d.getUTCMonth()] + ' ' + d.getUTCFullYear();
114
+ }
115
+ /** How the expiry reads: "Expires 3 Feb 2027 (in 12 days)", "Expired 3 Feb 2027", and its tone. */
116
+ export function expiryOf(expiresAt, now = Date.now()) {
117
+ if (!expiresAt || Number.isNaN(Date.parse(expiresAt)))
118
+ return null;
119
+ const left = Date.parse(expiresAt) - now;
120
+ const days = Math.ceil(left / DAY_MS);
121
+ if (left <= 0)
122
+ return { text: 'Expired ' + dateText(expiresAt), tone: 'danger', days };
123
+ return {
124
+ text: 'Expires ' + dateText(expiresAt) + ' (' + (days <= 1 ? 'today or tomorrow' : 'in ' + days + ' days') + ')',
125
+ tone: days < EXPIRY_WARN_DAYS ? 'warn' : 'ok',
126
+ days,
127
+ };
128
+ }
129
+ /** The state badge of a GitHub card (the B5 words), then "shared". */
130
+ function badgesOf(s, now) {
131
+ const expiry = s.token === 'saved' ? expiryOf(s.expiresAt, now) : null;
132
+ const state = s.token === 'unreadable'
133
+ ? { text: "can't be read", tone: 'danger' }
134
+ : s.token === 'none'
135
+ ? { text: 'not saved', tone: 'muted' }
136
+ : expiry?.tone === 'danger'
137
+ ? { text: 'expired', tone: 'danger' }
138
+ : expiry?.tone === 'warn'
139
+ ? { text: 'expires soon', tone: 'warn' }
140
+ : { text: 'saved', tone: 'ok' };
141
+ return [state, ...(s.shareToken && s.token !== 'none' ? [{ text: 'shared', tone: 'accent' }] : [])];
142
+ }
143
+ /** The GitHub card (the "thing you connect" pattern): used by the GitHub page and the Accounts page. */
144
+ export function githubItem(s, now = Date.now()) {
145
+ const expiry = s.token === 'saved' ? expiryOf(s.expiresAt, now) : null;
146
+ const badges = badgesOf(s, now);
147
+ const who = s.login && s.token === 'saved'
148
+ ? 'Signed in as ' +
149
+ s.login +
150
+ (s.repoCount !== null
151
+ ? ', reaches ' + (s.repoCount >= 100 ? '100 or more' : plural(s.repoCount, 'repository', 'repositories'))
152
+ : '')
153
+ : undefined;
154
+ const details = [
155
+ ...(who ? [who] : []),
156
+ ...(expiry ? [expiry.text] : []),
157
+ ...(s.token === 'unreadable' ? ['Only the Windows user that saved it can read it.'] : []),
158
+ ...(s.kind === 'classic' ? ['A classic token reaches every repository you can: a fine-grained one is safer.'] : []),
159
+ ].slice(0, 2);
160
+ const signIn = action(githubLogin, {}, { key: 'g', verb: 'sign in', label: s.token === 'none' ? 'Sign in…' : 'Sign in again…' });
161
+ const share = action(setSetting, { scope: 'user', key: 'github.shareToken', value: !s.shareToken }, {
162
+ key: 't',
163
+ verb: s.shareToken ? 'stop sharing' : 'share',
164
+ label: s.shareToken ? 'Stop sharing' : 'Share with sandboxes',
165
+ importance: 1,
166
+ });
167
+ return {
168
+ id: 'github',
169
+ label: 'GitHub',
170
+ hint: badges[0].text,
171
+ tone: badges[0].tone,
172
+ badges,
173
+ description: 'A token that lets agents in a sandbox use gh and git over https.',
174
+ ...(details.length ? { details } : {}),
175
+ primary: s.token === 'saved' ? share : signIn,
176
+ actions: s.token === 'none'
177
+ ? []
178
+ : [
179
+ ...(s.token === 'saved' ? [signIn] : []),
180
+ action(githubLogout, {}, { key: 'x', verb: 'remove', label: 'Remove…' }),
181
+ ],
182
+ };
183
+ }
184
+ /** The GitHub section (the card, the git-access fact, the warnings and the link that opens GitHub's form). */
185
+ export function githubSection(s, now = Date.now()) {
186
+ const facts = s.gitAccess === 'full'
187
+ ? []
188
+ : [
189
+ {
97
190
  id: 'gitAccess',
98
191
  label: 'Git access',
99
192
  value: s.gitAccess + ' (the token is only handed over with "full")',
100
193
  tone: 'warn',
194
+ description: 'Change it in Settings (provisioning.git.access).',
101
195
  },
102
- { id: 'token', label: 'Token', value: TOKEN_TEXT[s.token] + savedDate, tone: TOKEN_TONE[s.token] },
103
- ...(s.kind
104
- ? [
105
- {
106
- id: 'kind',
107
- label: 'Kind',
108
- value: KIND_TEXT[s.kind],
109
- ...(s.kind === 'classic' ? { tone: 'warn' } : {}),
110
- },
111
- ]
112
- : []),
113
- ];
114
- const notes = [
115
- ...(s.token === 'none'
116
- ? [{ text: 'Next: sandbox github login', tone: 'muted', only: 'cli' }]
117
- : []),
118
- ...(s.token === 'unreadable'
119
- ? [
120
- {
121
- text: 'Only the Windows user that saved it can read it. Save it again: sandbox github login',
122
- tone: 'muted',
123
- },
124
- ]
125
- : []),
126
- ...GITHUB_WARNINGS.map((text) => ({ text, tone: 'muted' })),
127
- ];
128
- const actions = [
129
- action(githubLogin, {}, { key: 'g', verb: 'save token' }),
130
- ...(s.token !== 'none' ? [action(githubLogout, {}, { key: 'x', verb: 'delete token' })] : []),
131
- ];
132
- const section = { id: 'github', facts, notes, actions };
133
- return { title: 'GitHub', sections: [section] };
196
+ ];
197
+ const notes = GITHUB_WARNINGS.map((text) => ({ text, tone: 'muted' }));
198
+ const make = link({ kind: 'url', href: s.tokenUrl }, 'Make a new token…', {
199
+ key: 'n',
200
+ verb: 'new token',
201
+ importance: 2,
202
+ description: "Opens GitHub's form with a name, an expiry and the permissions already filled in.",
203
+ });
204
+ return {
205
+ id: 'github',
206
+ title: 'GitHub',
207
+ facts,
208
+ items: [githubItem(s, now)],
209
+ notes,
210
+ actions: [make],
211
+ };
212
+ }
213
+ /** The status page: the card, the git access it needs, the warnings and how to make a new token. */
214
+ export function githubPage(s, now = Date.now()) {
215
+ return { title: 'GitHub', sections: [githubSection(s, now)] };
134
216
  }
135
217
  export const githubView = defineView({
136
218
  id: 'github',
137
219
  title: 'GitHub',
138
220
  summary: 'A GitHub token for sandboxes: whether they get it, and the saved token.',
139
221
  queries: { status: githubStatus },
140
- operations: [githubLogin, githubLogout],
222
+ operations: [githubLogin, githubLogout, setSetting],
141
223
  build: ({ status }) => githubPage(status),
142
224
  });
@@ -1,7 +1,9 @@
1
- import { GithubToken } from '../../../host/github/index.js';
1
+ import { GithubToken, type GithubVerdict } from '../../../host/github/index.js';
2
2
  import { type OperationIO } from '../../kernel/index.js';
3
+ /** Asks GitHub about a token just typed (the self-test passes its own). */
4
+ export type VerifyToken = (token: string) => Promise<GithubVerdict>;
3
5
  /** Asks for the token (one hidden question) and saves it. The validator answers with fixed texts only. */
4
- export declare function saveGithubTokenFlow(io: OperationIO, store: GithubToken): Promise<{
6
+ export declare function saveGithubTokenFlow(io: OperationIO, store: GithubToken, verify?: VerifyToken): Promise<{
5
7
  message: string;
6
8
  }>;
7
9
  /** Deletes the saved token after asking (the CLI's typed logout is the answer). */
@@ -26,6 +28,11 @@ export declare const githubStatusRun: import("../../kernel/define.js").Implement
26
28
  classic: "classic";
27
29
  }>>;
28
30
  savedAt: import("zod").ZodNullable<import("zod").ZodString>;
31
+ login: import("zod").ZodNullable<import("zod").ZodString>;
32
+ expiresAt: import("zod").ZodNullable<import("zod").ZodString>;
33
+ repoCount: import("zod").ZodNullable<import("zod").ZodNumber>;
34
+ checkedAt: import("zod").ZodNullable<import("zod").ZodString>;
35
+ tokenUrl: import("zod").ZodString;
29
36
  }, import("zod/v4/core").$strip>, "github.status">, import("../../../cli/commands/context.js").Context>;
30
37
  export declare const githubLoginRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{}, import("zod/v4/core").$strict>, import("zod").ZodObject<{
31
38
  message: import("zod").ZodString;
@@ -1,14 +1,16 @@
1
1
  // The GitHub feature's work: its status, saving the token through io.secret (kept only for the save, never returned,
2
2
  // logged or shown), and deleting it after a confirmation. The flows take the store, so the self-test runs them with a
3
3
  // store of its own.
4
+ import os from 'node:os';
4
5
  import { requireOnPc } from '../../../cli/commands/context.js';
5
- import { GithubToken } from '../../../host/github/index.js';
6
+ import { fail } from '../../../core/util/index.js';
7
+ import { GithubToken, verifyGithubToken } from '../../../host/github/index.js';
6
8
  import { githubTokenProblem } from '../../../shared/github/index.js';
7
9
  import { implement } from '../../context.js';
8
10
  import { cancelled } from '../../kernel/index.js';
9
- import { GITHUB_STEPS, GITHUB_WARNINGS, githubLogin, githubLogout, githubStatus } from './github.def.js';
11
+ import { GITHUB_STEPS, GITHUB_WARNINGS, expiryOf, githubLogin, githubLogout, githubStatus, tokenUrlFor, } from './github.def.js';
10
12
  /** Asks for the token (one hidden question) and saves it. The validator answers with fixed texts only. */
11
- export async function saveGithubTokenFlow(io, store) {
13
+ export async function saveGithubTokenFlow(io, store, verify = verifyGithubToken) {
12
14
  io.log('Make a GitHub token only for sandboxes:');
13
15
  GITHUB_STEPS.forEach((s, i) => io.log(' ' + (i + 1) + '. ' + s));
14
16
  io.log('What you type is not shown. It is kept encrypted for your Windows user and never printed.', 'muted');
@@ -18,11 +20,42 @@ export async function saveGithubTokenFlow(io, store) {
18
20
  });
19
21
  if (token === undefined)
20
22
  cancelled();
21
- const kind = store.save(token.trim());
23
+ // GitHub says whose token it is and when it ends; one it refuses is not saved. No answer (offline) is not a refusal.
24
+ io.progress('Asking GitHub about the token');
25
+ const verdict = await verify(token.trim());
26
+ if (verdict.kind === 'refused')
27
+ fail('GitHub refused this token.', 'Copy it again from GitHub, and check that it has not expired or been revoked.');
28
+ if (verdict.kind === 'unreachable')
29
+ io.log('The token could not be checked (' + verdict.reason + ') and is saved without a check.', 'warn');
30
+ const check = verdict.kind === 'ok'
31
+ ? {
32
+ login: verdict.check.login,
33
+ expiresAt: verdict.check.expiresAt,
34
+ repoCount: verdict.check.repos.count,
35
+ checkedAt: new Date().toISOString(),
36
+ }
37
+ : undefined;
38
+ const kind = store.save(token.trim(), check);
39
+ if (verdict.kind === 'ok') {
40
+ const { repos } = verdict.check;
41
+ io.log('GitHub says this is ' +
42
+ verdict.check.login +
43
+ '. It reaches ' +
44
+ repos.count +
45
+ (repos.count >= 100 ? ' or more' : '') +
46
+ ' repositories' +
47
+ (repos.sample.length
48
+ ? ' (' + repos.sample.join(', ') + (repos.count > repos.sample.length ? ', ...' : '') + ')'
49
+ : '') +
50
+ '.', 'ok');
51
+ const expiry = expiryOf(verdict.check.expiresAt);
52
+ if (expiry)
53
+ io.log(expiry.text + '.', expiry.tone === 'ok' ? 'muted' : 'warn');
54
+ }
22
55
  GITHUB_WARNINGS.forEach((w) => io.log(w, 'warn'));
23
56
  if (kind === 'classic')
24
57
  io.log('This is a classic token: it reaches every repository you can. A fine-grained one is safer.', 'warn');
25
- return { message: 'Saved. Sandboxes get it once you turn it on: sandbox config set github.shareToken true' };
58
+ return { message: 'Saved. Sandboxes get it once you turn on Share the token with sandboxes.' };
26
59
  }
27
60
  /** Deletes the saved token after asking (the CLI's typed logout is the answer). */
28
61
  export async function deleteGithubTokenFlow(io, store) {
@@ -47,6 +80,11 @@ export const githubStatusRun = implement(githubStatus, async (ctx) => {
47
80
  token,
48
81
  kind: token === 'none' ? null : store.kind(),
49
82
  savedAt: token === 'none' ? null : store.savedAt(),
83
+ login: token === 'none' ? null : (store.check()?.login ?? null),
84
+ expiresAt: token === 'none' ? null : (store.check()?.expiresAt ?? null),
85
+ repoCount: token === 'none' ? null : (store.check()?.repoCount ?? null),
86
+ checkedAt: token === 'none' ? null : (store.check()?.checkedAt ?? null),
87
+ tokenUrl: tokenUrlFor(os.hostname()),
50
88
  };
51
89
  });
52
90
  export const githubLoginRun = implement(githubLogin, async (ctx, _input, io) => {
@@ -96,7 +96,7 @@ export function resultLines(r) {
96
96
  if (r.means)
97
97
  out.push({ text: ' What it means: ' + r.means });
98
98
  if (r.fixable && r.plan)
99
- out.push({ text: ' Fix: sandbox repair will ' + r.plan });
99
+ out.push({ text: ' Fix: repair will ' + r.plan });
100
100
  else if (r.fix)
101
101
  out.push({ text: ' Fix: ' + r.fix });
102
102
  const lines = r.lines ?? [];
@@ -277,7 +277,13 @@ export function healthPage(o) {
277
277
  ],
278
278
  })),
279
279
  notes: canEnable
280
- ? [{ text: 'Turn Windows Sandbox on from the menu or with sandbox setup.', tone: 'muted', only: 'web' }]
280
+ ? [
281
+ {
282
+ text: 'Windows asks for administrator permission to turn this on, so do it at this PC from Maintenance > Health in the menu.',
283
+ tone: 'muted',
284
+ only: 'web',
285
+ },
286
+ ]
281
287
  : [],
282
288
  });
283
289
  }
@@ -92,14 +92,6 @@ export function logsPage(files, tail) {
92
92
  return {
93
93
  title: 'Logs',
94
94
  sections: [{ id: 'files', items }, ...tailSection],
95
- actions: [
96
- // The web hides command links (it follows live instead); this one stays because `logs` is a hand-written command.
97
- link({ kind: 'command', argv: ['logs', '--follow'] }, 'Follow the newest log', {
98
- key: 'f',
99
- verb: 'follow in the terminal',
100
- importance: 2,
101
- }),
102
- ],
103
95
  empty: { title: 'No logs yet', text: 'Logs appear after the first start.', cliText: 'No sandbox logs yet.' },
104
96
  };
105
97
  }
@@ -151,7 +151,7 @@ export function projectsPage(o) {
151
151
  {
152
152
  id: 'roots',
153
153
  label: 'Roots',
154
- value: o.roots.length ? o.roots.join(', ') : 'none (sandbox projects roots add <folder>)',
154
+ value: o.roots.length ? o.roots.join(', ') : 'none',
155
155
  },
156
156
  ],
157
157
  actions: [
@@ -185,7 +185,7 @@ export function projectsPage(o) {
185
185
  columnsOn: 'cli',
186
186
  items: o.projects.map(projectItem),
187
187
  notes: o.projects.length === 0
188
- ? [{ text: 'No projects yet: start one with sandbox claude in its folder.', tone: 'muted' }]
188
+ ? [{ text: 'No projects yet: start an agent in a project folder.', tone: 'muted' }]
189
189
  : [{ text: '* = this folder. Choose with: sandbox projects choose', tone: 'muted', only: 'cli' }],
190
190
  };
191
191
  return {
@@ -11,7 +11,7 @@ import { addProject, allProjects, chooseProjects, excludeProject, includeProject
11
11
  const NEXT_START = 'This changes what is mapped from the next start.';
12
12
  function runningNote() {
13
13
  return isSandboxRunning()
14
- ? ' The running sandbox keeps what it has; start a project in it (sandbox claude there) and it joins without a restart when it can, or you are offered one.'
14
+ ? ' The running sandbox keeps what it has; start a project in it (from its folder) and it joins without a restart when it can, or you are offered one.'
15
15
  : '';
16
16
  }
17
17
  /** The folder given, or one picked from the known projects (or this folder) when it was left out. */
@@ -39,7 +39,7 @@ export const chooseProjectsRun = implement(chooseProjects, async (ctx, _input, i
39
39
  const rows = service.rows().filter((r) => !r.root);
40
40
  if (!rows.length)
41
41
  return {
42
- message: 'No projects to choose from yet (projects in a root are always mapped with it). Start one with sandbox claude in its folder.',
42
+ message: 'No projects to choose from yet (projects in a root are always mapped with it). Start an agent in a project folder first.',
43
43
  };
44
44
  const all = service.settings().all;
45
45
  const picked = await io.checklist({
@@ -108,7 +108,7 @@ function includeOrExcludeText(service, action, dir, changed) {
108
108
  ? (action === 'exclude' ? 'Excluded ' : 'Included ') + path.resolve(dir) + '. ' + NEXT_START
109
109
  : 'Nothing changed.') +
110
110
  (action === 'include' && !service.settings().all
111
- ? ' Mapping all projects is off: sandbox projects all on maps your projects at every start.'
111
+ ? ' Mapping all projects is off: turn it on in Projects to map them at every start.'
112
112
  : '') +
113
113
  (changed ? runningNote() : ''));
114
114
  }
@@ -131,11 +131,11 @@ export const projectRootsRun = implement(projectRoots, async (ctx, input, io) =>
131
131
  if (input.change === 'list') {
132
132
  const list = service.settings().roots;
133
133
  return {
134
- message: list.length ? list.join('\n') : 'No project roots. Add one: sandbox projects roots add <folder>',
134
+ message: list.length ? list.join('\n') : 'No project roots. Add one on the Projects page.',
135
135
  };
136
136
  }
137
137
  if (!input.rootFolder)
138
- fail('Which folder?', 'Usage: sandbox projects roots ' + input.change + ' <folder>');
138
+ fail('Which folder?', 'Give the folder to ' + input.change + '.');
139
139
  if (input.change === 'add') {
140
140
  const { dir, added } = service.addRoot(input.rootFolder);
141
141
  return {
@@ -191,7 +191,7 @@ function facts(o) {
191
191
  }),
192
192
  ...(s.running
193
193
  ? [
194
- fact('window', 'Window', s.headless ? 'hidden (sandbox show opens it)' : 'shown'),
194
+ fact('window', 'Window', s.headless ? 'hidden' : 'shown'),
195
195
  fact('uptime', 'Uptime', uptime(s) ?? '-'),
196
196
  // The session count is its own fact: the helper only says whether it answers.
197
197
  fact('helper', 'Helper', s.daemon === '-' ? '-' : s.daemon.startsWith('pong') ? 'Responding' : s.daemon),
@@ -200,7 +200,7 @@ function facts(o) {
200
200
  : []),
201
201
  ...(o.leftover ? [fact('leftover', 'Windows Sandbox', o.leftover.text, { tone: 'warn' })] : []),
202
202
  ...(o.lastEnd ? [fact('lastEnd', 'Last sandbox', o.lastEnd.text, { tone: 'warn' })] : []),
203
- fact('account', 'Account', s.account ?? 'none selected (sandbox account use)'),
203
+ fact('account', 'Account', s.account ?? 'none selected'),
204
204
  fact('sync', 'Background sync', s.syncWatcher ? 'on' : 'off'),
205
205
  ];
206
206
  }
@@ -216,10 +216,11 @@ function projectsSection(o) {
216
216
  id: 'projects',
217
217
  title: 'Projects',
218
218
  items: [...o.projects.map(projectItem), ...roots],
219
+ ...(here ? {} : { actions: [link({ kind: 'view', view: 'agents' }, 'Start an agent here…', { verb: 'start' })] }),
219
220
  notes: [
220
221
  here
221
222
  ? { text: '* = the project of this folder', tone: 'muted' }
222
- : { text: o.here.folder + ' is not in this sandbox (start it here: sandbox claude)', tone: 'muted' },
223
+ : { text: o.here.folder + ' is not in this sandbox.', tone: 'muted' },
223
224
  ],
224
225
  };
225
226
  }
@@ -254,6 +255,9 @@ export function sandboxPage(o) {
254
255
  facts: facts(o),
255
256
  // Only the window action that changes something: show a hidden window, hide a shown one.
256
257
  actions: [
258
+ ...(running && !o.sandbox.account
259
+ ? [link({ kind: 'view', view: 'accounts' }, 'Choose an account…', { verb: 'choose', importance: 4 })]
260
+ : []),
257
261
  ...(o.leftover ? [action(cleanUpSandbox, {}, { key: 'c', verb: 'clean up', importance: 1 })] : []),
258
262
  ...(running
259
263
  ? [
@@ -12,9 +12,9 @@ export declare const SessionRowSchema: z.ZodObject<{
12
12
  cwd: z.ZodString;
13
13
  project: z.ZodOptional<z.ZodString>;
14
14
  phase: z.ZodEnum<{
15
+ running: "running";
15
16
  failed: "failed";
16
17
  starting: "starting";
17
- running: "running";
18
18
  exited: "exited";
19
19
  }>;
20
20
  message: z.ZodString;
@@ -47,9 +47,9 @@ export declare const SessionsListSchema: z.ZodObject<{
47
47
  cwd: z.ZodString;
48
48
  project: z.ZodOptional<z.ZodString>;
49
49
  phase: z.ZodEnum<{
50
+ running: "running";
50
51
  failed: "failed";
51
52
  starting: "starting";
52
- running: "running";
53
53
  exited: "exited";
54
54
  }>;
55
55
  message: z.ZodString;
@@ -89,9 +89,9 @@ export declare const listSessions: import("../../kernel/define.js").QueryDef<z.Z
89
89
  cwd: z.ZodString;
90
90
  project: z.ZodOptional<z.ZodString>;
91
91
  phase: z.ZodEnum<{
92
+ running: "running";
92
93
  failed: "failed";
93
94
  starting: "starting";
94
- running: "running";
95
95
  exited: "exited";
96
96
  }>;
97
97
  message: z.ZodString;
@@ -155,9 +155,9 @@ export declare const sessionsView: import("../../kernel/define.js").ViewDef<{
155
155
  cwd: z.ZodString;
156
156
  project: z.ZodOptional<z.ZodString>;
157
157
  phase: z.ZodEnum<{
158
+ running: "running";
158
159
  failed: "failed";
159
160
  starting: "starting";
160
- running: "running";
161
161
  exited: "exited";
162
162
  }>;
163
163
  message: z.ZodString;
@@ -221,11 +221,6 @@ export function sessionsPage(o) {
221
221
  ...(sessions.length
222
222
  ? [action(restartSessions, { all: false }, { key: 'r', verb: 'restart agents', importance: 1 })]
223
223
  : []),
224
- link({ kind: 'command', argv: ['resume'] }, 'Reopen last sessions', {
225
- key: 'o',
226
- verb: 'reopen last sessions',
227
- importance: 2,
228
- }),
229
224
  ],
230
225
  empty: {
231
226
  title: o.sandboxRunning ? 'No sessions' : 'No sandbox is running',
@@ -17,9 +17,9 @@ export declare const listSessionsRun: import("../../kernel/define.js").Implement
17
17
  cwd: import("zod").ZodString;
18
18
  project: import("zod").ZodOptional<import("zod").ZodString>;
19
19
  phase: import("zod").ZodEnum<{
20
+ running: "running";
20
21
  failed: "failed";
21
22
  starting: "starting";
22
- running: "running";
23
23
  exited: "exited";
24
24
  }>;
25
25
  message: import("zod").ZodString;