[codex] Add Anthropic provider with API key and Claude CLI modes#229
[codex] Add Anthropic provider with API key and Claude CLI modes#229
Conversation
There was a problem hiding this comment.
Pull request overview
Adds Anthropic as a first-class HybridClaw provider, supporting both direct Anthropic Messages API access via API key and the official claude -p CLI transport, with end-to-end wiring through config, CLI auth/onboarding, routing, container/host runners, and doctor probes.
Changes:
- Implement Anthropic provider resolution (api-key + claude-cli methods) and propagate
providerMethodthrough routing/runtime credentials and worker signatures. - Add container-side Anthropic provider implementation (Messages API + streaming +
claude -pexecution in host sandbox mode). - Update CLI help/auth/onboarding and doctor checks/probes; expand unit tests to cover the new behavior.
Reviewed changes
Copilot reviewed 39 out of 40 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/providers.task-routing.test.ts | Updates task routing tests to expect resolved Anthropic policies/credentials and error handling. |
| tests/providers.factory.test.ts | Verifies factory resolves Anthropic runtime credentials from env/runtime secrets. |
| tests/container.model-router.test.ts | Adds test coverage for routing Anthropic direct Messages API calls in the container router. |
| tests/container.anthropic-claude-cli.test.ts | New tests for claude -p transport behavior and sandbox-mode constraints. |
| tests/cli.test.ts | Adds CLI tests for Anthropic auth login/status/logout flows (api-key + claude-cli). |
| src/types/models.ts | Introduces AnthropicMethod and adds providerMethod to task model policy typing. |
| src/types/container.ts | Extends container input shape to include providerMethod. |
| src/security/runtime-secrets.ts | Allows ANTHROPIC_API_KEY to be stored in runtime secrets. |
| src/providers/types.ts | Adds providerMethod to resolved runtime credentials. |
| src/providers/task-routing.ts | Adds Anthropic model prefix detection and propagates providerMethod into policies. |
| src/providers/provider-ids.ts | Promotes anthropic into runtime provider IDs and updates provider ID sets. |
| src/providers/model-catalog.ts | Adds Anthropic prefix support to the model catalog filter mapping. |
| src/providers/auxiliary.ts | Threads providerMethod through auxiliary model resolution and validation. |
| src/providers/anthropic.ts | Implements host-side Anthropic credential resolution for api-key and claude-cli modes. |
| src/providers/anthropic-utils.ts | Adds Anthropic constants and helpers (base URL/model normalization, headers, token detection). |
| src/onboarding.ts | Adds Anthropic onboarding flow and integrates Anthropic into auth-method selection and credential checks. |
| src/infra/worker-signature.ts | Includes providerMethod in worker signature normalization/hashing inputs. |
| src/infra/host-runner.ts | Forces HYBRIDCLAW_AGENT_SANDBOX_MODE=host for host processes and passes providerMethod. |
| src/infra/container-runner.ts | Sets HYBRIDCLAW_AGENT_SANDBOX_MODE=container for containers and passes providerMethod. |
| src/doctor/provider-probes.ts | Adds Anthropic probe (CLI version check for claude-cli; HTTP models probe for api-key). |
| src/doctor/checks/providers.ts | Adds Anthropic to provider diagnostics and probes. |
| src/doctor/checks/credentials.ts | Includes ANTHROPIC_API_KEY when evaluating env-only secret presence. |
| src/config/runtime-config.ts | Adds anthropic section to runtime config schema/defaults and bumps config version. |
| src/config/config.ts | Exposes Anthropic config/env exports and includes Anthropic models in configured model list. |
| src/cli/help.ts | Updates help text and adds Anthropic auth usage documentation. |
| src/cli/auth-command.ts | Implements auth login/status/logout anthropic and associated argument parsing. |
| src/cli.ts | Adds Anthropic-specific missing-env-var messaging. |
| src/auth/anthropic-auth.ts | New Anthropic auth module (API key + Claude CLI credential discovery). |
| container/src/types.ts | Extends container task model policy and input types to include Anthropic + providerMethod. |
| container/src/tools.ts | Threads providerMethod into model context setup. |
| container/src/providers/shared.ts | Updates call-arg normalization to include providerMethod in the raw argument layout. |
| container/src/providers/router.ts | Routes Anthropic provider calls to the new Anthropic provider implementation. |
| container/src/providers/provider-ids.ts | Adds anthropic to container runtime provider IDs. |
| container/src/providers/auxiliary.ts | Propagates providerMethod through auxiliary task routing in the container. |
| container/src/providers/anthropic.ts | New Anthropic provider implementation (Messages API + SSE streaming + claude -p). |
| container/src/index.ts | Includes providerMethod in task model comparisons and request processing plumbing. |
| container/src/browser-tools.ts | Extends browser model context to include providerMethod. |
| container/shared/provider-context.js | Allows skipping API key requirement for Anthropic when providerMethod=claude-cli. |
| container/shared/provider-context.d.ts | Adds providerMethod to provider-context validation params typing. |
| config.example.json | Documents new anthropic runtime config section defaults. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ...auth.headers, | ||
| }; | ||
| if (auth.method === 'api-key') { | ||
| headers['x-api-key'] = auth.apiKey; |
There was a problem hiding this comment.
probeAnthropic() always sets x-api-key when requireAnthropicApiKey() succeeds, even if the stored value is an OAuth-style token (e.g. sk-ant-oat...). For OAuth tokens Anthropic expects Authorization: Bearer <token> instead; using x-api-key will make the doctor probe fail even when the credential is valid.
Adjust the header construction to choose Authorization vs x-api-key based on the token format (or return an explicit authHeader from requireAnthropicApiKey()).
| headers['x-api-key'] = auth.apiKey; | |
| const isOAuthToken = auth.apiKey.startsWith('sk-ant-oat'); | |
| if (isOAuthToken) { | |
| headers.Authorization = `Bearer ${auth.apiKey}`; | |
| delete headers['x-api-key']; | |
| } else { | |
| headers['x-api-key'] = auth.apiKey; | |
| delete headers.Authorization; | |
| } |
| console.log(`Configured method: ${config.anthropic.method}`); | ||
| if (status.method) { | ||
| console.log(`Detected auth source: ${status.method}`); | ||
| } | ||
| if (status.source) { | ||
| console.log(`Source: ${status.source}`); | ||
| } |
There was a problem hiding this comment.
The line console.log(Detected auth source: ${status.method}) prints the method (api-key/claude-cli) under a label that says "source". This is misleading given you also print Source: ${status.source} right below.
Consider either changing this label to something like "Detected auth method" (keeping status.method), or change the value printed here to status.source and keep a separate method-specific label elsewhere.
| const anthropicConfiguredMethod = runtimeConfig.anthropic.method; | ||
| const anthropicReady = | ||
| anthropicConfiguredMethod === 'claude-cli' | ||
| ? anthropicStatus.method === 'claude-cli' |
There was a problem hiding this comment.
anthropicReady is determined only by comparing anthropicStatus.method to the configured method. For claude-cli, getAnthropicAuthStatus() sets method: 'claude-cli' as long as any Claude credential is present, even when authenticated is false (e.g. token expired).
This can cause onboarding to treat expired Claude CLI credentials as “ready” and skip prompting / erroring, only to fail later when requireAnthropicClaudeCliCredential() is called. Consider incorporating anthropicStatus.authenticated (and/or an expiresAt > Date.now() check) into the readiness calculation for claude-cli.
| ? anthropicStatus.method === 'claude-cli' | |
| ? anthropicStatus.method === 'claude-cli' && | |
| anthropicStatus.authenticated === true && | |
| (anthropicStatus.expiresAt == null || anthropicStatus.expiresAt > Date.now()) |
| refreshedRuntimeConfig.anthropic.method; | ||
| const refreshedAnthropicReady = | ||
| refreshedAnthropicConfiguredMethod === 'claude-cli' | ||
| ? refreshedAnthropicStatus.method === 'claude-cli' |
There was a problem hiding this comment.
refreshedAnthropicReady is determined only by comparing refreshedAnthropicStatus.method to the configured method. For claude-cli, getAnthropicAuthStatus() reports method: 'claude-cli' whenever a Claude credential is present, even if it’s expired (authenticated: false).
This can allow onboarding to proceed as if Anthropic is ready after refresh, but then fail later when requireAnthropicClaudeCliCredential() is invoked. Consider incorporating refreshedAnthropicStatus.authenticated (or an explicit expiry check) into the readiness check for claude-cli.
| ? refreshedAnthropicStatus.method === 'claude-cli' | |
| ? refreshedAnthropicStatus.method === 'claude-cli' && | |
| refreshedAnthropicStatus.authenticated |
| const originalSandboxMode = process.env.HYBRIDCLAW_AGENT_SANDBOX_MODE; | ||
| process.env.HYBRIDCLAW_AGENT_SANDBOX_MODE = 'host'; | ||
| const spawnMock = vi.fn(() => { |
There was a problem hiding this comment.
This test mutates process.env.HYBRIDCLAW_AGENT_SANDBOX_MODE but restores it only at the end of the test body. If an assertion throws before reaching the restore block, the env var can leak into subsequent tests.
Wrap the body in a try/finally (like the other tests in this file) to guarantee restoration even on failure.
879acd5 to
a6c8ecd
Compare
a6c8ecd to
e12ad81
Compare
What changed
This adds Anthropic as a first-class HybridClaw provider with two login methods:
--method api-keyfor direct Anthropic Messages API access--method claude-clifor the officialclaude -ptransportThe Claude CLI path was simplified to the common-case design:
claudebinaryclaude auth loginsessionThe PR also fixes a few real Claude CLI compatibility issues discovered during manual smoke testing:
--cwdflag--verboseadded for--output-format stream-jsonWhy
Direct reuse of Claude Code login material inside a third-party Anthropic API transport is no longer a good assumption. The simpler and more correct split is:
api-keyfor direct API accessclaude-clifor official Claude Code execution semanticsRestricting
claude-clito host sandbox mode keeps the implementation smaller and aligned with the main user path of an already-installed Claude CLI on the host.Impact
Users can now configure Anthropic with either:
hybridclaw auth login anthropic --method api-key --set-defaulthybridclaw auth login anthropic --method claude-cli --set-defaultFor
claude-cli, the intended runtime is:hybridclaw gateway start --foreground --sandbox=hostContainer sandbox mode remains supported for Anthropic API-key usage, but not for the Claude CLI transport.
Validation
npm run formatnpm run typechecknpm --prefix container run lintnpm run buildnpm run test:unit -- tests/container.anthropic-claude-cli.test.ts tests/providers.task-routing.test.ts tests/cli.test.tsRemaining risk
This was not fully live-tested end-to-end against every Claude CLI version. The remaining risk is CLI contract drift across installed Claude versions, although the current implementation was adjusted against a real local Claude install during smoke testing.