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
@@ -8,7 +8,7 @@ import { assertSafeMapping } from '../core/safe-paths.js';
8
8
  import { safeText } from '../core/util/index.js';
9
9
  import { isSafeFileName } from '../shared/paths/index.js';
10
10
  import { removeRefusal, safeRemove } from '../shared/io/index.js';
11
- import { writeJsonAtomic } from '../shared/io/index.js';
11
+ import { rotateLog, writeJsonAtomic } from '../shared/io/index.js';
12
12
  export const hostileInputSuite = {
13
13
  name: 'hostile-input',
14
14
  title: 'file names, deletes, printed text, mappings and state files from an untrusted sandbox',
@@ -63,5 +63,15 @@ export const hostileInputSuite = {
63
63
  fs.writeFileSync(path.join(repoDir, '.sandbox-config.json'), '{"readOnly": true}' + ' '.repeat(1100 * 1024));
64
64
  await t.throws(() => f.run({ cwd: repoDir, trustConfig: true }), 'over 1 MB', 'a .sandbox-config.json over 1 MB is refused before it is parsed');
65
65
  fs.rmSync(path.join(repoDir, '.sandbox-config.json'));
66
+ t.section('a log that grows for ever (Headroom appends to one on the data disk)');
67
+ const logFile = t.at('rotate', 'proxy.log');
68
+ fs.mkdirSync(path.dirname(logFile), { recursive: true });
69
+ t.check(!rotateLog(logFile, 10), 'no log yet: nothing to rotate');
70
+ fs.writeFileSync(logFile, 'x'.repeat(10));
71
+ t.check(!rotateLog(logFile, 10) && fs.existsSync(logFile), 'a log at the limit stays');
72
+ fs.writeFileSync(logFile + '.1', 'old');
73
+ fs.writeFileSync(logFile, 'y'.repeat(11));
74
+ t.check(rotateLog(logFile, 10), 'a log over the limit is rotated');
75
+ t.check(!fs.existsSync(logFile) && fs.readFileSync(logFile + '.1', 'utf8') === 'y'.repeat(11), 'it replaces the older copy');
66
76
  },
67
77
  };
@@ -16,6 +16,7 @@ import { persistedInstallsSuite } from './persisted-installs.test.js';
16
16
  import { pcInstallsSuite } from './pc-installs.test.js';
17
17
  import { steamSuite } from './steam.test.js';
18
18
  import { enginesSuite } from './engines.test.js';
19
+ import { mixamoSuite } from './mixamo.test.js';
19
20
  import { unitySuite } from './unity.test.js';
20
21
  import { fivemSuite } from './fivem.test.js';
21
22
  import { androidSuite } from './android.test.js';
@@ -139,6 +140,7 @@ export const SUITES = [
139
140
  persistedInstallsSuite,
140
141
  steamSuite,
141
142
  enginesSuite,
143
+ mixamoSuite,
142
144
  unitySuite,
143
145
  fivemSuite,
144
146
  androidSuite,
@@ -10,6 +10,7 @@ import { appsOptions, stageTicks } from '../cli/menu/pages/apps-page.js';
10
10
  import { maintenanceOptions } from '../cli/menu/pages/maintenance-page.js';
11
11
  import { sandboxOptions } from '../cli/menu/pages/sandbox-page.js';
12
12
  import { sessionsOptions } from '../cli/menu/pages/sessions-page.js';
13
+ import { UNREAL_SAMPLE } from './support/unreal-sample.js';
13
14
  import { settingsOf, settingsOptions } from '../cli/menu/pages/settings-page.js';
14
15
  import { CATEGORIES } from '../app/features/settings/index.js';
15
16
  import { agentsOptions } from '../cli/menu/pages/agents-page.js';
@@ -199,6 +200,7 @@ export function renderScreens(dir) {
199
200
  { name: 'redis', running: false, port: 6379, title: 'Redis' },
200
201
  ],
201
202
  defaultAgent: 'claude',
203
+ unreal: UNREAL_SAMPLE,
202
204
  };
