windows-development-sandbox 1.10.0 → 1.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/README.md +64 -17
  2. package/config.example.json +5 -1
  3. package/dist/app/features/accounts/accounts.def.d.ts +122 -0
  4. package/dist/app/features/accounts/accounts.def.js +243 -0
  5. package/dist/app/features/accounts/accounts.run.d.ts +51 -0
  6. package/dist/app/features/accounts/accounts.run.js +125 -0
  7. package/dist/app/features/accounts/index.d.ts +2 -0
  8. package/dist/app/features/accounts/index.js +3 -0
  9. package/dist/app/features/agents/agent-entry.d.ts +17 -0
  10. package/dist/app/features/agents/agent-entry.js +50 -0
  11. package/dist/app/features/agents/agents.def.d.ts +65 -0
  12. package/dist/app/features/agents/agents.def.js +142 -0
  13. package/dist/app/features/agents/agents.run.d.ts +31 -0
  14. package/dist/app/features/agents/agents.run.js +189 -0
  15. package/dist/app/features/agents/index.d.ts +3 -0
  16. package/dist/app/features/agents/index.js +4 -0
  17. package/dist/app/features/apps/apps-rows.d.ts +31 -0
  18. package/dist/app/features/apps/apps-rows.js +73 -0
  19. package/dist/app/features/apps/apps.def.d.ts +181 -0
  20. package/dist/app/features/apps/apps.def.js +264 -0
  21. package/dist/app/features/apps/apps.run.d.ts +58 -0
  22. package/dist/app/features/apps/apps.run.js +76 -0
  23. package/dist/app/features/apps/index.d.ts +3 -0
  24. package/dist/app/features/apps/index.js +4 -0
  25. package/dist/app/features/data/data.def.d.ts +154 -0
  26. package/dist/app/features/data/data.def.js +201 -0
  27. package/dist/app/features/data/data.run.d.ts +46 -0
  28. package/dist/app/features/data/data.run.js +141 -0
  29. package/dist/app/features/data/index.d.ts +2 -0
  30. package/dist/app/features/data/index.js +3 -0
  31. package/dist/app/features/defs.d.ts +952 -4
  32. package/dist/app/features/defs.js +147 -10
  33. package/dist/app/features/engine/engine.def.d.ts +228 -0
  34. package/dist/app/features/engine/engine.def.js +194 -0
  35. package/dist/app/features/engine/engine.run.d.ts +72 -0
  36. package/dist/app/features/engine/engine.run.js +77 -0
  37. package/dist/app/features/engine/index.d.ts +2 -0
  38. package/dist/app/features/engine/index.js +4 -0
  39. package/dist/app/features/files/files.def.d.ts +51 -0
  40. package/dist/app/features/files/files.def.js +38 -0
  41. package/dist/app/features/files/files.run.d.ts +20 -0
  42. package/dist/app/features/files/files.run.js +59 -0
  43. package/dist/app/features/files/index.d.ts +2 -0
  44. package/dist/app/features/files/index.js +3 -0
  45. package/dist/app/features/github/github.def.d.ts +80 -0
  46. package/dist/app/features/github/github.def.js +142 -0
  47. package/dist/app/features/github/github.run.d.ts +36 -0
  48. package/dist/app/features/github/github.run.js +59 -0
  49. package/dist/app/features/github/index.d.ts +2 -0
  50. package/dist/app/features/github/index.js +3 -0
  51. package/dist/app/features/health/health.def.d.ts +275 -0
  52. package/dist/app/features/health/health.def.js +281 -0
  53. package/dist/app/features/health/health.run.d.ts +125 -0
  54. package/dist/app/features/health/health.run.js +204 -0
  55. package/dist/app/features/health/index.d.ts +2 -0
  56. package/dist/app/features/health/index.js +3 -0
  57. package/dist/app/features/logs/index.d.ts +2 -0
  58. package/dist/app/features/logs/index.js +3 -0
  59. package/dist/app/features/logs/logs.def.d.ts +78 -0
  60. package/dist/app/features/logs/logs.def.js +115 -0
  61. package/dist/app/features/logs/logs.run.d.ts +20 -0
  62. package/dist/app/features/logs/logs.run.js +42 -0
  63. package/dist/app/features/projects/index.d.ts +2 -0
  64. package/dist/app/features/projects/index.js +3 -0
  65. package/dist/app/features/projects/projects.def.d.ts +101 -0
  66. package/dist/app/features/projects/projects.def.js +208 -0
  67. package/dist/app/features/projects/projects.run.d.ts +56 -0
  68. package/dist/app/features/projects/projects.run.js +155 -0
  69. package/dist/app/features/sandbox/sandbox.def.js +11 -5
  70. package/dist/app/features/sessions/sessions.def.js +3 -0
  71. package/dist/app/features/settings/categories.def.js +3 -2
  72. package/dist/{cli/menu/settings/share.d.ts → app/features/settings/settings-share.d.ts} +1 -1
  73. package/dist/{cli/menu/settings/share.js → app/features/settings/settings-share.js} +3 -2
  74. package/dist/app/features/settings/settings.def.d.ts +22 -5
  75. package/dist/app/features/settings/settings.def.js +81 -6
  76. package/dist/app/features/settings/settings.run.d.ts +17 -0
  77. package/dist/app/features/settings/settings.run.js +89 -4
  78. package/dist/app/features/storage/index.d.ts +2 -0
  79. package/dist/app/features/storage/index.js +3 -0
  80. package/dist/app/features/storage/storage.def.d.ts +133 -0
  81. package/dist/app/features/storage/storage.def.js +225 -0
  82. package/dist/app/features/storage/storage.run.d.ts +62 -0
  83. package/dist/app/features/storage/storage.run.js +370 -0
  84. package/dist/app/features/updates/index.d.ts +3 -0
  85. package/dist/app/features/updates/index.js +4 -0
  86. package/dist/app/features/updates/update-lines.d.ts +13 -0
  87. package/dist/app/features/updates/update-lines.js +61 -0
  88. package/dist/app/features/updates/updates.def.d.ts +89 -0
  89. package/dist/app/features/updates/updates.def.js +225 -0
  90. package/dist/app/features/updates/updates.run.d.ts +48 -0
  91. package/dist/app/features/updates/updates.run.js +154 -0
  92. package/dist/app/features/vpn/index.d.ts +2 -0
  93. package/dist/app/features/vpn/index.js +3 -0
  94. package/dist/app/features/vpn/vpn.def.d.ts +112 -0
  95. package/dist/app/features/vpn/vpn.def.js +174 -0
  96. package/dist/app/features/vpn/vpn.run.d.ts +37 -0
  97. package/dist/app/features/vpn/vpn.run.js +54 -0
  98. package/dist/app/features/web/index.d.ts +2 -0
  99. package/dist/app/features/web/index.js +3 -0
  100. package/dist/app/features/web/web.def.d.ts +33 -0
  101. package/dist/app/features/web/web.def.js +90 -0
  102. package/dist/app/features/web/web.run.d.ts +24 -0
  103. package/dist/app/features/web/web.run.js +93 -0
  104. package/dist/app/kernel/define.d.ts +10 -1
  105. package/dist/app/kernel/io.d.ts +34 -0
  106. package/dist/app/kernel/io.js +9 -0
  107. package/dist/app/kernel/rpc.d.ts +17 -0
  108. package/dist/app/kernel/text.d.ts +2 -0
  109. package/dist/app/kernel/text.js +7 -0
  110. package/dist/app/kernel/view.d.ts +24 -1
  111. package/dist/app/kernel/view.js +1 -1
  112. package/dist/app/registry.js +74 -1
  113. package/dist/cli/args.js +1 -1
  114. package/dist/cli/commands/context.d.ts +7 -0
  115. package/dist/cli/commands/context.js +29 -2
  116. package/dist/cli/commands/engine.js +5 -58
  117. package/dist/cli/commands/github.d.ts +2 -0
  118. package/dist/cli/commands/github.js +11 -0
  119. package/dist/cli/commands/index.d.ts +3 -5
  120. package/dist/cli/commands/index.js +3 -5
  121. package/dist/cli/commands/launch/launch.js +21 -4
  122. package/dist/cli/commands/launch/running.js +4 -1
  123. package/dist/cli/commands/maintenance/config.js +1 -6
  124. package/dist/cli/commands/maintenance/index.d.ts +0 -1
  125. package/dist/cli/commands/maintenance/index.js +1 -2
  126. package/dist/cli/commands/maintenance/logs.js +27 -32
  127. package/dist/cli/commands/maintenance/migrate.d.ts +0 -8
  128. package/dist/cli/commands/maintenance/migrate.js +2 -44
  129. package/dist/cli/commands/maintenance/update.d.ts +0 -19
  130. package/dist/cli/commands/maintenance/update.js +1 -99
  131. package/dist/cli/commands/projects/index.d.ts +0 -1
  132. package/dist/cli/commands/projects/index.js +0 -1
  133. package/dist/cli/commands/registry.d.ts +1 -1
  134. package/dist/cli/commands/registry.js +29 -11
  135. package/dist/cli/commands/repair/checks/data.js +1 -1
  136. package/dist/cli/commands/repair/checks/hcs.js +28 -1
  137. package/dist/cli/commands/repair/checks/sandbox.js +1 -1
  138. package/dist/cli/commands/repair/health-run.d.ts +30 -0
  139. package/dist/cli/commands/repair/health-run.js +32 -0
  140. package/dist/cli/commands/repair/printer.d.ts +2 -2
  141. package/dist/cli/commands/repair/printer.js +2 -11
  142. package/dist/cli/commands/repair/run.d.ts +1 -21
  143. package/dist/cli/commands/repair/run.js +13 -70
  144. package/dist/cli/commands/storage/index.d.ts +0 -1
  145. package/dist/cli/commands/storage/index.js +0 -1
  146. package/dist/cli/commands/storage/offer.js +6 -2
  147. package/dist/cli/commands/vpn.d.ts +1 -3
  148. package/dist/cli/commands/vpn.js +8 -89
  149. package/dist/cli/commands/web.d.ts +2 -1
  150. package/dist/cli/commands/web.js +14 -78
  151. package/dist/cli/index.js +3 -2
  152. package/dist/cli/manifest.d.ts +16 -153
  153. package/dist/cli/manifest.js +20 -50
  154. package/dist/cli/menu/command-form.js +1 -2
  155. package/dist/cli/menu/io-menu.d.ts +2 -2
  156. package/dist/cli/menu/io-menu.js +6 -4
  157. package/dist/cli/menu/menu.js +1 -1
  158. package/dist/cli/menu/pages/accounts-page.d.ts +5 -17
  159. package/dist/cli/menu/pages/accounts-page.js +28 -170
  160. package/dist/cli/menu/pages/agents-page.d.ts +17 -18
  161. package/dist/cli/menu/pages/agents-page.js +67 -124
  162. package/dist/cli/menu/pages/apps-page.d.ts +15 -8
  163. package/dist/cli/menu/pages/apps-page.js +121 -29
  164. package/dist/cli/menu/pages/list-page.js +10 -1
  165. package/dist/cli/menu/pages/maintenance-page.d.ts +51 -52
  166. package/dist/cli/menu/pages/maintenance-page.js +246 -427
  167. package/dist/cli/menu/pages/sandbox-page.d.ts +3 -5
  168. package/dist/cli/menu/pages/sandbox-page.js +34 -112
  169. package/dist/cli/menu/pages/settings-page.d.ts +21 -3
  170. package/dist/cli/menu/pages/settings-page.js +89 -17
  171. package/dist/cli/menu/render-view.js +2 -1
  172. package/dist/cli/menu/settings/index.d.ts +0 -1
  173. package/dist/cli/menu/settings/index.js +3 -3
  174. package/dist/cli/menu/views.js +28 -0
  175. package/dist/cli/operations/handler.js +7 -3
  176. package/dist/cli/operations/input.d.ts +1 -1
  177. package/dist/cli/operations/input.js +29 -5
  178. package/dist/cli/operations/io-cli.js +9 -1
  179. package/dist/cli/operations/spec.js +8 -2
  180. package/dist/cli/output/render-view.js +2 -0
  181. package/dist/cli/{commands/data/collect.js → services/data-collect.js} +2 -2
  182. package/dist/cli/{commands/data/remove.js → services/data-remove.js} +2 -2
  183. package/dist/cli/services/data-service.d.ts +1 -1
  184. package/dist/cli/services/data-service.js +2 -2
  185. package/dist/cli/services/databases-service.d.ts +5 -0
  186. package/dist/cli/services/databases-service.js +15 -0
  187. package/dist/cli/services/health-service.d.ts +8 -4
  188. package/dist/cli/services/health-service.js +10 -8
  189. package/dist/cli/services/index.d.ts +4 -1
  190. package/dist/cli/services/index.js +4 -1
  191. package/dist/cli/services/logs-service.d.ts +7 -0
  192. package/dist/cli/services/logs-service.js +13 -0
  193. package/dist/cli/services/storage-service.d.ts +19 -0
  194. package/dist/cli/services/storage-service.js +71 -0
  195. package/dist/cli/services/vpn-service.d.ts +6 -4
  196. package/dist/cli/services/vpn-service.js +5 -11
  197. package/dist/cli/services/web-service.d.ts +4 -3
  198. package/dist/cli/services/web-service.js +12 -32
  199. package/dist/core/config/defaults.js +2 -1
  200. package/dist/core/schema/config.d.ts +5 -0
  201. package/dist/core/schema/config.js +2 -0
  202. package/dist/core/schema/github.d.ts +4 -0
  203. package/dist/core/schema/github.js +7 -0
  204. package/dist/core/schema/index.d.ts +1 -0
  205. package/dist/core/schema/index.js +1 -0
  206. package/dist/core/schema/profile.d.ts +1 -0
  207. package/dist/core/schema/profile.js +3 -0
  208. package/dist/core/schema/sandbox.js +3 -3
  209. package/dist/core/schema/toolchains.js +2 -2
  210. package/dist/core/types.d.ts +6 -3
  211. package/dist/guest/boot/boot.js +8 -0
  212. package/dist/guest/daemon/daemon.js +1 -1
  213. package/dist/guest/daemon/sessions.d.ts +3 -2
  214. package/dist/guest/daemon/sessions.js +38 -7
  215. package/dist/guest/governor/core.d.ts +44 -0
  216. package/dist/guest/governor/core.js +124 -0
  217. package/dist/guest/governor/decide.d.ts +46 -18
  218. package/dist/guest/governor/decide.js +136 -82
  219. package/dist/guest/governor/governor.d.ts +2 -38
  220. package/dist/guest/governor/governor.js +33 -113
  221. package/dist/guest/governor/index.d.ts +1 -0
  222. package/dist/guest/governor/index.js +1 -0
  223. package/dist/guest/network/firewall.d.ts +2 -0
  224. package/dist/guest/network/firewall.js +3 -3
  225. package/dist/guest/sessions/viewer.js +5 -1
  226. package/dist/guest/setup/github-token.d.ts +7 -0
  227. package/dist/guest/setup/github-token.js +65 -0
  228. package/dist/guest/toolchains/languages/node.js +11 -1
  229. package/dist/guest/toolchains/native/rust-extras.js +2 -2
  230. package/dist/guest/toolchains/tools/cli.d.ts +8 -0
  231. package/dist/guest/toolchains/tools/cli.js +16 -2
  232. package/dist/guest/vpn/openvpn.d.ts +5 -0
  233. package/dist/guest/vpn/openvpn.js +14 -1
  234. package/dist/guest/vpn/vpn.js +55 -16
  235. package/dist/guest/vpn/wireguard.js +2 -0
  236. package/dist/host/channel/attach.d.ts +1 -0
  237. package/dist/host/channel/attach.js +1 -0
  238. package/dist/host/channel/channel.d.ts +1 -1
  239. package/dist/host/channel/channel.js +2 -2
  240. package/dist/host/engines/hcs/check.d.ts +1 -0
  241. package/dist/host/engines/hcs/check.js +1 -1
  242. package/dist/host/engines/hcs/engine.d.ts +1 -1
  243. package/dist/host/engines/hcs/engine.js +6 -4
  244. package/dist/host/engines/hcs/helper.d.ts +11 -0
  245. package/dist/host/engines/hcs/helper.js +31 -2
  246. package/dist/host/engines/hcs/leftovers.d.ts +42 -0
  247. package/dist/host/engines/hcs/leftovers.js +101 -0
  248. package/dist/host/engines/index.d.ts +1 -0
  249. package/dist/host/engines/index.js +1 -0
  250. package/dist/host/github/deliver.d.ts +13 -0
  251. package/dist/host/github/deliver.js +51 -0
  252. package/dist/host/github/index.d.ts +2 -0
  253. package/dist/host/github/index.js +3 -0
  254. package/dist/host/github/token.d.ts +21 -0
  255. package/dist/host/github/token.js +74 -0
  256. package/dist/host/launch/generate.js +11 -5
  257. package/dist/host/launch/index.d.ts +1 -1
  258. package/dist/host/launch/index.js +1 -1
  259. package/dist/host/launch/session-file.js +1 -2
  260. package/dist/host/launch/wsb-file.d.ts +11 -7
  261. package/dist/host/launch/wsb-file.js +30 -12
  262. package/dist/host/life/watchdog.d.ts +9 -0
  263. package/dist/host/life/watchdog.js +17 -1
  264. package/dist/host/sandbox/tasklist.d.ts +6 -0
  265. package/dist/host/sandbox/tasklist.js +37 -0
  266. package/dist/host/secrets/launch-secrets.d.ts +2 -1
  267. package/dist/host/secrets/launch-secrets.js +4 -2
  268. package/dist/host/vpn/status.js +13 -3
  269. package/dist/host/web/backend.d.ts +1 -3
  270. package/dist/host/web/index.d.ts +2 -1
  271. package/dist/host/web/index.js +2 -1
  272. package/dist/host/web/live.d.ts +23 -0
  273. package/dist/host/web/live.js +107 -0
  274. package/dist/host/web/rpc.d.ts +6 -1
  275. package/dist/host/web/rpc.js +23 -31
  276. package/dist/host/web/runner.d.ts +4 -0
  277. package/dist/host/web/runner.js +12 -1
  278. package/dist/host/web/server.d.ts +30 -2
  279. package/dist/host/web/server.js +87 -29
  280. package/dist/host/web/state.d.ts +1 -0
  281. package/dist/host/web/state.js +2 -0
  282. package/dist/runtime/agent.js +30 -6
  283. package/dist/runtime/daemon.js +323 -176
  284. package/dist/runtime/db.js +30 -6
  285. package/dist/runtime/main.js +375 -76
  286. package/dist/runtime/netproxy.js +30 -6
  287. package/dist/runtime/view.js +495 -146
  288. package/dist/selftest/app.test.js +620 -12
  289. package/dist/selftest/channel.test.js +18 -1
  290. package/dist/selftest/cli.test.js +1 -1
  291. package/dist/selftest/config.test.js +1 -1
  292. package/dist/selftest/contained.test.js +1 -1
  293. package/dist/selftest/data.test.js +1 -2
  294. package/dist/selftest/engine.test.js +3 -1
  295. package/dist/selftest/github.test.d.ts +2 -0
  296. package/dist/selftest/github.test.js +243 -0
  297. package/dist/selftest/hcs.test.js +105 -8
  298. package/dist/selftest/health.test.js +71 -0
  299. package/dist/selftest/heavy-load.test.js +132 -72
  300. package/dist/selftest/index.js +4 -0
  301. package/dist/selftest/layout.test.js +8 -0
  302. package/dist/selftest/live-terminal.test.js +74 -0
  303. package/dist/selftest/menu-screens.js +171 -56
  304. package/dist/selftest/menu.test.js +54 -37
  305. package/dist/selftest/migrate.test.js +14 -12
  306. package/dist/selftest/powershell-variables.test.d.ts +2 -0
  307. package/dist/selftest/powershell-variables.test.js +61 -0
  308. package/dist/selftest/support/engine-golden.js +1 -0
  309. package/dist/selftest/support/fixtures.d.ts +4 -0
  310. package/dist/selftest/support/web-preview.d.ts +1 -0
  311. package/dist/selftest/support/web-preview.js +856 -0
  312. package/dist/selftest/update-service.test.js +15 -8
  313. package/dist/selftest/vpn.test.js +86 -2
  314. package/dist/selftest/web.test.js +58 -11
  315. package/dist/shared/channel/run-dir.d.ts +2 -0
  316. package/dist/shared/channel/run-dir.js +3 -0
  317. package/dist/shared/channel/schema.d.ts +7 -0
  318. package/dist/shared/channel/schema.js +9 -1
  319. package/dist/shared/channel/secrets.d.ts +1 -0
  320. package/dist/shared/channel/secrets.js +3 -0
  321. package/dist/shared/github/index.d.ts +1 -0
  322. package/dist/shared/github/index.js +2 -0
  323. package/dist/shared/github/token.d.ts +29 -0
  324. package/dist/shared/github/token.js +89 -0
  325. package/dist/shared/memory/policy.d.ts +33 -12
  326. package/dist/shared/memory/policy.js +50 -19
  327. package/dist/shared/pty/ctrl-c.d.ts +18 -0
  328. package/dist/shared/pty/ctrl-c.js +36 -0
  329. package/dist/shared/pty/index.d.ts +1 -0
  330. package/dist/shared/pty/index.js +1 -0
  331. package/dist/shared/pty/terminal.d.ts +2 -0
  332. package/dist/shared/pty/terminal.js +21 -2
  333. package/dist/shared/vpn/kill-switch.d.ts +13 -2
  334. package/dist/shared/vpn/kill-switch.js +88 -8
  335. package/dist/shared/web/protocol.d.ts +0 -4
  336. package/dist/sync/watcher.js +1 -1
  337. package/dist/web/index.html +2 -2
  338. package/dist/web/{main-T5DMCHYV.css → main-6JF3ASB5.css} +1 -1
  339. package/dist/web/main-Z645W45E.js +219 -0
  340. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  341. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  342. package/package.json +10 -3
  343. package/sandbox/csharp/MemGov.cs +240 -38
  344. package/sandbox/csharp/PtyHost.cs +104 -15
  345. package/sandbox/csharp/SandboxTools/CpuCap.cs +50 -1
  346. package/sandbox/csharp/SandboxTools/Program.cs +1 -1
  347. package/sandbox/scripts/repair-wmi-services.ps1 +26 -0
  348. package/schemas/config.schema.json +19 -5
  349. package/schemas/sandbox-config.schema.json +7 -3
  350. package/dist/cli/commands/accounts.d.ts +0 -2
  351. package/dist/cli/commands/accounts.js +0 -96
  352. package/dist/cli/commands/data/clean.d.ts +0 -3
  353. package/dist/cli/commands/data/clean.js +0 -60
  354. package/dist/cli/commands/data/index.d.ts +0 -4
  355. package/dist/cli/commands/data/index.js +0 -62
  356. package/dist/cli/commands/maintenance/clean.d.ts +0 -2
  357. package/dist/cli/commands/maintenance/clean.js +0 -24
  358. package/dist/cli/commands/maintenance/config-share.d.ts +0 -12
  359. package/dist/cli/commands/maintenance/config-share.js +0 -80
  360. package/dist/cli/commands/projects/projects.d.ts +0 -2
  361. package/dist/cli/commands/projects/projects.js +0 -162
  362. package/dist/cli/commands/storage/contained.d.ts +0 -3
  363. package/dist/cli/commands/storage/contained.js +0 -179
  364. package/dist/cli/commands/storage/storage.d.ts +0 -8
  365. package/dist/cli/commands/storage/storage.js +0 -179
  366. package/dist/cli/menu/add-agent.d.ts +0 -47
  367. package/dist/cli/menu/add-agent.js +0 -168
  368. package/dist/cli/menu/apps.d.ts +0 -69
  369. package/dist/cli/menu/apps.js +0 -214
  370. package/dist/cli/menu/data-folder.d.ts +0 -18
  371. package/dist/cli/menu/data-folder.js +0 -99
  372. package/dist/cli/menu/share-settings.d.ts +0 -21
  373. package/dist/cli/menu/share-settings.js +0 -112
  374. package/dist/cli/menu/vpn-login.d.ts +0 -16
  375. package/dist/cli/menu/vpn-login.js +0 -74
  376. package/dist/web/main-NRRS5O6Z.js +0 -157
  377. /package/dist/cli/{commands/data/collect.d.ts → services/data-collect.d.ts} +0 -0
  378. /package/dist/cli/{commands/data/remove.d.ts → services/data-remove.d.ts} +0 -0
