@phnx-labs/agents-cli 1.22.117 → 1.22.119
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 +301 -0
- package/dist/bootstrap.js +4 -11
- package/dist/commands/accounts.js +7 -4
- package/dist/commands/browser.d.ts +52 -70
- package/dist/commands/browser.js +192 -3562
- package/dist/commands/daemon.d.ts +39 -2
- package/dist/commands/daemon.js +72 -73
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.js +53 -1
- package/dist/commands/projects.js +92 -7
- package/dist/commands/repo.js +13 -49
- package/dist/commands/route.js +5 -5
- package/dist/commands/routines.js +39 -12
- package/dist/commands/sessions-render.d.ts +1 -1
- package/dist/commands/sessions-render.js +0 -1
- package/dist/commands/sessions.test-fixture.d.ts +0 -1
- package/dist/commands/sessions.test-fixture.js +0 -37
- package/dist/commands/setup-browser.d.ts +17 -12
- package/dist/commands/setup-browser.js +52 -87
- package/dist/commands/setup-preferences.d.ts +0 -17
- package/dist/commands/setup-preferences.js +18 -49
- package/dist/commands/setup.js +9 -9
- package/dist/commands/ssh.js +10 -1
- package/dist/commands/sync.js +6 -44
- package/dist/commands/teams.js +0 -1
- package/dist/commands/update.d.ts +19 -0
- package/dist/commands/update.js +34 -6
- package/dist/commands/view.d.ts +12 -1
- package/dist/commands/view.js +47 -2
- package/dist/index.js +107 -26
- package/dist/lib/account-capabilities.js +0 -2
- package/dist/lib/account-catalog.d.ts +38 -0
- package/dist/lib/account-catalog.js +110 -26
- package/dist/lib/account-provider-registry.js +1 -1
- package/dist/lib/account-registry.d.ts +1 -1
- package/dist/lib/account-registry.js +36 -2
- package/dist/lib/accounting/rotate.js +22 -8
- package/dist/lib/accounting/usage-ingest.d.ts +6 -1
- package/dist/lib/accounting/usage-ingest.js +115 -45
- package/dist/lib/accounting/usage-sync.d.ts +134 -31
- package/dist/lib/accounting/usage-sync.js +227 -66
- package/dist/lib/accounting/usage.d.ts +16 -3
- package/dist/lib/accounting/usage.js +26 -8
- package/dist/lib/accounts/add.d.ts +3 -1
- package/dist/lib/accounts/add.js +24 -13
- package/dist/lib/accounts/slots.d.ts +8 -0
- package/dist/lib/accounts/slots.js +17 -0
- package/dist/lib/add-dir.js +0 -1
- package/dist/lib/agent-cli-commands.js +0 -1
- package/dist/lib/agent-spec/agents.d.ts +6 -7
- package/dist/lib/agent-spec/agents.js +7 -60
- package/dist/lib/auth-health.d.ts +69 -1
- package/dist/lib/auth-health.js +133 -4
- package/dist/lib/browser/context.d.ts +82 -0
- package/dist/lib/browser/context.js +88 -0
- package/dist/lib/browser/paths.d.ts +25 -0
- package/dist/lib/browser/paths.js +51 -0
- package/dist/lib/browser/record.d.ts +38 -0
- package/dist/lib/browser/record.js +57 -0
- package/dist/lib/browser/sessions-list.js +1 -2
- package/dist/lib/browser-client.d.ts +158 -0
- package/dist/lib/browser-client.js +234 -0
- package/dist/lib/channels/owner-sink.d.ts +1 -18
- package/dist/lib/channels/owner-sink.js +29 -82
- package/dist/lib/channels/providers/rush.d.ts +7 -7
- package/dist/lib/channels/providers/rush.js +127 -57
- package/dist/lib/claude-account-token.d.ts +7 -1
- package/dist/lib/claude-account-token.js +26 -4
- package/dist/lib/claude-statusline.d.ts +1 -1
- package/dist/lib/claude-statusline.js +15 -3
- package/dist/lib/commands.js +1 -1
- package/dist/lib/crabbox/lease.js +1 -1
- package/dist/lib/crabbox/runtimes.js +1 -3
- package/dist/lib/daemon/account-state-daemon-service.js +1 -0
- package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
- package/dist/lib/daemon/auth-sync-service.js +37 -43
- package/dist/lib/daemon/daemon.d.ts +25 -14
- package/dist/lib/daemon/daemon.js +92 -154
- package/dist/lib/daemon/runner.d.ts +2 -2
- package/dist/lib/daemon/runner.js +32 -9
- package/dist/lib/daemon/self-update-service.d.ts +35 -34
- package/dist/lib/daemon/self-update-service.js +40 -36
- package/dist/lib/daemon/service.d.ts +12 -8
- package/dist/lib/daemon/supervisor.d.ts +65 -54
- package/dist/lib/daemon/supervisor.js +136 -171
- package/dist/lib/daemon/testdata/process-view-start.js +5 -2
- package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
- package/dist/lib/daemon/usage-sync-service.js +46 -78
- package/dist/lib/daemon-health.d.ts +22 -5
- package/dist/lib/daemon-health.js +47 -3
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +0 -10
- package/dist/lib/daemon-ticks.js +4 -2
- package/dist/lib/devices/doctor-findings.js +8 -11
- package/dist/lib/devices/harness-inventory.js +14 -5
- package/dist/lib/exec-account-home.js +5 -0
- package/dist/lib/exec.js +10 -13
- package/dist/lib/feed/events.js +0 -1
- package/dist/lib/feed/tool-activity.js +5 -2
- package/dist/lib/fleet-shared-state.d.ts +45 -0
- package/dist/lib/fleet-shared-state.js +78 -9
- package/dist/lib/fs-atomic.js +25 -0
- package/dist/lib/github/pr-mergeable.d.ts +4 -2
- package/dist/lib/github/pr-mergeable.js +5 -3
- package/dist/lib/github/project-prs.d.ts +58 -3
- package/dist/lib/github/project-prs.js +205 -7
- package/dist/lib/harness/adapters/cursor.js +14 -4
- package/dist/lib/harness-auth-capabilities.d.ts +4 -3
- package/dist/lib/harness-auth-capabilities.js +1 -2
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/hooks/cache.d.ts +7 -0
- package/dist/lib/hooks/cache.js +27 -2
- package/dist/lib/hooks/install.js +7 -1
- package/dist/lib/hosts/ready.d.ts +38 -8
- package/dist/lib/hosts/ready.js +62 -14
- package/dist/lib/installations/active-check.d.ts +27 -0
- package/dist/lib/installations/active-check.js +61 -0
- package/dist/lib/installations/migrate.js +6 -3
- package/dist/lib/installations/shims.d.ts +1 -1
- package/dist/lib/installations/shims.js +32 -12
- package/dist/lib/installations/store.d.ts +9 -0
- package/dist/lib/installations/store.js +24 -0
- package/dist/lib/installations/update.js +21 -15
- package/dist/lib/installations/versions.js +15 -3
- package/dist/lib/manifest.js +1 -1
- package/dist/lib/models.js +0 -2
- package/dist/lib/open-url.js +28 -54
- package/dist/lib/profiles.js +0 -2
- package/dist/lib/projects.d.ts +16 -0
- package/dist/lib/projects.js +31 -0
- package/dist/lib/refresh.js +1 -1
- package/dist/lib/routine-readiness.d.ts +35 -0
- package/dist/lib/routine-readiness.js +74 -16
- package/dist/lib/rules/compile.js +6 -0
- package/dist/lib/sandbox.js +0 -2
- package/dist/lib/secrets-policy.d.ts +89 -11
- package/dist/lib/secrets-policy.js +175 -28
- package/dist/lib/session/active.d.ts +17 -6
- package/dist/lib/session/active.js +105 -49
- package/dist/lib/session/db.d.ts +1 -1
- package/dist/lib/session/db.js +65 -18
- package/dist/lib/session/discover.js +1 -283
- package/dist/lib/session/mirror.d.ts +4 -3
- package/dist/lib/session/mirror.js +4 -3
- package/dist/lib/session/parse.d.ts +0 -2
- package/dist/lib/session/parse.js +1 -138
- package/dist/lib/session/pid-registry.d.ts +6 -3
- package/dist/lib/session/pid-registry.js +18 -8
- package/dist/lib/session/remote/watch.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +7 -3
- package/dist/lib/session/throughput.d.ts +1 -3
- package/dist/lib/session/throughput.js +0 -20
- package/dist/lib/session/types.d.ts +2 -2
- package/dist/lib/session/types.js +2 -2
- package/dist/lib/sessions-client.d.ts +60 -1
- package/dist/lib/sessions-client.js +227 -4
- package/dist/lib/setup-tool-status.js +5 -10
- package/dist/lib/signin-badge.d.ts +3 -2
- package/dist/lib/signin-badge.js +6 -3
- package/dist/lib/smart-launch.js +4 -1
- package/dist/lib/staleness/detectors/commands.d.ts +1 -1
- package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
- package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
- package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
- package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
- package/dist/lib/staleness/detectors/rules.d.ts +1 -1
- package/dist/lib/staleness/detectors/skills.d.ts +1 -1
- package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
- package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
- package/dist/lib/staleness/writers/commands.d.ts +1 -1
- package/dist/lib/staleness/writers/hooks.d.ts +1 -1
- package/dist/lib/staleness/writers/mcp.d.ts +1 -1
- package/dist/lib/staleness/writers/permissions.d.ts +1 -1
- package/dist/lib/staleness/writers/plugins.d.ts +1 -1
- package/dist/lib/staleness/writers/rules.d.ts +1 -1
- package/dist/lib/staleness/writers/skills.d.ts +1 -1
- package/dist/lib/staleness/writers/sources.js +5 -0
- package/dist/lib/staleness/writers/subagents.d.ts +1 -1
- package/dist/lib/staleness/writers/workflows.d.ts +1 -1
- package/dist/lib/state.d.ts +6 -4
- package/dist/lib/state.js +7 -17
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/parsers.js +1 -136
- package/dist/lib/teams/placement-probe.js +50 -15
- package/dist/lib/teams/scheduler.d.ts +7 -0
- package/dist/lib/types.d.ts +16 -1
- package/dist/lib/types.js +1 -1
- package/dist/lib/view-types.d.ts +32 -0
- package/dist/lib/watchdog/read.js +0 -1
- package/dist/lib/watchdog/rotate.js +1 -2
- package/dist/lib/watchdog/runner.js +2 -2
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdogTail.js +0 -31
- package/package.json +1 -1
- package/scripts/postinstall.js +8 -8
- package/dist/browser.d.ts +0 -2
- package/dist/browser.js +0 -17
- package/dist/commands/browser-picker.d.ts +0 -18
- package/dist/commands/browser-picker.js +0 -97
- package/dist/lib/browser/arc-discovery.d.ts +0 -52
- package/dist/lib/browser/arc-discovery.js +0 -197
- package/dist/lib/browser/arc-dom.d.ts +0 -14
- package/dist/lib/browser/arc-dom.js +0 -121
- package/dist/lib/browser/caller-identity.d.ts +0 -40
- package/dist/lib/browser/caller-identity.js +0 -175
- package/dist/lib/browser/cdp.d.ts +0 -51
- package/dist/lib/browser/cdp.js +0 -254
- package/dist/lib/browser/chrome.d.ts +0 -141
- package/dist/lib/browser/chrome.js +0 -704
- package/dist/lib/browser/chromium-discovery.d.ts +0 -28
- package/dist/lib/browser/chromium-discovery.js +0 -96
- package/dist/lib/browser/devices.d.ts +0 -34
- package/dist/lib/browser/devices.js +0 -61
- package/dist/lib/browser/domain-skills.d.ts +0 -71
- package/dist/lib/browser/domain-skills.js +0 -195
- package/dist/lib/browser/drivers/arc.d.ts +0 -40
- package/dist/lib/browser/drivers/arc.js +0 -276
- package/dist/lib/browser/drivers/firefox.d.ts +0 -99
- package/dist/lib/browser/drivers/firefox.js +0 -377
- package/dist/lib/browser/drivers/local.d.ts +0 -55
- package/dist/lib/browser/drivers/local.js +0 -266
- package/dist/lib/browser/drivers/ssh.d.ts +0 -120
- package/dist/lib/browser/drivers/ssh.js +0 -467
- package/dist/lib/browser/editor.d.ts +0 -3
- package/dist/lib/browser/editor.js +0 -50
- package/dist/lib/browser/ffmpeg.d.ts +0 -12
- package/dist/lib/browser/ffmpeg.js +0 -184
- package/dist/lib/browser/firefox-discovery.d.ts +0 -69
- package/dist/lib/browser/firefox-discovery.js +0 -162
- package/dist/lib/browser/har.d.ts +0 -85
- package/dist/lib/browser/har.js +0 -77
- package/dist/lib/browser/hygiene.d.ts +0 -97
- package/dist/lib/browser/hygiene.js +0 -153
- package/dist/lib/browser/index.d.ts +0 -5
- package/dist/lib/browser/index.js +0 -5
- package/dist/lib/browser/input.d.ts +0 -7
- package/dist/lib/browser/input.js +0 -92
- package/dist/lib/browser/ipc.d.ts +0 -204
- package/dist/lib/browser/ipc.js +0 -1453
- package/dist/lib/browser/login-detection.d.ts +0 -87
- package/dist/lib/browser/login-detection.js +0 -274
- package/dist/lib/browser/profiles.d.ts +0 -422
- package/dist/lib/browser/profiles.js +0 -1157
- package/dist/lib/browser/refs.d.ts +0 -89
- package/dist/lib/browser/refs.js +0 -191
- package/dist/lib/browser/registry.d.ts +0 -70
- package/dist/lib/browser/registry.js +0 -252
- package/dist/lib/browser/remote-control.d.ts +0 -59
- package/dist/lib/browser/remote-control.js +0 -85
- package/dist/lib/browser/resolve-target.d.ts +0 -66
- package/dist/lib/browser/resolve-target.js +0 -264
- package/dist/lib/browser/runtime-state.d.ts +0 -291
- package/dist/lib/browser/runtime-state.js +0 -584
- package/dist/lib/browser/secret-ref.d.ts +0 -10
- package/dist/lib/browser/secret-ref.js +0 -14
- package/dist/lib/browser/service.d.ts +0 -812
- package/dist/lib/browser/service.js +0 -4693
- package/dist/lib/browser/stream.d.ts +0 -17
- package/dist/lib/browser/stream.js +0 -72
- package/dist/lib/browser/task-index.d.ts +0 -78
- package/dist/lib/browser/task-index.js +0 -199
- package/dist/lib/browser/types.d.ts +0 -647
- package/dist/lib/browser/types.js +0 -61
- package/dist/lib/browser/upload.d.ts +0 -29
- package/dist/lib/browser/upload.js +0 -298
- package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
- package/dist/lib/daemon/browser-ipc-service.js +0 -46
- package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
- package/dist/lib/daemon/browser-task-reap-service.js +0 -26
- package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
- package/dist/lib/fleet-shared-repo-sync.js +0 -490
|
@@ -89,9 +89,9 @@ export declare const ALL_AGENT_IDS: AgentId[];
|
|
|
89
89
|
* import (runner.ts already imports from routines.ts).
|
|
90
90
|
*
|
|
91
91
|
* This is a curated subset of AGENT_COMMANDS, not "every harness that can run
|
|
92
|
-
* headlessly". Expanding it is a product change
|
|
93
|
-
*
|
|
94
|
-
*
|
|
92
|
+
* headlessly". Expanding it is a product change. Argv itself is baked from
|
|
93
|
+
* AGENT_COMMANDS in daemon/runner.ts bakeRoutineArgv — do not reintroduce a
|
|
94
|
+
* second token table.
|
|
95
95
|
*/
|
|
96
96
|
export declare const ROUTINE_AGENT_IDS: readonly string[];
|
|
97
97
|
/** Agents that can receive managed installs, imports, and resource sync writes. */
|
|
@@ -222,7 +222,7 @@ export declare function formatClaudeOrgLabel(orgType: string | null | undefined)
|
|
|
222
222
|
*/
|
|
223
223
|
export declare function accountOrgBadge(info?: Pick<AccountInfo, 'organizationType' | 'organizationName'> | null): string | null;
|
|
224
224
|
/** Agents whose local credential formats expose enough state for account selection. */
|
|
225
|
-
export declare const ACCOUNT_INSPECTION_AGENT_IDS: readonly ["claude", "codex", "
|
|
225
|
+
export declare const ACCOUNT_INSPECTION_AGENT_IDS: readonly ["claude", "codex", "cursor", "grok", "antigravity", "kimi", "droid", "opencode", "muse"];
|
|
226
226
|
/** Whether agents-cli can determine this agent's per-version sign-in state. */
|
|
227
227
|
export declare function supportsAccountInspection(agentId: AgentId): boolean;
|
|
228
228
|
/**
|
|
@@ -573,9 +573,8 @@ export declare function stripJsonComments(content: string): string;
|
|
|
573
573
|
*
|
|
574
574
|
* All three MCP path resolvers read `MCP_TARGETS`, so the file the writer
|
|
575
575
|
* targets, the file the parser reads, and the file the staleness detector
|
|
576
|
-
* watches cannot drift apart. An agent with no MCP target
|
|
577
|
-
*
|
|
578
|
-
* parse.
|
|
576
|
+
* watches cannot drift apart. An agent with no MCP target keeps the historical
|
|
577
|
+
* settings.json default so old configs still parse.
|
|
579
578
|
*/
|
|
580
579
|
export declare function getUserMcpConfigPath(agentId: AgentId): string;
|
|
581
580
|
/**
|
|
@@ -268,6 +268,8 @@ export const AGENTS = {
|
|
|
268
268
|
format: 'markdown',
|
|
269
269
|
variableSyntax: '$ARGUMENTS',
|
|
270
270
|
supportsHooks: true,
|
|
271
|
+
nativePluginSkills: true,
|
|
272
|
+
ownedSkillDirs: ['synced'],
|
|
271
273
|
// Claude Code grew a native `claude --cloud "<prompt>"` (Anthropic-managed
|
|
272
274
|
// infra, claude.ai/code; requires claude.ai subscription auth). Routing
|
|
273
275
|
// still goes to Rush Cloud deliberately — it keeps cloud tasks in one
|
|
@@ -305,46 +307,6 @@ export const AGENTS = {
|
|
|
305
307
|
// ~/.codex/agents/*.toml (name, description, developer_instructions).
|
|
306
308
|
capabilities: { hooks: { since: '0.116.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: { since: '0.138.0' }, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: { since: '0.117.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: true, modes: ['plan', 'edit', 'auto', 'skip'], interactiveRepl: true },
|
|
307
309
|
},
|
|
308
|
-
gemini: {
|
|
309
|
-
id: 'gemini',
|
|
310
|
-
name: 'Gemini',
|
|
311
|
-
sessionDir: ['.gemini', 'tmp'],
|
|
312
|
-
sessionFileExt: '.json',
|
|
313
|
-
versionStdoutMatch: 'semver',
|
|
314
|
-
unmanagedBinary: 'path',
|
|
315
|
-
mcpRegister: 'cli',
|
|
316
|
-
mcpAddHttp: 'transport',
|
|
317
|
-
mcpAddStdio: 'simple',
|
|
318
|
-
mcpConfigWrite: 'json-mcpServers',
|
|
319
|
-
color: 'blue',
|
|
320
|
-
cliCommand: 'gemini',
|
|
321
|
-
npmPackage: '@google/gemini-cli',
|
|
322
|
-
configDir: path.join(HOME, '.gemini'),
|
|
323
|
-
commandsDir: path.join(HOME, '.gemini', 'commands'),
|
|
324
|
-
commandsSubdir: 'commands',
|
|
325
|
-
skillsDir: path.join(HOME, '.gemini', 'skills'),
|
|
326
|
-
hooksDir: 'hooks',
|
|
327
|
-
instructionsFile: 'GEMINI.md',
|
|
328
|
-
format: 'toml',
|
|
329
|
-
variableSyntax: '{{args}}',
|
|
330
|
-
supportsHooks: true,
|
|
331
|
-
nativeAgentsSkillsDir: true,
|
|
332
|
-
// Google retired the Gemini CLI (announced at Google I/O 2026, May 19); the `gemini`
|
|
333
|
-
// command stopped serving free/Pro/Ultra requests on June 18, 2026. Antigravity CLI
|
|
334
|
-
// (`agy`) is the official successor. Hard deprecation keeps legacy Gemini
|
|
335
|
-
// sessions/config parseable while blocking install/import/sync.
|
|
336
|
-
deprecated: {
|
|
337
|
-
by: 'Google',
|
|
338
|
-
date: 'June 18, 2026',
|
|
339
|
-
reason: 'The Gemini CLI was retired for free, Pro, and Ultra tiers and no longer serves requests (announced at Google I/O 2026 on May 19).',
|
|
340
|
-
replacement: 'antigravity',
|
|
341
|
-
url: 'https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/',
|
|
342
|
-
hard: true,
|
|
343
|
-
},
|
|
344
|
-
// gemini hooks: shipped in v0.26.0 (Jan 2026); older binaries silently ignore the `hooks` key.
|
|
345
|
-
// extensions: gemini-extension.json bundles shipped in v0.8.0; custom subagents in v0.36.0.
|
|
346
|
-
capabilities: { hooks: { since: '0.26.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: { since: '0.8.0' }, subagents: { since: '0.36.0' }, rules: { file: 'GEMINI.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'skip'], rulesImports: true, interactiveRepl: false },
|
|
347
|
-
},
|
|
348
310
|
cursor: {
|
|
349
311
|
id: 'cursor',
|
|
350
312
|
name: 'Cursor',
|
|
@@ -1019,16 +981,13 @@ export const ALL_AGENT_IDS = Object.keys(AGENTS);
|
|
|
1019
981
|
* import (runner.ts already imports from routines.ts).
|
|
1020
982
|
*
|
|
1021
983
|
* This is a curated subset of AGENT_COMMANDS, not "every harness that can run
|
|
1022
|
-
* headlessly". Expanding it is a product change
|
|
1023
|
-
*
|
|
1024
|
-
*
|
|
984
|
+
* headlessly". Expanding it is a product change. Argv itself is baked from
|
|
985
|
+
* AGENT_COMMANDS in daemon/runner.ts bakeRoutineArgv — do not reintroduce a
|
|
986
|
+
* second token table.
|
|
1025
987
|
*/
|
|
1026
988
|
export const ROUTINE_AGENT_IDS = Object.freeze([
|
|
1027
989
|
'claude',
|
|
1028
990
|
'codex',
|
|
1029
|
-
// gemini is hard-deprecated (Antigravity replaced it) — no routine target. A
|
|
1030
|
-
// legacy gemini routine now fails validateJob loud instead of firing a
|
|
1031
|
-
// retired backend; the id survives only for reading old sessions/config.
|
|
1032
991
|
'cursor',
|
|
1033
992
|
'kimi',
|
|
1034
993
|
'droid',
|
|
@@ -1338,7 +1297,6 @@ export function accountOrgBadge(info) {
|
|
|
1338
1297
|
export const ACCOUNT_INSPECTION_AGENT_IDS = [
|
|
1339
1298
|
'claude',
|
|
1340
1299
|
'codex',
|
|
1341
|
-
'gemini',
|
|
1342
1300
|
'cursor',
|
|
1343
1301
|
'grok',
|
|
1344
1302
|
'antigravity',
|
|
@@ -1429,7 +1387,6 @@ function resolveAccountCredentialPath(base, ...segments) {
|
|
|
1429
1387
|
const CREDENTIAL_FILE_SEGMENTS = {
|
|
1430
1388
|
claude: [['.claude', '.claude.json'], ['.claude.json']],
|
|
1431
1389
|
codex: [['.codex', 'auth.json']],
|
|
1432
|
-
gemini: [['.gemini', 'google_accounts.json']],
|
|
1433
1390
|
grok: [['.grok', 'auth.json']],
|
|
1434
1391
|
kimi: [['.kimi-code', 'credentials', 'kimi-code.json']],
|
|
1435
1392
|
droid: [['.factory', 'auth.v2.file']],
|
|
@@ -2028,7 +1985,6 @@ export async function getAccountInfo(agentId, home) {
|
|
|
2028
1985
|
const configFiles = {
|
|
2029
1986
|
claude: path.join(base, '.claude.json'),
|
|
2030
1987
|
codex: path.join(base, '.codex', 'auth.json'),
|
|
2031
|
-
gemini: path.join(base, '.gemini', 'google_accounts.json'),
|
|
2032
1988
|
// OpenCode keeps every session in ONE sqlite file rather than a directory of
|
|
2033
1989
|
// per-session transcripts, so the session-file walk resolveLastActive runs
|
|
2034
1990
|
// for other agents finds nothing. Its mtime fallback is the right read here:
|
|
@@ -2151,11 +2107,6 @@ export async function getAccountInfo(agentId, home) {
|
|
|
2151
2107
|
signedIn: !!email,
|
|
2152
2108
|
};
|
|
2153
2109
|
}
|
|
2154
|
-
case 'gemini': {
|
|
2155
|
-
const data = JSON.parse(await fs.promises.readFile(path.join(base, '.gemini', 'google_accounts.json'), 'utf-8'));
|
|
2156
|
-
const email = data.active || null;
|
|
2157
|
-
return { ...empty, email, signedIn: !!email, lastActive };
|
|
2158
|
-
}
|
|
2159
2110
|
case 'cursor': {
|
|
2160
2111
|
// Cursor CLI keeps account metadata in ~/.cursor/cli-config.json
|
|
2161
2112
|
// (authInfo: { email, userId, authId }) and its OAuth tokens SEPARATELY
|
|
@@ -2902,9 +2853,8 @@ function parseMcpFromOpenCodeConfig(configPath) {
|
|
|
2902
2853
|
*
|
|
2903
2854
|
* All three MCP path resolvers read `MCP_TARGETS`, so the file the writer
|
|
2904
2855
|
* targets, the file the parser reads, and the file the staleness detector
|
|
2905
|
-
* watches cannot drift apart. An agent with no MCP target
|
|
2906
|
-
*
|
|
2907
|
-
* parse.
|
|
2856
|
+
* watches cannot drift apart. An agent with no MCP target keeps the historical
|
|
2857
|
+
* settings.json default so old configs still parse.
|
|
2908
2858
|
*/
|
|
2909
2859
|
export function getUserMcpConfigPath(agentId) {
|
|
2910
2860
|
return getMcpConfigPathForHome(agentId, HOME);
|
|
@@ -3036,9 +2986,6 @@ const AGENT_NAME_ALIASES = {
|
|
|
3036
2986
|
codex: 'codex',
|
|
3037
2987
|
'openai-codex': 'codex',
|
|
3038
2988
|
cx: 'codex',
|
|
3039
|
-
gemini: 'gemini',
|
|
3040
|
-
'gemini-cli': 'gemini',
|
|
3041
|
-
gx: 'gemini',
|
|
3042
2989
|
cursor: 'cursor',
|
|
3043
2990
|
'cursor-agent': 'cursor',
|
|
3044
2991
|
cr: 'cursor',
|
|
@@ -19,10 +19,21 @@ import { type ProviderProbe } from './accounting/usage.js';
|
|
|
19
19
|
* endpoint cannot prove live for a known non-revocation
|
|
20
20
|
* reason (Claude setup-token usage-scope gap — RUSH-2392),
|
|
21
21
|
* OR a throttled probe with no fresh previous verdict (PHNX-4051).
|
|
22
|
+
* - `no_evidence` — a credential is present on disk but THIS box has no evidence,
|
|
23
|
+
* either way, that it can authenticate: no probe was possible
|
|
24
|
+
* (a worker's setup-token box does not probe the usage endpoint)
|
|
25
|
+
* and no run outcome has been recorded here yet. Distinct from
|
|
26
|
+
* `unverified` (which is "signed in, no probe endpoint exists for
|
|
27
|
+
* this harness") — `no_evidence` is "we did not look", the exact
|
|
28
|
+
* state PHNX-4116 stops rendering as a verdict word. A worker
|
|
29
|
+
* never PUBLISHES this row (it is dropped in
|
|
30
|
+
* {@link probeLocalFleetAuth}); it surfaces only as an in-memory
|
|
31
|
+
* account-catalog verdict whose display is a FACT (token presence
|
|
32
|
+
* + recorded run outcomes), never the word "no_evidence".
|
|
22
33
|
* - `unconfigured`— no usable credential on disk.
|
|
23
34
|
* - `error` — network/other failure; verdict indeterminate (keep the last known one).
|
|
24
35
|
*/
|
|
25
|
-
export type AuthVerdict = 'live' | 'revoked' | 'expired' | 'rate_limited' | 'unverified' | 'unconfigured' | 'error';
|
|
36
|
+
export type AuthVerdict = 'live' | 'revoked' | 'expired' | 'rate_limited' | 'unverified' | 'no_evidence' | 'unconfigured' | 'error';
|
|
26
37
|
export interface AuthHealth {
|
|
27
38
|
verdict: AuthVerdict;
|
|
28
39
|
/** epoch ms of the probe. */
|
|
@@ -33,6 +44,15 @@ export interface AuthHealth {
|
|
|
33
44
|
account?: string;
|
|
34
45
|
/** Stable registered account id. Display labels are never used as identity. */
|
|
35
46
|
accountId?: string;
|
|
47
|
+
/**
|
|
48
|
+
* How this row was observed (PHNX-4116). `probe` (default when absent) is a
|
|
49
|
+
* network/usage-endpoint probe the daemon ran; `run` is a REAL agent run
|
|
50
|
+
* outcome recorded on exit — a successful inference (`live`) or an auth failure
|
|
51
|
+
* (`revoked`/`expired`/`rate_limited`). The auth FACT `agents view` renders
|
|
52
|
+
* (`last used ok 12m ago` vs `last auth failure 401 …`) reads this so a token's
|
|
53
|
+
* usability is stated as evidence, not inferred from a stale usage file.
|
|
54
|
+
*/
|
|
55
|
+
source?: 'probe' | 'run';
|
|
36
56
|
}
|
|
37
57
|
/** Maximum age of an auth verdict used for automatic routing decisions. */
|
|
38
58
|
export declare const AUTH_PROBE_MAX_AGE_MS: number;
|
|
@@ -284,4 +304,52 @@ export declare function probeLocalFleetAuth(opts?: {
|
|
|
284
304
|
* merges as before.
|
|
285
305
|
*/
|
|
286
306
|
export declare function writeFleetAuthRows(host: string, rows: AuthProbeRow[], installed?: ReadonlySet<string>): void;
|
|
307
|
+
/**
|
|
308
|
+
* The installed-version label a run outcome falls back to when the account has no
|
|
309
|
+
* slot on this box: the explicit `version`, else the label derived from `home` (a
|
|
310
|
+
* version home is `<versionDir>/home`, so the label is its parent dir's basename).
|
|
311
|
+
* Null when neither resolves. Pure (no fs) so it is unit-tested directly.
|
|
312
|
+
*/
|
|
313
|
+
export declare function runOutcomeVersionKey(opts: {
|
|
314
|
+
version?: string | null;
|
|
315
|
+
home?: string | null;
|
|
316
|
+
}): string | null;
|
|
317
|
+
/** A real agent-run outcome — the evidence behind `last used ok` / `last auth failure`. */
|
|
318
|
+
export type RunAuthOutcome = {
|
|
319
|
+
ok: true;
|
|
320
|
+
} | {
|
|
321
|
+
ok: false;
|
|
322
|
+
verdict: 'revoked' | 'expired' | 'rate_limited' | 'error';
|
|
323
|
+
detail?: string;
|
|
324
|
+
resetsAt?: number | null;
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* Record a REAL run's auth outcome into the auth-health cache with
|
|
328
|
+
* `source: 'run'`. Best-effort and never throws: an unattributable run (no
|
|
329
|
+
* accountId/version/home) writes nothing, and the underlying write already
|
|
330
|
+
* swallows IO errors. Keyed exactly as the catalog reads, so the recorded fact
|
|
331
|
+
* surfaces on the same account row.
|
|
332
|
+
*/
|
|
333
|
+
export declare function recordRunAuthOutcome(opts: {
|
|
334
|
+
agent: AgentId | string;
|
|
335
|
+
accountId?: string | null;
|
|
336
|
+
version?: string | null;
|
|
337
|
+
home?: string | null;
|
|
338
|
+
account?: string;
|
|
339
|
+
outcome: RunAuthOutcome;
|
|
340
|
+
host?: string;
|
|
341
|
+
now?: number;
|
|
342
|
+
}): void;
|
|
343
|
+
/** `Sep 20 14:02` — the fact timestamp for an auth failure. */
|
|
344
|
+
export declare function factTimestamp(ms: number): string;
|
|
345
|
+
/**
|
|
346
|
+
* The auth FACT `agents view` / `agents accounts` render per account per box
|
|
347
|
+
* (PHNX-4116): what actually happened here, with its time — never a word that
|
|
348
|
+
* means "we did not look". A `live` row (a recorded run OR a real probe) reads
|
|
349
|
+
* `last used ok <age>`; a server rejection reads `last auth failure <detail>
|
|
350
|
+
* <time>`; a throttle reads `rate-limited <detail> (<time>)`; anything with no
|
|
351
|
+
* usable evidence — including `no_evidence`/`unverified` — reads `not used on
|
|
352
|
+
* this box yet`.
|
|
353
|
+
*/
|
|
354
|
+
export declare function formatAuthFact(health: AuthHealth | null | undefined, now?: number): string;
|
|
287
355
|
export {};
|
package/dist/lib/auth-health.js
CHANGED
|
@@ -27,6 +27,7 @@ import { getVersionHomePath, listInstalledVersions } from './installations/versi
|
|
|
27
27
|
import { atomicWriteFileSync, ensureLockTarget, withFileLock } from './fs-atomic.js';
|
|
28
28
|
import { selfConfiguredDeviceRole } from './device-config.js';
|
|
29
29
|
import { mayIssueUsageEndpointProbe, trySpendUsageApiCall } from './usage-refresh.js';
|
|
30
|
+
import { machineId } from './machine-id.js';
|
|
30
31
|
/** Maximum age of an auth verdict used for automatic routing decisions. */
|
|
31
32
|
export const AUTH_PROBE_MAX_AGE_MS = 20 * 60_000;
|
|
32
33
|
/** Agents with a live network probe wired up today. The rest are best-effort. */
|
|
@@ -82,6 +83,7 @@ const VERDICT_GLYPHS = {
|
|
|
82
83
|
expired: '○', // ○
|
|
83
84
|
rate_limited: '◐', // ◐
|
|
84
85
|
unverified: '◐', // ◐
|
|
86
|
+
no_evidence: '◌', // ◌ — credential present, no evidence either way
|
|
85
87
|
unconfigured: '·', // ·
|
|
86
88
|
error: '·', // ·
|
|
87
89
|
};
|
|
@@ -97,6 +99,7 @@ export function verdictLabel(verdict) {
|
|
|
97
99
|
case 'expired': return 'expired';
|
|
98
100
|
case 'rate_limited': return 'limited';
|
|
99
101
|
case 'unverified': return 'unverified';
|
|
102
|
+
case 'no_evidence': return 'no evidence';
|
|
100
103
|
case 'unconfigured': return '—';
|
|
101
104
|
case 'error': return '?';
|
|
102
105
|
}
|
|
@@ -109,7 +112,7 @@ export function summarizeVerdicts(verdicts) {
|
|
|
109
112
|
for (const v of verdicts) {
|
|
110
113
|
if (v === 'live')
|
|
111
114
|
live++;
|
|
112
|
-
else if (v === 'unverified')
|
|
115
|
+
else if (v === 'unverified' || v === 'no_evidence')
|
|
113
116
|
present++;
|
|
114
117
|
else if (v === 'revoked')
|
|
115
118
|
bad++;
|
|
@@ -132,6 +135,7 @@ export function verdictColor(verdict) {
|
|
|
132
135
|
case 'live': return 'green';
|
|
133
136
|
case 'revoked': return 'red';
|
|
134
137
|
case 'unverified': return 'gray';
|
|
138
|
+
case 'no_evidence': return 'gray';
|
|
135
139
|
case 'unconfigured': return 'dim';
|
|
136
140
|
default: return 'yellow'; // expired / rate_limited / error — soft, self-healing/indeterminate
|
|
137
141
|
}
|
|
@@ -188,6 +192,9 @@ export function summarizeHostAuth(cache, host) {
|
|
|
188
192
|
case 'unverified':
|
|
189
193
|
present++;
|
|
190
194
|
break; // signed in, no probe — benign
|
|
195
|
+
case 'no_evidence':
|
|
196
|
+
present++;
|
|
197
|
+
break; // credential present, no evidence — benign
|
|
191
198
|
case 'revoked':
|
|
192
199
|
revoked++;
|
|
193
200
|
break; // server said no — re-login
|
|
@@ -437,6 +444,14 @@ function verdictFromFreshUsage(usageKey, signedIn, now) {
|
|
|
437
444
|
const capturedAt = snapshot?.capturedAt?.getTime();
|
|
438
445
|
if (!capturedAt || now - capturedAt >= FRESH_USAGE_VERDICT_MAX_AGE_MS)
|
|
439
446
|
return null;
|
|
447
|
+
// A `sync`-sourced snapshot arrived from ANOTHER box's poller over the fleet
|
|
448
|
+
// store — it proves the shared setup-token works SOMEWHERE, not that THIS box
|
|
449
|
+
// can authenticate (PHNX-4116). Deriving `live` from it is exactly the false
|
|
450
|
+
// "we did not look here, but a synced file is fresh, so call it live" the
|
|
451
|
+
// ticket removes. Only a reading THIS box captured itself (statusline/poll)
|
|
452
|
+
// is admissible live evidence.
|
|
453
|
+
if (snapshot?.freshness?.source === 'sync')
|
|
454
|
+
return null;
|
|
440
455
|
const ageMin = Math.max(1, Math.round((now - capturedAt) / 60_000));
|
|
441
456
|
return { verdict: 'live', checkedAt: now, detail: `token proven live by a usage fetch ${ageMin}m ago` };
|
|
442
457
|
}
|
|
@@ -461,15 +476,21 @@ export async function probeAuthHealth(agent, home, opts) {
|
|
|
461
476
|
role: selfConfiguredDeviceRole(),
|
|
462
477
|
forceLive: opts?.forceLive,
|
|
463
478
|
})) {
|
|
479
|
+
// A worker's setup-token cannot read the usage endpoint (RUSH-2392), so this
|
|
480
|
+
// box has no probe evidence for the account. This is NOT `unverified` (which
|
|
481
|
+
// conflated it with codex/grok's permanent no-probe-endpoint state): it is
|
|
482
|
+
// "we did not look here". A worker never publishes it — probeLocalFleetAuth
|
|
483
|
+
// drops `no_evidence` like `unconfigured` — so the account's facts come from
|
|
484
|
+
// token presence + recorded run outcomes instead (PHNX-4116).
|
|
464
485
|
return {
|
|
465
|
-
verdict: '
|
|
486
|
+
verdict: 'no_evidence',
|
|
466
487
|
checkedAt,
|
|
467
488
|
detail: 'setup-token box does not probe the usage endpoint',
|
|
468
489
|
};
|
|
469
490
|
}
|
|
470
491
|
if (opts?.forceLive !== true && usageScope && !trySpendUsageApiCall(usageScope, agent, checkedAt)) {
|
|
471
492
|
return {
|
|
472
|
-
verdict: '
|
|
493
|
+
verdict: 'no_evidence',
|
|
473
494
|
checkedAt,
|
|
474
495
|
detail: 'usage-endpoint budget already spent this hour',
|
|
475
496
|
};
|
|
@@ -573,7 +594,12 @@ export async function probeLocalFleetAuth(opts) {
|
|
|
573
594
|
const health = await probeAuthHealth(rep.agent, rep.home, { cliVersion: opts?.cliVersion, info: rep.info, forceLive: opts?.forceLive, signal: opts?.signal });
|
|
574
595
|
health.account = authAccountLabel(rep.info);
|
|
575
596
|
health.accountId = rep.accountId;
|
|
576
|
-
|
|
597
|
+
health.source = 'probe';
|
|
598
|
+
// `unconfigured` (no credential) and `no_evidence` (a worker that cannot probe)
|
|
599
|
+
// both write NO row: the first has nothing to say, the second says only "we
|
|
600
|
+
// did not look", which is not a fleet-publishable verdict — the account's
|
|
601
|
+
// facts come from token presence + recorded run outcomes instead (PHNX-4116).
|
|
602
|
+
if (health.verdict === 'unconfigured' || health.verdict === 'no_evidence') {
|
|
577
603
|
perGroup.push([]);
|
|
578
604
|
}
|
|
579
605
|
else {
|
|
@@ -618,3 +644,106 @@ export function writeFleetAuthRows(host, rows, installed) {
|
|
|
618
644
|
: undefined;
|
|
619
645
|
writeAuthHealthEntries(entries, drop);
|
|
620
646
|
}
|
|
647
|
+
// ---------------------------------------------------------------------------
|
|
648
|
+
// Run-outcome recording + auth-fact rendering (PHNX-4116)
|
|
649
|
+
//
|
|
650
|
+
// A worker never probes, so the honest evidence that its token authenticates is
|
|
651
|
+
// a REAL run: a successful inference (`live`) or an auth failure. These rows are
|
|
652
|
+
// written with `source: 'run'` and read back as a FACT — `last used ok 12m ago`
|
|
653
|
+
// / `last auth failure 401 Sep 20 14:02` / `not used on this box yet` — instead
|
|
654
|
+
// of the misleading verdict word the ticket removes.
|
|
655
|
+
// ---------------------------------------------------------------------------
|
|
656
|
+
/**
|
|
657
|
+
* The installed-version label a run outcome falls back to when the account has no
|
|
658
|
+
* slot on this box: the explicit `version`, else the label derived from `home` (a
|
|
659
|
+
* version home is `<versionDir>/home`, so the label is its parent dir's basename).
|
|
660
|
+
* Null when neither resolves. Pure (no fs) so it is unit-tested directly.
|
|
661
|
+
*/
|
|
662
|
+
export function runOutcomeVersionKey(opts) {
|
|
663
|
+
if (opts.version)
|
|
664
|
+
return opts.version;
|
|
665
|
+
if (opts.home) {
|
|
666
|
+
const label = path.basename(path.dirname(opts.home));
|
|
667
|
+
if (label && label !== '.' && label !== path.sep)
|
|
668
|
+
return label;
|
|
669
|
+
}
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Record a REAL run's auth outcome into the auth-health cache with
|
|
674
|
+
* `source: 'run'`. Best-effort and never throws: an unattributable run (no
|
|
675
|
+
* accountId/version/home) writes nothing, and the underlying write already
|
|
676
|
+
* swallows IO errors. Keyed exactly as the catalog reads, so the recorded fact
|
|
677
|
+
* surfaces on the same account row.
|
|
678
|
+
*/
|
|
679
|
+
export function recordRunAuthOutcome(opts) {
|
|
680
|
+
// Key exactly as the account catalog READS (slot key first, then the version
|
|
681
|
+
// label) so the fact lands on the row: the slot key ONLY when a slot dir
|
|
682
|
+
// actually exists for this account on this box (workers), else the installed
|
|
683
|
+
// version label (headed native logins have no slot). A last-resort slot key
|
|
684
|
+
// keeps an attributable-by-id run recorded even with no version hint.
|
|
685
|
+
const slotKey = opts.accountId && readSlots(readMeta())[opts.accountId]
|
|
686
|
+
? slotAuthVersionKey(opts.accountId)
|
|
687
|
+
: null;
|
|
688
|
+
const versionKey = slotKey
|
|
689
|
+
?? runOutcomeVersionKey(opts)
|
|
690
|
+
?? (opts.accountId ? slotAuthVersionKey(opts.accountId) : null);
|
|
691
|
+
if (!versionKey)
|
|
692
|
+
return;
|
|
693
|
+
const checkedAt = opts.now ?? Date.now();
|
|
694
|
+
const host = opts.host ?? machineId();
|
|
695
|
+
let detail;
|
|
696
|
+
if (opts.outcome.ok) {
|
|
697
|
+
detail = 'run ok';
|
|
698
|
+
}
|
|
699
|
+
else if (opts.outcome.verdict === 'rate_limited' && opts.outcome.resetsAt) {
|
|
700
|
+
detail = `until ${factClock(opts.outcome.resetsAt)}`;
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
detail = opts.outcome.detail;
|
|
704
|
+
}
|
|
705
|
+
const health = {
|
|
706
|
+
verdict: opts.outcome.ok ? 'live' : opts.outcome.verdict,
|
|
707
|
+
checkedAt,
|
|
708
|
+
source: 'run',
|
|
709
|
+
...(detail ? { detail } : {}),
|
|
710
|
+
...(opts.account ? { account: opts.account } : {}),
|
|
711
|
+
...(opts.accountId ? { accountId: opts.accountId } : {}),
|
|
712
|
+
};
|
|
713
|
+
writeAuthHealthEntries({ [authCacheKey(host, opts.agent, versionKey)]: health });
|
|
714
|
+
}
|
|
715
|
+
const FACT_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
716
|
+
/** `HH:MM` local clock — used for a throttle reset time. */
|
|
717
|
+
function factClock(ms) {
|
|
718
|
+
const d = new Date(ms);
|
|
719
|
+
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
|
720
|
+
}
|
|
721
|
+
/** `Sep 20 14:02` — the fact timestamp for an auth failure. */
|
|
722
|
+
export function factTimestamp(ms) {
|
|
723
|
+
const d = new Date(ms);
|
|
724
|
+
return `${FACT_MONTHS[d.getMonth()]} ${d.getDate()} ${factClock(ms)}`;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* The auth FACT `agents view` / `agents accounts` render per account per box
|
|
728
|
+
* (PHNX-4116): what actually happened here, with its time — never a word that
|
|
729
|
+
* means "we did not look". A `live` row (a recorded run OR a real probe) reads
|
|
730
|
+
* `last used ok <age>`; a server rejection reads `last auth failure <detail>
|
|
731
|
+
* <time>`; a throttle reads `rate-limited <detail> (<time>)`; anything with no
|
|
732
|
+
* usable evidence — including `no_evidence`/`unverified` — reads `not used on
|
|
733
|
+
* this box yet`.
|
|
734
|
+
*/
|
|
735
|
+
export function formatAuthFact(health, now = Date.now()) {
|
|
736
|
+
if (!health)
|
|
737
|
+
return 'not used on this box yet';
|
|
738
|
+
switch (health.verdict) {
|
|
739
|
+
case 'live':
|
|
740
|
+
return `last used ok ${formatCheckedAge(health.checkedAt, now)}`;
|
|
741
|
+
case 'revoked':
|
|
742
|
+
case 'expired':
|
|
743
|
+
return `last auth failure${health.detail ? ` ${health.detail}` : ''} ${factTimestamp(health.checkedAt)}`;
|
|
744
|
+
case 'rate_limited':
|
|
745
|
+
return `rate-limited${health.detail ? ` ${health.detail}` : ''} (${factTimestamp(health.checkedAt)})`;
|
|
746
|
+
default:
|
|
747
|
+
return 'not used on this box yet';
|
|
748
|
+
}
|
|
749
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* context.ts — everything agents-cli knows that the standalone `browser` engine
|
|
3
|
+
* cannot work out for itself, serialized as one JSON object onto fd 3.
|
|
4
|
+
*
|
|
5
|
+
* This is the whole contract of the consumer half (browser-cli integration
|
|
6
|
+
* contract §1). The engine accepts exactly this shape:
|
|
7
|
+
*
|
|
8
|
+
* version `1`. The engine matches on it; a field added later must be
|
|
9
|
+
* optional so an older engine keeps working.
|
|
10
|
+
* target the `--device <name>` target, resolved against the fleet:
|
|
11
|
+
* device registry, ssh identity, platform. The engine matches
|
|
12
|
+
* its own `--device <alias>` against this instead of reading a
|
|
13
|
+
* registry it does not have, then falls back to `~/.ssh/config`.
|
|
14
|
+
* Absent for a local invocation.
|
|
15
|
+
* session who is acting — actor id and agent session — so an action
|
|
16
|
+
* lands in the right session history.
|
|
17
|
+
* remoteControl whether THIS machine consents to being driven by a peer.
|
|
18
|
+
* agents-cli owns the policy; browser-cli enforces the flag.
|
|
19
|
+
*
|
|
20
|
+
* WHAT IS DELIBERATELY NOT HERE. The CDP/BiDi/Arc endpoint, the IPC socket, the
|
|
21
|
+
* chrome-data store and the profile declarations are the ENGINE's: browser-cli
|
|
22
|
+
* keeps every on-disk path the in-repo subsystem used (integration contract §4)
|
|
23
|
+
* and hydrates its own transport from them. agents-cli publishing an endpoint
|
|
24
|
+
* here would be a second, drifting copy of that answer.
|
|
25
|
+
*
|
|
26
|
+
* The context is PUSHED (written and closed) rather than exposed as a callback,
|
|
27
|
+
* so the engine never re-enters agents-cli and there is exactly one direction of
|
|
28
|
+
* dependency.
|
|
29
|
+
*/
|
|
30
|
+
/** A `--device <name>` target, resolved against the fleet. */
|
|
31
|
+
export interface BrowserTargetContext {
|
|
32
|
+
/** The device name as the user typed it. */
|
|
33
|
+
alias: string;
|
|
34
|
+
/** `user@host`, already validated against ssh option injection. */
|
|
35
|
+
host: string;
|
|
36
|
+
user: string;
|
|
37
|
+
/** Bare host — the ssh-config Host name or address, without the user. */
|
|
38
|
+
hostname: string;
|
|
39
|
+
platform: string;
|
|
40
|
+
/** Per-device ssh identity flags, in argv order. Possibly empty. */
|
|
41
|
+
sshArgs: string[];
|
|
42
|
+
}
|
|
43
|
+
/** Who is acting, so the engine can stamp the action it reports back. */
|
|
44
|
+
interface BrowserSessionContext {
|
|
45
|
+
sessionId?: string;
|
|
46
|
+
launchId?: string;
|
|
47
|
+
actor: string;
|
|
48
|
+
}
|
|
49
|
+
interface BrowserContext {
|
|
50
|
+
version: 1;
|
|
51
|
+
target?: BrowserTargetContext;
|
|
52
|
+
session: BrowserSessionContext;
|
|
53
|
+
remoteControl: {
|
|
54
|
+
allowed: boolean;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Whether this machine allows other fleet machines to drive its browser. Reads
|
|
59
|
+
* the device-scope `browser.remote-control` config key. Unset = off (deny).
|
|
60
|
+
* browser-cli reads and writes the same key, so a `remote-control on` typed at
|
|
61
|
+
* either surface agrees.
|
|
62
|
+
*/
|
|
63
|
+
export declare function remoteControlEnabled(): boolean;
|
|
64
|
+
interface BuildContextOptions {
|
|
65
|
+
/** `--device <name>`, if given (only meaningful on `start`). */
|
|
66
|
+
device?: string;
|
|
67
|
+
/**
|
|
68
|
+
* A precomputed target, when the caller already resolved the device. When
|
|
69
|
+
* present, `device` need not be re-resolved.
|
|
70
|
+
*/
|
|
71
|
+
target?: BrowserTargetContext;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Resolve `--device <name>` to a fleet target and build the context handed to
|
|
75
|
+
* the engine on fd 3.
|
|
76
|
+
*
|
|
77
|
+
* Unlike `agents computer`, browser drives ANY platform, so `resolveRemoteDevice`
|
|
78
|
+
* is called with no platform expectation — a `--device` pointing at a Mac, Linux
|
|
79
|
+
* box or Windows host all resolve.
|
|
80
|
+
*/
|
|
81
|
+
export declare function buildBrowserContext(opts?: BuildContextOptions): Promise<BrowserContext>;
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* context.ts — everything agents-cli knows that the standalone `browser` engine
|
|
3
|
+
* cannot work out for itself, serialized as one JSON object onto fd 3.
|
|
4
|
+
*
|
|
5
|
+
* This is the whole contract of the consumer half (browser-cli integration
|
|
6
|
+
* contract §1). The engine accepts exactly this shape:
|
|
7
|
+
*
|
|
8
|
+
* version `1`. The engine matches on it; a field added later must be
|
|
9
|
+
* optional so an older engine keeps working.
|
|
10
|
+
* target the `--device <name>` target, resolved against the fleet:
|
|
11
|
+
* device registry, ssh identity, platform. The engine matches
|
|
12
|
+
* its own `--device <alias>` against this instead of reading a
|
|
13
|
+
* registry it does not have, then falls back to `~/.ssh/config`.
|
|
14
|
+
* Absent for a local invocation.
|
|
15
|
+
* session who is acting — actor id and agent session — so an action
|
|
16
|
+
* lands in the right session history.
|
|
17
|
+
* remoteControl whether THIS machine consents to being driven by a peer.
|
|
18
|
+
* agents-cli owns the policy; browser-cli enforces the flag.
|
|
19
|
+
*
|
|
20
|
+
* WHAT IS DELIBERATELY NOT HERE. The CDP/BiDi/Arc endpoint, the IPC socket, the
|
|
21
|
+
* chrome-data store and the profile declarations are the ENGINE's: browser-cli
|
|
22
|
+
* keeps every on-disk path the in-repo subsystem used (integration contract §4)
|
|
23
|
+
* and hydrates its own transport from them. agents-cli publishing an endpoint
|
|
24
|
+
* here would be a second, drifting copy of that answer.
|
|
25
|
+
*
|
|
26
|
+
* The context is PUSHED (written and closed) rather than exposed as a callback,
|
|
27
|
+
* so the engine never re-enters agents-cli and there is exactly one direction of
|
|
28
|
+
* dependency.
|
|
29
|
+
*/
|
|
30
|
+
import { resolveActor } from '../actor.js';
|
|
31
|
+
import { resolveRemoteDevice } from '../ssh-tunnel.js';
|
|
32
|
+
import { getConfigValue } from '../device-config.js';
|
|
33
|
+
/**
|
|
34
|
+
* Which agent session is acting. The harness-native id first, then agents' own —
|
|
35
|
+
* the same precedence `lib/computer/context.ts` uses.
|
|
36
|
+
*/
|
|
37
|
+
function agentSessionId(env = process.env) {
|
|
38
|
+
return env.CODEX_THREAD_ID
|
|
39
|
+
|| env.CLAUDE_CODE_SESSION_ID
|
|
40
|
+
|| env.CLAUDE_SESSION_ID
|
|
41
|
+
|| env.AGENTS_SESSION_ID
|
|
42
|
+
|| env.AGENT_SESSION_ID
|
|
43
|
+
|| env.AGENTS_RUN_ID
|
|
44
|
+
|| undefined;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether this machine allows other fleet machines to drive its browser. Reads
|
|
48
|
+
* the device-scope `browser.remote-control` config key. Unset = off (deny).
|
|
49
|
+
* browser-cli reads and writes the same key, so a `remote-control on` typed at
|
|
50
|
+
* either surface agrees.
|
|
51
|
+
*/
|
|
52
|
+
export function remoteControlEnabled() {
|
|
53
|
+
return getConfigValue('browser.remote-control').value === true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve `--device <name>` to a fleet target and build the context handed to
|
|
57
|
+
* the engine on fd 3.
|
|
58
|
+
*
|
|
59
|
+
* Unlike `agents computer`, browser drives ANY platform, so `resolveRemoteDevice`
|
|
60
|
+
* is called with no platform expectation — a `--device` pointing at a Mac, Linux
|
|
61
|
+
* box or Windows host all resolve.
|
|
62
|
+
*/
|
|
63
|
+
export async function buildBrowserContext(opts = {}) {
|
|
64
|
+
let target = opts.target;
|
|
65
|
+
if (!target && opts.device && opts.device !== 'local') {
|
|
66
|
+
const resolved = await resolveRemoteDevice(opts.device);
|
|
67
|
+
target = {
|
|
68
|
+
alias: opts.device,
|
|
69
|
+
host: resolved.target,
|
|
70
|
+
user: resolved.user,
|
|
71
|
+
hostname: resolved.host,
|
|
72
|
+
platform: resolved.device.platform,
|
|
73
|
+
sshArgs: resolved.identityArgs,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
version: 1,
|
|
78
|
+
// Spread rather than assigned: a local invocation must not ship a `target`
|
|
79
|
+
// key at all, so the engine never has to distinguish absent from null.
|
|
80
|
+
...(target ? { target } : {}),
|
|
81
|
+
session: {
|
|
82
|
+
sessionId: agentSessionId(),
|
|
83
|
+
launchId: process.env.AGENT_LAUNCH_ID,
|
|
84
|
+
actor: resolveActor().id,
|
|
85
|
+
},
|
|
86
|
+
remoteControl: { allowed: remoteControlEnabled() },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paths.ts — the on-disk browser layout the CONSUMER still reads.
|
|
3
|
+
*
|
|
4
|
+
* browser-cli owns the browser process, drivers and IPC, but it keeps every
|
|
5
|
+
* on-disk path the in-repo subsystem used (integration contract §4). So
|
|
6
|
+
* `agents browser sessions` (`sessions-list.ts`) and the feed's browser tool
|
|
7
|
+
* rows (`feed/tool-activity.ts`) read a profile's captures straight off disk —
|
|
8
|
+
* no daemon, no engine — and need just these path helpers. They were the pure
|
|
9
|
+
* path corner of the deleted `profiles.ts` / `runtime-state.ts` engine files.
|
|
10
|
+
*
|
|
11
|
+
* A profile's live tasks and captures may live under a COMPOSITE runtime dir
|
|
12
|
+
* (`<profile>@<endpoint>`, `<profile>@<endpoint>.<fork>`), not the bare
|
|
13
|
+
* `<profile>` dir, so `listProfileCacheDirs` matches every dir that belongs to a
|
|
14
|
+
* profile — the same last-`@` rule browser-cli names them with.
|
|
15
|
+
*/
|
|
16
|
+
export { getBrowserDurableDir } from '../state.js';
|
|
17
|
+
/** Root of the browser runtime cache — `~/.agents/.cache/browser/`. */
|
|
18
|
+
export declare function getBrowserRuntimeDir(): string;
|
|
19
|
+
/** The runtime cache dir for one profile's bare name. */
|
|
20
|
+
export declare function getProfileRuntimeDir(name: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Every runtime-cache dir that belongs to a profile, composite forms included.
|
|
23
|
+
* Empty when the root does not exist yet.
|
|
24
|
+
*/
|
|
25
|
+
export declare function listProfileCacheDirs(profileName: string): string[];
|