@phnx-labs/agents-cli 1.20.88 → 1.20.89
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 +263 -0
- package/README.md +9 -1
- package/dist/bin/agents +0 -0
- package/dist/commands/commands.js +7 -7
- package/dist/commands/factory.js +26 -2
- package/dist/commands/funnel.js +16 -1
- package/dist/commands/menubar.js +117 -34
- package/dist/commands/routines.js +23 -1
- package/dist/commands/secrets-rotate-passphrase.d.ts +17 -0
- package/dist/commands/secrets-rotate-passphrase.js +96 -0
- package/dist/commands/secrets.js +2 -0
- package/dist/commands/sessions.d.ts +7 -1
- package/dist/commands/sessions.js +39 -12
- package/dist/commands/webhook.js +7 -2
- package/dist/lib/commands.js +9 -1
- package/dist/lib/daemon.d.ts +29 -0
- package/dist/lib/daemon.js +58 -4
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/factory/snapshot.d.ts +78 -0
- package/dist/lib/factory/snapshot.js +209 -0
- package/dist/lib/fs-atomic.d.ts +14 -1
- package/dist/lib/fs-atomic.js +35 -3
- package/dist/lib/funnel.d.ts +1 -0
- package/dist/lib/funnel.js +8 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +2 -2
- package/dist/lib/menubar/install-menubar.d.ts +53 -2
- package/dist/lib/menubar/install-menubar.js +183 -28
- package/dist/lib/platform/process.d.ts +2 -0
- package/dist/lib/platform/process.js +5 -3
- package/dist/lib/resources.d.ts +8 -0
- package/dist/lib/resources.js +34 -1
- package/dist/lib/routines-placement.d.ts +2 -1
- package/dist/lib/routines-placement.js +8 -4
- package/dist/lib/routines.d.ts +57 -1
- package/dist/lib/routines.js +74 -1
- package/dist/lib/runner.d.ts +2 -0
- package/dist/lib/runner.js +21 -8
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/bundles.js +9 -34
- package/dist/lib/secrets/filestore.d.ts +152 -34
- package/dist/lib/secrets/filestore.js +676 -123
- package/dist/lib/session/remote-active.d.ts +4 -1
- package/dist/lib/session/remote-active.js +8 -2
- package/dist/lib/session/viewing-in.d.ts +31 -0
- package/dist/lib/session/viewing-in.js +47 -0
- package/dist/lib/state.d.ts +17 -0
- package/dist/lib/state.js +30 -2
- package/dist/lib/triggers/handlers.d.ts +95 -0
- package/dist/lib/triggers/handlers.js +384 -0
- package/dist/lib/triggers/webhook.d.ts +10 -2
- package/dist/lib/triggers/webhook.js +65 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,268 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.20.89
|
|
4
|
+
|
|
5
|
+
- **Webhook handler layer for one-off agent/workflow/command/routine triggers.**
|
|
6
|
+
Routines still fire from signed webhooks, but a new `~/.agents/webhooks/*.yml`
|
|
7
|
+
layer can also run one-off actions: `run.agent`, `run.workflow`, `run.command`,
|
|
8
|
+
or delegate to an existing `routine`. Handlers support the same source/event/
|
|
9
|
+
action/label/repo/branch filters as routine triggers, plus Linear
|
|
10
|
+
`stateTo`/`stateFrom` state-change filters. Prompts and commands can use
|
|
11
|
+
`{{issue.identifier}}`, `{{updatedFrom.state.name}}`, etc. The receiver emits
|
|
12
|
+
`webhook.received`, `webhook.authorized`, `webhook.rejected`, `webhook.matched`,
|
|
13
|
+
`webhook.fired`, `webhook.handler.start`, and `webhook.handler.end` events.
|
|
14
|
+
Source: `apps/cli/src/lib/triggers/handlers.ts`,
|
|
15
|
+
`apps/cli/src/lib/triggers/webhook.ts`, `apps/cli/src/lib/routines.ts`,
|
|
16
|
+
`apps/cli/src/commands/routines.ts`, `apps/cli/docs/03-routines.md`.
|
|
17
|
+
|
|
18
|
+
- **`agents routines add` gains `--state-to` and `--state-from` filters for Linear
|
|
19
|
+
triggers.** A Linear routine or handler can now fire only on a specific state
|
|
20
|
+
transition (for example `--state-to Plan`), instead of on every issue update.
|
|
21
|
+
|
|
22
|
+
- **Values substituted into `run.command` are shell-quoted.** A webhook context is
|
|
23
|
+
built from an external payload, and fields like `issue.title` or a GitHub
|
|
24
|
+
`pull_request` title are free text any outside contributor can set — pasted raw
|
|
25
|
+
into a shell command they would be a command-injection sink. Substituted values
|
|
26
|
+
are now single-quoted (POSIX `sh`), so a payload stays one inert argument while
|
|
27
|
+
the operator's own template keeps its pipes, redirects, and `&&`. On Windows,
|
|
28
|
+
where `exec` runs through `cmd.exe` and these quoting rules do not hold, a
|
|
29
|
+
`run.command` containing `{{…}}` is refused with a clear error rather than run.
|
|
30
|
+
`run.prompt` is unaffected — it never reaches a shell.
|
|
31
|
+
Source: `apps/cli/src/lib/routines.ts` (`substituteWebhookCommand`,
|
|
32
|
+
`assertShellSubstitutionSupported`), `apps/cli/src/lib/triggers/handlers.ts`.
|
|
33
|
+
|
|
34
|
+
- **The `Cmd-Shift-O` quick-dispatch bar now lists the repo's open Linear tickets,
|
|
35
|
+
and dispatches one on a click (RUSH-2098).** The panel only captured NEW work;
|
|
36
|
+
it now also shows what already exists. Switching the repo dropdown switches the
|
|
37
|
+
Linear project (the repo name is matched against `linear projects` reduced to
|
|
38
|
+
lowercase alphanumerics, so `agents-cli` finds "Agents CLI" with nothing to
|
|
39
|
+
configure; a worktree resolves to its parent repo, and a repo that matches no
|
|
40
|
+
project says so and lets you pick one, remembered per repo). Rows are ranked
|
|
41
|
+
urgent-first — Linear priority, then overdue, then in progress, then newest —
|
|
42
|
+
and typing filters them, so an existing ticket surfaces before Return files a
|
|
43
|
+
duplicate. Clicking a row (or `⌘1`–`⌘5`) dispatches that ticket to the selected
|
|
44
|
+
agents in the picked repo: **Run** claims it and implements it, **Plan** posts a
|
|
45
|
+
plan as a ticket comment. `⌘`-click opens it in Linear instead. The list renders
|
|
46
|
+
from a 90-second warm cache so the panel still appears instantly. Source:
|
|
47
|
+
`apps/cli/menubar/Sources/MenubarHelper/LinearTickets.swift`,
|
|
48
|
+
`apps/cli/menubar/Sources/MenubarHelper/PromptPanel.swift`,
|
|
49
|
+
`apps/cli/menubar/Sources/MenubarHelper/AgentsCLI.swift`.
|
|
50
|
+
|
|
51
|
+
- **Fixed: a menu-bar dispatch whose child printed more than ~64 KiB hung forever
|
|
52
|
+
and never notified.** The helper read a monitored child's stdout only from the
|
|
53
|
+
process-termination handler, so a child that filled the pipe buffer blocked on
|
|
54
|
+
write, never exited, and the completion callback never fired — two `linear`
|
|
55
|
+
processes were left wedged by a single ticket fetch. Both monitored paths (the
|
|
56
|
+
ticket agent and `linear create`) now drain stdout, and feed stdin, on a
|
|
57
|
+
background queue while the child runs. Source:
|
|
58
|
+
`apps/cli/menubar/Sources/MenubarHelper/AgentsCLI.swift`.
|
|
59
|
+
|
|
60
|
+
- **The daemon warns when it was launched from an ephemeral root.** A daemon
|
|
61
|
+
started from a temp dir (`/tmp`, `/var/folders`, `/dev/shm`) or a git worktree
|
|
62
|
+
resolves its own job modules by dynamic `import()` rooted at the launch entry
|
|
63
|
+
(`getAgentsBinPath` → `process.argv[1]`). When that directory is later removed
|
|
64
|
+
— a `/tmp` cleanup, a review/verify checkout teardown, `git worktree remove` —
|
|
65
|
+
the long-lived daemon keeps ENOENT-ing on every routine's imports
|
|
66
|
+
(`auto-dispatch.ts`, `routines-placement.ts`, `devices/fleet.ts`), silently
|
|
67
|
+
wedging until restart. `anchorDaemonCwd` already rescues the cwd, but nothing
|
|
68
|
+
can re-root a deleted module tree. `runDaemon` now calls
|
|
69
|
+
`warnEphemeralDaemonRoot` at startup, so the risk is logged the moment the
|
|
70
|
+
daemon comes up — including a direct `agents __daemon-run` that never passes
|
|
71
|
+
through the launch-time `validateDaemonBinary` check. That launch-time check is
|
|
72
|
+
also broadened from git-worktree-only to any ephemeral root via the shared
|
|
73
|
+
`describeEphemeralDaemonRoot` predicate. The fix for a wedged daemon is
|
|
74
|
+
unchanged: run it from the globally installed binary
|
|
75
|
+
(`npm i -g @phnx-labs/agents-cli`) so its entry roots at a stable version home.
|
|
76
|
+
Source: `apps/cli/src/lib/daemon.ts`
|
|
77
|
+
(`describeEphemeralDaemonRoot`, `warnEphemeralDaemonRoot`, `validateDaemonBinary`).
|
|
78
|
+
|
|
79
|
+
- **A `README.md` / `AGENTS.md` sitting in a resource directory is no longer
|
|
80
|
+
installed as a resource.** `listResources` skipped only dotfiles, so every `.md`
|
|
81
|
+
beside the actual resources was materialized as one: `commands/README.md` — which
|
|
82
|
+
the system repo has shipped for months — installed a bogus `/README` slash command
|
|
83
|
+
into every agent home, and adding per-directory `AGENTS.md` docs would have added
|
|
84
|
+
`/AGENTS`, `/CLAUDE`, and `/GEMINI` alongside it. `README`, `AGENTS`, `CLAUDE`, and
|
|
85
|
+
`GEMINI` are now filtered from both `listResources` and `resolveResource` for every
|
|
86
|
+
kind **except `rules`**, where `AGENTS.md` *is* the resource (the composed ruleset
|
|
87
|
+
that syncs as each agent's memory file). The check tests `!entry.isDirectory()`
|
|
88
|
+
rather than `isFile()`, because a `Dirent` for a symlink reports
|
|
89
|
+
`isFile() === false` and `CLAUDE.md`/`GEMINI.md` are symlinks to `AGENTS.md` by
|
|
90
|
+
convention — a resource *directory* named `agents/` is still a real resource.
|
|
91
|
+
Verified against the real installed layers: 30 commands with `README` leaking
|
|
92
|
+
before, 29 with none after.
|
|
93
|
+
- **`agents commands list` and the command picker no longer offer a name that
|
|
94
|
+
cannot be opened.** `listCentralCommands` and `discoverCommands`
|
|
95
|
+
(`src/lib/commands.ts`) run their own `readdirSync` scans rather than going
|
|
96
|
+
through `listResources`, so they kept offering `README` while
|
|
97
|
+
`agents commands view README` answered "not found" — a listed-but-unopenable
|
|
98
|
+
name. Both now share the one exported `isDirectoryDoc` predicate, so every
|
|
99
|
+
enumerator agrees. Verified: 27 names with `README` before, 26 with none after.
|
|
100
|
+
- **`agents commands add/remove/view` no longer suggest `README` as the example
|
|
101
|
+
command name.** With `README` reserved as a directory doc, the six hardcoded
|
|
102
|
+
examples in the help text and non-interactive hints named a command that can never
|
|
103
|
+
exist. They now use `plan`, which actually ships.
|
|
104
|
+
|
|
105
|
+
- **File-backed secrets bundles no longer require `AGENTS_SECRETS_PASSPHRASE` on
|
|
106
|
+
macOS.** The encrypted file store now silently auto-provisions a stable
|
|
107
|
+
machine-local key (a 0600 file under `~/.agents/.secrets-key/`, kept outside the
|
|
108
|
+
encrypted store) on first use on **every** platform, macOS included — no prompt,
|
|
109
|
+
no Touch ID, nothing to set or remember. Previously a file-backed bundle on a Mac
|
|
110
|
+
hard-failed unless `AGENTS_SECRETS_PASSPHRASE` was exported, which blocked
|
|
111
|
+
headless reads (e.g. the `auth` bundle the usage/auth reader consults) and
|
|
112
|
+
frequently hung. Setting `AGENTS_SECRETS_PASSPHRASE` still works and takes
|
|
113
|
+
precedence — use it to hold the key off disk or to share one bundle's ciphertext
|
|
114
|
+
across boxes under a common key. Source: `apps/cli/src/lib/secrets/filestore.ts`,
|
|
115
|
+
`apps/cli/src/lib/secrets/bundles.ts`.
|
|
116
|
+
|
|
117
|
+
- **Menu-bar & daemon notifications now use the current agents-cli mark, not the
|
|
118
|
+
legacy logo.** A desktop notification from the menu-bar helper or the routines
|
|
119
|
+
daemon showed the old `assets/logo.png` gradient "A" — outdated, and blank in the
|
|
120
|
+
notification's left-hand app-icon slot. `MenubarHelper.app`'s `AppIcon.icns` is
|
|
121
|
+
now generated from the current brand mark (`assets/app-icon.svg` → `app-icon.png`:
|
|
122
|
+
the lime-tile lowercase `a` shared with the agi-cli web favicon and the menu-bar
|
|
123
|
+
glyph), which drives both the notification's right-hand `contentImage` and its
|
|
124
|
+
left-hand app icon. The installer also registers the bundle with LaunchServices
|
|
125
|
+
(`lsregister -f`) at its `~/Library/Application Support` path so the OS can resolve
|
|
126
|
+
that app icon. Source: `apps/cli/menubar/scripts/build.sh`,
|
|
127
|
+
`apps/cli/src/lib/menubar/install-menubar.ts`, `assets/app-icon.svg`.
|
|
128
|
+
|
|
129
|
+
- **The menu bar is a single instance, always.** Two copies of the helper could
|
|
130
|
+
run at once — launchd's `KeepAlive` service plus a LaunchServices/`open` launch
|
|
131
|
+
of the same `.app` — putting two agents marks in the menu bar, and the second
|
|
132
|
+
copy could hold `Cmd-Shift-V`/`Cmd-Shift-O` (`RegisterEventHotKey` is
|
|
133
|
+
first-come). The helper now takes an `flock` on
|
|
134
|
+
`~/.agents/.cache/state/menubar.lock` at launch and holds it for its lifetime;
|
|
135
|
+
a helper that cannot take the lock pops the **running** helper's menu open and
|
|
136
|
+
exits 0, since re-launching a menu-bar app means "show me the one I already
|
|
137
|
+
have". An `flock` rather than a pid file: the kernel releases it when the
|
|
138
|
+
holder dies, so a `SIGKILL`ed helper cannot leave a stale "already running"
|
|
139
|
+
that blocks every later launch. Source:
|
|
140
|
+
`apps/cli/menubar/Sources/MenubarHelper/SingleInstance.swift`,
|
|
141
|
+
`apps/cli/menubar/Sources/MenubarHelper/StatusItemController.swift`.
|
|
142
|
+
|
|
143
|
+
- **`agents menubar setup` configures the menu bar end-to-end.** One idempotent
|
|
144
|
+
command for a machine that is wrong — never configured, helper down, or showing
|
|
145
|
+
a duplicate icon. It ends every running helper, installs/refreshes the bundle,
|
|
146
|
+
checks its code signature, writes the launchd login item (`RunAtLoad` +
|
|
147
|
+
`KeepAlive`), clears a previous `agents menubar disable`, and verifies exactly
|
|
148
|
+
one helper came back up — reporting each as its own step and exiting nonzero if
|
|
149
|
+
it cannot reach that state. `--check` reports without changing; `--json` emits
|
|
150
|
+
the step list. Source: `apps/cli/src/commands/menubar.ts`,
|
|
151
|
+
`apps/cli/src/lib/menubar/install-menubar.ts`.
|
|
152
|
+
|
|
153
|
+
- **`agents menubar status` now shows a duplicate.** Live helper processes were
|
|
154
|
+
collapsed to a boolean `running`, so two copies of the *installed* bundle — the
|
|
155
|
+
duplicate a user actually sees — reported as healthy. `--json` now carries an
|
|
156
|
+
`instances` array (copies of the installed bundle) beside the existing
|
|
157
|
+
`foreignInstances`, and the text readout names every extra pid and points at
|
|
158
|
+
`agents menubar setup`. Source:
|
|
159
|
+
`apps/cli/src/lib/menubar/install-menubar.ts` (`classifyMenubarProcesses`).
|
|
160
|
+
|
|
161
|
+
- **Quick-dispatch ticket list: one-row filter + sort, and a scrollable list.**
|
|
162
|
+
The ticket controls sit on a single row of popups next to the Linear project
|
|
163
|
+
(project · filter · sort) — not a chip matrix or two-column block. Quick filter
|
|
164
|
+
options: All open, Todo, Doing, Backlog, P1 only, P2 only, Overdue. Quick sort
|
|
165
|
+
options: Urgent first, Newest, Oldest, Due date, Priority (flat list, no
|
|
166
|
+
status grouping). Filter and sort picks are remembered across summons. Ticket
|
|
167
|
+
rows scroll inside a fixed viewport so more than five matches stay reachable
|
|
168
|
+
without growing the panel. Source:
|
|
169
|
+
`apps/cli/menubar/Sources/MenubarHelper/LinearTickets.swift`,
|
|
170
|
+
`apps/cli/menubar/Sources/MenubarHelper/PromptPanel.swift`.
|
|
171
|
+
|
|
172
|
+
- **`release.sh` now takes a release lease, and refuses to bump past an
|
|
173
|
+
unpublished tag.** Releases run from whichever fleet box an agent happens to be
|
|
174
|
+
on, so two agents could enter the pipeline at once; the collision only surfaced
|
|
175
|
+
at the publish gate (`merged tree != built tree -- refusing to publish`), after
|
|
176
|
+
one run had already merged and tagged, leaving the version merged but unshipped.
|
|
177
|
+
A new `scripts/release-lease.sh` holds mutual exclusion on `origin` as an orphan
|
|
178
|
+
commit at `refs/release-lock/held` — a second claimant's push can never be a
|
|
179
|
+
fast-forward, so git's rejection *is* the failed lock acquisition. The lease is
|
|
180
|
+
claimed before the first mutation and dropped by the existing cleanup trap on
|
|
181
|
+
every exit path. Because a healthy release routinely outlives any sane
|
|
182
|
+
expiry — the CI matrix alone has run 57 minutes and release 1.20.77 took 186
|
|
183
|
+
minutes — the lease is **renewed** by a background renewer for the whole run,
|
|
184
|
+
and the squash-merge, the tag, and the publish each **verify** ownership first,
|
|
185
|
+
failing closed if it can no longer be proven. A lease that stops being renewed
|
|
186
|
+
is reclaimable after 30 minutes, and reclaiming names the dead holder instead
|
|
187
|
+
of silently overwriting it. Separately,
|
|
188
|
+
`release.sh` now refuses to cut a new version while an older `v*` tag exists
|
|
189
|
+
that npm never received, and points at the re-run that finishes it — bumping
|
|
190
|
+
past an unpublished tag is what turned a one-version gap into npm 1.20.78 vs
|
|
191
|
+
main 1.20.81. Source: `apps/cli/scripts/release-lease.sh`,
|
|
192
|
+
`apps/cli/scripts/release.sh`.
|
|
193
|
+
|
|
194
|
+
- **`agents funnel down` disables a public Funnel port from the same wrapper used
|
|
195
|
+
to enable ingress.** Webhook ingress now has a complete local receiver runbook:
|
|
196
|
+
keep GitHub/Linear signing keys in `agents secrets`, bind the receiver to
|
|
197
|
+
`127.0.0.1`, expose it with `agents funnel up`, rotate one source secret at a
|
|
198
|
+
time, and turn the public port off with `agents funnel down` before stopping or
|
|
199
|
+
moving the receiver. Source: `apps/cli/src/commands/funnel.ts`,
|
|
200
|
+
`apps/cli/src/lib/funnel.ts`, `apps/cli/docs/03-routines.md`.
|
|
201
|
+
|
|
202
|
+
- **New `agents secrets rotate-passphrase` re-keys the encrypted file store under
|
|
203
|
+
a new master passphrase, atomically (RUSH-1975).** Until now there was no
|
|
204
|
+
supported way to rotate the file-store passphrase — `rekey` only renames macOS
|
|
205
|
+
keychain service names and `rotate <bundle> <key>` replaces a single secret
|
|
206
|
+
value, so a leaked passphrase (RUSH-1968) could only be remediated by a
|
|
207
|
+
hand-rolled non-atomic script or an export-to-plaintext round-trip (the exact
|
|
208
|
+
exposure being fixed). The new command decrypts every `<item>.enc` under the
|
|
209
|
+
current key, re-encrypts under a freshly generated one, and swaps both the
|
|
210
|
+
ciphertext and the 0600 key file by directory rename after verifying every item
|
|
211
|
+
round-trips. A crash at any point self-heals on the next *rotate* run to a single
|
|
212
|
+
readable store — content-aware recovery probes which key actually decrypts the
|
|
213
|
+
live store (not merely which files are present) and classifies the WHOLE store:
|
|
214
|
+
it completes the rotation forward or rolls back only when one key opens every
|
|
215
|
+
item, and if a later `secrets set` contaminated a crashed rotation into a MIXED
|
|
216
|
+
store (items under two keys at once, or a store dir recreated by an interstitial
|
|
217
|
+
write after the crash left it absent, so its backup holds items the live dir does
|
|
218
|
+
not) it refuses with an actionable error and preserves every recovery artifact
|
|
219
|
+
rather than sweeping the only copy of a key or the backed-up ciphertext — so a
|
|
220
|
+
crash anywhere in the swap can never orphan the store, even when a write landed in
|
|
221
|
+
between. The rotation and every store write run under
|
|
222
|
+
one cross-process lock, so a `secrets set` or a second rotation can never
|
|
223
|
+
interleave with a swap in the first place. No plaintext secret or passphrase is
|
|
224
|
+
ever written to disk, argv, or a log. Items
|
|
225
|
+
that don't decrypt under the current key (orphan caches, stale test artifacts)
|
|
226
|
+
are carried through verbatim, never re-keyed. Dry-run by default (`--commit` to
|
|
227
|
+
apply). A dry run never re-keys, but it *does* heal an interrupted rotation —
|
|
228
|
+
that is how a crashed store becomes readable again without re-keying it — and it
|
|
229
|
+
says so instead of claiming nothing was written. Refuses while the secrets-agent
|
|
230
|
+
holds live unlocks or while
|
|
231
|
+
`AGENTS_SECRETS_PASSPHRASE` is exported in the environment, unless `--force`.
|
|
232
|
+
Headless-safe and Linux-first. Source: `apps/cli/src/lib/secrets/filestore.ts`,
|
|
233
|
+
`apps/cli/src/commands/secrets-rotate-passphrase.ts`.
|
|
234
|
+
|
|
235
|
+
- **`agents sessions --active --json` now reports who is watching each session.**
|
|
236
|
+
The `viewingIn` field carries the same string the table prints — `codium tab 3`,
|
|
237
|
+
`ghostty tab 2`, or `detached` for a live tmux pane with **no client attached**
|
|
238
|
+
(its terminal was closed or crashed). It is `null` both for a session that isn't
|
|
239
|
+
tmux-hosted and for one whose pane the locator could not resolve — `detached` is
|
|
240
|
+
claimed only when the pane was actually located, so absence of evidence is never
|
|
241
|
+
reported as evidence of absence. Previously the JSON path returned
|
|
242
|
+
before the locator pass ran, so the field never appeared and a machine consumer
|
|
243
|
+
could not tell a session someone is looking at from an orphaned one — which is
|
|
244
|
+
exactly what the Factory extension's `Agents: Resume` picker ranks by. The JSON
|
|
245
|
+
path resolves tmux clients only — no osascript — so scriptable output keeps the
|
|
246
|
+
cheapness the old ordering was protecting; a Ghostty-attached client resolves as
|
|
247
|
+
`ghostty` without its tab number. Peers running an older CLI that still emits the
|
|
248
|
+
`{app, tab}` object are normalized at the fan-out boundary, so a mixed-version
|
|
249
|
+
fleet sweep stays correct. Source: `apps/cli/src/lib/session/viewing-in.ts`
|
|
250
|
+
(`viewingInLabel`, `parseViewingIn`), `apps/cli/src/commands/sessions.ts`
|
|
251
|
+
(`serializeActiveSessionsForJson`, `enrichTmuxLocators`),
|
|
252
|
+
`apps/cli/src/lib/session/remote-active.ts`.
|
|
253
|
+
|
|
254
|
+
- **Webhook handlers gain `run.env` and `host` placement.** A handler can now
|
|
255
|
+
inject environment variables into the process it spawns (`run.env`), and choose
|
|
256
|
+
where that run executes (`host`). `host` takes a device name (`yosemite-s0`), or
|
|
257
|
+
`fleet` to pick any eligible online worker, or `fleet/<platform>` /
|
|
258
|
+
`<platform>/fleet` (also a bare `linux` / `macos` / `windows`) to restrict that
|
|
259
|
+
pick to one platform. A fleet expression that matches no eligible device fails
|
|
260
|
+
loudly rather than silently falling back to the local machine, so `fleet/linux`
|
|
261
|
+
can never land on a macOS box. Omitting `host` runs locally, as before.
|
|
262
|
+
Source: `apps/cli/src/lib/triggers/handlers.ts` (`resolveHandlerHost`),
|
|
263
|
+
`apps/cli/src/lib/routines-placement.ts` (`pickFleetDevice` platform filter),
|
|
264
|
+
`apps/cli/src/lib/routines.ts` (`JobConfig.env`), `apps/cli/src/lib/runner.ts`.
|
|
265
|
+
|
|
3
266
|
## 1.20.88
|
|
4
267
|
|
|
5
268
|
- **`agents doctor` redesigned into a prioritized, fleet-aware, per-version
|
package/README.md
CHANGED
|
@@ -1002,16 +1002,24 @@ Other useful commands: `agents doctor` checks CLI availability and resource sync
|
|
|
1002
1002
|
On macOS, `agents-cli` puts a status item in your menu bar -- a live glance at what your agents are doing, plus a Spotlight-style bar for filing work without breaking focus.
|
|
1003
1003
|
|
|
1004
1004
|
```bash
|
|
1005
|
-
agents menubar
|
|
1005
|
+
agents menubar setup # configure end-to-end: one instance, started at login
|
|
1006
1006
|
agents menubar status # is it installed and running?
|
|
1007
1007
|
```
|
|
1008
1008
|
|
|
1009
|
+
There is only ever **one** agents mark: the helper takes a lock at launch, so a
|
|
1010
|
+
second copy surfaces the running one's menu and exits instead of adding a
|
|
1011
|
+
duplicate icon. `agents menubar setup` is the recovery command when a machine is
|
|
1012
|
+
already wrong -- it ends any duplicate, installs the bundle, wires the login
|
|
1013
|
+
item, and verifies exactly one helper came back up.
|
|
1014
|
+
|
|
1009
1015
|
The dropdown surfaces a **NEEDS YOU** queue (agents waiting on a question, a plan review, or a permission prompt), the running roster, and a routines summary -- the same live state as `agents sessions --active`, one click away.
|
|
1010
1016
|
|
|
1011
1017
|
### Quick-issue bar (⌘⇧O)
|
|
1012
1018
|
|
|
1013
1019
|
Press `Cmd-Shift-O` anywhere for a thin capture surface: type a one-line note, `Cmd-V` to paste, and attach one or more recent screenshots (double-click a thumbnail to preview it in full). Submit, and a headless agent picks the right project from your recent sessions, investigates, and files the Linear ticket itself -- you never leave what you were doing.
|
|
1014
1020
|
|
|
1021
|
+
The bar also lists the **open Linear tickets of the repo you picked**, urgent first. Switching the repo switches the Linear project; typing filters the list, so an existing ticket shows up before you file a duplicate; and clicking a row (or `⌘1`-`⌘5`) dispatches that ticket to the selected agents -- **Run** implements it, **Plan** posts a plan as a ticket comment.
|
|
1022
|
+
|
|
1015
1023
|
<p align="center">
|
|
1016
1024
|
<img src="assets/menubar-quickissue.svg" alt="The Cmd-Shift-O quick-issue bar: a one-line note with attached screenshot thumbnails that a headless agent turns into a filed Linear ticket" width="100%" />
|
|
1017
1025
|
</p>
|
package/dist/bin/agents
CHANGED
|
Binary file
|
|
@@ -35,7 +35,7 @@ Examples:
|
|
|
35
35
|
agents commands add
|
|
36
36
|
|
|
37
37
|
# Install specific commands by name
|
|
38
|
-
agents commands add --names
|
|
38
|
+
agents commands add --names plan,debug --agents codex@0.116.0
|
|
39
39
|
|
|
40
40
|
When to use:
|
|
41
41
|
- Project setup: 'agents commands add gh:team/commands' to sync everyone's workflow
|
|
@@ -90,7 +90,7 @@ Examples:
|
|
|
90
90
|
agents commands add
|
|
91
91
|
|
|
92
92
|
# Install specific commands to a single version
|
|
93
|
-
agents commands add --names
|
|
93
|
+
agents commands add --names plan,debug --agents codex@0.116.0
|
|
94
94
|
|
|
95
95
|
# Pull commands from GitHub and sync to all installed agents
|
|
96
96
|
agents commands add gh:user/repo --agents claude,codex,cursor
|
|
@@ -125,7 +125,7 @@ Examples:
|
|
|
125
125
|
else {
|
|
126
126
|
if (!isInteractiveTerminal()) {
|
|
127
127
|
requireInteractiveSelection('Selecting commands from ~/.agents/commands/', [
|
|
128
|
-
'agents commands add --names
|
|
128
|
+
'agents commands add --names plan,debug --agents codex',
|
|
129
129
|
'agents commands add gh:user/repo --agents codex',
|
|
130
130
|
]);
|
|
131
131
|
}
|
|
@@ -272,7 +272,7 @@ Examples:
|
|
|
272
272
|
.addHelpText('after', `
|
|
273
273
|
Examples:
|
|
274
274
|
# Remove a command by name
|
|
275
|
-
agents commands remove
|
|
275
|
+
agents commands remove plan
|
|
276
276
|
|
|
277
277
|
# Interactive: pick commands to remove
|
|
278
278
|
agents commands remove
|
|
@@ -305,7 +305,7 @@ Examples:
|
|
|
305
305
|
}
|
|
306
306
|
if (!isInteractiveTerminal()) {
|
|
307
307
|
requireInteractiveSelection('Selecting commands to remove', [
|
|
308
|
-
'agents commands remove
|
|
308
|
+
'agents commands remove plan',
|
|
309
309
|
]);
|
|
310
310
|
}
|
|
311
311
|
try {
|
|
@@ -422,7 +422,7 @@ Examples:
|
|
|
422
422
|
.addHelpText('after', `
|
|
423
423
|
Examples:
|
|
424
424
|
# View a specific command
|
|
425
|
-
agents commands view
|
|
425
|
+
agents commands view plan
|
|
426
426
|
|
|
427
427
|
# Interactive picker
|
|
428
428
|
agents commands view
|
|
@@ -437,7 +437,7 @@ Examples:
|
|
|
437
437
|
}
|
|
438
438
|
if (!isInteractiveTerminal()) {
|
|
439
439
|
requireInteractiveSelection('Selecting a command to view', [
|
|
440
|
-
'agents commands view
|
|
440
|
+
'agents commands view plan',
|
|
441
441
|
]);
|
|
442
442
|
}
|
|
443
443
|
try {
|
package/dist/commands/factory.js
CHANGED
|
@@ -6,6 +6,7 @@ import { homedir } from 'os';
|
|
|
6
6
|
import { betaEnableHint, isBetaEnabled } from '../lib/beta.js';
|
|
7
7
|
import { insertTask } from '../lib/cloud/store.js';
|
|
8
8
|
import { emit } from '../lib/events.js';
|
|
9
|
+
import { buildFactorySnapshot } from '../lib/factory/snapshot.js';
|
|
9
10
|
function requireFactoryUrl() {
|
|
10
11
|
const url = process.env.FACTORY_FLOOR_URL;
|
|
11
12
|
if (!url) {
|
|
@@ -51,14 +52,28 @@ export function registerFactoryCommands(program) {
|
|
|
51
52
|
Examples:
|
|
52
53
|
agents factory submit PROJ-123
|
|
53
54
|
agents factory submit https://linear.app/example/issue/PROJ-123
|
|
55
|
+
agents factory snapshot --json
|
|
54
56
|
`);
|
|
55
|
-
factory.hook('preAction', () => {
|
|
56
|
-
|
|
57
|
+
factory.hook('preAction', (_thisCommand, actionCommand) => {
|
|
58
|
+
// Foreman must be able to read its tick input before any beta-gated action.
|
|
59
|
+
if (enabled || actionCommand.name() === 'snapshot')
|
|
57
60
|
return;
|
|
58
61
|
console.error(chalk.red('agents factory is in beta.'));
|
|
59
62
|
console.error(chalk.gray(betaEnableHint('factory')));
|
|
60
63
|
process.exit(1);
|
|
61
64
|
});
|
|
65
|
+
factory
|
|
66
|
+
.command('snapshot')
|
|
67
|
+
.description('Read the complete Software Factory state without dispatching or changing it.')
|
|
68
|
+
.option('--json', 'Output the stable machine-readable snapshot')
|
|
69
|
+
.action(async (opts) => {
|
|
70
|
+
const snapshot = await buildFactorySnapshot();
|
|
71
|
+
if (opts.json) {
|
|
72
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
renderSnapshot(snapshot);
|
|
76
|
+
});
|
|
62
77
|
factory
|
|
63
78
|
.command('submit <linear-ref>')
|
|
64
79
|
.description('Submit a Linear issue (PROJ-123 or URL) to the Software Factory.')
|
|
@@ -93,3 +108,12 @@ Examples:
|
|
|
93
108
|
console.log(` tail output agents cloud logs ${result.cloud_execution_id}`);
|
|
94
109
|
});
|
|
95
110
|
}
|
|
111
|
+
function renderSnapshot(snapshot) {
|
|
112
|
+
console.log(chalk.bold(`Factory snapshot ${snapshot.generatedAt}`));
|
|
113
|
+
console.log(` sessions ${snapshot.sessions.length}`);
|
|
114
|
+
console.log(` open PRs ${snapshot.prs.length}`);
|
|
115
|
+
console.log(` devices ${snapshot.devices.length}`);
|
|
116
|
+
for (const [project, queue] of Object.entries(snapshot.queues)) {
|
|
117
|
+
console.log(` ${project.padEnd(12)} todo ${queue.todo} · in progress ${queue.inProgress} · blocked ${queue.blocked}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
package/dist/commands/funnel.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { buildFunnelStatusCommand, buildFunnelUpCommand, parseFunnelPort } from '../lib/funnel.js';
|
|
2
|
+
import { buildFunnelDownCommand, buildFunnelStatusCommand, buildFunnelUpCommand, parseFunnelPort } from '../lib/funnel.js';
|
|
3
3
|
import { resolveHost } from '../lib/hosts/registry.js';
|
|
4
4
|
import { resolveRemoteOsSync } from '../lib/hosts/remote-os.js';
|
|
5
5
|
import { sshTargetFor } from '../lib/hosts/types.js';
|
|
@@ -59,4 +59,19 @@ export function registerFunnelCommand(program) {
|
|
|
59
59
|
process.exit(1);
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
|
+
funnel
|
|
63
|
+
.command('down <host>')
|
|
64
|
+
.description('Disable Tailscale Funnel exposure for a public HTTPS port.')
|
|
65
|
+
.option('--port <n>', 'Public Funnel port: 443, 8443, or 10000', '443')
|
|
66
|
+
.action(async (host, opts) => {
|
|
67
|
+
try {
|
|
68
|
+
const publicPort = parseFunnelPort(opts.port ?? '443');
|
|
69
|
+
await runOnHost(host, buildFunnelDownCommand(publicPort));
|
|
70
|
+
console.log(chalk.green(`Funnel disabled on ${host}: public :${publicPort}`));
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
console.error(chalk.red(err.message));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
62
77
|
}
|
package/dist/commands/menubar.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* user; these commands are the manual override.
|
|
8
8
|
*/
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
-
import {
|
|
10
|
+
import { setHelpSections } from '../lib/help.js';
|
|
11
|
+
import { enableMenubarService, disableMenubarService, getMenubarStatus, runMenubarSetup, } from '../lib/menubar/install-menubar.js';
|
|
11
12
|
function notMac() {
|
|
12
13
|
if (process.platform !== 'darwin') {
|
|
13
14
|
console.log(chalk.yellow('The menu bar helper is macOS only.'));
|
|
@@ -15,10 +16,122 @@ function notMac() {
|
|
|
15
16
|
}
|
|
16
17
|
return false;
|
|
17
18
|
}
|
|
19
|
+
/** Shared status readout — `status`, bare `menubar`, and `setup --check` all end here. */
|
|
20
|
+
function printStatus(s, opts = {}) {
|
|
21
|
+
const yn = (b) => (b ? chalk.green('yes') : chalk.gray('no'));
|
|
22
|
+
console.log(chalk.bold('Menu bar helper\n'));
|
|
23
|
+
console.log(` running ${yn(s.running)}`);
|
|
24
|
+
console.log(` service installed ${yn(s.serviceInstalled)}`);
|
|
25
|
+
if (opts.brief) {
|
|
26
|
+
console.log(chalk.gray('\n setup | enable | disable | status'));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.log(` app installed ${s.installedApp ? chalk.gray(s.installedApp) : chalk.gray('no')}`);
|
|
30
|
+
console.log(` installed version ${s.installedVersion ? chalk.gray(s.installedVersion) : chalk.gray('unknown')}`);
|
|
31
|
+
console.log(` current version ${chalk.gray(s.currentVersion)}`);
|
|
32
|
+
console.log(` bundle source ${s.source ? chalk.gray(s.source) : chalk.red('missing (cannot enable)')}`);
|
|
33
|
+
console.log(` disabled by user ${yn(s.disabledByUser)}`);
|
|
34
|
+
// Two copies of the INSTALLED bundle is the duplicate the user sees as two
|
|
35
|
+
// agents marks in the menu bar. It used to read as a healthy `running: yes`.
|
|
36
|
+
if (s.instances.length > 1) {
|
|
37
|
+
console.log(chalk.yellow(`\n ${s.instances.length} copies of the installed helper are running — that is the duplicate menu-bar icon:`));
|
|
38
|
+
for (const p of s.instances)
|
|
39
|
+
console.log(chalk.gray(` ${p.pid} ${p.executable}`));
|
|
40
|
+
console.log(chalk.gray(' Fix it with `agents menubar setup`.'));
|
|
41
|
+
}
|
|
42
|
+
if (s.foreignInstances.length > 0) {
|
|
43
|
+
// RegisterEventHotKey is first-come, so the helper that registered the
|
|
44
|
+
// chord first owns Cmd-Shift-V/O. A process list cannot say which that
|
|
45
|
+
// was — only that a rival exists — so report the conflict, not a winner.
|
|
46
|
+
// The loser has no other symptom: its chords simply never fire.
|
|
47
|
+
const n = s.foreignInstances.length;
|
|
48
|
+
console.log(chalk.yellow(`\n ${n} other helper process${n === 1 ? '' : 'es'} running — ${n === 1 ? 'it' : 'they'} may hold Cmd-Shift-V/O instead of the installed one:`));
|
|
49
|
+
for (const p of s.foreignInstances)
|
|
50
|
+
console.log(chalk.gray(` ${p.pid} ${p.executable}`));
|
|
51
|
+
console.log(chalk.gray(' End them with `agents menubar setup`.'));
|
|
52
|
+
}
|
|
53
|
+
if (s.stale) {
|
|
54
|
+
console.log(chalk.yellow('\n Installed helper is stale — runs on next `agents` startup, or `agents menubar setup` now.'));
|
|
55
|
+
}
|
|
56
|
+
else if (!s.serviceInstalled && !s.disabledByUser) {
|
|
57
|
+
console.log(chalk.gray('\n Set it up with `agents menubar setup`.'));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function printSetupResult(r) {
|
|
61
|
+
console.log(chalk.bold('Menu bar setup\n'));
|
|
62
|
+
for (const step of r.steps) {
|
|
63
|
+
const mark = step.outcome === 'failed' ? chalk.red('✗')
|
|
64
|
+
: step.outcome === 'changed' ? chalk.green('+') : chalk.green('✓');
|
|
65
|
+
console.log(` ${mark} ${step.name.padEnd(15)} ${chalk.gray(step.detail)}`);
|
|
66
|
+
}
|
|
67
|
+
console.log();
|
|
68
|
+
if (r.configured) {
|
|
69
|
+
console.log(chalk.green('Menu bar configured.') + chalk.gray(' One agents mark, started at login.'));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log(chalk.red('Menu bar not fully configured.') + chalk.gray(' See the failed step above.'));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
18
75
|
export function registerMenubarCommands(program) {
|
|
19
76
|
const menubar = program
|
|
20
77
|
.command('menubar')
|
|
21
78
|
.description('Manage the macOS menu-bar helper (running sessions, agents awaiting input, routines)');
|
|
79
|
+
// `setup` is the one command that gets a machine to the intended state:
|
|
80
|
+
// exactly one status item, started at login. `enable` stays the narrow
|
|
81
|
+
// install+start; setup adds duplicate cleanup and verifies the end state.
|
|
82
|
+
const setup = menubar
|
|
83
|
+
.command('setup')
|
|
84
|
+
.description('Configure the menu bar end-to-end: one instance, started at login')
|
|
85
|
+
.option('--check', 'Report the current state, change nothing')
|
|
86
|
+
.option('--json', 'Emit machine-readable JSON')
|
|
87
|
+
.action((options) => {
|
|
88
|
+
if (options.check) {
|
|
89
|
+
const s = getMenubarStatus();
|
|
90
|
+
if (options.json) {
|
|
91
|
+
process.stdout.write(JSON.stringify(s) + '\n');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (notMac())
|
|
95
|
+
return;
|
|
96
|
+
printStatus(s);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (!options.json && notMac())
|
|
100
|
+
return;
|
|
101
|
+
const r = runMenubarSetup();
|
|
102
|
+
if (options.json) {
|
|
103
|
+
process.stdout.write(JSON.stringify(r) + '\n');
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
printSetupResult(r);
|
|
107
|
+
}
|
|
108
|
+
if (!r.configured)
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
});
|
|
111
|
+
setHelpSections(setup, {
|
|
112
|
+
examples: `
|
|
113
|
+
# Configure the menu bar end-to-end (idempotent — safe to re-run)
|
|
114
|
+
agents menubar setup
|
|
115
|
+
|
|
116
|
+
# Two agents marks in the menu bar? This ends the duplicate.
|
|
117
|
+
agents menubar setup
|
|
118
|
+
|
|
119
|
+
# See the current state without changing anything
|
|
120
|
+
agents menubar setup --check
|
|
121
|
+
`,
|
|
122
|
+
notes: `
|
|
123
|
+
Configures, in order: every running helper ended, the helper bundle at
|
|
124
|
+
~/Library/Application Support/agents-cli, its code signature, the launchd
|
|
125
|
+
login item (com.phnx-labs.agents-menubar — RunAtLoad + KeepAlive), then
|
|
126
|
+
verifies exactly one helper came back up.
|
|
127
|
+
|
|
128
|
+
Every running helper is ended and launchd restarts one, so the survivor is
|
|
129
|
+
always the login-managed copy. Exits nonzero if it cannot reach that state.
|
|
130
|
+
|
|
131
|
+
Setup clears a previous \`agents menubar disable\`. To turn the menu bar off
|
|
132
|
+
again, run \`agents menubar disable\`.
|
|
133
|
+
`,
|
|
134
|
+
});
|
|
22
135
|
menubar
|
|
23
136
|
.command('enable')
|
|
24
137
|
.description('Install and start the menu-bar helper (launches at login)')
|
|
@@ -40,7 +153,7 @@ export function registerMenubarCommands(program) {
|
|
|
40
153
|
if (notMac())
|
|
41
154
|
return;
|
|
42
155
|
disableMenubarService();
|
|
43
|
-
console.log(chalk.green('Menu bar helper disabled.') + chalk.gray(' Re-enable any time with `agents menubar
|
|
156
|
+
console.log(chalk.green('Menu bar helper disabled.') + chalk.gray(' Re-enable any time with `agents menubar setup`.'));
|
|
44
157
|
});
|
|
45
158
|
menubar
|
|
46
159
|
.command('status')
|
|
@@ -56,33 +169,7 @@ export function registerMenubarCommands(program) {
|
|
|
56
169
|
console.log(chalk.yellow('The menu bar helper is macOS only.'));
|
|
57
170
|
return;
|
|
58
171
|
}
|
|
59
|
-
|
|
60
|
-
console.log(chalk.bold('Menu bar helper\n'));
|
|
61
|
-
console.log(` running ${yn(s.running)}`);
|
|
62
|
-
console.log(` service installed ${yn(s.serviceInstalled)}`);
|
|
63
|
-
console.log(` app installed ${s.installedApp ? chalk.gray(s.installedApp) : chalk.gray('no')}`);
|
|
64
|
-
console.log(` installed version ${s.installedVersion ? chalk.gray(s.installedVersion) : chalk.gray('unknown')}`);
|
|
65
|
-
console.log(` current version ${chalk.gray(s.currentVersion)}`);
|
|
66
|
-
console.log(` bundle source ${s.source ? chalk.gray(s.source) : chalk.red('missing (cannot enable)')}`);
|
|
67
|
-
console.log(` disabled by user ${yn(s.disabledByUser)}`);
|
|
68
|
-
if (s.foreignInstances.length > 0) {
|
|
69
|
-
// RegisterEventHotKey is first-come, so the helper that registered the
|
|
70
|
-
// chord first owns Cmd-Shift-V/O. A process list cannot say which that
|
|
71
|
-
// was — only that a rival exists — so report the conflict, not a
|
|
72
|
-
// winner. The loser
|
|
73
|
-
// has no other symptom: its chords simply never fire.
|
|
74
|
-
const n = s.foreignInstances.length;
|
|
75
|
-
console.log(chalk.yellow(`\n ${n} other helper process${n === 1 ? '' : 'es'} running — ${n === 1 ? 'it' : 'they'} may hold Cmd-Shift-V/O instead of the installed one:`));
|
|
76
|
-
for (const p of s.foreignInstances)
|
|
77
|
-
console.log(chalk.gray(` ${p.pid} ${p.executable}`));
|
|
78
|
-
console.log(chalk.gray(' End it, then `agents menubar enable` to restart the installed helper.'));
|
|
79
|
-
}
|
|
80
|
-
if (s.stale) {
|
|
81
|
-
console.log(chalk.yellow('\n Installed helper is stale — runs on next `agents` startup, or `agents menubar enable` now.'));
|
|
82
|
-
}
|
|
83
|
-
else if (!s.serviceInstalled && !s.disabledByUser) {
|
|
84
|
-
console.log(chalk.gray('\n Enable it with `agents menubar enable`.'));
|
|
85
|
-
}
|
|
172
|
+
printStatus(s);
|
|
86
173
|
});
|
|
87
174
|
// Bare `agents menubar` -> status.
|
|
88
175
|
menubar.action(() => {
|
|
@@ -91,10 +178,6 @@ export function registerMenubarCommands(program) {
|
|
|
91
178
|
console.log(chalk.yellow('The menu bar helper is macOS only.'));
|
|
92
179
|
return;
|
|
93
180
|
}
|
|
94
|
-
|
|
95
|
-
console.log(chalk.bold('Menu bar helper\n'));
|
|
96
|
-
console.log(` running ${yn(s.running)}`);
|
|
97
|
-
console.log(` service installed ${yn(s.serviceInstalled)}`);
|
|
98
|
-
console.log(chalk.gray('\n enable | disable | status'));
|
|
181
|
+
printStatus(s, { brief: true });
|
|
99
182
|
});
|
|
100
183
|
}
|