fix(autopilot): cd into --repo so Bun auto-loads brain/.env#464
Conversation
The wrapper sources ~/.zshrc for API keys, but launchd (and systemd, and crontab) start it with CWD = / or $HOME. Bun auto-loads .env from CWD, so users who keep secrets in brain/.env (a natural Bun pattern, no dotenv package needed) never have those vars visible to the daemon. The symptom is silent: cycles look healthy in autopilot.log ([cycle] score=N elapsed=1s next=150s) and gbrain doctor stays at 90/100, but autopilot.err fills with per-page "OPENAI_API_KEY missing" errors and Embedded 0 chunks across N pages on every cycle. Per-page embed errors are caught and warn-logged instead of failing the cycle, so no healthcheck flags it. Single-line fix: cd into --repo before exec. Bun finds brain/.env on the next process startup. Existing shell-init env vars still take precedence (Bun's auto-load doesn't overwrite), so users with keys in ~/.zshrc are unaffected. writeWrapperScript() is shared across all four installers (launchd, systemd, crontab, ephemeral) so this one site covers everything.
|
Friendly bump 🙏 — this regression bit me again today (2026-05-27 on v0.41.18.0). Daemon worker had zero API keys in env (verified via Applied this PR's diff locally to The current upstream template at CI's green. Happy to add a test asserting the install template emits the |
Symptom
Autopilot looks healthy.
gbrain autopilot --statusreturns the cheerful[cycle] score=N elapsed=1s next=150sline.autopilot.logshows cycles dispatching every 5 minutes.gbrain doctorstays at 90/100. ButEmbedded 0 chunks across N pageson every cycle, and the real story is buried in~/.gbrain/autopilot.err:Per-page embed failures are caught and warn-logged rather than propagated, so the cycle exits status=success with
chunks_embedded: 0. No healthcheck flags it. New pages stop being searchable; nothing tells you.Root cause
writeWrapperScript()(src/commands/autopilot.ts) generates this wrapper:It sources the shell profile to pick up API keys exported there. That works for users who keep
OPENAI_API_KEYin~/.zshrc. But Bun also auto-loads.envfrom CWD (the whole point of "no dotenv needed" in a Bun project), and many users put their gbrain secrets inbrain/.env— alongside the brain content, not in shell-init. Since launchd starts the wrapper with CWD =/or$HOME(not--repo), Bun's auto-load looks in the wrong directory and the daemon never seesbrain/.env.The interactive
gbraininvocation works because users typically run it from inside the brain repo, where Bun does findbrain/.env. So this only bites the daemon path, and only for users whose keys are inbrain/.env.Fix
cd '\${safeRepoPath}' || exit 1beforeexec. Bun's CWD-based auto-load now findsbrain/.envon every wrapper invocation. Existing shell-init env vars still take precedence (Bun's auto-load doesn't overwrite), so users with keys in~/.zshrcare unaffected.writeWrapperScript()is shared across launchd / systemd / crontab / ephemeral installers, so one site covers all four.Repro
OPENAI_API_KEYfrom~/.zshrcto~/brain/.env. Confirm in a fresh shell thatecho \$OPENAI_API_KEYis empty.I confirmed the fix locally by patching
~/.gbrain/autopilot-run.shdirectly and checkingps ewwon the spawned Minions worker —OPENAI_API_KEYwas visible in the worker child's env after the fix, absent before.Possible follow-ups (not in this PR)
gbrain autopilotcould also read--repo/.envitself at startup, so the fix works regardless of how the daemon is invoked. Wider blast radius — left for a separate change.gbrain doctorcould aggregate per-page embed failures into awarn-status check when failure rate exceeds a threshold. Would have caught this within one cycle. Happy to file a separate issue if there's interest.Tests
No new test in this PR —
writeWrapperScriptis a string-template function and the change is two lines of generated shell. Happy to add a snapshot test if reviewers prefer.