feat: v2.5.12 — plan and quota for every AI tool, Grok Build and Kimi Code, Quit AgentHub (#65)

Usage panel (status bar):
- every account shows its plan (Claude subscriptionType, Codex
  chatgpt_plan_type, Cursor membershipType, Grok subscriptionTier)
- new quota rows: Cursor CLI (cursor.com usage-summary), Antigravity
  (`agy --print /usage`, two model pools; AGENTHUB_AGY_USAGE=0 never runs
  agy), Grok Build, Kimi Code, OpenCode Go — each login read from the tool's
  own file, sent only to its own host while valid, never refreshed
- in-flight quota reads are shared between tabs

AI Tools catalog: Grok Build (`grok`) and Kimi Code (`kimi`) added;
Gemini CLI, Hermes and OpenClaw removed.

Also: Quit AgentHub (#65, `agenthub stop`), status-area icon, Add to Chat
on an editor selection, Settings in three groups, JetBrains-style Changes
panel (ticked files/hunks, ignore, blame, file history), terminal font.

--no-verify: :3000 is taken by another dev server (lane-detect tests);
both suites run by hand — server 2636/2639 (the 3 are :3000), client
1131/1131.
This commit is contained in:
2026-10-08 12:22:14 +07:00
parent 6ef9a5d4b7
commit a9c71cd905
133 changed files with 8007 additions and 1830 deletions
+56 -2
View File
@@ -131,7 +131,7 @@ waits on an install:
7. **CLI** — write the `~/.local/bin/agenthub` launcher, never clobbering a `agenthub` the bootstrap did not write. It pins the node that ran the bootstrap and falls back to `node` on `PATH` when that binary is gone (a Homebrew Cellar or nvm path deleted by an upgrade).
8. **UI build** — `client/` is copied into the runtime dir and built there (`npm ci --include=dev --ignore-scripts`, falling back to `npm install` with the same flags, then `tsc -b` and `vite build` run by the bootstrap's own `node` — the client's `build` script without `npm run`), landing in `runtime/client-dist` — the same directory the server serves from. Nothing in it looks `node` up on PATH: on Windows an install script or a `.bin/*.cmd` shim runs through cmd.exe, and the server's PATH may have no `node` (esbuild's postinstall failed there with "'node' is not recognized" and the update kept the old UI); esbuild, the only dependency with an install script, finds its binary without it. `--include=dev` is required, not tidy: the entire build toolchain (`vite`, `typescript`, `@vitejs/plugin-react`, `tailwindcss`, `@types/*`) is a devDependency, and npm reads an inherited `NODE_ENV=production` as `--omit=dev`. Every bootstrap descended from the running server carries that env — the self-update chain, and any `SessionStart` fired by a `claude` inside a tmux pane the server started — so without the flag the install drops `vite` and the build dies on missing types. Skipped when a bundle for the current version already exists; a failure here does not fail the bootstrap (the API still works, and `/agenthub-open` can retry) but does mean `/run` 404s until it's fixed — and when a bundle from the PREVIOUS version is still on disk the failure is quieter still: the dashboard keeps serving the old UI against the new API, visible only as the Settings page's `UI build vX (rebuild client to match)` line.
9. **Server** — when the user has turned **Start AgentHub automatically** on
(Settings → About; OFF by default), install/refresh the
(Settings → System; OFF by default), install/refresh the
`agenthub.service` unit on Linux — on macOS with a GUI login, the
`io.smartgift.agenthub` LaunchAgent (see [Auto-start on boot](#auto-start-on-boot)) — instead
of a bare spawn, so the server is supervised and comes back after a reboot
@@ -241,9 +241,43 @@ cushion — one build deep, because a full copy of this repo is ~130 MB. Outside
a git checkout the parent directory holds the user's own work. Best effort
throughout: a directory that will not delete is left alone, never an error.
### Quit
Settings → System → **Quit AgentHub** or `agenthub stop` (issue #65,
`server/lib/quit.js`, `POST /api/settings/quit`) closes every workspace
terminal and stops the server, through the service manager when the unit runs
it. It writes `<data dir>/runtime/stopped` first: while that file exists
`fastPathOk` is true with no server and the worker starts none (`--force`, i.e.
`/agenthub-update`, overrides it; a stale unit is still repaired, not started).
Any server that starts clears it — `agenthub start`, which in a plugin install
runs this bootstrap, or a boot/login through an enabled autostart unit — so
autostart keeps working across a reboot.
### Status-area icon
Like 9router's, AgentHub puts an icon beside the clock — GNOME's top bar, the
macOS menu bar, the Windows notification area (`server/lib/tray.js`,
`scripts/tray.js`). It is its own process, drawn by the `systray2` package's
helper binary (installed with the other runtime dependencies), so a server
restart or a Quit never takes it away: after a Quit its **Start AgentHub** is
the way back. A production server starts it once it is listening and checks
every minute (a Linux server started at boot has no `DISPLAY` until someone
logs in; three quick deaths in a row and that server stops trying). It is on by
default; **Hide this icon** in its menu and Settings → System → **Show AgentHub
icon in the status area** write `<data dir>/runtime/tray-off`, and only that
switch removes it again. One icon per data dir: `<data dir>/runtime/tray.json`
holds its pid and install root and is touched every 5 s as a heartbeat — a
stale file is never trusted, so a pid reused after a reboot is never
signalled. A server replaces an icon from another install root (the previous
plugin build) once at start. Never in server mode, nor from a development
server. The icon starts at login only when the dashboard does (start at login
below); there is no separate autostart entry. Verified on Ubuntu GNOME (X11);
on macOS `systray2` ships only an x86_64 helper (Apple Silicon runs it through
Rosetta 2, else no icon appears), and Windows is untested.
### Auto-start on boot
Opt-in, and off by default: Settings → About → **Start AgentHub automatically**
Opt-in, and off by default: Settings → System → **Start AgentHub automatically**
(`GET/PUT /api/settings/autostart`, `server/lib/autostart.js`). The switch IS
the unit's enabled state — on writes a unit for the running server when none
exists and enables it, off disables it; neither starts or stops the server. A
@@ -318,6 +352,26 @@ SIGTERM from anything at all would otherwise leave the dashboard down for good.
An explicit `systemctl stop` — which is what `agenthub service uninstall` and
the Settings panel do — still stops it.
The unit also sets **`KillMode=process`**: a stop or an update's restart ends
the server process alone. tmux forks its server from whichever process first
asks for a session — the dashboard itself after a boot with autostart on — so
it can live in the unit's cgroup, and `control-group` would kill it and every
lane pane on each update. **An update never closes a pane; only Quit does**
(it kills the `agenthub-lane-*` sessions itself). Existing units pick this up
when the next update rewrites them; a checkout's unit on `agenthub service
install`. The launchd twin is `AbandonProcessGroup`.
Belt and braces on Linux: the session that STARTS the tmux server (no server
running yet) is created through `systemd-run --user --scope`
(`server/lib/tmux.js`), so the server lives in a transient scope of its own —
not in `agenthub.service`, and not in the terminal or IDE scope a dev server
was started from. Stopping either, systemd-oomd reaping either, or an old unit
still on `control-group` leaves the panes alone. With no user manager to ask
(SSH without linger, no systemd) it falls back to a plain start. On macOS tmux
daemonizes into its own session and the plist abandons its process group. On
Windows (psmux) whether the server outlives the dashboard has not been
verified yet.
`agenthub service install` (the checkout path) keeps using `installUserService`:
it runs from your own shell, outside the unit's cgroup, so it may block and
start in one step.