@phnx-labs/agents-cli 1.22.45 → 1.22.47
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 +303 -0
- package/LICENSE +102 -182
- package/README.md +54 -17
- package/dist/cli/command-registry.d.ts +2 -1
- package/dist/cli/command-registry.js +6 -2
- package/dist/commands/accounts.js +56 -12
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +229 -0
- package/dist/commands/browser-sessions-picker.js +9 -7
- package/dist/commands/browser.js +266 -15
- package/dist/commands/config.js +26 -4
- package/dist/commands/cost.d.ts +1 -1
- package/dist/commands/detach.d.ts +13 -0
- package/dist/commands/detach.js +11 -8
- package/dist/commands/exec.js +55 -38
- package/dist/commands/feed-watch.d.ts +2 -0
- package/dist/commands/feed-watch.js +28 -0
- package/dist/commands/feed.js +27 -0
- package/dist/commands/feedback.js +3 -19
- package/dist/commands/insights.d.ts +1 -1
- package/dist/commands/insights.js +2 -2
- package/dist/commands/lease.js +3 -3
- package/dist/commands/routines.js +1 -1
- package/dist/commands/sessions-picker-factory.d.ts +2 -1
- package/dist/commands/sessions-picker-factory.js +1 -1
- package/dist/commands/sessions-stop.d.ts +24 -0
- package/dist/commands/sessions-stop.js +66 -0
- package/dist/commands/sessions-trace.d.ts +82 -0
- package/dist/commands/sessions-trace.js +387 -0
- package/dist/commands/sessions.js +4 -0
- package/dist/commands/ssh.d.ts +15 -0
- package/dist/commands/ssh.js +194 -16
- package/dist/commands/teams.js +34 -2
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +19 -32
- package/dist/commands/watchdog.js +9 -14
- package/dist/lib/account-capabilities.d.ts +4 -0
- package/dist/lib/account-capabilities.js +13 -9
- package/dist/lib/account-registry.d.ts +2 -0
- package/dist/lib/account-registry.js +30 -2
- package/dist/lib/accounting/rotate.d.ts +9 -0
- package/dist/lib/accounting/rotate.js +12 -2
- package/dist/lib/accounting/usage.d.ts +92 -6
- package/dist/lib/accounting/usage.js +329 -75
- package/dist/lib/agent-modes.js +29 -1
- package/dist/lib/agent-spec/agents.d.ts +4 -3
- package/dist/lib/agent-spec/agents.js +20 -7
- package/dist/lib/analytics/mix-commands.d.ts +1 -1
- package/dist/lib/analytics/mix-commands.js +2 -2
- package/dist/lib/auth-health.d.ts +13 -3
- package/dist/lib/auth-health.js +50 -8
- package/dist/lib/browser/devices.d.ts +19 -0
- package/dist/lib/browser/devices.js +23 -0
- package/dist/lib/browser/ipc.d.ts +1 -1
- package/dist/lib/browser/ipc.js +33 -7
- package/dist/lib/browser/profiles.d.ts +106 -1
- package/dist/lib/browser/profiles.js +300 -17
- package/dist/lib/browser/remote-control.d.ts +26 -2
- package/dist/lib/browser/remote-control.js +33 -3
- package/dist/lib/browser/runtime-state.d.ts +9 -0
- package/dist/lib/browser/runtime-state.js +24 -8
- package/dist/lib/browser/service.d.ts +37 -0
- package/dist/lib/browser/service.js +78 -20
- package/dist/lib/browser/sessions-list.d.ts +1 -3
- package/dist/lib/browser/sessions-list.js +3 -16
- package/dist/lib/browser/types.d.ts +12 -1
- package/dist/lib/cli-resources.js +17 -15
- package/dist/lib/codex-policy.d.ts +11 -1
- package/dist/lib/codex-policy.js +20 -5
- package/dist/lib/config-keys.d.ts +4 -3
- package/dist/lib/config-keys.js +17 -7
- package/dist/lib/daemon/runner.js +15 -2
- package/dist/lib/daemon-ticks.js +2 -2
- package/dist/lib/device-config.d.ts +25 -8
- package/dist/lib/device-config.js +90 -17
- package/dist/lib/devices/config-migration.d.ts +7 -0
- package/dist/lib/devices/config-migration.js +67 -3
- package/dist/lib/devices/harness-inventory.js +20 -3
- package/dist/lib/devices/health.d.ts +27 -2
- package/dist/lib/devices/health.js +39 -9
- package/dist/lib/devices/interactive-host.d.ts +15 -0
- package/dist/lib/devices/interactive-host.js +53 -0
- package/dist/lib/devices/registry.d.ts +61 -4
- package/dist/lib/devices/registry.js +94 -38
- package/dist/lib/devices/stats-cache.d.ts +6 -0
- package/dist/lib/devices/stats-cache.js +10 -2
- package/dist/lib/exec.d.ts +25 -3
- package/dist/lib/exec.js +75 -22
- package/dist/lib/feed/answer.d.ts +29 -0
- package/dist/lib/feed/answer.js +145 -0
- package/dist/lib/feed/attention.d.ts +83 -0
- package/dist/lib/feed/attention.js +0 -0
- package/dist/lib/feed/feed.d.ts +109 -0
- package/dist/lib/feed/feed.js +154 -3
- package/dist/lib/feed/pr-status.d.ts +14 -0
- package/dist/lib/feed/pr-status.js +47 -0
- package/dist/lib/feed/watch.d.ts +59 -0
- package/dist/lib/feed/watch.js +180 -0
- package/dist/lib/fleet/capture.js +6 -0
- package/dist/lib/fleet/remote-login.js +8 -3
- package/dist/lib/fleet/types.d.ts +17 -0
- package/dist/lib/harness/adapter.d.ts +11 -0
- package/dist/lib/harness/adapters/claude.js +38 -24
- package/dist/lib/harness/adapters/codex.js +2 -4
- package/dist/lib/hooks/cache.js +8 -0
- package/dist/lib/hooks/match.d.ts +3 -0
- package/dist/lib/hooks/match.js +25 -0
- package/dist/lib/hosts/passthrough.js +16 -0
- package/dist/lib/hosts/registry.js +7 -0
- package/dist/lib/identity/client.d.ts +53 -0
- package/dist/lib/identity/client.js +106 -0
- package/dist/lib/identity/index.d.ts +115 -0
- package/dist/lib/identity/index.js +82 -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/menubar/install-menubar.d.ts +13 -0
- package/dist/lib/menubar/install-menubar.js +26 -2
- package/dist/lib/open-url.d.ts +60 -2
- package/dist/lib/open-url.js +167 -12
- package/dist/lib/probe.d.ts +8 -0
- package/dist/lib/probe.js +105 -0
- package/dist/lib/refresh-coordinator.d.ts +1 -1
- package/dist/lib/refresh-coordinator.js +1 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +2 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +13 -1
- package/dist/lib/session/active.d.ts +67 -0
- package/dist/lib/session/active.js +69 -1
- package/dist/lib/session/db.js +4 -2
- package/dist/lib/session/discover.d.ts +2 -0
- package/dist/lib/session/discover.js +12 -6
- package/dist/lib/session/prompt.d.ts +35 -0
- package/dist/lib/session/prompt.js +56 -0
- package/dist/lib/session/remote/watch.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +6 -1
- package/dist/lib/session/render.js +7 -3
- package/dist/lib/session/session-cache.js +5 -1
- package/dist/lib/session/trajectory-compare.d.ts +47 -0
- package/dist/lib/session/trajectory-compare.js +116 -0
- package/dist/lib/session/trajectory-html.d.ts +15 -0
- package/dist/lib/session/trajectory-html.js +750 -0
- package/dist/lib/session/trajectory-lineage.d.ts +92 -0
- package/dist/lib/session/trajectory-lineage.js +195 -0
- package/dist/lib/session/trajectory-text.d.ts +37 -0
- package/dist/lib/session/trajectory-text.js +277 -0
- package/dist/lib/session/trajectory.d.ts +102 -0
- package/dist/lib/session/trajectory.js +335 -0
- package/dist/lib/startup/command-registry.d.ts +5 -2
- package/dist/lib/startup/command-registry.js +8 -5
- package/dist/lib/state.d.ts +1 -1
- package/dist/lib/state.js +4 -2
- package/dist/lib/teams/agents.js +9 -0
- package/dist/lib/terminal/inject.d.ts +19 -0
- package/dist/lib/terminal/inject.js +19 -9
- package/dist/lib/tmux/session.d.ts +46 -13
- package/dist/lib/tmux/session.js +173 -27
- package/dist/lib/types.d.ts +16 -0
- package/dist/lib/usage-backoff.d.ts +2 -1
- package/dist/lib/usage-backoff.js +60 -19
- package/dist/lib/usage-refresh.d.ts +17 -3
- package/dist/lib/usage-refresh.js +57 -11
- package/dist/lib/view-types.d.ts +2 -2
- package/dist/lib/watchdog/log.d.ts +1 -1
- package/dist/lib/watchdog/log.js +1 -1
- package/dist/lib/watchdog/runner.d.ts +36 -45
- package/dist/lib/watchdog/runner.js +150 -119
- package/dist/lib/watchdog/service.d.ts +0 -1
- package/dist/lib/watchdog/service.js +0 -1
- package/dist/lib/watchdog/watchdog-agent.d.ts +38 -0
- package/dist/lib/watchdog/watchdog-agent.js +59 -0
- package/dist/lib/watchdog/watchdog.d.ts +11 -2
- package/dist/lib/watchdog/watchdog.js +37 -104
- package/package.json +2 -2
- package/dist/commands/usage.d.ts +0 -27
- package/dist/commands/usage.js +0 -96
|
@@ -1,53 +1,9 @@
|
|
|
1
|
-
// Watchdog: pure logic for detecting stalled agent terminals and rendering
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
const BLOCKED_HINTS = [
|
|
8
|
-
'blocked',
|
|
9
|
-
'stuck',
|
|
10
|
-
"can't",
|
|
11
|
-
'cannot',
|
|
12
|
-
'unable',
|
|
13
|
-
'failed',
|
|
14
|
-
'error',
|
|
15
|
-
'exception',
|
|
16
|
-
'traceback',
|
|
17
|
-
'timed out',
|
|
18
|
-
'timeout',
|
|
19
|
-
'rate limit',
|
|
20
|
-
'permission denied',
|
|
21
|
-
];
|
|
22
|
-
const WAITING_HINTS = [
|
|
23
|
-
'waiting on user',
|
|
24
|
-
'awaiting user',
|
|
25
|
-
'askuserquestion',
|
|
26
|
-
];
|
|
27
|
-
const COMPLETION_HINTS = [
|
|
28
|
-
'done',
|
|
29
|
-
'completed',
|
|
30
|
-
'all set',
|
|
31
|
-
'finished',
|
|
32
|
-
];
|
|
33
|
-
const TOOL_CALL_HINTS = [
|
|
34
|
-
'"type":"tool_use"',
|
|
35
|
-
'"type":"tool_call"',
|
|
36
|
-
'"type":"function_call"',
|
|
37
|
-
];
|
|
38
|
-
const ASSISTANT_LINE_HINTS = [
|
|
39
|
-
'"type":"assistant"',
|
|
40
|
-
'"role":"assistant"',
|
|
41
|
-
'"payload":{"type":"message"',
|
|
42
|
-
];
|
|
43
|
-
const PROMISE_HINTS = [
|
|
44
|
-
"i'll",
|
|
45
|
-
'i will',
|
|
46
|
-
'let me',
|
|
47
|
-
'going to',
|
|
48
|
-
"next i'll",
|
|
49
|
-
'next i will',
|
|
50
|
-
];
|
|
1
|
+
// Watchdog: pure logic for detecting stalled agent terminals and rendering the
|
|
2
|
+
// prompt that the watchdog AGENT reads to decide idle-vs-unfinished and craft a
|
|
3
|
+
// nudge. There is deliberately no heuristic decider here (no regex over the tail
|
|
4
|
+
// guessing "done" vs "stuck") — that judgment is the agent's job. This module
|
|
5
|
+
// only classifies idleness by timestamp and renders/parses the agent's I/O, so
|
|
6
|
+
// it reads no files and touches no host APIs and can be unit-tested in isolation.
|
|
51
7
|
export function classifyTerminal(input) {
|
|
52
8
|
if (input.optedOut)
|
|
53
9
|
return { kind: 'opted_out' };
|
|
@@ -64,14 +20,16 @@ export function classifyTerminal(input) {
|
|
|
64
20
|
}
|
|
65
21
|
return { kind: 'stalled', stalledForMs: age };
|
|
66
22
|
}
|
|
67
|
-
export const WATCHDOG_SYSTEM_PROMPT = `You are the watchdog for AI coding agents running in terminals.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
23
|
+
export const WATCHDOG_SYSTEM_PROMPT = `You are the watchdog for AI coding agents running in terminals. You are given the idle
|
|
24
|
+
sessions on this machine — each with its originating TASK, how long it has been idle, and
|
|
25
|
+
the tail of its transcript. Your one job is to tell, for each one, whether it is
|
|
26
|
+
IDLE-BUT-UNFINISHED (it was given a task, went quiet, and has NOT finished or handed it
|
|
27
|
+
off) or IDLE-AND-DONE (it finished, or it genuinely needs the human). Idle-but-unfinished
|
|
28
|
+
is the dangerous state — the work is most likely to be silently abandoned — so those get a
|
|
29
|
+
NUDGE that drives them to finish. Everything else is a SKIP.
|
|
72
30
|
|
|
73
|
-
Read
|
|
74
|
-
it," not "decide."
|
|
31
|
+
Read each transcript before judging — an agent that already reached a decision needs "do
|
|
32
|
+
it," not "decide." Judge from the task + tail, not from keywords.
|
|
75
33
|
|
|
76
34
|
NUDGE when the agent went idle and could keep going on its own:
|
|
77
35
|
- It asked permission for an obvious or already-authorized next step
|
|
@@ -92,18 +50,19 @@ The nudge text MUST carry context, not shove:
|
|
|
92
50
|
- Tell it to use best judgment and finish end-to-end WITHOUT asking again.
|
|
93
51
|
- Imperative, 1-2 sentences, no emojis, under 240 characters.
|
|
94
52
|
|
|
95
|
-
SKIP
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
-
|
|
103
|
-
|
|
53
|
+
SKIP in two distinct cases — mark which with "needsHuman":
|
|
54
|
+
- needsHuman=true — the agent is genuinely blocked on a human (these belong in the user's
|
|
55
|
+
feed): credentials, auth, login, 2FA, or biometric; an irreversible or outward-facing
|
|
56
|
+
action needing sign-off (force-push, delete prod data, publish/release, spend money,
|
|
57
|
+
send an external message) UNLESS the House Rules below authorize it; a real product or
|
|
58
|
+
intent decision with genuine ambiguity (not a trivial default); or you cannot tell what
|
|
59
|
+
the agent is doing.
|
|
60
|
+
- needsHuman=false — the task is actually complete (idle-and-done). Leave it alone; do NOT
|
|
61
|
+
poke a finished session.
|
|
104
62
|
|
|
105
|
-
Respond with ONLY a JSON array (no prose, no code fence)
|
|
106
|
-
|
|
63
|
+
Respond with ONLY a JSON array (no prose, no code fence). Include "needsHuman" on every
|
|
64
|
+
skip:
|
|
65
|
+
[{"terminalId":"<id>","action":"nudge"|"skip","text":"<message or empty>","reason":"<brief>","needsHuman":true|false}]`;
|
|
107
66
|
// User-editable playbook appended below the built-in prompt. The user maintains
|
|
108
67
|
// the source at ~/.agents/playbooks/watchdog.md (read by the delivery layer);
|
|
109
68
|
// this function is pure so it can be tested without filesystem access.
|
|
@@ -115,10 +74,14 @@ export function composePromptWithPlaybook(basePrompt, playbook) {
|
|
|
115
74
|
}
|
|
116
75
|
export function renderWatchdogPrompt(candidates, playbook = '') {
|
|
117
76
|
const systemPrompt = composePromptWithPlaybook(WATCHDOG_SYSTEM_PROMPT, playbook);
|
|
118
|
-
const parts = [systemPrompt, '', '
|
|
77
|
+
const parts = [systemPrompt, '', 'IDLE SESSIONS:', ''];
|
|
119
78
|
for (const c of candidates) {
|
|
120
79
|
const seconds = Math.round(c.stalledForMs / 1000);
|
|
121
80
|
parts.push(`--- terminal ${c.terminalId} (${c.agentType}, idle ${seconds}s) ---`);
|
|
81
|
+
if (c.task)
|
|
82
|
+
parts.push(`task: ${c.task}`);
|
|
83
|
+
if (c.cwd)
|
|
84
|
+
parts.push(`cwd: ${c.cwd}`);
|
|
122
85
|
parts.push('last JSONL lines:');
|
|
123
86
|
for (const line of c.tailLines) {
|
|
124
87
|
parts.push(line);
|
|
@@ -153,41 +116,11 @@ export function parseWatchdogResponse(stdout) {
|
|
|
153
116
|
const reason = typeof obj.reason === 'string' ? obj.reason : '';
|
|
154
117
|
if (!terminalId || !action)
|
|
155
118
|
continue;
|
|
156
|
-
|
|
119
|
+
// needsHuman only meaningful on a skip; a nudge is never "needs human".
|
|
120
|
+
const needsHuman = action === 'skip' && obj.needsHuman === true ? true
|
|
121
|
+
: action === 'skip' && obj.needsHuman === false ? false
|
|
122
|
+
: undefined;
|
|
123
|
+
decisions.push({ terminalId, action, text, reason, needsHuman });
|
|
157
124
|
}
|
|
158
125
|
return decisions;
|
|
159
126
|
}
|
|
160
|
-
export function isLikelyTrulyBlocked(candidate) {
|
|
161
|
-
// With no tail to reason over, only a very long stall counts as blocked.
|
|
162
|
-
if (candidate.tailLines.length === 0)
|
|
163
|
-
return candidate.stalledForMs >= FORCE_REVIEW_STALL_MS;
|
|
164
|
-
const lowerTail = candidate.tailLines.join('\n').toLowerCase();
|
|
165
|
-
// Waiting-on-user and completion hints are checked BEFORE the 15m force-review
|
|
166
|
-
// short-circuit — a long-idle OPEN QUESTION must defer (not be blindly
|
|
167
|
-
// force-nudged) and a finished task is done. The old order tested stall age
|
|
168
|
-
// first, so a 15m-60m idle session whose tail said "waiting on user" / "done"
|
|
169
|
-
// was force-nudged anyway. Precedence fixed here (watchdog-brain-v2).
|
|
170
|
-
if (WAITING_HINTS.some((hint) => lowerTail.includes(hint)))
|
|
171
|
-
return false;
|
|
172
|
-
if (COMPLETION_HINTS.some((hint) => lowerTail.includes(hint)))
|
|
173
|
-
return false;
|
|
174
|
-
if (candidate.stalledForMs >= FORCE_REVIEW_STALL_MS)
|
|
175
|
-
return true;
|
|
176
|
-
if (BLOCKED_HINTS.some((hint) => lowerTail.includes(hint)))
|
|
177
|
-
return true;
|
|
178
|
-
let sawToolAfter = false;
|
|
179
|
-
for (let i = candidate.tailLines.length - 1; i >= 0; i--) {
|
|
180
|
-
const line = candidate.tailLines[i].toLowerCase();
|
|
181
|
-
if (TOOL_CALL_HINTS.some((hint) => line.includes(hint))) {
|
|
182
|
-
sawToolAfter = true;
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
if (!sawToolAfter) {
|
|
186
|
-
const isAssistantLine = ASSISTANT_LINE_HINTS.some((hint) => line.includes(hint));
|
|
187
|
-
const hasPromise = PROMISE_HINTS.some((hint) => line.includes(hint));
|
|
188
|
-
if (isAssistantLine && hasPromise)
|
|
189
|
-
return true;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return false;
|
|
193
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.47",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"multi-agent"
|
|
80
80
|
],
|
|
81
81
|
"author": "Phoenix Labs",
|
|
82
|
-
"license": "Apache-2.0",
|
|
82
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
83
83
|
"engines": {
|
|
84
84
|
"node": ">=22.5.0",
|
|
85
85
|
"npm": ">=9"
|
package/dist/commands/usage.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Usage command -- show rate-limit / quota status for each installed agent.
|
|
3
|
-
*
|
|
4
|
-
* Lists every installed agent with the best available usage snapshot:
|
|
5
|
-
* - claude: live OAuth API call (cached for 5 minutes)
|
|
6
|
-
* - codex: parsed from latest session log's rate_limits event
|
|
7
|
-
* - kimi: live Kimi Code /usages API call (cached for 5 minutes)
|
|
8
|
-
* - droid: live Factory billing/limits API call (cached for 5 minutes)
|
|
9
|
-
* - grok: parsed from the latest local usage event
|
|
10
|
-
* - cursor: live Cursor usage API call (cached for 5 minutes)
|
|
11
|
-
* - others: marked as "not exposed by CLI"
|
|
12
|
-
*/
|
|
13
|
-
import type { Command } from 'commander';
|
|
14
|
-
import type { AgentId } from '../lib/types.js';
|
|
15
|
-
import { getUsageInfoForIdentity } from '../lib/accounting/usage.js';
|
|
16
|
-
/** One agent's usage snapshot — the unit the text and --json renderers share. */
|
|
17
|
-
export interface AgentUsageRecord {
|
|
18
|
-
agent: AgentId;
|
|
19
|
-
/** Plain agent name (no ANSI) — safe to emit in --json; the text table colorizes it. */
|
|
20
|
-
label: string;
|
|
21
|
-
status: 'unsupported' | 'no-version' | 'not-signed-in' | 'ok';
|
|
22
|
-
email?: string;
|
|
23
|
-
usage?: Awaited<ReturnType<typeof getUsageInfoForIdentity>>;
|
|
24
|
-
}
|
|
25
|
-
export declare function registerUsageCommand(program: Command): void;
|
|
26
|
-
/** Render one usage record as the human table section. */
|
|
27
|
-
export declare function formatAgentUsage(rec: AgentUsageRecord): string;
|
package/dist/commands/usage.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { addHostOption } from '../lib/hosts/option.js';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { ALL_AGENT_IDS, AGENTS, getAccountInfo, agentLabel, resolveAgentName, formatAgentError, } from '../lib/agents.js';
|
|
4
|
-
import { listInstalledVersions, getGlobalDefault, getVersionHomePath } from '../lib/installations/versions.js';
|
|
5
|
-
import { agentReportsUsage, formatUsageSection, getUsageInfoForIdentity } from '../lib/accounting/usage.js';
|
|
6
|
-
export function registerUsageCommand(program) {
|
|
7
|
-
addHostOption(program.command('usage [agent]'))
|
|
8
|
-
.description('Show rate-limit / quota usage per agent')
|
|
9
|
-
.option('--json', 'Emit machine-readable JSON (per-agent usage snapshot) instead of the table')
|
|
10
|
-
.option('-r, --refresh', 'Collect fresh usage through the shared device coordinator before rendering')
|
|
11
|
-
.addHelpText('after', `
|
|
12
|
-
Examples:
|
|
13
|
-
agents usage Show usage for all installed agents
|
|
14
|
-
agents usage claude Show usage for Claude only
|
|
15
|
-
agents usage codex Show usage for Codex only
|
|
16
|
-
agents usage claude --refresh Explicitly refresh Claude once, device-wide
|
|
17
|
-
agents usage --json Machine-readable snapshot for scripts
|
|
18
|
-
`)
|
|
19
|
-
.action(async (agentFilter, options) => {
|
|
20
|
-
let filter;
|
|
21
|
-
if (agentFilter) {
|
|
22
|
-
const resolved = resolveAgentName(agentFilter);
|
|
23
|
-
if (!resolved) {
|
|
24
|
-
console.error(chalk.red(formatAgentError(agentFilter)));
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
filter = resolved;
|
|
28
|
-
}
|
|
29
|
-
const targets = filter
|
|
30
|
-
? [filter]
|
|
31
|
-
: ALL_AGENT_IDS.filter((id) => listInstalledVersions(id).length > 0);
|
|
32
|
-
if (targets.length === 0) {
|
|
33
|
-
if (options.json) {
|
|
34
|
-
console.log('[]');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
console.log(chalk.gray('No agents installed. Run `agents add <agent>` first.'));
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
const records = await Promise.all(targets.map((agentId) => collectAgentUsage(agentId, options.refresh === true)));
|
|
41
|
-
if (options.json) {
|
|
42
|
-
console.log(JSON.stringify(records, null, 2));
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
console.log(records.map(formatAgentUsage).filter(Boolean).join('\n\n'));
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
/** Gather one agent's usage snapshot as structured data (shared by both renderers). */
|
|
49
|
-
async function collectAgentUsage(agentId, forceRefresh = false) {
|
|
50
|
-
// Plain name — color is applied only at text-render time (formatAgentUsage), so
|
|
51
|
-
// `--json` never emits ANSI escapes in `label` (e.g. under FORCE_COLOR=1).
|
|
52
|
-
const label = AGENTS[agentId].name;
|
|
53
|
-
if (!agentReportsUsage(agentId)) {
|
|
54
|
-
return { agent: agentId, label, status: 'unsupported' };
|
|
55
|
-
}
|
|
56
|
-
const versions = listInstalledVersions(agentId);
|
|
57
|
-
const version = getGlobalDefault(agentId) || versions[0];
|
|
58
|
-
if (!version) {
|
|
59
|
-
return { agent: agentId, label, status: 'no-version' };
|
|
60
|
-
}
|
|
61
|
-
const home = getVersionHomePath(agentId, version);
|
|
62
|
-
const info = await getAccountInfo(agentId, home);
|
|
63
|
-
if (!info.usageKey && !info.accountKey) {
|
|
64
|
-
return { agent: agentId, label, status: 'not-signed-in' };
|
|
65
|
-
}
|
|
66
|
-
const usage = await getUsageInfoForIdentity({ agentId, home, info, cliVersion: null }, forceRefresh ? { forceRefresh: true } : undefined);
|
|
67
|
-
return { agent: agentId, label, status: 'ok', email: info.email ?? undefined, usage };
|
|
68
|
-
}
|
|
69
|
-
/** Render one usage record as the human table section. */
|
|
70
|
-
export function formatAgentUsage(rec) {
|
|
71
|
-
const cfg = AGENTS[rec.agent];
|
|
72
|
-
// Colorize the heading here (not in the record) so the plain `label` stays
|
|
73
|
-
// clean for --json while the text table keeps its per-agent color.
|
|
74
|
-
const heading = agentLabel(rec.agent);
|
|
75
|
-
switch (rec.status) {
|
|
76
|
-
case 'unsupported':
|
|
77
|
-
return [heading, ` ${chalk.dim(`${cfg.name} CLI does not publish usage data.`)}`].join('\n');
|
|
78
|
-
case 'no-version':
|
|
79
|
-
return [heading, ` ${chalk.dim('No version installed.')}`].join('\n');
|
|
80
|
-
case 'not-signed-in':
|
|
81
|
-
return [heading, ` ${chalk.dim('Not signed in.')}`].join('\n');
|
|
82
|
-
case 'ok': {
|
|
83
|
-
const lines = [heading];
|
|
84
|
-
if (rec.email)
|
|
85
|
-
lines.push(` ${chalk.dim(rec.email)}`);
|
|
86
|
-
const section = formatUsageSection(rec.usage);
|
|
87
|
-
if (section.length === 0) {
|
|
88
|
-
lines.push(` ${chalk.dim('No usage data available right now.')}`);
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
lines.push(...section);
|
|
92
|
-
}
|
|
93
|
-
return lines.join('\n');
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|