windows-development-sandbox 1.7.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/config.example.json +12 -1
  2. package/dist/app/context.d.ts +8 -0
  3. package/dist/app/context.js +12 -0
  4. package/dist/app/features/defs.d.ts +322 -0
  5. package/dist/app/features/defs.js +28 -0
  6. package/dist/app/features/sandbox/index.d.ts +2 -0
  7. package/dist/app/features/sandbox/index.js +3 -0
  8. package/dist/app/features/sandbox/sandbox.def.d.ts +193 -0
  9. package/dist/app/features/sandbox/sandbox.def.js +245 -0
  10. package/dist/app/features/sandbox/sandbox.run.d.ts +72 -0
  11. package/dist/app/features/sandbox/sandbox.run.js +98 -0
  12. package/dist/app/features/sessions/index.d.ts +2 -0
  13. package/dist/app/features/sessions/index.js +3 -0
  14. package/dist/app/features/sessions/sessions.def.d.ts +141 -0
  15. package/dist/app/features/sessions/sessions.def.js +176 -0
  16. package/dist/app/features/sessions/sessions.run.d.ts +39 -0
  17. package/dist/app/features/sessions/sessions.run.js +58 -0
  18. package/dist/{cli/menu/settings/categories.d.ts → app/features/settings/categories.def.d.ts} +2 -0
  19. package/dist/{cli/menu/settings/categories.js → app/features/settings/categories.def.js} +24 -5
  20. package/dist/app/features/settings/index.d.ts +5 -0
  21. package/dist/app/features/settings/index.js +6 -0
  22. package/dist/{cli/menu/settings/config-file.d.ts → app/features/settings/settings-file.d.ts} +1 -1
  23. package/dist/{cli/menu/settings/config-file.js → app/features/settings/settings-file.js} +1 -1
  24. package/dist/app/features/settings/settings-rows.d.ts +35 -0
  25. package/dist/app/features/settings/settings-rows.js +94 -0
  26. package/dist/app/features/settings/settings.def.d.ts +157 -0
  27. package/dist/app/features/settings/settings.def.js +179 -0
  28. package/dist/app/features/settings/settings.run.d.ts +67 -0
  29. package/dist/app/features/settings/settings.run.js +84 -0
  30. package/dist/app/kernel/define.d.ts +143 -0
  31. package/dist/app/kernel/define.js +42 -0
  32. package/dist/app/kernel/errors.d.ts +34 -0
  33. package/dist/app/kernel/errors.js +45 -0
  34. package/dist/app/kernel/fields.d.ts +70 -0
  35. package/dist/app/kernel/fields.js +243 -0
  36. package/dist/app/kernel/index.d.ts +8 -0
  37. package/dist/app/kernel/index.js +10 -0
  38. package/dist/app/kernel/io.d.ts +193 -0
  39. package/dist/app/kernel/io.js +126 -0
  40. package/dist/app/kernel/rpc.d.ts +156 -0
  41. package/dist/app/kernel/rpc.js +47 -0
  42. package/dist/app/kernel/text.d.ts +6 -0
  43. package/dist/app/kernel/text.js +57 -0
  44. package/dist/app/kernel/view.d.ts +146 -0
  45. package/dist/app/kernel/view.js +28 -0
  46. package/dist/app/registry.d.ts +10 -0
  47. package/dist/app/registry.js +50 -0
  48. package/dist/cli/args.d.ts +5 -0
  49. package/dist/cli/args.js +6 -0
  50. package/dist/cli/commands/engine.d.ts +2 -0
  51. package/dist/cli/commands/engine.js +60 -0
  52. package/dist/cli/commands/index.d.ts +1 -1
  53. package/dist/cli/commands/index.js +1 -1
  54. package/dist/cli/commands/launch/launch.js +49 -7
  55. package/dist/cli/commands/launch/prepare.js +3 -3
  56. package/dist/cli/commands/launch/running.js +2 -1
  57. package/dist/cli/commands/launch/summary.d.ts +2 -0
  58. package/dist/cli/commands/maintenance/config-share.d.ts +12 -0
  59. package/dist/cli/commands/maintenance/config-share.js +80 -0
  60. package/dist/cli/commands/maintenance/config.js +8 -82
  61. package/dist/cli/commands/maintenance/logs.js +14 -0
  62. package/dist/cli/commands/registry.js +3 -7
  63. package/dist/cli/commands/repair/checks/contained.d.ts +2 -0
  64. package/dist/cli/commands/repair/checks/contained.js +139 -0
  65. package/dist/cli/commands/repair/checks/hcs.d.ts +7 -0
  66. package/dist/cli/commands/repair/checks/hcs.js +28 -0
  67. package/dist/cli/commands/repair/checks/index.js +4 -0
  68. package/dist/cli/commands/repair/checks/pc.js +32 -1
  69. package/dist/cli/commands/repair/checks/sandbox.js +16 -1
  70. package/dist/cli/commands/repair/checks/vpn.js +34 -9
  71. package/dist/cli/commands/repair/env.js +2 -1
  72. package/dist/cli/commands/sessions/index.d.ts +0 -4
  73. package/dist/cli/commands/sessions/index.js +3 -7
  74. package/dist/cli/commands/shared-use.d.ts +5 -2
  75. package/dist/cli/commands/shared-use.js +11 -8
  76. package/dist/cli/commands/storage/contained.d.ts +3 -0
  77. package/dist/cli/commands/storage/contained.js +179 -0
  78. package/dist/cli/commands/storage/offer.js +7 -1
  79. package/dist/cli/commands/storage/storage.js +10 -2
  80. package/dist/cli/commands/user-config.js +1 -1
  81. package/dist/cli/commands/vpn.js +20 -3
  82. package/dist/cli/commands/web.d.ts +5 -0
  83. package/dist/cli/commands/web.js +40 -0
  84. package/dist/cli/index.js +2 -1
  85. package/dist/cli/manifest.d.ts +72 -104
  86. package/dist/cli/manifest.js +64 -39
  87. package/dist/cli/menu/add-agent.d.ts +1 -1
  88. package/dist/cli/menu/add-agent.js +1 -1
  89. package/dist/cli/menu/apps.d.ts +7 -1
  90. package/dist/cli/menu/apps.js +36 -0
  91. package/dist/cli/menu/command-fields.d.ts +1 -1
  92. package/dist/cli/menu/command-form.js +2 -2
  93. package/dist/cli/menu/io-menu.d.ts +29 -0
  94. package/dist/cli/menu/io-menu.js +126 -0
  95. package/dist/cli/menu/live.d.ts +27 -0
  96. package/dist/cli/menu/live.js +69 -0
  97. package/dist/cli/menu/memory.d.ts +19 -0
  98. package/dist/cli/menu/memory.js +53 -0
  99. package/dist/cli/menu/menu.d.ts +10 -1
  100. package/dist/cli/menu/menu.js +27 -9
  101. package/dist/cli/menu/pages/agents-page.d.ts +3 -1
  102. package/dist/cli/menu/pages/agents-page.js +12 -5
  103. package/dist/cli/menu/pages/apps-page.d.ts +3 -1
  104. package/dist/cli/menu/pages/apps-page.js +19 -2
  105. package/dist/cli/menu/pages/list-page.d.ts +27 -0
  106. package/dist/cli/menu/pages/list-page.js +109 -7
  107. package/dist/cli/menu/pages/maintenance-page.d.ts +0 -5
  108. package/dist/cli/menu/pages/maintenance-page.js +17 -19
  109. package/dist/cli/menu/pages/sandbox-page.d.ts +4 -6
  110. package/dist/cli/menu/pages/sandbox-page.js +26 -118
  111. package/dist/cli/menu/pages/scopes.d.ts +1 -1
  112. package/dist/cli/menu/pages/sessions-page.d.ts +5 -11
  113. package/dist/cli/menu/pages/sessions-page.js +19 -88
  114. package/dist/cli/menu/pages/settings-page.d.ts +2 -10
  115. package/dist/cli/menu/pages/settings-page.js +64 -94
  116. package/dist/cli/menu/render-view.d.ts +28 -0
  117. package/dist/cli/menu/render-view.js +112 -0
  118. package/dist/cli/menu/settings/{path-kind.d.ts → ask-path.d.ts} +1 -7
  119. package/dist/cli/menu/settings/ask-path.js +14 -0
  120. package/dist/cli/menu/settings/collections/collection-editor.d.ts +1 -1
  121. package/dist/cli/menu/settings/collections/string-list-editor.d.ts +1 -2
  122. package/dist/cli/menu/settings/collections/string-list-editor.js +1 -1
  123. package/dist/cli/menu/settings/draft.d.ts +1 -1
  124. package/dist/cli/menu/settings/format.d.ts +1 -3
  125. package/dist/cli/menu/settings/format.js +6 -36
  126. package/dist/cli/menu/settings/index.d.ts +1 -3
  127. package/dist/cli/menu/settings/index.js +3 -3
  128. package/dist/cli/menu/settings/object-editor.d.ts +1 -1
  129. package/dist/cli/menu/settings/share.d.ts +36 -0
  130. package/dist/cli/menu/settings/share.js +131 -0
  131. package/dist/cli/menu/settings/value-editor.d.ts +5 -1
  132. package/dist/cli/menu/settings/value-editor.js +57 -54
  133. package/dist/cli/menu/several.js +1 -1
  134. package/dist/cli/menu/share-settings.d.ts +21 -0
  135. package/dist/cli/menu/share-settings.js +112 -0
  136. package/dist/cli/menu/shell.d.ts +21 -5
  137. package/dist/cli/menu/shell.js +31 -6
  138. package/dist/cli/menu/views.d.ts +7 -0
  139. package/dist/cli/menu/views.js +11 -0
  140. package/dist/cli/menu/vpn-login.js +8 -0
  141. package/dist/cli/operations/handler.d.ts +6 -0
  142. package/dist/cli/operations/handler.js +61 -0
  143. package/dist/cli/operations/index.d.ts +4 -0
  144. package/dist/cli/operations/index.js +5 -0
  145. package/dist/cli/operations/input.d.ts +6 -0
  146. package/dist/cli/operations/input.js +37 -0
  147. package/dist/cli/operations/io-cli.d.ts +7 -0
  148. package/dist/cli/operations/io-cli.js +62 -0
  149. package/dist/cli/operations/spec.d.ts +18 -0
  150. package/dist/cli/operations/spec.js +108 -0
  151. package/dist/cli/output/colors.d.ts +2 -2
  152. package/dist/cli/output/colors.js +2 -11
  153. package/dist/cli/output/index.d.ts +1 -0
  154. package/dist/cli/output/index.js +1 -0
  155. package/dist/cli/output/print.d.ts +3 -1
  156. package/dist/cli/output/print.js +7 -3
  157. package/dist/cli/output/progress.d.ts +15 -0
  158. package/dist/cli/output/progress.js +44 -0
  159. package/dist/cli/output/render-view.d.ts +4 -0
  160. package/dist/cli/output/render-view.js +63 -0
  161. package/dist/cli/services/agent-commands-service.d.ts +2 -0
  162. package/dist/cli/services/agent-commands-service.js +11 -2
  163. package/dist/cli/services/logs-service.js +4 -3
  164. package/dist/cli/tui/app.d.ts +6 -1
  165. package/dist/cli/tui/app.js +26 -4
  166. package/dist/cli/tui/list/list-screen.d.ts +14 -0
  167. package/dist/cli/tui/list/list-screen.js +64 -36
  168. package/dist/cli/tui/list/list-types.d.ts +2 -0
  169. package/dist/cli/tui/terminal.d.ts +7 -0
  170. package/dist/cli/tui/terminal.js +30 -10
  171. package/dist/core/config/defaults.js +9 -1
  172. package/dist/core/schema/config.d.ts +14 -0
  173. package/dist/core/schema/config.js +16 -1
  174. package/dist/core/schema/harness.d.ts +1 -0
  175. package/dist/core/schema/harness.js +5 -0
  176. package/dist/core/schema/sandbox.d.ts +6 -0
  177. package/dist/core/schema/sandbox.js +20 -3
  178. package/dist/core/schema/vpn.d.ts +2 -1
  179. package/dist/core/schema/vpn.js +4 -4
  180. package/dist/core/schema/web.d.ts +1 -0
  181. package/dist/core/schema/web.js +4 -1
  182. package/dist/core/types.d.ts +23 -5
  183. package/dist/core/util/fs.d.ts +2 -0
  184. package/dist/core/util/fs.js +9 -0
  185. package/dist/core/util/index.d.ts +1 -1
  186. package/dist/core/util/index.js +1 -1
  187. package/dist/guest/agents/agent-files.d.ts +5 -0
  188. package/dist/guest/agents/agent-files.js +4 -0
  189. package/dist/guest/agents/local-state.d.ts +2 -0
  190. package/dist/guest/agents/local-state.js +21 -1
  191. package/dist/guest/boot/boot.js +5 -2
  192. package/dist/guest/daemon/daemon.js +4 -0
  193. package/dist/guest/daemon/sessions.d.ts +5 -0
  194. package/dist/guest/daemon/sessions.js +17 -3
  195. package/dist/guest/entries/daemon.js +3 -0
  196. package/dist/guest/governor/decide.d.ts +71 -0
  197. package/dist/guest/governor/decide.js +211 -0
  198. package/dist/guest/governor/governor.d.ts +45 -0
  199. package/dist/guest/governor/governor.js +230 -0
  200. package/dist/guest/governor/index.d.ts +2 -0
  201. package/dist/guest/governor/index.js +2 -0
  202. package/dist/guest/network/firewall.d.ts +1 -0
  203. package/dist/guest/network/firewall.js +65 -23
  204. package/dist/guest/runtime/csc.d.ts +1 -0
  205. package/dist/guest/runtime/csc.js +1 -0
  206. package/dist/guest/runtime/process.js +53 -5
  207. package/dist/guest/sessions/viewer.js +3 -1
  208. package/dist/guest/setup/claude-mem.js +12 -4
  209. package/dist/guest/toolchains/languages/node.js +29 -3
  210. package/dist/guest/toolchains/native/rust-extras.js +4 -2
  211. package/dist/guest/vpn/vpn.js +69 -21
  212. package/dist/guest/vpn/wireguard.d.ts +32 -0
  213. package/dist/guest/vpn/wireguard.js +132 -0
  214. package/dist/host/agent-commands/commands.d.ts +4 -0
  215. package/dist/host/agent-commands/commands.js +31 -7
  216. package/dist/host/audit/log.d.ts +1 -1
  217. package/dist/host/channel/attach.d.ts +3 -1
  218. package/dist/host/channel/attach.js +5 -2
  219. package/dist/host/channel/channel.d.ts +2 -0
  220. package/dist/host/channel/channel.js +4 -0
  221. package/dist/host/engines/engine.d.ts +12 -0
  222. package/dist/host/engines/engine.js +1 -0
  223. package/dist/host/engines/hcs/check.d.ts +25 -0
  224. package/dist/host/engines/hcs/check.js +95 -0
  225. package/dist/host/engines/hcs/documents.d.ts +44 -0
  226. package/dist/host/engines/hcs/documents.js +155 -0
  227. package/dist/host/engines/hcs/engine.d.ts +39 -0
  228. package/dist/host/engines/hcs/engine.js +171 -0
  229. package/dist/host/engines/hcs/helper.d.ts +43 -0
  230. package/dist/host/engines/hcs/helper.js +173 -0
  231. package/dist/host/engines/hcs/images.d.ts +37 -0
  232. package/dist/host/engines/hcs/images.js +152 -0
  233. package/dist/host/engines/index.d.ts +7 -0
  234. package/dist/host/engines/index.js +5 -0
  235. package/dist/host/engines/registry.d.ts +16 -0
  236. package/dist/host/engines/registry.js +30 -0
  237. package/dist/host/engines/types.d.ts +58 -0
  238. package/dist/host/engines/types.js +1 -0
  239. package/dist/host/engines/windows-sandbox/engine.d.ts +16 -0
  240. package/dist/host/engines/windows-sandbox/engine.js +63 -0
  241. package/dist/host/engines/windows-sandbox/wsb-text.d.ts +2 -0
  242. package/dist/host/engines/windows-sandbox/wsb-text.js +29 -0
  243. package/dist/host/launch/generate.d.ts +5 -1
  244. package/dist/host/launch/generate.js +50 -8
  245. package/dist/host/launch/index.d.ts +3 -1
  246. package/dist/host/launch/index.js +3 -1
  247. package/dist/host/launch/launch-plan.d.ts +13 -0
  248. package/dist/host/launch/launch-plan.js +26 -0
  249. package/dist/host/launch/mappings.js +2 -1
  250. package/dist/host/launch/risk-notes.d.ts +10 -0
  251. package/dist/host/launch/risk-notes.js +18 -0
  252. package/dist/host/launch/session-file.d.ts +2 -0
  253. package/dist/host/launch/session-file.js +7 -1
  254. package/dist/host/launch/wsb-file.d.ts +28 -4
  255. package/dist/host/launch/wsb-file.js +61 -42
  256. package/dist/host/life/crash-report.d.ts +27 -1
  257. package/dist/host/life/crash-report.js +173 -7
  258. package/dist/host/life/host-memory.d.ts +41 -0
  259. package/dist/host/life/host-memory.js +140 -0
  260. package/dist/host/life/index.d.ts +2 -0
  261. package/dist/host/life/index.js +2 -0
  262. package/dist/host/life/stop-events.d.ts +2 -0
  263. package/dist/host/life/stop-events.js +20 -3
  264. package/dist/host/life/stop-warning.js +1 -1
  265. package/dist/host/life/watchdog.d.ts +56 -0
  266. package/dist/host/life/watchdog.js +124 -0
  267. package/dist/host/native.d.ts +3 -2
  268. package/dist/host/native.js +22 -3
  269. package/dist/host/profile/claude-login.js +3 -2
  270. package/dist/host/sandbox/contained-deps.d.ts +2 -0
  271. package/dist/host/sandbox/contained-deps.js +17 -0
  272. package/dist/host/sandbox/index.d.ts +2 -0
  273. package/dist/host/sandbox/index.js +2 -0
  274. package/dist/host/sandbox/platform.d.ts +2 -0
  275. package/dist/host/sandbox/platform.js +7 -7
  276. package/dist/host/sandbox/tasklist.d.ts +13 -0
  277. package/dist/host/sandbox/tasklist.js +47 -0
  278. package/dist/host/secrets/launch-secrets.js +2 -1
  279. package/dist/host/sharing/current.js +2 -6
  280. package/dist/host/storage/contained/check.d.ts +9 -0
  281. package/dist/host/storage/contained/check.js +37 -0
  282. package/dist/host/storage/contained/contained-storage.d.ts +66 -0
  283. package/dist/host/storage/contained/contained-storage.js +168 -0
  284. package/dist/host/storage/contained/disk-port.d.ts +43 -0
  285. package/dist/host/storage/contained/disk-port.js +122 -0
  286. package/dist/host/storage/contained/index.d.ts +9 -0
  287. package/dist/host/storage/contained/index.js +8 -0
  288. package/dist/host/storage/contained/layout.d.ts +28 -0
  289. package/dist/host/storage/contained/layout.js +45 -0
  290. package/dist/host/storage/contained/resolve.d.ts +4 -0
  291. package/dist/host/storage/contained/resolve.js +30 -0
  292. package/dist/host/storage/contained/setting.d.ts +1 -0
  293. package/dist/host/storage/contained/setting.js +5 -0
  294. package/dist/host/storage/contained/state.d.ts +89 -0
  295. package/dist/host/storage/contained/state.js +166 -0
  296. package/dist/host/storage/contained/transfer.d.ts +24 -0
  297. package/dist/host/storage/contained/transfer.js +121 -0
  298. package/dist/host/storage/index.d.ts +2 -0
  299. package/dist/host/storage/index.js +2 -0
  300. package/dist/host/storage/providers/contained.d.ts +15 -0
  301. package/dist/host/storage/providers/contained.js +39 -0
  302. package/dist/host/storage/providers/folder.d.ts +12 -0
  303. package/dist/host/storage/providers/folder.js +20 -0
  304. package/dist/host/storage/providers/index.d.ts +4 -0
  305. package/dist/host/storage/providers/index.js +3 -0
  306. package/dist/host/storage/providers/registry.d.ts +9 -0
  307. package/dist/host/storage/providers/registry.js +14 -0
  308. package/dist/host/storage/providers/types.d.ts +33 -0
  309. package/dist/host/storage/providers/types.js +1 -0
  310. package/dist/host/tools/conpty.js +3 -2
  311. package/dist/host/tools/windows-terminal.js +8 -7
  312. package/dist/host/vpn/index.d.ts +1 -1
  313. package/dist/host/vpn/index.js +1 -1
  314. package/dist/host/vpn/prepare.d.ts +2 -0
  315. package/dist/host/vpn/prepare.js +63 -2
  316. package/dist/host/web/browser.d.ts +2 -0
  317. package/dist/host/web/browser.js +14 -1
  318. package/dist/host/web/index.d.ts +3 -2
  319. package/dist/host/web/index.js +3 -2
  320. package/dist/host/web/rpc.d.ts +24 -0
  321. package/dist/host/web/rpc.js +149 -0
  322. package/dist/host/web/runner.d.ts +2 -0
  323. package/dist/host/web/runner.js +1 -2
  324. package/dist/host/web/server.d.ts +7 -0
  325. package/dist/host/web/server.js +23 -3
  326. package/dist/runtime/agent.js +83 -18
  327. package/dist/runtime/daemon.js +1805 -1221
  328. package/dist/runtime/db.js +83 -18
  329. package/dist/runtime/main.js +779 -219
  330. package/dist/runtime/netproxy.js +73 -11
  331. package/dist/runtime/view.js +91 -16
  332. package/dist/selftest/agent-paths.test.js +11 -1
  333. package/dist/selftest/app.test.d.ts +2 -0
  334. package/dist/selftest/app.test.js +324 -0
  335. package/dist/selftest/channel.test.js +13 -0
  336. package/dist/selftest/cli.test.js +2 -1
  337. package/dist/selftest/config.test.js +59 -0
  338. package/dist/selftest/contained.test.d.ts +2 -0
  339. package/dist/selftest/contained.test.js +544 -0
  340. package/dist/selftest/credentials.test.js +1 -1
  341. package/dist/selftest/engine.test.d.ts +2 -0
  342. package/dist/selftest/engine.test.js +127 -0
  343. package/dist/selftest/guest-boot.test.js +47 -1
  344. package/dist/selftest/hcs.test.d.ts +2 -0
  345. package/dist/selftest/hcs.test.js +482 -0
  346. package/dist/selftest/heavy-load.test.d.ts +2 -0
  347. package/dist/selftest/heavy-load.test.js +355 -0
  348. package/dist/selftest/index.js +12 -0
  349. package/dist/selftest/layout.test.js +12 -0
  350. package/dist/selftest/menu-perf.test.d.ts +2 -0
  351. package/dist/selftest/menu-perf.test.js +248 -0
  352. package/dist/selftest/menu-screens.js +4 -2
  353. package/dist/selftest/menu.test.js +36 -8
  354. package/dist/selftest/network.test.js +20 -1
  355. package/dist/selftest/support/engine-cases.d.ts +8 -0
  356. package/dist/selftest/support/engine-cases.js +65 -0
  357. package/dist/selftest/support/engine-golden.d.ts +3 -0
  358. package/dist/selftest/support/engine-golden.js +66 -0
  359. package/dist/selftest/support/fixtures.d.ts +8 -1
  360. package/dist/selftest/support/hcs-probes.d.ts +17 -0
  361. package/dist/selftest/support/hcs-probes.js +41 -0
  362. package/dist/selftest/vpn.test.js +136 -3
  363. package/dist/selftest/web.test.js +101 -3
  364. package/dist/shared/channel/run-dir.d.ts +4 -1
  365. package/dist/shared/channel/run-dir.js +7 -1
  366. package/dist/shared/channel/schema.d.ts +35 -1
  367. package/dist/shared/channel/schema.js +27 -0
  368. package/dist/shared/channel/secrets.d.ts +1 -0
  369. package/dist/shared/channel/secrets.js +6 -0
  370. package/dist/shared/io/copy-tree.d.ts +7 -0
  371. package/dist/shared/io/copy-tree.js +45 -0
  372. package/dist/shared/io/index.d.ts +2 -0
  373. package/dist/shared/io/index.js +2 -0
  374. package/dist/shared/io/robocopy-result.d.ts +6 -0
  375. package/dist/shared/io/robocopy-result.js +23 -0
  376. package/dist/shared/memory/index.d.ts +1 -0
  377. package/dist/shared/memory/index.js +1 -0
  378. package/dist/shared/memory/policy.d.ts +38 -0
  379. package/dist/shared/memory/policy.js +63 -0
  380. package/dist/shared/network/firewall-rules.d.ts +8 -0
  381. package/dist/shared/network/firewall-rules.js +28 -0
  382. package/dist/shared/pty/output-filter.d.ts +14 -1
  383. package/dist/shared/pty/output-filter.js +22 -4
  384. package/dist/shared/pty/terminal.d.ts +4 -1
  385. package/dist/shared/pty/terminal.js +4 -2
  386. package/dist/shared/system.d.ts +1 -0
  387. package/dist/shared/system.js +1 -0
  388. package/dist/shared/vpn/download.d.ts +16 -0
  389. package/dist/shared/vpn/download.js +16 -0
  390. package/dist/shared/vpn/wireguard.d.ts +51 -0
  391. package/dist/shared/vpn/wireguard.js +227 -0
  392. package/dist/sync/sync-cycle.js +5 -5
  393. package/dist/sync/watcher.d.ts +13 -0
  394. package/dist/sync/watcher.js +92 -3
  395. package/dist/web/index.html +4 -3
  396. package/dist/web/main-NRRS5O6Z.js +157 -0
  397. package/dist/web/main-T5DMCHYV.css +1 -0
  398. package/native/csharp/HcsHost/AssemblyInfo.cs +14 -0
  399. package/native/csharp/HcsHost/Guard.cs +100 -0
  400. package/native/csharp/HcsHost/Hcs.cs +256 -0
  401. package/native/csharp/HcsHost/Json.cs +78 -0
  402. package/native/csharp/HcsHost/Program.cs +185 -0
  403. package/native/csharp/HcsHost/VirtualDisk.cs +95 -0
  404. package/native/csharp/HostTools/AssemblyInfo.cs +14 -0
  405. package/native/csharp/HostTools/MemoryStatus.cs +128 -0
  406. package/native/csharp/HostTools/Program.cs +58 -17
  407. package/native/scripts/check-contained.ps1 +28 -0
  408. package/native/scripts/contained-common.ps1 +241 -0
  409. package/native/scripts/dismount-contained.ps1 +66 -0
  410. package/native/scripts/memory-status.ps1 +42 -0
  411. package/native/scripts/mount-contained.ps1 +139 -0
  412. package/native/scripts/run-elevated.ps1 +17 -4
  413. package/native/scripts/stop-events.ps1 +23 -8
  414. package/package.json +9 -2
  415. package/sandbox/csharp/MemGov.cs +466 -0
  416. package/sandbox/csharp/PtyHost.cs +111 -5
  417. package/sandbox/scripts/new-shortcuts.ps1 +3 -1
  418. package/schemas/config.schema.json +66 -8
  419. package/schemas/sandbox-config.schema.json +3 -3
  420. package/dist/cli/commands/sessions/control.d.ts +0 -4
  421. package/dist/cli/commands/sessions/control.js +0 -66
  422. package/dist/cli/commands/sessions/list.d.ts +0 -2
  423. package/dist/cli/commands/sessions/list.js +0 -48
  424. package/dist/cli/commands/sessions/status.d.ts +0 -4
  425. package/dist/cli/commands/sessions/status.js +0 -114
  426. package/dist/cli/commands/sessions/window.d.ts +0 -3
  427. package/dist/cli/commands/sessions/window.js +0 -18
  428. package/dist/cli/menu/settings/path-kind.js +0 -44
  429. package/dist/web/main-CXESL347.js +0 -45
  430. package/dist/web/main-VLR7DVQ4.css +0 -1
  431. /package/dist/{cli/menu/settings → app/kernel}/schema-node.d.ts +0 -0
  432. /package/dist/{cli/menu/settings → app/kernel}/schema-node.js +0 -0
