@phnx-labs/agents-cli 1.20.72 → 1.20.73
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 +299 -0
- package/README.md +40 -4
- package/dist/bin/agents +0 -0
- package/dist/commands/activity.d.ts +12 -0
- package/dist/commands/activity.js +68 -0
- package/dist/commands/apply.d.ts +13 -0
- package/dist/commands/apply.js +28 -3
- package/dist/commands/browser.js +1 -1
- package/dist/commands/cloud.js +21 -11
- package/dist/commands/doctor.js +8 -4
- package/dist/commands/events.d.ts +11 -10
- package/dist/commands/events.js +30 -21
- package/dist/commands/exec.d.ts +34 -0
- package/dist/commands/exec.js +337 -39
- package/dist/commands/feed.d.ts +13 -0
- package/dist/commands/feed.js +130 -29
- package/dist/commands/hq.d.ts +2 -0
- package/dist/commands/hq.js +58 -0
- package/dist/commands/lease.d.ts +19 -0
- package/dist/commands/lease.js +138 -11
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +123 -0
- package/dist/commands/logs.js +1 -1
- package/dist/commands/mine.d.ts +27 -0
- package/dist/commands/mine.js +207 -0
- package/dist/commands/monitors.js +79 -49
- package/dist/commands/plugins.js +1 -0
- package/dist/commands/profiles.js +158 -0
- package/dist/commands/repo.js +2 -60
- package/dist/commands/resources.d.ts +5 -0
- package/dist/commands/resources.js +95 -0
- package/dist/commands/routines.d.ts +2 -0
- package/dist/commands/routines.js +139 -37
- package/dist/commands/secrets.js +234 -47
- package/dist/commands/send.d.ts +15 -0
- package/dist/commands/send.js +62 -0
- package/dist/commands/sessions-tail.d.ts +1 -0
- package/dist/commands/sessions-tail.js +20 -9
- package/dist/commands/sessions.js +18 -3
- package/dist/commands/setup-mine.d.ts +18 -0
- package/dist/commands/setup-mine.js +106 -0
- package/dist/commands/setup-share.js +2 -2
- package/dist/commands/setup.js +3 -1
- package/dist/commands/share.d.ts +8 -1
- package/dist/commands/share.js +122 -37
- package/dist/commands/ssh.d.ts +9 -0
- package/dist/commands/ssh.js +196 -26
- package/dist/commands/sync.js +1 -1
- package/dist/commands/teams.js +11 -2
- package/dist/index.js +79 -10
- package/dist/lib/activity.d.ts +124 -0
- package/dist/lib/activity.js +542 -0
- package/dist/lib/agents.js +16 -5
- package/dist/lib/artifact-actions.d.ts +1 -1
- package/dist/lib/artifact-actions.js +1 -1
- package/dist/lib/ask-classifier.d.ts +2 -1
- package/dist/lib/ask-classifier.js +3 -3
- package/dist/lib/auth-health.d.ts +32 -4
- package/dist/lib/auth-health.js +40 -1
- package/dist/lib/auto-pull.js +8 -1
- package/dist/lib/brand.d.ts +40 -0
- package/dist/lib/brand.js +122 -0
- package/dist/lib/browser/drivers/ssh.js +4 -7
- package/dist/lib/browser/service.js +6 -8
- package/dist/lib/channels/providers/index.d.ts +2 -0
- package/dist/lib/channels/providers/index.js +20 -0
- package/dist/lib/channels/providers/mailbox.d.ts +2 -0
- package/dist/lib/channels/providers/mailbox.js +26 -0
- package/dist/lib/channels/providers/openclaw-telegram.d.ts +2 -0
- package/dist/lib/channels/providers/openclaw-telegram.js +34 -0
- package/dist/lib/channels/providers/rush.d.ts +6 -0
- package/dist/lib/channels/providers/rush.js +55 -0
- package/dist/lib/channels/registry.d.ts +42 -0
- package/dist/lib/channels/registry.js +20 -0
- package/dist/lib/channels/resolve.d.ts +11 -0
- package/dist/lib/channels/resolve.js +12 -0
- package/dist/lib/cli-resources.d.ts +18 -0
- package/dist/lib/cli-resources.js +53 -4
- package/dist/lib/cloud/codex.js +6 -3
- package/dist/lib/cloud/factory.d.ts +1 -0
- package/dist/lib/cloud/factory.js +10 -4
- package/dist/lib/cloud/rush.d.ts +3 -1
- package/dist/lib/cloud/rush.js +6 -1
- package/dist/lib/cloud/types.d.ts +2 -0
- package/dist/lib/codex-home.d.ts +35 -0
- package/dist/lib/codex-home.js +136 -0
- package/dist/lib/crabbox/cli.d.ts +44 -0
- package/dist/lib/crabbox/cli.js +120 -20
- package/dist/lib/crabbox/lease.d.ts +52 -1
- package/dist/lib/crabbox/lease.js +117 -16
- package/dist/lib/crabbox/progress.d.ts +31 -0
- package/dist/lib/crabbox/progress.js +76 -0
- package/dist/lib/crabbox/runtimes.d.ts +4 -0
- package/dist/lib/crabbox/runtimes.js +33 -8
- package/dist/lib/crabbox/setup-copy.d.ts +87 -0
- package/dist/lib/crabbox/setup-copy.js +127 -0
- package/dist/lib/daemon.d.ts +5 -5
- package/dist/lib/daemon.js +9 -9
- package/dist/lib/drive-sync.js +6 -3
- package/dist/lib/event-stream.d.ts +36 -0
- package/dist/lib/event-stream.js +68 -0
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/exec.d.ts +11 -0
- package/dist/lib/exec.js +23 -2
- package/dist/lib/feed-policy.d.ts +1 -1
- package/dist/lib/feed-policy.js +14 -9
- package/dist/lib/feed-ranking.d.ts +32 -0
- package/dist/lib/feed-ranking.js +224 -0
- package/dist/lib/feed.d.ts +35 -2
- package/dist/lib/feed.js +66 -3
- package/dist/lib/fleet/auth-sync.d.ts +53 -0
- package/dist/lib/fleet/auth-sync.js +92 -0
- package/dist/lib/fleet/remote-login.d.ts +187 -0
- package/dist/lib/fleet/remote-login.js +556 -0
- package/dist/lib/format.d.ts +30 -3
- package/dist/lib/format.js +34 -5
- package/dist/lib/fs-atomic.d.ts +7 -2
- package/dist/lib/fs-atomic.js +8 -12
- package/dist/lib/git.d.ts +16 -0
- package/dist/lib/git.js +79 -2
- package/dist/lib/heal.js +11 -3
- package/dist/lib/hosts/progress.d.ts +28 -10
- package/dist/lib/hosts/progress.js +79 -22
- package/dist/lib/hosts/remote-cmd.js +4 -0
- package/dist/lib/hq/floor.d.ts +87 -0
- package/dist/lib/hq/floor.js +226 -0
- package/dist/lib/menubar/install-menubar.js +14 -20
- package/dist/lib/notify.d.ts +1 -0
- package/dist/lib/notify.js +3 -3
- package/dist/lib/open-url.d.ts +2 -0
- package/dist/lib/open-url.js +19 -0
- package/dist/lib/openclaw-keychain.d.ts +56 -0
- package/dist/lib/openclaw-keychain.js +236 -0
- package/dist/lib/overdue.js +10 -0
- package/dist/lib/permissions.d.ts +44 -1
- package/dist/lib/permissions.js +284 -7
- package/dist/lib/project-launch.d.ts +6 -12
- package/dist/lib/project-launch.js +13 -228
- package/dist/lib/project-resources.d.ts +7 -0
- package/dist/lib/project-resources.js +291 -0
- package/dist/lib/pty-client.d.ts +27 -0
- package/dist/lib/pty-client.js +136 -10
- package/dist/lib/refresh.js +14 -10
- package/dist/lib/resource-profiles.d.ts +26 -0
- package/dist/lib/resource-profiles.js +157 -0
- package/dist/lib/resources/permissions.js +7 -1
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +1 -1
- package/dist/lib/resources.js +32 -3
- package/dist/lib/routines.d.ts +11 -0
- package/dist/lib/routines.js +56 -15
- package/dist/lib/runner.d.ts +1 -0
- package/dist/lib/runner.js +66 -5
- package/dist/lib/secrets/bundles.d.ts +16 -3
- package/dist/lib/secrets/bundles.js +206 -37
- package/dist/lib/secrets/icloud-import.d.ts +2 -2
- package/dist/lib/secrets/icloud-import.js +9 -6
- package/dist/lib/secrets/mcp.js +1 -1
- package/dist/lib/secrets/vault-age-helper.d.ts +1 -0
- package/dist/lib/secrets/vault-age-helper.js +34 -0
- package/dist/lib/secrets/vault.d.ts +49 -0
- package/dist/lib/secrets/vault.js +397 -0
- package/dist/lib/self-heal/checks/path.js +3 -1
- package/dist/lib/self-heal/checks/shadowing.js +10 -2
- package/dist/lib/self-heal/checks/shims.js +19 -11
- package/dist/lib/session/cloud.d.ts +2 -2
- package/dist/lib/session/cloud.js +2 -2
- package/dist/lib/session/db.d.ts +4 -0
- package/dist/lib/session/db.js +44 -7
- package/dist/lib/session/discover.d.ts +2 -0
- package/dist/lib/session/discover.js +114 -3
- package/dist/lib/session/stream-render.d.ts +3 -0
- package/dist/lib/session/stream-render.js +130 -0
- package/dist/lib/session/types.d.ts +6 -0
- package/dist/lib/share/analytics.d.ts +13 -0
- package/dist/lib/share/analytics.js +45 -0
- package/dist/lib/share/config.d.ts +19 -5
- package/dist/lib/share/config.js +44 -8
- package/dist/lib/share/provision.d.ts +58 -6
- package/dist/lib/share/provision.js +97 -22
- package/dist/lib/share/publish.d.ts +36 -13
- package/dist/lib/share/publish.js +55 -8
- package/dist/lib/share/worker-template.js +83 -17
- package/dist/lib/shims.d.ts +9 -0
- package/dist/lib/shims.js +91 -13
- package/dist/lib/ssh-exec.d.ts +14 -0
- package/dist/lib/ssh-exec.js +57 -0
- package/dist/lib/staleness/detectors/hooks.js +25 -1
- package/dist/lib/staleness/detectors/permissions.js +66 -0
- package/dist/lib/staleness/detectors/workflows.js +20 -0
- package/dist/lib/staleness/writers/hooks.js +58 -4
- package/dist/lib/staleness/writers/permissions.js +2 -2
- package/dist/lib/staleness/writers/skills.js +1 -1
- package/dist/lib/staleness/writers/sources.d.ts +10 -1
- package/dist/lib/staleness/writers/sources.js +68 -1
- package/dist/lib/star-nudge.d.ts +45 -0
- package/dist/lib/star-nudge.js +91 -0
- package/dist/lib/startup/command-registry.d.ts +6 -1
- package/dist/lib/startup/command-registry.js +17 -3
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +3 -0
- package/dist/lib/subagents-registry.d.ts +2 -0
- package/dist/lib/subagents-registry.js +3 -0
- package/dist/lib/teams/parsers.js +214 -6
- package/dist/lib/teams/supervisor.d.ts +7 -0
- package/dist/lib/teams/supervisor.js +2 -1
- package/dist/lib/template.d.ts +1 -1
- package/dist/lib/template.js +1 -1
- package/dist/lib/triggers/webhook.js +36 -3
- package/dist/lib/types.d.ts +73 -0
- package/dist/lib/version.d.ts +40 -0
- package/dist/lib/version.js +94 -16
- package/dist/lib/versions.d.ts +11 -0
- package/dist/lib/versions.js +208 -76
- package/dist/lib/workflows.d.ts +2 -0
- package/dist/lib/workflows.js +88 -0
- package/package.json +2 -1
- package/dist/commands/daemon.d.ts +0 -10
- package/dist/commands/daemon.js +0 -121
package/dist/commands/ssh.js
CHANGED
|
@@ -19,6 +19,7 @@ import { getCliVersion } from '../lib/version.js';
|
|
|
19
19
|
import { readAndResolveBundleEnv, isHeadlessSecretsContext } from '../lib/secrets/bundles.js';
|
|
20
20
|
import { machineId } from '../lib/session/sync/config.js';
|
|
21
21
|
import { registerFleetCaptureCommand } from './fleet-capture.js';
|
|
22
|
+
import { registerFleetApplyAlias } from './apply.js';
|
|
22
23
|
import { addIgnored, getDevice, loadDevices, loadIgnored, removeDevice, removeIgnored, upsertDevice, } from '../lib/devices/registry.js';
|
|
23
24
|
import { addControlToken } from '../lib/serve/token.js';
|
|
24
25
|
import { DEFAULT_SERVE_PORT } from '../lib/serve/server.js';
|
|
@@ -40,7 +41,10 @@ import { checkAllClis } from '../lib/teams/agents.js';
|
|
|
40
41
|
import { buildRemoteAgentsInvocation } from '../lib/hosts/remote-cmd.js';
|
|
41
42
|
import { sshExecAsync } from '../lib/ssh-exec.js';
|
|
42
43
|
import { ALL_AGENT_IDS } from '../lib/agents.js';
|
|
43
|
-
import {
|
|
44
|
+
import { crabboxList, crabboxFind, crabboxSshArgv } from '../lib/crabbox/cli.js';
|
|
45
|
+
import { boxAddress, boxStatus, fmtIdleShort, fmtExpiresShort } from './lease.js';
|
|
46
|
+
import { authCellColor, formatCheckedAge, isDeadVerdict, probeLocalFleetAuth, readAuthHealthCache, summarizeHostAuth, summarizeVerdicts, verdictColor, verdictLabel, writeFleetAuthRows, } from '../lib/auth-health.js';
|
|
47
|
+
import { runFleetLogin } from '../lib/fleet/remote-login.js';
|
|
44
48
|
/** One-line summary of a device for `list`. `isSelf` marks the machine this
|
|
45
49
|
* command is running on so it stands out from the rest of the tailnet. */
|
|
46
50
|
function deviceSummary(d, isSelf = false) {
|
|
@@ -130,6 +134,74 @@ function renderDeviceTable(reg, names, self, statsMap, full = false) {
|
|
|
130
134
|
}
|
|
131
135
|
return lines;
|
|
132
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Live "Leased boxes" section for `agents devices` (F4, RUSH-1923), computed
|
|
139
|
+
* from `crabboxList()` — these are ephemeral crabbox leases, NEVER written into
|
|
140
|
+
* the device registry. Returns [] when crabbox is unavailable / has no creds /
|
|
141
|
+
* reports no boxes, so the section is simply omitted. `nowSecs` is injected so
|
|
142
|
+
* the row formatting is deterministic in tests.
|
|
143
|
+
*/
|
|
144
|
+
export function renderLeasedBoxesSection(boxes, nowSecs) {
|
|
145
|
+
if (boxes.length === 0)
|
|
146
|
+
return [];
|
|
147
|
+
const lines = [];
|
|
148
|
+
lines.push('');
|
|
149
|
+
lines.push(chalk.bold('Leased boxes') + chalk.gray(' (ephemeral · via crabbox)'));
|
|
150
|
+
lines.push(' ' +
|
|
151
|
+
chalk.gray('box'.padEnd(16)) +
|
|
152
|
+
chalk.gray('class'.padEnd(10)) +
|
|
153
|
+
chalk.gray('address'.padEnd(24)) +
|
|
154
|
+
chalk.gray('status'.padEnd(9)) +
|
|
155
|
+
chalk.gray('idle'.padEnd(12)) +
|
|
156
|
+
chalk.gray('expires'));
|
|
157
|
+
for (const b of boxes) {
|
|
158
|
+
const addr = boxAddress(b) ?? '—';
|
|
159
|
+
lines.push(' ' +
|
|
160
|
+
chalk.cyan(b.slug.padEnd(16)) +
|
|
161
|
+
(b.class ?? '?').padEnd(10) +
|
|
162
|
+
addr.padEnd(24) +
|
|
163
|
+
boxStatus(b).padEnd(9) +
|
|
164
|
+
chalk.gray(fmtIdleShort(b, nowSecs).padEnd(12)) +
|
|
165
|
+
chalk.gray(fmtExpiresShort(b, nowSecs)));
|
|
166
|
+
}
|
|
167
|
+
lines.push(chalk.gray(' Reuse a box with `agents run --box <slug>` · stop with `agents lease stop <slug>`'));
|
|
168
|
+
return lines;
|
|
169
|
+
}
|
|
170
|
+
/** The leased-box rows for the devices list, or [] when crabbox can't be read. */
|
|
171
|
+
function loadLeasedBoxesSection() {
|
|
172
|
+
try {
|
|
173
|
+
const boxes = crabboxList({ secretsBundle: process.env.AGENTS_LEASE_SECRETS_BUNDLE, timeoutMs: 5000 });
|
|
174
|
+
return renderLeasedBoxesSection(boxes, Math.floor(Date.now() / 1000));
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return []; // crabbox not installed / no provider creds — omit the section
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* `agents ssh <slug>` targeting a leased crabbox box. crabbox provisions a
|
|
182
|
+
* per-lease identity key, so we ssh via crabbox's OWN emitted invocation
|
|
183
|
+
* (`crabboxSshArgv`) — a raw `ssh crabbox@ip` fails publickey. Returns false when
|
|
184
|
+
* `name` is not a known crabbox slug so the caller can fall through to the normal
|
|
185
|
+
* "Unknown device" error.
|
|
186
|
+
*/
|
|
187
|
+
function trySshLeasedBox(name, cmd) {
|
|
188
|
+
let box;
|
|
189
|
+
try {
|
|
190
|
+
box = crabboxFind(name, { secretsBundle: process.env.AGENTS_LEASE_SECRETS_BUNDLE, timeoutMs: 5000 });
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return false; // crabbox unavailable — not a leased-box target
|
|
194
|
+
}
|
|
195
|
+
if (!box)
|
|
196
|
+
return false;
|
|
197
|
+
const sshArgv = crabboxSshArgv(name, { secretsBundle: process.env.AGENTS_LEASE_SECRETS_BUNDLE, timeoutMs: 8000 });
|
|
198
|
+
if (!sshArgv) {
|
|
199
|
+
console.error(chalk.red(`Leased box '${name}' is not reachable yet (status: ${boxStatus(box)}).`));
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
const res = spawnSync(sshArgv[0], [...sshArgv.slice(1), ...cmd], { stdio: 'inherit' });
|
|
203
|
+
process.exit(res.status ?? 1);
|
|
204
|
+
}
|
|
133
205
|
/** Resolve a device or exit with a clear error. */
|
|
134
206
|
async function mustGetDevice(name) {
|
|
135
207
|
const d = await getDevice(name);
|
|
@@ -423,17 +495,28 @@ async function runFleetPing(opts) {
|
|
|
423
495
|
if (opts.strict && anyBad)
|
|
424
496
|
process.exitCode = 1;
|
|
425
497
|
}
|
|
426
|
-
/**
|
|
498
|
+
/** Resolve an {@link AuthCellColor} to a chalk painter. Single map for cells + labels. */
|
|
499
|
+
const CELL_PAINT = {
|
|
500
|
+
green: chalk.green,
|
|
501
|
+
yellow: chalk.yellow,
|
|
502
|
+
red: chalk.red,
|
|
503
|
+
gray: chalk.gray,
|
|
504
|
+
dim: chalk.dim,
|
|
505
|
+
};
|
|
506
|
+
/**
|
|
507
|
+
* Color a per-host×agent cell. The numerator counts accounts that are usable
|
|
508
|
+
* right now — live-verified PLUS signed-in-but-unverifiable (codex/grok) — over
|
|
509
|
+
* the total, so a logged-in codex fleet reads "1/1", not a scary "0/1". Color is
|
|
510
|
+
* the shared {@link authCellColor}: red only for revoked (re-login), yellow for
|
|
511
|
+
* soft/expired (self-refreshes), gray for present-but-unverifiable, green when
|
|
512
|
+
* all live.
|
|
513
|
+
*/
|
|
427
514
|
function authCell(summary, width) {
|
|
428
515
|
if (summary.total === 0)
|
|
429
516
|
return chalk.dim('·'.padEnd(width));
|
|
430
|
-
const
|
|
431
|
-
const padded =
|
|
432
|
-
|
|
433
|
-
return chalk.red(padded);
|
|
434
|
-
if (summary.warn > 0)
|
|
435
|
-
return chalk.yellow(padded);
|
|
436
|
-
return chalk.green(padded);
|
|
517
|
+
const ok = summary.live + summary.present;
|
|
518
|
+
const padded = `${ok}/${summary.total}`.padEnd(width);
|
|
519
|
+
return CELL_PAINT[authCellColor(summary)](padded);
|
|
437
520
|
}
|
|
438
521
|
/** Render the fleet auth matrix (device rows × agent columns) plus an optional per-account breakdown. */
|
|
439
522
|
function renderAuthMatrix(results, opts) {
|
|
@@ -466,7 +549,7 @@ function renderAuthMatrix(results, opts) {
|
|
|
466
549
|
lines.push(` ${r.host.padEnd(nameW)} ${cells.join(' ')}${note}`);
|
|
467
550
|
}
|
|
468
551
|
lines.push('');
|
|
469
|
-
lines.push(chalk.gray(' cell =
|
|
552
|
+
lines.push(chalk.gray(' cell = signed-in/total accounts · green live · gray signed-in (unverifiable: codex/grok) · yellow expired (self-refreshes) · red revoked (re-login)'));
|
|
470
553
|
if (opts?.verbose) {
|
|
471
554
|
lines.push('');
|
|
472
555
|
lines.push(chalk.bold('Accounts'));
|
|
@@ -475,9 +558,7 @@ function renderAuthMatrix(results, opts) {
|
|
|
475
558
|
continue;
|
|
476
559
|
for (const row of r.rows.slice().sort((x, y) => (x.agent + x.version).localeCompare(y.agent + y.version))) {
|
|
477
560
|
const v = row.health.verdict;
|
|
478
|
-
const label = v
|
|
479
|
-
: (v === 'revoked' || v === 'expired') ? chalk.red(verdictLabel(v))
|
|
480
|
-
: chalk.yellow(verdictLabel(v));
|
|
561
|
+
const label = CELL_PAINT[verdictColor(v)](verdictLabel(v));
|
|
481
562
|
const acctRaw = row.account ?? '—';
|
|
482
563
|
const acct = row.account ? chalk.cyan(acctRaw.padEnd(28)) : chalk.dim(acctRaw.padEnd(28));
|
|
483
564
|
const detail = row.health.detail ? chalk.dim(` ${row.health.detail}`) : '';
|
|
@@ -488,6 +569,46 @@ function renderAuthMatrix(results, opts) {
|
|
|
488
569
|
}
|
|
489
570
|
return lines;
|
|
490
571
|
}
|
|
572
|
+
/** Render the final `fleet login` result summary in the house auth-matrix style. */
|
|
573
|
+
function renderLoginMatrix(results) {
|
|
574
|
+
const lines = [chalk.bold('Fleet login')];
|
|
575
|
+
if (results.length === 0) {
|
|
576
|
+
lines.push(chalk.gray(' Nothing pending — every requested account is already logged in.'));
|
|
577
|
+
return lines;
|
|
578
|
+
}
|
|
579
|
+
const nameW = Math.max(6, ...results.map((r) => `${r.agent}@${r.device}`.length));
|
|
580
|
+
for (const r of results) {
|
|
581
|
+
const who = `${r.agent}@${r.device}`.padEnd(nameW);
|
|
582
|
+
let badge;
|
|
583
|
+
switch (r.state) {
|
|
584
|
+
case 'authorized':
|
|
585
|
+
badge = chalk.green('authorized');
|
|
586
|
+
break;
|
|
587
|
+
case 'ready':
|
|
588
|
+
badge = chalk.cyan('code ready');
|
|
589
|
+
break;
|
|
590
|
+
case 'driving':
|
|
591
|
+
badge = chalk.yellow('driving');
|
|
592
|
+
break;
|
|
593
|
+
case 'skipped':
|
|
594
|
+
badge = chalk.gray('not remotable');
|
|
595
|
+
break;
|
|
596
|
+
case 'error':
|
|
597
|
+
badge = chalk.red('error');
|
|
598
|
+
break;
|
|
599
|
+
default:
|
|
600
|
+
badge = chalk.gray(r.state);
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
const note = r.detail ? chalk.dim(` ${r.detail}`) : (r.reason && !r.remotable ? chalk.dim(` ${r.reason}`) : '');
|
|
604
|
+
lines.push(` ${who} ${badge}${note}`);
|
|
605
|
+
}
|
|
606
|
+
const auth = results.filter((r) => r.state === 'authorized').length;
|
|
607
|
+
const remotable = results.filter((r) => r.remotable).length;
|
|
608
|
+
lines.push('');
|
|
609
|
+
lines.push(chalk.gray(` ${auth}/${remotable} remotable logins authorized`));
|
|
610
|
+
return lines;
|
|
611
|
+
}
|
|
491
612
|
/** Register the `agents devices` command tree (also aliased as `fleet`). */
|
|
492
613
|
function registerDevicesCommands(program) {
|
|
493
614
|
const devicesCmd = program
|
|
@@ -529,6 +650,9 @@ Typical workflow:
|
|
|
529
650
|
});
|
|
530
651
|
// `agents fleet capture` — snapshot live state into agents.yaml fleet:.
|
|
531
652
|
registerFleetCaptureCommand(devicesCmd);
|
|
653
|
+
// `agents fleet apply` — same reconcile engine as the top-level `agents apply`,
|
|
654
|
+
// surfaced under the fleet tree for discoverability.
|
|
655
|
+
registerFleetApplyAlias(devicesCmd);
|
|
532
656
|
devicesCmd
|
|
533
657
|
.command('register <name>')
|
|
534
658
|
.description('Register a discovered (pending) node by name — used by the menu-bar "NEW DEVICES → Register" action.')
|
|
@@ -576,16 +700,7 @@ Typical workflow:
|
|
|
576
700
|
}
|
|
577
701
|
console.log(chalk.green(`No longer ignoring '${name}'`) + chalk.gray(' — run `agents devices sync` to register it.'));
|
|
578
702
|
});
|
|
579
|
-
|
|
580
|
-
.command('list')
|
|
581
|
-
.alias('ls')
|
|
582
|
-
.description('List registered devices with platform, address, reachability, and live resource headroom.')
|
|
583
|
-
.option('--json', 'output the registry as a JSON array (for scripts and hooks)')
|
|
584
|
-
.option('--no-stats', 'skip the live resource probe (instant; names/addresses only)')
|
|
585
|
-
.option('--refresh', 'force a live probe of every device, bypassing the cache')
|
|
586
|
-
.option('--live', 'alias of --refresh (shorter to type)')
|
|
587
|
-
.option('-f, --full', 'full mode: add per-device core count and free/total memory')
|
|
588
|
-
.action(async (opts) => {
|
|
703
|
+
const runList = async (opts = {}) => {
|
|
589
704
|
const reg = await loadDevices();
|
|
590
705
|
const names = Object.keys(reg).sort();
|
|
591
706
|
if (opts.json) {
|
|
@@ -629,7 +744,26 @@ Typical workflow:
|
|
|
629
744
|
if (freshness?.servedFromCache && freshness.oldestFetchedAt != null) {
|
|
630
745
|
console.log(chalk.gray(` updated ${formatCheckedAge(freshness.oldestFetchedAt)} — pass --refresh (--live) for a live probe`));
|
|
631
746
|
}
|
|
632
|
-
|
|
747
|
+
// Ephemeral crabbox leases live alongside the registered fleet but are never
|
|
748
|
+
// written into the registry — surface them as their own live section. This is
|
|
749
|
+
// a live provider call, so honor --no-stats (the explicit "instant, no probes"
|
|
750
|
+
// opt-out) and bound it so a slow provider can't hang `agents devices`.
|
|
751
|
+
if (opts.stats !== false) {
|
|
752
|
+
for (const line of loadLeasedBoxesSection())
|
|
753
|
+
console.log(line);
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
devicesCmd.action(runList);
|
|
757
|
+
devicesCmd
|
|
758
|
+
.command('list')
|
|
759
|
+
.alias('ls')
|
|
760
|
+
.description('List registered devices with platform, address, reachability, and live resource headroom.')
|
|
761
|
+
.option('--json', 'output the registry as a JSON array (for scripts and hooks)')
|
|
762
|
+
.option('--no-stats', 'skip the live resource probe (instant; names/addresses only)')
|
|
763
|
+
.option('--refresh', 'force a live probe of every device, bypassing the cache')
|
|
764
|
+
.option('--live', 'alias of --refresh (shorter to type)')
|
|
765
|
+
.option('-f, --full', 'full mode: add per-device core count and free/total memory')
|
|
766
|
+
.action(runList);
|
|
633
767
|
devicesCmd
|
|
634
768
|
.command('status')
|
|
635
769
|
.description('Show fleet health: warnings rollup, per-device sync drift, CLI readiness, version skew, and resource headroom.')
|
|
@@ -648,8 +782,41 @@ Typical workflow:
|
|
|
648
782
|
.option('--local', 'probe only this host (used internally for fan-out)')
|
|
649
783
|
.option('--verbose', 'show a per-account breakdown, not just the per-host rollup')
|
|
650
784
|
.option('--strict', 'exit non-zero when any account is revoked (expired is soft — it self-refreshes)')
|
|
785
|
+
.action(async (opts, cmd) => {
|
|
786
|
+
// The root program also defines a global `--verbose` (startup self-heal
|
|
787
|
+
// detail), and commander binds a shared long flag to the program, not the
|
|
788
|
+
// leaf — so `fleet ping --verbose` never set opts.verbose and the
|
|
789
|
+
// per-account breakdown was silently unreachable. Read the effective value
|
|
790
|
+
// from the merged globals so the flag works at either level.
|
|
791
|
+
const verbose = opts.verbose ?? Boolean(cmd.optsWithGlobals().verbose);
|
|
792
|
+
await runFleetPing({ ...opts, verbose });
|
|
793
|
+
});
|
|
794
|
+
devicesCmd
|
|
795
|
+
.command('login')
|
|
796
|
+
.description('Log agent CLIs into fleet boxes over SSH: drive each box\'s device-code OAuth, scrape the URL + code, and surface every pending login in one local browser page. Default drives all codes at once; --interactive walks one box at a time (codes requested just-in-time so they don\'t expire).')
|
|
797
|
+
.option('--agents <csv>', 'only these agents (comma-separated); default: every agent with a device-code flow')
|
|
798
|
+
.option('--devices <csv>', 'only these devices (comma-separated); default: every online box')
|
|
799
|
+
.option('--all', 'target every device-code pair regardless of cached login state (cold cache / forced re-login)')
|
|
800
|
+
.option('--interactive', 'guided one-box-at-a-time wizard (codes requested just-in-time)')
|
|
801
|
+
.option('--json', 'output the final result matrix as JSON')
|
|
651
802
|
.action(async (opts) => {
|
|
652
|
-
|
|
803
|
+
const csv = (s) => (s ? s.split(',').map((x) => x.trim()).filter(Boolean) : undefined);
|
|
804
|
+
const results = await runFleetLogin({
|
|
805
|
+
agents: csv(opts.agents),
|
|
806
|
+
devices: csv(opts.devices),
|
|
807
|
+
all: opts.all,
|
|
808
|
+
interactive: opts.interactive,
|
|
809
|
+
json: opts.json,
|
|
810
|
+
});
|
|
811
|
+
if (opts.json) {
|
|
812
|
+
console.log(JSON.stringify(results, null, 2));
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
for (const line of renderLoginMatrix(results))
|
|
816
|
+
console.log(line);
|
|
817
|
+
}
|
|
818
|
+
if (results.some((r) => r.remotable && r.state !== 'authorized'))
|
|
819
|
+
process.exitCode = 1;
|
|
653
820
|
});
|
|
654
821
|
devicesCmd
|
|
655
822
|
.command('show <name>')
|
|
@@ -849,6 +1016,9 @@ secrets bundle via an askpass shim — the password never touches argv.
|
|
|
849
1016
|
// A bare unregistered alias still errors as "Unknown device".
|
|
850
1017
|
const device = resolveDeviceTarget(name, await loadDevices());
|
|
851
1018
|
if (!device) {
|
|
1019
|
+
// Not a registered device — it may be a leased crabbox box slug. ssh into
|
|
1020
|
+
// it directly (crabbox@<tailnet|ip>:2222) before giving up.
|
|
1021
|
+
trySshLeasedBox(name, cmd); // exits the process on a match
|
|
852
1022
|
console.error(chalk.red(`Unknown device '${name}'. See 'agents devices list'.`));
|
|
853
1023
|
process.exit(1);
|
|
854
1024
|
}
|
|
@@ -907,7 +1077,7 @@ async function runAskpass() {
|
|
|
907
1077
|
process.exit(1);
|
|
908
1078
|
}
|
|
909
1079
|
try {
|
|
910
|
-
const { env } = readAndResolveBundleEnv(bundle, { caller: 'agents ssh', agentOnly: isHeadlessSecretsContext() });
|
|
1080
|
+
const { env } = readAndResolveBundleEnv(bundle, { caller: 'agents ssh', keys: [key], keyMode: 'storage', agentOnly: isHeadlessSecretsContext() });
|
|
911
1081
|
const value = env[key];
|
|
912
1082
|
if (value === undefined) {
|
|
913
1083
|
console.error(`askpass: key '${key}' not found in bundle '${bundle}'`);
|
package/dist/commands/sync.js
CHANGED
|
@@ -544,7 +544,7 @@ function runLaunchMode(agent, version, cwd, quiet) {
|
|
|
544
544
|
if (result.rulesCompiled)
|
|
545
545
|
bits.push('rules');
|
|
546
546
|
if (result.workspaceLinks > 0)
|
|
547
|
-
bits.push(`${result.workspaceLinks}
|
|
547
|
+
bits.push(`${result.workspaceLinks} project resource(s)`);
|
|
548
548
|
const mpCount = Object.keys(result.marketplaces).length;
|
|
549
549
|
if (mpCount > 0) {
|
|
550
550
|
const pluginCount = Object.values(result.marketplaces).reduce((acc, names) => acc + names.length, 0);
|
package/dist/commands/teams.js
CHANGED
|
@@ -7,6 +7,8 @@ import { AgentManager, AgentStatus, checkCliSignedIn, collectTeamsDoctorData, ge
|
|
|
7
7
|
import { mailboxDir, enqueue } from '../lib/mailbox.js';
|
|
8
8
|
import { resolveProvider } from '../lib/cloud/registry.js';
|
|
9
9
|
import { emit } from '../lib/events.js';
|
|
10
|
+
import { maybeShowStarNudge } from '../lib/star-nudge.js';
|
|
11
|
+
import { shareRuntimeEnv } from '../lib/share/config.js';
|
|
10
12
|
import { runSupervisor } from '../lib/teams/supervisor.js';
|
|
11
13
|
import { debug } from '../lib/teams/debug.js';
|
|
12
14
|
import { runPrWatch, DEFAULT_MAX_WAVES, } from '../lib/teams/pr-watch.js';
|
|
@@ -49,8 +51,6 @@ const VALID_AGENTS = Object.keys(AGENT_NAMES);
|
|
|
49
51
|
const VALID_MODES = ['plan', 'edit', 'auto', 'skip', 'full'];
|
|
50
52
|
const VALID_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max', 'auto'];
|
|
51
53
|
const VALID_CLOUD_PROVIDERS = ['rush', 'codex', 'factory'];
|
|
52
|
-
// Auto-enable JSON mode when piped / not a TTY so AI agent consumers get
|
|
53
|
-
// parseable output by default.
|
|
54
54
|
function statusColor(status) {
|
|
55
55
|
switch (status) {
|
|
56
56
|
case 'pending': return chalk.blue;
|
|
@@ -1352,6 +1352,7 @@ export function registerTeamsCommands(program) {
|
|
|
1352
1352
|
repo: opts.repo,
|
|
1353
1353
|
branch: opts.branch,
|
|
1354
1354
|
model: a.model ?? undefined,
|
|
1355
|
+
env: shareRuntimeEnv({ agentOnly: true }),
|
|
1355
1356
|
};
|
|
1356
1357
|
const cloudTask = await prov.dispatch(dispatchOpts);
|
|
1357
1358
|
return { cloudSessionId: cloudTask.id };
|
|
@@ -1369,6 +1370,7 @@ export function registerTeamsCommands(program) {
|
|
|
1369
1370
|
repo: opts.repo,
|
|
1370
1371
|
branch: opts.branch,
|
|
1371
1372
|
model: opts.model,
|
|
1373
|
+
env: shareRuntimeEnv({ agentOnly: true }),
|
|
1372
1374
|
};
|
|
1373
1375
|
try {
|
|
1374
1376
|
const cloudTask = await prov.dispatch(dispatchOpts);
|
|
@@ -1594,6 +1596,13 @@ export function registerTeamsCommands(program) {
|
|
|
1594
1596
|
emit('teams.complete', { module: 'teams', team, stoppedBy: result.stoppedBy, waves: result.waves, durationMs: result.elapsed_ms });
|
|
1595
1597
|
if (result.stoppedBy === 'drained') {
|
|
1596
1598
|
console.log(chalk.green(`Factory drained in ${elapsed}s (${result.waves} waves).`));
|
|
1599
|
+
// First-successful-team star nudge (one-time, non-nagging). "drained"
|
|
1600
|
+
// only means nothing is pending/running — teammates may have failed — so
|
|
1601
|
+
// gate on a clean drain (failed === 0). A team where every teammate
|
|
1602
|
+
// failed is not the success this nudge celebrates.
|
|
1603
|
+
if ((result.failed ?? 0) === 0) {
|
|
1604
|
+
maybeShowStarNudge({ quiet: opts.json });
|
|
1605
|
+
}
|
|
1597
1606
|
}
|
|
1598
1607
|
else if (result.stoppedBy === 'max-waves') {
|
|
1599
1608
|
console.error(chalk.yellow(`Hit --max-waves=${maxWaves}; stopping. Re-run to continue.`));
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,11 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
26
26
|
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
27
27
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
28
28
|
const VERSION = packageJson.version;
|
|
29
|
+
if (process.argv[2] === '__vault-age-helper') {
|
|
30
|
+
const { runVaultAgeHelperCli } = await import('./lib/secrets/vault-age-helper.js');
|
|
31
|
+
await runVaultAgeHelperCli();
|
|
32
|
+
process.exit(process.exitCode ?? 0);
|
|
33
|
+
}
|
|
29
34
|
import { NPM_PACKAGE_NAME, deriveGlobalPrefix, detectPackageManager, installPackageIntoPrefix, installPackageWithBun, verifyInstalledVersion, refreshAliasShims, downloadVerifiedTarball, } from './lib/self-update.js';
|
|
30
35
|
// Detect dev/working-tree builds and default the noisy startup steps off.
|
|
31
36
|
// Three cases trip this:
|
|
@@ -50,7 +55,7 @@ if (IS_DEV_BUILD) {
|
|
|
50
55
|
// module on each invocation (which loaded the whole ~50-module tree before the
|
|
51
56
|
// first byte of output), the registry maps a command name to a thunk that
|
|
52
57
|
// imports only what that command needs. See src/lib/startup/command-registry.ts.
|
|
53
|
-
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadPackages,
|
|
58
|
+
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadResources, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadPackages, loadRoutines, loadMonitors, loadRun, loadFork, loadDefaults, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadCheck, loadStatus, loadProfiles, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadDrive, loadFactory, loadUsage, loadCost, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadHq, loadFeed, loadActivity, loadMailboxes, } from './lib/startup/command-registry.js';
|
|
54
59
|
import { applyGlobalHelpConventions } from './lib/help.js';
|
|
55
60
|
import { renderWhatsNew } from './lib/whats-new.js';
|
|
56
61
|
import { emit, redactArgs } from './lib/events.js';
|
|
@@ -68,9 +73,18 @@ if (process.argv[2] === '__shim') {
|
|
|
68
73
|
const code = await execShimPassthrough(agent, rawArgs, process.cwd(), pinned || undefined);
|
|
69
74
|
process.exit(code);
|
|
70
75
|
}
|
|
76
|
+
if (process.argv[2] === '__daemon-run') {
|
|
77
|
+
const { runDaemon } = await import('./lib/daemon.js');
|
|
78
|
+
await runDaemon();
|
|
79
|
+
process.exit(process.exitCode ?? 0);
|
|
80
|
+
}
|
|
81
|
+
// White-label: the shim for a brand (e.g. `jack`) exports AGENTS_BRAND, so the
|
|
82
|
+
// CLI presents its own name/help/errors as the brand. Unbranded (AGENTS_BRAND
|
|
83
|
+
// unset) resolves to 'agents' and everything below is byte-identical to before.
|
|
84
|
+
const BRAND = resolveBrandName();
|
|
71
85
|
const program = new Command();
|
|
72
86
|
program
|
|
73
|
-
.name(
|
|
87
|
+
.name(BRAND)
|
|
74
88
|
.description('Environment manager for AI agents')
|
|
75
89
|
.version(VERSION)
|
|
76
90
|
.option('--verbose', 'Show startup self-heal details on stderr')
|
|
@@ -86,7 +100,7 @@ program
|
|
|
86
100
|
function auditCommandPath(cmd) {
|
|
87
101
|
const parts = [];
|
|
88
102
|
let c = cmd;
|
|
89
|
-
while (c && c.name() && c.name() !==
|
|
103
|
+
while (c && c.name() && c.name() !== BRAND) {
|
|
90
104
|
parts.unshift(c.name());
|
|
91
105
|
c = c.parent;
|
|
92
106
|
}
|
|
@@ -129,11 +143,36 @@ program.hook('postAction', (_thisCommand, actionCommand) => {
|
|
|
129
143
|
// Best-effort completion record; the start line is the durable audit fact.
|
|
130
144
|
}
|
|
131
145
|
});
|
|
146
|
+
/**
|
|
147
|
+
* Skin the static root help for a brand: rewrite the visible `agents` command
|
|
148
|
+
* examples to the brand name and drop lines for commands this brand disabled.
|
|
149
|
+
* A no-op for the unbranded `agents` CLI with nothing disabled.
|
|
150
|
+
*/
|
|
151
|
+
function brandRootHelp(raw) {
|
|
152
|
+
let text = raw;
|
|
153
|
+
if (BRAND !== 'agents') {
|
|
154
|
+
text = text
|
|
155
|
+
.replace(/Usage: agents /g, `Usage: ${BRAND} `)
|
|
156
|
+
.replace(/^ {2}agents /gm, ` ${BRAND} `)
|
|
157
|
+
.replace(/Run 'agents /g, `Run '${BRAND} `);
|
|
158
|
+
}
|
|
159
|
+
const disabled = disabledCommandsForActiveBrand();
|
|
160
|
+
if (disabled.size > 0) {
|
|
161
|
+
text = text
|
|
162
|
+
.split('\n')
|
|
163
|
+
.filter((line) => {
|
|
164
|
+
const m = line.match(/^ {2}([a-z][\w-]*)/);
|
|
165
|
+
return !(m && disabled.has(m[1]));
|
|
166
|
+
})
|
|
167
|
+
.join('\n');
|
|
168
|
+
}
|
|
169
|
+
return text;
|
|
170
|
+
}
|
|
132
171
|
// Custom help for the main program only
|
|
133
172
|
const originalHelpInformation = program.helpInformation.bind(program);
|
|
134
173
|
program.helpInformation = function () {
|
|
135
|
-
if (this.name() ===
|
|
136
|
-
return `Usage: agents [command] [options]
|
|
174
|
+
if (this.name() === BRAND && !this.parent) {
|
|
175
|
+
return brandRootHelp(`Usage: agents [command] [options]
|
|
137
176
|
|
|
138
177
|
Install, configure, run, and dispatch AI coding agents from one place.
|
|
139
178
|
Works with Claude, Codex, Gemini, Cursor, OpenCode, OpenClaw, and Droid.
|
|
@@ -154,6 +193,7 @@ Agent versions:
|
|
|
154
193
|
trash Inspect and restore soft-deleted version directories
|
|
155
194
|
view [agent[@version]] List versions, or inspect one in detail
|
|
156
195
|
inspect <target> Deep details for one agent+version, or a DotAgents repo (user|system|project|alias|path)
|
|
196
|
+
resources Show merged DotAgents resources with their winning layer
|
|
157
197
|
|
|
158
198
|
Agent configuration (synced across versions):
|
|
159
199
|
rules Instructions given to agents (CLAUDE.md, etc.)
|
|
@@ -173,6 +213,7 @@ Run and dispatch:
|
|
|
173
213
|
run <agent|profile> [prompt] Run an agent. Omit prompt for interactive mode.
|
|
174
214
|
defaults Configure run defaults by agent/version selector
|
|
175
215
|
teams Coordinate multiple agents on shared work
|
|
216
|
+
hq JSON bridge for the interactive Agents HQ floor
|
|
176
217
|
routines Run agents on a cron schedule (scheduler auto-starts)
|
|
177
218
|
webhook Receive signed GitHub/Linear webhooks for trigger routines
|
|
178
219
|
funnel Expose a webhook receiver through Tailscale Funnel
|
|
@@ -182,6 +223,7 @@ Run and dispatch:
|
|
|
182
223
|
pty Drive interactive terminal programs (REPLs, TUIs) via a persistent PTY session
|
|
183
224
|
|
|
184
225
|
Credentials and profiles:
|
|
226
|
+
profile Activate resource profiles across skills, MCP, permissions, and secrets
|
|
185
227
|
profiles Bundles of (host CLI, endpoint, model, auth)
|
|
186
228
|
secrets Keychain-backed env bundles; use 'secrets exec <bundle> -- <cmd>' to inject into a subprocess
|
|
187
229
|
|
|
@@ -213,7 +255,7 @@ Options:
|
|
|
213
255
|
--verbose Show startup self-heal details on stderr
|
|
214
256
|
|
|
215
257
|
System config lives in ~/.agents/.system/. Run 'agents <command> --help' for details.
|
|
216
|
-
|
|
258
|
+
`);
|
|
217
259
|
}
|
|
218
260
|
return originalHelpInformation();
|
|
219
261
|
};
|
|
@@ -251,6 +293,7 @@ async function showWhatsNew(fromVersion, toVersion) {
|
|
|
251
293
|
}
|
|
252
294
|
const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
253
295
|
import { getUpdateCheckPath, getMigratedSentinelPath, getUserAgentsDir, getRuntimeStateDir } from './lib/state.js';
|
|
296
|
+
import { resolveBrandName, disabledCommandsForActiveBrand } from './lib/brand.js';
|
|
254
297
|
import { readUpdateCache, saveUpdateCheck, dismissUpdateVersion, shouldPromptUpgrade, findAgentsCliInstalls, } from './lib/self-update.js';
|
|
255
298
|
const UPDATE_CHECK_FILE = getUpdateCheckPath();
|
|
256
299
|
/**
|
|
@@ -668,7 +711,9 @@ async function registerEagerForRequest(name) {
|
|
|
668
711
|
async function registerAllEagerCommands() {
|
|
669
712
|
await reg(loadView);
|
|
670
713
|
await reg(loadShare);
|
|
714
|
+
await reg(loadSend);
|
|
671
715
|
await reg(loadInspect);
|
|
716
|
+
await reg(loadResources);
|
|
672
717
|
await reg(loadFeedback);
|
|
673
718
|
await reg(loadCommands);
|
|
674
719
|
await reg(loadHooks);
|
|
@@ -686,7 +731,6 @@ async function registerAllEagerCommands() {
|
|
|
686
731
|
await reg(loadVersions);
|
|
687
732
|
await reg(loadImport);
|
|
688
733
|
await reg(loadPackages);
|
|
689
|
-
await reg(loadDaemon);
|
|
690
734
|
await reg(loadRoutines);
|
|
691
735
|
await reg(loadMonitors);
|
|
692
736
|
await reg(loadRun);
|
|
@@ -703,6 +747,7 @@ async function registerAllEagerCommands() {
|
|
|
703
747
|
registerExecAliasCommand(program);
|
|
704
748
|
await reg(loadProfiles);
|
|
705
749
|
await reg(loadSecrets);
|
|
750
|
+
await reg(loadLogin);
|
|
706
751
|
await reg(loadWallet);
|
|
707
752
|
await reg(loadHelper);
|
|
708
753
|
await reg(loadMenubar);
|
|
@@ -717,6 +762,7 @@ async function registerAllEagerCommands() {
|
|
|
717
762
|
await reg(loadOutput);
|
|
718
763
|
await reg(loadBudget);
|
|
719
764
|
await reg(loadAlias);
|
|
765
|
+
await reg(loadMine);
|
|
720
766
|
await reg(loadPty);
|
|
721
767
|
await reg(loadTmux);
|
|
722
768
|
await reg(loadWatchdog);
|
|
@@ -728,7 +774,9 @@ async function registerAllEagerCommands() {
|
|
|
728
774
|
await reg(loadAudit);
|
|
729
775
|
await reg(loadWebhook);
|
|
730
776
|
await reg(loadFunnel);
|
|
777
|
+
await reg(loadHq);
|
|
731
778
|
await reg(loadFeed);
|
|
779
|
+
await reg(loadActivity);
|
|
732
780
|
await reg(loadMailboxes);
|
|
733
781
|
await reg(loadSsh);
|
|
734
782
|
registerJobsCronAliasCommand(program, 'jobs');
|
|
@@ -795,13 +843,19 @@ const verboseStartup = passedArgs.includes('--verbose');
|
|
|
795
843
|
// Help and version output are pure documentation — they must never gate on
|
|
796
844
|
// setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
|
|
797
845
|
const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
|
|
846
|
+
// White-label: a brand can hide built-in top-level commands. A hidden command
|
|
847
|
+
// must behave as if it doesn't exist under this brand (unknown-command +
|
|
848
|
+
// spellcheck), while `agents` itself is unaffected. `brandDisabled` is empty for
|
|
849
|
+
// the unbranded CLI, so all of this is a no-op there.
|
|
850
|
+
const brandDisabled = disabledCommandsForActiveBrand();
|
|
851
|
+
const requestedIsDisabled = requestedCommand !== undefined && brandDisabled.has(requestedCommand);
|
|
798
852
|
// `--host` passthrough: run this invocation on a remote machine over SSH instead
|
|
799
853
|
// of locally. Handled before any local command registration / update check /
|
|
800
854
|
// background sync — a remote run needs none of that. Only the allowlisted
|
|
801
855
|
// read-only + config + teams commands route here; `run`/`sessions` are absent
|
|
802
856
|
// from the table and fall through to their own richer `--host` handling below.
|
|
803
857
|
// `--help`/`--version` stay local (docs must work without a reachable host).
|
|
804
|
-
if (requestedCommand !== undefined && !helpOrVersionRequested) {
|
|
858
|
+
if (requestedCommand !== undefined && !helpOrVersionRequested && !requestedIsDisabled) {
|
|
805
859
|
const { maybeRunOnHost } = await import('./lib/hosts/passthrough.js');
|
|
806
860
|
if (await maybeRunOnHost(requestedCommand, passedArgs)) {
|
|
807
861
|
process.exit(process.exitCode ?? 0);
|
|
@@ -810,7 +864,13 @@ if (requestedCommand !== undefined && !helpOrVersionRequested) {
|
|
|
810
864
|
// Register only the command(s) this invocation actually uses. Lazy commands
|
|
811
865
|
// (sessions/teams/cloud) are handled after applyGlobalHelpConventions below.
|
|
812
866
|
const isLazyRequest = requestedCommand !== undefined && LAZY_COMMAND_NAMES.has(requestedCommand);
|
|
813
|
-
if (
|
|
867
|
+
if (requestedIsDisabled) {
|
|
868
|
+
// The brand turned this command off: register the full tree so the "did you
|
|
869
|
+
// mean" picker still works, then strip the disabled commands below so the
|
|
870
|
+
// request resolves as unknown.
|
|
871
|
+
await registerAllEagerCommands();
|
|
872
|
+
}
|
|
873
|
+
else if (requestedCommand !== undefined && !isLazyRequest) {
|
|
814
874
|
const known = await registerEagerForRequest(requestedCommand);
|
|
815
875
|
if (!known) {
|
|
816
876
|
// Unknown top-level command: register the full tree so the "did you mean"
|
|
@@ -828,10 +888,19 @@ if (requestedCommand !== undefined && !isLazyRequest) {
|
|
|
828
888
|
applyGlobalHelpConventions(program);
|
|
829
889
|
// Lazy commands pull in the SQLite-backed session/cloud stack; register them
|
|
830
890
|
// only when explicitly requested, keeping lightweight commands off that path.
|
|
831
|
-
if (isLazyRequest) {
|
|
891
|
+
if (isLazyRequest && !requestedIsDisabled) {
|
|
832
892
|
for (const loader of COMMAND_LOADERS[requestedCommand])
|
|
833
893
|
await reg(loader);
|
|
834
894
|
}
|
|
895
|
+
// White-label: remove any commands this brand disabled so they resolve as
|
|
896
|
+
// unknown (the command:* handler then reports "unknown command"). Unbranded or
|
|
897
|
+
// nothing-disabled → no-op. Done after all registration paths above.
|
|
898
|
+
if (brandDisabled.size > 0) {
|
|
899
|
+
const kept = program.commands.filter((c) => !brandDisabled.has(c.name()));
|
|
900
|
+
if (kept.length !== program.commands.length) {
|
|
901
|
+
program.commands = kept;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
835
904
|
// Pure documentation paths (--version / --help / -h) return immediately: skip
|
|
836
905
|
// the update check (PATH scan + cache read) and the detached background sync
|
|
837
906
|
// (spawns a child process) that every other invocation runs.
|