@phnx-labs/agents-cli 1.22.84 → 1.22.85
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 +57 -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/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 +32 -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 +28 -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 +15 -0
- package/dist/lib/installations/shims.js +69 -0
- 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 +191 -0
- package/dist/lib/secrets-client.js +710 -0
- package/dist/lib/secrets-policy.d.ts +222 -0
- package/dist/lib/secrets-policy.js +483 -0
- package/dist/lib/secrets-types.d.ts +188 -0
- package/dist/lib/secrets-types.js +24 -0
- 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 +4 -29
- 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
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The headless-context detector shared by every secrets read path that could
|
|
3
|
-
* raise a macOS Touch ID sheet: bundle resolution (bundles.ts) and raw item
|
|
4
|
-
* reads (index.ts). It lives in its own module so index.ts can use it without
|
|
5
|
-
* importing bundles.ts (which already imports index.ts).
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* True when the current process was structurally launched by an agent runtime
|
|
9
|
-
* that must NEVER raise a Keychain biometry prompt on the user's screen:
|
|
10
|
-
* - `AGENTS_RUNTIME` is `headless`, `teams`, or `terminal` — i.e. ANY agent
|
|
11
|
-
* launch, interactive included, and inherited by everything spawned beneath
|
|
12
|
-
* one (set on the child env by `agents run --headless`, scheduled routines,
|
|
13
|
-
* teammates, and interactive runs — see exec.ts:430, runner.ts,
|
|
14
|
-
* teams/agents.ts).
|
|
15
|
-
*
|
|
16
|
-
* Only **macOS keychain** reads pop an interactive Touch ID sheet — the secrets
|
|
17
|
-
* broker itself is a no-op off darwin (see agent.ts), and libsecret (Linux) /
|
|
18
|
-
* the Windows credential store resolve without any prompt. So off-darwin this
|
|
19
|
-
* ALWAYS returns false: forcing broker-only there would break every headless
|
|
20
|
-
* Linux/Windows read (CI, `agents run --headless`, routines, the Linux-driven
|
|
21
|
-
* release flow) for no benefit — there is no prompt to suppress.
|
|
22
|
-
*
|
|
23
|
-
* A read in a macOS headless context resolves broker-only (agentOnly) and fails
|
|
24
|
-
* fast with an actionable error instead of hijacking Touch ID. This generalizes
|
|
25
|
-
* the per-caller broker-only pattern used across the headless secrets readers.
|
|
26
|
-
*/
|
|
27
|
-
export declare function isHeadlessSecretsContext(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
|
|
28
|
-
/**
|
|
29
|
-
* True when this process runs structurally inside a coding-agent session: any
|
|
30
|
-
* `agents run` launch (AGENTS_RUNTIME / AGENT_SESSION_ID ride the child env —
|
|
31
|
-
* exec.ts buildExecEnv — and are inherited by every shell the agent spawns), or
|
|
32
|
-
* a harness's own tool shell launched outside agents-cli (Claude Code stamps
|
|
33
|
-
* CLAUDECODE on its Bash tool).
|
|
34
|
-
*
|
|
35
|
-
* Distinct from isHeadlessSecretsContext above on both axes: it is
|
|
36
|
-
* platform-independent (no biometry involved), and a TTY does not clear it (an
|
|
37
|
-
* agent inside tmux has one). Its job is the materialization boundary, not the
|
|
38
|
-
* prompt boundary: a command that would PRINT a plaintext value refuses under
|
|
39
|
-
* an agent, because anything printed lands in the agent's context and session
|
|
40
|
-
* transcript. The agent path to secret values is injection — `secrets exec`,
|
|
41
|
-
* `run --secrets` — which places them only in a child process env.
|
|
42
|
-
*/
|
|
43
|
-
export declare function isAgentInvocationContext(env?: NodeJS.ProcessEnv): boolean;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The headless-context detector shared by every secrets read path that could
|
|
3
|
-
* raise a macOS Touch ID sheet: bundle resolution (bundles.ts) and raw item
|
|
4
|
-
* reads (index.ts). It lives in its own module so index.ts can use it without
|
|
5
|
-
* importing bundles.ts (which already imports index.ts).
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* True when the current process was structurally launched by an agent runtime
|
|
9
|
-
* that must NEVER raise a Keychain biometry prompt on the user's screen:
|
|
10
|
-
* - `AGENTS_RUNTIME` is `headless`, `teams`, or `terminal` — i.e. ANY agent
|
|
11
|
-
* launch, interactive included, and inherited by everything spawned beneath
|
|
12
|
-
* one (set on the child env by `agents run --headless`, scheduled routines,
|
|
13
|
-
* teammates, and interactive runs — see exec.ts:430, runner.ts,
|
|
14
|
-
* teams/agents.ts).
|
|
15
|
-
*
|
|
16
|
-
* Only **macOS keychain** reads pop an interactive Touch ID sheet — the secrets
|
|
17
|
-
* broker itself is a no-op off darwin (see agent.ts), and libsecret (Linux) /
|
|
18
|
-
* the Windows credential store resolve without any prompt. So off-darwin this
|
|
19
|
-
* ALWAYS returns false: forcing broker-only there would break every headless
|
|
20
|
-
* Linux/Windows read (CI, `agents run --headless`, routines, the Linux-driven
|
|
21
|
-
* release flow) for no benefit — there is no prompt to suppress.
|
|
22
|
-
*
|
|
23
|
-
* A read in a macOS headless context resolves broker-only (agentOnly) and fails
|
|
24
|
-
* fast with an actionable error instead of hijacking Touch ID. This generalizes
|
|
25
|
-
* the per-caller broker-only pattern used across the headless secrets readers.
|
|
26
|
-
*/
|
|
27
|
-
export function isHeadlessSecretsContext(env = process.env, platform = process.platform) {
|
|
28
|
-
if (platform !== 'darwin')
|
|
29
|
-
return false; // no biometry prompt to suppress off-darwin
|
|
30
|
-
// Every agent-launch runtime resolves broker-only, interactive included.
|
|
31
|
-
const runtime = env.AGENTS_RUNTIME;
|
|
32
|
-
if (runtime === 'headless' || runtime === 'teams' || runtime === 'terminal')
|
|
33
|
-
return true;
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* True when this process runs structurally inside a coding-agent session: any
|
|
38
|
-
* `agents run` launch (AGENTS_RUNTIME / AGENT_SESSION_ID ride the child env —
|
|
39
|
-
* exec.ts buildExecEnv — and are inherited by every shell the agent spawns), or
|
|
40
|
-
* a harness's own tool shell launched outside agents-cli (Claude Code stamps
|
|
41
|
-
* CLAUDECODE on its Bash tool).
|
|
42
|
-
*
|
|
43
|
-
* Distinct from isHeadlessSecretsContext above on both axes: it is
|
|
44
|
-
* platform-independent (no biometry involved), and a TTY does not clear it (an
|
|
45
|
-
* agent inside tmux has one). Its job is the materialization boundary, not the
|
|
46
|
-
* prompt boundary: a command that would PRINT a plaintext value refuses under
|
|
47
|
-
* an agent, because anything printed lands in the agent's context and session
|
|
48
|
-
* transcript. The agent path to secret values is injection — `secrets exec`,
|
|
49
|
-
* `run --secrets` — which places them only in a child process env.
|
|
50
|
-
*/
|
|
51
|
-
export function isAgentInvocationContext(env = process.env) {
|
|
52
|
-
return Boolean(env.AGENTS_RUNTIME ||
|
|
53
|
-
env.AGENT_SESSION_ID ||
|
|
54
|
-
env.AGENTS_SESSION_ID ||
|
|
55
|
-
env.CLAUDECODE);
|
|
56
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recovery for LEGACY SYNCHRONIZABLE (iCloud Keychain) bundles.
|
|
3
|
-
*
|
|
4
|
-
* The pre-biometry helper era defaulted bundles to iCloud Keychain sync. The
|
|
5
|
-
* device-local cutover (biometry ACL + kSecAttrSynchronizable false on every
|
|
6
|
-
* query) orphaned those items: they still sync back via iCloud Keychain, but
|
|
7
|
-
* neither `secrets list` nor `migrate-acl` can see them. This module powers
|
|
8
|
-
* `agents secrets import --from icloud` — discover the orphaned bundles,
|
|
9
|
-
* re-import them as normal device-local bundles, and optionally purge the
|
|
10
|
-
* iCloud copies.
|
|
11
|
-
*
|
|
12
|
-
* The item-name scheme is the same one the modern store uses (see bundles.ts):
|
|
13
|
-
* metadata under `agents-cli.bundles.<name>`, one value per key under
|
|
14
|
-
* `agents-cli.secrets.<bundle>.<key>`. Legacy env keys never contained a dot,
|
|
15
|
-
* so metadata-less synced secret services split at their LAST dot to recover
|
|
16
|
-
* the bundle/key boundary even for dotted bundle names like `hetzner.com`.
|
|
17
|
-
*/
|
|
18
|
-
import { type SecretsBackend } from './bundles.js';
|
|
19
|
-
/** One orphaned iCloud bundle, as discovered from the synced item names. */
|
|
20
|
-
export interface SyncedBundleCandidate {
|
|
21
|
-
/** Bundle name derived from the iCloud service names. */
|
|
22
|
-
name: string;
|
|
23
|
-
/** Env keys that have a per-key secret item in the iCloud keychain. */
|
|
24
|
-
keys: string[];
|
|
25
|
-
/** True when an `agents-cli.bundles.<name>` metadata item exists in iCloud. */
|
|
26
|
-
hasMeta: boolean;
|
|
27
|
-
/** Every iCloud service name belonging to this candidate (the purge set). */
|
|
28
|
-
services: string[];
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Group raw synced service names into per-bundle candidates. Pure — separated
|
|
32
|
-
* from discovery so the parsing rules are unit-testable without a keychain.
|
|
33
|
-
*
|
|
34
|
-
* A bundle can surface as metadata only (`agents-cli.bundles.<name>`), as
|
|
35
|
-
* secret items only (`agents-cli.secrets.<bundle>.<KEY>` — metadata never
|
|
36
|
-
* synced), or both; all three shapes appear in real iCloud strays, so every
|
|
37
|
-
* one becomes a candidate.
|
|
38
|
-
*/
|
|
39
|
-
export declare function groupSyncedServices(services: string[]): SyncedBundleCandidate[];
|
|
40
|
-
/** Enumerate the iCloud keychain and return every orphaned bundle candidate. */
|
|
41
|
-
export declare function discoverSyncedBundles(): SyncedBundleCandidate[];
|
|
42
|
-
export interface ImportSyncedOptions {
|
|
43
|
-
/** Overwrite keys that already exist in the local bundle. */
|
|
44
|
-
force?: boolean;
|
|
45
|
-
/** Store imported values as literals in the bundle metadata (no keychain items). */
|
|
46
|
-
allPlaintext?: boolean;
|
|
47
|
-
/** Backend for a newly created bundle (existing bundles keep theirs). */
|
|
48
|
-
backend?: SecretsBackend;
|
|
49
|
-
/** Delete the iCloud copies of successfully-read items after import. */
|
|
50
|
-
purge?: boolean;
|
|
51
|
-
}
|
|
52
|
-
export interface ImportSyncedResult {
|
|
53
|
-
name: string;
|
|
54
|
-
added: number;
|
|
55
|
-
skipped: number;
|
|
56
|
-
/** Keys whose iCloud value could not be read (left in place, never purged). */
|
|
57
|
-
missing: string[];
|
|
58
|
-
/**
|
|
59
|
-
* Keys the modern store refuses by policy (reserved env names like USER,
|
|
60
|
-
* loader/interpreter vars like DYLD_*) — the pre-cutover store accepted
|
|
61
|
-
* them. Left in iCloud, never purged: the iCloud item is the only copy.
|
|
62
|
-
*/
|
|
63
|
-
unimportable: string[];
|
|
64
|
-
purged: number;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Import one discovered iCloud bundle into the local (device-local) store.
|
|
68
|
-
*
|
|
69
|
-
* Values come from the synced secret items; the synced metadata item, when
|
|
70
|
-
* present, contributes the description, literal vars, and non-keychain refs
|
|
71
|
-
* (env:/file:/exec: refs carry no stored secret, so copying the ref preserves
|
|
72
|
-
* them exactly). Existing local keys are skipped unless `force`. Keys the
|
|
73
|
-
* modern store refuses by policy (reserved / loader env names the pre-cutover
|
|
74
|
-
* store accepted) are reported as `unimportable` instead of aborting the whole
|
|
75
|
-
* bundle. With `purge`, only services whose value provably lives locally
|
|
76
|
-
* (imported now, or skipped-because-present) are deleted from iCloud — an
|
|
77
|
-
* unreadable or unimportable item is never destroyed.
|
|
78
|
-
*/
|
|
79
|
-
export declare function importSyncedBundle(candidate: SyncedBundleCandidate, opts?: ImportSyncedOptions): ImportSyncedResult;
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recovery for LEGACY SYNCHRONIZABLE (iCloud Keychain) bundles.
|
|
3
|
-
*
|
|
4
|
-
* The pre-biometry helper era defaulted bundles to iCloud Keychain sync. The
|
|
5
|
-
* device-local cutover (biometry ACL + kSecAttrSynchronizable false on every
|
|
6
|
-
* query) orphaned those items: they still sync back via iCloud Keychain, but
|
|
7
|
-
* neither `secrets list` nor `migrate-acl` can see them. This module powers
|
|
8
|
-
* `agents secrets import --from icloud` — discover the orphaned bundles,
|
|
9
|
-
* re-import them as normal device-local bundles, and optionally purge the
|
|
10
|
-
* iCloud copies.
|
|
11
|
-
*
|
|
12
|
-
* The item-name scheme is the same one the modern store uses (see bundles.ts):
|
|
13
|
-
* metadata under `agents-cli.bundles.<name>`, one value per key under
|
|
14
|
-
* `agents-cli.secrets.<bundle>.<key>`. Legacy env keys never contained a dot,
|
|
15
|
-
* so metadata-less synced secret services split at their LAST dot to recover
|
|
16
|
-
* the bundle/key boundary even for dotted bundle names like `hetzner.com`.
|
|
17
|
-
*/
|
|
18
|
-
import { deleteSyncedKeychainItem, getSyncedKeychainTokens, listSyncedKeychainItems, parseBundleValue, secretsKeychainItem, serializeRef, SECRETS_ITEM_PREFIX, } from './index.js';
|
|
19
|
-
import { BUNDLE_META_PREFIX, BUNDLE_NAME_PATTERN, BUNDLE_KEY_PATTERN, ENV_KEY_PATTERN, bundleKeyToEnvKey, bundleExists, bundleItemStore, bundlePolicy, isLoaderOrInterpreterEnv, isReservedEnvName, keychainRef, readBundle, writeBundle, } from './bundles.js';
|
|
20
|
-
/**
|
|
21
|
-
* Group raw synced service names into per-bundle candidates. Pure — separated
|
|
22
|
-
* from discovery so the parsing rules are unit-testable without a keychain.
|
|
23
|
-
*
|
|
24
|
-
* A bundle can surface as metadata only (`agents-cli.bundles.<name>`), as
|
|
25
|
-
* secret items only (`agents-cli.secrets.<bundle>.<KEY>` — metadata never
|
|
26
|
-
* synced), or both; all three shapes appear in real iCloud strays, so every
|
|
27
|
-
* one becomes a candidate.
|
|
28
|
-
*/
|
|
29
|
-
export function groupSyncedServices(services) {
|
|
30
|
-
const byName = new Map();
|
|
31
|
-
const claim = (name) => {
|
|
32
|
-
let c = byName.get(name);
|
|
33
|
-
if (!c) {
|
|
34
|
-
c = { name, keys: [], hasMeta: false, services: [] };
|
|
35
|
-
byName.set(name, c);
|
|
36
|
-
}
|
|
37
|
-
return c;
|
|
38
|
-
};
|
|
39
|
-
for (const svc of services) {
|
|
40
|
-
if (svc.startsWith(BUNDLE_META_PREFIX)) {
|
|
41
|
-
const name = svc.slice(BUNDLE_META_PREFIX.length);
|
|
42
|
-
if (!BUNDLE_NAME_PATTERN.test(name))
|
|
43
|
-
continue;
|
|
44
|
-
const c = claim(name);
|
|
45
|
-
c.hasMeta = true;
|
|
46
|
-
c.services.push(svc);
|
|
47
|
-
}
|
|
48
|
-
else if (svc.startsWith(SECRETS_ITEM_PREFIX)) {
|
|
49
|
-
const rest = svc.slice(SECRETS_ITEM_PREFIX.length);
|
|
50
|
-
const cut = rest.lastIndexOf('.');
|
|
51
|
-
if (cut <= 0)
|
|
52
|
-
continue;
|
|
53
|
-
const name = rest.slice(0, cut);
|
|
54
|
-
const key = rest.slice(cut + 1);
|
|
55
|
-
if (!BUNDLE_NAME_PATTERN.test(name) || !ENV_KEY_PATTERN.test(key))
|
|
56
|
-
continue;
|
|
57
|
-
const c = claim(name);
|
|
58
|
-
if (!c.keys.includes(key))
|
|
59
|
-
c.keys.push(key);
|
|
60
|
-
c.services.push(svc);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
64
|
-
}
|
|
65
|
-
/** Enumerate the iCloud keychain and return every orphaned bundle candidate. */
|
|
66
|
-
export function discoverSyncedBundles() {
|
|
67
|
-
return groupSyncedServices(listSyncedKeychainItems('agents-cli.'));
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Import one discovered iCloud bundle into the local (device-local) store.
|
|
71
|
-
*
|
|
72
|
-
* Values come from the synced secret items; the synced metadata item, when
|
|
73
|
-
* present, contributes the description, literal vars, and non-keychain refs
|
|
74
|
-
* (env:/file:/exec: refs carry no stored secret, so copying the ref preserves
|
|
75
|
-
* them exactly). Existing local keys are skipped unless `force`. Keys the
|
|
76
|
-
* modern store refuses by policy (reserved / loader env names the pre-cutover
|
|
77
|
-
* store accepted) are reported as `unimportable` instead of aborting the whole
|
|
78
|
-
* bundle. With `purge`, only services whose value provably lives locally
|
|
79
|
-
* (imported now, or skipped-because-present) are deleted from iCloud — an
|
|
80
|
-
* unreadable or unimportable item is never destroyed.
|
|
81
|
-
*/
|
|
82
|
-
export function importSyncedBundle(candidate, opts = {}) {
|
|
83
|
-
const values = getSyncedKeychainTokens(candidate.services);
|
|
84
|
-
let bundle;
|
|
85
|
-
if (bundleExists(candidate.name)) {
|
|
86
|
-
bundle = readBundle(candidate.name);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
bundle = {
|
|
90
|
-
name: candidate.name,
|
|
91
|
-
backend: opts.backend === 'keychain' ? undefined : opts.backend,
|
|
92
|
-
vars: {},
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
const metaService = BUNDLE_META_PREFIX + candidate.name;
|
|
96
|
-
let metaVars = {};
|
|
97
|
-
const metaJson = values.get(metaService);
|
|
98
|
-
if (metaJson !== undefined) {
|
|
99
|
-
try {
|
|
100
|
-
const parsed = JSON.parse(metaJson);
|
|
101
|
-
if (parsed && typeof parsed === 'object') {
|
|
102
|
-
if (typeof parsed.description === 'string' && !bundle.description) {
|
|
103
|
-
bundle.description = parsed.description;
|
|
104
|
-
}
|
|
105
|
-
if (parsed.vars && typeof parsed.vars === 'object') {
|
|
106
|
-
metaVars = parsed.vars;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
// Corrupt legacy metadata — the per-key secret items still import.
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
const store = bundleItemStore(bundle.backend, { noAcl: bundlePolicy(bundle) === 'never' });
|
|
115
|
-
let added = 0;
|
|
116
|
-
let skipped = 0;
|
|
117
|
-
const missing = [];
|
|
118
|
-
const unimportable = [];
|
|
119
|
-
// Services whose value now provably lives in the local store — imported this
|
|
120
|
-
// run, or skipped because the local bundle already carries the key. Only
|
|
121
|
-
// these may be purged; a missing or unimportable key's iCloud item is its
|
|
122
|
-
// only copy and must survive.
|
|
123
|
-
const purgeable = new Set();
|
|
124
|
-
const rejectedByPolicy = (key) => {
|
|
125
|
-
const envKey = bundleKeyToEnvKey(key);
|
|
126
|
-
return isLoaderOrInterpreterEnv(envKey) || isReservedEnvName(envKey);
|
|
127
|
-
};
|
|
128
|
-
// Keys with a synced secret item: re-store the value device-locally.
|
|
129
|
-
for (const key of candidate.keys) {
|
|
130
|
-
// The pre-cutover store accepted keys the modern one refuses (writeBundle
|
|
131
|
-
// throws) — skip them instead of aborting the whole bundle.
|
|
132
|
-
if (rejectedByPolicy(key)) {
|
|
133
|
-
unimportable.push(key);
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const value = values.get(secretsKeychainItem(candidate.name, key));
|
|
137
|
-
if (value === undefined) {
|
|
138
|
-
missing.push(key);
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
if (!opts.force && key in bundle.vars) {
|
|
142
|
-
skipped++;
|
|
143
|
-
purgeable.add(secretsKeychainItem(candidate.name, key));
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
if (opts.allPlaintext) {
|
|
147
|
-
bundle.vars[key] = { value };
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
store.set(secretsKeychainItem(candidate.name, key), value);
|
|
151
|
-
bundle.vars[key] = keychainRef(key);
|
|
152
|
-
}
|
|
153
|
-
purgeable.add(secretsKeychainItem(candidate.name, key));
|
|
154
|
-
added++;
|
|
155
|
-
}
|
|
156
|
-
// Vars declared only in the synced metadata: literals and non-keychain refs
|
|
157
|
-
// carry everything they need; a keychain ref without its synced item is
|
|
158
|
-
// unrecoverable.
|
|
159
|
-
for (const [key, raw] of Object.entries(metaVars)) {
|
|
160
|
-
if (!BUNDLE_KEY_PATTERN.test(key))
|
|
161
|
-
continue;
|
|
162
|
-
if (candidate.keys.includes(key))
|
|
163
|
-
continue; // the secret item already covered it
|
|
164
|
-
if (rejectedByPolicy(key)) {
|
|
165
|
-
if (!unimportable.includes(key))
|
|
166
|
-
unimportable.push(key);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
let parsed;
|
|
170
|
-
try {
|
|
171
|
-
parsed = parseBundleValue(raw);
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
continue; // malformed legacy entry
|
|
175
|
-
}
|
|
176
|
-
if ('ref' in parsed && parsed.ref.provider === 'keychain') {
|
|
177
|
-
if (!missing.includes(key))
|
|
178
|
-
missing.push(key);
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
if (!opts.force && key in bundle.vars) {
|
|
182
|
-
skipped++;
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
bundle.vars[key] = 'literal' in parsed ? { value: parsed.literal } : serializeRef(parsed.ref);
|
|
186
|
-
added++;
|
|
187
|
-
}
|
|
188
|
-
writeBundle(bundle);
|
|
189
|
-
// The metadata item is only a projection of what was just written locally —
|
|
190
|
-
// safe to purge once writeBundle has succeeded, UNLESS it still names keys
|
|
191
|
-
// that never made it over (missing/unimportable): then it stays as the only
|
|
192
|
-
// record of them.
|
|
193
|
-
if (metaJson !== undefined && missing.length === 0 && unimportable.length === 0) {
|
|
194
|
-
purgeable.add(metaService);
|
|
195
|
-
}
|
|
196
|
-
let purged = 0;
|
|
197
|
-
if (opts.purge) {
|
|
198
|
-
for (const svc of candidate.services) {
|
|
199
|
-
if (!purgeable.has(svc))
|
|
200
|
-
continue;
|
|
201
|
-
if (deleteSyncedKeychainItem(svc))
|
|
202
|
-
purged++;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return { name: candidate.name, added, skipped, missing, unimportable, purged };
|
|
206
|
-
}
|