@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,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Filters for `agents secrets list`.
|
|
3
|
-
*
|
|
4
|
-
* The listing had no filtering at all: `--device`/`--device` pick a machine and
|
|
5
|
-
* `--json` picks a format, but nothing selected over the bundles themselves. On
|
|
6
|
-
* a fleet with fifty-odd bundles the answerable questions — which ones read with
|
|
7
|
-
* no Touch ID at all, which hold a raw literal, which have already expired, what
|
|
8
|
-
* has not been touched in three months — meant piping the table through grep, or
|
|
9
|
-
* were simply unanswerable.
|
|
10
|
-
*
|
|
11
|
-
* Everything here is pure: parsing produces a `SecretsListFilter`, and
|
|
12
|
-
* `bundleMatchesFilter` is a predicate over a bundle plus the ambient facts it
|
|
13
|
-
* cannot derive itself (which bundles the broker currently holds, and the
|
|
14
|
-
* current time). That keeps the whole surface unit-testable without a keychain.
|
|
15
|
-
*
|
|
16
|
-
* Shape follows the `agents sessions` house style: comma-separated lists, an
|
|
17
|
-
* unknown value is a loud error naming the valid set (never a silent ignore),
|
|
18
|
-
* and every axis narrows independently so they AND-compose.
|
|
19
|
-
*/
|
|
20
|
-
import { parseCommaSeparatedList } from '../format.js';
|
|
21
|
-
// From the leaf module, NOT discover.js — the latter imports `../sqlite.js`, so
|
|
22
|
-
// reaching through it for one parser would pull node:sqlite into every `agents
|
|
23
|
-
// secrets` invocation and print Node's SQLite ExperimentalWarning on stderr.
|
|
24
|
-
import { parseTimeFilter } from '../text/relative-time.js';
|
|
25
|
-
import { describeBundle, bundlePolicy, SECRET_TYPES, } from './bundles.js';
|
|
26
|
-
/** Ref kinds a var's value can have, from `describeBundle`. */
|
|
27
|
-
export const REF_KINDS = ['literal', 'keychain', 'env', 'file', 'exec'];
|
|
28
|
-
const POLICIES = ['always', 'hold', 'never'];
|
|
29
|
-
const BACKENDS = ['keychain', 'file', 'vault'];
|
|
30
|
-
/** Default window for `--expiring` with no argument — matches the EXPIRING column. */
|
|
31
|
-
export const DEFAULT_EXPIRING_DAYS = 30;
|
|
32
|
-
/**
|
|
33
|
-
* Validate one comma-separated enum list. An unknown value throws and names the
|
|
34
|
-
* whole valid set — a silent ignore would let `--policy hodl` quietly return
|
|
35
|
-
* every bundle, which reads as "nothing matches that" and is worse than an error.
|
|
36
|
-
* Values are lowercased, matching `parsePolicyOpt`'s handling of policy names.
|
|
37
|
-
*/
|
|
38
|
-
export function parseEnumList(raw, flag, valid) {
|
|
39
|
-
const parts = parseCommaSeparatedList(raw).map((s) => s.toLowerCase());
|
|
40
|
-
if (parts.length === 0) {
|
|
41
|
-
throw new Error(`${flag} requires at least one value. Valid values: ${valid.join(', ')}`);
|
|
42
|
-
}
|
|
43
|
-
for (const p of parts) {
|
|
44
|
-
if (!valid.includes(p)) {
|
|
45
|
-
throw new Error(`Invalid value "${p}" for ${flag}. Valid values: ${valid.join(', ')}`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// De-dup so `--policy hold,hold` behaves like `--policy hold`.
|
|
49
|
-
return [...new Set(parts)];
|
|
50
|
-
}
|
|
51
|
-
/** Parse `--expiring` — a bare flag means the default window, a value means N days. */
|
|
52
|
-
function parseExpiringDays(raw) {
|
|
53
|
-
if (raw === undefined || raw === false)
|
|
54
|
-
return undefined;
|
|
55
|
-
if (raw === true || raw === '')
|
|
56
|
-
return DEFAULT_EXPIRING_DAYS;
|
|
57
|
-
const n = Number(raw);
|
|
58
|
-
// 0 is rejected rather than accepted as a no-op: the window is `0 <= d < N`,
|
|
59
|
-
// so `--expiring 0` can never match anything, not even a key expiring today.
|
|
60
|
-
// A flag that silently returns nothing is worse than one that says why.
|
|
61
|
-
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
|
|
62
|
-
throw new Error(`Invalid --expiring '${raw}'. Use a whole number of days >= 1, e.g. --expiring 7. ` +
|
|
63
|
-
'For keys that have already lapsed, use --expired.');
|
|
64
|
-
}
|
|
65
|
-
return n;
|
|
66
|
-
}
|
|
67
|
-
/** Build a validated filter from commander's option bag. Throws on bad input. */
|
|
68
|
-
export function parseListFilters(opts, query) {
|
|
69
|
-
if (opts.held && opts.notHeld) {
|
|
70
|
-
throw new Error('--held and --not-held are mutually exclusive');
|
|
71
|
-
}
|
|
72
|
-
const filter = {};
|
|
73
|
-
const trimmedQuery = query?.trim();
|
|
74
|
-
if (trimmedQuery)
|
|
75
|
-
filter.query = trimmedQuery.toLowerCase();
|
|
76
|
-
if (opts.policy)
|
|
77
|
-
filter.policy = parseEnumList(opts.policy, '--policy', POLICIES);
|
|
78
|
-
if (opts.backend)
|
|
79
|
-
filter.backend = parseEnumList(opts.backend, '--backend', BACKENDS);
|
|
80
|
-
if (opts.type)
|
|
81
|
-
filter.type = parseEnumList(opts.type, '--type', SECRET_TYPES);
|
|
82
|
-
if (opts.kind)
|
|
83
|
-
filter.kind = parseEnumList(opts.kind, '--kind', REF_KINDS);
|
|
84
|
-
if (opts.held)
|
|
85
|
-
filter.held = true;
|
|
86
|
-
if (opts.notHeld)
|
|
87
|
-
filter.held = false;
|
|
88
|
-
if (opts.expired)
|
|
89
|
-
filter.expired = true;
|
|
90
|
-
const expiringDays = parseExpiringDays(opts.expiring);
|
|
91
|
-
if (expiringDays !== undefined)
|
|
92
|
-
filter.expiringDays = expiringDays;
|
|
93
|
-
if (opts.unused) {
|
|
94
|
-
// parseTimeFilter turns '90d' into "the epoch-ms 90 days ago"; a bundle is
|
|
95
|
-
// unused when its last_used predates that instant.
|
|
96
|
-
const cutoff = parseTimeFilter(opts.unused);
|
|
97
|
-
if (!cutoff) {
|
|
98
|
-
throw new Error(`Invalid --unused '${opts.unused}'. Use e.g. 30d, 4w, 3mo, or an ISO date.`);
|
|
99
|
-
}
|
|
100
|
-
filter.unusedBefore = cutoff;
|
|
101
|
-
}
|
|
102
|
-
return filter;
|
|
103
|
-
}
|
|
104
|
-
/** True when any axis is set — used to decide whether the empty state should
|
|
105
|
-
* explain itself rather than claim there are no bundles at all. */
|
|
106
|
-
export function filterIsActive(f) {
|
|
107
|
-
return Object.keys(f).length > 0;
|
|
108
|
-
}
|
|
109
|
-
/** Whole days from now until end-of-day UTC of an ISO date. Negative once past.
|
|
110
|
-
* Mirrors the `daysUntil` used by the human render so the filter and the column
|
|
111
|
-
* can never disagree about whether something has expired. */
|
|
112
|
-
function daysUntil(iso, now) {
|
|
113
|
-
const target = new Date(`${iso}T23:59:59Z`).getTime();
|
|
114
|
-
return Math.floor((target - now) / (24 * 60 * 60 * 1000));
|
|
115
|
-
}
|
|
116
|
-
/** Expiry tallies for one bundle: how many vars are already past, and how many
|
|
117
|
-
* fall due within `withinDays`. */
|
|
118
|
-
export function bundleExpiry(b, now, withinDays = DEFAULT_EXPIRING_DAYS) {
|
|
119
|
-
let expired = 0;
|
|
120
|
-
let soon = 0;
|
|
121
|
-
for (const m of Object.values(b.meta ?? {})) {
|
|
122
|
-
if (!m.expires)
|
|
123
|
-
continue;
|
|
124
|
-
const d = daysUntil(m.expires, now);
|
|
125
|
-
if (d < 0)
|
|
126
|
-
expired++;
|
|
127
|
-
else if (d < withinDays)
|
|
128
|
-
soon++;
|
|
129
|
-
}
|
|
130
|
-
return { expired, soon };
|
|
131
|
-
}
|
|
132
|
-
/** Does this bundle satisfy every set axis? Pure. */
|
|
133
|
-
export function bundleMatchesFilter(b, f, ctx) {
|
|
134
|
-
if (f.query) {
|
|
135
|
-
const haystack = `${b.name} ${b.description ?? ''}`.toLowerCase();
|
|
136
|
-
if (!haystack.includes(f.query))
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
if (f.policy && !f.policy.includes(bundlePolicy(b)))
|
|
140
|
-
return false;
|
|
141
|
-
if (f.backend && !f.backend.includes(b.backend ?? 'keychain'))
|
|
142
|
-
return false;
|
|
143
|
-
if (f.held !== undefined) {
|
|
144
|
-
// A lapsed entry is not held — same liveness rule the POLICY column uses.
|
|
145
|
-
const exp = ctx.held.get(b.name);
|
|
146
|
-
const isHeld = exp !== undefined && exp > ctx.now;
|
|
147
|
-
if (isHeld !== f.held)
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
if (f.type) {
|
|
151
|
-
const types = Object.values(b.meta ?? {}).map((m) => m.type).filter(Boolean);
|
|
152
|
-
if (!types.some((t) => f.type.includes(t)))
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
if (f.kind) {
|
|
156
|
-
const kinds = describeBundle(b).map((e) => e.kind);
|
|
157
|
-
if (!kinds.some((k) => f.kind.includes(k)))
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
if (f.expired || f.expiringDays !== undefined) {
|
|
161
|
-
const { expired, soon } = bundleExpiry(b, ctx.now, f.expiringDays ?? DEFAULT_EXPIRING_DAYS);
|
|
162
|
-
if (f.expired && expired === 0)
|
|
163
|
-
return false;
|
|
164
|
-
if (f.expiringDays !== undefined && soon === 0)
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
if (f.unusedBefore !== undefined) {
|
|
168
|
-
// Never used counts as unused — it is the strongest form of the answer.
|
|
169
|
-
if (b.last_used && new Date(b.last_used).getTime() >= f.unusedBefore)
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
/** Sort fields for `--sort`. `name` is the default and matches `listBundles()`.
|
|
175
|
-
* `used` is most-recently-used first and `uses` is most-frequently-accessed
|
|
176
|
-
* first; both read the value-free usage read-model (lib/secrets/usage-db.ts). */
|
|
177
|
-
export const SORT_FIELDS = ['name', 'used', 'uses', 'created', 'updated', 'expiry'];
|
|
178
|
-
export function parseSortField(raw) {
|
|
179
|
-
if (!raw)
|
|
180
|
-
return 'name';
|
|
181
|
-
const v = raw.toLowerCase();
|
|
182
|
-
if (!SORT_FIELDS.includes(v)) {
|
|
183
|
-
throw new Error(`Invalid --sort '${raw}'. Valid values: ${SORT_FIELDS.join(', ')}`);
|
|
184
|
-
}
|
|
185
|
-
return v;
|
|
186
|
-
}
|
|
187
|
-
/** Epoch-ms of a bundle's soonest expiry, or Infinity when nothing expires — so
|
|
188
|
-
* `--sort expiry` puts the most urgent first and never-expiring bundles last. */
|
|
189
|
-
function soonestExpiry(b) {
|
|
190
|
-
let soonest = Infinity;
|
|
191
|
-
for (const m of Object.values(b.meta ?? {})) {
|
|
192
|
-
if (!m.expires)
|
|
193
|
-
continue;
|
|
194
|
-
const t = new Date(`${m.expires}T23:59:59Z`).getTime();
|
|
195
|
-
if (t < soonest)
|
|
196
|
-
soonest = t;
|
|
197
|
-
}
|
|
198
|
-
return soonest;
|
|
199
|
-
}
|
|
200
|
-
/** Sort a copy. Time fields are most-recent-first (the useful direction for
|
|
201
|
-
* "what did I touch lately"); `uses` is most-frequently-accessed first; `expiry`
|
|
202
|
-
* is soonest-first; ties fall back to name so the order is stable. `usage`
|
|
203
|
-
* carries the value-free read-model facts `used`/`uses` rank on — when omitted,
|
|
204
|
-
* `used` falls back to the throttled `last_used` stamp and `uses` sees zero. */
|
|
205
|
-
export function sortBundles(bundles, field, usage) {
|
|
206
|
-
const stamp = (iso) => (iso ? new Date(iso).getTime() : 0);
|
|
207
|
-
const byName = (a, z) => a.name.localeCompare(z.name);
|
|
208
|
-
// `used` combines the throttled keychain stamp with the exact usage-DB recency
|
|
209
|
-
// so a just-recorded access ranks a bundle even before its stamp catches up.
|
|
210
|
-
const usedMs = (b) => Math.max(stamp(b.last_used), stamp(usage?.get(b.name)?.lastUsedAt));
|
|
211
|
-
const usesOf = (b) => usage?.get(b.name)?.uses ?? 0;
|
|
212
|
-
const out = [...bundles];
|
|
213
|
-
if (field === 'name')
|
|
214
|
-
return out.sort(byName);
|
|
215
|
-
out.sort((a, z) => {
|
|
216
|
-
if (field === 'expiry') {
|
|
217
|
-
const d = soonestExpiry(a) - soonestExpiry(z);
|
|
218
|
-
return d !== 0 ? d : byName(a, z);
|
|
219
|
-
}
|
|
220
|
-
if (field === 'used') {
|
|
221
|
-
const d = usedMs(z) - usedMs(a);
|
|
222
|
-
return d !== 0 ? d : byName(a, z);
|
|
223
|
-
}
|
|
224
|
-
if (field === 'uses') {
|
|
225
|
-
const d = usesOf(z) - usesOf(a);
|
|
226
|
-
return d !== 0 ? d : byName(a, z);
|
|
227
|
-
}
|
|
228
|
-
const key = field === 'created' ? 'created_at' : 'updated_at';
|
|
229
|
-
const d = stamp(z[key]) - stamp(a[key]);
|
|
230
|
-
return d !== 0 ? d : byName(a, z);
|
|
231
|
-
});
|
|
232
|
-
return out;
|
|
233
|
-
}
|
|
234
|
-
/** Human summary of the active filters, for the empty state. `sessions` only
|
|
235
|
-
* echoes --project/--all on a miss, which leaves you guessing which flag emptied
|
|
236
|
-
* the list; naming every active axis is the difference between "nothing matched"
|
|
237
|
-
* and knowing what to relax. */
|
|
238
|
-
export function describeFilter(f) {
|
|
239
|
-
const parts = [];
|
|
240
|
-
if (f.query)
|
|
241
|
-
parts.push(`matching "${f.query}"`);
|
|
242
|
-
if (f.policy)
|
|
243
|
-
parts.push(`policy ${f.policy.join('/')}`);
|
|
244
|
-
if (f.backend)
|
|
245
|
-
parts.push(`backend ${f.backend.join('/')}`);
|
|
246
|
-
if (f.type)
|
|
247
|
-
parts.push(`type ${f.type.join('/')}`);
|
|
248
|
-
if (f.kind)
|
|
249
|
-
parts.push(`kind ${f.kind.join('/')}`);
|
|
250
|
-
if (f.held === true)
|
|
251
|
-
parts.push('currently held');
|
|
252
|
-
if (f.held === false)
|
|
253
|
-
parts.push('not currently held');
|
|
254
|
-
if (f.expired)
|
|
255
|
-
parts.push('with an expired key');
|
|
256
|
-
if (f.expiringDays !== undefined)
|
|
257
|
-
parts.push(`expiring within ${f.expiringDays}d`);
|
|
258
|
-
if (f.unusedBefore !== undefined)
|
|
259
|
-
parts.push('unused since the given cutoff');
|
|
260
|
-
return parts.join(', ');
|
|
261
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zero-knowledge / JIT secret delivery over MCP (issue #333).
|
|
3
|
-
*
|
|
4
|
-
* A stdio MCP server exposing a single `get_secret(bundle, key)` tool. Unlike
|
|
5
|
-
* `agents secrets exec`, which spawns the child process with EVERY resolved
|
|
6
|
-
* value baked into `process.env` (any subprocess can `printenv` the lot — see
|
|
7
|
-
* buildSecretsExecEnv in src/commands/secrets.ts), this server hands an
|
|
8
|
-
* MCP-speaking agent framework ONE credential at a time, by name, at call
|
|
9
|
-
* time. The raw value is returned only inside the tool result: it never enters
|
|
10
|
-
* the child's environment, is never logged, and never appears in `tools/list`
|
|
11
|
-
* metadata (names only).
|
|
12
|
-
*
|
|
13
|
-
* Resolution reuses the canonical bundle read path
|
|
14
|
-
* (readAndResolveBundleEnv -> getKeychainToken); no keychain access is
|
|
15
|
-
* re-implemented here. On Linux the libsecret backend (or its encrypted-file
|
|
16
|
-
* fallback for locked/headless collections) applies transparently through that
|
|
17
|
-
* abstraction, so the server stays backend-agnostic and works unchanged on
|
|
18
|
-
* macOS.
|
|
19
|
-
*
|
|
20
|
-
* Transport: the MCP stdio transport is newline-delimited JSON-RPC 2.0 (one
|
|
21
|
-
* message per line, UTF-8, no embedded newlines). That framing is trivial, so
|
|
22
|
-
* we implement it directly rather than pull in the @modelcontextprotocol/sdk
|
|
23
|
-
* server dependency — keeping the tree dependency-free and the handler pure and
|
|
24
|
-
* unit-testable in-process.
|
|
25
|
-
*/
|
|
26
|
-
/** MCP protocol revision this server negotiates. */
|
|
27
|
-
export declare const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
28
|
-
/** The single tool this server exposes. */
|
|
29
|
-
export declare const GET_SECRET_TOOL = "get_secret";
|
|
30
|
-
/** serverInfo.name reported at `initialize`. */
|
|
31
|
-
export declare const MCP_SERVER_NAME = "agents-secrets";
|
|
32
|
-
export interface JsonRpcRequest {
|
|
33
|
-
jsonrpc?: string;
|
|
34
|
-
id?: string | number | null;
|
|
35
|
-
method?: string;
|
|
36
|
-
params?: unknown;
|
|
37
|
-
}
|
|
38
|
-
interface JsonRpcSuccess {
|
|
39
|
-
jsonrpc: '2.0';
|
|
40
|
-
id: string | number | null;
|
|
41
|
-
result: unknown;
|
|
42
|
-
}
|
|
43
|
-
interface JsonRpcErrorResponse {
|
|
44
|
-
jsonrpc: '2.0';
|
|
45
|
-
id: string | number | null;
|
|
46
|
-
error: {
|
|
47
|
-
code: number;
|
|
48
|
-
message: string;
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
export type JsonRpcResponse = JsonRpcSuccess | JsonRpcErrorResponse;
|
|
52
|
-
/** Per-bundle metadata for `tools/list` — names only, NEVER values. */
|
|
53
|
-
export interface BundleKeyMetadata {
|
|
54
|
-
bundle: string;
|
|
55
|
-
description?: string;
|
|
56
|
-
keys: string[];
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Enumerate every bundle and its key names WITHOUT resolving or reading any
|
|
60
|
-
* value. Mirrors how `agents secrets list` surfaces bundle metadata: names are
|
|
61
|
-
* safe to expose, values are not. `listBundles()` reads only the bundle
|
|
62
|
-
* metadata items (var definitions), never the secret items behind them.
|
|
63
|
-
*/
|
|
64
|
-
export declare function listSecretMetadata(): BundleKeyMetadata[];
|
|
65
|
-
/**
|
|
66
|
-
* Resolve a single secret value by bundle + key through the canonical read
|
|
67
|
-
* path. Throws a clear error when the bundle or the key is absent.
|
|
68
|
-
*
|
|
69
|
-
* The bundle metadata is read first (readBundle) so a missing key is reported
|
|
70
|
-
* without triggering resolution — and, on macOS, without prompting Touch ID —
|
|
71
|
-
* for the other keys in the bundle.
|
|
72
|
-
*/
|
|
73
|
-
export declare function resolveSecret(bundle: string, key: string): string;
|
|
74
|
-
/**
|
|
75
|
-
* Handle one parsed JSON-RPC request. Returns the response object, or `null`
|
|
76
|
-
* for notifications (which take no reply). Pure — no I/O, no logging — so tests
|
|
77
|
-
* drive it directly.
|
|
78
|
-
*/
|
|
79
|
-
export declare function handleMcpRequest(req: JsonRpcRequest, ctx?: {
|
|
80
|
-
version?: string;
|
|
81
|
-
}): JsonRpcResponse | null;
|
|
82
|
-
/**
|
|
83
|
-
* Run the stdio MCP server: read newline-delimited JSON-RPC requests from
|
|
84
|
-
* `input`, write responses to `output`. Resolves when the input stream ends.
|
|
85
|
-
* Streams are injectable so tests can drive a full request/response loop
|
|
86
|
-
* without spawning a process.
|
|
87
|
-
*/
|
|
88
|
-
export declare function runSecretsMcpServer(opts?: {
|
|
89
|
-
input?: NodeJS.ReadableStream;
|
|
90
|
-
output?: NodeJS.WritableStream;
|
|
91
|
-
version?: string;
|
|
92
|
-
}): Promise<void>;
|
|
93
|
-
export {};
|
package/dist/lib/secrets/mcp.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zero-knowledge / JIT secret delivery over MCP (issue #333).
|
|
3
|
-
*
|
|
4
|
-
* A stdio MCP server exposing a single `get_secret(bundle, key)` tool. Unlike
|
|
5
|
-
* `agents secrets exec`, which spawns the child process with EVERY resolved
|
|
6
|
-
* value baked into `process.env` (any subprocess can `printenv` the lot — see
|
|
7
|
-
* buildSecretsExecEnv in src/commands/secrets.ts), this server hands an
|
|
8
|
-
* MCP-speaking agent framework ONE credential at a time, by name, at call
|
|
9
|
-
* time. The raw value is returned only inside the tool result: it never enters
|
|
10
|
-
* the child's environment, is never logged, and never appears in `tools/list`
|
|
11
|
-
* metadata (names only).
|
|
12
|
-
*
|
|
13
|
-
* Resolution reuses the canonical bundle read path
|
|
14
|
-
* (readAndResolveBundleEnv -> getKeychainToken); no keychain access is
|
|
15
|
-
* re-implemented here. On Linux the libsecret backend (or its encrypted-file
|
|
16
|
-
* fallback for locked/headless collections) applies transparently through that
|
|
17
|
-
* abstraction, so the server stays backend-agnostic and works unchanged on
|
|
18
|
-
* macOS.
|
|
19
|
-
*
|
|
20
|
-
* Transport: the MCP stdio transport is newline-delimited JSON-RPC 2.0 (one
|
|
21
|
-
* message per line, UTF-8, no embedded newlines). That framing is trivial, so
|
|
22
|
-
* we implement it directly rather than pull in the @modelcontextprotocol/sdk
|
|
23
|
-
* server dependency — keeping the tree dependency-free and the handler pure and
|
|
24
|
-
* unit-testable in-process.
|
|
25
|
-
*/
|
|
26
|
-
import * as readline from 'readline';
|
|
27
|
-
import { listBundles, readAndResolveBundleEnv, readBundle, validateBundleName, } from './bundles.js';
|
|
28
|
-
/** MCP protocol revision this server negotiates. */
|
|
29
|
-
export const MCP_PROTOCOL_VERSION = '2024-11-05';
|
|
30
|
-
/** The single tool this server exposes. */
|
|
31
|
-
export const GET_SECRET_TOOL = 'get_secret';
|
|
32
|
-
/** serverInfo.name reported at `initialize`. */
|
|
33
|
-
export const MCP_SERVER_NAME = 'agents-secrets';
|
|
34
|
-
// JSON-RPC 2.0 error codes we use.
|
|
35
|
-
const JSONRPC_PARSE_ERROR = -32700;
|
|
36
|
-
const JSONRPC_INVALID_REQUEST = -32600;
|
|
37
|
-
const JSONRPC_METHOD_NOT_FOUND = -32601;
|
|
38
|
-
const JSONRPC_INVALID_PARAMS = -32602;
|
|
39
|
-
/**
|
|
40
|
-
* Enumerate every bundle and its key names WITHOUT resolving or reading any
|
|
41
|
-
* value. Mirrors how `agents secrets list` surfaces bundle metadata: names are
|
|
42
|
-
* safe to expose, values are not. `listBundles()` reads only the bundle
|
|
43
|
-
* metadata items (var definitions), never the secret items behind them.
|
|
44
|
-
*/
|
|
45
|
-
export function listSecretMetadata() {
|
|
46
|
-
return listBundles()
|
|
47
|
-
.map((b) => ({
|
|
48
|
-
bundle: b.name,
|
|
49
|
-
description: b.description,
|
|
50
|
-
keys: Object.keys(b.vars).sort(),
|
|
51
|
-
}))
|
|
52
|
-
.sort((a, b) => a.bundle.localeCompare(b.bundle));
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Resolve a single secret value by bundle + key through the canonical read
|
|
56
|
-
* path. Throws a clear error when the bundle or the key is absent.
|
|
57
|
-
*
|
|
58
|
-
* The bundle metadata is read first (readBundle) so a missing key is reported
|
|
59
|
-
* without triggering resolution — and, on macOS, without prompting Touch ID —
|
|
60
|
-
* for the other keys in the bundle.
|
|
61
|
-
*/
|
|
62
|
-
export function resolveSecret(bundle, key) {
|
|
63
|
-
validateBundleName(bundle);
|
|
64
|
-
// readBundle throws `Secrets bundle '<name>' not found.` for a missing bundle.
|
|
65
|
-
const meta = readBundle(bundle);
|
|
66
|
-
if (!Object.prototype.hasOwnProperty.call(meta.vars, key)) {
|
|
67
|
-
const available = Object.keys(meta.vars).sort();
|
|
68
|
-
throw new Error(`Key '${key}' not found in bundle '${bundle}'.` +
|
|
69
|
-
(available.length ? ` Available keys: ${available.join(', ')}.` : ' Bundle has no keys.'));
|
|
70
|
-
}
|
|
71
|
-
// An MCP `get_secret` tool call is a program asking for a value, never a human
|
|
72
|
-
// at a Touch ID sheet — so the read is always `agentOnly` (SEC-13: never pop
|
|
73
|
-
// biometry on its own). A `never`/no-ACL or broker-held bundle resolves
|
|
74
|
-
// silently; a locked bundle THROWS the actionable "unlock <name>" message,
|
|
75
|
-
// which propagates as the MCP tool error (the caller surfaces it) rather than
|
|
76
|
-
// popping an unanswerable prompt.
|
|
77
|
-
const { env } = readAndResolveBundleEnv(bundle, { caller: 'secrets-mcp', keys: [key], keyMode: 'storage', agentOnly: true });
|
|
78
|
-
const value = env[key];
|
|
79
|
-
if (value === undefined) {
|
|
80
|
-
throw new Error(`Key '${key}' in bundle '${bundle}' could not be resolved.`);
|
|
81
|
-
}
|
|
82
|
-
return value;
|
|
83
|
-
}
|
|
84
|
-
/** Build the `get_secret` tool definition, advertising available names only. */
|
|
85
|
-
function getSecretToolDefinition() {
|
|
86
|
-
let catalog = '(no bundles configured)';
|
|
87
|
-
try {
|
|
88
|
-
const meta = listSecretMetadata();
|
|
89
|
-
if (meta.length > 0) {
|
|
90
|
-
catalog = meta
|
|
91
|
-
.map((b) => `${b.bundle}: ${b.keys.length ? b.keys.join(', ') : '(no keys)'}`)
|
|
92
|
-
.join(' | ');
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// Listing is best-effort decoration; never fail tools/list because the
|
|
97
|
-
// keychain enumeration hiccuped.
|
|
98
|
-
catalog = '(unavailable)';
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
name: GET_SECRET_TOOL,
|
|
102
|
-
description: 'Fetch a single secret value by bundle + key, resolved at call time. The value ' +
|
|
103
|
-
'is returned only in this tool result and never enters the process environment. ' +
|
|
104
|
-
`Available bundles and keys (names only): ${catalog}`,
|
|
105
|
-
inputSchema: {
|
|
106
|
-
type: 'object',
|
|
107
|
-
properties: {
|
|
108
|
-
bundle: { type: 'string', description: 'Secrets bundle name' },
|
|
109
|
-
key: { type: 'string', description: 'Key / env-var name within the bundle' },
|
|
110
|
-
},
|
|
111
|
-
required: ['bundle', 'key'],
|
|
112
|
-
additionalProperties: false,
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
function success(id, result) {
|
|
117
|
-
return { jsonrpc: '2.0', id, result };
|
|
118
|
-
}
|
|
119
|
-
function rpcError(id, code, message) {
|
|
120
|
-
return { jsonrpc: '2.0', id, error: { code, message } };
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* A tool-execution failure. Per the MCP spec, tool errors are reported IN the
|
|
124
|
-
* result (`isError: true`) — not as a JSON-RPC error — so the model sees and
|
|
125
|
-
* can react to the message. The value channel (`content`) still carries no
|
|
126
|
-
* secret here.
|
|
127
|
-
*/
|
|
128
|
-
function toolError(id, message) {
|
|
129
|
-
return success(id, { content: [{ type: 'text', text: message }], isError: true });
|
|
130
|
-
}
|
|
131
|
-
function handleToolCall(id, params) {
|
|
132
|
-
const p = (params ?? {});
|
|
133
|
-
if (p.name !== GET_SECRET_TOOL) {
|
|
134
|
-
return rpcError(id, JSONRPC_INVALID_PARAMS, `Unknown tool: ${String(p.name)}`);
|
|
135
|
-
}
|
|
136
|
-
const args = (p.arguments ?? {});
|
|
137
|
-
if (typeof args.bundle !== 'string' || !args.bundle || typeof args.key !== 'string' || !args.key) {
|
|
138
|
-
return toolError(id, `${GET_SECRET_TOOL} requires non-empty string 'bundle' and 'key' arguments.`);
|
|
139
|
-
}
|
|
140
|
-
try {
|
|
141
|
-
const value = resolveSecret(args.bundle, args.key);
|
|
142
|
-
return success(id, { content: [{ type: 'text', text: value }] });
|
|
143
|
-
}
|
|
144
|
-
catch (err) {
|
|
145
|
-
return toolError(id, err.message);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Handle one parsed JSON-RPC request. Returns the response object, or `null`
|
|
150
|
-
* for notifications (which take no reply). Pure — no I/O, no logging — so tests
|
|
151
|
-
* drive it directly.
|
|
152
|
-
*/
|
|
153
|
-
export function handleMcpRequest(req, ctx = {}) {
|
|
154
|
-
const id = req.id ?? null;
|
|
155
|
-
const isNotification = req.id === undefined || req.id === null;
|
|
156
|
-
switch (req.method) {
|
|
157
|
-
case 'initialize':
|
|
158
|
-
return success(id, {
|
|
159
|
-
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
160
|
-
capabilities: { tools: {} },
|
|
161
|
-
serverInfo: { name: MCP_SERVER_NAME, version: ctx.version ?? '0.0.0' },
|
|
162
|
-
});
|
|
163
|
-
case 'notifications/initialized':
|
|
164
|
-
case 'initialized':
|
|
165
|
-
return null; // notification — no response
|
|
166
|
-
case 'ping':
|
|
167
|
-
return success(id, {});
|
|
168
|
-
case 'tools/list':
|
|
169
|
-
return success(id, { tools: [getSecretToolDefinition()] });
|
|
170
|
-
case 'tools/call':
|
|
171
|
-
return handleToolCall(id, req.params);
|
|
172
|
-
default:
|
|
173
|
-
if (isNotification)
|
|
174
|
-
return null; // unknown notification — stay silent
|
|
175
|
-
return rpcError(id, JSONRPC_METHOD_NOT_FOUND, `Method not found: ${String(req.method)}`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
function writeMessage(output, msg) {
|
|
179
|
-
output.write(JSON.stringify(msg) + '\n');
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Run the stdio MCP server: read newline-delimited JSON-RPC requests from
|
|
183
|
-
* `input`, write responses to `output`. Resolves when the input stream ends.
|
|
184
|
-
* Streams are injectable so tests can drive a full request/response loop
|
|
185
|
-
* without spawning a process.
|
|
186
|
-
*/
|
|
187
|
-
export async function runSecretsMcpServer(opts = {}) {
|
|
188
|
-
const input = opts.input ?? process.stdin;
|
|
189
|
-
const output = opts.output ?? process.stdout;
|
|
190
|
-
const rl = readline.createInterface({ input, crlfDelay: Infinity });
|
|
191
|
-
for await (const line of rl) {
|
|
192
|
-
const trimmed = line.trim();
|
|
193
|
-
if (!trimmed)
|
|
194
|
-
continue;
|
|
195
|
-
let req;
|
|
196
|
-
try {
|
|
197
|
-
req = JSON.parse(trimmed);
|
|
198
|
-
}
|
|
199
|
-
catch {
|
|
200
|
-
writeMessage(output, rpcError(null, JSONRPC_PARSE_ERROR, 'Parse error'));
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
if (!req || typeof req !== 'object' || typeof req.method !== 'string') {
|
|
204
|
-
writeMessage(output, rpcError((req && req.id) ?? null, JSONRPC_INVALID_REQUEST, 'Invalid Request'));
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
const res = handleMcpRequest(req, { version: opts.version });
|
|
208
|
-
if (res)
|
|
209
|
-
writeMessage(output, res);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thin compatibility shim over the shared secrets library.
|
|
3
|
-
*
|
|
4
|
-
* Profile tokens are stored at `agents-cli.<provider>.token`; secrets bundles
|
|
5
|
-
* live at `agents-cli.secrets.<bundle>.<KEY>`. Both go through the same
|
|
6
|
-
* keychain primitives in ./index.ts.
|
|
7
|
-
*/
|
|
8
|
-
export { hasKeychainToken, getKeychainToken, setKeychainToken, deleteKeychainToken, } from './index.js';
|
|
9
|
-
/** Map a provider name to its keychain item identifier. */
|
|
10
|
-
export declare function keychainItemName(provider: string): string;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thin compatibility shim over the shared secrets library.
|
|
3
|
-
*
|
|
4
|
-
* Profile tokens are stored at `agents-cli.<provider>.token`; secrets bundles
|
|
5
|
-
* live at `agents-cli.secrets.<bundle>.<KEY>`. Both go through the same
|
|
6
|
-
* keychain primitives in ./index.ts.
|
|
7
|
-
*/
|
|
8
|
-
export { hasKeychainToken, getKeychainToken, setKeychainToken, deleteKeychainToken, } from './index.js';
|
|
9
|
-
import { profileKeychainItem } from './index.js';
|
|
10
|
-
/** Map a provider name to its keychain item identifier. */
|
|
11
|
-
export function keychainItemName(provider) {
|
|
12
|
-
return profileKeychainItem(provider);
|
|
13
|
-
}
|