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
@@ -3,72 +3,99 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { Config, Mapping } from '../core/schema.js';
5
5
  import { guestPathFor } from '../cli/commands/context.js';
6
- import { buildWsb } from '../host/generate.js';
6
+ import { buildWsb } from '../host/launch/wsb.js';
7
7
  import { guestDrive, isValidGuestPath, planMirrorWorkspace, sandboxFolder, substDrives } from '../shared/guest-paths.js';
8
8
  import { guestWorkspaceAliases, projectFolderName } from '../shared/paths.js';
9
9
  import { syncHistoryIn, syncHistoryOut } from '../sync/history.js';
10
- export const mirrorTests = async (t, f) => {
11
- const { check, rejects, write } = t;
12
- t.section('mirrored guest paths (other drive letters)');
13
- check(['C:\\Assets', 'C:\\Workspace\\x', 'F:\\SteamLibrary\\steamapps\\common\\Game', 'f:\\x', 'Z:\\a b\\c (1)'].every(isValidGuestPath), 'guest paths under C:\\ or on D: to Z: are accepted');
14
- check(!['C:\\', 'F:\\', 'F:', 'C:\\Windows\\x', 'C:\\Users\\me', 'c:\\program files\\x', 'C:\\AgentData\\x', 'C:\\Mirror', 'C:\\Mirror\\F\\x', 'A:\\x', 'B:\\x', 'C:\\a\\..\\Windows', 'F:\\a\\..\\b', 'F:\\a:b', 'F:\\a/b', 'F:\\a\\\\b', 'F:\\a.\\b', 'F:\\a|b', 'F:\\a\nb', '\\\\server\\share\\x', 'relative\\x', 'F:x'].some(isValidGuestPath), 'drive roots, reserved folders, C:\\Mirror, A:/B:, .., streams, odd characters and UNC paths are refused');
15
- const mapping = (guest) => Mapping.safeParse({ host: 'E:\\Games', guest, readOnly: true }).success;
16
- check(mapping('F:\\SteamLibrary\\Game') && !mapping('C:\\Mirror\\F\\x') && mapping('C:\\F') && !mapping('F:\\'), 'schema: mappings[].guest may be on another drive letter, never C:\\Mirror or a drive root');
17
- check(Config.safeParse({ ...f.makeConfig(), guestWorkspace: 'mirror' }).success && Config.safeParse({ ...f.makeConfig(), guestWorkspace: 'F:\\AgentProjects\\x' }).success && !Config.safeParse({ ...f.makeConfig(), guestWorkspace: 'C:\\Users\\x' }).success, 'schema: guestWorkspace accepts "mirror" and D:-Z: paths, refuses reserved ones');
18
- check(guestDrive('f:\\x') === 'F' && guestDrive('\\\\s\\x') === null, 'drive letter of a guest path');
19
- check(sandboxFolder('F:\\SteamLibrary\\Game') === 'C:\\Mirror\\F\\SteamLibrary\\Game' && sandboxFolder('f:\\x') === 'C:\\Mirror\\F\\x' && sandboxFolder('C:\\Assets') === 'C:\\Assets', 'F:\\x really lives at C:\\Mirror\\F\\x; C: paths stay as they are');
20
- check(JSON.stringify(substDrives(['C:\\a', 'G:\\z', 'F:\\x', 'f:\\y'])) === JSON.stringify([{ letter: 'F', target: 'C:\\Mirror\\F' }, { letter: 'G', target: 'C:\\Mirror\\G' }]), 'one subst drive per used letter (never C:), sorted');
21
- check(JSON.stringify(guestWorkspaceAliases('F:\\AgentProjects\\mw2')) === JSON.stringify(['C:\\Mirror\\F\\AgentProjects\\mw2', 'F:\\AgentProjects\\mw2']) && guestWorkspaceAliases('C:\\Workspace\\x').length === 1, 'a mirrored workspace is also known by its real C:\\Mirror path');
22
- const wsb = buildWsb(f.baseConfig, 4096, [{ host: 'E:\\SteamLibrary\\steamapps\\common\\Game', guest: 'F:\\SteamLibrary\\steamapps\\common\\Game', readOnly: true }, { host: 'D:\\Assets', guest: 'C:\\Assets', readOnly: false }]);
23
- check(wsb.includes('<SandboxFolder>C:\\Mirror\\F\\SteamLibrary\\steamapps\\common\\Game</SandboxFolder>') && wsb.includes('<SandboxFolder>C:\\Assets</SandboxFolder>') && !wsb.includes('<SandboxFolder>F:'), 'the .wsb maps F:\\ guests under C:\\Mirror\\F');
24
- t.section('guestWorkspace "mirror"');
25
- const fallback = 'C:\\Workspace\\mw2-agent';
26
- const onF = planMirrorWorkspace('F:\\AgentProjects\\mw2-agent', fallback);
27
- check(onF.guest === 'F:\\AgentProjects\\mw2-agent' && (onF.note ?? '').includes('C:\\Mirror\\F\\AgentProjects\\mw2-agent'), 'a workspace on F: appears at its PC path (mirror + subst), with a note');
28
- check(planMirrorWorkspace('f:\\AgentProjects\\x\\', fallback).guest === 'F:\\AgentProjects\\x', 'trailing backslash and lower-case letter are normalised');
29
- const onC = planMirrorWorkspace('C:\\dev\\mw2-agent', fallback);
30
- check(onC.guest === 'C:\\dev\\mw2-agent' && onC.note === null, 'a workspace on C: outside reserved folders keeps its path');
31
- const reserved = planMirrorWorkspace('C:\\Users\\josh\\mw2-agent', fallback);
32
- check(reserved.guest === fallback && (reserved.note ?? '').includes('reserved'), 'C:\\Users\\... falls back to C:\\Workspace\\<name> with a note');
33
- check(planMirrorWorkspace('\\\\server\\share\\mw2-agent', fallback).guest === fallback && planMirrorWorkspace('A:\\mw2-agent', fallback).guest === fallback, 'UNC paths and A:/B: fall back too');
34
- const mirrorConfig = { ...f.makeConfig(), guestWorkspace: 'mirror' };
35
- const resolved = await f.run({}, mirrorConfig);
36
- const expected = planMirrorWorkspace(f.workA, 'C:\\Workspace\\work-a');
37
- check(resolved.config.guestWorkspace === expected.guest && (expected.note === null || resolved.notes.includes(expected.note)), 'resolveRun turns "mirror" into a concrete guest path (and explains a fallback in run.notes)');
38
- t.section('generate: mirrored mappings and workspace');
39
- const game = f.at('steam', 'Game');
40
- fs.mkdirSync(game, { recursive: true });
41
- const base = await f.run();
42
- const run = { ...base, config: { ...base.config, guestWorkspace: 'G:\\Projects\\work-a', mappings: [{ host: game, guest: 'F:\\SteamLibrary\\steamapps\\common\\Game', readOnly: true }] } };
43
- const generated = f.generate(run);
44
- const session = f.readJson(generated.sessionPath);
45
- const text = fs.readFileSync(generated.wsbPath, 'utf8');
46
- check(session.workspace === 'G:\\Projects\\work-a' && JSON.stringify(session.substDrives) === JSON.stringify([{ letter: 'F', target: 'C:\\Mirror\\F' }, { letter: 'G', target: 'C:\\Mirror\\G' }]), 'session.json: workspace at its drive-letter path, subst drives listed for bootstrap.ps1');
47
- check(text.includes('<SandboxFolder>C:\\Mirror\\G\\Projects\\work-a</SandboxFolder>') && text.includes('<SandboxFolder>C:\\Mirror\\F\\SteamLibrary\\steamapps\\common\\Game</SandboxFolder>'), '.wsb: workspace and mapping under C:\\Mirror');
48
- check(JSON.stringify(f.readJson(f.generate(base).sessionPath).substDrives) === '[]', 'no other drive letters: no subst drives');
49
- check(await rejects(() => f.generate({ ...run, config: { ...run.config, mappings: [{ host: game, guest: 'C:\\Mirror\\F\\x', readOnly: true }] } }), 'not a usable sandbox folder'), 'a mapping straight into C:\\Mirror is refused');
50
- check(await rejects(() => f.generate({ ...run, config: { ...run.config, mappings: [{ host: game, guest: 'C:\\Windows\\x', readOnly: true }] } }), 'not a usable sandbox folder'), 'a reserved guest folder is refused by the host too (not only the schema)');
51
- check(await rejects(() => f.generate({ ...run, config: { ...run.config, mappings: [{ host: game, guest: 'g:\\projects\\work-a\\game', readOnly: true }] } }), 'overlapping'), 'mappings overlapping on the same mirrored drive are refused');
52
- t.section('exec / attach cwd and history for a mirrored workspace');
53
- const ws = 'F:\\AgentProjects\\mw2-agent';
54
- check(guestPathFor(ws + '\\crates\\engine', ws, ws) === ws + '\\crates\\engine', 'a host folder maps to the same guest folder');
55
- const hostDot = f.at('mirror-host-claude');
56
- const guestDot = f.at('mirror-guest-claude');
57
- const project = projectFolderName(ws);
58
- check(project === 'F--AgentProjects-mw2-agent', 'Claude project folder for F:\\AgentProjects\\mw2-agent is F--AgentProjects-mw2-agent on both sides');
59
- write(path.join(hostDot, 'projects', project, 's1.jsonl'), '{"cwd":"F:\\\\AgentProjects\\\\mw2-agent","t":1}\n');
60
- write(path.join(hostDot, 'history.jsonl'), '{"display":"hi","timestamp":1,"project":"F:\\\\AgentProjects\\\\mw2-agent"}\n');
61
- const paths = { hostClaudeDir: hostDot, guestClaudeDir: guestDot, hostWorkspace: ws, guestWorkspace: ws };
62
- syncHistoryIn(paths);
63
- check(fs.readFileSync(path.join(guestDot, 'projects', project, 's1.jsonl'), 'utf8').includes('F:\\\\AgentProjects\\\\mw2-agent') && fs.readdirSync(path.join(guestDot, 'projects')).length === 1, 'history in: same folder name, paths unchanged');
64
- write(path.join(guestDot, 'projects', project, 's2.jsonl'), '{"cwd":"F:\\\\AgentProjects\\\\mw2-agent","t":2}\n');
65
- write(path.join(guestDot, 'projects', 'C--Mirror-F-AgentProjects-mw2-agent', 's3.jsonl'), '{"cwd":"C:\\\\Mirror\\\\F\\\\AgentProjects\\\\mw2-agent","f":"C:/Mirror/F/AgentProjects/mw2-agent/src/main.rs"}\n');
66
- fs.appendFileSync(path.join(guestDot, 'history.jsonl'), '{"display":"real path","timestamp":2,"project":"C:\\\\Mirror\\\\F\\\\AgentProjects\\\\mw2-agent"}\n');
67
- syncHistoryOut(paths);
68
- const s3 = path.join(hostDot, 'projects', project, 's3.jsonl');
69
- check(fs.existsSync(path.join(hostDot, 'projects', project, 's2.jsonl')) && fs.existsSync(s3) && !fs.existsSync(path.join(hostDot, 'projects', 'C--Mirror-F-AgentProjects-mw2-agent')), 'history out: transcripts under the real C:\\Mirror path come back into the PC project folder');
70
- const s3Text = fs.existsSync(s3) ? fs.readFileSync(s3, 'utf8') : '';
71
- check(s3Text.includes('"cwd":"F:\\\\AgentProjects\\\\mw2-agent"') && s3Text.includes('F:/AgentProjects/mw2-agent/src/main.rs') && !s3Text.includes('Mirror'), 'history out: C:\\Mirror\\F\\... paths are translated back to F:\\...');
72
- const hist = fs.readFileSync(path.join(hostDot, 'history.jsonl'), 'utf8');
73
- check(hist.includes('"project":"F:\\\\AgentProjects\\\\mw2-agent"}') && !hist.includes('Mirror'), 'prompt history out: real paths translated too');
10
+ export const mirrorSuite = {
11
+ name: 'mirror',
12
+ title: 'guest paths on other drive letters, guestWorkspace "mirror"',
13
+ run: async (t, f) => {
14
+ t.section('which guest paths are valid');
15
+ const valid = ['C:\\Assets', 'C:\\Workspace\\x', 'F:\\SteamLibrary\\steamapps\\common\\Game', 'f:\\x', 'Z:\\a b\\c (1)'];
16
+ t.equal(valid.filter((p) => !isValidGuestPath(p)), [], 'guest paths under C:\\ or on D: to Z: are accepted');
17
+ const invalid = ['C:\\', 'F:\\', 'F:', 'C:\\Windows\\x', 'C:\\Users\\me', 'c:\\program files\\x', 'C:\\AgentData\\x', 'C:\\Mirror', 'C:\\Mirror\\F\\x', 'A:\\x', 'B:\\x', 'C:\\a\\..\\Windows', 'F:\\a\\..\\b', 'F:\\a:b', 'F:\\a/b', 'F:\\a\\\\b', 'F:\\a.\\b', 'F:\\a|b', 'F:\\a\nb', '\\\\server\\share\\x', 'relative\\x', 'F:x'];
18
+ t.equal(invalid.filter(isValidGuestPath), [], 'drive roots, reserved folders, C:\\Mirror, A:/B:, .., streams, odd characters and UNC paths are refused');
19
+ const mappingAccepts = (guest) => Mapping.safeParse({ host: 'E:\\Games', guest, readOnly: true }).success;
20
+ t.check(mappingAccepts('F:\\SteamLibrary\\Game') && mappingAccepts('C:\\F'), 'schema: mappings[].guest may be on another drive letter');
21
+ t.check(!mappingAccepts('C:\\Mirror\\F\\x') && !mappingAccepts('F:\\'), 'schema: mappings[].guest may never be C:\\Mirror or a drive root');
22
+ const workspaceAccepts = (guestWorkspace) => Config.safeParse({ ...f.makeConfig(), guestWorkspace }).success;
23
+ t.check(workspaceAccepts('mirror') && workspaceAccepts('F:\\AgentProjects\\x'), 'schema: guestWorkspace accepts "mirror" and D:-Z: paths');
24
+ t.check(!workspaceAccepts('C:\\Users\\x'), 'schema: guestWorkspace refuses reserved paths');
25
+ t.equal([guestDrive('f:\\x'), guestDrive('\\\\s\\x')], ['F', null], 'drive letter of a guest path');
26
+ t.section('where a drive-letter path really lives');
27
+ t.equal([sandboxFolder('F:\\SteamLibrary\\Game'), sandboxFolder('f:\\x'), sandboxFolder('C:\\Assets')], ['C:\\Mirror\\F\\SteamLibrary\\Game', 'C:\\Mirror\\F\\x', 'C:\\Assets'], 'F:\\x really lives at C:\\Mirror\\F\\x; C: paths stay as they are');
28
+ t.equal(substDrives(['C:\\a', 'G:\\z', 'F:\\x', 'f:\\y']), [{ letter: 'F', target: 'C:\\Mirror\\F' }, { letter: 'G', target: 'C:\\Mirror\\G' }], 'one subst drive per used letter (never C:), sorted');
29
+ t.equal(guestWorkspaceAliases('F:\\AgentProjects\\mw2'), ['C:\\Mirror\\F\\AgentProjects\\mw2', 'F:\\AgentProjects\\mw2'], 'a mirrored workspace is also known by its real C:\\Mirror path');
30
+ t.equal(guestWorkspaceAliases('C:\\Workspace\\x').length, 1, 'a C: workspace has no alias');
31
+ const wsb = buildWsb(f.baseConfig, 4096, [
32
+ { host: 'E:\\SteamLibrary\\steamapps\\common\\Game', guest: 'F:\\SteamLibrary\\steamapps\\common\\Game', readOnly: true },
33
+ { host: 'D:\\Assets', guest: 'C:\\Assets', readOnly: false },
34
+ ]);
35
+ t.includes(wsb, '<SandboxFolder>C:\\Mirror\\F\\SteamLibrary\\steamapps\\common\\Game</SandboxFolder>', 'the .wsb maps F:\\ guests under C:\\Mirror\\F');
36
+ t.includes(wsb, '<SandboxFolder>C:\\Assets</SandboxFolder>', 'the .wsb maps C:\\ guests as they are');
37
+ t.excludes(wsb, '<SandboxFolder>F:', 'the .wsb never maps a drive-letter folder directly');
38
+ t.section('guestWorkspace "mirror"');
39
+ const fallback = 'C:\\Workspace\\mw2-agent';
40
+ const onF = planMirrorWorkspace('F:\\AgentProjects\\mw2-agent', fallback);
41
+ t.equal(onF.guest, 'F:\\AgentProjects\\mw2-agent', 'a workspace on F: appears at its PC path (mirror + subst)');
42
+ t.includes(onF.note ?? '', 'C:\\Mirror\\F\\AgentProjects\\mw2-agent', 'a workspace on F: comes with a note saying where it really is');
43
+ t.equal(planMirrorWorkspace('f:\\AgentProjects\\x\\', fallback).guest, 'F:\\AgentProjects\\x', 'a trailing backslash and a lower-case letter are normalised');
44
+ const onC = planMirrorWorkspace('C:\\dev\\mw2-agent', fallback);
45
+ t.equal({ guest: onC.guest, note: onC.note }, { guest: 'C:\\dev\\mw2-agent', note: null }, 'a workspace on C: outside reserved folders keeps its path');
46
+ const reserved = planMirrorWorkspace('C:\\Users\\josh\\mw2-agent', fallback);
47
+ t.equal(reserved.guest, fallback, 'C:\\Users\\... falls back to C:\\Workspace\\<name>...');
48
+ t.includes(reserved.note ?? '', 'reserved', '... with a note');
49
+ t.equal(planMirrorWorkspace('\\\\server\\share\\mw2-agent', fallback).guest, fallback, 'UNC paths fall back');
50
+ t.equal(planMirrorWorkspace('A:\\mw2-agent', fallback).guest, fallback, 'A: and B: fall back');
51
+ const resolved = await f.run({}, { ...f.makeConfig(), guestWorkspace: 'mirror' });
52
+ const expected = planMirrorWorkspace(f.workA, 'C:\\Workspace\\work-a');
53
+ t.equal(resolved.config.guestWorkspace, expected.guest, 'resolveRun turns "mirror" into a concrete guest path');
54
+ t.check(expected.note === null || resolved.notes.includes(expected.note), 'resolveRun explains a fallback in run.notes', JSON.stringify(resolved.notes));
55
+ t.section('generating a sandbox with drive-letter folders');
56
+ const game = t.dir('steam', 'Game');
57
+ const base = await f.run();
58
+ const run = { ...base, config: { ...base.config, guestWorkspace: 'G:\\Projects\\work-a', mappings: [{ host: game, guest: 'F:\\SteamLibrary\\steamapps\\common\\Game', readOnly: true }] } };
59
+ const generated = f.generate(run);
60
+ const session = f.readJson(generated.sessionPath);
61
+ const text = fs.readFileSync(generated.wsbPath, 'utf8');
62
+ t.equal(session.workspace, 'G:\\Projects\\work-a', 'session.json: the workspace is at its drive-letter path');
63
+ t.equal(session.substDrives, [{ letter: 'F', target: 'C:\\Mirror\\F' }, { letter: 'G', target: 'C:\\Mirror\\G' }], 'session.json: subst drives are listed for bootstrap.ps1');
64
+ t.includes(text, '<SandboxFolder>C:\\Mirror\\G\\Projects\\work-a</SandboxFolder>', '.wsb: the workspace is under C:\\Mirror');
65
+ t.includes(text, '<SandboxFolder>C:\\Mirror\\F\\SteamLibrary\\steamapps\\common\\Game</SandboxFolder>', '.wsb: the mapping is under C:\\Mirror');
66
+ t.equal(f.readJson(f.generate(base).sessionPath).substDrives, [], 'no other drive letters: no subst drives');
67
+ const withMapping = (guest) => ({ ...run, config: { ...run.config, mappings: [{ host: game, guest, readOnly: true }] } });
68
+ await t.throws(() => f.generate(withMapping('C:\\Mirror\\F\\x')), 'not a usable sandbox folder', 'a mapping straight into C:\\Mirror is refused');
69
+ await t.throws(() => f.generate(withMapping('C:\\Windows\\x')), 'not a usable sandbox folder', 'a reserved guest folder is refused by the host too (not only the schema)');
70
+ await t.throws(() => f.generate(withMapping('g:\\projects\\work-a\\game')), 'overlapping', 'mappings overlapping on the same mirrored drive are refused');
71
+ t.section('exec, attach and history for a mirrored workspace');
72
+ const workspace = 'F:\\AgentProjects\\mw2-agent';
73
+ t.equal(guestPathFor(workspace + '\\crates\\engine', workspace, workspace), workspace + '\\crates\\engine', 'a host folder maps to the same guest folder');
74
+ const project = projectFolderName(workspace);
75
+ t.equal(project, 'F--AgentProjects-mw2-agent', 'the Claude project folder is the same on both sides');
76
+ const sides = f.historySides('mirror', workspace, workspace);
77
+ const hostProject = path.join(sides.hostClaudeDir, 'projects', project);
78
+ const guestProject = path.join(sides.guestClaudeDir, 'projects', project);
79
+ t.write(path.join(hostProject, 's1.jsonl'), '{"cwd":"F:\\\\AgentProjects\\\\mw2-agent","t":1}\n');
80
+ t.write(path.join(sides.hostClaudeDir, 'history.jsonl'), '{"display":"hi","timestamp":1,"project":"F:\\\\AgentProjects\\\\mw2-agent"}\n');
81
+ syncHistoryIn(sides);
82
+ t.includes(fs.readFileSync(path.join(guestProject, 's1.jsonl'), 'utf8'), 'F:\\\\AgentProjects\\\\mw2-agent', 'history in: paths are unchanged');
83
+ t.equal(fs.readdirSync(path.join(sides.guestClaudeDir, 'projects')).length, 1, 'history in: only the one project folder is created');
84
+ t.write(path.join(guestProject, 's2.jsonl'), '{"cwd":"F:\\\\AgentProjects\\\\mw2-agent","t":2}\n');
85
+ const realPathProject = path.join(sides.guestClaudeDir, 'projects', 'C--Mirror-F-AgentProjects-mw2-agent');
86
+ t.write(path.join(realPathProject, 's3.jsonl'), '{"cwd":"C:\\\\Mirror\\\\F\\\\AgentProjects\\\\mw2-agent","f":"C:/Mirror/F/AgentProjects/mw2-agent/src/main.rs"}\n');
87
+ fs.appendFileSync(path.join(sides.guestClaudeDir, 'history.jsonl'), '{"display":"real path","timestamp":2,"project":"C:\\\\Mirror\\\\F\\\\AgentProjects\\\\mw2-agent"}\n');
88
+ syncHistoryOut(sides);
89
+ const s3 = path.join(hostProject, 's3.jsonl');
90
+ t.check(fs.existsSync(path.join(hostProject, 's2.jsonl')), 'history out: transcripts under the PC path come back');
91
+ t.check(fs.existsSync(s3), 'history out: transcripts under the real C:\\Mirror path come back into the PC project folder');
92
+ t.check(!fs.existsSync(path.join(sides.hostClaudeDir, 'projects', 'C--Mirror-F-AgentProjects-mw2-agent')), 'history out: no C:\\Mirror project folder appears on the PC');
93
+ const s3Text = fs.existsSync(s3) ? fs.readFileSync(s3, 'utf8') : '';
94
+ t.includes(s3Text, '"cwd":"F:\\\\AgentProjects\\\\mw2-agent"', 'history out: C:\\Mirror\\F\\... paths are translated back to F:\\...');
95
+ t.includes(s3Text, 'F:/AgentProjects/mw2-agent/src/main.rs', 'history out: forward-slash paths are translated too');
96
+ t.excludes(s3Text, 'Mirror', 'history out: nothing mentions C:\\Mirror any more');
97
+ const promptHistory = fs.readFileSync(path.join(sides.hostClaudeDir, 'history.jsonl'), 'utf8');
98
+ t.includes(promptHistory, '"project":"F:\\\\AgentProjects\\\\mw2-agent"}', 'prompt history out: real paths are translated');
99
+ t.excludes(promptHistory, 'Mirror', 'prompt history out: nothing mentions C:\\Mirror');
100
+ },
74
101
  };
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './suite.js';
2
+ export declare const networkSuite: Suite;
@@ -0,0 +1,40 @@
1
+ import { isSandboxAddress } from '../host/attach.js';
2
+ import { buildFirewallCommands, cidrToRange, ipv4DnsServers, parseDefaultGateways, parseDnsServers, subtractIps } from '../shared/netrules.js';
3
+ import { expandAllowList, hostAllowed, withBlockedHosts } from '../shared/netpolicy.js';
4
+ export const networkSuite = {
5
+ name: 'network',
6
+ title: 'firewall maths, gateway/DNS parsing, attach address checks',
7
+ run: (t) => {
8
+ t.section('firewall');
9
+ const [start172, end172] = cidrToRange('172.16.0.0/12');
10
+ const gateway = 172 * 16777216 + 28 * 65536 + 1;
11
+ const remaining = subtractIps([[start172, end172]], ['172.28.0.1']);
12
+ t.check(remaining.every(([start, end]) => gateway < start || gateway > end), 'the private block minus the gateway no longer contains the gateway');
13
+ t.excludes(buildFirewallCommands(['8.8.8.8']).join(), '8.8.8.8', 'public addresses are not part of the private-range rules');
14
+ t.section('reading Windows output');
15
+ t.equal(parseDefaultGateways(' 0.0.0.0 0.0.0.0 172.28.80.1 172.28.85.200 5\n'), ['172.28.80.1'], 'the default gateway is read from the route table');
16
+ t.equal(parseDnsServers(' DNS Servers . . . : 172.28.80.1\n 8.8.8.8\n NetBIOS : x\n'), ['172.28.80.1', '8.8.8.8'], 'DNS servers are read from ipconfig output');
17
+ t.equal(ipv4DnsServers(['172.28.80.1', '8.8.8.8:1053', '[::1]:53', 'fe80::1']), ['172.28.80.1', '8.8.8.8'], 'DNS servers from the resolver (any Windows language): IPv4 only, ports dropped');
18
+ t.section('attach only reaches into the sandbox');
19
+ const interfaces = {
20
+ 'vEthernet (Default Switch)': [{ address: '172.20.0.1', netmask: '255.255.240.0', family: 'IPv4', internal: false, mac: '', cidr: null }],
21
+ 'vEthernet (WSL (Hyper-V firewall))': [{ address: '172.30.0.1', netmask: '255.255.240.0', family: 'IPv4', internal: false, mac: '', cidr: null }],
22
+ Ethernet: [{ address: '192.168.1.10', netmask: '255.255.255.0', family: 'IPv4', internal: false, mac: '', cidr: null }],
23
+ };
24
+ t.check(isSandboxAddress('172.20.5.9', interfaces), 'an address in the Hyper-V subnet is the sandbox');
25
+ t.check(!isSandboxAddress('192.168.1.20', interfaces), 'a LAN address is not');
26
+ t.check(!isSandboxAddress('172.30.4.4', interfaces), 'an address on the WSL Hyper-V network is not (keystrokes never go to a WSL VM)');
27
+ t.check(!isSandboxAddress('127.0.0.1', interfaces), 'loopback is not');
28
+ t.check(!isSandboxAddress('8.8.8.8', interfaces), 'an internet address is not');
29
+ t.check(!isSandboxAddress('172.20.0.1', interfaces), 'this PC\'s own address on that subnet is not');
30
+ t.section('network settings');
31
+ const allow = expandAllowList(['@developer', 'Example.COM', '*.corp.example', 'not a host!']);
32
+ t.check(allow.includes('registry.npmjs.org') && allow.includes('api.anthropic.com') && allow.includes('example.com') && !allow.includes('not a host!'), 'allowOnly: @developer expands to registries, GitHub and AI providers; bad entries are dropped');
33
+ t.equal(['a.corp.example', 'corp.example', 'example.com', 'www.example.com', 'evil-example.com'].map((h) => hostAllowed(h, allow)), [true, false, true, false, false], 'allowOnly: *.x covers subdomains only, a name covers only itself');
34
+ const hosts = '127.0.0.1 localhost\r\n# mine\r\n';
35
+ const once = withBlockedHosts(hosts, ['Telemetry.Example.com', '*.bad']);
36
+ t.check(once.includes('0.0.0.0 telemetry.example.com') && once.includes('# mine') && !once.includes('*.bad'), 'blockHosts: names go to 0.0.0.0, your own lines stay, wildcards are skipped (a hosts file cannot do them)');
37
+ t.equal(withBlockedHosts(once, ['telemetry.example.com']), once, 'blockHosts: applying again changes nothing');
38
+ t.check(!withBlockedHosts(once, []).includes('telemetry'), 'blockHosts: an empty list removes the section');
39
+ },
40
+ };
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './suite.js';
2
+ export declare const personalizationSuite: Suite;
@@ -0,0 +1,53 @@
1
+ // Things written for Windows itself on this PC: the registry personalisation (.reg file) and the Windows Terminal
2
+ // PowerShell 7 profile. The Windows Terminal part is only tried on temp copies.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { toRegFile } from '../host/personalize.js';
6
+ import { PWSH_PROFILE_GUID, syncPwshProfile, withPwshProfile } from '../host/terminal.js';
7
+ export const personalizationSuite = {
8
+ name: 'personalization',
9
+ title: 'registry personalisation (.reg) and the Windows Terminal PowerShell 7 profile',
10
+ run: (t) => {
11
+ t.section('registry values');
12
+ const reg = toRegFile([
13
+ { key: 'Control Panel\\Desktop', name: 'WallpaperStyle', kind: 'String', value: '10' },
14
+ { key: 'X', name: 'D', kind: 'DWord', value: 4294967295 },
15
+ { key: 'X', name: 'B', kind: 'Binary', value: Buffer.from([1, 255]).toString('base64') },
16
+ ]);
17
+ t.includes(reg, '[HKEY_CURRENT_USER\\Control Panel\\Desktop]', 'the .reg file only writes under HKCU');
18
+ t.includes(reg, '"WallpaperStyle"="10"', 'string values are written as strings');
19
+ t.includes(reg, '"D"=dword:ffffffff', 'DWORD values are written in hex');
20
+ t.includes(reg, '"B"=hex:01,ff', 'binary values are written as hex bytes');
21
+ t.section('Windows Terminal profile');
22
+ const shown = withPwshProfile({ theme: 'dark', profiles: { defaults: { font: {} } } }, true);
23
+ const shownProfiles = shown?.profiles;
24
+ t.equal(shownProfiles?.list.length, 1, 'a PowerShell 7 profile is added once pwsh is installed');
25
+ t.equal(shownProfiles?.list[0].guid, PWSH_PROFILE_GUID, 'the added profile has our GUID');
26
+ t.includes(String(shownProfiles?.list[0].commandline), 'pwsh.exe', 'the added profile starts pwsh.exe');
27
+ t.check(Boolean(shownProfiles?.defaults) && shown?.theme === 'dark', 'the rest of the settings are kept');
28
+ t.equal(withPwshProfile(shown, true), null, 'nothing is rewritten when the profile is already there');
29
+ t.equal(withPwshProfile({ profiles: { defaults: {} } }, false), null, 'nothing is rewritten when pwsh was never wanted');
30
+ const edited = { profiles: { list: [{ guid: 'other' }, { guid: PWSH_PROFILE_GUID.toUpperCase(), name: 'My pwsh', fontSize: 14 }] } };
31
+ const hidden = withPwshProfile(edited, false)?.profiles;
32
+ t.equal({ hidden: hidden?.list[1].hidden, name: hidden?.list[1].name, fontSize: hidden?.list[1].fontSize, count: hidden?.list.length }, { hidden: true, name: 'My pwsh', fontSize: 14, count: 2 }, 'turning pwsh off hides the profile and keeps the user\'s edits');
33
+ const oldForm = withPwshProfile({ profiles: [{ guid: 'a' }] }, true)?.profiles;
34
+ t.equal(oldForm.list.length, 2, 'the old array form of "profiles" works');
35
+ t.section('Windows Terminal settings.json on disk (JSONC, temp copy)');
36
+ const data = t.at('wt-data');
37
+ const settings = t.write(path.join(data, 'tools', 'terminal', 'settings', 'settings.json'), '// edited in the sandbox\n{ "theme": "light", "profiles": { "list": [{ "guid": "{x}", }] } }');
38
+ const read = () => JSON.parse(fs.readFileSync(settings, 'utf8'));
39
+ syncPwshProfile(data, true);
40
+ const untouched = fs.readFileSync(settings, 'utf8');
41
+ t.check(untouched.startsWith('// edited'), 'untouched until pwsh is installed (comments and trailing commas survive)');
42
+ t.write(path.join(data, 'tools', 'pwsh', 'pwsh.exe'), 'MZ');
43
+ syncPwshProfile(data, true);
44
+ const added = read();
45
+ t.equal(added.theme, 'light', 'once pwsh is installed the profile is added and the user\'s settings are kept');
46
+ t.equal(added.profiles.list.map((p) => p.guid), ['{x}', PWSH_PROFILE_GUID], 'the profile is added after the existing ones');
47
+ syncPwshProfile(data, false);
48
+ t.equal(read().profiles.list[1].hidden, true, 'the profile is hidden when pwsh is turned off');
49
+ t.write(settings, '{ not json');
50
+ syncPwshProfile(data, true);
51
+ t.equal(fs.readFileSync(settings, 'utf8'), '{ not json', 'an unreadable settings file is left alone');
52
+ },
53
+ };
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './suite.js';
2
+ export declare const projectsSuite: Suite;