windows-development-sandbox 1.17.0 → 1.18.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 (319) hide show
  1. package/README.md +92 -92
  2. package/config.example.json +97 -4
  3. package/dist/app/features/accounts/accounts.def.d.ts +116 -2
  4. package/dist/app/features/accounts/accounts.def.js +80 -12
  5. package/dist/app/features/accounts/accounts.run.d.ts +29 -0
  6. package/dist/app/features/accounts/accounts.run.js +36 -6
  7. package/dist/app/features/accounts/index.d.ts +2 -0
  8. package/dist/app/features/accounts/index.js +4 -1
  9. package/dist/app/features/accounts/services.def.d.ts +111 -0
  10. package/dist/app/features/accounts/services.def.js +187 -0
  11. package/dist/app/features/accounts/services.run.d.ts +65 -0
  12. package/dist/app/features/accounts/services.run.js +212 -0
  13. package/dist/app/features/defs.d.ts +112 -1
  14. package/dist/app/features/defs.js +5 -0
  15. package/dist/app/features/settings/categories.def.js +21 -4
  16. package/dist/app/registry.js +5 -1
  17. package/dist/bin/agent-sandbox.js +2 -2
  18. package/dist/bin/{chunk-QLGGAG3D.js → chunk-BKF43GJ3.js} +51 -16
  19. package/dist/bin/{chunk-REE3EMHD.js → chunk-YD3A67RS.js} +3812 -1702
  20. package/dist/bin/{chunk-XYUVIVJT.js → chunk-Z5D32FN3.js} +1 -1
  21. package/dist/bin/claude-host.js +2 -2
  22. package/dist/bin/claude-sbx.js +2 -2
  23. package/dist/bin/codex-sbx.js +2 -2
  24. package/dist/bin/gemini-sbx.js +2 -2
  25. package/dist/bin/sandbox.js +2 -2
  26. package/dist/bin/sbx-hide.js +2 -2
  27. package/dist/bin/sbx-show.js +2 -2
  28. package/dist/cli/commands/launch/launch.js +30 -1
  29. package/dist/cli/manifest.d.ts +93 -65
  30. package/dist/cli/manifest.js +23 -5
  31. package/dist/cli/menu/pages/accounts-page.js +31 -16
  32. package/dist/cli/services/accounts-service.d.ts +14 -4
  33. package/dist/cli/services/accounts-service.js +19 -6
  34. package/dist/cli/services/data-collect.js +26 -1
  35. package/dist/core/config/defaults.js +47 -4
  36. package/dist/core/detect/toolchains.d.ts +15 -1
  37. package/dist/core/detect/toolchains.js +45 -12
  38. package/dist/core/local-config/overrides.js +6 -0
  39. package/dist/core/schema/accounts.d.ts +27 -0
  40. package/dist/core/schema/accounts.js +32 -0
  41. package/dist/core/schema/config.d.ts +239 -0
  42. package/dist/core/schema/config.js +6 -0
  43. package/dist/core/schema/pc-installs.d.ts +7 -0
  44. package/dist/core/schema/pc-installs.js +17 -0
  45. package/dist/core/schema/toolchains.d.ts +61 -0
  46. package/dist/core/schema/toolchains.js +106 -0
  47. package/dist/core/types.d.ts +18 -0
  48. package/dist/guest/accounts/adapter.d.ts +25 -0
  49. package/dist/guest/accounts/adapter.js +21 -0
  50. package/dist/guest/accounts/adapters.d.ts +5 -0
  51. package/dist/guest/accounts/adapters.js +18 -0
  52. package/dist/guest/accounts/apply.d.ts +41 -0
  53. package/dist/guest/accounts/apply.js +47 -0
  54. package/dist/guest/accounts/kinds/appstore.d.ts +2 -0
  55. package/dist/guest/accounts/kinds/appstore.js +7 -0
  56. package/dist/guest/accounts/kinds/cfx.d.ts +2 -0
  57. package/dist/guest/accounts/kinds/cfx.js +7 -0
  58. package/dist/guest/accounts/kinds/epic.d.ts +2 -0
  59. package/dist/guest/accounts/kinds/epic.js +26 -0
  60. package/dist/guest/accounts/kinds/expo.d.ts +2 -0
  61. package/dist/guest/accounts/kinds/expo.js +7 -0
  62. package/dist/guest/accounts/kinds/keystore.d.ts +2 -0
  63. package/dist/guest/accounts/kinds/keystore.js +7 -0
  64. package/dist/guest/accounts/kinds/play.d.ts +2 -0
  65. package/dist/guest/accounts/kinds/play.js +7 -0
  66. package/dist/guest/accounts/kinds/steam.d.ts +4 -0
  67. package/dist/guest/accounts/kinds/steam.js +17 -0
  68. package/dist/guest/accounts/kinds/unity.d.ts +2 -0
  69. package/dist/guest/accounts/kinds/unity.js +7 -0
  70. package/dist/guest/accounts/watch.d.ts +28 -0
  71. package/dist/guest/accounts/watch.js +78 -0
  72. package/dist/guest/agents/agent-files.d.ts +6 -0
  73. package/dist/guest/agents/agent-files.js +14 -0
  74. package/dist/guest/agents/local-state.d.ts +2 -0
  75. package/dist/guest/agents/local-state.js +13 -1
  76. package/dist/guest/apps/resolvers-gamedev.d.ts +2 -0
  77. package/dist/guest/apps/resolvers-gamedev.js +1 -0
  78. package/dist/guest/apps/resolvers-mobile.d.ts +2 -0
  79. package/dist/guest/apps/resolvers-mobile.js +1 -0
  80. package/dist/guest/apps/resolvers.js +4 -0
  81. package/dist/guest/boot/boot.d.ts +5 -0
  82. package/dist/guest/boot/boot.js +36 -1
  83. package/dist/guest/daemon/accounts.d.ts +3 -0
  84. package/dist/guest/daemon/accounts.js +25 -0
  85. package/dist/guest/daemon/daemon.js +2 -0
  86. package/dist/guest/daemon/idle-shutdown.js +3 -1
  87. package/dist/guest/daemon/sessions.js +2 -0
  88. package/dist/guest/devtools/android.d.ts +2 -0
  89. package/dist/guest/devtools/android.js +7 -0
  90. package/dist/guest/devtools/devtool.d.ts +9 -0
  91. package/dist/guest/devtools/devtool.js +5 -0
  92. package/dist/guest/devtools/fivem.d.ts +2 -0
  93. package/dist/guest/devtools/fivem.js +7 -0
  94. package/dist/guest/devtools/registry.d.ts +6 -0
  95. package/dist/guest/devtools/registry.js +12 -0
  96. package/dist/guest/devtools/steam.d.ts +2 -0
  97. package/dist/guest/devtools/steam.js +228 -0
  98. package/dist/guest/devtools/unity.d.ts +2 -0
  99. package/dist/guest/devtools/unity.js +7 -0
  100. package/dist/guest/entries/daemon.js +2 -0
  101. package/dist/guest/entries/tool.d.ts +1 -0
  102. package/dist/guest/entries/tool.js +17 -0
  103. package/dist/guest/toolchains/engines/godot.d.ts +4 -0
  104. package/dist/guest/toolchains/engines/godot.js +4 -0
  105. package/dist/guest/toolchains/engines/unity-release.d.ts +15 -0
  106. package/dist/guest/toolchains/engines/unity-release.js +6 -0
  107. package/dist/guest/toolchains/engines/unity.d.ts +4 -0
  108. package/dist/guest/toolchains/engines/unity.js +4 -0
  109. package/dist/guest/toolchains/engines/unreal-engines.d.ts +4 -0
  110. package/dist/guest/toolchains/engines/unreal-engines.js +4 -0
  111. package/dist/guest/toolchains/fivem/artifacts.d.ts +6 -0
  112. package/dist/guest/toolchains/fivem/artifacts.js +7 -0
  113. package/dist/guest/toolchains/fivem/fivem.d.ts +4 -0
  114. package/dist/guest/toolchains/fivem/fivem.js +4 -0
  115. package/dist/guest/toolchains/installers.d.ts +12 -0
  116. package/dist/guest/toolchains/installers.js +39 -0
  117. package/dist/guest/toolchains/languages/java.js +1 -1
  118. package/dist/guest/toolchains/languages/kotlin.d.ts +3 -0
  119. package/dist/guest/toolchains/languages/kotlin.js +6 -0
  120. package/dist/guest/toolchains/mobile/android-repo.d.ts +11 -0
  121. package/dist/guest/toolchains/mobile/android-repo.js +10 -0
  122. package/dist/guest/toolchains/mobile/android.d.ts +17 -0
  123. package/dist/guest/toolchains/mobile/android.js +16 -0
  124. package/dist/guest/toolchains/mobile/dart.d.ts +4 -0
  125. package/dist/guest/toolchains/mobile/dart.js +4 -0
  126. package/dist/guest/toolchains/mobile/flutter.d.ts +4 -0
  127. package/dist/guest/toolchains/mobile/flutter.js +4 -0
  128. package/dist/guest/toolchains/mobile/react-native.d.ts +4 -0
  129. package/dist/guest/toolchains/mobile/react-native.js +4 -0
  130. package/dist/guest/toolchains/native/unreal.d.ts +3 -7
  131. package/dist/guest/toolchains/native/unreal.js +16 -155
  132. package/dist/guest/toolchains/native/visual-studio.d.ts +15 -0
  133. package/dist/guest/toolchains/native/visual-studio.js +159 -0
  134. package/dist/guest/toolchains/native/vs-request.d.ts +19 -0
  135. package/dist/guest/toolchains/native/vs-request.js +46 -0
  136. package/dist/guest/toolchains/not-yet.d.ts +2 -0
  137. package/dist/guest/toolchains/not-yet.js +6 -0
  138. package/dist/guest/toolchains/persisted.d.ts +48 -0
  139. package/dist/guest/toolchains/persisted.js +159 -0
  140. package/dist/guest/toolchains/plan.js +80 -4
  141. package/dist/guest/toolchains/steam/apps.d.ts +27 -0
  142. package/dist/guest/toolchains/steam/apps.js +112 -0
  143. package/dist/guest/toolchains/steam/client.d.ts +5 -0
  144. package/dist/guest/toolchains/steam/client.js +67 -0
  145. package/dist/guest/toolchains/steam/commands.d.ts +58 -0
  146. package/dist/guest/toolchains/steam/commands.js +155 -0
  147. package/dist/guest/toolchains/steam/steam.d.ts +4 -0
  148. package/dist/guest/toolchains/steam/steam.js +61 -0
  149. package/dist/guest/toolchains/steam/steamcmd-install.d.ts +29 -0
  150. package/dist/guest/toolchains/steam/steamcmd-install.js +177 -0
  151. package/dist/guest/toolchains/steam/steamcmd.d.ts +6 -0
  152. package/dist/guest/toolchains/steam/steamcmd.js +23 -0
  153. package/dist/guest/toolchains/step.d.ts +10 -0
  154. package/dist/host/accounts/deliver.d.ts +26 -0
  155. package/dist/host/accounts/deliver.js +80 -0
  156. package/dist/host/accounts/handoff.d.ts +17 -0
  157. package/dist/host/accounts/handoff.js +110 -0
  158. package/dist/host/accounts/index.d.ts +6 -0
  159. package/dist/host/accounts/index.js +8 -0
  160. package/dist/host/accounts/kinds/appstore.d.ts +2 -0
  161. package/dist/host/accounts/kinds/appstore.js +10 -0
  162. package/dist/host/accounts/kinds/cfx.d.ts +2 -0
  163. package/dist/host/accounts/kinds/cfx.js +7 -0
  164. package/dist/host/accounts/kinds/epic.d.ts +2 -0
  165. package/dist/host/accounts/kinds/epic.js +15 -0
  166. package/dist/host/accounts/kinds/expo.d.ts +2 -0
  167. package/dist/host/accounts/kinds/expo.js +7 -0
  168. package/dist/host/accounts/kinds/keystore.d.ts +2 -0
  169. package/dist/host/accounts/kinds/keystore.js +7 -0
  170. package/dist/host/accounts/kinds/play.d.ts +2 -0
  171. package/dist/host/accounts/kinds/play.js +7 -0
  172. package/dist/host/accounts/kinds/steam.d.ts +6 -0
  173. package/dist/host/accounts/kinds/steam.js +31 -0
  174. package/dist/host/accounts/kinds/unity.d.ts +2 -0
  175. package/dist/host/accounts/kinds/unity.js +13 -0
  176. package/dist/host/accounts/leases.d.ts +40 -0
  177. package/dist/host/accounts/leases.js +62 -0
  178. package/dist/host/accounts/source.d.ts +21 -0
  179. package/dist/host/accounts/source.js +15 -0
  180. package/dist/host/accounts/sources.d.ts +5 -0
  181. package/dist/host/accounts/sources.js +18 -0
  182. package/dist/host/accounts/store.d.ts +23 -0
  183. package/dist/host/accounts/store.js +74 -0
  184. package/dist/host/audit/log.d.ts +1 -1
  185. package/dist/host/launch/generate.d.ts +2 -0
  186. package/dist/host/launch/generate.js +16 -1
  187. package/dist/host/launch/hints/android.d.ts +3 -0
  188. package/dist/host/launch/hints/android.js +3 -0
  189. package/dist/host/launch/hints/engines.d.ts +3 -0
  190. package/dist/host/launch/hints/engines.js +3 -0
  191. package/dist/host/launch/hints/fivem.d.ts +3 -0
  192. package/dist/host/launch/hints/fivem.js +3 -0
  193. package/dist/host/launch/hints/hint.d.ts +2 -0
  194. package/dist/host/launch/hints/hint.js +1 -0
  195. package/dist/host/launch/hints/index.d.ts +5 -0
  196. package/dist/host/launch/hints/index.js +29 -0
  197. package/dist/host/launch/hints/mobile.d.ts +3 -0
  198. package/dist/host/launch/hints/mobile.js +3 -0
  199. package/dist/host/launch/hints/steam.d.ts +7 -0
  200. package/dist/host/launch/hints/steam.js +59 -0
  201. package/dist/host/launch/index.d.ts +2 -2
  202. package/dist/host/launch/index.js +2 -2
  203. package/dist/host/launch/mappings.js +2 -0
  204. package/dist/host/launch/session-file.js +2 -0
  205. package/dist/host/launch/stage.js +10 -1
  206. package/dist/host/launch/toolchains.d.ts +7 -0
  207. package/dist/host/launch/toolchains.js +11 -0
  208. package/dist/host/pc-installs/discover.d.ts +19 -0
  209. package/dist/host/pc-installs/discover.js +59 -0
  210. package/dist/host/pc-installs/epic.d.ts +2 -0
  211. package/dist/host/pc-installs/epic.js +7 -0
  212. package/dist/host/pc-installs/index.d.ts +3 -0
  213. package/dist/host/pc-installs/index.js +2 -0
  214. package/dist/host/pc-installs/source.d.ts +9 -0
  215. package/dist/host/pc-installs/source.js +1 -0
  216. package/dist/host/pc-installs/steam.d.ts +16 -0
  217. package/dist/host/pc-installs/steam.js +143 -0
  218. package/dist/host/pc-installs/unity.d.ts +2 -0
  219. package/dist/host/pc-installs/unity.js +7 -0
  220. package/dist/host/pc-installs/unreal.d.ts +2 -0
  221. package/dist/host/pc-installs/unreal.js +7 -0
  222. package/dist/host/profile/accounts.d.ts +21 -6
  223. package/dist/host/profile/accounts.js +108 -28
  224. package/dist/host/profile/claude-login.d.ts +32 -2
  225. package/dist/host/profile/claude-login.js +100 -8
  226. package/dist/host/profile/index.d.ts +1 -1
  227. package/dist/host/profile/index.js +1 -1
  228. package/dist/host/secrets/launch-secrets.js +1 -0
  229. package/dist/runtime/agent.js +321 -9
  230. package/dist/runtime/daemon.js +1987 -595
  231. package/dist/runtime/db.js +334 -11
  232. package/dist/runtime/main.js +2530 -709
  233. package/dist/runtime/netproxy.js +321 -9
  234. package/dist/runtime/tool.js +28549 -0
  235. package/dist/runtime/view.js +321 -9
  236. package/dist/selftest/accounts.test.js +172 -1
  237. package/dist/selftest/agent-paths.test.js +13 -1
  238. package/dist/selftest/android.test.d.ts +2 -0
  239. package/dist/selftest/android.test.js +7 -0
  240. package/dist/selftest/app.test.js +1 -1
  241. package/dist/selftest/engines.test.d.ts +2 -0
  242. package/dist/selftest/engines.test.js +7 -0
  243. package/dist/selftest/fivem.test.d.ts +2 -0
  244. package/dist/selftest/fivem.test.js +7 -0
  245. package/dist/selftest/generate.test.js +1 -1
  246. package/dist/selftest/index.js +18 -0
  247. package/dist/selftest/menu-screens.js +1 -0
  248. package/dist/selftest/menu.test.js +2 -1
  249. package/dist/selftest/mobile.test.d.ts +2 -0
  250. package/dist/selftest/mobile.test.js +7 -0
  251. package/dist/selftest/pc-installs.test.d.ts +2 -0
  252. package/dist/selftest/pc-installs.test.js +111 -0
  253. package/dist/selftest/persisted-installs.test.d.ts +2 -0
  254. package/dist/selftest/persisted-installs.test.js +107 -0
  255. package/dist/selftest/projects.test.js +6 -4
  256. package/dist/selftest/service-accounts.test.d.ts +2 -0
  257. package/dist/selftest/service-accounts.test.js +320 -0
  258. package/dist/selftest/steam.test.d.ts +2 -0
  259. package/dist/selftest/steam.test.js +316 -0
  260. package/dist/selftest/storage-helper.test.js +31 -8
  261. package/dist/selftest/support/engine-golden.js +3 -0
  262. package/dist/selftest/support/fixtures.d.ts +87 -0
  263. package/dist/selftest/support/fixtures.js +4 -3
  264. package/dist/selftest/support/pc-memory.d.ts +2 -0
  265. package/dist/selftest/support/pc-memory.js +17 -0
  266. package/dist/selftest/support/web-preview.js +20 -1
  267. package/dist/selftest/unity.test.d.ts +2 -0
  268. package/dist/selftest/unity.test.js +7 -0
  269. package/dist/shared/accounts/fingerprint.d.ts +1 -0
  270. package/dist/shared/accounts/fingerprint.js +20 -0
  271. package/dist/shared/accounts/index.d.ts +5 -0
  272. package/dist/shared/accounts/index.js +7 -0
  273. package/dist/shared/accounts/kinds/epic.d.ts +10 -0
  274. package/dist/shared/accounts/kinds/epic.js +56 -0
  275. package/dist/shared/accounts/kinds/steam.d.ts +28 -0
  276. package/dist/shared/accounts/kinds/steam.js +99 -0
  277. package/dist/shared/accounts/kinds.d.ts +41 -0
  278. package/dist/shared/accounts/kinds.js +154 -0
  279. package/dist/shared/accounts/payloads.d.ts +151 -0
  280. package/dist/shared/accounts/payloads.js +128 -0
  281. package/dist/shared/apps/catalogue-gamedev.d.ts +2 -0
  282. package/dist/shared/apps/catalogue-gamedev.js +1 -0
  283. package/dist/shared/apps/catalogue-mobile.d.ts +2 -0
  284. package/dist/shared/apps/catalogue-mobile.js +1 -0
  285. package/dist/shared/apps/catalogue-modding.d.ts +2 -0
  286. package/dist/shared/apps/catalogue-modding.js +1 -0
  287. package/dist/shared/apps/catalogue.d.ts +1 -0
  288. package/dist/shared/apps/catalogue.js +6 -1
  289. package/dist/shared/channel/schema.d.ts +183 -0
  290. package/dist/shared/channel/secrets.d.ts +42 -0
  291. package/dist/shared/channel/secrets.js +3 -0
  292. package/dist/shared/download/downloader.d.ts +7 -0
  293. package/dist/shared/download/downloader.js +10 -3
  294. package/dist/shared/installs.d.ts +19 -0
  295. package/dist/shared/installs.js +13 -0
  296. package/dist/shared/io/bounded.d.ts +6 -0
  297. package/dist/shared/io/bounded.js +38 -0
  298. package/dist/shared/io/index.d.ts +2 -0
  299. package/dist/shared/io/index.js +2 -0
  300. package/dist/shared/io/text.d.ts +9 -0
  301. package/dist/shared/io/text.js +18 -0
  302. package/dist/shared/paths/guest-paths.js +1 -1
  303. package/dist/shared/paths/index.d.ts +1 -0
  304. package/dist/shared/paths/index.js +1 -0
  305. package/dist/shared/paths/pc-installs.d.ts +20 -0
  306. package/dist/shared/paths/pc-installs.js +19 -0
  307. package/dist/shared/vdf.d.ts +25 -0
  308. package/dist/shared/vdf.js +151 -0
  309. package/dist/sync/sync-cycle.js +9 -0
  310. package/dist/web/index.html +1 -1
  311. package/dist/web/{main-JTH3FT4O.js → main-CWIF4VQI.js} +63 -61
  312. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  313. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  314. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  315. package/package.json +1 -1
  316. package/sandbox/scripts/add-firewall-port.ps1 +13 -0
  317. package/sandbox/templates/fivem/README.txt +2 -0
  318. package/schemas/config.schema.json +485 -0
  319. package/schemas/sandbox-config.schema.json +416 -0
