windows-development-sandbox 1.9.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 (398) hide show
  1. package/README.md +64 -17
  2. package/config.example.json +7 -2
  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 +23 -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 -88
  149. package/dist/cli/commands/web.d.ts +7 -1
  150. package/dist/cli/commands/web.js +45 -71
  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 +3 -2
  200. package/dist/core/schema/config.d.ts +6 -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/schema/vpn.js +1 -1
  211. package/dist/core/schema/web.d.ts +1 -0
  212. package/dist/core/schema/web.js +4 -1
  213. package/dist/core/types.d.ts +6 -3
  214. package/dist/guest/boot/boot.js +11 -1
  215. package/dist/guest/daemon/daemon.js +1 -1
  216. package/dist/guest/daemon/sessions.d.ts +3 -2
  217. package/dist/guest/daemon/sessions.js +38 -7
  218. package/dist/guest/governor/core.d.ts +44 -0
  219. package/dist/guest/governor/core.js +124 -0
  220. package/dist/guest/governor/decide.d.ts +53 -15
  221. package/dist/guest/governor/decide.js +155 -72
  222. package/dist/guest/governor/governor.d.ts +2 -35
  223. package/dist/guest/governor/governor.js +35 -99
  224. package/dist/guest/governor/index.d.ts +1 -0
  225. package/dist/guest/governor/index.js +1 -0
  226. package/dist/guest/network/firewall.d.ts +3 -0
  227. package/dist/guest/network/firewall.js +65 -23
  228. package/dist/guest/runtime/process.js +53 -5
  229. package/dist/guest/sessions/viewer.js +5 -1
  230. package/dist/guest/setup/claude-mem.js +12 -4
  231. package/dist/guest/setup/github-token.d.ts +7 -0
  232. package/dist/guest/setup/github-token.js +65 -0
  233. package/dist/guest/toolchains/languages/node.js +40 -4
  234. package/dist/guest/toolchains/native/rust-extras.js +2 -2
  235. package/dist/guest/toolchains/tools/cli.d.ts +8 -0
  236. package/dist/guest/toolchains/tools/cli.js +16 -2
  237. package/dist/guest/vpn/openvpn.d.ts +5 -0
  238. package/dist/guest/vpn/openvpn.js +14 -1
  239. package/dist/guest/vpn/vpn.js +55 -16
  240. package/dist/guest/vpn/wireguard.js +2 -0
  241. package/dist/host/channel/attach.d.ts +1 -0
  242. package/dist/host/channel/attach.js +1 -0
  243. package/dist/host/channel/channel.d.ts +1 -1
  244. package/dist/host/channel/channel.js +2 -2
  245. package/dist/host/engines/hcs/check.d.ts +1 -0
  246. package/dist/host/engines/hcs/check.js +1 -1
  247. package/dist/host/engines/hcs/engine.d.ts +1 -1
  248. package/dist/host/engines/hcs/engine.js +6 -4
  249. package/dist/host/engines/hcs/helper.d.ts +11 -0
  250. package/dist/host/engines/hcs/helper.js +31 -2
  251. package/dist/host/engines/hcs/leftovers.d.ts +42 -0
  252. package/dist/host/engines/hcs/leftovers.js +101 -0
  253. package/dist/host/engines/index.d.ts +1 -0
  254. package/dist/host/engines/index.js +1 -0
  255. package/dist/host/github/deliver.d.ts +13 -0
  256. package/dist/host/github/deliver.js +51 -0
  257. package/dist/host/github/index.d.ts +2 -0
  258. package/dist/host/github/index.js +3 -0
  259. package/dist/host/github/token.d.ts +21 -0
  260. package/dist/host/github/token.js +74 -0
  261. package/dist/host/launch/generate.js +11 -5
  262. package/dist/host/launch/index.d.ts +1 -1
  263. package/dist/host/launch/index.js +1 -1
  264. package/dist/host/launch/session-file.js +1 -2
  265. package/dist/host/launch/wsb-file.d.ts +11 -7
  266. package/dist/host/launch/wsb-file.js +30 -12
  267. package/dist/host/life/host-memory.d.ts +1 -0
  268. package/dist/host/life/host-memory.js +4 -1
  269. package/dist/host/life/watchdog.d.ts +11 -0
  270. package/dist/host/life/watchdog.js +36 -4
  271. package/dist/host/sandbox/tasklist.d.ts +6 -0
  272. package/dist/host/sandbox/tasklist.js +37 -0
  273. package/dist/host/secrets/launch-secrets.d.ts +2 -1
  274. package/dist/host/secrets/launch-secrets.js +4 -2
  275. package/dist/host/vpn/status.js +13 -3
  276. package/dist/host/web/backend.d.ts +1 -3
  277. package/dist/host/web/index.d.ts +2 -1
  278. package/dist/host/web/index.js +2 -1
  279. package/dist/host/web/live.d.ts +23 -0
  280. package/dist/host/web/live.js +107 -0
  281. package/dist/host/web/rpc.d.ts +6 -1
  282. package/dist/host/web/rpc.js +23 -31
  283. package/dist/host/web/runner.d.ts +4 -0
  284. package/dist/host/web/runner.js +12 -1
  285. package/dist/host/web/server.d.ts +30 -2
  286. package/dist/host/web/server.js +87 -29
  287. package/dist/host/web/state.d.ts +1 -0
  288. package/dist/host/web/state.js +2 -0
  289. package/dist/runtime/agent.js +44 -16
  290. package/dist/runtime/daemon.js +1154 -857
  291. package/dist/runtime/db.js +44 -16
  292. package/dist/runtime/main.js +532 -115
  293. package/dist/runtime/netproxy.js +34 -9
  294. package/dist/runtime/view.js +499 -149
  295. package/dist/selftest/app.test.js +620 -12
  296. package/dist/selftest/channel.test.js +18 -1
  297. package/dist/selftest/cli.test.js +1 -1
  298. package/dist/selftest/config.test.js +1 -1
  299. package/dist/selftest/contained.test.js +1 -1
  300. package/dist/selftest/data.test.js +1 -2
  301. package/dist/selftest/engine.test.js +3 -1
  302. package/dist/selftest/github.test.d.ts +2 -0
  303. package/dist/selftest/github.test.js +243 -0
  304. package/dist/selftest/guest-boot.test.js +47 -1
  305. package/dist/selftest/hcs.test.js +105 -8
  306. package/dist/selftest/health.test.js +71 -0
  307. package/dist/selftest/heavy-load.test.js +152 -66
  308. package/dist/selftest/index.js +4 -0
  309. package/dist/selftest/layout.test.js +8 -0
  310. package/dist/selftest/live-terminal.test.js +74 -0
  311. package/dist/selftest/menu-screens.js +171 -56
  312. package/dist/selftest/menu.test.js +54 -37
  313. package/dist/selftest/migrate.test.js +14 -12
  314. package/dist/selftest/network.test.js +20 -1
  315. package/dist/selftest/powershell-variables.test.d.ts +2 -0
  316. package/dist/selftest/powershell-variables.test.js +61 -0
  317. package/dist/selftest/support/engine-golden.js +1 -0
  318. package/dist/selftest/support/fixtures.d.ts +5 -0
  319. package/dist/selftest/support/web-preview.d.ts +1 -0
  320. package/dist/selftest/support/web-preview.js +856 -0
  321. package/dist/selftest/update-service.test.js +15 -8
  322. package/dist/selftest/vpn.test.js +86 -2
  323. package/dist/selftest/web.test.js +58 -11
  324. package/dist/shared/channel/run-dir.d.ts +2 -0
  325. package/dist/shared/channel/run-dir.js +3 -0
  326. package/dist/shared/channel/schema.d.ts +7 -0
  327. package/dist/shared/channel/schema.js +9 -1
  328. package/dist/shared/channel/secrets.d.ts +1 -0
  329. package/dist/shared/channel/secrets.js +3 -0
  330. package/dist/shared/github/index.d.ts +1 -0
  331. package/dist/shared/github/index.js +2 -0
  332. package/dist/shared/github/token.d.ts +29 -0
  333. package/dist/shared/github/token.js +89 -0
  334. package/dist/shared/io/copy-tree.d.ts +7 -0
  335. package/dist/shared/io/copy-tree.js +45 -0
  336. package/dist/shared/io/index.d.ts +2 -0
  337. package/dist/shared/io/index.js +2 -0
  338. package/dist/shared/io/robocopy-result.d.ts +6 -0
  339. package/dist/shared/io/robocopy-result.js +23 -0
  340. package/dist/shared/memory/policy.d.ts +38 -12
  341. package/dist/shared/memory/policy.js +58 -19
  342. package/dist/shared/network/firewall-rules.d.ts +8 -0
  343. package/dist/shared/network/firewall-rules.js +28 -0
  344. package/dist/shared/pty/ctrl-c.d.ts +18 -0
  345. package/dist/shared/pty/ctrl-c.js +36 -0
  346. package/dist/shared/pty/index.d.ts +1 -0
  347. package/dist/shared/pty/index.js +1 -0
  348. package/dist/shared/pty/terminal.d.ts +2 -0
  349. package/dist/shared/pty/terminal.js +21 -2
  350. package/dist/shared/vpn/kill-switch.d.ts +13 -2
  351. package/dist/shared/vpn/kill-switch.js +88 -8
  352. package/dist/shared/web/protocol.d.ts +0 -4
  353. package/dist/sync/watcher.js +1 -1
  354. package/dist/web/index.html +2 -2
  355. package/dist/web/{main-T5DMCHYV.css → main-6JF3ASB5.css} +1 -1
  356. package/dist/web/main-Z645W45E.js +219 -0
  357. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  358. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  359. package/native/csharp/HostTools/MemoryStatus.cs +128 -107
  360. package/native/scripts/memory-status.ps1 +10 -2
  361. package/package.json +10 -3
  362. package/sandbox/csharp/MemGov.cs +668 -398
  363. package/sandbox/csharp/PtyHost.cs +104 -15
  364. package/sandbox/csharp/SandboxTools/CpuCap.cs +50 -1
  365. package/sandbox/csharp/SandboxTools/Program.cs +1 -1
  366. package/sandbox/scripts/new-shortcuts.ps1 +3 -1
  367. package/sandbox/scripts/repair-wmi-services.ps1 +26 -0
  368. package/schemas/config.schema.json +25 -7
  369. package/schemas/sandbox-config.schema.json +7 -3
  370. package/dist/cli/commands/accounts.d.ts +0 -2
  371. package/dist/cli/commands/accounts.js +0 -96
  372. package/dist/cli/commands/data/clean.d.ts +0 -3
  373. package/dist/cli/commands/data/clean.js +0 -60
  374. package/dist/cli/commands/data/index.d.ts +0 -4
  375. package/dist/cli/commands/data/index.js +0 -62
  376. package/dist/cli/commands/maintenance/clean.d.ts +0 -2
  377. package/dist/cli/commands/maintenance/clean.js +0 -24
  378. package/dist/cli/commands/maintenance/config-share.d.ts +0 -12
  379. package/dist/cli/commands/maintenance/config-share.js +0 -80
  380. package/dist/cli/commands/projects/projects.d.ts +0 -2
  381. package/dist/cli/commands/projects/projects.js +0 -162
  382. package/dist/cli/commands/storage/contained.d.ts +0 -3
  383. package/dist/cli/commands/storage/contained.js +0 -179
  384. package/dist/cli/commands/storage/storage.d.ts +0 -8
  385. package/dist/cli/commands/storage/storage.js +0 -179
  386. package/dist/cli/menu/add-agent.d.ts +0 -47
  387. package/dist/cli/menu/add-agent.js +0 -168
  388. package/dist/cli/menu/apps.d.ts +0 -69
  389. package/dist/cli/menu/apps.js +0 -214
  390. package/dist/cli/menu/data-folder.d.ts +0 -18
  391. package/dist/cli/menu/data-folder.js +0 -99
  392. package/dist/cli/menu/share-settings.d.ts +0 -21
  393. package/dist/cli/menu/share-settings.js +0 -112
  394. package/dist/cli/menu/vpn-login.d.ts +0 -16
  395. package/dist/cli/menu/vpn-login.js +0 -74
  396. package/dist/web/main-NRRS5O6Z.js +0 -157
  397. /package/dist/cli/{commands/data/collect.d.ts → services/data-collect.d.ts} +0 -0
  398. /package/dist/cli/{commands/data/remove.d.ts → services/data-remove.d.ts} +0 -0
