Skip to content

Commit 575178f

Browse files
refactor: optimize preference struct
1 parent c0fd9c8 commit 575178f

File tree

2 files changed

+224
-83
lines changed

2 files changed

+224
-83
lines changed

async-code-web/components/code-agent-settings.tsx

Lines changed: 199 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,88 +8,165 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
88
import { Button } from "@/components/ui/button";
99
import { Label } from "@/components/ui/label";
1010
import { Alert, AlertDescription } from "@/components/ui/alert";
11-
import { AlertCircle, Save } from "lucide-react";
11+
import { AlertCircle, Save, Key, Settings2 } from "lucide-react";
1212
import { toast } from "sonner";
1313
import { SupabaseService } from "@/lib/supabase-service";
1414
import { useUserProfile } from "@/hooks/useUserProfile";
1515

1616
interface CodeAgentConfig {
17-
claudeCode?: Record<string, string>;
18-
codexCLI?: Record<string, string>;
17+
claudeCode?: {
18+
env?: Record<string, string>;
19+
credentials?: Record<string, any> | null;
20+
};
21+
codex?: {
22+
env?: Record<string, string>;
23+
};
1924
}
2025

2126
const DEFAULT_CLAUDE_ENV = {
2227
ANTHROPIC_API_KEY: "",
2328
// Add other Claude-specific env vars here if needed
2429
};
2530

31+
const DEFAULT_CLAUDE_CREDENTIALS = {
32+
// Example structure - user can customize
33+
};
34+
2635
const DEFAULT_CODEX_ENV = {
2736
OPENAI_API_KEY: "",
2837
DISABLE_SANDBOX: "yes",
2938
CONTINUE_ON_BROWSER: "no",
3039
// Add other Codex-specific env vars here if needed
3140
};
3241