203
205
  const stopped = {
204
206
  ...sandbox,
@@ -399,7 +401,7 @@ export function renderScreens(dir) {
399
401
  out.push(block('1 Agents · Start', draw(0, { ...agentsOptions(agentsModel, API_FOLDER), startCategory: 'start' }, size, status), size), block('1 Agents · Options (navigation focused)', draw(0, { ...agentsOptions(agentsModel, API_FOLDER), startCategory: 'options' }, size, status, 0, true), size), block('1 Agents · On this PC', draw(0, { ...agentsOptions(agentsModel, API_FOLDER), startCategory: 'host' }, size, status), size), block('2 Sessions', draw(1, sessionsOptions({ sandboxRunning: true, sessions: SESSIONS, defaultAgent: 'claude' }), size, status), size), block('2 Sessions (no sandbox)', draw(1, sessionsOptions({ sandboxRunning: false, sessions: [], defaultAgent: 'claude' }), size), size));
400
402
  for (const category of ['status', 'databases'])
401
403
  out.push(block('3 Sandbox (stopped) · ' + category, draw(2, { ...sandboxOptions(stopped), startCategory: category }, size), size));
402
- for (const category of ['status', 'projects', 'databases'])
404
+ for (const category of ['status', 'projects', 'databases', 'unreal'])
403
405
  out.push(block('3 Sandbox · ' + category, draw(2, { ...sandboxOptions(sandbox), startCategory: category }, size, status), size));
404
406
  out.push(block('4 Apps (1 unsaved)', draw(3, appsOptions(user, appsData, appsDraft, 'user'), size, status, appsDraft.count()), size));
405
407
  for (const category of CATEGORIES)
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './support/suite.js';
2
+ export declare const mixamoSuite: Suite;
@@ -0,0 +1,60 @@
1
+ // Mixamo: the browser policy table (downloads folder, no GPU) and the guide's Mixamo lines and editor names.
2
+ import { browserPolicies } from '../shared/browser-policy.js';
3
+ import { unrealGuideLines } from '../shared/unreal/guide.js';
4
+ const DOWNLOADS = 'C:\\AgentData\\downloads';
5
+ const PROJECT = { workspace: 'C:\\P\\Game', uproject: 'C:\\P\\Game\\Game.uproject' };
6
+ function statusWith(version) {
7
+ return {
8
+ updatedAt: 'now',
9
+ engines: [
10
+ {
11
+ id: 'UE_' + version,
12
+ dir: 'C:\\E',
13
+ version,
14
+ build: 'installed',
15
+ origin: 'pc',
16
+ writable: true,
17
+ sizeBytes: null,
18
+ },
19
+ ],
20
+ projects: [{ workspace: PROJECT.workspace, engine: 'C:\\E', reason: 'test' }],
21
+ launcher: { installed: false, installLocation: null, problem: null, freeBytes: null, needBytes: 1 },
22
+ visualStudio: { version: '17.14', path: 'C:/VS', how: 'kept', check: 'ok', ok: true },
23
+ debugger: null,
24
+ ddc: { mode: 'persisted', dir: null },
25
+ vaults: [],
26
+ mcpUrl: null,
27
+ };
28
+ }
29
+ export const mixamoSuite = {
30
+ name: 'mixamo',
31
+ title: 'Mixamo: browser downloads folder and the guide lines for bringing characters into Unreal',
32
+ run: async (t) => {
33
+ t.section('browser policies');
34
+ const four = browserPolicies({ downloads: DOWNLOADS });
35
+ t.equal(four.length, 4, 'two browsers, two policies each');
36
+ t.equal(four.filter((p) => p.name === 'DefaultDownloadDirectory').map((p) => p.data), [DOWNLOADS, DOWNLOADS], 'both browsers download to the data disk');
37
+ t.equal(browserPolicies({ downloads: null }).length, 2, 'no folder: only the GPU policy');
38
+ await t.throws(() => browserPolicies({ downloads: 'C:\\a"b' }), 'not a folder', 'a quote in the folder is refused');
39
+ await t.throws(() => browserPolicies({ downloads: 'downloads' }), 'not a folder', 'a relative folder is refused');
40
+ t.section('the guide');
41
+ const on = { mixamo: { downloads: DOWNLOADS, script: null } };
42
+ const guide = unrealGuideLines(statusWith('5.4.4'), [PROJECT], on).join('\n');
43
+ t.includes(guide, 'https://www.mixamo.com', 'the Mixamo line is there');
44
+ t.includes(guide, DOWNLOADS, 'it names the downloads folder');
45
+ t.includes(guide, 'Mixamo into Unreal', 'and the import line');
46
+ t.excludes(guide, 'Mixamo import script', 'no script line without a script');
47
+ const withScript = unrealGuideLines(statusWith('5.4.4'), [PROJECT], {
48
+ mixamo: { downloads: DOWNLOADS, script: 'C:\\x\\import_mixamo.py' },
49
+ }).join('\n');
50
+ t.includes(withScript, 'Mixamo import script', 'a script adds its line');
51
+ t.excludes(unrealGuideLines(statusWith('5.4.4'), [PROJECT]).join('\n'), 'Mixamo', 'off by default');
52
+ const noEngine = { ...statusWith('5.4.4'), engines: [] };
53
+ t.excludes(unrealGuideLines(noEngine, [PROJECT], on).join('\n'), 'Mixamo', 'no engine: no Mixamo lines');
54
+ t.section('UE4 names');
55
+ const ue4 = unrealGuideLines(statusWith('4.27.2'), [PROJECT]).join('\n');
56
+ t.includes(ue4, 'UE4Editor-Cmd.exe', 'a UE4 engine uses UE4Editor-Cmd.exe');
57
+ t.excludes(ue4, 'UnrealEditor', 'and not the UE5 names');
58
+ t.includes(unrealGuideLines(statusWith('5.4.4'), [PROJECT]).join('\n'), 'UnrealEditor-Cmd.exe', 'UE5 keeps its names');
59
+ },
60
+ };
@@ -1,7 +1,9 @@
1
1
  // What reaches the sandbox from this PC's Claude files, and what is stripped first: API keys, cloud credentials, tokens
2
2
  // in MCP server definitions. Also the secret-looking files in a workspace that the launch warns about.
3
3
  import path from 'node:path';
4
- import { findSecretFiles, sandboxGlobalState, stripMcpServers, stripSettings } from '../host/profile/index.js';
4
+ import fs from 'node:fs';
5
+ import { DEFAULTS } from '../core/config/index.js';
6
+ import { findSecretFiles, pluginFilter, pluginTrimLevel, sandboxGlobalState, stageClaudeProfile, stripMcpServers, stripSettings, } from '../host/profile/index.js';
5
7
  const REMOVED = '[removed by agent-sandbox]';
6
8
  export const secretsSuite = {
7
9
  name: 'secrets',
@@ -111,5 +113,38 @@ export const secretsSuite = {
111
113
  ])
112
114
  t.write(path.join(workspace, file), 'x');
113
115
  t.equal(findSecretFiles(workspace).sort(), ['.env', 'api\\credentials.json', 'server.pem'], 'secret-looking workspace files are found for the launch warning (templates and dependencies ignored)');
116
+ t.section('plugins staged from this PC');
117
+ const hostClaude = t.at('plugins-host');
118
+ const plugins = path.join(hostClaude, 'plugins');
119
+ t.write(path.join(plugins, 'installed_plugins.json'), '{"version":2,"plugins":{}}');
120
+ t.write(path.join(plugins, 'known_marketplaces.json'), '{}');
121
+ t.write(path.join(plugins, 'cache', 'm', 'p', '1.0', 'plugin.json'), '{}');
122
+ t.write(path.join(plugins, 'cache', 'm', 'p', '1.0', 'node_modules', 'dep', 'index.js'), 'x');
123
+ t.write(path.join(plugins, 'cache', 'temp_git_1_abc', 'junk.txt'), 'x');
124
+ t.write(path.join(plugins, 'marketplaces', 'm', 'marketplace.json'), '{}');
125
+ t.write(path.join(plugins, 'marketplaces', 'm', '.git', 'HEAD'), 'ref');
126
+ t.write(path.join(plugins, 'marketplaces', 'm.backup', 'old.txt'), 'x');
127
+ t.write(path.join(plugins, 'marketplaces', 'temp_subdir_2.clone', 'junk.txt'), 'x');
128
+ t.write(path.join(hostClaude, 'settings.json'), '{"enabledPlugins":{"p@m":true}}');
129
+ const launch = t.at('plugins-launch');
130
+ const profile = { ...DEFAULTS.claudeProfile, hostDir: hostClaude, globalStateFile: '' };
131
+ const staged = stageClaudeProfile(profile, launch, 'D:/Code/app', DEFAULTS.secrets);
132
+ const out = path.join(launch, 'claude-profile', 'plugins');
133
+ t.check(staged.staged.includes('plugins'), 'plugins are staged');
134
+ t.check(fs.existsSync(path.join(out, 'cache', 'm', 'p', '1.0', 'plugin.json')) &&
135
+ fs.existsSync(path.join(out, 'marketplaces', 'm', 'marketplace.json')) &&
136
+ fs.existsSync(path.join(out, 'marketplaces', 'm', '.git', 'HEAD')) &&
137
+ fs.existsSync(path.join(out, 'cache', 'm', 'p', '1.0', 'node_modules', 'dep', 'index.js')), 'the plugin cache, marketplaces and (when they fit) their git folders and dependencies are copied');
138
+ t.check(!fs.existsSync(path.join(out, 'cache', 'temp_git_1_abc')) &&
139
+ !fs.existsSync(path.join(out, 'marketplaces', 'm.backup')) &&
140
+ !fs.existsSync(path.join(out, 'marketplaces', 'temp_subdir_2.clone')), 'interrupted clones, backups and staging folders of plugins are left out');
141
+ t.check(pluginTrimLevel(plugins) === 0, 'a plugins folder under the cap is not trimmed');
142
+ const git = path.join(plugins, 'marketplaces', 'm', '.git');
143
+ const deps = path.join(plugins, 'cache', 'm', 'p', '1.0', 'node_modules');
144
+ t.check(pluginFilter(plugins, 0)(git) &&
145
+ !pluginFilter(plugins, 1)(git) &&
146
+ pluginFilter(plugins, 1)(deps) &&
147
+ !pluginFilter(plugins, 2)(deps), 'a plugins folder over the cap loses git history first, then node_modules');
148
+ t.check(fs.readFileSync(path.join(launch, 'claude-profile', 'settings.json'), 'utf8').includes('p@m'), 'enabledPlugins reach the sandbox');
114
149
  },
115
150
  };
