mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
Drop dead exports + misleading comments + unreachable defaults flagged by phase-D simplify review (autonomous-yes items only): - channel-state: drop unused TEST_CONSTANTS re-export - retry-policy: drop unused RejectKind type alias - achievements: drop misleading "fallback" comment on exhaustive switch - notification-status-card: drop unreachable default in stateBadgeFor - withings: export isWithingsRefreshReauthFailure + dedupe inline classifier in /api/withings/status route - codex-slug-cache: drop test-only inspectCodexSlugCache helper; tests read getCachedCodexSlug() instead Findings 1, 2, 8, 9 deferred to v1.4.16 (require product call). Finding 11 is an explicit anti-finding. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
/**
|
|
* Process-local positive cache for working Codex model slugs.
|
|
*
|
|
* Phase C1 (v1.4.15) — slug-drift defence per
|
|
* `docs/codex-protocol-spec.md` §7b. The ChatGPT-Codex backend rotates
|
|
* its allow-list silently; pinning a single `CODEX_MODEL` slug bricks
|
|
* the integration the moment upstream flips it. Instead of pinning,
|
|
* `CodexClient` walks an ordered fallback chain on every fresh
|
|
* request series and caches the first slug that returns 200 +
|
|
* `response.completed` for `CACHE_TTL_MS` so subsequent requests skip
|
|
* the chain walk.
|
|
*
|
|
* Lives in its own module so:
|
|
* - Tests can `clearCodexSlugCache()` between cases.
|
|
* - The shared Map state stays out of `CodexClient` itself (so
|
|
* instance creation in `resolveProvider` doesn't reset the cache).
|
|
* - Future telemetry (cache hit-rate emission) hooks here cleanly.
|
|
*/
|
|
|
|
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour per spec §7b
|
|
|
|
interface CachedEntry {
|
|
slug: string;
|
|
cachedAtMs: number;
|
|
}
|
|
|
|
/**
|
|
* Cache key — currently a single global slot ("codex"). Kept in a Map
|
|
* so future per-account / per-model-family scoping is a one-line
|
|
* change.
|
|
*/
|
|
const CACHE_KEY = "codex";
|
|
|
|
const cache = new Map<string, CachedEntry>();
|
|
|
|
export function getCachedCodexSlug(now: number = Date.now()): string | null {
|
|
const entry = cache.get(CACHE_KEY);
|
|
if (!entry) return null;
|
|
if (now - entry.cachedAtMs > CACHE_TTL_MS) {
|
|
cache.delete(CACHE_KEY);
|
|
return null;
|
|
}
|
|
return entry.slug;
|
|
}
|
|
|
|
export function setCachedCodexSlug(
|
|
slug: string,
|
|
now: number = Date.now(),
|
|
): void {
|
|
cache.set(CACHE_KEY, { slug, cachedAtMs: now });
|
|
}
|
|
|
|
export function invalidateCachedCodexSlug(): void {
|
|
cache.delete(CACHE_KEY);
|
|
}
|
|
|
|
/** Test-only: reset the cache between cases. */
|
|
export function clearCodexSlugCache(): void {
|
|
cache.clear();
|
|
}
|
|
|
|
export const CODEX_SLUG_CACHE_TTL_MS = CACHE_TTL_MS;
|