@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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Agent event stream parsers.
|
|
3
3
|
*
|
|
4
4
|
* Normalizes the heterogeneous JSON event formats emitted by each agent CLI
|
|
5
|
-
* (Claude, Codex,
|
|
5
|
+
* (Claude, Codex, Cursor, OpenCode, Grok, Antigravity, Kimi) into a unified
|
|
6
6
|
* event schema with consistent types: init, message, tool_use, bash,
|
|
7
7
|
* file_read, file_write, file_create, file_delete, result, error, and others.
|
|
8
8
|
*/
|
|
@@ -18,7 +18,6 @@ const droidToolUseMap = new Map();
|
|
|
18
18
|
const TEAM_EVENT_NORMALIZERS = {
|
|
19
19
|
codex: normalizeCodex,
|
|
20
20
|
cursor: normalizeCursor,
|
|
21
|
-
gemini: normalizeGemini,
|
|
22
21
|
claude: normalizeClaude,
|
|
23
22
|
opencode: normalizeOpencode,
|
|
24
23
|
grok: normalizeGrok,
|
|
@@ -415,140 +414,6 @@ function normalizeCursor(raw) {
|
|
|
415
414
|
timestamp: timestamp,
|
|
416
415
|
}];
|
|
417
416
|
}
|
|
418
|
-
function normalizeGemini(raw) {
|
|
419
|
-
if (!raw || typeof raw !== 'object') {
|
|
420
|
-
return [{
|
|
421
|
-
type: 'unknown',
|
|
422
|
-
agent: 'gemini',
|
|
423
|
-
raw: raw,
|
|
424
|
-
timestamp: new Date().toISOString(),
|
|
425
|
-
}];
|
|
426
|
-
}
|
|
427
|
-
const eventType = raw?.type || 'unknown';
|
|
428
|
-
const timestamp = raw?.timestamp || new Date().toISOString();
|
|
429
|
-
if (eventType === 'init') {
|
|
430
|
-
return [{
|
|
431
|
-
type: 'init',
|
|
432
|
-
agent: 'gemini',
|
|
433
|
-
model: raw?.model,
|
|
434
|
-
session_id: raw?.session_id,
|
|
435
|
-
timestamp: timestamp,
|
|
436
|
-
}];
|
|
437
|
-
}
|
|
438
|
-
else if (eventType === 'message') {
|
|
439
|
-
const role = raw?.role || 'assistant';
|
|
440
|
-
if (role === 'assistant') {
|
|
441
|
-
return [{
|
|
442
|
-
type: 'message',
|
|
443
|
-
agent: 'gemini',
|
|
444
|
-
content: raw?.content || '',
|
|
445
|
-
complete: !raw?.delta,
|
|
446
|
-
timestamp: timestamp,
|
|
447
|
-
}];
|
|
448
|
-
}
|
|
449
|
-
else {
|
|
450
|
-
return [{
|
|
451
|
-
type: 'user_message',
|
|
452
|
-
agent: 'gemini',
|
|
453
|
-
content: raw?.content || '',
|
|
454
|
-
timestamp: timestamp,
|
|
455
|
-
}];
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
else if (eventType === 'tool_call' || eventType === 'tool_use') {
|
|
459
|
-
const toolNameRaw = raw?.tool_name || raw?.name || 'unknown';
|
|
460
|
-
const toolName = String(toolNameRaw);
|
|
461
|
-
let toolArgsRaw = raw?.parameters;
|
|
462
|
-
if (toolArgsRaw === null || toolArgsRaw === undefined) {
|
|
463
|
-
toolArgsRaw = raw?.args;
|
|
464
|
-
}
|
|
465
|
-
const toolArgs = (typeof toolArgsRaw === 'object' && toolArgsRaw !== null) ? toolArgsRaw : {};
|
|
466
|
-
const toolNameLower = toolName.toLowerCase();
|
|
467
|
-
const filePath = toolArgs?.file_path || toolArgs?.path || '';
|
|
468
|
-
const command = toolArgs?.command || '';
|
|
469
|
-
// File write/edit tools - Gemini uses 'replace', 'edit', 'patch', 'write_file', etc.
|
|
470
|
-
const writeTools = ['replace', 'edit', 'patch', 'write_file', 'edit_file', 'update_file', 'modify_file'];
|
|
471
|
-
if (writeTools.includes(toolNameLower) || (toolNameLower.includes('write') && toolNameLower.includes('file'))) {
|
|
472
|
-
if (!filePath.trim()) {
|
|
473
|
-
return [];
|
|
474
|
-
}
|
|
475
|
-
return [{
|
|
476
|
-
type: 'file_write',
|
|
477
|
-
agent: 'gemini',
|
|
478
|
-
tool: toolName,
|
|
479
|
-
path: filePath,
|
|
480
|
-
timestamp: timestamp,
|
|
481
|
-
}];
|
|
482
|
-
}
|
|
483
|
-
// File read tools
|
|
484
|
-
const readTools = ['read_file', 'view_file', 'cat_file', 'get_file'];
|
|
485
|
-
if (readTools.includes(toolNameLower) || (toolNameLower.includes('read') && toolNameLower.includes('file'))) {
|
|
486
|
-
if (!filePath.trim()) {
|
|
487
|
-
return [];
|
|
488
|
-
}
|
|
489
|
-
return [{
|
|
490
|
-
type: 'file_read',
|
|
491
|
-
agent: 'gemini',
|
|
492
|
-
tool: toolName,
|
|
493
|
-
path: filePath,
|
|
494
|
-
timestamp: timestamp,
|
|
495
|
-
}];
|
|
496
|
-
}
|
|
497
|
-
// File delete tools
|
|
498
|
-
const deleteTools = ['delete_file', 'remove_file', 'rm_file'];
|
|
499
|
-
if (deleteTools.includes(toolNameLower) || (toolNameLower.includes('delete') && toolNameLower.includes('file'))) {
|
|
500
|
-
if (!filePath.trim()) {
|
|
501
|
-
return [];
|
|
502
|
-
}
|
|
503
|
-
return [{
|
|
504
|
-
type: 'file_delete',
|
|
505
|
-
agent: 'gemini',
|
|
506
|
-
tool: toolName,
|
|
507
|
-
path: filePath,
|
|
508
|
-
timestamp: timestamp,
|
|
509
|
-
}];
|
|
510
|
-
}
|
|
511
|
-
// Shell/bash tools
|
|
512
|
-
if (['shell', 'bash', 'execute', 'run_command', 'run_shell_command'].includes(toolNameLower)) {
|
|
513
|
-
if (!command.trim()) {
|
|
514
|
-
return [];
|
|
515
|
-
}
|
|
516
|
-
return [{
|
|
517
|
-
type: 'bash',
|
|
518
|
-
agent: 'gemini',
|
|
519
|
-
tool: toolName,
|
|
520
|
-
command: command,
|
|
521
|
-
timestamp: timestamp,
|
|
522
|
-
}];
|
|
523
|
-
}
|
|
524
|
-
return [{
|
|
525
|
-
type: 'tool_use',
|
|
526
|
-
agent: 'gemini',
|
|
527
|
-
tool: toolName,
|
|
528
|
-
args: toolArgs,
|
|
529
|
-
timestamp: timestamp,
|
|
530
|
-
}];
|
|
531
|
-
}
|
|
532
|
-
else if (eventType === 'result') {
|
|
533
|
-
const stats = raw?.stats || {};
|
|
534
|
-
return [{
|
|
535
|
-
type: 'result',
|
|
536
|
-
agent: 'gemini',
|
|
537
|
-
status: raw?.status || 'success',
|
|
538
|
-
duration_ms: stats?.duration_ms,
|
|
539
|
-
usage: {
|
|
540
|
-
total_tokens: stats?.total_tokens || 0,
|
|
541
|
-
},
|
|
542
|
-
timestamp: timestamp,
|
|
543
|
-
}];
|
|
544
|
-
}
|
|
545
|
-
return [{
|
|
546
|
-
type: eventType,
|
|
547
|
-
agent: 'gemini',
|
|
548
|
-
raw: raw,
|
|
549
|
-
timestamp: timestamp,
|
|
550
|
-
}];
|
|
551
|
-
}
|
|
552
417
|
function normalizeClaude(raw) {
|
|
553
418
|
const eventType = raw.type || 'unknown';
|
|
554
419
|
const subtype = raw.subtype;
|
|
@@ -16,12 +16,16 @@
|
|
|
16
16
|
* of the rank stays live (the pure pick recounts the roster each call), only the
|
|
17
17
|
* SSH-measured load/harness snapshot is reused within the TTL.
|
|
18
18
|
*
|
|
19
|
-
* All SSH here is via `
|
|
20
|
-
* parallel; a slow or wedged box degrades to "no signal" instead of blocking
|
|
21
|
-
* launch. The readiness payload is `agents view --json`, so remote and local
|
|
22
|
-
* candidates carry the same installed/sign-in verdict.
|
|
19
|
+
* All SSH here is via `spawn` (async, kill-bounded) so the whole pool is probed
|
|
20
|
+
* in parallel; a slow or wedged box degrades to "no signal" instead of blocking
|
|
21
|
+
* the launch. The readiness payload is `agents view --json`, so remote and local
|
|
22
|
+
* candidates carry the same installed/sign-in verdict. The dispatcher's usage
|
|
23
|
+
* envelope rides the same round-trip on stdin (`agents __usage-ingest` runs
|
|
24
|
+
* first in the probe, PHNX-4116), so the worker the pick lands on holds current
|
|
25
|
+
* usage numbers at dispatch rather than at the next usage-sync tick.
|
|
23
26
|
*/
|
|
24
|
-
import {
|
|
27
|
+
import { spawn } from 'child_process';
|
|
28
|
+
import { buildFleetStatePayload } from '../accounting/usage-sync.js';
|
|
25
29
|
import { probeFleetStats, headroom } from '../devices/health.js';
|
|
26
30
|
import { loadDevicesSync } from '../devices/registry.js';
|
|
27
31
|
import { buildSshInvocation, writeAskpassShim } from '../devices/connect.js';
|
|
@@ -51,34 +55,62 @@ export function clearPlacementSignalCache() {
|
|
|
51
55
|
* failure) — reachability is then left to {@link probeFleetStats}; `false` when
|
|
52
56
|
* the box answered but agents-cli or the agent is absent (a genuine can't-run).
|
|
53
57
|
*/
|
|
54
|
-
function probeRemoteReadiness(device, agent) {
|
|
58
|
+
function probeRemoteReadiness(device, agent, usagePayload) {
|
|
59
|
+
const unknown = { installed: undefined, signedIn: undefined, pickerEligible: undefined };
|
|
55
60
|
let args;
|
|
56
61
|
let env;
|
|
57
62
|
try {
|
|
58
63
|
const shim = writeAskpassShim();
|
|
59
|
-
const cmd = buildReadyProbeCommand(device.shell === 'powershell' ? 'windows' : undefined);
|
|
64
|
+
const cmd = buildReadyProbeCommand(device.shell === 'powershell' ? 'windows' : undefined, { ingestUsage: true });
|
|
60
65
|
// agentOnly: a read-only probe must never force a foreground Touch ID sheet
|
|
61
66
|
// on a password-auth device (mirrors probeDeviceStats in devices/health).
|
|
62
67
|
({ args, env } = buildSshInvocation(device, [cmd], shim, {}, { agentOnly: true }));
|
|
63
68
|
}
|
|
64
69
|
catch {
|
|
65
|
-
return Promise.resolve(
|
|
70
|
+
return Promise.resolve(unknown);
|
|
66
71
|
}
|
|
67
72
|
return new Promise((resolve) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
const child = spawn('ssh', args, {
|
|
74
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
75
|
+
env: { ...process.env, ...env },
|
|
76
|
+
windowsHide: true,
|
|
77
|
+
});
|
|
78
|
+
let stdout = '';
|
|
79
|
+
let settled = false;
|
|
80
|
+
const finish = (value) => {
|
|
81
|
+
if (settled)
|
|
82
|
+
return;
|
|
83
|
+
settled = true;
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
clearTimeout(killTimer);
|
|
86
|
+
resolve(value);
|
|
87
|
+
};
|
|
88
|
+
// A wedged box degrades to "no signal": SIGTERM at the budget, SIGKILL shortly
|
|
89
|
+
// after, so one dead peer can never hold the whole placement wave open.
|
|
90
|
+
const timer = setTimeout(() => child.kill('SIGTERM'), READY_PROBE_TIMEOUT_MS);
|
|
91
|
+
const killTimer = setTimeout(() => { if (!settled)
|
|
92
|
+
child.kill('SIGKILL'); }, READY_PROBE_TIMEOUT_MS + 250);
|
|
93
|
+
killTimer.unref?.();
|
|
94
|
+
child.stdout.setEncoding('utf-8');
|
|
95
|
+
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
|
96
|
+
child.stderr.resume();
|
|
97
|
+
child.on('error', () => finish(unknown));
|
|
98
|
+
child.on('close', (code) => {
|
|
99
|
+
if (code !== 0 || !stdout)
|
|
100
|
+
return finish(unknown);
|
|
71
101
|
const probe = parseReadyProbe(stdout);
|
|
72
102
|
if (!probe.reachable)
|
|
73
|
-
return
|
|
103
|
+
return finish(unknown);
|
|
74
104
|
if (!probe.version)
|
|
75
|
-
return
|
|
105
|
+
return finish({ installed: false, signedIn: false, pickerEligible: false });
|
|
76
106
|
const installed = viewHasAgent(probe.view, agent);
|
|
77
107
|
const eligibility = installed
|
|
78
108
|
? viewAgentAccountEligibility(probe.view, agent)
|
|
79
109
|
: { signedIn: false, pickerEligible: false };
|
|
80
|
-
|
|
110
|
+
finish({ installed, ...eligibility });
|
|
81
111
|
});
|
|
112
|
+
child.stdin.on('error', () => { });
|
|
113
|
+
child.stdin.end(usagePayload);
|
|
82
114
|
});
|
|
83
115
|
}
|
|
84
116
|
/**
|
|
@@ -107,6 +139,9 @@ export async function probePoolSignals(pool, agent, opts = {}) {
|
|
|
107
139
|
}
|
|
108
140
|
const selfProfile = profiles.find((d) => normalizeHost(d.name) === self);
|
|
109
141
|
const stats = await probeFleetStats(profiles, { selfName: selfProfile?.name });
|
|
142
|
+
// Built once per wave: this box's current usage rows (headed only — a worker
|
|
143
|
+
// dispatcher has none to give), ingested by every remote candidate it probes.
|
|
144
|
+
const usagePayload = JSON.stringify(buildFleetStatePayload({ usageOnly: true }));
|
|
110
145
|
const installed = new Map(agent
|
|
111
146
|
? await Promise.all(profiles.map(async (d) => {
|
|
112
147
|
const isSelf = normalizeHost(d.name) === self;
|
|
@@ -125,7 +160,7 @@ export async function probePoolSignals(pool, agent, opts = {}) {
|
|
|
125
160
|
pickerEligible: readiness.some((candidate) => candidate.ready || isSignInRecoverable(candidate)),
|
|
126
161
|
}];
|
|
127
162
|
}
|
|
128
|
-
return [d.name, await probeRemoteReadiness(d, agent)];
|
|
163
|
+
return [d.name, await probeRemoteReadiness(d, agent, usagePayload)];
|
|
129
164
|
}))
|
|
130
165
|
: []);
|
|
131
166
|
const signals = new Map();
|
|
@@ -29,6 +29,13 @@ export interface DevicePlacementSignal {
|
|
|
29
29
|
signedIn?: boolean;
|
|
30
30
|
/** Requested agent has at least one row the interactive account picker can launch. */
|
|
31
31
|
pickerEligible?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The box's OWN aggregate readiness reason when it has no ready account
|
|
34
|
+
* (`runReady.reason`, e.g. `all signed_out`), so the placement error names why
|
|
35
|
+
* the remote turned itself away rather than a bare "no ready harness account"
|
|
36
|
+
* (PHNX-4116). Undefined when ready or when the remote gave no reason.
|
|
37
|
+
*/
|
|
38
|
+
reason?: string;
|
|
32
39
|
}
|
|
33
40
|
/** Team fields the placement cascade reads (a subset of TeamMeta). */
|
|
34
41
|
interface PlacementTeam {
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import type { CloudProviderId } from './cloud/types.js';
|
|
9
9
|
import type { FeedBroadcastConfig } from './feed-broadcast.js';
|
|
10
10
|
/** Unique identifier for a current or legacy AI coding agent. */
|
|
11
|
-
export declare const AGENT_IDS: readonly ["claude", "codex", "
|
|
11
|
+
export declare const AGENT_IDS: readonly ["claude", "codex", "cursor", "opencode", "openclaw", "copilot", "amp", "goose", "antigravity", "grok", "kimi", "droid", "hermes", "muse", "warp"];
|
|
12
12
|
export type AgentId = typeof AGENT_IDS[number];
|
|
13
13
|
export declare function isAgentId(value: string): value is AgentId;
|
|
14
14
|
/**
|
|
@@ -190,6 +190,21 @@ export interface AgentConfig {
|
|
|
190
190
|
variableSyntax: string;
|
|
191
191
|
supportsHooks: boolean;
|
|
192
192
|
nativeAgentsSkillsDir?: boolean;
|
|
193
|
+
/**
|
|
194
|
+
* The harness loads a plugin's bundled skills itself, namespaced
|
|
195
|
+
* `<plugin>:<skill>`, from the plugin registration sync writes. Sync must
|
|
196
|
+
* then NOT also flatten them into top-level `skills/<name>/`: the flat copy
|
|
197
|
+
* is a second, un-namespaced registration of the same skill, so every
|
|
198
|
+
* plugin skill showed twice (`/continue` and `/sessions:continue`). Harnesses
|
|
199
|
+
* without this flag only see plugin skills through the flattened copies.
|
|
200
|
+
*/
|
|
201
|
+
nativePluginSkills?: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Directories under the version home's `skills/` that the harness itself
|
|
204
|
+
* writes (Claude Code's `synced` bucket). The sync never placed them, so the
|
|
205
|
+
* full-sync orphan sweep and skill cleanup must never remove them.
|
|
206
|
+
*/
|
|
207
|
+
ownedSkillDirs?: readonly string[];
|
|
193
208
|
/**
|
|
194
209
|
* This agent's *own* cloud backend. `agents cloud run --agent <id>` routes
|
|
195
210
|
* here when no `--provider` is given (precedence: --provider > this >
|
package/dist/lib/types.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* formats for each supported agent.
|
|
7
7
|
*/
|
|
8
8
|
/** Unique identifier for a current or legacy AI coding agent. */
|
|
9
|
-
export const AGENT_IDS = ['claude', 'codex', '
|
|
9
|
+
export const AGENT_IDS = ['claude', 'codex', 'cursor', 'opencode', 'openclaw', 'copilot', 'amp', 'goose', 'antigravity', 'grok', 'kimi', 'droid', 'hermes', 'muse', 'warp'];
|
|
10
10
|
export function isAgentId(value) {
|
|
11
11
|
return AGENT_IDS.includes(value);
|
|
12
12
|
}
|
package/dist/lib/view-types.d.ts
CHANGED
|
@@ -66,6 +66,33 @@ export interface ViewJsonVersion {
|
|
|
66
66
|
} | null;
|
|
67
67
|
resources?: VersionResourcesJson;
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Whether a `run` on THIS box would find a launch-ready account for the agent,
|
|
71
|
+
* computed from the SAME enumeration the local router uses
|
|
72
|
+
* (`collectRunCandidates` → `readinessFromCandidate`), including native slots
|
|
73
|
+
* (`fromSlot`) — NOT the per-version `versions[]` list, which enumerates version
|
|
74
|
+
* homes and misses the account slots a run actually picks from (PHNX-4116). This
|
|
75
|
+
* is the one readiness gate: `agents run --device auto` reads THIS answer off
|
|
76
|
+
* `agents view --json` rather than re-deriving freshness on the dispatching box,
|
|
77
|
+
* so the box that runs and the box that dispatches can never disagree.
|
|
78
|
+
*/
|
|
79
|
+
export interface ViewJsonRunReady {
|
|
80
|
+
/** At least one account is launch-ready right now. */
|
|
81
|
+
ready: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* When ready, names a ready account (`ready (work)`); when not, the aggregate
|
|
84
|
+
* exclusion reason the dispatcher surfaces (`all signed_out`, `no accounts`, or
|
|
85
|
+
* a comma list of distinct reasons).
|
|
86
|
+
*/
|
|
87
|
+
reason: string;
|
|
88
|
+
/** Per-account verdict; `reason` is `'ready'` for a ready account, else the
|
|
89
|
+
* `readinessFromCandidate` reason (`signed_out`/`revoked`/`rate_limited`/…). */
|
|
90
|
+
accounts: Array<{
|
|
91
|
+
name: string;
|
|
92
|
+
ready: boolean;
|
|
93
|
+
reason: string;
|
|
94
|
+
}>;
|
|
95
|
+
}
|
|
69
96
|
export interface ViewJsonAgent {
|
|
70
97
|
agent: AgentId;
|
|
71
98
|
versions: ViewJsonVersion[];
|
|
@@ -74,6 +101,11 @@ export interface ViewJsonAgent {
|
|
|
74
101
|
* never the internal catalog row, so consumers (AGI EXT) read one shape.
|
|
75
102
|
*/
|
|
76
103
|
accounts?: AccountListEntryJson[];
|
|
104
|
+
/**
|
|
105
|
+
* The one run-readiness gate for this agent on this box (PHNX-4116). Absent on
|
|
106
|
+
* an older remote CLI, whose readers fall back to per-version `launchable`.
|
|
107
|
+
*/
|
|
108
|
+
runReady?: ViewJsonRunReady;
|
|
77
109
|
harnesses: ProfileSummary[];
|
|
78
110
|
}
|
|
79
111
|
export type ResourceSection = 'commands' | 'skills' | 'mcp' | 'memory' | 'hooks' | 'workflows' | 'plugins';
|
|
@@ -35,7 +35,6 @@ const WATCHDOG_SESSION_LAYOUT = {
|
|
|
35
35
|
claude: { subdir: 'projects', ext: '.jsonl' },
|
|
36
36
|
codex: { subdir: 'sessions', ext: '.jsonl' },
|
|
37
37
|
droid: { subdir: 'sessions', ext: '.jsonl' },
|
|
38
|
-
gemini: { subdir: 'tmp', ext: '.json' },
|
|
39
38
|
};
|
|
40
39
|
const WATCHDOG_SESSION_LAYOUT_DEFAULT = { subdir: 'projects', ext: '.jsonl' };
|
|
41
40
|
/**
|
|
@@ -145,7 +145,6 @@ function nextOccurrenceMs(hour, minute, timeZone, nowMs) {
|
|
|
145
145
|
export const ROTATE_EXIT_SEQUENCES = {
|
|
146
146
|
claude: ['\x1b', '\x03', '\x03'], // Esc, Ctrl+C, Ctrl+C (Esc first for Claude)
|
|
147
147
|
codex: ['\x03', '\x03'], // Ctrl+C twice
|
|
148
|
-
gemini: ['\x03', '\x03'],
|
|
149
148
|
cursor: ['\x03', '\x03'],
|
|
150
149
|
opencode: ['\x03', '\x03'],
|
|
151
150
|
};
|
|
@@ -314,7 +313,7 @@ export async function defaultRotateGate() {
|
|
|
314
313
|
}
|
|
315
314
|
// --- readiness --------------------------------------------------------------------
|
|
316
315
|
/** Transcript layouts to probe for the new session (mirrors read.ts's table). */
|
|
317
|
-
const ROTATE_TRANSCRIPT_AGENTS = ['claude', 'codex', '
|
|
316
|
+
const ROTATE_TRANSCRIPT_AGENTS = ['claude', 'codex', 'droid'];
|
|
318
317
|
/**
|
|
319
318
|
* Default TUI-liveness probe for the relaunched session: the new session's
|
|
320
319
|
* transcript resolves under any known harness layout. `--session-id` is honored
|
|
@@ -379,7 +379,7 @@ export async function runWatchdogTick(opts = {}) {
|
|
|
379
379
|
session,
|
|
380
380
|
candidate: {
|
|
381
381
|
terminalId: sid,
|
|
382
|
-
agentType: session.kind === 'codex'
|
|
382
|
+
agentType: session.kind === 'codex' ? 'codex' : 'claude',
|
|
383
383
|
tailLines: tail,
|
|
384
384
|
stalledForMs: st.stalledForMs,
|
|
385
385
|
task: session.topic ?? session.label ?? session.name,
|
|
@@ -502,7 +502,7 @@ export async function runWatchdogTick(opts = {}) {
|
|
|
502
502
|
const tailLines = tailCache.get(session.sessionId) ?? tailFor(session);
|
|
503
503
|
const candidate = {
|
|
504
504
|
terminalId: session.sessionId,
|
|
505
|
-
agentType: (session.kind === 'codex'
|
|
505
|
+
agentType: (session.kind === 'codex' ? 'codex' : 'claude'),
|
|
506
506
|
tailLines,
|
|
507
507
|
stalledForMs: status.stalledForMs,
|
|
508
508
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export interface WatchdogCandidate {
|
|
2
2
|
terminalId: string;
|
|
3
|
-
agentType: 'claude' | 'codex'
|
|
3
|
+
agentType: 'claude' | 'codex';
|
|
4
4
|
tailLines: string[];
|
|
5
5
|
stalledForMs: number;
|
|
6
6
|
/** The originating task / first prompt / topic — so the agent can judge "was given a task but hasn't finished it". */
|
|
@@ -32,13 +32,6 @@ export function summarizeWatchdogTail(tailLines, agentType) {
|
|
|
32
32
|
else if (claim?.role === 'assistant' && !lastAssistant)
|
|
33
33
|
lastAssistant = claim.text;
|
|
34
34
|
}
|
|
35
|
-
else if (agentType === 'gemini') {
|
|
36
|
-
const claim = readGemini(raw);
|
|
37
|
-
if (claim?.role === 'user' && !lastUser)
|
|
38
|
-
lastUser = claim.text;
|
|
39
|
-
else if (claim?.role === 'assistant' && !lastAssistant)
|
|
40
|
-
lastAssistant = claim.text;
|
|
41
|
-
}
|
|
42
35
|
}
|
|
43
36
|
return {
|
|
44
37
|
lastUserMessage: clip(lastUser),
|
|
@@ -71,30 +64,6 @@ function readCodex(raw) {
|
|
|
71
64
|
return null;
|
|
72
65
|
return { role, text };
|
|
73
66
|
}
|
|
74
|
-
// Gemini stores chats as a single pretty-printed JSON document — readTailLines
|
|
75
|
-
// returns partial fragments that JSON.parse rejects. So in practice this
|
|
76
|
-
// branch only fires for the JSONL-style events that some Gemini wrappers emit
|
|
77
|
-
// during a live session. Acceptable: the result is an empty summary, which
|
|
78
|
-
// the UI handles gracefully.
|
|
79
|
-
function readGemini(raw) {
|
|
80
|
-
const t = raw.type;
|
|
81
|
-
if (t === 'user_message') {
|
|
82
|
-
const text = typeof raw.text === 'string' ? raw.text : null;
|
|
83
|
-
return text ? { role: 'user', text } : null;
|
|
84
|
-
}
|
|
85
|
-
if (t === 'agent_message' || t === 'model_message') {
|
|
86
|
-
const text = typeof raw.text === 'string' ? raw.text : null;
|
|
87
|
-
return text ? { role: 'assistant', text } : null;
|
|
88
|
-
}
|
|
89
|
-
if (t === 'message') {
|
|
90
|
-
const role = raw.role === 'user' ? 'user' : raw.role === 'assistant' ? 'assistant' : null;
|
|
91
|
-
if (!role)
|
|
92
|
-
return null;
|
|
93
|
-
const text = typeof raw.text === 'string' ? raw.text : pickContentText(raw.content);
|
|
94
|
-
return text ? { role, text } : null;
|
|
95
|
-
}
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
67
|
function pickContentText(content) {
|
|
99
68
|
if (typeof content === 'string')
|
|
100
69
|
return isSyntheticTagged(content) ? null : content;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.119",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/scripts/postinstall.js
CHANGED
|
@@ -50,16 +50,16 @@ function shellQuote(value) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Shorthands that delegate to the installed agents-cli entrypoint.
|
|
53
|
-
const ALIASES = ['
|
|
53
|
+
const ALIASES = ['teams'];
|
|
54
54
|
|
|
55
55
|
// Aliases this script USED to write and must now remove on every install.
|
|
56
|
-
// `secrets` (PHNX-3989), `sessions` (PHNX-4012),
|
|
57
|
-
// names belong to standalone CLIs now (`pty` ->
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
// PATH — the 1.22.85 fork
|
|
61
|
-
// runs, so it prunes here.
|
|
62
|
-
const RETIRED_ALIASES = ['secrets', 'sessions', 'pty'];
|
|
56
|
+
// `secrets` (PHNX-3989), `sessions` (PHNX-4012), `pty` (PHNX-4091), and
|
|
57
|
+
// `browser` (PHNX-4101): those names belong to standalone CLIs now (`pty` ->
|
|
58
|
+
// `@phnx-labs/term-cli`'s `term`; `browser` -> `@phnx-labs/browser-cli`). An
|
|
59
|
+
// alias shim that `exec`s `agents <name>` re-enters the passthrough and shadows
|
|
60
|
+
// the real binary because the shims dir sits first on PATH — the 1.22.85 fork
|
|
61
|
+
// bomb. Postinstall is the upgrade path that actually runs, so it prunes here.
|
|
62
|
+
const RETIRED_ALIASES = ['secrets', 'sessions', 'pty', 'browser'];
|
|
63
63
|
|
|
64
64
|
function removeRetiredAliasShims() {
|
|
65
65
|
for (const name of RETIRED_ALIASES) {
|
package/dist/browser.d.ts
DELETED
package/dist/browser.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import { registerBrowserSubcommands } from './commands/browser.js';
|
|
4
|
-
import { maybeRunStandaloneOnHost } from './lib/hosts/passthrough.js';
|
|
5
|
-
async function main() {
|
|
6
|
-
// `browser … --device <box>` routes to a remote over SSH, exactly like
|
|
7
|
-
// `agents browser … --device <box>` does through index.ts. Standalone-only:
|
|
8
|
-
// this binary never enters index.ts, so without this the flag was dropped.
|
|
9
|
-
if (await maybeRunStandaloneOnHost('browser')) {
|
|
10
|
-
process.exit(process.exitCode ?? 0);
|
|
11
|
-
}
|
|
12
|
-
const program = new Command();
|
|
13
|
-
program.name('browser').description('Browser automation via CDP');
|
|
14
|
-
registerBrowserSubcommands(program);
|
|
15
|
-
program.parse();
|
|
16
|
-
}
|
|
17
|
-
void main();
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { ProfileStatus, TaskStatus } from '../lib/browser/types.js';
|
|
2
|
-
export interface BrowserTask {
|
|
3
|
-
task: TaskStatus;
|
|
4
|
-
profile: ProfileStatus;
|
|
5
|
-
}
|
|
6
|
-
interface PickedBrowserTask {
|
|
7
|
-
task: BrowserTask;
|
|
8
|
-
action: 'view' | 'stop';
|
|
9
|
-
}
|
|
10
|
-
interface BrowserPickerConfig {
|
|
11
|
-
message: string;
|
|
12
|
-
tasks: BrowserTask[];
|
|
13
|
-
pageSize?: number;
|
|
14
|
-
initialSearch?: string;
|
|
15
|
-
}
|
|
16
|
-
/** Show an interactive browser task picker. */
|
|
17
|
-
export declare function browserTaskPicker(config: BrowserPickerConfig): Promise<PickedBrowserTask | null>;
|
|
18
|
-
export {};
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive browser task picker with preview.
|
|
3
|
-
*
|
|
4
|
-
* Powers the fuzzy-searchable task list shown by `agents browser status` in a TTY.
|
|
5
|
-
* Shows task name, profile, domains, and tab count; preview pane shows full tab list.
|
|
6
|
-
*/
|
|
7
|
-
import chalk from 'chalk';
|
|
8
|
-
import { truncate, humanDuration } from '../lib/format.js';
|
|
9
|
-
import { itemPicker } from '../lib/picker.js';
|
|
10
|
-
const DOT = chalk.gray(' · ');
|
|
11
|
-
function formatAge(ms) {
|
|
12
|
-
return humanDuration(Date.now() - ms) + ' ago';
|
|
13
|
-
}
|
|
14
|
-
/** Build the preview pane for a browser task. */
|
|
15
|
-
function buildBrowserPreview(item) {
|
|
16
|
-
const { task, profile } = item;
|
|
17
|
-
const lines = [];
|
|
18
|
-
// Line 1: Task name (ID: xxx)
|
|
19
|
-
lines.push(chalk.bold.white(`Task: ${task.name}`) + chalk.gray(` (${task.id})`));
|
|
20
|
-
// Line 2: Profile info
|
|
21
|
-
const profileParts = [chalk.cyan(profile.name)];
|
|
22
|
-
if (profile.port)
|
|
23
|
-
profileParts.push(`port ${profile.port}`);
|
|
24
|
-
if (profile.pid)
|
|
25
|
-
profileParts.push(`pid ${profile.pid}`);
|
|
26
|
-
lines.push(chalk.gray('Profile: ') + profileParts.join(DOT));
|
|
27
|
-
// Line 3: Timing
|
|
28
|
-
const started = formatAge(task.createdAt);
|
|
29
|
-
const duration = humanDuration(Date.now() - task.createdAt);
|
|
30
|
-
lines.push(chalk.gray('Started: ') + chalk.white(started) + DOT + chalk.gray('Duration: ') + chalk.white(duration));
|
|
31
|
-
// Blank line
|
|
32
|
-
lines.push('');
|
|
33
|
-
// Tabs section
|
|
34
|
-
if (task.tabs && task.tabs.length > 0) {
|
|
35
|
-
lines.push(chalk.gray('Tabs:'));
|
|
36
|
-
const termWidth = process.stdout.columns || 80;
|
|
37
|
-
const urlMax = Math.max(30, termWidth - 16);
|
|
38
|
-
for (const tab of task.tabs) {
|
|
39
|
-
const marker = tab.current ? chalk.yellow('*') : ' ';
|
|
40
|
-
const id = chalk.gray(tab.id);
|
|
41
|
-
const url = truncate(tab.url, urlMax);
|
|
42
|
-
lines.push(` ${id} ${marker} ${chalk.white(url)}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
lines.push(chalk.gray('No tabs open'));
|
|
47
|
-
}
|
|
48
|
-
return lines.join('\n');
|
|
49
|
-
}
|
|
50
|
-
/** Build the list label for a browser task. */
|
|
51
|
-
function buildBrowserLabel(item, query) {
|
|
52
|
-
const { task, profile } = item;
|
|
53
|
-
const termWidth = process.stdout.columns || 80;
|
|
54
|
-
// Format: name profile tabs domains age
|
|
55
|
-
const name = task.name.padEnd(20);
|
|
56
|
-
const profileName = profile.name.padEnd(12);
|
|
57
|
-
const tabs = `${task.tabCount} tab${task.tabCount === 1 ? '' : 's'}`.padEnd(8);
|
|
58
|
-
const age = formatAge(task.createdAt);
|
|
59
|
-
// Domains - fill remaining space
|
|
60
|
-
const fixedWidth = 20 + 12 + 8 + 10; // name + profile + tabs + age
|
|
61
|
-
const domainsWidth = Math.max(10, termWidth - fixedWidth - 4);
|
|
62
|
-
const domains = task.domains?.length
|
|
63
|
-
? truncate(task.domains.join(', '), domainsWidth)
|
|
64
|
-
: chalk.gray('no sites');
|
|
65
|
-
// Highlight query matches
|
|
66
|
-
let label = `${name}${chalk.cyan(profileName)}${tabs}${domains.padEnd(domainsWidth)}${chalk.gray(age)}`;
|
|
67
|
-
if (query) {
|
|
68
|
-
const re = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
69
|
-
label = label.replace(re, chalk.yellow.bold('$1'));
|
|
70
|
-
}
|
|
71
|
-
return label;
|
|
72
|
-
}
|
|
73
|
-
/** Show an interactive browser task picker. */
|
|
74
|
-
export async function browserTaskPicker(config) {
|
|
75
|
-
const picked = await itemPicker({
|
|
76
|
-
message: config.message,
|
|
77
|
-
items: config.tasks,
|
|
78
|
-
filter: (query) => {
|
|
79
|
-
if (!query)
|
|
80
|
-
return config.tasks;
|
|
81
|
-
const q = query.toLowerCase();
|
|
82
|
-
return config.tasks.filter((t) => t.task.name.toLowerCase().includes(q) ||
|
|
83
|
-
t.profile.name.toLowerCase().includes(q) ||
|
|
84
|
-
t.task.domains?.some((d) => d.toLowerCase().includes(q)));
|
|
85
|
-
},
|
|
86
|
-
labelFor: buildBrowserLabel,
|
|
87
|
-
buildPreview: buildBrowserPreview,
|
|
88
|
-
shortIdFor: (t) => t.task.name,
|
|
89
|
-
pageSize: config.pageSize ?? 10,
|
|
90
|
-
initialSearch: config.initialSearch,
|
|
91
|
-
emptyMessage: 'No browser tasks running.',
|
|
92
|
-
enterHint: 'view tabs',
|
|
93
|
-
});
|
|
94
|
-
if (!picked)
|
|
95
|
-
return null;
|
|
96
|
-
return { task: picked.item, action: 'view' };
|
|
97
|
-
}
|