windows-development-sandbox 1.0.5 → 1.4.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 (453) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -8
  3. package/config.example.json +55 -5
  4. package/dist/api.js +2 -1
  5. package/dist/cli/args.d.ts +23 -0
  6. package/dist/cli/args.js +38 -4
  7. package/dist/cli/colors.d.ts +24 -0
  8. package/dist/cli/colors.js +42 -0
  9. package/dist/cli/commands/accounts.js +2 -2
  10. package/dist/cli/commands/agent-commands.d.ts +6 -0
  11. package/dist/cli/commands/agent-commands.js +61 -0
  12. package/dist/cli/commands/context.d.ts +2 -0
  13. package/dist/cli/commands/context.js +5 -1
  14. package/dist/cli/commands/data/clean.d.ts +15 -0
  15. package/dist/cli/commands/data/clean.js +106 -0
  16. package/dist/cli/commands/data/collect.d.ts +11 -0
  17. package/dist/cli/commands/data/collect.js +54 -0
  18. package/dist/cli/commands/data/index.d.ts +4 -0
  19. package/dist/cli/commands/data/index.js +45 -0
  20. package/dist/cli/commands/data/remove.d.ts +2 -0
  21. package/dist/cli/commands/data/remove.js +30 -0
  22. package/dist/cli/commands/index.d.ts +6 -4
  23. package/dist/cli/commands/index.js +6 -4
  24. package/dist/cli/commands/launch.d.ts +26 -2
  25. package/dist/cli/commands/launch.js +256 -42
  26. package/dist/cli/commands/maintenance.d.ts +2 -0
  27. package/dist/cli/commands/maintenance.js +67 -5
  28. package/dist/cli/commands/project.d.ts +29 -0
  29. package/dist/cli/commands/project.js +63 -0
  30. package/dist/cli/commands/projects.d.ts +14 -0
  31. package/dist/cli/commands/projects.js +146 -0
  32. package/dist/cli/commands/repair/checks.d.ts +2 -0
  33. package/dist/cli/commands/{repair.js → repair/checks.js} +19 -181
  34. package/dist/cli/commands/{repair.d.ts → repair/finders.d.ts} +0 -29
  35. package/dist/cli/commands/repair/finders.js +108 -0
  36. package/dist/cli/commands/repair/index.d.ts +12 -0
  37. package/dist/cli/commands/repair/index.js +61 -0
  38. package/dist/cli/commands/repair/types.d.ts +26 -0
  39. package/dist/cli/commands/repair/types.js +3 -0
  40. package/dist/cli/commands/restart.d.ts +6 -0
  41. package/dist/cli/commands/restart.js +85 -0
  42. package/dist/cli/commands/sessions.d.ts +2 -1
  43. package/dist/cli/commands/sessions.js +189 -45
  44. package/dist/cli/commands/setup/checks.d.ts +13 -0
  45. package/dist/cli/commands/setup/checks.js +71 -0
  46. package/dist/cli/commands/{setup.d.ts → setup/facts.d.ts} +1 -15
  47. package/dist/cli/commands/setup/facts.js +60 -0
  48. package/dist/cli/commands/setup/index.d.ts +2 -0
  49. package/dist/cli/commands/setup/index.js +43 -0
  50. package/dist/cli/commands/user-config.d.ts +8 -0
  51. package/dist/cli/commands/user-config.js +41 -0
  52. package/dist/cli/history.d.ts +11 -0
  53. package/dist/cli/history.js +21 -0
  54. package/dist/cli/index.d.ts +7 -1
  55. package/dist/cli/index.js +42 -15
  56. package/dist/cli/manifest.d.ts +8 -0
  57. package/dist/cli/manifest.js +40 -15
  58. package/dist/cli/menu/apps.d.ts +21 -0
  59. package/dist/cli/menu/apps.js +122 -0
  60. package/dist/cli/menu/command-form.d.ts +28 -0
  61. package/dist/cli/menu/command-form.js +219 -0
  62. package/dist/cli/menu/index.d.ts +26 -0
  63. package/dist/cli/menu/index.js +180 -0
  64. package/dist/cli/menu/settings/categories.d.ts +9 -0
  65. package/dist/cli/menu/settings/categories.js +28 -0
  66. package/dist/cli/menu/settings/config-file.d.ts +40 -0
  67. package/dist/cli/menu/settings/config-file.js +71 -0
  68. package/dist/cli/menu/settings/editors.d.ts +30 -0
  69. package/dist/cli/menu/settings/editors.js +305 -0
  70. package/dist/cli/menu/settings/index.d.ts +32 -0
  71. package/dist/cli/menu/settings/index.js +136 -0
  72. package/dist/cli/menu/settings/schema-node.d.ts +62 -0
  73. package/dist/cli/menu/settings/schema-node.js +104 -0
  74. package/dist/cli/menu/several.d.ts +20 -0
  75. package/dist/cli/menu/several.js +102 -0
  76. package/dist/cli/progress.d.ts +9 -0
  77. package/dist/cli/progress.js +49 -0
  78. package/dist/cli/tui/app.d.ts +39 -0
  79. package/dist/cli/tui/app.js +52 -0
  80. package/dist/cli/tui/dialogs.d.ts +57 -0
  81. package/dist/cli/tui/dialogs.js +162 -0
  82. package/dist/cli/tui/frame.d.ts +18 -0
  83. package/dist/cli/tui/frame.js +27 -0
  84. package/dist/cli/tui/index.d.ts +7 -0
  85. package/dist/cli/tui/index.js +8 -0
  86. package/dist/cli/tui/list-screen.d.ts +101 -0
  87. package/dist/cli/tui/list-screen.js +299 -0
  88. package/dist/cli/tui/terminal.d.ts +37 -0
  89. package/dist/cli/tui/terminal.js +89 -0
  90. package/dist/cli/tui/text.d.ts +20 -0
  91. package/dist/cli/tui/text.js +105 -0
  92. package/dist/cli/tui/theme.d.ts +45 -0
  93. package/dist/cli/tui/theme.js +30 -0
  94. package/dist/cli/validate.d.ts +17 -0
  95. package/dist/cli/validate.js +89 -0
  96. package/dist/core/config.js +12 -6
  97. package/dist/core/disk.d.ts +16 -0
  98. package/dist/core/disk.js +55 -0
  99. package/dist/core/local-config.d.ts +23 -0
  100. package/dist/core/local-config.js +79 -7
  101. package/dist/core/project-policy.d.ts +15 -0
  102. package/dist/core/project-policy.js +35 -0
  103. package/dist/core/prompt.d.ts +4 -0
  104. package/dist/core/prompt.js +15 -0
  105. package/dist/core/safe-paths.d.ts +4 -0
  106. package/dist/core/safe-paths.js +37 -0
  107. package/dist/core/schema/apps.d.ts +40 -0
  108. package/dist/core/schema/apps.js +27 -0
  109. package/dist/core/schema/common.d.ts +17 -0
  110. package/dist/core/schema/common.js +15 -0
  111. package/dist/core/schema/config.d.ts +659 -0
  112. package/dist/core/schema/config.js +75 -0
  113. package/dist/core/schema/databases.d.ts +33 -0
  114. package/dist/core/schema/databases.js +35 -0
  115. package/dist/core/schema/harness.d.ts +12 -0
  116. package/dist/core/schema/harness.js +17 -0
  117. package/dist/core/schema/json-schema.d.ts +5 -0
  118. package/dist/core/schema/json-schema.js +25 -0
  119. package/dist/core/schema/profile.d.ts +32 -0
  120. package/dist/core/schema/profile.js +31 -0
  121. package/dist/core/schema/projects.d.ts +9 -0
  122. package/dist/core/schema/projects.js +11 -0
  123. package/dist/core/schema/sandbox.d.ts +78 -0
  124. package/dist/core/schema/sandbox.js +64 -0
  125. package/dist/core/schema/toolchains.d.ts +96 -0
  126. package/dist/core/schema/toolchains.js +88 -0
  127. package/dist/core/schema.d.ts +6 -604
  128. package/dist/core/schema.js +8 -264
  129. package/dist/core/types.d.ts +35 -1
  130. package/dist/core/util.d.ts +9 -0
  131. package/dist/core/util.js +14 -3
  132. package/dist/core/workspace.d.ts +0 -5
  133. package/dist/core/workspace.js +33 -50
  134. package/dist/guest/apps/index.d.ts +10 -0
  135. package/dist/guest/apps/index.js +206 -0
  136. package/dist/guest/apps/resolvers.d.ts +8 -0
  137. package/dist/guest/apps/resolvers.js +90 -0
  138. package/dist/guest/audit-parse.d.ts +7 -0
  139. package/dist/guest/audit-parse.js +24 -0
  140. package/dist/guest/audit.d.ts +4 -0
  141. package/dist/guest/audit.js +47 -0
  142. package/dist/guest/boot-env.d.ts +4 -0
  143. package/dist/guest/boot-env.js +25 -0
  144. package/dist/guest/cmdline.d.ts +12 -0
  145. package/dist/guest/cmdline.js +23 -0
  146. package/dist/guest/daemon/exec.d.ts +5 -0
  147. package/dist/guest/daemon/exec.js +32 -0
  148. package/dist/guest/daemon/project.d.ts +14 -0
  149. package/dist/guest/daemon/project.js +132 -0
  150. package/dist/guest/daemon/relays.d.ts +1 -0
  151. package/dist/guest/daemon/relays.js +18 -0
  152. package/dist/guest/daemon/session.d.ts +13 -0
  153. package/dist/guest/daemon/session.js +139 -0
  154. package/dist/guest/daemon/shutdown.d.ts +1 -0
  155. package/dist/guest/daemon/shutdown.js +21 -0
  156. package/dist/guest/daemon/state.d.ts +17 -0
  157. package/dist/guest/daemon/state.js +31 -0
  158. package/dist/guest/daemon.js +12 -141
  159. package/dist/guest/db.js +18 -3
  160. package/dist/guest/env.d.ts +3 -0
  161. package/dist/guest/env.js +7 -3
  162. package/dist/guest/guide.d.ts +6 -2
  163. package/dist/guest/guide.js +5 -4
  164. package/dist/guest/helpers.d.ts +3 -0
  165. package/dist/guest/helpers.js +26 -0
  166. package/dist/guest/main.js +31 -8
  167. package/dist/guest/netproxy.d.ts +1 -0
  168. package/dist/guest/netproxy.js +100 -0
  169. package/dist/guest/ports.d.ts +6 -0
  170. package/dist/guest/ports.js +22 -0
  171. package/dist/guest/project-context.d.ts +7 -0
  172. package/dist/guest/project-context.js +10 -0
  173. package/dist/guest/project-env.d.ts +20 -0
  174. package/dist/guest/project-env.js +20 -0
  175. package/dist/guest/project-status.d.ts +5 -0
  176. package/dist/guest/project-status.js +17 -0
  177. package/dist/guest/ptyhost.d.ts +0 -18
  178. package/dist/guest/ptyhost.js +17 -65
  179. package/dist/guest/setup/agents.d.ts +5 -2
  180. package/dist/guest/setup/agents.js +22 -8
  181. package/dist/guest/setup/claude.d.ts +0 -4
  182. package/dist/guest/setup/claude.js +59 -5
  183. package/dist/guest/setup/environment.d.ts +13 -3
  184. package/dist/guest/setup/environment.js +14 -10
  185. package/dist/guest/setup/hooks.d.ts +9 -1
  186. package/dist/guest/setup/hooks.js +28 -14
  187. package/dist/guest/setup/network.d.ts +9 -0
  188. package/dist/guest/setup/network.js +97 -4
  189. package/dist/guest/setup/user.js +4 -0
  190. package/dist/guest/toolchains/catalogue.d.ts +4 -0
  191. package/dist/guest/toolchains/catalogue.js +2 -2
  192. package/dist/guest/toolchains/cli.js +25 -17
  193. package/dist/guest/toolchains/common.d.ts +18 -1
  194. package/dist/guest/toolchains/common.js +34 -11
  195. package/dist/guest/toolchains/databases/dynamodb.d.ts +8 -0
  196. package/dist/guest/toolchains/databases/dynamodb.js +52 -0
  197. package/dist/guest/toolchains/{databases.d.ts → databases/index.d.ts} +2 -3
  198. package/dist/guest/toolchains/databases/index.js +62 -0
  199. package/dist/guest/toolchains/databases/mongodb.d.ts +3 -0
  200. package/dist/guest/toolchains/databases/mongodb.js +31 -0
  201. package/dist/guest/toolchains/databases/mysql.d.ts +3 -0
  202. package/dist/guest/toolchains/databases/mysql.js +38 -0
  203. package/dist/guest/toolchains/databases/postgres.d.ts +5 -0
  204. package/dist/guest/toolchains/databases/postgres.js +38 -0
  205. package/dist/guest/toolchains/databases/redis.d.ts +3 -0
  206. package/dist/guest/toolchains/databases/redis.js +16 -0
  207. package/dist/guest/toolchains/databases/server.d.ts +14 -0
  208. package/dist/guest/toolchains/databases/server.js +52 -0
  209. package/dist/guest/toolchains/git.js +25 -16
  210. package/dist/guest/toolchains/go.js +1 -1
  211. package/dist/guest/toolchains/index.d.ts +13 -9
  212. package/dist/guest/toolchains/index.js +40 -82
  213. package/dist/guest/toolchains/java.js +5 -5
  214. package/dist/guest/toolchains/leftovers.d.ts +2 -0
  215. package/dist/guest/toolchains/leftovers.js +26 -0
  216. package/dist/guest/toolchains/managers.js +12 -10
  217. package/dist/guest/toolchains/{binstall.js → native/binstall.js} +6 -9
  218. package/dist/guest/toolchains/native/cpp.d.ts +4 -0
  219. package/dist/guest/toolchains/native/cpp.js +38 -0
  220. package/dist/guest/toolchains/native/dotnet.d.ts +4 -0
  221. package/dist/guest/toolchains/native/dotnet.js +103 -0
  222. package/dist/guest/toolchains/native/mesa.d.ts +7 -0
  223. package/dist/guest/toolchains/native/mesa.js +56 -0
  224. package/dist/guest/toolchains/native/msvc/environment.d.ts +16 -0
  225. package/dist/guest/toolchains/native/msvc/environment.js +57 -0
  226. package/dist/guest/toolchains/native/msvc/index.d.ts +6 -0
  227. package/dist/guest/toolchains/native/msvc/index.js +48 -0
  228. package/dist/guest/toolchains/native/msvc/packages.d.ts +6 -0
  229. package/dist/guest/toolchains/native/msvc/packages.js +156 -0
  230. package/dist/guest/toolchains/native/msvc/tree.d.ts +15 -0
  231. package/dist/guest/toolchains/native/msvc/tree.js +21 -0
  232. package/dist/guest/toolchains/native/rust.d.ts +3 -0
  233. package/dist/guest/toolchains/native/rust.js +45 -0
  234. package/dist/guest/toolchains/node.js +26 -6
  235. package/dist/guest/toolchains/plan.d.ts +24 -0
  236. package/dist/guest/toolchains/plan.js +140 -0
  237. package/dist/guest/toolchains/pwsh.js +1 -1
  238. package/dist/guest/toolchains/python.js +43 -12
  239. package/dist/guest/toolchains/runtimes.d.ts +2 -0
  240. package/dist/guest/toolchains/runtimes.js +6 -8
  241. package/dist/guest/toolchains/scripting/php.d.ts +3 -0
  242. package/dist/guest/toolchains/scripting/php.js +71 -0
  243. package/dist/guest/toolchains/scripting/ruby.d.ts +3 -0
  244. package/dist/guest/toolchains/scripting/ruby.js +47 -0
  245. package/dist/guest/toolchains/scripting/vcruntime.d.ts +7 -0
  246. package/dist/guest/toolchains/scripting/vcruntime.js +45 -0
  247. package/dist/guest/toolchains/vscode-profile.d.ts +30 -0
  248. package/dist/guest/toolchains/vscode-profile.js +69 -0
  249. package/dist/guest/toolchains/vscode.d.ts +17 -0
  250. package/dist/guest/toolchains/vscode.js +87 -1
  251. package/dist/guest/view.js +21 -1
  252. package/dist/host/accounts.js +14 -5
  253. package/dist/host/agent-commands.d.ts +25 -0
  254. package/dist/host/agent-commands.js +145 -0
  255. package/dist/host/attach.d.ts +17 -0
  256. package/dist/host/attach.js +70 -0
  257. package/dist/host/audit/collect.d.ts +34 -0
  258. package/dist/host/audit/collect.js +126 -0
  259. package/dist/host/audit/log.d.ts +46 -0
  260. package/dist/host/audit/log.js +143 -0
  261. package/dist/host/channel.d.ts +23 -22
  262. package/dist/host/channel.js +66 -84
  263. package/dist/host/claude-profile.js +7 -3
  264. package/dist/host/cleanup.js +2 -11
  265. package/dist/host/database-plan.d.ts +12 -0
  266. package/dist/host/database-plan.js +52 -0
  267. package/dist/host/generate.d.ts +10 -12
  268. package/dist/host/generate.js +58 -135
  269. package/dist/host/join.d.ts +28 -0
  270. package/dist/host/join.js +76 -0
  271. package/dist/host/launch/mappings.d.ts +3 -0
  272. package/dist/host/launch/mappings.js +54 -0
  273. package/dist/host/launch/projects.d.ts +15 -0
  274. package/dist/host/launch/projects.js +56 -0
  275. package/dist/host/launch/stage.d.ts +11 -0
  276. package/dist/host/launch/stage.js +66 -0
  277. package/dist/host/launch/wsb.d.ts +10 -0
  278. package/dist/host/launch/wsb.js +34 -0
  279. package/dist/host/motw.d.ts +17 -0
  280. package/dist/host/motw.js +74 -0
  281. package/dist/host/pc-scan.d.ts +6 -0
  282. package/dist/host/pc-scan.js +37 -0
  283. package/dist/host/platform.d.ts +2 -0
  284. package/dist/host/platform.js +16 -1
  285. package/dist/host/port-forward.d.ts +8 -0
  286. package/dist/host/port-forward.js +37 -0
  287. package/dist/host/project-scan.d.ts +1 -17
  288. package/dist/host/project-scan.js +2 -86
  289. package/dist/host/projects.d.ts +100 -0
  290. package/dist/host/projects.js +147 -0
  291. package/dist/host/repo-guard.d.ts +25 -5
  292. package/dist/host/repo-guard.js +242 -23
  293. package/dist/host/stops.d.ts +67 -0
  294. package/dist/host/stops.js +126 -0
  295. package/dist/host/terminal.d.ts +8 -0
  296. package/dist/host/terminal.js +48 -1
  297. package/dist/host/test-guard.d.ts +6 -0
  298. package/dist/host/test-guard.js +15 -0
  299. package/dist/host/window.js +7 -3
  300. package/dist/host/workspace-view/index.d.ts +20 -0
  301. package/dist/host/workspace-view/index.js +53 -0
  302. package/dist/host/workspace-view/rules.d.ts +26 -0
  303. package/dist/host/workspace-view/rules.js +93 -0
  304. package/dist/host/workspace-view/view.d.ts +50 -0
  305. package/dist/host/workspace-view/view.js +281 -0
  306. package/dist/host/wsb.d.ts +37 -0
  307. package/dist/host/wsb.js +101 -0
  308. package/dist/runtime/daemon.js +11791 -2821
  309. package/dist/runtime/db.js +6585 -466
  310. package/dist/runtime/main.js +10238 -1724
  311. package/dist/runtime/netproxy.js +25936 -0
  312. package/dist/runtime/view.js +6183 -59
  313. package/dist/selftest/accounts.test.d.ts +2 -0
  314. package/dist/selftest/accounts.test.js +44 -0
  315. package/dist/selftest/agent-commands.test.d.ts +2 -0
  316. package/dist/selftest/agent-commands.test.js +47 -0
  317. package/dist/selftest/audit.test.d.ts +2 -0
  318. package/dist/selftest/audit.test.js +57 -0
  319. package/dist/selftest/channel.test.d.ts +2 -0
  320. package/dist/selftest/channel.test.js +44 -0
  321. package/dist/selftest/cli.test.d.ts +2 -2
  322. package/dist/selftest/cli.test.js +145 -27
  323. package/dist/selftest/cmdline.test.d.ts +2 -0
  324. package/dist/selftest/cmdline.test.js +45 -0
  325. package/dist/selftest/config.test.d.ts +2 -3
  326. package/dist/selftest/config.test.js +36 -72
  327. package/dist/selftest/data.test.d.ts +2 -2
  328. package/dist/selftest/data.test.js +77 -70
  329. package/dist/selftest/detect.test.d.ts +2 -2
  330. package/dist/selftest/detect.test.js +54 -38
  331. package/dist/selftest/fixtures.d.ts +89 -4
  332. package/dist/selftest/fixtures.js +43 -24
  333. package/dist/selftest/generate.test.d.ts +2 -0
  334. package/dist/selftest/generate.test.js +68 -0
  335. package/dist/selftest/git-access.test.d.ts +2 -0
  336. package/dist/selftest/git-access.test.js +26 -0
  337. package/dist/selftest/guest-boot.test.d.ts +2 -0
  338. package/dist/selftest/guest-boot.test.js +76 -0
  339. package/dist/selftest/harness.d.ts +44 -7
  340. package/dist/selftest/harness.js +62 -21
  341. package/dist/selftest/host-sim.test.d.ts +2 -0
  342. package/dist/selftest/host-sim.test.js +93 -0
  343. package/dist/selftest/hostile-input.test.d.ts +2 -0
  344. package/dist/selftest/hostile-input.test.js +54 -0
  345. package/dist/selftest/index.d.ts +8 -1
  346. package/dist/selftest/index.js +137 -35
  347. package/dist/selftest/links.test.d.ts +2 -0
  348. package/dist/selftest/links.test.js +50 -0
  349. package/dist/selftest/live-daemon.test.d.ts +2 -0
  350. package/dist/selftest/live-daemon.test.js +91 -0
  351. package/dist/selftest/live-git.test.d.ts +2 -0
  352. package/dist/selftest/live-git.test.js +60 -0
  353. package/dist/selftest/live-network.test.d.ts +2 -0
  354. package/dist/selftest/live-network.test.js +63 -0
  355. package/dist/selftest/live-projects.test.d.ts +2 -0
  356. package/dist/selftest/live-projects.test.js +127 -0
  357. package/dist/selftest/live.d.ts +65 -0
  358. package/dist/selftest/live.js +109 -0
  359. package/dist/selftest/local-config.test.d.ts +2 -0
  360. package/dist/selftest/local-config.test.js +120 -0
  361. package/dist/selftest/mirror.test.d.ts +2 -3
  362. package/dist/selftest/mirror.test.js +92 -65
  363. package/dist/selftest/network.test.d.ts +2 -0
  364. package/dist/selftest/network.test.js +40 -0
  365. package/dist/selftest/personalization.test.d.ts +2 -0
  366. package/dist/selftest/personalization.test.js +53 -0
  367. package/dist/selftest/projects.test.d.ts +2 -0
  368. package/dist/selftest/projects.test.js +254 -0
  369. package/dist/selftest/repair.test.d.ts +2 -0
  370. package/dist/selftest/repair.test.js +42 -0
  371. package/dist/selftest/repo-guard.test.d.ts +2 -0
  372. package/dist/selftest/repo-guard.test.js +121 -0
  373. package/dist/selftest/secrets.test.d.ts +2 -0
  374. package/dist/selftest/secrets.test.js +71 -0
  375. package/dist/selftest/setup.test.d.ts +2 -2
  376. package/dist/selftest/setup.test.js +45 -22
  377. package/dist/selftest/suite.d.ts +13 -0
  378. package/dist/selftest/suite.js +1 -0
  379. package/dist/selftest/sync.test.d.ts +2 -3
  380. package/dist/selftest/sync.test.js +130 -78
  381. package/dist/selftest/test-guard.test.d.ts +2 -0
  382. package/dist/selftest/test-guard.test.js +49 -0
  383. package/dist/selftest/toolchains.test.d.ts +2 -0
  384. package/dist/selftest/toolchains.test.js +133 -0
  385. package/dist/selftest/workspace-view.test.d.ts +2 -0
  386. package/dist/selftest/workspace-view.test.js +89 -0
  387. package/dist/selftest/workspace.test.d.ts +2 -0
  388. package/dist/selftest/workspace.test.js +53 -0
  389. package/dist/shared/apps.d.ts +49 -0
  390. package/dist/shared/apps.js +114 -0
  391. package/dist/shared/download.d.ts +3 -1
  392. package/dist/shared/download.js +19 -4
  393. package/dist/shared/netpolicy.d.ts +27 -0
  394. package/dist/shared/netpolicy.js +51 -0
  395. package/dist/shared/paths.d.ts +5 -0
  396. package/dist/shared/paths.js +6 -0
  397. package/dist/shared/projects.d.ts +53 -0
  398. package/dist/shared/projects.js +67 -0
  399. package/dist/shared/pty-client.d.ts +9 -4
  400. package/dist/shared/pty-client.js +139 -17
  401. package/dist/shared/rundir.d.ts +12 -1
  402. package/dist/shared/rundir.js +14 -4
  403. package/dist/shared/safe-remove.js +11 -0
  404. package/dist/shared/schema.d.ts +453 -0
  405. package/dist/shared/schema.js +43 -2
  406. package/dist/shared/system.d.ts +1 -0
  407. package/dist/shared/system.js +1 -0
  408. package/dist/shared/test-guard.d.ts +4 -0
  409. package/dist/shared/test-guard.js +37 -0
  410. package/dist/sync/claude-mem.js +6 -3
  411. package/dist/sync/history.d.ts +29 -12
  412. package/dist/sync/history.js +72 -25
  413. package/dist/sync/watcher.d.ts +8 -0
  414. package/dist/sync/watcher.js +99 -8
  415. package/package.json +7 -6
  416. package/sandbox/bootstrap.ps1 +258 -258
  417. package/sandbox/cargoshim.cs +56 -0
  418. package/sandbox/cpucap.cs +33 -0
  419. package/sandbox/cpucap.ps1 +20 -0
  420. package/sandbox/logon.cmd +24 -16
  421. package/sandbox/memlog.ps1 +34 -0
  422. package/sandbox/openssl.cs +42 -0
  423. package/sandbox/pagefile.cs +31 -0
  424. package/sandbox/pagefile.ps1 +21 -0
  425. package/sandbox/ptyhost.cs +120 -11
  426. package/sandbox/trust.cmd +35 -35
  427. package/schemas/config.schema.json +403 -3
  428. package/schemas/sandbox-config.schema.json +143 -13
  429. package/dist/cli/commands/data.d.ts +0 -46
  430. package/dist/cli/commands/data.js +0 -249
  431. package/dist/cli/commands/setup.js +0 -152
  432. package/dist/cli/menu.d.ts +0 -22
  433. package/dist/cli/menu.js +0 -187
  434. package/dist/cli/settings.d.ts +0 -11
  435. package/dist/cli/settings.js +0 -156
  436. package/dist/guest/toolchains/databases.js +0 -226
  437. package/dist/guest/toolchains/msvc.d.ts +0 -10
  438. package/dist/guest/toolchains/msvc.js +0 -216
  439. package/dist/guest/toolchains/native.d.ts +0 -12
  440. package/dist/guest/toolchains/native.js +0 -220
  441. package/dist/guest/toolchains/scripting.d.ts +0 -10
  442. package/dist/guest/toolchains/scripting.js +0 -137
  443. package/dist/selftest/guest.test.d.ts +0 -3
  444. package/dist/selftest/guest.test.js +0 -61
  445. package/dist/selftest/helpers.test.d.ts +0 -2
  446. package/dist/selftest/helpers.test.js +0 -125
  447. package/dist/selftest/host.test.d.ts +0 -2
  448. package/dist/selftest/host.test.js +0 -71
  449. package/dist/selftest/live.test.d.ts +0 -7
  450. package/dist/selftest/live.test.js +0 -127
  451. package/dist/selftest/security.test.d.ts +0 -3
  452. package/dist/selftest/security.test.js +0 -143
  453. /package/dist/guest/toolchains/{binstall.d.ts → native/binstall.d.ts} +0 -0
