diff --git a/CLAUDE.md b/CLAUDE.md index 285a142..4eff876 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,6 +87,9 @@ src/ │ ├── token-extractor.js # Legacy token extraction from DB │ └── database.js # SQLite database access │ +├── webui/ # Web Management Interface +│ └── index.js # Express router and API endpoints +│ ├── cli/ # CLI tools │ └── accounts.js # Account management CLI │ @@ -105,9 +108,31 @@ src/ └── native-module-helper.js # Auto-rebuild for native modules ``` +**Frontend Structure (public/):** + +``` +public/ +├── index.html # Main entry point +├── js/ +│ ├── app.js # Main application logic (Alpine.js) +│ ├── store.js # Global state management +│ ├── components/ # UI Components +│ │ ├── dashboard.js # Real-time stats & charts +│ │ ├── account-manager.js # Account list & OAuth handling +│ │ ├── logs-viewer.js # Live log streaming +│ │ └── claude-config.js # CLI settings editor +│ └── utils/ # Frontend utilities +└── views/ # HTML partials (loaded dynamically) + ├── dashboard.html + ├── accounts.html + ├── settings.html + └── logs.html +``` + **Key Modules:** -- **src/server.js**: Express server exposing Anthropic-compatible endpoints (`/v1/messages`, `/v1/models`, `/health`, `/account-limits`) +- **src/server.js**: Express server exposing Anthropic-compatible endpoints (`/v1/messages`, `/v1/models`, `/health`, `/account-limits`) and mounting WebUI +- **src/webui/index.js**: WebUI backend handling API routes (`/api/*`) for config, accounts, and logs - **src/cloudcode/**: Cloud Code API client with retry/failover logic, streaming and non-streaming support - **src/account-manager/**: Multi-account pool with sticky selection, rate limit handling, and automatic cooldown - **src/auth/**: Authentication including Google OAuth, token extraction, database access, and auto-rebuild of native modules @@ -152,6 +177,18 @@ src/ - If rebuild succeeds, the module is reloaded; if reload fails, a server restart is required - Implementation in `src/utils/native-module-helper.js` and lazy loading in `src/auth/database.js` +**Web Management UI:** + +- **Stack**: Vanilla JS + Alpine.js + Tailwind CSS (via CDN) +- **Architecture**: Single Page Application (SPA) with dynamic view loading +- **State Management**: Alpine.store for global state (accounts, settings, logs) +- **Features**: + - Real-time dashboard with Chart.js visualization + - OAuth flow handling via popup window + - Live log streaming via Server-Sent Events (SSE) + - Config editor for both Proxy and Claude CLI (`~/.claude/settings.json`) +- **Security**: Optional password protection via `WEBUI_PASSWORD` env var + ## Testing Notes - Tests require the server to be running (`npm start` in separate terminal) @@ -195,6 +232,14 @@ src/ - `logger.setDebug(true)` - Enable debug mode - `logger.isDebugEnabled` - Check if debug mode is on +**WebUI APIs:** + +- `/api/accounts/*` - Account management (list, add, remove, refresh) +- `/api/config/*` - Server configuration (read/write) +- `/api/claude/config` - Claude CLI settings +- `/api/logs/stream` - SSE endpoint for real-time logs +- `/api/auth/url` - Generate Google OAuth URL + ## Maintenance When making significant changes to the codebase (new modules, refactoring, architectural changes), update this CLAUDE.md and the README.md file to keep documentation in sync. diff --git a/public/js/components/claude-config.js b/public/js/components/claude-config.js index 4a84e64..3d91702 100644 --- a/public/js/components/claude-config.js +++ b/public/js/components/claude-config.js @@ -57,7 +57,8 @@ window.Components.claudeConfig = () => ({ toggleGemini1mSuffix(enabled) { for (const field of this.geminiModelFields) { const val = this.config.env[field]; - if (val && val.toLowerCase().includes('gemini')) { + // Fix: Case-insensitive check for gemini + if (val && /gemini/i.test(val)) { if (enabled && !val.includes('[1m]')) { this.config.env[field] = val.trim() + ' [1m]'; } else if (!enabled && val.includes('[1m]')) { @@ -68,6 +69,25 @@ window.Components.claudeConfig = () => ({ this.gemini1mSuffix = enabled; }, + /** + * Helper to select a model from the dropdown + * @param {string} field - The config.env field to update + * @param {string} modelId - The selected model ID + */ + selectModel(field, modelId) { + if (!this.config.env) this.config.env = {}; + + let finalModelId = modelId; + // If 1M mode is enabled and it's a Gemini model, append the suffix + if (this.gemini1mSuffix && modelId.toLowerCase().includes('gemini')) { + if (!finalModelId.includes('[1m]')) { + finalModelId = finalModelId.trim() + ' [1m]'; + } + } + + this.config.env[field] = finalModelId; + }, + async fetchConfig() { const password = Alpine.store('global').webuiPassword; try { diff --git a/public/views/settings.html b/public/views/settings.html index 5c68679..ed3a2f0 100644 --- a/public/views/settings.html +++ b/public/views/settings.html @@ -99,11 +99,13 @@ + @change="$store.settings.saveSettings(true)" + aria-label="Polling interval slider"> + @change="$store.settings.saveSettings(true)" + aria-label="Polling interval value">