Spaces:
Sleeping
Sleeping
Commit History
-
refactor: decouple AccountPool, split codex-api and web.ts, fix CI (#113)
icebear +1: icebear0828Unverified signature* refactor: decouple AccountPool, split codex-api and web.ts, fix CI Phase 0 — CI fixes: - Fix TS2322: WS transport instructions type mismatch (Electron/Docker build) - Remove duplicate selectAll i18n keys (Vite warning) - Add rebase retry to sync-changelog.yml push step (race with bump-electron) Phase 1-5 — Architecture improvements: - Extract codex-types.ts: API type definitions separated from class - Extract rotation-strategy.ts: pure-function selection logic (10 tests) - Split web.ts (605 LOC) into routes/admin/ (health/update/connection/settings) - Extract account-persistence.ts: injectable fs persistence interface (8 tests) - Split codex-api.ts into codex-sse/usage/models modules (10 tests) All existing import paths preserved via re-exports. 902 tests pass (70 files), build clean. * fix: address PR #113 review issues before merge - Fix needsPersist regression: load() now returns { entries, needsPersist } and auto-persists when backfill is applied (restores original behavio…0c8b3c0 -
fix: make instructions optional in /v1/responses for client compatibility (#71) (#112)
icebear +1: icebear0828Unverified signatureCherry and other third-party clients don't send the `instructions` field. Previously this returned 400; now it defaults to an empty string. Co-authored-by: icebear0828 <icebear0828@users.noreply.github.com>6fa846e
-
fix: dynamic import `ws` to prevent Electron ESM startup crash (#88)
icebear +1: icebear0828Unverified signatureThe `ws` package uses CJS `require('events')` internally. When esbuild bundles the server as ESM for Electron, these dynamic requires fail at startup with "Dynamic require of 'events' is not supported". Switch to lazy dynamic `import("ws")` so the CJS dependency chain is only resolved at first WebSocket use, not at module load time. Also lazy-import `https-proxy-agent` for the same reason. Closes #87 Co-authored-by: icebear0828 <icebear0828@users.noreply.github.com>ea2be52
-
feat: WebSocket transport + previous_response_id for multi-turn (#84)
icebear +1: icebear0828Unverified signature* feat: WebSocket transport + previous_response_id for multi-turn (#83) The Codex backend only supports `previous_response_id` over WebSocket, not HTTP SSE. This adds a dual-track transport layer: - /v1/responses now connects to the backend via WebSocket by default - WebSocket omits the `store` field, letting the server persist responses - Clients can send `previous_response_id` to reference earlier turns - On WebSocket failure, automatically falls back to HTTP SSE New files: - src/proxy/ws-transport.ts — WebSocket-to-SSE bridge (ReadableStream) Dependencies: ws, https-proxy-agent * fix: move ws from devDependencies to dependencies ws is imported at runtime by src/proxy/ws-transport.ts, so it must be in dependencies for production installs (npm install --omit=dev). --------- Co-authored-by: icebear0828 <icebear0828@users.noreply.github.com>d07b5c0
-
feat: display secondary rate limit window (weekly cap) in dashboard
icebear0828 +1: Claude Opus 4.6Add secondary_rate_limit to CodexQuota type and map secondary_window from Codex usage API. Dashboard now shows both primary (hourly) and secondary (weekly) quota bars with independent progress and reset times. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>2996fd8
-
feat: add Structured Outputs support (response_format → text.format)
icebear0828 +1: Claude Opus 4.6OpenAI response_format (json_object/json_schema) and Gemini responseMimeType/responseSchema now translate to Codex text.format. /v1/responses endpoint passes through text field as-is. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>53d3b3b -
fix: update model catalog — gpt-5.4/5.3-codex removed, gpt-5.2-codex is new flagship
icebear0828 +1: Claude Opus 4.6Backend dropped gpt-5.4, gpt-5.3-codex, gpt-5.3-codex-spark. New model list: gpt-5.2-codex (default), gpt-5.2, gpt-5.1-codex-max, gpt-5.1-codex, gpt-5.1, gpt-5-codex, gpt-5, gpt-oss-120b, gpt-oss-20b, gpt-5.1-codex-mini, gpt-5-codex-mini. - config/default.yaml: default model → gpt-5.2-codex - config/models.yaml: full rewrite matching backend /codex/models response - model-store.ts: isCodexCompatibleId() now matches gpt-oss-*, normalizeBackendModel() handles supported_reasoning_levels + effort key - codex-api.ts: getModels() adds ?client_version= (now required by backend) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>597668f
-
fix: strip service_tier from request body + add OpenAI-Beta header + update gpt-5.4 efforts
icebear0828 +1: Claude Opus 4.6- Codex backend rejects service_tier in request body ("Unsupported service_tier: fast"). Desktop app handles Fast mode at app-server level, not via the API. Proxy now strips service_tier before sending to backend. - Add OpenAI-Beta: responses_websockets=2026-02-06 header (matching Desktop binary). - Update gpt-5.4 reasoning efforts: minimal→none, add xhigh (matching backend). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>44b20f4
-
feat: model name suffix system + service_tier + Docker persistence fix (#35)
icebear +2: icebear0828, Claude Opus 4.6Unverified signature* feat: model name suffix system + service_tier (fast mode) support Add a unified model name suffix convention so CLI tools (Claude Code, opencode, etc.) can control reasoning effort and speed mode purely through the model name — no extra API fields needed. Suffix parsing (`parseModelName`): - `-fast` / `-flex` → `service_tier` - `-minimal` / `-low` / `-medium` / `-high` / `-xhigh` → `reasoning_effort` - Known model IDs and aliases are never suffix-parsed - Priority: explicit API field > suffix > model default > config default Backend: - `parseModelName()` in model-store.ts (core suffix resolver) - `service_tier` field in Zod schema + CodexResponsesRequest interface - `default_service_tier` in config schema + default.yaml - All 3 translation layers (OpenAI/Anthropic/Gemini) use parseModelName() Dashboard: - Standard / Fast speed toggle in ApiConfig - AnthropicSetup shows compound model name with suffixes - CodeExamples builds compound model name, suppresses redundant params Exampl…5416ffb -
feat: proxy assignment management page with bulk operations (#34)
icebear +2: icebear0828, Claude Opus 4.6Unverified signature* feat: proxy assignment management page with bulk operations Add dedicated page (#/proxy-settings) for managing proxy assignments across hundreds of accounts with matrix-style dual-column layout: - Left sidebar: proxy group list with counts, search, click-to-filter - Right panel: paginated account table (50/page) with search, status filter, Shift+click range select, per-row proxy dropdown - Bulk actions: batch assign to proxy, round-robin even distribute, rule-based assignment modal - Import/export: download JSON assignments, upload with diff preview - Hash routing (zero-dependency), Header navigation links - Backend: 6 new bulk API endpoints + ProxyPool.bulkAssign() - i18n: 35 new translation keys (en + zh) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review issues in proxy assignment feature - Make accountPool required param (was optional but always passed) - Extract isValidProxyId() helper to eliminate duplicated validation - Add resp.ok checks…f878eff -
fix: remove wall-clock timeout from streaming SSE to prevent thinking chain cutoff
icebear0828 +1: Claude Opus 4.6The 60s --max-time timeout was killing curl mid-stream during long reasoning/thinking outputs (30-120+ seconds). Streaming requests now rely on header timeout + AbortSignal for protection instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>ffcac87 -
fix: proxy pool review fixes — security, validation, robustness
icebear0828 +1: Claude Opus 4.6- Skip disabled proxies in healthCheckAll (avoid unnecessary network calls) - Validate proxy URL scheme (only http/https/socks5/socks5h allowed) - Deduplicate proxy URLs on add (return existing ID if URL already exists) - Mask proxy credentials server-side via getAllMasked() (remove client-side maskUrl) - Add try-catch to all use-proxies hook operations (prevent unhandled rejections) - Fix misleading route ordering comment in proxies.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>deae086 -
feat: add proxy pool for per-account proxy routing
icebear0828 +1: Claude Opus 4.6Add proxy pool system allowing different accounts to route through different upstream proxies for IP diversity and risk isolation. Backend: - ProxyPool manager with CRUD, assignment, round-robin, health checks - TlsTransport interface accepts per-request proxyUrl parameter - Both curl-cli and libcurl-ffi transports support per-request proxy - CodexApi forwards proxy through the request chain - REST API at /api/proxies for proxy + assignment management - Periodic health checks (default 5min) via api.ipify.org Frontend: - ProxyPool management section in dashboard - Per-account proxy selector dropdown in AccountCard - Four assignment modes: Global/Direct/Auto(Round-Robin)/Specific Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>4ebb914
-
fix: remove unsupported previous_response_id parameter (#30)
icebear +2: icebear0828, Claude Opus 4.6Unverified signature* fix: remove unsupported previous_response_id parameter (#29) The Codex backend no longer accepts `previous_response_id`. Sending it causes HTTP 400: "Unsupported parameter: previous_response_id". Remove the injection logic in proxy-handler and clean up related dead code across translation functions and session manager. Closes #29 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove dead SessionManager (was only used for previous_response_id) SessionManager, session hashing, and onResponseId session storage were only used to track previous_response_id across turns. Since the backend no longer supports this parameter, the entire session system is dead code. Remove it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: icebear0828 <icebear0828@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>3c0eaf7
-
refactor: extract startServer() + path abstraction for Electron reuse
icebear0828 +1: Claude Opus 4.6- Export startServer(options?) from src/index.ts so Electron can import it - Add src/paths.ts: centralized path getters (getConfigDir, getDataDir, etc.) with setPaths() hook for Electron to redirect to userData - Replace all process.cwd() calls with path getters across src/ - Extract shared/ from web/ (i18n, theme, hooks, utils) for desktop reuse - Add /desktop route in web.ts (serves desktop UI if built, no-op otherwise) CLI behavior is unchanged — all path getters default to process.cwd(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>4a940a5 -
feat: add reasoning/thinking output support for OpenAI and Anthropic routes
icebear0828 +1: Claude Opus 4.6- Always send `summary: "auto"` to Codex API so reasoning summaries are available - Parse `response.reasoning_summary_text.delta/done` SSE events - OpenAI route: emit `reasoning_content` in chunks when client sends `reasoning_effort` - Anthropic route: emit thinking blocks when client sends `thinking.type: enabled/adaptive` - Extract block lifecycle helpers in Anthropic translator to eliminate duplication - Log unknown SSE events via console.debug for discovering new event types Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>7366e72 -
feat: add image input support for all API routes
icebear0828 +1: Claude Opus 4.6Previously all image content was silently discarded by the translation layer. Now image_url (OpenAI), image/base64 (Anthropic), and inlineData (Gemini) content parts are translated to Codex input_image format and passed through to the backend. Closes #25 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>142c9c4
-
fix: handle error SSE events from Codex API instead of silently dropping them
icebear0828 +1: Claude Opus 4.6When the Codex API returns `event: error` or `event: response.failed`, the proxy now surfaces the error message to clients instead of returning empty responses. Also detects non-SSE JSON error bodies from upstream. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>cf0807a
-
feat: dynamic model list fetching from Codex backend (#9)
icebear +2: icebear0828, Claude Opus 4.6Unverified signature* feat: dynamic model list fetching from Codex backend Replace static-only model loading with a hybrid approach: - Static YAML models as fallback baseline - Background fetcher probes /codex/models and /models endpoints - Backend models merge into catalog (backend wins, YAML fills gaps) - Aliases always from YAML (user-customizable) - New /debug/models endpoint for diagnostics - All fetch errors non-fatal — gracefully falls back to static models Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: PR #9 review issues + complete Codex model catalog - Remove dead _backendModelCount variable from model-store - Fix hot-reload wiping backend models: add triggerImmediateRefresh() in model-fetcher, chain it in reloadAllConfigs() - Fix premature "hot-reloaded" log message ordering in config.ts - Gate raw model sample logging to first fetch only in codex-api - Remove dead re-exports from routes/models.ts - Expand getModels() to probe sentinel/chat-requirements + flatten nested …5f8456f
-
feat: OpenCode platform support, vitest tests, changelog & Docker hardening
icebear0828 +1: Claude Opus 4.6- Add opencode.json provider config for OpenCode IDE integration - Add CHANGELOG.md covering all versions (v0.1.0–v0.8.0) - Add vitest with unit tests for account-pool, codex-api, codex-event-extractor - Add request-id middleware to global middleware chain - Harden Dockerfile: non-root user, HEALTHCHECK probe - Simplify /health endpoint to return pool summary only Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>92c5df7 -
feat: native tool calling support across all protocols (#8)
icebear +2: icebear0828, Claude Opus 4.6Unverified signature* feat: native tool calling support across all protocols - Fix 500 errors: wrap c.req.json() in try-catch for all 3 routes, returning proper 400 responses for malformed JSON - Forward tool definitions to Codex API instead of sending tools: [] - Parse function call SSE events (output_item.added, function_call_arguments.delta/done) with item_id→call_id mapping - Translate tool calls back to each protocol's format: OpenAI: streaming tool_calls chunks + finish_reason "tool_calls" Anthropic: tool_use content blocks + stop_reason "tool_use" Gemini: functionCall parts in candidates - Convert tool results from each protocol to native function_call_output input items - New shared module: tool-format.ts for cross-protocol tool conversion Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review issues in native tool calling - Fix Gemini call_id collision: use counter-based IDs with name→callId correlation instead of fragile fc_${name} pattern - Remove dead c…ab2754a
-
feat: add libcurl-impersonate FFI transport for Chrome TLS fingerprint
icebear0828 +1: Claude Opus 4.6Windows lacks curl-impersonate CLI, so use koffi FFI to call libcurl-impersonate DLL directly. Introduces TlsTransport abstraction with auto-selection: FFI on Windows, CLI subprocess on macOS/Linux. All three API protocols (OpenAI/Anthropic/Gemini) verified working. - New: src/tls/transport.ts (interface + singleton factory) - New: src/tls/curl-cli-transport.ts (extracted from codex-api.ts) - New: src/tls/libcurl-ffi-transport.ts (koffi FFI, curl_multi streaming) - New: scripts/poc-libcurl-ffi.ts (POC verification script) - Updated: setup-curl.ts auto-downloads DLL + cacert.pem on Windows - Updated: codex-api.ts uses transport abstraction (~200 lines removed) - Updated: curl-fetch.ts delegates to transport singleton Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>3d01305 -
refactor: architecture audit fixes round 2 (P0-P2)
icebear0828 +1: Claude Opus 4.6- Fix curl subprocess leak on client disconnect (abort signal propagation) - Cap SSE parse buffer at 10MB to prevent memory exhaustion - Fatal error triggers graceful shutdown instead of hard exit - Session hash: 128-bit + JSON.stringify to prevent collisions - Gemini SSE line endings normalized to \n\n - Extract shared translation utils (desktop context, instructions, budgetToEffort) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>d6c3bb0
-
refactor: architecture audit fixes (P0-P2 stability & reliability)
icebear0828 +1: Claude Opus 4.6P0: curl header parse timeout (30s), kill curl on stream disconnect via AbortController, cap error response body at 1MB. P1: acquireLock TTL (5min auto-release), atomic config hot-reload, encapsulate 429 counting in AccountPool, graceful shutdown with server.close() + 5s drain period. P2: send error SSE event on stream failure, structured JSON logger, O(1) session LRU eviction via Map insertion order. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>b1107bc -
refactor: fingerprint alignment, auto-maintenance, Docker deployment
icebear0828 +1: Claude Opus 4.6Phase 1 - Eliminate fingerprint detection gaps: - Dynamic sec-ch-ua from config chromium_version (aligned with TLS profile) - Inject full fingerprint headers into curl-fetch (OAuth/appcast) - Unify Accept-Encoding based on curl-impersonate detection - Optimize header ordering (single sort pass) Phase 2 - Auto-maintenance: - Structured YAML mutation utility (replace regex hacks) - Auto full-update pipeline on new version detection - Config hot-reload after updates - Exponential backoff token refresh (5 attempts + 10m recovery) - Immediate persistence for critical data (tokens, cf_clearance) - Cookie v2 format with expiry timestamps - Graceful shutdown with 10s timeout protection - Usage counters auto-reset on rate limit window rollover Phase 3 - Docker deployment: - Dockerfile, docker-compose.yml, .dockerignore - Deployment docs in README Fixes: - Extract planType from JWT /auth claim (was looking in /profile) - Backfill missing planType on startup for existing accounts - Show full d…347f81b -
refactor: extract shared proxy handler, eliminate route duplication
icebear0828 +1: Claude Opus 4.6- Extract withRetry() to src/utils/retry.ts (was duplicated 3x) - Create shared proxy-handler.ts encapsulating acquire/stream/release lifecycle - Refactor chat.ts, messages.ts, gemini.ts to use shared handler (-264 lines net) - Convert models.ts from default export to named createModelRoutes() - Extract GEMINI_STATUS_MAP to types/gemini.ts (was duplicated in error-handler) - Add SessionManager.destroy() with interval cleanup + unref - Remove dead captureCookies() method from codex-api.ts - Add stack trace logging to error handler - Fix hardcoded localhost in startup message - Use atomic write-to-temp-then-rename for cookie-jar and account-pool - Add warning logs for previously silent catch blocks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>8b777a2 -
feat: auto proxy detection for curl + tighten dashboard layout
icebear0828 +1: Claude Opus 4.6- Add tls.proxy_url config + HTTPS_PROXY env var support - Inject proxy args into both curlPost() and getUsage() curl calls - Explicitly set Accept-Encoding to gzip,deflate for system curl compat - Include setup-curl.ts postinstall script (was gitignored) - Tighten dashboard font sizes and spacing for compact layout - Rename dashboard1.html → dashboard.html, fix web.ts reference Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>d85b21d -
feat: desktop context injection, multi-turn sessions, TLS impersonation & auto-update
icebear0828 +1: Claude Opus 4.6- Inject Codex Desktop system prompt into every request for full feature parity - Add previous_response_id for multi-turn conversation continuity - Add curl-impersonate auto-detection with Chrome TLS fingerprint fallback - Fix appcast XML parsing for element-style Sparkle tags - Remove private maintenance scripts from public repo - Add README with setup and usage instructions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>22a7de1
-
feat: anti-detection hardening — curl TLS, auto-version, jitter & Chromium headers
icebear0828 +1: Claude Opus 4.6- Replace fetch() with curl subprocess for POST /codex/responses to match native TLS fingerprint (Cloudflare evasion) - Auto-apply version updates from Sparkle appcast to config file and runtime - Add timing jitter to all fixed intervals (refresh, retry, backoff, polling) to eliminate automation signatures - Add sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform, sec-fetch-* headers to match Chromium/Electron request fingerprint - Add CookieJar.captureRaw() for Set-Cookie handling from curl responses Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>0d2f54c -
feat: reactive API config, live code examples, quota display & curl fix
icebear0828 +1: Claude Opus 4.6- Dashboard API config fields are now editable (input/select) with live code example updates and localStorage persistence - Model dropdown populated from /v1/models endpoint - Account cards show official rate limit quota with progress bar - Fix curl getUsage() failing on Windows by removing Accept-Encoding header and using --compressed flag instead - Remove .claude/settings.local.json from tracking (contains local keys) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>0fedcca
-
feat: architecture audit — disguise hardening, update automation, robustness
icebear0828 +1: Claude Opus 4.6Disguise hardening (P0): - Enforce HTTP header order from fingerprint.yaml config - Add browser-level headers (Accept-Encoding, Accept-Language) - Add Codex-specific request body fields (tools, previous_response_id) - Protect /debug/fingerprint endpoint (dev/localhost only) Update automation (P0-P1): - Integrate appcast version checker into server process (30-min polling) - Expose update state via /health endpoint - Externalize model catalog to config/models.yaml - Fail fast on critical extraction failures (originator, api_base_url) - Update apply-update.ts to compare models against YAML config Robustness (P0-P1): - Add 5xx retry with exponential backoff in chat route (max 2 retries) - Wrap HTML file reads in try-catch to prevent server crashes - Add config load try-catch with friendly error messages - Log persistence errors instead of silently swallowing - Add retry (1 attempt, 5s delay) for token refresh failures Code hygiene: - Delete leftover WHAM dist files - Mark 6 backward-co…85aec43 -
refactor: remove dead code, extract hardcoded values into config
icebear0828 +1: Claude Opus 4.6- Delete unused AuthManager (src/auth/manager.ts, replaced by AccountPool) - Delete unused ProxyClient + serializePath (src/proxy/client.ts) - Replace hardcoded "Codex Desktop" in chatgpt-oauth.ts with config.client.originator - Extract magic timestamp 1700000000 into MODEL_CREATED_TIMESTAMP constant - Move session TTL and cleanup interval from hardcoded values to config schema - Fix unused `target` variable in models.ts route loop Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>ab21b87 -
Initial commit: Codex Proxy with quota API and Cloudflare bypass
icebear0828 +1: Claude Opus 4.6OpenAI-compatible proxy that translates Chat Completions API to ChatGPT's Codex Responses API. Includes multi-account management, official quota querying via curl subprocess (bypasses Cloudflare TLS fingerprinting), per-account cookie jar, and full API documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>5d0a52f