← Back to PRs

#21216: feat(models): add apiKeyHelper for dynamic API key resolution

by chrisvanbuskirk open 2026-02-19 19:55 View on GitHub →
docs gateway commands agents size: M
## Summary - Adds `apiKeyHelper` field to model provider config, allowing a shell command to resolve API keys at runtime (secret managers, gateway proxies, dynamic rotation) - Uses cross-platform shell via `buildNodeShellCommand` (`/bin/sh -lc` on Unix, `cmd.exe` on Windows) - Async `execFile` with 10s timeout — non-blocking for gateway request flow - Auth label rendering uses config-only check (no command execution in `/status`) - Surfaces `apiKeyHelper` providers in `models status --json` and probe targets - Marks `apiKeyHelper` as sensitive in Zod schema (consistent with `apiKey`) ## Review issue fixes 1. **Cross-platform shell** — replaced hardcoded `/bin/sh` with `buildNodeShellCommand()` from `src/infra/node-shell.ts` 2. **Sensitive marking** — `apiKeyHelper` now uses `.register(sensitive)` in Zod schema 3. **No side effects in label rendering** — auth label checks config presence only, never runs the command 4. **Async execution** — converted from blocking `execFileSync` to async `execFile` callback pattern (Codex P1) 5. **Probe target support** — `apiKeyHelper` providers are now included in auth probe targets (Codex P2) ## Testing ### Automated - 761 test files, 6121 tests all passing - New test coverage: `model-auth.test.ts` (7 new tests), `list.probe.test.ts` (4 new tests) - Tests cover: key resolution, whitespace trimming, timeout handling, empty output, Tailscale Aperture `echo '-'` pattern, probe target selection, env priority over helper ### Manual (end-to-end) - **Tailscale Aperture**: Configured `apiKeyHelper: "echo '-'"` proxying to Anthropic — verified dummy key flows through gateway, model responds correctly - **1Password CLI**: Configured `apiKeyHelper: "op read 'op://Personal/ANTHROPIC_API_KEY/license key'"` — verified vault key retrieval, model responds correctly - **`models status --json`**: Verified `apiKeyHelper` providers appear in status output with correct `effective.kind` - **Sensitive data audit**: Confirmed no secrets, tokens, or vault paths leak in diff ## Test plan - [ ] `pnpm build` passes - [ ] `pnpm format:check` passes - [ ] `pnpm lint` — 0 warnings, 0 errors - [ ] `pnpm test` — 6121/6121 tests pass - [ ] Verify cross-platform shell on Windows (uses `cmd.exe /d /s /c`) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds `apiKeyHelper` field to model provider config for dynamic API key resolution via shell commands at runtime. Key changes: - **Core implementation**: New `resolveApiKeyHelper()` in `src/agents/model-auth.ts:73-92` runs user-configured shell commands with 10s timeout using cross-platform `buildNodeShellCommand()` (Unix: `/bin/sh -lc`, Windows: `cmd.exe /d /s /c`) - **Auth priority**: Profiles → env vars → `apiKeyHelper` → inline `apiKey` (lines 232-248) - **Zod schema**: Marks `apiKeyHelper` as sensitive in `src/config/zod-schema.core.ts:62` - **Status/probe support**: Providers with `apiKeyHelper` correctly surface in `models status --json` and probe targets - **No side effects in labels**: Auth label rendering checks config presence only (line 77), never executes command - **Comprehensive tests**: 7 new tests in `model-auth.test.ts` (key resolution, whitespace, timeout, empty output, Tailscale `-` pattern) + 4 probe tests Documentation updated for English and zh-CN with examples for Tailscale Aperture and 1Password CLI. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk - The implementation is production-ready: uses async non-blocking `execFile` with timeout protection, properly handles cross-platform shells, maintains correct auth priority order, includes comprehensive test coverage (11 new tests), and follows the project's conventions. The shell command execution is intentional for user-configured commands from their own config file (not untrusted input), with proper error handling and fallback to inline `apiKey` on failure. - No files require special attention <sub>Last reviewed commit: b457fa7</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs