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
|
@@ -1,4 +1,251 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// Unity (toolchains.unity), for each project: the Unity CLI on PATH (pinned, checked), every editor the sandbox has
|
|
2
|
+
// (this PC's, read-only, and the data disk's) registered with it, the editor the project's ProjectVersion.txt names
|
|
3
|
+
// (installed on the data disk when nothing has that exact version), UNITY_EDITOR, the Asset Store cache, the licence,
|
|
4
|
+
// Unity Hub when asked and the agent's MCP server and skills. Machine-wide writes only add; a project that joins
|
|
5
|
+
// later only adds its own editor choice. A project is never opened or changed here.
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { readBoundedFile } from '../../../shared/io/bounded.js';
|
|
9
|
+
import { chooseEditor, editorsAddArgs, findEditors, installArgs, installModulesArgs, installNeedBytes, installPathArgs, unitySummaryLine, wantedVersion, } from '../../../shared/unity/index.js';
|
|
10
|
+
import { errMsg } from '../../../core/util/errors.js';
|
|
11
|
+
import { activateUnityLicence } from '../../accounts/kinds/unity.js';
|
|
12
|
+
import { exists, mkdirp } from '../../runtime/fs.js';
|
|
13
|
+
import { log, warn } from '../../runtime/log.js';
|
|
14
|
+
import { dryRun } from '../../runtime/paths.js';
|
|
15
|
+
import { heavy } from '../../runtime/pools.js';
|
|
16
|
+
import { runScriptWithJsonAsync } from '../../runtime/process.js';
|
|
17
|
+
import { projectWorkspace } from '../../runtime/project-context.js';
|
|
18
|
+
import { session } from '../../runtime/session.js';
|
|
19
|
+
import { setupUnityAgentTools } from '../../setup/unity-agent.js';
|
|
20
|
+
import { ensurePersisted, installRecords } from '../persisted.js';
|
|
21
|
+
import { ensureUnityCli, runUnityCli, runUnityCliChecked } from './unity-cli.js';
|
|
22
|
+
import { ensureUnityHub } from './unity-hub.js';
|
|
23
|
+
import { findUnityRelease } from './unity-release.js';
|
|
24
|
+
import { ASSET_DIR, EDITORS_DIR, readUnityStatus, updateUnityStatus } from './unity-status.js';
|
|
25
|
+
const MAX_PROJECT_VERSION_BYTES = 64 * 1024;
|
|
26
|
+
const MARKER = 'Editor\\Unity.exe';
|
|
27
|
+
const TERMS = 'https://unity.com/legal/terms-of-service';
|
|
28
|
+
const FALLBACK_NEED_BYTES = 15 * 1024 ** 3;
|
|
29
|
+
/** The real disk, for the editor search. */
|
|
30
|
+
const DISK = {
|
|
31
|
+
exists: (file) => exists(file),
|
|
32
|
+
list: (dir) => {
|
|
33
|
+
try {
|
|
34
|
+
return fs.readdirSync(dir).slice(0, 500);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return []; // Not a folder: nothing in it.
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
/** This PC's editors the host mapped (session.json's pcInstalls; the id is the version). */
|
|
42
|
+
function mappedEditors(ctx) {
|
|
43
|
+
return ctx.pcInstalls.items.filter((i) => i.kind === 'unity').map((i) => ({ guest: i.guest, id: i.id }));
|
|
44
|
+
}
|
|
45
|
+
/** Every editor this sandbox has. */
|
|
46
|
+
export function sandboxEditors(ctx) {
|
|
47
|
+
return findEditors({ mapped: mappedEditors(ctx), dataRoot: EDITORS_DIR }, DISK);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The editor a project asks for: its ProjectVersion.txt (read bounded) always wins, since opening a project in another
|
|
51
|
+
* version upgrades its files; the version the setting names is only for folders without one.
|
|
52
|
+
*/
|
|
53
|
+
export function wantedEditor(config, workspace) {
|
|
54
|
+
const file = path.join(workspace, 'ProjectSettings', 'ProjectVersion.txt');
|
|
55
|
+
const bytes = readBoundedFile(file, MAX_PROJECT_VERSION_BYTES);
|
|
56
|
+
return wantedVersion(bytes ? bytes.toString('utf8') : null, config.version);
|
|
57
|
+
}
|
|
58
|
+
/** Whether the project's editor is already here (a first install goes to the background). */
|
|
59
|
+
export function unityEditorSaved(config, ctx) {
|
|
60
|
+
const wanted = wantedEditor(config, projectWorkspace());
|
|
61
|
+
if (!wanted)
|
|
62
|
+
return true;
|
|
63
|
+
return sandboxEditors(ctx).some((e) => e.version === wanted.version);
|
|
64
|
+
}
|
|
65
|
+
function sizeOf(e) {
|
|
66
|
+
if (e.origin !== 'data')
|
|
67
|
+
return null;
|
|
68
|
+
return installRecords().find((r) => r.name === 'unity/editors' && r.version === e.version)?.sizeBytes ?? null;
|
|
69
|
+
}
|
|
70
|
+
const statusEditor = (e) => ({
|
|
71
|
+
version: e.version,
|
|
72
|
+
root: e.root,
|
|
73
|
+
origin: e.origin,
|
|
74
|
+
writable: e.writable,
|
|
75
|
+
modules: [...e.modules],
|
|
76
|
+
sizeBytes: sizeOf(e),
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* Installs one editor version on the data disk with the CLI (into a staging folder with TEMP inside it) and returns its
|
|
80
|
+
* folder. One heavy slot while the CLI runs.
|
|
81
|
+
*/
|
|
82
|
+
export async function ensureUnityEditor(cli, version, changeset, modules, keepVersions) {
|
|
83
|
+
const release = await findUnityRelease(version);
|
|
84
|
+
const cs = changeset ?? release?.changeset ?? null;
|
|
85
|
+
const need = release ? installNeedBytes(release, modules) : FALLBACK_NEED_BYTES;
|
|
86
|
+
const installed = await ensurePersisted({ name: 'unity/editors', version, needBytes: need, marker: MARKER, what: 'Unity ' + version, keepVersions }, async (staging, temp) => {
|
|
87
|
+
log('Installing Unity ' + version + ' on the data disk (several GB; this takes a while) ...');
|
|
88
|
+
await heavy.run(() => runUnityCliChecked(cli.exe, installArgs(version, cs, modules, staging), { temp }));
|
|
89
|
+
});
|
|
90
|
+
return installed.dir;
|
|
91
|
+
}
|
|
92
|
+
/** Adds modules to an editor on the data disk. */
|
|
93
|
+
async function addModules(cli, editor, modules) {
|
|
94
|
+
const temp = path.join(editor.root, '.temp');
|
|
95
|
+
mkdirp(temp);
|
|
96
|
+
try {
|
|
97
|
+
log('Adding to Unity ' + editor.version + ': ' + modules.join(', ') + ' ...');
|
|
98
|
+
const args = installModulesArgs(editor.version, modules, editor.root);
|
|
99
|
+
await heavy.run(() => runUnityCliChecked(cli.exe, args, { temp }));
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
fs.rmSync(temp, { recursive: true, force: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** While the sandbox runs: lists the editors again (the user may have installed one with Hub) for the status. */
|
|
106
|
+
export function captureUnityEditors() {
|
|
107
|
+
const unity = session.toolchains.unity;
|
|
108
|
+
if (dryRun || unity.enabled !== true || !exists(EDITORS_DIR))
|
|
109
|
+
return Promise.resolve();
|
|
110
|
+
const editors = sandboxEditors({ pcInstalls: session.pcInstalls ?? { items: [] } });
|
|
111
|
+
const describe = (list) => list.map((e) => e.root + '|' + e.modules.join(',')).join(';');
|
|
112
|
+
if (describe(editors) !== describe(readUnityStatus().editors))
|
|
113
|
+
updateUnityStatus((s) => ({ ...s, editors: editors.slice(0, 32).map(statusEditor) }));
|
|
114
|
+
return Promise.resolve();
|
|
115
|
+
}
|
|
116
|
+
/** The editors, the project's choice, the CLI, the licence, the asset cache: set up and written to the status. */
|
|
117
|
+
export async function setupUnity(env, config, ctx) {
|
|
118
|
+
if (!config.acceptLicense)
|
|
119
|
+
throw new Error('Unity is set up only with toolchains.unity.acceptLicense = true: you accept the Unity terms of service (' +
|
|
120
|
+
TERMS +
|
|
121
|
+
') and the editor needs a Unity licence (Personal is free).');
|
|
122
|
+
const workspace = projectWorkspace();
|
|
123
|
+
if (dryRun) {
|
|
124
|
+
log('(dry run) would set up Unity ' + config.version + ' for ' + workspace);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const cli = await ensureUnityCli(config.cliVersion);
|
|
128
|
+
env.prepend.push(path.dirname(cli.exe));
|
|
129
|
+
let problem = null;
|
|
130
|
+
const quiet = (what) => (e) => warn('Unity CLI: ' + what + ' (' + errMsg(e) + ')');
|
|
131
|
+
// Settings that only keep the CLI quiet: a failure is not worth stopping for.
|
|
132
|
+
await runUnityCli(cli.exe, ['config', 'update-check', 'off'], { timeoutMs: 60_000 }).catch(quiet('update-check'));
|
|
133
|
+
await runUnityCli(cli.exe, ['analytics', 'opt-out'], { timeoutMs: 60_000 }).catch(quiet('analytics'));
|
|
134
|
+
mkdirp(EDITORS_DIR);
|
|
135
|
+
await runUnityCli(cli.exe, installPathArgs(EDITORS_DIR), { timeoutMs: 60_000 }).catch(quiet('install-path'));
|
|
136
|
+
let editors = sandboxEditors(ctx);
|
|
137
|
+
if (editors.length)
|
|
138
|
+
await runUnityCli(cli.exe, editorsAddArgs(editors.map((e) => e.root)), { timeoutMs: 120_000 }).catch(quiet('editors add'));
|
|
139
|
+
const wanted = wantedEditor(config, workspace);
|
|
140
|
+
let choice = chooseEditor(editors, wanted, config.modules, true);
|
|
141
|
+
const install = choice.install;
|
|
142
|
+
if (install) {
|
|
143
|
+
try {
|
|
144
|
+
const { version, changeset } = install;
|
|
145
|
+
await ensureUnityEditor(cli, version, changeset, config.modules, config.keepVersions);
|
|
146
|
+
editors = sandboxEditors(ctx);
|
|
147
|
+
choice = chooseEditor(editors, wanted, config.modules, true);
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
problem = 'Unity ' + install.version + ' was not installed: ' + errMsg(e);
|
|
151
|
+
warn(problem);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
let editor = choice.editor;
|
|
155
|
+
if (editor) {
|
|
156
|
+
const have = editor.modules;
|
|
157
|
+
const lack = config.modules.filter((m) => !have.includes(m));
|
|
158
|
+
if (lack.length && editor.writable) {
|
|
159
|
+
try {
|
|
160
|
+
await addModules(cli, editor, lack);
|
|
161
|
+
editors = sandboxEditors(ctx);
|
|
162
|
+
const root = editor.root;
|
|
163
|
+
editor = editors.find((e) => e.root === root) ?? editor;
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
problem = 'Unity modules were not added: ' + errMsg(e);
|
|
167
|
+
warn(problem);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (lack.length) {
|
|
171
|
+
problem =
|
|
172
|
+
'Unity ' +
|
|
173
|
+
editor.version +
|
|
174
|
+
' from this PC has no ' +
|
|
175
|
+
lack[0] +
|
|
176
|
+
" module: add it with Unity Hub on your PC, or remove that editor from This PC's editors to install one here.";
|
|
177
|
+
warn(problem);
|
|
178
|
+
}
|
|
179
|
+
env.vars.UNITY_EDITOR = editor.exe;
|
|
180
|
+
}
|
|
181
|
+
// The Asset Store cache: where downloads made here go (HKCU too, so an editor started from Explorer agrees).
|
|
182
|
+
// For "pc" the mapped Asset Store folder (guest of the unity-assets item) sits inside the cache root.
|
|
183
|
+
const pcAssets = ctx.pcInstalls.items.filter((i) => i.kind === 'unity-assets').map((i) => i.guest);
|
|
184
|
+
let cacheDir = null;
|
|
185
|
+
if (config.assetCache === 'persisted')
|
|
186
|
+
cacheDir = mkdirp(ASSET_DIR);
|
|
187
|
+
else if (config.assetCache === 'pc' && pcAssets[0])
|
|
188
|
+
cacheDir = path.win32.dirname(pcAssets[0]);
|
|
189
|
+
if (cacheDir) {
|
|
190
|
+
env.vars.ASSETSTORE_CACHE_PATH = cacheDir;
|
|
191
|
+
if (!ctx.duringSession)
|
|
192
|
+
await runScriptWithJsonAsync('set-registry-values.ps1', 'PlanFile', {
|
|
193
|
+
values: [{ hive: 'HKCU', key: 'Environment', name: 'ASSETSTORE_CACHE_PATH', value: cacheDir }],
|
|
194
|
+
}).catch((e) => warn('Unity: the Asset Store cache was not set for Explorer (' + errMsg(e) + ')'));
|
|
195
|
+
}
|
|
196
|
+
const licence = await activateUnityLicence({ editorExe: editor?.exe ?? null, unityExe: cli.exe }).catch((e) => {
|
|
197
|
+
warn('Unity licence: ' + errMsg(e));
|
|
198
|
+
return readUnityStatus().licence;
|
|
199
|
+
});
|
|
200
|
+
let hub = readUnityStatus().hub;
|
|
201
|
+
if (config.hub)
|
|
202
|
+
try {
|
|
203
|
+
hub = { installed: true, path: await ensureUnityHub() };
|
|
204
|
+
}
|
|
205
|
+
catch (e) {
|
|
206
|
+
warn('Unity Hub was not installed: ' + errMsg(e));
|
|
207
|
+
}
|
|
208
|
+
const agent = await setupUnityAgentTools({
|
|
209
|
+
unityExe: cli.exe,
|
|
210
|
+
projectPath: workspace,
|
|
211
|
+
mcp: config.mcp,
|
|
212
|
+
skills: config.agentSkills,
|
|
213
|
+
}).catch((e) => {
|
|
214
|
+
warn('Unity agent tools: ' + errMsg(e));
|
|
215
|
+
return { mcp: null, skills: false };
|
|
216
|
+
});
|
|
217
|
+
const chosen = editor;
|
|
218
|
+
const status = updateUnityStatus((s) => {
|
|
219
|
+
const mine = s.projects.find((p) => p.workspace.toLowerCase() === workspace.toLowerCase());
|
|
220
|
+
return {
|
|
221
|
+
...s,
|
|
222
|
+
cli: { version: cli.version, path: cli.exe },
|
|
223
|
+
editors: editors.slice(0, 32).map(statusEditor),
|
|
224
|
+
projects: [
|
|
225
|
+
...s.projects.filter((p) => p !== mine),
|
|
226
|
+
{
|
|
227
|
+
workspace,
|
|
228
|
+
version: wanted?.version ?? null,
|
|
229
|
+
editor: chosen?.root ?? null,
|
|
230
|
+
reason: choice.reason,
|
|
231
|
+
bridge: mine?.bridge ?? null,
|
|
232
|
+
},
|
|
233
|
+
].slice(-64),
|
|
234
|
+
licence,
|
|
235
|
+
assetCache: { mode: config.assetCache, dir: cacheDir },
|
|
236
|
+
pcAssets: pcAssets.slice(0, 4),
|
|
237
|
+
hub,
|
|
238
|
+
mcp: agent.mcp ?? (ctx.duringSession ? s.mcp : null),
|
|
239
|
+
skills: agent.skills || (ctx.duringSession && s.skills),
|
|
240
|
+
problem,
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
env.summary.push(unitySummaryLine(status, workspace));
|
|
244
|
+
log('Unity: ' +
|
|
245
|
+
editors.length +
|
|
246
|
+
' editor(s); ' +
|
|
247
|
+
(editor ? editor.version + ' at ' + editor.root : 'none for this project') +
|
|
248
|
+
' (' +
|
|
249
|
+
choice.reason +
|
|
250
|
+
')');
|
|
4
251
|
}
|
|
@@ -18,7 +18,7 @@ const EMPTY = {
|
|
|
18
18
|
updatedAt: '',
|
|
19
19
|
engines: [],
|
|
20
20
|
projects: [],
|
|
21
|
-
launcher: { installed: false, installLocation: null, problem: null, freeBytes: null, needBytes: 0 },
|
|
21
|
+
launcher: { installed: false, exe: null, installLocation: null, problem: null, freeBytes: null, needBytes: 0 },
|
|
22
22
|
visualStudio: { version: '', path: '', how: '', check: '', ok: false },
|
|
23
23
|
debugger: null,
|
|
24
24
|
ddc: { mode: 'local', dir: null },
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Mesa for running OpenGL and Vulkan programs without a GPU (llvmpipe, lavapipe).
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { downloader } from '../../../shared/download/index.js';
|
|
3
|
+
import { downloader, fetchJson } from '../../../shared/download/index.js';
|
|
4
4
|
import { SYSTEM } from '../../../shared/system.js';
|
|
5
5
|
import { dryRun } from '../../runtime/paths.js';
|
|
6
6
|
import { execAsync, scriptOutput } from '../../runtime/process.js';
|
|
7
7
|
import { exists } from '../../runtime/fs.js';
|
|
8
|
-
import { log
|
|
8
|
+
import { log } from '../../runtime/log.js';
|
|
9
9
|
import { errMsg } from '../../../core/util/errors.js';
|
|
10
10
|
import { arch, tool } from '../tool-env.js';
|
|
11
11
|
import { installFromGithub } from '../install.js';
|
|
@@ -33,9 +33,28 @@ export async function setupMesa(env, config) {
|
|
|
33
33
|
if (config.vulkan)
|
|
34
34
|
await setupLavapipe(env, dir);
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
36
|
+
/** LunarG's newest SDK version per platform ({"windows": "1.4.363.1", ...}). */
|
|
37
|
+
const VULKAN_LATEST = 'https://vulkan.lunarg.com/sdk/latest.json';
|
|
38
|
+
const runtimeUrl = (version) => 'https://sdk.lunarg.com/sdk/download/' + version + '/windows/vulkan-runtime-components.zip';
|
|
39
|
+
/** Known to have the runtime zip: used when LunarG cannot be asked, or none of its versions has one. */
|
|
40
|
+
const VULKAN_PINNED = '1.4.363.0';
|
|
41
|
+
/**
|
|
42
|
+
* The versions to try for the runtime zip, newest first: LunarG's latest, its x.y.z.0 (a point release such as
|
|
43
|
+
* 1.4.363.1 reissues the SDK without the runtime zip), then the pinned one.
|
|
44
|
+
*/
|
|
45
|
+
async function runtimeVersions() {
|
|
46
|
+
let latest = null;
|
|
47
|
+
try {
|
|
48
|
+
const value = (await fetchJson(VULKAN_LATEST)).windows;
|
|
49
|
+
if (typeof value === 'string' && /^\d{1,2}\.\d{1,2}\.\d{1,4}\.\d{1,3}$/.test(value))
|
|
50
|
+
latest = value;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// LunarG not reachable: the pinned version.
|
|
54
|
+
}
|
|
55
|
+
const found = latest ? [latest, latest.replace(/\.\d+$/, '.0')] : [];
|
|
56
|
+
return [...new Set([...found, VULKAN_PINNED])];
|
|
57
|
+
}
|
|
39
58
|
/**
|
|
40
59
|
* Software Vulkan: Mesa's lavapipe plus the Khronos loader (vulkan-1.dll, from LunarG's runtime components; Windows
|
|
41
60
|
* Sandbox has no Vulkan loader). The loader ignores VK_DRIVER_FILES in elevated processes, and everything in the
|
|
@@ -52,12 +71,17 @@ const setupLavapipe = async (env, mesaDir) => {
|
|
|
52
71
|
// LunarG publishes no checksum for this zip; it comes over HTTPS from LunarG, the Khronos SDK's maintainer.
|
|
53
72
|
// The zip has x86 and x64 folders: only the 64-bit loader (and vulkaninfo) is extracted.
|
|
54
73
|
const fetchRuntime = (url) => downloader.archive(url, dir, { locate: 'vulkan-1.dll', fileName: 'vulkan-runtime.zip', members: ['*/x64/*'] });
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
const versions = await runtimeVersions();
|
|
75
|
+
for (const [n, version] of versions.entries()) {
|
|
76
|
+
try {
|
|
77
|
+
await fetchRuntime(runtimeUrl(version));
|
|
78
|
+
log('Vulkan loader ' + version + ' installed');
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
if (n === versions.length - 1)
|
|
83
|
+
throw new Error('the Vulkan loader could not be downloaded (' + errMsg(e) + ')');
|
|
84
|
+
}
|
|
61
85
|
}
|
|
62
86
|
}
|
|
63
87
|
const register = (file) => execAsync(SYSTEM.reg(), ['add', 'HKLM\\SOFTWARE\\Khronos\\Vulkan\\Drivers', '/v', file, '/t', 'REG_DWORD', '/d', '0', '/f'], { stdio: 'ignore' });
|
|
@@ -26,7 +26,8 @@ import { ensureVisualStudio, prefetchVisualStudio, visualStudioSaved, visualStud
|
|
|
26
26
|
import { duringSession } from '../runtime/project-context.js';
|
|
27
27
|
import { setupSteam } from './steam/steam.js';
|
|
28
28
|
import { setupGodot } from './engines/godot.js';
|
|
29
|
-
import { setupUnity } from './engines/unity.js';
|
|
29
|
+
import { setupUnity, unityEditorSaved } from './engines/unity.js';
|
|
30
|
+
import { prefetchUnityCli } from './engines/unity-cli.js';
|
|
30
31
|
import { setupFivem } from './fivem/fivem.js';
|
|
31
32
|
import { setupAndroid } from './mobile/android.js';
|
|
32
33
|
import { setupFlutter } from './mobile/flutter.js';
|
|
@@ -146,6 +147,9 @@ export function plan(config, versions, o) {
|
|
|
146
147
|
name: 'Unity',
|
|
147
148
|
enabled: wanted(config.unity.enabled, 'unity'),
|
|
148
149
|
after: ['Visual Studio', 'Android SDK'],
|
|
150
|
+
...ahead(() => prefetchUnityCli(config.unity.cliVersion)),
|
|
151
|
+
// Only the first install of an editor (several GB) goes to the background; a saved one is back in seconds.
|
|
152
|
+
background: deferred && !unityEditorSaved(config.unity, ctx),
|
|
149
153
|
install: (env) => setupUnity(env, config.unity, ctx),
|
|
150
154
|
},
|
|
151
155
|
{
|
|
@@ -34,13 +34,13 @@ export function ghExePath() {
|
|
|
34
34
|
* gh keeps its token in Windows Credential Manager unless told otherwise, and that is wiped with the sandbox: only a
|
|
35
35
|
* login made with --insecure-storage (the token in GH_CONFIG_DIR, which persists) is still there next time.
|
|
36
36
|
*/
|
|
37
|
-
const GH_NOTE = ' (log in once with: gh auth login --web --insecure-storage
|
|
37
|
+
const GH_NOTE = ' (signed in on your PC? turn on github.usePcLogin and the login comes from there; otherwise log in once with: gh auth login --web --insecure-storage, which is kept between sandboxes)';
|
|
38
38
|
/** A saved gh account whose token was in Credential Manager (gone now): say how to log in so that it lasts. */
|
|
39
39
|
function warnKeyringLogin() {
|
|
40
40
|
try {
|
|
41
41
|
const hosts = fs.readFileSync(path.join(ghConfigDir(), 'hosts.yml'), 'utf8');
|
|
42
42
|
if (/^\s*user:/m.test(hosts) && !/oauth_token:/.test(hosts))
|
|
43
|
-
warn('gh: the GitHub login from an earlier sandbox was kept in Windows Credential Manager, which is wiped with each sandbox. Log in again with: gh auth login --web --insecure-storage (then it lasts).');
|
|
43
|
+
warn('gh: the GitHub login from an earlier sandbox was kept in Windows Credential Manager, which is wiped with each sandbox. Log in again with: gh auth login --web --insecure-storage (then it lasts), or sign gh in on your PC and turn on github.usePcLogin.');
|
|
44
44
|
}
|
|
45
45
|
catch {
|
|
46
46
|
// No saved login: nothing to say.
|
|
@@ -2,6 +2,7 @@ import type { BootWant } from '../../core/local-config/index.js';
|
|
|
2
2
|
import type { Config } from '../../core/types.js';
|
|
3
3
|
import { type Payloads, type ServiceKind, type UsedBy } from '../../shared/accounts/index.js';
|
|
4
4
|
import { AccountLeases } from './leases.js';
|
|
5
|
+
import type { PcEnv } from './source.js';
|
|
5
6
|
import { ServiceAccountStore } from './store.js';
|
|
6
7
|
export interface AccountDelivery {
|
|
7
8
|
readonly payloads: Payloads;
|
|
@@ -22,5 +23,7 @@ export interface DeliveryInput {
|
|
|
22
23
|
readonly isRunning: (dataSet: string) => boolean;
|
|
23
24
|
readonly store?: ServiceAccountStore;
|
|
24
25
|
readonly leases?: AccountLeases;
|
|
26
|
+
/** This PC's folders: a shared kind with nothing saved takes this PC's own sign-in (left out: never). */
|
|
27
|
+
readonly pc?: PcEnv;
|
|
25
28
|
}
|
|
26
29
|
export declare function prepareServiceAccounts(i: DeliveryInput): AccountDelivery;
|
|
@@ -2,6 +2,7 @@ import { fingerprint, SERVICE_KINDS, SERVICE_SPECS, serviceWanted, } from '../..
|
|
|
2
2
|
import { setNameOf } from '../storage/index.js';
|
|
3
3
|
import { takeHandoffs } from './handoff.js';
|
|
4
4
|
import { AccountLeases } from './leases.js';
|
|
5
|
+
import { HOST_SOURCES } from './sources.js';
|
|
5
6
|
import { ServiceAccountStore } from './store.js';
|
|
6
7
|
/** How to get a first sign-in of a kind, for the note when none is saved. */
|
|
7
8
|
function howToSave(spec) {
|
|
@@ -9,14 +10,34 @@ function howToSave(spec) {
|
|
|
9
10
|
return "sign in to the Epic Games Launcher in this sandbox once, or take this PC's: sandbox account import epic";
|
|
10
11
|
if (spec.kind === 'steam')
|
|
11
12
|
return 'sign in once in this sandbox with sbx-steam login';
|
|
13
|
+
if (spec.kind === 'unity')
|
|
14
|
+
return 'sign in to Unity once in this sandbox (Unity page: Sign in to Unity), or: sandbox account login unity';
|
|
12
15
|
if (spec.kind === 'comet')
|
|
13
16
|
return 'sign in to Comet once in this sandbox, and it is kept for the next ones';
|
|
17
|
+
if (spec.kind === 'perplexityapp')
|
|
18
|
+
return 'sign in to the Perplexity app once in this sandbox, and it is kept for the next ones';
|
|
14
19
|
if (spec.returns)
|
|
15
20
|
return 'sign in once in this sandbox, or: sandbox account import ' + spec.kind;
|
|
16
21
|
return spec.sources.includes('typed')
|
|
17
22
|
? 'save one: sandbox account login ' + spec.kind
|
|
18
23
|
: 'save one: sandbox account import ' + spec.kind + ' --from <file>';
|
|
19
24
|
}
|
|
25
|
+
/** The first time a shared kind has nothing saved: this PC's own sign-in, saved like `sandbox account import`. */
|
|
26
|
+
function takeFromPc(kind, pc, store) {
|
|
27
|
+
const source = HOST_SOURCES[kind];
|
|
28
|
+
if (!pc || !source.fromPc)
|
|
29
|
+
return null;
|
|
30
|
+
let payload = null;
|
|
31
|
+
try {
|
|
32
|
+
payload = source.fromPc(pc);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null; // This PC's program has no usable sign-in: the note says how to save one.
|
|
36
|
+
}
|
|
37
|
+
if (payload)
|
|
38
|
+
store.save(kind, payload, 'pc', source.summary(payload));
|
|
39
|
+
return payload;
|
|
40
|
+
}
|
|
20
41
|
export function prepareServiceAccounts(i) {
|
|
21
42
|
const store = i.store ?? new ServiceAccountStore(i.storageRoot);
|
|
22
43
|
const leases = i.leases ?? new AccountLeases(i.storageRoot);
|
|
@@ -49,6 +70,8 @@ export function prepareServiceAccounts(i) {
|
|
|
49
70
|
catch {
|
|
50
71
|
unreadable = true;
|
|
51
72
|
}
|
|
73
|
+
if (!payload && !unreadable)
|
|
74
|
+
payload = takeFromPc(kind, i.pc, store);
|
|
52
75
|
if (spec.singleHolder) {
|
|
53
76
|
const claim = leases.claim(kind, dataSet, i.launchId, payload ? fingerprint(payload) : null, i.isRunning);
|
|
54
77
|
if (!claim.ok) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Perplexity desktop app: nothing to take from this PC. The app is an Electron program in a Microsoft Store package;
|
|
2
|
+
// its cookies are sealed to your Windows account (Chromium's cookie key is protected by DPAPI), so they cannot open in
|
|
3
|
+
// a sandbox, and reading them would be what password stealers do. The sign-in only ever comes from a sandbox you
|
|
4
|
+
// signed in in (the same way as Comet's).
|
|
5
|
+
import { cometSummary } from '../../../shared/accounts/index.js';
|
|
6
|
+
export const perplexityAppSource = {
|
|
7
|
+
summary: (p) => cometSummary(p),
|
|
8
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { HostSource } from '../source.js';
|
|
2
2
|
export declare const unitySource: HostSource<'unity'>;
|
|
@@ -1,13 +1,65 @@
|
|
|
1
|
-
|
|
1
|
+
// Unity: this PC's licence file (Unity_lic.ulf, or the named-user UnityEntitlementLicense.xml), a licence file you
|
|
2
|
+
// name, or the Unity ID (and serial) typed for a sandbox to sign in with. Unity ties a licence file to the computer, so
|
|
3
|
+
// a sandbox may refuse one taken from this PC; the typed Unity ID is the way that always works.
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { MAX_ULF_BYTES, unityEntitlementFile, unitySummary, unityUlfFile, UnityPayloadSchema, } from '../../../shared/accounts/index.js';
|
|
6
|
+
import { fail } from '../../../core/util/index.js';
|
|
7
|
+
import { decodeText, readBoundedFile } from '../../../shared/io/index.js';
|
|
8
|
+
const NOT_A_LICENCE = 'That is not a Unity licence file (Unity_lic.ulf) or entitlement file.';
|
|
9
|
+
const WHERE = 'Pick the file from C:\\ProgramData\\Unity or %LOCALAPPDATA%\\Unity\\licenses.';
|
|
10
|
+
function readText(file) {
|
|
11
|
+
const bytes = readBoundedFile(file, MAX_ULF_BYTES);
|
|
12
|
+
return bytes ? decodeText(bytes).text : null;
|
|
13
|
+
}
|
|
14
|
+
/** The email, password and serial answers that were typed (trimmed, empty ones left out). */
|
|
15
|
+
function typed(answers) {
|
|
16
|
+
const out = {};
|
|
17
|
+
const email = (answers.email ?? '').trim();
|
|
18
|
+
const password = answers.password ?? '';
|
|
19
|
+
const serial = (answers.serial ?? '').trim();
|
|
20
|
+
if (email)
|
|
21
|
+
out.email = email;
|
|
22
|
+
if (password)
|
|
23
|
+
out.password = password;
|
|
24
|
+
if (serial)
|
|
25
|
+
out.serial = serial;
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
function checked(candidate) {
|
|
29
|
+
const parsed = UnityPayloadSchema.safeParse(candidate);
|
|
30
|
+
if (!parsed.success)
|
|
31
|
+
fail('That Unity sign-in is not valid (check the email, and that the licence file is not too big).', WHERE);
|
|
32
|
+
return parsed.data;
|
|
33
|
+
}
|
|
2
34
|
export const unitySource = {
|
|
3
|
-
fromPc(
|
|
4
|
-
|
|
35
|
+
fromPc(pc, from) {
|
|
36
|
+
const ulf = (pc.programData || from ? readText(unityUlfFile(from ? path.resolve(from) : pc.programData)) : null) ??
|
|
37
|
+
(pc.localAppData
|
|
38
|
+
? readText(path.win32.join(pc.localAppData, 'VirtualStore', 'ProgramData', 'Unity', 'Unity_lic.ulf'))
|
|
39
|
+
: null);
|
|
40
|
+
const entitlement = pc.localAppData ? readText(unityEntitlementFile(pc.localAppData)) : null;
|
|
41
|
+
if (ulf === null && entitlement === null)
|
|
42
|
+
return null;
|
|
43
|
+
return { ...(ulf !== null ? { ulf } : {}), ...(entitlement !== null ? { entitlement } : {}) };
|
|
5
44
|
},
|
|
6
|
-
fromFile(
|
|
7
|
-
|
|
45
|
+
fromFile(bytes, answers) {
|
|
46
|
+
const text = decodeText(bytes).text;
|
|
47
|
+
const candidate = { ...typed(answers) };
|
|
48
|
+
if (text.includes('Entitlement'))
|
|
49
|
+
candidate.entitlement = text;
|
|
50
|
+
else if (text.includes('<License'))
|
|
51
|
+
candidate.ulf = text;
|
|
52
|
+
else
|
|
53
|
+
fail(NOT_A_LICENCE, WHERE);
|
|
54
|
+
return checked(candidate);
|
|
8
55
|
},
|
|
9
|
-
fromAnswers(
|
|
10
|
-
|
|
56
|
+
fromAnswers(answers) {
|
|
57
|
+
const t = typed(answers);
|
|
58
|
+
if (t.serial && !(t.email && t.password))
|
|
59
|
+
fail('A serial needs your Unity ID email and password.', 'Type them too, or leave the serial empty.');
|
|
60
|
+
if (!t.email || !t.password)
|
|
61
|
+
fail('A Unity sign-in needs your Unity ID email and password.', 'Type both: sandbox account login unity');
|
|
62
|
+
return checked(t);
|
|
11
63
|
},
|
|
12
|
-
summary:
|
|
64
|
+
summary: unitySummary,
|
|
13
65
|
};
|
|
@@ -2,16 +2,17 @@ import { z } from 'zod';
|
|
|
2
2
|
import { type ServiceKind } from '../../shared/accounts/index.js';
|
|
3
3
|
declare const LeaseSchema: z.ZodObject<{
|
|
4
4
|
kind: z.ZodEnum<{
|
|
5
|
-
unity: "unity";
|
|
6
5
|
keystore: "keystore";
|
|
7
6
|
play: "play";
|
|
8
7
|
expo: "expo";
|
|
9
8
|
appstore: "appstore";
|
|
9
|
+
unity: "unity";
|
|
10
10
|
steam: "steam";
|
|
11
11
|
epic: "epic";
|
|
12
12
|
cfx: "cfx";
|
|
13
13
|
perplexity: "perplexity";
|
|
14
14
|
comet: "comet";
|
|
15
|
+
perplexityapp: "perplexityapp";
|
|
15
16
|
}>;
|
|
16
17
|
dataSet: z.ZodString;
|
|
17
18
|
launchId: z.ZodString;
|
|
@@ -10,6 +10,8 @@ export interface PcEnv {
|
|
|
10
10
|
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
11
11
|
/** A variable of this Windows user's saved environment (HKCU\Environment), or null when it is not set. */
|
|
12
12
|
readonly userVariable?: (name: string) => string | null;
|
|
13
|
+
/** This PC's local drives ("D:\\"), asked only by the searches that look on every drive. */
|
|
14
|
+
readonly drives?: () => readonly string[];
|
|
13
15
|
}
|
|
14
16
|
/** The value of `name` in `reg query <key> /v <name>` output, or null. */
|
|
15
17
|
export declare function parseRegValue(text: string, name: string): string | null;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// (fromFile, `sandbox account import <kind> --from <file>`), or typed (fromAnswers, `sandbox account login <kind>`).
|
|
3
3
|
// One module per kind under kinds/; sources.ts lists them.
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { diskUsage } from '../../core/disk.js';
|
|
5
6
|
import { fail } from '../../core/util/index.js';
|
|
6
7
|
import { SYSTEM } from '../../shared/system.js';
|
|
7
8
|
import { refuseInSelfTest } from '../test-guard.js';
|
|
@@ -35,6 +36,7 @@ export function pcEnv() {
|
|
|
35
36
|
programFiles: env.ProgramFiles ?? 'C:\\Program Files',
|
|
36
37
|
env,
|
|
37
38
|
userVariable: savedUserVariable,
|
|
39
|
+
drives: () => diskUsage(env.ProgramData ?? 'C:\\').map((d) => d.drive),
|
|
38
40
|
};
|
|
39
41
|
}
|
|
40
42
|
/** What a kind whose host side is not written yet says. */
|
|
@@ -4,6 +4,7 @@ import { cometSource } from './kinds/comet.js';
|
|
|
4
4
|
import { epicSource } from './kinds/epic.js';
|
|
5
5
|
import { expoSource } from './kinds/expo.js';
|
|
6
6
|
import { keystoreSource } from './kinds/keystore.js';
|
|
7
|
+
import { perplexityAppSource } from './kinds/perplexityapp.js';
|
|
7
8
|
import { perplexitySource } from './kinds/perplexity.js';
|
|
8
9
|
import { playSource } from './kinds/play.js';
|
|
9
10
|
import { steamSource } from './kinds/steam.js';
|
|
@@ -19,4 +20,5 @@ export const HOST_SOURCES = {
|
|
|
19
20
|
appstore: appstoreSource,
|
|
20
21
|
perplexity: perplexitySource,
|
|
21
22
|
comet: cometSource,
|
|
23
|
+
perplexityapp: perplexityAppSource,
|
|
22
24
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { prepareStorePackages, spawnStoreRunner, stagedPackageDir, StorePackages, STORE_PACKAGE_MAX_AGE_MS, type ScriptRun, type StoreApp, type StoreRunner, type StoreStaging, } from './store-package.js';
|