Skip to main content
← Back to list
01Issue
FeatureOpenExtensionsPublic
AssigneesNone

Relationships

#2093 agent-runner: allow using the Claude CLI's stored login instead of requiring an API key

Opened by hmcrum · 9/10/2026

The gap

run and review cannot use the Claude Code CLI's own stored login. Auth resolution ends in an unconditional throw, so an API key is mandatory even when the machine already has an authenticated CLI:

// models/_lib/auth.ts — resolveApiKey()
// globalArgs.apiKey -> Deno.env.get(envVar) -> throw

For interactive or human-gated workflows this is the difference between "works with what the operator already has" and "provision and store a secret first".

Why it looks close to free

models/_lib/runner.ts already forwards HOME in FORWARDED_ENV_VARS, so the spawned binary can see the CLI's credential store. The CLI authenticates happily in exactly the stripped environment buildSubprocessEnv constructs, with no key present:

env -i PATH=... HOME=... TMPDIR=... USER=... LANG=... SHELL=... TERM=dumb \
  claude -p 'Reply with exactly: OK' --allowedTools ''
# -> OK, exit 0

The only thing preventing it is that resolveApiKey runs at runner.ts:32, before ensureBinary and before the spawn.

Why it cannot be configured around

Setting apiKeyEnvVar to any populated variable does not help, because the resolved value is then injected as ANTHROPIC_API_KEY, and a set key takes precedence over the stored login:

⚠ claude.ai connectors are disabled because ANTHROPIC_API_KEY or another
  auth source is set and takes precedence over your claude.ai login
Failed to authenticate. API Error: 401 API key is invalid.

An empty value is falsy and re-throws. So the key must be genuinely absent from the subprocess environment, which means the spawn has to be owned by whoever wants CLI auth — there is no configuration that reaches it.

Suggested change

Let auth be selectable rather than assumed, e.g. a global argument:

auth: "apiKey" | "cli"    # default "apiKey", preserving current behaviour

Under cli, skip resolveApiKey and omit the key variable from buildSubprocessEnv entirely (rather than setting it empty). A softer variant: when no apiKey/apiKeyEnvVar is configured and no env var is present, fall through to a keyless spawn instead of throwing — though an explicit switch is easier to reason about, and keeps a missing-key typo from silently changing how a CI run bills.

Codex would need its own answer; this request is only about the claude provider.

Current workaround

A local export const extension on @swamp/agent-runner adding a run_local method that re-implements the spawn: same buildArgs translation, same env allowlist, minus the key. Roughly 150 lines, and it duplicates provider logic that will drift. Happy to send a PR against the real thing instead if the auth switch above is the shape you would accept.

Upstream repository: https://github.com/swamp-club/swamp-extensions

Environment

  • Extension: @swamp/agent-runner@2026.08.23.1
  • swamp: 20260820.072114.0-sha.05d20b6a
  • OS: darwin (aarch64)
  • Deno: 2.8.3
  • Shell: /bin/zsh
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

9/10/2026, 3:03:11 AM

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.