@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
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* On-demand download + verification of the macOS keychain broker helper
|
|
3
|
-
* ("Agents CLI.app").
|
|
4
|
-
*
|
|
5
|
-
* Mirrors the ComputerHelper / MenubarHelper download model
|
|
6
|
-
* (`../helper-download.ts`): the helper ships as a signed + notarized `.app`
|
|
7
|
-
* zipped as a GitHub release asset on the helper's own `keychain/v<x.y.z>` tag,
|
|
8
|
-
* NOT the CLI's tag (see `helper-versions.ts`). A fresh `npm i -g`
|
|
9
|
-
* machine whose tarball lacks a bundled copy fetches the asset for the running
|
|
10
|
-
* CLI version, verifies its sha256 + code signature before install.
|
|
11
|
-
*
|
|
12
|
-
* Unlike the menu-bar helper, the keychain helper pins NO designated
|
|
13
|
-
* requirement (no `expectedBundleId`). The menu-bar `expectedBundleId` exists
|
|
14
|
-
* because macOS keys the Accessibility (TCC) grant to the bundle's designated
|
|
15
|
-
* requirement, so a substituted bundle whose DR does not pin (id, team) would
|
|
16
|
-
* silently revoke that upgrade-stable grant. The keychain helper has no such
|
|
17
|
-
* DR-keyed grant to protect: its items are gated by the access-group
|
|
18
|
-
* entitlement (`keychain-helper.swift` → `kSecAttrAccessGroup`, the
|
|
19
|
-
* `keychain-access-groups` entitlement) plus a biometry `SecAccessControl`
|
|
20
|
-
* (user presence) — NOT a `SecTrustedApplication` ACL keyed to a bundle-id/DR.
|
|
21
|
-
* The release-time gate `scripts/verify-keychain-helper.sh` correspondingly
|
|
22
|
-
* pins the executable's sha256, not a designated requirement. So Team +
|
|
23
|
-
* notarization is the verification boundary here, exactly like ComputerHelper.
|
|
24
|
-
*/
|
|
25
|
-
import { type HelperSpec } from '../helper-download.js';
|
|
26
|
-
/** The zipped `.app` release asset name (the bundle name has a space). */
|
|
27
|
-
export declare const KEYCHAIN_HELPER_ASSET = "Agents_CLI.app.zip";
|
|
28
|
-
/** The bundle directory name once extracted. */
|
|
29
|
-
export declare const KEYCHAIN_HELPER_APP_NAME = "Agents CLI.app";
|
|
30
|
-
/** Keychain-helper identity + verification policy — Team + notarization only,
|
|
31
|
-
* no DR pin (see docblock). */
|
|
32
|
-
export declare const KEYCHAIN_HELPER_SPEC: HelperSpec;
|
|
33
|
-
/** Release-asset URLs for the keychain helper zip + its checksum at `v<version>`. */
|
|
34
|
-
export declare function keychainHelperAssetUrls(version: string): {
|
|
35
|
-
zip: string;
|
|
36
|
-
sha256: string;
|
|
37
|
-
};
|
|
38
|
-
/** Cache dir for the downloaded keychain helper, one subdir per release tag. */
|
|
39
|
-
export declare function keychainHelperCacheDir(version: string): string;
|
|
40
|
-
/** Verify a keychain helper `.app`: codesign + Team + notarization (no DR pin). */
|
|
41
|
-
export declare function verifyKeychainHelper(appPath: string): void;
|
|
42
|
-
/**
|
|
43
|
-
* Download the keychain helper release asset for `version`, verify sha256 +
|
|
44
|
-
* signature, and return the path to the extracted `Agents CLI.app` in the
|
|
45
|
-
* cache. A missing asset is a hard error naming the exact tag.
|
|
46
|
-
*/
|
|
47
|
-
export declare function downloadKeychainHelperApp(version: string): Promise<string>;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* On-demand download + verification of the macOS keychain broker helper
|
|
3
|
-
* ("Agents CLI.app").
|
|
4
|
-
*
|
|
5
|
-
* Mirrors the ComputerHelper / MenubarHelper download model
|
|
6
|
-
* (`../helper-download.ts`): the helper ships as a signed + notarized `.app`
|
|
7
|
-
* zipped as a GitHub release asset on the helper's own `keychain/v<x.y.z>` tag,
|
|
8
|
-
* NOT the CLI's tag (see `helper-versions.ts`). A fresh `npm i -g`
|
|
9
|
-
* machine whose tarball lacks a bundled copy fetches the asset for the running
|
|
10
|
-
* CLI version, verifies its sha256 + code signature before install.
|
|
11
|
-
*
|
|
12
|
-
* Unlike the menu-bar helper, the keychain helper pins NO designated
|
|
13
|
-
* requirement (no `expectedBundleId`). The menu-bar `expectedBundleId` exists
|
|
14
|
-
* because macOS keys the Accessibility (TCC) grant to the bundle's designated
|
|
15
|
-
* requirement, so a substituted bundle whose DR does not pin (id, team) would
|
|
16
|
-
* silently revoke that upgrade-stable grant. The keychain helper has no such
|
|
17
|
-
* DR-keyed grant to protect: its items are gated by the access-group
|
|
18
|
-
* entitlement (`keychain-helper.swift` → `kSecAttrAccessGroup`, the
|
|
19
|
-
* `keychain-access-groups` entitlement) plus a biometry `SecAccessControl`
|
|
20
|
-
* (user presence) — NOT a `SecTrustedApplication` ACL keyed to a bundle-id/DR.
|
|
21
|
-
* The release-time gate `scripts/verify-keychain-helper.sh` correspondingly
|
|
22
|
-
* pins the executable's sha256, not a designated requirement. So Team +
|
|
23
|
-
* notarization is the verification boundary here, exactly like ComputerHelper.
|
|
24
|
-
*/
|
|
25
|
-
import { EXPECTED_TEAM_ID, downloadHelperApp, helperAssetUrls, helperCacheDir, verifyHelperApp, } from '../helper-download.js';
|
|
26
|
-
/** The zipped `.app` release asset name (the bundle name has a space). */
|
|
27
|
-
// Underscore, and NOT a space. GitHub rewrites a space in a release-asset name
|
|
28
|
-
// to a dot on upload, so an asset staged as `Agents CLI.app.zip` is SERVED as
|
|
29
|
-
// `Agents.CLI.app.zip` and a client asking for the spaced name 404s forever --
|
|
30
|
-
// which is exactly what shipped. The fix is to CHOOSE a name GitHub leaves
|
|
31
|
-
// alone rather than to mirror its rewrite: `Agents.CLI.app.zip` would work by
|
|
32
|
-
// matching an artifact of that behavior, and would silently break if the
|
|
33
|
-
// normalization rule ever changed. Underscores are preserved verbatim (verified
|
|
34
|
-
// against a real upload).
|
|
35
|
-
//
|
|
36
|
-
// The bundle DIRECTORY inside the zip keeps its space (`appName` below) -- that
|
|
37
|
-
// is the on-disk bundle name macOS and the TCC grant key on, and it is unchanged.
|
|
38
|
-
export const KEYCHAIN_HELPER_ASSET = 'Agents_CLI.app.zip';
|
|
39
|
-
/** The bundle directory name once extracted. */
|
|
40
|
-
export const KEYCHAIN_HELPER_APP_NAME = 'Agents CLI.app';
|
|
41
|
-
/** Keychain-helper identity + verification policy — Team + notarization only,
|
|
42
|
-
* no DR pin (see docblock). */
|
|
43
|
-
export const KEYCHAIN_HELPER_SPEC = {
|
|
44
|
-
helper: 'keychain',
|
|
45
|
-
assetName: KEYCHAIN_HELPER_ASSET,
|
|
46
|
-
appName: KEYCHAIN_HELPER_APP_NAME,
|
|
47
|
-
cacheSubdir: ['secrets', 'mac-helper'],
|
|
48
|
-
expectedTeamId: EXPECTED_TEAM_ID,
|
|
49
|
-
localBuildHint: 'cli/scripts/build-keychain-helper.sh',
|
|
50
|
-
};
|
|
51
|
-
/** Release-asset URLs for the keychain helper zip + its checksum at `v<version>`. */
|
|
52
|
-
export function keychainHelperAssetUrls(version) {
|
|
53
|
-
return helperAssetUrls(KEYCHAIN_HELPER_SPEC, version);
|
|
54
|
-
}
|
|
55
|
-
/** Cache dir for the downloaded keychain helper, one subdir per release tag. */
|
|
56
|
-
export function keychainHelperCacheDir(version) {
|
|
57
|
-
return helperCacheDir(KEYCHAIN_HELPER_SPEC, version);
|
|
58
|
-
}
|
|
59
|
-
/** Verify a keychain helper `.app`: codesign + Team + notarization (no DR pin). */
|
|
60
|
-
export function verifyKeychainHelper(appPath) {
|
|
61
|
-
verifyHelperApp(appPath, KEYCHAIN_HELPER_SPEC);
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Download the keychain helper release asset for `version`, verify sha256 +
|
|
65
|
-
* signature, and return the path to the extracted `Agents CLI.app` in the
|
|
66
|
-
* cache. A missing asset is a hard error naming the exact tag.
|
|
67
|
-
*/
|
|
68
|
-
export function downloadKeychainHelperApp(version) {
|
|
69
|
-
return downloadHelperApp(KEYCHAIN_HELPER_SPEC, version);
|
|
70
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rush `SyncBackend` driver — the original (and currently default) transport
|
|
3
|
-
* for `agents secrets push/pull`. Talks to api.prix.dev and authenticates with
|
|
4
|
-
* the session token written by `rush login` (`~/.rush/user.yaml`).
|
|
5
|
-
*
|
|
6
|
-
* This is the ONE place in the secrets module allowed to reference Rush
|
|
7
|
-
* (api.prix.dev / ~/.rush). It is an opt-in driver kept for backwards
|
|
8
|
-
* compatibility with bundles already pushed to Rush; `sync.ts` selects it as
|
|
9
|
-
* the default but the transport seam (`SyncBackend`) lets other backends drop
|
|
10
|
-
* in without touching the crypto or push/pull logic.
|
|
11
|
-
*/
|
|
12
|
-
import type { SyncBackend } from '../sync-backend.js';
|
|
13
|
-
/** The Rush transport. Plaintext never reaches here — only ciphertext envelopes. */
|
|
14
|
-
export declare const rushSyncBackend: SyncBackend;
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rush `SyncBackend` driver — the original (and currently default) transport
|
|
3
|
-
* for `agents secrets push/pull`. Talks to api.prix.dev and authenticates with
|
|
4
|
-
* the session token written by `rush login` (`~/.rush/user.yaml`).
|
|
5
|
-
*
|
|
6
|
-
* This is the ONE place in the secrets module allowed to reference Rush
|
|
7
|
-
* (api.prix.dev / ~/.rush). It is an opt-in driver kept for backwards
|
|
8
|
-
* compatibility with bundles already pushed to Rush; `sync.ts` selects it as
|
|
9
|
-
* the default but the transport seam (`SyncBackend`) lets other backends drop
|
|
10
|
-
* in without touching the crypto or push/pull logic.
|
|
11
|
-
*/
|
|
12
|
-
import * as fs from 'fs';
|
|
13
|
-
import * as os from 'os';
|
|
14
|
-
import * as path from 'path';
|
|
15
|
-
import * as yaml from 'yaml';
|
|
16
|
-
import { isRushSessionExpired } from '../../rush-session.js';
|
|
17
|
-
const PROXY_BASE = 'https://api.prix.dev';
|
|
18
|
-
const USER_YAML = path.join(os.homedir(), '.rush', 'user.yaml');
|
|
19
|
-
const BUNDLE_ENDPOINT = '/api/v1/secrets/bundles';
|
|
20
|
-
function readRushToken() {
|
|
21
|
-
if (!fs.existsSync(USER_YAML)) {
|
|
22
|
-
throw new Error('Not logged in to Rush. Run `rush login` first.');
|
|
23
|
-
}
|
|
24
|
-
const raw = fs.readFileSync(USER_YAML, 'utf-8');
|
|
25
|
-
const data = yaml.parse(raw);
|
|
26
|
-
const token = data?.session?.access_token;
|
|
27
|
-
if (!token) {
|
|
28
|
-
throw new Error('No session token in ~/.rush/user.yaml. Run `rush login` first.');
|
|
29
|
-
}
|
|
30
|
-
const expiresAt = data.session?.expires_at;
|
|
31
|
-
if (isRushSessionExpired(expiresAt)) {
|
|
32
|
-
const expiredAt = new Date(expiresAt).toISOString();
|
|
33
|
-
throw new Error(`Rush session expired at ${expiredAt}. Run \`rush login\` to refresh.`);
|
|
34
|
-
}
|
|
35
|
-
return token;
|
|
36
|
-
}
|
|
37
|
-
async function api(method, endpoint, body) {
|
|
38
|
-
const token = readRushToken();
|
|
39
|
-
const url = endpoint.startsWith('http') ? endpoint : `${PROXY_BASE}${endpoint}`;
|
|
40
|
-
return fetch(url, {
|
|
41
|
-
method,
|
|
42
|
-
headers: {
|
|
43
|
-
Authorization: `Bearer ${token}`,
|
|
44
|
-
'Content-Type': 'application/json',
|
|
45
|
-
},
|
|
46
|
-
body: body === undefined ? undefined : JSON.stringify(body),
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
function bundlePath(name) {
|
|
50
|
-
return `${BUNDLE_ENDPOINT}/${encodeURIComponent(name)}`;
|
|
51
|
-
}
|
|
52
|
-
/** The Rush transport. Plaintext never reaches here — only ciphertext envelopes. */
|
|
53
|
-
export const rushSyncBackend = {
|
|
54
|
-
async putEnvelope(name, payload) {
|
|
55
|
-
const res = await api('PUT', bundlePath(name), payload);
|
|
56
|
-
if (!res.ok) {
|
|
57
|
-
const body = await res.text().catch(() => '');
|
|
58
|
-
throw new Error(`Push failed (${res.status} ${res.statusText}): ${body}`);
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
async getEnvelope(name) {
|
|
62
|
-
const res = await api('GET', bundlePath(name));
|
|
63
|
-
if (res.status === 404)
|
|
64
|
-
return null;
|
|
65
|
-
if (!res.ok) {
|
|
66
|
-
const body = await res.text().catch(() => '');
|
|
67
|
-
throw new Error(`Pull failed (${res.status} ${res.statusText}): ${body}`);
|
|
68
|
-
}
|
|
69
|
-
return await res.json();
|
|
70
|
-
},
|
|
71
|
-
async deleteEnvelope(name) {
|
|
72
|
-
const res = await api('DELETE', bundlePath(name));
|
|
73
|
-
if (res.status === 404)
|
|
74
|
-
return false;
|
|
75
|
-
if (!res.ok) {
|
|
76
|
-
const body = await res.text().catch(() => '');
|
|
77
|
-
throw new Error(`Delete failed (${res.status} ${res.statusText}): ${body}`);
|
|
78
|
-
}
|
|
79
|
-
return true;
|
|
80
|
-
},
|
|
81
|
-
async listEnvelopes() {
|
|
82
|
-
const res = await api('GET', BUNDLE_ENDPOINT);
|
|
83
|
-
if (!res.ok) {
|
|
84
|
-
const body = await res.text().catch(() => '');
|
|
85
|
-
throw new Error(`List failed (${res.status} ${res.statusText}): ${body}`);
|
|
86
|
-
}
|
|
87
|
-
const data = await res.json();
|
|
88
|
-
return data.bundles ?? [];
|
|
89
|
-
},
|
|
90
|
-
};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared helpers for the Linux/Windows encrypted-file fallback.
|
|
3
|
-
*
|
|
4
|
-
* When the native credential store (GNOME Keyring / Windows Credential Manager)
|
|
5
|
-
* is unreachable, both backends route to the AES-256-GCM file store. The routing
|
|
6
|
-
* is "sticky": once any item is on disk, every op stays on the file store. That
|
|
7
|
-
* is correct for perf, but on its own it would silently *shadow* secrets that
|
|
8
|
-
* still live in the native store — reads for them would return empty with no
|
|
9
|
-
* hint. This module centralizes the two pieces that keep that from being silent:
|
|
10
|
-
*
|
|
11
|
-
* 1. `noteNativeShadow()` — a one-time stderr notice, emitted when a read
|
|
12
|
-
* falls through to the native store (found something shadowed, or hit a
|
|
13
|
-
* locked/unreachable store), pointing at `agents secrets import-keyring`.
|
|
14
|
-
* 2. The result types for that import command.
|
|
15
|
-
*
|
|
16
|
-
* macOS has no file fallback (see ./index.ts) and uses `migrate-acl` /
|
|
17
|
-
* `migrate-orphans` for its own invisible-item classes, so none of this runs
|
|
18
|
-
* there.
|
|
19
|
-
*/
|
|
20
|
-
export type NativeImportStatus = 'imported' | 'would-import' | 'exists' | 'failed';
|
|
21
|
-
export interface NativeImportResult {
|
|
22
|
-
item: string;
|
|
23
|
-
status: NativeImportStatus;
|
|
24
|
-
detail?: string;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Outcome of an `import-keyring` run. `available` is false when no native
|
|
28
|
-
* tooling exists (no `secret-tool` / no `powershell.exe`); `locked` is true when
|
|
29
|
-
* the native store exists but is locked/unreachable, so nothing could be read.
|
|
30
|
-
*/
|
|
31
|
-
export interface NativeImportReport {
|
|
32
|
-
available: boolean;
|
|
33
|
-
locked: boolean;
|
|
34
|
-
results: NativeImportResult[];
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Emit a one-time stderr notice that the file fallback is masking the native
|
|
38
|
-
* credential store. Both backends share the copy so the guidance is identical.
|
|
39
|
-
*
|
|
40
|
-
* 'shadowed' — a secret was just read from the native store that isn't in the
|
|
41
|
-
* file store. It works, but each read pays a native lookup and it
|
|
42
|
-
* won't survive the store locking; suggest migrating it.
|
|
43
|
-
* 'locked' — the native store is locked/unreachable, so its secrets can't be
|
|
44
|
-
* read in this session at all; the user must unlock, then migrate.
|
|
45
|
-
*/
|
|
46
|
-
export declare function noteNativeShadow(kind: 'shadowed' | 'locked', fileDir: string): void;
|
|
47
|
-
/** Test-only: clear the one-time notice guard between cases. */
|
|
48
|
-
export declare function _resetFallbackNoticeForTest(): void;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared helpers for the Linux/Windows encrypted-file fallback.
|
|
3
|
-
*
|
|
4
|
-
* When the native credential store (GNOME Keyring / Windows Credential Manager)
|
|
5
|
-
* is unreachable, both backends route to the AES-256-GCM file store. The routing
|
|
6
|
-
* is "sticky": once any item is on disk, every op stays on the file store. That
|
|
7
|
-
* is correct for perf, but on its own it would silently *shadow* secrets that
|
|
8
|
-
* still live in the native store — reads for them would return empty with no
|
|
9
|
-
* hint. This module centralizes the two pieces that keep that from being silent:
|
|
10
|
-
*
|
|
11
|
-
* 1. `noteNativeShadow()` — a one-time stderr notice, emitted when a read
|
|
12
|
-
* falls through to the native store (found something shadowed, or hit a
|
|
13
|
-
* locked/unreachable store), pointing at `agents secrets import-keyring`.
|
|
14
|
-
* 2. The result types for that import command.
|
|
15
|
-
*
|
|
16
|
-
* macOS has no file fallback (see ./index.ts) and uses `migrate-acl` /
|
|
17
|
-
* `migrate-orphans` for its own invisible-item classes, so none of this runs
|
|
18
|
-
* there.
|
|
19
|
-
*/
|
|
20
|
-
let noticeEmitted = false;
|
|
21
|
-
/**
|
|
22
|
-
* Emit a one-time stderr notice that the file fallback is masking the native
|
|
23
|
-
* credential store. Both backends share the copy so the guidance is identical.
|
|
24
|
-
*
|
|
25
|
-
* 'shadowed' — a secret was just read from the native store that isn't in the
|
|
26
|
-
* file store. It works, but each read pays a native lookup and it
|
|
27
|
-
* won't survive the store locking; suggest migrating it.
|
|
28
|
-
* 'locked' — the native store is locked/unreachable, so its secrets can't be
|
|
29
|
-
* read in this session at all; the user must unlock, then migrate.
|
|
30
|
-
*/
|
|
31
|
-
export function noteNativeShadow(kind, fileDir) {
|
|
32
|
-
if (noticeEmitted)
|
|
33
|
-
return;
|
|
34
|
-
noticeEmitted = true;
|
|
35
|
-
if (kind === 'locked') {
|
|
36
|
-
process.stderr.write(`[agents] the native credential store is locked/unreachable — secrets stored there are not ` +
|
|
37
|
-
`readable in this session. Unlock it, then run \`agents secrets import-keyring\` to migrate ` +
|
|
38
|
-
`them into the encrypted file store at ${fileDir}.\n`);
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
process.stderr.write(`[agents] read a secret from the native credential store that is not in the file store at ` +
|
|
42
|
-
`${fileDir}. Run \`agents secrets import-keyring\` to migrate it so it stays readable headless.\n`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/** Test-only: clear the one-time notice guard between cases. */
|
|
46
|
-
export function _resetFallbackNoticeForTest() {
|
|
47
|
-
noticeEmitted = false;
|
|
48
|
-
}
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Passphrase-encrypted file store for secrets — platform-neutral.
|
|
3
|
-
*
|
|
4
|
-
* An AES-256-GCM encrypted-file store under `~/.agents/.cache/secrets/`. The
|
|
5
|
-
* encryption key is scrypt-derived from a passphrase read from
|
|
6
|
-
* `AGENTS_SECRETS_PASSPHRASE` (preferred) or a machine-local key the store
|
|
7
|
-
* auto-provisions on first use. One `<item>.enc` JSON file per item, mode 0600.
|
|
8
|
-
*
|
|
9
|
-
* Two callers, one policy: the store silently auto-provisions a stable
|
|
10
|
-
* machine-local key (a 0600 file under `~/.agents/.secrets-key/`) on EVERY
|
|
11
|
-
* platform, so it works out of the box with no passphrase to set or remember and
|
|
12
|
-
* never pops a prompt or Touch ID sheet.
|
|
13
|
-
* - Linux (src/lib/secrets/linux.ts): the headless fallback when the default
|
|
14
|
-
* Secret Service collection is locked.
|
|
15
|
-
* - macOS/Windows file-backed bundles (src/lib/secrets/bundles.ts): an explicit,
|
|
16
|
-
* opt-in non-biometry backend for headless/remote runs.
|
|
17
|
-
* Set AGENTS_SECRETS_PASSPHRASE to opt into a key held off disk instead (e.g. to
|
|
18
|
-
* share one bundle's ciphertext across boxes under a common key).
|
|
19
|
-
*
|
|
20
|
-
* The item-name scheme is shared with the keychain backend so a file-backed
|
|
21
|
-
* item and its keychain twin carry identical names:
|
|
22
|
-
* `agents-cli.bundles.<name>` and `agents-cli.secrets.<bundle>.<key>`.
|
|
23
|
-
*/
|
|
24
|
-
import type { KeychainBackend } from './index.js';
|
|
25
|
-
export declare function fileDir(): string;
|
|
26
|
-
/** True if a machine-local passphrase has already been provisioned. */
|
|
27
|
-
export declare function machinePassphraseExists(): boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Resolve the passphrase for the encrypted file store.
|
|
30
|
-
*
|
|
31
|
-
* Order: AGENTS_SECRETS_PASSPHRASE > previously-provisioned machine-local key >
|
|
32
|
-
* legacy co-located key > a freshly auto-provisioned machine-local key. It NEVER
|
|
33
|
-
* prompts, and never fails for WANT of a passphrase — the file store must work on
|
|
34
|
-
* every platform (macOS included) without the user setting, typing, or
|
|
35
|
-
* remembering one. (It can still throw if provisioning cannot write the key file
|
|
36
|
-
* at all; that is a disk/permissions failure, not a missing passphrase.) Provisioning
|
|
37
|
-
* writes a 0600 key file (encryption-at-rest, same posture as an SSH key); set
|
|
38
|
-
* AGENTS_SECRETS_PASSPHRASE to opt into an off-disk key.
|
|
39
|
-
*/
|
|
40
|
-
export declare function getPassphrase(): string;
|
|
41
|
-
/** Encrypted-file on-disk shape. Exported for tests. */
|
|
42
|
-
export interface EncFile {
|
|
43
|
-
salt: string;
|
|
44
|
-
iv: string;
|
|
45
|
-
authTag: string;
|
|
46
|
-
ciphertext: string;
|
|
47
|
-
}
|
|
48
|
-
/** Encrypt plaintext under a passphrase using AES-256-GCM with a random
|
|
49
|
-
* scrypt salt and a random 96-bit IV. Exported for tests. */
|
|
50
|
-
export declare function encryptForFallback(plaintext: string, passphrase: string): EncFile;
|
|
51
|
-
/** Decrypt an EncFile under a passphrase. Throws on wrong key or tampered
|
|
52
|
-
* ciphertext (auth-tag mismatch). Exported for tests. */
|
|
53
|
-
export declare function decryptForFallback(enc: EncFile, passphrase: string): string;
|
|
54
|
-
/** Absolute path of one encrypted file-store item. No read or decrypt occurs. */
|
|
55
|
-
export declare function fileStoreItemPath(item: string): string;
|
|
56
|
-
declare function fileHas(item: string): boolean;
|
|
57
|
-
declare function fileGet(item: string): string;
|
|
58
|
-
declare function fileGetBatch(items: string[]): Map<string, string>;
|
|
59
|
-
declare function fileSet(item: string, value: string): void;
|
|
60
|
-
declare function fileDelete(item: string): boolean;
|
|
61
|
-
declare function fileList(prefix: string): string[];
|
|
62
|
-
/** True if the fallback dir has any committed encrypted items. */
|
|
63
|
-
export declare function fileStoreHasItems(): boolean;
|
|
64
|
-
/** Low-level file-store ops, exported so callers (linux fallback, macOS
|
|
65
|
-
* file-backed bundles) can opt into or out of passphrase auto-provision. */
|
|
66
|
-
export declare const fileStore: {
|
|
67
|
-
has: typeof fileHas;
|
|
68
|
-
get: typeof fileGet;
|
|
69
|
-
getBatch: typeof fileGetBatch;
|
|
70
|
-
set: typeof fileSet;
|
|
71
|
-
delete: typeof fileDelete;
|
|
72
|
-
list: typeof fileList;
|
|
73
|
-
};
|
|
74
|
-
/** File-only KeychainBackend (exported for tests; the Linux backend uses these
|
|
75
|
-
* ops with auto-provision allowed). */
|
|
76
|
-
export declare const fileBackend: KeychainBackend;
|
|
77
|
-
/** Resolved passphrase directory (exported for integration tests). */
|
|
78
|
-
export declare function resolvePassphraseDir(): string;
|
|
79
|
-
/**
|
|
80
|
-
* Path of the machine-local passphrase file that currently holds the file-store
|
|
81
|
-
* key, or null if none is provisioned. Prefers the canonical #479 location and
|
|
82
|
-
* falls back to the legacy co-located path, mirroring `readMachinePassphrase`.
|
|
83
|
-
*/
|
|
84
|
-
export declare function machinePassphraseSourcePath(): string | null;
|
|
85
|
-
/** Outcome of a `rotatePassphrase` run. Carries no secret material. */
|
|
86
|
-
export interface RotatePassphraseReport {
|
|
87
|
-
/** True when nothing was written (report-only). */
|
|
88
|
-
dryRun: boolean;
|
|
89
|
-
/** True when the store was re-encrypted and the key file swapped. */
|
|
90
|
-
committed: boolean;
|
|
91
|
-
/** Encrypted items that decrypt under the current key and were (or would be) re-keyed. */
|
|
92
|
-
bundleCount: number;
|
|
93
|
-
/** `.enc` files that do NOT decrypt under the current key — left untouched, never re-keyed. */
|
|
94
|
-
skipped: string[];
|
|
95
|
-
/** Every re-keyed item round-tripped (decrypt) under the new key before any swap. */
|
|
96
|
-
roundTripOk: boolean;
|
|
97
|
-
/** The machine-local passphrase file that was (or would be) rewritten in place. */
|
|
98
|
-
keyFilePath: string;
|
|
99
|
-
/**
|
|
100
|
-
* A previous rotation had left artifacts on disk and they were healed before
|
|
101
|
-
* this rotation proceeded. This happens under `--dry-run` too — recovery is how
|
|
102
|
-
* a crashed store becomes readable again without re-keying it — and is the only
|
|
103
|
-
* thing a dry run writes.
|
|
104
|
-
*/
|
|
105
|
-
recoveredInterruptedRotation: boolean;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Recover from a rotation that was interrupted mid-swap on a prior run, so the
|
|
109
|
-
* store is always left in a single, self-consistent, readable state.
|
|
110
|
-
*
|
|
111
|
-
* Recovery is CONTENT-aware, not presence-aware, and it classifies the WHOLE
|
|
112
|
-
* store, not just one item. The mere existence of the store dir and the key file
|
|
113
|
-
* does not prove they match (RUSH-1975 data-loss window): on the non-co-located
|
|
114
|
-
* key path the swap is four renames, and a crash after the store swap
|
|
115
|
-
* (`stageDir`->`dir`) but before the key swap (`keyTmp`->`keyPath`) finishes
|
|
116
|
-
* leaves a NEW-key store next to the OLD key file, both present. A presence check
|
|
117
|
-
* would see "both here" and wrongly sweep the only copies of the old ciphertext
|
|
118
|
-
* (`<dir>.rotate-old-*`) and the new key (`<key>.rotate-new`), permanently
|
|
119
|
-
* orphaning every secret. So we probe the actual ciphertext with `classifyStore`,
|
|
120
|
-
* which distinguishes a store that opens fully under one key ('all') from one that
|
|
121
|
-
* is MIXED — some items under the live key, others under the incoming key ('some',
|
|
122
|
-
* e.g. after a mid-swap crash contaminated by a later `secrets set`):
|
|
123
|
-
*
|
|
124
|
-
* 1. The live key opens EVERY non-orphan item ('all') → rotation complete and
|
|
125
|
-
* consistent (or never interrupted); sweeping the `.rotate-*` artifacts is safe —
|
|
126
|
-
* unless a `<dir>.rotate-old-*` backup still holds `.enc` items absent from the
|
|
127
|
-
* live dir. That means the live dir is not the post-swap store but a fresh dir an
|
|
128
|
-
* interstitial `secrets set` created after a crash in the move-aside window left
|
|
129
|
-
* the store dir absent, so the backup is the only copy of those items → REFUSE.
|
|
130
|
-
* 2. Else, if `<key>.rotate-new` opens every non-orphan item ('all'), the crash
|
|
131
|
-
* landed after the store swap but before the key swap finished → finish the
|
|
132
|
-
* rotation forward by installing `.rotate-new` as the live key, then sweep.
|
|
133
|
-
* 3. Else, if neither key opens any item, roll back: restore the
|
|
134
|
-
* `<dir>.rotate-old-*` backup over `dir` and `<key>.rotate-oldkey` over the key
|
|
135
|
-
* file — but only once the backup is proven to open fully under the old key.
|
|
136
|
-
* 4. If a key opens SOME but not all items ('some'), the store is MIXED — an
|
|
137
|
-
* interrupted rotation contaminated by a later write, with live data under two
|
|
138
|
-
* keys at once. Sweeping would delete the only copy of one of those keys, so we
|
|
139
|
-
* REFUSE: throw an actionable error and preserve every recovery artifact for
|
|
140
|
-
* out-of-band repair. Likewise, if neither forward nor rollback can be proven,
|
|
141
|
-
* leave every artifact in place — a leftover temp dir is recoverable, deleting
|
|
142
|
-
* the only copy of a key or ciphertext is not.
|
|
143
|
-
*
|
|
144
|
-
* A phase-marker / journal file was considered and deliberately skipped: the
|
|
145
|
-
* AES-256-GCM auth tag already makes the decrypt probe an authoritative,
|
|
146
|
-
* self-validating record of which key matches the store. A separate marker would
|
|
147
|
-
* be a second source of truth that can disagree with reality — its own write has
|
|
148
|
-
* crash windows, and a stale marker misleads — so it would weaken, not strengthen,
|
|
149
|
-
* this guarantee. Idempotent; a no-op when no rotation artifacts are present.
|
|
150
|
-
* Callers run this under the store lock (see `withStoreLock`).
|
|
151
|
-
*/
|
|
152
|
-
/**
|
|
153
|
-
* Whether a previous rotation left artifacts on disk — i.e. whether
|
|
154
|
-
* {@link recoverInterruptedRotation} would do any work. Read-only, so `--dry-run`
|
|
155
|
-
* can report a pending recovery without performing (and thus writing) one.
|
|
156
|
-
*/
|
|
157
|
-
export declare function hasInterruptedRotationArtifacts(keyPath: string): boolean;
|
|
158
|
-
/**
|
|
159
|
-
* Rotate the machine-local file-store passphrase: decrypt every `.enc` item
|
|
160
|
-
* under the current key and re-encrypt it under a freshly generated one, then
|
|
161
|
-
* swap both the ciphertext and the key file atomically.
|
|
162
|
-
*
|
|
163
|
-
* Safety contract (RUSH-1975):
|
|
164
|
-
* - Verify before writing: every re-keyed item must round-trip decrypt under
|
|
165
|
-
* the new key, and the re-keyed count must reconcile with the source, or the
|
|
166
|
-
* run aborts having written nothing.
|
|
167
|
-
* - Atomic: the new store is staged in a sibling temp dir, fsync'd, then swapped
|
|
168
|
-
* into place by directory rename; the new key file is fsync'd and swapped the
|
|
169
|
-
* same way. A crash before the swap leaves the old store and old key fully
|
|
170
|
-
* intact and readable; a crash inside the swap self-heals on the next run
|
|
171
|
-
* (see `recoverInterruptedRotation`). No half-re-keyed store is ever exposed.
|
|
172
|
-
* - No plaintext (secret value or passphrase) is ever written to disk, argv, or
|
|
173
|
-
* a log — only ciphertext is staged, and the new key lands only in the 0600
|
|
174
|
-
* key file.
|
|
175
|
-
* - Items that do not decrypt under the current key (orphan caches, stale test
|
|
176
|
-
* artifacts written under another key) are copied through verbatim, never
|
|
177
|
-
* re-keyed, and reported in `skipped`.
|
|
178
|
-
*
|
|
179
|
-
* `newPassphrase` and the `on*` callbacks are test seams. `newPassphrase` pins the
|
|
180
|
-
* generated key so a test can assert the swap. `onStagedBeforeCommit` fires after
|
|
181
|
-
* staging but before any swap (a crash here leaves the old store fully intact).
|
|
182
|
-
* `onStoreSwappedBeforeKeySwap` fires after the store swap but before the key swap
|
|
183
|
-
* begins (Window A: NEW-key store beside the OLD key file). `onKeyBackedUpBeforeNewKey`
|
|
184
|
-
* fires after the old key is moved aside but before the new key lands (Window B: NEW
|
|
185
|
-
* store, key file absent). Each throws to simulate a mid-swap crash at that exact
|
|
186
|
-
* point, and next-run recovery must heal it without data loss. `tamperStaged` forces
|
|
187
|
-
* a staged item to fail its round-trip check, exercising the verify-before-swap abort.
|
|
188
|
-
*/
|
|
189
|
-
export interface RotatePassphraseOpts {
|
|
190
|
-
dryRun?: boolean;
|
|
191
|
-
newPassphrase?: string;
|
|
192
|
-
onStagedBeforeCommit?: () => void;
|
|
193
|
-
onStoreMovedAsideBeforeSwap?: () => void;
|
|
194
|
-
onStoreSwappedBeforeKeySwap?: (heartbeat: () => void) => void;
|
|
195
|
-
onKeyBackedUpBeforeNewKey?: () => void;
|
|
196
|
-
tamperStaged?: boolean;
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Rotate under the exclusive store lock, so no `secrets set`/`delete` and no
|
|
200
|
-
* second rotation can interleave with the swap (see `withStoreLock`). The whole
|
|
201
|
-
* run — recovery, verify, swap — holds the lock; it is released on return or throw.
|
|
202
|
-
*/
|
|
203
|
-
export declare function rotatePassphrase(opts?: RotatePassphraseOpts): RotatePassphraseReport;
|
|
204
|
-
/** Test-only: reset module state (file dir + cached passphrase). */
|
|
205
|
-
export declare function _resetFileStoreForTest(opts?: {
|
|
206
|
-
fileDir?: string | null;
|
|
207
|
-
passphraseDir?: string | null;
|
|
208
|
-
passphrase?: string | null;
|
|
209
|
-
}): void;
|
|
210
|
-
/** Test-only: flush the in-memory metadata cache to disk now. Production flushes
|
|
211
|
-
* once at process exit; a test needs the on-disk copy mid-run to simulate the
|
|
212
|
-
* next `agents` process reading it. */
|
|
213
|
-
export declare function _flushMetaCacheForTest(): void;
|
|
214
|
-
/** Test-only: shorten the store-lock acquire timeout so a contended-lock assertion
|
|
215
|
-
* fails fast instead of waiting out the 30s production budget. */
|
|
216
|
-
export declare function _setFileStoreLockTimeoutForTest(ms: number | null): void;
|
|
217
|
-
/** Test-only: shrink the store-lock stale window so a heartbeat/steal assertion runs
|
|
218
|
-
* in milliseconds instead of the 5s production window. */
|
|
219
|
-
export declare function _setFileStoreLockStaleMsForTest(ms: number | null): void;
|
|
220
|
-
/** Test-only: the cross-process store-lock target (sibling of the store dir). */
|
|
221
|
-
export declare function _fileStoreLockPathForTest(): string;
|
|
222
|
-
export {};
|