Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,11 @@ NEXT_PUBLIC_AUTH_PROVIDERS=github,vercel

These API keys can be set globally (fallback for all users) or left unset to require users to provide their own:

- `ANTHROPIC_API_KEY`: Anthropic API key for Claude agent (users can override in their profile)
- `AI_GATEWAY_API_KEY`: AI Gateway API key for branch name generation and Codex (users can override)
- `AI_GATEWAY_API_KEY`: AI Gateway API key for Claude, Codex, branch name generation, title generation, and commit message generation (users can override)
- `ANTHROPIC_API_KEY`: Optional legacy/direct Anthropic key for agents that talk to Anthropic directly, such as some OpenCode configurations. It is not required for the Claude agent in this template.
- `CURSOR_API_KEY`: For Cursor agent support (users can override)
- `GEMINI_API_KEY`: For Google Gemini agent support (users can override)
- `OPENAI_API_KEY`: For Codex and OpenCode agents (users can override)
- `OPENAI_API_KEY`: For OpenCode direct OpenAI usage (users can override)

> **Note**: Users can provide their own API keys in their profile settings, which take precedence over global environment variables.

Expand Down Expand Up @@ -554,4 +554,3 @@ Confirm that:
- **Users must connect GitHub** (if they signed in with Vercel) to access repositories
- **API keys** can now be per-user - users can override global API keys in their profile
- **Breaking API changes**: If you have external integrations calling your API, they'll need to be updated to include authentication

5 changes: 5 additions & 0 deletions app/api/api-keys/check/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export async function GET(req: NextRequest) {
})
}

// Claude and Codex always use AI Gateway in this template, even for Claude-family models.
if (agent === 'claude' || agent === 'codex') {
provider = 'aigateway'
}

// Override provider based on model for multi-provider agents
if (model && (agent === 'cursor' || agent === 'opencode')) {
if (isAnthropicModel(model)) {
Expand Down
13 changes: 9 additions & 4 deletions components/api-keys-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ interface ApiKeysDialogProps {
type Provider = 'openai' | 'gemini' | 'cursor' | 'anthropic' | 'aigateway'

const PROVIDERS = [
{ id: 'aigateway' as Provider, name: 'AI Gateway', placeholder: 'gw_...' },
{ id: 'anthropic' as Provider, name: 'Anthropic', placeholder: 'sk-ant-...' },
{ id: 'aigateway' as Provider, name: 'AI Gateway', placeholder: 'vck_...' },
{ id: 'openai' as Provider, name: 'OpenAI', placeholder: 'sk-...' },
{ id: 'gemini' as Provider, name: 'Gemini', placeholder: 'AIza...' },
{ id: 'cursor' as Provider, name: 'Cursor', placeholder: 'cur_...' },
{ id: 'anthropic' as Provider, name: 'Anthropic (legacy)', placeholder: 'sk-ant-...', visibility: 'saved-only' },
]

export function ApiKeysDialog({ open, onOpenChange }: ApiKeysDialogProps) {
Expand Down Expand Up @@ -174,18 +174,23 @@ export function ApiKeysDialog({ open, onOpenChange }: ApiKeysDialogProps) {
setShowKeys((prev) => ({ ...prev, [provider]: !prev[provider] }))
}

const visibleProviders = PROVIDERS.filter(
(provider) => provider.visibility !== 'saved-only' || savedKeys.has(provider.id),
)

return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>API Keys</DialogTitle>
<DialogDescription>
Configure your own API keys. System defaults will be used if not provided.
Configure your own API keys. Claude and Codex use AI Gateway in this template. System defaults will be used
if not provided.
</DialogDescription>
</DialogHeader>

<div className="space-y-2">
{PROVIDERS.map((provider) => {
{visibleProviders.map((provider) => {
const hasSavedKey = savedKeys.has(provider.id)
const isCleared = clearedKeys.has(provider.id)
const showSaveButton = !hasSavedKey || isCleared
Expand Down
26 changes: 0 additions & 26 deletions components/task-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,32 +130,6 @@ const DEFAULT_MODELS = {
opencode: 'gpt-5',
} as const

// API key requirements for each agent
const AGENT_API_KEY_REQUIREMENTS: Record<string, Provider[]> = {
claude: ['anthropic'],
codex: ['aigateway'], // Uses AI Gateway for OpenAI proxy
copilot: [], // Uses user's GitHub account token automatically
cursor: ['cursor'],
gemini: ['gemini'],
opencode: [], // Will be determined dynamically based on selected model
}

type Provider = 'openai' | 'gemini' | 'cursor' | 'anthropic' | 'aigateway'

// Helper to determine which API key is needed for opencode based on model
const getOpenCodeRequiredKeys = (model: string): Provider[] => {
// Check if it's an Anthropic model (claude models)
if (model.includes('claude') || model.includes('sonnet') || model.includes('opus')) {
return ['anthropic']
}
// Check if it's an OpenAI/GPT model (uses AI Gateway)
if (model.includes('gpt')) {
return ['aigateway']
}
// Fallback to both if we can't determine
return ['aigateway', 'anthropic']
}

export function TaskForm({
onSubmit,
isSubmitting,
Expand Down
Loading