feat(ai-tools): drop OpenCode and Grok Build

- opencode.json / grok.json leave the built-in catalog; opencode-usage.js,
  its poller, transcript branch and table creation go with it (existing
  tables and session rows stay as history).
- A Grok payload reaching the Claude hook route (Grok runs the Claude hooks
  itself) is dropped instead of recorded as tool = 'grok'.
- quota.js no longer reads Grok or OpenCode Go logins.
- Redone on v2.8.22 from c6b2892 (feat/grok-usage, never merged).
This commit is contained in:
2026-10-23 13:44:24 +07:00
parent 9ea7758abb
commit c0832182f2
31 changed files with 116 additions and 1277 deletions
-2
View File
@@ -50,9 +50,7 @@ const QUOTA_TOOL_LABEL: Record<ToolQuota["id"], string> = {
codex: "Codex",
antigravity: "Antigravity",
cursor: "Cursor",
grok: "Grok",
kimi: "Kimi",
opencode: "OpenCode Go",
};
function localDate(d = new Date()): string {
@@ -69,7 +69,7 @@ const TOOLS: AiToolsView = {
tool("codex", "Codex", { launcher: "codex", proxyCapable: true, prompt: null }),
tool("gemini", "Gemini"),
tool("aider", "Aider", { installed: false, path: null }),
tool("grok", "Grok", { enabled: false }),
tool("goose", "Goose", { enabled: false }),
],
};
@@ -65,7 +65,7 @@ const VIEW: AiToolsView = {
tools: [
tool("claude", "Claude Code"),
tool("gemini", "Gemini"),
tool("grok", "Grok", { enabled: false }),
tool("goose", "Goose", { enabled: false }),
tool("aider", "Aider", { installed: false, path: null }),
],
};
@@ -155,9 +155,9 @@ describe("AiToolsSettings", () => {
await user.click(within(gemini).getByText("Set default"));
expect(api.aiTools.update).toHaveBeenCalledWith("gemini", { default: true });
// A disabled tool cannot become the default.
const grok = screen.getByTestId("ai-tool-grok");
expect(within(grok).getByText("Set default")).toBeDisabled();
expect(within(grok).getByRole("switch")).toHaveAttribute("aria-checked", "false");
const goose = screen.getByTestId("ai-tool-goose");
expect(within(goose).getByText("Set default")).toBeDisabled();
expect(within(goose).getByRole("switch")).toHaveAttribute("aria-checked", "false");
});
it("refreshes detection", async () => {
@@ -179,7 +179,7 @@ describe("AiToolsSettings", () => {
await user.click(screen.getByRole("menuitem", { name: "Disable" }));
expect(api.aiTools.update).toHaveBeenCalledWith("gemini", { enabled: false });
// A disabled tool cannot become the default from the menu either.
await user.pointer({ keys: "[MouseRight]", target: screen.getByTestId("ai-tool-grok") });
await user.pointer({ keys: "[MouseRight]", target: screen.getByTestId("ai-tool-goose") });
expect(screen.getByRole("menuitem", { name: "Set default" })).toBeDisabled();
});
});
@@ -301,11 +301,9 @@ describe("AiToolsSettings — install and update", () => {
describe("AiToolIcon", () => {
it("retries the icon when the same instance shows another tool", async () => {
const { AiToolIcon } = await import("../../AiToolIcon");
const { container, rerender } = render(
<AiToolIcon tool={{ id: "opencode", label: "OpenCode" }} />
);
const { container, rerender } = render(<AiToolIcon tool={{ id: "aider", label: "Aider" }} />);
fireEvent.error(container.querySelector("img")!);
expect(screen.getByText("O")).toBeInTheDocument();
expect(screen.getByText("A")).toBeInTheDocument();
rerender(<AiToolIcon tool={{ id: "cursor", label: "Cursor CLI" }} />);
expect(container.querySelector('img[src="/api/ai-tools/cursor/icon"]')).toBeTruthy();
});
+4 -5
View File
@@ -732,10 +732,9 @@ export interface Session {
/** The AI coding CLI that produced the session: `"claude"` (the default —
* Claude Code's hooks and imported transcripts), `"codex"` (Codex's rollout
* journals and hooks), `"antigravity"` (agy's conversation files and
* hooks), `"cursor"` (Cursor's agent transcripts, no tokens), `"omp"`,
* `"opencode"` (their own session stores, with tokens and subagents) or
* `"grok"` (Grok running the Claude hooks, no tokens). Maps to
* `sessions.tool`. */
* hooks), `"cursor"` (Cursor's agent transcripts, no tokens) or `"omp"`
* (its own session journals, with tokens and subagents). Rows an older build
* wrote may still say `"opencode"` or `"grok"`. Maps to `sessions.tool`. */
tool?: string;
/** The workspace (lane id) that owns the session — the one whose pane held
* it; null for a session no workspace started.
@@ -1217,7 +1216,7 @@ export interface CostResult {
/** GET /api/pricing/quota: plan quota spent per native tool (server/lib/quota.js). */
export interface ToolQuota {
id: "claude" | "codex" | "antigravity" | "cursor" | "grok" | "kimi" | "opencode";
id: "claude" | "codex" | "antigravity" | "cursor" | "kimi";
/** One per plan window: `5h`, `wk`, `mo`, …; `usedPercent` is 0–100.
* `group` names the model pool a window belongs to (Antigravity's). */
windows: Array<{