windows-development-sandbox 1.11.7 → 1.13.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 (246) hide show
  1. package/README.md +13 -6
  2. package/config.example.json +15 -4
  3. package/dist/app/features/access/access.def.d.ts +190 -0
  4. package/dist/app/features/access/access.def.js +273 -0
  5. package/dist/app/features/access/access.run.d.ts +79 -0
  6. package/dist/app/features/access/access.run.js +129 -0
  7. package/dist/app/features/access/index.d.ts +2 -0
  8. package/dist/app/features/access/index.js +3 -0
  9. package/dist/app/features/agents/agent-entry.js +5 -0
  10. package/dist/app/features/agents/agents.def.js +1 -0
  11. package/dist/app/features/apps/apps-rows.d.ts +4 -1
  12. package/dist/app/features/apps/apps-rows.js +7 -2
  13. package/dist/app/features/apps/apps.def.d.ts +19 -0
  14. package/dist/app/features/apps/apps.def.js +50 -12
  15. package/dist/app/features/apps/apps.run.d.ts +2 -0
  16. package/dist/app/features/apps/apps.run.js +1 -1
  17. package/dist/app/features/audit/audit.def.d.ts +350 -0
  18. package/dist/app/features/audit/audit.def.js +457 -0
  19. package/dist/app/features/audit/audit.run.d.ts +110 -0
  20. package/dist/app/features/audit/audit.run.js +133 -0
  21. package/dist/app/features/audit/index.d.ts +2 -0
  22. package/dist/app/features/audit/index.js +3 -0
  23. package/dist/app/features/defs.d.ts +349 -4
  24. package/dist/app/features/defs.js +16 -1
  25. package/dist/app/features/files/files.def.js +1 -0
  26. package/dist/app/features/health/health.def.js +1 -0
  27. package/dist/app/features/sandbox/sandbox.def.js +3 -0
  28. package/dist/app/features/sessions/sessions.def.d.ts +36 -0
  29. package/dist/app/features/sessions/sessions.def.js +77 -9
  30. package/dist/app/features/sessions/sessions.run.d.ts +25 -2
  31. package/dist/app/features/sessions/sessions.run.js +71 -3
  32. package/dist/app/features/settings/settings.def.d.ts +8 -0
  33. package/dist/app/features/settings/settings.def.js +17 -0
  34. package/dist/app/features/settings/settings.run.d.ts +3 -0
  35. package/dist/app/features/settings/settings.run.js +16 -2
  36. package/dist/app/features/web/web.def.d.ts +9 -0
  37. package/dist/app/features/web/web.def.js +26 -1
  38. package/dist/app/features/web/web.run.d.ts +3 -0
  39. package/dist/app/features/web/web.run.js +11 -3
  40. package/dist/app/kernel/access.d.ts +33 -0
  41. package/dist/app/kernel/access.js +44 -0
  42. package/dist/app/kernel/define.d.ts +26 -2
  43. package/dist/app/kernel/index.d.ts +1 -0
  44. package/dist/app/kernel/index.js +1 -0
  45. package/dist/app/kernel/rpc.d.ts +5 -2
  46. package/dist/app/registry.d.ts +4 -1
  47. package/dist/app/registry.js +26 -4
  48. package/dist/cli/args.d.ts +0 -3
  49. package/dist/cli/args.js +3 -4
  50. package/dist/cli/commands/index.d.ts +1 -1
  51. package/dist/cli/commands/index.js +1 -1
  52. package/dist/cli/commands/launch/prepare.js +1 -1
  53. package/dist/cli/commands/maintenance/index.d.ts +0 -1
  54. package/dist/cli/commands/maintenance/index.js +0 -1
  55. package/dist/cli/commands/registry.js +3 -2
  56. package/dist/cli/commands/remote.d.ts +2 -0
  57. package/dist/cli/commands/remote.js +49 -0
  58. package/dist/cli/commands/sessions/reopen.js +3 -0
  59. package/dist/cli/commands/web.js +66 -0
  60. package/dist/cli/manifest.d.ts +63 -45
  61. package/dist/cli/manifest.js +33 -18
  62. package/dist/cli/menu/io-menu.d.ts +5 -3
  63. package/dist/cli/menu/io-menu.js +12 -5
  64. package/dist/cli/menu/pages/apps-page.js +2 -15
  65. package/dist/cli/menu/pages/maintenance-page.d.ts +4 -0
  66. package/dist/cli/menu/pages/maintenance-page.js +51 -8
  67. package/dist/cli/menu/remote-menu.d.ts +19 -0
  68. package/dist/cli/menu/remote-menu.js +108 -0
  69. package/dist/cli/menu/views.js +4 -0
  70. package/dist/cli/operations/handler.js +15 -1
  71. package/dist/cli/services/remote-client.d.ts +16 -0
  72. package/dist/cli/services/remote-client.js +141 -0
  73. package/dist/cli/services/sessions-service.d.ts +5 -0
  74. package/dist/cli/services/sessions-service.js +17 -1
  75. package/dist/core/config/defaults.js +12 -2
  76. package/dist/core/local-config/file.js +16 -2
  77. package/dist/core/local-config/overrides.js +7 -0
  78. package/dist/core/run/versions.js +9 -8
  79. package/dist/core/schema/apps.d.ts +9 -0
  80. package/dist/core/schema/apps.js +9 -0
  81. package/dist/core/schema/config.d.ts +23 -3
  82. package/dist/core/schema/config.js +42 -5
  83. package/dist/core/schema/sandbox.d.ts +1 -0
  84. package/dist/core/schema/sandbox.js +11 -3
  85. package/dist/core/schema/web.d.ts +51 -0
  86. package/dist/core/schema/web.js +46 -1
  87. package/dist/core/util/fs.d.ts +5 -0
  88. package/dist/core/util/fs.js +23 -0
  89. package/dist/core/util/index.d.ts +1 -1
  90. package/dist/core/util/index.js +1 -1
  91. package/dist/guest/apps/app-installer.js +50 -4
  92. package/dist/guest/apps/index.d.ts +1 -1
  93. package/dist/guest/apps/index.js +1 -1
  94. package/dist/guest/apps/sources.js +1 -1
  95. package/dist/guest/audit/parse.d.ts +32 -2
  96. package/dist/guest/audit/parse.js +43 -5
  97. package/dist/guest/audit/process-audit.d.ts +3 -1
  98. package/dist/guest/audit/process-audit.js +9 -6
  99. package/dist/guest/boot/boot.js +12 -3
  100. package/dist/guest/boot/guide.d.ts +1 -1
  101. package/dist/guest/boot/guide.js +27 -3
  102. package/dist/guest/daemon/daemon.js +2 -0
  103. package/dist/guest/daemon/idle-shutdown.js +2 -1
  104. package/dist/guest/daemon/sessions.d.ts +23 -2
  105. package/dist/guest/daemon/sessions.js +151 -20
  106. package/dist/guest/entries/daemon.js +2 -1
  107. package/dist/guest/git/decision.js +7 -0
  108. package/dist/guest/sessions/cmdline.d.ts +14 -0
  109. package/dist/guest/sessions/cmdline.js +10 -0
  110. package/dist/guest/setup/addon-mcp.d.ts +2 -0
  111. package/dist/guest/setup/addon-mcp.js +49 -0
  112. package/dist/guest/setup/agent-addons.d.ts +9 -0
  113. package/dist/guest/setup/agent-addons.js +22 -0
  114. package/dist/guest/setup/headroom.d.ts +7 -0
  115. package/dist/guest/setup/headroom.js +60 -0
  116. package/dist/guest/setup/managed-policy.d.ts +2 -1
  117. package/dist/guest/setup/managed-policy.js +15 -6
  118. package/dist/guest/toolchains/plan.js +8 -3
  119. package/dist/guest/toolchains/setup.js +3 -0
  120. package/dist/host/audit/attribute.d.ts +46 -0
  121. package/dist/host/audit/attribute.js +76 -0
  122. package/dist/host/audit/collect.d.ts +29 -3
  123. package/dist/host/audit/collect.js +256 -61
  124. package/dist/host/audit/git-commits.d.ts +29 -0
  125. package/dist/host/audit/git-commits.js +157 -0
  126. package/dist/host/audit/index.d.ts +5 -0
  127. package/dist/host/audit/index.js +6 -0
  128. package/dist/host/audit/log.d.ts +45 -7
  129. package/dist/host/audit/log.js +148 -45
  130. package/dist/host/audit/redact.d.ts +2 -0
  131. package/dist/host/audit/redact.js +67 -0
  132. package/dist/host/channel/channel.d.ts +5 -1
  133. package/dist/host/channel/channel.js +12 -0
  134. package/dist/host/guard/agent-files.js +7 -1
  135. package/dist/host/guard/commands.js +13 -1
  136. package/dist/host/guard/git-config.js +27 -7
  137. package/dist/host/guard/git-dirs.js +78 -3
  138. package/dist/host/guard/mark-of-the-web.js +3 -1
  139. package/dist/host/guard/nested-repos.js +7 -2
  140. package/dist/host/guard/store.js +1 -0
  141. package/dist/host/guard/types.d.ts +2 -0
  142. package/dist/host/launch/mappings.js +11 -1
  143. package/dist/host/launch/project-scan.js +2 -21
  144. package/dist/host/launch/stage.js +4 -0
  145. package/dist/host/life/session-ledger.js +2 -1
  146. package/dist/host/remote/access-store.d.ts +156 -0
  147. package/dist/host/remote/access-store.js +308 -0
  148. package/dist/host/remote/cert.d.ts +20 -0
  149. package/dist/host/remote/cert.js +103 -0
  150. package/dist/host/remote/door-client.d.ts +13 -0
  151. package/dist/host/remote/door-client.js +54 -0
  152. package/dist/host/remote/firewall.d.ts +44 -0
  153. package/dist/host/remote/firewall.js +118 -0
  154. package/dist/host/remote/gate.d.ts +125 -0
  155. package/dist/host/remote/gate.js +391 -0
  156. package/dist/host/remote/index.d.ts +8 -0
  157. package/dist/host/remote/index.js +10 -0
  158. package/dist/host/remote/lan.d.ts +24 -0
  159. package/dist/host/remote/lan.js +88 -0
  160. package/dist/host/remote/tls-store.d.ts +22 -0
  161. package/dist/host/remote/tls-store.js +63 -0
  162. package/dist/host/remote/windows-helper.d.ts +67 -0
  163. package/dist/host/remote/windows-helper.js +129 -0
  164. package/dist/host/web/bridge.d.ts +5 -1
  165. package/dist/host/web/bridge.js +6 -2
  166. package/dist/host/web/browser.js +1 -1
  167. package/dist/host/web/control.d.ts +4 -1
  168. package/dist/host/web/control.js +28 -4
  169. package/dist/host/web/rpc.d.ts +16 -2
  170. package/dist/host/web/rpc.js +37 -4
  171. package/dist/host/web/runner.d.ts +4 -0
  172. package/dist/host/web/runner.js +12 -0
  173. package/dist/host/web/server.d.ts +18 -2
  174. package/dist/host/web/server.js +97 -24
  175. package/dist/host/web/state.d.ts +5 -0
  176. package/dist/host/web/state.js +8 -0
  177. package/dist/runtime/agent.js +184 -18
  178. package/dist/runtime/daemon.js +772 -338
  179. package/dist/runtime/db.js +184 -18
  180. package/dist/runtime/gitguard.js +6 -0
  181. package/dist/runtime/main.js +966 -553
  182. package/dist/runtime/netproxy.js +184 -18
  183. package/dist/runtime/view.js +186 -18
  184. package/dist/selftest/app.test.js +66 -7
  185. package/dist/selftest/audit.test.js +354 -47
  186. package/dist/selftest/channel.test.js +16 -0
  187. package/dist/selftest/cmdline.test.js +16 -0
  188. package/dist/selftest/git-access.test.js +8 -0
  189. package/dist/selftest/hcs.test.js +35 -0
  190. package/dist/selftest/hostile-input.test.js +12 -1
  191. package/dist/selftest/index.js +2 -0
  192. package/dist/selftest/live-daemon.test.js +25 -0
  193. package/dist/selftest/local-config.test.js +30 -0
  194. package/dist/selftest/menu-screens.js +1 -1
  195. package/dist/selftest/menu.test.js +3 -3
  196. package/dist/selftest/remote.test.d.ts +2 -0
  197. package/dist/selftest/remote.test.js +536 -0
  198. package/dist/selftest/repo-guard.test.js +56 -2
  199. package/dist/selftest/resume.test.js +2 -1
  200. package/dist/selftest/sharing.test.js +9 -2
  201. package/dist/selftest/support/engine-golden.js +9 -0
  202. package/dist/selftest/support/fixtures.d.ts +14 -0
  203. package/dist/selftest/support/web-preview.js +157 -0
  204. package/dist/selftest/toolchains.test.js +147 -2
  205. package/dist/selftest/web.test.js +2 -0
  206. package/dist/shared/apps/agent-addons.d.ts +64 -0
  207. package/dist/shared/apps/agent-addons.js +95 -0
  208. package/dist/shared/apps/catalogue.d.ts +33 -2
  209. package/dist/shared/apps/catalogue.js +102 -11
  210. package/dist/shared/apps/index.d.ts +3 -2
  211. package/dist/shared/apps/index.js +3 -2
  212. package/dist/shared/apps/selection.d.ts +2 -0
  213. package/dist/shared/apps/selection.js +7 -0
  214. package/dist/shared/channel/schema.d.ts +23 -0
  215. package/dist/shared/channel/schema.js +8 -0
  216. package/dist/shared/pty/protocol.d.ts +2 -0
  217. package/dist/shared/pty/protocol.js +2 -0
  218. package/dist/sync/watcher.js +9 -1
  219. package/dist/web/index.html +2 -2
  220. package/dist/web/{main-RQFN4WBY.css → main-UAQPI5XD.css} +1 -1
  221. package/dist/web/main-Z2WYAE2Z.js +221 -0
  222. package/native/csharp/HcsHost/AssemblyInfo.cs +5 -3
  223. package/native/csharp/HcsHost/Hcs.cs +9 -0
  224. package/native/csharp/HcsHost/Program.cs +26 -1
  225. package/native/csharp/HcsHost/Share.cs +173 -0
  226. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  227. package/native/csharp/HostTools/Program.cs +17 -1
  228. package/native/csharp/HostTools/WebHelper.cs +499 -0
  229. package/native/scripts/get-network-profile.ps1 +19 -0
  230. package/native/scripts/set-folder-access.ps1 +11 -0
  231. package/native/scripts/set-web-firewall.ps1 +103 -0
  232. package/package.json +1 -1
  233. package/sandbox/addons/ccusage/package-lock.json +111 -0
  234. package/sandbox/addons/ccusage/package.json +7 -0
  235. package/sandbox/addons/context7/package-lock.json +1313 -0
  236. package/sandbox/addons/context7/package.json +7 -0
  237. package/sandbox/addons/headroom/requirements.in +1 -0
  238. package/sandbox/addons/headroom/requirements.txt +2922 -0
  239. package/sandbox/addons/playwright-mcp/package-lock.json +56 -0
  240. package/sandbox/addons/playwright-mcp/package.json +7 -0
  241. package/sandbox/csharp/PtyHost.cs +20 -0
  242. package/schemas/config.schema.json +78 -4
  243. package/schemas/sandbox-config.schema.json +2 -20
  244. package/dist/cli/commands/maintenance/audit.d.ts +0 -3
  245. package/dist/cli/commands/maintenance/audit.js +0 -58
  246. package/dist/web/main-K2JMCAZI.js +0 -219
@@ -1363,11 +1363,11 @@ var require_lodash = __commonJS({
1363
1363
  return isFunction(object2[key]);
1364
1364
  });
1365
1365
  }
1366
- function baseGet(object2, path75) {
1367
- path75 = castPath(path75, object2);
1368
- var index = 0, length = path75.length;
1366
+ function baseGet(object2, path77) {
1367
+ path77 = castPath(path77, object2);
1368
+ var index = 0, length = path77.length;
1369
1369
  while (object2 != null && index < length) {
1370
- object2 = object2[toKey(path75[index++])];
1370
+ object2 = object2[toKey(path77[index++])];
1371
1371
  }
1372
1372
  return index && index == length ? object2 : undefined2;
1373
1373
  }
@@ -1431,10 +1431,10 @@ var require_lodash = __commonJS({
1431
1431
  });
1432
1432
  return accumulator;
1433
1433
  }
