@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.
Files changed (102) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +2 -1
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/doctor.js +5 -2
  5. package/dist/commands/feed.js +63 -40
  6. package/dist/commands/hooks.js +9 -45
  7. package/dist/commands/menubar.js +24 -24
  8. package/dist/commands/message.js +23 -3
  9. package/dist/commands/monitors.js +8 -6
  10. package/dist/commands/perf.d.ts +13 -0
  11. package/dist/commands/perf.js +80 -23
  12. package/dist/commands/projects.d.ts +11 -0
  13. package/dist/commands/projects.js +153 -21
  14. package/dist/commands/routines.js +46 -1
  15. package/dist/commands/ssh.js +69 -0
  16. package/dist/commands/trends.d.ts +2 -0
  17. package/dist/commands/trends.js +158 -0
  18. package/dist/commands/usage.d.ts +4 -4
  19. package/dist/commands/view.d.ts +6 -0
  20. package/dist/commands/view.js +90 -45
  21. package/dist/index.js +14 -1
  22. package/dist/lib/activity.d.ts +6 -0
  23. package/dist/lib/activity.js +3 -0
  24. package/dist/lib/agents.js +2 -2
  25. package/dist/lib/analytics/dashboard.d.ts +11 -0
  26. package/dist/lib/analytics/dashboard.js +31 -0
  27. package/dist/lib/analytics/recipes.d.ts +32 -0
  28. package/dist/lib/analytics/recipes.js +316 -0
  29. package/dist/lib/analytics/usage-db.d.ts +84 -0
  30. package/dist/lib/analytics/usage-db.js +301 -0
  31. package/dist/lib/browser/service.js +18 -0
  32. package/dist/lib/channels/providers/openclaw-telegram.js +1 -1
  33. package/dist/lib/channels/resolve.d.ts +18 -1
  34. package/dist/lib/channels/resolve.js +15 -5
  35. package/dist/lib/cli-resources.d.ts +20 -0
  36. package/dist/lib/cli-resources.js +48 -1
  37. package/dist/lib/daemon.js +51 -14
  38. package/dist/lib/devices/health-report.d.ts +5 -0
  39. package/dist/lib/devices/health-report.js +3 -0
  40. package/dist/lib/feed-broadcast.d.ts +93 -12
  41. package/dist/lib/feed-broadcast.js +235 -30
  42. package/dist/lib/feed-post.d.ts +16 -2
  43. package/dist/lib/feed-post.js +35 -7
  44. package/dist/lib/feed.js +1 -1
  45. package/dist/lib/fleet-cache.d.ts +37 -0
  46. package/dist/lib/fleet-cache.js +40 -0
  47. package/dist/lib/fleet-status.d.ts +53 -0
  48. package/dist/lib/fleet-status.js +120 -0
  49. package/dist/lib/friction-heuristics.d.ts +32 -0
  50. package/dist/lib/friction-heuristics.js +47 -0
  51. package/dist/lib/hooks/cache.js +28 -6
  52. package/dist/lib/hooks/profile.d.ts +8 -0
  53. package/dist/lib/hooks/profile.js +14 -4
  54. package/dist/lib/hooks.d.ts +16 -1
  55. package/dist/lib/hooks.js +133 -18
  56. package/dist/lib/linear-cache.d.ts +63 -0
  57. package/dist/lib/linear-cache.js +146 -0
  58. package/dist/lib/linear-project-counts.d.ts +35 -5
  59. package/dist/lib/linear-project-counts.js +61 -16
  60. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  61. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +3 -1
  62. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  63. package/dist/lib/menubar/install-menubar.d.ts +7 -0
  64. package/dist/lib/menubar/install-menubar.js +36 -6
  65. package/dist/lib/monitors/config.d.ts +1 -1
  66. package/dist/lib/monitors/dispatch.d.ts +6 -2
  67. package/dist/lib/monitors/dispatch.js +10 -15
  68. package/dist/lib/notify.d.ts +42 -6
  69. package/dist/lib/notify.js +41 -32
  70. package/dist/lib/perf/db.d.ts +6 -1
  71. package/dist/lib/perf/db.js +35 -5
  72. package/dist/lib/perf/types.d.ts +10 -0
  73. package/dist/lib/project-doctor.d.ts +36 -0
  74. package/dist/lib/project-doctor.js +45 -0
  75. package/dist/lib/project-import.d.ts +11 -1
  76. package/dist/lib/project-import.js +17 -3
  77. package/dist/lib/project-status.d.ts +25 -5
  78. package/dist/lib/project-status.js +48 -6
  79. package/dist/lib/rotate.d.ts +27 -0
  80. package/dist/lib/rotate.js +44 -17
  81. package/dist/lib/routines.d.ts +16 -0
  82. package/dist/lib/routines.js +39 -0
  83. package/dist/lib/runner.js +34 -0
  84. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  85. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  86. package/dist/lib/secrets/usage-db.d.ts +3 -63
  87. package/dist/lib/secrets/usage-db.js +46 -186
  88. package/dist/lib/session/db.d.ts +2 -1
  89. package/dist/lib/session/db.js +14 -3
  90. package/dist/lib/session/discover.d.ts +3 -0
  91. package/dist/lib/session/discover.js +8 -0
  92. package/dist/lib/session/types.d.ts +1 -0
  93. package/dist/lib/startup/command-registry.d.ts +1 -0
  94. package/dist/lib/startup/command-registry.js +2 -0
  95. package/dist/lib/state.d.ts +31 -3
  96. package/dist/lib/state.js +53 -10
  97. package/dist/lib/types.d.ts +14 -4
  98. package/dist/lib/usage-refresh.d.ts +106 -0
  99. package/dist/lib/usage-refresh.js +238 -0
  100. package/dist/lib/usage.d.ts +152 -17
  101. package/dist/lib/usage.js +393 -79
  102. package/package.json +1 -1
