mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
feat: add GPT-5.2 model support and refresh profiles functionality
- Introduced the GPT-5.2 model with advanced coding capabilities across various components. - Added a new button in ProfilesView to refresh default profiles, enhancing user experience. - Updated CodexSetupStep to clarify authentication requirements and added commands for verifying login status. - Enhanced utility functions to recognize the new GPT-5.2 model in the application.
This commit is contained in:
@@ -146,6 +146,13 @@ const CLAUDE_MODELS: ModelOption[] = [
|
||||
];
|
||||
|
||||
const CODEX_MODELS: ModelOption[] = [
|
||||
{
|
||||
id: "gpt-5.2",
|
||||
label: "GPT-5.2",
|
||||
description: "Latest OpenAI model with advanced coding capabilities.",
|
||||
badge: "Latest",
|
||||
provider: "codex",
|
||||
},
|
||||
{
|
||||
id: "gpt-5.1-codex-max",
|
||||
label: "GPT-5.1 Codex Max",
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
GripVertical,
|
||||
Lock,
|
||||
Check,
|
||||
RefreshCw,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
@@ -89,6 +90,7 @@ const CLAUDE_MODELS: { id: AgentModel; label: string }[] = [
|
||||
];
|
||||
|
||||
const CODEX_MODELS: { id: AgentModel; label: string }[] = [
|
||||
{ id: "gpt-5.2", label: "GPT-5.2" },
|
||||
{ id: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max" },
|
||||
{ id: "gpt-5.1-codex", label: "GPT-5.1 Codex" },
|
||||
{ id: "gpt-5.1-codex-mini", label: "GPT-5.1 Codex Mini" },
|
||||
@@ -461,6 +463,7 @@ export function ProfilesView() {
|
||||
updateAIProfile,
|
||||
removeAIProfile,
|
||||
reorderAIProfiles,
|
||||
resetAIProfiles,
|
||||
} = useAppStore();
|
||||
const shortcuts = useKeyboardShortcutsConfig();
|
||||
|
||||
@@ -529,6 +532,13 @@ export function ProfilesView() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleResetProfiles = () => {
|
||||
resetAIProfiles();
|
||||
toast.success("Profiles refreshed", {
|
||||
description: "Default profiles have been updated to the latest version",
|
||||
});
|
||||
};
|
||||
|
||||
// Build keyboard shortcuts for profiles view
|
||||
const profilesShortcuts: KeyboardShortcut[] = useMemo(() => {
|
||||
const shortcutsList: KeyboardShortcut[] = [];
|
||||
@@ -568,15 +578,26 @@ export function ProfilesView() {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<HotkeyButton
|
||||
onClick={() => setShowAddDialog(true)}
|
||||
hotkey={shortcuts.addProfile}
|
||||
hotkeyActive={false}
|
||||
data-testid="add-profile-button"
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
New Profile
|
||||
</HotkeyButton>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleResetProfiles}
|
||||
data-testid="refresh-profiles-button"
|
||||
className="gap-2"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
Refresh Defaults
|
||||
</Button>
|
||||
<HotkeyButton
|
||||
onClick={() => setShowAddDialog(true)}
|
||||
hotkey={shortcuts.addProfile}
|
||||
hotkeyActive={false}
|
||||
data-testid="add-profile-button"
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
New Profile
|
||||
</HotkeyButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -282,21 +282,21 @@ export function CodexSetupStep({
|
||||
<Key className="w-5 h-5" />
|
||||
Authentication
|
||||
</CardTitle>
|
||||
<CardDescription>Codex requires an OpenAI API key</CardDescription>
|
||||
<CardDescription>Codex requires authentication via ChatGPT account or API key</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{codexCliStatus?.installed && (
|
||||
<div className="p-4 rounded-lg bg-muted/50 border border-border">
|
||||
<div className="flex items-start gap-3">
|
||||
<Terminal className="w-5 h-5 text-green-500 mt-0.5" />
|
||||
<div>
|
||||
<p className="font-medium text-foreground">
|
||||
Authenticate via CLI
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-foreground mb-2">
|
||||
Authenticate via CLI (Recommended)
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground mb-2">
|
||||
Run this command in your terminal:
|
||||
<p className="text-sm text-muted-foreground mb-3">
|
||||
Run the following command in your terminal to login with your ChatGPT account:
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<code className="bg-muted px-3 py-1 rounded text-sm font-mono text-foreground">
|
||||
codex auth login
|
||||
</code>
|
||||
@@ -308,6 +308,21 @@ export function CodexSetupStep({
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mb-2">
|
||||
After logging in, you can verify your authentication status:
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="bg-muted px-3 py-1 rounded text-sm font-mono text-foreground">
|
||||
codex login status
|
||||
</code>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => copyCommand("codex login status")}
|
||||
>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@ export function cn(...inputs: ClassValue[]) {
|
||||
export function isCodexModel(model?: AgentModel | string): boolean {
|
||||
if (!model) return false;
|
||||
const codexModels: string[] = [
|
||||
"gpt-5.2",
|
||||
"gpt-5.1-codex-max",
|
||||
"gpt-5.1-codex",
|
||||
"gpt-5.1-codex-mini",
|
||||
@@ -36,6 +37,7 @@ export function getModelDisplayName(model: AgentModel | string): string {
|
||||
haiku: "Claude Haiku",
|
||||
sonnet: "Claude Sonnet",
|
||||
opus: "Claude Opus",
|
||||
"gpt-5.2": "GPT-5.2",
|
||||
"gpt-5.1-codex-max": "GPT-5.1 Codex Max",
|
||||
"gpt-5.1-codex": "GPT-5.1 Codex",
|
||||
"gpt-5.1-codex-mini": "GPT-5.1 Codex Mini",
|
||||
|
||||
@@ -203,6 +203,7 @@ export interface FeatureImagePath {
|
||||
export type ClaudeModel = "opus" | "sonnet" | "haiku";
|
||||
// OpenAI/Codex models
|
||||
export type OpenAIModel =
|
||||
| "gpt-5.2"
|
||||
| "gpt-5.1-codex-max"
|
||||
| "gpt-5.1-codex"
|
||||
| "gpt-5.1-codex-mini"
|
||||
@@ -445,6 +446,7 @@ export interface AppActions {
|
||||
updateAIProfile: (id: string, updates: Partial<AIProfile>) => void;
|
||||
removeAIProfile: (id: string) => void;
|
||||
reorderAIProfiles: (oldIndex: number, newIndex: number) => void;
|
||||
resetAIProfiles: () => void;
|
||||
|
||||
// Project Analysis actions
|
||||
setProjectAnalysis: (analysis: ProjectAnalysis | null) => void;
|
||||
@@ -491,6 +493,16 @@ const DEFAULT_AI_PROFILES: AIProfile[] = [
|
||||
isBuiltIn: true,
|
||||
icon: "Zap",
|
||||
},
|
||||
{
|
||||
id: "profile-gpt52",
|
||||
name: "GPT-5.2",
|
||||
description: "GPT-5.2 - Latest OpenAI model for advanced coding tasks.",
|
||||
model: "gpt-5.2",
|
||||
thinkingLevel: "none",
|
||||
provider: "codex",
|
||||
isBuiltIn: true,
|
||||
icon: "Sparkles",
|
||||
},
|
||||
{
|
||||
id: "profile-codex-power",
|
||||
name: "Codex Power",
|
||||
@@ -1106,6 +1118,14 @@ export const useAppStore = create<AppState & AppActions>()(
|
||||
set({ aiProfiles: profiles });
|
||||
},
|
||||
|
||||
resetAIProfiles: () => {
|
||||
// Merge: keep user-created profiles, but refresh all built-in profiles to latest defaults
|
||||
const currentProfiles = get().aiProfiles;
|
||||
const userProfiles = currentProfiles.filter((p) => !p.isBuiltIn);
|
||||
const mergedProfiles = [...DEFAULT_AI_PROFILES, ...userProfiles];
|
||||
set({ aiProfiles: mergedProfiles });
|
||||
},
|
||||
|
||||
// Project Analysis actions
|
||||
setProjectAnalysis: (analysis) => set({ projectAnalysis: analysis }),
|
||||
setIsAnalyzing: (analyzing) => set({ isAnalyzing: analyzing }),
|
||||
|
||||
@@ -90,6 +90,15 @@ export function createModelsRoutes(): Router {
|
||||
supportsVision: true,
|
||||
supportsTools: false,
|
||||
},
|
||||
{
|
||||
id: "gpt-5.2",
|
||||
name: "GPT-5.2 (Codex)",
|
||||
provider: "openai",
|
||||
contextWindow: 256000,
|
||||
maxOutputTokens: 32768,
|
||||
supportsVision: true,
|
||||
supportsTools: true,
|
||||
},
|
||||
];
|
||||
|
||||
res.json({ success: true, models });
|
||||
|
||||
@@ -249,56 +249,41 @@ export function createSetupRoutes(): Router {
|
||||
const { stdout: versionOut } = await execAsync("codex --version");
|
||||
version = versionOut.trim();
|
||||
} catch {
|
||||
// Version command might not be available
|
||||
version = "unknown";
|
||||
}
|
||||
} catch {
|
||||
// Not found
|
||||
}
|
||||
|
||||
// Check for OpenAI/Codex authentication
|
||||
// Simplified: only check via CLI command, no file parsing
|
||||
let auth = {
|
||||
authenticated: false,
|
||||
method: "none" as string,
|
||||
hasAuthFile: false,
|
||||
hasEnvKey: !!process.env.OPENAI_API_KEY,
|
||||
hasStoredApiKey: !!apiKeys.openai,
|
||||
hasEnvApiKey: !!process.env.OPENAI_API_KEY,
|
||||
// Additional fields for subscription/account detection
|
||||
hasSubscription: false,
|
||||
cliLoggedIn: false,
|
||||
};
|
||||
|
||||
// Check for OpenAI CLI auth file (~/.codex/auth.json or similar)
|
||||
const codexAuthPaths = [
|
||||
path.join(os.homedir(), ".codex", "auth.json"),
|
||||
path.join(os.homedir(), ".openai", "credentials"),
|
||||
path.join(os.homedir(), ".config", "openai", "credentials.json"),
|
||||
];
|
||||
|
||||
for (const authPath of codexAuthPaths) {
|
||||
// Try to verify authentication using codex CLI command if CLI is installed
|
||||
if (installed && cliPath) {
|
||||
try {
|
||||
const authContent = await fs.readFile(authPath, "utf-8");
|
||||
const authData = JSON.parse(authContent);
|
||||
auth.hasAuthFile = true;
|
||||
const { stdout: statusOutput } = await execAsync(`"${cliPath}" login status 2>&1`, {
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
// Check for subscription/tokens
|
||||
if (authData.subscription || authData.plan || authData.account_type) {
|
||||
auth.hasSubscription = true;
|
||||
// Check if the output indicates logged in status
|
||||
if (statusOutput && (statusOutput.includes('Logged in') || statusOutput.includes('Authenticated'))) {
|
||||
auth.authenticated = true;
|
||||
auth.method = "subscription"; // Codex subscription (Plus/Team)
|
||||
} else if (authData.access_token || authData.api_key) {
|
||||
auth.cliLoggedIn = true;
|
||||
auth.authenticated = true;
|
||||
auth.method = "cli_verified"; // CLI logged in with account
|
||||
auth.method = "cli_verified"; // CLI verified via login status command
|
||||
}
|
||||
break;
|
||||
} catch {
|
||||
// Auth file not found at this path
|
||||
} catch (error) {
|
||||
// CLI check failed - user needs to login manually
|
||||
console.log("[Setup] Codex login status check failed:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Environment variable has highest priority
|
||||
if (auth.hasEnvApiKey) {
|
||||
// Environment variable override
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
auth.authenticated = true;
|
||||
auth.method = "env"; // OPENAI_API_KEY environment variable
|
||||
}
|
||||
|
||||
@@ -18,6 +18,13 @@ import type { EventEmitter, EventType } from "../lib/events.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
// Model name mappings for Claude (matching electron version)
|
||||
const MODEL_MAP: Record<string, string> = {
|
||||
haiku: "claude-haiku-4-5",
|
||||
sonnet: "claude-sonnet-4-20250514",
|
||||
opus: "claude-opus-4-5-20251101",
|
||||
};
|
||||
|
||||
interface Feature {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -25,6 +32,37 @@ interface Feature {
|
||||
status: string;
|
||||
priority?: number;
|
||||
spec?: string;
|
||||
model?: string; // Model to use for this feature
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model string from feature's model property
|
||||
* Supports model keys like "opus", "sonnet", "haiku" or full model strings
|
||||
* Also supports OpenAI/Codex models like "gpt-5.2", "gpt-5.1-codex", etc.
|
||||
*/
|
||||
function getModelString(feature: Feature): string {
|
||||
const modelKey = feature.model || "opus"; // Default to opus
|
||||
|
||||
// Check if it's an OpenAI/Codex model (starts with "gpt-" or "o" for O-series)
|
||||
if (modelKey.startsWith("gpt-") || modelKey.startsWith("o")) {
|
||||
console.log(`[AutoMode] Using OpenAI/Codex model from feature ${feature.id}: ${modelKey} (passing through)`);
|
||||
return modelKey;
|
||||
}
|
||||
|
||||
// If it's already a full Claude model string (contains "claude-"), use it directly
|
||||
if (modelKey.includes("claude-")) {
|
||||
console.log(`[AutoMode] Using Claude model from feature ${feature.id}: ${modelKey} (full model string)`);
|
||||
return modelKey;
|
||||
}
|
||||
|
||||
// Otherwise, look it up in the Claude model map
|
||||
const modelString = MODEL_MAP[modelKey] || MODEL_MAP.opus;
|
||||
if (modelString !== MODEL_MAP.opus || modelKey === "opus") {
|
||||
console.log(`[AutoMode] Resolved Claude model for feature ${feature.id}: "${modelKey}" -> "${modelString}"`);
|
||||
} else {
|
||||
console.warn(`[AutoMode] Unknown model key "${modelKey}" for feature ${feature.id}, defaulting to "${modelString}"`);
|
||||
}
|
||||
return modelString;
|
||||
}
|
||||
|
||||
interface RunningFeature {
|
||||
@@ -199,8 +237,12 @@ export class AutoModeService {
|
||||
// Build the prompt
|
||||
const prompt = this.buildFeaturePrompt(feature);
|
||||
|
||||
// Run the agent
|
||||
await this.runAgent(workDir, featureId, prompt, abortController);
|
||||
// Get model from feature
|
||||
const model = getModelString(feature);
|
||||
console.log(`[AutoMode] Executing feature ${featureId} with model: ${model}`);
|
||||
|
||||
// Run the agent with the feature's model
|
||||
await this.runAgent(workDir, featureId, prompt, abortController, undefined, model);
|
||||
|
||||
// Mark as waiting_approval for user review
|
||||
await this.updateFeatureStatus(projectPath, featureId, "waiting_approval");
|
||||
@@ -330,7 +372,12 @@ export class AutoModeService {
|
||||
});
|
||||
|
||||
try {
|
||||
await this.runAgent(workDir, featureId, prompt, abortController, imagePaths);
|
||||
// Load feature to get its model
|
||||
const feature = await this.loadFeature(projectPath, featureId);
|
||||
const model = feature ? getModelString(feature) : MODEL_MAP.opus;
|
||||
console.log(`[AutoMode] Follow-up for feature ${featureId} using model: ${model}`);
|
||||
|
||||
await this.runAgent(workDir, featureId, prompt, abortController, imagePaths, model);
|
||||
|
||||
this.emitAutoModeEvent("auto_mode_feature_complete", {
|
||||
featureId,
|
||||
@@ -709,10 +756,23 @@ When done, summarize what you implemented and any notes for the developer.`;
|
||||
featureId: string,
|
||||
prompt: string,
|
||||
abortController: AbortController,
|
||||
imagePaths?: string[]
|
||||
imagePaths?: string[],
|
||||
model?: string
|
||||
): Promise<void> {
|
||||
const finalModel = model || MODEL_MAP.opus;
|
||||
console.log(`[AutoMode] runAgent called for feature ${featureId} with model: ${finalModel}`);
|
||||
|
||||
// Check if this is an OpenAI/Codex model - Claude Agent SDK doesn't support these
|
||||
if (finalModel.startsWith("gpt-") || finalModel.startsWith("o")) {
|
||||
const errorMessage = `OpenAI/Codex models (like "${finalModel}") are not yet supported in server mode. ` +
|
||||
`Please use a Claude model (opus, sonnet, or haiku) instead. ` +
|
||||
`OpenAI/Codex models are only supported in the Electron app.`;
|
||||
console.error(`[AutoMode] ${errorMessage}`);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const options: Options = {
|
||||
model: "claude-opus-4-5-20251101",
|
||||
model: finalModel,
|
||||
maxTurns: 50,
|
||||
cwd: workDir,
|
||||
allowedTools: [
|
||||
|
||||
Reference in New Issue
Block a user