@phnx-labs/agents-cli 1.14.7 → 1.16.0
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 +78 -39
- package/README.md +74 -7
- package/dist/commands/alias.js +2 -2
- package/dist/commands/beta.js +6 -1
- package/dist/commands/browser-picker.d.ts +21 -0
- package/dist/commands/browser-picker.js +114 -0
- package/dist/commands/browser.js +546 -75
- package/dist/commands/commands.js +72 -22
- package/dist/commands/daemon.js +2 -2
- package/dist/commands/exec.js +9 -2
- package/dist/commands/fork.js +2 -2
- package/dist/commands/hooks.js +71 -26
- package/dist/commands/mcp.js +85 -43
- package/dist/commands/plugins.js +48 -15
- package/dist/commands/prune.d.ts +0 -20
- package/dist/commands/prune.js +291 -16
- package/dist/commands/pull.js +3 -3
- package/dist/commands/repo.js +1 -1
- package/dist/commands/routines.js +2 -2
- package/dist/commands/secrets.js +37 -1
- package/dist/commands/sessions.js +62 -19
- package/dist/commands/{init.d.ts → setup.d.ts} +7 -6
- package/dist/commands/{init.js → setup.js} +32 -21
- package/dist/commands/skills.js +60 -19
- package/dist/commands/subagents.js +41 -13
- package/dist/commands/teams.js +2 -3
- package/dist/commands/usage.js +6 -0
- package/dist/commands/utils.d.ts +16 -0
- package/dist/commands/utils.js +32 -0
- package/dist/commands/versions.js +8 -6
- package/dist/commands/view.js +61 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.js +17 -20
- package/dist/lib/agents.js +2 -2
- package/dist/lib/auto-pull-worker.js +2 -3
- package/dist/lib/auto-pull.js +2 -2
- package/dist/lib/browser/cdp.d.ts +7 -1
- package/dist/lib/browser/cdp.js +29 -1
- package/dist/lib/browser/chrome.js +6 -3
- package/dist/lib/browser/devices.d.ts +4 -0
- package/dist/lib/browser/devices.js +27 -0
- package/dist/lib/browser/drivers/local.js +9 -4
- package/dist/lib/browser/drivers/ssh.d.ts +1 -0
- package/dist/lib/browser/drivers/ssh.js +32 -4
- package/dist/lib/browser/ipc.js +145 -23
- package/dist/lib/browser/profiles.d.ts +5 -2
- package/dist/lib/browser/profiles.js +77 -37
- package/dist/lib/browser/service.d.ts +84 -13
- package/dist/lib/browser/service.js +806 -122
- package/dist/lib/browser/types.d.ts +81 -3
- package/dist/lib/browser/types.js +16 -0
- package/dist/lib/cloud/rush.js +2 -2
- package/dist/lib/cloud/store.js +2 -2
- package/dist/lib/commands.d.ts +1 -0
- package/dist/lib/commands.js +6 -2
- package/dist/lib/daemon.js +6 -7
- package/dist/lib/doctor-diff.js +4 -4
- package/dist/lib/events.d.ts +94 -1
- package/dist/lib/events.js +264 -6
- package/dist/lib/exec.js +16 -10
- package/dist/lib/hooks.d.ts +11 -7
- package/dist/lib/hooks.js +125 -49
- package/dist/lib/migrate.d.ts +1 -1
- package/dist/lib/migrate.js +1178 -21
- package/dist/lib/models.js +2 -2
- package/dist/lib/permissions.d.ts +14 -11
- package/dist/lib/permissions.js +46 -42
- package/dist/lib/plugins.d.ts +30 -1
- package/dist/lib/plugins.js +75 -3
- package/dist/lib/pty-server.js +9 -10
- package/dist/lib/resources/hooks.d.ts +5 -1
- package/dist/lib/resources/hooks.js +21 -4
- package/dist/lib/rotate.js +3 -4
- package/dist/lib/routines.d.ts +15 -0
- package/dist/lib/routines.js +68 -0
- package/dist/lib/runner.js +9 -5
- package/dist/lib/secrets/index.d.ts +14 -11
- package/dist/lib/secrets/index.js +49 -21
- package/dist/lib/secrets/linux.d.ts +27 -0
- package/dist/lib/secrets/linux.js +161 -0
- package/dist/lib/session/active.d.ts +3 -0
- package/dist/lib/session/active.js +92 -6
- package/dist/lib/session/cloud.js +2 -2
- package/dist/lib/session/db.d.ts +4 -0
- package/dist/lib/session/db.js +34 -3
- package/dist/lib/session/discover.js +30 -15
- package/dist/lib/session/team-filter.js +2 -2
- package/dist/lib/shims.d.ts +2 -2
- package/dist/lib/shims.js +6 -6
- package/dist/lib/skills.js +6 -2
- package/dist/lib/state.d.ts +86 -14
- package/dist/lib/state.js +150 -23
- package/dist/lib/subagents.d.ts +28 -0
- package/dist/lib/subagents.js +98 -1
- package/dist/lib/sync-manifest.d.ts +1 -1
- package/dist/lib/sync-manifest.js +3 -3
- package/dist/lib/teams/persistence.js +15 -5
- package/dist/lib/teams/registry.js +2 -2
- package/dist/lib/types.d.ts +32 -3
- package/dist/lib/types.js +3 -3
- package/dist/lib/usage.d.ts +1 -1
- package/dist/lib/usage.js +15 -48
- package/dist/lib/versions.js +31 -21
- package/package.json +1 -1
- package/scripts/postinstall.js +37 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,62 +1,101 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.
|
|
3
|
+
## 1.16.0
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**System-repo sweep: ~/.agents-system reduced to npm-shipped defaults only**
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
7
|
+
- New migrators move every form of operational state out of ~/.agents-system into user-side buckets: sessions, teams (live + per-run), trash, repos (→ ~/.agents-<alias>/ peer dirs), legacy swarm/, cache/, cloud/.
|
|
8
|
+
- SQLite DBs merge row-level (INSERT OR IGNORE) into the user-side DB; filesystem dirs merge dir-by-dir with user-side winning on collision.
|
|
9
|
+
- Dead artifacts dropped automatically: bin/agents-keychain-*, empty shims/, .DS_Store-only versions/ skeletons.
|
|
10
|
+
- Unrecognized leftover dirs print a one-line stderr warning so future drift surfaces immediately.
|
|
11
|
+
- Migration diagnostics moved to stderr — `eval "$(agents secrets export …)"` stops being polluted by log lines.
|
|
12
|
+
- DB merge now skips FTS5 virtual + shadow tables (previously corrupted the session_text index). Indexer re-populates FTS on the next scan.
|
|
13
|
+
- Stale ~/.agents-system/agents.yaml is now dropped when a user copy exists.
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
**~/.agents split into .history/ and .cache/ buckets**
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
- Durable runtime state (sessions, versions, runs, teams/agents, trash, backups) moves to ~/.agents/.history/.
|
|
18
|
+
- Regenerable runtime state (shims, packages, cloud, logs, swarmify, helpers, browser runtime, fetch cache, dot-files) moves to ~/.agents/.cache/.
|
|
19
|
+
- Single-line gitignore for backing up ~/.agents/ — no more per-subdir cherry-picking.
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
+
**Browser: profiles fold into agents.yaml + many new automation commands**
|
|
22
|
+
|
|
23
|
+
- Profile YAMLs at ~/.agents/browser/profiles/*.yaml now live as a `browser:` section in agents.yaml. Single user-facing file, single sync.
|
|
24
|
+
- Single window per profile; `start` renamed to `open`; new tab subcommands; session history with profile picker; viewport piped through to the launched browser.
|
|
25
|
+
- New commands: `agents browser set viewport`, `set device`, `devices`, `console`, `errors`, `requests`, `responsebody`, `wait`, `download`, `waitdownload`.
|
|
26
|
+
|
|
27
|
+
**Hooks: hooks.yaml folded into agents.yaml `hooks:` section**
|
|
28
|
+
|
|
29
|
+
- ~/.agents/hooks.yaml is migrated into agents.yaml on first run; the standalone file is removed.
|
|
30
|
+
- System repo ships the same shape — one config file, layered project > user > system.
|
|
31
|
+
|
|
32
|
+
**Sessions & secrets**
|
|
33
|
+
|
|
34
|
+
- `agents secrets exec <bundle> -- <command>` injects a bundle's env vars into a one-shot subprocess (no shell-state leakage).
|
|
35
|
+
- `agents sessions` now groups active sessions by workspace and surfaces session topics in the picker.
|
|
36
|
+
- Session discovery scans both version repos; migrator merges overlapping versions instead of leaving duplicates.
|
|
37
|
+
|
|
38
|
+
**Renames**
|
|
39
|
+
|
|
40
|
+
- `agents init` → `agents setup`.
|
|
41
|
+
- `permissions/sets/` → `permissions/presets/` (resource directory + on-disk migration to match rules/presets convention).
|
|
42
|
+
|
|
43
|
+
**Dev**
|
|
21
44
|
|
|
22
|
-
|
|
45
|
+
- Crabbox remote-test profile (~$0.14/hr) + `scripts/sandbox.sh` documented in README and CLAUDE.md. Tests run remotely to avoid freezing the local machine.
|
|
23
46
|
|
|
24
|
-
|
|
47
|
+
## 1.15.0
|
|
25
48
|
|
|
26
|
-
|
|
27
|
-
- `agents init` and `agents pull` now bootstrap and sync the fixed system repo in `~/.agents-system/`.
|
|
28
|
-
- `agents repo init` now supports `--from <source>` and no longer pushes users toward generated names like `~/.agents-mine`; `~/.agents` is now the recommended clean path for a personal repo.
|
|
49
|
+
**Secrets: Linux support via libsecret/GNOME Keyring**
|
|
29
50
|
|
|
30
|
-
|
|
51
|
+
- `agents secrets` now works on Linux backed by libsecret/GNOME Keyring with the same UX as macOS Keychain. Headless workarounds documented.
|
|
52
|
+
- New `agents password generate` subcommand.
|
|
53
|
+
- Lifecycle events emitted for secrets and other subsystems; richer metadata (timing helpers) on the events system.
|
|
31
54
|
|
|
32
|
-
|
|
33
|
-
- New subcommands mirror the old daemon controls under `routines`: `agents routines start`, `stop`, `status`, `scheduler-logs`. The word "daemon" is no longer exposed in user-facing help.
|
|
34
|
-
- `agents daemon <start|stop|status|logs>` is **deprecated**. It still works but prints a migration warning and is hidden from top-level help. Will be removed in v2.0.
|
|
35
|
-
- Top-level help restructured: the old "Automation" grab-bag is gone. Commands are now grouped as **Run agents** (`run`, `teams`, `sessions`), **Schedule** (`routines`), and **Helpers** (`pty`).
|
|
55
|
+
**Browser**
|
|
36
56
|
|
|
37
|
-
|
|
57
|
+
- HTTP and WebSocket endpoint support for remote browsers.
|
|
58
|
+
- Concurrent Electron profile forks no longer step on each other; cleanup hardened.
|
|
59
|
+
- Remote browser restart works; SSH port handling improved; page target created when none exists for Electron apps.
|
|
60
|
+
- Events emitted for navigation and screenshots.
|
|
38
61
|
|
|
39
|
-
-
|
|
62
|
+
**First-run UX**
|
|
40
63
|
|
|
41
|
-
|
|
64
|
+
- Improved new-user experience: clearer CLI help, better defaults, audit-log opt-out, better run-timing display.
|
|
42
65
|
|
|
43
|
-
|
|
44
|
-
- Existing users whose upstream still points at the old default see a one-time nudge on `agents pull --upstream` with the command to switch. Nothing else breaks; legacy remotes continue to work.
|
|
66
|
+
**Prune**
|
|
45
67
|
|
|
46
|
-
|
|
68
|
+
- `agents prune` learned `trash`, `sessions`, and `runs` cleanup targets.
|
|
47
69
|
|
|
48
|
-
|
|
49
|
-
- Positional query resolves to a session ID (renders directly), a path (`.`, `../`, `/path`) to filter by project, or free text for search
|
|
50
|
-
- Claude `/resume` history fallback (previously only in `view`) now fires from the top-level command too
|
|
51
|
-
- Picker shows the selected-session preview by default; space hides it
|
|
70
|
+
**Fixes**
|
|
52
71
|
|
|
53
|
-
|
|
72
|
+
- Command-injection hole in daemon + secrets closed.
|
|
73
|
+
- Layered permission resolution corrected; daemon tests isolated from real user state.
|
|
74
|
+
- `.tmp-bun` gitignore pattern fixed.
|
|
75
|
+
- `codex` interactive mode no longer routes through `exec` subcommand.
|
|
54
76
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
**Docs**
|
|
78
|
+
|
|
79
|
+
- Security/privacy section in README, browser skill + automation guide, FAQ updated with audit-log transparency.
|
|
80
|
+
|
|
81
|
+
## 1.14.6
|
|
82
|
+
|
|
83
|
+
**Fix: OAuth token refresh now persists to Keychain**
|
|
84
|
+
|
|
85
|
+
- Fixed bug where refreshed Claude OAuth tokens were used but never saved back to macOS Keychain
|
|
86
|
+
- Previously, agents-cli would refresh expired tokens on each run but discard them, eventually exhausting the refresh token
|
|
87
|
+
- Now refreshed `accessToken`, `refreshToken`, and `expiresAt` are written back to Keychain after successful refresh
|
|
88
|
+
- Accounts will stay healthy across runs without requiring re-login
|
|
89
|
+
|
|
90
|
+
## 1.14.5
|
|
91
|
+
|
|
92
|
+
**Browser: custom binary and Electron app support**
|
|
93
|
+
|
|
94
|
+
- Added `binary` field to browser profiles for specifying custom executable paths (e.g., Electron apps like Rush)
|
|
95
|
+
- Added `electron` field to browser profiles — when true, uses existing windows instead of creating new ones (Electron doesn't support `Target.createTarget`)
|
|
96
|
+
- New `custom` browser type that requires a binary path
|
|
97
|
+
- Works with both local and SSH-based browser connections
|
|
98
|
+
- Example profile for Rush: `agents browser profiles edit rush --browser custom --binary "/Applications/Rush.app/Contents/MacOS/Rush" --electron`
|
|
60
99
|
|
|
61
100
|
## 1.12.0
|
|
62
101
|
|
package/README.md
CHANGED
|
@@ -53,6 +53,7 @@ Also available as `ag` -- all commands work with both `agents` and `ag`.
|
|
|
53
53
|
- [PTY](#pty)
|
|
54
54
|
- [Portable setup](#portable-setup)
|
|
55
55
|
- [Private skills](#private-skills)
|
|
56
|
+
- [Security & Privacy](#security--privacy)
|
|
56
57
|
- [Compatibility](#compatibility)
|
|
57
58
|
- [FAQ](#faq)
|
|
58
59
|
|
|
@@ -252,12 +253,12 @@ Give agents access to a real browser — no relay extension, no cloud service, n
|
|
|
252
253
|
agents browser profiles create work --browser chrome
|
|
253
254
|
|
|
254
255
|
# Start a task, navigate, interact
|
|
255
|
-
agents browser start login-flow --
|
|
256
|
-
agents browser navigate login-flow https://app.example.com
|
|
256
|
+
agents browser start --profile work --task login-flow --url https://app.example.com
|
|
257
257
|
agents browser refs login-flow # Get interactive element refs
|
|
258
|
-
agents browser click login-flow
|
|
259
|
-
agents browser type login-flow
|
|
258
|
+
agents browser click login-flow 42 # Click element ref 42
|
|
259
|
+
agents browser type login-flow 15 "hello" # Type into element ref 15
|
|
260
260
|
agents browser screenshot login-flow # Smart resizing, token-efficient
|
|
261
|
+
agents browser done login-flow # Close task's tabs when finished
|
|
261
262
|
```
|
|
262
263
|
|
|
263
264
|
### Why this works where Playwright fails
|
|
@@ -293,9 +294,9 @@ agents browser profiles create bank --browser chrome --secrets bank-creds
|
|
|
293
294
|
Control Electron apps (Slack, Discord, VS Code, your own app) with custom binaries:
|
|
294
295
|
|
|
295
296
|
```bash
|
|
296
|
-
agents browser profiles create
|
|
297
|
+
agents browser profiles create slack \
|
|
297
298
|
--browser custom \
|
|
298
|
-
--binary "/Applications/
|
|
299
|
+
--binary "/Applications/Slack.app/Contents/MacOS/Slack" \
|
|
299
300
|
--electron
|
|
300
301
|
```
|
|
301
302
|
|
|
@@ -453,6 +454,70 @@ Extras clone into `~/.agents-system/.repos/<alias>/` and ship the same layout as
|
|
|
453
454
|
|
|
454
455
|
---
|
|
455
456
|
|
|
457
|
+
## Security & Privacy
|
|
458
|
+
|
|
459
|
+
**Everything stays on your machine.** No telemetry, no cloud sync (unless you opt into iCloud Keychain for secrets), no phone-home. Here's exactly what `agents-cli` stores locally and why.
|
|
460
|
+
|
|
461
|
+
### Event log
|
|
462
|
+
|
|
463
|
+
Every agent run, version install, browser launch, and secrets access is logged to `~/.agents/logs/events-YYYY-MM-DD.jsonl`. This gives you a complete record of what agents did on your machine.
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
# What gets logged (example event):
|
|
467
|
+
{
|
|
468
|
+
"ts": "2026-05-09T10:23:45Z",
|
|
469
|
+
"event": "agent.run.end",
|
|
470
|
+
"agent": "claude",
|
|
471
|
+
"version": "2.1.121",
|
|
472
|
+
"prompt": "Fix the auth bug in...", # truncated to 200 chars
|
|
473
|
+
"durationMs": 45230,
|
|
474
|
+
"exitCode": 0,
|
|
475
|
+
"hostname": "your-mac",
|
|
476
|
+
"platform": "darwin"
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**What's logged:** Operation type, agent, version, timing, truncated prompts (first 200 chars), exit codes, errors. **What's NOT logged:** Full prompts, outputs, file contents, secret values (only bundle names).
|
|
481
|
+
|
|
482
|
+
**Permissions:** Logs directory is `0700` (owner-only), files are `0600`. Only you can read them.
|
|
483
|
+
|
|
484
|
+
**Retention:** 30 days by default, then auto-pruned.
|
|
485
|
+
|
|
486
|
+
**Opt out:** Set `AGENTS_DISABLE_EVENT_LOG=1` in your shell to disable completely.
|
|
487
|
+
|
|
488
|
+
### Session search
|
|
489
|
+
|
|
490
|
+
Conversations with Claude, Codex, Gemini, and other agents scatter across their native storage. Session search indexes them locally so you can find any conversation:
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
agents sessions "auth middleware" # Full-text search across all agents
|
|
494
|
+
agents sessions --agent claude --since 7d
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
The index lives at `~/.agents/sessions/sessions.db` (SQLite + FTS5). Nothing leaves your machine. See [Sessions](#sessions-across-agents) for full usage.
|
|
498
|
+
|
|
499
|
+
### Secrets
|
|
500
|
+
|
|
501
|
+
API keys and credentials are stored in macOS Keychain, never in plaintext files. Bundle definitions also live in Keychain.
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
agents secrets create my-keys
|
|
505
|
+
agents secrets add my-keys API_KEY # Prompts for value, stores in Keychain
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
With `--icloud-sync`, secrets sync via iCloud Keychain to your other Macs. Without it, they stay device-local. See [Secrets](#secrets) for full usage.
|
|
509
|
+
|
|
510
|
+
### Summary
|
|
511
|
+
|
|
512
|
+
| Data | Location | Who can read | Opt out |
|
|
513
|
+
|------|----------|--------------|---------|
|
|
514
|
+
| Event log | `~/.agents/logs/` | You only (0600) | `AGENTS_DISABLE_EVENT_LOG=1` |
|
|
515
|
+
| Session index | `~/.agents/sessions/` | You only | Delete the directory |
|
|
516
|
+
| Secrets | macOS Keychain | You + apps you authorize | Don't use `agents secrets` |
|
|
517
|
+
| Config | `~/.agents/` | You only | N/A |
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
456
521
|
## Compatibility
|
|
457
522
|
|
|
458
523
|
| Agent | Versions | MCP | Commands | Skills | Rules | Hooks | Plugins | Permissions | Routines | Teams |
|
|
@@ -505,7 +570,9 @@ Your choice. We hand off to the original CLI process — use your existing subsc
|
|
|
505
570
|
|
|
506
571
|
### Does it store my API keys or send telemetry?
|
|
507
572
|
|
|
508
|
-
No. API keys come from your shell environment or each agent CLI's existing auth.
|
|
573
|
+
**No telemetry, no phone-home.** Everything stays local. API keys come from your shell environment or each agent CLI's existing auth.
|
|
574
|
+
|
|
575
|
+
For full transparency: `agents-cli` keeps a local event log at `~/.agents/logs/` so you can see exactly what agents did on your machine. Logs are owner-readable only (0600) and auto-prune after 30 days. Set `AGENTS_DISABLE_EVENT_LOG=1` to disable. See [Security & Privacy](#security--privacy) for details.
|
|
509
576
|
|
|
510
577
|
### Which platforms?
|
|
511
578
|
|
package/dist/commands/alias.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import {
|
|
4
|
+
import { getUserAgentsDir } from '../lib/state.js';
|
|
5
5
|
import { getShimsDir } from '../lib/shims.js';
|
|
6
6
|
import { ALL_AGENT_IDS, AGENTS } from '../lib/agents.js';
|
|
7
|
-
const ALIASES_FILE = path.join(
|
|
7
|
+
const ALIASES_FILE = path.join(getUserAgentsDir(), 'aliases.json');
|
|
8
8
|
/** Names that would clobber an agent CLI shim or the `agents` binary itself. */
|
|
9
9
|
function reservedNames() {
|
|
10
10
|
const reserved = new Set(['agents']);
|
package/dist/commands/beta.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { ALL_BETA_FEATURES, getBetaConfigLocation, getEnabledBetaFeatures, setBetaEnabled, } from '../lib/beta.js';
|
|
3
|
+
const BETA_DESCRIPTIONS = {
|
|
4
|
+
drive: 'Google Drive integration for reading and writing files',
|
|
5
|
+
factory: 'Cloud-based agent dispatch via Rush Factory',
|
|
6
|
+
};
|
|
3
7
|
function parseFeatures(values) {
|
|
4
8
|
const valid = new Set(ALL_BETA_FEATURES);
|
|
5
9
|
const invalid = values.filter((value) => !valid.has(value));
|
|
@@ -29,7 +33,8 @@ Examples:
|
|
|
29
33
|
console.log(chalk.bold('Beta Features'));
|
|
30
34
|
for (const feature of ALL_BETA_FEATURES) {
|
|
31
35
|
const state = enabled.has(feature) ? chalk.green('enabled') : chalk.gray('disabled');
|
|
32
|
-
|
|
36
|
+
const desc = BETA_DESCRIPTIONS[feature] || '';
|
|
37
|
+
console.log(` ${feature.padEnd(10)} ${state.padEnd(18)} ${chalk.dim(desc)}`);
|
|
33
38
|
}
|
|
34
39
|
console.log('');
|
|
35
40
|
console.log(chalk.gray(`Config: ${location.path}`));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ProfileStatus, TaskStatus } from '../lib/browser/types.js';
|
|
2
|
+
export interface BrowserTask {
|
|
3
|
+
task: TaskStatus;
|
|
4
|
+
profile: ProfileStatus;
|
|
5
|
+
}
|
|
6
|
+
export interface PickedBrowserTask {
|
|
7
|
+
task: BrowserTask;
|
|
8
|
+
action: 'view' | 'stop';
|
|
9
|
+
}
|
|
10
|
+
/** Build the preview pane for a browser task. */
|
|
11
|
+
export declare function buildBrowserPreview(item: BrowserTask): string;
|
|
12
|
+
/** Build the list label for a browser task. */
|
|
13
|
+
export declare function buildBrowserLabel(item: BrowserTask, query: string): string;
|
|
14
|
+
export interface BrowserPickerConfig {
|
|
15
|
+
message: string;
|
|
16
|
+
tasks: BrowserTask[];
|
|
17
|
+
pageSize?: number;
|
|
18
|
+
initialSearch?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Show an interactive browser task picker. */
|
|
21
|
+
export declare function browserTaskPicker(config: BrowserPickerConfig): Promise<PickedBrowserTask | null>;
|
|
@@ -0,0 +1,114 @@
|
|
|
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 { itemPicker } from '../lib/picker.js';
|
|
9
|
+
const DOT = chalk.gray(' · ');
|
|
10
|
+
function humanDuration(ms) {
|
|
11
|
+
const s = Math.floor(ms / 1000);
|
|
12
|
+
if (s < 60)
|
|
13
|
+
return `${s}s`;
|
|
14
|
+
const m = Math.floor(s / 60);
|
|
15
|
+
if (m < 60)
|
|
16
|
+
return `${m}m`;
|
|
17
|
+
const h = Math.floor(m / 60);
|
|
18
|
+
const mm = m % 60;
|
|
19
|
+
if (h < 24)
|
|
20
|
+
return mm ? `${h}h ${mm}m` : `${h}h`;
|
|
21
|
+
const d = Math.floor(h / 24);
|
|
22
|
+
const hh = h % 24;
|
|
23
|
+
return hh ? `${d}d ${hh}h` : `${d}d`;
|
|
24
|
+
}
|
|
25
|
+
function formatAge(ms) {
|
|
26
|
+
return humanDuration(Date.now() - ms) + ' ago';
|
|
27
|
+
}
|
|
28
|
+
function truncate(s, max) {
|
|
29
|
+
return s.length > max ? s.slice(0, max - 1) + '…' : s;
|
|
30
|
+
}
|
|
31
|
+
/** Build the preview pane for a browser task. */
|
|
32
|
+
export function buildBrowserPreview(item) {
|
|
33
|
+
const { task, profile } = item;
|
|
34
|
+
const lines = [];
|
|
35
|
+
// Line 1: Task name (ID: xxx)
|
|
36
|
+
lines.push(chalk.bold.white(`Task: ${task.name}`) + chalk.gray(` (${task.id})`));
|
|
37
|
+
// Line 2: Profile info
|
|
38
|
+
const profileParts = [chalk.cyan(profile.name)];
|
|
39
|
+
if (profile.port)
|
|
40
|
+
profileParts.push(`port ${profile.port}`);
|
|
41
|
+
if (profile.pid)
|
|
42
|
+
profileParts.push(`pid ${profile.pid}`);
|
|
43
|
+
lines.push(chalk.gray('Profile: ') + profileParts.join(DOT));
|
|
44
|
+
// Line 3: Timing
|
|
45
|
+
const started = formatAge(task.createdAt);
|
|
46
|
+
const duration = humanDuration(Date.now() - task.createdAt);
|
|
47
|
+
lines.push(chalk.gray('Started: ') + chalk.white(started) + DOT + chalk.gray('Duration: ') + chalk.white(duration));
|
|
48
|
+
// Blank line
|
|
49
|
+
lines.push('');
|
|
50
|
+
// Tabs section
|
|
51
|
+
if (task.tabs && task.tabs.length > 0) {
|
|
52
|
+
lines.push(chalk.gray('Tabs:'));
|
|
53
|
+
const termWidth = process.stdout.columns || 80;
|
|
54
|
+
const urlMax = Math.max(30, termWidth - 16);
|
|
55
|
+
for (const tab of task.tabs) {
|
|
56
|
+
const marker = tab.current ? chalk.yellow('*') : ' ';
|
|
57
|
+
const id = chalk.gray(tab.id);
|
|
58
|
+
const url = truncate(tab.url, urlMax);
|
|
59
|
+
lines.push(` ${id} ${marker} ${chalk.white(url)}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
lines.push(chalk.gray('No tabs open'));
|
|
64
|
+
}
|
|
65
|
+
return lines.join('\n');
|
|
66
|
+
}
|
|
67
|
+
/** Build the list label for a browser task. */
|
|
68
|
+
export function buildBrowserLabel(item, query) {
|
|
69
|
+
const { task, profile } = item;
|
|
70
|
+
const termWidth = process.stdout.columns || 80;
|
|
71
|
+
// Format: name profile tabs domains age
|
|
72
|
+
const name = task.name.padEnd(20);
|
|
73
|
+
const profileName = profile.name.padEnd(12);
|
|
74
|
+
const tabs = `${task.tabCount} tab${task.tabCount === 1 ? '' : 's'}`.padEnd(8);
|
|
75
|
+
const age = formatAge(task.createdAt);
|
|
76
|
+
// Domains - fill remaining space
|
|
77
|
+
const fixedWidth = 20 + 12 + 8 + 10; // name + profile + tabs + age
|
|
78
|
+
const domainsWidth = Math.max(10, termWidth - fixedWidth - 4);
|
|
79
|
+
const domains = task.domains?.length
|
|
80
|
+
? truncate(task.domains.join(', '), domainsWidth)
|
|
81
|
+
: chalk.gray('no sites');
|
|
82
|
+
// Highlight query matches
|
|
83
|
+
let label = `${name}${chalk.cyan(profileName)}${tabs}${domains.padEnd(domainsWidth)}${chalk.gray(age)}`;
|
|
84
|
+
if (query) {
|
|
85
|
+
const re = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
86
|
+
label = label.replace(re, chalk.yellow.bold('$1'));
|
|
87
|
+
}
|
|
88
|
+
return label;
|
|
89
|
+
}
|
|
90
|
+
/** Show an interactive browser task picker. */
|
|
91
|
+
export async function browserTaskPicker(config) {
|
|
92
|
+
const picked = await itemPicker({
|
|
93
|
+
message: config.message,
|
|
94
|
+
items: config.tasks,
|
|
95
|
+
filter: (query) => {
|
|
96
|
+
if (!query)
|
|
97
|
+
return config.tasks;
|
|
98
|
+
const q = query.toLowerCase();
|
|
99
|
+
return config.tasks.filter((t) => t.task.name.toLowerCase().includes(q) ||
|
|
100
|
+
t.profile.name.toLowerCase().includes(q) ||
|
|
101
|
+
t.task.domains?.some((d) => d.toLowerCase().includes(q)));
|
|
102
|
+
},
|
|
103
|
+
labelFor: buildBrowserLabel,
|
|
104
|
+
buildPreview: buildBrowserPreview,
|
|
105
|
+
shortIdFor: (t) => t.task.name,
|
|
106
|
+
pageSize: config.pageSize ?? 10,
|
|
107
|
+
initialSearch: config.initialSearch,
|
|
108
|
+
emptyMessage: 'No browser tasks running.',
|
|
109
|
+
enterHint: 'view tabs',
|
|
110
|
+
});
|
|
111
|
+
if (!picked)
|
|
112
|
+
return null;
|
|
113
|
+
return { task: picked.item, action: 'view' };
|
|
114
|
+
}
|