@@ -49,6 +49,7 @@ export declare function usageUnreachableError(agent: string, cause?: unknown): s
49
49
  * fresh" (never force a refresh on a token with no known expiry).
50
50
  */
51
51
  export declare function claudeAccessTokenNeedsRefresh(expiresAt: number | null | undefined, nowMs?: number): boolean;
52
+ export declare function setClaudeUsageCachePathForTest(cachePath: string | null): string | null;
52
53
  /** Discriminator for usage window types. */
53
54
  export type UsageWindowKey = 'session' | 'week' | 'sonnet_week' | 'month';
54
55
  /** A single rate-limit window with utilization percentage and reset time. */
@@ -155,10 +156,37 @@ export declare function buildCanonicalUsageContext(inputs: UsageIdentityInput[])
155
156
  * Claude account with no data) versus simply not applicable (OpenCode).
156
157
  */
157
158
  export declare function agentReportsUsage(agentId: AgentId): boolean;
158
- /** Fetch usage info for all unique accounts in parallel, keyed by usage key. */
159
+ /**
160
+ * Whether an agent's usage source makes a live NETWORK call (Claude/Kimi/Droid/
161
+ * Cursor/Antigravity) versus reading local session logs (Codex/Grok). Only the
162
+ * networked ones go through the on-disk cache, and only they need the daemon's
163
+ * background refresher to keep that cache warm for the routing hot path.
164
+ */
165
+ export declare function agentUsesNetworkUsage(agentId: AgentId): boolean;
166
+ /**
167
+ * Concurrent live usage fetches for a single `agents view` / rotation pass.
168
+ * High enough to finish a multi-account refresh in one round-trip window; low
169
+ * enough that a cold cache of 10+ accounts cannot open 10+ HTTP calls at once
170
+ * (and cannot stack behind delayed responses until the process is pegged).
171
+ */
172
+ export declare const USAGE_FETCH_CONCURRENCY = 3;
173
+ /**
174
+ * How long a cached snapshot is treated as fresh enough that we skip the network
175
+ * entirely. Five minutes balances "still accurate enough to glance at" against
176
+ * "don't re-hit every account on every `agents view` in a tight loop". Was 2
177
+ * minutes; that re-fired too often when delayed responses stacked.
178
+ */
179
+ export declare const USAGE_CACHE_FRESH_MS: number;
180
+ /**
181
+ * Unified entry for every multi-account usage lookup (`agents view`, rotation,
182
+ * JSON export). Deduplicates by usage identity, then fans out through the
183
+ * shared SWR + timeout path with a hard concurrency cap so delayed calls
184
+ * cannot pile up.
185
+ */
159
186
  export declare function getUsageInfoByIdentity(inputs: UsageIdentityInput[], opts?: {
160
187
  forceRefresh?: boolean;
161
188
  maxAgeMs?: number;
189
+ readOnly?: boolean;
162
190
  }): Promise<{
163
191
  canonicalByUsageKey: Map<string, AccountInfo>;
164
192
  usageByKey: Map<string, UsageInfo>;
@@ -174,24 +202,46 @@ export declare function swrWindowMsFor(maxAgeMs?: number): number;
174
202
  /**
175
203
  * Fetch usage for a single identity using stale-while-revalidate.
176
204
  *
177
- * - Cache fresh (< 2 min): return cached snapshot, NO network.
178
- * - Cache stale but < 24h: return cached snapshot instantly, fire background refresh.
179
- * - Cache too stale or absent: block on live fetch, fall back to cache on error.
205
+ * - Cache fresh (< 5 min): return cached snapshot, NO network.
206
+ * - Cache stale but < 24h: return cached snapshot instantly, enqueue a
207
+ * concurrency-capped background refresh.
208
+ * - Cache too stale or absent: block on live fetch (shared in-flight promise),
209
+ * fall back to cache on error.
180
210
  *
181
- * This keeps `agents run` startup off the network on the hot path. The first
182
- * invocation after a cold install or 24h gap still blocks once to seed the
183
- * cache; every run after that returns instantly while the cache silently
184
- * refreshes in the background.
211
+ * This keeps `agents run` / `agents view` off the network on the hot path. The
212
+ * first invocation after a cold install or 24h gap still blocks once to seed
213
+ * the cache; every run after that returns instantly while the cache silently
214
+ * refreshes in the background — never more than {@link USAGE_BG_REFRESH_CONCURRENCY}
215
+ * at a time.
185
216
  */
186
217
  export declare function getUsageInfoForIdentity(input: UsageIdentityInput, opts?: {
187
218
  forceRefresh?: boolean;
188
219
  maxAgeMs?: number;
220
+ readOnly?: boolean;
189
221
  }): Promise<UsageInfo>;
190
- /** Format a one-line usage summary with compact bars for inline display. */
191
- export declare function formatUsageSummary(plan: string | null, snapshot: UsageSnapshot | null, planWidth?: number, opts?: {
222
+ /**
223
+ * Pick which usage windows to render in a compact one-line summary.
224
+ *
225
+ * Overview rows (`agents view` all agents) must stay narrow enough that one
226
+ * multi-window agent (Antigravity's four model quotas, Droid's three buckets)
227
+ * does not force every other row to pad to ~200 columns and wrap. Prefer the
228
+ * canonical session + week windows when present; otherwise take the highest
229
+ * utilization remaining. Returns the full set when `maxWindows` is unset.
230
+ */
231
+ export declare function pickCompactUsageWindows(windows: UsageWindow[], maxWindows?: number): UsageWindow[];
232
+ /** Options for {@link formatUsageSummary}. */
233
+ export interface FormatUsageSummaryOpts {
192
234
  unavailable?: boolean;
193
235
  unverified?: boolean;
194
- }): string;
236
+ /**
237
+ * Cap how many usage windows render on one line. Overview (`agents view`
238
+ * with no agent filter) passes 2 so multi-window agents cannot blow out
239
+ * column width; single-agent and detail views leave this unset.
240
+ */
241
+ maxWindows?: number;
242
+ }
243
+ /** Format a one-line usage summary with compact bars for inline display. */
244
+ export declare function formatUsageSummary(plan: string | null, snapshot: UsageSnapshot | null, planWidth?: number, opts?: FormatUsageSummaryOpts): string;
195
245
  /**
196
246
  * Derive an account's real throttle state from its live usage windows — the
197
247
  * single signal both the `agents view` badge and run-rotation eligibility share
@@ -212,6 +262,39 @@ export declare function formatUsageSummary(plan: string | null, snapshot: UsageS
212
262
  * one model, not the account, so it shouldn't flip the whole row to throttled.
213
263
  */
214
264
  export declare function deriveUsageStatusFromSnapshot(snapshot: UsageSnapshot | null | undefined): 'available' | 'rate_limited' | null;
265
+ /** A prior sample of one window's utilization, for burn-rate projection. */
266
+ export interface UsagePriorSample {
267
+ /** Epoch ms the prior snapshot was captured. */
268
+ capturedAt: number;
269
+ /** The session window's `usedPercent` in that prior snapshot. */
270
+ usedPercent: number;
271
+ }
272
+ /**
273
+ * An account's throttle state PLUS how long until it caps, projected from the
274
+ * burn rate on its 5-hour `session` window — the window that throttles the next
275
+ * request soonest. `deriveUsageStatusFromSnapshot` answers only "maxed right
276
+ * now (100%)?"; this answers "and how close is it getting?", so routing can
277
+ * deprioritize an account burning toward its cap before it actually hits it,
278
+ * instead of treating 85%-and-climbing the same as 85%-and-idle.
279
+ *
280
+ * `minutesToLimit`:
281
+ * - `0` — already rate-limited (a blocking window at 100%).
282
+ * - `n > 0` — projected minutes until the session window reaches 100%, from
283
+ * `(100 - used) / burnRatePerMinute`, where the burn rate is
284
+ * measured between `prev` and this snapshot.
285
+ * - `null` — unknown: no snapshot, no session window, no prior sample, or
286
+ * usage flat/falling since `prev` (a reset or an idle account is
287
+ * not "projected to cap", so it is NOT deprioritized).
288
+ *
289
+ * Pure: the daemon's refresher supplies `prev` from the last snapshot it stored
290
+ * (`usage-refresh.ts`); the routing hot path reads the daemon-computed result
291
+ * from the headroom cache rather than recomputing (it has no `prev`).
292
+ */
293
+ export interface UsageHeadroom {
294
+ status: 'available' | 'rate_limited' | null;
295
+ minutesToLimit: number | null;
296
+ }
297
+ export declare function deriveUsageHeadroom(snapshot: UsageSnapshot | null | undefined, prev?: UsagePriorSample | null): UsageHeadroom;
215
298
  /**
216
299
  * Compact colored badge for the account's overall usage status. Renders only
217
300
  * when the account is throttled — `available` and `null` return ''.
@@ -433,13 +516,65 @@ export interface CursorUsageResponse {
433
516
  * bar to draw here and return no windows rather than a misleading empty gauge.
434
517
  */
435
518
  export declare function normalizeCursorUsage(data: CursorUsageResponse): UsageWindow[];
519
+ /** Per-window plan usage percentages Cursor's dashboard breaks usage into (Auto+Composer / API / Total). */
520
+ interface CursorPlanUsage {
521
+ autoPercentUsed?: number | null;
522
+ apiPercentUsed?: number | null;
523
+ totalPercentUsed?: number | null;
524
+ }
525
+ /** Response shape from Cursor's dashboard current-period-usage endpoint (subset we render). */
526
+ export interface CursorPeriodUsageResponse {
527
+ planUsage?: CursorPlanUsage | null;
528
+ /** ISO timestamp, or a unix-ms string, marking the end of the current billing cycle. */
529
+ billingCycleEnd?: string | number | null;
530
+ }
531
+ /** Response shape from Cursor's usage-summary endpoint (subset we render). */
532
+ export interface CursorUsageSummaryResponse {
533
+ /** True on a plan with no consumption cap; only tiered self-serve plans populate the percent fields. */
534
+ isUnlimited?: boolean | null;
535
+ individualUsage?: {
536
+ plan?: CursorPlanUsage | null;
537
+ } | null;
538
+ billingCycleEnd?: string | number | null;
539
+ }
540
+ /**
541
+ * Normalize Cursor's dashboard `get-current-period-usage` payload — the
542
+ * primary usage source, giving the same Auto+Composer / API / Total breakdown
543
+ * the web dashboard shows.
544
+ */
545
+ export declare function normalizeCursorPeriodUsage(data: CursorPeriodUsageResponse): UsageWindow[];
546
+ /**
547
+ * Normalize Cursor's `usage-summary` fallback payload — the same Auto/API/Total
548
+ * breakdown nested under `individualUsage.plan`, used when the primary
549
+ * dashboard endpoint returns no usable `planUsage` (seen on some
550
+ * enterprise/team accounts). An unlimited plan (`isUnlimited: true`) with no
551
+ * usable percent has nothing to draw and returns no windows, rather than a
552
+ * misleading empty gauge.
553
+ */
554
+ export declare function normalizeCursorUsageSummary(data: CursorUsageSummaryResponse): UsageWindow[];
436
555
  /**
437
- * Fetch Cursor usage from the dashboard usage endpoint. Cursor authenticates the
438
- * request with a `WorkosCursorSessionToken` cookie of the form
439
- * `<oauth-subject>::<access-token>` (the same pair the web dashboard sends), not a
440
- * bearer header. Free/legacy plans return a monthly request bar; usage-based plans
441
- * have no request cap, so they return a live-but-window-less snapshot (the account
442
- * row still renders, without a misleading empty gauge).
556
+ * Fetch Cursor usage. Cursor authenticates every one of these requests with a
557
+ * `WorkosCursorSessionToken` cookie of the form `<oauth-subject>::<access-token>`
558
+ * (the same pair the web dashboard sends), not a bearer header, so all three
559
+ * sources below share one resolved cookie.
560
+ *
561
+ * Three sources, tried in order, because no single endpoint carries usable data
562
+ * for every plan shape:
563
+ *
564
+ * 1. `get-current-period-usage` — the primary source, and the richest: the
565
+ * Auto+Composer / API / Total percent breakdown the dashboard itself shows.
566
+ * 2. `usage-summary` — some enterprise/team accounts return no usable
567
+ * `planUsage` from (1); this nests the same three percentages under
568
+ * `individualUsage.plan` instead.
569
+ * 3. The legacy `/api/usage` request-cap endpoint — the original source,
570
+ * kept as the final fallback for free/legacy plans that predate the
571
+ * percent-based breakdown above and only ever exposed a monthly request cap.
572
+ *
573
+ * The first source to yield a non-empty window list wins; a source that errors
574
+ * or returns no usable numbers falls through to the next rather than surfacing
575
+ * an error — only the last resort's own response/error is surfaced when every
576
+ * source comes up empty, so a plan enrolled in exactly one billing model still
577
+ * renders instead of reporting three swallowed failures.
443
578
  */
444
579
  declare function getCursorUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
445
580
  /** The OAuth token `agy` stores (inside `{ token: … }`) in the OS keyring or file. */