Absolutely. The clean pattern is:
- inject compile-time constants in
Bun.build({ define })
- reference those constants in code (not just
process.env[key])
- fallback to runtime env when desired
Here is a concrete example matching your repo style.
Use define in your build script (like packages/opencode/script/build.ts):
await Bun.build({
// ...
define: {
OPENCODE_VERSION: `'${Script.version}'`,
OPENCODE_CHANNEL: `'${Script.channel}'`,
// compile-time baked defaults
OPENCODE_API_COMPILED: JSON.stringify(process.env.OPENCODE_API ?? ""),
OPENCODE_MODELS_URL_COMPILED: JSON.stringify(process.env.OPENCODE_MODELS_URL ?? ""),
OPENCODE_DISABLE_SHARE_COMPILED: JSON.stringify(process.env.OPENCODE_DISABLE_SHARE ?? ""),
},
})
Then in flags (or wherever you centralize env):
declare const OPENCODE_API_COMPILED: string
declare const OPENCODE_MODELS_URL_COMPILED: string
declare const OPENCODE_DISABLE_SHARE_COMPILED: string
function truthyValue(v: string | undefined) {
const s = v?.toLowerCase()
return s === "true" || s === "1"
}
export namespace Flag {
// prefer baked value, fallback to runtime env
export const OPENCODE_API = OPENCODE_API_COMPILED || process.env.OPENCODE_API
export const OPENCODE_MODELS_URL =
OPENCODE_MODELS_URL_COMPILED || process.env.OPENCODE_MODELS_URL
export const OPENCODE_DISABLE_SHARE = truthyValue(
OPENCODE_DISABLE_SHARE_COMPILED || process.env.OPENCODE_DISABLE_SHARE,
)
}
How Bun resolves into generated JS:
define is textual replacement at build time.
- If your code references
OPENCODE_API_COMPILED, Bun substitutes the literal string into output.
- If you only use
process.env[key] dynamic indexing, Bun cannot safely replace specific keys.
Quick way to verify:
- Build once with
OPENCODE_API=https://example.internal
- Inspect bundle output and search for
example.internal (or run and log Flag.OPENCODE_API)
- You should see the baked value even when runtime env is unset.
Practical split (recommended):
- Bake: host URLs, non-secret feature defaults.
- Runtime only: secrets, per-session/per-command behavior (
OPENCODE_CLIENT, auth/passwords, config dir overrides).
If you want, I can next propose an exact allowlist/denylist for your current .env so you can wire it in safely.