diff --git a/.changeset/calm-bridges-build.md b/.changeset/calm-bridges-build.md new file mode 100644 index 000000000000..a24f99771a6e --- /dev/null +++ b/.changeset/calm-bridges-build.md @@ -0,0 +1,5 @@ +--- +'@ai-sdk/harness-claude-code': patch +--- + +fix(harness-claude-code): avoid invalid bridge asset paths in bundled applications diff --git a/packages/harness-claude-code/src/claude-code-bootstrap.test.ts b/packages/harness-claude-code/src/claude-code-bootstrap.test.ts new file mode 100644 index 000000000000..777953ee572c --- /dev/null +++ b/packages/harness-claude-code/src/claude-code-bootstrap.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from 'vitest'; +import { resolveBridgeAssetUrl } from './claude-code-bootstrap'; + +describe('resolveBridgeAssetUrl', () => { + it('resolves bridge assets beside source and bundled modules', () => { + const sourceModuleUrl = new URL( + './claude-code-bootstrap.ts', + import.meta.url, + ); + const bundledModuleUrl = new URL('../dist/index.js', import.meta.url); + + expect( + resolveBridgeAssetUrl({ + name: 'package.json', + moduleUrl: sourceModuleUrl, + }), + ).toEqual(new URL('./bridge/package.json', import.meta.url)); + expect( + resolveBridgeAssetUrl({ + name: 'package.json', + moduleUrl: bundledModuleUrl, + }), + ).toEqual(new URL('../dist/bridge/package.json', import.meta.url)); + }); +}); diff --git a/packages/harness-claude-code/src/claude-code-bootstrap.ts b/packages/harness-claude-code/src/claude-code-bootstrap.ts index 7a45beb9f301..452dc5bf53a6 100644 --- a/packages/harness-claude-code/src/claude-code-bootstrap.ts +++ b/packages/harness-claude-code/src/claude-code-bootstrap.ts @@ -51,19 +51,23 @@ export async function getClaudeCodeBootstrap(): Promise { } async function readBridgeAsset(name: string): Promise { - const candidates = [ - new URL(`./bridge/${name}`, import.meta.url), - new URL(`../bridge/${name}`, import.meta.url), - ]; - let lastErr: unknown; - for (const url of candidates) { - try { - return await readFile(fileURLToPath(url), 'utf8'); - } catch (err) { - const code = (err as NodeJS.ErrnoException).code; - if (code !== 'ENOENT') throw err; - lastErr = err; - } - } - throw lastErr ?? new Error(`bridge asset not found: ${name}`); + return readFile( + fileURLToPath( + resolveBridgeAssetUrl({ + name, + moduleUrl: import.meta.url, + }), + ), + 'utf8', + ); +} + +export function resolveBridgeAssetUrl({ + name, + moduleUrl, +}: { + name: string; + moduleUrl: string | URL; +}): URL { + return new URL(`./bridge/${name}`, moduleUrl); }