windows-development-sandbox 1.25.1 → 1.27.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.
- package/config.example.json +12 -3
- package/dist/app/features/accounts/services.def.d.ts +1 -1
- package/dist/app/features/accounts/services.def.js +1 -1
- package/dist/app/features/defs.d.ts +198 -3
- package/dist/app/features/defs.js +8 -0
- package/dist/app/features/settings/categories.def.js +1 -0
- package/dist/app/features/unity/index.d.ts +2 -0
- package/dist/app/features/unity/index.js +3 -0
- package/dist/app/features/unity/unity.def.d.ts +294 -0
- package/dist/app/features/unity/unity.def.js +471 -0
- package/dist/app/features/unity/unity.run.d.ts +142 -0
- package/dist/app/features/unity/unity.run.js +363 -0
- package/dist/app/features/unreal/unreal.run.js +13 -3
- package/dist/app/registry.js +6 -0
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-G3TG4ZT7.js → chunk-4TLSWPG7.js} +1 -1
- package/dist/bin/{chunk-3GAXLATK.js → chunk-QUHNXHVG.js} +72 -5
- package/dist/bin/{chunk-F4PVBN23.js → chunk-RSIX7C6D.js} +3633 -1972
- package/dist/bin/claude-host.js +2 -2
- package/dist/bin/claude-sbx.js +2 -2
- package/dist/bin/codex-sbx.js +2 -2
- package/dist/bin/gemini-sbx.js +2 -2
- package/dist/bin/sandbox.js +2 -2
- package/dist/bin/sbx-hide.js +2 -2
- package/dist/bin/sbx-show.js +2 -2
- package/dist/cli/commands/launch/launch.js +14 -1
- package/dist/cli/commands/registry.js +3 -0
- package/dist/cli/manifest.d.ts +33 -1
- package/dist/cli/manifest.js +32 -1
- package/dist/cli/menu/coverage-exempt.js +1 -0
- package/dist/cli/menu/pages/sandbox-page.d.ts +3 -0
- package/dist/cli/menu/pages/sandbox-page.js +40 -3
- package/dist/cli/menu/views.js +2 -0
- package/dist/core/config/defaults.js +16 -3
- package/dist/core/schema/accounts.d.ts +3 -0
- package/dist/core/schema/accounts.js +1 -0
- package/dist/core/schema/config.d.ts +29 -13
- package/dist/core/schema/github.d.ts +1 -0
- package/dist/core/schema/github.js +3 -0
- package/dist/core/schema/pc-installs.d.ts +2 -1
- package/dist/core/schema/pc-installs.js +6 -1
- package/dist/core/schema/toolchains.d.ts +8 -0
- package/dist/core/schema/toolchains.js +19 -4
- package/dist/guest/accounts/adapters.js +2 -0
- package/dist/guest/accounts/apply.d.ts +4 -2
- package/dist/guest/accounts/kinds/perplexityapp.d.ts +7 -0
- package/dist/guest/accounts/kinds/perplexityapp.js +53 -0
- package/dist/guest/accounts/kinds/unity.d.ts +27 -0
- package/dist/guest/accounts/kinds/unity.js +164 -7
- package/dist/guest/apps/app-installer.js +18 -0
- package/dist/guest/apps/store-app.d.ts +4 -0
- package/dist/guest/apps/store-app.js +19 -0
- package/dist/guest/apps/store-check.d.ts +8 -0
- package/dist/guest/apps/store-check.js +33 -0
- package/dist/guest/boot/boot.js +1 -1
- package/dist/guest/boot/guide.js +18 -0
- package/dist/guest/browser/comet.d.ts +3 -1
- package/dist/guest/browser/comet.js +1 -1
- package/dist/guest/browser/perplexity-app.d.ts +12 -0
- package/dist/guest/browser/perplexity-app.js +60 -0
- package/dist/guest/daemon/daemon.js +2 -0
- package/dist/guest/devtools/devtool.d.ts +1 -1
- package/dist/guest/devtools/registry.js +1 -2
- package/dist/guest/entries/tool.js +1 -1
- package/dist/guest/setup/addon-mcp.d.ts +7 -2
- package/dist/guest/setup/addon-mcp.js +19 -9
- package/dist/guest/setup/github-token.js +4 -4
- package/dist/guest/setup/unity-agent.d.ts +11 -0
- package/dist/guest/setup/unity-agent.js +39 -0
- package/dist/guest/toolchains/engines/epic-launcher.d.ts +1 -1
- package/dist/guest/toolchains/engines/epic-launcher.js +11 -7
- package/dist/guest/toolchains/engines/unity-cli.d.ts +24 -0
- package/dist/guest/toolchains/engines/unity-cli.js +74 -0
- package/dist/guest/toolchains/engines/unity-hub.d.ts +2 -0
- package/dist/guest/toolchains/engines/unity-hub.js +27 -0
- package/dist/guest/toolchains/engines/unity-release.d.ts +4 -15
- package/dist/guest/toolchains/engines/unity-release.js +17 -5
- package/dist/guest/toolchains/engines/unity-status.d.ts +11 -0
- package/dist/guest/toolchains/engines/unity-status.js +39 -0
- package/dist/guest/toolchains/engines/unity.d.ts +20 -1
- package/dist/guest/toolchains/engines/unity.js +250 -3
- package/dist/guest/toolchains/engines/unreal-status.js +1 -1
- package/dist/guest/toolchains/native/mesa.js +35 -11
- package/dist/guest/toolchains/native/unreal.js +1 -0
- package/dist/guest/toolchains/plan.js +5 -1
- package/dist/guest/toolchains/tools/cli.js +2 -2
- package/dist/host/accounts/deliver.d.ts +3 -0
- package/dist/host/accounts/deliver.js +23 -0
- package/dist/host/accounts/kinds/perplexityapp.d.ts +2 -0
- package/dist/host/accounts/kinds/perplexityapp.js +8 -0
- package/dist/host/accounts/kinds/unity.d.ts +1 -1
- package/dist/host/accounts/kinds/unity.js +60 -8
- package/dist/host/accounts/leases.d.ts +2 -1
- package/dist/host/accounts/source.d.ts +2 -0
- package/dist/host/accounts/source.js +2 -0
- package/dist/host/accounts/sources.js +2 -0
- package/dist/host/apps/index.d.ts +1 -0
- package/dist/host/apps/index.js +2 -0
- package/dist/host/apps/store-package.d.ts +51 -0
- package/dist/host/apps/store-package.js +209 -0
- package/dist/host/github/deliver.d.ts +8 -3
- package/dist/host/github/deliver.js +42 -29
- package/dist/host/github/index.d.ts +1 -0
- package/dist/host/github/index.js +1 -0
- package/dist/host/github/pc-login.d.ts +27 -0
- package/dist/host/github/pc-login.js +129 -0
- package/dist/host/pc-installs/discover.d.ts +4 -0
- package/dist/host/pc-installs/discover.js +19 -6
- package/dist/host/pc-installs/epic.js +3 -0
- package/dist/host/pc-installs/source.d.ts +1 -1
- package/dist/host/pc-installs/unity-assets.d.ts +5 -0
- package/dist/host/pc-installs/unity-assets.js +18 -0
- package/dist/host/pc-installs/unity.d.ts +22 -0
- package/dist/host/pc-installs/unity.js +144 -2
- package/dist/host/pc-installs/unreal.d.ts +2 -0
- package/dist/host/pc-installs/unreal.js +24 -8
- package/dist/runtime/agent.js +116 -11
- package/dist/runtime/daemon.js +2205 -987
- package/dist/runtime/db.js +116 -11
- package/dist/runtime/main.js +2578 -1375
- package/dist/runtime/netproxy.js +116 -11
- package/dist/runtime/tool.js +133 -31
- package/dist/runtime/view.js +116 -11
- package/dist/selftest/agents-web.test.js +2 -1
- package/dist/selftest/engines.test.js +32 -3
- package/dist/selftest/github.test.js +64 -1
- package/dist/selftest/index.js +10 -0
- package/dist/selftest/menu-screens.js +3 -1
- package/dist/selftest/mixamo.test.js +1 -1
- package/dist/selftest/persisted-installs.test.js +1 -1
- package/dist/selftest/service-accounts.test.js +13 -2
- package/dist/selftest/store-app.test.d.ts +2 -0
- package/dist/selftest/store-app.test.js +162 -0
- package/dist/selftest/support/engine-golden.js +7 -0
- package/dist/selftest/support/fixtures.d.ts +10 -1
- package/dist/selftest/support/unity-sample.d.ts +2 -0
- package/dist/selftest/support/unity-sample.js +80 -0
- package/dist/selftest/unity-accounts.test.d.ts +2 -0
- package/dist/selftest/unity-accounts.test.js +106 -0
- package/dist/selftest/unity-agent.test.d.ts +2 -0
- package/dist/selftest/unity-agent.test.js +47 -0
- package/dist/selftest/unity-page.test.d.ts +2 -0
- package/dist/selftest/unity-page.test.js +177 -0
- package/dist/selftest/unity-setup.test.d.ts +2 -0
- package/dist/selftest/unity-setup.test.js +49 -0
- package/dist/selftest/unity.test.js +238 -3
- package/dist/shared/accounts/index.d.ts +1 -0
- package/dist/shared/accounts/index.js +1 -0
- package/dist/shared/accounts/kinds/unity.d.ts +28 -0
- package/dist/shared/accounts/kinds/unity.js +88 -0
- package/dist/shared/accounts/kinds.d.ts +2 -2
- package/dist/shared/accounts/kinds.js +23 -1
- package/dist/shared/accounts/payloads.d.ts +52 -0
- package/dist/shared/accounts/payloads.js +14 -2
- package/dist/shared/apps/catalogue-agents-web.d.ts +7 -0
- package/dist/shared/apps/catalogue-agents-web.js +23 -0
- package/dist/shared/apps/catalogue-gamedev.d.ts +3 -0
- package/dist/shared/apps/catalogue-gamedev.js +31 -0
- package/dist/shared/apps/catalogue.d.ts +8 -0
- package/dist/shared/apps/index.d.ts +3 -2
- package/dist/shared/apps/index.js +3 -2
- package/dist/shared/apps/selection.d.ts +6 -0
- package/dist/shared/apps/selection.js +9 -0
- package/dist/shared/apps/store-package.d.ts +31 -0
- package/dist/shared/apps/store-package.js +34 -0
- package/dist/shared/channel/schema.d.ts +24 -0
- package/dist/shared/channel/secrets.d.ts +17 -0
- package/dist/shared/paths/pc-installs.d.ts +1 -1
- package/dist/shared/paths/pc-installs.js +1 -0
- package/dist/shared/readiness.d.ts +2 -2
- package/dist/shared/readiness.js +2 -2
- package/dist/shared/unity/asset-cache.d.ts +20 -0
- package/dist/shared/unity/asset-cache.js +44 -0
- package/dist/shared/unity/cli.d.ts +40 -0
- package/dist/shared/unity/cli.js +77 -0
- package/dist/shared/unity/editors.d.ts +46 -0
- package/dist/shared/unity/editors.js +89 -0
- package/dist/shared/unity/guide.d.ts +9 -0
- package/dist/shared/unity/guide.js +84 -0
- package/dist/shared/unity/index.d.ts +9 -0
- package/dist/shared/unity/index.js +11 -0
- package/dist/shared/unity/manifest.d.ts +10 -0
- package/dist/shared/unity/manifest.js +53 -0
- package/dist/shared/unity/mcp.d.ts +8 -0
- package/dist/shared/unity/mcp.js +12 -0
- package/dist/shared/unity/release.d.ts +28 -0
- package/dist/shared/unity/release.js +83 -0
- package/dist/shared/unity/status.d.ts +59 -0
- package/dist/shared/unity/status.js +45 -0
- package/dist/shared/unity/version.d.ts +27 -0
- package/dist/shared/unity/version.js +75 -0
- package/dist/shared/unreal/status.d.ts +1 -0
- package/dist/shared/unreal/status.js +2 -0
- package/dist/web/index.html +1 -1
- package/dist/web/{main-FNE6XEHA.js → main-VPMYTSUL.js} +67 -67
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
- package/native/scripts/get-store-package.ps1 +58 -0
- package/package.json +1 -1
- package/sandbox/addons/openupm-cli/package-lock.json +2921 -0
- package/sandbox/addons/openupm-cli/package.json +7 -0
- package/sandbox/addons/unity-mcp/requirements.in +1 -0
- package/sandbox/addons/unity-mcp/requirements.txt +1165 -0
- package/sandbox/csharp/AppActivation.cs +38 -0
- package/sandbox/scripts/install-store-package.ps1 +28 -0
- package/sandbox/scripts/start-store-app.ps1 +29 -0
- package/schemas/config.schema.json +61 -6
- package/schemas/sandbox-config.schema.json +38 -5
- package/dist/guest/devtools/unity.d.ts +0 -2
- package/dist/guest/devtools/unity.js +0 -7
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
// The Unity area's work: what the sandbox last found (tools\unity\status.json, written by the guest, so it is read with
|
|
2
|
+
// its schema and its text made safe), what this PC has that the next sandbox maps (its editors and Asset Store
|
|
3
|
+
// downloads: bounded reads), opening Unity Hub and signing in (exec requests in the running sandbox) and adding or
|
|
4
|
+
// removing the editor bridge in a project's Packages/manifest.json (only on request, after a confirmation).
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { humanBytes } from '../../../core/disk.js';
|
|
8
|
+
import { assertNoLinks, errMsg, fail, safeText } from '../../../core/util/index.js';
|
|
9
|
+
import { ProjectsService } from '../../../cli/services/projects-service.js';
|
|
10
|
+
import { SandboxChannel } from '../../../host/channel/index.js';
|
|
11
|
+
import { isSandboxRunning } from '../../../host/sandbox/index.js';
|
|
12
|
+
import { pcEnv } from '../../../host/accounts/index.js';
|
|
13
|
+
import { assetCacheEnv, findUnityEditorsOnPc, unityAssetCacheDirs, unityPcFiles, } from '../../../host/pc-installs/unity.js';
|
|
14
|
+
import { readBoundedFile, readValid, writeFileAtomic } from '../../../shared/io/index.js';
|
|
15
|
+
import { RunDir } from '../../../shared/channel/index.js';
|
|
16
|
+
import { assetPackages, bridgeOf, COPLAY_PACKAGE_URL, DEFAULT_PIPELINE_VERSION, ORIGIN_LABELS, parseProjectVersion, supportsPipeline, UNITY_STATUS_FILE, UnityStatusSchema, withBridge, } from '../../../shared/unity/index.js';
|
|
17
|
+
import { implement } from '../../context.js';
|
|
18
|
+
import { addEditorBridge, openUnityHub, removeEditorBridge, unityOverview, unitySignIn, } from './unity.def.js';
|
|
19
|
+
const win = path.win32;
|
|
20
|
+
const MAX_PACKAGES_SHOWN = 40;
|
|
21
|
+
const MAX_MANIFEST_READ = 1024 * 1024 + 1;
|
|
22
|
+
const PIPELINE_INDEX_URL = 'https://packages.unity.com/com.unity.pipeline';
|
|
23
|
+
const MAX_INDEX_BYTES = 2 * 1024 * 1024;
|
|
24
|
+
const GUEST_EXPLORER = 'C:\\Windows\\explorer.exe';
|
|
25
|
+
const SEMVER = /^\d{1,6}\.\d{1,6}\.\d{1,6}(-[0-9A-Za-z.-]{1,50})?(\+[0-9A-Za-z.-]{1,50})?$/;
|
|
26
|
+
const s = (text) => safeText(text);
|
|
27
|
+
const sn = (text) => (text === null ? null : safeText(text));
|
|
28
|
+
export function readStatus(dataPath) {
|
|
29
|
+
const file = path.join(dataPath, 'tools', 'unity', UNITY_STATUS_FILE);
|
|
30
|
+
try {
|
|
31
|
+
assertNoLinks(dataPath, file);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null; // A link under the data folder: never followed.
|
|
35
|
+
}
|
|
36
|
+
return readValid(UnityStatusSchema, file);
|
|
37
|
+
}
|
|
38
|
+
const settingText = (v) => (Array.isArray(v) ? v.join(', ') || 'none' : String(v));
|
|
39
|
+
/** The overview from the sandbox's status and this PC's own look (no sandbox needed). */
|
|
40
|
+
export function unityOverviewOf(ctx, status, pcLook, hostFolders = []) {
|
|
41
|
+
const config = ctx.loaded.config;
|
|
42
|
+
let pc = { editors: [], assets: [] };
|
|
43
|
+
let problem = null;
|
|
44
|
+
try {
|
|
45
|
+
pc = pcLook();
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
problem = 'Could not look at this PC: ' + errMsg(e);
|
|
49
|
+
}
|
|
50
|
+
const u = config.toolchains.unity;
|
|
51
|
+
const hostFolderOf = (workspace) => {
|
|
52
|
+
const name = win.basename(workspace).toLowerCase();
|
|
53
|
+
const same = hostFolders.filter((f) => win.basename(f).toLowerCase() === name);
|
|
54
|
+
return same.length === 1 ? same[0] : null;
|
|
55
|
+
};
|
|
56
|
+
const editorLabel = (root) => {
|
|
57
|
+
if (root === null)
|
|
58
|
+
return null;
|
|
59
|
+
const e = status?.editors.find((x) => x.root.toLowerCase() === root.toLowerCase());
|
|
60
|
+
return s(e ? 'Unity ' + e.version : root);
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
setUp: status !== null,
|
|
64
|
+
checkedAt: status ? s(status.updatedAt) : null,
|
|
65
|
+
cli: status?.cli ? { version: s(status.cli.version), path: s(status.cli.path) } : null,
|
|
66
|
+
editors: (status?.editors ?? []).map((e) => ({
|
|
67
|
+
version: s(e.version),
|
|
68
|
+
dir: s(e.root),
|
|
69
|
+
where: ORIGIN_LABELS[e.origin],
|
|
70
|
+
writable: e.writable,
|
|
71
|
+
modules: e.modules.map(s),
|
|
72
|
+
sizeBytes: e.sizeBytes,
|
|
73
|
+
projects: (status?.projects ?? [])
|
|
74
|
+
.filter((p) => p.editor?.toLowerCase() === e.root.toLowerCase())
|
|
75
|
+
.map((p) => s(win.basename(p.workspace))),
|
|
76
|
+
})),
|
|
77
|
+
projects: (status?.projects ?? []).map((p) => ({
|
|
78
|
+
project: s(win.basename(p.workspace)),
|
|
79
|
+
folder: hostFolderOf(p.workspace),
|
|
80
|
+
version: sn(p.version),
|
|
81
|
+
editor: editorLabel(p.editor),
|
|
82
|
+
reason: s(p.reason),
|
|
83
|
+
bridge: p.bridge,
|
|
84
|
+
pipeline: p.version !== null && supportsPipeline(p.version),
|
|
85
|
+
})),
|
|
86
|
+
licence: {
|
|
87
|
+
state: status?.licence.state ?? 'unknown',
|
|
88
|
+
kind: s(status?.licence.kind ?? ''),
|
|
89
|
+
detail: s(status?.licence.detail ?? ''),
|
|
90
|
+
user: sn(status?.licence.user ?? null),
|
|
91
|
+
},
|
|
92
|
+
hub: { installed: status?.hub.installed ?? false, setting: u.hub },
|
|
93
|
+
assetCache: { mode: status?.assetCache.mode ?? u.assetCache, dir: sn(status?.assetCache.dir ?? null) },
|
|
94
|
+
agentTools: {
|
|
95
|
+
cliVersion: u.cliVersion,
|
|
96
|
+
mcp: sn(status?.mcp ?? null),
|
|
97
|
+
mcpSetting: u.mcp,
|
|
98
|
+
skills: status?.skills ?? false,
|
|
99
|
+
skillsSetting: u.agentSkills,
|
|
100
|
+
},
|
|
101
|
+
pc: { setting: settingText(config.pcInstalls.unityEditors), editors: pc.editors, problem },
|
|
102
|
+
assets: { setting: settingText(config.pcInstalls.unityAssets), folders: pc.assets },
|
|
103
|
+
problem: sn(status?.problem ?? null),
|
|
104
|
+
settings: {
|
|
105
|
+
enabled: settingText(u.enabled),
|
|
106
|
+
acceptLicense: u.acceptLicense,
|
|
107
|
+
version: u.version,
|
|
108
|
+
modules: settingText(u.modules),
|
|
109
|
+
hub: u.hub,
|
|
110
|
+
keepVersions: u.keepVersions,
|
|
111
|
+
mcp: u.mcp,
|
|
112
|
+
agentSkills: u.agentSkills,
|
|
113
|
+
cliVersion: u.cliVersion,
|
|
114
|
+
assetCache: u.assetCache,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** This PC's disk for the packages (bounded lists). */
|
|
119
|
+
export const PC_ASSET_FS = {
|
|
120
|
+
list: (dir) => {
|
|
121
|
+
try {
|
|
122
|
+
return fs.readdirSync(dir).slice(0, 5000);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return []; // Not a folder: nothing in it.
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
isDir: (dir) => {
|
|
129
|
+
try {
|
|
130
|
+
return fs.lstatSync(dir).isDirectory();
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
size: (file) => {
|
|
137
|
+
try {
|
|
138
|
+
const st = fs.lstatSync(file);
|
|
139
|
+
return st.isFile() ? st.size : null;
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
/** What this PC has for the next sandbox: its editors and its Asset Store packages. */
|
|
147
|
+
function lookAtPc(ctx) {
|
|
148
|
+
const config = ctx.loaded.config.pcInstalls;
|
|
149
|
+
const pc = pcEnv();
|
|
150
|
+
const files = unityPcFiles(pc);
|
|
151
|
+
const editors = config.unityEditors === false
|
|
152
|
+
? []
|
|
153
|
+
: findUnityEditorsOnPc(files, Array.isArray(config.unityEditors) ? config.unityEditors.map(String) : true).map((e) => ({ version: e.id, folder: e.host }));
|
|
154
|
+
const assets = config.unityAssets === false
|
|
155
|
+
? []
|
|
156
|
+
: unityAssetCacheDirs(files, assetCacheEnv(pc)).map((folder) => {
|
|
157
|
+
const packages = assetPackages(folder, PC_ASSET_FS);
|
|
158
|
+
return {
|
|
159
|
+
folder,
|
|
160
|
+
packages: packages.slice(0, MAX_PACKAGES_SHOWN).map((p) => ({
|
|
161
|
+
publisher: p.publisher,
|
|
162
|
+
category: p.category,
|
|
163
|
+
name: p.name,
|
|
164
|
+
size: humanBytes(p.sizeBytes),
|
|
165
|
+
})),
|
|
166
|
+
more: Math.max(0, packages.length - MAX_PACKAGES_SHOWN),
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
return { editors, assets };
|
|
170
|
+
}
|
|
171
|
+
export const unityOverviewRun = implement(unityOverview, async (ctx) => {
|
|
172
|
+
let hostFolders = [];
|
|
173
|
+
try {
|
|
174
|
+
hostFolders = (await new ProjectsService(ctx).list()).projects.map((p) => p.folder);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// No project list: the page shows the projects without bridge buttons.
|
|
178
|
+
}
|
|
179
|
+
return unityOverviewOf(ctx, readStatus(ctx.dataPath), () => lookAtPc(ctx), hostFolders);
|
|
180
|
+
});
|
|
181
|
+
// ---- Unity Hub and sign-in (the running sandbox) ----
|
|
182
|
+
async function inSandbox(ctx, argv, seconds) {
|
|
183
|
+
if (!isSandboxRunning())
|
|
184
|
+
fail('No sandbox is running.', 'Start one for a Unity project; Unity opens in its window.');
|
|
185
|
+
const status = new RunDir(ctx.dataPath).readSandboxStatus();
|
|
186
|
+
if (status?.phase !== 'ready')
|
|
187
|
+
fail('The sandbox is not ready yet.', 'Try again in a moment.');
|
|
188
|
+
const result = await new SandboxChannel(ctx.dataPath).exec(argv, status.guestWorkspace ?? 'C:\\', seconds);
|
|
189
|
+
// explorer.exe returns 1 even when it started the program.
|
|
190
|
+
if (!result.ok && !(argv[0] === GUEST_EXPLORER && result.exitCode === 1))
|
|
191
|
+
fail('The sandbox could not do it: ' + safeText(result.message));
|
|
192
|
+
}
|
|
193
|
+
export const openUnityHubRun = implement(openUnityHub, async (ctx, _input, io) => {
|
|
194
|
+
const hub = readStatus(ctx.dataPath)?.hub;
|
|
195
|
+
if (!hub?.installed || !hub.path)
|
|
196
|
+
fail('Unity Hub is not installed in this sandbox.', 'Turn on Unity Hub in the Unity set-up settings and restart.');
|
|
197
|
+
io.progress('Opening Unity Hub in the sandbox…');
|
|
198
|
+
await inSandbox(ctx, [GUEST_EXPLORER, hub.path], 30);
|
|
199
|
+
return {
|
|
200
|
+
message: 'Unity Hub opens in the sandbox window. Sign in once; the licence is kept for the next sandboxes.',
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
export const unitySignInRun = implement(unitySignIn, async (ctx, _input, io) => {
|
|
204
|
+
const status = readStatus(ctx.dataPath);
|
|
205
|
+
io.progress('Opening the Unity sign-in in the sandbox…');
|
|
206
|
+
if (status?.hub.installed && status.hub.path)
|
|
207
|
+
await inSandbox(ctx, [GUEST_EXPLORER, status.hub.path], 30);
|
|
208
|
+
else if (status?.cli)
|
|
209
|
+
await inSandbox(ctx, [status.cli.path, 'auth', 'login'], 300);
|
|
210
|
+
else
|
|
211
|
+
fail('Unity is not set up in a sandbox yet.', 'Start a sandbox for a Unity project first.');
|
|
212
|
+
return { message: 'Sign in in the sandbox window; the licence is kept for the next sandboxes.' };
|
|
213
|
+
});
|
|
214
|
+
// ---- The editor bridge ----
|
|
215
|
+
/** The newest Unity Pipeline package version, from Unity's package registry (bounded, checked as a version). */
|
|
216
|
+
export async function latestPipelineVersion(fetcher = fetch) {
|
|
217
|
+
const response = await fetcher(PIPELINE_INDEX_URL, { signal: AbortSignal.timeout(15_000) });
|
|
218
|
+
if (!response.ok)
|
|
219
|
+
throw new Error('Unity answered ' + response.status);
|
|
220
|
+
const reader = response.body?.getReader();
|
|
221
|
+
if (!reader)
|
|
222
|
+
throw new Error('Unity sent nothing');
|
|
223
|
+
const chunks = [];
|
|
224
|
+
let total = 0;
|
|
225
|
+
for (;;) {
|
|
226
|
+
const { done, value } = await reader.read();
|
|
227
|
+
if (done)
|
|
228
|
+
break;
|
|
229
|
+
total += value.length;
|
|
230
|
+
if (total > MAX_INDEX_BYTES) {
|
|
231
|
+
await reader.cancel();
|
|
232
|
+
throw new Error('the package list is over 2 MB');
|
|
233
|
+
}
|
|
234
|
+
chunks.push(Buffer.from(value));
|
|
235
|
+
}
|
|
236
|
+
const json = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
237
|
+
const latest = json?.['dist-tags']?.latest;
|
|
238
|
+
if (typeof latest !== 'string' || !SEMVER.test(latest))
|
|
239
|
+
throw new Error('no usable version in the package list');
|
|
240
|
+
return latest;
|
|
241
|
+
}
|
|
242
|
+
/** A project of this PC: its manifest file and the editor version it asks for. */
|
|
243
|
+
export function unityProjectProblem(dir) {
|
|
244
|
+
if (!win.isAbsolute(dir))
|
|
245
|
+
return 'Give the full path of the Unity project folder.';
|
|
246
|
+
const version = readBoundedFile(win.join(dir, 'ProjectSettings', 'ProjectVersion.txt'), 64 * 1024);
|
|
247
|
+
if (!version || !parseProjectVersion(version.toString('utf8')))
|
|
248
|
+
return dir + ' is not a Unity project (no ProjectSettings\\ProjectVersion.txt).';
|
|
249
|
+
return readBoundedFile(win.join(dir, 'Packages', 'manifest.json'), MAX_MANIFEST_READ)
|
|
250
|
+
? null
|
|
251
|
+
: dir + ' has no Packages\\manifest.json.';
|
|
252
|
+
}
|
|
253
|
+
function manifestOf(dir) {
|
|
254
|
+
const problem = unityProjectProblem(dir);
|
|
255
|
+
if (problem)
|
|
256
|
+
fail(problem, 'Pick the folder that has ProjectSettings and Packages in it.');
|
|
257
|
+
const file = win.join(dir, 'Packages', 'manifest.json');
|
|
258
|
+
assertNoLinks(dir, file);
|
|
259
|
+
const text = readBoundedFile(file, MAX_MANIFEST_READ).toString('utf8');
|
|
260
|
+
const version = parseProjectVersion(readBoundedFile(win.join(dir, 'ProjectSettings', 'ProjectVersion.txt'), 64 * 1024).toString('utf8'));
|
|
261
|
+
return { file, text, unity: version.version };
|
|
262
|
+
}
|
|
263
|
+
/** Adds the editor bridge to a project's manifest after the confirmation; the rest of the file is kept. */
|
|
264
|
+
export async function addBridgeToProject(dir, wanted, hooks) {
|
|
265
|
+
const { file, text, unity } = manifestOf(dir);
|
|
266
|
+
const have = bridgeOf(text);
|
|
267
|
+
if (have)
|
|
268
|
+
return { file, message: win.basename(dir) + ' has the editor bridge already (' + bridgeName(have) + ').' };
|
|
269
|
+
const pipeline = supportsPipeline(unity);
|
|
270
|
+
const bridge = wanted ?? (pipeline ? 'pipeline' : 'coplay');
|
|
271
|
+
if (bridge === 'pipeline' && !pipeline)
|
|
272
|
+
fail('Unity ' + unity + ' cannot take the Unity Pipeline package.', 'It needs Unity 6 or later: use the other bridge.');
|
|
273
|
+
let version = DEFAULT_PIPELINE_VERSION;
|
|
274
|
+
let note = '';
|
|
275
|
+
if (bridge === 'pipeline') {
|
|
276
|
+
try {
|
|
277
|
+
version = await hooks.pipelineVersion();
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
note = ' (could not ask Unity for the newest version: ' + errMsg(e) + '; used ' + version + ')';
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const what = bridge === 'pipeline'
|
|
284
|
+
? 'com.unity.pipeline ' + version
|
|
285
|
+
: 'MCP for Unity (' + COPLAY_PACKAGE_URL.split('?')[0] + ')';
|
|
286
|
+
if (!(await hooks.confirm(file, what)))
|
|
287
|
+
return { file, message: 'Nothing was changed.' };
|
|
288
|
+
writeFileAtomic(file, withBridge(text, bridge, true, version));
|
|
289
|
+
return {
|
|
290
|
+
file,
|
|
291
|
+
message: 'Added ' +
|
|
292
|
+
what +
|
|
293
|
+
' to ' +
|
|
294
|
+
file +
|
|
295
|
+
note +
|
|
296
|
+
'.' +
|
|
297
|
+
(bridge === 'coplay' ? ' Turn on MCP for Unity in Apps so agents can reach it.' : ''),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/** Takes the editor bridge out of a project's manifest after the confirmation. */
|
|
301
|
+
export async function removeBridgeFromProject(dir, hooks) {
|
|
302
|
+
const { file, text } = manifestOf(dir);
|
|
303
|
+
const have = bridgeOf(text);
|
|
304
|
+
if (!have)
|
|
305
|
+
return { file, message: win.basename(dir) + ' has no editor bridge.' };
|
|
306
|
+
if (!(await hooks.confirm(file, 'remove ' + bridgeName(have))))
|
|
307
|
+
return { file, message: 'Nothing was changed.' };
|
|
308
|
+
writeFileAtomic(file, withBridge(text, have, false));
|
|
309
|
+
return { file, message: 'Removed ' + bridgeName(have) + ' from ' + file + '.' };
|
|
310
|
+
}
|
|
311
|
+
const bridgeName = (b) => (b === 'pipeline' ? 'Unity Pipeline' : 'MCP for Unity');
|
|
312
|
+
async function projectOf(ctx, given, io) {
|
|
313
|
+
if (given)
|
|
314
|
+
return win.resolve(given);
|
|
315
|
+
let known = [];
|
|
316
|
+
try {
|
|
317
|
+
known = (await new ProjectsService(ctx).list()).projects
|
|
318
|
+
.map((p) => p.folder)
|
|
319
|
+
.filter((f) => unityProjectProblem(f) === null);
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
// No project list: ask for the folder.
|
|
323
|
+
}
|
|
324
|
+
if (known.length === 1)
|
|
325
|
+
return known[0];
|
|
326
|
+
if (known.length > 1) {
|
|
327
|
+
const picked = await io.choose({
|
|
328
|
+
title: 'Which Unity project?',
|
|
329
|
+
lines: [],
|
|
330
|
+
choices: known.map((f) => ({ value: f, label: win.basename(f), hint: f })),
|
|
331
|
+
});
|
|
332
|
+
if (picked)
|
|
333
|
+
return picked;
|
|
334
|
+
}
|
|
335
|
+
const dir = await io.path({
|
|
336
|
+
title: 'Which Unity project folder?',
|
|
337
|
+
lines: ['The folder that holds Assets, Packages and ProjectSettings.'],
|
|
338
|
+
pathKind: 'dir',
|
|
339
|
+
validate: (v) => unityProjectProblem(win.resolve(v)) ?? true,
|
|
340
|
+
});
|
|
341
|
+
if (!dir)
|
|
342
|
+
fail('No folder given.', 'Nothing was changed.');
|
|
343
|
+
return win.resolve(dir);
|
|
344
|
+
}
|
|
345
|
+
export const addBridgeRun = implement(addEditorBridge, async (ctx, input, io) => {
|
|
346
|
+
const dir = await projectOf(ctx, input.project, io);
|
|
347
|
+
const edit = await addBridgeToProject(dir, input.bridge, {
|
|
348
|
+
pipelineVersion: () => latestPipelineVersion(),
|
|
349
|
+
confirm: (file, what) => io.confirm({
|
|
350
|
+
title: 'Change the project?',
|
|
351
|
+
lines: ['This adds ' + what + ' to ' + file + '.', 'Nothing else in the project changes.'],
|
|
352
|
+
yes: 'Add',
|
|
353
|
+
}),
|
|
354
|
+
});
|
|
355
|
+
return { message: edit.message };
|
|
356
|
+
});
|
|
357
|
+
export const removeBridgeRun = implement(removeEditorBridge, async (ctx, input, io) => {
|
|
358
|
+
const dir = await projectOf(ctx, input.project, io);
|
|
359
|
+
const edit = await removeBridgeFromProject(dir, {
|
|
360
|
+
confirm: (file, what) => io.confirm({ title: 'Change the project?', lines: ['This will ' + what + ' in ' + file + '.'], yes: 'Remove' }),
|
|
361
|
+
});
|
|
362
|
+
return { message: edit.message };
|
|
363
|
+
});
|
|
@@ -19,7 +19,11 @@ import { ConfigFile } from '../settings/index.js';
|
|
|
19
19
|
import { forgetEngineFolder, openEpicLauncher, registerEngineFolder, unrealOverview, } from './unreal.def.js';
|
|
20
20
|
const win = path.win32;
|
|
21
21
|
/** The launcher the sandbox installs (the 64-bit one of launcher 17 and later). */
|
|
22
|
-
|
|
22
|
+
/** Where the launcher installs in the sandbox: Program Files for newer launchers, (x86) for older ones. */
|
|
23
|
+
const GUEST_LAUNCHERS = [
|
|
24
|
+
'C:\\Program Files\\Epic Games\\Launcher\\Portal\\Binaries\\Win64\\EpicGamesLauncher.exe',
|
|
25
|
+
'C:\\Program Files (x86)\\Epic Games\\Launcher\\Portal\\Binaries\\Win64\\EpicGamesLauncher.exe',
|
|
26
|
+
];
|
|
23
27
|
const MAX_PACKS_SHOWN = 40;
|
|
24
28
|
const s = (text) => safeText(text);
|
|
25
29
|
const sn = (text) => (text === null ? null : safeText(text));
|
|
@@ -204,10 +208,16 @@ export const openEpicLauncherRun = implement(openEpicLauncher, async (ctx, _inpu
|
|
|
204
208
|
const status = new RunDir(ctx.dataPath).readSandboxStatus();
|
|
205
209
|
if (status?.phase !== 'ready')
|
|
206
210
|
fail('The sandbox is not ready yet.', 'Try again in a moment.');
|
|
207
|
-
|
|
211
|
+
const launcher = readStatus(ctx.dataPath)?.launcher;
|
|
212
|
+
if (!launcher?.installed)
|
|
208
213
|
fail('The Epic Games Launcher is not installed in this sandbox.', 'It is installed when the sandbox is set up for Unreal with the licence accepted (Unreal set-up settings).');
|
|
209
214
|
io.progress('Opening the Epic Games Launcher in the sandbox…');
|
|
210
|
-
const result = await new SandboxChannel(ctx.dataPath).exec(
|
|
215
|
+
const result = await new SandboxChannel(ctx.dataPath).exec(
|
|
216
|
+
// The guest's status only picks one of the known paths.
|
|
217
|
+
[
|
|
218
|
+
'C:\\Windows\\explorer.exe',
|
|
219
|
+
GUEST_LAUNCHERS.find((p) => p.toLowerCase() === launcher.exe?.toLowerCase()) ?? GUEST_LAUNCHERS[0],
|
|
220
|
+
], status.guestWorkspace ?? 'C:\\', 30);
|
|
211
221
|
// explorer.exe returns 1 even when it started the program.
|
|
212
222
|
if (!result.ok && result.exitCode !== 1)
|
|
213
223
|
fail('The sandbox could not open it: ' + safeText(result.message));
|
package/dist/app/registry.js
CHANGED
|
@@ -21,6 +21,7 @@ import { vpnLoginRun, vpnLogoutRun, vpnStatusRun } from './features/vpn/index.js
|
|
|
21
21
|
import { githubLoginRun, githubLogoutRun, githubStatusRun } from './features/github/index.js';
|
|
22
22
|
import { accessStatusRun, allowDeviceRun, lanOffRun, lanOnRun, revokeAccessRun, revokeAllAccessRun, } from './features/access/index.js';
|
|
23
23
|
import { forgetEngineFolderRun, openEpicLauncherRun, registerEngineFolderRun, unrealOverviewRun, } from './features/unreal/index.js';
|
|
24
|
+
import { addBridgeRun, openUnityHubRun, removeBridgeRun, unityOverviewRun, unitySignInRun, } from './features/unity/index.js';
|
|
24
25
|
export const IMPLEMENTATIONS = [
|
|
25
26
|
sandboxStatusRun,
|
|
26
27
|
showWindowRun,
|
|
@@ -114,6 +115,11 @@ export const IMPLEMENTATIONS = [
|
|
|
114
115
|
registerEngineFolderRun,
|
|
115
116
|
forgetEngineFolderRun,
|
|
116
117
|
openEpicLauncherRun,
|
|
118
|
+
unityOverviewRun,
|
|
119
|
+
openUnityHubRun,
|
|
120
|
+
unitySignInRun,
|
|
121
|
+
addBridgeRun,
|
|
122
|
+
removeBridgeRun,
|
|
117
123
|
];
|
|
118
124
|
export function implementationOf(id) {
|
|
119
125
|
return IMPLEMENTATIONS.find((i) => i.def.id === id);
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
run
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-QUHNXHVG.js";
|
|
6
|
+
import "./chunk-RSIX7C6D.js";
|
|
7
7
|
|
|
8
8
|
// src/bin/agent-sandbox.ts
|
|
9
9
|
run(process.argv.slice(2));
|
|
@@ -223,6 +223,9 @@ import {
|
|
|
223
223
|
takeHiddenInstallNotice,
|
|
224
224
|
toWireError,
|
|
225
225
|
trustStorePath,
|
|
226
|
+
unityOverview,
|
|
227
|
+
unityPage,
|
|
228
|
+
unityView,
|
|
226
229
|
unrealOverview,
|
|
227
230
|
unrealPage,
|
|
228
231
|
unrealView,
|
|
@@ -254,7 +257,7 @@ import {
|
|
|
254
257
|
withOperationCommands,
|
|
255
258
|
working,
|
|
256
259
|
writeJson
|
|
257
|
-
} from "./chunk-
|
|
260
|
+
} from "./chunk-RSIX7C6D.js";
|
|
258
261
|
|
|
259
262
|
// node_modules/commander/lib/error.js
|
|
260
263
|
var require_error = __commonJS({
|
|
@@ -3987,7 +3990,7 @@ var COMMAND_LIST = [
|
|
|
3987
3990
|
},
|
|
3988
3991
|
{
|
|
3989
3992
|
name: "name",
|
|
3990
|
-
description: "account name, or the service (status, import, login, logout): epic, steam, unity, cfx, keystore, play, expo, appstore, perplexity or
|
|
3993
|
+
description: "account name, or the service (status, import, login, logout): epic, steam, unity, cfx, keystore, play, expo, appstore, perplexity, comet or perplexityapp",
|
|
3991
3994
|
required: false,
|
|
3992
3995
|
choices: "accounts"
|
|
3993
3996
|
}
|
|
@@ -4095,6 +4098,37 @@ var COMMAND_LIST = [
|
|
|
4095
4098
|
],
|
|
4096
4099
|
options: []
|
|
4097
4100
|
},
|
|
4101
|
+
{
|
|
4102
|
+
name: "unity",
|
|
4103
|
+
summary: "Unity: the sandbox's editors, this PC's editors and Asset Store downloads, the licence, the editor bridge, Unity Hub",
|
|
4104
|
+
group: "Projects & accounts",
|
|
4105
|
+
json,
|
|
4106
|
+
examples: [
|
|
4107
|
+
"sandbox unity editors, which project opens with which, the licence",
|
|
4108
|
+
"sandbox unity sign-in sign in to Unity in the running sandbox",
|
|
4109
|
+
"sandbox unity add-bridge D:\\Game add the editor bridge to a project (asks first)"
|
|
4110
|
+
],
|
|
4111
|
+
args: [
|
|
4112
|
+
{
|
|
4113
|
+
name: "action",
|
|
4114
|
+
description: "status, hub, sign-in, add-bridge, remove-bridge",
|
|
4115
|
+
required: false,
|
|
4116
|
+
choices: ["status"]
|
|
4117
|
+
},
|
|
4118
|
+
{
|
|
4119
|
+
name: "project",
|
|
4120
|
+
description: "(add-bridge, remove-bridge) the Unity project folder",
|
|
4121
|
+
required: false,
|
|
4122
|
+
when: []
|
|
4123
|
+
}
|
|
4124
|
+
],
|
|
4125
|
+
options: [
|
|
4126
|
+
text("--bridge", "name", "(add-bridge) pipeline (Unity 6 and later) or coplay (older Unity)", {
|
|
4127
|
+
when: "add-bridge",
|
|
4128
|
+
label: "Bridge"
|
|
4129
|
+
})
|
|
4130
|
+
]
|
|
4131
|
+
},
|
|
4098
4132
|
{
|
|
4099
4133
|
name: "engine",
|
|
4100
4134
|
summary: "(beta) what runs your sandbox (Windows Sandbox or HCS) and what each engine can do on this PC",
|
|
@@ -6768,6 +6802,7 @@ var MENU_VIEWS = [
|
|
|
6768
6802
|
{ view: appsView, section: "Apps" },
|
|
6769
6803
|
{ view: projectsView, section: "Sandbox" },
|
|
6770
6804
|
{ view: unrealView, section: "Sandbox" },
|
|
6805
|
+
{ view: unityView, section: "Sandbox" },
|
|
6771
6806
|
{ view: healthView, section: "Maintenance" },
|
|
6772
6807
|
{ view: dataView, section: "Maintenance" },
|
|
6773
6808
|
{ view: storageView, section: "Maintenance" },
|
|
@@ -8649,11 +8684,19 @@ var CATEGORIES3 = [
|
|
|
8649
8684
|
id: "unreal",
|
|
8650
8685
|
label: "Unreal Engine",
|
|
8651
8686
|
description: "Engines, which project uses which, this PC's engines and assets, Visual Studio and the debugger."
|
|
8687
|
+
},
|
|
8688
|
+
{
|
|
8689
|
+
id: "unity",
|
|
8690
|
+
label: "Unity",
|
|
8691
|
+
description: "Editors, this PC's editors and Asset Store downloads, the licence, the editor bridge and Unity Hub."
|
|
8652
8692
|
}
|
|
8653
8693
|
];
|
|
8654
8694
|
function unrealMenu(m) {
|
|
8655
8695
|
return m.unreal ? menuView(unrealPage(m.unreal), { category: "unreal", prefix: "unreal:", refresh: false }) : void 0;
|
|
8656
8696
|
}
|
|
8697
|
+
function unityMenu(m) {
|
|
8698
|
+
return m.unity ? menuView(unityPage(m.unity), { category: "unity", prefix: "unity:", refresh: false }) : void 0;
|
|
8699
|
+
}
|
|
8657
8700
|
function statusView(m) {
|
|
8658
8701
|
return menuView(sandboxPage(m.report), { category: "status", prefix: "status:", refresh: false });
|
|
8659
8702
|
}
|
|
@@ -8663,6 +8706,7 @@ function projectsView2(m) {
|
|
|
8663
8706
|
function viewFor(m, category) {
|
|
8664
8707
|
if (category === "projects") return projectsView2(m);
|
|
8665
8708
|
if (category === "unreal") return unrealMenu(m);
|
|
8709
|
+
if (category === "unity") return unityMenu(m);
|
|
8666
8710
|
if (category === "status" || category === void 0) return statusView(m);
|
|
8667
8711
|
return void 0;
|
|
8668
8712
|
}
|
|
@@ -8700,12 +8744,32 @@ function sandboxOptions(m) {
|
|
|
8700
8744
|
disabled: "l looks again."
|
|
8701
8745
|
}
|
|
8702
8746
|
];
|
|
8747
|
+
const unity = unityMenu(m);
|
|
8748
|
+
const noUnity = unity ? [] : [
|
|
8749
|
+
{
|
|
8750
|
+
value: "info:unity",
|
|
8751
|
+
label: "Could not read the Unity set-up",
|
|
8752
|
+
category: "unity",
|
|
8753
|
+
section: "Unity",
|
|
8754
|
+
description: "l looks again.",
|
|
8755
|
+
disabled: "l looks again."
|
|
8756
|
+
}
|
|
8757
|
+
];
|
|
8703
8758
|
return {
|
|
8704
8759
|
path: [],
|
|
8705
|
-
items: [
|
|
8760
|
+
items: [
|
|
8761
|
+
...statusItems,
|
|
8762
|
+
...projects.items,
|
|
8763
|
+
...databases,
|
|
8764
|
+
...noDatabases,
|
|
8765
|
+
...unreal?.items ?? [],
|
|
8766
|
+
...noUnreal,
|
|
8767
|
+
...unity?.items ?? [],
|
|
8768
|
+
...noUnity
|
|
8769
|
+
],
|
|
8706
8770
|
categories: CATEGORIES3,
|
|
8707
8771
|
empty: "Nothing here yet.",
|
|
8708
|
-
actions: (category) => category === "projects" ? [...projects.actions, REFRESH] : category === "databases" ? [REFRESH] : category === "unreal" ? [...unreal?.actions ?? [], REFRESH] : [...status.actions, REFRESH],
|
|
8772
|
+
actions: (category) => category === "projects" ? [...projects.actions, REFRESH] : category === "databases" ? [REFRESH] : category === "unreal" ? [...unreal?.actions ?? [], REFRESH] : category === "unity" ? [...unity?.actions ?? [], REFRESH] : [...status.actions, REFRESH],
|
|
8709
8773
|
enterLabel: (category, id) => viewFor(m, category)?.enterLabel(category, id) || enterLabel2(id ?? "", m)
|
|
8710
8774
|
};
|
|
8711
8775
|
}
|
|
@@ -8759,7 +8823,8 @@ var SandboxPage = class extends ListPage {
|
|
|
8759
8823
|
projects: await readQuery(listProjects, this.ctx),
|
|
8760
8824
|
databases,
|
|
8761
8825
|
defaultAgent: this.defaultAgent(),
|
|
8762
|
-
unreal: await readQuery(unrealOverview, this.ctx).catch(() => null)
|
|
8826
|
+
unreal: await readQuery(unrealOverview, this.ctx).catch(() => null),
|
|
8827
|
+
unity: await readQuery(unityOverview, this.ctx).catch(() => null)
|
|
8763
8828
|
};
|
|
8764
8829
|
}
|
|
8765
8830
|
/** The folder `sbx-db` runs in: the sandbox's own workspace. */
|
|
@@ -10531,6 +10596,8 @@ var HANDLERS = {
|
|
|
10531
10596
|
engine,
|
|
10532
10597
|
// The bare command shows the overview; its actions (add-engine, forget-engine, launcher) are the operations' hints.
|
|
10533
10598
|
unreal: definitionHandler(unrealOverview),
|
|
10599
|
+
// Same for Unity: its actions (hub, sign-in, add-bridge, remove-bridge) are the operations' hints.
|
|
10600
|
+
unity: definitionHandler(unityOverview),
|
|
10534
10601
|
"host-claude": claudeHost,
|
|
10535
10602
|
host: hostCommand,
|
|
10536
10603
|
_watch: watcher,
|