diff --git a/.changeset/add-bt-cli-bin.md b/.changeset/add-bt-cli-bin.md new file mode 100644 index 000000000..c0859c3a7 --- /dev/null +++ b/.changeset/add-bt-cli-bin.md @@ -0,0 +1,5 @@ +--- +"braintrust": minor +--- + +feat: ship the `bt` CLI with the SDK. Installing `braintrust` now exposes a `bt` command in `node_modules/.bin` that runs the prebuilt native binary for your platform (delivered via `@braintrust/bt-*` optional dependencies). diff --git a/js/bin/bt.cjs b/js/bin/bt.cjs new file mode 100755 index 000000000..5dd1e3d54 --- /dev/null +++ b/js/bin/bt.cjs @@ -0,0 +1,76 @@ +#!/usr/bin/env node +"use strict"; + +// Launcher for the `bt` CLI. The native binary ships in a per-platform +// package (`@braintrust/bt-`) listed as an optionalDependency of +// `braintrust`; npm/pnpm install only the one matching the host. This script +// resolves that binary and forwards argv + exit code. + +const { spawnSync } = require("node:child_process"); + +const PLATFORM_PACKAGES = { + "darwin-arm64": "@braintrust/bt-darwin-arm64", + "darwin-x64": "@braintrust/bt-darwin-x64", + "linux-arm64": "@braintrust/bt-linux-arm64", + "linux-x64-glibc": "@braintrust/bt-linux-x64", + "linux-x64-musl": "@braintrust/bt-linux-x64-musl", + "win32-arm64": "@braintrust/bt-win32-arm64", + "win32-x64": "@braintrust/bt-win32-x64", +}; + +function detectLibc() { + if (process.platform !== "linux") return null; + try { + const report = process.report && process.report.getReport(); + if (report && report.header && report.header.glibcVersionRuntime) { + return "glibc"; + } + return "musl"; + } catch { + return "glibc"; + } +} + +function platformKey() { + const { platform, arch } = process; + if (platform === "linux" && arch === "x64") { + return `linux-x64-${detectLibc()}`; + } + return `${platform}-${arch}`; +} + +function binaryName() { + return process.platform === "win32" ? "bt.exe" : "bt"; +} + +function resolveBinary() { + const pkg = PLATFORM_PACKAGES[platformKey()]; + if (!pkg) { + throw new Error( + `No prebuilt bt binary for ${process.platform}-${process.arch}. ` + + `Supported platforms: ${Object.keys(PLATFORM_PACKAGES).join(", ")}.`, + ); + } + try { + return require.resolve(`${pkg}/bin/${binaryName()}`); + } catch (err) { + throw new Error( + `Failed to locate the bt binary from "${pkg}". It is an optional ` + + `dependency of "braintrust"; reinstall your dependencies to fetch ` + + `it (${err.message}).`, + ); + } +} + +try { + const result = spawnSync(resolveBinary(), process.argv.slice(2), { + stdio: "inherit", + windowsHide: true, + }); + if (result.error) throw result.error; + if (result.signal) process.kill(process.pid, result.signal); + process.exit(result.status ?? 1); +} catch (err) { + console.error(`bt: ${err.message}`); + process.exit(1); +} diff --git a/js/package.json b/js/package.json index 8361036e0..0bf568b95 100644 --- a/js/package.json +++ b/js/package.json @@ -19,7 +19,8 @@ "./dist/index.d.mts": "./dist/browser.d.mts" }, "bin": { - "braintrust": "./dist/cli.js" + "braintrust": "./dist/cli.js", + "bt": "./bin/bt.cjs" }, "exports": { "./package.json": "./package.json", @@ -127,7 +128,8 @@ "files": [ "dist/**/*", "dev/dist/**/*", - "util/dist/**/*" + "util/dist/**/*", + "bin/bt.cjs" ], "scripts": { "build": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" tsup", @@ -224,6 +226,15 @@ "peerDependencies": { "zod": "^3.25.34 || ^4.0" }, + "optionalDependencies": { + "@braintrust/bt-darwin-arm64": "0.10.0", + "@braintrust/bt-darwin-x64": "0.10.0", + "@braintrust/bt-linux-arm64": "0.10.0", + "@braintrust/bt-linux-x64": "0.10.0", + "@braintrust/bt-linux-x64-musl": "0.10.0", + "@braintrust/bt-win32-arm64": "0.10.0", + "@braintrust/bt-win32-x64": "0.10.0" + }, "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/", diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1f040496c..31bde25c3 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -10,6 +10,7 @@ blockExoticSubdeps: true minimumReleaseAge: 4320 # 3 days (in minutes) minimumReleaseAgeExclude: - "@apm-js-collab/*" # instrumentation deps we need to pin closely + - "@braintrust/bt-*" # bt binary packages, published in lockstep with braintrust trustPolicy: no-downgrade # Ignore the check for packages published more than 30 days ago (pnpm 10.27+) # Useful for older packages that pre-date provenance support