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
@@ -1,24 +1,40 @@
1
+ import { z } from 'zod';
1
2
  import { projectFolderName, translatePaths } from '../shared/paths.js';
2
3
  export { projectFolderName, translatePaths };
3
- /** Host-only record of the last launch: which folders map to each other and what syncs (from the effective config). */
4
- export interface SyncState {
5
- hostClaudeDir: string;
4
+ declare const SyncStateSchema: z.ZodObject<{
5
+ hostClaudeDir: z.ZodString;
6
+ hostWorkspace: z.ZodString;
7
+ guestWorkspace: z.ZodString;
8
+ syncHistory: z.ZodOptional<z.ZodBoolean>;
9
+ syncClaudeMem: z.ZodOptional<z.ZodBoolean>;
10
+ syncClaudeMemBack: z.ZodOptional<z.ZodBoolean>;
11
+ historyScope: z.ZodOptional<z.ZodEnum<{
12
+ project: "project";
13
+ all: "all";
14
+ }>>;
15
+ memHostStamp: z.ZodOptional<z.ZodNumber>;
16
+ memGuestStamp: z.ZodOptional<z.ZodNumber>;
17
+ projects: z.ZodOptional<z.ZodArray<z.ZodObject<{
18
+ hostWorkspace: z.ZodString;
19
+ guestWorkspace: z.ZodString;
20
+ }, z.core.$strip>>>;
21
+ }, z.core.$strip>;
22
+ export type SyncState = z.output<typeof SyncStateSchema>;
23
+ export interface WorkspacePair {
6
24
  hostWorkspace: string;
7
25
  guestWorkspace: string;
8
- syncHistory?: boolean;
9
- syncClaudeMem?: boolean;
10
- /** Newest claude-mem database time on each side right after the last sync (detects edits on both sides). */
11
- memHostStamp?: number;
12
- memGuestStamp?: number;
13
26
  }
14
- export interface SyncPaths {
27
+ /** extra: projects registered after boot (C:\Projects\<name> -> their real folder on this PC). */
28
+ export interface SyncPaths extends WorkspacePair {
15
29
  hostClaudeDir: string;
16
30
  guestClaudeDir: string;
17
- hostWorkspace: string;
18
- guestWorkspace: string;
31
+ extra?: WorkspacePair[];
19
32
  }
20
33
  /** Brings this PC's history into the sandbox state (run before the sandbox starts). Returns files copied. */
21
- export declare const syncHistoryIn: (p: SyncPaths) => number;
34
+ export declare const syncHistoryIn: (p: SyncPaths, options?: {
35
+ promptHistory?: boolean;
36
+ scope?: "project" | "all";
37
+ }) => number;
22
38
  /** Copies new sandbox history back to this PC (safe while the sandbox runs: transcripts are append-only). */
23
39
  export declare const syncHistoryOut: (p: SyncPaths) => number;
24
40
  /**
@@ -26,5 +42,6 @@ export declare const syncHistoryOut: (p: SyncPaths) => number;
26
42
  * never live in the data folder the sandbox can write (a forged copy could redirect writes anywhere on this PC).
27
43
  */
28
44
  export declare const syncStatePath: (dataPath: string) => string;
45
+ /** A state file that is missing, oversized or not in this shape counts as no state (nothing syncs back until the next launch). */
29
46
  export declare const readSyncState: (dataPath: string) => SyncState | null;
30
47
  export declare const writeSyncState: (dataPath: string, state: SyncState) => void;
@@ -7,17 +7,33 @@
7
7
  //
8
8
  // Safety (the sandbox side is untrusted):
9
9
  // - links are never followed or written through, on either side;
10
- // - what comes BACK is limited to transcripts (.jsonl), prompt history and to-do lists (.json), each capped in
11
- // size, and never Claude's memory folders. Nothing executable or instruction-like flows to this PC.
10
+ // - what comes BACK is limited to the sandbox's own projects: their transcripts (.jsonl) and prompt-history lines,
11
+ // plus to-do lists (.json), each capped in size, and never Claude's memory folders. A sandbox cannot plant a
12
+ // conversation for another project on this PC (claude --resume there would load it);
13
+ // - what goes IN is, by default (historyScope "project"), only the sandbox's own projects: other projects'
14
+ // conversations, file backups and prompts are not the agent's business.
12
15
  import fs from 'node:fs';
13
16
  import path from 'node:path';
14
17
  import _ from 'lodash';
15
- import { guestWorkspaceAliases, projectFolderName, translatePaths } from '../shared/paths.js';
16
- import { dirExists, ensureDir, fileExists, isLink, removeLink, tryReadJson, writeJson } from '../core/util.js';
18
+ import { z } from 'zod';
19
+ import { guestWorkspaceAliases, isSafeFileName, projectFolderName, translatePaths } from '../shared/paths.js';
20
+ import { readValid, writeJsonAtomic } from '../shared/rundir.js';
21
+ import { assertTestWritable } from '../host/test-guard.js';
22
+ import { dirExists, ensureDir, fileExists, isLink, removeLink } from '../core/util.js';
17
23
  export { projectFolderName, translatePaths };
18
24
  const SESSION_DIRS = ['todos', 'plans', 'file-history'];
19
25
  const DATA_FILE = /\.(jsonl?|txt|md)$/i;
20
26
  const MAX_FILE_BYTES = 100 * 1024 * 1024;
27
+ /** Host-only record of the last launch: which folders map to each other and what syncs (from the effective config). */
28
+ const AbsolutePath = z.string().max(1024).refine((p) => path.isAbsolute(p), 'must be an absolute path');
29
+ const SyncStateSchema = z.object({
30
+ hostClaudeDir: AbsolutePath, hostWorkspace: AbsolutePath, guestWorkspace: z.string().max(1024),
31
+ syncHistory: z.boolean().optional(), syncClaudeMem: z.boolean().optional(), syncClaudeMemBack: z.boolean().optional(), historyScope: z.enum(['project', 'all']).optional(),
32
+ /** Newest claude-mem database time on each side right after the last sync (detects edits on both sides). */
33
+ memHostStamp: z.number().optional(), memGuestStamp: z.number().optional(),
34
+ /** Projects registered while the sandbox ran (their history goes back to their own folders). */
35
+ projects: z.array(z.object({ hostWorkspace: AbsolutePath, guestWorkspace: z.string().max(1024) })).max(256).optional(),
36
+ });
21
37
  const anyFile = () => true;
22
38
  const transcript = (name) => name.endsWith('.jsonl');
23
39
  const json = (name) => name.endsWith('.json');
@@ -43,6 +59,8 @@ const copyNewer = (from, to, options = {}) => {
43
59
  return 0;
44
60
  const accept = options.accept ?? anyFile;
45
61
  return _.sumBy(fs.readdirSync(from, { withFileTypes: true }), (entry) => {
62
+ if (!isSafeFileName(entry.name))
63
+ return 0;
46
64
  const source = path.join(from, entry.name);
47
65
  const destination = path.join(to, entry.name);
48
66
  if (entry.isDirectory())
@@ -69,26 +87,52 @@ const copyNewer = (from, to, options = {}) => {
69
87
  });
70
88
  };
71
89
  const readLines = (file) => fileExists(file) && !isLink(file) && fs.statSync(file).size <= MAX_FILE_BYTES ? fs.readFileSync(file, 'utf8').split(/\r?\n/).filter((l) => l.trim() !== '') : [];
90
+ const isJsonObject = (line) => { try {
91
+ const v = JSON.parse(line);
92
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
93
+ }
94
+ catch {
95
+ return false;
96
+ } };
97
+ /** A prompt-history line whose project is one of `workspaces` (on this PC's side). */
98
+ const inProjects = (line, workspaces) => {
99
+ try {
100
+ const project = JSON.parse(line).project;
101
+ return typeof project === 'string' && workspaces.some((w) => path.resolve(w).toLowerCase() === path.resolve(project).toLowerCase());
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ };
72
107
  const lineTime = (line) => { try {
73
108
  return Number(JSON.parse(line).timestamp) || 0;
74
109
  }
75
110
  catch {
76
111
  return 0;
77
112
  } };
113
+ /** Every workspace pair of the sandbox: the launch folder's, then the projects registered later (most specific first). */
114
+ const pairsOf = (p) => _.sortBy([{ hostWorkspace: p.hostWorkspace, guestWorkspace: p.guestWorkspace }, ...(p.extra ?? [])], (x) => -x.hostWorkspace.length);
115
+ const translateAll = (text, pairs) => pairs.reduce((out, [from, to]) => translatePaths(out, from, to), text);
78
116
  /** Brings this PC's history into the sandbox state (run before the sandbox starts). Returns files copied. */
79
- export const syncHistoryIn = (p) => {
80
- const hostProject = projectFolderName(p.hostWorkspace);
81
- const guestProject = projectFolderName(p.guestWorkspace);
82
- const transform = (t) => translatePaths(t, p.hostWorkspace, p.guestWorkspace);
117
+ export const syncHistoryIn = (p, options = {}) => {
118
+ const all = options.scope === 'all';
119
+ assertTestWritable(p.guestClaudeDir);
120
+ const pairs = pairsOf(p);
121
+ const byHost = new Map(pairs.map((x) => [projectFolderName(x.hostWorkspace), projectFolderName(x.guestWorkspace)]));
122
+ const guestNames = new Set(pairs.map((x) => projectFolderName(x.guestWorkspace)));
123
+ const transform = (t) => translateAll(t, pairs.map((x) => [x.hostWorkspace, x.guestWorkspace]));
83
124
  const hostProjects = path.join(p.hostClaudeDir, 'projects');
84
- const names = dirExists(hostProjects) ? fs.readdirSync(hostProjects).filter((name) => name !== guestProject || hostProject === guestProject) : [];
85
- const projects = _.sumBy(names, (name) => name === hostProject
86
- ? copyNewer(path.join(hostProjects, name), path.join(p.guestClaudeDir, 'projects', guestProject), { transform })
125
+ const names = dirExists(hostProjects) ? fs.readdirSync(hostProjects).filter((name) => isSafeFileName(name) && (all ? !guestNames.has(name) || byHost.has(name) : byHost.has(name))) : [];
126
+ const projects = _.sumBy(names, (name) => byHost.has(name)
127
+ ? copyNewer(path.join(hostProjects, name), path.join(p.guestClaudeDir, 'projects', byHost.get(name)), { transform })
87
128
  : copyNewer(path.join(hostProjects, name), path.join(p.guestClaudeDir, 'projects', name)));
88
- const sessions = _.sumBy(SESSION_DIRS, (dir) => copyNewer(path.join(p.hostClaudeDir, dir), path.join(p.guestClaudeDir, dir)));
129
+ // To-do lists, plans and file backups are kept by session, not by project: only with historyScope "all".
130
+ const sessions = all ? _.sumBy(SESSION_DIRS, (dir) => copyNewer(path.join(p.hostClaudeDir, dir), path.join(p.guestClaudeDir, dir))) : 0;
131
+ if (options.promptHistory === false)
132
+ return projects + sessions;
89
133
  // Prompt history: rebuild the sandbox copy from this PC's file plus anything only the sandbox has.
90
134
  const target = path.join(p.guestClaudeDir, 'history.jsonl');
91
- const hostLines = readLines(path.join(p.hostClaudeDir, 'history.jsonl')).map(transform);
135
+ const hostLines = readLines(path.join(p.hostClaudeDir, 'history.jsonl')).filter((l) => all || inProjects(l, pairs.map((x) => x.hostWorkspace))).map(transform);
92
136
  const known = new Set(hostLines);
93
137
  const merged = _.sortBy([...hostLines, ...readLines(target).filter((l) => !known.has(l))], lineTime);
94
138
  if (merged.length) {
@@ -99,21 +143,23 @@ export const syncHistoryIn = (p) => {
99
143
  };
100
144
  /** Copies new sandbox history back to this PC (safe while the sandbox runs: transcripts are append-only). */
101
145
  export const syncHistoryOut = (p) => {
102
- const hostProject = projectFolderName(p.hostWorkspace);
103
- // A mirrored workspace on F: really lives at C:\Mirror\F\...; tools that resolve real paths record that one.
104
- const guestNames = guestWorkspaceAliases(p.guestWorkspace);
105
- const guestProjects = new Set(guestNames.map(projectFolderName));
106
- const transform = (t) => guestNames.reduce((out, guest) => translatePaths(out, guest, p.hostWorkspace), t);
146
+ assertTestWritable(p.hostClaudeDir);
147
+ // A mirrored workspace on F: really lives at C:\Mirror\F\...; tools that resolve real paths record that one. A
148
+ // project registered later is in the hub (C:\Projects\<name>) and goes back to its real folder on this PC.
149
+ const pairs = pairsOf(p).flatMap((x) => guestWorkspaceAliases(x.guestWorkspace).map((guest) => [guest, x.hostWorkspace]));
150
+ const byGuest = new Map(pairs.map(([guest, host]) => [projectFolderName(guest), projectFolderName(host)]));
151
+ const transform = (t) => translateAll(t, pairs);
107
152
  const guestProjectsDir = path.join(p.guestClaudeDir, 'projects');
108
- const names = dirExists(guestProjectsDir) && !isLink(guestProjectsDir) ? fs.readdirSync(guestProjectsDir) : [];
109
- const projects = _.sumBy(names, (name) => guestProjects.has(name)
110
- ? copyNewer(path.join(guestProjectsDir, name), path.join(p.hostClaudeDir, 'projects', hostProject), { accept: transcript, skipDirs: NO_MEMORY, transform })
111
- : copyNewer(path.join(guestProjectsDir, name), path.join(p.hostClaudeDir, 'projects', name), { accept: transcript, skipDirs: NO_MEMORY }));
153
+ const names = dirExists(guestProjectsDir) && !isLink(guestProjectsDir) ? fs.readdirSync(guestProjectsDir).filter(isSafeFileName) : [];
154
+ // Only this sandbox's projects: a transcript under any other folder name would be planted into that project on this PC.
155
+ const projects = _.sumBy(names.filter((name) => byGuest.has(name)), (name) => copyNewer(path.join(guestProjectsDir, name), path.join(p.hostClaudeDir, 'projects', byGuest.get(name)), { accept: transcript, skipDirs: NO_MEMORY, transform }));
112
156
  // Only to-do lists come back besides transcripts; plans and file backups stay in the sandbox.
113
157
  const sessions = copyNewer(path.join(p.guestClaudeDir, 'todos'), path.join(p.hostClaudeDir, 'todos'), { accept: json });
114
158
  const hostFile = path.join(p.hostClaudeDir, 'history.jsonl');
115
159
  const known = new Set(readLines(hostFile));
116
- const missing = readLines(path.join(p.guestClaudeDir, 'history.jsonl')).map(transform).filter((l) => !known.has(l));
160
+ // Only prompt-history entries (one JSON object per line) of this sandbox's projects come back.
161
+ const hostWorkspaces = pairsOf(p).map((x) => x.hostWorkspace);
162
+ const missing = readLines(path.join(p.guestClaudeDir, 'history.jsonl')).filter(isJsonObject).map(transform).filter((l) => inProjects(l, hostWorkspaces) && !known.has(l));
117
163
  if (missing.length) {
118
164
  ensureDir(p.hostClaudeDir);
119
165
  fs.appendFileSync(hostFile, missing.join('\n') + '\n', 'utf8');
@@ -125,5 +171,6 @@ export const syncHistoryOut = (p) => {
125
171
  * never live in the data folder the sandbox can write (a forged copy could redirect writes anywhere on this PC).
126
172
  */
127
173
  export const syncStatePath = (dataPath) => path.join(path.dirname(path.dirname(dataPath)), 'sync', path.basename(dataPath) + '.json');
128
- export const readSyncState = (dataPath) => tryReadJson(syncStatePath(dataPath));
129
- export const writeSyncState = (dataPath, state) => writeJson(syncStatePath(dataPath), state);
174
+ /** A state file that is missing, oversized or not in this shape counts as no state (nothing syncs back until the next launch). */
175
+ export const readSyncState = (dataPath) => readValid(SyncStateSchema, syncStatePath(dataPath));
176
+ export const writeSyncState = (dataPath, state) => writeJsonAtomic(syncStatePath(dataPath), state);
@@ -1,5 +1,13 @@
1
1
  import type { Config } from '../core/types.js';
2
2
  import { type MemResult } from './claude-mem.js';
3
+ /**
4
+ * A project registered while the sandbox runs: its history comes in now (the sandbox's copy only gains files), and
5
+ * from now on syncs back to its own folder on this PC.
6
+ */
7
+ export declare const syncProjectIn: (dataPath: string, project: {
8
+ hostWorkspace: string;
9
+ guestWorkspace: string;
10
+ }) => number;
3
11
  export interface SyncSummary {
4
12
  history: number;
5
13
  claudeMem: MemResult | 'disabled';
@@ -3,10 +3,18 @@
3
3
  // Its pid file and log live in host-only storage, never in the data folder the sandbox can write.
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
+ import { writeFileAtomic } from '../shared/rundir.js';
7
+ import { refuseInSelfTest } from '../host/test-guard.js';
6
8
  import { assertNoLinks, errMsg, sleep } from '../core/util.js';
7
9
  import { hostClaudeDir } from '../host/claude-profile.js';
8
10
  import { checkRepo } from '../host/repo-guard.js';
9
11
  import { isSandboxRunning } from '../host/platform.js';
12
+ import { readActiveViews, WorkspaceView } from '../host/workspace-view/index.js';
13
+ import { readForwards, sandboxAddress, startPortForwards } from '../host/port-forward.js';
14
+ import { AuditCollector, readAuditSettings } from '../host/audit/collect.js';
15
+ import { readMarkedProjects, saveDataSize, watchAndMark } from '../host/motw.js';
16
+ import { sizeOf } from '../core/disk.js';
17
+ import { beatLife, causeSince, endLife, keepAwake, readLife } from '../host/stops.js';
10
18
  import { hostClaudeMemDir, syncClaudeMem } from './claude-mem.js';
11
19
  import { readSyncState, syncHistoryIn, syncHistoryOut, writeSyncState } from './history.js';
12
20
  const WATCH_INTERVAL_MS = 60 * 1000;
@@ -16,7 +24,21 @@ const guestMem = (dataPath) => path.join(dataPath, 'state', 'claude-mem');
16
24
  /** Sync paths for the sandbox side, after checking nothing on the way there is a link planted by the sandbox. */
17
25
  const toPaths = (state, dataPath) => {
18
26
  assertNoLinks(dataPath, guestClaude(dataPath));
19
- return { hostClaudeDir: state.hostClaudeDir, guestClaudeDir: guestClaude(dataPath), hostWorkspace: state.hostWorkspace, guestWorkspace: state.guestWorkspace };
27
+ return { hostClaudeDir: state.hostClaudeDir, guestClaudeDir: guestClaude(dataPath), hostWorkspace: state.hostWorkspace, guestWorkspace: state.guestWorkspace, extra: state.projects ?? [] };
28
+ };
29
+ /**
30
+ * A project registered while the sandbox runs: its history comes in now (the sandbox's copy only gains files), and
31
+ * from now on syncs back to its own folder on this PC.
32
+ */
33
+ export const syncProjectIn = (dataPath, project) => {
34
+ const state = readSyncState(dataPath);
35
+ if (!state)
36
+ return 0;
37
+ const projects = [...(state.projects ?? []).filter((p) => p.hostWorkspace.toLowerCase() !== project.hostWorkspace.toLowerCase()), project];
38
+ const next = { ...state, projects };
39
+ writeSyncState(dataPath, next);
40
+ // Prompt history is left alone: sessions are appending to the sandbox's copy right now.
41
+ return state.syncHistory ? syncHistoryIn(toPaths(next, dataPath), { promptHistory: false, scope: state.historyScope }) : 0;
20
42
  };
21
43
  const memSync = (dataPath, state, direction) => {
22
44
  assertNoLinks(dataPath, guestMem(dataPath));
@@ -31,11 +53,11 @@ export const syncBeforeLaunch = (config, dataPath, hostWorkspace) => {
31
53
  const previous = readSyncState(dataPath);
32
54
  if (previous?.syncHistory)
33
55
  syncHistoryOut(toPaths(previous, dataPath));
34
- const state = { ...previous, hostClaudeDir: hostClaudeDir(profile), hostWorkspace, guestWorkspace: config.guestWorkspace, syncHistory: profile.syncHistory, syncClaudeMem: profile.syncClaudeMem };
56
+ const state = { ...previous, hostClaudeDir: hostClaudeDir(profile), hostWorkspace, guestWorkspace: config.guestWorkspace, syncHistory: profile.syncHistory, historyScope: profile.historyScope, syncClaudeMem: profile.syncClaudeMem, syncClaudeMemBack: profile.syncClaudeMemBack, projects: [] };
35
57
  if (profile.syncHistory)
36
- summary.history = syncHistoryIn(toPaths(state, dataPath));
58
+ summary.history = syncHistoryIn(toPaths(state, dataPath), { scope: profile.historyScope });
37
59
  if (profile.syncClaudeMem) {
38
- if (previous?.syncClaudeMem)
60
+ if (previous?.syncClaudeMem && previous.syncClaudeMemBack)
39
61
  memSync(dataPath, state, 'out');
40
62
  summary.claudeMem = memSync(dataPath, state, 'in');
41
63
  }
@@ -50,7 +72,7 @@ export const syncBack = (dataPath, final) => {
50
72
  return summary;
51
73
  if (state.syncHistory)
52
74
  summary.history = syncHistoryOut(toPaths(state, dataPath));
53
- if (final && state.syncClaudeMem)
75
+ if (final && state.syncClaudeMem && state.syncClaudeMemBack)
54
76
  summary.claudeMem = memSync(dataPath, state, 'out');
55
77
  writeSyncState(dataPath, state);
56
78
  return summary;
@@ -58,7 +80,9 @@ export const syncBack = (dataPath, final) => {
58
80
  const hostRunDir = (dataPath) => path.join(storageOf(dataPath), 'run');
59
81
  const watcherPidFile = (dataPath) => path.join(hostRunDir(dataPath), 'watcher-' + path.basename(dataPath) + '.pid');
60
82
  const watcherLog = (dataPath) => path.join(storageOf(dataPath), 'logs', 'host-sync-' + path.basename(dataPath) + '.log');
61
- const alive = (pid) => { try {
83
+ // Signal 0 only asks whether the process exists. pid 0 would mean "my own process group", so it never counts.
84
+ const alive = (pid) => { if (!Number.isInteger(pid) || pid <= 0)
85
+ return false; try {
62
86
  process.kill(pid, 0);
63
87
  return true;
64
88
  }
@@ -75,19 +99,22 @@ export const watcherRunning = (dataPath) => {
75
99
  };
76
100
  /** Body of the detached watcher process (`agent-sandbox _watch`). */
77
101
  export const watch = async (dataPath) => {
102
+ refuseInSelfTest('start the background sync');
78
103
  if (watcherRunning(dataPath))
79
104
  return;
80
105
  fs.mkdirSync(hostRunDir(dataPath), { recursive: true });
81
106
  fs.mkdirSync(path.dirname(watcherLog(dataPath)), { recursive: true });
82
- fs.writeFileSync(watcherPidFile(dataPath), String(process.pid));
107
+ writeFileAtomic(watcherPidFile(dataPath), String(process.pid));
83
108
  const note = (message) => { try {
84
109
  fs.appendFileSync(watcherLog(dataPath), new Date().toISOString() + ' ' + message + '\n');
85
110
  }
86
111
  catch { /* ignore */ } };
87
112
  const sync = (final) => {
88
113
  try {
89
- for (const finding of checkRepo(dataPath))
114
+ for (const finding of checkRepo(dataPath)) {
90
115
  note('SECURITY: neutralised a git ' + finding.kind + ' added inside the sandbox: ' + finding.detail);
116
+ audit?.guard(finding.kind + ': ' + finding.detail);
117
+ }
91
118
  }
92
119
  catch (e) {
93
120
  note('repo guard failed: ' + errMsg(e));
@@ -101,16 +128,80 @@ export const watch = async (dataPath) => {
101
128
  note('sync failed: ' + errMsg(e));
102
129
  }
103
130
  };
131
+ // workspaceFilter: each filtered project is kept in step with its view from now until the sandbox closes.
132
+ // audit.enabled: programs, files, sessions and guard findings into the host-only audit log.
133
+ const auditSettings = readAuditSettings(storageOf(dataPath), dataPath);
134
+ const audit = auditSettings?.enabled ? new AuditCollector(dataPath, storageOf(dataPath), auditSettings) : null;
135
+ audit?.start();
136
+ const auditTimer = audit ? setInterval(() => { try {
137
+ audit.flush();
138
+ }
139
+ catch (e) {
140
+ note('audit: ' + errMsg(e));
141
+ } }, 10000) : null;
142
+ // storage.markSandboxFiles: programs and scripts that appear in the projects get the "from the internet" mark.
143
+ const stopMarking = watchAndMark(readMarkedProjects(storageOf(dataPath), dataPath), (m) => note(m));
144
+ // network.forwardPorts: localhost on this PC reaches servers in the sandbox while it runs.
145
+ const stopForwards = startPortForwards(readForwards(storageOf(dataPath), dataPath), () => sandboxAddress(dataPath), (m) => note('port forward: ' + m));
146
+ const views = readActiveViews(storageOf(dataPath), dataPath);
147
+ const stops = views.flatMap((v) => {
148
+ try {
149
+ return [new WorkspaceView({ ...v, note: (m) => note('workspace ' + v.host + ': ' + m) }).watch()];
150
+ }
151
+ catch (e) {
152
+ note('workspace ' + v.host + ': could not watch: ' + errMsg(e));
153
+ return [];
154
+ }
155
+ });
156
+ // sandbox.keepAwake, and the life record that tells the next launch how this run ended (host/stops.ts).
157
+ const awake = readLife(storageOf(dataPath), dataPath)?.keepAwake ? keepAwake(process.pid) : null;
104
158
  try {
105
159
  await sleep(30000);
106
160
  while (isSandboxRunning()) {
161
+ beatLife(storageOf(dataPath), dataPath);
107
162
  sync(false);
108
163
  await sleep(WATCH_INTERVAL_MS);
109
164
  }
165
+ try {
166
+ const life = readLife(storageOf(dataPath), dataPath);
167
+ const cause = life ? causeSince(life.lastSeen) : null;
168
+ endLife(storageOf(dataPath), dataPath, cause);
169
+ if (cause)
170
+ note('sandbox ended: ' + cause.text);
171
+ }
172
+ catch (e) {
173
+ note('could not record how the sandbox ended: ' + errMsg(e));
174
+ }
110
175
  await sleep(3000);
111
176
  sync(true);
112
177
  }
113
178
  finally {
179
+ awake?.kill();
180
+ stops.forEach((stop) => stop());
181
+ stopForwards();
182
+ stopMarking();
183
+ if (auditTimer)
184
+ clearInterval(auditTimer);
185
+ try {
186
+ audit?.stop();
187
+ }
188
+ catch (e) {
189
+ note('audit: ' + errMsg(e));
190
+ }
191
+ for (const v of views) {
192
+ try {
193
+ const s = new WorkspaceView(v).syncAll();
194
+ note('workspace ' + v.host + ': final sync ' + s.toHost + ' out, ' + s.toView + ' in');
195
+ }
196
+ catch (e) {
197
+ note('workspace ' + v.host + ': final sync failed: ' + errMsg(e));
198
+ }
199
+ }
200
+ // For the next launch's storage.warnDataGb (measuring then would be slow).
201
+ try {
202
+ saveDataSize(storageOf(dataPath), dataPath, sizeOf(dataPath));
203
+ }
204
+ catch { /* best effort */ }
114
205
  fs.rmSync(watcherPidFile(dataPath), { force: true });
115
206
  }
116
207
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windows-development-sandbox",
3
- "version": "1.0.5",
3
+ "version": "1.4.0",
4
4
  "description": "A disposable Windows Sandbox for development, with or without an AI agent (Claude Code, Codex, Gemini, opencode, ...): the folder you run it from is the workspace, toolchains and databases for your project install themselves, and nothing else on your PC is reachable.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,14 +37,14 @@
37
37
  "hooks",
38
38
  "config.example.json",
39
39
  ".sandbox-config.example.json",
40
- "README.md"
40
+ "README.md",
41
+ "LICENSE"
41
42
  ],
42
43
  "scripts": {
43
44
  "build": "tsc -p tsconfig.json && node scripts/bundle.mjs && node scripts/schemas.mjs",
44
45
  "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
45
46
  "rebuild": "npm run clean && npm run build",
46
47
  "test": "npm run build && node dist/bin/agent-sandbox.js self-test",
47
- "prepare": "npm run build",
48
48
  "prepack": "npm run rebuild",
49
49
  "release:patch": "npm version patch && git push --follow-tags",
50
50
  "release:minor": "npm version minor && git push --follow-tags",
@@ -72,6 +72,7 @@
72
72
  "dependencies": {
73
73
  "@inquirer/prompts": "^7.10.1",
74
74
  "commander": "^14.0.3",
75
+ "ignore": "^7.0.12",
75
76
  "jsonc-parser": "^3.3.1",
76
77
  "lodash": "^4.18.1",
77
78
  "zod": "^4.6.5"
@@ -84,10 +85,10 @@
84
85
  },
85
86
  "repository": {
86
87
  "type": "git",
87
- "url": "git+https://github.com/JoshDiDuca/agent-sandbox.git"
88
+ "url": "git+https://github.com/JoshDiDuca/windows-development-sandbox.git"
88
89
  },
89
- "homepage": "https://github.com/JoshDiDuca/agent-sandbox#readme",
90
+ "homepage": "https://github.com/JoshDiDuca/windows-development-sandbox#readme",
90
91
  "bugs": {
91
- "url": "https://github.com/JoshDiDuca/agent-sandbox/issues"
92
+ "url": "https://github.com/JoshDiDuca/windows-development-sandbox/issues"
92
93
  }
93
94
  }