windows-development-sandbox 1.20.0 → 1.23.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 +8 -1
- package/dist/app/features/access/access.def.js +5 -2
- package/dist/app/features/access/access.run.js +7 -3
- package/dist/app/features/accounts/accounts.def.d.ts +29 -1
- package/dist/app/features/accounts/accounts.def.js +10 -4
- package/dist/app/features/accounts/accounts.run.d.ts +1 -0
- package/dist/app/features/accounts/accounts.run.js +7 -2
- package/dist/app/features/accounts/services.def.d.ts +4 -1
- package/dist/app/features/accounts/services.def.js +15 -14
- package/dist/app/features/accounts/services.run.d.ts +1 -0
- package/dist/app/features/accounts/services.run.js +41 -15
- package/dist/app/features/defs.d.ts +39 -2
- package/dist/app/features/engine/engine.run.js +1 -1
- package/dist/app/features/github/github.def.d.ts +44 -3
- package/dist/app/features/github/github.def.js +145 -63
- package/dist/app/features/github/github.run.d.ts +9 -2
- package/dist/app/features/github/github.run.js +43 -5
- package/dist/app/features/health/health.def.js +8 -2
- package/dist/app/features/logs/logs.def.js +0 -8
- package/dist/app/features/projects/projects.def.js +2 -2
- package/dist/app/features/projects/projects.run.js +5 -5
- package/dist/app/features/sandbox/sandbox.def.js +7 -3
- package/dist/app/features/sessions/sessions.def.d.ts +4 -4
- package/dist/app/features/sessions/sessions.def.js +0 -5
- package/dist/app/features/sessions/sessions.run.d.ts +1 -1
- package/dist/app/features/sessions/sessions.run.js +2 -2
- package/dist/app/features/settings/categories.def.js +2 -0
- package/dist/app/features/settings/settings-rows.js +1 -1
- package/dist/app/features/settings/settings.def.js +1 -1
- package/dist/app/features/settings/settings.run.js +3 -3
- package/dist/app/features/storage/storage.run.js +18 -9
- package/dist/app/features/updates/update-lines.js +2 -2
- package/dist/app/features/updates/updates.def.js +1 -1
- package/dist/app/features/updates/updates.run.js +1 -1
- package/dist/app/features/web/web.def.js +2 -2
- package/dist/app/features/web/web.run.js +12 -4
- package/dist/app/kernel/errors.d.ts +7 -0
- package/dist/app/kernel/errors.js +18 -3
- package/dist/app/kernel/rpc.d.ts +5 -0
- package/dist/app/kernel/view.d.ts +11 -1
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-DK4NTBUF.js → chunk-JETGP2C3.js} +172 -48
- package/dist/bin/{chunk-2IMT7ZCT.js → chunk-XZKOPGNH.js} +1 -1
- package/dist/bin/{chunk-Z2P5OGKL.js → chunk-ZTTOF5V3.js} +2430 -1688
- 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/manifest.d.ts +2 -2
- package/dist/cli/manifest.js +2 -1
- package/dist/cli/menu/coverage-exempt.d.ts +17 -0
- package/dist/cli/menu/coverage-exempt.js +61 -0
- package/dist/cli/menu/io-menu.d.ts +2 -0
- package/dist/cli/menu/io-menu.js +50 -5
- package/dist/cli/menu/pages/accounts-page.js +1 -1
- package/dist/cli/menu/pages/agents-page.js +8 -2
- package/dist/cli/menu/pages/maintenance-page.d.ts +6 -0
- package/dist/cli/menu/pages/maintenance-page.js +9 -2
- package/dist/cli/menu/pages/sessions-page.d.ts +2 -0
- package/dist/cli/menu/pages/sessions-page.js +12 -4
- package/dist/cli/menu/pages/settings-page.js +12 -1
- package/dist/cli/menu/remote-menu.js +1 -1
- package/dist/cli/menu/shell.d.ts +5 -0
- package/dist/cli/menu/shell.js +13 -0
- package/dist/cli/report.js +2 -0
- package/dist/cli/tui/app.d.ts +5 -0
- package/dist/cli/tui/app.js +2 -0
- package/dist/core/config/defaults.js +3 -1
- package/dist/core/schema/accounts.d.ts +6 -0
- package/dist/core/schema/accounts.js +13 -11
- package/dist/core/schema/config.d.ts +20 -8
- package/dist/core/schema/config.js +4 -4
- package/dist/core/schema/github.js +1 -1
- package/dist/core/schema/profile.js +8 -4
- package/dist/core/schema/resume.js +1 -1
- package/dist/core/schema/sandbox.js +3 -3
- package/dist/core/schema/vpn.js +1 -1
- package/dist/core/schema/web.js +3 -3
- package/dist/core/types.d.ts +6 -0
- package/dist/core/util/errors.d.ts +11 -1
- package/dist/core/util/errors.js +7 -1
- package/dist/core/util/index.d.ts +1 -1
- package/dist/core/util/index.js +1 -1
- package/dist/guest/accounts/adapter.d.ts +2 -0
- package/dist/guest/accounts/adapters.js +4 -0
- package/dist/guest/accounts/apply.d.ts +6 -2
- package/dist/guest/accounts/kinds/cfx.d.ts +7 -0
- package/dist/guest/accounts/kinds/cfx.js +33 -3
- package/dist/guest/accounts/kinds/comet.d.ts +7 -0
- package/dist/guest/accounts/kinds/comet.js +50 -0
- package/dist/guest/accounts/kinds/perplexity.d.ts +2 -0
- package/dist/guest/accounts/kinds/perplexity.js +12 -0
- package/dist/guest/accounts/watch.d.ts +2 -0
- package/dist/guest/accounts/watch.js +12 -2
- package/dist/guest/agents/agent-installer.js +1 -1
- package/dist/guest/agents/profile-links.d.ts +2 -0
- package/dist/guest/agents/profile-links.js +18 -4
- package/dist/guest/apps/after-install.js +19 -0
- package/dist/guest/apps/app-installer.d.ts +11 -0
- package/dist/guest/apps/app-installer.js +69 -6
- package/dist/guest/apps/comet-unpack.d.ts +20 -0
- package/dist/guest/apps/comet-unpack.js +127 -0
- package/dist/guest/apps/index.d.ts +1 -1
- package/dist/guest/apps/index.js +1 -1
- package/dist/guest/apps/resolvers-agents-web.d.ts +2 -0
- package/dist/guest/apps/resolvers-agents-web.js +7 -0
- package/dist/guest/apps/resolvers.js +2 -0
- package/dist/guest/apps/shortcuts.d.ts +2 -0
- package/dist/guest/apps/shortcuts.js +1 -0
- package/dist/guest/apps/unpack.js +14 -4
- package/dist/guest/boot/boot.d.ts +45 -16
- package/dist/guest/boot/boot.js +316 -145
- package/dist/guest/boot/graph.d.ts +252 -0
- package/dist/guest/boot/graph.js +257 -0
- package/dist/guest/boot/guide-lines.d.ts +25 -0
- package/dist/guest/boot/guide-lines.js +41 -0
- package/dist/guest/boot/guide.d.ts +1 -4
- package/dist/guest/boot/guide.js +6 -8
- package/dist/guest/boot/runner.d.ts +77 -0
- package/dist/guest/boot/runner.js +215 -0
- package/dist/guest/browser/cdp.d.ts +71 -0
- package/dist/guest/browser/cdp.js +306 -0
- package/dist/guest/browser/comet.d.ts +28 -0
- package/dist/guest/browser/comet.js +155 -0
- package/dist/guest/daemon/project-provisioner.js +6 -1
- package/dist/guest/devtools/fivem.d.ts +1 -1
- package/dist/guest/devtools/fivem.js +243 -3
- package/dist/guest/entries/daemon.js +2 -0
- package/dist/guest/runtime/log.d.ts +12 -0
- package/dist/guest/runtime/log.js +29 -5
- package/dist/guest/runtime/pools.d.ts +24 -0
- package/dist/guest/runtime/pools.js +60 -0
- package/dist/guest/runtime/process.d.ts +4 -0
- package/dist/guest/runtime/process.js +38 -1
- package/dist/guest/runtime/status.d.ts +5 -1
- package/dist/guest/runtime/status.js +19 -0
- package/dist/guest/setup/addon-mcp.d.ts +1 -1
- package/dist/guest/setup/addon-mcp.js +52 -12
- package/dist/guest/setup/agent-addons.d.ts +3 -2
- package/dist/guest/setup/agent-addons.js +12 -3
- package/dist/guest/setup/environment.d.ts +1 -1
- package/dist/guest/setup/environment.js +25 -12
- package/dist/guest/setup/headroom.d.ts +8 -3
- package/dist/guest/setup/headroom.js +44 -15
- package/dist/guest/setup/path-plan.d.ts +28 -0
- package/dist/guest/setup/path-plan.js +69 -2
- package/dist/guest/setup/shell-path.d.ts +1 -1
- package/dist/guest/setup/shell-path.js +3 -3
- package/dist/guest/toolchains/fivem/artifacts.d.ts +48 -1
- package/dist/guest/toolchains/fivem/artifacts.js +97 -4
- package/dist/guest/toolchains/fivem/fivem.d.ts +32 -1
- package/dist/guest/toolchains/fivem/fivem.js +168 -3
- package/dist/guest/toolchains/fivem/scaffold.d.ts +35 -0
- package/dist/guest/toolchains/fivem/scaffold.js +105 -0
- package/dist/guest/toolchains/fivem/serve-plan.d.ts +34 -0
- package/dist/guest/toolchains/fivem/serve-plan.js +61 -0
- package/dist/guest/toolchains/languages/go.js +1 -1
- package/dist/guest/toolchains/languages/node.js +2 -1
- package/dist/guest/toolchains/languages/php.d.ts +2 -0
- package/dist/guest/toolchains/languages/php.js +13 -1
- package/dist/guest/toolchains/languages/python-tools.js +1 -1
- package/dist/guest/toolchains/languages/ruby.js +1 -1
- package/dist/guest/toolchains/native/binstall.js +1 -1
- package/dist/guest/toolchains/native/msvc/packages.js +10 -4
- package/dist/guest/toolchains/native/rust.d.ts +2 -0
- package/dist/guest/toolchains/native/rust.js +11 -4
- package/dist/guest/toolchains/native/unreal.d.ts +2 -0
- package/dist/guest/toolchains/native/unreal.js +16 -6
- package/dist/guest/toolchains/native/vcruntime.d.ts +2 -0
- package/dist/guest/toolchains/native/vcruntime.js +10 -2
- package/dist/guest/toolchains/native/visual-studio.d.ts +3 -0
- package/dist/guest/toolchains/native/visual-studio.js +44 -26
- package/dist/guest/toolchains/native/vs-request.d.ts +2 -0
- package/dist/guest/toolchains/native/vs-request.js +2 -0
- package/dist/guest/toolchains/plan.js +19 -8
- package/dist/guest/toolchains/scheduler.d.ts +2 -21
- package/dist/guest/toolchains/scheduler.js +7 -121
- package/dist/guest/toolchains/setup.d.ts +15 -2
- package/dist/guest/toolchains/setup.js +39 -20
- package/dist/guest/toolchains/split.d.ts +28 -0
- package/dist/guest/toolchains/split.js +60 -0
- package/dist/guest/toolchains/steam/client.js +2 -1
- package/dist/guest/toolchains/step-scheduler.d.ts +51 -0
- package/dist/guest/toolchains/step-scheduler.js +147 -0
- package/dist/guest/toolchains/step.d.ts +13 -0
- package/dist/guest/toolchains/tools/git.js +18 -0
- package/dist/guest/vpn/openvpn.js +3 -4
- package/dist/guest/vpn/wireguard.js +11 -4
- package/dist/host/accounts/deliver.js +7 -0
- package/dist/host/accounts/handoff.js +2 -6
- package/dist/host/accounts/index.d.ts +1 -1
- package/dist/host/accounts/index.js +1 -1
- package/dist/host/accounts/kinds/cfx.d.ts +1 -1
- package/dist/host/accounts/kinds/cfx.js +47 -4
- package/dist/host/accounts/kinds/comet.d.ts +2 -0
- package/dist/host/accounts/kinds/comet.js +6 -0
- package/dist/host/accounts/kinds/perplexity.d.ts +2 -0
- package/dist/host/accounts/kinds/perplexity.js +30 -0
- package/dist/host/accounts/leases.d.ts +7 -1
- package/dist/host/accounts/leases.js +13 -2
- package/dist/host/accounts/source.d.ts +7 -1
- package/dist/host/accounts/source.js +27 -0
- package/dist/host/accounts/sources.js +4 -0
- package/dist/host/github/deliver.d.ts +1 -1
- package/dist/host/github/deliver.js +20 -1
- package/dist/host/github/index.d.ts +2 -1
- package/dist/host/github/index.js +1 -0
- package/dist/host/github/token.d.ts +13 -1
- package/dist/host/github/token.js +19 -2
- package/dist/host/github/verify.d.ts +27 -0
- package/dist/host/github/verify.js +61 -0
- package/dist/host/launch/hints/fivem.d.ts +3 -1
- package/dist/host/launch/hints/fivem.js +68 -2
- package/dist/host/launch/session-file.js +4 -0
- package/dist/host/launch/toolchains.d.ts +2 -1
- package/dist/host/launch/toolchains.js +5 -3
- package/dist/host/vpn/nordvpn.d.ts +2 -4
- package/dist/host/vpn/nordvpn.js +2 -24
- package/dist/runtime/agent.js +405 -90
- package/dist/runtime/daemon.js +3010 -1383
- package/dist/runtime/db.js +333 -90
- package/dist/runtime/main.js +4720 -1954
- package/dist/runtime/netproxy.js +286 -69
- package/dist/runtime/tool.js +2852 -1780
- package/dist/runtime/view.js +286 -69
- package/dist/selftest/agent-paths.test.js +39 -1
- package/dist/selftest/agents-web.test.d.ts +2 -0
- package/dist/selftest/agents-web.test.js +647 -0
- package/dist/selftest/app.test.js +5 -2
- package/dist/selftest/boot-runner.test.d.ts +2 -0
- package/dist/selftest/boot-runner.test.js +456 -0
- package/dist/selftest/channel.test.js +15 -0
- package/dist/selftest/fivem.test.js +249 -3
- package/dist/selftest/github.test.js +94 -9
- package/dist/selftest/guest-boot.test.js +3 -0
- package/dist/selftest/health.test.js +1 -1
- package/dist/selftest/index.js +8 -0
- package/dist/selftest/layout.test.js +5 -0
- package/dist/selftest/menu-coverage.test.d.ts +2 -0
- package/dist/selftest/menu-coverage.test.js +46 -0
- package/dist/selftest/menu-screens.js +37 -1
- package/dist/selftest/persisted-installs.test.js +6 -3
- package/dist/selftest/service-accounts.test.js +3 -2
- package/dist/selftest/support/engine-golden.js +27 -0
- package/dist/selftest/support/fixtures.d.ts +6 -0
- package/dist/selftest/support/web-preview.d.ts +34 -1
- package/dist/selftest/support/web-preview.js +21 -18
- package/dist/selftest/toolchains.test.js +23 -1
- package/dist/selftest/ui-text.test.d.ts +7 -0
- package/dist/selftest/ui-text.test.js +300 -0
- package/dist/shared/accounts/index.d.ts +2 -0
- package/dist/shared/accounts/index.js +2 -0
- package/dist/shared/accounts/kinds/comet.d.ts +12 -0
- package/dist/shared/accounts/kinds/comet.js +64 -0
- package/dist/shared/accounts/kinds/perplexity.d.ts +8 -0
- package/dist/shared/accounts/kinds/perplexity.js +37 -0
- package/dist/shared/accounts/kinds.d.ts +24 -5
- package/dist/shared/accounts/kinds.js +86 -4
- package/dist/shared/accounts/payloads.d.ts +81 -0
- package/dist/shared/accounts/payloads.js +39 -1
- package/dist/shared/apps/agent-addons.d.ts +33 -5
- package/dist/shared/apps/agent-addons.js +95 -3
- package/dist/shared/apps/catalogue-agents-web.d.ts +14 -0
- package/dist/shared/apps/catalogue-agents-web.js +112 -0
- package/dist/shared/apps/catalogue-modding.js +32 -1
- package/dist/shared/apps/catalogue.d.ts +27 -3
- package/dist/shared/apps/catalogue.js +2 -1
- package/dist/shared/apps/index.d.ts +3 -1
- package/dist/shared/apps/index.js +3 -1
- package/dist/shared/channel/schema.d.ts +21 -3
- package/dist/shared/channel/schema.js +7 -0
- package/dist/shared/channel/secrets.d.ts +19 -0
- package/dist/shared/concurrency/index.d.ts +3 -0
- package/dist/shared/concurrency/index.js +3 -0
- package/dist/shared/concurrency/installer-retry.d.ts +6 -0
- package/dist/shared/concurrency/installer-retry.js +10 -0
- package/dist/shared/concurrency/limiter.d.ts +19 -0
- package/dist/shared/concurrency/limiter.js +67 -0
- package/dist/shared/concurrency/pools.d.ts +7 -0
- package/dist/shared/concurrency/pools.js +9 -0
- package/dist/shared/download/archive.d.ts +2 -0
- package/dist/shared/download/archive.js +11 -1
- package/dist/shared/download/bounded-fetch.d.ts +13 -0
- package/dist/shared/download/bounded-fetch.js +40 -0
- package/dist/shared/download/downloader.d.ts +5 -0
- package/dist/shared/download/downloader.js +50 -38
- package/dist/shared/download/index.d.ts +2 -1
- package/dist/shared/download/index.js +2 -1
- package/dist/shared/readiness.d.ts +2 -2
- package/dist/shared/readiness.js +2 -2
- package/dist/web/index.html +1 -1
- package/dist/web/{main-OUUQYNK4.js → main-CCILLX2M.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/package.json +1 -1
- package/sandbox/addons/browser-use-mcp/requirements.in +1 -0
- package/sandbox/addons/browser-use-mcp/requirements.txt +2350 -0
- package/sandbox/addons/chrome-devtools-mcp/package-lock.json +38 -0
- package/sandbox/addons/chrome-devtools-mcp/package.json +7 -0
- package/sandbox/addons/comet-mcp/package-lock.json +1277 -0
- package/sandbox/addons/comet-mcp/package.json +7 -0
- package/sandbox/addons/perplexity-mcp/package-lock.json +1655 -0
- package/sandbox/addons/perplexity-mcp/package.json +7 -0
- package/sandbox/scripts/add-firewall-port.ps1 +18 -4
- package/sandbox/scripts/set-machine-environment.ps1 +30 -15
- package/sandbox/templates/fivem/README.txt +6 -2
- package/sandbox/templates/fivem/cs/Client/Client.csproj +15 -0
- package/sandbox/templates/fivem/cs/Client/ClientMain.cs +20 -0
- package/sandbox/templates/fivem/cs/Server/Server.csproj +14 -0
- package/sandbox/templates/fivem/cs/Server/ServerMain.cs +20 -0
- package/sandbox/templates/fivem/cs/fxmanifest.lua +11 -0
- package/sandbox/templates/fivem/js/client.js +12 -0
- package/sandbox/templates/fivem/js/fxmanifest.lua +9 -0
- package/sandbox/templates/fivem/js/server.js +6 -0
- package/sandbox/templates/fivem/lua/client.lua +8 -0
- package/sandbox/templates/fivem/lua/fxmanifest.lua +10 -0
- package/sandbox/templates/fivem/lua/server.lua +6 -0
- package/sandbox/templates/fivem/server.cfg +37 -0
- package/sandbox/templates/fivem/ts/build.js +17 -0
- package/sandbox/templates/fivem/ts/fxmanifest.lua +9 -0
- package/sandbox/templates/fivem/ts/package.json +16 -0
- package/sandbox/templates/fivem/ts/src/client/client.ts +12 -0
- package/sandbox/templates/fivem/ts/src/client/tsconfig.json +13 -0
- package/sandbox/templates/fivem/ts/src/server/server.ts +6 -0
- package/sandbox/templates/fivem/ts/src/server/tsconfig.json +13 -0
- package/schemas/config.schema.json +56 -27
- package/schemas/sandbox-config.schema.json +37 -14
|
@@ -20,6 +20,11 @@ export const BootProgressSchema = z.strictObject({
|
|
|
20
20
|
done: z.number().int().min(0).max(1000),
|
|
21
21
|
total: z.number().int().min(0).max(1000),
|
|
22
22
|
});
|
|
23
|
+
/** Work that goes on after the agent has started (Visual Studio installing): its name and how it is going. */
|
|
24
|
+
export const BackgroundWorkSchema = z.strictObject({
|
|
25
|
+
name: Text(64),
|
|
26
|
+
state: z.enum(['running', 'done', 'failed']),
|
|
27
|
+
});
|
|
23
28
|
/** C:\AgentData\run\sandbox.json: boot progress of the sandbox as a whole. */
|
|
24
29
|
export const SandboxStatusSchema = z.object({
|
|
25
30
|
launchId: Text(64),
|
|
@@ -33,6 +38,8 @@ export const SandboxStatusSchema = z.object({
|
|
|
33
38
|
addresses: z.array(z.string().max(45)).max(8).optional(),
|
|
34
39
|
/** A malformed progress is dropped, never the status (the phase still counts). */
|
|
35
40
|
progress: BootProgressSchema.optional().catch(undefined),
|
|
41
|
+
/** Installs that continue after the boot is ready; a malformed list is dropped, never the status. */
|
|
42
|
+
background: z.array(BackgroundWorkSchema).max(16).optional().catch(undefined),
|
|
36
43
|
});
|
|
37
44
|
const VersionSchema = z.strictObject({ spec: Text(128), source: Text(256) });
|
|
38
45
|
const DatabasePlanSchema = z.strictObject({
|
|
@@ -51,6 +51,25 @@ export declare const SecretsFileSchema: z.ZodObject<{
|
|
|
51
51
|
keyId: z.ZodString;
|
|
52
52
|
p8: z.ZodString;
|
|
53
53
|
}, z.core.$strict>>;
|
|
54
|
+
perplexity: z.ZodOptional<z.ZodObject<{
|
|
55
|
+
apiKey: z.ZodString;
|
|
56
|
+
}, z.core.$strict>>;
|
|
57
|
+
comet: z.ZodOptional<z.ZodObject<{
|
|
58
|
+
cookies: z.ZodArray<z.ZodObject<{
|
|
59
|
+
name: z.ZodString;
|
|
60
|
+
value: z.ZodString;
|
|
61
|
+
domain: z.ZodString;
|
|
62
|
+
path: z.ZodString;
|
|
63
|
+
expires: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
secure: z.ZodBoolean;
|
|
65
|
+
httpOnly: z.ZodBoolean;
|
|
66
|
+
sameSite: z.ZodOptional<z.ZodEnum<{
|
|
67
|
+
Strict: "Strict";
|
|
68
|
+
Lax: "Lax";
|
|
69
|
+
None: "None";
|
|
70
|
+
}>>;
|
|
71
|
+
}, z.core.$strict>>;
|
|
72
|
+
}, z.core.$strict>>;
|
|
54
73
|
}, z.core.$strict>>;
|
|
55
74
|
liveFoldersKey: z.ZodOptional<z.ZodString>;
|
|
56
75
|
}, z.core.$strict>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** msiexec: "another installation is already in progress". */
|
|
2
|
+
export declare const ERROR_INSTALL_ALREADY_RUNNING = 1618;
|
|
3
|
+
export declare const INSTALLER_RETRIES = 20;
|
|
4
|
+
export declare const INSTALLER_WAIT_MS = 15000;
|
|
5
|
+
/** Whether an installer that exited with `code` after `attempt` tries (1 = the first) should be started again. */
|
|
6
|
+
export declare function installerRetry(code: number | null, attempt: number): boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Windows Installer lets one install run at a time machine-wide: another process holding Global\_MSIExecute makes
|
|
2
|
+
// msiexec exit with 1618. Pure.
|
|
3
|
+
/** msiexec: "another installation is already in progress". */
|
|
4
|
+
export const ERROR_INSTALL_ALREADY_RUNNING = 1618;
|
|
5
|
+
export const INSTALLER_RETRIES = 20;
|
|
6
|
+
export const INSTALLER_WAIT_MS = 15_000;
|
|
7
|
+
/** Whether an installer that exited with `code` after `attempt` tries (1 = the first) should be started again. */
|
|
8
|
+
export function installerRetry(code, attempt) {
|
|
9
|
+
return code === ERROR_INSTALL_ALREADY_RUNNING && attempt <= INSTALLER_RETRIES;
|
|
10
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type Priority = 'foreground' | 'background';
|
|
2
|
+
/** Runs `work` so that every pool slot it asks for queues behind foreground work. */
|
|
3
|
+
export declare function asBackground<T>(work: () => T): T;
|
|
4
|
+
export declare class Limiter {
|
|
5
|
+
private readonly size;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
private active;
|
|
8
|
+
private peakActive;
|
|
9
|
+
private readonly queues;
|
|
10
|
+
constructor(size: number, name?: string);
|
|
11
|
+
/** The most slots that were ever busy at once (tests). */
|
|
12
|
+
get peak(): number;
|
|
13
|
+
get running(): number;
|
|
14
|
+
/** Runs `work` in a free slot (waiting its turn), and frees the slot afterwards. */
|
|
15
|
+
run<T>(work: () => Promise<T> | T, priority?: Priority): Promise<T>;
|
|
16
|
+
private acquire;
|
|
17
|
+
private take;
|
|
18
|
+
private release;
|
|
19
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// A counting limiter for heavy work (downloads, unpacking, installers). FIFO, foreground before background. A task
|
|
2
|
+
// that holds a slot may not take a slot of another limiter: nested pools throw, so a deadlock cannot happen. Pure.
|
|
3
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
4
|
+
/** The pool whose slot the current async context holds (at most one). */
|
|
5
|
+
const held = new AsyncLocalStorage();
|
|
6
|
+
/** The priority of work started in this async context (set by `asBackground`). */
|
|
7
|
+
const priorityOf = new AsyncLocalStorage();
|
|
8
|
+
/** Runs `work` so that every pool slot it asks for queues behind foreground work. */
|
|
9
|
+
export function asBackground(work) {
|
|
10
|
+
return priorityOf.run('background', work);
|
|
11
|
+
}
|
|
12
|
+
export class Limiter {
|
|
13
|
+
size;
|
|
14
|
+
name;
|
|
15
|
+
active = 0;
|
|
16
|
+
peakActive = 0;
|
|
17
|
+
queues = { foreground: [], background: [] };
|
|
18
|
+
constructor(size, name = 'pool') {
|
|
19
|
+
this.size = size;
|
|
20
|
+
this.name = name;
|
|
21
|
+
if (!Number.isInteger(size) || size < 1)
|
|
22
|
+
throw new Error('A pool needs a size of at least 1');
|
|
23
|
+
}
|
|
24
|
+
/** The most slots that were ever busy at once (tests). */
|
|
25
|
+
get peak() {
|
|
26
|
+
return this.peakActive;
|
|
27
|
+
}
|
|
28
|
+
get running() {
|
|
29
|
+
return this.active;
|
|
30
|
+
}
|
|
31
|
+
/** Runs `work` in a free slot (waiting its turn), and frees the slot afterwards. */
|
|
32
|
+
async run(work, priority = priorityOf.getStore() ?? 'foreground') {
|
|
33
|
+
const holding = held.getStore();
|
|
34
|
+
if (holding)
|
|
35
|
+
throw new Error('nested pool: ' + this.name + ' requested while holding ' + holding.name);
|
|
36
|
+
await this.acquire(priority);
|
|
37
|
+
try {
|
|
38
|
+
return await held.run(this, async () => work());
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
this.release();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
acquire(priority) {
|
|
45
|
+
if (this.active < this.size) {
|
|
46
|
+
this.take();
|
|
47
|
+
return Promise.resolve();
|
|
48
|
+
}
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
this.queues[priority].push({
|
|
51
|
+
start: () => {
|
|
52
|
+
this.take();
|
|
53
|
+
resolve();
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
take() {
|
|
59
|
+
this.active++;
|
|
60
|
+
this.peakActive = Math.max(this.peakActive, this.active);
|
|
61
|
+
}
|
|
62
|
+
release() {
|
|
63
|
+
this.active--;
|
|
64
|
+
const next = this.queues.foreground.shift() ?? this.queues.background.shift();
|
|
65
|
+
next?.start();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Limiter } from './limiter.js';
|
|
2
|
+
/** HTTP bodies. */
|
|
3
|
+
export declare const net: Limiter;
|
|
4
|
+
/** extractArchive, 7-Zip/NSIS unpack, npm ci/install, uv/pip, Steam, robocopy of big layouts, rustup/cargo. */
|
|
5
|
+
export declare const heavy: Limiter;
|
|
6
|
+
/** Every msiexec, vcredist, DXSETUP and the Visual Studio bootstrapper: Windows runs one install at a time. */
|
|
7
|
+
export declare const installer: Limiter;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// The three pools (one set per process): the limiter instances only, so pure code can take a slot. The guest wires
|
|
2
|
+
// them to the downloader and extractor (guest/runtime/pools.ts installPools).
|
|
3
|
+
import { Limiter } from './limiter.js';
|
|
4
|
+
/** HTTP bodies. */
|
|
5
|
+
export const net = new Limiter(4, 'net');
|
|
6
|
+
/** extractArchive, 7-Zip/NSIS unpack, npm ci/install, uv/pip, Steam, robocopy of big layouts, rustup/cargo. */
|
|
7
|
+
export const heavy = new Limiter(2, 'heavy');
|
|
8
|
+
/** Every msiexec, vcredist, DXSETUP and the Visual Studio bootstrapper: Windows runs one install at a time. */
|
|
9
|
+
export const installer = new Limiter(1, 'installer');
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Limiter } from '../concurrency/index.js';
|
|
1
2
|
/**
|
|
2
3
|
* A fresh `<destination>.staging-<hex>` folder next to `destination`: fill it, then moveIntoPlace. An interrupted copy
|
|
3
4
|
* or extraction leaves only the staging folder (cleanup/repair remove those), never a half-filled destination.
|
|
@@ -5,6 +6,7 @@
|
|
|
5
6
|
export declare function stagingFolder(destination: string): string;
|
|
6
7
|
/** Replaces `destination` with the finished `work` folder (a folder rename: the destination is never half there). */
|
|
7
8
|
export declare function moveIntoPlace(work: string, destination: string): void;
|
|
9
|
+
export declare function limitExtraction(limiter: Limiter | null): void;
|
|
8
10
|
/**
|
|
9
11
|
* Extracts .zip / .nupkg / .7z / .tar.gz with the tar.exe that ships with Windows, falling back to Expand-Archive.
|
|
10
12
|
* `members` limits it to those paths inside the archive (e.g. skip pgAdmin in the PostgreSQL zip).
|
|
@@ -18,11 +18,21 @@ export function moveIntoPlace(work, destination) {
|
|
|
18
18
|
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
19
19
|
fs.renameSync(work, destination);
|
|
20
20
|
}
|
|
21
|
+
/** The guest's heavy pool (set by the guest at start-up; host code leaves it unset). */
|
|
22
|
+
let extractLimit = null;
|
|
23
|
+
export function limitExtraction(limiter) {
|
|
24
|
+
extractLimit = limiter;
|
|
25
|
+
}
|
|
21
26
|
/**
|
|
22
27
|
* Extracts .zip / .nupkg / .7z / .tar.gz with the tar.exe that ships with Windows, falling back to Expand-Archive.
|
|
23
28
|
* `members` limits it to those paths inside the archive (e.g. skip pgAdmin in the PostgreSQL zip).
|
|
24
29
|
*/
|
|
25
|
-
export
|
|
30
|
+
export function extractArchive(archive, destination, members = []) {
|
|
31
|
+
return extractLimit
|
|
32
|
+
? extractLimit.run(() => extract(archive, destination, members))
|
|
33
|
+
: extract(archive, destination, members);
|
|
34
|
+
}
|
|
35
|
+
async function extract(archive, destination, members) {
|
|
26
36
|
fs.mkdirSync(destination, { recursive: true });
|
|
27
37
|
let status = await spawnAsync(SYSTEM.tar(), ['-xf', archive, '-C', destination, ...members]);
|
|
28
38
|
if (status !== 0 && /\.(zip|nupkg)$/i.test(archive)) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Reads a URL as text, refusing more than `max` bytes. */
|
|
2
|
+
export type Fetcher = (url: string, max: number) => Promise<string>;
|
|
3
|
+
/** An answer whatever its status: the status, the header values asked for, and the body (at most `max` bytes). */
|
|
4
|
+
export interface BoundedResponse {
|
|
5
|
+
readonly status: number;
|
|
6
|
+
readonly header: (name: string) => string | null;
|
|
7
|
+
readonly text: string;
|
|
8
|
+
}
|
|
9
|
+
/** Reads a request with headers (or any other init) once; a status that is not 200 is an answer, not an error. */
|
|
10
|
+
export type RequestFetcher = (url: string, max: number, init?: RequestInit) => Promise<BoundedResponse>;
|
|
11
|
+
export declare function fetchBounded(url: string, max: number): Promise<string>;
|
|
12
|
+
/** One request with a timeout (10 s by default), no retries: for a check that should answer quickly. */
|
|
13
|
+
export declare function fetchBoundedResponse(url: string, max: number, init?: RequestInit, timeoutMs?: number): Promise<BoundedResponse>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Reading an answer from the internet with a size limit: a server cannot make this PC download without end. Node
|
|
2
|
+
// built-ins only (it is part of the shared download helpers).
|
|
3
|
+
import { fetchRetry } from './http.js';
|
|
4
|
+
/** The body of a response, refusing more than `max` bytes. */
|
|
5
|
+
async function readBounded(response, url, max) {
|
|
6
|
+
const host = new URL(url).host;
|
|
7
|
+
const length = Number(response.headers.get('content-length')) || 0;
|
|
8
|
+
if (length > max)
|
|
9
|
+
throw new Error('The answer from ' + host + ' is too big.');
|
|
10
|
+
const reader = response.body?.getReader();
|
|
11
|
+
if (!reader)
|
|
12
|
+
throw new Error('Empty answer from ' + host + '.');
|
|
13
|
+
const chunks = [];
|
|
14
|
+
let size = 0;
|
|
15
|
+
for (;;) {
|
|
16
|
+
const { done, value } = await reader.read();
|
|
17
|
+
if (done)
|
|
18
|
+
break;
|
|
19
|
+
size += value.length;
|
|
20
|
+
if (size > max) {
|
|
21
|
+
await reader.cancel();
|
|
22
|
+
throw new Error('The answer from ' + host + ' is too big.');
|
|
23
|
+
}
|
|
24
|
+
chunks.push(value);
|
|
25
|
+
}
|
|
26
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
27
|
+
}
|
|
28
|
+
export async function fetchBounded(url, max) {
|
|
29
|
+
return readBounded(await fetchRetry(url, {}, 2, 30000), url, max);
|
|
30
|
+
}
|
|
31
|
+
/** One request with a timeout (10 s by default), no retries: for a check that should answer quickly. */
|
|
32
|
+
export async function fetchBoundedResponse(url, max, init = {}, timeoutMs = 10000) {
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
...init,
|
|
35
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
36
|
+
headers: { 'User-Agent': 'windows-development-sandbox', ...(init.headers ?? {}) },
|
|
37
|
+
});
|
|
38
|
+
const text = await readBounded(response, url, max);
|
|
39
|
+
return { status: response.status, header: (name) => response.headers.get(name), text };
|
|
40
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Limiter } from '../concurrency/index.js';
|
|
1
2
|
/** Expected checksums of a download; any that are given must match. */
|
|
2
3
|
export interface Checksums {
|
|
3
4
|
sha256?: string | null;
|
|
@@ -16,6 +17,8 @@ export interface ArchiveOptions extends Checksums {
|
|
|
16
17
|
/** Folders above `locate`'s to keep. */
|
|
17
18
|
up?: number;
|
|
18
19
|
fileName?: string;
|
|
20
|
+
/** A copy of the archive that `file()` already downloaded (checked then): used and removed instead of downloading. */
|
|
21
|
+
cached?: string;
|
|
19
22
|
members?: string[];
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
@@ -26,6 +29,8 @@ export type DownloadWatcher = () => ((fraction: number) => void) | null;
|
|
|
26
29
|
export declare class Downloader {
|
|
27
30
|
/** downloads.requireChecksum: refuse anything without an expected hash. The guest sets it from session.json. */
|
|
28
31
|
requireChecksum: boolean;
|
|
32
|
+
/** The guest's net pool: how many HTTP bodies stream at once. Host code leaves it null. */
|
|
33
|
+
limit: Limiter | null;
|
|
29
34
|
private watcher;
|
|
30
35
|
/** Reports coarse progress of the downloads that start from now on to `watcher` (null: stop). */
|
|
31
36
|
watch(watcher: DownloadWatcher | null): void;
|
|
@@ -9,6 +9,8 @@ import { extractArchive, findFile, moveIntoPlace, stagingFolder } from './archiv
|
|
|
9
9
|
export class Downloader {
|
|
10
10
|
/** downloads.requireChecksum: refuse anything without an expected hash. The guest sets it from session.json. */
|
|
11
11
|
requireChecksum = false;
|
|
12
|
+
/** The guest's net pool: how many HTTP bodies stream at once. Host code leaves it null. */
|
|
13
|
+
limit = null;
|
|
12
14
|
watcher = null;
|
|
13
15
|
/** Reports coarse progress of the downloads that start from now on to `watcher` (null: stop). */
|
|
14
16
|
watch(watcher) {
|
|
@@ -28,48 +30,52 @@ export class Downloader {
|
|
|
28
30
|
url +
|
|
29
31
|
'): no checksum is published for it, and downloads.requireChecksum is on. Turn that setting off to allow downloads that cannot be verified.');
|
|
30
32
|
}
|
|
31
|
-
const response = await fetchRetry(url, {}, 3, 20 * 60 * 1000);
|
|
32
|
-
if (!response.body)
|
|
33
|
-
throw new Error('Empty response for ' + url);
|
|
34
33
|
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
35
34
|
// Only the hashes that will be checked (sha512 of a 330 MB archive is not free).
|
|
36
35
|
const checks = ['sha256', 'sha512', 'sha1'].flatMap((kind) => {
|
|
37
36
|
const want = sums[kind]?.trim().toLowerCase();
|
|
38
37
|
return want ? [{ want, hash: crypto.createHash(kind) }] : [];
|
|
39
38
|
});
|
|
40
|
-
// Progress for the boot screen: every 10% or 5 s at most, never per chunk; called here so it knows who is
|
|
41
|
-
// downloading.
|
|
42
|
-
const size = Number(response.headers.get('content-length')) || 0;
|
|
43
|
-
const report = size > 0 ? (this.watcher?.() ?? null) : null;
|
|
44
|
-
let received = 0;
|
|
45
|
-
let tenth = 0;
|
|
46
|
-
let reportedAt = Date.now();
|
|
47
|
-
const tap = new Transform({
|
|
48
|
-
transform(chunk, _encoding, done) {
|
|
49
|
-
for (const c of checks)
|
|
50
|
-
c.hash.update(chunk);
|
|
51
|
-
if (report) {
|
|
52
|
-
received += chunk.length;
|
|
53
|
-
const now = Math.floor((received * 10) / size);
|
|
54
|
-
if (now !== tenth || Date.now() - reportedAt > 5000) {
|
|
55
|
-
tenth = now;
|
|
56
|
-
reportedAt = Date.now();
|
|
57
|
-
try {
|
|
58
|
-
report(Math.min(1, received / size));
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
/* progress never breaks a download */
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
done(null, chunk);
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
39
|
// Written to <destination>.part and moved into place only once complete and verified, so an interrupted download
|
|
69
40
|
// never leaves a partial file under the real name.
|
|
70
41
|
const partial = destination + '.part';
|
|
71
|
-
|
|
42
|
+
// Only the transfer holds a net slot; the signature check and the rename below stay outside it.
|
|
43
|
+
const transfer = async () => {
|
|
44
|
+
const response = await fetchRetry(url, {}, 3, 20 * 60 * 1000);
|
|
45
|
+
if (!response.body)
|
|
46
|
+
throw new Error('Empty response for ' + url);
|
|
47
|
+
// Progress for the boot screen: every 10% or 5 s at most, never per chunk; called here so it knows who is
|
|
48
|
+
// downloading.
|
|
49
|
+
const size = Number(response.headers.get('content-length')) || 0;
|
|
50
|
+
const report = size > 0 ? (this.watcher?.() ?? null) : null;
|
|
51
|
+
let received = 0;
|
|
52
|
+
let tenth = 0;
|
|
53
|
+
let reportedAt = Date.now();
|
|
54
|
+
const tap = new Transform({
|
|
55
|
+
transform(chunk, _encoding, done) {
|
|
56
|
+
for (const c of checks)
|
|
57
|
+
c.hash.update(chunk);
|
|
58
|
+
if (report) {
|
|
59
|
+
received += chunk.length;
|
|
60
|
+
const now = Math.floor((received * 10) / size);
|
|
61
|
+
if (now !== tenth || Date.now() - reportedAt > 5000) {
|
|
62
|
+
tenth = now;
|
|
63
|
+
reportedAt = Date.now();
|
|
64
|
+
try {
|
|
65
|
+
report(Math.min(1, received / size));
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
/* progress never breaks a download */
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
done(null, chunk);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
72
75
|
await pipeline(Readable.fromWeb(response.body), tap, fs.createWriteStream(partial));
|
|
76
|
+
};
|
|
77
|
+
try {
|
|
78
|
+
await (this.limit ? this.limit.run(transfer) : transfer());
|
|
73
79
|
if (checks.some((c) => c.hash.digest('hex') !== c.want))
|
|
74
80
|
throw new Error('Checksum mismatch for ' + url);
|
|
75
81
|
if (sums.verify)
|
|
@@ -90,12 +96,18 @@ export class Downloader {
|
|
|
90
96
|
const work = stagingFolder(destination);
|
|
91
97
|
try {
|
|
92
98
|
const archive = path.join(work, options.fileName ?? path.basename(new URL(url).pathname));
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
if (options.cached && fs.existsSync(options.cached)) {
|
|
100
|
+
fs.mkdirSync(work, { recursive: true });
|
|
101
|
+
fs.copyFileSync(options.cached, archive);
|
|
102
|
+
fs.rmSync(options.cached, { force: true });
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
await this.file(url, archive, {
|
|
106
|
+
sha256: options.sha256,
|
|
107
|
+
sha512: options.sha512,
|
|
108
|
+
sha1: options.sha1,
|
|
109
|
+
...(options.verify ? { verify: options.verify } : {}),
|
|
110
|
+
});
|
|
99
111
|
const extracted = path.join(work, 'x');
|
|
100
112
|
await extractArchive(archive, extracted, options.members);
|
|
101
113
|
let root = extracted;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { fetchRetry, fetchJson, fetchText } from './http.js';
|
|
2
|
-
export {
|
|
2
|
+
export { fetchBounded, fetchBoundedResponse, type BoundedResponse, type Fetcher, type RequestFetcher, } from './bounded-fetch.js';
|
|
3
|
+
export { stagingFolder, moveIntoPlace, extractArchive, findFile, limitExtraction } from './archive.js';
|
|
3
4
|
export { type ArchiveOptions, type Checksums, type DownloadWatcher, Downloader, downloader } from './downloader.js';
|
|
4
5
|
export { spawnAsync } from './spawn.js';
|
|
5
6
|
export { type ReleaseAsset, githubAsset, parseReleasePage, latestGithubTag, parseReleaseTags } from './github.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { fetchRetry, fetchJson, fetchText } from './http.js';
|
|
2
|
-
export {
|
|
2
|
+
export { fetchBounded, fetchBoundedResponse, } from './bounded-fetch.js';
|
|
3
|
+
export { stagingFolder, moveIntoPlace, extractArchive, findFile, limitExtraction } from './archive.js';
|
|
3
4
|
export { Downloader, downloader } from './downloader.js';
|
|
4
5
|
export { spawnAsync } from './spawn.js';
|
|
5
6
|
export { githubAsset, parseReleasePage, latestGithubTag, parseReleaseTags } from './github.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export declare const NOT_READY: {
|
|
2
2
|
/** Toolchains whose step is a stub: their settings rows are hidden and "auto" never turns them on. */
|
|
3
|
-
readonly toolchains: readonly ["unity", "godot", "android", "flutter", "dart", "reactNative"
|
|
3
|
+
readonly toolchains: readonly ["unity", "godot", "android", "flutter", "dart", "reactNative"];
|
|
4
4
|
/** Service sign-ins whose host or guest side is a stub: the menu and the web do not list them. */
|
|
5
|
-
readonly kinds: readonly ["unity", "keystore", "play", "expo", "appstore"
|
|
5
|
+
readonly kinds: readonly ["unity", "keystore", "play", "expo", "appstore"];
|
|
6
6
|
readonly unreal: readonly ["engines"];
|
|
7
7
|
};
|
|
8
8
|
export declare const isToolchainReady: (name: string) => boolean;
|
package/dist/shared/readiness.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// entries here (the end of round 2 leaves it empty). Pure.
|
|
3
3
|
export const NOT_READY = {
|
|
4
4
|
/** Toolchains whose step is a stub: their settings rows are hidden and "auto" never turns them on. */
|
|
5
|
-
toolchains: ['unity', 'godot', 'android', 'flutter', 'dart', 'reactNative'
|
|
5
|
+
toolchains: ['unity', 'godot', 'android', 'flutter', 'dart', 'reactNative'],
|
|
6
6
|
/** Service sign-ins whose host or guest side is a stub: the menu and the web do not list them. */
|
|
7
|
-
kinds: ['unity', 'keystore', 'play', 'expo', 'appstore'
|
|
7
|
+
kinds: ['unity', 'keystore', 'play', 'expo', 'appstore'],
|
|
8
8
|
unreal: ['engines'],
|
|
9
9
|
};
|
|
10
10
|
export const isToolchainReady = (name) => !NOT_READY.toolchains.includes(name);
|
package/dist/web/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Sandbox</title>
|
|
7
7
|
<link rel="icon" href="data:," />
|
|
8
8
|
<link rel="stylesheet" href="/main-XT7T6ODM.css" />
|
|
9
|
-
<script type="module" src="/main-
|
|
9
|
+
<script type="module" src="/main-CCILLX2M.js"></script>
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|