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
@@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
8
8
  [assembly: AssemblyCompany("windows-development-sandbox (open source)")]
9
9
  [assembly: AssemblyProduct("Windows Development Sandbox")]
10
10
  [assembly: AssemblyCopyright("MIT License, windows-development-sandbox contributors")]
11
- [assembly: AssemblyVersion("1.10.0.0")]
12
- [assembly: AssemblyFileVersion("1.10.0.0")]
13
- [assembly: AssemblyInformationalVersion("1.10.0")]
11
+ [assembly: AssemblyVersion("1.11.3.0")]
12
+ [assembly: AssemblyFileVersion("1.11.3.0")]
13
+ [assembly: AssemblyInformationalVersion("1.11.3")]
14
14
  [assembly: ComVisible(false)]
@@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
8
8
  [assembly: AssemblyCompany("windows-development-sandbox (open source)")]
9
9
  [assembly: AssemblyProduct("Windows Development Sandbox")]
10
10
  [assembly: AssemblyCopyright("MIT License, windows-development-sandbox contributors")]
11
- [assembly: AssemblyVersion("1.10.0.0")]
12
- [assembly: AssemblyFileVersion("1.10.0.0")]
13
- [assembly: AssemblyInformationalVersion("1.10.0")]
11
+ [assembly: AssemblyVersion("1.11.3.0")]
12
+ [assembly: AssemblyFileVersion("1.11.3.0")]
13
+ [assembly: AssemblyInformationalVersion("1.11.3")]
14
14
  [assembly: ComVisible(false)]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windows-development-sandbox",
3
- "version": "1.10.0",
3
+ "version": "1.11.3",
4
4
  "description": "A disposable Windows Sandbox for development, with or without an AI agent (Claude Code, Codex, Gemini, opencode, ...): the folder you run it from is the workspace, toolchains and databases for your project install themselves, and nothing else on your PC is reachable.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -42,7 +42,7 @@
42
42
  "LICENSE"
43
43
  ],
44
44
  "scripts": {
45
- "build": "tsc -p tsconfig.json && node scripts/bundle.mjs && node scripts/bundle-web.mjs && node scripts/schemas.mjs",
45
+ "build": "tsc -p tsconfig.json && tsc -p web/tsconfig.json --noEmit && node scripts/bundle.mjs && node scripts/bundle-web.mjs && node scripts/schemas.mjs",
46
46
  "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
47
47
  "rebuild": "npm run clean && npm run build",
48
48
  "test": "npm run build && node dist/bin/agent-sandbox.js self-test",
@@ -55,7 +55,9 @@
55
55
  "doctor": "node dist/bin/agent-sandbox.js doctor",
56
56
  "lint": "eslint src scripts web",
57
57
  "format": "prettier --write src scripts web",
58
- "format:check": "prettier --check src scripts web"
58
+ "format:check": "prettier --check src scripts web",
59
+ "web:preview": "node dist/selftest/support/web-preview.js",
60
+ "web:shots": "node scripts/web-shots.mjs"
59
61
  },
