windows-development-sandbox 1.25.1 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/registry.js +6 -0
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-F4PVBN23.js → chunk-6WWMBCT2.js} +3580 -1962
- package/dist/bin/{chunk-3GAXLATK.js → chunk-JXL6ZOO3.js} +72 -5
- package/dist/bin/{chunk-G3TG4ZT7.js → chunk-ZXCAVVS4.js} +1 -1
- 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 +12 -0
- 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 +36 -0
- 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/plan.js +5 -1
- package/dist/guest/toolchains/tools/cli.js +2 -2
- package/dist/host/accounts/deliver.js +4 -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/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 +16 -4
- 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 +20 -0
- package/dist/host/pc-installs/unity.js +135 -2
- package/dist/runtime/agent.js +116 -11
- package/dist/runtime/daemon.js +2170 -976
- package/dist/runtime/db.js +116 -11
- package/dist/runtime/main.js +2543 -1364
- 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 +2 -2
- 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/persisted-installs.test.js +1 -1
- package/dist/selftest/service-accounts.test.js +1 -1
- 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/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,7 +1,242 @@
|
|
|
1
|
+
// Unity: project versions, the editor choice, the release API, the Unity CLI, the editor bridge, the Asset Store cache,
|
|
2
|
+
// the status, the guide lines and the licence file helpers (pure; nothing is downloaded or run).
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { parseLicenceStatus, ulfSummary, UnityPayloadSchema, unityEntitlementFile, unitySummary, unityUlfFile, } from '../shared/accounts/index.js';
|
|
5
|
+
import { isKindReady, isToolchainReady } from '../shared/readiness.js';
|
|
6
|
+
import { assetPackages, bridgeOf, chooseEditor, cliManifestUrl, CliManifestSchema, COPLAY_PACKAGE, COPLAY_PACKAGE_URL, compareUnityVersions, editorModules, editorsAddArgs, installArgs, installModulesArgs, installNeedBytes, installPathArgs, licenceStatusArgs, ORIGIN_LABELS, parseProjectVersion, pcAssetCacheDirs, pickCliBinary, pickRelease, PIPELINE_PACKAGE, releaseUrl, supportsPipeline, UNITY_CLI_ENV, unityCliMcpServer, unityGuideLines, UnityStatusSchema, wantedVersion, withBridge, } from '../shared/unity/index.js';
|
|
7
|
+
const editor = (version, origin, modules = []) => {
|
|
8
|
+
const root = origin === 'pc' ? 'C:\\PcInstalls\\unity\\' + version : 'C:\\AgentData\\tools\\unity\\editors\\' + version;
|
|
9
|
+
return { version, root, exe: root + '\\Editor\\Unity.exe', origin, writable: origin === 'data', modules };
|
|
10
|
+
};
|
|
11
|
+
const RELEASE = {
|
|
12
|
+
results: [
|
|
13
|
+
{
|
|
14
|
+
version: '6000.0.58f2',
|
|
15
|
+
shortRevision: '92dee566b325',
|
|
16
|
+
downloads: [
|
|
17
|
+
{
|
|
18
|
+
url: 'https://download.unity3d.com/download_unity/92dee566b325/Windows64EditorInstaller/UnitySetup64-6000.0.58f2.exe',
|
|
19
|
+
type: 'EXE',
|
|
20
|
+
integrity: 'md5-AAAAAAAAAAAAAAAAAAAAAA==',
|
|
21
|
+
downloadSize: { value: 4_000_000_000 },
|
|
22
|
+
installedSize: { value: 8_000_000_000 },
|
|
23
|
+
modules: [
|
|
24
|
+
{
|
|
25
|
+
id: 'android',
|
|
26
|
+
downloadSize: { value: 100 },
|
|
27
|
+
installedSize: { value: 200 },
|
|
28
|
+
integrity: 'md5-BBBBBBBBBBBBBBBBBBBBBB==',
|
|
29
|
+
subModules: [
|
|
30
|
+
{ id: 'android-sdk-ndk-tools', downloadSize: { value: 10 }, installedSize: { value: 20 } },
|
|
31
|
+
{ id: 'android-open-jdk', downloadSize: { value: 1 }, installedSize: { value: 2 } },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{ id: 'webgl', downloadSize: { value: 50 }, installedSize: { value: 60 } },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
const SHA = 'a'.repeat(64);
|
|
42
|
+
function status(over = {}) {
|
|
43
|
+
return {
|
|
44
|
+
updatedAt: 'now',
|
|
45
|
+
cli: { version: '1.0.0-beta.16', path: 'C:\\AgentData\\tools\\unity\\cli\\1.0.0-beta.16\\unity.exe' },
|
|
46
|
+
editors: [
|
|
47
|
+
{ ...editor('6000.0.58f2', 'pc'), modules: [], sizeBytes: null },
|
|
48
|
+
{ ...editor('2022.3.40f1', 'data'), modules: [], sizeBytes: null },
|
|
49
|
+
],
|
|
50
|
+
projects: [
|
|
51
|
+
{
|
|
52
|
+
workspace: 'C:\\Workspace\\G',
|
|
53
|
+
version: '6000.0.58f2',
|
|
54
|
+
editor: 'C:\\PcInstalls\\unity\\6000.0.58f2',
|
|
55
|
+
reason: 'the project asks for 6000.0.58f2',
|
|
56
|
+
bridge: null,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
licence: { state: 'active', kind: 'personal', detail: 'Personal', user: null },
|
|
60
|
+
assetCache: { mode: 'persisted', dir: 'C:\\AgentData\\tools\\unity\\asset-store' },
|
|
61
|
+
pcAssets: [],
|
|
62
|
+
hub: { installed: false, path: null },
|
|
63
|
+
mcp: null,
|
|
64
|
+
skills: false,
|
|
65
|
+
problem: null,
|
|
66
|
+
...over,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
1
69
|
export const unitySuite = {
|
|
2
70
|
name: 'unity',
|
|
3
|
-
title: 'Unity: editors,
|
|
4
|
-
run: (t) => {
|
|
5
|
-
t.
|
|
71
|
+
title: 'Unity: editors, the release API, the CLI, the editor bridge, the guide and the licence file',
|
|
72
|
+
run: async (t) => {
|
|
73
|
+
t.section('ProjectVersion.txt');
|
|
74
|
+
t.equal(parseProjectVersion('m_EditorVersion: 6000.0.58f2\nm_EditorVersionWithRevision: 6000.0.58f2 (92dee566b325)\n'), { version: '6000.0.58f2', changeset: '92dee566b325' }, 'version and changeset');
|
|
75
|
+
t.equal(parseProjectVersion('m_EditorVersion: 2022.3.40f1\n'), { version: '2022.3.40f1', changeset: null }, 'without a revision');
|
|
76
|
+
t.equal(wantedVersion('m_EditorVersion: 2022.3.40f1\n', '6000.0.58f2'), { version: '2022.3.40f1', changeset: null }, "a project's own version wins over the setting");
|
|
77
|
+
t.equal(wantedVersion(null, '6000.0.58f2'), { version: '6000.0.58f2', changeset: null }, 'the setting is for folders without one');
|
|
78
|
+
t.equal(wantedVersion(null, 'project'), null, '"project" and no file: nothing');
|
|
79
|
+
t.equal(parseProjectVersion('m_EditorVersion: 2022.3.40f1\r\nm_EditorVersionWithRevision: 2022.3.40f1 (0123456789ab)\r\n'), { version: '2022.3.40f1', changeset: '0123456789ab' }, 'CRLF');
|
|
80
|
+
t.equal(parseProjectVersion('hello\nm_EditorVersion: banana\n'), null, 'garbage');
|
|
81
|
+
t.equal(parseProjectVersion('x'.repeat(70 * 1024) + '\nm_EditorVersion: 6000.0.58f2\n'), null, 'past 64 KB');
|
|
82
|
+
t.equal(parseProjectVersion('\n'.repeat(60) + 'm_EditorVersion: 6000.0.58f2\n'), null, 'past 50 lines');
|
|
83
|
+
t.section('versions');
|
|
84
|
+
t.check(compareUnityVersions('2022.3.9f1', '2022.3.10f1') < 0, 'patch numbers compare as numbers');
|
|
85
|
+
t.check(compareUnityVersions('6000.0.1f1', '2022.3.40f1') > 0, 'newer major is newer');
|
|
86
|
+
t.check(compareUnityVersions('6000.0.1b1', '6000.0.1f1') < 0, 'beta before final');
|
|
87
|
+
t.check(compareUnityVersions('6000.0.1f1', '6000.0.1f1') === 0, 'equal');
|
|
88
|
+
t.check(supportsPipeline('6000.0.58f2') && !supportsPipeline('2022.3.40f1'), 'the pipeline needs Unity 6');
|
|
89
|
+
t.section('chooseEditor');
|
|
90
|
+
const pc = editor('6000.0.58f2', 'pc', ['android']);
|
|
91
|
+
const data = editor('6000.0.58f2', 'data', ['android', 'webgl']);
|
|
92
|
+
const other = editor('2022.3.40f1', 'data');
|
|
93
|
+
const want = { version: '6000.0.58f2', changeset: '92dee566b325' };
|
|
94
|
+
t.equal(chooseEditor([pc, data], null, [], true).reason, 'no ProjectSettings/ProjectVersion.txt', 'no version file');
|
|
95
|
+
const a = chooseEditor([pc, data], want, ['android'], true);
|
|
96
|
+
t.equal([a.editor?.origin, a.reason], ['pc', 'the project asks for 6000.0.58f2'], 'the PC copy first');
|
|
97
|
+
const b = chooseEditor([pc, data], want, ['android', 'webgl'], true);
|
|
98
|
+
t.equal([b.editor?.origin, b.reason], ['data', '6000.0.58f2 from this PC lacks webgl; using the data disk copy'], 'the PC copy lacks a module');
|
|
99
|
+
const c = chooseEditor([other], want, [], true);
|
|
100
|
+
t.equal([c.editor, c.install, c.reason], [null, want, '6000.0.58f2 is not here yet; installing it on the data disk'], 'install it, never another version');
|
|
101
|
+
const d = chooseEditor([other], want, [], false);
|
|
102
|
+
t.equal([d.editor, d.install, d.reason], [
|
|
103
|
+
null,
|
|
104
|
+
null,
|
|
105
|
+
'6000.0.58f2 is not on this PC or the data disk; accept the Unity licence (Unity set-up settings) to install it',
|
|
106
|
+
], 'no licence accepted');
|
|
107
|
+
t.equal(chooseEditor([pc], want, ['webgl'], false).editor?.origin, 'pc', 'a PC copy missing modules beats none');
|
|
108
|
+
t.section('editorModules');
|
|
109
|
+
const root = t.dir('editor');
|
|
110
|
+
const engines = ['Editor', 'Data', 'PlaybackEngines'];
|
|
111
|
+
t.dir('editor', ...engines, 'AndroidPlayer');
|
|
112
|
+
t.dir('editor', ...engines, 'windowsstandalonesupport', 'Variations', 'win64_development_il2cpp');
|
|
113
|
+
t.equal(editorModules(root, { exists: (f) => fs.existsSync(f), list: (d) => fs.readdirSync(d) }), ['android', 'windows-il2cpp'], 'modules found by their folders');
|
|
114
|
+
t.section('release API');
|
|
115
|
+
t.includes(releaseUrl('6000.0.58f2'), 'version=6000.0.58f2&platform=WINDOWS', 'release url');
|
|
116
|
+
t.includes(releaseUrl('a&b'), 'version=a%26b', 'release url encodes');
|
|
117
|
+
const rel = pickRelease(RELEASE);
|
|
118
|
+
t.equal(rel?.changeset, '92dee566b325', 'changeset');
|
|
119
|
+
t.equal(rel?.editor.integrity, 'md5-AAAAAAAAAAAAAAAAAAAAAA==', 'md5 integrity kept');
|
|
120
|
+
t.equal(rel?.modules[0]?.children, ['android-sdk-ndk-tools', 'android-open-jdk'], 'android children');
|
|
121
|
+
t.equal(pickRelease({ results: [] }), null, 'no result');
|
|
122
|
+
t.equal(pickRelease('nope'), null, 'not an object');
|
|
123
|
+
if (rel) {
|
|
124
|
+
t.equal(installNeedBytes(rel, []), Math.ceil(12_000_000_000 * 1.1), 'editor only');
|
|
125
|
+
t.equal(installNeedBytes(rel, ['android']), Math.ceil((12_000_000_000 + 100 + 200 + 11 + 22) * 1.1), 'module and children');
|
|
126
|
+
}
|
|
127
|
+
t.section('Unity CLI');
|
|
128
|
+
t.equal(cliManifestUrl('latest'), 'https://public-cdn.cloud.unity3d.com/hub/prod/cli/latest-beta.json', 'latest');
|
|
129
|
+
t.equal(cliManifestUrl('1.0.0-beta.16'), 'https://public-cdn.cloud.unity3d.com/hub/prod/cli/1.0.0-beta.16/latest.json', 'pinned');
|
|
130
|
+
await t.throws(() => cliManifestUrl('../x'), 'not a Unity CLI version', 'a bad version');
|
|
131
|
+
const manifest = (filename, sha256) => CliManifestSchema.parse({ version: '1.0.0-beta.16', binaries: { 'win32-x64': { filename, sha256, size: 44 } } });
|
|
132
|
+
t.equal(pickCliBinary(manifest('unity-windows-x64.exe', SHA), 'x64'), {
|
|
133
|
+
version: '1.0.0-beta.16',
|
|
134
|
+
url: 'https://public-cdn.cloud.unity3d.com/hub/prod/cli/1.0.0-beta.16/unity-windows-x64.exe',
|
|
135
|
+
sha256: SHA,
|
|
136
|
+
size: 44,
|
|
137
|
+
}, 'the x64 build');
|
|
138
|
+
t.equal(pickCliBinary(manifest('..\\evil.exe', SHA), 'x64'), null, 'a bad file name');
|
|
139
|
+
t.equal(pickCliBinary(manifest('unity-windows-x64.exe', 'abc'), 'x64'), null, 'a bad sha');
|
|
140
|
+
t.equal(pickCliBinary(manifest('unity-windows-x64.exe', SHA), 'arm64'), null, 'no build for this CPU');
|
|
141
|
+
t.equal(installArgs('6000.0.58f2', '92dee566b325', ['android'], 'C:\\S'), ['install', '6000.0.58f2', '-c', '92dee566b325', '-m', 'android', '--cm', '--download-directory', 'C:\\S'], 'install args');
|
|
142
|
+
t.equal(installArgs('6000.0.58f2', null, [], 'C:\\S'), ['install', '6000.0.58f2', '--download-directory', 'C:\\S'], 'install args, bare');
|
|
143
|
+
t.equal(installModulesArgs('6000.0.58f2', ['webgl', 'ios'], 'C:\\S'), ['install-modules', '-e', '6000.0.58f2', '-m', 'webgl', 'ios', '--cm', '--download-directory', 'C:\\S'], 'install-modules args');
|
|
144
|
+
t.equal(installPathArgs('C:\\E'), ['install-path', '--set', 'C:\\E'], 'install-path args');
|
|
145
|
+
t.equal(editorsAddArgs(['C:\\A', 'C:\\B']), ['editors', 'add', 'C:\\A', 'C:\\B'], 'editors add args');
|
|
146
|
+
t.equal(licenceStatusArgs(), ['license', 'status', '--format', 'json'], 'licence status args');
|
|
147
|
+
t.section('MCP');
|
|
148
|
+
t.equal(unityCliMcpServer('C:\\u\\unity.exe', 'C:\\Workspace\\G'), {
|
|
149
|
+
type: 'stdio',
|
|
150
|
+
command: 'C:\\u\\unity.exe',
|
|
151
|
+
args: ['mcp', '--cli-tools'],
|
|
152
|
+
env: { ...UNITY_CLI_ENV, UNITY_PROJECT_PATH: 'C:\\Workspace\\G' },
|
|
153
|
+
}, 'with a project');
|
|
154
|
+
t.equal(unityCliMcpServer('u', null).env, { ...UNITY_CLI_ENV }, 'without a project');
|
|
155
|
+
t.section('Packages/manifest.json');
|
|
156
|
+
const manifestText = JSON.stringify({ dependencies: { 'com.a': '1.0.0', 'com.b': '2.0.0' }, scopedRegistries: [] }, null, 2) + '\n';
|
|
157
|
+
for (const bridge of ['pipeline', 'coplay']) {
|
|
158
|
+
const on = withBridge(manifestText, bridge, true);
|
|
159
|
+
t.equal(bridgeOf(on), bridge, bridge + ' added');
|
|
160
|
+
t.equal(Object.keys(JSON.parse(on).dependencies).slice(0, 2), ['com.a', 'com.b'], bridge + ': order kept');
|
|
161
|
+
t.equal(withBridge(on, bridge, false), manifestText, bridge + ': removed again, byte for byte');
|
|
162
|
+
}
|
|
163
|
+
t.equal(JSON.parse(withBridge(manifestText, 'pipeline', true, '0.11.0')).dependencies[PIPELINE_PACKAGE], '0.11.0', 'pipeline version');
|
|
164
|
+
t.equal(JSON.parse(withBridge(manifestText, 'coplay', true)).dependencies[COPLAY_PACKAGE], COPLAY_PACKAGE_URL, 'coplay url');
|
|
165
|
+
const crlf = withBridge(manifestText.replace(/\n/g, '\r\n'), 'pipeline', true);
|
|
166
|
+
t.check(crlf.includes('\r\n') && !/[^\r]\n/.test(crlf), 'CRLF kept');
|
|
167
|
+
t.equal(bridgeOf('not json'), null, 'garbage has no bridge');
|
|
168
|
+
t.equal(bridgeOf(manifestText), null, 'no bridge');
|
|
169
|
+
await t.throws(() => withBridge('[]', 'pipeline', true), 'not an object', 'refuses an array');
|
|
170
|
+
await t.throws(() => withBridge('{"a":"' + 'x'.repeat(1024 * 1024) + '"}', 'pipeline', true), 'over 1 MB', 'refuses a big file');
|
|
171
|
+
await t.throws(() => withBridge(manifestText, 'pipeline', true, '1.0; rm'), 'not a package version', 'bad version');
|
|
172
|
+
t.section('Asset Store cache');
|
|
173
|
+
const dirs = new Set(['C:\\Env\\Asset Store-5.x', 'C:\\Users\\u\\AppData\\Roaming\\Unity\\Asset Store-5.x']);
|
|
174
|
+
const isDir = (d) => dirs.has(d);
|
|
175
|
+
t.equal(pcAssetCacheDirs({ appData: 'C:\\Users\\u\\AppData\\Roaming', envValue: 'C:\\Env' }, isDir), ['C:\\Env', 'C:\\Users\\u\\AppData\\Roaming\\Unity'], 'the environment first');
|
|
176
|
+
t.equal(pcAssetCacheDirs({ appData: 'C:\\Users\\u\\AppData\\Roaming' }, isDir), ['C:\\Users\\u\\AppData\\Roaming\\Unity'], 'the default');
|
|
177
|
+
t.equal(pcAssetCacheDirs({ appData: 'C:\\X' }, isDir), [], 'none');
|
|
178
|
+
const tree = {
|
|
179
|
+
R: ['Pub', 'file.txt'],
|
|
180
|
+
'R\\Pub': ['Cat'],
|
|
181
|
+
'R\\Pub\\Cat': ['a.unitypackage', 'b.txt'],
|
|
182
|
+
};
|
|
183
|
+
const assetFs = {
|
|
184
|
+
list: (d) => tree[d] ?? [],
|
|
185
|
+
isDir: (d) => d in tree,
|
|
186
|
+
size: (f) => (f.endsWith('.unitypackage') ? 7 : null),
|
|
187
|
+
};
|
|
188
|
+
t.equal(assetPackages('R', assetFs), [{ publisher: 'Pub', category: 'Cat', name: 'a', sizeBytes: 7 }], 'packages');
|
|
189
|
+
const many = {
|
|
190
|
+
R: ['P'],
|
|
191
|
+
'R\\P': ['C'],
|
|
192
|
+
'R\\P\\C': Array.from({ length: 2500 }, (_, i) => 'p' + i + '.unitypackage'),
|
|
193
|
+
};
|
|
194
|
+
t.equal(assetPackages('R', { ...assetFs, list: (d) => many[d] ?? [], isDir: (d) => d in many }).length, 2000, 'at most 2000');
|
|
195
|
+
t.section('status');
|
|
196
|
+
const good = status();
|
|
197
|
+
t.check(UnityStatusSchema.safeParse(good).success, 'a status parses');
|
|
198
|
+
t.check(!UnityStatusSchema.safeParse({ ...good, editors: Array.from({ length: 33 }, () => good.editors[0]) }).success, '33 editors are refused');
|
|
199
|
+
t.section('guide');
|
|
200
|
+
const lines = (s) => unityGuideLines(s, [{ workspace: 'C:\\Workspace\\G' }]).join('\n');
|
|
201
|
+
const exe = '"C:\\PcInstalls\\unity\\6000.0.58f2\\Editor\\Unity.exe"';
|
|
202
|
+
t.equal(unityGuideLines(good, [{ workspace: 'C:\\Workspace\\G' }])[0], '- Unity: editor 6000.0.58f2 at `C:\\PcInstalls\\unity\\6000.0.58f2\\Editor\\Unity.exe` (' +
|
|
203
|
+
ORIGIN_LABELS.pc +
|
|
204
|
+
'; the project asks for 6000.0.58f2). Build or run code without opening it: `' +
|
|
205
|
+
exe +
|
|
206
|
+
' -batchmode -nographics -quit -projectPath "C:\\Workspace\\G" -executeMethod <Class.Method> -logFile -`; tests: `-runTests -testPlatform EditMode -testResults <file>` (without -quit). Opening the project in another Unity version upgrades its files: never do that without asking the user. It is the user\'s PC copy, read-only: never add modules to it.', 'PC read-only line');
|
|
207
|
+
const onData = lines(status({
|
|
208
|
+
projects: [{ ...good.projects[0], editor: 'C:\\AgentData\\tools\\unity\\editors\\2022.3.40f1' }],
|
|
209
|
+
}));
|
|
210
|
+
t.includes(onData, '(' + ORIGIN_LABELS.data + ';', 'data origin label');
|
|
211
|
+
t.excludes(onData, 'read-only: never add modules', 'a data editor has no read-only note');
|
|
212
|
+
t.includes(lines(good), '- `unity` (Unity CLI 1.0.0-beta.16) is on PATH: `unity editors -i`, `unity build`, `unity test`, `unity open <project>` (opens it signed in).', 'CLI line');
|
|
213
|
+
t.includes(lines(good), "- To drive a running editor (scenes, GameObjects, play mode) the project needs an editor bridge package: ask the user to add it on the sandbox tool's Unity page. Never change Packages/manifest.json yourself unless asked.", 'no bridge');
|
|
214
|
+
const bridged = (bridge) => lines(status({ projects: [{ ...good.projects[0], bridge }] }));
|
|
215
|
+
t.includes(bridged('pipeline'), '- The open editor can be driven with `unity command` and `unity eval` (Unity Pipeline package); MCP clients use sbx-unity.', 'pipeline');
|
|
216
|
+
t.includes(bridged('coplay'), '- The open editor can be driven with MCP for Unity (sbx-unity-mcp in Apps).', 'coplay');
|
|
217
|
+
t.excludes(bridged('pipeline'), 'needs an editor bridge', 'no bridge line when there is one');
|
|
218
|
+
t.includes(lines(status({ pcAssets: ['C:\\PcInstalls\\unity\\AssetStore'] })), "- The user's Unity Asset Store downloads (read-only): `C:\\PcInstalls\\unity\\AssetStore`; import one with the editor closed:", 'assets');
|
|
219
|
+
t.includes(lines(status({ licence: { state: 'none', kind: '', detail: 'no licence', user: null } })), "- Unity licence: no licence. Batch mode fails without one: ask the user to sign in to Unity on the sandbox tool's Unity page.", 'no licence');
|
|
220
|
+
t.equal(unityGuideLines(good, [{ workspace: 'C:\\Elsewhere' }]), [], 'nothing for a project Unity has no word on');
|
|
221
|
+
t.section('licence files');
|
|
222
|
+
t.equal(unityUlfFile('C:\\ProgramData'), 'C:\\ProgramData\\Unity\\Unity_lic.ulf', 'ulf path');
|
|
223
|
+
t.equal(unityEntitlementFile('C:\\Users\\u\\AppData\\Local'), 'C:\\Users\\u\\AppData\\Local\\Unity\\licenses\\UnityEntitlementLicense.xml', 'entitlement path');
|
|
224
|
+
const ulf = '<?xml version="1.0" encoding="UTF-8"?>\r\n<root>\r\n <License id="Terms">\r\n <SerialMasked Value="U3-XXXX-XXXX-XXXX-XXXX-XXXX" />\r\n <UpdateDate Value="2026-10-01T10:00:00" />\r\n </License>\r\n</root>';
|
|
225
|
+
t.equal(ulfSummary(ulf), { serialMasked: 'U3-XXXX-XXXX-XXXX-XXXX-XXXX', updated: '2026-10-01T10:00:00', kind: 'personal' }, 'personal ulf');
|
|
226
|
+
t.equal(ulfSummary(ulf.replace('U3-', 'F4-'))?.kind, 'paid', 'paid ulf');
|
|
227
|
+
t.equal(ulfSummary('hello'), null, 'not a ulf');
|
|
228
|
+
t.equal(unitySummary({ email: 'me@x.com', password: 'hunter2hunter2' }), 'me@x.com', 'the email');
|
|
229
|
+
t.excludes(unitySummary({ email: 'me@x.com', password: 'hunter2hunter2', serial: 'SECRET' }), 'hunter2', 'no password in the summary');
|
|
230
|
+
t.equal(unitySummary({ ulf }), 'licence file (Personal)', 'a personal file');
|
|
231
|
+
t.equal(unitySummary({ ulf: ulf.replace('U3-', 'F4-') }), 'licence file (Plus or Pro)', 'a paid file');
|
|
232
|
+
t.check(!UnityPayloadSchema.safeParse({}).success, 'an empty sign-in is refused');
|
|
233
|
+
t.check(UnityPayloadSchema.safeParse({ entitlement: '<x/>' }).success, 'an entitlement alone is a sign-in');
|
|
234
|
+
t.equal(parseLicenceStatus({ weird: true }).state, 'unknown', 'an unknown shape is unknown');
|
|
235
|
+
t.equal(parseLicenceStatus('text').state, 'unknown', 'text is unknown');
|
|
236
|
+
t.equal(parseLicenceStatus(null).state, 'unknown', 'null is unknown');
|
|
237
|
+
t.equal(parseLicenceStatus({ licenses: [] }).state, 'none', 'no licences');
|
|
238
|
+
t.equal(parseLicenceStatus({ status: 'active', type: 'Personal' }).state, 'active', 'active');
|
|
239
|
+
t.section('readiness');
|
|
240
|
+
t.check(isToolchainReady('unity') && isKindReady('unity'), 'Unity is offered');
|
|
6
241
|
},
|
|
7
242
|
};
|
|
@@ -3,6 +3,7 @@ export * from './payloads.js';
|
|
|
3
3
|
export { fingerprint } from './fingerprint.js';
|
|
4
4
|
export { epicSettingsFile, epicSettingsFiles, MAX_EPIC_INI_BYTES, readEpicLogin, withEpicLogin, type EpicPayload, } from './kinds/epic.js';
|
|
5
5
|
export * from './kinds/steam.js';
|
|
6
|
+
export * from './kinds/unity.js';
|
|
6
7
|
export { PERPLEXITY_ENV, perplexityKeyFromText, perplexitySummary, type PerplexityPayload, } from './kinds/perplexity.js';
|
|
7
8
|
export { cometPayloadFromCookies, cometSummary, cookiesToRestore, type CometPayload } from './kinds/comet.js';
|
|
8
9
|
export * from './anthropic.js';
|
|
@@ -5,6 +5,7 @@ export * from './payloads.js';
|
|
|
5
5
|
export { fingerprint } from './fingerprint.js';
|
|
6
6
|
export { epicSettingsFile, epicSettingsFiles, MAX_EPIC_INI_BYTES, readEpicLogin, withEpicLogin, } from './kinds/epic.js';
|
|
7
7
|
export * from './kinds/steam.js';
|
|
8
|
+
export * from './kinds/unity.js';
|
|
8
9
|
export { PERPLEXITY_ENV, perplexityKeyFromText, perplexitySummary, } from './kinds/perplexity.js';
|
|
9
10
|
export { cometPayloadFromCookies, cometSummary, cookiesToRestore } from './kinds/comet.js';
|
|
10
11
|
export * from './anthropic.js';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Payload } from '../payloads.js';
|
|
2
|
+
export type UnityPayload = Payload<'unity'>;
|
|
3
|
+
/** The licence files' size limit (the payload's). */
|
|
4
|
+
export declare const MAX_ULF_BYTES: number;
|
|
5
|
+
/** %PROGRAMDATA%\Unity\Unity_lic.ulf */
|
|
6
|
+
export declare function unityUlfFile(programData: string): string;
|
|
7
|
+
/** %LOCALAPPDATA%\Unity\licenses\UnityEntitlementLicense.xml */
|
|
8
|
+
export declare function unityEntitlementFile(localAppData: string): string;
|
|
9
|
+
export interface UlfSummary {
|
|
10
|
+
/** The serial as Unity masks it in the file (XXXX-...), never a full one. */
|
|
11
|
+
readonly serialMasked: string | null;
|
|
12
|
+
readonly updated: string | null;
|
|
13
|
+
readonly kind: 'personal' | 'paid' | 'unknown';
|
|
14
|
+
}
|
|
15
|
+
/** What a Unity_lic.ulf says (its SerialMasked and UpdateDate), or null when it holds neither. */
|
|
16
|
+
export declare function ulfSummary(text: string): UlfSummary | null;
|
|
17
|
+
/** One line for the status: the Unity ID, else what kind of licence file it is. Never a secret. */
|
|
18
|
+
export declare function unitySummary(p: UnityPayload): string;
|
|
19
|
+
export interface LicenceStatus {
|
|
20
|
+
readonly state: 'active' | 'none' | 'unknown';
|
|
21
|
+
readonly kind: string;
|
|
22
|
+
readonly detail: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* What `unity license status --format json` printed. The shape is not documented (checked against the docs, not a live
|
|
26
|
+
* run), so anything unrecognised is 'unknown', never a guess.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseLicenceStatus(json: unknown): LicenceStatus;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Unity's licence files and what they say, without secrets: the serial licence (Unity_lic.ulf), the named-user licence
|
|
2
|
+
// (UnityEntitlementLicense.xml) and the answer of `unity license status`. Pure text handling, every scan bounded.
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
/** The licence files' size limit (the payload's). */
|
|
5
|
+
export const MAX_ULF_BYTES = 128 * 1024;
|
|
6
|
+
const MAX_LINES = 4000;
|
|
7
|
+
const MAX_FIELD = 200;
|
|
8
|
+
/** %PROGRAMDATA%\Unity\Unity_lic.ulf */
|
|
9
|
+
export function unityUlfFile(programData) {
|
|
10
|
+
return path.win32.join(programData, 'Unity', 'Unity_lic.ulf');
|
|
11
|
+
}
|
|
12
|
+
/** %LOCALAPPDATA%\Unity\licenses\UnityEntitlementLicense.xml */
|
|
13
|
+
export function unityEntitlementFile(localAppData) {
|
|
14
|
+
return path.win32.join(localAppData, 'Unity', 'licenses', 'UnityEntitlementLicense.xml');
|
|
15
|
+
}
|
|
16
|
+
/** The value of `<Name Value="..."/>` on one line, by index scan (no backtracking). */
|
|
17
|
+
function valueOf(line, name) {
|
|
18
|
+
const at = line.indexOf('<' + name);
|
|
19
|
+
if (at < 0)
|
|
20
|
+
return null;
|
|
21
|
+
const key = line.indexOf('Value="', at);
|
|
22
|
+
if (key < 0)
|
|
23
|
+
return null;
|
|
24
|
+
const from = key + 7;
|
|
25
|
+
const end = line.indexOf('"', from);
|
|
26
|
+
if (end < 0 || end - from > MAX_FIELD)
|
|
27
|
+
return null;
|
|
28
|
+
return line.slice(from, end);
|
|
29
|
+
}
|
|
30
|
+
/** What a Unity_lic.ulf says (its SerialMasked and UpdateDate), or null when it holds neither. */
|
|
31
|
+
export function ulfSummary(text) {
|
|
32
|
+
let serialMasked = null;
|
|
33
|
+
let updated = null;
|
|
34
|
+
const lines = text.slice(0, MAX_ULF_BYTES).split('\n', MAX_LINES);
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
serialMasked ??= valueOf(line, 'SerialMasked');
|
|
37
|
+
updated ??= valueOf(line, 'UpdateDate');
|
|
38
|
+
if (serialMasked !== null && updated !== null)
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
if (serialMasked === null && updated === null)
|
|
42
|
+
return null;
|
|
43
|
+
const kind = serialMasked === null ? 'unknown' : serialMasked.startsWith('U3-') ? 'personal' : 'paid';
|
|
44
|
+
return { serialMasked, updated, kind };
|
|
45
|
+
}
|
|
46
|
+
/** One line for the status: the Unity ID, else what kind of licence file it is. Never a secret. */
|
|
47
|
+
export function unitySummary(p) {
|
|
48
|
+
if (p.email)
|
|
49
|
+
return p.email;
|
|
50
|
+
if (p.ulf) {
|
|
51
|
+
const kind = ulfSummary(p.ulf)?.kind;
|
|
52
|
+
return kind === 'personal' ? 'licence file (Personal)' : 'licence file (Plus or Pro)';
|
|
53
|
+
}
|
|
54
|
+
if (p.entitlement)
|
|
55
|
+
return 'licence file (Personal)';
|
|
56
|
+
return 'serial';
|
|
57
|
+
}
|
|
58
|
+
function text(v) {
|
|
59
|
+
return typeof v === 'string' ? v.slice(0, MAX_FIELD) : '';
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* What `unity license status --format json` printed. The shape is not documented (checked against the docs, not a live
|
|
63
|
+
* run), so anything unrecognised is 'unknown', never a guess.
|
|
64
|
+
*/
|
|
65
|
+
export function parseLicenceStatus(json) {
|
|
66
|
+
const unknown = { state: 'unknown', kind: '', detail: 'not recognised' };
|
|
67
|
+
if (typeof json !== 'object' || json === null || Array.isArray(json))
|
|
68
|
+
return unknown;
|
|
69
|
+
const o = json;
|
|
70
|
+
const licences = Array.isArray(o.licenses)
|
|
71
|
+
? o.licenses
|
|
72
|
+
: Array.isArray(o.licences)
|
|
73
|
+
? o.licences
|
|
74
|
+
: null;
|
|
75
|
+
if (licences !== null) {
|
|
76
|
+
if (!licences.length)
|
|
77
|
+
return { state: 'none', kind: '', detail: 'no licence' };
|
|
78
|
+
const first = licences[0];
|
|
79
|
+
const f = typeof first === 'object' && first !== null ? first : {};
|
|
80
|
+
return { state: 'active', kind: text(f.type ?? f.kind), detail: text(f.detail ?? f.name ?? f.status) };
|
|
81
|
+
}
|
|
82
|
+
const status = text(o.status ?? o.state).toLowerCase();
|
|
83
|
+
if (status === 'active' || status === 'activated' || status === 'valid')
|
|
84
|
+
return { state: 'active', kind: text(o.type ?? o.kind), detail: text(o.detail ?? o.message) || status };
|
|
85
|
+
if (status === 'none' || status === 'inactive' || status === 'not activated' || status === 'unlicensed')
|
|
86
|
+
return { state: 'none', kind: '', detail: text(o.detail ?? o.message) || status };
|
|
87
|
+
return unknown;
|
|
88
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export declare const SERVICE_KINDS: readonly ["epic", "steam", "unity", "cfx", "keystore", "play", "expo", "appstore", "perplexity", "comet"];
|
|
1
|
+
export declare const SERVICE_KINDS: readonly ["epic", "steam", "unity", "cfx", "keystore", "play", "expo", "appstore", "perplexity", "comet", "perplexityapp"];
|
|
2
2
|
export type ServiceKind = (typeof SERVICE_KINDS)[number];
|
|
3
3
|
/** The toolchains a sign-in can serve (each is a key of `toolchains`). */
|
|
4
4
|
export type ToolchainUser = 'unreal' | 'steam' | 'unity' | 'fivem' | 'android' | 'flutter' | 'reactNative' | 'godot' | 'dotnet';
|
|
5
5
|
/** The apps a sign-in can serve: on when one of APP_USERS' apps is in apps.install. */
|
|
6
|
-
export type AppUser = 'perplexity' | 'comet';
|
|
6
|
+
export type AppUser = 'perplexity' | 'comet' | 'perplexityapp';
|
|
7
7
|
/** What a sign-in serves (it is handed over only when one of them is on for the boot). */
|
|
8
8
|
export type UsedBy = ToolchainUser | AppUser;
|
|
9
9
|
/** The toolchains a sign-in can serve. */
|
|
@@ -13,6 +13,7 @@ export const SERVICE_KINDS = [
|
|
|
13
13
|
'appstore',
|
|
14
14
|
'perplexity',
|
|
15
15
|
'comet',
|
|
16
|
+
'perplexityapp',
|
|
16
17
|
];
|
|
17
18
|
/** The toolchains a sign-in can serve. */
|
|
18
19
|
export const USED_BY = [
|
|
@@ -30,6 +31,7 @@ export const USED_BY = [
|
|
|
30
31
|
export const APP_USERS = {
|
|
31
32
|
perplexity: ['perplexity-mcp'],
|
|
32
33
|
comet: ['comet'],
|
|
34
|
+
perplexityapp: ['perplexity-desktop'],
|
|
33
35
|
};
|
|
34
36
|
export const ACCOUNT_SOURCES = ['pc', 'file', 'typed', 'sandbox'];
|
|
35
37
|
const AGENT_CAN_USE = 'The agent in the sandbox can use this sign-in while it is there.';
|
|
@@ -77,7 +79,9 @@ export const SERVICE_SPECS = {
|
|
|
77
79
|
warnings: [
|
|
78
80
|
AGENT_CAN_USE,
|
|
79
81
|
ONE_PLACE,
|
|
80
|
-
'
|
|
82
|
+
'Unity Personal is activated by signing in: sign in to Unity once in a sandbox and the licence it makes is kept for the next ones (if Unity refuses it there, sign in again).',
|
|
83
|
+
'A Plus or Pro serial is activated in each sandbox and handed back when the sandbox stops cleanly; a sandbox closed abruptly may keep the seat until you remove it at id.unity.com (My Seats).',
|
|
84
|
+
'A licence file taken from this PC may not work in a sandbox: Unity ties it to the computer.',
|
|
81
85
|
],
|
|
82
86
|
fields: [
|
|
83
87
|
{ name: 'email', label: 'Unity ID email', secret: false, optional: true },
|
|
@@ -206,6 +210,23 @@ export const SERVICE_SPECS = {
|
|
|
206
210
|
],
|
|
207
211
|
fields: [],
|
|
208
212
|
},
|
|
213
|
+
perplexityapp: {
|
|
214
|
+
kind: 'perplexityapp',
|
|
215
|
+
label: 'Perplexity desktop app',
|
|
216
|
+
usedBy: ['perplexityapp'],
|
|
217
|
+
sources: ['sandbox'],
|
|
218
|
+
singleHolder: false,
|
|
219
|
+
returns: true,
|
|
220
|
+
maxBytes: 256 * 1024,
|
|
221
|
+
warnings: [
|
|
222
|
+
AGENT_CAN_USE +
|
|
223
|
+
' It can act as you in the Perplexity app and spend your Perplexity Computer credits; the app acts on the pages it reads, which can try to steer it.',
|
|
224
|
+
"Your sign-in in the Perplexity app can't be copied from this PC: Windows locks it to your PC account. Sign in once inside a sandbox and it is kept for the next ones.",
|
|
225
|
+
'Only the perplexity.ai sign-in cookies are kept: never saved passwords, other sites, localStorage or history.',
|
|
226
|
+
'Several sandboxes can use it at once: the sign-in the last one hands back is kept.',
|
|
227
|
+
],
|
|
228
|
+
fields: [],
|
|
229
|
+
},
|
|
209
230
|
};
|
|
210
231
|
/** Where a sandbox leaves a renewed sign-in, under the data folder: state\accounts\<kind>.json. */
|
|
211
232
|
export const HANDOFF_PARTS = ['state', 'accounts'];
|
|
@@ -218,6 +239,7 @@ export function isServiceKind(value) {
|
|
|
218
239
|
const APP_USER_TEXT = {
|
|
219
240
|
perplexity: 'the Perplexity MCP app',
|
|
220
241
|
comet: 'the Comet app',
|
|
242
|
+
perplexityapp: 'the Perplexity desktop app',
|
|
221
243
|
};
|
|
222
244
|
/** What a sign-in serves, for a sentence: "unreal", "reactNative or flutter", "the Comet app". */
|
|
223
245
|
export function usedByText(usedBy) {
|
|
@@ -17,6 +17,7 @@ export declare const SteamPayloadSchema: z.ZodObject<{
|
|
|
17
17
|
/** A Unity licence file (Unity_lic.ulf), and the Unity ID to sign in with when it has to be made again. */
|
|
18
18
|
export declare const UnityPayloadSchema: z.ZodObject<{
|
|
19
19
|
ulf: z.ZodOptional<z.ZodString>;
|
|
20
|
+
entitlement: z.ZodOptional<z.ZodString>;
|
|
20
21
|
email: z.ZodOptional<z.ZodString>;
|
|
21
22
|
password: z.ZodOptional<z.ZodString>;
|
|
22
23
|
serial: z.ZodOptional<z.ZodString>;
|
|
@@ -91,6 +92,23 @@ export declare const CometPayloadSchema: z.ZodObject<{
|
|
|
91
92
|
}>>;
|
|
92
93
|
}, z.core.$strict>>;
|
|
93
94
|
}, z.core.$strict>;
|
|
95
|
+
/** The Perplexity desktop app's sign-in: the same perplexity.ai cookies as Comet's. */
|
|
96
|
+
export declare const PerplexityAppPayloadSchema: z.ZodObject<{
|
|
97
|
+
cookies: z.ZodArray<z.ZodObject<{
|
|
98
|
+
name: z.ZodString;
|
|
99
|
+
value: z.ZodString;
|
|
100
|
+
domain: z.ZodString;
|
|
101
|
+
path: z.ZodString;
|
|
102
|
+
expires: z.ZodOptional<z.ZodNumber>;
|
|
103
|
+
secure: z.ZodBoolean;
|
|
104
|
+
httpOnly: z.ZodBoolean;
|
|
105
|
+
sameSite: z.ZodOptional<z.ZodEnum<{
|
|
106
|
+
Strict: "Strict";
|
|
107
|
+
Lax: "Lax";
|
|
108
|
+
None: "None";
|
|
109
|
+
}>>;
|
|
110
|
+
}, z.core.$strict>>;
|
|
111
|
+
}, z.core.$strict>;
|
|
94
112
|
export declare const PAYLOADS: {
|
|
95
113
|
readonly epic: z.ZodObject<{
|
|
96
114
|
data: z.ZodString;
|
|
@@ -106,6 +124,7 @@ export declare const PAYLOADS: {
|
|
|
106
124
|
}, z.core.$strict>;
|
|
107
125
|
readonly unity: z.ZodObject<{
|
|
108
126
|
ulf: z.ZodOptional<z.ZodString>;
|
|
127
|
+
entitlement: z.ZodOptional<z.ZodString>;
|
|
109
128
|
email: z.ZodOptional<z.ZodString>;
|
|
110
129
|
password: z.ZodOptional<z.ZodString>;
|
|
111
130
|
serial: z.ZodOptional<z.ZodString>;
|
|
@@ -151,6 +170,22 @@ export declare const PAYLOADS: {
|
|
|
151
170
|
}>>;
|
|
152
171
|
}, z.core.$strict>>;
|
|
153
172
|
}, z.core.$strict>;
|
|
173
|
+
readonly perplexityapp: z.ZodObject<{
|
|
174
|
+
cookies: z.ZodArray<z.ZodObject<{
|
|
175
|
+
name: z.ZodString;
|
|
176
|
+
value: z.ZodString;
|
|
177
|
+
domain: z.ZodString;
|
|
178
|
+
path: z.ZodString;
|
|
179
|
+
expires: z.ZodOptional<z.ZodNumber>;
|
|
180
|
+
secure: z.ZodBoolean;
|
|
181
|
+
httpOnly: z.ZodBoolean;
|
|
182
|
+
sameSite: z.ZodOptional<z.ZodEnum<{
|
|
183
|
+
Strict: "Strict";
|
|
184
|
+
Lax: "Lax";
|
|
185
|
+
None: "None";
|
|
186
|
+
}>>;
|
|
187
|
+
}, z.core.$strict>>;
|
|
188
|
+
}, z.core.$strict>;
|
|
154
189
|
};
|
|
155
190
|
export type Payload<K extends ServiceKind> = z.output<(typeof PAYLOADS)[K]>;
|
|
156
191
|
export type AnyPayload = Payload<ServiceKind>;
|
|
@@ -173,6 +208,7 @@ export declare const ServiceAccountsSchema: z.ZodObject<{
|
|
|
173
208
|
}, z.core.$strict>>;
|
|
174
209
|
unity: z.ZodOptional<z.ZodObject<{
|
|
175
210
|
ulf: z.ZodOptional<z.ZodString>;
|
|
211
|
+
entitlement: z.ZodOptional<z.ZodString>;
|
|
176
212
|
email: z.ZodOptional<z.ZodString>;
|
|
177
213
|
password: z.ZodOptional<z.ZodString>;
|
|
178
214
|
serial: z.ZodOptional<z.ZodString>;
|
|
@@ -218,6 +254,22 @@ export declare const ServiceAccountsSchema: z.ZodObject<{
|
|
|
218
254
|
}>>;
|
|
219
255
|
}, z.core.$strict>>;
|
|
220
256
|
}, z.core.$strict>>;
|
|
257
|
+
perplexityapp: z.ZodOptional<z.ZodObject<{
|
|
258
|
+
cookies: z.ZodArray<z.ZodObject<{
|
|
259
|
+
name: z.ZodString;
|
|
260
|
+
value: z.ZodString;
|
|
261
|
+
domain: z.ZodString;
|
|
262
|
+
path: z.ZodString;
|
|
263
|
+
expires: z.ZodOptional<z.ZodNumber>;
|
|
264
|
+
secure: z.ZodBoolean;
|
|
265
|
+
httpOnly: z.ZodBoolean;
|
|
266
|
+
sameSite: z.ZodOptional<z.ZodEnum<{
|
|
267
|
+
Strict: "Strict";
|
|
268
|
+
Lax: "Lax";
|
|
269
|
+
None: "None";
|
|
270
|
+
}>>;
|
|
271
|
+
}, z.core.$strict>>;
|
|
272
|
+
}, z.core.$strict>>;
|
|
221
273
|
}, z.core.$strict>;
|
|
222
274
|
export type ServiceAccounts = z.output<typeof ServiceAccountsSchema>;
|
|
223
275
|
/** A launch's id (generate.ts: "<ms>-<6 hex>"). */
|
|
@@ -33,16 +33,24 @@ export const SteamPayloadSchema = z.strictObject({
|
|
|
33
33
|
client: base64(256 * KB).optional(),
|
|
34
34
|
});
|
|
35
35
|
/** A Unity licence file (Unity_lic.ulf), and the Unity ID to sign in with when it has to be made again. */
|
|
36
|
-
export const UnityPayloadSchema = z
|
|
36
|
+
export const UnityPayloadSchema = z
|
|
37
|
+
.strictObject({
|
|
37
38
|
ulf: z
|
|
38
39
|
.string()
|
|
39
40
|
.min(1)
|
|
40
41
|
.max(128 * KB)
|
|
41
42
|
.optional(),
|
|
43
|
+
/** The named-user licence (UnityEntitlementLicense.xml) a Unity sign-in makes. */
|
|
44
|
+
entitlement: z
|
|
45
|
+
.string()
|
|
46
|
+
.min(1)
|
|
47
|
+
.max(128 * KB)
|
|
48
|
+
.optional(),
|
|
42
49
|
email: z.string().email().max(256).optional(),
|
|
43
50
|
password: secretText(1024).optional(),
|
|
44
51
|
serial: secretText(64).optional(),
|
|
45
|
-
})
|
|
52
|
+
})
|
|
53
|
+
.refine((p) => Object.keys(p).length > 0, 'empty Unity sign-in');
|
|
46
54
|
/** A FiveM / RedM server licence key from the Cfx.re keymaster, and an optional Steam Web API key. */
|
|
47
55
|
export const CfxPayloadSchema = z.strictObject({
|
|
48
56
|
licenseKey: z
|
|
@@ -125,6 +133,8 @@ export const CometCookieSchema = z.strictObject({
|
|
|
125
133
|
export const CometPayloadSchema = z.strictObject({
|
|
126
134
|
cookies: z.array(CometCookieSchema).min(1).max(MAX_COMET_COOKIES),
|
|
127
135
|
});
|
|
136
|
+
/** The Perplexity desktop app's sign-in: the same perplexity.ai cookies as Comet's. */
|
|
137
|
+
export const PerplexityAppPayloadSchema = CometPayloadSchema;
|
|
128
138
|
export const PAYLOADS = {
|
|
129
139
|
epic: EpicPayloadSchema,
|
|
130
140
|
steam: SteamPayloadSchema,
|
|
@@ -136,6 +146,7 @@ export const PAYLOADS = {
|
|
|
136
146
|
appstore: AppstorePayloadSchema,
|
|
137
147
|
perplexity: PerplexityPayloadSchema,
|
|
138
148
|
comet: CometPayloadSchema,
|
|
149
|
+
perplexityapp: PerplexityAppPayloadSchema,
|
|
139
150
|
};
|
|
140
151
|
/** The secrets-file field: the sign-ins this launch hands over, each optional. */
|
|
141
152
|
export const ServiceAccountsSchema = z.strictObject({
|
|
@@ -149,6 +160,7 @@ export const ServiceAccountsSchema = z.strictObject({
|
|
|
149
160
|
appstore: AppstorePayloadSchema.optional(),
|
|
150
161
|
perplexity: PerplexityPayloadSchema.optional(),
|
|
151
162
|
comet: CometPayloadSchema.optional(),
|
|
163
|
+
perplexityapp: PerplexityAppPayloadSchema.optional(),
|
|
152
164
|
});
|
|
153
165
|
/** A launch's id (generate.ts: "<ms>-<6 hex>"). */
|
|
154
166
|
export const LaunchIdSchema = z.string().regex(/^[A-Za-z0-9-]{1,64}$/);
|
|
@@ -14,4 +14,11 @@ export declare const COMET_DOWNLOAD_URL = "https://www.perplexity.ai/rest/browse
|
|
|
14
14
|
* subject with E=<contact address> first (E=..., CN="PERPLEXITY AI, INC.", O=...), so CN is not at the start.
|
|
15
15
|
*/
|
|
16
16
|
export declare const COMET_SIGNER: AppSigner;
|
|
17
|
+
/**
|
|
18
|
+
* The Perplexity desktop app is Store-only (its old installer is gone): product 9P9XG917PWCJ, an MSIX of about 300 MB.
|
|
19
|
+
* The package is fetched on this PC with your own Store tooling and installed in the sandbox with Add-AppxPackage.
|
|
20
|
+
*/
|
|
21
|
+
export declare const PERPLEXITY_DESKTOP_PRODUCT_ID = "9P9XG917PWCJ";
|
|
22
|
+
export declare const PERPLEXITY_DESKTOP_PACKAGE = "PerplexityAI.PerplexityApp";
|
|
23
|
+
export declare const PERPLEXITY_DESKTOP_FAMILY = "PerplexityAI.PerplexityApp_3jh4kjrg4dzr2";
|
|
17
24
|
export declare const AGENTS_WEB_APPS: readonly AppEntry[];
|