From ec75291d5bce21079f36af1dfd7dd4b7287f375e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Barreiro?= <52393857+BarreiroT@users.noreply.github.com> Date: Thu, 11 Jun 2026 17:29:17 -0300 Subject: [PATCH] Allow overriding the API base url (#11440) * Allow overriding the base url * Override the mcpbaseurl * update the api base url * Fix tests --- .../src/runtime/cline-environment.test.ts | 128 +++++++++++------- .../shared/src/runtime/cline-environment.ts | 39 ++++-- 2 files changed, 108 insertions(+), 59 deletions(-) diff --git a/sdk/packages/shared/src/runtime/cline-environment.test.ts b/sdk/packages/shared/src/runtime/cline-environment.test.ts index 417cc44894..95ca7728c9 100644 --- a/sdk/packages/shared/src/runtime/cline-environment.test.ts +++ b/sdk/packages/shared/src/runtime/cline-environment.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { CLINE_ENVIRONMENT_ENV, CLINE_ENVIRONMENT_OVERRIDE_ENV, @@ -8,58 +8,75 @@ import { resolveClineEnvironment, } from "./cline-environment"; +const ENV_KEYS = [ + CLINE_ENVIRONMENT_ENV, + CLINE_ENVIRONMENT_OVERRIDE_ENV, + "CLINE_API_BASE_URL", +] as const; + +const originalEnvValues = Object.fromEntries( + ENV_KEYS.map((key) => [key, process.env[key]]), +); + +beforeEach(() => { + vi.unstubAllGlobals(); + for (const key of ENV_KEYS) { + delete process.env[key]; + } +}); + +afterEach(() => { + vi.unstubAllGlobals(); + for (const key of ENV_KEYS) { + const value = originalEnvValues[key]; + if (typeof value === "string") { + process.env[key] = value; + } else { + delete process.env[key]; + } + } +}); + describe("resolveClineEnvironment", () => { it("defaults to production when no env var is set", () => { - expect(resolveClineEnvironment({ env: {} })).toBe("production"); + expect(resolveClineEnvironment()).toBe(DEFAULT_CLINE_ENVIRONMENT); }); - it("reads CLINE_ENVIRONMENT", () => { - expect( - resolveClineEnvironment({ - env: { [CLINE_ENVIRONMENT_ENV]: "staging" }, - }), - ).toBe("staging"); - expect( - resolveClineEnvironment({ - env: { [CLINE_ENVIRONMENT_ENV]: "local" }, - }), - ).toBe("local"); + it("reads CLINE_ENVIRONMENT from process.env", () => { + process.env[CLINE_ENVIRONMENT_ENV] = "staging"; + expect(resolveClineEnvironment()).toBe("staging"); + + process.env[CLINE_ENVIRONMENT_ENV] = "local"; + expect(resolveClineEnvironment()).toBe("local"); }); it("prefers CLINE_ENVIRONMENT_OVERRIDE over CLINE_ENVIRONMENT", () => { - expect( - resolveClineEnvironment({ - env: { - [CLINE_ENVIRONMENT_OVERRIDE_ENV]: "local", - [CLINE_ENVIRONMENT_ENV]: "staging", - }, - }), - ).toBe("local"); + process.env[CLINE_ENVIRONMENT_OVERRIDE_ENV] = "local"; + process.env[CLINE_ENVIRONMENT_ENV] = "staging"; + + expect(resolveClineEnvironment()).toBe("local"); }); it("normalizes case and surrounding whitespace", () => { - expect( - resolveClineEnvironment({ - env: { [CLINE_ENVIRONMENT_ENV]: " STAGING " }, - }), - ).toBe("staging"); + process.env[CLINE_ENVIRONMENT_ENV] = " STAGING "; + + expect(resolveClineEnvironment()).toBe("staging"); }); it("ignores unknown values and falls through to the next source", () => { - expect( - resolveClineEnvironment({ - env: { - [CLINE_ENVIRONMENT_OVERRIDE_ENV]: "qa", - [CLINE_ENVIRONMENT_ENV]: "staging", - }, - }), - ).toBe("staging"); + process.env[CLINE_ENVIRONMENT_OVERRIDE_ENV] = "qa"; + process.env[CLINE_ENVIRONMENT_ENV] = "staging"; + expect(resolveClineEnvironment()).toBe("staging"); - expect( - resolveClineEnvironment({ - env: { [CLINE_ENVIRONMENT_ENV]: "qa" }, - }), - ).toBe(DEFAULT_CLINE_ENVIRONMENT); + delete process.env[CLINE_ENVIRONMENT_OVERRIDE_ENV]; + process.env[CLINE_ENVIRONMENT_ENV] = "qa"; + expect(resolveClineEnvironment()).toBe(DEFAULT_CLINE_ENVIRONMENT); + }); + + it("defaults to production when process is unavailable", () => { + vi.stubGlobal("process", undefined); + + expect(resolveClineEnvironment()).toBe(DEFAULT_CLINE_ENVIRONMENT); }); }); @@ -74,18 +91,31 @@ describe("getClineEnvironmentConfig", () => { ); }); - it("resolves from env when no explicit environment is passed", () => { - expect( - getClineEnvironmentConfig({ - env: { [CLINE_ENVIRONMENT_ENV]: "staging" }, - }), - ).toBe(CLINE_ENVIRONMENTS.staging); + it("falls back to production by default", () => { + expect(getClineEnvironmentConfig()).toBe(CLINE_ENVIRONMENTS.production); }); - it("falls back to production by default", () => { - expect(getClineEnvironmentConfig({ env: {} })).toBe( - CLINE_ENVIRONMENTS.production, - ); + it("uses the resolved process.env environment when no explicit environment is provided", () => { + process.env[CLINE_ENVIRONMENT_ENV] = "staging"; + + expect(getClineEnvironmentConfig()).toBe(CLINE_ENVIRONMENTS.staging); + }); + + it("applies CLINE_API_BASE_URL without mutating the catalog config", () => { + process.env.CLINE_API_BASE_URL = "http://127.0.0.1:3000"; + + expect(getClineEnvironmentConfig("local")).toEqual({ + ...CLINE_ENVIRONMENTS.local, + apiBaseUrl: "http://127.0.0.1:3000", + mcpBaseUrl: "http://127.0.0.1:3000/v1/mcp", + }); + expect(CLINE_ENVIRONMENTS.local.apiBaseUrl).toBe("http://localhost:7777"); + }); + + it("defaults to production when process is unavailable", () => { + vi.stubGlobal("process", undefined); + + expect(getClineEnvironmentConfig()).toBe(CLINE_ENVIRONMENTS.production); }); }); diff --git a/sdk/packages/shared/src/runtime/cline-environment.ts b/sdk/packages/shared/src/runtime/cline-environment.ts index 33248821d3..612b340ba8 100644 --- a/sdk/packages/shared/src/runtime/cline-environment.ts +++ b/sdk/packages/shared/src/runtime/cline-environment.ts @@ -67,10 +67,8 @@ function readProcessEnv(): NodeJS.ProcessEnv { return process.env; } -export function resolveClineEnvironment( - options: ResolveClineEnvironmentOptions = {}, -): ClineEnvironment { - const env = options.env ?? readProcessEnv(); +export function resolveClineEnvironment(): ClineEnvironment { + const env = readProcessEnv(); return ( normalizeClineEnvironment(env[CLINE_ENVIRONMENT_OVERRIDE_ENV]) ?? normalizeClineEnvironment(env[CLINE_ENVIRONMENT_ENV]) ?? @@ -78,11 +76,32 @@ export function resolveClineEnvironment( ); } -export function getClineEnvironmentConfig( - environmentOrOptions?: ClineEnvironment | ResolveClineEnvironmentOptions, -): ClineEnvironmentConfig { - if (typeof environmentOrOptions === "string") { - return CLINE_ENVIRONMENTS[environmentOrOptions]; +function getEnvConfig(env?: ClineEnvironment) { + if (typeof env === "string") { + return CLINE_ENVIRONMENTS[env]; } - return CLINE_ENVIRONMENTS[resolveClineEnvironment(environmentOrOptions)]; + return CLINE_ENVIRONMENTS[resolveClineEnvironment()]; +} + +function applyConfigOverrides( + config: ClineEnvironmentConfig, + env: NodeJS.ProcessEnv, +): ClineEnvironmentConfig { + if (env.CLINE_API_BASE_URL) { + config = { + ...config, + apiBaseUrl: env.CLINE_API_BASE_URL, + mcpBaseUrl: `${env.CLINE_API_BASE_URL}/v1/mcp`, + }; + } + + return config; +} + +export function getClineEnvironmentConfig( + env?: ClineEnvironment, +): ClineEnvironmentConfig { + const config = getEnvConfig(env); + + return applyConfigOverrides(config, readProcessEnv()); }