60
62
  "publishConfig": {
61
63
  "access": "public"
@@ -98,6 +100,7 @@
98
100
  "@mantine/hooks": "^7.17.8",
99
101
  "@mantine/modals": "^7.17.8",
100
102
  "@mantine/notifications": "^7.17.8",
103
+ "@mantine/spotlight": "7.17.8",
101
104
  "@tabler/icons-react": "^3.49.0",
102
105
  "@tanstack/react-query": "^5.104.3",
103
106
  "@types/lodash": "^4.17.26",
@@ -106,10 +109,14 @@
106
109
  "@types/react-dom": "^18.3.7",
107
110
  "@types/ws": "^8.18.2",
108
111
  "@xterm/addon-fit": "^0.11.0",
112
+ "@xterm/addon-search": "0.16.0",
113
+ "@xterm/addon-unicode11": "0.9.0",
114
+ "@xterm/addon-web-links": "0.12.0",
109
115
  "@xterm/xterm": "^6.0.0",
110
116
  "esbuild": "^0.28.2",
111
117
  "eslint": "^9.39.5",
112
118
  "mantine-form-zod-resolver": "^1.3.0",
119
+ "playwright-core": "1.63.0",
113
120
  "prettier": "^3.9.10",
114
121
  "react": "^18.3.1",
115
122
  "react-dom": "^18.3.1",
@@ -35,6 +35,24 @@ static class MemGov
35
35
  private const uint IdleClass = 0x00000040;
36
36
  private const uint BelowNormalClass = 0x00004000;
37
37
  private const uint NormalClass = 0x00000020;
38
+ // The throttle job: compilers join it from the governor's step 2; its CPU cap follows the dial.
39
+ private const string ThrottleJobName = "Local\\AgentSandboxBuildThrottle";
40
+ private const uint ProcessTerminate = 0x0001;
41
+ private const uint ProcessSetQuota = 0x0100;
42
+ private const int JobObjectCpuRateControlInformation = 15;
43
+ private const uint CpuRateEnable = 0x1;
44
+ private const uint CpuRateHardCap = 0x4;
45
+ private const int ProcessMemoryPriorityClass = 0;
46
+ private const uint MemoryPriorityLow = 2;
47
+ private const uint MemoryPriorityNormal = 5;
48
+
49
+ // JOBOBJECT_CPU_RATE_CONTROL_INFORMATION (CpuRate in hundredths of a percent).
50
+ [StructLayout(LayoutKind.Sequential)]
51
+ private struct CpuRateControl
52
+ {
53
+ public uint ControlFlags;
54
+ public uint CpuRate;
55
+ }
38
56
 
39
57
  // MEMORYSTATUSEX.
40
58
  [StructLayout(LayoutKind.Sequential)]
@@ -51,6 +69,23 @@ static class MemGov
51
69
  public ulong AvailExtendedVirtual;
52
70
  }
53
71
 
72
+ // PROCESS_MEMORY_COUNTERS_EX (PrivateUsage is the private memory Process.PrivateMemorySize64 reported).
73
+ [StructLayout(LayoutKind.Sequential)]
74
+ private struct MemoryCounters
75
+ {
76
+ public uint Size;
77
+ public uint PageFaultCount;
78
+ public UIntPtr PeakWorkingSetSize;
79
+ public UIntPtr WorkingSetSize;
80
+ public UIntPtr QuotaPeakPagedPoolUsage;
81
+ public UIntPtr QuotaPagedPoolUsage;
82
+ public UIntPtr QuotaPeakNonPagedPoolUsage;
83
+ public UIntPtr QuotaNonPagedPoolUsage;
84
+ public UIntPtr PagefileUsage;
85
+ public UIntPtr PeakPagefileUsage;
86
+ public UIntPtr PrivateUsage;
87
+ }
88
+
54
89
  private static class NativeMethods