@@ -0,0 +1,13 @@
1
+ import type { BootWant } from '../../core/local-config/index.js';
2
+ import type { Config } from '../../core/types.js';
3
+ import { GithubToken } from './token.js';
4
+ /** github.shareToken is on and git access is "full" for every project this sandbox boots with. */
5
+ export declare function githubTokenAllowed(config: Config, others?: readonly BootWant[]): boolean;
6
+ export interface GithubDelivery {
7
+ /** The token for the one-shot secrets file, when it is handed over. */
8
+ readonly token?: string;
9
+ /** What to tell you about it (never the token). */
10
+ readonly notes: readonly string[];
11
+ }
12
+ /** What this launch hands over. Never throws: a token that cannot be read is a note, and the launch goes on. */
13
+ export declare function prepareGithubToken(config: Config, storageRoot: string, store?: GithubToken, others?: readonly BootWant[]): GithubDelivery;
@@ -0,0 +1,51 @@
1
+ import { GithubToken } from './token.js';
2
+ /** The first project (the launch's own, then the others at boot) whose git access is not "full", or null. */
3
+ function stricterProject(config, others) {
4
+ if (config.provisioning.git.access !== 'full')
5
+ return { name: '', access: config.provisioning.git.access };
6
+ const other = others.find((o) => o.config.provisioning.git.access !== 'full');
7
+ return other ? { name: other.name, access: other.config.provisioning.git.access } : null;
8
+ }
9
+ /** github.shareToken is on and git access is "full" for every project this sandbox boots with. */
10
+ export function githubTokenAllowed(config, others = []) {
11
+ return config.github.shareToken && stricterProject(config, others) === null;
12
+ }
13
+ /** What this launch hands over. Never throws: a token that cannot be read is a note, and the launch goes on. */
14
+ export function prepareGithubToken(config, storageRoot, store, others = []) {
15
+ if (!config.github.shareToken)
16
+ return { notes: [] };
17
+ const stricter = stricterProject(config, others);
18
+ if (stricter)
19
+ return {
20
+ notes: [
21
+ 'github.shareToken is on, but git access is "' +
22
+ stricter.access +
23
+ '"' +
24
+ (stricter.name ? ' for ' + stricter.name + ' (also in this sandbox)' : ' in this sandbox') +
25
+ ', so your GitHub token was not handed over (it needs provisioning.git.access "full").',
26
+ ],
27
+ };
28
+ const tokens = store ?? new GithubToken(storageRoot);
29
+ let token;
30
+ try {
31
+ token = tokens.read();
32
+ }
33
+ catch {
34
+ // The reason may come from DPAPI or a damaged file; a fixed text is enough (and can never hold the token).
35
+ return {
36
+ notes: [
37
+ 'Your saved GitHub token could not be read (only the Windows user that saved it can), so it was not handed over. Save it again: sandbox github login',
38
+ ],
39
+ };
40
+ }
41
+ if (!token)
42
+ return {
43
+ notes: ['github.shareToken is on, but no GitHub token is saved. Save one: sandbox github login'],
44
+ };
45
+ return {
46
+ token,
47
+ notes: [
48
+ 'Your GitHub token is handed to this sandbox (github.shareToken): the agent can read it and use it for whatever it allows.',
49
+ ],
50
+ };
51
+ }
@@ -0,0 +1,2 @@
1
+ export { GithubToken, checkGithubToken, type TokenState } from './token.js';
2
+ export { githubTokenAllowed, prepareGithubToken, type GithubDelivery } from './deliver.js';
@@ -0,0 +1,3 @@
1
+ // The GitHub token for sandboxes: its encrypted store and whether a launch hands it over.
2
+ export { GithubToken, checkGithubToken } from './token.js';
3
+ export { githubTokenAllowed, prepareGithubToken } from './deliver.js';
@@ -0,0 +1,21 @@
1
+ import { type GithubTokenKind } from '../../shared/github/index.js';
2
+ import { SecretStore } from '../secrets/index.js';
3
+ /** What the status may say about the token: never its value. */
4
+ export type TokenState = 'none' | 'saved' | 'unreadable';
5
+ /** The token's kind, or a refusal that says what to do (the message never holds what was typed). */
6
+ export declare function checkGithubToken(token: string): GithubTokenKind;
7
+ export declare class GithubToken {
8
+ private readonly storageRoot;
9
+ private readonly secrets;
10
+ constructor(storageRoot: string, secrets?: SecretStore);
11
+ private file;
12
+ /** Checks, encrypts and saves the token (replacing an earlier one). Returns its kind. */
13
+ save(token: string): GithubTokenKind;
14
+ /** The token, decrypted. Only the launch calls this; throws when this Windows user cannot open it. */
15
+ read(): string | null;
16
+ /** Whether a token is saved and can be decrypted by this Windows user (the value is never returned). */
17
+ state(): TokenState;
18
+ savedAt(): string | null;
19
+ kind(): GithubTokenKind | null;
20
+ remove(): boolean;
21
+ }
@@ -0,0 +1,74 @@
1
+ // The GitHub token you saved for sandboxes (github.shareToken), kept encrypted with Windows DPAPI in host-only storage
2
+ // (<storage>\github\token.json). It is read only when a sandbox starts, to hand to the sandbox in a one-shot file, and
3
+ // is never printed, logged or returned to a surface: they only learn its state, kind and when it was saved.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { z } from 'zod';
7
+ import { JsonFile } from '../../shared/io/index.js';
8
+ import { GITHUB_TOKEN_KINDS, GithubTokenSchema, githubTokenKind, githubTokenProblem, } from '../../shared/github/index.js';
9
+ import { fail } from '../../core/util/index.js';
10
+ import { SecretStore } from '../secrets/index.js';
11
+ const SavedSchema = z.object({
12
+ savedAt: z.string().max(64),
13
+ kind: z.enum(GITHUB_TOKEN_KINDS),
14
+ sealed: z.string().max(10000),
15
+ });
16
+ /** The token's kind, or a refusal that says what to do (the message never holds what was typed). */
17
+ export function checkGithubToken(token) {
18
+ const problem = githubTokenProblem(token);
19
+ if (problem)
20
+ fail(problem.message, problem.fix);
21
+ return githubTokenKind(token) ?? fail('That is not a GitHub personal access token.', 'Run: sandbox github login');
22
+ }
23
+ export class GithubToken {
24
+ storageRoot;
25
+ secrets;
26
+ constructor(storageRoot, secrets = SecretStore.standard()) {
27
+ this.storageRoot = storageRoot;
28
+ this.secrets = secrets;
29
+ }
30
+ file() {
31
+ return new JsonFile(SavedSchema, path.join(this.storageRoot, 'github', 'token.json'));
32
+ }
33
+ /** Checks, encrypts and saves the token (replacing an earlier one). Returns its kind. */
34
+ save(token) {
35
+ const kind = checkGithubToken(token);
36
+ this.file().write({ savedAt: new Date().toISOString(), kind, sealed: this.secrets.sealJson({ token }) });
37
+ return kind;
38
+ }
39
+ /** The token, decrypted. Only the launch calls this; throws when this Windows user cannot open it. */
40
+ read() {
41
+ const saved = this.file().read();
42
+ if (!saved)
43
+ return null;
44
+ const value = this.secrets.openJson(saved.sealed);
45
+ const token = GithubTokenSchema.safeParse(value?.token);
46
+ if (!token.success)
47
+ fail('The saved GitHub token is damaged.', 'Save it again with: sandbox github login');
48
+ return token.data;
49
+ }
50
+ /** Whether a token is saved and can be decrypted by this Windows user (the value is never returned). */
51
+ state() {
52
+ if (!this.file().read())
53
+ return 'none';
54
+ try {
55
+ return this.read() ? 'saved' : 'none';
56
+ }
57
+ catch {
58
+ return 'unreadable';
59
+ }
60
+ }
61
+ savedAt() {
62
+ return this.file().read()?.savedAt ?? null;
63
+ }
64
+ kind() {
65
+ return this.file().read()?.kind ?? null;
66
+ }
67
+ remove() {
68
+ const file = this.file().file;
69
+ if (!fs.existsSync(file))
70
+ return false;
71
+ fs.rmSync(file, { force: true });
72
+ return true;
73
+ }
74
+ }
@@ -9,6 +9,7 @@ import { dirExists, ensureDir, fail, writeJson } from '../../core/util/index.js'
9
9
  import { engineFor } from '../engines/index.js';
