better-ccflare 3.5.50 → 3.5.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/better-ccflare +0 -0
- package/package.json +56 -56
package/README.md
CHANGED
|
@@ -803,10 +803,11 @@ Inspired by [snipeship/ccflare](https://github.com/snipeship/ccflare) - thanks f
|
|
|
803
803
|
- [@CorentinLumineau](https://github.com/CorentinLumineau) - Plugin agent discovery scanning Claude Code plugin directories for agent definitions alongside the built-in agents path, with manifest parsing, name validation, and dashboard display (PR #197)
|
|
804
804
|
- [@d4rken](https://github.com/d4rken) - fixed dashboard "Active" badge showing the most-recently-used account instead of the one the load balancer would actually pick next — introduced a side-effect-free `peek()` to `LoadBalancingStrategy` (shared `peek-availability.ts` mirrors auto-unpause logic without DB writes), dashboard accounts endpoint stamps `isPrimary: true` on the predicted pick, badge renamed from "Active" to "Primary"; also inlined the account-row controls previously hidden behind a `MoreHorizontal` dropdown into flat action buttons (PR #218); Dashboard account row reorganization into wrap-friendly multi-row layout with overflow kebab menu, restored tooltip context for moved actions, and deduped re-auth condition (PR #204); surfaced API key mutation errors inline in create/delete/toggle dialogs, prevented first-key lockout by disabling admin checkbox during loading with fail-safe isFirstKey guard, and added dismissible error banner for role/toggle failures (PR #205); fixed OAuth account creation silently discarding the priority value by adding a priority column to oauth_sessions and threading it through init → session → callback → account creation, plus replaced the constraint-dropping `CREATE TABLE … AS SELECT` tier-drop rebuild with an explicit schema-preserving migration (PR #206); surfaced update-check failure reason in dashboard tile by including the underlying error message in the backend 500 response body, switching to structured Logger, and displaying the error message under "Check Failed" in the frontend sidebar tile (PR #207); exempted /api/version/check from authentication so the dashboard update tile works correctly when API keys are configured (PR #208); replaced bare error-code list with grouped error UI featuring per-(errorCode, account) dismissal, time-window selector (1h/24h/7d/all) persisted in localStorage, clickable rows opening a details modal with human-readable descriptions and recovery info, and a CTE+window-function backend query joining account rate-limit state (PR #209); added combined-quota "5h Pool" and "7d Pool" metric tiles to the Overview dashboard aggregating per-account utilization across all eligible accounts, with worst-account subline, popover breakdown by contributing/exhausted/excluded/fallback accounts, at-risk projection, and next-reset time (PR #210); fixed SIGTERM race where interval-manager's synchronous process.exit(0) short-circuited async shutdown, dropping in-flight requests and pending DB writes — removed the early exit, added HTTP drain via serverInstance.stop(), reordered shutdown sequence (schedulers → HTTP drain → usage worker → DB flush), and added a 30s watchdog to bound total shutdown time (PR #214); fixed API key copy button silently failing on plain-HTTP hosts by adding a `copyText` helper that prefers the async Clipboard API and falls back to an off-screen textarea + `execCommand` for non-secure contexts, and routed `CopyButton` and the inline copy buttons in `ApiKeysTab` through it so the checkmark animation works in both cases (PR #215); fixed post-processor worker timing out on large databases (multi-GB) by passing `fastMode=true` to skip the redundant PRAGMA integrity_check — the main thread already ran it, and re-running it in the worker blew past the 10s startup deadline causing silent request loss (PR #220); replaced the bare recent-errors string list with a grouped error card featuring per-(errorCode, account) dismissal, time-window selector (1h/24h/7d/all), clickable rows opening a details modal with human-readable descriptions, recovery info, and provider-aware copy for model_fallback_429 that escalates to error only when no other accounts are available (PR #212); redesigned request history row layout into a two-row card that never wraps the top-line summary, switched the list view to the summary-only endpoint to eliminate the ~12MB per-page-load body fetch, and fixed the API key and account filter dropdowns to source options from dedicated endpoints so deleted-but-historical entries remain selectable (PR #221); replaced fs.copyFileSync migration backup with VACUUM INTO + atomic rename via a .partial temp file, producing a defragmented consistent snapshot and ensuring the named backup only appears complete (PR #222); added bounded backup retention — after each migration backup, prunes `.backup.<ts>` files to keep the newest N (default 3, configurable via `BETTER_CCFLARE_MIGRATION_BACKUP_KEEP`, 0 to disable), sorting by name-embedded timestamp to survive rsync/clock-drift, skipping operator-renamed files with non-integer suffixes (PR #223); bumped the post-processor worker startup timeout from 10s to 60s and made it overridable via `CF_WORKER_STARTUP_TIMEOUT_MS`, preventing silent analytics loss on large (multi-GB) databases where the per-handle PRAGMA work blew past the old deadline (PR #224); narrowed the migration backup gate from a 41-condition OR to only the three genuinely irreversible operations (refresh_token NOT NULL rebuild, account_tier drop, oauth_sessions.tier drop), eliminating multi-GB backup copies on every restart when only additive ALTER TABLE ADD COLUMN migrations are pending (PR #225); resolved all 42 error-level Biome diagnostics across dashboard components, hooks, and openai-formats tests — replaced `noNonNullAssertedOptionalChain` patterns in tests with explicit non-null assertions after `toBeDefined()`, converted `forEach` callbacks to `for…of` to fix `useIterableCallbackReturn`, added scoped `biome-ignore` comments with explanations for `noArrayIndexKey` cases where stable ids aren't available, and fixed two a11y errors in ErrorBoundary (SVG role/aria-label/title, button type attribute) (PR #226); replaced the blocking startup `PRAGMA integrity_check` (which froze the event loop for 94s on a 7.6 GiB DB) with a dual-timer background scheduler running `quick_check` every 6h and a full `integrity_check` + `foreign_key_check` every 24h in a dedicated `bun:sqlite` worker, added a sticky-corrupt rule so a quick `ok` cannot mask a full corruption finding, exposed the status via an expanded `/api/storage` response and a new `POST /api/storage/integrity/check` on-demand endpoint, and surfaced it in the Overview dashboard via a `StorageIntegrityCard` and sticky `StorageIntegrityBanner` on corruption — also adds `foreign_key_check` to the full probe since `integrity_check` per SQLite docs does not verify foreign keys (PR #227); fixed Logger.{error,warn,info,debug} silently emitting `{}` for Error instances by normalizing Errors to plain objects with name/message/stack/cause before serialization — affects 159 call sites including the "Failed to intercept/modify request" line in agent-interceptor — and demoted all per-request INFO logs in agent-interceptor and extractSystemPrompt to DEBUG, plus skipped the spurious `~/.claude/.claude/agents` path construction for global CLAUDE.md entries since global agents are already loaded unconditionally by AgentRegistry (PR #228); fixed `LOG_LEVEL=DEBUG` being silently ignored — `LogLevel.DEBUG === 0` is falsy so `getLogLevelFromEnv() || level` fell back to the constructor default (INFO), suppressing all debug output even when explicitly configured; switched to `??` so the env override applies for DEBUG too, and added regression tests covering DEBUG/WARN/ERROR/unset/unknown values plus the `silentConsole` side-effect (PR #229); eliminated the blocking full VACUUM trap — hourly retention cleanup and "Compact now" called `incrementalVacuum` which silently fell back to a live full VACUUM on existing DBs (auto_vacuum≠INCREMENTAL), freezing the proxy for minutes every hour — replaced with: `configureSqlite` and `ensureSchema` set `PRAGMA auto_vacuum = INCREMENTAL` on fresh DBs, new `bootstrapAutoVacuum()` migrates existing mode-0 DBs once at startup before HTTP binds (one-time VACUUM cost, never blocks live traffic), `incrementalVacuum()` now dispatches `PRAGMA incremental_vacuum(N)` to a worker thread on mode-2 DBs or logs and returns otherwise (no destructive fallback), drops hourly N from 200000 to 8000 (~32 MiB) to keep writer-slot hold sub-100ms, adds consecutive-skip escalation after 3 missed ticks, and gates the auto_vacuum PRAGMA on mode=0 only to preserve operator-set FULL mode; also removed the dashboard "Compact now" button/endpoint (same hang trap from a different surface) while retaining `bun run cli --compact` with a writer-lock probe that refuses if a running service holds it; and dropped the stale `fastMode` constructor arg from DatabaseOperations after PR #227's integrity-scheduler rework made it redundant (PR #230); fixed `mmapSize=0` being silently ignored in `configureSqlite` — the `> 0` guard meant the default `mmapSize: 0` fell through to bun:sqlite's built-in mmap default (~15 GiB observed on a 15 GiB DB), causing the OOM-killing VACUUM scenario from #230 to recur; also applied memory-bounded PRAGMAs (`mmap_size=0`, `cache_size=-2000`, `temp_store=FILE`) to the full-VACUUM worker thread opened by `compact()` / `--compact`, which had the same gap (PR #231); bounded AsyncDbWriter retention to stop a post-processor worker memory leak — split the single job queue into separate metadata (count-capped at 2000) and payload (count-capped at 1000, byte-capped at 100 MB) queues, added a MAX_JOBS_PER_TICK=50 / MAX_DRAIN_MS_PER_TICK=250ms dual budget so processQueue yields to the event loop instead of monopolizing it, added round-robin scheduling to prevent starvation between queue types, added canAcceptPayload() preflight to skip serialization under backpressure, and surfaced per-queue lengths, byte budget, oldest-job age, and per-kind drop counters in getHealth() (PR #234); enabled the refresh-usage button for Codex OAuth accounts — sends one minimal `/responses` request (bounded to `max_output_tokens: 1`, `reasoning.effort: "minimal"`) to capture `x-codex-*` rate-limit headers, cancels the response body after the header snapshot, deduplicates concurrent clicks per-account with a shared in-flight promise, and wires the result into the existing usageCache + accounts.rate_limit_reset flow; polling intentionally excluded to avoid burning quota at 90s cadence (PR #219); added "Avg / day" and "Avg / week" burn-rate sub-rows to the Plan Value metric card on the Overview dashboard — computed server-side from fixed 7-day and 30-day windows (filter-independent) with a clamped divisor via `effectiveBurnRateDays` so thin history doesn't inflate averages, plus a generic `subRows` prop on `MetricCard` for the UI (PR #235); routed Anthropic 529 overloaded_error responses through the existing rate-limit failover machinery so configured fallback providers take over during an overload instead of forwarding the 529 directly to the client — `parseRateLimit` treats 529 as rate-limited, parsing Retry-After (delta-seconds and HTTP-date) and x-ratelimit-reset with a `clampResetTime` helper that caps reset times to 24h to prevent hostile headers from permanently disabling an account; mid-stream `SseRateLimitSniffer` fires on `overloaded_error` frames for Anthropic-shape providers (anthropic/claude-oauth) only with a line-anchored regex to prevent false positives; two new `RateLimitReason` values (`upstream_529_overloaded_with_reset` / `upstream_529_overloaded_no_reset`) surface accurate audit attribution in the dashboard; `returnRateLimitedResponseOnExhaustion` forwards the original 529 upstream response to the client when the full account pool is exhausted so callers see the real error instead of a generic 503 (PR #236); fixed auto-refresh scheduler probing manually-paused accounts in an endless loop — the eligibility SQL excluded paused accounts only when `auto_pause_on_overage_enabled=0`, ignoring `pause_reason`, while the auto-resume guard in `sendDummyMessage` only un-pauses accounts where `pause_reason IN (NULL,'overage')`; aligned the SQL eligibility query to a positive allowlist (`paused=0 OR (overage_enabled=1 AND pause_reason IN (NULL,'overage'))`) and tightened `isOveragePaused` in `account-selector.ts` to require the same condition, so manual, failure-threshold, and peak_hours pauses are left completely alone; adds a regression test that re-executes the live scheduler SQL against a seeded in-memory SQLite DB covering all seven pause-reason scenarios (PR #237); replaced flat per-account 429 cooldown with adaptive exponential backoff — `consecutive_rate_limits` counter increments atomically in SQL on each 429 and drives `min(upstream_reset, BASE × 2^(n−1))` cooldown (capped at MAX, default 30s base / 5min max), counter resets after 5min of clean operation or on successful token refresh, all four 429 paths (response-processor, model_fallback_429, all_models_exhausted_429, mid-stream SSE sniffer) route through a single `applyRateLimitCooldown` helper, tunables overridable via `CCFLARE_RATE_LIMIT_BACKOFF_BASE_MS` / `CCFLARE_RATE_LIMIT_BACKOFF_MAX_MS` / `CCFLARE_RATE_LIMIT_RESET_STABILITY_MS` (PR #213)
|
|
805
805
|
- [@zionts](https://github.com/zionts) — fix false "integrity check failed" banner on large DBs; adaptive incremental vacuum; payload retention default reduction ([#259](https://github.com/tombii/better-ccflare/pull/259)); session-affinity load-balancing strategy for per-client sticky routing with least-used spread ([#285](https://github.com/tombii/better-ccflare/pull/285))
|
|
806
|
-
- [@StartupBros](https://github.com/StartupBros) — surface Codex SSE `error`/`response.failed` events to clients instead of fabricating empty success turns ([#274](https://github.com/tombii/better-ccflare/pull/274)); synthesize safe `count_tokens` responses for Codex without upstream calls or OAuth refresh ([#275](https://github.com/tombii/better-ccflare/pull/275)); sanitize Codex tool-call inputs at the Anthropic compatibility boundary ([#277](https://github.com/tombii/better-ccflare/pull/277)); align Codex tool-call semantics so tool loops work correctly with Anthropic-compatible clients ([#278](https://github.com/tombii/better-ccflare/pull/278)); inject a continuation nudge after Codex replays Skill tool results so the model resumes the original request instead of stalling ([#279](https://github.com/tombii/better-ccflare/pull/279)); add native xAI/Grok OAuth provider with token refresh, Grok Build credits polling via gRPC-web, and full dashboard integration ([#281](https://github.com/tombii/better-ccflare/pull/281)); session volume circuit breaker guarding against runaway subagent fan-out — per-session rolling-hour request counting with a warn threshold (default 300/h) and opt-in reject threshold, Anthropic-shaped 429 before account selection burns upstream quota, and Codex Responses session-identity propagation so `/v1/responses` traffic is governed too ([#303](https://github.com/tombii/better-ccflare/pull/303)); opt-in OpenAI `prompt_cache_key` for Codex requests with per-conversation partitioning (session id + instructions + first input item) to keep concurrent subagent conversations from thrashing a single cache key, gated to OpenAI's own hosts so custom/self-hosted OpenAI-compatible endpoints are unaffected ([#306](https://github.com/tombii/better-ccflare/pull/306)); enabled the GPT-5.6 model family (sol/terra/luna) on the Codex provider by advertising codex-cli 0.144.1 in request headers and adding context-window metadata (372k for 5.6, 272k for 5.5, 128k for 5.3-codex-spark) with longest-prefix fallback so dated/suffixed model variants still resolve to their family's window ([#304](https://github.com/tombii/better-ccflare/pull/304)); recover from missing Anthropic `message_stop` frames on native `/v1/messages` streams — after a terminal `message_delta` with a non-null `stop_reason`, a fail-closed SSE observer gives the upstream a 10s grace period before synthesizing the missing terminator, avoiding Claude Code's ~300s stall watchdog as the only recovery path ([#307](https://github.com/tombii/better-ccflare/pull/307)); fix Codex on-demand usage refresh for ChatGPT subscription accounts by omitting `max_output_tokens` on the canonical subscription endpoint (which rejects that field with a 400), while preserving it for custom API-compatible endpoints ([#310](https://github.com/tombii/better-ccflare/pull/310)); track project and agent attribution sources — low-cardinality provenance labels (`header_project`/`path_project`/`heading_project`/`none` and `header_agent`/`prompt_agent`/`none`) showing whether request attribution came from an explicit header, workspace path, prompt heading, or agent detection, with a conservative slug validator that rejects secret/hex/credential/hostname-shaped headings and a source-authority-ranked UPSERT so a lower-confidence re-save can never clobber a higher-confidence attribution ([#313](https://github.com/tombii/better-ccflare/pull/313)); gracefully drain in-flight SSE/proxy responses on shutdown instead of severing them mid-stream — `serverInstance.stop()` refuses new connections while active streams run to completion within a configurable drain budget (`CCFLARE_SHUTDOWN_DRAIN_MS`, default 60s), force-closes whatever remains via tracked stream wrappers (Bun 1.x cannot escalate an earlier graceful `stop()` with `stop(true)`), and awaits stream settlement before usage-collector drain so late finalizers still record usage; deduplicated CLI vs. server signal-handler ownership so the CLI's `process.exit()` no longer races the server's drain ([#314](https://github.com/tombii/better-ccflare/pull/314)); made remote models.dev pricing catalogue fetches single-flight and time-bounded — concurrent cost estimates share one in-flight request, and a stalled catalogue fetch aborts after 10s and falls back to bundled pricing instead of hanging request finalization ([#315](https://github.com/tombii/better-ccflare/pull/315)); hardened UsageCollector's finalization lifecycle — active SSE streams now survive past the old fixed eviction window as long as chunks keep arriving, payload memory is bounded by global active/pending byte budgets with exact-sized typed-array copies, detached finalizers atomically take ownership of request state before their first `await` so capacity eviction or ID reuse can't corrupt a concurrent pricing flow, pricing estimation is deadline-bounded via `CF_PRICING_TIMEOUT_MS`, and `drain()` forces pending pricing futures to zero-cost and loops until finalization work is genuinely quiescent instead of trusting a single `allSettled` snapshot ([#316](https://github.com/tombii/better-ccflare/pull/316)); hardened Codex Anthropic-to-Responses request translation — explicit `tool_choice` mapping (`auto`/`any`/`none`/named tool → Codex equivalents, applied independent of whether `tools` are present) with a `ValidationError` for unknown types or names, `disable_parallel_tool_use` forwarded as `parallel_tool_calls: false`, robust `tool_result` serialization handling non-text/null content and oversized payloads with `is_error` preservation, source-order-preserving message conversion so Codex sees the same block chronology the client sent, and a single-tail Skill continuation nudge that fires exactly once regardless of parallel fan-out ([#317](https://github.com/tombii/better-ccflare/pull/317)); ambient `.d.ts` stubs for the gitignored, build-generated database worker modules so `bun run typecheck` succeeds on a clean checkout before `bun run build` has produced the real files ([#320](https://github.com/tombii/better-ccflare/pull/320)); surface WARN and ERROR log lines on the console in headless `bun start` mode via a module-level `setConsoleLogging` override, fixing a silent-monitoring gap where those lines only reached the log bus/file and never the terminal or journald ([#321](https://github.com/tombii/better-ccflare/pull/321)); extracted the PostgreSQL ADD COLUMN duplicate-column race handling in `runMigrationsPg` into an exported, independently-tested `addColumnTolerant` helper covering the full SQLSTATE `42701` discrimination branch matrix ([#322](https://github.com/tombii/better-ccflare/pull/322)); single-flight recovery probes after a mature rate-limit cooldown expires — once an account has racked up 5+ consecutive 429s, only one process-local request is admitted to probe it within a 2-minute lease window after cooldown expiry, and concurrent requests selecting the same account fall through to the next account instead of stampeding it and re-triggering the same 429 storm, with a bounded self-heal on leaked leases and an eviction cap on the in-memory lease map ([#323](https://github.com/tombii/better-ccflare/pull/323)); fixed Codex response-side protocol fidelity — `response.incomplete` events (and `response.completed` carrying `status: "incomplete"`) now map to the correct Anthropic terminal (`refusal` for `content_filter`, `max_tokens` otherwise) instead of a fabricated `end_turn`/`tool_use`; a broader `CODEX_ERROR_TYPE_BY_CODE` table classifies quota/throttle/policy/subscription error codes to their correct Anthropic error type and HTTP status instead of collapsing them into a generic `api_error`, with a message-regex fallback recognizing context-overflow even without the literal error code; and `normalizeCodexInputUsage` excludes cached tokens from `input_tokens` so cache reads are no longer double-counted against Anthropic's additive usage semantics ([#324](https://github.com/tombii/better-ccflare/pull/324))
|
|
806
|
+
- [@StartupBros](https://github.com/StartupBros) — surface Codex SSE `error`/`response.failed` events to clients instead of fabricating empty success turns ([#274](https://github.com/tombii/better-ccflare/pull/274)); synthesize safe `count_tokens` responses for Codex without upstream calls or OAuth refresh ([#275](https://github.com/tombii/better-ccflare/pull/275)); sanitize Codex tool-call inputs at the Anthropic compatibility boundary ([#277](https://github.com/tombii/better-ccflare/pull/277)); align Codex tool-call semantics so tool loops work correctly with Anthropic-compatible clients ([#278](https://github.com/tombii/better-ccflare/pull/278)); inject a continuation nudge after Codex replays Skill tool results so the model resumes the original request instead of stalling ([#279](https://github.com/tombii/better-ccflare/pull/279)); add native xAI/Grok OAuth provider with token refresh, Grok Build credits polling via gRPC-web, and full dashboard integration ([#281](https://github.com/tombii/better-ccflare/pull/281)); session volume circuit breaker guarding against runaway subagent fan-out — per-session rolling-hour request counting with a warn threshold (default 300/h) and opt-in reject threshold, Anthropic-shaped 429 before account selection burns upstream quota, and Codex Responses session-identity propagation so `/v1/responses` traffic is governed too ([#303](https://github.com/tombii/better-ccflare/pull/303)); opt-in OpenAI `prompt_cache_key` for Codex requests with per-conversation partitioning (session id + instructions + first input item) to keep concurrent subagent conversations from thrashing a single cache key, gated to OpenAI's own hosts so custom/self-hosted OpenAI-compatible endpoints are unaffected ([#306](https://github.com/tombii/better-ccflare/pull/306)); enabled the GPT-5.6 model family (sol/terra/luna) on the Codex provider by advertising codex-cli 0.144.1 in request headers and adding context-window metadata (372k for 5.6, 272k for 5.5, 128k for 5.3-codex-spark) with longest-prefix fallback so dated/suffixed model variants still resolve to their family's window ([#304](https://github.com/tombii/better-ccflare/pull/304)); recover from missing Anthropic `message_stop` frames on native `/v1/messages` streams — after a terminal `message_delta` with a non-null `stop_reason`, a fail-closed SSE observer gives the upstream a 10s grace period before synthesizing the missing terminator, avoiding Claude Code's ~300s stall watchdog as the only recovery path ([#307](https://github.com/tombii/better-ccflare/pull/307)); fix Codex on-demand usage refresh for ChatGPT subscription accounts by omitting `max_output_tokens` on the canonical subscription endpoint (which rejects that field with a 400), while preserving it for custom API-compatible endpoints ([#310](https://github.com/tombii/better-ccflare/pull/310)); track project and agent attribution sources — low-cardinality provenance labels (`header_project`/`path_project`/`heading_project`/`none` and `header_agent`/`prompt_agent`/`none`) showing whether request attribution came from an explicit header, workspace path, prompt heading, or agent detection, with a conservative slug validator that rejects secret/hex/credential/hostname-shaped headings and a source-authority-ranked UPSERT so a lower-confidence re-save can never clobber a higher-confidence attribution ([#313](https://github.com/tombii/better-ccflare/pull/313)); gracefully drain in-flight SSE/proxy responses on shutdown instead of severing them mid-stream — `serverInstance.stop()` refuses new connections while active streams run to completion within a configurable drain budget (`CCFLARE_SHUTDOWN_DRAIN_MS`, default 60s), force-closes whatever remains via tracked stream wrappers (Bun 1.x cannot escalate an earlier graceful `stop()` with `stop(true)`), and awaits stream settlement before usage-collector drain so late finalizers still record usage; deduplicated CLI vs. server signal-handler ownership so the CLI's `process.exit()` no longer races the server's drain ([#314](https://github.com/tombii/better-ccflare/pull/314)); made remote models.dev pricing catalogue fetches single-flight and time-bounded — concurrent cost estimates share one in-flight request, and a stalled catalogue fetch aborts after 10s and falls back to bundled pricing instead of hanging request finalization ([#315](https://github.com/tombii/better-ccflare/pull/315)); hardened UsageCollector's finalization lifecycle — active SSE streams now survive past the old fixed eviction window as long as chunks keep arriving, payload memory is bounded by global active/pending byte budgets with exact-sized typed-array copies, detached finalizers atomically take ownership of request state before their first `await` so capacity eviction or ID reuse can't corrupt a concurrent pricing flow, pricing estimation is deadline-bounded via `CF_PRICING_TIMEOUT_MS`, and `drain()` forces pending pricing futures to zero-cost and loops until finalization work is genuinely quiescent instead of trusting a single `allSettled` snapshot ([#316](https://github.com/tombii/better-ccflare/pull/316)); hardened Codex Anthropic-to-Responses request translation — explicit `tool_choice` mapping (`auto`/`any`/`none`/named tool → Codex equivalents, applied independent of whether `tools` are present) with a `ValidationError` for unknown types or names, `disable_parallel_tool_use` forwarded as `parallel_tool_calls: false`, robust `tool_result` serialization handling non-text/null content and oversized payloads with `is_error` preservation, source-order-preserving message conversion so Codex sees the same block chronology the client sent, and a single-tail Skill continuation nudge that fires exactly once regardless of parallel fan-out ([#317](https://github.com/tombii/better-ccflare/pull/317)); ambient `.d.ts` stubs for the gitignored, build-generated database worker modules so `bun run typecheck` succeeds on a clean checkout before `bun run build` has produced the real files ([#320](https://github.com/tombii/better-ccflare/pull/320)); surface WARN and ERROR log lines on the console in headless `bun start` mode via a module-level `setConsoleLogging` override, fixing a silent-monitoring gap where those lines only reached the log bus/file and never the terminal or journald ([#321](https://github.com/tombii/better-ccflare/pull/321)); extracted the PostgreSQL ADD COLUMN duplicate-column race handling in `runMigrationsPg` into an exported, independently-tested `addColumnTolerant` helper covering the full SQLSTATE `42701` discrimination branch matrix ([#322](https://github.com/tombii/better-ccflare/pull/322)); single-flight recovery probes after a mature rate-limit cooldown expires — once an account has racked up 5+ consecutive 429s, only one process-local request is admitted to probe it within a 2-minute lease window after cooldown expiry, and concurrent requests selecting the same account fall through to the next account instead of stampeding it and re-triggering the same 429 storm, with a bounded self-heal on leaked leases and an eviction cap on the in-memory lease map ([#323](https://github.com/tombii/better-ccflare/pull/323)); fixed Codex response-side protocol fidelity — `response.incomplete` events (and `response.completed` carrying `status: "incomplete"`) now map to the correct Anthropic terminal (`refusal` for `content_filter`, `max_tokens` otherwise) instead of a fabricated `end_turn`/`tool_use`; a broader `CODEX_ERROR_TYPE_BY_CODE` table classifies quota/throttle/policy/subscription error codes to their correct Anthropic error type and HTTP status instead of collapsing them into a generic `api_error`, with a message-regex fallback recognizing context-overflow even without the literal error code; and `normalizeCodexInputUsage` excludes cached tokens from `input_tokens` so cache reads are no longer double-counted against Anthropic's additive usage semantics ([#324](https://github.com/tombii/better-ccflare/pull/324)); `GET /api/sessions/:sessionId/account` for local status-line integrations — a DB-backed lookup of the most recent `requests` row for a client session's `account_used`, reusing `createAccountsListHandler` so the response can never drift from `GET /api/accounts`, with a bounded/sanitized session-id path segment matching the write path's own sanitization and a structurally-exact GET-only auth exemption ([#396](https://github.com/tombii/better-ccflare/pull/396)); broke a `@better-ccflare/types` ↔ `@better-ccflare/core` module-init cycle that crashed on any entry point loading the types barrel first, by exporting `./models`/`./model-mappings` as core subpaths and importing those leaves directly from `types/agent.ts` instead of the core barrel ([#397](https://github.com/tombii/better-ccflare/pull/397)); opt-in xAI cache-native conversation identity — a stable `x-grok-conv-id` header hashed from the client session id, plus sticky per-conversation account affinity so a conversation keeps hitting the same xAI account's cache partition ([#398](https://github.com/tombii/better-ccflare/pull/398))
|
|
807
|
+
- [@goldmedal](https://github.com/goldmedal) — fixed a spent `extra_usage` overage pool wrongly benching an otherwise-routable account with a permanent (no-reset) `usage_exhausted` state — split the admission-gating utilization (`getRepresentativeUtilizationForProvider`, excludes `extra_usage`) from a new ranking-only utilization (`getRankingUtilizationForProvider`, includes it) so `SessionStrategy`'s water-filling sort still sees overage headroom while admission no longer does, aligned reset derivation with the gate window, and fixed the accounts-list status label reusing the same signal ([#405](https://github.com/tombii/better-ccflare/pull/405), fixes [#404](https://github.com/tombii/better-ccflare/issues/404))
|
|
807
808
|
- [@robsonek](https://github.com/robsonek) — Usage History dashboard tab: per-account, per-window usage utilization time series with server-computed exhaustion prediction, persisted via a callback-injected snapshot hook on the existing `/oauth/usage` poll (no new Anthropic API calls), dual SQLite/PostgreSQL migration, retention pruning, and jitter-tolerant window segmentation ([#294](https://github.com/tombii/better-ccflare/pull/294))
|
|
808
809
|
- [@flex-seongmin](https://github.com/flex-seongmin) — Explicit outbound forward-proxy support (`BETTER_CCFLARE_OUTBOUND_PROXY`) for enterprise TLS-intercepting egress gateways — a global Bun fetch wrapper installed at both server and CLI startup so provider requests, OAuth flows, usage polling, and webhooks are all covered without threading a `proxy` option through every call site, with always-exempt loopback destinations (full `127.0.0.0/8` range), caller-supplied `proxy` precedence, strict no-op when unset, and credential-safe logging on both the enable path and invalid-URL rejection path ([#334](https://github.com/tombii/better-ccflare/pull/334))
|
|
809
|
-
- [@CooLowbro](https://github.com/CooLowbro) — Opt-in guard (`CCFLARE_DISABLE_COMBO_SESSION_FALLBACK`) preventing exhausted combo-routed requests from silently falling through into normal `SessionStrategy` routing, so isolated combo pools (e.g. Anthropic-only vs. Codex-only) stay isolated instead of spilling over — applied at both the account-selection prefilter and the post-failover fallback point, with rejected requests still recorded through the usage collector as `combo_session_fallback_disabled` so failure metrics remain visible ([#339](https://github.com/tombii/better-ccflare/pull/339))
|
|
810
|
+
- [@CooLowbro](https://github.com/CooLowbro) — Opt-in guard (`CCFLARE_DISABLE_COMBO_SESSION_FALLBACK`) preventing exhausted combo-routed requests from silently falling through into normal `SessionStrategy` routing, so isolated combo pools (e.g. Anthropic-only vs. Codex-only) stay isolated instead of spilling over — applied at both the account-selection prefilter and the post-failover fallback point, with rejected requests still recorded through the usage collector as `combo_session_fallback_disabled` so failure metrics remain visible ([#339](https://github.com/tombii/better-ccflare/pull/339)); added the Fable model family to the Codex and Qwen default model maps — `CodexProvider.mapModel` had no `fable` branch so the map entry alone would have sat unreachable, with a regression test pinning the fix ([#394](https://github.com/tombii/better-ccflare/pull/394)); let a combo slot forward the client's own model instead of forcing a rewrite — an empty `model` becomes a pure account filter, gated to providers that accept client-supplied Claude model ids (`PROVIDERS_ACCEPTING_CLIENT_MODEL`) and validated on both the create and update routes to close a create-then-empty bypass, with the per-slot capacity filter falling back to the request's model when the slot is passthrough and the dashboard blocking an invalid empty-model slot for incompatible providers ([#395](https://github.com/tombii/better-ccflare/pull/395)); `GET /api/models?provider=<x>` plus a shared dashboard model picker so the combo slot and account-mapping fields stop being free text — for Codex it lists the models ccflare already knows (`CODEX_KNOWN_MODELS`, derived from the context-window table) alongside the OpenAI section of the cached models.dev catalogue marked `reference`, deduplicated by id with the stronger marking winning, degrading to the builtins with a warning (never failing the request) when the catalogue is unavailable, and keeping the two groups visually distinct since catalogue membership proves a model exists but not that the account's plan may call it ([#399](https://github.com/tombii/better-ccflare/pull/399)); replaced the compiled Codex default-model map with defaults derived from each account's own live model listing (`chatgpt.com/backend-api/codex/models`, the same endpoint the Codex CLI reads) — resolution order becomes operator override → this account's listing → another account's listing (labelled `shared`, since two of three real accounts answer 401 on their own listing while serving traffic fine) → the provider's built-in map, with tiering following the listing's own `priority` ordering instead of a hand-maintained table, `visibility: hide` entries (routing aliases, the auto-review model) filtered out, and an editable override surface (Settings → Advanced) kept reachable even before the first successful discovery ([#403](https://github.com/tombii/better-ccflare/pull/403))
|
|
810
811
|
- [@vansh2408](https://github.com/vansh2408) — Fixed Codex-routed requests failing with `400 Invalid JSON schema: regex lookaround is not supported` whenever an MCP tool's input schema used a regex `pattern` with lookaround (e.g. an email-validation tool) — extended the existing OpenAI schema sanitizer (renamed `removeUriFormat` → `sanitizeSchemaForOpenAI`) with a lookaround-detecting rule that safely drops the unsupported `pattern` while preserving the field's `type: "string"`, and applied it to the Codex provider's tool conversion, which also brought the pre-existing `$schema`/`format: "uri"` scrubbing to that path for the first time ([#359](https://github.com/tombii/better-ccflare/pull/359))
|
|
811
812
|
|
|
812
813
|
## Contributing
|
package/dist/better-ccflare
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
2
|
+
"name": "better-ccflare",
|
|
3
|
+
"version": "3.5.52",
|
|
4
|
+
"description": "Load balancer proxy for Claude API with intelligent distribution across multiple OAuth accounts to avoid rate limiting",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/tombii/better-ccflare.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/tombii/better-ccflare#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/tombii/better-ccflare/issues"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"better-ccflare": "dist/better-ccflare"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "bun run src/main.ts",
|
|
20
|
+
"build": "case \"$CCFLARE_BUILD_SUFFIX\" in \"\"|*[!a-zA-Z0-9._-]*) if [ -n \"$CCFLARE_BUILD_SUFFIX\" ]; then echo 'CCFLARE_BUILD_SUFFIX must only contain alphanumeric characters, dots, underscores, and hyphens' >&2; exit 1; fi ;; esac && mkdir -p dist && bun -e \"const fs=require('fs'); if (!fs.existsSync('../../packages/database/src/inline-vacuum-worker.ts')) { fs.writeFileSync('../../packages/database/src/inline-vacuum-worker.ts', 'export const EMBEDDED_VACUUM_WORKER_CODE = \\\"\\\";'); }\" && bun -e \"const fs=require('fs'); if (!fs.existsSync('../../packages/database/src/inline-incremental-vacuum-worker.ts')) { fs.writeFileSync('../../packages/database/src/inline-incremental-vacuum-worker.ts', 'export const EMBEDDED_INCREMENTAL_VACUUM_WORKER_CODE = \\\"\\\";'); }\" && bun -e \"const fs=require('fs'); if (!fs.existsSync('../../packages/database/src/inline-integrity-check-worker.ts')) { fs.writeFileSync('../../packages/database/src/inline-integrity-check-worker.ts', 'export const EMBEDDED_INTEGRITY_CHECK_WORKER_CODE = \\\"\\\";'); }\" && bun build ../../packages/database/src/vacuum-worker.ts --outfile dist/vacuum-worker.js --target=bun --minify && bun -e \"const fs=require('fs'); const code=fs.readFileSync('dist/vacuum-worker.js','utf8'); const encoded=Buffer.from(code).toString('base64'); fs.writeFileSync('../../packages/database/src/inline-vacuum-worker.ts', 'export const EMBEDDED_VACUUM_WORKER_CODE = \\\"' + encoded + '\\\";');\" && bun build ../../packages/database/src/incremental-vacuum-worker.ts --outfile dist/incremental-vacuum-worker.js --target=bun --minify && bun -e \"const fs=require('fs'); const code=fs.readFileSync('dist/incremental-vacuum-worker.js','utf8'); const encoded=Buffer.from(code).toString('base64'); fs.writeFileSync('../../packages/database/src/inline-incremental-vacuum-worker.ts', 'export const EMBEDDED_INCREMENTAL_VACUUM_WORKER_CODE = \\\"' + encoded + '\\\";');\" && bun build ../../packages/database/src/integrity-check-worker.ts --outfile dist/integrity-check-worker.js --target=bun --minify && bun -e \"const fs=require('fs'); const code=fs.readFileSync('dist/integrity-check-worker.js','utf8'); const encoded=Buffer.from(code).toString('base64'); fs.writeFileSync('../../packages/database/src/inline-integrity-check-worker.ts', 'export const EMBEDDED_INTEGRITY_CHECK_WORKER_CODE = \\\"' + encoded + '\\\";');\" && rm -f dist/better-ccflare && bun build src/main.ts --compile --outfile dist/better-ccflare --target=bun --minify --define __BETTER_CCFLARE_VERSION__='\\\"'$(node -p \"require('./package.json').version\")$([ -n \"$CCFLARE_BUILD_SUFFIX\" ] && printf \"+%s\" \"$CCFLARE_BUILD_SUFFIX\")'\\\"' && rm -f dist/vacuum-worker.js dist/incremental-vacuum-worker.js dist/integrity-check-worker.js",
|
|
21
|
+
"build:multi": "bun run build-multi-arch.ts",
|
|
22
|
+
"build:linux-amd64": "bun run build && bun build src/main.ts --compile --outfile dist/better-ccflare-linux-amd64 --target=bun-linux-amd64 --minify --define __BETTER_CCFLARE_VERSION__='\\\"'$(node -p \"require('./package.json').version\")$([ -n \"$CCFLARE_BUILD_SUFFIX\" ] && printf \"+%s\" \"$CCFLARE_BUILD_SUFFIX\")'\\\"'",
|
|
23
|
+
"build:linux-arm64": "bun run build && bun build src/main.ts --compile --outfile dist/better-ccflare-linux-arm64 --target=bun-linux-arm64 --minify --define __BETTER_CCFLARE_VERSION__='\\\"'$(node -p \"require('./package.json').version\")$([ -n \"$CCFLARE_BUILD_SUFFIX\" ] && printf \"+%s\" \"$CCFLARE_BUILD_SUFFIX\")'\\\"'",
|
|
24
|
+
"build:macos-x86_64": "bun run build && bun build src/main.ts --compile --outfile dist/better-ccflare-macos-x86_64 --target=bun-darwin-x64 --minify --define __BETTER_CCFLARE_VERSION__='\\\"'$(node -p \"require('./package.json').version\")$([ -n \"$CCFLARE_BUILD_SUFFIX\" ] && printf \"+%s\" \"$CCFLARE_BUILD_SUFFIX\")'\\\"'",
|
|
25
|
+
"build:macos-arm64": "bun run build && bun build src/main.ts --compile --outfile dist/better-ccflare-macos-arm64 --target=bun-darwin-arm64 --minify --define __BETTER_CCFLARE_VERSION__='\\\"'$(node -p \"require('./package.json').version\")$([ -n \"$CCFLARE_BUILD_SUFFIX\" ] && printf \"+%s\" \"$CCFLARE_BUILD_SUFFIX\")'\\\"'",
|
|
26
|
+
"build:windows-x64": "bun run build && bun build src/main.ts --compile --outfile dist/better-ccflare-windows-x64.exe --target=bun-windows-x64 --minify --define __BETTER_CCFLARE_VERSION__='\\\"'$(node -p \"require('./package.json').version\")$([ -n \"$CCFLARE_BUILD_SUFFIX\" ] && printf \"+%s\" \"$CCFLARE_BUILD_SUFFIX\")'\\\"'",
|
|
27
|
+
"prepublishOnly": "cp ../../README.md . && bun run --cwd ../.. build:dashboard && bun run build && jq 'del(.dependencies[\"@better-ccflare/cli-commands\", \"@better-ccflare/config\", \"@better-ccflare/core\", \"@better-ccflare/core-di\", \"@better-ccflare/database\", \"@better-ccflare/logger\", \"@better-ccflare/server\"])' package.json > package.json.tmp && mv package.json.tmp package.json",
|
|
28
|
+
"postpublish": "chmod +x dist/better-ccflare"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.0.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"dotenv": "^17.4.0"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist/better-ccflare",
|
|
41
|
+
"README.md"
|
|
42
|
+
],
|
|
43
|
+
"keywords": [
|
|
44
|
+
"claude",
|
|
45
|
+
"anthropic",
|
|
46
|
+
"api",
|
|
47
|
+
"proxy",
|
|
48
|
+
"load-balancer",
|
|
49
|
+
"rate-limiting",
|
|
50
|
+
"oauth",
|
|
51
|
+
"cli",
|
|
52
|
+
"analytics",
|
|
53
|
+
"monitoring"
|
|
54
|
+
],
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
58
|
}
|