55
90
  {
56
91
  [DllImport("kernel32.dll", SetLastError = true)]
@@ -77,6 +112,27 @@ static class MemGov
77
112
  [DllImport("kernel32.dll", SetLastError = true)]
78
113
  public static extern bool CloseHandle(IntPtr handle);
79
114
 
115
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
116
+ public static extern IntPtr CreateJobObjectW(IntPtr attributes, string name);
117
+
118
+ [DllImport("kernel32.dll", SetLastError = true)]
119
+ public static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
120
+
121
+ [DllImport("kernel32.dll", SetLastError = true)]
122
+ public static extern bool SetInformationJobObject(IntPtr job, int infoClass, ref CpuRateControl info, int length);
123
+
124
+ [DllImport("kernel32.dll", SetLastError = true)]
125
+ public static extern bool SetProcessInformation(IntPtr process, int infoClass, ref uint info, int length);
126
+
127
+ [DllImport("kernel32.dll", SetLastError = true)]
128
+ public static extern bool SetProcessWorkingSetSize(IntPtr process, IntPtr minimum, IntPtr maximum);
129
+
130
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
131
+ public static extern bool QueryFullProcessImageNameW(IntPtr process, uint flags, StringBuilder name, ref uint size);
132
+
133
+ [DllImport("kernel32.dll", EntryPoint = "K32GetProcessMemoryInfo", SetLastError = true)]
134
+ public static extern bool GetProcessMemoryInfo(IntPtr process, ref MemoryCounters counters, uint size);
135
+
80
136
  [DllImport("ntdll.dll")]
81
137
  public static extern int NtSuspendProcess(IntPtr process);
82
138
 
@@ -90,6 +146,7 @@ static class MemGov
90
146
  /// <summary>The paused process ids on disk, so a memgov that is killed mid-pause is undone by the next one.</summary>
91
147
  private static readonly string HeldFile = Path.Combine(Path.GetTempPath(), "agent-sandbox-memgov-held.txt");
92
148
  private static IntPtr job = IntPtr.Zero;
149
+ private static IntPtr throttleJob = IntPtr.Zero;
93
150
  private static volatile bool stop;
94
151
 
95
152
  static int Main(string[] args)
@@ -113,6 +170,8 @@ static class MemGov
113
170
  }
114
171
  // A memgov that ended without resuming (killed, crashed) left its pauses in the file: undo them first.
115
172
  RecoverHeld();
173
+ // A memgov that ended while compilers were capped left the cap: lift it before anything else.
174
+ SetCpuRate(100);
116
175
  Thread commands = new Thread(ReadCommands);
117
176
  commands.IsBackground = true;
118
177
  commands.Start();
@@ -136,15 +195,28 @@ static class MemGov
136
195
  }
137
196
  }
138
197
  ResumeAll();
198
+ SetCpuRate(100);
139
199
  return 0;
140
200
  }
141
201
 
202
+ /// <summary>The self-test's own job (MEMGOV_JOB_NAME, a name in the agents' job family), or null.</summary>
203
+ private static string TestJobName()
204
+ {
205
+ string name = Environment.GetEnvironmentVariable("MEMGOV_JOB_NAME");
206
+ return !string.IsNullOrEmpty(name) && name.StartsWith(AgentJobName + "Test", StringComparison.Ordinal) ? name : null;
207
+ }
208
+
209
+ private static string JobName()
210
+ {
211
+ return TestJobName() ?? AgentJobName;
212
+ }
213
+
142
214
  /// <summary>The job, opened once it exists (ptyhost makes it when the first agent starts).</summary>
143
215
  private static IntPtr Job()
144
216
  {
145
217
  if (job == IntPtr.Zero)
146
218
  {
147
- job = NativeMethods.OpenJobObjectW(JobObjectQuery, false, AgentJobName);
219
+ job = NativeMethods.OpenJobObjectW(JobObjectQuery, false, JobName());
148
220
  }
149
221
  return job;
150
222
  }
@@ -164,36 +236,70 @@ static class MemGov
164
236
  sb.Append(",\"commitAvailMb\":").Append(Mb(status.AvailPageFile));
165
237
  sb.Append(",\"procs\":[");
166
238
  bool first = true;
167
- int self = Process.GetCurrentProcess().Id;
239
+ int self = SelfId;
168
240
  foreach (int pid in JobProcessIds())
