From 7bba0a1463f83e2cce96c2e143a984f990cf1edc Mon Sep 17 00:00:00 2001 From: Christopher Tso Date: Wed, 6 May 2026 14:02:34 +0200 Subject: [PATCH] fix(cli): correct resolveCliPath fallback paths for src + dist layouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both fallback path resolutions in resolveCliPath were off: - src: '../../../cli.ts' from .../apps/cli/src/commands/results/ resolved to apps/cli/cli.ts (missing src/). Should be '../../cli.ts'. - dist: '../../cli.js' from .../apps/cli/dist/ resolved to apps/cli.js (one dir too high). tsup emits chunks alongside cli.js, so the entry is in the same dir — should be 'cli.js'. End users with `agentv` installed globally were unaffected because the third fallback (global PATH lookup) covered them. Affected: developers running Studio from a source checkout against any cwd that isn't the agentv repo itself. Strengthened the existing resume-API tests to assert exact 202 (was [202, 500]) so a regression of this off-by-one will fail CI. Closes #1221 Co-Authored-By: Claude Opus 4.7 --- apps/cli/src/commands/results/eval-runner.ts | 11 +++++-- apps/cli/test/commands/results/serve.test.ts | 32 +++++++++----------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/apps/cli/src/commands/results/eval-runner.ts b/apps/cli/src/commands/results/eval-runner.ts index 790b1009..6559a3da 100644 --- a/apps/cli/src/commands/results/eval-runner.ts +++ b/apps/cli/src/commands/results/eval-runner.ts @@ -209,11 +209,16 @@ function resolveCliPath(cwd: string): { binPath: string; args: string[] } | unde } // 2. Try from the current running process location (handles both CJS __dirname - // and ESM import.meta.url; fileURLToPath works correctly on Windows) + // and ESM import.meta.url; fileURLToPath works correctly on Windows). + // Layouts we can be loaded from: + // - dev/source: this module sits at apps/cli/src/commands/results/eval-runner.ts, + // so cli.ts is two dirs up at apps/cli/src/cli.ts. + // - bundled dist: tsup emits the chunk into apps/cli/dist/, alongside cli.js, + // so the entry is in the same directory as currentDir. const currentDir = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); - const fromSrc = path.resolve(currentDir, '../../../cli.ts'); - const fromDist = path.resolve(currentDir, '../../cli.js'); + const fromSrc = path.resolve(currentDir, '../../cli.ts'); + const fromDist = path.resolve(currentDir, 'cli.js'); if (existsSync(fromSrc)) return { binPath: 'bun', args: [fromSrc] }; if (existsSync(fromDist)) return { binPath: 'bun', args: [fromDist] }; diff --git a/apps/cli/test/commands/results/serve.test.ts b/apps/cli/test/commands/results/serve.test.ts index 397f7316..0e69e495 100644 --- a/apps/cli/test/commands/results/serve.test.ts +++ b/apps/cli/test/commands/results/serve.test.ts @@ -900,13 +900,13 @@ describe('serve app', () => { resume: true, }), }); - // Either 202 (spawn succeeded) or 500 (no CLI on disk in test env). - expect([202, 500]).toContain(res.status); - const data = (await res.json()) as { command?: string; error?: string }; - if (res.status === 202) { - expect(data.command).toContain('--resume'); - expect(data.command).toContain('--output .agentv/results/runs/2026-05-06T00-00-00-000Z'); - } + // resolveCliPath must resolve in the test env via the running-process + // fallback (apps/cli/src/cli.ts is two dirs up from this module). A + // 500 here would mean the off-by-one regression from #1221 came back. + expect(res.status).toBe(202); + const data = (await res.json()) as { command: string }; + expect(data.command).toContain('--resume'); + expect(data.command).toContain('--output .agentv/results/runs/2026-05-06T00-00-00-000Z'); }); it('builds --rerun-failed + --output flags from the request', async () => { @@ -921,12 +921,10 @@ describe('serve app', () => { rerun_failed: true, }), }); - expect([202, 500]).toContain(res.status); - if (res.status === 202) { - const data = (await res.json()) as { command: string }; - expect(data.command).toContain('--rerun-failed'); - expect(data.command).toContain('--output runs/r1'); - } + expect(res.status).toBe(202); + const data = (await res.json()) as { command: string }; + expect(data.command).toContain('--rerun-failed'); + expect(data.command).toContain('--output runs/r1'); }); it('builds --retry-errors from the request', async () => { @@ -939,11 +937,9 @@ describe('serve app', () => { retry_errors: 'runs/r0/index.jsonl', }), }); - expect([202, 500]).toContain(res.status); - if (res.status === 202) { - const data = (await res.json()) as { command: string }; - expect(data.command).toContain('--retry-errors runs/r0/index.jsonl'); - } + expect(res.status).toBe(202); + const data = (await res.json()) as { command: string }; + expect(data.command).toContain('--retry-errors runs/r0/index.jsonl'); }); it('rejects resume + rerun_failed combo with 400', async () => {