mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
feat(settings): add OpenAI/Google API key support and unified ModelId type
- Add OpenAI API key storage to store-api-key handler - Include Google/OpenAI key status in credentials API responses - Add unified ModelId type for Claude, Codex, Cursor, OpenCode, and dynamic providers - Update PhaseModelEntry to support all provider model types
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
* Each provider shows: `{ configured: boolean, masked: string }`
|
* Each provider shows: `{ configured: boolean, masked: string }`
|
||||||
* Masked shows first 4 and last 4 characters for verification.
|
* Masked shows first 4 and last 4 characters for verification.
|
||||||
*
|
*
|
||||||
* Response: `{ "success": true, "credentials": { anthropic } }`
|
* Response: `{ "success": true, "credentials": { anthropic, google, openai } }`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* PUT /api/settings/credentials - Update API credentials
|
* PUT /api/settings/credentials - Update API credentials
|
||||||
*
|
*
|
||||||
* Updates API keys for Anthropic. Partial updates supported.
|
* Updates API keys for supported providers. Partial updates supported.
|
||||||
* Returns masked credentials for verification without exposing full keys.
|
* Returns masked credentials for verification without exposing full keys.
|
||||||
*
|
*
|
||||||
* Request body: `Partial<Credentials>` (usually just apiKeys)
|
* Request body: `Partial<Credentials>` (usually just apiKeys)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export function createApiKeysHandler() {
|
|||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
hasAnthropicKey: !!getApiKey('anthropic') || !!process.env.ANTHROPIC_API_KEY,
|
hasAnthropicKey: !!getApiKey('anthropic') || !!process.env.ANTHROPIC_API_KEY,
|
||||||
|
hasGoogleKey: !!getApiKey('google'),
|
||||||
hasOpenaiKey: !!getApiKey('openai') || !!process.env.OPENAI_API_KEY,
|
hasOpenaiKey: !!getApiKey('openai') || !!process.env.OPENAI_API_KEY,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -21,22 +21,25 @@ export function createStoreApiKeyHandler() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setApiKey(provider, apiKey);
|
const providerEnvMap: Record<string, string> = {
|
||||||
|
anthropic: 'ANTHROPIC_API_KEY',
|
||||||
// Also set as environment variable and persist to .env
|
anthropic_oauth_token: 'ANTHROPIC_API_KEY',
|
||||||
if (provider === 'anthropic' || provider === 'anthropic_oauth_token') {
|
openai: 'OPENAI_API_KEY',
|
||||||
// Both API key and OAuth token use ANTHROPIC_API_KEY
|
};
|
||||||
process.env.ANTHROPIC_API_KEY = apiKey;
|
const envKey = providerEnvMap[provider];
|
||||||
await persistApiKeyToEnv('ANTHROPIC_API_KEY', apiKey);
|
if (!envKey) {
|
||||||
logger.info('[Setup] Stored API key as ANTHROPIC_API_KEY');
|
|
||||||
} else {
|
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: `Unsupported provider: ${provider}. Only anthropic is supported.`,
|
error: `Unsupported provider: ${provider}. Only anthropic and openai are supported.`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setApiKey(provider, apiKey);
|
||||||
|
process.env[envKey] = apiKey;
|
||||||
|
await persistApiKeyToEnv(envKey, apiKey);
|
||||||
|
logger.info(`[Setup] Stored API key as ${envKey}`);
|
||||||
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, 'Store API key failed');
|
logError(error, 'Store API key failed');
|
||||||
|
|||||||
@@ -431,6 +431,8 @@ export class SettingsService {
|
|||||||
*/
|
*/
|
||||||
async getMaskedCredentials(): Promise<{
|
async getMaskedCredentials(): Promise<{
|
||||||
anthropic: { configured: boolean; masked: string };
|
anthropic: { configured: boolean; masked: string };
|
||||||
|
google: { configured: boolean; masked: string };
|
||||||
|
openai: { configured: boolean; masked: string };
|
||||||
}> {
|
}> {
|
||||||
const credentials = await this.getCredentials();
|
const credentials = await this.getCredentials();
|
||||||
|
|
||||||
@@ -444,6 +446,14 @@ export class SettingsService {
|
|||||||
configured: !!credentials.apiKeys.anthropic,
|
configured: !!credentials.apiKeys.anthropic,
|
||||||
masked: maskKey(credentials.apiKeys.anthropic),
|
masked: maskKey(credentials.apiKeys.anthropic),
|
||||||
},
|
},
|
||||||
|
google: {
|
||||||
|
configured: !!credentials.apiKeys.google,
|
||||||
|
masked: maskKey(credentials.apiKeys.google),
|
||||||
|
},
|
||||||
|
openai: {
|
||||||
|
configured: !!credentials.apiKeys.openai,
|
||||||
|
masked: maskKey(credentials.apiKeys.openai),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export {
|
|||||||
type ModelAlias,
|
type ModelAlias,
|
||||||
type CodexModelId,
|
type CodexModelId,
|
||||||
type AgentModel,
|
type AgentModel,
|
||||||
|
type ModelId,
|
||||||
} from './model.js';
|
} from './model.js';
|
||||||
|
|
||||||
// Event types
|
// Event types
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* Model alias mapping for Claude models
|
* Model alias mapping for Claude models
|
||||||
*/
|
*/
|
||||||
|
import type { CursorModelId } from './cursor-models.js';
|
||||||
|
import type { OpencodeModelId } from './opencode-models.js';
|
||||||
|
|
||||||
export const CLAUDE_MODEL_MAP: Record<string, string> = {
|
export const CLAUDE_MODEL_MAP: Record<string, string> = {
|
||||||
haiku: 'claude-haiku-4-5-20251001',
|
haiku: 'claude-haiku-4-5-20251001',
|
||||||
sonnet: 'claude-sonnet-4-5-20250929',
|
sonnet: 'claude-sonnet-4-5-20250929',
|
||||||
@@ -74,3 +77,26 @@ export type CodexModelId = (typeof CODEX_MODEL_MAP)[keyof typeof CODEX_MODEL_MAP
|
|||||||
* Represents available models across providers
|
* Represents available models across providers
|
||||||
*/
|
*/
|
||||||
export type AgentModel = ModelAlias | CodexModelId;
|
export type AgentModel = ModelAlias | CodexModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic provider model IDs discovered at runtime (provider/model format)
|
||||||
|
*/
|
||||||
|
export type DynamicModelId = `${string}/${string}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider-prefixed model IDs used for routing
|
||||||
|
*/
|
||||||
|
export type PrefixedCursorModelId = `cursor-${string}`;
|
||||||
|
export type PrefixedOpencodeModelId = `opencode-${string}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ModelId - Unified model identifier across providers
|
||||||
|
*/
|
||||||
|
export type ModelId =
|
||||||
|
| ModelAlias
|
||||||
|
| CodexModelId
|
||||||
|
| CursorModelId
|
||||||
|
| OpencodeModelId
|
||||||
|
| DynamicModelId
|
||||||
|
| PrefixedCursorModelId
|
||||||
|
| PrefixedOpencodeModelId;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* (for file I/O via SettingsService) and the UI (for state management and sync).
|
* (for file I/O via SettingsService) and the UI (for state management and sync).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ModelAlias, AgentModel, CodexModelId } from './model.js';
|
import type { ModelAlias, ModelId } from './model.js';
|
||||||
import type { CursorModelId } from './cursor-models.js';
|
import type { CursorModelId } from './cursor-models.js';
|
||||||
import { CURSOR_MODEL_MAP, getAllCursorModelIds } from './cursor-models.js';
|
import { CURSOR_MODEL_MAP, getAllCursorModelIds } from './cursor-models.js';
|
||||||
import type { OpencodeModelId } from './opencode-models.js';
|
import type { OpencodeModelId } from './opencode-models.js';
|
||||||
@@ -114,8 +114,8 @@ const DEFAULT_CODEX_ADDITIONAL_DIRS: string[] = [];
|
|||||||
* - Cursor models: Handle thinking internally
|
* - Cursor models: Handle thinking internally
|
||||||
*/
|
*/
|
||||||
export interface PhaseModelEntry {
|
export interface PhaseModelEntry {
|
||||||
/** The model to use (Claude alias, Cursor model ID, or Codex model ID) */
|
/** The model to use (supports Claude, Cursor, Codex, OpenCode, and dynamic provider IDs) */
|
||||||
model: ModelAlias | CursorModelId | CodexModelId;
|
model: ModelId;
|
||||||
/** Extended thinking level (only applies to Claude models, defaults to 'none') */
|
/** Extended thinking level (only applies to Claude models, defaults to 'none') */
|
||||||
thinkingLevel?: ThinkingLevel;
|
thinkingLevel?: ThinkingLevel;
|
||||||
/** Reasoning effort level (only applies to Codex models, defaults to 'none') */
|
/** Reasoning effort level (only applies to Codex models, defaults to 'none') */
|
||||||
|
|||||||
Reference in New Issue
Block a user