169
241
  {
170
242
  if (pid == self)
171
243
  {
172
244
  continue;
173
245
  }
174
- try
246
+ // Read everything first: a process that ends half way must not leave half an entry behind.
247
+ string entry = Entry(pid);
248
+ if (entry == null)
175
249
  {
176
- using (Process p = Process.GetProcessById(pid))
177
- {
178
- if (!first)
179
- {
180
- sb.Append(',');
181
- }
182
- first = false;
183
- sb.Append("{\"pid\":").Append(pid.ToString(CultureInfo.InvariantCulture));
184
- sb.Append(",\"name\":").Append(Json(p.ProcessName));
185
- sb.Append(",\"mb\":").Append(Mb((ulong)p.PrivateMemorySize64));
186
- sb.Append(",\"prio\":\"").Append(PriorityName(pid)).Append("\"}");
187
- }
250
+ continue;
188
251
  }
189
- catch (Exception)
252
+ if (!first)
190
253
  {
191
- // The process ended while it was read.
254
+ sb.Append(',');
192
255
  }
256
+ first = false;
257
+ sb.Append(entry);
193
258
  }
194
259
  return sb.Append("]}").ToString();
195
260
  }
196
261
 
262
+ private static readonly int SelfId = Process.GetCurrentProcess().Id;
263
+
264
+ /// <summary>
265
+ /// One process's line, read through one handle: its name, private memory and priority (null when it ended). Not
266
+ /// System.Diagnostics.Process, which reads the whole system's process list for every process it opens: with a
267
+ /// build's dozens of processes every two seconds that alone kept a CPU busy.
268
+ /// </summary>
269
+ private static string Entry(int pid)
270
+ {
271
+ IntPtr process = NativeMethods.OpenProcess(ProcessQueryLimited, false, pid);
272
+ if (process == IntPtr.Zero)
273
+ {
274
+ return null;
275
+ }
276
+ try
277
+ {
278
+ StringBuilder path = new StringBuilder(1024);
279
+ uint size = (uint)path.Capacity;
280
+ if (!NativeMethods.QueryFullProcessImageNameW(process, 0, path, ref size))
281
+ {
282
+ return null;
283
+ }
284
+ MemoryCounters counters = new MemoryCounters();
285
+ counters.Size = (uint)Marshal.SizeOf(typeof(MemoryCounters));
286
+ if (!NativeMethods.GetProcessMemoryInfo(process, ref counters, counters.Size))
287
+ {
288
+ return null;
289
+ }
290
+ uint c = NativeMethods.GetPriorityClass(process);
291
+ string priority = c == IdleClass ? "idle" : c == BelowNormalClass ? "below" : "normal";
292
+ return "{\"pid\":" + pid.ToString(CultureInfo.InvariantCulture)
293
+ + ",\"name\":" + Json(Path.GetFileNameWithoutExtension(path.ToString(0, (int)size)))
294
+ + ",\"mb\":" + Mb(counters.PrivateUsage.ToUInt64())
295
+ + ",\"prio\":\"" + priority + "\"}";
296
+ }
297
+ finally
298
+ {
299
+ NativeMethods.CloseHandle(process);
300
+ }
301
+ }
302
+
197
303
  private static List<int> JobProcessIds()
198
304
  {
199
305
  List<int> ids = new List<int>();
@@ -224,28 +330,10 @@ static class MemGov
224
330
  return ids;
225
331
  }
226
332
 
227
- private static string PriorityName(int pid)
228
- {
229
- IntPtr process = NativeMethods.OpenProcess(ProcessQueryLimited, false, pid);
230
- if (process == IntPtr.Zero)
231
- {
232
- return "normal";
233
- }
234
- try
235
- {
236
- uint c = NativeMethods.GetPriorityClass(process);
237
- return c == IdleClass ? "idle" : c == BelowNormalClass ? "below" : "normal";
238
- }
239
- finally
240
- {
241
- NativeMethods.CloseHandle(process);
242
- }
243
- }
244
-
245
333
  /// <summary>Whether pid is a process of the agents' job and not this one: the only ones this program touches.</summary>
246
334
  private static bool Allowed(int pid)
247
335
  {
248
- return pid != Process.GetCurrentProcess().Id && JobProcessIds().Contains(pid);
336
+ return pid != SelfId && JobProcessIds().Contains(pid);
249
337
  }
250
338
 
251
339
  private static void ReadCommands()
@@ -277,12 +365,39 @@ static class MemGov
277
365
  stop = true;
