Skip to content

Commit 4a5e3ad

Browse files
committed
improve override logic
make it more aesthetically pleasing
1 parent f8386ce commit 4a5e3ad

File tree

2 files changed

+183
-32
lines changed

2 files changed

+183
-32
lines changed

cli/src/config/__tests__/env-overrides.test.ts

Lines changed: 128 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect, beforeEach, afterEach } from "vitest"
2-
import { applyEnvOverrides, PROVIDER_ENV_VAR, PROVIDER_OVERRIDE_PREFIX } from "../env-overrides.js"
2+
import { applyEnvOverrides, PROVIDER_ENV_VAR, KILOCODE_PREFIX, KILO_PREFIX } from "../env-overrides.js"
33
import type { CLIConfig } from "../types.js"
44

55
describe("env-overrides", () => {
@@ -10,6 +10,13 @@ describe("env-overrides", () => {
1010
// Reset environment variables before each test
1111
process.env = { ...originalEnv }
1212

13+
// Clear any KILOCODE_* or KILO_* environment variables to ensure clean test state
14+
for (const key of Object.keys(process.env)) {
15+
if (key.startsWith(KILOCODE_PREFIX) || key.startsWith(KILO_PREFIX)) {
16+
delete process.env[key]
17+
}
18+
}
19+
1320
// Create a test config
1421
testConfig = {
1522
version: "1.0.0",
@@ -70,24 +77,97 @@ describe("env-overrides", () => {
7077
})
7178
})
7279

73-
describe("KILO_PROVIDER_OVERRIDE_* overrides", () => {
74-
it("should override any field in current provider", () => {
75-
process.env[`${PROVIDER_OVERRIDE_PREFIX}kilocodeModel`] = "anthropic/claude-opus-4.0"
76-
process.env[`${PROVIDER_OVERRIDE_PREFIX}kilocodeOrganizationId`] = "new-org-id"
80+
describe("KILOCODE_* overrides for kilocode provider", () => {
81+
it("should transform KILOCODE_MODEL to kilocodeModel", () => {
82+
process.env[`${KILOCODE_PREFIX}MODEL`] = "anthropic/claude-opus-4.0"
83+
84+
const result = applyEnvOverrides(testConfig)
85+
86+
const provider = result.providers.find((p) => p.id === "default")
87+
expect(provider?.kilocodeModel).toBe("anthropic/claude-opus-4.0")
88+
})
89+
90+
it("should transform KILOCODE_ORGANIZATION_ID to kilocodeOrganizationId", () => {
91+
process.env[`${KILOCODE_PREFIX}ORGANIZATION_ID`] = "new-org-id"
92+
93+
const result = applyEnvOverrides(testConfig)
94+
95+
const provider = result.providers.find((p) => p.id === "default")
96+
expect(provider?.kilocodeOrganizationId).toBe("new-org-id")
97+
})
98+
99+
it("should handle multiple KILOCODE_* overrides", () => {
100+
process.env[`${KILOCODE_PREFIX}MODEL`] = "anthropic/claude-opus-4.0"
101+
process.env[`${KILOCODE_PREFIX}ORGANIZATION_ID`] = "new-org-id"
102+
process.env[`${KILOCODE_PREFIX}TOKEN`] = "new-token"
77103

78104
const result = applyEnvOverrides(testConfig)
79105

80106
const provider = result.providers.find((p) => p.id === "default")
81107
expect(provider?.kilocodeModel).toBe("anthropic/claude-opus-4.0")
82108
expect(provider?.kilocodeOrganizationId).toBe("new-org-id")
109+
expect(provider?.kilocodeToken).toBe("new-token")
110+
})
111+
})
112+
113+
describe("KILO_* overrides for non-kilocode providers", () => {
114+
it("should transform KILO_API_KEY to apiKey for non-kilocode provider", () => {
115+
process.env[PROVIDER_ENV_VAR] = "anthropic-provider"
116+
process.env[`${KILO_PREFIX}API_KEY`] = "new-key"
117+
118+
const result = applyEnvOverrides(testConfig)
119+
120+
expect(result.provider).toBe("anthropic-provider")
121+
const provider = result.providers.find((p) => p.id === "anthropic-provider")
122+
expect(provider?.apiKey).toBe("new-key")
123+
})
124+
125+
it("should transform KILO_API_MODEL_ID to apiModelId", () => {
126+
process.env[PROVIDER_ENV_VAR] = "anthropic-provider"
127+
process.env[`${KILO_PREFIX}API_MODEL_ID`] = "claude-3-opus-20240229"
128+
129+
const result = applyEnvOverrides(testConfig)
130+
131+
const provider = result.providers.find((p) => p.id === "anthropic-provider")
132+
expect(provider?.apiModelId).toBe("claude-3-opus-20240229")
133+
})
134+
135+
it("should transform KILO_BASE_URL to baseUrl", () => {
136+
process.env[PROVIDER_ENV_VAR] = "anthropic-provider"
137+
process.env[`${KILO_PREFIX}BASE_URL`] = "https://api.example.com"
138+
139+
const result = applyEnvOverrides(testConfig)
140+
141+
const provider = result.providers.find((p) => p.id === "anthropic-provider")
142+
expect(provider?.baseUrl).toBe("https://api.example.com")
143+
})
144+
145+
it("should not apply KILO_* overrides to kilocode provider", () => {
146+
process.env[PROVIDER_ENV_VAR] = "kilocode"
147+
process.env[`${KILO_PREFIX}API_KEY`] = "should-not-apply"
148+
149+
const result = applyEnvOverrides(testConfig)
150+
151+
const provider = result.providers.find((p) => p.id === "default")
152+
expect(provider?.apiKey).toBeUndefined()
153+
})
154+
155+
it("should not apply KILOCODE_* overrides to non-kilocode provider", () => {
156+
process.env[PROVIDER_ENV_VAR] = "anthropic-provider"
157+
process.env[`${KILOCODE_PREFIX}MODEL`] = "should-not-apply"
158+
159+
const result = applyEnvOverrides(testConfig)
160+
161+
const provider = result.providers.find((p) => p.id === "anthropic-provider")
162+
expect(provider?.kilocodeModel).toBeUndefined()
83163
})
84164
})
85165

86166
describe("Combined overrides", () => {
87-
it("should apply both provider and field overrides together", () => {
167+
it("should apply both provider and field overrides together for non-kilocode provider", () => {
88168
process.env[PROVIDER_ENV_VAR] = "anthropic-provider"
89-
process.env[`${PROVIDER_OVERRIDE_PREFIX}apiModelId`] = "claude-3-opus-20240229"
90-
process.env[`${PROVIDER_OVERRIDE_PREFIX}apiKey`] = "new-key"
169+
process.env[`${KILO_PREFIX}API_MODEL_ID`] = "claude-3-opus-20240229"
170+
process.env[`${KILO_PREFIX}API_KEY`] = "new-key"
91171

92172
const result = applyEnvOverrides(testConfig)
93173

@@ -96,6 +176,19 @@ describe("env-overrides", () => {
96176
expect(provider?.apiModelId).toBe("claude-3-opus-20240229")
97177
expect(provider?.apiKey).toBe("new-key")
98178
})
179+
180+
it("should apply both provider and field overrides together for kilocode provider", () => {
181+
process.env[PROVIDER_ENV_VAR] = "default"
182+
process.env[`${KILOCODE_PREFIX}MODEL`] = "anthropic/claude-opus-4.0"
183+
process.env[`${KILOCODE_PREFIX}ORGANIZATION_ID`] = "new-org-id"
184+
185+
const result = applyEnvOverrides(testConfig)
186+
187+
expect(result.provider).toBe("default")
188+
const provider = result.providers.find((p) => p.id === "default")
189+
expect(provider?.kilocodeModel).toBe("anthropic/claude-opus-4.0")
190+
expect(provider?.kilocodeOrganizationId).toBe("new-org-id")
191+
})
99192
})
100193

101194
describe("Edge cases", () => {
@@ -115,8 +208,8 @@ describe("env-overrides", () => {
115208
expect(result).toEqual(testConfig)
116209
})
117210

118-
it("should handle empty string override values", () => {
119-
process.env[`${PROVIDER_OVERRIDE_PREFIX}apiModelId`] = ""
211+
it("should handle empty string override values for KILOCODE_*", () => {
212+
process.env[`${KILOCODE_PREFIX}MODEL`] = ""
120213

121214
const result = applyEnvOverrides(testConfig)
122215

@@ -125,13 +218,36 @@ describe("env-overrides", () => {
125218
expect(provider?.kilocodeModel).toBe("anthropic/claude-sonnet-4.5")
126219
})
127220

128-
it("should ignore KILO_PROVIDER_OVERRIDE_ with no field name", () => {
129-
process.env[PROVIDER_OVERRIDE_PREFIX] = "value"
221+
it("should handle empty string override values for KILO_*", () => {
222+
process.env[PROVIDER_ENV_VAR] = "anthropic-provider"
223+
process.env[`${KILO_PREFIX}API_KEY`] = ""
224+
225+
const result = applyEnvOverrides(testConfig)
226+
227+
// Empty strings should not trigger overrides
228+
const provider = result.providers.find((p) => p.id === "anthropic-provider")
229+
expect(provider?.apiKey).toBe("test-key")
230+
})
231+
232+
it("should ignore KILOCODE_ with no field name", () => {
233+
process.env[KILOCODE_PREFIX.slice(0, -1)] = "value"
130234

131235
const result = applyEnvOverrides(testConfig)
132236

133237
// Should not modify anything
134238
expect(result).toEqual(testConfig)
135239
})
240+
241+
it("should ignore KILO_PROVIDER since it's handled separately", () => {
242+
process.env[PROVIDER_ENV_VAR] = "anthropic-provider"
243+
244+
const result = applyEnvOverrides(testConfig)
245+
246+
// KILO_PROVIDER should change the provider but not add a 'provider' field
247+
expect(result.provider).toBe("anthropic-provider")
248+
249+
const provider = result.providers.find((p) => p.id === "anthropic-provider")
250+
expect(provider?.provider).toBe("anthropic") // Original value
251+
})
136252
})
137253
})

cli/src/config/env-overrides.ts

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,37 @@ import type { CLIConfig } from "./types.js"
22
import { logs } from "../services/logs.js"
33

44
/**
5-
* Environment variable prefix for provider field overrides
5+
* Environment variable name for provider selection
66
*/
7-
export const PROVIDER_OVERRIDE_PREFIX = "KILO_PROVIDER_OVERRIDE_"
7+
export const PROVIDER_ENV_VAR = "KILO_PROVIDER"
88

99
/**
10-
* Environment variable name for provider selection
10+
* Environment variable prefix for Kilocode provider
1111
*/
12-
export const PROVIDER_ENV_VAR = "KILO_PROVIDER"
12+
export const KILOCODE_PREFIX = "KILOCODE_"
13+
14+
/**
15+
* Environment variable prefix for other providers
16+
*/
17+
export const KILO_PREFIX = "KILO_"
18+
19+
const specificEnvVars = new Set([PROVIDER_ENV_VAR])
1320

1421
/**
1522
* Apply environment variable overrides to the config
1623
* Overrides the current provider's settings based on environment variables
1724
*
1825
* Environment variables:
1926
* - KILO_PROVIDER: Override the active provider ID
20-
* - KILO_PROVIDER_OVERRIDE_<fieldName>: Override any field in the current provider
27+
* - For Kilocode provider: KILOCODE_<FIELD_NAME> (e.g., KILOCODE_MODEL → kilocodeModel)
28+
* Examples:
29+
* - KILOCODE_MODEL → kilocodeModel
30+
* - KILOCODE_ORGANIZATION_ID → kilocodeOrganizationId
31+
* - For other providers: KILO_<FIELD_NAME> (e.g., KILO_API_KEY → apiKey)
2132
* Examples:
22-
* - KILO_PROVIDER_OVERRIDE_apiModelId
23-
* - KILO_PROVIDER_OVERRIDE_kilocodeModel
24-
* - KILO_PROVIDER_OVERRIDE_kilocodeOrganizationId
25-
* - KILO_PROVIDER_OVERRIDE_apiKey
33+
* - KILO_API_KEY → apiKey
34+
* - KILO_BASE_URL → baseUrl
35+
* - KILO_API_MODEL_ID → apiModelId
2636
*
2737
* @param config The config to apply overrides to
2838
* @returns The config with environment variable overrides applied
@@ -57,8 +67,8 @@ export function applyEnvOverrides(config: CLIConfig): CLIConfig {
5767
return overriddenConfig
5868
}
5969

60-
// Find all KILO_PROVIDER_OVERRIDE_* environment variables
61-
const overrideFields = getProviderOverrideFields()
70+
// Find all environment variable overrides for the current provider
71+
const overrideFields = getProviderOverrideFields(currentProvider.provider)
6272

6373
if (overrideFields.length > 0) {
6474
// Create a new providers array with the updated provider
@@ -71,7 +81,7 @@ export function applyEnvOverrides(config: CLIConfig): CLIConfig {
7181
updatedProvider[fieldName] = value
7282

7383
logs.info(
74-
`Config override: ${fieldName} set to "${value}" from ${PROVIDER_OVERRIDE_PREFIX}${fieldName} for provider "${currentProvider.id}"`,
84+
`Config override: ${fieldName} set to "${value}" for provider "${currentProvider.id}"`,
7585
"EnvOverrides",
7686
)
7787
}
@@ -87,18 +97,43 @@ export function applyEnvOverrides(config: CLIConfig): CLIConfig {
8797
}
8898

8999
/**
90-
* Get all KILO_PROVIDER_OVERRIDE_* environment variables
100+
* Convert snake_case or SCREAMING_SNAKE_CASE to camelCase
101+
* Examples:
102+
* - API_KEY → apiKey
103+
* - BASE_URL → baseUrl
104+
* - API_MODEL_ID → apiModelId
105+
* - ORGANIZATION_ID → organizationId
106+
*/
107+
function snakeToCamelCase(str: string): string {
108+
return str.toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
109+
}
110+
111+
/**
112+
* Get all environment variable overrides for the current provider
113+
* - For Kilocode provider: looks for KILOCODE_* vars and transforms to kilocodeXyz
114+
* - For other providers: looks for KILO_* vars (excluding KILO_PROVIDER) and transforms to xyzAbc
91115
* Returns an array of { fieldName, value } objects
92116
*/
93-
function getProviderOverrideFields(): Array<{ fieldName: string; value: string }> {
117+
function getProviderOverrideFields(provider: string): Array<{ fieldName: string; value: string }> {
94118
const overrides: Array<{ fieldName: string; value: string }> = []
95119

96-
for (const [key, value] of Object.entries(process.env)) {
97-
if (key.startsWith(PROVIDER_OVERRIDE_PREFIX) && value) {
98-
const fieldName = key.substring(PROVIDER_OVERRIDE_PREFIX.length)
99-
100-
if (fieldName) {
101-
overrides.push({ fieldName, value })
120+
if (provider === "kilocode") {
121+
// For Kilocode provider: KILOCODE_XYZ → kilocodeXyz
122+
for (const [key, value] of Object.entries(process.env)) {
123+
if (key.startsWith(KILOCODE_PREFIX) && value) {
124+
overrides.push({ fieldName: snakeToCamelCase(key), value })
125+
}
126+
}
127+
} else {
128+
// For other providers: KILO_XYZ_ABC → xyzAbc
129+
for (const [key, value] of Object.entries(process.env)) {
130+
if (key.startsWith(KILO_PREFIX) && !specificEnvVars.has(key) && value) {
131+
const remainder = key.substring(KILO_PREFIX.length)
132+
133+
if (remainder) {
134+
const fieldName = snakeToCamelCase(remainder)
135+
overrides.push({ fieldName, value })
136+
}
102137
}
103138
}
104139
}

0 commit comments

Comments
 (0)