42+
// Helper function to check if credentials is meaningful (not empty/null/undefined)
43+
const hasMeaningfulCredentials = (creds: any): boolean => {
44+
if (!creds || creds === null || creds === undefined || creds === '') {
45+
return false;
46+
}
47+
if (typeof creds === 'object' && Object.keys(creds).length === 0) {
48+
return false;
49+
}
50+
return true;
51+
};
52+
3353
export function CodeAgentSettings() {
3454
const { profile, refreshProfile } = useUserProfile();
3555
const [claudeEnv, setClaudeEnv] = useState("");
56+
const [claudeCredentials, setClaudeCredentials] = useState("");
3657
const [codexEnv, setCodexEnv] = useState("");
3758
const [isLoading, setIsLoading] = useState(false);
38-
const [errors, setErrors] = useState<{ claude?: string; codex?: string }>({});
59+
const [errors, setErrors] = useState<{
60+
claudeEnv?: string;
61+
claudeCredentials?: string;
62+
codexEnv?: string;
63+
}>({});
3964

4065
// Load settings from profile on mount
4166
useEffect(() => {
4267
if (profile?.preferences) {
43-
const prefs = profile.preferences as CodeAgentConfig;
44-
setClaudeEnv(JSON.stringify(prefs.claudeCode || DEFAULT_CLAUDE_ENV, null, 2));
45-
setCodexEnv(JSON.stringify(prefs.codexCLI || DEFAULT_CODEX_ENV, null, 2));
68+
const prefs = profile.preferences as any; // Use any for backward compatibility
69+
70+
// Handle backward compatibility for Claude config
71+
let claudeConfig: any = {};
72+
if (prefs.claudeCode) {
73+
// Check if it's the new structure (has env/credentials properties)
74+
if (prefs.claudeCode.env || prefs.claudeCode.credentials) {
75+
claudeConfig = prefs.claudeCode;
76+
} else {
77+
// Old structure - migrate to new format
78+
const { credentials, ...envVars } = prefs.claudeCode;
79+
80+
claudeConfig = {
81+
env: envVars,
82+
credentials: hasMeaningfulCredentials(credentials) ? credentials : null
83+
};
84+
}
85+
}
86+
87+
// Handle backward compatibility for Codex config
88+
let codexConfig: any = {};
89+
if (prefs.codex) {
90+
// Check if it's the new structure
91+
if (prefs.codex.env) {
92+
codexConfig = prefs.codex;
93+
} else {
94+
// New structure for codex
95+
codexConfig = { env: prefs.codex };
96+
}
97+
} else if (prefs.codexCLI) {
98+
// Old codexCLI key - migrate to new codex key
99+
codexConfig = { env: prefs.codexCLI };
100+
}
101+
102+
setClaudeEnv(JSON.stringify(claudeConfig.env || DEFAULT_CLAUDE_ENV, null, 2));
103+
setClaudeCredentials(JSON.stringify(claudeConfig.credentials || DEFAULT_CLAUDE_CREDENTIALS, null, 2));
104+
setCodexEnv(JSON.stringify(codexConfig.env || DEFAULT_CODEX_ENV, null, 2));
46105
} else {
47106
setClaudeEnv(JSON.stringify(DEFAULT_CLAUDE_ENV, null, 2));
107+
setClaudeCredentials(JSON.stringify(DEFAULT_CLAUDE_CREDENTIALS, null, 2));
48108
setCodexEnv(JSON.stringify(DEFAULT_CODEX_ENV, null, 2));
49109
}
50110
}, [profile]);
51111

52-
const validateJSON = (value: string, agent: "claude" | "codex") => {
112+
const validateJSON = (value: string, key: string) => {
53113
try {
54114
JSON.parse(value);
55-
setErrors(prev => ({ ...prev, [agent]: undefined }));
115+
setErrors(prev => ({ ...prev, [key]: undefined }));
56116
return true;
57117
} catch (e) {
58-
setErrors(prev => ({ ...prev, [agent]: "Invalid JSON format" }));
118+
setErrors(prev => ({ ...prev, [key]: "Invalid JSON format" }));
59119
return false;
60120
}
61121
};
62122

63123
const handleSave = async () => {
64-
// Validate both JSONs
65-
const isClaudeValid = validateJSON(claudeEnv, "claude");
66-
const isCodexValid = validateJSON(codexEnv, "codex");
124+
// Validate all JSONs
125+
const isClaudeEnvValid = validateJSON(claudeEnv, "claudeEnv");
126+
const isClaudeCredentialsValid = validateJSON(claudeCredentials, "claudeCredentials");
127+
const isCodexEnvValid = validateJSON(codexEnv, "codexEnv");
67128

68-
if (!isClaudeValid || !isCodexValid) {
129+
if (!isClaudeEnvValid || !isClaudeCredentialsValid || !isCodexEnvValid) {
69130
toast.error("Please fix JSON errors before saving");
70131
return;
71132
}
72133

73134
setIsLoading(true);
74135
try {
75-
const claudeConfig = JSON.parse(claudeEnv);
76-
const codexConfig = JSON.parse(codexEnv);
136+
const claudeEnvConfig = JSON.parse(claudeEnv);
137+
const claudeCredentialsConfig = JSON.parse(claudeCredentials);
138+
const codexEnvConfig = JSON.parse(codexEnv);
77139

78140
const preferences: CodeAgentConfig = {
79-
claudeCode: claudeConfig,
80-
codexCLI: codexConfig,
141+
claudeCode: {
142+
env: claudeEnvConfig,
143+
credentials: hasMeaningfulCredentials(claudeCredentialsConfig) ? claudeCredentialsConfig : null,
144+
},
145+
codex: {
146+
env: codexEnvConfig,
147+
},
81148
};
82149

83150
// Merge with existing preferences if any
84151
const existingPrefs = (profile?.preferences || {}) as Record<string, any>;
152+
153+
// Clean up old keys during migration
154+
const { codexCLI, ...cleanedPrefs } = existingPrefs;
155+
85156
const mergedPrefs = {
86-
...existingPrefs,
157+
...cleanedPrefs,
87158
...preferences,
88159
};
89160

90161
await SupabaseService.updateUserProfile({ preferences: mergedPrefs });
91162
await refreshProfile();
92-
toast.success("Code agent settings saved successfully");
163+
164+
// Provide feedback about credentials handling
165+
const credentialsMessage = hasMeaningfulCredentials(claudeCredentialsConfig)
166+
? "Claude credentials will be configured"
167+
: "Claude credentials are empty and will be skipped";
168+
169+
toast.success(`Code agent settings saved successfully. ${credentialsMessage}`);
93170
} catch (error) {
94171
console.error("Failed to save settings:", error);
95172
toast.error("Failed to save settings");
@@ -104,70 +181,126 @@ export function CodeAgentSettings() {
104181
<CardHeader>
105182
<CardTitle>Code Agent Settings</CardTitle>
106183
<CardDescription>
107-
Configure environment variables for each code agent. These settings will be used when creating containers.
184+
Configure environment variables and credentials for each code agent. These settings will be used when creating containers.
108185
</CardDescription>
109186
</CardHeader>
110-
<CardContent className="space-y-6">
187+
<CardContent className="space-y-8">
111188
<Alert>
112189
<AlertCircle className="h-4 w-4" />
113190
<AlertDescription>
114-
<strong>Important:</strong> Store sensitive API keys here instead of hardcoding them. Personal settings will override default environment variables.
191+
<strong>Important:</strong> Environment variables and credentials are stored separately. Store sensitive API keys in environment variables, and authentication configs in credentials.
115192
</AlertDescription>
116193
</Alert>
117194

118195
{/* Claude Code Settings */}
119-
<div className="space-y-2">
120-
<Label htmlFor="claude-env">Claude Code Environment Variables</Label>
121-
<div className="border rounded-lg overflow-hidden">
122-
<CodeMirror
123-
id="claude-env"
124-
value={claudeEnv}
125-
height="200px"
126-
extensions={[javascript({ jsx: false })]}
127-
theme={githubLight}
128-
onChange={(value) => {
129-
setClaudeEnv(value);
130-
validateJSON(value, "claude");
131-
}}
132-
placeholder={JSON.stringify(DEFAULT_CLAUDE_ENV, null, 2)}
133-
/>
196+
<div className="space-y-6">
197+
<div className="flex items-center gap-2 pb-2 border-b">
198+
<Settings2 className="w-5 h-5 text-blue-600" />
199+
<h3 className="text-lg font-semibold">Claude Code Configuration</h3>
200+
</div>
201+
202+
{/* Claude Environment Variables */}
203+
<div className="space-y-2">
204+
<Label htmlFor="claude-env" className="flex items-center gap-2">
205+
<Settings2 className="w-4 h-4" />
206+
Environment Variables
207+
</Label>
208+
<div className="border rounded-lg overflow-hidden">
209+
<CodeMirror
210+
id="claude-env"
211+
value={claudeEnv}
212+
height="200px"
213+
extensions={[javascript({ jsx: false })]}
214+
theme={githubLight}
215+
onChange={(value) => {
216+
setClaudeEnv(value);
217+
validateJSON(value, "claudeEnv");
218+
}}
219+
placeholder={JSON.stringify(DEFAULT_CLAUDE_ENV, null, 2)}
220+
/>
221+
</div>
222+
{errors.claudeEnv && (
223+
<p className="text-sm text-red-500 mt-1">{errors.claudeEnv}</p>
224+
)}
225+
<p className="text-sm text-muted-foreground">
226+
Configure environment variables for Claude Code CLI (@anthropic-ai/claude-code)
227+
</p>
228+
</div>
229+
230+
{/* Claude Credentials */}
231+
<div className="space-y-2">
232+
<Label htmlFor="claude-credentials" className="flex items-center gap-2">
233+
<Key className="w-4 h-4" />
234+
Credentials (Optional)
235+
</Label>
236+
<div className="border rounded-lg overflow-hidden">
237+
<CodeMirror
238+
id="claude-credentials"
239+
value={claudeCredentials}
240+
height="150px"
241+
extensions={[javascript({ jsx: false })]}
242+
theme={githubLight}
243+
onChange={(value) => {
244+
setClaudeCredentials(value);
245+
validateJSON(value, "claudeCredentials");
246+
}}
247+
placeholder={JSON.stringify(DEFAULT_CLAUDE_CREDENTIALS, null, 2)}
248+
/>
249+
</div>
250+
{errors.claudeCredentials && (
251+
<p className="text-sm text-red-500 mt-1">{errors.claudeCredentials}</p>
252+
)}
253+
<p className="text-sm text-muted-foreground">
254+
Configure authentication credentials for Claude Code CLI (will be saved to ~/.claude/.credentials.json)
255+
</p>
134256
</div>
135-
{errors.claude && (
136-
<p className="text-sm text-red-500 mt-1">{errors.claude}</p>
137-
)}
138-
<p className="text-sm text-muted-foreground">
139-
Configure environment variables for Claude Code CLI (@anthropic-ai/claude-code)
140-
</p>
141257
</div>
142258

143259
{/* Codex CLI Settings */}
144-
<div className="space-y-2">
145-
<Label htmlFor="codex-env">Codex CLI Environment Variables</Label>
146-
<div className="border rounded-lg overflow-hidden">
147-
<CodeMirror
148-
id="codex-env"
149-
value={codexEnv}
150-
height="200px"
151-
extensions={[javascript({ jsx: false })]}
152-
theme={githubLight}
153-
onChange={(value) => {
154-
setCodexEnv(value);
155-
validateJSON(value, "codex");
156-
}}
157-
placeholder={JSON.stringify(DEFAULT_CODEX_ENV, null, 2)}
158-
/>
260+
<div className="space-y-6">
261+
<div className="flex items-center gap-2 pb-2 border-b">
262+
<Settings2 className="w-5 h-5 text-green-600" />
263+
<h3 className="text-lg font-semibold">Codex CLI Configuration</h3>
264+
</div>
265+
266+
{/* Codex Environment Variables */}
267+
<div className="space-y-2">
268+
<Label htmlFor="codex-env" className="flex items-center gap-2">
269+
<Settings2 className="w-4 h-4" />
270+
Environment Variables
271+
</Label>
272+
<div className="border rounded-lg overflow-hidden">
273+
<CodeMirror
274+
id="codex-env"
275+
value={codexEnv}
276+
height="200px"
277+
extensions={[javascript({ jsx: false })]}
278+
theme={githubLight}
279+
onChange={(value) => {
280+
setCodexEnv(value);
281+
validateJSON(value, "codexEnv");
282+
}}
283+
placeholder={JSON.stringify(DEFAULT_CODEX_ENV, null, 2)}
284+
/>
285+
</div>
286+
{errors.codexEnv && (
287+
<p className="text-sm text-red-500 mt-1">{errors.codexEnv}</p>
288+
)}
289+
<p className="text-sm text-muted-foreground">
290+
Configure environment variables for Codex CLI (@openai/codex)
291+
</p>
292+
</div>
293+
294+
<div className="p-3 bg-yellow-50 border border-yellow-200 rounded-md">
295+
<p className="text-sm text-yellow-800">
296+
<strong>Note:</strong> Codex CLI does not require separate credentials configuration. All settings are handled via environment variables.
297+
</p>
159298
</div>
160-
{errors.codex && (
161-
<p className="text-sm text-red-500 mt-1">{errors.codex}</p>
162-
)}
163-
<p className="text-sm text-muted-foreground">
164-
Configure environment variables for Codex CLI (@openai/codex)
165-
</p>
166299
</div>
167300

168301
<Button
169302
onClick={handleSave}
170-
disabled={isLoading || !!errors.claude || !!errors.codex}
303+
disabled={isLoading || !!errors.claudeEnv || !!errors.claudeCredentials || !!errors.codexEnv}
171304
className="w-full"
172305
>
173306
<Save className="w-4 h-4 mr-2" />

0 commit comments

Comments
 (0)