278
366
  return;
279
367
  }
368
+ if (parts.Length == 2 && parts[0] == "cpurate")
369
+ {
370
+ int percent;
371
+ if (int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out percent))
372
+ {
373
+ SetCpuRate(percent);
374
+ }
375
+ return;
376
+ }
280
377
  int pid;
281
- if (parts.Length < 3 || !int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out pid) || !Allowed(pid))
378
+ if (parts.Length < 2 || !int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out pid) || !Allowed(pid))
379
+ {
380
+ return;
381
+ }
382
+ if (parts[0] == "throttle")
383
+ {
384
+ Throttle(pid);
385
+ return;
386
+ }
387
+ if (parts[0] == "trim")
388
+ {
389
+ Trim(pid);
390
+ return;
391
+ }
392
+ if (parts.Length < 3)
282
393
  {
283
394
  return;
284
395
  }
285
- if (parts[0] == "priority")
396
+ if (parts[0] == "memprio")
397
+ {
398
+ SetMemoryPriority(pid, parts[2] == "low" ? MemoryPriorityLow : MemoryPriorityNormal);
399
+ }
400
+ else if (parts[0] == "priority")
286
401
  {
287
402
  uint cls = parts[2] == "idle" ? IdleClass : parts[2] == "below" ? BelowNormalClass : NormalClass;
288
403
  IntPtr process = NativeMethods.OpenProcess(ProcessSetInformation, false, pid);
@@ -303,6 +418,93 @@ static class MemGov
303
418
  }
304
419
  }
305
420
 
421
+ /// <summary>The throttle job, made once. A process already in the agents' job is nested in it (Windows 8+).</summary>
422
+ private static IntPtr ThrottleJob()
423
+ {
424
+ if (throttleJob == IntPtr.Zero)
425
+ {
426
+ string test = TestJobName();
427
+ throttleJob = NativeMethods.CreateJobObjectW(IntPtr.Zero, test != null ? test + "Throttle" : ThrottleJobName);
428
+ }
429
+ return throttleJob;
430
+ }
431
+
432
+ /// <summary>The CPU cap of the compilers in the throttle job, percent of the sandbox; 100 or more lifts it.</summary>
433
+ private static void SetCpuRate(int percent)
434
+ {
435
+ IntPtr throttle = ThrottleJob();
436
+ if (throttle == IntPtr.Zero)
437
+ {
438
+ return;
439
+ }
440
+ CpuRateControl info = new CpuRateControl();
441
+ if (percent < 100)
442
+ {
443
+ info.ControlFlags = CpuRateEnable | CpuRateHardCap;
444
+ info.CpuRate = (uint)(Math.Max(1, percent) * 100);
445
+ }
446
+ NativeMethods.SetInformationJobObject(throttle, JobObjectCpuRateControlInformation, ref info, Marshal.SizeOf(typeof(CpuRateControl)));
447
+ }
448
+
449
+ private static void Throttle(int pid)
450
+ {
451
+ IntPtr throttle = ThrottleJob();
452
+ if (throttle == IntPtr.Zero)
453
+ {
454
+ return;
455
+ }
456
+ IntPtr process = NativeMethods.OpenProcess(ProcessSetQuota | ProcessTerminate, false, pid);
457
+ if (process == IntPtr.Zero)
458
+ {
459
+ return;
460
+ }
461
+ try
462
+ {
463
+ NativeMethods.AssignProcessToJobObject(throttle, process);
464
+ }
465
+ finally
466
+ {
467
+ NativeMethods.CloseHandle(process);
468
+ }
469
+ }
470
+
471
+ /// <summary>Low memory priority: Windows trims this process's pages before others' (the agents keep theirs).</summary>
472
+ private static void SetMemoryPriority(int pid, uint priority)
473
+ {
474
+ IntPtr process = NativeMethods.OpenProcess(ProcessSetInformation, false, pid);
475
+ if (process == IntPtr.Zero)
476
+ {
477
+ return;
478
+ }
479
+ try
480
+ {
481
+ uint value = priority;
482
+ NativeMethods.SetProcessInformation(process, ProcessMemoryPriorityClass, ref value, sizeof(uint));
483
+ }
484
+ finally
485
+ {
486
+ NativeMethods.CloseHandle(process);
487
+ }
488
+ }
489
+
490
+ /// <summary>A waiting compiler's pages go to the paging file now, so the ones that run have the RAM.</summary>
491
+ private static void Trim(int pid)
492
+ {
493
+ IntPtr process = NativeMethods.OpenProcess(ProcessSetQuota, false, pid);
494
+ if (process == IntPtr.Zero)
495
+ {
496
+ return;
497
+ }
498
+ try
499
+ {
500
+ NativeMethods.SetProcessWorkingSetSize(process, new IntPtr(-1), new IntPtr(-1));
501
+ }
502
+ finally
503
+ {
504
+ NativeMethods.CloseHandle(process);
505
+ }
506
+ }
507
+
306
508
  private static void Suspend(int pid, int ms)
