From b4d3426e6a325ae36d54f2d5e06bdcfb5795e38a Mon Sep 17 00:00:00 2001 From: catlog22 Date: Wed, 25 Feb 2026 22:37:30 +0800 Subject: [PATCH] feat: add CLI config preview API for Codex and Gemini - Implemented `fetchCodexConfigPreview` and `fetchGeminiConfigPreview` functions in the API layer to retrieve masked configuration files. - Added new interfaces `CodexConfigPreviewResponse` and `GeminiConfigPreviewResponse` to define the structure of the API responses. - Created utility functions to read and mask sensitive values from `config.toml` and `auth.json` for Codex, and `settings.json` for Gemini. - Updated CLI settings routes to handle new preview endpoints. - Enhanced session content parser to support Claude JSONL format. - Updated UI components to reflect changes in history page and navigation, including new tabs for observability. - Localized changes for English and Chinese languages to reflect "CLI History" terminology. --- .../api-settings/CliSettingsModal.tsx | 140 ++++- .../components/issue/hub/IssueHubHeader.tsx | 14 +- .../src/components/issue/hub/IssueHubTabs.tsx | 3 +- .../src/components/layout/Sidebar.tsx | 2 - ccw/frontend/src/lib/api.ts | 48 ++ ccw/frontend/src/locales/en/common.json | 2 +- ccw/frontend/src/locales/en/navigation.json | 2 +- ccw/frontend/src/locales/zh/common.json | 2 +- ccw/frontend/src/locales/zh/navigation.json | 2 +- ccw/frontend/src/pages/HistoryPage.tsx | 316 +++++----- ccw/frontend/src/pages/IssueHubPage.tsx | 22 +- ccw/src/core/routes/cli-settings-routes.ts | 171 ++++++ ccw/src/tools/claude-session-parser.ts | 539 ++++++++++++++++++ ccw/src/tools/session-content-parser.ts | 5 +- ccw/src/types/cli-settings.ts | 32 ++ 15 files changed, 1137 insertions(+), 163 deletions(-) create mode 100644 ccw/src/tools/claude-session-parser.ts diff --git a/ccw/frontend/src/components/api-settings/CliSettingsModal.tsx b/ccw/frontend/src/components/api-settings/CliSettingsModal.tsx index 672a145e..bb811d13 100644 --- a/ccw/frontend/src/components/api-settings/CliSettingsModal.tsx +++ b/ccw/frontend/src/components/api-settings/CliSettingsModal.tsx @@ -5,7 +5,7 @@ import { useState, useEffect } from 'react'; import { useIntl } from 'react-intl'; -import { Check, Eye, EyeOff, X, Plus } from 'lucide-react'; +import { Check, Eye, EyeOff, X, Plus, Loader2, Download } from 'lucide-react'; import { Dialog, DialogContent, @@ -23,6 +23,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/Tabs'; import { useCreateCliSettings, useUpdateCliSettings, useProviders } from '@/hooks/useApiSettings'; import { useNotifications } from '@/hooks/useNotifications'; +import { fetchCodexConfigPreview, fetchGeminiConfigPreview } from '@/lib/api'; import type { CliSettingsEndpoint, CliProvider } from '@/lib/api'; // ========== Types ========== @@ -101,6 +102,8 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider } // Gemini specific const [geminiApiKey, setGeminiApiKey] = useState(''); const [showGeminiKey, setShowGeminiKey] = useState(false); + const [geminiSettingsJson, setGeminiSettingsJson] = useState(''); + const [isLoadingGeminiConfig, setIsLoadingGeminiConfig] = useState(false); // Shared const [model, setModel] = useState(''); @@ -112,6 +115,9 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider } const [showJsonInput, setShowJsonInput] = useState(false); const [errors, setErrors] = useState>({}); + // Codex config preview loading state + const [isLoadingCodexConfig, setIsLoadingCodexConfig] = useState(false); + // Initialize form useEffect(() => { if (cliSettings) { @@ -171,6 +177,7 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider } setWriteCommonConfig(false); setGeminiApiKey(''); setShowGeminiKey(false); + setGeminiSettingsJson(''); setAvailableModels([]); setModelInput(''); setTags([]); @@ -224,6 +231,47 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider } return Object.keys(newErrors).length === 0; }; + // Handle load Codex config preview + const handleLoadCodexConfig = async () => { + setIsLoadingCodexConfig(true); + try { + const result = await fetchCodexConfigPreview(); + if (result.success) { + if (result.configToml) { + setConfigToml(result.configToml); + } + if (result.authJson) { + setAuthJson(result.authJson); + } + } else { + error(formatMessage({ id: 'apiSettings.cliSettings.loadConfigError' }) || 'Failed to load config'); + } + } catch (err) { + error(formatMessage({ id: 'apiSettings.cliSettings.loadConfigError' }) || 'Failed to load config'); + } finally { + setIsLoadingCodexConfig(false); + } + }; + + // Handle load Gemini config preview + const handleLoadGeminiConfig = async () => { + setIsLoadingGeminiConfig(true); + try { + const result = await fetchGeminiConfigPreview(); + if (result.success) { + if (result.settingsJson) { + setGeminiSettingsJson(result.settingsJson); + } + } else { + error(formatMessage({ id: 'apiSettings.cliSettings.loadConfigError' }) || 'Failed to load config'); + } + } catch (err) { + error(formatMessage({ id: 'apiSettings.cliSettings.loadConfigError' }) || 'Failed to load config'); + } finally { + setIsLoadingGeminiConfig(false); + } + }; + // Handle save const handleSave = async () => { if (!validateForm()) return; @@ -521,6 +569,35 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider } {/* ========== Codex Settings ========== */} {cliProvider === 'codex' && (
+ {/* Load Global Config Button */} +
+
+

Load Global Config

+

+ Load config.toml and auth.json from global Codex config directory +

+
+ +
+ {/* API Key */}
@@ -645,6 +722,35 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider } {/* ========== Gemini Settings ========== */} {cliProvider === 'gemini' && (
+ {/* Load Global Config Button */} +
+
+

Load Global Config

+

+ Load settings.json from global Gemini config directory +

+
+ +
+
@@ -675,6 +781,38 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider } placeholder="gemini-2.5-flash" />
+ + {/* settings.json Editor */} + {geminiSettingsJson && ( +
+
+ + +
+