From 5f430cb0e77822a254f7ec016f41fe9d6ab0ca9f Mon Sep 17 00:00:00 2001 From: Christopher Date: Thu, 9 Apr 2026 08:24:21 +0000 Subject: [PATCH 1/2] fix(studio): rename /api/projects to /api/benchmarks Renames the studio API endpoints from /api/projects to /api/benchmarks and updates the startup log to show "Benchmarks API" instead of "Projects API", aligning with user-facing labels. Closes #1016 Co-Authored-By: Claude Sonnet 4.6 (1M context) --- apps/cli/src/commands/results/eval-runner.ts | 12 ++--- apps/cli/src/commands/results/serve.ts | 50 ++++++++++---------- apps/studio/src/lib/api.ts | 12 ++--- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/apps/cli/src/commands/results/eval-runner.ts b/apps/cli/src/commands/results/eval-runner.ts index 0e19e10e1..90dc96767 100644 --- a/apps/cli/src/commands/results/eval-runner.ts +++ b/apps/cli/src/commands/results/eval-runner.ts @@ -359,7 +359,7 @@ export function registerEvalRoutes( }); // ── Project-scoped variants ──────────────────────────────────────────── - app.get('/api/projects/:projectId/eval/discover', async (c) => { + app.get('/api/benchmarks/:projectId/eval/discover', async (c) => { const cwd = getCwd(c); try { const files = await discoverEvalFiles(cwd); @@ -375,7 +375,7 @@ export function registerEvalRoutes( } }); - app.get('/api/projects/:projectId/eval/targets', async (c) => { + app.get('/api/benchmarks/:projectId/eval/targets', async (c) => { const cwd = getCwd(c); try { const names = await discoverTargetsInProject(cwd); @@ -385,7 +385,7 @@ export function registerEvalRoutes( } }); - app.post('/api/projects/:projectId/eval/run', async (c) => { + app.post('/api/benchmarks/:projectId/eval/run', async (c) => { const cwd = getCwd(c); let body: RunEvalRequest; @@ -459,7 +459,7 @@ export function registerEvalRoutes( } }); - app.get('/api/projects/:projectId/eval/status/:id', (c) => { + app.get('/api/benchmarks/:projectId/eval/status/:id', (c) => { const id = c.req.param('id'); const run = activeRuns.get(id ?? ''); if (!run) return c.json({ error: 'Run not found' }, 404); @@ -475,7 +475,7 @@ export function registerEvalRoutes( }); }); - app.get('/api/projects/:projectId/eval/runs', (c) => { + app.get('/api/benchmarks/:projectId/eval/runs', (c) => { const runs = [...activeRuns.values()].map((r) => ({ id: r.id, status: r.status, @@ -488,7 +488,7 @@ export function registerEvalRoutes( return c.json({ runs }); }); - app.post('/api/projects/:projectId/eval/preview', async (c) => { + app.post('/api/benchmarks/:projectId/eval/preview', async (c) => { let body: RunEvalRequest; try { body = await c.req.json(); diff --git a/apps/cli/src/commands/results/serve.ts b/apps/cli/src/commands/results/serve.ts index 72371a7ab..7e2affe09 100644 --- a/apps/cli/src/commands/results/serve.ts +++ b/apps/cli/src/commands/results/serve.ts @@ -11,11 +11,11 @@ * - GET /api/runs/:filename — load results from a specific run workspace * - GET /api/feedback — read feedback reviews * - POST /api/feedback — write feedback reviews - * - GET /api/projects — list registered projects - * - GET /api/projects/:projectId/runs — project-scoped run list + * - GET /api/benchmarks — list registered benchmarks + * - GET /api/benchmarks/:projectId/runs — benchmark-scoped run list * * All data routes (runs, suites, categories, evals, experiments, targets) - * exist in both unscoped (/api/...) and project-scoped (/api/projects/:projectId/...) + * exist in both unscoped (/api/...) and benchmark-scoped (/api/benchmarks/:projectId/...) * variants. They share handler functions via DataContext, differing only in * how searchDir is resolved. * @@ -773,7 +773,7 @@ export function createApp( }; } - app.get('/api/projects', async (c) => { + app.get('/api/benchmarks', async (c) => { const registry = loadProjectRegistry(); const projects = await Promise.all( registry.projects.map(async (p) => { @@ -802,7 +802,7 @@ export function createApp( return c.json({ projects }); }); - app.post('/api/projects', async (c) => { + app.post('/api/benchmarks', async (c) => { if (readOnly) { return c.json({ error: 'Studio is running in read-only mode' }, 403); } @@ -816,7 +816,7 @@ export function createApp( } }); - app.delete('/api/projects/:projectId', (c) => { + app.delete('/api/benchmarks/:projectId', (c) => { if (readOnly) { return c.json({ error: 'Studio is running in read-only mode' }, 403); } @@ -825,7 +825,7 @@ export function createApp( return c.json({ ok: true }); }); - app.get('/api/projects/:projectId/summary', async (c) => { + app.get('/api/benchmarks/:projectId/summary', async (c) => { const project = getProject(c.req.param('projectId') ?? ''); if (!project) return c.json({ error: 'Project not found' }, 404); try { @@ -846,7 +846,7 @@ export function createApp( } }); - app.post('/api/projects/discover', async (c) => { + app.post('/api/benchmarks/discover', async (c) => { if (readOnly) { return c.json({ error: 'Studio is running in read-only mode' }, 403); } @@ -862,7 +862,7 @@ export function createApp( }); /** Aggregate runs from all registered projects, sorted by timestamp descending. */ - app.get('/api/projects/all-runs', async (c) => { + app.get('/api/benchmarks/all-runs', async (c) => { const registry = loadProjectRegistry(); const allRuns: Array<{ filename: string; @@ -1026,7 +1026,7 @@ export function createApp( // ── Data routes (project-scoped) ────────────────────────────────────── // Same handlers as above, with project-resolved DataContext via withProject. - app.get('/api/projects/:projectId/config', (c) => + app.get('/api/benchmarks/:projectId/config', (c) => withProject(c, (ctx, dataCtx) => handleConfig(ctx, dataCtx, { readOnly, @@ -1034,36 +1034,36 @@ export function createApp( }), ), ); - app.get('/api/projects/:projectId/remote/status', (c) => + app.get('/api/benchmarks/:projectId/remote/status', (c) => withProject(c, async (ctx, dataCtx) => ctx.json(await getRemoteResultsStatus(dataCtx.searchDir)), ), ); - app.post('/api/projects/:projectId/remote/sync', (c) => + app.post('/api/benchmarks/:projectId/remote/sync', (c) => withProject(c, async (ctx, dataCtx) => ctx.json(await syncRemoteResults(dataCtx.searchDir))), ); - app.get('/api/projects/:projectId/runs', (c) => withProject(c, handleRuns)); - app.get('/api/projects/:projectId/runs/:filename', (c) => withProject(c, handleRunDetail)); - app.get('/api/projects/:projectId/runs/:filename/suites', (c) => withProject(c, handleRunSuites)); - app.get('/api/projects/:projectId/runs/:filename/categories', (c) => + app.get('/api/benchmarks/:projectId/runs', (c) => withProject(c, handleRuns)); + app.get('/api/benchmarks/:projectId/runs/:filename', (c) => withProject(c, handleRunDetail)); + app.get('/api/benchmarks/:projectId/runs/:filename/suites', (c) => withProject(c, handleRunSuites)); + app.get('/api/benchmarks/:projectId/runs/:filename/categories', (c) => withProject(c, handleRunCategories), ); - app.get('/api/projects/:projectId/runs/:filename/categories/:category/suites', (c) => + app.get('/api/benchmarks/:projectId/runs/:filename/categories/:category/suites', (c) => withProject(c, handleCategorySuites), ); - app.get('/api/projects/:projectId/runs/:filename/evals/:evalId', (c) => + app.get('/api/benchmarks/:projectId/runs/:filename/evals/:evalId', (c) => withProject(c, handleEvalDetail), ); - app.get('/api/projects/:projectId/runs/:filename/evals/:evalId/files', (c) => + app.get('/api/benchmarks/:projectId/runs/:filename/evals/:evalId/files', (c) => withProject(c, handleEvalFiles), ); - app.get('/api/projects/:projectId/runs/:filename/evals/:evalId/files/*', (c) => + app.get('/api/benchmarks/:projectId/runs/:filename/evals/:evalId/files/*', (c) => withProject(c, handleEvalFileContent), ); - app.get('/api/projects/:projectId/experiments', (c) => withProject(c, handleExperiments)); - app.get('/api/projects/:projectId/compare', (c) => withProject(c, handleCompare)); - app.get('/api/projects/:projectId/targets', (c) => withProject(c, handleTargets)); - app.get('/api/projects/:projectId/feedback', (c) => withProject(c, handleFeedbackRead)); + app.get('/api/benchmarks/:projectId/experiments', (c) => withProject(c, handleExperiments)); + app.get('/api/benchmarks/:projectId/compare', (c) => withProject(c, handleCompare)); + app.get('/api/benchmarks/:projectId/targets', (c) => withProject(c, handleTargets)); + app.get('/api/benchmarks/:projectId/feedback', (c) => withProject(c, handleFeedbackRead)); // ── Eval runner routes (discovery, launch, status) ──────────────────── @@ -1308,7 +1308,7 @@ export const resultsServeCommand = command({ console.log('Run an evaluation to see results: agentv eval '); } console.log(`Dashboard: http://localhost:${listenPort}`); - console.log(`Projects API: http://localhost:${listenPort}/api/projects`); + console.log(`Benchmarks API: http://localhost:${listenPort}/api/benchmarks`); console.log('Press Ctrl+C to stop'); const { serve: startServer } = await import('@hono/node-server'); diff --git a/apps/studio/src/lib/api.ts b/apps/studio/src/lib/api.ts index fa7a43fe7..c114078ec 100644 --- a/apps/studio/src/lib/api.ts +++ b/apps/studio/src/lib/api.ts @@ -230,7 +230,7 @@ export function isPassing(score: number, passThreshold: number = DEFAULT_PASS_TH export const projectListOptions = queryOptions({ queryKey: ['projects'], - queryFn: () => fetchJson('/api/projects'), + queryFn: () => fetchJson('/api/benchmarks'), refetchInterval: 10_000, }); @@ -240,7 +240,7 @@ export function useProjectList() { export const allProjectRunsOptions = queryOptions({ queryKey: ['projects', 'all-runs'], - queryFn: () => fetchJson('/api/projects/all-runs'), + queryFn: () => fetchJson('/api/benchmarks/all-runs'), refetchInterval: 5_000, }); @@ -249,7 +249,7 @@ export function useAllProjectRuns() { } export async function addProjectApi(projectPath: string): Promise { - const res = await fetch('/api/projects', { + const res = await fetch('/api/benchmarks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: projectPath }), @@ -262,7 +262,7 @@ export async function addProjectApi(projectPath: string): Promise } export async function removeProjectApi(projectId: string): Promise { - const res = await fetch(`/api/projects/${encodeURIComponent(projectId)}`, { + const res = await fetch(`/api/benchmarks/${encodeURIComponent(projectId)}`, { method: 'DELETE', }); if (!res.ok) { @@ -271,7 +271,7 @@ export async function removeProjectApi(projectId: string): Promise { } export async function discoverProjectsApi(dirPath: string): Promise { - const res = await fetch('/api/projects/discover', { + const res = await fetch('/api/benchmarks/discover', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: dirPath }), @@ -286,7 +286,7 @@ export async function discoverProjectsApi(dirPath: string): Promise Date: Thu, 9 Apr 2026 08:25:39 +0000 Subject: [PATCH 2/2] style: fix biome formatting for benchmarks route --- apps/cli/src/commands/results/serve.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/cli/src/commands/results/serve.ts b/apps/cli/src/commands/results/serve.ts index 7e2affe09..acb658c84 100644 --- a/apps/cli/src/commands/results/serve.ts +++ b/apps/cli/src/commands/results/serve.ts @@ -1044,7 +1044,9 @@ export function createApp( ); app.get('/api/benchmarks/:projectId/runs', (c) => withProject(c, handleRuns)); app.get('/api/benchmarks/:projectId/runs/:filename', (c) => withProject(c, handleRunDetail)); - app.get('/api/benchmarks/:projectId/runs/:filename/suites', (c) => withProject(c, handleRunSuites)); + app.get('/api/benchmarks/:projectId/runs/:filename/suites', (c) => + withProject(c, handleRunSuites), + ); app.get('/api/benchmarks/:projectId/runs/:filename/categories', (c) => withProject(c, handleRunCategories), );