feat: model lists and prices that follow each AI tool, a Settings tab per tool, live lane status (#40)

Pricing: the LiteLLM sync now adds every current Claude / GPT (>= 5) /
Gemini (>= 3) chat model before it is used and keeps its own rows
(model_pricing.source = 'sync', the Auto badge) at the published rate. A
user's row (any PUT) and a deleted pattern (model_pricing_dismissed) are
never touched; a deleted built-in row is not re-seeded at the next start; a
reset re-applies the list at once; retired Claude models are priced only
once used.

Models: GET /api/ai-tools/:id/models reads each tool's own list (Codex's
models_cache.json, Claude's aliases named after the newest version plus
priced versions, a catalog tool's vendor rows plus the models its sessions
used). A catalog `model: {flag, vendor?}` field passes `<flag> <id>`
(validated: never a flag or shell text); a per-tool default model starts
each run, and a resume keeps the session's own model.

Settings: every installed, enabled catalog tool without its own tab gets
one (AiToolConfig.tsx): path, homepage, Update, sessions/cost/lanes,
default model, vendor pricing, and the config files its release catalog
lists, edited over the opened sha, syntax-checked, backed up, written
atomically; credential-looking names refused. Antigravity keeps its hooks
card there.

Lanes: a just-started run no longer reads "dead" (age counts the run's
start while the old session is still bound); a second dashboard on the
same database re-broadcasts the lanes the other changed (PRAGMA
data_version, every 2 s); a lane start with a bad model answers 400.

DB: every migration runs under a cross-process lock (<db>-migrate.lock), so
two dashboards started at once on a fresh database both come up.
This commit is contained in:
2026-10-01 13:39:17 +07:00
parent c5aba094bd
commit f887c16309
51 changed files with 2343 additions and 203 deletions
+7 -1
View File
@@ -537,7 +537,12 @@ Request body shape:
| `DELETE` | `/api/git-credentials/:protocol/:host` | Same-origin guarded delete. `404 ENOTFOUND`. |
| `GET` | `/api/ai-tools` | The AI Tools catalog (`server/lib/ai-tools.js`: `server/data/ai-tools/*.json` + `DASHBOARD_AI_TOOLS_DIR`) with detection, enabled flag and the effective default. Flags only, never arg arrays. |
| `POST` | `/api/ai-tools/refresh` | Same-origin guarded: merge the login shell's PATH (`server/lib/shell-path.js`), re-detect. |
| `PUT` | `/api/ai-tools/:id` | Same-origin guarded `{enabled?, default?: true}`. `404 ENOTOOL`, `400 EBADDEFAULT`/`EBADFIELD`. |
| `PUT` | `/api/ai-tools/:id` | Same-origin guarded `{enabled?, default?: true, defaultModel?}` (`""`/`null` clears the default model). `404 ENOTOOL`, `400 EBADDEFAULT`/`EBADFIELD`/`EBADMODEL`. |
| `GET` | `/api/ai-tools/:id/usage` | `{sessions, cost}` for `sessions.tool = id`. `404 ENOTOOL`. |
| `GET` | `/api/ai-tools/:id/config` | The config files the tool's trusted catalog file lists (`server/lib/ai-tool-config.js`), with `exists`/`format`. |
| `GET` | `/api/ai-tools/:id/config/:index` | One file's `{text, sha}`. `404 ENOFILE`/`ENOENT`, `403 ESECRET`, `413 ETOOBIG`, `415 EBINARY`. |
| `PUT` | `/api/ai-tools/:id/config/:index` | Same-origin guarded `{text, baseSha}`: syntax-checked, backed up, atomic. `409 ESTALE`, `400 EBADSYNTAX`, plus the read errors. |
| `GET` | `/api/ai-tools/:id/models` | The tool's own model list (`server/lib/ai-tool-models.js`: Codex's `models_cache.json`, Claude's aliases + priced versions, a catalog tool's vendor rows + the models its sessions used) and its `defaultModel`. `404 ENOTOOL`. |
| `POST` | `/api/ai-tools/:id/install` | Same-origin guarded: start the catalog's install job (`server/lib/ai-tool-jobs.js` — `npm install -g <pkg>@latest` with the login shell's npm, or a vendor script downloaded and run with `bash`). `202 {job}`; `404 ENOTOOL`, `400 ENOINSTALL`, `409 EINSTALLED`/`ENONPM`/`EBUSY`. |
| `POST` | `/api/ai-tools/:id/update` | Same-origin guarded: start the update job; npm only for an npm-managed tool (`server/lib/ai-tool-versions.js`), else `409 ENOTNPM`. `400 ENOUPDATE`, `409 ENOTINSTALLED`/`EBUSY`. |
| `POST` | `/api/ai-tools/update-all` | Same-origin guarded: one job over every npm-managed tool with a newer version plus every self updater. `400 ENOTHING`. |
@@ -1564,6 +1569,7 @@ DASHBOARD_DB_PATH=./data/dashboard.db # SQLite database path
DASHBOARD_AI_TOOLS_DIR= # Extra AI Tools catalog JSON files; an id here replaces the built-in one
AGENTHUB_SHELL_PATH=1 # 0 = never run the login shell to read its PATH (startup + Refresh)
AGENTHUB_AI_TOOLS_SYNC=1 # 0 = never copy the newest release's AI Tools catalog (server/lib/ai-tools-sync.js, daily)
DASHBOARD_LANE_SYNC_MS=2000 # re-broadcast lanes another dashboard on the same DB changed (PRAGMA data_version); 0 = off
# Background services
DASHBOARD_SESSION_SYNC_MS=30000 # Continuous project-sync poll interval (ms); 0 disables the poll (watcher stays)