@phnx-labs/agents-cli 1.22.7 → 1.22.8
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 +44 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/browser.js +61 -0
- package/dist/commands/exec.js +49 -13
- package/dist/commands/harness.d.ts +0 -1
- package/dist/commands/harness.js +60 -4
- package/dist/commands/monitors.js +2 -2
- package/dist/commands/routines.js +2 -2
- package/dist/commands/run-account-picker.js +2 -0
- package/dist/commands/snapshot.d.ts +11 -0
- package/dist/commands/snapshot.js +107 -0
- package/dist/commands/teams.js +2 -1
- package/dist/commands/view.d.ts +7 -0
- package/dist/commands/view.js +1 -1
- package/dist/index.js +4 -1
- package/dist/lib/browser/ipc.js +2 -0
- package/dist/lib/browser/remote-control.d.ts +35 -0
- package/dist/lib/browser/remote-control.js +48 -0
- package/dist/lib/browser/service.d.ts +19 -0
- package/dist/lib/browser/service.js +19 -1
- package/dist/lib/browser/types.d.ts +14 -2
- package/dist/lib/device-config.js +8 -0
- package/dist/lib/hosts/passthrough.d.ts +10 -1
- package/dist/lib/hosts/passthrough.js +23 -2
- package/dist/lib/hosts/remote-cmd.js +1 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/placement.d.ts +82 -0
- package/dist/lib/placement.js +188 -0
- package/dist/lib/profiles.d.ts +31 -9
- package/dist/lib/profiles.js +83 -13
- package/dist/lib/rotate.d.ts +19 -4
- package/dist/lib/rotate.js +24 -1
- package/dist/lib/routines.d.ts +2 -0
- package/dist/lib/runner.d.ts +3 -0
- package/dist/lib/runner.js +90 -7
- 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/snapshot.d.ts +103 -0
- package/dist/lib/snapshot.js +99 -0
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.8
|
|
4
|
+
|
|
5
|
+
- **`agents browser` now gates cross-machine drives behind per-device consent.** `agents browser <cmd> --host <device>` already routes a browser command to another fleet machine over SSH and drives its browser — but nothing asked that machine's permission, so any box you could SSH to, you could drive. A new device-local `browser.remote-control` setting (off by default, never synced) fixes that: a fleet-remote `browser --host <this-machine> start` is refused with an actionable message until the owner runs `agents browser remote-control on` here. Local starts (no `--host`) are never gated. The fleet passthrough marks every `--host` dispatch with `AGENTS_FLEET_REMOTE` so the far side can tell a cross-machine drive from a local one. New command: `agents browser remote-control [on|off]` (no arg prints status; `--json` supported). Source: `apps/cli/src/lib/browser/remote-control.ts`, `apps/cli/src/commands/browser.ts`, `apps/cli/src/lib/hosts/passthrough.ts`, `apps/cli/src/lib/device-config.ts`, `apps/cli/docs/browser.md`.
|
|
6
|
+
|
|
7
|
+
- **`agents browser` tasks are now attributed to the caller that ran `start`, not
|
|
8
|
+
to the browser daemon.** `Task.owner` (RUSH-2020) was resolved with
|
|
9
|
+
`resolveActor()` *inside* the shared, long-lived browser daemon, so every task —
|
|
10
|
+
no matter which agent or person opened it — was stamped with the identity of
|
|
11
|
+
whoever happened to start the daemon. The caller's identity is now forwarded over
|
|
12
|
+
IPC: the CLI (the caller's own process) puts `actor` (`resolveActor().id`) and
|
|
13
|
+
`launchId` (`$AGENT_LAUNCH_ID`, the per-run id `exec.ts` injects for every harness)
|
|
14
|
+
on the `start` request, and the daemon stamps exactly those. Adds `Task.launchId` —
|
|
15
|
+
which run created a task — the scope a later `browser status --mine` and the
|
|
16
|
+
no-flag current-task default will filter on. Source:
|
|
17
|
+
`apps/cli/src/lib/browser/types.ts`, `apps/cli/src/lib/browser/service.ts`,
|
|
18
|
+
`apps/cli/src/lib/browser/ipc.ts`, `apps/cli/src/commands/browser.ts`.
|
|
19
|
+
|
|
20
|
+
- **`agents harness edit` and `agents harness rename` are now real commands.** `editProfile` and `renameProfile` already existed in `lib/profiles.ts` but nothing on the CLI surface reached them, so changing a custom harness meant hand-editing its YAML. `agents harness edit <name>` applies `--model`, `--base-url`, `--version`, and `--description` in place, preserving fork lineage (an edit never marks a harness as forked from itself). `agents harness rename <name> <new-name>` renames the YAML file and its `name` field, and rewrites `forkedFrom` on every harness that pointed at the old name so the fork graph stays accurate. There is deliberately no `--label`: the header `agents view` prints is derived from the harness name, so renaming is how you change it.
|
|
21
|
+
|
|
22
|
+
- **Run-time messages call a custom harness a "custom harness", not a "profile".** When you `agents run <name>` a custom harness (created with `agents harness add`), the CLI now says `Resolved custom harness '<name>'` and, for a discarded cost tier, `cost tiers don't apply to custom harness '<name>'` — instead of the legacy internal noun "profile". The `--strategy` and account-picker notices on a custom-harness run are aligned too. Behavior is unchanged; the legacy `agents profiles` alias still works. Source: `apps/cli/src/commands/exec.ts`.
|
|
23
|
+
|
|
24
|
+
- **Placement model + `agents run --where` (Phase 2 surface consolidation).**
|
|
25
|
+
"Where does the body run?" is one shared object (`local | device | fleet | cloud | lease`)
|
|
26
|
+
in `src/lib/placement.ts`. `agents run --where device:<name>|auto|lease[:backend]|local`
|
|
27
|
+
expands into the existing `--host` / `--lease` paths; mixing doors fails loud. Docs
|
|
28
|
+
(`00-concepts.md` § Placement, `hosts.md`) and help on run / routines / monitors teach
|
|
29
|
+
the matrix — including that monitors `--device` is **owner**, not body placement.
|
|
30
|
+
Old flags remain aliases. Source: `apps/cli/src/lib/placement.ts`, `apps/cli/src/commands/exec.ts`.
|
|
31
|
+
|
|
32
|
+
- **`agents harness fork` no longer accepts `--label` (breaking change).** The `--label` flag was used to set a human-facing display name for a custom harness. Display names are now always derived from the profile's `name` via a curated vendor/brand table (`deepseek-flash` → `DeepSeek Flash`, `spark` → `Spark`), so the flag is superfluous. Any script that passes `--label` to `agents harness fork` will receive a CLI error; remove the flag to migrate.
|
|
33
|
+
|
|
34
|
+
- **`agents run` no longer auto-picks an account whose token the server has already rejected.** Account rotation judged an account "signed in" from a local heuristic — a credential file is present and its email decodes — which cannot tell a good token from a revoked-but-unexpired one, so `balanced`/`available`/`run auto` could route into a `revoked` account and die at spawn ("session expired"). Eligibility now also reads the daemon's live auth-health probe (`auth-health.ts`): a `revoked` (401/403) account is excluded from the pick, reported as `revoked` by the pre-flight readiness check, shown as "needs re-login" in the account picker, and named in the teams throttle warning. Fail-open: a missing probe or any non-revoked verdict never blocks a launch (a cached `revoked` keeps gating until the daemon's next probe clears it). Source: `apps/cli/src/lib/rotate.ts`, `apps/cli/src/commands/run-account-picker.ts`, `apps/cli/src/commands/teams.ts`, `apps/cli/docs/hosts.md`.
|
|
35
|
+
|
|
36
|
+
- **Scheduled routines no longer overlap or outlive their configured timeout (RUSH-2186).** Detached cron, catchup, and monitor launches now take a cross-process per-routine claim and refuse a second fire while the prior run is alive. The configured deadline is persisted in run metadata; both the live runner and the restart-recovery monitor kill the owned process tree and record `timeout` when it expires. Source: `apps/cli/src/lib/runner.ts`, `apps/cli/src/lib/routines.ts`, `apps/cli/docs/03-routines.md`.
|
|
37
|
+
|
|
38
|
+
- **`agents snapshot` — one-process poll for inventory + active sessions (Phase 4 surface consolidation).**
|
|
39
|
+
Consumers (Factory, scripts, menubar) were forking `view --json` × N harnesses plus
|
|
40
|
+
`sessions --active --json` (and sometimes feed) on every tick. `agents snapshot --json`
|
|
41
|
+
returns the same shapes in one invocation: `inventory` (view), `sessions` (active rows),
|
|
42
|
+
optional `--with-feed` / `--with-sync`. Default sessions scope is this machine; `--all-hosts`
|
|
43
|
+
matches full `sessions --active` fan-out. Does **not** redefine `agents status`, which stays
|
|
44
|
+
the UnifiedSyncStatus sync contract. Source: `apps/cli/src/commands/snapshot.ts`,
|
|
45
|
+
`apps/cli/src/lib/snapshot.ts`.
|
|
46
|
+
|
|
3
47
|
## 1.22.7
|
|
4
48
|
|
|
5
49
|
- **`agents feed --project <name>` scopes the whole feed to one project.** Open
|
package/dist/bin/agents
CHANGED
|
Binary file
|
package/dist/commands/browser.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { listProfiles, getProfile, createProfile, deleteProfile, ensureDefaultBrowserProfile, getConfiguredDefaultProfileName, DEFAULT_BROWSER_PROFILE_NAME, getProfileRuntimeDir, extractConfiguredPort, findFreeProfilePort, getEndpointPresets, } from '../lib/browser/profiles.js';
|
|
4
4
|
import { updateMeta } from '../lib/state.js';
|
|
5
|
+
import { resolveActor } from '../lib/actor.js';
|
|
5
6
|
import { loginsForProfile, profilesLoggedInto, serviceForUrl, loginsWithAccountsForProfile, accountsForProfile, credKeysForService, AUTH_SIGNATURES, } from '../lib/browser/login-detection.js';
|
|
6
7
|
import { parseSecretRef } from '../lib/browser/secret-ref.js';
|
|
7
8
|
import { readAndResolveBundleEnv, isHeadlessSecretsContext, bundleExists, readBundle, describeBundle } from '../lib/secrets/bundles.js';
|
|
@@ -13,6 +14,8 @@ import { discoverBrowserWsUrl, verifyBrowserIdentity } from '../lib/browser/cdp.
|
|
|
13
14
|
import { parseTargetFilter } from '../lib/browser/service.js';
|
|
14
15
|
import { BrowserDaemonNotRunningError, formatBrowserDaemonNotRunningError, sendIPCRequest, } from '../lib/browser/ipc.js';
|
|
15
16
|
import { browserTaskPicker } from './browser-picker.js';
|
|
17
|
+
import { assertRemoteControlAllowed } from '../lib/browser/remote-control.js';
|
|
18
|
+
import { getConfigValue, setConfigValue } from '../lib/device-config.js';
|
|
16
19
|
import { isInteractiveTerminal } from './utils.js';
|
|
17
20
|
import { registerCommandGroups, setHelpSections } from '../lib/help.js';
|
|
18
21
|
import { buildHar } from '../lib/browser/har.js';
|
|
@@ -76,6 +79,12 @@ export function registerBrowserCommand(program) {
|
|
|
76
79
|
agents browser navigate https://example.com
|
|
77
80
|
agents browser screenshot
|
|
78
81
|
|
|
82
|
+
# Drive another machine's browser (needs its consent — see remote-control)
|
|
83
|
+
agents browser start --host zion
|
|
84
|
+
|
|
85
|
+
# Allow / deny other fleet machines driving THIS machine's browser
|
|
86
|
+
agents browser remote-control on
|
|
87
|
+
|
|
79
88
|
# End the session when done
|
|
80
89
|
agents browser done
|
|
81
90
|
`,
|
|
@@ -609,6 +618,42 @@ function registerProfilesCommands(browser) {
|
|
|
609
618
|
});
|
|
610
619
|
}
|
|
611
620
|
function registerTaskCommands(browser) {
|
|
621
|
+
browser
|
|
622
|
+
.command('remote-control [state]')
|
|
623
|
+
.description("Allow or deny other fleet machines driving THIS machine's browser over `browser --host`. " +
|
|
624
|
+
'`on`/`off` to set (device-local, never synced); no argument prints the current value. Default off.')
|
|
625
|
+
.option('--json', 'Output as JSON')
|
|
626
|
+
.action((state, opts) => {
|
|
627
|
+
const KEY = 'browser.remote-control';
|
|
628
|
+
if (state === undefined) {
|
|
629
|
+
const cur = getConfigValue(KEY).value === true;
|
|
630
|
+
if (opts.json) {
|
|
631
|
+
console.log(JSON.stringify({ remoteControl: cur }));
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
console.log(`Remote browser control (this machine): ${cur ? 'on' : 'off'}`);
|
|
635
|
+
if (!cur)
|
|
636
|
+
console.log('Enable with: agents browser remote-control on');
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const norm = state.toLowerCase();
|
|
640
|
+
const onWords = ['on', 'true', 'yes', 'allow', 'enable'];
|
|
641
|
+
const offWords = ['off', 'false', 'no', 'deny', 'disable'];
|
|
642
|
+
if (!onWords.includes(norm) && !offWords.includes(norm)) {
|
|
643
|
+
console.error(`Expected "on" or "off", got "${state}".`);
|
|
644
|
+
process.exit(1);
|
|
645
|
+
}
|
|
646
|
+
const value = onWords.includes(norm);
|
|
647
|
+
setConfigValue(KEY, value);
|
|
648
|
+
if (opts.json) {
|
|
649
|
+
console.log(JSON.stringify({ remoteControl: value }));
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
console.log(`Remote browser control (this machine) is now ${value ? 'on' : 'off'}.`);
|
|
653
|
+
console.log(value
|
|
654
|
+
? 'Other fleet machines can now drive this browser via `browser --host <this-device>`.'
|
|
655
|
+
: 'Cross-machine `browser --host` drives to this machine are refused.');
|
|
656
|
+
});
|
|
612
657
|
browser
|
|
613
658
|
.command('start')
|
|
614
659
|
.description('Start a browser task. Pass --profile <name>; omit to use your configured default (`agents browser profiles set-default`), else auto-pick an installed Chromium-family browser.')
|
|
@@ -622,6 +667,16 @@ function registerTaskCommands(browser) {
|
|
|
622
667
|
.option('--duration <sec>', 'Recording duration cap in seconds (with --record; default 60)', (v) => parseInt(v, 10))
|
|
623
668
|
.option('--max-mb <mb>', 'Recording size cap in MB (with --record; default 25)', (v) => parseInt(v, 10))
|
|
624
669
|
.action(async (opts) => {
|
|
670
|
+
// Consent gate: a fleet-remote `browser --host <this-machine> start` may
|
|
671
|
+
// only open a browser here if the owner opted in. Refuse before we resolve
|
|
672
|
+
// or auto-create any profile. Local starts are never gated.
|
|
673
|
+
try {
|
|
674
|
+
assertRemoteControlAllowed();
|
|
675
|
+
}
|
|
676
|
+
catch (err) {
|
|
677
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
678
|
+
process.exit(1);
|
|
679
|
+
}
|
|
625
680
|
let profileName = opts.profile;
|
|
626
681
|
if (!profileName) {
|
|
627
682
|
try {
|
|
@@ -698,6 +753,12 @@ function registerTaskCommands(browser) {
|
|
|
698
753
|
url: opts.url,
|
|
699
754
|
endpoint: opts.endpoint,
|
|
700
755
|
skipDomainSkill: opts.skills === false,
|
|
756
|
+
// Forward the caller's identity: the browser daemon is shared, so it
|
|
757
|
+
// cannot resolve who/which-run called `start`. `resolveActor()` runs
|
|
758
|
+
// here in the CLI (the caller's process); `$AGENT_LAUNCH_ID` is the
|
|
759
|
+
// per-run id exec.ts injects for every harness.
|
|
760
|
+
actor: resolveActor().id,
|
|
761
|
+
launchId: process.env.AGENT_LAUNCH_ID,
|
|
701
762
|
});
|
|
702
763
|
if (!response.ok) {
|
|
703
764
|
console.error(response.error);
|
package/dist/commands/exec.js
CHANGED
|
@@ -553,13 +553,14 @@ export function registerRunCommand(program) {
|
|
|
553
553
|
.option('--budget <tokens>', 'Loop token hard-cap: stop once cumulative tokens reach this (stoppedBy: budget), enforced outside the agent. Loop only.')
|
|
554
554
|
.option('--until <signal>', 'Loop stop condition. `signal` reads <runDir>/loop-signal.json {continue,reason} each iteration; absent or continue:false stops (fail-closed). Loop only.')
|
|
555
555
|
.option('--interval <dur>', 'Loop delay between iterations ("0" back-to-back, "30m" paces). Loop only.')
|
|
556
|
-
.option('--
|
|
557
|
-
.option('--
|
|
556
|
+
.option('--where <spec>', 'Where this run\'s body executes (one placement door): local | device:<name> | auto | lease[:backend]. Expands to --host/--lease. Do not combine with those flags. See docs/00-concepts.md#placement.')
|
|
557
|
+
.option('--host <name>', 'Offload this run onto another machine over SSH — a device name, registered host, or user@host. Pass "auto" to pick from 14d usage affinity (most-used online device has highest probability). Same as --where device:<name>. See `agents devices`.')
|
|
558
|
+
.option('--device <name>', 'Alias of --host. Pass "auto" for affinity-based device pick (same as --where auto).')
|
|
558
559
|
.option('--remote-cwd <dir>', "Explicit host working directory for --host runs, used VERBATIM (overrides --cwd; usually --cwd suffices — it re-roots a local-home path onto the remote home). Pass a single-quoted '$HOME/…' or a valid remote absolute path; a local ~ expands here and won't exist there (/Users/you vs /home/you).")
|
|
559
560
|
.option('--no-follow', 'With --host, dispatch detached and return immediately (track via `agents hosts ps/logs`).')
|
|
560
561
|
.option('--any', 'With --host <cap> (a capability tag), pick any matching host instead of erroring when several match.')
|
|
561
562
|
.option('--copy-creds', 'With --host, copy the picked runtime credentials (and Claude OAuth token) to the host, then shred them after the run. Opt-in per run.')
|
|
562
|
-
.option('--lease [backend]', 'Invent a disposable cloud box for this run and tear it down after (via crabbox). Optional backend selects the cloud (hetzner/aws/do). Unlike --host, no machine is registered.')
|
|
563
|
+
.option('--lease [backend]', 'Invent a disposable cloud box for this run and tear it down after (via crabbox). Optional backend selects the cloud (hetzner/aws/do). Same as --where lease[:backend]. Unlike --host, no machine is registered.')
|
|
563
564
|
.option('--box <slug>', 'Reuse an existing warm crabbox box for this run instead of provisioning a disposable --lease box.')
|
|
564
565
|
.option('--keep-box', 'With --lease, keep the box after the run instead of stopping it.')
|
|
565
566
|
.option('--reuse', 'With --lease, reuse the most-recently-used warm box if one exists (else provision fresh). The scriptable form of the interactive reuse picker.')
|
|
@@ -601,6 +602,11 @@ export function registerRunCommand(program) {
|
|
|
601
602
|
agents run auto "fix the flaky test" --mode edit
|
|
602
603
|
agents run auto --host yosemite-s0 "fix the flaky test" # pin the host
|
|
603
604
|
|
|
605
|
+
# Placement (one door — where the body runs). Old flags still work.
|
|
606
|
+
agents run claude "…" --where device:yosemite-s0 # = --host yosemite-s0
|
|
607
|
+
agents run claude "…" --where auto # = --device auto
|
|
608
|
+
agents run claude "fix CI" --where lease --mode edit
|
|
609
|
+
|
|
604
610
|
# Open the session in a terminal tab — detected from where your sessions
|
|
605
611
|
# already run (Ghostty / iTerm / Terminal.app); force one with a value
|
|
606
612
|
agents run claude --terminal
|
|
@@ -688,6 +694,36 @@ export function registerRunCommand(program) {
|
|
|
688
694
|
await handleTerminalHandoff(agentSpec, options, prompt);
|
|
689
695
|
return;
|
|
690
696
|
}
|
|
697
|
+
// Placement: --where expands into --host / --lease before any dispatch.
|
|
698
|
+
// One door for "where does the body run?" — old flags remain aliases.
|
|
699
|
+
// See lib/placement.ts and docs/00-concepts.md#placement.
|
|
700
|
+
{
|
|
701
|
+
const { placementFromRunFlags, expandPlacementToRunFlags, PlacementError } = await import('../lib/placement.js');
|
|
702
|
+
try {
|
|
703
|
+
const placement = placementFromRunFlags(options);
|
|
704
|
+
if (options.where) {
|
|
705
|
+
const expanded = expandPlacementToRunFlags(placement);
|
|
706
|
+
if (expanded.host !== undefined)
|
|
707
|
+
options.host = expanded.host;
|
|
708
|
+
if (expanded.device !== undefined)
|
|
709
|
+
options.device = expanded.device;
|
|
710
|
+
if (expanded.lease !== undefined)
|
|
711
|
+
options.lease = expanded.lease;
|
|
712
|
+
if (expanded.box !== undefined)
|
|
713
|
+
options.box = expanded.box;
|
|
714
|
+
// Clear the where flag so remote re-entry (host dispatch) does not
|
|
715
|
+
// re-expand and conflict with the concrete host we just set.
|
|
716
|
+
options.where = undefined;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
catch (err) {
|
|
720
|
+
if (err instanceof PlacementError) {
|
|
721
|
+
console.error(chalk.red(err.message));
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
throw err;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
691
727
|
// --notify: post a desktop notification when this run finishes. Armed on
|
|
692
728
|
// process exit so it covers EVERY dispatch path below (local, --host,
|
|
693
729
|
// --lease, the error path) instead of one branch. Only for headless runs
|
|
@@ -1615,7 +1651,7 @@ export function registerRunCommand(program) {
|
|
|
1615
1651
|
const cwd = options.cwd ?? process.cwd();
|
|
1616
1652
|
if (accountPickerRequested && !isValidAgent(rawAgent)) {
|
|
1617
1653
|
if (profileExists(rawAgent)) {
|
|
1618
|
-
console.error(chalk.red(`Account selection is not available for
|
|
1654
|
+
console.error(chalk.red(`Account selection is not available for custom harness '${rawAgent}'. Run its concrete host agent with @ instead.`));
|
|
1619
1655
|
process.exit(1);
|
|
1620
1656
|
}
|
|
1621
1657
|
if (resolveWorkflowRef(rawAgent, cwd)) {
|
|
@@ -1675,7 +1711,7 @@ export function registerRunCommand(program) {
|
|
|
1675
1711
|
profileEnv = resolved.env;
|
|
1676
1712
|
profileFallbackModel = resolved.fallbackModel;
|
|
1677
1713
|
fromProfile = true;
|
|
1678
|
-
process.stderr.write(chalk.gray(`Resolved
|
|
1714
|
+
process.stderr.write(chalk.gray(`Resolved custom harness '${resolved.profileName}' -> ${agent}${version ? `@${version}` : ''}\n`));
|
|
1679
1715
|
if (resolved.tierNote) {
|
|
1680
1716
|
process.stderr.write(chalk.gray(`[agents] ${resolved.tierNote}\n`));
|
|
1681
1717
|
}
|
|
@@ -1685,7 +1721,7 @@ export function registerRunCommand(program) {
|
|
|
1685
1721
|
// native, HOST-catalog tier block below. When the profile has no
|
|
1686
1722
|
// `models:` opt-in at all, resolvedModel stays undefined and
|
|
1687
1723
|
// options.model is left as the raw tier token on purpose — the
|
|
1688
|
-
// "cost tiers don't apply to
|
|
1724
|
+
// "cost tiers don't apply to custom harness ..." discard guard further
|
|
1689
1725
|
// down this function is the canonical fallback for that case, and
|
|
1690
1726
|
// this block must not race it with a second, differently-worded
|
|
1691
1727
|
// message.
|
|
@@ -1894,7 +1930,7 @@ export function registerRunCommand(program) {
|
|
|
1894
1930
|
else {
|
|
1895
1931
|
console.error(chalk.red(`Unknown agent: ${rawAgent}`));
|
|
1896
1932
|
console.error(chalk.gray(`Available agents: ${ALL_AGENT_IDS.join(', ')}`));
|
|
1897
|
-
console.error(chalk.gray(`Or add a
|
|
1933
|
+
console.error(chalk.gray(`Or add a custom harness: agents harness add <name>`));
|
|
1898
1934
|
process.exit(1);
|
|
1899
1935
|
}
|
|
1900
1936
|
}
|
|
@@ -2062,7 +2098,7 @@ export function registerRunCommand(program) {
|
|
|
2062
2098
|
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} ignored: version ${version} is pinned\n`));
|
|
2063
2099
|
}
|
|
2064
2100
|
else if (fromProfile) {
|
|
2065
|
-
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} ignored:
|
|
2101
|
+
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} ignored: custom harness pins its own version/auth\n`));
|
|
2066
2102
|
}
|
|
2067
2103
|
else {
|
|
2068
2104
|
try {
|
|
@@ -2312,12 +2348,12 @@ export function registerRunCommand(program) {
|
|
|
2312
2348
|
? (workflowModel ?? (options.fallback ? undefined : runDefaults.model))
|
|
2313
2349
|
: undefined);
|
|
2314
2350
|
// Cost tiers (cheap|default|best|ultra) resolve against a harness's own model
|
|
2315
|
-
// catalog. A
|
|
2316
|
-
// tier here would forward an incompatible host-harness model to a
|
|
2317
|
-
// Discard it loudly and let the
|
|
2351
|
+
// catalog. A custom harness's model comes from its endpoint, not the host
|
|
2352
|
+
// harness, so a tier here would forward an incompatible host-harness model to a
|
|
2353
|
+
// different API. Discard it loudly and let the custom harness's own model stand.
|
|
2318
2354
|
if (fromProfile && model && isTierToken(model)) {
|
|
2319
|
-
process.stderr.write(chalk.yellow(`[agents] --model ${model}: cost tiers don't apply to
|
|
2320
|
-
`(its model comes from the endpoint) — ignoring the tier, using the
|
|
2355
|
+
process.stderr.write(chalk.yellow(`[agents] --model ${model}: cost tiers don't apply to custom harness '${rawAgent}' ` +
|
|
2356
|
+
`(its model comes from the endpoint) — ignoring the tier, using the custom harness's configured model\n`));
|
|
2321
2357
|
model = undefined;
|
|
2322
2358
|
}
|
|
2323
2359
|
const execOptions = {
|
package/dist/commands/harness.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
import { addProfile, ensureProviderToken } from './profiles.js';
|
|
15
|
-
import { listProfiles, readProfile, writeProfile, deleteProfile, profileExists, profileHostLabel, profileProviderLabel, profileModelLabel, profileAuthLabel, profileLabel, forkProfile, profileFromHostModel, authEnvKeyForHost, getProfilePath, validateProfileName, } from '../lib/profiles.js';
|
|
15
|
+
import { listProfiles, readProfile, writeProfile, deleteProfile, profileExists, profileHostLabel, profileProviderLabel, profileModelLabel, profileAuthLabel, profileLabel, forkProfile, profileFromHostModel, authEnvKeyForHost, getProfilePath, validateProfileName, editProfile, renameProfile, } from '../lib/profiles.js';
|
|
16
16
|
import { listPresets } from '../lib/profiles-presets.js';
|
|
17
17
|
import { AGENTS, ALL_AGENT_IDS, resolveAgentName } from '../lib/agents.js';
|
|
18
18
|
/** Short capability summary for a native harness — its supported run modes. */
|
|
@@ -58,7 +58,6 @@ export function buildFork(source, name, opts) {
|
|
|
58
58
|
baseUrl: opts.baseUrl,
|
|
59
59
|
provider: opts.authProvider,
|
|
60
60
|
version: opts.version,
|
|
61
|
-
label: opts.label,
|
|
62
61
|
description: opts.description,
|
|
63
62
|
});
|
|
64
63
|
}
|
|
@@ -75,7 +74,6 @@ export function buildFork(source, name, opts) {
|
|
|
75
74
|
baseUrl: opts.baseUrl,
|
|
76
75
|
provider: opts.authProvider,
|
|
77
76
|
authEnvVar: opts.authProvider ? authEnvKeyForHostOrThrow(host) : undefined,
|
|
78
|
-
label: opts.label,
|
|
79
77
|
description: opts.description ?? `Forked from ${host}: ${opts.model}`,
|
|
80
78
|
});
|
|
81
79
|
}
|
|
@@ -147,7 +145,6 @@ Examples:
|
|
|
147
145
|
.option('--base-url <url>', 'Custom endpoint base URL (claude/codex hosts)')
|
|
148
146
|
.option('--auth-provider <provider>', 'Attach a keychain-backed API key under this provider')
|
|
149
147
|
.option('--version <version>', 'Pin the host CLI version (e.g., 1.16.0)')
|
|
150
|
-
.option('--label <text>', 'Human-facing name shown by `agents view` (defaults to <name>)')
|
|
151
148
|
.option('--description <text>', 'One-line description')
|
|
152
149
|
.option('--key-stdin', 'Read the API key from stdin instead of prompting (for scripts/CI)')
|
|
153
150
|
.option('--force', 'Overwrite an existing harness with the same name')
|
|
@@ -249,4 +246,63 @@ Examples:
|
|
|
249
246
|
}
|
|
250
247
|
console.log(chalk.green(`Harness '${name}' removed.`));
|
|
251
248
|
});
|
|
249
|
+
cmd
|
|
250
|
+
.command('edit <name>')
|
|
251
|
+
.description('Edit a custom harness in place (model, endpoint, host version, description).')
|
|
252
|
+
// Flags mirror ForkProfileOptions exactly — `editProfile` reuses
|
|
253
|
+
// `forkProfile`'s override logic, so anything it does not accept would be a
|
|
254
|
+
// flag that parses and does nothing. There is deliberately no --label: the
|
|
255
|
+
// header `agents view` prints is derived from the harness name.
|
|
256
|
+
.option('--model <id>', 'Swap the pinned model (e.g., meta/muse-spark-2.0)')
|
|
257
|
+
.option('--base-url <url>', 'Set or replace the endpoint base URL (claude/codex hosts)')
|
|
258
|
+
.option('--version <ver>', 'Re-pin the host CLI version. Empty string unpins (tracks latest).')
|
|
259
|
+
.option('--description <text>', 'Set the one-line description')
|
|
260
|
+
.addHelpText('after', `
|
|
261
|
+
Examples:
|
|
262
|
+
# Upgrade the pinned model
|
|
263
|
+
agents harness edit spark --model meta/muse-spark-2.0
|
|
264
|
+
|
|
265
|
+
# Point to a new private endpoint
|
|
266
|
+
agents harness edit corp --base-url https://gw.corp/v2
|
|
267
|
+
|
|
268
|
+
# Track the host's latest instead of a pinned version
|
|
269
|
+
agents harness edit spark --version ""
|
|
270
|
+
|
|
271
|
+
# The display name is derived from the harness name, so to change it, rename:
|
|
272
|
+
agents harness rename spark muse-spark
|
|
273
|
+
`)
|
|
274
|
+
.action((name, opts) => {
|
|
275
|
+
try {
|
|
276
|
+
const edited = editProfile(readProfile(name), opts);
|
|
277
|
+
writeProfile(edited);
|
|
278
|
+
console.log(chalk.green(`Harness '${name}' updated.`));
|
|
279
|
+
console.log(chalk.gray(`Model: ${profileModelLabel(edited)}`));
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
console.error(chalk.red(err.message));
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
cmd
|
|
287
|
+
.command('rename <name> <new-name>')
|
|
288
|
+
.description('Rename a custom harness — updates the file name and its internal name field.')
|
|
289
|
+
.addHelpText('after', `
|
|
290
|
+
Examples:
|
|
291
|
+
# Rename 'spark' to 'muse'
|
|
292
|
+
agents harness rename spark muse
|
|
293
|
+
|
|
294
|
+
# Rename then run under the new name
|
|
295
|
+
agents harness rename deepseek ds && agents run ds "hello"
|
|
296
|
+
`)
|
|
297
|
+
.action((name, newName) => {
|
|
298
|
+
try {
|
|
299
|
+
renameProfile(name, newName);
|
|
300
|
+
console.log(chalk.green(`Harness '${name}' renamed to '${newName}'.`));
|
|
301
|
+
console.log(chalk.gray(`Run: agents run ${newName} "hello"`));
|
|
302
|
+
}
|
|
303
|
+
catch (err) {
|
|
304
|
+
console.error(chalk.red(err.message));
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
252
308
|
}
|
|
@@ -315,9 +315,9 @@ export function registerMonitorsCommands(program) {
|
|
|
315
315
|
.option('--notify [channel]', 'Notify the owner (notify.owner); [channel] overrides the owner channel')
|
|
316
316
|
.option('--webhook-out <url>', 'POST the event to this URL')
|
|
317
317
|
// PLACEMENT / hygiene
|
|
318
|
-
.option('--device <name>', 'OWNER
|
|
318
|
+
.option('--device <name>', 'OWNER (not body placement) — the single machine that evaluates + fires (exactly-once). See docs/00-concepts.md#placement.')
|
|
319
319
|
.option('--devices <list>', 'Allowlist (comma-separated): each device fires independently')
|
|
320
|
-
.option('--run-on <host>', '
|
|
320
|
+
.option('--run-on <host>', 'BODY placement — execute the ACTION on this machine over SSH (same idea as run --where device:<host>)')
|
|
321
321
|
.option('--rate-limit <spec>', 'Auto-pause if it fires more than N/<interval> (e.g. 5/1m)')
|
|
322
322
|
.option('--disabled', 'Create the monitor paused (enable later with resume)')
|
|
323
323
|
.action(async (nameOrPath, options) => {
|
|
@@ -628,8 +628,8 @@ export function registerRoutinesCommands(program) {
|
|
|
628
628
|
.option('-t, --timeout <timeout>', 'Kill the agent if it runs longer than this (e.g., 10m, 2h, 3d, 1w; max 1w)', '10m')
|
|
629
629
|
.option('--timezone <tz>', 'Interpret schedule in this timezone (e.g., America/Los_Angeles)')
|
|
630
630
|
.option('--devices <names>', 'Fleet allowlist (comma-separated): only listed devices schedule and fire this routine. Omit for unrestricted.')
|
|
631
|
-
.option('--run-on <name>', '
|
|
632
|
-
.option('--placement <strategy>', `
|
|
631
|
+
.option('--run-on <name>', 'BODY placement: execute the job body on this machine over SSH (registered host, device, capability tag, or user@host). Sets hostStrategy=host. Not eligibility — see --devices. Auto-pins devices to THIS machine unless --devices is given. Same model as agents run --where device:<name> (docs/00-concepts.md#placement).')
|
|
632
|
+
.option('--placement <strategy>', `BODY placement strategy: ${HOST_STRATEGIES.join('|')} (default: local, or host when --run-on is set). Maps to the shared Placement model (local|device|fleet|cloud). Not the same as --host (which manages routines on a remote machine).`)
|
|
633
633
|
.option('--run-cwd <dir>', 'Working directory on the --run-on host (--remote-cwd is taken by the remote-management passthrough)')
|
|
634
634
|
.option('--at <time>', 'One-shot mode: run once at this time (e.g., "14:30" or "2026-02-24 09:00"), then disable')
|
|
635
635
|
.option('--on <source:event>', 'Webhook trigger instead of/in addition to a schedule: github:pull_request or linear:Issue')
|
|
@@ -36,6 +36,8 @@ function disabledReason(candidate) {
|
|
|
36
36
|
return undefined;
|
|
37
37
|
if (readiness.reason === 'signed_out')
|
|
38
38
|
return 'logged out';
|
|
39
|
+
if (readiness.reason === 'revoked')
|
|
40
|
+
return 'needs re-login';
|
|
39
41
|
if (readiness.reason === 'out_of_credits')
|
|
40
42
|
return 'out of credits';
|
|
41
43
|
const windows = candidate.usageSnapshot?.windows ?? [];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents snapshot` — one-process consumer snapshot for pollers.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the N× `view --json` + `sessions --active --json` (+ optional feed)
|
|
5
|
+
* fork storm with a single command. Does NOT replace `agents status`, which
|
|
6
|
+
* remains the UnifiedSyncStatus sync contract for menubar / Agency drift.
|
|
7
|
+
*
|
|
8
|
+
* JSON shape: {@link FleetSnapshot} in `lib/snapshot.ts` (version: 1).
|
|
9
|
+
*/
|
|
10
|
+
import type { Command } from 'commander';
|
|
11
|
+
export declare function registerSnapshotCommand(program: Command): void;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents snapshot` — one-process consumer snapshot for pollers.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the N× `view --json` + `sessions --active --json` (+ optional feed)
|
|
5
|
+
* fork storm with a single command. Does NOT replace `agents status`, which
|
|
6
|
+
* remains the UnifiedSyncStatus sync contract for menubar / Agency drift.
|
|
7
|
+
*
|
|
8
|
+
* JSON shape: {@link FleetSnapshot} in `lib/snapshot.ts` (version: 1).
|
|
9
|
+
*/
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { addHostOption } from '../lib/hosts/option.js';
|
|
12
|
+
import { setHelpSections } from '../lib/help.js';
|
|
13
|
+
import { resolveAgentName, AGENTS } from '../lib/agents.js';
|
|
14
|
+
import { computeSnapshot } from '../lib/snapshot.js';
|
|
15
|
+
import { resolveSurface } from './utils.js';
|
|
16
|
+
function resolveAgentFilter(raw) {
|
|
17
|
+
if (!raw)
|
|
18
|
+
return undefined;
|
|
19
|
+
const name = resolveAgentName(raw.split('@')[0]);
|
|
20
|
+
if (!name) {
|
|
21
|
+
console.error(chalk.red(`Unknown agent "${raw}".`));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
return name;
|
|
25
|
+
}
|
|
26
|
+
/** Compact human summary — the JSON path is the real contract. */
|
|
27
|
+
function renderHuman(snap) {
|
|
28
|
+
console.log(chalk.bold('Fleet snapshot') + chalk.gray(` · ${snap.host} · ${snap.capturedAt}`));
|
|
29
|
+
const invParts = [];
|
|
30
|
+
for (const a of snap.inventory) {
|
|
31
|
+
if (a.versions.length === 0)
|
|
32
|
+
continue;
|
|
33
|
+
const def = a.versions.find((v) => v.isDefault) ?? a.versions[0];
|
|
34
|
+
const label = AGENTS[a.agent]?.name ?? a.agent;
|
|
35
|
+
const signed = def.signedIn ? chalk.green('in') : chalk.gray('out');
|
|
36
|
+
invParts.push(`${label}@${def.version} (${signed})`);
|
|
37
|
+
}
|
|
38
|
+
console.log(` ${'Inventory'.padEnd(12)} ${invParts.length ? invParts.join(chalk.gray(' · ')) : chalk.gray('(none installed)')}`);
|
|
39
|
+
const { running, live, byContext, byAgent } = snap.agents;
|
|
40
|
+
const ctx = Object.entries(byContext)
|
|
41
|
+
.map(([k, n]) => `${k}:${n}`)
|
|
42
|
+
.join(' ');
|
|
43
|
+
const ag = Object.entries(byAgent)
|
|
44
|
+
.map(([k, n]) => `${k}:${n}`)
|
|
45
|
+
.join(' ');
|
|
46
|
+
console.log(` ${'Sessions'.padEnd(12)} ${live} live · ${running} running` +
|
|
47
|
+
(snap.remoteDeviceCount > 0 ? chalk.gray(` · ${snap.remoteDeviceCount} remote device(s)`) : '') +
|
|
48
|
+
(ctx ? chalk.gray(` · ${ctx}`) : '') +
|
|
49
|
+
(ag ? chalk.gray(` · ${ag}`) : ''));
|
|
50
|
+
if (snap.feed) {
|
|
51
|
+
console.log(` ${'Feed'.padEnd(12)} ${snap.feed.openBlocks} open block${snap.feed.openBlocks === 1 ? '' : 's'}`);
|
|
52
|
+
}
|
|
53
|
+
if (snap.sync) {
|
|
54
|
+
const need = snap.sync.agents.filter((a) => a.needsSync).length;
|
|
55
|
+
const sys = snap.sync.system.unknown
|
|
56
|
+
? 'system unknown'
|
|
57
|
+
: snap.sync.system.behind > 0
|
|
58
|
+
? `system ${snap.sync.system.behind} behind`
|
|
59
|
+
: 'system ok';
|
|
60
|
+
console.log(` ${'Sync'.padEnd(12)} ${sys} · ${need} version${need === 1 ? '' : 's'} need sync`);
|
|
61
|
+
}
|
|
62
|
+
console.log(chalk.gray('\n Machine-readable: agents snapshot --json\n' +
|
|
63
|
+
' Sync-only (unchanged): agents status --json'));
|
|
64
|
+
}
|
|
65
|
+
export function registerSnapshotCommand(program) {
|
|
66
|
+
const cmd = addHostOption(program
|
|
67
|
+
.command('snapshot')
|
|
68
|
+
.description('One-process poll snapshot: install inventory + active sessions (optional feed/sync). Not the sync-status command — use `agents status` for drift.'))
|
|
69
|
+
.option('--json', 'Emit the machine-readable FleetSnapshot contract (version 1)')
|
|
70
|
+
.option('--all-hosts', 'Include remote devices in the sessions gather (same fan-out as `sessions --active`; default is this machine only)')
|
|
71
|
+
.option('--with-feed', 'Include open feed-block summary (needs-you count + compact rows)')
|
|
72
|
+
.option('--with-sync', 'Include UnifiedSyncStatus (same engine as `agents status`; opt-in)')
|
|
73
|
+
.option('--agent <name>', 'Restrict inventory to one agent (e.g. claude)');
|
|
74
|
+
setHelpSections(cmd, {
|
|
75
|
+
examples: `
|
|
76
|
+
# One-shot inventory + local active sessions (JSON)
|
|
77
|
+
agents snapshot --json
|
|
78
|
+
|
|
79
|
+
# Same, plus open feed blocks for needs-you polls
|
|
80
|
+
agents snapshot --json --with-feed
|
|
81
|
+
|
|
82
|
+
# Fleet-wide active sessions (matches sessions --active scope)
|
|
83
|
+
agents snapshot --json --all-hosts
|
|
84
|
+
|
|
85
|
+
# Inventory for one harness only (Factory-style usage poll)
|
|
86
|
+
agents snapshot --json --agent claude
|
|
87
|
+
|
|
88
|
+
# Run the whole snapshot on another device
|
|
89
|
+
agents snapshot --json --host yosemite-s0
|
|
90
|
+
`,
|
|
91
|
+
});
|
|
92
|
+
cmd.action(async (opts, command) => {
|
|
93
|
+
const surface = resolveSurface(command);
|
|
94
|
+
const agent = resolveAgentFilter(opts.agent);
|
|
95
|
+
const snap = await computeSnapshot({
|
|
96
|
+
agent,
|
|
97
|
+
local: opts.allHosts !== true,
|
|
98
|
+
withFeed: opts.withFeed === true,
|
|
99
|
+
withSync: opts.withSync === true,
|
|
100
|
+
});
|
|
101
|
+
if (surface.json || opts.json) {
|
|
102
|
+
console.log(JSON.stringify(snap, null, 2));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
renderHuman(snap);
|
|
106
|
+
});
|
|
107
|
+
}
|
package/dist/commands/teams.js
CHANGED
|
@@ -275,7 +275,8 @@ function throttleWarningLine(agent, version, r) {
|
|
|
275
275
|
const acct = r.email ? ` (${r.email})` : '';
|
|
276
276
|
const reason = r.reason === 'out_of_credits' ? 'is out of credits'
|
|
277
277
|
: r.reason === 'signed_out' ? 'is not signed in'
|
|
278
|
-
: '
|
|
278
|
+
: r.reason === 'revoked' ? 'needs re-login (its token was revoked)'
|
|
279
|
+
: 'is rate-limited right now';
|
|
279
280
|
return (chalk.yellow(`⚠ ${who}${acct} ${reason}.`) +
|
|
280
281
|
chalk.gray(`\n A pinned version skips account rotation, so it will launch on this account and may immediately hit its limit.` +
|
|
281
282
|
`\n Use a bare \`${agent}\` teammate to let the team pick a healthy account, or pass --force to silence this.`));
|
package/dist/commands/view.d.ts
CHANGED
|
@@ -129,6 +129,13 @@ export declare function parseResourceSections(options: {
|
|
|
129
129
|
resources?: string | boolean;
|
|
130
130
|
detailed?: boolean;
|
|
131
131
|
} & ViewSectionFilter, jsonMode: boolean): Set<ResourceSection>;
|
|
132
|
+
/**
|
|
133
|
+
* Collect structured info for one or more agents without rendering to the
|
|
134
|
+
* terminal. Used by `--json` output and any programmatic consumer (e.g. the
|
|
135
|
+
* agents-cli extension's "resume current session in best available version"
|
|
136
|
+
* command).
|
|
137
|
+
*/
|
|
138
|
+
export declare function collectAgentsJson(filterAgentId?: AgentId, resourceSections?: Set<ResourceSection>): Promise<ViewJsonAgent[]>;
|
|
132
139
|
/**
|
|
133
140
|
* Identity key for duplicate-install detection. Prefers accountKey — which
|
|
134
141
|
* encodes account AND org — over the bare email: two installs can share an
|
package/dist/commands/view.js
CHANGED
|
@@ -1242,7 +1242,7 @@ export function parseResourceSections(options, jsonMode) {
|
|
|
1242
1242
|
* agents-cli extension's "resume current session in best available version"
|
|
1243
1243
|
* command).
|
|
1244
1244
|
*/
|
|
1245
|
-
async function collectAgentsJson(filterAgentId, resourceSections) {
|
|
1245
|
+
export async function collectAgentsJson(filterAgentId, resourceSections) {
|
|
1246
1246
|
const agentsToShow = filterAgentId ? [filterAgentId] : ALL_AGENT_IDS;
|
|
1247
1247
|
const wantResources = !!resourceSections && resourceSections.size > 0;
|
|
1248
1248
|
// Only pay for the git-status + resource scans when resources were requested.
|
package/dist/index.js
CHANGED
|
@@ -94,7 +94,7 @@ if (IS_DEV_BUILD) {
|
|
|
94
94
|
// module on each invocation (which loaded the whole ~50-module tree before the
|
|
95
95
|
// first byte of output), the registry maps a command name to a thunk that
|
|
96
96
|
// imports only what that command needs. See src/lib/startup/command-registry.ts.
|
|
97
|
-
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadExport, loadPackages, loadRoutines, loadMonitors, loadProjects, loadRun, loadFork, loadDefaults, loadSet, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadStatus, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadPerf, loadTrends, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadFeed, loadMailboxes, } from './lib/startup/command-registry.js';
|
|
97
|
+
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadExport, loadPackages, loadRoutines, loadMonitors, loadProjects, loadRun, loadFork, loadDefaults, loadSet, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadStatus, loadSnapshot, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadPerf, loadTrends, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadFeed, loadMailboxes, } from './lib/startup/command-registry.js';
|
|
98
98
|
import { applyGlobalHelpConventions } from './lib/help.js';
|
|
99
99
|
import { renderWhatsNew } from './lib/whats-new.js';
|
|
100
100
|
import { getCliLaunch } from './lib/cli-entry.js';
|
|
@@ -317,6 +317,8 @@ Credentials and profiles:
|
|
|
317
317
|
|
|
318
318
|
Diagnostics:
|
|
319
319
|
doctor [agent[@version]] Diagnose CLI availability, sync status, and resource divergence; --check for the CI drift gate
|
|
320
|
+
status Unified sync status (drift/missing) — not the live fleet snapshot
|
|
321
|
+
snapshot One-process poll: inventory + active sessions (+ optional feed/sync)
|
|
320
322
|
usage [agent] Show rate-limit and quota usage per agent
|
|
321
323
|
perf Latency rollups (hooks, commands, runs) from the disposable perf warehouse
|
|
322
324
|
|
|
@@ -938,6 +940,7 @@ async function registerAllEagerCommands() {
|
|
|
938
940
|
registerCheckTombstoneCommand(program);
|
|
939
941
|
await reg(loadApply);
|
|
940
942
|
await reg(loadStatus);
|
|
943
|
+
await reg(loadSnapshot);
|
|
941
944
|
registerExecAliasCommand(program);
|
|
942
945
|
await reg(loadProfiles);
|
|
943
946
|
await reg(loadHarness);
|
package/dist/lib/browser/ipc.js
CHANGED