feat: unified Claude API key and profile system with z.AI, MiniMax, OpenRouter support#600
Conversation
Introduce APP_HOST variable to allow custom hostname configuration for the web server. Default to localhost if VITE_HOSTNAME is not set. Update relevant URLs and CORS origins to use APP_HOST, enhancing flexibility for local development and deployment. This change improves the application's adaptability to different environments.
Updated the process termination logic in ClaudeUsageService to handle Windows environments correctly. The code now checks the operating system and calls the appropriate kill method, ensuring consistent behavior across platforms.
Refactored the process termination logic in both ClaudeUsageService and TerminalService to use a centralized method for killing PTY processes. This ensures consistent handling of process termination across Windows and Unix-like systems, improving reliability and maintainability of the code.
…ests Added a mock for the Unix platform in the SIGTERM test case to ensure proper behavior during testing on non-Windows systems. This change enhances the reliability of the tests by simulating the expected environment for process termination.
Add support for managing multiple Claude-compatible API endpoints (z.AI GLM, AWS Bedrock, etc.) through provider profiles in settings. Features: - New ClaudeApiProfile type with base URL, API key, model mappings - Pre-configured z.AI GLM template with correct model names - Profile selector in Settings > Claude > API Profiles - Clean switching between profiles and direct Anthropic API - Immediate persistence to prevent data loss on restart Profile support added to all execution paths: - Agent service (chat) - Ideation service - Auto-mode service (feature agents, enhancements) - Simple query service (title generation, descriptions, etc.) - Backlog planning, commit messages, spec generation - GitHub issue validation, suggestions Environment variables set when profile is active: - ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN/API_KEY - ANTHROPIC_DEFAULT_HAIKU/SONNET/OPUS_MODEL - API_TIMEOUT_MS, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC
Resolved conflict in terminal-service.ts by accepting upstream Electron detection properties alongside local Windows termination fixes.
…upport The `./start-automaker.sh` script doesn't work when invoked from Windows CMD or PowerShell because: 1. The `./` prefix is Unix-style path notation 2. Windows shells don't execute .sh files directly This adds a Node.js launcher (`start-automaker.mjs`) that: - Detects the platform and finds bash (Git Bash, MSYS2, Cygwin, or WSL) - Converts Windows paths to Unix-style for bash compatibility - Passes all arguments through to the original bash script - Provides helpful error messages if bash isn't found The npm scripts now use `node start-automaker.mjs` which works on all platforms while preserving the full functionality of the bash TUI launcher.
- Remove duplicate killPtyProcess method in claude-usage-service.ts - Import and use spawnSync correctly instead of spawn.sync - Fix misleading comment about shell option and signal handling
- Add convertPathForBash() function that detects bash variant: - Cygwin: /cygdrive/c/path - WSL: /mnt/c/path - MSYS/Git Bash: /c/path - Update exit handler to properly handle signal termination (exit code 1 when killed by signal vs code from child) Addresses remaining CodeRabbit PR AutoMaker-Org#586 recommendations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Added handlers for theme submenu to manage mouse enter/leave events with a delay, preventing premature closure. - Implemented dynamic positioning for the submenu to avoid viewport overflow, ensuring better visibility. - Updated styles to accommodate new positioning logic and added scroll functionality for theme selection. These changes improve user experience by making the theme selection process more intuitive and visually accessible.
- Add detectBashVariant() that checks $OSTYPE for reliable WSL/MSYS/Cygwin detection instead of relying solely on executable path - Add input validation to convertPathForBash() to catch null/undefined args - Add validate_port() function in bash script to reject invalid port input (non-numeric, out of range) with clear error messages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Collect web and server port inputs first, then validate both before assigning to global variables. This prevents WEB_PORT from being modified when SERVER_PORT validation subsequently fails. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…roject-theme-ui-overlap fix: enhance project context menu with theme submenu improvements
…r-compatibility fix: add cross-platform Node.js launcher for Windows CMD/PowerShell support
…ode-max-glm-api-keys
- Add ApiKeySource type ('inline' | 'env' | 'credentials') to ClaudeApiProfile
- Allow profiles to source API keys from credentials.json or environment variables
- Add provider templates: OpenRouter, MiniMax, MiniMax (China)
- Auto-migrate existing users with Anthropic key to "Direct Anthropic" profile
- Update all API call sites to pass credentials for key resolution
- Add API key source selector to profile creation UI
- Increment settings version to 5 for migration support
This allows users to:
- Share a single API key across multiple profile configurations
- Use environment variables for CI/CD deployments
- Easily switch between providers without re-entering keys
Allow users to override the global Claude API profile on a per-project basis. This enables experimentation with alternative providers (z.AI GLM, MiniMax, etc.) on specific projects while keeping others on Direct Anthropic. Changes: - Add activeClaudeApiProfileId to ProjectSettings type - Update getActiveClaudeApiProfile() to check project settings first - Pass projectPath to all server call sites (14 files) - Add Claude section to Project Settings UI with profile selector - Add setProjectClaudeApiProfile store action with server persistence - Update project settings loader to restore per-project profile - Document per-project override in UNIFIED_API_KEY_PROFILES.md Note: This only affects Claude model calls. When using Codex, OpenCode, or Cursor models, the Claude API profile setting has no effect.
Keep DATA_DIR export from feature branch and adopt improved CORS_ORIGINS handling from main that ensures localhost and 127.0.0.1 are always included for local development.
Updated enhance-prompt and generate-title endpoints to accept and use projectPath, ensuring per-project Claude API profile selection works correctly for these operations. Changes: - enhance.ts: Accept projectPath in request body, pass to getActiveClaudeApiProfile - generate-title.ts: Accept projectPath in request body, pass to getActiveClaudeApiProfile - http-api-client.ts: Update both API calls to accept projectPath - electron.ts: Update interface for both endpoints - enhance-with-ai.tsx: Get currentProject.path from store and pass it - use-board-actions.ts: Pass projectPath to generateTitle call All 16 call sites now pass project context to getActiveClaudeApiProfile().
Added claudeApiProfiles and activeClaudeApiProfileId to the buildSettingsUpdateFromStore() function so that Claude API profiles are properly persisted to the server settings file. Previously, profiles were only stored in Zustand state but not synced to the server, causing them to be lost on hot reload.
Two issues were preventing Claude API profiles from persisting: 1. Server: claudeApiProfiles was not protected against accidental empty array overwrites - added to ignoreEmptyArrayOverwrite list 2. UI: hydrateStoreFromSettings() was not loading claudeApiProfiles and activeClaudeApiProfileId from server settings into Zustand store Now profiles will properly survive browser refreshes and hot reloads.
When setting activeClaudeApiProfileId to undefined (meaning "use global"), the JavaScript spread operator was ignoring it, keeping the old value. Now explicitly delete the key when undefined is passed, so the project properly falls back to the global setting.
JavaScript/JSON doesn't serialize undefined values, so when the UI tried
to send {activeClaudeApiProfileId: undefined} to clear the project override,
the key was simply not included in the request.
Solution: Use "__USE_GLOBAL__" marker string when selecting "Use Global Setting".
The server detects this marker and deletes the key from project settings,
properly falling back to global configuration.
Prevent users from creating profiles with duplicate names (case-insensitive). Shows an error message under the name field and disables the save button when a duplicate is detected.
Added an info box below the Anthropic API Key field explaining: - How to use this key with Claude API Profiles (credentials source) - How to configure alternative providers (inline key source) This helps users understand the relationship between the API Keys section and the Claude API Profiles feature.
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (6)
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughAdds a Claude API profile system: types, settings migration, profile resolution (project override → global → direct Anthropic), credential sourcing (inline/env/credentials), UI for managing profiles, and threads profile+credentials through provider, routes, and services. Changes
Sequence Diagram(s)sequenceDiagram
participant Route as Route Handler
participant Helper as getActiveClaudeApiProfile
participant Settings as SettingsService
participant Store as App Store
participant Provider as Claude Provider
Route->>Helper: getActiveClaudeApiProfile(settingsService, logPrefix, projectPath)
Helper->>Settings: getProjectSettings(projectPath)
Settings-->>Helper: projectSettings
alt project override exists
Helper->>Store: lookup profile by project.activeClaudeApiProfileId
Store-->>Helper: ClaudeApiProfile
else fallback to global
Helper->>Settings: getGlobalSettings()
Settings-->>Helper: globalSettings
Helper->>Store: lookup profile by global.activeClaudeApiProfileId
Store-->>Helper: ClaudeApiProfile
end
alt profile found
Helper->>Helper: resolve credentials (inline/env/credentials) using credentials arg
Helper-->>Route: { profile, credentials }
else no profile
Helper-->>Route: { profile: undefined, credentials }
end
Route->>Provider: executeQuery({ claudeApiProfile, credentials, ... })
Provider->>Provider: buildEnv(claudeApiProfile, credentials)
Note over Provider: construct env vars (API key, base URL, timeout, models)
Provider-->>Route: response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @stefandevo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the flexibility and management of Claude API access by introducing a unified system for API key sourcing and per-project profile overrides. It allows users to configure how API keys are resolved (inline, environment variable, or global credentials) and provides the ability to assign different Claude-compatible API endpoints to individual projects. This change aims to improve experimentation, cost optimization, and regional compliance by making it easier to switch between various providers like z.AI GLM, MiniMax, and OpenRouter, all while maintaining a consistent and user-friendly experience. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This is an impressive and extensive pull request that successfully implements a unified API key and profile system for Claude. The introduction of flexible API key sourcing, per-project profile overrides, and support for alternative providers like z.AI, MiniMax, and OpenRouter is a significant architectural improvement. The code is well-structured, and the addition of documentation and a settings migration path shows great attention to detail.
I've identified one critical issue in the API key resolution logic on the server and one high-severity issue in the UI that could cause a crash. Once these are addressed, this will be an excellent addition to the codebase.
Update buildEnv() to pass through ANTHROPIC_BASE_URL from process.env when no API profile is configured. This maintains backward compatibility for users who set custom endpoints via environment variables without creating a profile. Also fix prettier formatting in use-project-creation.ts from merge.
1. Critical: Use credentials file API key when no profile is active
- buildEnv() now prioritizes credentials.apiKeys.anthropic over
process.env.ANTHROPIC_API_KEY when no profile is configured
- This ensures API keys saved in the UI settings are used correctly
2. High: Handle undefined API key in maskApiKey function
- maskApiKey() now accepts optional string parameter
- Returns masked placeholder for undefined/short keys
- Prevents runtime error for profiles with non-inline key sources
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/ui/src/components/views/board-view/hooks/use-board-actions.ts (1)
210-252: AddprojectPathto theuseCallbackdependency array.Line 215 uses
projectPathin thegenerateTitlecall, but it's missing from the dependency array. If the user switches projects, the callback will continue using the staleprojectPathvalue.🔧 Suggested fix
[ addFeature, persistFeatureCreate, persistFeatureUpdate, updateFeature, saveCategory, currentProject, + projectPath, onWorktreeCreated, onWorktreeAutoSelect, getPrimaryWorktreeBranch, features, ]
🤖 Fix all issues with AI agents
In `@apps/server/src/routes/context/routes/describe-file.ts`:
- Around line 22-26: The per-project lookup is using the file's directory as cwd
when calling getAutoLoadClaudeMdSetting and getActiveClaudeApiProfile, causing
settings in the project root to be missed; change the code that computes cwd for
those calls to resolve the project root instead of the file's parent directory
by traversing upward from the file path and stopping on project markers (e.g.,
.git, .automaker/settings.json or similar), then pass that projectRoot into
getAutoLoadClaudeMdSetting(...) and getActiveClaudeApiProfile(...) in place of
the current cwd usage.
In `@apps/server/src/services/auto-mode-service.ts`:
- Around line 3269-3270: Nested executeQuery calls are not forwarding
credentials when apiKeySource === 'credentials', so plan revisions, per-task
executions and continuations fail; update each nested call that currently passes
claudeApiProfile (e.g., the calls around the claudeApiProfile parameter) to also
forward the active credentials from that profile (e.g., include
claudeApiProfile.credentials or a credentials field) when
claudeApiProfile.apiKeySource === 'credentials'; apply this fix to the
occurrences at the executeQuery invocations near the existing claudeApiProfile
usage and the other two locations mentioned (around lines 3415-3416 and
3510-3511) so all nested plan/task/continuation executeQuery calls receive
credentials.
In
`@apps/ui/src/components/layout/project-switcher/components/project-context-menu.tsx`:
- Around line 456-460: Clamp the computed submenu maxHeight so it never goes
negative by replacing the direct subtraction with a non-negative expression
(e.g., use Math.max(0, submenuPosition.maxHeight -
THEME_SUBMENU_CONSTANTS.SUBMENU_HEADER_HEIGHT)) and use that clamped value in
the inline style for the div; update the logic around submenuPosition and
THEME_SUBMENU_CONSTANTS.SUBMENU_HEADER_HEIGHT in project-context-menu.tsx so the
style always receives a safe, non-negative maxHeight.
In
`@apps/ui/src/components/views/settings-view/providers/claude-settings-tab/api-profiles-section.tsx`:
- Around line 50-54: maskApiKey currently assumes a string and will throw if
profile.apiKey is undefined for non-inline profiles; update maskApiKey (and
calls in the profile card rendering) to accept undefined/null and return a safe
label such as 'Environment' or 'Credentials' (or a masked placeholder like
'••••••••') when key is missing, and ensure the UI shows the apiKeySource value
as the visible source label when apiKey is not provided; reference maskApiKey
and where profile.apiKey and profile.apiKeySource are read in the profile card
rendering to implement this guard and display the source.
In `@apps/ui/src/hooks/use-settings-migration.ts`:
- Around line 674-675: parseLocalStorageSettings and mergeSettings currently
omit the new claudeApiProfiles and activeClaudeApiProfileId fields so
localStorage-stored Claude profiles get dropped during migration/fallback;
update both functions to read and include settings.claudeApiProfiles (defaulting
to [] if undefined) and settings.activeClaudeApiProfileId (defaulting to null if
undefined) when constructing the returned/merged settings object so local
profiles survive server-unreachable or first-time migration paths.
In `@apps/ui/src/store/app-store.ts`:
- Around line 2540-2550: When deleting a Claude profile in
deleteClaudeApiProfile, also scan and clear any per-project overrides that
reference the deleted id: for each project with activeClaudeApiProfileId === id
set it to '__USE_GLOBAL__' and persist via
httpClient.settings.updateProject(projectId, { activeClaudeApiProfileId:
'__USE_GLOBAL__' }); after updating the in-memory claudeApiProfiles and
activeClaudeApiProfileId, await all updateProject calls and then call
syncSettingsToServer(); also adjust imports to use the `@automaker/`* namespace
(e.g., import httpClient from '@automaker/http' or the appropriate `@automaker`
settings client) instead of '@/...'.
🧹 Nitpick comments (1)
apps/server/src/routes/enhance-prompt/routes/enhance.ts (1)
145-155: Consider usingprojectPathascwdwhen available.Currently,
cwdis always set toprocess.cwd()even whenprojectPathis provided. While this may be intentional since prompt enhancement doesn't need filesystem access, usingprojectPathascwd(when provided) would be more consistent with the profile resolution context.💡 Optional: Use projectPath as cwd when available
const result = await simpleQuery({ prompt: `${systemPrompt}\n\n${userPrompt}`, model: resolvedModel, - cwd: process.cwd(), // Enhancement doesn't need a specific working directory + cwd: projectPath || process.cwd(), // Use project path for consistency with profile resolution maxTurns: 1, allowedTools: [], thinkingLevel,
1. auto-mode-service.ts: Forward credentials in nested executeQuery calls
- Plan revisions, per-task executions, and continuations now receive
credentials for profiles using 'credentials' apiKeySource
2. app-store.ts: Clear per-project overrides when deleting profile
- Projects referencing deleted profile are reset to "use global"
- Persists changes via httpClient.settings.updateProject()
3. use-board-actions.ts: Add projectPath to useCallback dependency array
- Prevents stale closure when switching projects during title generation
4. project-context-menu.tsx: Clamp submenu maxHeight to avoid negative values
- Prevents collapsed scroll area on very small viewports
5. use-settings-migration.ts: Include Claude API profiles in localStorage migration
- Adds claudeApiProfiles and activeClaudeApiProfileId to parseLocalStorageSettings()
- Adds merge logic in mergeSettings() to preserve profiles during migration
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@apps/ui/src/components/views/settings-view/providers/claude-settings-tab/api-profiles-section.tsx`:
- Around line 160-179: The timeout parsing in handleSave builds
ClaudeApiProfile.timeoutMs using parseInt on formData.timeoutMs which truncates
decimals and misparses scientific notation; replace that logic to convert via
Number(formData.timeoutMs) and validate with Number.isFinite (or isFinite) so
decimal (e.g., "123.45") and scientific ("1e3") strings produce correct numeric
values, and set timeoutMs to the numeric value only when the conversion yields a
finite number (otherwise undefined).
🧹 Nitpick comments (2)
apps/server/src/providers/claude-provider.ts (2)
26-52: Consider removing system vars fromALLOWED_ENV_VARSto reduce redundancy.System variables (PATH, HOME, etc.) are listed in both
ALLOWED_ENV_VARS(lines 42-48) andSYSTEM_ENV_VARS(line 52). SinceSYSTEM_ENV_VARSis now used exclusively for adding system vars (line 153), the entries inALLOWED_ENV_VARSare unused and potentially misleading.♻️ Suggested cleanup
const ALLOWED_ENV_VARS = [ // Authentication 'ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', // Endpoint configuration 'ANTHROPIC_BASE_URL', 'API_TIMEOUT_MS', // Model mappings 'ANTHROPIC_DEFAULT_HAIKU_MODEL', 'ANTHROPIC_DEFAULT_SONNET_MODEL', 'ANTHROPIC_DEFAULT_OPUS_MODEL', // Traffic control 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC', - // System vars (always from process.env) - 'PATH', - 'HOME', - 'SHELL', - 'TERM', - 'USER', - 'LANG', - 'LC_ALL', ];
288-300:detectInstallation()only checks environment variables, not credentials-based API keys.The method checks
process.env.ANTHROPIC_API_KEYto determinehasApiKeyandauthenticatedstatus. With credentials-based keys supported in the UI settings (viabuildEnv()at lines 135-136), this may reporthasApiKey: falseeven when a valid key is configured through the credentials file.This is an architectural limitation:
detectInstallation()is called fromProviderFactory.checkAllProviders()without credentials context, since provider instances are created fresh from a factory and credentials are only available duringexecuteQuery(). Fixing this would require passing credentials through the factory chain or storing them in the provider instance—changes that may warrant separate consideration.
…ic notation parseInt truncates decimals and misparses scientific notation (e.g., "1e3"). Using Number() with Number.isFinite() validation correctly handles: - Decimal values: "123.45" -> 123.45 - Scientific notation: "1e3" -> 1000 - Invalid values: "abc" -> undefined
Summary
This PR implements a unified Claude API key and profile system that allows flexible configuration of how API keys are resolved, plus per-project profile overrides. Adds support for alternative Claude-compatible providers like z.AI GLM, MiniMax, and OpenRouter.
Key Features
Flexible API Key Sourcing: Profiles can now source their API key from:
inline- Key stored directly in the profile (for alternative providers)credentials- Uses the Anthropic key from Settings → API Keysenv- UsesANTHROPIC_API_KEYenvironment variablePer-Project Profile Overrides: Each project can override the global Claude API profile, enabling:
New Provider Templates:
UX Improvements:
Documentation
See docs/UNIFIED_API_KEY_PROFILES.md for complete implementation details including:
Changes
Types (
libs/types/src/settings.ts)ApiKeySourcetype ('inline' | 'env' | 'credentials')apiKeySourcefield toClaudeApiProfileactiveClaudeApiProfileIdtoProjectSettingsServer
getActiveClaudeApiProfile()to check project settings firstprojectPathparameter to all 16 Claude API call sitesclaudeApiProfilesfrom accidental empty array overwritesUI
ProjectClaudeSectioncomponent for per-project profile selectionclaudeApiProfilesandactiveClaudeApiProfileIdTesting
Screenshots
Summary by CodeRabbit
New Features
Chores
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.