1434
- function baseInvoke(object2, path75, args) {
1435
- path75 = castPath(path75, object2);
1436
- object2 = parent(object2, path75);
1437
- var func = object2 == null ? object2 : object2[toKey(last(path75))];
1434
+ function baseInvoke(object2, path77, args) {
1435
+ path77 = castPath(path77, object2);
1436
+ object2 = parent(object2, path77);
1437
+ var func = object2 == null ? object2 : object2[toKey(last(path77))];
1438
1438
  return func == null ? undefined2 : apply(func, object2, args);
1439
1439
  }
1440
1440
  function baseIsArguments(value) {
@@ -1590,13 +1590,13 @@ var require_lodash = __commonJS({
1590
1590
  return object2 === source || baseIsMatch(object2, source, matchData);
1591
1591
  };
1592
1592
  }
1593
- function baseMatchesProperty(path75, srcValue) {
1594
- if (isKey(path75) && isStrictComparable(srcValue)) {
1595
- return matchesStrictComparable(toKey(path75), srcValue);
1593
+ function baseMatchesProperty(path77, srcValue) {
1594
+ if (isKey(path77) && isStrictComparable(srcValue)) {
1595
+ return matchesStrictComparable(toKey(path77), srcValue);
1596
1596
  }
1597
1597
  return function(object2) {
1598
- var objValue = get(object2, path75);
1599
- return objValue === undefined2 && objValue === srcValue ? hasIn(object2, path75) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
1598
+ var objValue = get(object2, path77);
1599
+ return objValue === undefined2 && objValue === srcValue ? hasIn(object2, path77) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
1600
1600
  };
1601
1601
  }
1602
1602
  function baseMerge(object2, source, srcIndex, customizer, stack) {
@@ -1693,23 +1693,23 @@ var require_lodash = __commonJS({
1693
1693
  });
1694
1694
  }
1695
1695
  function basePick(object2, paths) {
1696
- return basePickBy(object2, paths, function(value, path75) {
1697
- return hasIn(object2, path75);
1696
+ return basePickBy(object2, paths, function(value, path77) {
1697
+ return hasIn(object2, path77);
1698
1698
  });
1699
1699
  }
1700
1700
  function basePickBy(object2, paths, predicate) {
1701
1701
  var index = -1, length = paths.length, result2 = {};
1702
1702
  while (++index < length) {
1703
- var path75 = paths[index], value = baseGet(object2, path75);
1704
- if (predicate(value, path75)) {
1705
- baseSet(result2, castPath(path75, object2), value);
1703
+ var path77 = paths[index], value = baseGet(object2, path77);
1704
+ if (predicate(value, path77)) {
1705
+ baseSet(result2, castPath(path77, object2), value);
1706
1706
  }
1707
1707
  }
1708
1708
  return result2;
1709
1709
  }
1710
- function basePropertyDeep(path75) {
1710
+ function basePropertyDeep(path77) {
1711
1711
  return function(object2) {
1712
- return baseGet(object2, path75);
1712
+ return baseGet(object2, path77);
1713
1713
  };
1714
1714
  }
1715
1715
  function basePullAll(array2, values2, iteratee2, comparator) {
@@ -1783,14 +1783,14 @@ var require_lodash = __commonJS({
1783
1783
  var array2 = values(collection);
1784
1784
  return shuffleSelf(array2, baseClamp(n, 0, array2.length));
1785
1785
  }
1786
- function baseSet(object2, path75, value, customizer) {
1786
+ function baseSet(object2, path77, value, customizer) {
1787
1787
  if (!isObject2(object2)) {
1788
1788
  return object2;
1789
1789
  }
1790
- path75 = castPath(path75, object2);
1791
- var index = -1, length = path75.length, lastIndex = length - 1, nested = object2;
1790
+ path77 = castPath(path77, object2);
1791
+ var index = -1, length = path77.length, lastIndex = length - 1, nested = object2;
1792
1792
  while (nested != null && ++index < length) {
1793
- var key = toKey(path75[index]), newValue = value;
1793
+ var key = toKey(path77[index]), newValue = value;
1794
1794
  if (key === "__proto__" || key === "constructor" || key === "prototype") {
1795
1795
  return object2;
1796
1796
  }
@@ -1798,7 +1798,7 @@ var require_lodash = __commonJS({
1798
1798
  var objValue = nested[key];
1799
1799
  newValue = customizer ? customizer(objValue, key, nested) : undefined2;
1800
1800
  if (newValue === undefined2) {
1801
- newValue = isObject2(objValue) ? objValue : isIndex(path75[index + 1]) ? [] : {};
1801
+ newValue = isObject2(objValue) ? objValue : isIndex(path77[index + 1]) ? [] : {};
1802
1802
  }
1803
1803
  }
1804
1804
  assignValue(nested, key, newValue);
@@ -1964,14 +1964,14 @@ var require_lodash = __commonJS({
1964
1964
  }
1965
1965
  return result2;
1966
1966
  }
1967
- function baseUnset(object2, path75) {
1968
- path75 = castPath(path75, object2);
1969
- var index = -1, length = path75.length;
1967
+ function baseUnset(object2, path77) {
1968
+ path77 = castPath(path77, object2);
1969
+ var index = -1, length = path77.length;
1970
1970
  if (!length) {
1971
1971
  return true;
1972
1972
  }
1973
1973
  while (++index < length) {
1974
- var key = toKey(path75[index]);
1974
+ var key = toKey(path77[index]);
1975
1975
  if (key === "__proto__" && !hasOwnProperty.call(object2, "__proto__")) {
1976
1976
  return false;
1977
1977
  }
@@ -1979,11 +1979,11 @@ var require_lodash = __commonJS({
1979
1979
  return false;
1980
1980
  }
1981
1981
  }
1982
- var obj = parent(object2, path75);
1983
- return obj == null || delete obj[toKey(last(path75))];
1982
+ var obj = parent(object2, path77);
1983
+ return obj == null || delete obj[toKey(last(path77))];
1984
1984
  }
1985
- function baseUpdate(object2, path75, updater, customizer) {
1986
- return baseSet(object2, path75, updater(baseGet(object2, path75)), customizer);
1985
+ function baseUpdate(object2, path77, updater, customizer) {
1986
+ return baseSet(object2, path77, updater(baseGet(object2, path77)), customizer);
1987
1987
  }
1988
1988
  function baseWhile(array2, predicate, isDrop, fromRight) {
1989
1989
  var length = array2.length, index = fromRight ? length : -1;
@@ -2866,11 +2866,11 @@ var require_lodash = __commonJS({
2866
2866
  var match = source.match(reWrapDetails);
2867
2867
  return match ? match[1].split(reSplitDetails) : [];
2868
2868
  }
2869
- function hasPath(object2, path75, hasFunc) {
2870
- path75 = castPath(path75, object2);
2871
- var index = -1, length = path75.length, result2 = false;
2869
+ function hasPath(object2, path77, hasFunc) {
2870
+ path77 = castPath(path77, object2);
2871
+ var index = -1, length = path77.length, result2 = false;
2872
2872
  while (++index < length) {
2873
- var key = toKey(path75[index]);
2873
+ var key = toKey(path77[index]);
2874
2874
  if (!(result2 = object2 != null && hasFunc(object2, key))) {
2875
2875
  break;
2876
2876
  }
@@ -3072,8 +3072,8 @@ var require_lodash = __commonJS({
3072
3072
  return apply(func, this, otherArgs);
3073
3073
  };
3074
3074
  }
3075
- function parent(object2, path75) {
3076
- return path75.length < 2 ? object2 : baseGet(object2, baseSlice(path75, 0, -1));
3075
+ function parent(object2, path77) {
3076
+ return path77.length < 2 ? object2 : baseGet(object2, baseSlice(path77, 0, -1));
3077
3077
  }
3078
3078
  function reorder(array2, indexes) {
3079
3079
  var arrLength = array2.length, length = nativeMin(indexes.length, arrLength), oldArray = copyArray(array2);
@@ -3708,10 +3708,10 @@ var require_lodash = __commonJS({
3708
3708
  }
3709
3709
  return isString(collection) ? fromIndex <= length && collection.indexOf(value, fromIndex) > -1 : !!length && baseIndexOf(collection, value, fromIndex) > -1;
3710
3710
  }
3711
- var invokeMap = baseRest(function(collection, path75, args) {
3712
- var index = -1, isFunc = typeof path75 == "function", result2 = isArrayLike(collection) ? Array2(collection.length) : [];
3711
+ var invokeMap = baseRest(function(collection, path77, args) {
3712
+ var index = -1, isFunc = typeof path77 == "function", result2 = isArrayLike(collection) ? Array2(collection.length) : [];
3713
3713
  baseEach(collection, function(value) {
3714
- result2[++index] = isFunc ? apply(path75, value, args) : baseInvoke(value, path75, args);
3714
+ result2[++index] = isFunc ? apply(path77, value, args) : baseInvoke(value, path77, args);
3715
3715
  });
3716
3716
  return result2;
3717
3717
  });
@@ -4363,15 +4363,15 @@ var require_lodash = __commonJS({
4363
4363
  function functionsIn(object2) {
4364
4364
  return object2 == null ? [] : baseFunctions(object2, keysIn(object2));
4365
4365
  }
4366
- function get(object2, path75, defaultValue) {
4367
- var result2 = object2 == null ? undefined2 : baseGet(object2, path75);
4366
+ function get(object2, path77, defaultValue) {
4367
+ var result2 = object2 == null ? undefined2 : baseGet(object2, path77);
4368
4368
  return result2 === undefined2 ? defaultValue : result2;
4369
4369
  }
4370
- function has(object2, path75) {
4371
- return object2 != null && hasPath(object2, path75, baseHas);
4370
+ function has(object2, path77) {
4371
+ return object2 != null && hasPath(object2, path77, baseHas);
4372
4372
  }
4373
- function hasIn(object2, path75) {
4374
- return object2 != null && hasPath(object2, path75, baseHasIn);
4373
+ function hasIn(object2, path77) {
4374
+ return object2 != null && hasPath(object2, path77, baseHasIn);
4375
4375
  }
4376
4376
  var invert = createInverter(function(result2, value, key) {
4377
4377
  if (value != null && typeof value.toString != "function") {
@@ -4424,10 +4424,10 @@ var require_lodash = __commonJS({
4424
4424
  return result2;
4425
4425
  }
4426
4426
  var isDeep = false;
4427
- paths = arrayMap(paths, function(path75) {
4428
- path75 = castPath(path75, object2);
4429
- isDeep || (isDeep = path75.length > 1);
4430
- return path75;
4427
+ paths = arrayMap(paths, function(path77) {
4428
+ path77 = castPath(path77, object2);
4429
+ isDeep || (isDeep = path77.length > 1);
4430
+ return path77;
4431
4431
  });
4432
4432
  copyObject(object2, getAllKeysIn(object2), result2);
4433
4433
  if (isDeep) {
@@ -4453,19 +4453,19 @@ var require_lodash = __commonJS({
4453
4453
  return [prop];
4454
4454
  });
4455
4455
  predicate = getIteratee(predicate);
4456
- return basePickBy(object2, props, function(value, path75) {
4457
- return predicate(value, path75[0]);
4456
+ return basePickBy(object2, props, function(value, path77) {
4457
+ return predicate(value, path77[0]);
4458
4458
  });
4459
4459
  }
4460
- function result(object2, path75, defaultValue) {
4461
- path75 = castPath(path75, object2);
4462
- var index = -1, length = path75.length;
4460
+ function result(object2, path77, defaultValue) {
4461
+ path77 = castPath(path77, object2);
4462
+ var index = -1, length = path77.length;
4463
4463
  if (!length) {
4464
4464
  length = 1;
4465
4465
  object2 = undefined2;
4466
4466
  }
4467
4467
  while (++index < length) {
4468
- var value = object2 == null ? undefined2 : object2[toKey(path75[index])];
4468
+ var value = object2 == null ? undefined2 : object2[toKey(path77[index])];
4469
4469
  if (value === undefined2) {
4470
4470
  index = length;
4471
4471
  value = defaultValue;
@@ -4474,12 +4474,12 @@ var require_lodash = __commonJS({
4474
4474
  }
4475
4475
  return object2;
4476
4476
  }
4477
- function set2(object2, path75, value) {
4478
- return object2 == null ? object2 : baseSet(object2, path75, value);
4477
+ function set2(object2, path77, value) {
4478
+ return object2 == null ? object2 : baseSet(object2, path77, value);
4479
4479
  }
4480
- function setWith(object2, path75, value, customizer) {
4480
+ function setWith(object2, path77, value, customizer) {
4481
4481
  customizer = typeof customizer == "function" ? customizer : undefined2;
4482
- return object2 == null ? object2 : baseSet(object2, path75, value, customizer);
4482
+ return object2 == null ? object2 : baseSet(object2, path77, value, customizer);
4483
4483
  }
4484
4484
  var toPairs = createToPairs(keys);
4485
4485
  var toPairsIn = createToPairs(keysIn);
@@ -4501,15 +4501,15 @@ var require_lodash = __commonJS({
4501
4501
  });
4502
4502
  return accumulator;
4503
4503
  }
4504
- function unset(object2, path75) {
4505
- return object2 == null ? true : baseUnset(object2, path75);
4504
+ function unset(object2, path77) {
4505
+ return object2 == null ? true : baseUnset(object2, path77);
4506
4506
  }
4507
- function update(object2, path75, updater) {
4508
- return object2 == null ? object2 : baseUpdate(object2, path75, castFunction(updater));
4507
+ function update(object2, path77, updater) {
4508
+ return object2 == null ? object2 : baseUpdate(object2, path77, castFunction(updater));
4509
4509
  }
4510
- function updateWith(object2, path75, updater, customizer) {
4510
+ function updateWith(object2, path77, updater, customizer) {
4511
4511
  customizer = typeof customizer == "function" ? customizer : undefined2;
4512
- return object2 == null ? object2 : baseUpdate(object2, path75, castFunction(updater), customizer);
4512
+ return object2 == null ? object2 : baseUpdate(object2, path77, castFunction(updater), customizer);
4513
4513
  }
4514
4514
  function values(object2) {
4515
4515
  return object2 == null ? [] : baseValues(object2, keys(object2));
@@ -4895,17 +4895,17 @@ var require_lodash = __commonJS({
4895
4895
  function matches(source) {
4896
4896
  return baseMatches(baseClone(source, CLONE_DEEP_FLAG));
4897
4897
  }
4898
- function matchesProperty(path75, srcValue) {
4899
- return baseMatchesProperty(path75, baseClone(srcValue, CLONE_DEEP_FLAG));
4898
+ function matchesProperty(path77, srcValue) {
4899
+ return baseMatchesProperty(path77, baseClone(srcValue, CLONE_DEEP_FLAG));
4900
4900
  }
4901
- var method = baseRest(function(path75, args) {
4901
+ var method = baseRest(function(path77, args) {
4902
4902
  return function(object2) {
4903
- return baseInvoke(object2, path75, args);
4903
+ return baseInvoke(object2, path77, args);
4904
4904
  };
4905
4905
  });
4906
4906
  var methodOf = baseRest(function(object2, args) {
4907
- return function(path75) {
4908
- return baseInvoke(object2, path75, args);
4907
+ return function(path77) {
4908
+ return baseInvoke(object2, path77, args);
4909
4909
  };
4910
4910
  });
4911
4911
  function mixin(object2, source, options) {
@@ -4952,12 +4952,12 @@ var require_lodash = __commonJS({
4952
4952
  var over = createOver(arrayMap);
4953
4953
  var overEvery = createOver(arrayEvery);
4954
4954
  var overSome = createOver(arraySome);
4955
- function property(path75) {
4956
- return isKey(path75) ? baseProperty(toKey(path75)) : basePropertyDeep(path75);
4955
+ function property(path77) {
4956
+ return isKey(path77) ? baseProperty(toKey(path77)) : basePropertyDeep(path77);
4957
4957
  }
4958
4958
  function propertyOf(object2) {
4959
- return function(path75) {
4960
- return object2 == null ? undefined2 : baseGet(object2, path75);
4959
+ return function(path77) {
4960
+ return object2 == null ? undefined2 : baseGet(object2, path77);
4961
4961
  };
4962
4962
  }
4963
4963
  var range = createRange();
@@ -5410,12 +5410,12 @@ var require_lodash = __commonJS({
5410
5410
  LazyWrapper.prototype.findLast = function(predicate) {
5411
5411
  return this.reverse().find(predicate);
5412
5412
  };
5413
- LazyWrapper.prototype.invokeMap = baseRest(function(path75, args) {
5414
- if (typeof path75 == "function") {
5413
+ LazyWrapper.prototype.invokeMap = baseRest(function(path77, args) {
5414
+ if (typeof path77 == "function") {
5415
5415
  return new LazyWrapper(this);
5416
5416
  }
5417
5417
  return this.map(function(value) {
5418
- return baseInvoke(value, path75, args);
5418
+ return baseInvoke(value, path77, args);
5419
5419
  });
5420
5420
  });
5421
5421
  LazyWrapper.prototype.reject = function(predicate) {
@@ -5532,8 +5532,8 @@ var require_lodash = __commonJS({
5532
5532
  });
5533
5533
 
5534
5534
  // src/guest/entries/main.ts
5535
- import fs61 from "node:fs";
5536
- import path74 from "node:path";
5535
+ import fs63 from "node:fs";
5536
+ import path76 from "node:path";
5537
5537
 
5538
5538
  // src/guest/runtime/paths.ts
5539
5539
  import path7 from "node:path";
@@ -6358,10 +6358,10 @@ function mergeDefs(...defs) {
6358
6358
  function cloneDef(schema) {
6359
6359
  return mergeDefs(schema._zod.def);
6360
6360
  }
6361
- function getElementAtPath(obj, path75) {
6362
- if (!path75)
6361
+ function getElementAtPath(obj, path77) {
6362
+ if (!path77)
6363
6363
  return obj;
6364
- return path75.reduce((acc, key) => acc?.[key], obj);
6364
+ return path77.reduce((acc, key) => acc?.[key], obj);
6365
6365
  }
6366
6366
  function promiseAllObject(promisesObj) {
6367
6367
  const keys = Object.keys(promisesObj);
@@ -6701,11 +6701,11 @@ function explicitlyAborted(x, startIndex = 0) {
6701
6701
  }
6702
6702
  return false;
6703
6703
  }
6704
- function prefixIssues(path75, issues) {
6704
+ function prefixIssues(path77, issues) {
6705
6705
  return issues.map((iss) => {
6706
6706
  var _a3;
6707
6707
  (_a3 = iss).path ?? (_a3.path = []);
6708
- iss.path.unshift(path75);
6708
+ iss.path.unshift(path77);
6709
6709
  return iss;
6710
6710
  });
6711
6711
  }
@@ -7155,16 +7155,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
7155
7155
  }
7156
7156
  function formatError(error62, mapper = (issue2) => issue2.message) {
7157
7157
  const fieldErrors = { _errors: [] };
7158
- const processError = (error63, path75 = []) => {
7158
+ const processError = (error63, path77 = []) => {
7159
7159
  for (const issue2 of error63.issues) {
7160
7160
  if (issue2.code === "invalid_union" && issue2.errors.length) {
7161
- issue2.errors.map((issues) => processError({ issues }, [...path75, ...issue2.path]));
7161
+ issue2.errors.map((issues) => processError({ issues }, [...path77, ...issue2.path]));
7162
7162
  } else if (issue2.code === "invalid_key") {
7163
- processError({ issues: issue2.issues }, [...path75, ...issue2.path]);
7163
+ processError({ issues: issue2.issues }, [...path77, ...issue2.path]);
7164
7164
  } else if (issue2.code === "invalid_element") {
7165
- processError({ issues: issue2.issues }, [...path75, ...issue2.path]);
7165
+ processError({ issues: issue2.issues }, [...path77, ...issue2.path]);
7166
7166
  } else {
7167
- const fullpath = [...path75, ...issue2.path];
7167
+ const fullpath = [...path77, ...issue2.path];
7168
7168
  if (fullpath.length === 0) {
7169
7169
  fieldErrors._errors.push(mapper(issue2));
7170
7170
  } else {
@@ -7203,17 +7203,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
7203
7203
  }
7204
7204
  function treeifyError(error62, mapper = (issue2) => issue2.message) {
7205
7205
  const result = { errors: [] };
7206
- const processError = (error63, path75 = []) => {
7206
+ const processError = (error63, path77 = []) => {
7207
7207
  var _a3;
7208
7208
  for (const issue2 of error63.issues) {
7209
7209
  if (issue2.code === "invalid_union" && issue2.errors.length) {
7210
- issue2.errors.map((issues) => processError({ issues }, [...path75, ...issue2.path]));
7210
+ issue2.errors.map((issues) => processError({ issues }, [...path77, ...issue2.path]));
7211
7211
  } else if (issue2.code === "invalid_key") {
7212
- processError({ issues: issue2.issues }, [...path75, ...issue2.path]);
7212
+ processError({ issues: issue2.issues }, [...path77, ...issue2.path]);
7213
7213
  } else if (issue2.code === "invalid_element") {
7214
- processError({ issues: issue2.issues }, [...path75, ...issue2.path]);
7214
+ processError({ issues: issue2.issues }, [...path77, ...issue2.path]);
7215
7215
  } else {
7216
- const fullpath = [...path75, ...issue2.path];
7216
+ const fullpath = [...path77, ...issue2.path];
7217
7217
  if (fullpath.length === 0) {
7218
7218
  result.errors.push(mapper(issue2));
7219
7219
  continue;
@@ -7252,8 +7252,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
7252
7252
  }
7253
7253
  function toDotPath(_path) {
7254
7254
  const segs = [];
7255
- const path75 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
7256
- for (const seg of path75) {
7255
+ const path77 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
7256
+ for (const seg of path77) {
7257
7257
  if (typeof seg === "number")
7258
7258
  segs.push(`[${seg}]`);
7259
7259
  else if (typeof seg === "symbol")
@@ -24355,13 +24355,13 @@ function resolveRef(ref, ctx) {
24355
24355
  if (!ref.startsWith("#")) {
24356
24356
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
24357
24357
  }
24358
- const path75 = ref.slice(1).split("/").filter(Boolean);
24359
- if (path75.length === 0) {
24358
+ const path77 = ref.slice(1).split("/").filter(Boolean);
24359
+ if (path77.length === 0) {
24360
24360
  return ctx.rootSchema;
24361
24361
  }
24362
24362
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
24363
- if (path75[0] === defsKey) {
24364
- const key = path75[1] === void 0 ? void 0 : decodeJSONPointerSegment(path75[1]);
24363
+ if (path77[0] === defsKey) {
24364
+ const key = path77[1] === void 0 ? void 0 : decodeJSONPointerSegment(path77[1]);
24365
24365
  if (!key || !ctx.defs[key]) {
24366
24366
  throw new Error(`Reference not found: ${ref}`);
24367
24367
  }
@@ -25534,14 +25534,19 @@ var NetworkSchema = external_exports.strictObject({
25534
25534
  });
25535
25535
  var AuditSchema = external_exports.strictObject({
25536
25536
  enabled: external_exports.boolean().describe(
25537
- "Keep an audit log on this PC of what happened in your sandboxes: programs started (with their command lines), files changed in the project, sessions, and what the repo guard caught. The sandbox cannot change it. Read it with sandbox audit."
25537
+ "Keep an audit log on this PC of what your agents did: programs started (with their command lines, secrets hidden), files changed in the projects, git commits, sessions, and what the repo guard caught, each linked to the agent session that did it where that can be known. The sandbox cannot change it. Read it with sandbox audit or on the Activity page."
25538
+ ),
25539
+ days: external_exports.number().int().min(1).max(365).describe(
25540
+ "Days the audit log is kept (1 to 30; a larger number keeps 30); older days are deleted when a sandbox starts and once a day."
25538
25541
  ),
25539
- days: external_exports.number().int().min(1).max(365).describe("Days the audit log is kept."),
25540
25542
  maxMb: external_exports.number().int().min(5).max(5e3).describe("Most space the audit log may take, in MB (older days go first). Days before today are compressed."),
25541
25543
  processes: external_exports.boolean().describe(
25542
25544
  "Record every program started in the sandbox with its command line (Windows process auditing; repeated runs are counted, not listed)."
25543
25545
  ),
25544
- files: external_exports.boolean().describe("Record the files changed in the project while the sandbox runs.")
25546
+ files: external_exports.boolean().describe("Record the files changed in the projects while the sandbox runs."),
25547
+ commits: external_exports.boolean().describe(
25548
+ "Record the git commits made in the projects while the sandbox runs (read from each project's .git folder on this PC)."
25549
+ )
25545
25550
  });
25546
25551
  var WorkspaceFilterSchema = external_exports.strictObject({
25547
25552
  enabled: external_exports.boolean().describe(
@@ -25744,7 +25749,100 @@ var ToolchainsSchema = external_exports.strictObject({
25744
25749
 
25745
25750
  // src/shared/apps/catalogue.ts
25746
25751
  var gh = (github, asset) => ({ github, asset });
25752
+ var RTK_VERSION = "1.0.1";
25753
+ var RTK_URL = "https://github.com/rtk-ai/rtk/releases/download/v" + RTK_VERSION + "/rtk-oss-x86_64-pc-windows-msvc.zip";
25754
+ var RTK_SHA256 = "0e53e2eca471e15fbd3636e64e6bc0b182cbc278ed0339c80450a2dd03f4cd6b";
25755
+ var HEADROOM_VERSION = "0.41.0";
25756
+ var CCUSAGE_VERSION = "20.0.36";
25757
+ var PLAYWRIGHT_MCP_VERSION = "0.0.83";
25758
+ var CONTEXT7_MCP_VERSION = "4.4.2";
25747
25759
  var APPS = [
25760
+ // ---- agent add-ons
25761
+ {
25762
+ id: "rtk",
25763
+ name: "rtk (Rust Token Killer)",
25764
+ category: "agents",
25765
+ about: "Compacts the output of shell commands (git, tests, builds, ls ...) before the agent reads it, so it uses fewer tokens. Claude Code's Bash commands go through it on their own.",
25766
+ size: "6 MB",
25767
+ source: { url: RTK_URL, sha256: RTK_SHA256 },
25768
+ exe: "rtk.exe",
25769
+ cli: true,
25770
+ plugsInto: "Claude Code through a hook in the sandbox's managed settings; other agents are told to use it",
25771
+ userOnly: true
25772
+ },
25773
+ {
25774
+ id: "headroom",
25775
+ name: "Headroom",
25776
+ category: "agents",
25777
+ about: "Compresses what Claude Code sends to the model (tool output, logs, files) so it uses fewer tokens: a local proxy on 127.0.0.1:8787 that sits in Claude Code's API path, offline apart from forwarding to api.anthropic.com.",
25778
+ size: "150 MB",
25779
+ source: { locked: "uv", package: "headroom-ai", version: HEADROOM_VERSION },
25780
+ exe: "headroom.exe",
25781
+ service: true,
25782
+ plugsInto: "Claude Code: the sandbox starts the proxy and points Claude Code at it (managed settings)",
25783
+ userOnly: true
25784
+ },
25785
+ {
25786
+ id: "ccusage",
25787
+ name: "ccusage",
25788
+ category: "agents",
25789
+ about: "Reports the agents' token use and cost by day, month or session from their logs in the sandbox (`ccusage daily`; add --offline to skip fetching prices).",
25790
+ size: "5 MB",
25791
+ source: { locked: "npm", package: "ccusage", version: CCUSAGE_VERSION },
25792
+ exe: "ccusage.cmd",
25793
+ cli: true,
25794
+ plugsInto: "Reads Claude Code's session logs in the sandbox; changes nothing",
25795
+ userOnly: true
25796
+ },
25797
+ {
25798
+ id: "playwright-mcp",
25799
+ name: "Playwright MCP",
25800
+ category: "agents",
25801
+ about: "Lets Claude Code drive a browser (Microsoft Edge, already in the sandbox) to open pages, click, fill forms and take screenshots: the MCP server of microsoft/playwright-mcp.",
25802
+ size: "20 MB",
25803
+ source: { locked: "npm", package: "@playwright/mcp", version: PLAYWRIGHT_MCP_VERSION },
25804
+ exe: "playwright-mcp.cmd",
25805
+ service: true,
25806
+ mcp: { name: "sbx-playwright", script: "node_modules/@playwright/mcp/cli.js", args: ["--browser", "msedge"] },
25807
+ plugsInto: "Claude Code as the MCP server sbx-playwright (the sandbox's Claude settings)",
25808
+ userOnly: true
25809
+ },
25810
+ {
25811
+ id: "context7",
25812
+ name: "Context7 MCP",
25813
+ category: "agents",
25814
+ about: "Up-to-date documentation and code examples for libraries, looked up by Claude Code while it works (upstash/context7). The library names and questions go to Upstash's context7.com.",
25815
+ size: "55 MB",
25816
+ source: { locked: "npm", package: "@upstash/context7-mcp", version: CONTEXT7_MCP_VERSION },
25817
+ exe: "context7-mcp.cmd",
25818
+ service: true,
25819
+ mcp: { name: "sbx-context7", script: "node_modules/@upstash/context7-mcp/dist/index.js" },
25820
+ plugsInto: "Claude Code as the MCP server sbx-context7 (the sandbox's Claude settings)",
25821
+ userOnly: true
25822
+ },
25823
+ {
25824
+ id: "claude-mem",
25825
+ name: "claude-mem",
25826
+ category: "agents",
25827
+ about: "Persistent memory for Claude Code (a plugin you install on this PC): its database is brought into the sandbox.",
25828
+ size: "",
25829
+ source: { resolve: "setting" },
25830
+ exe: "",
25831
+ setting: "claudeProfile.syncClaudeMem",
25832
+ plugsInto: "Claude Code, with your Claude settings copied from this PC",
25833
+ userOnly: true
25834
+ },
25835
+ {
25836
+ id: "rea",
25837
+ name: "REA (Reverse Engineer Anything)",
25838
+ category: "agents",
25839
+ about: "Agent-driven reverse engineering (MCP server and CLI, by morluto). Run `rea setup` once to add it to Claude Code and Codex. Native binaries need Ghidra; needs Node 22.19+.",
25840
+ size: "8 MB",
25841
+ source: { npm: "rea-agents" },
25842
+ exe: "rea.cmd",
25843
+ cli: true,
25844
+ plugsInto: "Claude Code and Codex as an MCP server, after `rea setup`"
25845
+ },
25748
25846
  // ---- editors
25749
25847
  {
25750
25848
  id: "vscode",
@@ -26206,16 +26304,6 @@ var APPS = [
26206
26304
  cli: true
26207
26305
  },
26208
26306
  // ---- reverse engineering
26209
- {
26210
- id: "rea",
26211
- name: "REA (Reverse Engineer Anything)",
26212
- category: "reversing",
26213
- about: "Agent-driven reverse engineering (MCP server and CLI, by morluto). Run `rea setup` once to add it to Claude Code and Codex. Native binaries need Ghidra; needs Node 22.19+.",
26214
- size: "8 MB",
26215
- source: { npm: "rea-agents" },
26216
- exe: "rea.cmd",
26217
- cli: true
26218
- },
26219
26307
  {
26220
26308
  id: "ghidra",
26221
26309
  name: "Ghidra",
@@ -26528,13 +26616,87 @@ function appsToInstall(apps, gitAccess) {
26528
26616
  const needs7zip = allowed.some((e) => e.needs?.includes("7zip") || e.unpack === "nsis") && !allowed.some((e) => e.id === "7zip");
26529
26617
  return { entries: [...needs7zip ? [appById("7zip")] : [], ...allowed], skipped };
26530
26618
  }
26619
+ function appsNeedUv(apps) {
26620
+ return Boolean(
26621
+ apps?.install.some((id) => {
26622
+ const source = appById(id)?.source;
26623
+ return source !== void 0 && "locked" in source && source.locked === "uv";
26624
+ })
26625
+ );
26626
+ }
26531
26627
  function appsNeedMesa(apps) {
26532
26628
  return Boolean(apps?.install.some((id) => appById(id)?.needs?.includes("mesa")));
26533
26629
  }
26534
26630
 
26631
+ // src/shared/apps/agent-addons.ts
26632
+ function rtkHookCommand(exe) {
26633
+ return '"' + exe.replace(/\\/g, "/") + '" hook claude';
26634
+ }
26635
+ function claudeManagedSettings(deny, rtkExe, headroomUrl2 = null) {
26636
+ if (!deny.length && !rtkExe && !headroomUrl2) return null;
26637
+ return {
26638
+ ...deny.length ? { permissions: { deny } } : {},
26639
+ ...rtkExe ? { hooks: { PreToolUse: [{ matcher: "Bash", hooks: [{ type: "command", command: rtkHookCommand(rtkExe) }] }] } } : {},
26640
+ ...headroomUrl2 ? { env: { ANTHROPIC_BASE_URL: headroomUrl2 } } : {}
26641
+ };
26642
+ }
26643
+ var RTK_GUIDE_LINE = "- rtk is on PATH and compacts command output: Claude Code's Bash commands go through it on their own; in other agents prefix shell commands with `rtk` (`rtk git status`, `rtk cargo test`, `rtk ls`).";
26644
+ var HEADROOM_PORT = 8787;
26645
+ var HEADROOM_UPSTREAM = "https://api.anthropic.com";
26646
+ function headroomUrl(port = HEADROOM_PORT) {
26647
+ return "http://127.0.0.1:" + port;
26648
+ }
26649
+ function headroomArgs(port = HEADROOM_PORT) {
26650
+ return [
26651
+ "proxy",
26652
+ "--host",
26653
+ "127.0.0.1",
26654
+ "--port",
26655
+ String(port),
26656
+ "--anthropic-api-url",
26657
+ HEADROOM_UPSTREAM,
26658
+ "--no-telemetry"
26659
+ ];
26660
+ }
26661
+ var HEADROOM_ENV = {
26662
+ HEADROOM_OFFLINE: "1",
26663
+ HEADROOM_TELEMETRY: "off",
26664
+ HEADROOM_BEACON: "off",
26665
+ HEADROOM_UPDATE_CHECK: "off",
26666
+ DO_NOT_TRACK: "1",
26667
+ HF_HUB_OFFLINE: "1",
26668
+ PYTHONIOENCODING: "utf-8"
26669
+ };
26670
+ var SECRET_NAME = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)/i;
26671
+ function headroomEnv(base) {
26672
+ const env = {};
26673
+ for (const [name, value] of Object.entries(base))
26674
+ if (value !== void 0 && !SECRET_NAME.test(name) && !/^(ANTHROPIC|OPENAI|CLAUDE)_/i.test(name)) env[name] = value;
26675
+ return { ...env, ...HEADROOM_ENV };
26676
+ }
26677
+ function headroomGuideLine(logFile2) {
26678
+ return "- Headroom compresses Claude Code's requests: Claude Code reaches the API through its proxy on " + headroomUrl() + " (log: `" + logFile2 + "`). If Claude Code cannot reach the API, the proxy stopped: it starts again with the next sandbox, or turn Headroom off in Apps.";
26679
+ }
26680
+ function addonMcpServer(folder, mcp) {
26681
+ const script = folder.replace(/[\\/]+$/, "") + "\\" + mcp.script.replace(/\//g, "\\");
26682
+ return { type: "stdio", command: "node", args: [script, ...mcp.args ?? []], env: {} };
26683
+ }
26684
+ function withAddonMcpServers(current, servers, known) {
26685
+ const kept = Object.fromEntries(Object.entries(current).filter(([name]) => !known.includes(name) || name in servers));
26686
+ return { ...kept, ...servers };
26687
+ }
26688
+ function addonGuideLine(mcpNames, ccusage) {
26689
+ return [
26690
+ ...mcpNames.length ? ["- MCP servers in Claude Code from Apps: " + mcpNames.join(", ") + "."] : [],
26691
+ ...ccusage ? ["- ccusage is on PATH: `ccusage daily` (or `monthly`, `session`) reports token use and cost."] : []
26692
+ ];
26693
+ }
26694
+
26535
26695
  // src/core/schema/apps.ts
26536
26696
  var CATALOGUE_IDS = APPS.filter((a) => !a.setting).map((a) => a.id);
26537
26697
  var AppIdSchema = external_exports.enum(CATALOGUE_IDS);
26698
+ var PROJECT_IDS = APPS.filter((a) => !a.setting && !a.userOnly).map((a) => a.id);
26699
+ var ProjectAppIdSchema = external_exports.enum(PROJECT_IDS);
26538
26700
  var CustomAppIdSchema = external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,40}$/, "lower-case letters, digits and - only");
26539
26701
  var compiles = (pattern) => {
26540
26702
  try {
@@ -26558,6 +26720,11 @@ var CustomAppSchema = external_exports.strictObject({
26558
26720
  }).refine((a) => a.github && a.asset ? !a.url : Boolean(a.url) && !a.github && !a.asset, {
26559
26721
  message: "give github + asset, or url (not both)"
26560
26722
  });
26723
+ var ProjectAppsSchema = external_exports.strictObject({
26724
+ install: external_exports.array(ProjectAppIdSchema).describe(
26725
+ "Apps from the catalogue this project adds to yours (agent add-ons such as rtk, Headroom and the MCP servers are set in your own settings only)."
26726
+ )
26727
+ });
26561
26728
  var AppsSchema = external_exports.strictObject({
26562
26729
  install: external_exports.array(AppIdSchema).describe(
26563
26730
  "Apps from the catalogue to install in every sandbox (the Apps screen in the menu lists them by category). Each is downloaded once and kept."
@@ -26598,12 +26765,39 @@ var ResumeSchema = external_exports.strictObject({
26598
26765
  });
26599
26766
 
26600
26767
  // src/core/schema/web.ts
26768
+ var REMOTE_ROLES = ["view", "control"];
26769
+ var WindowsUserNameSchema = external_exports.string().trim().min(1).max(120).regex(
26770
+ /^(?:[A-Za-z0-9][A-Za-z0-9._ -]{0,62}\\)?[^\\/"[\]:;|=,+*?<>@]{1,64}$/,
26771
+ "a Windows user name (name or DOMAIN\\name)"
26772
+ );
26773
+ var RemoteUserSchema = external_exports.strictObject({
26774
+ name: WindowsUserNameSchema.describe("The Windows user (name, or DOMAIN\\name)"),
26775
+ role: external_exports.enum(REMOTE_ROLES).describe("view: sees everything, terminals read-only. control: everything but remote access, logins and tokens.")
26776
+ });
26777
+ var WebLanSchema = external_exports.strictObject({
26778
+ enabled: external_exports.boolean().describe(
26779
+ "(Beta) Devices on your network (a phone): an HTTPS address on this PC for devices you pair with a one-time code (sandbox web pair). Needs a firewall rule (sandbox web lan on asks Windows once). Off: every paired device is signed out."
26780
+ ),
26781
+ port: external_exports.number().int().min(1024).max(65535).describe("HTTPS port for paired devices on your network."),
26782
+ address: external_exports.string().trim().max(15).regex(/^$|^\d{1,3}(\.\d{1,3}){3}$/, "an IPv4 address or empty").describe("This PC's private address to listen on. Empty: the one of its network adapter (never a public one).")
26783
+ });
26784
+ var WebRemoteSchema = external_exports.strictObject({
26785
+ windowsSignIn: external_exports.boolean().describe(
26786
+ "(Beta) Other Windows users of this PC named in web.remote.users sign in to your web UI (and sandbox remote) with their own Windows account: no password, no link."
26787
+ ),
26788
+ users: external_exports.array(RemoteUserSchema).max(32).describe("Who else may sign in with Windows, and their role (view or control)."),
26789
+ rememberDays: external_exports.number().int().min(1).max(90).describe(
26790
+ "How many days a paired device or a Windows sign-in stays signed in (from when it signed in, not renewed)."
26791
+ ),
26792
+ lan: WebLanSchema
26793
+ });
26601
26794
  var WebSchema = external_exports.strictObject({
26602
26795
  enabled: external_exports.boolean().describe(
26603
26796
  "(Beta) The web UI: your sandbox sessions in a browser on this PC (http://127.0.0.1:<port>, this PC only). On: every sandbox start also starts it in the background (it keeps running until sandbox web stop or a restart of the PC). Off: only sandbox web start runs it. Open it signed in with sandbox web open."
26604
26797
  ),
26605
26798
  port: external_exports.number().int().min(1024).max(65535).describe("Port of the web UI on 127.0.0.1 (this PC only). Pick another when it is taken."),
26606
- openOnLaunch: external_exports.boolean().describe("(Beta) With the web UI on: open it in your browser, already signed in, every time a sandbox starts.")
26799
+ openOnLaunch: external_exports.boolean().describe("(Beta) With the web UI on: open it in your browser, already signed in, every time a sandbox starts."),
26800
+ remote: WebRemoteSchema.describe("(Beta) Access for other Windows users of this PC and for paired devices.")
26607
26801
  });
26608
26802
 
26609
26803
  // src/core/schema/vpn.ts
@@ -26633,6 +26827,36 @@ var GithubSchema = external_exports.strictObject({
26633
26827
  });
26634
26828
 
26635
26829
  // src/core/schema/config.ts
26830
+ var RESERVED_ENV_PREFIX = /^(AGENT_|GIT_|GH_|GITHUB_|NODE_|NPM_CONFIG_(USERCONFIG|GLOBALCONFIG|PREFIX|SCRIPT_SHELL)|SSL_|CLAUDE|ANTHROPIC_|OPENAI_|CODEX_|GEMINI_|GOOGLE_|COPILOT_|AWS_|AZURE_)/i;
26831
+ var RESERVED_ENV_SUFFIX = /(_PROXY|_BASE_URL|_ENDPOINT|_CA_BUNDLE|_CA_CERTS)$/i;
26832
+ var RESERVED_ENV_NAMES = new Set(
26833
+ [
26834
+ "PATH",
26835
+ "PATHEXT",
26836
+ "BASH_ENV",
26837
+ "ENV",
26838
+ "COMSPEC",
26839
+ "PSMODULEPATH",
26840
+ "PYTHONPATH",
26841
+ "PYTHONSTARTUP",
26842
+ "PERL5OPT",
26843
+ "RUBYOPT",
26844
+ "JAVA_TOOL_OPTIONS",
26845
+ "LD_PRELOAD",
26846
+ "SYSTEMROOT",
26847
+ "WINDIR",
26848
+ "HOME",
26849
+ "USERPROFILE",
26850
+ "APPDATA",
26851
+ "LOCALAPPDATA",
26852
+ "PROGRAMDATA",
26853
+ "TEMP",
26854
+ "TMP"
26855
+ ].map((n) => n.toUpperCase())
26856
+ );
26857
+ function reservedEnvName(name) {
26858
+ return RESERVED_ENV_PREFIX.test(name) || RESERVED_ENV_SUFFIX.test(name) || RESERVED_ENV_NAMES.has(name.toUpperCase());
26859
+ }
26636
26860
  var ConfigSchema = external_exports.strictObject({
26637
26861
  $schema: SchemaRefSchema.optional(),
26638
26862
  defaultHarness: NameSchema.describe("Agent started by `sandbox launch` (sandbox claude always runs claude)."),
@@ -26699,10 +26923,14 @@ var LocalOverridesSchema = external_exports.strictObject({
26699
26923
  scripts: StringsSchema.describe(
26700
26924
  "Scripts (.ps1 / .mjs / .cmd, relative to this file) run inside the sandbox before the agent."
26701
26925
  ),
26702
- // This PC's resources are yours to share: CPU and paging file (this PC's disk) are not a project's to set.
26926
+ // This PC's resources are yours to share: CPU, paging file (this PC's disk) and the RAM kept for this PC are not a
26927
+ // project's to set.
26703
26928
  sandbox: SandboxSchema.omit({
26704
26929
  cpuLimit: true,
26705
26930
  pageFileMb: true,
26931
+ reserveHostMb: true,
26932
+ maxMemoryPercent: true,
26933
+ maxMemoryMb: true,
26706
26934
  engine: true,
26707
26935
  agentMemoryHeadroomMb: true,
26708
26936
  pressureResponse: true
@@ -26716,14 +26944,17 @@ var LocalOverridesSchema = external_exports.strictObject({
26716
26944
  // Every toolchain setting, each one optional (a project usually only switches languages on or pins a version).
26717
26945
  toolchains: external_exports.strictObject({
26718
26946
  ...import_lodash.default.mapValues(import_lodash.default.omit(ToolchainsSchema.shape, "environment"), (shape) => shape.partial()),
26719
- environment: ToolchainsSchema.shape.environment
26947
+ environment: ToolchainsSchema.shape.environment.refine(
26948
+ (env) => !Object.keys(env).some(reservedEnvName),
26949
+ "names the sandbox sets or that carry logins (AGENT_*, GIT_*, PATH, BASH_ENV, NODE_*, proxies, ANTHROPIC_*, CLAUDE_* ...) cannot be set by a project"
26950
+ )
26720
26951
  }).partial(),
26721
26952
  databases: external_exports.strictObject(import_lodash.default.mapValues(DatabasesSchema.shape, (shape) => shape.partial())).partial(),
26722
26953
  attach: AttachSchema.partial(),
26723
26954
  personalization: toggle("Copy your desktop look into the sandbox.").partial(),
26724
26955
  // Catalogue apps only (a vetted list): your own programs (apps.custom) download anything, so a repo cannot add
26725
- // them.
26726
- apps: AppsSchema.pick({ install: true }).partial(),
26956
+ // them, nor agent add-ons that are yours only (rtk hooks every agent command, Headroom sits in its API path).
26957
+ apps: ProjectAppsSchema.partial(),
26727
26958
  claudeProfile: ClaudeProfileSchema.pick({
26728
26959
  enabled: true,
26729
26960
  overwrite: true,
@@ -26786,6 +27017,7 @@ var ProjectSpecSchema = external_exports.strictObject({
26786
27017
  /** Its hooks were staged in C:\AgentLaunch\projects\<name>\hooks. */
26787
27018
  hooks: external_exports.boolean()
26788
27019
  });
27020
+ var ShellChoiceSchema = external_exports.enum(["auto", "pwsh", "powershell", "cmd"]);
26789
27021
  var GuestRequestSchema = external_exports.discriminatedUnion("kind", [
26790
27022
  external_exports.strictObject({
26791
27023
  kind: external_exports.literal("session"),
@@ -26795,8 +27027,12 @@ var GuestRequestSchema = external_exports.discriminatedUnion("kind", [
26795
27027
  cwd: Text(),
26796
27028
  project: ProjectSpecSchema.optional(),
26797
27029
  harnessSpec: HarnessSchema.optional(),
27030
+ /** A plain shell instead of an agent (harness is then "shell" and args are ignored). */
27031
+ shell: ShellChoiceSchema.optional(),
26798
27032
  createdAt: TimeSchema
26799
27033
  }),
27034
+ /** End a running session (an agent or a shell) and everything it started. */
27035
+ external_exports.strictObject({ kind: external_exports.literal("close"), id: IdSchema, session: IdSchema, createdAt: TimeSchema }),
26800
27036
  /** Provision a registered project without starting an agent (sandbox start / exec / db from its folder). */
26801
27037
  external_exports.strictObject({ kind: external_exports.literal("project"), id: IdSchema, project: ProjectSpecSchema, createdAt: TimeSchema }),
26802
27038
  external_exports.strictObject({
@@ -26862,6 +27098,8 @@ var SessionStatusSchema = external_exports.object({
26862
27098
  id: IdSchema,
26863
27099
  launchId: Text(64),
26864
27100
  harness: Text(64),
27101
+ /** A plain shell (harness is then the shell: pwsh, powershell or cmd); absent: an agent. */
27102
+ kind: external_exports.enum(["agent", "shell"]).optional(),
26865
27103
  cwd: Text(),
26866
27104
  /** The project it runs in: its hub name, or the launch folder's for the first project. */
26867
27105
  project: Text(64).optional(),
@@ -27531,92 +27769,52 @@ function writeStatus(phase, message) {
27531
27769
  }
27532
27770
 
27533
27771
  // src/guest/boot/boot.ts
27534
- import fs60 from "node:fs";
27535
- import path73 from "node:path";
27536
- import { spawn as spawn9 } from "node:child_process";
27772
+ import fs62 from "node:fs";
27773
+ import path75 from "node:path";
27774
+ import { spawn as spawn10 } from "node:child_process";
27537
27775
 
27538
27776
  // src/guest/boot/guide.ts
27777
+ import fs12 from "node:fs";
27778
+ import path17 from "node:path";
27779
+
27780
+ // src/guest/apps/find-exe.ts
27539
27781
  import fs10 from "node:fs";
27540
27782
  import path14 from "node:path";
27541
- var BEGIN = "<!-- agent-sandbox:begin (generated every boot, do not edit) -->";
27542
- var END = "<!-- agent-sandbox:end -->";
27543
- var mirrorLine = () => {
27544
- const drives = (session.substDrives ?? []).filter((d) => fs10.existsSync(d.letter + ":\\"));
27545
- if (!drives.length) return [];
27546
- return [
27547
- "- " + drives.map((d) => d.letter + ":").join(", ") + " = mapped folders at their PC paths (subst onto `" + drives.map((d) => d.target).join("`, `") + "`; real-path tools may print that form)."
27548
- ];
27549
- };
27550
- function sandboxGuide(toolSummary, lan = "blocked", projects = []) {
27551
- return [
27552
- BEGIN,
27553
- "## Sandbox manifest \u2014 you are in a DISPOSABLE Windows Sandbox",
27554
- "Wiped on close: everything except the paths below (installers, winget/choco, C:\\Users, Program Files, registry).",
27555
- "- RW, persists: `" + workspace + "` = host folder " + session.workspaceHost,
27556
- // Projects started from other folders while this sandbox runs: every agent here can see them.
27557
- ...projects.map(
27558
- (p) => "- RW, persists: `" + p.guest + "` = host folder " + p.host + " (another project in this sandbox; your own is `%AGENT_WORKSPACE%`)"
27559
- ),
27560
- "- RW, persists: `" + toolsDir + "` (toolchains) \u2014 put portable exes in `" + path14.join(toolsDir, "bin") + "` (on PATH)",
27561
- "- RW, persists: `" + stateDir + "` (agent config/history; do not edit logins) \xB7 logs: `" + path14.join(dataDir, "logs") + "`",
27562
- "- RO: `" + launchDir + "` (this boot's scripts) \xB7 lost on close: everything else, incl. `C:\\nvm` (Node symlink)",
27563
- ...mirrorLine(),
27564
- "- Tools: " + toolSummary.join(" \xB7 "),
27565
- "- Persistent installs: `npm i -g` (per nvm version), `pip`, `cargo install`, `dotnet tool -g`, `uv tool`, `bun add -g`. Real installers must re-run each boot via a hook (ask the user).",
27566
- "- Mapped folders (workspace, C:\\AgentData) allow NO symlinks or junctions: pnpm runs hoisted (set), npm workspaces / `file:` deps and deno node_modules dirs fail there (use a folder on C:\\ outside them), bun cannot save its lockfile there (`bun install --no-save`).",
27567
- "- Network: " + (lan === "offline" ? "OFF" : lan === "blocked" ? "internet yes, host LAN blocked" : "internet yes, host LAN NOT blocked this time") + ". Other sessions may share this workspace; the user may be typing from the host.",
27568
- END
27569
- ].join("\n");
27570
- }
27571
- function upsertGuide(file3, guide) {
27572
- fs10.mkdirSync(path14.dirname(file3), { recursive: true });
27573
- const current = fs10.existsSync(file3) ? fs10.readFileSync(file3, "utf8") : "";
27574
- const start = current.indexOf(BEGIN);
27575
- const end = current.indexOf(END);
27576
- const without = start >= 0 && end > start ? current.slice(0, start) + current.slice(end + END.length) : current;
27577
- fs10.writeFileSync(file3, without.trimEnd() + (without.trim() ? "\n\n" : "") + guide + "\n", "utf8");
27578
- }
27579
- function managedClaudeMd() {
27580
- return path14.join(
27581
- process.env.AGENT_MANAGED_DIR ?? path14.join(process.env.ProgramFiles ?? "C:\\Program Files", "ClaudeCode"),
27582
- "CLAUDE.md"
27583
- );
27584
- }
27585
- function writeGuides(toolSummary, lan, projects = []) {
27586
- const guide = sandboxGuide(toolSummary, lan, projects);
27587
- const managed = managedClaudeMd();
27588
- fs10.mkdirSync(path14.dirname(managed), { recursive: true });
27589
- fs10.writeFileSync(managed, guide + "\n", "utf8");
27590
- fs10.writeFileSync(path14.join(dataDir, "SANDBOX.md"), guide + "\n", "utf8");
27591
- upsertGuide(path14.join(stateDir, "codex", "AGENTS.md"), guide);
27592
- upsertGuide(path14.join(stateDir, "gemini", ".gemini", "GEMINI.md"), guide);
27593
- removeGuide(path14.join(claudeDir, "CLAUDE.md"));
27594
- return managed;
27595
- }
27596
- var removeGuide = (file3) => {
27597
- if (!fs10.existsSync(file3)) return;
27598
- const current = fs10.readFileSync(file3, "utf8");
27599
- const start = current.indexOf(BEGIN);
27600
- const end = current.indexOf(END);
27601
- if (start >= 0 && end > start)
27602
- fs10.writeFileSync(file3, (current.slice(0, start) + current.slice(end + END.length)).trim() + "\n", "utf8");
27603
- };
27604
-
27605
- // src/guest/runtime/fs.ts
27606
- import fs11 from "node:fs";
27607
- function exists(p) {
27608
- return fs11.existsSync(p);
27609
- }
27610
- function mkdirp(p) {
27611
- fs11.mkdirSync(p, { recursive: true });
27612
- return p;
27783
+ var MAX_DEPTH = 7;
27784
+ var MAX_ENTRIES = 5e4;
27785
+ var X64_FOLDER = /(^|[\\/])(amd64|x64|x86_64|win64)([\\/]|$)/i;
27786
+ function findExe(root, exe) {
27787
+ const match = exeMatcher(exe);
27788
+ const best = (hits) => hits.find((h) => X64_FOLDER.test(path14.relative(root, h))) ?? hits[0] ?? null;
27789
+ let queue = [root];
27790
+ let seen = 0;
27791
+ for (let depth = 0; depth < MAX_DEPTH && queue.length; depth++) {
27792
+ const next = [];
27793
+ const hits = [];
27794
+ for (const dir of queue) {
27795
+ let entries3;
27796
+ try {
27797
+ entries3 = fs10.readdirSync(dir, { withFileTypes: true });
27798
+ } catch {
27799
+ continue;
27800
+ }
27801
+ const hit = entries3.find((e) => e.isFile() && match(e.name));
27802
+ if (hit) hits.push(path14.join(dir, hit.name));
27803
+ else
27804
+ next.push(...entries3.filter((e) => e.isDirectory() && !e.isSymbolicLink()).map((e) => path14.join(dir, e.name)));
27805
+ if ((seen += entries3.length) > MAX_ENTRIES) return best(hits);
27806
+ }
27807
+ if (hits.length) return best(hits);
27808
+ queue = next;
27809
+ }
27810
+ return null;
27613
27811
  }
27614
27812
 
27615
27813
  // src/guest/toolchains/tool-env.ts
27616
27814
  import path16 from "node:path";
27617
27815
 
27618
27816
  // src/guest/runtime/process.ts
27619
- import fs12 from "node:fs";
27817
+ import fs11 from "node:fs";
27620
27818
  import os4 from "node:os";
27621
27819
  import path15 from "node:path";
27622
27820
  import { spawnSync } from "node:child_process";
@@ -27651,24 +27849,24 @@ function scriptOutput(name, args = [], options = {}) {
27651
27849
  return execOutput(SYSTEM.powershell(), scriptArgs(name, args), options);
27652
27850
  }
27653
27851
  function runScriptWithJson(name, param, data, args = []) {
27654
- const dir = fs12.mkdtempSync(path15.join(os4.tmpdir(), "sbx-"));
27852
+ const dir = fs11.mkdtempSync(path15.join(os4.tmpdir(), "sbx-"));
27655
27853
  const file3 = path15.join(dir, "input.json");
27656
27854
  try {
27657
- fs12.writeFileSync(file3, JSON.stringify(data), "utf8");
27855
+ fs11.writeFileSync(file3, JSON.stringify(data), "utf8");
27658
27856
  runScript(name, ["-" + param, file3, ...args]);
27659
27857
  } finally {
27660
- fs12.rmSync(dir, { recursive: true, force: true });
27858
+ fs11.rmSync(dir, { recursive: true, force: true });
27661
27859
  }
27662
27860
  }
27663
27861
  function robocopy(from, to, files = []) {
27664
27862
  if (!isWin) {
27665
27863
  if (!files.length) {
27666
- fs12.cpSync(from, to, { recursive: true });
27864
+ fs11.cpSync(from, to, { recursive: true });
27667
27865
  return;
27668
27866
  }
27669
- fs12.mkdirSync(to, { recursive: true });
27670
- for (const f of files.filter((f2) => fs12.existsSync(path15.join(from, f2))))
27671
- fs12.copyFileSync(path15.join(from, f), path15.join(to, f));
27867
+ fs11.mkdirSync(to, { recursive: true });
27868
+ for (const f of files.filter((f2) => fs11.existsSync(path15.join(from, f2))))
27869
+ fs11.copyFileSync(path15.join(from, f), path15.join(to, f));
27672
27870
  return;
27673
27871
  }
27674
27872
  const what = files.length ? files : ["/E"];
@@ -27729,13 +27927,216 @@ function versionOf(exe, args = ["--version"], options = {}) {
27729
27927
  return /\d+\.\d+\.\d+\S*/.exec(execOutput(exe, args, options))?.[0] ?? "installed";
27730
27928
  }
27731
27929
 
27930
+ // src/guest/setup/agent-addons.ts
27931
+ function installedAddon(id) {
27932
+ const entry = appById(id);
27933
+ if (dryRun || !entry || !(session.apps?.install ?? []).includes(id)) return null;
27934
+ return findExe(tool("apps", id), entry.exe);
27935
+ }
27936
+ function installedRtk() {
27937
+ return installedAddon("rtk");
27938
+ }
27939
+ function installedMcpAddons() {
27940
+ return APPS.filter((a) => a.mcp && installedAddon(a.id)).map((a) => ({ id: a.id, folder: tool("apps", a.id) }));
27941
+ }
27942
+
27943
+ // src/guest/boot/guide.ts
27944
+ var BEGIN = "<!-- agent-sandbox:begin (generated every boot, do not edit) -->";
27945
+ var END = "<!-- agent-sandbox:end -->";
27946
+ var mirrorLine = () => {
27947
+ const drives = (session.substDrives ?? []).filter((d) => fs12.existsSync(d.letter + ":\\"));
27948
+ if (!drives.length) return [];
27949
+ return [
27950
+ "- " + drives.map((d) => d.letter + ":").join(", ") + " = mapped folders at their PC paths (subst onto `" + drives.map((d) => d.target).join("`, `") + "`; real-path tools may print that form)."
27951
+ ];
27952
+ };
27953
+ function sandboxGuide(toolSummary, lan = "blocked", projects = [], addons = []) {
27954
+ return [
27955
+ BEGIN,
27956
+ "## Sandbox manifest \u2014 you are in a DISPOSABLE Windows Sandbox",
27957
+ "Wiped on close: everything except the paths below (installers, winget/choco, C:\\Users, Program Files, registry).",
27958
+ "- RW, persists: `" + workspace + "` = host folder " + session.workspaceHost,
27959
+ // Projects started from other folders while this sandbox runs: every agent here can see them.
27960
+ ...projects.map(
27961
+ (p) => "- RW, persists: `" + p.guest + "` = host folder " + p.host + " (another project in this sandbox; your own is `%AGENT_WORKSPACE%`)"
27962
+ ),
27963
+ "- RW, persists: `" + toolsDir + "` (toolchains) \u2014 put portable exes in `" + path17.join(toolsDir, "bin") + "` (on PATH)",
27964
+ "- RW, persists: `" + stateDir + "` (agent config/history; do not edit logins) \xB7 logs: `" + path17.join(dataDir, "logs") + "`",
27965
+ "- RO: `" + launchDir + "` (this boot's scripts) \xB7 lost on close: everything else, incl. `C:\\nvm` (Node symlink)",
27966
+ ...mirrorLine(),
27967
+ "- Tools: " + toolSummary.join(" \xB7 "),
27968
+ // Agent add-ons that change how the agent works (rtk, Headroom, MCP servers, ccusage).
27969
+ ...addons,
27970
+ "- Persistent installs: `npm i -g` (per nvm version), `pip`, `cargo install`, `dotnet tool -g`, `uv tool`, `bun add -g`. Real installers must re-run each boot via a hook (ask the user).",
27971
+ "- Mapped folders (workspace, C:\\AgentData) allow NO symlinks or junctions: pnpm runs hoisted (set), npm workspaces / `file:` deps and deno node_modules dirs fail there (use a folder on C:\\ outside them), bun cannot save its lockfile there (`bun install --no-save`).",
27972
+ "- Network: " + (lan === "offline" ? "OFF" : lan === "blocked" ? "internet yes, host LAN blocked" : "internet yes, host LAN NOT blocked this time") + ". Other sessions may share this workspace; the user may be typing from the host.",
27973
+ END
27974
+ ].join("\n");
27975
+ }
27976
+ function upsertGuide(file3, guide) {
27977
+ fs12.mkdirSync(path17.dirname(file3), { recursive: true });
27978
+ const current = fs12.existsSync(file3) ? fs12.readFileSync(file3, "utf8") : "";
27979
+ const start = current.indexOf(BEGIN);
27980
+ const end = current.indexOf(END);
27981
+ const without = start >= 0 && end > start ? current.slice(0, start) + current.slice(end + END.length) : current;
27982
+ fs12.writeFileSync(file3, without.trimEnd() + (without.trim() ? "\n\n" : "") + guide + "\n", "utf8");
27983
+ }
27984
+ function managedClaudeMd() {
27985
+ return path17.join(
27986
+ process.env.AGENT_MANAGED_DIR ?? path17.join(process.env.ProgramFiles ?? "C:\\Program Files", "ClaudeCode"),
27987
+ "CLAUDE.md"
27988
+ );
27989
+ }
27990
+ var headroomRuns = () => {
27991
+ try {
27992
+ const file3 = path17.join(path17.dirname(managedClaudeMd()), "managed-settings.json");
27993
+ const settings = JSON.parse(fs12.readFileSync(file3, "utf8"));
27994
+ return typeof settings.env?.ANTHROPIC_BASE_URL === "string";
27995
+ } catch {
27996
+ return false;
27997
+ }
27998
+ };
27999
+ function addonLines() {
28000
+ const mcp = APPS.flatMap((a) => a.mcp && installedAddon(a.id) ? [a.mcp.name] : []);
28001
+ return [
28002
+ ...installedRtk() ? [RTK_GUIDE_LINE] : [],
28003
+ ...installedAddon("headroom") && headroomRuns() ? [headroomGuideLine(path17.join(logsDir, "headroom.log"))] : [],
28004
+ ...addonGuideLine(mcp, Boolean(installedAddon("ccusage")))
28005
+ ];
28006
+ }
28007
+ function writeGuides(toolSummary, lan, projects = []) {
28008
+ const guide = sandboxGuide(toolSummary, lan, projects, addonLines());
28009
+ const managed = managedClaudeMd();
28010
+ fs12.mkdirSync(path17.dirname(managed), { recursive: true });
28011
+ fs12.writeFileSync(managed, guide + "\n", "utf8");
28012
+ fs12.writeFileSync(path17.join(dataDir, "SANDBOX.md"), guide + "\n", "utf8");
28013
+ upsertGuide(path17.join(stateDir, "codex", "AGENTS.md"), guide);
28014
+ upsertGuide(path17.join(stateDir, "gemini", ".gemini", "GEMINI.md"), guide);
28015
+ removeGuide(path17.join(claudeDir, "CLAUDE.md"));
28016
+ return managed;
28017
+ }
28018
+ var removeGuide = (file3) => {
28019
+ if (!fs12.existsSync(file3)) return;
28020
+ const current = fs12.readFileSync(file3, "utf8");
28021
+ const start = current.indexOf(BEGIN);
28022
+ const end = current.indexOf(END);
28023
+ if (start >= 0 && end > start)
28024
+ fs12.writeFileSync(file3, (current.slice(0, start) + current.slice(end + END.length)).trim() + "\n", "utf8");
28025
+ };
28026
+
28027
+ // src/guest/runtime/fs.ts
28028
+ import fs13 from "node:fs";
28029
+ function exists(p) {
28030
+ return fs13.existsSync(p);
28031
+ }
28032
+ function mkdirp(p) {
28033
+ fs13.mkdirSync(p, { recursive: true });
28034
+ return p;
28035
+ }
28036
+
27732
28037
  // src/guest/apps/app-installer.ts
27733
- import fs16 from "node:fs";
27734
- import path21 from "node:path";
28038
+ import fs17 from "node:fs";
28039
+ import path22 from "node:path";
28040
+
28041
+ // src/guest/toolchains/runtimes.ts
28042
+ import path18 from "node:path";
28043
+
28044
+ // src/guest/toolchains/install.ts
28045
+ import fs14 from "node:fs";
28046
+ async function downloadFile(url2, file3, sha256 = null) {
28047
+ const part = file3 + ".part";
28048
+ try {
28049
+ await downloader.file(url2, part, sha256);
28050
+ fs14.rmSync(file3, { force: true });
28051
+ fs14.renameSync(part, file3);
28052
+ } finally {
28053
+ fs14.rmSync(part, { force: true });
28054
+ }
28055
+ }
28056
+ async function installFromGithub(repo, pattern, o) {
28057
+ const asset = await githubAsset(repo, pattern, o.searchOlder);
28058
+ log("Downloading " + (typeof o.what === "string" ? o.what : o.what(asset)) + " ...");
28059
+ await downloader.archive(asset.url, o.dest, {
28060
+ sha256: asset.sha256,
28061
+ fileName: asset.name,
28062
+ ...o.locate ? { locate: o.locate } : {},
28063
+ ...o.up ? { up: o.up } : {}
28064
+ });
28065
+ }
28066
+
28067
+ // src/guest/toolchains/runtimes.ts
28068
+ async function setupSingleExe(env, name, repo, pattern, exeName, dir, options = {}) {
28069
+ if (dryRun) {
28070
+ log("(dry run) would provision " + name);
28071
+ return;
28072
+ }
28073
+ const bin = options.subdir ? path18.join(dir, options.subdir) : dir;
28074
+ const exe = path18.join(bin, exeName);
28075
+ if (!exists(exe) || options.refresh) {
28076
+ const size = options.size ? ", ~" + options.size : "";
28077
+ await installFromGithub(repo, pattern, {
28078
+ dest: dir,
28079
+ what: (a) => name + " (" + a.name + size + " once)",
28080
+ ...options.subdir ? {} : { locate: exeName }
28081
+ });
28082
+ }
28083
+ if (!exists(exe)) throw new Error(exeName + " missing after installing " + name);
28084
+ env.prepend.push(bin);
28085
+ env.summary.push(name + ": " + versionOf(exe, options.versionArgs) + (options.note ?? ""));
28086
+ }
28087
+ async function setupBun(env) {
28088
+ await setupSingleExe(
28089
+ env,
28090
+ "bun",
28091
+ "oven-sh/bun",
28092
+ arch === "arm64" ? /^bun-windows-aarch64\.zip$/ : /^bun-windows-x64\.zip$/,
28093
+ "bun.exe",
28094
+ tool("bun", "bin"),
28095
+ { size: "35 MB" }
28096
+ );
28097
+ Object.assign(env.vars, {
28098
+ BUN_INSTALL: tool("bun"),
28099
+ BUN_INSTALL_CACHE_DIR: path18.join(localDir, "bun-cache"),
28100
+ BUN_TMPDIR: path18.join(localDir, "bun-tmp")
28101
+ });
28102
+ }
28103
+ async function setupDeno(env) {
28104
+ await setupSingleExe(
28105
+ env,
28106
+ "deno",
28107
+ "denoland/deno",
28108
+ arch === "arm64" ? /^deno-aarch64-pc-windows-msvc[.]zip$/ : /^deno-x86_64-pc-windows-msvc[.]zip$/,
28109
+ "deno.exe",
28110
+ tool("deno", "bin"),
28111
+ { size: "40 MB" }
28112
+ );
28113
+ Object.assign(env.vars, { DENO_DIR: tool("deno-cache"), DENO_INSTALL_ROOT: tool("deno"), DENO_NO_UPDATE_CHECK: "1" });
28114
+ }
28115
+ function uvVars() {
28116
+ return {
28117
+ UV_CACHE_DIR: tool("uv-cache"),
28118
+ UV_PYTHON_INSTALL_DIR: tool("uv-python"),
28119
+ UV_TOOL_DIR: tool("uv-tools"),
28120
+ UV_TOOL_BIN_DIR: tool("uv-bin")
28121
+ };
28122
+ }
28123
+ async function setupUv(env) {
28124
+ await setupSingleExe(
28125
+ env,
28126
+ "uv",
28127
+ "astral-sh/uv",
28128
+ arch === "arm64" ? /^uv-aarch64-pc-windows-msvc\.zip$/ : /^uv-x86_64-pc-windows-msvc\.zip$/,
28129
+ "uv.exe",
28130
+ tool("uv"),
28131
+ { size: "20 MB" }
28132
+ );
28133
+ Object.assign(env.vars, uvVars());
28134
+ env.prepend.push(mkdirp(tool("uv-bin")));
28135
+ }
27735
28136
 
27736
28137
  // src/guest/apps/after-install.ts
27737
- import fs13 from "node:fs";
27738
- import path17 from "node:path";
28138
+ import fs15 from "node:fs";
28139
+ import path19 from "node:path";
27739
28140
  var SYSINTERNALS_TOOLS = [
27740
28141
  "Process Explorer",
27741
28142
  "Process Monitor",
@@ -27756,16 +28157,16 @@ var SYSINTERNALS_TOOLS = [
27756
28157
  "ZoomIt"
27757
28158
  ];
27758
28159
  async function afterInstall(entry, exe) {
27759
- const dir = path17.dirname(exe);
28160
+ const dir = path19.dirname(exe);
27760
28161
  if (entry.needs?.includes("mesa")) {
27761
28162
  const mesa = tool("mesa", arch === "arm64" ? "arm64" : "x64");
27762
28163
  for (const dll of ["opengl32.dll", "libgallium_wgl.dll", "dxil.dll"]) {
27763
- if (exists(path17.join(mesa, dll)) && !exists(path17.join(dir, dll)))
27764
- fs13.copyFileSync(path17.join(mesa, dll), path17.join(dir, dll));
28164
+ if (exists(path19.join(mesa, dll)) && !exists(path19.join(dir, dll)))
28165
+ fs15.copyFileSync(path19.join(mesa, dll), path19.join(dir, dll));
27765
28166
  }
27766
28167
  }
27767
- if (entry.id === "godot" && !exists(path17.join(dir, "._sc_"))) fs13.writeFileSync(path17.join(dir, "._sc_"), "");
27768
- if (entry.id === "sublime") mkdirp(path17.join(dir, "Data"));
28168
+ if (entry.id === "godot" && !exists(path19.join(dir, "._sc_"))) fs15.writeFileSync(path19.join(dir, "._sc_"), "");
28169
+ if (entry.id === "sublime") mkdirp(path19.join(dir, "Data"));
27769
28170
  if (entry.id === "sysinternals") {
27770
28171
  for (const name of SYSINTERNALS_TOOLS) {
27771
28172
  await execAsync(
@@ -27777,47 +28178,14 @@ async function afterInstall(entry, exe) {
27777
28178
  }
27778
28179
  }
27779
28180
 
27780
- // src/guest/apps/find-exe.ts
27781
- import fs14 from "node:fs";
27782
- import path18 from "node:path";
27783
- var MAX_DEPTH = 7;
27784
- var MAX_ENTRIES = 5e4;
27785
- var X64_FOLDER = /(^|[\\/])(amd64|x64|x86_64|win64)([\\/]|$)/i;
27786
- function findExe(root, exe) {
27787
- const match = exeMatcher(exe);
27788
- const best = (hits) => hits.find((h) => X64_FOLDER.test(path18.relative(root, h))) ?? hits[0] ?? null;
27789
- let queue = [root];
27790
- let seen = 0;
27791
- for (let depth = 0; depth < MAX_DEPTH && queue.length; depth++) {
27792
- const next = [];
27793
- const hits = [];
27794
- for (const dir of queue) {
27795
- let entries3;
27796
- try {
27797
- entries3 = fs14.readdirSync(dir, { withFileTypes: true });
27798
- } catch {
27799
- continue;
27800
- }
27801
- const hit = entries3.find((e) => e.isFile() && match(e.name));
27802
- if (hit) hits.push(path18.join(dir, hit.name));
27803
- else
27804
- next.push(...entries3.filter((e) => e.isDirectory() && !e.isSymbolicLink()).map((e) => path18.join(dir, e.name)));
27805
- if ((seen += entries3.length) > MAX_ENTRIES) return best(hits);
27806
- }
27807
- if (hits.length) return best(hits);
27808
- queue = next;
27809
- }
27810
- return null;
27811
- }
27812
-
27813
28181
  // src/guest/apps/shortcuts.ts
27814
28182
  import os5 from "node:os";
27815
- import path19 from "node:path";
28183
+ import path20 from "node:path";
27816
28184
  function shortcuts(apps) {
27817
28185
  if (!apps.length) return;
27818
- const desktop = path19.join(os5.homedir(), "Desktop");
27819
- const start = path19.join(
27820
- process.env.APPDATA ?? path19.join(os5.homedir(), "AppData", "Roaming"),
28186
+ const desktop = path20.join(os5.homedir(), "Desktop");
28187
+ const start = path20.join(
28188
+ process.env.APPDATA ?? path20.join(os5.homedir(), "AppData", "Roaming"),
27821
28189
  "Microsoft",
27822
28190
  "Windows",
27823
28191
  "Start Menu",
@@ -27827,9 +28195,9 @@ function shortcuts(apps) {
27827
28195
  const links = apps.flatMap((app) => {
27828
28196
  const name = app.name.replace(/[\\/:*?"<>|]/g, "");
27829
28197
  return [desktop, start].map((folder) => ({
27830
- link: path19.join(folder, name + ".lnk"),
28198
+ link: path20.join(folder, name + ".lnk"),
27831
28199
  target: app.exe,
27832
- workingDirectory: path19.dirname(app.exe)
28200
+ workingDirectory: path20.dirname(app.exe)
27833
28201
  }));
27834
28202
  });
27835
28203
  runScriptWithJson("new-shortcuts.ps1", "ListFile", links);
@@ -27954,19 +28322,19 @@ async function resolve(entry) {
27954
28322
  }
27955
28323
  if ("url" in s) {
27956
28324
  const name = decodeURIComponent(new URL(s.url).pathname.split("/").pop() || entry.id);
27957
- return { url: s.url, fileName: /\.\w{2,5}$/.test(name) ? name : name + ".zip" };
28325
+ return { url: s.url, fileName: /\.\w{2,5}$/.test(name) ? name : name + ".zip", sha256: s.sha256 };
27958
28326
  }
27959
28327
  if ("resolve" in s && RESOLVERS[s.resolve]) return RESOLVERS[s.resolve]();
27960
28328
  throw new Error("no download source for " + entry.name);
27961
28329
  }
27962
28330
 
27963
28331
  // src/guest/apps/unpack.ts
27964
- import fs15 from "node:fs";
27965
- import path20 from "node:path";
28332
+ import fs16 from "node:fs";
28333
+ import path21 from "node:path";
27966
28334
  var sevenZip = () => tool("apps", "7zip", "7z.exe");
27967
28335
  async function unpack(entry, file3, out, d) {
27968
28336
  if (entry.id === "7zip") {
27969
- const sevenZr = path20.join(path20.dirname(file3), "7zr.exe");
28337
+ const sevenZr = path21.join(path21.dirname(file3), "7zr.exe");
27970
28338
  const zr = await githubAsset("ip7z/7zip", /^7zr\.exe$/);
27971
28339
  await downloader.file(zr.url, sevenZr, zr.sha256);
27972
28340
  await execAsync(sevenZr, ["x", "-y", "-o" + out, file3], { stdio: "ignore" });
@@ -27975,17 +28343,17 @@ async function unpack(entry, file3, out, d) {
27975
28343
  switch (entry.unpack ?? "archive") {
27976
28344
  case "file": {
27977
28345
  mkdirp(out);
27978
- fs15.renameSync(file3, path20.join(out, /^\//.test(entry.exe) ? path20.basename(file3) : entry.exe));
28346
+ fs16.renameSync(file3, path21.join(out, /^\//.test(entry.exe) ? path21.basename(file3) : entry.exe));
27979
28347
  return;
27980
28348
  }
27981
28349
  case "nsis": {
27982
28350
  if (!exists(sevenZip()))
27983
28351
  throw new Error("7-Zip is needed to unpack " + entry.name + " and could not be installed");
27984
- const raw = path20.join(path20.dirname(out), "raw");
28352
+ const raw = path21.join(path21.dirname(out), "raw");
27985
28353
  await execAsync(sevenZip(), ["x", "-y", "-o" + raw, file3], { stdio: "ignore" });
27986
28354
  const inner = findExe(raw, "/^app-64\\.7z$/");
27987
28355
  if (inner) await execAsync(sevenZip(), ["x", "-y", "-o" + out, inner], { stdio: "ignore" });
27988
- else fs15.renameSync(raw, out);
28356
+ else fs16.renameSync(raw, out);
27989
28357
  return;
27990
28358
  }
27991
28359
  case "msi": {
@@ -28035,12 +28403,12 @@ var AppInstaller = class {
28035
28403
  async installOne(entry) {
28036
28404
  try {
28037
28405
  const dest = tool("apps", ...entry.id.split("/"));
28038
- if (!exists(path21.join(dest, MARKER)) || this.options.refresh) await this.download(entry, dest);
28406
+ if (!exists(path22.join(dest, MARKER)) || this.options.refresh) await this.download(entry, dest);
28039
28407
  const exe = findExe(dest, entry.exe);
28040
28408
  if (!exe) throw new Error(entry.exe + " not found in the download");
28041
28409
  await afterInstall(entry, exe);
28042
- if (entry.cli || entry.id === "sysinternals") this.env.prepend.push(path21.dirname(exe));
28043
- if (!entry.cli) this.gui.push({ name: entry.name, exe });
28410
+ if (!entry.service && (entry.cli || entry.id === "sysinternals")) this.env.prepend.push(path22.dirname(exe));
28411
+ if (!entry.cli && !entry.service) this.gui.push({ name: entry.name, exe });
28044
28412
  this.installed.push(entry.name);
28045
28413
  } catch (e) {
28046
28414
  warn(
@@ -28052,10 +28420,13 @@ var AppInstaller = class {
28052
28420
  /** Downloads and unpacks one entry into its folder (replaced in one step: never half there). */
28053
28421
  async download(entry, dest) {
28054
28422
  const work = stagingFolder(dest);
28055
- const out = path21.join(work, "out");
28423
+ const out = path22.join(work, "out");
28056
28424
  try {
28057
- if ("npm" in entry.source) {
28058
- const npmCli = path21.join(path21.dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
28425
+ if ("locked" in entry.source) {
28426
+ log("Installing " + entry.name + " " + entry.source.version + " (pinned, every file checked) ...");
28427
+ await installLocked(entry.id, entry.source.locked, mkdirp(out));
28428
+ } else if ("npm" in entry.source) {
28429
+ const npmCli = path22.join(path22.dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
28059
28430
  log("Installing " + entry.name + " (npm) ...");
28060
28431
  await execAsync(process.execPath, [
28061
28432
  npmCli,
@@ -28070,17 +28441,60 @@ var AppInstaller = class {
28070
28441
  } else {
28071
28442
  const d = await resolve(entry);
28072
28443
  log("Downloading " + entry.name + " (" + d.fileName + ", ~" + entry.size + " once) ...");
28073
- const file3 = path21.join(mkdirp(work), d.fileName.replace(/[\\/:*?"<>|]/g, "_"));
28444
+ const file3 = path22.join(mkdirp(work), d.fileName.replace(/[\\/:*?"<>|]/g, "_"));
28074
28445
  await downloader.file(d.url, file3, { sha256: d.sha256 ?? null });
28075
28446
  await unpack(entry, file3, out, d);
28076
28447
  }
28077
- fs16.writeFileSync(path21.join(out, MARKER), JSON.stringify({ id: entry.id, at: (/* @__PURE__ */ new Date()).toISOString() }));
28448
+ fs17.writeFileSync(path22.join(out, MARKER), JSON.stringify({ id: entry.id, at: (/* @__PURE__ */ new Date()).toISOString() }));
28078
28449
  moveIntoPlace(out, dest);
28079
28450
  } finally {
28080
- safeRemove(work, path21.dirname(work));
28451
+ safeRemove(work, path22.dirname(work));
28081
28452
  }
28082
28453
  }
28083
28454
  };
28455
+ var LOCKED_PYTHON = "3.13";
28456
+ async function installLocked(id, kind, out) {
28457
+ const lock = path22.join(launchDir, "sandbox", "addons", id);
28458
+ if (kind === "npm") {
28459
+ for (const f of ["package.json", "package-lock.json"]) fs17.copyFileSync(path22.join(lock, f), path22.join(out, f));
28460
+ const npmCli = path22.join(path22.dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
28461
+ await execAsync(
28462
+ process.execPath,
28463
+ [npmCli, "ci", "--ignore-scripts", "--no-audit", "--no-fund", "--loglevel=error"],
28464
+ { cwd: out }
28465
+ );
28466
+ return;
28467
+ }
28468
+ const uv = findExe(tool("uv"), "uv.exe");
28469
+ if (!uv) throw new Error("uv is not installed (the uv step failed)");
28470
+ const env = { ...process.env, ...uvVars() };
28471
+ const venv = path22.join(out, "venv");
28472
+ await execAsync(
28473
+ uv,
28474
+ ["venv", "--relocatable", "--python", LOCKED_PYTHON, "--python-preference", "only-managed", venv],
28475
+ {
28476
+ env
28477
+ }
28478
+ );
28479
+ await execAsync(
28480
+ uv,
28481
+ [
28482
+ "pip",
28483
+ "install",
28484
+ "--python",
28485
+ path22.join(venv, "Scripts", "python.exe"),
28486
+ "--require-hashes",
28487
+ "--no-deps",
28488
+ "--only-binary",
28489
+ ":all:",
28490
+ "--link-mode",
28491
+ "copy",
28492
+ "-r",
28493
+ path22.join(lock, "requirements.txt")
28494
+ ],
28495
+ { env }
28496
+ );
28497
+ }
28084
28498
  async function setupApps(env, apps, o) {
28085
28499
  const { entries: entries3, skipped } = appsToInstall(apps, o.gitAccess);
28086
28500
  for (const s of skipped) env.summary.push("app skipped: " + s);
@@ -28092,8 +28506,8 @@ async function setupApps(env, apps, o) {
28092
28506
  }
28093
28507
 
28094
28508
  // src/guest/toolchains/detect.ts
28095
- import fs17 from "node:fs";
28096
- import path22 from "node:path";
28509
+ import fs18 from "node:fs";
28510
+ import path23 from "node:path";
28097
28511
 
28098
28512
  // src/guest/runtime/project-context.ts
28099
28513
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -28235,14 +28649,14 @@ function workspaceEntries() {
28235
28649
  if (known) return known;
28236
28650
  const list = (dir) => {
28237
28651
  try {
28238
- return fs17.readdirSync(dir, { withFileTypes: true });
28652
+ return fs18.readdirSync(dir, { withFileTypes: true });
28239
28653
  } catch {
28240
28654
  return [];
28241
28655
  }
28242
28656
  };
28243
28657
  const top = list(workspace2);
28244
28658
  const below = top.filter((d) => d.isDirectory() && !d.name.startsWith(".") && !SKIP_DIRS.has(d.name.toLowerCase())).flatMap(
28245
- (d) => list(path22.join(workspace2, d.name)).filter((f) => f.isFile()).map((f) => d.name + "/" + f.name)
28659
+ (d) => list(path23.join(workspace2, d.name)).filter((f) => f.isFile()).map((f) => d.name + "/" + f.name)
28246
28660
  );
28247
28661
  const found = [...top.filter((d) => d.isFile()).map((d) => d.name), ...below];
28248
28662
  entries.set(workspace2.toLowerCase(), found);
@@ -28253,9 +28667,9 @@ function wanted(setting, toolchain) {
28253
28667
  }
28254
28668
  function versionFromFiles(files, parse5 = (t) => specFromVersionFile(t)) {
28255
28669
  for (const name of files) {
28256
- const file3 = path22.join(projectWorkspace(), name);
28670
+ const file3 = path23.join(projectWorkspace(), name);
28257
28671
  if (!exists(file3)) continue;
28258
- const spec = parse5(fs17.readFileSync(file3, "utf8"), name);
28672
+ const spec = parse5(fs18.readFileSync(file3, "utf8"), name);
28259
28673
  if (spec) return { spec, source: name };
28260
28674
  }
28261
28675
  return null;
@@ -28318,22 +28732,22 @@ var DatabaseServer = class {
28318
28732
  };
28319
28733
 
28320
28734
  // src/guest/databases/dynamodb.ts
28321
- import fs22 from "node:fs";
28322
- import path25 from "node:path";
28735
+ import fs23 from "node:fs";
28736
+ import path26 from "node:path";
28323
28737
  import { spawn as spawn3 } from "node:child_process";
28324
28738
 
28325
28739
  // src/guest/toolchains/languages/java.ts
28326
- import fs20 from "node:fs";
28327
- import path23 from "node:path";
28740
+ import fs21 from "node:fs";
28741
+ import path24 from "node:path";
28328
28742
 
28329
28743
  // src/guest/toolchains/version-cache.ts
28330
- import fs19 from "node:fs";
28744
+ import fs20 from "node:fs";
28331
28745
 
28332
28746
  // src/guest/toolchains/state-file.ts
28333
- import fs18 from "node:fs";
28747
+ import fs19 from "node:fs";
28334
28748
  function readState(file3, schema, empty) {
28335
28749
  try {
28336
- return schema.safeParse(JSON.parse(fs18.readFileSync(file3, "utf8").replace(/^\uFEFF/, ""))).data ?? empty;
28750
+ return schema.safeParse(JSON.parse(fs19.readFileSync(file3, "utf8").replace(/^\uFEFF/, ""))).data ?? empty;
28337
28751
  } catch {
28338
28752
  return empty;
28339
28753
  }
@@ -28368,7 +28782,7 @@ var VersionCache = class {
28368
28782
  return cached2.value;
28369
28783
  try {
28370
28784
  const value = await resolve2();
28371
- fs19.mkdirSync(toolsDir, { recursive: true });
28785
+ fs20.mkdirSync(toolsDir, { recursive: true });
28372
28786
  writeJsonAtomic(this.file(), { ...this.remembered(), [key]: { value, at: Date.now() } });
28373
28787
  return value;
28374
28788
  } catch (e) {
@@ -28421,7 +28835,7 @@ var setupJdk = async (env, config2) => {
28421
28835
  const { major, source } = await javaMajor(config2);
28422
28836
  const release = await resolveCached("java:" + major, async () => (await temurin(major)).release_name);
28423
28837
  const home = tool("java", release.replace(/[^A-Za-z0-9.-]/g, "_"));
28424
- if (!exists(path23.join(home, "bin", "java.exe"))) {
28838
+ if (!exists(path24.join(home, "bin", "java.exe"))) {
28425
28839
  const asset = await temurin(major);
28426
28840
  log("Downloading Java " + release + " (JDK, ~200 MB once) ...");
28427
28841
  await downloader.archive(asset.binary.package.link, home, {
@@ -28431,13 +28845,13 @@ var setupJdk = async (env, config2) => {
28431
28845
  });
28432
28846
  }
28433
28847
  env.vars.JAVA_HOME = home;
28434
- env.prepend.push(path23.join(home, "bin"));
28848
+ env.prepend.push(path24.join(home, "bin"));
28435
28849
  env.summary.push("java: " + release + " (Temurin, from " + source + ")");
28436
28850
  };
28437
28851
  async function ensureJdk() {
28438
28852
  const root = tool("java");
28439
- const installed = exists(root) ? fs20.readdirSync(root).filter((d) => exists(path23.join(root, d, "bin", "java.exe"))).sort((a, b) => b.localeCompare(a, void 0, { numeric: true })) : [];
28440
- if (installed.length) return path23.join(root, installed[0]);
28853
+ const installed = exists(root) ? fs21.readdirSync(root).filter((d) => exists(path24.join(root, d, "bin", "java.exe"))).sort((a, b) => b.localeCompare(a, void 0, { numeric: true })) : [];
28854
+ if (installed.length) return path24.join(root, installed[0]);
28441
28855
  const scratch = emptyEnv();
28442
28856
  await setupJdk(scratch, { enabled: true, version: "lts", maven: false, gradle: false });
28443
28857
  return scratch.vars.JAVA_HOME;
@@ -28450,7 +28864,7 @@ var setupMaven = async (env) => {
28450
28864
  return pickPackageVersion(versions, "3") ?? Promise.reject(new Error("No Maven 3 release found"));
28451
28865
  });
28452
28866
  const home = tool("maven", version2);
28453
- if (!exists(path23.join(home, "bin", "mvn.cmd"))) {
28867
+ if (!exists(path24.join(home, "bin", "mvn.cmd"))) {
28454
28868
  const url2 = MAVEN + version2 + "/apache-maven-" + version2 + "-bin.zip";
28455
28869
  log("Downloading Maven " + version2 + " (~10 MB once) ...");
28456
28870
  await downloader.archive(url2, home, {
@@ -28460,7 +28874,7 @@ var setupMaven = async (env) => {
28460
28874
  }
28461
28875
  const repo = tool("maven-repository");
28462
28876
  Object.assign(env.vars, { MAVEN_HOME: home, M2_HOME: home, MAVEN_OPTS: "-Dmaven.repo.local=" + repo });
28463
- env.prepend.push(path23.join(home, "bin"));
28877
+ env.prepend.push(path24.join(home, "bin"));
28464
28878
  env.summary.push("maven: " + version2);
28465
28879
  };
28466
28880
  var setupGradle = async (env) => {
@@ -28472,7 +28886,7 @@ var setupGradle = async (env) => {
28472
28886
  async () => current?.version ?? Promise.reject(new Error("Gradle version lookup failed"))
28473
28887
  );
28474
28888
  const home = tool("gradle", version2);
28475
- if (!exists(path23.join(home, "bin", "gradle.bat"))) {
28889
+ if (!exists(path24.join(home, "bin", "gradle.bat"))) {
28476
28890
  if (!current || current.version !== version2)
28477
28891
  throw new Error("Gradle " + version2 + " is not installed and cannot be downloaded right now");
28478
28892
  log("Downloading Gradle " + version2 + " (~130 MB once) ...");
@@ -28483,13 +28897,13 @@ var setupGradle = async (env) => {
28483
28897
  });
28484
28898
  }
28485
28899
  const userHome = tool("gradle-home");
28486
- fs20.mkdirSync(userHome, { recursive: true });
28900
+ fs21.mkdirSync(userHome, { recursive: true });
28487
28901
  Object.assign(env.vars, {
28488
28902
  GRADLE_HOME: home,
28489
28903
  GRADLE_USER_HOME: userHome,
28490
28904
  GRADLE_OPTS: "-Dorg.gradle.vfs.watch=false"
28491
28905
  });
28492
- env.prepend.push(path23.join(home, "bin"));
28906
+ env.prepend.push(path24.join(home, "bin"));
28493
28907
  env.summary.push("gradle: " + version2);
28494
28908
  };
28495
28909
  async function setupJava(env, config2) {
@@ -28513,29 +28927,29 @@ async function setupJava(env, config2) {
28513
28927
  }
28514
28928
 
28515
28929
  // src/guest/databases/server.ts
28516
- import fs21 from "node:fs";
28517
- import path24 from "node:path";
28930
+ import fs22 from "node:fs";
28931
+ import path25 from "node:path";
28518
28932
  import { spawn as spawn2 } from "node:child_process";
28519
28933
  function dataFor(name) {
28520
- return path24.join(dataDir, "databases", name);
28934
+ return path25.join(dataDir, "databases", name);
28521
28935
  }
28522
28936
  async function startServer(name, exe, args, port) {
28523
28937
  if (await portOpen(port)) throw new Error("port " + port + " is already in use (change databases." + name + ".port)");
28524
- const logFile2 = path24.join(logsDir, name + ".log");
28525
- const out = fs21.openSync(logFile2, "a");
28938
+ const logFile2 = path25.join(logsDir, name + ".log");
28939
+ const out = fs22.openSync(logFile2, "a");
28526
28940
  spawn2(exe, args, { detached: true, stdio: ["ignore", out, out], windowsHide: true }).unref();
28527
- fs21.closeSync(out);
28941
+ fs22.closeSync(out);
28528
28942
  if (!await waitForPort(port, 60))
28529
28943
  throw new Error(name + " did not start listening on port " + port + "; see " + logFile2);
28530
28944
  }
28531
28945
  async function clearStaleLocks(dir, files, port) {
28532
28946
  if (await portOpen(port)) return;
28533
- for (const f of files) fs21.rmSync(path24.join(dir, f), { force: true });
28947
+ for (const f of files) fs22.rmSync(path25.join(dir, f), { force: true });
28534
28948
  }
28535
28949
  async function createOnce(data, marker, create) {
28536
- if (exists(path24.join(data, marker))) return;
28950
+ if (exists(path25.join(data, marker))) return;
28537
28951
  const existed = exists(data);
28538
- mkdirp(path24.dirname(data));
28952
+ mkdirp(path25.dirname(data));
28539
28953
  try {
28540
28954
  await create();
28541
28955
  } catch (e) {
@@ -28559,9 +28973,9 @@ var DynamoServer = class extends DatabaseServer {
28559
28973
  * local endpoint up from AWS_ENDPOINT_URL_DYNAMODB, with dummy credentials (real ones never reach the sandbox).
28560
28974
  */
28561
28975
  async start(env, plan2) {
28562
- const java = path25.join(await ensureJdk(), "bin", "java.exe");
28976
+ const java = path26.join(await ensureJdk(), "bin", "java.exe");
28563
28977
  const home = tool("dynamodb-local");
28564
- if (!exists(path25.join(home, "DynamoDBLocal.jar"))) {
28978
+ if (!exists(path26.join(home, "DynamoDBLocal.jar"))) {
28565
28979
  log("Downloading DynamoDB Local (~60 MB once) ...");
28566
28980
  const sha256 = (await fetchText(DYNAMO_ZIP + ".sha256")).trim().split(/\s+/)[0];
28567
28981
  await downloader.archive(DYNAMO_ZIP, home, {
@@ -28575,9 +28989,9 @@ var DynamoServer = class extends DatabaseServer {
28575
28989
  "dynamodb",
28576
28990
  java,
28577
28991
  [
28578
- "-Djava.library.path=" + path25.join(home, "DynamoDBLocal_lib"),
28992
+ "-Djava.library.path=" + path26.join(home, "DynamoDBLocal_lib"),
28579
28993
  "-jar",
28580
- path25.join(home, "DynamoDBLocal.jar"),
28994
+ path26.join(home, "DynamoDBLocal.jar"),
28581
28995
  "-sharedDb",
28582
28996
  "-dbPath",
28583
28997
  data,
@@ -28601,10 +29015,10 @@ var DynamoServer = class extends DatabaseServer {
28601
29015
  );
28602
29016
  if (!plan2.adminPort) return;
28603
29017
  const prefix = tool("dynamodb-admin");
28604
- const script = path25.join(prefix, "node_modules", "dynamodb-admin", "dist", "dynamodb-admin.js");
29018
+ const script = path26.join(prefix, "node_modules", "dynamodb-admin", "dist", "dynamodb-admin.js");
28605
29019
  if (!exists(script)) {
28606
29020
  log("Installing dynamodb-admin, the DynamoDB web UI (~20 MB once) ...");
28607
- const npmCli = path25.join(path25.dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
29021
+ const npmCli = path26.join(path26.dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
28608
29022
  await execAsync(
28609
29023
  process.execPath,
28610
29024
  [npmCli, "install", "--global", "--prefix", prefix, "dynamodb-admin", "--no-fund", "--no-audit"],
@@ -28617,15 +29031,15 @@ var DynamoServer = class extends DatabaseServer {
28617
29031
  );
28618
29032
  return;
28619
29033
  }
28620
- const out = fs22.openSync(path25.join(logsDir, "dynamodb-admin.log"), "a");
29034
+ const out = fs23.openSync(path26.join(logsDir, "dynamodb-admin.log"), "a");
28621
29035
  spawn3(
28622
29036
  process.execPath,
28623
29037
  [script, "--port", String(plan2.adminPort), "--dynamo-endpoint", endpoint, "--skip-default-credentials"],
28624
29038
  { detached: true, stdio: ["ignore", out, out], windowsHide: true, env: { ...process.env, ...aws } }
28625
29039
  ).unref();
28626
- fs22.closeSync(out);
29040
+ fs23.closeSync(out);
28627
29041
  if (!await waitForPort(plan2.adminPort, 30))
28628
- throw new Error("dynamodb-admin did not start; see " + path25.join(logsDir, "dynamodb-admin.log"));
29042
+ throw new Error("dynamodb-admin did not start; see " + path26.join(logsDir, "dynamodb-admin.log"));
28629
29043
  env.summary.push(
28630
29044
  "dynamodb-admin: http://localhost:" + plan2.adminPort + " (browse and edit tables in the sandbox's browser)"
28631
29045
  );
@@ -28633,32 +29047,7 @@ var DynamoServer = class extends DatabaseServer {
28633
29047
  };
28634
29048
 
28635
29049
  // src/guest/databases/mongodb.ts
28636
- import path26 from "node:path";
28637
-
28638
- // src/guest/toolchains/install.ts
28639
- import fs23 from "node:fs";
28640
- async function downloadFile(url2, file3, sha256 = null) {
28641
- const part = file3 + ".part";
28642
- try {
28643
- await downloader.file(url2, part, sha256);
28644
- fs23.rmSync(file3, { force: true });
28645
- fs23.renameSync(part, file3);
28646
- } finally {
28647
- fs23.rmSync(part, { force: true });
28648
- }
28649
- }
28650
- async function installFromGithub(repo, pattern, o) {
28651
- const asset = await githubAsset(repo, pattern, o.searchOlder);
28652
- log("Downloading " + (typeof o.what === "string" ? o.what : o.what(asset)) + " ...");
28653
- await downloader.archive(asset.url, o.dest, {
28654
- sha256: asset.sha256,
28655
- fileName: asset.name,
28656
- ...o.locate ? { locate: o.locate } : {},
28657
- ...o.up ? { up: o.up } : {}
28658
- });
28659
- }
28660
-
28661
- // src/guest/databases/mongodb.ts
29050
+ import path27 from "node:path";
28662
29051
  var MongoServer = class extends DatabaseServer {
28663
29052
  id = "mongodb";
28664
29053
  async start(env, plan2) {
@@ -28673,13 +29062,13 @@ var MongoServer = class extends DatabaseServer {
28673
29062
  };
28674
29063
  const version2 = await resolveCached("mongodb:" + plan2.version, async () => (await find()).version);
28675
29064
  const home = tool("mongodb", version2);
28676
- if (!exists(path26.join(home, "bin", "mongod.exe"))) {
29065
+ if (!exists(path27.join(home, "bin", "mongod.exe"))) {
28677
29066
  const { archive } = await find();
28678
29067
  log("Downloading MongoDB " + version2 + " (~400 MB once) ...");
28679
29068
  await downloader.archive(archive.url, home, { sha256: archive.sha256, locate: "mongod.exe", up: 1 });
28680
29069
  }
28681
29070
  const shell = tool("mongosh");
28682
- if (!exists(path26.join(shell, "mongosh.exe"))) {
29071
+ if (!exists(path27.join(shell, "mongosh.exe"))) {
28683
29072
  await installFromGithub("mongodb-js/mongosh", /^mongosh-[\d.]+-win32-x64\.zip$/, {
28684
29073
  dest: shell,
28685
29074
  what: "mongosh, the MongoDB shell (~50 MB once)",
@@ -28690,17 +29079,17 @@ var MongoServer = class extends DatabaseServer {
28690
29079
  await clearStaleLocks(data, ["mongod.lock"], plan2.port);
28691
29080
  await startServer(
28692
29081
  "mongodb",
28693
- path26.join(home, "bin", "mongod.exe"),
29082
+ path27.join(home, "bin", "mongod.exe"),
28694
29083
  ["--dbpath", data, "--bind_ip", "127.0.0.1", "--port", String(plan2.port)],
28695
29084
  plan2.port
28696
29085
  );
28697
- env.prepend.push(path26.join(home, "bin"), shell);
29086
+ env.prepend.push(path27.join(home, "bin"), shell);
28698
29087
  env.summary.push("mongodb " + version2 + ": mongodb://127.0.0.1:" + plan2.port + " (mongosh on PATH)");
28699
29088
  }
28700
29089
  };
28701
29090
 
28702
29091
  // src/guest/databases/mysql.ts
28703
- import path27 from "node:path";
29092
+ import path28 from "node:path";
28704
29093
  var MARIA = "https://downloads.mariadb.org/rest-api/mariadb/";
28705
29094
  var MysqlServer = class extends DatabaseServer {
28706
29095
  id = "mysql";
@@ -28718,8 +29107,8 @@ var MysqlServer = class extends DatabaseServer {
28718
29107
  };
28719
29108
  const version2 = await resolveCached("mariadb-version:" + series, async () => (await latest()).version);
28720
29109
  const home = tool("mariadb", version2);
28721
- const bin = path27.join(home, "bin");
28722
- if (!exists(path27.join(bin, "mariadbd.exe"))) {
29110
+ const bin = path28.join(home, "bin");
29111
+ if (!exists(path28.join(bin, "mariadbd.exe"))) {
28723
29112
  const { zip } = await latest();
28724
29113
  log("Downloading MariaDB " + version2 + " (~90 MB once) ...");
28725
29114
  await downloader.archive(zip.file_download_url.replace(/^http:/, "https:"), home, {
@@ -28732,13 +29121,13 @@ var MysqlServer = class extends DatabaseServer {
28732
29121
  const data = dataFor("mariadb");
28733
29122
  await createOnce(data, "mysql", async () => {
28734
29123
  log("Creating the MariaDB database (first start) ...");
28735
- await execAsync(path27.join(bin, "mariadb-install-db.exe"), ["--datadir=" + data, "--port=" + plan2.port], {
29124
+ await execAsync(path28.join(bin, "mariadb-install-db.exe"), ["--datadir=" + data, "--port=" + plan2.port], {
28736
29125
  stdio: "ignore"
28737
29126
  });
28738
29127
  });
28739
29128
  await startServer(
28740
29129
  "mysql",
28741
- path27.join(bin, "mariadbd.exe"),
29130
+ path28.join(bin, "mariadbd.exe"),
28742
29131
  ["--datadir=" + data, "--port=" + plan2.port, "--bind-address=127.0.0.1", "--console"],
28743
29132
  plan2.port
28744
29133
  );
@@ -28752,7 +29141,7 @@ var MysqlServer = class extends DatabaseServer {
28752
29141
 
28753
29142
  // src/guest/databases/postgres.ts
28754
29143
  import fs24 from "node:fs";
28755
- import path28 from "node:path";
29144
+ import path29 from "node:path";
28756
29145
  function postgresData(version2) {
28757
29146
  return dataFor("postgres-" + version2.split(".")[0]);
28758
29147
  }
@@ -28767,8 +29156,8 @@ var PostgresServer = class extends DatabaseServer {
28767
29156
  return hit ? hit.major + "." + hit.latestMinor : Promise.reject(new Error("No supported PostgreSQL " + plan2.version));
28768
29157
  });
28769
29158
  const home = tool("postgres", exact);
28770
- const bin = path28.join(home, "bin");
28771
- if (!exists(path28.join(bin, "pg_ctl.exe"))) {
29159
+ const bin = path29.join(home, "bin");
29160
+ if (!exists(path29.join(bin, "pg_ctl.exe"))) {
28772
29161
  log("Downloading PostgreSQL " + exact + " (~330 MB once) ...");
28773
29162
  await downloader.archive(
28774
29163
  "https://get.enterprisedb.com/postgresql/postgresql-" + exact + "-1-windows-x64-binaries.zip",
@@ -28785,7 +29174,7 @@ var PostgresServer = class extends DatabaseServer {
28785
29174
  await createOnce(data, "PG_VERSION", async () => {
28786
29175
  log("Creating the PostgreSQL database (first start) ...");
28787
29176
  await execAsync(
28788
- path28.join(bin, "initdb.exe"),
29177
+ path29.join(bin, "initdb.exe"),
28789
29178
  ["-D", data, "-U", "postgres", "-A", "trust", "-E", "UTF8", "--locale=C"],
28790
29179
  { stdio: "ignore" }
28791
29180
  );
@@ -28794,7 +29183,7 @@ var PostgresServer = class extends DatabaseServer {
28794
29183
  throw new Error("port " + plan2.port + " is already in use (change databases.postgres.port)");
28795
29184
  await clearStaleLocks(data, ["postmaster.pid"], plan2.port);
28796
29185
  await execAsync(
28797
- path28.join(bin, "pg_ctl.exe"),
29186
+ path29.join(bin, "pg_ctl.exe"),
28798
29187
  [
28799
29188
  "start",
28800
29189
  "-D",
@@ -28803,7 +29192,7 @@ var PostgresServer = class extends DatabaseServer {
28803
29192
  "-t",
28804
29193
  "60",
28805
29194
  "-l",
28806
- path28.join(logsDir, "postgres.log"),
29195
+ path29.join(logsDir, "postgres.log"),
28807
29196
  "-o",
28808
29197
  "-p " + plan2.port + " -c listen_addresses=localhost"
28809
29198
  ],
@@ -28820,8 +29209,8 @@ var PostgresServer = class extends DatabaseServer {
28820
29209
  if (exists(tool("postgres"))) {
28821
29210
  for (const version2 of fs24.readdirSync(tool("postgres"))) {
28822
29211
  const data = postgresData(version2);
28823
- const pgCtl = path28.join(tool("postgres", version2), "bin", "pg_ctl.exe");
28824
- if (exists(path28.join(data, "postmaster.pid")) && exists(pgCtl))
29212
+ const pgCtl = path29.join(tool("postgres", version2), "bin", "pg_ctl.exe");
29213
+ if (exists(path29.join(data, "postmaster.pid")) && exists(pgCtl))
28825
29214
  await execAsync(pgCtl, ["stop", "-D", data, "-m", "fast", "-w"], { stdio: "ignore" });
28826
29215
  }
28827
29216
  }
@@ -28830,12 +29219,12 @@ var PostgresServer = class extends DatabaseServer {
28830
29219
  };
28831
29220
 
28832
29221
  // src/guest/databases/redis.ts
28833
- import path29 from "node:path";
29222
+ import path30 from "node:path";
28834
29223
  var RedisServer = class extends DatabaseServer {
28835
29224
  id = "redis";
28836
29225
  async start(env, plan2) {
28837
29226
  const home = tool("redis");
28838
- if (!exists(path29.join(home, "redis-server.exe"))) {
29227
+ if (!exists(path30.join(home, "redis-server.exe"))) {
28839
29228
  await installFromGithub("redis-windows/redis-windows", /^Redis-[\d.]+-Windows-x64-msys2\.zip$/, {
28840
29229
  dest: home,
28841
29230
  what: (a) => "Redis (" + a.name + ", ~10 MB once)",
@@ -28845,7 +29234,7 @@ var RedisServer = class extends DatabaseServer {
28845
29234
  const data = mkdirp(dataFor("redis"));
28846
29235
  await startServer(
28847
29236
  "redis",
28848
- path29.join(home, "redis-server.exe"),
29237
+ path30.join(home, "redis-server.exe"),
28849
29238
  ["--port", String(plan2.port), "--bind", "127.0.0.1", "--dir", data.replace(/\\/g, "/"), "--appendonly", "yes"],
28850
29239
  plan2.port
28851
29240
  );
@@ -28884,15 +29273,15 @@ async function setupDatabase(env, plan2) {
28884
29273
 
28885
29274
  // src/guest/toolchains/tools/git.ts
28886
29275
  import fs26 from "node:fs";
28887
- import path31 from "node:path";
29276
+ import path32 from "node:path";
28888
29277
 
28889
29278
  // src/guest/runtime/csc.ts
28890
29279
  import fs25 from "node:fs";
28891
29280
  import os6 from "node:os";
28892
- import path30 from "node:path";
29281
+ import path31 from "node:path";
28893
29282
  import crypto3 from "node:crypto";
28894
29283
  import { spawnSync as spawnSync3 } from "node:child_process";
28895
- var CSC = path30.join(process.env.WINDIR ?? "C:\\Windows", "Microsoft.NET", "Framework64", "v4.0.30319", "csc.exe");
29284
+ var CSC = path31.join(process.env.WINDIR ?? "C:\\Windows", "Microsoft.NET", "Framework64", "v4.0.30319", "csc.exe");
28896
29285
  var SOURCES = {
28897
29286
  ptyhost: "PtyHost.cs",
28898
29287
  gitshim: "GitShim.cs",
@@ -28904,17 +29293,17 @@ var SOURCES = {
28904
29293
  memgov: "MemGov.cs"
28905
29294
  };
28906
29295
  function sourceOf(name) {
28907
- return path30.join(launchDir, "sandbox", "csharp", SOURCES[name]);
29296
+ return path31.join(launchDir, "sandbox", "csharp", SOURCES[name]);
28908
29297
  }
28909
29298
  function sourceHash(name) {
28910
29299
  return crypto3.createHash("sha256").update(fs25.readFileSync(sourceOf(name))).digest("hex").slice(0, 12);
28911
29300
  }
28912
29301
  function ensureCompiled(name) {
28913
- const exe = path30.join(os6.tmpdir(), "agent-sandbox", name + "-" + sourceHash(name) + ".exe");
29302
+ const exe = path31.join(os6.tmpdir(), "agent-sandbox", name + "-" + sourceHash(name) + ".exe");
28914
29303
  if (exists(exe)) return exe;
28915
29304
  if (!exists(CSC))
28916
29305
  throw new Error("The .NET Framework C# compiler was not found at " + CSC + " (it ships with Windows 10/11).");
28917
- mkdirp(path30.dirname(exe));
29306
+ mkdirp(path31.dirname(exe));
28918
29307
  const result = spawnSync3(CSC, ["/nologo", "/optimize", "/target:exe", "/out:" + exe, sourceOf(name)], {
28919
29308
  encoding: "utf8",
28920
29309
  windowsHide: true
@@ -28953,18 +29342,18 @@ var installPortableGit = async (root) => {
28953
29342
  };
28954
29343
  var installGuard = (env, access, realGit) => {
28955
29344
  const dir = mkdirp(tool("gitguard"));
28956
- const guard = path31.join(launchDir, "runtime", "gitguard.js");
28957
- if (duringSession() && (exists(path31.join(dir, "git.exe")) || exists(path31.join(dir, "git.cmd")))) {
29345
+ const guard = path32.join(launchDir, "runtime", "gitguard.js");
29346
+ if (duringSession() && (exists(path32.join(dir, "git.exe")) || exists(path32.join(dir, "git.cmd")))) {
28958
29347
  } else
28959
29348
  try {
28960
- fs26.copyFileSync(ensureCompiled("gitshim"), path31.join(dir, "git.exe"));
28961
- fs26.rmSync(path31.join(dir, "git.cmd"), { force: true });
29349
+ fs26.copyFileSync(ensureCompiled("gitshim"), path32.join(dir, "git.exe"));
29350
+ fs26.rmSync(path32.join(dir, "git.cmd"), { force: true });
28962
29351
  } catch (e) {
28963
29352
  warn("git guard: falling back to git.cmd (" + errMsg(e) + ")");
28964
- fs26.writeFileSync(path31.join(dir, "git.cmd"), '@"' + process.execPath + '" "' + guard + '" %*\r\n');
29353
+ fs26.writeFileSync(path32.join(dir, "git.cmd"), '@"' + process.execPath + '" "' + guard + '" %*\r\n');
28965
29354
  }
28966
29355
  fs26.writeFileSync(
28967
- path31.join(dir, "bashenv.sh"),
29356
+ path32.join(dir, "bashenv.sh"),
28968
29357
  'git() { "' + posix(process.execPath) + '" "' + posix(guard) + '" "$@"; }\nexport -f git\n'
28969
29358
  );
28970
29359
  env.prepend.unshift(dir);
@@ -28973,13 +29362,13 @@ var installGuard = (env, access, realGit) => {
28973
29362
  AGENT_REAL_GIT: access === "readonly" ? realGit : "",
28974
29363
  AGENT_GUARD_NODE: process.execPath,
28975
29364
  AGENT_GUARD_SCRIPT: guard,
28976
- BASH_ENV: posix(path31.join(dir, "bashenv.sh"))
29365
+ BASH_ENV: posix(path32.join(dir, "bashenv.sh"))
28977
29366
  });
28978
29367
  if (access === "readonly") {
28979
- const hooks = mkdirp(path31.join(dir, "hooks"));
29368
+ const hooks = mkdirp(path32.join(dir, "hooks"));
28980
29369
  for (const hook of BLOCKED_HOOKS)
28981
29370
  fs26.writeFileSync(
28982
- path31.join(hooks, hook),
29371
+ path32.join(hooks, hook),
28983
29372
  '#!/bin/sh\necho "git is read-only in this sandbox: ' + hook + ' refused." >&2\nexit 1\n'
28984
29373
  );
28985
29374
  Object.assign(env.vars, {
@@ -28988,12 +29377,12 @@ var installGuard = (env, access, realGit) => {
28988
29377
  GIT_CONFIG_VALUE_0: posix(hooks)
28989
29378
  });
28990
29379
  } else {
28991
- env.vars.GIT_DIR = path31.join(dataDir, "git-disabled");
29380
+ env.vars.GIT_DIR = path32.join(dataDir, "git-disabled");
28992
29381
  }
28993
29382
  };
28994
29383
  var pointAtOwnCertificates = (root) => {
28995
- const config2 = path31.join(root, "etc", "gitconfig");
28996
- const bundle = path31.join(root, "mingw64", "etc", "ssl", "certs", "ca-bundle.crt");
29384
+ const config2 = path32.join(root, "etc", "gitconfig");
29385
+ const bundle = path32.join(root, "mingw64", "etc", "ssl", "certs", "ca-bundle.crt");
28997
29386
  if (!exists(config2) || !exists(bundle)) return;
28998
29387
  const text = fs26.readFileSync(config2, "utf8");
28999
29388
  const fixed = text.replace(/^(\s*sslCAInfo\s*=\s*).*$/gim, (_m, key) => key + posix(bundle));
@@ -29001,9 +29390,9 @@ var pointAtOwnCertificates = (root) => {
29001
29390
  };
29002
29391
  async function setupGit(env, hostCopy, refresh, access) {
29003
29392
  const root = tool("git");
29004
- const bash = path31.join(root, "bin", "bash.exe");
29005
- const gitExe = path31.join(root, "cmd", "git.exe");
29006
- const hostGit = path31.join(hostToolsDir, "git");
29393
+ const bash = path32.join(root, "bin", "bash.exe");
29394
+ const gitExe = path32.join(root, "cmd", "git.exe");
29395
+ const hostGit = path32.join(hostToolsDir, "git");
29007
29396
  if (exists(root) && !exists(bash)) {
29008
29397
  log("Replacing the old MinGit (no bash) with full Git for Windows.");
29009
29398
  safeRemove(root, toolsDir);
@@ -29017,11 +29406,11 @@ async function setupGit(env, hostCopy, refresh, access) {
29017
29406
  }
29018
29407
  const work = stagingFolder(root);
29019
29408
  try {
29020
- if (hostCopy && exists(path31.join(hostGit, "bin", "bash.exe"))) {
29409
+ if (hostCopy && exists(path32.join(hostGit, "bin", "bash.exe"))) {
29021
29410
  log("Copying Git for Windows from this PC ...");
29022
29411
  robocopy(hostGit, work);
29023
29412
  } else await installPortableGit(work);
29024
- if (!exists(path31.join(work, "bin", "bash.exe"))) throw new Error("bash.exe missing after installing Git");
29413
+ if (!exists(path32.join(work, "bin", "bash.exe"))) throw new Error("bash.exe missing after installing Git");
29025
29414
  moveIntoPlace(work, root);
29026
29415
  } finally {
29027
29416
  safeRemove(work, toolsDir);
@@ -29030,10 +29419,10 @@ async function setupGit(env, hostCopy, refresh, access) {
29030
29419
  if (!exists(bash)) throw new Error("bash.exe missing after installing Git");
29031
29420
  pointAtOwnCertificates(root);
29032
29421
  env.vars.CLAUDE_CODE_GIT_BASH_PATH = bash;
29033
- env.append.push(path31.join(root, "usr", "bin"));
29422
+ env.append.push(path32.join(root, "usr", "bin"));
29034
29423
  const version2 = execOutput(gitExe, ["--version"]).replace(/^git version /, "");
29035
29424
  if (access === "full") {
29036
- env.prepend.push(path31.join(root, "cmd"));
29425
+ env.prepend.push(path32.join(root, "cmd"));
29037
29426
  env.summary.push("git: " + version2);
29038
29427
  return;
29039
29428
  }
@@ -29045,7 +29434,7 @@ async function setupGit(env, hostCopy, refresh, access) {
29045
29434
 
29046
29435
  // src/guest/toolchains/languages/go.ts
29047
29436
  import fs27 from "node:fs";
29048
- import path32 from "node:path";
29437
+ import path33 from "node:path";
29049
29438
 
29050
29439
  // src/guest/toolchains/package-ledger.ts
29051
29440
  var LedgerSchema = external_exports.record(external_exports.string(), external_exports.string());
@@ -29099,7 +29488,7 @@ async function setupGo(env, config2) {
29099
29488
  }
29100
29489
  const version2 = await resolveCached("go:" + config2.version, async () => (await findRelease(config2.version)).version);
29101
29490
  const root = tool("go", version2);
29102
- if (!exists(path32.join(root, "bin", "go.exe"))) {
29491
+ if (!exists(path33.join(root, "bin", "go.exe"))) {
29103
29492
  const goArch = arch === "arm64" ? "arm64" : "amd64";
29104
29493
  const file3 = (await findRelease(version2)).files.find(
29105
29494
  (f) => f.os === "windows" && f.arch === goArch && f.kind === "archive"
@@ -29116,16 +29505,16 @@ async function setupGo(env, config2) {
29116
29505
  const vars = {
29117
29506
  GOROOT: root,
29118
29507
  GOPATH,
29119
- GOBIN: path32.join(GOPATH, "bin"),
29120
- GOMODCACHE: path32.join(GOPATH, "pkg", "mod"),
29508
+ GOBIN: path33.join(GOPATH, "bin"),
29509
+ GOMODCACHE: path33.join(GOPATH, "pkg", "mod"),
29121
29510
  GOCACHE: tool("go-cache"),
29122
29511
  GOTOOLCHAIN: "auto",
29123
29512
  GOTELEMETRY: "off"
29124
29513
  };
29125
29514
  fs27.mkdirSync(vars.GOBIN, { recursive: true });
29126
29515
  Object.assign(env.vars, vars);
29127
- env.prepend.push(path32.join(root, "bin"), vars.GOBIN);
29128
- const go = path32.join(root, "bin", "go.exe");
29516
+ env.prepend.push(path33.join(root, "bin"), vars.GOBIN);
29517
+ const go = path33.join(root, "bin", "go.exe");
29129
29518
  env.summary.push("go: " + versionOf(go, ["version"]));
29130
29519
  await installOnce(
29131
29520
  env,
@@ -29183,77 +29572,6 @@ function decideCli(settings, ctx) {
29183
29572
  return Object.fromEntries(CLI_NAMES.map((name) => [name, decide(name)]));
29184
29573
  }
29185
29574
 
29186
- // src/guest/toolchains/runtimes.ts
29187
- import path33 from "node:path";
29188
- async function setupSingleExe(env, name, repo, pattern, exeName, dir, options = {}) {
29189
- if (dryRun) {
29190
- log("(dry run) would provision " + name);
29191
- return;
29192
- }
29193
- const bin = options.subdir ? path33.join(dir, options.subdir) : dir;
29194
- const exe = path33.join(bin, exeName);
29195
- if (!exists(exe) || options.refresh) {
29196
- const size = options.size ? ", ~" + options.size : "";
29197
- await installFromGithub(repo, pattern, {
29198
- dest: dir,
29199
- what: (a) => name + " (" + a.name + size + " once)",
29200
- ...options.subdir ? {} : { locate: exeName }
29201
- });
29202
- }
29203
- if (!exists(exe)) throw new Error(exeName + " missing after installing " + name);
29204
- env.prepend.push(bin);
29205
- env.summary.push(name + ": " + versionOf(exe, options.versionArgs) + (options.note ?? ""));
29206
- }
29207
- async function setupBun(env) {
29208
- await setupSingleExe(
29209
- env,
29210
- "bun",
29211
- "oven-sh/bun",
29212
- arch === "arm64" ? /^bun-windows-aarch64\.zip$/ : /^bun-windows-x64\.zip$/,
29213
- "bun.exe",
29214
- tool("bun", "bin"),
29215
- { size: "35 MB" }
29216
- );
29217
- Object.assign(env.vars, {
29218
- BUN_INSTALL: tool("bun"),
29219
- BUN_INSTALL_CACHE_DIR: path33.join(localDir, "bun-cache"),
29220
- BUN_TMPDIR: path33.join(localDir, "bun-tmp")
29221
- });
29222
- }
29223
- async function setupDeno(env) {
29224
- await setupSingleExe(
29225
- env,
29226
- "deno",
29227
- "denoland/deno",
29228
- arch === "arm64" ? /^deno-aarch64-pc-windows-msvc[.]zip$/ : /^deno-x86_64-pc-windows-msvc[.]zip$/,
29229
- "deno.exe",
29230
- tool("deno", "bin"),
29231
- { size: "40 MB" }
29232
- );
29233
- Object.assign(env.vars, { DENO_DIR: tool("deno-cache"), DENO_INSTALL_ROOT: tool("deno"), DENO_NO_UPDATE_CHECK: "1" });
29234
- }
29235
- function uvVars() {
29236
- return {
29237
- UV_CACHE_DIR: tool("uv-cache"),
29238
- UV_PYTHON_INSTALL_DIR: tool("uv-python"),
29239
- UV_TOOL_DIR: tool("uv-tools"),
29240
- UV_TOOL_BIN_DIR: tool("uv-bin")
29241
- };
29242
- }
29243
- async function setupUv(env) {
29244
- await setupSingleExe(
29245
- env,
29246
- "uv",
29247
- "astral-sh/uv",
29248
- arch === "arm64" ? /^uv-aarch64-pc-windows-msvc\.zip$/ : /^uv-x86_64-pc-windows-msvc\.zip$/,
29249
- "uv.exe",
29250
- tool("uv"),
29251
- { size: "20 MB" }
29252
- );
29253
- Object.assign(env.vars, uvVars());
29254
- env.prepend.push(mkdirp(tool("uv-bin")));
29255
- }
29256
-
29257
29575
  // src/guest/toolchains/tools/cli.ts
29258
29576
  function ghConfigDir() {
29259
29577
  return path34.join(stateDir, "gh");
@@ -31286,12 +31604,12 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
31286
31604
  }
31287
31605
  return result;
31288
31606
  }
31289
- function findNodeAtLocation(root, path75) {
31607
+ function findNodeAtLocation(root, path77) {
31290
31608
  if (!root) {
31291
31609
  return void 0;
31292
31610
  }
31293
31611
  let node2 = root;
31294
- for (let segment of path75) {
31612
+ for (let segment of path77) {
31295
31613
  if (typeof segment === "string") {
31296
31614
  if (node2.type !== "object" || !Array.isArray(node2.children)) {
31297
31615
  return void 0;
@@ -31645,14 +31963,14 @@ function getNodeType(value) {
31645
31963
 
31646
31964
  // node_modules/jsonc-parser/lib/esm/impl/edit.js
31647
31965
  function setProperty(text, originalPath, value, options) {
31648
- const path75 = originalPath.slice();
31966
+ const path77 = originalPath.slice();
31649
31967
  const errors = [];
31650
31968
  const root = parseTree(text, errors);
31651
31969
  let parent = void 0;
31652
31970
  let lastSegment = void 0;
31653
- while (path75.length > 0) {
31654
- lastSegment = path75.pop();
31655
- parent = findNodeAtLocation(root, path75);
31971
+ while (path77.length > 0) {
31972
+ lastSegment = path77.pop();
31973
+ parent = findNodeAtLocation(root, path77);
31656
31974
  if (parent === void 0 && value !== void 0) {
31657
31975
  if (typeof lastSegment === "string") {
31658
31976
  value = { [lastSegment]: value };
@@ -31838,8 +32156,8 @@ var ParseErrorCode;
31838
32156
  ParseErrorCode2[ParseErrorCode2["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
31839
32157
  ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
31840
32158
  })(ParseErrorCode || (ParseErrorCode = {}));
31841
- function modify(text, path75, value, options) {
31842
- return setProperty(text, path75, value, options);
32159
+ function modify(text, path77, value, options) {
32160
+ return setProperty(text, path77, value, options);
31843
32161
  }
31844
32162
  function applyEdits(text, edits) {
31845
32163
  let sortedEdits = edits.slice(0).sort((a, b) => {
@@ -32232,7 +32550,12 @@ function plan(config2, versions, o) {
32232
32550
  },
32233
32551
  { name: "Deno", enabled: wanted(config2.deno.enabled, "deno"), install: (env) => setupDeno(env) },
32234
32552
  { name: "Bun", enabled: wanted(config2.bun.enabled, "bun"), install: (env) => setupBun(env) },
32235
- { name: "uv", enabled: wanted(config2.uv.enabled, "uv"), install: (env) => setupUv(env) },
32553
+ // Headroom (an agent add-on) installs with uv: the uv step runs for it too, before the apps.
32554
+ {
32555
+ name: "uv",
32556
+ enabled: wanted(config2.uv.enabled, "uv") || appsNeedUv(o.apps),
32557
+ install: (env) => setupUv(env)
32558
+ },
32236
32559
  { name: "PowerShell 7", enabled: wanted(config2.pwsh.enabled, "pwsh"), install: (env) => setupPwsh(env, o.refresh) },
32237
32560
  // The openssl tool comes from Git's install: after Git.
32238
32561
  {
@@ -32258,7 +32581,7 @@ function plan(config2, versions, o) {
32258
32581
  name: "Apps",
32259
32582
  enabled: apps,
32260
32583
  shared: true,
32261
- after: ["Mesa 3D", "Visual C++ runtime"],
32584
+ after: ["Mesa 3D", "Visual C++ runtime", "uv"],
32262
32585
  install: (env) => setupApps(env, o.apps, { gitAccess: o.gitAccess, refresh: o.refresh })
32263
32586
  },
32264
32587
  {
@@ -32399,6 +32722,7 @@ async function setupToolchains(config2, versions, options, run3 = {}) {
32399
32722
  env.summary.push(...part.summary);
32400
32723
  }
32401
32724
  Object.assign(env.vars, config2.environment);
32725
+ Object.assign(env.vars, envs.get("Git")?.vars);
32402
32726
  log("Set up " + steps.length + " toolchains and databases.");
32403
32727
  return env;
32404
32728
  }
@@ -33496,42 +33820,124 @@ function gitDenyRules(access) {
33496
33820
  function managedSettingsFile() {
33497
33821
  return path63.join(path63.dirname(managedClaudeMd()), "managed-settings.json");
33498
33822
  }
33499
- function writeManagedSettings2(access) {
33823
+ function writeManagedSettings2(access, headroom = null) {
33500
33824
  const file3 = managedSettingsFile();
33501
- const deny = gitDenyRules(access);
33502
- if (deny.length === 0) {
33825
+ const rtk = installedRtk();
33826
+ const settings = claudeManagedSettings(gitDenyRules(access), rtk, headroom);
33827
+ if (!settings) {
33503
33828
  fs50.rmSync(file3, { force: true });
33504
33829
  return;
33505
33830
  }
33506
33831
  fs50.mkdirSync(path63.dirname(file3), { recursive: true });
33507
- fs50.writeFileSync(file3, JSON.stringify({ permissions: { deny } }, null, 2) + "\n", "utf8");
33832
+ fs50.writeFileSync(file3, JSON.stringify(settings, null, 2) + "\n", "utf8");
33833
+ if (rtk) log("rtk: Claude Code's Bash commands go through it (hook in " + file3 + ")");
33508
33834
  }
33509
33835
 
33510
- // src/guest/setup/desktop.ts
33836
+ // src/guest/setup/addon-mcp.ts
33511
33837
  import fs51 from "node:fs";
33512
- import os10 from "node:os";
33513
33838
  import path64 from "node:path";
33839
+ var ADDON_MCP_NAMES = APPS.flatMap((a) => a.mcp ? [a.mcp.name] : []);
33840
+ function writeAddonMcpServers() {
33841
+ const servers = {};
33842
+ for (const { id, folder } of installedMcpAddons()) {
33843
+ const mcp = APPS.find((a) => a.id === id)?.mcp;
33844
+ if (mcp) servers[mcp.name] = addonMcpServer(folder, mcp);
33845
+ }
33846
+ const file3 = path64.join(claudeDir, ".claude.json");
33847
+ let state = {};
33848
+ if (fs51.existsSync(file3)) {
33849
+ try {
33850
+ const text = fs51.readFileSync(file3, "utf8");
33851
+ state = JSON.parse(text.charCodeAt(0) === 65279 ? text.slice(1) : text);
33852
+ } catch {
33853
+ warn("Claude Code MCP servers from Apps: " + file3 + " is not valid JSON, left as it is");
33854
+ return [];
33855
+ }
33856
+ } else if (!Object.keys(servers).length) return [];
33857
+ if (typeof state !== "object" || state === null || Array.isArray(state)) return [];
33858
+ const current = state.mcpServers;
33859
+ const before = current && typeof current === "object" && !Array.isArray(current) ? current : {};
33860
+ const after = withAddonMcpServers(before, servers, ADDON_MCP_NAMES);
33861
+ if (JSON.stringify(before) !== JSON.stringify(after)) {
33862
+ fs51.mkdirSync(claudeDir, { recursive: true });
33863
+ writeJsonAtomic(file3, { ...state, mcpServers: after });
33864
+ }
33865
+ const names = Object.keys(servers);
33866
+ if (names.length) log("Claude Code MCP servers from Apps: " + names.join(", "));
33867
+ return names;
33868
+ }
33869
+
33870
+ // src/guest/setup/headroom.ts
33871
+ import fs52 from "node:fs";
33872
+ import http from "node:http";
33873
+ import path65 from "node:path";
33874
+ import { spawn as spawn7 } from "node:child_process";
33875
+ var HEADROOM_LOG = path65.join(logsDir, "headroom.log");
33876
+ function answersAsHeadroom(port) {
33877
+ return new Promise((resolve2) => {
33878
+ const req = http.get({ host: "127.0.0.1", port, path: "/livez", timeout: 3e3 }, (res) => {
33879
+ let body = "";
33880
+ res.setEncoding("utf8");
33881
+ res.on("data", (chunk) => {
33882
+ if (body.length < 4096) body += chunk;
33883
+ });
33884
+ res.on("end", () => resolve2(res.statusCode === 200 && /"service"\s*:\s*"headroom-proxy"/.test(body)));
33885
+ });
33886
+ req.on("timeout", () => req.destroy());
33887
+ req.on("error", () => resolve2(false));
33888
+ });
33889
+ }
33890
+ async function startHeadroom(env) {
33891
+ const exe = installedAddon("headroom");
33892
+ if (!exe) return null;
33893
+ if (env.ANTHROPIC_BASE_URL) {
33894
+ log("Headroom: not used, ANTHROPIC_BASE_URL is set in the agent environment (" + env.ANTHROPIC_BASE_URL + ")");
33895
+ return null;
33896
+ }
33897
+ const port = HEADROOM_PORT;
33898
+ if (!await portOpen(port)) {
33899
+ const out = fs52.openSync(HEADROOM_LOG, "a");
33900
+ spawn7(exe, headroomArgs(port), {
33901
+ detached: true,
33902
+ stdio: ["ignore", out, out],
33903
+ windowsHide: true,
33904
+ env: headroomEnv(env)
33905
+ }).unref();
33906
+ fs52.closeSync(out);
33907
+ if (!await waitForPort(port, 90))
33908
+ throw new Error("the Headroom proxy did not start on port " + port + "; see " + HEADROOM_LOG);
33909
+ }
33910
+ if (!await answersAsHeadroom(port))
33911
+ throw new Error("port " + port + " is taken by another program; Claude Code talks to the API directly");
33912
+ log("Headroom: Claude Code reaches the API through " + headroomUrl(port) + " (log: " + HEADROOM_LOG + ")");
33913
+ return headroomUrl(port);
33914
+ }
33915
+
33916
+ // src/guest/setup/desktop.ts
33917
+ import fs53 from "node:fs";
33918
+ import os10 from "node:os";
33919
+ import path66 from "node:path";
33514
33920
  function applyPersonalization() {
33515
- const dir = path64.join(launchDir, "personalize");
33921
+ const dir = path66.join(launchDir, "personalize");
33516
33922
  if (!session.personalization || !exists(dir) || dryRun || !isWin) return;
33517
- exec(SYSTEM.reg(), ["import", path64.join(dir, "personalize.reg")], { stdio: "ignore" });
33518
- const wallpaper = fs51.readdirSync(dir).find((f) => f.startsWith("wallpaper"));
33519
- const local = wallpaper ? path64.join(mkdirp(path64.join(os10.homedir(), "Pictures")), wallpaper) : null;
33520
- if (wallpaper && local) fs51.copyFileSync(path64.join(dir, wallpaper), local);
33923
+ exec(SYSTEM.reg(), ["import", path66.join(dir, "personalize.reg")], { stdio: "ignore" });
33924
+ const wallpaper = fs53.readdirSync(dir).find((f) => f.startsWith("wallpaper"));
33925
+ const local = wallpaper ? path66.join(mkdirp(path66.join(os10.homedir(), "Pictures")), wallpaper) : null;
33926
+ if (wallpaper && local) fs53.copyFileSync(path66.join(dir, wallpaper), local);
33521
33927
  runScript("set-desktop.ps1", local ? ["-Wallpaper", local] : []);
33522
33928
  log("Desktop personalisation applied from this PC.");
33523
33929
  }
33524
33930
 
33525
33931
  // src/guest/setup/git-identity.ts
33526
- import fs52 from "node:fs";
33932
+ import fs54 from "node:fs";
33527
33933
  import os11 from "node:os";
33528
- import path65 from "node:path";
33934
+ import path67 from "node:path";
33529
33935
  import { spawnSync as spawnSync7 } from "node:child_process";
33530
33936
  function setupGitConfig(env) {
33531
33937
  if (dryRun) return;
33532
- const source = path65.join(launchDir, "home", ".gitconfig");
33533
- if (exists(source)) fs52.copyFileSync(source, path65.join(os11.homedir(), ".gitconfig"));
33534
- const git = path65.join(toolsDir, "git", "cmd", "git.exe");
33938
+ const source = path67.join(launchDir, "home", ".gitconfig");
33939
+ if (exists(source)) fs54.copyFileSync(source, path67.join(os11.homedir(), ".gitconfig"));
33940
+ const git = path67.join(toolsDir, "git", "cmd", "git.exe");
33535
33941
  if (!exists(git)) return;
33536
33942
  const { GIT_DIR: _gitDir, ...rest } = env;
33537
33943
  spawnSync7(git, ["config", "--global", "--add", "safe.directory", "*"], {
@@ -33544,7 +33950,7 @@ function setupGitConfig(env) {
33544
33950
  }
33545
33951
 
33546
33952
  // src/guest/setup/github-token.ts
33547
- import fs53 from "node:fs";
33953
+ import fs55 from "node:fs";
33548
33954
  import { spawnSync as spawnSync8 } from "node:child_process";
33549
33955
 
33550
33956
  // src/shared/github/token.ts
@@ -33590,7 +33996,7 @@ function ghOutputText(text, token) {
33590
33996
  var GH_TIMEOUT_MS = 6e4;
33591
33997
  function prepareGithubFolder(token) {
33592
33998
  const dir = deliveredGhConfigDir();
33593
- fs53.rmSync(dir, { recursive: true, force: true });
33999
+ fs55.rmSync(dir, { recursive: true, force: true });
33594
34000
  if (!token) return false;
33595
34001
  mkdirp(dir);
33596
34002
  return true;
@@ -33618,7 +34024,7 @@ function loginGithub(token, env) {
33618
34024
  const gh2 = ghExePath();
33619
34025
  const dir = deliveredGhConfigDir();
33620
34026
  if (!exists(gh2) || env.GH_CONFIG_DIR !== dir) {
33621
- fs53.rmSync(dir, { recursive: true, force: true });
34027
+ fs55.rmSync(dir, { recursive: true, force: true });
33622
34028
  log("A GitHub token arrived, but gh is not set up in this sandbox (toolchains.cli.gh), so it is not used.");
33623
34029
  return;
33624
34030
  }
@@ -33626,7 +34032,7 @@ function loginGithub(token, env) {
33626
34032
  runGh(ghLoginCommand(gh2, dir, token, env), token, "gh auth login");
33627
34033
  runGh(ghSetupGitCommand(gh2, dir, env), token, "gh auth setup-git");
33628
34034
  } catch (e) {
33629
- fs53.rmSync(dir, { recursive: true, force: true });
34035
+ fs55.rmSync(dir, { recursive: true, force: true });
33630
34036
  env.GH_CONFIG_DIR = mkdirp(ghConfigDir());
33631
34037
  addMachineEnvironment({ GH_CONFIG_DIR: env.GH_CONFIG_DIR }, []);
33632
34038
  throw e;
@@ -33640,35 +34046,35 @@ function ensurePtyHost() {
33640
34046
  }
33641
34047
 
33642
34048
  // src/guest/toolchains/leftovers.ts
33643
- import fs54 from "node:fs";
33644
- import path66 from "node:path";
34049
+ import fs56 from "node:fs";
34050
+ import path68 from "node:path";
33645
34051
  var STAGING = /\.staging-[0-9a-f]+$/i;
33646
34052
  var entries2 = (dir) => {
33647
34053
  try {
33648
- return fs54.readdirSync(dir, { withFileTypes: true });
34054
+ return fs56.readdirSync(dir, { withFileTypes: true });
33649
34055
  } catch {
33650
34056
  return [];
33651
34057
  }
33652
34058
  };
33653
34059
  function pruneToolLeftovers() {
33654
- for (const legacy of ["ptyhost", "gitshim"]) safeRemove(path66.join(toolsDir, legacy), toolsDir);
34060
+ for (const legacy of ["ptyhost", "gitshim"]) safeRemove(path68.join(toolsDir, legacy), toolsDir);
33655
34061
  for (const top of entries2(toolsDir)) {
33656
- const dir = path66.join(toolsDir, top.name);
34062
+ const dir = path68.join(toolsDir, top.name);
33657
34063
  if (STAGING.test(top.name)) safeRemove(dir, toolsDir);
33658
34064
  else if (top.isDirectory())
33659
- for (const sub of entries2(dir).filter((e) => STAGING.test(e.name))) safeRemove(path66.join(dir, sub.name), dir);
34065
+ for (const sub of entries2(dir).filter((e) => STAGING.test(e.name))) safeRemove(path68.join(dir, sub.name), dir);
33660
34066
  }
33661
34067
  }
33662
34068
 
33663
34069
  // src/guest/audit/process-audit.ts
33664
- import path67 from "node:path";
34070
+ import path69 from "node:path";
33665
34071
  import { spawnSync as spawnSync9 } from "node:child_process";
33666
34072
  var PROCESS_CREATION = "{0CCE922B-69AE-11D9-BED3-505054503030}";
33667
34073
  var auditOn = () => Boolean(session.auditProcesses) && isWin && !dryRun;
33668
34074
  function enableProcessAudit() {
33669
34075
  if (!auditOn()) return;
33670
34076
  spawnSync9(
33671
- path67.join(process.env.WINDIR ?? "C:\\Windows", "System32", "auditpol.exe"),
34077
+ path69.join(process.env.WINDIR ?? "C:\\Windows", "System32", "auditpol.exe"),
33672
34078
  ["/set", "/subcategory:" + PROCESS_CREATION, "/success:enable"],
33673
34079
  { windowsHide: true, stdio: "ignore" }
33674
34080
  );
@@ -33692,10 +34098,10 @@ function enableProcessAudit() {
33692
34098
 
33693
34099
  // src/guest/vpn/vpn.ts
33694
34100
  import dns2 from "node:dns";
33695
- import fs58 from "node:fs";
34101
+ import fs60 from "node:fs";
33696
34102
  import net3 from "node:net";
33697
34103
  import os14 from "node:os";
33698
- import path71 from "node:path";
34104
+ import path73 from "node:path";
33699
34105
 
33700
34106
  // src/shared/vpn/ovpn.ts
33701
34107
  var PROGRAM_DIRECTIVES = [
@@ -34218,8 +34624,8 @@ function tunnelAddressFromLog(log2) {
34218
34624
 
34219
34625
  // src/guest/vpn/openvpn.ts
34220
34626
  import crypto4 from "node:crypto";
34221
- import fs55 from "node:fs";
34222
- import path68 from "node:path";
34627
+ import fs57 from "node:fs";
34628
+ import path70 from "node:path";
34223
34629
 
34224
34630
  // src/shared/vpn/download.ts
34225
34631
  var OPENVPN_MSI = {
@@ -34249,7 +34655,7 @@ var WIREGUARD_MSI = {
34249
34655
 
34250
34656
  // src/guest/vpn/openvpn.ts
34251
34657
  function openvpnExe(env = process.env) {
34252
- return path68.join(env.ProgramFiles ?? "C:\\Program Files", "OpenVPN", "bin", "openvpn.exe");
34658
+ return path70.join(env.ProgramFiles ?? "C:\\Program Files", "OpenVPN", "bin", "openvpn.exe");
34253
34659
  }
34254
34660
  function openvpnDownload(arch2 = process.arch) {
34255
34661
  const entry = arch2 === "arm64" ? OPENVPN_MSI.arm64 : OPENVPN_MSI.x64;
@@ -34260,20 +34666,20 @@ function repairServiceQueries() {
34260
34666
  if (did === "removed") log("VPN: repaired the Windows service queries installers use (a Windows Sandbox gap).");
34261
34667
  else if (did !== "absent" && did !== "kept") warn("VPN: could not check the Windows service queries installers use.");
34262
34668
  }
34263
- var hasHash = (file3, sha256) => crypto4.createHash("sha256").update(fs55.readFileSync(file3)).digest("hex") === sha256;
34669
+ var hasHash = (file3, sha256) => crypto4.createHash("sha256").update(fs57.readFileSync(file3)).digest("hex") === sha256;
34264
34670
  async function ensureOpenVpn() {
34265
34671
  const exe = openvpnExe();
34266
34672
  if (!isWin || dryRun) return exe;
34267
34673
  if (exists(exe)) return exe;
34268
34674
  const dl = openvpnDownload();
34269
- const msi = path68.join(mkdirp(path68.join(toolsDir, "downloads")), dl.file);
34675
+ const msi = path70.join(mkdirp(path70.join(toolsDir, "downloads")), dl.file);
34270
34676
  if (!exists(msi) || !hasHash(msi, dl.sha256)) {
34271
34677
  log("VPN: downloading OpenVPN " + OPENVPN_MSI.version + " (about 6 MB)");
34272
34678
  await downloader.file(dl.url, msi, dl.sha256);
34273
34679
  }
34274
34680
  repairServiceQueries();
34275
34681
  log("VPN: installing OpenVPN");
34276
- const logFile2 = path68.join(toolsDir, "downloads", "openvpn-install.log");
34682
+ const logFile2 = path70.join(toolsDir, "downloads", "openvpn-install.log");
34277
34683
  const code = await spawnAsync(SYSTEM.msiexec(), ["/i", msi, "/qn", "/norestart", "/l*v", logFile2], {
34278
34684
  stdio: "ignore"
34279
34685
  });
@@ -34284,10 +34690,10 @@ async function ensureOpenVpn() {
34284
34690
  }
34285
34691
 
34286
34692
  // src/guest/vpn/tunnel.ts
34287
- import { spawn as spawn7 } from "node:child_process";
34288
- import fs56 from "node:fs";
34693
+ import { spawn as spawn8 } from "node:child_process";
34694
+ import fs58 from "node:fs";
34289
34695
  import os12 from "node:os";
34290
- import path69 from "node:path";
34696
+ import path71 from "node:path";
34291
34697
  var DONE = "Initialization Sequence Completed";
34292
34698
  function failureReason(text) {
34293
34699
  if (/AUTH_FAILED/i.test(text))
@@ -34303,19 +34709,19 @@ function failureReason(text) {
34303
34709
  }
34304
34710
  var tail = (text, lines = 12) => text.trim().split(/\r?\n/).slice(-lines).join("\n");
34305
34711
  async function startTunnel(o) {
34306
- const dir = fs56.mkdtempSync(path69.join(os12.tmpdir(), "sbx-vpn-"));
34307
- const auth = path69.join(dir, "login.txt");
34308
- const logFile2 = path69.join(dir, "openvpn.log");
34712
+ const dir = fs58.mkdtempSync(path71.join(os12.tmpdir(), "sbx-vpn-"));
34713
+ const auth = path71.join(dir, "login.txt");
34714
+ const logFile2 = path71.join(dir, "openvpn.log");
34309
34715
  const keepLog = () => {
34310
34716
  try {
34311
- fs56.mkdirSync(logsDir, { recursive: true });
34312
- fs56.copyFileSync(logFile2, path69.join(logsDir, "openvpn.log"));
34717
+ fs58.mkdirSync(logsDir, { recursive: true });
34718
+ fs58.copyFileSync(logFile2, path71.join(logsDir, "openvpn.log"));
34313
34719
  } catch {
34314
34720
  }
34315
34721
  };
34316
- fs56.writeFileSync(auth, o.username + "\r\n" + o.password + "\r\n", "utf8");
34722
+ fs58.writeFileSync(auth, o.username + "\r\n" + o.password + "\r\n", "utf8");
34317
34723
  try {
34318
- const child = spawn7(
34724
+ const child = spawn8(
34319
34725
  o.exe,
34320
34726
  ["--config", o.profile, "--auth-user-pass", auth, "--auth-nocache", "--verb", "3", "--log", logFile2],
34321
34727
  { detached: true, stdio: "ignore", windowsHide: true }
@@ -34331,7 +34737,7 @@ async function startTunnel(o) {
34331
34737
  child.unref();
34332
34738
  const deadline = Date.now() + (o.timeoutMs ?? 9e4);
34333
34739
  for (; ; ) {
34334
- const text = fs56.existsSync(logFile2) ? fs56.readFileSync(logFile2, "utf8") : "";
34740
+ const text = fs58.existsSync(logFile2) ? fs58.readFileSync(logFile2, "utf8") : "";
34335
34741
  if (text.includes(DONE)) {
34336
34742
  keepLog();
34337
34743
  const tunnelIp = tunnelAddressFromLog(text) ?? void 0;
@@ -34351,41 +34757,41 @@ async function startTunnel(o) {
34351
34757
  await new Promise((r) => setTimeout(r, 500));
34352
34758
  }
34353
34759
  } finally {
34354
- fs56.rmSync(auth, { force: true });
34760
+ fs58.rmSync(auth, { force: true });
34355
34761
  }
34356
34762
  }
34357
34763
 
34358
34764
  // src/guest/vpn/wireguard.ts
34359
34765
  import { execFile } from "node:child_process";
34360
34766
  import crypto5 from "node:crypto";
34361
- import fs57 from "node:fs";
34767
+ import fs59 from "node:fs";
34362
34768
  import os13 from "node:os";
34363
- import path70 from "node:path";
34769
+ import path72 from "node:path";
34364
34770
  var WIREGUARD_TUNNEL = "sandbox";
34365
34771
  function wireguardDir(env = process.env) {
34366
- return path70.join(env.ProgramFiles ?? "C:\\Program Files", "WireGuard");
34772
+ return path72.join(env.ProgramFiles ?? "C:\\Program Files", "WireGuard");
34367
34773
  }
34368
34774
  function wireguardExe(env = process.env) {
34369
- return path70.join(wireguardDir(env), "wireguard.exe");
34775
+ return path72.join(wireguardDir(env), "wireguard.exe");
34370
34776
  }
34371
34777
  function wireguardDownload(arch2 = process.arch) {
34372
34778
  const entry = arch2 === "arm64" ? WIREGUARD_MSI.arm64 : WIREGUARD_MSI.x64;
34373
34779
  return { ...entry, url: WIREGUARD_MSI.base + entry.file };
34374
34780
  }
34375
- var hasHash2 = (file3, sha256) => crypto5.createHash("sha256").update(fs57.readFileSync(file3)).digest("hex") === sha256;
34781
+ var hasHash2 = (file3, sha256) => crypto5.createHash("sha256").update(fs59.readFileSync(file3)).digest("hex") === sha256;
34376
34782
  async function ensureWireguard() {
34377
34783
  const exe = wireguardExe();
34378
34784
  if (!isWin || dryRun) return exe;
34379
34785
  if (exists(exe)) return exe;
34380
34786
  const dl = wireguardDownload();
34381
- const msi = path70.join(mkdirp(path70.join(toolsDir, "downloads")), dl.file);
34787
+ const msi = path72.join(mkdirp(path72.join(toolsDir, "downloads")), dl.file);
34382
34788
  if (!exists(msi) || !hasHash2(msi, dl.sha256)) {
34383
34789
  log("VPN: downloading WireGuard " + WIREGUARD_MSI.version + " (about 3 MB)");
34384
34790
  await downloader.file(dl.url, msi, dl.sha256);
34385
34791
  }
34386
34792
  repairServiceQueries();
34387
34793
  log("VPN: installing WireGuard");
34388
- const logFile2 = path70.join(toolsDir, "downloads", "wireguard-install.log");
34794
+ const logFile2 = path72.join(toolsDir, "downloads", "wireguard-install.log");
34389
34795
  const code = await spawnAsync(
34390
34796
  SYSTEM.msiexec(),
34391
34797
  ["/i", msi, "/qn", "/norestart", "DO_NOT_LAUNCH=1", "/l*v", logFile2],
@@ -34417,10 +34823,10 @@ async function removeWireguardTunnel(exe) {
34417
34823
  }
34418
34824
  }
34419
34825
  async function startWireguardTunnel(o) {
34420
- const root = path70.join(process.env.SystemRoot ?? "C:\\Windows", "Temp");
34421
- const dir = fs57.mkdtempSync(path70.join(fs57.existsSync(root) ? root : os13.tmpdir(), "sbx-wg-"));
34422
- const file3 = path70.join(dir, WIREGUARD_TUNNEL + ".conf");
34423
- fs57.writeFileSync(file3, o.config, "utf8");
34826
+ const root = path72.join(process.env.SystemRoot ?? "C:\\Windows", "Temp");
34827
+ const dir = fs59.mkdtempSync(path72.join(fs59.existsSync(root) ? root : os13.tmpdir(), "sbx-wg-"));
34828
+ const file3 = path72.join(dir, WIREGUARD_TUNNEL + ".conf");
34829
+ fs59.writeFileSync(file3, o.config, "utf8");
34424
34830
  const fail = async (message) => {
34425
34831
  await removeWireguardTunnel(o.exe);
34426
34832
  return { ok: false, message };
@@ -34441,11 +34847,11 @@ async function startWireguardTunnel(o) {
34441
34847
  await sleep(500);
34442
34848
  }
34443
34849
  } finally {
34444
- fs57.rmSync(dir, { recursive: true, force: true });
34850
+ fs59.rmSync(dir, { recursive: true, force: true });
34445
34851
  }
34446
34852
  }
34447
34853
  async function waitForHandshake(exe, poke, timeoutMs = 3e4) {
34448
- const wg = path70.join(path70.dirname(exe), "wg.exe");
34854
+ const wg = path72.join(path72.dirname(exe), "wg.exe");
34449
34855
  if (!exists(wg)) return { ok: true, message: "the tunnel service is running (no wg.exe to check the handshake)" };
34450
34856
  const deadline = Date.now() + timeoutMs;
34451
34857
  for (; ; ) {
@@ -34580,8 +34986,8 @@ async function setupVpn(secrets, ptyHostExe) {
34580
34986
  } else {
34581
34987
  const credentials = secrets.vpn;
34582
34988
  if (!credentials) throw new Error("the VPN login did not arrive (start the sandbox again)");
34583
- const profile = path71.join(launchDir, "vpn", "server.ovpn");
34584
- const remotes = assertSafeOvpn(fs58.readFileSync(profile, "utf8"), vpn.server);
34989
+ const profile = path73.join(launchDir, "vpn", "server.ovpn");
34990
+ const remotes = assertSafeOvpn(fs60.readFileSync(profile, "utf8"), vpn.server);
34585
34991
  const exe = await ensureOpenVpn();
34586
34992
  const servers = await serverAddresses(remotes);
34587
34993
  if (servers.length === 0) throw new Error("the VPN server addresses could not be found");
@@ -34630,6 +35036,8 @@ var FrameType = {
34630
35036
  Resize: 3,
34631
35037
  Restart: 4,
34632
35038
  Ping: 5,
35039
+ /** Ends the program and everything it started, and with it the session. */
35040
+ Close: 6,
34633
35041
  Output: 16,
34634
35042
  Exit: 17,
34635
35043
  Error: 18,
@@ -34834,7 +35242,7 @@ var OutputFilter = class {
34834
35242
  };
34835
35243
 
34836
35244
  // src/shared/pty/console-input.ts
34837
- import { spawn as spawn8 } from "node:child_process";
35245
+ import { spawn as spawn9 } from "node:child_process";
34838
35246
  function enableVtInput() {
34839
35247
  if (process.platform !== "win32" || !process.stdin.isTTY)
34840
35248
  return { ready: Promise.resolve(false), stop: () => Promise.resolve() };
@@ -34842,7 +35250,7 @@ function enableVtInput() {
34842
35250
  let exited = Promise.resolve();
34843
35251
  const ready = new Promise((resolve2) => {
34844
35252
  try {
34845
- child = spawn8(SYSTEM.powershell(), scriptArgs("enable-vt-input.ps1", ["-ProcessId", String(process.pid)]), {
35253
+ child = spawn9(SYSTEM.powershell(), scriptArgs("enable-vt-input.ps1", ["-ProcessId", String(process.pid)]), {
34846
35254
  stdio: "ignore",
34847
35255
  windowsHide: true
34848
35256
  });
@@ -35091,7 +35499,7 @@ async function viewSession(id) {
35091
35499
  }
35092
35500
 
35093
35501
  // src/guest/boot/boot-env.ts
35094
- import path72 from "node:path";
35502
+ import path74 from "node:path";
35095
35503
 
35096
35504
  // src/guest/boot/project-env.ts
35097
35505
  var EnvSchema = external_exports.object({
@@ -35110,7 +35518,7 @@ var BootEnvSchema = external_exports.object({
35110
35518
  });
35111
35519
 
35112
35520
  // src/guest/boot/boot-env.ts
35113
- var file2 = () => path72.join(localDir, "boot-env.json");
35521
+ var file2 = () => path74.join(localDir, "boot-env.json");
35114
35522
  function saveBootEnv(value) {
35115
35523
  try {
35116
35524
  writeJsonAtomic(file2(), value);
@@ -35139,7 +35547,7 @@ function saveProjectStatus(name, workspace2, phase, message, databases, summary)
35139
35547
  }
35140
35548
 
35141
35549
  // src/guest/runtime/secrets.ts
35142
- import fs59 from "node:fs";
35550
+ import fs61 from "node:fs";
35143
35551
 
35144
35552
  // src/shared/channel/secrets.ts
35145
35553
  var Environment = external_exports.record(external_exports.string().max(256), external_exports.string().max(65536));
@@ -35167,12 +35575,12 @@ function takeSecrets(id) {
35167
35575
  const file3 = run.secretFile(id);
35168
35576
  let text;
35169
35577
  try {
35170
- if (fs59.statSync(file3).size > MAX_BYTES) throw new Error("too big");
35171
- text = fs59.readFileSync(file3, "utf8").replace(/^\uFEFF/, "");
35578
+ if (fs61.statSync(file3).size > MAX_BYTES) throw new Error("too big");
35579
+ text = fs61.readFileSync(file3, "utf8").replace(/^\uFEFF/, "");
35172
35580
  } catch {
35173
35581
  return {};
35174
35582
  } finally {
35175
- fs59.rmSync(file3, { force: true });
35583
+ fs61.rmSync(file3, { force: true });
35176
35584
  }
35177
35585
  try {
35178
35586
  const parsed = SecretsFileSchema.safeParse(JSON.parse(text));
@@ -35210,7 +35618,7 @@ async function soft(what, work) {
35210
35618
  }
35211
35619
  }
35212
35620
  function startDaemon(env, secrets) {
35213
- spawn9(process.execPath, [path73.join(path73.dirname(process.argv[1]), "daemon.js")], {
35621
+ spawn10(process.execPath, [path75.join(path75.dirname(process.argv[1]), "daemon.js")], {
35214
35622
  detached: true,
35215
35623
  stdio: "ignore",
35216
35624
  windowsHide: true,
@@ -35223,20 +35631,20 @@ function startDaemon(env, secrets) {
35223
35631
  }).unref();
35224
35632
  }
35225
35633
  function writeDbCommand() {
35226
- const dir = mkdirp(path73.join(localDir, "bin"));
35227
- const db = path73.join(path73.dirname(process.argv[1]), "db.js");
35228
- fs60.writeFileSync(
35229
- path73.join(dir, "sbx-db.cmd"),
35634
+ const dir = mkdirp(path75.join(localDir, "bin"));
35635
+ const db = path75.join(path75.dirname(process.argv[1]), "db.js");
35636
+ fs62.writeFileSync(
35637
+ path75.join(dir, "sbx-db.cmd"),
35230
35638
  '@setlocal\r\n@set AGENT_LOG_PREFIX=db\r\n@"' + process.execPath + '" "' + db + '" %*\r\n'
35231
35639
  );
35232
35640
  writeAgentCommands(dir);
35233
35641
  return dir;
35234
35642
  }
35235
35643
  function writeAgentCommands(dir) {
35236
- const install = { node: process.execPath, script: path73.join(path73.dirname(process.argv[1]), "agent.js") };
35644
+ const install = { node: process.execPath, script: path75.join(path75.dirname(process.argv[1]), "agent.js") };
35237
35645
  for (const shim3 of agentShims(toolsDir, session.agents ?? {}))
35238
- fs60.writeFileSync(path73.join(dir, shim3.command + ".cmd"), shimText(shim3, install));
35239
- fs60.writeFileSync(path73.join(dir, "sbx-agent.cmd"), sbxAgentText(install));
35646
+ fs62.writeFileSync(path75.join(dir, shim3.command + ".cmd"), shimText(shim3, install));
35647
+ fs62.writeFileSync(path75.join(dir, "sbx-agent.cmd"), sbxAgentText(install));
35240
35648
  }
35241
35649
  async function waitForPrimary() {
35242
35650
  for (let i = 0; i < PRIMARY_POLLS; i++) {
@@ -35264,7 +35672,7 @@ var Boot = class {
35264
35672
  log(
35265
35673
  "Agent sandbox: " + session.project + " | workspace " + workspace + " | agent " + session.harnessName + " | log " + logPath
35266
35674
  );
35267
- if (!fs60.statSync(workspace, { throwIfNoEntry: false })?.isDirectory())
35675
+ if (!fs62.statSync(workspace, { throwIfNoEntry: false })?.isDirectory())
35268
35676
  throw new Error("Workspace folder is missing in the sandbox: " + workspace);
35269
35677
  mkdirp(toolsDir);
35270
35678
  mkdirp(run.root);
@@ -35276,7 +35684,7 @@ var Boot = class {
35276
35684
  const { tools, env } = await this.developerTools();
35277
35685
  await this.vpn();
35278
35686
  await this.gitIdentity(env);
35279
- await this.claude(tools);
35687
+ await this.claude(tools, env);
35280
35688
  await this.agent(env);
35281
35689
  this.preAgentHooks(env);
35282
35690
  await this.start(env);
@@ -35359,15 +35767,20 @@ var Boot = class {
35359
35767
  await soft("GitHub token", () => loginGithub(token, env));
35360
35768
  }
35361
35769
  }
35362
- async claude(tools) {
35770
+ async claude(tools, env) {
35363
35771
  step(6, "Claude settings, history and claude-mem");
35364
35772
  if (session.harnessName === "claude")
35365
35773
  await soft("Claude settings", () => syncClaudeProfile(this.secrets.claudeCredentials));
35366
35774
  await soft("claude-mem restore", restoreClaudeMem);
35367
35775
  await soft("Agent files of the last sandbox", resetMachineBound);
35368
35776
  await soft("Agent databases restore", restoreLocalState);
35777
+ let headroom = null;
35778
+ await soft("MCP servers from Apps", writeAddonMcpServers);
35779
+ await soft("Headroom proxy", async () => {
35780
+ headroom = await startHeadroom(env);
35781
+ });
35782
+ await soft("Claude managed settings", () => writeManagedSettings2(this.gitAccess, headroom));
35369
35783
  await soft("Sandbox guide for the agent", () => writeGuides(tools.summary, this.lan));
35370
- await soft("Claude managed settings", () => writeManagedSettings2(this.gitAccess));
35371
35784
  }
35372
35785
  async agent(env) {
35373
35786
  step(7, "Agent: " + session.harnessName);
@@ -35419,11 +35832,11 @@ runBoot().catch((e) => {
35419
35832
  const message = errMsg(e) + (e instanceof Error && e.stack ? "\n" + e.stack : "");
35420
35833
  try {
35421
35834
  setSandboxStatus("failed", errMsg(e));
35422
- fs61.writeFileSync(path74.join(logsDir, "guest-error.txt"), message);
35835
+ fs63.writeFileSync(path76.join(logsDir, "guest-error.txt"), message);
35423
35836
  } catch {
35424
35837
  }
35425
35838
  console.error(
35426
- "\n\x1B[31mThe sandbox could not start: " + errMsg(e) + "\x1B[0m\nDetails: " + path74.join(logsDir, "guest-error.txt")
35839
+ "\n\x1B[31mThe sandbox could not start: " + errMsg(e) + "\x1B[0m\nDetails: " + path76.join(logsDir, "guest-error.txt")
35427
35840
  );
35428
35841
  process.exitCode = 1;
35429
35842
  });