@phnx-labs/agents-cli 1.20.78 → 1.20.83
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 +595 -0
- package/README.md +26 -26
- package/dist/bin/agents +0 -0
- package/dist/commands/activity.d.ts +9 -0
- package/dist/commands/activity.js +153 -24
- package/dist/commands/apply.js +11 -4
- package/dist/commands/cli.js +1 -1
- package/dist/commands/commands.d.ts +1 -1
- package/dist/commands/commands.js +7 -6
- package/dist/commands/defaults.js +3 -11
- package/dist/commands/doctor.d.ts +66 -0
- package/dist/commands/doctor.js +596 -33
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.js +24 -5
- package/dist/commands/feed.d.ts +4 -0
- package/dist/commands/feed.js +87 -11
- package/dist/commands/harness.js +2 -2
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +8 -4
- package/dist/commands/logs.js +6 -0
- package/dist/commands/menubar.js +12 -0
- package/dist/commands/models.js +1 -1
- package/dist/commands/packages.js +6 -6
- package/dist/commands/permissions.js +1 -1
- package/dist/commands/profiles.js +1 -1
- package/dist/commands/pull.d.ts +1 -1
- package/dist/commands/pull.js +4 -4
- package/dist/commands/repo.d.ts +78 -0
- package/dist/commands/repo.js +187 -11
- package/dist/commands/routines.d.ts +8 -1
- package/dist/commands/routines.js +241 -77
- package/dist/commands/rules.js +13 -12
- package/dist/commands/secrets.d.ts +3 -1
- package/dist/commands/secrets.js +100 -32
- package/dist/commands/sessions-inject.d.ts +7 -4
- package/dist/commands/sessions-inject.js +18 -18
- package/dist/commands/sessions-picker.js +32 -3
- package/dist/commands/sessions-resume.d.ts +16 -0
- package/dist/commands/sessions-resume.js +14 -13
- package/dist/commands/sessions.d.ts +115 -0
- package/dist/commands/sessions.js +189 -35
- package/dist/commands/set.d.ts +15 -0
- package/dist/commands/set.js +79 -0
- package/dist/commands/setup-fleet.d.ts +21 -0
- package/dist/commands/setup-fleet.js +201 -0
- package/dist/commands/setup-secrets.d.ts +20 -0
- package/dist/commands/setup-secrets.js +223 -0
- package/dist/commands/setup.js +17 -3
- package/dist/commands/skills.js +1 -1
- package/dist/commands/sync.js +7 -2
- package/dist/commands/teams-picker.js +0 -1
- package/dist/commands/teams.d.ts +33 -1
- package/dist/commands/teams.js +208 -44
- package/dist/commands/versions.js +10 -5
- package/dist/commands/view.d.ts +1 -0
- package/dist/commands/view.js +11 -0
- package/dist/index.js +52 -8
- package/dist/lib/acp/harnesses.js +0 -7
- package/dist/lib/activity.d.ts +156 -0
- package/dist/lib/activity.js +282 -0
- package/dist/lib/agents.d.ts +9 -2
- package/dist/lib/agents.js +72 -63
- package/dist/lib/browser/service.js +3 -0
- package/dist/lib/browser/types.d.ts +7 -0
- package/dist/lib/capabilities.js +6 -2
- package/dist/lib/crabbox/lease.js +2 -2
- package/dist/lib/crabbox/setup-copy.d.ts +4 -4
- package/dist/lib/crabbox/setup-copy.js +4 -4
- package/dist/lib/daemon.d.ts +18 -0
- package/dist/lib/daemon.js +47 -6
- package/dist/lib/devices/fleet.d.ts +1 -1
- package/dist/lib/devices/fleet.js +1 -1
- package/dist/lib/doctor-diff.d.ts +20 -0
- package/dist/lib/doctor-diff.js +6 -1
- package/dist/lib/drift.d.ts +31 -11
- package/dist/lib/drift.js +58 -7
- package/dist/lib/events.d.ts +8 -1
- package/dist/lib/events.js +10 -1
- package/dist/lib/exec.d.ts +7 -0
- package/dist/lib/exec.js +69 -11
- package/dist/lib/feed-post.d.ts +28 -1
- package/dist/lib/feed-post.js +110 -2
- package/dist/lib/fleet/apply.js +8 -1
- package/dist/lib/fleet/auth-sync.d.ts +18 -1
- package/dist/lib/fleet/auth-sync.js +25 -11
- package/dist/lib/fleet/remote-login.js +5 -0
- package/dist/lib/git.d.ts +14 -14
- package/dist/lib/git.js +41 -44
- package/dist/lib/hooks.d.ts +71 -0
- package/dist/lib/hooks.js +245 -95
- package/dist/lib/hosts/dispatch.d.ts +28 -1
- package/dist/lib/hosts/dispatch.js +33 -5
- package/dist/lib/hosts/option.js +2 -2
- package/dist/lib/hosts/passthrough.d.ts +22 -1
- package/dist/lib/hosts/passthrough.js +238 -13
- package/dist/lib/hosts/run-target.d.ts +2 -0
- package/dist/lib/hosts/run-target.js +1 -0
- package/dist/lib/hq/floor.d.ts +1 -1
- package/dist/lib/hq/floor.js +6 -1
- package/dist/lib/mcp.js +3 -71
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +24 -0
- package/dist/lib/menubar/install-menubar.js +52 -2
- package/dist/lib/menubar/notify-desktop.d.ts +15 -0
- package/dist/lib/menubar/notify-desktop.js +41 -5
- package/dist/lib/merged-resources.d.ts +11 -0
- package/dist/{commands/resources.js → lib/merged-resources.js} +11 -19
- package/dist/lib/permissions.d.ts +0 -32
- package/dist/lib/permissions.js +4 -157
- package/dist/lib/picker.js +1 -1
- package/dist/lib/platform/process.d.ts +16 -5
- package/dist/lib/platform/process.js +54 -0
- package/dist/lib/plugins.d.ts +0 -8
- package/dist/lib/plugins.js +4 -110
- package/dist/lib/project-resources.js +4 -1
- package/dist/lib/refresh.d.ts +5 -3
- package/dist/lib/refresh.js +7 -5
- package/dist/lib/resources/commands.js +4 -1
- package/dist/lib/resources/mcp.js +0 -4
- package/dist/lib/resources/permissions.d.ts +1 -1
- package/dist/lib/resources/permissions.js +1 -5
- package/dist/lib/resources/rules.js +4 -1
- package/dist/lib/resources/skills.js +4 -1
- package/dist/lib/resources/subagents.js +4 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/routines.d.ts +33 -0
- package/dist/lib/routines.js +122 -0
- package/dist/lib/run-defaults.d.ts +1 -0
- package/dist/lib/run-defaults.js +9 -0
- package/dist/lib/runner.d.ts +0 -12
- package/dist/lib/runner.js +49 -10
- package/dist/lib/scheduler.js +16 -4
- 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 +16 -0
- package/dist/lib/secrets/agent.js +105 -3
- package/dist/lib/secrets/audit.d.ts +46 -0
- package/dist/lib/secrets/audit.js +56 -0
- package/dist/lib/secrets/bundles.d.ts +11 -8
- package/dist/lib/secrets/bundles.js +31 -23
- package/dist/lib/secrets/index.d.ts +2 -1
- package/dist/lib/secrets/index.js +8 -2
- package/dist/lib/secrets/remote.d.ts +75 -2
- package/dist/lib/secrets/remote.js +139 -5
- package/dist/lib/secrets/sync.js +3 -3
- package/dist/lib/session/active.d.ts +128 -26
- package/dist/lib/session/active.js +192 -53
- package/dist/lib/session/actor-sidecar.d.ts +23 -0
- package/dist/lib/session/actor-sidecar.js +101 -0
- package/dist/lib/session/db.d.ts +4 -1
- package/dist/lib/session/db.js +65 -8
- package/dist/lib/session/discover.d.ts +4 -0
- package/dist/lib/session/discover.js +13 -0
- package/dist/lib/session/hook-sessions.d.ts +9 -0
- package/dist/lib/session/hook-sessions.js +57 -8
- package/dist/lib/session/pid-registry.d.ts +13 -0
- package/dist/lib/session/render.d.ts +2 -0
- package/dist/lib/session/render.js +1 -1
- package/dist/lib/session/types.d.ts +16 -0
- package/dist/lib/staleness/detectors/commands.js +2 -2
- package/dist/lib/staleness/detectors/permissions.js +1 -45
- package/dist/lib/staleness/writers/commands.js +4 -5
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +1 -2
- package/dist/lib/startup/command-registry.js +2 -4
- package/dist/lib/state.js +57 -1
- package/dist/lib/subagents-registry.js +5 -12
- package/dist/lib/subagents.d.ts +0 -10
- package/dist/lib/subagents.js +0 -12
- package/dist/lib/teams/agents.d.ts +0 -2
- package/dist/lib/teams/agents.js +10 -27
- package/dist/lib/teams/index.d.ts +1 -1
- package/dist/lib/teams/index.js +1 -1
- package/dist/lib/types.d.ts +17 -9
- package/dist/lib/usage.d.ts +28 -5
- package/dist/lib/usage.js +271 -8
- package/dist/lib/versions.js +7 -1
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdog.js +25 -13
- package/package.json +1 -2
- package/dist/commands/check.d.ts +0 -15
- package/dist/commands/check.js +0 -180
- package/dist/commands/resources.d.ts +0 -5
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents set` — ergonomic top-level setter for per-agent/version run defaults.
|
|
3
|
+
*
|
|
4
|
+
* `agents set claude@2.1.220 --model opus-5` pins the default model (and/or
|
|
5
|
+
* mode) that `agents run` uses for that agent version. It reads and writes the
|
|
6
|
+
* same store as `agents defaults run set` (agents.yaml -> run.defaults), so the
|
|
7
|
+
* two stay consistent — `set` is just the short front door.
|
|
8
|
+
*
|
|
9
|
+
* agents set # list every configured default
|
|
10
|
+
* agents set claude@2.1.220 # show the default for one version
|
|
11
|
+
* agents set claude@2.1.220 --model opus-5
|
|
12
|
+
* agents set 'claude:*' --mode auto --model opus
|
|
13
|
+
*/
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import { setHelpSections } from '../lib/help.js';
|
|
16
|
+
import { formatRunDefaultEntry, listRunDefaults, parseRunDefaultSelector, setRunDefault, } from '../lib/run-defaults.js';
|
|
17
|
+
export function registerSetCommand(program) {
|
|
18
|
+
const set = program
|
|
19
|
+
.command('set [selector]')
|
|
20
|
+
.description('Set the default model/mode an agent version uses for `agents run`')
|
|
21
|
+
.option('--model <model>', 'Default model or model alias, forwarded via --model')
|
|
22
|
+
.option('--mode <mode>', "Default mode: plan, edit, auto, skip. 'full' accepted as alias for skip.")
|
|
23
|
+
.action((selector, options) => {
|
|
24
|
+
try {
|
|
25
|
+
const hasFlags = options.model !== undefined || options.mode !== undefined;
|
|
26
|
+
if (!selector) {
|
|
27
|
+
if (hasFlags) {
|
|
28
|
+
throw new Error('Selector is required when passing --model/--mode. Example: agents set claude@2.1.220 --model opus-5');
|
|
29
|
+
}
|
|
30
|
+
const entries = listRunDefaults();
|
|
31
|
+
if (entries.length === 0) {
|
|
32
|
+
console.log(chalk.gray('No agent defaults configured.'));
|
|
33
|
+
console.log(chalk.gray('Set one with: agents set claude@2.1.220 --model opus-5'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(chalk.bold('Agent Defaults\n'));
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
console.log(` ${formatRunDefaultEntry(entry)}`);
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!hasFlags) {
|
|
43
|
+
const parsed = parseRunDefaultSelector(selector);
|
|
44
|
+
const entry = listRunDefaults().find((e) => e.selector === parsed.selector);
|
|
45
|
+
if (!entry || (!entry.defaults.mode && !entry.defaults.model)) {
|
|
46
|
+
console.log(chalk.gray(`No default set for ${parsed.selector}.`));
|
|
47
|
+
console.log(chalk.gray(`Set one with: agents set ${selector} --model <model>`));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
console.log(` ${formatRunDefaultEntry(entry)}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const entry = setRunDefault(selector, {
|
|
54
|
+
...(options.mode !== undefined ? { mode: options.mode } : {}),
|
|
55
|
+
...(options.model !== undefined ? { model: options.model } : {}),
|
|
56
|
+
});
|
|
57
|
+
console.log(chalk.green('Set default:'));
|
|
58
|
+
console.log(` ${formatRunDefaultEntry(entry)}`);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error(chalk.red(err.message));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
setHelpSections(set, {
|
|
66
|
+
examples: `
|
|
67
|
+
agents set claude@2.1.220 --model opus-5
|
|
68
|
+
agents set 'claude:*' --mode auto --model opus
|
|
69
|
+
agents set claude@2.1.220
|
|
70
|
+
agents set
|
|
71
|
+
`,
|
|
72
|
+
notes: `
|
|
73
|
+
Selectors use <agent>@<version> or <agent>:<version>; * matches all versions.
|
|
74
|
+
Exact selectors override wildcard selectors field by field.
|
|
75
|
+
Writes the same store as 'agents defaults run set'. Explicit flags on
|
|
76
|
+
'agents run' always win over configured defaults.
|
|
77
|
+
`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents setup fleet` — guided Tailscale device onboarding.
|
|
3
|
+
*
|
|
4
|
+
* This wizard is a front door over the existing fleet/device commands: sync
|
|
5
|
+
* Tailscale into the registry, choose auth, render SSH config, test devices, and
|
|
6
|
+
* optionally run the fleet updater without reimplementing those subcommands.
|
|
7
|
+
*/
|
|
8
|
+
import type { Command } from 'commander';
|
|
9
|
+
interface SetupFleetOptions {
|
|
10
|
+
auth?: string;
|
|
11
|
+
bundle?: string;
|
|
12
|
+
bundleKey?: string;
|
|
13
|
+
yes?: boolean;
|
|
14
|
+
writeSshConfig?: boolean;
|
|
15
|
+
testConnectivity?: boolean;
|
|
16
|
+
fleetUpdate?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function runFleetSetupWizard(opts?: SetupFleetOptions): Promise<boolean>;
|
|
19
|
+
/** Register `agents setup fleet` under the parent `setup` command. */
|
|
20
|
+
export declare function registerSetupFleetCommand(setupCmd: Command): void;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents setup fleet` — guided Tailscale device onboarding.
|
|
3
|
+
*
|
|
4
|
+
* This wizard is a front door over the existing fleet/device commands: sync
|
|
5
|
+
* Tailscale into the registry, choose auth, render SSH config, test devices, and
|
|
6
|
+
* optionally run the fleet updater without reimplementing those subcommands.
|
|
7
|
+
*/
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { spawnSync } from 'node:child_process';
|
|
10
|
+
import { getCliLaunch } from '../lib/cli-entry.js';
|
|
11
|
+
import { loadDevices } from '../lib/devices/registry.js';
|
|
12
|
+
import { runDeviceSync } from '../lib/devices/sync.js';
|
|
13
|
+
import { tailscaleStatusJson } from '../lib/devices/tailscale.js';
|
|
14
|
+
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
15
|
+
function parseAuth(raw) {
|
|
16
|
+
const value = (raw ?? 'key').toLowerCase();
|
|
17
|
+
if (value === 'key' || value === 'password')
|
|
18
|
+
return value;
|
|
19
|
+
throw new Error(`Invalid --auth '${raw}'. Use key or password.`);
|
|
20
|
+
}
|
|
21
|
+
function runAgentsSubcommand(args, opts = {}) {
|
|
22
|
+
const launch = getCliLaunch(args);
|
|
23
|
+
const res = spawnSync(launch.command, launch.args, { stdio: 'inherit', env: process.env });
|
|
24
|
+
if ((res.status ?? 1) === 0)
|
|
25
|
+
return true;
|
|
26
|
+
if (opts.optional)
|
|
27
|
+
return false;
|
|
28
|
+
throw new Error(`agents ${args.join(' ')} failed with exit ${res.status ?? 1}.`);
|
|
29
|
+
}
|
|
30
|
+
function commandExists(command) {
|
|
31
|
+
const res = spawnSync(command, ['version'], { stdio: 'ignore', windowsHide: true });
|
|
32
|
+
if (!res.error)
|
|
33
|
+
return true;
|
|
34
|
+
const fallback = spawnSync(command, ['--version'], { stdio: 'ignore', windowsHide: true });
|
|
35
|
+
return !fallback.error;
|
|
36
|
+
}
|
|
37
|
+
function tailscaleStatusOk() {
|
|
38
|
+
try {
|
|
39
|
+
tailscaleStatusJson();
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function printTailscaleInstallInstructions() {
|
|
47
|
+
console.error(chalk.red('Tailscale is not installed or not on PATH.'));
|
|
48
|
+
const platform = process.platform;
|
|
49
|
+
console.log(chalk.bold('\nInstall Tailscale, then re-run `agents setup fleet`:'));
|
|
50
|
+
if (platform === 'darwin') {
|
|
51
|
+
console.log(chalk.cyan(' brew install --cask tailscale'));
|
|
52
|
+
console.log(chalk.gray(' or install from https://tailscale.com/download/mac'));
|
|
53
|
+
}
|
|
54
|
+
else if (platform === 'linux') {
|
|
55
|
+
console.log(chalk.cyan(' curl -fsSL https://tailscale.com/install.sh | sh'));
|
|
56
|
+
console.log(chalk.gray(' then run: sudo tailscale up'));
|
|
57
|
+
}
|
|
58
|
+
else if (platform === 'win32') {
|
|
59
|
+
console.log(chalk.cyan(' winget install Tailscale.Tailscale'));
|
|
60
|
+
console.log(chalk.gray(' or install from https://tailscale.com/download/windows'));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.log(chalk.gray(' https://tailscale.com/download'));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function resolveInteractiveChoices(opts) {
|
|
67
|
+
if (!isInteractiveTerminal()) {
|
|
68
|
+
return {
|
|
69
|
+
auth: parseAuth(opts.auth),
|
|
70
|
+
writeSshConfig: Boolean(opts.writeSshConfig),
|
|
71
|
+
testConnectivity: Boolean(opts.testConnectivity),
|
|
72
|
+
fleetUpdate: Boolean(opts.fleetUpdate),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const { confirm, select } = await import('@inquirer/prompts');
|
|
76
|
+
const auth = opts.auth ? parseAuth(opts.auth) : await select({
|
|
77
|
+
message: 'Default SSH auth for registered devices',
|
|
78
|
+
default: 'key',
|
|
79
|
+
choices: [
|
|
80
|
+
{ name: 'key — use your SSH agent / on-disk public keys', value: 'key' },
|
|
81
|
+
{ name: 'password — read the login password from an agents secrets bundle', value: 'password' },
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
auth,
|
|
86
|
+
writeSshConfig: opts.writeSshConfig ?? await confirm({
|
|
87
|
+
message: 'Write ~/.ssh/config.d/agents for plain ssh/scp/rsync?',
|
|
88
|
+
default: true,
|
|
89
|
+
}),
|
|
90
|
+
testConnectivity: opts.testConnectivity ?? await confirm({
|
|
91
|
+
message: 'Test connectivity with `agents ssh <device> uname`?',
|
|
92
|
+
default: true,
|
|
93
|
+
}),
|
|
94
|
+
fleetUpdate: opts.fleetUpdate ?? await confirm({
|
|
95
|
+
message: 'Run `agents fleet update` on online devices now?',
|
|
96
|
+
default: false,
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function syncDevices(opts) {
|
|
101
|
+
if (isInteractiveTerminal() && !opts.yes) {
|
|
102
|
+
return runAgentsSubcommand(['devices', 'sync'], { optional: true });
|
|
103
|
+
}
|
|
104
|
+
const res = await runDeviceSync({ soft: true });
|
|
105
|
+
if (!res.ok) {
|
|
106
|
+
console.error(chalk.red(`Tailscale discovery failed: ${res.reason ?? 'unknown error'}`));
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const extra = res.pending.length ? chalk.gray(` (${res.pending.length} new)`) : '';
|
|
110
|
+
console.log(chalk.green(`Synced ${res.synced} device${res.synced === 1 ? '' : 's'} from Tailscale${extra}.`));
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
async function applyAuth(auth, opts) {
|
|
114
|
+
const reg = await loadDevices();
|
|
115
|
+
const names = Object.keys(reg).sort();
|
|
116
|
+
if (names.length === 0) {
|
|
117
|
+
console.log(chalk.gray("No devices registered yet. Run 'agents setup fleet --yes' after joining Tailscale."));
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
if (auth === 'password' && !opts.bundle) {
|
|
121
|
+
throw new Error('--bundle is required with --auth password.');
|
|
122
|
+
}
|
|
123
|
+
for (const name of names) {
|
|
124
|
+
const args = ['devices', 'set', name, '--auth', auth];
|
|
125
|
+
if (opts.bundle)
|
|
126
|
+
args.push('--bundle', opts.bundle);
|
|
127
|
+
if (opts.bundleKey)
|
|
128
|
+
args.push('--bundle-key', opts.bundleKey);
|
|
129
|
+
runAgentsSubcommand(args);
|
|
130
|
+
}
|
|
131
|
+
return names;
|
|
132
|
+
}
|
|
133
|
+
function testConnectivity(names) {
|
|
134
|
+
if (names.length === 0)
|
|
135
|
+
return;
|
|
136
|
+
console.log(chalk.bold('\nTesting connectivity:'));
|
|
137
|
+
for (const name of names) {
|
|
138
|
+
const ok = runAgentsSubcommand(['ssh', name, 'uname'], { optional: true });
|
|
139
|
+
console.log(ok ? chalk.green(` ${name}: ok`) : chalk.yellow(` ${name}: failed`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function printOnboardingSummary(names, auth) {
|
|
143
|
+
console.log(chalk.bold('\nFleet setup summary.'));
|
|
144
|
+
console.log(chalk.gray(`devices: ${names.length ? names.join(', ') : 'none registered'}`));
|
|
145
|
+
console.log(chalk.gray(`auth: ${auth}${auth === 'password' ? ' via secrets bundle' : ''}`));
|
|
146
|
+
console.log(chalk.bold('\nTry:'));
|
|
147
|
+
console.log(chalk.cyan(' agents devices list'));
|
|
148
|
+
console.log(chalk.cyan(' agents ssh <device> uname'));
|
|
149
|
+
console.log(chalk.cyan(' agents fleet run hostname'));
|
|
150
|
+
console.log(chalk.cyan(' agents fleet update'));
|
|
151
|
+
}
|
|
152
|
+
export async function runFleetSetupWizard(opts = {}) {
|
|
153
|
+
if (!commandExists('tailscale')) {
|
|
154
|
+
printTailscaleInstallInstructions();
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
if (!tailscaleStatusOk()) {
|
|
158
|
+
console.error(chalk.red('Tailscale is installed but not logged in or the daemon is unavailable.'));
|
|
159
|
+
console.log(chalk.gray('Run `tailscale login` (or `sudo tailscale up` on Linux), then re-run `agents setup fleet`.'));
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
const choices = await resolveInteractiveChoices(opts);
|
|
163
|
+
const synced = await syncDevices(opts);
|
|
164
|
+
if (!synced)
|
|
165
|
+
return false;
|
|
166
|
+
const names = await applyAuth(choices.auth, opts);
|
|
167
|
+
if (choices.writeSshConfig)
|
|
168
|
+
runAgentsSubcommand(['devices', 'render', '--write']);
|
|
169
|
+
if (choices.testConnectivity)
|
|
170
|
+
testConnectivity(names);
|
|
171
|
+
if (choices.fleetUpdate)
|
|
172
|
+
runAgentsSubcommand(['fleet', 'update']);
|
|
173
|
+
printOnboardingSummary(names, choices.auth);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
/** Register `agents setup fleet` under the parent `setup` command. */
|
|
177
|
+
export function registerSetupFleetCommand(setupCmd) {
|
|
178
|
+
setupCmd
|
|
179
|
+
.command('fleet')
|
|
180
|
+
.description('Set up `agents fleet` — discover Tailscale devices, choose auth, render SSH config, and test connectivity.')
|
|
181
|
+
.option('--auth <method>', 'device auth method: key or password', 'key')
|
|
182
|
+
.option('--bundle <name>', 'secrets bundle holding the login password when --auth password is used')
|
|
183
|
+
.option('--bundle-key <key>', "key within the secrets bundle (default 'password')")
|
|
184
|
+
.option('--yes', 'non-interactive: register every discovered, non-ignored Tailscale node')
|
|
185
|
+
.option('--write-ssh-config', 'write ~/.ssh/config.d/agents after syncing')
|
|
186
|
+
.option('--test-connectivity', 'run `agents ssh <device> uname` for each registered device')
|
|
187
|
+
.option('--fleet-update', 'run `agents fleet update` after setup')
|
|
188
|
+
.action(async (options) => {
|
|
189
|
+
try {
|
|
190
|
+
await runFleetSetupWizard(options);
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
if (isPromptCancelled(err)) {
|
|
194
|
+
console.log(chalk.yellow('\nCancelled'));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
console.error(chalk.red(err.message));
|
|
198
|
+
process.exitCode = 1;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents setup secrets` — guided defaults for `agents secrets` onboarding.
|
|
3
|
+
*
|
|
4
|
+
* The wizard writes setup preferences and delegates imports to the existing
|
|
5
|
+
* `agents secrets import` command, keeping bundle storage as the source of truth.
|
|
6
|
+
*/
|
|
7
|
+
import type { Command } from 'commander';
|
|
8
|
+
interface SetupSecretsOptions {
|
|
9
|
+
backend?: string;
|
|
10
|
+
policy?: string;
|
|
11
|
+
importFrom?: string;
|
|
12
|
+
bundle?: string;
|
|
13
|
+
vault?: string;
|
|
14
|
+
iUnderstand?: boolean;
|
|
15
|
+
force?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function runSecretsSetupWizard(opts?: SetupSecretsOptions): Promise<boolean>;
|
|
18
|
+
/** Register `agents setup secrets` under the parent `setup` command. */
|
|
19
|
+
export declare function registerSetupSecretsCommand(setupCmd: Command): void;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents setup secrets` — guided defaults for `agents secrets` onboarding.
|
|
3
|
+
*
|
|
4
|
+
* The wizard writes setup preferences and delegates imports to the existing
|
|
5
|
+
* `agents secrets import` command, keeping bundle storage as the source of truth.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { spawnSync } from 'node:child_process';
|
|
11
|
+
import { getCliLaunch } from '../lib/cli-entry.js';
|
|
12
|
+
import { getHistoryDir, updateMeta } from '../lib/state.js';
|
|
13
|
+
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
14
|
+
function defaultBackendForPlatform(platform = process.platform) {
|
|
15
|
+
return platform === 'darwin' ? 'keychain' : 'file';
|
|
16
|
+
}
|
|
17
|
+
function setupSecretsPrefsPath() {
|
|
18
|
+
return path.join(getHistoryDir(), 'setup', 'secrets.json');
|
|
19
|
+
}
|
|
20
|
+
function parseBackend(raw) {
|
|
21
|
+
const value = (raw ?? defaultBackendForPlatform()).toLowerCase();
|
|
22
|
+
if (value === 'keychain' || value === 'file' || value === 'vault')
|
|
23
|
+
return value;
|
|
24
|
+
throw new Error(`Invalid --backend '${raw}'. Use keychain, file, or vault.`);
|
|
25
|
+
}
|
|
26
|
+
function parsePolicy(raw) {
|
|
27
|
+
const value = (raw ?? 'daily').toLowerCase();
|
|
28
|
+
if (value === 'daily' || value === 'always' || value === 'never')
|
|
29
|
+
return value;
|
|
30
|
+
throw new Error(`Invalid --policy '${raw}'. Use daily, always, or never.`);
|
|
31
|
+
}
|
|
32
|
+
function parseImportSource(raw) {
|
|
33
|
+
const value = (raw ?? 'none').toLowerCase();
|
|
34
|
+
if (value === 'none' || value === 'env' || value === '1password' || value === 'icloud')
|
|
35
|
+
return value;
|
|
36
|
+
throw new Error(`Invalid --import-from '${raw}'. Use none, env, 1password, or icloud.`);
|
|
37
|
+
}
|
|
38
|
+
function hasCommand(command) {
|
|
39
|
+
const res = spawnSync(command, ['--version'], { stdio: 'ignore', windowsHide: true });
|
|
40
|
+
return !res.error;
|
|
41
|
+
}
|
|
42
|
+
function saveSetupSecretsPrefs(prefs) {
|
|
43
|
+
const file = setupSecretsPrefsPath();
|
|
44
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
45
|
+
fs.writeFileSync(file, JSON.stringify(prefs, null, 2) + '\n', { mode: 0o600 });
|
|
46
|
+
return file;
|
|
47
|
+
}
|
|
48
|
+
function applySecretsDefaults(backend, policy) {
|
|
49
|
+
updateMeta((meta) => ({
|
|
50
|
+
...meta,
|
|
51
|
+
secrets: {
|
|
52
|
+
...meta.secrets,
|
|
53
|
+
backend,
|
|
54
|
+
...(policy === 'never' ? {} : { policy }),
|
|
55
|
+
},
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
function backendArgs(backend) {
|
|
59
|
+
return backend === 'vault' ? ['--synced'] : ['--backend', backend];
|
|
60
|
+
}
|
|
61
|
+
function printBackendNotes(backend) {
|
|
62
|
+
if (backend === 'keychain') {
|
|
63
|
+
console.log(chalk.gray('backend: keychain — macOS reads may ask for Touch ID or the device password.'));
|
|
64
|
+
}
|
|
65
|
+
else if (backend === 'file') {
|
|
66
|
+
console.log(chalk.gray('backend: file — set AGENTS_SECRETS_PASSPHRASE for headless encrypted-file reads.'));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(chalk.gray('backend: vault — synced ~/.agents/vault.age storage; unlock it with agents login.'));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function runAgentsSubcommand(args) {
|
|
73
|
+
const launch = getCliLaunch(args);
|
|
74
|
+
const res = spawnSync(launch.command, launch.args, { stdio: 'inherit', env: process.env });
|
|
75
|
+
if ((res.status ?? 1) !== 0) {
|
|
76
|
+
throw new Error(`agents ${args.join(' ')} failed with exit ${res.status ?? 1}.`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function maybeImportSecrets(source, opts) {
|
|
80
|
+
if (source === 'none')
|
|
81
|
+
return;
|
|
82
|
+
let bundle = opts.bundle;
|
|
83
|
+
if (!bundle) {
|
|
84
|
+
if (!isInteractiveTerminal()) {
|
|
85
|
+
throw new Error(`--bundle is required with --import-from ${source} in a non-interactive shell.`);
|
|
86
|
+
}
|
|
87
|
+
const { input } = await import('@inquirer/prompts');
|
|
88
|
+
bundle = await input({
|
|
89
|
+
message: 'Import into which bundle?',
|
|
90
|
+
default: 'default',
|
|
91
|
+
validate: (v) => (v.trim().length > 0 ? true : 'Enter a bundle name.'),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const args = ['secrets', 'import', bundle, ...backendArgs(opts.backend)];
|
|
95
|
+
if (opts.force)
|
|
96
|
+
args.push('--force');
|
|
97
|
+
if (source === 'env') {
|
|
98
|
+
let envPath = opts.vault;
|
|
99
|
+
if (!envPath) {
|
|
100
|
+
if (!isInteractiveTerminal()) {
|
|
101
|
+
throw new Error('--vault must name the .env path when --import-from env is used non-interactively.');
|
|
102
|
+
}
|
|
103
|
+
const { input } = await import('@inquirer/prompts');
|
|
104
|
+
envPath = await input({
|
|
105
|
+
message: '.env file path',
|
|
106
|
+
default: '.env',
|
|
107
|
+
validate: (v) => (v.trim().length > 0 ? true : 'Enter a .env path.'),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
args.push('--from', envPath);
|
|
111
|
+
}
|
|
112
|
+
else if (source === '1password') {
|
|
113
|
+
const vault = opts.vault;
|
|
114
|
+
if (!vault)
|
|
115
|
+
throw new Error('--vault <name> is required with --import-from 1password.');
|
|
116
|
+
args.push('--from', `1password:${vault}`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
args.push('--from', 'icloud');
|
|
120
|
+
}
|
|
121
|
+
runAgentsSubcommand(args);
|
|
122
|
+
}
|
|
123
|
+
async function resolveInteractiveChoices(opts) {
|
|
124
|
+
if (!isInteractiveTerminal()) {
|
|
125
|
+
return {
|
|
126
|
+
backend: parseBackend(opts.backend),
|
|
127
|
+
policy: parsePolicy(opts.policy),
|
|
128
|
+
importSource: parseImportSource(opts.importFrom),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const { select } = await import('@inquirer/prompts');
|
|
132
|
+
const backend = opts.backend ? parseBackend(opts.backend) : await select({
|
|
133
|
+
message: 'Default secrets backend',
|
|
134
|
+
default: defaultBackendForPlatform(),
|
|
135
|
+
choices: [
|
|
136
|
+
{ name: 'keychain — local OS keychain; reads may ask for Touch ID/device password', value: 'keychain' },
|
|
137
|
+
{ name: 'file — passphrase-encrypted file store for headless machines', value: 'file' },
|
|
138
|
+
{ name: 'vault — synced ~/.agents/vault.age file; requires agents login', value: 'vault' },
|
|
139
|
+
],
|
|
140
|
+
});
|
|
141
|
+
const policy = opts.policy ? parsePolicy(opts.policy) : await select({
|
|
142
|
+
message: 'Default prompt policy',
|
|
143
|
+
default: 'daily',
|
|
144
|
+
choices: [
|
|
145
|
+
{ name: 'daily — ask once, then hold for the configured window', value: 'daily' },
|
|
146
|
+
{ name: 'always — ask on every read', value: 'always' },
|
|
147
|
+
{ name: 'never — no biometry ACL; use only for automation-only bundles', value: 'never' },
|
|
148
|
+
],
|
|
149
|
+
});
|
|
150
|
+
const importChoices = [
|
|
151
|
+
{ name: 'skip import', value: 'none' },
|
|
152
|
+
{ name: 'import from .env file', value: 'env' },
|
|
153
|
+
...(hasCommand('op') ? [{ name: 'import from 1Password vault', value: '1password' }] : []),
|
|
154
|
+
{ name: 'import legacy iCloud Keychain bundles', value: 'icloud' },
|
|
155
|
+
];
|
|
156
|
+
const importSource = opts.importFrom ? parseImportSource(opts.importFrom) : await select({
|
|
157
|
+
message: 'Import existing secrets now?',
|
|
158
|
+
default: 'none',
|
|
159
|
+
choices: importChoices,
|
|
160
|
+
});
|
|
161
|
+
return { backend, policy, importSource };
|
|
162
|
+
}
|
|
163
|
+
function printOnboardingSummary(backend, policy, prefsFile) {
|
|
164
|
+
console.log(chalk.bold('\nSecrets setup saved.'));
|
|
165
|
+
console.log(chalk.gray(`preferences: ${prefsFile}`));
|
|
166
|
+
printBackendNotes(backend);
|
|
167
|
+
if (policy === 'never') {
|
|
168
|
+
console.log(chalk.yellow('policy: never is saved as a setup preference; create automation-only bundles with --policy never --i-understand.'));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.log(chalk.gray(`policy: ${policy} — used by bundles that do not set their own policy.`));
|
|
172
|
+
}
|
|
173
|
+
console.log(chalk.bold('\nTry:'));
|
|
174
|
+
console.log(chalk.cyan(` agents secrets create prod ${backendArgs(backend).join(' ')} --policy ${policy === 'never' ? 'daily' : policy}`));
|
|
175
|
+
console.log(chalk.cyan(' agents secrets add prod API_KEY'));
|
|
176
|
+
console.log(chalk.cyan(' agents run claude "deploy" --secrets prod'));
|
|
177
|
+
}
|
|
178
|
+
export async function runSecretsSetupWizard(opts = {}) {
|
|
179
|
+
const { backend, policy, importSource } = await resolveInteractiveChoices(opts);
|
|
180
|
+
if (policy === 'never' && !opts.iUnderstand && !isInteractiveTerminal()) {
|
|
181
|
+
throw new Error("Refusing to save policy 'never' non-interactively without --i-understand.");
|
|
182
|
+
}
|
|
183
|
+
applySecretsDefaults(backend, policy);
|
|
184
|
+
const prefsFile = saveSetupSecretsPrefs({
|
|
185
|
+
defaultBackend: backend,
|
|
186
|
+
defaultPolicy: policy,
|
|
187
|
+
updatedAt: new Date().toISOString(),
|
|
188
|
+
});
|
|
189
|
+
await maybeImportSecrets(importSource, {
|
|
190
|
+
bundle: opts.bundle,
|
|
191
|
+
vault: opts.vault,
|
|
192
|
+
backend,
|
|
193
|
+
force: opts.force,
|
|
194
|
+
});
|
|
195
|
+
printOnboardingSummary(backend, policy, prefsFile);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
/** Register `agents setup secrets` under the parent `setup` command. */
|
|
199
|
+
export function registerSetupSecretsCommand(setupCmd) {
|
|
200
|
+
setupCmd
|
|
201
|
+
.command('secrets')
|
|
202
|
+
.description('Configure `agents secrets` defaults and optionally import existing secrets.')
|
|
203
|
+
.option('--backend <backend>', 'default backend: keychain, file, or vault')
|
|
204
|
+
.option('--policy <policy>', 'default prompt policy: daily, always, or never')
|
|
205
|
+
.option('--import-from <source>', 'optional import source: none, env, 1password, or icloud')
|
|
206
|
+
.option('--bundle <name>', 'bundle name for optional imports')
|
|
207
|
+
.option('--vault <name-or-path>', '1Password vault name, or .env path with --import-from env')
|
|
208
|
+
.option('--i-understand', 'allow saving the "never" policy preference without an interactive prompt')
|
|
209
|
+
.option('--force', 'pass --force through to optional imports')
|
|
210
|
+
.action(async (options) => {
|
|
211
|
+
try {
|
|
212
|
+
await runSecretsSetupWizard(options);
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
if (isPromptCancelled(err)) {
|
|
216
|
+
console.log(chalk.yellow('\nCancelled'));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
console.error(chalk.red(err.message));
|
|
220
|
+
process.exitCode = 1;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -22,6 +22,8 @@ import { registerSetupBrowserCommand, runBrowserWizard } from './setup-browser.j
|
|
|
22
22
|
import { registerSetupComputerCommand, runComputerWizard } from './setup-computer.js';
|
|
23
23
|
import { registerSetupShareCommand, runShareWizard } from './setup-share.js';
|
|
24
24
|
import { registerSetupMineCommand } from './setup-mine.js';
|
|
25
|
+
import { registerSetupSecretsCommand } from './setup-secrets.js';
|
|
26
|
+
import { registerSetupFleetCommand } from './setup-fleet.js';
|
|
25
27
|
const HOME = os.homedir();
|
|
26
28
|
/**
|
|
27
29
|
* Import an existing unmanaged agent installation into agents-cli.
|
|
@@ -247,6 +249,8 @@ async function runSetupHub() {
|
|
|
247
249
|
{ name: 'browser — drive a real Chrome/Brave/Edge for web automation', value: 'browser' },
|
|
248
250
|
{ name: 'computer — control native macOS apps (screenshot, click, type)', value: 'computer' },
|
|
249
251
|
{ name: 'share — publish shareable links (Cloudflare R2 + Worker)', value: 'share' },
|
|
252
|
+
{ name: 'secrets — choose storage defaults and import existing credentials', value: 'secrets' },
|
|
253
|
+
{ name: 'fleet — discover Tailscale devices and configure SSH access', value: 'fleet' },
|
|
250
254
|
],
|
|
251
255
|
});
|
|
252
256
|
for (const pick of picks) {
|
|
@@ -257,6 +261,10 @@ async function runSetupHub() {
|
|
|
257
261
|
await runComputerWizard();
|
|
258
262
|
else if (pick === 'share')
|
|
259
263
|
await runShareWizard();
|
|
264
|
+
else if (pick === 'secrets')
|
|
265
|
+
await import('./setup-secrets.js').then((m) => m.runSecretsSetupWizard());
|
|
266
|
+
else if (pick === 'fleet')
|
|
267
|
+
await import('./setup-fleet.js').then((m) => m.runFleetSetupWizard());
|
|
260
268
|
}
|
|
261
269
|
}
|
|
262
270
|
catch (err) {
|
|
@@ -272,11 +280,13 @@ export function registerSetupCommand(program) {
|
|
|
272
280
|
.description('First-time setup. Clones a config repo and installs agent CLIs.')
|
|
273
281
|
.option('-f, --force', 'Re-run setup even if ~/.agents/.system/ already exists (use with caution)')
|
|
274
282
|
.option('--no-system-repo', 'Skip cloning the system repo (you must populate ~/.agents/.system/ yourself)');
|
|
275
|
-
// Capability subcommands: `agents setup browser|computer|share|mine`.
|
|
283
|
+
// Capability subcommands: `agents setup browser|computer|share|mine|secrets|fleet`.
|
|
276
284
|
registerSetupBrowserCommand(setupCmd);
|
|
277
285
|
registerSetupComputerCommand(setupCmd);
|
|
278
286
|
registerSetupShareCommand(setupCmd);
|
|
279
287
|
registerSetupMineCommand(setupCmd);
|
|
288
|
+
registerSetupSecretsCommand(setupCmd);
|
|
289
|
+
registerSetupFleetCommand(setupCmd);
|
|
280
290
|
setHelpSections(setupCmd, {
|
|
281
291
|
examples: `
|
|
282
292
|
# First-time setup (clones the system repo into ~/.agents/.system/)
|
|
@@ -289,20 +299,24 @@ export function registerSetupCommand(program) {
|
|
|
289
299
|
agents setup browser
|
|
290
300
|
agents setup computer
|
|
291
301
|
agents setup share
|
|
302
|
+
agents setup secrets
|
|
303
|
+
agents setup fleet
|
|
292
304
|
`,
|
|
293
305
|
notes: `
|
|
294
306
|
What it does:
|
|
295
307
|
1. Clones the system repo into ~/.agents/.system/
|
|
296
308
|
2. Imports any unmanaged agent installations it finds
|
|
297
|
-
3. On a TTY, offers to set up optional capabilities (browser/computer/share)
|
|
309
|
+
3. On a TTY, offers to set up optional capabilities (browser/computer/share/secrets/fleet)
|
|
298
310
|
|
|
299
311
|
Capability setup can also be run any time on its own:
|
|
300
312
|
agents setup browser # detect a browser + create the default profile
|
|
301
313
|
agents setup computer # install the signed macOS helper + grant permissions
|
|
302
314
|
agents setup share # provision or join a Cloudflare share endpoint
|
|
315
|
+
agents setup secrets # choose secrets backend/policy defaults + import
|
|
316
|
+
agents setup fleet # discover Tailscale devices + configure SSH access
|
|
303
317
|
|
|
304
318
|
To install CLIs from agents.yaml and sync resources into version homes:
|
|
305
|
-
agents
|
|
319
|
+
agents sync --local -y
|
|
306
320
|
`,
|
|
307
321
|
});
|
|
308
322
|
setupCmd.action(async (options) => {
|
package/dist/commands/skills.js
CHANGED
|
@@ -81,7 +81,7 @@ When to use:
|
|
|
81
81
|
skillsCmd
|
|
82
82
|
.command('add [source]')
|
|
83
83
|
.description('Install skills from a source (GitHub, local) or pick from central storage')
|
|
84
|
-
.option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or
|
|
84
|
+
.option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or antigravity@default')
|
|
85
85
|
.option('--names <list>', 'Skill names from ~/.agents/skills/ (comma-separated)')
|
|
86
86
|
.option('-y, --yes', 'Skip all prompts')
|
|
87
87
|
.addHelpText('after', `
|
package/dist/commands/sync.js
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
*/
|
|
32
32
|
import * as path from 'path';
|
|
33
33
|
import chalk from 'chalk';
|
|
34
|
-
import { agentLabel, resolveAgentName,
|
|
34
|
+
import { agentLabel, resolveAgentName, MANAGED_AGENT_IDS, isAgentHardDeprecated, hardDeprecationError } from '../lib/agents.js';
|
|
35
35
|
import { isVersionInstalled, syncResourcesToVersion, parseAgentSpec, resolveVersion, resolveVersionAlias, listInstalledVersions, getAvailableResources, getActuallySyncedResources, getProjectOnlyResources, getNewResources, hasNewResources, promptResourceSelection, promptNewResourceSelection, buildRepoScopedSelection, mergeRepoScopedSelections, listRepoNames, getVersionHomePath, } from '../lib/versions.js';
|
|
36
36
|
import { capableAgents } from '../lib/capabilities.js';
|
|
37
37
|
import { parseHookManifest, registerHooksToSettings } from '../lib/hooks.js';
|
|
@@ -128,7 +128,7 @@ function repoChoiceLabel(repo) {
|
|
|
128
128
|
async function runInteractiveReconcile(opts, outLog, errLog) {
|
|
129
129
|
const { checkbox } = await import('@inquirer/prompts');
|
|
130
130
|
const cwd = opts.cwd || process.cwd();
|
|
131
|
-
const installedAgents =
|
|
131
|
+
const installedAgents = MANAGED_AGENT_IDS.filter((a) => listInstalledVersions(a).length > 0);
|
|
132
132
|
if (installedAgents.length === 0) {
|
|
133
133
|
errLog(chalk.red('No agents installed. Install one: agents add claude@latest'));
|
|
134
134
|
process.exitCode = 1;
|
|
@@ -311,6 +311,11 @@ async function runSync(agentSpec, repoArg, opts) {
|
|
|
311
311
|
}
|
|
312
312
|
agentId = resolved;
|
|
313
313
|
}
|
|
314
|
+
if (agentId && isAgentHardDeprecated(agentId)) {
|
|
315
|
+
errLog(chalk.red(hardDeprecationError(agentId)));
|
|
316
|
+
process.exitCode = 1;
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
314
319
|
if (opts.agentVersion) {
|
|
315
320
|
// Legacy flag and the launch-shim hot path (`--agent-version <concrete>`):
|
|
316
321
|
// pass through verbatim. Selector aliases are a positional-spec feature.
|