feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
use std::env::VarError;
|
|
|
|
|
use std::fmt::{Display, Formatter};
|
feat: merge 2nd round from all rcc/* sessions
- api: tool_use parsing, message_delta, request_id tracking, retry logic
- tools: extended tool suite (WebSearch, WebFetch, Agent, etc.)
- cli: live streamed conversations, session restore, compact commands
- runtime: config loading, system prompt builder, token usage, compaction
2026-03-31 17:43:25 +00:00
|
|
|
use std::time::Duration;
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum ApiError {
|
|
|
|
|
MissingApiKey,
|
2026-03-31 23:38:05 +00:00
|
|
|
ExpiredOAuthToken,
|
|
|
|
|
Auth(String),
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
InvalidApiKeyEnv(VarError),
|
|
|
|
|
Http(reqwest::Error),
|
|
|
|
|
Io(std::io::Error),
|
|
|
|
|
Json(serde_json::Error),
|
feat: merge 2nd round from all rcc/* sessions
- api: tool_use parsing, message_delta, request_id tracking, retry logic
- tools: extended tool suite (WebSearch, WebFetch, Agent, etc.)
- cli: live streamed conversations, session restore, compact commands
- runtime: config loading, system prompt builder, token usage, compaction
2026-03-31 17:43:25 +00:00
|
|
|
Api {
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
status: reqwest::StatusCode,
|
feat: merge 2nd round from all rcc/* sessions
- api: tool_use parsing, message_delta, request_id tracking, retry logic
- tools: extended tool suite (WebSearch, WebFetch, Agent, etc.)
- cli: live streamed conversations, session restore, compact commands
- runtime: config loading, system prompt builder, token usage, compaction
2026-03-31 17:43:25 +00:00
|
|
|
error_type: Option<String>,
|
|
|
|
|
message: Option<String>,
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
body: String,
|
feat: merge 2nd round from all rcc/* sessions
- api: tool_use parsing, message_delta, request_id tracking, retry logic
- tools: extended tool suite (WebSearch, WebFetch, Agent, etc.)
- cli: live streamed conversations, session restore, compact commands
- runtime: config loading, system prompt builder, token usage, compaction
2026-03-31 17:43:25 +00:00
|
|
|
retryable: bool,
|
|
|
|
|
},
|
|
|
|
|
RetriesExhausted {
|
|
|
|
|
attempts: u32,
|
|
|
|
|
last_error: Box<ApiError>,
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
},
|
|
|
|
|
InvalidSseFrame(&'static str),
|
feat: merge 2nd round from all rcc/* sessions
- api: tool_use parsing, message_delta, request_id tracking, retry logic
- tools: extended tool suite (WebSearch, WebFetch, Agent, etc.)
- cli: live streamed conversations, session restore, compact commands
- runtime: config loading, system prompt builder, token usage, compaction
2026-03-31 17:43:25 +00:00
|
|
|
BackoffOverflow {
|
|
|
|
|
attempt: u32,
|
|
|
|
|
base_delay: Duration,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ApiError {
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub fn is_retryable(&self) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Http(error) => error.is_connect() || error.is_timeout() || error.is_request(),
|
|
|
|
|
Self::Api { retryable, .. } => *retryable,
|
|
|
|
|
Self::RetriesExhausted { last_error, .. } => last_error.is_retryable(),
|
|
|
|
|
Self::MissingApiKey
|
2026-03-31 23:38:05 +00:00
|
|
|
| Self::ExpiredOAuthToken
|
|
|
|
|
| Self::Auth(_)
|
feat: merge 2nd round from all rcc/* sessions
- api: tool_use parsing, message_delta, request_id tracking, retry logic
- tools: extended tool suite (WebSearch, WebFetch, Agent, etc.)
- cli: live streamed conversations, session restore, compact commands
- runtime: config loading, system prompt builder, token usage, compaction
2026-03-31 17:43:25 +00:00
|
|
|
| Self::InvalidApiKeyEnv(_)
|
|
|
|
|
| Self::Io(_)
|
|
|
|
|
| Self::Json(_)
|
|
|
|
|
| Self::InvalidSseFrame(_)
|
|
|
|
|
| Self::BackoffOverflow { .. } => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for ApiError {
|
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Self::MissingApiKey => {
|
docs(providers): add honest provider/auth support matrix + improve MissingApiKey error copy
Problem:
- `rust/README.md` on `dev/rust` only shows `ANTHROPIC_API_KEY` under
Configuration and says nothing about other providers, so users
asking "does AWS Bedrock work? other API keys?" have no answer and
assume silent support.
- `ApiError::MissingApiKey` Display message says "Anthropic API" but
gives no signal to a user who exported `OPENAI_API_KEY` (or
`AWS_ACCESS_KEY_ID`) that their key is being ignored because this
branch has no code path for that provider.
- The multi-provider routing work (providers/anthropic.rs,
providers/openai_compat.rs, prefix routing for openai/, qwen/, etc.)
landed on `main` but has not yet merged into `dev/rust`, so the
support matrix actually differs between branches. Nothing in dev
docs communicates that.
Changes:
1. `rust/README.md`: new "Providers & Auth Support Matrix" section
between Configuration and Features, split into three sub-sections:
a. Supported on `dev/rust` (this branch) — just Anthropic. Explicit
call-out that OPENAI_API_KEY / XAI_API_KEY / DASHSCOPE_API_KEY
are ignored here because the `providers/` module does not exist
on this branch.
b. Additionally supported on `main` — xAI, OpenAI, DashScope, with
a note that model-name prefix routing (`openai/`, `gpt-`,
`qwen/`, `qwen-`) wins over env-var presence.
c. Not supported anywhere in this repo (yet) — AWS Bedrock, Google
Vertex AI, Azure OpenAI, Google Gemini, each with a one-line
"why it doesn't work today" pointing at the concrete code gap
(no SigV4 signer, no Google ADC path, api-version query params,
etc.) so users don't chase phantom config knobs. Proxy-based
escape hatch documented.
2. `rust/crates/api/src/error.rs`: `MissingApiKey` Display message now
keeps the grep-stable prefix ("ANTHROPIC_AUTH_TOKEN or
ANTHROPIC_API_KEY is not set") AND tells the user exactly which
other env vars are ignored on this branch plus which providers
aren't supported on any branch yet, with a pointer at the README
matrix section. Non-breaking change — the variant is still a unit
struct, no callers need to change.
3. New regression test
`missing_api_key_display_lists_supported_and_unsupported_providers_and_points_at_readme`
in `rust/crates/api/src/error.rs` asserts the grep-stable prefix is
preserved AND that OPENAI_API_KEY, XAI_API_KEY, DASHSCOPE_API_KEY,
Bedrock, Vertex, Azure, and rust/README.md all appear in the
rendered message, so future tweaks cannot silently drop the
user-facing signal without breaking CI.
Verification:
- `cargo build --release -p api` clean
- `cargo test --release -p api` 26 unit + 6 integration = 32 passing
- New regression test passes
- `cargo fmt -p api` clean
- `cargo clippy --release -p api` clean
Note: workspace-wide `cargo test` shows 11 pre-existing
`rusty-claude-cli` failures on clean `dev/rust` HEAD caused by tests
reading `~/.claude/plugins/installed/sample-hooks-bundled/` from the
host home directory instead of an isolated test fixture. These are
environment-leak test brittleness, not caused by this PR (verified by
stashing changes and re-running — failures reproduce on unmodified
HEAD). Filing as a separate ROADMAP pinpoint.
Does not close any open issue (issues are disabled on the repo);
addresses Clawhip dogfood nudge from 2026-04-08 about users asking
"other api keys? AWS Bedrock도 되냐" without a clear matrix.
Co-authored-by: gaebal-gajae <gaebal-gajae@layofflabs.com>
2026-04-08 15:19:48 +09:00
|
|
|
// Intentionally explicit about what this branch does and
|
|
|
|
|
// does not support so users who exported OPENAI_API_KEY,
|
|
|
|
|
// XAI_API_KEY, DASHSCOPE_API_KEY, AWS_*, or Google
|
|
|
|
|
// service-account credentials get an immediate "aha"
|
|
|
|
|
// instead of assuming a misconfiguration bug. See
|
|
|
|
|
// rust/README.md § "Providers & Auth Support Matrix"
|
|
|
|
|
// for the full matrix and the branch differences.
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
write!(
|
|
|
|
|
f,
|
docs(providers): add honest provider/auth support matrix + improve MissingApiKey error copy
Problem:
- `rust/README.md` on `dev/rust` only shows `ANTHROPIC_API_KEY` under
Configuration and says nothing about other providers, so users
asking "does AWS Bedrock work? other API keys?" have no answer and
assume silent support.
- `ApiError::MissingApiKey` Display message says "Anthropic API" but
gives no signal to a user who exported `OPENAI_API_KEY` (or
`AWS_ACCESS_KEY_ID`) that their key is being ignored because this
branch has no code path for that provider.
- The multi-provider routing work (providers/anthropic.rs,
providers/openai_compat.rs, prefix routing for openai/, qwen/, etc.)
landed on `main` but has not yet merged into `dev/rust`, so the
support matrix actually differs between branches. Nothing in dev
docs communicates that.
Changes:
1. `rust/README.md`: new "Providers & Auth Support Matrix" section
between Configuration and Features, split into three sub-sections:
a. Supported on `dev/rust` (this branch) — just Anthropic. Explicit
call-out that OPENAI_API_KEY / XAI_API_KEY / DASHSCOPE_API_KEY
are ignored here because the `providers/` module does not exist
on this branch.
b. Additionally supported on `main` — xAI, OpenAI, DashScope, with
a note that model-name prefix routing (`openai/`, `gpt-`,
`qwen/`, `qwen-`) wins over env-var presence.
c. Not supported anywhere in this repo (yet) — AWS Bedrock, Google
Vertex AI, Azure OpenAI, Google Gemini, each with a one-line
"why it doesn't work today" pointing at the concrete code gap
(no SigV4 signer, no Google ADC path, api-version query params,
etc.) so users don't chase phantom config knobs. Proxy-based
escape hatch documented.
2. `rust/crates/api/src/error.rs`: `MissingApiKey` Display message now
keeps the grep-stable prefix ("ANTHROPIC_AUTH_TOKEN or
ANTHROPIC_API_KEY is not set") AND tells the user exactly which
other env vars are ignored on this branch plus which providers
aren't supported on any branch yet, with a pointer at the README
matrix section. Non-breaking change — the variant is still a unit
struct, no callers need to change.
3. New regression test
`missing_api_key_display_lists_supported_and_unsupported_providers_and_points_at_readme`
in `rust/crates/api/src/error.rs` asserts the grep-stable prefix is
preserved AND that OPENAI_API_KEY, XAI_API_KEY, DASHSCOPE_API_KEY,
Bedrock, Vertex, Azure, and rust/README.md all appear in the
rendered message, so future tweaks cannot silently drop the
user-facing signal without breaking CI.
Verification:
- `cargo build --release -p api` clean
- `cargo test --release -p api` 26 unit + 6 integration = 32 passing
- New regression test passes
- `cargo fmt -p api` clean
- `cargo clippy --release -p api` clean
Note: workspace-wide `cargo test` shows 11 pre-existing
`rusty-claude-cli` failures on clean `dev/rust` HEAD caused by tests
reading `~/.claude/plugins/installed/sample-hooks-bundled/` from the
host home directory instead of an isolated test fixture. These are
environment-leak test brittleness, not caused by this PR (verified by
stashing changes and re-running — failures reproduce on unmodified
HEAD). Filing as a separate ROADMAP pinpoint.
Does not close any open issue (issues are disabled on the repo);
addresses Clawhip dogfood nudge from 2026-04-08 about users asking
"other api keys? AWS Bedrock도 되냐" without a clear matrix.
Co-authored-by: gaebal-gajae <gaebal-gajae@layofflabs.com>
2026-04-08 15:19:48 +09:00
|
|
|
"ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY is not set; export one before calling the Anthropic API. \
|
|
|
|
|
On this branch (`dev/rust`) only Anthropic is wired up \
|
|
|
|
|
— OPENAI_API_KEY, XAI_API_KEY, DASHSCOPE_API_KEY, and \
|
|
|
|
|
AWS/Google credentials are ignored. Multi-provider \
|
|
|
|
|
routing (OpenAI, xAI, DashScope) lives on `main`; AWS \
|
|
|
|
|
Bedrock, Google Vertex AI, and Azure OpenAI are not \
|
|
|
|
|
supported on any branch yet. See rust/README.md \
|
|
|
|
|
§ 'Providers & Auth Support Matrix' for details."
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
)
|
|
|
|
|
}
|
2026-03-31 23:38:05 +00:00
|
|
|
Self::ExpiredOAuthToken => {
|
|
|
|
|
write!(
|
|
|
|
|
f,
|
|
|
|
|
"saved OAuth token is expired and no refresh token is available"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
Self::Auth(message) => write!(f, "auth error: {message}"),
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
Self::InvalidApiKeyEnv(error) => {
|
2026-03-31 18:39:39 +00:00
|
|
|
write!(
|
|
|
|
|
f,
|
|
|
|
|
"failed to read ANTHROPIC_AUTH_TOKEN / ANTHROPIC_API_KEY: {error}"
|
|
|
|
|
)
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
}
|
|
|
|
|
Self::Http(error) => write!(f, "http error: {error}"),
|
|
|
|
|
Self::Io(error) => write!(f, "io error: {error}"),
|
|
|
|
|
Self::Json(error) => write!(f, "json error: {error}"),
|
feat: merge 2nd round from all rcc/* sessions
- api: tool_use parsing, message_delta, request_id tracking, retry logic
- tools: extended tool suite (WebSearch, WebFetch, Agent, etc.)
- cli: live streamed conversations, session restore, compact commands
- runtime: config loading, system prompt builder, token usage, compaction
2026-03-31 17:43:25 +00:00
|
|
|
Self::Api {
|
|
|
|
|
status,
|
|
|
|
|
error_type,
|
|
|
|
|
message,
|
|
|
|
|
body,
|
|
|
|
|
..
|
|
|
|
|
} => match (error_type, message) {
|
|
|
|
|
(Some(error_type), Some(message)) => {
|
|
|
|
|
write!(
|
|
|
|
|
f,
|
|
|
|
|
"anthropic api returned {status} ({error_type}): {message}"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
_ => write!(f, "anthropic api returned {status}: {body}"),
|
|
|
|
|
},
|
|
|
|
|
Self::RetriesExhausted {
|
|
|
|
|
attempts,
|
|
|
|
|
last_error,
|
|
|
|
|
} => write!(
|
|
|
|
|
f,
|
|
|
|
|
"anthropic api failed after {attempts} attempts: {last_error}"
|
|
|
|
|
),
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
Self::InvalidSseFrame(message) => write!(f, "invalid sse frame: {message}"),
|
feat: merge 2nd round from all rcc/* sessions
- api: tool_use parsing, message_delta, request_id tracking, retry logic
- tools: extended tool suite (WebSearch, WebFetch, Agent, etc.)
- cli: live streamed conversations, session restore, compact commands
- runtime: config loading, system prompt builder, token usage, compaction
2026-03-31 17:43:25 +00:00
|
|
|
Self::BackoffOverflow {
|
|
|
|
|
attempt,
|
|
|
|
|
base_delay,
|
|
|
|
|
} => write!(
|
|
|
|
|
f,
|
|
|
|
|
"retry backoff overflowed on attempt {attempt} with base delay {base_delay:?}"
|
|
|
|
|
),
|
feat: Rust port of Claude Code CLI
Crates:
- api: Anthropic Messages API client with SSE streaming
- tools: Claude-compatible tool implementations (Bash, Read, Write, Edit, Glob, Grep + extended suite)
- runtime: conversation loop, session persistence, permissions, system prompt builder
- rusty-claude-cli: terminal UI with markdown rendering, syntax highlighting, spinners
- commands: subcommand definitions
- compat-harness: upstream TS parity verification
All crates pass cargo fmt/clippy/test.
2026-03-31 17:43:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::error::Error for ApiError {}
|
|
|
|
|
|
|
|
|
|
impl From<reqwest::Error> for ApiError {
|
|
|
|
|
fn from(value: reqwest::Error) -> Self {
|
|
|
|
|
Self::Http(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<std::io::Error> for ApiError {
|
|
|
|
|
fn from(value: std::io::Error) -> Self {
|
|
|
|
|
Self::Io(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<serde_json::Error> for ApiError {
|
|
|
|
|
fn from(value: serde_json::Error) -> Self {
|
|
|
|
|
Self::Json(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<VarError> for ApiError {
|
|
|
|
|
fn from(value: VarError) -> Self {
|
|
|
|
|
Self::InvalidApiKeyEnv(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
docs(providers): add honest provider/auth support matrix + improve MissingApiKey error copy
Problem:
- `rust/README.md` on `dev/rust` only shows `ANTHROPIC_API_KEY` under
Configuration and says nothing about other providers, so users
asking "does AWS Bedrock work? other API keys?" have no answer and
assume silent support.
- `ApiError::MissingApiKey` Display message says "Anthropic API" but
gives no signal to a user who exported `OPENAI_API_KEY` (or
`AWS_ACCESS_KEY_ID`) that their key is being ignored because this
branch has no code path for that provider.
- The multi-provider routing work (providers/anthropic.rs,
providers/openai_compat.rs, prefix routing for openai/, qwen/, etc.)
landed on `main` but has not yet merged into `dev/rust`, so the
support matrix actually differs between branches. Nothing in dev
docs communicates that.
Changes:
1. `rust/README.md`: new "Providers & Auth Support Matrix" section
between Configuration and Features, split into three sub-sections:
a. Supported on `dev/rust` (this branch) — just Anthropic. Explicit
call-out that OPENAI_API_KEY / XAI_API_KEY / DASHSCOPE_API_KEY
are ignored here because the `providers/` module does not exist
on this branch.
b. Additionally supported on `main` — xAI, OpenAI, DashScope, with
a note that model-name prefix routing (`openai/`, `gpt-`,
`qwen/`, `qwen-`) wins over env-var presence.
c. Not supported anywhere in this repo (yet) — AWS Bedrock, Google
Vertex AI, Azure OpenAI, Google Gemini, each with a one-line
"why it doesn't work today" pointing at the concrete code gap
(no SigV4 signer, no Google ADC path, api-version query params,
etc.) so users don't chase phantom config knobs. Proxy-based
escape hatch documented.
2. `rust/crates/api/src/error.rs`: `MissingApiKey` Display message now
keeps the grep-stable prefix ("ANTHROPIC_AUTH_TOKEN or
ANTHROPIC_API_KEY is not set") AND tells the user exactly which
other env vars are ignored on this branch plus which providers
aren't supported on any branch yet, with a pointer at the README
matrix section. Non-breaking change — the variant is still a unit
struct, no callers need to change.
3. New regression test
`missing_api_key_display_lists_supported_and_unsupported_providers_and_points_at_readme`
in `rust/crates/api/src/error.rs` asserts the grep-stable prefix is
preserved AND that OPENAI_API_KEY, XAI_API_KEY, DASHSCOPE_API_KEY,
Bedrock, Vertex, Azure, and rust/README.md all appear in the
rendered message, so future tweaks cannot silently drop the
user-facing signal without breaking CI.
Verification:
- `cargo build --release -p api` clean
- `cargo test --release -p api` 26 unit + 6 integration = 32 passing
- New regression test passes
- `cargo fmt -p api` clean
- `cargo clippy --release -p api` clean
Note: workspace-wide `cargo test` shows 11 pre-existing
`rusty-claude-cli` failures on clean `dev/rust` HEAD caused by tests
reading `~/.claude/plugins/installed/sample-hooks-bundled/` from the
host home directory instead of an isolated test fixture. These are
environment-leak test brittleness, not caused by this PR (verified by
stashing changes and re-running — failures reproduce on unmodified
HEAD). Filing as a separate ROADMAP pinpoint.
Does not close any open issue (issues are disabled on the repo);
addresses Clawhip dogfood nudge from 2026-04-08 about users asking
"other api keys? AWS Bedrock도 되냐" without a clear matrix.
Co-authored-by: gaebal-gajae <gaebal-gajae@layofflabs.com>
2026-04-08 15:19:48 +09:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn missing_api_key_display_lists_supported_and_unsupported_providers_and_points_at_readme() {
|
|
|
|
|
// given
|
|
|
|
|
let error = ApiError::MissingApiKey;
|
|
|
|
|
|
|
|
|
|
// when
|
|
|
|
|
let rendered = format!("{error}");
|
|
|
|
|
|
|
|
|
|
// then — the message must keep the grep-stable core so CI
|
|
|
|
|
// parsers and docs that quote the exact substring continue to
|
|
|
|
|
// resolve, AND it must tell the user which env vars are
|
|
|
|
|
// ignored on this branch and where to find the full matrix.
|
|
|
|
|
assert!(
|
|
|
|
|
rendered.contains("ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY is not set"),
|
|
|
|
|
"grep-stable prefix must remain intact, got: {rendered}"
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
rendered.contains("OPENAI_API_KEY"),
|
|
|
|
|
"should explicitly call out that OPENAI_API_KEY is ignored on dev/rust, got: {rendered}"
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
rendered.contains("XAI_API_KEY"),
|
|
|
|
|
"should explicitly call out that XAI_API_KEY is ignored on dev/rust, got: {rendered}"
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
rendered.contains("DASHSCOPE_API_KEY"),
|
|
|
|
|
"should explicitly call out that DASHSCOPE_API_KEY is ignored on dev/rust, got: {rendered}"
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
rendered.contains("Bedrock") && rendered.contains("Vertex") && rendered.contains("Azure"),
|
|
|
|
|
"should tell users Bedrock/Vertex/Azure are not supported on any branch, got: {rendered}"
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
rendered.contains("rust/README.md"),
|
|
|
|
|
"should point users at the README matrix for the full story, got: {rendered}"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|