From 0f53ec84e8ee362b14709d29f10b3dfa390556ab Mon Sep 17 00:00:00 2001 From: bft-codebot Date: Fri, 6 Feb 2026 16:24:48 +0000 Subject: [PATCH] sync(bfmono): fix(gambit): align env loading with init and block .gambit env writes (+19 more) (bfmono@9a36c4a7e) This PR is an automated gambitmono sync of bfmono Gambit packages. - Source: `packages/gambit/` - Core: `packages/gambit-core/` - bfmono rev: 9a36c4a7e Changes: - 9a36c4a7e fix(gambit): align env loading with init and block .gambit env writes - dbe7c54ca feat(gambit-bot): add file actions and scenario deck structure - 855784d6b docs(gambit): add public permissions guide and API jsdoc - 8f0ca0a85 feat(gambit): trace effective permission layers at runtime - 90b4b5071 feat(gambit-core): add phase-1 permission contract primitives - df9280f6a fix(gambit): restore build-bot deck path compatibility - daca46555 feat(simulator-ui): wire build, test, and grade to workspace sessions - e404a17d7 feat(gambit): add workspace-backed serve and bot sandbox flow - 5f4fa86b9 feat(gambit): scaffold workspace defaults in init - cf9b23778 feat(gambit-core): add schema guards and model param passthrough - d0e5a9617 [gambit] move chat message into transcript so it scrolls - 5c6125d99 feat(simulator-ui): open workbench drawer by default - 7c9cd05f8 feat(simulator): gate chat accordion by env flag - a2599068e feat(simulator-ui): add build chat history loading - 9911dae22 feat(simulator-ui): add workbench chat drawer accordion - 8cab8ec1f feat(simulator-ui): dock calibrate drawer and sync updates - d41ba101d Add AAR for phase 3.1.5 deck format build tab - b1b5e2a7e Prefer PROMPT.md after build scaffolds - 50fac8f7b Update Build tab for deck format 1.0 - c5d99df6a feat(gambit-core): add deck format 1.0 stdlib assets Do not edit this repo directly; make changes in bfmono and re-run the sync. --- src/cli.ts | 29 ++++++++++++++++++++++++----- src/commands/scaffold_utils.ts | 12 ++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 1671beb25..1fa8ef782 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -41,6 +41,7 @@ import { loadProjectConfig, resolveWorkspacePermissions, } from "./project_config.ts"; +import { resolveProjectRoot } from "./cli_utils.ts"; const logger = console; const DEFAULT_OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"; @@ -140,11 +141,29 @@ async function resolveBotRoot(opts: { } async function loadGambitEnv() { - const envPath = path.resolve(Deno.cwd(), "gambit", ".env"); - try { - await loadDotenv({ envPath, export: true }); - } catch (err) { - if (!(err instanceof Deno.errors.NotFound)) { + const cwd = Deno.cwd(); + const projectRoot = resolveProjectRoot(cwd); + const candidates = new Set(); + + if (projectRoot) { + candidates.add(path.join(projectRoot, ".env")); + } + candidates.add(path.join(cwd, ".env")); + + // Legacy fallback for old gambit demo layout; prefer root/local .env above. + if (projectRoot) { + candidates.add(path.join(projectRoot, "gambit", ".env")); + } + candidates.add(path.join(cwd, "gambit", ".env")); + + for (const envPath of candidates) { + try { + const info = await Deno.stat(envPath); + if (!info.isFile) continue; + await loadDotenv({ envPath, export: true }); + return; + } catch (err) { + if (err instanceof Deno.errors.NotFound) continue; throw err; } } diff --git a/src/commands/scaffold_utils.ts b/src/commands/scaffold_utils.ts index 9d977786c..8ebd859ca 100644 --- a/src/commands/scaffold_utils.ts +++ b/src/commands/scaffold_utils.ts @@ -176,7 +176,19 @@ export function resolveInvokePrefix(): string { return "gambit"; } +function isPathWithinDotGambit(pathValue: string): boolean { + const normalized = path.normalize(path.resolve(pathValue)); + const segments = normalized.split(/[/\\]+/).filter(Boolean); + return segments.includes(".gambit"); +} + export async function ensureOpenRouterEnv(envPath: string): Promise { + if (isPathWithinDotGambit(envPath)) { + throw new Error( + `Refusing to write OPENROUTER_API_KEY inside .gambit metadata: ${envPath}`, + ); + } + const envKey = Deno.env.get("OPENROUTER_API_KEY")?.trim(); if (envKey) { return false;