windows-development-sandbox 1.17.3 → 1.18.2
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 +1 -1
- 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-IIHL7W6B.js} +51 -16
- package/dist/bin/{chunk-XYUVIVJT.js → chunk-M4OM632I.js} +1 -1
- package/dist/bin/{chunk-REE3EMHD.js → chunk-MQIA7GNB.js} +3813 -1702
- 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/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 +35 -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 -167
- 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 +101 -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 -604
- package/dist/runtime/db.js +334 -11
- package/dist/runtime/main.js +2519 -723
- 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 +1 -0
- 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
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
// Several Claude logins for the sandbox. Each account is a saved copy of Claude's .credentials.json
|
|
2
|
-
// `oauthAccount` block of .claude.json, kept
|
|
3
|
-
//
|
|
1
|
+
// Several Claude logins for the sandbox. Each account is a saved copy of Claude's .credentials.json (a subscription) or
|
|
2
|
+
// an Anthropic API key (one a Console login made, or one you gave), plus the `oauthAccount` block of .claude.json, kept
|
|
3
|
+
// in host-only storage (<storage>\accounts, never mapped into a sandbox) and encrypted with Windows DPAPI
|
|
4
|
+
// (host/secrets): only the name, e-mail, date, kind and a key's last 4 characters are readable in the file. Accounts
|
|
4
5
|
// saved before that were plain; they are encrypted the first time they are read.
|
|
5
|
-
// Switching writes them into the sandbox's persistent Claude state
|
|
6
|
-
// restarted in place with --continue so the
|
|
6
|
+
// Switching writes them into the sandbox's persistent Claude state, where Claude itself keeps that kind of login
|
|
7
|
+
// (claude-login.ts); when the sandbox is running the agent is then restarted in place with --continue so the
|
|
8
|
+
// conversation carries on.
|
|
7
9
|
import fs from 'node:fs';
|
|
8
10
|
import path from 'node:path';
|
|
9
11
|
import { z } from 'zod';
|
|
10
12
|
import { isSafeFileName } from '../../shared/paths/index.js';
|
|
11
13
|
import { JsonFile } from '../../shared/io/index.js';
|
|
12
14
|
import { storageOf } from '../storage/index.js';
|
|
13
|
-
import { errMsg, fail, fileExists } from '../../core/util/index.js';
|
|
15
|
+
import { errMsg, fail, fileExists, isLink } from '../../core/util/index.js';
|
|
14
16
|
import { SecretStore } from '../secrets/index.js';
|
|
15
17
|
import { PluginShare } from './plugin-share.js';
|
|
16
|
-
import { clearAccountCaches, emailOf, MAX_STATE_BYTES, readLogin, sandboxClaudeDir, readUntrusted, sandboxLogin, writeSandboxJson, } from './claude-login.js';
|
|
18
|
+
import { apiKeyProblem, clearAccountCaches, dropSubscription, emailOf, keyState, LOGIN_KINDS, maskApiKey, MAX_STATE_BYTES, readLogin, sandboxClaudeDir, readUntrusted, sandboxLogin, subscriptionState, writeSandboxJson, } from './claude-login.js';
|
|
17
19
|
const NAME = /^[A-Za-z0-9_-]{1,40}$/;
|
|
20
|
+
/** A plain file from before accounts were encrypted (always a subscription). */
|
|
18
21
|
const AccountSchema = z.object({
|
|
19
22
|
name: z.string().regex(NAME),
|
|
20
23
|
savedAt: z.string().max(64),
|
|
@@ -26,15 +29,27 @@ const IndexSchema = z.object({
|
|
|
26
29
|
default: z.string().regex(NAME).optional(),
|
|
27
30
|
active: z.record(z.string().max(200), z.string().regex(NAME)).optional(),
|
|
28
31
|
});
|
|
29
|
-
/** On disk: the readable details and the sealed login ({ credentials, oauthAccount }). */
|
|
32
|
+
/** On disk: the readable details and the sealed login ({ credentials, oauthAccount, apiKey }). */
|
|
30
33
|
const SealedSchema = z.object({
|
|
31
34
|
name: z.string().regex(NAME),
|
|
32
35
|
savedAt: z.string().max(64),
|
|
33
36
|
email: z.string().max(200).nullable(),
|
|
37
|
+
/** Missing in accounts saved before keys could be: a subscription. */
|
|
38
|
+
kind: z.enum(LOGIN_KINDS).optional(),
|
|
39
|
+
keyHint: z
|
|
40
|
+
.string()
|
|
41
|
+
.regex(/^sk-ant-\.\.\.[A-Za-z0-9_-]{4}$/)
|
|
42
|
+
.optional(),
|
|
34
43
|
sealed: z.string().max(200000),
|
|
35
44
|
});
|
|
36
45
|
const StoredSchema = z.union([SealedSchema, AccountSchema]);
|
|
37
46
|
const isSealed = (s) => 'sealed' in s;
|
|
47
|
+
/** What the sealed part holds. */
|
|
48
|
+
const LoginSchema = z.object({
|
|
49
|
+
credentials: z.unknown(),
|
|
50
|
+
oauthAccount: z.unknown(),
|
|
51
|
+
apiKey: z.string().max(512).optional(),
|
|
52
|
+
});
|
|
38
53
|
export function accountsDir(storageRoot) {
|
|
39
54
|
return path.join(storageRoot, 'accounts');
|
|
40
55
|
}
|
|
@@ -95,18 +110,40 @@ export class AccountStore {
|
|
|
95
110
|
return new JsonFile(StoredSchema, path.join(accountsDir(this.storageRoot), name + '.json'));
|
|
96
111
|
}
|
|
97
112
|
seal(account) {
|
|
98
|
-
const { name, savedAt, email, credentials, oauthAccount } = account;
|
|
99
|
-
return {
|
|
113
|
+
const { name, savedAt, email, kind, keyHint, credentials, oauthAccount, apiKey } = account;
|
|
114
|
+
return {
|
|
115
|
+
name,
|
|
116
|
+
savedAt,
|
|
117
|
+
email,
|
|
118
|
+
kind,
|
|
119
|
+
...(keyHint ? { keyHint } : {}),
|
|
120
|
+
sealed: this.secrets.sealJson({ credentials, oauthAccount, ...(apiKey ? { apiKey } : {}) }),
|
|
121
|
+
};
|
|
100
122
|
}
|
|
101
123
|
store(name, login) {
|
|
102
|
-
const account = {
|
|
124
|
+
const account = {
|
|
125
|
+
name,
|
|
126
|
+
savedAt: new Date().toISOString(),
|
|
127
|
+
email: emailOf(login.oauthAccount),
|
|
128
|
+
keyHint: login.apiKey ? maskApiKey(login.apiKey) : null,
|
|
129
|
+
...login,
|
|
130
|
+
};
|
|
103
131
|
this.file(name).write(this.seal(account));
|
|
104
132
|
return account;
|
|
105
133
|
}
|
|
106
|
-
/** The saved account's name, date
|
|
134
|
+
/** The saved account's name, date, e-mail and kind (nothing is decrypted). */
|
|
107
135
|
meta(name) {
|
|
108
136
|
const stored = validName(name) ? this.file(name).read() : null;
|
|
109
|
-
|
|
137
|
+
if (!stored)
|
|
138
|
+
return null;
|
|
139
|
+
const sealed = isSealed(stored) ? stored : null;
|
|
140
|
+
return {
|
|
141
|
+
name: stored.name,
|
|
142
|
+
savedAt: stored.savedAt,
|
|
143
|
+
email: stored.email,
|
|
144
|
+
kind: sealed?.kind ?? 'subscription',
|
|
145
|
+
keyHint: sealed?.keyHint ?? null,
|
|
146
|
+
};
|
|
110
147
|
}
|
|
111
148
|
/**
|
|
112
149
|
* The saved account with its login decrypted. A plain file from an older version is encrypted on the spot (it stays
|
|
@@ -117,16 +154,29 @@ export class AccountStore {
|
|
|
117
154
|
if (!stored)
|
|
118
155
|
return null;
|
|
119
156
|
if (isSealed(stored)) {
|
|
120
|
-
const login = this.secrets.openJson(stored.sealed);
|
|
121
|
-
|
|
157
|
+
const login = LoginSchema.parse(this.secrets.openJson(stored.sealed));
|
|
158
|
+
const kind = stored.kind ?? 'subscription';
|
|
159
|
+
if (kind !== 'subscription' && (login.apiKey === undefined || apiKeyProblem(login.apiKey) !== null))
|
|
160
|
+
fail('The saved account "' + name + '" holds no usable API key.', 'Remove it and save the key again.');
|
|
161
|
+
return {
|
|
162
|
+
name: stored.name,
|
|
163
|
+
savedAt: stored.savedAt,
|
|
164
|
+
email: stored.email,
|
|
165
|
+
kind,
|
|
166
|
+
keyHint: stored.keyHint ?? null,
|
|
167
|
+
credentials: login.credentials,
|
|
168
|
+
oauthAccount: login.oauthAccount,
|
|
169
|
+
...(kind === 'subscription' ? {} : { apiKey: login.apiKey }),
|
|
170
|
+
};
|
|
122
171
|
}
|
|
172
|
+
const plain = { ...stored, kind: 'subscription', keyHint: null };
|
|
123
173
|
try {
|
|
124
|
-
this.file(name).write(this.seal(
|
|
174
|
+
this.file(name).write(this.seal(plain));
|
|
125
175
|
}
|
|
126
176
|
catch {
|
|
127
177
|
// Could not encrypt now (DPAPI unavailable): the old plain file stays and is tried again next time.
|
|
128
178
|
}
|
|
129
|
-
return
|
|
179
|
+
return plain;
|
|
130
180
|
}
|
|
131
181
|
/** Encrypts every account still saved as plain text (older versions); returns how many it did. */
|
|
132
182
|
sealLegacy() {
|
|
@@ -171,22 +221,39 @@ export class AccountStore {
|
|
|
171
221
|
isDefault: a.name === def,
|
|
172
222
|
isActive: a.name === active,
|
|
173
223
|
savedAt: a.savedAt,
|
|
224
|
+
kind: a.kind,
|
|
225
|
+
keyHint: a.keyHint,
|
|
174
226
|
}));
|
|
175
227
|
}
|
|
176
|
-
/**
|
|
177
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Saves the sandbox's current login (or this PC's, with fromHost) under `name`, whichever kind it is (`how.keyOnly`:
|
|
230
|
+
* only an API key; `how.envKey`: this PC's ANTHROPIC_API_KEY when nothing else is found).
|
|
231
|
+
*/
|
|
232
|
+
save(dataPath, name, fromHost, how = {}) {
|
|
178
233
|
assertName(name);
|
|
179
234
|
const source = fromHost ?? sandboxLogin(dataPath);
|
|
180
|
-
|
|
181
|
-
|
|
235
|
+
return this.keep(dataPath, name, readLogin(source.claudeDir, source.globalStateFile, how), !fromHost);
|
|
236
|
+
}
|
|
237
|
+
/** Saves an API key you gave (typed in) under `name`. */
|
|
238
|
+
saveApiKey(dataPath, name, apiKey) {
|
|
239
|
+
assertName(name);
|
|
240
|
+
const key = apiKey.trim();
|
|
241
|
+
const problem = apiKeyProblem(key);
|
|
242
|
+
if (problem)
|
|
243
|
+
fail('That is not an Anthropic API key.', problem);
|
|
244
|
+
return this.keep(dataPath, name, { kind: 'api-key', credentials: null, oauthAccount: null, apiKey: key }, false);
|
|
245
|
+
}
|
|
246
|
+
keep(dataPath, name, login, fromSandbox) {
|
|
247
|
+
const account = this.store(name, login);
|
|
248
|
+
if (fromSandbox)
|
|
182
249
|
setActive(dataPath, name);
|
|
183
250
|
if (!this.defaultName())
|
|
184
251
|
this.setDefault(name);
|
|
185
252
|
return account;
|
|
186
253
|
}
|
|
187
254
|
/**
|
|
188
|
-
* Makes `name` the sandbox's login. Claude refreshes (rotates) tokens while it runs, so the outgoing
|
|
189
|
-
* current tokens are saved back first; otherwise its stored refresh token could already be invalid.
|
|
255
|
+
* Makes `name` the sandbox's login. Claude refreshes (rotates) subscription tokens while it runs, so the outgoing
|
|
256
|
+
* subscription's current tokens are saved back first; otherwise its stored refresh token could already be invalid.
|
|
190
257
|
*/
|
|
191
258
|
activate(dataPath, name) {
|
|
192
259
|
const account = this.read(name) ??
|
|
@@ -196,9 +263,12 @@ export class AccountStore {
|
|
|
196
263
|
.join(', ') || 'none yet; save one with sandbox account save <name>'));
|
|
197
264
|
const { claudeDir, globalStateFile } = sandboxLogin(dataPath);
|
|
198
265
|
const outgoing = this.activeFor(dataPath);
|
|
199
|
-
if (outgoing && outgoing !== name && this.meta(outgoing)
|
|
266
|
+
if (outgoing && outgoing !== name && this.meta(outgoing)?.kind === 'subscription') {
|
|
200
267
|
try {
|
|
201
|
-
|
|
268
|
+
const current = readLogin(claudeDir, globalStateFile);
|
|
269
|
+
// Only a subscription is saved back over a subscription (a key login made inside would not be one).
|
|
270
|
+
if (current.kind === 'subscription')
|
|
271
|
+
this.store(outgoing, current);
|
|
202
272
|
}
|
|
203
273
|
catch {
|
|
204
274
|
/* nothing usable to save back */
|
|
@@ -206,10 +276,20 @@ export class AccountStore {
|
|
|
206
276
|
}
|
|
207
277
|
fs.mkdirSync(claudeDir, { recursive: true });
|
|
208
278
|
clearAccountCaches(claudeDir);
|
|
209
|
-
writeSandboxJson(path.join(claudeDir, '.credentials.json'), account.credentials);
|
|
210
279
|
const state = readUntrusted(globalStateFile, MAX_STATE_BYTES);
|
|
211
|
-
|
|
212
|
-
|
|
280
|
+
// A state file that is there but cannot be read (too big, not JSON) is not replaced: it holds the projects' state.
|
|
281
|
+
const writable = state !== null || !fileExists(globalStateFile) || isLink(globalStateFile);
|
|
282
|
+
if (account.kind === 'subscription') {
|
|
283
|
+
writeSandboxJson(path.join(claudeDir, '.credentials.json'), account.credentials);
|
|
284
|
+
if (writable && (account.oauthAccount || typeof state?.primaryApiKey === 'string'))
|
|
285
|
+
writeSandboxJson(globalStateFile, subscriptionState(state ?? {}, account.oauthAccount));
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
if (!writable)
|
|
289
|
+
fail("The sandbox's Claude state (" + globalStateFile + ') cannot be read, so the API key cannot be put in.', 'Delete that file (Claude makes a new one), then switch again.');
|
|
290
|
+
dropSubscription(claudeDir);
|
|
291
|
+
writeSandboxJson(globalStateFile, keyState(state ?? {}, account.apiKey ?? '', account.oauthAccount));
|
|
292
|
+
}
|
|
213
293
|
this.shareStep(dataPath, claudeDir);
|
|
214
294
|
setActive(dataPath, name);
|
|
215
295
|
return account;
|
|
@@ -1,7 +1,25 @@
|
|
|
1
|
+
/** subscription: claude.ai OAuth; console: the key a Console login made; api-key: a key you gave. */
|
|
2
|
+
export type LoginKind = 'subscription' | 'console' | 'api-key';
|
|
3
|
+
export declare const LOGIN_KINDS: readonly ["subscription", "console", "api-key"];
|
|
1
4
|
export interface Login {
|
|
5
|
+
readonly kind: LoginKind;
|
|
6
|
+
/** The text of .credentials.json (a subscription); null for a key. */
|
|
2
7
|
readonly credentials: unknown;
|
|
3
8
|
readonly oauthAccount: unknown;
|
|
9
|
+
/** The API key (console and api-key). */
|
|
10
|
+
readonly apiKey?: string;
|
|
4
11
|
}
|
|
12
|
+
/** How a login is looked for. */
|
|
13
|
+
export interface CaptureOptions {
|
|
14
|
+
/** Only an API key (skip a subscription found next to it). */
|
|
15
|
+
readonly keyOnly?: boolean;
|
|
16
|
+
/** ANTHROPIC_API_KEY of this PC: the last place looked at (--from-host only). */
|
|
17
|
+
readonly envKey?: string | null;
|
|
18
|
+
}
|
|
19
|
+
/** What is wrong with a typed or found API key, or null when it has the right shape. Never quotes the key. */
|
|
20
|
+
export declare function apiKeyProblem(key: string): string | null;
|
|
21
|
+
/** The most of a key that is ever shown: sk-ant-...abcd. */
|
|
22
|
+
export declare function maskApiKey(key: string): string;
|
|
5
23
|
export interface LoginFiles {
|
|
6
24
|
readonly claudeDir: string;
|
|
7
25
|
readonly globalStateFile: string;
|
|
@@ -12,8 +30,20 @@ export declare function sandboxClaudeDir(dataPath: string): string;
|
|
|
12
30
|
export declare function emailOf(oauthAccount: unknown): string | null;
|
|
13
31
|
/** Reads a JSON file that may be sandbox-written: no links, size-capped. */
|
|
14
32
|
export declare function readUntrusted(file: string, max: number): unknown;
|
|
15
|
-
/**
|
|
16
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The login currently in a Claude config folder (+ its global state file), whichever kind it is: a subscription first
|
|
35
|
+
* (Claude uses it over a key), then a Console or API key in `primaryApiKey`, then `envKey`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readLogin(claudeDir: string, globalStateFile: string, how?: CaptureOptions): Login;
|
|
38
|
+
/** The sandbox's .claude.json for a subscription: no `primaryApiKey` (Claude would ask about it), its e-mail block. */
|
|
39
|
+
export declare function subscriptionState(state: Record<string, unknown>, oauthAccount: unknown): Record<string, unknown>;
|
|
40
|
+
/** The sandbox's .claude.json for a key: the key where a Console login keeps it, approved, with its e-mail block. */
|
|
41
|
+
export declare function keyState(state: Record<string, unknown>, apiKey: string, oauthAccount: unknown): Record<string, unknown>;
|
|
42
|
+
/**
|
|
43
|
+
* Takes the subscription out of the sandbox's .credentials.json (Claude would use it over the key); the rest of the
|
|
44
|
+
* file (MCP server logins) stays. A file that cannot be read holds no login worth keeping and goes.
|
|
45
|
+
*/
|
|
46
|
+
export declare function dropSubscription(claudeDir: string): void;
|
|
17
47
|
/** The sandbox's Claude login files (refuses when the data folder holds a link on the way). */
|
|
18
48
|
export declare function sandboxLogin(dataPath: string): LoginFiles;
|
|
19
49
|
/** Replaces a file that may be a sandbox-planted link instead of writing through it. */
|
|
@@ -1,10 +1,44 @@
|
|
|
1
|
-
// A Claude login as files
|
|
2
|
-
//
|
|
1
|
+
// A Claude login as files. Claude Code keeps three kinds (checked against Claude Code 2.1.296):
|
|
2
|
+
// - a subscription (/login with Claude Pro, Max, Team ...): `claudeAiOauth` in .credentials.json;
|
|
3
|
+
// - a Console login (/login with a Claude Console account): Claude makes an API key and keeps it as `primaryApiKey` in
|
|
4
|
+
// .claude.json (not in .credentials.json), with the last 20 characters in `customApiKeyResponses.approved`;
|
|
5
|
+
// - an API key of your own (ANTHROPIC_API_KEY): goes where a Console key goes, so Claude uses it without asking.
|
|
6
|
+
// Plus the `oauthAccount` block of .claude.json (the e-mail). Claude uses a subscription over a `primaryApiKey`, so a
|
|
7
|
+
// key login goes in with no `claudeAiOauth` next to it, and a subscription goes in with no `primaryApiKey`.
|
|
8
|
+
// The sandbox's copies are guest-written: read without following links and size-capped; links are replaced, never
|
|
9
|
+
// written through. A key read from the sandbox must have the shape of an API key.
|
|
3
10
|
import fs from 'node:fs';
|
|
4
11
|
import path from 'node:path';
|
|
5
12
|
import { assertTestWritable } from '../test-guard.js';
|
|
6
13
|
import { hostDataFolder, hostDataRoot } from '../storage/index.js';
|
|
7
14
|
import { assertNoLinks, fail, fileExists, isLink, removeLink, tryReadJson, writeJson } from '../../core/util/index.js';
|
|
15
|
+
export const LOGIN_KINDS = ['subscription', 'console', 'api-key'];
|
|
16
|
+
/** sk-ant-api03-...: a normal Anthropic API key (letters, digits, - and _, which is what Claude Code accepts). */
|
|
17
|
+
const API_KEY = /^sk-ant-api\d{2}-[A-Za-z0-9_-]{20,400}$/;
|
|
18
|
+
const API_USER_KEY = /^sk-ant-usr\d{2}-[A-Za-z0-9_-]{20,400}$/;
|
|
19
|
+
/** What is wrong with a typed or found API key, or null when it has the right shape. Never quotes the key. */
|
|
20
|
+
export function apiKeyProblem(key) {
|
|
21
|
+
if (API_KEY.test(key) || API_USER_KEY.test(key))
|
|
22
|
+
return null;
|
|
23
|
+
if (key.startsWith('sk-ant-oat'))
|
|
24
|
+
return 'That is a Claude subscription token (from claude setup-token), not an API key. Save the subscription login instead.';
|
|
25
|
+
if (key.startsWith('sk-ant-admin'))
|
|
26
|
+
return 'That is an Admin key: it cannot run Claude. Make an API key instead.';
|
|
27
|
+
return 'An Anthropic API key starts with sk-ant-api or sk-ant-usr and has only letters, digits, - and _ (copy it from the Claude Console).';
|
|
28
|
+
}
|
|
29
|
+
/** The most of a key that is ever shown: sk-ant-...abcd. */
|
|
30
|
+
export function maskApiKey(key) {
|
|
31
|
+
return 'sk-ant-...' + key.slice(-4);
|
|
32
|
+
}
|
|
33
|
+
/** What Claude Code records in customApiKeyResponses for a key it may use (its last 20 characters). */
|
|
34
|
+
function approvalOf(key) {
|
|
35
|
+
return key.trim().slice(-20);
|
|
36
|
+
}
|
|
37
|
+
/** A subscription login: .credentials.json with a `claudeAiOauth` block. */
|
|
38
|
+
function hasSubscription(credentials) {
|
|
39
|
+
const oauth = credentials?.claudeAiOauth;
|
|
40
|
+
return typeof oauth === 'object' && oauth !== null;
|
|
41
|
+
}
|
|
8
42
|
const MAX_LOGIN_BYTES = 1024 * 1024;
|
|
9
43
|
/** .claude.json holds per-project state and can be large; still capped so a hostile file cannot exhaust memory. */
|
|
10
44
|
export const MAX_STATE_BYTES = 256 * 1024 * 1024;
|
|
@@ -21,16 +55,75 @@ export function emailOf(oauthAccount) {
|
|
|
21
55
|
export function readUntrusted(file, max) {
|
|
22
56
|
return fileExists(file) && !isLink(file) && fs.statSync(file).size <= max ? tryReadJson(file) : null;
|
|
23
57
|
}
|
|
24
|
-
/**
|
|
25
|
-
|
|
58
|
+
/**
|
|
59
|
+
* The login currently in a Claude config folder (+ its global state file), whichever kind it is: a subscription first
|
|
60
|
+
* (Claude uses it over a key), then a Console or API key in `primaryApiKey`, then `envKey`.
|
|
61
|
+
*/
|
|
62
|
+
export function readLogin(claudeDir, globalStateFile, how = {}) {
|
|
26
63
|
const credentials = readUntrusted(path.join(claudeDir, '.credentials.json'), MAX_LOGIN_BYTES);
|
|
27
|
-
|
|
28
|
-
|
|
64
|
+
const state = readUntrusted(globalStateFile, MAX_STATE_BYTES);
|
|
65
|
+
const oauthAccount = state?.oauthAccount ?? null;
|
|
66
|
+
if (!how.keyOnly && hasSubscription(credentials))
|
|
67
|
+
return { kind: 'subscription', credentials, oauthAccount };
|
|
68
|
+
const managed = typeof state?.primaryApiKey === 'string' ? state.primaryApiKey.trim() : null;
|
|
69
|
+
if (managed !== null && apiKeyProblem(managed) === null)
|
|
70
|
+
return { kind: oauthAccount ? 'console' : 'api-key', credentials: null, oauthAccount, apiKey: managed };
|
|
71
|
+
const envKey = how.envKey?.trim();
|
|
72
|
+
if (envKey && apiKeyProblem(envKey) === null)
|
|
73
|
+
return { kind: 'api-key', credentials: null, oauthAccount: null, apiKey: envKey };
|
|
74
|
+
return how.keyOnly
|
|
75
|
+
? fail('No Claude API key found in ' + claudeDir + (how.envKey === undefined ? '.' : ' or in ANTHROPIC_API_KEY.'), 'Sign in to Claude with a Claude Console account first, or save a key you type: sandbox account save <name> --api-key')
|
|
76
|
+
: fail('No Claude login found in ' + claudeDir + '.', 'Sign in to Claude first (inside the sandbox, or on this PC with --from-host), then save it again.');
|
|
77
|
+
}
|
|
78
|
+
/** customApiKeyResponses with `add` approved, or `remove` taken off the approved list (Claude's own shape). */
|
|
79
|
+
function approvals(state, change) {
|
|
80
|
+
const given = (state.customApiKeyResponses ?? {});
|
|
81
|
+
const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === 'string') : [];
|
|
82
|
+
const add = change.add === undefined ? undefined : approvalOf(change.add);
|
|
83
|
+
const remove = change.remove === undefined ? undefined : approvalOf(change.remove);
|
|
84
|
+
if (add === undefined && !strings(given.approved).includes(remove ?? ''))
|
|
85
|
+
return state;
|
|
86
|
+
const approved = strings(given.approved).filter((k) => k !== add && k !== remove);
|
|
29
87
|
return {
|
|
30
|
-
|
|
31
|
-
|
|
88
|
+
...state,
|
|
89
|
+
customApiKeyResponses: {
|
|
90
|
+
approved: add === undefined ? approved : [...approved, add],
|
|
91
|
+
rejected: strings(given.rejected).filter((k) => k !== add),
|
|
92
|
+
},
|
|
32
93
|
};
|
|
33
94
|
}
|
|
95
|
+
/** The sandbox's .claude.json for a subscription: no `primaryApiKey` (Claude would ask about it), its e-mail block. */
|
|
96
|
+
export function subscriptionState(state, oauthAccount) {
|
|
97
|
+
const { primaryApiKey, ...rest } = state;
|
|
98
|
+
const cleaned = typeof primaryApiKey === 'string' ? approvals(rest, { remove: primaryApiKey }) : rest;
|
|
99
|
+
return oauthAccount ? { ...cleaned, oauthAccount } : cleaned;
|
|
100
|
+
}
|
|
101
|
+
/** The sandbox's .claude.json for a key: the key where a Console login keeps it, approved, with its e-mail block. */
|
|
102
|
+
export function keyState(state, apiKey, oauthAccount) {
|
|
103
|
+
const { primaryApiKey, oauthAccount: _outgoing, ...rest } = state;
|
|
104
|
+
const cleaned = typeof primaryApiKey === 'string' ? approvals(rest, { remove: primaryApiKey }) : rest;
|
|
105
|
+
return { ...approvals(cleaned, { add: apiKey }), primaryApiKey: apiKey, ...(oauthAccount ? { oauthAccount } : {}) };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Takes the subscription out of the sandbox's .credentials.json (Claude would use it over the key); the rest of the
|
|
109
|
+
* file (MCP server logins) stays. A file that cannot be read holds no login worth keeping and goes.
|
|
110
|
+
*/
|
|
111
|
+
export function dropSubscription(claudeDir) {
|
|
112
|
+
const file = path.join(claudeDir, '.credentials.json');
|
|
113
|
+
assertTestWritable(file);
|
|
114
|
+
if (isLink(file))
|
|
115
|
+
return removeLink(file);
|
|
116
|
+
if (!fs.existsSync(file))
|
|
117
|
+
return;
|
|
118
|
+
const credentials = readUntrusted(file, MAX_LOGIN_BYTES);
|
|
119
|
+
if (typeof credentials !== 'object' || credentials === null || Array.isArray(credentials))
|
|
120
|
+
return fs.rmSync(file, { force: true });
|
|
121
|
+
const { claudeAiOauth: _login, ...rest } = credentials;
|
|
122
|
+
if (Object.keys(rest).length)
|
|
123
|
+
writeSandboxJson(file, rest);
|
|
124
|
+
else
|
|
125
|
+
fs.rmSync(file, { force: true });
|
|
126
|
+
}
|
|
34
127
|
/** The sandbox's Claude login files (refuses when the data folder holds a link on the way). */
|
|
35
128
|
export function sandboxLogin(dataPath) {
|
|
36
129
|
assertNoLinks(hostDataRoot(dataPath), sandboxClaudeDir(dataPath));
|
|
@@ -6,4 +6,4 @@ export { toRegFile, type RegValue } from './reg-file.js';
|
|
|
6
6
|
export { AccountStore, accountsDir, type Account, type AccountMeta, type AccountRow } from './accounts.js';
|
|
7
7
|
export { PluginShare, PLUGIN_SETTING_KEYS, MAX_PLUGIN_FILES, MAX_PLUGIN_BYTES } from './plugin-share.js';
|
|
8
8
|
export { AgentAccountStore } from './agent-accounts.js';
|
|
9
|
-
export { sandboxClaudeDir } from './claude-login.js';
|
|
9
|
+
export { apiKeyProblem, maskApiKey, sandboxClaudeDir, type LoginKind } from './claude-login.js';
|
|
@@ -6,4 +6,4 @@ export { toRegFile } from './reg-file.js';
|
|
|
6
6
|
export { AccountStore, accountsDir } from './accounts.js';
|
|
7
7
|
export { PluginShare, PLUGIN_SETTING_KEYS, MAX_PLUGIN_FILES, MAX_PLUGIN_BYTES } from './plugin-share.js';
|
|
8
8
|
export { AgentAccountStore } from './agent-accounts.js';
|
|
9
|
-
export { sandboxClaudeDir } from './claude-login.js';
|
|
9
|
+
export { apiKeyProblem, maskApiKey, sandboxClaudeDir } from './claude-login.js';
|