windows-development-sandbox 1.10.0 → 1.11.4

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 (383) 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 +194 -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 +959 -4
  32. package/dist/app/features/defs.js +151 -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 +297 -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 +220 -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 +224 -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 +35 -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/host-memory.d.ts +18 -0
  263. package/dist/host/life/host-memory.js +66 -0
  264. package/dist/host/life/watchdog.d.ts +9 -0
  265. package/dist/host/life/watchdog.js +17 -1
  266. package/dist/host/sandbox/tasklist.d.ts +6 -0
  267. package/dist/host/sandbox/tasklist.js +37 -0
  268. package/dist/host/secrets/launch-secrets.d.ts +2 -1
  269. package/dist/host/secrets/launch-secrets.js +4 -2
  270. package/dist/host/vpn/status.js +13 -3
  271. package/dist/host/web/backend.d.ts +1 -3
  272. package/dist/host/web/index.d.ts +2 -1
  273. package/dist/host/web/index.js +2 -1
  274. package/dist/host/web/live.d.ts +23 -0
  275. package/dist/host/web/live.js +107 -0
  276. package/dist/host/web/rpc.d.ts +6 -1
  277. package/dist/host/web/rpc.js +23 -31
  278. package/dist/host/web/runner.d.ts +4 -0
  279. package/dist/host/web/runner.js +12 -1
  280. package/dist/host/web/server.d.ts +30 -2
  281. package/dist/host/web/server.js +87 -29
  282. package/dist/host/web/state.d.ts +1 -0
  283. package/dist/host/web/state.js +2 -0
  284. package/dist/runtime/agent.js +30 -6
  285. package/dist/runtime/daemon.js +337 -177
  286. package/dist/runtime/db.js +30 -6
  287. package/dist/runtime/main.js +389 -77
  288. package/dist/runtime/netproxy.js +30 -6
  289. package/dist/runtime/view.js +495 -146
  290. package/dist/selftest/app.test.js +632 -12
  291. package/dist/selftest/channel.test.js +18 -1
  292. package/dist/selftest/cli.test.js +1 -1
  293. package/dist/selftest/config.test.js +1 -1
  294. package/dist/selftest/contained.test.js +1 -1
  295. package/dist/selftest/data.test.js +1 -2
  296. package/dist/selftest/engine.test.js +3 -1
  297. package/dist/selftest/github.test.d.ts +2 -0
  298. package/dist/selftest/github.test.js +243 -0
  299. package/dist/selftest/hcs.test.js +105 -8
  300. package/dist/selftest/health.test.js +71 -0
  301. package/dist/selftest/heavy-load.test.js +132 -72
  302. package/dist/selftest/index.js +4 -0
  303. package/dist/selftest/layout.test.js +8 -0
  304. package/dist/selftest/live-terminal.test.js +74 -0
  305. package/dist/selftest/menu-screens.js +171 -56
  306. package/dist/selftest/menu.test.js +54 -37
  307. package/dist/selftest/migrate.test.js +14 -12
  308. package/dist/selftest/powershell-variables.test.d.ts +2 -0
  309. package/dist/selftest/powershell-variables.test.js +61 -0
  310. package/dist/selftest/support/engine-golden.js +1 -0
  311. package/dist/selftest/support/fixtures.d.ts +4 -0
  312. package/dist/selftest/support/web-preview.d.ts +1 -0
  313. package/dist/selftest/support/web-preview.js +856 -0
  314. package/dist/selftest/update-service.test.js +15 -8
  315. package/dist/selftest/vpn.test.js +86 -2
  316. package/dist/selftest/web.test.js +58 -11
  317. package/dist/shared/channel/run-dir.d.ts +2 -0
  318. package/dist/shared/channel/run-dir.js +3 -0
  319. package/dist/shared/channel/schema.d.ts +7 -0
  320. package/dist/shared/channel/schema.js +9 -1
  321. package/dist/shared/channel/secrets.d.ts +1 -0
  322. package/dist/shared/channel/secrets.js +3 -0
  323. package/dist/shared/github/index.d.ts +1 -0
  324. package/dist/shared/github/index.js +2 -0
  325. package/dist/shared/github/token.d.ts +29 -0
  326. package/dist/shared/github/token.js +89 -0
  327. package/dist/shared/memory/policy.d.ts +33 -12
  328. package/dist/shared/memory/policy.js +50 -19
  329. package/dist/shared/pty/ctrl-c.d.ts +18 -0
  330. package/dist/shared/pty/ctrl-c.js +36 -0
  331. package/dist/shared/pty/index.d.ts +1 -0
  332. package/dist/shared/pty/index.js +1 -0
  333. package/dist/shared/pty/terminal.d.ts +2 -0
  334. package/dist/shared/pty/terminal.js +21 -2
  335. package/dist/shared/vpn/kill-switch.d.ts +13 -2
  336. package/dist/shared/vpn/kill-switch.js +88 -8
  337. package/dist/shared/web/protocol.d.ts +0 -4
  338. package/dist/sync/watcher.d.ts +2 -0
  339. package/dist/sync/watcher.js +8 -3
  340. package/dist/web/index.html +2 -2
  341. package/dist/web/{main-T5DMCHYV.css → main-MU5XWZMS.css} +1 -1
  342. package/dist/web/main-SLQNJRXD.js +219 -0
  343. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  344. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  345. package/native/csharp/HostTools/MemoryStatus.cs +58 -2
  346. package/native/csharp/HostTools/Program.cs +7 -1
  347. package/package.json +10 -3
  348. package/sandbox/csharp/MemGov.cs +240 -38
  349. package/sandbox/csharp/PtyHost.cs +104 -15
  350. package/sandbox/csharp/SandboxTools/CpuCap.cs +50 -1
  351. package/sandbox/csharp/SandboxTools/Program.cs +1 -1
  352. package/sandbox/scripts/repair-wmi-services.ps1 +26 -0
  353. package/schemas/config.schema.json +19 -5
  354. package/schemas/sandbox-config.schema.json +7 -3
  355. package/dist/cli/commands/accounts.d.ts +0 -2
  356. package/dist/cli/commands/accounts.js +0 -96
  357. package/dist/cli/commands/data/clean.d.ts +0 -3
  358. package/dist/cli/commands/data/clean.js +0 -60
  359. package/dist/cli/commands/data/index.d.ts +0 -4
  360. package/dist/cli/commands/data/index.js +0 -62
  361. package/dist/cli/commands/maintenance/clean.d.ts +0 -2
  362. package/dist/cli/commands/maintenance/clean.js +0 -24
  363. package/dist/cli/commands/maintenance/config-share.d.ts +0 -12
  364. package/dist/cli/commands/maintenance/config-share.js +0 -80
  365. package/dist/cli/commands/projects/projects.d.ts +0 -2
  366. package/dist/cli/commands/projects/projects.js +0 -162
  367. package/dist/cli/commands/storage/contained.d.ts +0 -3
  368. package/dist/cli/commands/storage/contained.js +0 -179
  369. package/dist/cli/commands/storage/storage.d.ts +0 -8
  370. package/dist/cli/commands/storage/storage.js +0 -179
  371. package/dist/cli/menu/add-agent.d.ts +0 -47
  372. package/dist/cli/menu/add-agent.js +0 -168
  373. package/dist/cli/menu/apps.d.ts +0 -69
  374. package/dist/cli/menu/apps.js +0 -214
  375. package/dist/cli/menu/data-folder.d.ts +0 -18
  376. package/dist/cli/menu/data-folder.js +0 -99
  377. package/dist/cli/menu/share-settings.d.ts +0 -21
  378. package/dist/cli/menu/share-settings.js +0 -112
  379. package/dist/cli/menu/vpn-login.d.ts +0 -16
  380. package/dist/cli/menu/vpn-login.js +0 -74
  381. package/dist/web/main-NRRS5O6Z.js +0 -157
  382. /package/dist/cli/{commands/data/collect.d.ts → services/data-collect.d.ts} +0 -0
  383. /package/dist/cli/{commands/data/remove.d.ts → services/data-remove.d.ts} +0 -0
