bingocode 1.0.20 → 1.0.22
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/package.json +1 -1
- package/src/components/Onboarding.tsx +6 -2
- package/src/entrypoints/init.ts +1 -9
- package/src/manager/CliMenuManager.tsx +125 -52
- package/src/server/cli/providerManager.ts +18 -67
- package/src/server/cli/providersMenu.tsx +44 -74
- package/src/server/proxy/handler.ts +289 -302
- package/src/server/services/providerService.ts +24 -10
- package/src/utils/auth.ts +1 -1
- package/src/utils/config.ts +5 -11
- package/src/utils/preflightChecks.tsx +3 -3
- package/src/utils/proxy.ts +1 -13
- package/src/server/proxy/streaming/anthropicStreamLabeler.ts +0 -56
|
@@ -409,8 +409,8 @@ export class ProviderService {
|
|
|
409
409
|
}
|
|
410
410
|
|
|
411
411
|
try {
|
|
412
|
-
const
|
|
413
|
-
const res = await fetch(url, { headers, signal: AbortSignal.timeout(10000), ...
|
|
412
|
+
const directOpts = getDirectFetchOptions()
|
|
413
|
+
const res = await fetch(url, { headers, signal: AbortSignal.timeout(10000), ...directOpts })
|
|
414
414
|
if (!res.ok) {
|
|
415
415
|
console.error(`[ProviderService] Failed to fetch models from ${url}: ${res.status}`)
|
|
416
416
|
return []
|
|
@@ -438,11 +438,26 @@ export class ProviderService {
|
|
|
438
438
|
|
|
439
439
|
// If no modelId provided, try to fetch from provider or use preset default
|
|
440
440
|
let modelId = overrides?.modelId || provider.models.main
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
441
|
+
const needsAutoDetect =
|
|
442
|
+
!modelId ||
|
|
443
|
+
modelId === 'auto' ||
|
|
444
|
+
(apiFormat !== 'anthropic' && modelId.startsWith('claude-'))
|
|
445
|
+
if (needsAutoDetect) {
|
|
446
|
+
const fetched = await this.fetchProviderModels(id).catch(() => [])
|
|
447
|
+
if (fetched.length > 0) {
|
|
448
|
+
modelId = fetched[0] // Use first available model for testing
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// 兜底:如果仍然没有有效的 modelId,直接返回有意义的错误
|
|
453
|
+
if (!modelId) {
|
|
454
|
+
return {
|
|
455
|
+
connectivity: {
|
|
456
|
+
success: false,
|
|
457
|
+
latencyMs: 0,
|
|
458
|
+
error: '无法确定测试用模型:models.main 为空且自动拉取模型列表失败。请先在槽位配置中选择模型,或检查 API Key 和网络连接。',
|
|
459
|
+
},
|
|
460
|
+
}
|
|
446
461
|
}
|
|
447
462
|
|
|
448
463
|
if (!baseUrl || !provider.apiKey) {
|
|
@@ -491,7 +506,6 @@ export class ProviderService {
|
|
|
491
506
|
const start = Date.now()
|
|
492
507
|
try {
|
|
493
508
|
const { url, headers, body } = buildDirectTestRequest(base, apiKey, modelId, format)
|
|
494
|
-
// 使用 getDirectFetchOptions 以绕开系统代理,测试直接连接
|
|
495
509
|
const directOpts = getDirectFetchOptions()
|
|
496
510
|
const response = await fetch(url, {
|
|
497
511
|
method: 'POST',
|
|
@@ -556,13 +570,13 @@ export class ProviderService {
|
|
|
556
570
|
}
|
|
557
571
|
|
|
558
572
|
// Call upstream with transformed request
|
|
559
|
-
const
|
|
573
|
+
const directOpts = getDirectFetchOptions()
|
|
560
574
|
const response = await fetch(upstreamUrl, {
|
|
561
575
|
method: 'POST',
|
|
562
576
|
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
|
563
577
|
body: JSON.stringify(transformedBody),
|
|
564
578
|
signal: AbortSignal.timeout(30000),
|
|
565
|
-
...
|
|
579
|
+
...directOpts,
|
|
566
580
|
})
|
|
567
581
|
|
|
568
582
|
if (!response.ok) {
|
package/src/utils/auth.ts
CHANGED
|
@@ -88,7 +88,7 @@ const DEFAULT_API_KEY_HELPER_TTL = 5 * 60 * 1000
|
|
|
88
88
|
* who runs `claude` in their terminal with an API key sees every CCD session
|
|
89
89
|
* also use that key — and fail if it's stale/wrong-org.
|
|
90
90
|
*/
|
|
91
|
-
function isManagedOAuthContext(): boolean {
|
|
91
|
+
export function isManagedOAuthContext(): boolean {
|
|
92
92
|
return (
|
|
93
93
|
isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) ||
|
|
94
94
|
process.env.CLAUDE_CODE_ENTRYPOINT === 'claude-desktop'
|
package/src/utils/config.ts
CHANGED
|
@@ -1344,17 +1344,11 @@ export function enableConfigs(): void {
|
|
|
1344
1344
|
// to prevent us from adding config reading during module initialization
|
|
1345
1345
|
configReadingAllowed = true
|
|
1346
1346
|
// We only check the global config because currently all the configs share a file
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
)
|
|
1353
|
-
} catch (e) {
|
|
1354
|
-
logForDebugging(`Failed to load config during enableConfigs: ${e}`, { level: 'error' })
|
|
1355
|
-
// If it's a corrupted file, we allow the boostrap to continue with defaults
|
|
1356
|
-
// instead of hard-crashing the process.
|
|
1357
|
-
}
|
|
1347
|
+
getConfig(
|
|
1348
|
+
getGlobalClaudeFile(),
|
|
1349
|
+
createDefaultGlobalConfig,
|
|
1350
|
+
true /* throw on invalid */,
|
|
1351
|
+
)
|
|
1358
1352
|
|
|
1359
1353
|
logForDiagnosticsNoPII('info', 'enable_configs_completed', {
|
|
1360
1354
|
duration_ms: Date.now() - startTime,
|
|
@@ -202,7 +202,7 @@ export function PreflightStep(t0) {
|
|
|
202
202
|
|
|
203
203
|
//@C:ID=F.PC._temp;K=F;V=1.0;P=Helper function for process exit;D=UI;M=Connectivity;S=Utility;In=void;Out=void
|
|
204
204
|
function _temp() {
|
|
205
|
-
console.log("F.PC._temp
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
console.log("F.PC._temp");
|
|
206
|
+
|
|
207
|
+
return process.exit(1);
|
|
208
208
|
}
|
package/src/utils/proxy.ts
CHANGED
|
@@ -334,26 +334,14 @@ export function getDirectFetchOptions(): {
|
|
|
334
334
|
return { ...base, proxy: undefined, ...getTLSFetchOptions() }
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
-
// Check if system proxy exists
|
|
338
|
-
const proxyUrl = getProxyUrl()
|
|
339
|
-
if (!proxyUrl) {
|
|
340
|
-
// No proxy configured, just return normal fetch options
|
|
341
|
-
return { ...base, ...getTLSFetchOptions() }
|
|
342
|
-
}
|
|
343
|
-
|
|
344
337
|
// In Node.js/undici, a fresh Agent with no proxy settings bypasses system defaults
|
|
345
338
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
346
339
|
const undiciMod = require('undici') as typeof undici
|
|
347
340
|
const tlsOpts = getTLSFetchOptions()
|
|
348
341
|
|
|
349
|
-
// Use the global dispatcher's options if possible, or fresh default options
|
|
350
|
-
const agentOptions = tlsOpts.dispatcher && 'options' in (tlsOpts.dispatcher as any)
|
|
351
|
-
? (tlsOpts.dispatcher as any).options
|
|
352
|
-
: {}
|
|
353
|
-
|
|
354
342
|
return {
|
|
355
343
|
...base,
|
|
356
|
-
dispatcher: new undiciMod.Agent(
|
|
344
|
+
dispatcher: new undiciMod.Agent(tlsOpts.dispatcher ? (tlsOpts.dispatcher as any).options : {}),
|
|
357
345
|
}
|
|
358
346
|
}
|
|
359
347
|
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Anthropic-to-Anthropic SSE stream labeler.
|
|
3
|
-
*
|
|
4
|
-
* Intercepts an Anthropic Messages API stream and replaces the 'model' field
|
|
5
|
-
* in the 'message_start' event with a custom label.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export function anthropicStreamLabeler(
|
|
9
|
-
upstream: ReadableStream<Uint8Array>,
|
|
10
|
-
label: string,
|
|
11
|
-
): ReadableStream<Uint8Array> {
|
|
12
|
-
const encoder = new TextEncoder()
|
|
13
|
-
const decoder = new TextDecoder()
|
|
14
|
-
let buffer = ''
|
|
15
|
-
|
|
16
|
-
return new ReadableStream({
|
|
17
|
-
async start(controller) {
|
|
18
|
-
const reader = upstream.getReader()
|
|
19
|
-
try {
|
|
20
|
-
while (true) {
|
|
21
|
-
const { done, value } = await reader.read()
|
|
22
|
-
if (done) break
|
|
23
|
-
|
|
24
|
-
buffer += decoder.decode(value, { stream: true })
|
|
25
|
-
const lines = buffer.split('\n')
|
|
26
|
-
buffer = lines.pop() || ''
|
|
27
|
-
|
|
28
|
-
for (const line of lines) {
|
|
29
|
-
const trimmed = line.trim()
|
|
30
|
-
if (!trimmed || !trimmed.startsWith('data: ')) {
|
|
31
|
-
controller.enqueue(encoder.encode(line + '\n'))
|
|
32
|
-
continue
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const jsonStr = trimmed.slice(6)
|
|
36
|
-
try {
|
|
37
|
-
const data = JSON.parse(jsonStr)
|
|
38
|
-
if (data.type === 'message_start' && data.message) {
|
|
39
|
-
data.message.model = label
|
|
40
|
-
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n`))
|
|
41
|
-
} else {
|
|
42
|
-
controller.enqueue(encoder.encode(line + '\n'))
|
|
43
|
-
}
|
|
44
|
-
} catch {
|
|
45
|
-
controller.enqueue(encoder.encode(line + '\n'))
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
} catch (err) {
|
|
50
|
-
controller.error(err)
|
|
51
|
-
} finally {
|
|
52
|
-
controller.close()
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
})
|
|
56
|
-
}
|