From 804e2f1077feee1b07cf100dcd007c59045b379d Mon Sep 17 00:00:00 2001 From: Will Washburn Date: Wed, 22 Apr 2026 12:09:55 -0400 Subject: [PATCH] fix(sdk): find broker in package-root bin/ without relying on postinstall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The agent-relay npm tarball ships platform-specific brokers at its top-level `bin/` (`bin/agent-relay-broker-darwin-arm64`, etc.), while `getBrokerBinaryPath()` only searched `packages/sdk/bin/`. Users without a successful postinstall step (--ignore-scripts, pnpm/bun script policies, corporate proxies blocking the fallback download) got `spawn agent-relay-broker ENOENT` even though the binary was sitting in the installed tarball. Walk up to 6 ancestors of the SDK module and check for `bin/agent-relay-broker(-{platform}-{arch})` so the tarball layout resolves directly — no postinstall copy required. Dev checkout still resolves to target/release via the existing earlier search step. Verified against the real agent-relay@4.0.37 tarball with --ignore-scripts: resolver now returns the bundled binary from both the `packages/sdk/` and `node_modules/@agent-relay/sdk/` load paths. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/sdk/src/broker-path.ts | 45 ++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/packages/sdk/src/broker-path.ts b/packages/sdk/src/broker-path.ts index 1a3879bbb..e9c6d58a2 100644 --- a/packages/sdk/src/broker-path.ts +++ b/packages/sdk/src/broker-path.ts @@ -90,6 +90,25 @@ function getSdkBinDirs(): string[] { return binDirs; } +// The `agent-relay` npm tarball ships platform-specific brokers at its +// top-level `bin/` (not inside `packages/sdk/bin/`). Walk up from the SDK +// module looking for any ancestor with a `bin/` directory so we can find +// the binary without depending on postinstall to copy it. +function getAncestorBinDirs(): string[] { + const binDirs: string[] = []; + const start = getCurrentModuleDir(); + if (!start) return binDirs; + + let current = resolve(start); + for (let i = 0; i < 6; i++) { + addUniquePath(binDirs, join(current, 'bin')); + const parent = resolve(current, '..'); + if (parent === current) break; + current = parent; + } + return binDirs; +} + function getDevelopmentBinaryPaths(ext: string, binDirs: string[]): string[] { const binaryPaths: string[] = []; const repoRoots = new Set(); @@ -146,14 +165,18 @@ function getSourceCheckoutBinaryPaths(ext: string, binDirs: string[]): string[] * 2. Local Cargo build when the SDK is loaded from an agent-relay source checkout * 3. SDK's bin/ directory (resolved via CJS globals, createRequire, or import.meta.url) * 4. Platform-specific name (agent-relay-broker-{platform}-{arch}) in bin/ - * 5. Common Cargo development paths (target/release and target/debug) - * 6. PATH lookup via `which` / `where` + * 5. Ancestor bin/ directories (the `agent-relay` tarball ships platform- + * specific broker binaries at its package-root `bin/` — finding them + * here removes the postinstall copy dependency) + * 6. Common Cargo development paths (target/release and target/debug) + * 7. PATH lookup via `which` / `where` * * @returns Absolute path to the broker binary, or null if not found */ export function getBrokerBinaryPath(): string | null { const ext = process.platform === 'win32' ? '.exe' : ''; const binDirs = getSdkBinDirs(); + const ancestorBinDirs = getAncestorBinDirs(); const platformSpecific = `${BROKER_NAME}-${process.platform}-${process.arch}${ext}`; const override = process.env.BROKER_BINARY_PATH ?? process.env.AGENT_RELAY_BIN; @@ -189,14 +212,28 @@ export function getBrokerBinaryPath(): string | null { } } - // 4. Common development paths for local Cargo builds. + // 4. Ancestor bin/ directories (the agent-relay tarball ships brokers at + // its package-root bin/ — exact name first for parity with bundled SDKs, + // then platform-specific name for the prebuilt-only case). + for (const binDir of ancestorBinDirs) { + const exactPath = join(binDir, `${BROKER_NAME}${ext}`); + if (existsSync(exactPath)) { + return exactPath; + } + const platformPath = join(binDir, platformSpecific); + if (existsSync(platformPath)) { + return platformPath; + } + } + + // 5. Common development paths for local Cargo builds. for (const developmentPath of getDevelopmentBinaryPaths(ext, binDirs)) { if (existsSync(developmentPath)) { return developmentPath; } } - // 5. PATH lookup + // 6. PATH lookup try { const cmd = process.platform === 'win32' ? 'where' : 'which'; const result = execFileSync(cmd, [BROKER_NAME], {