@@ -129,6 +129,15 @@ const activate = async (version) => {
129
129
  fs.rmSync(NVM_SYMLINK, { recursive: true, force: true });
130
130
  fs.symlinkSync(path.join(NVM_HOME, 'v' + version), NVM_SYMLINK, 'junction');
131
131
  };
132
+ /**
133
+ * Playwright works in every sandbox without setup: the browsers `npx playwright install` downloads go in the tools
134
+ * folder, so they last between sandboxes, and its MCP server (and `channel: 'msedge'` in tests) uses Edge, the one
135
+ * browser Windows Sandbox always has (Chrome is not there).
136
+ */
137
+ const PLAYWRIGHT_VARS = () => ({
138
+ PLAYWRIGHT_BROWSERS_PATH: tool('ms-playwright'),
139
+ PLAYWRIGHT_MCP_BROWSER: 'msedge',
140
+ });
132
141
  export async function setupNode(env, versions, config) {
133
142
  if (dryRun) {
134
143
  log('(dry run) would provision nvm and Node ' + versions.node.spec + ' (from ' + versions.node.source + ')');
@@ -162,7 +171,7 @@ export async function setupNode(env, versions, config) {
162
171
  warn('Node ' + version + ' from this PC: ' + errMsg(e));
163
172
  }
164
173
  }
165
- Object.assign(env.vars, { NVM_HOME, NVM_SYMLINK });
174
+ Object.assign(env.vars, { NVM_HOME, NVM_SYMLINK, ...PLAYWRIGHT_VARS() });
166
175
  // At boot, nvm's symlink selects the version. A project added later must not switch it under the sessions already
167
176
  // running: its own version's folder goes first on its sessions' PATH instead.
168
177
  if (duringSession())
@@ -172,6 +181,7 @@ export async function setupNode(env, versions, config) {
172
181
  env.prepend.push(NVM_HOME, NVM_SYMLINK);
173
182
  }
174
183
  env.summary.push('node: ' + main + ' (from ' + versions.node.source + ', switch with nvm use)');
184
+ env.summary.push("playwright: ready (Edge; browsers it downloads are kept; in tests use channel: 'msedge')");
175
185
  await packageManagers(env, config, main);
176
186
  }
