Files
OmniRoute/open-sse/handlers/chatCore/cliproxyapiCredentials.ts
Paijo 7cfabfc5c9 perf(executors): lazy-load the executor registry — defer class imports + construction to first use (#11220) (#11421)
Validated in a combined 3-PR batch worktree off release/v3.8.51 tip (a sibling PR from the same author, #11495, was held out — a typecheck error in zai-web.ts only reproduced with this PR + #11495 boarded together, and cleared without #11495; isolated this PR alone confirmed clean on its own too, so the interaction belonged to #11495's side — see its comment).
- Golden lock: executor-map-golden.test.ts — passes byte-identical (same keys, classes, provider identities, dispatch guards)
- Focused tests part of batch's 94/94 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for the measured, careful methodology here — the golden-lock contract plus the isolated DATA_DIR benchmarking make this an easy PR to trust despite the wide surface (72 files).
2026-08-25 18:22:46 -03:00

77 lines
3.1 KiB
TypeScript

/**
* CLIProxyAPI dedicated-credential resolution (#7645).
*
* CLIProxyAPI requires its own separately-configured `api-keys:` credential
* and rejects any other token with 401. Before this fix, both the direct
* `mode: "cliproxyapi"` passthrough leg and the `mode: "fallback"` retry leg
* (`open-sse/handlers/chatCore/executorProxy.ts::resolveExecutorWithProxy`)
* reused the resolved connection's own credentials — the native provider's
* key — as the Authorization header sent to CLIProxyAPI, making the fallback
* path a permanent no-op for every provider configured this way.
*
* This module resolves and applies the dedicated `cliproxyapi_api_key`
* setting at the executor boundary, so `CliproxyapiExecutor` itself stays
* credential-source-agnostic (it just uses whatever `credentials` it's
* handed — see `buildHeaders()`).
*/
import type { ProviderCredentials } from "../../executors/base.ts";
type ExecutorInput = {
credentials: ProviderCredentials;
};
// No index signature: executors (BaseExecutor subclasses) must satisfy this
// structurally, and class instances don't carry index signatures.
type ExecutorLike = {
execute: (input: ExecutorInput) => Promise<unknown>;
};
/**
* Reads the dedicated CLIProxyAPI key out of a settings blob (as returned by
* `getCachedSettings()`), trimmed and normalized to `null` when absent/blank.
*/
export function resolveDedicatedCliproxyapiApiKey(
settings: Record<string, unknown> | null | undefined
): string | null {
const raw = settings?.cliproxyapi_api_key;
return typeof raw === "string" && raw.trim() ? raw.trim() : null;
}
/**
* Builds the credentials to use for a CLIProxyAPI-bound request. When a
* dedicated key is configured it always wins — CLIProxyAPI is a single
* shared instance serving every provider, so the resolved connection's own
* (provider-specific, and possibly already-failed) credential is never the
* right token for it. Falls back to the connection's own credentials only
* when no dedicated key is configured, preserving the pre-existing behavior
* for operators who previously worked around this by pasting a valid
* CLIProxyAPI key into the connection's own `apiKey` field.
*/
export function resolveCliproxyapiCredentials(
connectionCredentials: ProviderCredentials,
dedicatedApiKey: string | null
): ProviderCredentials {
if (!dedicatedApiKey) return connectionCredentials;
return { ...connectionCredentials, apiKey: dedicatedApiKey, accessToken: undefined };
}
/**
* Wraps an executor so every `execute()` call is routed with the dedicated
* CLIProxyAPI credential substituted in when one is configured. No-op
* wrapper when no dedicated key is set (returns the executor unchanged).
*/
export function wrapExecutorWithCliproxyapiCredentials<T extends ExecutorLike>(
executor: T,
dedicatedApiKey: string | null
): T {
if (!dedicatedApiKey) return executor;
const wrapped = Object.create(executor) as T;
wrapped.execute = (input: ExecutorInput) =>
executor.execute({
...input,
credentials: resolveCliproxyapiCredentials(input.credentials, dedicatedApiKey),
});
return wrapped;
}