@phnx-labs/agents-cli 1.22.84 → 1.22.86
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 +96 -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/alias.js +7 -3
- 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 +38 -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 +32 -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 +21 -4
- package/dist/lib/installations/shims.js +86 -5
- 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 +199 -0
- package/dist/lib/secrets-client.js +718 -0
- package/dist/lib/secrets-policy.d.ts +235 -0
- package/dist/lib/secrets-policy.js +505 -0
- package/dist/lib/secrets-types.d.ts +188 -0
- package/dist/lib/secrets-types.js +24 -0
- package/dist/lib/self-heal/checks/shims.js +5 -3
- 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 +35 -30
- 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
package/dist/commands/exec.js
CHANGED
|
@@ -843,6 +843,23 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
843
843
|
console.error(chalk.red(`Invalid account picker target: ${agentSpec}. Use agents run <agent>@.`));
|
|
844
844
|
process.exit(1);
|
|
845
845
|
}
|
|
846
|
+
// Peel `#name` off before --device dispatch so the selector rides the hop
|
|
847
|
+
// unchanged and the peer resolves ITS slot (PHNX-3940 T5). The later local
|
|
848
|
+
// parse is idempotent when the values match.
|
|
849
|
+
{
|
|
850
|
+
const labelParts = normalizedAgentSpec.split('#');
|
|
851
|
+
if (labelParts.length > 2 || labelParts[1] === '') {
|
|
852
|
+
console.error(chalk.red(`Invalid account label in '${normalizedAgentSpec}'.`));
|
|
853
|
+
process.exit(1);
|
|
854
|
+
}
|
|
855
|
+
const specAccountLabel = labelParts[1];
|
|
856
|
+
if (specAccountLabel && options.account && specAccountLabel !== options.account) {
|
|
857
|
+
console.error(chalk.red(`Account '${specAccountLabel}' from the agent spec conflicts with --account '${options.account}'.`));
|
|
858
|
+
process.exit(1);
|
|
859
|
+
}
|
|
860
|
+
if (specAccountLabel)
|
|
861
|
+
options.account = specAccountLabel;
|
|
862
|
+
}
|
|
846
863
|
// Hard-deprecated harnesses cannot be run — point the user at the successor.
|
|
847
864
|
const runBaseAgentName = normalizedAgentSpec.split('#')[0].split('@')[0];
|
|
848
865
|
const runBaseAgentId = resolveAgentName(runBaseAgentName);
|
|
@@ -1130,11 +1147,11 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1130
1147
|
// with an actionable hint instead of hard-failing the run (F5).
|
|
1131
1148
|
if (netMode === 'tailscale') {
|
|
1132
1149
|
const { pickTailscaleBundleFromList } = await import('../lib/crabbox/cli.js');
|
|
1133
|
-
const { listBundles } = await import('../lib/secrets
|
|
1150
|
+
const { listBundles } = await import('../lib/secrets-client.js');
|
|
1134
1151
|
let hasKey = !!process.env.CRABBOX_TAILSCALE_AUTH_KEY;
|
|
1135
1152
|
if (!hasKey) {
|
|
1136
1153
|
try {
|
|
1137
|
-
hasKey = !!pickTailscaleBundleFromList(listBundles());
|
|
1154
|
+
hasKey = !!pickTailscaleBundleFromList(await listBundles());
|
|
1138
1155
|
}
|
|
1139
1156
|
catch {
|
|
1140
1157
|
hasKey = false;
|
|
@@ -1800,12 +1817,16 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1800
1817
|
}
|
|
1801
1818
|
process.exit(resumeExit);
|
|
1802
1819
|
}
|
|
1803
|
-
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, implicitModeFor, headlessPlanStallCommand, nativeResume, resolveInteractive, inferredInteractiveWithoutTty }, { ALL_AGENT_IDS, ACCOUNT_INSPECTION_AGENT_IDS, agentLabel, supportsAccountInspection }, { profileExists, readProfile, resolveProfileForRun },
|
|
1820
|
+
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, implicitModeFor, headlessPlanStallCommand, nativeResume, resolveInteractive, inferredInteractiveWithoutTty }, { ALL_AGENT_IDS, ACCOUNT_INSPECTION_AGENT_IDS, agentLabel, supportsAccountInspection }, { profileExists, readProfile, resolveProfileForRun },
|
|
1821
|
+
// Engine DATA ops resolve through the standalone `secrets` process client
|
|
1822
|
+
// (PHNX-3989); the agents-owned `bundle@host` policy helpers stay in agents-cli.
|
|
1823
|
+
{ readAndResolveBundleEnv, describeBundle, remoteResolveEnv }, { assertRemoteBundleFlagsUnsupported, splitBundleRef, resolveSecretsContextForRun }, { resolveHostSshTarget }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, rotationFailoverChain, shouldArmRotationFailover, RUN_STRATEGIES, collectHarnessCandidates, pickHarnessWeighted, classifyHarnessCandidates, formatHarnessPickBanner, formatNoHealthyHarnessError, formatNoHealthyAccountError, formatNoVerifiedUsageError, signInRecoverableCandidates }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias, ensureAgentRunnable }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef, resolveAllowedSubagents, pruneStaleWorkflowSubagents, ensureSubagentDispatchTool }, { resolveRunDefaults }, { getMcpServersByName, buildWorkflowMcpConfig }, { supports, capableAgents }, { shareRuntimeEnv },] = await Promise.all([
|
|
1804
1824
|
import('../lib/exec.js'),
|
|
1805
1825
|
import('../lib/agents.js'),
|
|
1806
1826
|
import('../lib/profiles.js'),
|
|
1807
|
-
import('../lib/secrets
|
|
1808
|
-
import('../lib/secrets
|
|
1827
|
+
import('../lib/secrets-client.js'),
|
|
1828
|
+
import('../lib/secrets-policy.js'),
|
|
1829
|
+
import('../lib/hosts/credential-transport.js'),
|
|
1809
1830
|
import('../lib/accounting/rotate.js'),
|
|
1810
1831
|
import('../lib/installations/versions.js'),
|
|
1811
1832
|
import('../lib/plugins/plugins.js'),
|
|
@@ -1844,6 +1865,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1844
1865
|
let profileEnv;
|
|
1845
1866
|
let accountEnv;
|
|
1846
1867
|
let accountConfigVersion;
|
|
1868
|
+
let execHome;
|
|
1847
1869
|
let profileProvider;
|
|
1848
1870
|
let fromProfile = false;
|
|
1849
1871
|
let profileName;
|
|
@@ -2169,6 +2191,10 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2169
2191
|
if (!selected)
|
|
2170
2192
|
return;
|
|
2171
2193
|
version = selected.version;
|
|
2194
|
+
if (selected.nativeAccount)
|
|
2195
|
+
options.account = selected.nativeAccount;
|
|
2196
|
+
if (selected.slotDir)
|
|
2197
|
+
execHome = selected.slotDir;
|
|
2172
2198
|
if (!options.quiet) {
|
|
2173
2199
|
const identity = selected.accountLabel || 'signed-in account';
|
|
2174
2200
|
process.stderr.write(chalk.gray(`[agents] selected ${identity} · ${agent}@${selected.version} for this run\n`));
|
|
@@ -2204,35 +2230,49 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2204
2230
|
process.exit(1);
|
|
2205
2231
|
}
|
|
2206
2232
|
if (spawnAccount.kind === 'native') {
|
|
2207
|
-
//
|
|
2208
|
-
//
|
|
2209
|
-
// injected. Fail closed for a remote target.
|
|
2233
|
+
// `#name` / `--account` on a --device run is forwarded unchanged at
|
|
2234
|
+
// dispatch time; the peer resolves ITS slot. This block is local-only.
|
|
2210
2235
|
const remoteTarget = options.host || options.device;
|
|
2211
|
-
if (remoteTarget) {
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2236
|
+
if (!remoteTarget) {
|
|
2237
|
+
const { adoptedConfigPointsAtHome, adoptedSymlinkMismatchError, isSymlinkAdoptedHarness, resolveNativeSpawnHome, symlinkAdoptedAccountError, } = await import('../lib/exec-account-home.js');
|
|
2238
|
+
const { readMeta } = await import('../lib/state.js');
|
|
2239
|
+
const meta = readMeta();
|
|
2240
|
+
if (isSymlinkAdoptedHarness(agent)) {
|
|
2241
|
+
const defaultName = meta.accounts?.defaults?.[agent];
|
|
2242
|
+
if (spawnAccount.name !== defaultName) {
|
|
2243
|
+
console.error(chalk.red(symlinkAdoptedAccountError(agent, spawnAccount.name, defaultName)));
|
|
2244
|
+
process.exit(1);
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
try {
|
|
2248
|
+
const resolved = await resolveNativeSpawnHome(agent, spawnAccount, meta);
|
|
2249
|
+
if (isSymlinkAdoptedHarness(agent) && !adoptedConfigPointsAtHome(agent, resolved.execHome)) {
|
|
2250
|
+
console.error(chalk.red(adoptedSymlinkMismatchError(agent, spawnAccount.name, resolved.execHome)));
|
|
2251
|
+
process.exit(1);
|
|
2252
|
+
}
|
|
2253
|
+
execHome = resolved.execHome;
|
|
2254
|
+
if (resolved.source === 'legacy-home') {
|
|
2255
|
+
accountConfigVersion = resolved.label;
|
|
2256
|
+
// A leftover version-labeled home is both the spawn HOME and
|
|
2257
|
+
// the account's installation — the pre-T5 `resolveAccountVersion`
|
|
2258
|
+
// behavior. An explicit `@<label>` still controls the binary.
|
|
2259
|
+
if (!version && !fromProfile && resolved.label)
|
|
2260
|
+
version = resolved.label;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
catch (err) {
|
|
2264
|
+
console.error(chalk.red(err.message));
|
|
2265
|
+
process.exit(1);
|
|
2266
|
+
}
|
|
2267
|
+
// Slot / provisioned: binary comes from the one managed install unless `@<label>` pins it.
|
|
2268
|
+
if (!version && !fromProfile) {
|
|
2269
|
+
const { ensureHarnessInstallation } = await import('../lib/installations/store.js');
|
|
2270
|
+
const { installation } = await ensureHarnessInstallation(agent);
|
|
2271
|
+
version = installation.label;
|
|
2272
|
+
}
|
|
2273
|
+
if (!options.quiet)
|
|
2274
|
+
process.stderr.write(chalk.gray(`[agents] account '${spawnAccount.name}' · ${agent}\n`));
|
|
2227
2275
|
}
|
|
2228
|
-
// An account-only run executes the account's own stable installation.
|
|
2229
|
-
// This keeps its model/mode configuration and binary update policy
|
|
2230
|
-
// together. An explicit installation or profile still controls the
|
|
2231
|
-
// executable independently; configVersion continues to select auth.
|
|
2232
|
-
if (!version && !fromProfile)
|
|
2233
|
-
version = accountConfigVersion;
|
|
2234
|
-
if (!options.quiet)
|
|
2235
|
-
process.stderr.write(chalk.gray(`[agents] account '${spawnAccount.name}' · ${agent}\n`));
|
|
2236
2276
|
}
|
|
2237
2277
|
else {
|
|
2238
2278
|
accountEnv = spawnAccount.env;
|
|
@@ -2713,10 +2753,12 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2713
2753
|
authedViaSetupToken = resolveClaudeSetupToken(getVersionHomePath('claude', version)) !== null;
|
|
2714
2754
|
}
|
|
2715
2755
|
if (!info.signedIn && !authedViaSetupToken) {
|
|
2716
|
-
const {
|
|
2717
|
-
const hint =
|
|
2718
|
-
? `agents accounts
|
|
2719
|
-
:
|
|
2756
|
+
const { addSupported } = await import('../lib/accounts/add.js');
|
|
2757
|
+
const hint = spawnAccount?.kind === 'native'
|
|
2758
|
+
? `agents accounts login ${agent}#${spawnAccount.name}`
|
|
2759
|
+
: addSupported(agent)
|
|
2760
|
+
? `agents accounts add ${agent} <name>`
|
|
2761
|
+
: loginHint(agent);
|
|
2720
2762
|
process.stderr.write(chalk.yellow(`${agent} looks logged out — sign in with: ${hint}. Launching anyway...\n`));
|
|
2721
2763
|
}
|
|
2722
2764
|
}
|
|
@@ -2805,6 +2847,11 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2805
2847
|
? options.secretsKeys.split(',').map((k) => k.trim()).filter(Boolean)
|
|
2806
2848
|
: undefined;
|
|
2807
2849
|
let secretsEnv = {};
|
|
2850
|
+
// Resource-profile scoping (CTX-1): agents-cli computes which bundle
|
|
2851
|
+
// names the active profile allows and forwards it as the client's
|
|
2852
|
+
// `allowedBundles` on every local resolution — the standalone has no
|
|
2853
|
+
// concept of a profile. undefined (no active profile) is full trust.
|
|
2854
|
+
const secretsContext = await resolveSecretsContextForRun(agent);
|
|
2808
2855
|
for (const bundleRef of options.secrets) {
|
|
2809
2856
|
try {
|
|
2810
2857
|
const { bundle: bundleName, host } = splitBundleRef(bundleRef);
|
|
@@ -2822,7 +2869,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2822
2869
|
secretsEnv = { ...secretsEnv, ...bundleEnv };
|
|
2823
2870
|
}
|
|
2824
2871
|
else {
|
|
2825
|
-
const { bundle, env: bundleEnv } = readAndResolveBundleEnv(bundleName, {
|
|
2872
|
+
const { bundle, env: bundleEnv } = await readAndResolveBundleEnv(bundleName, {
|
|
2826
2873
|
caller: `agent ${agent}`,
|
|
2827
2874
|
agent,
|
|
2828
2875
|
keys: secretsKeysSubset,
|
|
@@ -2834,8 +2881,8 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2834
2881
|
// isHeadlessSecretsContext() left `--interactive` launches (the watchdog's
|
|
2835
2882
|
// `agents run auto --interactive`) able to prompt, piling up helper sheets.
|
|
2836
2883
|
agentOnly: true,
|
|
2837
|
-
});
|
|
2838
|
-
const entries = describeBundle(bundle);
|
|
2884
|
+
}, secretsContext);
|
|
2885
|
+
const entries = await describeBundle(bundle, secretsContext);
|
|
2839
2886
|
const counts = {};
|
|
2840
2887
|
for (const e of entries) {
|
|
2841
2888
|
counts[e.kind] = (counts[e.kind] || 0) + 1;
|
|
@@ -2881,6 +2928,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2881
2928
|
harnessName: profileName,
|
|
2882
2929
|
version,
|
|
2883
2930
|
configVersion: accountConfigVersion,
|
|
2931
|
+
execHome,
|
|
2884
2932
|
prompt,
|
|
2885
2933
|
interactive: options.interactive || forceInteractive,
|
|
2886
2934
|
mode: requestedMode,
|
|
@@ -15,7 +15,7 @@ import * as yaml from 'yaml';
|
|
|
15
15
|
import { loadDevices } from '../lib/devices/registry.js';
|
|
16
16
|
import { readMeta, updateMeta, getDevicePinsPath } from '../lib/state.js';
|
|
17
17
|
import { machineId } from '../lib/machine-id.js';
|
|
18
|
-
import { listBundles } from '../lib/secrets
|
|
18
|
+
import { listBundles, SecretsClientError } from '../lib/secrets-client.js';
|
|
19
19
|
import { listJobs } from '../lib/scheduling/routines.js';
|
|
20
20
|
import { captureFleet } from '../lib/fleet/capture.js';
|
|
21
21
|
/** Read THIS machine's pins file into `name -> [id@latest]`. Only used with
|
|
@@ -37,6 +37,25 @@ function agentsFromPins(names) {
|
|
|
37
37
|
const ids = pins?.agents ? Object.keys(pins.agents) : [];
|
|
38
38
|
return ids.length > 0 ? { [self]: ids.map((id) => `${id}@latest`) } : {};
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* The names to snapshot into `fleet.secretsBundles` — best-effort. Capture's
|
|
42
|
+
* primary job (device/agent/routine snapshot) is unrelated to secrets, so a
|
|
43
|
+
* missing standalone `secrets` install (DIST-1: no fallback engine) degrades
|
|
44
|
+
* this ONE optional field to empty with a warning rather than failing the
|
|
45
|
+
* whole capture. A real store error still surfaces.
|
|
46
|
+
*/
|
|
47
|
+
async function captureSecretsBundleNames() {
|
|
48
|
+
try {
|
|
49
|
+
return (await listBundles()).map((b) => b.name);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (err instanceof SecretsClientError && err.code === 'SECRETS_BIN_MISSING') {
|
|
53
|
+
console.error(chalk.yellow('warning: standalone `secrets` CLI not installed — capturing with no secrets bundles.'));
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
40
59
|
async function runCapture(opts) {
|
|
41
60
|
const meta = readMeta();
|
|
42
61
|
// Roster: every registered device name.
|
|
@@ -68,7 +87,7 @@ async function runCapture(opts) {
|
|
|
68
87
|
// Browser profiles are intentionally NOT captured — the central `browser:`
|
|
69
88
|
// block already syncs via the repo, and its ssh:// endpoints can carry
|
|
70
89
|
// `user@host`, which must never be copied into the fleet: block.
|
|
71
|
-
secretsBundles:
|
|
90
|
+
secretsBundles: await captureSecretsBundleNames(),
|
|
72
91
|
routines: listJobs().map((j) => j.name),
|
|
73
92
|
};
|
|
74
93
|
// Discovery decisions and dismissals are device-scoped now (PHNX-3315): they
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
import chalk from 'chalk';
|
|
30
30
|
import { baseUrlEnvKeyForHost, authEnvKeyForHost, modelEnvKeyForHost, validateProfileName, listProfiles, } from '../lib/profiles.js';
|
|
31
31
|
import { listPresets, getPreset } from '../lib/profiles-presets.js';
|
|
32
|
-
import { listBundles } from '../lib/secrets
|
|
32
|
+
import { listBundles } from '../lib/secrets-client.js';
|
|
33
33
|
import { AGENTS, ALL_AGENT_IDS, isSelfUpdatingAgent, resolveAgentName } from '../lib/agents.js';
|
|
34
34
|
import { readAccountRegistry } from '../lib/account-registry.js';
|
|
35
35
|
/**
|
|
@@ -127,7 +127,7 @@ function knownProviders() {
|
|
|
127
127
|
* is today's behavior, lifted verbatim; RUSH-2220 enriches the bundle browse.
|
|
128
128
|
*/
|
|
129
129
|
async function askKeySource(io, draft, provider) {
|
|
130
|
-
const bundles = listBundles();
|
|
130
|
+
const bundles = await listBundles();
|
|
131
131
|
const source = bundles.length > 0
|
|
132
132
|
? await io.select({
|
|
133
133
|
message: `How should '${provider}' get its key?`,
|
package/dist/commands/lease.js
CHANGED
|
@@ -11,8 +11,7 @@ import chalk from 'chalk';
|
|
|
11
11
|
import { showUrl } from '../lib/open-url.js';
|
|
12
12
|
import { crabboxList, crabboxStop, reapSafeOrphans, reapOrphans, setLeaseSecretsBundle } from '../lib/crabbox/cli.js';
|
|
13
13
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
14
|
-
import { bundleExists,
|
|
15
|
-
import { secretsKeychainItem } from '../lib/secrets/index.js';
|
|
14
|
+
import { bundleExists, keychainRef, readBundle, secretsKeychainItem, writeBundleWithItems } from '../lib/secrets-client.js';
|
|
16
15
|
function fmtIdle(box) {
|
|
17
16
|
if (box.lastTouchedAt === null)
|
|
18
17
|
return 'idle ?';
|
|
@@ -108,13 +107,11 @@ async function captureTailscaleAuthKey() {
|
|
|
108
107
|
console.error(chalk.dim('Skipped Tailscale setup — public-IP leases only.'));
|
|
109
108
|
return;
|
|
110
109
|
}
|
|
111
|
-
const bundle = bundleExists(TAILSCALE_BUNDLE)
|
|
112
|
-
? readBundle(TAILSCALE_BUNDLE)
|
|
110
|
+
const bundle = (await bundleExists(TAILSCALE_BUNDLE))
|
|
111
|
+
? await readBundle(TAILSCALE_BUNDLE)
|
|
113
112
|
: { name: TAILSCALE_BUNDLE, description: 'Tailscale ephemeral auth key for crabbox tailnet leases', vars: {} };
|
|
114
|
-
const store = bundleItemStore(bundle.backend);
|
|
115
|
-
store.set(secretsKeychainItem(TAILSCALE_BUNDLE, TAILSCALE_KEY), key);
|
|
116
113
|
bundle.vars[TAILSCALE_KEY] = keychainRef(TAILSCALE_KEY);
|
|
117
|
-
|
|
114
|
+
await writeBundleWithItems(bundle, new Map([[secretsKeychainItem(TAILSCALE_BUNDLE, TAILSCALE_KEY), key]]));
|
|
118
115
|
console.error(chalk.green(`✔ Stored Tailscale auth key in keychain bundle '${TAILSCALE_BUNDLE}'.`));
|
|
119
116
|
console.error(chalk.dim(' Add --tailscale to a lease (reuse defaults to it) to reach the box over your tailnet.'));
|
|
120
117
|
}
|
|
@@ -176,13 +173,11 @@ export async function runLeaseSetup(opts = {}) {
|
|
|
176
173
|
else
|
|
177
174
|
spinner.succeed('Token valid — Hetzner API reachable.');
|
|
178
175
|
// Store into the `hetzner.com` keychain bundle (mirrors writeSyncBundle).
|
|
179
|
-
const bundle = bundleExists(HETZNER_BUNDLE)
|
|
180
|
-
? readBundle(HETZNER_BUNDLE)
|
|
176
|
+
const bundle = (await bundleExists(HETZNER_BUNDLE))
|
|
177
|
+
? await readBundle(HETZNER_BUNDLE)
|
|
181
178
|
: { name: HETZNER_BUNDLE, description: 'Hetzner Cloud API token for crabbox leases', vars: {} };
|
|
182
|
-
const store = bundleItemStore(bundle.backend);
|
|
183
|
-
store.set(secretsKeychainItem(HETZNER_BUNDLE, HCLOUD_KEY), token);
|
|
184
179
|
bundle.vars[HCLOUD_KEY] = keychainRef(HCLOUD_KEY);
|
|
185
|
-
|
|
180
|
+
await writeBundleWithItems(bundle, new Map([[secretsKeychainItem(HETZNER_BUNDLE, HCLOUD_KEY), token]]));
|
|
186
181
|
setLeaseSecretsBundle(HETZNER_BUNDLE);
|
|
187
182
|
console.error(chalk.green(`\n✔ Stored in keychain bundle '${HETZNER_BUNDLE}' and set as the default lease provider.`));
|
|
188
183
|
console.error(chalk.dim(' Run `agents run <agent> "…" --lease` — no env var, no flag needed.'));
|
|
@@ -38,8 +38,8 @@ export interface AddProfileOptions {
|
|
|
38
38
|
*
|
|
39
39
|
* Reads the bundle value via {@link getKeychainToken} — this pops Touch ID on
|
|
40
40
|
* macOS once, for that bundle-namespaced item (`agents-cli.secrets.*`, gated by
|
|
41
|
-
*
|
|
42
|
-
* it to `
|
|
41
|
+
* the standalone's user-presence rule for `agents-cli.secrets.*` items) — then writes
|
|
42
|
+
* it to `profileKeychainItem(provider)`, a plain `agents-cli.<provider>.token`
|
|
43
43
|
* item. That item matches neither the `agents-cli.secrets.` nor
|
|
44
44
|
* `agents-cli.bundles.` prefix, so every later read of the harness's own key is
|
|
45
45
|
* silent — no repeat Touch ID prompt.
|
|
@@ -10,9 +10,7 @@ import chalk from 'chalk';
|
|
|
10
10
|
import { readStdinSync } from '../lib/format.js';
|
|
11
11
|
import { writeProfile, profileFromPreset, profileFromHostModel, baseUrlEnvKeyForHost, authEnvKeyForHost, validateProfileName, profileExists, } from '../lib/profiles.js';
|
|
12
12
|
import { getPreset, listPresets } from '../lib/profiles-presets.js';
|
|
13
|
-
import { hasKeychainToken,
|
|
14
|
-
import { parseBundleValue, secretsKeychainItem } from '../lib/secrets/index.js';
|
|
15
|
-
import { readBundle } from '../lib/secrets/bundles.js';
|
|
13
|
+
import { getKeychainToken, hasKeychainToken, isSecretsClientError, parseBundleValue, profileKeychainItem, readBundle, secretsKeychainItem, setKeychainToken, } from '../lib/secrets-client.js';
|
|
16
14
|
import { isInteractiveTerminal } from './utils.js';
|
|
17
15
|
import { ALL_AGENT_IDS } from '../lib/agents.js';
|
|
18
16
|
import { findAccount } from '../lib/account-registry.js';
|
|
@@ -28,7 +26,7 @@ export function buildProfileFromCollection(name, preset, collected, version) {
|
|
|
28
26
|
env: { ...preset.env, ...collected },
|
|
29
27
|
auth: {
|
|
30
28
|
envVar: preset.authEnvVar,
|
|
31
|
-
keychainItem:
|
|
29
|
+
keychainItem: profileKeychainItem(preset.provider),
|
|
32
30
|
},
|
|
33
31
|
authOptional: preset.authOptional,
|
|
34
32
|
description: preset.description,
|
|
@@ -47,8 +45,8 @@ async function promptForSecret(message) {
|
|
|
47
45
|
/** Read all available data from stdin synchronously, trimmed. */
|
|
48
46
|
/** Ensure a provider API key exists in keychain, prompting or reading stdin if missing. */
|
|
49
47
|
export async function ensureProviderToken(provider, signupUrl, fromStdin) {
|
|
50
|
-
const item =
|
|
51
|
-
if (hasKeychainToken(item)) {
|
|
48
|
+
const item = profileKeychainItem(provider);
|
|
49
|
+
if (await hasKeychainToken(item)) {
|
|
52
50
|
return;
|
|
53
51
|
}
|
|
54
52
|
let token;
|
|
@@ -62,7 +60,7 @@ export async function ensureProviderToken(provider, signupUrl, fromStdin) {
|
|
|
62
60
|
const hint = signupUrl ? ` (get one at ${signupUrl})` : '';
|
|
63
61
|
token = await promptForSecret(`Enter API key for ${provider}${hint}`);
|
|
64
62
|
}
|
|
65
|
-
setKeychainToken(item, token);
|
|
63
|
+
await setKeychainToken(item, token);
|
|
66
64
|
console.log(chalk.green(`Stored in keychain: ${item}`));
|
|
67
65
|
}
|
|
68
66
|
/**
|
|
@@ -72,8 +70,8 @@ export async function ensureProviderToken(provider, signupUrl, fromStdin) {
|
|
|
72
70
|
*
|
|
73
71
|
* Reads the bundle value via {@link getKeychainToken} — this pops Touch ID on
|
|
74
72
|
* macOS once, for that bundle-namespaced item (`agents-cli.secrets.*`, gated by
|
|
75
|
-
*
|
|
76
|
-
* it to `
|
|
73
|
+
* the standalone's user-presence rule for `agents-cli.secrets.*` items) — then writes
|
|
74
|
+
* it to `profileKeychainItem(provider)`, a plain `agents-cli.<provider>.token`
|
|
77
75
|
* item. That item matches neither the `agents-cli.secrets.` nor
|
|
78
76
|
* `agents-cli.bundles.` prefix, so every later read of the harness's own key is
|
|
79
77
|
* silent — no repeat Touch ID prompt.
|
|
@@ -106,7 +104,16 @@ export async function applyFromSecrets(profile, spec, explicitAuthProvider, opts
|
|
|
106
104
|
const sep = spec.indexOf(':');
|
|
107
105
|
const bundleName = sep === -1 ? spec : spec.slice(0, sep);
|
|
108
106
|
const requestedKey = sep === -1 ? undefined : spec.slice(sep + 1);
|
|
109
|
-
|
|
107
|
+
let bundle;
|
|
108
|
+
try {
|
|
109
|
+
bundle = await readBundle(bundleName);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
// The standalone reports only a code; name the bundle the user asked for.
|
|
113
|
+
if (!isSecretsClientError(err, 'NOT_FOUND'))
|
|
114
|
+
throw err;
|
|
115
|
+
throw new Error(`Secrets bundle '${bundleName}' not found. List bundles with 'agents secrets list'.`);
|
|
116
|
+
}
|
|
110
117
|
const keys = Object.keys(bundle.vars);
|
|
111
118
|
const key = requestedKey ?? (keys.length === 1 ? keys[0] : undefined);
|
|
112
119
|
if (!key) {
|
|
@@ -123,7 +130,7 @@ export async function applyFromSecrets(profile, spec, explicitAuthProvider, opts
|
|
|
123
130
|
value = parsed.literal;
|
|
124
131
|
}
|
|
125
132
|
else if (parsed.ref.provider === 'keychain') {
|
|
126
|
-
value = getKeychainToken(secretsKeychainItem(bundle.name, parsed.ref.value));
|
|
133
|
+
value = await getKeychainToken(secretsKeychainItem(bundle.name, parsed.ref.value));
|
|
127
134
|
}
|
|
128
135
|
else {
|
|
129
136
|
throw new Error(`Bundle '${bundleName}' key '${key}' is a '${parsed.ref.provider}:' reference, not a keychain-backed secret — --from-secrets only copies keychain-backed values.`);
|
|
@@ -134,8 +141,8 @@ export async function applyFromSecrets(profile, spec, explicitAuthProvider, opts
|
|
|
134
141
|
`on a fork, so it never silently overwrites the source harness's shared keychain item.`);
|
|
135
142
|
}
|
|
136
143
|
const provider = explicitAuthProvider || (profile.auth ? profile.provider : undefined) || bundleName;
|
|
137
|
-
const item =
|
|
138
|
-
setKeychainToken(item, value);
|
|
144
|
+
const item = profileKeychainItem(provider);
|
|
145
|
+
await setKeychainToken(item, value);
|
|
139
146
|
if (!profile.auth) {
|
|
140
147
|
const envVar = authEnvKeyForHost(profile.host.agent);
|
|
141
148
|
if (!envVar) {
|
package/dist/commands/repo.js
CHANGED
|
@@ -64,7 +64,7 @@ async function publishUserRepoAccountState(provisionAuth) {
|
|
|
64
64
|
if (usage.error)
|
|
65
65
|
console.error(chalk.yellow(`Usage snapshot: ${usage.error}`));
|
|
66
66
|
if (provisionAuth) {
|
|
67
|
-
const { syncReservedAuthBundle } = await import('../lib/secrets
|
|
67
|
+
const { syncReservedAuthBundle } = await import('../lib/secrets-policy.js');
|
|
68
68
|
const auth = await syncReservedAuthBundle();
|
|
69
69
|
if (auth.pushed.length > 0)
|
|
70
70
|
console.log(chalk.gray(`Auth bundle: pushed to ${auth.pushed.join(', ')}`));
|
|
@@ -72,7 +72,7 @@ async function publishUserRepoAccountState(provisionAuth) {
|
|
|
72
72
|
console.error(chalk.yellow(`Auth bundle: ${err.device}: ${err.message}`));
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
|
-
const { publishReservedAuthVerdict } = await import('../lib/secrets
|
|
75
|
+
const { publishReservedAuthVerdict } = await import('../lib/secrets-policy.js');
|
|
76
76
|
const auth = await publishReservedAuthVerdict();
|
|
77
77
|
if (auth.error)
|
|
78
78
|
console.error(chalk.yellow(`Auth verdict: ${auth.device}: ${auth.error}`));
|
|
@@ -85,7 +85,7 @@ async function consumeUserRepoAccountState() {
|
|
|
85
85
|
console.log(chalk.gray(`Usage snapshot: merged ${usage.merged} row(s) from ${usage.sources.join(', ')}`));
|
|
86
86
|
for (const err of usage.errors)
|
|
87
87
|
console.error(chalk.yellow(`Usage snapshot: ${err.device}: ${err.message}`));
|
|
88
|
-
const { syncReservedAuthBundle } = await import('../lib/secrets
|
|
88
|
+
const { syncReservedAuthBundle } = await import('../lib/secrets-policy.js');
|
|
89
89
|
const auth = await syncReservedAuthBundle();
|
|
90
90
|
if (auth.pushed.length > 0)
|
|
91
91
|
console.log(chalk.gray(`Auth bundle: pushed to ${auth.pushed.join(', ')}`));
|
|
@@ -12,7 +12,7 @@ export interface RunAccountChoice {
|
|
|
12
12
|
*/
|
|
13
13
|
signInRequired: boolean;
|
|
14
14
|
}
|
|
15
|
-
/** One named account row for `agents accounts
|
|
15
|
+
/** One named account row for `agents accounts default` (reuses this picker's layout). */
|
|
16
16
|
export interface SwitchAccountRow {
|
|
17
17
|
accountName: string;
|
|
18
18
|
kind: 'provider' | 'native';
|
|
@@ -25,8 +25,8 @@ export declare function formatAccountLimits(candidate: RotateCandidate): string;
|
|
|
25
25
|
/** Build aligned picker rows with usable accounts first and unsafe rows disabled. */
|
|
26
26
|
export declare function buildRunAccountChoices(candidates: RotateCandidate[], globalDefault: string | null): RunAccountChoice[];
|
|
27
27
|
/**
|
|
28
|
-
* Aligned picker rows for `accounts
|
|
29
|
-
* (identity, status, limits) but the value is the named account to
|
|
28
|
+
* Aligned picker rows for `accounts default`. Same columns as the run picker
|
|
29
|
+
* (identity, status, limits) but the value is the named account to make default.
|
|
30
30
|
* Rows stay selectable: setting a default is not a launch.
|
|
31
31
|
*/
|
|
32
32
|
export declare function buildSwitchAccountChoices(rows: SwitchAccountRow[]): RunAccountChoice[];
|
|
@@ -4,6 +4,7 @@ import { agentLabel } from '../lib/agents.js';
|
|
|
4
4
|
import { loginHint } from '../lib/signin-badge.js';
|
|
5
5
|
import { collectRunCandidates, readinessFromCandidate, isSignInRecoverable, } from '../lib/accounting/rotate.js';
|
|
6
6
|
import { compareVersions, getGlobalDefault } from '../lib/installations/versions.js';
|
|
7
|
+
import { verdictLabel } from '../lib/auth-health.js';
|
|
7
8
|
import { isInteractiveTerminal, isPromptCancelled, requireInteractiveSelection } from './utils.js';
|
|
8
9
|
const CANCEL_SELECTION = '__agents_cancel_account_selection__';
|
|
9
10
|
const WINDOW_ORDER = ['session', 'week', 'sonnet_week', 'month'];
|
|
@@ -65,28 +66,28 @@ export function buildRunAccountChoices(candidates, globalDefault) {
|
|
|
65
66
|
const readiness = readinessFromCandidate(candidate);
|
|
66
67
|
const disabled = disabledReason(candidate, readiness);
|
|
67
68
|
const signInRequired = isSignInRecoverable(readiness);
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
69
|
+
const account = candidate.nativeAccount
|
|
70
|
+
|| candidate.accountLabel
|
|
71
|
+
|| 'account unavailable';
|
|
72
|
+
const marked = candidate.version === globalDefault && !candidate.nativeAccount
|
|
73
|
+
? `${account} (default)`
|
|
74
|
+
: account;
|
|
75
|
+
const verdict = !readiness.ready && readiness.reason === 'revoked'
|
|
75
76
|
? 'needs re-login'
|
|
76
|
-
:
|
|
77
|
+
: !readiness.ready && readiness.reason === 'signed_out'
|
|
77
78
|
? 'logged out'
|
|
78
|
-
:
|
|
79
|
+
: candidate.authVerdict
|
|
80
|
+
? verdictLabel(candidate.authVerdict).toLowerCase()
|
|
81
|
+
: 'live';
|
|
79
82
|
return {
|
|
80
83
|
candidate,
|
|
81
|
-
account:
|
|
82
|
-
|
|
83
|
-
status,
|
|
84
|
-
plan: candidate.usageSnapshot?.plan ?? candidate.plan ?? 'plan unavailable',
|
|
84
|
+
account: marked,
|
|
85
|
+
verdict,
|
|
85
86
|
// An auth-blocked row shows what picking it DOES; its quota is moot until
|
|
86
87
|
// there is a credential to spend it with.
|
|
87
|
-
limits:
|
|
88
|
+
limits: !readiness.ready && readiness.reason === 'revoked'
|
|
88
89
|
? 'launch to re-authenticate'
|
|
89
|
-
:
|
|
90
|
+
: !readiness.ready && readiness.reason === 'signed_out'
|
|
90
91
|
? 'launch to sign in'
|
|
91
92
|
: formatAccountLimits(candidate),
|
|
92
93
|
disabled,
|
|
@@ -107,23 +108,22 @@ export function buildRunAccountChoices(candidates, globalDefault) {
|
|
|
107
108
|
return compareVersions(b.candidate.version, a.candidate.version);
|
|
108
109
|
});
|
|
109
110
|
const accountWidth = Math.max(0, ...rows.map((row) => row.account.length));
|
|
110
|
-
const
|
|
111
|
-
const statusWidth = Math.max(0, ...rows.map((row) => row.status.length));
|
|
112
|
-
const planWidth = Math.max(0, ...rows.map((row) => row.plan.length));
|
|
111
|
+
const verdictWidth = Math.max(0, ...rows.map((row) => row.verdict.length));
|
|
113
112
|
return rows.map((row) => ({
|
|
114
113
|
name: [
|
|
115
114
|
row.account.padEnd(accountWidth),
|
|
116
|
-
row.
|
|
117
|
-
row.status.padEnd(statusWidth),
|
|
118
|
-
row.plan.padEnd(planWidth),
|
|
115
|
+
row.verdict.padEnd(verdictWidth),
|
|
119
116
|
row.limits,
|
|
120
117
|
].join(' '),
|
|
121
|
-
value: row.candidate.version,
|
|
118
|
+
value: row.candidate.nativeAccount ?? row.candidate.version,
|
|
122
119
|
disabled: row.disabled,
|
|
123
120
|
ready: row.ready,
|
|
124
121
|
signInRequired: row.signInRequired,
|
|
125
122
|
}));
|
|
126
123
|
}
|
|
124
|
+
function candidatePickerValue(candidate) {
|
|
125
|
+
return candidate.nativeAccount ?? candidate.version;
|
|
126
|
+
}
|
|
127
127
|
function switchRowStatus(row) {
|
|
128
128
|
if (!row.candidate) {
|
|
129
129
|
return {
|
|
@@ -151,8 +151,8 @@ function switchRowStatus(row) {
|
|
|
151
151
|
return { status, limits, ready: readiness.ready };
|
|
152
152
|
}
|
|
153
153
|
/**
|
|
154
|
-
* Aligned picker rows for `accounts
|
|
155
|
-
* (identity, status, limits) but the value is the named account to
|
|
154
|
+
* Aligned picker rows for `accounts default`. Same columns as the run picker
|
|
155
|
+
* (identity, status, limits) but the value is the named account to make default.
|
|
156
156
|
* Rows stay selectable: setting a default is not a launch.
|
|
157
157
|
*/
|
|
158
158
|
export function buildSwitchAccountChoices(rows) {
|
|
@@ -184,12 +184,11 @@ export function buildSwitchAccountChoices(rows) {
|
|
|
184
184
|
export async function pickSwitchAccount(agent, rows) {
|
|
185
185
|
if (!isInteractiveTerminal()) {
|
|
186
186
|
requireInteractiveSelection(`Selecting a ${agentLabel(agent)} account`, [
|
|
187
|
-
`agents accounts
|
|
188
|
-
`agents accounts set-default ${agent} <account>`,
|
|
187
|
+
`agents accounts default ${agent} <account>`,
|
|
189
188
|
]);
|
|
190
189
|
}
|
|
191
190
|
if (rows.length === 0) {
|
|
192
|
-
throw new Error(`No named accounts for ${agent}. Add one with 'agents accounts add
|
|
191
|
+
throw new Error(`No named accounts for ${agent}. Add one with 'agents accounts add ${agent} [name]'.`);
|
|
193
192
|
}
|
|
194
193
|
const choices = buildSwitchAccountChoices(rows).map(({ ready: _ready, signInRequired: _signInRequired, ...choice }) => choice);
|
|
195
194
|
try {
|
|
@@ -292,16 +291,16 @@ export async function pickRunAccountCandidate(agent) {
|
|
|
292
291
|
});
|
|
293
292
|
}
|
|
294
293
|
try {
|
|
295
|
-
const
|
|
294
|
+
const picked = await select({
|
|
296
295
|
message: needsSignIn
|
|
297
296
|
? `Select a ${agentLabel(agent)} account for this run (pick a logged-out one to sign in):`
|
|
298
297
|
: `Select a ${agentLabel(agent)} account for this run:`,
|
|
299
298
|
choices: promptChoices,
|
|
300
299
|
loop: false,
|
|
301
300
|
});
|
|
302
|
-
if (
|
|
301
|
+
if (picked === CANCEL_SELECTION)
|
|
303
302
|
return null;
|
|
304
|
-
return candidates.find((candidate) => candidate
|
|
303
|
+
return candidates.find((candidate) => candidatePickerValue(candidate) === picked) ?? null;
|
|
305
304
|
}
|
|
306
305
|
catch (err) {
|
|
307
306
|
if (isPromptCancelled(err))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents secrets` — thin passthrough to the standalone `secrets` CLI
|
|
3
|
+
* (PHNX-3989, DIST-1). Every bundle/keychain/vault operation now lives in
|
|
4
|
+
* `@phnx-labs/secrets-cli`, not in this repo; this command re-declares no
|
|
5
|
+
* subcommand or flag of its own. `agents secrets <anything>` execs the
|
|
6
|
+
* installed executable with the same argv, `SECRETS_HOME` defaulted to this
|
|
7
|
+
* box's existing store (`buildServeEnv`, matching the process client's own
|
|
8
|
+
* default so an interactive `secrets list` and an injected `readBundle` see
|
|
9
|
+
* the same store), and `SECRETS_SCOPE`/`SECRETS_CONTEXT` passed through
|
|
10
|
+
* unmodified from the ambient environment — this is the operator's own
|
|
11
|
+
* terminal, not an injected agent exec, so there is no separate scope to
|
|
12
|
+
* compute here (that stays in `lib/exec.ts` for the run/inject path).
|
|
13
|
+
*
|
|
14
|
+
* A missing executable fails loud with install guidance — no fallback to the
|
|
15
|
+
* retired in-repo engine (`commands/secrets.ts` and its siblings), which stays
|
|
16
|
+
* in the tree, unregistered, until every consumer has moved off it (tasks.md
|
|
17
|
+
* item 7).
|
|
18
|
+
*/
|
|
19
|
+
import type { Command } from 'commander';
|
|
20
|
+
export declare function registerSecretsCommands(program: Command): void;
|