@phnx-labs/agents-cli 1.22.74 → 1.22.76
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 +152 -0
- package/README.md +21 -9
- package/dist/bootstrap.js +7 -7
- package/dist/cli/command-registry.js +5 -0
- package/dist/commands/artifacts-setup.js +1 -1
- package/dist/commands/artifacts.js +1 -1
- package/dist/commands/auth.js +7 -1
- package/dist/commands/browser.js +104 -10
- package/dist/commands/commands.js +7 -6
- package/dist/commands/computer.d.ts +1 -0
- package/dist/commands/computer.js +26 -7
- package/dist/commands/config.js +27 -4
- package/dist/commands/cost.js +6 -4
- package/dist/commands/doctor.d.ts +6 -5
- package/dist/commands/doctor.js +32 -274
- package/dist/commands/exec.d.ts +2 -0
- package/dist/commands/exec.js +9 -2
- package/dist/commands/harness.d.ts +1 -0
- package/dist/commands/harness.js +11 -3
- package/dist/commands/hooks.js +7 -6
- package/dist/commands/mcp.js +7 -6
- package/dist/commands/memory.js +7 -7
- package/dist/commands/monitors.js +3 -2
- package/dist/commands/open.d.ts +25 -12
- package/dist/commands/open.js +24 -10
- package/dist/commands/permissions.js +7 -6
- package/dist/commands/plugins.js +21 -17
- package/dist/commands/route.js +33 -16
- package/dist/commands/rules.js +7 -12
- package/dist/commands/sessions-share.js +1 -1
- package/dist/commands/setup-watchdog.js +2 -2
- package/dist/commands/setup.js +22 -1
- package/dist/commands/share.js +26 -10
- package/dist/commands/skills.js +7 -6
- package/dist/commands/subagents.js +7 -6
- package/dist/commands/sync.js +81 -10
- package/dist/commands/view.js +4 -1
- package/dist/commands/watchdog.d.ts +1 -1
- package/dist/commands/watchdog.js +10 -10
- package/dist/commands/webhook.d.ts +4 -0
- package/dist/commands/webhook.js +22 -4
- package/dist/commands/workflows.js +7 -6
- package/dist/lib/account-registry.js +27 -6
- package/dist/lib/accounting/rotate.d.ts +3 -1
- package/dist/lib/accounting/rotate.js +8 -4
- package/dist/lib/auth-health.d.ts +2 -0
- package/dist/lib/auth-health.js +2 -0
- package/dist/lib/browser/chrome.d.ts +21 -0
- package/dist/lib/browser/chrome.js +60 -3
- package/dist/lib/browser/drivers/local.d.ts +21 -0
- package/dist/lib/browser/drivers/local.js +102 -9
- package/dist/lib/browser/profiles.d.ts +29 -1
- package/dist/lib/browser/profiles.js +50 -1
- package/dist/lib/browser/types.d.ts +18 -0
- package/dist/lib/computer/computer-rpc.d.ts +6 -1
- package/dist/lib/computer/computer-rpc.js +23 -3
- package/dist/lib/computer/des.d.ts +1 -0
- package/dist/lib/computer/des.js +114 -0
- package/dist/lib/computer/rfb-client.d.ts +53 -0
- package/dist/lib/computer/rfb-client.js +562 -0
- package/dist/lib/config-keys.d.ts +7 -2
- package/dist/lib/config-keys.js +17 -2
- package/dist/lib/daemon/auth-sync-service.js +3 -0
- package/dist/lib/daemon/daemon.js +17 -10
- package/dist/lib/daemon/session-summarizer-service.d.ts +24 -0
- package/dist/lib/daemon/session-summarizer-service.js +39 -0
- package/dist/lib/daemon/usage-sync-service.js +3 -0
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/daemon-ticks.d.ts +2 -2
- package/dist/lib/daemon-ticks.js +2 -1
- package/dist/lib/daemon-webhooks.js +15 -2
- package/dist/lib/deeplink/register.js +10 -9
- package/dist/lib/deeplink/url.d.ts +4 -4
- package/dist/lib/deeplink/url.js +4 -4
- package/dist/lib/device-config.js +25 -0
- package/dist/lib/devices/doctor-findings.d.ts +4 -4
- package/dist/lib/devices/doctor-findings.js +14 -8
- package/dist/lib/devices/registry.js +2 -0
- package/dist/lib/devices/stats-cache.d.ts +4 -0
- package/dist/lib/devices/stats-cache.js +19 -0
- package/dist/lib/drift-sync.d.ts +3 -1
- package/dist/lib/drift-sync.js +16 -5
- package/dist/lib/exec.d.ts +2 -0
- package/dist/lib/exec.js +16 -1
- package/dist/lib/fleet-shared-repo-sync.d.ts +12 -0
- package/dist/lib/fleet-shared-repo-sync.js +101 -4
- package/dist/lib/fleet-shared-state.d.ts +8 -0
- package/dist/lib/heal.d.ts +4 -3
- package/dist/lib/heal.js +5 -4
- package/dist/lib/hosts/ready.d.ts +1 -1
- package/dist/lib/hosts/ready.js +16 -4
- package/dist/lib/hosts/reconnect.js +4 -2
- package/dist/lib/identity/client.d.ts +6 -0
- package/dist/lib/identity/index.d.ts +16 -0
- package/dist/lib/identity/index.js +25 -1
- package/dist/lib/profiles.d.ts +2 -0
- package/dist/lib/profiles.js +28 -9
- package/dist/lib/reconcile-and-repair.d.ts +109 -0
- package/dist/lib/reconcile-and-repair.js +267 -0
- package/dist/lib/routers.d.ts +12 -1
- package/dist/lib/routers.js +30 -1
- package/dist/lib/scheduling/routines.js +8 -2
- package/dist/lib/session/active.d.ts +13 -0
- package/dist/lib/session/db.d.ts +47 -7
- package/dist/lib/session/db.js +114 -12
- package/dist/lib/session/mirror.js +58 -0
- package/dist/lib/session/remote/remote-list.d.ts +2 -0
- package/dist/lib/session/remote/remote-list.js +4 -0
- package/dist/lib/session/remote/watch.js +22 -2
- package/dist/lib/session/session-cache.d.ts +19 -0
- package/dist/lib/session/session-cache.js +46 -0
- package/dist/lib/session/types.d.ts +34 -0
- package/dist/lib/share/backend.d.ts +6 -4
- package/dist/lib/share/backend.js +10 -8
- package/dist/lib/share/config.d.ts +4 -3
- package/dist/lib/share/config.js +10 -1
- package/dist/lib/share/delete.d.ts +1 -1
- package/dist/lib/share/delete.js +1 -1
- package/dist/lib/share/html.d.ts +1 -1
- package/dist/lib/share/html.js +1 -1
- package/dist/lib/share/provision.d.ts +1 -1
- package/dist/lib/share/provision.js +2 -2
- package/dist/lib/share/publish.d.ts +23 -7
- package/dist/lib/share/publish.js +58 -12
- package/dist/lib/share/worker-template.js +221 -60
- package/dist/lib/startup/command-registry.js +2 -2
- package/dist/lib/state.d.ts +15 -0
- package/dist/lib/state.js +29 -7
- package/dist/lib/summarizer/config.d.ts +46 -0
- package/dist/lib/summarizer/config.js +83 -0
- package/dist/lib/summarizer/pass.d.ts +45 -0
- package/dist/lib/summarizer/pass.js +112 -0
- package/dist/lib/summarizer/summarize.d.ts +68 -0
- package/dist/lib/summarizer/summarize.js +120 -0
- package/dist/lib/teams/agents.d.ts +4 -3
- package/dist/lib/teams/agents.js +12 -4
- package/dist/lib/teams/scheduler.d.ts +4 -2
- package/dist/lib/teams/scheduler.js +6 -6
- package/dist/lib/tmux/session.d.ts +2 -0
- package/dist/lib/tmux/session.js +7 -1
- package/dist/lib/types.d.ts +20 -0
- package/dist/lib/verbs.d.ts +23 -0
- package/dist/lib/verbs.js +24 -0
- package/dist/lib/view-types.d.ts +4 -0
- package/dist/lib/watchdog/rotate.d.ts +1 -1
- package/dist/lib/watchdog/rotate.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,157 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.76
|
|
4
|
+
|
|
5
|
+
- **Per-session summarizer: bound the mirror publish (PHNX-3939 follow-up).** The fleet session mirror now caps `checkpoints`/`summaryChecklist` on the **publish** side (50/100 items, 400-char text) exactly as it already did on consume, so a box's own summarizer output can never ride unbounded into the git-synced `daemon-state.json` every peer fetches — matching the `snippet()` cap already applied to `goal`. Source: `cli/src/lib/session/mirror.ts`.
|
|
6
|
+
|
|
7
|
+
- **Per-session summarizer on the session stream (PHNX-3939).** A new opt-in daemon
|
|
8
|
+
service computes a 1–2 line **goal**, progress **checkpoints**, and a detailed
|
|
9
|
+
**checklist** for each running session and delivers them on `agents sessions watch`
|
|
10
|
+
/ `agents feed watch --json` (as `goal` / `checkpoints` / `summaryChecklist` /
|
|
11
|
+
`summaryState`), so the AGI extension sidebar can render them. It is **off by
|
|
12
|
+
default and never on the request path**: the model call runs only in the
|
|
13
|
+
background `session-summarizer` service, reader-gated and bounded per tick, and a
|
|
14
|
+
computed summary is cached in the transcript-keyed `session_summaries` table so it
|
|
15
|
+
never recomputes on unchanged bytes and `agents sessions <id>` timing is unchanged.
|
|
16
|
+
Enable it with `agents config set summarizer.enabled on` plus a local
|
|
17
|
+
Anthropic-wire endpoint — `agents config set summarizer.baseUrl http://localhost:11434`
|
|
18
|
+
and `agents config set summarizer.model qwen2.5:3b` (env overrides:
|
|
19
|
+
`AGENTS_SUMMARIZER_ENABLED` / `AGENTS_SUMMARIZER_BASEURL` / `AGENTS_SUMMARIZER_MODEL`).
|
|
20
|
+
Summaries ride the fleet session mirror, so a peer's sessions carry them too. When
|
|
21
|
+
disabled or unconfigured, zero model calls are made and rows read
|
|
22
|
+
`summaryState: "skipped"`. Source: `cli/src/lib/summarizer/`,
|
|
23
|
+
`cli/src/lib/daemon/session-summarizer-service.ts`, `cli/src/lib/session/db.ts`.
|
|
24
|
+
|
|
25
|
+
- **`agents config unset project.root` now actually clears it.** `projectRoot` is a
|
|
26
|
+
machine-local key persisted to the device doc, which `writeMeta` only clears when
|
|
27
|
+
the key is present-but-falsy; the unset path dropped the key entirely, so
|
|
28
|
+
`agents config get project.root` still returned the old value afterward. Source:
|
|
29
|
+
`cli/src/commands/config.ts`.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
type: fixed
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
Keep fleet account and device routing consistent: poolless teams now use the active worker pool, remote readiness rejects stale account evidence, removed devices lose cached stats, partial device writes preserve unrelated metadata, paths are passed without shell reparsing, and harness forks can translate across native hosts.
|
|
36
|
+
|
|
37
|
+
- **Managed share: collision recovery, race-safe republish, and OAuth avatars
|
|
38
|
+
(PHNX-3942, folding PHNX-3547 + PHNX-3579).** Three correctness gaps on the
|
|
39
|
+
artifact-sharing path are closed. (1) A same-local-part account move no longer
|
|
40
|
+
dead-ends on the permanent 409: the Worker's `__handles/<handle>` claim now records
|
|
41
|
+
the verified email, and a request whose verified email matches the claim but whose
|
|
42
|
+
userId differs (the same human re-authenticated under a new account id) transfers
|
|
43
|
+
the claim and re-stamps the old account's objects instead of refusing; when the
|
|
44
|
+
handle genuinely belongs to someone else, `agents artifacts share --handle <name>`
|
|
45
|
+
publishes under an alternate first-writer-claimed namespace (new `x-share-handle`
|
|
46
|
+
Worker header). (2) A concurrent republish no longer silently discards a body: the
|
|
47
|
+
Worker's read → archive-revision → overwrite sequence is now a bounded
|
|
48
|
+
compare-and-swap loop (`onlyIf.etagMatches`, the primitive PATCH and the usage
|
|
49
|
+
ledger already used) — a conflicting write 409s the put, the loser re-reads,
|
|
50
|
+
archives the winner's body as a revision, and lands its own as canonical, so every
|
|
51
|
+
accepted body survives as canonical or revision. (3) Share attribution prefers the
|
|
52
|
+
hosted OAuth profile image: the Phoenix session now carries `avatarUrl` (captured
|
|
53
|
+
at login and refreshed from `/api/v1/auth/me`), and publish stamps it before
|
|
54
|
+
falling back to the email Gravatar. Also, publish failures stop appending
|
|
55
|
+
'Check the write token' to quota/rate rejections (413/429) — the Worker's actual
|
|
56
|
+
reason is surfaced, and only 401/403 mention the token (or `agents auth login` on
|
|
57
|
+
the managed endpoint). Source: `cli/src/lib/share/worker-template.ts`,
|
|
58
|
+
`cli/src/lib/share/publish.ts`, `cli/src/lib/identity/client.ts`,
|
|
59
|
+
`cli/src/lib/identity/index.ts`, `cli/src/commands/auth.ts`,
|
|
60
|
+
`cli/src/commands/share.ts`.
|
|
61
|
+
|
|
62
|
+
- **Release recovery and exclusion now hold under rebases, interruption, and concurrent releasers (PHNX-3945).** A retry validates the merged target version and the recorded/fetched release PR identity, then tags and promotes the exact attested PR head even when the merge tree differs. Attestation fallback is capped at 45 seconds total (15-second optional fetch plus a 30-second poll), content-addressed records publish through a same-directory atomic rename, and release-lease acquisition/reclamation uses one expected-old-ref compare-and-swap instead of overwritable custom-ref pushes or a delete/create gap.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
type: changed
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
- **`agents watchdog` uses the standard `enable`/`disable` verbs (PHNX-3949).**
|
|
69
|
+
The device-local daemon watchdog pass is now toggled with
|
|
70
|
+
`agents watchdog enable` / `agents watchdog disable`, matching the
|
|
71
|
+
`enable`/`disable` shape `menubar` and `daemon` already use for the same
|
|
72
|
+
concept. The old `on` / `off` spellings keep working as aliases, so no existing
|
|
73
|
+
invocation or script breaks. Source: `cli/src/commands/watchdog.ts`.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
type: changed
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
- **CLI verb vocabulary unified behind one canonical alias table (PHNX-3949).**
|
|
80
|
+
A new `cli/src/lib/verbs.ts` defines the standard short-form aliases
|
|
81
|
+
(`list`→`ls`, `remove`→`rm`, `view`→`show`, `rename`→`mv`) and a `withAliases`
|
|
82
|
+
helper, applied uniformly across `skills`, `hooks`, `commands`, `rules`,
|
|
83
|
+
`permissions`, `mcp`, `subagents`, `workflows`, and `memory` so every resource
|
|
84
|
+
group answers to the same shortcuts. No primary command name changes. Source:
|
|
85
|
+
`cli/src/lib/verbs.ts`.
|
|
86
|
+
- **`agents route` now uses the standard verbs and pluralizes its noun (PHNX-3949).**
|
|
87
|
+
The router command group matches the shape `harness` already set: read-one is
|
|
88
|
+
`route view <name>` (old `show` kept as an alias), create is `route add <name>`
|
|
89
|
+
(old `create` kept as an alias), `list` gains the `ls` short alias, and the
|
|
90
|
+
whole tree resolves under the plural noun `agents routes ...`. New
|
|
91
|
+
`route rename <old> <new>` re-keys a user-layer router in place, preserving
|
|
92
|
+
every field (harness allowlists, weights, linked accounts, hijack flag); it
|
|
93
|
+
fails loud on a missing source, a name collision in any layer, or a router
|
|
94
|
+
that resolves from a non-user layer. Source: `cli/src/commands/route.ts`,
|
|
95
|
+
`cli/src/lib/routers.ts`.
|
|
96
|
+
- **`plugins` moves to the canonical `view`/`add` verbs, and `monitors` gains an
|
|
97
|
+
`rm` alias (PHNX-3949).** `agents plugins info` is now `agents plugins view`
|
|
98
|
+
and `agents plugins install` is now `agents plugins add`; the old names keep
|
|
99
|
+
working as aliases, so no existing invocation breaks. `plugins list` gains an
|
|
100
|
+
`ls` alias and `plugins remove` gains `rm`. `agents monitors remove <name>`
|
|
101
|
+
gains the `rm` alias. Source: `cli/src/commands/plugins.ts`,
|
|
102
|
+
`cli/src/commands/monitors.ts`.
|
|
103
|
+
- **`agents open` is now the machine-only `_callback` verb; handler setup moved under `agents setup url-scheme` (PHNX-3949).**
|
|
104
|
+
`agents open <url>` was never a user command — it is the OS callback the
|
|
105
|
+
`agents://` deep-link handler invokes when you click a session link in a
|
|
106
|
+
rendered artifact. It is now hidden as the machine-only `agents _callback`
|
|
107
|
+
verb (humans resume with `agents sessions resume`), and the register /
|
|
108
|
+
unregister / status plumbing moved to a visible `agents setup url-scheme`
|
|
109
|
+
group. Nothing breaks: `agents open <url>` stays a hidden alias so OS handlers
|
|
110
|
+
written by older CLIs keep resolving until `agents setup url-scheme register`
|
|
111
|
+
re-writes them to the new verb, and `agents open register|unregister|status`
|
|
112
|
+
still work as hidden back-compat subcommands. Source:
|
|
113
|
+
`cli/src/commands/open.ts`, `cli/src/commands/setup.ts`,
|
|
114
|
+
`cli/src/lib/deeplink/register.ts`.
|
|
115
|
+
|
|
116
|
+
- **One canonical Comet: attach-only browser profiles + closed port-squat (PHNX-3967).** A browser profile now carries a `launchPolicy` (`attach-only` | `launch`) and a durable `--user-data-dir`. An attach-only profile (`agents browser profiles create <name> --browser comet --attach-only`, and Arc always) NEVER spawns a rival window — agents attach to the browser you already started with remote debugging, else fail loud with the exact `open -a <app> --args --remote-debugging-port=<port> --user-data-dir=<durable>` relaunch. Before adopting an endpoint the local driver now verifies ownership beyond browser family: it compares the running instance's `--user-data-dir` to the profile's durable dir and rejects a foreign port-squatter (e.g. a logged-out `/tmp` Comet on the canonical port) instead of driving it — and if an occupant is serving CDP but its `--user-data-dir` can't be read, the guard refuses to attach to the unverified instance (fail loud) rather than driving it silently, with `profiles doctor` surfacing the same unverified state instead of a green check. The durable dir lives under `~/.agents/.history/browser-profiles/<name>/` (outside `.cache`), so a one-time sign-in survives quit+relaunch and `profiles remove`. `agents browser profiles doctor` flags a foreign-user-data-dir Comet on the canonical port. Source: `apps/cli/src/lib/browser/drivers/local.ts`, `apps/cli/src/lib/browser/{profiles,chrome,types}.ts`, `apps/cli/src/commands/browser.ts`.
|
|
117
|
+
|
|
118
|
+
- **`agents doctor` is now diagnose-only; `agents sync` is the single fixer.** `doctor --fix` is removed — running it prints a one-line redirect to `agents sync` and exits non-zero — and every `doctor` remediation now points at `agents sync`. `sync` gains the repairs `doctor --fix` used to own: managed-hook-runtime-shim repair (the "source mismatch" fixer), hook re-wiring, and full home-rot heal, running on **every** sync path — the umbrella, agent-scoped, single-version, and the `agents sync status` / menu-bar drift path — so one `agents sync` now converges a version that `doctor` flags, instead of two commands that could disagree. Repair failures surface in `--json` output and the process exit code. The machine-wide stale-CLI purge no longer runs on a routine sync; it is now the explicit `agents sync --prune-clis` flag. Source: `cli/src/lib/reconcile-and-repair.ts`, `cli/src/commands/sync.ts`, `cli/src/commands/doctor.ts`, `cli/src/lib/drift-sync.ts`.
|
|
119
|
+
|
|
120
|
+
## 1.22.75
|
|
121
|
+
|
|
122
|
+
- **Account labels are per-harness, and survive a daemon publish (PHNX-3887).** One
|
|
123
|
+
identity signed into several harnesses can now carry the SAME label on each —
|
|
124
|
+
`claude#icloud`, `codex#icloud`, and `grok#icloud` all resolve to that harness's own
|
|
125
|
+
login, instead of the first-labelled harness squatting the name and forcing prefixes
|
|
126
|
+
(`cxicloud`, `gkicloud`) on the rest. Uniqueness is scoped to `(agent, label)`;
|
|
127
|
+
provider accounts, selected by bare name via `--account`, stay globally unique, and a
|
|
128
|
+
duplicate label within one harness is still refused. Separately, `agents accounts
|
|
129
|
+
label` wrote version-scoped rows into the central `agents.yaml` as a plain file write
|
|
130
|
+
with no commit, so the daemon's `publish <device> daemon state` tick — which committed
|
|
131
|
+
only the per-device doc and then rebased `--autostash` over the dirty central file —
|
|
132
|
+
silently destroyed them: every box lost its account labels on its next publish. The
|
|
133
|
+
publish now commits `agents.yaml` alongside the device doc, so labels persist and
|
|
134
|
+
actually sync fleet-wide as `accounts label --help` already promised. Source:
|
|
135
|
+
`cli/src/lib/account-registry.ts`, `cli/src/lib/fleet-shared-repo-sync.ts`.
|
|
136
|
+
|
|
137
|
+
- **Keep one daemon alive across concurrent stop/start (PHNX-3941).** Prevent a replacement daemon from exiting while stop holds the lifecycle lock, so concurrent stop/start cannot leave zero instances, and extend real-boot supervisor-wiring coverage to all 20 services. Source: `cli/src/lib/daemon/daemon.ts`.
|
|
138
|
+
|
|
139
|
+
- **`agents computer` can now drive a Linux GUI desktop over VNC/RFB (PHNX-3948).**
|
|
140
|
+
A third computer backend joins the macOS (Accessibility) and Windows (UIA) native
|
|
141
|
+
helpers — this one speaks the RFB/VNC protocol directly to an x11vnc/Xvnc server, so
|
|
142
|
+
a headless Linux desktop (or an LXD container exposing x11vnc on the host's Tailscale
|
|
143
|
+
IP) becomes drivable with no per-box native helper install. Select it with
|
|
144
|
+
`agents computer --vnc <host:port>` (port defaults to 5901; password via
|
|
145
|
+
`--vnc-password` or `COMPUTER_HELPER_VNC_PASSWORD`), then use the same verbs as the
|
|
146
|
+
other backends: `screenshot`, `click`, `right-click`, `type`, `type-text`, `key`,
|
|
147
|
+
`scroll`, `wait`. It is coordinate-based — RFB carries a framebuffer and pointer/key
|
|
148
|
+
events, not an accessibility tree — so the AX-tree verbs (`describe`, `get-text`,
|
|
149
|
+
`ax-action`, element-id targeting) and `launch` fail loud with a clear message rather
|
|
150
|
+
than silently no-op'ing. VNC authentication uses a bundled pure-JS DES (Node 24's
|
|
151
|
+
OpenSSL 3 keeps DES in the off-by-default legacy provider). Source:
|
|
152
|
+
`cli/src/lib/computer/rfb-client.ts`, `cli/src/lib/computer/des.ts`,
|
|
153
|
+
`cli/src/lib/computer/computer-rpc.ts`, `cli/src/commands/computer.ts`.
|
|
154
|
+
|
|
3
155
|
## 1.22.74
|
|
4
156
|
|
|
5
157
|
- **Launching an agent no longer dirties your tracked `.gitignore` (PHNX-3718).**
|
package/README.md
CHANGED
|
@@ -493,6 +493,18 @@ Slack). Owner delivery attempts every selected channel, reports partial
|
|
|
493
493
|
failures, and safely forwards Rush-backed channels from headless workers to a
|
|
494
494
|
capable Mac; ordinary milestone posts remain record-only unless configured.
|
|
495
495
|
|
|
496
|
+
An optional **per-session summarizer** (off by default) adds a daemon-computed
|
|
497
|
+
`goal`, progress `checkpoints`, and a detailed `summaryChecklist` to each session
|
|
498
|
+
row on the `feed watch` / `sessions watch` stream. It never runs on a request
|
|
499
|
+
path — the model call lives in a background daemon service, cached per transcript
|
|
500
|
+
so it never recomputes on unchanged bytes. Turn it on with a local model endpoint:
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
agents config set summarizer.enabled on
|
|
504
|
+
agents config set summarizer.baseUrl http://localhost:11434 # Ollama / vLLM / LiteLLM
|
|
505
|
+
agents config set summarizer.model qwen2.5:3b
|
|
506
|
+
```
|
|
507
|
+
|
|
496
508
|
Top-level questions and waiting notifications publish one atomic open-block record per session, including the mailbox id, host, runtime, and every answer option. The default view collapses agents under the **outcome** they serve (Linear ticket, PR, worktree slug, or Unassigned) so a 1,100-agent fleet reads as dozens of deliverables. Answered, resumed, and stopped blocks clear automatically; Task subagents are excluded. The rendered reply command uses the same mailbox id with `agents message`, so the decision routes back to the agent that asked it.
|
|
497
509
|
|
|
498
510
|
### Auto-nudge stalls
|
|
@@ -504,7 +516,7 @@ agents watchdog --nudge # actually inject "Continue." into the stalled split
|
|
|
504
516
|
agents watchdog --watch # daemon loop: a tick every --interval
|
|
505
517
|
```
|
|
506
518
|
|
|
507
|
-
`agents watchdog` detects a stalled session, resolves the *exact* terminal split it lives in (tmux, iTerm, VSCodium, or a raw pty), and injects a nudge -- `Continue.` by default, or set `--text`. Its timestamped default output shows attention-worthy sessions with their agent, host app, machine, project, activity, age, path, latest preview, and decision reason; `--verbose` restores healthy/non-actionable inspections. It's dry by default; `--nudge` acts on a single tick. `agents watchdog
|
|
519
|
+
`agents watchdog` detects a stalled session, resolves the *exact* terminal split it lives in (tmux, iTerm, VSCodium, or a raw pty), and injects a nudge -- `Continue.` by default, or set `--text`. Its timestamped default output shows attention-worthy sessions with their agent, host app, machine, project, activity, age, path, latest preview, and decision reason; `--verbose` restores healthy/non-actionable inspections. It's dry by default; `--nudge` acts on a single tick. `agents watchdog enable|disable` controls the device-local daemon pass, which runs once every three minutes. Steer a single run with `agents watchdog policy <id> off | keep | handsoff`.
|
|
508
520
|
|
|
509
521
|
A stalled session whose tail shows a hard account limit ("You've hit your weekly limit · resets …") is **rotated in place** instead of nudged: the watchdog gates on the same healthy-account selection `agents run auto` makes (zero healthy → one skip event per cooldown window, terminal untouched), injects the harness's exit sequence, relaunches `agents run auto --interactive --session-id <uuid>` in the *same* tab, then replays the old session's resume once the new TUI is live. Default on; `agents watchdog rotate off` disables it (nudging stays on).
|
|
510
522
|
|
|
@@ -623,7 +635,7 @@ A **router** is a reusable, task-typed allowlist -- which harnesses, which model
|
|
|
623
635
|
|
|
624
636
|
```bash
|
|
625
637
|
# Scope a router to two harnesses, capped at a tier
|
|
626
|
-
agents route
|
|
638
|
+
agents route add research --harness gemini,kimi --tier cheap,default # alias: create
|
|
627
639
|
|
|
628
640
|
# Narrow one harness's model set
|
|
629
641
|
agents route allow research kimi kimi-k2
|
|
@@ -632,11 +644,12 @@ agents route allow research kimi kimi-k2
|
|
|
632
644
|
agents route link-account research gemini personal
|
|
633
645
|
agents route link-account research kimi work
|
|
634
646
|
|
|
635
|
-
agents route
|
|
647
|
+
agents route view research # alias: show
|
|
648
|
+
agents route rename research prod # rename a router, preserving its config
|
|
636
649
|
agents route list --json
|
|
637
650
|
```
|
|
638
651
|
|
|
639
|
-
Router YAML has no secrets -- safe to `agents repo push` to a shared repo. Harness ids and model/tier tokens are validated on `
|
|
652
|
+
Router YAML has no secrets -- safe to `agents repo push` to a shared repo. Harness ids and model/tier tokens are validated on `add`/`allow`: an unknown harness or an unverifiable model id fails loud and writes nothing. Routers resolve as a layered resource (project > user > system, same as profiles). The old verbs `create`/`show` remain as aliases.
|
|
640
653
|
|
|
641
654
|
---
|
|
642
655
|
|
|
@@ -903,9 +916,8 @@ Resolution is project > user > system: a `<repo>/.agents/workflows/<name>/` over
|
|
|
903
916
|
Bundle skills, commands, hooks, MCP servers, settings, and permissions under a single manifest. One source dir at `~/.agents/plugins/<name>/`, mirrored into every installed Claude / OpenClaw version automatically.
|
|
904
917
|
|
|
905
918
|
```bash
|
|
906
|
-
#
|
|
907
|
-
agents plugins
|
|
908
|
-
agents plugins install ./my-plugin
|
|
919
|
+
# Add (install) from a git URL or local path — `install` is an alias for `add`
|
|
920
|
+
agents plugins add hivemind@https://github.com/activeloopai/hivemind.git
|
|
909
921
|
agents plugins add ./my-plugin
|
|
910
922
|
|
|
911
923
|
# Apply to one agent (default version) or all supported
|
|
@@ -934,7 +946,7 @@ Plugins that ship `hooks/`, `.mcp.json`, `bin/`, `scripts/`, `settings.json` (no
|
|
|
934
946
|
|
|
935
947
|
```bash
|
|
936
948
|
# Hooks-bearing plugins copy in but stay disabled by default
|
|
937
|
-
agents plugins
|
|
949
|
+
agents plugins add hivemind@https://github.com/activeloopai/hivemind.git \
|
|
938
950
|
--allow-exec-surfaces
|
|
939
951
|
|
|
940
952
|
# Same gate on re-sync (e.g., after upstream updates)
|
|
@@ -1756,7 +1768,7 @@ Agents are defined in [src/lib/agents.ts](cli/src/lib/agents.ts) -- each is a co
|
|
|
1756
1768
|
|---|---|
|
|
1757
1769
|
| [`cli`](cli) | **The CLI** (this README) — version management, config sync, sessions, teams, cloud, browser, computer, secrets. |
|
|
1758
1770
|
| [phnx-labs/agi-ext](https://github.com/phnx-labs/agi-ext) | **AGI EXT** — the VS Code extension (agent terminals as tabs + the Fleet dashboard). Moved to its own repo (RUSH-3189); separate product, own publish identity. |
|
|
1759
|
-
| [`native/computer-mac`](native/computer-mac) · [`native/computer-win`](native/computer-win) | Native backends behind `agents computer` — Swift (macOS Accessibility + screen capture) and C#/.NET (Windows UI Automation). |
|
|
1771
|
+
| [`native/computer-mac`](native/computer-mac) · [`native/computer-win`](native/computer-win) | Native backends behind `agents computer` — Swift (macOS Accessibility + screen capture) and C#/.NET (Windows UI Automation). A third, install-free backend drives a **Linux GUI desktop over VNC/RFB**: `agents computer --vnc <host:port> screenshot` (coordinate-based — screenshot/click/type/key/scroll against any x11vnc/Xvnc server). |
|
|
1760
1772
|
| [`packages/session-tracker`](packages/session-tracker) | The `SessionStart` hook that writes live-session state **AGI EXT** reads back (agi-ext `src/core/liveSession.ts`) — not the CLI, which reads transcripts. |
|
|
1761
1773
|
|
|
1762
1774
|
## Contributing
|
package/dist/bootstrap.js
CHANGED
|
@@ -265,15 +265,15 @@ function maybeWarnMultiInstall() {
|
|
|
265
265
|
for (const info of inventory) {
|
|
266
266
|
console.error(chalk.gray(` ${info.packageRoot} ${info.version} (${info.note})`));
|
|
267
267
|
}
|
|
268
|
-
// RUSH-2705/2713: only advertise `agents
|
|
269
|
-
// really delete. A duplicate
|
|
270
|
-
// OR a pre-1.22.30 copy left alone only because no fixed peer exists — the
|
|
271
|
-
// latter is genuinely vulnerable, not healthy) makes
|
|
272
|
-
//
|
|
268
|
+
// RUSH-2705/2713: only advertise the `agents sync --prune-clis` purge for copies it
|
|
269
|
+
// will really delete. A duplicate sync won't auto-purge (a healthy >=1.22.30
|
|
270
|
+
// peer, OR a pre-1.22.30 copy left alone only because no fixed peer exists — the
|
|
271
|
+
// latter is genuinely vulnerable, not healthy) makes it a remedy that no-ops
|
|
272
|
+
// forever — name the manual removal command instead.
|
|
273
273
|
const peers = inventory.filter((info) => !info.running);
|
|
274
274
|
console.error(chalk.gray('Upgrades apply to the running copy.'));
|
|
275
275
|
if (peers.some((info) => info.autoPurgeable)) {
|
|
276
|
-
console.error(chalk.gray('Purge npx-cache / legacy / pre-1.22.30 copies with: agents
|
|
276
|
+
console.error(chalk.gray('Purge npx-cache / legacy / pre-1.22.30 copies with: agents sync --prune-clis'));
|
|
277
277
|
}
|
|
278
278
|
for (const peer of peers.filter((info) => !info.autoPurgeable)) {
|
|
279
279
|
console.error(chalk.gray(`Remove the ${peer.version} copy at ${peer.packageRoot} with: ${manualUninstallCommand(peer.packageRoot)}`));
|
|
@@ -731,7 +731,7 @@ async function runUpgrade(version, options) {
|
|
|
731
731
|
console.log(chalk.gray(`Purged ${purge.removed.length} stale agents-cli install${purge.removed.length === 1 ? '' : 's'} (npx-cache / legacy / pre-1.22.30).`));
|
|
732
732
|
}
|
|
733
733
|
if (purge.failed.length > 0) {
|
|
734
|
-
console.log(chalk.yellow(`Could not purge ${purge.failed.length} stale install${purge.failed.length === 1 ? '' : 's'}; re-run agents
|
|
734
|
+
console.log(chalk.yellow(`Could not purge ${purge.failed.length} stale install${purge.failed.length === 1 ? '' : 's'}; re-run agents sync --prune-clis.`));
|
|
735
735
|
}
|
|
736
736
|
// RUSH-2705/2713: duplicates --fix won't auto-purge (a healthy
|
|
737
737
|
// >=1.22.30 peer, or a pre-1.22.30 copy with no fixed peer to fall back
|
|
@@ -157,6 +157,10 @@ export const COMMAND_LOADERS = {
|
|
|
157
157
|
monitors: [loadMonitors],
|
|
158
158
|
projects: [loadProjects],
|
|
159
159
|
run: [loadRun],
|
|
160
|
+
// `_callback` is the machine-only agents:// deep-link verb; `open` is its
|
|
161
|
+
// hidden back-compat alias (OS handlers written by older CLIs call it). Both
|
|
162
|
+
// tokens must lazy-load the same module so either resolves. See commands/open.ts.
|
|
163
|
+
_callback: [loadOpen],
|
|
160
164
|
open: [loadOpen],
|
|
161
165
|
reconnect: [loadReconnect],
|
|
162
166
|
fork: [loadFork],
|
|
@@ -167,6 +171,7 @@ export const COMMAND_LOADERS = {
|
|
|
167
171
|
restore: [loadRestore],
|
|
168
172
|
doctor: [loadDoctor],
|
|
169
173
|
route: [loadRoute],
|
|
174
|
+
routes: [loadRoute],
|
|
170
175
|
harness: [loadHarness],
|
|
171
176
|
harnesses: [loadHarness],
|
|
172
177
|
secrets: [loadSecrets],
|
|
@@ -98,7 +98,7 @@ export async function runShareWizard() {
|
|
|
98
98
|
}
|
|
99
99
|
const { input } = await import('@inquirer/prompts');
|
|
100
100
|
const baseUrl = await input({
|
|
101
|
-
message: 'Endpoint base URL (e.g. https://share.
|
|
101
|
+
message: 'Endpoint base URL (e.g. https://share.getrush.ai)',
|
|
102
102
|
validate: (v) => (v.trim().startsWith('http') ? true : 'Enter the full https:// URL of the endpoint.'),
|
|
103
103
|
});
|
|
104
104
|
await runShareJoin(baseUrl.trim());
|
|
@@ -31,7 +31,7 @@ export function registerArtifactsCommands(program) {
|
|
|
31
31
|
|
|
32
32
|
# Or provision your own Cloudflare endpoint (or join a teammate's)
|
|
33
33
|
agents artifacts setup
|
|
34
|
-
agents artifacts share join https://share.
|
|
34
|
+
agents artifacts share join https://share.getrush.ai
|
|
35
35
|
|
|
36
36
|
# What's live in your namespace, and taking one down
|
|
37
37
|
agents artifacts share list
|
package/dist/commands/auth.js
CHANGED
|
@@ -26,7 +26,13 @@ async function login() {
|
|
|
26
26
|
await sleep(interval);
|
|
27
27
|
const poll = await pollDeviceToken(grant.device_code);
|
|
28
28
|
if (poll.status === 'authorized') {
|
|
29
|
-
|
|
29
|
+
const avatarUrl = poll.user.avatar_url ?? poll.user.picture;
|
|
30
|
+
writeSession({
|
|
31
|
+
access_token: poll.access_token,
|
|
32
|
+
email: poll.user.email,
|
|
33
|
+
userId: poll.user.id,
|
|
34
|
+
...(avatarUrl ? { avatarUrl } : {}),
|
|
35
|
+
});
|
|
30
36
|
console.log(chalk.green(`\n Signed in as ${poll.user.email}.`));
|
|
31
37
|
return;
|
|
32
38
|
}
|
package/dist/commands/browser.js
CHANGED
|
@@ -3,13 +3,13 @@ import chalk from 'chalk';
|
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
|
-
import { listProfiles, getProfile, createProfile, deleteProfile, getConfiguredDefaultProfileName, resolveProfileRef, resolveProfileRefForStart, getProfileRuntimeDir, extractConfiguredPort, findFreeProfilePort, getEndpointPresets, formatProfilesTable, editProfile, renameProfile, assertRegistrableProfileName, isProfileLaunchableHere, } from '../lib/browser/profiles.js';
|
|
6
|
+
import { listProfiles, getProfile, createProfile, deleteProfile, getConfiguredDefaultProfileName, resolveProfileRef, resolveProfileRefForStart, getProfileRuntimeDir, extractConfiguredPort, findFreeProfilePort, getEndpointPresets, formatProfilesTable, editProfile, renameProfile, assertRegistrableProfileName, isProfileLaunchableHere, isAttachOnlyProfile, resolveProfileDataDir, normalizeDataDir, } from '../lib/browser/profiles.js';
|
|
7
7
|
import { declaringDevices, migrateCentralBrowserProfiles, profileKind } from '../lib/browser/registry.js';
|
|
8
8
|
import { resolveActor } from '../lib/actor.js';
|
|
9
9
|
import { loginsForProfile, profilesLoggedInto, serviceForUrl, loginsWithAccountsForProfile, accountsForProfile, credKeysForService, AUTH_SIGNATURES, } from '../lib/browser/login-detection.js';
|
|
10
10
|
import { parseSecretRef } from '../lib/browser/secret-ref.js';
|
|
11
11
|
import { readAndResolveBundleEnv, bundleExists, readBundle, describeBundle } from '../lib/secrets/bundles.js';
|
|
12
|
-
import { findBrowserPath, getPortOccupant, isLauncherScript, listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
12
|
+
import { findBrowserPath, getPortOccupant, getProcessUserDataDir, isLauncherScript, listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
13
13
|
import { listProfileCacheDirs, removeProfileCache, listAllProfileSnapshots, buildProfilePrunePlan, pruneProfiles, PRUNE_REASON_TEXT, identityLoopbackMismatch, } from '../lib/browser/runtime-state.js';
|
|
14
14
|
import { DEFAULT_VIEWPORT, parseWindowSize, parseWindowPosition } from '../lib/browser/devices.js';
|
|
15
15
|
import { runBrowserSessionsCommand } from './browser-sessions-picker.js';
|
|
@@ -610,6 +610,8 @@ function registerProfilesCommands(browser) {
|
|
|
610
610
|
.option('--binary <path>', 'Absolute path to the browser/app binary (required with --browser custom)')
|
|
611
611
|
.option('--electron', 'Treat this profile as an Electron desktop app: never call Target.createTarget; bind to the visible window using --target-filter or the skip-invisible heuristic')
|
|
612
612
|
.option('--target-filter <expr>', 'Pick the existing CDP page target to drive (Electron apps, and Arc — which reuses an open tab / Space rather than creating one). Format: url:<substring> or title:<substring>')
|
|
613
|
+
.option('--attach-only', 'Never spawn a rival window: attach to a browser you already started with remote debugging, else fail loud with a relaunch hint (PHNX-3967). The model for a canonical signed-in Comet; Arc is always attach-only. Pairs with a durable --user-data-dir.')
|
|
614
|
+
.option('--user-data-dir <path>', "Absolute durable --user-data-dir for this profile's browser. Default for attach-only: ~/.agents/.history/browser-profiles/<name>/chrome-data (outside .cache, so a one-time sign-in survives relaunch).")
|
|
613
615
|
.action(async (name, opts) => {
|
|
614
616
|
try {
|
|
615
617
|
assertRegistrableProfileName(name);
|
|
@@ -676,6 +678,10 @@ function registerProfilesCommands(browser) {
|
|
|
676
678
|
viewport.x = pos.x;
|
|
677
679
|
viewport.y = pos.y;
|
|
678
680
|
}
|
|
681
|
+
if (opts.userDataDir && !path.isAbsolute(String(opts.userDataDir))) {
|
|
682
|
+
console.error('--user-data-dir must be an absolute path');
|
|
683
|
+
process.exit(1);
|
|
684
|
+
}
|
|
679
685
|
const profile = {
|
|
680
686
|
name,
|
|
681
687
|
description: opts.description,
|
|
@@ -684,6 +690,8 @@ function registerProfilesCommands(browser) {
|
|
|
684
690
|
electron: opts.electron || undefined,
|
|
685
691
|
targetFilter: opts.targetFilter,
|
|
686
692
|
endpoints,
|
|
693
|
+
launchPolicy: opts.attachOnly ? 'attach-only' : undefined,
|
|
694
|
+
userDataDir: opts.userDataDir ? String(opts.userDataDir) : undefined,
|
|
687
695
|
secrets: opts.secrets,
|
|
688
696
|
chrome: opts.headless ? { headless: true } : undefined,
|
|
689
697
|
viewport,
|
|
@@ -693,6 +701,15 @@ function registerProfilesCommands(browser) {
|
|
|
693
701
|
console.log(port !== undefined
|
|
694
702
|
? `Added "${name}" on ${machineId()} (port ${port}).`
|
|
695
703
|
: `Added "${name}" on ${machineId()}.`);
|
|
704
|
+
// Attach-only profiles never launch a browser — tell the user the one-time
|
|
705
|
+
// relaunch that makes their canonical instance attachable, pinned to the
|
|
706
|
+
// durable data dir so the sign-in persists (PHNX-3967).
|
|
707
|
+
if (isAttachOnlyProfile(profile) && profile.browser !== 'arc' && port !== undefined) {
|
|
708
|
+
const app = profile.browser === 'comet' ? 'Comet' : profile.browser;
|
|
709
|
+
console.log(`attach-only: agents will not spawn ${app}. Start the canonical one once with:\n` +
|
|
710
|
+
` open -a ${app} --args --remote-debugging-port=${port} --user-data-dir=${resolveProfileDataDir(profile)}\n` +
|
|
711
|
+
`Sign in in that window; the data dir is durable so the login survives quit+relaunch.`);
|
|
712
|
+
}
|
|
696
713
|
// Warn (don't fail) if the declared secrets bundle doesn't exist yet — it
|
|
697
714
|
// may be created later, but a typo should surface now.
|
|
698
715
|
if (opts.secrets && !bundleExists(opts.secrets)) {
|
|
@@ -717,6 +734,9 @@ function registerProfilesCommands(browser) {
|
|
|
717
734
|
.option('--electron', 'Treat this profile as an Electron desktop app')
|
|
718
735
|
.option('--no-electron', 'Stop treating it as an Electron app')
|
|
719
736
|
.option('--target-filter <expr>', "url:<substring> or title:<substring>; consulted on Electron and Arc profiles (pass '' to clear)")
|
|
737
|
+
.option('--attach-only', 'Make this profile attach-only (never spawn a rival window) — PHNX-3967')
|
|
738
|
+
.option('--launch', 'Undo attach-only: let agents launch the browser when nothing is serving CDP on the port')
|
|
739
|
+
.option('--user-data-dir <path>', "Absolute durable --user-data-dir (pass '' to clear back to the default durable dir)")
|
|
720
740
|
.option('--json', 'Output machine-readable JSON')
|
|
721
741
|
.action(async (name, opts) => {
|
|
722
742
|
// The browser type and the name are identity, not settings: both key the
|
|
@@ -734,6 +754,14 @@ function registerProfilesCommands(browser) {
|
|
|
734
754
|
process.exit(1);
|
|
735
755
|
}
|
|
736
756
|
}
|
|
757
|
+
if (opts.attachOnly && opts.launch) {
|
|
758
|
+
console.error('Pass either --attach-only or --launch, not both.');
|
|
759
|
+
process.exit(1);
|
|
760
|
+
}
|
|
761
|
+
if (opts.userDataDir && !path.isAbsolute(String(opts.userDataDir))) {
|
|
762
|
+
console.error('--user-data-dir must be an absolute path');
|
|
763
|
+
process.exit(1);
|
|
764
|
+
}
|
|
737
765
|
const patch = {};
|
|
738
766
|
if (opts.description !== undefined)
|
|
739
767
|
patch.description = opts.description || undefined;
|
|
@@ -745,6 +773,15 @@ function registerProfilesCommands(browser) {
|
|
|
745
773
|
patch.binary = opts.binary;
|
|
746
774
|
if (opts.targetFilter !== undefined)
|
|
747
775
|
patch.targetFilter = opts.targetFilter || undefined;
|
|
776
|
+
// launchPolicy toggle: --attach-only sets it, --launch clears it (back to
|
|
777
|
+
// the default launch behavior). commander leaves each undefined when unset.
|
|
778
|
+
if (opts.attachOnly)
|
|
779
|
+
patch.launchPolicy = 'attach-only';
|
|
780
|
+
else if (opts.launch)
|
|
781
|
+
patch.launchPolicy = undefined;
|
|
782
|
+
// '' clears back to the default durable dir; any other value pins it.
|
|
783
|
+
if (opts.userDataDir !== undefined)
|
|
784
|
+
patch.userDataDir = opts.userDataDir || undefined;
|
|
748
785
|
// commander maps --electron/--no-electron and --headless/--no-headless onto
|
|
749
786
|
// one boolean each, and leaves it undefined when neither was passed.
|
|
750
787
|
if (opts.electron !== undefined)
|
|
@@ -879,6 +916,13 @@ function registerProfilesCommands(browser) {
|
|
|
879
916
|
console.log(`Binary: ${profile.binary}`);
|
|
880
917
|
if (profile.electron)
|
|
881
918
|
console.log(`Electron: true`);
|
|
919
|
+
if (isAttachOnlyProfile(profile)) {
|
|
920
|
+
const why = profile.browser === 'arc' ? ' (Arc is always attach-only)' : '';
|
|
921
|
+
console.log(`Launch policy: attach-only${why} — agents attach, never spawn a rival window`);
|
|
922
|
+
if (profile.browser !== 'arc') {
|
|
923
|
+
console.log(`User-data-dir: ${resolveProfileDataDir(profile)} (durable)`);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
882
926
|
if (profile.targetFilter)
|
|
883
927
|
console.log(`Target filter: ${profile.targetFilter}`);
|
|
884
928
|
if (profile.description)
|
|
@@ -1049,6 +1093,17 @@ function registerProfilesCommands(browser) {
|
|
|
1049
1093
|
\`prune\` only considers profiles this device declares. It never removes a
|
|
1050
1094
|
profile that is in use, the configured default, or the \`auto-chrome\`
|
|
1051
1095
|
profile a setup wizard created.
|
|
1096
|
+
|
|
1097
|
+
Arc is your PERSONAL browser and Comet is what agents drive (PHNX-3967).
|
|
1098
|
+
Arc is single-instance with no debug port, so agents can only attach to a
|
|
1099
|
+
running Arc, never launch one — it stays yours. Point agents at Comet as
|
|
1100
|
+
one canonical signed-in profile that never spawns a second window:
|
|
1101
|
+
agents browser profiles create agents-comet --browser comet --attach-only
|
|
1102
|
+
An \`--attach-only\` profile (and Arc, always) attaches to a browser you
|
|
1103
|
+
already started with remote debugging and fails loud with the relaunch
|
|
1104
|
+
command otherwise, pinned to a durable --user-data-dir so the sign-in
|
|
1105
|
+
survives quit+relaunch. A foreign browser squatting the canonical port is
|
|
1106
|
+
rejected, not driven — \`agents browser profiles doctor <name>\` flags it.
|
|
1052
1107
|
`,
|
|
1053
1108
|
});
|
|
1054
1109
|
profiles
|
|
@@ -1157,12 +1212,47 @@ function registerProfilesCommands(browser) {
|
|
|
1157
1212
|
try {
|
|
1158
1213
|
const { browser } = await discoverBrowserWsUrl(port, 'localhost', profile.name);
|
|
1159
1214
|
verifyBrowserIdentity(browser, profile.browser, port);
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1215
|
+
// Port-squat check (PHNX-3967): for an attach-only profile, a browser
|
|
1216
|
+
// of the right FAMILY on the canonical port is not enough — confirm
|
|
1217
|
+
// its --user-data-dir is this profile's durable dir. A logged-out
|
|
1218
|
+
// /tmp Comet answering CDP here would otherwise pass as ready and get
|
|
1219
|
+
// driven as if it were the credentialed browser.
|
|
1220
|
+
const expectedDir = resolveProfileDataDir(profile);
|
|
1221
|
+
const ownershipChecked = isAttachOnlyProfile(profile) && profile.browser !== 'arc';
|
|
1222
|
+
const runningDir = ownershipChecked ? getProcessUserDataDir(occupant.pid) : null;
|
|
1223
|
+
if (runningDir && normalizeDataDir(runningDir) !== normalizeDataDir(expectedDir)) {
|
|
1224
|
+
checks.push({
|
|
1225
|
+
label: 'port',
|
|
1226
|
+
ok: false,
|
|
1227
|
+
detail: `${port} serving ${browser} (pid ${occupant.pid}) but from a FOREIGN ` +
|
|
1228
|
+
`user-data-dir ${runningDir} (expected ${expectedDir}). This is a ` +
|
|
1229
|
+
`port-squatter, not this profile's browser — agents will refuse to drive ` +
|
|
1230
|
+
`it. Close it (\`kill ${occupant.pid}\`) and relaunch the canonical browser ` +
|
|
1231
|
+
`with --user-data-dir=${expectedDir}.`,
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
else if (ownershipChecked && !runningDir) {
|
|
1235
|
+
// Attach-only, an occupant is serving CDP, but its --user-data-dir
|
|
1236
|
+
// couldn't be read — ownership is UNVERIFIED. Surface it rather than
|
|
1237
|
+
// report a silent green, matching the runtime guard which refuses to
|
|
1238
|
+
// attach to an unverifiable instance (PHNX-3967).
|
|
1239
|
+
checks.push({
|
|
1240
|
+
label: 'port',
|
|
1241
|
+
ok: false,
|
|
1242
|
+
detail: `${port} serving ${browser} (pid ${occupant.pid}) but its --user-data-dir ` +
|
|
1243
|
+
`could not be read, so ownership can't be confirmed. agents will refuse to ` +
|
|
1244
|
+
`attach to an unverified instance. Relaunch the canonical browser with ` +
|
|
1245
|
+
`--user-data-dir=${expectedDir} so the attach-only guard can verify it.`,
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
checks.push({
|
|
1250
|
+
label: 'port',
|
|
1251
|
+
ok: true,
|
|
1252
|
+
detail: `${port} serving ${browser} (pid ${occupant.pid})`,
|
|
1253
|
+
});
|
|
1254
|
+
attachingToExistingBrowser = true;
|
|
1255
|
+
}
|
|
1166
1256
|
}
|
|
1167
1257
|
catch (err) {
|
|
1168
1258
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1174,8 +1264,12 @@ function registerProfilesCommands(browser) {
|
|
|
1174
1264
|
}
|
|
1175
1265
|
}
|
|
1176
1266
|
}
|
|
1177
|
-
// 3. User-data-dir exists and is writable
|
|
1178
|
-
|
|
1267
|
+
// 3. User-data-dir exists and is writable. An attach-only profile's login
|
|
1268
|
+
// lives in the DURABLE dir (outside .cache) the canonical browser is
|
|
1269
|
+
// launched with; a launch-policy profile uses the managed cache dir.
|
|
1270
|
+
const userDataDir = isAttachOnlyProfile(profile) && profile.browser !== 'arc'
|
|
1271
|
+
? resolveProfileDataDir(profile)
|
|
1272
|
+
: path.join(getProfileRuntimeDir(name), 'chrome-data');
|
|
1179
1273
|
try {
|
|
1180
1274
|
if (!fs.existsSync(userDataDir)) {
|
|
1181
1275
|
checks.push({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withAliases } from '../lib/verbs.js';
|
|
1
2
|
import chalk from 'chalk';
|
|
2
3
|
import ora from 'ora';
|
|
3
4
|
import * as fs from 'fs';
|
|
@@ -42,8 +43,8 @@ When to use:
|
|
|
42
43
|
- New version: 'agents commands add --agents claude@2.1.112' to carry commands forward
|
|
43
44
|
- Custom tooling: write a command markdown file, test it, then share via 'agents commands add ~/my-cmd.md'
|
|
44
45
|
`);
|
|
45
|
-
commandsCmd
|
|
46
|
-
.command('list [agent]')
|
|
46
|
+
withAliases(commandsCmd
|
|
47
|
+
.command('list [agent]'), 'list')
|
|
47
48
|
.option('--json', 'Emit machine-readable JSON instead of the table/picker')
|
|
48
49
|
.description('Show which slash commands are installed and which agent versions they are synced to')
|
|
49
50
|
.option('-a, --agent <agent>', 'Filter to a specific agent (alternative to positional arg)')
|
|
@@ -265,8 +266,8 @@ Examples:
|
|
|
265
266
|
process.exit(1);
|
|
266
267
|
}
|
|
267
268
|
});
|
|
268
|
-
commandsCmd
|
|
269
|
-
.command('remove [name]')
|
|
269
|
+
withAliases(commandsCmd
|
|
270
|
+
.command('remove [name]'), 'remove')
|
|
270
271
|
.description('Delete a command from agents (interactive picker if no name given)')
|
|
271
272
|
.option('-a, --agents <list>', 'Limit removal to specific agents (e.g., claude,codex)')
|
|
272
273
|
.addHelpText('after', `
|
|
@@ -405,8 +406,8 @@ Examples:
|
|
|
405
406
|
console.error(chalk.gray('Use: agents prune cleanup commands (or `agents prune cleanup` for everything)'));
|
|
406
407
|
process.exit(1);
|
|
407
408
|
});
|
|
408
|
-
commandsCmd
|
|
409
|
-
.command('view [name]')
|
|
409
|
+
withAliases(commandsCmd
|
|
410
|
+
.command('view [name]'), 'view')
|
|
410
411
|
.description('Read the full content of a command file with markdown rendering')
|
|
411
412
|
.addHelpText('after', `
|
|
412
413
|
Examples:
|
|
@@ -10,6 +10,7 @@ import { resolveHelperExec, resolveSocketPath } from '../lib/computer/computer-r
|
|
|
10
10
|
export declare function shouldBlockOffPlatform(opts: {
|
|
11
11
|
platform: NodeJS.Platform;
|
|
12
12
|
tcpConfigured: boolean;
|
|
13
|
+
vncConfigured?: boolean;
|
|
13
14
|
device?: string;
|
|
14
15
|
}): boolean;
|
|
15
16
|
/**
|