Skip to content

feat(config): support configurable fallback base URLs - #242

Open
vibe-cy wants to merge 1 commit into
moazbuilds:masterfrom
vibe-cy:feat/configurable-fallback-base-url
Open

feat(config): support configurable fallback base URLs#242
vibe-cy wants to merge 1 commit into
moazbuilds:masterfrom
vibe-cy:feat/configurable-fallback-base-url

Conversation

@vibe-cy

@vibe-cy vibe-cy commented Jun 30, 2026

Copy link
Copy Markdown

Problem
ClaudeClaw currently ties the fallback provider base URL to the special glm model name. This makes fallback routing provider-specific and forces users of Anthropic-compatible providers such as OpenRouter, LiteLLM, or local proxies to rely on a hardcoded GLM endpoint or misleading model names.

Solution
This PR adds explicit baseUrl support to the model configuration and routes the complete model config into Claude subprocess environment construction. Explicit base URLs now set ANTHROPIC_BASE_URL; the existing model: "glm" behavior remains intact as the legacy default when no base URL is configured.

Changes

  • Add baseUrl / fallback.baseUrl parsing to settings.
  • Refactor runner environment construction to accept full ModelConfig objects.
  • Preserve legacy GLM endpoint behavior when baseUrl is omitted.
  • Update config/start command docs for fallback base URL usage.
  • Add focused tests for settings parsing and environment generation.
  • Add a minimal demo script for inspecting fallback base URL env injection.
  • Bump plugin and marketplace metadata to 1.0.41.

Testing

  • bun test src/__tests__/config.test.ts src/__tests__/runner-env.test.ts src/__tests__/jobs.test.ts
    • 18 pass / 0 fail
  • bun run examples/fallback-base-url/demo.ts
    • confirms ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL are populated from fallback config
  • bun test
    • 127 pass / 3 fail
    • Remaining failures are pre-existing HOME-path expectations in tests/session-files.test.ts and match the baseline observed before this change.

Notes for Reviewer

  • Explicit baseUrl takes precedence over the GLM default endpoint.
  • model: "glm" without baseUrl still maps to https://api.z.ai/api/anthropic for backward compatibility.
  • sameModelConfig now includes baseUrl so a fallback can differ from the primary by provider endpoint even when model/API fields overlap.
  • I kept the change dependency-free and limited to the existing config/runner path.

Closes #215

@TerrysPOV TerrysPOV left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[COMPLEX — triage] 9 files, but mitigated: +194/-46 (well under 300 lines), substance is in two code files (config.ts, runner.ts); the rest are tests, docs, one example, and version files. Clean refactor of the existing config/runner env path — no new process model or plugin API.

Vision: ALIGNED — strictly opt-in. With baseUrl unset (every existing install), the default path is byte-identical, and the previously-implicit GLM→z.ai endpoint becomes explicit and configurable rather than hidden. Docs match the code; the buildChildEnv export the example relies on is real.

Overlapping PRs: the 1.0.41 version bump collides with #234, which merged earlier today and already put master at 1.0.41 — this branch (last updated 2026-06-30) needs a rebase onto current master + re-bump to 1.0.42, or both version guards fail. Also shares runner.ts regions with open #222 / #251 (merge-time friction only, no logical overlap).

Code review

Found 2 issues:

  1. Ambient ANTHROPIC_API_KEY is forwarded to a user-specified baseUrl. cleanSpawnEnv copies the whole parent env and strips only the three CLAUDE_CODE* keys — not ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN:

claudeclaw/src/runner.ts

Lines 107 to 119 in 829e7ec

function cleanSpawnEnv(): Record<string, string> {
const stripped = new Set([
"CLAUDECODE",
"CLAUDE_CODE_OAUTH_TOKEN",
"CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST",
]);
const out: Record<string, string> = {};
for (const [key, value] of Object.entries(process.env)) {
if (stripped.has(key)) continue;
if (typeof value === "string") out[key] = value;
}
return out;
}

buildChildEnv sets ANTHROPIC_AUTH_TOKEN only when a config api is present and points ANTHROPIC_BASE_URL at the custom URL, but never clears an inherited Anthropic key:

claudeclaw/src/runner.ts

Lines 334 to 354 in 829e7ec

export function buildChildEnv(baseEnv: Record<string, string>, config: ModelConfig): Record<string, string> {
const childEnv: Record<string, string> = { ...baseEnv };
const model = config.model.trim();
const api = config.api.trim();
const baseUrl = (config.baseUrl ?? "").trim();
const normalizedModel = model.toLowerCase();
if (api) childEnv.ANTHROPIC_AUTH_TOKEN = api;
if (baseUrl) {
childEnv.ANTHROPIC_BASE_URL = baseUrl;
} else if (normalizedModel === "glm") {
childEnv.ANTHROPIC_BASE_URL = "https://api.z.ai/api/anthropic";
}
if (normalizedModel === "glm") {
childEnv.API_TIMEOUT_MS = "3000000";
}
return childEnv;
}

So a user who has ANTHROPIC_API_KEY exported (a standard Claude Code auth method), points baseUrl/fallback.baseUrl at a third-party proxy, and leaves api blank ("just route through my proxy, no separate key") ships their real Anthropic key to that host. Pre-PR this couldn't happen for arbitrary hosts — only the hardcoded z.ai endpoint under model: glm. Suggest: when a custom baseUrl is set and no api is configured, drop ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN from the child env (the same posture #102 established for host-managed tokens).

  1. The 50-minute request timeout isn't extended for custom baseUrls. API_TIMEOUT_MS = "3000000" is still gated on model === "glm" only:

claudeclaw/src/runner.ts

Lines 349 to 351 in 829e7ec

if (normalizedModel === "glm") {
childEnv.API_TIMEOUT_MS = "3000000";
}

The whole point of the feature is slower Anthropic-compatible providers (OpenRouter, LiteLLM, local proxies), but those get the default client timeout where glm doesn't — so "my OpenRouter fallback times out but glm didn't." Extend the timeout when a custom baseUrl is set, not just for glm.

@vibe-cy
vibe-cy force-pushed the feat/configurable-fallback-base-url branch from 829e7ec to 90b4e41 Compare July 20, 2026 08:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: make fallback base URL configurable via settings.json

2 participants