@phnx-labs/agents-cli 1.22.57 → 1.22.58
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 +56 -0
- package/dist/bootstrap.js +8 -1
- package/dist/commands/accounts.js +7 -3
- package/dist/commands/apply.js +10 -2
- package/dist/commands/fork.d.ts +23 -10
- package/dist/commands/fork.js +115 -58
- package/dist/commands/monitors.js +11 -0
- package/dist/commands/prune.js +5 -3
- package/dist/commands/routines.d.ts +8 -0
- package/dist/commands/routines.js +57 -3
- package/dist/commands/sessions-picker.d.ts +11 -0
- package/dist/commands/sessions-picker.js +16 -0
- package/dist/commands/sessions.js +1 -0
- package/dist/commands/share.d.ts +14 -0
- package/dist/commands/share.js +43 -2
- package/dist/commands/status.js +1 -1
- package/dist/commands/sync.js +83 -7
- package/dist/commands/traces.js +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -1
- package/dist/lib/account-registry.d.ts +5 -1
- package/dist/lib/account-registry.js +47 -14
- package/dist/lib/accounting/capacity.d.ts +18 -7
- package/dist/lib/accounting/capacity.js +19 -8
- package/dist/lib/accounting/usage-sync.d.ts +29 -1
- package/dist/lib/accounting/usage-sync.js +76 -2
- package/dist/lib/accounting/usage.js +7 -1
- package/dist/lib/auth-mint.d.ts +11 -1
- package/dist/lib/auth-mint.js +21 -6
- package/dist/lib/browser/ipc.d.ts +8 -0
- package/dist/lib/browser/ipc.js +87 -0
- package/dist/lib/browser/service.d.ts +19 -0
- package/dist/lib/browser/service.js +96 -11
- package/dist/lib/browser/sessions-list.js +10 -1
- package/dist/lib/daemon/runner.d.ts +3 -0
- package/dist/lib/daemon/runner.js +86 -45
- package/dist/lib/daemon/usage-sync-service.d.ts +3 -3
- package/dist/lib/daemon/usage-sync-service.js +14 -8
- package/dist/lib/daemon-services.js +1 -1
- package/dist/lib/devices/connect.d.ts +17 -8
- package/dist/lib/devices/connect.js +31 -14
- package/dist/lib/doctor-diff.js +77 -7
- package/dist/lib/fleet/manifest.d.ts +17 -0
- package/dist/lib/fleet/manifest.js +26 -0
- package/dist/lib/hooks/install.d.ts +27 -11
- package/dist/lib/hooks/install.js +42 -17
- package/dist/lib/hosts/reconnect.d.ts +52 -203
- package/dist/lib/hosts/reconnect.js +64 -284
- package/dist/lib/installations/migrate.d.ts +6 -120
- package/dist/lib/installations/migrate.js +27 -259
- package/dist/lib/installations/shims.d.ts +13 -95
- package/dist/lib/installations/shims.js +22 -139
- package/dist/lib/installations/store.js +1 -1
- package/dist/lib/installations/versions.d.ts +26 -133
- package/dist/lib/installations/versions.js +41 -204
- package/dist/lib/plugins/skills.d.ts +8 -1
- package/dist/lib/plugins/skills.js +18 -2
- package/dist/lib/refresh.d.ts +9 -0
- package/dist/lib/refresh.js +3 -1
- package/dist/lib/routine-readiness.d.ts +15 -1
- package/dist/lib/routine-readiness.js +41 -0
- package/dist/lib/sandbox.d.ts +4 -1
- package/dist/lib/sandbox.js +30 -1
- package/dist/lib/secrets/agent.d.ts +80 -225
- package/dist/lib/secrets/agent.js +139 -401
- package/dist/lib/secrets/bundles.d.ts +73 -222
- package/dist/lib/secrets/bundles.js +168 -467
- package/dist/lib/secrets/reaper.d.ts +28 -70
- package/dist/lib/secrets/reaper.js +30 -85
- package/dist/lib/secrets/remote.d.ts +42 -129
- package/dist/lib/secrets/remote.js +55 -173
- package/dist/lib/self-heal/checks/install-staging.d.ts +4 -0
- package/dist/lib/self-heal/checks/install-staging.js +96 -0
- package/dist/lib/self-heal/registry.js +2 -0
- package/dist/lib/self-heal/types.d.ts +1 -1
- package/dist/lib/self-update.d.ts +23 -0
- package/dist/lib/self-update.js +50 -0
- package/dist/lib/session/active.d.ts +13 -1
- package/dist/lib/session/active.js +2 -0
- package/dist/lib/session/db.d.ts +20 -1
- package/dist/lib/session/db.js +139 -9
- package/dist/lib/session/fork.d.ts +45 -26
- package/dist/lib/session/fork.js +32 -95
- package/dist/lib/session/tool-calls.d.ts +43 -1
- package/dist/lib/session/tool-calls.js +74 -44
- package/dist/lib/session/tool-store.d.ts +33 -2
- package/dist/lib/session/tool-store.js +56 -3
- package/dist/lib/staleness/writers/sources.d.ts +5 -0
- package/dist/lib/staleness/writers/sources.js +2 -1
- package/dist/lib/sync-status.d.ts +22 -0
- package/dist/lib/sync-status.js +27 -0
- package/dist/lib/sync-umbrella.d.ts +9 -0
- package/dist/lib/sync-umbrella.js +21 -2
- package/dist/lib/traces/insights.d.ts +47 -14
- package/dist/lib/traces/insights.js +92 -21
- package/dist/lib/traces/phenotype.d.ts +23 -3
- package/dist/lib/traces/phenotype.js +72 -24
- package/dist/lib/traces/sync.d.ts +15 -0
- package/dist/lib/traces/sync.js +104 -19
- package/dist/lib/traces/worker-template.js +154 -1
- package/package.json +1 -1
|
@@ -1,24 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Secret bundles — named sets of environment variables backed by a secret store.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
6
|
-
* `agents-cli.secrets.<bundle>.<key>`. Two backends carry those items:
|
|
7
|
-
*
|
|
8
|
-
* - `keychain` (default): the macOS Keychain (device-local, Touch ID / device
|
|
9
|
-
* passcode gated) or Linux libsecret — see src/lib/secrets/index.ts.
|
|
10
|
-
* - `file`: an AES-256-GCM encrypted-file store keyed by a passphrase
|
|
11
|
-
* (src/lib/secrets/filestore.ts). Opt-in, for headless / remote runs where
|
|
12
|
-
* no biometry prompt can be satisfied (e.g. a release on a remote Mac over
|
|
13
|
-
* SSH). The item-name scheme is identical, so the only difference is where
|
|
14
|
-
* bytes land. A file-backed bundle is discovered by the presence of its
|
|
15
|
-
* metadata item in the file store.
|
|
16
|
-
* - `vault`: a single age-encrypted ~/.agents/vault.age file unlocked by
|
|
17
|
-
* `agents secrets vault unlock`; intended for user-managed cross-machine file sync.
|
|
18
|
-
*
|
|
19
|
-
* Server-backed cross-machine sync is handled by src/lib/secrets/sync.ts via
|
|
20
|
-
* an explicit encrypted export/import flow; the bundle layer also supports the
|
|
21
|
-
* local vault backend for user-managed file sync.
|
|
3
|
+
* Metadata lives under `agents-cli.bundles.<name>`; values under
|
|
4
|
+
* `agents-cli.secrets.<bundle>.<key>`. Backends: `keychain` (default),
|
|
5
|
+
* `file` (headless/passphrase), and `vault` (age-encrypted, user-synced).
|
|
22
6
|
*/
|
|
23
7
|
import { type BundleValue, type SecretRef } from './index.js';
|
|
24
8
|
/** Which store carries a bundle's items. */
|
|
@@ -26,69 +10,40 @@ export type SecretsBackend = 'keychain' | 'file' | 'vault';
|
|
|
26
10
|
/** Disable the broker-only guard for in-memory keychain tests. */
|
|
27
11
|
export declare function setKeychainAgentOnlyBypassForTest(bypass: boolean): void;
|
|
28
12
|
/**
|
|
29
|
-
* Discover a bundle's backend by location
|
|
30
|
-
*
|
|
31
|
-
* check — no passphrase, no Touch ID — so it sidesteps the chicken-and-egg of
|
|
32
|
-
* "read metadata to learn where metadata lives." Absent ⇒ keychain.
|
|
13
|
+
* Discover a bundle's backend by location. File store is checked first (a plain
|
|
14
|
+
* existence test, no passphrase); absent/locked vault falls back to keychain.
|
|
33
15
|
*/
|
|
34
16
|
export declare function bundleBackend(name: string): SecretsBackend;
|
|
35
17
|
/** Allowed values for a secret's `type` metadata field. */
|
|
36
18
|
export declare const SECRET_TYPES: readonly ["api-key", "token", "password", "url", "database-url", "ssh-key", "certificate", "webhook", "note"];
|
|
37
19
|
export type SecretType = typeof SECRET_TYPES[number];
|
|
38
|
-
/** Per-secret metadata
|
|
20
|
+
/** Per-secret metadata; absent fields are omitted at write time. */
|
|
39
21
|
export interface VarMeta {
|
|
40
22
|
type?: SecretType;
|
|
41
|
-
/** ISO date 'YYYY-MM-DD'.
|
|
23
|
+
/** Future-dated ISO date ('YYYY-MM-DD'). */
|
|
42
24
|
expires?: string;
|
|
43
|
-
/** Singular freeform note. */
|
|
44
25
|
note?: string;
|
|
45
26
|
}
|
|
46
27
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* (Historical name — the window is now a rolling ~1 week, not one calendar day.)
|
|
52
|
-
* Eligible for the secrets-agent — the first real keychain read auto-loads it
|
|
53
|
-
* (auto-cache is on by default) so concurrent runs read it silently, or `unlock`
|
|
54
|
-
* it explicitly. Held from that unlock (not refreshed on use); re-asks sooner
|
|
55
|
-
* after sleep, logout, or `agents secrets lock`. A bare screen-lock does NOT
|
|
56
|
-
* drop it (the login password already gates a locked screen).
|
|
57
|
-
* - `always`: asks every time. Never auto-held — only an explicit `agents
|
|
58
|
-
* secrets unlock` ever holds it; every other read pops Touch ID. Opt a
|
|
59
|
-
* high-value bundle into this when you want to confirm every single read.
|
|
60
|
-
* - `never`: stored WITHOUT the biometry access control — reads are fully
|
|
61
|
-
* silent (no Touch ID, no broker). The least-safe tier: any code running as
|
|
62
|
-
* the user reads it with no user-presence check. Reserved for low-sensitivity,
|
|
63
|
-
* automation-only credentials. Writing a `never` item needs the signed helper's
|
|
64
|
-
* `set-no-acl` path (see keychain-helper.swift); an older pinned helper rejects
|
|
65
|
-
* it loudly rather than silently downgrading to `always`.
|
|
66
|
-
*
|
|
67
|
-
* The default is configurable via `secrets.policy` in agents.yaml. Stored on disk
|
|
68
|
-
* under the legacy `tier` key (`session` == `hold`, `biometry` == explicit
|
|
69
|
-
* `always`, `none` == `never`, absent == inherit the default) so bundles stay
|
|
70
|
-
* readable across mixed CLI versions on synced machines. The user-facing
|
|
71
|
-
* vocabulary is `policy`/`always`/`hold`/`never`.
|
|
28
|
+
* Bundle prompt policy. `hold` (default): one Touch ID per hold window (~7d),
|
|
29
|
+
* then silent via the secrets-agent. `always`: prompt every read. `never`: no
|
|
30
|
+
* biometry ACL — least-safe, automation-only. Configurable via `secrets.policy`;
|
|
31
|
+
* persisted under the legacy `tier` key for cross-version sync.
|
|
72
32
|
*/
|
|
73
33
|
export type SecretsPolicy = 'always' | 'hold' | 'never';
|
|
74
|
-
/** A named set of environment variable definitions backed by
|
|
34
|
+
/** A named set of environment variable definitions backed by secret stores. */
|
|
75
35
|
export interface SecretsBundle {
|
|
76
36
|
name: string;
|
|
77
37
|
description?: string;
|
|
78
38
|
allow_exec?: boolean;
|
|
79
|
-
/**
|
|
39
|
+
/** Absent ⇒ `keychain`. */
|
|
80
40
|
backend?: SecretsBackend;
|
|
81
|
-
/**
|
|
82
|
-
* the legacy `tier` key — see SecretsPolicy. */
|
|
41
|
+
/** Absent ⇒ configured default (`hold`). */
|
|
83
42
|
policy?: SecretsPolicy;
|
|
84
|
-
/** ISO 8601 UTC timestamp. Set once on the first writeBundle() for a bundle. */
|
|
85
43
|
created_at?: string;
|
|
86
|
-
/** ISO 8601 UTC timestamp. Refreshed on every writeBundle(). */
|
|
87
44
|
updated_at?: string;
|
|
88
|
-
/** ISO 8601 UTC timestamp. Stamped by resolveBundleEnv (throttled). */
|
|
89
45
|
last_used?: string;
|
|
90
46
|
vars: Record<string, BundleValue>;
|
|
91
|
-
/** Optional per-var metadata, keyed by var name (parallel to `vars`). */
|
|
92
47
|
meta?: Record<string, VarMeta>;
|
|
93
48
|
}
|
|
94
49
|
export interface LegacyBundleCandidate {
|
|
@@ -102,11 +57,8 @@ export declare const BUNDLE_KEY_PATTERN: RegExp;
|
|
|
102
57
|
export declare const BUNDLE_META_PREFIX = "agents-cli.bundles.";
|
|
103
58
|
export declare const RESERVED_ENV_NAMES: Set<string>;
|
|
104
59
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* so they never pop Touch ID and they can cross the fleet. A keychain- or
|
|
108
|
-
* vault-backed bundle of this name is a misconfiguration: the consumer used to
|
|
109
|
-
* return null (SEC-GAP-3) and silently fall through to Touch ID.
|
|
60
|
+
* Reserved FILE-BACKED bundle for long-lived setup tokens. Must be file-backed;
|
|
61
|
+
* a keychain/vault `auth` bundle is a misconfiguration and is ignored.
|
|
110
62
|
*/
|
|
111
63
|
export declare const AUTH_BUNDLE_NAME = "auth";
|
|
112
64
|
export declare const AUTH_BUNDLE_BACKEND: SecretsBackend;
|
|
@@ -118,11 +70,17 @@ export declare class ReservedBundleWrongBackendError extends Error {
|
|
|
118
70
|
readonly backend: SecretsBackend;
|
|
119
71
|
constructor(bundle: string, backend: SecretsBackend);
|
|
120
72
|
}
|
|
121
|
-
/** Fail loud when
|
|
73
|
+
/** Fail loud when a reserved bundle is on the wrong backend. */
|
|
122
74
|
export declare function assertReservedBundleBackend(name: string, backend: SecretsBackend): void;
|
|
123
75
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
76
|
+
* Check the reserved `auth` bundle. `ok` is true when absent or file-backed.
|
|
77
|
+
*
|
|
78
|
+
* Read-only status probe (`agents doctor`, `agents fleet apply`) — unlike the
|
|
79
|
+
* destructive-write guards `bundleExists()`/`hasKeychainToken()` document
|
|
80
|
+
* themselves as failing loud for, a diagnostic MUST NOT crash the whole
|
|
81
|
+
* command because ONE optional finding could not reach the keychain (e.g. the
|
|
82
|
+
* macOS Keychain helper source is unavailable — PHNX-3385). Treat an
|
|
83
|
+
* unreachable keychain the same as "bundle absent": nothing to warn about.
|
|
126
84
|
*/
|
|
127
85
|
export declare function inspectReservedAuthBundle(): {
|
|
128
86
|
exists: boolean;
|
|
@@ -130,10 +88,9 @@ export declare function inspectReservedAuthBundle(): {
|
|
|
130
88
|
ok: boolean;
|
|
131
89
|
};
|
|
132
90
|
/**
|
|
133
|
-
* After a file-backed import,
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* the destination daemon does not hold still counted as success.
|
|
91
|
+
* After a file-backed import, verify the keys actually decrypt. Import used to
|
|
92
|
+
* report success for ciphertext sealed under a forwarded passphrase this process
|
|
93
|
+
* does not hold.
|
|
137
94
|
*/
|
|
138
95
|
export declare function assertFileBundleDecryptable(name: string, keys: string[]): void;
|
|
139
96
|
export declare function bundleToEnvPrefix(name: string): string;
|
|
@@ -147,67 +104,42 @@ export declare function validateEnvKey(key: string): void;
|
|
|
147
104
|
/** Assert that `t` is one of the known SECRET_TYPES. Throws with the allowed list otherwise. */
|
|
148
105
|
export declare function validateSecretType(t: string): asserts t is SecretType;
|
|
149
106
|
/**
|
|
150
|
-
* Validate
|
|
151
|
-
*
|
|
152
|
-
* "today" is treated as past (per spec).
|
|
107
|
+
* Validate a future `expires` date. Accepts strict 'YYYY-MM-DD'; end-of-day UTC
|
|
108
|
+
* means "today" is past.
|
|
153
109
|
*/
|
|
154
110
|
export declare function validateExpiresFutureDated(iso: string): void;
|
|
155
111
|
export declare function bundleExists(name: string): boolean;
|
|
156
112
|
/**
|
|
157
|
-
* Thrown
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* store). It is deliberately narrow — a bundle that is merely *locked for this
|
|
161
|
-
* run* (headless macOS without `AGENTS_SECRETS_PASSPHRASE`, or a vault that is
|
|
162
|
-
* not logged in) is recoverable and must not be collapsed into this.
|
|
113
|
+
* Thrown for file-store ciphertext that will not decrypt (lost/rotated key or
|
|
114
|
+
* tampered store). Narrow: temporarily-locked bundles are recoverable and must
|
|
115
|
+
* not be collapsed here.
|
|
163
116
|
*/
|
|
164
117
|
export declare class BundleUndecryptableError extends Error {
|
|
165
118
|
constructor(message: string);
|
|
166
119
|
}
|
|
167
120
|
/**
|
|
168
|
-
* Read a bundle,
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* Deleting such a bundle is the only way out of that state, and deletion needs
|
|
173
|
-
* no plaintext, so it must not be gated behind a successful decrypt. Every other
|
|
174
|
-
* failure rethrows: a genuinely missing bundle ("not found"), and — critically —
|
|
175
|
-
* a bundle that is only *temporarily locked* for this run (headless macOS with no
|
|
176
|
-
* `AGENTS_SECRETS_PASSPHRASE`, or a not-logged-in vault). Collapsing that
|
|
177
|
-
* recoverable "set the env / log in" state into "unreadable, safe to delete"
|
|
178
|
-
* would let `secrets delete <name> --yes` silently destroy a perfectly healthy
|
|
179
|
-
* bundle from a cron/launchd run that merely forgot to export the passphrase.
|
|
121
|
+
* Read a bundle, returning null only when metadata is present but permanently
|
|
122
|
+
* unreadable (`BundleUndecryptableError`). Other failures — including temporary
|
|
123
|
+
* lockout — rethrow so a healthy bundle is never deleted by mistake.
|
|
180
124
|
*/
|
|
181
125
|
export declare function readBundleIfDecryptable(name: string): SecretsBundle | null;
|
|
182
126
|
export declare function readBundle(name: string): SecretsBundle;
|
|
183
|
-
/**
|
|
184
|
-
* policy. Configurable via `secrets.policy` in agents.yaml; `hold` (one Touch ID
|
|
185
|
-
* per hold window — `secrets.agent.holdMs`, 7d by default) unless the user
|
|
186
|
-
* explicitly opts back into prompt-every-time with `always`. Best-effort: an
|
|
187
|
-
* unreadable config falls back to the `hold` default. */
|
|
127
|
+
/** Default policy for bundles without an explicit one (`secrets.policy`). */
|
|
188
128
|
export declare function secretsDefaultPolicy(): SecretsPolicy;
|
|
189
|
-
/**
|
|
129
|
+
/** Effective prompt policy of a bundle. */
|
|
190
130
|
export declare function bundlePolicy(bundle: SecretsBundle): SecretsPolicy;
|
|
191
131
|
/** Options for writeBundle. */
|
|
192
132
|
export interface WriteBundleOptions {
|
|
193
133
|
/**
|
|
194
|
-
* Skip evicting the
|
|
195
|
-
*
|
|
196
|
-
* stampLastUsed (a usage-telemetry timestamp, fired on every broker HIT):
|
|
197
|
-
* evicting there would make the cache destroy itself on first use. Every
|
|
198
|
-
* mutating writer (add / rotate / remove / rename / policy / import) must
|
|
199
|
-
* leave this unset so a broker-held copy never serves stale values for up
|
|
200
|
-
* to the ~7d hold.
|
|
134
|
+
* Skip evicting the broker-held copy. Only for no-op writers such as
|
|
135
|
+
* stampLastUsed; mutating writes must evict so stale values aren't served.
|
|
201
136
|
*/
|
|
202
137
|
skipBrokerEviction?: boolean;
|
|
203
138
|
}
|
|
204
139
|
/**
|
|
205
|
-
* Whether a bundle write should evict the broker-held copy.
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
* kill-switch the read fast-path honors), or when a test keychain backend is
|
|
209
|
-
* installed (an in-memory backend has no real keychain behind it, and a test
|
|
210
|
-
* writing bundle 'prod' must never evict the user's real 'prod' unlock).
|
|
140
|
+
* Whether a bundle write should evict the broker-held copy. Exported for tests.
|
|
141
|
+
* Skips when the writer opted out, the broker is disabled, or a test backend is
|
|
142
|
+
* active (so tests don't evict the user's real unlocks).
|
|
211
143
|
*/
|
|
212
144
|
export declare function shouldEvictAfterBundleWrite(skipRequested: boolean, noAgentEnv: string | undefined, backendOverridden: boolean): boolean;
|
|
213
145
|
export declare function writeBundle(bundle: SecretsBundle, opts?: WriteBundleOptions): void;
|
|
@@ -225,18 +157,14 @@ export declare const __metaIndexForTest: {
|
|
|
225
157
|
remove: typeof removeBundleFromMetaIndex;
|
|
226
158
|
};
|
|
227
159
|
/**
|
|
228
|
-
* Re-write
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* delete-then-add `set-no-acl`) — contents and updated_at are preserved and no
|
|
232
|
-
* extra keychain read is issued. Exported for tests. Returns the count healed.
|
|
160
|
+
* Re-write keychain metadata items without the biometry ACL. The caller supplies
|
|
161
|
+
* the JSON already read, so contents are preserved and no extra read is issued.
|
|
162
|
+
* Exported for tests.
|
|
233
163
|
*/
|
|
234
164
|
export declare function healKeychainBundleMetadata(metaJsonByName: Map<string, string>): number;
|
|
235
165
|
/**
|
|
236
|
-
* One-time driver
|
|
237
|
-
*
|
|
238
|
-
* backend has no real keychain — and gated by a sentinel so it runs at most
|
|
239
|
-
* once. Best-effort: a heal failure never breaks bundle listing.
|
|
166
|
+
* One-time driver for healKeychainBundleMetadata. macOS + real keychain only;
|
|
167
|
+
* gated by a sentinel. Best-effort: a heal failure never breaks listing.
|
|
240
168
|
*/
|
|
241
169
|
export declare function healKeychainBundleMetadataAclOnce(metaJsonByName: Map<string, string>): void;
|
|
242
170
|
export declare function listBundles(): SecretsBundle[];
|
|
@@ -249,64 +177,28 @@ export declare function describeBundle(bundle: SecretsBundle): BundleEntryInfo[]
|
|
|
249
177
|
export declare function stampLastUsed(bundle: SecretsBundle): void;
|
|
250
178
|
/** Options for resolveBundleEnv. */
|
|
251
179
|
export interface ResolveBundleOptions {
|
|
252
|
-
/**
|
|
253
|
-
* Human-readable label for who is requesting the secrets. Currently
|
|
254
|
-
* informational only — the helper's Touch ID prompt is set by the OS and
|
|
255
|
-
* cannot be reliably customized once we drop the per-batch reason path,
|
|
256
|
-
* but we keep this in the API so call sites stay explicit about who's
|
|
257
|
-
* about to read the bundle.
|
|
258
|
-
*/
|
|
259
180
|
caller?: string;
|
|
260
|
-
/** Harness type whose unlock may be reused
|
|
181
|
+
/** Harness type whose unlock may be reused. */
|
|
261
182
|
agent?: string;
|
|
262
|
-
/**
|
|
183
|
+
/** Duration shown in the Touch ID prompt. */
|
|
263
184
|
duration?: string;
|
|
264
|
-
/**
|
|
185
|
+
/** Allow this call to raise an interactive biometric prompt. */
|
|
265
186
|
interactiveUnlock?: boolean;
|
|
266
|
-
/**
|
|
267
|
-
* Skip the secrets-agent fast-path and read straight from the keychain
|
|
268
|
-
* (popping Touch ID). Set by callers that must NOT serve a cached snapshot —
|
|
269
|
-
* `unlock` (which populates the agent in the first place) and any flow that
|
|
270
|
-
* needs live values. Also honored via AGENTS_SECRETS_NO_AGENT=1.
|
|
271
|
-
*/
|
|
187
|
+
/** Skip the broker fast-path and read from the keychain directly. */
|
|
272
188
|
noAgent?: boolean;
|
|
273
|
-
/**
|
|
274
|
-
* Resolve only from an already-unlocked secrets-agent snapshot. If the
|
|
275
|
-
* broker has no snapshot, fail before touching Keychain or any other store.
|
|
276
|
-
* Background processes use this to guarantee they never surface a biometric
|
|
277
|
-
* prompt that nobody can answer.
|
|
278
|
-
*/
|
|
189
|
+
/** Resolve only from an already-unlocked broker snapshot; fail before prompting. */
|
|
279
190
|
agentOnly?: boolean;
|
|
280
|
-
/**
|
|
281
|
-
* Inject only this subset of keys from the bundle. Keys not in this list are
|
|
282
|
-
* silently excluded from the returned env map. An error is thrown if any
|
|
283
|
-
* requested key is absent from the bundle (fail-loud, never silent skip).
|
|
284
|
-
* When absent or empty, all keys are injected (original behaviour).
|
|
285
|
-
*/
|
|
191
|
+
/** Inject only these keys. Errors if any requested key is absent. */
|
|
286
192
|
keys?: string[];
|
|
287
|
-
/**
|
|
288
|
-
* When true, skip the pre-run expiry check and inject keys even if their
|
|
289
|
-
* `expires` date is in the past. By default any expired key (or a key whose
|
|
290
|
-
* bundle-level expiry has passed) aborts the run before Touch ID is popped.
|
|
291
|
-
*/
|
|
193
|
+
/** Skip the per-key expiry gate. */
|
|
292
194
|
allowExpired?: boolean;
|
|
293
|
-
/**
|
|
294
|
-
* `process` projects dotted account keys like `GITHUB_USERNAME.personal` to
|
|
295
|
-
* the shell-safe base env name (`GITHUB_USERNAME`). Direct value lookups and
|
|
296
|
-
* backup/export flows use `storage` to preserve the exact bundle key names.
|
|
297
|
-
*/
|
|
195
|
+
/** `process` projects dotted keys to shell-safe env names; `storage` preserves them. */
|
|
298
196
|
keyMode?: 'process' | 'storage';
|
|
299
197
|
}
|
|
300
198
|
export declare function canCacheResolvedEnv(bundle: SecretsBundle, selectedKeys: Set<string>, keyMode: ResolveBundleOptions['keyMode']): boolean;
|
|
301
199
|
/**
|
|
302
|
-
* Apply
|
|
303
|
-
* the
|
|
304
|
-
* scoped lease env, so a naive fast-path return could silently defeat --keys
|
|
305
|
-
* and inject expired values. Mirrors the slow-path pre-checks in `resolveBundleEnv` /
|
|
306
|
-
* `readAndResolveBundleEnv` and returns a new env whose keys match the subset.
|
|
307
|
-
*
|
|
308
|
-
* Exported for tests; production callers reach it via the fast-path branch in
|
|
309
|
-
* `readAndResolveBundleEnv`.
|
|
200
|
+
* Apply --keys and --allow-expired to a broker snapshot so the fast path
|
|
201
|
+
* mirrors the slow path's gates. Exported for tests.
|
|
310
202
|
*/
|
|
311
203
|
export declare function filterAgentHitBySubsetAndExpiry(hit: {
|
|
312
204
|
bundle: SecretsBundle;
|
|
@@ -316,14 +208,8 @@ export declare function filterAgentHitBySubsetAndExpiry(hit: {
|
|
|
316
208
|
env: Record<string, string>;
|
|
317
209
|
};
|
|
318
210
|
/**
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
* yet. Silently applying them would inject the full remote env or an expired
|
|
322
|
-
* value, defeating the least-privilege intent, so we fail loud.
|
|
323
|
-
*
|
|
324
|
-
* Exported so `agents run --secrets bundle@host` and `agents secrets exec
|
|
325
|
-
* --device` share the exact same error text; the tests exercise this helper
|
|
326
|
-
* directly instead of driving the whole CLI.
|
|
211
|
+
* Fail loud when remote bundle resolution is asked for flags the SSH resolver
|
|
212
|
+
* does not yet thread. Exported so callers share the same error text.
|
|
327
213
|
*/
|
|
328
214
|
export declare function assertRemoteBundleFlagsUnsupported(bundleName: string, host: string, opts: {
|
|
329
215
|
keys?: string[];
|
|
@@ -343,16 +229,8 @@ export declare function resolveBundleEnv(bundle: SecretsBundle, _opts?: ResolveB
|
|
|
343
229
|
export { isHeadlessSecretsContext, isAgentInvocationContext } from './headless.js';
|
|
344
230
|
/**
|
|
345
231
|
* Read a bundle's metadata AND resolve its env in a single Touch ID prompt.
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
* (metadata read via `get-auth`, then secret values via `get-batch`) which
|
|
349
|
-
* surfaced as two consecutive Touch ID prompts. macOS does not honor
|
|
350
|
-
* "Always Allow" for items protected with `kSecAttrAccessControl`+biometry,
|
|
351
|
-
* so caching at the OS level was never an option. This collapses both reads
|
|
352
|
-
* into one `get-batch` call: we enumerate the bundle's secret items first
|
|
353
|
-
* (silent — `list` returns attrs only and does not trigger biometry) and
|
|
354
|
-
* include the metadata item in the same batch. One prompt, correctly scoped
|
|
355
|
-
* to the bundle name and caller.
|
|
232
|
+
* `readBundle` + `resolveBundleEnv` used to issue two LAContext calls (two
|
|
233
|
+
* prompts). This collapses them into one batch that includes the metadata item.
|
|
356
234
|
*/
|
|
357
235
|
export declare function readAndResolveBundleEnv(name: string, opts?: ResolveBundleOptions): {
|
|
358
236
|
bundle: SecretsBundle;
|
|
@@ -370,57 +248,30 @@ export interface RotateOptions {
|
|
|
370
248
|
meta?: Partial<VarMeta>;
|
|
371
249
|
}
|
|
372
250
|
/**
|
|
373
|
-
* Rotate a keychain-backed secret
|
|
374
|
-
*
|
|
375
|
-
* unless `clearMeta` or a `meta` patch is supplied.
|
|
251
|
+
* Rotate a keychain-backed secret. Errors if the key is absent; preserves meta
|
|
252
|
+
* unless cleared or patched.
|
|
376
253
|
*/
|
|
377
254
|
export declare function rotateBundleSecret(bundle: SecretsBundle, key: string, opts: RotateOptions): void;
|
|
378
255
|
/**
|
|
379
|
-
* Reconcile
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
* every value item carrying the ACL it was created with — and macOS gates each
|
|
384
|
-
* read on the ITEM's ACL, not the bundle's declared tier (spec SEC-19). Without
|
|
385
|
-
* this reconcile, `agents secrets policy <b> never` reports "silent" while the
|
|
386
|
-
* still-ACL'd value keeps popping Touch ID on every read, forever.
|
|
387
|
-
*
|
|
388
|
-
* hold/always -> never strips the biometry ACL (helper `set-no-acl`: delete+add)
|
|
389
|
-
* never -> hold/always re-attaches it (helper `set`)
|
|
390
|
-
*
|
|
391
|
-
* The current values are read in ONE batch, so the reconcile costs at most a
|
|
392
|
-
* single Touch ID — the last prompt a hold->never bundle will ever raise (a
|
|
393
|
-
* never->* flip reads silently, since the items are already no-ACL). No-op on the
|
|
394
|
-
* ACL to write for non-keychain backends (file/vault have no biometry concept),
|
|
395
|
-
* and a metadata-only write when the bundle has no keychain-backed values.
|
|
256
|
+
* Reconcile keychain value items to the bundle's current policy. macOS gates
|
|
257
|
+
* reads on each item's ACL, not the bundle's declared policy, so a policy change
|
|
258
|
+
* alone would leave stale ACLs. hold/always → never strips ACL; never → *
|
|
259
|
+
* re-attaches it. Non-keychain backends no-op.
|
|
396
260
|
*/
|
|
397
261
|
export declare function reAclBundleItems(bundle: SecretsBundle): void;
|
|
398
262
|
/** Options for renameBundle. */
|
|
399
263
|
export interface RenameOptions {
|
|
400
|
-
/**
|
|
264
|
+
/** Overwrite an existing destination bundle. */
|
|
401
265
|
force?: boolean;
|
|
402
266
|
}
|
|
403
267
|
/**
|
|
404
|
-
* Rename a bundle:
|
|
405
|
-
*
|
|
406
|
-
* Sequence is ordered so the source stays intact if anything in the copy
|
|
407
|
-
* phase fails:
|
|
408
|
-
* 1) read source, validate dest
|
|
409
|
-
* 2) purge dest if --force, refuse otherwise
|
|
410
|
-
* 3) copy each keychain value source -> dest
|
|
411
|
-
* 4) write new bundle metadata
|
|
412
|
-
* 5) delete the old per-key keychain items + old metadata
|
|
413
|
-
*
|
|
414
|
-
* Steps 1-4 are reversible. If 5 partially fails, running `rename` again is
|
|
415
|
-
* a safe no-op for the source items.
|
|
268
|
+
* Rename a bundle: copy metadata + keychain values to the new name, then delete
|
|
269
|
+
* the source. Steps are ordered so a copy-phase failure leaves the source intact.
|
|
416
270
|
*/
|
|
417
271
|
export declare function renameBundle(oldName: string, newName: string, opts?: RenameOptions): void;
|
|
418
272
|
/**
|
|
419
|
-
* The store (keychain or encrypted file)
|
|
420
|
-
*
|
|
421
|
-
* secretsKeychainItem) in the same store as the bundle's metadata, for `add` /
|
|
422
|
-
* `import` / `remove` / `delete`. Pass the bundle's resolved backend
|
|
423
|
-
* (`bundle.backend ?? 'keychain'`).
|
|
273
|
+
* The item store (keychain or encrypted file) for a bundle's per-key secrets.
|
|
274
|
+
* Pass the resolved backend (`bundle.backend ?? 'keychain'`).
|
|
424
275
|
*/
|
|
425
276
|
export declare function bundleItemStore(backend: SecretsBackend | undefined, opts?: {
|
|
426
277
|
noAcl?: boolean;
|