@phnx-labs/agents-cli 1.22.47 → 1.22.49
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 +401 -17
- package/README.md +66 -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 +206 -32
- 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.d.ts +21 -0
- package/dist/commands/daemon.js +147 -12
- package/dist/commands/exec.js +52 -15
- 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 +152 -52
- package/dist/commands/ssh.js +54 -7
- package/dist/commands/traces.d.ts +2 -0
- package/dist/commands/traces.js +184 -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/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/versions.js +25 -2
- 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 +39 -15
- package/dist/lib/share/publish.js +55 -26
- package/dist/lib/share/worker-template.js +171 -11
- 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/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 +447 -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';
|
|
@@ -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)'));
|
|
102
162
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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>'."));
|
|
168
|
+
}
|
|
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,6 +187,107 @@ function parseHarness(agentRaw) {
|
|
|
116
187
|
throw new Error(`Unknown agent '${agentRaw}'.`);
|
|
117
188
|
return agentRaw;
|
|
118
189
|
}
|
|
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
|
+
}));
|
|
281
|
+
try {
|
|
282
|
+
const key = await select({ message: `Select the ${agent} login to label:`, choices, loop: false });
|
|
283
|
+
return identities.find(identity => identity.identityKey === key) ?? null;
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
if (isPromptCancelled(err))
|
|
287
|
+
return null;
|
|
288
|
+
throw err;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
119
291
|
function providerAuthenticatesHarness(provider, auth, agent) {
|
|
120
292
|
try {
|
|
121
293
|
getAccountProvider(provider).envFor(agent, auth);
|
|
@@ -288,32 +460,24 @@ export function registerAccountsCommand(program) {
|
|
|
288
460
|
cleanCommandError(command, err);
|
|
289
461
|
}
|
|
290
462
|
});
|
|
291
|
-
accounts.command('label <
|
|
292
|
-
.description('Label a native login
|
|
463
|
+
const labelCmd = accounts.command('label <source> [label]')
|
|
464
|
+
.description('Label a native login by harness or <harness>@<version>; the label binds to the account identity, not the version')
|
|
293
465
|
.option('--account <email-or-id>', 'Native identity to label when the harness has multiple logins')
|
|
294
|
-
.action(async (
|
|
466
|
+
.action(async (source, label, o, command) => {
|
|
295
467
|
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}'.`));
|
|
468
|
+
await runAccountsLabel(source, label, o);
|
|
312
469
|
}
|
|
313
470
|
catch (err) {
|
|
314
471
|
cleanCommandError(command, err);
|
|
315
472
|
}
|
|
316
473
|
});
|
|
474
|
+
setHelpSections(labelCmd, {
|
|
475
|
+
examples: `agents accounts label codex work
|
|
476
|
+
agents accounts label codex@0.146.0 personal
|
|
477
|
+
agents accounts label codex work --account you@example.com
|
|
478
|
+
agents run codex#work`,
|
|
479
|
+
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>.',
|
|
480
|
+
});
|
|
317
481
|
accounts.command('attach <account> <target>')
|
|
318
482
|
.description('Attach a named account to a native installation or custom harness')
|
|
319
483
|
.action(async (name, target) => {
|
|
@@ -351,12 +515,21 @@ export function registerAccountsCommand(program) {
|
|
|
351
515
|
getAccountProvider(account.provider).envFor(targetAgent, account.auth);
|
|
352
516
|
}
|
|
353
517
|
bindAccount(name, target);
|
|
518
|
+
writeClaudeInteractiveOauthToken(t, targetAgent);
|
|
354
519
|
console.log(chalk.green(`Attached ${account.name} to ${target}.`));
|
|
355
520
|
});
|
|
356
521
|
accounts.command('detach <account> <target>')
|
|
357
522
|
.description('Remove one account attachment')
|
|
358
523
|
.action((name, target) => {
|
|
359
524
|
unbindAccount(name, target);
|
|
525
|
+
// With the binding gone, no setup-token resolves for this version home, so this
|
|
526
|
+
// clears any .oauth_token the attach left behind (else interactive runs would keep
|
|
527
|
+
// authenticating as the just-detached account).
|
|
528
|
+
try {
|
|
529
|
+
const t = classifyAttachTarget(target);
|
|
530
|
+
writeClaudeInteractiveOauthToken(t, t.kind === 'profile' ? t.profile.host.agent : t.agent);
|
|
531
|
+
}
|
|
532
|
+
catch { /* an unresolvable target has no version home to clean */ }
|
|
360
533
|
console.log(chalk.green(`Detached ${name} from ${target}.`));
|
|
361
534
|
});
|
|
362
535
|
accounts.command('rename <old> <new>').description('Rename an account without changing its stable id').action((oldName, newName) => renameAccount(oldName, newName));
|
|
@@ -469,6 +642,7 @@ agents run claude`,
|
|
|
469
642
|
agents accounts add openrouter-work --provider openrouter --auth api-key --from-secrets openrouter.ai:OPENROUTER_API_KEY
|
|
470
643
|
agents accounts set-key work
|
|
471
644
|
agents accounts name claude@2.1.220 work
|
|
645
|
+
agents accounts label codex@0.146.0 personal
|
|
472
646
|
agents accounts attach work claude@2.1.225
|
|
473
647
|
agents accounts view work --json
|
|
474
648
|
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;
|