@phnx-labs/agents-cli 1.20.32 → 1.20.34
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 +7 -0
- package/README.md +28 -2
- package/dist/commands/commands.js +3 -3
- package/dist/commands/computer-actions.js +1 -0
- package/dist/commands/computer.d.ts +23 -0
- package/dist/commands/computer.js +45 -3
- package/dist/commands/cost.js +2 -2
- package/dist/commands/doctor.d.ts +10 -0
- package/dist/commands/doctor.js +51 -2
- package/dist/commands/exec.js +56 -1
- package/dist/commands/hooks.js +3 -3
- package/dist/commands/import.js +1 -1
- package/dist/commands/inspect.js +13 -17
- package/dist/commands/mcp.js +3 -3
- package/dist/commands/permissions.js +3 -3
- package/dist/commands/rules.js +3 -3
- package/dist/commands/secrets-migrate.js +23 -11
- package/dist/commands/secrets.d.ts +20 -0
- package/dist/commands/secrets.js +53 -1
- package/dist/commands/sessions.js +18 -1
- package/dist/commands/skills.js +3 -3
- package/dist/commands/status.d.ts +12 -0
- package/dist/commands/status.js +81 -0
- package/dist/commands/sync.js +2 -2
- package/dist/commands/teams.js +77 -18
- package/dist/commands/usage.js +2 -2
- package/dist/commands/utils.d.ts +8 -0
- package/dist/commands/utils.js +20 -0
- package/dist/commands/versions.js +4 -3
- package/dist/commands/view.d.ts +39 -0
- package/dist/commands/view.js +217 -84
- package/dist/commands/workflows.js +3 -3
- package/dist/index.js +16 -2
- package/dist/lib/acp/harnesses.d.ts +1 -1
- package/dist/lib/acp/harnesses.js +2 -2
- package/dist/lib/agent-spec/index.d.ts +18 -0
- package/dist/lib/agent-spec/index.js +35 -0
- package/dist/lib/agent-spec/primitives.d.ts +28 -0
- package/dist/lib/agent-spec/primitives.js +57 -0
- package/dist/lib/agent-spec/provider.d.ts +2 -0
- package/dist/lib/agent-spec/provider.js +9 -0
- package/dist/lib/agent-spec/resolve.d.ts +33 -0
- package/dist/lib/agent-spec/resolve.js +174 -0
- package/dist/lib/agent-spec/types.d.ts +57 -0
- package/dist/lib/agent-spec/types.js +18 -0
- package/dist/lib/agents.d.ts +12 -0
- package/dist/lib/agents.js +115 -32
- package/dist/lib/browser/chrome.js +20 -0
- package/dist/lib/browser/drivers/ssh.d.ts +19 -0
- package/dist/lib/browser/drivers/ssh.js +18 -3
- package/dist/lib/crabbox/cli.d.ts +98 -0
- package/dist/lib/crabbox/cli.js +218 -0
- package/dist/lib/crabbox/lease.d.ts +41 -0
- package/dist/lib/crabbox/lease.js +73 -0
- package/dist/lib/crabbox/runtimes.d.ts +57 -0
- package/dist/lib/crabbox/runtimes.js +109 -0
- package/dist/lib/doctor-diff.js +29 -2
- package/dist/lib/drift-sync.d.ts +43 -0
- package/dist/lib/drift-sync.js +179 -0
- package/dist/lib/exec.d.ts +15 -0
- package/dist/lib/exec.js +21 -11
- package/dist/lib/hosts/dispatch.d.ts +27 -10
- package/dist/lib/hosts/dispatch.js +55 -19
- package/dist/lib/hosts/option.d.ts +14 -0
- package/dist/lib/hosts/option.js +19 -0
- package/dist/lib/hosts/passthrough.d.ts +30 -0
- package/dist/lib/hosts/passthrough.js +141 -0
- package/dist/lib/hosts/remote-cmd.d.ts +36 -0
- package/dist/lib/hosts/remote-cmd.js +56 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/platform/winpath.d.ts +31 -2
- package/dist/lib/platform/winpath.js +133 -24
- package/dist/lib/pwsh.d.ts +11 -0
- package/dist/lib/pwsh.js +13 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.d.ts +42 -1
- package/dist/lib/secrets/agent.js +89 -11
- package/dist/lib/secrets/bundles.js +61 -21
- package/dist/lib/secrets/filestore.js +31 -1
- package/dist/lib/secrets/index.d.ts +44 -1
- package/dist/lib/secrets/index.js +108 -10
- package/dist/lib/secrets/linux.d.ts +14 -0
- package/dist/lib/secrets/linux.js +21 -0
- package/dist/lib/secrets/windows.d.ts +74 -0
- package/dist/lib/secrets/windows.js +440 -0
- package/dist/lib/session/active.d.ts +8 -0
- package/dist/lib/session/active.js +18 -1
- package/dist/lib/session/provenance.d.ts +56 -0
- package/dist/lib/session/provenance.js +157 -0
- package/dist/lib/shims.d.ts +20 -0
- package/dist/lib/shims.js +53 -20
- package/dist/lib/ssh-exec.d.ts +22 -0
- package/dist/lib/ssh-exec.js +59 -2
- package/dist/lib/ssh-tunnel.d.ts +0 -5
- package/dist/lib/ssh-tunnel.js +65 -8
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/sync-status.d.ts +102 -0
- package/dist/lib/sync-status.js +135 -0
- package/dist/lib/teams/agents.d.ts +24 -0
- package/dist/lib/teams/agents.js +30 -1
- package/dist/lib/types.d.ts +20 -1
- package/dist/lib/usage.d.ts +30 -0
- package/dist/lib/usage.js +159 -2
- package/dist/lib/versions.d.ts +2 -4
- package/dist/lib/versions.js +7 -20
- package/package.json +2 -1
- package/dist/lib/agent-spec.d.ts +0 -36
- package/dist/lib/agent-spec.js +0 -157
|
@@ -23,7 +23,7 @@ import chalk from 'chalk';
|
|
|
23
23
|
import * as crypto from 'crypto';
|
|
24
24
|
import * as fs from 'fs';
|
|
25
25
|
import * as path from 'path';
|
|
26
|
-
import { deleteKeychainToken, getKeychainToken, hasKeychainToken, listKeychainItems, setKeychainToken, } from '../lib/secrets/index.js';
|
|
26
|
+
import { deleteKeychainToken, getKeychainToken, getKeychainTokens, hasKeychainToken, listKeychainItems, listLegacyKeychainItems, setKeychainToken, } from '../lib/secrets/index.js';
|
|
27
27
|
import { getBackupsDir } from '../lib/state.js';
|
|
28
28
|
import { encryptBlob, MIN_PASSPHRASE_LEN } from '../lib/secrets/sync.js';
|
|
29
29
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
@@ -104,6 +104,7 @@ export function registerSecretsMigrateAclCommand(secrets) {
|
|
|
104
104
|
.description('Refresh existing keychain ACLs to use the signed Agents CLI helper. Dry-run by default.')
|
|
105
105
|
.option('--commit', 'Perform writes (default is dry-run reporting only)')
|
|
106
106
|
.option('--prefix <p>', `Restrict to items beginning with PREFIX (default ${ITEM_PREFIX})`, ITEM_PREFIX)
|
|
107
|
+
.option('--all', 'Rewrite EVERY matching item, not just legacy stragglers (slower; a Touch ID prompt per item)')
|
|
107
108
|
.option('--passphrase-env <var>', 'Read the backup passphrase from this env var instead of prompting')
|
|
108
109
|
.action(async (opts) => {
|
|
109
110
|
try {
|
|
@@ -114,15 +115,24 @@ export function registerSecretsMigrateAclCommand(secrets) {
|
|
|
114
115
|
if (!prefix.startsWith(ITEM_PREFIX)) {
|
|
115
116
|
throw new Error(`--prefix must start with '${ITEM_PREFIX}' to avoid touching unrelated Keychain items (got '${prefix}').`);
|
|
116
117
|
}
|
|
117
|
-
|
|
118
|
+
// Default: migrate ONLY legacy stragglers (items still in the file-based
|
|
119
|
+
// keychain) — modern DP items need no rewrite and touching them is a
|
|
120
|
+
// Touch ID prompt per item for nothing. `--all` forces the old full
|
|
121
|
+
// rewrite. Either way this is one command over every matching item — no
|
|
122
|
+
// one-by-one invocation.
|
|
123
|
+
const names = opts.all ? listKeychainItems(prefix) : listLegacyKeychainItems(prefix);
|
|
124
|
+
const items = names.map((item) => {
|
|
118
125
|
const localExists = hasKeychainToken(item);
|
|
119
126
|
return { item, sync: !localExists };
|
|
120
127
|
});
|
|
121
128
|
if (items.length === 0) {
|
|
122
|
-
console.log(
|
|
129
|
+
console.log(opts.all
|
|
130
|
+
? chalk.gray(`No keychain items with prefix '${prefix}'.`)
|
|
131
|
+
: chalk.green(`Nothing to migrate — all items under '${prefix}' already use the modern ACL.`));
|
|
123
132
|
return;
|
|
124
133
|
}
|
|
125
|
-
|
|
134
|
+
const label = opts.all ? 'item' : 'legacy item';
|
|
135
|
+
console.log(chalk.bold(`Found ${items.length} ${label}(s) under '${prefix}' to migrate.`));
|
|
126
136
|
if (!opts.commit) {
|
|
127
137
|
for (const { item, sync } of items) {
|
|
128
138
|
console.log(` ${chalk.cyan(item)} ${chalk.gray(sync ? '(synced)' : '(local)')}`);
|
|
@@ -131,16 +141,18 @@ export function registerSecretsMigrateAclCommand(secrets) {
|
|
|
131
141
|
console.log(chalk.gray('Dry-run — pass --commit to perform the migration.'));
|
|
132
142
|
return;
|
|
133
143
|
}
|
|
134
|
-
// Commit phase. Snapshot every value first
|
|
144
|
+
// Commit phase. Snapshot every value first (one batched read behind a
|
|
145
|
+
// single helper process, so DP items share one auth context), encrypt,
|
|
146
|
+
// then mutate.
|
|
147
|
+
const fetched = getKeychainTokens(items.map((i) => i.item));
|
|
135
148
|
const records = [];
|
|
136
149
|
for (const { item, sync } of items) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
catch (err) {
|
|
142
|
-
console.error(chalk.red(`Skipping '${item}': read failed (${err.message}).`));
|
|
150
|
+
const value = fetched.get(item);
|
|
151
|
+
if (value === undefined) {
|
|
152
|
+
console.error(chalk.red(`Skipping '${item}': read failed or item absent.`));
|
|
153
|
+
continue;
|
|
143
154
|
}
|
|
155
|
+
records.push({ item, sync, value });
|
|
144
156
|
}
|
|
145
157
|
if (records.length === 0) {
|
|
146
158
|
console.error(chalk.red('No items could be read. Aborting before any writes.'));
|
|
@@ -18,3 +18,23 @@ export { SSH_TARGET_RE, assertValidSshTarget };
|
|
|
18
18
|
export declare function bundleEnvToDotenv(env: Record<string, string>): string;
|
|
19
19
|
/** Register the `agents secrets` command tree. */
|
|
20
20
|
export declare function registerSecretsCommands(program: Command): void;
|
|
21
|
+
/**
|
|
22
|
+
* Quote one argument for a Windows `cmd.exe` command line, as built by Node's
|
|
23
|
+
* `spawn(..., { shell: true })` on win32 (`agents secrets exec`). cmd.exe does
|
|
24
|
+
* NO quoting of its own, so an unquoted arg with a space is split into several
|
|
25
|
+
* args, and a cmd metacharacter (`&|<>()^`) would be interpreted by the shell.
|
|
26
|
+
* We wrap any arg with whitespace, a quote, or a metacharacter in double quotes
|
|
27
|
+
* and escape embedded quotes / trailing backslashes per the CommandLineToArgvW
|
|
28
|
+
* rules, so the *child's* argv parse reconstructs the original argument.
|
|
29
|
+
*
|
|
30
|
+
* CAVEAT: cmd.exe expands `%VAR%` (always) and `!VAR!` (under delayed expansion)
|
|
31
|
+
* BEFORE argv parsing, and double-quoting does NOT suppress `%`/`!` (the
|
|
32
|
+
* "BatBadBut" / CVE-2024-1874 class). We deliberately do not escape `%`/`!`:
|
|
33
|
+
* `agents secrets exec` runs a caller-supplied command against a bundle the
|
|
34
|
+
* caller owns, so caller-controlled `%`/`!` is not a privilege boundary. If that
|
|
35
|
+
* ever changes (exec'ing an untrusted command line), route through a shell that
|
|
36
|
+
* disables expansion rather than relying on this quoter. An empty arg becomes
|
|
37
|
+
* `""`. Exported for tests. No-ops on non-Windows (the caller only invokes it
|
|
38
|
+
* under `process.platform === 'win32'`).
|
|
39
|
+
*/
|
|
40
|
+
export declare function quoteWin32ExecArg(arg: string): string;
|
package/dist/commands/secrets.js
CHANGED
|
@@ -1325,8 +1325,18 @@ Examples:
|
|
|
1325
1325
|
secretEnv = readAndResolveBundleEnv(bundleName, { caller: `command ${cmd}` }).env;
|
|
1326
1326
|
}
|
|
1327
1327
|
const { spawn } = await import('child_process');
|
|
1328
|
-
|
|
1328
|
+
// On Windows, spawn without a shell ENOENTs for `.cmd`/`.bat` launchers
|
|
1329
|
+
// (npm, yarn, most JS CLIs) and shell built-ins, so we set shell:true.
|
|
1330
|
+
// With shell:true Node hands cmd.exe a single command line with NO quoting
|
|
1331
|
+
// of its own, so args containing spaces or cmd metacharacters must be
|
|
1332
|
+
// quoted here (quoteWin32ExecArg) or they'd be split. See that helper for
|
|
1333
|
+
// the cmd.exe %VAR%/!VAR! expansion caveat.
|
|
1334
|
+
const useShell = process.platform === 'win32';
|
|
1335
|
+
const spawnCmd = useShell ? quoteWin32ExecArg(cmd) : cmd;
|
|
1336
|
+
const spawnArgs = useShell ? args.map(quoteWin32ExecArg) : args;
|
|
1337
|
+
const proc = spawn(spawnCmd, spawnArgs, {
|
|
1329
1338
|
stdio: 'inherit',
|
|
1339
|
+
shell: useShell,
|
|
1330
1340
|
env: { ...process.env, ...secretEnv },
|
|
1331
1341
|
});
|
|
1332
1342
|
proc.on('close', (code) => process.exit(code ?? 0));
|
|
@@ -1619,6 +1629,48 @@ function humanRemaining(expiresAt) {
|
|
|
1619
1629
|
const days = Math.round(hours / 24);
|
|
1620
1630
|
return `locks in ${days} day${days === 1 ? '' : 's'}`;
|
|
1621
1631
|
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Quote one argument for a Windows `cmd.exe` command line, as built by Node's
|
|
1634
|
+
* `spawn(..., { shell: true })` on win32 (`agents secrets exec`). cmd.exe does
|
|
1635
|
+
* NO quoting of its own, so an unquoted arg with a space is split into several
|
|
1636
|
+
* args, and a cmd metacharacter (`&|<>()^`) would be interpreted by the shell.
|
|
1637
|
+
* We wrap any arg with whitespace, a quote, or a metacharacter in double quotes
|
|
1638
|
+
* and escape embedded quotes / trailing backslashes per the CommandLineToArgvW
|
|
1639
|
+
* rules, so the *child's* argv parse reconstructs the original argument.
|
|
1640
|
+
*
|
|
1641
|
+
* CAVEAT: cmd.exe expands `%VAR%` (always) and `!VAR!` (under delayed expansion)
|
|
1642
|
+
* BEFORE argv parsing, and double-quoting does NOT suppress `%`/`!` (the
|
|
1643
|
+
* "BatBadBut" / CVE-2024-1874 class). We deliberately do not escape `%`/`!`:
|
|
1644
|
+
* `agents secrets exec` runs a caller-supplied command against a bundle the
|
|
1645
|
+
* caller owns, so caller-controlled `%`/`!` is not a privilege boundary. If that
|
|
1646
|
+
* ever changes (exec'ing an untrusted command line), route through a shell that
|
|
1647
|
+
* disables expansion rather than relying on this quoter. An empty arg becomes
|
|
1648
|
+
* `""`. Exported for tests. No-ops on non-Windows (the caller only invokes it
|
|
1649
|
+
* under `process.platform === 'win32'`).
|
|
1650
|
+
*/
|
|
1651
|
+
export function quoteWin32ExecArg(arg) {
|
|
1652
|
+
if (arg.length > 0 && !/[\s"&|<>()^]/.test(arg))
|
|
1653
|
+
return arg;
|
|
1654
|
+
let result = '"';
|
|
1655
|
+
let backslashes = 0;
|
|
1656
|
+
for (const ch of arg) {
|
|
1657
|
+
if (ch === '\\') {
|
|
1658
|
+
backslashes += 1;
|
|
1659
|
+
continue;
|
|
1660
|
+
}
|
|
1661
|
+
if (ch === '"') {
|
|
1662
|
+
// Double the run of backslashes, then escape this quote.
|
|
1663
|
+
result += '\\'.repeat(backslashes * 2 + 1) + '"';
|
|
1664
|
+
backslashes = 0;
|
|
1665
|
+
continue;
|
|
1666
|
+
}
|
|
1667
|
+
result += '\\'.repeat(backslashes) + ch;
|
|
1668
|
+
backslashes = 0;
|
|
1669
|
+
}
|
|
1670
|
+
// Trailing backslashes precede the closing quote → must be doubled.
|
|
1671
|
+
result += '\\'.repeat(backslashes * 2) + '"';
|
|
1672
|
+
return result;
|
|
1673
|
+
}
|
|
1622
1674
|
/**
|
|
1623
1675
|
* Copy text to the system clipboard, cross-platform.
|
|
1624
1676
|
* macOS: `pbcopy`. Windows: `clip`. Linux: tries `wl-copy` (Wayland), then
|
|
@@ -215,6 +215,23 @@ function signalBadges(s) {
|
|
|
215
215
|
parts.push(chalk.magenta(`wt:${s.worktree.slug}`));
|
|
216
216
|
return parts.join(' ');
|
|
217
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Compact provenance badge: how to reach the session, not what it's doing.
|
|
220
|
+
* `ssh` flags a remote host; the tmux pane id is the send-keys target the feed
|
|
221
|
+
* would type back into. Local, non-tmux sessions add nothing (the common case).
|
|
222
|
+
*/
|
|
223
|
+
function provenanceBadge(p) {
|
|
224
|
+
if (!p)
|
|
225
|
+
return '';
|
|
226
|
+
const parts = [];
|
|
227
|
+
if (p.transport === 'ssh')
|
|
228
|
+
parts.push(chalk.red('ssh'));
|
|
229
|
+
if (p.mux?.kind === 'tmux' && p.mux.pane)
|
|
230
|
+
parts.push(chalk.green(`tmux ${p.mux.pane}`));
|
|
231
|
+
else if (p.mux?.kind === 'screen')
|
|
232
|
+
parts.push(chalk.green('screen'));
|
|
233
|
+
return parts.join(' ');
|
|
234
|
+
}
|
|
218
235
|
/**
|
|
219
236
|
* Render a single agent-session row inside an already-printed group header.
|
|
220
237
|
* Indent is the leading whitespace (2 spaces for flat groups, 4 inside a
|
|
@@ -228,7 +245,7 @@ function printActiveRow(s, indent) {
|
|
|
228
245
|
const hostCol = chalk.gray(padToWidth(truncateToWidth(s.host ?? '-', 8), 9));
|
|
229
246
|
const statusCol = statusColor(s.status)(padToWidth(truncateToWidth(activityLabel(s), 8), 9));
|
|
230
247
|
const fork = s.pidCount && s.pidCount > 1 ? chalk.dim(`×${s.pidCount} `) : '';
|
|
231
|
-
const badges = (fork ? fork : '') + signalBadges(s);
|
|
248
|
+
const badges = (fork ? fork : '') + [signalBadges(s), provenanceBadge(s.provenance)].filter(Boolean).join(' ');
|
|
232
249
|
const desc = buildSessionDescription(s) || '-';
|
|
233
250
|
// Fill the remaining width with the preview so nothing wraps under tmux/SSH.
|
|
234
251
|
const fixed = stringWidth(indent) + 9 + 9 + 9 + 9 + (badges ? stringWidth(badges) + 1 : 0);
|
package/dist/commands/skills.js
CHANGED
|
@@ -8,9 +8,9 @@ import { AGENTS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/
|
|
|
8
8
|
import { capableAgents } from '../lib/capabilities.js';
|
|
9
9
|
import { cloneRepo } from '../lib/git.js';
|
|
10
10
|
import { discoverSkillsFromRepo, installSkillCentrally, listInstalledSkills, listInstalledSkillsWithScope, getSkillInfo, getSkillRules, getSkillsDir, countSkillFiles, tryParseSkillMetadata, diffVersionSkills, iterSkillsCapableVersions, removeSkillFromVersion, } from '../lib/skills.js';
|
|
11
|
-
import { getGlobalDefault,
|
|
11
|
+
import { getGlobalDefault, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, } from '../lib/versions.js';
|
|
12
12
|
import { recordVersionResources } from '../lib/state.js';
|
|
13
|
-
import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, resolveAgentTargetsAutoInstalling, promptRemovalTargets, } from './utils.js';
|
|
13
|
+
import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, resolveAgentTargetsAutoInstalling, promptRemovalTargets, resolveListFilterOrExit, } from './utils.js';
|
|
14
14
|
import { showResourceList, buildTargetsSection, } from './resource-view.js';
|
|
15
15
|
/** Register the `agents skills` command tree (list, add, remove, sync, prune, view). */
|
|
16
16
|
export function registerSkillsCommands(program) {
|
|
@@ -59,7 +59,7 @@ When to use:
|
|
|
59
59
|
process.exit(1);
|
|
60
60
|
}
|
|
61
61
|
filterAgent = resolved;
|
|
62
|
-
filterVersion =
|
|
62
|
+
filterVersion = resolveListFilterOrExit(resolved, parts[1]);
|
|
63
63
|
}
|
|
64
64
|
const rows = await buildSkillRows({ filterAgent, filterVersion });
|
|
65
65
|
spinner.stop();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents status` — the unified sync-status surface.
|
|
3
|
+
*
|
|
4
|
+
* One command that answers "is my fleet in sync?" the same way every other
|
|
5
|
+
* surface does, because it reads the same engine (computeSyncStatus). Human mode
|
|
6
|
+
* renders the summary and, when a TTY finds drift, offers the interactive
|
|
7
|
+
* "sync now?" flow (promptDriftSync). `--json` emits the stable UnifiedSyncStatus
|
|
8
|
+
* contract the menu-bar and Agency consume. `--yes` reconciles everything with no
|
|
9
|
+
* prompts (the "kick it" path, safe in scripts).
|
|
10
|
+
*/
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
export declare function registerStatusCommand(program: Command): void;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents status` — the unified sync-status surface.
|
|
3
|
+
*
|
|
4
|
+
* One command that answers "is my fleet in sync?" the same way every other
|
|
5
|
+
* surface does, because it reads the same engine (computeSyncStatus). Human mode
|
|
6
|
+
* renders the summary and, when a TTY finds drift, offers the interactive
|
|
7
|
+
* "sync now?" flow (promptDriftSync). `--json` emits the stable UnifiedSyncStatus
|
|
8
|
+
* contract the menu-bar and Agency consume. `--yes` reconciles everything with no
|
|
9
|
+
* prompts (the "kick it" path, safe in scripts).
|
|
10
|
+
*/
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { AGENTS } from '../lib/agents.js';
|
|
13
|
+
import { setHelpSections } from '../lib/help.js';
|
|
14
|
+
import { computeSyncStatus } from '../lib/sync-status.js';
|
|
15
|
+
import { promptDriftSync } from '../lib/drift-sync.js';
|
|
16
|
+
const agentName = (id) => AGENTS[id]?.name ?? id;
|
|
17
|
+
function versionSummary(v) {
|
|
18
|
+
if (!v.everSynced)
|
|
19
|
+
return chalk.gray('never synced');
|
|
20
|
+
if (v.needsSync) {
|
|
21
|
+
const bits = [];
|
|
22
|
+
if (v.counts.drifted)
|
|
23
|
+
bits.push(`${v.counts.drifted} drifted`);
|
|
24
|
+
if (v.counts.missing)
|
|
25
|
+
bits.push(`${v.counts.missing} missing`);
|
|
26
|
+
return chalk.yellow(bits.join(' · '));
|
|
27
|
+
}
|
|
28
|
+
return chalk.green('in sync');
|
|
29
|
+
}
|
|
30
|
+
export function registerStatusCommand(program) {
|
|
31
|
+
const cmd = program
|
|
32
|
+
.command('status')
|
|
33
|
+
.description('Unified sync status across the fleet — what is drifted, missing, or behind, with an option to sync it.')
|
|
34
|
+
.option('--json', 'Output the machine-readable UnifiedSyncStatus contract')
|
|
35
|
+
.option('--yes', 'Reconcile everything detected (pull .system if behind + sync drifted/missing resources) without prompting')
|
|
36
|
+
.option('--cwd <path>', 'Resolution cwd for project layer detection (default: process.cwd())');
|
|
37
|
+
setHelpSections(cmd, {
|
|
38
|
+
examples: `
|
|
39
|
+
# Show what's out of sync; offer to fix it (interactive)
|
|
40
|
+
agents status
|
|
41
|
+
|
|
42
|
+
# Machine-readable status for the menu-bar / Agency
|
|
43
|
+
agents status --json
|
|
44
|
+
|
|
45
|
+
# Reconcile everything with no prompts (CI / scripts / the "kick it" path)
|
|
46
|
+
agents status --yes
|
|
47
|
+
`,
|
|
48
|
+
});
|
|
49
|
+
cmd.action(async (opts) => {
|
|
50
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
51
|
+
if (opts.json) {
|
|
52
|
+
const status = await computeSyncStatus({ cwd });
|
|
53
|
+
console.log(JSON.stringify(status, null, 2));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const status = await computeSyncStatus({ cwd });
|
|
57
|
+
// Human summary header (always) — the per-version readout.
|
|
58
|
+
console.log(chalk.bold('Fleet sync status'));
|
|
59
|
+
if (status.system.unknown) {
|
|
60
|
+
console.log(` ${'.system repo'.padEnd(28)} ${chalk.gray('freshness unknown (no upstream)')}`);
|
|
61
|
+
}
|
|
62
|
+
else if (status.system.behind > 0) {
|
|
63
|
+
console.log(` ${'.system repo'.padEnd(28)} ${chalk.yellow(`${status.system.behind} behind`)}`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log(` ${'.system repo'.padEnd(28)} ${chalk.green('up to date')}`);
|
|
67
|
+
}
|
|
68
|
+
if (status.agents.length === 0) {
|
|
69
|
+
console.log(chalk.gray(' (no installed agent versions)'));
|
|
70
|
+
}
|
|
71
|
+
for (const v of status.agents) {
|
|
72
|
+
const label = `${agentName(v.agent)}@${v.version}${v.isDefault ? chalk.gray(' (default)') : ''}`;
|
|
73
|
+
console.log(` ${label.padEnd(28)} ${versionSummary(v)}`);
|
|
74
|
+
}
|
|
75
|
+
if (status.totals.orphan > 0) {
|
|
76
|
+
console.log(chalk.gray(` (${status.totals.orphan} orphan${status.totals.orphan === 1 ? '' : 's'} — run \`agents prune cleanup\`)`));
|
|
77
|
+
}
|
|
78
|
+
// Hand off to the shared interactive/apply flow (summary already printed above).
|
|
79
|
+
await promptDriftSync({ cwd, yes: opts.yes, status, quiet: true });
|
|
80
|
+
});
|
|
81
|
+
}
|
package/dist/commands/sync.js
CHANGED
|
@@ -35,10 +35,10 @@ import { compileRulesForProject } from '../lib/rules/compile.js';
|
|
|
35
35
|
import { runLaunchSync } from '../lib/project-launch.js';
|
|
36
36
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
37
37
|
import { runUmbrellaSync } from '../lib/sync-umbrella.js';
|
|
38
|
+
import { addHostOption } from '../lib/hosts/option.js';
|
|
38
39
|
/** Register the `agents sync` command. */
|
|
39
40
|
export function registerSyncCommand(program) {
|
|
40
|
-
program
|
|
41
|
-
.command('sync [agentSpec] [repo]')
|
|
41
|
+
addHostOption(program.command('sync [agentSpec] [repo]'))
|
|
42
42
|
.summary('Make this machine current, or sync resources into one agent')
|
|
43
43
|
.description('With an [agentSpec], syncs resources (commands, skills, hooks, rules, MCPs, plugins, etc.) into that installed agent version — previews changes and lets you pick. e.g. "claude", "claude@2.1.142", a selector: @latest / @oldest / @pinned (= @default), or @all for every installed version.\n\nAppend a [repo] (or pass --repo) to scope the sync to a single DotAgent repo — system / user / project / <alias>. e.g. "agents sync claude@all system" reconciles only the system repo\'s resources into every installed Claude.\n\nWith NO agent, runs the umbrella verb: fetch remote state (config repos + secrets + sessions) then reconcile it into every installed agent. Scope it with --repos / --secrets / --sessions, --cloud (fetch only), or --local (reconcile only).')
|
|
44
44
|
.option('--agent <agent>', 'Agent identifier (legacy form; prefer the positional spec)')
|
package/dist/commands/teams.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import * as fs from 'fs/promises';
|
|
3
|
+
import { addHostOption } from '../lib/hosts/option.js';
|
|
3
4
|
import * as path from 'path';
|
|
4
|
-
import { AgentManager, checkAllClis, getAgentsDir, VALID_TASK_TYPES, } from '../lib/teams/agents.js';
|
|
5
|
+
import { AgentManager, checkAllClis, checkCliSignedIn, getAgentsDir, resolveSignInAdvisory, VALID_TASK_TYPES, } from '../lib/teams/agents.js';
|
|
5
6
|
import { resolveProvider } from '../lib/cloud/registry.js';
|
|
6
7
|
import { runSupervisor } from '../lib/teams/supervisor.js';
|
|
7
8
|
import { handleSpawn, handleStatus, handleStop, handleTasks, toTaskStatusSummary, } from '../lib/teams/api.js';
|
|
@@ -9,6 +10,7 @@ import { createTeam, ensureTeam, getTeam, loadTeams, removeTeam, teamExists, } f
|
|
|
9
10
|
import { setHelpSections } from '../lib/help.js';
|
|
10
11
|
import { createWorktree, isGitRepo, hasUncommittedChanges, removeWorktree, } from '../lib/teams/worktree.js';
|
|
11
12
|
import { isVersionInstalled, resolveVersionAlias, resolveVersionAliasLoose } from '../lib/versions.js';
|
|
13
|
+
import { AGENTS, warnAgentDeprecated } from '../lib/agents.js';
|
|
12
14
|
import { discoverSessions, parseTimeFilter, resolveSessionById } from '../lib/session/discover.js';
|
|
13
15
|
import { buildPreview as buildSessionPreview } from './sessions-picker.js';
|
|
14
16
|
import { parseExecEnv } from '../lib/exec.js';
|
|
@@ -132,7 +134,7 @@ function parseTeammate(spec) {
|
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
die(`Unknown teammate '${spec}'. Available agents: ${VALID_AGENTS.join(', ')}.\n` +
|
|
135
|
-
` Use 'claude', '
|
|
137
|
+
` Use 'claude', 'kimi@latest', 'kimi@0.19.2' (a version from 'agents view'), or a profile name.`);
|
|
136
138
|
}
|
|
137
139
|
function shortId(id) {
|
|
138
140
|
return id.slice(0, 8);
|
|
@@ -182,6 +184,31 @@ export function wireCloudDispatcher(mgr) {
|
|
|
182
184
|
return { cloudSessionId: cloudTask.id };
|
|
183
185
|
});
|
|
184
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Advisory: warn once per agent type of any still-pending (staged `--after`)
|
|
189
|
+
* teammate whose CLI may not be signed in. Warn-only — never blocks `start`.
|
|
190
|
+
* Local teammates only; cloud teammates authenticate through their provider.
|
|
191
|
+
*/
|
|
192
|
+
async function warnUnsignedTeammates(mgr, team) {
|
|
193
|
+
let pending;
|
|
194
|
+
try {
|
|
195
|
+
pending = (await mgr.listByTask(team)).filter((a) => a.status === 'pending' && !a.cloudProvider);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return; // team not loadable yet — nothing to warn about
|
|
199
|
+
}
|
|
200
|
+
const seen = new Set();
|
|
201
|
+
for (const a of pending) {
|
|
202
|
+
const agent = a.agentType;
|
|
203
|
+
if (seen.has(agent) || !AGENT_NAMES[agent])
|
|
204
|
+
continue;
|
|
205
|
+
seen.add(agent);
|
|
206
|
+
if (!(await checkCliSignedIn(agent))) {
|
|
207
|
+
console.error(chalk.yellow(`⚠ ${AGENT_NAMES[agent]} may not be signed in (detection is unreliable). Launching anyway.`) +
|
|
208
|
+
chalk.gray(`\n If it fails to start, run \`${AGENTS[agent].cliCommand}\` to log in, or pass --force to silence this.`));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
185
212
|
/** Single-wave start used by `teams start` without --watch. */
|
|
186
213
|
async function runOneWave(mgr, team, json) {
|
|
187
214
|
const launched = await mgr.startReady(team);
|
|
@@ -673,8 +700,7 @@ export function registerTeamsCommands(program) {
|
|
|
673
700
|
`,
|
|
674
701
|
});
|
|
675
702
|
// list
|
|
676
|
-
teams
|
|
677
|
-
.command('list [query]')
|
|
703
|
+
addHostOption(teams.command('list [query]'))
|
|
678
704
|
.alias('ls')
|
|
679
705
|
.description('List your teams, most recent activity first')
|
|
680
706
|
.option('-a, --agent <agent>', 'Filter: only teams with this agent (e.g. claude or claude@2.1.112)')
|
|
@@ -785,8 +811,7 @@ export function registerTeamsCommands(program) {
|
|
|
785
811
|
printTeamTable(rows);
|
|
786
812
|
});
|
|
787
813
|
// create
|
|
788
|
-
teams
|
|
789
|
-
.command('create <team>')
|
|
814
|
+
addHostOption(teams.command('create <team>'))
|
|
790
815
|
.aliases(['c', 'new'])
|
|
791
816
|
.description('Start a new team. No teammates yet; add them with `teams add`.')
|
|
792
817
|
.option('-d, --description <text>', 'One-line summary of what this team is working on')
|
|
@@ -825,8 +850,7 @@ export function registerTeamsCommands(program) {
|
|
|
825
850
|
}
|
|
826
851
|
});
|
|
827
852
|
// add
|
|
828
|
-
teams
|
|
829
|
-
.command('add <team> <teammate> <task>')
|
|
853
|
+
addHostOption(teams.command('add <team> <teammate> <task>'))
|
|
830
854
|
.alias('a')
|
|
831
855
|
.description("Add a teammate to work on a task. Runs in background; returns immediately. Use 'status' to check in.")
|
|
832
856
|
.option('-n, --name <name>', 'Friendly name for this teammate (e.g. alice). Required if using --after. Unique within team.')
|
|
@@ -841,6 +865,7 @@ export function registerTeamsCommands(program) {
|
|
|
841
865
|
.option('--cloud <provider>', `Dispatch to cloud backend instead of local CLI: ${VALID_CLOUD_PROVIDERS.join('|')}`)
|
|
842
866
|
.option('--repo <owner/repo>', 'GitHub repository (required for --cloud rush)')
|
|
843
867
|
.option('--branch <name>', 'Target git branch for cloud dispatch')
|
|
868
|
+
.option('--force', "Skip the advisory 'may not be signed in' warning (detection is unreliable)")
|
|
844
869
|
.option('--json', 'Output machine-readable JSON')
|
|
845
870
|
.action(async (team, teammate, task, opts) => {
|
|
846
871
|
if (!VALID_MODES.includes(opts.mode)) {
|
|
@@ -867,10 +892,18 @@ export function registerTeamsCommands(program) {
|
|
|
867
892
|
}
|
|
868
893
|
}
|
|
869
894
|
const { agent, version, profileName } = parseTeammate(teammate);
|
|
895
|
+
warnAgentDeprecated(agent);
|
|
870
896
|
if (version && !isVersionInstalled(agent, version)) {
|
|
871
897
|
die(`${AGENT_NAMES[agent]} ${version} isn't installed.\n` +
|
|
872
898
|
` Install it: agents add ${agent}@${version}\n` +
|
|
873
|
-
` Or see what's installed: agents view ${agent}`);
|
|
899
|
+
` Or see what's installed (incl. @latest): agents view ${agent}`);
|
|
900
|
+
}
|
|
901
|
+
// Advisory sign-in check: warn but NEVER block. Detection is unreliable
|
|
902
|
+
// for opaque-cred agents, so a false negative must not stop a team. Cloud
|
|
903
|
+
// dispatch authenticates through the provider, not the local CLI — skip it.
|
|
904
|
+
if (!opts.force && !cloudProviderId && !(await checkCliSignedIn(agent))) {
|
|
905
|
+
console.error(chalk.yellow(`⚠ ${AGENT_NAMES[agent]} may not be signed in (detection is unreliable). Adding anyway.`) +
|
|
906
|
+
chalk.gray(`\n If it fails to start, run \`${AGENTS[agent].cliCommand}\` to log in, or pass --force to silence this.`));
|
|
874
907
|
}
|
|
875
908
|
if (opts.name !== undefined) {
|
|
876
909
|
if (!opts.name || !/^[A-Za-z0-9_-]+$/.test(opts.name)) {
|
|
@@ -1041,8 +1074,7 @@ export function registerTeamsCommands(program) {
|
|
|
1041
1074
|
}
|
|
1042
1075
|
});
|
|
1043
1076
|
// status
|
|
1044
|
-
teams
|
|
1045
|
-
.command('status [team]')
|
|
1077
|
+
addHostOption(teams.command('status [team]'))
|
|
1046
1078
|
.aliases(['s', 'st', 'check'])
|
|
1047
1079
|
.description("Check in on a team: status, files touched, recent commands, last messages. Pass --verbose for the full per-teammate dump; --since for delta polling.")
|
|
1048
1080
|
.option('-f, --filter <state>', 'Show only teammates in this state: running, completed, failed, stopped, or all (default: all)', 'all')
|
|
@@ -1136,13 +1168,13 @@ export function registerTeamsCommands(program) {
|
|
|
1136
1168
|
console.log(chalk.gray(`${running.length} teammate${running.length === 1 ? '' : 's'} running. See 'agents sessions --active' for the full cross-context view.`));
|
|
1137
1169
|
});
|
|
1138
1170
|
// start — fire any staged teammates whose --after deps have all completed
|
|
1139
|
-
teams
|
|
1140
|
-
.command('start [team]')
|
|
1171
|
+
addHostOption(teams.command('start [team]'))
|
|
1141
1172
|
.description('Launch any pending teammates whose --after dependencies are satisfied. Use --watch to keep draining the DAG as teammates finish and as new tasks are added mid-flight.')
|
|
1142
1173
|
.option('--json', 'Output machine-readable JSON')
|
|
1143
1174
|
.option('--watch', 'Keep running: poll every --interval seconds, fire new waves, exit when the DAG drains.')
|
|
1144
1175
|
.option('--interval <seconds>', 'Seconds between waves in --watch mode (default 8)', '8')
|
|
1145
1176
|
.option('--max-waves <n>', 'Safety cap on waves in --watch mode (default 1000)', '1000')
|
|
1177
|
+
.option('--force', "Skip the advisory 'may not be signed in' warning for staged teammates (detection is unreliable)")
|
|
1146
1178
|
.action(async (team, opts) => {
|
|
1147
1179
|
const mgr = mkManager();
|
|
1148
1180
|
wireCloudDispatcher(mgr);
|
|
@@ -1152,6 +1184,8 @@ export function registerTeamsCommands(program) {
|
|
|
1152
1184
|
return;
|
|
1153
1185
|
team = picked;
|
|
1154
1186
|
}
|
|
1187
|
+
if (!opts.force && !isJsonMode(opts))
|
|
1188
|
+
await warnUnsignedTeammates(mgr, team);
|
|
1155
1189
|
if (!opts.watch) {
|
|
1156
1190
|
await runOneWave(mgr, team, Boolean(opts.json));
|
|
1157
1191
|
return;
|
|
@@ -1190,8 +1224,7 @@ export function registerTeamsCommands(program) {
|
|
|
1190
1224
|
}
|
|
1191
1225
|
});
|
|
1192
1226
|
// stop
|
|
1193
|
-
teams
|
|
1194
|
-
.command('stop [team] [teammate]')
|
|
1227
|
+
addHostOption(teams.command('stop [team] [teammate]'))
|
|
1195
1228
|
.description('Stop a running teammate. Can be restarted later. Cleans up worktree if no uncommitted changes.')
|
|
1196
1229
|
.option('--json', 'Output machine-readable JSON')
|
|
1197
1230
|
.action(async (team, ref, opts) => {
|
|
@@ -1451,19 +1484,45 @@ export function registerTeamsCommands(program) {
|
|
|
1451
1484
|
teams
|
|
1452
1485
|
.command('doctor')
|
|
1453
1486
|
.alias('dr')
|
|
1454
|
-
.description('Check which agents are installed and available to join a team. Verifies CLI paths.')
|
|
1487
|
+
.description('Check which agents are installed and available to join a team. Verifies CLI paths and shows an advisory sign-in hint.')
|
|
1455
1488
|
.option('--json', 'Output machine-readable JSON')
|
|
1456
1489
|
.action(async (opts) => {
|
|
1457
1490
|
const info = checkAllClis();
|
|
1491
|
+
// Advisory enrichment only. Sign-in detection is UNRELIABLE, so it never
|
|
1492
|
+
// changes the authoritative installed/ready column — it annotates. And an
|
|
1493
|
+
// agent that is actually running in a team is treated as signed in
|
|
1494
|
+
// regardless of the probe, so doctor never reports a working agent as
|
|
1495
|
+
// logged out ("don't show wrong stuff").
|
|
1496
|
+
const running = new Set();
|
|
1497
|
+
try {
|
|
1498
|
+
for (const a of await mkManager().listRunning())
|
|
1499
|
+
running.add(a.agentType);
|
|
1500
|
+
}
|
|
1501
|
+
catch { /* no teams yet — leave running empty */ }
|
|
1502
|
+
const auth = {};
|
|
1503
|
+
await Promise.all(Object.entries(info).map(async ([name, entry]) => {
|
|
1504
|
+
const isRunning = running.has(name);
|
|
1505
|
+
const probe = entry.installed && !isRunning ? await checkCliSignedIn(name) : false;
|
|
1506
|
+
auth[name] = resolveSignInAdvisory(entry.installed, isRunning, probe);
|
|
1507
|
+
}));
|
|
1458
1508
|
if (isJsonMode(opts)) {
|
|
1459
|
-
|
|
1509
|
+
const merged = {};
|
|
1510
|
+
for (const [name, entry] of Object.entries(info))
|
|
1511
|
+
merged[name] = { ...entry, ...auth[name] };
|
|
1512
|
+
console.log(JSON.stringify(merged, null, 2));
|
|
1460
1513
|
return;
|
|
1461
1514
|
}
|
|
1462
1515
|
console.log(chalk.bold('Who can join a team:'));
|
|
1463
1516
|
for (const [name, entry] of Object.entries(info)) {
|
|
1464
1517
|
const pretty = AGENT_NAMES[name] || name;
|
|
1465
1518
|
if (entry.installed) {
|
|
1466
|
-
|
|
1519
|
+
const { signedIn, running: isRunning } = auth[name];
|
|
1520
|
+
const hint = isRunning
|
|
1521
|
+
? chalk.gray('in use')
|
|
1522
|
+
: signedIn
|
|
1523
|
+
? chalk.gray('signed in')
|
|
1524
|
+
: chalk.gray('sign-in unverified');
|
|
1525
|
+
console.log(` ${chalk.green('ready')} ${pretty.padEnd(10)} ${chalk.gray(entry.path || '')} ${hint}`);
|
|
1467
1526
|
}
|
|
1468
1527
|
else {
|
|
1469
1528
|
console.log(` ${chalk.red('no ')} ${pretty.padEnd(10)} ${chalk.gray(entry.error || 'not installed')}`);
|
package/dist/commands/usage.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addHostOption } from '../lib/hosts/option.js';
|
|
1
2
|
import chalk from 'chalk';
|
|
2
3
|
import { ALL_AGENT_IDS, AGENTS, getAccountInfo, agentLabel, resolveAgentName, formatAgentError, } from '../lib/agents.js';
|
|
3
4
|
import { listInstalledVersions, getGlobalDefault, getVersionHomePath } from '../lib/versions.js';
|
|
@@ -5,8 +6,7 @@ import { formatUsageSection, getUsageInfoForIdentity } from '../lib/usage.js';
|
|
|
5
6
|
/** Agents whose CLI surfaces usage data we can read today. */
|
|
6
7
|
const USAGE_SUPPORTED = new Set(['claude', 'codex']);
|
|
7
8
|
export function registerUsageCommand(program) {
|
|
8
|
-
program
|
|
9
|
-
.command('usage [agent]')
|
|
9
|
+
addHostOption(program.command('usage [agent]'))
|
|
10
10
|
.description('Show rate-limit / quota usage per agent')
|
|
11
11
|
.addHelpText('after', `
|
|
12
12
|
Examples:
|
package/dist/commands/utils.d.ts
CHANGED
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentId } from '../lib/types.js';
|
|
8
8
|
import { type InstalledAgentTargetResult, type VersionSelectionResult } from '../lib/versions.js';
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a read/list command's `@version` filter through the agent-spec engine,
|
|
11
|
+
* exiting cleanly on a bad spec. Drop-in for the old
|
|
12
|
+
* `resolveVersionAlias(agent, parts[1])`:
|
|
13
|
+
* undefined → show all · @default/@pinned → the default version ·
|
|
14
|
+
* @latest/@oldest/@x.y.z → concrete.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveListFilterOrExit(agent: AgentId, qualifier: string | undefined | null): string | undefined;
|
|
9
17
|
/**
|
|
10
18
|
* Check if an error is from user cancelling a prompt (Ctrl+C)
|
|
11
19
|
*/
|
package/dist/commands/utils.js
CHANGED
|
@@ -11,6 +11,26 @@ import ora from 'ora';
|
|
|
11
11
|
import { confirm } from '@inquirer/prompts';
|
|
12
12
|
import { agentLabel, resolveAgentName } from '../lib/agents.js';
|
|
13
13
|
import { installVersion, listInstalledVersions, resolveAgentVersionTargets, resolveInstalledAgentTargets, } from '../lib/versions.js';
|
|
14
|
+
import { resolveListFilter, AgentSpecError } from '../lib/agent-spec/index.js';
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a read/list command's `@version` filter through the agent-spec engine,
|
|
17
|
+
* exiting cleanly on a bad spec. Drop-in for the old
|
|
18
|
+
* `resolveVersionAlias(agent, parts[1])`:
|
|
19
|
+
* undefined → show all · @default/@pinned → the default version ·
|
|
20
|
+
* @latest/@oldest/@x.y.z → concrete.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveListFilterOrExit(agent, qualifier) {
|
|
23
|
+
try {
|
|
24
|
+
return resolveListFilter(agent, qualifier);
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
if (e instanceof AgentSpecError) {
|
|
28
|
+
console.error(chalk.red(e.message));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
throw e;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
14
34
|
/**
|
|
15
35
|
* Check if an error is from user cancelling a prompt (Ctrl+C)
|
|
16
36
|
*/
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { addHostOption } from '../lib/hosts/option.js';
|
|
1
2
|
import chalk from 'chalk';
|
|
2
3
|
import ora from 'ora';
|
|
3
4
|
import * as fs from 'fs';
|
|
4
5
|
import * as path from 'path';
|
|
5
6
|
import { select, confirm, checkbox } from '@inquirer/prompts';
|
|
6
|
-
import { AGENTS, ALL_AGENT_IDS, getAccountEmail, getAccountInfo, agentLabel, } from '../lib/agents.js';
|
|
7
|
+
import { AGENTS, ALL_AGENT_IDS, getAccountEmail, getAccountInfo, agentLabel, warnAgentDeprecated, } from '../lib/agents.js';
|
|
7
8
|
import { formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
|
|
8
9
|
import { viewAction } from './view.js';
|
|
9
10
|
import { readManifest, writeManifest, createDefaultManifest } from '../lib/manifest.js';
|
|
@@ -272,6 +273,7 @@ export function registerVersionsCommands(program) {
|
|
|
272
273
|
}
|
|
273
274
|
const { agent, version } = parsed;
|
|
274
275
|
const agentConfig = AGENTS[agent];
|
|
276
|
+
warnAgentDeprecated(agent);
|
|
275
277
|
if (!agentConfig.npmPackage && !agentConfig.installScript) {
|
|
276
278
|
console.log(chalk.yellow(`${agentLabel(agentConfig.id)} has no npm package. Install manually.`));
|
|
277
279
|
continue;
|
|
@@ -762,8 +764,7 @@ export function registerVersionsCommands(program) {
|
|
|
762
764
|
}
|
|
763
765
|
});
|
|
764
766
|
// Deprecated: use `agents view` instead
|
|
765
|
-
program
|
|
766
|
-
.command('list [agent]')
|
|
767
|
+
addHostOption(program.command('list [agent]'))
|
|
767
768
|
.description('List installed agent CLI versions')
|
|
768
769
|
.action(async (agentArg) => {
|
|
769
770
|
console.log(chalk.red('Deprecated: "agents list" is now "agents view"\n'));
|