10
10
  import { capturePersonalization, stageClaudeProfile } from '../profile/index.js';
11
11
  import { clearSecrets, writeSecrets } from '../secrets/index.js';
12
+ import { githubTokenAllowed } from '../github/index.js';
12
13
  import { folderAttachment } from '../storage/providers/index.js';
13
14
  import { assertTestWritable } from '../test-guard.js';
14
15
  import { writeLoadSettings } from '../life/watchdog.js';
@@ -20,7 +21,7 @@ import { guestSession, logonEnv } from './session-file.js';
20
21
  import { copyRuntime, stageGitIdentity, stageHooks } from './stage.js';
21
22
  import { decideProjectToolchains } from './toolchains.js';
22
23
  import { buildLaunchPlan } from './launch-plan.js';
23
- import { decideMemory, GUEST, memoryNote, resolveMemoryMb } from './wsb-file.js';
24
+ import { cappedMemoryMb, decideMemory, GUEST, memoryNote, residentNote, resolveMemoryMb } from './wsb-file.js';
24
25
  const NO_PROFILE = { hostDir: null, staged: [], skipped: [], credentials: false, secretsRemoved: [] };
25
26
  /** Moves the staged copy of this PC's Claude login out of the launch folder; its text goes in the one-shot file. */
26
27
  function takeStagedCredentials(launchPath) {
@@ -59,7 +60,9 @@ opened) {
59
60
  assertTestWritable(dataPath);
60
61
  // Checked before anything is written, so a refusal leaves nothing behind.
61
62
  const commitAvailableMb = o.commitAvailableMb ?? null;
62
- const memoryMb = Math.max(resolveMemoryMb(config.sandbox, o.freeMemoryBytes, undefined, commitAvailableMb), ...others.map((p) => (typeof p.config.sandbox.memoryMb === 'number' ? p.config.sandbox.memoryMb : 0)));
63
+ const askedMb = Math.max(resolveMemoryMb(config.sandbox, o.freeMemoryBytes, undefined, commitAvailableMb), ...others.map((p) => (typeof p.config.sandbox.memoryMb === 'number' ? p.config.sandbox.memoryMb : 0)));
64
+ // Never more than this PC can keep in RAM for it, whatever was asked.
65
+ const memoryMb = cappedMemoryMb(askedMb, config.sandbox, Math.floor(os.totalmem() / 1048576));
63
66
  const launchId = Date.now() + '-' + crypto.randomBytes(3).toString('hex');
64
67
  const launchPath = path.join(storageRoot, 'launches', launchId);
65
68
  // Whatever must stay secret goes in a one-shot file in the data folder, never into the launch folder (the sandbox
@@ -104,6 +107,8 @@ opened) {
104
107
  ...(claudeCredentials ? { claudeCredentials } : {}),
105
108
  ...(o.vpn?.credentials ? { vpn: o.vpn.credentials } : {}),
106
109
  ...(o.vpn?.wireguardConfig ? { wireguardConfig: o.vpn.wireguardConfig } : {}),
110
+ // Checked again on the configs the sandbox boots with: never when a project in it has git access below "full".
111
+ ...(o.githubToken && githubTokenAllowed(config, others) ? { githubToken: o.githubToken } : {}),
107
112
  });
