Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-bt-cli-bin.md
Original file line number Diff line number Diff line change
@@ -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).
76 changes: 76 additions & 0 deletions js/bin/bt.cjs
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be a .cjs file I think. In general, let's follow https://github.com/getsentry/sentry-cli/blob/master/scripts/install.js or https://blog.sentry.io/publishing-binaries-on-npm/ to the tee. The logs are very intentional there and we should log similar things to ensure people are not confused when installing the package and things fail.

I also have an example repo on how this should look like: https://github.com/lforst/npm-binary-example

Original file line number Diff line number Diff line change
@@ -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-<platform>`) 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);
}
15 changes: 13 additions & 2 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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/",
Expand Down
1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading