fix(healthcheck): per-provider proactive-refresh skip list (rescue short-TTL OAuth) (#3159)

Per-provider proactive-refresh skip list (OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS) to rescue short-TTL OAuth. Integrated into release/v3.8.10.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-04 18:33:31 -03:00
committed by GitHub
parent 1be9606cc2
commit 42df1703c6
4 changed files with 104 additions and 0 deletions

View File

@@ -1347,6 +1347,12 @@ APP_LOG_TO_FILE=true
# Disable the OAuth token healthcheck loop during tests (default: true).
# OMNIROUTE_DISABLE_TOKEN_HEALTHCHECK=true
# Exclude specific providers from the PROACTIVE token-refresh sweep (comma-separated,
# case-insensitive). Targeted alternative to OMNIROUTE_DISABLE_TOKEN_HEALTHCHECK: keeps
# rotating-cascade providers (Codex/OpenAI share one Auth0 family) on the reactive 401
# path only, while short-TTL providers like Kimi-coding keep being refreshed proactively.
# OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS=codex,openai
# Silence healthcheck noise in Playwright stdout (default: true).
# OMNIROUTE_HIDE_HEALTHCHECK_LOGS=true

View File

@@ -911,6 +911,7 @@ value below unset in production deployments.
| `OMNIROUTE_E2E_PASSWORD` | falls back to `INITIAL_PASSWORD` | `scripts/dev/run-next-playwright.mjs` | Admin password injected into the Playwright environment. |
| `OMNIROUTE_DISABLE_LOCAL_HEALTHCHECK` | `true` | `scripts/dev/run-next-playwright.mjs` | Disable the local healthcheck poll during Playwright runs. |
| `OMNIROUTE_DISABLE_TOKEN_HEALTHCHECK` | `true` | `scripts/dev/run-next-playwright.mjs` | Disable the OAuth token healthcheck loop during tests. |
| `OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS` | _(unset)_ | `src/lib/tokenHealthCheck.ts` | Comma-separated providers excluded from the proactive token-refresh sweep (e.g. `codex,openai`). Targeted alternative to fully disabling the healthcheck — short-TTL providers keep refreshing while cascade providers stay reactive-only. |
| `OMNIROUTE_HIDE_HEALTHCHECK_LOGS` | `true` | `scripts/dev/run-next-playwright.mjs` | Silence healthcheck noise in Playwright stdout. |
| `OMNIROUTE_PLAYWRIGHT_SKIP_BUILD` | `0` | `scripts/dev/run-next-playwright.mjs` | Skip the Next.js production build before Playwright starts (CI optimization). |
| `OMNIROUTE_SKIP_UNINSTALL_HOOK` | `0` | `scripts/build/uninstall.mjs` | Skip the OmniRoute uninstall hook (used by CI to keep `node_modules` intact). |

View File

@@ -109,6 +109,25 @@ function isHealthCheckDisabled(): boolean {
);
}
/**
* Providers excluded from the PROACTIVE refresh sweep, comma-separated and
* case-insensitive (e.g. "codex,openai"). A targeted alternative to the blunt
* OMNIROUTE_DISABLE_TOKEN_HEALTHCHECK switch: it lets an operator keep the
* rotating-token cascade providers (Codex/OpenAI share one Auth0 family) off the
* proactive sweep — leaving their refresh to the reactive, serialized 401 path —
* WITHOUT also starving short-TTL providers like Kimi-coding, whose tokens expire
* while idle when the whole sweep is disabled.
*/
function getHealthCheckSkipProviders(): Set<string> {
const raw = process.env.OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS || "";
return new Set(
raw
.split(",")
.map((s) => s.trim().toLowerCase())
.filter(Boolean)
);
}
// ── Logging helper ───────────────────────────────────────────────────────────
let cachedHideLogs: boolean | null = null;
let cacheTimestamp = 0;
@@ -265,6 +284,13 @@ export async function checkConnection(conn) {
const latestConnection = (await getProviderConnectionById(conn.id)) || conn;
conn = latestConnection;
// Per-provider opt-out of proactive refresh (e.g. Codex/OpenAI cascade
// providers) — their token stays on the reactive, serialized 401 path while
// other providers keep being refreshed proactively.
if (getHealthCheckSkipProviders().has(String(conn.provider || "").toLowerCase())) {
return;
}
// Determine interval (0 = disabled)
const intervalMin = conn.healthCheckInterval ?? DEFAULT_HEALTH_CHECK_INTERVAL_MIN;
if (intervalMin <= 0) return;

View File

@@ -398,3 +398,74 @@ test("checkConnection skips interval refresh when token expiry is known and stil
}
);
});
test("checkConnection skips providers listed in OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS (#kimi-15)", async () => {
await resetStorage();
const providerId = "custom-oauth-skip-list";
const refreshRequests: string[] = [];
const prevSkip = process.env.OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS;
await withHttpServer(
(req, res) => {
let body = "";
req.setEncoding("utf8");
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", () => {
refreshRequests.push(body);
res.writeHead(200, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
access_token: "should-not-be-fetched",
refresh_token: "should-not-be-fetched",
expires_in: 3600,
})
);
});
},
async (tokenServer) => {
await withPatchedProvider(
providerId,
{
tokenUrl: `${tokenServer.url}/token`,
clientId: "skip-client-id",
clientSecret: "skip-client-secret",
},
async () => {
const connection = await providersDb.createProviderConnection({
provider: providerId,
authType: "oauth",
name: "Skip-list Account",
email: "skip@example.com",
accessToken: "stale-access-token",
refreshToken: "refresh-token-skip",
isActive: true,
});
// The connection is due for refresh (no known expiry, never checked).
// With the provider listed, the proactive sweep must skip it entirely —
// NO refresh request is made.
process.env.OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS = `foo, ${providerId} ,bar`;
await tokenHealthCheck.checkConnection(connection);
assert.equal(
refreshRequests.length,
0,
"listed provider must NOT trigger a proactive refresh"
);
// Control: with the provider no longer listed, the same due connection
// IS refreshed — proving the skip (not token freshness) gated it.
process.env.OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS = "some-other-provider";
const stillStale = await providersDb.getProviderConnectionById((connection as any).id);
await tokenHealthCheck.checkConnection(stillStale);
assert.equal(refreshRequests.length, 1, "non-listed provider must refresh");
}
);
}
);
if (prevSkip === undefined) delete process.env.OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS;
else process.env.OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS = prevSkip;
});