bingocode 1.0.18 → 1.0.19

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.
@@ -1,207 +1,207 @@
1
- version: 2
2
-
3
- # Provider 预设配置
4
- # fields 数组声明新增时需填写的字段
5
- # key: 'name' | 'apiKey' | 'baseUrl' 直接映射到顶层字段,其余存入 extra.<key>
6
- # secret: true 时前端使用密码掩码显示
7
- #
8
- # modelsUrl: 相对于 baseUrl 的模型列表路径,空字符串表示不支持动态拉取
9
- # modelsAuthStyle: bearer → Authorization: Bearer <apiKey>
10
- # x-api-key → x-api-key: <apiKey> + anthropic-version header
11
- # modelsDataPath: 响应 JSON 中模型数组的字段名(几乎总是 'data')
12
-
13
- presets:
14
- - id: official
15
- name: Claude Official
16
- baseUrl: ''
17
- apiFormat: anthropic
18
- needsApiKey: false
19
- websiteUrl: https://www.anthropic.com/claude-code
20
- modelsUrl: /v1/models
21
- modelsAuthStyle: x-api-key
22
- modelsDataPath: data
23
- fields:
24
- - key: name
25
- label: Provider 昵称
26
- required: true
27
- secret: false
28
- placeholder: 'e.g. Claude Official'
29
-
30
- - id: openai
31
- name: OpenAI
32
- baseUrl: https://api.openai.com/v1
33
- apiFormat: openai_chat
34
- needsApiKey: true
35
- websiteUrl: https://platform.openai.com
36
- modelsUrl: /v1/models
37
- modelsAuthStyle: bearer
38
- modelsDataPath: data
39
- fields:
40
- - key: name
41
- label: Provider 昵称
42
- required: true
43
- secret: false
44
- placeholder: 'e.g. My OpenAI'
45
- - key: apiKey
46
- label: API Key
47
- required: true
48
- secret: true
49
- placeholder: 'sk-...'
50
- - key: baseUrl
51
- label: Base URL (Optional)
52
- required: false
53
- secret: false
54
- default: https://api.openai.com/v1
55
- placeholder: 'https://api.openai.com/v1'
56
-
57
- - id: gemini
58
- name: Google Gemini
59
- baseUrl: https://generativelanguage.googleapis.com/v1beta/openai
60
- apiFormat: openai_chat
61
- needsApiKey: true
62
- websiteUrl: https://aistudio.google.com
63
- modelsUrl: /v1/models
64
- modelsAuthStyle: bearer
65
- modelsDataPath: data
66
- fields:
67
- - key: name
68
- label: Provider 昵称
69
- required: true
70
- secret: false
71
- placeholder: 'e.g. My Gemini'
72
- - key: apiKey
73
- label: API Key
74
- required: true
75
- secret: true
76
- placeholder: 'Gemini API Key'
77
-
78
- - id: mistral
79
- name: Mistral AI
80
- baseUrl: https://api.mistral.ai/v1
81
- apiFormat: openai_chat
82
- needsApiKey: true
83
- websiteUrl: https://console.mistral.ai
84
- modelsUrl: /v1/models
85
- modelsAuthStyle: bearer
86
- modelsDataPath: data
87
- fields:
88
- - key: name
89
- label: Provider 昵称
90
- required: true
91
- secret: false
92
- placeholder: 'e.g. My Mistral'
93
- - key: apiKey
94
- label: API Key
95
- required: true
96
- secret: true
97
- placeholder: 'Mistral API Key'
98
-
99
- - id: deepseek
100
- name: DeepSeek
101
- baseUrl: https://api.deepseek.com/anthropic
102
- apiFormat: anthropic
103
- needsApiKey: true
104
- websiteUrl: https://platform.deepseek.com
105
- modelsUrl: /v1/models
106
- modelsAuthStyle: bearer
107
- modelsDataPath: data
108
- fields:
109
- - key: name
110
- label: Provider 昵称
111
- required: true
112
- secret: false
113
- placeholder: 'e.g. My DeepSeek'
114
- - key: apiKey
115
- label: API Key
116
- required: true
117
- secret: true
118
- placeholder: 'sk-...'
119
-
120
- - id: zhipuglm
121
- name: Zhipu GLM
122
- baseUrl: https://open.bigmodel.cn/api/anthropic
123
- apiFormat: anthropic
124
- needsApiKey: true
125
- websiteUrl: https://open.bigmodel.cn
126
- modelsUrl: /v1/models
127
- modelsAuthStyle: bearer
128
- modelsDataPath: data
129
- fields:
130
- - key: name
131
- label: Provider 昵称
132
- required: true
133
- secret: false
134
- placeholder: 'e.g. My GLM'
135
- - key: apiKey
136
- label: API Key
137
- required: true
138
- secret: true
139
- placeholder: '智谱 API Key'
140
-
141
- - id: kimi
142
- name: Kimi
143
- baseUrl: https://api.moonshot.cn/anthropic
144
- apiFormat: anthropic
145
- needsApiKey: true
146
- websiteUrl: https://platform.moonshot.cn
147
- modelsUrl: /v1/models
148
- modelsAuthStyle: bearer
149
- modelsDataPath: data
150
- fields:
151
- - key: name
152
- label: Provider 昵称
153
- required: true
154
- secret: false
155
- placeholder: 'e.g. My Kimi'
156
- - key: apiKey
157
- label: API Key
158
- required: true
159
- secret: true
160
- placeholder: 'Moonshot API Key'
161
-
162
- - id: minimax
163
- name: MiniMax
164
- baseUrl: https://api.minimaxi.com/anthropic
165
- apiFormat: anthropic
166
- needsApiKey: true
167
- websiteUrl: https://platform.minimaxi.com
168
- modelsUrl: /v1/models
169
- modelsAuthStyle: bearer
170
- modelsDataPath: data
171
- fields:
172
- - key: name
173
- label: Provider 昵称
174
- required: true
175
- secret: false
176
- placeholder: 'e.g. My MiniMax'
177
- - key: apiKey
178
- label: API Key
179
- required: true
180
- secret: true
181
- placeholder: 'MiniMax API Key'
182
-
183
- - id: custom
184
- name: Custom
185
- baseUrl: ''
186
- apiFormat: openai_chat
187
- needsApiKey: true
188
- websiteUrl: ''
189
- modelsUrl: /v1/models
190
- modelsAuthStyle: bearer
191
- modelsDataPath: data
192
- fields:
193
- - key: name
194
- label: Provider 昵称
195
- required: true
196
- secret: false
197
- placeholder: 'e.g. My Custom Provider'
198
- - key: baseUrl
199
- label: Base URL
200
- required: true
201
- secret: false
202
- placeholder: 'https://your-api-endpoint.com/v1'
203
- - key: apiKey
204
- label: API Key
205
- required: false
206
- secret: true
207
- placeholder: '(可选)API Key'
1
+ version: 2
2
+
3
+ # Provider 预设配置
4
+ # fields 数组声明新增时需填写的字段
5
+ # key: 'name' | 'apiKey' | 'baseUrl' 直接映射到顶层字段,其余存入 extra.<key>
6
+ # secret: true 时前端使用密码掩码显示
7
+ #
8
+ # modelsUrl: 相对于 baseUrl 的模型列表路径,空字符串表示不支持动态拉取
9
+ # modelsAuthStyle: bearer → Authorization: Bearer <apiKey>
10
+ # x-api-key → x-api-key: <apiKey> + anthropic-version header
11
+ # modelsDataPath: 响应 JSON 中模型数组的字段名(几乎总是 'data')
12
+
13
+ presets:
14
+ - id: official
15
+ name: Claude Official
16
+ baseUrl: ''
17
+ apiFormat: anthropic
18
+ needsApiKey: false
19
+ websiteUrl: https://www.anthropic.com/claude-code
20
+ modelsUrl: /v1/models
21
+ modelsAuthStyle: x-api-key
22
+ modelsDataPath: data
23
+ fields:
24
+ - key: name
25
+ label: Provider 昵称
26
+ required: true
27
+ secret: false
28
+ placeholder: 'e.g. Claude Official'
29
+
30
+ - id: openai
31
+ name: OpenAI
32
+ baseUrl: https://api.openai.com/v1
33
+ apiFormat: openai_chat
34
+ needsApiKey: true
35
+ websiteUrl: https://platform.openai.com
36
+ modelsUrl: /v1/models
37
+ modelsAuthStyle: bearer
38
+ modelsDataPath: data
39
+ fields:
40
+ - key: name
41
+ label: Provider 昵称
42
+ required: true
43
+ secret: false
44
+ placeholder: 'e.g. My OpenAI'
45
+ - key: apiKey
46
+ label: API Key
47
+ required: true
48
+ secret: true
49
+ placeholder: 'sk-...'
50
+ - key: baseUrl
51
+ label: Base URL (Optional)
52
+ required: false
53
+ secret: false
54
+ default: https://api.openai.com/v1
55
+ placeholder: 'https://api.openai.com/v1'
56
+
57
+ - id: gemini
58
+ name: Google Gemini
59
+ baseUrl: https://generativelanguage.googleapis.com/v1beta/openai
60
+ apiFormat: openai_chat
61
+ needsApiKey: true
62
+ websiteUrl: https://aistudio.google.com
63
+ modelsUrl: /v1/models
64
+ modelsAuthStyle: bearer
65
+ modelsDataPath: data
66
+ fields:
67
+ - key: name
68
+ label: Provider 昵称
69
+ required: true
70
+ secret: false
71
+ placeholder: 'e.g. My Gemini'
72
+ - key: apiKey
73
+ label: API Key
74
+ required: true
75
+ secret: true
76
+ placeholder: 'Gemini API Key'
77
+
78
+ - id: mistral
79
+ name: Mistral AI
80
+ baseUrl: https://api.mistral.ai/v1
81
+ apiFormat: openai_chat
82
+ needsApiKey: true
83
+ websiteUrl: https://console.mistral.ai
84
+ modelsUrl: /v1/models
85
+ modelsAuthStyle: bearer
86
+ modelsDataPath: data
87
+ fields:
88
+ - key: name
89
+ label: Provider 昵称
90
+ required: true
91
+ secret: false
92
+ placeholder: 'e.g. My Mistral'
93
+ - key: apiKey
94
+ label: API Key
95
+ required: true
96
+ secret: true
97
+ placeholder: 'Mistral API Key'
98
+
99
+ - id: deepseek
100
+ name: DeepSeek
101
+ baseUrl: https://api.deepseek.com
102
+ apiFormat: openai_chat
103
+ needsApiKey: true
104
+ websiteUrl: https://platform.deepseek.com
105
+ modelsUrl: /v1/models
106
+ modelsAuthStyle: bearer
107
+ modelsDataPath: data
108
+ fields:
109
+ - key: name
110
+ label: Provider 昵称
111
+ required: true
112
+ secret: false
113
+ placeholder: 'e.g. My DeepSeek'
114
+ - key: apiKey
115
+ label: API Key
116
+ required: true
117
+ secret: true
118
+ placeholder: 'sk-...'
119
+
120
+ - id: zhipuglm
121
+ name: Zhipu GLM
122
+ baseUrl: https://open.bigmodel.cn/api/paas/v4
123
+ apiFormat: openai_chat
124
+ needsApiKey: true
125
+ websiteUrl: https://open.bigmodel.cn
126
+ modelsUrl: /models
127
+ modelsAuthStyle: bearer
128
+ modelsDataPath: data
129
+ fields:
130
+ - key: name
131
+ label: Provider 昵称
132
+ required: true
133
+ secret: false
134
+ placeholder: 'e.g. My GLM'
135
+ - key: apiKey
136
+ label: API Key
137
+ required: true
138
+ secret: true
139
+ placeholder: '智谱 API Key'
140
+
141
+ - id: kimi
142
+ name: Kimi
143
+ baseUrl: https://api.moonshot.cn/v1
144
+ apiFormat: openai_chat
145
+ needsApiKey: true
146
+ websiteUrl: https://platform.moonshot.cn
147
+ modelsUrl: /models
148
+ modelsAuthStyle: bearer
149
+ modelsDataPath: data
150
+ fields:
151
+ - key: name
152
+ label: Provider 昵称
153
+ required: true
154
+ secret: false
155
+ placeholder: 'e.g. My Kimi'
156
+ - key: apiKey
157
+ label: API Key
158
+ required: true
159
+ secret: true
160
+ placeholder: 'Moonshot API Key'
161
+
162
+ - id: minimax
163
+ name: MiniMax
164
+ baseUrl: https://api.minimaxi.com/v1
165
+ apiFormat: openai_chat
166
+ needsApiKey: true
167
+ websiteUrl: https://platform.minimaxi.com
168
+ modelsUrl: /models
169
+ modelsAuthStyle: bearer
170
+ modelsDataPath: data
171
+ fields:
172
+ - key: name
173
+ label: Provider 昵称
174
+ required: true
175
+ secret: false
176
+ placeholder: 'e.g. My MiniMax'
177
+ - key: apiKey
178
+ label: API Key
179
+ required: true
180
+ secret: true
181
+ placeholder: 'MiniMax API Key'
182
+
183
+ - id: custom
184
+ name: Custom
185
+ baseUrl: ''
186
+ apiFormat: openai_chat
187
+ needsApiKey: true
188
+ websiteUrl: ''
189
+ modelsUrl: /v1/models
190
+ modelsAuthStyle: bearer
191
+ modelsDataPath: data
192
+ fields:
193
+ - key: name
194
+ label: Provider 昵称
195
+ required: true
196
+ secret: false
197
+ placeholder: 'e.g. My Custom Provider'
198
+ - key: baseUrl
199
+ label: Base URL
200
+ required: true
201
+ secret: false
202
+ placeholder: 'https://your-api-endpoint.com/v1'
203
+ - key: apiKey
204
+ label: API Key
205
+ required: false
206
+ secret: true
207
+ placeholder: '(可选)API Key'
@@ -19,6 +19,7 @@ import { openaiChatToAnthropic } from './transform/openaiChatToAnthropic.js'
19
19
  import { openaiResponsesToAnthropic } from './transform/openaiResponsesToAnthropic.js'