@@ -0,0 +1,18 @@
1
+ import { appstoreAdapter } from './kinds/appstore.js';
2
+ import { cfxAdapter } from './kinds/cfx.js';
3
+ import { epicAdapter } from './kinds/epic.js';
4
+ import { expoAdapter } from './kinds/expo.js';
5
+ import { keystoreAdapter } from './kinds/keystore.js';
6
+ import { playAdapter } from './kinds/play.js';
7
+ import { steamAdapter } from './kinds/steam.js';
8
+ import { unityAdapter } from './kinds/unity.js';
9
+ export const ADAPTERS = {
10
+ epic: epicAdapter,
11
+ steam: steamAdapter,
12
+ unity: unityAdapter,
13
+ cfx: cfxAdapter,
14
+ keystore: keystoreAdapter,
15
+ play: playAdapter,
16
+ expo: expoAdapter,
17
+ appstore: appstoreAdapter,
18
+ };
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ import { type ServiceKind } from '../../shared/accounts/kinds.js';
3
+ import type { ServiceAccounts } from '../../shared/accounts/payloads.js';
4
+ import { type GuestAdapter } from './adapter.js';
5
+ export declare const DeliveredSchema: z.ZodObject<{
6
+ fingerprints: z.ZodRecord<z.ZodEnum<{
7
+ unity: "unity";
8
+ steam: "steam";
9
+ epic: "epic";
10
+ cfx: "cfx";
11
+ keystore: "keystore";
12
+ play: "play";
13
+ expo: "expo";
14
+ appstore: "appstore";
15
+ }> & z.core.$partial, z.ZodString>;
16
+ leased: z.ZodArray<z.ZodEnum<{
17
+ unity: "unity";
18
+ steam: "steam";
19
+ epic: "epic";
20
+ cfx: "cfx";
21
+ keystore: "keystore";
22
+ play: "play";
23
+ expo: "expo";
24
+ appstore: "appstore";
25
+ }>>;
26
+ }, z.core.$strip>;
27
+ export type Delivered = z.output<typeof DeliveredSchema>;
28
+ export declare function deliveredFile(root?: string): string;
29
+ export declare function readDelivered(root?: string): Delivered;
30
+ export interface ApplyOptions {
31
+ /** The kinds whose lease this launch holds (session.json accountLeases). */
32
+ readonly leased?: readonly ServiceKind[];
33
+ /** The boot log. */
34
+ readonly say?: (line: string) => void;
35
+ readonly root?: string;
36
+ readonly adapters?: {
37
+ readonly [K in ServiceKind]?: GuestAdapter<K>;
38
+ };
39
+ }
40
+ /** Applies every sign-in given; returns the variables for the agent's sessions. A failing kind is a log line. */
41
+ export declare function applyServiceAccounts(given: ServiceAccounts | undefined, o?: ApplyOptions): Promise<Record<string, string>>;
@@ -0,0 +1,47 @@
1
+ // At boot: each handed-over sign-in goes to its tool (the adapters), then is dropped from memory. What was delivered
2
+ // is remembered only as fingerprints, with the kinds whose lease this launch holds, in delivered.json on the
3
+ // sandbox's own disk: the daemon's watch starts from them, so its first look never hands back what it was given.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { z } from 'zod';
7
+ import { fingerprint } from '../../shared/accounts/fingerprint.js';
8
+ import { SERVICE_KINDS } from '../../shared/accounts/kinds.js';
9
+ import { readValid, writeJsonAtomic } from '../../shared/io/json.js';
10
+ import { errMsg } from '../../core/util/errors.js';
11
+ import { accountsRoot } from './adapter.js';
12
+ import { ADAPTERS } from './adapters.js';
13
+ export const DeliveredSchema = z.object({
14
+ fingerprints: z.partialRecord(z.enum(SERVICE_KINDS), z.string().max(128)),
15
+ leased: z.array(z.enum(SERVICE_KINDS)),
16
+ });
17
+ export function deliveredFile(root = accountsRoot()) {
18
+ return path.join(root, 'delivered.json');
19
+ }
20
+ export function readDelivered(root = accountsRoot()) {
21
+ return readValid(DeliveredSchema, deliveredFile(root)) ?? { fingerprints: {}, leased: [] };
22
+ }
23
+ /** Applies every sign-in given; returns the variables for the agent's sessions. A failing kind is a log line. */
24
+ export async function applyServiceAccounts(given, o = {}) {
25
+ const say = o.say ?? (() => undefined);
26
+ const env = {};
27
+ const fingerprints = {};
28
+ for (const kind of SERVICE_KINDS) {
29
+ const payload = given?.[kind];
30
+ if (payload === undefined)
31
+ continue;
32
+ const adapter = (o.adapters?.[kind] ?? ADAPTERS[kind]);
33
+ try {
34
+ const applied = await adapter.apply(payload);
35
+ Object.assign(env, applied.env ?? {});
36
+ fingerprints[kind] = fingerprint(payload);
37
+ say(applied.summary);
38
+ }
39
+ catch (e) {
40
+ say(kind + ' sign-in: could not be used (' + errMsg(e) + ')');
41
+ }
42
+ }
43
+ const root = o.root ?? accountsRoot();
44
+ fs.mkdirSync(root, { recursive: true });
45
+ writeJsonAtomic(deliveredFile(root), { fingerprints, leased: [...(o.leased ?? [])] });
46
+ return env;
47
+ }
@@ -0,0 +1,2 @@
1
+ import { type GuestAdapter } from '../adapter.js';
2
+ export declare const appstoreAdapter: GuestAdapter<'appstore'>;
@@ -0,0 +1,7 @@
1
+ import { notAvailableYet } from '../adapter.js';
2
+ export const appstoreAdapter = {
3
+ kind: 'appstore',
4
+ apply(_p) {
5
+ return notAvailableYet('appstore');
6
+ },
7
+ };
@@ -0,0 +1,2 @@
1
+ import { type GuestAdapter } from '../adapter.js';
2
+ export declare const cfxAdapter: GuestAdapter<'cfx'>;
@@ -0,0 +1,7 @@
1
+ import { notAvailableYet } from '../adapter.js';
2
+ export const cfxAdapter = {
3
+ kind: 'cfx',
4
+ apply(_p) {
5
+ return notAvailableYet('cfx');
6
+ },
7
+ };
@@ -0,0 +1,2 @@
1
+ import { type GuestAdapter } from '../adapter.js';
2
+ export declare const epicAdapter: GuestAdapter<'epic'>;
@@ -0,0 +1,26 @@
1
+ // Epic Games: the handed-over sign-in goes into the launcher's GameUserSettings.ini before anyone opens it (its other
2
+ // settings are kept, and the file keeps the encoding it had: the launcher often writes UTF-16). The watch reads it
3
+ // back to notice when the launcher renews it, or when you sign in here the first time.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { epicSettingsFile, MAX_EPIC_INI_BYTES, readEpicLogin, withEpicLogin, } from '../../../shared/accounts/index.js';
7
+ import { decodeText, encodeText, readBoundedFile } from '../../../shared/io/index.js';
8
+ import { localAppData } from '../adapter.js';
9
+ function settingsFile() {
10
+ return epicSettingsFile(localAppData());
11
+ }
12
+ export const epicAdapter = {
13
+ kind: 'epic',
14
+ apply(p) {
15
+ const file = settingsFile();
16
+ const bytes = readBoundedFile(file, MAX_EPIC_INI_BYTES);
17
+ const read = bytes ? decodeText(bytes) : { text: '', encoding: 'utf8' };
18
+ fs.mkdirSync(path.dirname(file), { recursive: true });
19
+ fs.writeFileSync(file, encodeText(withEpicLogin(read.text, p), read.encoding));
20
+ return { summary: 'Epic Games Launcher: signed in with your saved sign-in' };
21
+ },
22
+ current() {
23
+ const bytes = readBoundedFile(settingsFile(), MAX_EPIC_INI_BYTES);
24
+ return bytes ? readEpicLogin(decodeText(bytes).text) : null;
25
+ },
26
+ };
@@ -0,0 +1,2 @@
1
+ import { type GuestAdapter } from '../adapter.js';
2
+ export declare const expoAdapter: GuestAdapter<'expo'>;
@@ -0,0 +1,7 @@
1
+ import { notAvailableYet } from '../adapter.js';
2
+ export const expoAdapter = {
3
+ kind: 'expo',
4
+ apply(_p) {
5
+ return notAvailableYet('expo');
6
+ },
7
+ };
@@ -0,0 +1,2 @@
1
+ import { type GuestAdapter } from '../adapter.js';
2
+ export declare const keystoreAdapter: GuestAdapter<'keystore'>;
@@ -0,0 +1,7 @@
1
+ import { notAvailableYet } from '../adapter.js';
2
+ export const keystoreAdapter = {
3
+ kind: 'keystore',
4
+ apply(_p) {
5
+ return notAvailableYet('keystore');
6
+ },
7
+ };
@@ -0,0 +1,2 @@
1
+ import { type GuestAdapter } from '../adapter.js';
2
+ export declare const playAdapter: GuestAdapter<'play'>;
@@ -0,0 +1,7 @@
1
+ import { notAvailableYet } from '../adapter.js';
2
+ export const playAdapter = {
3
+ kind: 'play',
4
+ apply(_p) {
5
+ return notAvailableYet('play');
6
+ },
7
+ };
@@ -0,0 +1,4 @@
1
+ import type { GuestAdapter } from '../adapter.js';
2
+ /** The adapter for a SteamCMD folder (the self-test passes its own). */
3
+ export declare function steamAdapterFor(steamcmdDir: string): GuestAdapter<'steam'>;
4
+ export declare const steamAdapter: GuestAdapter<'steam'>;
@@ -0,0 +1,17 @@
1
+ // Steam: SteamCMD's cached login goes into C:\SteamCMD\config (config.vdf, the account name) and the ssfn sentry files
2
+ // beside steamcmd.exe, on the sandbox's own disk, before the Steam step copies SteamCMD's binaries in (that copy never
3
+ // touches config\). The watch reads it back when SteamCMD renews it, or after a first `sbx-steam login` here.
4
+ import { readSteamCmdLogin, writeSteamCmdLogin } from '../../../shared/accounts/kinds/steam.js';
5
+ import { STEAMCMD_DIR } from '../../toolchains/steam/commands.js';
6
+ /** The adapter for a SteamCMD folder (the self-test passes its own). */
7
+ export function steamAdapterFor(steamcmdDir) {
8
+ return {
9
+ kind: 'steam',
10
+ apply(p) {
11
+ writeSteamCmdLogin(steamcmdDir, p);
12
+ return { summary: 'SteamCMD: signed in as ' + p.account + ' with your saved sign-in' };
13
+ },
14
+ current: () => readSteamCmdLogin(steamcmdDir),
15
+ };
16
+ }
17
+ export const steamAdapter = steamAdapterFor(STEAMCMD_DIR);
@@ -0,0 +1,2 @@
1
+ import { type GuestAdapter } from '../adapter.js';
2
+ export declare const unityAdapter: GuestAdapter<'unity'>;
@@ -0,0 +1,7 @@
1
+ import { notAvailableYet } from '../adapter.js';
2
+ export const unityAdapter = {
3
+ kind: 'unity',
4
+ apply(_p) {
5
+ return notAvailableYet('unity');
6
+ },
7
+ };
@@ -0,0 +1,28 @@
1
+ import { type ServiceKind } from '../../shared/accounts/kinds.js';
2
+ import type { GuestAdapter } from './adapter.js';
3
+ export interface WatchOptions {
4
+ /** The data folder (C:\AgentData); the handoffs go in its state\accounts. */
5
+ readonly dataDir: string;
6
+ readonly launchId: string;
7
+ /** Where delivered.json is (default: the sandbox's accounts folder). */
8
+ readonly root?: string;
9
+ readonly adapters?: {
10
+ readonly [K in ServiceKind]?: GuestAdapter<K>;
11
+ };
12
+ readonly warn?: (line: string) => void;
13
+ }
14
+ export declare class AccountWatch {
15
+ private readonly o;
16
+ private readonly last;
17
+ private readonly timers;
18
+ private readonly kinds;
19
+ constructor(kinds: readonly ServiceKind[], o: WatchOptions);
20
+ private adapter;
21
+ /** The kinds being watched. */
22
+ watched(): readonly ServiceKind[];
23
+ start(): void;
24
+ /** One look at one kind; true when a handoff was written. */
25
+ check(kind: ServiceKind): boolean;
26
+ /** Looks at every kind now; with `final` (the sandbox stops) each adapter also lets go of what it holds. */
27
+ flush(final?: boolean): Promise<void>;
28
+ }
@@ -0,0 +1,78 @@
1
+ // While the sandbox runs: each sign-in that may come back (Epic, Steam, Unity) is looked at every minute or so, and
2
+ // when the tool holds a different one (renewed, or a first sign-in made here) it is left for the PC in
3
+ // <data>\state\accounts\<kind>.json, which the PC seals and deletes on its next sync. Only kinds whose lease this
4
+ // launch holds; seeded with what was delivered, so the first look writes nothing. flush() looks once more now (the
5
+ // daemon calls it where claude-mem is saved) and, at the end, lets go of what must not stay taken.
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { fingerprint } from '../../shared/accounts/fingerprint.js';
9
+ import { HANDOFF_PARTS, SERVICE_SPECS } from '../../shared/accounts/kinds.js';
10
+ import { writeJsonAtomic } from '../../shared/io/json.js';
11
+ import { errMsg } from '../../core/util/errors.js';
12
+ import { ADAPTERS } from './adapters.js';
13
+ import { readDelivered } from './apply.js';
14
+ const DEFAULT_CHECK_MS = 60_000;
15
+ export class AccountWatch {
16
+ o;
17
+ last = new Map();
18
+ timers = [];
19
+ kinds;
20
+ constructor(kinds, o) {
21
+ this.o = o;
22
+ this.kinds = kinds.filter((k) => SERVICE_SPECS[k].returns && this.adapter(k).current);
23
+ const delivered = readDelivered(o.root);
24
+ for (const kind of this.kinds)
25
+ this.last.set(kind, delivered.fingerprints[kind] ?? null);
26
+ }
27
+ adapter(kind) {
28
+ return (this.o.adapters?.[kind] ?? ADAPTERS[kind]);
29
+ }
30
+ /** The kinds being watched. */
31
+ watched() {
32
+ return this.kinds;
33
+ }
34
+ start() {
35
+ for (const kind of this.kinds) {
36
+ const timer = setInterval(() => this.check(kind), this.adapter(kind).checkMs ?? DEFAULT_CHECK_MS);
37
+ timer.unref();
38
+ this.timers.push(timer);
39
+ }
40
+ }
41
+ /** One look at one kind; true when a handoff was written. */
42
+ check(kind) {
43
+ try {
44
+ const now = this.adapter(kind).current?.() ?? null;
45
+ if (!now)
46
+ return false;
47
+ const print = fingerprint(now);
48
+ if (print === this.last.get(kind))
49
+ return false;
50
+ const file = path.join(this.o.dataDir, ...HANDOFF_PARTS, kind + '.json');
51
+ fs.mkdirSync(path.dirname(file), { recursive: true });
52
+ writeJsonAtomic(file, { kind, launchId: this.o.launchId, payload: now });
53
+ this.last.set(kind, print);
54
+ return true;
55
+ }
56
+ catch (e) {
57
+ this.o.warn?.(SERVICE_SPECS[kind].label + ' sign-in: could not keep it for the next sandbox: ' + errMsg(e));
58
+ return false;
59
+ }
60
+ }
61
+ /** Looks at every kind now; with `final` (the sandbox stops) each adapter also lets go of what it holds. */
62
+ async flush(final = false) {
63
+ for (const kind of this.kinds)
64
+ this.check(kind);
65
+ if (!final)
66
+ return;
67
+ for (const timer of this.timers)
68
+ clearInterval(timer);
69
+ for (const kind of this.kinds) {
70
+ try {
71
+ await this.adapter(kind).release?.();
72
+ }
73
+ catch (e) {
74
+ this.o.warn?.(SERVICE_SPECS[kind].label + ': could not let go of the sign-in: ' + errMsg(e));
75
+ }
76
+ }
77
+ }
78
+ }
@@ -48,6 +48,12 @@ export declare function bootResetPaths(agents: Readonly<Record<string, Harness>>
48
48
  agent: string;
49
49
  path: string;
50
50
  }[];
