Summary
Adds OpenRouter alongside the existing Vercel AI Gateway as a runtime-switchable LLM + embedding provider. Vercel AI Gateway stays the default so existing deployments are unaffected; opting into OpenRouter is a single env var (LLM_PROVIDER=openrouter) plus an API key.
The CLI deploy flow mints the API key for users via OpenRouter's OAuth-PKCE flow so there's no copy-paste, and exposes both a re-run rotate prompt and a focused set-openrouter-key subcommand so users can replace the key later without touching the dashboard.
Motivation
- Self-hosters outside Vercel don't get OIDC token auth for AI Gateway and have to manage
AI_GATEWAY_API_KEY plus a Vercel account anyway.
- OpenRouter gives users a single key that bills their own account and unifies usage across 300+ models.
- Both providers proxy the same
openai/text-embedding-3-large model at 1024 dims, so existing pgvector memories remain valid after switching — no re-embedding required.
What changed
1. Core provider abstraction (commit c33a9ab)
src/server/clients/ai/ (new):
provider.ts exposes getLanguageModel(canonicalModelId) which returns either an anthropic/<id> string (Gateway) or a LanguageModelV3 instance from @openrouter/ai-sdk-provider (OpenRouter).
embedding.ts exposes getEmbeddingModel() with the same dual-mode return.
model-mapping.ts maps stored canonical ids (claude-sonnet-4-5-20250929, etc.) to the slug each provider expects.
src/env.ts: adds LLM_PROVIDER (enum, default vercel-ai-gateway), OPENROUTER_API_KEY, AI_GATEWAY_API_KEY, plus a cross-field check that refuses to boot with LLM_PROVIDER=openrouter and no key.
Call-site refactors (all five inlined model-string constructions now go through the factory):
src/server/api/routers/trustclaw/agent/setup.ts (ToolLoopAgent)
src/server/api/routers/trustclaw/agent/compaction/memory-flush.ts (generateText)
src/server/api/routers/trustclaw/agent/compaction/run-compaction.ts (×2: summarize + stagedSummarize merge)
src/server/api/routers/trustclaw/agent/tools/memory-save.ts (embed)
src/server/api/routers/trustclaw/agent/tools/memory-search.ts (×2: tool + background context lookup)
Deploy-time UX so a non-engineer can pick OpenRouter without editing files:
- README Deploy-to-Vercel button:
env=... now includes LLM_PROVIDER and OPENROUTER_API_KEY with explainer text; leaving them blank keeps the Gateway default.
npx @composio/trustclaw deploy CLI: select prompt for the provider, writes both LLM_PROVIDER and OPENROUTER_API_KEY to the Vercel project env.
.env.example documents the new vars; README has a new "Choosing an LLM provider" section.
2. Browser-based OpenRouter login via PKCE (commit d10d21d)
Instead of asking the user to paste an sk-or-... key, the CLI now mints one for them automatically via OpenRouter's OAuth-PKCE flow (same pattern as the openrouter-web mission-control oauth-test reference):
- CLI generates a base64url
code_verifier + S256 code_challenge and a random CSRF state
- Spins up a loopback HTTP server on a random ephemeral port
- Opens
https://openrouter.ai/auth?callback_url=http://localhost:<port>/callback&code_challenge=...&state=... in the user's browser
- User logs in and approves in OpenRouter's UI
- CLI receives the redirect, validates
state, and POSTs {code, code_verifier, code_challenge_method: 'S256'} to https://openrouter.ai/api/v1/auth/keys
- CLI writes the returned
sk-or-... to the Vercel project's OPENROUTER_API_KEY
Manual paste is still offered as a fallback for headless / SSH sessions or when the PKCE flow errors (no browser, blocked network, timeout). New file cli/src/openrouter-auth.ts (~250 lines) using only Node built-ins (crypto, http) and the existing open package — no new runtime deps.
3. Override the key after the first deploy (commit 344de10)
Two paths to rotate OPENROUTER_API_KEY post-deploy:
trustclaw set-openrouter-key — focused subcommand that asks for a new key (browser PKCE or manual paste), writes it to the Vercel project, and skips everything else (no store provisioning, no Composio prompt). Best for quick rotations. Also doubles as the "switch from Gateway to OpenRouter after the fact" command since it always writes LLM_PROVIDER=openrouter alongside the key.
trustclaw deploy re-run — when the project already has OPENROUTER_API_KEY, the prompt now offers keep / rotate via browser / rotate by paste (defaults to "keep" so accidental re-deploys don't churn credentials). Previously this skipped silently.
- Implementation: extracted a small
upsertEnvVars() helper in cli/src/env-vars.ts so both setEnvVars (deploy) and the new subcommand share the same Vercel API loop. resolveOpenRouterKey is exported from cli/src/inputs.ts for reuse. The new subcommand is wired up in cli/src/index.ts.
- README has a new "Rotating or overriding the OpenRouter API key later" section documenting all three paths (subcommand, redeploy, Vercel dashboard).
Backward compatibility
- Default behaviour is unchanged. With no env set, every code path resolves to the same
anthropic/<id> string the previous code constructed inline.
- The CLI only writes
LLM_PROVIDER to Vercel when the user explicitly picks OpenRouter, so existing deploys redeploying through the new CLI keep the Gateway path.
- DB schema is untouched. The
composioClawInstance.anthropicModel column still stores the same canonical ids; only the resolution layer changes.
Test plan
Files touched
Provider abstraction (commit c33a9ab):
.env.example
README.md
cli/src/deploy.ts
cli/src/env-vars.ts
cli/src/inputs.ts
package.json (+ pnpm-lock.yaml: @openrouter/ai-sdk-provider@^2.9.0)
src/env.ts
src/server/api/routers/trustclaw/agent/compaction/memory-flush.ts
src/server/api/routers/trustclaw/agent/compaction/run-compaction.ts
src/server/api/routers/trustclaw/agent/setup.ts
src/server/api/routers/trustclaw/agent/tools/memory-save.ts
src/server/api/routers/trustclaw/agent/tools/memory-search.ts
src/server/clients/ai/embedding.ts (new)
src/server/clients/ai/index.ts (new)
src/server/clients/ai/model-mapping.ts (new)
src/server/clients/ai/provider.ts (new)
PKCE login (commit d10d21d):
README.md
cli/src/inputs.ts
cli/src/openrouter-auth.ts (new)
Key rotation (commit 344de10):
README.md
cli/src/env-vars.ts
cli/src/index.ts
cli/src/inputs.ts
cli/src/set-openrouter-key.ts (new)