Skip to content

Commit 692afd9

Browse files
lpcoxCopilot
andauthored
refactor: extract Copilot API resolver from cli.ts (#2624) (#2636)
Extract resolveCopilotApiKey, deriveCopilotApiTargetFromProviderBaseUrl, deriveCopilotApiBasePathFromProviderBaseUrl, and resolveCopilotApiRouting into src/copilot-api-resolver.ts (84 lines). cli.ts re-exports all symbols for backwards compatibility. All 32 cli tests pass. Build passes. Closes #2624 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 1412c72 commit 692afd9

2 files changed

Lines changed: 96 additions & 84 deletions

File tree

src/cli.ts

Lines changed: 12 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -126,92 +126,20 @@ import {
126126
} from './option-parsers';
127127
import { processAgentImageOption } from './domain-utils';
128128

129-
export const program = new Command();
130-
131-
/**
132-
* Resolve the Copilot BYOK key from supported environment variables.
133-
* COPILOT_API_KEY takes precedence over COPILOT_PROVIDER_API_KEY.
134-
*/
135-
export function resolveCopilotApiKey(
136-
env: Record<string, string | undefined> = process.env
137-
): string | undefined {
138-
return env.COPILOT_API_KEY || env.COPILOT_PROVIDER_API_KEY;
139-
}
140-
141-
/**
142-
* Derive a Copilot API target hostname from COPILOT_PROVIDER_BASE_URL.
143-
* Returns undefined when the value is empty or not a valid URL/host.
144-
*/
145-
export function deriveCopilotApiTargetFromProviderBaseUrl(
146-
providerBaseUrl: string | undefined
147-
): string | undefined {
148-
const trimmed = providerBaseUrl?.trim();
149-
if (!trimmed) return undefined;
150-
151-
const candidate = trimmed.includes('://')
152-
? trimmed
153-
: `https://${trimmed}`;
154-
155-
try {
156-
return new URL(candidate).hostname || undefined;
157-
} catch {
158-
return undefined;
159-
}
160-
}
161-
162-
/**
163-
* Derive a Copilot API base-path prefix from COPILOT_PROVIDER_BASE_URL.
164-
* Returns undefined when the value is empty, invalid, or has no path.
165-
*/
166-
export function deriveCopilotApiBasePathFromProviderBaseUrl(
167-
providerBaseUrl: string | undefined
168-
): string | undefined {
169-
const trimmed = providerBaseUrl?.trim();
170-
if (!trimmed) return undefined;
171-
172-
const candidate = trimmed.includes('://')
173-
? trimmed
174-
: `https://${trimmed}`;
129+
import {
130+
resolveCopilotApiKey,
131+
resolveCopilotApiRouting,
132+
} from './copilot-api-resolver';
175133

176-
try {
177-
const pathname = new URL(candidate).pathname.replace(/\/+$/, '');
178-
if (!pathname || pathname === '/') return undefined;
179-
return pathname.startsWith('/') ? pathname : `/${pathname}`;
180-
} catch {
181-
return undefined;
182-
}
183-
}
134+
// Re-export for backwards compatibility (used by cli.test.ts and other consumers)
135+
export {
136+
resolveCopilotApiKey,
137+
deriveCopilotApiTargetFromProviderBaseUrl,
138+
deriveCopilotApiBasePathFromProviderBaseUrl,
139+
resolveCopilotApiRouting,
140+
} from './copilot-api-resolver';
184141

185-
/**
186-
* Resolve Copilot target/base-path routing for BYOK provider-style env vars.
187-
*
188-
* Target precedence:
189-
* 1. --copilot-api-target
190-
* 2. COPILOT_API_TARGET
191-
* 3. Hostname from COPILOT_PROVIDER_BASE_URL
192-
*
193-
* Base path precedence:
194-
* 1. COPILOT_API_BASE_PATH
195-
* 2. Pathname from COPILOT_PROVIDER_BASE_URL
196-
*/
197-
export function resolveCopilotApiRouting(
198-
options: { copilotApiTarget?: string },
199-
env: Record<string, string | undefined> = process.env
200-
): { copilotApiTarget?: string; copilotApiBasePath?: string } {
201-
const providerBaseUrl = env.COPILOT_PROVIDER_BASE_URL;
202-
const copilotApiTargetFromProviderBaseUrl = deriveCopilotApiTargetFromProviderBaseUrl(providerBaseUrl);
203-
const copilotApiBasePathFromProviderBaseUrl = deriveCopilotApiBasePathFromProviderBaseUrl(providerBaseUrl);
204-
205-
return {
206-
copilotApiTarget:
207-
options.copilotApiTarget ||
208-
env.COPILOT_API_TARGET ||
209-
copilotApiTargetFromProviderBaseUrl,
210-
copilotApiBasePath:
211-
env.COPILOT_API_BASE_PATH ||
212-
copilotApiBasePathFromProviderBaseUrl,
213-
};
214-
}
142+
export const program = new Command();
215143

216144
// Option group markers used by the custom help formatter to insert section headers.
217145
// Each key is the long flag name of the first option in a group.

src/copilot-api-resolver.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* Resolve the Copilot BYOK key from supported environment variables.
3+
* COPILOT_API_KEY takes precedence over COPILOT_PROVIDER_API_KEY.
4+
*/
5+
export function resolveCopilotApiKey(
6+
env: Record<string, string | undefined> = process.env
7+
): string | undefined {
8+
return env.COPILOT_API_KEY || env.COPILOT_PROVIDER_API_KEY;
9+
}
10+
11+
/**
12+
* Derive a Copilot API target hostname from COPILOT_PROVIDER_BASE_URL.
13+
* Returns undefined when the value is empty or not a valid URL/host.
14+
*/
15+
export function deriveCopilotApiTargetFromProviderBaseUrl(
16+
providerBaseUrl: string | undefined
17+
): string | undefined {
18+
const trimmed = providerBaseUrl?.trim();
19+
if (!trimmed) return undefined;
20+
21+
const candidate = trimmed.includes('://')
22+
? trimmed
23+
: `https://${trimmed}`;
24+
25+
try {
26+
return new URL(candidate).hostname || undefined;
27+
} catch {
28+
return undefined;
29+
}
30+
}
31+
32+
/**
33+
* Derive a Copilot API base-path prefix from COPILOT_PROVIDER_BASE_URL.
34+
* Returns undefined when the value is empty, invalid, or has no path.
35+
*/
36+
export function deriveCopilotApiBasePathFromProviderBaseUrl(
37+
providerBaseUrl: string | undefined
38+
): string | undefined {
39+
const trimmed = providerBaseUrl?.trim();
40+
if (!trimmed) return undefined;
41+
42+
const candidate = trimmed.includes('://')
43+
? trimmed
44+
: `https://${trimmed}`;
45+
46+
try {
47+
const pathname = new URL(candidate).pathname.replace(/\/+$/, '');
48+
if (!pathname || pathname === '/') return undefined;
49+
return pathname.startsWith('/') ? pathname : `/${pathname}`;
50+
} catch {
51+
return undefined;
52+
}
53+
}
54+
55+
/**
56+
* Resolve Copilot target/base-path routing for BYOK provider-style env vars.
57+
*
58+
* Target precedence:
59+
* 1. --copilot-api-target
60+
* 2. COPILOT_API_TARGET
61+
* 3. Hostname from COPILOT_PROVIDER_BASE_URL
62+
*
63+
* Base path precedence:
64+
* 1. COPILOT_API_BASE_PATH
65+
* 2. Pathname from COPILOT_PROVIDER_BASE_URL
66+
*/
67+
export function resolveCopilotApiRouting(
68+
options: { copilotApiTarget?: string },
69+
env: Record<string, string | undefined> = process.env
70+
): { copilotApiTarget?: string; copilotApiBasePath?: string } {
71+
const providerBaseUrl = env.COPILOT_PROVIDER_BASE_URL;
72+
const copilotApiTargetFromProviderBaseUrl = deriveCopilotApiTargetFromProviderBaseUrl(providerBaseUrl);
73+
const copilotApiBasePathFromProviderBaseUrl = deriveCopilotApiBasePathFromProviderBaseUrl(providerBaseUrl);
74+
75+
return {
76+
copilotApiTarget:
77+
options.copilotApiTarget ||
78+
env.COPILOT_API_TARGET ||
79+
copilotApiTargetFromProviderBaseUrl,
80+
copilotApiBasePath:
81+
env.COPILOT_API_BASE_PATH ||
82+
copilotApiBasePathFromProviderBaseUrl,
83+
};
84+
}

0 commit comments

Comments
 (0)