177
187
  /** Global npm packages (per Node version, like nvm keeps them) and pnpm/yarn through corepack. */
@@ -7,7 +7,7 @@ import { execOutput } from '../../runtime/process.js';
7
7
  import { ensureCompiled } from '../../runtime/csc.js';
8
8
  import { arch, tool } from '../tool-env.js';
9
9
  import { wanted } from '../detect.js';
10
- import { cargoJobs } from '../../../shared/memory/index.js';
10
+ import { cargoJobs, linkSlotsFor } from '../../../shared/memory/index.js';
11
11
  /**
12
12
  * Where cargo builds: shared with the PC's target\, one on the sandbox's disk, or a path. "separate" sets no variable:
13
13
  * the cargo shim (sandbox/csharp/CargoShim.cs) gives every checkout its own target\sandbox, so git worktrees of several
@@ -41,7 +41,7 @@ export function linkGate(env, rust, host) {
41
41
  const gate = path.join(mkdirp(tool('shims')), 'linkgate.exe');
42
42
  fs.copyFileSync(ensureCompiled('linkgate'), gate);
43
43
  const memoryGb = os.totalmem() / 1024 ** 3;
44
- const slots = rust.linkSlots === 'auto' ? Math.max(2, Math.min(4, Math.floor(memoryGb / 8))) : rust.linkSlots;
44
+ const slots = rust.linkSlots === 'auto' ? linkSlotsFor(memoryGb) : rust.linkSlots;
45
45
  env.vars.AGENT_LINK_SLOTS = String(slots);
46
46
  env.vars.AGENT_REAL_LINKER = gcc;
47
47
  env.vars.CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER = gate;
@@ -3,6 +3,14 @@ import { type CliSettings } from '../catalogue.js';
3
3
  import { type ToolEnv } from '../tool-env.js';
4
4
  /** gh keeps `gh auth login` here, under the agent state (persists like Claude's login; never copied from the PC). */
5
5
  export declare function ghConfigDir(): string;
6
+ /**
7
+ * gh's folder for the token the host handed over (github.shareToken): on the sandbox's own disk, so it is gone with the
8
+ * sandbox and never reaches the persistent state other sandboxes and projects share. The boot creates it only when a
9
+ * token arrived; while it exists, every gh setup (later projects too) points GH_CONFIG_DIR at it.
10
+ */
11
+ export declare function deliveredGhConfigDir(): string;
12
+ /** gh.exe where the catalogue installs it. */
13
+ export declare function ghExePath(): string;
6
14
  /**
7
15
  * Installs the tools toolchains.cli asks for. Settings still "auto" here (normally the host decides) use the same
8
16
  * rules on the workspace as the host would. The gh rule is enforced here again, whatever the host sent.
@@ -2,7 +2,7 @@
2
2
  // `openssl` shim onto the copy Git for Windows already ships.
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
- import { dryRun } from '../../runtime/paths.js';
5
+ import { dryRun, localDir } from '../../runtime/paths.js';
6
6
  import { exists, mkdirp } from '../../runtime/fs.js';
7
7
  import { log, warn } from '../../runtime/log.js';
8
8
  import { stateDir } from '../../runtime/session.js';
@@ -16,9 +16,39 @@ import { ensureCompiled } from '../../runtime/csc.js';
16
16
  export function ghConfigDir() {
17
17
  return path.join(stateDir, 'gh');
18
18
  }
19
+ /**
20
+ * gh's folder for the token the host handed over (github.shareToken): on the sandbox's own disk, so it is gone with the
21
+ * sandbox and never reaches the persistent state other sandboxes and projects share. The boot creates it only when a
22
+ * token arrived; while it exists, every gh setup (later projects too) points GH_CONFIG_DIR at it.
23
+ */
24
+ export function deliveredGhConfigDir() {
25
+ return path.join(localDir, 'gh-token');
26
+ }
27
+ /** gh.exe where the catalogue installs it. */
28
+ export function ghExePath() {
29
+ const entry = CLI_TOOLS.find((t) => t.name === 'gh');
30
+ const dir = tool('cli', 'gh');
31
+ return path.join(entry?.subdir ? path.join(dir, entry.subdir) : dir, entry?.exe ?? 'gh.exe');
32
+ }
33
+ /**
34
+ * gh keeps its token in Windows Credential Manager unless told otherwise, and that is wiped with the sandbox: only a
35
+ * login made with --insecure-storage (the token in GH_CONFIG_DIR, which persists) is still there next time.
36
+ */
37
+ const GH_NOTE = ' (log in once with: gh auth login --web --insecure-storage; it is kept between sandboxes)';
38
+ /** A saved gh account whose token was in Credential Manager (gone now): say how to log in so that it lasts. */
39
+ function warnKeyringLogin() {
40
+ try {
41
+ const hosts = fs.readFileSync(path.join(ghConfigDir(), 'hosts.yml'), 'utf8');
42
+ if (/^\s*user:/m.test(hosts) && !/oauth_token:/.test(hosts))
43
+ warn('gh: the GitHub login from an earlier sandbox was kept in Windows Credential Manager, which is wiped with each sandbox. Log in again with: gh auth login --web --insecure-storage (then it lasts).');
44
+ }
45
+ catch {
46
+ // No saved login: nothing to say.
47
+ }
48
+ }
19
49
  const toolVars = {
20
50
  gh: () => ({
21
- GH_CONFIG_DIR: mkdirp(ghConfigDir()),
51
+ GH_CONFIG_DIR: exists(deliveredGhConfigDir()) ? deliveredGhConfigDir() : mkdirp(ghConfigDir()),
22
52
  GH_NO_UPDATE_NOTIFIER: '1',
23
53
  GH_NO_EXTENSION_UPDATE_NOTIFIER: '1',
24
54
  }),
@@ -71,8 +101,11 @@ export async function setupCli(env, settings, gitAccess, refresh = false) {
71
101
  refresh,
72
102
  subdir: entry.subdir,
73
103
  size: entry.size,
104
+ ...(name === 'gh' ? { note: GH_NOTE } : {}),
74
105
  });
