@phnx-labs/agents-cli 1.22.84 → 1.22.86
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/CHANGELOG.md +96 -0
- package/README.md +80 -53
- package/dist/bootstrap.d.ts +7 -6
- package/dist/bootstrap.js +20 -27
- package/dist/cli/command-registry.js +5 -1
- package/dist/commands/accounts.d.ts +6 -13
- package/dist/commands/accounts.js +330 -143
- package/dist/commands/alias.js +7 -3
- package/dist/commands/apply.js +1 -1
- package/dist/commands/auth-mint.d.ts +7 -3
- package/dist/commands/auth-mint.js +21 -9
- package/dist/commands/auth.js +2 -3
- package/dist/commands/browser.js +10 -10
- package/dist/commands/daemon.d.ts +7 -3
- package/dist/commands/daemon.js +40 -25
- package/dist/commands/doctor.js +28 -2
- package/dist/commands/exec.js +87 -39
- package/dist/commands/fleet-capture.js +21 -2
- package/dist/commands/harness-wizard.js +2 -2
- package/dist/commands/lease.js +7 -12
- package/dist/commands/profiles.d.ts +2 -2
- package/dist/commands/profiles.js +20 -13
- package/dist/commands/repo.js +3 -3
- package/dist/commands/run-account-picker.d.ts +3 -3
- package/dist/commands/run-account-picker.js +29 -30
- package/dist/commands/secrets-passthrough.d.ts +20 -0
- package/dist/commands/secrets-passthrough.js +43 -0
- package/dist/commands/setup-accounts.d.ts +3 -2
- package/dist/commands/setup-accounts.js +4 -4
- package/dist/commands/setup-secrets.d.ts +16 -21
- package/dist/commands/setup-secrets.js +43 -224
- package/dist/commands/ssh.js +2 -2
- package/dist/commands/sync.js +1 -1
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.js +43 -40
- package/dist/commands/versions.d.ts +11 -11
- package/dist/commands/versions.js +131 -83
- package/dist/commands/view.d.ts +1 -9
- package/dist/commands/view.js +41 -55
- package/dist/commands/webhook.js +5 -5
- package/dist/commands/workflows.js +2 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.js +13 -42
- package/dist/lib/account-capabilities.d.ts +1 -1
- package/dist/lib/account-capabilities.js +1 -1
- package/dist/lib/account-catalog.d.ts +121 -6
- package/dist/lib/account-catalog.js +421 -7
- package/dist/lib/account-registry.d.ts +8 -30
- package/dist/lib/account-registry.js +36 -79
- package/dist/lib/account-schema.d.ts +1 -1
- package/dist/lib/account-schema.js +1 -1
- package/dist/lib/accounting/account-pool-collect.js +16 -4
- package/dist/lib/accounting/rotate.d.ts +20 -7
- package/dist/lib/accounting/rotate.js +89 -13
- package/dist/lib/accounting/usage.js +11 -16
- package/dist/lib/accounts/add.d.ts +138 -0
- package/dist/lib/accounts/add.js +651 -0
- package/dist/lib/accounts/migrate.d.ts +117 -0
- package/dist/lib/accounts/migrate.js +536 -0
- package/dist/lib/accounts/slots.d.ts +13 -0
- package/dist/lib/accounts/slots.js +100 -0
- package/dist/lib/agent-spec/agents.d.ts +16 -0
- package/dist/lib/agent-spec/agents.js +38 -4
- package/dist/lib/app-bundle-install.js +5 -4
- package/dist/lib/auth-health.d.ts +3 -0
- package/dist/lib/auth-health.js +11 -0
- package/dist/lib/auth-mint.d.ts +52 -16
- package/dist/lib/auth-mint.js +117 -39
- package/dist/lib/browser/chrome.d.ts +1 -1
- package/dist/lib/browser/chrome.js +5 -5
- package/dist/lib/byok-usage.js +3 -3
- package/dist/lib/claude-account-token.d.ts +47 -3
- package/dist/lib/claude-account-token.js +172 -50
- package/dist/lib/cloud/antigravity.js +4 -4
- package/dist/lib/cloud/cursor.js +4 -4
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +6 -6
- package/dist/lib/crabbox/runtimes.d.ts +3 -3
- package/dist/lib/crabbox/runtimes.js +5 -5
- package/dist/lib/daemon/account-state-daemon-service.d.ts +37 -1
- package/dist/lib/daemon/account-state-daemon-service.js +199 -3
- package/dist/lib/daemon/auth-sync-service.js +32 -1
- package/dist/lib/daemon/daemon.d.ts +6 -16
- package/dist/lib/daemon/daemon.js +52 -97
- package/dist/lib/daemon/daemon.test-fixture.d.ts +7 -9
- package/dist/lib/daemon/daemon.test-fixture.js +22 -40
- package/dist/lib/daemon/harness-update-service.d.ts +1 -1
- package/dist/lib/daemon/harness-update-service.js +1 -1
- package/dist/lib/daemon/runner.d.ts +15 -3
- package/dist/lib/daemon/runner.js +49 -24
- package/dist/lib/daemon-health.d.ts +1 -2
- package/dist/lib/daemon-health.js +7 -6
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +1 -11
- package/dist/lib/daemon-webhooks.d.ts +8 -7
- package/dist/lib/daemon-webhooks.js +10 -9
- package/dist/lib/device-config.js +1 -1
- package/dist/lib/devices/doctor-findings.d.ts +1 -1
- package/dist/lib/devices/harness-inventory.d.ts +39 -0
- package/dist/lib/devices/harness-inventory.js +126 -4
- package/dist/lib/doctor-diff.js +2 -1
- package/dist/lib/exec-account-home.d.ts +38 -0
- package/dist/lib/exec-account-home.js +164 -0
- package/dist/lib/exec.d.ts +30 -0
- package/dist/lib/exec.js +101 -31
- package/dist/lib/fleet/apply.d.ts +2 -2
- package/dist/lib/fleet/apply.js +4 -4
- package/dist/lib/fleet/auth-sync.js +4 -3
- package/dist/lib/fleet-shared-repo-sync.js +6 -9
- package/dist/lib/harness/adapter.d.ts +7 -7
- package/dist/lib/harness/adapter.js +10 -6
- package/dist/lib/harness/adapters/grok.js +8 -3
- package/dist/lib/harness/adapters/muse.js +1 -1
- package/dist/lib/harness/adapters/opencode.js +12 -3
- package/dist/lib/harness-auth-capabilities.d.ts +61 -0
- package/dist/lib/harness-auth-capabilities.js +52 -0
- package/dist/lib/helper-versions.d.ts +6 -4
- package/dist/lib/helper-versions.js +5 -4
- package/dist/lib/hosts/credential-transport.d.ts +10 -0
- package/dist/lib/hosts/credential-transport.js +37 -0
- package/dist/lib/hosts/dispatch.d.ts +7 -0
- package/dist/lib/hosts/dispatch.js +13 -6
- package/dist/lib/identity/client.d.ts +3 -3
- package/dist/lib/identity/client.js +3 -3
- package/dist/lib/installations/index.d.ts +1 -1
- package/dist/lib/installations/index.js +1 -1
- package/dist/lib/installations/migrate.js +9 -0
- package/dist/lib/installations/resolve.js +1 -1
- package/dist/lib/installations/shims.d.ts +21 -4
- package/dist/lib/installations/shims.js +86 -5
- package/dist/lib/installations/store.d.ts +43 -0
- package/dist/lib/installations/store.js +75 -7
- package/dist/lib/installations/versions.js +1 -1
- package/dist/lib/menubar/install-menubar.d.ts +3 -3
- package/dist/lib/menubar/install-menubar.js +3 -3
- package/dist/lib/native-accounts.d.ts +35 -0
- package/dist/lib/native-accounts.js +41 -0
- package/dist/lib/net-close.d.ts +11 -0
- package/dist/lib/net-close.js +25 -0
- package/dist/lib/openclaw-keychain.js +27 -1
- package/dist/lib/profiles.js +22 -9
- package/dist/lib/project-resources.js +1 -1
- package/dist/lib/reserved-stores.d.ts +53 -0
- package/dist/lib/reserved-stores.js +120 -0
- package/dist/lib/secrets-client.d.ts +199 -0
- package/dist/lib/secrets-client.js +718 -0
- package/dist/lib/secrets-policy.d.ts +235 -0
- package/dist/lib/secrets-policy.js +505 -0
- package/dist/lib/secrets-types.d.ts +188 -0
- package/dist/lib/secrets-types.js +24 -0
- package/dist/lib/self-heal/checks/shims.js +5 -3
- package/dist/lib/service-manifest.js +3 -4
- package/dist/lib/session/db.d.ts +18 -0
- package/dist/lib/session/db.js +47 -14
- package/dist/lib/session/sync/config.js +2 -2
- package/dist/lib/sha256-asset.d.ts +11 -16
- package/dist/lib/sha256-asset.js +11 -16
- package/dist/lib/share/config.js +14 -5
- package/dist/lib/signin-badge.d.ts +26 -0
- package/dist/lib/signin-badge.js +42 -0
- package/dist/lib/staleness/detectors/workflows.js +3 -105
- package/dist/lib/staleness/writers/workflows.js +2 -1
- package/dist/lib/state.d.ts +7 -4
- package/dist/lib/state.js +18 -7
- package/dist/lib/sync-umbrella.js +2 -2
- package/dist/lib/teams/agents.js +1 -1
- package/dist/lib/types.d.ts +74 -33
- package/dist/lib/view-types.d.ts +6 -3
- package/dist/lib/workflows-registry.d.ts +71 -0
- package/dist/lib/workflows-registry.js +280 -0
- package/dist/lib/workflows.d.ts +21 -21
- package/dist/lib/workflows.js +12 -327
- package/package.json +2 -3
- package/scripts/postinstall.js +35 -30
- package/dist/commands/secrets-import.d.ts +0 -18
- package/dist/commands/secrets-import.js +0 -74
- package/dist/commands/secrets-migrate.d.ts +0 -25
- package/dist/commands/secrets-migrate.js +0 -334
- package/dist/commands/secrets-rotate-passphrase.d.ts +0 -17
- package/dist/commands/secrets-rotate-passphrase.js +0 -96
- package/dist/commands/secrets-sync.d.ts +0 -11
- package/dist/commands/secrets-sync.js +0 -153
- package/dist/commands/secrets-vault.d.ts +0 -10
- package/dist/commands/secrets-vault.js +0 -130
- package/dist/commands/secrets.d.ts +0 -212
- package/dist/commands/secrets.js +0 -3030
- package/dist/lib/accounts/connect.d.ts +0 -176
- package/dist/lib/accounts/connect.js +0 -453
- package/dist/lib/daemon/keychain-reap-service.d.ts +0 -17
- package/dist/lib/daemon/keychain-reap-service.js +0 -32
- package/dist/lib/daemon/secrets-broker-service.d.ts +0 -21
- package/dist/lib/daemon/secrets-broker-service.js +0 -51
- package/dist/lib/secrets/agent.d.ts +0 -358
- package/dist/lib/secrets/agent.js +0 -1291
- package/dist/lib/secrets/audit.d.ts +0 -46
- package/dist/lib/secrets/audit.js +0 -101
- package/dist/lib/secrets/bundles.d.ts +0 -290
- package/dist/lib/secrets/bundles.js +0 -1547
- package/dist/lib/secrets/download-keychain.d.ts +0 -47
- package/dist/lib/secrets/download-keychain.js +0 -70
- package/dist/lib/secrets/drivers/rush.d.ts +0 -14
- package/dist/lib/secrets/drivers/rush.js +0 -90
- package/dist/lib/secrets/fallback.d.ts +0 -48
- package/dist/lib/secrets/fallback.js +0 -48
- package/dist/lib/secrets/filestore.d.ts +0 -222
- package/dist/lib/secrets/filestore.js +0 -1099
- package/dist/lib/secrets/headless.d.ts +0 -43
- package/dist/lib/secrets/headless.js +0 -56
- package/dist/lib/secrets/icloud-import.d.ts +0 -79
- package/dist/lib/secrets/icloud-import.js +0 -206
- package/dist/lib/secrets/index.d.ts +0 -451
- package/dist/lib/secrets/index.js +0 -1568
- package/dist/lib/secrets/install-helper.d.ts +0 -72
- package/dist/lib/secrets/install-helper.js +0 -245
- package/dist/lib/secrets/lease.d.ts +0 -25
- package/dist/lib/secrets/lease.js +0 -44
- package/dist/lib/secrets/linux.d.ts +0 -77
- package/dist/lib/secrets/linux.js +0 -393
- package/dist/lib/secrets/list-filter.d.ts +0 -109
- package/dist/lib/secrets/list-filter.js +0 -261
- package/dist/lib/secrets/mcp.d.ts +0 -93
- package/dist/lib/secrets/mcp.js +0 -211
- package/dist/lib/secrets/profiles.d.ts +0 -10
- package/dist/lib/secrets/profiles.js +0 -13
- package/dist/lib/secrets/push.d.ts +0 -133
- package/dist/lib/secrets/push.js +0 -273
- package/dist/lib/secrets/rc-hygiene.d.ts +0 -63
- package/dist/lib/secrets/rc-hygiene.js +0 -143
- package/dist/lib/secrets/read-backoff.d.ts +0 -27
- package/dist/lib/secrets/read-backoff.js +0 -64
- package/dist/lib/secrets/reaper.d.ts +0 -90
- package/dist/lib/secrets/reaper.js +0 -243
- package/dist/lib/secrets/remote.d.ts +0 -152
- package/dist/lib/secrets/remote.js +0 -344
- package/dist/lib/secrets/reserved-sync.d.ts +0 -66
- package/dist/lib/secrets/reserved-sync.js +0 -147
- package/dist/lib/secrets/scope.d.ts +0 -26
- package/dist/lib/secrets/scope.js +0 -29
- package/dist/lib/secrets/session-store.d.ts +0 -107
- package/dist/lib/secrets/session-store.js +0 -342
- package/dist/lib/secrets/sync-backend.d.ts +0 -48
- package/dist/lib/secrets/sync-backend.js +0 -13
- package/dist/lib/secrets/sync-commands.d.ts +0 -21
- package/dist/lib/secrets/sync-commands.js +0 -21
- package/dist/lib/secrets/sync.d.ts +0 -48
- package/dist/lib/secrets/sync.js +0 -237
- package/dist/lib/secrets/unlock-hints.d.ts +0 -27
- package/dist/lib/secrets/unlock-hints.js +0 -36
- package/dist/lib/secrets/usage-db.d.ts +0 -46
- package/dist/lib/secrets/usage-db.js +0 -96
- package/dist/lib/secrets/vault-age-helper.d.ts +0 -1
- package/dist/lib/secrets/vault-age-helper.js +0 -34
- package/dist/lib/secrets/vault.d.ts +0 -49
- package/dist/lib/secrets/vault.js +0 -399
- package/dist/lib/secrets/windows.d.ts +0 -81
- package/dist/lib/secrets/windows.js +0 -556
- package/scripts/install-helper.js +0 -97
- /package/dist/lib/{secrets/sync-passphrase.d.ts → sync-passphrase.d.ts} +0 -0
- /package/dist/lib/{secrets/sync-passphrase.js → sync-passphrase.js} +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-device account slots (PHNX-3940).
|
|
3
|
+
*
|
|
4
|
+
* An account is a credential slot, not an installation. The binary lives in
|
|
5
|
+
* the one managed harness install; each account gets a HOME-shaped dir under
|
|
6
|
+
* `~/.agents/.history/accounts/<harness>/<accountId>/` with no binary in it.
|
|
7
|
+
* Native OAuth files stay in this dir on the device that minted them and are
|
|
8
|
+
* never copied. Settings and resources are projected through the same writers
|
|
9
|
+
* version homes use (`carryForwardSettings`, `getWriter`) — credentials are
|
|
10
|
+
* excluded by those writers.
|
|
11
|
+
*
|
|
12
|
+
* Slot records live in the device doc (`deviceAccounts.slots`), never central.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'node:fs';
|
|
15
|
+
import * as path from 'node:path';
|
|
16
|
+
import { agentConfigDirName } from '../agents.js';
|
|
17
|
+
import { harnessAuth, harnessWorkerIsPerDevice } from '../harness-auth-capabilities.js';
|
|
18
|
+
import { getGlobalDefault, getVersionHomePath, listInstalledVersions } from '../installations/store.js';
|
|
19
|
+
import { carryForwardSettings } from '../settings-manifest.js';
|
|
20
|
+
import { getHistoryDir, updateMeta } from '../state.js';
|
|
21
|
+
import { ALL_RESOURCE_KINDS, getDetector, getWriter, kindToCapability } from '../staleness/registry.js';
|
|
22
|
+
import { supports } from '../capabilities.js';
|
|
23
|
+
import { isAgentId } from '../types.js';
|
|
24
|
+
const AUTH_MODES = ['native', 'durable', 'per-device'];
|
|
25
|
+
export function slotDir(harness, accountId) {
|
|
26
|
+
if (!isAgentId(harness))
|
|
27
|
+
throw new Error(`Unknown harness '${harness}' for a slot dir.`);
|
|
28
|
+
if (!accountId || /[\\/]|\.\./.test(accountId)) {
|
|
29
|
+
throw new Error(`Invalid account id '${accountId}' for a slot dir.`);
|
|
30
|
+
}
|
|
31
|
+
return path.join(getHistoryDir(), 'accounts', harness, accountId);
|
|
32
|
+
}
|
|
33
|
+
export function readSlots(meta) {
|
|
34
|
+
return { ...(meta.deviceAccounts?.slots ?? {}) };
|
|
35
|
+
}
|
|
36
|
+
export function recordSlot(accountId, slot) {
|
|
37
|
+
if (slot.accountId !== accountId) {
|
|
38
|
+
throw new Error(`recordSlot accountId mismatch: key '${accountId}' vs slot.accountId '${slot.accountId}'.`);
|
|
39
|
+
}
|
|
40
|
+
if (!AUTH_MODES.includes(slot.authMode)) {
|
|
41
|
+
throw new Error(`recordSlot: unknown authMode '${slot.authMode}'.`);
|
|
42
|
+
}
|
|
43
|
+
updateMeta((current) => ({
|
|
44
|
+
...current,
|
|
45
|
+
deviceAccounts: {
|
|
46
|
+
...current.deviceAccounts,
|
|
47
|
+
slots: { ...current.deviceAccounts?.slots, [accountId]: slot },
|
|
48
|
+
},
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
function defaultAuthMode(harness) {
|
|
52
|
+
return harnessWorkerIsPerDevice(harness) ? 'per-device' : 'native';
|
|
53
|
+
}
|
|
54
|
+
function sourceVersion(harness) {
|
|
55
|
+
return getGlobalDefault(harness) ?? listInstalledVersions(harness)[0] ?? null;
|
|
56
|
+
}
|
|
57
|
+
function projectResources(harness, version, destHome, fromHome) {
|
|
58
|
+
const cwd = process.cwd();
|
|
59
|
+
for (const kind of ALL_RESOURCE_KINDS) {
|
|
60
|
+
if (!supports(harness, kindToCapability(kind), version).ok)
|
|
61
|
+
continue;
|
|
62
|
+
const writer = getWriter(kind, harness);
|
|
63
|
+
if (!writer)
|
|
64
|
+
continue;
|
|
65
|
+
if (kind === 'rules') {
|
|
66
|
+
writer.write({ version, versionHome: destHome, selection: { preset: 'default' }, cwd });
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const names = getDetector(kind, harness)?.list({ version, versionHome: fromHome, cwd }) ?? [];
|
|
70
|
+
if (names.length === 0)
|
|
71
|
+
continue;
|
|
72
|
+
writer.write({ version, versionHome: destHome, selection: names, cwd });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create the HOME-shaped slot dir and project settings/resources from the
|
|
77
|
+
* managed install. Does not copy credentials (carryForwardSettings excludes
|
|
78
|
+
* them; resource writers write skills/hooks/commands, never OAuth files).
|
|
79
|
+
* Does not persist the slot record — call {@link recordSlot} after identity
|
|
80
|
+
* is captured.
|
|
81
|
+
*/
|
|
82
|
+
export function ensureSlot(harness, accountId) {
|
|
83
|
+
harnessAuth(harness);
|
|
84
|
+
const dir = slotDir(harness, accountId);
|
|
85
|
+
fs.mkdirSync(path.join(dir, agentConfigDirName(harness)), { recursive: true });
|
|
86
|
+
const version = sourceVersion(harness);
|
|
87
|
+
if (version) {
|
|
88
|
+
const fromHome = getVersionHomePath(harness, version);
|
|
89
|
+
if (fs.existsSync(fromHome)) {
|
|
90
|
+
carryForwardSettings(harness, fromHome, dir);
|
|
91
|
+
projectResources(harness, version, dir, fromHome);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
accountId,
|
|
96
|
+
slotDir: dir,
|
|
97
|
+
authMode: defaultAuthMode(harness),
|
|
98
|
+
verdict: 'unconfigured',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -236,6 +236,22 @@ export declare function supportsAccountInspection(agentId: AgentId): boolean;
|
|
|
236
236
|
export declare function accountDisplayLabel(info?: Pick<AccountInfo, 'email' | 'accountId' | 'signedIn' | 'organizationType' | 'organizationName'> | null): string;
|
|
237
237
|
/** Return the email address associated with the agent's auth config, or null. */
|
|
238
238
|
export declare function getAccountEmail(agentId: AgentId, home?: string): Promise<string | null>;
|
|
239
|
+
/**
|
|
240
|
+
* Extract full account information (identity, plan, usage status, credits) from
|
|
241
|
+
* the agent's local auth/config files. Supports Claude, Codex, and Gemini.
|
|
242
|
+
*/
|
|
243
|
+
/**
|
|
244
|
+
* Resolve a file-auth agent's credential file. Sign-in is account-global, but
|
|
245
|
+
* each installed version gets an isolated home; the credential physically lives
|
|
246
|
+
* only in the home the user logged in under (the one the `~/.<config>` symlink
|
|
247
|
+
* targets). Check the per-version `base` first, then fall back to the active
|
|
248
|
+
* config location under the real HOME so every installed version reflects the
|
|
249
|
+
* true account state (droid/antigravity/kimi all stored login per-version-home
|
|
250
|
+
* and showed non-active versions as "not signed in"). Returns the first
|
|
251
|
+
* existing path, or null.
|
|
252
|
+
*/
|
|
253
|
+
/** True when `dir` is under the per-account slot root (`~/.agents/.history/accounts/`). */
|
|
254
|
+
export declare function isAccountSlotDir(dir: string): boolean;
|
|
239
255
|
/** Where an agent's credential file lives, split into the per-version copy and
|
|
240
256
|
* the active/global copy under the real HOME. */
|
|
241
257
|
export interface CredentialPresence {
|
|
@@ -22,7 +22,8 @@ import { execFileShellSpec } from '../platform/index.js';
|
|
|
22
22
|
import { latestFileMtimeMs } from '../fs-walk.js';
|
|
23
23
|
import { damerauLevenshtein } from '../fuzzy.js';
|
|
24
24
|
import { probeCapture } from '../probe.js';
|
|
25
|
-
import { getCacheDir, getVersionsDir, getShimsDir, getHistoryDir, getCliVersionCachePath } from '../state.js';
|
|
25
|
+
import { getCacheDir, getVersionsDir, getShimsDir, getHistoryDir, getCliVersionCachePath, readMeta } from '../state.js';
|
|
26
|
+
import { registeredNativeAccountForEmail, parseNativeIdentityKey } from '../native-accounts.js';
|
|
26
27
|
import { resolveVersion, getVersionHomePath, getBinaryPath } from '../installations/versions.js';
|
|
27
28
|
import { supports } from '../capabilities.js';
|
|
28
29
|
import { MCP_TARGETS } from '../mcp-registry.js';
|
|
@@ -126,6 +127,12 @@ export function findInPath(command, options = {}) {
|
|
|
126
127
|
const stat = fs.statSync(full);
|
|
127
128
|
if (!stat.isFile())
|
|
128
129
|
continue;
|
|
130
|
+
// A shell only runs an executable file; `which` skips a mode-644 namesake and
|
|
131
|
+
// so must this, or a stray non-executable `secrets`/`claude` file earlier on
|
|
132
|
+
// PATH would shadow the real binary further down. On win32 the mode bit is
|
|
133
|
+
// meaningless (PATHEXT decides), so the check is POSIX-only.
|
|
134
|
+
if (process.platform !== 'win32')
|
|
135
|
+
fs.accessSync(full, fs.constants.X_OK);
|
|
129
136
|
const native = resolveNativeBinaryPath(command, full, options);
|
|
130
137
|
if (native)
|
|
131
138
|
return native;
|
|
@@ -1384,6 +1391,12 @@ export async function getAccountEmail(agentId, home) {
|
|
|
1384
1391
|
* and showed non-active versions as "not signed in"). Returns the first
|
|
1385
1392
|
* existing path, or null.
|
|
1386
1393
|
*/
|
|
1394
|
+
/** True when `dir` is under the per-account slot root (`~/.agents/.history/accounts/`). */
|
|
1395
|
+
export function isAccountSlotDir(dir) {
|
|
1396
|
+
const root = path.resolve(getHistoryDir(), 'accounts');
|
|
1397
|
+
const resolved = path.resolve(dir);
|
|
1398
|
+
return resolved === root || resolved.startsWith(root + path.sep);
|
|
1399
|
+
}
|
|
1387
1400
|
function resolveAccountCredentialPath(base, ...segments) {
|
|
1388
1401
|
const perVersion = path.join(base, ...segments);
|
|
1389
1402
|
try {
|
|
@@ -1391,6 +1404,10 @@ function resolveAccountCredentialPath(base, ...segments) {
|
|
|
1391
1404
|
return perVersion;
|
|
1392
1405
|
}
|
|
1393
1406
|
catch { /* unreadable */ }
|
|
1407
|
+
// A slot is the account's own HOME (PHNX-3940 T5). Never inherit another
|
|
1408
|
+
// account's adopted ~/.<config> — that is the cross-account leak.
|
|
1409
|
+
if (isAccountSlotDir(base))
|
|
1410
|
+
return null;
|
|
1394
1411
|
const active = path.join(process.env.AGENTS_REAL_HOME || os.homedir(), ...segments);
|
|
1395
1412
|
if (active !== perVersion) {
|
|
1396
1413
|
try {
|
|
@@ -1900,13 +1917,30 @@ export function readClaudeHomeConfig(base) {
|
|
|
1900
1917
|
const oa = config.oauthAccount;
|
|
1901
1918
|
if (!oa)
|
|
1902
1919
|
return null;
|
|
1903
|
-
const
|
|
1904
|
-
|
|
1920
|
+
const email = normalizeIdentityPart(oa.emailAddress);
|
|
1921
|
+
let accountId = normalizeIdentityPart(oa.accountUuid);
|
|
1922
|
+
let organizationId = normalizeIdentityPart(oa.organizationUuid);
|
|
1923
|
+
// Worker slot provisioned from a durable setup-token: only the email was
|
|
1924
|
+
// seeded on disk (seedClaudeWorkerHomeIdentity), so accountUuid/organizationUuid
|
|
1925
|
+
// are absent and both accountKey and usageKey would be null — the row reads
|
|
1926
|
+
// "not connected here" (PHNX-3940, sibling worker-identity diagnosis). Complete
|
|
1927
|
+
// the identity from the fleet-synced registry row for this email, keyed exactly
|
|
1928
|
+
// as a headed login's home carries it. Guarded on email-only so a headed home
|
|
1929
|
+
// (uuids on disk) never pays the readMeta() read, and fails CLOSED to email-only
|
|
1930
|
+
// when zero or several rows match (registeredNativeAccountForEmail never guesses).
|
|
1931
|
+
if (email && !accountId && !organizationId) {
|
|
1932
|
+
const registered = registeredNativeAccountForEmail(readMeta(), 'claude', email);
|
|
1933
|
+
const parts = registered ? parseNativeIdentityKey('claude', registered.identityKey) : null;
|
|
1934
|
+
if (parts) {
|
|
1935
|
+
accountId = normalizeIdentityPart(parts.account);
|
|
1936
|
+
organizationId = normalizeIdentityPart(parts.org);
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1905
1939
|
return {
|
|
1906
1940
|
path: activeFile,
|
|
1907
1941
|
config,
|
|
1908
1942
|
identity: {
|
|
1909
|
-
email: oa.emailAddress || null,
|
|
1943
|
+
email: email ?? (oa.emailAddress || null),
|
|
1910
1944
|
accountId,
|
|
1911
1945
|
organizationId,
|
|
1912
1946
|
organizationName: oa.organizationName ?? null,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Atomic, serialized install of a macOS `.app` bundle to a stable user path.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Used by the menu-bar helper (`lib/menubar/install-menubar.ts`), which is
|
|
5
|
+
* (re)installed on the hot path of ordinary `agents` invocations. (A second
|
|
6
|
+
* helper this module used to also serve, the secrets keychain broker, moved
|
|
7
|
+
* out of this repo entirely with the standalone `secrets` engine —
|
|
8
|
+
* PHNX-3989 — and installs itself now.) It previously did a non-atomic
|
|
8
9
|
* `rm -rf dest` + `cp -R src dest` straight onto the live bundle. That copy takes
|
|
9
10
|
* long enough that a concurrent reader (Gatekeeper, or an exec of the bundle) sees
|
|
10
11
|
* a half-written `.app` — a truncated Mach-O / mismatched `_CodeSignature` hash —
|
|
@@ -24,6 +24,8 @@ export interface AuthHealth {
|
|
|
24
24
|
detail?: string;
|
|
25
25
|
/** account label for display (email / id), when known. Never part of the key. */
|
|
26
26
|
account?: string;
|
|
27
|
+
/** Stable registered account id. Display labels are never used as identity. */
|
|
28
|
+
accountId?: string;
|
|
27
29
|
}
|
|
28
30
|
/** Maximum age of an auth verdict used for automatic routing decisions. */
|
|
29
31
|
export declare const AUTH_PROBE_MAX_AGE_MS: number;
|
|
@@ -173,6 +175,7 @@ export interface AuthProbeRow {
|
|
|
173
175
|
agent: AgentId;
|
|
174
176
|
version: string;
|
|
175
177
|
account?: string;
|
|
178
|
+
accountId?: string;
|
|
176
179
|
health: AuthHealth;
|
|
177
180
|
}
|
|
178
181
|
/** One installed (agent, version) home, tagged with its resolved account label. */
|
package/dist/lib/auth-health.js
CHANGED
|
@@ -255,6 +255,7 @@ export function readFleetAuthRows(host) {
|
|
|
255
255
|
agent: agent,
|
|
256
256
|
version: identity.slice(separator + 1),
|
|
257
257
|
account: health.account,
|
|
258
|
+
accountId: health.accountId,
|
|
258
259
|
health,
|
|
259
260
|
});
|
|
260
261
|
}
|
|
@@ -422,6 +423,14 @@ export async function probeLocalFleetAuth(opts) {
|
|
|
422
423
|
inst.info = await getAccountInfo(inst.agent, inst.home).catch(() => null);
|
|
423
424
|
inst.account = authAccountLabel(inst.info);
|
|
424
425
|
}));
|
|
426
|
+
const [{ readMeta }, { findNativeAccountByIdentity }] = await Promise.all([
|
|
427
|
+
import('./state.js'),
|
|
428
|
+
import('./account-registry.js'),
|
|
429
|
+
]);
|
|
430
|
+
const meta = readMeta();
|
|
431
|
+
for (const inst of installs) {
|
|
432
|
+
inst.accountId = findNativeAccountByIdentity(meta, inst.agent, inst.info)?.id;
|
|
433
|
+
}
|
|
425
434
|
// Probe once per (agent, account) — but only for the network-probing agents
|
|
426
435
|
// that can actually 429; best-effort agents stay per-install (see
|
|
427
436
|
// groupFleetAuthInstalls). Groups run in parallel: they target distinct
|
|
@@ -430,12 +439,14 @@ export async function probeLocalFleetAuth(opts) {
|
|
|
430
439
|
const rep = group.probe;
|
|
431
440
|
const health = await probeAuthHealth(rep.agent, rep.home, { cliVersion: opts?.cliVersion, info: rep.info, forceLive: opts?.forceLive, signal: opts?.signal });
|
|
432
441
|
health.account = authAccountLabel(rep.info);
|
|
442
|
+
health.accountId = rep.accountId;
|
|
433
443
|
if (health.verdict === 'unconfigured')
|
|
434
444
|
return [];
|
|
435
445
|
return group.members.map((inst) => ({
|
|
436
446
|
agent: inst.agent,
|
|
437
447
|
version: inst.version,
|
|
438
448
|
account: health.account,
|
|
449
|
+
accountId: inst.accountId ?? health.accountId,
|
|
439
450
|
// A distinct object per row so a later mutation of one can't bleed across.
|
|
440
451
|
health: { ...health },
|
|
441
452
|
}));
|
package/dist/lib/auth-mint.d.ts
CHANGED
|
@@ -15,25 +15,34 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { AgentId } from './types.js';
|
|
17
17
|
import { type CredentialAccount } from './account-registry.js';
|
|
18
|
+
import { type StorableCredentialKind } from './secrets-policy.js';
|
|
18
19
|
import { type DriveOptions, type PtyDriver } from './fleet/remote-login.js';
|
|
19
20
|
/** Well-formed Claude setup-token as it appears inside a TTY blob. */
|
|
20
21
|
export declare const CLAUDE_SETUP_TOKEN_CAPTURE_RE: RegExp;
|
|
21
22
|
export interface MintFlow {
|
|
22
23
|
harness: AgentId;
|
|
23
24
|
provider: string;
|
|
24
|
-
auth: 'setup-token';
|
|
25
|
-
/** Interactive mint argv after HOME=… <bin>. Null when stdin-seed only. */
|
|
25
|
+
auth: 'setup-token' | 'api-key';
|
|
26
|
+
/** Interactive mint argv after HOME=… <bin>. Null when stdin-seed only or an api-key collection flow. */
|
|
26
27
|
mintArgs: string[] | null;
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
/** api-key flows: the env var the collected key injects as on a worker (e.g. OPENAI_API_KEY). */
|
|
29
|
+
apiKeyEnv?: string;
|
|
30
|
+
/** setup-token flows: authorize-URL and token patterns scraped from the PTY. */
|
|
31
|
+
verificationUrlRegex?: RegExp;
|
|
32
|
+
tokenCapture?: RegExp;
|
|
29
33
|
}
|
|
30
34
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
+
* Durable-credential flows per harness. Only Claude exposes an interactive
|
|
36
|
+
* setup-token MINT; every other harness's durable credential is a provider API
|
|
37
|
+
* key, COLLECTED via `--api-key` or a prompt by `accounts add` (never derived
|
|
38
|
+
* from OAuth). The api-key entries describe collection only — they have no
|
|
39
|
+
* mint argv, and `mintAndSeed` refuses them with the `accounts add` pointer.
|
|
40
|
+
* Native device-code login (kimi/antigravity) stays on `agents fleet login`.
|
|
41
|
+
* The apiKeyEnv values are pinned to HARNESS_AUTH's `api-key:<ENV>` worker
|
|
42
|
+
* kinds by auth-mint.test.ts — keep them in lockstep.
|
|
35
43
|
*/
|
|
36
44
|
export declare const MINT_FLOWS: Record<string, MintFlow>;
|
|
45
|
+
/** Harnesses with an interactive setup-token mint (api-key flows are collection, not mint). */
|
|
37
46
|
export declare function listMintableHarnesses(): AgentId[];
|
|
38
47
|
export declare function getMintFlow(harnessRaw: string): MintFlow;
|
|
39
48
|
export declare function unmintableMessage(harness: string): string;
|
|
@@ -75,6 +84,32 @@ export declare function resolveMintIdentity(input: ResolveMintIdentityInput): Re
|
|
|
75
84
|
export declare function seedReservedAuthToken(email: string, token: string): {
|
|
76
85
|
key: string;
|
|
77
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* The env var a harness's durable worker credential injects as on a worker
|
|
89
|
+
* (claude's setup-token rides CLAUDE_CODE_OAUTH_TOKEN; api-key harnesses ride
|
|
90
|
+
* their MINT_FLOWS apiKeyEnv). Throws for a harness with no portable worker
|
|
91
|
+
* credential — kimi/antigravity log in per box.
|
|
92
|
+
*/
|
|
93
|
+
export declare function workerCredentialEnv(harness: AgentId): string;
|
|
94
|
+
/**
|
|
95
|
+
* The reserved-store key for one account's worker credential:
|
|
96
|
+
* `<ENV>_<accountId>` — keyed by account id, never by name or email, so a
|
|
97
|
+
* rename or an email change never breaks the key. Hyphens are stripped because
|
|
98
|
+
* the bundle key grammar (`BUNDLE_KEY_PATTERN`) is env-var-shaped.
|
|
99
|
+
*/
|
|
100
|
+
export declare function workerCredentialStoreKey(harness: AgentId, accountId: string): string;
|
|
101
|
+
/**
|
|
102
|
+
* Write (or rotate) one account's worker credential in the reserved
|
|
103
|
+
* `__<harness>__` store (PHNX-3940). Only non-rotating kinds may enter
|
|
104
|
+
* (`assertStorableCredentialKind` refuses a rotating OAuth/session credential at
|
|
105
|
+
* this boundary — RUSH-1958). FILE-backed, policy `never`, same contract as the
|
|
106
|
+
* legacy `auth` bundle: a keychain- or vault-backed store of this name fails
|
|
107
|
+
* loud, since worker provisioning reads the file backend.
|
|
108
|
+
*/
|
|
109
|
+
export declare function seedReservedStoreKey(harness: AgentId, kind: StorableCredentialKind, key: string, value: string): {
|
|
110
|
+
bundle: string;
|
|
111
|
+
key: string;
|
|
112
|
+
};
|
|
78
113
|
/**
|
|
79
114
|
* Create or rotate the named provider account that `agents run --account` and
|
|
80
115
|
* `agents accounts sync` consume. Existing account of a different kind fails
|
|
@@ -104,7 +139,7 @@ export interface DriveSetupTokenMintOpts extends MintDriveHooks {
|
|
|
104
139
|
* Tears the session down on the way out (success, timeout, or throw).
|
|
105
140
|
*/
|
|
106
141
|
export declare function driveSetupTokenMint(command: string, flow: MintFlow, opts?: DriveSetupTokenMintOpts): Promise<DriveMintResult>;
|
|
107
|
-
export declare function buildMintCommand(flow: MintFlow, bin: string, home: string): string;
|
|
142
|
+
export declare function buildMintCommand(flow: MintFlow, bin: string, home: string, env?: Record<string, string>): string;
|
|
108
143
|
export declare function resolveMintInstallation(harness: AgentId): {
|
|
109
144
|
version: string;
|
|
110
145
|
bin: string;
|
|
@@ -146,13 +181,14 @@ export declare function resolveSyncTargets(fleet: boolean, devices: string[]): P
|
|
|
146
181
|
/**
|
|
147
182
|
* True when a Claude setup-token is already seeded on this box (setup status).
|
|
148
183
|
*
|
|
149
|
-
* Read-only status probe (`agents setup`, `agents doctor`) — the
|
|
150
|
-
* auth-bundle check
|
|
151
|
-
*
|
|
152
|
-
* unavailable — PHNX-3385
|
|
153
|
-
*
|
|
154
|
-
* guards, not a diagnostic. An unreachable
|
|
155
|
-
*
|
|
184
|
+
* Read-only status probe (`agents setup`, `agents doctor`) — neither the
|
|
185
|
+
* account-registry read nor the reserved auth-bundle check MUST crash the whole
|
|
186
|
+
* command because the secret store could not be reached (the macOS Keychain
|
|
187
|
+
* helper source unavailable — PHNX-3385 — or the standalone `secrets` CLI
|
|
188
|
+
* missing / unspawnable); the client's calls fail loud by contract, but that
|
|
189
|
+
* contract is for destructive-write guards, not a diagnostic. An unreachable
|
|
190
|
+
* store here just means "cannot confirm," reported honestly rather than
|
|
191
|
+
* propagated.
|
|
156
192
|
*/
|
|
157
193
|
export declare function hasMintedSetupToken(): {
|
|
158
194
|
ready: boolean;
|