@phnx-labs/agents-cli 1.22.47 → 1.22.50
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 +503 -17
- package/README.md +76 -32
- package/dist/bootstrap.js +4 -2
- package/dist/cli/command-registry.d.ts +1 -0
- package/dist/cli/command-registry.js +2 -0
- package/dist/commands/accounts.d.ts +58 -0
- package/dist/commands/accounts.js +201 -39
- package/dist/commands/artifacts.js +7 -6
- package/dist/commands/attach.js +1 -1
- package/dist/commands/browser.js +501 -103
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/config.js +0 -20
- package/dist/commands/daemon-test-harness.d.ts +42 -0
- package/dist/commands/daemon-test-harness.js +90 -0
- package/dist/commands/daemon.d.ts +21 -0
- package/dist/commands/daemon.js +147 -12
- package/dist/commands/device-config-test-harness.d.ts +11 -0
- package/dist/commands/device-config-test-harness.js +59 -0
- package/dist/commands/exec.js +76 -16
- package/dist/commands/focus.d.ts +14 -0
- package/dist/commands/focus.js +48 -5
- package/dist/commands/go.d.ts +3 -0
- package/dist/commands/go.js +16 -3
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/lease.d.ts +1 -1
- package/dist/commands/lease.js +4 -3
- package/dist/commands/mailboxes.js +4 -3
- package/dist/commands/menubar.js +11 -4
- package/dist/commands/projects.d.ts +16 -1
- package/dist/commands/projects.js +58 -64
- package/dist/commands/resume.js +1 -1
- package/dist/commands/route.js +4 -4
- package/dist/commands/routines.js +3 -2
- package/dist/commands/run-broadcast.d.ts +1 -1
- package/dist/commands/run-broadcast.js +1 -1
- package/dist/commands/sessions-picker.d.ts +22 -0
- package/dist/commands/sessions-picker.js +48 -9
- package/dist/commands/sessions-trace.js +1 -1
- package/dist/commands/sessions.js +1 -0
- package/dist/commands/setup-secrets.js +23 -0
- package/dist/commands/share.d.ts +20 -5
- package/dist/commands/share.js +158 -53
- package/dist/commands/ssh.js +54 -7
- package/dist/commands/teams.d.ts +12 -0
- package/dist/commands/teams.js +44 -67
- package/dist/commands/traces.d.ts +2 -0
- package/dist/commands/traces.js +188 -0
- package/dist/commands/view.d.ts +1 -1
- package/dist/commands/view.js +17 -34
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/lib/account-provider-registry.d.ts +9 -0
- package/dist/lib/account-provider-registry.js +20 -0
- package/dist/lib/accounting/account-pool-collect.d.ts +34 -0
- package/dist/lib/accounting/account-pool-collect.js +71 -0
- package/dist/lib/accounting/account-pool.d.ts +46 -0
- package/dist/lib/accounting/account-pool.js +33 -0
- package/dist/lib/accounting/capacity.d.ts +24 -0
- package/dist/lib/accounting/capacity.js +30 -0
- package/dist/lib/accounting/rotate.d.ts +11 -18
- package/dist/lib/accounting/rotate.js +6 -24
- package/dist/lib/accounting/usage.d.ts +14 -3
- package/dist/lib/accounting/usage.js +64 -18
- package/dist/lib/agent-spec/agents.js +11 -9
- package/dist/lib/browser/drivers/ssh.d.ts +16 -2
- package/dist/lib/browser/drivers/ssh.js +36 -9
- package/dist/lib/browser/ipc.js +8 -3
- package/dist/lib/browser/profiles.d.ts +35 -101
- package/dist/lib/browser/profiles.js +130 -301
- package/dist/lib/browser/registry.d.ts +39 -0
- package/dist/lib/browser/registry.js +149 -0
- package/dist/lib/browser/remote-control.d.ts +8 -6
- package/dist/lib/browser/remote-control.js +8 -6
- package/dist/lib/browser/resolve-target.d.ts +68 -0
- package/dist/lib/browser/resolve-target.js +250 -0
- package/dist/lib/browser/runtime-state.d.ts +40 -16
- package/dist/lib/browser/runtime-state.js +92 -25
- package/dist/lib/browser/service.d.ts +28 -9
- package/dist/lib/browser/service.js +139 -62
- package/dist/lib/browser/task-index.d.ts +70 -0
- package/dist/lib/browser/task-index.js +180 -0
- package/dist/lib/byok-usage.js +2 -2
- package/dist/lib/claude-statusline.d.ts +29 -0
- package/dist/lib/claude-statusline.js +139 -0
- package/dist/lib/cloud/host.d.ts +1 -1
- package/dist/lib/cloud/host.js +1 -1
- package/dist/lib/cloud/rush.js +1 -1
- package/dist/lib/computer/computer-rpc.js +2 -2
- package/dist/lib/computer/download.d.ts +15 -15
- package/dist/lib/computer/download.js +35 -98
- package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
- package/dist/lib/computer/ssh-tunnel.js +12 -22
- package/dist/lib/config-keys.d.ts +2 -8
- package/dist/lib/config-keys.js +2 -13
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +2 -2
- package/dist/lib/crabbox/lease.d.ts +9 -0
- package/dist/lib/crabbox/lease.js +70 -15
- package/dist/lib/crabbox/runtimes.js +1 -1
- package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
- package/dist/lib/daemon/account-state-daemon-service.js +26 -0
- package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
- package/dist/lib/daemon/browser-ipc-service.js +46 -0
- package/dist/lib/daemon/daemon.d.ts +3 -0
- package/dist/lib/daemon/daemon.js +212 -444
- package/dist/lib/daemon/device-probe-service.d.ts +26 -0
- package/dist/lib/daemon/device-probe-service.js +47 -0
- package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
- package/dist/lib/daemon/keychain-reap-service.js +32 -0
- package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
- package/dist/lib/daemon/monitor-engine-service.js +29 -0
- package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
- package/dist/lib/daemon/secrets-broker-service.js +66 -0
- package/dist/lib/daemon/self-heal-service.d.ts +27 -0
- package/dist/lib/daemon/self-heal-service.js +48 -0
- package/dist/lib/daemon/service.d.ts +102 -0
- package/dist/lib/daemon/service.js +98 -0
- package/dist/lib/daemon/session-index-service.d.ts +22 -0
- package/dist/lib/daemon/session-index-service.js +40 -0
- package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
- package/dist/lib/daemon/state-dir-check-service.js +63 -0
- package/dist/lib/daemon/supervisor.d.ts +87 -0
- package/dist/lib/daemon/supervisor.js +298 -0
- package/dist/lib/daemon/watchdog-service.d.ts +19 -0
- package/dist/lib/daemon/watchdog-service.js +40 -0
- package/dist/lib/daemon-health.d.ts +15 -0
- package/dist/lib/daemon-health.js +29 -5
- package/dist/lib/daemon-services.d.ts +8 -1
- package/dist/lib/daemon-services.js +55 -0
- package/dist/lib/daemon-ticks.d.ts +13 -2
- package/dist/lib/daemon-ticks.js +20 -18
- package/dist/lib/device-config.d.ts +0 -2
- package/dist/lib/device-config.js +1 -23
- package/dist/lib/devices/registry.d.ts +1 -1
- package/dist/lib/devices/registry.js +1 -1
- package/dist/lib/devices/stats-cache.d.ts +20 -0
- package/dist/lib/devices/stats-cache.js +49 -5
- package/dist/lib/devices/worker-pick.d.ts +48 -0
- package/dist/lib/devices/worker-pick.js +113 -0
- package/dist/lib/exec.d.ts +67 -13
- package/dist/lib/exec.js +85 -29
- package/dist/lib/harness/adapters/cursor.js +8 -11
- package/dist/lib/helper-download.d.ts +108 -0
- package/dist/lib/helper-download.js +194 -0
- package/dist/lib/helper-versions.d.ts +52 -0
- package/dist/lib/helper-versions.js +58 -0
- package/dist/lib/hooks/cache.js +11 -0
- package/dist/lib/hooks/install.js +2 -2
- package/dist/lib/hosts/credentials.d.ts +1 -1
- package/dist/lib/hosts/credentials.js +1 -1
- package/dist/lib/hosts/dispatch.d.ts +7 -1
- package/dist/lib/hosts/dispatch.js +24 -5
- package/dist/lib/hosts/passthrough.d.ts +8 -15
- package/dist/lib/hosts/passthrough.js +19 -16
- package/dist/lib/hosts/providers/devices.d.ts +1 -1
- package/dist/lib/hosts/providers/devices.js +1 -1
- package/dist/lib/hosts/reconnect.d.ts +179 -21
- package/dist/lib/hosts/reconnect.js +266 -45
- package/dist/lib/installations/migrate.d.ts +4 -5
- package/dist/lib/installations/migrate.js +6 -7
- package/dist/lib/installations/shims.d.ts +1 -1
- package/dist/lib/installations/shims.js +23 -13
- package/dist/lib/installations/store.js +6 -3
- package/dist/lib/installations/versions.js +73 -48
- package/dist/lib/linear-autoclose.d.ts +1 -1
- package/dist/lib/linear-autoclose.js +1 -1
- package/dist/lib/loop.d.ts +2 -2
- package/dist/lib/loop.js +1 -1
- package/dist/lib/menubar/download-menubar.d.ts +47 -0
- package/dist/lib/menubar/download-menubar.js +60 -0
- package/dist/lib/menubar/install-menubar.d.ts +31 -7
- package/dist/lib/menubar/install-menubar.js +79 -21
- package/dist/lib/menubar/notify-desktop.d.ts +3 -3
- package/dist/lib/menubar/notify-desktop.js +6 -6
- package/dist/lib/model-tiers.d.ts +1 -1
- package/dist/lib/project-probe.d.ts +1 -1
- package/dist/lib/project-probe.js +1 -1
- package/dist/lib/project-pull.d.ts +1 -1
- package/dist/lib/project-pull.js +2 -2
- package/dist/lib/projects.d.ts +1 -1
- package/dist/lib/projects.js +1 -1
- package/dist/lib/sandbox.js +6 -7
- package/dist/lib/secrets/download-keychain.d.ts +47 -0
- package/dist/lib/secrets/download-keychain.js +70 -0
- package/dist/lib/secrets/install-helper.d.ts +20 -1
- package/dist/lib/secrets/install-helper.js +44 -3
- package/dist/lib/session/active.js +17 -1
- package/dist/lib/session/db.d.ts +11 -1
- package/dist/lib/session/db.js +138 -6
- package/dist/lib/session/discover.d.ts +21 -1
- package/dist/lib/session/discover.js +33 -5
- package/dist/lib/session/highlights.d.ts +49 -1
- package/dist/lib/session/highlights.js +95 -0
- package/dist/lib/session/host-link.d.ts +16 -2
- package/dist/lib/session/host-link.js +7 -1
- package/dist/lib/session/parse.js +33 -1
- package/dist/lib/session/remote/remote-list.d.ts +9 -0
- package/dist/lib/session/remote/remote-list.js +19 -2
- package/dist/lib/session/remote/watch.js +11 -2
- package/dist/lib/session/session-cache.d.ts +53 -0
- package/dist/lib/session/session-cache.js +88 -0
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/tool-calls.d.ts +2 -0
- package/dist/lib/session/tool-calls.js +1 -1
- package/dist/lib/session/trajectory-html.d.ts +5 -1
- package/dist/lib/session/trajectory-html.js +42 -22
- package/dist/lib/session/trajectory-text.js +9 -2
- package/dist/lib/session/trajectory.js +53 -4
- package/dist/lib/session/types.d.ts +15 -0
- package/dist/lib/sha256-asset.d.ts +26 -0
- package/dist/lib/sha256-asset.js +41 -0
- package/dist/lib/share/backend.d.ts +95 -0
- package/dist/lib/share/backend.js +139 -0
- package/dist/lib/share/delete.d.ts +6 -2
- package/dist/lib/share/delete.js +8 -9
- package/dist/lib/share/html.d.ts +22 -0
- package/dist/lib/share/html.js +88 -0
- package/dist/lib/share/provision.d.ts +18 -4
- package/dist/lib/share/provision.js +38 -4
- package/dist/lib/share/publish.d.ts +44 -15
- package/dist/lib/share/publish.js +58 -26
- package/dist/lib/share/worker-template.js +402 -25
- package/dist/lib/sqlite.js +16 -3
- package/dist/lib/ssh-exec.d.ts +65 -0
- package/dist/lib/ssh-exec.js +120 -4
- package/dist/lib/startup/command-registry.js +1 -1
- package/dist/lib/startup/dev-build.js +3 -3
- package/dist/lib/state.d.ts +1 -1
- package/dist/lib/state.js +7 -3
- package/dist/lib/subagents-registry.js +1 -1
- package/dist/lib/teams/agents.d.ts +14 -12
- package/dist/lib/teams/agents.js +188 -73
- package/dist/lib/teams/api.d.ts +6 -1
- package/dist/lib/teams/api.js +2 -0
- package/dist/lib/teams/scheduler.d.ts +7 -0
- package/dist/lib/teams/scheduler.js +26 -8
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/types.d.ts +1 -1
- package/dist/lib/terminal/types.js +1 -1
- package/dist/lib/tmux/orphan-reap.js +1 -1
- package/dist/lib/tmux/session.js +31 -0
- package/dist/lib/traces/backend.d.ts +29 -0
- package/dist/lib/traces/backend.js +46 -0
- package/dist/lib/traces/classify.d.ts +52 -0
- package/dist/lib/traces/classify.js +72 -0
- package/dist/lib/traces/config.d.ts +10 -0
- package/dist/lib/traces/config.js +2 -0
- package/dist/lib/traces/provision.d.ts +19 -0
- package/dist/lib/traces/provision.js +30 -0
- package/dist/lib/traces/sync.d.ts +150 -0
- package/dist/lib/traces/sync.js +459 -0
- package/dist/lib/traces/worker-template.d.ts +4 -0
- package/dist/lib/traces/worker-template.js +159 -0
- package/dist/lib/types.d.ts +18 -19
- package/dist/lib/types.js +14 -0
- package/dist/lib/usage-refresh.d.ts +1 -1
- package/dist/lib/usage-refresh.js +11 -8
- package/dist/lib/workflows.d.ts +1 -1
- package/package.json +4 -6
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -26
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -135
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
- package/dist/lib/usage-fleet.d.ts +0 -32
- package/dist/lib/usage-fleet.js +0 -125
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
1
3
|
import chalk from 'chalk';
|
|
2
|
-
import { password } from '@inquirer/prompts';
|
|
4
|
+
import { password, select } from '@inquirer/prompts';
|
|
5
|
+
import { resolveClaudeSetupToken } from '../lib/claude-account-token.js';
|
|
3
6
|
import { setHelpSections } from '../lib/help.js';
|
|
4
7
|
import { readMeta, updateMeta } from '../lib/state.js';
|
|
5
8
|
import { ALL_AGENT_IDS, getAccountInfo, resolveAgentName } from '../lib/agents.js';
|
|
6
9
|
import { getGlobalDefault, getVersionHomePath, listInstalledVersions } from '../lib/installations/versions.js';
|
|
7
10
|
import { assertNativeAccountNameable, nativeAccountCapability, nativeIdentityKey } from '../lib/account-capabilities.js';
|
|
8
11
|
import { collectRunCandidates } from '../lib/accounting/rotate.js';
|
|
9
|
-
import { isInteractiveTerminal } from './utils.js';
|
|
12
|
+
import { isInteractiveTerminal, isPromptCancelled, requireInteractiveSelection } from './utils.js';
|
|
10
13
|
import { buildSwitchAccountChoices, formatAccountLimits, pickSwitchAccount } from './run-account-picker.js';
|
|
11
14
|
import { profileExists, readProfile } from '../lib/profiles.js';
|
|
12
15
|
import { pushBundleToHost } from '../lib/secrets/push.js';
|
|
@@ -15,7 +18,7 @@ import { resolveRemoteOsSync } from '../lib/hosts/remote-os.js';
|
|
|
15
18
|
import { runDevicesAccounts } from './ssh.js';
|
|
16
19
|
import { discoverNativeAccounts } from '../lib/account-catalog.js';
|
|
17
20
|
import { readAndResolveBundleEnv } from '../lib/secrets/bundles.js';
|
|
18
|
-
import { getAccountProvider, listAccountProviders } from '../lib/account-provider-registry.js';
|
|
21
|
+
import { getAccountProvider, listAccountProviders, providerAuthenticatesHarness } from '../lib/account-provider-registry.js';
|
|
19
22
|
import { accountBindings, addAccount, addNativeAccount, bindAccount, findAccount, findUnifiedAccount, inspectAccount, labelNativeAccount, listNativeAccounts, readAccountRegistry, removeAccount, renameAccount, setAccountSecret, unbindAccount } from '../lib/account-registry.js';
|
|
20
23
|
function cleanCommandError(command, err) {
|
|
21
24
|
command.error(err instanceof Error ? err.message : String(err), { exitCode: 1, code: 'accounts.error' });
|
|
@@ -66,6 +69,33 @@ export function classifyAttachTarget(target) {
|
|
|
66
69
|
return { kind: 'device-agent', agent };
|
|
67
70
|
throw new Error(`Unknown attach target '${target}'. Expected an installed <agent>@<version>, a harness id, or an existing custom harness profile.`);
|
|
68
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Persist the attached setup-token to a per-version `.oauth_token` file so an
|
|
74
|
+
* INTERACTIVE Claude launch on a keychain-less Linux worker can authenticate from it.
|
|
75
|
+
* Headless runs inject the token via `buildExecEnv`, but an interactive launch defers to
|
|
76
|
+
* the per-version login (`claudeAdapter.applyExecConfigEnv`), and the shim's Linux fallback
|
|
77
|
+
* (`claudeAdapter.shimConfigEnvBash`) reads exactly this file. Without writing it, a
|
|
78
|
+
* freshly-attached setup-token was invisible to interactive runs on a worker. macOS keeps
|
|
79
|
+
* the credential in the keychain, so `resolveClaudeSetupToken` returns null there and this
|
|
80
|
+
* is a no-op off Linux.
|
|
81
|
+
*/
|
|
82
|
+
export function writeClaudeInteractiveOauthToken(target, targetAgent) {
|
|
83
|
+
if (process.platform !== 'linux' || targetAgent !== 'claude' || target.kind !== 'installation')
|
|
84
|
+
return;
|
|
85
|
+
const versionHome = getVersionHomePath('claude', target.version);
|
|
86
|
+
const tokenPath = path.join(versionHome, '.claude', '.oauth_token');
|
|
87
|
+
const token = resolveClaudeSetupToken(versionHome);
|
|
88
|
+
// A re-point (attach B over A, or a detach) can leave no setup-token resolving for
|
|
89
|
+
// this version — B's may not be minted yet. A leftover file from the previous binding
|
|
90
|
+
// would silently authenticate interactive runs as the OLD account (the shim's Linux
|
|
91
|
+
// fallback reads it), so clear it rather than leave it stale.
|
|
92
|
+
if (!token) {
|
|
93
|
+
fs.rmSync(tokenPath, { force: true });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
fs.mkdirSync(path.dirname(tokenPath), { recursive: true });
|
|
97
|
+
fs.writeFileSync(tokenPath, token, { mode: 0o600 });
|
|
98
|
+
}
|
|
69
99
|
export function parseBundleKey(raw) {
|
|
70
100
|
const colon = raw.indexOf(':');
|
|
71
101
|
if (colon < 1 || colon === raw.length - 1)
|
|
@@ -93,18 +123,59 @@ async function printAccounts(json, fleet = false) {
|
|
|
93
123
|
console.log(JSON.stringify([...records.map(account => publicAccount(inspectAccount(account.name))), ...native], null, 2));
|
|
94
124
|
return;
|
|
95
125
|
}
|
|
96
|
-
console.log(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
126
|
+
console.log(renderAccountList(records, native));
|
|
127
|
+
}
|
|
128
|
+
/** Pure text renderer for `agents accounts list` — grouped, column-aligned, labels first-class. */
|
|
129
|
+
export function renderAccountList(records, native) {
|
|
130
|
+
const out = [];
|
|
131
|
+
// Native logins, grouped by harness — the selection surface for `<harness>#<label>`.
|
|
132
|
+
out.push(chalk.bold('Native logins') + chalk.gray(' ') + chalk.gray('run <harness>#<label>'));
|
|
133
|
+
if (!native.length) {
|
|
134
|
+
out.push(chalk.gray(' No signed-in native accounts found.'));
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const byHarness = new Map();
|
|
138
|
+
for (const acct of native) {
|
|
139
|
+
const list = byHarness.get(acct.agent) ?? [];
|
|
140
|
+
list.push(acct);
|
|
141
|
+
byHarness.set(acct.agent, list);
|
|
142
|
+
}
|
|
143
|
+
const harnessW = Math.max(6, ...[...byHarness.keys()].map(h => h.length));
|
|
144
|
+
const labelW = Math.max(6, ...native.map(a => (a.name ?? '—').length));
|
|
145
|
+
const idW = Math.max(8, ...native.map(a => a.display.length));
|
|
146
|
+
let sawDefault = false;
|
|
147
|
+
for (const [harness, list] of [...byHarness.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
148
|
+
const defaultVersion = getGlobalDefault(harness);
|
|
149
|
+
list.forEach((acct, i) => {
|
|
150
|
+
const harnessCell = chalk.gray((i === 0 ? harness : '').padEnd(harnessW));
|
|
151
|
+
const rawLabel = acct.name ?? '—';
|
|
152
|
+
const labelCell = acct.name ? chalk.cyan(rawLabel.padEnd(labelW)) : chalk.gray(rawLabel.padEnd(labelW));
|
|
153
|
+
const isDefault = defaultVersion ? acct.versions.includes(defaultVersion) : false;
|
|
154
|
+
if (isDefault)
|
|
155
|
+
sawDefault = true;
|
|
156
|
+
const marker = isDefault ? chalk.green('*') : ' ';
|
|
157
|
+
out.push(` ${harnessCell} ${labelCell}${marker} ${acct.display.padEnd(idW)} ${chalk.gray(acct.versions.join(', '))}`);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if (sawDefault)
|
|
161
|
+
out.push(chalk.gray('\n * default account for that harness (used when you pass neither @version nor #label)'));
|
|
162
|
+
}
|
|
163
|
+
// Provider bundles — the durable API-key/setup-token accounts, selected with `--account <name>`.
|
|
164
|
+
out.push('');
|
|
165
|
+
out.push(chalk.bold('Provider bundles') + chalk.gray(' run <harness> --account <name>'));
|
|
166
|
+
if (!records.length) {
|
|
167
|
+
out.push(chalk.gray(" None. Add one with 'agents accounts add <name> --provider <provider> --auth <type>'."));
|
|
102
168
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
169
|
+
else {
|
|
170
|
+
const nameW = Math.max(4, ...records.map(a => a.name.length));
|
|
171
|
+
const provW = Math.max(8, ...records.map(a => a.provider.length));
|
|
172
|
+
const authW = Math.max(10, ...records.map(a => a.auth.length));
|
|
173
|
+
for (const account of records) {
|
|
174
|
+
const present = inspectAccount(account.name).secretPresent ? chalk.green('ready') : chalk.red('missing on this device');
|
|
175
|
+
out.push(` ${chalk.cyan(account.name.padEnd(nameW))} ${account.provider.padEnd(provW)} ${account.auth.padEnd(authW)} ${present}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return out.join('\n');
|
|
108
179
|
}
|
|
109
180
|
function parseAuth(raw) {
|
|
110
181
|
if (raw === 'api-key' || raw === 'setup-token' || raw === 'bearer-token')
|
|
@@ -116,15 +187,104 @@ function parseHarness(agentRaw) {
|
|
|
116
187
|
throw new Error(`Unknown agent '${agentRaw}'.`);
|
|
117
188
|
return agentRaw;
|
|
118
189
|
}
|
|
119
|
-
|
|
190
|
+
/**
|
|
191
|
+
* Fold signed-in run candidates into distinct identities — the unit a label
|
|
192
|
+
* binds to. The same account signed into several versions is ONE row, so a
|
|
193
|
+
* multi-version single-account harness never demands a selector. Candidates
|
|
194
|
+
* with no stable identity (no accountKey, no email) are dropped: there is
|
|
195
|
+
* nothing durable for a label to bind to. Default-version identity first.
|
|
196
|
+
*/
|
|
197
|
+
export function groupLabelIdentities(candidates, defaultVersion) {
|
|
198
|
+
const byKey = new Map();
|
|
199
|
+
for (const candidate of candidates) {
|
|
200
|
+
const key = candidate.accountKey ?? candidate.email?.toLowerCase();
|
|
201
|
+
if (!key)
|
|
202
|
+
continue;
|
|
203
|
+
const row = byKey.get(key) ?? { identityKey: key, email: null, versions: [], isDefault: false };
|
|
204
|
+
row.email ??= candidate.email;
|
|
205
|
+
row.versions.push(candidate.version);
|
|
206
|
+
if (candidate.version === defaultVersion)
|
|
207
|
+
row.isDefault = true;
|
|
208
|
+
byKey.set(key, row);
|
|
209
|
+
}
|
|
210
|
+
return [...byKey.values()].sort((a, b) => a.isDefault !== b.isDefault ? (a.isDefault ? -1 : 1) : (a.email ?? a.identityKey).localeCompare(b.email ?? b.identityKey));
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Resolve which signed-in login a bare-harness `accounts label` call means.
|
|
214
|
+
* `collect` is injectable (the `resolveRunVersion` pattern) so tests drive the
|
|
215
|
+
* real selection path against fixture candidates.
|
|
216
|
+
*/
|
|
217
|
+
export async function resolveLabelIdentity(agent, accountSelector, collect = collectRunCandidates) {
|
|
218
|
+
const candidates = (await collect(agent)).filter(candidate => candidate.signedIn);
|
|
219
|
+
const identities = groupLabelIdentities(candidates, getGlobalDefault(agent));
|
|
220
|
+
if (identities.length === 0)
|
|
221
|
+
throw new Error(`No signed-in ${agent} account with a stable identity. Run the harness and complete its login first.`);
|
|
222
|
+
const needle = accountSelector?.toLowerCase();
|
|
223
|
+
if (needle) {
|
|
224
|
+
const match = identities.find(identity => identity.email?.toLowerCase() === needle || identity.identityKey.toLowerCase() === needle);
|
|
225
|
+
if (!match)
|
|
226
|
+
throw new Error(`Unknown ${agent} account '${accountSelector}'.`);
|
|
227
|
+
return { kind: 'selected', identity: match };
|
|
228
|
+
}
|
|
229
|
+
if (identities.length === 1)
|
|
230
|
+
return { kind: 'selected', identity: identities[0] };
|
|
231
|
+
return { kind: 'ambiguous', identities };
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Body of `accounts label`, exported with the injectable candidate collector
|
|
235
|
+
* so tests exercise everything but the TTY prompt itself.
|
|
236
|
+
*/
|
|
237
|
+
export async function runAccountsLabel(source, label, opts, collect = collectRunCandidates) {
|
|
238
|
+
if (source.includes('@')) {
|
|
239
|
+
// <harness>@<version> pins the login by where it is signed in, so a
|
|
240
|
+
// second selector can only contradict it.
|
|
241
|
+
if (opts.account)
|
|
242
|
+
throw new Error(`'${source}' already selects one login; drop --account.`);
|
|
243
|
+
const identity = await nativeIdentityFromSource(source);
|
|
244
|
+
const account = labelNativeAccount(identity.agent, identity.identityKey, identity.identityLabel, label, identity.scope);
|
|
245
|
+
console.log(chalk.green(`Labeled ${identity.agent} account ${identity.identityLabel ?? identity.identityKey} as '${account.name}'.`));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const agent = parseHarness(source);
|
|
249
|
+
assertNativeAccountNameable(agent);
|
|
250
|
+
const selection = await resolveLabelIdentity(agent, opts.account, collect);
|
|
251
|
+
let selected;
|
|
252
|
+
if (selection.kind === 'selected') {
|
|
253
|
+
selected = selection.identity;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
if (!isInteractiveTerminal()) {
|
|
257
|
+
requireInteractiveSelection(`Selecting the ${agent} login to label`, [
|
|
258
|
+
`agents accounts label ${agent} ${label ?? '<label>'} --account <email|id>`,
|
|
259
|
+
`agents accounts label ${agent}@<version> ${label ?? '<label>'}`,
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
const picked = await pickLabelIdentity(agent, selection.identities);
|
|
263
|
+
if (!picked)
|
|
264
|
+
return;
|
|
265
|
+
selected = picked;
|
|
266
|
+
}
|
|
267
|
+
const account = labelNativeAccount(agent, selected.identityKey, selected.email ?? undefined, label, nativeAccountCapability(agent).scope);
|
|
268
|
+
console.log(chalk.green(`Labeled ${agent} account ${selected.email ?? selected.identityKey} as '${account.name}'.`));
|
|
269
|
+
}
|
|
270
|
+
/** Prompt for the login a label should bind to. A cancelled picker writes nothing. */
|
|
271
|
+
async function pickLabelIdentity(agent, identities) {
|
|
272
|
+
const idW = Math.max(0, ...identities.map(identity => (identity.email ?? identity.identityKey).length));
|
|
273
|
+
const choices = identities.map(identity => ({
|
|
274
|
+
name: [
|
|
275
|
+
(identity.email ?? identity.identityKey).padEnd(idW),
|
|
276
|
+
identity.isDefault ? chalk.green('default') : ' ',
|
|
277
|
+
chalk.gray(identity.versions.join(', ')),
|
|
278
|
+
].join(' '),
|
|
279
|
+
value: identity.identityKey,
|
|
280
|
+
}));
|
|
120
281
|
try {
|
|
121
|
-
|
|
122
|
-
return
|
|
282
|
+
const key = await select({ message: `Select the ${agent} login to label:`, choices, loop: false });
|
|
283
|
+
return identities.find(identity => identity.identityKey === key) ?? null;
|
|
123
284
|
}
|
|
124
285
|
catch (err) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return false;
|
|
286
|
+
if (isPromptCancelled(err))
|
|
287
|
+
return null;
|
|
128
288
|
throw err;
|
|
129
289
|
}
|
|
130
290
|
}
|
|
@@ -288,32 +448,24 @@ export function registerAccountsCommand(program) {
|
|
|
288
448
|
cleanCommandError(command, err);
|
|
289
449
|
}
|
|
290
450
|
});
|
|
291
|
-
accounts.command('label <
|
|
292
|
-
.description('Label a native login
|
|
451
|
+
const labelCmd = accounts.command('label <source> [label]')
|
|
452
|
+
.description('Label a native login by harness or <harness>@<version>; the label binds to the account identity, not the version')
|
|
293
453
|
.option('--account <email-or-id>', 'Native identity to label when the harness has multiple logins')
|
|
294
|
-
.action(async (
|
|
454
|
+
.action(async (source, label, o, command) => {
|
|
295
455
|
try {
|
|
296
|
-
|
|
297
|
-
assertNativeAccountNameable(agent);
|
|
298
|
-
const candidates = (await collectRunCandidates(agent)).filter(candidate => candidate.signedIn);
|
|
299
|
-
const needle = o.account?.toLowerCase();
|
|
300
|
-
const selected = needle
|
|
301
|
-
? candidates.find(candidate => candidate.email?.toLowerCase() === needle || candidate.accountKey?.toLowerCase() === needle)
|
|
302
|
-
: candidates.find(candidate => candidate.version === getGlobalDefault(agent)) ?? (candidates.length === 1 ? candidates[0] : undefined);
|
|
303
|
-
if (!selected)
|
|
304
|
-
throw new Error(needle
|
|
305
|
-
? `Unknown ${agent} account '${o.account}'.`
|
|
306
|
-
: `Multiple ${agent} accounts are connected; pass --account <email|id>.`);
|
|
307
|
-
const identityKey = selected.accountKey ?? selected.email?.toLowerCase();
|
|
308
|
-
if (!identityKey)
|
|
309
|
-
throw new Error(`${agent} account '${selected.version}' has no stable identity.`);
|
|
310
|
-
const account = labelNativeAccount(agent, identityKey, selected.email ?? undefined, label, nativeAccountCapability(agent).scope);
|
|
311
|
-
console.log(chalk.green(`Labeled ${agent} account ${selected.email ?? identityKey} as '${account.name}'.`));
|
|
456
|
+
await runAccountsLabel(source, label, o);
|
|
312
457
|
}
|
|
313
458
|
catch (err) {
|
|
314
459
|
cleanCommandError(command, err);
|
|
315
460
|
}
|
|
316
461
|
});
|
|
462
|
+
setHelpSections(labelCmd, {
|
|
463
|
+
examples: `agents accounts label codex work
|
|
464
|
+
agents accounts label codex@0.146.0 personal
|
|
465
|
+
agents accounts label codex work --account you@example.com
|
|
466
|
+
agents run codex#work`,
|
|
467
|
+
notes: 'The label binds to the signed-in account identity, not the version — codex#work keeps selecting that account after it moves to a newer install. One signed-in login needs no selector; with several, an interactive terminal opens a picker, while scripts pass --account or <harness>@<version>.',
|
|
468
|
+
});
|
|
317
469
|
accounts.command('attach <account> <target>')
|
|
318
470
|
.description('Attach a named account to a native installation or custom harness')
|
|
319
471
|
.action(async (name, target) => {
|
|
@@ -351,12 +503,21 @@ export function registerAccountsCommand(program) {
|
|
|
351
503
|
getAccountProvider(account.provider).envFor(targetAgent, account.auth);
|
|
352
504
|
}
|
|
353
505
|
bindAccount(name, target);
|
|
506
|
+
writeClaudeInteractiveOauthToken(t, targetAgent);
|
|
354
507
|
console.log(chalk.green(`Attached ${account.name} to ${target}.`));
|
|
355
508
|
});
|
|
356
509
|
accounts.command('detach <account> <target>')
|
|
357
510
|
.description('Remove one account attachment')
|
|
358
511
|
.action((name, target) => {
|
|
359
512
|
unbindAccount(name, target);
|
|
513
|
+
// With the binding gone, no setup-token resolves for this version home, so this
|
|
514
|
+
// clears any .oauth_token the attach left behind (else interactive runs would keep
|
|
515
|
+
// authenticating as the just-detached account).
|
|
516
|
+
try {
|
|
517
|
+
const t = classifyAttachTarget(target);
|
|
518
|
+
writeClaudeInteractiveOauthToken(t, t.kind === 'profile' ? t.profile.host.agent : t.agent);
|
|
519
|
+
}
|
|
520
|
+
catch { /* an unresolvable target has no version home to clean */ }
|
|
360
521
|
console.log(chalk.green(`Detached ${name} from ${target}.`));
|
|
361
522
|
});
|
|
362
523
|
accounts.command('rename <old> <new>').description('Rename an account without changing its stable id').action((oldName, newName) => renameAccount(oldName, newName));
|
|
@@ -469,6 +630,7 @@ agents run claude`,
|
|
|
469
630
|
agents accounts add openrouter-work --provider openrouter --auth api-key --from-secrets openrouter.ai:OPENROUTER_API_KEY
|
|
470
631
|
agents accounts set-key work
|
|
471
632
|
agents accounts name claude@2.1.220 work
|
|
633
|
+
agents accounts label codex@0.146.0 personal
|
|
472
634
|
agents accounts attach work claude@2.1.225
|
|
473
635
|
agents accounts view work --json
|
|
474
636
|
agents accounts switch claude
|
|
@@ -20,18 +20,19 @@ import { registerArtifactsSetupCommand } from './artifacts-setup.js';
|
|
|
20
20
|
export function registerArtifactsCommands(program) {
|
|
21
21
|
const artifactsCmd = program
|
|
22
22
|
.command('artifacts')
|
|
23
|
-
.description('Publish agent-made artifacts (plans, reports, visuals) to
|
|
23
|
+
.description('Publish agent-made artifacts (plans, reports, visuals) to a shareable link — managed if signed in, otherwise your Cloudflare R2.');
|
|
24
24
|
registerArtifactsSetupCommand(artifactsCmd);
|
|
25
25
|
registerShareCommands(artifactsCmd);
|
|
26
26
|
setHelpSections(artifactsCmd, {
|
|
27
27
|
examples: `
|
|
28
|
-
#
|
|
28
|
+
# Signed in? Just publish — no Cloudflare setup
|
|
29
|
+
agents auth login
|
|
30
|
+
agents artifacts share ./out/plan.html
|
|
31
|
+
|
|
32
|
+
# Or provision your own Cloudflare endpoint (or join a teammate's)
|
|
29
33
|
agents artifacts setup
|
|
30
34
|
agents artifacts share join https://share.agents-cli.sh
|
|
31
35
|
|
|
32
|
-
# Publish — auto OG cover, default 30d expiry, shareable link
|
|
33
|
-
agents artifacts share ./out/plan.html
|
|
34
|
-
|
|
35
36
|
# What's live in your namespace, and taking one down
|
|
36
37
|
agents artifacts share list
|
|
37
38
|
agents artifacts unshare my-plan-a1b2
|
|
@@ -50,7 +51,7 @@ export function registerArtifactsCommands(program) {
|
|
|
50
51
|
agents artifacts unshare <targets...> is the nested alias of
|
|
51
52
|
agents artifacts share delete. Top-level \`agents unshare\` is gone.
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
Architecture: cli/docs/observability.md.
|
|
54
55
|
`,
|
|
55
56
|
});
|
|
56
57
|
registerUnshareCommand(artifactsCmd);
|
package/dist/commands/attach.js
CHANGED
|
@@ -50,7 +50,7 @@ export async function attachAction(id) {
|
|
|
50
50
|
// process on the owning machine.
|
|
51
51
|
const peer = sessionRecoveryPeer(meta);
|
|
52
52
|
if (peer) {
|
|
53
|
-
const routed = await runOnPeer(attachRecoveryArgs(meta), peer, { tty: true });
|
|
53
|
+
const routed = await runOnPeer(attachRecoveryArgs(meta), peer, { tty: true, sessionId: meta.id });
|
|
54
54
|
if (routed === 'no-target') {
|
|
55
55
|
console.error(chalk.red(`Cannot attach ${meta.shortId}: origin device ${peer} is not a registered reachable peer.`));
|
|
56
56
|
process.exitCode = 1;
|