@@ -5532,7 +5532,7 @@ var require_lodash = __commonJS({
5532
5532
  });
5533
5533
 
5534
5534
  // src/guest/entries/main.ts
5535
- import fs60 from "node:fs";
5535
+ import fs61 from "node:fs";
5536
5536
  import path74 from "node:path";
5537
5537
 
5538
5538
  // src/guest/runtime/paths.ts
@@ -25411,6 +25411,9 @@ var AttachSchema = external_exports.strictObject({
25411
25411
  ),
25412
25412
  autoHide: external_exports.boolean().describe(
25413
25413
  "Hide the Windows Sandbox window whenever this terminal attaches; it keeps running (sandbox show brings it back)."
25414
+ ),
25415
+ confirmCtrlC: external_exports.boolean().describe(
25416
+ 'In an attached terminal and the sandbox window, a single Ctrl+C is held back (the title says so) and only a second one within 1.5 seconds reaches the agent: a Ctrl+C meant as "copy" no longer stops it. Esc still stops an agent.'
25414
25417
  )
25415
25418
  });
25416
25419
  var AgentCommandsSchema = external_exports.strictObject({
@@ -25455,7 +25458,9 @@ var SandboxSchema = external_exports.strictObject({
25455
25458
  engine: external_exports.enum(["windows-sandbox", "hcs"]).describe(
25456
25459
  '(alpha) What runs the sandbox. "windows-sandbox" = Windows Sandbox (the default: desktop, Windows Terminal, GPU, clipboard, audio, printers). "hcs" = a headless Hyper-V isolated Server Core container for agents (no desktop, GPU or clipboard); it needs Windows features and an image on this PC (sandbox engine check hcs lists what is missing), and starting it is not enabled yet in this alpha. sandbox engine shows what each engine can do. Yours only: a project file (.sandbox-config.json) cannot set it.'
25457
25460
  ),
25458
- memoryMb: external_exports.union([external_exports.literal("auto"), external_exports.number().int().min(2048)]).describe('Sandbox RAM in MB (>= 2048), or "auto": free RAM minus reserveHostMb, capped at maxMemoryMb.'),
25461
+ memoryMb: external_exports.union([external_exports.literal("auto"), external_exports.number().int().min(2048)]).describe(
25462
+ 'Sandbox RAM in MB (>= 2048), or "auto": free RAM minus reserveHostMb, capped at maxMemoryMb. Never more than this PC can keep in RAM for it (its RAM minus the larger of reserveHostMb and 25 %, 2 GB more with vGPU): a bigger value is lowered.'
25463
+ ),
25459
25464
  maxMemoryMb: external_exports.number().int().positive().describe('Upper limit for memoryMb "auto".'),
25460
25465
  maxMemoryPercent: external_exports.number().int().min(10).max(100).describe(`memoryMb "auto" never takes more than this share of this PC's total RAM (percent).`),
25461
25466
  cpuLimit: external_exports.union([external_exports.literal("none"), external_exports.number().int().min(5).max(100)]).describe(
@@ -25468,10 +25473,10 @@ var SandboxSchema = external_exports.strictObject({
25468
25473
  'Memory always left for this PC when memoryMb is "auto": the sandbox gets at most (free RAM, and RAM plus paging file still unused) minus this.'
25469
25474
  ),
25470
25475
  agentMemoryHeadroomMb: external_exports.union([external_exports.literal("auto"), external_exports.literal("none"), external_exports.number().int().min(0)]).describe(
25471
- 'Memory kept out of the agents reach inside the sandbox, in MB: everything the agents start (their builds, tests, tools) together may use at most the sandbox memory minus this; a runaway parallel build then gets out-of-memory errors in its own session instead of freezing or crashing the whole sandbox. "auto" = the larger of 2048 MB and 10 % of the sandbox memory; "none" = no limit. Yours only: a project file cannot change it.'
25476
+ 'The last resort that keeps the sandbox up, in MB below its commit limit (RAM plus paging file): builds are slowed down long before (sandbox.pressureResponse); only if everything the agents start together would take the sandbox to the very edge of its memory, where Windows inside it would start failing, is a build refused memory instead of Windows. "auto" = the larger of 1536 MB and 3 % of the commit limit; "none" = no backstop. Yours only: a project file cannot change it.'
25472
25477
  ),
25473
25478
  pressureResponse: external_exports.boolean().describe(
25474
- "When the sandbox (or this PC) runs low on memory, slow the heaviest build programs down (lower priority) and, if that is not enough, pause the biggest compiler for a few seconds at a time, so heavy parallel builds slow down instead of closing the sandbox. Never touches the agents themselves. Yours only: a project file cannot change it."
25479
+ "When the sandbox (or this PC) runs low on memory, slow builds down in steps, like a PC that gets slower instead of failing: build programs get lower CPU and memory priority, then less CPU, then compilers take turns (the waiting ones briefly suspended and trimmed to the paging file). Nothing is stopped or killed, and the agents themselves are never touched. Yours only: a project file cannot change it."
25475
25480
  ),
25476
25481
  vGpu: external_exports.boolean().describe(
25477
25482
  "Virtual GPU. Off by default: Microsoft notes it widens the sandbox attack surface. The sandbox then uses this PC's graphics card, and with some graphics drivers heavy use can freeze the whole PC: turn it on only when a program in the sandbox needs the GPU (the mesa toolchain gives software OpenGL without it)."
@@ -25617,7 +25622,7 @@ var ToolchainsSchema = external_exports.strictObject({
25617
25622
  `GNU host: "lld" links with Rust's own lld through MinGW's gcc (much less memory than GNU ld), "gnu" with GNU ld.`
25618
25623
  ),
25619
25624
  linkSlots: external_exports.union([external_exports.literal("auto"), external_exports.number().int().min(1).max(32)]).describe(
25620
- 'GNU host: how many programs may link at the same time in the whole sandbox (other links wait). "auto" = one per 8 GB of sandbox memory, 2 to 4. Several agents linking big programs at once can take Windows down.'
25625
+ 'GNU host: how many programs may link at the same time in the whole sandbox (other links wait). "auto" = one per 3 GB of sandbox memory after 6 GB, 2 to 8 (the memory governor slows links down too when memory runs short).'
25621
25626
  )
25622
25627
  }),
25623
25628
  msvc: external_exports.strictObject({
@@ -25692,7 +25697,7 @@ var ToolchainsSchema = external_exports.strictObject({
25692
25697
  }),
25693
25698
  cli: external_exports.strictObject({
25694
25699
  gh: EnabledSchema.describe(
25695
- 'GitHub CLI (gh). Only with provisioning.git.access "full": gh can push and merge through the GitHub API, past a read-only git. Your PC\'s gh login is never copied; `gh auth login` inside persists (GH_CONFIG_DIR). "auto" = when the project has a .github folder.'
25700
+ 'GitHub CLI (gh). Only with provisioning.git.access "full": gh can push and merge through the GitHub API, past a read-only git. Your PC\'s gh login is never copied; `gh auth login` inside persists (GH_CONFIG_DIR), unless a token you saved for sandboxes is handed over (github.shareToken). "auto" = when the project has a .github folder.'
25696
25701
  ),
25697
25702
  zstd: EnabledSchema.describe('Zstandard (zstd). "auto" = off; set true to get it.'),
25698
25703
  openssl: EnabledSchema.describe(
@@ -26595,16 +26600,17 @@ var ResumeSchema = external_exports.strictObject({
26595
26600
  // src/core/schema/web.ts
26596
26601
  var WebSchema = external_exports.strictObject({
26597
26602
  enabled: external_exports.boolean().describe(
26598
- "(Beta) Allow the web UI: your sandbox sessions in a browser on this PC. Off by default, and nothing starts it by itself yet: sandbox web start works either way and runs it until sandbox web stop (or a restart of the PC)."
26603
+ "(Beta) The web UI: your sandbox sessions in a browser on this PC (http://127.0.0.1:<port>, this PC only). On: every sandbox start also starts it in the background (it keeps running until sandbox web stop or a restart of the PC). Off: only sandbox web start runs it. Open it signed in with sandbox web open."
26599
26604
  ),
26600
- port: external_exports.number().int().min(1024).max(65535).describe("Port of the web UI on 127.0.0.1 (this PC only). Pick another when it is taken.")
26605
+ port: external_exports.number().int().min(1024).max(65535).describe("Port of the web UI on 127.0.0.1 (this PC only). Pick another when it is taken."),
26606
+ openOnLaunch: external_exports.boolean().describe("(Beta) With the web UI on: open it in your browser, already signed in, every time a sandbox starts.")
26601
26607
  });
26602
26608
 
26603
26609
  // src/core/schema/vpn.ts
26604
26610
  var VPN_PROVIDERS = ["nordvpn", "openvpn-file", "wireguard-file"];
26605
26611
  var VpnSchema = external_exports.strictObject({
26606
26612
  enabled: external_exports.boolean().describe(
26607
- "Send the sandbox's traffic through a VPN. Only the sandbox: your PC's own connection is untouched. Needs a saved login first (sandbox vpn login). The agent in the sandbox can read that login and can turn the tunnel off (it is the administrator there)."
26613
+ "(Alpha: not yet tried on a real PC) Send the sandbox's traffic through a VPN. Only the sandbox: your PC's own connection is untouched. Needs a saved login first (sandbox vpn login). The agent in the sandbox can read that login and can turn the tunnel off (it is the administrator there)."
26608
26614
  ),
26609
26615
  provider: external_exports.enum(VPN_PROVIDERS).describe(
26610
26616
  "nordvpn: a recommended NordVPN server is picked at every launch. openvpn-file: your own .ovpn file (configFile). wireguard-file: your own WireGuard .conf (configFile), e.g. from your company."
@@ -26619,6 +26625,13 @@ var VpnSchema = external_exports.strictObject({
26619
26625
  )
26620
26626
  });
26621
26627
 
26628
+ // src/core/schema/github.ts
26629
+ var GithubSchema = external_exports.strictObject({
26630
+ shareToken: external_exports.boolean().describe(
26631
+ 'Give each sandbox the GitHub token you saved with `sandbox github login` (best a fine-grained token limited to some repositories and permissions), so gh and https git work there without logging in. Only with provisioning.git.access "full". The agent in the sandbox can read the token and use it for whatever it allows. A project file (.sandbox-config.json) cannot turn this on.'
26632
+ )
26633
+ });
26634
+
26622
26635
  // src/core/schema/config.ts
26623
26636
  var ConfigSchema = external_exports.strictObject({
26624
26637
  $schema: SchemaRefSchema.optional(),
@@ -26649,6 +26662,7 @@ var ConfigSchema = external_exports.strictObject({
26649
26662
  }),
26650
26663
  network: NetworkSchema,
26651
26664
  vpn: VpnSchema,
26665
+ github: GithubSchema,
26652
26666
  provisioning: ProvisioningSchema,
26653
26667
  downloads: DownloadsSchema,
26654
26668
  toolchains: ToolchainsSchema,
@@ -26811,19 +26825,25 @@ var GuestRequestSchema = external_exports.discriminatedUnion("kind", [
26811
26825
  id: IdSchema,
26812
26826
  reason: Text(300),
26813
26827
  seconds: external_exports.number().int().min(10).max(300),
26828
+ /** "severe": the PC is nearly out of memory; builds go to the governor's last step at once. */
26829
+ level: external_exports.enum(["slow", "severe"]).optional(),
26814
26830
  createdAt: TimeSchema
26815
26831
  })
26816
26832
  ]);
26817
26833
  var PressureStatusSchema = external_exports.object({
26818
26834
  level: external_exports.enum(["ok", "throttled", "suspending"]),
26835
+ /** The governor's dial, 0 (nothing wrong) to 4 (one compiler at a time). */
26836
+ step: external_exports.number().int().min(0).max(4).optional(),
26819
26837
  profile: external_exports.enum(["normal", "gentle"]),
26820
26838
  /** Share of the sandbox's RAM still free and of its commit limit in use (percent). */
26821
26839
  ramFreePercent: external_exports.number().int().min(0).max(100),
26822
26840
  commitPercent: external_exports.number().int().min(0).max(100),
26823
26841
  /** Programs running at lower priority because of it. */
26824
26842
  lowered: external_exports.number().int().min(0).max(1024),
26825
- /** Programs paused right now (name, process id, seconds left). */
26843
+ /** Compilers waiting their turn right now (name, process id, seconds until they may go on). */
26826
26844
  paused: external_exports.array(external_exports.object({ name: Text(64), pid: external_exports.number().int(), seconds: external_exports.number().int().min(0).max(60) })).max(8),
26845
+ /** The CPU share the compilers get together (percent; 100 = no cap). */
26846
+ cpuPercent: external_exports.number().int().min(0).max(100).optional(),
26827
26847
  /** The PC itself asked for slowing down (its memory or CPU is low). */
26828
26848
  pcLow: external_exports.boolean(),
26829
26849
  lastAction: Text(300),
@@ -26867,6 +26887,8 @@ var VpnStatusSchema = external_exports.object({
26867
26887
  state: external_exports.enum(["off", "starting", "up", "failed"]),
26868
26888
  message: Text(2e3),
26869
26889
  killSwitch: external_exports.boolean(),
26890
+ /** A direct connection was tried after the kill switch was applied: false = it still got out. */
26891
+ killSwitchHeld: external_exports.boolean().optional(),
26870
26892
  /** The server's name, e.g. uk2201.nordvpn.com. */
26871
26893
  server: Text(128).optional(),
26872
26894
  /** What the sandbox's traffic looks like from outside, once the tunnel is up. */
@@ -27014,6 +27036,21 @@ function safeRemove(target, root) {
27014
27036
  fs3.rmSync(target, { recursive: true, force: true, maxRetries: 2 });
27015
27037
  }
27016
27038
 
27039
+ // src/shared/io/robocopy-result.ts
27040
+ function robocopyFailed(code) {
27041
+ return code === null || code >= 8;
27042
+ }
27043
+ function robocopyMeaning(code) {
27044
+ if (code === null) return "robocopy did not finish";
27045
+ if (code >= 16) return "robocopy stopped with a fatal error (bad path or no access)";
27046
+ if (code >= 8) return code & 1 ? "some files were copied and some could not be" : "files could not be copied";
27047
+ return code === 0 ? "nothing to copy" : "copied";
27048
+ }
27049
+ function robocopyErrors(output3, max = 3) {
27050
+ const lines = output3.split(/\r?\n/).map((l) => l.trim()).filter((l) => /\bERROR\b/.test(l) || /^The (process|system) cannot/i.test(l));
27051
+ return lines.slice(0, max).map((l) => l.length > 220 ? l.slice(0, 217) + "..." : l);
27052
+ }
27053
+
27017
27054
  // src/shared/channel/run-dir.ts
27018
27055
  function safeId(id) {
27019
27056
  return IdSchema.parse(id);
@@ -27039,6 +27076,8 @@ var RunDir = class {
27039
27076
  vpnFile;
27040
27077
  /** What the memory governor in the sandbox is doing (pressure.json). */
27041
27078
  pressureFile;
27079
+ /** What the sandbox's programs may really use (budget.json): processors, memory, build jobs, link slots. */
27080
+ budgetFile;
27042
27081
  requestsDir;
27043
27082
  responsesDir;
27044
27083
  sessionsDir;
@@ -27052,6 +27091,7 @@ var RunDir = class {
27052
27091
  this.sandboxFile = path6.join(this.root, "sandbox.json");
27053
27092
  this.vpnFile = path6.join(this.root, "vpn.json");
27054
27093
  this.pressureFile = path6.join(this.root, "pressure.json");
27094
+ this.budgetFile = path6.join(this.root, "budget.json");
27055
27095
  this.requestsDir = path6.join(this.root, "requests");
27056
27096
  this.responsesDir = path6.join(this.root, "responses");
27057
27097
  this.sessionsDir = path6.join(this.root, "sessions");
@@ -27491,7 +27531,7 @@ function writeStatus(phase, message) {
27491
27531
  }
27492
27532
 
27493
27533
  // src/guest/boot/boot.ts
27494
- import fs59 from "node:fs";
27534
+ import fs60 from "node:fs";
27495
27535
  import path73 from "node:path";
27496
27536
  import { spawn as spawn9 } from "node:child_process";
27497
27537
 
@@ -27596,7 +27636,16 @@ function execOutput(file3, args, options = {}) {
27596
27636
  return result.status === 0 ? String(result.stdout ?? "").trim() : "";
27597
27637
  }
27598
27638
  function runScript(name, args = []) {
27599
- exec(SYSTEM.powershell(), scriptArgs(name, args));
27639
+ const result = spawnSync(SYSTEM.powershell(), scriptArgs(name, args), {
27640
+ stdio: ["inherit", "inherit", "pipe"],
27641
+ encoding: "utf8",
27642
+ windowsHide: true
27643
+ });
27644
+ if (result.error) throw result.error;
27645
+ const errors = String(result.stderr ?? "").trim();
27646
+ if (errors) process.stderr.write(errors + "\n");
27647
+ if (result.status !== 0)
27648
+ throw new Error(name + " failed with exit code " + result.status + (errors ? ": " + errors.slice(0, 400) : ""));
27600
27649
  }
27601
27650
  function scriptOutput(name, args = [], options = {}) {
27602
27651
  return execOutput(SYSTEM.powershell(), scriptArgs(name, args), options);
@@ -27622,14 +27671,40 @@ function robocopy(from, to, files = []) {
27622
27671
  fs12.copyFileSync(path15.join(from, f), path15.join(to, f));
27623
27672
  return;
27624
27673
  }
27625
- const what = files.length ? files : ["/E", "/MT:16"];
27626
- const result = spawnSync(
27627
- SYSTEM.robocopy(),
27628
- [from, to, ...what, "/NFL", "/NDL", "/NJH", "/NJS", "/NP", "/R:1", "/W:1", "/XJ"],
27629
- { stdio: "ignore", windowsHide: true }
27674
+ const what = files.length ? files : ["/E"];
27675
+ const passes = [
27676
+ ["/MT:16", "/R:1", "/W:1"],
27677
+ ["/MT:1", "/R:4", "/W:3"]
27678
+ ];
27679
+ let code = null;
27680
+ let output3 = "";
27681
+ for (const pass of passes) {
27682
+ const result = spawnSync(
27683
+ SYSTEM.robocopy(),
27684
+ [
27685
+ from,
27686
+ to,
27687
+ ...what,
27688
+ ...files.length ? [] : [pass[0]],
27689
+ pass[1],
27690
+ pass[2],
27691
+ "/NFL",
27692
+ "/NDL",
27693
+ "/NJH",
27694
+ "/NJS",
27695
+ "/NP",
27696
+ "/XJ"
27697
+ ],
27698
+ { encoding: "utf8", windowsHide: true }
27699
+ );
27700
+ code = result.status;
27701
+ output3 = String(result.stdout ?? "") + String(result.stderr ?? "");
27702
+ if (!robocopyFailed(code)) return;
27703
+ }
27704
+ const why = robocopyErrors(output3);
27705
+ throw new Error(
27706
+ "robocopy " + from + " -> " + to + " failed (exit " + code + ": " + robocopyMeaning(code) + ")" + (why.length ? ": " + why.join(" | ") : "")
27630
27707
  );
27631
- if ((result.status ?? 16) >= 8)
27632
- throw new Error("robocopy " + from + " -> " + to + " failed (exit " + result.status + ")");
27633
27708
  }
27634
27709
  function copyNode(from, to) {
27635
27710
  const work = stagingFolder(to);
@@ -29183,9 +29258,17 @@ async function setupUv(env) {
29183
29258
  function ghConfigDir() {
29184
29259
  return path34.join(stateDir, "gh");
29185
29260
  }
29261
+ function deliveredGhConfigDir() {
29262
+ return path34.join(localDir, "gh-token");
29263
+ }
29264
+ function ghExePath() {
29265
+ const entry = CLI_TOOLS.find((t) => t.name === "gh");
29266
+ const dir = tool("cli", "gh");
29267
+ return path34.join(entry?.subdir ? path34.join(dir, entry.subdir) : dir, entry?.exe ?? "gh.exe");
29268
+ }
29186
29269
  var toolVars = {
29187
29270
  gh: () => ({
29188
- GH_CONFIG_DIR: mkdirp(ghConfigDir()),
29271
+ GH_CONFIG_DIR: exists(deliveredGhConfigDir()) ? deliveredGhConfigDir() : mkdirp(ghConfigDir()),
29189
29272
  GH_NO_UPDATE_NOTIFIER: "1",
29190
29273
  GH_NO_EXTENSION_UPDATE_NOTIFIER: "1"
29191
29274
  })
@@ -29990,6 +30073,10 @@ var ensureNvm = async () => {
29990
30073
  log("Downloading nvm-windows (" + asset.url.split("/").slice(-2, -1)[0] + ") ...");
29991
30074
  await downloader.archive(asset.url, NVM_HOME, { sha256: asset.sha256, locate: "nvm.exe", fileName: asset.name });
29992
30075
  };
30076
+ var hostNodeFolder = (version2) => {
30077
+ const dir = path45.join(hostToolsDir, "node");
30078
+ return session.hostTools.nodeVersion?.replace(/^v/, "") === version2 && exists(path45.join(dir, "node.exe")) ? dir : null;
30079
+ };
29993
30080
  var installNodeVersion = async (version2) => {
29994
30081
  const dir = path45.join(NVM_HOME, "v" + version2);
29995
30082
  const fromHost = path45.join(HOST_NVM, "v" + version2);
@@ -29999,6 +30086,12 @@ var installNodeVersion = async (version2) => {
29999
30086
  copyNode(fromHost, dir);
30000
30087
  return;
30001
30088
  }
30089
+ const hostNode = hostNodeFolder(version2);
30090
+ if (hostNode) {
30091
+ log("Copying Node " + version2 + " from this PC (without global packages) ...");
30092
+ copyNode(hostNode, dir);
30093
+ return;
30094
+ }
30002
30095
  log("Downloading Node " + version2 + " (~30 MB once) ...");
30003
30096
  mkdirp(NVM_TEMP);
30004
30097
  let out;
@@ -30007,8 +30100,13 @@ var installNodeVersion = async (version2) => {
30007
30100
  } finally {
30008
30101
  safeRemove(NVM_TEMP, NVM_HOME);
30009
30102
  }
30010
- if (!exists(path45.join(dir, "node.exe")))
30011
- throw new Error("nvm install " + version2 + " failed: " + out.trim().split("\n").slice(-2).join(" "));
30103
+ if (!exists(path45.join(dir, "node.exe"))) {
30104
+ const tail2 = out.trim().split(/\r?\n/).filter(Boolean).slice(-3).join(" ");
30105
+ const why = /complete/i.test(out) ? "nvm said it finished, but its move into the data folder did not happen" : "nvm could not download or unpack it: check the internet connection and the free disk space";
30106
+ throw new Error(
30107
+ "nvm install " + version2 + " did not leave " + path45.join(dir, "node.exe") + " (" + why + ")" + (tail2 ? ". nvm printed: " + tail2 : "")
30108
+ );
30109
+ }
30012
30110
  };
30013
30111
  var activate = async (version2) => {
30014
30112
  await nvm(["use", version2]);
@@ -30016,6 +30114,10 @@ var activate = async (version2) => {
30016
30114
  fs36.rmSync(NVM_SYMLINK, { recursive: true, force: true });
30017
30115
  fs36.symlinkSync(path45.join(NVM_HOME, "v" + version2), NVM_SYMLINK, "junction");
30018
30116
  };
30117
+ var PLAYWRIGHT_VARS = () => ({
30118
+ PLAYWRIGHT_BROWSERS_PATH: tool("ms-playwright"),
30119
+ PLAYWRIGHT_MCP_BROWSER: "msedge"
30120
+ });
30019
30121
  async function setupNode(env, versions, config2) {
30020
30122
  if (dryRun) {
30021
30123
  log("(dry run) would provision nvm and Node " + versions.node.spec + " (from " + versions.node.source + ")");
@@ -30049,13 +30151,14 @@ async function setupNode(env, versions, config2) {
30049
30151
  warn("Node " + version2 + " from this PC: " + errMsg(e));
30050
30152
  }
30051
30153
  }
30052
- Object.assign(env.vars, { NVM_HOME, NVM_SYMLINK });
30154
+ Object.assign(env.vars, { NVM_HOME, NVM_SYMLINK, ...PLAYWRIGHT_VARS() });
30053
30155
  if (duringSession()) env.prepend.push(path45.join(NVM_HOME, "v" + main), NVM_HOME, NVM_SYMLINK);
30054
30156
  else {
30055
30157
  await activate(main);
30056
30158
  env.prepend.push(NVM_HOME, NVM_SYMLINK);
30057
30159
  }
30058
30160
  env.summary.push("node: " + main + " (from " + versions.node.source + ", switch with nvm use)");
30161
+ env.summary.push("playwright: ready (Edge; browsers it downloads are kept; in tests use channel: 'msedge')");
30059
30162
  await packageManagers(env, config2, main);
30060
30163
  }
30061
30164
  var packageManagers = async (env, config2, version2) => {
@@ -31966,11 +32069,17 @@ import os8 from "node:os";
31966
32069
  import path51 from "node:path";
31967
32070
 
31968
32071
  // src/shared/memory/policy.ts
32072
+ function buildJobs(cpus, memoryGb) {
32073
+ return Math.max(2, Math.min(cpus, Math.floor((memoryGb - 6) / 3)));
32074
+ }
32075
+ function linkSlotsFor(memoryGb) {
32076
+ return Math.max(2, Math.min(8, Math.floor((memoryGb - 6) / 3)));
32077
+ }
31969
32078
  function cargoJobs(cpus, memoryGb, sessions) {
31970
- const byCpu = Math.max(2, Math.min(4, Math.floor(cpus / 4)));
32079
+ const byCpu = Math.max(2, Math.min(8, Math.floor(cpus / 4)));
31971
32080
  const byMemory = Math.max(1, Math.floor(memoryGb / 4));
31972
32081
  const jobs = Math.min(byCpu, byMemory);
31973
- return Math.max(1, Math.min(jobs, Math.floor(8 / Math.max(1, sessions))));
32082
+ return Math.max(1, Math.min(jobs, Math.floor(buildJobs(cpus, memoryGb) / Math.max(1, sessions))));
31974
32083
  }
31975
32084
 
31976
32085
  // src/guest/toolchains/native/rust-extras.ts
@@ -31993,7 +32102,7 @@ function linkGate(env, rust, host) {
31993
32102
  const gate = path51.join(mkdirp(tool("shims")), "linkgate.exe");
31994
32103
  fs41.copyFileSync(ensureCompiled("linkgate"), gate);
31995
32104
  const memoryGb = os8.totalmem() / 1024 ** 3;
31996
- const slots = rust.linkSlots === "auto" ? Math.max(2, Math.min(4, Math.floor(memoryGb / 8))) : rust.linkSlots;
32105
+ const slots = rust.linkSlots === "auto" ? linkSlotsFor(memoryGb) : rust.linkSlots;
31997
32106
  env.vars.AGENT_LINK_SLOTS = String(slots);
31998
32107
  env.vars.AGENT_REAL_LINKER = gcc;
31999
32108
  env.vars.CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER = gate;
@@ -33042,9 +33151,31 @@ function buildFirewallCommands(exemptIps) {
33042
33151
  }
33043
33152
  return commands;
33044
33153
  }
33154
+ var ATTACH_RULE = "AgentSandbox-Attach";
33155
+ function hasShortName(file3) {
33156
+ return file3.split(/[/\\]/).some((part) => /~\d/.test(part));
33157
+ }
33158
+ function attachRuleCommands(ptyHostExe) {
33159
+ return [
33160
+ ["advfirewall", "firewall", "delete", "rule", "name=" + ATTACH_RULE],
33161
+ [
33162
+ "advfirewall",
33163
+ "firewall",
33164
+ "add",
33165
+ "rule",
33166
+ "name=" + ATTACH_RULE,
33167
+ "dir=in",
33168
+ "action=allow",
33169
+ "enable=yes",
33170
+ "profile=any",
33171
+ "protocol=tcp",
33172
+ "remoteip=LocalSubnet",
33173
+ "program=" + ptyHostExe
33174
+ ]
33175
+ ];
33176
+ }
33045
33177
 
33046
33178
  // src/guest/network/firewall.ts
33047
- var ATTACH_RULE = "AgentSandbox-Attach";
33048
33179
  var netsh = (args) => {
33049
33180
  const r = spawnSync5(SYSTEM.netsh(), args, { encoding: "utf8", windowsHide: true });
33050
33181
  return { status: r.status, out: (r.stdout ?? "") + (r.stderr ?? "") };
@@ -33078,23 +33209,38 @@ var waitForInternet = async (seconds) => {
33078
33209
  }
33079
33210
  return false;
33080
33211
  };
33081
- var attachRule = (ptyHostExe) => [
33082
- ["advfirewall", "firewall", "delete", "rule", "name=" + ATTACH_RULE],
33083
- [
33084
- "advfirewall",
33085
- "firewall",
33086
- "add",
33087
- "rule",
33088
- "name=" + ATTACH_RULE,
33089
- "dir=in",
33090
- "action=allow",
33091
- "enable=yes",
33092
- "profile=any",
33093
- "protocol=tcp",
33094
- "remoteip=LocalSubnet",
33095
- "program=" + ptyHostExe
33096
- ]
33097
- ];
33212
+ var problems = [];
33213
+ function networkProblems() {
33214
+ return problems;
33215
+ }
33216
+ function longPath(file3) {
33217
+ if (!hasShortName(file3)) return file3;
33218
+ try {
33219
+ return fs48.realpathSync.native(file3);
33220
+ } catch {
33221
+ return file3;
33222
+ }
33223
+ }
33224
+ async function applyAttachRule(ptyHostExe) {
33225
+ if (!session.attach.enabled) return;
33226
+ if (!ptyHostExe) {
33227
+ problems.push("attach: the session host was not built, so the firewall rule for it was not created");
33228
+ warn(problems[problems.length - 1]);
33229
+ return;
33230
+ }
33231
+ let last = "";
33232
+ for (let attempt = 1; attempt <= 3; attempt++) {
33233
+ try {
33234
+ run2(attachRuleCommands(longPath(ptyHostExe)));
33235
+ return;
33236
+ } catch (e) {
33237
+ last = errMsg(e);
33238
+ if (attempt < 3) await new Promise((r) => setTimeout(r, 1e3));
33239
+ }
33240
+ }
33241
+ problems.push("attach: the firewall rule for the session host could not be created (" + last + ")");
33242
+ warn("PROTECTION MISSING: " + problems[problems.length - 1]);
33243
+ }
33098
33244
  function run2(commands) {
33099
33245
  for (const c of commands) {
33100
33246
  const r = netsh(c);
@@ -33107,13 +33253,14 @@ async function applyNetworkPolicy(ptyHostExe) {
33107
33253
  log("Networking is disabled for this sandbox.");
33108
33254
  return "offline";
33109
33255
  }
33110
- const block = session.network.blockPrivateRanges;
33111
- const exempt = block ? discoverExemptIps() : [];
33112
- const lanRules = block ? buildFirewallCommands(exempt) : [];
33256
+ const block2 = session.network.blockPrivateRanges;
33257
+ const exempt = block2 ? discoverExemptIps() : [];
33258
+ const lanRules = block2 ? buildFirewallCommands(exempt) : [];
33259
+ const forwardRules = forwardRule();
33113
33260
  const commands = [
33114
33261
  ...lanRules,
33115
- ...ptyHostExe && session.attach.enabled ? attachRule(ptyHostExe) : [],
33116
- ...forwardRule()
33262
+ ...ptyHostExe && session.attach.enabled ? attachRuleCommands(ptyHostExe) : [],
33263
+ ...forwardRules
33117
33264
  ];
33118
33265
  if (isWin && !dryRun) {
33119
33266
  try {
@@ -33127,20 +33274,33 @@ async function applyNetworkPolicy(ptyHostExe) {
33127
33274
  warn("network.dns could not be applied: " + errMsg(e));
33128
33275
  }
33129
33276
  }
33130
- if (!block) log("Private-range blocking disabled by config.");
33131
- if (block)
33277
+ if (!block2) log("Private-range blocking disabled by config.");
33278
+ if (block2)
33132
33279
  log(
33133
33280
  "Gateway/DNS addresses kept reachable for DNS only: " + (exempt.length ? [...new Set(exempt)].join(", ") : "(none found)")
33134
33281
  );
33135
33282
  if (!isWin || dryRun) {
33136
33283
  log("(dry run) firewall plan:");
33137
33284
  for (const c of commands) log(" netsh " + c.join(" "));
33138
- return block ? "blocked" : "open";
33285
+ return block2 ? "blocked" : "open";
33139
33286
  }
33140
33287
  if (commands.length === 0) return "open";
33141
- const online = block ? await waitForInternet(30) : true;
33142
- run2(commands);
33143
- if (!block) return "open";
33288
+ const online = block2 ? await waitForInternet(30) : true;
33289
+ const failures = [];
33290
+ try {
33291
+ run2(lanRules);
33292
+ } catch (e) {
33293
+ failures.push(errMsg(e));
33294
+ }
33295
+ await applyAttachRule(ptyHostExe);
33296
+ try {
33297
+ run2(forwardRules);
33298
+ } catch (e) {
33299
+ problems.push("forwardPorts: the firewall rule for the relays could not be created (" + errMsg(e) + ")");
33300
+ warn("PROTECTION MISSING: " + problems[problems.length - 1]);
33301
+ }
33302
+ if (failures.length) throw new Error(failures[0]);
33303
+ if (!block2) return "open";
33144
33304
  if (online && !await waitForInternet(15)) {
33145
33305
  run2(lanRules.filter((c) => c[2] === "delete"));
33146
33306
  warn(
@@ -33370,18 +33530,109 @@ function setupGitConfig(env) {
33370
33530
  spawnSync7(git, ["config", "--global", "core.autocrlf", "input"], { env: rest, windowsHide: true, stdio: "ignore" });
33371
33531
  }
33372
33532
 
33533
+ // src/guest/setup/github-token.ts
33534
+ import fs53 from "node:fs";
33535
+ import { spawnSync as spawnSync8 } from "node:child_process";
33536
+
33537
+ // src/shared/github/token.ts
33538
+ var MIN_LENGTH = 20;
33539
+ var MAX_LENGTH = 255;
33540
+ var PRINTABLE = /^[\x21-\x7e]+$/;
33541
+ var GithubTokenSchema = external_exports.string().min(MIN_LENGTH).max(MAX_LENGTH).regex(PRINTABLE);
33542
+ var TOKEN_VARIABLES = /^(GH_TOKEN|GITHUB_TOKEN|GH_ENTERPRISE_TOKEN|GITHUB_ENTERPRISE_TOKEN)$/i;
33543
+ var ghEnv = (base, configDir) => ({
33544
+ ...Object.fromEntries(
33545
+ Object.entries(base).filter((e) => e[1] !== void 0 && !TOKEN_VARIABLES.test(e[0]))
33546
+ ),
33547
+ GH_CONFIG_DIR: configDir,
33548
+ GH_PROMPT_DISABLED: "1",
33549
+ GH_NO_UPDATE_NOTIFIER: "1"
33550
+ });
33551
+ function ghLoginCommand(ghExe, configDir, token, base) {
33552
+ return {
33553
+ file: ghExe,
33554
+ args: [
33555
+ "auth",
33556
+ "login",
33557
+ "--hostname",
33558
+ "github.com",
33559
+ "--git-protocol",
33560
+ "https",
33561
+ "--insecure-storage",
33562
+ "--with-token"
33563
+ ],
33564
+ input: token + "\n",
33565
+ env: ghEnv(base, configDir)
33566
+ };
33567
+ }
33568
+ function ghSetupGitCommand(ghExe, configDir, base) {
33569
+ return { file: ghExe, args: ["auth", "setup-git", "--hostname", "github.com"], env: ghEnv(base, configDir) };
33570
+ }
33571
+ function ghOutputText(text, token) {
33572
+ const clean = token ? text.split(token).join("***") : text;
33573
+ return clean.replace(/\s+/g, " ").trim().slice(0, 300);
33574
+ }
33575
+
33576
+ // src/guest/setup/github-token.ts
33577
+ var GH_TIMEOUT_MS = 6e4;
33578
+ function prepareGithubFolder(token) {
33579
+ const dir = deliveredGhConfigDir();
33580
+ fs53.rmSync(dir, { recursive: true, force: true });
33581
+ if (!token) return false;
33582
+ mkdirp(dir);
33583
+ return true;
33584
+ }
33585
+ function runGh(command, token, what) {
33586
+ const result = spawnSync8(command.file, [...command.args], {
33587
+ env: command.env,
33588
+ encoding: "utf8",
33589
+ windowsHide: true,
33590
+ timeout: GH_TIMEOUT_MS,
33591
+ stdio: ["pipe", "pipe", "pipe"],
33592
+ ...command.input !== void 0 ? { input: command.input } : {}
33593
+ });
33594
+ if (result.error) throw new Error(what + " could not run: " + ghOutputText(result.error.message, token));
33595
+ if (result.status !== 0)
33596
+ throw new Error(
33597
+ what + " failed: " + ghOutputText(String(result.stderr ?? "") || String(result.stdout ?? ""), token)
33598
+ );
33599
+ }
33600
+ function loginGithub(token, env) {
33601
+ if (dryRun) {
33602
+ log("(dry run) would log gh in with the GitHub token from your PC");
33603
+ return;
33604
+ }
33605
+ const gh2 = ghExePath();
33606
+ const dir = deliveredGhConfigDir();
33607
+ if (!exists(gh2) || env.GH_CONFIG_DIR !== dir) {
33608
+ fs53.rmSync(dir, { recursive: true, force: true });
33609
+ log("A GitHub token arrived, but gh is not set up in this sandbox (toolchains.cli.gh), so it is not used.");
33610
+ return;
33611
+ }
33612
+ try {
33613
+ runGh(ghLoginCommand(gh2, dir, token, env), token, "gh auth login");
33614
+ runGh(ghSetupGitCommand(gh2, dir, env), token, "gh auth setup-git");
33615
+ } catch (e) {
33616
+ fs53.rmSync(dir, { recursive: true, force: true });
33617
+ env.GH_CONFIG_DIR = mkdirp(ghConfigDir());
33618
+ addMachineEnvironment({ GH_CONFIG_DIR: env.GH_CONFIG_DIR }, []);
33619
+ throw e;
33620
+ }
33621
+ log("gh and https git to github.com use the GitHub token from your PC (github.shareToken); the agent can read it.");
33622
+ }
33623
+
33373
33624
  // src/guest/sessions/pty-host.ts
33374
33625
  function ensurePtyHost() {
33375
33626
  return ensureCompiled("ptyhost");
33376
33627
  }
33377
33628
 
33378
33629
  // src/guest/toolchains/leftovers.ts
33379
- import fs53 from "node:fs";
33630
+ import fs54 from "node:fs";
33380
33631
  import path66 from "node:path";
33381
33632
  var STAGING = /\.staging-[0-9a-f]+$/i;
33382
33633
  var entries2 = (dir) => {
33383
33634
  try {
33384
- return fs53.readdirSync(dir, { withFileTypes: true });
33635
+ return fs54.readdirSync(dir, { withFileTypes: true });
33385
33636
  } catch {
33386
33637
  return [];
33387
33638
  }
@@ -33398,17 +33649,17 @@ function pruneToolLeftovers() {
33398
33649
 
33399
33650
  // src/guest/audit/process-audit.ts
33400
33651
  import path67 from "node:path";
33401
- import { spawnSync as spawnSync8 } from "node:child_process";
33652
+ import { spawnSync as spawnSync9 } from "node:child_process";
33402
33653
  var PROCESS_CREATION = "{0CCE922B-69AE-11D9-BED3-505054503030}";
33403
33654
  var auditOn = () => Boolean(session.auditProcesses) && isWin && !dryRun;
33404
33655
  function enableProcessAudit() {
33405
33656
  if (!auditOn()) return;
33406
- spawnSync8(
33657
+ spawnSync9(
33407
33658
  path67.join(process.env.WINDIR ?? "C:\\Windows", "System32", "auditpol.exe"),
33408
33659
  ["/set", "/subcategory:" + PROCESS_CREATION, "/success:enable"],
33409
33660
  { windowsHide: true, stdio: "ignore" }
33410
33661
  );
33411
- spawnSync8(
33662
+ spawnSync9(
33412
33663
  SYSTEM.reg(),
33413
33664
  [
33414
33665
  "add",
@@ -33428,7 +33679,8 @@ function enableProcessAudit() {
33428
33679
 
33429
33680
  // src/guest/vpn/vpn.ts
33430
33681
  import dns2 from "node:dns";
33431
- import fs57 from "node:fs";
33682
+ import fs58 from "node:fs";
33683
+ import net3 from "node:net";
33432
33684
  import os14 from "node:os";
33433
33685
  import path71 from "node:path";
33434
33686
 
@@ -33584,22 +33836,22 @@ function directiveOf(line) {
33584
33836
  return { name: parts2[0].toLowerCase(), args: parts2.slice(1) };
33585
33837
  }
33586
33838
  function inspectOvpn(text) {
33587
- const problems = [];
33839
+ const problems2 = [];
33588
33840
  const found = [];
33589
33841
  const refuse = (message) => {
33590
- if (problems.length < 20 && !problems.includes(message)) problems.push(message);
33842
+ if (problems2.length < 20 && !problems2.includes(message)) problems2.push(message);
33591
33843
  };
33592
33844
  if (Buffer.byteLength(text, "utf8") > MAX_OVPN_BYTES) return { ok: false, problems: ["the file is too big"] };
33593
33845
  if (text.includes("\0")) return { ok: false, problems: ["the file is not text"] };
33594
33846
  const lines = text.split(/\r?\n/);
33595
33847
  if (lines.length > MAX_LINES) return { ok: false, problems: ["the file has too many lines"] };
33596
- let block = null;
33848
+ let block2 = null;
33597
33849
  let defaultProto = "udp";
33598
33850
  lines.forEach((line, i) => {
33599
33851
  const at = "line " + (i + 1) + ": ";
33600
33852
  const trimmed = line.trim();
33601
- if (block) {
33602
- if (trimmed.toLowerCase() === "</" + block + ">") block = null;
33853
+ if (block2) {
33854
+ if (trimmed.toLowerCase() === "</" + block2 + ">") block2 = null;
33603
33855
  else if (/^<\/?[A-Za-z-]+>$/.test(trimmed)) refuse(at + "a section inside a section");
33604
33856
  return;
33605
33857
  }
@@ -33607,7 +33859,7 @@ function inspectOvpn(text) {
33607
33859
  if (open2) {
33608
33860
  const tag = open2[1].toLowerCase();
33609
33861
  if (!BLOCKS.has(tag)) refuse(at + "<" + tag + "> is not allowed");
33610
- block = tag;
33862
+ block2 = tag;
33611
33863
  return;
33612
33864
  }
33613
33865
  const d = directiveOf(line);
@@ -33632,10 +33884,10 @@ function inspectOvpn(text) {
33632
33884
  }
33633
33885
  });
33634
33886
  const remotes = found.map((r) => ({ host: r.host, port: r.port, proto: r.proto ?? defaultProto }));
33635
- if (block) refuse("a <" + block + "> section is never closed");
33887
+ if (block2) refuse("a <" + block2 + "> section is never closed");
33636
33888
  if (remotes.length === 0) refuse("it names no remote server");
33637
33889
  if (remotes.length > 64) refuse("it names too many servers");
33638
- return problems.length ? { ok: false, problems } : { ok: true, remotes };
33890
+ return problems2.length ? { ok: false, problems: problems2 } : { ok: true, remotes };
33639
33891
  }
33640
33892
  function assertSafeOvpn(text, what) {
33641
33893
  const report = inspectOvpn(text);
@@ -33672,9 +33924,9 @@ var inRange = (text, low, high) => {
33672
33924
  };
33673
33925
  var splitList = (text) => text.split(",").map((s) => s.trim()).filter(Boolean);
33674
33926
  function inspectWireguard(text) {
33675
- const problems = [];
33927
+ const problems2 = [];
33676
33928
  const refuse = (message) => {
33677
- if (problems.length < 20 && !problems.includes(message)) problems.push(message);
33929
+ if (problems2.length < 20 && !problems2.includes(message)) problems2.push(message);
33678
33930
  };
33679
33931
  if (Buffer.byteLength(text, "utf8") > MAX_WIREGUARD_BYTES) return { ok: false, problems: ["the file is too big"] };
33680
33932
  if (text.includes("\0")) return { ok: false, problems: ["the file is not text"] };
@@ -33716,7 +33968,7 @@ function inspectWireguard(text) {
33716
33968
  if (interfaces.length !== 1) refuse("it needs exactly one [Interface] section");
33717
33969
  if (peers.length === 0) refuse("it has no [Peer] section");
33718
33970
  if (peers.length > MAX_PEERS) refuse("it has too many [Peer] sections");
33719
- if (problems.length) return { ok: false, problems };
33971
+ if (problems2.length) return { ok: false, problems: problems2 };
33720
33972
  const face = interfaces[0];
33721
33973
  const privateKey = one(face, "privatekey");
33722
33974
  if (!privateKey) refuse("[Interface] has no PrivateKey");
@@ -33763,7 +34015,7 @@ function inspectWireguard(text) {
33763
34015
  ...keepalive !== void 0 ? { keepalive } : {}
33764
34016
  });
33765
34017
  });
33766
- if (problems.length || !privateKey) return { ok: false, problems };
34018
+ if (problems2.length || !privateKey) return { ok: false, problems: problems2 };
33767
34019
  return {
33768
34020
  ok: true,
33769
34021
  config: {
@@ -33841,8 +34093,22 @@ var drop = (name) => [
33841
34093
  "rule",
33842
34094
  "name=" + VPN_RULE_PREFIX + name
33843
34095
  ];
34096
+ var block = (name, extra) => [
34097
+ "advfirewall",
34098
+ "firewall",
34099
+ "add",
34100
+ "rule",
34101
+ "name=" + VPN_RULE_PREFIX + name,
34102
+ "dir=out",
34103
+ "action=block",
34104
+ "enable=yes",
34105
+ "profile=any",
34106
+ ...extra
34107
+ ];
34108
+ var BLOCK_RULES = ["Block-Internet", "Block-IPv6", "Block-DNS-UDP", "Block-DNS-TCP"];
33844
34109
  function ruleNames(serverCount) {
33845
34110
  return [
34111
+ ...BLOCK_RULES,
33846
34112
  "Loopback",
33847
34113
  "DHCP",
33848
34114
  "Host",
@@ -33850,6 +34116,39 @@ function ruleNames(serverCount) {
33850
34116
  ...Array.from({ length: Math.max(serverCount, 0) }, (_v, i) => "Server-" + (i + 1))
33851
34117
  ];
33852
34118
  }
34119
+ var NOT_INTERNET = [
34120
+ ["0.0.0.0", "0.255.255.255"],
34121
+ ["10.0.0.0", "10.255.255.255"],
34122
+ ["100.64.0.0", "100.127.255.255"],
34123
+ ["127.0.0.0", "127.255.255.255"],
34124
+ ["169.254.0.0", "169.254.255.255"],
34125
+ ["172.16.0.0", "172.31.255.255"],
34126
+ ["192.168.0.0", "192.168.255.255"],
34127
+ ["224.0.0.0", "255.255.255.255"]
34128
+ ];
34129
+ var LAST_IPV4 = 4294967295;
34130
+ function ipNumber(ip) {
34131
+ return ip.split(".").reduce((n, part) => n * 256 + Number(part), 0);
34132
+ }
34133
+ function ipText(n) {
34134
+ return [24, 16, 8, 0].map((shift) => Math.floor(n / 2 ** shift) % 256).join(".");
34135
+ }
34136
+ function ipv4Except(excluded) {
34137
+ const sorted = [...excluded].sort((a, b) => a[0] - b[0]);
34138
+ const out = [];
34139
+ let next = 0;
34140
+ for (const [from, to] of sorted) {
34141
+ if (from > next) out.push(ipText(next) + "-" + ipText(from - 1));
34142
+ next = Math.max(next, to + 1);
34143
+ }
34144
+ if (next <= LAST_IPV4) out.push(ipText(next) + "-" + ipText(LAST_IPV4));
34145
+ return out.join(",");
34146
+ }
34147
+ function internetExcept(servers) {
34148
+ const ranges = NOT_INTERNET.map(([a, b]) => [ipNumber(a), ipNumber(b)]);
34149
+ for (const s of cleanServers(servers)) ranges.push([ipNumber(s.ip), ipNumber(s.ip)]);
34150
+ return ipv4Except(ranges);
34151
+ }
33853
34152
  function cleanServers(servers) {
33854
34153
  const seen = /* @__PURE__ */ new Set();
33855
34154
  return servers.filter((s) => {
@@ -33864,6 +34163,10 @@ function killSwitchCommands(servers, ptyHostExe) {
33864
34163
  return [
33865
34164
  ...ruleNames(Math.max(list.length, 8)).map(drop),
33866
34165
  ["advfirewall", "set", "allprofiles", "state", "on"],
34166
+ block("Block-Internet", ["protocol=any", "remoteip=" + internetExcept(list)]),
34167
+ block("Block-IPv6", ["protocol=any", "remoteip=2000::/3"]),
34168
+ block("Block-DNS-UDP", ["protocol=udp", "remoteport=53"]),
34169
+ block("Block-DNS-TCP", ["protocol=tcp", "remoteport=53"]),
33867
34170
  ["advfirewall", "set", "allprofiles", "firewallpolicy", "blockinbound,blockoutbound"],
33868
34171
  rule("Loopback", ["protocol=any", "remoteip=127.0.0.0/8"]),
33869
34172
  rule("DHCP", ["protocol=udp", "localport=68", "remoteport=67"]),
@@ -33875,7 +34178,21 @@ function killSwitchCommands(servers, ptyHostExe) {
33875
34178
  }
33876
34179
  function tunnelCommands(tunnelIp) {
33877
34180
  if (!isIPv4(tunnelIp)) throw new Error("Not an IPv4 address: " + tunnelIp);
33878
- return [drop("Tunnel"), rule("Tunnel", ["protocol=any", "localip=" + tunnelIp])];
34181
+ const n = ipNumber(tunnelIp);
34182
+ const notTunnel = ipv4Except([[n, n]]);
34183
+ return [
34184
+ ...["Block-Internet", "Block-DNS-UDP", "Block-DNS-TCP"].map((name) => [
34185
+ "advfirewall",
34186
+ "firewall",
34187
+ "set",
34188
+ "rule",
34189
+ "name=" + VPN_RULE_PREFIX + name,
34190
+ "new",
34191
+ "localip=" + notTunnel
34192
+ ]),
34193
+ drop("Tunnel"),
34194
+ rule("Tunnel", ["protocol=any", "localip=" + tunnelIp])
34195
+ ];
33879
34196
  }
33880
34197
  function tunnelAddressFromLog(log2) {
33881
34198
  const add = /net_addr_v4_add:\s*(\d{1,3}(?:\.\d{1,3}){3})\/\d+/.exec(log2);
@@ -33888,7 +34205,7 @@ function tunnelAddressFromLog(log2) {
33888
34205
 
33889
34206
  // src/guest/vpn/openvpn.ts
33890
34207
  import crypto4 from "node:crypto";
33891
- import fs54 from "node:fs";
34208
+ import fs55 from "node:fs";
33892
34209
  import path68 from "node:path";
33893
34210
 
33894
34211
  // src/shared/vpn/download.ts
@@ -33925,7 +34242,12 @@ function openvpnDownload(arch2 = process.arch) {
33925
34242
  const entry = arch2 === "arm64" ? OPENVPN_MSI.arm64 : OPENVPN_MSI.x64;
33926
34243
  return { ...entry, url: OPENVPN_MSI.base + entry.file };
33927
34244
  }
33928
- var hasHash = (file3, sha256) => crypto4.createHash("sha256").update(fs54.readFileSync(file3)).digest("hex") === sha256;
34245
+ function repairServiceQueries() {
34246
+ const did = scriptOutput("repair-wmi-services.ps1");
34247
+ if (did === "removed") log("VPN: repaired the Windows service queries installers use (a Windows Sandbox gap).");
34248
+ else if (did !== "absent" && did !== "kept") warn("VPN: could not check the Windows service queries installers use.");
34249
+ }
34250
+ var hasHash = (file3, sha256) => crypto4.createHash("sha256").update(fs55.readFileSync(file3)).digest("hex") === sha256;
33929
34251
  async function ensureOpenVpn() {
33930
34252
  const exe = openvpnExe();
33931
34253
  if (!isWin || dryRun) return exe;
@@ -33936,6 +34258,7 @@ async function ensureOpenVpn() {
33936
34258
  log("VPN: downloading OpenVPN " + OPENVPN_MSI.version + " (about 6 MB)");
33937
34259
  await downloader.file(dl.url, msi, dl.sha256);
33938
34260
  }
34261
+ repairServiceQueries();
33939
34262
  log("VPN: installing OpenVPN");
33940
34263
  const logFile2 = path68.join(toolsDir, "downloads", "openvpn-install.log");
33941
34264
  const code = await spawnAsync(SYSTEM.msiexec(), ["/i", msi, "/qn", "/norestart", "/l*v", logFile2], {
@@ -33949,7 +34272,7 @@ async function ensureOpenVpn() {
33949
34272
 
33950
34273
  // src/guest/vpn/tunnel.ts
33951
34274
  import { spawn as spawn7 } from "node:child_process";
33952
- import fs55 from "node:fs";
34275
+ import fs56 from "node:fs";
33953
34276
  import os12 from "node:os";
33954
34277
  import path69 from "node:path";
33955
34278
  var DONE = "Initialization Sequence Completed";
@@ -33967,17 +34290,17 @@ function failureReason(text) {
33967
34290
  }
33968
34291
  var tail = (text, lines = 12) => text.trim().split(/\r?\n/).slice(-lines).join("\n");
33969
34292
  async function startTunnel(o) {
33970
- const dir = fs55.mkdtempSync(path69.join(os12.tmpdir(), "sbx-vpn-"));
34293
+ const dir = fs56.mkdtempSync(path69.join(os12.tmpdir(), "sbx-vpn-"));
33971
34294
  const auth = path69.join(dir, "login.txt");
33972
34295
  const logFile2 = path69.join(dir, "openvpn.log");
33973
34296
  const keepLog = () => {
33974
34297
  try {
33975
- fs55.mkdirSync(logsDir, { recursive: true });
33976
- fs55.copyFileSync(logFile2, path69.join(logsDir, "openvpn.log"));
34298
+ fs56.mkdirSync(logsDir, { recursive: true });
34299
+ fs56.copyFileSync(logFile2, path69.join(logsDir, "openvpn.log"));
33977
34300
  } catch {
33978
34301
  }
33979
34302
  };
33980
- fs55.writeFileSync(auth, o.username + "\r\n" + o.password + "\r\n", "utf8");
34303
+ fs56.writeFileSync(auth, o.username + "\r\n" + o.password + "\r\n", "utf8");
33981
34304
  try {
33982
34305
  const child = spawn7(
33983
34306
  o.exe,
@@ -33995,7 +34318,7 @@ async function startTunnel(o) {
33995
34318
  child.unref();
33996
34319
  const deadline = Date.now() + (o.timeoutMs ?? 9e4);
33997
34320
  for (; ; ) {
33998
- const text = fs55.existsSync(logFile2) ? fs55.readFileSync(logFile2, "utf8") : "";
34321
+ const text = fs56.existsSync(logFile2) ? fs56.readFileSync(logFile2, "utf8") : "";
33999
34322
  if (text.includes(DONE)) {
34000
34323
  keepLog();
34001
34324
  const tunnelIp = tunnelAddressFromLog(text) ?? void 0;
@@ -34015,14 +34338,14 @@ async function startTunnel(o) {
34015
34338
  await new Promise((r) => setTimeout(r, 500));
34016
34339
  }
34017
34340
  } finally {
34018
- fs55.rmSync(auth, { force: true });
34341
+ fs56.rmSync(auth, { force: true });
34019
34342
  }
34020
34343
  }
34021
34344
 
34022
34345
  // src/guest/vpn/wireguard.ts
34023
34346
  import { execFile } from "node:child_process";
34024
34347
  import crypto5 from "node:crypto";
34025
- import fs56 from "node:fs";
34348
+ import fs57 from "node:fs";
34026
34349
  import os13 from "node:os";
34027
34350
  import path70 from "node:path";
34028
34351
  var WIREGUARD_TUNNEL = "sandbox";
@@ -34036,7 +34359,7 @@ function wireguardDownload(arch2 = process.arch) {
34036
34359
  const entry = arch2 === "arm64" ? WIREGUARD_MSI.arm64 : WIREGUARD_MSI.x64;
34037
34360
  return { ...entry, url: WIREGUARD_MSI.base + entry.file };
34038
34361
  }
34039
- var hasHash2 = (file3, sha256) => crypto5.createHash("sha256").update(fs56.readFileSync(file3)).digest("hex") === sha256;
34362
+ var hasHash2 = (file3, sha256) => crypto5.createHash("sha256").update(fs57.readFileSync(file3)).digest("hex") === sha256;
34040
34363
  async function ensureWireguard() {
34041
34364
  const exe = wireguardExe();
34042
34365
  if (!isWin || dryRun) return exe;
@@ -34047,6 +34370,7 @@ async function ensureWireguard() {
34047
34370
  log("VPN: downloading WireGuard " + WIREGUARD_MSI.version + " (about 3 MB)");
34048
34371
  await downloader.file(dl.url, msi, dl.sha256);
34049
34372
  }
34373
+ repairServiceQueries();
34050
34374
  log("VPN: installing WireGuard");
34051
34375
  const logFile2 = path70.join(toolsDir, "downloads", "wireguard-install.log");
34052
34376
  const code = await spawnAsync(
@@ -34081,9 +34405,9 @@ async function removeWireguardTunnel(exe) {
34081
34405
  }
34082
34406
  async function startWireguardTunnel(o) {
34083
34407
  const root = path70.join(process.env.SystemRoot ?? "C:\\Windows", "Temp");
34084
- const dir = fs56.mkdtempSync(path70.join(fs56.existsSync(root) ? root : os13.tmpdir(), "sbx-wg-"));
34408
+ const dir = fs57.mkdtempSync(path70.join(fs57.existsSync(root) ? root : os13.tmpdir(), "sbx-wg-"));
34085
34409
  const file3 = path70.join(dir, WIREGUARD_TUNNEL + ".conf");
34086
- fs56.writeFileSync(file3, o.config, "utf8");
34410
+ fs57.writeFileSync(file3, o.config, "utf8");
34087
34411
  const fail = async (message) => {
34088
34412
  await removeWireguardTunnel(o.exe);
34089
34413
  return { ok: false, message };
@@ -34104,7 +34428,7 @@ async function startWireguardTunnel(o) {
34104
34428
  await sleep(500);
34105
34429
  }
34106
34430
  } finally {
34107
- fs56.rmSync(dir, { recursive: true, force: true });
34431
+ fs57.rmSync(dir, { recursive: true, force: true });
34108
34432
  }
34109
34433
  }
34110
34434
  async function waitForHandshake(exe, poke, timeoutMs = 3e4) {
@@ -34173,15 +34497,47 @@ async function publicAddress() {
34173
34497
  }
34174
34498
  }
34175
34499
  var hasAdapter2 = (ip) => Object.values(os14.networkInterfaces()).some((list) => (list ?? []).some((i) => i.address === ip));
34500
+ var PROBE_TARGETS = ["1.1.1.1", "8.8.8.8", "9.9.9.9"];
34501
+ var PROBE_TIMEOUT_MS = 3e3;
34502
+ function connects(ip, port) {
34503
+ return new Promise((resolve2) => {
34504
+ const socket = net3.connect({ host: ip, port });
34505
+ const done = (ok) => {
34506
+ socket.destroy();
34507
+ resolve2(ok);
34508
+ };
34509
+ socket.setTimeout(PROBE_TIMEOUT_MS, () => done(false));
34510
+ socket.once("connect", () => done(true));
34511
+ socket.once("error", () => done(false));
34512
+ });
34513
+ }
34514
+ async function directConnectionBlocked(servers) {
34515
+ const targets = PROBE_TARGETS.filter((ip) => !servers.some((s) => s.ip === ip));
34516
+ const results = await Promise.all(targets.map((ip) => connects(ip, 443)));
34517
+ return !results.some(Boolean);
34518
+ }
34176
34519
  async function setupVpn(secrets, ptyHostExe) {
34177
34520
  const vpn = session.vpn;
34178
34521
  if (!vpn) return true;
34179
34522
  const killSwitch = vpn.killSwitch;
34180
34523
  let blocked = false;
34181
- const block = (servers) => {
34182
- if (isWin && !dryRun) run2(killSwitchCommands(servers, ptyHostExe));
34183
- blocked = true;
34184
- log("VPN: kill switch on: nothing leaves the sandbox except through the tunnel.");
34524
+ let held = false;
34525
+ const block2 = async (servers) => {
34526
+ if (!isWin || dryRun) {
34527
+ blocked = held = true;
34528
+ return;
34529
+ }
34530
+ try {
34531
+ run2(killSwitchCommands(servers, ptyHostExe ? longPath(ptyHostExe) : null));
34532
+ } finally {
34533
+ blocked = true;
34534
+ held = await directConnectionBlocked(servers);
34535
+ }
34536
+ if (held) log("VPN: kill switch on: nothing leaves the sandbox except through the tunnel (checked).");
34537
+ else
34538
+ warn(
34539
+ "VPN: the kill switch did not hold: a direct connection still got out. If the tunnel drops, traffic leaves through your normal connection."
34540
+ );
34185
34541
  };
34186
34542
  setVpnStatus("starting", "Starting the VPN");
34187
34543
  try {
@@ -34197,7 +34553,7 @@ async function setupVpn(secrets, ptyHostExe) {
34197
34553
  warn(
34198
34554
  "VPN: AllowedIPs does not include 0.0.0.0/0: with the kill switch on, only the tunnel traffic can leave the sandbox."
34199
34555
  );
34200
- if (killSwitch) block(servers);
34556
+ if (killSwitch) await block2(servers);
34201
34557
  if (dryRun || !isWin) {
34202
34558
  log("(dry run) would start the WireGuard tunnel " + vpn.server);
34203
34559
  setVpnStatus("off", "dry run");
@@ -34212,11 +34568,11 @@ async function setupVpn(secrets, ptyHostExe) {
34212
34568
  const credentials = secrets.vpn;
34213
34569
  if (!credentials) throw new Error("the VPN login did not arrive (start the sandbox again)");
34214
34570
  const profile = path71.join(launchDir, "vpn", "server.ovpn");
34215
- const remotes = assertSafeOvpn(fs57.readFileSync(profile, "utf8"), vpn.server);
34571
+ const remotes = assertSafeOvpn(fs58.readFileSync(profile, "utf8"), vpn.server);
34216
34572
  const exe = await ensureOpenVpn();
34217
34573
  const servers = await serverAddresses(remotes);
34218
34574
  if (servers.length === 0) throw new Error("the VPN server addresses could not be found");
34219
- if (killSwitch) block(servers);
34575
+ if (killSwitch) await block2(servers);
34220
34576
  if (dryRun || !isWin) {
34221
34577
  log("(dry run) would start the VPN tunnel to " + vpn.server);
34222
34578
  setVpnStatus("off", "dry run");
@@ -34235,18 +34591,21 @@ async function setupVpn(secrets, ptyHostExe) {
34235
34591
  throw new Error("the tunnel reported it was up, but no network adapter with its address appeared");
34236
34592
  const publicIp = await publicAddress();
34237
34593
  log("VPN: up" + (publicIp ? " (the sandbox appears as " + publicIp + ")" : "") + ".");
34238
- setVpnStatus("up", "The tunnel is up", publicIp ? { publicIp } : {});
34594
+ setVpnStatus("up", "The tunnel is up", {
34595
+ ...publicIp ? { publicIp } : {},
34596
+ ...killSwitch ? { killSwitchHeld: held } : {}
34597
+ });
34239
34598
  return true;
34240
34599
  } catch (e) {
34241
34600
  const reason = errMsg(e);
34242
34601
  try {
34243
- if (killSwitch && !blocked) block([]);
34602
+ if (killSwitch && !blocked) await block2([]);
34244
34603
  } catch (blockError) {
34245
34604
  warn("VPN: could not close the network after the failure: " + errMsg(blockError));
34246
34605
  }
34247
- const text = "VPN failed: " + reason + (killSwitch ? " The kill switch is on, so this sandbox has NO internet access." : " The kill switch is off, so this sandbox is using your normal connection.");
34248
- warn(text);
34249
- setVpnStatus("failed", reason);
34606
+ const after = !killSwitch ? " The kill switch is off, so this sandbox is using your normal connection." : held ? " The kill switch is on, so this sandbox has NO internet access." : " The kill switch could NOT close the network, so this sandbox is ONLINE through your normal connection, without the VPN.";
34607
+ warn("VPN failed: " + reason + after);
34608
+ setVpnStatus("failed", reason, killSwitch ? { killSwitchHeld: held } : {});
34250
34609
  return false;
34251
34610
  }
34252
34611
  }
@@ -34303,10 +34662,10 @@ var FrameReader = class {
34303
34662
  };
34304
34663
 
34305
34664
  // src/shared/pty/client.ts
34306
- import net3 from "node:net";
34665
+ import net4 from "node:net";
34307
34666
  var connectOnce = (host, port, timeoutMs) => {
34308
34667
  return new Promise((resolve2, reject) => {
34309
- const socket = net3.connect({ host, port });
34668
+ const socket = net4.connect({ host, port });
34310
34669
  const timer = setTimeout(() => {
34311
34670
  socket.destroy();
34312
34671
  reject(new Error("timed out connecting to " + host + ":" + port));
@@ -34527,6 +34886,36 @@ function unfinishedSequence(data) {
34527
34886
  return data.length;
34528
34887
  }
34529
34888
 
34889
+ // src/shared/pty/ctrl-c.ts
34890
+ var CTRL_C_CONFIRM_MS = 1500;
34891
+ var CTRL_C_HINT = "Ctrl+C again to send it to the program (Esc stops an agent)";
34892
+ var OSC_TITLE = /\x1b\][02];([^\x07\x1b]{0,256})(?:\x07|\x1b\\)/g;
34893
+ var CtrlCGuard = class {
34894
+ armedAt = Number.NEGATIVE_INFINITY;
34895
+ lastTitle = "";
34896
+ /** What to send for one read from the keyboard: a lone Ctrl+C only when another came just before. */
34897
+ push(chunk, nowMs) {
34898
+ if (chunk.length !== 1 || chunk[0] !== 3) return { send: chunk, held: false };
34899
+ if (nowMs - this.armedAt <= CTRL_C_CONFIRM_MS) {
34900
+ this.armedAt = Number.NEGATIVE_INFINITY;
34901
+ return { send: chunk, held: false };
34902
+ }
34903
+ this.armedAt = nowMs;
34904
+ return { send: Buffer.alloc(0), held: true };
34905
+ }
34906
+ /** Remembers the title the program last set (from its output), to put it back after the hint. */
34907
+ watchOutput(text) {
34908
+ for (const m of text.matchAll(OSC_TITLE)) this.lastTitle = m[1];
34909
+ }
34910
+ /** The escape sequence that shows the hint, and the one that puts the program's title back. */
34911
+ hintTitle() {
34912
+ return "\x1B]0;" + CTRL_C_HINT + "\x07";
34913
+ }
34914
+ restoreTitle() {
34915
+ return "\x1B]0;" + this.lastTitle.replace(/[\x00-\x1f\x7f]/g, "") + "\x07";
34916
+ }
34917
+ };
34918
+
34530
34919
  // src/shared/pty/terminal.ts
34531
34920
  var MOUSE_OFF = "\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1005l\x1B[?1006l\x1B[?1015l";
34532
34921
  function fixWindowsKeys(chunk) {
@@ -34557,6 +34946,7 @@ function runTerminalClient(socket, token, options = {}) {
34557
34946
  clearInterval(heartbeat);
34558
34947
  clearInterval(sizeWatch);
34559
34948
  clearTimeout(joinTimer);
34949
+ clearTimeout(hintTimer);
34560
34950
  socket.destroy();
34561
34951
  stdout.write("\x1B[0m\x1B[?25h" + MOUSE_OFF + "\r\n");
34562
34952
  void (vtInput?.stop() ?? Promise.resolve()).then(() => {
@@ -34570,11 +34960,23 @@ function runTerminalClient(socket, token, options = {}) {
34570
34960
  const sendInput = (data) => {
34571
34961
  if (data.length && !done) socket.write(encodeFrame(FrameType.Input, swapKeys ? fixWindowsKeys(data) : data));
34572
34962
  };
34963
+ const guard = options.confirmCtrlC ? new CtrlCGuard() : null;
34964
+ let hintTimer;
34573
34965
  const onInput = (chunk) => {
34574
34966
  if (options.detachKey !== void 0 && chunk.includes(options.detachKey)) {
34575
34967
  finish({ reason: "detach" });
34576
34968
  return;
34577
34969
  }
34970
+ if (guard) {
34971
+ const decided = guard.push(chunk, Date.now());
34972
+ clearTimeout(hintTimer);
34973
+ if (decided.held) {
34974
+ stdout.write(guard.hintTitle());
34975
+ hintTimer = setTimeout(() => stdout.write(guard.restoreTitle()), CTRL_C_CONFIRM_MS);
34976
+ return;
34977
+ }
34978
+ if (decided.send.length === 1 && decided.send[0] === 3) stdout.write(guard.restoreTitle());
34979
+ }
34578
34980
  clearTimeout(joinTimer);
34579
34981
  sendInput(joiner.push(chunk));
34580
34982
  if (joiner.holding()) joinTimer = setTimeout(() => sendInput(joiner.flush()), JOIN_MS);
@@ -34607,8 +35009,11 @@ function runTerminalClient(socket, token, options = {}) {
34607
35009
  return;
34608
35010
  }
34609
35011
  for (const frame of frames) {
34610
- if (frame.type === FrameType.Output) stdout.write(filter.push(frame.payload));
34611
- else if (frame.type === FrameType.Console) passthrough = frame.payload.toString("latin1") === "conpty.dll";
35012
+ if (frame.type === FrameType.Output) {
35013
+ const out = filter.push(frame.payload);
35014
+ guard?.watchOutput(out.toString("latin1"));
35015
+ stdout.write(out);
35016
+ } else if (frame.type === FrameType.Console) passthrough = frame.payload.toString("latin1") === "conpty.dll";
34612
35017
  else if (frame.type === FrameType.Restarting) stdout.write(MOUSE_OFF + "\x1B[2J\x1B[3J\x1B[H");
34613
35018
  else if (frame.type === FrameType.Exit)
34614
35019
  finish({ reason: "exit", exitCode: frame.payload.length >= 4 ? frame.payload.readInt32LE(0) : 1 });
@@ -34661,7 +35066,10 @@ async function viewSession(id) {
34661
35066
  return 1;
34662
35067
  }
34663
35068
  const socket = await connect({ ...session2.attach, addresses: ["127.0.0.1"] });
34664
- const result = await runTerminalClient(socket, session2.attach.token, { clipboardWrites: true });
35069
+ const result = await runTerminalClient(socket, session2.attach.token, {
35070
+ clipboardWrites: true,
35071
+ confirmCtrlC: session.attach.confirmCtrlC !== false
35072
+ });
34665
35073
  if (result.reason === "exit") console.log(session2.harness + " exited with code " + result.exitCode + ".");
34666
35074
  else if (result.reason === "error") console.log("Session error: " + result.message);
34667
35075
  return result.reason === "exit" ? result.exitCode : 1;
@@ -34716,7 +35124,7 @@ function saveProjectStatus(name, workspace2, phase, message, databases, summary)
34716
35124
  }
34717
35125
 
34718
35126
  // src/guest/runtime/secrets.ts
34719
- import fs58 from "node:fs";
35127
+ import fs59 from "node:fs";
34720
35128
 
34721
35129
  // src/shared/channel/secrets.ts
34722
35130
  var Environment = external_exports.record(external_exports.string().max(256), external_exports.string().max(65536));
@@ -34732,7 +35140,9 @@ var SecretsFileSchema = external_exports.strictObject({
34732
35140
  /** VPN service credentials (vpn.provider's login), only for the launch's own sandbox. */
34733
35141
  vpn: external_exports.strictObject({ username: external_exports.string().min(1).max(256), password: external_exports.string().min(1).max(1024) }).optional(),
34734
35142
  /** The checked WireGuard config of vpn.provider wireguard-file (holds the private key). */
34735
- wireguardConfig: external_exports.string().min(1).max(32 * 1024).optional()
35143
+ wireguardConfig: external_exports.string().min(1).max(32 * 1024).optional(),
35144
+ /** The GitHub token you saved for sandboxes (github.shareToken), only with git access "full". */
35145
+ githubToken: GithubTokenSchema.optional()
34736
35146
  });
34737
35147
 
34738
35148
  // src/guest/runtime/secrets.ts
@@ -34742,12 +35152,12 @@ function takeSecrets(id) {
34742
35152
  const file3 = run.secretFile(id);
34743
35153
  let text;
34744
35154
  try {
34745
- if (fs58.statSync(file3).size > MAX_BYTES) throw new Error("too big");
34746
- text = fs58.readFileSync(file3, "utf8").replace(/^\uFEFF/, "");
35155
+ if (fs59.statSync(file3).size > MAX_BYTES) throw new Error("too big");
35156
+ text = fs59.readFileSync(file3, "utf8").replace(/^\uFEFF/, "");
34747
35157
  } catch {
34748
35158
  return {};
34749
35159
  } finally {
34750
- fs58.rmSync(file3, { force: true });
35160
+ fs59.rmSync(file3, { force: true });
34751
35161
  }
34752
35162
  try {
34753
35163
  const parsed = SecretsFileSchema.safeParse(JSON.parse(text));
@@ -34800,7 +35210,7 @@ function startDaemon(env, secrets) {
34800
35210
  function writeDbCommand() {
34801
35211
  const dir = mkdirp(path73.join(localDir, "bin"));
34802
35212
  const db = path73.join(path73.dirname(process.argv[1]), "db.js");
34803
- fs59.writeFileSync(
35213
+ fs60.writeFileSync(
34804
35214
  path73.join(dir, "sbx-db.cmd"),
34805
35215
  '@setlocal\r\n@set AGENT_LOG_PREFIX=db\r\n@"' + process.execPath + '" "' + db + '" %*\r\n'
34806
35216
  );
@@ -34810,8 +35220,8 @@ function writeDbCommand() {
34810
35220
  function writeAgentCommands(dir) {
34811
35221
  const install = { node: process.execPath, script: path73.join(path73.dirname(process.argv[1]), "agent.js") };
34812
35222
  for (const shim3 of agentShims(toolsDir, session.agents ?? {}))
34813
- fs59.writeFileSync(path73.join(dir, shim3.command + ".cmd"), shimText(shim3, install));
34814
- fs59.writeFileSync(path73.join(dir, "sbx-agent.cmd"), sbxAgentText(install));
35223
+ fs60.writeFileSync(path73.join(dir, shim3.command + ".cmd"), shimText(shim3, install));
35224
+ fs60.writeFileSync(path73.join(dir, "sbx-agent.cmd"), sbxAgentText(install));
34815
35225
  }
34816
35226
  async function waitForPrimary() {
34817
35227
  for (let i = 0; i < PRIMARY_POLLS; i++) {
@@ -34839,12 +35249,13 @@ var Boot = class {
34839
35249
  log(
34840
35250
  "Agent sandbox: " + session.project + " | workspace " + workspace + " | agent " + session.harnessName + " | log " + logPath
34841
35251
  );
34842
- if (!fs59.statSync(workspace, { throwIfNoEntry: false })?.isDirectory())
35252
+ if (!fs60.statSync(workspace, { throwIfNoEntry: false })?.isDirectory())
34843
35253
  throw new Error("Workspace folder is missing in the sandbox: " + workspace);
34844
35254
  mkdirp(toolsDir);
34845
35255
  mkdirp(run.root);
34846
35256
  this.secrets = takeSecrets(session.launchId);
34847
35257
  applyBootEnvironment(this.secrets.harnessEnvironment);
35258
+ prepareGithubFolder(this.secrets.githubToken);
34848
35259
  await this.networkAndSessionHost();
34849
35260
  await this.hooksAndDesktop();
34850
35261
  const { tools, env } = await this.developerTools();
@@ -34883,6 +35294,7 @@ var Boot = class {
34883
35294
  databases: session.databases ?? [],
34884
35295
  apps: session.apps
34885
35296
  });
35297
+ for (const p of networkProblems()) tools.summary.push("PROTECTION MISSING: " + p);
34886
35298
  if (!dryRun) tools.prepend.push(writeDbCommand());
34887
35299
  Object.assign(tools.vars, proxyEnv());
34888
35300
  await soft("Network: allow only some hosts", async () => {
@@ -34926,6 +35338,11 @@ var Boot = class {
34926
35338
  async gitIdentity(env) {
34927
35339
  step(5, "Git identity");
34928
35340
  await soft("Git config", () => setupGitConfig(env));
35341
+ const token = this.secrets.githubToken;
35342
+ if (token) {
35343
+ this.secrets = { ...this.secrets, githubToken: void 0 };
35344
+ await soft("GitHub token", () => loginGithub(token, env));
35345
+ }
34929
35346
  }
34930
35347
  async claude(tools) {
34931
35348
  step(6, "Claude settings, history and claude-mem");
@@ -34987,7 +35404,7 @@ runBoot().catch((e) => {
34987
35404
  const message = errMsg(e) + (e instanceof Error && e.stack ? "\n" + e.stack : "");
34988
35405
  try {
34989
35406
  setSandboxStatus("failed", errMsg(e));
34990
- fs60.writeFileSync(path74.join(logsDir, "guest-error.txt"), message);
35407
+ fs61.writeFileSync(path74.join(logsDir, "guest-error.txt"), message);
34991
35408
  } catch {
34992
35409
  }
34993
35410
  console.error(