@@ -3,19 +3,137 @@
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { assertNoLinks, safeText } from '../../core/util/index.js';
6
- import { explainStop, readStopEvents } from './stop-events.js';
6
+ import { vgpuWarning } from '../launch/risk-notes.js';
7
+ import { hostMemoryLine, readHostMemory } from './host-memory.js';
8
+ import { hostDataFolder, hostDataRoot } from '../storage/index.js';
9
+ import { bugcheckCode, explainStop, readStopEvents } from './stop-events.js';
7
10
  const KEEP_REPORTS = 20;
8
11
  const MEMORY_TAIL_BYTES = 16 * 1024;
12
+ /** A gap longer than this between "last seen running" and the restart means the PC stopped responding first. */
13
+ const FREEZE_GAP_MS = 5 * 60 * 1000;
9
14
  function crashDir(storageRoot) {
10
15
  return path.join(storageRoot, 'logs', 'crashes');
11
16
  }
17
+ /** "This PC stopped responding at X and was restarted at Y (n minutes later)", or null when there was no long gap. */
18
+ export function freezeLine(cause, lastSeen) {
19
+ if (cause.kind !== 'crash')
20
+ return null;
21
+ const gap = Date.parse(cause.at) - Date.parse(lastSeen);
22
+ if (!Number.isFinite(gap) || gap <= FREEZE_GAP_MS)
23
+ return null;
24
+ return ('This PC stopped responding at ' +
25
+ lastSeen +
26
+ ' (the last time anything was seen running) and was restarted at ' +
27
+ cause.at +
28
+ ' (' +
29
+ Math.round(gap / 60000) +
30
+ ' minutes later).');
31
+ }
32
+ /** What each kind of event usually means, one line per kind that appears (pure). */
33
+ export function findingLines(events) {
34
+ const lines = [];
35
+ const has = (pred) => events.find(pred);
36
+ const lower = (e) => e.provider.toLowerCase();
37
+ const resource = has((e) => e.id === 2004 && lower(e).includes('resource-exhaustion'));
38
+ if (resource)
39
+ lines.push('Resource-Exhaustion-Detector 2004: memory ran out (RAM plus paging file). Programs were using too much at once.');
40
+ const bug = has((e) => e.id === 1001 && lower(e).includes('systemerrorreporting'));
41
+ const bugCode = bug ? /bugcheck was:\s*(0x[0-9a-f]{1,8})/i.exec(bug.process)?.[1] : undefined;
42
+ const power = has((e) => e.id === 41 && lower(e).includes('kernel-power'));
43
+ const powerCode = power ? bugcheckCode(power.process) : null;
44
+ if (bug || powerCode)
45
+ lines.push('Blue screen with stop code ' +
46
+ (bugCode ?? powerCode) +
47
+ ': search that code on Microsoft Learn; drivers, memory or hardware are the usual causes.');
48
+ else if (power)
49
+ lines.push('Kernel-Power 41 without a stop code: the PC froze, lost power or was reset by hand, with no blue screen.');
50
+ if (has((e) => lower(e).includes('whea-logger')))
51
+ lines.push('WHEA-Logger: Windows reported a hardware error (CPU, memory, a bus or a disk): heat or failing hardware.');
52
+ if (has((e) => lower(e).startsWith('hyper-v')))
53
+ lines.push("Hyper-V events: the sandbox's virtual machine (or the service that runs it) reported a problem.");
54
+ if (has((e) => /^(application error|windows error reporting)$/i.test(e.provider)))
55
+ lines.push("A crash report names vmwp.exe or vmmem, the programs that hold the sandbox's VM.");
56
+ return lines;
57
+ }
58
+ /** Crash dumps in these folders newer than `since`: names and times only. */
59
+ export function findDumps(dirs, since) {
60
+ const found = [];
61
+ for (const dir of dirs) {
62
+ try {
63
+ for (const name of fs.readdirSync(dir)) {
64
+ if (!/\.dmp$/i.test(name))
65
+ continue;
66
+ const stat = fs.lstatSync(path.join(dir, name));
67
+ if (stat.isFile() && stat.mtime >= since)
68
+ found.push({ name: path.join(dir, name), at: stat.mtime.toISOString() });
69
+ }
70
+ }
71
+ catch {
72
+ // No such folder, or no access: no dumps to list.
73
+ }
74
+ }
75
+ return found.sort((a, b) => a.at.localeCompare(b.at)).slice(0, 10);
76
+ }
77
+ /** Whether the newest launch started at or before `lastSeen` had vGPU on (host-only .wsb in <storage>\launches). */
78
+ export function launchHadVgpu(storageRoot, lastSeen) {
79
+ try {
80
+ const root = path.join(storageRoot, 'launches');
81
+ const limit = Date.parse(lastSeen) + 60 * 1000;
82
+ const newest = fs
83
+ .readdirSync(root)
84
+ .map((name) => ({ name, at: fs.lstatSync(path.join(root, name)).mtimeMs }))
85
+ .filter((l) => l.at <= limit)
86
+ .sort((a, b) => b.at - a.at)[0];
87
+ if (!newest)
88
+ return false;
89
+ const dir = path.join(root, newest.name);
90
+ const wsb = fs.readdirSync(dir).find((f) => f.endsWith('.wsb'));
91
+ return wsb
92
+ ? /<VGpu>\s*Enable\s*<\/VGpu>/i.test(fs.readFileSync(path.join(dir, wsb), 'utf8').slice(0, 65536))
93
+ : false;
94
+ }
95
+ catch {
96
+ // No launch folder to read: unknown, so nothing is claimed.
97
+ return false;
98
+ }
99
+ }
100
+ /** A tail of this PC's own memory log (written by the watcher), without following links. */
101
+ function hostMemoryTail(storageRoot, dataPath) {
102
+ try {
103
+ const file = path.join(storageRoot, 'logs', 'host-memory-' + path.basename(dataPath) + '.log');
104
+ const stat = fs.lstatSync(file);
105
+ if (!stat.isFile())
106
+ return '';
107
+ const length = Math.min(stat.size, MEMORY_TAIL_BYTES);
108
+ const fd = fs.openSync(file, 'r');
109
+ try {
110
+ const buffer = Buffer.alloc(length);
111
+ fs.readSync(fd, buffer, 0, length, stat.size - length);
112
+ return buffer.toString('utf8');
113
+ }
114
+ finally {
115
+ fs.closeSync(fd);
116
+ }
117
+ }
118
+ catch {
119
+ return '';
120
+ }
121
+ }
12
122
  /**
13
123
  * The report text (pure): what happened (the sandbox's or this PC's stop code, where the dump is) and the memory log's
14
124
  * last lines. `memoryLog` is the sandbox's own file, so it is shown through safeText.
15
125
  */
