@phnx-labs/agents-cli 1.22.24 → 1.22.26
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 +350 -1
- package/README.md +31 -6
- package/dist/bin/agents +0 -0
- package/dist/browser.js +14 -4
- package/dist/commands/apply.js +52 -8
- package/dist/commands/browser.js +35 -0
- package/dist/commands/cloud.js +9 -5
- package/dist/commands/doctor.d.ts +24 -0
- package/dist/commands/doctor.js +108 -9
- package/dist/commands/exec.js +19 -16
- package/dist/commands/feed.d.ts +5 -0
- package/dist/commands/feed.js +21 -1
- package/dist/commands/focus.js +2 -2
- package/dist/commands/insights.d.ts +25 -19
- package/dist/commands/insights.js +107 -33
- package/dist/commands/menubar.js +8 -0
- package/dist/commands/reconnect.d.ts +46 -0
- package/dist/commands/reconnect.js +109 -0
- package/dist/commands/routines.d.ts +3 -0
- package/dist/commands/routines.js +72 -49
- package/dist/commands/run-cloud.js +1 -1
- package/dist/commands/secrets.d.ts +2 -8
- package/dist/commands/secrets.js +29 -105
- package/dist/commands/sessions-browser.d.ts +1 -1
- package/dist/commands/sessions-browser.js +27 -7
- package/dist/commands/sessions-resume.js +3 -2
- package/dist/commands/sessions.d.ts +13 -1
- package/dist/commands/sessions.js +28 -2
- package/dist/commands/setup-secrets.d.ts +1 -0
- package/dist/commands/setup-secrets.js +1 -1
- package/dist/commands/setup-watchdog.js +5 -10
- package/dist/commands/setup.d.ts +26 -3
- package/dist/commands/setup.js +106 -47
- package/dist/commands/teams.d.ts +6 -0
- package/dist/commands/teams.js +46 -2
- package/dist/commands/trends.d.ts +8 -0
- package/dist/commands/trends.js +10 -156
- package/dist/commands/watchdog.d.ts +3 -4
- package/dist/commands/watchdog.js +26 -66
- package/dist/index.js +37 -2
- package/dist/lib/agents.d.ts +11 -0
- package/dist/lib/agents.js +155 -23
- package/dist/lib/analytics/dashboard.d.ts +10 -6
- package/dist/lib/analytics/dashboard.js +6 -4
- package/dist/lib/analytics/mix-commands.d.ts +53 -0
- package/dist/lib/analytics/mix-commands.js +229 -0
- package/dist/lib/analytics/recipes.d.ts +19 -14
- package/dist/lib/analytics/recipes.js +4 -2
- package/dist/lib/browser/ipc.d.ts +26 -0
- package/dist/lib/browser/ipc.js +139 -24
- package/dist/lib/browser/profiles.d.ts +11 -0
- package/dist/lib/browser/profiles.js +1 -1
- package/dist/lib/browser/stream.d.ts +14 -0
- package/dist/lib/browser/stream.js +71 -0
- package/dist/lib/channels/owner-sink.d.ts +27 -0
- package/dist/lib/channels/owner-sink.js +93 -0
- package/dist/lib/cloud/cursor.d.ts +79 -0
- package/dist/lib/cloud/cursor.js +228 -0
- package/dist/lib/cloud/registry.js +2 -0
- package/dist/lib/cloud/types.d.ts +7 -2
- package/dist/lib/cloud/types.js +14 -0
- package/dist/lib/crabbox/cli.d.ts +2 -2
- package/dist/lib/crabbox/config.d.ts +7 -8
- package/dist/lib/crabbox/config.js +14 -14
- package/dist/lib/crabbox/lease.d.ts +11 -4
- package/dist/lib/crabbox/lease.js +40 -8
- package/dist/lib/crabbox/setup-copy.d.ts +5 -0
- package/dist/lib/crabbox/setup-copy.js +17 -1
- package/dist/lib/daemon.js +27 -1
- package/dist/lib/device-config.js +7 -0
- package/dist/lib/devices/doctor-findings.d.ts +13 -1
- package/dist/lib/devices/doctor-findings.js +73 -2
- package/dist/lib/events.d.ts +9 -0
- package/dist/lib/events.js +58 -0
- package/dist/lib/exec.d.ts +3 -3
- package/dist/lib/exec.js +24 -10
- package/dist/lib/feed-outcome.d.ts +3 -0
- package/dist/lib/feed-outcome.js +18 -10
- package/dist/lib/feed.d.ts +4 -0
- package/dist/lib/fleet/apply.d.ts +59 -3
- package/dist/lib/fleet/apply.js +183 -6
- package/dist/lib/fleet/types.d.ts +21 -2
- package/dist/lib/hooks/cache.js +15 -0
- package/dist/lib/hosts/passthrough.d.ts +44 -0
- package/dist/lib/hosts/passthrough.js +84 -12
- package/dist/lib/hosts/ready.d.ts +2 -0
- package/dist/lib/hosts/ready.js +10 -1
- package/dist/lib/hosts/reconnect.d.ts +14 -12
- package/dist/lib/hosts/reconnect.js +41 -40
- package/dist/lib/mcp.js +5 -1
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/snapshot.d.ts +15 -0
- package/dist/lib/menubar/snapshot.js +40 -0
- package/dist/lib/plugins.js +13 -1
- package/dist/lib/resources/mcp.js +3 -0
- package/dist/lib/routine-process-cleanup.d.ts +9 -0
- package/dist/lib/routine-process-cleanup.js +73 -0
- package/dist/lib/routines.js +14 -2
- package/dist/lib/runner.d.ts +0 -3
- package/dist/lib/runner.js +6 -19
- 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/bundles.js +1 -20
- package/dist/lib/secrets/filestore.d.ts +2 -0
- package/dist/lib/secrets/filestore.js +13 -0
- package/dist/lib/secrets/push.d.ts +94 -0
- package/dist/lib/secrets/push.js +145 -0
- package/dist/lib/secrets/rc-hygiene.d.ts +14 -0
- package/dist/lib/secrets/rc-hygiene.js +14 -1
- package/dist/lib/secrets/reaper.d.ts +15 -1
- package/dist/lib/secrets/reaper.js +30 -3
- package/dist/lib/session/active.d.ts +4 -0
- package/dist/lib/session/db.d.ts +21 -3
- package/dist/lib/session/db.js +229 -15
- package/dist/lib/session/discover.d.ts +1 -0
- package/dist/lib/session/discover.js +115 -19
- package/dist/lib/session/insights.d.ts +18 -0
- package/dist/lib/session/insights.js +143 -1
- package/dist/lib/session/remote-list.d.ts +2 -0
- package/dist/lib/session/remote-list.js +1 -0
- package/dist/lib/session/session-cache.d.ts +4 -4
- package/dist/lib/session/session-cache.js +4 -4
- package/dist/lib/session/tool-index.js +133 -22
- package/dist/lib/session/tool-store.d.ts +26 -2
- package/dist/lib/session/tool-store.js +36 -17
- package/dist/lib/shims.js +21 -1
- package/dist/lib/signin-badge.js +2 -0
- package/dist/lib/ssh-exec.js +8 -2
- package/dist/lib/startup/command-registry.d.ts +16 -0
- package/dist/lib/startup/command-registry.js +46 -0
- package/dist/lib/teams/agents.d.ts +13 -0
- package/dist/lib/teams/agents.js +76 -8
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/placement-probe.d.ts +21 -0
- package/dist/lib/teams/placement-probe.js +135 -0
- package/dist/lib/teams/scheduler.d.ts +74 -1
- package/dist/lib/teams/scheduler.js +187 -10
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/versions.js +16 -1
- package/dist/lib/watchdog/service.d.ts +17 -0
- package/dist/lib/watchdog/service.js +39 -0
- package/package.json +1 -1
|
@@ -3,17 +3,12 @@ import { spawnSync } from 'node:child_process';
|
|
|
3
3
|
import { getCliLaunch } from '../lib/cli-entry.js';
|
|
4
4
|
import { loadDevices } from '../lib/devices/registry.js';
|
|
5
5
|
import { machineId, normalizeHost } from '../lib/machine-id.js';
|
|
6
|
-
import {
|
|
7
|
-
import { readJob, setJobEnabled } from '../lib/routines.js';
|
|
6
|
+
import { getConfigValue, setConfigValue } from '../lib/device-config.js';
|
|
8
7
|
import { isPromptCancelled, requireInteractiveSelection } from './utils.js';
|
|
9
|
-
const ROUTINE = 'watchdog';
|
|
10
8
|
export async function runWatchdogSetupWizard() {
|
|
11
|
-
if (!readJob(ROUTINE)) {
|
|
12
|
-
throw new Error("Built-in routine 'watchdog' is missing. Run: agents repo pull system");
|
|
13
|
-
}
|
|
14
9
|
const registry = await loadDevices();
|
|
15
10
|
const devices = [...new Set([...Object.keys(registry), machineId()].map(normalizeHost))].sort();
|
|
16
|
-
const enabled = new Set(
|
|
11
|
+
const enabled = new Set(devices.filter((device) => getConfigValue('watchdog.enabled', { device }).value === true));
|
|
17
12
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
18
13
|
requireInteractiveSelection('watchdog devices', ['agents watchdog on', 'agents watchdog on --host <device>']);
|
|
19
14
|
}
|
|
@@ -27,10 +22,10 @@ export async function runWatchdogSetupWizard() {
|
|
|
27
22
|
for (const device of devices) {
|
|
28
23
|
const on = selectedSet.has(device);
|
|
29
24
|
if (device === normalizeHost(machineId())) {
|
|
30
|
-
|
|
25
|
+
setConfigValue('watchdog.enabled', on);
|
|
31
26
|
continue;
|
|
32
27
|
}
|
|
33
|
-
const launch = getCliLaunch(['
|
|
28
|
+
const launch = getCliLaunch(['watchdog', on ? 'on' : 'off', '--host', device]);
|
|
34
29
|
const result = spawnSync(launch.command, launch.args, { stdio: 'inherit', env: process.env });
|
|
35
30
|
if ((result.status ?? 1) !== 0)
|
|
36
31
|
throw new Error(`Could not configure watchdog on ${device}`);
|
|
@@ -49,6 +44,6 @@ export async function runWatchdogSetupWizard() {
|
|
|
49
44
|
}
|
|
50
45
|
export function registerSetupWatchdogCommand(setup) {
|
|
51
46
|
setup.command('watchdog')
|
|
52
|
-
.description('Choose the devices where the
|
|
47
|
+
.description('Choose the devices where the daemon watchdog pass runs.')
|
|
53
48
|
.action(runWatchdogSetupWizard);
|
|
54
49
|
}
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
* ~/.agents/.system/ and installs agent CLIs with resource syncing.
|
|
6
6
|
*/
|
|
7
7
|
import type { Command } from 'commander';
|
|
8
|
-
|
|
9
|
-
export declare function runSetup(program: Command, options?: {
|
|
8
|
+
interface RunSetupOptions {
|
|
10
9
|
force?: boolean;
|
|
11
10
|
suppressFooter?: boolean;
|
|
12
11
|
systemRepo?: boolean;
|
|
13
|
-
|
|
12
|
+
runHub?: () => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/** First-run setup. Clones ~/.agents/.system/ from the system repo if needed. */
|
|
15
|
+
export declare function runSetup(program: Command, options?: RunSetupOptions): Promise<void>;
|
|
14
16
|
/**
|
|
15
17
|
* Ensure the system repo exists before running a command that needs it.
|
|
16
18
|
* If ~/.agents/.system/ is not a git repo AND we're in an interactive TTY,
|
|
@@ -18,5 +20,26 @@ export declare function runSetup(program: Command, options?: {
|
|
|
18
20
|
* error and exit.
|
|
19
21
|
*/
|
|
20
22
|
export declare function ensureInitialized(program: Command): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Interactive "what else do you want to set up?" menu shown after the bare
|
|
25
|
+
* `agents setup` finishes on a TTY. Each pick runs that capability's guided
|
|
26
|
+
* wizard. Never throws — a cancel or an optional wizard's error just skips the
|
|
27
|
+
* rest and lets core setup complete.
|
|
28
|
+
*/
|
|
29
|
+
export type SetupPhase = 'browser' | 'computer' | 'share' | 'secrets' | 'fleet' | 'watchdog' | 'preferences';
|
|
30
|
+
export type SetupStatusState = 'ready' | 'missing' | 'n/a';
|
|
31
|
+
export interface SetupStatusRow {
|
|
32
|
+
phase: 'core' | SetupPhase;
|
|
33
|
+
state: SetupStatusState;
|
|
34
|
+
detail: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function getSetupStatus(): Promise<SetupStatusRow[]>;
|
|
37
|
+
export declare function renderSetupStatus(rows: SetupStatusRow[]): void;
|
|
38
|
+
export declare function runSetupHub(deps?: {
|
|
39
|
+
interactive?: boolean;
|
|
40
|
+
selectPhase?: (rows: SetupStatusRow[]) => Promise<SetupPhase | 'exit'>;
|
|
41
|
+
runPhase?: (phase: SetupPhase) => Promise<void>;
|
|
42
|
+
}): Promise<void>;
|
|
21
43
|
/** Register the `agents setup` command and its capability subcommands. */
|
|
22
44
|
export declare function registerSetupCommand(program: Command): void;
|
|
45
|
+
export {};
|
package/dist/commands/setup.js
CHANGED
|
@@ -26,6 +26,13 @@ import { registerSetupSecretsCommand } from './setup-secrets.js';
|
|
|
26
26
|
import { registerSetupFleetCommand } from './setup-fleet.js';
|
|
27
27
|
import { registerSetupWatchdogCommand, runWatchdogSetupWizard } from './setup-watchdog.js';
|
|
28
28
|
import { runPreferencesStep } from './setup-preferences.js';
|
|
29
|
+
import { getConfiguredDefaultProfileName, getProfile, isProfileLaunchableHere, DEFAULT_BROWSER_PROFILE_NAME } from '../lib/browser/profiles.js';
|
|
30
|
+
import { listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
31
|
+
import { probeComputerTrust } from './computer.js';
|
|
32
|
+
import { readShareConfig } from '../lib/share/config.js';
|
|
33
|
+
import { loadDevices } from '../lib/devices/registry.js';
|
|
34
|
+
import { getConfigValue } from '../lib/device-config.js';
|
|
35
|
+
import { setupSecretsPrefsPath } from './setup-secrets.js';
|
|
29
36
|
const HOME = os.homedir();
|
|
30
37
|
/**
|
|
31
38
|
* Import an existing unmanaged agent installation into agents-cli.
|
|
@@ -70,9 +77,9 @@ export async function runSetup(program, options = {}) {
|
|
|
70
77
|
const agentsDir = getAgentsDir();
|
|
71
78
|
const alreadyConfigured = isGitRepo(agentsDir);
|
|
72
79
|
if (alreadyConfigured && !options.force) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
if (options.suppressFooter)
|
|
81
|
+
return;
|
|
82
|
+
await (options.runHub ?? runSetupHub)();
|
|
76
83
|
return;
|
|
77
84
|
}
|
|
78
85
|
// Detect existing installations BEFORE cloning (they won't exist after if we import)
|
|
@@ -193,11 +200,7 @@ export async function runSetup(program, options = {}) {
|
|
|
193
200
|
// Fresh-machine hub: offer to set up the optional capabilities that need their
|
|
194
201
|
// own guided flow. TTY-only and fully opt-in — a non-interactive `agents setup`
|
|
195
202
|
// stops at the system-repo bootstrap above, unchanged.
|
|
196
|
-
await runSetupHub();
|
|
197
|
-
// Preferences step: the two questions that keep agents off the wrong machine —
|
|
198
|
-
// which box you sit at (interactive host) and which browser agents drive here.
|
|
199
|
-
// TTY-only, skippable, and writes the same keys as `agents devices …`.
|
|
200
|
-
await runPreferencesStep();
|
|
203
|
+
await (options.runHub ?? runSetupHub)();
|
|
201
204
|
console.log(chalk.bold('\nSetup complete. Try:'));
|
|
202
205
|
console.log(chalk.cyan(' agents view ') + chalk.gray(' # see what\'s installed'));
|
|
203
206
|
console.log(chalk.cyan(' agents run <agent> "hello" ') + chalk.gray(' # run an agent'));
|
|
@@ -229,42 +232,88 @@ export async function ensureInitialized(program) {
|
|
|
229
232
|
}
|
|
230
233
|
await runSetup(program, { suppressFooter: true });
|
|
231
234
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
235
|
+
export async function getSetupStatus() {
|
|
236
|
+
const configuredBrowserProfile = getConfiguredDefaultProfileName();
|
|
237
|
+
const browserProfile = await getProfile(configuredBrowserProfile ?? DEFAULT_BROWSER_PROFILE_NAME);
|
|
238
|
+
const browserReady = browserProfile !== null && isProfileLaunchableHere(browserProfile);
|
|
239
|
+
const installedBrowsers = listInstalledBrowsers();
|
|
240
|
+
const computerState = process.platform === 'darwin' ? (await probeComputerTrust() ? 'ready' : 'missing') : 'n/a';
|
|
241
|
+
const devices = await loadDevices();
|
|
242
|
+
const coreReady = isGitRepo(getAgentsDir());
|
|
243
|
+
const secretsReady = fs.existsSync(setupSecretsPrefsPath());
|
|
244
|
+
const shareConfig = readShareConfig();
|
|
245
|
+
const watchdogEnabled = getConfigValue('watchdog.enabled').value === true;
|
|
246
|
+
const interactiveHost = getConfigValue('interactive.host').value;
|
|
247
|
+
const defaultBrowser = getConfigValue('browser.profile').value;
|
|
248
|
+
return [
|
|
249
|
+
{ phase: 'core', state: coreReady ? 'ready' : 'missing', detail: coreReady ? 'system repo ready' : 'system repo missing' },
|
|
250
|
+
{ phase: 'browser', state: browserReady ? 'ready' : 'missing', detail: browserReady ? `profile ${browserProfile.name}` : browserProfile ? `profile ${browserProfile.name} cannot launch here` : installedBrowsers.length ? 'no default profile' : 'no supported browser found' },
|
|
251
|
+
{ phase: 'computer', state: computerState, detail: computerState === 'ready' ? 'helper trusted' : computerState === 'n/a' ? 'macOS local setup only' : 'helper not running or not trusted' },
|
|
252
|
+
{ phase: 'secrets', state: secretsReady ? 'ready' : 'missing', detail: secretsReady ? 'defaults chosen' : 'defaults not chosen' },
|
|
253
|
+
{ phase: 'fleet', state: Object.keys(devices).length ? 'ready' : 'missing', detail: Object.keys(devices).length ? `${Object.keys(devices).length} device${Object.keys(devices).length === 1 ? '' : 's'} registered` : 'no devices registered' },
|
|
254
|
+
{ phase: 'share', state: shareConfig ? 'ready' : 'missing', detail: shareConfig?.baseUrl ?? 'endpoint not configured' },
|
|
255
|
+
{ phase: 'watchdog', state: watchdogEnabled ? 'ready' : 'missing', detail: watchdogEnabled ? 'enabled on this device' : 'disabled on this device' },
|
|
256
|
+
{ phase: 'preferences', state: interactiveHost || defaultBrowser ? 'ready' : 'missing', detail: [interactiveHost && `host ${interactiveHost}`, defaultBrowser && `browser ${defaultBrowser}`].filter(Boolean).join(' · ') || 'interactive host and browser unset' },
|
|
257
|
+
];
|
|
258
|
+
}
|
|
259
|
+
export function renderSetupStatus(rows) {
|
|
260
|
+
console.log(chalk.bold('\nagents setup — onboarding\n'));
|
|
261
|
+
for (const row of rows) {
|
|
262
|
+
const marker = row.state === 'ready' ? chalk.green('[x]') : row.state === 'n/a' ? chalk.gray('[-]') : chalk.yellow('[ ]');
|
|
263
|
+
const label = row.phase[0].toUpperCase() + row.phase.slice(1);
|
|
264
|
+
console.log(` ${marker} ${label.padEnd(12)} ${chalk.gray(row.detail)}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async function runSetupPhase(phase) {
|
|
268
|
+
if (phase === 'browser')
|
|
269
|
+
await runBrowserWizard();
|
|
270
|
+
else if (phase === 'computer')
|
|
271
|
+
await runComputerWizard();
|
|
272
|
+
else if (phase === 'share')
|
|
273
|
+
await runShareWizard();
|
|
274
|
+
else if (phase === 'secrets')
|
|
275
|
+
await import('./setup-secrets.js').then((m) => m.runSecretsSetupWizard());
|
|
276
|
+
else if (phase === 'fleet')
|
|
277
|
+
await import('./setup-fleet.js').then((m) => m.runFleetSetupWizard());
|
|
278
|
+
else if (phase === 'watchdog')
|
|
279
|
+
await runWatchdogSetupWizard();
|
|
280
|
+
else
|
|
281
|
+
await runPreferencesStep();
|
|
282
|
+
}
|
|
283
|
+
export async function runSetupHub(deps = {}) {
|
|
284
|
+
const interactive = deps.interactive ?? isInteractiveTerminal();
|
|
285
|
+
if (!interactive) {
|
|
286
|
+
const rows = await getSetupStatus();
|
|
287
|
+
renderSetupStatus(rows);
|
|
288
|
+
if (rows.some((row) => row.state === 'missing'))
|
|
289
|
+
process.exitCode = 1;
|
|
240
290
|
return;
|
|
291
|
+
}
|
|
241
292
|
try {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
{
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
293
|
+
while (true) {
|
|
294
|
+
const rows = await getSetupStatus();
|
|
295
|
+
renderSetupStatus(rows);
|
|
296
|
+
let pick;
|
|
297
|
+
if (deps.selectPhase) {
|
|
298
|
+
pick = await deps.selectPhase(rows);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
const { select } = await import('@inquirer/prompts');
|
|
302
|
+
pick = await select({
|
|
303
|
+
message: 'Choose a phase to configure',
|
|
304
|
+
choices: [
|
|
305
|
+
...rows.filter((row) => row.phase !== 'core' && row.state !== 'n/a').map((row) => ({
|
|
306
|
+
name: `${row.state === 'ready' ? '[x]' : '[ ]'} ${row.phase.padEnd(12)} ${row.detail}`,
|
|
307
|
+
value: row.phase,
|
|
308
|
+
})),
|
|
309
|
+
{ name: 'Exit setup', value: 'exit' },
|
|
310
|
+
],
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
if (pick === 'exit')
|
|
314
|
+
return;
|
|
255
315
|
console.log();
|
|
256
|
-
|
|
257
|
-
await runBrowserWizard();
|
|
258
|
-
else if (pick === 'computer')
|
|
259
|
-
await runComputerWizard();
|
|
260
|
-
else if (pick === 'share')
|
|
261
|
-
await runShareWizard();
|
|
262
|
-
else if (pick === 'secrets')
|
|
263
|
-
await import('./setup-secrets.js').then((m) => m.runSecretsSetupWizard());
|
|
264
|
-
else if (pick === 'fleet')
|
|
265
|
-
await import('./setup-fleet.js').then((m) => m.runFleetSetupWizard());
|
|
266
|
-
else if (pick === 'watchdog')
|
|
267
|
-
await runWatchdogSetupWizard();
|
|
316
|
+
await (deps.runPhase ?? runSetupPhase)(pick);
|
|
268
317
|
}
|
|
269
318
|
}
|
|
270
319
|
catch (err) {
|
|
@@ -277,7 +326,7 @@ async function runSetupHub() {
|
|
|
277
326
|
export function registerSetupCommand(program) {
|
|
278
327
|
const setupCmd = program
|
|
279
328
|
.command('setup')
|
|
280
|
-
.description('
|
|
329
|
+
.description('Set up agents-cli, or re-open the capability onboarding hub.')
|
|
281
330
|
.option('-f, --force', 'Re-run setup even if ~/.agents/.system/ already exists (use with caution)')
|
|
282
331
|
.option('--no-system-repo', 'Skip cloning the system repo (you must populate ~/.agents/.system/ yourself)');
|
|
283
332
|
// Capability subcommands: `agents setup browser|computer|share|mine|secrets|fleet`.
|
|
@@ -288,6 +337,18 @@ export function registerSetupCommand(program) {
|
|
|
288
337
|
registerSetupSecretsCommand(setupCmd);
|
|
289
338
|
registerSetupFleetCommand(setupCmd);
|
|
290
339
|
registerSetupWatchdogCommand(setupCmd);
|
|
340
|
+
setupCmd.command('status')
|
|
341
|
+
.description('Show setup readiness for core, browser, computer, secrets, fleet, share, watchdog, and preferences.')
|
|
342
|
+
.option('--json', 'print machine-readable JSON')
|
|
343
|
+
.action(async (options) => {
|
|
344
|
+
const rows = await getSetupStatus();
|
|
345
|
+
if (options.json)
|
|
346
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
347
|
+
else
|
|
348
|
+
renderSetupStatus(rows);
|
|
349
|
+
if (rows.some((row) => row.state === 'missing'))
|
|
350
|
+
process.exitCode = 1;
|
|
351
|
+
});
|
|
291
352
|
setHelpSections(setupCmd, {
|
|
292
353
|
examples: `
|
|
293
354
|
# First-time setup (clones the system repo into ~/.agents/.system/)
|
|
@@ -308,10 +369,8 @@ export function registerSetupCommand(program) {
|
|
|
308
369
|
What it does:
|
|
309
370
|
1. Clones the system repo into ~/.agents/.system/
|
|
310
371
|
2. Imports any unmanaged agent installations it finds
|
|
311
|
-
3.
|
|
312
|
-
4.
|
|
313
|
-
and which browser agents drive here — both skippable, both the same
|
|
314
|
-
keys 'agents devices set-interactive' / 'browser profiles set-default' write
|
|
372
|
+
3. Opens a re-runnable capability menu with live ready/missing status
|
|
373
|
+
4. Delegates each selection to its existing setup wizard
|
|
315
374
|
|
|
316
375
|
Capability setup can also be run any time on its own:
|
|
317
376
|
agents setup browser # detect a browser + create the default profile
|
|
@@ -319,7 +378,7 @@ export function registerSetupCommand(program) {
|
|
|
319
378
|
agents setup share # provision or join a Cloudflare share endpoint
|
|
320
379
|
agents setup secrets # choose secrets backend/policy defaults + import
|
|
321
380
|
agents setup fleet # discover Tailscale devices + configure SSH access
|
|
322
|
-
agents setup watchdog # choose which devices run the watchdog
|
|
381
|
+
agents setup watchdog # choose which devices run the daemon watchdog pass
|
|
323
382
|
|
|
324
383
|
To install CLIs from agents.yaml and sync resources into version homes:
|
|
325
384
|
agents sync --local -y
|
package/dist/commands/teams.d.ts
CHANGED
|
@@ -34,6 +34,12 @@ export interface TeamListAgentSnapshot {
|
|
|
34
34
|
cloud_provider: string | null;
|
|
35
35
|
pr_url: string | null;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* One-line operator nudge after `teams start`: teammates are briefed to post
|
|
39
|
+
* IMPORTANT milestones to the feed (RUSH-2250), and this is how to watch them.
|
|
40
|
+
* Print-only — no engine behavior.
|
|
41
|
+
*/
|
|
42
|
+
export declare function printFeedHint(team: string): void;
|
|
37
43
|
/** Where `teams message`/`teams resume` routes a follow-up, by teammate status. */
|
|
38
44
|
export type TeamMessageRoute = {
|
|
39
45
|
kind: 'steer';
|
package/dist/commands/teams.js
CHANGED
|
@@ -47,8 +47,9 @@ const AGENT_NAMES = {
|
|
|
47
47
|
antigravity: 'Antigravity',
|
|
48
48
|
kimi: 'Kimi',
|
|
49
49
|
droid: 'Droid',
|
|
50
|
+
warp: 'Warp',
|
|
50
51
|
};
|
|
51
|
-
const VALID_AGENTS = ['claude', 'codex', 'cursor', 'opencode', 'grok', 'antigravity', 'kimi', 'droid'];
|
|
52
|
+
const VALID_AGENTS = ['claude', 'codex', 'cursor', 'opencode', 'grok', 'antigravity', 'kimi', 'droid', 'warp'];
|
|
52
53
|
// 'full' kept as historical alias for 'skip'; normalized to 'skip' downstream.
|
|
53
54
|
const VALID_MODES = ['plan', 'edit', 'auto', 'skip', 'full'];
|
|
54
55
|
const VALID_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max', 'auto'];
|
|
@@ -152,6 +153,18 @@ function fullName(type, version) {
|
|
|
152
153
|
const name = AGENT_NAMES[type];
|
|
153
154
|
return version ? `${name} ${version}` : name;
|
|
154
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* One-line operator nudge after `teams start`: teammates are briefed to post
|
|
158
|
+
* IMPORTANT milestones to the feed (RUSH-2250), and this is how to watch them.
|
|
159
|
+
* Print-only — no engine behavior.
|
|
160
|
+
*/
|
|
161
|
+
export function printFeedHint(team) {
|
|
162
|
+
console.log(chalk.gray('Tip: teammates post IMPORTANT milestones to the feed (') +
|
|
163
|
+
chalk.cyan('agents feed timeline') +
|
|
164
|
+
chalk.gray('); watch team progress with ') +
|
|
165
|
+
chalk.cyan(`agents teams status ${team}`) +
|
|
166
|
+
chalk.gray('.'));
|
|
167
|
+
}
|
|
155
168
|
/**
|
|
156
169
|
* Resolve a teammate spec to its execution target.
|
|
157
170
|
*
|
|
@@ -364,6 +377,8 @@ async function runOneWave(mgr, team, json) {
|
|
|
364
377
|
console.log(` ${chalk.blue(h)} ${chalk.gray('after')} ${a.after.join(', ')}`);
|
|
365
378
|
}
|
|
366
379
|
}
|
|
380
|
+
if (launched.length > 0)
|
|
381
|
+
printFeedHint(team);
|
|
367
382
|
}
|
|
368
383
|
/**
|
|
369
384
|
* Path to the pr-watch dedupe-state file for a team. Persists the set of
|
|
@@ -1279,7 +1294,7 @@ export function registerTeamsCommands(program) {
|
|
|
1279
1294
|
.alias('a')
|
|
1280
1295
|
.description("Add a teammate to work on a task. Runs in background; returns immediately. Use 'status' to check in.")
|
|
1281
1296
|
.option('-n, --name <name>', 'Friendly name for this teammate (e.g. alice). Required if using --after. Unique within team.')
|
|
1282
|
-
.option('-m, --mode <mode>', `Permissions: plan (read-only) | edit (can write files) | auto (smart classifier auto-approves safe ops) | skip (bypass all permission prompts). 'full' accepted as alias for skip. Teammates run headless: plan works headless on claude/codex/droid/opencode; kimi/grok/
|
|
1297
|
+
.option('-m, --mode <mode>', `Permissions: plan (read-only) | edit (can write files) | auto (smart classifier auto-approves safe ops) | skip (bypass all permission prompts). 'full' accepted as alias for skip. Teammates run headless: plan works headless on claude/codex/cursor/droid/opencode; kimi/grok/antigravity have no headless plan mode and auto-downgrade a plan request to auto.`, 'edit')
|
|
1283
1298
|
.option('-e, --effort <effort>', `Reasoning intensity: ${VALID_EFFORTS.join('|')}`, 'medium')
|
|
1284
1299
|
.option('--model <model>', 'Cost tier (cheap|default|best|ultra) or a concrete id (e.g. claude-opus-4-8); tiers resolve per harness+version to a supported model')
|
|
1285
1300
|
.option('--env <key=value>', 'Set an environment variable for this teammate (repeatable for multiple vars)', (val, prev) => [...prev, val], [])
|
|
@@ -1765,11 +1780,40 @@ export function registerTeamsCommands(program) {
|
|
|
1765
1780
|
await warnUnsignedTeammates(mgr, team);
|
|
1766
1781
|
await warnThrottledTeammates(mgr, team);
|
|
1767
1782
|
}
|
|
1783
|
+
// Health-/harness-aware pre-flight (RUSH-2002): if the team declares a
|
|
1784
|
+
// device pool and no device in it can run a pending teammate's agent, fail
|
|
1785
|
+
// loud here rather than launching a wave that strands teammates pending.
|
|
1786
|
+
// --force downgrades it to a warning so the user can still try.
|
|
1787
|
+
const placementError = await mgr.preflightPlacement(team);
|
|
1788
|
+
if (placementError) {
|
|
1789
|
+
if (isJsonMode(opts)) {
|
|
1790
|
+
console.log(JSON.stringify({
|
|
1791
|
+
team,
|
|
1792
|
+
error: 'no-viable-device',
|
|
1793
|
+
message: placementError.message,
|
|
1794
|
+
excluded: placementError.excluded,
|
|
1795
|
+
}));
|
|
1796
|
+
if (!opts.force) {
|
|
1797
|
+
process.exitCode = 1;
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
else if (opts.force) {
|
|
1802
|
+
console.error(chalk.yellow(`⚠ ${placementError.message}\n Continuing anyway (--force).`));
|
|
1803
|
+
}
|
|
1804
|
+
else {
|
|
1805
|
+
console.error(chalk.red(placementError.message));
|
|
1806
|
+
process.exitCode = 1;
|
|
1807
|
+
return;
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1768
1810
|
emit('teams.start', { module: 'teams', team, watch: Boolean(opts.watch) });
|
|
1769
1811
|
if (!opts.watch) {
|
|
1770
1812
|
await runOneWave(mgr, team, Boolean(opts.json));
|
|
1771
1813
|
return;
|
|
1772
1814
|
}
|
|
1815
|
+
if (!isJsonMode(opts))
|
|
1816
|
+
printFeedHint(team);
|
|
1773
1817
|
const intervalMs = Math.max(1000, Number.parseInt(opts.interval, 10) * 1000 || 8000);
|
|
1774
1818
|
const maxWaves = Math.max(1, Number.parseInt(opts.maxWaves, 10) || 1000);
|
|
1775
1819
|
const json = isJsonMode(opts);
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deprecated top-level spelling of the counter-mix tree.
|
|
3
|
+
*
|
|
4
|
+
* Implementation lives under `agents insights mix` (see
|
|
5
|
+
* `lib/analytics/mix-commands.ts`). This file only registers the thin alias so
|
|
6
|
+
* old scripts and agent briefs keep working while printing one deprecation line
|
|
7
|
+
* — no second recipe implementation.
|
|
8
|
+
*/
|
|
1
9
|
import type { Command } from 'commander';
|
|
2
10
|
export declare function registerTrendsCommand(program: Command): void;
|
package/dist/commands/trends.js
CHANGED
|
@@ -1,158 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
function parseLimit(raw, fallback) {
|
|
11
|
-
const n = parseInt(raw ?? String(fallback), 10);
|
|
12
|
-
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
13
|
-
}
|
|
14
|
-
function printSection(section) {
|
|
15
|
-
console.log(chalk.bold(section.title));
|
|
16
|
-
if (section.rows.length === 0) {
|
|
17
|
-
console.log(chalk.gray(' (no data)'));
|
|
18
|
-
console.log();
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
const keys = Object.keys(section.rows[0]);
|
|
22
|
-
const widths = keys.map((k) => Math.max(k.length, ...section.rows.map((r) => String(r[k] ?? '').length), 4));
|
|
23
|
-
const pad = (s, w) => (s.length >= w ? s.slice(0, w) : s + ' '.repeat(w - s.length));
|
|
24
|
-
console.log(chalk.gray(keys.map((k, i) => pad(k.toUpperCase(), widths[i])).join(' ')));
|
|
25
|
-
for (const row of section.rows) {
|
|
26
|
-
console.log(keys.map((k, i) => pad(String(row[k] ?? ''), widths[i])).join(' '));
|
|
27
|
-
}
|
|
28
|
-
console.log();
|
|
29
|
-
}
|
|
30
|
-
function renderDashboard(days, asJson) {
|
|
31
|
-
const dash = buildTrendsDashboard({ days });
|
|
32
|
-
if (asJson) {
|
|
33
|
-
console.log(JSON.stringify(dash, null, 2));
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
console.log(chalk.bold(`agents trends — last ${dash.window.days} days`));
|
|
37
|
-
console.log(chalk.gray(`compute ${dash.durationMs}ms · usage ${usageDbPath()}`));
|
|
38
|
-
console.log();
|
|
39
|
-
if (dash.sections.length === 0) {
|
|
40
|
-
console.log(chalk.gray('No session or usage data in this window yet.'));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
for (const section of dash.sections)
|
|
44
|
-
printSection(section);
|
|
45
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Deprecated top-level spelling of the counter-mix tree.
|
|
3
|
+
*
|
|
4
|
+
* Implementation lives under `agents insights mix` (see
|
|
5
|
+
* `lib/analytics/mix-commands.ts`). This file only registers the thin alias so
|
|
6
|
+
* old scripts and agent briefs keep working while printing one deprecation line
|
|
7
|
+
* — no second recipe implementation.
|
|
8
|
+
*/
|
|
9
|
+
import { registerDeprecatedTrendsAlias } from '../lib/analytics/mix-commands.js';
|
|
46
10
|
export function registerTrendsCommand(program) {
|
|
47
|
-
|
|
48
|
-
.command('trends')
|
|
49
|
-
.description('Usage analytics — harness/model mix, token ratios, resource frequency')
|
|
50
|
-
.option('--days <n>', 'Days of history to include', '7')
|
|
51
|
-
.option('--json', 'Emit JSON instead of tables')
|
|
52
|
-
.action(function summary() {
|
|
53
|
-
const opts = this.opts();
|
|
54
|
-
renderDashboard(parseDays(opts.days), Boolean(opts.json));
|
|
55
|
-
});
|
|
56
|
-
setHelpSections(trends, {
|
|
57
|
-
examples: `
|
|
58
|
-
# Auto recipe dashboard (7d)
|
|
59
|
-
agents trends
|
|
60
|
-
|
|
61
|
-
# Last 30 days
|
|
62
|
-
agents trends --days 30
|
|
63
|
-
|
|
64
|
-
# One recipe as JSON
|
|
65
|
-
agents trends harness-mix --json
|
|
66
|
-
|
|
67
|
-
# Raw usage events
|
|
68
|
-
agents trends query --kind secret --days 7
|
|
69
|
-
|
|
70
|
-
# List baked recipe ids
|
|
71
|
-
agents trends recipes
|
|
72
|
-
`,
|
|
73
|
-
notes: `
|
|
74
|
-
Session recipes read sessions.db; resource recipes read ~/.agents/.history/analytics/usage.db.
|
|
75
|
-
Empty recipes are skipped on the default dashboard.
|
|
76
|
-
Quota / rate-limits remain on \`agents usage\`; latency on \`agents perf\`.
|
|
77
|
-
`,
|
|
78
|
-
});
|
|
79
|
-
trends.command('recipes')
|
|
80
|
-
.description('List baked recipe ids')
|
|
81
|
-
.option('--json', 'Emit JSON')
|
|
82
|
-
.action((opts) => {
|
|
83
|
-
const list = listRecipes();
|
|
84
|
-
if (opts.json) {
|
|
85
|
-
console.log(JSON.stringify(list, null, 2));
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
for (const r of list) {
|
|
89
|
-
console.log(`${r.id.padEnd(22)} ${r.store.padEnd(10)} ${r.title}`);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
trends.command('query')
|
|
93
|
-
.description('Raw usage-event query')
|
|
94
|
-
.option('--kind <kind>', `One of: ${USAGE_KINDS.join(', ')}`)
|
|
95
|
-
.option('--name <name>', 'Resource name filter')
|
|
96
|
-
.option('--event <event>', 'Event name filter')
|
|
97
|
-
.option('--days <n>', 'Days of history', '7')
|
|
98
|
-
.option('--limit <n>', 'Max rows', '40')
|
|
99
|
-
.option('--json', 'Emit JSON')
|
|
100
|
-
.action((opts) => {
|
|
101
|
-
const win = trendsWindow(parseDays(opts.days));
|
|
102
|
-
const kind = opts.kind && USAGE_KINDS.includes(opts.kind)
|
|
103
|
-
? opts.kind
|
|
104
|
-
: undefined;
|
|
105
|
-
if (opts.kind && !kind) {
|
|
106
|
-
console.error(`Unknown kind '${opts.kind}'. Expected: ${USAGE_KINDS.join(', ')}`);
|
|
107
|
-
process.exitCode = 1;
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const rows = queryUsage({
|
|
111
|
-
kind,
|
|
112
|
-
name: opts.name,
|
|
113
|
-
event: opts.event,
|
|
114
|
-
sinceIso: win.sinceIso,
|
|
115
|
-
limit: parseLimit(opts.limit, 40),
|
|
116
|
-
});
|
|
117
|
-
if (opts.json) {
|
|
118
|
-
console.log(JSON.stringify({ window: win, rows }, null, 2));
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (rows.length === 0) {
|
|
122
|
-
console.log(chalk.gray('No usage events.'));
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
printSection({
|
|
126
|
-
id: 'query',
|
|
127
|
-
title: 'Usage events',
|
|
128
|
-
rows: rows.map((r) => ({
|
|
129
|
-
ts: r.ts,
|
|
130
|
-
kind: r.kind,
|
|
131
|
-
name: r.name,
|
|
132
|
-
event: r.event,
|
|
133
|
-
agent: r.agent,
|
|
134
|
-
})),
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
for (const id of RECIPE_IDS) {
|
|
138
|
-
trends.command(id)
|
|
139
|
-
.description(`Recipe: ${id}`)
|
|
140
|
-
.option('--days <n>', 'Days of history', '7')
|
|
141
|
-
.option('--json', 'Emit JSON')
|
|
142
|
-
.action(function recipeAction() {
|
|
143
|
-
const parent = this.parent?.opts?.();
|
|
144
|
-
const opts = { ...parent, ...this.opts() };
|
|
145
|
-
const win = trendsWindow(parseDays(opts.days));
|
|
146
|
-
const section = runRecipe(id, win);
|
|
147
|
-
if (opts.json) {
|
|
148
|
-
console.log(JSON.stringify({ window: win, section }, null, 2));
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
if (section.empty) {
|
|
152
|
-
console.log(chalk.gray(`No data for recipe '${id}' in the last ${win.days} days.`));
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
printSection(section);
|
|
156
|
-
});
|
|
157
|
-
}
|
|
11
|
+
registerDeprecatedTrendsAlias(program);
|
|
158
12
|
}
|
|
@@ -10,12 +10,11 @@
|
|
|
10
10
|
* agents watchdog --nudge one tick, actually injects (explicit opt-in)
|
|
11
11
|
* agents watchdog --watch manual poll loop (dry unless --nudge)
|
|
12
12
|
* agents watchdog --json machine-readable tick output (for the menu-bar)
|
|
13
|
-
* agents watchdog on|off turn the
|
|
13
|
+
* agents watchdog on|off turn the device-local daemon pass on/off
|
|
14
14
|
* agents watchdog policy <id> <p> per-session policy: off | keep | handsoff
|
|
15
15
|
*
|
|
16
|
-
* The
|
|
17
|
-
*
|
|
18
|
-
* repo; on/off only changes this device's routine membership.
|
|
16
|
+
* The agents daemon is the sole automatic watchdog scheduler. The menu bar reads
|
|
17
|
+
* persisted state; it never runs a tick.
|
|
19
18
|
*/
|
|
20
19
|
import type { Command } from 'commander';
|
|
21
20
|
/** Register the `agents watchdog` command tree. */
|