1+ "use client" ;
2+
3+ import React , { useState , useEffect } from "react" ;
4+ import CodeMirror from "@uiw/react-codemirror" ;
5+ import { javascript } from "@codemirror/lang-javascript" ;
6+ import { githubLight } from "@uiw/codemirror-theme-github" ;
7+ import { Card , CardContent , CardDescription , CardHeader , CardTitle } from "@/components/ui/card" ;
8+ import { Button } from "@/components/ui/button" ;
9+ import { Label } from "@/components/ui/label" ;
10+ import { Alert , AlertDescription } from "@/components/ui/alert" ;
11+ import { AlertCircle , Save } from "lucide-react" ;
12+ import { toast } from "sonner" ;
13+ import { SupabaseService } from "@/lib/supabase-service" ;
14+ import { useUserProfile } from "@/hooks/useUserProfile" ;
15+
16+ interface CodeAgentConfig {
17+ claudeCode ?: Record < string , string > ;
18+ codexCLI ?: Record < string , string > ;
19+ }
20+
21+ const DEFAULT_CLAUDE_ENV = {
22+ ANTHROPIC_API_KEY : "" ,
23+ // Add other Claude-specific env vars here if needed
24+ } ;
25+
26+ const DEFAULT_CODEX_ENV = {
27+ OPENAI_API_KEY : "" ,
28+ DISABLE_SANDBOX : "yes" ,
29+ CONTINUE_ON_BROWSER : "no" ,
30+ // Add other Codex-specific env vars here if needed
31+ } ;
32+
33+ export function CodeAgentSettings ( ) {
34+ const { profile, refreshProfile } = useUserProfile ( ) ;
35+ const [ claudeEnv , setClaudeEnv ] = useState ( "" ) ;
36+ const [ codexEnv , setCodexEnv ] = useState ( "" ) ;
37+ const [ isLoading , setIsLoading ] = useState ( false ) ;
38+ const [ errors , setErrors ] = useState < { claude ?: string ; codex ?: string } > ( { } ) ;
39+
40+ // Load settings from profile on mount
41+ useEffect ( ( ) => {
42+ 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 ) ) ;
46+ } else {
47+ setClaudeEnv ( JSON . stringify ( DEFAULT_CLAUDE_ENV , null , 2 ) ) ;
48+ setCodexEnv ( JSON . stringify ( DEFAULT_CODEX_ENV , null , 2 ) ) ;
49+ }
50+ } , [ profile ] ) ;
51+
52+ const validateJSON = ( value : string , agent : "claude" | "codex" ) => {
53+ try {
54+ JSON . parse ( value ) ;
55+ setErrors ( prev => ( { ...prev , [ agent ] : undefined } ) ) ;
56+ return true ;
57+ } catch ( e ) {
58+ setErrors ( prev => ( { ...prev , [ agent ] : "Invalid JSON format" } ) ) ;
59+ return false ;
60+ }
61+ } ;
62+
63+ const handleSave = async ( ) => {
64+ // Validate both JSONs
65+ const isClaudeValid = validateJSON ( claudeEnv , "claude" ) ;
66+ const isCodexValid = validateJSON ( codexEnv , "codex" ) ;
67+
68+ if ( ! isClaudeValid || ! isCodexValid ) {
69+ toast . error ( "Please fix JSON errors before saving" ) ;
70+ return ;
71+ }
72+
73+ setIsLoading ( true ) ;
74+ try {
75+ const claudeConfig = JSON . parse ( claudeEnv ) ;
76+ const codexConfig = JSON . parse ( codexEnv ) ;
77+
78+ const preferences : CodeAgentConfig = {
79+ claudeCode : claudeConfig ,
80+ codexCLI : codexConfig ,
81+ } ;
82+
83+ // Merge with existing preferences if any
84+ const existingPrefs = ( profile ?. preferences || { } ) as Record < string , any > ;
85+ const mergedPrefs = {
86+ ...existingPrefs ,
87+ ...preferences ,
88+ } ;
89+
90+ await SupabaseService . updateUserProfile ( { preferences : mergedPrefs } ) ;
91+ await refreshProfile ( ) ;
92+ toast . success ( "Code agent settings saved successfully" ) ;
93+ } catch ( error ) {
94+ console . error ( "Failed to save settings:" , error ) ;
95+ toast . error ( "Failed to save settings" ) ;
96+ } finally {
97+ setIsLoading ( false ) ;
98+ }
99+ } ;
100+
101+ return (
102+ < div className = "space-y-6" >
103+ < Card >
104+ < CardHeader >
105+ < CardTitle > Code Agent Settings</ CardTitle >
106+ < CardDescription >
107+ Configure environment variables for each code agent. These settings will be used when creating containers.
108+ </ CardDescription >
109+ </ CardHeader >
110+ < CardContent className = "space-y-6" >
111+ < Alert >
112+ < AlertCircle className = "h-4 w-4" />
113+ < AlertDescription >
114+ < strong > Important:</ strong > Store sensitive API keys here instead of hardcoding them. Personal settings will override default environment variables.
115+ </ AlertDescription >
116+ </ Alert >
117+
118+ { /* 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+ />
134+ </ 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 >
141+ </ div >
142+
143+ { /* 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+ />
159+ </ 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 >
166+ </ div >
167+
168+ < Button
169+ onClick = { handleSave }
170+ disabled = { isLoading || ! ! errors . claude || ! ! errors . codex }
171+ className = "w-full"
172+ >
173+ < Save className = "w-4 h-4 mr-2" />
174+ { isLoading ? "Saving..." : "Save Settings" }
175+ </ Button >
176+ </ CardContent >
177+ </ Card >
178+ </ div >
179+ ) ;
180+ }
0 commit comments