307
509
  {
308
510
  lock (Gate)
@@ -432,7 +432,17 @@ sealed class ClientHub
432
432
  clients.Add(client);
433
433
  }
434
434
  }
435
- TakeOwnership(client);
435
+ // Passed through, a size change makes the program redraw for everyone (a second copy of the screen in some),
436
+ // so a viewer that only looks leaves the size to the one in use; it takes over when it types or is resized.
437
+ bool owned;
438
+ lock (gate)
439
+ {
440
+ owned = sizeOwner != null;
441
+ }
442
+ if (!passthrough || !owned)
443
+ {
444
+ TakeOwnership(client);
445
+ }
436
446
  return true;
437
447
  }
438
448
 
@@ -781,9 +791,11 @@ sealed class ClientHub
781
791
  return;
782
792
  }
783
793
  PseudoConsole current;
794
+ bool same;
784
795
  lock (gate)
785
796
  {
786
797
  current = console;
798
+ same = cols == width && rows == height;
787
799
  cols = width;
788
800
  rows = height;
789
801
  }
@@ -791,7 +803,18 @@ sealed class ClientHub
791
803
  {
792
804
  return;
793
805
  }
794
- // ConPTY repaints everything on a size change; resizing by one row first forces that even for the same size.
806
+ if (Native.ConsoleKind.StartsWith("conpty"))
807
+ {
808
+ // Passed through: nothing repaints, the program redraws itself on every resize. Programs like Claude Code
809
+ // leave a second copy of their screen behind when they do, so resize only when the size really changes.
810
+ if (!same)
811
+ {
812
+ current.Resize(width, height);
813
+ }
814
+ return;
815
+ }
816
+ // Windows' own ConPTY repaints everything on a size change; resizing by one row first forces that even for the
817
+ // same size.
795
818
  current.Resize(width, (short)(height - 1));
796
819
  current.Resize(width, height);
797
820
  }
@@ -1090,6 +1113,10 @@ static class Native
1090
1113
  private const string AgentJobName = "Local\\AgentSandboxAgentsJob";
1091
1114
  private const int JobObjectExtendedLimitInformation = 9;
1092
1115
  private const uint JobObjectLimitJobMemory = 0x00000200;
1116
+ private const int ErrorAlreadyExists = 183;
1117
+ /// <summary>The backstop's headroom for "auto": this much of the commit limit, at least MinHeadroomMb.</summary>
1118
+ private const int HeadroomPercent = 3;
1119
+ private const long MinHeadroomMb = 1536;
1093
1120
  private const long BytesPerMb = 1024L * 1024L;
1094
1121
  private const int UseStdHandles = 0x00000100;
1095
1122
  private static readonly IntPtr PseudoConsoleAttribute = (IntPtr)0x00020016;
