@phnx-labs/agents-cli 1.20.74 → 1.20.77
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 +1055 -0
- package/README.md +36 -10
- package/dist/bin/agents +0 -0
- package/dist/commands/apply.js +21 -3
- package/dist/commands/attach.d.ts +10 -0
- package/dist/commands/attach.js +41 -0
- package/dist/commands/computer.js +3 -3
- package/dist/commands/detach-core.d.ts +46 -0
- package/dist/commands/detach-core.js +61 -0
- package/dist/commands/detach.d.ts +5 -0
- package/dist/commands/detach.js +171 -0
- package/dist/commands/doctor.js +18 -0
- package/dist/commands/exec.js +83 -13
- package/dist/commands/feed.d.ts +7 -1
- package/dist/commands/feed.js +47 -4
- package/dist/commands/go.d.ts +9 -2
- package/dist/commands/go.js +16 -6
- package/dist/commands/harness.d.ts +14 -0
- package/dist/commands/harness.js +145 -0
- package/dist/commands/import.js +38 -8
- package/dist/commands/inspect.js +5 -2
- package/dist/commands/profiles.d.ts +20 -0
- package/dist/commands/profiles.js +68 -21
- package/dist/commands/repo.js +14 -0
- package/dist/commands/routines.js +286 -15
- package/dist/commands/secrets.js +102 -37
- package/dist/commands/sessions-browser.js +12 -2
- package/dist/commands/sessions-export.js +19 -5
- package/dist/commands/sessions-migrate.d.ts +29 -0
- package/dist/commands/sessions-migrate.js +596 -0
- package/dist/commands/sessions-picker.d.ts +14 -1
- package/dist/commands/sessions-picker.js +184 -16
- package/dist/commands/sessions.d.ts +56 -14
- package/dist/commands/sessions.js +322 -62
- package/dist/commands/setup-computer.js +2 -2
- package/dist/commands/ssh.d.ts +13 -0
- package/dist/commands/ssh.js +110 -37
- package/dist/commands/status.js +10 -2
- package/dist/commands/versions.js +15 -6
- package/dist/commands/view.d.ts +5 -0
- package/dist/commands/view.js +24 -4
- package/dist/commands/watchdog.d.ts +7 -2
- package/dist/commands/watchdog.js +73 -57
- package/dist/index.js +59 -7
- package/dist/lib/activity.d.ts +17 -6
- package/dist/lib/activity.js +466 -40
- package/dist/lib/actor.d.ts +50 -0
- package/dist/lib/actor.js +166 -0
- package/dist/lib/agent-spec/provider.js +2 -1
- package/dist/lib/agent-spec/resolve.js +19 -5
- package/dist/lib/agent-spec/types.d.ts +9 -1
- package/dist/lib/agents.d.ts +21 -0
- package/dist/lib/agents.js +70 -6
- package/dist/lib/cloud/codex.d.ts +2 -0
- package/dist/lib/cloud/codex.js +14 -3
- package/dist/lib/cloud/session-index.d.ts +32 -0
- package/dist/lib/cloud/session-index.js +58 -0
- package/dist/lib/cloud/store.d.ts +7 -0
- package/dist/lib/cloud/store.js +25 -0
- package/dist/lib/config-transfer.js +4 -0
- package/dist/lib/daemon.d.ts +10 -1
- package/dist/lib/daemon.js +114 -11
- package/dist/lib/devices/connect.d.ts +15 -1
- package/dist/lib/devices/connect.js +15 -1
- package/dist/lib/devices/fleet.d.ts +37 -1
- package/dist/lib/devices/fleet.js +36 -2
- package/dist/lib/devices/health-report.d.ts +38 -0
- package/dist/lib/devices/health-report.js +214 -0
- package/dist/lib/devices/health.js +4 -1
- package/dist/lib/devices/reachability.d.ts +31 -0
- package/dist/lib/devices/reachability.js +40 -0
- package/dist/lib/devices/registry.d.ts +33 -0
- package/dist/lib/devices/registry.js +37 -0
- package/dist/lib/devices/resolve-target.d.ts +15 -27
- package/dist/lib/devices/resolve-target.js +63 -102
- package/dist/lib/devices/sync.d.ts +18 -0
- package/dist/lib/devices/sync.js +23 -1
- package/dist/lib/devices/tailscale.d.ts +3 -0
- package/dist/lib/devices/tailscale.js +1 -0
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/exec.d.ts +82 -0
- package/dist/lib/exec.js +218 -6
- package/dist/lib/feed-post.d.ts +63 -0
- package/dist/lib/feed-post.js +204 -0
- package/dist/lib/feed.d.ts +7 -1
- package/dist/lib/feed.js +96 -6
- package/dist/lib/fleet/apply.d.ts +32 -2
- package/dist/lib/fleet/apply.js +97 -10
- package/dist/lib/fleet/types.d.ts +11 -0
- package/dist/lib/fs-walk.d.ts +13 -0
- package/dist/lib/fs-walk.js +16 -7
- package/dist/lib/heal.d.ts +7 -4
- package/dist/lib/heal.js +10 -22
- package/dist/lib/hooks.d.ts +1 -0
- package/dist/lib/hooks.js +156 -0
- package/dist/lib/hosts/dispatch.d.ts +16 -0
- package/dist/lib/hosts/dispatch.js +26 -4
- package/dist/lib/hosts/passthrough.js +9 -2
- package/dist/lib/hosts/reconnect.d.ts +78 -0
- package/dist/lib/hosts/reconnect.js +127 -0
- package/dist/lib/hosts/registry.d.ts +75 -14
- package/dist/lib/hosts/registry.js +205 -30
- package/dist/lib/hosts/remote-cmd.d.ts +11 -0
- package/dist/lib/hosts/remote-cmd.js +32 -8
- package/dist/lib/hosts/remote-session-id.d.ts +45 -0
- package/dist/lib/hosts/remote-session-id.js +84 -0
- package/dist/lib/hosts/run-target.d.ts +17 -5
- package/dist/lib/hosts/run-target.js +30 -10
- package/dist/lib/hosts/session-index.d.ts +18 -1
- package/dist/lib/hosts/session-index.js +40 -5
- package/dist/lib/hosts/session-marker.d.ts +33 -0
- package/dist/lib/hosts/session-marker.js +51 -0
- package/dist/lib/hosts/tasks.d.ts +8 -4
- package/dist/lib/import.d.ts +2 -0
- package/dist/lib/import.js +35 -2
- package/dist/lib/menubar/install-menubar.d.ts +29 -0
- package/dist/lib/menubar/install-menubar.js +59 -1
- package/dist/lib/menubar/notify-desktop.d.ts +44 -0
- package/dist/lib/menubar/notify-desktop.js +78 -0
- package/dist/lib/migrate.d.ts +16 -0
- package/dist/lib/migrate.js +36 -0
- package/dist/lib/models.d.ts +27 -0
- package/dist/lib/models.js +54 -1
- package/dist/lib/overdue.d.ts +6 -2
- package/dist/lib/overdue.js +10 -36
- package/dist/lib/picker.js +4 -1
- package/dist/lib/platform/process.d.ts +17 -0
- package/dist/lib/platform/process.js +70 -0
- package/dist/lib/profiles-presets.js +9 -7
- package/dist/lib/profiles.d.ts +31 -0
- package/dist/lib/profiles.js +70 -0
- package/dist/lib/pty-server.d.ts +2 -10
- package/dist/lib/pty-server.js +4 -38
- package/dist/lib/registry.d.ts +1 -1
- package/dist/lib/registry.js +48 -8
- package/dist/lib/rotate.d.ts +18 -0
- package/dist/lib/rotate.js +28 -0
- package/dist/lib/routine-notify.d.ts +76 -0
- package/dist/lib/routine-notify.js +190 -0
- package/dist/lib/routines-placement.d.ts +38 -0
- package/dist/lib/routines-placement.js +79 -0
- package/dist/lib/routines-project.d.ts +97 -0
- package/dist/lib/routines-project.js +349 -0
- package/dist/lib/routines.d.ts +94 -0
- package/dist/lib/routines.js +93 -9
- package/dist/lib/runner.d.ts +12 -2
- package/dist/lib/runner.js +242 -20
- package/dist/lib/sandbox.js +10 -0
- package/dist/lib/secrets/account-token.d.ts +20 -0
- package/dist/lib/secrets/account-token.js +64 -0
- package/dist/lib/secrets/agent.d.ts +74 -4
- package/dist/lib/secrets/agent.js +181 -107
- package/dist/lib/secrets/bundles.d.ts +54 -6
- package/dist/lib/secrets/bundles.js +230 -34
- package/dist/lib/secrets/index.d.ts +26 -3
- package/dist/lib/secrets/index.js +42 -12
- package/dist/lib/secrets/rc-hygiene.d.ts +55 -0
- package/dist/lib/secrets/rc-hygiene.js +154 -0
- package/dist/lib/secrets/remote.d.ts +7 -4
- package/dist/lib/secrets/remote.js +7 -4
- package/dist/lib/secrets/session-store.d.ts +6 -2
- package/dist/lib/secrets/session-store.js +65 -15
- package/dist/lib/secrets/sync-commands.d.ts +21 -0
- package/dist/lib/secrets/sync-commands.js +21 -0
- package/dist/lib/secrets/unlock-hints.d.ts +27 -0
- package/dist/lib/secrets/unlock-hints.js +36 -0
- package/dist/lib/self-update.d.ts +23 -2
- package/dist/lib/self-update.js +86 -5
- package/dist/lib/session/active.d.ts +124 -7
- package/dist/lib/session/active.js +390 -56
- package/dist/lib/session/cloud.js +2 -1
- package/dist/lib/session/db.d.ts +54 -0
- package/dist/lib/session/db.js +320 -19
- package/dist/lib/session/detached.d.ts +30 -0
- package/dist/lib/session/detached.js +92 -0
- package/dist/lib/session/discover.d.ts +478 -3
- package/dist/lib/session/discover.js +1740 -472
- package/dist/lib/session/fork.js +2 -1
- package/dist/lib/session/hook-sessions.d.ts +43 -0
- package/dist/lib/session/hook-sessions.js +135 -0
- package/dist/lib/session/linear.d.ts +6 -0
- package/dist/lib/session/linear.js +55 -0
- package/dist/lib/session/migrate-targets.d.ts +65 -0
- package/dist/lib/session/migrate-targets.js +94 -0
- package/dist/lib/session/migrations.d.ts +37 -0
- package/dist/lib/session/migrations.js +60 -0
- package/dist/lib/session/parse.d.ts +18 -0
- package/dist/lib/session/parse.js +141 -32
- package/dist/lib/session/pid-registry.d.ts +34 -2
- package/dist/lib/session/pid-registry.js +49 -2
- package/dist/lib/session/prompt.d.ts +7 -0
- package/dist/lib/session/prompt.js +37 -0
- package/dist/lib/session/provenance.d.ts +7 -0
- package/dist/lib/session/render.d.ts +7 -2
- package/dist/lib/session/render.js +40 -26
- package/dist/lib/session/short-id.d.ts +17 -0
- package/dist/lib/session/short-id.js +20 -0
- package/dist/lib/session/state.d.ts +4 -0
- package/dist/lib/session/state.js +99 -13
- package/dist/lib/session/types.d.ts +25 -1
- package/dist/lib/session/types.js +8 -0
- package/dist/lib/shims.d.ts +1 -1
- package/dist/lib/shims.js +20 -6
- package/dist/lib/sqlite.d.ts +3 -2
- package/dist/lib/sqlite.js +27 -4
- package/dist/lib/staleness/detectors/commands.js +1 -1
- package/dist/lib/staleness/detectors/workflows.js +13 -2
- package/dist/lib/staleness/writers/commands.js +7 -7
- package/dist/lib/staleness/writers/workflows.d.ts +4 -2
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +3 -0
- package/dist/lib/state.d.ts +8 -4
- package/dist/lib/state.js +21 -37
- package/dist/lib/teams/agents.d.ts +19 -10
- package/dist/lib/teams/agents.js +40 -39
- package/dist/lib/types.d.ts +47 -2
- package/dist/lib/usage.d.ts +33 -1
- package/dist/lib/usage.js +172 -12
- package/dist/lib/versions.d.ts +9 -2
- package/dist/lib/versions.js +37 -8
- package/dist/lib/watchdog/log.d.ts +43 -0
- package/dist/lib/watchdog/log.js +69 -0
- package/dist/lib/watchdog/routine.d.ts +44 -0
- package/dist/lib/watchdog/routine.js +69 -0
- package/dist/lib/watchdog/runner.d.ts +51 -7
- package/dist/lib/watchdog/runner.js +239 -64
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdog.js +31 -16
- package/dist/lib/workflows.d.ts +16 -0
- package/dist/lib/workflows.js +110 -1
- package/package.json +1 -1
|
@@ -40,6 +40,7 @@ export declare const DEFAULT_TTL_MS: number;
|
|
|
40
40
|
* spawnSync argv (unlike a NUL byte); `status` hides these entries.
|
|
41
41
|
*/
|
|
42
42
|
export declare const META_CACHE_PREFIX = "!meta:";
|
|
43
|
+
export { SYNC_GET_CMD, SYNC_PING_CMD, SYNC_LOCK_CMD } from './sync-commands.js';
|
|
43
44
|
/**
|
|
44
45
|
* Decide whether a persistent broker should self-heal onto freshly-installed
|
|
45
46
|
* code (exit so launchd relaunches it). Only when the store is EMPTY: exiting
|
|
@@ -68,12 +69,14 @@ export interface StoredBundle {
|
|
|
68
69
|
env: Record<string, string>;
|
|
69
70
|
/** epoch ms; the entry is gone once Date.now() passes this. */
|
|
70
71
|
expiresAt: number;
|
|
72
|
+
harness: string;
|
|
71
73
|
}
|
|
72
74
|
/** One unlocked bundle as reported by `status`. */
|
|
73
75
|
export interface AgentStatusEntry {
|
|
74
76
|
name: string;
|
|
75
77
|
expiresAt: number;
|
|
76
78
|
keyCount: number;
|
|
79
|
+
harness: string;
|
|
77
80
|
}
|
|
78
81
|
/**
|
|
79
82
|
* Read the current broker capability token, or null if none is present. Clients
|
|
@@ -105,10 +108,12 @@ export type Request = {
|
|
|
105
108
|
} | {
|
|
106
109
|
cmd: 'get';
|
|
107
110
|
name: string;
|
|
111
|
+
harness?: string;
|
|
108
112
|
token?: string;
|
|
109
113
|
} | {
|
|
110
114
|
cmd: 'load';
|
|
111
115
|
name: string;
|
|
116
|
+
harness?: string;
|
|
112
117
|
bundle: SecretsBundle;
|
|
113
118
|
env: Record<string, string>;
|
|
114
119
|
ttlMs: number;
|
|
@@ -165,6 +170,7 @@ export type Response = {
|
|
|
165
170
|
* exiting. Pure + exported for unit testing.
|
|
166
171
|
*/
|
|
167
172
|
export declare function realBundleCount(store: Map<string, StoredBundle>): number;
|
|
173
|
+
export declare function scopedBundleKey(name: string, harness: string): string;
|
|
168
174
|
export declare function handleAgentRequest(store: Map<string, StoredBundle>, req: Request, now?: number): Response;
|
|
169
175
|
/**
|
|
170
176
|
* Decide whether a `watch-lock` helper line should wipe the in-memory store.
|
|
@@ -232,15 +238,61 @@ export declare function startHostedBroker(): Promise<{
|
|
|
232
238
|
/** True if a broker socket exists at all. Cheap; gates the sync read so the
|
|
233
239
|
* never-unlocked path stays a single stat. */
|
|
234
240
|
export declare function agentSocketExists(): boolean;
|
|
241
|
+
/**
|
|
242
|
+
* Spawn one of the `__secrets-*` sync clients and return its result.
|
|
243
|
+
*
|
|
244
|
+
* These three call sites used to hand-roll `spawnSync(process.execPath, ['-e',
|
|
245
|
+
* <inline node program>, …])`. That is correct only for a JS install, where
|
|
246
|
+
* `process.execPath` is `node`. Since 1.20.53 the shipped macOS `agents` is a
|
|
247
|
+
* **bun-compiled Mach-O**, where `process.execPath` is the CLI binary itself —
|
|
248
|
+
* so the spawn became `agents -e <program> …`, which commander rejects with
|
|
249
|
+
* `error: unknown option '-e'` and a non-zero exit. Every sync client then took
|
|
250
|
+
* its own failure path (`agentGetSync` → null, `agentReachableSync` → false,
|
|
251
|
+
* `agentEvictSync` → no-op), which reads as "broker down" and falls through to
|
|
252
|
+
* a real keychain read. Net effect on the standalone binary: the broker cache
|
|
253
|
+
* was never hit, so the `daily` policy's one-prompt-per-7d never applied and
|
|
254
|
+
* every bundle read re-popped Touch ID.
|
|
255
|
+
*
|
|
256
|
+
* Same defect class as the broker-launch bug fixed in 1.20.56 (see cliSpawn's
|
|
257
|
+
* doc comment); these three sites were simply never converted. Routing them
|
|
258
|
+
* through `getCliLaunch` fixes both install shapes at once and keeps a single
|
|
259
|
+
* code path.
|
|
260
|
+
*
|
|
261
|
+
* The subcommands are top-level `__secrets-*` tokens intercepted in index.ts
|
|
262
|
+
* BEFORE commander and before the CLI's startup work, exactly like
|
|
263
|
+
* `__daemon-run` and `__vault-age-helper`. That is load-bearing, not cosmetic:
|
|
264
|
+
* a normal command path runs `checkForUpdates()` and `spawnDetachedSync()` on
|
|
265
|
+
* every invocation (index.ts), so registering these as ordinary hidden
|
|
266
|
+
* subcommands would fire an update check and spawn a detached background sync
|
|
267
|
+
* on every cache hit — turning the hot read path into a process fork storm.
|
|
268
|
+
*/
|
|
269
|
+
export declare function syncClientLaunch(sub: string[], agentsBin?: string): {
|
|
270
|
+
command: string;
|
|
271
|
+
args: string[];
|
|
272
|
+
};
|
|
235
273
|
/**
|
|
236
274
|
* Synchronous read for the hot path. Returns the cached resolved bundle, or
|
|
237
275
|
* null if the agent isn't running / doesn't hold this bundle / anything fails
|
|
238
276
|
* (soft — caller falls through to the real keychain). macOS only.
|
|
239
277
|
*/
|
|
240
|
-
export declare function agentGetSync(name: string): {
|
|
278
|
+
export declare function agentGetSync(name: string, harness?: string): {
|
|
241
279
|
bundle: SecretsBundle;
|
|
242
280
|
env: Record<string, string>;
|
|
243
281
|
} | null;
|
|
282
|
+
/**
|
|
283
|
+
* Last non-empty line of a child's stdout — the payload line.
|
|
284
|
+
*
|
|
285
|
+
* The inline `node -e` client this replaced was a bare node process that could
|
|
286
|
+
* only ever emit its own JSON. The replacement boots the real CLI, so anything
|
|
287
|
+
* the CLI prints on the way up shares that stream. Today the known chatter (the
|
|
288
|
+
* `~/.agents/ is N commits behind` notice) correctly goes to stderr, but a
|
|
289
|
+
* single future stdout write anywhere in startup would make `JSON.parse` throw
|
|
290
|
+
* and turn every cache hit back into a silent miss — i.e. quietly reintroduce
|
|
291
|
+
* the Touch-ID-on-every-read bug this fix closes, with no failing test to catch
|
|
292
|
+
* it. Anchoring on the last line makes the payload the terminator of the
|
|
293
|
+
* stream rather than the whole of it, so preceding chatter is inert.
|
|
294
|
+
*/
|
|
295
|
+
export declare function lastLine(stdout: string): string;
|
|
244
296
|
/**
|
|
245
297
|
* Synchronous liveness check: is a broker actually LISTENING and answering (not
|
|
246
298
|
* just a lingering socket file)? Used to decide whether the auto-cache may take
|
|
@@ -259,6 +311,25 @@ export declare function agentReachableSync(): boolean;
|
|
|
259
311
|
* macOS only.
|
|
260
312
|
*/
|
|
261
313
|
export declare function agentEvictSync(name: string): void;
|
|
314
|
+
/** Body of `__secrets-get <name>`. Prints `{bundle, env}` as JSON on a
|
|
315
|
+
* cache hit. Exit 0 = hit, 3 = miss or broker down. */
|
|
316
|
+
export declare function runAgentGetSync(name: string, harness?: string): Promise<number>;
|
|
317
|
+
/** Body of `__secrets-ping`. Exit 0 = a broker is listening and speaking
|
|
318
|
+
* our protocol, 3 = nothing there. Deliberately does NOT gate on
|
|
319
|
+
* PROTOCOL_VERSION: this only decides whether the auto-cache may take the
|
|
320
|
+
* synchronous warm path, and a version-skewed broker still answers reads. That
|
|
321
|
+
* matches the inline program this replaced. */
|
|
322
|
+
export declare function runAgentPingSync(): Promise<number>;
|
|
323
|
+
/** Body of `__secrets-lock <name>`. Best-effort evict; exit 0 even when no
|
|
324
|
+
* broker answers, so a missing broker never fails the mutating write that
|
|
325
|
+
* triggered it (agentEvictSync discards this either way).
|
|
326
|
+
*
|
|
327
|
+
* An empty name is refused rather than forwarded: the broker treats a nameless
|
|
328
|
+
* lock as lock-ALL (handleAgentRequest), so a bare `__secrets-lock` would wipe
|
|
329
|
+
* every held bundle and re-prompt Touch ID for each. That was unreachable while
|
|
330
|
+
* this was an inline `node -e` string; as a top-level argv token it is one typo
|
|
331
|
+
* away, and agentEvictSync only ever locks by name. */
|
|
332
|
+
export declare function runAgentLockSync(name: string): Promise<number>;
|
|
262
333
|
/**
|
|
263
334
|
* Read the cached `secrets list` metadata snapshot for the given keychain
|
|
264
335
|
* name-set hash, or null on miss / no broker / off-darwin. Reuses the value
|
|
@@ -321,7 +392,7 @@ export declare function clampHoldMs(v: unknown): number;
|
|
|
321
392
|
* The worker reuses the robust `ensureAgentRunning` path (spawn-then-ping) rather
|
|
322
393
|
* than a tight inline retry loop. Best-effort; never throws. macOS only.
|
|
323
394
|
*/
|
|
324
|
-
export declare function agentAutoLoadSync(name: string, bundle: SecretsBundle, env: Record<string, string>, ttlMs: number): void;
|
|
395
|
+
export declare function agentAutoLoadSync(name: string, bundle: SecretsBundle, env: Record<string, string>, ttlMs: number, harness?: string): void;
|
|
325
396
|
/**
|
|
326
397
|
* Body of the hidden `secrets _agent-load` worker. Reads one `{name, bundle,
|
|
327
398
|
* env, ttlMs}` payload from stdin, ensures the broker is up (robust, generous
|
|
@@ -336,7 +407,7 @@ export declare function agentAutoLoadSync(name: string, bundle: SecretsBundle, e
|
|
|
336
407
|
*/
|
|
337
408
|
export declare function runAgentLoadFromStdin(): Promise<void>;
|
|
338
409
|
/** Store a resolved bundle in the broker. Returns false on transport failure. */
|
|
339
|
-
export declare function agentLoad(name: string, bundle: SecretsBundle, env: Record<string, string>, ttlMs: number): Promise<boolean>;
|
|
410
|
+
export declare function agentLoad(name: string, bundle: SecretsBundle, env: Record<string, string>, ttlMs: number, harness?: string): Promise<boolean>;
|
|
340
411
|
/** Wipe one bundle (or all if name omitted) from the broker. Returns the count
|
|
341
412
|
* wiped, or 0 when no broker is running. */
|
|
342
413
|
export declare function agentLock(name?: string): Promise<number>;
|
|
@@ -364,4 +435,3 @@ export declare function agentPing(): Promise<{
|
|
|
364
435
|
* gets starved under heavy load, so it's last.
|
|
365
436
|
*/
|
|
366
437
|
export declare function ensureAgentRunning(timeoutMs?: number): Promise<boolean>;
|
|
367
|
-
export {};
|
|
@@ -35,9 +35,10 @@ import { getKeychainHelperPath } from './install-helper.js';
|
|
|
35
35
|
import { getCliVersion, getCliVersionFresh } from '../version.js';
|
|
36
36
|
import { getCliLaunch } from '../cli-entry.js';
|
|
37
37
|
import { rehydrateSessions, pruneSessionsOnSleep } from './session-store.js';
|
|
38
|
+
import { SYNC_GET_CMD, SYNC_PING_CMD, SYNC_LOCK_CMD } from './sync-commands.js';
|
|
38
39
|
/** Bumped when the wire protocol changes; a client that pings a mismatched
|
|
39
40
|
* server kills and respawns it rather than talking a stale dialect. */
|
|
40
|
-
const PROTOCOL_VERSION =
|
|
41
|
+
const PROTOCOL_VERSION = 2;
|
|
41
42
|
/** Default lifetime of an unlocked bundle when `--ttl` is not given. */
|
|
42
43
|
export const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7d
|
|
43
44
|
/**
|
|
@@ -58,6 +59,34 @@ export const META_CACHE_PREFIX = '!meta:';
|
|
|
58
59
|
const IDLE_EXIT_MS = 5 * 60 * 1000; // 5m
|
|
59
60
|
/** How often the broker sweeps expired entries. */
|
|
60
61
|
const SWEEP_INTERVAL_MS = 30 * 1000;
|
|
62
|
+
/**
|
|
63
|
+
* Timeouts for the three synchronous broker clients, split across the process
|
|
64
|
+
* boundary they now straddle.
|
|
65
|
+
*
|
|
66
|
+
* `SOCKET_*` bound the socket round-trip inside the spawned `__secrets-*`
|
|
67
|
+
* child, and carry over unchanged from the inline `node -e` programs these
|
|
68
|
+
* replaced. `SYNC_*` bound the parent's `spawnSync` and must additionally cover
|
|
69
|
+
* the child's boot, so each is its socket budget plus ~2s of headroom. A parent
|
|
70
|
+
* timeout that fired before the child's own timer would report "broker down"
|
|
71
|
+
* for a broker that is merely slow, which costs a Touch ID prompt — so the
|
|
72
|
+
* parent budget is deliberately the looser of the two.
|
|
73
|
+
*
|
|
74
|
+
* Boot cost, measured on the bun-compiled binary on an M-series Mac (median of
|
|
75
|
+
* 15, against a live broker): ~96ms via the index.ts intercept, ~165ms if the
|
|
76
|
+
* intercept is moved below the startup statements, ~44ms for the `node -e` this
|
|
77
|
+
* replaces. The intercept is what keeps the regression modest; see index.ts.
|
|
78
|
+
*/
|
|
79
|
+
const SOCKET_GET_TIMEOUT_MS = 2000;
|
|
80
|
+
const SOCKET_PING_TIMEOUT_MS = 700;
|
|
81
|
+
const SOCKET_LOCK_TIMEOUT_MS = 2000;
|
|
82
|
+
const SYNC_GET_TIMEOUT_MS = 4000;
|
|
83
|
+
const SYNC_PING_TIMEOUT_MS = 2500;
|
|
84
|
+
const SYNC_LOCK_TIMEOUT_MS = 4000;
|
|
85
|
+
// The argv tokens live in a leaf module so index.ts can bind the SAME values
|
|
86
|
+
// without importing this one. Re-exported here for callers already reaching for
|
|
87
|
+
// agent.js. See sync-commands.ts for why a shared binding rather than matching
|
|
88
|
+
// literals: the drift it prevents is silent and costs a Touch ID prompt per read.
|
|
89
|
+
export { SYNC_GET_CMD, SYNC_PING_CMD, SYNC_LOCK_CMD } from './sync-commands.js';
|
|
61
90
|
/**
|
|
62
91
|
* Decide whether a persistent broker should self-heal onto freshly-installed
|
|
63
92
|
* code (exit so launchd relaunches it). Only when the store is EMPTY: exiting
|
|
@@ -104,7 +133,8 @@ function onDarwin() {
|
|
|
104
133
|
function rehydrateStore(now = Date.now()) {
|
|
105
134
|
const store = new Map();
|
|
106
135
|
for (const { name, entry } of rehydrateSessions(now)) {
|
|
107
|
-
|
|
136
|
+
const harness = entry.harness || 'cli';
|
|
137
|
+
store.set(scopedBundleKey(name, harness), { bundle: entry.bundle, env: entry.env, expiresAt: entry.expiresAt, harness });
|
|
108
138
|
}
|
|
109
139
|
return store;
|
|
110
140
|
}
|
|
@@ -262,11 +292,14 @@ export async function uninstallSecretsAgentService() {
|
|
|
262
292
|
*/
|
|
263
293
|
export function realBundleCount(store) {
|
|
264
294
|
let n = 0;
|
|
265
|
-
for (const
|
|
266
|
-
if (!name.startsWith(META_CACHE_PREFIX))
|
|
295
|
+
for (const e of store.values())
|
|
296
|
+
if (!e.bundle.name.startsWith(META_CACHE_PREFIX))
|
|
267
297
|
n++;
|
|
268
298
|
return n;
|
|
269
299
|
}
|
|
300
|
+
export function scopedBundleKey(name, harness) {
|
|
301
|
+
return `${harness}:${name}`;
|
|
302
|
+
}
|
|
270
303
|
export function handleAgentRequest(store, req, now = Date.now()) {
|
|
271
304
|
switch (req.cmd) {
|
|
272
305
|
case 'ping':
|
|
@@ -276,20 +309,26 @@ export function handleAgentRequest(store, req, now = Date.now()) {
|
|
|
276
309
|
// the broker is running pre-upgrade code and should be restarted.
|
|
277
310
|
return { ok: true, cmd: 'ping', version: PROTOCOL_VERSION, cliVersion: getCliVersion() };
|
|
278
311
|
case 'get': {
|
|
279
|
-
const
|
|
312
|
+
const key = scopedBundleKey(req.name, req.harness || 'cli');
|
|
313
|
+
const e = store.get(key);
|
|
280
314
|
if (!e || now >= e.expiresAt) {
|
|
281
315
|
if (e)
|
|
282
|
-
store.delete(
|
|
316
|
+
store.delete(key); // drop expired on read
|
|
283
317
|
return { ok: true, cmd: 'get', hit: false };
|
|
284
318
|
}
|
|
285
319
|
return { ok: true, cmd: 'get', hit: true, bundle: e.bundle, env: e.env };
|
|
286
320
|
}
|
|
287
321
|
case 'load':
|
|
288
|
-
|
|
322
|
+
const harness = req.harness || 'cli';
|
|
323
|
+
store.set(scopedBundleKey(req.name, harness), { bundle: req.bundle, env: req.env, expiresAt: now + req.ttlMs, harness });
|
|
289
324
|
return { ok: true, cmd: 'load' };
|
|
290
325
|
case 'lock': {
|
|
291
326
|
if (req.name) {
|
|
292
|
-
|
|
327
|
+
let wiped = 0;
|
|
328
|
+
for (const [key, entry] of store)
|
|
329
|
+
if (entry.bundle.name === req.name && store.delete(key))
|
|
330
|
+
wiped++;
|
|
331
|
+
return { ok: true, cmd: 'lock', wiped };
|
|
293
332
|
}
|
|
294
333
|
const wiped = store.size;
|
|
295
334
|
store.clear();
|
|
@@ -300,9 +339,9 @@ export function handleAgentRequest(store, req, now = Date.now()) {
|
|
|
300
339
|
for (const [name, e] of store) {
|
|
301
340
|
if (now >= e.expiresAt)
|
|
302
341
|
continue;
|
|
303
|
-
if (name.startsWith(META_CACHE_PREFIX))
|
|
304
|
-
continue; // internal list cache
|
|
305
|
-
entries.push({ name, expiresAt: e.expiresAt, keyCount: Object.keys(e.env).length });
|
|
342
|
+
if (e.bundle.name.startsWith(META_CACHE_PREFIX))
|
|
343
|
+
continue; // internal list cache
|
|
344
|
+
entries.push({ name: e.bundle.name, expiresAt: e.expiresAt, keyCount: Object.keys(e.env).length, harness: e.harness });
|
|
306
345
|
}
|
|
307
346
|
return { ok: true, cmd: 'status', entries };
|
|
308
347
|
}
|
|
@@ -744,51 +783,65 @@ export function agentSocketExists() {
|
|
|
744
783
|
return onDarwin() && fs.existsSync(socketPath());
|
|
745
784
|
}
|
|
746
785
|
/**
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
*
|
|
750
|
-
*
|
|
751
|
-
*
|
|
752
|
-
* -
|
|
786
|
+
* Spawn one of the `__secrets-*` sync clients and return its result.
|
|
787
|
+
*
|
|
788
|
+
* These three call sites used to hand-roll `spawnSync(process.execPath, ['-e',
|
|
789
|
+
* <inline node program>, …])`. That is correct only for a JS install, where
|
|
790
|
+
* `process.execPath` is `node`. Since 1.20.53 the shipped macOS `agents` is a
|
|
791
|
+
* **bun-compiled Mach-O**, where `process.execPath` is the CLI binary itself —
|
|
792
|
+
* so the spawn became `agents -e <program> …`, which commander rejects with
|
|
793
|
+
* `error: unknown option '-e'` and a non-zero exit. Every sync client then took
|
|
794
|
+
* its own failure path (`agentGetSync` → null, `agentReachableSync` → false,
|
|
795
|
+
* `agentEvictSync` → no-op), which reads as "broker down" and falls through to
|
|
796
|
+
* a real keychain read. Net effect on the standalone binary: the broker cache
|
|
797
|
+
* was never hit, so the `daily` policy's one-prompt-per-7d never applied and
|
|
798
|
+
* every bundle read re-popped Touch ID.
|
|
799
|
+
*
|
|
800
|
+
* Same defect class as the broker-launch bug fixed in 1.20.56 (see cliSpawn's
|
|
801
|
+
* doc comment); these three sites were simply never converted. Routing them
|
|
802
|
+
* through `getCliLaunch` fixes both install shapes at once and keeps a single
|
|
803
|
+
* code path.
|
|
804
|
+
*
|
|
805
|
+
* The subcommands are top-level `__secrets-*` tokens intercepted in index.ts
|
|
806
|
+
* BEFORE commander and before the CLI's startup work, exactly like
|
|
807
|
+
* `__daemon-run` and `__vault-age-helper`. That is load-bearing, not cosmetic:
|
|
808
|
+
* a normal command path runs `checkForUpdates()` and `spawnDetachedSync()` on
|
|
809
|
+
* every invocation (index.ts), so registering these as ordinary hidden
|
|
810
|
+
* subcommands would fire an update check and spawn a detached background sync
|
|
811
|
+
* on every cache hit — turning the hot read path into a process fork storm.
|
|
753
812
|
*/
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
process.exit(3);
|
|
774
|
-
});
|
|
775
|
-
`;
|
|
813
|
+
export function syncClientLaunch(sub, agentsBin) {
|
|
814
|
+
return agentsBin ? getCliLaunch(sub, agentsBin) : getCliLaunch(sub);
|
|
815
|
+
}
|
|
816
|
+
function syncClient(sub, timeout) {
|
|
817
|
+
// Soft on every failure: bundles.ts's fast path promises "any failure falls
|
|
818
|
+
// through to the real keychain read below". spawnSync reports most failures
|
|
819
|
+
// via r.error rather than throwing, but getCliLaunch/getAgentsBinPath DO
|
|
820
|
+
// throw on a broken install — a bunfs entry whose execPath is gone, or a
|
|
821
|
+
// browser/computer shim with no sibling index.js. Without this catch those
|
|
822
|
+
// turn a graceful fallback into a crash, and only on already-degraded
|
|
823
|
+
// installs, which is the worst time for it.
|
|
824
|
+
try {
|
|
825
|
+
const { command, args } = syncClientLaunch(sub);
|
|
826
|
+
return spawnSync(command, args, { encoding: 'utf-8', timeout });
|
|
827
|
+
}
|
|
828
|
+
catch {
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
776
832
|
/**
|
|
777
833
|
* Synchronous read for the hot path. Returns the cached resolved bundle, or
|
|
778
834
|
* null if the agent isn't running / doesn't hold this bundle / anything fails
|
|
779
835
|
* (soft — caller falls through to the real keychain). macOS only.
|
|
780
836
|
*/
|
|
781
|
-
export function agentGetSync(name) {
|
|
837
|
+
export function agentGetSync(name, harness = 'cli') {
|
|
782
838
|
if (!agentSocketExists())
|
|
783
839
|
return null;
|
|
784
|
-
const r =
|
|
785
|
-
|
|
786
|
-
timeout: 3000,
|
|
787
|
-
});
|
|
788
|
-
if (r.status !== 0 || !r.stdout)
|
|
840
|
+
const r = syncClient([SYNC_GET_CMD, name, harness], SYNC_GET_TIMEOUT_MS);
|
|
841
|
+
if (!r || r.status !== 0 || !r.stdout)
|
|
789
842
|
return null;
|
|
790
843
|
try {
|
|
791
|
-
const o = JSON.parse(r.stdout);
|
|
844
|
+
const o = JSON.parse(lastLine(r.stdout));
|
|
792
845
|
if (!o || typeof o !== 'object' || !o.env)
|
|
793
846
|
return null;
|
|
794
847
|
return { bundle: o.bundle, env: o.env };
|
|
@@ -797,30 +850,28 @@ export function agentGetSync(name) {
|
|
|
797
850
|
return null;
|
|
798
851
|
}
|
|
799
852
|
}
|
|
800
|
-
/**
|
|
801
|
-
*
|
|
802
|
-
*
|
|
803
|
-
*
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
});
|
|
823
|
-
`;
|
|
853
|
+
/**
|
|
854
|
+
* Last non-empty line of a child's stdout — the payload line.
|
|
855
|
+
*
|
|
856
|
+
* The inline `node -e` client this replaced was a bare node process that could
|
|
857
|
+
* only ever emit its own JSON. The replacement boots the real CLI, so anything
|
|
858
|
+
* the CLI prints on the way up shares that stream. Today the known chatter (the
|
|
859
|
+
* `~/.agents/ is N commits behind` notice) correctly goes to stderr, but a
|
|
860
|
+
* single future stdout write anywhere in startup would make `JSON.parse` throw
|
|
861
|
+
* and turn every cache hit back into a silent miss — i.e. quietly reintroduce
|
|
862
|
+
* the Touch-ID-on-every-read bug this fix closes, with no failing test to catch
|
|
863
|
+
* it. Anchoring on the last line makes the payload the terminator of the
|
|
864
|
+
* stream rather than the whole of it, so preceding chatter is inert.
|
|
865
|
+
*/
|
|
866
|
+
export function lastLine(stdout) {
|
|
867
|
+
const lines = stdout.split('\n');
|
|
868
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
869
|
+
const t = lines[i].trim();
|
|
870
|
+
if (t)
|
|
871
|
+
return t;
|
|
872
|
+
}
|
|
873
|
+
return '';
|
|
874
|
+
}
|
|
824
875
|
/**
|
|
825
876
|
* Synchronous liveness check: is a broker actually LISTENING and answering (not
|
|
826
877
|
* just a lingering socket file)? Used to decide whether the auto-cache may take
|
|
@@ -834,36 +885,9 @@ export function agentReachableSync() {
|
|
|
834
885
|
return false;
|
|
835
886
|
if (!agentSocketExists())
|
|
836
887
|
return false;
|
|
837
|
-
const r =
|
|
838
|
-
return r.status === 0 && !r.error;
|
|
888
|
+
const r = syncClient([SYNC_PING_CMD], SYNC_PING_TIMEOUT_MS);
|
|
889
|
+
return r !== null && r.status === 0 && !r.error;
|
|
839
890
|
}
|
|
840
|
-
/**
|
|
841
|
-
* Inline node program for the synchronous evict path. Mirrors SYNC_GET_PROGRAM:
|
|
842
|
-
* writeBundle is synchronous and called synchronously everywhere, so a stale
|
|
843
|
-
* broker entry must be evicted without awaiting a socket round-trip. Sends one
|
|
844
|
-
* {cmd:'lock', name} and exits 0 (evicted or nothing held) / 3 (agent down).
|
|
845
|
-
* argv after -e: [execPath, <socket>, <name>].
|
|
846
|
-
*/
|
|
847
|
-
const SYNC_LOCK_PROGRAM = `
|
|
848
|
-
const net = require('net'), fs = require('fs');
|
|
849
|
-
const sock = process.argv[1], name = process.argv[2], tokenPath = process.argv[3];
|
|
850
|
-
let token; try { token = fs.readFileSync(tokenPath, 'utf-8').trim() || undefined; } catch (e) {}
|
|
851
|
-
const c = net.createConnection(sock);
|
|
852
|
-
let buf = '';
|
|
853
|
-
const down = () => { try { c.destroy(); } catch (e) {} process.exit(3); };
|
|
854
|
-
const timer = setTimeout(down, 2000);
|
|
855
|
-
c.on('error', down);
|
|
856
|
-
c.on('connect', () => c.write(JSON.stringify({ cmd: 'lock', name, token }) + '\\n'));
|
|
857
|
-
c.setEncoding('utf-8');
|
|
858
|
-
c.on('data', (d) => {
|
|
859
|
-
buf += d;
|
|
860
|
-
const nl = buf.indexOf('\\n');
|
|
861
|
-
if (nl < 0) return;
|
|
862
|
-
clearTimeout(timer);
|
|
863
|
-
try { c.destroy(); } catch (e) {}
|
|
864
|
-
process.exit(0);
|
|
865
|
-
});
|
|
866
|
-
`;
|
|
867
891
|
/**
|
|
868
892
|
* Synchronously evict one bundle from the broker. Called after a mutating
|
|
869
893
|
* keychain write (add / rotate / remove / rename / delete) so the broker never
|
|
@@ -877,10 +901,60 @@ export function agentEvictSync(name) {
|
|
|
877
901
|
return;
|
|
878
902
|
if (!agentSocketExists())
|
|
879
903
|
return;
|
|
880
|
-
try
|
|
881
|
-
|
|
904
|
+
// No try/catch: syncClient swallows its own failures and returns null.
|
|
905
|
+
syncClient([SYNC_LOCK_CMD, name], SYNC_LOCK_TIMEOUT_MS);
|
|
906
|
+
}
|
|
907
|
+
// ─── Top-level `__secrets-*` sync-client entrypoints ────────────────────────
|
|
908
|
+
// The bodies behind the three spawns above. Each does one socket round-trip and
|
|
909
|
+
// signals the parent through its exit code, mirroring the inline `node -e`
|
|
910
|
+
// programs these replaced: 0 = hit/alive, 3 = miss/down. Lock deviates twice:
|
|
911
|
+
// it reports 0 even when no broker answers, and 3 for an empty name, which it
|
|
912
|
+
// refuses rather than forwarding as a lock-ALL (see runAgentLockSync).
|
|
913
|
+
// Dispatched from index.ts BEFORE commander and before the startup statements,
|
|
914
|
+
// so a cache hit never runs checkForUpdates() or forks a detached sync.
|
|
915
|
+
/** Body of `__secrets-get <name>`. Prints `{bundle, env}` as JSON on a
|
|
916
|
+
* cache hit. Exit 0 = hit, 3 = miss or broker down. */
|
|
917
|
+
export async function runAgentGetSync(name, harness = 'cli') {
|
|
918
|
+
const r = await request({ cmd: 'get', name, harness }, SOCKET_GET_TIMEOUT_MS);
|
|
919
|
+
if (r?.ok === true && r.cmd === 'get' && r.hit) {
|
|
920
|
+
// Trailing newline: the parent reads the LAST line (see lastLine), so the
|
|
921
|
+
// payload must terminate the stream even if anything ever precedes it.
|
|
922
|
+
//
|
|
923
|
+
// Awaited on the write callback, not fire-and-forget: stdout is a pipe here
|
|
924
|
+
// (the parent captures it), pipe writes are asynchronous, and the caller
|
|
925
|
+
// exits the process the moment this resolves. An unflushed write would be
|
|
926
|
+
// truncated on exit — the parent would see a partial or empty payload, treat
|
|
927
|
+
// it as a miss, and fall through to a keychain read, silently costing the
|
|
928
|
+
// Touch ID prompt this whole path exists to avoid.
|
|
929
|
+
const payload = JSON.stringify({ bundle: r.bundle, env: r.env }) + '\n';
|
|
930
|
+
await new Promise((resolve) => { process.stdout.write(payload, () => resolve()); });
|
|
931
|
+
return 0;
|
|
882
932
|
}
|
|
883
|
-
|
|
933
|
+
return 3;
|
|
934
|
+
}
|
|
935
|
+
/** Body of `__secrets-ping`. Exit 0 = a broker is listening and speaking
|
|
936
|
+
* our protocol, 3 = nothing there. Deliberately does NOT gate on
|
|
937
|
+
* PROTOCOL_VERSION: this only decides whether the auto-cache may take the
|
|
938
|
+
* synchronous warm path, and a version-skewed broker still answers reads. That
|
|
939
|
+
* matches the inline program this replaced. */
|
|
940
|
+
export async function runAgentPingSync() {
|
|
941
|
+
const r = await request({ cmd: 'ping' }, SOCKET_PING_TIMEOUT_MS);
|
|
942
|
+
return r?.ok === true && r.cmd === 'ping' ? 0 : 3;
|
|
943
|
+
}
|
|
944
|
+
/** Body of `__secrets-lock <name>`. Best-effort evict; exit 0 even when no
|
|
945
|
+
* broker answers, so a missing broker never fails the mutating write that
|
|
946
|
+
* triggered it (agentEvictSync discards this either way).
|
|
947
|
+
*
|
|
948
|
+
* An empty name is refused rather than forwarded: the broker treats a nameless
|
|
949
|
+
* lock as lock-ALL (handleAgentRequest), so a bare `__secrets-lock` would wipe
|
|
950
|
+
* every held bundle and re-prompt Touch ID for each. That was unreachable while
|
|
951
|
+
* this was an inline `node -e` string; as a top-level argv token it is one typo
|
|
952
|
+
* away, and agentEvictSync only ever locks by name. */
|
|
953
|
+
export async function runAgentLockSync(name) {
|
|
954
|
+
if (!name)
|
|
955
|
+
return 3;
|
|
956
|
+
await request({ cmd: 'lock', name }, SOCKET_LOCK_TIMEOUT_MS);
|
|
957
|
+
return 0;
|
|
884
958
|
}
|
|
885
959
|
// Key inside the cached entry's env that holds the JSON metadata snapshot.
|
|
886
960
|
const META_SNAPSHOT_KEY = '__snapshot__';
|
|
@@ -992,10 +1066,10 @@ export function clampHoldMs(v) {
|
|
|
992
1066
|
* The worker reuses the robust `ensureAgentRunning` path (spawn-then-ping) rather
|
|
993
1067
|
* than a tight inline retry loop. Best-effort; never throws. macOS only.
|
|
994
1068
|
*/
|
|
995
|
-
export function agentAutoLoadSync(name, bundle, env, ttlMs) {
|
|
1069
|
+
export function agentAutoLoadSync(name, bundle, env, ttlMs, harness = 'cli') {
|
|
996
1070
|
if (!onDarwin())
|
|
997
1071
|
return;
|
|
998
|
-
const payload = JSON.stringify({ name, bundle, env, ttlMs });
|
|
1072
|
+
const payload = JSON.stringify({ name, bundle, env, ttlMs, harness });
|
|
999
1073
|
// Broker actually LISTENING → deterministic synchronous warm (bounded; the read
|
|
1000
1074
|
// already paid a Touch ID, so <1s here is invisible). We gate on a real liveness
|
|
1001
1075
|
// ping, NOT mere socket-file existence: a broker that died leaving its socket
|
|
@@ -1062,13 +1136,13 @@ export async function runAgentLoadFromStdin() {
|
|
|
1062
1136
|
process.exitCode = 1; // broker couldn't be brought up — did NOT load
|
|
1063
1137
|
return;
|
|
1064
1138
|
}
|
|
1065
|
-
const loaded = await agentLoad(payload.name, payload.bundle, payload.env, payload.ttlMs ?? DEFAULT_TTL_MS);
|
|
1139
|
+
const loaded = await agentLoad(payload.name, payload.bundle, payload.env, payload.ttlMs ?? DEFAULT_TTL_MS, payload.harness ?? 'cli');
|
|
1066
1140
|
if (!loaded)
|
|
1067
1141
|
process.exitCode = 1; // transport failed — did NOT load
|
|
1068
1142
|
}
|
|
1069
1143
|
/** Store a resolved bundle in the broker. Returns false on transport failure. */
|
|
1070
|
-
export async function agentLoad(name, bundle, env, ttlMs) {
|
|
1071
|
-
const r = await request({ cmd: 'load', name, bundle, env, ttlMs });
|
|
1144
|
+
export async function agentLoad(name, bundle, env, ttlMs, harness = 'cli') {
|
|
1145
|
+
const r = await request({ cmd: 'load', name, bundle, env, ttlMs, harness });
|
|
1072
1146
|
return r?.ok === true && r.cmd === 'load';
|
|
1073
1147
|
}
|
|
1074
1148
|
/** Wipe one bundle (or all if name omitted) from the broker. Returns the count
|