mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 02:42:24 +03:00
fix(sse): honor CLIProxyAPI environment API key (#12099)
Honra a chave de API dedicada de ambiente do CLIProxyAPI, com teste próprio. Validado no worktree combinado. Obrigado!
This commit is contained in:
@@ -2042,6 +2042,8 @@ APP_LOG_TO_FILE=true
|
||||
# CLIPROXYAPI_HOST=127.0.0.1
|
||||
# CLIPROXYAPI_PORT=5544
|
||||
# CLIPROXYAPI_CONFIG_DIR=~/.cli-proxy-api
|
||||
# Data-plane key fallback; the cliproxyapi_api_key setting takes precedence.
|
||||
# CLIPROXYAPI_API_KEY=
|
||||
# Management key for an externally managed instance. Embedded instances use
|
||||
# OmniRoute's encrypted service key.
|
||||
# CLIPROXYAPI_MANAGEMENT_KEY=
|
||||
|
||||
@@ -1046,6 +1046,7 @@ desktop install.
|
||||
| `EMBED_WS_PROXY_PORT` | `20131` | `src/lib/services/embedWsProxy.ts` | Port for the embedded-service WebSocket proxy server. |
|
||||
| `CLIPROXYAPI_HOST` | `127.0.0.1` | `open-sse/executors/cliproxyapi.ts` | CLIProxyAPI bridge host (legacy integration). |
|
||||
| `CLIPROXYAPI_PORT` | `5544` | `open-sse/executors/cliproxyapi.ts` | CLIProxyAPI bridge port. |
|
||||
| `CLIPROXYAPI_API_KEY` | _(empty)_ | `open-sse/handlers/chatCore/cliproxyapiCredentials.ts` | Data-plane key fallback when the `cliproxyapi_api_key` setting is absent. |
|
||||
| `CLIPROXYAPI_MANAGEMENT_KEY` | _(empty)_ | `src/lib/services/cliproxyAccountHealth.ts` | Management key for account-health reads from an externally managed CLIProxyAPI instance. |
|
||||
| `CLIPROXYAPI_CONFIG_DIR` | `~/.cli-proxy-api` | `src/lib/versionManager/processManager.ts` | CLIProxyAPI config directory. |
|
||||
| `MUX_SERVICE_PORT` | `8322` | `src/lib/services/bootstrap.ts` | Override the port where the embedded Mux (coder/mux) agent-orchestration daemon listens (always 127.0.0.1). |
|
||||
|
||||
@@ -28,14 +28,16 @@ type ExecutorLike = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads the dedicated CLIProxyAPI key out of a settings blob (as returned by
|
||||
* `getCachedSettings()`), trimmed and normalized to `null` when absent/blank.
|
||||
* Reads the dedicated CLIProxyAPI key from settings, then falls back to the
|
||||
* environment. Values are 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;
|
||||
if (typeof raw === "string" && raw.trim()) return raw.trim();
|
||||
const envKey = process.env.CLIPROXYAPI_API_KEY;
|
||||
return typeof envKey === "string" && envKey.trim() ? envKey.trim() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,7 +56,7 @@ function parseFallbackCodes(raw: unknown): number[] | null {
|
||||
* Reads the CLIProxyAPI-related settings shared by both the direct
|
||||
* `mode: "cliproxyapi"` passthrough leg and the `mode: "fallback"` retry leg:
|
||||
* the custom fallback status codes and the dedicated credential (#7645).
|
||||
* Falls back to defaults / no dedicated key on any read failure.
|
||||
* Falls back to defaults and the environment key on any read failure.
|
||||
*/
|
||||
async function loadCliproxyapiSettings(): Promise<{
|
||||
fallbackCodes: number[];
|
||||
@@ -71,7 +71,10 @@ async function loadCliproxyapiSettings(): Promise<{
|
||||
dedicatedApiKey: resolveDedicatedCliproxyapiApiKey(allSettings),
|
||||
};
|
||||
} catch {
|
||||
return { fallbackCodes: [...DEFAULT_FALLBACK_CODES], dedicatedApiKey: null };
|
||||
return {
|
||||
fallbackCodes: [...DEFAULT_FALLBACK_CODES],
|
||||
dedicatedApiKey: resolveDedicatedCliproxyapiApiKey(null),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,18 +29,27 @@ const settingsDb = await import("../../src/lib/db/settings.ts");
|
||||
const upstreamProxyDb = await import("../../src/lib/db/upstreamProxy.ts");
|
||||
const { resolveExecutorWithProxy } =
|
||||
await import("../../open-sse/handlers/chatCore/executorProxy.ts");
|
||||
const { resolveDedicatedCliproxyapiApiKey } =
|
||||
await import("../../open-sse/handlers/chatCore/cliproxyapiCredentials.ts");
|
||||
const { clearUpstreamProxyConfigCache } =
|
||||
await import("../../open-sse/handlers/chatCore/comboContextCache.ts");
|
||||
const { updateSettingsSchema } = await import("../../src/shared/validation/settingsSchemas.ts");
|
||||
|
||||
const NATIVE_KEY = "sk-native-provider-key-cliproxyapi-must-not-see";
|
||||
const DEDICATED_KEY = "cpa-dedicated-key-configured-by-operator";
|
||||
const ENV_KEY = "cpa-dedicated-key-from-environment";
|
||||
const originalEnvKey = process.env.CLIPROXYAPI_API_KEY;
|
||||
|
||||
before(async () => {
|
||||
await coreDb.ensureDbInitialized();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (originalEnvKey === undefined) {
|
||||
delete process.env.CLIPROXYAPI_API_KEY;
|
||||
} else {
|
||||
process.env.CLIPROXYAPI_API_KEY = originalEnvKey;
|
||||
}
|
||||
clearUpstreamProxyConfigCache();
|
||||
const { dbCache } = await import("../../src/lib/db/readCache.ts");
|
||||
dbCache?.invalidate?.("settings");
|
||||
@@ -111,6 +120,34 @@ describe("#7645 — settingsSchemas has a dedicated cliproxyapi_api_key field",
|
||||
});
|
||||
|
||||
describe("#7645 — CLIProxyAPI fallback leg authenticates with the dedicated key", () => {
|
||||
it("uses CLIPROXYAPI_API_KEY when settings are unavailable", () => {
|
||||
process.env.CLIPROXYAPI_API_KEY = ` ${ENV_KEY} `;
|
||||
assert.equal(resolveDedicatedCliproxyapiApiKey(null), ENV_KEY);
|
||||
});
|
||||
|
||||
it("uses CLIPROXYAPI_API_KEY when no settings key is configured", async () => {
|
||||
process.env.CLIPROXYAPI_API_KEY = ENV_KEY;
|
||||
await settingsDb.updateSettings({ cliproxyapi_api_key: "" });
|
||||
await upstreamProxyDb.upsertUpstreamProxyConfig({
|
||||
providerId: "anthropic-7645-env-key",
|
||||
mode: "cliproxyapi",
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
const executor = await resolveExecutorWithProxy("anthropic-7645-env-key", undefined, null);
|
||||
const { headers, called } = await withCapturedCliproxyapiRequest(() =>
|
||||
(executor as ExecutorLike).execute({
|
||||
model: "claude-3-opus",
|
||||
body: { model: "claude-3-opus", messages: [{ role: "user", content: "hi" }] },
|
||||
stream: false,
|
||||
credentials: { apiKey: NATIVE_KEY },
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(called, true);
|
||||
assert.equal(headers.Authorization, `Bearer ${ENV_KEY}`);
|
||||
});
|
||||
|
||||
it("uses the dedicated cliproxyapi_api_key, not the failed native provider's own credential", async () => {
|
||||
await settingsDb.updateSettings({ cliproxyapi_api_key: DEDICATED_KEY });
|
||||
await upstreamProxyDb.upsertUpstreamProxyConfig({
|
||||
@@ -210,6 +247,7 @@ describe("#7645 — CLIProxyAPI fallback leg authenticates with the dedicated ke
|
||||
});
|
||||
|
||||
it("falls back to the connection's own credential when no dedicated key is configured (no regression)", async () => {
|
||||
delete process.env.CLIPROXYAPI_API_KEY;
|
||||
await settingsDb.updateSettings({ cliproxyapi_api_key: "" });
|
||||
await upstreamProxyDb.upsertUpstreamProxyConfig({
|
||||
providerId: "anthropic-7645-no-dedicated-key",
|
||||
|
||||
Reference in New Issue
Block a user