@@ -69,6 +69,7 @@ export const LAUNCH_FILES_GOLDEN = [
69
69
  'sandbox/csharp/SandboxTools/MemoryLog.cs',
70
70
  'sandbox/csharp/SandboxTools/PageFile.cs',
71
71
  'sandbox/csharp/SandboxTools/Program.cs',
72
+ 'sandbox/csharp/VsSetup.cs',
72
73
  'sandbox/csharp/VsWhere.cs',
73
74
  'sandbox/csharp/VtInput.cs',
74
75
  'sandbox/scripts/add-firewall-port.ps1',
@@ -76,6 +77,7 @@ export const LAUNCH_FILES_GOLDEN = [
76
77
  'sandbox/scripts/expand-archive.ps1',
77
78
  'sandbox/scripts/get-gpu-names.ps1',
78
79
  'sandbox/scripts/get-signature.ps1',
80
+ 'sandbox/scripts/get-vs-setup.ps1',
79
81
  'sandbox/scripts/install-shell-path.ps1',
80
82
  'sandbox/scripts/link-profile-dirs.ps1',
81
83
  'sandbox/scripts/new-shortcuts.ps1',
@@ -85,6 +87,7 @@ export const LAUNCH_FILES_GOLDEN = [
85
87
  'sandbox/scripts/set-desktop.ps1',
86
88
  'sandbox/scripts/set-dns-servers.ps1',
87
89
  'sandbox/scripts/set-machine-environment.ps1',
90
+ 'sandbox/scripts/set-registry-values.ps1',
88
91
  'sandbox/templates/fivem/README.txt',
89
92
  'sandbox/templates/fivem/cs/Client/Client.csproj',
90
93
  'sandbox/templates/fivem/cs/Client/ClientMain.cs',
@@ -142,7 +142,9 @@ export declare function createFixtures(t: Harness): {
142
142
  pcInstalls: {
143
143
  steamGames: boolean | number[];
144
144
  epicGames: boolean | string[];
145
- unrealEngines: boolean | string[];
145
+ unrealEngines: boolean | string[] | "auto";
146
+ unrealEngineFolders: string[];
147
+ unrealAssets: boolean | "auto";
146
148
  unityEditors: boolean | string[];
147
149
  };
148
150
  provisioning: {
@@ -0,0 +1,2 @@
1
+ import type { UnrealOverview } from '../../app/features/unreal/index.js';
2
+ export declare const UNREAL_SAMPLE: UnrealOverview;
@@ -0,0 +1,83 @@
1
+ export const UNREAL_SAMPLE = {
2
+ setUp: true,
3
+ checkedAt: '2026-11-04T12:00:00.000Z',
4
+ engines: [
5
+ {
6
+ id: 'UE_5.6',
7
+ version: '5.6.1',
8
+ dir: 'C:\\PcInstalls\\unreal\\UE_5.6',
9
+ where: 'this PC, read-only',
10
+ writable: false,
11
+ build: 'installed',
12
+ sizeBytes: null,
13
+ projects: ['ShooterGame'],
14
+ },
15
+ {
16
+ id: 'UE_5.4',
17
+ version: '5.4.4',
18
+ dir: 'C:\\AgentData\\tools\\unreal\\engines\\UE_5.4',
19
+ where: 'Epic Games Launcher, data disk',
20
+ writable: true,
21
+ build: 'installed',
22
+ sizeBytes: 48 * 1024 ** 3,
23
+ projects: [],
24
+ },
25
+ ],
26
+ projects: [{ project: 'ShooterGame', engine: 'UE_5.6', reason: 'the project asks for 5.6' }],
27
+ launcher: {
28
+ installed: true,
29
+ installLocation: 'C:\\AgentData\\tools\\unreal\\engines',
30
+ problem: null,
31
+ free: '812 GB',
32
+ },
33
+ visualStudio: {
34
+ version: '17.14.37827.9',
35
+ path: 'C:\\AgentData\\tools\\visualstudio\\persist\\Community',
36
+ how: 'kept on the data disk',
37
+ check: 'Visual Studio 17.14.37827.9, MSVC 14.44.35207, Windows SDK 10.0.22621.0',
38
+ ok: true,
39
+ },
40
+ debugger: 'C:\\AgentData\\tools\\visualstudio\\persist\\WindowsKits10\\Debuggers\\x64\\cdb.exe',
41
+ ddc: 'C:\\AgentData\\tools\\unreal\\ddc',
42
+ mcpUrl: null,
43
+ pc: {
44
+ setting: 'auto',
45
+ engines: [
46
+ {
47
+ id: 'UE_5.6',
48
+ name: 'Unreal Engine 5.6.1',
49
+ folder: 'C:\\Program Files\\Epic Games\\UE_5.6',
50
+ writable: false,
51
+ source: false,
52
+ },
53
+ {
54
+ id: 'dir-UnrealEngine',
55
+ name: 'Unreal Engine 5.7.0 (UnrealEngine)',
56
+ folder: 'D:\\Src\\UnrealEngine',
57
+ writable: true,
58
+ source: false,
59
+ },
60
+ ],
61
+ problem: null,
62
+ },
63
+ vault: {
64
+ setting: 'auto',
65
+ folders: [
66
+ {
67
+ folder: 'C:\\ProgramData\\Epic\\EpicGamesLauncher\\VaultCache',
68
+ packs: [
69
+ { name: 'CityParkEnvironment', size: '2.1 GB', kind: 'content' },
70
+ { name: 'ToolkitPlugin', size: '45 MB', kind: 'plugin' },
71
+ ],
72
+ more: 0,
73
+ },
74
+ ],
75
+ },
76
+ settings: {
77
+ enabled: 'auto',
78
+ acceptLicense: true,
79
+ persistEngines: true,
80
+ derivedDataCache: 'persisted',
81
+ visualStudio: 'full',
82
+ },
83
+ };
@@ -8,12 +8,12 @@ import { decidePwsh, pcHasPwsh, PWSH_MAX_BYTES, readPwshScripts, usesPwsh } from
8
8
  import { projectFolderLines, toolLines } from '../guest/boot/guide-lines.js';
9
9
  import { binstallAsset, CLI_TOOLS, decideCli, pwshAsset } from '../guest/toolchains/catalogue.js';
10
10
  import { missingExtensions, vscodeExtensions, vscodeSettings } from '../guest/toolchains/tools/vscode-profile.js';
11
- import { addonGuideLine, addonMcpServer, APP_CATEGORIES, APPS, appsNeedMesa, appsNeedUv, appsToInstall, CCUSAGE_VERSION, claudeManagedSettings, CONTEXT7_MCP_VERSION, exeMatcher, HEADROOM_VERSION, headroomArgs, headroomEnv, headroomUrl, PLAYWRIGHT_MCP_VERSION, RTK_VERSION, withAddonMcpServers, } from '../shared/apps/index.js';
11
+ import { addonGuideLine, addonMcpServer, APP_CATEGORIES, APPS, associationPlan, appsNeedMesa, appsNeedUv, appsToInstall, CCUSAGE_VERSION, claudeManagedSettings, CONTEXT7_MCP_VERSION, exeMatcher, HEADROOM_VERSION, headroomArgs, headroomEnv, headroomUrl, PLAYWRIGHT_MCP_VERSION, previewGuideLine, RTK_VERSION, withAddonMcpServers, } from '../shared/apps/index.js';
12
12
  import { ConfigSchema, LocalOverridesSchema } from '../core/schema/index.js';
13
13
  export const toolchainsSuite = {
14
14
  name: 'toolchains',
15
15
  title: 'version pickers, release assets, PowerShell 7 and CLI tool decisions',
16
- run: (t) => {
16
+ run: async (t) => {
17
17
  t.section('version pickers');
18
18
  const releases = [
19
19
  { version: 'v23.1.0', lts: false },
@@ -198,6 +198,36 @@ export const toolchainsSuite = {
198
198
  t.check(ids.every((id, i) => ids.indexOf(id) === i) &&
199
199
  APPS.every((a) => APP_CATEGORIES.some((c) => c.id === a.category)) &&
200
200
  !APPS.some(badPattern), 'apps: ids are unique, every app is in a known category, every GitHub asset pattern compiles');
201
+ t.section('asset preview apps');
202
+ const previewPick = (id, files) => {
203
+ const source = APPS.find((x) => x.id === id)?.source;
204
+ const re = new RegExp(source && 'github' in source ? source.asset : '$^');
205
+ return files.filter((f) => re.test(f));
206
+ };
207
+ t.check(['f3d', 'imagemagick', 'nomacs'].every((id) => APPS.some((a) => a.id === id && a.category === 'gamedev')) &&
208
+ previewPick('f3d', [
209
+ 'F3D-4.0.1-Windows-x86_64.zip',
210
+ 'F3D-4.0.1-Windows-x86_64.exe',
211
+ 'F3D-4.0.1-Windows-x86_64-raytracing.zip',
212
+ ]).join() === 'F3D-4.0.1-Windows-x86_64.zip' &&
213
+ previewPick('imagemagick', [
214
+ 'ImageMagick-7.1.2-35-portable-Q16-HDRI-x64.7z',
215
+ 'ImageMagick-7.1.2-35-portable-Q16-x64.7z',
216
+ 'ImageMagick-7.1.2-35-portable-Q16-HDRI-arm64.7z',
217
+ ]).join() === 'ImageMagick-7.1.2-35-portable-Q16-HDRI-x64.7z' &&
218
+ previewPick('nomacs', ['nomacs-portable-win.zip', 'nomacs-setup.exe']).join() === 'nomacs-portable-win.zip', 'preview apps: F3D, ImageMagick and nomacs are in the catalogue and pick the portable download only');
219
+ const f3d = associationPlan('f3d', 'C:\\x\\f3d.exe');
220
+ t.check(f3d.some((v) => v.key === 'Software\\Classes\\.fbx' && v.value === 'Sandbox.f3d') &&
221
+ f3d.some((v) => v.key.endsWith('\\shell\\open\\command') && v.value === '"C:\\x\\f3d.exe" "%1"') &&
222
+ f3d.every((v) => v.hive === 'HKCU' && v.name === '') &&
223
+ associationPlan('nomacs', 'C:\\x\\nomacs.exe').some((v) => v.key === 'Software\\Classes\\.tga') &&
224
+ associationPlan('krita', 'C:\\x\\k.exe').length === 0, 'preview apps: double-click associations name the app per extension; an app without any gets none');
225
+ await t.throws(() => associationPlan('f3d', 'f3d.exe'), 'not a program path', 'a relative program path is refused');
226
+ await t.throws(() => associationPlan('f3d', 'C:\\a"b.exe'), 'not a program path', 'a quoted program path is refused');
227
+ t.check(previewGuideLine([]) === null &&
228
+ previewGuideLine(['jq']) === null &&
229
+ (previewGuideLine(['f3d']) ?? '').includes('--output=') &&
230
+ previewGuideLine(['imagemagick']) !== null, 'preview apps: the agent preview line appears only when F3D or ImageMagick is installed');
201
231
  t.check(exeMatcher('/^Godot_v[\\d.]+-stable_win64\\.exe$/')('Godot_v4.7.2-stable_win64.exe') &&
202
232
  !exeMatcher('/^Godot_v[\\d.]+-stable_win64\\.exe$/')('Godot_v4.7.2-stable_win64_console.exe') &&
203
233
  exeMatcher('Code.exe')('code.EXE'), 'apps: the program is found by name or by pattern');
@@ -1,7 +1,7 @@
1
1
  export * from './kinds.js';
2
2
  export * from './payloads.js';
3
3
  export { fingerprint } from './fingerprint.js';
4
- export { epicSettingsFile, MAX_EPIC_INI_BYTES, readEpicLogin, withEpicLogin, type EpicPayload } from './kinds/epic.js';
4
+ export { epicSettingsFile, epicSettingsFiles, MAX_EPIC_INI_BYTES, readEpicLogin, withEpicLogin, type EpicPayload, } from './kinds/epic.js';
5
5
  export * from './kinds/steam.js';
6
6
  export { PERPLEXITY_ENV, perplexityKeyFromText, perplexitySummary, type PerplexityPayload, } from './kinds/perplexity.js';
7
7
  export { cometPayloadFromCookies, cometSummary, cookiesToRestore, type CometPayload } from './kinds/comet.js';
@@ -3,7 +3,7 @@
3
3
  export * from './kinds.js';
4
4
  export * from './payloads.js';
5
5
  export { fingerprint } from './fingerprint.js';
6
- export { epicSettingsFile, MAX_EPIC_INI_BYTES, readEpicLogin, withEpicLogin } from './kinds/epic.js';
6
+ export { epicSettingsFile, epicSettingsFiles, MAX_EPIC_INI_BYTES, readEpicLogin, withEpicLogin, } from './kinds/epic.js';
7
7
  export * from './kinds/steam.js';
8
8
  export { PERPLEXITY_ENV, perplexityKeyFromText, perplexitySummary, } from './kinds/perplexity.js';
9
9
  export { cometPayloadFromCookies, cometSummary, cookiesToRestore } from './kinds/comet.js';
@@ -2,8 +2,13 @@ import type { Payload } from '../payloads.js';
2
2
  /** At most this much of a GameUserSettings.ini is read. */
3
3
  export declare const MAX_EPIC_INI_BYTES: number;
4
4
  export type EpicPayload = Payload<'epic'>;
5
- /** GameUserSettings.ini under a LocalAppData folder (the PC's or the sandbox's). */
5
+ /** GameUserSettings.ini under a LocalAppData folder (the PC's or the sandbox's), where launchers before 17 keep it. */
6
6
  export declare function epicSettingsFile(localAppData: string): string;
7
+ /**
8
+ * Every GameUserSettings.ini the launcher may read, newest place first: launcher 17 and later (20.3 checked) keep it in
9
+ * Config\WindowsEditor, older ones in Config\Windows.
10
+ */
11
+ export declare function epicSettingsFiles(localAppData: string): string[];
7
12
  /** The saved sign-in in an ini text, or null (none, turned off, or not the expected shape). */
8
13
  export declare function readEpicLogin(ini: string): EpicPayload | null;
9
14
  /** The ini text with its [RememberMe] section replaced by this sign-in (the other sections are kept). */
@@ -6,10 +6,20 @@ import { EpicPayloadSchema } from '../payloads.js';
6
6
  /** At most this much of a GameUserSettings.ini is read. */
7
7
  export const MAX_EPIC_INI_BYTES = 1024 * 1024;
8
8
  const SECTION = '[rememberme]';
9
- /** GameUserSettings.ini under a LocalAppData folder (the PC's or the sandbox's). */
9
+ /** GameUserSettings.ini under a LocalAppData folder (the PC's or the sandbox's), where launchers before 17 keep it. */
10
10
  export function epicSettingsFile(localAppData) {
11
11
  return localAppData + '\\EpicGamesLauncher\\Saved\\Config\\Windows\\GameUserSettings.ini';
12
12
  }
13
+ /**
14
+ * Every GameUserSettings.ini the launcher may read, newest place first: launcher 17 and later (20.3 checked) keep it in
15
+ * Config\WindowsEditor, older ones in Config\Windows.
16
+ */
17
+ export function epicSettingsFiles(localAppData) {
18
+ return [
19
+ localAppData + '\\EpicGamesLauncher\\Saved\\Config\\WindowsEditor\\GameUserSettings.ini',
20
+ epicSettingsFile(localAppData),
21
+ ];
22
+ }
13
23
  const isHeader = (line) => line.trimStart().startsWith('[');
14
24
  /** The saved sign-in in an ini text, or null (none, turned off, or not the expected shape). */
15
25
  export function readEpicLogin(ini) {
@@ -0,0 +1,7 @@
1
+ import type { RegistryValue } from '../unreal/engines.js';
2
+ export declare const ASSOCIATIONS: {
3
+ readonly f3d: readonly [".fbx", ".glb", ".gltf", ".obj", ".stl", ".ply", ".3ds", ".usd", ".usdz"];
4
+ readonly nomacs: readonly [".tga", ".psd", ".dds", ".exr"];
5
+ };
6
+ /** The registry values that make `appId` open its file types; no values for an app that opens none. */
7
+ export declare function associationPlan(appId: string, exe: string): RegistryValue[];
@@ -0,0 +1,26 @@
1
+ // Which apps open which game-asset files when you double-click them (per user, written to the registry each boot
2
+ // because the sandbox's registry is new every time). Pure: the guest writes the plan with set-registry-values.ps1.
3
+ import path from 'node:path';
4
+ export const ASSOCIATIONS = {
5
+ f3d: ['.fbx', '.glb', '.gltf', '.obj', '.stl', '.ply', '.3ds', '.usd', '.usdz'],
6
+ nomacs: ['.tga', '.psd', '.dds', '.exr'],
7
+ };
8
+ const NAMES = { f3d: 'F3D', nomacs: 'nomacs' };
9
+ /** The registry values that make `appId` open its file types; no values for an app that opens none. */
10
+ export function associationPlan(appId, exe) {
11
+ if (!Object.hasOwn(ASSOCIATIONS, appId))
12
+ return [];
13
+ const id = appId;
14
+ if (!path.win32.isAbsolute(exe) || /["\r\n]/.test(exe))
15
+ throw new Error('not a program path: ' + exe);
16
+ const classes = 'Software\\Classes';
17
+ const type = classes + '\\Sandbox.' + id;
18
+ const quoted = '"' + exe + '"';
19
+ const v = (key, value) => ({ hive: 'HKCU', key, name: '', value });
20
+ return [
21
+ ...ASSOCIATIONS[id].map((ext) => v(classes + '\\' + ext, 'Sandbox.' + id)),
22
+ v(type, NAMES[id] + ' file'),
23
+ v(type + '\\DefaultIcon', quoted + ',0'),
24
+ v(type + '\\shell\\open\\command', quoted + ' "%1"'),
25
+ ];
26
+ }
@@ -9,6 +9,9 @@ export declare const BROWSER_USE_VERSION = "0.13.14";
9
9
  export declare const PERPLEXITY_COMPUTER_MCP_URL = "https://www.perplexity.ai/rest/computer/mcp";
10
10
  /** Comet's Windows installer: a redirect to the current release (no checksum is published; its signer is checked). */
11
11
  export declare const COMET_DOWNLOAD_URL = "https://www.perplexity.ai/rest/browser/download?platform=win_x64&channel=stable";
12
- /** Who signs Comet: Perplexity AI, Inc., with an EV code-signing certificate from GlobalSign. */
12
+ /**
13
+ * Who signs Comet: Perplexity AI, Inc., with an EV code-signing certificate from GlobalSign. Windows reports the
14
+ * subject with E=<contact address> first (E=..., CN="PERPLEXITY AI, INC.", O=...), so CN is not at the start.
15
+ */
13
16
  export declare const COMET_SIGNER: AppSigner;
14
17
  export declare const AGENTS_WEB_APPS: readonly AppEntry[];
@@ -7,9 +7,12 @@ export const BROWSER_USE_VERSION = '0.13.14';
7
7
  export const PERPLEXITY_COMPUTER_MCP_URL = 'https://www.perplexity.ai/rest/computer/mcp';
8
8
  /** Comet's Windows installer: a redirect to the current release (no checksum is published; its signer is checked). */
9
9
  export const COMET_DOWNLOAD_URL = 'https://www.perplexity.ai/rest/browser/download?platform=win_x64&channel=stable';
10
- /** Who signs Comet: Perplexity AI, Inc., with an EV code-signing certificate from GlobalSign. */
10
+ /**
11
+ * Who signs Comet: Perplexity AI, Inc., with an EV code-signing certificate from GlobalSign. Windows reports the
12
+ * subject with E=<contact address> first (E=..., CN="PERPLEXITY AI, INC.", O=...), so CN is not at the start.
13
+ */
11
14
  export const COMET_SIGNER = {
12
- subject: ['^CN="?PERPLEXITY AI, INC\\.?"?(,|$)', '(^|, )O="?PERPLEXITY AI, INC\\.?"?(,|$)'],
15
+ subject: ['(^|, )CN="?PERPLEXITY AI, INC\\.?"?(,|$)', '(^|, )O="?PERPLEXITY AI, INC\\.?"?(,|$)'],
13
16
  issuer: ['^CN=GlobalSign GCC R45 EV CodeSigning CA 2020(,|$)'],
14
17
  };
15
18
  /** Microsoft Edge, already in every sandbox: the browser Chrome DevTools MCP starts. */
@@ -1 +1,71 @@
1
- export const GAMEDEV_APPS = [];
1
+ export const GAMEDEV_APPS = [
2
+ {
3
+ id: 'krita',
4
+ name: 'Krita',
5
+ category: 'gamedev',
6
+ about: 'Painting and texture editor (PSD, layers, brushes), checked against the SHA-256 download.kde.org publishes.',
7
+ size: '450 MB',
8
+ source: { resolve: 'krita' },
9
+ exe: 'krita.exe',
10
+ needs: ['mesa'],
11
+ },
12
+ {
13
+ id: 'inkscape',
14
+ name: 'Inkscape',
15
+ category: 'gamedev',
16
+ about: 'Vector graphics for UI, icons and 2D art (SVG), checked against the SHA-256 inkscape.org publishes.',
17
+ size: '400 MB',
18
+ source: { resolve: 'inkscape' },
19
+ exe: 'inkscape.exe',
20
+ needs: ['7zip'],
21
+ },
22
+ {
23
+ id: 'audacity',
24
+ name: 'Audacity',
25
+ category: 'gamedev',
26
+ about: 'Records and edits sound effects and music (WAV, OGG, FLAC).',
27
+ size: '80 MB',
28
+ source: { github: 'audacity/audacity', asset: '^audacity-win-[\\d.]+-(?:64bit\\.zip|x86_64\\.7z)$' },
29
+ exe: 'Audacity.exe',
30
+ needs: ['7zip'],
31
+ },
32
+ {
33
+ id: 'renderdoc',
34
+ name: 'RenderDoc',
35
+ category: 'gamedev',
36
+ 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.",
37
+ size: '70 MB',
38
+ source: { resolve: 'renderdoc' },
39
+ exe: 'qrenderdoc.exe',
40
+ },
41
+ {
42
+ id: 'f3d',
43
+ name: 'F3D',
44
+ category: 'gamedev',
45
+ about: 'Fast 3D viewer (FBX, glTF/GLB, OBJ, STL, PLY, 3DS, USD); renders still previews from the command line.',
46
+ size: '100 MB',
47
+ source: { github: 'f3d-app/f3d', asset: '^F3D-[\\d.]+-Windows-x86_64\\.zip$' },
48
+ exe: 'f3d.exe',
49
+ needs: ['mesa'],
50
+ },
51
+ {
52
+ id: 'imagemagick',
53
+ name: 'ImageMagick',
54
+ category: 'gamedev',
55
+ about: 'Converts and thumbnails textures (TGA, PSD, DDS, EXR, HDR) and builds contact sheets (magick montage).',
56
+ size: '150 MB',
57
+ source: { github: 'ImageMagick/ImageMagick', asset: '^ImageMagick-[\\d.]+-\\d+-portable-Q16-HDRI-x64\\.7z$' },
58
+ exe: 'magick.exe',
59
+ cli: true,
60
+ needs: ['7zip'],
61
+ },
62
+ {
63
+ id: 'nomacs',
64
+ name: 'nomacs',
65
+ category: 'gamedev',
66
+ about: 'Image viewer for textures (TGA, PSD, DDS, EXR).',
67
+ size: '60 MB',
68
+ source: { github: 'nomacs/nomacs', asset: '^nomacs-portable-win\\.zip$' },
69
+ exe: 'nomacs.exe',
70
+ },
71
+ ];
@@ -1,4 +1,6 @@
1
1
  export { type AddonMcp, type AppCategory, APP_CATEGORIES, type AppSource, type AppEntry, APPS, appById, CCUSAGE_VERSION, CONTEXT7_MCP_VERSION, HEADROOM_VERSION, PLAYWRIGHT_MCP_VERSION, RTK_VERSION, } from './catalogue.js';
2
+ export { ASSOCIATIONS, associationPlan } from './associations.js';
3
+ export { previewGuideLine } from './preview.js';
2
4
  export { exeMatcher, appsToInstall, appsNeedMesa, appsNeedUv } from './selection.js';
3
5
  export { signerMatches, type AppSigner, type Signature } from '../signer.js';
4
6
  export { AGENTS_WEB_APPS, BROWSER_USE_VERSION, CHROME_DEVTOOLS_MCP_VERSION, COMET_DOWNLOAD_URL, COMET_MCP_VERSION, COMET_SIGNER, PERPLEXITY_COMPUTER_MCP_URL, PERPLEXITY_MCP_VERSION, } from './catalogue-agents-web.js';
@@ -1,4 +1,6 @@
1
1
  export { APP_CATEGORIES, APPS, appById, CCUSAGE_VERSION, CONTEXT7_MCP_VERSION, HEADROOM_VERSION, PLAYWRIGHT_MCP_VERSION, RTK_VERSION, } from './catalogue.js';
2
+ export { ASSOCIATIONS, associationPlan } from './associations.js';
3
+ export { previewGuideLine } from './preview.js';
2
4
  export { exeMatcher, appsToInstall, appsNeedMesa, appsNeedUv } from './selection.js';
3
5
  export { signerMatches } from '../signer.js';
4
6
  export { AGENTS_WEB_APPS, BROWSER_USE_VERSION, CHROME_DEVTOOLS_MCP_VERSION, COMET_DOWNLOAD_URL, COMET_MCP_VERSION, COMET_SIGNER, PERPLEXITY_COMPUTER_MCP_URL, PERPLEXITY_MCP_VERSION, } from './catalogue-agents-web.js';
@@ -0,0 +1,2 @@
1
+ /** Null unless F3D or ImageMagick is installed; `installed` holds app ids. */
2
+ export declare function previewGuideLine(installed: readonly string[]): string | null;
@@ -0,0 +1,10 @@
1
+ // The one line that tells an agent how to render previews of meshes and textures for the owner (pure).
2
+ /** Null unless F3D or ImageMagick is installed; `installed` holds app ids. */
3
+ export function previewGuideLine(installed) {
4
+ if (!installed.includes('f3d') && !installed.includes('imagemagick'))
5
+ return null;
6
+ return ('asset previews: `f3d "<mesh>" --output="<pkg>\\previews\\<name>.png" --resolution=1024,768 --up=+Y` per mesh ' +
7
+ '(--up=+Z for Unreal-space files), `magick montage "<pkg>\\source\\**\\*.png" -thumbnail 256x256 -geometry +4+4 ' +
8
+ '-label %f "<pkg>\\previews\\textures.png"` for textures (convert TGA/PSD/DDS/EXR to PNG first with magick); ' +
9
+ "leave them in the package's previews\\ folder for the owner to look at before import");
10
+ }
@@ -0,0 +1,14 @@
1
+ export interface BrowserPolicy {
2
+ readonly key: string;
3
+ readonly name: string;
4
+ readonly type: 'REG_DWORD' | 'REG_SZ';
5
+ readonly data: string;
6
+ }
7
+ export declare const BROWSER_POLICY_KEYS: readonly ["HKLM\\SOFTWARE\\Policies\\Microsoft\\Edge", "HKLM\\SOFTWARE\\Policies\\Google\\Chrome"];
8
+ /**
9
+ * The policies for each browser: no hardware acceleration (today's behaviour) and, when a downloads folder is given,
10
+ * DefaultDownloadDirectory (the default only: Playwright's download path and the user's own choice still win).
11
+ */
12
+ export declare function browserPolicies(o: {
13
+ readonly downloads: string | null;
14
+ }): BrowserPolicy[];