windows-development-sandbox 1.25.0 → 1.25.1

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 (158) hide show
  1. package/config.example.json +5 -2
  2. package/dist/app/features/data/data.def.d.ts +4 -4
  3. package/dist/app/features/data/data.run.d.ts +1 -1
  4. package/dist/app/features/defs.d.ts +161 -6
  5. package/dist/app/features/defs.js +7 -0
  6. package/dist/app/features/projects/projects.def.d.ts +1 -1
  7. package/dist/app/features/projects/projects.run.d.ts +1 -1
  8. package/dist/app/features/unreal/index.d.ts +2 -0
  9. package/dist/app/features/unreal/index.js +3 -0
  10. package/dist/app/features/unreal/unreal.def.d.ts +236 -0
  11. package/dist/app/features/unreal/unreal.def.js +379 -0
  12. package/dist/app/features/unreal/unreal.run.d.ts +96 -0
  13. package/dist/app/features/unreal/unreal.run.js +217 -0
  14. package/dist/app/registry.js +5 -0
  15. package/dist/bin/agent-sandbox.js +2 -2
  16. package/dist/bin/{chunk-SUYYVIDW.js → chunk-3GAXLATK.js} +58 -5
  17. package/dist/bin/{chunk-IZRGTSQ5.js → chunk-F4PVBN23.js} +2465 -1392
  18. package/dist/bin/{chunk-KH4MI2U5.js → chunk-G3TG4ZT7.js} +1 -1
  19. package/dist/bin/claude-host.js +2 -2
  20. package/dist/bin/claude-sbx.js +2 -2
  21. package/dist/bin/codex-sbx.js +2 -2
  22. package/dist/bin/gemini-sbx.js +2 -2
  23. package/dist/bin/sandbox.js +2 -2
  24. package/dist/bin/sbx-hide.js +2 -2
  25. package/dist/bin/sbx-show.js +2 -2
  26. package/dist/cli/commands/launch/launch.js +5 -2
  27. package/dist/cli/commands/registry.js +3 -0
  28. package/dist/cli/manifest.d.ts +18 -0
  29. package/dist/cli/manifest.js +26 -0
  30. package/dist/cli/menu/coverage-exempt.js +1 -0
  31. package/dist/cli/menu/pages/sandbox-page.d.ts +3 -0
  32. package/dist/cli/menu/pages/sandbox-page.js +34 -4
  33. package/dist/cli/menu/views.js +2 -0
  34. package/dist/core/config/defaults.js +18 -4
  35. package/dist/core/schema/config.d.ts +9 -7
  36. package/dist/core/schema/pc-installs.d.ts +3 -1
  37. package/dist/core/schema/pc-installs.js +10 -1
  38. package/dist/core/schema/toolchains.js +1 -1
  39. package/dist/guest/accounts/kinds/epic.js +18 -13
  40. package/dist/guest/apps/after-install.d.ts +1 -1
  41. package/dist/guest/apps/after-install.js +6 -1
  42. package/dist/guest/apps/app-installer.d.ts +1 -0
  43. package/dist/guest/apps/app-installer.js +13 -3
  44. package/dist/guest/apps/comet-unpack.js +3 -2
  45. package/dist/guest/apps/resolvers-gamedev.js +55 -1
  46. package/dist/guest/boot/boot.js +14 -1
  47. package/dist/guest/boot/guide.js +24 -0
  48. package/dist/guest/daemon/daemon.js +3 -0
  49. package/dist/guest/setup/addon-mcp.d.ts +7 -0
  50. package/dist/guest/setup/addon-mcp.js +19 -4
  51. package/dist/guest/setup/browser-gpu.js +14 -11
  52. package/dist/guest/setup/headroom.js +4 -0
  53. package/dist/guest/toolchains/engines/epic-launcher.d.ts +26 -0
  54. package/dist/guest/toolchains/engines/epic-launcher.js +127 -0
  55. package/dist/guest/toolchains/engines/unreal-engines.d.ts +21 -1
  56. package/dist/guest/toolchains/engines/unreal-engines.js +163 -3
  57. package/dist/guest/toolchains/engines/unreal-status.d.ts +13 -0
  58. package/dist/guest/toolchains/engines/unreal-status.js +41 -0
  59. package/dist/guest/toolchains/native/debuggers.d.ts +6 -0
  60. package/dist/guest/toolchains/native/debuggers.js +57 -0
  61. package/dist/guest/toolchains/native/mesa.js +15 -8
  62. package/dist/guest/toolchains/native/rust-extras.d.ts +7 -0
  63. package/dist/guest/toolchains/native/rust-extras.js +23 -1
  64. package/dist/guest/toolchains/native/unreal.d.ts +2 -2
  65. package/dist/guest/toolchains/native/unreal.js +96 -18
  66. package/dist/guest/toolchains/plan.js +12 -1
  67. package/dist/guest/toolchains/tools/vscode-profile.js +2 -0
  68. package/dist/host/accounts/kinds/epic.js +11 -5
  69. package/dist/host/accounts/source.d.ts +2 -0
  70. package/dist/host/accounts/source.js +1 -0
  71. package/dist/host/pc-installs/discover.d.ts +2 -0
  72. package/dist/host/pc-installs/discover.js +25 -5
  73. package/dist/host/pc-installs/epic.d.ts +3 -0
  74. package/dist/host/pc-installs/epic.js +33 -2
  75. package/dist/host/pc-installs/source.d.ts +1 -1
  76. package/dist/host/pc-installs/unreal-vault.d.ts +11 -0
  77. package/dist/host/pc-installs/unreal-vault.js +78 -0
  78. package/dist/host/pc-installs/unreal.d.ts +36 -0
  79. package/dist/host/pc-installs/unreal.js +145 -2
  80. package/dist/host/profile/claude-profile.d.ts +8 -0
  81. package/dist/host/profile/claude-profile.js +43 -4
  82. package/dist/host/profile/index.d.ts +1 -1
  83. package/dist/host/profile/index.js +1 -1
  84. package/dist/runtime/agent.js +81 -7
  85. package/dist/runtime/daemon.js +2645 -1287
  86. package/dist/runtime/db.js +81 -7
  87. package/dist/runtime/main.js +2901 -1722
  88. package/dist/runtime/netproxy.js +81 -7
  89. package/dist/runtime/tool.js +81 -7
  90. package/dist/runtime/view.js +81 -7
  91. package/dist/selftest/agent-paths.test.js +1 -0
  92. package/dist/selftest/agents-web.test.js +12 -0
  93. package/dist/selftest/engines.test.js +602 -3
  94. package/dist/selftest/heavy-load.test.js +3 -1
  95. package/dist/selftest/hostile-input.test.js +11 -1
  96. package/dist/selftest/index.js +2 -0
  97. package/dist/selftest/menu-screens.js +3 -1
  98. package/dist/selftest/mixamo.test.d.ts +2 -0
  99. package/dist/selftest/mixamo.test.js +60 -0
  100. package/dist/selftest/secrets.test.js +36 -1
  101. package/dist/selftest/support/engine-golden.js +3 -0
  102. package/dist/selftest/support/fixtures.d.ts +3 -1
  103. package/dist/selftest/support/unreal-sample.d.ts +2 -0
  104. package/dist/selftest/support/unreal-sample.js +83 -0
  105. package/dist/selftest/toolchains.test.js +32 -2
  106. package/dist/shared/accounts/index.d.ts +1 -1
  107. package/dist/shared/accounts/index.js +1 -1
  108. package/dist/shared/accounts/kinds/epic.d.ts +6 -1
  109. package/dist/shared/accounts/kinds/epic.js +11 -1
  110. package/dist/shared/apps/associations.d.ts +7 -0
  111. package/dist/shared/apps/associations.js +26 -0
  112. package/dist/shared/apps/catalogue-agents-web.d.ts +4 -1
  113. package/dist/shared/apps/catalogue-agents-web.js +5 -2
  114. package/dist/shared/apps/catalogue-gamedev.js +71 -1
  115. package/dist/shared/apps/index.d.ts +2 -0
  116. package/dist/shared/apps/index.js +2 -0
  117. package/dist/shared/apps/preview.d.ts +2 -0
  118. package/dist/shared/apps/preview.js +10 -0
  119. package/dist/shared/browser-policy.d.ts +14 -0
  120. package/dist/shared/browser-policy.js +22 -0
  121. package/dist/shared/io/index.d.ts +1 -0
  122. package/dist/shared/io/index.js +1 -0
  123. package/dist/shared/io/rotate-log.d.ts +2 -0
  124. package/dist/shared/io/rotate-log.js +16 -0
  125. package/dist/shared/memory/policy.d.ts +5 -0
  126. package/dist/shared/memory/policy.js +7 -0
  127. package/dist/shared/paths/pc-installs.d.ts +13 -2
  128. package/dist/shared/paths/pc-installs.js +1 -0
  129. package/dist/shared/readiness.d.ts +0 -1
  130. package/dist/shared/readiness.js +0 -1
  131. package/dist/shared/signer.d.ts +4 -0
  132. package/dist/shared/signer.js +2 -0
  133. package/dist/shared/unreal/engines.d.ts +161 -0
  134. package/dist/shared/unreal/engines.js +308 -0
  135. package/dist/shared/unreal/guide.d.ts +19 -0
  136. package/dist/shared/unreal/guide.js +105 -0
  137. package/dist/shared/unreal/index.d.ts +7 -0
  138. package/dist/shared/unreal/index.js +8 -0
  139. package/dist/shared/unreal/launcher-files.d.ts +88 -0
  140. package/dist/shared/unreal/launcher-files.js +292 -0
  141. package/dist/shared/unreal/mixamo.d.ts +5 -0
  142. package/dist/shared/unreal/mixamo.js +16 -0
  143. package/dist/shared/unreal/room.d.ts +17 -0
  144. package/dist/shared/unreal/room.js +34 -0
  145. package/dist/shared/unreal/status.d.ts +54 -0
  146. package/dist/shared/unreal/status.js +57 -0
  147. package/dist/shared/unreal/vault.d.ts +21 -0
  148. package/dist/shared/unreal/vault.js +67 -0
  149. package/dist/shared/unreal/vs-check.d.ts +39 -0
  150. package/dist/shared/unreal/vs-check.js +79 -0
  151. package/dist/web/index.html +1 -1
  152. package/dist/web/{main-CCILLX2M.js → main-FNE6XEHA.js} +84 -84
  153. package/package.json +1 -1
  154. package/sandbox/csharp/VsSetup.cs +210 -0
  155. package/sandbox/scripts/get-vs-setup.ps1 +93 -0
  156. package/sandbox/scripts/set-registry-values.ps1 +35 -0
  157. package/schemas/config.schema.json +35 -2
  158. package/schemas/sandbox-config.schema.json +8 -1
