项目文件夹

文件
2026-07-13 13:39:12 +08:00

79 行
2.9 KiB
TypeScript

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
import { fetchLiveProviderLimits } from "@/lib/usage/providerLimits";
import { isClaudeExtraUsageBlockEnabled } from "@/lib/providers/claudeExtraUsage";
// #4604 — Lazy backoff for the best-effort live-WS sidecar bridge. In single-port
// deployments the sidecar (port 20129) is not running, so every compression event
// POST failed with ECONNREFUSED; because the global fetch is proxyFetch, each
// failure logged a "[ProxyFetch] Undici dispatcher failed" warning (272× in 42min).
// After a few consecutive failures we stop attempting for a cooldown window (then
// probe once), mirroring the project's lazy circuit-breaker recovery. A success
// clears the backoff so a sidecar that comes up later is picked back up.
const LIVE_WS_MAX_CONSECUTIVE_FAILURES = 3;
const LIVE_WS_DISABLE_MS = 60_000;
let liveWsConsecutiveFailures = 0;
let liveWsDisabledUntil = 0;
/** Test-only: reset the live-WS forwarding backoff state. */
export function __resetLiveWsForwardingState(): void {
liveWsConsecutiveFailures = 0;
liveWsDisabledUntil = 0;
}
export async function forwardDashboardEventToLiveWs(
event: string,
payload: unknown,
fetchImpl: typeof fetch = fetch,
now: () => number = Date.now
): Promise<void> {
// Skip while the bridge is in a cooldown window after repeated failures.
if (liveWsDisabledUntil > now()) return;
const port = process.env.LIVE_WS_PORT || "20129";
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 1_500);
try {
await fetchImpl(`http://127.0.0.1:${port}/__omniroute_event`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ event, payload, timestamp: now() }),
signal: controller.signal,
});
// Success → the sidecar is reachable; clear any accumulated backoff.
liveWsConsecutiveFailures = 0;
liveWsDisabledUntil = 0;
} catch {
// Best-effort sidecar bridge; do not affect the chat hot path. Trip the
// cooldown once failures pile up so a missing sidecar stops spamming logs.
liveWsConsecutiveFailures += 1;
if (liveWsConsecutiveFailures >= LIVE_WS_MAX_CONSECUTIVE_FAILURES) {
liveWsDisabledUntil = now() + LIVE_WS_DISABLE_MS;
liveWsConsecutiveFailures = 0;
}
} finally {
clearTimeout(timeout);
}
}
export async function maybeSyncClaudeExtraUsageState({
provider,
connectionId,
providerSpecificData,
log,
}: {
provider: string | null | undefined;
connectionId: string | null | undefined;
providerSpecificData: unknown;
log?: { debug?: (...args: unknown[]) => void; warn?: (...args: unknown[]) => void } | null;
}) {
if (!connectionId || !isClaudeExtraUsageBlockEnabled(provider, providerSpecificData)) {
return;
}
try {
await fetchLiveProviderLimits(connectionId);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
log?.debug?.("CLAUDE_USAGE", `Failed to sync Claude extra-usage state: ${message}`);
}
}