51
+ /**
52
+ * Codex's config.toml with its own command sandbox off, or null when it already says which one to use. Inside Windows
53
+ * Sandbox that inner sandbox only costs: its setup re-grants ACLs over the whole profile at every boot (minutes), and
54
+ * every command hangs until it is done. A `sandbox_mode` the user set (before the first [table]) is kept.
55
+ */
56
+ export declare function codexConfigWithoutSandbox(toml: string): string | null;
51
57
  /** A SQLite database file or one of its -wal / -shm siblings. */
52
58
  export declare const isDatabaseFile: (name: string) => boolean;
53
59
  /** Files that make a consistent copy: the databases and their -wal (the -shm is rebuilt by SQLite). */
@@ -61,6 +61,20 @@ export function localStateEntries(agents, stateDir, localRoot) {
61
61
  export function bootResetPaths(agents, stateDir) {
62
62
  return Object.entries(agents).flatMap(([agent, harness]) => (harness.bootReset ?? []).map((rel) => ({ agent, path: path.join(stateDir, agent, rel) })));
63
63
  }
64
+ /**
65
+ * Codex's config.toml with its own command sandbox off, or null when it already says which one to use. Inside Windows
66
+ * Sandbox that inner sandbox only costs: its setup re-grants ACLs over the whole profile at every boot (minutes), and
67
+ * every command hangs until it is done. A `sandbox_mode` the user set (before the first [table]) is kept.
68
+ */
69
+ export function codexConfigWithoutSandbox(toml) {
70
+ const firstTable = toml.search(/^\s*\[/m);
71
+ const topLevel = firstTable < 0 ? toml : toml.slice(0, firstTable);
72
+ if (/^\s*sandbox_mode\s*=/m.test(topLevel))
73
+ return null;
74
+ return ('# Set by the sandbox: Windows Sandbox already contains Codex, its own command sandbox would only slow it down.\n' +
75
+ 'sandbox_mode = "danger-full-access"\n' +
76
+ (toml ? '\n' + toml : ''));
77
+ }
64
78
  /** A SQLite database file or one of its -wal / -shm siblings. */
65
79
  export const isDatabaseFile = (name) => /\.(sqlite3?|db)(-wal|-shm)?$/i.test(name);
66
80
  /** Files that make a consistent copy: the databases and their -wal (the -shm is rebuilt by SQLite). */
@@ -5,6 +5,8 @@ export declare function agentLocalStates(): LocalState[];
5
5
  export declare function localStateVars(): Record<string, string>;
6
6
  /** Boot: what an agent sealed to the last sandbox (bootReset) is deleted, so it sets it up again. Links are left. */
7
7
  export declare function resetMachineBound(): void;
8
+ /** Boot: Codex runs its commands without its own sandbox (codexConfigWithoutSandbox), unless the user picked one. */
9
+ export declare function codexWithoutInnerSandbox(): void;
8
10
  /** Boot: the saved databases of every configured agent are copied to the sandbox's disk, where the agent runs them. */
9
11
  export declare function restoreLocalState(): void;
10
12
  /**
@@ -10,7 +10,7 @@ import { execOutput } from '../runtime/process.js';
10
10
  import { log, warn } from '../runtime/log.js';
11
11
  import { session, stateDir } from '../runtime/session.js';
12
12
  import { errMsg } from '../../core/util/errors.js';
13
- import { bootResetPaths, isCopyableDatabaseFile, isDatabaseFile, localStateEntries, } from './agent-files.js';
13
+ import { bootResetPaths, codexConfigWithoutSandbox, isCopyableDatabaseFile, isDatabaseFile, localStateEntries, } from './agent-files.js';
14
14
  const SQLITE_HEADER = 'SQLite format 3\0';
15
15
  /** The sandbox-disk folder that holds the working copies. */
16
16
  const localRoot = () => path.join(localDir, 'agent-state');
@@ -102,6 +102,18 @@ export function resetMachineBound() {
102
102
  }
103
103
  }
104
104
  }