75
106
  Object.assign(env.vars, toolVars[name]?.() ?? {});
107
+ if (name === 'gh')
108
+ warnKeyringLogin();
76
109
  }
77
110
  catch (e) {
78
111
  // One tool failing never takes the others down.
@@ -5,5 +5,10 @@ export declare function openvpnDownload(arch?: string): {
5
5
  sha256: string;
6
6
  url: string;
7
7
  };
8
+ /**
9
+ * Windows Sandbox cannot answer WMI questions about services (a class whose provider is missing), and installers that
10
+ * ask (OpenVPN's MSI does) fail with 1603: the dead class is removed first (repair-wmi-services.ps1). Never throws.
11
+ */
12
+ export declare function repairServiceQueries(): void;
8
13
  /** Installs OpenVPN unless it is there; returns the path of openvpn.exe. Throws with the reason when it cannot. */
9
14
  export declare function ensureOpenVpn(): Promise<string>;
@@ -8,7 +8,8 @@ import { downloader, spawnAsync } from '../../shared/download/index.js';
8
8
  import { SYSTEM } from '../../shared/system.js';
9
9
  import { dryRun, isWin, toolsDir } from '../runtime/paths.js';
10
10
  import { exists, mkdirp } from '../runtime/fs.js';
11
- import { log } from '../runtime/log.js';
11
+ import { log, warn } from '../runtime/log.js';
12
+ import { scriptOutput } from '../runtime/process.js';
12
13
  import { OPENVPN_MSI } from '../../shared/vpn/download.js';
13
14
  /** Where the MSI puts the program. */
14
15
  export function openvpnExe(env = process.env) {
@@ -18,6 +19,17 @@ export function openvpnDownload(arch = process.arch) {
18
19
  const entry = arch === 'arm64' ? OPENVPN_MSI.arm64 : OPENVPN_MSI.x64;
19
20
  return { ...entry, url: OPENVPN_MSI.base + entry.file };
20
21
  }
22
+ /**
23
+ * Windows Sandbox cannot answer WMI questions about services (a class whose provider is missing), and installers that
24
+ * ask (OpenVPN's MSI does) fail with 1603: the dead class is removed first (repair-wmi-services.ps1). Never throws.
25
+ */
26
+ export function repairServiceQueries() {
27
+ const did = scriptOutput('repair-wmi-services.ps1');
28
+ if (did === 'removed')
29
+ log('VPN: repaired the Windows service queries installers use (a Windows Sandbox gap).');
30
+ else if (did !== 'absent' && did !== 'kept')
31
+ warn('VPN: could not check the Windows service queries installers use.');
32
+ }
21
33
  const hasHash = (file, sha256) => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex') === sha256;
22
34
  /** Installs OpenVPN unless it is there; returns the path of openvpn.exe. Throws with the reason when it cannot. */
23
35
  export async function ensureOpenVpn() {
@@ -33,6 +45,7 @@ export async function ensureOpenVpn() {
33
45
  log('VPN: downloading OpenVPN ' + OPENVPN_MSI.version + ' (about 6 MB)');
34
46
  await downloader.file(dl.url, msi, dl.sha256);
35
47
  }
48
+ repairServiceQueries();
36
49
  log('VPN: installing OpenVPN');
37
50
  const logFile = path.join(toolsDir, 'downloads', 'openvpn-install.log');
38
51
  const code = await spawnAsync(SYSTEM.msiexec(), ['/i', msi, '/qn', '/norestart', '/l*v', logFile], {
@@ -4,6 +4,7 @@
4
4
  // warns.
5
5
  import dns from 'node:dns';
6
6
  import fs from 'node:fs';
7
+ import net from 'node:net';
7
8
  import os from 'node:os';
8
9
  import path from 'node:path';
9
10
  import { now } from '../../shared/channel/run-dir.js';
@@ -13,7 +14,7 @@ import { assertSafeWireguard, renderWireguard, routesEverything, wireguardEndpoi
13
14
  import { isIPv4 } from '../../shared/network/firewall-rules.js';
14
15
  import { killSwitchCommands, tunnelCommands } from '../../shared/vpn/kill-switch.js';
15
16
  import { errMsg } from '../../core/util/errors.js';
16
- import { run as netsh } from '../network/firewall.js';
17
+ import { longPath, run as netsh } from '../network/firewall.js';
17
18
  import { dryRun, isWin, launchDir, run } from '../runtime/paths.js';
18
19
  import { log, warn } from '../runtime/log.js';
19
20
  import { session } from '../runtime/session.js';
@@ -74,6 +75,27 @@ async function publicAddress() {
74
75
  }
75
76
  }
76
77
  const hasAdapter = (ip) => Object.values(os.networkInterfaces()).some((list) => (list ?? []).some((i) => i.address === ip));
78
+ /** Well-known public addresses a direct connection is tried to, to see that the kill switch holds. */
79
+ const PROBE_TARGETS = ['1.1.1.1', '8.8.8.8', '9.9.9.9'];
80
+ const PROBE_TIMEOUT_MS = 3000;
81
+ function connects(ip, port) {
82
+ return new Promise((resolve) => {
83
+ const socket = net.connect({ host: ip, port });
84
+ const done = (ok) => {
85
+ socket.destroy();
86
+ resolve(ok);
87
+ };
88
+ socket.setTimeout(PROBE_TIMEOUT_MS, () => done(false));
89
+ socket.once('connect', () => done(true));
90
+ socket.once('error', () => done(false));
91
+ });
92
+ }
93
+ /** True when a direct connection out (before the tunnel exists) fails: the kill switch holds. */
94
+ async function directConnectionBlocked(servers) {
95
+ const targets = PROBE_TARGETS.filter((ip) => !servers.some((s) => s.ip === ip));
96
+ const results = await Promise.all(targets.map((ip) => connects(ip, 443)));
97
+ return !results.some(Boolean);
98
+ }
77
99
  /** The whole step. Returns true when the tunnel is up. */
78
100
  export async function setupVpn(secrets, ptyHostExe) {
79
101
  const vpn = session.vpn;
@@ -81,11 +103,24 @@ export async function setupVpn(secrets, ptyHostExe) {
81
103
  return true;
82
104
  const killSwitch = vpn.killSwitch;
83
105
  let blocked = false;
84
- const block = (servers) => {
85
- if (isWin && !dryRun)
86
- netsh(killSwitchCommands(servers, ptyHostExe));
87
- blocked = true;
88
- log('VPN: kill switch on: nothing leaves the sandbox except through the tunnel.');
106
+ /** A direct connection was tried after the kill switch and failed: only then is the sandbox said to be offline. */
107
+ let held = false;
108
+ const block = async (servers) => {
109
+ if (!isWin || dryRun) {
110
+ blocked = held = true;
111
+ return;
112
+ }
113
+ try {
114
+ netsh(killSwitchCommands(servers, ptyHostExe ? longPath(ptyHostExe) : null));
115
+ }
116
+ finally {
117
+ blocked = true;
118
+ held = await directConnectionBlocked(servers);
119
+ }
120
+ if (held)
121
+ log('VPN: kill switch on: nothing leaves the sandbox except through the tunnel (checked).');
122
+ else
123
+ warn('VPN: the kill switch did not hold: a direct connection still got out. If the tunnel drops, traffic leaves through your normal connection.');
89
124
  };
90
125
  setVpnStatus('starting', 'Starting the VPN');
91
126
  try {
@@ -103,7 +138,7 @@ export async function setupVpn(secrets, ptyHostExe) {
103
138
  if (killSwitch && !routesEverything(config))
104
139
  warn('VPN: AllowedIPs does not include 0.0.0.0/0: with the kill switch on, only the tunnel traffic can leave the sandbox.');
105
140
  if (killSwitch)
106
- block(servers);
141
+ await block(servers);
107
142
  if (dryRun || !isWin) {
108
143
  log('(dry run) would start the WireGuard tunnel ' + vpn.server);
109
144
  setVpnStatus('off', 'dry run');
@@ -129,7 +164,7 @@ export async function setupVpn(secrets, ptyHostExe) {
129
164
  if (servers.length === 0)
130
165
  throw new Error('the VPN server addresses could not be found');
131
166
  if (killSwitch)
132
- block(servers);
167
+ await block(servers);
133
168
  if (dryRun || !isWin) {
134
169
  log('(dry run) would start the VPN tunnel to ' + vpn.server);
135
170
  setVpnStatus('off', 'dry run');
@@ -149,7 +184,10 @@ export async function setupVpn(secrets, ptyHostExe) {
149
184
  throw new Error('the tunnel reported it was up, but no network adapter with its address appeared');
150
185
  const publicIp = await publicAddress();
151
186
  log('VPN: up' + (publicIp ? ' (the sandbox appears as ' + publicIp + ')' : '') + '.');
152
- setVpnStatus('up', 'The tunnel is up', publicIp ? { publicIp } : {});
187
+ setVpnStatus('up', 'The tunnel is up', {
188
+ ...(publicIp ? { publicIp } : {}),
189
+ ...(killSwitch ? { killSwitchHeld: held } : {}),
190
+ });
153
191
  return true;
154
192
  }
155
193
  catch (e) {
@@ -157,18 +195,19 @@ export async function setupVpn(secrets, ptyHostExe) {
157
195
  // Without the kill switch the sandbox keeps its normal connection: the rest of the boot goes on, with a warning.
158
196
  try {
159
197
  if (killSwitch && !blocked)
160
- block([]);
198
+ await block([]);
161
199
  }
162
200
  catch (blockError) {
163
201
  warn('VPN: could not close the network after the failure: ' + errMsg(blockError));
164
202
  }
165
- const text = 'VPN failed: ' +
166
- reason +
167
- (killSwitch
203
+ // "No internet" is said only when a direct connection was tried and failed.
204
+ const after = !killSwitch
205
+ ? ' The kill switch is off, so this sandbox is using your normal connection.'
206
+ : held
168
207
  ? ' The kill switch is on, so this sandbox has NO internet access.'
169
- : ' The kill switch is off, so this sandbox is using your normal connection.');
170
- warn(text);
171
- setVpnStatus('failed', reason);
208
+ : ' The kill switch could NOT close the network, so this sandbox is ONLINE through your normal connection, without the VPN.';
209
+ warn('VPN failed: ' + reason + after);
210
+ setVpnStatus('failed', reason, killSwitch ? { killSwitchHeld: held } : {});
172
211
  return false;
173
212
  }
174
213
  }
@@ -14,6 +14,7 @@ import { handshakeDone, serviceRunning } from '../../shared/vpn/wireguard.js';
14
14
  import { dryRun, isWin, toolsDir } from '../runtime/paths.js';
15
15
  import { exists, mkdirp } from '../runtime/fs.js';
16
16
  import { log } from '../runtime/log.js';
17
+ import { repairServiceQueries } from './openvpn.js';
17
18
  /** The tunnel's name: the config file is sandbox.conf and the Windows service WireGuardTunnel$sandbox. */
18
19
  export const WIREGUARD_TUNNEL = 'sandbox';
19
20
  export function wireguardDir(env = process.env) {
@@ -41,6 +42,7 @@ export async function ensureWireguard() {
41
42
  log('VPN: downloading WireGuard ' + WIREGUARD_MSI.version + ' (about 3 MB)');
42
43
  await downloader.file(dl.url, msi, dl.sha256);
43
44
  }
45
+ repairServiceQueries();
44
46
  log('VPN: installing WireGuard');
45
47
  const logFile = path.join(toolsDir, 'downloads', 'wireguard-install.log');
46
48
  // DO_NOT_LAUNCH: the installer would otherwise start the manager window.
@@ -18,4 +18,5 @@ export declare function findUnwatchedSession(dataPath: string, harness: string,
18
18
  /** Bridges this terminal to a session. Returns the agent's exit code, or 0 when the user detaches. */
19
19
  export declare function attachTerminal(session: SessionStatus, o?: {
20
20
  clipboardWrites?: boolean;
21
+ confirmCtrlC?: boolean;
21
22
  }): Promise<number>;
@@ -74,6 +74,7 @@ export async function attachTerminal(session, o = {}) {
74
74
  const result = await runTerminalClient(socket, session.attach.token, {
75
75
  detachKey: DETACH_KEY,
76
76
  clipboardWrites: o.clipboardWrites,
77
+ confirmCtrlC: o.confirmCtrlC,
77
78
  });
78
79
  switch (result.reason) {
79
80
  case 'detach':
@@ -47,7 +47,7 @@ export declare class SandboxChannel {
47
47
  /** Restarts running sessions in place (they resume via the harness's resumeArguments, e.g. claude --continue). */
48
48
  restart(harness: string | null, cwd?: string): Promise<GuestResponse>;
49
49
  /** The PC is low on memory or CPU: the sandbox slows its builds down for `seconds`. Never waits for an answer. */
50
- pressure(reason: string, seconds: number): void;
50
+ pressure(reason: string, seconds: number, level?: 'slow' | 'severe'): void;
51
51
  ping(): Promise<GuestResponse>;
52
52
  runningSessions(): SessionStatus[];
53
53
  /** Running sessions of the sandbox that is running now (its launch id), newest last, capped. */
@@ -180,8 +180,8 @@ export class SandboxChannel {
180
180
  return this.waitForResponse(requestId, 30000);
181
181
  }
182
182
  /** The PC is low on memory or CPU: the sandbox slows its builds down for `seconds`. Never waits for an answer. */
183
- pressure(reason, seconds) {
184
- this.send({ kind: 'pressure', id: newId(), reason, seconds, createdAt: now() });
183
+ pressure(reason, seconds, level = 'slow') {
184
+ this.send({ kind: 'pressure', id: newId(), reason, seconds, level, createdAt: now() });
185
185
  }
186
186
  async ping() {
187
187
  const requestId = newId();
@@ -19,6 +19,7 @@ export interface HcsProbes {
19
19
  imageStores(): readonly ImageStore[];
20
20
  }
21
21
  export declare function checkHcs(p: HcsProbes): EngineCheck;
22
+ export declare function system32Dir(): string;
22
23
  /** The groups of a token from `whoami /groups /fo csv /nh`: name, type, SID, attributes per line. */
23
24
  export declare function parseWhoamiGroups(csv: string): TokenGroups;
24
25
  /** The real probes: sc.exe query, whoami, file checks. They only read. */
@@ -45,7 +45,7 @@ export function checkHcs(p) {
45
45
  need('No Windows Server Core (ltsc2022) image is on this PC. ' + image.reason, 'Pull one with Docker Desktop (Windows containers mode): docker pull mcr.microsoft.com/windows/servercore:ltsc2022 - or put your own in a folder with an image.json and set AGENT_SANDBOX_HCS_IMAGE to it. This tool does not download images in this alpha.');
46
46
  return { available: reasons.length === 0, reasons, fixes };
47
47
  }
48
- function system32Dir() {
48
+ export function system32Dir() {
49
49
  return path.dirname(system32('x'));
50
50
  }
51
51
  /** The groups of a token from `whoami /groups /fo csv /nh`: name, type, SID, attributes per line. */
@@ -32,7 +32,7 @@ export declare class HcsEngine implements SandboxEngine {
32
32
  private helper;
33
33
  start(plan: EnginePlan): Promise<RunningSandbox>;
34
34
  private tryTerminate;
35
- /** Ours: the compute systems the helper lists with our owner name. */
35
+ /** Ours: the compute systems the helper lists with our owner name and this Windows user's id prefix. */
36
36
  private ours;
37
37
  running(): Promise<boolean>;
38
38
  stop(): Promise<void>;
@@ -9,7 +9,7 @@ import { fail, UserError } from '../../../core/util/index.js';
9
9
  import { selfTestRunning } from '../../test-guard.js';
10
10
  import { checkHcs, realHcsProbes } from './check.js';
11
11
  import { buildDocuments, HCS_OWNER } from './documents.js';
12
- import { HcsHelper, hcsHostExe } from './helper.js';
12
+ import { HcsHelper, hcsHostExe, isMySystemId, userSystemPrefix } from './helper.js';
13
13
  import { findImage } from './images.js';
14
14
  export const HCS_CAPABILITIES = {
15
15
  desktop: false,
@@ -76,7 +76,7 @@ export class HcsEngine {
76
76
  const docs = buildDocuments({
77
77
  launch,
78
78
  image: this.image,
79
- id: 'agent-sandbox-' + slug,
79
+ id: userSystemPrefix() + slug,
80
80
  scratchVhdx,
81
81
  hostCpus: (this.deps.hostCpus ?? (() => os.cpus().length))(),
82
82
  });
@@ -147,10 +147,12 @@ export class HcsEngine {
147
147
  throw e; // a system that is already gone is fine
148
148
  }
149
149
  }
150
- /** Ours: the compute systems the helper lists with our owner name. */
150
+ /** Ours: the compute systems the helper lists with our owner name and this Windows user's id prefix. */
151
151
  ours(helper) {
152
152
  const parsed = SystemListSchema.safeParse(JSON.parse(helper.enumerate().json || '[]'));
153
- return parsed.success ? parsed.data.filter((s) => s.Owner === HCS_OWNER).map((s) => s.Id) : [];
153
+ return parsed.success
154
+ ? parsed.data.filter((s) => s.Owner === HCS_OWNER && isMySystemId(s.Id)).map((s) => s.Id)
155
+ : [];
154
156
  }
155
157
  running() {
156
158
  try {
@@ -1,4 +1,13 @@
1
1
  import { z } from 'zod';
2
+ /** Whether an id has the shape of the ones this tool gives its compute systems. */
3
+ export declare function isOurSystemId(id: string): boolean;
4
+ /**
5
+ * The start of the ids this Windows user's sandboxes get: the Owner is the same for everyone, so on a PC shared with
6
+ * other users this tag is what keeps one user from listing or stopping another's compute systems.
7
+ */
8
+ export declare function userSystemPrefix(user?: string, domain?: string): string;
9
+ /** One of this tool's ids, given by this Windows user. */
10
+ export declare function isMySystemId(id: string): boolean;
2
11
  export declare const HcsReplySchema: z.ZodObject<{
3
12
  ok: z.ZodBoolean;
4
13
  hresult: z.ZodString;
@@ -16,6 +25,8 @@ export type HelperRunner = (exe: string, args: readonly string[], timeoutMs: num
16
25
  export declare const spawnRunner: HelperRunner;
17
26
  /** Compiles the helper's sources to `exe` with the .NET Framework csc (C# 5). False when it cannot be built. */
18
27
  export declare function buildHcsHostTo(exe: string): boolean;
28
+ /** HcsHost.exe when it was built already (never builds it): a PC that never used HCS has none. */
29
+ export declare function builtHcsHostExe(storageRoot: string): string | null;
19
30
  /**
20
31
  * <storage>\bin\hcs\<hash of its sources>\HcsHost.exe, built the first time. Its own folder: HostTools' cleanup of old
21
32
  * builds only touches twelve-digit hash folders directly in bin. Null when it cannot be built.
@@ -15,6 +15,26 @@ const MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
15
15
  const CALL_TIMEOUT_MS = 240000;
16
16
  /** Only ids this tool gives its compute systems (HcsHost.exe refuses any other, and checks the Owner too). */
17
17
  const SAFE_ID = /^agent-sandbox-[A-Za-z0-9._-]{1,100}$/;
18
+ /** Whether an id has the shape of the ones this tool gives its compute systems. */
19
+ export function isOurSystemId(id) {
20
+ return SAFE_ID.test(id);
21
+ }
22
+ /**
23
+ * The start of the ids this Windows user's sandboxes get: the Owner is the same for everyone, so on a PC shared with
24
+ * other users this tag is what keeps one user from listing or stopping another's compute systems.
25
+ */
26
+ export function userSystemPrefix(user = os.userInfo().username, domain = process.env.USERDOMAIN ?? '') {
27
+ const tag = crypto
28
+ .createHash('sha256')
29
+ .update((domain + '\\' + user).toLowerCase())
30
+ .digest('hex')
31
+ .slice(0, 8);
32
+ return 'agent-sandbox-' + tag + '-';
33
+ }
34
+ /** One of this tool's ids, given by this Windows user. */
35
+ export function isMySystemId(id) {
36
+ return isOurSystemId(id) && id.startsWith(userSystemPrefix());
37
+ }
18
38
  export const HcsReplySchema = z.object({
19
39
  ok: z.boolean(),
20
40
  hresult: z.string().max(16),
@@ -51,12 +71,22 @@ export function buildHcsHostTo(exe) {
51
71
  const r = spawnSync(SYSTEM.csc(), ['/nologo', '/optimize', '/target:exe', '/r:System.Web.Extensions.dll', '/out:' + exe, ...sources], { windowsHide: true, timeout: 120000 });
52
72
  return r.status === 0 && fs.existsSync(exe);
53
73
  }
74
+ /** HcsHost.exe when it was built already (never builds it): a PC that never used HCS has none. */
75
+ export function builtHcsHostExe(storageRoot) {
76
+ const exe = hcsHostPath(storageRoot);
77
+ return exe && fs.existsSync(exe) ? exe : null;
78
+ }
54
79
  /**
55
80
  * <storage>\bin\hcs\<hash of its sources>\HcsHost.exe, built the first time. Its own folder: HostTools' cleanup of old
56
81
  * builds only touches twelve-digit hash folders directly in bin. Null when it cannot be built.
57
82
  */
58
83
  export function hcsHostExe(storageRoot) {
59
84
  refuseInSelfTest('build HcsHost.exe');
85
+ const exe = hcsHostPath(storageRoot);
86
+ return exe && (fs.existsSync(exe) || buildHcsHostTo(exe)) ? exe : null;
87
+ }
88
+ /** Where HcsHost.exe is (or would be built): named by the hash of its sources. Null without the sources. */
89
+ function hcsHostPath(storageRoot) {
60
90
  const dir = nativeFile('csharp', 'HcsHost');
61
91
  const hash = crypto.createHash('sha256');
62
92
  try {
@@ -69,8 +99,7 @@ export function hcsHostExe(storageRoot) {
69
99
  catch {
70
100
  return null; // no sources
71
101
  }
72
- const exe = path.join(storageRoot, 'bin', 'hcs', hash.digest('hex').slice(0, 12), 'HcsHost.exe');
73
- return fs.existsSync(exe) || buildHcsHostTo(exe) ? exe : null;
102
+ return path.join(storageRoot, 'bin', 'hcs', hash.digest('hex').slice(0, 12), 'HcsHost.exe');
74
103
  }
75
104
  /** A path argument: absolute, not an option. Nothing else reaches the helper's command line. */
76
105
  function pathArg(value, what) {
@@ -0,0 +1,42 @@
1
+ import { type HcsProbes } from './check.js';
2
+ import { HcsHelper } from './helper.js';
3
+ export interface LeftoverSystem {
4
+ readonly id: string;
5
+ /** Container or VirtualMachine (the utility VM). */
6
+ readonly kind: string;
7
+ readonly state: string;
8
+ /** When it was created (ISO text), when HCS says. */
9
+ readonly since: string | null;
10
+ }
11
+ export type LeftoverLook = {
12
+ readonly available: true;
13
+ readonly systems: readonly LeftoverSystem[];
14
+ } | {
15
+ readonly available: false;
16
+ readonly reason: string;
17
+ };
18
+ export interface LeftoverOutcome {
19
+ readonly id: string;
20
+ readonly stopped: boolean;
21
+ /** Why not, in plain words. */
22
+ readonly problem?: string;
23
+ }
24
+ /** What the look runs on: replaceable by tests (probes of a pretend PC, a fake helper). */
25
+ export interface LeftoverDeps {
26
+ readonly probes: HcsProbes;
27
+ /** The helper, or null when HcsHost.exe was never built (then nothing of ours can be running). Never builds it. */
28
+ helper(storageRoot: string): HcsHelper | null;
29
+ }
30
+ export declare const LEFTOVER_DEPS: {
31
+ current: LeftoverDeps | null;
32
+ };
33
+ /**
34
+ * The compute systems of ours that exist now. Never builds HcsHost.exe: a PC that never used HCS has none, and
35
+ * nothing of ours can be running there.
36
+ */
37
+ export declare function lookForLeftovers(storageRoot: string): LeftoverLook;
38
+ /**
39
+ * Stops the named systems, containers before the utility VM that hosts them. Each id is looked up again first: one that
40
+ * is not one of ours (or is gone) never reaches the helper; the helper checks the Owner with HCS itself once more.
41
+ */
42
+ export declare function stopLeftovers(storageRoot: string, ids: readonly string[]): LeftoverOutcome[];