@phnx-labs/agents-cli 1.22.84 → 1.22.85
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 -0
- package/README.md +80 -53
- package/dist/bootstrap.d.ts +7 -6
- package/dist/bootstrap.js +20 -27
- package/dist/cli/command-registry.js +5 -1
- package/dist/commands/accounts.d.ts +6 -13
- package/dist/commands/accounts.js +330 -143
- package/dist/commands/apply.js +1 -1
- package/dist/commands/auth-mint.d.ts +7 -3
- package/dist/commands/auth-mint.js +21 -9
- package/dist/commands/auth.js +2 -3
- package/dist/commands/browser.js +10 -10
- package/dist/commands/daemon.d.ts +7 -3
- package/dist/commands/daemon.js +40 -25
- package/dist/commands/doctor.js +28 -2
- package/dist/commands/exec.js +87 -39
- package/dist/commands/fleet-capture.js +21 -2
- package/dist/commands/harness-wizard.js +2 -2
- package/dist/commands/lease.js +7 -12
- package/dist/commands/profiles.d.ts +2 -2
- package/dist/commands/profiles.js +20 -13
- package/dist/commands/repo.js +3 -3
- package/dist/commands/run-account-picker.d.ts +3 -3
- package/dist/commands/run-account-picker.js +29 -30
- package/dist/commands/secrets-passthrough.d.ts +20 -0
- package/dist/commands/secrets-passthrough.js +43 -0
- package/dist/commands/setup-accounts.d.ts +3 -2
- package/dist/commands/setup-accounts.js +4 -4
- package/dist/commands/setup-secrets.d.ts +16 -21
- package/dist/commands/setup-secrets.js +43 -224
- package/dist/commands/ssh.js +2 -2
- package/dist/commands/sync.js +1 -1
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.js +43 -40
- package/dist/commands/versions.d.ts +11 -11
- package/dist/commands/versions.js +131 -83
- package/dist/commands/view.d.ts +1 -9
- package/dist/commands/view.js +41 -55
- package/dist/commands/webhook.js +5 -5
- package/dist/commands/workflows.js +2 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.js +13 -42
- package/dist/lib/account-capabilities.d.ts +1 -1
- package/dist/lib/account-capabilities.js +1 -1
- package/dist/lib/account-catalog.d.ts +121 -6
- package/dist/lib/account-catalog.js +421 -7
- package/dist/lib/account-registry.d.ts +8 -30
- package/dist/lib/account-registry.js +36 -79
- package/dist/lib/account-schema.d.ts +1 -1
- package/dist/lib/account-schema.js +1 -1
- package/dist/lib/accounting/account-pool-collect.js +16 -4
- package/dist/lib/accounting/rotate.d.ts +20 -7
- package/dist/lib/accounting/rotate.js +89 -13
- package/dist/lib/accounting/usage.js +11 -16
- package/dist/lib/accounts/add.d.ts +138 -0
- package/dist/lib/accounts/add.js +651 -0
- package/dist/lib/accounts/migrate.d.ts +117 -0
- package/dist/lib/accounts/migrate.js +536 -0
- package/dist/lib/accounts/slots.d.ts +13 -0
- package/dist/lib/accounts/slots.js +100 -0
- package/dist/lib/agent-spec/agents.d.ts +16 -0
- package/dist/lib/agent-spec/agents.js +32 -4
- package/dist/lib/app-bundle-install.js +5 -4
- package/dist/lib/auth-health.d.ts +3 -0
- package/dist/lib/auth-health.js +11 -0
- package/dist/lib/auth-mint.d.ts +52 -16
- package/dist/lib/auth-mint.js +117 -39
- package/dist/lib/browser/chrome.d.ts +1 -1
- package/dist/lib/browser/chrome.js +5 -5
- package/dist/lib/byok-usage.js +3 -3
- package/dist/lib/claude-account-token.d.ts +47 -3
- package/dist/lib/claude-account-token.js +172 -50
- package/dist/lib/cloud/antigravity.js +4 -4
- package/dist/lib/cloud/cursor.js +4 -4
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +6 -6
- package/dist/lib/crabbox/runtimes.d.ts +3 -3
- package/dist/lib/crabbox/runtimes.js +5 -5
- package/dist/lib/daemon/account-state-daemon-service.d.ts +37 -1
- package/dist/lib/daemon/account-state-daemon-service.js +199 -3
- package/dist/lib/daemon/auth-sync-service.js +28 -1
- package/dist/lib/daemon/daemon.d.ts +6 -16
- package/dist/lib/daemon/daemon.js +52 -97
- package/dist/lib/daemon/daemon.test-fixture.d.ts +7 -9
- package/dist/lib/daemon/daemon.test-fixture.js +22 -40
- package/dist/lib/daemon/harness-update-service.d.ts +1 -1
- package/dist/lib/daemon/harness-update-service.js +1 -1
- package/dist/lib/daemon/runner.d.ts +15 -3
- package/dist/lib/daemon/runner.js +49 -24
- package/dist/lib/daemon-health.d.ts +1 -2
- package/dist/lib/daemon-health.js +7 -6
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +1 -11
- package/dist/lib/daemon-webhooks.d.ts +8 -7
- package/dist/lib/daemon-webhooks.js +10 -9
- package/dist/lib/device-config.js +1 -1
- package/dist/lib/devices/doctor-findings.d.ts +1 -1
- package/dist/lib/devices/harness-inventory.d.ts +39 -0
- package/dist/lib/devices/harness-inventory.js +126 -4
- package/dist/lib/doctor-diff.js +2 -1
- package/dist/lib/exec-account-home.d.ts +38 -0
- package/dist/lib/exec-account-home.js +164 -0
- package/dist/lib/exec.d.ts +30 -0
- package/dist/lib/exec.js +101 -31
- package/dist/lib/fleet/apply.d.ts +2 -2
- package/dist/lib/fleet/apply.js +4 -4
- package/dist/lib/fleet/auth-sync.js +4 -3
- package/dist/lib/fleet-shared-repo-sync.js +6 -9
- package/dist/lib/harness/adapter.d.ts +7 -7
- package/dist/lib/harness/adapter.js +10 -6
- package/dist/lib/harness/adapters/grok.js +8 -3
- package/dist/lib/harness/adapters/muse.js +1 -1
- package/dist/lib/harness/adapters/opencode.js +12 -3
- package/dist/lib/harness-auth-capabilities.d.ts +61 -0
- package/dist/lib/harness-auth-capabilities.js +52 -0
- package/dist/lib/helper-versions.d.ts +6 -4
- package/dist/lib/helper-versions.js +5 -4
- package/dist/lib/hosts/credential-transport.d.ts +10 -0
- package/dist/lib/hosts/credential-transport.js +37 -0
- package/dist/lib/hosts/dispatch.d.ts +7 -0
- package/dist/lib/hosts/dispatch.js +13 -6
- package/dist/lib/identity/client.d.ts +3 -3
- package/dist/lib/identity/client.js +3 -3
- package/dist/lib/installations/index.d.ts +1 -1
- package/dist/lib/installations/index.js +1 -1
- package/dist/lib/installations/migrate.js +9 -0
- package/dist/lib/installations/resolve.js +1 -1
- package/dist/lib/installations/shims.d.ts +15 -0
- package/dist/lib/installations/shims.js +69 -0
- package/dist/lib/installations/store.d.ts +43 -0
- package/dist/lib/installations/store.js +75 -7
- package/dist/lib/installations/versions.js +1 -1
- package/dist/lib/menubar/install-menubar.d.ts +3 -3
- package/dist/lib/menubar/install-menubar.js +3 -3
- package/dist/lib/native-accounts.d.ts +35 -0
- package/dist/lib/native-accounts.js +41 -0
- package/dist/lib/net-close.d.ts +11 -0
- package/dist/lib/net-close.js +25 -0
- package/dist/lib/openclaw-keychain.js +27 -1
- package/dist/lib/profiles.js +22 -9
- package/dist/lib/project-resources.js +1 -1
- package/dist/lib/reserved-stores.d.ts +53 -0
- package/dist/lib/reserved-stores.js +120 -0
- package/dist/lib/secrets-client.d.ts +191 -0
- package/dist/lib/secrets-client.js +710 -0
- package/dist/lib/secrets-policy.d.ts +222 -0
- package/dist/lib/secrets-policy.js +483 -0
- package/dist/lib/secrets-types.d.ts +188 -0
- package/dist/lib/secrets-types.js +24 -0
- package/dist/lib/service-manifest.js +3 -4
- package/dist/lib/session/db.d.ts +18 -0
- package/dist/lib/session/db.js +47 -14
- package/dist/lib/session/sync/config.js +2 -2
- package/dist/lib/sha256-asset.d.ts +11 -16
- package/dist/lib/sha256-asset.js +11 -16
- package/dist/lib/share/config.js +14 -5
- package/dist/lib/signin-badge.d.ts +26 -0
- package/dist/lib/signin-badge.js +42 -0
- package/dist/lib/staleness/detectors/workflows.js +3 -105
- package/dist/lib/staleness/writers/workflows.js +2 -1
- package/dist/lib/state.d.ts +7 -4
- package/dist/lib/state.js +18 -7
- package/dist/lib/sync-umbrella.js +2 -2
- package/dist/lib/teams/agents.js +1 -1
- package/dist/lib/types.d.ts +74 -33
- package/dist/lib/view-types.d.ts +6 -3
- package/dist/lib/workflows-registry.d.ts +71 -0
- package/dist/lib/workflows-registry.js +280 -0
- package/dist/lib/workflows.d.ts +21 -21
- package/dist/lib/workflows.js +12 -327
- package/package.json +2 -3
- package/scripts/postinstall.js +4 -29
- package/dist/commands/secrets-import.d.ts +0 -18
- package/dist/commands/secrets-import.js +0 -74
- package/dist/commands/secrets-migrate.d.ts +0 -25
- package/dist/commands/secrets-migrate.js +0 -334
- package/dist/commands/secrets-rotate-passphrase.d.ts +0 -17
- package/dist/commands/secrets-rotate-passphrase.js +0 -96
- package/dist/commands/secrets-sync.d.ts +0 -11
- package/dist/commands/secrets-sync.js +0 -153
- package/dist/commands/secrets-vault.d.ts +0 -10
- package/dist/commands/secrets-vault.js +0 -130
- package/dist/commands/secrets.d.ts +0 -212
- package/dist/commands/secrets.js +0 -3030
- package/dist/lib/accounts/connect.d.ts +0 -176
- package/dist/lib/accounts/connect.js +0 -453
- package/dist/lib/daemon/keychain-reap-service.d.ts +0 -17
- package/dist/lib/daemon/keychain-reap-service.js +0 -32
- package/dist/lib/daemon/secrets-broker-service.d.ts +0 -21
- package/dist/lib/daemon/secrets-broker-service.js +0 -51
- package/dist/lib/secrets/agent.d.ts +0 -358
- package/dist/lib/secrets/agent.js +0 -1291
- package/dist/lib/secrets/audit.d.ts +0 -46
- package/dist/lib/secrets/audit.js +0 -101
- package/dist/lib/secrets/bundles.d.ts +0 -290
- package/dist/lib/secrets/bundles.js +0 -1547
- package/dist/lib/secrets/download-keychain.d.ts +0 -47
- package/dist/lib/secrets/download-keychain.js +0 -70
- package/dist/lib/secrets/drivers/rush.d.ts +0 -14
- package/dist/lib/secrets/drivers/rush.js +0 -90
- package/dist/lib/secrets/fallback.d.ts +0 -48
- package/dist/lib/secrets/fallback.js +0 -48
- package/dist/lib/secrets/filestore.d.ts +0 -222
- package/dist/lib/secrets/filestore.js +0 -1099
- package/dist/lib/secrets/headless.d.ts +0 -43
- package/dist/lib/secrets/headless.js +0 -56
- package/dist/lib/secrets/icloud-import.d.ts +0 -79
- package/dist/lib/secrets/icloud-import.js +0 -206
- package/dist/lib/secrets/index.d.ts +0 -451
- package/dist/lib/secrets/index.js +0 -1568
- package/dist/lib/secrets/install-helper.d.ts +0 -72
- package/dist/lib/secrets/install-helper.js +0 -245
- package/dist/lib/secrets/lease.d.ts +0 -25
- package/dist/lib/secrets/lease.js +0 -44
- package/dist/lib/secrets/linux.d.ts +0 -77
- package/dist/lib/secrets/linux.js +0 -393
- package/dist/lib/secrets/list-filter.d.ts +0 -109
- package/dist/lib/secrets/list-filter.js +0 -261
- package/dist/lib/secrets/mcp.d.ts +0 -93
- package/dist/lib/secrets/mcp.js +0 -211
- package/dist/lib/secrets/profiles.d.ts +0 -10
- package/dist/lib/secrets/profiles.js +0 -13
- package/dist/lib/secrets/push.d.ts +0 -133
- package/dist/lib/secrets/push.js +0 -273
- package/dist/lib/secrets/rc-hygiene.d.ts +0 -63
- package/dist/lib/secrets/rc-hygiene.js +0 -143
- package/dist/lib/secrets/read-backoff.d.ts +0 -27
- package/dist/lib/secrets/read-backoff.js +0 -64
- package/dist/lib/secrets/reaper.d.ts +0 -90
- package/dist/lib/secrets/reaper.js +0 -243
- package/dist/lib/secrets/remote.d.ts +0 -152
- package/dist/lib/secrets/remote.js +0 -344
- package/dist/lib/secrets/reserved-sync.d.ts +0 -66
- package/dist/lib/secrets/reserved-sync.js +0 -147
- package/dist/lib/secrets/scope.d.ts +0 -26
- package/dist/lib/secrets/scope.js +0 -29
- package/dist/lib/secrets/session-store.d.ts +0 -107
- package/dist/lib/secrets/session-store.js +0 -342
- package/dist/lib/secrets/sync-backend.d.ts +0 -48
- package/dist/lib/secrets/sync-backend.js +0 -13
- package/dist/lib/secrets/sync-commands.d.ts +0 -21
- package/dist/lib/secrets/sync-commands.js +0 -21
- package/dist/lib/secrets/sync.d.ts +0 -48
- package/dist/lib/secrets/sync.js +0 -237
- package/dist/lib/secrets/unlock-hints.d.ts +0 -27
- package/dist/lib/secrets/unlock-hints.js +0 -36
- package/dist/lib/secrets/usage-db.d.ts +0 -46
- package/dist/lib/secrets/usage-db.js +0 -96
- package/dist/lib/secrets/vault-age-helper.d.ts +0 -1
- package/dist/lib/secrets/vault-age-helper.js +0 -34
- package/dist/lib/secrets/vault.d.ts +0 -49
- package/dist/lib/secrets/vault.js +0 -399
- package/dist/lib/secrets/windows.d.ts +0 -81
- package/dist/lib/secrets/windows.js +0 -556
- package/scripts/install-helper.js +0 -97
- /package/dist/lib/{secrets/sync-passphrase.d.ts → sync-passphrase.d.ts} +0 -0
- /package/dist/lib/{secrets/sync-passphrase.js → sync-passphrase.js} +0 -0
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Negative memo for failed/cancelled macOS keychain reads (the "back-off").
|
|
3
|
-
*
|
|
4
|
-
* A cancelled Touch ID sheet is the user saying "not now" — but a polling
|
|
5
|
-
* caller (AGI EXT host's `agents view` loop, a watch script)
|
|
6
|
-
* retries the same read a few seconds later and pops the sheet again, forever.
|
|
7
|
-
* The headless guard (index.ts `assertRawKeychainReadAllowed`) covers the
|
|
8
|
-
* no-TTY case; this memo covers a context that CAN prompt but just had its
|
|
9
|
-
* prompt cancelled or fail: the next read of the same item within the TTL
|
|
10
|
-
* throws the back-off error instead of re-prompting. Any successful read (or
|
|
11
|
-
* write) of the item clears the memo.
|
|
12
|
-
*
|
|
13
|
-
* Stored as regenerable state under `~/.agents/.cache/keychain-read-backoff/`,
|
|
14
|
-
* one file per item (filename is a hash of the item name; the file carries no
|
|
15
|
-
* secret material — a name and a deadline only). All operations are
|
|
16
|
-
* best-effort: a lost memo costs at most one extra prompt, never a read.
|
|
17
|
-
*/
|
|
18
|
-
import { createHash } from 'node:crypto';
|
|
19
|
-
import * as fs from 'fs';
|
|
20
|
-
import * as os from 'os';
|
|
21
|
-
import * as path from 'path';
|
|
22
|
-
/** How long a failed/cancelled read suppresses retries of the same item. */
|
|
23
|
-
export const KEYCHAIN_READ_BACKOFF_TTL_MS = 5 * 60 * 1000;
|
|
24
|
-
let dirOverride = null;
|
|
25
|
-
/** Test seam: point the memo at a temp dir so tests never touch the real cache. */
|
|
26
|
-
export function setKeychainReadBackoffDirForTest(dir) {
|
|
27
|
-
dirOverride = dir;
|
|
28
|
-
}
|
|
29
|
-
function backoffDir() {
|
|
30
|
-
return dirOverride ?? path.join(os.homedir(), '.agents', '.cache', 'keychain-read-backoff');
|
|
31
|
-
}
|
|
32
|
-
function backoffFile(key) {
|
|
33
|
-
const digest = createHash('sha256').update(key).digest('hex').slice(0, 24);
|
|
34
|
-
return path.join(backoffDir(), `${digest}.json`);
|
|
35
|
-
}
|
|
36
|
-
/** True while `key` is inside the back-off window opened by a failed/cancelled read. */
|
|
37
|
-
export function isKeychainReadBackedOff(key, now = Date.now()) {
|
|
38
|
-
try {
|
|
39
|
-
const parsed = JSON.parse(fs.readFileSync(backoffFile(key), 'utf8'));
|
|
40
|
-
return typeof parsed.until === 'number' && parsed.until > now;
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
return false; // absent or malformed memo → no back-off
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/** Open (or refresh) the back-off window for `key` after a failed/cancelled read. */
|
|
47
|
-
export function noteKeychainReadFailure(key, now = Date.now()) {
|
|
48
|
-
try {
|
|
49
|
-
fs.mkdirSync(backoffDir(), { recursive: true, mode: 0o700 });
|
|
50
|
-
fs.writeFileSync(backoffFile(key), JSON.stringify({ item: key, until: now + KEYCHAIN_READ_BACKOFF_TTL_MS }), { mode: 0o600 });
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
/* best-effort — the cache dir is regenerable; a lost memo costs one prompt */
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/** Clear the memo: a successful read or write of the item resets the back-off. */
|
|
57
|
-
export function clearKeychainReadBackoff(key) {
|
|
58
|
-
try {
|
|
59
|
-
fs.rmSync(backoffFile(key), { force: true });
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
/* best-effort */
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reaper for orphaned / wedged keychain-helper processes.
|
|
3
|
-
*
|
|
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}.
|
|
8
|
-
*/
|
|
9
|
-
/** Grace before a helper whose parent exited is considered an orphan. */
|
|
10
|
-
export declare const ORPHAN_GRACE_SEC = 30;
|
|
11
|
-
/** Grace before a helper with a live parent is considered stuck. */
|
|
12
|
-
export declare const STUCK_GRACE_SEC = 90;
|
|
13
|
-
/** Snapshot of one process row from `ps`, fed into the pure planner. */
|
|
14
|
-
export interface KeychainProcessSnapshot {
|
|
15
|
-
pid: number;
|
|
16
|
-
ppid: number;
|
|
17
|
-
/** Elapsed seconds since the process started. */
|
|
18
|
-
elapsedSec: number;
|
|
19
|
-
/** Stable start-time fingerprint from {@link captureProcessStartTime}; null fails closed. */
|
|
20
|
-
startTime: string | null;
|
|
21
|
-
/** True for a reap-eligible helper invocation (short-lived keychain verb). */
|
|
22
|
-
isHelper: boolean;
|
|
23
|
-
/**
|
|
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).
|
|
28
|
-
*/
|
|
29
|
-
isWatchLock?: boolean;
|
|
30
|
-
}
|
|
31
|
-
/** Tracked state for a stuck parent across two-sweep debounce. */
|
|
32
|
-
export interface StuckParentCandidate {
|
|
33
|
-
pid: number;
|
|
34
|
-
startTime: string | null;
|
|
35
|
-
helperPid: number;
|
|
36
|
-
helperStartTime: string | null;
|
|
37
|
-
firstSeenAt: number;
|
|
38
|
-
stage: 'watch' | 'escalate';
|
|
39
|
-
}
|
|
40
|
-
/** Result of one planning pass. */
|
|
41
|
-
export interface ReapPlan {
|
|
42
|
-
/** PIDs to kill this tick. */
|
|
43
|
-
kill: number[];
|
|
44
|
-
/** Candidates to carry forward to the next sweep. */
|
|
45
|
-
nextCandidates: Map<number, StuckParentCandidate>;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Pure predicate: decide which processes to kill given a `ps`-like snapshot.
|
|
49
|
-
*
|
|
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.
|
|
55
|
-
*
|
|
56
|
-
* Fails closed when start time can't be captured.
|
|
57
|
-
*/
|
|
58
|
-
export declare function planKeychainReap(snapshots: KeychainProcessSnapshot[], now: number, prevCandidates: ReadonlyMap<number, StuckParentCandidate>): ReapPlan;
|
|
59
|
-
/**
|
|
60
|
-
* Parse macOS `ps -o etime=` elapsed time into whole seconds.
|
|
61
|
-
*
|
|
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`.
|
|
64
|
-
*/
|
|
65
|
-
export declare function parseEtimeToSeconds(raw: string): number | null;
|
|
66
|
-
/**
|
|
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.
|
|
70
|
-
*/
|
|
71
|
-
export declare function isReapableHelperCommand(command: string, helperPath: string): boolean;
|
|
72
|
-
/**
|
|
73
|
-
* Whether a `ps` command line is the deliberately long-lived `watch-lock`
|
|
74
|
-
* watcher. Inverse of {@link isReapableHelperCommand} for the watch-lock verb.
|
|
75
|
-
*/
|
|
76
|
-
export declare function isWatchLockHelperCommand(command: string, helperPath: string): boolean;
|
|
77
|
-
/** Test seam: reset the persisted candidate state. */
|
|
78
|
-
export declare function resetKeychainReaperCandidatesForTest(): void;
|
|
79
|
-
/**
|
|
80
|
-
* Impure driver: snapshot all processes once with `ps`, plan the reap, then
|
|
81
|
-
* execute kills through {@link killTree}.
|
|
82
|
-
*
|
|
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.
|
|
85
|
-
*/
|
|
86
|
-
export declare function reapOrphanedKeychainProcesses(): Promise<{
|
|
87
|
-
reaped: number;
|
|
88
|
-
details: string[];
|
|
89
|
-
plan: ReapPlan;
|
|
90
|
-
}>;
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reaper for orphaned / wedged keychain-helper processes.
|
|
3
|
-
*
|
|
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}.
|
|
8
|
-
*/
|
|
9
|
-
import { killTree, captureProcessStartTime } from '../platform/process.js';
|
|
10
|
-
import { execFileBounded } from '../exec-bounded.js';
|
|
11
|
-
/** Deadline for the whole-process-table `ps` on the keychain-reap tick. */
|
|
12
|
-
const KEYCHAIN_PS_TIMEOUT_MS = 5_000;
|
|
13
|
-
import { getKeychainHelperPath } from './install-helper.js';
|
|
14
|
-
/** Grace before a helper whose parent exited is considered an orphan. */
|
|
15
|
-
export const ORPHAN_GRACE_SEC = 30;
|
|
16
|
-
/** Grace before a helper with a live parent is considered stuck. */
|
|
17
|
-
export const STUCK_GRACE_SEC = 90;
|
|
18
|
-
/**
|
|
19
|
-
* Pure predicate: decide which processes to kill given a `ps`-like snapshot.
|
|
20
|
-
*
|
|
21
|
-
* Three conservative classes:
|
|
22
|
-
* 1. Orphaned helper: PPID == 1, helper path, alive longer than grace.
|
|
23
|
-
* 2. Stuck `agents` parent: helper child alive longer than stuck grace,
|
|
24
|
-
* recorded on first sight, child killed on second sweep, parent on third.
|
|
25
|
-
* 3. Orphaned `watch-lock`: long-lived watcher whose owning daemon is gone.
|
|
26
|
-
*
|
|
27
|
-
* Fails closed when start time can't be captured.
|
|
28
|
-
*/
|
|
29
|
-
export function planKeychainReap(snapshots, now, prevCandidates) {
|
|
30
|
-
const pidMap = new Map(snapshots.map((s) => [s.pid, s]));
|
|
31
|
-
const kill = [];
|
|
32
|
-
const nextCandidates = new Map();
|
|
33
|
-
for (const s of snapshots) {
|
|
34
|
-
if (!s.isHelper)
|
|
35
|
-
continue;
|
|
36
|
-
if (s.ppid === 1) {
|
|
37
|
-
// Orphaned helper: fail closed without a start-time fingerprint.
|
|
38
|
-
if (s.elapsedSec <= ORPHAN_GRACE_SEC)
|
|
39
|
-
continue;
|
|
40
|
-
if (s.startTime == null)
|
|
41
|
-
continue;
|
|
42
|
-
kill.push(s.pid);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
// Stuck agents: helper with a live parent wedged longer than the timeout.
|
|
46
|
-
if (s.elapsedSec <= STUCK_GRACE_SEC)
|
|
47
|
-
continue;
|
|
48
|
-
const parent = pidMap.get(s.ppid);
|
|
49
|
-
if (!parent)
|
|
50
|
-
continue;
|
|
51
|
-
if (s.startTime == null || parent.startTime == null)
|
|
52
|
-
continue;
|
|
53
|
-
const prev = prevCandidates.get(parent.pid);
|
|
54
|
-
if (prev &&
|
|
55
|
-
prev.helperPid === s.pid &&
|
|
56
|
-
prev.helperStartTime === s.startTime &&
|
|
57
|
-
prev.startTime === parent.startTime) {
|
|
58
|
-
if (prev.stage === 'watch') {
|
|
59
|
-
// Second sweep: kill the child first so spawnSync returns; escalate next.
|
|
60
|
-
kill.push(s.pid);
|
|
61
|
-
nextCandidates.set(parent.pid, { ...prev, stage: 'escalate' });
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
// Third sweep: child did not free the parent, so the parent is wedged.
|
|
65
|
-
kill.push(parent.pid);
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
// First sight of this (parent, helper) pair — record it and wait.
|
|
69
|
-
nextCandidates.set(parent.pid, {
|
|
70
|
-
pid: parent.pid,
|
|
71
|
-
startTime: parent.startTime,
|
|
72
|
-
helperPid: s.pid,
|
|
73
|
-
helperStartTime: s.startTime,
|
|
74
|
-
firstSeenAt: now,
|
|
75
|
-
stage: 'watch',
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
// Separate path for orphaned watch-lock watchers: only reap when the owning
|
|
79
|
-
// daemon is provably absent from the process table.
|
|
80
|
-
for (const s of snapshots) {
|
|
81
|
-
if (!s.isWatchLock)
|
|
82
|
-
continue;
|
|
83
|
-
if (s.startTime == null)
|
|
84
|
-
continue;
|
|
85
|
-
if (s.elapsedSec <= ORPHAN_GRACE_SEC)
|
|
86
|
-
continue;
|
|
87
|
-
if (s.ppid !== 1) {
|
|
88
|
-
const parent = pidMap.get(s.ppid);
|
|
89
|
-
if (parent)
|
|
90
|
-
continue;
|
|
91
|
-
// Parent missing means the owner is dead (or reparenting race); safe to reap.
|
|
92
|
-
}
|
|
93
|
-
kill.push(s.pid);
|
|
94
|
-
}
|
|
95
|
-
return { kill, nextCandidates };
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Parse macOS `ps -o etime=` elapsed time into whole seconds.
|
|
99
|
-
*
|
|
100
|
-
* BSD `etime` renders as `[[dd-]hh:]mm:ss`. `etimes` (raw seconds) is a GNU
|
|
101
|
-
* extension that macOS `ps` rejects, so darwin uses `etime`.
|
|
102
|
-
*/
|
|
103
|
-
export function parseEtimeToSeconds(raw) {
|
|
104
|
-
const m = raw.match(/^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/);
|
|
105
|
-
if (!m)
|
|
106
|
-
return null;
|
|
107
|
-
const days = m[1] ? parseInt(m[1], 10) : 0;
|
|
108
|
-
const hours = m[2] ? parseInt(m[2], 10) : 0;
|
|
109
|
-
const mins = parseInt(m[3], 10);
|
|
110
|
-
const secs = parseInt(m[4], 10);
|
|
111
|
-
if ([days, hours, mins, secs].some(isNaN))
|
|
112
|
-
return null;
|
|
113
|
-
return ((days * 24 + hours) * 60 + mins) * 60 + secs;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Parse one `ps` output line.
|
|
117
|
-
*
|
|
118
|
-
* Expected format from `ps -ax -o pid=,ppid=,etime=,command=`:
|
|
119
|
-
* "<pid> <ppid> <etime> <command...>"
|
|
120
|
-
*/
|
|
121
|
-
function parsePsLine(line) {
|
|
122
|
-
const trimmed = line.trim();
|
|
123
|
-
if (!trimmed)
|
|
124
|
-
return null;
|
|
125
|
-
const m = trimmed.match(/^(\d+)\s+(\d+)\s+(\S+)\s+(.*)$/);
|
|
126
|
-
if (!m)
|
|
127
|
-
return null;
|
|
128
|
-
const pid = parseInt(m[1], 10);
|
|
129
|
-
const ppid = parseInt(m[2], 10);
|
|
130
|
-
const elapsedSec = parseEtimeToSeconds(m[3]);
|
|
131
|
-
const command = m[4];
|
|
132
|
-
if (isNaN(pid) || isNaN(ppid) || elapsedSec == null)
|
|
133
|
-
return null;
|
|
134
|
-
return { pid, ppid, elapsedSec, command };
|
|
135
|
-
}
|
|
136
|
-
/** The one helper verb that is deliberately long-lived: the auto-lock watcher. */
|
|
137
|
-
const HELPER_WATCH_LOCK_VERB = 'watch-lock';
|
|
138
|
-
/**
|
|
139
|
-
* Whether a `ps` command line is a reap-eligible helper invocation: the installed
|
|
140
|
-
* helper binary running a short-lived keychain verb. Returns false for the
|
|
141
|
-
* long-lived `watch-lock` watcher, which must never be mistaken for a stuck read.
|
|
142
|
-
*/
|
|
143
|
-
export function isReapableHelperCommand(command, helperPath) {
|
|
144
|
-
if (command === helperPath)
|
|
145
|
-
return true; // bare exec, no verb — never watch-lock
|
|
146
|
-
if (!command.startsWith(`${helperPath} `))
|
|
147
|
-
return false; // not our helper
|
|
148
|
-
const firstArg = command.slice(helperPath.length + 1).trimStart().split(/\s+/)[0];
|
|
149
|
-
return firstArg !== HELPER_WATCH_LOCK_VERB;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Whether a `ps` command line is the deliberately long-lived `watch-lock`
|
|
153
|
-
* watcher. Inverse of {@link isReapableHelperCommand} for the watch-lock verb.
|
|
154
|
-
*/
|
|
155
|
-
export function isWatchLockHelperCommand(command, helperPath) {
|
|
156
|
-
if (!command.startsWith(`${helperPath} `))
|
|
157
|
-
return false;
|
|
158
|
-
const firstArg = command.slice(helperPath.length + 1).trimStart().split(/\s+/)[0];
|
|
159
|
-
return firstArg === HELPER_WATCH_LOCK_VERB;
|
|
160
|
-
}
|
|
161
|
-
/** Module-state for the two-sweep stuck-parent debounce. */
|
|
162
|
-
let stuckParentCandidates = new Map();
|
|
163
|
-
/** Test seam: reset the persisted candidate state. */
|
|
164
|
-
export function resetKeychainReaperCandidatesForTest() {
|
|
165
|
-
stuckParentCandidates = new Map();
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Impure driver: snapshot all processes once with `ps`, plan the reap, then
|
|
169
|
-
* execute kills through {@link killTree}.
|
|
170
|
-
*
|
|
171
|
-
* Path-matches the full helper path so an unrelated binary named "Agents CLI" is
|
|
172
|
-
* never targeted. Returns on non-darwin without shelling anything.
|
|
173
|
-
*/
|
|
174
|
-
export async function reapOrphanedKeychainProcesses() {
|
|
175
|
-
const details = [];
|
|
176
|
-
if (process.platform !== 'darwin') {
|
|
177
|
-
return { reaped: 0, details, plan: { kill: [], nextCandidates: new Map() } };
|
|
178
|
-
}
|
|
179
|
-
let helperPath;
|
|
180
|
-
try {
|
|
181
|
-
helperPath = getKeychainHelperPath();
|
|
182
|
-
}
|
|
183
|
-
catch (err) {
|
|
184
|
-
return { reaped: 0, details: [`helper path resolution failed: ${err.message}`], plan: { kill: [], nextCandidates: new Map() } };
|
|
185
|
-
}
|
|
186
|
-
let out;
|
|
187
|
-
{
|
|
188
|
-
// Async, deadline-bounded: this whole-process-table `ps` runs on the daemon's
|
|
189
|
-
// shared event loop every keychain-reap tick, so a synchronous `execFileSync`
|
|
190
|
-
// (unbounded) would freeze it (PHNX-3695).
|
|
191
|
-
const res = await execFileBounded('ps', ['-ax', '-o', 'pid=,ppid=,etime=,command='], { timeoutMs: KEYCHAIN_PS_TIMEOUT_MS });
|
|
192
|
-
if (res.code !== 0) {
|
|
193
|
-
return { reaped: 0, details: [`ps failed${res.timedOut ? ' (timed out)' : ''}: ${res.stderr.trim() || `exit ${res.code}`}`], plan: { kill: [], nextCandidates: new Map() } };
|
|
194
|
-
}
|
|
195
|
-
out = res.stdout;
|
|
196
|
-
}
|
|
197
|
-
const rows = [];
|
|
198
|
-
for (const line of out.split('\n')) {
|
|
199
|
-
const parsed = parsePsLine(line);
|
|
200
|
-
if (!parsed)
|
|
201
|
-
continue;
|
|
202
|
-
const { pid, ppid, elapsedSec, command } = parsed;
|
|
203
|
-
// Match the full argv so the long-lived `watch-lock` watcher is never
|
|
204
|
-
// mistaken for a stuck read. Orphaned watch-locks are tracked separately.
|
|
205
|
-
const isHelper = isReapableHelperCommand(command, helperPath);
|
|
206
|
-
const isWatchLock = isWatchLockHelperCommand(command, helperPath);
|
|
207
|
-
rows.push({ pid, ppid, elapsedSec, isHelper, isWatchLock, startTime: null });
|
|
208
|
-
}
|
|
209
|
-
const rowByPid = new Map(rows.map((r) => [r.pid, r]));
|
|
210
|
-
for (const row of rows) {
|
|
211
|
-
if (!row.isHelper && !row.isWatchLock)
|
|
212
|
-
continue;
|
|
213
|
-
row.startTime = captureProcessStartTime(row.pid);
|
|
214
|
-
if (row.isHelper && row.ppid !== 1 && row.elapsedSec > STUCK_GRACE_SEC) {
|
|
215
|
-
const parent = rowByPid.get(row.ppid);
|
|
216
|
-
if (parent && parent.startTime === null) {
|
|
217
|
-
parent.startTime = captureProcessStartTime(row.ppid);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const snapshots = rows.map((r) => ({
|
|
222
|
-
pid: r.pid,
|
|
223
|
-
ppid: r.ppid,
|
|
224
|
-
elapsedSec: r.elapsedSec,
|
|
225
|
-
startTime: r.startTime,
|
|
226
|
-
isHelper: r.isHelper,
|
|
227
|
-
isWatchLock: r.isWatchLock,
|
|
228
|
-
}));
|
|
229
|
-
const plan = planKeychainReap(snapshots, Date.now(), stuckParentCandidates);
|
|
230
|
-
stuckParentCandidates = plan.nextCandidates;
|
|
231
|
-
for (const pid of plan.kill) {
|
|
232
|
-
if (pid === process.pid)
|
|
233
|
-
continue; // never self-terminate the reaper
|
|
234
|
-
try {
|
|
235
|
-
killTree(pid);
|
|
236
|
-
details.push(`killed pid ${pid}`);
|
|
237
|
-
}
|
|
238
|
-
catch (err) {
|
|
239
|
-
details.push(`failed to kill pid ${pid}: ${err.message}`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return { reaped: plan.kill.length, details, plan };
|
|
243
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remote secrets — read and use `agents secrets` bundles that live on another
|
|
3
|
-
* host over the same SSH path that `agents secrets export --device` uses.
|
|
4
|
-
*
|
|
5
|
-
* Browse streams remote stdout verbatim; use resolves a bundle to an env map
|
|
6
|
-
* and injects it ephemerally without persisting it locally.
|
|
7
|
-
*/
|
|
8
|
-
import { type SshExecResult } from '../ssh-exec.js';
|
|
9
|
-
export declare const REMOTE_TIMEOUT_MS = 30000;
|
|
10
|
-
export interface RemoteSecretsRawOptions {
|
|
11
|
-
tty?: boolean;
|
|
12
|
-
input?: string;
|
|
13
|
-
osLookupName?: string;
|
|
14
|
-
secret?: boolean;
|
|
15
|
-
timeoutMs?: number;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* SSH options for secret-carrying transport. Pins the managed host key and
|
|
19
|
-
* disables multiplex so no reusable control socket lingers for other invocations.
|
|
20
|
-
*/
|
|
21
|
-
export declare function credentialTransportSshOpts(target: string): {
|
|
22
|
-
hostKeyOpts: string[];
|
|
23
|
-
multiplex: false;
|
|
24
|
-
};
|
|
25
|
-
/** Refuse durable credential transfer until the destination SSH key is pinned. */
|
|
26
|
-
export declare function assertCredentialTransportHostPinned(target: string, pinned?: boolean): void;
|
|
27
|
-
/**
|
|
28
|
-
* Trust boundary for a remote-resolved env map. A peer's export output is
|
|
29
|
-
* untrusted input that could reshape this process once merged into the env, so
|
|
30
|
-
* block dangerous override classes here for every consumer:
|
|
31
|
-
* loader/interpreter vars, GIT_*, *_PROXY, and *_BASE_URL.
|
|
32
|
-
*/
|
|
33
|
-
export declare function isDangerousRemoteEnvKey(name: string): boolean;
|
|
34
|
-
/**
|
|
35
|
-
* Resolve a `--device` value to an ssh target string for the remote-secrets
|
|
36
|
-
* path. Delegates to the same resolver `run --device` uses; on a miss, treats
|
|
37
|
-
* the value as a raw ssh target and validates it.
|
|
38
|
-
*/
|
|
39
|
-
export declare function resolveHostSshTarget(nameOrAlias: string): Promise<string>;
|
|
40
|
-
/**
|
|
41
|
-
* Merge `--host` / `--hosts` (and their `--device` / `--devices` aliases) into
|
|
42
|
-
* an ordered, de-duplicated list. Empty when none is set.
|
|
43
|
-
*/
|
|
44
|
-
export declare function parseHostsOption(opts: {
|
|
45
|
-
host?: string;
|
|
46
|
-
hosts?: string;
|
|
47
|
-
device?: string;
|
|
48
|
-
devices?: string;
|
|
49
|
-
}): string[];
|
|
50
|
-
/**
|
|
51
|
-
* Split a `bundle@host` reference. No `@` → a local bundle. Bundle names can't
|
|
52
|
-
* contain `@`, so the FIRST `@` separates bundle from ssh target; the target
|
|
53
|
-
* itself may be `user@host` (e.g. `r2.backups@muqsit@box`).
|
|
54
|
-
*/
|
|
55
|
-
export declare function splitBundleRef(ref: string): {
|
|
56
|
-
bundle: string;
|
|
57
|
-
host?: string;
|
|
58
|
-
};
|
|
59
|
-
/**
|
|
60
|
-
* Run `agents secrets <args>` on a remote host over ssh and return the raw
|
|
61
|
-
* result. Used by browse commands; `tty` forces `-tt` so remote passphrase
|
|
62
|
-
* prompts can surface.
|
|
63
|
-
*/
|
|
64
|
-
export declare function remoteSecretsRaw(target: string, args: string[], opts?: RemoteSecretsRawOptions): SshExecResult;
|
|
65
|
-
/** Async, kill-bounded inverse of {@link remoteSecretsRaw} for daemon callers. */
|
|
66
|
-
export declare function remoteSecretsRawAsync(target: string, args: string[], opts?: RemoteSecretsRawOptions): Promise<SshExecResult>;
|
|
67
|
-
/**
|
|
68
|
-
* Run a remote `agents secrets <args>` foreground with local stdio inherited,
|
|
69
|
-
* so the remote sees a real TTY and its passphrase prompt surfaces and reads
|
|
70
|
-
* keystrokes. Output streams to the terminal; only the exit code is returned.
|
|
71
|
-
*/
|
|
72
|
-
export declare function remoteSecretsStream(target: string, args: string[], opts?: {
|
|
73
|
-
osLookupName?: string;
|
|
74
|
-
}): number;
|
|
75
|
-
/**
|
|
76
|
-
* Resolve a remote bundle to a plaintext env map by driving the remote's
|
|
77
|
-
* `agents secrets export <bundle> --plaintext --format json`. Values cross over
|
|
78
|
-
* ssh stdout, parsed in memory, never persisted.
|
|
79
|
-
*
|
|
80
|
-
* The remote unlocks the bundle with its own credentials; this machine does NOT
|
|
81
|
-
* forward its passphrase because the remote bundle is encrypted with the
|
|
82
|
-
* remote's passphrase.
|
|
83
|
-
*/
|
|
84
|
-
export declare function remoteResolveEnv(target: string, bundle: string, opts?: {
|
|
85
|
-
osLookupName?: string;
|
|
86
|
-
}): Promise<Record<string, string>>;
|
|
87
|
-
/**
|
|
88
|
-
* Outcome of a post-push read-back verification.
|
|
89
|
-
* - `ok` — the pushed keys materialized readably.
|
|
90
|
-
* - `locked-keychain` — read-back gave the specific locked-keychain signal.
|
|
91
|
-
* - `error` — a different failure (SSH, timeout, etc.).
|
|
92
|
-
*/
|
|
93
|
-
export type RemoteKeychainWriteVerification = {
|
|
94
|
-
ok: true;
|
|
95
|
-
} | {
|
|
96
|
-
ok: false;
|
|
97
|
-
kind: 'locked-keychain';
|
|
98
|
-
reason: string;
|
|
99
|
-
} | {
|
|
100
|
-
ok: false;
|
|
101
|
-
kind: 'error';
|
|
102
|
-
reason: string;
|
|
103
|
-
};
|
|
104
|
-
/**
|
|
105
|
-
* Decide whether a keychain-backed push to a remote actually persisted its
|
|
106
|
-
* secret value items by reading the bundle back the same way a later resolve
|
|
107
|
-
* will. Catches the silent failure where headless SSH writes metadata but not
|
|
108
|
-
* readable value items to a locked macOS login keychain.
|
|
109
|
-
*/
|
|
110
|
-
export declare function evaluateKeychainWriteVerification(pushedKeys: string[], readBack: {
|
|
111
|
-
ok: true;
|
|
112
|
-
keys: string[];
|
|
113
|
-
} | {
|
|
114
|
-
ok: false;
|
|
115
|
-
stderr: string;
|
|
116
|
-
}): RemoteKeychainWriteVerification;
|
|
117
|
-
/**
|
|
118
|
-
* Actionable error message for a failed keychain-over-SSH push. Names the cause
|
|
119
|
-
* (locked remote login keychain) and steers to the two real fixes.
|
|
120
|
-
*/
|
|
121
|
-
export declare function keychainWriteFailureMessage(host: string, bundle: string, reason: string): string;
|
|
122
|
-
/**
|
|
123
|
-
* Read a bundle back from a remote over SSH and confirm the pushed keys
|
|
124
|
-
* materialized. Drives the marker-gated json transport but keeps only KEY NAMES;
|
|
125
|
-
* plaintext values are discarded immediately. Returns a verification verdict.
|
|
126
|
-
*/
|
|
127
|
-
export declare function verifyRemoteKeychainPush(target: string, bundle: string, pushedKeys: string[], opts?: {
|
|
128
|
-
osLookupName?: string;
|
|
129
|
-
secret?: boolean;
|
|
130
|
-
timeoutMs?: number;
|
|
131
|
-
}): RemoteKeychainWriteVerification;
|
|
132
|
-
/** Async, kill-bounded read-back verification for daemon-originated pushes. */
|
|
133
|
-
export declare function verifyRemoteKeychainPushAsync(target: string, bundle: string, pushedKeys: string[], opts?: {
|
|
134
|
-
osLookupName?: string;
|
|
135
|
-
secret?: boolean;
|
|
136
|
-
timeoutMs?: number;
|
|
137
|
-
}): Promise<RemoteKeychainWriteVerification>;
|
|
138
|
-
/**
|
|
139
|
-
* `bash -lc` command + stdin payload for a file-backed remote import.
|
|
140
|
-
*
|
|
141
|
-
* The file store is passphrase-free: the remote auto-provisions its own machine-
|
|
142
|
-
* local key, so reads are headless. AGENTS_SECRETS_PASSPHRASE must NOT be
|
|
143
|
-
* forwarded (PHNX-2371): a remote keyed to a secret its daemon does not hold
|
|
144
|
-
* reports "Imported N key(s)" then fails every later decrypt.
|
|
145
|
-
*/
|
|
146
|
-
export declare function buildRemoteFileImportCommand(bundle: string, dotenv: string, opts?: {
|
|
147
|
-
force?: boolean;
|
|
148
|
-
policyNever?: boolean;
|
|
149
|
-
}): {
|
|
150
|
-
remoteCmd: string;
|
|
151
|
-
input: string;
|
|
152
|
-
};
|