Duplicate Code Opportunity
Summary
- Pattern: API proxy credential/target/base-path environment variable names are declared in multiple places: TypeScript wrapper config assembly and JavaScript provider adapters. The same matrix (
*_API_KEY, *_API_TARGET, *_API_BASE_PATH, auth-header overrides, BYOK fields) is repeated as string literals.
- Locations:
src/commands/build-config.ts lines 170-201; containers/api-proxy/providers/openai.js lines 32-37, 40-49; containers/api-proxy/providers/anthropic.js lines 45-52; containers/api-proxy/providers/gemini.js lines 27-32; containers/api-proxy/providers/copilot.js lines 53-59.
- Impact: The wrapper decides which host credentials/config values enter the isolated API proxy, while provider adapters decide how those same values are consumed. Duplicating the env matrix increases the chance that a new provider setting is forwarded but not read, read but not forwarded, or documented inconsistently.
Evidence
The wrapper copies API proxy env/config values into WrapperConfig using repeated string literals:
openaiApiKey: process.env.OPENAI_API_KEY,
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
copilotGithubToken: process.env.COPILOT_GITHUB_TOKEN,
copilotProviderApiKey: process.env.COPILOT_PROVIDER_API_KEY,
copilotProviderType:
(options.copilotProviderType as string | undefined) || process.env.COPILOT_PROVIDER_TYPE,
copilotProviderBaseUrl:
(options.copilotProviderBaseUrl as string | undefined) || process.env.COPILOT_PROVIDER_BASE_URL,
geminiApiKey: process.env.GEMINI_API_KEY,
// ...
openaiApiTarget:
(options.openaiApiTarget as string | undefined) || process.env.OPENAI_API_TARGET,
openaiApiBasePath:
(options.openaiApiBasePath as string | undefined) || process.env.OPENAI_API_BASE_PATH,
anthropicApiTarget:
(options.anthropicApiTarget as string | undefined) || process.env.ANTHROPIC_API_TARGET,
anthropicApiBasePath:
(options.anthropicApiBasePath as string | undefined) || process.env.ANTHROPIC_API_BASE_PATH,
openaiApiAuthHeader:
(options.openaiApiAuthHeader as string | undefined) || process.env.AWF_OPENAI_AUTH_HEADER,
anthropicApiAuthHeader:
(options.anthropicApiAuthHeader as string | undefined) || process.env.AWF_ANTHROPIC_AUTH_HEADER,
geminiApiTarget:
(options.geminiApiTarget as string | undefined) || process.env.GEMINI_API_TARGET,
geminiApiBasePath:
(options.geminiApiBasePath as string | undefined) || process.env.GEMINI_API_BASE_PATH,
Provider adapters repeat the same env names when consuming the values:
const { apiKey: openaiApiKey, rawTarget: openaiTarget, basePath: openaiBasePath } = createBaseAdapterConfig(env, {
keyEnvVar: 'OPENAI_API_KEY',
targetEnvVar: 'OPENAI_API_TARGET',
basePathEnvVar: 'OPENAI_API_BASE_PATH',
defaultTarget: 'api.openai.com',
});
const customAuthHeader = (() => {
const header = validateAuthHeaderEnv('AWF_OPENAI_AUTH_HEADER', env.AWF_OPENAI_AUTH_HEADER);
// ...
})();
const { apiKey, rawTarget, basePath } = createBaseAdapterConfig(env, {
keyEnvVar: 'ANTHROPIC_API_KEY',
targetEnvVar: 'ANTHROPIC_API_TARGET',
basePathEnvVar: 'ANTHROPIC_API_BASE_PATH',
defaultTarget: 'api.anthropic.com',
});
const authHeaderName = validateAuthHeaderEnv('AWF_ANTHROPIC_AUTH_HEADER', env.AWF_ANTHROPIC_AUTH_HEADER, 'x-api-key');
const { apiKey, rawTarget, basePath } = createBaseAdapterConfig(env, {
keyEnvVar: 'GEMINI_API_KEY',
targetEnvVar: 'GEMINI_API_TARGET',
basePathEnvVar: 'GEMINI_API_BASE_PATH',
defaultTarget: 'generativelanguage.googleapis.com',
});
Suggested Refactoring
Introduce a single provider environment matrix, for example src/api-proxy-provider-env.ts (and generated/JSON-exported data for the container JS side), describing each provider's:
- key env var
- target env var
- base path env var
- auth-header override env var, where supported
- default upstream target
- wrapper option field names
Use it in build-config.ts to assemble wrapper config and in containers/api-proxy/providers/* to call createBaseAdapterConfig(). If sharing TS directly with container JS is not practical, generate a small checked-in JSON/CommonJS constants file during build so both sides consume the same source of truth.
Affected Files
src/commands/build-config.ts — lines 170-201
containers/api-proxy/providers/openai.js — lines 32-37, 40-49
containers/api-proxy/providers/anthropic.js — lines 45-52
containers/api-proxy/providers/gemini.js — lines 27-32
containers/api-proxy/providers/copilot.js — lines 53-59
Effort Estimate
Medium
Detected by Duplicate Code Detector workflow. Run date: 2026-06-17
Generated by Duplicate Code Detector · ◷
Duplicate Code Opportunity
Summary
*_API_KEY,*_API_TARGET,*_API_BASE_PATH, auth-header overrides, BYOK fields) is repeated as string literals.src/commands/build-config.tslines 170-201;containers/api-proxy/providers/openai.jslines 32-37, 40-49;containers/api-proxy/providers/anthropic.jslines 45-52;containers/api-proxy/providers/gemini.jslines 27-32;containers/api-proxy/providers/copilot.jslines 53-59.Evidence
The wrapper copies API proxy env/config values into
WrapperConfigusing repeated string literals:Provider adapters repeat the same env names when consuming the values:
Suggested Refactoring
Introduce a single provider environment matrix, for example
src/api-proxy-provider-env.ts(and generated/JSON-exported data for the container JS side), describing each provider's:Use it in
build-config.tsto assemble wrapper config and incontainers/api-proxy/providers/*to callcreateBaseAdapterConfig(). If sharing TS directly with container JS is not practical, generate a small checked-in JSON/CommonJS constants file during build so both sides consume the same source of truth.Affected Files
src/commands/build-config.ts— lines 170-201containers/api-proxy/providers/openai.js— lines 32-37, 40-49containers/api-proxy/providers/anthropic.js— lines 45-52containers/api-proxy/providers/gemini.js— lines 27-32containers/api-proxy/providers/copilot.js— lines 53-59Effort Estimate
Medium
Detected by Duplicate Code Detector workflow. Run date: 2026-06-17