better-ccflare 3.5.46 → 3.5.47
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 +2 -2
- package/dist/better-ccflare +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -792,12 +792,12 @@ Inspired by [snipeship/ccflare](https://github.com/snipeship/ccflare) - thanks f
|
|
|
792
792
|
- [@jw409](https://github.com/jw409) - Fixed OAuth account addition in WSL2 and compiled binaries by replacing unreliable prompt() with readline; systemd deployment guide, BUN_JSC_* crash loop analysis, and preflight environment validator (PR #106)
|
|
793
793
|
- [@materemias](https://github.com/materemias) - Testing and validation of Vertex AI provider implementation, thorough debugging of OAuth API key authentication (issue #54), requesting and validating AWS Bedrock support (issue #49), extensive testing of new releases and features; fix request details modal hydration race where payload row committed after parent request row, adding lazy re-fetch via `/api/requests/payload/:id` with 404 fallback (PR #186)
|
|
794
794
|
- [@tqtensor](https://github.com/tqtensor) - Comprehensive memory leak fix preventing OOM kills with smart chunk capping, memory monitoring, and optimized cleanup (PR #67)
|
|
795
|
-
- [@lunetics](https://github.com/lunetics) - Force-reset rate limit feature allowing manual clearing of stale rate-limit locks via API, CLI, and dashboard with immediate usage polling (PR #68), OOM kill prevention with periodic data retention cleanup, 3-day default retention, and time-scoped stats queries (PR #70), model registry sync removing retired models and adding sonnet-4.6 CLI shortcut (PR #71); migrated Anthropic usage handling to the new `limits[]` payload shape — per-account display (session/weekly/per-model rows with severity and overage-credit meter), account ranking/utilization and capacity-guard reads, and model-aware proactive throttling so a per-model weekly cap (e.g. Fable) only throttles matching-model requests instead of the whole account, with flat-window fallback throughout ([#296](https://github.com/tombii/better-ccflare/pull/296)); surfaced usage-window exhaustion that was invisible to both `/health` and the accounts dashboard during the 2026-07-09 incident — added a `usage_exhausted` pool counter (subset of `routable`), extracted `rateLimitStatus` display precedence into a tested pure helper (usage exhaustion > unified header snapshot > legacy cooldown > OK), and a shared staleness guard so a known-past window reset is never reported as exhausted on either surface, with characterization tests pinning the incident's account-wide-bench-to-pool-flap chain ([#299](https://github.com/tombii/better-ccflare/pull/299)); fixed the agent interceptor silently rewriting subagent request models to stale/retired ids via a hardcoded alias map, and unconditionally overriding explicit per-call model choices — rewrites now fire only on an explicit DB preference, with a live Anthropic model catalog (`GET /v1/models`, weekly refresh, console-API accounts only by default to avoid OAuth ban risk, passive capture from pass-through traffic) vetting rewrite targets and backing dashboard model dropdowns/validation; added `original_model`/`applied_model` request-row provenance and an `x-better-ccflare-model-rewrite` response header, post-rewrite-aware account/combo routing, agent model provenance (`modelSource`, revert-to-inherit) with a redesigned two-mechanism dashboard edit dialog separating the agent-file default from the runtime preference override ([#300](https://github.com/tombii/better-ccflare/pull/300)); persisted `requires_reauth` detection for dead OAuth refresh tokens after a real incident where two accounts' tokens silently failed for ~43h undetected — preserves the machine-readable OAuth error code ahead of `error_description` in the Anthropic/Codex/xAI providers (previously discarded, so detection could never match), routing exclusion at the single `isAccountAvailable` choke point plus all proactive refresh paths, clear-sites across every reauth flow (CLI, dashboard, Qwen/Codex HTTP handlers), a new `auth_failure` webhook/SSE alert with cooldown dedup, and a dashboard "Needs authentication" badge, backed by ~120 new tests ([#311](https://github.com/tombii/better-ccflare/pull/311)); added opt-in model-scoped capacity routing (`MODEL_SCOPED_CAPACITY_ROUTING=exhausted`) that filters accounts whose per-model-family weekly cap is exhausted, combining telemetry-based `weekly_scoped` usage windows with a reactive 5-minute in-memory negative cache fed by observed `out_of_credits` 429 responses (provenance-tagged `recent_upstream_rejection` vs. `telemetry_confirmed` for honest client messaging), plus a new `session-drain-soonest` load-balancing strategy (`LB_STRATEGY=session-drain-soonest`) that preserves session-stickiness while ranking fresh-selection candidates by earliest future `weekly_all` reset to consume capacity before it expires ([#325](https://github.com/tombii/better-ccflare/pull/325)); Routing settings card on the Settings tab wiring the strategy selector and model-scoped capacity routing toggle from PR #325 to the dashboard — a shared `resolveEnvFileSetting<T>()` resolver backs `getStrategySource()`/`getModelScopedCapacityRoutingSource()` so the env>file>default precedence can't drift between the getter and its source twin, both controls show an env-locked badge and disable themselves when overridden by `LB_STRATEGY`/`MODEL_SCOPED_CAPACITY_ROUTING`, an active-but-unlisted strategy renders as a disabled "(current)" item instead of a blank Select that could be silently overwritten, and a strategy switch now also invalidates the accounts query so the "Primary" badge updates immediately instead of drifting for up to 60s ([#329](https://github.com/tombii/better-ccflare/pull/329)); guarded logger `JSON.stringify` call sites (`Logger.formatMessage`, `LogFileWriter.write`) against circular references and `BigInt` values that previously threw uncaught out of routine `log.debug/info/warn/error` calls into caller code, with a sanitized fallback that preserves `ts`/`level`/`msg` and byte-identical output for normal serializable events ([#330](https://github.com/tombii/better-ccflare/pull/330)); fixed a false auto-pause chain where a legitimately usage-throttled account's `five_hour` window reset triggered an auto-refresh probe, but `applyUsageThrottling` had no exemption for internal synthetic probes and returned better-ccflare's own 529 back to the scheduler, which counted it as a genuine endpoint failure toward `FAILURE_THRESHOLD` — exempted requests carrying the pre-existing `x-better-ccflare-auto-refresh`/`x-better-ccflare-keepalive` loop-prevention headers from usage throttling entirely, and stopped counting a 529 probe response toward the consecutive-failure pause threshold as defense in depth, with TDD regression tests proving both failure modes pre-fix ([#331](https://github.com/tombii/better-ccflare/pull/331)); `docs/routing-architecture.md`, a technical reference documenting the account-selection pipeline end-to-end — the four load-balancing strategies, usage-throttling's pacing-line model, model-scoped capacity routing, and auto-fallback — each with a plain-English explanation and a Mermaid flowchart cross-checked against source, including accuracy fixes from two rounds of Greptile review (combo-routed requests skip model-scoped weekly windows; `out_of_credits`/keepalive 429s don't apply an account cooldown; a request with no `clientSessionId` isn't recorded as a sticky session-affinity mapping; `checkForAutoFallbackAccounts` and `wouldAutoUnpause` are two distinct eligibility rules, not one shared rule) plus a correction for the synthetic-probe throttle exemption landed by PR #331 after this doc branched ([#332](https://github.com/tombii/better-ccflare/pull/332)); fixed the `build:linux-*`/`build:macos-*`/`build:windows-x64` platform-specific build scripts omitting the `--define __BETTER_CCFLARE_VERSION__` flag that the plain `build` script sets, causing locally-built platform binaries to misreport their version via the unrelated `CLAUDE_CLI_VERSION` fallback in `getVersionSync()` ([#333](https://github.com/tombii/better-ccflare/pull/333)); closed a security hole where the `x-better-ccflare-auto-refresh`/`x-better-ccflare-keepalive` internal-probe markers were trusted from any client, letting a forged marker bypass usage-throttling, suppress per-account rate-limit cooldowns, and defeat the 529 in-place retry safety net — gated all probe-marker read-sites behind a process-local secret (`crypto.randomUUID()`, minted at startup and threaded through `ProxyContext`) that only the two schedulers' own loopback probes carry, with the secret stripped from request history, cache-body storage, and the outbound provider request path so it can never leak ([#336](https://github.com/tombii/better-ccflare/pull/336)); separated transient 529-overload cooldown handling from the 429 rate-limit ramp after a production incident where reset-less 529 bursts drove healthy accounts to a 5-minute cooldown ceiling purely on server-side overload signals — a reset-less 529 now takes a fixed short cooldown (`CCFLARE_OVERLOAD_COOLDOWN_MS`) instead of the exponential ramp, a 529-with-reset caps window-scale reset values at overload scale rather than the 429 ceiling, neither 529 variant advances the `consecutive_rate_limits` streak, the single-flight recovery-probe gate arms on the persisted overload reason so an all-529 account still gets a single-flight probe, an all-suppressed candidate pool retries the highest-priority account ungated instead of failing with a hard 503, and the capacity-exhaustion response distinguishes real weekly exhaustion from an eligible account merely cooling down ([#342](https://github.com/tombii/better-ccflare/pull/342)); diagnosed the root cause of orphaned `Response.clone()` tee branches on the 529-overload check path (issue #354) with a comprehensive test suite covering every affected call site (PR #355) — landed as the two still-open header-only `parseRateLimit` clone removals and disposal of the terminal-529 check clone via the existing `drainBody`/`cancelDiscardedResponseBody` helpers (consistent with the Bun no-op-leak finding from PR #353), plus the analogous unreleased `parseUsage` streaming clone in `updateAccountMetadata` ([#355](https://github.com/tombii/better-ccflare/pull/355)); found the same orphaned-tee defect class surviving at the sites #355 deliberately left alone — three body-inspecting error classifiers (`isModelUnavailableError`, `isInvalidThinkingSignatureError`, `isCacheControlRejectionError`) cloned before their content-type gate and stranded that copy on every non-JSON body (ordinary for providers like Qwen), and `OpenAICompatibleProvider.processResponse` had the inversion — its *incoming* response was the orphan, on every successful JSON reply from every openai-compatible provider — fixed by cloning only after the gate and cancelling the discarded original once conversion succeeds, with 9 regression tests pinning both the disposal and the unchanged classifications ([#358](https://github.com/tombii/better-ccflare/pull/358)); fixed `makeProxyRequest` replacing any caller-supplied abort signal with its own timeout-only controller that becomes unreachable once headers arrive, so a client disconnect mid-stream (idle watchdog, Ctrl-C, dropped network) never aborted the upstream fetch — `reader.cancel()` measured as a no-op on Bun, only `abort()` closes the socket, leaking one upstream connection and burning quota for as long as the provider held it open — combined the caller's signal with the header-phase timeout via `AbortSignal.any`, routed every upstream call through one `forwardUpstream` helper that passes the signal explicitly (since provider body transforms rebuild the Request from its URL and drop any signal it carried), and taught the top-level catch to distinguish a client-disconnect `AbortError` from a genuine account failure — returning 499 immediately instead of walking every remaining account and fallback route for a request nobody is listening to ([#360](https://github.com/tombii/better-ccflare/pull/360)); fixed the request-payload endpoint reporting "Request not found" whenever a payload was merely absent — payloads are legitimately missing when capture is disabled or when the usage collector releases long-running requests' payloads after `REQUEST_PAYLOAD_RETENTION_MS` to bound memory, a distinction that cost real debugging time chasing a 340-second request that was in the table the whole time — the 404 now explains a payload isn't stored and why one can legitimately be absent, with the status and JSON shape unchanged ([#362](https://github.com/tombii/better-ccflare/pull/362)); persisted the client session id (body `metadata.user_id`) with each request row after a stream-stall investigation misattributed 43 requests from a locally spawned subagent to a user's test run — the session id was already used for session-affinity routing but never stored, so a session's own subagents were indistinguishable from the session itself in request history; threaded it through the usage collector and all `forwardToClient`/synthetic-failure `saveRequest` call sites with preserve-first `COALESCE` semantics so error-path re-saves can't blank out attribution the main path recorded, sanitized on write (control-character strip, 200-char cap, mirroring `sanitizeProjectName`) since the value was previously only routing state and had no bound on stored length, and fixed the shared `toRequest`/`toRequestResponse` mappers that were silently dropping the field for any caller besides the hand-built HTTP handler — hardened through two rounds of independent adversarial review (Codex, Kimi) that caught a missing session id on 4 of 6 failure-recording paths and the mapper gap, with 9 new tests plus the full database/proxy suite green ([#363](https://github.com/tombii/better-ccflare/pull/363)); surfaced `stream_terminal_state` through `/api/requests` and the live dashboard summary after a day lost diagnosing four stalled requests that all read as healthy `statusCode: 200` — the column was already persisted but no route read it back, so a stream that died mid-content, was cancelled by the client, or was recovered was indistinguishable from a clean response; both write surfaces (REST read path and the live SSE summary) narrow the unconstrained TEXT column through a shared guard instead of an unchecked cast, an unrecognized value surfaces as `"unknown"` rather than collapsing into the same `undefined` a non-streaming request produces, and the narrowing helpers ship from a new cycle-free `@better-ccflare/types/request` subpath since evaluating the package barrel first trips a pre-existing types↔core import cycle — hardened through three rounds of independent adversarial review (Codex, Kimi, Gemini) that caught the unchecked cast, the unknown-state conflation, and an unhardened live-summary path ([#364](https://github.com/tombii/better-ccflare/pull/364))
|
|
795
|
+
- [@lunetics](https://github.com/lunetics) - Force-reset rate limit feature allowing manual clearing of stale rate-limit locks via API, CLI, and dashboard with immediate usage polling (PR #68), OOM kill prevention with periodic data retention cleanup, 3-day default retention, and time-scoped stats queries (PR #70), model registry sync removing retired models and adding sonnet-4.6 CLI shortcut (PR #71); migrated Anthropic usage handling to the new `limits[]` payload shape — per-account display (session/weekly/per-model rows with severity and overage-credit meter), account ranking/utilization and capacity-guard reads, and model-aware proactive throttling so a per-model weekly cap (e.g. Fable) only throttles matching-model requests instead of the whole account, with flat-window fallback throughout ([#296](https://github.com/tombii/better-ccflare/pull/296)); surfaced usage-window exhaustion that was invisible to both `/health` and the accounts dashboard during the 2026-07-09 incident — added a `usage_exhausted` pool counter (subset of `routable`), extracted `rateLimitStatus` display precedence into a tested pure helper (usage exhaustion > unified header snapshot > legacy cooldown > OK), and a shared staleness guard so a known-past window reset is never reported as exhausted on either surface, with characterization tests pinning the incident's account-wide-bench-to-pool-flap chain ([#299](https://github.com/tombii/better-ccflare/pull/299)); fixed the agent interceptor silently rewriting subagent request models to stale/retired ids via a hardcoded alias map, and unconditionally overriding explicit per-call model choices — rewrites now fire only on an explicit DB preference, with a live Anthropic model catalog (`GET /v1/models`, weekly refresh, console-API accounts only by default to avoid OAuth ban risk, passive capture from pass-through traffic) vetting rewrite targets and backing dashboard model dropdowns/validation; added `original_model`/`applied_model` request-row provenance and an `x-better-ccflare-model-rewrite` response header, post-rewrite-aware account/combo routing, agent model provenance (`modelSource`, revert-to-inherit) with a redesigned two-mechanism dashboard edit dialog separating the agent-file default from the runtime preference override ([#300](https://github.com/tombii/better-ccflare/pull/300)); persisted `requires_reauth` detection for dead OAuth refresh tokens after a real incident where two accounts' tokens silently failed for ~43h undetected — preserves the machine-readable OAuth error code ahead of `error_description` in the Anthropic/Codex/xAI providers (previously discarded, so detection could never match), routing exclusion at the single `isAccountAvailable` choke point plus all proactive refresh paths, clear-sites across every reauth flow (CLI, dashboard, Qwen/Codex HTTP handlers), a new `auth_failure` webhook/SSE alert with cooldown dedup, and a dashboard "Needs authentication" badge, backed by ~120 new tests ([#311](https://github.com/tombii/better-ccflare/pull/311)); added opt-in model-scoped capacity routing (`MODEL_SCOPED_CAPACITY_ROUTING=exhausted`) that filters accounts whose per-model-family weekly cap is exhausted, combining telemetry-based `weekly_scoped` usage windows with a reactive 5-minute in-memory negative cache fed by observed `out_of_credits` 429 responses (provenance-tagged `recent_upstream_rejection` vs. `telemetry_confirmed` for honest client messaging), plus a new `session-drain-soonest` load-balancing strategy (`LB_STRATEGY=session-drain-soonest`) that preserves session-stickiness while ranking fresh-selection candidates by earliest future `weekly_all` reset to consume capacity before it expires ([#325](https://github.com/tombii/better-ccflare/pull/325)); Routing settings card on the Settings tab wiring the strategy selector and model-scoped capacity routing toggle from PR #325 to the dashboard — a shared `resolveEnvFileSetting<T>()` resolver backs `getStrategySource()`/`getModelScopedCapacityRoutingSource()` so the env>file>default precedence can't drift between the getter and its source twin, both controls show an env-locked badge and disable themselves when overridden by `LB_STRATEGY`/`MODEL_SCOPED_CAPACITY_ROUTING`, an active-but-unlisted strategy renders as a disabled "(current)" item instead of a blank Select that could be silently overwritten, and a strategy switch now also invalidates the accounts query so the "Primary" badge updates immediately instead of drifting for up to 60s ([#329](https://github.com/tombii/better-ccflare/pull/329)); guarded logger `JSON.stringify` call sites (`Logger.formatMessage`, `LogFileWriter.write`) against circular references and `BigInt` values that previously threw uncaught out of routine `log.debug/info/warn/error` calls into caller code, with a sanitized fallback that preserves `ts`/`level`/`msg` and byte-identical output for normal serializable events ([#330](https://github.com/tombii/better-ccflare/pull/330)); fixed a false auto-pause chain where a legitimately usage-throttled account's `five_hour` window reset triggered an auto-refresh probe, but `applyUsageThrottling` had no exemption for internal synthetic probes and returned better-ccflare's own 529 back to the scheduler, which counted it as a genuine endpoint failure toward `FAILURE_THRESHOLD` — exempted requests carrying the pre-existing `x-better-ccflare-auto-refresh`/`x-better-ccflare-keepalive` loop-prevention headers from usage throttling entirely, and stopped counting a 529 probe response toward the consecutive-failure pause threshold as defense in depth, with TDD regression tests proving both failure modes pre-fix ([#331](https://github.com/tombii/better-ccflare/pull/331)); `docs/routing-architecture.md`, a technical reference documenting the account-selection pipeline end-to-end — the four load-balancing strategies, usage-throttling's pacing-line model, model-scoped capacity routing, and auto-fallback — each with a plain-English explanation and a Mermaid flowchart cross-checked against source, including accuracy fixes from two rounds of Greptile review (combo-routed requests skip model-scoped weekly windows; `out_of_credits`/keepalive 429s don't apply an account cooldown; a request with no `clientSessionId` isn't recorded as a sticky session-affinity mapping; `checkForAutoFallbackAccounts` and `wouldAutoUnpause` are two distinct eligibility rules, not one shared rule) plus a correction for the synthetic-probe throttle exemption landed by PR #331 after this doc branched ([#332](https://github.com/tombii/better-ccflare/pull/332)); fixed the `build:linux-*`/`build:macos-*`/`build:windows-x64` platform-specific build scripts omitting the `--define __BETTER_CCFLARE_VERSION__` flag that the plain `build` script sets, causing locally-built platform binaries to misreport their version via the unrelated `CLAUDE_CLI_VERSION` fallback in `getVersionSync()` ([#333](https://github.com/tombii/better-ccflare/pull/333)); closed a security hole where the `x-better-ccflare-auto-refresh`/`x-better-ccflare-keepalive` internal-probe markers were trusted from any client, letting a forged marker bypass usage-throttling, suppress per-account rate-limit cooldowns, and defeat the 529 in-place retry safety net — gated all probe-marker read-sites behind a process-local secret (`crypto.randomUUID()`, minted at startup and threaded through `ProxyContext`) that only the two schedulers' own loopback probes carry, with the secret stripped from request history, cache-body storage, and the outbound provider request path so it can never leak ([#336](https://github.com/tombii/better-ccflare/pull/336)); separated transient 529-overload cooldown handling from the 429 rate-limit ramp after a production incident where reset-less 529 bursts drove healthy accounts to a 5-minute cooldown ceiling purely on server-side overload signals — a reset-less 529 now takes a fixed short cooldown (`CCFLARE_OVERLOAD_COOLDOWN_MS`) instead of the exponential ramp, a 529-with-reset caps window-scale reset values at overload scale rather than the 429 ceiling, neither 529 variant advances the `consecutive_rate_limits` streak, the single-flight recovery-probe gate arms on the persisted overload reason so an all-529 account still gets a single-flight probe, an all-suppressed candidate pool retries the highest-priority account ungated instead of failing with a hard 503, and the capacity-exhaustion response distinguishes real weekly exhaustion from an eligible account merely cooling down ([#342](https://github.com/tombii/better-ccflare/pull/342)); diagnosed the root cause of orphaned `Response.clone()` tee branches on the 529-overload check path (issue #354) with a comprehensive test suite covering every affected call site (PR #355) — landed as the two still-open header-only `parseRateLimit` clone removals and disposal of the terminal-529 check clone via the existing `drainBody`/`cancelDiscardedResponseBody` helpers (consistent with the Bun no-op-leak finding from PR #353), plus the analogous unreleased `parseUsage` streaming clone in `updateAccountMetadata` ([#355](https://github.com/tombii/better-ccflare/pull/355)); found the same orphaned-tee defect class surviving at the sites #355 deliberately left alone — three body-inspecting error classifiers (`isModelUnavailableError`, `isInvalidThinkingSignatureError`, `isCacheControlRejectionError`) cloned before their content-type gate and stranded that copy on every non-JSON body (ordinary for providers like Qwen), and `OpenAICompatibleProvider.processResponse` had the inversion — its *incoming* response was the orphan, on every successful JSON reply from every openai-compatible provider — fixed by cloning only after the gate and cancelling the discarded original once conversion succeeds, with 9 regression tests pinning both the disposal and the unchanged classifications ([#358](https://github.com/tombii/better-ccflare/pull/358)); fixed `makeProxyRequest` replacing any caller-supplied abort signal with its own timeout-only controller that becomes unreachable once headers arrive, so a client disconnect mid-stream (idle watchdog, Ctrl-C, dropped network) never aborted the upstream fetch — `reader.cancel()` measured as a no-op on Bun, only `abort()` closes the socket, leaking one upstream connection and burning quota for as long as the provider held it open — combined the caller's signal with the header-phase timeout via `AbortSignal.any`, routed every upstream call through one `forwardUpstream` helper that passes the signal explicitly (since provider body transforms rebuild the Request from its URL and drop any signal it carried), and taught the top-level catch to distinguish a client-disconnect `AbortError` from a genuine account failure — returning 499 immediately instead of walking every remaining account and fallback route for a request nobody is listening to ([#360](https://github.com/tombii/better-ccflare/pull/360)); fixed the request-payload endpoint reporting "Request not found" whenever a payload was merely absent — payloads are legitimately missing when capture is disabled or when the usage collector releases long-running requests' payloads after `REQUEST_PAYLOAD_RETENTION_MS` to bound memory, a distinction that cost real debugging time chasing a 340-second request that was in the table the whole time — the 404 now explains a payload isn't stored and why one can legitimately be absent, with the status and JSON shape unchanged ([#362](https://github.com/tombii/better-ccflare/pull/362)); persisted the client session id (body `metadata.user_id`) with each request row after a stream-stall investigation misattributed 43 requests from a locally spawned subagent to a user's test run — the session id was already used for session-affinity routing but never stored, so a session's own subagents were indistinguishable from the session itself in request history; threaded it through the usage collector and all `forwardToClient`/synthetic-failure `saveRequest` call sites with preserve-first `COALESCE` semantics so error-path re-saves can't blank out attribution the main path recorded, sanitized on write (control-character strip, 200-char cap, mirroring `sanitizeProjectName`) since the value was previously only routing state and had no bound on stored length, and fixed the shared `toRequest`/`toRequestResponse` mappers that were silently dropping the field for any caller besides the hand-built HTTP handler — hardened through two rounds of independent adversarial review (Codex, Kimi) that caught a missing session id on 4 of 6 failure-recording paths and the mapper gap, with 9 new tests plus the full database/proxy suite green ([#363](https://github.com/tombii/better-ccflare/pull/363)); surfaced `stream_terminal_state` through `/api/requests` and the live dashboard summary after a day lost diagnosing four stalled requests that all read as healthy `statusCode: 200` — the column was already persisted but no route read it back, so a stream that died mid-content, was cancelled by the client, or was recovered was indistinguishable from a clean response; both write surfaces (REST read path and the live SSE summary) narrow the unconstrained TEXT column through a shared guard instead of an unchecked cast, an unrecognized value surfaces as `"unknown"` rather than collapsing into the same `undefined` a non-streaming request produces, and the narrowing helpers ship from a new cycle-free `@better-ccflare/types/request` subpath since evaluating the package barrel first trips a pre-existing types↔core import cycle — hardened through three rounds of independent adversarial review (Codex, Kimi, Gemini) that caught the unchecked cast, the unknown-state conflation, and an unhardened live-summary path ([#364](https://github.com/tombii/better-ccflare/pull/364)); fixed spurious usage-window resets from upstream `resets_at` timestamp jitter — providers recompute the reset time per response and it jitters by fractions of a second around the same instant, which `notifyWindowReset`'s any-forward-movement check misread as a real rollover on nearly every poll (99.4% of 1564 detections over 48h of production traffic across three Anthropic accounts, max 1.879s jitter vs. genuine rollovers all advancing exactly 5.00h), each false positive spuriously resetting the session-affinity state the `session`/`session-drain-soonest` strategies depend on; added a `WINDOW_RESET_MIN_ADVANCE_MS` (60s) minimum-advance threshold sitting in the empty gap between jitter and real rollovers, backed by 4 new regression tests ([#381](https://github.com/tombii/better-ccflare/pull/381))
|
|
796
796
|
- [@troykelly](https://github.com/troykelly) - Comprehensive PostgreSQL compatibility fixes including boolean type handling, identifier case preservation, BIGINT string coercion, UNION ALL type alignment, HAVING clause compatibility, parameter ordering corrections, worker initialization, and connection pooling (issue #81); detailed bug report and root cause analysis for `/api/accounts` Invalid Date error affecting PostgreSQL BIGINT columns (issue #88)
|
|
797
797
|
- [@cowwoc](https://github.com/cowwoc) - Compact reliability fixes, space breakdown after cleanup, requests tab without payloads (PR #149); clear stale rate_limited_until when usage API shows capacity restored (PR #150); prevent manually-paused accounts from being selected as auto-fallback candidates (PR #151); balance new sessions by utilization within same-priority accounts using water-filling algorithm (PR #152); fix ModuleNotFound crash in compiled binary when using Compact Database by embedding vacuum-worker at build time (PR #155); expected usage position indicator on rate limit bars showing projected pacing vs. reset window (PR #156); show explicit rate-limited state when usage data unavailable on startup (PR #161); deduplicate concurrent fetchAndCache calls per account to prevent redundant Anthropic requests (PR #159); make GET /api/accounts cache-only and await usage fetch in refresh endpoint, eliminating blind 5s timeout (PR #162); mark account rate-limited when all models exhausted to prevent stale-state retry loops (PR #163); use retry-after header for dynamic model-exhaustion cooldown instead of hardcoded 1 hour (PR #164); remove implicit sonnet catch-all in getModelList preventing silent unexpected model remaps (PR #165); reduce log noise by aggregating auto-unpause skip messages and suppressing identity model mapping logs (PR #167); reasoning effort compatibility layer for OpenAI/Codex routes with deterministic downgrade mapping and count_tokens path support (PR #172, implemented in PR #188); unified Codex model mapping path (PR #203)
|
|
798
798
|
- [@wonkooklee](https://github.com/wonkooklee) - Fixed ZlibError on startup caused by Bun auto-decompressing upstream 404 responses while leaving stale `content-encoding: gzip` headers intact — applied `withSanitizedProxyHeaders` to the model-not-found early-return path, matching every other forward path (PR #243)
|
|
799
799
|
- [@Cotch22](https://github.com/Cotch22) - Fixed garbled non-ASCII text in request details modal by decoding base64 bodies as UTF-8 via `TextDecoder` instead of raw `atob()` (PR #246)
|
|
800
|
-
- [@zenprocess](https://github.com/zenprocess) - LeastUsed load-balancing strategy selecting accounts by lowest utilization to prevent burst pool-exhaustion (PR #193); fix keepalive 429s incorrectly cooling accounts by skipping cooldown application on synthetic keepalive replays, and shorten the no-reset fallback cooldown from 1 hour to 60 seconds (overridable via env) to prevent mass account lockout on burst contention (PR #196); optional `X-Anthropic-Agent-Id` header for explicit per-agent attribution ([#260](https://github.com/tombii/better-ccflare/pull/260)); detect and record premature SSE termination for all Anthropic-Messages-shaped streams — generalized the terminal-recovery wrapper to cover every anthropic-compatible provider (not just native Anthropic), added a `stream_terminal_state` column distinguishing complete/recovered/error/truncated/client-cancelled outcomes, and fixed success-rate metrics silently recording truncated mid-stream responses as successful ([#344](https://github.com/tombii/better-ccflare/pull/344)); fixed usage-exhausted accounts staying in the selection pool and getting cycled through every model instead of being skipped, by filtering accounts whose representative usage window is fully exhausted out of `getOrderedAccounts()` before the load-balancing strategy ever sees them, with `isAccountAvailable` gaining an optional usage-snapshot parameter so the same exhaustion check backs both the forced-account and combo-slot selection paths ([#345](https://github.com/tombii/better-ccflare/pull/345)); fixed dashboard double-submit creating duplicate accounts and the by-name delete cascade that could wipe every account sharing a name — added a DB-level `UNIQUE(name, provider, custom_endpoint)` guard across all add paths (dashboard, CLI, OAuth), switched account deletion to be genuinely id-scoped everywhere, and added a non-destructive migration that merges existing duplicate rows (credentials, counters, flags, cooldown state) into a single survivor before enforcing uniqueness on both the SQLite and PostgreSQL paths ([#343](https://github.com/tombii/better-ccflare/pull/343)); added MiniMax Token Plan usage polling mirroring the zai/xai fetcher pattern, deriving each window's interval length from its own `end_time - start_time`, inverting the API's remaining-percent into utilization, and using only the `general` model-class row (never falling back to an unrelated quota pool like `video`) so a healthy account can't be misreported as exhausted ([#346](https://github.com/tombii/better-ccflare/pull/346)); fixed the dashboard's `no_account` bucket always reporting 0% success and being un-clickable — `COALESCE(account_used, ...)` in the success-rate query and an `IS NULL OR = ...` drill-down filter so NULL-encoded and legacy `'no_account'`-literal rows both collapse into the same bucket, plus a self-reported PostgreSQL production incident (GROUP BY functional-dependency violation from the placeholder renumberer) diagnosed and fixed same-day with a verified `GROUP BY 1` rewrite, BIGINT-as-string coercions across several repositories, and a new `DATABASE_URL`-gated live PostgreSQL query harness ([#352](https://github.com/tombii/better-ccflare/pull/352)); hardened MiniMax Token Plan status-code handling — replaced the `!== 0` failure check with a positive success allowlist distinguishing known failures (auth/quota) from unrecognized codes so a future MiniMax success code other than `0` can't silently read as a permanent failure, and fixed `registerMinimaxUsagePolling` silently skipping accounts with a missing/empty API key with no log signal, which previously surfaced as a misleading "no Minimax accounts found" diagnosis when accounts existed but lacked credentials ([#350](https://github.com/tombii/better-ccflare/pull/350)); gated agent workspace auto-discovery in `interceptAndModifyRequest` behind an opt-in `BETTER_CCFLARE_AGENT_WORKSPACE_AUTODISCOVER` flag (off by default), mirroring the existing `BETTER_CCFLARE_DISCOVER_PLUGIN_AGENTS` precedent — stops every request in containerized/server deploys from regex-hunting filesystem paths out of client system prompts and burning a hot-path `existsSync` + `registerWorkspace()` cache refresh that could never succeed against the local allowlist anyway, while already-persisted workspaces continue to load normally ([#361](https://github.com/tombii/better-ccflare/pull/361)); fixed Bun's native off-heap body retention (issue #273) on abandoned proxy `Response`s — benchmarked and disproved the prior `body.cancel()` mitigation as a no-op on every released Bun (indistinguishable RSS growth with or without it, confirmed upstream at oven-sh/bun#35093), then replaced it with a chunked drain (`drainBody`) that actually releases the native backing store, wired across all 12 failover/retry discard sites plus the previously-unreleased `extractUsageInfo` response clone in `updateAccountMetadata`; also caught and disclosed a flaw in the PR's own real-network leak harness after merge (bad auth token producing 144-byte responses, plus shared-process RSS measurement masking the signal), replacing it with an isolated-process rig and cautioning that its own median result is inconclusive at n=6 ([#353](https://github.com/tombii/better-ccflare/pull/353)); made the pool-exhausted 503 usage-aware after a production incident where every account was usage-capped (not rate-limited) for 116 minutes — `/health` kept reporting `routable: 3` and every 503 carried a flat `Retry-After: 60`, which combined with `CLAUDE_CODE_MAX_RETRIES=5` to kill client retries in ~300s; added a `usage_exhausted` reason to the pool-exhausted response ladder, derived `next_available_at`/`Retry-After` from the earliest of cooldown expiry and known usage reset with a 600s (UsageCache TTL) floor when no reset is known, and made `/health`'s `routable` count usage-aware to match what the proxy will actually route ([#365](https://github.com/tombii/better-ccflare/pull/365)); added a per-account/per-provider circuit breaker (closed/open/half-open) around the proxy's failure handling, tripping on sustained upstream failures independently of rate-limit/usage state and exposing a distinct `circuit_open` pool-exhaustion reason with its own 30s Retry-After cooldown floor — composed against the concurrently-landed usage-aware pool-exhaustion work (#365) so `circuit_open` still reports the longer, more honest wait when a breaker-tripped account also has a known usage or rate-limit recovery time, rather than undercutting it with the bare 30s floor ([#349](https://github.com/tombii/better-ccflare/pull/349)); surfaced full anomaly-detector totals and truncation state in the Insights dashboard — a new per-detector `AnomalyDetectorSummary` (`totalCount`/`truncated`) exposes the untrimmed detected count alongside the existing top-N sample so an operator can tell "50 of 50" (healthy) from "50 of 3,247" (a real spike) instead of a silently saturated cap, with a new `AnomaliesView` panel surfacing severity ordering, a truncation badge, and threshold text derived live from response `meta` rather than hard-coded; also fixed a self-caught regression where sanitizing the `project` field for display before anomaly detection collapsed distinct same-63-char-prefix projects into one runaway-loop group — moved the sanitizer to the output boundary so detection always sees the raw, already-length-capped DB value ([#369](https://github.com/tombii/better-ccflare/pull/369)); added an optional build-time `CCFLARE_BUILD_SUFFIX` env var so patched/forked builds report `<version>+<suffix>` instead of an indistinguishable stock version string, surfaced through the CLI `--version` flag, the server startup banner, and the dashboard bundle, with a strict no-op (byte-identical output) when unset ([#371](https://github.com/tombii/better-ccflare/pull/371)); fixed MiniMax Token Plan and Alibaba Coding Plan accounts collapsing to a single most-restrictive usage bar instead of separate 5-hour/7-day rows — added the missing providers to the dashboard's weekly-usage allow-list and a shape-based probe in `RateLimitProgress` disambiguating MiniMax's canonical camelCase `resetAt` windows from the Anthropic-style snake_case `resets_at` shape, so each window now renders its own utilization instead of the top-level `Math.max`-across-windows fallback hiding real headroom ([#372](https://github.com/tombii/better-ccflare/pull/372)); fixed `runaway_loop` anomaly detector firing CRITICAL alerts on healthy fleet parallelism (issue #367) — refined the detector's group key from `(account, model, project)` to `(account, model, project, agentUsed)` so independently-routed workers sharing one account+model+project no longer collapse into a single bucket and trip each other, threaded `agentUsed` through the row interface and detection path, added `x-claude-code-session-id` as a last-resort attribution fallback in the agent interceptor (after explicit-header and system-prompt detection) with a distinct `session_header` attribution source so claude-cli traffic gains per-session identity with no client change, and made the `loopMinRequests` threshold configurable with project preserved in the cooldown/alert dedup ID so distinct projects on the same account/model can't suppress each other's alerts ([#370](https://github.com/tombii/better-ccflare/pull/370))
|
|
800
|
+
- [@zenprocess](https://github.com/zenprocess) - LeastUsed load-balancing strategy selecting accounts by lowest utilization to prevent burst pool-exhaustion (PR #193); fix keepalive 429s incorrectly cooling accounts by skipping cooldown application on synthetic keepalive replays, and shorten the no-reset fallback cooldown from 1 hour to 60 seconds (overridable via env) to prevent mass account lockout on burst contention (PR #196); optional `X-Anthropic-Agent-Id` header for explicit per-agent attribution ([#260](https://github.com/tombii/better-ccflare/pull/260)); detect and record premature SSE termination for all Anthropic-Messages-shaped streams — generalized the terminal-recovery wrapper to cover every anthropic-compatible provider (not just native Anthropic), added a `stream_terminal_state` column distinguishing complete/recovered/error/truncated/client-cancelled outcomes, and fixed success-rate metrics silently recording truncated mid-stream responses as successful ([#344](https://github.com/tombii/better-ccflare/pull/344)); fixed usage-exhausted accounts staying in the selection pool and getting cycled through every model instead of being skipped, by filtering accounts whose representative usage window is fully exhausted out of `getOrderedAccounts()` before the load-balancing strategy ever sees them, with `isAccountAvailable` gaining an optional usage-snapshot parameter so the same exhaustion check backs both the forced-account and combo-slot selection paths ([#345](https://github.com/tombii/better-ccflare/pull/345)); fixed dashboard double-submit creating duplicate accounts and the by-name delete cascade that could wipe every account sharing a name — added a DB-level `UNIQUE(name, provider, custom_endpoint)` guard across all add paths (dashboard, CLI, OAuth), switched account deletion to be genuinely id-scoped everywhere, and added a non-destructive migration that merges existing duplicate rows (credentials, counters, flags, cooldown state) into a single survivor before enforcing uniqueness on both the SQLite and PostgreSQL paths ([#343](https://github.com/tombii/better-ccflare/pull/343)); added MiniMax Token Plan usage polling mirroring the zai/xai fetcher pattern, deriving each window's interval length from its own `end_time - start_time`, inverting the API's remaining-percent into utilization, and using only the `general` model-class row (never falling back to an unrelated quota pool like `video`) so a healthy account can't be misreported as exhausted ([#346](https://github.com/tombii/better-ccflare/pull/346)); fixed the dashboard's `no_account` bucket always reporting 0% success and being un-clickable — `COALESCE(account_used, ...)` in the success-rate query and an `IS NULL OR = ...` drill-down filter so NULL-encoded and legacy `'no_account'`-literal rows both collapse into the same bucket, plus a self-reported PostgreSQL production incident (GROUP BY functional-dependency violation from the placeholder renumberer) diagnosed and fixed same-day with a verified `GROUP BY 1` rewrite, BIGINT-as-string coercions across several repositories, and a new `DATABASE_URL`-gated live PostgreSQL query harness ([#352](https://github.com/tombii/better-ccflare/pull/352)); added a startup-time multi-instance guard warning the operator (or optionally refusing to start, via `BETTER_CCFLARE_MULTI_INSTANCE=refuse`) when another live ccflare process shares the same database — a `instance_heartbeats` table (ported to both SQLite and PostgreSQL) is written and refreshed on a 5s tick per instance, with rows older than 30s treated as a dead predecessor so a crash never blocks a legitimate restart, naming the seven categories of in-process coordination state (session affinity, usage caches, refresh mutex, circuit breaker, etc.) that silently diverge across instances sharing one database ([#376](https://github.com/tombii/better-ccflare/pull/376)); hardened MiniMax Token Plan status-code handling — replaced the `!== 0` failure check with a positive success allowlist distinguishing known failures (auth/quota) from unrecognized codes so a future MiniMax success code other than `0` can't silently read as a permanent failure, and fixed `registerMinimaxUsagePolling` silently skipping accounts with a missing/empty API key with no log signal, which previously surfaced as a misleading "no Minimax accounts found" diagnosis when accounts existed but lacked credentials ([#350](https://github.com/tombii/better-ccflare/pull/350)); gated agent workspace auto-discovery in `interceptAndModifyRequest` behind an opt-in `BETTER_CCFLARE_AGENT_WORKSPACE_AUTODISCOVER` flag (off by default), mirroring the existing `BETTER_CCFLARE_DISCOVER_PLUGIN_AGENTS` precedent — stops every request in containerized/server deploys from regex-hunting filesystem paths out of client system prompts and burning a hot-path `existsSync` + `registerWorkspace()` cache refresh that could never succeed against the local allowlist anyway, while already-persisted workspaces continue to load normally ([#361](https://github.com/tombii/better-ccflare/pull/361)); fixed Bun's native off-heap body retention (issue #273) on abandoned proxy `Response`s — benchmarked and disproved the prior `body.cancel()` mitigation as a no-op on every released Bun (indistinguishable RSS growth with or without it, confirmed upstream at oven-sh/bun#35093), then replaced it with a chunked drain (`drainBody`) that actually releases the native backing store, wired across all 12 failover/retry discard sites plus the previously-unreleased `extractUsageInfo` response clone in `updateAccountMetadata`; also caught and disclosed a flaw in the PR's own real-network leak harness after merge (bad auth token producing 144-byte responses, plus shared-process RSS measurement masking the signal), replacing it with an isolated-process rig and cautioning that its own median result is inconclusive at n=6 ([#353](https://github.com/tombii/better-ccflare/pull/353)); made the pool-exhausted 503 usage-aware after a production incident where every account was usage-capped (not rate-limited) for 116 minutes — `/health` kept reporting `routable: 3` and every 503 carried a flat `Retry-After: 60`, which combined with `CLAUDE_CODE_MAX_RETRIES=5` to kill client retries in ~300s; added a `usage_exhausted` reason to the pool-exhausted response ladder, derived `next_available_at`/`Retry-After` from the earliest of cooldown expiry and known usage reset with a 600s (UsageCache TTL) floor when no reset is known, and made `/health`'s `routable` count usage-aware to match what the proxy will actually route ([#365](https://github.com/tombii/better-ccflare/pull/365)); added a per-account/per-provider circuit breaker (closed/open/half-open) around the proxy's failure handling, tripping on sustained upstream failures independently of rate-limit/usage state and exposing a distinct `circuit_open` pool-exhaustion reason with its own 30s Retry-After cooldown floor — composed against the concurrently-landed usage-aware pool-exhaustion work (#365) so `circuit_open` still reports the longer, more honest wait when a breaker-tripped account also has a known usage or rate-limit recovery time, rather than undercutting it with the bare 30s floor ([#349](https://github.com/tombii/better-ccflare/pull/349)); surfaced full anomaly-detector totals and truncation state in the Insights dashboard — a new per-detector `AnomalyDetectorSummary` (`totalCount`/`truncated`) exposes the untrimmed detected count alongside the existing top-N sample so an operator can tell "50 of 50" (healthy) from "50 of 3,247" (a real spike) instead of a silently saturated cap, with a new `AnomaliesView` panel surfacing severity ordering, a truncation badge, and threshold text derived live from response `meta` rather than hard-coded; also fixed a self-caught regression where sanitizing the `project` field for display before anomaly detection collapsed distinct same-63-char-prefix projects into one runaway-loop group — moved the sanitizer to the output boundary so detection always sees the raw, already-length-capped DB value ([#369](https://github.com/tombii/better-ccflare/pull/369)); added an optional build-time `CCFLARE_BUILD_SUFFIX` env var so patched/forked builds report `<version>+<suffix>` instead of an indistinguishable stock version string, surfaced through the CLI `--version` flag, the server startup banner, and the dashboard bundle, with a strict no-op (byte-identical output) when unset ([#371](https://github.com/tombii/better-ccflare/pull/371)); fixed MiniMax Token Plan and Alibaba Coding Plan accounts collapsing to a single most-restrictive usage bar instead of separate 5-hour/7-day rows — added the missing providers to the dashboard's weekly-usage allow-list and a shape-based probe in `RateLimitProgress` disambiguating MiniMax's canonical camelCase `resetAt` windows from the Anthropic-style snake_case `resets_at` shape, so each window now renders its own utilization instead of the top-level `Math.max`-across-windows fallback hiding real headroom ([#372](https://github.com/tombii/better-ccflare/pull/372)); fixed `runaway_loop` anomaly detector firing CRITICAL alerts on healthy fleet parallelism (issue #367) — refined the detector's group key from `(account, model, project)` to `(account, model, project, agentUsed)` so independently-routed workers sharing one account+model+project no longer collapse into a single bucket and trip each other, threaded `agentUsed` through the row interface and detection path, added `x-claude-code-session-id` as a last-resort attribution fallback in the agent interceptor (after explicit-header and system-prompt detection) with a distinct `session_header` attribution source so claude-cli traffic gains per-session identity with no client change, and made the `loopMinRequests` threshold configurable with project preserved in the cooldown/alert dedup ID so distinct projects on the same account/model can't suppress each other's alerts ([#370](https://github.com/tombii/better-ccflare/pull/370)); deployment guide section documenting the single-instance-per-database operator rule — names the seven categories of in-process coordination state (session affinity, usage caches, refresh mutex, circuit breaker, etc.) that silently diverge when two instances share a database, explains why the divergence produces no error, and gives concrete alternatives (cold-handoff rolling restarts instead of true blue/green, and account-partitioned horizontal scaling with one database per instance); also flagged the pre-existing Kubernetes example's contradiction with the new rule for maintainer resolution rather than editing it unilaterally ([#377](https://github.com/tombii/better-ccflare/pull/377)); added build-time provenance (`version`, `git_sha`, `git_ref`, `build_date`) to the `/health` response so operators can verify the running image matches the deploy branch HEAD, with each field falling back to `"unknown"` when unset so the shape stays stable ([#380](https://github.com/tombii/better-ccflare/pull/380))
|
|
801
801
|
- [@robsonek](https://github.com/robsonek) - Stopped accounts being benched for a request-scoped Anthropic 429 that reports `x-should-retry: true` with no rate-limit metadata — added a fail-closed, header-only `isRetryable429` predicate (scanning all header names by `anthropic-ratelimit-`/`x-ratelimit-` prefix so a future window shape is automatically declined) that classifies such responses as `windowless_429`, failing over per-request with no account cooldown and no consecutive-429 increment instead of draining the pool account-by-account until force-reset was required (PR #375)
|
|
802
802
|
- [@issmirnov](https://github.com/issmirnov) - Persisted analytics view controls (view mode, time range, metric, model breakdown, filters) in the URL query string and localStorage so selections survive reloads and are shareable via link, with defensive decoding and cumulative-view coupling enforced centrally (PR #252); round the Success Rate Trend chart's Y-axis tick labels and tooltip to zero decimals via `formatPercentage(..., 0)` so values like `99.1666...%` no longer overflow their container, and add a human-readable "Success Rate" line name (PR #280)
|
|
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)
|
package/dist/better-ccflare
CHANGED
|
Binary file
|
package/package.json
CHANGED