mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-26 09:02:11 +03:00
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).
77 lines
3.1 KiB
TypeScript
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;
|
|
}
|