@phnx-labs/agents-cli 1.21.0 → 1.21.2
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 +198 -0
- package/README.md +2 -1
- package/dist/bin/agents +0 -0
- package/dist/commands/doctor.js +5 -2
- package/dist/commands/feed.js +63 -40
- package/dist/commands/hooks.js +9 -45
- package/dist/commands/menubar.js +24 -24
- package/dist/commands/message.js +23 -3
- package/dist/commands/monitors.js +8 -6
- package/dist/commands/perf.d.ts +13 -0
- package/dist/commands/perf.js +80 -23
- package/dist/commands/projects.d.ts +11 -0
- package/dist/commands/projects.js +153 -21
- package/dist/commands/routines.js +46 -1
- package/dist/commands/ssh.js +69 -0
- package/dist/commands/trends.d.ts +2 -0
- package/dist/commands/trends.js +158 -0
- package/dist/commands/usage.d.ts +4 -4
- package/dist/commands/view.d.ts +6 -0
- package/dist/commands/view.js +90 -45
- package/dist/index.js +14 -1
- package/dist/lib/activity.d.ts +6 -0
- package/dist/lib/activity.js +3 -0
- package/dist/lib/agents.js +2 -2
- package/dist/lib/analytics/dashboard.d.ts +11 -0
- package/dist/lib/analytics/dashboard.js +31 -0
- package/dist/lib/analytics/recipes.d.ts +32 -0
- package/dist/lib/analytics/recipes.js +316 -0
- package/dist/lib/analytics/usage-db.d.ts +84 -0
- package/dist/lib/analytics/usage-db.js +301 -0
- package/dist/lib/browser/service.js +18 -0
- package/dist/lib/channels/providers/openclaw-telegram.js +1 -1
- package/dist/lib/channels/resolve.d.ts +18 -1
- package/dist/lib/channels/resolve.js +15 -5
- package/dist/lib/cli-resources.d.ts +20 -0
- package/dist/lib/cli-resources.js +48 -1
- package/dist/lib/daemon.js +51 -14
- package/dist/lib/devices/health-report.d.ts +5 -0
- package/dist/lib/devices/health-report.js +3 -0
- package/dist/lib/feed-broadcast.d.ts +93 -12
- package/dist/lib/feed-broadcast.js +235 -30
- package/dist/lib/feed-post.d.ts +16 -2
- package/dist/lib/feed-post.js +35 -7
- package/dist/lib/feed.js +1 -1
- package/dist/lib/fleet-cache.d.ts +37 -0
- package/dist/lib/fleet-cache.js +40 -0
- package/dist/lib/fleet-status.d.ts +53 -0
- package/dist/lib/fleet-status.js +120 -0
- package/dist/lib/friction-heuristics.d.ts +32 -0
- package/dist/lib/friction-heuristics.js +47 -0
- package/dist/lib/hooks/cache.js +28 -6
- package/dist/lib/hooks/profile.d.ts +8 -0
- package/dist/lib/hooks/profile.js +14 -4
- package/dist/lib/hooks.d.ts +16 -1
- package/dist/lib/hooks.js +133 -18
- package/dist/lib/linear-cache.d.ts +63 -0
- package/dist/lib/linear-cache.js +146 -0
- package/dist/lib/linear-project-counts.d.ts +35 -5
- package/dist/lib/linear-project-counts.js +61 -16
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +3 -1
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +7 -0
- package/dist/lib/menubar/install-menubar.js +36 -6
- package/dist/lib/monitors/config.d.ts +1 -1
- package/dist/lib/monitors/dispatch.d.ts +6 -2
- package/dist/lib/monitors/dispatch.js +10 -15
- package/dist/lib/notify.d.ts +42 -6
- package/dist/lib/notify.js +41 -32
- package/dist/lib/perf/db.d.ts +6 -1
- package/dist/lib/perf/db.js +35 -5
- package/dist/lib/perf/types.d.ts +10 -0
- package/dist/lib/project-doctor.d.ts +36 -0
- package/dist/lib/project-doctor.js +45 -0
- package/dist/lib/project-import.d.ts +11 -1
- package/dist/lib/project-import.js +17 -3
- package/dist/lib/project-status.d.ts +25 -5
- package/dist/lib/project-status.js +48 -6
- package/dist/lib/rotate.d.ts +27 -0
- package/dist/lib/rotate.js +44 -17
- package/dist/lib/routines.d.ts +16 -0
- package/dist/lib/routines.js +39 -0
- package/dist/lib/runner.js +34 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/usage-db.d.ts +3 -63
- package/dist/lib/secrets/usage-db.js +46 -186
- package/dist/lib/session/db.d.ts +2 -1
- package/dist/lib/session/db.js +14 -3
- package/dist/lib/session/discover.d.ts +3 -0
- package/dist/lib/session/discover.js +8 -0
- package/dist/lib/session/types.d.ts +1 -0
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/state.d.ts +31 -3
- package/dist/lib/state.js +53 -10
- package/dist/lib/types.d.ts +14 -4
- package/dist/lib/usage-refresh.d.ts +106 -0
- package/dist/lib/usage-refresh.js +238 -0
- package/dist/lib/usage.d.ts +152 -17
- package/dist/lib/usage.js +393 -79
- package/package.json +1 -1
package/dist/commands/view.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { addHostOption } from '../lib/hosts/option.js';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import {
|
|
3
|
+
import { termLink } from '../lib/format.js';
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import * as path from 'path';
|
|
@@ -31,9 +31,31 @@ import { renderHarnessDetail } from './harness.js';
|
|
|
31
31
|
import { loadManifest, isStale } from '../lib/staleness/index.js';
|
|
32
32
|
import { confirm } from '@inquirer/prompts';
|
|
33
33
|
import { formatPath, isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
34
|
-
import { terminalWidth, truncateToWidth, stringWidth } from '../lib/session/width.js';
|
|
34
|
+
import { terminalWidth, truncateToWidth, stringWidth, padToWidth } from '../lib/session/width.js';
|
|
35
35
|
/** Shared account identity formatter, re-exported for the view-specific tests. */
|
|
36
36
|
export const accountColumnLabel = accountDisplayLabel;
|
|
37
|
+
/**
|
|
38
|
+
* Overview (`agents view` with no agent filter) caps compact usage windows so
|
|
39
|
+
* multi-meter agents (Antigravity's four model quotas, Droid's three buckets)
|
|
40
|
+
* cannot force every row to pad past the terminal width and wrap. Single-agent
|
|
41
|
+
* views leave the cap unset and show every blocking window.
|
|
42
|
+
*/
|
|
43
|
+
const OVERVIEW_MAX_USAGE_WINDOWS = 2;
|
|
44
|
+
/** Fixed width for the last-active column ("just now", "8h ago", "2d ago"). */
|
|
45
|
+
const LAST_ACTIVE_COL_WIDTH = 10;
|
|
46
|
+
/**
|
|
47
|
+
* Join fixed view columns with a consistent two-space gutter. Empty trailing
|
|
48
|
+
* columns are dropped so a row without an auth chip does not grow a dangling
|
|
49
|
+
* gutter, but interior empties stay padded so later columns stay aligned.
|
|
50
|
+
*/
|
|
51
|
+
export function joinViewColumns(cols) {
|
|
52
|
+
// Trim only pure-trailing empty strings so auth/status can be absent without
|
|
53
|
+
// shifting earlier columns for rows that do carry them.
|
|
54
|
+
let end = cols.length;
|
|
55
|
+
while (end > 0 && cols[end - 1] === '')
|
|
56
|
+
end--;
|
|
57
|
+
return cols.slice(0, end).join(' ');
|
|
58
|
+
}
|
|
37
59
|
/**
|
|
38
60
|
* Custom harnesses (the `~/.agents/profiles/*.yml` bundles), sorted by name and
|
|
39
61
|
* optionally narrowed to the ones that run on one host agent. YAMLs that fail
|
|
@@ -246,6 +268,8 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
246
268
|
: 'Checking installed agents...';
|
|
247
269
|
const spinner = ora({ text: spinnerText, isSilent: !process.stdout.isTTY }).start();
|
|
248
270
|
const agentsToShow = filterAgentId ? [filterAgentId] : ALL_AGENT_IDS;
|
|
271
|
+
// Overview caps meter count; single-agent view shows every blocking window.
|
|
272
|
+
const usageWindowCap = filterAgentId ? undefined : OVERVIEW_MAX_USAGE_WINDOWS;
|
|
249
273
|
// A globally-installed CLI is superseded only by a NORMAL managed version — that
|
|
250
274
|
// is when agents-cli owns the launcher and a "global" row would just be our own
|
|
251
275
|
// shim reported back. `--isolated` promises the opposite: no default, no bare
|
|
@@ -265,7 +289,6 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
265
289
|
const cliStates = Object.fromEntries(await Promise.all(agentsToShow
|
|
266
290
|
.filter((agentId) => !hasNonIsolatedVersion(agentId))
|
|
267
291
|
.map(async (agentId) => [agentId, await getUnmanagedCliState(agentId)])));
|
|
268
|
-
spinner.stop();
|
|
269
292
|
const showPaths = !!filterAgentId;
|
|
270
293
|
const harnesses = getHarnesses(filterAgentId);
|
|
271
294
|
// Auto-heal stale versioned aliases. Pre-v2 aliases (e.g. pre-CLAUDE_CONFIG_DIR
|
|
@@ -287,11 +310,15 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
287
310
|
}
|
|
288
311
|
}
|
|
289
312
|
// Shim healing is silent — users don't need to know about internal repairs
|
|
290
|
-
console.log(chalk.bold('Installed Agent CLIs\n'));
|
|
291
313
|
const selfHost = machineId();
|
|
292
314
|
// Read the auth-health cache once (not per version row — see the batching note above).
|
|
293
315
|
const authCache = readAuthHealthCache();
|
|
294
|
-
// Pre-fetch account info for all versions in parallel
|
|
316
|
+
// Pre-fetch account info for all versions in parallel. Spinner stays up through
|
|
317
|
+
// account + usage so a multi-account cold path doesn't leave a blank terminal
|
|
318
|
+
// after "Checking…" vanishes (the hang the screenshots caught).
|
|
319
|
+
spinner.text = filterAgentId
|
|
320
|
+
? `Loading ${agentLabel(filterAgentId)} accounts...`
|
|
321
|
+
: 'Loading accounts and usage...';
|
|
295
322
|
const infoFetches = [];
|
|
296
323
|
const globalInfoFetches = [];
|
|
297
324
|
for (const agentId of agentsToShow) {
|
|
@@ -329,6 +356,7 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
329
356
|
// or org scope, not a specific installed version. Version homes cache those
|
|
330
357
|
// values independently, so older installs can show stale values. Reuse the
|
|
331
358
|
// freshest cache entry per stable usage identity and keep lastActive per version.
|
|
359
|
+
// Goes through the unified usage core (SWR cache + concurrency cap + timeout).
|
|
332
360
|
const { canonicalByUsageKey, usageByKey } = await getUsageInfoByIdentity([
|
|
333
361
|
...infoResults.map(({ agentId, home, version, info }) => ({
|
|
334
362
|
agentId,
|
|
@@ -342,6 +370,8 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
342
370
|
info,
|
|
343
371
|
})),
|
|
344
372
|
], { forceRefresh: viewOpts?.forceRefresh });
|
|
373
|
+
spinner.stop();
|
|
374
|
+
console.log(chalk.bold('Installed Agent CLIs\n'));
|
|
345
375
|
const mergeCanonical = (info) => {
|
|
346
376
|
const key = getUsageLookupKey(info);
|
|
347
377
|
if (!key)
|
|
@@ -441,7 +471,8 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
441
471
|
}
|
|
442
472
|
}
|
|
443
473
|
}
|
|
444
|
-
// Second pass: compute max visible usage + status widths (now that maxPlanWidth is settled)
|
|
474
|
+
// Second pass: compute max visible usage + status widths (now that maxPlanWidth is settled).
|
|
475
|
+
// stringWidth (not String.length) so chalk + block-bar glyphs pad correctly.
|
|
445
476
|
for (const agentId of versionManaged) {
|
|
446
477
|
const versions = listInstalledVersions(agentId);
|
|
447
478
|
for (const v of versions) {
|
|
@@ -451,10 +482,14 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
451
482
|
const usageInfo = usageKey ? usageByKey.get(usageKey) : undefined;
|
|
452
483
|
const usageUnavailable = agentReportsUsage(agentId) && !!info?.signedIn && !usageInfo?.snapshot;
|
|
453
484
|
const usageUnverified = !!usageInfo?.snapshot && !!usageInfo.error;
|
|
454
|
-
const usageStr = formatUsageSummary(info?.plan || null, usageInfo?.snapshot || null, maxPlanWidth, {
|
|
455
|
-
|
|
485
|
+
const usageStr = formatUsageSummary(info?.plan || null, usageInfo?.snapshot || null, maxPlanWidth, {
|
|
486
|
+
unavailable: usageUnavailable,
|
|
487
|
+
unverified: usageUnverified,
|
|
488
|
+
maxWindows: usageWindowCap,
|
|
489
|
+
});
|
|
490
|
+
maxUsageWidth = Math.max(maxUsageWidth, stringWidth(usageStr));
|
|
456
491
|
const statusStr = formatUsageStatusBadge(info?.usageStatus);
|
|
457
|
-
maxStatusWidth = Math.max(maxStatusWidth,
|
|
492
|
+
maxStatusWidth = Math.max(maxStatusWidth, stringWidth(statusStr));
|
|
458
493
|
}
|
|
459
494
|
}
|
|
460
495
|
for (const agentId of versionManaged) {
|
|
@@ -496,18 +531,26 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
496
531
|
const vInfo = rawInfo ? mergeCanonical(rawInfo) : undefined;
|
|
497
532
|
const usageKey = getUsageLookupKey(vInfo);
|
|
498
533
|
const usageInfo = usageKey ? usageByKey.get(usageKey) : undefined;
|
|
499
|
-
//
|
|
534
|
+
// Fixed columns for every signed-in row so status / lastActive / auth
|
|
535
|
+
// stay vertically aligned across agents — even when this row has no
|
|
536
|
+
// usage bars or no rate-limit badge. Skipping empty columns mid-table
|
|
537
|
+
// was what made the multi-agent view look unjustified next to
|
|
538
|
+
// `agents view claude`.
|
|
500
539
|
const parts = [` ${label}`];
|
|
501
540
|
// Configured model — same priority as the version, right beside it.
|
|
502
541
|
if (maxModelWidth > 0) {
|
|
503
542
|
const model = modelByKey.get(`${agentId}:${version}`) ?? '';
|
|
504
|
-
parts.push(chalk.yellow(model
|
|
543
|
+
parts.push(chalk.yellow(padToWidth(model, maxModelWidth)));
|
|
505
544
|
}
|
|
506
545
|
const hasEmail = !!vInfo?.email;
|
|
507
546
|
const signedIn = !!vInfo?.signedIn;
|
|
508
547
|
const usageUnavailable = agentReportsUsage(agentId) && signedIn && !usageInfo?.snapshot;
|
|
509
548
|
const usageUnverified = !!usageInfo?.snapshot && !!usageInfo.error;
|
|
510
|
-
const usageStr = formatUsageSummary(vInfo?.plan || null, usageInfo?.snapshot || null, maxPlanWidth, {
|
|
549
|
+
const usageStr = formatUsageSummary(vInfo?.plan || null, usageInfo?.snapshot || null, maxPlanWidth, {
|
|
550
|
+
unavailable: usageUnavailable,
|
|
551
|
+
unverified: usageUnverified,
|
|
552
|
+
maxWindows: usageWindowCap,
|
|
553
|
+
});
|
|
511
554
|
const hasUsage = usageStr.length > 0;
|
|
512
555
|
// Only show lastActive for versions with an actual logged-in account.
|
|
513
556
|
// Otherwise it reflects install time (misleading "just now" for fresh installs).
|
|
@@ -534,25 +577,21 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
534
577
|
: '(logged out — log in with: ' + loginHint(agentId) + ')'));
|
|
535
578
|
}
|
|
536
579
|
else {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
parts.push(
|
|
544
|
-
}
|
|
545
|
-
if (hasUsage || hasActive) {
|
|
546
|
-
const usagePad = ' '.repeat(Math.max(0, maxUsageWidth - visibleWidth(usageStr)));
|
|
547
|
-
parts.push(usageStr + usagePad);
|
|
580
|
+
// Always emit account / usage / status / lastActive columns once any
|
|
581
|
+
// signed-in row exists in the table (widths are global). Empty cells
|
|
582
|
+
// are space-padded so later columns do not drift left.
|
|
583
|
+
const display = accountColumnLabel(vInfo);
|
|
584
|
+
parts.push(display ? chalk.cyan(padToWidth(display, maxEmail)) : ' '.repeat(maxEmail));
|
|
585
|
+
if (maxUsageWidth > 0) {
|
|
586
|
+
parts.push(padToWidth(usageStr, maxUsageWidth));
|
|
548
587
|
}
|
|
549
|
-
const statusStr = formatUsageStatusBadge(vInfo?.usageStatus);
|
|
550
588
|
if (maxStatusWidth > 0) {
|
|
551
|
-
const
|
|
552
|
-
parts.push(statusStr
|
|
589
|
+
const statusStr = formatUsageStatusBadge(vInfo?.usageStatus);
|
|
590
|
+
parts.push(padToWidth(statusStr, maxStatusWidth));
|
|
553
591
|
}
|
|
554
|
-
|
|
555
|
-
|
|
592
|
+
// Fixed-width lastActive so the auth chip (●/○/◐) lines up even when
|
|
593
|
+
// some rows have no email-derived lastActive.
|
|
594
|
+
parts.push(hasActive ? padToWidth(activeStr, LAST_ACTIVE_COL_WIDTH) : ' '.repeat(LAST_ACTIVE_COL_WIDTH));
|
|
556
595
|
}
|
|
557
596
|
if (runDefaultBits.length > 0) {
|
|
558
597
|
parts.push(chalk.gray(`run ${runDefaultBits.join(' ')}`));
|
|
@@ -560,7 +599,7 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
560
599
|
const authChip = liveAuthChip(authCache, selfHost, agentId, version);
|
|
561
600
|
if (authChip)
|
|
562
601
|
parts.push(authChip);
|
|
563
|
-
console.log(parts
|
|
602
|
+
console.log(joinViewColumns(parts));
|
|
564
603
|
if (showPaths) {
|
|
565
604
|
const versionDir = getVersionDir(agentId, version);
|
|
566
605
|
console.log(chalk.gray(` ${versionDir}`));
|
|
@@ -586,18 +625,25 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
586
625
|
const cliState = cliStates[agentId];
|
|
587
626
|
return `${cliState?.version || 'installed'} (global)`.length;
|
|
588
627
|
}));
|
|
589
|
-
// Pre-pass: max badge
|
|
590
|
-
//
|
|
591
|
-
// have "out of credits" shifts every other row's `lastActive` left by
|
|
592
|
-
// ~16 chars, exactly what the version-managed block at maxStatusWidth
|
|
593
|
-
// already solves above.
|
|
628
|
+
// Pre-pass: max badge/usage/email widths so columns line up the same way
|
|
629
|
+
// the version-managed block does (stringWidth for chalk-aware padding).
|
|
594
630
|
let gMaxStatusWidth = 0;
|
|
631
|
+
let gMaxUsageWidth = 0;
|
|
632
|
+
let gMaxEmail = 0;
|
|
595
633
|
for (const agentId of globallyInstalled) {
|
|
596
634
|
const gInfoRaw = globalInfoMap.get(agentId);
|
|
597
635
|
const gInfo = gInfoRaw ? mergeCanonical(gInfoRaw) : undefined;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
636
|
+
gMaxStatusWidth = Math.max(gMaxStatusWidth, stringWidth(formatUsageStatusBadge(gInfo?.usageStatus)));
|
|
637
|
+
const gUsageKey = getUsageLookupKey(gInfo);
|
|
638
|
+
const gUsage = gUsageKey ? usageByKey.get(gUsageKey) : undefined;
|
|
639
|
+
const gUsageStr = formatUsageSummary(gInfo?.plan || null, gUsage?.snapshot || null, 3, {
|
|
640
|
+
unverified: !!gUsage?.snapshot && !!gUsage.error,
|
|
641
|
+
maxWindows: usageWindowCap,
|
|
642
|
+
});
|
|
643
|
+
gMaxUsageWidth = Math.max(gMaxUsageWidth, stringWidth(gUsageStr));
|
|
644
|
+
const gDisplay = accountColumnLabel(gInfo);
|
|
645
|
+
if (gDisplay)
|
|
646
|
+
gMaxEmail = Math.max(gMaxEmail, gDisplay.length);
|
|
601
647
|
}
|
|
602
648
|
for (const agentId of globallyInstalled) {
|
|
603
649
|
const agent = AGENTS[agentId];
|
|
@@ -613,22 +659,21 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
613
659
|
const gUsage = gUsageKey ? usageByKey.get(gUsageKey) : undefined;
|
|
614
660
|
const gUsageStr = formatUsageSummary(gInfo?.plan || null, gUsage?.snapshot || null, 3, {
|
|
615
661
|
unverified: !!gUsage?.snapshot && !!gUsage.error,
|
|
662
|
+
maxWindows: usageWindowCap,
|
|
616
663
|
});
|
|
617
664
|
const gActiveStr = gInfo ? formatLastActive(gInfo.lastActive) : '';
|
|
618
665
|
if (gInfo?.email || gUsageStr || gActiveStr || gInfo?.signedIn) {
|
|
619
666
|
const gDisplay = accountColumnLabel(gInfo);
|
|
620
|
-
parts.push(gDisplay ? chalk.cyan(gDisplay) : '');
|
|
667
|
+
parts.push(gDisplay ? chalk.cyan(padToWidth(gDisplay, gMaxEmail)) : ' '.repeat(gMaxEmail));
|
|
621
668
|
}
|
|
622
|
-
if (
|
|
623
|
-
parts.push(gUsageStr);
|
|
624
|
-
const gStatusStr = formatUsageStatusBadge(gInfo?.usageStatus);
|
|
669
|
+
if (gMaxUsageWidth > 0)
|
|
670
|
+
parts.push(padToWidth(gUsageStr, gMaxUsageWidth));
|
|
625
671
|
if (gMaxStatusWidth > 0) {
|
|
626
|
-
|
|
627
|
-
parts.push(gStatusStr + statusPad);
|
|
672
|
+
parts.push(padToWidth(formatUsageStatusBadge(gInfo?.usageStatus), gMaxStatusWidth));
|
|
628
673
|
}
|
|
629
674
|
if (gActiveStr)
|
|
630
|
-
parts.push(gActiveStr);
|
|
631
|
-
console.log(parts
|
|
675
|
+
parts.push(padToWidth(gActiveStr, LAST_ACTIVE_COL_WIDTH));
|
|
676
|
+
console.log(joinViewColumns(parts));
|
|
632
677
|
if (showPaths && cliState?.path) {
|
|
633
678
|
console.log(chalk.gray(` ${cliState.path}`));
|
|
634
679
|
}
|
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, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadFeed, loadActivity, 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, 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, loadActivity, 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';
|
|
@@ -197,6 +197,18 @@ program.hook('postAction', (_thisCommand, actionCommand) => {
|
|
|
197
197
|
command,
|
|
198
198
|
...(durationMs !== undefined ? { durationMs } : {}),
|
|
199
199
|
});
|
|
200
|
+
if (parts[0] === 'run') {
|
|
201
|
+
const agentName = actionCommand.args?.[0] ? String(actionCommand.args[0]).split('@')[0] : 'run';
|
|
202
|
+
void import('./lib/analytics/usage-db.js').then(({ recordUsage }) => {
|
|
203
|
+
recordUsage({
|
|
204
|
+
kind: 'agent',
|
|
205
|
+
name: agentName || 'run',
|
|
206
|
+
event: 'invoke',
|
|
207
|
+
source: 'cli',
|
|
208
|
+
meta: durationMs !== undefined ? { durationMs } : undefined,
|
|
209
|
+
});
|
|
210
|
+
}).catch(() => { });
|
|
211
|
+
}
|
|
200
212
|
// Disposable perf warehouse — fail-soft spool append (no SQLite on this path).
|
|
201
213
|
if (durationMs !== undefined && parts[0] !== 'perf') {
|
|
202
214
|
void import('./lib/perf/spool.js').then(({ recordSample }) => {
|
|
@@ -933,6 +945,7 @@ async function registerAllEagerCommands() {
|
|
|
933
945
|
await reg(loadUsage);
|
|
934
946
|
await reg(loadCost);
|
|
935
947
|
await reg(loadPerf);
|
|
948
|
+
await reg(loadTrends);
|
|
936
949
|
await reg(loadOutput);
|
|
937
950
|
await reg(loadBudget);
|
|
938
951
|
await reg(loadAlias);
|
package/dist/lib/activity.d.ts
CHANGED
|
@@ -89,6 +89,12 @@ export interface ActivityEvent {
|
|
|
89
89
|
tool?: string;
|
|
90
90
|
/** One-line human summary (plan title, PR command, sub-agent role, status text). */
|
|
91
91
|
detail?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Short subject for deliberate status posts (`feed post --title`). Phone
|
|
94
|
+
* broadcasts put this on the first line; `detail` is the body. Optional on
|
|
95
|
+
* older events that only carried `detail`.
|
|
96
|
+
*/
|
|
97
|
+
title?: string;
|
|
92
98
|
/** Extracted URL when the event has one (e.g. the opened PR). */
|
|
93
99
|
url?: string;
|
|
94
100
|
/** Auto-stamped process identity for deliberate posts (from pid registry / env). */
|
package/dist/lib/activity.js
CHANGED
|
@@ -141,6 +141,7 @@ function parseLine(line) {
|
|
|
141
141
|
kind: parsed.kind,
|
|
142
142
|
tool: parsed.tool,
|
|
143
143
|
detail: parsed.detail,
|
|
144
|
+
title: typeof parsed.title === 'string' ? parsed.title : undefined,
|
|
144
145
|
url: parsed.url,
|
|
145
146
|
pid: typeof parsed.pid === 'number' ? parsed.pid : undefined,
|
|
146
147
|
launchId: parsed.launchId,
|
|
@@ -398,6 +399,8 @@ export function formatProgressUpdate(ev, opts = {}) {
|
|
|
398
399
|
].filter((c) => Boolean(c));
|
|
399
400
|
if (chips.length > 0)
|
|
400
401
|
lines.push(` ${chalk.gray(chips.join(' · '))}`);
|
|
402
|
+
if (ev.title)
|
|
403
|
+
lines.push(` ${chalk.white.bold(ev.title)}`);
|
|
401
404
|
if (ev.detail)
|
|
402
405
|
lines.push(` ${chalk.white(`"${ev.detail}"`)}`);
|
|
403
406
|
if (ev.attachments?.length) {
|
package/dist/lib/agents.js
CHANGED
|
@@ -1754,8 +1754,8 @@ export async function getAccountInfo(agentId, home) {
|
|
|
1754
1754
|
}
|
|
1755
1755
|
}
|
|
1756
1756
|
// Fresh window for the cached session walk. Matches USAGE_CACHE_FRESH_MS in
|
|
1757
|
-
// usage.ts so a launch storm reuses both probes for the same period.
|
|
1758
|
-
const LAST_ACTIVE_CACHE_FRESH_MS =
|
|
1757
|
+
// usage.ts (5 minutes) so a launch storm reuses both probes for the same period.
|
|
1758
|
+
const LAST_ACTIVE_CACHE_FRESH_MS = 5 * 60 * 1000;
|
|
1759
1759
|
const getLastActiveCachePath = () => path.join(getCacheDir(), 'last-active.json');
|
|
1760
1760
|
/**
|
|
1761
1761
|
* Determine when the agent was last used by checking session file mtimes,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RECIPE_IDS, runRecipe, trendsWindow, type RecipeId, type RecipeSection, type TrendsWindow } from './recipes.js';
|
|
2
|
+
export interface TrendsDashboard {
|
|
3
|
+
window: TrendsWindow;
|
|
4
|
+
durationMs: number;
|
|
5
|
+
sections: RecipeSection[];
|
|
6
|
+
}
|
|
7
|
+
export declare function buildTrendsDashboard(opts?: {
|
|
8
|
+
days?: number;
|
|
9
|
+
ids?: RecipeId[];
|
|
10
|
+
}): TrendsDashboard;
|
|
11
|
+
export { trendsWindow, runRecipe, RECIPE_IDS, type RecipeId, type RecipeSection };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { RECIPE_IDS, runRecipe, trendsWindow, } from './recipes.js';
|
|
2
|
+
const DEFAULT_ORDER = [
|
|
3
|
+
'harness-mix',
|
|
4
|
+
'model-mix',
|
|
5
|
+
'session-volume',
|
|
6
|
+
'token-ratio',
|
|
7
|
+
'tools-per-session',
|
|
8
|
+
'secrets-hot',
|
|
9
|
+
'browser-activity',
|
|
10
|
+
'resource-mix',
|
|
11
|
+
];
|
|
12
|
+
export function buildTrendsDashboard(opts = {}) {
|
|
13
|
+
const t0 = Date.now();
|
|
14
|
+
const win = trendsWindow(opts.days ?? 7);
|
|
15
|
+
const ids = opts.ids ?? DEFAULT_ORDER;
|
|
16
|
+
const sections = [];
|
|
17
|
+
for (const id of ids) {
|
|
18
|
+
if (!RECIPE_IDS.includes(id))
|
|
19
|
+
continue;
|
|
20
|
+
const section = runRecipe(id, win);
|
|
21
|
+
if (section.empty)
|
|
22
|
+
continue;
|
|
23
|
+
sections.push(section);
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
window: win,
|
|
27
|
+
durationMs: Date.now() - t0,
|
|
28
|
+
sections,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export { trendsWindow, runRecipe, RECIPE_IDS };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type UsageKind } from './usage-db.js';
|
|
2
|
+
export interface TrendsWindow {
|
|
3
|
+
days: number;
|
|
4
|
+
sinceIso: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RecipeSection {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
store: 'sessions' | 'usage';
|
|
10
|
+
rows: Array<Record<string, string | number | null>>;
|
|
11
|
+
empty?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function trendsWindow(days: number): TrendsWindow;
|
|
14
|
+
export declare function recipeHarnessMix(win: TrendsWindow): RecipeSection;
|
|
15
|
+
export declare function recipeModelMix(win: TrendsWindow): RecipeSection;
|
|
16
|
+
export declare function recipeToolsPerSession(win: TrendsWindow): RecipeSection;
|
|
17
|
+
export declare function recipeTokenRatio(win: TrendsWindow): RecipeSection;
|
|
18
|
+
export declare function recipeSessionVolume(win: TrendsWindow): RecipeSection;
|
|
19
|
+
export declare function recipeSecretsHot(win: TrendsWindow): RecipeSection;
|
|
20
|
+
export declare function recipeBrowserActivity(win: TrendsWindow): RecipeSection;
|
|
21
|
+
export declare function recipeResourceMix(win: TrendsWindow): RecipeSection;
|
|
22
|
+
export type RecipeId = 'harness-mix' | 'model-mix' | 'tools-per-session' | 'token-ratio' | 'session-volume' | 'secrets-hot' | 'browser-activity' | 'resource-mix';
|
|
23
|
+
export declare const RECIPE_IDS: readonly RecipeId[];
|
|
24
|
+
export declare function runRecipe(id: RecipeId, win: TrendsWindow): RecipeSection;
|
|
25
|
+
export declare function listRecipes(): Array<{
|
|
26
|
+
id: RecipeId;
|
|
27
|
+
title: string;
|
|
28
|
+
store: 'sessions' | 'usage';
|
|
29
|
+
}>;
|
|
30
|
+
export declare function hasSessionsInWindow(win: TrendsWindow): boolean;
|
|
31
|
+
export declare function usageKindsPresent(win: TrendsWindow): UsageKind[];
|
|
32
|
+
export declare function usageEventCount(kind: UsageKind, win: TrendsWindow): number;
|