pi2dsh 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/README.zh.md +16 -1
- package/dist/compat/pi-ai.d.mts +10 -1
- package/dist/compat/pi-ai.d.mts.map +1 -1
- package/dist/compat/pi-ai.mjs +2 -2918
- package/dist/credentials-oauth.d.mts +27 -0
- package/dist/credentials-oauth.d.mts.map +1 -0
- package/dist/credentials-oauth.mjs +91 -0
- package/dist/credentials-oauth.mjs.map +1 -0
- package/dist/host.mjs +3 -1
- package/dist/host.mjs.map +1 -1
- package/dist/oauth-bridge-BpPrppjR.mjs +359 -0
- package/dist/oauth-bridge-BpPrppjR.mjs.map +1 -0
- package/dist/pi-ai-CWFlgigJ.mjs +2947 -0
- package/dist/pi-ai-CWFlgigJ.mjs.map +1 -0
- package/dist/{runtime-oLd2EInK.mjs → runtime-6DefpI6h.mjs} +11 -355
- package/dist/runtime-6DefpI6h.mjs.map +1 -0
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -1
- package/package.json +11 -1
- package/dist/compat/pi-ai.mjs.map +0 -1
- package/dist/runtime-oLd2EInK.mjs.map +0 -1
package/dist/compat/pi-ai.mjs
CHANGED
|
@@ -1,2920 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
import { n as __require } from "../rolldown-runtime-D-uZhY3_.mjs";
|
|
3
2
|
import { c as uuidv7, t as typebox_exports } from "../build-DsGYAgiT.mjs";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* Regex patterns to detect context overflow errors from different providers.
|
|
7
|
-
*
|
|
8
|
-
* These patterns match error messages returned when the input exceeds
|
|
9
|
-
* the model's context window.
|
|
10
|
-
*
|
|
11
|
-
* Provider-specific patterns (with example error messages):
|
|
12
|
-
*
|
|
13
|
-
* - Anthropic: "prompt is too long: 213462 tokens > 200000 maximum"
|
|
14
|
-
* - Anthropic: "413 {\"error\":{\"type\":\"request_too_large\",\"message\":\"Request exceeds the maximum size\"}}"
|
|
15
|
-
* - OpenAI: "Your input exceeds the context window of this model"
|
|
16
|
-
* - OpenAI/LiteLLM: "Requested token count exceeds the model's maximum context length of 131072 tokens"
|
|
17
|
-
* - OpenAI-compatible: "Input length (265330) exceeds model's maximum context length (262144)."
|
|
18
|
-
* - Google: "The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)"
|
|
19
|
-
* - xAI: "This model's maximum prompt length is 131072 but the request contains 537812 tokens"
|
|
20
|
-
* - Groq: "Please reduce the length of the messages or completion"
|
|
21
|
-
* - OpenRouter: "This endpoint's maximum context length is X tokens. However, you requested about Y tokens"
|
|
22
|
-
* - OpenRouter/Poolside: "Input length X exceeds the maximum allowed input length of Y tokens."
|
|
23
|
-
* - Together AI: "The input (X tokens) is longer than the model's context length (Y tokens)."
|
|
24
|
-
* - llama.cpp: "the request exceeds the available context size, try increasing it"
|
|
25
|
-
* - LM Studio: "tokens to keep from the initial prompt is greater than the context length"
|
|
26
|
-
* - GitHub Copilot: "prompt token count of X exceeds the limit of Y"
|
|
27
|
-
* - MiniMax: "invalid params, context window exceeds limit"
|
|
28
|
-
* - Kimi For Coding: "Your request exceeded model token limit: X (requested: Y)"
|
|
29
|
-
* - DS4: "Prompt has X tokens, but the configured context size is Y tokens"
|
|
30
|
-
* - Cerebras: "400/413 status code (no body)"
|
|
31
|
-
* - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
|
|
32
|
-
* - z.ai: Does NOT error, accepts overflow silently - handled via usage.input > contextWindow
|
|
33
|
-
* - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason "length"
|
|
34
|
-
* with output=0 (no room left to generate). Detected via stopReason "length" + zero output +
|
|
35
|
-
* input filling the context window.
|
|
36
|
-
* - DashScope/Qwen: "Range of input length should be [1, X]" (HTTP 400 invalid_parameter_error)
|
|
37
|
-
* - Ollama: Some deployments truncate silently, others return errors like "prompt too long; exceeded max context length by X tokens"
|
|
38
|
-
*/
|
|
39
|
-
const OVERFLOW_PATTERNS = [
|
|
40
|
-
/prompt is too long/i,
|
|
41
|
-
/request_too_large/i,
|
|
42
|
-
/input is too long for requested model/i,
|
|
43
|
-
/exceeds the context window/i,
|
|
44
|
-
/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\d,]+ tokens?|\s*\([\d,]+\))/i,
|
|
45
|
-
/input token count.*exceeds the maximum/i,
|
|
46
|
-
/maximum prompt length is \d+/i,
|
|
47
|
-
/reduce the length of the messages/i,
|
|
48
|
-
/maximum context length is \d+ tokens/i,
|
|
49
|
-
/exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i,
|
|
50
|
-
/input \(\d+ tokens\) is longer than the model'?s context length \(\d+ tokens\)/i,
|
|
51
|
-
/exceeds the limit of \d+/i,
|
|
52
|
-
/exceeds the available context size/i,
|
|
53
|
-
/greater than the context length/i,
|
|
54
|
-
/context window exceeds limit/i,
|
|
55
|
-
/exceeded model token limit/i,
|
|
56
|
-
/too large for model with \d+ maximum context length/i,
|
|
57
|
-
/prompt has [\d,]+ tokens?, but the configured context size is [\d,]+ tokens?/i,
|
|
58
|
-
/model_context_window_exceeded/i,
|
|
59
|
-
/prompt too long; exceeded (?:max )?context length/i,
|
|
60
|
-
/range of input length should be/i,
|
|
61
|
-
/context[_ ]length[_ ]exceeded/i,
|
|
62
|
-
/too many tokens/i,
|
|
63
|
-
/token limit exceeded/i,
|
|
64
|
-
/^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i
|
|
65
|
-
];
|
|
66
|
-
/**
|
|
67
|
-
* Patterns that indicate non-overflow errors (e.g. rate limiting, server errors).
|
|
68
|
-
* Error messages matching any of these are excluded from overflow detection
|
|
69
|
-
* even if they also match an OVERFLOW_PATTERN.
|
|
70
|
-
*
|
|
71
|
-
* Example: Bedrock formats throttling errors as "ThrottlingException: Too many tokens,
|
|
72
|
-
* please wait before trying again." which would match the /too many tokens/i overflow
|
|
73
|
-
* pattern without this exclusion.
|
|
74
|
-
*/
|
|
75
|
-
const NON_OVERFLOW_PATTERNS = [
|
|
76
|
-
/^(Throttling error|Service unavailable):/i,
|
|
77
|
-
/rate limit/i,
|
|
78
|
-
/too many requests/i
|
|
79
|
-
];
|
|
80
|
-
/**
|
|
81
|
-
* Check if an assistant message represents a context overflow error.
|
|
82
|
-
*
|
|
83
|
-
* This handles three cases:
|
|
84
|
-
* 1. Error-based overflow: Most providers return stopReason "error" with a
|
|
85
|
-
* specific error message pattern.
|
|
86
|
-
* 2. Silent overflow: Some providers accept overflow requests and return
|
|
87
|
-
* successfully. For these, we check if usage.input exceeds the context window.
|
|
88
|
-
* 3. Length-stop overflow: Xiaomi MiMo can return "length" with zero output when
|
|
89
|
-
* the input fills the context window.
|
|
90
|
-
*
|
|
91
|
-
* ## Reliability by Provider
|
|
92
|
-
*
|
|
93
|
-
* **Reliable detection (returns error with detectable message):**
|
|
94
|
-
* - Anthropic: "prompt is too long: X tokens > Y maximum" or "request_too_large"
|
|
95
|
-
* - OpenAI (Completions & Responses): "exceeds the context window", "exceeds the model's maximum context length of X tokens", or "exceeds model's maximum context length (X)"
|
|
96
|
-
* - Google Gemini: "input token count exceeds the maximum"
|
|
97
|
-
* - xAI (Grok): "maximum prompt length is X but request contains Y"
|
|
98
|
-
* - Groq: "reduce the length of the messages"
|
|
99
|
-
* - Cerebras: 400/413 status code (no body)
|
|
100
|
-
* - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
|
|
101
|
-
* - OpenRouter (most backends): "maximum context length is X tokens"
|
|
102
|
-
* - OpenRouter/Poolside: "Input length X exceeds the maximum allowed input length of Y tokens."
|
|
103
|
-
* - Together AI: "The input (X tokens) is longer than the model's context length (Y tokens)."
|
|
104
|
-
* - llama.cpp: "exceeds the available context size"
|
|
105
|
-
* - LM Studio: "greater than the context length"
|
|
106
|
-
* - Kimi For Coding: "exceeded model token limit: X (requested: Y)"
|
|
107
|
-
* - DS4: "Prompt has X tokens, but the configured context size is Y tokens"
|
|
108
|
-
* - DashScope/Qwen: "Range of input length should be [1, X]"
|
|
109
|
-
*
|
|
110
|
-
* **Unreliable detection:**
|
|
111
|
-
* - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),
|
|
112
|
-
* sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.
|
|
113
|
-
* - Xiaomi MiMo: Truncates input to fit contextWindow then returns stopReason "length" with
|
|
114
|
-
* output=0. Pass contextWindow param to detect via the "filled context + zero output" signal.
|
|
115
|
-
* - Ollama: May truncate input silently for some setups, but may also return explicit
|
|
116
|
-
* overflow errors that match the patterns above. Silent truncation still cannot be
|
|
117
|
-
* detected here because we do not know the expected token count.
|
|
118
|
-
*
|
|
119
|
-
* ## Custom Providers
|
|
120
|
-
*
|
|
121
|
-
* If you've added custom models via settings.json, this function may not detect
|
|
122
|
-
* overflow errors from those providers. To add support:
|
|
123
|
-
*
|
|
124
|
-
* 1. Send a request that exceeds the model's context window
|
|
125
|
-
* 2. Check the errorMessage in the response
|
|
126
|
-
* 3. Create a regex pattern that matches the error
|
|
127
|
-
* 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or
|
|
128
|
-
* check the errorMessage yourself before calling this function
|
|
129
|
-
*
|
|
130
|
-
* @param message - The assistant message to check
|
|
131
|
-
* @param contextWindow - Optional context window size for detecting silent overflow (z.ai)
|
|
132
|
-
* @returns true if the message indicates a context overflow
|
|
133
|
-
*/
|
|
134
|
-
function isContextOverflow(message, contextWindow) {
|
|
135
|
-
if (message.stopReason === "error" && message.errorMessage) {
|
|
136
|
-
if (!NON_OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage)) && OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage))) return true;
|
|
137
|
-
}
|
|
138
|
-
if (contextWindow && message.stopReason === "stop") {
|
|
139
|
-
if (message.usage.input + message.usage.cacheRead > contextWindow) return true;
|
|
140
|
-
}
|
|
141
|
-
if (contextWindow && message.stopReason === "length" && message.usage.output === 0) {
|
|
142
|
-
if (message.usage.input + message.usage.cacheRead >= contextWindow * .99) return true;
|
|
143
|
-
}
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Check whether a length stop ended below the caller or model's intended output limit.
|
|
148
|
-
* Such responses may be caused by context pressure or provider-side truncation, so callers
|
|
149
|
-
* can make one bounded compact-and-retry attempt. `desiredMaxOutput` must be the original
|
|
150
|
-
* limit before any context-based clamping.
|
|
151
|
-
*/
|
|
152
|
-
function isRecoverableLength(message, desiredMaxOutput) {
|
|
153
|
-
return message.stopReason === "length" && desiredMaxOutput > 0 && message.usage.output < desiredMaxOutput;
|
|
154
|
-
}
|
|
155
|
-
//#endregion
|
|
156
|
-
//#region src/compat/vendor/pi-ai-retry.ts
|
|
157
|
-
function buildProviderErrorPattern(patterns) {
|
|
158
|
-
return new RegExp(patterns.join("|"), "i");
|
|
159
|
-
}
|
|
160
|
-
const NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([
|
|
161
|
-
"GoUsageLimitError",
|
|
162
|
-
"FreeUsageLimitError",
|
|
163
|
-
"Monthly usage limit reached",
|
|
164
|
-
"available balance",
|
|
165
|
-
"insufficient_quota",
|
|
166
|
-
"out of budget",
|
|
167
|
-
"quota exceeded",
|
|
168
|
-
"billing"
|
|
169
|
-
]);
|
|
170
|
-
const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
171
|
-
"overloaded",
|
|
172
|
-
"rate.?limit",
|
|
173
|
-
"too many requests",
|
|
174
|
-
"429",
|
|
175
|
-
"500",
|
|
176
|
-
"502",
|
|
177
|
-
"503",
|
|
178
|
-
"504",
|
|
179
|
-
"524",
|
|
180
|
-
"service.?unavailable",
|
|
181
|
-
"server.?error",
|
|
182
|
-
"internal.?error",
|
|
183
|
-
"provider.?returned.?error",
|
|
184
|
-
"exceeded request buffer limit while retrying upstream",
|
|
185
|
-
"network.?error",
|
|
186
|
-
"connection.?error",
|
|
187
|
-
"connection.?refused",
|
|
188
|
-
"connection.?lost",
|
|
189
|
-
"other side closed",
|
|
190
|
-
"fetch failed",
|
|
191
|
-
"getaddrinfo",
|
|
192
|
-
"ENOTFOUND",
|
|
193
|
-
"EAI_AGAIN",
|
|
194
|
-
"upstream.?connect",
|
|
195
|
-
"reset before headers",
|
|
196
|
-
"socket hang up",
|
|
197
|
-
"socket connection was closed",
|
|
198
|
-
"timed? out",
|
|
199
|
-
"timeout",
|
|
200
|
-
"terminated",
|
|
201
|
-
"websocket.?closed",
|
|
202
|
-
"websocket.?error",
|
|
203
|
-
"ended without",
|
|
204
|
-
"stream ended before message_stop",
|
|
205
|
-
"stream ended before a terminal response event",
|
|
206
|
-
"http2 request did not get a response",
|
|
207
|
-
"retry delay",
|
|
208
|
-
"you can retry your request",
|
|
209
|
-
"try your request again",
|
|
210
|
-
"please retry your request",
|
|
211
|
-
"ResourceExhausted"
|
|
212
|
-
]);
|
|
213
|
-
/**
|
|
214
|
-
* Classifies whether a failed assistant message looks like a transient provider
|
|
215
|
-
* or transport error, so callers can decide if the last assistant turn should be
|
|
216
|
-
* restarted.
|
|
217
|
-
*
|
|
218
|
-
* This does not implement retry policy. Callers should first handle context
|
|
219
|
-
* overflow separately, then apply their own retry budget, backoff, and reporting
|
|
220
|
-
* before restarting the assistant turn.
|
|
221
|
-
*/
|
|
222
|
-
function isRetryableAssistantError(message) {
|
|
223
|
-
if (message.stopReason !== "error" || !message.errorMessage) return false;
|
|
224
|
-
const errorMessage = message.errorMessage;
|
|
225
|
-
if (NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN.test(errorMessage)) return false;
|
|
226
|
-
return RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage);
|
|
227
|
-
}
|
|
228
|
-
//#endregion
|
|
229
|
-
//#region src/compat/vendor/pi-ai-event-stream.ts
|
|
230
|
-
var EventStream = class {
|
|
231
|
-
queue = [];
|
|
232
|
-
waiting = [];
|
|
233
|
-
done = false;
|
|
234
|
-
finalResultPromise;
|
|
235
|
-
resolveFinalResult;
|
|
236
|
-
isComplete;
|
|
237
|
-
extractResult;
|
|
238
|
-
constructor(isComplete, extractResult) {
|
|
239
|
-
this.isComplete = isComplete;
|
|
240
|
-
this.extractResult = extractResult;
|
|
241
|
-
this.finalResultPromise = new Promise((resolve) => {
|
|
242
|
-
this.resolveFinalResult = resolve;
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
push(event) {
|
|
246
|
-
if (this.done) return;
|
|
247
|
-
if (this.isComplete(event)) {
|
|
248
|
-
this.done = true;
|
|
249
|
-
this.resolveFinalResult(this.extractResult(event));
|
|
250
|
-
}
|
|
251
|
-
const waiter = this.waiting.shift();
|
|
252
|
-
if (waiter) waiter({
|
|
253
|
-
value: event,
|
|
254
|
-
done: false
|
|
255
|
-
});
|
|
256
|
-
else this.queue.push(event);
|
|
257
|
-
}
|
|
258
|
-
end(result) {
|
|
259
|
-
this.done = true;
|
|
260
|
-
if (result !== void 0) this.resolveFinalResult(result);
|
|
261
|
-
while (this.waiting.length > 0) this.waiting.shift()({
|
|
262
|
-
value: void 0,
|
|
263
|
-
done: true
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
async *[Symbol.asyncIterator]() {
|
|
267
|
-
while (true) if (this.queue.length > 0) yield this.queue.shift();
|
|
268
|
-
else if (this.done) return;
|
|
269
|
-
else {
|
|
270
|
-
const result = await new Promise((resolve) => this.waiting.push(resolve));
|
|
271
|
-
if (result.done) return;
|
|
272
|
-
yield result.value;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
result() {
|
|
276
|
-
return this.finalResultPromise;
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
var AssistantMessageEventStream = class extends EventStream {
|
|
280
|
-
constructor() {
|
|
281
|
-
super((event) => event.type === "done" || event.type === "error", (event) => {
|
|
282
|
-
if (event.type === "done") return event.message;
|
|
283
|
-
else if (event.type === "error") return event.error;
|
|
284
|
-
throw new Error("Unexpected event type for final result");
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
//#endregion
|
|
289
|
-
//#region src/compat/vendor/pi-ai-lazy.ts
|
|
290
|
-
function createSetupErrorMessage(model, error) {
|
|
291
|
-
return {
|
|
292
|
-
role: "assistant",
|
|
293
|
-
content: [],
|
|
294
|
-
api: model.api,
|
|
295
|
-
provider: model.provider,
|
|
296
|
-
model: model.id,
|
|
297
|
-
usage: {
|
|
298
|
-
input: 0,
|
|
299
|
-
output: 0,
|
|
300
|
-
cacheRead: 0,
|
|
301
|
-
cacheWrite: 0,
|
|
302
|
-
totalTokens: 0,
|
|
303
|
-
cost: {
|
|
304
|
-
input: 0,
|
|
305
|
-
output: 0,
|
|
306
|
-
cacheRead: 0,
|
|
307
|
-
cacheWrite: 0,
|
|
308
|
-
total: 0
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
stopReason: "error",
|
|
312
|
-
errorMessage: error instanceof Error ? error.message : String(error),
|
|
313
|
-
timestamp: Date.now()
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
function hasResult(source) {
|
|
317
|
-
return typeof source.result === "function";
|
|
318
|
-
}
|
|
319
|
-
async function forwardStream(target, source) {
|
|
320
|
-
for await (const event of source) target.push(event);
|
|
321
|
-
target.end(hasResult(source) ? await source.result() : void 0);
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Returns a stream synchronously while running async setup (auth resolution,
|
|
325
|
-
* lazy module loading) behind it. Setup failures terminate the stream with an
|
|
326
|
-
* error event.
|
|
327
|
-
*/
|
|
328
|
-
function lazyStream(model, setup) {
|
|
329
|
-
const outer = new AssistantMessageEventStream();
|
|
330
|
-
setup().then((inner) => forwardStream(outer, inner)).catch((error) => {
|
|
331
|
-
const message = createSetupErrorMessage(model, error);
|
|
332
|
-
outer.push({
|
|
333
|
-
type: "error",
|
|
334
|
-
reason: "error",
|
|
335
|
-
error: message
|
|
336
|
-
});
|
|
337
|
-
outer.end(message);
|
|
338
|
-
});
|
|
339
|
-
return outer;
|
|
340
|
-
}
|
|
341
|
-
function lazyApi(load, capabilities) {
|
|
342
|
-
const api = {
|
|
343
|
-
stream: (model, context, options) => lazyStream(model, async () => (await load()).stream(model, context, options)),
|
|
344
|
-
streamSimple: (model, context, options) => lazyStream(model, async () => (await load()).streamSimple(model, context, options))
|
|
345
|
-
};
|
|
346
|
-
if (capabilities?.fetchDeferred) api.fetchDeferred = (model, handle, options) => lazyStream(model, async () => {
|
|
347
|
-
const implementation = await load();
|
|
348
|
-
if (!implementation.fetchDeferred) throw new Error("API does not support deferred responses");
|
|
349
|
-
return implementation.fetchDeferred(model, handle, options);
|
|
350
|
-
});
|
|
351
|
-
if (capabilities?.cancelDeferred) api.cancelDeferred = async (model, handle, options) => {
|
|
352
|
-
const implementation = await load();
|
|
353
|
-
if (!implementation.cancelDeferred) throw new Error("API cannot cancel deferred responses");
|
|
354
|
-
await implementation.cancelDeferred(model, handle, options);
|
|
355
|
-
};
|
|
356
|
-
return api;
|
|
357
|
-
}
|
|
358
|
-
//#endregion
|
|
359
|
-
//#region src/compat/vendor/pi-ai-provider.ts
|
|
360
|
-
function withCauseDetail(message, cause) {
|
|
361
|
-
if (cause === void 0 || cause === null) return message;
|
|
362
|
-
const detail = formatThrownValue(cause).trim();
|
|
363
|
-
if (!detail || message.includes(detail)) return message;
|
|
364
|
-
return `${message}: ${detail}`;
|
|
365
|
-
}
|
|
366
|
-
var ModelsError = class extends Error {
|
|
367
|
-
code;
|
|
368
|
-
constructor(code, message, options) {
|
|
369
|
-
super(withCauseDetail(message, options?.cause), options);
|
|
370
|
-
this.name = "ModelsError";
|
|
371
|
-
this.code = code;
|
|
372
|
-
}
|
|
373
|
-
};
|
|
374
|
-
/**
|
|
375
|
-
* Builds a provider from parts. Built-in provider factories and models.json
|
|
376
|
-
* custom providers both go through this. A single `api` streams all models;
|
|
377
|
-
* an `api` map dispatches on `model.api`, and a model whose api has no entry
|
|
378
|
-
* produces a stream error.
|
|
379
|
-
*/
|
|
380
|
-
function createProvider(input) {
|
|
381
|
-
const baselineModels = input.models;
|
|
382
|
-
let dynamicModels = [];
|
|
383
|
-
const fetchModels = input.fetchModels;
|
|
384
|
-
const currentModels = () => {
|
|
385
|
-
const merged = [...baselineModels];
|
|
386
|
-
for (const model of dynamicModels) {
|
|
387
|
-
const index = merged.findIndex((entry) => entry.id === model.id);
|
|
388
|
-
if (index >= 0) merged[index] = model;
|
|
389
|
-
else merged.push(model);
|
|
390
|
-
}
|
|
391
|
-
return merged;
|
|
392
|
-
};
|
|
393
|
-
const single = typeof input.api.stream === "function" ? input.api : void 0;
|
|
394
|
-
const byApi = single ? void 0 : input.api;
|
|
395
|
-
const apiFor = (model) => single ?? byApi?.[model.api];
|
|
396
|
-
const dispatch = (model, run) => {
|
|
397
|
-
const streams = apiFor(model);
|
|
398
|
-
if (!streams) return lazyStream(model, async () => {
|
|
399
|
-
throw new ModelsError("stream", `Provider ${input.id} has no API implementation for "${model.api}"`);
|
|
400
|
-
});
|
|
401
|
-
return run(streams);
|
|
402
|
-
};
|
|
403
|
-
const provider = {
|
|
404
|
-
id: input.id,
|
|
405
|
-
name: input.name ?? input.id,
|
|
406
|
-
baseUrl: input.baseUrl,
|
|
407
|
-
headers: input.headers,
|
|
408
|
-
auth: input.auth,
|
|
409
|
-
getModels: currentModels,
|
|
410
|
-
refreshModels: fetchModels ? async (context) => {
|
|
411
|
-
if (context.stored) {
|
|
412
|
-
const restored = context.stored.models.filter((model) => model.provider === input.id).map((model) => model);
|
|
413
|
-
if (!await context.publish({ update: () => {
|
|
414
|
-
dynamicModels = restored;
|
|
415
|
-
} })) return;
|
|
416
|
-
}
|
|
417
|
-
if (!context.allowNetwork || context.signal.aborted) return;
|
|
418
|
-
const refreshed = await fetchModels(context);
|
|
419
|
-
if (context.signal.aborted) return;
|
|
420
|
-
await context.publish({
|
|
421
|
-
persist: {
|
|
422
|
-
models: refreshed,
|
|
423
|
-
checkedAt: Date.now()
|
|
424
|
-
},
|
|
425
|
-
update: () => {
|
|
426
|
-
dynamicModels = refreshed;
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
} : void 0,
|
|
430
|
-
filterModels: input.filterModels,
|
|
431
|
-
stream: (model, context, options) => dispatch(model, (streams) => streams.stream(model, context, options)),
|
|
432
|
-
streamSimple: (model, context, options) => dispatch(model, (streams) => streams.streamSimple(model, context, options))
|
|
433
|
-
};
|
|
434
|
-
const streams = single ? [single] : Object.values(byApi ?? {}).filter((entry) => entry !== void 0);
|
|
435
|
-
if (streams.some((entry) => entry.fetchDeferred !== void 0)) provider.fetchDeferred = (model, handle, options) => lazyStream(model, async () => {
|
|
436
|
-
const implementation = apiFor(model);
|
|
437
|
-
if (!implementation?.fetchDeferred) throw new ModelsError("provider", `Provider ${input.id} does not support deferred responses for "${model.api}"`);
|
|
438
|
-
return implementation.fetchDeferred(model, handle, options);
|
|
439
|
-
});
|
|
440
|
-
if (streams.some((entry) => entry.cancelDeferred !== void 0)) provider.cancelDeferred = async (model, handle, options) => {
|
|
441
|
-
const implementation = apiFor(model);
|
|
442
|
-
if (!implementation?.cancelDeferred) throw new ModelsError("provider", `Provider ${input.id} cannot cancel deferred responses for "${model.api}"`);
|
|
443
|
-
await implementation.cancelDeferred(model, handle, options);
|
|
444
|
-
};
|
|
445
|
-
return provider;
|
|
446
|
-
}
|
|
447
|
-
//#endregion
|
|
448
|
-
//#region src/compat/vendor/pi-oauth-flows/provider-env.ts
|
|
449
|
-
let procEnvCache = null;
|
|
450
|
-
/**
|
|
451
|
-
* Fallback for https://github.com/oven-sh/bun/issues/27802.
|
|
452
|
-
* Bun compiled binaries can expose an empty process.env inside Linux sandboxes
|
|
453
|
-
* even though /proc/self/environ contains the environment.
|
|
454
|
-
*
|
|
455
|
-
* This intentionally duplicates restoreSandboxEnv() in
|
|
456
|
-
* packages/coding-agent/src/bun/restore-sandbox-env.ts. The ai package can be
|
|
457
|
-
* used directly, without going through that entrypoint, so provider env lookup
|
|
458
|
-
* must not depend on process.env having been patched.
|
|
459
|
-
*/
|
|
460
|
-
function getBunSandboxEnvValue(name) {
|
|
461
|
-
if (typeof process === "undefined" || !process.versions?.bun || Object.keys(process.env).length > 0) return;
|
|
462
|
-
if (procEnvCache === null) {
|
|
463
|
-
procEnvCache = /* @__PURE__ */ new Map();
|
|
464
|
-
try {
|
|
465
|
-
const { readFileSync } = __require("node:fs");
|
|
466
|
-
const data = readFileSync("/proc/self/environ", "utf-8");
|
|
467
|
-
for (const entry of data.split("\0")) {
|
|
468
|
-
const idx = entry.indexOf("=");
|
|
469
|
-
if (idx > 0) procEnvCache.set(entry.slice(0, idx), entry.slice(idx + 1));
|
|
470
|
-
}
|
|
471
|
-
} catch {}
|
|
472
|
-
}
|
|
473
|
-
return procEnvCache.get(name);
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Resolve a provider env value from scoped overrides, normal process.env, then
|
|
477
|
-
* the duplicated Bun sandbox fallback for direct pi-ai consumers.
|
|
478
|
-
*/
|
|
479
|
-
function getProviderEnvValue(name, env) {
|
|
480
|
-
return env?.[name] || (typeof process !== "undefined" ? process.env[name] : void 0) || getBunSandboxEnvValue(name) || void 0;
|
|
481
|
-
}
|
|
482
|
-
//#endregion
|
|
483
|
-
//#region src/compat/vendor/pi-oauth-flows/device-code.ts
|
|
484
|
-
const CANCEL_MESSAGE = "Login cancelled";
|
|
485
|
-
const TIMEOUT_MESSAGE = "Device flow timed out";
|
|
486
|
-
const SLOW_DOWN_TIMEOUT_MESSAGE = "Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.";
|
|
487
|
-
const MINIMUM_INTERVAL_MS = 1e3;
|
|
488
|
-
const DEFAULT_POLL_INTERVAL_SECONDS$1 = 5;
|
|
489
|
-
const SLOW_DOWN_INTERVAL_INCREMENT_MS = 5e3;
|
|
490
|
-
function abortableSleep(ms, signal, cancelMessage) {
|
|
491
|
-
return new Promise((resolve, reject) => {
|
|
492
|
-
if (signal.aborted) {
|
|
493
|
-
reject(new Error(cancelMessage));
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
const onAbort = () => {
|
|
497
|
-
clearTimeout(timeout);
|
|
498
|
-
reject(new Error(cancelMessage));
|
|
499
|
-
};
|
|
500
|
-
const timeout = setTimeout(() => {
|
|
501
|
-
signal.removeEventListener("abort", onAbort);
|
|
502
|
-
resolve();
|
|
503
|
-
}, ms);
|
|
504
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
async function pollOAuthDeviceCodeFlow(options) {
|
|
508
|
-
const deadline = typeof options.expiresInSeconds === "number" ? Date.now() + options.expiresInSeconds * 1e3 : Number.POSITIVE_INFINITY;
|
|
509
|
-
let intervalMs = Math.max(MINIMUM_INTERVAL_MS, Math.floor((options.intervalSeconds ?? DEFAULT_POLL_INTERVAL_SECONDS$1) * 1e3));
|
|
510
|
-
let slowDownResponses = 0;
|
|
511
|
-
if (options.waitBeforeFirstPoll) {
|
|
512
|
-
const remainingMs = deadline - Date.now();
|
|
513
|
-
if (remainingMs > 0) await abortableSleep(Math.min(intervalMs, remainingMs), options.signal, CANCEL_MESSAGE);
|
|
514
|
-
}
|
|
515
|
-
while (Date.now() < deadline) {
|
|
516
|
-
if (options.signal.aborted) throw new Error(CANCEL_MESSAGE);
|
|
517
|
-
const result = await options.poll();
|
|
518
|
-
if (result.status === "complete") return result.value;
|
|
519
|
-
if (result.status === "failed") throw new Error(result.message);
|
|
520
|
-
if (result.status === "slow_down") {
|
|
521
|
-
slowDownResponses += 1;
|
|
522
|
-
intervalMs = typeof result.intervalSeconds === "number" && Number.isFinite(result.intervalSeconds) && result.intervalSeconds > 0 ? Math.max(MINIMUM_INTERVAL_MS, Math.floor(result.intervalSeconds * 1e3)) : Math.max(MINIMUM_INTERVAL_MS, intervalMs + SLOW_DOWN_INTERVAL_INCREMENT_MS);
|
|
523
|
-
}
|
|
524
|
-
const remainingMs = deadline - Date.now();
|
|
525
|
-
if (remainingMs <= 0) break;
|
|
526
|
-
await abortableSleep(Math.min(intervalMs, remainingMs), options.signal, CANCEL_MESSAGE);
|
|
527
|
-
}
|
|
528
|
-
throw new Error(slowDownResponses > 0 ? SLOW_DOWN_TIMEOUT_MESSAGE : TIMEOUT_MESSAGE);
|
|
529
|
-
}
|
|
530
|
-
//#endregion
|
|
531
|
-
//#region src/compat/vendor/pi-oauth-flows/oauth-page.ts
|
|
532
|
-
const LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>`;
|
|
533
|
-
function escapeHtml(value) {
|
|
534
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
535
|
-
}
|
|
536
|
-
function renderPage(options) {
|
|
537
|
-
const title = escapeHtml(options.title);
|
|
538
|
-
const heading = escapeHtml(options.heading);
|
|
539
|
-
const message = escapeHtml(options.message);
|
|
540
|
-
const details = options.details ? escapeHtml(options.details) : void 0;
|
|
541
|
-
return `<!doctype html>
|
|
542
|
-
<html lang="en">
|
|
543
|
-
<head>
|
|
544
|
-
<meta charset="utf-8" />
|
|
545
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
546
|
-
<title>${title}</title>
|
|
547
|
-
<style>
|
|
548
|
-
:root {
|
|
549
|
-
--text: #fafafa;
|
|
550
|
-
--text-dim: #a1a1aa;
|
|
551
|
-
--page-bg: #09090b;
|
|
552
|
-
--font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
553
|
-
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
554
|
-
}
|
|
555
|
-
* { box-sizing: border-box; }
|
|
556
|
-
html { color-scheme: dark; }
|
|
557
|
-
body {
|
|
558
|
-
margin: 0;
|
|
559
|
-
min-height: 100vh;
|
|
560
|
-
display: flex;
|
|
561
|
-
align-items: center;
|
|
562
|
-
justify-content: center;
|
|
563
|
-
padding: 24px;
|
|
564
|
-
background: var(--page-bg);
|
|
565
|
-
color: var(--text);
|
|
566
|
-
font-family: var(--font-sans);
|
|
567
|
-
text-align: center;
|
|
568
|
-
}
|
|
569
|
-
main {
|
|
570
|
-
width: 100%;
|
|
571
|
-
max-width: 560px;
|
|
572
|
-
display: flex;
|
|
573
|
-
flex-direction: column;
|
|
574
|
-
align-items: center;
|
|
575
|
-
justify-content: center;
|
|
576
|
-
}
|
|
577
|
-
.logo {
|
|
578
|
-
width: 72px;
|
|
579
|
-
height: 72px;
|
|
580
|
-
display: block;
|
|
581
|
-
margin-bottom: 24px;
|
|
582
|
-
}
|
|
583
|
-
h1 {
|
|
584
|
-
margin: 0 0 10px;
|
|
585
|
-
font-size: 28px;
|
|
586
|
-
line-height: 1.15;
|
|
587
|
-
font-weight: 650;
|
|
588
|
-
color: var(--text);
|
|
589
|
-
}
|
|
590
|
-
p {
|
|
591
|
-
margin: 0;
|
|
592
|
-
line-height: 1.7;
|
|
593
|
-
color: var(--text-dim);
|
|
594
|
-
font-size: 15px;
|
|
595
|
-
}
|
|
596
|
-
.details {
|
|
597
|
-
margin-top: 16px;
|
|
598
|
-
font-family: var(--font-mono);
|
|
599
|
-
font-size: 13px;
|
|
600
|
-
color: var(--text-dim);
|
|
601
|
-
white-space: pre-wrap;
|
|
602
|
-
word-break: break-word;
|
|
603
|
-
}
|
|
604
|
-
</style>
|
|
605
|
-
</head>
|
|
606
|
-
<body>
|
|
607
|
-
<main>
|
|
608
|
-
<div class="logo">${LOGO_SVG}</div>
|
|
609
|
-
<h1>${heading}</h1>
|
|
610
|
-
<p>${message}</p>
|
|
611
|
-
${details ? `<div class="details">${details}</div>` : ""}
|
|
612
|
-
</main>
|
|
613
|
-
</body>
|
|
614
|
-
</html>`;
|
|
615
|
-
}
|
|
616
|
-
function oauthSuccessHtml(message) {
|
|
617
|
-
return renderPage({
|
|
618
|
-
title: "Authentication successful",
|
|
619
|
-
heading: "Authentication successful",
|
|
620
|
-
message
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
function oauthErrorHtml(message, details) {
|
|
624
|
-
return renderPage({
|
|
625
|
-
title: "Authentication failed",
|
|
626
|
-
heading: "Authentication failed",
|
|
627
|
-
message,
|
|
628
|
-
details
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
//#endregion
|
|
632
|
-
//#region src/compat/vendor/pi-oauth-flows/pkce.ts
|
|
633
|
-
/**
|
|
634
|
-
* PKCE utilities using Web Crypto API.
|
|
635
|
-
* Works in both Node.js 20+ and browsers.
|
|
636
|
-
*/
|
|
637
|
-
/**
|
|
638
|
-
* Encode bytes as base64url string.
|
|
639
|
-
*/
|
|
640
|
-
function base64urlEncode(bytes) {
|
|
641
|
-
let binary = "";
|
|
642
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
643
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
644
|
-
}
|
|
645
|
-
/**
|
|
646
|
-
* Generate PKCE code verifier and challenge.
|
|
647
|
-
* Uses Web Crypto API for cross-platform compatibility.
|
|
648
|
-
*/
|
|
649
|
-
async function generatePKCE() {
|
|
650
|
-
const verifierBytes = /* @__PURE__ */ new Uint8Array(32);
|
|
651
|
-
crypto.getRandomValues(verifierBytes);
|
|
652
|
-
const verifier = base64urlEncode(verifierBytes);
|
|
653
|
-
const data = new TextEncoder().encode(verifier);
|
|
654
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
655
|
-
return {
|
|
656
|
-
verifier,
|
|
657
|
-
challenge: base64urlEncode(new Uint8Array(hashBuffer))
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
//#endregion
|
|
661
|
-
//#region src/compat/vendor/pi-oauth-flows/openai-codex.ts
|
|
662
|
-
/**
|
|
663
|
-
* OpenAI Codex (ChatGPT OAuth) flow
|
|
664
|
-
*
|
|
665
|
-
* NOTE: This module uses Node.js crypto and http for the OAuth callback.
|
|
666
|
-
* It is only intended for CLI use, not browser environments.
|
|
667
|
-
*/
|
|
668
|
-
let _randomBytes = null;
|
|
669
|
-
let _http = null;
|
|
670
|
-
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
|
|
671
|
-
import("node:crypto").then((m) => {
|
|
672
|
-
_randomBytes = m.randomBytes;
|
|
673
|
-
});
|
|
674
|
-
import("node:http").then((m) => {
|
|
675
|
-
_http = m;
|
|
676
|
-
});
|
|
677
|
-
}
|
|
678
|
-
const CLIENT_ID$3 = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
679
|
-
const AUTH_BASE_URL = "https://auth.openai.com";
|
|
680
|
-
const AUTHORIZE_URL$1 = `${AUTH_BASE_URL}/oauth/authorize`;
|
|
681
|
-
const TOKEN_URL$1 = `${AUTH_BASE_URL}/oauth/token`;
|
|
682
|
-
const REDIRECT_URI$1 = "http://localhost:1455/auth/callback";
|
|
683
|
-
const DEVICE_USER_CODE_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`;
|
|
684
|
-
const DEVICE_TOKEN_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/token`;
|
|
685
|
-
const DEVICE_VERIFICATION_URI = `${AUTH_BASE_URL}/codex/device`;
|
|
686
|
-
const DEVICE_REDIRECT_URI = `${AUTH_BASE_URL}/deviceauth/callback`;
|
|
687
|
-
const DEVICE_CODE_TIMEOUT_SECONDS$1 = 900;
|
|
688
|
-
const OPENAI_CODEX_BROWSER_LOGIN_METHOD = "browser";
|
|
689
|
-
const OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = "device_code";
|
|
690
|
-
const SCOPE = "openid profile email offline_access";
|
|
691
|
-
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
692
|
-
function getCallbackHost() {
|
|
693
|
-
return getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.1";
|
|
694
|
-
}
|
|
695
|
-
function createState() {
|
|
696
|
-
if (!_randomBytes) throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
|
|
697
|
-
return _randomBytes(16).toString("hex");
|
|
698
|
-
}
|
|
699
|
-
function parseAuthorizationInput$1(input) {
|
|
700
|
-
const value = input.trim();
|
|
701
|
-
if (!value) return {};
|
|
702
|
-
try {
|
|
703
|
-
const url = new URL(value);
|
|
704
|
-
return {
|
|
705
|
-
code: url.searchParams.get("code") ?? void 0,
|
|
706
|
-
state: url.searchParams.get("state") ?? void 0
|
|
707
|
-
};
|
|
708
|
-
} catch {}
|
|
709
|
-
if (value.includes("#")) {
|
|
710
|
-
const [code, state] = value.split("#", 2);
|
|
711
|
-
return {
|
|
712
|
-
code,
|
|
713
|
-
state
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
if (value.includes("code=")) {
|
|
717
|
-
const params = new URLSearchParams(value);
|
|
718
|
-
return {
|
|
719
|
-
code: params.get("code") ?? void 0,
|
|
720
|
-
state: params.get("state") ?? void 0
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
return { code: value };
|
|
724
|
-
}
|
|
725
|
-
function decodeJwt(token) {
|
|
726
|
-
try {
|
|
727
|
-
const parts = token.split(".");
|
|
728
|
-
if (parts.length !== 3) return null;
|
|
729
|
-
const payload = parts[1] ?? "";
|
|
730
|
-
const decoded = atob(payload);
|
|
731
|
-
return JSON.parse(decoded);
|
|
732
|
-
} catch {
|
|
733
|
-
return null;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
async function fetchWithLoginCancellation(input, init) {
|
|
737
|
-
try {
|
|
738
|
-
return await fetch(input, init);
|
|
739
|
-
} catch (error) {
|
|
740
|
-
if (init.signal?.aborted) throw new Error("Login cancelled");
|
|
741
|
-
throw error;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
async function readTokenResponse(response, operation) {
|
|
745
|
-
if (!response.ok) {
|
|
746
|
-
const text = await response.text().catch(() => "");
|
|
747
|
-
throw new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text || response.statusText}`);
|
|
748
|
-
}
|
|
749
|
-
const json = await response.json();
|
|
750
|
-
if (!json?.access_token || !json.refresh_token || typeof json.expires_in !== "number") throw new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);
|
|
751
|
-
return {
|
|
752
|
-
access: json.access_token,
|
|
753
|
-
refresh: json.refresh_token,
|
|
754
|
-
expires: Date.now() + json.expires_in * 1e3
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
async function exchangeAuthorizationCode$1(code, verifier, redirectUri, signal) {
|
|
758
|
-
return readTokenResponse(await fetchWithLoginCancellation(TOKEN_URL$1, {
|
|
759
|
-
method: "POST",
|
|
760
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
761
|
-
body: new URLSearchParams({
|
|
762
|
-
grant_type: "authorization_code",
|
|
763
|
-
client_id: CLIENT_ID$3,
|
|
764
|
-
code,
|
|
765
|
-
code_verifier: verifier,
|
|
766
|
-
redirect_uri: redirectUri
|
|
767
|
-
}),
|
|
768
|
-
signal
|
|
769
|
-
}), "exchange");
|
|
770
|
-
}
|
|
771
|
-
async function refreshAccessToken(refreshToken, signal) {
|
|
772
|
-
let response;
|
|
773
|
-
try {
|
|
774
|
-
response = await fetch(TOKEN_URL$1, {
|
|
775
|
-
method: "POST",
|
|
776
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
777
|
-
body: new URLSearchParams({
|
|
778
|
-
grant_type: "refresh_token",
|
|
779
|
-
refresh_token: refreshToken,
|
|
780
|
-
client_id: CLIENT_ID$3
|
|
781
|
-
}),
|
|
782
|
-
signal
|
|
783
|
-
});
|
|
784
|
-
} catch (error) {
|
|
785
|
-
throw new Error(`OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`);
|
|
786
|
-
}
|
|
787
|
-
return readTokenResponse(response, "refresh");
|
|
788
|
-
}
|
|
789
|
-
async function startOpenAICodexDeviceAuth(signal) {
|
|
790
|
-
const response = await fetchWithLoginCancellation(DEVICE_USER_CODE_URL, {
|
|
791
|
-
method: "POST",
|
|
792
|
-
headers: { "Content-Type": "application/json" },
|
|
793
|
-
body: JSON.stringify({ client_id: CLIENT_ID$3 }),
|
|
794
|
-
signal
|
|
795
|
-
});
|
|
796
|
-
if (!response.ok) {
|
|
797
|
-
if (response.status === 404) throw new Error("OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.");
|
|
798
|
-
const responseBody = await response.text().catch(() => "");
|
|
799
|
-
throw new Error(`OpenAI Codex device code request failed with status ${response.status}${responseBody ? `: ${responseBody}` : ""}`);
|
|
800
|
-
}
|
|
801
|
-
const json = await response.json();
|
|
802
|
-
const intervalSeconds = typeof json?.interval === "string" ? Number(json.interval.trim()) : json?.interval;
|
|
803
|
-
if (!json?.device_auth_id || !json.user_code || typeof intervalSeconds !== "number" || !Number.isFinite(intervalSeconds) || intervalSeconds < 0) throw new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);
|
|
804
|
-
return {
|
|
805
|
-
deviceAuthId: json.device_auth_id,
|
|
806
|
-
userCode: json.user_code,
|
|
807
|
-
intervalSeconds
|
|
808
|
-
};
|
|
809
|
-
}
|
|
810
|
-
async function pollOpenAICodexDeviceAuth(device, signal) {
|
|
811
|
-
return pollOAuthDeviceCodeFlow({
|
|
812
|
-
intervalSeconds: device.intervalSeconds,
|
|
813
|
-
expiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS$1,
|
|
814
|
-
signal,
|
|
815
|
-
poll: async () => {
|
|
816
|
-
const response = await fetchWithLoginCancellation(DEVICE_TOKEN_URL, {
|
|
817
|
-
method: "POST",
|
|
818
|
-
headers: { "Content-Type": "application/json" },
|
|
819
|
-
body: JSON.stringify({
|
|
820
|
-
device_auth_id: device.deviceAuthId,
|
|
821
|
-
user_code: device.userCode
|
|
822
|
-
}),
|
|
823
|
-
signal
|
|
824
|
-
});
|
|
825
|
-
if (response.ok) {
|
|
826
|
-
const json = await response.json();
|
|
827
|
-
if (!json?.authorization_code || !json.code_verifier) return {
|
|
828
|
-
status: "failed",
|
|
829
|
-
message: `Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`
|
|
830
|
-
};
|
|
831
|
-
return {
|
|
832
|
-
status: "complete",
|
|
833
|
-
value: {
|
|
834
|
-
authorizationCode: json.authorization_code,
|
|
835
|
-
codeVerifier: json.code_verifier
|
|
836
|
-
}
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
if (response.status === 403 || response.status === 404) return { status: "pending" };
|
|
840
|
-
const responseBody = await response.text().catch(() => "");
|
|
841
|
-
let errorCode;
|
|
842
|
-
try {
|
|
843
|
-
const error = JSON.parse(responseBody)?.error;
|
|
844
|
-
errorCode = typeof error === "object" ? error?.code : error;
|
|
845
|
-
} catch {}
|
|
846
|
-
if (errorCode === "deviceauth_authorization_pending") return { status: "pending" };
|
|
847
|
-
if (errorCode === "slow_down") return { status: "slow_down" };
|
|
848
|
-
return {
|
|
849
|
-
status: "failed",
|
|
850
|
-
message: `OpenAI Codex device auth failed with status ${response.status}${responseBody ? `: ${responseBody}` : ""}`
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
async function createAuthorizationFlow(originator = "pi") {
|
|
856
|
-
const { verifier, challenge } = await generatePKCE();
|
|
857
|
-
const state = createState();
|
|
858
|
-
const url = new URL(AUTHORIZE_URL$1);
|
|
859
|
-
url.searchParams.set("response_type", "code");
|
|
860
|
-
url.searchParams.set("client_id", CLIENT_ID$3);
|
|
861
|
-
url.searchParams.set("redirect_uri", REDIRECT_URI$1);
|
|
862
|
-
url.searchParams.set("scope", SCOPE);
|
|
863
|
-
url.searchParams.set("code_challenge", challenge);
|
|
864
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
865
|
-
url.searchParams.set("state", state);
|
|
866
|
-
url.searchParams.set("id_token_add_organizations", "true");
|
|
867
|
-
url.searchParams.set("codex_cli_simplified_flow", "true");
|
|
868
|
-
url.searchParams.set("originator", originator);
|
|
869
|
-
return {
|
|
870
|
-
verifier,
|
|
871
|
-
state,
|
|
872
|
-
url: url.toString()
|
|
873
|
-
};
|
|
874
|
-
}
|
|
875
|
-
function startLocalOAuthServer(state) {
|
|
876
|
-
if (!_http) throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
|
|
877
|
-
let settleWait;
|
|
878
|
-
const waitForCodePromise = new Promise((resolve) => {
|
|
879
|
-
let settled = false;
|
|
880
|
-
settleWait = (value) => {
|
|
881
|
-
if (settled) return;
|
|
882
|
-
settled = true;
|
|
883
|
-
resolve(value);
|
|
884
|
-
};
|
|
885
|
-
});
|
|
886
|
-
const server = _http.createServer((req, res) => {
|
|
887
|
-
try {
|
|
888
|
-
const url = new URL(req.url || "", "http://localhost");
|
|
889
|
-
if (url.pathname !== "/auth/callback") {
|
|
890
|
-
res.statusCode = 404;
|
|
891
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
892
|
-
res.end(oauthErrorHtml("Callback route not found."));
|
|
893
|
-
return;
|
|
894
|
-
}
|
|
895
|
-
if (url.searchParams.get("state") !== state) {
|
|
896
|
-
res.statusCode = 400;
|
|
897
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
898
|
-
res.end(oauthErrorHtml("State mismatch."));
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
const code = url.searchParams.get("code");
|
|
902
|
-
if (!code) {
|
|
903
|
-
res.statusCode = 400;
|
|
904
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
905
|
-
res.end(oauthErrorHtml("Missing authorization code."));
|
|
906
|
-
return;
|
|
907
|
-
}
|
|
908
|
-
res.statusCode = 200;
|
|
909
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
910
|
-
res.end(oauthSuccessHtml("OpenAI authentication completed. You can close this window."));
|
|
911
|
-
settleWait?.({ code });
|
|
912
|
-
} catch {
|
|
913
|
-
res.statusCode = 500;
|
|
914
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
915
|
-
res.end(oauthErrorHtml("Internal error while processing OAuth callback."));
|
|
916
|
-
}
|
|
917
|
-
});
|
|
918
|
-
return new Promise((resolve) => {
|
|
919
|
-
server.listen(1455, getCallbackHost(), () => {
|
|
920
|
-
resolve({
|
|
921
|
-
close: () => server.close(),
|
|
922
|
-
cancelWait: () => {
|
|
923
|
-
settleWait?.(null);
|
|
924
|
-
},
|
|
925
|
-
waitForCode: () => waitForCodePromise
|
|
926
|
-
});
|
|
927
|
-
}).on("error", (_err) => {
|
|
928
|
-
settleWait?.(null);
|
|
929
|
-
resolve({
|
|
930
|
-
close: () => {
|
|
931
|
-
try {
|
|
932
|
-
server.close();
|
|
933
|
-
} catch {}
|
|
934
|
-
},
|
|
935
|
-
cancelWait: () => {},
|
|
936
|
-
waitForCode: async () => null
|
|
937
|
-
});
|
|
938
|
-
});
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
function getAccountId(accessToken) {
|
|
942
|
-
const accountId = (decodeJwt(accessToken)?.[JWT_CLAIM_PATH])?.chatgpt_account_id;
|
|
943
|
-
return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
|
|
944
|
-
}
|
|
945
|
-
function credentialsFromToken(token) {
|
|
946
|
-
const accountId = getAccountId(token.access);
|
|
947
|
-
if (!accountId) throw new Error("Failed to extract accountId from token");
|
|
948
|
-
return {
|
|
949
|
-
type: "oauth",
|
|
950
|
-
access: token.access,
|
|
951
|
-
refresh: token.refresh,
|
|
952
|
-
expires: token.expires,
|
|
953
|
-
accountId
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
async function exchangeAuthorizationCodeForCredentials(code, verifier, redirectUri, signal) {
|
|
957
|
-
return credentialsFromToken(await exchangeAuthorizationCode$1(code, verifier, redirectUri, signal));
|
|
958
|
-
}
|
|
959
|
-
async function loginOpenAICodexDeviceCode(interaction) {
|
|
960
|
-
const device = await startOpenAICodexDeviceAuth(interaction.signal);
|
|
961
|
-
interaction.notify({
|
|
962
|
-
type: "device_code",
|
|
963
|
-
userCode: device.userCode,
|
|
964
|
-
verificationUri: DEVICE_VERIFICATION_URI,
|
|
965
|
-
intervalSeconds: device.intervalSeconds,
|
|
966
|
-
expiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS$1
|
|
967
|
-
});
|
|
968
|
-
const code = await pollOpenAICodexDeviceAuth(device, interaction.signal);
|
|
969
|
-
return exchangeAuthorizationCodeForCredentials(code.authorizationCode, code.codeVerifier, DEVICE_REDIRECT_URI, interaction.signal);
|
|
970
|
-
}
|
|
971
|
-
async function loginOpenAICodex(interaction) {
|
|
972
|
-
const { verifier, state, url } = await createAuthorizationFlow();
|
|
973
|
-
const server = await startLocalOAuthServer(state);
|
|
974
|
-
const manualAbort = new AbortController();
|
|
975
|
-
const onAbort = () => server.cancelWait();
|
|
976
|
-
interaction.signal.addEventListener("abort", onAbort, { once: true });
|
|
977
|
-
if (interaction.signal.aborted) onAbort();
|
|
978
|
-
let code;
|
|
979
|
-
let manualCode;
|
|
980
|
-
let manualError;
|
|
981
|
-
interaction.notify({
|
|
982
|
-
type: "auth_url",
|
|
983
|
-
url,
|
|
984
|
-
instructions: "A browser window should open. Complete login to finish."
|
|
985
|
-
});
|
|
986
|
-
try {
|
|
987
|
-
const manualPromise = interaction.prompt({
|
|
988
|
-
type: "manual_code",
|
|
989
|
-
message: "Complete login in your browser, or paste the authorization code / redirect URL here:",
|
|
990
|
-
placeholder: REDIRECT_URI$1,
|
|
991
|
-
signal: manualAbort.signal
|
|
992
|
-
}).then((input) => {
|
|
993
|
-
manualCode = input;
|
|
994
|
-
server.cancelWait();
|
|
995
|
-
}).catch((error) => {
|
|
996
|
-
manualError = error instanceof Error ? error : new Error(String(error));
|
|
997
|
-
server.cancelWait();
|
|
998
|
-
});
|
|
999
|
-
const result = await server.waitForCode();
|
|
1000
|
-
if (manualError) throw manualError;
|
|
1001
|
-
if (result?.code) code = result.code;
|
|
1002
|
-
else if (manualCode) {
|
|
1003
|
-
const parsed = parseAuthorizationInput$1(manualCode);
|
|
1004
|
-
if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
|
|
1005
|
-
code = parsed.code;
|
|
1006
|
-
}
|
|
1007
|
-
if (!code) {
|
|
1008
|
-
await manualPromise;
|
|
1009
|
-
if (manualError) throw manualError;
|
|
1010
|
-
if (manualCode) {
|
|
1011
|
-
const parsed = parseAuthorizationInput$1(manualCode);
|
|
1012
|
-
if (parsed.state && parsed.state !== state) throw new Error("State mismatch");
|
|
1013
|
-
code = parsed.code;
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
if (!code) throw new Error("Missing authorization code");
|
|
1017
|
-
return exchangeAuthorizationCodeForCredentials(code, verifier, REDIRECT_URI$1, interaction.signal);
|
|
1018
|
-
} finally {
|
|
1019
|
-
interaction.signal.removeEventListener("abort", onAbort);
|
|
1020
|
-
manualAbort.abort();
|
|
1021
|
-
server.close();
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
/**
|
|
1025
|
-
* Refresh OpenAI Codex OAuth token
|
|
1026
|
-
*/
|
|
1027
|
-
async function refreshOpenAICodexToken(refreshToken, signal) {
|
|
1028
|
-
return credentialsFromToken(await refreshAccessToken(refreshToken, signal));
|
|
1029
|
-
}
|
|
1030
|
-
const openaiCodexOAuth = {
|
|
1031
|
-
name: "OpenAI (ChatGPT Plus/Pro)",
|
|
1032
|
-
isSubscription: true,
|
|
1033
|
-
async login(interaction) {
|
|
1034
|
-
const method = await interaction.prompt({
|
|
1035
|
-
type: "select",
|
|
1036
|
-
message: "Select OpenAI Codex login method:",
|
|
1037
|
-
options: [{
|
|
1038
|
-
id: OPENAI_CODEX_BROWSER_LOGIN_METHOD,
|
|
1039
|
-
label: "Browser login (default)"
|
|
1040
|
-
}, {
|
|
1041
|
-
id: OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD,
|
|
1042
|
-
label: "Device code login (headless)"
|
|
1043
|
-
}]
|
|
1044
|
-
});
|
|
1045
|
-
if (method === OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD) return loginOpenAICodexDeviceCode(interaction);
|
|
1046
|
-
if (method !== OPENAI_CODEX_BROWSER_LOGIN_METHOD) throw new Error(`Unknown OpenAI Codex login method: ${method}`);
|
|
1047
|
-
return loginOpenAICodex(interaction);
|
|
1048
|
-
},
|
|
1049
|
-
refresh: (credential, signal) => refreshOpenAICodexToken(credential.refresh, signal),
|
|
1050
|
-
async toAuth(credential) {
|
|
1051
|
-
return { apiKey: credential.access };
|
|
1052
|
-
}
|
|
1053
|
-
};
|
|
1054
|
-
//#endregion
|
|
1055
|
-
//#region src/compat/vendor/pi-oauth-flows/anthropic.ts
|
|
1056
|
-
/**
|
|
1057
|
-
* Anthropic OAuth flow (Claude Pro/Max)
|
|
1058
|
-
*
|
|
1059
|
-
* NOTE: This module uses Node.js http.createServer for the OAuth callback server.
|
|
1060
|
-
* It is only intended for CLI use, not browser environments.
|
|
1061
|
-
*/
|
|
1062
|
-
let nodeApis = null;
|
|
1063
|
-
let nodeApisPromise = null;
|
|
1064
|
-
const decode$1 = (s) => atob(s);
|
|
1065
|
-
const CLIENT_ID$2 = decode$1("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
|
|
1066
|
-
const AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
|
|
1067
|
-
const TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
|
|
1068
|
-
const CALLBACK_HOST = getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.1";
|
|
1069
|
-
const CALLBACK_PORT = 53692;
|
|
1070
|
-
const CALLBACK_PATH = "/callback";
|
|
1071
|
-
const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
1072
|
-
const SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
|
|
1073
|
-
async function getNodeApis() {
|
|
1074
|
-
if (nodeApis) return nodeApis;
|
|
1075
|
-
if (!nodeApisPromise) {
|
|
1076
|
-
if (typeof process === "undefined" || !process.versions?.node && !process.versions?.bun) throw new Error("Anthropic OAuth is only available in Node.js environments");
|
|
1077
|
-
nodeApisPromise = import("node:http").then((httpModule) => ({ createServer: httpModule.createServer }));
|
|
1078
|
-
}
|
|
1079
|
-
nodeApis = await nodeApisPromise;
|
|
1080
|
-
return nodeApis;
|
|
1081
|
-
}
|
|
1082
|
-
function parseAuthorizationInput(input) {
|
|
1083
|
-
const value = input.trim();
|
|
1084
|
-
if (!value) return {};
|
|
1085
|
-
try {
|
|
1086
|
-
const url = new URL(value);
|
|
1087
|
-
return {
|
|
1088
|
-
code: url.searchParams.get("code") ?? void 0,
|
|
1089
|
-
state: url.searchParams.get("state") ?? void 0
|
|
1090
|
-
};
|
|
1091
|
-
} catch {}
|
|
1092
|
-
if (value.includes("#")) {
|
|
1093
|
-
const [code, state] = value.split("#", 2);
|
|
1094
|
-
return {
|
|
1095
|
-
code,
|
|
1096
|
-
state
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
if (value.includes("code=")) {
|
|
1100
|
-
const params = new URLSearchParams(value);
|
|
1101
|
-
return {
|
|
1102
|
-
code: params.get("code") ?? void 0,
|
|
1103
|
-
state: params.get("state") ?? void 0
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
return { code: value };
|
|
1107
|
-
}
|
|
1108
|
-
function formatErrorDetails(error) {
|
|
1109
|
-
if (error instanceof Error) {
|
|
1110
|
-
const details = [`${error.name}: ${error.message}`];
|
|
1111
|
-
const errorWithCode = error;
|
|
1112
|
-
if (errorWithCode.code) details.push(`code=${errorWithCode.code}`);
|
|
1113
|
-
if (typeof errorWithCode.errno !== "undefined") details.push(`errno=${String(errorWithCode.errno)}`);
|
|
1114
|
-
if (typeof error.cause !== "undefined") details.push(`cause=${formatErrorDetails(error.cause)}`);
|
|
1115
|
-
if (error.stack) details.push(`stack=${error.stack}`);
|
|
1116
|
-
return details.join("; ");
|
|
1117
|
-
}
|
|
1118
|
-
return String(error);
|
|
1119
|
-
}
|
|
1120
|
-
async function startCallbackServer(expectedState) {
|
|
1121
|
-
const { createServer } = await getNodeApis();
|
|
1122
|
-
return new Promise((resolve, reject) => {
|
|
1123
|
-
let settleWait;
|
|
1124
|
-
const waitForCodePromise = new Promise((resolveWait) => {
|
|
1125
|
-
let settled = false;
|
|
1126
|
-
settleWait = (value) => {
|
|
1127
|
-
if (settled) return;
|
|
1128
|
-
settled = true;
|
|
1129
|
-
resolveWait(value);
|
|
1130
|
-
};
|
|
1131
|
-
});
|
|
1132
|
-
const server = createServer((req, res) => {
|
|
1133
|
-
try {
|
|
1134
|
-
const url = new URL(req.url || "", "http://localhost");
|
|
1135
|
-
if (url.pathname !== CALLBACK_PATH) {
|
|
1136
|
-
res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
|
|
1137
|
-
res.end(oauthErrorHtml("Callback route not found."));
|
|
1138
|
-
return;
|
|
1139
|
-
}
|
|
1140
|
-
const code = url.searchParams.get("code");
|
|
1141
|
-
const state = url.searchParams.get("state");
|
|
1142
|
-
const error = url.searchParams.get("error");
|
|
1143
|
-
if (error) {
|
|
1144
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
1145
|
-
res.end(oauthErrorHtml("Anthropic authentication did not complete.", `Error: ${error}`));
|
|
1146
|
-
return;
|
|
1147
|
-
}
|
|
1148
|
-
if (!code || !state) {
|
|
1149
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
1150
|
-
res.end(oauthErrorHtml("Missing code or state parameter."));
|
|
1151
|
-
return;
|
|
1152
|
-
}
|
|
1153
|
-
if (state !== expectedState) {
|
|
1154
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
1155
|
-
res.end(oauthErrorHtml("State mismatch."));
|
|
1156
|
-
return;
|
|
1157
|
-
}
|
|
1158
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1159
|
-
res.end(oauthSuccessHtml("Anthropic authentication completed. You can close this window."));
|
|
1160
|
-
settleWait?.({
|
|
1161
|
-
code,
|
|
1162
|
-
state
|
|
1163
|
-
});
|
|
1164
|
-
} catch {
|
|
1165
|
-
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
1166
|
-
res.end("Internal error");
|
|
1167
|
-
}
|
|
1168
|
-
});
|
|
1169
|
-
server.on("error", (err) => {
|
|
1170
|
-
reject(err);
|
|
1171
|
-
});
|
|
1172
|
-
server.listen(CALLBACK_PORT, CALLBACK_HOST, () => {
|
|
1173
|
-
resolve({
|
|
1174
|
-
server,
|
|
1175
|
-
redirectUri: REDIRECT_URI,
|
|
1176
|
-
cancelWait: () => {
|
|
1177
|
-
settleWait?.(null);
|
|
1178
|
-
},
|
|
1179
|
-
waitForCode: () => waitForCodePromise
|
|
1180
|
-
});
|
|
1181
|
-
});
|
|
1182
|
-
});
|
|
1183
|
-
}
|
|
1184
|
-
async function postJson(url, body, signal) {
|
|
1185
|
-
const response = await fetch(url, {
|
|
1186
|
-
method: "POST",
|
|
1187
|
-
headers: {
|
|
1188
|
-
"Content-Type": "application/json",
|
|
1189
|
-
Accept: "application/json"
|
|
1190
|
-
},
|
|
1191
|
-
body: JSON.stringify(body),
|
|
1192
|
-
signal: AbortSignal.any([signal, AbortSignal.timeout(3e4)])
|
|
1193
|
-
});
|
|
1194
|
-
const responseBody = await response.text();
|
|
1195
|
-
if (!response.ok) throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);
|
|
1196
|
-
return responseBody;
|
|
1197
|
-
}
|
|
1198
|
-
async function exchangeAuthorizationCode(code, state, verifier, redirectUri, signal) {
|
|
1199
|
-
let responseBody;
|
|
1200
|
-
try {
|
|
1201
|
-
responseBody = await postJson(TOKEN_URL, {
|
|
1202
|
-
grant_type: "authorization_code",
|
|
1203
|
-
client_id: CLIENT_ID$2,
|
|
1204
|
-
code,
|
|
1205
|
-
state,
|
|
1206
|
-
redirect_uri: redirectUri,
|
|
1207
|
-
code_verifier: verifier
|
|
1208
|
-
}, signal);
|
|
1209
|
-
} catch (error) {
|
|
1210
|
-
throw new Error(`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; response_type=authorization_code; details=${formatErrorDetails(error)}`);
|
|
1211
|
-
}
|
|
1212
|
-
let tokenData;
|
|
1213
|
-
try {
|
|
1214
|
-
tokenData = JSON.parse(responseBody);
|
|
1215
|
-
} catch (error) {
|
|
1216
|
-
throw new Error(`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`);
|
|
1217
|
-
}
|
|
1218
|
-
return {
|
|
1219
|
-
type: "oauth",
|
|
1220
|
-
refresh: tokenData.refresh_token,
|
|
1221
|
-
access: tokenData.access_token,
|
|
1222
|
-
expires: Date.now() + tokenData.expires_in * 1e3 - 3e5
|
|
1223
|
-
};
|
|
1224
|
-
}
|
|
1225
|
-
async function loginAnthropic(interaction) {
|
|
1226
|
-
const { verifier, challenge } = await generatePKCE();
|
|
1227
|
-
const server = await startCallbackServer(verifier);
|
|
1228
|
-
const manualAbort = new AbortController();
|
|
1229
|
-
const onAbort = () => server.cancelWait();
|
|
1230
|
-
interaction.signal.addEventListener("abort", onAbort, { once: true });
|
|
1231
|
-
if (interaction.signal.aborted) onAbort();
|
|
1232
|
-
let code;
|
|
1233
|
-
let state;
|
|
1234
|
-
let manualInput;
|
|
1235
|
-
let manualError;
|
|
1236
|
-
try {
|
|
1237
|
-
const authParams = new URLSearchParams({
|
|
1238
|
-
code: "true",
|
|
1239
|
-
client_id: CLIENT_ID$2,
|
|
1240
|
-
response_type: "code",
|
|
1241
|
-
redirect_uri: REDIRECT_URI,
|
|
1242
|
-
scope: SCOPES,
|
|
1243
|
-
code_challenge: challenge,
|
|
1244
|
-
code_challenge_method: "S256",
|
|
1245
|
-
state: verifier
|
|
1246
|
-
});
|
|
1247
|
-
interaction.notify({
|
|
1248
|
-
type: "auth_url",
|
|
1249
|
-
url: `${AUTHORIZE_URL}?${authParams.toString()}`,
|
|
1250
|
-
instructions: "Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."
|
|
1251
|
-
});
|
|
1252
|
-
const manualPromise = interaction.prompt({
|
|
1253
|
-
type: "manual_code",
|
|
1254
|
-
message: "Complete login in your browser, or paste the authorization code / redirect URL here:",
|
|
1255
|
-
placeholder: REDIRECT_URI,
|
|
1256
|
-
signal: manualAbort.signal
|
|
1257
|
-
}).then((input) => {
|
|
1258
|
-
manualInput = input;
|
|
1259
|
-
server.cancelWait();
|
|
1260
|
-
}).catch((error) => {
|
|
1261
|
-
manualError = error instanceof Error ? error : new Error(String(error));
|
|
1262
|
-
server.cancelWait();
|
|
1263
|
-
});
|
|
1264
|
-
const result = await server.waitForCode();
|
|
1265
|
-
if (manualError) throw manualError;
|
|
1266
|
-
if (result?.code) {
|
|
1267
|
-
code = result.code;
|
|
1268
|
-
state = result.state;
|
|
1269
|
-
} else if (manualInput) {
|
|
1270
|
-
const parsed = parseAuthorizationInput(manualInput);
|
|
1271
|
-
if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
|
|
1272
|
-
code = parsed.code;
|
|
1273
|
-
state = parsed.state ?? verifier;
|
|
1274
|
-
}
|
|
1275
|
-
if (!code) {
|
|
1276
|
-
await manualPromise;
|
|
1277
|
-
if (manualError) throw manualError;
|
|
1278
|
-
if (manualInput) {
|
|
1279
|
-
const parsed = parseAuthorizationInput(manualInput);
|
|
1280
|
-
if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch");
|
|
1281
|
-
code = parsed.code;
|
|
1282
|
-
state = parsed.state ?? verifier;
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
if (!code) throw new Error("Missing authorization code");
|
|
1286
|
-
if (!state) throw new Error("Missing OAuth state");
|
|
1287
|
-
interaction.notify({
|
|
1288
|
-
type: "progress",
|
|
1289
|
-
message: "Exchanging authorization code for tokens..."
|
|
1290
|
-
});
|
|
1291
|
-
return exchangeAuthorizationCode(code, state, verifier, REDIRECT_URI, interaction.signal);
|
|
1292
|
-
} finally {
|
|
1293
|
-
interaction.signal.removeEventListener("abort", onAbort);
|
|
1294
|
-
manualAbort.abort();
|
|
1295
|
-
server.server.close();
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
/**
|
|
1299
|
-
* Refresh Anthropic OAuth token
|
|
1300
|
-
*/
|
|
1301
|
-
async function refreshAnthropicToken(refreshToken, signal) {
|
|
1302
|
-
let responseBody;
|
|
1303
|
-
try {
|
|
1304
|
-
responseBody = await postJson(TOKEN_URL, {
|
|
1305
|
-
grant_type: "refresh_token",
|
|
1306
|
-
client_id: CLIENT_ID$2,
|
|
1307
|
-
refresh_token: refreshToken
|
|
1308
|
-
}, signal);
|
|
1309
|
-
} catch (error) {
|
|
1310
|
-
throw new Error(`Anthropic token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`);
|
|
1311
|
-
}
|
|
1312
|
-
let data;
|
|
1313
|
-
try {
|
|
1314
|
-
data = JSON.parse(responseBody);
|
|
1315
|
-
} catch (error) {
|
|
1316
|
-
throw new Error(`Anthropic token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`);
|
|
1317
|
-
}
|
|
1318
|
-
return {
|
|
1319
|
-
type: "oauth",
|
|
1320
|
-
refresh: data.refresh_token,
|
|
1321
|
-
access: data.access_token,
|
|
1322
|
-
expires: Date.now() + data.expires_in * 1e3 - 3e5
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
const anthropicOAuth = {
|
|
1326
|
-
name: "Anthropic (Claude Pro/Max)",
|
|
1327
|
-
isSubscription: true,
|
|
1328
|
-
login: loginAnthropic,
|
|
1329
|
-
refresh: (credential, signal) => refreshAnthropicToken(credential.refresh, signal),
|
|
1330
|
-
async toAuth(credential) {
|
|
1331
|
-
return { apiKey: credential.access };
|
|
1332
|
-
}
|
|
1333
|
-
};
|
|
1334
|
-
//#endregion
|
|
1335
|
-
//#region src/compat/vendor/pi-oauth-flows/data/github-copilot.json
|
|
1336
|
-
var github_copilot_default = {
|
|
1337
|
-
"anthropic-messages": {
|
|
1338
|
-
"claude-haiku-4.5": {
|
|
1339
|
-
"id": "claude-haiku-4.5",
|
|
1340
|
-
"name": "Claude Haiku 4.5 (latest)",
|
|
1341
|
-
"api": "anthropic-messages",
|
|
1342
|
-
"provider": "github-copilot",
|
|
1343
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1344
|
-
"reasoning": true,
|
|
1345
|
-
"input": ["text", "image"],
|
|
1346
|
-
"cost": {
|
|
1347
|
-
"input": 1,
|
|
1348
|
-
"output": 5,
|
|
1349
|
-
"cacheRead": .1,
|
|
1350
|
-
"cacheWrite": 1.25
|
|
1351
|
-
},
|
|
1352
|
-
"contextWindow": 2e5,
|
|
1353
|
-
"maxTokens": 64e3,
|
|
1354
|
-
"headers": {
|
|
1355
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1356
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1357
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1358
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1359
|
-
},
|
|
1360
|
-
"compat": { "supportsEagerToolInputStreaming": false }
|
|
1361
|
-
},
|
|
1362
|
-
"claude-opus-4.5": {
|
|
1363
|
-
"id": "claude-opus-4.5",
|
|
1364
|
-
"name": "Claude Opus 4.5 (latest)",
|
|
1365
|
-
"api": "anthropic-messages",
|
|
1366
|
-
"provider": "github-copilot",
|
|
1367
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1368
|
-
"reasoning": true,
|
|
1369
|
-
"input": ["text", "image"],
|
|
1370
|
-
"cost": {
|
|
1371
|
-
"input": 5,
|
|
1372
|
-
"output": 25,
|
|
1373
|
-
"cacheRead": .5,
|
|
1374
|
-
"cacheWrite": 6.25
|
|
1375
|
-
},
|
|
1376
|
-
"contextWindow": 2e5,
|
|
1377
|
-
"maxTokens": 32e3,
|
|
1378
|
-
"headers": {
|
|
1379
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1380
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1381
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1382
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1383
|
-
}
|
|
1384
|
-
},
|
|
1385
|
-
"claude-opus-4.6": {
|
|
1386
|
-
"id": "claude-opus-4.6",
|
|
1387
|
-
"name": "Claude Opus 4.6",
|
|
1388
|
-
"api": "anthropic-messages",
|
|
1389
|
-
"provider": "github-copilot",
|
|
1390
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1391
|
-
"reasoning": true,
|
|
1392
|
-
"input": ["text", "image"],
|
|
1393
|
-
"cost": {
|
|
1394
|
-
"input": 5,
|
|
1395
|
-
"output": 25,
|
|
1396
|
-
"cacheRead": .5,
|
|
1397
|
-
"cacheWrite": 6.25
|
|
1398
|
-
},
|
|
1399
|
-
"contextWindow": 1e6,
|
|
1400
|
-
"maxTokens": 32e3,
|
|
1401
|
-
"headers": {
|
|
1402
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1403
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1404
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1405
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1406
|
-
},
|
|
1407
|
-
"thinkingLevelMap": { "max": "max" },
|
|
1408
|
-
"compat": { "forceAdaptiveThinking": true }
|
|
1409
|
-
},
|
|
1410
|
-
"claude-opus-4.7": {
|
|
1411
|
-
"id": "claude-opus-4.7",
|
|
1412
|
-
"name": "Claude Opus 4.7",
|
|
1413
|
-
"api": "anthropic-messages",
|
|
1414
|
-
"provider": "github-copilot",
|
|
1415
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1416
|
-
"reasoning": true,
|
|
1417
|
-
"input": ["text", "image"],
|
|
1418
|
-
"cost": {
|
|
1419
|
-
"input": 5,
|
|
1420
|
-
"output": 25,
|
|
1421
|
-
"cacheRead": .5,
|
|
1422
|
-
"cacheWrite": 6.25
|
|
1423
|
-
},
|
|
1424
|
-
"contextWindow": 1e6,
|
|
1425
|
-
"maxTokens": 32e3,
|
|
1426
|
-
"headers": {
|
|
1427
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1428
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1429
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1430
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1431
|
-
},
|
|
1432
|
-
"thinkingLevelMap": {
|
|
1433
|
-
"xhigh": "xhigh",
|
|
1434
|
-
"max": "max",
|
|
1435
|
-
"minimal": "low"
|
|
1436
|
-
},
|
|
1437
|
-
"compat": {
|
|
1438
|
-
"forceAdaptiveThinking": true,
|
|
1439
|
-
"supportsTemperature": false
|
|
1440
|
-
}
|
|
1441
|
-
},
|
|
1442
|
-
"claude-opus-4.8": {
|
|
1443
|
-
"id": "claude-opus-4.8",
|
|
1444
|
-
"name": "Claude Opus 4.8",
|
|
1445
|
-
"api": "anthropic-messages",
|
|
1446
|
-
"provider": "github-copilot",
|
|
1447
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1448
|
-
"reasoning": true,
|
|
1449
|
-
"input": ["text", "image"],
|
|
1450
|
-
"cost": {
|
|
1451
|
-
"input": 5,
|
|
1452
|
-
"output": 25,
|
|
1453
|
-
"cacheRead": .5,
|
|
1454
|
-
"cacheWrite": 6.25
|
|
1455
|
-
},
|
|
1456
|
-
"contextWindow": 1e6,
|
|
1457
|
-
"maxTokens": 64e3,
|
|
1458
|
-
"headers": {
|
|
1459
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1460
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1461
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1462
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1463
|
-
},
|
|
1464
|
-
"thinkingLevelMap": {
|
|
1465
|
-
"xhigh": "xhigh",
|
|
1466
|
-
"max": "max",
|
|
1467
|
-
"minimal": "low"
|
|
1468
|
-
},
|
|
1469
|
-
"compat": {
|
|
1470
|
-
"forceAdaptiveThinking": true,
|
|
1471
|
-
"supportsTemperature": false
|
|
1472
|
-
}
|
|
1473
|
-
},
|
|
1474
|
-
"claude-opus-5": {
|
|
1475
|
-
"id": "claude-opus-5",
|
|
1476
|
-
"name": "Claude Opus 5",
|
|
1477
|
-
"api": "anthropic-messages",
|
|
1478
|
-
"provider": "github-copilot",
|
|
1479
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1480
|
-
"reasoning": true,
|
|
1481
|
-
"input": ["text", "image"],
|
|
1482
|
-
"cost": {
|
|
1483
|
-
"input": 5,
|
|
1484
|
-
"output": 25,
|
|
1485
|
-
"cacheRead": .5,
|
|
1486
|
-
"cacheWrite": 6.25
|
|
1487
|
-
},
|
|
1488
|
-
"contextWindow": 1e6,
|
|
1489
|
-
"maxTokens": 64e3,
|
|
1490
|
-
"headers": {
|
|
1491
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1492
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1493
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1494
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1495
|
-
},
|
|
1496
|
-
"thinkingLevelMap": {
|
|
1497
|
-
"xhigh": "xhigh",
|
|
1498
|
-
"max": "max",
|
|
1499
|
-
"minimal": "low"
|
|
1500
|
-
},
|
|
1501
|
-
"compat": {
|
|
1502
|
-
"forceAdaptiveThinking": true,
|
|
1503
|
-
"supportsTemperature": false
|
|
1504
|
-
}
|
|
1505
|
-
},
|
|
1506
|
-
"claude-sonnet-4": {
|
|
1507
|
-
"id": "claude-sonnet-4",
|
|
1508
|
-
"name": "Claude Sonnet 4 (latest)",
|
|
1509
|
-
"api": "anthropic-messages",
|
|
1510
|
-
"provider": "github-copilot",
|
|
1511
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1512
|
-
"reasoning": true,
|
|
1513
|
-
"input": ["text", "image"],
|
|
1514
|
-
"cost": {
|
|
1515
|
-
"input": 3,
|
|
1516
|
-
"output": 15,
|
|
1517
|
-
"cacheRead": .3,
|
|
1518
|
-
"cacheWrite": 3.75
|
|
1519
|
-
},
|
|
1520
|
-
"contextWindow": 216e3,
|
|
1521
|
-
"maxTokens": 16e3,
|
|
1522
|
-
"headers": {
|
|
1523
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1524
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1525
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1526
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1527
|
-
},
|
|
1528
|
-
"compat": { "supportsEagerToolInputStreaming": false }
|
|
1529
|
-
},
|
|
1530
|
-
"claude-sonnet-4.5": {
|
|
1531
|
-
"id": "claude-sonnet-4.5",
|
|
1532
|
-
"name": "Claude Sonnet 4.5 (latest)",
|
|
1533
|
-
"api": "anthropic-messages",
|
|
1534
|
-
"provider": "github-copilot",
|
|
1535
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1536
|
-
"reasoning": true,
|
|
1537
|
-
"input": ["text", "image"],
|
|
1538
|
-
"cost": {
|
|
1539
|
-
"input": 3,
|
|
1540
|
-
"output": 15,
|
|
1541
|
-
"cacheRead": .3,
|
|
1542
|
-
"cacheWrite": 3.75
|
|
1543
|
-
},
|
|
1544
|
-
"contextWindow": 2e5,
|
|
1545
|
-
"maxTokens": 32e3,
|
|
1546
|
-
"headers": {
|
|
1547
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1548
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1549
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1550
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1551
|
-
},
|
|
1552
|
-
"compat": { "supportsEagerToolInputStreaming": false }
|
|
1553
|
-
},
|
|
1554
|
-
"claude-sonnet-4.6": {
|
|
1555
|
-
"id": "claude-sonnet-4.6",
|
|
1556
|
-
"name": "Claude Sonnet 4.6",
|
|
1557
|
-
"api": "anthropic-messages",
|
|
1558
|
-
"provider": "github-copilot",
|
|
1559
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1560
|
-
"reasoning": true,
|
|
1561
|
-
"input": ["text", "image"],
|
|
1562
|
-
"cost": {
|
|
1563
|
-
"input": 3,
|
|
1564
|
-
"output": 15,
|
|
1565
|
-
"cacheRead": .3,
|
|
1566
|
-
"cacheWrite": 3.75
|
|
1567
|
-
},
|
|
1568
|
-
"contextWindow": 1e6,
|
|
1569
|
-
"maxTokens": 32e3,
|
|
1570
|
-
"headers": {
|
|
1571
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1572
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1573
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1574
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1575
|
-
},
|
|
1576
|
-
"thinkingLevelMap": {
|
|
1577
|
-
"max": "max",
|
|
1578
|
-
"minimal": "low"
|
|
1579
|
-
},
|
|
1580
|
-
"compat": { "forceAdaptiveThinking": true }
|
|
1581
|
-
},
|
|
1582
|
-
"claude-sonnet-5": {
|
|
1583
|
-
"id": "claude-sonnet-5",
|
|
1584
|
-
"name": "Claude Sonnet 5",
|
|
1585
|
-
"api": "anthropic-messages",
|
|
1586
|
-
"provider": "github-copilot",
|
|
1587
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1588
|
-
"reasoning": true,
|
|
1589
|
-
"input": ["text", "image"],
|
|
1590
|
-
"cost": {
|
|
1591
|
-
"input": 2,
|
|
1592
|
-
"output": 10,
|
|
1593
|
-
"cacheRead": .2,
|
|
1594
|
-
"cacheWrite": 2.5
|
|
1595
|
-
},
|
|
1596
|
-
"contextWindow": 1e6,
|
|
1597
|
-
"maxTokens": 128e3,
|
|
1598
|
-
"headers": {
|
|
1599
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1600
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1601
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1602
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1603
|
-
},
|
|
1604
|
-
"thinkingLevelMap": {
|
|
1605
|
-
"xhigh": "xhigh",
|
|
1606
|
-
"max": "max"
|
|
1607
|
-
},
|
|
1608
|
-
"compat": { "forceAdaptiveThinking": true }
|
|
1609
|
-
}
|
|
1610
|
-
},
|
|
1611
|
-
"openai-completions": {
|
|
1612
|
-
"claude-fable-5": {
|
|
1613
|
-
"id": "claude-fable-5",
|
|
1614
|
-
"name": "Claude Fable 5",
|
|
1615
|
-
"api": "openai-completions",
|
|
1616
|
-
"provider": "github-copilot",
|
|
1617
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1618
|
-
"reasoning": true,
|
|
1619
|
-
"input": ["text", "image"],
|
|
1620
|
-
"cost": {
|
|
1621
|
-
"input": 10,
|
|
1622
|
-
"output": 50,
|
|
1623
|
-
"cacheRead": 1,
|
|
1624
|
-
"cacheWrite": 12.5
|
|
1625
|
-
},
|
|
1626
|
-
"contextWindow": 1e6,
|
|
1627
|
-
"maxTokens": 128e3,
|
|
1628
|
-
"headers": {
|
|
1629
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1630
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1631
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1632
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1633
|
-
},
|
|
1634
|
-
"compat": {
|
|
1635
|
-
"supportsStore": false,
|
|
1636
|
-
"supportsDeveloperRole": false,
|
|
1637
|
-
"supportsReasoningEffort": false
|
|
1638
|
-
},
|
|
1639
|
-
"thinkingLevelMap": {
|
|
1640
|
-
"off": null,
|
|
1641
|
-
"xhigh": "xhigh",
|
|
1642
|
-
"max": "max"
|
|
1643
|
-
}
|
|
1644
|
-
},
|
|
1645
|
-
"gemini-3.1-pro-preview": {
|
|
1646
|
-
"id": "gemini-3.1-pro-preview",
|
|
1647
|
-
"name": "Gemini 3.1 Pro Preview",
|
|
1648
|
-
"api": "openai-completions",
|
|
1649
|
-
"provider": "github-copilot",
|
|
1650
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1651
|
-
"reasoning": true,
|
|
1652
|
-
"input": ["text", "image"],
|
|
1653
|
-
"cost": {
|
|
1654
|
-
"input": 2,
|
|
1655
|
-
"output": 12,
|
|
1656
|
-
"cacheRead": .2,
|
|
1657
|
-
"cacheWrite": 0,
|
|
1658
|
-
"tiers": [{
|
|
1659
|
-
"inputTokensAbove": 2e5,
|
|
1660
|
-
"input": 4,
|
|
1661
|
-
"output": 18,
|
|
1662
|
-
"cacheRead": .4,
|
|
1663
|
-
"cacheWrite": 0
|
|
1664
|
-
}]
|
|
1665
|
-
},
|
|
1666
|
-
"contextWindow": 1e6,
|
|
1667
|
-
"maxTokens": 64e3,
|
|
1668
|
-
"headers": {
|
|
1669
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1670
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1671
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1672
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1673
|
-
},
|
|
1674
|
-
"compat": {
|
|
1675
|
-
"supportsStore": false,
|
|
1676
|
-
"supportsDeveloperRole": false,
|
|
1677
|
-
"supportsReasoningEffort": false
|
|
1678
|
-
}
|
|
1679
|
-
},
|
|
1680
|
-
"gemini-3.5-flash": {
|
|
1681
|
-
"id": "gemini-3.5-flash",
|
|
1682
|
-
"name": "Gemini 3.5 Flash",
|
|
1683
|
-
"api": "openai-completions",
|
|
1684
|
-
"provider": "github-copilot",
|
|
1685
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1686
|
-
"reasoning": true,
|
|
1687
|
-
"input": ["text", "image"],
|
|
1688
|
-
"cost": {
|
|
1689
|
-
"input": 1.5,
|
|
1690
|
-
"output": 9,
|
|
1691
|
-
"cacheRead": .15,
|
|
1692
|
-
"cacheWrite": 0
|
|
1693
|
-
},
|
|
1694
|
-
"contextWindow": 2e5,
|
|
1695
|
-
"maxTokens": 64e3,
|
|
1696
|
-
"headers": {
|
|
1697
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1698
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1699
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1700
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1701
|
-
},
|
|
1702
|
-
"compat": {
|
|
1703
|
-
"supportsStore": false,
|
|
1704
|
-
"supportsDeveloperRole": false,
|
|
1705
|
-
"supportsReasoningEffort": false
|
|
1706
|
-
}
|
|
1707
|
-
},
|
|
1708
|
-
"gemini-3.6-flash": {
|
|
1709
|
-
"id": "gemini-3.6-flash",
|
|
1710
|
-
"name": "Gemini 3.6 Flash",
|
|
1711
|
-
"api": "openai-completions",
|
|
1712
|
-
"provider": "github-copilot",
|
|
1713
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1714
|
-
"reasoning": true,
|
|
1715
|
-
"input": ["text", "image"],
|
|
1716
|
-
"cost": {
|
|
1717
|
-
"input": 1.5,
|
|
1718
|
-
"output": 7.5,
|
|
1719
|
-
"cacheRead": .15,
|
|
1720
|
-
"cacheWrite": 0
|
|
1721
|
-
},
|
|
1722
|
-
"contextWindow": 1e6,
|
|
1723
|
-
"maxTokens": 64e3,
|
|
1724
|
-
"headers": {
|
|
1725
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1726
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1727
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1728
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1729
|
-
},
|
|
1730
|
-
"compat": {
|
|
1731
|
-
"supportsStore": false,
|
|
1732
|
-
"supportsDeveloperRole": false,
|
|
1733
|
-
"supportsReasoningEffort": false
|
|
1734
|
-
}
|
|
1735
|
-
},
|
|
1736
|
-
"gpt-4.1": {
|
|
1737
|
-
"id": "gpt-4.1",
|
|
1738
|
-
"name": "GPT-4.1",
|
|
1739
|
-
"api": "openai-completions",
|
|
1740
|
-
"provider": "github-copilot",
|
|
1741
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1742
|
-
"reasoning": false,
|
|
1743
|
-
"input": ["text", "image"],
|
|
1744
|
-
"cost": {
|
|
1745
|
-
"input": 2,
|
|
1746
|
-
"output": 8,
|
|
1747
|
-
"cacheRead": .5,
|
|
1748
|
-
"cacheWrite": 0
|
|
1749
|
-
},
|
|
1750
|
-
"contextWindow": 128e3,
|
|
1751
|
-
"maxTokens": 16384,
|
|
1752
|
-
"headers": {
|
|
1753
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1754
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1755
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1756
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1757
|
-
},
|
|
1758
|
-
"compat": {
|
|
1759
|
-
"supportsStore": false,
|
|
1760
|
-
"supportsDeveloperRole": false,
|
|
1761
|
-
"supportsReasoningEffort": false
|
|
1762
|
-
}
|
|
1763
|
-
},
|
|
1764
|
-
"kimi-k2.7-code": {
|
|
1765
|
-
"id": "kimi-k2.7-code",
|
|
1766
|
-
"name": "Kimi K2.7 Code",
|
|
1767
|
-
"api": "openai-completions",
|
|
1768
|
-
"provider": "github-copilot",
|
|
1769
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1770
|
-
"reasoning": true,
|
|
1771
|
-
"input": ["text", "image"],
|
|
1772
|
-
"cost": {
|
|
1773
|
-
"input": .95,
|
|
1774
|
-
"output": 4,
|
|
1775
|
-
"cacheRead": .19,
|
|
1776
|
-
"cacheWrite": 0
|
|
1777
|
-
},
|
|
1778
|
-
"contextWindow": 256e3,
|
|
1779
|
-
"maxTokens": 32e3,
|
|
1780
|
-
"headers": {
|
|
1781
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1782
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1783
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1784
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1785
|
-
},
|
|
1786
|
-
"compat": {
|
|
1787
|
-
"supportsStore": false,
|
|
1788
|
-
"supportsDeveloperRole": false,
|
|
1789
|
-
"supportsReasoningEffort": false
|
|
1790
|
-
}
|
|
1791
|
-
},
|
|
1792
|
-
"kimi-k3": {
|
|
1793
|
-
"id": "kimi-k3",
|
|
1794
|
-
"name": "Kimi K3",
|
|
1795
|
-
"api": "openai-completions",
|
|
1796
|
-
"provider": "github-copilot",
|
|
1797
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1798
|
-
"reasoning": true,
|
|
1799
|
-
"input": ["text", "image"],
|
|
1800
|
-
"cost": {
|
|
1801
|
-
"input": .95,
|
|
1802
|
-
"output": 4,
|
|
1803
|
-
"cacheRead": .19,
|
|
1804
|
-
"cacheWrite": 0
|
|
1805
|
-
},
|
|
1806
|
-
"contextWindow": 1048576,
|
|
1807
|
-
"maxTokens": 131072,
|
|
1808
|
-
"headers": {
|
|
1809
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1810
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1811
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1812
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1813
|
-
},
|
|
1814
|
-
"compat": {
|
|
1815
|
-
"supportsStore": false,
|
|
1816
|
-
"supportsDeveloperRole": false,
|
|
1817
|
-
"supportsReasoningEffort": false
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
},
|
|
1821
|
-
"openai-responses": {
|
|
1822
|
-
"gpt-5-mini": {
|
|
1823
|
-
"id": "gpt-5-mini",
|
|
1824
|
-
"name": "GPT-5 Mini",
|
|
1825
|
-
"api": "openai-responses",
|
|
1826
|
-
"provider": "github-copilot",
|
|
1827
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1828
|
-
"reasoning": true,
|
|
1829
|
-
"input": ["text", "image"],
|
|
1830
|
-
"cost": {
|
|
1831
|
-
"input": .25,
|
|
1832
|
-
"output": 2,
|
|
1833
|
-
"cacheRead": .025,
|
|
1834
|
-
"cacheWrite": 0
|
|
1835
|
-
},
|
|
1836
|
-
"contextWindow": 264e3,
|
|
1837
|
-
"maxTokens": 64e3,
|
|
1838
|
-
"headers": {
|
|
1839
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1840
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1841
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1842
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1843
|
-
},
|
|
1844
|
-
"thinkingLevelMap": {
|
|
1845
|
-
"off": null,
|
|
1846
|
-
"minimal": "low",
|
|
1847
|
-
"low": "low",
|
|
1848
|
-
"medium": "medium",
|
|
1849
|
-
"high": "high",
|
|
1850
|
-
"xhigh": null,
|
|
1851
|
-
"max": null
|
|
1852
|
-
},
|
|
1853
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
1854
|
-
},
|
|
1855
|
-
"gpt-5.2": {
|
|
1856
|
-
"id": "gpt-5.2",
|
|
1857
|
-
"name": "GPT-5.2",
|
|
1858
|
-
"api": "openai-responses",
|
|
1859
|
-
"provider": "github-copilot",
|
|
1860
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1861
|
-
"reasoning": true,
|
|
1862
|
-
"input": ["text", "image"],
|
|
1863
|
-
"cost": {
|
|
1864
|
-
"input": 1.75,
|
|
1865
|
-
"output": 14,
|
|
1866
|
-
"cacheRead": .175,
|
|
1867
|
-
"cacheWrite": 0
|
|
1868
|
-
},
|
|
1869
|
-
"contextWindow": 4e5,
|
|
1870
|
-
"maxTokens": 128e3,
|
|
1871
|
-
"headers": {
|
|
1872
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1873
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1874
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1875
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1876
|
-
},
|
|
1877
|
-
"thinkingLevelMap": {
|
|
1878
|
-
"off": null,
|
|
1879
|
-
"minimal": "low",
|
|
1880
|
-
"xhigh": "xhigh"
|
|
1881
|
-
},
|
|
1882
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
1883
|
-
},
|
|
1884
|
-
"gpt-5.2-codex": {
|
|
1885
|
-
"id": "gpt-5.2-codex",
|
|
1886
|
-
"name": "GPT-5.2 Codex",
|
|
1887
|
-
"api": "openai-responses",
|
|
1888
|
-
"provider": "github-copilot",
|
|
1889
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1890
|
-
"reasoning": true,
|
|
1891
|
-
"input": ["text", "image"],
|
|
1892
|
-
"cost": {
|
|
1893
|
-
"input": 1.75,
|
|
1894
|
-
"output": 14,
|
|
1895
|
-
"cacheRead": .175,
|
|
1896
|
-
"cacheWrite": 0
|
|
1897
|
-
},
|
|
1898
|
-
"contextWindow": 4e5,
|
|
1899
|
-
"maxTokens": 128e3,
|
|
1900
|
-
"headers": {
|
|
1901
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1902
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1903
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1904
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1905
|
-
},
|
|
1906
|
-
"thinkingLevelMap": {
|
|
1907
|
-
"off": null,
|
|
1908
|
-
"minimal": "low",
|
|
1909
|
-
"xhigh": "xhigh"
|
|
1910
|
-
},
|
|
1911
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
1912
|
-
},
|
|
1913
|
-
"gpt-5.3-codex": {
|
|
1914
|
-
"id": "gpt-5.3-codex",
|
|
1915
|
-
"name": "GPT-5.3 Codex",
|
|
1916
|
-
"api": "openai-responses",
|
|
1917
|
-
"provider": "github-copilot",
|
|
1918
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1919
|
-
"reasoning": true,
|
|
1920
|
-
"input": ["text", "image"],
|
|
1921
|
-
"cost": {
|
|
1922
|
-
"input": 1.75,
|
|
1923
|
-
"output": 14,
|
|
1924
|
-
"cacheRead": .175,
|
|
1925
|
-
"cacheWrite": 0
|
|
1926
|
-
},
|
|
1927
|
-
"contextWindow": 1e6,
|
|
1928
|
-
"maxTokens": 128e3,
|
|
1929
|
-
"headers": {
|
|
1930
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1931
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1932
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1933
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1934
|
-
},
|
|
1935
|
-
"thinkingLevelMap": {
|
|
1936
|
-
"off": null,
|
|
1937
|
-
"minimal": "low",
|
|
1938
|
-
"low": "low",
|
|
1939
|
-
"medium": "medium",
|
|
1940
|
-
"high": "high",
|
|
1941
|
-
"xhigh": "xhigh",
|
|
1942
|
-
"max": null
|
|
1943
|
-
},
|
|
1944
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
1945
|
-
},
|
|
1946
|
-
"gpt-5.4": {
|
|
1947
|
-
"id": "gpt-5.4",
|
|
1948
|
-
"name": "GPT-5.4",
|
|
1949
|
-
"api": "openai-responses",
|
|
1950
|
-
"provider": "github-copilot",
|
|
1951
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1952
|
-
"reasoning": true,
|
|
1953
|
-
"input": ["text", "image"],
|
|
1954
|
-
"cost": {
|
|
1955
|
-
"input": 2.5,
|
|
1956
|
-
"output": 15,
|
|
1957
|
-
"cacheRead": .25,
|
|
1958
|
-
"cacheWrite": 0,
|
|
1959
|
-
"tiers": [{
|
|
1960
|
-
"inputTokensAbove": 272e3,
|
|
1961
|
-
"input": 5,
|
|
1962
|
-
"output": 22.5,
|
|
1963
|
-
"cacheRead": .5,
|
|
1964
|
-
"cacheWrite": 0
|
|
1965
|
-
}]
|
|
1966
|
-
},
|
|
1967
|
-
"contextWindow": 1e6,
|
|
1968
|
-
"maxTokens": 128e3,
|
|
1969
|
-
"headers": {
|
|
1970
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
1971
|
-
"Editor-Version": "vscode/1.107.0",
|
|
1972
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
1973
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
1974
|
-
},
|
|
1975
|
-
"thinkingLevelMap": {
|
|
1976
|
-
"off": null,
|
|
1977
|
-
"minimal": "low",
|
|
1978
|
-
"low": "low",
|
|
1979
|
-
"medium": "medium",
|
|
1980
|
-
"high": "high",
|
|
1981
|
-
"xhigh": "xhigh",
|
|
1982
|
-
"max": null
|
|
1983
|
-
},
|
|
1984
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
1985
|
-
},
|
|
1986
|
-
"gpt-5.4-mini": {
|
|
1987
|
-
"id": "gpt-5.4-mini",
|
|
1988
|
-
"name": "GPT-5.4 mini",
|
|
1989
|
-
"api": "openai-responses",
|
|
1990
|
-
"provider": "github-copilot",
|
|
1991
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
1992
|
-
"reasoning": true,
|
|
1993
|
-
"input": ["text", "image"],
|
|
1994
|
-
"cost": {
|
|
1995
|
-
"input": .75,
|
|
1996
|
-
"output": 4.5,
|
|
1997
|
-
"cacheRead": .075,
|
|
1998
|
-
"cacheWrite": 0
|
|
1999
|
-
},
|
|
2000
|
-
"contextWindow": 4e5,
|
|
2001
|
-
"maxTokens": 128e3,
|
|
2002
|
-
"headers": {
|
|
2003
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
2004
|
-
"Editor-Version": "vscode/1.107.0",
|
|
2005
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
2006
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
2007
|
-
},
|
|
2008
|
-
"thinkingLevelMap": {
|
|
2009
|
-
"off": null,
|
|
2010
|
-
"minimal": "low",
|
|
2011
|
-
"low": "low",
|
|
2012
|
-
"medium": "medium",
|
|
2013
|
-
"high": "high",
|
|
2014
|
-
"xhigh": "xhigh",
|
|
2015
|
-
"max": null
|
|
2016
|
-
},
|
|
2017
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
2018
|
-
},
|
|
2019
|
-
"gpt-5.4-nano": {
|
|
2020
|
-
"id": "gpt-5.4-nano",
|
|
2021
|
-
"name": "GPT-5.4 nano",
|
|
2022
|
-
"api": "openai-responses",
|
|
2023
|
-
"provider": "github-copilot",
|
|
2024
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
2025
|
-
"reasoning": true,
|
|
2026
|
-
"input": ["text", "image"],
|
|
2027
|
-
"cost": {
|
|
2028
|
-
"input": .2,
|
|
2029
|
-
"output": 1.25,
|
|
2030
|
-
"cacheRead": .02,
|
|
2031
|
-
"cacheWrite": 0
|
|
2032
|
-
},
|
|
2033
|
-
"contextWindow": 4e5,
|
|
2034
|
-
"maxTokens": 128e3,
|
|
2035
|
-
"headers": {
|
|
2036
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
2037
|
-
"Editor-Version": "vscode/1.107.0",
|
|
2038
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
2039
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
2040
|
-
},
|
|
2041
|
-
"thinkingLevelMap": {
|
|
2042
|
-
"off": null,
|
|
2043
|
-
"minimal": "low",
|
|
2044
|
-
"xhigh": "xhigh"
|
|
2045
|
-
},
|
|
2046
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
2047
|
-
},
|
|
2048
|
-
"gpt-5.5": {
|
|
2049
|
-
"id": "gpt-5.5",
|
|
2050
|
-
"name": "GPT-5.5",
|
|
2051
|
-
"api": "openai-responses",
|
|
2052
|
-
"provider": "github-copilot",
|
|
2053
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
2054
|
-
"reasoning": true,
|
|
2055
|
-
"input": ["text", "image"],
|
|
2056
|
-
"cost": {
|
|
2057
|
-
"input": 5,
|
|
2058
|
-
"output": 30,
|
|
2059
|
-
"cacheRead": .5,
|
|
2060
|
-
"cacheWrite": 0,
|
|
2061
|
-
"tiers": [{
|
|
2062
|
-
"inputTokensAbove": 272e3,
|
|
2063
|
-
"input": 10,
|
|
2064
|
-
"output": 45,
|
|
2065
|
-
"cacheRead": 1,
|
|
2066
|
-
"cacheWrite": 0
|
|
2067
|
-
}]
|
|
2068
|
-
},
|
|
2069
|
-
"contextWindow": 1e6,
|
|
2070
|
-
"maxTokens": 128e3,
|
|
2071
|
-
"headers": {
|
|
2072
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
2073
|
-
"Editor-Version": "vscode/1.107.0",
|
|
2074
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
2075
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
2076
|
-
},
|
|
2077
|
-
"thinkingLevelMap": {
|
|
2078
|
-
"off": null,
|
|
2079
|
-
"minimal": "low",
|
|
2080
|
-
"low": "low",
|
|
2081
|
-
"medium": "medium",
|
|
2082
|
-
"high": "high",
|
|
2083
|
-
"xhigh": "xhigh",
|
|
2084
|
-
"max": null
|
|
2085
|
-
},
|
|
2086
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
2087
|
-
},
|
|
2088
|
-
"gpt-5.6-luna": {
|
|
2089
|
-
"id": "gpt-5.6-luna",
|
|
2090
|
-
"name": "GPT-5.6 Luna",
|
|
2091
|
-
"api": "openai-responses",
|
|
2092
|
-
"provider": "github-copilot",
|
|
2093
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
2094
|
-
"reasoning": true,
|
|
2095
|
-
"input": ["text", "image"],
|
|
2096
|
-
"cost": {
|
|
2097
|
-
"input": .2,
|
|
2098
|
-
"output": 1.2,
|
|
2099
|
-
"cacheRead": .02,
|
|
2100
|
-
"cacheWrite": 0,
|
|
2101
|
-
"tiers": [{
|
|
2102
|
-
"inputTokensAbove": 2e5,
|
|
2103
|
-
"input": .4,
|
|
2104
|
-
"output": 1.8,
|
|
2105
|
-
"cacheRead": .04,
|
|
2106
|
-
"cacheWrite": 0
|
|
2107
|
-
}]
|
|
2108
|
-
},
|
|
2109
|
-
"contextWindow": 105e4,
|
|
2110
|
-
"maxTokens": 128e3,
|
|
2111
|
-
"headers": {
|
|
2112
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
2113
|
-
"Editor-Version": "vscode/1.107.0",
|
|
2114
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
2115
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
2116
|
-
},
|
|
2117
|
-
"thinkingLevelMap": {
|
|
2118
|
-
"off": null,
|
|
2119
|
-
"minimal": "low",
|
|
2120
|
-
"low": "low",
|
|
2121
|
-
"medium": "medium",
|
|
2122
|
-
"high": "high",
|
|
2123
|
-
"xhigh": "xhigh",
|
|
2124
|
-
"max": "max"
|
|
2125
|
-
},
|
|
2126
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
2127
|
-
},
|
|
2128
|
-
"gpt-5.6-sol": {
|
|
2129
|
-
"id": "gpt-5.6-sol",
|
|
2130
|
-
"name": "GPT-5.6 Sol",
|
|
2131
|
-
"api": "openai-responses",
|
|
2132
|
-
"provider": "github-copilot",
|
|
2133
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
2134
|
-
"reasoning": true,
|
|
2135
|
-
"input": ["text", "image"],
|
|
2136
|
-
"cost": {
|
|
2137
|
-
"input": 5,
|
|
2138
|
-
"output": 30,
|
|
2139
|
-
"cacheRead": .5,
|
|
2140
|
-
"cacheWrite": 6.25,
|
|
2141
|
-
"tiers": [{
|
|
2142
|
-
"inputTokensAbove": 272e3,
|
|
2143
|
-
"input": 10,
|
|
2144
|
-
"output": 45,
|
|
2145
|
-
"cacheRead": 1,
|
|
2146
|
-
"cacheWrite": 12.5
|
|
2147
|
-
}]
|
|
2148
|
-
},
|
|
2149
|
-
"contextWindow": 105e4,
|
|
2150
|
-
"maxTokens": 128e3,
|
|
2151
|
-
"headers": {
|
|
2152
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
2153
|
-
"Editor-Version": "vscode/1.107.0",
|
|
2154
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
2155
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
2156
|
-
},
|
|
2157
|
-
"thinkingLevelMap": {
|
|
2158
|
-
"off": null,
|
|
2159
|
-
"minimal": "low",
|
|
2160
|
-
"low": "low",
|
|
2161
|
-
"medium": "medium",
|
|
2162
|
-
"high": "high",
|
|
2163
|
-
"xhigh": "xhigh",
|
|
2164
|
-
"max": "max"
|
|
2165
|
-
},
|
|
2166
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
2167
|
-
},
|
|
2168
|
-
"gpt-5.6-terra": {
|
|
2169
|
-
"id": "gpt-5.6-terra",
|
|
2170
|
-
"name": "GPT-5.6 Terra",
|
|
2171
|
-
"api": "openai-responses",
|
|
2172
|
-
"provider": "github-copilot",
|
|
2173
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
2174
|
-
"reasoning": true,
|
|
2175
|
-
"input": ["text", "image"],
|
|
2176
|
-
"cost": {
|
|
2177
|
-
"input": 2,
|
|
2178
|
-
"output": 12,
|
|
2179
|
-
"cacheRead": .2,
|
|
2180
|
-
"cacheWrite": 0,
|
|
2181
|
-
"tiers": [{
|
|
2182
|
-
"inputTokensAbove": 272e3,
|
|
2183
|
-
"input": 4,
|
|
2184
|
-
"output": 18,
|
|
2185
|
-
"cacheRead": .4,
|
|
2186
|
-
"cacheWrite": 0
|
|
2187
|
-
}]
|
|
2188
|
-
},
|
|
2189
|
-
"contextWindow": 105e4,
|
|
2190
|
-
"maxTokens": 128e3,
|
|
2191
|
-
"headers": {
|
|
2192
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
2193
|
-
"Editor-Version": "vscode/1.107.0",
|
|
2194
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
2195
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
2196
|
-
},
|
|
2197
|
-
"thinkingLevelMap": {
|
|
2198
|
-
"off": null,
|
|
2199
|
-
"minimal": "low",
|
|
2200
|
-
"low": "low",
|
|
2201
|
-
"medium": "medium",
|
|
2202
|
-
"high": "high",
|
|
2203
|
-
"xhigh": "xhigh",
|
|
2204
|
-
"max": "max"
|
|
2205
|
-
},
|
|
2206
|
-
"compat": { "supportsOpenAIGrammarTools": true }
|
|
2207
|
-
},
|
|
2208
|
-
"grok-4.5": {
|
|
2209
|
-
"id": "grok-4.5",
|
|
2210
|
-
"name": "Grok 4.5",
|
|
2211
|
-
"api": "openai-responses",
|
|
2212
|
-
"provider": "github-copilot",
|
|
2213
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
2214
|
-
"reasoning": true,
|
|
2215
|
-
"input": ["text", "image"],
|
|
2216
|
-
"cost": {
|
|
2217
|
-
"input": 2,
|
|
2218
|
-
"output": 6,
|
|
2219
|
-
"cacheRead": .5,
|
|
2220
|
-
"cacheWrite": 0,
|
|
2221
|
-
"tiers": [{
|
|
2222
|
-
"inputTokensAbove": 2e5,
|
|
2223
|
-
"input": 4,
|
|
2224
|
-
"output": 12,
|
|
2225
|
-
"cacheRead": 1,
|
|
2226
|
-
"cacheWrite": 0
|
|
2227
|
-
}]
|
|
2228
|
-
},
|
|
2229
|
-
"contextWindow": 5e5,
|
|
2230
|
-
"maxTokens": 128e3,
|
|
2231
|
-
"headers": {
|
|
2232
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
2233
|
-
"Editor-Version": "vscode/1.107.0",
|
|
2234
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
2235
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
2236
|
-
},
|
|
2237
|
-
"thinkingLevelMap": {
|
|
2238
|
-
"off": null,
|
|
2239
|
-
"minimal": null,
|
|
2240
|
-
"low": "low",
|
|
2241
|
-
"medium": "medium",
|
|
2242
|
-
"high": "high",
|
|
2243
|
-
"xhigh": null,
|
|
2244
|
-
"max": null
|
|
2245
|
-
}
|
|
2246
|
-
},
|
|
2247
|
-
"mai-code-1-flash-picker": {
|
|
2248
|
-
"id": "mai-code-1-flash-picker",
|
|
2249
|
-
"name": "MAI-Code-1-Flash",
|
|
2250
|
-
"api": "openai-responses",
|
|
2251
|
-
"provider": "github-copilot",
|
|
2252
|
-
"baseUrl": "https://api.individual.githubcopilot.com",
|
|
2253
|
-
"reasoning": true,
|
|
2254
|
-
"input": ["text"],
|
|
2255
|
-
"cost": {
|
|
2256
|
-
"input": .75,
|
|
2257
|
-
"output": 4.5,
|
|
2258
|
-
"cacheRead": .075,
|
|
2259
|
-
"cacheWrite": 0
|
|
2260
|
-
},
|
|
2261
|
-
"contextWindow": 256e3,
|
|
2262
|
-
"maxTokens": 128e3,
|
|
2263
|
-
"headers": {
|
|
2264
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
2265
|
-
"Editor-Version": "vscode/1.107.0",
|
|
2266
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
2267
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
2268
|
-
},
|
|
2269
|
-
"thinkingLevelMap": {
|
|
2270
|
-
"off": null,
|
|
2271
|
-
"minimal": null,
|
|
2272
|
-
"low": "low",
|
|
2273
|
-
"medium": "medium",
|
|
2274
|
-
"high": "high",
|
|
2275
|
-
"xhigh": null,
|
|
2276
|
-
"max": null
|
|
2277
|
-
}
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
};
|
|
2281
|
-
//#endregion
|
|
2282
|
-
//#region src/compat/vendor/pi-oauth-flows/model-catalog.ts
|
|
2283
|
-
function flattenModelCatalog(_provider, groups) {
|
|
2284
|
-
return Object.assign({}, ...Object.values(groups));
|
|
2285
|
-
}
|
|
2286
|
-
//#endregion
|
|
2287
|
-
//#region src/compat/vendor/pi-oauth-flows/github-copilot.models.ts
|
|
2288
|
-
const GITHUB_COPILOT_MODELS = flattenModelCatalog("github-copilot", github_copilot_default);
|
|
2289
|
-
//#endregion
|
|
2290
|
-
//#region src/compat/vendor/pi-oauth-flows/github-copilot.ts
|
|
2291
|
-
/**
|
|
2292
|
-
* GitHub Copilot OAuth flow
|
|
2293
|
-
*/
|
|
2294
|
-
const decode = (s) => atob(s);
|
|
2295
|
-
const CLIENT_ID$1 = decode("SXYxLmI1MDdhMDhjODdlY2ZlOTg=");
|
|
2296
|
-
const COPILOT_HEADERS = {
|
|
2297
|
-
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
2298
|
-
"Editor-Version": "vscode/1.107.0",
|
|
2299
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
2300
|
-
"Copilot-Integration-Id": "vscode-chat"
|
|
2301
|
-
};
|
|
2302
|
-
const COPILOT_API_VERSION = "2026-06-01";
|
|
2303
|
-
function normalizeDomain(input) {
|
|
2304
|
-
const trimmed = input.trim();
|
|
2305
|
-
if (!trimmed) return null;
|
|
2306
|
-
try {
|
|
2307
|
-
return (trimmed.includes("://") ? new URL(trimmed) : new URL(`https://${trimmed}`)).hostname;
|
|
2308
|
-
} catch {
|
|
2309
|
-
return null;
|
|
2310
|
-
}
|
|
2311
|
-
}
|
|
2312
|
-
function getUrls(domain) {
|
|
2313
|
-
return {
|
|
2314
|
-
deviceCodeUrl: `https://${domain}/login/device/code`,
|
|
2315
|
-
accessTokenUrl: `https://${domain}/login/oauth/access_token`,
|
|
2316
|
-
copilotTokenUrl: `https://api.${domain}/copilot_internal/v2/token`
|
|
2317
|
-
};
|
|
2318
|
-
}
|
|
2319
|
-
/**
|
|
2320
|
-
* Parse the proxy-ep from a Copilot token and convert to API base URL.
|
|
2321
|
-
* Token format: tid=...;exp=...;proxy-ep=proxy.individual.githubcopilot.com;...
|
|
2322
|
-
* Returns API URL like https://api.individual.githubcopilot.com
|
|
2323
|
-
*/
|
|
2324
|
-
function getBaseUrlFromToken(token) {
|
|
2325
|
-
const match = token.match(/proxy-ep=([^;]+)/);
|
|
2326
|
-
if (!match) return null;
|
|
2327
|
-
return `https://${match[1].replace(/^proxy\./, "api.")}`;
|
|
2328
|
-
}
|
|
2329
|
-
function getGitHubCopilotBaseUrl(token, enterpriseDomain) {
|
|
2330
|
-
if (token) {
|
|
2331
|
-
const urlFromToken = getBaseUrlFromToken(token);
|
|
2332
|
-
if (urlFromToken) return urlFromToken;
|
|
2333
|
-
}
|
|
2334
|
-
if (enterpriseDomain) return `https://copilot-api.${enterpriseDomain}`;
|
|
2335
|
-
return "https://api.individual.githubcopilot.com";
|
|
2336
|
-
}
|
|
2337
|
-
function asRecord(value) {
|
|
2338
|
-
return value && typeof value === "object" ? value : void 0;
|
|
2339
|
-
}
|
|
2340
|
-
function parseAvailableCopilotModelIds(raw, allowPolicyFallback) {
|
|
2341
|
-
const data = asRecord(raw)?.data;
|
|
2342
|
-
if (!Array.isArray(data)) throw new Error("Invalid Copilot models response");
|
|
2343
|
-
const pickerIds = [];
|
|
2344
|
-
const policyEnabledIds = [];
|
|
2345
|
-
for (const rawItem of data) {
|
|
2346
|
-
const item = asRecord(rawItem);
|
|
2347
|
-
const id = item?.id;
|
|
2348
|
-
if (!item || typeof id !== "string") continue;
|
|
2349
|
-
if (asRecord(asRecord(item.capabilities)?.supports)?.tool_calls === false) continue;
|
|
2350
|
-
const policy = asRecord(item.policy);
|
|
2351
|
-
if (item.model_picker_enabled === true && policy?.state !== "disabled") pickerIds.push(id);
|
|
2352
|
-
if (policy?.state === "enabled") policyEnabledIds.push(id);
|
|
2353
|
-
}
|
|
2354
|
-
return pickerIds.length > 0 || !allowPolicyFallback ? pickerIds : policyEnabledIds;
|
|
2355
|
-
}
|
|
2356
|
-
async function fetchAvailableGitHubCopilotModelIds(copilotToken, enterpriseDomain, signal) {
|
|
2357
|
-
const baseUrl = getGitHubCopilotBaseUrl(copilotToken, enterpriseDomain);
|
|
2358
|
-
const allowPolicyFallback = baseUrl === "https://api.individual.githubcopilot.com";
|
|
2359
|
-
return parseAvailableCopilotModelIds(await fetchJson(`${baseUrl}/models`, {
|
|
2360
|
-
headers: {
|
|
2361
|
-
Accept: "application/json",
|
|
2362
|
-
Authorization: `Bearer ${copilotToken}`,
|
|
2363
|
-
...COPILOT_HEADERS,
|
|
2364
|
-
"X-GitHub-Api-Version": COPILOT_API_VERSION
|
|
2365
|
-
},
|
|
2366
|
-
signal: AbortSignal.any([signal, AbortSignal.timeout(5e3)])
|
|
2367
|
-
}), allowPolicyFallback);
|
|
2368
|
-
}
|
|
2369
|
-
async function fetchJson(url, init) {
|
|
2370
|
-
const response = await fetch(url, init);
|
|
2371
|
-
if (!response.ok) {
|
|
2372
|
-
const text = await response.text();
|
|
2373
|
-
throw new Error(`${response.status} ${response.statusText}: ${text}`);
|
|
2374
|
-
}
|
|
2375
|
-
return response.json();
|
|
2376
|
-
}
|
|
2377
|
-
async function startDeviceFlow(domain, signal) {
|
|
2378
|
-
const data = await fetchJson(getUrls(domain).deviceCodeUrl, {
|
|
2379
|
-
method: "POST",
|
|
2380
|
-
headers: {
|
|
2381
|
-
Accept: "application/json",
|
|
2382
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
2383
|
-
"User-Agent": "GitHubCopilotChat/0.35.0"
|
|
2384
|
-
},
|
|
2385
|
-
body: new URLSearchParams({
|
|
2386
|
-
client_id: CLIENT_ID$1,
|
|
2387
|
-
scope: "read:user"
|
|
2388
|
-
}),
|
|
2389
|
-
signal
|
|
2390
|
-
});
|
|
2391
|
-
if (!data || typeof data !== "object") throw new Error("Invalid device code response");
|
|
2392
|
-
const deviceCode = data.device_code;
|
|
2393
|
-
const userCode = data.user_code;
|
|
2394
|
-
const verificationUri = data.verification_uri;
|
|
2395
|
-
const interval = data.interval;
|
|
2396
|
-
const expiresIn = data.expires_in;
|
|
2397
|
-
if (typeof deviceCode !== "string" || typeof userCode !== "string" || typeof verificationUri !== "string" || interval !== void 0 && typeof interval !== "number" || typeof expiresIn !== "number") throw new Error("Invalid device code response fields");
|
|
2398
|
-
let parsedUri;
|
|
2399
|
-
try {
|
|
2400
|
-
parsedUri = new URL(verificationUri);
|
|
2401
|
-
} catch {
|
|
2402
|
-
throw new Error("Untrusted verification_uri in device code response");
|
|
2403
|
-
}
|
|
2404
|
-
if (parsedUri.protocol !== "https:" && parsedUri.protocol !== "http:") throw new Error("Untrusted verification_uri in device code response");
|
|
2405
|
-
return {
|
|
2406
|
-
device_code: deviceCode,
|
|
2407
|
-
user_code: userCode,
|
|
2408
|
-
verification_uri: parsedUri.href,
|
|
2409
|
-
interval,
|
|
2410
|
-
expires_in: expiresIn
|
|
2411
|
-
};
|
|
2412
|
-
}
|
|
2413
|
-
async function pollForGitHubAccessToken(domain, device, signal) {
|
|
2414
|
-
const urls = getUrls(domain);
|
|
2415
|
-
return pollOAuthDeviceCodeFlow({
|
|
2416
|
-
intervalSeconds: device.interval,
|
|
2417
|
-
expiresInSeconds: device.expires_in,
|
|
2418
|
-
waitBeforeFirstPoll: true,
|
|
2419
|
-
signal,
|
|
2420
|
-
poll: async () => {
|
|
2421
|
-
const raw = await fetchJson(urls.accessTokenUrl, {
|
|
2422
|
-
method: "POST",
|
|
2423
|
-
headers: {
|
|
2424
|
-
Accept: "application/json",
|
|
2425
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
2426
|
-
"User-Agent": "GitHubCopilotChat/0.35.0"
|
|
2427
|
-
},
|
|
2428
|
-
body: new URLSearchParams({
|
|
2429
|
-
client_id: CLIENT_ID$1,
|
|
2430
|
-
device_code: device.device_code,
|
|
2431
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
2432
|
-
}),
|
|
2433
|
-
signal
|
|
2434
|
-
});
|
|
2435
|
-
if (raw && typeof raw === "object" && typeof raw.access_token === "string") return {
|
|
2436
|
-
status: "complete",
|
|
2437
|
-
value: raw.access_token
|
|
2438
|
-
};
|
|
2439
|
-
if (raw && typeof raw === "object" && typeof raw.error === "string") {
|
|
2440
|
-
const { error, error_description: description, interval } = raw;
|
|
2441
|
-
if (error === "authorization_pending") return { status: "pending" };
|
|
2442
|
-
if (error === "slow_down") return {
|
|
2443
|
-
status: "slow_down",
|
|
2444
|
-
intervalSeconds: typeof interval === "number" ? interval : void 0
|
|
2445
|
-
};
|
|
2446
|
-
return {
|
|
2447
|
-
status: "failed",
|
|
2448
|
-
message: `Device flow failed: ${error}${description ? `: ${description}` : ""}`
|
|
2449
|
-
};
|
|
2450
|
-
}
|
|
2451
|
-
return {
|
|
2452
|
-
status: "failed",
|
|
2453
|
-
message: "Invalid device token response"
|
|
2454
|
-
};
|
|
2455
|
-
}
|
|
2456
|
-
});
|
|
2457
|
-
}
|
|
2458
|
-
async function refreshGitHubCopilotAccessToken(refreshToken, enterpriseDomain, signal) {
|
|
2459
|
-
const raw = await fetchJson(getUrls(enterpriseDomain || "github.com").copilotTokenUrl, {
|
|
2460
|
-
headers: {
|
|
2461
|
-
Accept: "application/json",
|
|
2462
|
-
Authorization: `Bearer ${refreshToken}`,
|
|
2463
|
-
...COPILOT_HEADERS
|
|
2464
|
-
},
|
|
2465
|
-
signal
|
|
2466
|
-
});
|
|
2467
|
-
if (!raw || typeof raw !== "object") throw new Error("Invalid Copilot token response");
|
|
2468
|
-
const token = raw.token;
|
|
2469
|
-
const expiresAt = raw.expires_at;
|
|
2470
|
-
if (typeof token !== "string" || typeof expiresAt !== "number") throw new Error("Invalid Copilot token response fields");
|
|
2471
|
-
return {
|
|
2472
|
-
type: "oauth",
|
|
2473
|
-
refresh: refreshToken,
|
|
2474
|
-
access: token,
|
|
2475
|
-
expires: expiresAt * 1e3 - 3e5,
|
|
2476
|
-
enterpriseUrl: enterpriseDomain
|
|
2477
|
-
};
|
|
2478
|
-
}
|
|
2479
|
-
/**
|
|
2480
|
-
* Refresh GitHub Copilot token
|
|
2481
|
-
*/
|
|
2482
|
-
async function refreshGitHubCopilotToken(refreshToken, enterpriseDomain, signal) {
|
|
2483
|
-
const credentials = await refreshGitHubCopilotAccessToken(refreshToken, enterpriseDomain, signal);
|
|
2484
|
-
return {
|
|
2485
|
-
...credentials,
|
|
2486
|
-
availableModelIds: await fetchAvailableGitHubCopilotModelIds(credentials.access, enterpriseDomain, signal)
|
|
2487
|
-
};
|
|
2488
|
-
}
|
|
2489
|
-
/**
|
|
2490
|
-
* Enable a model for the user's GitHub Copilot account.
|
|
2491
|
-
* This is required for some models (like Claude, Grok) before they can be used.
|
|
2492
|
-
*/
|
|
2493
|
-
async function enableGitHubCopilotModel(token, modelId, enterpriseDomain, signal) {
|
|
2494
|
-
const url = `${getGitHubCopilotBaseUrl(token, enterpriseDomain)}/models/${modelId}/policy`;
|
|
2495
|
-
try {
|
|
2496
|
-
return (await fetch(url, {
|
|
2497
|
-
method: "POST",
|
|
2498
|
-
headers: {
|
|
2499
|
-
"Content-Type": "application/json",
|
|
2500
|
-
Authorization: `Bearer ${token}`,
|
|
2501
|
-
...COPILOT_HEADERS,
|
|
2502
|
-
"openai-intent": "chat-policy",
|
|
2503
|
-
"x-interaction-type": "chat-policy"
|
|
2504
|
-
},
|
|
2505
|
-
body: JSON.stringify({ state: "enabled" }),
|
|
2506
|
-
signal
|
|
2507
|
-
})).ok;
|
|
2508
|
-
} catch (error) {
|
|
2509
|
-
if (signal.aborted) throw error;
|
|
2510
|
-
return false;
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
/**
|
|
2514
|
-
* Enable all known GitHub Copilot models that may require policy acceptance.
|
|
2515
|
-
* Called after successful login to ensure all models are available.
|
|
2516
|
-
*/
|
|
2517
|
-
async function enableAllGitHubCopilotModels(token, enterpriseDomain, signal) {
|
|
2518
|
-
const models = Object.values(GITHUB_COPILOT_MODELS);
|
|
2519
|
-
await Promise.all(models.map(async (model) => {
|
|
2520
|
-
await enableGitHubCopilotModel(token, model.id, enterpriseDomain, signal);
|
|
2521
|
-
}));
|
|
2522
|
-
}
|
|
2523
|
-
async function loginGitHubCopilot(interaction) {
|
|
2524
|
-
const input = await interaction.prompt({
|
|
2525
|
-
type: "text",
|
|
2526
|
-
message: "GitHub Enterprise URL/domain (blank for github.com)",
|
|
2527
|
-
placeholder: "company.ghe.com"
|
|
2528
|
-
});
|
|
2529
|
-
if (interaction.signal.aborted) throw new Error("Login cancelled");
|
|
2530
|
-
const trimmed = input.trim();
|
|
2531
|
-
const enterpriseDomain = normalizeDomain(input);
|
|
2532
|
-
if (trimmed && !enterpriseDomain) throw new Error("Invalid GitHub Enterprise URL/domain");
|
|
2533
|
-
const domain = enterpriseDomain || "github.com";
|
|
2534
|
-
const device = await startDeviceFlow(domain, interaction.signal);
|
|
2535
|
-
interaction.notify({
|
|
2536
|
-
type: "device_code",
|
|
2537
|
-
userCode: device.user_code,
|
|
2538
|
-
verificationUri: device.verification_uri,
|
|
2539
|
-
intervalSeconds: device.interval,
|
|
2540
|
-
expiresInSeconds: device.expires_in
|
|
2541
|
-
});
|
|
2542
|
-
const credentials = await refreshGitHubCopilotAccessToken(await pollForGitHubAccessToken(domain, device, interaction.signal), enterpriseDomain ?? void 0, interaction.signal);
|
|
2543
|
-
interaction.notify({
|
|
2544
|
-
type: "progress",
|
|
2545
|
-
message: "Enabling models..."
|
|
2546
|
-
});
|
|
2547
|
-
await enableAllGitHubCopilotModels(credentials.access, enterpriseDomain ?? void 0, interaction.signal);
|
|
2548
|
-
return {
|
|
2549
|
-
...credentials,
|
|
2550
|
-
availableModelIds: await fetchAvailableGitHubCopilotModelIds(credentials.access, enterpriseDomain ?? void 0, interaction.signal)
|
|
2551
|
-
};
|
|
2552
|
-
}
|
|
2553
|
-
function copilotEnterpriseDomain(credential) {
|
|
2554
|
-
const enterpriseUrl = credential.enterpriseUrl;
|
|
2555
|
-
if (typeof enterpriseUrl !== "string" || !enterpriseUrl) return void 0;
|
|
2556
|
-
return normalizeDomain(enterpriseUrl) ?? void 0;
|
|
2557
|
-
}
|
|
2558
|
-
const githubCopilotOAuth = {
|
|
2559
|
-
name: "GitHub Copilot",
|
|
2560
|
-
isSubscription: true,
|
|
2561
|
-
login: loginGitHubCopilot,
|
|
2562
|
-
refresh: (credential, signal) => refreshGitHubCopilotToken(credential.refresh, copilotEnterpriseDomain(credential), signal),
|
|
2563
|
-
/** Derive the credential-specific proxy endpoint for each request. */
|
|
2564
|
-
async toAuth(credential) {
|
|
2565
|
-
return {
|
|
2566
|
-
apiKey: credential.access,
|
|
2567
|
-
baseUrl: getGitHubCopilotBaseUrl(credential.access, copilotEnterpriseDomain(credential))
|
|
2568
|
-
};
|
|
2569
|
-
}
|
|
2570
|
-
};
|
|
2571
|
-
//#endregion
|
|
2572
|
-
//#region src/compat/vendor/pi-oauth-flows/kimi-coding.ts
|
|
2573
|
-
/**
|
|
2574
|
-
* Kimi Code (subscription) OAuth flow
|
|
2575
|
-
*
|
|
2576
|
-
* RFC 8628 device authorization grant against https://auth.kimi.com with JSON
|
|
2577
|
-
* responses. The access token authenticates requests to
|
|
2578
|
-
* https://api.kimi.com/coding as an `Authorization: Bearer` header.
|
|
2579
|
-
*/
|
|
2580
|
-
const CLIENT_ID = "17e5f671-d194-4dfb-9706-5516cb48c098";
|
|
2581
|
-
const DEFAULT_OAUTH_HOST = "https://auth.kimi.com";
|
|
2582
|
-
const DEVICE_CODE_TIMEOUT_SECONDS = 900;
|
|
2583
|
-
const DEFAULT_POLL_INTERVAL_SECONDS = 5;
|
|
2584
|
-
const REQUEST_TIMEOUT_MS = 3e4;
|
|
2585
|
-
const REFRESH_MAX_RETRIES = 3;
|
|
2586
|
-
function getOauthHost() {
|
|
2587
|
-
return (getProviderEnvValue("KIMI_CODE_OAUTH_HOST") || getProviderEnvValue("KIMI_OAUTH_HOST") || DEFAULT_OAUTH_HOST).replace(/\/+$/, "");
|
|
2588
|
-
}
|
|
2589
|
-
function requestSignal(signal) {
|
|
2590
|
-
return AbortSignal.any([AbortSignal.timeout(REQUEST_TIMEOUT_MS), signal]);
|
|
2591
|
-
}
|
|
2592
|
-
function formUrlEncode(fields) {
|
|
2593
|
-
return new URLSearchParams(fields).toString();
|
|
2594
|
-
}
|
|
2595
|
-
async function readJson(response) {
|
|
2596
|
-
try {
|
|
2597
|
-
const json = await response.json();
|
|
2598
|
-
return json && typeof json === "object" ? json : null;
|
|
2599
|
-
} catch {
|
|
2600
|
-
return null;
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
2603
|
-
/** The verification URI is opened in the user's browser; only http(s) URLs are trusted. */
|
|
2604
|
-
function trustedHttpUrl(value) {
|
|
2605
|
-
if (typeof value !== "string" || !value) return null;
|
|
2606
|
-
try {
|
|
2607
|
-
const url = new URL(value);
|
|
2608
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") return null;
|
|
2609
|
-
return url.href;
|
|
2610
|
-
} catch {
|
|
2611
|
-
return null;
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
2614
|
-
async function startDeviceAuthorization(oauthHost, signal) {
|
|
2615
|
-
const response = await fetch(`${oauthHost}/api/oauth/device_authorization`, {
|
|
2616
|
-
method: "POST",
|
|
2617
|
-
headers: {
|
|
2618
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
2619
|
-
Accept: "application/json"
|
|
2620
|
-
},
|
|
2621
|
-
body: formUrlEncode({ client_id: CLIENT_ID }),
|
|
2622
|
-
signal: requestSignal(signal)
|
|
2623
|
-
});
|
|
2624
|
-
if (!response.ok) {
|
|
2625
|
-
const text = await response.text().catch(() => "");
|
|
2626
|
-
throw new Error(`Kimi Code device authorization failed with status ${response.status}${text ? `: ${text}` : ""}`);
|
|
2627
|
-
}
|
|
2628
|
-
const json = await readJson(response);
|
|
2629
|
-
const deviceCode = json?.device_code;
|
|
2630
|
-
const userCode = json?.user_code;
|
|
2631
|
-
const verificationUri = json?.verification_uri;
|
|
2632
|
-
const verificationUriComplete = json?.verification_uri_complete;
|
|
2633
|
-
if (typeof deviceCode !== "string" || typeof userCode !== "string" || typeof verificationUri !== "string" || typeof verificationUriComplete !== "string" || !trustedHttpUrl(verificationUriComplete) || !trustedHttpUrl(verificationUri)) throw new Error(`Invalid Kimi Code device authorization response: ${JSON.stringify(json)}`);
|
|
2634
|
-
const interval = json?.interval;
|
|
2635
|
-
const expiresIn = json?.expires_in;
|
|
2636
|
-
return {
|
|
2637
|
-
deviceCode,
|
|
2638
|
-
userCode,
|
|
2639
|
-
verificationUri,
|
|
2640
|
-
verificationUriComplete,
|
|
2641
|
-
intervalSeconds: typeof interval === "number" && Number.isFinite(interval) && interval > 0 ? interval : DEFAULT_POLL_INTERVAL_SECONDS,
|
|
2642
|
-
expiresInSeconds: typeof expiresIn === "number" && Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : DEVICE_CODE_TIMEOUT_SECONDS
|
|
2643
|
-
};
|
|
2644
|
-
}
|
|
2645
|
-
function parseTokenResponse(json, operation) {
|
|
2646
|
-
const accessToken = json?.access_token;
|
|
2647
|
-
const refreshToken = json?.refresh_token;
|
|
2648
|
-
const expiresIn = json?.expires_in;
|
|
2649
|
-
if (typeof accessToken !== "string" || !accessToken || typeof refreshToken !== "string" || !refreshToken || typeof expiresIn !== "number" || !Number.isFinite(expiresIn) || expiresIn <= 0) throw new Error(`Kimi Code token ${operation} response missing fields: ${JSON.stringify(json)}`);
|
|
2650
|
-
return {
|
|
2651
|
-
access: accessToken,
|
|
2652
|
-
refresh: refreshToken,
|
|
2653
|
-
expires: Date.now() + expiresIn * 1e3
|
|
2654
|
-
};
|
|
2655
|
-
}
|
|
2656
|
-
async function pollForToken(oauthHost, device, signal) {
|
|
2657
|
-
return pollOAuthDeviceCodeFlow({
|
|
2658
|
-
intervalSeconds: device.intervalSeconds,
|
|
2659
|
-
expiresInSeconds: device.expiresInSeconds,
|
|
2660
|
-
waitBeforeFirstPoll: true,
|
|
2661
|
-
signal,
|
|
2662
|
-
poll: async () => {
|
|
2663
|
-
const response = await fetch(`${oauthHost}/api/oauth/token`, {
|
|
2664
|
-
method: "POST",
|
|
2665
|
-
headers: {
|
|
2666
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
2667
|
-
Accept: "application/json"
|
|
2668
|
-
},
|
|
2669
|
-
body: formUrlEncode({
|
|
2670
|
-
client_id: CLIENT_ID,
|
|
2671
|
-
device_code: device.deviceCode,
|
|
2672
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
2673
|
-
}),
|
|
2674
|
-
signal: requestSignal(signal)
|
|
2675
|
-
});
|
|
2676
|
-
if (response.status >= 500) {
|
|
2677
|
-
const text = await response.text().catch(() => "");
|
|
2678
|
-
return {
|
|
2679
|
-
status: "failed",
|
|
2680
|
-
message: `Kimi Code device token request failed with status ${response.status}${text ? `: ${text}` : ""}`
|
|
2681
|
-
};
|
|
2682
|
-
}
|
|
2683
|
-
const json = await readJson(response);
|
|
2684
|
-
if (response.ok && typeof json?.access_token === "string") try {
|
|
2685
|
-
return {
|
|
2686
|
-
status: "complete",
|
|
2687
|
-
value: parseTokenResponse(json, "poll")
|
|
2688
|
-
};
|
|
2689
|
-
} catch (error) {
|
|
2690
|
-
return {
|
|
2691
|
-
status: "failed",
|
|
2692
|
-
message: error instanceof Error ? error.message : String(error)
|
|
2693
|
-
};
|
|
2694
|
-
}
|
|
2695
|
-
const error = json?.error;
|
|
2696
|
-
const description = typeof json?.error_description === "string" ? `: ${json.error_description}` : "";
|
|
2697
|
-
if (error === "authorization_pending") return { status: "pending" };
|
|
2698
|
-
if (error === "slow_down") {
|
|
2699
|
-
const interval = json?.interval;
|
|
2700
|
-
return {
|
|
2701
|
-
status: "slow_down",
|
|
2702
|
-
intervalSeconds: typeof interval === "number" && interval > 0 ? interval : void 0
|
|
2703
|
-
};
|
|
2704
|
-
}
|
|
2705
|
-
if (error === "expired_token") return {
|
|
2706
|
-
status: "failed",
|
|
2707
|
-
message: "Kimi Code device authorization expired. Please restart login."
|
|
2708
|
-
};
|
|
2709
|
-
if (error === "access_denied") return {
|
|
2710
|
-
status: "failed",
|
|
2711
|
-
message: "Kimi Code login was denied."
|
|
2712
|
-
};
|
|
2713
|
-
return {
|
|
2714
|
-
status: "failed",
|
|
2715
|
-
message: `Kimi Code device token request failed (status ${response.status})${typeof error === "string" ? `: ${error}${description}` : ""}`
|
|
2716
|
-
};
|
|
2717
|
-
}
|
|
2718
|
-
});
|
|
2719
|
-
}
|
|
2720
|
-
function sleep(ms, signal) {
|
|
2721
|
-
return new Promise((resolve, reject) => {
|
|
2722
|
-
signal.throwIfAborted();
|
|
2723
|
-
const onAbort = () => {
|
|
2724
|
-
clearTimeout(timeout);
|
|
2725
|
-
reject(signal.reason);
|
|
2726
|
-
};
|
|
2727
|
-
const timeout = setTimeout(() => {
|
|
2728
|
-
signal.removeEventListener("abort", onAbort);
|
|
2729
|
-
resolve();
|
|
2730
|
-
}, ms);
|
|
2731
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
2732
|
-
});
|
|
2733
|
-
}
|
|
2734
|
-
function isRetryableRefreshFailure(response) {
|
|
2735
|
-
return response.status === 429 || response.status >= 500;
|
|
2736
|
-
}
|
|
2737
|
-
async function refreshToken(oauthHost, refreshTokenValue, signal) {
|
|
2738
|
-
let lastError;
|
|
2739
|
-
for (let attempt = 0; attempt <= REFRESH_MAX_RETRIES; attempt++) {
|
|
2740
|
-
if (attempt > 0) await sleep(1e3 * 2 ** (attempt - 1), signal);
|
|
2741
|
-
if (signal.aborted) throw new Error("Kimi Code token refresh aborted");
|
|
2742
|
-
let response;
|
|
2743
|
-
try {
|
|
2744
|
-
response = await fetch(`${oauthHost}/api/oauth/token`, {
|
|
2745
|
-
method: "POST",
|
|
2746
|
-
headers: {
|
|
2747
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
2748
|
-
Accept: "application/json"
|
|
2749
|
-
},
|
|
2750
|
-
body: formUrlEncode({
|
|
2751
|
-
client_id: CLIENT_ID,
|
|
2752
|
-
grant_type: "refresh_token",
|
|
2753
|
-
refresh_token: refreshTokenValue
|
|
2754
|
-
}),
|
|
2755
|
-
signal: requestSignal(signal)
|
|
2756
|
-
});
|
|
2757
|
-
} catch (error) {
|
|
2758
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2759
|
-
continue;
|
|
2760
|
-
}
|
|
2761
|
-
const json = await readJson(response);
|
|
2762
|
-
if (response.ok) return parseTokenResponse(json, "refresh");
|
|
2763
|
-
if (response.status === 401 || response.status === 403 || json?.error === "invalid_grant") {
|
|
2764
|
-
const description = typeof json?.error_description === "string" ? `: ${json.error_description}` : "";
|
|
2765
|
-
throw new Error(`Kimi Code token refresh unauthorized (status ${response.status})${description}`);
|
|
2766
|
-
}
|
|
2767
|
-
if (isRetryableRefreshFailure(response) && attempt < REFRESH_MAX_RETRIES) {
|
|
2768
|
-
lastError = /* @__PURE__ */ new Error(`Kimi Code token refresh failed with status ${response.status}`);
|
|
2769
|
-
continue;
|
|
2770
|
-
}
|
|
2771
|
-
const text = JSON.stringify(json);
|
|
2772
|
-
throw new Error(`Kimi Code token refresh failed with status ${response.status}${text ? `: ${text}` : ""}`);
|
|
2773
|
-
}
|
|
2774
|
-
throw lastError ?? /* @__PURE__ */ new Error("Kimi Code token refresh failed");
|
|
2775
|
-
}
|
|
2776
|
-
async function loginKimiCoding(interaction) {
|
|
2777
|
-
const oauthHost = getOauthHost();
|
|
2778
|
-
const device = await startDeviceAuthorization(oauthHost, interaction.signal);
|
|
2779
|
-
interaction.notify({
|
|
2780
|
-
type: "device_code",
|
|
2781
|
-
userCode: device.userCode,
|
|
2782
|
-
verificationUri: device.verificationUriComplete,
|
|
2783
|
-
intervalSeconds: device.intervalSeconds,
|
|
2784
|
-
expiresInSeconds: device.expiresInSeconds
|
|
2785
|
-
});
|
|
2786
|
-
const token = await pollForToken(oauthHost, device, interaction.signal);
|
|
2787
|
-
return {
|
|
2788
|
-
type: "oauth",
|
|
2789
|
-
access: token.access,
|
|
2790
|
-
refresh: token.refresh,
|
|
2791
|
-
expires: token.expires
|
|
2792
|
-
};
|
|
2793
|
-
}
|
|
2794
|
-
const kimiCodingOAuth = {
|
|
2795
|
-
name: "Kimi Code (subscription)",
|
|
2796
|
-
isSubscription: true,
|
|
2797
|
-
loginLabel: "Sign in with Kimi Code",
|
|
2798
|
-
login: loginKimiCoding,
|
|
2799
|
-
refresh: async (credential, signal) => {
|
|
2800
|
-
const token = await refreshToken(getOauthHost(), credential.refresh, signal);
|
|
2801
|
-
return {
|
|
2802
|
-
type: "oauth",
|
|
2803
|
-
access: token.access,
|
|
2804
|
-
refresh: token.refresh,
|
|
2805
|
-
expires: token.expires
|
|
2806
|
-
};
|
|
2807
|
-
},
|
|
2808
|
-
async toAuth(credential) {
|
|
2809
|
-
return { headers: { Authorization: `Bearer ${credential.access}` } };
|
|
2810
|
-
}
|
|
2811
|
-
};
|
|
2812
|
-
//#endregion
|
|
2813
|
-
//#region src/compat/pi-ai.ts
|
|
2814
|
-
const EXTENDED_THINKING_LEVELS = [
|
|
2815
|
-
"off",
|
|
2816
|
-
"minimal",
|
|
2817
|
-
"low",
|
|
2818
|
-
"medium",
|
|
2819
|
-
"high",
|
|
2820
|
-
"xhigh",
|
|
2821
|
-
"max"
|
|
2822
|
-
];
|
|
2823
|
-
function getSupportedThinkingLevels(model) {
|
|
2824
|
-
return EXTENDED_THINKING_LEVELS.filter((level) => {
|
|
2825
|
-
const mapped = model.thinkingLevelMap?.[level];
|
|
2826
|
-
if (mapped === null) return false;
|
|
2827
|
-
if (level === "xhigh" || level === "max") return mapped !== void 0;
|
|
2828
|
-
return true;
|
|
2829
|
-
});
|
|
2830
|
-
}
|
|
2831
|
-
function clampThinkingLevel(model, level) {
|
|
2832
|
-
const availableLevels = getSupportedThinkingLevels(model);
|
|
2833
|
-
if (availableLevels.includes(level)) return level;
|
|
2834
|
-
const requestedIndex = EXTENDED_THINKING_LEVELS.indexOf(level);
|
|
2835
|
-
if (requestedIndex === -1) return availableLevels[0] ?? "off";
|
|
2836
|
-
for (let i = requestedIndex; i < EXTENDED_THINKING_LEVELS.length; i += 1) {
|
|
2837
|
-
const candidate = EXTENDED_THINKING_LEVELS[i];
|
|
2838
|
-
if (availableLevels.includes(candidate)) return candidate;
|
|
2839
|
-
}
|
|
2840
|
-
for (let i = requestedIndex - 1; i >= 0; i -= 1) {
|
|
2841
|
-
const candidate = EXTENDED_THINKING_LEVELS[i];
|
|
2842
|
-
if (availableLevels.includes(candidate)) return candidate;
|
|
2843
|
-
}
|
|
2844
|
-
return availableLevels[0] ?? "off";
|
|
2845
|
-
}
|
|
2846
|
-
function modelsAreEqual(a, b) {
|
|
2847
|
-
if (a === null || a === void 0 || b === null || b === void 0) return false;
|
|
2848
|
-
return a.id === b.id && a.provider === b.provider;
|
|
2849
|
-
}
|
|
2850
|
-
function contentText(content, separator = "\n") {
|
|
2851
|
-
if (typeof content === "string") return content;
|
|
2852
|
-
return content.filter((block) => block.type === "text").map((block) => String(block.text ?? "")).join(separator);
|
|
2853
|
-
}
|
|
2854
|
-
const compatProviders = /* @__PURE__ */ new Map();
|
|
2855
|
-
function registerProvider(name, provider) {
|
|
2856
|
-
compatProviders.set(name, provider);
|
|
2857
|
-
}
|
|
2858
|
-
function getProviders() {
|
|
2859
|
-
return [...compatProviders.keys()];
|
|
2860
|
-
}
|
|
2861
|
-
function getProvider(name) {
|
|
2862
|
-
return compatProviders.get(name);
|
|
2863
|
-
}
|
|
2864
|
-
function getModel(_provider, _id) {}
|
|
2865
|
-
function getModels(_provider) {
|
|
2866
|
-
return [];
|
|
2867
|
-
}
|
|
2868
|
-
function unbridgedTransport(api) {
|
|
2869
|
-
throw new Error(`pi2dsh: the ${api} protocol client is not bridged; model transports stay native to DSH llm/credentials`);
|
|
2870
|
-
}
|
|
2871
|
-
function openAICompletionsApi() {
|
|
2872
|
-
return lazyApi(async () => unbridgedTransport("openai-completions"), void 0);
|
|
2873
|
-
}
|
|
2874
|
-
function openAIResponsesApi() {
|
|
2875
|
-
return lazyApi(async () => unbridgedTransport("openai-responses"), {
|
|
2876
|
-
fetchDeferred: true,
|
|
2877
|
-
cancelDeferred: true
|
|
2878
|
-
});
|
|
2879
|
-
}
|
|
2880
|
-
function anthropicMessagesApi() {
|
|
2881
|
-
return lazyApi(async () => unbridgedTransport("anthropic-messages"), void 0);
|
|
2882
|
-
}
|
|
2883
|
-
function builtinProviders() {
|
|
2884
|
-
const entry = (id, name, baseUrl, oauth) => ({
|
|
2885
|
-
id,
|
|
2886
|
-
name,
|
|
2887
|
-
api: "openai-completions",
|
|
2888
|
-
baseUrl,
|
|
2889
|
-
models: [],
|
|
2890
|
-
auth: { oauth }
|
|
2891
|
-
});
|
|
2892
|
-
return [
|
|
2893
|
-
entry("openai-codex", "OpenAI (ChatGPT Plus/Pro)", "https://chatgpt.com/backend-api/codex", openaiCodexOAuth),
|
|
2894
|
-
entry("anthropic", "Anthropic", "https://api.anthropic.com", anthropicOAuth),
|
|
2895
|
-
entry("github-copilot", "GitHub Copilot", "https://api.githubcopilot.com", githubCopilotOAuth),
|
|
2896
|
-
entry("kimi-coding", "Kimi Code", "https://api.moonshot.ai/anthropic", kimiCodingOAuth)
|
|
2897
|
-
];
|
|
2898
|
-
}
|
|
2899
|
-
const loadOpenAICodexOAuth = async () => openaiCodexOAuth;
|
|
2900
|
-
const loadAnthropicOAuth = async () => anthropicOAuth;
|
|
2901
|
-
const loadGitHubCopilotOAuth = async () => githubCopilotOAuth;
|
|
2902
|
-
const loadKimiCodingOAuth = async () => kimiCodingOAuth;
|
|
2903
|
-
function complete(..._args) {
|
|
2904
|
-
throw new Error("pi2dsh: pi-ai complete() routes model calls through Pi provider SDKs; use DSH llm adapters instead");
|
|
2905
|
-
}
|
|
2906
|
-
function stream(..._args) {
|
|
2907
|
-
throw new Error("pi2dsh: pi-ai stream() routes model calls through Pi provider SDKs; use DSH llm adapters instead");
|
|
2908
|
-
}
|
|
2909
|
-
function StringEnum(values, options = {}) {
|
|
2910
|
-
return {
|
|
2911
|
-
type: "string",
|
|
2912
|
-
enum: values,
|
|
2913
|
-
...options.description !== void 0 ? { description: options.description } : {},
|
|
2914
|
-
...options.default !== void 0 ? { default: options.default } : {}
|
|
2915
|
-
};
|
|
2916
|
-
}
|
|
2917
|
-
//#endregion
|
|
2918
|
-
export { AssistantMessageEventStream, ModelsError, StringEnum, typebox_exports as Type, anthropicMessagesApi, anthropicOAuth, builtinProviders, clampThinkingLevel, complete, contentText, createProvider, generatePKCE, getModel, getModels, getProvider, getProviders, getSupportedThinkingLevels, githubCopilotOAuth, isContextOverflow, isRecoverableLength, isRetryableAssistantError, kimiCodingOAuth, lazyApi, lazyStream, loadAnthropicOAuth, loadGitHubCopilotOAuth, loadKimiCodingOAuth, loadOpenAICodexOAuth, modelsAreEqual, openAICompletionsApi, openAIResponsesApi, openaiCodexOAuth, pollOAuthDeviceCodeFlow, registerProvider, stream, uuidv7 };
|
|
2919
|
-
|
|
2920
|
-
//# sourceMappingURL=pi-ai.mjs.map
|
|
3
|
+
import { A as pollOAuthDeviceCodeFlow, C as stream, D as anthropicOAuth, E as githubCopilotOAuth, F as AssistantMessageEventStream, I as isRetryableAssistantError, L as isContextOverflow, M as createProvider, N as lazyApi, O as openaiCodexOAuth, P as lazyStream, R as isRecoverableLength, S as registerProvider, T as kimiCodingOAuth, _ as loadOpenAICodexOAuth, a as complete, b as openAIResponsesApi, c as getApiProviders, d as getProvider, f as getProviders, g as loadKimiCodingOAuth, h as loadGitHubCopilotOAuth, i as clampThinkingLevel, j as ModelsError, k as generatePKCE, l as getModel, m as loadAnthropicOAuth, n as anthropicMessagesApi, o as contentText, p as getSupportedThinkingLevels, r as builtinProviders, s as getApiProvider, t as StringEnum, u as getModels, v as modelsAreEqual, w as unregisterApiProviders, x as registerApiProvider, y as openAICompletionsApi } from "../pi-ai-CWFlgigJ.mjs";
|
|
4
|
+
export { AssistantMessageEventStream, ModelsError, StringEnum, typebox_exports as Type, anthropicMessagesApi, anthropicOAuth, builtinProviders, clampThinkingLevel, complete, contentText, createProvider, generatePKCE, getApiProvider, getApiProviders, getModel, getModels, getProvider, getProviders, getSupportedThinkingLevels, githubCopilotOAuth, isContextOverflow, isRecoverableLength, isRetryableAssistantError, kimiCodingOAuth, lazyApi, lazyStream, loadAnthropicOAuth, loadGitHubCopilotOAuth, loadKimiCodingOAuth, loadOpenAICodexOAuth, modelsAreEqual, openAICompletionsApi, openAIResponsesApi, openaiCodexOAuth, pollOAuthDeviceCodeFlow, registerApiProvider, registerProvider, stream, unregisterApiProviders, uuidv7 };
|