From 2ba97e83ad364664177e5f9517b61a408b146af4 Mon Sep 17 00:00:00 2001 From: bcode Date: Sat, 2 May 2026 01:27:44 +0000 Subject: [PATCH 1/2] feat: embed Laminar project key into release binaries --- .github/workflows/release.yml | 4 ++++ packages/bcode-browser/src/telemetry.ts | 32 +++++++++++++++++++++++++ packages/opencode/script/build.ts | 4 ++++ packages/opencode/src/index.ts | 6 +++++ 4 files changed, 46 insertions(+) create mode 100644 packages/bcode-browser/src/telemetry.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b1905c6be..26781ede1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,6 +84,10 @@ jobs: OPENCODE_CHANNEL: latest GH_REPO: ${{ github.repository }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Embedded into the binary by build.ts via Bun `define`. Read at + # runtime in @browser-use/bcode-browser/telemetry, gated by + # DO_NOT_TRACK and any user-supplied LMNR_PROJECT_API_KEY. + BCODE_DEFAULT_LMNR_KEY: ${{ secrets.LMNR_PROJECT_API_KEY_OSS }} run: | ./packages/opencode/script/build.ts diff --git a/packages/bcode-browser/src/telemetry.ts b/packages/bcode-browser/src/telemetry.ts new file mode 100644 index 000000000..bba6cefa1 --- /dev/null +++ b/packages/bcode-browser/src/telemetry.ts @@ -0,0 +1,32 @@ +// Telemetry key injection. +// +// At build time, `packages/opencode/script/build.ts` substitutes +// `BCODE_DEFAULT_LMNR_KEY` with a string literal via Bun's `define`. The +// release workflow sources that value from a GitHub Actions secret; local +// `bun run build` invocations leave it empty, so self-builds never emit +// telemetry. +// +// At runtime we set `LMNR_PROJECT_API_KEY` from the embedded default if and +// only if: +// - DO_NOT_TRACK is not set, AND +// - the user has not already set LMNR_PROJECT_API_KEY (BYO key wins), AND +// - the embedded default is non-empty. +// +// No `if (telemetryEnabled)` branches downstream — the future Laminar wiring +// reads `LMNR_PROJECT_API_KEY` and initializes only when present, so the gate +// here decides everything. +// +// Must run before any code that loads Laminar / reads LMNR_PROJECT_API_KEY. + +declare const BCODE_DEFAULT_LMNR_KEY: string + +export const applyTelemetryKey = () => { + if (process.env.DO_NOT_TRACK) return + if (process.env.LMNR_PROJECT_API_KEY) return + // `typeof` first: in dev (no Bun `define` substitution) the identifier is + // undeclared and a direct read throws ReferenceError. + if (typeof BCODE_DEFAULT_LMNR_KEY === "undefined" || !BCODE_DEFAULT_LMNR_KEY) return + process.env.LMNR_PROJECT_API_KEY = BCODE_DEFAULT_LMNR_KEY +} + +export * as Telemetry from "./telemetry" diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 8f8b224f4..d89972744 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -235,6 +235,10 @@ for (const item of targets) { OPENCODE_WORKER_PATH: workerPath, OPENCODE_CHANNEL: `'${Script.channel}'`, OPENCODE_LIBC: item.os === "linux" ? `'${item.abi ?? "glibc"}'` : "", + // Build-time-embedded Laminar project key. Populated by release CI from + // the LMNR_PROJECT_API_KEY_OSS secret; empty for local builds. Runtime + // use is gated in @browser-use/bcode-browser/src/telemetry.ts. + BCODE_DEFAULT_LMNR_KEY: JSON.stringify(process.env.BCODE_DEFAULT_LMNR_KEY ?? ""), }, }) diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 86bda3960..e689f120e 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -1,3 +1,9 @@ +// Telemetry import sits above all others so the key gate runs before any +// downstream code reads LMNR_PROJECT_API_KEY. ESM hoists imports, but the +// `applyTelemetryKey()` call is the first non-import statement to execute. +import { Telemetry } from "@browser-use/bcode-browser/telemetry" +Telemetry.applyTelemetryKey() + import yargs from "yargs" import { hideBin } from "yargs/helpers" import { RunCommand } from "./cli/cmd/run" From 70e6fd191b3573bb2f5dc3abdf055372b3d4dc2a Mon Sep 17 00:00:00 2001 From: bcode Date: Sat, 2 May 2026 03:54:40 +0000 Subject: [PATCH 2/2] fix(telemetry): address cubic review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move applyTelemetryKey() invocation into the telemetry module itself as an import side effect. ESM evaluates module-import side-effects before any subsequent module-load code in the importing file, so this is unambiguously ordered before the rest of opencode's imports — sidestepping the static-import-hoisting concern. - Use `!== undefined` for LMNR_PROJECT_API_KEY presence check so an explicitly-set empty string is respected as 'no key please' instead of being clobbered by the embedded default. - DO_NOT_TRACK truthiness check is intentional and unchanged: the de-facto standard (consoledonottrack.com) defines opt-out as presence with any non-empty value. --- packages/bcode-browser/src/telemetry.ts | 29 ++++++++++++++++++------- packages/opencode/src/index.ts | 10 ++++----- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/bcode-browser/src/telemetry.ts b/packages/bcode-browser/src/telemetry.ts index bba6cefa1..fcb078d16 100644 --- a/packages/bcode-browser/src/telemetry.ts +++ b/packages/bcode-browser/src/telemetry.ts @@ -8,25 +8,38 @@ // // At runtime we set `LMNR_PROJECT_API_KEY` from the embedded default if and // only if: -// - DO_NOT_TRACK is not set, AND -// - the user has not already set LMNR_PROJECT_API_KEY (BYO key wins), AND +// - DO_NOT_TRACK is not set (any non-empty value opts out — DO_NOT_TRACK +// standard convention), AND +// - LMNR_PROJECT_API_KEY is not already set in the environment (BYO key +// wins; explicit empty string is respected as "no key please"), AND // - the embedded default is non-empty. // -// No `if (telemetryEnabled)` branches downstream — the future Laminar wiring -// reads `LMNR_PROJECT_API_KEY` and initializes only when present, so the gate -// here decides everything. -// -// Must run before any code that loads Laminar / reads LMNR_PROJECT_API_KEY. +// `applyTelemetryKey()` is invoked as a side effect on module import (last +// statement of this file). Because `packages/opencode/src/index.ts` imports +// this module before any other module that might consume the env var, the +// gate is guaranteed to run before any downstream module-load code can +// observe `LMNR_PROJECT_API_KEY` — sidestepping ESM static-import hoisting +// entirely. declare const BCODE_DEFAULT_LMNR_KEY: string export const applyTelemetryKey = () => { + // DO_NOT_TRACK: presence with any non-empty value opts out, per the + // de-facto standard (consoledonottrack.com, Astro, Homebrew, npm). if (process.env.DO_NOT_TRACK) return - if (process.env.LMNR_PROJECT_API_KEY) return + // LMNR_PROJECT_API_KEY: presence (not truthiness) wins so users who + // explicitly set it to an empty string get exactly that — no key. + if (process.env.LMNR_PROJECT_API_KEY !== undefined) return // `typeof` first: in dev (no Bun `define` substitution) the identifier is // undeclared and a direct read throws ReferenceError. if (typeof BCODE_DEFAULT_LMNR_KEY === "undefined" || !BCODE_DEFAULT_LMNR_KEY) return process.env.LMNR_PROJECT_API_KEY = BCODE_DEFAULT_LMNR_KEY } +// Run as an import side effect: this module is imported as the very first +// import of `packages/opencode/src/index.ts`, so by the time any other +// module's top-level code reads `LMNR_PROJECT_API_KEY` the gate has already +// resolved. +applyTelemetryKey() + export * as Telemetry from "./telemetry" diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index e689f120e..272df358a 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -1,8 +1,8 @@ -// Telemetry import sits above all others so the key gate runs before any -// downstream code reads LMNR_PROJECT_API_KEY. ESM hoists imports, but the -// `applyTelemetryKey()` call is the first non-import statement to execute. -import { Telemetry } from "@browser-use/bcode-browser/telemetry" -Telemetry.applyTelemetryKey() +// Telemetry key injection runs as an import side effect of this module, +// before any subsequent import is evaluated. Keep this as the FIRST import +// so the LMNR_PROJECT_API_KEY env var is settled before any downstream +// module-load code reads it. +import "@browser-use/bcode-browser/telemetry" import yargs from "yargs" import { hideBin } from "yargs/helpers"