@@ -0,0 +1,78 @@
1
+ // The Epic Games Launcher's vault cache of this PC (pcInstalls.unrealAssets): the Fab and Marketplace packs you
2
+ // downloaded, mapped read-only at C:\PcInstalls\unreal\Vault (Vault2 ... for more folders), so what you download on the
3
+ // PC is in the sandbox at once. Its folders come from the launcher's settings (shared/unreal/vault.ts), else the
4
+ // default one; only folders that exist are mapped.
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { epicSettingsFiles, MAX_EPIC_INI_BYTES } from '../../shared/accounts/index.js';
8
+ import { decodeText, readBoundedFile } from '../../shared/io/index.js';
9
+ import { defaultVaultDir, vaultDirsFromIni } from '../../shared/unreal/index.js';
10
+ const MAX_VAULTS = 4;
11
+ /** The vault cache folders of this PC: the ones its launcher settings name, else the default. */
12
+ export function vaultDirs(pc, isDir) {
13
+ const named = pc.localAppData
14
+ ? epicSettingsFiles(pc.localAppData).flatMap((file) => {
15
+ const bytes = readBoundedFile(file, MAX_EPIC_INI_BYTES);
16
+ return bytes ? vaultDirsFromIni(decodeText(bytes).text) : [];
17
+ })
18
+ : [];
19
+ const all = [...named, defaultVaultDir(pc.programData)];
20
+ return all
21
+ .filter((d, i) => all.findIndex((o) => o.toLowerCase() === d.toLowerCase()) === i)
22
+ .filter(isDir)
23
+ .slice(0, MAX_VAULTS);
24
+ }
25
+ /** The vault folders as installs to map (read-only). */
26
+ export function vaultInstalls(dirs) {
27
+ return dirs.map((dir, i) => ({
28
+ kind: 'unreal-vault',
29
+ id: i ? 'Vault' + (i + 1) : 'Vault',
30
+ name: 'Epic Games Launcher vault cache' + (i ? ' ' + (i + 1) : ''),
31
+ host: dir,
32
+ guest: '',
33
+ }));
34
+ }
35
+ const isDir = (dir) => {
36
+ try {
37
+ return fs.statSync(dir).isDirectory();
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ };
43
+ /** This PC's disk for vaultPacks (bounded lists). */
44
+ export const PC_VAULT_FS = {
45
+ exists: (file) => fs.existsSync(file),
46
+ list: (dir) => {
47
+ try {
48
+ return fs.readdirSync(dir).slice(0, 5000);
49
+ }
50
+ catch {
51
+ return []; // Not a folder: nothing in it.
52
+ }
53
+ },
54
+ size: (file) => {
55
+ try {
56
+ const s = fs.lstatSync(file);
57
+ return s.isFile() ? s.size : null;
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ },
63
+ isDir: (dir) => {
64
+ try {
65
+ return fs.lstatSync(dir).isDirectory();
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ },
71
+ };
72
+ export const unrealVault = {
73
+ kind: 'unreal-vault',
74
+ setting: 'unrealAssets',
75
+ find(_wanted, pc) {
76
+ return vaultInstalls(vaultDirs(pc, isDir)).map((v) => ({ ...v, host: path.win32.normalize(v.host) }));
77
+ },
78
+ };
@@ -1,2 +1,38 @@
1
+ import type { PcInstall } from '../../shared/paths/index.js';
2
+ import type { PcEnv } from '../accounts/index.js';
1
3
  import type { PcInstallSource } from './source.js';
4
+ /** At most this many engines of this PC are mapped. */
5
+ export declare const MAX_PC_ENGINES = 8;
6
+ /** What discovery reads on this PC (the self-test passes fixtures). */
7
+ export interface UnrealPcFiles {
8
+ /** LauncherInstalled.dat's text, or null. */
9
+ readonly launcherInstalled: string | null;
10
+ /** `reg query HKCU\Software\Epic Games\Unreal Engine\Builds` output ('' when the key is missing). */
11
+ readonly builds: string;
12
+ /** The launcher's default engines folder (<Program Files>\Epic Games). */
13
+ readonly defaultRoot: string;
14
+ readonly list: (dir: string) => string[];
15
+ readonly read: (file: string, max: number) => Buffer | null;
16
+ }
17
+ /** The values of `reg query <key>` output: the name and string of each REG_SZ / REG_EXPAND_SZ line. */
18
+ export declare function regValues(text: string): {
19
+ name: string;
20
+ value: string;
21
+ }[];
22
+ /** The engines of this PC that `wanted` maps, each folder once, at most MAX_PC_ENGINES. */
23
+ export declare function findUnrealInstalls(files: UnrealPcFiles, wanted: true | readonly (string | number)[]): PcInstall[];
24
+ /** A source build of this PC's Builds key. */
25
+ export interface PcSourceBuild {
26
+ readonly buildId: string;
27
+ readonly version: string;
28
+ readonly host: string;
29
+ }
30
+ /** Source builds this PC registered (the Builds key). They are not mapped by themselves: UBT writes inside them. */
31
+ export declare function pcSourceBuilds(files: Pick<UnrealPcFiles, 'read' | 'builds'>): PcSourceBuild[];
32
+ /** The engine folders you registered (pcInstalls.unrealEngineFolders), mapped writable. A folder that is a source
33
+ * build of this PC keeps the PC's build id. */
34
+ export declare function findEngineFolders(files: Pick<UnrealPcFiles, 'read' | 'builds'>, folders: readonly (string | number)[]): PcInstall[];
35
+ /** This PC's files (reg.exe through system32, bounded reads). */
36
+ export declare function unrealPcFiles(pc: PcEnv): UnrealPcFiles;
2
37
  export declare const unrealInstalls: PcInstallSource;
38
+ export declare const unrealFolders: PcInstallSource;
@@ -1,7 +1,150 @@
1
+ // Unreal Engine versions of this PC (pcInstalls.unrealEngines): the launcher's LauncherInstalled.dat (engines it
2
+ // installed, with its own ids) and the launcher's default folders (<Program Files>\Epic Games\UE_*, for an install
3
+ // whose LauncherInstalled.dat is gone). Each must hold Engine\Build\Build.version; it is mapped read-only at
4
+ // C:\PcInstalls\unreal\UE_5.4. Source builds (HKCU\Software\Epic Games\Unreal Engine\Builds) are NOT mapped by
5
+ // themselves, as UBT writes inside an engine. pcInstalls.unrealEngineFolders: folders you registered, mapped writable
6
+ // (dir-<name>; a source build of this PC keeps its build id). Every file is read bounded.
7
+ import { spawnSync } from 'node:child_process';
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+ import { readBoundedFile } from '../../shared/io/index.js';
11
+ import { SYSTEM } from '../../shared/system.js';
12
+ import { BUILDS_KEY, launcherInstallations, launcherInstalledFile, MAX_LAUNCHER_FILE_BYTES, parseBuildVersion, versionText, } from '../../shared/unreal/index.js';
13
+ const win = path.win32;
14
+ /** At most this many engines of this PC are mapped. */
15
+ export const MAX_PC_ENGINES = 8;
16
+ const APP_NAME = /^UE_(\d{1,2}\.\d{1,2})$/;
17
+ /** The values of `reg query <key>` output: the name and string of each REG_SZ / REG_EXPAND_SZ line. */
18
+ export function regValues(text) {
19
+ return text
20
+ .split(/\r?\n/)
21
+ .slice(0, 500)
22
+ .flatMap((line) => {
23
+ const m = /^\s{2,}(\S(?:.{0,200}?\S)?)\s{2,}REG_(?:EXPAND_)?SZ\s{2,}(.{1,1000})$/.exec(line);
24
+ return m ? [{ name: m[1], value: m[2].trim() }] : [];
25
+ });
26
+ }
27
+ const versionOf = (dir, files) => {
28
+ const bytes = files.read(win.join(dir, 'Engine', 'Build', 'Build.version'), 64 * 1024);
29
+ return bytes ? parseBuildVersion(bytes.toString('utf8')) : null;
30
+ };
31
+ const isWanted = (wanted, v) => wanted === true || wanted.map((w) => String(w).replace(/^UE_/i, '')).includes(v.major + '.' + v.minor);
32
+ /** The engines of this PC that `wanted` maps, each folder once, at most MAX_PC_ENGINES. */
33
+ export function findUnrealInstalls(files, wanted) {
34
+ const found = [];
35
+ const add = (install) => {
36
+ const key = win.resolve(install.host).toLowerCase();
37
+ if (found.length >= MAX_PC_ENGINES)
38
+ return;
39
+ if (found.some((f) => f.id === install.id || win.resolve(f.host).toLowerCase() === key))
40
+ return;
41
+ found.push(install);
42
+ };
43
+ const installed = (dir, ids) => {
44
+ const v = versionOf(dir, files);
45
+ if (!v || !isWanted(wanted, v))
46
+ return;
47
+ add({
48
+ kind: 'unreal',
49
+ id: 'UE_' + v.major + '.' + v.minor,
50
+ name: 'Unreal Engine ' + versionText(v),
51
+ version: versionText(v),
52
+ host: win.normalize(dir),
53
+ guest: '',
54
+ ...(ids ? { launcher: ids } : {}),
55
+ });
56
+ };
57
+ for (const i of files.launcherInstalled ? launcherInstallations(files.launcherInstalled) : [])
58
+ if (APP_NAME.test(i.appName))
59
+ installed(i.installLocation, i.ids ?? undefined);
60
+ for (const name of files
61
+ .list(files.defaultRoot)
62
+ .filter((n) => APP_NAME.test(n))
63
+ .sort())
64
+ installed(win.join(files.defaultRoot, name));
65
+ return found;
66
+ }
67
+ /** Source builds this PC registered (the Builds key). They are not mapped by themselves: UBT writes inside them. */
68
+ export function pcSourceBuilds(files) {
69
+ const found = [];
70
+ for (const { name, value } of regValues(files.builds)) {
71
+ const dir = value.replace(/\//g, '\\');
72
+ if (!win.isAbsolute(dir) || found.length >= MAX_PC_ENGINES)
73
+ continue;
74
+ const v = versionOf(dir, files);
75
+ if (v)
76
+ found.push({ buildId: name.slice(0, 64), version: versionText(v), host: win.normalize(dir) });
77
+ }
78
+ return found;
79
+ }
80
+ /** The engine folders you registered (pcInstalls.unrealEngineFolders), mapped writable. A folder that is a source
81
+ * build of this PC keeps the PC's build id. */
82
+ export function findEngineFolders(files, folders) {
83
+ const found = [];
84
+ const sources = pcSourceBuilds(files);
85
+ for (const raw of folders.map(String)) {
86
+ const dir = win.normalize(raw);
87
+ if (!win.isAbsolute(dir) || found.some((f) => f.host.toLowerCase() === dir.toLowerCase()))
88
+ continue;
89
+ const v = versionOf(dir, files);
90
+ if (!v)
91
+ continue;
92
+ const base = win
93
+ .basename(dir)
94
+ .replace(/[^A-Za-z0-9._-]/g, '')
95
+ .replace(/^\.+/, '')
96
+ .slice(0, 40) || 'engine';
97
+ const id = 'dir-' + base;
98
+ const source = sources.find((b) => b.host.toLowerCase() === dir.toLowerCase());
99
+ found.push({
100
+ kind: 'unreal',
101
+ id: found.some((f) => f.id === id) ? id + '-' + (found.length + 1) : id,
102
+ name: 'Unreal Engine ' + versionText(v) + ' (' + win.basename(dir).slice(0, 60) + ')',
103
+ version: versionText(v),
104
+ host: dir,
105
+ guest: '',
106
+ writable: true,
107
+ ...(source ? { buildId: source.buildId } : {}),
108
+ });
109
+ }
110
+ return found;
111
+ }
112
+ function listDir(dir) {
113
+ try {
114
+ return fs.readdirSync(dir).slice(0, 500);
115
+ }
116
+ catch {
117
+ return []; // Not there: nothing in it.
118
+ }
119
+ }
120
+ /** This PC's files (reg.exe through system32, bounded reads). */
121
+ export function unrealPcFiles(pc) {
122
+ const query = spawnSync(SYSTEM.reg(), ['query', 'HKCU\\' + BUILDS_KEY], {
123
+ encoding: 'utf8',
124
+ windowsHide: true,
125
+ timeout: 15_000,
126
+ maxBuffer: 256 * 1024,
127
+ });
128
+ const dat = readBoundedFile(launcherInstalledFile(pc.programData), MAX_LAUNCHER_FILE_BYTES);
129
+ return {
130
+ launcherInstalled: dat ? dat.toString('utf8') : null,
131
+ builds: query.status === 0 ? String(query.stdout ?? '') : '',
132
+ defaultRoot: win.join(pc.programFiles ?? 'C:\\Program Files', 'Epic Games'),
133
+ list: listDir,
134
+ read: readBoundedFile,
135
+ };
136
+ }
1
137
  export const unrealInstalls = {
2
138
  kind: 'unreal',
3
139
  setting: 'unrealEngines',
4
- find(_wanted, _pc) {
5
- return [];
140
+ find(wanted, pc) {
141
+ return findUnrealInstalls(unrealPcFiles(pc), wanted);
142
+ },
143
+ };
144
+ export const unrealFolders = {
145
+ kind: 'unreal',
146
+ setting: 'unrealEngineFolders',
147
+ find(wanted, pc) {
148
+ return wanted === true ? [] : findEngineFolders(unrealPcFiles(pc), wanted);
6
149
  },
7
150
  };
@@ -1,5 +1,13 @@
1
1
  import type { ClaudeProfileConfig, ProfileResult } from '../../core/types.js';
2
2
  import { type SecretsConfig } from './secrets.js';
3
+ /**
4
+ * Which parts of your plugins folder are staged, as a filter over full paths. Everything Claude needs to load a plugin
5
+ * (installed_plugins.json, marketplaces, the plugin cache) is kept; when that is over the size cap, git history
6
+ * (`.git`) and then `node_modules` are left out, and the guest's plugins restore what they need on first use.
7
+ */
8
+ export declare function pluginFilter(source: string, level: 0 | 1 | 2): (full: string) => boolean;
9
+ /** The least trimmed level at which the plugins folder fits the cap, or null when even the most trimmed does not. */
10
+ export declare function pluginTrimLevel(source: string): 0 | 1 | 2 | null;
3
11
  export declare function hostClaudeDir(settings: ClaudeProfileConfig): string;
4
12
  export declare function hostGlobalStateFile(settings: ClaudeProfileConfig): string;
5
13
  /**
@@ -5,17 +5,54 @@ import { dirExists, ensureDir, fileExists, homeDir, isLink, readJson, writeJson
5
5
  import { stripMcpServers, stripSettings } from './secrets.js';
6
6
  const MAX_BYTES = 250 * 1024 * 1024;
7
7
  /** Real files only: a link inside ~/.claude (even a loop or a dead one) is neither followed nor counted nor copied. */
8
- function directorySize(dir) {
8
+ function directorySize(dir, keep = () => true) {
9
9
  let total = 0;
10
10
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
11
11
  const full = path.join(dir, entry.name);
12
+ if (!keep(full))
13
+ continue;
12
14
  if (entry.isDirectory())
13
- total += directorySize(full);
15
+ total += directorySize(full, keep);
14
16
  else if (entry.isFile())
15
17
  total += fs.statSync(full).size;
16
18
  }
17
19
  return total;
18
20
  }
21
+ /** Plugin leftovers Claude never reads: interrupted clones (temp_*) and marketplace backups. */
22
+ function isPluginJunk(parts) {
23
+ const top = parts[0]?.toLowerCase();
24
+ const name = parts[1]?.toLowerCase() ?? '';
25
+ return (parts.length >= 2 &&
26
+ (top === 'cache' || top === 'marketplaces') &&
27
+ (name.startsWith('temp_') || name.endsWith('.backup') || name.endsWith('.staging')));
28
+ }
29
+ /**
30
+ * Which parts of your plugins folder are staged, as a filter over full paths. Everything Claude needs to load a plugin
31
+ * (installed_plugins.json, marketplaces, the plugin cache) is kept; when that is over the size cap, git history
32
+ * (`.git`) and then `node_modules` are left out, and the guest's plugins restore what they need on first use.
33
+ */
34
+ export function pluginFilter(source, level) {
35
+ return (full) => {
36
+ if (full === source)
37
+ return true;
38
+ const parts = path.relative(source, full).split(path.sep);
39
+ if (isPluginJunk(parts))
40
+ return false;
41
+ if (level >= 1 && parts.includes('.git'))
42
+ return false;
43
+ if (level >= 2 && parts.includes('node_modules'))
44
+ return false;
45
+ return true;
46
+ };
47
+ }
48
+ /** The least trimmed level at which the plugins folder fits the cap, or null when even the most trimmed does not. */
49
+ export function pluginTrimLevel(source) {
50
+ for (const level of [0, 1, 2]) {
51
+ if (directorySize(source, pluginFilter(source, level)) <= MAX_BYTES)
52
+ return level;
53
+ }
54
+ return null;
55
+ }
19
56
  function validJson(file) {
20
57
  try {
21
58
  JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''));
@@ -84,14 +121,16 @@ export function stageClaudeProfile(settings, launchPath, workspaceHost, secrets)
84
121
  fs.copyFileSync(source, destination);
85
122
  }
86
123
  else if (dirExists(source)) {
87
- if (directorySize(source) > MAX_BYTES) {
124
+ const level = name === 'plugins' ? pluginTrimLevel(source) : directorySize(source) > MAX_BYTES ? null : 0;
125
+ if (level === null) {
88
126
  result.skipped.push(name + ' (over 250 MB)');
89
127
  continue;
90
128
  }
129
+ const keep = name === 'plugins' ? pluginFilter(source, level) : () => true;
91
130
  fs.cpSync(source, destination, {
92
131
  recursive: true,
93
132
  force: true,
94
- filter: (from) => from === source || !isLink(from),
133
+ filter: (from) => from === source || (!isLink(from) && keep(from)),
95
134
  });
96
135
  }
97
136
  else {
@@ -1,6 +1,6 @@
1
1
  export { isSecretName, isSecretValue, mayPass, stripMcpServers, stripSettings, stripVariables, type SecretsConfig, } from './secrets.js';
2
2
  export { findSecretFiles } from './secret-files.js';
3
- export { hostClaudeDir, hostGlobalStateFile, sandboxGlobalState, stageClaudeProfile } from './claude-profile.js';
3
+ export { hostClaudeDir, hostGlobalStateFile, pluginFilter, pluginTrimLevel, sandboxGlobalState, stageClaudeProfile, } from './claude-profile.js';
4
4
  export { capturePersonalization } from './personalize.js';
5
5
  export { toRegFile, type RegValue } from './reg-file.js';
6
6
  export { AccountStore, accountsDir, type Account, type AccountMeta, type AccountRow } from './accounts.js';
@@ -1,6 +1,6 @@
1
1
  export { isSecretName, isSecretValue, mayPass, stripMcpServers, stripSettings, stripVariables, } from './secrets.js';
2
2
  export { findSecretFiles } from './secret-files.js';
3
- export { hostClaudeDir, hostGlobalStateFile, sandboxGlobalState, stageClaudeProfile } from './claude-profile.js';
3
+ export { hostClaudeDir, hostGlobalStateFile, pluginFilter, pluginTrimLevel, sandboxGlobalState, stageClaudeProfile, } from './claude-profile.js';
4
4
  export { capturePersonalization } from './personalize.js';
5
5
  export { toRegFile } from './reg-file.js';
6
6
  export { AccountStore, accountsDir } from './accounts.js';
@@ -25650,7 +25650,7 @@ var ToolchainsSchema = external_exports.strictObject({
25650
25650
  `"auto" (default): on the sandbox's own disk when C: has 25 GB free at boot, else "separate" (builds then write through the mapped workspace, which can overload this PC's disk). "shared": cargo's default target\\ (shared with builds on this PC, so builds on either side invalidate the other), "separate": <workspace>\\target\\sandbox (kept, no cross-talk), "local": on the sandbox's own disk (fastest, lost every boot), or a guest path.`
25651
25651
  ),
25652
25652
  linker: external_exports.enum(["lld", "gnu"]).describe(
25653
- `GNU host: "lld" links with Rust's own lld through MinGW's gcc (much less memory than GNU ld), "gnu" with GNU ld.`
25653
+ `"lld" (default) links with Rust's own lld: through MinGW's gcc on the GNU host (much less memory than GNU ld), and instead of link.exe on the MSVC host (link.exe writes executables into mapped folders as all zeros: os error 1392). "gnu" keeps GNU ld / link.exe.`
25654
25654
  ),
25655
25655
  linkSlots: external_exports.union([external_exports.literal("auto"), external_exports.number().int().min(1).max(32)]).describe(
25656
25656
  'GNU host: how many programs may link at the same time in the whole sandbox (other links wait). "auto" = one per 6 GB of sandbox memory, 1 to 4 (the memory governor slows links down too when memory runs short).'
@@ -25908,7 +25908,7 @@ var CHROME_DEVTOOLS_MCP_VERSION = "1.12.0";
25908
25908
  var BROWSER_USE_VERSION = "0.13.14";
25909
25909
  var PERPLEXITY_COMPUTER_MCP_URL = "https://www.perplexity.ai/rest/computer/mcp";
25910
25910
  var COMET_SIGNER = {
25911
- subject: ['^CN="?PERPLEXITY AI, INC\\.?"?(,|$)', '(^|, )O="?PERPLEXITY AI, INC\\.?"?(,|$)'],
25911
+ subject: ['(^|, )CN="?PERPLEXITY AI, INC\\.?"?(,|$)', '(^|, )O="?PERPLEXITY AI, INC\\.?"?(,|$)'],
25912
25912
  issuer: ["^CN=GlobalSign GCC R45 EV CodeSigning CA 2020(,|$)"]
25913
25913
  };
25914
25914
  var EDGE_EXE = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
@@ -26010,7 +26010,77 @@ var AGENTS_WEB_APPS = [
26010
26010
  ];
26011
26011
 
26012
26012
  // src/shared/apps/catalogue-gamedev.ts
26013
- var GAMEDEV_APPS = [];
26013
+ var GAMEDEV_APPS = [
26014
+ {
26015
+ id: "krita",
26016
+ name: "Krita",
26017
+ category: "gamedev",
26018
+ about: "Painting and texture editor (PSD, layers, brushes), checked against the SHA-256 download.kde.org publishes.",
26019
+ size: "450 MB",
26020
+ source: { resolve: "krita" },
26021
+ exe: "krita.exe",
26022
+ needs: ["mesa"]
26023
+ },
26024
+ {
26025
+ id: "inkscape",
26026
+ name: "Inkscape",
26027
+ category: "gamedev",
26028
+ about: "Vector graphics for UI, icons and 2D art (SVG), checked against the SHA-256 inkscape.org publishes.",
26029
+ size: "400 MB",
26030
+ source: { resolve: "inkscape" },
26031
+ exe: "inkscape.exe",
26032
+ needs: ["7zip"]
26033
+ },
26034
+ {
26035
+ id: "audacity",
26036
+ name: "Audacity",
26037
+ category: "gamedev",
26038
+ about: "Records and edits sound effects and music (WAV, OGG, FLAC).",
26039
+ size: "80 MB",
26040
+ source: { github: "audacity/audacity", asset: "^audacity-win-[\\d.]+-(?:64bit\\.zip|x86_64\\.7z)$" },
26041
+ exe: "Audacity.exe",
26042
+ needs: ["7zip"]
26043
+ },
26044
+ {
26045
+ id: "renderdoc",
26046
+ name: "RenderDoc",
26047
+ category: "gamedev",
26048
+ about: "Captures a frame of a game (Unreal's -d3d12/-vulkan builds) to inspect every draw call, texture and shader. Needs the sandbox's GPU (vGPU) for live captures.",
26049
+ size: "70 MB",
26050
+ source: { resolve: "renderdoc" },
26051
+ exe: "qrenderdoc.exe"
26052
+ },
26053
+ {
26054
+ id: "f3d",
26055
+ name: "F3D",
26056
+ category: "gamedev",
26057
+ about: "Fast 3D viewer (FBX, glTF/GLB, OBJ, STL, PLY, 3DS, USD); renders still previews from the command line.",
26058
+ size: "100 MB",
26059
+ source: { github: "f3d-app/f3d", asset: "^F3D-[\\d.]+-Windows-x86_64\\.zip$" },
26060
+ exe: "f3d.exe",
26061
+ needs: ["mesa"]
26062
+ },
26063
+ {
26064
+ id: "imagemagick",
26065
+ name: "ImageMagick",
26066
+ category: "gamedev",
26067
+ about: "Converts and thumbnails textures (TGA, PSD, DDS, EXR, HDR) and builds contact sheets (magick montage).",
26068
+ size: "150 MB",
26069
+ source: { github: "ImageMagick/ImageMagick", asset: "^ImageMagick-[\\d.]+-\\d+-portable-Q16-HDRI-x64\\.7z$" },
26070
+ exe: "magick.exe",
26071
+ cli: true,
26072
+ needs: ["7zip"]
26073
+ },
26074
+ {
26075
+ id: "nomacs",
26076
+ name: "nomacs",
26077
+ category: "gamedev",
26078
+ about: "Image viewer for textures (TGA, PSD, DDS, EXR).",
26079
+ size: "60 MB",
26080
+ source: { github: "nomacs/nomacs", asset: "^nomacs-portable-win\\.zip$" },
26081
+ exe: "nomacs.exe"
26082
+ }
26083
+ ];
26014
26084
 
26015
26085
  // src/shared/apps/catalogue-mobile.ts
26016
26086
  var MOBILE_APPS = [];
@@ -27239,10 +27309,14 @@ var PcInstallsSchema = external_exports.strictObject({
27239
27309
  "Epic Games Launcher games (by their app name)",
27240
27310
  '["Fortnite"]'
27241
27311
  ),
27242
- unrealEngines: pick2(
27243
- external_exports.string().regex(/^(UE_)?\d+\.\d+$/),
27244
- "Unreal Engine versions installed with the Epic Games Launcher",
27245
- '["5.4"]'
27312
+ unrealEngines: external_exports.union([external_exports.literal("auto"), external_exports.boolean(), external_exports.array(external_exports.string().regex(/^(UE_)?\d+\.\d+$/))]).describe(
27313
+ 'Unreal Engine versions installed on this PC (with the Epic Games Launcher, or source builds it lists), read-only in the sandbox under C:\\PcInstalls\\unreal and registered there, so projects build with them without a download. "auto" = all of them whenever the sandbox is set up for Unreal, true = always, false = never, or a list (e.g. ["5.4"]).'
27314
+ ),
27315
+ unrealEngineFolders: external_exports.array(external_exports.string().min(3).max(260)).max(8).describe(
27316
+ "Unreal Engine folders of this PC to use in the sandbox, writable (a source build, or a copy you build plugins into): each must hold Engine\\Build\\Build.version. Mapped under C:\\PcInstalls\\unreal and registered there."
27317
+ ),
27318
+ unrealAssets: external_exports.union([external_exports.literal("auto"), external_exports.boolean()]).describe(
27319
+ `The Epic Games Launcher's vault cache of this PC (the Fab and Marketplace packs you downloaded), read-only in the sandbox at C:\\PcInstalls\\unreal\\Vault: what you download on your PC is there for the agents at once. "auto" = whenever the sandbox is set up for Unreal, true = always, false = never.`
27246
27320
  ),
27247
27321
  unityEditors: pick2(
27248
27322
  external_exports.string().regex(/^\d+\.\d+\.\d+[abfp]\d+$/),