@@ -0,0 +1,206 @@
1
+ // Installs the apps you chose (apps.install from the catalogue, apps.custom of your own) into C:\AgentData\tools\apps,
2
+ // once each: later sandboxes find them there. Programs get a desktop and Start menu shortcut; command-line tools go on
3
+ // PATH. Installers are never run: archives are unpacked, NSIS installers opened with 7-Zip and MSIs extracted
4
+ // (msiexec /a), so nothing is registered and nothing is lost when the sandbox closes. One app failing never stops the
5
+ // others.
6
+ import fs from 'node:fs';
7
+ import os from 'node:os';
8
+ import path from 'node:path';
9
+ import { appsToInstall, exeMatcher } from '../../shared/apps.js';
10
+ import { download, extractArchive, githubAsset, moveIntoPlace, stagingFolder } from '../../shared/download.js';
11
+ import { safeRemove } from '../../shared/safe-remove.js';
12
+ import { SYSTEM } from '../../shared/system.js';
13
+ import { dryRun, errMsg, execAsync, exists, log, mkdirp, powershell, warn } from '../env.js';
14
+ import { arch, tool } from '../toolchains/common.js';
15
+ import { RESOLVERS } from './resolvers.js';
16
+ export { appsNeedMesa } from '../../shared/apps.js';
17
+ const MARKER = '.agent-sandbox-app';
18
+ /** How many apps download and unpack at once (they are big: a few at a time keeps the disk and network busy). */
19
+ const PARALLEL = 3;
20
+ const sevenZip = () => tool('apps', '7zip', '7z.exe');
21
+ /** Where to download an entry from. */
22
+ const resolve = async (entry) => {
23
+ const s = entry.source;
24
+ if ('github' in s) {
25
+ const a = await githubAsset(s.github, new RegExp(s.asset), true);
26
+ return { url: a.url, fileName: a.name, sha256: a.sha256 };
27
+ }
28
+ if ('url' in s) {
29
+ const name = decodeURIComponent(new URL(s.url).pathname.split('/').pop() || entry.id);
30
+ return { url: s.url, fileName: /\.\w{2,5}$/.test(name) ? name : name + '.zip' };
31
+ }
32
+ if ('resolve' in s && RESOLVERS[s.resolve])
33
+ return RESOLVERS[s.resolve]();
34
+ throw new Error('no download source for ' + entry.name);
35
+ };
36
+ /** The program in an installed folder (breadth first, bounded: big apps have tens of thousands of files). */
37
+ export const findExe = (root, exe) => {
38
+ const match = exeMatcher(exe);
39
+ let queue = [root];
40
+ let seen = 0;
41
+ for (let depth = 0; depth < 7 && queue.length; depth++) {
42
+ const next = [];
43
+ for (const dir of queue) {
44
+ let entries;
45
+ try {
46
+ entries = fs.readdirSync(dir, { withFileTypes: true });
47
+ }
48
+ catch {
49
+ continue;
50
+ }
51
+ const hit = entries.find((e) => e.isFile() && match(e.name));
52
+ if (hit)
53
+ return path.join(dir, hit.name);
54
+ next.push(...entries.filter((e) => e.isDirectory() && !e.isSymbolicLink()).map((e) => path.join(dir, e.name)));
55
+ if ((seen += entries.length) > 50000)
56
+ return null;
57
+ }
58
+ queue = next;
59
+ }
60
+ return null;
61
+ };
62
+ /** Downloads and unpacks one entry into its folder (replaced in one step: never half there). */
63
+ const installApp = async (entry, dest) => {
64
+ const work = stagingFolder(dest);
65
+ const out = path.join(work, 'out');
66
+ try {
67
+ if ('npm' in entry.source) {
68
+ const npmCli = path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
69
+ log('Installing ' + entry.name + ' (npm) ...');
70
+ await execAsync(process.execPath, [npmCli, 'install', '--prefix', mkdirp(out), '--no-audit', '--no-fund', '--loglevel=error', entry.source.npm]);
71
+ }
72
+ else {
73
+ const d = await resolve(entry);
74
+ log('Downloading ' + entry.name + ' (' + d.fileName + ', ~' + entry.size + ' once) ...');
75
+ const file = path.join(mkdirp(work), d.fileName.replace(/[\\/:*?"<>|]/g, '_'));
76
+ await download(d.url, file, { sha256: d.sha256 ?? null });
77
+ await unpack(entry, file, out, d);
78
+ }
79
+ fs.writeFileSync(path.join(out, MARKER), JSON.stringify({ id: entry.id, at: new Date().toISOString() }));
80
+ moveIntoPlace(out, dest);
81
+ }
82
+ finally {
83
+ safeRemove(work, path.dirname(work));
84
+ }
85
+ };
86
+ const unpack = async (entry, file, out, d) => {
87
+ if (entry.id === '7zip') {
88
+ // 7zr.exe (the small standalone 7-Zip) opens the full 7-Zip's installer.
89
+ const sevenZr = path.join(path.dirname(file), '7zr.exe');
90
+ const zr = await githubAsset('ip7z/7zip', /^7zr\.exe$/);
91
+ await download(zr.url, sevenZr, zr.sha256);
92
+ await execAsync(sevenZr, ['x', '-y', '-o' + out, file], { stdio: 'ignore' });
93
+ return;
94
+ }
95
+ switch (entry.unpack ?? 'archive') {
96
+ case 'file': {
97
+ mkdirp(out);
98
+ fs.renameSync(file, path.join(out, /^\//.test(entry.exe) ? path.basename(file) : entry.exe));
99
+ return;
100
+ }
101
+ case 'nsis': {
102
+ if (!exists(sevenZip()))
103
+ throw new Error('7-Zip is needed to unpack ' + entry.name + ' and could not be installed');
104
+ const raw = path.join(path.dirname(out), 'raw');
105
+ await execAsync(sevenZip(), ['x', '-y', '-o' + raw, file], { stdio: 'ignore' });
106
+ // electron-builder installers carry the app as one more archive inside.
107
+ const inner = findExe(raw, '/^app-64\\.7z$/');
108
+ if (inner)
109
+ await execAsync(sevenZip(), ['x', '-y', '-o' + out, inner], { stdio: 'ignore' });
110
+ else
111
+ fs.renameSync(raw, out);
112
+ return;
113
+ }
114
+ case 'msi': {
115
+ // An administrative install only extracts the files: nothing is registered with Windows.
116
+ mkdirp(out);
117
+ await execAsync(SYSTEM.msiexec(), ['/a', file, '/qn', 'TARGETDIR=' + out], { stdio: 'ignore' });
118
+ return;
119
+ }
120
+ default:
121
+ await extractArchive(file, out, d.members);
122
+ }
123
+ };
124
+ /** Settings an app needs on every boot (the sandbox's registry is new each time), and files next to its program. */
125
+ const afterInstall = async (entry, exe) => {
126
+ const dir = path.dirname(exe);
127
+ if (entry.needs?.includes('mesa')) {
128
+ const mesa = tool('mesa', arch === 'arm64' ? 'arm64' : 'x64');
129
+ for (const dll of ['opengl32.dll', 'libgallium_wgl.dll', 'dxil.dll']) {
130
+ if (exists(path.join(mesa, dll)) && !exists(path.join(dir, dll)))
131
+ fs.copyFileSync(path.join(mesa, dll), path.join(dir, dll));
132
+ }
133
+ }
134
+ // Godot and Sublime keep their settings next to the program instead of in the profile, which is wiped on close.
135
+ if (entry.id === 'godot' && !exists(path.join(dir, '._sc_')))
136
+ fs.writeFileSync(path.join(dir, '._sc_'), '');
137
+ if (entry.id === 'sublime')
138
+ mkdirp(path.join(dir, 'Data'));
139
+ if (entry.id === 'sysinternals') {
140
+ for (const name of ['Process Explorer', 'Process Monitor', 'Autoruns', 'TCPView', 'PsExec', 'PsList', 'PsKill', 'Handle', 'ListDLLs', 'Sigcheck', 'Strings', 'VMMap', 'RAMMap', 'DebugView', 'Disk2vhd', 'Du', 'ZoomIt']) {
141
+ await execAsync(SYSTEM.reg(), ['add', 'HKCU\\Software\\Sysinternals\\' + name, '/v', 'EulaAccepted', '/t', 'REG_DWORD', '/d', '1', '/f'], { stdio: 'ignore' }).catch(() => undefined);
142
+ }
143
+ }
144
+ };
145
+ /** Desktop and Start menu shortcuts, all in one PowerShell call. */
146
+ const shortcuts = (apps) => {
147
+ if (!apps.length)
148
+ return;
149
+ const q = (s) => "'" + s.replace(/'/g, "''") + "'";
150
+ const desktop = path.join(os.homedir(), 'Desktop');
151
+ const start = path.join(process.env.APPDATA ?? path.join(os.homedir(), 'AppData', 'Roaming'), 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Sandbox apps');
152
+ const lines = ['$w = New-Object -ComObject WScript.Shell', 'New-Item -ItemType Directory -Force -Path ' + q(start) + ' | Out-Null'];
153
+ for (const app of apps) {
154
+ for (const folder of [desktop, start]) {
155
+ const name = app.name.replace(/[\\/:*?"<>|]/g, '');
156
+ lines.push('$s = $w.CreateShortcut(' + q(path.join(folder, name + '.lnk')) + '); $s.TargetPath = ' + q(app.exe) + '; $s.WorkingDirectory = ' + q(path.dirname(app.exe)) + '; $s.Save()');
157
+ }
158
+ }
159
+ powershell(lines.join('\n'));
160
+ };
161
+ export const setupApps = async (env, apps, o) => {
162
+ const { entries, skipped } = appsToInstall(apps, o.gitAccess);
163
+ for (const s of skipped)
164
+ env.summary.push('app skipped: ' + s);
165
+ if (dryRun) {
166
+ log('(dry run) would install apps: ' + entries.map((e) => e.name).join(', '));
167
+ return;
168
+ }
169
+ const gui = [];
170
+ const installed = [];
171
+ const one = async (entry) => {
172
+ try {
173
+ const dest = tool('apps', ...entry.id.split('/'));
174
+ if (!exists(path.join(dest, MARKER)) || o.refresh)
175
+ await installApp(entry, dest);
176
+ const exe = findExe(dest, entry.exe);
177
+ if (!exe)
178
+ throw new Error(entry.exe + ' not found in the download');
179
+ await afterInstall(entry, exe);
180
+ if (entry.cli || entry.id === 'sysinternals')
181
+ env.prepend.push(path.dirname(exe));
182
+ if (!entry.cli)
183
+ gui.push({ name: entry.name, exe });
184
+ installed.push(entry.name);
185
+ }
186
+ catch (e) {
187
+ warn(entry.name + ' could not be installed and is skipped; the other apps are not affected. Reason: ' + errMsg(e));
188
+ env.summary.push(entry.name + ': FAILED');
189
+ }
190
+ };
191
+ // 7-Zip first: the NSIS apps need it to unpack.
192
+ const [first, rest] = entries[0]?.id === '7zip' ? [[entries[0]], entries.slice(1)] : [[], entries];
193
+ for (const e of first)
194
+ await one(e);
195
+ const queue = [...rest];
196
+ await Promise.all(Array.from({ length: Math.min(PARALLEL, queue.length) }, async () => { for (let e = queue.shift(); e; e = queue.shift())
197
+ await one(e); }));
198
+ try {
199
+ shortcuts(gui);
200
+ }
201
+ catch (e) {
202
+ warn('App shortcuts could not be created: ' + errMsg(e));
203
+ }
204
+ if (installed.length)
205
+ env.summary.push('apps: ' + installed.join(', ') + ' (shortcuts on the desktop and in Start > Sandbox apps)');
206
+ };
@@ -0,0 +1,8 @@
1
+ export interface Download {
2
+ url: string;
3
+ fileName: string;
4
+ sha256?: string | null;
5
+ /** Only these paths of the archive (pgAdmin out of the PostgreSQL zip). */
6
+ members?: string[];
7
+ }
8
+ export declare const RESOLVERS: Record<string, () => Promise<Download>>;
@@ -0,0 +1,90 @@
1
+ // Where the latest version of an app is, for apps that are not a plain GitHub release: each looks it up on the
2
+ // vendor's own site (and its published checksum, where there is one). Named by AppSource.resolve in shared/apps.ts.
3
+ import { fetchJson, fetchText, githubAsset, latestGithubTag } from '../../shared/download.js';
4
+ /** The SHA-256 on the line of a SHA256SUMS-style file that names `file`. */
5
+ const sumFor = (sums, file) => sums.split(/\r?\n/).map((l) => /^([0-9a-f]{64})\s+\*?(.+)$/i.exec(l.trim())).find((m) => m && m[2].trim().endsWith(file))?.[1].toLowerCase() ?? null;
6
+ const firstHash = (text) => /\b([0-9a-f]{64})\b/i.exec(text)?.[1].toLowerCase() ?? null;
7
+ const stripV = (tag) => tag.replace(/^v/, '');
8
+ export const RESOLVERS = {
9
+ sublime: async () => {
10
+ // Sublime publishes no checksums; the download is HTTPS from sublimetext.com.
11
+ const { latest_version: build } = await fetchJson('https://www.sublimetext.com/updates/4/stable_update_check');
12
+ return { url: 'https://download.sublimetext.com/sublime_text_build_' + build + '_x64.zip', fileName: 'sublime_text_' + build + '.zip' };
13
+ },
14
+ pgadmin: async () => {
15
+ // pgAdmin is only an installer on pgadmin.org; EnterpriseDB's PostgreSQL binaries zip has a portable copy.
16
+ const versions = (await fetchJson('https://www.postgresql.org/versions.json')).filter((v) => v.supported);
17
+ const current = versions.find((v) => v.current) ?? versions[versions.length - 1];
18
+ const exact = current.major + '.' + current.latestMinor;
19
+ return { url: 'https://get.enterprisedb.com/postgresql/postgresql-' + exact + '-1-windows-x64-binaries.zip', fileName: 'postgresql-' + exact + '.zip', members: ['pgsql/pgAdmin 4'] };
20
+ },
21
+ everything: async () => {
22
+ const page = await fetchText('https://www.voidtools.com/downloads/');
23
+ const version = /Everything-([\d.]+)\.x64\.zip/.exec(page)?.[1];
24
+ if (!version)
25
+ throw new Error('could not find the Everything download on voidtools.com');
26
+ const file = 'Everything-' + version + '.x64.zip';
27
+ const sums = await fetchText('https://www.voidtools.com/Everything-' + version + '.sha256').catch(() => '');
28
+ return { url: 'https://www.voidtools.com/' + file, fileName: file, sha256: sumFor(sums, file) };
29
+ },
30
+ chrome: async () => {
31
+ const data = await fetchJson('https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json');
32
+ const hit = data.channels.Stable.downloads.chrome.find((d) => d.platform === 'win64');
33
+ if (!hit)
34
+ throw new Error('no win64 Chrome for Testing build listed');
35
+ return { url: hit.url, fileName: 'chrome-win64.zip' };
36
+ },
37
+ firefox: async () => {
38
+ const { LATEST_FIREFOX_VERSION: v } = await fetchJson('https://product-details.mozilla.org/1.0/firefox_versions.json');
39
+ const base = 'https://archive.mozilla.org/pub/firefox/releases/' + v + '/';
40
+ const sums = await fetchText(base + 'SHA256SUMS');
41
+ return { url: base + 'win64/en-US/Firefox%20Setup%20' + v + '.exe', fileName: 'firefox-setup-' + v + '.exe', sha256: sumFor(sums, 'win64/en-US/Firefox Setup ' + v + '.exe') };
42
+ },
43
+ blender: async () => {
44
+ const releases = await fetchText('https://download.blender.org/release/');
45
+ const series = [...releases.matchAll(/href="Blender(\d+)\.(\d+)\/"/g)].map((m) => [Number(m[1]), Number(m[2])]).sort((a, b) => a[0] - b[0] || a[1] - b[1]).pop();
46
+ if (!series)
47
+ throw new Error('could not list Blender releases');
48
+ const folder = 'https://download.blender.org/release/Blender' + series[0] + '.' + series[1] + '/';
49
+ const listing = await fetchText(folder);
50
+ const files = [...listing.matchAll(/href="(blender-([\d.]+)-windows-x64\.zip)"/g)].map((m) => ({ file: m[1], v: m[2].split('.').map(Number) }));
51
+ const latest = files.sort((a, b) => a.v[2] - b.v[2]).pop();
52
+ if (!latest)
53
+ throw new Error('no Windows build in ' + folder);
54
+ const sums = await fetchText(folder + 'blender-' + latest.v.join('.') + '.sha256').catch(() => '');
55
+ return { url: folder + latest.file, fileName: latest.file, sha256: sumFor(sums, latest.file) };
56
+ },
57
+ kubectl: async () => {
58
+ const v = (await fetchText('https://dl.k8s.io/release/stable.txt')).trim();
59
+ const url = 'https://dl.k8s.io/release/' + v + '/bin/windows/amd64/kubectl.exe';
60
+ return { url, fileName: 'kubectl.exe', sha256: firstHash(await fetchText(url + '.sha256')) };
61
+ },
62
+ helm: async () => {
63
+ const v = await latestGithubTag('helm/helm');
64
+ const url = 'https://get.helm.sh/helm-' + v + '-windows-amd64.zip';
65
+ return { url, fileName: 'helm-' + v + '.zip', sha256: firstHash(await fetchText(url + '.sha256sum')) };
66
+ },
67
+ terraform: async () => {
68
+ const { current_version: v } = await fetchJson('https://checkpoint-api.hashicorp.com/v1/check/terraform');
69
+ const base = 'https://releases.hashicorp.com/terraform/' + v + '/';
70
+ const file = 'terraform_' + v + '_windows_amd64.zip';
71
+ return { url: base + file, fileName: file, sha256: sumFor(await fetchText(base + 'terraform_' + v + '_SHA256SUMS'), file) };
72
+ },
73
+ mitmproxy: async () => {
74
+ // mitmproxy's GitHub releases have no files; its own download server has them (no published checksums).
75
+ const v = stripV(await latestGithubTag('mitmproxy/mitmproxy'));
76
+ return { url: 'https://downloads.mitmproxy.org/' + v + '/mitmproxy-' + v + '-windows-x86_64.zip', fileName: 'mitmproxy-' + v + '.zip' };
77
+ },
78
+ sqlite: async () => {
79
+ // The download page lists every file in a CSV comment: PRODUCT,<version>,<year>/<file>,<size>,<sha3>.
80
+ const page = await fetchText('https://sqlite.org/download.html');
81
+ const file = /PRODUCT,[^,]+,(\d{4}\/sqlite-tools-win-x64-\d+\.zip),/.exec(page)?.[1];
82
+ if (!file)
83
+ throw new Error('could not find the sqlite tools download on sqlite.org');
84
+ return { url: 'https://sqlite.org/' + file, fileName: file.split('/')[1] };
85
+ },
86
+ '7zip': async () => {
87
+ const asset = await githubAsset('ip7z/7zip', /^7z\d+-x64\.exe$/);
88
+ return { url: asset.url, fileName: asset.name, sha256: asset.sha256 };
89
+ },
90
+ };
@@ -0,0 +1,7 @@
1
+ /** Background programs of Windows itself: never what an agent ran. */
2
+ export declare const NOISE: Set<string>;
3
+ /** The events in wevtutil's XML output as audit lines, with the newest record id seen. */
4
+ export declare const parseProcessEvents: (xml: string) => {
5
+ lines: string[];
6
+ last: number;
7
+ };
@@ -0,0 +1,24 @@
1
+ // Reading Windows process-creation events (Security log, 4688, as wevtutil prints them) into audit lines. Pure, so
2
+ // the self-test can check it on the PC side.
3
+ import path from 'node:path';
4
+ /** Background programs of Windows itself: never what an agent ran. */
5
+ export const NOISE = new Set(['conhost.exe', 'svchost.exe', 'backgroundtaskhost.exe', 'runtimebroker.exe', 'wmiprvse.exe', 'dllhost.exe', 'searchprotocolhost.exe', 'searchfilterhost.exe', 'taskhostw.exe', 'sppsvc.exe', 'wermgr.exe', 'consent.exe', 'smartscreen.exe', 'mpcmdrun.exe', 'compattelrunner.exe', 'wevtutil.exe', 'audiodg.exe', 'ctfmon.exe', 'sihost.exe', 'werfault.exe', 'msmpeng.exe', 'nissrv.exe', 'usoclient.exe', 'musnotification.exe']);
6
+ const decode = (s) => s.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, '&');
7
+ const field = (event, name) => decode(new RegExp("<Data Name='" + name + "'>([^<]*)</Data>").exec(event)?.[1] ?? '');
8
+ /** The events in wevtutil's XML output as audit lines, with the newest record id seen. */
9
+ export const parseProcessEvents = (xml) => {
10
+ const lines = [];
11
+ let last = 0;
12
+ for (const event of xml.split('</Event>')) {
13
+ const id = Number(/<EventRecordID>(\d+)<\/EventRecordID>/.exec(event)?.[1] ?? 0);
14
+ if (!id)
15
+ continue;
16
+ last = Math.max(last, id);
17
+ const program = field(event, 'NewProcessName');
18
+ if (!program || NOISE.has(path.win32.basename(program).toLowerCase()))
19
+ continue;
20
+ const t = /SystemTime='([^']+)'/.exec(event)?.[1] ?? new Date().toISOString();
21
+ lines.push(JSON.stringify({ t, k: 'proc', p: program.slice(0, 1024), c: field(event, 'CommandLine').slice(0, 2000), pp: field(event, 'ParentProcessName').slice(0, 1024) }));
22
+ }
23
+ return { lines, last };
24
+ };
@@ -0,0 +1,4 @@
1
+ /** Turns on process-creation auditing with command lines (the registry is new every boot). */
2
+ export declare const enableProcessAudit: () => void;
3
+ /** Reads new process events every 15 s for the daemon's life (from the moment it started: older ones are boot noise). */
4
+ export declare const startProcessCollector: () => void;
@@ -0,0 +1,47 @@
1
+ // audit.processes, sandbox side: Windows records every program started (Security log, event 4688, with its command
2
+ // line); the daemon reads the new events every 15 s and leaves them in run\audit\ as small .jsonl files, which the
3
+ // host takes into its own log (and deletes). Windows' own background programs are left out. The host treats all of it
4
+ // as untrusted (it is checked line by line there).
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { spawnSync } from 'node:child_process';
8
+ import { SYSTEM } from '../shared/system.js';
9
+ import { parseProcessEvents } from './audit-parse.js';
10
+ import { dryRun, errMsg, isWin, log, mkdirp, run, session, warn } from './env.js';
11
+ /** The "Process Creation" audit subcategory (a GUID, so it works in every Windows language). */
12
+ const PROCESS_CREATION = '{0CCE922B-69AE-11D9-BED3-505054503030}';
13
+ const MAX_PER_PASS = 2000;
14
+ const auditOn = () => Boolean(session.auditProcesses) && isWin && !dryRun;
15
+ /** Turns on process-creation auditing with command lines (the registry is new every boot). */
16
+ export const enableProcessAudit = () => {
17
+ if (!auditOn())
18
+ return;
19
+ spawnSync(path.join(process.env.WINDIR ?? 'C:\\Windows', 'System32', 'auditpol.exe'), ['/set', '/subcategory:' + PROCESS_CREATION, '/success:enable'], { windowsHide: true, stdio: 'ignore' });
20
+ spawnSync(SYSTEM.reg(), ['add', 'HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\Audit', '/v', 'ProcessCreationIncludeCmdLine_Enabled', '/t', 'REG_DWORD', '/d', '1', '/f'], { windowsHide: true, stdio: 'ignore' });
21
+ log('Audit: programs started in the sandbox are recorded (audit.processes).');
22
+ };
23
+ const wevtutil = () => path.join(process.env.WINDIR ?? 'C:\\Windows', 'System32', 'wevtutil.exe');
24
+ const newestRecord = () => {
25
+ const r = spawnSync(wevtutil(), ['qe', 'Security', '/q:*[System[(EventID=4688)]]', '/f:xml', '/c:1', '/rd:true'], { encoding: 'utf8', windowsHide: true });
26
+ return parseProcessEvents(r.stdout ?? '').last;
27
+ };
28
+ /** Reads new process events every 15 s for the daemon's life (from the moment it started: older ones are boot noise). */
29
+ export const startProcessCollector = () => {
30
+ if (!auditOn())
31
+ return;
32
+ let last = newestRecord();
33
+ const pass = () => {
34
+ try {
35
+ const r = spawnSync(wevtutil(), ['qe', 'Security', '/q:*[System[(EventID=4688) and (EventRecordID>' + last + ')]]', '/f:xml', '/c:' + MAX_PER_PASS], { encoding: 'utf8', windowsHide: true, maxBuffer: 64 * 1024 * 1024 });
36
+ const { lines, last: newest } = parseProcessEvents(r.stdout ?? '');
37
+ if (newest)
38
+ last = newest;
39
+ if (lines.length)
40
+ fs.writeFileSync(path.join(mkdirp(run.audit), Date.now() + '-' + process.pid + '.jsonl'), lines.join('\n') + '\n');
41
+ }
42
+ catch (e) {
43
+ warn('audit: ' + errMsg(e));
44
+ }
45
+ };
46
+ setInterval(pass, 15000).unref?.();
47
+ };
@@ -0,0 +1,4 @@
1
+ import { type BootEnvT } from './project-env.js';
2
+ export { baseEnvFor, type BootEnvT } from './project-env.js';
3
+ export declare const saveBootEnv: (value: BootEnvT) => void;
4
+ export declare const readBootEnv: () => BootEnvT | null;
@@ -0,0 +1,25 @@
1
+ // What the boot's provisioning left for projects that join later: the PATH before any toolchain was added, which
2
+ // variables the toolchains set, and what each step contributed. Kept on the sandbox's own disk (lost on close, as it
3
+ // should be). The session daemon builds every later project's environment from it instead of from the boot
4
+ // project's, so project A's Node, Rust target folder or database variables never leak into project B's sessions.
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { writeJsonAtomic } from '../shared/rundir.js';
8
+ import { localDir } from './env.js';
9
+ import { BootEnv } from './project-env.js';
10
+ export { baseEnvFor } from './project-env.js';
11
+ const file = () => path.join(localDir, 'boot-env.json');
12
+ export const saveBootEnv = (value) => {
13
+ try {
14
+ writeJsonAtomic(file(), value);
15
+ }
16
+ catch { /* projects added later then fall back to the boot environment */ }
17
+ };
18
+ export const readBootEnv = () => {
19
+ try {
20
+ return BootEnv.safeParse(JSON.parse(fs.readFileSync(file(), 'utf8'))).data ?? null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ };
@@ -0,0 +1,12 @@
1
+ export declare const quoteArg: (arg: string) => string;
2
+ /**
3
+ * Arguments on a `cmd /d /c <agent>.cmd ...` line (npm agents). cmd parses them twice: the /c line, then the .cmd's %*.
4
+ * & | < > ^ are literal inside quotes, so arguments with them are quoted (https://x?a=1&b=2 survives). % expands even
5
+ * inside quotes, so each one sits between quoted pieces ("50"%" off"): every %name% cmd could see then starts with a
6
+ * quote, and no variable is called that. cmd's quote state runs across the whole line (an embedded \" toggles it too),
7
+ * so a metacharacter it would see outside quotes gets ^^^ (one caret left after each parse). Arguments without any
8
+ * of these are quoted as usual.
9
+ */
10
+ export declare const cmdArgs: (args: string[]) => string;
11
+ /** The command line ptyhost starts; when the program is cmd.exe its arguments are quoted for cmd (cmdArgs). */
12
+ export declare const commandLine: (file: string, args: string[]) => string;
@@ -0,0 +1,23 @@
1
+ // Command lines for the agent: Windows argument quoting, and the extra layer cmd.exe needs for npm agents (.cmd files).
2
+ import path from 'node:path';
3
+ const quoted = (arg) => '"' + arg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, '$1$1') + '"';
4
+ export const quoteArg = (arg) => (arg !== '' && !/[\s"]/.test(arg) ? arg : quoted(arg));
5
+ /**
6
+ * Arguments on a `cmd /d /c <agent>.cmd ...` line (npm agents). cmd parses them twice: the /c line, then the .cmd's %*.
7
+ * & | < > ^ are literal inside quotes, so arguments with them are quoted (https://x?a=1&b=2 survives). % expands even
8
+ * inside quotes, so each one sits between quoted pieces ("50"%" off"): every %name% cmd could see then starts with a
9
+ * quote, and no variable is called that. cmd's quote state runs across the whole line (an embedded \" toggles it too),
10
+ * so a metacharacter it would see outside quotes gets ^^^ (one caret left after each parse). Arguments without any
11
+ * of these are quoted as usual.
12
+ */
13
+ export const cmdArgs = (args) => {
14
+ const pieces = args.map((arg) => (/[&|<>^%]/.test(arg) ? arg.split('%').map(quoted).join('%') : quoteArg(arg))).join(' ');
15
+ let inQuotes = false;
16
+ return [...pieces].map((c) => {
17
+ if (c === '"')
18
+ inQuotes = !inQuotes;
19
+ return !inQuotes && /[&|<>^]/.test(c) ? '^^^' + c : c;
20
+ }).join('');
21
+ };
22
+ /** The command line ptyhost starts; when the program is cmd.exe its arguments are quoted for cmd (cmdArgs). */
23
+ export const commandLine = (file, args) => path.win32.basename(file).toLowerCase() === 'cmd.exe' ? [quoteArg(file), cmdArgs(args)].filter(Boolean).join(' ') : [file, ...args].map(quoteArg).join(' ');
@@ -0,0 +1,5 @@
1
+ import type { GuestRequest } from '../../core/types.js';
2
+ /** Runs a command for the host without blocking the daemon (other requests and pings keep being answered). */
3
+ export declare const execCommand: (req: Extract<GuestRequest, {
4
+ kind: "exec";
5
+ }>) => void;
@@ -0,0 +1,32 @@
1
+ // `exec` requests: a command run for the host in the workspace.
2
+ import { spawn } from 'node:child_process';
3
+ import { SYSTEM } from '../../shared/system.js';
4
+ import { errMsg, exists } from '../env.js';
5
+ import { provisionedFor } from './project.js';
6
+ import { insideWorkspace, respond } from './state.js';
7
+ const MAX_OUTPUT = 1024 * 1024;
8
+ const quoteForCmd = (a) => (/[\s"&|<>^]/.test(a) ? '"' + a.replace(/"/g, '""') + '"' : a);
9
+ const cap = (s) => (s.length > MAX_OUTPUT ? s.slice(0, MAX_OUTPUT) + '\n[output truncated]' : s);
10
+ /** Runs a command for the host without blocking the daemon (other requests and pings keep being answered). */
11
+ export const execCommand = (req) => {
12
+ if (!insideWorkspace(req.cwd) || !exists(req.cwd)) {
13
+ respond(req.id, { ok: false, message: 'Folder is not inside the sandbox workspace: ' + req.cwd });
14
+ return;
15
+ }
16
+ // chcp 65001 first: console programs then write UTF-8 (not the OEM code page) into the pipes.
17
+ // In a project registered after boot, its own environment (its Node, Python, database variables).
18
+ const env = provisionedFor(req.cwd)?.env ?? process.env;
19
+ const child = spawn(SYSTEM.cmd(), ['/d', '/s', '/c', '"chcp 65001>nul & ' + req.command.map(quoteForCmd).join(' ') + '"'], { cwd: req.cwd, env, windowsHide: true, windowsVerbatimArguments: true });
20
+ const output = { stdout: '', stderr: '' };
21
+ // setEncoding decodes across chunks: a multi-byte character split between two reads is not mangled.
22
+ const collect = (key) => (d) => { if (output[key].length < MAX_OUTPUT)
23
+ output[key] += d; };
24
+ child.stdout.setEncoding('utf8').on('data', collect('stdout'));
25
+ child.stderr.setEncoding('utf8').on('data', collect('stderr'));
26
+ const timer = setTimeout(() => child.kill(), Math.min(req.timeoutSeconds, 3600) * 1000);
27
+ child.on('error', (e) => { clearTimeout(timer); respond(req.id, { ok: false, message: 'Could not run the command: ' + errMsg(e) }); });
28
+ child.on('close', (code) => {
29
+ clearTimeout(timer);
30
+ respond(req.id, { ok: code === 0, message: code === null ? 'stopped after ' + req.timeoutSeconds + ' s' : 'exit ' + code, exitCode: code ?? -1, stdout: cap(output.stdout), stderr: cap(output.stderr) });
31
+ });
32
+ };
@@ -0,0 +1,14 @@
1
+ import type { ProjectSpecT } from '../../shared/schema.js';
2
+ export interface Provisioned {
3
+ spec: ProjectSpecT;
4
+ env: NodeJS.ProcessEnv;
5
+ summary: string[];
6
+ }
7
+ /** The provisioned project containing this folder, if any (exec in its folders uses its environment). */
8
+ export declare const provisionedFor: (cwd: string) => Provisioned | null;
9
+ export declare const provisionedProjects: () => Provisioned[];
10
+ /**
11
+ * Provisions a project (or joins the run already doing it: two sessions requested at once wait for the same one).
12
+ * `say` gets the progress lines.
13
+ */
14
+ export declare const provisionProject: (spec: ProjectSpecT, say: (message: string) => void) => Promise<Provisioned>;