@phnx-labs/agents-cli 1.22.83 → 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 +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/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/sessions-trace.d.ts +11 -0
- package/dist/commands/sessions-trace.js +71 -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-ticks.js +23 -2
- 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/feed/activity-stream.d.ts +60 -0
- package/dist/lib/feed/activity-stream.js +271 -0
- package/dist/lib/feed/activity.d.ts +7 -0
- package/dist/lib/feed/activity.js +10 -4
- package/dist/lib/feed/watch.d.ts +9 -3
- package/dist/lib/feed/watch.js +72 -36
- 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/fleet-shared-state.d.ts +6 -0
- 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/active.d.ts +38 -4
- package/dist/lib/session/active.js +36 -4
- package/dist/lib/session/bash-command.js +10 -0
- package/dist/lib/session/db.d.ts +65 -2
- package/dist/lib/session/db.js +178 -18
- package/dist/lib/session/mirror.d.ts +5 -0
- package/dist/lib/session/mirror.js +166 -0
- package/dist/lib/session/parse.d.ts +49 -0
- package/dist/lib/session/parse.js +324 -30
- package/dist/lib/session/prompt.d.ts +93 -2
- package/dist/lib/session/prompt.js +266 -15
- package/dist/lib/session/remote/peer-stream.d.ts +47 -0
- package/dist/lib/session/remote/peer-stream.js +142 -0
- package/dist/lib/session/remote/watch.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +41 -42
- package/dist/lib/session/session-cache.d.ts +9 -0
- package/dist/lib/session/session-cache.js +40 -2
- package/dist/lib/session/sync/config.js +2 -2
- package/dist/lib/session/timeline-pass.d.ts +129 -0
- package/dist/lib/session/timeline-pass.js +323 -0
- package/dist/lib/session/timeline.d.ts +182 -0
- package/dist/lib/session/timeline.js +636 -0
- package/dist/lib/session/types.d.ts +155 -1
- 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/summarizer/pass.d.ts +2 -0
- package/dist/lib/summarizer/pass.js +14 -1
- package/dist/lib/summarizer/summarize.d.ts +7 -0
- package/dist/lib/summarizer/summarize.js +3 -0
- 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 -170
- package/dist/lib/accounts/connect.js +0 -383
- 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,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stable install location for the signed macOS Keychain helper.
|
|
3
|
-
*
|
|
4
|
-
* Why a stable path: every npm publish re-signs `Agents CLI.app` with a fresh
|
|
5
|
-
* timestamp, producing a new code signature. macOS Keychain trusted-app ACLs
|
|
6
|
-
* are pinned to the exact signature, so the ACL invalidates on every release
|
|
7
|
-
* when the helper lives inside the npm package directory. Copying the .app
|
|
8
|
-
* once to `~/Library/Application Support/agents-cli/` gives it a path that
|
|
9
|
-
* survives `npm i -g`, `scripts/install.sh`, version bumps, etc.
|
|
10
|
-
*
|
|
11
|
-
* This module is the SINGLE SOURCE OF TRUTH for the helper path. Other
|
|
12
|
-
* modules in `src/lib/secrets/` must import `getKeychainHelperPath()` rather
|
|
13
|
-
* than recomputing it.
|
|
14
|
-
*/
|
|
15
|
-
/** Redirect the install root (test only). Returns the previous override so callers can restore. */
|
|
16
|
-
export declare function setInstallRootForTest(dir: string | null): string | null;
|
|
17
|
-
/**
|
|
18
|
-
* Idempotent install. Copies the bundled `.app` to the stable user path. Skips
|
|
19
|
-
* if the destination already exists, `codesign --verify` passes, AND the
|
|
20
|
-
* installed executable matches the bundled one byte-for-byte — a valid
|
|
21
|
-
* signature alone is not enough, because an outdated helper signs clean too.
|
|
22
|
-
* `forceReinstall=true` skips all checks and always copies.
|
|
23
|
-
*
|
|
24
|
-
* Notarization is checked via `spctl --assess` after install — a failure is
|
|
25
|
-
* logged as a warning but does NOT throw. Notarization checks require network
|
|
26
|
-
* access (Gatekeeper ticket lookup) and are not load-bearing for the helper's
|
|
27
|
-
* keychain ACL semantics.
|
|
28
|
-
*/
|
|
29
|
-
export declare function ensureKeychainHelperInstalled(opts?: {
|
|
30
|
-
forceReinstall?: boolean;
|
|
31
|
-
}): void;
|
|
32
|
-
/**
|
|
33
|
-
* ASYNC, download-capable install — the EXPLICIT onboarding/upgrade path
|
|
34
|
-
* (`agents setup secrets`, the bootstrap upgrade self-heal). When a bundled or
|
|
35
|
-
* locally-built `.app` ships (the common case, and always true this release
|
|
36
|
-
* stage), it installs that copy with NO network. When the tarball lacks the
|
|
37
|
-
* bundle (a fresh `npm i -g` whose tarball dropped it), it fetches the signed +
|
|
38
|
-
* notarized release asset for the running CLI version — sha256 + codesign +
|
|
39
|
-
* Team + notarization verified before install — and installs from the cached
|
|
40
|
-
* copy. The sync `getKeychainHelperPath()` never routes here: it stays sync +
|
|
41
|
-
* network-free and fails loud toward this command when no source exists.
|
|
42
|
-
*/
|
|
43
|
-
export declare function ensureKeychainHelperInstalledAsync(opts?: {
|
|
44
|
-
forceReinstall?: boolean;
|
|
45
|
-
}): Promise<void>;
|
|
46
|
-
/**
|
|
47
|
-
* Return the absolute path to the helper executable. If the installed bundle
|
|
48
|
-
* is missing, or is stale relative to the bundled source helper, performs a
|
|
49
|
-
* lazy (re)install first. The staleness check is what lets an upgraded CLI
|
|
50
|
-
* replace a helper a previous version installed — the install never runs on
|
|
51
|
-
* `npm i -g`, so this call site is the only one every machine is guaranteed to
|
|
52
|
-
* pass through.
|
|
53
|
-
*
|
|
54
|
-
* SYNC and network-free by design: this is the secrets hot path, called by
|
|
55
|
-
* every keychain op. It never downloads. When no bundled/local source exists
|
|
56
|
-
* (a tarball that dropped the bundle), the reinstall's `sourceAppPath()` throws
|
|
57
|
-
* loud, directing the user to `agents setup secrets` — the explicit, async,
|
|
58
|
-
* download-capable path (`ensureKeychainHelperInstalledAsync`). Throws on
|
|
59
|
-
* non-darwin.
|
|
60
|
-
*/
|
|
61
|
-
export declare function getKeychainHelperPath(): string;
|
|
62
|
-
/** Diagnostic snapshot of the installed Keychain helper. */
|
|
63
|
-
export interface KeychainHelperStatus {
|
|
64
|
-
source: string | null;
|
|
65
|
-
destination: string;
|
|
66
|
-
installed: boolean;
|
|
67
|
-
codesignOk: boolean;
|
|
68
|
-
codesignOutput: string;
|
|
69
|
-
spctlOk: boolean;
|
|
70
|
-
spctlOutput: string;
|
|
71
|
-
}
|
|
72
|
-
export declare function getKeychainHelperStatus(): KeychainHelperStatus;
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stable install location for the signed macOS Keychain helper.
|
|
3
|
-
*
|
|
4
|
-
* Why a stable path: every npm publish re-signs `Agents CLI.app` with a fresh
|
|
5
|
-
* timestamp, producing a new code signature. macOS Keychain trusted-app ACLs
|
|
6
|
-
* are pinned to the exact signature, so the ACL invalidates on every release
|
|
7
|
-
* when the helper lives inside the npm package directory. Copying the .app
|
|
8
|
-
* once to `~/Library/Application Support/agents-cli/` gives it a path that
|
|
9
|
-
* survives `npm i -g`, `scripts/install.sh`, version bumps, etc.
|
|
10
|
-
*
|
|
11
|
-
* This module is the SINGLE SOURCE OF TRUTH for the helper path. Other
|
|
12
|
-
* modules in `src/lib/secrets/` must import `getKeychainHelperPath()` rather
|
|
13
|
-
* than recomputing it.
|
|
14
|
-
*/
|
|
15
|
-
import { fileURLToPath } from 'url';
|
|
16
|
-
import { spawnSync } from 'child_process';
|
|
17
|
-
import { createHash } from 'crypto';
|
|
18
|
-
import * as fs from 'fs';
|
|
19
|
-
import * as os from 'os';
|
|
20
|
-
import * as path from 'path';
|
|
21
|
-
import { copyAppBundle, withInstallLock } from '../app-bundle-install.js';
|
|
22
|
-
import { downloadKeychainHelperApp } from './download-keychain.js';
|
|
23
|
-
import { helperFloor } from '../helper-versions.js';
|
|
24
|
-
const APP_BUNDLE_NAME = 'Agents CLI.app';
|
|
25
|
-
const INSTALL_DIR_NAME = 'agents-cli';
|
|
26
|
-
let installRootOverride = null;
|
|
27
|
-
/** Redirect the install root (test only). Returns the previous override so callers can restore. */
|
|
28
|
-
export function setInstallRootForTest(dir) {
|
|
29
|
-
const prev = installRootOverride;
|
|
30
|
-
installRootOverride = dir;
|
|
31
|
-
return prev;
|
|
32
|
-
}
|
|
33
|
-
/** Absolute path to the installed `.app` bundle directory (not the executable). */
|
|
34
|
-
function installedAppPath() {
|
|
35
|
-
const root = installRootOverride ?? os.homedir();
|
|
36
|
-
return path.join(root, 'Library', 'Application Support', INSTALL_DIR_NAME, APP_BUNDLE_NAME);
|
|
37
|
-
}
|
|
38
|
-
/** Absolute path to the executable inside the installed `.app` bundle. */
|
|
39
|
-
function installedExecutablePath() {
|
|
40
|
-
return path.join(installedAppPath(), 'Contents', 'MacOS', 'Agents CLI');
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Locate the source `.app` bundle shipped alongside the compiled JS.
|
|
44
|
-
*
|
|
45
|
-
* Resolution order:
|
|
46
|
-
* 1. dist/lib/secrets/Agents CLI.app — sibling of this compiled file (npm install layout)
|
|
47
|
-
* 2. <repo>/bin/Agents CLI.app — raw working tree (`bun run dev`, tsx from src/)
|
|
48
|
-
*
|
|
49
|
-
* Throws if neither exists.
|
|
50
|
-
*/
|
|
51
|
-
function sourceAppPath() {
|
|
52
|
-
const candidates = [];
|
|
53
|
-
try {
|
|
54
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
55
|
-
candidates.push(path.join(here, APP_BUNDLE_NAME));
|
|
56
|
-
// tsx/src case: src/lib/secrets/install-helper.ts -> ../../../bin/Agents CLI.app
|
|
57
|
-
candidates.push(path.resolve(here, '..', '..', '..', 'bin', APP_BUNDLE_NAME));
|
|
58
|
-
}
|
|
59
|
-
catch { /* import.meta.url unavailable */ }
|
|
60
|
-
for (const c of candidates) {
|
|
61
|
-
if (fs.existsSync(c))
|
|
62
|
-
return c;
|
|
63
|
-
}
|
|
64
|
-
throw new Error(`Source ${APP_BUNDLE_NAME} not found. Looked in:\n ${candidates.join('\n ')}\n` +
|
|
65
|
-
'The npm package shipped without the bundled keychain helper. Run `agents setup secrets` to ' +
|
|
66
|
-
'download the signed helper for this version, or reinstall agents-cli.');
|
|
67
|
-
}
|
|
68
|
-
/** The bundled/local source `.app`, or null when absent (no throw) — the async
|
|
69
|
-
* download path resolves the missing bundle instead of failing. */
|
|
70
|
-
function bundledSourceAppPathOrNull() {
|
|
71
|
-
try {
|
|
72
|
-
return sourceAppPath();
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function assertDarwin() {
|
|
79
|
-
if (process.platform !== 'darwin') {
|
|
80
|
-
throw new Error('Keychain helper is macOS only.');
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function sha256File(filePath) {
|
|
84
|
-
return createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* True when the installed helper executable differs byte-for-byte from the
|
|
88
|
-
* bundled source helper. A stale-but-validly-signed helper is exactly how the
|
|
89
|
-
* broken 1.20.4 build (signed, but missing the keychain-access-groups
|
|
90
|
-
* entitlement) survived upgrades: codesign --verify passes on it, so the
|
|
91
|
-
* "exists and verifies" early-return kept it installed forever. Returns false
|
|
92
|
-
* when there is no bundled source to compare against (dev installs) or no
|
|
93
|
-
* installed copy yet — both cases are decided by the existence checks in the
|
|
94
|
-
* callers, not by staleness.
|
|
95
|
-
*/
|
|
96
|
-
function installedHelperIsStale() {
|
|
97
|
-
let srcApp;
|
|
98
|
-
try {
|
|
99
|
-
srcApp = sourceAppPath();
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
const srcExec = path.join(srcApp, 'Contents', 'MacOS', 'Agents CLI');
|
|
105
|
-
const destExec = installedExecutablePath();
|
|
106
|
-
if (!fs.existsSync(srcExec) || !fs.existsSync(destExec))
|
|
107
|
-
return false;
|
|
108
|
-
return sha256File(srcExec) !== sha256File(destExec);
|
|
109
|
-
}
|
|
110
|
-
function codesignVerify(appPath) {
|
|
111
|
-
const r = spawnSync('codesign', ['--verify', '--deep', '--strict', appPath], {
|
|
112
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
113
|
-
encoding: 'utf-8',
|
|
114
|
-
});
|
|
115
|
-
return { ok: r.status === 0, output: (r.stderr || r.stdout || '').toString().trim() };
|
|
116
|
-
}
|
|
117
|
-
function spctlAssess(appPath) {
|
|
118
|
-
const r = spawnSync('spctl', ['--assess', '--type', 'execute', '--verbose=2', appPath], {
|
|
119
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
120
|
-
encoding: 'utf-8',
|
|
121
|
-
});
|
|
122
|
-
return { ok: r.status === 0, output: (r.stderr || r.stdout || '').toString().trim() };
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Idempotent install. Copies the bundled `.app` to the stable user path. Skips
|
|
126
|
-
* if the destination already exists, `codesign --verify` passes, AND the
|
|
127
|
-
* installed executable matches the bundled one byte-for-byte — a valid
|
|
128
|
-
* signature alone is not enough, because an outdated helper signs clean too.
|
|
129
|
-
* `forceReinstall=true` skips all checks and always copies.
|
|
130
|
-
*
|
|
131
|
-
* Notarization is checked via `spctl --assess` after install — a failure is
|
|
132
|
-
* logged as a warning but does NOT throw. Notarization checks require network
|
|
133
|
-
* access (Gatekeeper ticket lookup) and are not load-bearing for the helper's
|
|
134
|
-
* keychain ACL semantics.
|
|
135
|
-
*/
|
|
136
|
-
export function ensureKeychainHelperInstalled(opts = {}) {
|
|
137
|
-
assertDarwin();
|
|
138
|
-
installKeychainHelperFromSource(sourceAppPath(), opts);
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* ASYNC, download-capable install — the EXPLICIT onboarding/upgrade path
|
|
142
|
-
* (`agents setup secrets`, the bootstrap upgrade self-heal). When a bundled or
|
|
143
|
-
* locally-built `.app` ships (the common case, and always true this release
|
|
144
|
-
* stage), it installs that copy with NO network. When the tarball lacks the
|
|
145
|
-
* bundle (a fresh `npm i -g` whose tarball dropped it), it fetches the signed +
|
|
146
|
-
* notarized release asset for the running CLI version — sha256 + codesign +
|
|
147
|
-
* Team + notarization verified before install — and installs from the cached
|
|
148
|
-
* copy. The sync `getKeychainHelperPath()` never routes here: it stays sync +
|
|
149
|
-
* network-free and fails loud toward this command when no source exists.
|
|
150
|
-
*/
|
|
151
|
-
export async function ensureKeychainHelperInstalledAsync(opts = {}) {
|
|
152
|
-
assertDarwin();
|
|
153
|
-
const bundled = bundledSourceAppPathOrNull();
|
|
154
|
-
const src = bundled ?? (await downloadKeychainHelperApp(helperFloor('keychain')));
|
|
155
|
-
installKeychainHelperFromSource(src, opts);
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Copy `src` (a verified `.app` bundle) to the stable user path, then verify.
|
|
159
|
-
* Shared by the sync (bundled-source) and async (download-capable) installers.
|
|
160
|
-
*/
|
|
161
|
-
function installKeychainHelperFromSource(src, opts = {}) {
|
|
162
|
-
const dest = installedAppPath();
|
|
163
|
-
const upToDate = () => fs.existsSync(dest) && codesignVerify(dest).ok && !installedHelperIsStale();
|
|
164
|
-
// Fast path: already installed, valid, and current — no lock, no copy.
|
|
165
|
-
if (!opts.forceReinstall && upToDate())
|
|
166
|
-
return;
|
|
167
|
-
// Serialize the install so concurrent `agents` invocations don't race the
|
|
168
|
-
// atomic swap (and don't each run a redundant `cp`). The re-check inside the
|
|
169
|
-
// lock means a burst of callers copies once, not N times — the stampede that
|
|
170
|
-
// transiently corrupted the bundle and tripped the "damaged" dialog.
|
|
171
|
-
withInstallLock(dest, (heartbeat) => {
|
|
172
|
-
if (!opts.forceReinstall && upToDate())
|
|
173
|
-
return;
|
|
174
|
-
copyAppBundle(src, dest);
|
|
175
|
-
heartbeat(); // cp -R done; keep the lock fresh across the codesign/spctl spawns
|
|
176
|
-
const verify = codesignVerify(dest);
|
|
177
|
-
if (!verify.ok) {
|
|
178
|
-
throw new Error(`Installed helper failed codesign verification at ${dest}.\n${verify.output}\n` +
|
|
179
|
-
'The bundle may be corrupted. Reinstall agents-cli to restore it; the helper reinstalls automatically on the next secrets use.');
|
|
180
|
-
}
|
|
181
|
-
heartbeat(); // codesign done; spctl does a network Gatekeeper lookup — refresh again
|
|
182
|
-
const assess = spctlAssess(dest);
|
|
183
|
-
if (!assess.ok) {
|
|
184
|
-
// Warn, do not fail. Gatekeeper ticket lookup needs network; offline
|
|
185
|
-
// installs and CI runners commonly fail this check. The ACL semantics
|
|
186
|
-
// we care about depend on codesign, not spctl.
|
|
187
|
-
process.stderr.write(`agents-cli: notarization check (spctl) did not pass for ${dest}: ${assess.output}\n`);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Return the absolute path to the helper executable. If the installed bundle
|
|
193
|
-
* is missing, or is stale relative to the bundled source helper, performs a
|
|
194
|
-
* lazy (re)install first. The staleness check is what lets an upgraded CLI
|
|
195
|
-
* replace a helper a previous version installed — the install never runs on
|
|
196
|
-
* `npm i -g`, so this call site is the only one every machine is guaranteed to
|
|
197
|
-
* pass through.
|
|
198
|
-
*
|
|
199
|
-
* SYNC and network-free by design: this is the secrets hot path, called by
|
|
200
|
-
* every keychain op. It never downloads. When no bundled/local source exists
|
|
201
|
-
* (a tarball that dropped the bundle), the reinstall's `sourceAppPath()` throws
|
|
202
|
-
* loud, directing the user to `agents setup secrets` — the explicit, async,
|
|
203
|
-
* download-capable path (`ensureKeychainHelperInstalledAsync`). Throws on
|
|
204
|
-
* non-darwin.
|
|
205
|
-
*/
|
|
206
|
-
export function getKeychainHelperPath() {
|
|
207
|
-
assertDarwin();
|
|
208
|
-
const exec = installedExecutablePath();
|
|
209
|
-
if (!fs.existsSync(exec) || installedHelperIsStale()) {
|
|
210
|
-
ensureKeychainHelperInstalled();
|
|
211
|
-
}
|
|
212
|
-
return exec;
|
|
213
|
-
}
|
|
214
|
-
export function getKeychainHelperStatus() {
|
|
215
|
-
assertDarwin();
|
|
216
|
-
const destApp = installedAppPath();
|
|
217
|
-
let src = null;
|
|
218
|
-
try {
|
|
219
|
-
src = sourceAppPath();
|
|
220
|
-
}
|
|
221
|
-
catch { /* missing source is reported as null */ }
|
|
222
|
-
const installed = fs.existsSync(destApp);
|
|
223
|
-
if (!installed) {
|
|
224
|
-
return {
|
|
225
|
-
source: src,
|
|
226
|
-
destination: destApp,
|
|
227
|
-
installed: false,
|
|
228
|
-
codesignOk: false,
|
|
229
|
-
codesignOutput: 'not installed',
|
|
230
|
-
spctlOk: false,
|
|
231
|
-
spctlOutput: 'not installed',
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
const cs = codesignVerify(destApp);
|
|
235
|
-
const sp = spctlAssess(destApp);
|
|
236
|
-
return {
|
|
237
|
-
source: src,
|
|
238
|
-
destination: destApp,
|
|
239
|
-
installed: true,
|
|
240
|
-
codesignOk: cs.ok,
|
|
241
|
-
codesignOutput: cs.output || 'ok',
|
|
242
|
-
spctlOk: sp.ok,
|
|
243
|
-
spctlOutput: sp.output || 'ok',
|
|
244
|
-
};
|
|
245
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export declare const MIN_LEASE_MS: number;
|
|
2
|
-
export declare const MAX_LEASE_MS: number;
|
|
3
|
-
export interface SecretLease {
|
|
4
|
-
id: string;
|
|
5
|
-
bundle: string;
|
|
6
|
-
keys: string[];
|
|
7
|
-
createdAt: number;
|
|
8
|
-
expiresAt: number;
|
|
9
|
-
harness: string;
|
|
10
|
-
sleepPersist: boolean;
|
|
11
|
-
}
|
|
12
|
-
export declare function clampLeaseTtlMs(ttlMs: number): number;
|
|
13
|
-
export declare function normalizeLeaseKeys(keys: readonly string[], availableKeys: readonly string[]): string[];
|
|
14
|
-
export declare function createSecretLease(input: {
|
|
15
|
-
bundle: string;
|
|
16
|
-
keys: readonly string[];
|
|
17
|
-
availableKeys: readonly string[];
|
|
18
|
-
ttlMs: number;
|
|
19
|
-
now?: number;
|
|
20
|
-
id?: string;
|
|
21
|
-
harness?: string;
|
|
22
|
-
sleepPersist?: boolean;
|
|
23
|
-
}): SecretLease;
|
|
24
|
-
export declare function leaseIsActive(lease: SecretLease, now?: number): boolean;
|
|
25
|
-
export declare function selectLeasedEnv(lease: SecretLease, env: Record<string, string>, now?: number): Record<string, string>;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { randomBytes } from 'crypto';
|
|
2
|
-
import { GLOBAL_HARNESS } from './scope.js';
|
|
3
|
-
export const MIN_LEASE_MS = 60 * 1000;
|
|
4
|
-
export const MAX_LEASE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
5
|
-
export function clampLeaseTtlMs(ttlMs) {
|
|
6
|
-
if (!Number.isFinite(ttlMs) || ttlMs <= 0) {
|
|
7
|
-
throw new Error('Lease duration must be a positive finite number.');
|
|
8
|
-
}
|
|
9
|
-
return Math.min(Math.max(Math.floor(ttlMs), MIN_LEASE_MS), MAX_LEASE_MS);
|
|
10
|
-
}
|
|
11
|
-
export function normalizeLeaseKeys(keys, availableKeys) {
|
|
12
|
-
const requested = [...new Set(keys.map((key) => key.trim()).filter(Boolean))].sort();
|
|
13
|
-
if (requested.length === 0)
|
|
14
|
-
throw new Error('A secret lease requires at least one key.');
|
|
15
|
-
const available = new Set(availableKeys);
|
|
16
|
-
const missing = requested.filter((key) => !available.has(key));
|
|
17
|
-
if (missing.length > 0)
|
|
18
|
-
throw new Error(`Unknown secret lease key(s): ${missing.join(', ')}`);
|
|
19
|
-
return requested;
|
|
20
|
-
}
|
|
21
|
-
export function createSecretLease(input) {
|
|
22
|
-
const createdAt = input.now ?? Date.now();
|
|
23
|
-
const ttlMs = clampLeaseTtlMs(input.ttlMs);
|
|
24
|
-
return {
|
|
25
|
-
id: input.id ?? randomBytes(12).toString('hex'),
|
|
26
|
-
bundle: input.bundle,
|
|
27
|
-
keys: normalizeLeaseKeys(input.keys, input.availableKeys),
|
|
28
|
-
createdAt,
|
|
29
|
-
expiresAt: createdAt + ttlMs,
|
|
30
|
-
harness: input.harness ?? GLOBAL_HARNESS,
|
|
31
|
-
sleepPersist: input.sleepPersist ?? false,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export function leaseIsActive(lease, now = Date.now()) {
|
|
35
|
-
return now < lease.expiresAt;
|
|
36
|
-
}
|
|
37
|
-
export function selectLeasedEnv(lease, env, now = Date.now()) {
|
|
38
|
-
if (!leaseIsActive(lease, now))
|
|
39
|
-
throw new Error(`Secret lease '${lease.id}' has expired.`);
|
|
40
|
-
const missing = lease.keys.filter((key) => typeof env[key] !== 'string');
|
|
41
|
-
if (missing.length > 0)
|
|
42
|
-
throw new Error(`Secret lease '${lease.id}' is missing value(s): ${missing.join(', ')}`);
|
|
43
|
-
return Object.fromEntries(lease.keys.map((key) => [key, env[key]]));
|
|
44
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Linux secret storage via libsecret (GNOME Keyring / Secret Service API).
|
|
3
|
-
*
|
|
4
|
-
* Primary backend: `secret-tool` CLI (libsecret-tools package).
|
|
5
|
-
*
|
|
6
|
-
* Headless fallback: when the default Secret Service collection is locked
|
|
7
|
-
* (common on server-class Linux — no graphical login means the keyring
|
|
8
|
-
* passphrase never enters the daemon, so `secret-tool store` fails with
|
|
9
|
-
* "Cannot create an item in a locked collection"), we transparently switch
|
|
10
|
-
* to the AES-256-GCM encrypted-file store in ./filestore.ts. The decision is
|
|
11
|
-
* cached per process; one stderr line is emitted the first time the fallback
|
|
12
|
-
* activates.
|
|
13
|
-
*
|
|
14
|
-
* Secrets stored via secret-tool use:
|
|
15
|
-
* service = "agents-cli"
|
|
16
|
-
* account = username
|
|
17
|
-
* item = the secret identifier
|
|
18
|
-
*/
|
|
19
|
-
import type { KeychainBackend } from './index.js';
|
|
20
|
-
import { type NativeImportReport } from './fallback.js';
|
|
21
|
-
export { encryptForFallback, decryptForFallback, fileBackend, type EncFile, } from './filestore.js';
|
|
22
|
-
/**
|
|
23
|
-
* True when secret operations currently route to the encrypted-file store
|
|
24
|
-
* instead of the Secret Service (the headless / locked-collection fallback).
|
|
25
|
-
*
|
|
26
|
-
* Runs the same `preflight()` decision every read and write uses, so it can't
|
|
27
|
-
* drift from where bytes actually land. `preflight()` throws only in the
|
|
28
|
-
* interactive / no-secret-tool / no-passphrase case — where nothing is stored
|
|
29
|
-
* — so treat that as "not on the file path".
|
|
30
|
-
*
|
|
31
|
-
* `listBundles()` needs this: under the fallback the keychain enumeration
|
|
32
|
-
* (`linuxBackend.list`) and the file enumeration read the SAME store, so
|
|
33
|
-
* without this signal every file-backed bundle would be listed twice.
|
|
34
|
-
*/
|
|
35
|
-
export declare function usesFileFallback(): boolean;
|
|
36
|
-
/** secret-tool lookup attributes:
|
|
37
|
-
* service=agents-cli account=<user> item=<itemName> */
|
|
38
|
-
export declare function hasSecretToolToken(item: string): boolean;
|
|
39
|
-
export declare function getSecretToolToken(item: string): string;
|
|
40
|
-
export declare function setSecretToolToken(item: string, value: string): void;
|
|
41
|
-
export declare function deleteSecretToolToken(item: string): boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Parse the item names out of `secret-tool search --all` output, keeping only
|
|
44
|
-
* those starting with `prefix`. Exported for tests.
|
|
45
|
-
*
|
|
46
|
-
* `output` must be the combined stdout+stderr of the search: libsecret splits
|
|
47
|
-
* the dump across both streams — the value/label/schema lines go to stdout
|
|
48
|
-
* while the `attribute.*` lines (which carry `attribute.item`, the only place
|
|
49
|
-
* the item name is reliably machine-readable) go to stderr (observed on
|
|
50
|
-
* libsecret 0.21.4). Which stream each line lands on has varied across
|
|
51
|
-
* libsecret versions, so callers concatenate both rather than bet on one.
|
|
52
|
-
*/
|
|
53
|
-
export declare function parseSecretToolItems(output: string, prefix: string): string[];
|
|
54
|
-
/**
|
|
55
|
-
* List secrets by prefix. secret-tool doesn't have a list command,
|
|
56
|
-
* so we use secret-tool search which outputs in a specific format.
|
|
57
|
-
*/
|
|
58
|
-
export declare function listSecretToolItems(prefix: string): string[];
|
|
59
|
-
/**
|
|
60
|
-
* Copy agents-cli items from the keyring into the file store (the `import-keyring`
|
|
61
|
-
* backend for Linux). Requires an unlocked keyring; items already in the file
|
|
62
|
-
* store are left untouched. With `commit=false` it reports what it *would* do.
|
|
63
|
-
*/
|
|
64
|
-
export declare function importNativeSecretToolItems(prefix: string, commit: boolean): NativeImportReport;
|
|
65
|
-
/** KeychainBackend implementation for Linux. Routes through secret-tool
|
|
66
|
-
* with a transparent encrypted-file fallback when the default Secret
|
|
67
|
-
* Service collection is locked (or libsecret-tools is not installed but
|
|
68
|
-
* AGENTS_SECRETS_PASSPHRASE is set). */
|
|
69
|
-
export declare const linuxBackend: KeychainBackend;
|
|
70
|
-
/** Test-only: reset module state so independent test cases don't bleed
|
|
71
|
-
* passphrase / fallback decisions across each other. File-store state (file
|
|
72
|
-
* dir + cached passphrase) lives in ./filestore.ts and is reset there. */
|
|
73
|
-
export declare function _resetForTest(opts?: {
|
|
74
|
-
fileDir?: string | null;
|
|
75
|
-
forceFileFallback?: boolean;
|
|
76
|
-
passphrase?: string | null;
|
|
77
|
-
}): void;
|