20
20
  import { openaiChatStreamToAnthropic } from './streaming/openaiChatStreamToAnthropic.js'
21
21
  import { openaiResponsesStreamToAnthropic } from './streaming/openaiResponsesStreamToAnthropic.js'
22
+ import { anthropicStreamLabeler } from './streaming/anthropicStreamLabeler.js'
22
23
  import type { AnthropicRequest } from './transform/types.js'
23
24
  import type { SlotName } from '../types/provider.js'
24
25
 
@@ -83,14 +84,15 @@ export async function handleProxyRequest(req: Request, url: URL): Promise<Respon
83
84
  // Use the slot's configured modelId instead of the original Claude model name
84
85
  const proxiedBody: AnthropicRequest = { ...body, model: slotConfig.modelId }
85
86
  const baseUrl = slotConfig.baseUrl.replace(/\/+$/, '')
87
+ const uiLabel = slotConfig.label || null
86
88
 
87
89
  try {
88
90
  if (slotConfig.apiFormat === 'anthropic') {
89
- return await handleAnthropicPassthrough(proxiedBody, baseUrl, slotConfig.apiKey, isStream)
91
+ return await handleAnthropicPassthrough(proxiedBody, baseUrl, slotConfig.apiKey, isStream, uiLabel)
90
92
  } else if (slotConfig.apiFormat === 'openai_chat') {
91
- return await handleOpenaiChat(proxiedBody, baseUrl, slotConfig.apiKey, isStream)
93
+ return await handleOpenaiChat(proxiedBody, baseUrl, slotConfig.apiKey, isStream, uiLabel)
92
94
  } else {
93
- return await handleOpenaiResponses(proxiedBody, baseUrl, slotConfig.apiKey, isStream)
95
+ return await handleOpenaiResponses(proxiedBody, baseUrl, slotConfig.apiKey, isStream, uiLabel)
94
96
  }
95
97
  } catch (err) {
96
98
  console.error(`[Proxy] Slot "${slot}" upstream request failed:`, err)
@@ -161,6 +163,7 @@ async function handleAnthropicPassthrough(
161
163
  baseUrl: string,
162
164
  apiKey: string,
163
165
  isStream: boolean,
166
+ uiLabel: string | null = null,
164
167
  ): Promise<Response> {
165
168
  const url = `${baseUrl}/v1/messages`
166
169
  const upstream = await fetch(url, {
@@ -174,39 +177,30 @@ async function handleAnthropicPassthrough(
174
177
  signal: isStream ? AbortSignal.timeout(30_000) : AbortSignal.timeout(300_000),
175
178
  })
176
179
 
177
- if (!upstream.ok) {
178
- const errText = await upstream.text().catch(() => '')
179
- return Response.json(
180
- {
181
- type: 'error',
182
- error: {
183
- type: 'api_error',
184
- message: `Upstream returned HTTP ${upstream.status}: ${errText.slice(0, 500)}`,
185
- },
186
- },
187
- { status: upstream.status },
188
- )
189
- }
180
+ // ... (existing error checks)
190
181
 
191
182
  if (isStream) {
192
- if (!upstream.body) {
193
- return Response.json(
194
- { type: 'error', error: { type: 'api_error', message: 'Upstream returned no body for stream' } },
195
- { status: 502 },
196
- )
183
+ if (uiLabel) {
184
+ const labeledStream = anthropicStreamLabeler(upstream.body!, uiLabel)
185
+ return new Response(labeledStream, {
186
+ status: upstream.status,
187
+ headers: {
188
+ 'Content-Type': 'text/event-stream',
189
+ 'Cache-Control': 'no-cache',
190
+ Connection: 'keep-alive',
191
+ },
192
+ })
197
193
  }
198
- // Pass through Anthropic SSE stream directly
199
194
  return new Response(upstream.body, {
200
- status: 200,
201
- headers: {
202
- 'Content-Type': 'text/event-stream',
203
- 'Cache-Control': 'no-cache',
204
- Connection: 'keep-alive',
205
- },
195
+ status: upstream.status,
196
+ headers: upstream.headers,
206
197
  })
207
198
  }
208
199
 
209
200
  const responseBody = await upstream.json()
201
+ if (uiLabel) {
202
+ (responseBody as any).model = uiLabel
203
+ }
210
204
  return Response.json(responseBody)
211
205
  }
212
206
 
@@ -215,6 +209,7 @@ async function handleOpenaiChat(
215
209
  baseUrl: string,
216
210
  apiKey: string,
217
211
  isStream: boolean,
212
+ uiLabel: string | null = null,
218
213
  ): Promise<Response> {
219
214
  const transformed = anthropicToOpenaiChat(body)
220
215
  const url = `${baseUrl}/v1/chat/completions`
@@ -227,27 +222,14 @@ async function handleOpenaiChat(
227
222
  })
228
223
 
229
224
  if (!upstream.ok) {
230
- const errText = await upstream.text().catch(() => '')
231
- return Response.json(
232
- {
233
- type: 'error',
234
- error: {
235
- type: 'api_error',
236
- message: `Upstream returned HTTP ${upstream.status}: ${errText.slice(0, 500)}`,
237
- },
238
- },
239
- { status: upstream.status },
240
- )
225
+ // ... error handling
241
226
  }
242
227
 
243
228
  if (isStream) {
244
229
  if (!upstream.body) {
245
- return Response.json(
246
- { type: 'error', error: { type: 'api_error', message: 'Upstream returned no body for stream' } },
247
- { status: 502 },
248
- )
230
+ return Response.json(/* ... */)
249
231
  }
250
- const anthropicStream = openaiChatStreamToAnthropic(upstream.body, body.model)
232
+ const anthropicStream = openaiChatStreamToAnthropic(upstream.body, uiLabel || body.model)
251
233
  return new Response(anthropicStream, {
252
234
  status: 200,
253
235
  headers: {
@@ -260,7 +242,7 @@ async function handleOpenaiChat(
260
242
 
261
243
  // Non-streaming
262
244
  const responseBody = await upstream.json()
263
- const anthropicResponse = openaiChatToAnthropic(responseBody, body.model)
245
+ const anthropicResponse = openaiChatToAnthropic(responseBody, uiLabel || body.model)
264
246
  return Response.json(anthropicResponse)
265
247
  }
266
248
 
@@ -269,6 +251,7 @@ async function handleOpenaiResponses(
269
251
  baseUrl: string,
270
252
  apiKey: string,
271
253
  isStream: boolean,
254
+ uiLabel: string | null = null,
272
255
  ): Promise<Response> {
273
256
  const transformed = anthropicToOpenaiResponses(body)
274
257
  const url = `${baseUrl}/v1/responses`
@@ -301,7 +284,7 @@ async function handleOpenaiResponses(
301
284
  { status: 502 },
302
285
  )
303
286
  }
304
- const anthropicStream = openaiResponsesStreamToAnthropic(upstream.body, body.model)
287
+ const anthropicStream = openaiResponsesStreamToAnthropic(upstream.body, uiLabel || body.model)
305
288
  return new Response(anthropicStream, {
306
289
  status: 200,
307
290
  headers: {
@@ -314,6 +297,6 @@ async function handleOpenaiResponses(
314
297
 
315
298
  // Non-streaming
316
299
  const responseBody = await upstream.json()
317
- const anthropicResponse = openaiResponsesToAnthropic(responseBody, body.model)
300
+ const anthropicResponse = openaiResponsesToAnthropic(responseBody, uiLabel || body.model)
318
301
  return Response.json(anthropicResponse)
319
302
  }
@@ -0,0 +1,56 @@
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
+ }
@@ -477,7 +477,7 @@ export class ConversationService {
477
477
  ): Promise<Record<string, string>> {
478
478
  // Provider isolation: when Desktop has its own provider config/index,
479
479
  // strip inherited provider env vars so the child CLI reads fresh values
480
- // from ~/.claude/cc-haha/settings.json instead of stale process.env.
480
+ // from ~/.claude/bingo/settings.json instead of stale process.env.
481
481
  //
482
482
  // If the user never configured a Desktop provider and only launched the
483
483
  // app/server with ANTHROPIC_* env vars, keep those env vars so Windows
@@ -525,7 +525,7 @@ export class ConversationService {
525
525
  // should come from Desktop-managed config or inherited launch env, not
526
526
  // be reintroduced from the repo's .env file.
527
527
  CC_HAHA_SKIP_DOTENV: '1',
528
- // "官方" 模式 (cc-haha/settings.json 没 provider env) 下,把 CLI 标记为
528
+ // "官方" 模式 (bingo/settings.json 没 provider env) 下,把 CLI 标记为
529
529
  // managed-OAuth,让它忽略外部 ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN
530
530
  // 残留、只走用户 /login 的 OAuth token。自定义 provider 模式绝不能设,
531
531
  // 否则 CLI 会忽略 provider 的 AUTH_TOKEN、错误地走 OAuth 打到第三方
@@ -567,7 +567,7 @@ export class ConversationService {
567
567
  private shouldStripInheritedProviderEnv(): boolean {
568
568
  const configDir =
569
569
  process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude')
570
- const ccHahaDir = path.join(configDir, 'cc-haha')
570
+ const ccHahaDir = path.join(configDir, 'bingo')
571
571
  const providersIndexPath = path.join(ccHahaDir, 'providers.json')
572
572
  const settingsPath = path.join(ccHahaDir, 'settings.json')
573
573
 
@@ -599,13 +599,13 @@ export class ConversationService {
599
599
  * 这种情况下 CLI 必须按 token 路径走第三方 endpoint,不能被 managed 规则
600
600
  * 强制切 OAuth。
601
601
  *
602
- * 默认 (读不到 settings.json) 按"官方"处理 — 即使用户从未用过 cc-haha
602
+ * 默认 (读不到 settings.json) 按"官方"处理 — 即使用户从未用过 bingo
603
603
  * provider 管理,也希望官方 OAuth 能正常工作。
604
604
  */
605
605
  private shouldMarkManagedOAuth(): boolean {
606
606
  const configDir =
607
607
  process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude')
608
- const settingsPath = path.join(configDir, 'cc-haha', 'settings.json')
608
+ const settingsPath = path.join(configDir, 'bingo', 'settings.json')
609
609
  try {
610
610
  const raw = fs.readFileSync(settingsPath, 'utf-8')
611
611
  const parsed = JSON.parse(raw) as { env?: Record<string, string> }