@phnx-labs/agents-cli 1.20.52 → 1.20.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -2
- package/README.md +12 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/cloud.d.ts +3 -0
- package/dist/commands/cloud.js +2 -1
- package/dist/commands/exec.js +65 -1
- package/dist/commands/feed.d.ts +29 -0
- package/dist/commands/feed.js +237 -32
- package/dist/commands/memory.d.ts +9 -0
- package/dist/commands/memory.js +164 -0
- package/dist/commands/message.d.ts +11 -6
- package/dist/commands/message.js +140 -5
- package/dist/commands/routines.js +12 -0
- package/dist/commands/secrets-migrate.d.ts +2 -1
- package/dist/commands/secrets-migrate.js +88 -13
- package/dist/commands/secrets.js +4 -1
- package/dist/commands/sessions.js +10 -1
- package/dist/commands/worktree.js +4 -2
- package/dist/index.js +16 -21
- package/dist/lib/agents.js +249 -17
- package/dist/lib/answer-router.d.ts +75 -0
- package/dist/lib/answer-router.js +149 -0
- package/dist/lib/ask-classifier.d.ts +71 -0
- package/dist/lib/ask-classifier.js +197 -0
- package/dist/lib/cloud/antigravity.d.ts +0 -2
- package/dist/lib/cloud/antigravity.js +2 -17
- package/dist/lib/cloud/codex.js +3 -18
- package/dist/lib/cloud/rush.js +3 -15
- package/dist/lib/cloud/stream.js +2 -0
- package/dist/lib/cloud/types.d.ts +21 -0
- package/dist/lib/cloud/types.js +81 -0
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +12 -2
- package/dist/lib/crabbox/lease.d.ts +13 -0
- package/dist/lib/crabbox/lease.js +11 -2
- package/dist/lib/crabbox/progress.d.ts +62 -0
- package/dist/lib/crabbox/progress.js +129 -0
- package/dist/lib/events.js +4 -1
- package/dist/lib/exec.js +19 -1
- package/dist/lib/feed-outcome.d.ts +101 -0
- package/dist/lib/feed-outcome.js +244 -0
- package/dist/lib/feed-policy.d.ts +30 -0
- package/dist/lib/feed-policy.js +133 -0
- package/dist/lib/feed.d.ts +127 -3
- package/dist/lib/feed.js +416 -40
- package/dist/lib/git.d.ts +17 -1
- package/dist/lib/git.js +20 -1
- package/dist/lib/hooks.js +522 -12
- package/dist/lib/hosts/passthrough.d.ts +3 -3
- package/dist/lib/hosts/passthrough.js +3 -4
- package/dist/lib/mailbox-gc.d.ts +22 -0
- package/dist/lib/mailbox-gc.js +161 -0
- package/dist/lib/mailbox.d.ts +26 -2
- package/dist/lib/mailbox.js +80 -5
- package/dist/lib/mcp.js +82 -0
- package/dist/lib/memory.d.ts +55 -0
- package/dist/lib/memory.js +274 -0
- package/dist/lib/notify.d.ts +16 -0
- package/dist/lib/notify.js +61 -0
- package/dist/lib/operator.d.ts +26 -0
- package/dist/lib/operator.js +107 -0
- package/dist/lib/plugins.d.ts +35 -0
- package/dist/lib/plugins.js +217 -0
- package/dist/lib/remote-agents-json.d.ts +14 -0
- package/dist/lib/remote-agents-json.js +94 -0
- package/dist/lib/resources/mcp.js +44 -0
- package/dist/lib/resources/memory.d.ts +15 -0
- package/dist/lib/resources/memory.js +46 -0
- package/dist/lib/resources/types.d.ts +2 -2
- package/dist/lib/runner.d.ts +43 -0
- package/dist/lib/runner.js +323 -74
- package/dist/lib/sandbox.js +6 -0
- package/dist/lib/secrets/bundles.js +38 -13
- package/dist/lib/secrets/icloud-import.d.ts +12 -3
- package/dist/lib/secrets/icloud-import.js +37 -7
- package/dist/lib/secrets/index.d.ts +106 -2
- package/dist/lib/secrets/index.js +603 -28
- package/dist/lib/session/active.d.ts +18 -0
- package/dist/lib/session/active.js +47 -17
- package/dist/lib/session/db.d.ts +9 -1
- package/dist/lib/session/db.js +18 -3
- package/dist/lib/session/discover.d.ts +13 -0
- package/dist/lib/session/discover.js +31 -0
- package/dist/lib/session/parse.d.ts +8 -0
- package/dist/lib/session/parse.js +42 -21
- package/dist/lib/session/remote-active.js +8 -89
- package/dist/lib/session/state.d.ts +11 -0
- package/dist/lib/session/state.js +37 -0
- package/dist/lib/session/tail.d.ts +23 -4
- package/dist/lib/session/tail.js +34 -16
- package/dist/lib/session/throughput.d.ts +30 -0
- package/dist/lib/session/throughput.js +86 -0
- package/dist/lib/shim-heal.d.ts +12 -3
- package/dist/lib/shim-heal.js +12 -6
- package/dist/lib/staleness/detectors/subagents.js +57 -3
- package/dist/lib/staleness/writers/hooks.js +7 -3
- package/dist/lib/staleness/writers/subagents.js +37 -6
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/subagents.d.ts +52 -0
- package/dist/lib/subagents.js +315 -12
- package/dist/lib/teams/worktree.d.ts +8 -0
- package/dist/lib/teams/worktree.js +8 -0
- package/dist/lib/types.d.ts +10 -2
- package/dist/lib/versions.js +38 -48
- package/package.json +4 -3
- package/scripts/postinstall.js +61 -1
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* the bundle/key boundary even for dotted bundle names like `hetzner.com`.
|
|
17
17
|
*/
|
|
18
18
|
import { deleteSyncedKeychainItem, getSyncedKeychainTokens, listSyncedKeychainItems, parseBundleValue, secretsKeychainItem, serializeRef, SECRETS_ITEM_PREFIX, } from './index.js';
|
|
19
|
-
import { BUNDLE_META_PREFIX, BUNDLE_NAME_PATTERN, ENV_KEY_PATTERN, bundleExists, bundleItemStore, bundlePolicy, keychainRef, readBundle, writeBundle, } from './bundles.js';
|
|
19
|
+
import { BUNDLE_META_PREFIX, BUNDLE_NAME_PATTERN, ENV_KEY_PATTERN, bundleExists, bundleItemStore, bundlePolicy, isLoaderOrInterpreterEnv, isReservedEnvName, keychainRef, readBundle, writeBundle, } from './bundles.js';
|
|
20
20
|
/**
|
|
21
21
|
* Group raw synced service names into per-bundle candidates. Pure — separated
|
|
22
22
|
* from discovery so the parsing rules are unit-testable without a keychain.
|
|
@@ -72,9 +72,12 @@ export function discoverSyncedBundles() {
|
|
|
72
72
|
* Values come from the synced secret items; the synced metadata item, when
|
|
73
73
|
* present, contributes the description, literal vars, and non-keychain refs
|
|
74
74
|
* (env:/file:/exec: refs carry no stored secret, so copying the ref preserves
|
|
75
|
-
* them exactly). Existing local keys are skipped unless `force`.
|
|
76
|
-
*
|
|
77
|
-
*
|
|
75
|
+
* them exactly). Existing local keys are skipped unless `force`. Keys the
|
|
76
|
+
* modern store refuses by policy (reserved / loader env names the pre-cutover
|
|
77
|
+
* store accepted) are reported as `unimportable` instead of aborting the whole
|
|
78
|
+
* bundle. With `purge`, only services whose value provably lives locally
|
|
79
|
+
* (imported now, or skipped-because-present) are deleted from iCloud — an
|
|
80
|
+
* unreadable or unimportable item is never destroyed.
|
|
78
81
|
*/
|
|
79
82
|
export function importSyncedBundle(candidate, opts = {}) {
|
|
80
83
|
const values = getSyncedKeychainTokens(candidate.services);
|
|
@@ -112,8 +115,21 @@ export function importSyncedBundle(candidate, opts = {}) {
|
|
|
112
115
|
let added = 0;
|
|
113
116
|
let skipped = 0;
|
|
114
117
|
const missing = [];
|
|
118
|
+
const unimportable = [];
|
|
119
|
+
// Services whose value now provably lives in the local store — imported this
|
|
120
|
+
// run, or skipped because the local bundle already carries the key. Only
|
|
121
|
+
// these may be purged; a missing or unimportable key's iCloud item is its
|
|
122
|
+
// only copy and must survive.
|
|
123
|
+
const purgeable = new Set();
|
|
124
|
+
const rejectedByPolicy = (key) => isLoaderOrInterpreterEnv(key) || isReservedEnvName(key);
|
|
115
125
|
// Keys with a synced secret item: re-store the value device-locally.
|
|
116
126
|
for (const key of candidate.keys) {
|
|
127
|
+
// The pre-cutover store accepted keys the modern one refuses (writeBundle
|
|
128
|
+
// throws) — skip them instead of aborting the whole bundle.
|
|
129
|
+
if (rejectedByPolicy(key)) {
|
|
130
|
+
unimportable.push(key);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
117
133
|
const value = values.get(secretsKeychainItem(candidate.name, key));
|
|
118
134
|
if (value === undefined) {
|
|
119
135
|
missing.push(key);
|
|
@@ -121,6 +137,7 @@ export function importSyncedBundle(candidate, opts = {}) {
|
|
|
121
137
|
}
|
|
122
138
|
if (!opts.force && key in bundle.vars) {
|
|
123
139
|
skipped++;
|
|
140
|
+
purgeable.add(secretsKeychainItem(candidate.name, key));
|
|
124
141
|
continue;
|
|
125
142
|
}
|
|
126
143
|
if (opts.allPlaintext) {
|
|
@@ -130,6 +147,7 @@ export function importSyncedBundle(candidate, opts = {}) {
|
|
|
130
147
|
store.set(secretsKeychainItem(candidate.name, key), value);
|
|
131
148
|
bundle.vars[key] = keychainRef(key);
|
|
132
149
|
}
|
|
150
|
+
purgeable.add(secretsKeychainItem(candidate.name, key));
|
|
133
151
|
added++;
|
|
134
152
|
}
|
|
135
153
|
// Vars declared only in the synced metadata: literals and non-keychain refs
|
|
@@ -140,6 +158,11 @@ export function importSyncedBundle(candidate, opts = {}) {
|
|
|
140
158
|
continue;
|
|
141
159
|
if (candidate.keys.includes(key))
|
|
142
160
|
continue; // the secret item already covered it
|
|
161
|
+
if (rejectedByPolicy(key)) {
|
|
162
|
+
if (!unimportable.includes(key))
|
|
163
|
+
unimportable.push(key);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
143
166
|
let parsed;
|
|
144
167
|
try {
|
|
145
168
|
parsed = parseBundleValue(raw);
|
|
@@ -160,14 +183,21 @@ export function importSyncedBundle(candidate, opts = {}) {
|
|
|
160
183
|
added++;
|
|
161
184
|
}
|
|
162
185
|
writeBundle(bundle);
|
|
186
|
+
// The metadata item is only a projection of what was just written locally —
|
|
187
|
+
// safe to purge once writeBundle has succeeded, UNLESS it still names keys
|
|
188
|
+
// that never made it over (missing/unimportable): then it stays as the only
|
|
189
|
+
// record of them.
|
|
190
|
+
if (metaJson !== undefined && missing.length === 0 && unimportable.length === 0) {
|
|
191
|
+
purgeable.add(metaService);
|
|
192
|
+
}
|
|
163
193
|
let purged = 0;
|
|
164
194
|
if (opts.purge) {
|
|
165
195
|
for (const svc of candidate.services) {
|
|
166
|
-
if (!
|
|
167
|
-
continue;
|
|
196
|
+
if (!purgeable.has(svc))
|
|
197
|
+
continue;
|
|
168
198
|
if (deleteSyncedKeychainItem(svc))
|
|
169
199
|
purged++;
|
|
170
200
|
}
|
|
171
201
|
}
|
|
172
|
-
return { name: candidate.name, added, skipped, missing, purged };
|
|
202
|
+
return { name: candidate.name, added, skipped, missing, unimportable, purged };
|
|
173
203
|
}
|
|
@@ -74,7 +74,11 @@ export declare function secretsKeychainItem(bundle: string, key: string): string
|
|
|
74
74
|
export interface KeychainBackend {
|
|
75
75
|
has(item: string): boolean;
|
|
76
76
|
get(item: string): string;
|
|
77
|
-
|
|
77
|
+
/** `opts.noAcl` mirrors setKeychainToken's no-ACL write path so tests can
|
|
78
|
+
* assert which path a write took; in-memory backends may ignore it. */
|
|
79
|
+
set(item: string, value: string, opts?: {
|
|
80
|
+
noAcl?: boolean;
|
|
81
|
+
}): void;
|
|
78
82
|
delete(item: string): boolean;
|
|
79
83
|
list(prefix: string): string[];
|
|
80
84
|
}
|
|
@@ -85,6 +89,103 @@ export declare function setKeychainBackendForTest(b: KeychainBackend | null): Ke
|
|
|
85
89
|
* with an in-memory backend there is no real keychain to dedup, so the broker
|
|
86
90
|
* fast-path must not engage. Always false in production (`backend` is null). */
|
|
87
91
|
export declare function isKeychainBackendOverridden(): boolean;
|
|
92
|
+
export declare const HMAC_KEY_ITEM = "agents-cli.hmackey";
|
|
93
|
+
/** Force hashed service names on with a fixed key (test only). Pass null to
|
|
94
|
+
* restore lazy production resolution. Composes with setKeychainBackendForTest
|
|
95
|
+
* so unit tests exercise the exact transform production uses. */
|
|
96
|
+
export declare function setKeychainServiceHashingForTest(key: Buffer | null): void;
|
|
97
|
+
/**
|
|
98
|
+
* Run `fn` with service-name hashing suspended: every primitive uses the
|
|
99
|
+
* literal names it is given. For migration flows ONLY — they enumerate raw
|
|
100
|
+
* names from the helper (which may be pre-re-key cleartext leftovers) and must
|
|
101
|
+
* read/delete those exact items, not their hashed transforms.
|
|
102
|
+
*/
|
|
103
|
+
export declare function withRawKeychainServiceNames<T>(fn: () => T): T;
|
|
104
|
+
/** The hashed (storage) service name for a cleartext item name. Exported for
|
|
105
|
+
* the re-key migration and tests; runtime callers go through the primitives,
|
|
106
|
+
* which apply this transparently. */
|
|
107
|
+
export declare function hashedServiceName(item: string, key: Buffer): string;
|
|
108
|
+
/**
|
|
109
|
+
* The storage-layer service name for `item`: hashed when hashing is active,
|
|
110
|
+
* the item itself otherwise. For callers that mix helper-enumerated
|
|
111
|
+
* (already-hashed) names with computed cleartext names in one lookup map —
|
|
112
|
+
* see readAndResolveBundleEnv.
|
|
113
|
+
*/
|
|
114
|
+
export declare function keychainServiceAlias(item: string): string;
|
|
115
|
+
/** One re-keyed (or failed) item, by its old cleartext service name. */
|
|
116
|
+
export interface RekeyPlanItem {
|
|
117
|
+
oldService: string;
|
|
118
|
+
newService: string;
|
|
119
|
+
/** Preserve the no-ACL write path for `never`-policy bundle items. */
|
|
120
|
+
noAcl: boolean;
|
|
121
|
+
/** Replacement payload (bundle metadata gets `name` injected); absent = copy verbatim. */
|
|
122
|
+
payload?: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Build the old→new mapping for a set of cleartext services. Bundle metadata
|
|
126
|
+
* is parsed first to (a) recover each bundle's prompt policy — the persisted
|
|
127
|
+
* `tier` token, where `none`/`never` means the item must be re-written through
|
|
128
|
+
* the no-ACL path — and (b) inject the cleartext `name` into the JSON, because
|
|
129
|
+
* after hashing the service name can no longer carry it (listBundles reads it
|
|
130
|
+
* back from the payload). A value item's tier is resolved from its bundle's
|
|
131
|
+
* metadata PAYLOAD in `values` — under the cleartext metadata name or its
|
|
132
|
+
* hashed transform — never from the metadata item being part of the same
|
|
133
|
+
* `services` batch: a --prefix run can scope a bundle's value items alone, and
|
|
134
|
+
* rekeyServiceNames supplies the out-of-scope metadata reads (see the
|
|
135
|
+
* supplemental batch there). Exported for unit tests.
|
|
136
|
+
*/
|
|
137
|
+
export declare function computeRekeyPlan(services: string[], values: Map<string, string>, key: Buffer): {
|
|
138
|
+
items: RekeyPlanItem[];
|
|
139
|
+
unreadable: string[];
|
|
140
|
+
};
|
|
141
|
+
/** Outcome of one rekeyServiceNames run. */
|
|
142
|
+
export interface RekeyReport {
|
|
143
|
+
/** Old cleartext services whose items now live under hashed names. */
|
|
144
|
+
migrated: string[];
|
|
145
|
+
failed: Array<{
|
|
146
|
+
item: string;
|
|
147
|
+
detail: string;
|
|
148
|
+
}>;
|
|
149
|
+
/** True when hashed naming is on after this run. */
|
|
150
|
+
activated: boolean;
|
|
151
|
+
nothingToDo: boolean;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The one-time re-key: move every cleartext-named `agents-cli.*` item to its
|
|
155
|
+
* hashed service name. Composed entirely from the existing helper primitives —
|
|
156
|
+
* no new Swift command:
|
|
157
|
+
*
|
|
158
|
+
* 1. Enumerate cleartext services (silent) and batch-read every value behind
|
|
159
|
+
* ONE Touch ID (`get-batch`; readItem also sweeps legacy/orphaned copies).
|
|
160
|
+
* 2. Write each hashed copy (`set`/`set-no-acl` never prompt), preserving
|
|
161
|
+
* the no-ACL tier for `never`-policy bundles.
|
|
162
|
+
* 3. Batch-verify every copy round-trips (second Touch ID).
|
|
163
|
+
* 4. Only then activate hashing (sentinel + pendingDeletes) and delete the
|
|
164
|
+
* old items (silent).
|
|
165
|
+
*
|
|
166
|
+
* Add-before-delete throughout: a cancel/crash/failure anywhere before step 4
|
|
167
|
+
* leaves every old item intact and hashing OFF — same rationale as the
|
|
168
|
+
* helper's migrate-orphans, which is also why no pre-write backup is taken.
|
|
169
|
+
* On ANY per-item failure nothing is deleted and the sentinel stays off
|
|
170
|
+
* (all-or-nothing activation); the report names every failed item. A crash
|
|
171
|
+
* between the sentinel write and the deletes is resumed silently by the next
|
|
172
|
+
* process (pendingDeletes). Idempotent: re-running converges.
|
|
173
|
+
*/
|
|
174
|
+
export declare function rekeyServiceNames(opts?: {
|
|
175
|
+
prefixes?: string[];
|
|
176
|
+
announce?: boolean;
|
|
177
|
+
log?: (line: string) => void;
|
|
178
|
+
}): RekeyReport;
|
|
179
|
+
/** Re-key state snapshot for `agents secrets rekey --status`. */
|
|
180
|
+
export declare function rekeyStatus(): {
|
|
181
|
+
migrated: boolean;
|
|
182
|
+
hasKey: boolean;
|
|
183
|
+
pendingDeletes: number;
|
|
184
|
+
cleartext: string[];
|
|
185
|
+
/** False when the enumeration probe fails (locked keybag) — the cleartext
|
|
186
|
+
* count is then meaningless. Only probeable once the key record exists. */
|
|
187
|
+
enumerationOk: boolean;
|
|
188
|
+
};
|
|
88
189
|
/** Check if a keychain/keyring item exists. Never prompts for biometry. */
|
|
89
190
|
export declare function hasKeychainToken(item: string): boolean;
|
|
90
191
|
/**
|
|
@@ -129,7 +230,10 @@ export declare function deleteKeychainToken(item: string): boolean;
|
|
|
129
230
|
* and the direct file enumeration return the same items.
|
|
130
231
|
*/
|
|
131
232
|
export declare function keychainUsesFileFallback(): boolean;
|
|
132
|
-
/** Enumerate keychain/keyring item names starting with the given prefix.
|
|
233
|
+
/** Enumerate keychain/keyring item names starting with the given prefix.
|
|
234
|
+
* With hashed service names active, the two sub-namespace prefixes callers
|
|
235
|
+
* use (bundle metadata; one bundle's value items) are mapped to their hashed
|
|
236
|
+
* shapes — the returned names are then storage (opaque) names. */
|
|
133
237
|
export declare function listKeychainItems(prefix: string): string[];
|
|
134
238
|
/**
|
|
135
239
|
* Enumerate ONLY legacy file-based-keychain item names with the given prefix —
|