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
+30 -12
View File
@@ -67,18 +67,9 @@ function getHookStatus() {
}
const raw = fs.readFileSync(getSettingsPath(), "utf8");
const settings = JSON.parse(raw);
const hookTypes = [
"PreToolUse",
"PostToolUse",
"Stop",
"SubagentStop",
"Notification",
"SessionStart",
"SessionEnd",
];
// The enabled plugin runs the hooks from its own plugin.json, so
// settings.json rightly holds none of ours (installHooks removes them).
const { pluginProvidesHooks } = require("../../scripts/install-hooks");
const { HOOK_TYPES: hookTypes, pluginProvidesHooks } = require("../../scripts/install-hooks");
if (pluginProvidesHooks(settings)) {
return {
installed: true,
@@ -200,10 +191,10 @@ function clearAll() {
// not cascade and stay as orphans.
db.prepare("DELETE FROM workflows").run();
// session_snapshots survive: a snapshot is a frozen copy the user chose to
// keep, like alert rules — deleted only from Settings → Snapshots.
// keep, like alert rules — deleted only from Settings → Snapshots & reports.
// report_definitions and report_runs survive too: a definition is user
// configuration and a run's files are reports already delivered — removed
// only from Settings → Reports.
// only from Settings → Snapshots & reports.
}
// POST /api/settings/reimport — re-import legacy sessions from ~/.claude/
@@ -283,6 +274,33 @@ router.put("/autostart", sameOriginGuard, (req, res) => {
}
});
// GET/PUT /api/settings/tray — AgentHub's icon in the OS status area
// (server/lib/tray.js). On by default; a PUT starts or stops the icon process.
const tray = require("../lib/tray");
router.get("/tray", (_req, res) => res.json(tray.status()));
router.put("/tray", sameOriginGuard, (req, res) => {
if (typeof req.body?.enabled !== "boolean") {
return res
.status(400)
.json({ error: { code: "EBADVALUE", message: "`enabled` must be a boolean" } });
}
res.json(tray.setEnabled(req.body.enabled));
});
// POST /api/settings/quit — Quit AgentHub (server/lib/quit.js): kill every
// lane pane, then stop this server once the answer is out. It stays stopped
// (the bootstrap starts none) until a server starts again — `agenthub start`,
// or boot/login through an enabled autostart unit.
const quit = require("../lib/quit");
router.post("/quit", sameOriginGuard, (_req, res) => {
quit.markQuit();
const runs = require("../lib/pty-run");
let killed = 0;
for (const run of runs.listRuns()) if (runs.killRun(run.id)) killed++;
res.on("finish", () => quit.stopServer());
res.json({ ok: true, killed });
});
// POST /api/settings/reset-pricing — reset pricing to defaults. The seed is
// Claude's; a model priced by the automatic sync (an OpenAI one, from Codex
// sessions) is fetched back at once, waiting at most RESET_SYNC_MS for it.