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:
+39
-12
@@ -57,6 +57,7 @@ const {
|
||||
listTree,
|
||||
listTreeAll,
|
||||
readFileAt,
|
||||
blameFile,
|
||||
readImageAt,
|
||||
} = require("../lib/git-diff");
|
||||
const { runGitAction } = require("../lib/git-ops");
|
||||
@@ -1010,6 +1011,29 @@ router.get("/:id/git/file", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Annotate (`?path=`): who last changed each line of the working-tree file —
|
||||
* git-diff.js's `blameFile`, with the whole-file read's guards (lexical lane
|
||||
* guard, 403 ESECRET, a size cap). A read like `/git/file`, so unguarded too.
|
||||
*/
|
||||
router.get("/:id/git/blame", async (req, res) => {
|
||||
const t = await gitTargetOr404(req, res);
|
||||
if (!t) return;
|
||||
try {
|
||||
const blame = await blameFile(t.cwd, typeof req.query.path === "string" ? req.query.path : "");
|
||||
res.json({ available: true, ...blame });
|
||||
} catch (err) {
|
||||
if (err.code === "ESECRET") {
|
||||
return res.status(403).json({ error: { code: err.code, message: err.message } });
|
||||
}
|
||||
if (["EBADPATH", "ENOFILE", "ETOOBIG"].includes(err.code)) {
|
||||
return res.status(400).json({ error: { code: err.code, message: err.message } });
|
||||
}
|
||||
// Not tracked (git blame has no history for it) or not a repo.
|
||||
res.json({ available: false });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* One image's bytes (`?path=&rev=work|HEAD|<sha>`) for the viewer's `<img>`:
|
||||
* `readImageAt`'s guards (the file read's, plus an image-extension allowlist
|
||||
@@ -1145,9 +1169,10 @@ router.post("/:id/git/commit-message", sameOriginGuard, async (req, res) => {
|
||||
const t = await gitTargetOr404(req, res);
|
||||
if (!t) return;
|
||||
try {
|
||||
res.json({ message: await suggestCommitMessage(t.cwd) });
|
||||
const paths = Array.isArray(req.body?.paths) ? req.body.paths : undefined;
|
||||
res.json({ message: await suggestCommitMessage(t.cwd, { paths }) });
|
||||
} catch (err) {
|
||||
const status = ["ENOSTAGED", "ENOTOOL"].includes(err.code)
|
||||
const status = ["ENOSTAGED", "ENOTOOL", "EBADPATH"].includes(err.code)
|
||||
? 400
|
||||
: err.code === "EAI"
|
||||
? 502
|
||||
@@ -1200,6 +1225,7 @@ router.post("/:id/git/:action", sameOriginGuard, async (req, res) => {
|
||||
"EBADSHA",
|
||||
"EMERGECOMMIT",
|
||||
"ENOPARENT",
|
||||
"ETRACKED",
|
||||
];
|
||||
// A numeric code is git's own exit status (execFile reports one that way):
|
||||
// git ran and refused — a rejected push, a failed credential, a diverged
|
||||
@@ -1209,16 +1235,17 @@ router.post("/:id/git/:action", sameOriginGuard, async (req, res) => {
|
||||
// EHUNKGONE is the same kind of answer: the tree moved under the click.
|
||||
const status = badRequest.includes(err.code)
|
||||
? 400
|
||||
: exited || [
|
||||
"EHUNKGONE",
|
||||
"EGITAUTH",
|
||||
"EMERGING",
|
||||
"ENOMERGE",
|
||||
"ECONFLICTS",
|
||||
"EPUSHED",
|
||||
"EEMPTY",
|
||||
"ESTALE",
|
||||
].includes(err.code)
|
||||
: exited ||
|
||||
[
|
||||
"EHUNKGONE",
|
||||
"EGITAUTH",
|
||||
"EMERGING",
|
||||
"ENOMERGE",
|
||||
"ECONFLICTS",
|
||||
"EPUSHED",
|
||||
"EEMPTY",
|
||||
"ESTALE",
|
||||
].includes(err.code)
|
||||
? 409
|
||||
: 500;
|
||||
// git's own stderr is the useful half of a failed push or pull (a
|
||||
|
||||
Reference in New Issue
Block a user