@phnx-labs/agents-cli 1.22.56 → 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 +70 -0
- package/README.md +4 -4
- package/dist/bootstrap.js +11 -2
- package/dist/cli/command-registry.d.ts +0 -1
- package/dist/cli/command-registry.js +0 -3
- package/dist/commands/accounts.js +7 -3
- package/dist/commands/apply.js +10 -2
- package/dist/commands/exec.js +1 -1
- package/dist/commands/fork.d.ts +23 -10
- package/dist/commands/fork.js +115 -58
- package/dist/commands/hooks.js +4 -4
- package/dist/commands/insights.d.ts +7 -5
- package/dist/commands/insights.js +16 -9
- package/dist/commands/monitors.js +11 -0
- package/dist/commands/perf.d.ts +16 -7
- package/dist/commands/perf.js +29 -20
- 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/rules.js +1 -1
- 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/ssh.js +24 -14
- package/dist/commands/status.js +1 -1
- package/dist/commands/sync.js +83 -7
- package/dist/commands/traces.js +7 -0
- package/dist/commands/trash.d.ts +2 -2
- package/dist/commands/trash.js +2 -6
- package/dist/commands/versions.d.ts +2 -2
- package/dist/commands/versions.js +1 -10
- package/dist/commands/view.d.ts +2 -2
- package/dist/commands/view.js +7 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +14 -0
- 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-ingest.d.ts +1 -0
- package/dist/lib/accounting/usage-ingest.js +75 -0
- package/dist/lib/accounting/usage-sync.d.ts +97 -0
- package/dist/lib/accounting/usage-sync.js +203 -0
- package/dist/lib/accounting/usage.d.ts +48 -2
- package/dist/lib/accounting/usage.js +79 -2
- package/dist/lib/agent-spec/agents.js +1 -1
- package/dist/lib/analytics/mix-commands.d.ts +8 -7
- package/dist/lib/analytics/mix-commands.js +50 -73
- 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/daemon.js +5 -0
- package/dist/lib/daemon/runner.d.ts +3 -0
- package/dist/lib/daemon/runner.js +95 -53
- package/dist/lib/daemon/usage-sync-service.d.ts +21 -0
- package/dist/lib/daemon/usage-sync-service.js +42 -0
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/device-config.d.ts +17 -6
- package/dist/lib/device-config.js +25 -11
- package/dist/lib/devices/connect.d.ts +17 -8
- package/dist/lib/devices/connect.js +31 -14
- package/dist/lib/devices/pool.d.ts +4 -3
- package/dist/lib/devices/pool.js +13 -5
- package/dist/lib/doctor-diff.js +77 -7
- package/dist/lib/exec.d.ts +6 -41
- package/dist/lib/exec.js +6 -41
- package/dist/lib/fleet/manifest.d.ts +17 -0
- package/dist/lib/fleet/manifest.js +26 -0
- package/dist/lib/git.d.ts +13 -1
- package/dist/lib/git.js +36 -7
- package/dist/lib/harness/adapter.d.ts +7 -7
- package/dist/lib/harness/adapters/claude.js +3 -2
- 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/hosts/remote-cmd.d.ts +9 -0
- package/dist/lib/hosts/remote-cmd.js +22 -0
- 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/perf/db.d.ts +1 -1
- package/dist/lib/perf/db.js +1 -1
- 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 +16 -32
- package/dist/lib/session/active.js +10 -68
- package/dist/lib/session/db.d.ts +24 -36
- package/dist/lib/session/db.js +143 -44
- package/dist/lib/session/discover.d.ts +6 -58
- package/dist/lib/session/discover.js +5 -43
- package/dist/lib/session/fork.d.ts +45 -26
- package/dist/lib/session/fork.js +32 -95
- package/dist/lib/session/parse.d.ts +1 -19
- package/dist/lib/session/parse.js +2 -15
- 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/startup/command-registry.d.ts +8 -2
- package/dist/lib/startup/command-registry.js +12 -4
- 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,20 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reaper for orphaned / wedged keychain-helper processes.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* The design splits cleanly into:
|
|
10
|
-
* - a pure planner ({@link planKeychainReap}) that is unit-testable without a
|
|
11
|
-
* real `ps` shell, and
|
|
12
|
-
* - an impure driver ({@link reapOrphanedKeychainProcesses}) that shells `ps`
|
|
13
|
-
* once per tick and executes kills through {@link killTree}.
|
|
4
|
+
* coreauthd / LocalAuthentication hangs can leave helper (and parent `agents`)
|
|
5
|
+
* processes pile up. The pure planner ({@link planKeychainReap}) is unit-testable
|
|
6
|
+
* without a real `ps`; the driver ({@link reapOrphanedKeychainProcesses}) shells
|
|
7
|
+
* `ps` once per tick and kills through {@link killTree}.
|
|
14
8
|
*/
|
|
15
|
-
/**
|
|
9
|
+
/** Grace before a helper whose parent exited is considered an orphan. */
|
|
16
10
|
export declare const ORPHAN_GRACE_SEC = 30;
|
|
17
|
-
/**
|
|
11
|
+
/** Grace before a helper with a live parent is considered stuck. */
|
|
18
12
|
export declare const STUCK_GRACE_SEC = 90;
|
|
19
13
|
/** Snapshot of one process row from `ps`, fed into the pure planner. */
|
|
20
14
|
export interface KeychainProcessSnapshot {
|
|
@@ -22,32 +16,19 @@ export interface KeychainProcessSnapshot {
|
|
|
22
16
|
ppid: number;
|
|
23
17
|
/** Elapsed seconds since the process started. */
|
|
24
18
|
elapsedSec: number;
|
|
25
|
-
/**
|
|
26
|
-
* Stable start-time fingerprint from {@link captureProcessStartTime}.
|
|
27
|
-
* `null` means "could not capture" — the planner must fail closed.
|
|
28
|
-
*/
|
|
19
|
+
/** Stable start-time fingerprint from {@link captureProcessStartTime}; null fails closed. */
|
|
29
20
|
startTime: string | null;
|
|
30
|
-
/**
|
|
31
|
-
* True when this process is a REAP-ELIGIBLE helper invocation — the installed
|
|
32
|
-
* helper binary running a short-lived keychain verb. False for a non-helper
|
|
33
|
-
* process AND for the long-lived `watch-lock` watcher (see
|
|
34
|
-
* {@link isReapableHelperCommand}).
|
|
35
|
-
*/
|
|
21
|
+
/** True for a reap-eligible helper invocation (short-lived keychain verb). */
|
|
36
22
|
isHelper: boolean;
|
|
37
23
|
/**
|
|
38
|
-
* True
|
|
39
|
-
* (auto-lock-on-sleep). Mutually exclusive with {@link isHelper}
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* reaps them only once the owning daemon is provably dead (RUSH-2419).
|
|
24
|
+
* True for the deliberately long-lived `watch-lock` watcher
|
|
25
|
+
* (auto-lock-on-sleep). Mutually exclusive with {@link isHelper}; killing it
|
|
26
|
+
* silently disables auto-lock-on-sleep, so it is reaped only once the owning
|
|
27
|
+
* daemon is provably dead (RUSH-2419).
|
|
43
28
|
*/
|
|
44
29
|
isWatchLock?: boolean;
|
|
45
30
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Tracked state for a stuck `agents` parent that has a live helper child.
|
|
48
|
-
* Keyed by parent PID. The two-sweep debounce avoids acting on a racy `ps`
|
|
49
|
-
* snapshot; the `stage` field drives child-first kill then parent escalation.
|
|
50
|
-
*/
|
|
31
|
+
/** Tracked state for a stuck parent across two-sweep debounce. */
|
|
51
32
|
export interface StuckParentCandidate {
|
|
52
33
|
pid: number;
|
|
53
34
|
startTime: string | null;
|
|
@@ -58,7 +39,7 @@ export interface StuckParentCandidate {
|
|
|
58
39
|
}
|
|
59
40
|
/** Result of one planning pass. */
|
|
60
41
|
export interface ReapPlan {
|
|
61
|
-
/** PIDs
|
|
42
|
+
/** PIDs to kill this tick. */
|
|
62
43
|
kill: number[];
|
|
63
44
|
/** Candidates to carry forward to the next sweep. */
|
|
64
45
|
nextCandidates: Map<number, StuckParentCandidate>;
|
|
@@ -66,51 +47,31 @@ export interface ReapPlan {
|
|
|
66
47
|
/**
|
|
67
48
|
* Pure predicate: decide which processes to kill given a `ps`-like snapshot.
|
|
68
49
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
50
|
+
* Three conservative classes:
|
|
51
|
+
* 1. Orphaned helper: PPID == 1, helper path, alive longer than grace.
|
|
52
|
+
* 2. Stuck `agents` parent: helper child alive longer than stuck grace,
|
|
53
|
+
* recorded on first sight, child killed on second sweep, parent on third.
|
|
54
|
+
* 3. Orphaned `watch-lock`: long-lived watcher whose owning daemon is gone.
|
|
72
55
|
*
|
|
73
|
-
*
|
|
74
|
-
* {@link ORPHAN_GRACE_SEC}.
|
|
75
|
-
* 2. Stuck `agents` parent: helper child alive longer than
|
|
76
|
-
* {@link STUCK_GRACE_SEC}. Recorded on first sight, child killed on the
|
|
77
|
-
* second consecutive sweep with the same PID + startTime, parent killed on
|
|
78
|
-
* the third sweep if the helper child is still present.
|
|
79
|
-
* 3. Orphaned `watch-lock` watcher (RUSH-2419): the long-lived auto-lock
|
|
80
|
-
* child whose owning daemon is provably dead. {@link isReapableHelperCommand}
|
|
81
|
-
* still excludes live-parent watch-locks from class 1/2; this path only
|
|
82
|
-
* reaps when the parent is gone from the snapshot (Unix reparents to init
|
|
83
|
-
* after daemon death) and the watcher itself has a start-time fingerprint.
|
|
84
|
-
*
|
|
85
|
-
* Never reaps a process whose start time could not be captured, whose path does
|
|
86
|
-
* not match the helper, or (for stuck parents) whose parent is no longer in the
|
|
87
|
-
* snapshot. A watch-lock whose parent IS still in the snapshot is never touched.
|
|
56
|
+
* Fails closed when start time can't be captured.
|
|
88
57
|
*/
|
|
89
58
|
export declare function planKeychainReap(snapshots: KeychainProcessSnapshot[], now: number, prevCandidates: ReadonlyMap<number, StuckParentCandidate>): ReapPlan;
|
|
90
59
|
/**
|
|
91
60
|
* Parse macOS `ps -o etime=` elapsed time into whole seconds.
|
|
92
61
|
*
|
|
93
|
-
* BSD `etime` renders as `[[dd-]hh:]mm:ss` (
|
|
94
|
-
*
|
|
95
|
-
* GNU/Linux procps extension that macOS `ps` rejects with a non-zero exit, so
|
|
96
|
-
* the reaper — which only ever runs on darwin — must read `etime`.
|
|
97
|
-
* Returns null for an unparseable value so the caller drops the row.
|
|
62
|
+
* BSD `etime` renders as `[[dd-]hh:]mm:ss`. `etimes` (raw seconds) is a GNU
|
|
63
|
+
* extension that macOS `ps` rejects, so darwin uses `etime`.
|
|
98
64
|
*/
|
|
99
65
|
export declare function parseEtimeToSeconds(raw: string): number | null;
|
|
100
66
|
/**
|
|
101
|
-
* Whether a `ps` command line is a
|
|
102
|
-
* helper binary running a short-lived keychain verb
|
|
103
|
-
*
|
|
104
|
-
* command AND for the deliberately long-lived `watch-lock` watcher — matching by
|
|
105
|
-
* the full argv (`ps … command=`), so a live-parent `watch-lock` child is never
|
|
106
|
-
* mistaken for a stuck read and killed. Pure; unit-tested.
|
|
67
|
+
* Whether a `ps` command line is a reap-eligible helper invocation: the installed
|
|
68
|
+
* helper binary running a short-lived keychain verb. Returns false for the
|
|
69
|
+
* long-lived `watch-lock` watcher, which must never be mistaken for a stuck read.
|
|
107
70
|
*/
|
|
108
71
|
export declare function isReapableHelperCommand(command: string, helperPath: string): boolean;
|
|
109
72
|
/**
|
|
110
73
|
* Whether a `ps` command line is the deliberately long-lived `watch-lock`
|
|
111
|
-
* watcher
|
|
112
|
-
* the watch-lock verb only — used by the orphaned-watch-lock reaper path
|
|
113
|
-
* (RUSH-2419). Pure; unit-tested.
|
|
74
|
+
* watcher. Inverse of {@link isReapableHelperCommand} for the watch-lock verb.
|
|
114
75
|
*/
|
|
115
76
|
export declare function isWatchLockHelperCommand(command: string, helperPath: string): boolean;
|
|
116
77
|
/** Test seam: reset the persisted candidate state. */
|
|
@@ -119,11 +80,8 @@ export declare function resetKeychainReaperCandidatesForTest(): void;
|
|
|
119
80
|
* Impure driver: snapshot all processes once with `ps`, plan the reap, then
|
|
120
81
|
* execute kills through {@link killTree}.
|
|
121
82
|
*
|
|
122
|
-
* Path-
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* Returns on non-darwin platforms without shelling anything — the helper only
|
|
126
|
-
* exists on macOS.
|
|
83
|
+
* Path-matches the full helper path so an unrelated binary named "Agents CLI" is
|
|
84
|
+
* never targeted. Returns on non-darwin without shelling anything.
|
|
127
85
|
*/
|
|
128
86
|
export declare function reapOrphanedKeychainProcesses(): {
|
|
129
87
|
reaped: number;
|
|
@@ -1,46 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reaper for orphaned / wedged keychain-helper processes.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* The design splits cleanly into:
|
|
10
|
-
* - a pure planner ({@link planKeychainReap}) that is unit-testable without a
|
|
11
|
-
* real `ps` shell, and
|
|
12
|
-
* - an impure driver ({@link reapOrphanedKeychainProcesses}) that shells `ps`
|
|
13
|
-
* once per tick and executes kills through {@link killTree}.
|
|
4
|
+
* coreauthd / LocalAuthentication hangs can leave helper (and parent `agents`)
|
|
5
|
+
* processes pile up. The pure planner ({@link planKeychainReap}) is unit-testable
|
|
6
|
+
* without a real `ps`; the driver ({@link reapOrphanedKeychainProcesses}) shells
|
|
7
|
+
* `ps` once per tick and kills through {@link killTree}.
|
|
14
8
|
*/
|
|
15
9
|
import { execFileSync } from 'child_process';
|
|
16
10
|
import { killTree, captureProcessStartTime } from '../platform/process.js';
|
|
17
11
|
import { getKeychainHelperPath } from './install-helper.js';
|
|
18
|
-
/**
|
|
12
|
+
/** Grace before a helper whose parent exited is considered an orphan. */
|
|
19
13
|
export const ORPHAN_GRACE_SEC = 30;
|
|
20
|
-
/**
|
|
14
|
+
/** Grace before a helper with a live parent is considered stuck. */
|
|
21
15
|
export const STUCK_GRACE_SEC = 90;
|
|
22
16
|
/**
|
|
23
17
|
* Pure predicate: decide which processes to kill given a `ps`-like snapshot.
|
|
24
18
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* {@link ORPHAN_GRACE_SEC}.
|
|
31
|
-
* 2. Stuck `agents` parent: helper child alive longer than
|
|
32
|
-
* {@link STUCK_GRACE_SEC}. Recorded on first sight, child killed on the
|
|
33
|
-
* second consecutive sweep with the same PID + startTime, parent killed on
|
|
34
|
-
* the third sweep if the helper child is still present.
|
|
35
|
-
* 3. Orphaned `watch-lock` watcher (RUSH-2419): the long-lived auto-lock
|
|
36
|
-
* child whose owning daemon is provably dead. {@link isReapableHelperCommand}
|
|
37
|
-
* still excludes live-parent watch-locks from class 1/2; this path only
|
|
38
|
-
* reaps when the parent is gone from the snapshot (Unix reparents to init
|
|
39
|
-
* after daemon death) and the watcher itself has a start-time fingerprint.
|
|
19
|
+
* Three conservative classes:
|
|
20
|
+
* 1. Orphaned helper: PPID == 1, helper path, alive longer than grace.
|
|
21
|
+
* 2. Stuck `agents` parent: helper child alive longer than stuck grace,
|
|
22
|
+
* recorded on first sight, child killed on second sweep, parent on third.
|
|
23
|
+
* 3. Orphaned `watch-lock`: long-lived watcher whose owning daemon is gone.
|
|
40
24
|
*
|
|
41
|
-
*
|
|
42
|
-
* not match the helper, or (for stuck parents) whose parent is no longer in the
|
|
43
|
-
* snapshot. A watch-lock whose parent IS still in the snapshot is never touched.
|
|
25
|
+
* Fails closed when start time can't be captured.
|
|
44
26
|
*/
|
|
45
27
|
export function planKeychainReap(snapshots, now, prevCandidates) {
|
|
46
28
|
const pidMap = new Map(snapshots.map((s) => [s.pid, s]));
|
|
@@ -50,8 +32,7 @@ export function planKeychainReap(snapshots, now, prevCandidates) {
|
|
|
50
32
|
if (!s.isHelper)
|
|
51
33
|
continue;
|
|
52
34
|
if (s.ppid === 1) {
|
|
53
|
-
// Orphaned helper:
|
|
54
|
-
// the process identity with a start-time fingerprint.
|
|
35
|
+
// Orphaned helper: fail closed without a start-time fingerprint.
|
|
55
36
|
if (s.elapsedSec <= ORPHAN_GRACE_SEC)
|
|
56
37
|
continue;
|
|
57
38
|
if (s.startTime == null)
|
|
@@ -59,8 +40,7 @@ export function planKeychainReap(snapshots, now, prevCandidates) {
|
|
|
59
40
|
kill.push(s.pid);
|
|
60
41
|
continue;
|
|
61
42
|
}
|
|
62
|
-
// Stuck agents:
|
|
63
|
-
// than the interactive timeout. The parent must still exist in the snapshot.
|
|
43
|
+
// Stuck agents: helper with a live parent wedged longer than the timeout.
|
|
64
44
|
if (s.elapsedSec <= STUCK_GRACE_SEC)
|
|
65
45
|
continue;
|
|
66
46
|
const parent = pidMap.get(s.ppid);
|
|
@@ -74,13 +54,12 @@ export function planKeychainReap(snapshots, now, prevCandidates) {
|
|
|
74
54
|
prev.helperStartTime === s.startTime &&
|
|
75
55
|
prev.startTime === parent.startTime) {
|
|
76
56
|
if (prev.stage === 'watch') {
|
|
77
|
-
// Second sweep: kill the child first so
|
|
78
|
-
// Keep the parent candidate at stage 'escalate' for the next sweep.
|
|
57
|
+
// Second sweep: kill the child first so spawnSync returns; escalate next.
|
|
79
58
|
kill.push(s.pid);
|
|
80
59
|
nextCandidates.set(parent.pid, { ...prev, stage: 'escalate' });
|
|
81
60
|
continue;
|
|
82
61
|
}
|
|
83
|
-
// Third sweep: child did not free the parent, so the parent
|
|
62
|
+
// Third sweep: child did not free the parent, so the parent is wedged.
|
|
84
63
|
kill.push(parent.pid);
|
|
85
64
|
continue;
|
|
86
65
|
}
|
|
@@ -94,32 +73,21 @@ export function planKeychainReap(snapshots, now, prevCandidates) {
|
|
|
94
73
|
stage: 'watch',
|
|
95
74
|
});
|
|
96
75
|
}
|
|
97
|
-
// Separate path for orphaned watch-lock watchers
|
|
98
|
-
//
|
|
99
|
-
// stays isHelper=false and is skipped above. Here we only kill when the
|
|
100
|
-
// owning daemon is provably absent from the process table.
|
|
76
|
+
// Separate path for orphaned watch-lock watchers: only reap when the owning
|
|
77
|
+
// daemon is provably absent from the process table.
|
|
101
78
|
for (const s of snapshots) {
|
|
102
79
|
if (!s.isWatchLock)
|
|
103
80
|
continue;
|
|
104
|
-
// Fail closed: no start-time fingerprint → refuse to kill (pid-reuse guard
|
|
105
|
-
// for the process we are about to target, same as orphan helpers).
|
|
106
81
|
if (s.startTime == null)
|
|
107
82
|
continue;
|
|
108
83
|
if (s.elapsedSec <= ORPHAN_GRACE_SEC)
|
|
109
84
|
continue;
|
|
110
|
-
// Live parent in this snapshot → owning daemon is still up. Leave it alone
|
|
111
|
-
// (this is the property isReapableHelperCommand protects for stuck-helper
|
|
112
|
-
// reaping; re-assert it here so a mis-tagged row cannot be killed either).
|
|
113
85
|
if (s.ppid !== 1) {
|
|
114
86
|
const parent = pidMap.get(s.ppid);
|
|
115
87
|
if (parent)
|
|
116
88
|
continue;
|
|
117
|
-
// Parent
|
|
118
|
-
// a dead parent reparents the child to init; a missing parent with a
|
|
119
|
-
// non-1 ppid is the race window before reparenting. Either way the owner
|
|
120
|
-
// is dead — safe to reap after grace + fingerprint.
|
|
89
|
+
// Parent missing means the owner is dead (or reparenting race); safe to reap.
|
|
121
90
|
}
|
|
122
|
-
// ppid===1 (reparented to init/launchd) or parent missing → orphaned.
|
|
123
91
|
kill.push(s.pid);
|
|
124
92
|
}
|
|
125
93
|
return { kill, nextCandidates };
|
|
@@ -127,11 +95,8 @@ export function planKeychainReap(snapshots, now, prevCandidates) {
|
|
|
127
95
|
/**
|
|
128
96
|
* Parse macOS `ps -o etime=` elapsed time into whole seconds.
|
|
129
97
|
*
|
|
130
|
-
* BSD `etime` renders as `[[dd-]hh:]mm:ss` (
|
|
131
|
-
*
|
|
132
|
-
* GNU/Linux procps extension that macOS `ps` rejects with a non-zero exit, so
|
|
133
|
-
* the reaper — which only ever runs on darwin — must read `etime`.
|
|
134
|
-
* Returns null for an unparseable value so the caller drops the row.
|
|
98
|
+
* BSD `etime` renders as `[[dd-]hh:]mm:ss`. `etimes` (raw seconds) is a GNU
|
|
99
|
+
* extension that macOS `ps` rejects, so darwin uses `etime`.
|
|
135
100
|
*/
|
|
136
101
|
export function parseEtimeToSeconds(raw) {
|
|
137
102
|
const m = raw.match(/^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/);
|
|
@@ -150,8 +115,6 @@ export function parseEtimeToSeconds(raw) {
|
|
|
150
115
|
*
|
|
151
116
|
* Expected format from `ps -ax -o pid=,ppid=,etime=,command=`:
|
|
152
117
|
* "<pid> <ppid> <etime> <command...>"
|
|
153
|
-
* where `<etime>` is BSD elapsed time (`[[dd-]hh:]mm:ss`). The command field is
|
|
154
|
-
* the remainder of the line and may contain spaces.
|
|
155
118
|
*/
|
|
156
119
|
function parsePsLine(line) {
|
|
157
120
|
const trimmed = line.trim();
|
|
@@ -168,22 +131,12 @@ function parsePsLine(line) {
|
|
|
168
131
|
return null;
|
|
169
132
|
return { pid, ppid, elapsedSec, command };
|
|
170
133
|
}
|
|
171
|
-
/**
|
|
172
|
-
* The one helper verb that is DELIBERATELY long-lived: the broker's auto-lock
|
|
173
|
-
* sleep/lock watcher (`spawn(getKeychainHelperPath(), ['watch-lock'], …)` in
|
|
174
|
-
* `agent.ts`). It lives for the broker's whole hold — potentially days — as a
|
|
175
|
-
* healthy child of the live broker/daemon, emitting LOCK/SLEEP lines that wipe
|
|
176
|
-
* the in-memory secret store on sleep. It is NOT a stuck keychain read, so the
|
|
177
|
-
* reaper must never target it: killing it silently disables auto-lock-on-sleep.
|
|
178
|
-
*/
|
|
134
|
+
/** The one helper verb that is deliberately long-lived: the auto-lock watcher. */
|
|
179
135
|
const HELPER_WATCH_LOCK_VERB = 'watch-lock';
|
|
180
136
|
/**
|
|
181
|
-
* Whether a `ps` command line is a
|
|
182
|
-
* helper binary running a short-lived keychain verb
|
|
183
|
-
*
|
|
184
|
-
* command AND for the deliberately long-lived `watch-lock` watcher — matching by
|
|
185
|
-
* the full argv (`ps … command=`), so a live-parent `watch-lock` child is never
|
|
186
|
-
* mistaken for a stuck read and killed. Pure; unit-tested.
|
|
137
|
+
* Whether a `ps` command line is a reap-eligible helper invocation: the installed
|
|
138
|
+
* helper binary running a short-lived keychain verb. Returns false for the
|
|
139
|
+
* long-lived `watch-lock` watcher, which must never be mistaken for a stuck read.
|
|
187
140
|
*/
|
|
188
141
|
export function isReapableHelperCommand(command, helperPath) {
|
|
189
142
|
if (command === helperPath)
|
|
@@ -195,9 +148,7 @@ export function isReapableHelperCommand(command, helperPath) {
|
|
|
195
148
|
}
|
|
196
149
|
/**
|
|
197
150
|
* Whether a `ps` command line is the deliberately long-lived `watch-lock`
|
|
198
|
-
* watcher
|
|
199
|
-
* the watch-lock verb only — used by the orphaned-watch-lock reaper path
|
|
200
|
-
* (RUSH-2419). Pure; unit-tested.
|
|
151
|
+
* watcher. Inverse of {@link isReapableHelperCommand} for the watch-lock verb.
|
|
201
152
|
*/
|
|
202
153
|
export function isWatchLockHelperCommand(command, helperPath) {
|
|
203
154
|
if (!command.startsWith(`${helperPath} `))
|
|
@@ -215,11 +166,8 @@ export function resetKeychainReaperCandidatesForTest() {
|
|
|
215
166
|
* Impure driver: snapshot all processes once with `ps`, plan the reap, then
|
|
216
167
|
* execute kills through {@link killTree}.
|
|
217
168
|
*
|
|
218
|
-
* Path-
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* Returns on non-darwin platforms without shelling anything — the helper only
|
|
222
|
-
* exists on macOS.
|
|
169
|
+
* Path-matches the full helper path so an unrelated binary named "Agents CLI" is
|
|
170
|
+
* never targeted. Returns on non-darwin without shelling anything.
|
|
223
171
|
*/
|
|
224
172
|
export function reapOrphanedKeychainProcesses() {
|
|
225
173
|
const details = [];
|
|
@@ -249,11 +197,8 @@ export function reapOrphanedKeychainProcesses() {
|
|
|
249
197
|
if (!parsed)
|
|
250
198
|
continue;
|
|
251
199
|
const { pid, ppid, elapsedSec, command } = parsed;
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
// whose live-parent child would otherwise be killed as if it were stuck
|
|
255
|
-
// (RUSH-2232 — that silently disabled auto-lock-on-sleep). Orphaned
|
|
256
|
-
// watch-locks are tracked separately via isWatchLock (RUSH-2419).
|
|
200
|
+
// Match the full argv so the long-lived `watch-lock` watcher is never
|
|
201
|
+
// mistaken for a stuck read. Orphaned watch-locks are tracked separately.
|
|
257
202
|
const isHelper = isReapableHelperCommand(command, helperPath);
|
|
258
203
|
const isWatchLock = isWatchLockHelperCommand(command, helperPath);
|
|
259
204
|
rows.push({ pid, ppid, elapsedSec, isHelper, isWatchLock, startTime: null });
|
|
@@ -1,37 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Remote secrets — read and use `agents secrets` bundles that live on another
|
|
3
|
-
* host
|
|
4
|
-
* (the write inverse) already uses.
|
|
3
|
+
* host over the same SSH path that `agents secrets export --device` uses.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* stdout back verbatim (lossless, no parsing).
|
|
9
|
-
* - use: resolve a remote bundle to an env map (JSON over ssh stdout) and
|
|
10
|
-
* inject it ephemerally — never written to this machine's keychain.
|
|
11
|
-
*
|
|
12
|
-
* Trust model: relies on the operator's existing SSH access to the host (same
|
|
13
|
-
* boundary as `export --device` / `run --device`). Bundle names are shell-quoted
|
|
14
|
-
* into the remote command; resolved VALUES return over ssh stdout. File-backend
|
|
15
|
-
* import never forwards AGENTS_SECRETS_PASSPHRASE (PHNX-2371). Nothing is
|
|
16
|
-
* persisted locally.
|
|
5
|
+
* Browse streams remote stdout verbatim; use resolves a bundle to an env map
|
|
6
|
+
* and injects it ephemerally without persisting it locally.
|
|
17
7
|
*/
|
|
18
8
|
import { type SshExecResult } from '../ssh-exec.js';
|
|
19
9
|
/**
|
|
20
|
-
* SSH options for
|
|
21
|
-
*
|
|
22
|
-
* (resolve/read-back) — so it MUST NOT ride the shared `accept-new` baseline
|
|
23
|
-
* against the user's own `~/.ssh/known_hosts` with a reusable control socket.
|
|
24
|
-
* Two hardenings over that baseline, matching the posture the `--copy-creds`
|
|
25
|
-
* dispatch already uses (`hosts/dispatch.ts` -> `hostKeyCheckingOpts`):
|
|
26
|
-
*
|
|
27
|
-
* - **Managed pinned host keys.** Verify against the CLI-owned known_hosts
|
|
28
|
-
* store (`known-hosts.ts`), not `~/.ssh/known_hosts`. A CHANGED key on a
|
|
29
|
-
* known host is refused (`StrictHostKeyChecking` `yes` once pinned,
|
|
30
|
-
* `accept-new` before that). Callers that copy durable provider credentials
|
|
31
|
-
* must separately require an existing pin before invoking this transport.
|
|
32
|
-
* - **No multiplex reuse.** `multiplex: false` — a credential channel never
|
|
33
|
-
* leaves a persistent `ControlMaster` socket lingering (60s `ControlPersist`)
|
|
34
|
-
* that any other `agents` invocation to that host would silently reuse.
|
|
10
|
+
* SSH options for secret-carrying transport. Pins the managed host key and
|
|
11
|
+
* disables multiplex so no reusable control socket lingers for other invocations.
|
|
35
12
|
*/
|
|
36
13
|
export declare function credentialTransportSshOpts(target: string): {
|
|
37
14
|
hostKeyOpts: string[];
|
|
@@ -40,37 +17,21 @@ export declare function credentialTransportSshOpts(target: string): {
|
|
|
40
17
|
/** Refuse durable credential transfer until the destination SSH key is pinned. */
|
|
41
18
|
export declare function assertCredentialTransportHostPinned(target: string, pinned?: boolean): void;
|
|
42
19
|
/**
|
|
43
|
-
* Trust boundary for a remote-resolved env map. A peer's
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* never the remote bundle). Block the dangerous-override classes here — at the
|
|
48
|
-
* source — so every consumer (`run --secrets b@host`, `secrets exec --host`) is
|
|
49
|
-
* protected, not just one call site:
|
|
50
|
-
* - LD_* / DYLD_* / NODE_OPTIONS and the other loader/interpreter injections
|
|
51
|
-
* (reuses the canonical bundles.ts predicate);
|
|
52
|
-
* - GIT_* — GIT_SSH_COMMAND et al. hijack every git subprocess;
|
|
53
|
-
* - *_PROXY — HTTP(S)_PROXY / ALL_PROXY reroute outbound traffic (MITM);
|
|
54
|
-
* - *_BASE_URL — ANTHROPIC_BASE_URL / OPENAI_BASE_URL redirect the model API.
|
|
55
|
-
* These keys are already rejected on the ADD side (validateEnvKey for loaders),
|
|
56
|
-
* so a legitimate bundle never carries them — only a hostile peer would.
|
|
20
|
+
* Trust boundary for a remote-resolved env map. A peer's export output is
|
|
21
|
+
* untrusted input that could reshape this process once merged into the env, so
|
|
22
|
+
* block dangerous override classes here for every consumer:
|
|
23
|
+
* loader/interpreter vars, GIT_*, *_PROXY, and *_BASE_URL.
|
|
57
24
|
*/
|
|
58
25
|
export declare function isDangerousRemoteEnvKey(name: string): boolean;
|
|
59
26
|
/**
|
|
60
|
-
* Resolve a `--device` value to an ssh target
|
|
61
|
-
* Delegates to the
|
|
62
|
-
*
|
|
63
|
-
* value as a raw ssh target and validates it against injection. Named distinctly
|
|
64
|
-
* from `../devices/resolve-target.ts` (which returns richer shapes) so importing
|
|
65
|
-
* the wrong one can't silently change which machine you dial.
|
|
27
|
+
* Resolve a `--device` value to an ssh target string for the remote-secrets
|
|
28
|
+
* path. Delegates to the same resolver `run --device` uses; on a miss, treats
|
|
29
|
+
* the value as a raw ssh target and validates it.
|
|
66
30
|
*/
|
|
67
31
|
export declare function resolveHostSshTarget(nameOrAlias: string): Promise<string>;
|
|
68
32
|
/**
|
|
69
|
-
* Merge `--host
|
|
70
|
-
*
|
|
71
|
-
* works. `--device`/`--devices` resolve identically to `--device`/`--hosts` so the
|
|
72
|
-
* fleet-wide `--device` vocabulary (see `agents run --device`, `agents feed --host`)
|
|
73
|
-
* works on the secrets remote commands too. Empty when none is set.
|
|
33
|
+
* Merge `--host` / `--hosts` (and their `--device` / `--devices` aliases) into
|
|
34
|
+
* an ordered, de-duplicated list. Empty when none is set.
|
|
74
35
|
*/
|
|
75
36
|
export declare function parseHostsOption(opts: {
|
|
76
37
|
host?: string;
|
|
@@ -79,11 +40,9 @@ export declare function parseHostsOption(opts: {
|
|
|
79
40
|
devices?: string;
|
|
80
41
|
}): string[];
|
|
81
42
|
/**
|
|
82
|
-
* Split a `bundle@host` reference. No `@` → a local bundle
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* `user@host` (e.g. `r2.backups@muqsit@box` → bundle `r2.backups`, host
|
|
86
|
-
* `muqsit@box`).
|
|
43
|
+
* Split a `bundle@host` reference. No `@` → a local bundle. Bundle names can't
|
|
44
|
+
* contain `@`, so the FIRST `@` separates bundle from ssh target; the target
|
|
45
|
+
* itself may be `user@host` (e.g. `r2.backups@muqsit@box`).
|
|
87
46
|
*/
|
|
88
47
|
export declare function splitBundleRef(ref: string): {
|
|
89
48
|
bundle: string;
|
|
@@ -91,9 +50,8 @@ export declare function splitBundleRef(ref: string): {
|
|
|
91
50
|
};
|
|
92
51
|
/**
|
|
93
52
|
* Run `agents secrets <args>` on a remote host over ssh and return the raw
|
|
94
|
-
* result. Used by
|
|
95
|
-
*
|
|
96
|
-
* remote Touch-ID / passphrase prompt can surface (e.g. `view --reveal`).
|
|
53
|
+
* result. Used by browse commands; `tty` forces `-tt` so remote passphrase
|
|
54
|
+
* prompts can surface.
|
|
97
55
|
*/
|
|
98
56
|
export declare function remoteSecretsRaw(target: string, args: string[], opts?: {
|
|
99
57
|
tty?: boolean;
|
|
@@ -102,17 +60,9 @@ export declare function remoteSecretsRaw(target: string, args: string[], opts?:
|
|
|
102
60
|
secret?: boolean;
|
|
103
61
|
}): SshExecResult;
|
|
104
62
|
/**
|
|
105
|
-
* Run a remote `agents secrets <args>`
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* Unlike `remoteSecretsRaw` — which pipes stdin, so even with `-tt` the remote
|
|
109
|
-
* process's `process.stdin.isTTY` is false and a passphrase prompt refuses to
|
|
110
|
-
* appear (the macOS file-store guard then hard-errors "needs
|
|
111
|
-
* AGENTS_SECRETS_PASSPHRASE") — this inherits the caller's real terminal, so the
|
|
112
|
-
* remote sees a genuine TTY and its hidden passphrase prompt surfaces and reads
|
|
113
|
-
* the keystrokes. This is the transport for `unlock --device`: you type the remote
|
|
114
|
-
* bundle's passphrase at your own terminal. Output is NOT captured (it streams
|
|
115
|
-
* to the terminal); only the exit code is returned.
|
|
63
|
+
* Run a remote `agents secrets <args>` foreground with local stdio inherited,
|
|
64
|
+
* so the remote sees a real TTY and its passphrase prompt surfaces and reads
|
|
65
|
+
* keystrokes. Output streams to the terminal; only the exit code is returned.
|
|
116
66
|
*/
|
|
117
67
|
export declare function remoteSecretsStream(target: string, args: string[], opts?: {
|
|
118
68
|
osLookupName?: string;
|
|
@@ -120,31 +70,20 @@ export declare function remoteSecretsStream(target: string, args: string[], opts
|
|
|
120
70
|
/**
|
|
121
71
|
* Resolve a remote bundle to a plaintext env map by driving the remote's
|
|
122
72
|
* `agents secrets export <bundle> --plaintext --format json`. Values cross over
|
|
123
|
-
* ssh stdout
|
|
73
|
+
* ssh stdout, parsed in memory, never persisted.
|
|
124
74
|
*
|
|
125
|
-
* The remote unlocks the bundle with
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* passphrase: the remote bundle is encrypted with the remote's passphrase, so
|
|
129
|
-
* overriding it would break the read. (A macOS remote under non-interactive
|
|
130
|
-
* SSH will block on Touch-ID — use `view`/`exec` with a remote `file` bundle,
|
|
131
|
-
* an already-unlocked remote secrets-agent, or an interactive `-tt` session.)
|
|
75
|
+
* The remote unlocks the bundle with its own credentials; this machine does NOT
|
|
76
|
+
* forward its passphrase because the remote bundle is encrypted with the
|
|
77
|
+
* remote's passphrase.
|
|
132
78
|
*/
|
|
133
79
|
export declare function remoteResolveEnv(target: string, bundle: string, opts?: {
|
|
134
80
|
osLookupName?: string;
|
|
135
81
|
}): Promise<Record<string, string>>;
|
|
136
82
|
/**
|
|
137
|
-
* Outcome of a post-push read-back verification
|
|
138
|
-
* - `ok`
|
|
139
|
-
* - `locked-keychain`
|
|
140
|
-
*
|
|
141
|
-
* in the secrets agent" guard, or a "stored item … not
|
|
142
|
-
* found" on read-back, or pushed keys simply absent). Only
|
|
143
|
-
* this verdict earns the locked-login-keychain diagnosis +
|
|
144
|
-
* `--remote-backend file` steer.
|
|
145
|
-
* - `error` — a DIFFERENT failure (flaky SSH, timeout, unparseable
|
|
146
|
-
* payload). The raw error is re-surfaced verbatim, never
|
|
147
|
-
* mislabeled as a locked keychain.
|
|
83
|
+
* Outcome of a post-push read-back verification.
|
|
84
|
+
* - `ok` — the pushed keys materialized readably.
|
|
85
|
+
* - `locked-keychain` — read-back gave the specific locked-keychain signal.
|
|
86
|
+
* - `error` — a different failure (SSH, timeout, etc.).
|
|
148
87
|
*/
|
|
149
88
|
export type RemoteKeychainWriteVerification = {
|
|
150
89
|
ok: true;
|
|
@@ -158,24 +97,10 @@ export type RemoteKeychainWriteVerification = {
|
|
|
158
97
|
reason: string;
|
|
159
98
|
};
|
|
160
99
|
/**
|
|
161
|
-
* Decide whether a keychain-backed push to a remote actually
|
|
162
|
-
* value items
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* a macOS host over headless SSH lands the bundle METADATA but not readable value
|
|
166
|
-
* items — the remote login keychain is locked in the non-interactive SSH context,
|
|
167
|
-
* so Security accepts the item WRITE at the DB level but the biometry-ACL'd item is
|
|
168
|
-
* unreadable, and the remote `import` still reports success (values written first,
|
|
169
|
-
* metadata `noAcl` last — bundles.ts writeBundleWithItems). The metadata-only bundle
|
|
170
|
-
* then fails every later read with the confusing `Bundle '<b>' key '<k>': stored
|
|
171
|
-
* item '<item>' not found` (bundles.ts resolveBundleEnv). We catch it by reading
|
|
172
|
-
* the bundle back the same way a later `secrets exec`/resolve will (the marker-gated
|
|
173
|
-
* json transport, driven headlessly on the remote so its `agentOnly` guard FAILS FAST
|
|
174
|
-
* before any keychain read — no Touch ID prompt) and confirming every pushed key
|
|
175
|
-
* returned.
|
|
176
|
-
*
|
|
177
|
-
* Pure so both branches are unit-testable without a real locked keychain: inject the
|
|
178
|
-
* "read-back failed / key absent" condition through `readBack`.
|
|
100
|
+
* Decide whether a keychain-backed push to a remote actually persisted its
|
|
101
|
+
* secret value items by reading the bundle back the same way a later resolve
|
|
102
|
+
* will. Catches the silent failure where headless SSH writes metadata but not
|
|
103
|
+
* readable value items to a locked macOS login keychain.
|
|
179
104
|
*/
|
|
180
105
|
export declare function evaluateKeychainWriteVerification(pushedKeys: string[], readBack: {
|
|
181
106
|
ok: true;
|
|
@@ -185,38 +110,26 @@ export declare function evaluateKeychainWriteVerification(pushedKeys: string[],
|
|
|
185
110
|
stderr: string;
|
|
186
111
|
}): RemoteKeychainWriteVerification;
|
|
187
112
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* re-run with the headless-readable file backend, or unlock the remote keychain.
|
|
191
|
-
* Pure + exported so the exact guidance is asserted in tests.
|
|
113
|
+
* Actionable error message for a failed keychain-over-SSH push. Names the cause
|
|
114
|
+
* (locked remote login keychain) and steers to the two real fixes.
|
|
192
115
|
*/
|
|
193
116
|
export declare function keychainWriteFailureMessage(host: string, bundle: string, reason: string): string;
|
|
194
117
|
/**
|
|
195
|
-
* Read a bundle back from a remote over SSH
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* --format json` under AGENTS_SECRETS_REMOTE_TRANSPORT) but keeps only the KEY
|
|
199
|
-
* NAMES; the plaintext values are dropped immediately and never retained or
|
|
200
|
-
* logged. Returns a verification verdict; the caller renders
|
|
201
|
-
* `keychainWriteFailureMessage` on failure.
|
|
118
|
+
* Read a bundle back from a remote over SSH and confirm the pushed keys
|
|
119
|
+
* materialized. Drives the marker-gated json transport but keeps only KEY NAMES;
|
|
120
|
+
* plaintext values are discarded immediately. Returns a verification verdict.
|
|
202
121
|
*/
|
|
203
122
|
export declare function verifyRemoteKeychainPush(target: string, bundle: string, pushedKeys: string[], opts?: {
|
|
204
123
|
osLookupName?: string;
|
|
205
124
|
secret?: boolean;
|
|
206
125
|
}): RemoteKeychainWriteVerification;
|
|
207
126
|
/**
|
|
208
|
-
*
|
|
209
|
-
* import for `secrets export --device … --remote-backend file`.
|
|
127
|
+
* `bash -lc` command + stdin payload for a file-backed remote import.
|
|
210
128
|
*
|
|
211
|
-
* The file store is passphrase-free: the remote
|
|
212
|
-
*
|
|
213
|
-
* `~/.agents/.secrets-key/`), so its reads are HEADLESS — no passphrase, no
|
|
214
|
-
* Touch ID. AGENTS_SECRETS_PASSPHRASE is a deprecated override and MUST NOT be
|
|
129
|
+
* The file store is passphrase-free: the remote auto-provisions its own machine-
|
|
130
|
+
* local key, so reads are headless. AGENTS_SECRETS_PASSPHRASE must NOT be
|
|
215
131
|
* forwarded (PHNX-2371): a remote keyed to a secret its daemon does not hold
|
|
216
132
|
* reports "Imported N key(s)" then fails every later decrypt.
|
|
217
|
-
*
|
|
218
|
-
* Pure — no I/O — so the exact command string and stdin ordering are unit-testable
|
|
219
|
-
* against the SSH boundary the same way `remoteSecretsRaw` is.
|
|
220
133
|
*/
|
|
221
134
|
export declare function buildRemoteFileImportCommand(bundle: string, dotenv: string, opts?: {
|
|
222
135
|
force?: boolean;
|