@phnx-labs/agents-cli 1.22.46 → 1.22.49
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 +663 -0
- package/LICENSE +102 -182
- package/README.md +96 -49
- package/dist/bootstrap.js +4 -2
- package/dist/cli/command-registry.d.ts +2 -1
- package/dist/cli/command-registry.js +6 -2
- package/dist/commands/accounts.d.ts +58 -0
- package/dist/commands/accounts.js +243 -25
- package/dist/commands/artifacts.js +7 -6
- package/dist/commands/attach.js +1 -1
- package/dist/commands/auth.js +7 -5
- package/dist/commands/browser-sessions-picker.js +9 -7
- package/dist/commands/browser.js +711 -62
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/config.js +26 -24
- package/dist/commands/cost.d.ts +1 -1
- package/dist/commands/daemon.d.ts +21 -0
- package/dist/commands/daemon.js +147 -12
- package/dist/commands/detach.d.ts +13 -0
- package/dist/commands/detach.js +11 -8
- package/dist/commands/exec.js +107 -53
- 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/focus.d.ts +14 -0
- package/dist/commands/focus.js +48 -5
- package/dist/commands/go.d.ts +3 -0
- package/dist/commands/go.js +16 -3
- package/dist/commands/insights.d.ts +1 -1
- package/dist/commands/insights.js +2 -2
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/lease.d.ts +1 -1
- package/dist/commands/lease.js +7 -6
- package/dist/commands/mailboxes.js +4 -3
- package/dist/commands/menubar.js +11 -4
- package/dist/commands/projects.d.ts +16 -1
- package/dist/commands/projects.js +58 -64
- package/dist/commands/resume.js +1 -1
- package/dist/commands/route.js +4 -4
- package/dist/commands/routines.js +4 -3
- package/dist/commands/run-broadcast.d.ts +1 -1
- package/dist/commands/run-broadcast.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-picker.d.ts +22 -0
- package/dist/commands/sessions-picker.js +48 -9
- 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 +5 -0
- package/dist/commands/setup-secrets.js +23 -0
- package/dist/commands/share.d.ts +20 -5
- package/dist/commands/share.js +152 -52
- package/dist/commands/ssh.d.ts +15 -0
- package/dist/commands/ssh.js +243 -18
- package/dist/commands/teams.js +34 -2
- package/dist/commands/traces.d.ts +2 -0
- package/dist/commands/traces.js +184 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +32 -62
- package/dist/commands/watchdog.js +9 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- 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 +84 -6
- package/dist/lib/accounting/usage.js +293 -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 +25 -15
- 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/drivers/ssh.d.ts +16 -2
- package/dist/lib/browser/drivers/ssh.js +36 -9
- package/dist/lib/browser/ipc.d.ts +1 -1
- package/dist/lib/browser/ipc.js +40 -9
- package/dist/lib/browser/profiles.d.ts +104 -65
- package/dist/lib/browser/profiles.js +277 -165
- package/dist/lib/browser/registry.d.ts +39 -0
- package/dist/lib/browser/registry.js +149 -0
- package/dist/lib/browser/remote-control.d.ts +28 -2
- package/dist/lib/browser/remote-control.js +35 -3
- package/dist/lib/browser/resolve-target.d.ts +68 -0
- package/dist/lib/browser/resolve-target.js +250 -0
- package/dist/lib/browser/runtime-state.d.ts +44 -11
- package/dist/lib/browser/runtime-state.js +98 -15
- package/dist/lib/browser/service.d.ts +65 -9
- package/dist/lib/browser/service.js +196 -61
- package/dist/lib/browser/sessions-list.d.ts +1 -3
- package/dist/lib/browser/sessions-list.js +3 -16
- package/dist/lib/browser/task-index.d.ts +70 -0
- package/dist/lib/browser/task-index.js +180 -0
- package/dist/lib/browser/types.d.ts +12 -1
- package/dist/lib/byok-usage.js +2 -2
- package/dist/lib/claude-statusline.d.ts +29 -0
- package/dist/lib/claude-statusline.js +139 -0
- package/dist/lib/cloud/host.d.ts +1 -1
- package/dist/lib/cloud/host.js +1 -1
- package/dist/lib/cloud/rush.js +1 -1
- package/dist/lib/codex-policy.d.ts +11 -1
- package/dist/lib/codex-policy.js +20 -5
- package/dist/lib/computer/computer-rpc.js +2 -2
- package/dist/lib/computer/download.d.ts +15 -15
- package/dist/lib/computer/download.js +35 -98
- package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
- package/dist/lib/computer/ssh-tunnel.js +12 -22
- package/dist/lib/config-keys.d.ts +6 -11
- package/dist/lib/config-keys.js +18 -19
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +2 -2
- package/dist/lib/crabbox/lease.d.ts +9 -0
- package/dist/lib/crabbox/lease.js +70 -15
- package/dist/lib/crabbox/runtimes.js +1 -1
- package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
- package/dist/lib/daemon/account-state-daemon-service.js +26 -0
- package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
- package/dist/lib/daemon/browser-ipc-service.js +46 -0
- package/dist/lib/daemon/daemon.d.ts +3 -0
- package/dist/lib/daemon/daemon.js +212 -444
- package/dist/lib/daemon/device-probe-service.d.ts +26 -0
- package/dist/lib/daemon/device-probe-service.js +47 -0
- package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
- package/dist/lib/daemon/keychain-reap-service.js +32 -0
- package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
- package/dist/lib/daemon/monitor-engine-service.js +29 -0
- package/dist/lib/daemon/runner.js +15 -2
- package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
- package/dist/lib/daemon/secrets-broker-service.js +66 -0
- package/dist/lib/daemon/self-heal-service.d.ts +27 -0
- package/dist/lib/daemon/self-heal-service.js +48 -0
- package/dist/lib/daemon/service.d.ts +102 -0
- package/dist/lib/daemon/service.js +98 -0
- package/dist/lib/daemon/session-index-service.d.ts +22 -0
- package/dist/lib/daemon/session-index-service.js +40 -0
- package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
- package/dist/lib/daemon/state-dir-check-service.js +63 -0
- package/dist/lib/daemon/supervisor.d.ts +87 -0
- package/dist/lib/daemon/supervisor.js +298 -0
- package/dist/lib/daemon/watchdog-service.d.ts +19 -0
- package/dist/lib/daemon/watchdog-service.js +40 -0
- package/dist/lib/daemon-health.d.ts +15 -0
- package/dist/lib/daemon-health.js +29 -5
- package/dist/lib/daemon-services.d.ts +8 -1
- package/dist/lib/daemon-services.js +55 -0
- package/dist/lib/daemon-ticks.d.ts +13 -2
- package/dist/lib/daemon-ticks.js +22 -20
- package/dist/lib/device-config.d.ts +25 -10
- package/dist/lib/device-config.js +90 -39
- package/dist/lib/devices/config-migration.d.ts +7 -0
- package/dist/lib/devices/config-migration.js +67 -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 +26 -0
- package/dist/lib/devices/stats-cache.js +59 -7
- package/dist/lib/devices/worker-pick.d.ts +48 -0
- package/dist/lib/devices/worker-pick.js +113 -0
- package/dist/lib/exec.d.ts +70 -14
- package/dist/lib/exec.js +110 -38
- 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/harness/adapters/cursor.js +8 -11
- package/dist/lib/helper-download.d.ts +108 -0
- package/dist/lib/helper-download.js +194 -0
- package/dist/lib/helper-versions.d.ts +52 -0
- package/dist/lib/helper-versions.js +58 -0
- package/dist/lib/hooks/cache.js +19 -0
- package/dist/lib/hooks/install.js +2 -2
- package/dist/lib/hooks/match.d.ts +3 -0
- package/dist/lib/hooks/match.js +25 -0
- package/dist/lib/hosts/credentials.d.ts +1 -1
- package/dist/lib/hosts/credentials.js +1 -1
- package/dist/lib/hosts/dispatch.d.ts +7 -1
- package/dist/lib/hosts/dispatch.js +24 -5
- package/dist/lib/hosts/passthrough.d.ts +8 -15
- package/dist/lib/hosts/passthrough.js +35 -16
- package/dist/lib/hosts/providers/devices.d.ts +1 -1
- package/dist/lib/hosts/providers/devices.js +1 -1
- package/dist/lib/hosts/reconnect.d.ts +179 -21
- package/dist/lib/hosts/reconnect.js +266 -45
- package/dist/lib/hosts/registry.js +7 -0
- package/dist/lib/installations/migrate.d.ts +4 -5
- package/dist/lib/installations/migrate.js +6 -7
- package/dist/lib/installations/shims.d.ts +1 -1
- package/dist/lib/installations/shims.js +23 -13
- package/dist/lib/installations/versions.js +25 -2
- package/dist/lib/linear-autoclose.d.ts +1 -1
- package/dist/lib/linear-autoclose.js +1 -1
- package/dist/lib/loop.d.ts +2 -2
- package/dist/lib/loop.js +1 -1
- package/dist/lib/menubar/download-menubar.d.ts +47 -0
- package/dist/lib/menubar/download-menubar.js +60 -0
- package/dist/lib/menubar/install-menubar.d.ts +44 -7
- package/dist/lib/menubar/install-menubar.js +105 -23
- package/dist/lib/menubar/notify-desktop.d.ts +3 -3
- package/dist/lib/menubar/notify-desktop.js +6 -6
- package/dist/lib/model-tiers.d.ts +1 -1
- package/dist/lib/open-url.d.ts +60 -2
- package/dist/lib/open-url.js +167 -12
- package/dist/lib/project-probe.d.ts +1 -1
- package/dist/lib/project-probe.js +1 -1
- package/dist/lib/project-pull.d.ts +1 -1
- package/dist/lib/project-pull.js +2 -2
- package/dist/lib/projects.d.ts +1 -1
- package/dist/lib/projects.js +1 -1
- package/dist/lib/refresh-coordinator.d.ts +1 -1
- package/dist/lib/refresh-coordinator.js +1 -1
- package/dist/lib/sandbox.js +6 -7
- package/dist/lib/secrets/download-keychain.d.ts +47 -0
- package/dist/lib/secrets/download-keychain.js +70 -0
- package/dist/lib/secrets/install-helper.d.ts +20 -1
- package/dist/lib/secrets/install-helper.js +44 -3
- package/dist/lib/session/active.d.ts +67 -0
- package/dist/lib/session/active.js +86 -2
- package/dist/lib/session/db.d.ts +11 -1
- package/dist/lib/session/db.js +142 -8
- package/dist/lib/session/discover.d.ts +23 -1
- package/dist/lib/session/discover.js +44 -10
- package/dist/lib/session/highlights.d.ts +49 -1
- package/dist/lib/session/highlights.js +95 -0
- package/dist/lib/session/host-link.d.ts +16 -2
- package/dist/lib/session/host-link.js +7 -1
- package/dist/lib/session/parse.js +33 -1
- package/dist/lib/session/prompt.d.ts +35 -0
- package/dist/lib/session/prompt.js +56 -0
- package/dist/lib/session/remote/remote-list.d.ts +9 -0
- package/dist/lib/session/remote/remote-list.js +19 -2
- package/dist/lib/session/remote/watch.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +17 -3
- package/dist/lib/session/render.js +7 -3
- package/dist/lib/session/session-cache.d.ts +53 -0
- package/dist/lib/session/session-cache.js +93 -1
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/tool-calls.d.ts +2 -0
- package/dist/lib/session/tool-calls.js +1 -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 +19 -0
- package/dist/lib/session/trajectory-html.js +770 -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 +284 -0
- package/dist/lib/session/trajectory.d.ts +102 -0
- package/dist/lib/session/trajectory.js +384 -0
- package/dist/lib/session/types.d.ts +15 -0
- package/dist/lib/sha256-asset.d.ts +26 -0
- package/dist/lib/sha256-asset.js +41 -0
- package/dist/lib/share/backend.d.ts +95 -0
- package/dist/lib/share/backend.js +139 -0
- package/dist/lib/share/delete.d.ts +6 -2
- package/dist/lib/share/delete.js +8 -9
- package/dist/lib/share/html.d.ts +22 -0
- package/dist/lib/share/html.js +88 -0
- package/dist/lib/share/provision.d.ts +18 -4
- package/dist/lib/share/provision.js +38 -4
- package/dist/lib/share/publish.d.ts +39 -15
- package/dist/lib/share/publish.js +55 -26
- package/dist/lib/share/worker-template.js +171 -11
- package/dist/lib/sqlite.js +16 -3
- package/dist/lib/ssh-exec.d.ts +65 -0
- package/dist/lib/ssh-exec.js +120 -4
- package/dist/lib/startup/command-registry.d.ts +3 -1
- package/dist/lib/startup/command-registry.js +6 -3
- package/dist/lib/startup/dev-build.js +3 -3
- package/dist/lib/state.d.ts +2 -2
- package/dist/lib/state.js +11 -5
- package/dist/lib/subagents-registry.js +1 -1
- package/dist/lib/teams/agents.js +9 -0
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/inject.d.ts +19 -0
- package/dist/lib/terminal/inject.js +19 -9
- package/dist/lib/terminal/types.d.ts +1 -1
- package/dist/lib/terminal/types.js +1 -1
- package/dist/lib/tmux/orphan-reap.js +1 -1
- package/dist/lib/tmux/session.d.ts +46 -13
- package/dist/lib/tmux/session.js +204 -27
- package/dist/lib/traces/backend.d.ts +29 -0
- package/dist/lib/traces/backend.js +46 -0
- package/dist/lib/traces/classify.d.ts +52 -0
- package/dist/lib/traces/classify.js +72 -0
- package/dist/lib/traces/config.d.ts +10 -0
- package/dist/lib/traces/config.js +2 -0
- package/dist/lib/traces/provision.d.ts +19 -0
- package/dist/lib/traces/provision.js +30 -0
- package/dist/lib/traces/sync.d.ts +150 -0
- package/dist/lib/traces/sync.js +447 -0
- package/dist/lib/traces/worker-template.d.ts +4 -0
- package/dist/lib/traces/worker-template.js +159 -0
- package/dist/lib/types.d.ts +34 -19
- package/dist/lib/types.js +14 -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 +18 -4
- package/dist/lib/usage-refresh.js +68 -19
- 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/dist/lib/workflows.d.ts +1 -1
- package/package.json +5 -7
- package/dist/commands/usage.d.ts +0 -27
- package/dist/commands/usage.js +0 -96
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -24
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -123
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
- package/dist/lib/usage-fleet.d.ts +0 -32
- package/dist/lib/usage-fleet.js +0 -125
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { setHelpSections } from '../lib/help.js';
|
|
3
|
+
import { syncTraces } from '../lib/traces/sync.js';
|
|
4
|
+
import { readSyncLedger } from '../lib/traces/sync.js';
|
|
5
|
+
import { managedTracesBaseUrl, resolveTracesBackend } from '../lib/traces/backend.js';
|
|
6
|
+
import { showUrl } from '../lib/open-url.js';
|
|
7
|
+
import { DEFAULT_CF_BUNDLE, readCloudflareCreds } from '../lib/share/config.js';
|
|
8
|
+
import { PHOENIX_ID_BASE } from '../lib/identity/client.js';
|
|
9
|
+
import { DEFAULT_BUCKET_NAME, DEFAULT_WORKER_NAME } from '../lib/traces/config.js';
|
|
10
|
+
import { DEFAULT_TRACES_DOMAIN } from '../lib/traces/backend.js';
|
|
11
|
+
import { provisionTraces } from '../lib/traces/provision.js';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Help text
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
const SYNC_EXAMPLES = `
|
|
16
|
+
$ agents traces sync
|
|
17
|
+
Push this device's derived, redacted trajectories to your Phoenix account.
|
|
18
|
+
|
|
19
|
+
$ agents traces sync --limit 20
|
|
20
|
+
Sync at most 20 sessions (useful for a first run on a large fleet device).
|
|
21
|
+
|
|
22
|
+
$ agents traces sync --dry-run --out ./trace-preview
|
|
23
|
+
Compute the shards from your real sessions.db and WRITE them locally — no
|
|
24
|
+
Phoenix sign-in, no worker, no upload. Point a console at ./trace-preview to
|
|
25
|
+
see your real trajectories before wiring the hosted path.
|
|
26
|
+
`.trimStart();
|
|
27
|
+
const SYNC_NOTES = `
|
|
28
|
+
Reads from the local sessions.db, computes a derived SessionTrajectory (steps +
|
|
29
|
+
gaps + stats, no raw transcript text), redacts secrets, and PUTs each to
|
|
30
|
+
the agents-traces store.
|
|
31
|
+
|
|
32
|
+
An incremental gate (traces-sync.json ledger) means re-runs only upload
|
|
33
|
+
sessions whose file_mtime_ms changed since the last sync.
|
|
34
|
+
|
|
35
|
+
Sign in first with: agents auth login
|
|
36
|
+
`.trimStart();
|
|
37
|
+
const STATUS_EXAMPLES = `
|
|
38
|
+
$ agents traces status
|
|
39
|
+
Show what's been synced — session count and last sync time for this device.
|
|
40
|
+
`.trimStart();
|
|
41
|
+
const OPEN_EXAMPLES = `
|
|
42
|
+
$ agents traces open
|
|
43
|
+
Open the Phoenix Evals console for your account.
|
|
44
|
+
`.trimStart();
|
|
45
|
+
const SETUP_EXAMPLES = `
|
|
46
|
+
$ agents traces setup
|
|
47
|
+
Provision the private agents-traces Worker, R2 bucket, and managed domain.
|
|
48
|
+
|
|
49
|
+
$ agents traces setup --account <id> --token <token>
|
|
50
|
+
Provision non-interactively with explicit Cloudflare credentials.
|
|
51
|
+
`.trimStart();
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Command handlers
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
async function handleSync(opts) {
|
|
56
|
+
const limit = opts.limit !== undefined ? parseInt(opts.limit, 10) : undefined;
|
|
57
|
+
const dryRun = opts.dryRun === true;
|
|
58
|
+
if (dryRun && !opts.out) {
|
|
59
|
+
console.error(chalk.red('traces sync --dry-run requires --out <dir>'));
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// A dry-run computes locally and needs no Phoenix backend; a real sync does.
|
|
64
|
+
if (!dryRun) {
|
|
65
|
+
try {
|
|
66
|
+
resolveTracesBackend();
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
console.error(chalk.red(err.message));
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
console.log(chalk.dim(dryRun ? `Computing traces locally → ${opts.out}` : 'Syncing traces…'));
|
|
75
|
+
let result;
|
|
76
|
+
try {
|
|
77
|
+
result = await syncTraces({ limit, dryRun, outDir: opts.out });
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
console.error(chalk.red(`Sync failed: ${err.message}`));
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const parts = [];
|
|
85
|
+
if (result.uploaded > 0) {
|
|
86
|
+
parts.push(chalk.green(`${result.uploaded} ${dryRun ? 'written' : 'uploaded'}`));
|
|
87
|
+
}
|
|
88
|
+
if (result.skipped > 0) {
|
|
89
|
+
parts.push(chalk.dim(`${result.skipped} skipped`));
|
|
90
|
+
}
|
|
91
|
+
if (result.errors > 0) {
|
|
92
|
+
parts.push(chalk.yellow(`${result.errors} errors`));
|
|
93
|
+
}
|
|
94
|
+
if (parts.length === 0) {
|
|
95
|
+
parts.push(chalk.dim('nothing new'));
|
|
96
|
+
}
|
|
97
|
+
console.log(parts.join(chalk.dim(' · ')));
|
|
98
|
+
}
|
|
99
|
+
async function handleStatus() {
|
|
100
|
+
const ledger = readSyncLedger();
|
|
101
|
+
if (!ledger.lastSyncMtime) {
|
|
102
|
+
console.log(chalk.dim('No sync recorded on this device. Run: agents traces sync'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const when = new Date(ledger.lastSyncMtime).toLocaleString();
|
|
106
|
+
console.log(`Last sync: ${chalk.bold(when)}`);
|
|
107
|
+
console.log(chalk.dim('Run `agents traces sync` to push new sessions.'));
|
|
108
|
+
}
|
|
109
|
+
async function handleOpen() {
|
|
110
|
+
// M2 console — for now open the base URL which will route to the console once deployed.
|
|
111
|
+
const url = managedTracesBaseUrl();
|
|
112
|
+
console.log(chalk.dim(`Opening ${url}`));
|
|
113
|
+
const outcome = await showUrl(url);
|
|
114
|
+
if (outcome.via === 'none') {
|
|
115
|
+
console.log(url);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function handleSetup(opts) {
|
|
119
|
+
const { input } = await import('@inquirer/prompts');
|
|
120
|
+
const { apiToken, accountId: bundledAccountId } = readCloudflareCreds(opts.bundle, {
|
|
121
|
+
apiToken: opts.token,
|
|
122
|
+
accountId: opts.account,
|
|
123
|
+
});
|
|
124
|
+
const accountId = opts.account ?? bundledAccountId ?? await input({ message: 'Cloudflare account id' });
|
|
125
|
+
if (!accountId)
|
|
126
|
+
throw new Error('A Cloudflare account id is required.');
|
|
127
|
+
const result = await provisionTraces({
|
|
128
|
+
apiToken,
|
|
129
|
+
accountId,
|
|
130
|
+
workerName: opts.worker,
|
|
131
|
+
bucketName: opts.bucket,
|
|
132
|
+
domain: opts.domain,
|
|
133
|
+
phoenixIdBase: PHOENIX_ID_BASE,
|
|
134
|
+
});
|
|
135
|
+
console.log(chalk.green(`Traces endpoint ready → ${chalk.bold(result.baseUrl)}`));
|
|
136
|
+
}
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Registration
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
export function registerTracesCommands(program) {
|
|
141
|
+
const tracesCmd = program
|
|
142
|
+
.command('traces')
|
|
143
|
+
.description("Sync this device's derived, redacted trajectories to your Phoenix account");
|
|
144
|
+
const setupCmd = tracesCmd
|
|
145
|
+
.command('setup')
|
|
146
|
+
.description('Provision the private Cloudflare Worker and R2 bucket for traces')
|
|
147
|
+
.option('--bundle <name>', 'secrets bundle holding the Cloudflare API token', DEFAULT_CF_BUNDLE)
|
|
148
|
+
.option('--worker <name>', 'Worker name', DEFAULT_WORKER_NAME)
|
|
149
|
+
.option('--bucket <name>', 'R2 bucket name', DEFAULT_BUCKET_NAME)
|
|
150
|
+
.option('--account <id>', 'Cloudflare account id (else read from the bundle / prompt)')
|
|
151
|
+
.option('--token <token>', 'Cloudflare API token (else read from the --bundle)')
|
|
152
|
+
.option('--domain <host>', 'custom domain to map', DEFAULT_TRACES_DOMAIN)
|
|
153
|
+
.action(async (opts) => {
|
|
154
|
+
try {
|
|
155
|
+
await handleSetup(opts);
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
console.error(chalk.red(err.message));
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
setHelpSections(setupCmd, { examples: SETUP_EXAMPLES });
|
|
163
|
+
// sync
|
|
164
|
+
const syncCmd = tracesCmd
|
|
165
|
+
.command('sync')
|
|
166
|
+
.description('Push derived, redacted trajectories (incremental)')
|
|
167
|
+
.option('--limit <n>', 'Maximum number of sessions to sync in this run')
|
|
168
|
+
.option('--dry-run', 'Compute the shards from real sessions.db and WRITE them locally (no auth, no upload)')
|
|
169
|
+
.option('--out <dir>', 'Directory to write index.json + sessions/<id>.json (required with --dry-run)')
|
|
170
|
+
.action(handleSync);
|
|
171
|
+
setHelpSections(syncCmd, { examples: SYNC_EXAMPLES, notes: SYNC_NOTES });
|
|
172
|
+
// status
|
|
173
|
+
const statusCmd = tracesCmd
|
|
174
|
+
.command('status')
|
|
175
|
+
.description('Show last sync time for this device')
|
|
176
|
+
.action(handleStatus);
|
|
177
|
+
setHelpSections(statusCmd, { examples: STATUS_EXAMPLES });
|
|
178
|
+
// open
|
|
179
|
+
const openCmd = tracesCmd
|
|
180
|
+
.command('open')
|
|
181
|
+
.description('Open the Phoenix Evals console')
|
|
182
|
+
.action(handleOpen);
|
|
183
|
+
setHelpSections(openCmd, { examples: OPEN_EXAMPLES });
|
|
184
|
+
}
|
package/dist/commands/view.d.ts
CHANGED
|
@@ -10,10 +10,12 @@ import type { Command } from 'commander';
|
|
|
10
10
|
import { accountDisplayLabel } from '../lib/agents.js';
|
|
11
11
|
import type { AccountInfo } from '../lib/agents.js';
|
|
12
12
|
import type { AgentId } from '../lib/types.js';
|
|
13
|
+
import type { FormatUsageSummaryOpts, UsageInfo } from '../lib/accounting/usage.js';
|
|
13
14
|
import type { ResourceSection, ViewJsonAgent } from '../lib/view-types.js';
|
|
14
15
|
export type { ResourceItemJson, ResourceSection, SyncState, VersionResourcesJson, ViewJsonAgent, ViewJsonVersion } from '../lib/view-types.js';
|
|
15
16
|
import { type ProfileSummary } from '../lib/profiles.js';
|
|
16
17
|
import { type ByokUsageResult } from '../lib/byok-usage.js';
|
|
18
|
+
export declare function viewUsageSummaryOptions(agentId: AgentId, signedIn: boolean, usageInfo: UsageInfo | undefined, maxWindows: number | undefined, version?: string): FormatUsageSummaryOpts;
|
|
17
19
|
/** Shared account identity formatter, re-exported for the view-specific tests. */
|
|
18
20
|
export declare const accountColumnLabel: typeof accountDisplayLabel;
|
|
19
21
|
/**
|
package/dist/commands/view.js
CHANGED
|
@@ -7,8 +7,8 @@ import * as path from 'path';
|
|
|
7
7
|
import { AGENTS, ALL_AGENT_IDS, accountDisplayLabel, getAllCliStates, getUnmanagedCliState, getAccountInfo, resolveAgentName, formatAgentError, agentLabel, colorAgent, } from '../lib/agents.js';
|
|
8
8
|
import { ambientClaudeToken, loginHint } from '../lib/signin-badge.js';
|
|
9
9
|
import { machineId } from '../lib/machine-id.js';
|
|
10
|
-
import { authCacheKey,
|
|
11
|
-
import { agentReportsUsage, deriveUsageStatusFromSnapshot, formatUsageSection, formatUsageSummary, formatUsageStatusBadge, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, isUsageHeadlessScopeError, } from '../lib/accounting/usage.js';
|
|
10
|
+
import { authCacheKey, readAuthHealthCache } from '../lib/auth-health.js';
|
|
11
|
+
import { agentReportsUsage, classifyUsageErrorKind, deriveUsageStatusFromSnapshot, formatUsageSection, formatUsageSummary, formatUsageStatusBadge, getUsageBenignState, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, isUsageHeadlessScopeError, } from '../lib/accounting/usage.js';
|
|
12
12
|
import { readManifest } from '../lib/manifest.js';
|
|
13
13
|
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, removeVersion, printTrashFooter, reconcileStaleLatestForAgent, isGlobalBinaryAgent, getLiveVersion, isVersionIsolated, getIsolatedDefault, } from '../lib/installations/versions.js';
|
|
14
14
|
import { ensureVersionedAliasCurrent, removeShim, } from '../lib/installations/shims.js';
|
|
@@ -32,6 +32,25 @@ import { renderHarnessDetail } from './harness.js';
|
|
|
32
32
|
import { confirm } from '@inquirer/prompts';
|
|
33
33
|
import { formatPath, isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
34
34
|
import { terminalWidth, truncateToWidth, stringWidth, padToWidth } from '../lib/session/width.js';
|
|
35
|
+
export function viewUsageSummaryOptions(agentId, signedIn, usageInfo, maxWindows, version) {
|
|
36
|
+
const headless = isUsageHeadlessScopeError(usageInfo?.error);
|
|
37
|
+
const benignState = usageInfo ? getUsageBenignState(usageInfo) : null;
|
|
38
|
+
return {
|
|
39
|
+
unavailable: agentReportsUsage(agentId) && signedIn && !usageInfo?.snapshot && !headless && !benignState,
|
|
40
|
+
unverified: !headless && !!usageInfo?.snapshot && !!usageInfo.error,
|
|
41
|
+
headless,
|
|
42
|
+
maxWindows,
|
|
43
|
+
expectedWindows: agentId === 'claude'
|
|
44
|
+
? [{ key: 'session', shortLabel: 'S' }, { key: 'week', shortLabel: 'W' }]
|
|
45
|
+
: undefined,
|
|
46
|
+
errorKind: classifyUsageErrorKind(usageInfo?.error),
|
|
47
|
+
errorDetail: usageInfo?.error ?? null,
|
|
48
|
+
benignState,
|
|
49
|
+
noRecentUsageLabel: agentId === 'grok'
|
|
50
|
+
? `run grok${version ? `@${version}` : ''} once to refresh usage`
|
|
51
|
+
: null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
35
54
|
/** Shared account identity formatter, re-exported for the view-specific tests. */
|
|
36
55
|
export const accountColumnLabel = accountDisplayLabel;
|
|
37
56
|
/**
|
|
@@ -79,8 +98,6 @@ export function compareAccountOrderedVersions(a, b, globalDefault) {
|
|
|
79
98
|
* views leave the cap unset and show every blocking window.
|
|
80
99
|
*/
|
|
81
100
|
const OVERVIEW_MAX_USAGE_WINDOWS = 2;
|
|
82
|
-
/** Fixed width for the last-active column ("just now", "8h ago", "2d ago"). */
|
|
83
|
-
const LAST_ACTIVE_COL_WIDTH = 10;
|
|
84
101
|
/**
|
|
85
102
|
* Join fixed view columns with a consistent two-space gutter. Empty trailing
|
|
86
103
|
* columns are dropped so a row without an auth chip does not grow a dangling
|
|
@@ -299,22 +316,6 @@ function renderHostClisSection(cwd) {
|
|
|
299
316
|
console.log(` ${chalk.red('error')} ${chalk.gray(err.file)}: ${chalk.gray(err.reason)}`);
|
|
300
317
|
}
|
|
301
318
|
}
|
|
302
|
-
/**
|
|
303
|
-
* A compact live-auth chip for a version row, read from the fleet auth-health
|
|
304
|
-
* cache (written by `agents fleet ping` / the daemon). Empty when the install
|
|
305
|
-
* hasn't been probed — this is the local "signed in" flag's ground-truth
|
|
306
|
-
* companion: ● live, ○ revoked (re-login), ◐ unverified/limited, plus its age.
|
|
307
|
-
*/
|
|
308
|
-
function liveAuthChip(cache, host, agentId, version) {
|
|
309
|
-
const h = cache[authCacheKey(host, agentId, version)];
|
|
310
|
-
if (!h)
|
|
311
|
-
return '';
|
|
312
|
-
const glyph = h.verdict === 'live' ? chalk.green('●')
|
|
313
|
-
: h.verdict === 'revoked' ? chalk.red('○')
|
|
314
|
-
: h.verdict === 'expired' ? chalk.yellow('○')
|
|
315
|
-
: chalk.yellow('◐');
|
|
316
|
-
return `${glyph} ${chalk.gray(formatCheckedAge(h.checkedAt))}`;
|
|
317
|
-
}
|
|
318
319
|
async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
319
320
|
const spinnerText = filterAgentId
|
|
320
321
|
? `Checking ${agentLabel(filterAgentId)} agents...`
|
|
@@ -365,9 +366,6 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
365
366
|
}
|
|
366
367
|
}
|
|
367
368
|
// Shim healing is silent — users don't need to know about internal repairs
|
|
368
|
-
const selfHost = machineId();
|
|
369
|
-
// Read the auth-health cache once (not per version row — see the batching note above).
|
|
370
|
-
const authCache = readAuthHealthCache();
|
|
371
369
|
// Pre-fetch account info for all versions in parallel. Spinner stays up through
|
|
372
370
|
// account + usage so a multi-account cold path doesn't leave a blank terminal
|
|
373
371
|
// after "Checking…" vanishes (the hang the screenshots caught).
|
|
@@ -535,15 +533,7 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
535
533
|
const info = rawInfo ? mergeCanonical(rawInfo) : undefined;
|
|
536
534
|
const usageKey = getUsageLookupKey(info);
|
|
537
535
|
const usageInfo = usageKey ? usageByKey.get(usageKey) : undefined;
|
|
538
|
-
const
|
|
539
|
-
const usageUnavailable = agentReportsUsage(agentId) && !!info?.signedIn && !usageInfo?.snapshot && !headless;
|
|
540
|
-
const usageUnverified = !headless && !!usageInfo?.snapshot && !!usageInfo.error;
|
|
541
|
-
const usageStr = formatUsageSummary(info?.plan || null, usageInfo?.snapshot || null, maxPlanWidth, {
|
|
542
|
-
unavailable: usageUnavailable,
|
|
543
|
-
unverified: usageUnverified,
|
|
544
|
-
headless,
|
|
545
|
-
maxWindows: usageWindowCap,
|
|
546
|
-
});
|
|
536
|
+
const usageStr = formatUsageSummary(info?.plan || null, usageInfo?.snapshot || null, maxPlanWidth, viewUsageSummaryOptions(agentId, !!info?.signedIn, usageInfo, usageWindowCap, v));
|
|
547
537
|
maxUsageWidth = Math.max(maxUsageWidth, stringWidth(usageStr));
|
|
548
538
|
const statusStr = formatUsageStatusBadge(info?.usageStatus);
|
|
549
539
|
maxStatusWidth = Math.max(maxStatusWidth, stringWidth(statusStr));
|
|
@@ -603,19 +593,12 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
603
593
|
}
|
|
604
594
|
const hasEmail = !!vInfo?.email;
|
|
605
595
|
const signedIn = !!vInfo?.signedIn;
|
|
606
|
-
const
|
|
607
|
-
const usageUnavailable = agentReportsUsage(agentId) && signedIn && !usageInfo?.snapshot && !headless;
|
|
608
|
-
const usageUnverified = !headless && !!usageInfo?.snapshot && !!usageInfo.error;
|
|
609
|
-
const usageStr = formatUsageSummary(vInfo?.plan || null, usageInfo?.snapshot || null, maxPlanWidth, {
|
|
610
|
-
unavailable: usageUnavailable,
|
|
611
|
-
unverified: usageUnverified,
|
|
612
|
-
headless,
|
|
613
|
-
maxWindows: usageWindowCap,
|
|
614
|
-
});
|
|
596
|
+
const usageStr = formatUsageSummary(vInfo?.plan || null, usageInfo?.snapshot || null, maxPlanWidth, viewUsageSummaryOptions(agentId, signedIn, usageInfo, usageWindowCap, version));
|
|
615
597
|
const hasUsage = usageStr.length > 0;
|
|
616
598
|
// Only show lastActive for versions with an actual logged-in account.
|
|
617
599
|
// Otherwise it reflects install time (misleading "just now" for fresh installs).
|
|
618
|
-
const
|
|
600
|
+
const lastActive = vInfo && hasEmail ? formatLastActive(vInfo.lastActive) : '';
|
|
601
|
+
const activeStr = lastActive;
|
|
619
602
|
const hasActive = activeStr.length > 0;
|
|
620
603
|
// The model now has its own column above; keep only the run mode here.
|
|
621
604
|
const runDefaults = resolveRunDefaults(agentId, version);
|
|
@@ -652,16 +635,12 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
652
635
|
const statusStr = formatUsageStatusBadge(vInfo?.usageStatus);
|
|
653
636
|
parts.push(padToWidth(statusStr, maxStatusWidth));
|
|
654
637
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
parts.push(hasActive ? padToWidth(activeStr, LAST_ACTIVE_COL_WIDTH) : ' '.repeat(LAST_ACTIVE_COL_WIDTH));
|
|
638
|
+
if (hasActive)
|
|
639
|
+
parts.push(activeStr);
|
|
658
640
|
}
|
|
659
641
|
if (runDefaultBits.length > 0) {
|
|
660
642
|
parts.push(chalk.gray(`run ${runDefaultBits.join(' ')}`));
|
|
661
643
|
}
|
|
662
|
-
const authChip = liveAuthChip(authCache, selfHost, agentId, version);
|
|
663
|
-
if (authChip)
|
|
664
|
-
parts.push(authChip);
|
|
665
644
|
console.log(joinViewColumns(parts));
|
|
666
645
|
if (showPaths) {
|
|
667
646
|
const versionDir = getVersionDir(agentId, version);
|
|
@@ -731,12 +710,7 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
731
710
|
gMaxStatusWidth = Math.max(gMaxStatusWidth, stringWidth(formatUsageStatusBadge(gInfo?.usageStatus)));
|
|
732
711
|
const gUsageKey = getUsageLookupKey(gInfo);
|
|
733
712
|
const gUsage = gUsageKey ? usageByKey.get(gUsageKey) : undefined;
|
|
734
|
-
const
|
|
735
|
-
const gUsageStr = formatUsageSummary(gInfo?.plan || null, gUsage?.snapshot || null, 3, {
|
|
736
|
-
unverified: !gHeadless && !!gUsage?.snapshot && !!gUsage.error,
|
|
737
|
-
headless: gHeadless,
|
|
738
|
-
maxWindows: usageWindowCap,
|
|
739
|
-
});
|
|
713
|
+
const gUsageStr = formatUsageSummary(gInfo?.plan || null, gUsage?.snapshot || null, 3, viewUsageSummaryOptions(agentId, !!gInfo?.signedIn, gUsage, usageWindowCap));
|
|
740
714
|
gMaxUsageWidth = Math.max(gMaxUsageWidth, stringWidth(gUsageStr));
|
|
741
715
|
const gDisplay = accountColumnLabel(gInfo);
|
|
742
716
|
if (gDisplay)
|
|
@@ -754,13 +728,9 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
754
728
|
const parts = [` ${verLabel}${padding}`];
|
|
755
729
|
const gUsageKey = getUsageLookupKey(gInfo);
|
|
756
730
|
const gUsage = gUsageKey ? usageByKey.get(gUsageKey) : undefined;
|
|
757
|
-
const
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
headless: gHeadless,
|
|
761
|
-
maxWindows: usageWindowCap,
|
|
762
|
-
});
|
|
763
|
-
const gActiveStr = gInfo ? formatLastActive(gInfo.lastActive) : '';
|
|
731
|
+
const gUsageStr = formatUsageSummary(gInfo?.plan || null, gUsage?.snapshot || null, 3, viewUsageSummaryOptions(agentId, !!gInfo?.signedIn, gUsage, usageWindowCap));
|
|
732
|
+
const gLastActive = gInfo ? formatLastActive(gInfo.lastActive) : '';
|
|
733
|
+
const gActiveStr = gLastActive;
|
|
764
734
|
if (gInfo?.email || gUsageStr || gActiveStr || gInfo?.signedIn) {
|
|
765
735
|
const gDisplay = accountColumnLabel(gInfo);
|
|
766
736
|
parts.push(gDisplay ? chalk.cyan(padToWidth(gDisplay, gMaxEmail)) : ' '.repeat(gMaxEmail));
|
|
@@ -771,7 +741,7 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
771
741
|
parts.push(padToWidth(formatUsageStatusBadge(gInfo?.usageStatus), gMaxStatusWidth));
|
|
772
742
|
}
|
|
773
743
|
if (gActiveStr)
|
|
774
|
-
parts.push(
|
|
744
|
+
parts.push(gActiveStr);
|
|
775
745
|
console.log(joinViewColumns(parts));
|
|
776
746
|
if (showPaths && cliState?.path) {
|
|
777
747
|
console.log(chalk.gray(` ${cliState.path}`));
|
|
@@ -144,8 +144,7 @@ export function registerWatchdogCommand(program) {
|
|
|
144
144
|
.option('--cooldown <dur>', 'Minimum time between nudges to the same session', humanMs(DEFAULT_THRESHOLDS.cooldownMs))
|
|
145
145
|
.option('--dormant <dur>', 'Idle time after which a session is left alone (dormant)', humanMs(DEFAULT_THRESHOLDS.dormantMs))
|
|
146
146
|
.option('--text <text>', 'Nudge text delivered into the terminal', 'Continue.')
|
|
147
|
-
.option('--smart', '
|
|
148
|
-
.option('--smart-agent <agent>', 'Agent the --smart decider runs as', 'claude')
|
|
147
|
+
.option('--smart-agent <agent>', 'Agent the watchdog decider runs as', 'claude')
|
|
149
148
|
.option('--allow-ghostty-focus', 'Permit the coarse, focus-stealing Ghostty path (off by default)')
|
|
150
149
|
.option('--verbose', 'Show healthy and non-actionable session inspections too')
|
|
151
150
|
.option('--json', 'Emit the tick result as JSON (for the menu-bar / scripts)')
|
|
@@ -163,7 +162,6 @@ export function registerWatchdogCommand(program) {
|
|
|
163
162
|
const tickOnce = async (willInject, sessions) => runWatchdogPass({
|
|
164
163
|
nudge: willInject,
|
|
165
164
|
nudgeText: opts.text,
|
|
166
|
-
smart: opts.smart === true,
|
|
167
165
|
smartAgent: opts.smartAgent,
|
|
168
166
|
thresholds,
|
|
169
167
|
allowGhosttyFocus: opts.allowGhosttyFocus === true,
|
|
@@ -233,17 +231,14 @@ export function registerWatchdogCommand(program) {
|
|
|
233
231
|
agents watchdog rotate off
|
|
234
232
|
`,
|
|
235
233
|
notes: `
|
|
236
|
-
Decision path:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
WORKFLOW.md in project/user workflows/ to override the prompt + model);
|
|
245
|
-
absent one, the built-in prompt runs via 'agents run --mode plan'. Pass
|
|
246
|
-
--smart to force every stalled candidate through the brain.
|
|
234
|
+
Decision path: the watchdog is an AGENT, not a heuristic script. Every idle
|
|
235
|
+
session on this machine (its task + transcript tail) is handed to ONE
|
|
236
|
+
'agents run --mode plan' call per tick, which judges each: idle-but-unfinished
|
|
237
|
+
(was given a task, went quiet, not finished/handed off) -> NUDGE that drives
|
|
238
|
+
it to finish; idle-and-done or genuinely-needs-human -> SKIP. The agent is a
|
|
239
|
+
customizable 'watchdog' workflow (drop a WORKFLOW.md in project/user
|
|
240
|
+
workflows/ to override the prompt + model); absent one, the built-in prompt
|
|
241
|
+
runs. Nothing is nudged when nothing is idle (no agent is spawned).
|
|
247
242
|
|
|
248
243
|
Delivery (answer-router): a running agent is steered via its mailbox; a
|
|
249
244
|
parked-on-question agent is answered into its EXACT split -- tmux / iTerm /
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* - `__vault-age-helper`
|
|
15
15
|
* - `__secrets-get` / `__secrets-ping` / `__secrets-lock` (SYNC_* tokens)
|
|
16
16
|
* - `__shim`
|
|
17
|
+
* - `__claude-statusline`
|
|
17
18
|
* - `__daemon-run`
|
|
18
19
|
*
|
|
19
20
|
* The tokens are imported from the leaf module sync-commands.ts — the SAME
|
|
@@ -70,6 +71,10 @@ if (process.argv[2] === '__shim') {
|
|
|
70
71
|
const code = await execShimPassthrough(agent, rawArgs, process.cwd(), pinned || undefined);
|
|
71
72
|
process.exit(code);
|
|
72
73
|
}
|
|
74
|
+
if (process.argv[2] === '__claude-statusline') {
|
|
75
|
+
const { runClaudeStatusLine } = await import('./lib/claude-statusline.js');
|
|
76
|
+
process.exit(await runClaudeStatusLine());
|
|
77
|
+
}
|
|
73
78
|
if (process.argv[2] === '__daemon-run') {
|
|
74
79
|
const { runDaemon, log: daemonLog } = await import('./lib/daemon/daemon.js');
|
|
75
80
|
// RUSH-2418: the daemon is the one always-on process here, and it ran with no
|
|
@@ -19,6 +19,10 @@ export interface NativeAccountCapability {
|
|
|
19
19
|
scope: 'version' | 'device' | 'unsupported';
|
|
20
20
|
status: 'supported' | 'conditional' | 'discovery-only' | 'unsupported';
|
|
21
21
|
}
|
|
22
|
+
/** Native-login selector coverage required by RUSH-3053. */
|
|
23
|
+
export declare const NATIVE_ACCOUNT_SELECTOR_AGENTS: readonly ["claude", "codex", "cursor", "grok", "kimi"];
|
|
24
|
+
/** Explicit dispositions for config-isolated harnesses outside that selector. */
|
|
25
|
+
export declare const NATIVE_ACCOUNT_SELECTOR_EXCLUSIONS: Partial<Record<AgentId, string>>;
|
|
22
26
|
/** Canonical truth for native-account naming and attachment semantics. */
|
|
23
27
|
export declare const NATIVE_ACCOUNT_CAPABILITIES: Record<AgentId, NativeAccountCapability>;
|
|
24
28
|
export declare function nativeAccountCapability(agent: AgentId): NativeAccountCapability;
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
/** Native-login selector coverage required by RUSH-3053. */
|
|
2
|
+
export const NATIVE_ACCOUNT_SELECTOR_AGENTS = ['claude', 'codex', 'cursor', 'grok', 'kimi'];
|
|
3
|
+
/** Explicit dispositions for config-isolated harnesses outside that selector. */
|
|
4
|
+
export const NATIVE_ACCOUNT_SELECTOR_EXCLUSIONS = {
|
|
5
|
+
copilot: 'no inspectable native identity',
|
|
6
|
+
opencode: 'provider-set identity is not safely attributable to one native login',
|
|
7
|
+
muse: 'email-only conditional identity is outside the RUSH-3053 harness contract',
|
|
8
|
+
};
|
|
1
9
|
/** Canonical truth for native-account naming and attachment semantics. */
|
|
2
10
|
export const NATIVE_ACCOUNT_CAPABILITIES = {
|
|
3
|
-
//
|
|
4
|
-
//
|
|
11
|
+
// Config-isolated harnesses with a stable native identity. A labeled launch
|
|
12
|
+
// may use the auth/config home from one installed version with another binary.
|
|
5
13
|
claude: { inspection: 'strong', scope: 'version', status: 'supported' },
|
|
6
14
|
codex: { inspection: 'strong', scope: 'version', status: 'supported' },
|
|
7
15
|
grok: { inspection: 'strong', scope: 'version', status: 'supported' },
|
|
16
|
+
cursor: { inspection: 'strong', scope: 'version', status: 'supported' },
|
|
17
|
+
// Kimi exposes a stable opaque id but no email, so it requires a manual label.
|
|
18
|
+
kimi: { inspection: 'opaque', scope: 'version', status: 'supported' },
|
|
8
19
|
// Version-scoped but only an email identity — nameable only when that email is
|
|
9
20
|
// present (the resolver rejects an emailless Muse login).
|
|
10
21
|
muse: { inspection: 'email', scope: 'version', status: 'conditional' },
|
|
@@ -17,12 +28,8 @@ export const NATIVE_ACCOUNT_CAPABILITIES = {
|
|
|
17
28
|
// "unsupported" is correct until a device-scoped identity key (with a stable
|
|
18
29
|
// device id) exists to validate against.
|
|
19
30
|
antigravity: { inspection: 'opaque', scope: 'device', status: 'unsupported' },
|
|
20
|
-
kimi: { inspection: 'opaque', scope: 'device', status: 'unsupported' },
|
|
21
31
|
droid: { inspection: 'opaque', scope: 'device', status: 'unsupported' },
|
|
22
32
|
opencode: { inspection: 'opaque', scope: 'device', status: 'unsupported' },
|
|
23
|
-
// Cursor multi-account isolation is unresolved (RUSH-2400) — blocked from
|
|
24
|
-
// native naming/attachment; its API-key path is a provider account instead.
|
|
25
|
-
cursor: { inspection: 'none', scope: 'unsupported', status: 'unsupported' },
|
|
26
33
|
// Discoverable in the list, but not nameable.
|
|
27
34
|
gemini: { inspection: 'email', scope: 'unsupported', status: 'discovery-only' },
|
|
28
35
|
copilot: { inspection: 'none', scope: 'unsupported', status: 'unsupported' },
|
|
@@ -62,9 +69,6 @@ export function nativeAccountNamingRefusal(agent) {
|
|
|
62
69
|
if (cap.scope === 'device') {
|
|
63
70
|
return `${agent} accounts can't be isolated by agents-cli yet (device-scoped login). ${suffix}`;
|
|
64
71
|
}
|
|
65
|
-
if (agent === 'cursor') {
|
|
66
|
-
return `${agent} accounts can't be isolated by agents-cli yet (multi-account isolation unresolved). ${suffix}`;
|
|
67
|
-
}
|
|
68
72
|
if (cap.status === 'discovery-only') {
|
|
69
73
|
return `${agent} native accounts are discovery-only; agents-cli cannot name or attach this login. ${suffix}`;
|
|
70
74
|
}
|
|
@@ -48,6 +48,8 @@ export declare function listNativeAccounts(meta: Pick<Meta, 'accounts'>): Native
|
|
|
48
48
|
*/
|
|
49
49
|
export declare function findUnifiedAccount(nameOrId: string, meta: Pick<Meta, 'accounts'>, doc?: AccountRegistryDocument): UnifiedAccount | null;
|
|
50
50
|
export declare function addNativeAccount(name: string, agent: AgentId, identityKey: string, identityLabel: string | undefined, scope: 'version' | 'device'): NativeAccount;
|
|
51
|
+
/** Create or replace the version-independent label for one native identity. */
|
|
52
|
+
export declare function labelNativeAccount(agent: AgentId, identityKey: string, identityLabel: string | undefined, label: string | undefined, scope: 'version' | 'device'): NativeAccount;
|
|
51
53
|
export declare function bindAccount(nameOrId: string, target: string): UnifiedAccount;
|
|
52
54
|
export declare function unbindAccount(nameOrId: string, target: string): void;
|
|
53
55
|
export declare function accountBindings(accountId: string, meta: Pick<Meta, 'accounts'>): string[];
|
|
@@ -25,12 +25,17 @@ import { bundleExists, deleteBundle, listBundles, readBundle, renameBundle, writ
|
|
|
25
25
|
import { getAccountProvider } from './account-provider-registry.js';
|
|
26
26
|
import { accountSecretItem, buildAccountBundle, parseAccountBundle } from './account-schema.js';
|
|
27
27
|
const NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
28
|
+
const NATIVE_LABEL = /^[a-zA-Z0-9][a-zA-Z0-9@._+-]*$/;
|
|
28
29
|
const AUTH_KINDS = ['api-key', 'setup-token', 'bearer-token'];
|
|
29
30
|
export function accountRegistryPath(base = getUserAgentsDir()) { return path.join(base, 'accounts.yaml'); }
|
|
30
31
|
function assertName(name) {
|
|
31
32
|
if (!NAME.test(name))
|
|
32
33
|
throw new Error('Account name must start with a letter or number and contain only letters, numbers, dot, underscore, or dash.');
|
|
33
34
|
}
|
|
35
|
+
function assertNativeLabel(label) {
|
|
36
|
+
if (!NATIVE_LABEL.test(label))
|
|
37
|
+
throw new Error('Account label must start with a letter or number and contain only letters, numbers, @, dot, underscore, plus, or dash.');
|
|
38
|
+
}
|
|
34
39
|
function isAccountAuthKind(value) {
|
|
35
40
|
return typeof value === 'string' && AUTH_KINDS.includes(value);
|
|
36
41
|
}
|
|
@@ -143,7 +148,8 @@ export function listNativeAccounts(meta) {
|
|
|
143
148
|
* able to omit it.
|
|
144
149
|
*/
|
|
145
150
|
export function findUnifiedAccount(nameOrId, meta, doc) {
|
|
146
|
-
const
|
|
151
|
+
const needle = nameOrId.toLowerCase();
|
|
152
|
+
const native = listNativeAccounts(meta).find(account => account.id === nameOrId || account.name.toLowerCase() === needle || account.identityLabel?.toLowerCase() === needle);
|
|
147
153
|
if (native)
|
|
148
154
|
return native;
|
|
149
155
|
const provider = findAccount(nameOrId, doc ?? readAccountRegistry());
|
|
@@ -154,7 +160,7 @@ function assertUniqueUnifiedName(name, meta, doc) {
|
|
|
154
160
|
throw new Error(`Account '${name}' already exists.`);
|
|
155
161
|
}
|
|
156
162
|
export function addNativeAccount(name, agent, identityKey, identityLabel, scope) {
|
|
157
|
-
|
|
163
|
+
assertNativeLabel(name);
|
|
158
164
|
const meta = readMeta();
|
|
159
165
|
assertUniqueUnifiedName(name, meta);
|
|
160
166
|
const duplicate = listNativeAccounts(meta).find(account => account.agent === agent && account.identityKey === identityKey);
|
|
@@ -170,6 +176,28 @@ export function addNativeAccount(name, agent, identityKey, identityLabel, scope)
|
|
|
170
176
|
}));
|
|
171
177
|
return account;
|
|
172
178
|
}
|
|
179
|
+
/** Create or replace the version-independent label for one native identity. */
|
|
180
|
+
export function labelNativeAccount(agent, identityKey, identityLabel, label, scope) {
|
|
181
|
+
const resolvedLabel = label ?? identityLabel;
|
|
182
|
+
if (!resolvedLabel)
|
|
183
|
+
throw new Error(`${agent} does not expose an email; pass a manual label.`);
|
|
184
|
+
assertNativeLabel(resolvedLabel);
|
|
185
|
+
const meta = readMeta();
|
|
186
|
+
const existing = listNativeAccounts(meta).find(account => account.agent === agent && account.identityKey === identityKey);
|
|
187
|
+
const collision = findUnifiedAccount(resolvedLabel, meta);
|
|
188
|
+
if (collision && collision.id !== existing?.id)
|
|
189
|
+
throw new Error(`Account '${resolvedLabel}' already exists.`);
|
|
190
|
+
if (!existing)
|
|
191
|
+
return addNativeAccount(resolvedLabel, agent, identityKey, identityLabel, scope);
|
|
192
|
+
updateMeta(current => ({
|
|
193
|
+
...current,
|
|
194
|
+
accounts: {
|
|
195
|
+
...current.accounts,
|
|
196
|
+
native: { ...current.accounts?.native, [existing.id]: { ...current.accounts?.native?.[existing.id], name: resolvedLabel, identityLabel } },
|
|
197
|
+
},
|
|
198
|
+
}));
|
|
199
|
+
return { ...existing, name: resolvedLabel, identityLabel };
|
|
200
|
+
}
|
|
173
201
|
export function bindAccount(nameOrId, target) {
|
|
174
202
|
const meta = readMeta();
|
|
175
203
|
const account = findUnifiedAccount(nameOrId, meta);
|
|
@@ -118,6 +118,15 @@ export declare const USAGE_DECISION_MAX_AGE_MS: number;
|
|
|
118
118
|
/**
|
|
119
119
|
* Whether this candidate's usage number is recent enough to route on. A missing
|
|
120
120
|
* snapshot is unverified by definition — there is no number to trust.
|
|
121
|
+
*
|
|
122
|
+
* A snapshot with NO windows is unverified for the same reason, however fresh
|
|
123
|
+
* it is: it carries a subscription plan and no utilization, so there is still
|
|
124
|
+
* no number. Freshness alone would make a meterless harness (Grok reports a
|
|
125
|
+
* tier and no meters) verify against nothing — and since `preferVerified`
|
|
126
|
+
* narrows the pool to verified candidates, the one account whose billing log
|
|
127
|
+
* was touched most recently would win every draw, then win again because
|
|
128
|
+
* running it refreshes that log. That self-reinforcing pin is exactly what the
|
|
129
|
+
* narrowing rule below exists to prevent.
|
|
121
130
|
*/
|
|
122
131
|
export declare function isUsageVerified(candidate: RotateCandidate, nowMs?: number): boolean;
|
|
123
132
|
/**
|
|
@@ -113,10 +113,20 @@ export const USAGE_DECISION_MAX_AGE_MS = 5 * 60 * 1000;
|
|
|
113
113
|
/**
|
|
114
114
|
* Whether this candidate's usage number is recent enough to route on. A missing
|
|
115
115
|
* snapshot is unverified by definition — there is no number to trust.
|
|
116
|
+
*
|
|
117
|
+
* A snapshot with NO windows is unverified for the same reason, however fresh
|
|
118
|
+
* it is: it carries a subscription plan and no utilization, so there is still
|
|
119
|
+
* no number. Freshness alone would make a meterless harness (Grok reports a
|
|
120
|
+
* tier and no meters) verify against nothing — and since `preferVerified`
|
|
121
|
+
* narrows the pool to verified candidates, the one account whose billing log
|
|
122
|
+
* was touched most recently would win every draw, then win again because
|
|
123
|
+
* running it refreshes that log. That self-reinforcing pin is exactly what the
|
|
124
|
+
* narrowing rule below exists to prevent.
|
|
116
125
|
*/
|
|
117
126
|
export function isUsageVerified(candidate, nowMs = Date.now()) {
|
|
118
|
-
const
|
|
119
|
-
|
|
127
|
+
const snapshot = candidate.usageSnapshot;
|
|
128
|
+
const capturedAt = snapshot?.capturedAt;
|
|
129
|
+
if (!capturedAt || !snapshot?.windows.length)
|
|
120
130
|
return false;
|
|
121
131
|
return nowMs - capturedAt.getTime() <= USAGE_DECISION_MAX_AGE_MS;
|
|
122
132
|
}
|