windows-development-sandbox 1.17.0 → 1.18.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/README.md +92 -92
- package/config.example.json +97 -4
- package/dist/app/features/accounts/accounts.def.d.ts +116 -2
- package/dist/app/features/accounts/accounts.def.js +80 -12
- package/dist/app/features/accounts/accounts.run.d.ts +29 -0
- package/dist/app/features/accounts/accounts.run.js +36 -6
- package/dist/app/features/accounts/index.d.ts +2 -0
- package/dist/app/features/accounts/index.js +4 -1
- package/dist/app/features/accounts/services.def.d.ts +111 -0
- package/dist/app/features/accounts/services.def.js +187 -0
- package/dist/app/features/accounts/services.run.d.ts +65 -0
- package/dist/app/features/accounts/services.run.js +212 -0
- package/dist/app/features/defs.d.ts +112 -1
- package/dist/app/features/defs.js +5 -0
- package/dist/app/features/settings/categories.def.js +21 -4
- package/dist/app/registry.js +5 -1
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-QLGGAG3D.js → chunk-BKF43GJ3.js} +51 -16
- package/dist/bin/{chunk-REE3EMHD.js → chunk-YD3A67RS.js} +3812 -1702
- package/dist/bin/{chunk-XYUVIVJT.js → chunk-Z5D32FN3.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 +30 -1
- package/dist/cli/manifest.d.ts +93 -65
- package/dist/cli/manifest.js +23 -5
- package/dist/cli/menu/pages/accounts-page.js +31 -16
- package/dist/cli/services/accounts-service.d.ts +14 -4
- package/dist/cli/services/accounts-service.js +19 -6
- package/dist/cli/services/data-collect.js +26 -1
- package/dist/core/config/defaults.js +47 -4
- package/dist/core/detect/toolchains.d.ts +15 -1
- package/dist/core/detect/toolchains.js +45 -12
- package/dist/core/local-config/overrides.js +6 -0
- package/dist/core/schema/accounts.d.ts +27 -0
- package/dist/core/schema/accounts.js +32 -0
- package/dist/core/schema/config.d.ts +239 -0
- package/dist/core/schema/config.js +6 -0
- package/dist/core/schema/pc-installs.d.ts +7 -0
- package/dist/core/schema/pc-installs.js +17 -0
- package/dist/core/schema/toolchains.d.ts +61 -0
- package/dist/core/schema/toolchains.js +106 -0
- package/dist/core/types.d.ts +18 -0
- package/dist/guest/accounts/adapter.d.ts +25 -0
- package/dist/guest/accounts/adapter.js +21 -0
- package/dist/guest/accounts/adapters.d.ts +5 -0
- package/dist/guest/accounts/adapters.js +18 -0
- package/dist/guest/accounts/apply.d.ts +41 -0
- package/dist/guest/accounts/apply.js +47 -0
- package/dist/guest/accounts/kinds/appstore.d.ts +2 -0
- package/dist/guest/accounts/kinds/appstore.js +7 -0
- package/dist/guest/accounts/kinds/cfx.d.ts +2 -0
- package/dist/guest/accounts/kinds/cfx.js +7 -0
- package/dist/guest/accounts/kinds/epic.d.ts +2 -0
- package/dist/guest/accounts/kinds/epic.js +26 -0
- package/dist/guest/accounts/kinds/expo.d.ts +2 -0
- package/dist/guest/accounts/kinds/expo.js +7 -0
- package/dist/guest/accounts/kinds/keystore.d.ts +2 -0
- package/dist/guest/accounts/kinds/keystore.js +7 -0
- package/dist/guest/accounts/kinds/play.d.ts +2 -0
- package/dist/guest/accounts/kinds/play.js +7 -0
- package/dist/guest/accounts/kinds/steam.d.ts +4 -0
- package/dist/guest/accounts/kinds/steam.js +17 -0
- package/dist/guest/accounts/kinds/unity.d.ts +2 -0
- package/dist/guest/accounts/kinds/unity.js +7 -0
- package/dist/guest/accounts/watch.d.ts +28 -0
- package/dist/guest/accounts/watch.js +78 -0
- package/dist/guest/agents/agent-files.d.ts +6 -0
- package/dist/guest/agents/agent-files.js +14 -0
- package/dist/guest/agents/local-state.d.ts +2 -0
- package/dist/guest/agents/local-state.js +13 -1
- package/dist/guest/apps/resolvers-gamedev.d.ts +2 -0
- package/dist/guest/apps/resolvers-gamedev.js +1 -0
- package/dist/guest/apps/resolvers-mobile.d.ts +2 -0
- package/dist/guest/apps/resolvers-mobile.js +1 -0
- package/dist/guest/apps/resolvers.js +4 -0
- package/dist/guest/boot/boot.d.ts +5 -0
- package/dist/guest/boot/boot.js +36 -1
- package/dist/guest/daemon/accounts.d.ts +3 -0
- package/dist/guest/daemon/accounts.js +25 -0
- package/dist/guest/daemon/daemon.js +2 -0
- package/dist/guest/daemon/idle-shutdown.js +3 -1
- package/dist/guest/daemon/sessions.js +2 -0
- package/dist/guest/devtools/android.d.ts +2 -0
- package/dist/guest/devtools/android.js +7 -0
- package/dist/guest/devtools/devtool.d.ts +9 -0
- package/dist/guest/devtools/devtool.js +5 -0
- package/dist/guest/devtools/fivem.d.ts +2 -0
- package/dist/guest/devtools/fivem.js +7 -0
- package/dist/guest/devtools/registry.d.ts +6 -0
- package/dist/guest/devtools/registry.js +12 -0
- package/dist/guest/devtools/steam.d.ts +2 -0
- package/dist/guest/devtools/steam.js +228 -0
- package/dist/guest/devtools/unity.d.ts +2 -0
- package/dist/guest/devtools/unity.js +7 -0
- package/dist/guest/entries/daemon.js +2 -0
- package/dist/guest/entries/tool.d.ts +1 -0
- package/dist/guest/entries/tool.js +17 -0
- package/dist/guest/toolchains/engines/godot.d.ts +4 -0
- package/dist/guest/toolchains/engines/godot.js +4 -0
- package/dist/guest/toolchains/engines/unity-release.d.ts +15 -0
- package/dist/guest/toolchains/engines/unity-release.js +6 -0
- package/dist/guest/toolchains/engines/unity.d.ts +4 -0
- package/dist/guest/toolchains/engines/unity.js +4 -0
- package/dist/guest/toolchains/engines/unreal-engines.d.ts +4 -0
- package/dist/guest/toolchains/engines/unreal-engines.js +4 -0
- package/dist/guest/toolchains/fivem/artifacts.d.ts +6 -0
- package/dist/guest/toolchains/fivem/artifacts.js +7 -0
- package/dist/guest/toolchains/fivem/fivem.d.ts +4 -0
- package/dist/guest/toolchains/fivem/fivem.js +4 -0
- package/dist/guest/toolchains/installers.d.ts +12 -0
- package/dist/guest/toolchains/installers.js +39 -0
- package/dist/guest/toolchains/languages/java.js +1 -1
- package/dist/guest/toolchains/languages/kotlin.d.ts +3 -0
- package/dist/guest/toolchains/languages/kotlin.js +6 -0
- package/dist/guest/toolchains/mobile/android-repo.d.ts +11 -0
- package/dist/guest/toolchains/mobile/android-repo.js +10 -0
- package/dist/guest/toolchains/mobile/android.d.ts +17 -0
- package/dist/guest/toolchains/mobile/android.js +16 -0
- package/dist/guest/toolchains/mobile/dart.d.ts +4 -0
- package/dist/guest/toolchains/mobile/dart.js +4 -0
- package/dist/guest/toolchains/mobile/flutter.d.ts +4 -0
- package/dist/guest/toolchains/mobile/flutter.js +4 -0
- package/dist/guest/toolchains/mobile/react-native.d.ts +4 -0
- package/dist/guest/toolchains/mobile/react-native.js +4 -0
- package/dist/guest/toolchains/native/unreal.d.ts +3 -7
- package/dist/guest/toolchains/native/unreal.js +16 -155
- package/dist/guest/toolchains/native/visual-studio.d.ts +15 -0
- package/dist/guest/toolchains/native/visual-studio.js +159 -0
- package/dist/guest/toolchains/native/vs-request.d.ts +19 -0
- package/dist/guest/toolchains/native/vs-request.js +46 -0
- package/dist/guest/toolchains/not-yet.d.ts +2 -0
- package/dist/guest/toolchains/not-yet.js +6 -0
- package/dist/guest/toolchains/persisted.d.ts +48 -0
- package/dist/guest/toolchains/persisted.js +159 -0
- package/dist/guest/toolchains/plan.js +80 -4
- package/dist/guest/toolchains/steam/apps.d.ts +27 -0
- package/dist/guest/toolchains/steam/apps.js +112 -0
- package/dist/guest/toolchains/steam/client.d.ts +5 -0
- package/dist/guest/toolchains/steam/client.js +67 -0
- package/dist/guest/toolchains/steam/commands.d.ts +58 -0
- package/dist/guest/toolchains/steam/commands.js +155 -0
- package/dist/guest/toolchains/steam/steam.d.ts +4 -0
- package/dist/guest/toolchains/steam/steam.js +61 -0
- package/dist/guest/toolchains/steam/steamcmd-install.d.ts +29 -0
- package/dist/guest/toolchains/steam/steamcmd-install.js +177 -0
- package/dist/guest/toolchains/steam/steamcmd.d.ts +6 -0
- package/dist/guest/toolchains/steam/steamcmd.js +23 -0
- package/dist/guest/toolchains/step.d.ts +10 -0
- package/dist/host/accounts/deliver.d.ts +26 -0
- package/dist/host/accounts/deliver.js +80 -0
- package/dist/host/accounts/handoff.d.ts +17 -0
- package/dist/host/accounts/handoff.js +110 -0
- package/dist/host/accounts/index.d.ts +6 -0
- package/dist/host/accounts/index.js +8 -0
- package/dist/host/accounts/kinds/appstore.d.ts +2 -0
- package/dist/host/accounts/kinds/appstore.js +10 -0
- package/dist/host/accounts/kinds/cfx.d.ts +2 -0
- package/dist/host/accounts/kinds/cfx.js +7 -0
- package/dist/host/accounts/kinds/epic.d.ts +2 -0
- package/dist/host/accounts/kinds/epic.js +15 -0
- package/dist/host/accounts/kinds/expo.d.ts +2 -0
- package/dist/host/accounts/kinds/expo.js +7 -0
- package/dist/host/accounts/kinds/keystore.d.ts +2 -0
- package/dist/host/accounts/kinds/keystore.js +7 -0
- package/dist/host/accounts/kinds/play.d.ts +2 -0
- package/dist/host/accounts/kinds/play.js +7 -0
- package/dist/host/accounts/kinds/steam.d.ts +6 -0
- package/dist/host/accounts/kinds/steam.js +31 -0
- package/dist/host/accounts/kinds/unity.d.ts +2 -0
- package/dist/host/accounts/kinds/unity.js +13 -0
- package/dist/host/accounts/leases.d.ts +40 -0
- package/dist/host/accounts/leases.js +62 -0
- package/dist/host/accounts/source.d.ts +21 -0
- package/dist/host/accounts/source.js +15 -0
- package/dist/host/accounts/sources.d.ts +5 -0
- package/dist/host/accounts/sources.js +18 -0
- package/dist/host/accounts/store.d.ts +23 -0
- package/dist/host/accounts/store.js +74 -0
- package/dist/host/audit/log.d.ts +1 -1
- package/dist/host/launch/generate.d.ts +2 -0
- package/dist/host/launch/generate.js +16 -1
- package/dist/host/launch/hints/android.d.ts +3 -0
- package/dist/host/launch/hints/android.js +3 -0
- package/dist/host/launch/hints/engines.d.ts +3 -0
- package/dist/host/launch/hints/engines.js +3 -0
- package/dist/host/launch/hints/fivem.d.ts +3 -0
- package/dist/host/launch/hints/fivem.js +3 -0
- package/dist/host/launch/hints/hint.d.ts +2 -0
- package/dist/host/launch/hints/hint.js +1 -0
- package/dist/host/launch/hints/index.d.ts +5 -0
- package/dist/host/launch/hints/index.js +29 -0
- package/dist/host/launch/hints/mobile.d.ts +3 -0
- package/dist/host/launch/hints/mobile.js +3 -0
- package/dist/host/launch/hints/steam.d.ts +7 -0
- package/dist/host/launch/hints/steam.js +59 -0
- package/dist/host/launch/index.d.ts +2 -2
- package/dist/host/launch/index.js +2 -2
- package/dist/host/launch/mappings.js +2 -0
- package/dist/host/launch/session-file.js +2 -0
- package/dist/host/launch/stage.js +10 -1
- package/dist/host/launch/toolchains.d.ts +7 -0
- package/dist/host/launch/toolchains.js +11 -0
- package/dist/host/pc-installs/discover.d.ts +19 -0
- package/dist/host/pc-installs/discover.js +59 -0
- package/dist/host/pc-installs/epic.d.ts +2 -0
- package/dist/host/pc-installs/epic.js +7 -0
- package/dist/host/pc-installs/index.d.ts +3 -0
- package/dist/host/pc-installs/index.js +2 -0
- package/dist/host/pc-installs/source.d.ts +9 -0
- package/dist/host/pc-installs/source.js +1 -0
- package/dist/host/pc-installs/steam.d.ts +16 -0
- package/dist/host/pc-installs/steam.js +143 -0
- package/dist/host/pc-installs/unity.d.ts +2 -0
- package/dist/host/pc-installs/unity.js +7 -0
- package/dist/host/pc-installs/unreal.d.ts +2 -0
- package/dist/host/pc-installs/unreal.js +7 -0
- package/dist/host/profile/accounts.d.ts +21 -6
- package/dist/host/profile/accounts.js +108 -28
- package/dist/host/profile/claude-login.d.ts +32 -2
- package/dist/host/profile/claude-login.js +100 -8
- package/dist/host/profile/index.d.ts +1 -1
- package/dist/host/profile/index.js +1 -1
- package/dist/host/secrets/launch-secrets.js +1 -0
- package/dist/runtime/agent.js +321 -9
- package/dist/runtime/daemon.js +1987 -595
- package/dist/runtime/db.js +334 -11
- package/dist/runtime/main.js +2530 -709
- package/dist/runtime/netproxy.js +321 -9
- package/dist/runtime/tool.js +28549 -0
- package/dist/runtime/view.js +321 -9
- package/dist/selftest/accounts.test.js +172 -1
- package/dist/selftest/agent-paths.test.js +13 -1
- package/dist/selftest/android.test.d.ts +2 -0
- package/dist/selftest/android.test.js +7 -0
- package/dist/selftest/app.test.js +1 -1
- package/dist/selftest/engines.test.d.ts +2 -0
- package/dist/selftest/engines.test.js +7 -0
- package/dist/selftest/fivem.test.d.ts +2 -0
- package/dist/selftest/fivem.test.js +7 -0
- package/dist/selftest/generate.test.js +1 -1
- package/dist/selftest/index.js +18 -0
- package/dist/selftest/menu-screens.js +1 -0
- package/dist/selftest/menu.test.js +2 -1
- package/dist/selftest/mobile.test.d.ts +2 -0
- package/dist/selftest/mobile.test.js +7 -0
- package/dist/selftest/pc-installs.test.d.ts +2 -0
- package/dist/selftest/pc-installs.test.js +111 -0
- package/dist/selftest/persisted-installs.test.d.ts +2 -0
- package/dist/selftest/persisted-installs.test.js +107 -0
- package/dist/selftest/projects.test.js +6 -4
- package/dist/selftest/service-accounts.test.d.ts +2 -0
- package/dist/selftest/service-accounts.test.js +320 -0
- package/dist/selftest/steam.test.d.ts +2 -0
- package/dist/selftest/steam.test.js +316 -0
- package/dist/selftest/storage-helper.test.js +31 -8
- package/dist/selftest/support/engine-golden.js +3 -0
- package/dist/selftest/support/fixtures.d.ts +87 -0
- package/dist/selftest/support/fixtures.js +4 -3
- package/dist/selftest/support/pc-memory.d.ts +2 -0
- package/dist/selftest/support/pc-memory.js +17 -0
- package/dist/selftest/support/web-preview.js +20 -1
- package/dist/selftest/unity.test.d.ts +2 -0
- package/dist/selftest/unity.test.js +7 -0
- package/dist/shared/accounts/fingerprint.d.ts +1 -0
- package/dist/shared/accounts/fingerprint.js +20 -0
- package/dist/shared/accounts/index.d.ts +5 -0
- package/dist/shared/accounts/index.js +7 -0
- package/dist/shared/accounts/kinds/epic.d.ts +10 -0
- package/dist/shared/accounts/kinds/epic.js +56 -0
- package/dist/shared/accounts/kinds/steam.d.ts +28 -0
- package/dist/shared/accounts/kinds/steam.js +99 -0
- package/dist/shared/accounts/kinds.d.ts +41 -0
- package/dist/shared/accounts/kinds.js +154 -0
- package/dist/shared/accounts/payloads.d.ts +151 -0
- package/dist/shared/accounts/payloads.js +128 -0
- package/dist/shared/apps/catalogue-gamedev.d.ts +2 -0
- package/dist/shared/apps/catalogue-gamedev.js +1 -0
- package/dist/shared/apps/catalogue-mobile.d.ts +2 -0
- package/dist/shared/apps/catalogue-mobile.js +1 -0
- package/dist/shared/apps/catalogue-modding.d.ts +2 -0
- package/dist/shared/apps/catalogue-modding.js +1 -0
- package/dist/shared/apps/catalogue.d.ts +1 -0
- package/dist/shared/apps/catalogue.js +6 -1
- package/dist/shared/channel/schema.d.ts +183 -0
- package/dist/shared/channel/secrets.d.ts +42 -0
- package/dist/shared/channel/secrets.js +3 -0
- package/dist/shared/download/downloader.d.ts +7 -0
- package/dist/shared/download/downloader.js +10 -3
- package/dist/shared/installs.d.ts +19 -0
- package/dist/shared/installs.js +13 -0
- package/dist/shared/io/bounded.d.ts +6 -0
- package/dist/shared/io/bounded.js +38 -0
- package/dist/shared/io/index.d.ts +2 -0
- package/dist/shared/io/index.js +2 -0
- package/dist/shared/io/text.d.ts +9 -0
- package/dist/shared/io/text.js +18 -0
- package/dist/shared/paths/guest-paths.js +1 -1
- package/dist/shared/paths/index.d.ts +1 -0
- package/dist/shared/paths/index.js +1 -0
- package/dist/shared/paths/pc-installs.d.ts +20 -0
- package/dist/shared/paths/pc-installs.js +19 -0
- package/dist/shared/vdf.d.ts +25 -0
- package/dist/shared/vdf.js +151 -0
- package/dist/sync/sync-cycle.js +9 -0
- package/dist/web/index.html +1 -1
- package/dist/web/{main-JTH3FT4O.js → main-CWIF4VQI.js} +63 -61
- 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/scripts/add-firewall-port.ps1 +13 -0
- package/sandbox/templates/fivem/README.txt +2 -0
- package/schemas/config.schema.json +485 -0
- package/schemas/sandbox-config.schema.json +416 -0
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// accounts.run.ts does the work over AccountsService.
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { action, defineOperation, defineQuery, defineView, plural, } from '../../kernel/index.js';
|
|
6
|
+
import { setSetting } from '../settings/settings.def.js';
|
|
7
|
+
import { listServices, SERVICE_OPERATIONS, serviceSections, ServiceRowSchema, } from './services.def.js';
|
|
6
8
|
const AgentSchema = z.string().default('claude').describe('whose login: an agent that keeps one (default: claude)');
|
|
7
9
|
const NameSchema = z.string().optional().describe('account name');
|
|
8
10
|
export const AccountRowSchema = z.object({
|
|
@@ -11,7 +13,19 @@ export const AccountRowSchema = z.object({
|
|
|
11
13
|
isDefault: z.boolean().describe('New launches start with it'),
|
|
12
14
|
isActive: z.boolean().describe('The sandbox uses it now'),
|
|
13
15
|
savedAt: z.string().describe('When it was saved (ISO time)'),
|
|
16
|
+
kind: z
|
|
17
|
+
.enum(['subscription', 'console', 'api-key'])
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Claude: a subscription login, a Claude Console login (its API key), or an API key you gave'),
|
|
20
|
+
keyHint: z.string().nullable().optional().describe('The most of an API key ever shown: sk-ant-...abcd'),
|
|
14
21
|
});
|
|
22
|
+
const KIND_LABELS = { subscription: 'subscription', console: 'Console', 'api-key': 'API key' };
|
|
23
|
+
/** "API key sk-ant-...abcd", "Console sk-ant-...abcd", "subscription"; null when the agent does not say. */
|
|
24
|
+
export function kindText(r) {
|
|
25
|
+
if (!r.kind)
|
|
26
|
+
return null;
|
|
27
|
+
return KIND_LABELS[r.kind] + (r.keyHint ? ' ' + r.keyHint : '');
|
|
28
|
+
}
|
|
15
29
|
export const AgentAccountsSchema = z.object({
|
|
16
30
|
name: z.string().describe('The agent'),
|
|
17
31
|
kept: z.boolean().describe('Its login can be saved and switched'),
|
|
@@ -21,25 +35,42 @@ export const AgentAccountsSchema = z.object({
|
|
|
21
35
|
export const AccountsListSchema = z.object({
|
|
22
36
|
agent: z.string().describe('The agent asked for (the command line table shows only it)'),
|
|
23
37
|
agents: z.array(AgentAccountsSchema).describe('Every agent, Claude first'),
|
|
38
|
+
services: z.array(ServiceRowSchema).describe('Service sign-ins for sandboxes (Epic, Steam ...)'),
|
|
24
39
|
});
|
|
25
40
|
const MessageSchema = z.object({ message: z.string().describe('What happened') });
|
|
26
41
|
/** The name rule of a saved login: what the store accepts. */
|
|
27
42
|
export const NAME_RULE = 'Use letters, digits, - and _ (up to 40).';
|
|
43
|
+
/** The "Services" block under `sandbox account list`: one line per service that is shared or saved. */
|
|
44
|
+
function servicesBlock(o) {
|
|
45
|
+
const kept = o.services.filter((s) => s.share || s.state !== 'none');
|
|
46
|
+
const lines = kept.map((s) => ' ' +
|
|
47
|
+
s.kind.padEnd(10) +
|
|
48
|
+
(s.share ? 'shared' : 'not shared') +
|
|
49
|
+
', ' +
|
|
50
|
+
(s.state === 'none' ? 'not saved' : s.state === 'saved' ? 'saved' : 'cannot be read'));
|
|
51
|
+
return ['', 'Services' + (kept.length ? '' : ': none shared or saved') + ' (sandbox account status)', ...lines];
|
|
52
|
+
}
|
|
28
53
|
/** `sandbox account list`: the table of one agent, as the command line has always padded it. */
|
|
29
54
|
export function accountsText(o) {
|
|
55
|
+
return [agentText(o), ...servicesBlock(o)].join('\n');
|
|
56
|
+
}
|
|
57
|
+
function agentText(o) {
|
|
30
58
|
const rows = o.agents.find((a) => a.name === o.agent)?.accounts ?? [];
|
|
31
59
|
if (!rows.length)
|
|
32
60
|
return o.agent === 'claude'
|
|
33
|
-
? "No saved accounts. Sign in inside the sandbox, then: sandbox account save <name> (or --from-host to save this PC's login)"
|
|
61
|
+
? "No saved accounts. Sign in inside the sandbox, then: sandbox account save <name> (or --from-host to save this PC's login, or --api-key to type an API key)"
|
|
34
62
|
: 'No saved ' +
|
|
35
63
|
o.agent +
|
|
36
64
|
' accounts. Sign in inside the sandbox, then: sandbox account save <name> --agent ' +
|
|
37
65
|
o.agent;
|
|
66
|
+
// The KIND column only for an agent whose logins have kinds (Claude).
|
|
67
|
+
const kinds = rows.some((a) => a.kind);
|
|
38
68
|
const table = [
|
|
39
|
-
['', 'NAME', 'EMAIL', 'SAVED'],
|
|
69
|
+
['', 'NAME', ...(kinds ? ['KIND'] : []), 'EMAIL', 'SAVED'],
|
|
40
70
|
...rows.map((a) => [
|
|
41
71
|
(a.isActive ? '*' : ' ') + (a.isDefault ? 'D' : ' '),
|
|
42
72
|
a.name,
|
|
73
|
+
...(kinds ? [kindText(a) ?? '-'] : []),
|
|
43
74
|
a.email ?? '-',
|
|
44
75
|
a.savedAt.slice(0, 16).replace('T', ' '),
|
|
45
76
|
]),
|
|
@@ -72,6 +103,10 @@ export const saveAccount = defineOperation({
|
|
|
72
103
|
agent: AgentSchema,
|
|
73
104
|
name: NameSchema,
|
|
74
105
|
fromHost: z.boolean().default(false).describe("(save) save this PC's login instead of the sandbox's"),
|
|
106
|
+
apiKey: z
|
|
107
|
+
.boolean()
|
|
108
|
+
.default(false)
|
|
109
|
+
.describe("(save, Claude) an Anthropic API key: typed in (hidden), or with --from-host this PC's"),
|
|
75
110
|
}),
|
|
76
111
|
output: z.object({ name: z.string().describe('The saved login'), message: z.string().describe('What happened') }),
|
|
77
112
|
text: (o) => o.message,
|
|
@@ -79,7 +114,7 @@ export const saveAccount = defineOperation({
|
|
|
79
114
|
name: 'account',
|
|
80
115
|
action: 'save',
|
|
81
116
|
positional: ['name'],
|
|
82
|
-
flags: { agent: { flag: '--agent' }, fromHost: { flag: '--from-host' } },
|
|
117
|
+
flags: { agent: { flag: '--agent' }, fromHost: { flag: '--from-host' }, apiKey: { flag: '--api-key' } },
|
|
83
118
|
},
|
|
84
119
|
});
|
|
85
120
|
export const useAccount = defineOperation({
|
|
@@ -173,11 +208,25 @@ function saveItems(a) {
|
|
|
173
208
|
},
|
|
174
209
|
]
|
|
175
210
|
: []),
|
|
211
|
+
...(a.name === 'claude'
|
|
212
|
+
? [
|
|
213
|
+
{
|
|
214
|
+
id: '@save-key',
|
|
215
|
+
label: '+ Save an API key…',
|
|
216
|
+
group: 'Save a login',
|
|
217
|
+
description: 'Saves an Anthropic API key (from the Claude Console) under a name you choose. What you type is not shown.',
|
|
218
|
+
primary: action(saveAccount, { agent: a.name, fromHost: false, apiKey: true }, { verb: 'save', label: 'Save an API key' }),
|
|
219
|
+
},
|
|
220
|
+
]
|
|
221
|
+
: []),
|
|
176
222
|
];
|
|
177
223
|
}
|
|
178
224
|
/** One saved login: its state in the hint and badges, Enter or a click uses it, d/r/x change it. */
|
|
179
225
|
function loginItem(a, r) {
|
|
180
|
-
const
|
|
226
|
+
const kind = kindText(r);
|
|
227
|
+
const hint = [r.kind ? KIND_LABELS[r.kind] : '', r.isActive ? 'in the sandbox' : '', r.isDefault ? 'default' : '']
|
|
228
|
+
.filter(Boolean)
|
|
229
|
+
.join(' · ');
|
|
181
230
|
return {
|
|
182
231
|
id: a.name + ':' + r.name,
|
|
183
232
|
label: r.name,
|
|
@@ -185,11 +234,17 @@ function loginItem(a, r) {
|
|
|
185
234
|
icon: a.name,
|
|
186
235
|
hint,
|
|
187
236
|
badges: [
|
|
237
|
+
...(r.kind ? [{ text: KIND_LABELS[r.kind], tone: r.kind === 'subscription' ? 'muted' : 'warn' }] : []),
|
|
188
238
|
...(r.isActive ? [{ text: 'in the sandbox', tone: 'ok' }] : []),
|
|
189
239
|
...(r.isDefault ? [{ text: 'default', tone: 'accent' }] : []),
|
|
190
240
|
],
|
|
191
241
|
description: r.isActive ? 'The sandbox uses this login now.' : 'Choose it to make the sandbox use this login.',
|
|
192
|
-
details: [
|
|
242
|
+
details: [
|
|
243
|
+
'Agent ' + a.name,
|
|
244
|
+
...(kind ? ['Kind ' + kind] : []),
|
|
245
|
+
'Email ' + (r.email ?? '-'),
|
|
246
|
+
'Saved ' + r.savedAt.slice(0, 16).replace('T', ' '),
|
|
247
|
+
],
|
|
193
248
|
// The login in use already offers nothing to switch to.
|
|
194
249
|
...(r.isActive ? {} : { primary: action(useAccount, { agent: a.name, name: r.name }, { verb: 'use' }) }),
|
|
195
250
|
actions: [
|
|
@@ -223,9 +278,9 @@ export function agentSection(a) {
|
|
|
223
278
|
items: [...saveItems(a), ...a.accounts.map((r) => loginItem(a, r))],
|
|
224
279
|
};
|
|
225
280
|
}
|
|
226
|
-
/** The web's page: one card per agent (its logins and the save rows). */
|
|
227
|
-
export function accountsPage(o) {
|
|
228
|
-
return { title: 'Accounts', sections: o.agents.map(agentSection) };
|
|
281
|
+
/** The web's page: one card per agent (its logins and the save rows), then one section per service sign-in. */
|
|
282
|
+
export function accountsPage(o, services = { services: o.services }) {
|
|
283
|
+
return { title: 'Accounts', sections: [...o.agents.map(agentSection), ...serviceSections(services)] };
|
|
229
284
|
}
|
|
230
285
|
/**
|
|
231
286
|
* One agent's page for the menu: its section, and the keys of its save rows (s, p) which the menu's key bar shows
|
|
@@ -238,6 +293,11 @@ export function agentPage(a) {
|
|
|
238
293
|
...(a.canSavePc
|
|
239
294
|
? [action(saveAccount, { agent: a.name, fromHost: true }, { key: 'p', verb: 'save PC login', importance: 3 })]
|
|
240
295
|
: []),
|
|
296
|
+
...(a.name === 'claude'
|
|
297
|
+
? [
|
|
298
|
+
action(saveAccount, { agent: a.name, fromHost: false, apiKey: true }, { key: 'k', verb: 'save API key', importance: 3 }),
|
|
299
|
+
]
|
|
300
|
+
: []),
|
|
241
301
|
]
|
|
242
302
|
: [];
|
|
243
303
|
return { title: 'Accounts', sections: [agentSection(a)], actions: keys };
|
|
@@ -245,8 +305,16 @@ export function agentPage(a) {
|
|
|
245
305
|
export const accountsView = defineView({
|
|
246
306
|
id: 'accounts',
|
|
247
307
|
title: 'Accounts',
|
|
248
|
-
summary: 'Logins saved on this PC for each agent
|
|
249
|
-
queries: { accounts: listAccounts },
|
|
250
|
-
operations: [
|
|
251
|
-
|
|
308
|
+
summary: 'Logins saved on this PC for each agent (every launch starts with the default; "Use this login" switches now), and the service sign-ins sandboxes may get (Epic, Steam, Unity, signing keys ...).',
|
|
309
|
+
queries: { accounts: listAccounts, services: listServices },
|
|
310
|
+
operations: [
|
|
311
|
+
saveAccount,
|
|
312
|
+
useAccount,
|
|
313
|
+
defaultAccount,
|
|
314
|
+
renameAccount,
|
|
315
|
+
removeAccount,
|
|
316
|
+
...SERVICE_OPERATIONS,
|
|
317
|
+
setSetting,
|
|
318
|
+
],
|
|
319
|
+
build: ({ accounts, services }) => accountsPage(accounts, services),
|
|
252
320
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type OperationIO } from '../../kernel/index.js';
|
|
1
2
|
export declare const listAccountsRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").QueryDef<import("zod").ZodObject<{
|
|
2
3
|
agent: import("zod").ZodDefault<import("zod").ZodString>;
|
|
3
4
|
}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
|
|
@@ -12,13 +13,41 @@ export declare const listAccountsRun: import("../../kernel/define.js").Implement
|
|
|
12
13
|
isDefault: import("zod").ZodBoolean;
|
|
13
14
|
isActive: import("zod").ZodBoolean;
|
|
14
15
|
savedAt: import("zod").ZodString;
|
|
16
|
+
kind: import("zod").ZodOptional<import("zod").ZodEnum<{
|
|
17
|
+
subscription: "subscription";
|
|
18
|
+
console: "console";
|
|
19
|
+
"api-key": "api-key";
|
|
20
|
+
}>>;
|
|
21
|
+
keyHint: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
|
|
15
22
|
}, import("zod/v4/core").$strip>>;
|
|
16
23
|
}, import("zod/v4/core").$strip>>;
|
|
24
|
+
services: import("zod").ZodArray<import("zod").ZodObject<{
|
|
25
|
+
kind: import("zod").ZodString;
|
|
26
|
+
label: import("zod").ZodString;
|
|
27
|
+
share: import("zod").ZodBoolean;
|
|
28
|
+
state: import("zod").ZodEnum<{
|
|
29
|
+
none: "none";
|
|
30
|
+
saved: "saved";
|
|
31
|
+
unreadable: "unreadable";
|
|
32
|
+
}>;
|
|
33
|
+
source: import("zod").ZodNullable<import("zod").ZodString>;
|
|
34
|
+
savedAt: import("zod").ZodNullable<import("zod").ZodString>;
|
|
35
|
+
summary: import("zod").ZodNullable<import("zod").ZodString>;
|
|
36
|
+
heldBy: import("zod").ZodNullable<import("zod").ZodString>;
|
|
37
|
+
usedBy: import("zod").ZodArray<import("zod").ZodString>;
|
|
38
|
+
warnings: import("zod").ZodArray<import("zod").ZodString>;
|
|
39
|
+
canImport: import("zod").ZodBoolean;
|
|
40
|
+
canLogin: import("zod").ZodBoolean;
|
|
41
|
+
needsFile: import("zod").ZodBoolean;
|
|
42
|
+
}, import("zod/v4/core").$strip>>;
|
|
17
43
|
}, import("zod/v4/core").$strip>, "accounts.list">, import("../../../cli/commands/context.js").Context>;
|
|
44
|
+
/** Asks for an API key (one hidden question). The validator answers with fixed texts only, never the key. */
|
|
45
|
+
export declare function askApiKey(io: OperationIO): Promise<string>;
|
|
18
46
|
export declare const saveAccountRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{
|
|
19
47
|
agent: import("zod").ZodDefault<import("zod").ZodString>;
|
|
20
48
|
name: import("zod").ZodOptional<import("zod").ZodString>;
|
|
21
49
|
fromHost: import("zod").ZodDefault<import("zod").ZodBoolean>;
|
|
50
|
+
apiKey: import("zod").ZodDefault<import("zod").ZodBoolean>;
|
|
22
51
|
}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
|
|
23
52
|
name: import("zod").ZodString;
|
|
24
53
|
message: import("zod").ZodString;
|
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
// safeText. Logins are never read here: the store keeps them sealed, and outputs hold names, emails and dates only.
|
|
4
4
|
import { fail, safeText } from '../../../core/util/index.js';
|
|
5
5
|
import { AccountsService } from '../../../cli/services/index.js';
|
|
6
|
+
import { AccountLeases, ServiceAccountStore } from '../../../host/accounts/index.js';
|
|
7
|
+
import { SERVICE_KINDS } from '../../../shared/accounts/index.js';
|
|
8
|
+
import { apiKeyProblem } from '../../../host/profile/index.js';
|
|
6
9
|
import { implement } from '../../context.js';
|
|
10
|
+
import { serviceRow } from './services.run.js';
|
|
7
11
|
import { cancelled } from '../../kernel/index.js';
|
|
8
|
-
import { defaultAccount, listAccounts, NAME_RULE, removeAccount, renameAccount, saveAccount, useAccount, } from './accounts.def.js';
|
|
12
|
+
import { defaultAccount, kindText, listAccounts, NAME_RULE, removeAccount, renameAccount, saveAccount, useAccount, } from './accounts.def.js';
|
|
9
13
|
const NAME = /^[A-Za-z0-9_-]{1,40}$/;
|
|
10
14
|
const validName = (v) => NAME.test(v.trim()) || NAME_RULE;
|
|
11
15
|
/** "work" (me@example.com) as the notices and the command line have always said it. */
|
|
@@ -21,6 +25,7 @@ function savedLogins(service, agent) {
|
|
|
21
25
|
isDefault: r.isDefault,
|
|
22
26
|
isActive: r.isActive,
|
|
23
27
|
savedAt: r.savedAt,
|
|
28
|
+
...(r.kind ? { kind: r.kind, keyHint: r.keyHint ?? null } : {}),
|
|
24
29
|
}));
|
|
25
30
|
}
|
|
26
31
|
catch {
|
|
@@ -46,7 +51,10 @@ async function accountName(io, service, verb, agent, given) {
|
|
|
46
51
|
fail('No saved accounts.', 'Sign in inside the sandbox, then run: sandbox account save <name>');
|
|
47
52
|
const picked = await io.choose({
|
|
48
53
|
title: 'Which account?',
|
|
49
|
-
choices: rows.map((a) =>
|
|
54
|
+
choices: rows.map((a) => {
|
|
55
|
+
const kind = kindText(a);
|
|
56
|
+
return { value: a.name, label: label(a), ...(kind ? { description: kind } : {}) };
|
|
57
|
+
}),
|
|
50
58
|
});
|
|
51
59
|
if (picked === undefined)
|
|
52
60
|
cancelled();
|
|
@@ -63,13 +71,34 @@ export const listAccountsRun = implement(listAccounts, async (ctx, input) => {
|
|
|
63
71
|
// An agent that keeps no login, or no such agent: the command line says why (the store's own message).
|
|
64
72
|
if (!agents.some((a) => a.name === input.agent && a.kept))
|
|
65
73
|
service.list(input.agent);
|
|
66
|
-
|
|
74
|
+
const store = new ServiceAccountStore(ctx.storageRoot);
|
|
75
|
+
const leases = new AccountLeases(ctx.storageRoot);
|
|
76
|
+
const services = SERVICE_KINDS.map((k) => serviceRow(k, ctx.loaded.config, store, leases));
|
|
77
|
+
return { agent: input.agent, agents, services };
|
|
67
78
|
});
|
|
79
|
+
/** Asks for an API key (one hidden question). The validator answers with fixed texts only, never the key. */
|
|
80
|
+
export async function askApiKey(io) {
|
|
81
|
+
io.log('Make an API key in the Claude Console (platform.claude.com, API keys), then paste it here.');
|
|
82
|
+
io.log('What you type is not shown. It is kept encrypted for your Windows user and never printed.', 'muted');
|
|
83
|
+
const key = await io.secret({
|
|
84
|
+
title: 'Anthropic API key',
|
|
85
|
+
validate: (v) => apiKeyProblem(v.trim()) ?? true,
|
|
86
|
+
});
|
|
87
|
+
if (key === undefined)
|
|
88
|
+
cancelled();
|
|
89
|
+
return key.trim();
|
|
90
|
+
}
|
|
68
91
|
export const saveAccountRun = implement(saveAccount, async (ctx, input, io) => {
|
|
69
92
|
const service = new AccountsService(ctx);
|
|
93
|
+
if (input.apiKey && input.agent !== 'claude')
|
|
94
|
+
fail('--api-key is for Claude accounts only.', 'Leave out --agent, or leave out --api-key.');
|
|
70
95
|
const name = await accountName(io, service, 'save', input.agent, input.name);
|
|
71
|
-
|
|
72
|
-
|
|
96
|
+
// --api-key alone: a key typed in; with --from-host: this PC's Console key or ANTHROPIC_API_KEY.
|
|
97
|
+
const saved = input.apiKey && !input.fromHost
|
|
98
|
+
? service.saveApiKey(name, await askApiKey(io))
|
|
99
|
+
: service.save(name, input.fromHost, input.agent, input.apiKey);
|
|
100
|
+
const kind = saved.kind && saved.kind !== 'subscription' ? kindText(saved) : null;
|
|
101
|
+
return { name: saved.name, message: 'Saved account ' + label(saved) + (kind ? ' (' + kind + ')' : '') + '.' };
|
|
73
102
|
});
|
|
74
103
|
/**
|
|
75
104
|
* Makes the sandbox use a saved login, then offers to restart the agent's running sessions. The switch is done before
|
|
@@ -77,7 +106,8 @@ export const saveAccountRun = implement(saveAccount, async (ctx, input, io) => {
|
|
|
77
106
|
*/
|
|
78
107
|
async function switchTo(io, service, name, agent, noRestart) {
|
|
79
108
|
const used = service.activate(name, agent);
|
|
80
|
-
const
|
|
109
|
+
const kind = used.kind && used.kind !== 'subscription' ? kindText(used) : null;
|
|
110
|
+
const lines = ['Sandbox now uses ' + label(used) + (kind ? ' (' + kind + ')' : '') + '.'];
|
|
81
111
|
if (used.note)
|
|
82
112
|
lines.push(safeText(used.note));
|
|
83
113
|
if (service.hasRunningSessions(agent) && !noRestart) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
// The accounts feature: the saved logins of each agent,
|
|
1
|
+
// The accounts feature: the saved logins of each agent (save, use, default, rename, remove), and the service sign-ins
|
|
2
|
+
// sandboxes may get (Epic, Steam, Unity, signing keys ...: status, import, sign in, forget).
|
|
2
3
|
export * from './accounts.def.js';
|
|
3
4
|
export * from './accounts.run.js';
|
|
5
|
+
export * from './services.def.js';
|
|
6
|
+
export * from './services.run.js';
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type Page, type Section } from '../../kernel/index.js';
|
|
3
|
+
/** The kinds, as the help says them (the self-test checks this against shared/accounts SERVICE_KINDS). */
|
|
4
|
+
export declare const SERVICE_NAMES_TEXT = "epic, steam, unity, cfx, keystore, play, expo or appstore";
|
|
5
|
+
export declare const ServiceRowSchema: z.ZodObject<{
|
|
6
|
+
kind: z.ZodString;
|
|
7
|
+
label: z.ZodString;
|
|
8
|
+
share: z.ZodBoolean;
|
|
9
|
+
state: z.ZodEnum<{
|
|
10
|
+
none: "none";
|
|
11
|
+
saved: "saved";
|
|
12
|
+
unreadable: "unreadable";
|
|
13
|
+
}>;
|
|
14
|
+
source: z.ZodNullable<z.ZodString>;
|
|
15
|
+
savedAt: z.ZodNullable<z.ZodString>;
|
|
16
|
+
summary: z.ZodNullable<z.ZodString>;
|
|
17
|
+
heldBy: z.ZodNullable<z.ZodString>;
|
|
18
|
+
usedBy: z.ZodArray<z.ZodString>;
|
|
19
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
20
|
+
canImport: z.ZodBoolean;
|
|
21
|
+
canLogin: z.ZodBoolean;
|
|
22
|
+
needsFile: z.ZodBoolean;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
export type ServiceRow = z.output<typeof ServiceRowSchema>;
|
|
25
|
+
export declare const ServicesListSchema: z.ZodObject<{
|
|
26
|
+
services: z.ZodArray<z.ZodObject<{
|
|
27
|
+
kind: z.ZodString;
|
|
28
|
+
label: z.ZodString;
|
|
29
|
+
share: z.ZodBoolean;
|
|
30
|
+
state: z.ZodEnum<{
|
|
31
|
+
none: "none";
|
|
32
|
+
saved: "saved";
|
|
33
|
+
unreadable: "unreadable";
|
|
34
|
+
}>;
|
|
35
|
+
source: z.ZodNullable<z.ZodString>;
|
|
36
|
+
savedAt: z.ZodNullable<z.ZodString>;
|
|
37
|
+
summary: z.ZodNullable<z.ZodString>;
|
|
38
|
+
heldBy: z.ZodNullable<z.ZodString>;
|
|
39
|
+
usedBy: z.ZodArray<z.ZodString>;
|
|
40
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
41
|
+
canImport: z.ZodBoolean;
|
|
42
|
+
canLogin: z.ZodBoolean;
|
|
43
|
+
needsFile: z.ZodBoolean;
|
|
44
|
+
}, z.core.$strip>>;
|
|
45
|
+
}, z.core.$strip>;
|
|
46
|
+
export type ServicesList = z.output<typeof ServicesListSchema>;
|
|
47
|
+
/** `sandbox account status`: one line per service. */
|
|
48
|
+
export declare function servicesText(o: ServicesList): string;
|
|
49
|
+
export declare const listServices: import("../../kernel/define.js").QueryDef<z.ZodObject<{
|
|
50
|
+
name: z.ZodOptional<z.ZodString>;
|
|
51
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
52
|
+
services: z.ZodArray<z.ZodObject<{
|
|
53
|
+
kind: z.ZodString;
|
|
54
|
+
label: z.ZodString;
|
|
55
|
+
share: z.ZodBoolean;
|
|
56
|
+
state: z.ZodEnum<{
|
|
57
|
+
none: "none";
|
|
58
|
+
saved: "saved";
|
|
59
|
+
unreadable: "unreadable";
|
|
60
|
+
}>;
|
|
61
|
+
source: z.ZodNullable<z.ZodString>;
|
|
62
|
+
savedAt: z.ZodNullable<z.ZodString>;
|
|
63
|
+
summary: z.ZodNullable<z.ZodString>;
|
|
64
|
+
heldBy: z.ZodNullable<z.ZodString>;
|
|
65
|
+
usedBy: z.ZodArray<z.ZodString>;
|
|
66
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
67
|
+
canImport: z.ZodBoolean;
|
|
68
|
+
canLogin: z.ZodBoolean;
|
|
69
|
+
needsFile: z.ZodBoolean;
|
|
70
|
+
}, z.core.$strip>>;
|
|
71
|
+
}, z.core.$strip>, "accounts.services">;
|
|
72
|
+
export declare const serviceImport: import("../../kernel/define.js").OperationDef<z.ZodObject<{
|
|
73
|
+
name: z.ZodOptional<z.ZodString>;
|
|
74
|
+
from: z.ZodOptional<z.ZodString>;
|
|
75
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
76
|
+
message: z.ZodString;
|
|
77
|
+
}, z.core.$strip>, "accounts.service.import">;
|
|
78
|
+
export declare const serviceLogin: import("../../kernel/define.js").OperationDef<z.ZodObject<{
|
|
79
|
+
name: z.ZodOptional<z.ZodString>;
|
|
80
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
81
|
+
message: z.ZodString;
|
|
82
|
+
}, z.core.$strip>, "accounts.service.login">;
|
|
83
|
+
export declare const serviceLogout: import("../../kernel/define.js").OperationDef<z.ZodObject<{
|
|
84
|
+
name: z.ZodOptional<z.ZodString>;
|
|
85
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
86
|
+
deleted: z.ZodBoolean;
|
|
87
|
+
message: z.ZodString;
|
|
88
|
+
}, z.core.$strip>, "accounts.service.logout">;
|
|
89
|
+
export declare const SERVICE_OPERATIONS: readonly [import("../../kernel/define.js").OperationDef<z.ZodObject<{
|
|
90
|
+
name: z.ZodOptional<z.ZodString>;
|
|
91
|
+
from: z.ZodOptional<z.ZodString>;
|
|
92
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
93
|
+
message: z.ZodString;
|
|
94
|
+
}, z.core.$strip>, "accounts.service.import">, import("../../kernel/define.js").OperationDef<z.ZodObject<{
|
|
95
|
+
name: z.ZodOptional<z.ZodString>;
|
|
96
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
97
|
+
message: z.ZodString;
|
|
98
|
+
}, z.core.$strip>, "accounts.service.login">, import("../../kernel/define.js").OperationDef<z.ZodObject<{
|
|
99
|
+
name: z.ZodOptional<z.ZodString>;
|
|
100
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
101
|
+
deleted: z.ZodBoolean;
|
|
102
|
+
message: z.ZodString;
|
|
103
|
+
}, z.core.$strip>, "accounts.service.logout">];
|
|
104
|
+
/** The category id of a service in the menu and its section id on the web. */
|
|
105
|
+
export declare function serviceCategory(kind: string): string;
|
|
106
|
+
/** One service's section: its facts, its warnings and its actions. */
|
|
107
|
+
export declare function serviceSection(s: ServiceRow): Section;
|
|
108
|
+
/** The sections of every service (appended to the accounts page on the web). */
|
|
109
|
+
export declare function serviceSections(o: ServicesList): Section[];
|
|
110
|
+
/** One service's page for the menu: its section, and its actions as keys. */
|
|
111
|
+
export declare function servicePage(s: ServiceRow): Page;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Service sign-ins for sandboxes (accounts.<kind>.share): Epic, Steam, Unity, Cfx.re, signing keys and app-store
|
|
2
|
+
// keys. Whether each one is shared and saved (where it came from and when, never its value), taking one from this PC
|
|
3
|
+
// or a file, typing one, and forgetting it. Pure: schemas, labels and page builders; services.run.ts does the work.
|
|
4
|
+
// The kinds live in shared/accounts (a def imports no shared code): the query's rows carry everything a page shows.
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { action } from '../../kernel/index.js';
|
|
7
|
+
import { defineOperation, defineQuery } from '../../kernel/index.js';
|
|
8
|
+
import { setSetting } from '../settings/settings.def.js';
|
|
9
|
+
/** The kinds, as the help says them (the self-test checks this against shared/accounts SERVICE_KINDS). */
|
|
10
|
+
export const SERVICE_NAMES_TEXT = 'epic, steam, unity, cfx, keystore, play, expo or appstore';
|
|
11
|
+
const KindSchema = z
|
|
12
|
+
.string()
|
|
13
|
+
.max(40)
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('the service: ' + SERVICE_NAMES_TEXT);
|
|
16
|
+
export const ServiceRowSchema = z.object({
|
|
17
|
+
kind: z.string().describe('The service (epic, steam ...)'),
|
|
18
|
+
label: z.string().describe('Its name'),
|
|
19
|
+
share: z.boolean().describe('Sandboxes get it (setting accounts.<kind>.share)'),
|
|
20
|
+
state: z.enum(['none', 'saved', 'unreadable']).describe('Whether one is saved and can be opened by you'),
|
|
21
|
+
source: z.string().nullable().describe('Where the saved one came from: pc, file, typed or sandbox'),
|
|
22
|
+
savedAt: z.string().nullable().describe('When it was saved'),
|
|
23
|
+
summary: z.string().nullable().describe('Which account (never the secret)'),
|
|
24
|
+
heldBy: z.string().nullable().describe('The data set whose sandbox holds it now (one-holder services)'),
|
|
25
|
+
usedBy: z.array(z.string()).describe('The toolchains it is handed over for'),
|
|
26
|
+
warnings: z.array(z.string()).describe('What everyone who shares it must know'),
|
|
27
|
+
canImport: z.boolean().describe('It can be taken from this PC or a file (sandbox account import)'),
|
|
28
|
+
canLogin: z.boolean().describe('It can be typed (sandbox account login)'),
|
|
29
|
+
needsFile: z.boolean().describe('Importing it needs --from <file>'),
|
|
30
|
+
});
|
|
31
|
+
export const ServicesListSchema = z.object({ services: z.array(ServiceRowSchema) });
|
|
32
|
+
const STATE_TEXT = {
|
|
33
|
+
none: 'not saved',
|
|
34
|
+
saved: 'saved (encrypted)',
|
|
35
|
+
unreadable: 'cannot be read',
|
|
36
|
+
};
|
|
37
|
+
const STATE_TONE = {
|
|
38
|
+
none: 'muted',
|
|
39
|
+
saved: 'ok',
|
|
40
|
+
unreadable: 'danger',
|
|
41
|
+
};
|
|
42
|
+
const SOURCE_TEXT = {
|
|
43
|
+
pc: 'taken from this PC',
|
|
44
|
+
file: 'from a file',
|
|
45
|
+
typed: 'typed in',
|
|
46
|
+
sandbox: 'signed in (or renewed) in a sandbox',
|
|
47
|
+
};
|
|
48
|
+
const day = (iso) => (iso ? iso.slice(0, 10) : '');
|
|
49
|
+
/** `sandbox account status`: one line per service. */
|
|
50
|
+
export function servicesText(o) {
|
|
51
|
+
const table = [
|
|
52
|
+
['SERVICE', 'SHARE', 'SIGN-IN', 'SAVED', 'NOTE'],
|
|
53
|
+
...o.services.map((s) => [
|
|
54
|
+
s.kind,
|
|
55
|
+
s.share ? 'on' : 'off',
|
|
56
|
+
STATE_TEXT[s.state],
|
|
57
|
+
day(s.savedAt) || '-',
|
|
58
|
+
[s.summary, s.heldBy ? 'in use by sandbox ' + s.heldBy : null].filter(Boolean).join(', ') || '-',
|
|
59
|
+
]),
|
|
60
|
+
];
|
|
61
|
+
const widths = table[0].map((_, i) => Math.max(...table.map((r) => r[i].length)));
|
|
62
|
+
return table
|
|
63
|
+
.map((r) => r.map((cell, i) => (i === r.length - 1 ? cell : cell.padEnd(widths[i] + 2))).join(''))
|
|
64
|
+
.join('\n');
|
|
65
|
+
}
|
|
66
|
+
export const listServices = defineQuery({
|
|
67
|
+
id: 'accounts.services',
|
|
68
|
+
title: 'Services',
|
|
69
|
+
summary: 'service sign-ins for sandboxes (Epic, Steam, Unity, signing keys ...): shared or not, saved or not',
|
|
70
|
+
input: z.object({ name: KindSchema }),
|
|
71
|
+
output: ServicesListSchema,
|
|
72
|
+
text: servicesText,
|
|
73
|
+
cli: { name: 'account', action: 'status', positional: ['name'], json: true },
|
|
74
|
+
});
|
|
75
|
+
const MessageSchema = z.object({ message: z.string().describe('What happened') });
|
|
76
|
+
export const serviceImport = defineOperation({
|
|
77
|
+
id: 'accounts.service.import',
|
|
78
|
+
title: 'Import…',
|
|
79
|
+
summary: "save a service sign-in from this PC's program or a file, for sandboxes",
|
|
80
|
+
input: z.object({
|
|
81
|
+
name: KindSchema,
|
|
82
|
+
from: z.string().max(1000).optional().describe('(import) the file or folder to take the sign-in from'),
|
|
83
|
+
}),
|
|
84
|
+
output: MessageSchema,
|
|
85
|
+
text: (o) => o.message,
|
|
86
|
+
cli: { name: 'account', action: 'import', positional: ['name'], flags: { from: { flag: '--from', value: 'path' } } },
|
|
87
|
+
});
|
|
88
|
+
export const serviceLogin = defineOperation({
|
|
89
|
+
id: 'accounts.service.login',
|
|
90
|
+
title: 'Sign in…',
|
|
91
|
+
summary: 'type a service key or token to keep for sandboxes',
|
|
92
|
+
input: z.object({ name: KindSchema }),
|
|
93
|
+
output: MessageSchema,
|
|
94
|
+
text: (o) => o.message,
|
|
95
|
+
cli: { name: 'account', action: 'login', positional: ['name'] },
|
|
96
|
+
});
|
|
97
|
+
export const serviceLogout = defineOperation({
|
|
98
|
+
id: 'accounts.service.logout',
|
|
99
|
+
title: 'Forget…',
|
|
100
|
+
summary: 'delete a saved service sign-in, and every copy a sandbox left that this PC has not taken yet',
|
|
101
|
+
danger: true,
|
|
102
|
+
input: z.object({ name: KindSchema }),
|
|
103
|
+
output: z.object({ deleted: z.boolean().describe('One was saved and is gone'), message: z.string() }),
|
|
104
|
+
text: (o) => o.message,
|
|
105
|
+
cli: { name: 'account', action: 'logout', positional: ['name'] },
|
|
106
|
+
});
|
|
107
|
+
export const SERVICE_OPERATIONS = [serviceImport, serviceLogin, serviceLogout];
|
|
108
|
+
/** The category id of a service in the menu and its section id on the web. */
|
|
109
|
+
export function serviceCategory(kind) {
|
|
110
|
+
return 'service:' + kind;
|
|
111
|
+
}
|
|
112
|
+
/** What can be done with one service: share or stop, import, sign in, forget. */
|
|
113
|
+
function serviceActions(s) {
|
|
114
|
+
return [
|
|
115
|
+
action(setSetting, { scope: 'user', key: 'accounts.' + s.kind + '.share', value: !s.share }, {
|
|
116
|
+
key: 't',
|
|
117
|
+
verb: s.share ? 'stop sharing' : 'share',
|
|
118
|
+
label: s.share ? 'Stop sharing' : 'Share with sandboxes',
|
|
119
|
+
importance: 1,
|
|
120
|
+
}),
|
|
121
|
+
...(s.canImport ? [action(serviceImport, { name: s.kind }, { key: 'i', verb: 'import', importance: 1 })] : []),
|
|
122
|
+
...(s.canLogin ? [action(serviceLogin, { name: s.kind }, { key: 'n', verb: 'sign in', importance: 1 })] : []),
|
|
123
|
+
...(s.state !== 'none'
|
|
124
|
+
? [action(serviceLogout, { name: s.kind }, { key: 'x', verb: 'forget', importance: 2 })]
|
|
125
|
+
: []),
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
/** How to get a first sign-in, in words. */
|
|
129
|
+
function howTo(s) {
|
|
130
|
+
const ways = [
|
|
131
|
+
...(s.canImport ? ['sandbox account import ' + s.kind + (s.needsFile ? ' --from <file>' : '')] : []),
|
|
132
|
+
...(s.canLogin ? ['sandbox account login ' + s.kind] : []),
|
|
133
|
+
];
|
|
134
|
+
return ways.length ? ways.join(', or ') : 'sign in once inside a sandbox';
|
|
135
|
+
}
|
|
136
|
+
/** One service's section: its facts, its warnings and its actions. */
|
|
137
|
+
export function serviceSection(s) {
|
|
138
|
+
const facts = [
|
|
139
|
+
{
|
|
140
|
+
id: s.kind + ':share',
|
|
141
|
+
label: 'Shared with sandboxes',
|
|
142
|
+
value: s.share ? 'on' : 'off (sandbox config set accounts.' + s.kind + '.share true)',
|
|
143
|
+
tone: s.share ? 'ok' : 'muted',
|
|
144
|
+
description: 'For sandboxes that use ' + s.usedBy.join(' or ') + '. ' + s.warnings.join(' '),
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: s.kind + ':state',
|
|
148
|
+
label: 'Sign-in',
|
|
149
|
+
value: STATE_TEXT[s.state] + (s.savedAt ? ' ' + day(s.savedAt) : ''),
|
|
150
|
+
tone: STATE_TONE[s.state],
|
|
151
|
+
description: s.state === 'none' ? 'Save one: ' + howTo(s) + '.' : undefined,
|
|
152
|
+
},
|
|
153
|
+
...(s.source ? [{ id: s.kind + ':source', label: 'From', value: SOURCE_TEXT[s.source] ?? s.source }] : []),
|
|
154
|
+
...(s.summary ? [{ id: s.kind + ':summary', label: 'Account', value: s.summary }] : []),
|
|
155
|
+
...(s.heldBy
|
|
156
|
+
? [{ id: s.kind + ':held', label: 'In use by', value: 'sandbox ' + s.heldBy, tone: 'accent' }]
|
|
157
|
+
: []),
|
|
158
|
+
];
|
|
159
|
+
const warnings = s.warnings.map((w, i) => ({
|
|
160
|
+
id: s.kind + ':warning:' + i,
|
|
161
|
+
label: w,
|
|
162
|
+
tone: 'warn',
|
|
163
|
+
group: 'Before you share it',
|
|
164
|
+
}));
|
|
165
|
+
const notes = [
|
|
166
|
+
...(s.state === 'unreadable'
|
|
167
|
+
? [{ text: 'Only the Windows user that saved it can read it: save it again.', tone: 'muted' }]
|
|
168
|
+
: []),
|
|
169
|
+
];
|
|
170
|
+
return {
|
|
171
|
+
id: serviceCategory(s.kind),
|
|
172
|
+
title: s.label,
|
|
173
|
+
summary: (s.share ? 'shared' : 'not shared') + ' · ' + STATE_TEXT[s.state],
|
|
174
|
+
facts,
|
|
175
|
+
items: warnings,
|
|
176
|
+
notes,
|
|
177
|
+
actions: serviceActions(s),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/** The sections of every service (appended to the accounts page on the web). */
|
|
181
|
+
export function serviceSections(o) {
|
|
182
|
+
return o.services.map(serviceSection);
|
|
183
|
+
}
|
|
184
|
+
/** One service's page for the menu: its section, and its actions as keys. */
|
|
185
|
+
export function servicePage(s) {
|
|
186
|
+
return { title: 'Accounts', sections: [serviceSection(s)] };
|
|
187
|
+
}
|