@@ -1157,6 +1184,21 @@ static class Native
1157
1184
  public ulong OtherTransferCount;
1158
1185
  }
1159
1186
 
1187
+ // MEMORYSTATUSEX: ullTotalPageFile is the commit limit (RAM plus paging files).
1188
+ [StructLayout(LayoutKind.Sequential)]
1189
+ private struct MemoryStatus
1190
+ {
1191
+ public uint Length;
1192
+ public uint MemoryLoad;
1193
+ public ulong TotalPhys;
1194
+ public ulong AvailPhys;
1195
+ public ulong TotalPageFile;
1196
+ public ulong AvailPageFile;
1197
+ public ulong TotalVirtual;
1198
+ public ulong AvailVirtual;
1199
+ public ulong AvailExtendedVirtual;
1200
+ }
1201
+
1160
1202
  [StructLayout(LayoutKind.Sequential)]
1161
1203
  private struct JobExtendedLimit
1162
1204
  {
@@ -1228,6 +1270,9 @@ static class Native
1228
1270
  [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
1229
1271
  public static extern IntPtr CreateJobObjectW(IntPtr attributes, string name);
1230
1272
 
1273
+ [DllImport("kernel32.dll", SetLastError = true)]
1274
+ public static extern bool GlobalMemoryStatusEx(ref MemoryStatus status);
1275
+
1231
1276
  [DllImport("kernel32.dll", SetLastError = true)]
1232
1277
  public static extern bool SetInformationJobObject(IntPtr job, int infoClass, ref JobExtendedLimit info, int length);
1233
1278
 
@@ -1300,10 +1345,12 @@ static class Native
1300
1345
  private static bool agentJobTried;
1301
1346
 
1302
1347
  /// <summary>
1303
- /// The shared job of everything the agents start, with a memory limit for all of it together (PTYHOST_JOB_MB, set
1304
- /// by the daemon from sandbox.agentMemoryHeadroomMb): a runaway build then gets out-of-memory errors in its own
1305
- /// session instead of using up the whole sandbox. Zero when no limit is asked for or the job cannot be made; ptyhost
1306
- /// itself is never in it. The handle stays open for ptyhost's whole life (the job lives while a handle is open).
1348
+ /// The shared job of everything the agents start (the governor reads it to slow builds down). Its only limit is a
1349
+ /// last-resort backstop: the sandbox's commit limit minus a small headroom (PTYHOST_BACKSTOP_MB: "auto", a number
1350
+ /// of MB, or "none"). The job's committed memory is part of the whole sandbox's, so it can reach the backstop only
1351
+ /// when Windows inside the sandbox is about to refuse memory to everyone; then a build is refused instead of
1352
+ /// Windows' own processes. Only the ptyhost that creates the job sets it: one that joins it never changes it.
1353
+ /// ptyhost itself is never in it; the handle stays open for ptyhost's whole life.
1307
1354
  /// </summary>
1308
1355
  private static IntPtr AgentJob(Log log)
1309
1356
  {
@@ -1312,31 +1359,73 @@ static class Native
1312
1359
  return agentJob;
1313
1360
  }
1314
1361
  agentJobTried = true;
1315
- long limitMb;
1316
- if (!long.TryParse(Environment.GetEnvironmentVariable("PTYHOST_JOB_MB"), out limitMb) || limitMb < 512)
1362
+ string setting = Environment.GetEnvironmentVariable("PTYHOST_BACKSTOP_MB");
1363
+ if (string.IsNullOrEmpty(setting))
1317
1364
  {
1318
1365
  return IntPtr.Zero;
1319
1366
  }
1320
- IntPtr job = NativeMethods.CreateJobObjectW(IntPtr.Zero, AgentJobName);
1367
+ // The self-test uses a job of its own (a name in the same family), never the agents' real one.
1368
+ string name = Environment.GetEnvironmentVariable("PTYHOST_JOB_NAME");
1369
+ if (string.IsNullOrEmpty(name) || !name.StartsWith(AgentJobName + "Test", StringComparison.Ordinal))
1370
+ {
1371
+ name = AgentJobName;
1372
+ }
1373
+ IntPtr job = NativeMethods.CreateJobObjectW(IntPtr.Zero, name);
1374
+ int error = Marshal.GetLastWin32Error();
1321
1375
  if (job == IntPtr.Zero)
1322
1376
  {
1323
- log.Write("agent job: CreateJobObject failed (" + Marshal.GetLastWin32Error() + "); no memory limit");
1377
+ log.Write("agent job: CreateJobObject failed (" + error + "); agents run outside it");
1324
1378
  return IntPtr.Zero;
1325
1379
  }
1380
+ agentJob = job;
1381
+ if (error == ErrorAlreadyExists)
1382
+ {
1383
+ log.Write("agent job: joined the existing one (its backstop stays as it was set)");
1384
+ return job;
1385
+ }
1386
+ if (setting == "none")
1387
+ {
1388
+ log.Write("agent job: no backstop (sandbox.agentMemoryHeadroomMb is none)");
1389
+ return job;
1390
+ }
1391
+ long commitMb;
1392
+ long limitMb = BackstopMb(setting, out commitMb);
1393
+ if (limitMb <= 0)
1394
+ {
1395
+ log.Write("agent job: the commit limit cannot be read; no backstop");
1396
+ return job;
1397
+ }
1326
1398
  JobExtendedLimit info = new JobExtendedLimit();
1327
1399
  info.BasicLimitInformation.LimitFlags = JobObjectLimitJobMemory;
1328
1400
  info.JobMemoryLimit = new UIntPtr((ulong)(limitMb * BytesPerMb));
1329
1401
  if (!NativeMethods.SetInformationJobObject(job, JobObjectExtendedLimitInformation, ref info, Marshal.SizeOf(typeof(JobExtendedLimit))))
1330
1402
  {
1331
- log.Write("agent job: setting the limit failed (" + Marshal.GetLastWin32Error() + "); no memory limit");
1332
- NativeMethods.CloseHandle(job);
1333
- return IntPtr.Zero;
1403
+ log.Write("agent job: setting the backstop failed (" + Marshal.GetLastWin32Error() + "); no backstop");
1404
+ return job;
1334
1405
  }
1335
- log.Write("agent job: everything the agent starts may use " + limitMb + " MB together");
1336
- agentJob = job;
1406
+ log.Write("agent job: last-resort backstop at " + limitMb + " MB committed (the sandbox's commit limit is " + commitMb + " MB)");
1337
1407
  return job;
1338
1408
  }
1339
1409
 
1410
+ /// <summary>The commit limit minus the headroom ("auto" or MB), at least half of it; 0 when it cannot be read.</summary>
1411
+ private static long BackstopMb(string setting, out long commitMb)
1412
+ {
1413
+ commitMb = 0;
1414
+ MemoryStatus status = new MemoryStatus();
1415
+ status.Length = (uint)Marshal.SizeOf(typeof(MemoryStatus));
1416
+ if (!NativeMethods.GlobalMemoryStatusEx(ref status))
1417
+ {
1418
+ return 0;
1419
+ }
1420
+ commitMb = (long)(status.TotalPageFile / (ulong)BytesPerMb);
1421
+ long headroom;
1422
+ if (setting == "auto" || !long.TryParse(setting, out headroom) || headroom < 0)
1423
+ {
1424
+ headroom = Math.Max(MinHeadroomMb, commitMb * HeadroomPercent / 100);
1425
+ }
1426
+ return Math.Max(commitMb / 2, commitMb - headroom);
1427
+ }
1428
+
1340
1429
  /// <summary>Starts `commandLine` attached to the pseudo console. Null std handles: never leak our own stdio into the child.</summary>
1341
1430
  public static IntPtr Spawn(string commandLine, IntPtr pseudoConsole, Log log)
1342
1431
  {