fix: shell-independent API key resolution (#11)#13
Conversation
Fixes the 'Missing Authentication header' bug in non-interactive shells by loading sidecar's .env and auth.json into process.env at startup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New module loads API keys from sidecar .env and auth.json into process.env at CLI bootstrap, with priority: env > .env > auth.json. Exports loadEnvEntries from api-key-store for reuse. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace dotenv + legacy migration in CLI with loadCredentials(). Simplify validateApiKey() to pure process.env check with actionable error message mentioning sidecar setup, ~/.zshenv, and auth.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add zsh-specific note to Option B setup and troubleshooting entry for "Missing Authentication header" in non-interactive shells. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents Jest from running duplicate tests from git worktree directories. Applied to jest.config.js and all package.json test scripts that override testPathIgnorePatterns. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR adds a centralized credential loader (src/utils/env-loader.js) that aggregates API keys from process.env, a sidecar .env, and auth.json in deterministic priority, updates startup to invoke it, exposes loadEnvEntries from api-key-store, tightens validator checks, adds tests, and updates docs and test ignore patterns. Changes
Sequence DiagramsequenceDiagram
participant CLI as Sidecar CLI
participant Loader as env-loader
participant SidecarEnv as Sidecar .env
participant AuthJSON as auth.json
participant ProcessEnv as process.env
participant Validator as validateApiKey()
CLI->>Loader: loadCredentials()
Loader->>ProcessEnv: Inspect existing env keys
ProcessEnv-->>Loader: Existing values (if any)
Loader->>SidecarEnv: Read ~/.config/sidecar/.env
SidecarEnv-->>Loader: .env entries
Loader->>ProcessEnv: Merge .env entries (no overwrite)
Loader->>Loader: Migrate legacy key names (e.g., GEMINI_API_KEY → GOOGLE_GENERATIVE_AI_API_KEY)
Loader->>AuthJSON: Read ~/.local/share/opencode/auth.json
AuthJSON-->>Loader: JSON credentials
Loader->>ProcessEnv: Merge auth.json entries (if not set)
Loader-->>CLI: Done
CLI->>Validator: validateApiKey()
Validator->>ProcessEnv: Check required API env var
ProcessEnv-->>Validator: Key present or undefined
Validator-->>CLI: Validation result / error message
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
skill/SKILL.md (1)
876-879: Consider documenting credential source precedence explicitly in troubleshooting.Adding a short note like “resolution order is
process.env→~/.config/sidecar/.env→~/.local/share/opencode/auth.json(first wins)” would reduce ambiguity when multiple sources are present.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skill/SKILL.md` around lines 876 - 879, Add a single-sentence troubleshooting note in SKILL.md near the API key setup section that explicitly states the credential source precedence: "resolution order is process.env → ~/.config/sidecar/.env → ~/.local/share/opencode/auth.json (first wins)"; place it alongside the existing three bullet fixes so readers know which source takes priority when multiple credentials exist.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/superpowers/specs/2026-03-14-shell-independent-keys-design.md`:
- Line 73: Update the spec's unit test path reference: replace the incorrect
path string "tests/utils/env-loader.test.js" with the actual test file path
"tests/env-loader.test.js" in the document (the line starting with "Unit Tests:
`tests/utils/env-loader.test.js`") so the spec correctly points to the added
test file.
- Around line 19-23: Add a language/info string "text" to the fenced code blocks
that show the env precedence list (the block starting with "1. process.env
(already set)") and the block starting "Error: GOOGLE_GENERATIVE_AI_API_KEY not
found." (and the other occurrence around lines 50-58) so the fences read ```text
instead of ```, satisfying MD040; locate these by searching for those exact
block contents and update each opening fence only.
In `@src/utils/env-loader.js`:
- Around line 8-10: Convert the repository to ESM so env-loader.js can be
migrated: add "type": "module" to package.json, update logger.js,
api-key-store.js, and auth-json.js to export via ESM (export default / named
exports) and adjust their internal require/module.exports usage, then change
env-loader.js to use import statements for logger, loadEnvEntries,
PROVIDER_ENV_MAP, LEGACY_KEY_NAMES, and readAuthJsonKeys; ensure exported symbol
names match (or add named exports) so env-loader.js can import them directly.
In `@src/utils/validators.js`:
- Around line 252-257: The remediation text that builds the error string for
providerInfo.key currently hardcodes a single auth.json location; update that
message to avoid implying a single path by referring to the "default auth.json
path" or making it explicit that the shown path is an example (e.g. "or add key
to the default auth.json path (e.g. ~/.local/share/opencode/auth.json)"). Modify
the error string construction around the code that references providerInfo.key
so the third bullet becomes generic wording like "or add key to the default
auth.json path (for example: ~/.local/share/opencode/auth.json)", preserving
providerInfo.key interpolation and the other bullets.
---
Nitpick comments:
In `@skill/SKILL.md`:
- Around line 876-879: Add a single-sentence troubleshooting note in SKILL.md
near the API key setup section that explicitly states the credential source
precedence: "resolution order is process.env → ~/.config/sidecar/.env →
~/.local/share/opencode/auth.json (first wins)"; place it alongside the existing
three bullet fixes so readers know which source takes priority when multiple
credentials exist.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1a350fb4-dfc3-4612-b4da-b7b5d00cb920
📒 Files selected for processing (10)
CLAUDE.mdbin/sidecar.jsdocs/superpowers/specs/2026-03-14-shell-independent-keys-design.mdjest.config.jspackage.jsonskill/SKILL.mdsrc/utils/api-key-store.jssrc/utils/env-loader.jssrc/utils/validators.jstests/env-loader.test.js
| ``` | ||
| 1. process.env (already set) <- highest, never overwritten | ||
| 2. ~/.config/sidecar/.env <- user-configured via `sidecar setup` | ||
| 3. ~/.local/share/opencode/auth.json <- OpenCode SDK fallback | ||
| ``` |
There was a problem hiding this comment.
Add language identifiers to fenced code blocks.
Both fenced blocks should include a language/info string to satisfy MD040.
Proposed fix
-```
+```text
1. process.env (already set) <- highest, never overwritten
2. ~/.config/sidecar/.env <- user-configured via `sidecar setup`
3. ~/.local/share/opencode/auth.json <- OpenCode SDK fallback@@
- +text
Error: GOOGLE_GENERATIVE_AI_API_KEY not found.
@@
- Add key to ~/.local/share/opencode/auth.json
Also applies to: 50-58
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 19-19: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/superpowers/specs/2026-03-14-shell-independent-keys-design.md` around
lines 19 - 23, Add a language/info string "text" to the fenced code blocks that
show the env precedence list (the block starting with "1. process.env (already
set)") and the block starting "Error: GOOGLE_GENERATIVE_AI_API_KEY not found."
(and the other occurrence around lines 50-58) so the fences read ```text instead
of ```, satisfying MD040; locate these by searching for those exact block
contents and update each opening fence only.
docs/superpowers/specs/2026-03-14-shell-independent-keys-design.md
Outdated
Show resolved
Hide resolved
| const { logger } = require('./logger'); | ||
| const { loadEnvEntries, PROVIDER_ENV_MAP, LEGACY_KEY_NAMES } = require('./api-key-store'); | ||
| const { readAuthJsonKeys } = require('./auth-json'); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "package.json module type:"
jq -r '.type // "<missing>"' package.json
echo
echo "CommonJS usage in changed src/bin files:"
rg -nP --type=js '\b(require\s*\(|module\.exports\b)' src/utils/env-loader.js bin/sidecar.jsRepository: jrenaldi79/sidecar
Length of output: 1582
🏁 Script executed:
# Check the three imported modules
echo "=== logger.js (first 15 lines) ==="
head -15 src/utils/logger.js
echo
echo "=== api-key-store.js (first 15 lines) ==="
head -15 src/utils/api-key-store.js
echo
echo "=== auth-json.js (first 15 lines) ==="
head -15 src/utils/auth-json.js
echo
echo "=== Check for module.exports in dependencies ==="
rg -n 'module\.exports|export ' src/utils/{logger,api-key-store,auth-json}.js
echo
echo "=== Check if CLAUDE.md was updated ==="
rg 'env-loader|env_loader' CLAUDE.md || echo "No mention of env-loader in CLAUDE.md"Repository: jrenaldi79/sidecar
Length of output: 1998
Convert to ESM as part of broader package migration.
This file uses CommonJS (require/module.exports) in a src/ directory, violating the ESM-only guideline. However, converting only this file is not feasible—all three imported modules (logger, api-key-store, auth-json) are currently CommonJS, and package.json lacks "type": "module".
To complete the conversion:
- Set
"type": "module"in package.json - Convert logger.js, api-key-store.js, and auth-json.js to ESM exports
- Update env-loader.js to use ESM imports (as proposed in original comment)
CLAUDE.md was correctly updated with env-loader.js documentation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/utils/env-loader.js` around lines 8 - 10, Convert the repository to ESM
so env-loader.js can be migrated: add "type": "module" to package.json, update
logger.js, api-key-store.js, and auth-json.js to export via ESM (export default
/ named exports) and adjust their internal require/module.exports usage, then
change env-loader.js to use import statements for logger, loadEnvEntries,
PROVIDER_ENV_MAP, LEGACY_KEY_NAMES, and readAuthJsonKeys; ensure exported symbol
names match (or add named exports) so env-loader.js can import them directly.
| error: `Error: ${providerInfo.key} not found.\n\n` + | ||
| 'In non-interactive shells (Claude Code, CI), ~/.zshrc is not sourced.\n' + | ||
| 'Fix with one of:\n' + | ||
| ' - Run `sidecar setup` to store keys in sidecar\'s config\n' + | ||
| ' - Move your export to ~/.zshenv (sourced by all zsh shells)\n' + | ||
| ' - Add key to ~/.local/share/opencode/auth.json' |
There was a problem hiding this comment.
Avoid hardcoding a single auth.json path in the remediation text.
The message currently implies only one location. In non-default setups this can misdirect users; make it explicitly “default path” or generic.
Suggested wording tweak
- ' - Add key to ~/.local/share/opencode/auth.json'
+ ' - Add key to your OpenCode auth.json (default: ~/.local/share/opencode/auth.json)'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| error: `Error: ${providerInfo.key} not found.\n\n` + | |
| 'In non-interactive shells (Claude Code, CI), ~/.zshrc is not sourced.\n' + | |
| 'Fix with one of:\n' + | |
| ' - Run `sidecar setup` to store keys in sidecar\'s config\n' + | |
| ' - Move your export to ~/.zshenv (sourced by all zsh shells)\n' + | |
| ' - Add key to ~/.local/share/opencode/auth.json' | |
| error: `Error: ${providerInfo.key} not found.\n\n` + | |
| 'In non-interactive shells (Claude Code, CI), ~/.zshrc is not sourced.\n' + | |
| 'Fix with one of:\n' + | |
| ' - Run `sidecar setup` to store keys in sidecar\'s config\n' + | |
| ' - Move your export to ~/.zshenv (sourced by all zsh shells)\n' + | |
| ' - Add key to your OpenCode auth.json (default: ~/.local/share/opencode/auth.json)' |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/utils/validators.js` around lines 252 - 257, The remediation text that
builds the error string for providerInfo.key currently hardcodes a single
auth.json location; update that message to avoid implying a single path by
referring to the "default auth.json path" or making it explicit that the shown
path is an example (e.g. "or add key to the default auth.json path (e.g.
~/.local/share/opencode/auth.json)"). Modify the error string construction
around the code that references providerInfo.key so the third bullet becomes
generic wording like "or add key to the default auth.json path (for example:
~/.local/share/opencode/auth.json)", preserving providerInfo.key interpolation
and the other bullets.
Address CodeRabbit review feedback: correct test file path in spec, add credential resolution order to troubleshooting entry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
docs/superpowers/specs/2026-03-14-shell-independent-keys-design.md (1)
19-23:⚠️ Potential issue | 🟡 MinorAdd language tags to fenced code blocks to satisfy MD040.
Both opening fences should include an info string (e.g.,
text).Proposed fix
-``` +```text 1. process.env (already set) <- highest, never overwritten 2. ~/.config/sidecar/.env <- user-configured via `sidecar setup` 3. ~/.local/share/opencode/auth.json <- OpenCode SDK fallback-``` +```text Error: GOOGLE_GENERATIVE_AI_API_KEY not found. In non-interactive shells (Claude Code, CI), ~/.zshrc is not sourced. Fix with one of: - Run `sidecar setup` to store keys in sidecar's config - Move your export to ~/.zshenv (sourced by all zsh shells) - Add key to ~/.local/share/opencode/auth.json</details> Also applies to: 50-58 <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/superpowers/specs/2026-03-14-shell-independent-keys-design.mdaround
lines 19 - 23, The Markdown fenced code blocks in the file lack info strings
(violating MD040); update both opening fences around the list and the error
message blocks (and the other block at lines ~50-58) to include an info string
such as text (e.g., changetotext) so each fenced code block has a
language tag and satisfies MD040.</details> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In@docs/superpowers/specs/2026-03-14-shell-independent-keys-design.md:
- Around line 19-23: The Markdown fenced code blocks in the file lack info
strings (violating MD040); update both opening fences around the list and the
error message blocks (and the other block at lines ~50-58) to include an info
string such as text (e.g., changetotext) so each fenced code block has
a language tag and satisfies MD040.</details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro **Run ID**: `ea6a05a3-023b-405b-9010-aea24624f8db` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 3dc379f72856229c0df722fa563466791a70fd22 and 901f40b35dc695c9ea7887573fa781c0a7ec4e23. </details> <details> <summary>📒 Files selected for processing (2)</summary> * `docs/superpowers/specs/2026-03-14-shell-independent-keys-design.md` * `skill/SKILL.md` </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
Summary
src/utils/env-loader.jsthat loads API keys from sidecar's.envand OpenCode'sauth.jsonintoprocess.envat CLI bootstrap, before validation runsvalidateApiKey()invalidators.jsto a pureprocess.envcheck with actionable error messagebin/sidecar.jswith a singleloadCredentials()call.worktrees/from Jest test discovery to prevent duplicate test runsPriority order:
process.env>~/.config/sidecar/.env>~/.local/share/opencode/auth.json(first wins, never overwrite)Closes #11
Test plan
tests/env-loader.test.jsnode bin/sidecar.js --version~/.zshrc(not~/.zshenv)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes / Validation
Documentation
Tests