108
113
  fs.writeFileSync(path.join(launchPath, 'env.cmd'), logonEnv(session.workspace), 'utf8');
109
114
  copyRuntime(o.packageRoot, launchPath);
@@ -127,9 +132,10 @@ opened) {
127
132
  launchId,
128
133
  sessionPath,
129
134
  memoryMb,
130
- memoryNote: config.sandbox.memoryMb === 'auto'
131
- ? memoryNote(decideMemory(config.sandbox, Math.floor((o.freeMemoryBytes ?? os.freemem()) / 1048576), Math.floor(os.totalmem() / 1048576), commitAvailableMb))
132
- : null,
135
+ memoryNote: residentNote(askedMb, memoryMb) ??
136
+ (config.sandbox.memoryMb === 'auto'
137
+ ? memoryNote(decideMemory(config.sandbox, Math.floor((o.freeMemoryBytes ?? os.freemem()) / 1048576), Math.floor(os.totalmem() / 1048576), commitAvailableMb))
138
+ : null),
133
139
  bootConfig: config,
134
140
  widened: wide.widened,
135
141
  mappings,
@@ -1,7 +1,7 @@
1
1
  export { generateSandbox } from './generate.js';
2
2
  export { decideProjectToolchains } from './toolchains.js';
3
3
  export { hostToolsOf, logonEnv } from './session-file.js';
4
- export { agentMemoryLimitMb, decideMemory, GUEST, memoryNote, resolveMemoryMb } from './wsb-file.js';
4
+ export { cappedMemoryMb, decideMemory, GUEST, memoryNote, residentCapMb, residentNote, resolveMemoryMb, } from './wsb-file.js';
5
5
  export { GENTLE_NOTE, vgpuSource, vgpuWarning } from './risk-notes.js';
6
6
  export { buildLaunchPlan, buildWsb } from './launch-plan.js';
7
7
  export { MAX_BOOT_PROJECTS, planBootProjects, shownByRoot, type BootProjects } from './boot-projects.js';
@@ -1,7 +1,7 @@
1
1
  export { generateSandbox } from './generate.js';
2
2
  export { decideProjectToolchains } from './toolchains.js';
3
3
  export { hostToolsOf, logonEnv } from './session-file.js';
4
- export { agentMemoryLimitMb, decideMemory, GUEST, memoryNote, resolveMemoryMb } from './wsb-file.js';
4
+ export { cappedMemoryMb, decideMemory, GUEST, memoryNote, residentCapMb, residentNote, resolveMemoryMb, } from './wsb-file.js';
5
5
  export { GENTLE_NOTE, vgpuSource, vgpuWarning } from './risk-notes.js';
6
6
  export { buildLaunchPlan, buildWsb } from './launch-plan.js';
7
7
  export { MAX_BOOT_PROJECTS, planBootProjects, shownByRoot } from './boot-projects.js';
@@ -5,7 +5,6 @@ import { resolveToolchains } from '../../core/detect/index.js';
5
5
  import { hubName, PROJECTS_GUEST, substDrives } from '../../shared/paths/index.js';
6
6
  import { hostClaudeDir } from '../profile/index.js';
7
7
  import { databaseCatalog } from './database-plan.js';
8
- import { agentMemoryLimitMb } from './wsb-file.js';
9
8
  import { effectiveCpuLimit } from '../../shared/memory/index.js';
10
9
  import { loadProfileOf } from './risk-notes.js';
11
10
  /** What was found on this PC to copy instead of download. */
@@ -65,7 +64,7 @@ export function guestSession(o, parts) {
65
64
  cpuLimitPercent: effectiveCpuLimit(config.sandbox.cpuLimit, loadProfileOf(config)),
66
65
  loadProfile: loadProfileOf(config),
67
66
  pageFileMb: config.sandbox.pageFileMb,
68
- agentMemoryLimitMb: agentMemoryLimitMb(parts.memoryMb, config.sandbox.agentMemoryHeadroomMb),
67
+ agentBackstop: config.sandbox.agentMemoryHeadroomMb,
69
68
  pressureResponse: config.sandbox.pressureResponse,
70
69
  apps: config.apps,
71
70
  auditProcesses: config.audit.enabled && config.audit.processes,
@@ -6,7 +6,7 @@ export declare const GUEST: {
6
6
  readonly mirror: "C:\\Mirror";
7
7
  };
8
8
  type SandboxSettings = Config['sandbox'];
9
- export type MemoryLimit = 'free' | 'share' | 'max' | 'commit';
9
+ export type MemoryLimit = 'free' | 'share' | 'max' | 'commit' | 'resident';
10
10
  export interface MemoryDecision {
11
11
  readonly mb: number;
12
12
  /** What set the size. */
@@ -17,18 +17,22 @@ export interface MemoryDecision {
17
17
  readonly freeMb: number;
18
18
  readonly commitAvailableMb: number | null;
19
19
  }
20
+ /**
21
+ * The most memory this PC can keep in RAM for the sandbox (pure): its RAM minus max(reserveHostMb, 25 %), and 2 GB
22
+ * more with vGPU. Windows Sandbox's memory is this PC's memory taken as needed; beyond what the PC can keep resident it
23
+ * is paged to this PC's disk while the sandbox pages to its own, and that is what can freeze the whole PC.
24
+ */
25
+ export declare function residentCapMb(sandbox: SandboxSettings, totalMb: number): number;
26
+ /** A fixed memoryMb above what this PC can keep resident is lowered to that (never below 4 GB). */
27
+ export declare function cappedMemoryMb(memoryMb: number, sandbox: SandboxSettings, totalMb: number): number;
28
+ /** The launch note when a fixed memoryMb was lowered. */
29
+ export declare function residentNote(asked: number, given: number): string | null;
20
30
  /**
21
31
  * "auto" (pure): free RAM minus the reserve, never more than maxMemoryMb or maxMemoryPercent of the PC's total RAM,
22
32
  * and, when the PC's available commit (RAM plus paging file, what the sandbox's memory is charged against) is known,
23
33
  * never more than that minus the reserve either.
24
34
  */
25
35
  export declare function decideMemory(sandbox: SandboxSettings, freeMb: number, totalMb: number, commitAvailableMb: number | null): MemoryDecision;
26
- /**
27
- * The most memory everything the agents start may use together (pure): the sandbox's memory minus the headroom
28
- * ("auto" =
29
- * the larger of 2048 MB and 10 %), at least half of it so a tiny sandbox still works; null = no limit ("none").
30
- */
31
- export declare function agentMemoryLimitMb(memoryMb: number, headroom: SandboxSettings['agentMemoryHeadroomMb']): number | null;
32
36
  /** A plain-words note when the commit made the sandbox's memory much smaller (2 GB or more) than free RAM allowed. */
33
37
  export declare function memoryNote(decision: MemoryDecision): string | null;
34
38
  /** "auto": see decideMemory. commitAvailableMb: from HostTools memory-status (omitted = not checked). */
@@ -11,6 +11,34 @@ export const GUEST = {
11
11
  const MB = 1048576;
12
12
  const MIN_AUTO_MEMORY_MB = 4096;
13
13
  const roundDown = (mb) => Math.floor(mb / 1024) * 1024;
14
+ /** GPU-PV (vGPU) needs room of its own on this PC: memory the graphics driver pins for the sandbox. */
15
+ const VGPU_RESERVE_MB = 2048;
16
+ /**
17
+ * The most memory this PC can keep in RAM for the sandbox (pure): its RAM minus max(reserveHostMb, 25 %), and 2 GB
18
+ * more with vGPU. Windows Sandbox's memory is this PC's memory taken as needed; beyond what the PC can keep resident it
19
+ * is paged to this PC's disk while the sandbox pages to its own, and that is what can freeze the whole PC.
20
+ */
21
+ export function residentCapMb(sandbox, totalMb) {
22
+ const keep = Math.max(sandbox.reserveHostMb, Math.floor(totalMb / 4)) + (sandbox.vGpu ? VGPU_RESERVE_MB : 0);
23
+ return roundDown(totalMb - keep);
24
+ }
25
+ /** A fixed memoryMb above what this PC can keep resident is lowered to that (never below 4 GB). */
26
+ export function cappedMemoryMb(memoryMb, sandbox, totalMb) {
27
+ const cap = residentCapMb(sandbox, totalMb);
28
+ return cap >= MIN_AUTO_MEMORY_MB && memoryMb > cap ? cap : memoryMb;
29
+ }
30
+ /** The launch note when a fixed memoryMb was lowered. */
31
+ export function residentNote(asked, given) {
32
+ if (given >= asked)
33
+ return null;
34
+ return ('sandbox.memoryMb asks for ' +
35
+ asked +
36
+ ' MB; this PC can keep ' +
37
+ given +
38
+ ' MB in RAM for the sandbox (its RAM minus what it needs itself), so the sandbox gets ' +
39
+ given +
40
+ " MB. More would be paged to this PC's disk, which can freeze the whole PC.");
41
+ }
14
42
  /**
15
43
  * "auto" (pure): free RAM minus the reserve, never more than maxMemoryMb or maxMemoryPercent of the PC's total RAM,
16
44
  * and, when the PC's available commit (RAM plus paging file, what the sandbox's memory is charged against) is known,
@@ -25,22 +53,12 @@ export function decideMemory(sandbox, freeMb, totalMb, commitAvailableMb) {
25
53
  ['share', shareMb],
26
54
  ['free', byFree],
27
55
  ['commit', byCommit],
56
+ ['resident', residentCapMb(sandbox, totalMb)],
28
57
  ];
29
58
  const lowest = candidates.reduce((a, b) => (b[1] < a[1] ? b : a));
30
- const withoutCommit = roundDown(Math.min(sandbox.maxMemoryMb, shareMb, byFree));
59
+ const withoutCommit = roundDown(Math.min(sandbox.maxMemoryMb, shareMb, byFree, residentCapMb(sandbox, totalMb)));
31
60
  return { mb: roundDown(lowest[1]), limitedBy: lowest[0], withoutCommitMb: withoutCommit, freeMb, commitAvailableMb };
32
61
  }
33
- /**
34
- * The most memory everything the agents start may use together (pure): the sandbox's memory minus the headroom
35
- * ("auto" =
36
- * the larger of 2048 MB and 10 %), at least half of it so a tiny sandbox still works; null = no limit ("none").
37
- */
38
- export function agentMemoryLimitMb(memoryMb, headroom) {
39
- if (headroom === 'none')
40
- return null;
41
- const keep = headroom === 'auto' ? Math.max(2048, Math.floor(memoryMb / 10)) : headroom;
42
- return Math.max(Math.floor(memoryMb / 2), memoryMb - keep);
43
- }
44
62
  /** A plain-words note when the commit made the sandbox's memory much smaller (2 GB or more) than free RAM allowed. */
45
63
  export function memoryNote(decision) {
46
64
  if (decision.limitedBy !== 'commit' || decision.withoutCommitMb - decision.mb < 2048)
@@ -3,6 +3,8 @@ import { type HostThresholds, type LoadProfile } from '../../shared/memory/index
3
3
  import { type HostMemory } from './host-memory.js';
4
4
  export interface WatchdogState {
5
5
  readonly low: boolean;
6
+ /** Nearly out of memory: the request asks for the governor's last step. */
7
+ readonly severe: boolean;
6
8
  readonly calm: number;
7
9
  /** Seconds the CPU has been above the limit. */
8
10
  readonly cpuHighSeconds: number;
@@ -31,6 +33,8 @@ export interface WatchdogStep {
31
33
  readonly send: boolean;
32
34
  /** Why the PC is low (plain words), empty when it is not. */
33
35
  readonly reason: string;
36
+ /** Nearly out of memory: builds go to the governor's last step. */
37
+ readonly severe: boolean;
34
38
  /** The state changed (the log says so). */
35
39
  readonly changed: boolean;
36
40
  }
@@ -40,6 +44,11 @@ export interface WatchdogStep {
40
44
  * `scale` widens the limits (1.25 = the level it must climb back above).
41
45
  */
42
46
  export declare function lowReason(memory: HostMemory, reserveHostMb: number, t: HostThresholds, scale: number): string;
47
+ /**
48
+ * Nearly out of memory on this PC: under 1.5 GB of RAM free, or commit within max(2 GB, 4 %) of its limit. Then the
49
+ * sandbox's builds go to the governor's last step at once (one compiler at a time, 15 % CPU) until it passes.
50
+ */
51
+ export declare function severeHost(memory: HostMemory): boolean;
43
52
  /** One watchdog step. Hysteresis: low starts at the limits and ends above 1.25 times them for calmSamples samples. */
44
53
  export declare function watchdogStep(prev: WatchdogState, input: WatchdogInput): WatchdogStep;
45
54
  /** Total CPU use between two os.cpus() readings (percent). */
@@ -5,6 +5,7 @@ import { HOST_THRESHOLDS } from '../../shared/memory/index.js';
5
5
  import { hostMemoryLine } from './host-memory.js';
6
6
  export const WATCHDOG_START = {
7
7
  low: false,
8
+ severe: false,
8
9
  calm: 0,
9
10
  cpuHighSeconds: 0,
10
11
  diskHighSeconds: 0,
@@ -27,6 +28,17 @@ export function lowReason(memory, reserveHostMb, t, scale) {
27
28
  return 'only ' + memory.commitAvailableMb + ' MB of memory (RAM plus paging file) is left on this PC';
28
29
  return '';
29
30
  }
31
+ /**
32
+ * Nearly out of memory on this PC: under 1.5 GB of RAM free, or commit within max(2 GB, 4 %) of its limit. Then the
33
+ * sandbox's builds go to the governor's last step at once (one compiler at a time, 15 % CPU) until it passes.
34
+ */
35
+ export function severeHost(memory) {
36
+ return (memory.availableMb < SEVERE_AVAILABLE_MB ||
37
+ memory.commitAvailableMb < Math.max(SEVERE_COMMIT_MB, (memory.commitLimitMb * SEVERE_COMMIT_PERCENT) / 100));
38
+ }
39
+ const SEVERE_AVAILABLE_MB = 1536;
40
+ const SEVERE_COMMIT_MB = 2048;
41
+ const SEVERE_COMMIT_PERCENT = 4;
30
42
  /** One watchdog step. Hysteresis: low starts at the limits and ends above 1.25 times them for calmSamples samples. */
31
43
  export function watchdogStep(prev, input) {
32
44
  const t = HOST_THRESHOLDS[input.profile];
@@ -54,10 +66,13 @@ export function watchdogStep(prev, input) {
54
66
  calm = 0;
55
67
  }
56
68
  }
57
- const send = low && input.nowMs - prev.lastSentAt >= RESEND_MS;
69
+ const severe = low && input.memory !== null && severeHost(input.memory);
70
+ // Getting severe is sent at once; otherwise the request is repeated every RESEND_MS while the PC stays low.
71
+ const send = low && (input.nowMs - prev.lastSentAt >= RESEND_MS || (severe && !prev.severe));
58
72
  return {
59
73
  state: {
60
74
  low,
75
+ severe,
61
76
  calm,
62
77
  cpuHighSeconds: cpuHigh,
63
78
  diskHighSeconds: diskHigh,
@@ -65,6 +80,7 @@ export function watchdogStep(prev, input) {
65
80
  },
66
81
  send,
67
82
  reason: reason || (low ? 'this PC is still low' : ''),
83
+ severe,
68
84
  changed: low !== prev.low,
69
85
  };
70
86
  }
@@ -7,6 +7,12 @@ export interface TasklistRunners {
7
7
  export declare function useTasklistRunners(replacement?: TasklistRunners): void;
8
8
  /** While set, a synchronous read returns this copy (undefined: none yet, run tasklist). Null removes it. */
9
9
  export declare function useTasklistCache(read: (() => string | undefined) | null): void;
10
+ /**
11
+ * For a long-running server (the web UI): questions within `freshMs` of the last run share its list; an older one
12
+ * (up to `staleMs`) is still answered from it while a new list is fetched in the background, so no request waits for
13
+ * tasklist. Nothing runs while nobody asks. Returns the function that stops it.
14
+ */
15
+ export declare function keepTasklistFresh(freshMs?: number, staleMs?: number): () => void;
10
16
  /** The process list now: the cached copy when the menu keeps one, else a run of tasklist the caller waits for. */
11
17
  export declare function readTasklist(): string;
12
18
  /** The process list from a run of tasklist that does not block the program (the menu's background refresh). */
@@ -37,6 +37,43 @@ export function useTasklistRunners(replacement = REAL_RUNNERS) {
37
37
  export function useTasklistCache(read) {
38
38
  cache = read;
39
39
  }
40
+ /**
41
+ * For a long-running server (the web UI): questions within `freshMs` of the last run share its list; an older one
42
+ * (up to `staleMs`) is still answered from it while a new list is fetched in the background, so no request waits for
43
+ * tasklist. Nothing runs while nobody asks. Returns the function that stops it.
44
+ */
45
+ export function keepTasklistFresh(freshMs = 2000, staleMs = 15000) {
46
+ let copy;
47
+ let at = 0;
48
+ let fetching = false;
49
+ const refresh = () => {
50
+ if (fetching)
51
+ return;
52
+ fetching = true;
53
+ void runners
54
+ .async()
55
+ .then((list) => {
56
+ copy = list;
57
+ at = Date.now();
58
+ })
59
+ .finally(() => {
60
+ fetching = false;
61
+ });
62
+ };
63
+ useTasklistCache(() => {
64
+ const age = Date.now() - at;
65
+ if (copy !== undefined && age < freshMs)
66
+ return copy;
67
+ if (copy !== undefined && age < staleMs) {
68
+ refresh();
69
+ return copy;
70
+ }
71
+ copy = runners.sync();
72
+ at = Date.now();
73
+ return copy;
74
+ });
75
+ return () => useTasklistCache(null);
76
+ }
40
77
  /** The process list now: the cached copy when the menu keeps one, else a run of tasklist the caller waits for. */
41
78
  export function readTasklist() {
42
79
  return cache?.() ?? runners.sync();
@@ -4,7 +4,8 @@ import type { GuestRequest } from '../../core/types.js';
4
4
  export declare function writeSecrets(dataPath: string, id: string, secrets: SecretsFile): string | null;
5
5
  /**
6
6
  * Removes whatever one-shot files are left (the sandbox did not start, or did not get to read them) and, from older
7
- * versions, a login copy in a launch folder. Called before a launch writes new ones and when the sandbox stops.
7
+ * versions, a login copy in a launch folder. Called before a launch writes new ones, and when a launch fails before
8
+ * its sandbox starts.
8
9
  */
9
10
  export declare function clearSecrets(storageRoot: string, dataPath: string): number;
10
11
  /**
@@ -12,7 +12,8 @@ export function writeSecrets(dataPath, id, secrets) {
12
12
  !Object.keys(secrets.projectHarnessEnvironment ?? {}).length &&
13
13
  !secrets.claudeCredentials &&
14
14
  !secrets.vpn &&
15
- !secrets.wireguardConfig;
15
+ !secrets.wireguardConfig &&
16
+ !secrets.githubToken;
16
17
  if (empty)
17
18
  return null;
18
19
  const run = new RunDir(dataPath);
@@ -23,7 +24,8 @@ export function writeSecrets(dataPath, id, secrets) {
23
24
  }
24
25
  /**
25
26
  * Removes whatever one-shot files are left (the sandbox did not start, or did not get to read them) and, from older
26
- * versions, a login copy in a launch folder. Called before a launch writes new ones and when the sandbox stops.
27
+ * versions, a login copy in a launch folder. Called before a launch writes new ones, and when a launch fails before
28
+ * its sandbox starts.
27
29
  */
28
30
  export function clearSecrets(storageRoot, dataPath) {
29
31
  let removed = 0;
@@ -17,8 +17,16 @@ export function vpnLine(status, hostIp = null) {
17
17
  if (!status)
18
18
  return { level: 'warn', text: 'The sandbox has not reported on the VPN.' };
19
19
  const message = safeText(status.message).slice(0, 300);
20
+ const leaky = status.killSwitch && status.killSwitchHeld === false;
20
21
  if (status.state === 'up') {
21
22
  const same = hostIp !== null && status.publicIp === hostIp;
23
+ if (!same && leaky)
24
+ return {
25
+ level: 'warn',
26
+ text: 'VPN up' +
27
+ (status.publicIp ? ', the sandbox appears as ' + safeText(status.publicIp) : '') +
28
+ ', but the kill switch did not hold: if the tunnel drops, traffic goes out through your normal connection.',
29
+ };
22
30
  return same
23
31
  ? {
24
32
  level: 'bad',
@@ -40,9 +48,11 @@ export function vpnLine(status, hostIp = null) {
40
48
  level: 'bad',
41
49
  text: 'VPN failed: ' +
42
50
  message +
43
- (status.killSwitch
44
- ? ' The kill switch is on, so the sandbox has no internet.'
45
- : ' The kill switch is off, so the sandbox uses your normal connection.'),
51
+ (leaky
52
+ ? ' The kill switch did not hold, so the sandbox is ONLINE through your normal connection, without the VPN.'
53
+ : status.killSwitch
54
+ ? ' The kill switch is on, so the sandbox has no internet.'
55
+ : ' The kill switch is off, so the sandbox uses your normal connection.'),
46
56
  };
47
57
  return { level: 'warn', text: 'VPN ' + status.state + (message ? ': ' + message : '') };
48
58
  }
@@ -1,5 +1,5 @@
1
1
  import type { Endpoint } from '../../shared/pty/index.js';
2
- import type { NewSessionRequest, WebInfo, WebSession } from '../../shared/web/index.js';
2
+ import type { WebInfo, WebSession } from '../../shared/web/index.js';
3
3
  export interface WebBackend {
4
4
  info(): Promise<WebInfo>;
5
5
  /** Sessions as the browser may see them: never an attach token or address. */
@@ -9,6 +9,4 @@ export interface WebBackend {
9
9
  * running session, the sandbox is not shared with this user, or it has no address on this PC's sandbox network.
10
10
  */
11
11
  endpoint(sessionId: string): Endpoint | null;
12
- /** Starts an agent in a project of the running sandbox and waits until it runs. */
13
- create(request: NewSessionRequest): Promise<WebSession>;
14
12
  }
@@ -4,7 +4,8 @@ export { openBrowser, openIssuePage } from './browser.js';
4
4
  export { TerminalBridge } from './bridge.js';
5
5
  export { WebControl, type WebStatus } from './control.js';
6
6
  export { WebRunner } from './runner.js';
7
+ export { LiveHub, type LiveListener } from './live.js';
7
8
  export { RpcSession } from './rpc.js';
8
- export { WebServer, CONTENT_SECURITY_POLICY, RPC_PATH, type WebServerOptions } from './server.js';
9
+ export { WebServer, CONTENT_SECURITY_POLICY, IDLE_EXIT_MS, RPC_PATH, type WebServerOptions } from './server.js';
9
10
  export { StaticFiles } from './static-files.js';
10
11
  export { WebStore, WebStateSchema, type WebState } from './state.js';
@@ -3,7 +3,8 @@ export { openBrowser, openIssuePage } from './browser.js';
3
3
  export { TerminalBridge } from './bridge.js';
4
4
  export { WebControl } from './control.js';
5
5
  export { WebRunner } from './runner.js';
6
+ export { LiveHub } from './live.js';
6
7
  export { RpcSession } from './rpc.js';
7
- export { WebServer, CONTENT_SECURITY_POLICY, RPC_PATH } from './server.js';
8
+ export { WebServer, CONTENT_SECURITY_POLICY, IDLE_EXIT_MS, RPC_PATH } from './server.js';
8
9
  export { StaticFiles } from './static-files.js';
9
10
  export { WebStore, WebStateSchema } from './state.js';
@@ -0,0 +1,23 @@
1
+ import type { RpcHost } from '../../app/kernel/index.js';
2
+ export type LiveListener = (event: {
3
+ readonly output: unknown;
4
+ } | {
5
+ readonly error: unknown;
6
+ }) => void;
7
+ export declare class LiveHub {
8
+ private readonly host;
9
+ private readonly polls;
10
+ constructor(host: RpcHost);
11
+ /** How many polls run (for tests and the idle check). */
12
+ get size(): number;
13
+ /**
14
+ * Starts listening; the listener gets the current answer (at once when one is known) and every change. Returns the
15
+ * function that stops listening.
16
+ */
17
+ watch(query: string, input: unknown, listener: LiveListener): () => void;
18
+ /** Something changed (an operation ran): every query reads again now, at its own pace. */
19
+ poke(): void;
20
+ private schedule;
21
+ private isLive;
22
+ private read;
23
+ }