16
- export function crashReportText(cause, events, memoryLog, lastSeen) {
17
- const eventLines = events.length
18
- ? events.map((e) => ' ' + e.at + ' ' + e.provider + ' ' + e.id + (e.process ? ': ' + e.process.replace(/\s+/g, ' ') : ''))
126
+ export function crashReportText(cause, events, memoryLog, lastSeen, extra = {}) {
127
+ const seen = new Set();
128
+ const unique = events.filter((e) => {
129
+ const key = e.at + '|' + e.id + '|' + e.provider;
130
+ if (seen.has(key))
131
+ return false;
132
+ seen.add(key);
133
+ return true;
134
+ });
135
+ const eventLines = unique.length
136
+ ? unique.map((e) => ' ' + e.at + ' ' + e.provider + ' ' + e.id + (e.process ? ': ' + e.process.replace(/\s+/g, ' ') : ''))
19
137
  : [' (none)'];
20
138
  const memoryLines = memoryLog
21
139
  ? memoryLog
@@ -24,23 +142,63 @@ export function crashReportText(cause, events, memoryLog, lastSeen) {
24
142
  .slice(-30)
25
143
  .map((l) => ' ' + safeText(l))
26
144
  : [' (none found)'];
145
+ const hostLines = (extra.hostMemoryLog ?? '')
146
+ .split(/\r?\n/)
147
+ .filter(Boolean)
148
+ .slice(-20)
149
+ .map((l) => ' ' + safeText(l));
150
+ const freeze = freezeLine(cause, lastSeen);
151
+ const meaning = findingLines(unique);
152
+ const dumps = extra.dumps ?? [];
153
+ const vgpu = extra.vgpu ? ['vGPU was on for this sandbox. ' + vgpuWarning('its launch settings'), ''] : [];
27
154
  return [
28
155
  'Sandbox crash report, written ' + new Date().toISOString(),
29
156
  'What happened: ' + cause.text + ' at ' + cause.at + ' (the sandbox was last seen running at ' + lastSeen + ').',
157
+ ...(freeze ? [freeze] : []),
30
158
  '',
31
159
  'Windows events around then:',
32
160
  ...eventLines,
33
161
  '',
162
+ ...vgpu,
163
+ 'What that usually means:',
164
+ ...(meaning.length ? meaning.map((l) => ' ' + l) : [' (no memory, blue screen or hardware event was logged)']),
165
+ '',
166
+ 'Crash dumps Windows left (C:\\Windows\\Minidump, MEMORY.DMP):',
167
+ ...(dumps.length ? dumps.map((d) => ' ' + d.name + ' ' + d.at) : [' (none from that time)']),
168
+ '',
169
+ "This PC's memory while the sandbox ran (host-memory log):",
170
+ ...(hostLines.length ? hostLines : [' (none: the background sync was not running)']),
171
+ ...(extra.hostMemoryNow ? [' after the restart: ' + extra.hostMemoryNow] : []),
172
+ '',
34
173
  "The sandbox's memory log, last lines (C:\\AgentData\\logs\\memory-*.log inside it):",
35
174
  ...memoryLines,
36
175
  '',
37
176
  ].join('\n');
38
177
  }
178
+ /** The newest crash report in <storage>\logs\crashes (at most 64 KB of it), or null. */
179
+ export function newestCrashReport(storageRoot) {
180
+ try {
181
+ const dir = crashDir(storageRoot);
182
+ const newest = fs
183
+ .readdirSync(dir)
184
+ .filter((f) => /^crash-.*\.txt$/.test(f))
185
+ .sort()
186
+ .pop();
187
+ if (!newest)
188
+ return null;
189
+ const file = path.join(dir, newest);
190
+ return { file, text: safeText(fs.readFileSync(file, 'utf8').slice(0, 64 * 1024)) };
191
+ }
192
+ catch {
193
+ // No reports folder: none yet.
194
+ return null;
195
+ }
196
+ }
39
197
  /** The newest memory log the sandbox wrote, last 16 KB at most, without following links in the data folder. */
40
198
  function memoryLogTail(dataPath) {
41
199
  try {
42
- const logs = path.join(dataPath, 'logs');
43
- assertNoLinks(dataPath, logs);
200
+ const logs = hostDataFolder(dataPath, 'logs');
201
+ assertNoLinks(hostDataRoot(dataPath), logs);
44
202
  const newest = fs
45
203
  .readdirSync(logs)
46
204
  .filter((f) => /^memory-\d{8}-\d{6}\.log$/.test(f))
@@ -78,7 +236,15 @@ export function saveCrashReport(storageRoot, dataPath, lastSeen) {
78
236
  const dir = crashDir(storageRoot);
79
237
  fs.mkdirSync(dir, { recursive: true });
80
238
  const file = path.join(dir, 'crash-' + cause.at.replace(/[:.]/g, '-') + '.txt');
81
- fs.writeFileSync(file, crashReportText(cause, events, memoryLogTail(dataPath), lastSeen));
239
+ const since = new Date(Date.parse(lastSeen) - 10 * 60 * 1000);
240
+ const windir = process.env.SystemRoot ?? 'C:\\Windows';
241
+ const memoryNow = readHostMemory(storageRoot);
242
+ fs.writeFileSync(file, crashReportText(cause, events, memoryLogTail(dataPath), lastSeen, {
243
+ hostMemoryLog: hostMemoryTail(storageRoot, dataPath),
244
+ vgpu: launchHadVgpu(storageRoot, lastSeen),
245
+ dumps: findDumps([path.join(windir, 'Minidump'), windir], since),
246
+ ...(memoryNow ? { hostMemoryNow: hostMemoryLine(memoryNow) } : {}),
247
+ }));
82
248
  for (const old of fs
83
249
  .readdirSync(dir)
84
250
  .filter((f) => f.startsWith('crash-'))
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ export declare const HostMemorySchema: z.ZodObject<{
3
+ totalMb: z.ZodNumber;
4
+ availableMb: z.ZodNumber;
5
+ commitLimitMb: z.ZodNumber;
6
+ commitAvailableMb: z.ZodNumber;
7
+ pageFile: z.ZodCatch<z.ZodEnum<{
8
+ auto: "auto";
9
+ none: "none";
10
+ fixed: "fixed";
11
+ }>>;
12
+ pageFileMaxMb: z.ZodCatch<z.ZodNumber>;
13
+ diskQueue: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
14
+ }, z.core.$strip>;
15
+ export type HostMemory = z.output<typeof HostMemorySchema>;
16
+ /** The parsed answer of memory-status, or null (not Windows, unreadable, nonsense). */
17
+ export declare function parseHostMemory(text: string): HostMemory | null;
18
+ /** This PC's memory now, or null when it cannot be read (and always during the self-test: it never asks Windows). */
19
+ export declare function readHostMemory(storageRoot: string): HostMemory | null;
20
+ /** The same without blocking: for the watcher, which must never wait on it. */
21
+ export declare function readHostMemoryAsync(storageRoot: string): Promise<HostMemory | null>;
22
+ /** How low this PC's memory is: what the host watchdog acts on (pure). */
23
+ export interface HostPressure {
24
+ readonly low: boolean;
25
+ /** Plain words for the log and the sandbox. */
26
+ readonly reason: string;
27
+ }
28
+ /** Low = available commit under half the reserve, or available RAM under 2 GB. */
29
+ export declare function hostPressure(memory: HostMemory, reserveHostMb: number): HostPressure;
30
+ /** One line for the host memory log. */
31
+ export declare function hostMemoryLine(memory: HostMemory, at?: Date): string;
32
+ /**
33
+ * Whether Windows Sandbox could push this PC past its commit limit (pure): the paging file is fixed or off and the
34
+ * commit limit leaves less than the sandbox's memory plus what this PC already uses. Null when all is well.
35
+ */
36
+ export declare function pageFileRisk(memory: HostMemory, sandboxMb: number): {
37
+ neededGb: number;
38
+ text: string;
39
+ } | null;
40
+ /** Whether the page file hint may be shown now (once a day at most); records that it was. */
41
+ export declare function takePageFileHint(storageRoot: string, now?: Date): boolean;
@@ -0,0 +1,140 @@
1
+ // This PC's memory (RAM and commit) for sizing the sandbox, the host watchdog and the doctor. HostTools.exe
2
+ // memory-status reads it (built in <storage>\bin), else native\scripts\memory-status.ps1. Windows Sandbox takes its
3
+ // memory from this PC as it goes (the setting is a ceiling, not a reservation), so what decides "slow or closed" is the
4
+ // PC's commit limit (RAM plus paging files), not only free RAM.
5
+ import { spawn, spawnSync } from 'node:child_process';
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { z } from 'zod';
9
+ import { isWin } from '../../core/util/index.js';
10
+ import { SYSTEM } from '../../shared/system.js';
11
+ import { hostToolsExe, nativeScript } from '../native.js';
12
+ import { selfTestRunning } from '../test-guard.js';
13
+ export const HostMemorySchema = z.object({
14
+ totalMb: z.number().int().nonnegative(),
15
+ availableMb: z.number().int().nonnegative(),
16
+ commitLimitMb: z.number().int().nonnegative(),
17
+ commitAvailableMb: z.number().int().nonnegative(),
18
+ /** How the paging file is set up: Windows manages it, a fixed size, or none. */
19
+ pageFile: z.enum(['auto', 'fixed', 'none']).catch('auto'),
20
+ /** Fixed: the total of its maximum sizes (MB). */
21
+ pageFileMaxMb: z.number().int().nonnegative().catch(0),
22
+ /** The disks' queue length now (all disks together); absent when this PC has no such counter. */
23
+ diskQueue: z.number().nonnegative().max(100000).optional().catch(undefined),
24
+ });
25
+ const TIMEOUT_MS = 20000;
26
+ /** The parsed answer of memory-status, or null (not Windows, unreadable, nonsense). */
27
+ export function parseHostMemory(text) {
28
+ try {
29
+ const parsed = HostMemorySchema.safeParse(JSON.parse(text));
30
+ return parsed.success && parsed.data.totalMb > 0 ? parsed.data : null;
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ function command(storageRoot) {
37
+ const tools = hostToolsExe(storageRoot);
38
+ return tools
39
+ ? { file: tools, args: ['memory-status'] }
40
+ : { file: SYSTEM.powershell(), args: nativeScript('memory-status.ps1') };
41
+ }
42
+ /** This PC's memory now, or null when it cannot be read (and always during the self-test: it never asks Windows). */
43
+ export function readHostMemory(storageRoot) {
44
+ if (!isWin || selfTestRunning())
45
+ return null;
46
+ const { file, args } = command(storageRoot);
47
+ const result = spawnSync(file, args, { encoding: 'utf8', windowsHide: true, timeout: TIMEOUT_MS });
48
+ return parseHostMemory(result.stdout || '');
49
+ }
50
+ /** The same without blocking: for the watcher, which must never wait on it. */
51
+ export function readHostMemoryAsync(storageRoot) {
52
+ if (!isWin || selfTestRunning())
53
+ return Promise.resolve(null);
54
+ return new Promise((resolve) => {
55
+ try {
56
+ const { file, args } = command(storageRoot);
57
+ const child = spawn(file, args, { windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] });
58
+ let out = '';
59
+ child.stdout.on('data', (d) => (out += d.toString('utf8').slice(0, 4096)));
60
+ const timer = setTimeout(() => child.kill(), TIMEOUT_MS);
61
+ child.on('error', () => resolve(null));
62
+ child.on('close', () => {
63
+ clearTimeout(timer);
64
+ resolve(parseHostMemory(out));
65
+ });
66
+ }
67
+ catch {
68
+ // Could not even start it: no answer this time.
69
+ resolve(null);
70
+ }
71
+ });
72
+ }
73
+ /** Low = available commit under half the reserve, or available RAM under 2 GB. */
74
+ export function hostPressure(memory, reserveHostMb) {
75
+ if (memory.commitAvailableMb < reserveHostMb / 2)
76
+ return {
77
+ low: true,
78
+ reason: 'this PC has only ' + memory.commitAvailableMb + ' MB of memory (RAM plus paging file) left to hand out',
79
+ };
80
+ if (memory.availableMb < 2048)
81
+ return { low: true, reason: 'this PC has only ' + memory.availableMb + ' MB of free RAM left' };
82
+ return { low: false, reason: '' };
83
+ }
84
+ /** One line for the host memory log. */
85
+ export function hostMemoryLine(memory, at = new Date()) {
86
+ return (at.toISOString() +
87
+ ' | RAM free/total MB ' +
88
+ memory.availableMb +
89
+ '/' +
90
+ memory.totalMb +
91
+ ' | commit free/limit MB ' +
92
+ memory.commitAvailableMb +
93
+ '/' +
94
+ memory.commitLimitMb +
95
+ ' | page file ' +
96
+ memory.pageFile +
97
+ (memory.diskQueue === undefined ? '' : ' | disk queue ' + memory.diskQueue));
98
+ }
99
+ /**
100
+ * Whether Windows Sandbox could push this PC past its commit limit (pure): the paging file is fixed or off and the
101
+ * commit limit leaves less than the sandbox's memory plus what this PC already uses. Null when all is well.
102
+ */
103
+ export function pageFileRisk(memory, sandboxMb) {
104
+ if (memory.pageFile === 'auto')
105
+ return null;
106
+ const inUseMb = memory.commitLimitMb - memory.commitAvailableMb;
107
+ const neededMb = inUseMb + sandboxMb;
108
+ if (memory.commitLimitMb >= neededMb)
109
+ return null;
110
+ // The commit limit is RAM plus the paging files: the paging file has to make up the difference.
111
+ const neededGb = Math.max(1, Math.ceil((neededMb - memory.totalMb) / 1024));
112
+ return {
113
+ neededGb,
114
+ text: 'Windows Sandbox takes its memory from this PC as it goes; if this PC runs out, Windows closes the sandbox instead of slowing it down. ' +
115
+ 'Let Windows manage the page file size (System > Advanced system settings > Performance > Settings > Advanced > Virtual memory: "Automatically manage paging file size"), or make the page file at least ' +
116
+ neededGb +
117
+ ' GB.',
118
+ };
119
+ }
120
+ const HINT_EVERY_MS = 24 * 60 * 60 * 1000;
121
+ /** Whether the page file hint may be shown now (once a day at most); records that it was. */
122
+ export function takePageFileHint(storageRoot, now = new Date()) {
123
+ const file = path.join(storageRoot, 'run', 'pagefile-hint.txt');
124
+ try {
125
+ const last = Number(fs.readFileSync(file, 'utf8'));
126
+ if (Number.isFinite(last) && now.getTime() - last < HINT_EVERY_MS)
127
+ return false;
128
+ }
129
+ catch {
130
+ // Never shown: show it.
131
+ }
132
+ try {
133
+ fs.mkdirSync(path.dirname(file), { recursive: true });
134
+ fs.writeFileSync(file, String(now.getTime()));
135
+ }
136
+ catch {
137
+ // Cannot record it: showing it again next time is fine.
138
+ }
139
+ return true;
140
+ }
@@ -5,3 +5,5 @@ export * from './keep-awake.js';
5
5
  export * from './windows-update.js';
6
6
  export * from './crash-report.js';
7
7
  export * from './session-ledger.js';
8
+ export * from './host-memory.js';
9
+ export * from './watchdog.js';
@@ -5,3 +5,5 @@ export * from './keep-awake.js';
5
5
  export * from './windows-update.js';
6
6
  export * from './crash-report.js';
7
7
  export * from './session-ledger.js';
8
+ export * from './host-memory.js';
9
+ export * from './watchdog.js';
@@ -12,6 +12,8 @@ export interface StopCause {
12
12
  at: string;
13
13
  text: string;
14
14
  }
15
+ /** The stop code in Kernel-Power 41's detail ("BugcheckCode 209" -> "0xD1"); null for 0 (no blue screen). */
16
+ export declare function bugcheckCode(detail: string): string | null;
15
17
  /**
16
18
  * The first event that ends a running sandbox (pure): Windows inside the sandbox crashing (Hyper-V 18590; `process`
17
19
  * holds its message with the stop code), a restart or shutdown, a crash or power loss, sleep, low memory. Switching
@@ -12,12 +12,17 @@ const StopEventsSchema = z
12
12
  provider: z.string().max(200),
13
13
  process: z
14
14
  .string()
15
- .max(400)
15
+ .max(1600)
16
16
  .nullable()
17
17
  .transform((p) => p ?? ''),
18
18
  }))
19
19
  .max(100);
20
20
  const UPDATERS = /^(trustedinstaller|mousocoreworker|musnotification(ux)?|usoclient|wuauclt|tiworker|updateorchestrator)\.exe$/i;
21
+ /** The stop code in Kernel-Power 41's detail ("BugcheckCode 209" -> "0xD1"); null for 0 (no blue screen). */
22
+ export function bugcheckCode(detail) {
23
+ const n = Number(/BugcheckCode\s+(\d+)/.exec(detail)?.[1] ?? 0);
24
+ return n > 0 ? '0x' + n.toString(16).toUpperCase() : null;
25
+ }
21
26
  /**
22
27
  * The first event that ends a running sandbox (pure): Windows inside the sandbox crashing (Hyper-V 18590; `process`
23
28
  * holds its message with the stop code), a restart or shutdown, a crash or power loss, sleep, low memory. Switching
@@ -41,12 +46,24 @@ export function explainStop(events) {
41
46
  },
42
47
  ];
43
48
  }
44
- if ((e.id === 41 && p.includes('kernel-power')) || (e.id === 6008 && p.includes('eventlog')))
49
+ if (e.id === 41 && p.includes('kernel-power')) {
50
+ const code = bugcheckCode(e.process);
51
+ return [
52
+ {
53
+ kind: 'crash',
54
+ at: e.at,
55
+ text: code
56
+ ? 'this PC blue-screened (stop code ' + code + ')'
57
+ : 'Windows stopped unexpectedly (a freeze, a crash, a power cut, or the power button held down)',
58
+ },
59
+ ];
60
+ }
61
+ if (e.id === 6008 && p.includes('eventlog'))
45
62
  return [
46
63
  {
47
64
  kind: 'crash',
48
65
  at: e.at,
49
- text: 'Windows stopped unexpectedly (a crash, a power cut, or the power button held down)',
66
+ text: 'Windows stopped unexpectedly (a freeze, a crash, a power cut, or the power button held down)',
50
67
  },
51
68
  ];
52
69
  if (e.id === 42 && p.includes('kernel-power'))
@@ -1,7 +1,7 @@
1
1
  const ADVICE = {
2
2
  updates: 'Before leaving agents overnight, restart for pending updates first or pause them (Settings > Windows Update > Pause updates).',
3
3
  restart: 'If that was not you, check Settings > Windows Update and Event Viewer > Windows Logs > System.',
4
- crash: 'Event Viewer > Windows Logs > System has the details; a laptop also needs to stay plugged in.',
4
+ crash: 'sandbox logs --crash prints what Windows logged and how much memory this PC had; Event Viewer > Windows Logs > System has the rest. A laptop also needs to stay plugged in.',
5
5
  sleep: 'sandbox.keepAwake keeps this PC awake while a sandbox runs (closing a laptop lid still sleeps it). Check it is on, and that sleep is not forced by a policy.',
6
6
  sandboxCrash: 'C:\\AgentData\\logs\\memory-*.log shows what used memory just before. Fewer builds at once inside the sandbox (e.g. cargo build.jobs) makes it less likely.',
7
7
  memory: 'Give the sandbox less RAM (sandbox.maxMemoryMb, sandbox.reserveHostMb) or run fewer builds at once inside it.',
@@ -0,0 +1,56 @@
1
+ import type os from 'node:os';
2
+ import { type HostThresholds, type LoadProfile } from '../../shared/memory/index.js';
3
+ import { type HostMemory } from './host-memory.js';
4
+ export interface WatchdogState {
5
+ readonly low: boolean;
6
+ readonly calm: number;
7
+ /** Seconds the CPU has been above the limit. */
8
+ readonly cpuHighSeconds: number;
9
+ /** Seconds the disk queue has been above the limit. */
10
+ readonly diskHighSeconds: number;
11
+ /** Time (ms) the last request went out. */
12
+ readonly lastSentAt: number;
13
+ }
14
+ export declare const WATCHDOG_START: WatchdogState;
15
+ /** The request is repeated this often while the PC stays low, and each one lasts a bit longer than that. */
16
+ export declare const RESEND_MS = 30000;
17
+ export declare const REQUEST_SECONDS = 90;
18
+ export interface WatchdogInput {
19
+ /** Null when memory could not be read this time (only the CPU counts then). */
20
+ readonly memory: HostMemory | null;
21
+ /** Total CPU use since the last sample (percent), null when unknown. */
22
+ readonly cpuPercent: number | null;
23
+ readonly intervalSeconds: number;
24
+ readonly nowMs: number;
25
+ readonly reserveHostMb: number;
26
+ readonly profile: LoadProfile;
27
+ }
28
+ export interface WatchdogStep {
29
+ readonly state: WatchdogState;
30
+ /** Send a request to the sandbox now. */
31
+ readonly send: boolean;
32
+ /** Why the PC is low (plain words), empty when it is not. */
33
+ readonly reason: string;
34
+ /** The state changed (the log says so). */
35
+ readonly changed: boolean;
36
+ }
37
+ /**
38
+ * Why this PC counts as low, for these numbers ("" when it does not). Low = available RAM under max(4 GB, 12 % of RAM)
39
+ * or available commit under 15 % of the commit limit or half the reserve (the gentle profile: 6 GB, 15 %, 20 %).
40
+ * `scale` widens the limits (1.25 = the level it must climb back above).
41
+ */
42
+ export declare function lowReason(memory: HostMemory, reserveHostMb: number, t: HostThresholds, scale: number): string;
43
+ /** One watchdog step. Hysteresis: low starts at the limits and ends above 1.25 times them for calmSamples samples. */
44
+ export declare function watchdogStep(prev: WatchdogState, input: WatchdogInput): WatchdogStep;
45
+ /** Total CPU use between two os.cpus() readings (percent). */
46
+ export declare function cpuPercentBetween(a: readonly os.CpuInfo[], b: readonly os.CpuInfo[]): number | null;
47
+ /** One line in <storage>\logs\host-memory-<data>.log, which the crash report reads; the file is kept small. */
48
+ export declare function appendHostMemory(file: string, memory: HostMemory, cpuPercent: number | null): void;
49
+ /** What the launch decided for the watchdog (host-only, in <storage>\run): the load profile and the PC's reserve. */
50
+ export interface LoadSettings {
51
+ readonly profile: LoadProfile;
52
+ readonly reserveHostMb: number;
53
+ }
54
+ export declare function writeLoadSettings(storageRoot: string, dataPath: string, settings: LoadSettings): void;
55
+ /** The settings of the running launch; the defaults (normal profile, 8 GB reserve) when none were written. */
56
+ export declare function readLoadSettings(storageRoot: string, dataPath: string): LoadSettings;
@@ -0,0 +1,124 @@
1
+ import { z } from 'zod';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { HOST_THRESHOLDS } from '../../shared/memory/index.js';
5
+ import { hostMemoryLine } from './host-memory.js';
6
+ export const WATCHDOG_START = {
7
+ low: false,
8
+ calm: 0,
9
+ cpuHighSeconds: 0,
10
+ diskHighSeconds: 0,
11
+ lastSentAt: 0,
12
+ };
13
+ /** The request is repeated this often while the PC stays low, and each one lasts a bit longer than that. */
14
+ export const RESEND_MS = 30000;
15
+ export const REQUEST_SECONDS = 90;
16
+ /**
17
+ * Why this PC counts as low, for these numbers ("" when it does not). Low = available RAM under max(4 GB, 12 % of RAM)
18
+ * or available commit under 15 % of the commit limit or half the reserve (the gentle profile: 6 GB, 15 %, 20 %).
19
+ * `scale` widens the limits (1.25 = the level it must climb back above).
20
+ */
21
+ export function lowReason(memory, reserveHostMb, t, scale) {
22
+ const availableLimit = Math.max(t.availableMinMb, (memory.totalMb * t.availablePercent) / 100) * scale;
23
+ if (memory.availableMb < availableLimit)
24
+ return 'only ' + memory.availableMb + " MB of this PC's RAM is free";
25
+ const commitLimit = Math.max((memory.commitLimitMb * t.commitAvailablePercent) / 100, reserveHostMb / 2) * scale;
26
+ if (memory.commitAvailableMb < commitLimit)
27
+ return 'only ' + memory.commitAvailableMb + ' MB of memory (RAM plus paging file) is left on this PC';
28
+ return '';
29
+ }
30
+ /** One watchdog step. Hysteresis: low starts at the limits and ends above 1.25 times them for calmSamples samples. */
31
+ export function watchdogStep(prev, input) {
32
+ const t = HOST_THRESHOLDS[input.profile];
33
+ let reason = '';
34
+ if (input.memory)
35
+ reason = lowReason(input.memory, input.reserveHostMb, t, 1);
36
+ const cpuHigh = input.cpuPercent !== null && input.cpuPercent >= t.cpuPercent ? prev.cpuHighSeconds + input.intervalSeconds : 0;
37
+ if (!reason && cpuHigh >= t.cpuSeconds)
38
+ reason = "this PC's CPU has been above " + t.cpuPercent + '% for ' + cpuHigh + ' seconds';
39
+ const diskQueue = input.memory?.diskQueue;
40
+ const diskHigh = diskQueue !== undefined && diskQueue >= t.diskQueue ? prev.diskHighSeconds + input.intervalSeconds : 0;
41
+ if (!reason && diskHigh >= t.diskSeconds)
42
+ reason = "this PC's disks have had a queue of " + diskQueue + ' for ' + diskHigh + ' seconds';
43
+ const calmNow = input.memory ? lowReason(input.memory, input.reserveHostMb, t, 1.25) === '' : true;
44
+ let low = prev.low;
45
+ let calm = prev.calm;
46
+ if (reason) {
47
+ low = true;
48
+ calm = 0;
49
+ }
50
+ else if (prev.low) {
51
+ calm = calmNow && cpuHigh === 0 && diskHigh === 0 ? prev.calm + 1 : 0;
52
+ if (calm >= t.calmSamples) {
53
+ low = false;
54
+ calm = 0;
55
+ }
56
+ }
57
+ const send = low && input.nowMs - prev.lastSentAt >= RESEND_MS;
58
+ return {
59
+ state: {
60
+ low,
61
+ calm,
62
+ cpuHighSeconds: cpuHigh,
63
+ diskHighSeconds: diskHigh,
64
+ lastSentAt: send ? input.nowMs : prev.lastSentAt,
65
+ },
66
+ send,
67
+ reason: reason || (low ? 'this PC is still low' : ''),
68
+ changed: low !== prev.low,
69
+ };
70
+ }
71
+ /** Total CPU use between two os.cpus() readings (percent). */
72
+ export function cpuPercentBetween(a, b) {
73
+ let busy = 0;
74
+ let total = 0;
75
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
76
+ const x = a[i].times;
77
+ const y = b[i].times;
78
+ const idle = y.idle - x.idle;
79
+ const all = y.user - x.user + (y.nice - x.nice) + (y.sys - x.sys) + (y.irq - x.irq) + idle;
80
+ busy += all - idle;
81
+ total += all;
82
+ }
83
+ return total > 0 ? Math.round((busy / total) * 100) : null;
84
+ }
85
+ const MAX_LOG_BYTES = 512 * 1024;
86
+ /** One line in <storage>\logs\host-memory-<data>.log, which the crash report reads; the file is kept small. */
87
+ export function appendHostMemory(file, memory, cpuPercent) {
88
+ try {
89
+ fs.mkdirSync(path.dirname(file), { recursive: true });
90
+ try {
91
+ if (fs.statSync(file).size > MAX_LOG_BYTES)
92
+ fs.renameSync(file, file + '.old');
93
+ }
94
+ catch {
95
+ // No log yet.
96
+ }
97
+ fs.appendFileSync(file, hostMemoryLine(memory) + (cpuPercent === null ? '' : ' | CPU ' + cpuPercent + '%') + '\n');
98
+ }
99
+ catch {
100
+ // The log is a courtesy: never a reason to fail.
101
+ }
102
+ }
103
+ const LoadSettingsSchema = z.object({
104
+ profile: z.enum(['normal', 'gentle']),
105
+ reserveHostMb: z.number().int().nonnegative(),
106
+ });
107
+ function loadFile(storageRoot, dataPath) {
108
+ return path.join(storageRoot, 'run', 'load-' + path.basename(dataPath) + '.json');
109
+ }
110
+ export function writeLoadSettings(storageRoot, dataPath, settings) {
111
+ const file = loadFile(storageRoot, dataPath);
112
+ fs.mkdirSync(path.dirname(file), { recursive: true });
113
+ fs.writeFileSync(file, JSON.stringify(settings));
114
+ }
115
+ /** The settings of the running launch; the defaults (normal profile, 8 GB reserve) when none were written. */
116
+ export function readLoadSettings(storageRoot, dataPath) {
117
+ try {
118
+ return LoadSettingsSchema.parse(JSON.parse(fs.readFileSync(loadFile(storageRoot, dataPath), 'utf8')));
119
+ }
120
+ catch {
121
+ // Not written (an older launch) or damaged: the defaults.
122
+ return { profile: 'normal', reserveHostMb: 8192 };
123
+ }
124
+ }