105
+ /** Boot: Codex runs its commands without its own sandbox (codexConfigWithoutSandbox), unless the user picked one. */
106
+ export function codexWithoutInnerSandbox() {
107
+ if (dryRun || !session.agents?.codex)
108
+ return;
109
+ const file = path.join(stateDir, 'codex', 'config.toml');
110
+ const updated = codexConfigWithoutSandbox(exists(file) ? fs.readFileSync(file, 'utf8') : '');
111
+ if (updated === null)
112
+ return;
113
+ mkdirp(path.dirname(file));
114
+ fs.writeFileSync(file, updated, 'utf8');
115
+ log('codex: its own command sandbox is off (sandbox_mode = "danger-full-access" in config.toml).');
116
+ }
105
117
  /** Boot: the saved databases of every configured agent are copied to the sandbox's disk, where the agent runs them. */
106
118
  export function restoreLocalState() {
107
119
  if (dryRun)
@@ -0,0 +1,2 @@
1
+ import type { Download } from './resolvers.js';
2
+ export declare const GAMEDEV_RESOLVERS: Readonly<Record<string, () => Promise<Download>>>;
@@ -0,0 +1 @@
1
+ export const GAMEDEV_RESOLVERS = {};
@@ -0,0 +1,2 @@
1
+ import type { Download } from './resolvers.js';
2
+ export declare const MOBILE_RESOLVERS: Readonly<Record<string, () => Promise<Download>>>;
@@ -0,0 +1 @@
1
+ export const MOBILE_RESOLVERS = {};
@@ -1,6 +1,8 @@
1
1
  // Where the latest version of an app is, for apps that are not a plain GitHub release: each looks it up on the
2
2
  // vendor's own site (and its published checksum, where there is one). Named by AppSource.resolve in shared/apps.ts.
3
3
  import { fetchJson, fetchText, githubAsset, latestGithubTag } from '../../shared/download/index.js';
4
+ import { GAMEDEV_RESOLVERS } from './resolvers-gamedev.js';
5
+ import { MOBILE_RESOLVERS } from './resolvers-mobile.js';
4
6
  /** The SHA-256 on the line of a SHA256SUMS-style file that names `file`. */
5
7
  const sumFor = (sums, file) => sums
6
8
  .split(/\r?\n/)
@@ -10,6 +12,8 @@ const sumFor = (sums, file) => sums
10
12
  const firstHash = (text) => /\b([0-9a-f]{64})\b/i.exec(text)?.[1].toLowerCase() ?? null;
11
13
  const stripV = (tag) => tag.replace(/^v/, '');
12
14
  export const RESOLVERS = {
15
+ ...GAMEDEV_RESOLVERS,
16
+ ...MOBILE_RESOLVERS,
13
17
  sublime: async () => {
14
18
  // Sublime publishes no checksums; the download is HTTPS from sublimetext.com.
15
19
  const { latest_version: build } = await fetchJson('https://www.sublimetext.com/updates/4/stable_update_check');
@@ -6,6 +6,11 @@ export declare class Boot {
6
6
  /** What the host handed over in its one-shot file (read and deleted at the start of run()). */
7
7
  private secrets;
8
8
  run(): Promise<void>;
9
+ /**
10
+ * Your saved service sign-ins (accounts.<kind>.share) go to their tools before anything opens them, and leave memory
11
+ * at once; what they give the agent (EXPO_TOKEN ...) joins its environment (in memory only).
12
+ */
13
+ private serviceAccounts;
9
14
  private networkAndSessionHost;
10
15
  private hooksAndDesktop;
11
16
  /** Toolchains, databases, apps, the network proxy and VS Code; then the agent environment, saved machine-wide. */
@@ -6,7 +6,7 @@ import { writeGuides } from './guide.js';
6
6
  import { setupToolchains } from '../toolchains/index.js';
7
7
  import { agents } from '../agents/agent-installer.js';
8
8
  import { agentShims, sbxAgentText, shimText } from '../agents/agent-files.js';
9
- import { resetMachineBound, restoreLocalState } from '../agents/local-state.js';
9
+ import { codexWithoutInnerSandbox, resetMachineBound, restoreLocalState } from '../agents/local-state.js';
10
10
  import { installShellPath } from '../setup/shell-path.js';
11
11
  import { linkProfileDirs } from '../agents/profile-links.js';
12
12
  import { restoreClaudeMem } from '../setup/claude-mem.js';
@@ -22,6 +22,8 @@ import { applyPersonalization } from '../setup/desktop.js';
22
22
  import { keepBrowsersOffGpu } from '../setup/browser-gpu.js';
23
23
  import { setupGitConfig } from '../setup/git-identity.js';
24
24
  import { loginGithub, prepareGithubFolder } from '../setup/github-token.js';
25
+ import { applyServiceAccounts } from '../accounts/apply.js';
26
+ import { devToolsFor } from '../devtools/registry.js';
25
27
  import { ensurePtyHost } from '../sessions/pty-host.js';
26
28
  import { pruneToolLeftovers } from '../toolchains/leftovers.js';
27
29
  import { wanted } from '../toolchains/detect.js';
@@ -95,8 +97,23 @@ function writeDbCommand() {
95
97
  const db = path.join(path.dirname(process.argv[1]), 'db.js');
96
98
  fs.writeFileSync(path.join(dir, 'sbx-db.cmd'), '@setlocal\r\n@set AGENT_LOG_PREFIX=db\r\n@"' + process.execPath + '" "' + db + '" %*\r\n');
97
99
  writeAgentCommands(dir);
100
+ writeDevToolCommands(dir);
98
101
  return dir;
99
102
  }
103
+ /** sbx-steam, sbx-fivem, sbx-unity, sbx-android for the toolchains that are on: runtime\tool.js with that helper. */
104
+ function writeDevToolCommands(dir) {
105
+ const script = path.join(path.dirname(process.argv[1]), 'tool.js');
106
+ for (const tool of devToolsFor(session.toolchains))
107
+ fs.writeFileSync(path.join(dir, 'sbx-' + tool.name + '.cmd'), '@setlocal\r\n@set AGENT_LOG_PREFIX=' +
108
+ tool.name +
109
+ '\r\n@"' +
110
+ process.execPath +
111
+ '" "' +
112
+ script +
113
+ '" ' +
114
+ tool.name +
115
+ ' %*\r\n');
116
+ }
100
117
  /**
101
118
  * `claude`, `codex` ... by name in every terminal tab, installed yet or not (the command installs the agent on first
102
119
  * use), and `sbx-agent`. Written to the sandbox's disk each boot, next to `sbx-db`.
@@ -146,6 +163,7 @@ export class Boot {
146
163
  mkdirp(toolsDir);
147
164
  mkdirp(run.root);
148
165
  this.secrets = takeSecrets(session.launchId);
166
+ await this.serviceAccounts();
149
167
  applyBootEnvironment(this.secrets.harnessEnvironment);
150
168
  prepareGithubFolder(this.secrets.githubToken);
151
169
  await this.networkAndSessionHost();
@@ -158,6 +176,21 @@ export class Boot {
158
176
  this.preAgentHooks(env);
159
177
  await this.start(env);
160
178
  }
179
+ /**
180
+ * Your saved service sign-ins (accounts.<kind>.share) go to their tools before anything opens them, and leave memory
181
+ * at once; what they give the agent (EXPO_TOKEN ...) joins its environment (in memory only).
182
+ */
183
+ async serviceAccounts() {
184
+ const given = this.secrets.serviceAccounts;
185
+ this.secrets = { ...this.secrets, serviceAccounts: undefined };
186
+ if (dryRun || (!given && !session.accountLeases?.length))
187
+ return;
188
+ await soft('Service sign-ins', async () => {
189
+ const env = await applyServiceAccounts(given, { leased: session.accountLeases ?? [], say: (line) => log(line) });
190
+ if (Object.keys(env).length)
191
+ this.secrets = { ...this.secrets, harnessEnvironment: { ...env, ...this.secrets.harnessEnvironment } };
192
+ });
193
+ }
161
194
  async networkAndSessionHost() {
162
195
  step(1, 'Network and session host');
163
196
  if (isWin && !dryRun && session.attach.enabled)
@@ -186,6 +219,7 @@ export class Boot {
186
219
  refresh: session.provisioning.refresh,
187
220
  databases: session.databases ?? [],
188
221
  apps: session.apps,
222
+ ...(session.pcInstalls ? { pcInstalls: session.pcInstalls } : {}),
189
223
  });
190
224
  for (const p of networkProblems())
191
225
  tools.summary.push('PROTECTION MISSING: ' + p);
@@ -246,6 +280,7 @@ export class Boot {
246
280
  await soft('Claude settings', () => syncClaudeProfile(this.secrets.claudeCredentials));
247
281
  await soft('claude-mem restore', restoreClaudeMem);
248
282
  await soft('Agent files of the last sandbox', resetMachineBound);
283
+ await soft('Codex without its own sandbox', codexWithoutInnerSandbox);
249
284
  await soft('Agent databases restore', restoreLocalState);
250
285
  // Agent add-ons: MCP servers in the sandbox's Claude Code, and the Headroom proxy before the managed settings point
251
286
  // Claude Code at it (the guide reads them back, so it comes last).
@@ -0,0 +1,3 @@
1
+ export declare function startAccountWatch(): void;
2
+ /** Looks once more now; `final` (the sandbox stops) also lets go of what must not stay taken. Never throws. */
3
+ export declare function flushAccounts(final?: boolean): Promise<void>;
@@ -0,0 +1,25 @@
1
+ // The daemon's one watch over the service sign-ins this launch may hand back (session.json accountLeases), and its
2
+ // flush where claude-mem is saved (every 2 minutes, when a session ends, before an idle shutdown).
3
+ import { AccountWatch } from '../accounts/watch.js';
4
+ import { dataDir } from '../runtime/paths.js';
5
+ import { log, warn } from '../runtime/log.js';
6
+ import { session } from '../runtime/session.js';
7
+ let watch = null;
8
+ export function startAccountWatch() {
9
+ const kinds = session.accountLeases ?? [];
10
+ if (!kinds.length)
11
+ return;
12
+ watch = new AccountWatch(kinds, { dataDir, launchId: session.launchId, warn });
13
+ watch.start();
14
+ if (watch.watched().length)
15
+ log('Service sign-ins kept for the next sandbox: ' + watch.watched().join(', '));
16
+ }
17
+ /** Looks once more now; `final` (the sandbox stops) also lets go of what must not stay taken. Never throws. */
18
+ export async function flushAccounts(final = false) {
19
+ try {
20
+ await watch?.flush(final);
21
+ }
22
+ catch (e) {
23
+ warn('service sign-ins: ' + String(e));
24
+ }
25
+ }
@@ -11,6 +11,7 @@ import { withRequestSecrets } from '../runtime/secrets.js';
11
11
  import { liveFolders } from './live.js';
12
12
  import { warn } from '../runtime/log.js';
13
13
  import { errMsg } from '../../core/util/errors.js';
14
+ import { flushAccounts } from './accounts.js';
14
15
  const POLL_MS = 400;
15
16
  const MEM_SAVE_MS = 120000;
16
17
  export class Daemon {
@@ -45,6 +46,7 @@ export class Daemon {
45
46
  this.lastMemSave = Date.now();
46
47
  safely('claude-mem save', saveClaudeMem);
47
48
  safely('agent databases save', saveLocalState);
49
+ void flushAccounts();
48
50
  }
49
51
  }
50
52
  handle(req) {
@@ -8,6 +8,7 @@ import { isWin, run } from '../runtime/paths.js';
8
8
  import { log } from '../runtime/log.js';
9
9
  import { session } from '../runtime/session.js';
10
10
  import { safely } from './state.js';
11
+ import { flushAccounts } from './accounts.js';
11
12
  /** How long the sandbox must stay idle before it shuts down. */
12
13
  const GRACE_MS = 15000;
13
14
  export class IdleShutdown {
@@ -28,7 +29,8 @@ export class IdleShutdown {
28
29
  log('Headless sandbox has no sessions left; shutting down.');
29
30
  safely('claude-mem save', saveClaudeMem);
30
31
  safely('agent databases save', saveLocalState);
31
- spawnSync(SYSTEM.shutdown(), ['/s', '/t', '5'], { windowsHide: true });
32
+ // Renewed sign-ins go to the PC, and a held one (a Unity seat) is let go, before Windows stops.
33
+ void flushAccounts(true).finally(() => spawnSync(SYSTEM.shutdown(), ['/s', '/t', '5'], { windowsHide: true }));
32
34
  }, GRACE_MS);
33
35
  }
34
36
  idle() {