/** * @file Supplementary OpenAPI 3.0 fragments for the AI Tools routes mounted at * `/api/ai-tools` (see server/routes/ai-tools.js + server/lib/ai-tools.js): * list the catalog with detection, re-detect after hydrating PATH, and * enable/disable a tool or make it the default. Exports `{ tags, schemas, * paths }` for server/openapi-extra.js; schemas are prefixed `AiTools`. * @author Nguyễn Ngọc Trí Vĩ */ const tags = [ { name: "AI Tools", description: "The catalog of AI coding CLIs a lane can run (server/data/ai-tools/*.json + DASHBOARD_AI_TOOLS_DIR), what is installed on PATH, and the user's enable/default choice", }, ]; const errorRef = { $ref: "#/components/schemas/AiToolsError" }; const schemas = { AiToolsTool: { type: "object", required: ["id", "label", "installed", "enabled", "source"], properties: { id: { type: "string", example: "antigravity" }, label: { type: "string", example: "Antigravity" }, command: { type: "string", nullable: true, example: "agy" }, homepageUrl: { type: "string", nullable: true }, installed: { type: "boolean" }, path: { type: "string", nullable: true, example: "/home/u/.local/bin/agy" }, enabled: { type: "boolean" }, source: { type: "string", enum: ["builtin", "custom"] }, launcher: { type: "string", nullable: true, enum: ["claude", "codex", null] }, prompt: { type: "object", nullable: true, properties: { mode: { type: "string", enum: ["argv", "flag", "after-start"] } }, }, resume: { type: "boolean", description: "Offers Resume last session" }, bypass: { type: "boolean", description: "Offers the per-run Bypass permissions switch" }, routerCapable: { type: "boolean", description: "Can use an AI Router profile" }, }, }, AiToolsView: { type: "object", required: ["tools", "default"], properties: { tools: { type: "array", items: { $ref: "#/components/schemas/AiToolsTool" } }, default: { type: "string", nullable: true, description: "Effective default tool id" }, }, }, AiToolsError: { type: "object", properties: { error: { type: "object", properties: { code: { type: "string" }, message: { type: "string" } }, }, }, }, }; const ok = { 200: { description: "The catalog with detection and the effective default", content: { "application/json": { schema: { $ref: "#/components/schemas/AiToolsView" } } }, }, }; const paths = { "/api/ai-tools": { get: { tags: ["AI Tools"], summary: "List the AI Tools catalog", description: "Every catalog tool with whether it was found on PATH, whether it is enabled, and the effective default (stored default if installed+enabled, else claude, else the first installed enabled tool, else null). Arg arrays are never returned.", operationId: "aiToolsList", responses: ok, }, }, "/api/ai-tools/refresh": { post: { tags: ["AI Tools"], summary: "Re-detect installed tools", description: "Merges the login shell's PATH ($SHELL -ilc, 5 s timeout, off with AGENTHUB_SHELL_PATH=0) into the server's, re-reads the catalog and re-detects. Same-origin only.", operationId: "aiToolsRefresh", responses: ok, }, }, "/api/ai-tools/{id}": { put: { tags: ["AI Tools"], summary: "Enable/disable a tool or make it the default", description: "A disabled tool is hidden from Add lane and a NEW lane for it answers 400 EDISABLED; existing lanes keep working. Disabling the default clears it. Same-origin only.", operationId: "aiToolsUpdate", parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }], requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: { enabled: { type: "boolean" }, default: { type: "boolean", enum: [true] }, }, }, }, }, }, responses: { ...ok, 400: { description: "EBADDEFAULT (not installed or disabled) or EBADFIELD", content: { "application/json": { schema: errorRef } }, }, 404: { description: "ENOTOOL — unknown id", content: { "application/json": { schema: errorRef } }, }, }, }, }, }; module.exports = { tags, schemas, paths };