@lynn123411/dsh-a6api 1.2.0 → 1.4.0
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 +10 -3
- package/lib/client.js +2154 -284
- package/lib/client.js.map +4 -4
- package/lib/index.js +1371 -641
- package/lib/index.js.map +4 -4
- package/lib/types/client/components/A6ApiSidebarCard.d.ts +18 -0
- package/lib/types/client/components/ModelCatalogPanel.d.ts +11 -0
- package/lib/types/client/components/PricePill.d.ts +12 -0
- package/lib/types/client/store.d.ts +92 -2
- package/lib/types/index.d.ts +3 -3
- package/lib/types/server/a6api-client.d.ts +32 -6
- package/lib/types/server/catalog.d.ts +45 -4
- package/lib/types/server/probe.d.ts +2 -0
- package/lib/types/server/sync.d.ts +28 -9
- package/lib/types/types.d.ts +71 -5
- package/package.json +4 -1
package/lib/index.js
CHANGED
|
@@ -1,457 +1,162 @@
|
|
|
1
1
|
// src/server/catalog.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
contextWindow: 1048576,
|
|
49
|
-
maxTokens: 65536,
|
|
50
|
-
modalities: ["text", "image"],
|
|
51
|
-
reasoningEfforts: { off: "", low: "low", medium: "medium", high: "high" },
|
|
52
|
-
officialPriceMicros: { input: 3e6, output: 15e6, cacheRead: 3e5, cacheWrite: 375e4 }
|
|
53
|
-
},
|
|
54
|
-
"gpt-5.4": {
|
|
55
|
-
id: "gpt-5.4",
|
|
56
|
-
name: "GPT-5.4",
|
|
57
|
-
brand: "OpenAI",
|
|
58
|
-
contextWindow: 524288,
|
|
59
|
-
maxTokens: 32768,
|
|
60
|
-
modalities: ["text", "image"],
|
|
61
|
-
reasoningEfforts: { off: "", low: "low", medium: "medium", high: "high" },
|
|
62
|
-
officialPriceMicros: { input: 25e5, output: 1e7, cacheRead: 25e4, cacheWrite: 3e6 }
|
|
63
|
-
},
|
|
64
|
-
"gpt-5.4-mini": {
|
|
65
|
-
id: "gpt-5.4-mini",
|
|
66
|
-
name: "GPT-5.4 Mini",
|
|
67
|
-
brand: "OpenAI",
|
|
68
|
-
contextWindow: 262144,
|
|
69
|
-
maxTokens: 32768,
|
|
70
|
-
modalities: ["text", "image"],
|
|
71
|
-
reasoningEfforts: { off: "", low: "low", medium: "medium", high: "high" },
|
|
72
|
-
officialPriceMicros: { input: 15e4, output: 6e5, cacheRead: 75e3, cacheWrite: 15e4 }
|
|
73
|
-
},
|
|
74
|
-
"gpt-5.3-codex": {
|
|
75
|
-
id: "gpt-5.3-codex",
|
|
76
|
-
name: "GPT-5.3 Codex",
|
|
77
|
-
brand: "OpenAI",
|
|
78
|
-
contextWindow: 262144,
|
|
79
|
-
maxTokens: 32768,
|
|
80
|
-
modalities: ["text"],
|
|
81
|
-
officialPriceMicros: { input: 2e6, output: 8e6, cacheRead: 2e5, cacheWrite: 2e6 }
|
|
82
|
-
},
|
|
83
|
-
"gpt-5.3-codex-spark": {
|
|
84
|
-
id: "gpt-5.3-codex-spark",
|
|
85
|
-
name: "GPT-5.3 Codex Spark",
|
|
86
|
-
brand: "OpenAI",
|
|
87
|
-
contextWindow: 262144,
|
|
88
|
-
maxTokens: 16384,
|
|
89
|
-
modalities: ["text"],
|
|
90
|
-
officialPriceMicros: { input: 1e6, output: 4e6, cacheRead: 1e5, cacheWrite: 1e6 }
|
|
91
|
-
},
|
|
92
|
-
"gpt-5.2": {
|
|
93
|
-
id: "gpt-5.2",
|
|
94
|
-
name: "GPT-5.2",
|
|
95
|
-
brand: "OpenAI",
|
|
96
|
-
contextWindow: 262144,
|
|
97
|
-
maxTokens: 32768,
|
|
98
|
-
modalities: ["text", "image"],
|
|
99
|
-
officialPriceMicros: { input: 2e6, output: 8e6, cacheRead: 2e5, cacheWrite: 2e6 }
|
|
100
|
-
},
|
|
101
|
-
"gpt-4o": {
|
|
102
|
-
id: "gpt-4o",
|
|
103
|
-
name: "GPT-4o",
|
|
104
|
-
brand: "OpenAI",
|
|
105
|
-
contextWindow: 128e3,
|
|
106
|
-
maxTokens: 16384,
|
|
107
|
-
modalities: ["text", "image"],
|
|
108
|
-
officialPriceMicros: { input: 25e5, output: 1e7, cacheRead: 125e4, cacheWrite: 25e5 }
|
|
109
|
-
},
|
|
110
|
-
"gpt-4o-mini": {
|
|
111
|
-
id: "gpt-4o-mini",
|
|
112
|
-
name: "GPT-4o Mini",
|
|
113
|
-
brand: "OpenAI",
|
|
114
|
-
contextWindow: 128e3,
|
|
115
|
-
maxTokens: 16384,
|
|
116
|
-
modalities: ["text", "image"],
|
|
117
|
-
officialPriceMicros: { input: 15e4, output: 6e5, cacheRead: 75e3, cacheWrite: 15e4 }
|
|
118
|
-
},
|
|
119
|
-
// --- Anthropic Claude Series ---
|
|
120
|
-
"claude-opus-5": {
|
|
121
|
-
id: "claude-opus-5",
|
|
122
|
-
name: "Claude Opus 5",
|
|
123
|
-
brand: "Anthropic",
|
|
124
|
-
contextWindow: 2e5,
|
|
125
|
-
maxTokens: 32768,
|
|
126
|
-
modalities: ["text", "image"],
|
|
127
|
-
thinkingFormat: "anthropic",
|
|
128
|
-
officialPriceMicros: { input: 15e6, output: 75e6, cacheRead: 15e5, cacheWrite: 1875e4 }
|
|
129
|
-
},
|
|
130
|
-
"claude-sonnet-5": {
|
|
131
|
-
id: "claude-sonnet-5",
|
|
132
|
-
name: "Claude Sonnet 5",
|
|
133
|
-
brand: "Anthropic",
|
|
134
|
-
contextWindow: 2e5,
|
|
135
|
-
maxTokens: 32768,
|
|
136
|
-
modalities: ["text", "image"],
|
|
137
|
-
thinkingFormat: "anthropic",
|
|
138
|
-
officialPriceMicros: { input: 3e6, output: 15e6, cacheRead: 3e5, cacheWrite: 375e4 }
|
|
139
|
-
},
|
|
140
|
-
"claude-fable-5": {
|
|
141
|
-
id: "claude-fable-5",
|
|
142
|
-
name: "Claude Fable 5",
|
|
143
|
-
brand: "Anthropic",
|
|
144
|
-
contextWindow: 2e5,
|
|
145
|
-
maxTokens: 32768,
|
|
146
|
-
modalities: ["text", "image"],
|
|
147
|
-
officialPriceMicros: { input: 3e6, output: 15e6, cacheRead: 3e5, cacheWrite: 375e4 }
|
|
148
|
-
},
|
|
149
|
-
"claude-opus-4-8": {
|
|
150
|
-
id: "claude-opus-4-8",
|
|
151
|
-
name: "Claude Opus 4.8",
|
|
152
|
-
brand: "Anthropic",
|
|
153
|
-
contextWindow: 2e5,
|
|
154
|
-
maxTokens: 16384,
|
|
155
|
-
modalities: ["text", "image"],
|
|
156
|
-
officialPriceMicros: { input: 15e6, output: 75e6, cacheRead: 15e5, cacheWrite: 1875e4 }
|
|
157
|
-
},
|
|
158
|
-
"claude-opus-4-7": {
|
|
159
|
-
id: "claude-opus-4-7",
|
|
160
|
-
name: "Claude Opus 4.7",
|
|
161
|
-
brand: "Anthropic",
|
|
162
|
-
contextWindow: 2e5,
|
|
163
|
-
maxTokens: 16384,
|
|
164
|
-
modalities: ["text", "image"],
|
|
165
|
-
officialPriceMicros: { input: 15e6, output: 75e6, cacheRead: 15e5, cacheWrite: 1875e4 }
|
|
166
|
-
},
|
|
167
|
-
"claude-opus-4-6": {
|
|
168
|
-
id: "claude-opus-4-6",
|
|
169
|
-
name: "Claude Opus 4.6",
|
|
170
|
-
brand: "Anthropic",
|
|
171
|
-
contextWindow: 2e5,
|
|
172
|
-
maxTokens: 16384,
|
|
173
|
-
modalities: ["text", "image"],
|
|
174
|
-
officialPriceMicros: { input: 15e6, output: 75e6, cacheRead: 15e5, cacheWrite: 1875e4 }
|
|
175
|
-
},
|
|
176
|
-
"claude-sonnet-4-6": {
|
|
177
|
-
id: "claude-sonnet-4-6",
|
|
178
|
-
name: "Claude Sonnet 4.6",
|
|
179
|
-
brand: "Anthropic",
|
|
180
|
-
contextWindow: 2e5,
|
|
181
|
-
maxTokens: 16384,
|
|
182
|
-
modalities: ["text", "image"],
|
|
183
|
-
officialPriceMicros: { input: 3e6, output: 15e6, cacheRead: 3e5, cacheWrite: 375e4 }
|
|
184
|
-
},
|
|
185
|
-
"claude-haiku-4-5": {
|
|
186
|
-
id: "claude-haiku-4-5",
|
|
187
|
-
name: "Claude Haiku 4.5",
|
|
188
|
-
brand: "Anthropic",
|
|
189
|
-
contextWindow: 2e5,
|
|
190
|
-
maxTokens: 8192,
|
|
191
|
-
modalities: ["text", "image"],
|
|
192
|
-
officialPriceMicros: { input: 25e4, output: 125e4, cacheRead: 25e3, cacheWrite: 312500 }
|
|
193
|
-
},
|
|
194
|
-
// --- DeepSeek Series ---
|
|
195
|
-
"deepseek-v4-pro": {
|
|
196
|
-
id: "deepseek-v4-pro",
|
|
197
|
-
name: "DeepSeek V4 Pro",
|
|
198
|
-
brand: "DeepSeek",
|
|
199
|
-
contextWindow: 131072,
|
|
200
|
-
maxTokens: 32768,
|
|
201
|
-
modalities: ["text"],
|
|
202
|
-
thinkingFormat: "deepseek",
|
|
203
|
-
officialPriceMicros: { input: 55e4, output: 219e4, cacheRead: 14e4, cacheWrite: 55e4 }
|
|
204
|
-
},
|
|
205
|
-
"deepseek-v4-flash": {
|
|
206
|
-
id: "deepseek-v4-flash",
|
|
207
|
-
name: "DeepSeek V4 Flash",
|
|
208
|
-
brand: "DeepSeek",
|
|
209
|
-
contextWindow: 131072,
|
|
210
|
-
maxTokens: 16384,
|
|
211
|
-
modalities: ["text"],
|
|
212
|
-
officialPriceMicros: { input: 14e4, output: 28e4, cacheRead: 14e3, cacheWrite: 14e4 }
|
|
213
|
-
},
|
|
214
|
-
"DeepSeek-V4-Flash-0731": {
|
|
215
|
-
id: "DeepSeek-V4-Flash-0731",
|
|
216
|
-
name: "DeepSeek V4 Flash (0731)",
|
|
217
|
-
brand: "DeepSeek",
|
|
218
|
-
contextWindow: 131072,
|
|
219
|
-
maxTokens: 16384,
|
|
220
|
-
modalities: ["text"],
|
|
221
|
-
officialPriceMicros: { input: 14e4, output: 28e4, cacheRead: 14e3, cacheWrite: 14e4 }
|
|
222
|
-
},
|
|
223
|
-
"deepseek-v4-pro-0813": {
|
|
224
|
-
id: "deepseek-v4-pro-0813",
|
|
225
|
-
name: "DeepSeek V4 Pro (0813)",
|
|
226
|
-
brand: "DeepSeek",
|
|
227
|
-
contextWindow: 131072,
|
|
228
|
-
maxTokens: 32768,
|
|
229
|
-
modalities: ["text"],
|
|
230
|
-
thinkingFormat: "deepseek",
|
|
231
|
-
officialPriceMicros: { input: 55e4, output: 219e4, cacheRead: 14e4, cacheWrite: 55e4 }
|
|
232
|
-
},
|
|
233
|
-
"deepseek-v4-flash-vision-exp": {
|
|
234
|
-
id: "deepseek-v4-flash-vision-exp",
|
|
235
|
-
name: "DeepSeek V4 Flash Vision Exp",
|
|
236
|
-
brand: "DeepSeek",
|
|
237
|
-
contextWindow: 131072,
|
|
238
|
-
maxTokens: 16384,
|
|
239
|
-
modalities: ["text", "image"],
|
|
240
|
-
officialPriceMicros: { input: 14e4, output: 28e4, cacheRead: 14e3, cacheWrite: 14e4 }
|
|
241
|
-
},
|
|
242
|
-
"deepseek-chat": {
|
|
243
|
-
id: "deepseek-chat",
|
|
244
|
-
name: "DeepSeek Chat",
|
|
245
|
-
brand: "DeepSeek",
|
|
246
|
-
contextWindow: 65536,
|
|
247
|
-
maxTokens: 8192,
|
|
248
|
-
modalities: ["text"],
|
|
249
|
-
officialPriceMicros: { input: 14e4, output: 28e4, cacheRead: 14e3, cacheWrite: 14e4 }
|
|
250
|
-
},
|
|
251
|
-
"deepseek-reasoner": {
|
|
252
|
-
id: "deepseek-reasoner",
|
|
253
|
-
name: "DeepSeek Reasoner",
|
|
254
|
-
brand: "DeepSeek",
|
|
255
|
-
contextWindow: 65536,
|
|
256
|
-
maxTokens: 16384,
|
|
257
|
-
modalities: ["text"],
|
|
258
|
-
thinkingFormat: "deepseek",
|
|
259
|
-
officialPriceMicros: { input: 55e4, output: 219e4, cacheRead: 14e4, cacheWrite: 55e4 }
|
|
260
|
-
},
|
|
261
|
-
// --- Google Gemini Series ---
|
|
262
|
-
"gemini-3.7-flash": {
|
|
263
|
-
id: "gemini-3.7-flash",
|
|
264
|
-
name: "Gemini 3.7 Flash",
|
|
265
|
-
brand: "Google",
|
|
266
|
-
contextWindow: 1048576,
|
|
267
|
-
maxTokens: 65536,
|
|
268
|
-
modalities: ["text", "image"],
|
|
269
|
-
reasoningEfforts: { off: "", low: "low", medium: "medium", high: "high" },
|
|
270
|
-
officialPriceMicros: { input: 1e5, output: 4e5, cacheRead: 25e3, cacheWrite: 1e5 }
|
|
271
|
-
},
|
|
272
|
-
"gemini-3.6-flash": {
|
|
273
|
-
id: "gemini-3.6-flash",
|
|
274
|
-
name: "Gemini 3.6 Flash",
|
|
275
|
-
brand: "Google",
|
|
276
|
-
contextWindow: 1048576,
|
|
277
|
-
maxTokens: 65536,
|
|
278
|
-
modalities: ["text", "image"],
|
|
279
|
-
officialPriceMicros: { input: 1e5, output: 4e5, cacheRead: 25e3, cacheWrite: 1e5 }
|
|
280
|
-
},
|
|
281
|
-
"gemini-3.5-flash": {
|
|
282
|
-
id: "gemini-3.5-flash",
|
|
283
|
-
name: "Gemini 3.5 Flash",
|
|
284
|
-
brand: "Google",
|
|
285
|
-
contextWindow: 1048576,
|
|
286
|
-
maxTokens: 65536,
|
|
287
|
-
modalities: ["text", "image"],
|
|
288
|
-
officialPriceMicros: { input: 75e3, output: 3e5, cacheRead: 18750, cacheWrite: 75e3 }
|
|
289
|
-
},
|
|
290
|
-
"gemini-3.5-flash-high": {
|
|
291
|
-
id: "gemini-3.5-flash-high",
|
|
292
|
-
name: "Gemini 3.5 Flash High",
|
|
293
|
-
brand: "Google",
|
|
294
|
-
contextWindow: 1048576,
|
|
295
|
-
maxTokens: 65536,
|
|
296
|
-
modalities: ["text", "image"],
|
|
297
|
-
reasoningEfforts: { high: "high" },
|
|
298
|
-
officialPriceMicros: { input: 75e3, output: 3e5, cacheRead: 18750, cacheWrite: 75e3 }
|
|
299
|
-
},
|
|
300
|
-
"gemini-3.1-pro-preview": {
|
|
301
|
-
id: "gemini-3.1-pro-preview",
|
|
302
|
-
name: "Gemini 3.1 Pro Preview",
|
|
303
|
-
brand: "Google",
|
|
304
|
-
contextWindow: 2097152,
|
|
305
|
-
maxTokens: 65536,
|
|
306
|
-
modalities: ["text", "image"],
|
|
307
|
-
officialPriceMicros: { input: 125e4, output: 5e6, cacheRead: 312500, cacheWrite: 125e4 }
|
|
308
|
-
},
|
|
309
|
-
"gemini-3.1-flash-lite-preview": {
|
|
310
|
-
id: "gemini-3.1-flash-lite-preview",
|
|
311
|
-
name: "Gemini 3.1 Flash Lite Preview",
|
|
312
|
-
brand: "Google",
|
|
313
|
-
contextWindow: 1048576,
|
|
314
|
-
maxTokens: 32768,
|
|
315
|
-
modalities: ["text", "image"],
|
|
316
|
-
officialPriceMicros: { input: 75e3, output: 3e5, cacheRead: 18750, cacheWrite: 75e3 }
|
|
317
|
-
},
|
|
318
|
-
"gemini-2.5-pro": {
|
|
319
|
-
id: "gemini-2.5-pro",
|
|
320
|
-
name: "Gemini 2.5 Pro",
|
|
321
|
-
brand: "Google",
|
|
322
|
-
contextWindow: 2097152,
|
|
323
|
-
maxTokens: 65536,
|
|
324
|
-
modalities: ["text", "image"],
|
|
325
|
-
officialPriceMicros: { input: 125e4, output: 5e6, cacheRead: 312500, cacheWrite: 125e4 }
|
|
326
|
-
},
|
|
327
|
-
"gemini-2.5-flash": {
|
|
328
|
-
id: "gemini-2.5-flash",
|
|
329
|
-
name: "Gemini 2.5 Flash",
|
|
330
|
-
brand: "Google",
|
|
331
|
-
contextWindow: 1048576,
|
|
332
|
-
maxTokens: 32768,
|
|
333
|
-
modalities: ["text", "image"],
|
|
334
|
-
officialPriceMicros: { input: 75e3, output: 3e5, cacheRead: 18750, cacheWrite: 75e3 }
|
|
335
|
-
},
|
|
336
|
-
// --- xAI Grok Series ---
|
|
337
|
-
"grok-4.6": {
|
|
338
|
-
id: "grok-4.6",
|
|
339
|
-
name: "Grok 4.6",
|
|
340
|
-
brand: "xAI",
|
|
341
|
-
contextWindow: 262144,
|
|
342
|
-
maxTokens: 32768,
|
|
343
|
-
modalities: ["text", "image"],
|
|
344
|
-
reasoningEfforts: { off: "", high: "high" },
|
|
345
|
-
officialPriceMicros: { input: 2e6, output: 1e7, cacheRead: 2e5, cacheWrite: 25e5 }
|
|
346
|
-
},
|
|
347
|
-
"grok-4.5": {
|
|
348
|
-
id: "grok-4.5",
|
|
349
|
-
name: "Grok 4.5",
|
|
350
|
-
brand: "xAI",
|
|
351
|
-
contextWindow: 131072,
|
|
352
|
-
maxTokens: 16384,
|
|
353
|
-
modalities: ["text", "image"],
|
|
354
|
-
officialPriceMicros: { input: 2e6, output: 1e7, cacheRead: 2e5, cacheWrite: 25e5 }
|
|
355
|
-
},
|
|
356
|
-
"grok-4.3": {
|
|
357
|
-
id: "grok-4.3",
|
|
358
|
-
name: "Grok 4.3",
|
|
359
|
-
brand: "xAI",
|
|
360
|
-
contextWindow: 131072,
|
|
361
|
-
maxTokens: 16384,
|
|
362
|
-
modalities: ["text"],
|
|
363
|
-
officialPriceMicros: { input: 2e6, output: 1e7, cacheRead: 2e5, cacheWrite: 25e5 }
|
|
364
|
-
},
|
|
365
|
-
// --- Zhipu GLM Series ---
|
|
366
|
-
"glm-5.3": {
|
|
367
|
-
id: "glm-5.3",
|
|
368
|
-
name: "GLM 5.3",
|
|
369
|
-
brand: "Zhipu",
|
|
370
|
-
contextWindow: 131072,
|
|
371
|
-
maxTokens: 32768,
|
|
372
|
-
modalities: ["text", "image"],
|
|
373
|
-
thinkingFormat: "deepseek",
|
|
374
|
-
officialPriceMicros: { input: 1e6, output: 4e6, cacheRead: 2e5, cacheWrite: 1e6 }
|
|
375
|
-
},
|
|
376
|
-
"glm-5.3-flash": {
|
|
377
|
-
id: "glm-5.3-flash",
|
|
378
|
-
name: "GLM 5.3 Flash",
|
|
379
|
-
brand: "Zhipu",
|
|
380
|
-
contextWindow: 131072,
|
|
381
|
-
maxTokens: 16384,
|
|
382
|
-
modalities: ["text"],
|
|
383
|
-
officialPriceMicros: { input: 1e5, output: 2e5, cacheRead: 1e4, cacheWrite: 1e5 }
|
|
384
|
-
},
|
|
385
|
-
"glm-5.2": {
|
|
386
|
-
id: "glm-5.2",
|
|
387
|
-
name: "GLM 5.2",
|
|
388
|
-
brand: "Zhipu",
|
|
389
|
-
contextWindow: 131072,
|
|
390
|
-
maxTokens: 16384,
|
|
391
|
-
modalities: ["text"],
|
|
392
|
-
officialPriceMicros: { input: 1e6, output: 4e6, cacheRead: 2e5, cacheWrite: 1e6 }
|
|
393
|
-
},
|
|
394
|
-
// --- Moonshot Kimi Series ---
|
|
395
|
-
"kimi-k3": {
|
|
396
|
-
id: "kimi-k3",
|
|
397
|
-
name: "Kimi K3",
|
|
398
|
-
brand: "Moonshot",
|
|
399
|
-
contextWindow: 262144,
|
|
400
|
-
maxTokens: 32768,
|
|
401
|
-
modalities: ["text", "image"],
|
|
402
|
-
thinkingFormat: "deepseek",
|
|
403
|
-
officialPriceMicros: { input: 15e5, output: 6e6, cacheRead: 3e5, cacheWrite: 15e5 }
|
|
404
|
-
},
|
|
405
|
-
"kimi-k2.7-code": {
|
|
406
|
-
id: "kimi-k2.7-code",
|
|
407
|
-
name: "Kimi K2.7 Code",
|
|
408
|
-
brand: "Moonshot",
|
|
409
|
-
contextWindow: 262144,
|
|
410
|
-
maxTokens: 32768,
|
|
411
|
-
modalities: ["text"],
|
|
412
|
-
officialPriceMicros: { input: 1e6, output: 4e6, cacheRead: 2e5, cacheWrite: 1e6 }
|
|
413
|
-
},
|
|
414
|
-
// --- Alibaba Qwen Series ---
|
|
415
|
-
"qwen3.8-max": {
|
|
416
|
-
id: "qwen3.8-max",
|
|
417
|
-
name: "Qwen 3.8 Max",
|
|
418
|
-
brand: "Alibaba",
|
|
419
|
-
contextWindow: 1048576,
|
|
420
|
-
maxTokens: 32768,
|
|
421
|
-
modalities: ["text", "image"],
|
|
422
|
-
thinkingFormat: "deepseek",
|
|
423
|
-
officialPriceMicros: { input: 2e6, output: 8e6, cacheRead: 4e5, cacheWrite: 2e6 }
|
|
424
|
-
},
|
|
425
|
-
"qwen3.8-flash": {
|
|
426
|
-
id: "qwen3.8-flash",
|
|
427
|
-
name: "Qwen 3.8 Flash",
|
|
428
|
-
brand: "Alibaba",
|
|
429
|
-
contextWindow: 1048576,
|
|
430
|
-
maxTokens: 16384,
|
|
431
|
-
modalities: ["text"],
|
|
432
|
-
officialPriceMicros: { input: 1e5, output: 3e5, cacheRead: 2e4, cacheWrite: 1e5 }
|
|
433
|
-
},
|
|
434
|
-
"qwen3.7-max": {
|
|
435
|
-
id: "qwen3.7-max",
|
|
436
|
-
name: "Qwen 3.7 Max",
|
|
437
|
-
brand: "Alibaba",
|
|
438
|
-
contextWindow: 1048576,
|
|
439
|
-
maxTokens: 32768,
|
|
440
|
-
modalities: ["text", "image"],
|
|
441
|
-
thinkingFormat: "deepseek",
|
|
442
|
-
officialPriceMicros: { input: 2e6, output: 8e6, cacheRead: 4e5, cacheWrite: 2e6 }
|
|
443
|
-
},
|
|
444
|
-
// --- MiniMax Series ---
|
|
445
|
-
"minimax-m3": {
|
|
446
|
-
id: "minimax-m3",
|
|
447
|
-
name: "MiniMax M3",
|
|
448
|
-
brand: "MiniMax",
|
|
449
|
-
contextWindow: 1048576,
|
|
450
|
-
maxTokens: 32768,
|
|
451
|
-
modalities: ["text"],
|
|
452
|
-
officialPriceMicros: { input: 1e6, output: 4e6, cacheRead: 2e5, cacheWrite: 1e6 }
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import fsp from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
// node_modules/.pnpm/@deepseek-ai+dsh-home-paths@0.1.2-alpha.2_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-in_201e0f99eb7c6b9ed77157bb5f14e24e/node_modules/@deepseek-ai/dsh-home-paths/lib/index.js
|
|
7
|
+
import { opendir, realpath } from "node:fs/promises";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
10
|
+
var DSH_HOME_DIR_NAME = ".dsh";
|
|
11
|
+
var DEFAULT_DSH_HOME_DISPLAY = `~/${DSH_HOME_DIR_NAME}`;
|
|
12
|
+
var DSH_HOME_ENV = "DSH_HOME";
|
|
13
|
+
function defaultDshHome() {
|
|
14
|
+
return join(homedir(), DSH_HOME_DIR_NAME);
|
|
15
|
+
}
|
|
16
|
+
function expandHomePath(path3) {
|
|
17
|
+
if (path3 === "~") return homedir();
|
|
18
|
+
if (path3.startsWith("~/") || path3.startsWith("~\\")) return join(homedir(), path3.slice(2));
|
|
19
|
+
return path3;
|
|
20
|
+
}
|
|
21
|
+
function resolveDshHome(configured, env = process.env) {
|
|
22
|
+
const fromEnv = env[DSH_HOME_ENV];
|
|
23
|
+
return resolve(expandHomePath(configured ?? (fromEnv !== void 0 && fromEnv.trim().length > 0 ? fromEnv : defaultDshHome())));
|
|
24
|
+
}
|
|
25
|
+
function dshHomePath(...segments) {
|
|
26
|
+
return join(resolveDshHome(), ...segments);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/server/catalog.ts
|
|
30
|
+
var CATALOG_VERSION = 1;
|
|
31
|
+
function catalogFile() {
|
|
32
|
+
return dshHomePath("dsh-a6api", "catalog.json");
|
|
33
|
+
}
|
|
34
|
+
function legacyCatalogFile() {
|
|
35
|
+
return dshHomePath("dsh-a6api-catalog.json");
|
|
36
|
+
}
|
|
37
|
+
function ensureRelocated() {
|
|
38
|
+
const target = catalogFile();
|
|
39
|
+
const legacy = legacyCatalogFile();
|
|
40
|
+
try {
|
|
41
|
+
fs.accessSync(target);
|
|
42
|
+
try {
|
|
43
|
+
fs.unlinkSync(legacy);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
} catch {
|
|
453
48
|
}
|
|
49
|
+
try {
|
|
50
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
51
|
+
fs.renameSync(legacy, target);
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
var catalogCache = null;
|
|
56
|
+
function ensureLoaded() {
|
|
57
|
+
if (catalogCache) return catalogCache;
|
|
58
|
+
ensureRelocated();
|
|
59
|
+
const sources = [catalogFile(), legacyCatalogFile()];
|
|
60
|
+
for (const file of sources) {
|
|
61
|
+
try {
|
|
62
|
+
const raw = fs.readFileSync(file, "utf8");
|
|
63
|
+
const j = JSON.parse(raw);
|
|
64
|
+
catalogCache = Array.isArray(j?.entries) ? j.entries.filter((e) => e && typeof e.id === "string") : [];
|
|
65
|
+
if (file !== catalogFile()) {
|
|
66
|
+
try {
|
|
67
|
+
fs.unlinkSync(file);
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return catalogCache;
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catalogCache = [];
|
|
76
|
+
return catalogCache;
|
|
77
|
+
}
|
|
78
|
+
function getCatalog() {
|
|
79
|
+
return ensureLoaded();
|
|
80
|
+
}
|
|
81
|
+
function getCatalogEntry(id) {
|
|
82
|
+
const t = id.toLowerCase();
|
|
83
|
+
return ensureLoaded().find((e) => e.id.toLowerCase() === t);
|
|
84
|
+
}
|
|
85
|
+
var writeChain = Promise.resolve();
|
|
86
|
+
function enqueueWrite(fn) {
|
|
87
|
+
const next = writeChain.then(fn, fn);
|
|
88
|
+
writeChain = next.then(
|
|
89
|
+
() => {
|
|
90
|
+
},
|
|
91
|
+
() => {
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
return next;
|
|
95
|
+
}
|
|
96
|
+
async function writeCatalog(entries) {
|
|
97
|
+
const file = catalogFile();
|
|
98
|
+
await fsp.mkdir(path.dirname(file), { recursive: true });
|
|
99
|
+
const tmp = `${file}.tmp`;
|
|
100
|
+
await fsp.writeFile(tmp, JSON.stringify({ version: CATALOG_VERSION, entries }, null, 2), {
|
|
101
|
+
encoding: "utf8",
|
|
102
|
+
mode: 420
|
|
103
|
+
});
|
|
104
|
+
await fsp.rename(tmp, file);
|
|
105
|
+
catalogCache = entries;
|
|
106
|
+
}
|
|
107
|
+
async function upsertCatalogEntries(entries) {
|
|
108
|
+
await enqueueWrite(async () => {
|
|
109
|
+
const cur = ensureLoaded();
|
|
110
|
+
const map = /* @__PURE__ */ new Map();
|
|
111
|
+
for (const e of cur) map.set(e.id.toLowerCase(), e);
|
|
112
|
+
for (const e of entries) {
|
|
113
|
+
const clean = Object.fromEntries(Object.entries(e).filter(([, v]) => v !== void 0));
|
|
114
|
+
const key = clean.id.toLowerCase();
|
|
115
|
+
const prev = map.get(key);
|
|
116
|
+
map.set(key, prev ? { ...prev, ...clean } : clean);
|
|
117
|
+
}
|
|
118
|
+
await writeCatalog([...map.values()]);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async function clearCatalog() {
|
|
122
|
+
await enqueueWrite(async () => {
|
|
123
|
+
await writeCatalog([]);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async function updateCatalogEntry(id, patch) {
|
|
127
|
+
let result = null;
|
|
128
|
+
await enqueueWrite(async () => {
|
|
129
|
+
const cur = ensureLoaded();
|
|
130
|
+
const idx = cur.findIndex((e) => e.id.toLowerCase() === id.toLowerCase());
|
|
131
|
+
if (idx < 0) return;
|
|
132
|
+
const next = { ...cur[idx], ...patch, id: cur[idx].id, updatedAt: Date.now() };
|
|
133
|
+
for (const k of Object.keys(patch)) {
|
|
134
|
+
if (patch[k] === null) delete next[k];
|
|
135
|
+
}
|
|
136
|
+
const list = cur.slice();
|
|
137
|
+
list[idx] = next;
|
|
138
|
+
await writeCatalog(list);
|
|
139
|
+
result = next;
|
|
140
|
+
});
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
var BRAND_NORMALIZE = {
|
|
144
|
+
meituan: "MeiTuan",
|
|
145
|
+
tencent: "Tencent",
|
|
146
|
+
xiaomi: "Xiaomi",
|
|
147
|
+
openai: "OpenAI",
|
|
148
|
+
anthropic: "Anthropic",
|
|
149
|
+
google: "Google",
|
|
150
|
+
deepseek: "DeepSeek",
|
|
151
|
+
zhipu: "Zhipu",
|
|
152
|
+
moonshot: "Moonshot",
|
|
153
|
+
alibaba: "Alibaba",
|
|
154
|
+
minimax: "MiniMax"
|
|
454
155
|
};
|
|
156
|
+
function normalizeBrand(raw) {
|
|
157
|
+
const k = String(raw || "").toLowerCase();
|
|
158
|
+
return BRAND_NORMALIZE[k] || raw || "Other";
|
|
159
|
+
}
|
|
455
160
|
function inferBrand(modelId) {
|
|
456
161
|
const m = modelId.toLowerCase();
|
|
457
162
|
if (m.startsWith("gpt") || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("chatgpt")) return "OpenAI";
|
|
@@ -467,29 +172,194 @@ function inferBrand(modelId) {
|
|
|
467
172
|
if (m.startsWith("hunyuan") || m.startsWith("tencent") || m.startsWith("hy")) return "Tencent";
|
|
468
173
|
return "Other";
|
|
469
174
|
}
|
|
175
|
+
var DEFAULT_REASONING_EFFORTS = {
|
|
176
|
+
off: null,
|
|
177
|
+
minimal: "minimal",
|
|
178
|
+
low: "low",
|
|
179
|
+
medium: "medium",
|
|
180
|
+
high: "high",
|
|
181
|
+
xhigh: "xhigh",
|
|
182
|
+
max: "max"
|
|
183
|
+
};
|
|
470
184
|
function resolveModelMeta(modelId) {
|
|
471
|
-
|
|
472
|
-
|
|
185
|
+
const entry = getCatalogEntry(modelId);
|
|
186
|
+
if (entry) {
|
|
187
|
+
return {
|
|
188
|
+
id: entry.id,
|
|
189
|
+
name: entry.name || entry.id,
|
|
190
|
+
brand: entry.brand || inferBrand(entry.id),
|
|
191
|
+
contextWindow: entry.contextWindow ?? 262144,
|
|
192
|
+
maxTokens: entry.maxTokens ?? 32768,
|
|
193
|
+
modalities: entry.input && entry.input.length > 0 ? [...entry.input] : ["text"],
|
|
194
|
+
...entry.reasoningEfforts && typeof entry.reasoningEfforts === "object" && Object.keys(entry.reasoningEfforts).length > 0 ? {
|
|
195
|
+
reasoningEfforts: Object.fromEntries(
|
|
196
|
+
Object.entries(entry.reasoningEfforts).filter(([, v]) => v !== null && v !== void 0)
|
|
197
|
+
)
|
|
198
|
+
} : {}
|
|
199
|
+
};
|
|
473
200
|
}
|
|
474
201
|
const lowerId = modelId.toLowerCase();
|
|
475
|
-
for (const [k, v] of Object.entries(A6API_CATALOG)) {
|
|
476
|
-
if (k.toLowerCase() === lowerId) {
|
|
477
|
-
return v;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
const brand = inferBrand(modelId);
|
|
481
202
|
const isVision = lowerId.includes("vision") || lowerId.includes("vl") || lowerId.includes("image");
|
|
482
203
|
const hasReasoning = lowerId.includes("think") || lowerId.includes("reason") || lowerId.includes("pro") || lowerId.includes("sol");
|
|
483
204
|
return {
|
|
484
205
|
id: modelId,
|
|
485
206
|
name: modelId,
|
|
486
|
-
brand,
|
|
207
|
+
brand: inferBrand(modelId),
|
|
487
208
|
contextWindow: 262144,
|
|
488
209
|
maxTokens: 32768,
|
|
489
210
|
modalities: isVision ? ["text", "image"] : ["text"],
|
|
490
211
|
...hasReasoning ? { thinkingFormat: "deepseek" } : {}
|
|
491
212
|
};
|
|
492
213
|
}
|
|
214
|
+
var MARKET_SEARCH = "https://a6api.com/api/marketplace/channels/search";
|
|
215
|
+
var PAGE_SIZE = 500;
|
|
216
|
+
var CONCURRENCY = 6;
|
|
217
|
+
function buildWebHeaders(userId, accessToken) {
|
|
218
|
+
const headers = {
|
|
219
|
+
Accept: "application/json",
|
|
220
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
|
|
221
|
+
};
|
|
222
|
+
const uid = userId ? String(userId).trim() : "";
|
|
223
|
+
const token = accessToken ? String(accessToken).trim() : "";
|
|
224
|
+
if (uid) headers["New-Api-User"] = uid;
|
|
225
|
+
if (token) {
|
|
226
|
+
headers["Authorization"] = token;
|
|
227
|
+
headers["Cookie"] = `session=${token}`;
|
|
228
|
+
}
|
|
229
|
+
return headers;
|
|
230
|
+
}
|
|
231
|
+
async function fetchMarketplaceModels(userId, accessToken) {
|
|
232
|
+
if (!userId && !accessToken) {
|
|
233
|
+
throw new Error("\u9700\u5148\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C\u624D\u80FD\u83B7\u53D6\u5E02\u573A\u6A21\u578B");
|
|
234
|
+
}
|
|
235
|
+
const headers = buildWebHeaders(userId, accessToken);
|
|
236
|
+
const first = await (async () => {
|
|
237
|
+
const res = await fetch(`${MARKET_SEARCH}?view=list&page=1&page_size=${PAGE_SIZE}`, {
|
|
238
|
+
headers,
|
|
239
|
+
signal: AbortSignal.timeout(15e3)
|
|
240
|
+
});
|
|
241
|
+
if (!res.ok) throw new Error(`A6API \u5E02\u573A\u63A5\u53E3 HTTP ${res.status}`);
|
|
242
|
+
return res.json();
|
|
243
|
+
})();
|
|
244
|
+
const total = Number(first?.data?.total || 0);
|
|
245
|
+
const pages = Math.max(1, Math.ceil(total / PAGE_SIZE));
|
|
246
|
+
const all = [...first?.data?.items || []];
|
|
247
|
+
let failedPages = 0;
|
|
248
|
+
let idx = 2;
|
|
249
|
+
const worker = async () => {
|
|
250
|
+
while (idx <= pages) {
|
|
251
|
+
const p = idx++;
|
|
252
|
+
try {
|
|
253
|
+
const res = await fetch(`${MARKET_SEARCH}?view=list&page=${p}&page_size=${PAGE_SIZE}`, {
|
|
254
|
+
headers,
|
|
255
|
+
signal: AbortSignal.timeout(15e3)
|
|
256
|
+
});
|
|
257
|
+
const j = await res.json();
|
|
258
|
+
all.push(...j?.data?.items || []);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
failedPages++;
|
|
261
|
+
console.warn("[dsh-a6api] fetchMarketplaceModels page", p, "failed:", err?.message || err);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
await Promise.all(Array.from({ length: CONCURRENCY }, () => worker()));
|
|
266
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
267
|
+
for (const it of all) {
|
|
268
|
+
const name2 = it?.model_name;
|
|
269
|
+
if (!name2) continue;
|
|
270
|
+
if (!byModel.has(name2)) {
|
|
271
|
+
byModel.set(name2, {
|
|
272
|
+
id: String(name2),
|
|
273
|
+
brand: normalizeBrand(it?.brand),
|
|
274
|
+
// 默认声明 DSH 全部思考档位(用户可修改;upsert 仅补缺失字段,已有自定义不受影响)
|
|
275
|
+
reasoningEfforts: { ...DEFAULT_REASONING_EFFORTS }
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const models = [...byModel.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
280
|
+
return { models, failedPages };
|
|
281
|
+
}
|
|
282
|
+
var OPENROUTER_URL = "https://openrouter.ai/api/v1/models";
|
|
283
|
+
var OR_TTL_MS = 60 * 60 * 1e3;
|
|
284
|
+
var orCache = null;
|
|
285
|
+
async function getOpenRouterModels() {
|
|
286
|
+
if (orCache && Date.now() - orCache.at < OR_TTL_MS) return orCache.models;
|
|
287
|
+
try {
|
|
288
|
+
const res = await fetch(OPENROUTER_URL, {
|
|
289
|
+
headers: { Accept: "application/json", "User-Agent": "Mozilla/5.0" },
|
|
290
|
+
signal: AbortSignal.timeout(2e4)
|
|
291
|
+
});
|
|
292
|
+
if (!res.ok) throw new Error(`OpenRouter HTTP ${res.status}`);
|
|
293
|
+
const j = await res.json();
|
|
294
|
+
if (!Array.isArray(j?.data)) throw new Error("OpenRouter \u8FD4\u56DE\u683C\u5F0F\u5F02\u5E38");
|
|
295
|
+
orCache = { at: Date.now(), models: j.data };
|
|
296
|
+
return orCache.models;
|
|
297
|
+
} catch (err) {
|
|
298
|
+
if (orCache) return orCache.models;
|
|
299
|
+
throw err;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
var tailOf = (s) => String(s).split("/").pop() || "";
|
|
303
|
+
function matchOpenRouter(models, target) {
|
|
304
|
+
const t = target.toLowerCase();
|
|
305
|
+
const tTail = tailOf(t);
|
|
306
|
+
const norm = (s) => s.toLowerCase().replace(/[\s_.]+/g, "-");
|
|
307
|
+
const byTail = models.find((m) => tailOf(String(m.id)).toLowerCase() === tTail);
|
|
308
|
+
if (byTail) return byTail;
|
|
309
|
+
const nTail = norm(tTail);
|
|
310
|
+
const byNorm = models.find((m) => norm(tailOf(String(m.id))) === nTail);
|
|
311
|
+
if (byNorm) return byNorm;
|
|
312
|
+
const stripped = tTail.replace(/-(?:\d{4}-\d{2}-\d{2}|\d{8}|-\d{2,4}|\d\.\d+)$/, "");
|
|
313
|
+
if (stripped && stripped !== tTail) {
|
|
314
|
+
const byStripped = models.find((m) => tailOf(String(m.id)).toLowerCase() === stripped);
|
|
315
|
+
if (byStripped) return byStripped;
|
|
316
|
+
}
|
|
317
|
+
const tSeg = t.split("/").pop() || t;
|
|
318
|
+
return models.find((m) => {
|
|
319
|
+
const id = String(m.id).toLowerCase();
|
|
320
|
+
if (!id.includes(t)) return false;
|
|
321
|
+
const tailSeg = id.split("/").pop() || "";
|
|
322
|
+
return tailSeg === t || tailSeg.split("-").includes(tSeg);
|
|
323
|
+
}) || null;
|
|
324
|
+
}
|
|
325
|
+
function orModalities(m) {
|
|
326
|
+
const set = /* @__PURE__ */ new Set();
|
|
327
|
+
const input = m?.architecture?.input_modalities || [];
|
|
328
|
+
for (const mod of input) {
|
|
329
|
+
const k = String(mod).toLowerCase();
|
|
330
|
+
if (k === "text") set.add("text");
|
|
331
|
+
else if (k === "image") set.add("image");
|
|
332
|
+
}
|
|
333
|
+
return [...set];
|
|
334
|
+
}
|
|
335
|
+
async function queryOpenRouter(ids) {
|
|
336
|
+
const uniq = [...new Set(ids.map((s) => String(s).trim()).filter(Boolean))];
|
|
337
|
+
if (uniq.length === 0) return { updated: [], notFound: [] };
|
|
338
|
+
const models = await getOpenRouterModels();
|
|
339
|
+
const updated = [];
|
|
340
|
+
const notFound = [];
|
|
341
|
+
for (const id of uniq) {
|
|
342
|
+
const hit = matchOpenRouter(models, id);
|
|
343
|
+
if (!hit) {
|
|
344
|
+
notFound.push(id);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const patch = {
|
|
348
|
+
updatedAt: Date.now()
|
|
349
|
+
};
|
|
350
|
+
const ctx = hit.context_length ?? hit.top_provider?.context_length;
|
|
351
|
+
if (ctx != null && Number(ctx) > 0) patch.contextWindow = Number(ctx);
|
|
352
|
+
const maxOut = hit.top_provider?.max_completion_tokens ?? hit.max_completion_tokens;
|
|
353
|
+
if (maxOut != null && Number(maxOut) > 0) patch.maxTokens = Number(maxOut);
|
|
354
|
+
const mods = orModalities(hit);
|
|
355
|
+
if (mods.length > 0) patch.input = mods;
|
|
356
|
+
updated.push({ id, ...patch });
|
|
357
|
+
}
|
|
358
|
+
if (updated.length > 0) {
|
|
359
|
+
await upsertCatalogEntries(updated);
|
|
360
|
+
}
|
|
361
|
+
return { updated, notFound };
|
|
362
|
+
}
|
|
493
363
|
|
|
494
364
|
// src/server/a6api-client.ts
|
|
495
365
|
function cleanBaseUrl(url) {
|
|
@@ -516,7 +386,7 @@ function formatCnyPrice(micros, exchangeRate = 6.7209) {
|
|
|
516
386
|
if (cny < 1) return `\xA5${cny.toFixed(4)}`;
|
|
517
387
|
return `\xA5${cny.toFixed(3)}`;
|
|
518
388
|
}
|
|
519
|
-
function
|
|
389
|
+
function buildWebHeaders2(userId, accessToken) {
|
|
520
390
|
const headers = {
|
|
521
391
|
Accept: "application/json",
|
|
522
392
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
|
|
@@ -525,8 +395,8 @@ function buildWebHeaders(userId, sessionCookie) {
|
|
|
525
395
|
if (uid) {
|
|
526
396
|
headers["New-Api-User"] = uid;
|
|
527
397
|
}
|
|
528
|
-
if (
|
|
529
|
-
const raw =
|
|
398
|
+
if (accessToken && accessToken.trim()) {
|
|
399
|
+
const raw = accessToken.trim();
|
|
530
400
|
if (raw.startsWith("session=")) {
|
|
531
401
|
headers["Cookie"] = raw;
|
|
532
402
|
} else if (raw.includes(";")) {
|
|
@@ -538,7 +408,7 @@ function buildWebHeaders(userId, sessionCookie) {
|
|
|
538
408
|
}
|
|
539
409
|
return headers;
|
|
540
410
|
}
|
|
541
|
-
async function fetchBalance(baseURL, apiKey, userId,
|
|
411
|
+
async function fetchBalance(baseURL, apiKey, userId, accessToken) {
|
|
542
412
|
const cleanUrl = cleanBaseUrl(baseURL);
|
|
543
413
|
let hasAccountAuth = false;
|
|
544
414
|
let accountBalanceUsd = 0;
|
|
@@ -548,13 +418,13 @@ async function fetchBalance(baseURL, apiKey, userId, sessionCookie) {
|
|
|
548
418
|
let responseUserId = userId;
|
|
549
419
|
let usedUsd = 0;
|
|
550
420
|
let requestCount = 0;
|
|
551
|
-
if (userId ||
|
|
421
|
+
if (userId || accessToken) {
|
|
552
422
|
const candidates = ["https://a6api.com/api/user/self", `${cleanUrl}/api/user/self`];
|
|
553
423
|
const uniqueCandidates = [...new Set(candidates)];
|
|
554
424
|
for (const url of uniqueCandidates) {
|
|
555
425
|
try {
|
|
556
426
|
const res = await fetch(url, {
|
|
557
|
-
headers:
|
|
427
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
558
428
|
signal: AbortSignal.timeout(6e3)
|
|
559
429
|
});
|
|
560
430
|
if (res.ok) {
|
|
@@ -642,11 +512,11 @@ async function fetchTokenModels(baseURL, apiKey) {
|
|
|
642
512
|
}
|
|
643
513
|
return [];
|
|
644
514
|
}
|
|
645
|
-
async function fetchRecentLogs(userId,
|
|
646
|
-
if (!userId && !
|
|
515
|
+
async function fetchRecentLogs(userId, accessToken, limit = 30) {
|
|
516
|
+
if (!userId && !accessToken) return [];
|
|
647
517
|
try {
|
|
648
518
|
const res = await fetch(`https://a6api.com/api/log/self?p=1&page_size=${limit}&type=0`, {
|
|
649
|
-
headers:
|
|
519
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
650
520
|
signal: AbortSignal.timeout(8e3)
|
|
651
521
|
});
|
|
652
522
|
if (res.ok) {
|
|
@@ -681,6 +551,7 @@ async function fetchRecentLogs(userId, sessionCookie, limit = 30) {
|
|
|
681
551
|
model_name: it.model_name || it.marketplace_model_name || "",
|
|
682
552
|
channel: channelId,
|
|
683
553
|
channel_name: it.channel_name || (channelId ? `\u5546\u6237 #${channelId}` : void 0),
|
|
554
|
+
token_id: it.token_id !== void 0 && it.token_id !== null ? Number(it.token_id) : void 0,
|
|
684
555
|
prompt_tokens: Number(it.prompt_tokens || 0),
|
|
685
556
|
completion_tokens: Number(it.completion_tokens || 0),
|
|
686
557
|
use_time: Number(it.use_time || 0),
|
|
@@ -700,7 +571,7 @@ async function fetchRecentLogs(userId, sessionCookie, limit = 30) {
|
|
|
700
571
|
}
|
|
701
572
|
return [];
|
|
702
573
|
}
|
|
703
|
-
async function fetchChannelDetails(channelId, userId,
|
|
574
|
+
async function fetchChannelDetails(channelId, userId, accessToken, targetModelName, logSnapshot) {
|
|
704
575
|
if (!channelId) return null;
|
|
705
576
|
const targetName = targetModelName || "";
|
|
706
577
|
const meta = resolveModelMeta(targetName);
|
|
@@ -708,7 +579,7 @@ async function fetchChannelDetails(channelId, userId, sessionCookie, targetModel
|
|
|
708
579
|
const res = await fetch(
|
|
709
580
|
`https://a6api.com/api/marketplace/channels/search?channel_id=${channelId}&view=list&page=1&page_size=20`,
|
|
710
581
|
{
|
|
711
|
-
headers:
|
|
582
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
712
583
|
signal: AbortSignal.timeout(8e3)
|
|
713
584
|
}
|
|
714
585
|
);
|
|
@@ -781,7 +652,8 @@ async function fetchChannelDetails(channelId, userId, sessionCookie, targetModel
|
|
|
781
652
|
const ratioFormatted = ratioCny.toFixed(4);
|
|
782
653
|
return {
|
|
783
654
|
listing_id: item.listing_id,
|
|
784
|
-
channel_id
|
|
655
|
+
// 归一化为数字,避免官方返回字符串 channel_id 导致严格相等比较失效(接管/固定判定依赖)
|
|
656
|
+
channel_id: item.channel_id !== void 0 && item.channel_id !== null ? Number(item.channel_id) : channelId,
|
|
785
657
|
channel_name: item.channel_name || `\u5546\u6237 #${channelId}`,
|
|
786
658
|
supplier_name: item.supplier_name || item.supplier_nickname || "GPT\u4F4E\u4EF7",
|
|
787
659
|
supplier_id: item.supplier_id || 290,
|
|
@@ -819,8 +691,10 @@ async function fetchChannelDetails(channelId, userId, sessionCookie, targetModel
|
|
|
819
691
|
last_success_text: formatRelativeTime(lastSuccessAt),
|
|
820
692
|
authenticity_guaranteed: Boolean(item.authenticity_guaranteed),
|
|
821
693
|
authenticity_badge: item.authenticity_guarantee_badge_key,
|
|
822
|
-
|
|
823
|
-
|
|
694
|
+
// 官方固定状态值是 pin_here / pin_elsewhere(早期代码误用 'pinned',已修正)
|
|
695
|
+
is_pinned: item.pin_status === "pin_here" || item.route_status === "pin_here",
|
|
696
|
+
pin_status: typeof item.pin_status === "string" ? item.pin_status : void 0,
|
|
697
|
+
user_channel_disabled: Boolean(item.user_channel_disabled || item.route_status === "user_disabled"),
|
|
824
698
|
supplier_channel_disabled: Boolean(item.supplier_channel_disabled),
|
|
825
699
|
raw: item
|
|
826
700
|
};
|
|
@@ -892,13 +766,13 @@ async function fetchChannelDetails(channelId, userId, sessionCookie, targetModel
|
|
|
892
766
|
}
|
|
893
767
|
return null;
|
|
894
768
|
}
|
|
895
|
-
async function fetchPriceFluctuation(userId,
|
|
896
|
-
const token = (accessToken ||
|
|
769
|
+
async function fetchPriceFluctuation(userId, accessToken) {
|
|
770
|
+
const token = (accessToken || "").trim();
|
|
897
771
|
const uid = (userId || "").trim();
|
|
898
772
|
if (!uid && !token) {
|
|
899
773
|
return { pendingCount: 0, unseenCount: 0, totalCount: 0, authError: false };
|
|
900
774
|
}
|
|
901
|
-
const headers =
|
|
775
|
+
const headers = buildWebHeaders2(uid || void 0, token || void 0);
|
|
902
776
|
const url = "https://a6api.com/api/marketplace/price-notices";
|
|
903
777
|
try {
|
|
904
778
|
const res = await fetch(url, { headers, signal: AbortSignal.timeout(8e3) });
|
|
@@ -952,9 +826,161 @@ async function fetchPriceFluctuation(userId, sessionCookie, accessToken) {
|
|
|
952
826
|
return { pendingCount: 0, unseenCount: 0, totalCount: 0 };
|
|
953
827
|
}
|
|
954
828
|
}
|
|
829
|
+
function parseMarketplaceResult(json) {
|
|
830
|
+
if (!json) return { ok: false, message: "\u7A7A\u54CD\u5E94" };
|
|
831
|
+
const top = json.success === false ? json : null;
|
|
832
|
+
const inner = json.data && json.data.success === false ? json.data : null;
|
|
833
|
+
if (top) return { ok: false, message: top.message || "\u64CD\u4F5C\u5931\u8D25" };
|
|
834
|
+
if (inner) return { ok: false, message: inner.message || "\u64CD\u4F5C\u5931\u8D25" };
|
|
835
|
+
return { ok: true, data: json.data };
|
|
836
|
+
}
|
|
837
|
+
function extractArray(json) {
|
|
838
|
+
if (!json) return [];
|
|
839
|
+
if (Array.isArray(json)) return json;
|
|
840
|
+
if (Array.isArray(json.data)) return json.data;
|
|
841
|
+
if (json.data && Array.isArray(json.data.data)) return json.data.data;
|
|
842
|
+
if (json.data && Array.isArray(json.data.items)) return json.data.items;
|
|
843
|
+
if (json.data && json.data.data && Array.isArray(json.data.data.items)) return json.data.data.items;
|
|
844
|
+
return [];
|
|
845
|
+
}
|
|
846
|
+
async function fetchMarketplacePins(userId, accessToken) {
|
|
847
|
+
if (!userId && !accessToken) return [];
|
|
848
|
+
try {
|
|
849
|
+
const res = await fetch("https://a6api.com/api/marketplace/pins", {
|
|
850
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
851
|
+
signal: AbortSignal.timeout(8e3)
|
|
852
|
+
});
|
|
853
|
+
if (!res.ok) {
|
|
854
|
+
console.warn("[dsh-a6api] fetchMarketplacePins HTTP", res.status);
|
|
855
|
+
return [];
|
|
856
|
+
}
|
|
857
|
+
const json = await res.json().catch(() => null);
|
|
858
|
+
if (!json || json.success === false) return [];
|
|
859
|
+
return extractArray(json).filter((it) => it && it.model_name).map((it) => ({
|
|
860
|
+
id: it.id !== void 0 ? Number(it.id) : void 0,
|
|
861
|
+
token_id: Number(it.token_id || 0),
|
|
862
|
+
token_name: it.token_name || void 0,
|
|
863
|
+
model_name: String(it.model_name),
|
|
864
|
+
channel_id: it.channel_id !== void 0 && Number(it.channel_id) > 0 ? Number(it.channel_id) : void 0,
|
|
865
|
+
channel_name: it.channel_name || void 0,
|
|
866
|
+
supplier_name: it.supplier_name || void 0,
|
|
867
|
+
supplier_nickname: it.supplier_nickname || void 0,
|
|
868
|
+
fallback_to_smart_routing: it.fallback_to_smart_routing !== void 0 ? Boolean(it.fallback_to_smart_routing) : void 0,
|
|
869
|
+
created_at: it.created_at !== void 0 ? Number(it.created_at) : void 0
|
|
870
|
+
// 不下发 raw:客户端 60s 轮询用 JSON 对比判断变化,raw 含易变字段会导致误判整页刷新
|
|
871
|
+
}));
|
|
872
|
+
} catch (err) {
|
|
873
|
+
console.warn("[dsh-a6api] fetchMarketplacePins error:", err);
|
|
874
|
+
return [];
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
async function fetchTokens(userId, accessToken) {
|
|
878
|
+
if (!userId && !accessToken) return [];
|
|
879
|
+
try {
|
|
880
|
+
const res = await fetch("https://a6api.com/api/token/?p=1&size=100", {
|
|
881
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
882
|
+
signal: AbortSignal.timeout(8e3)
|
|
883
|
+
});
|
|
884
|
+
if (!res.ok) return [];
|
|
885
|
+
const json = await res.json().catch(() => null);
|
|
886
|
+
if (!json || json.success === false) return [];
|
|
887
|
+
return extractArray(json).filter((it) => it && Number(it.id) > 0).map((it) => ({
|
|
888
|
+
id: Number(it.id),
|
|
889
|
+
name: it.name || void 0,
|
|
890
|
+
key: typeof it.key === "string" ? it.key : void 0,
|
|
891
|
+
status: it.status !== void 0 ? Number(it.status) : void 0,
|
|
892
|
+
raw: it
|
|
893
|
+
}));
|
|
894
|
+
} catch (err) {
|
|
895
|
+
console.warn("[dsh-a6api] fetchTokens error:", err);
|
|
896
|
+
return [];
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
function friendlyActionError(err) {
|
|
900
|
+
const raw = err?.message || String(err);
|
|
901
|
+
if (raw.includes("aborted due to timeout") || err?.name === "TimeoutError") {
|
|
902
|
+
return "\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u91CD\u8BD5";
|
|
903
|
+
}
|
|
904
|
+
return raw;
|
|
905
|
+
}
|
|
906
|
+
async function marketplacePin(userId, accessToken, payload) {
|
|
907
|
+
try {
|
|
908
|
+
const res = await fetch("https://a6api.com/api/marketplace/pin", {
|
|
909
|
+
method: "POST",
|
|
910
|
+
headers: {
|
|
911
|
+
"Content-Type": "application/json",
|
|
912
|
+
...buildWebHeaders2(userId, accessToken)
|
|
913
|
+
},
|
|
914
|
+
body: JSON.stringify(payload),
|
|
915
|
+
signal: AbortSignal.timeout(1e4)
|
|
916
|
+
});
|
|
917
|
+
const json = await res.json().catch(() => null);
|
|
918
|
+
const result = parseMarketplaceResult(json);
|
|
919
|
+
if (!res.ok && !json) return { ok: false, message: `HTTP ${res.status}` };
|
|
920
|
+
return result;
|
|
921
|
+
} catch (err) {
|
|
922
|
+
console.warn("[dsh-a6api] marketplacePin error:", err);
|
|
923
|
+
return { ok: false, message: friendlyActionError(err) };
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
async function marketplaceUnpin(userId, accessToken, payload) {
|
|
927
|
+
try {
|
|
928
|
+
const res = await fetch("https://a6api.com/api/marketplace/unpin", {
|
|
929
|
+
method: "POST",
|
|
930
|
+
headers: {
|
|
931
|
+
"Content-Type": "application/json",
|
|
932
|
+
...buildWebHeaders2(userId, accessToken)
|
|
933
|
+
},
|
|
934
|
+
body: JSON.stringify(payload),
|
|
935
|
+
signal: AbortSignal.timeout(1e4)
|
|
936
|
+
});
|
|
937
|
+
const json = await res.json().catch(() => null);
|
|
938
|
+
if (!res.ok && !json) return { ok: false, message: `HTTP ${res.status}` };
|
|
939
|
+
return parseMarketplaceResult(json);
|
|
940
|
+
} catch (err) {
|
|
941
|
+
console.warn("[dsh-a6api] marketplaceUnpin error:", err);
|
|
942
|
+
return { ok: false, message: friendlyActionError(err) };
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
async function marketplaceDisableChannel(userId, accessToken, channelId, model) {
|
|
946
|
+
try {
|
|
947
|
+
const res = await fetch(
|
|
948
|
+
`https://a6api.com/api/marketplace/channels/${channelId}/disable?model=${encodeURIComponent(model)}`,
|
|
949
|
+
{
|
|
950
|
+
method: "POST",
|
|
951
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
952
|
+
signal: AbortSignal.timeout(1e4)
|
|
953
|
+
}
|
|
954
|
+
);
|
|
955
|
+
const json = await res.json().catch(() => null);
|
|
956
|
+
if (!res.ok && !json) return { ok: false, message: `HTTP ${res.status}` };
|
|
957
|
+
return parseMarketplaceResult(json);
|
|
958
|
+
} catch (err) {
|
|
959
|
+
console.warn("[dsh-a6api] marketplaceDisableChannel error:", err);
|
|
960
|
+
return { ok: false, message: friendlyActionError(err) };
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
async function marketplaceRestoreChannel(userId, accessToken, channelId, model) {
|
|
964
|
+
try {
|
|
965
|
+
const res = await fetch(
|
|
966
|
+
`https://a6api.com/api/marketplace/channels/${channelId}/restore?model=${encodeURIComponent(model)}`,
|
|
967
|
+
{
|
|
968
|
+
method: "POST",
|
|
969
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
970
|
+
signal: AbortSignal.timeout(1e4)
|
|
971
|
+
}
|
|
972
|
+
);
|
|
973
|
+
const json = await res.json().catch(() => null);
|
|
974
|
+
if (!res.ok && !json) return { ok: false, message: `HTTP ${res.status}` };
|
|
975
|
+
return parseMarketplaceResult(json);
|
|
976
|
+
} catch (err) {
|
|
977
|
+
console.warn("[dsh-a6api] marketplaceRestoreChannel error:", err);
|
|
978
|
+
return { ok: false, message: friendlyActionError(err) };
|
|
979
|
+
}
|
|
980
|
+
}
|
|
955
981
|
|
|
956
982
|
// src/server/probe.ts
|
|
957
|
-
var sleep = (ms) => new Promise((
|
|
983
|
+
var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
958
984
|
async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName) {
|
|
959
985
|
const targetModel = modelName || "";
|
|
960
986
|
const cleanUrl = cleanBaseUrl(baseURL);
|
|
@@ -1017,6 +1043,8 @@ async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName)
|
|
|
1017
1043
|
success: true,
|
|
1018
1044
|
channelId,
|
|
1019
1045
|
channelName: log.channel_name,
|
|
1046
|
+
// 该次请求由 API Key 发起,日志中记录的 token_id 即该 Key 的令牌 ID
|
|
1047
|
+
tokenId: log.token_id !== void 0 && Number(log.token_id) > 0 ? Number(log.token_id) : void 0,
|
|
1020
1048
|
merchant,
|
|
1021
1049
|
durationMs
|
|
1022
1050
|
};
|
|
@@ -1082,77 +1110,56 @@ async function getKnownMerchantsFromLogs(userId, accessToken, modelNames = [], l
|
|
|
1082
1110
|
}
|
|
1083
1111
|
|
|
1084
1112
|
// src/server/sync.ts
|
|
1085
|
-
import * as
|
|
1086
|
-
import * as
|
|
1113
|
+
import * as fs2 from "node:fs";
|
|
1114
|
+
import * as fsp2 from "node:fs/promises";
|
|
1115
|
+
import * as path2 from "node:path";
|
|
1087
1116
|
import * as os from "node:os";
|
|
1088
1117
|
var A6API_CRED_REF = "A6API_API_KEY";
|
|
1118
|
+
var A6API_TOKEN_REF = "A6API_ACCESS_TOKEN";
|
|
1119
|
+
var A6API_USER_REF = "A6API_USER_ID";
|
|
1120
|
+
var SETTINGS_NS = "llm-pi-ai";
|
|
1121
|
+
var PROVIDER_KEY = "a6api";
|
|
1122
|
+
var DEFAULT_BASE_URL = "https://api.a6api.com";
|
|
1123
|
+
var LEGACY_CONFIG_NAME = "dsh-a6api-config.json";
|
|
1089
1124
|
async function atomicWriteFile(filePath, content, mode = 384) {
|
|
1090
|
-
const dir =
|
|
1091
|
-
await
|
|
1092
|
-
const tmpPath =
|
|
1093
|
-
await
|
|
1094
|
-
await
|
|
1125
|
+
const dir = path2.dirname(filePath);
|
|
1126
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
1127
|
+
const tmpPath = path2.join(dir, `.${path2.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
1128
|
+
await fsp2.writeFile(tmpPath, content, { mode });
|
|
1129
|
+
await fsp2.rename(tmpPath, filePath);
|
|
1095
1130
|
}
|
|
1096
1131
|
function dshHome() {
|
|
1097
|
-
return process.env.DSH_HOME ||
|
|
1132
|
+
return process.env.DSH_HOME || path2.join(os.homedir(), ".dsh");
|
|
1098
1133
|
}
|
|
1099
|
-
function
|
|
1100
|
-
return
|
|
1134
|
+
function legacyConfigFile() {
|
|
1135
|
+
return path2.join(dshHome(), LEGACY_CONFIG_NAME);
|
|
1101
1136
|
}
|
|
1102
1137
|
function credentialsFile() {
|
|
1103
|
-
return
|
|
1138
|
+
return path2.join(dshHome(), ".credentials.yaml");
|
|
1104
1139
|
}
|
|
1105
1140
|
function settingsFile() {
|
|
1106
|
-
return
|
|
1141
|
+
return path2.join(dshHome(), "settings.yaml");
|
|
1107
1142
|
}
|
|
1108
|
-
|
|
1109
|
-
const filePath = configFile();
|
|
1110
|
-
let raw = "";
|
|
1143
|
+
function getCredentials(ctx) {
|
|
1111
1144
|
try {
|
|
1112
|
-
|
|
1145
|
+
if (ctx && typeof ctx.get === "function") return ctx.get("credentials");
|
|
1113
1146
|
} catch {
|
|
1114
|
-
const apiKey = await readCredentialKey(A6API_CRED_REF) || "";
|
|
1115
|
-
return {
|
|
1116
|
-
baseURL: "https://api.a6api.com",
|
|
1117
|
-
apiKey,
|
|
1118
|
-
userId: "",
|
|
1119
|
-
activeModels: []
|
|
1120
|
-
};
|
|
1121
1147
|
}
|
|
1148
|
+
return void 0;
|
|
1149
|
+
}
|
|
1150
|
+
function getSettings(ctx) {
|
|
1122
1151
|
try {
|
|
1123
|
-
|
|
1124
|
-
const apiKey = parsed.apiKey || await readCredentialKey(A6API_CRED_REF) || "";
|
|
1125
|
-
const accessToken = parsed.accessToken || parsed.systemAccessToken || parsed.sessionCookie || "";
|
|
1126
|
-
return {
|
|
1127
|
-
baseURL: parsed.baseURL || "https://api.a6api.com",
|
|
1128
|
-
apiKey,
|
|
1129
|
-
accessToken,
|
|
1130
|
-
userId: parsed.userId || "",
|
|
1131
|
-
sessionCookie: accessToken,
|
|
1132
|
-
activeModels: Array.isArray(parsed.activeModels) ? parsed.activeModels : [],
|
|
1133
|
-
customBaseURL: parsed.customBaseURL
|
|
1134
|
-
};
|
|
1152
|
+
if (ctx && typeof ctx.get === "function") return ctx.get("settings");
|
|
1135
1153
|
} catch {
|
|
1136
|
-
return {
|
|
1137
|
-
baseURL: "https://api.a6api.com",
|
|
1138
|
-
apiKey: "",
|
|
1139
|
-
accessToken: "",
|
|
1140
|
-
userId: "",
|
|
1141
|
-
activeModels: []
|
|
1142
|
-
};
|
|
1143
1154
|
}
|
|
1155
|
+
return void 0;
|
|
1144
1156
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
const { apiKey: _apiKey, ...safeConfig } = config;
|
|
1148
|
-
await atomicWriteFile(filePath, JSON.stringify(safeConfig, null, 2));
|
|
1149
|
-
if (config.apiKey && config.apiKey.trim()) {
|
|
1150
|
-
await writeCredentialKey(A6API_CRED_REF, config.apiKey.trim());
|
|
1151
|
-
}
|
|
1157
|
+
function stripV1(baseURL) {
|
|
1158
|
+
return baseURL.replace(/\/v1\/?$/, "");
|
|
1152
1159
|
}
|
|
1153
1160
|
async function readCredentialKey(refKey) {
|
|
1154
1161
|
try {
|
|
1155
|
-
const yaml = await
|
|
1162
|
+
const yaml = await fsp2.readFile(credentialsFile(), "utf8");
|
|
1156
1163
|
let inRefs = false;
|
|
1157
1164
|
for (const line of yaml.split(/\r?\n/)) {
|
|
1158
1165
|
const trimmed = line.trim();
|
|
@@ -1169,7 +1176,7 @@ async function readCredentialKey(refKey) {
|
|
|
1169
1176
|
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
1170
1177
|
val = val.slice(1, -1);
|
|
1171
1178
|
}
|
|
1172
|
-
return val;
|
|
1179
|
+
return val || null;
|
|
1173
1180
|
}
|
|
1174
1181
|
}
|
|
1175
1182
|
} catch {
|
|
@@ -1180,14 +1187,14 @@ async function writeCredentialKey(refKey, value) {
|
|
|
1180
1187
|
const cFile = credentialsFile();
|
|
1181
1188
|
let yaml = "";
|
|
1182
1189
|
try {
|
|
1183
|
-
yaml = await
|
|
1190
|
+
yaml = await fsp2.readFile(cFile, "utf8");
|
|
1184
1191
|
} catch {
|
|
1185
1192
|
yaml = "version: 1\nrefs:\n";
|
|
1186
1193
|
}
|
|
1187
1194
|
const lines = yaml.split(/\r?\n/);
|
|
1188
1195
|
let inRefs = false;
|
|
1189
1196
|
let refsLineIdx = -1;
|
|
1190
|
-
let
|
|
1197
|
+
let foundIdx = -1;
|
|
1191
1198
|
for (let i = 0; i < lines.length; i++) {
|
|
1192
1199
|
const line = lines[i];
|
|
1193
1200
|
const trimmed = line.trim();
|
|
@@ -1205,58 +1212,81 @@ async function writeCredentialKey(refKey, value) {
|
|
|
1205
1212
|
if (inRefs) {
|
|
1206
1213
|
const m = /^([A-Za-z0-9_.\-]+):/.exec(trimmed);
|
|
1207
1214
|
if (m && m[1] === refKey) {
|
|
1208
|
-
|
|
1209
|
-
found = true;
|
|
1215
|
+
foundIdx = i;
|
|
1210
1216
|
break;
|
|
1211
1217
|
}
|
|
1212
1218
|
}
|
|
1213
1219
|
}
|
|
1214
|
-
if (
|
|
1215
|
-
if (
|
|
1216
|
-
|
|
1220
|
+
if (value === "") {
|
|
1221
|
+
if (foundIdx >= 0) lines.splice(foundIdx, 1);
|
|
1222
|
+
} else {
|
|
1223
|
+
if (foundIdx >= 0) {
|
|
1224
|
+
lines[foundIdx] = ` ${refKey}: ${JSON.stringify(value)}`;
|
|
1217
1225
|
} else {
|
|
1218
|
-
|
|
1226
|
+
if (refsLineIdx >= 0) {
|
|
1227
|
+
lines.splice(refsLineIdx + 1, 0, ` ${refKey}: ${JSON.stringify(value)}`);
|
|
1228
|
+
} else {
|
|
1229
|
+
lines.push("refs:", ` ${refKey}: ${JSON.stringify(value)}`);
|
|
1230
|
+
}
|
|
1219
1231
|
}
|
|
1220
1232
|
}
|
|
1221
1233
|
await atomicWriteFile(cFile, lines.join("\n"), 384);
|
|
1222
1234
|
}
|
|
1223
|
-
async function
|
|
1224
|
-
const sFile = settingsFile();
|
|
1225
|
-
let yaml = "";
|
|
1235
|
+
async function readRawConfiguredModels() {
|
|
1226
1236
|
try {
|
|
1227
|
-
yaml = await
|
|
1237
|
+
const yaml = await fsp2.readFile(settingsFile(), "utf8");
|
|
1238
|
+
const lines = yaml.split(/\r?\n/);
|
|
1239
|
+
let inA6 = false;
|
|
1240
|
+
let inModels = false;
|
|
1241
|
+
const modelIds = [];
|
|
1242
|
+
for (const line of lines) {
|
|
1243
|
+
const trimmed = line.trim();
|
|
1244
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
1245
|
+
if (indent === 4 && trimmed.startsWith("a6api:")) {
|
|
1246
|
+
inA6 = true;
|
|
1247
|
+
inModels = false;
|
|
1248
|
+
continue;
|
|
1249
|
+
}
|
|
1250
|
+
if (inA6 && indent <= 4 && !trimmed.startsWith("a6api:")) {
|
|
1251
|
+
inA6 = false;
|
|
1252
|
+
inModels = false;
|
|
1253
|
+
}
|
|
1254
|
+
if (inA6 && indent === 6 && trimmed.startsWith("models:")) {
|
|
1255
|
+
inModels = true;
|
|
1256
|
+
continue;
|
|
1257
|
+
}
|
|
1258
|
+
if (inModels && indent === 8 && trimmed.startsWith("- id:")) {
|
|
1259
|
+
const id = trimmed.replace(/^- id:\s*/, "").trim();
|
|
1260
|
+
if (id) modelIds.push(id);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
return modelIds;
|
|
1228
1264
|
} catch {
|
|
1229
|
-
|
|
1265
|
+
return [];
|
|
1230
1266
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1267
|
+
}
|
|
1268
|
+
async function readRawA6apiBaseURL() {
|
|
1269
|
+
try {
|
|
1270
|
+
const yaml = await fsp2.readFile(settingsFile(), "utf8");
|
|
1271
|
+
let inA6 = false;
|
|
1272
|
+
for (const line of yaml.split(/\r?\n/)) {
|
|
1273
|
+
const trimmed = line.trim();
|
|
1274
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1275
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
1276
|
+
if (indent === 4 && trimmed.startsWith("a6api:")) {
|
|
1277
|
+
inA6 = true;
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
if (inA6 && indent <= 4 && !trimmed.startsWith("a6api:")) inA6 = false;
|
|
1281
|
+
if (inA6 && indent === 6 && trimmed.startsWith("baseURL:")) {
|
|
1282
|
+
return trimmed.replace(/^baseURL:\s*/, "").trim().replace(/^["']|["']$/g, "");
|
|
1245
1283
|
}
|
|
1246
1284
|
}
|
|
1247
|
-
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
` displayName: A6API`,
|
|
1253
|
-
` apiKeyEnv: ${A6API_CRED_REF}`,
|
|
1254
|
-
` api: openai-completions`,
|
|
1255
|
-
` baseURL: ${dshBaseUrl}`,
|
|
1256
|
-
` models:`,
|
|
1257
|
-
...modelEntries.length > 0 ? modelEntries : [` []`]
|
|
1258
|
-
];
|
|
1259
|
-
const lines = yaml.split(/\r?\n/);
|
|
1285
|
+
} catch {
|
|
1286
|
+
}
|
|
1287
|
+
return "";
|
|
1288
|
+
}
|
|
1289
|
+
function scanA6apiBlockRange(lines) {
|
|
1260
1290
|
let inLlm = false;
|
|
1261
1291
|
let inProviders = false;
|
|
1262
1292
|
let inA6 = false;
|
|
@@ -1302,6 +1332,46 @@ async function syncToDshSettings(baseURL, modelIds) {
|
|
|
1302
1332
|
inA6 = false;
|
|
1303
1333
|
}
|
|
1304
1334
|
}
|
|
1335
|
+
return { a6Start, a6End, providersLineIdx, llmLineIdx };
|
|
1336
|
+
}
|
|
1337
|
+
async function writeRawA6apiBlock(baseURL, modelIds) {
|
|
1338
|
+
const sFile = settingsFile();
|
|
1339
|
+
let yaml = "";
|
|
1340
|
+
try {
|
|
1341
|
+
yaml = await fsp2.readFile(sFile, "utf8");
|
|
1342
|
+
} catch {
|
|
1343
|
+
yaml = "llm-pi-ai:\n providers:\n";
|
|
1344
|
+
}
|
|
1345
|
+
const modelEntries = modelIds.map((id) => {
|
|
1346
|
+
const entry = getCatalogEntry(id);
|
|
1347
|
+
const lines2 = [` - id: ${id}`];
|
|
1348
|
+
if (entry?.name) lines2.push(` name: ${JSON.stringify(String(entry.name).replace(/\r?\n/g, " "))}`);
|
|
1349
|
+
if (entry?.contextWindow != null) lines2.push(` contextWindow: ${entry.contextWindow}`);
|
|
1350
|
+
if (entry?.maxTokens != null) lines2.push(` maxTokens: ${entry.maxTokens}`);
|
|
1351
|
+
if (entry?.input && entry.input.length > 0) {
|
|
1352
|
+
lines2.push(` input:`);
|
|
1353
|
+
for (const m of entry.input) lines2.push(` - ${m}`);
|
|
1354
|
+
}
|
|
1355
|
+
if (entry?.reasoningEfforts && typeof entry.reasoningEfforts === "object") {
|
|
1356
|
+
lines2.push(` reasoningEfforts:`);
|
|
1357
|
+
for (const [k, v] of Object.entries(entry.reasoningEfforts)) {
|
|
1358
|
+
lines2.push(v ? ` ${k}: ${v}` : ` ${k}: `);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
return lines2.join("\n");
|
|
1362
|
+
});
|
|
1363
|
+
const dshBaseUrl = baseURL.endsWith("/v1") ? baseURL : `${baseURL.replace(/\/+$/, "")}/v1`;
|
|
1364
|
+
const a6apiBlockLines = [
|
|
1365
|
+
` a6api:`,
|
|
1366
|
+
` displayName: A6API`,
|
|
1367
|
+
` apiKeyEnv: ${A6API_CRED_REF}`,
|
|
1368
|
+
` api: openai-completions`,
|
|
1369
|
+
` baseURL: ${dshBaseUrl}`,
|
|
1370
|
+
` models:`,
|
|
1371
|
+
...modelEntries
|
|
1372
|
+
];
|
|
1373
|
+
const lines = yaml.split(/\r?\n/);
|
|
1374
|
+
const { a6Start, a6End, providersLineIdx, llmLineIdx } = scanA6apiBlockRange(lines);
|
|
1305
1375
|
if (a6Start >= 0) {
|
|
1306
1376
|
lines.splice(a6Start, a6End - a6Start, ...a6apiBlockLines);
|
|
1307
1377
|
} else if (providersLineIdx >= 0) {
|
|
@@ -1313,38 +1383,305 @@ async function syncToDshSettings(baseURL, modelIds) {
|
|
|
1313
1383
|
}
|
|
1314
1384
|
await atomicWriteFile(sFile, lines.join("\n"), 420);
|
|
1315
1385
|
}
|
|
1316
|
-
async function
|
|
1386
|
+
async function removeRawA6apiBlock() {
|
|
1387
|
+
const sFile = settingsFile();
|
|
1388
|
+
let yaml = "";
|
|
1317
1389
|
try {
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1390
|
+
yaml = await fsp2.readFile(sFile, "utf8");
|
|
1391
|
+
} catch {
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
const lines = yaml.split(/\r?\n/);
|
|
1395
|
+
const { a6Start, a6End, providersLineIdx, llmLineIdx } = scanA6apiBlockRange(lines);
|
|
1396
|
+
if (a6Start < 0) return;
|
|
1397
|
+
lines.splice(a6Start, a6End - a6Start);
|
|
1398
|
+
if (providersLineIdx >= 0) {
|
|
1399
|
+
let hasProvider = false;
|
|
1400
|
+
for (let i = providersLineIdx + 1; i < lines.length; i++) {
|
|
1401
|
+
const trimmed = lines[i].trim();
|
|
1402
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1403
|
+
const indent = lines[i].match(/^\s*/)?.[0].length ?? 0;
|
|
1404
|
+
if (indent <= 2) break;
|
|
1405
|
+
if (indent === 4) {
|
|
1406
|
+
hasProvider = true;
|
|
1407
|
+
break;
|
|
1330
1408
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1409
|
+
}
|
|
1410
|
+
if (!hasProvider) lines.splice(providersLineIdx, 1);
|
|
1411
|
+
}
|
|
1412
|
+
if (llmLineIdx >= 0) {
|
|
1413
|
+
let hasLlmKey = false;
|
|
1414
|
+
for (let i = llmLineIdx + 1; i < lines.length; i++) {
|
|
1415
|
+
const trimmed = lines[i].trim();
|
|
1416
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1417
|
+
const indent = lines[i].match(/^\s*/)?.[0].length ?? 0;
|
|
1418
|
+
if (indent === 0) break;
|
|
1419
|
+
if (indent === 2) {
|
|
1420
|
+
hasLlmKey = true;
|
|
1421
|
+
break;
|
|
1334
1422
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1423
|
+
}
|
|
1424
|
+
if (!hasLlmKey) lines.splice(llmLineIdx, 1);
|
|
1425
|
+
}
|
|
1426
|
+
await atomicWriteFile(sFile, lines.join("\n"), 420);
|
|
1427
|
+
}
|
|
1428
|
+
function normalizeLegacy(parsed) {
|
|
1429
|
+
return {
|
|
1430
|
+
apiKey: typeof parsed?.apiKey === "string" ? parsed.apiKey : "",
|
|
1431
|
+
accessToken: String(parsed?.accessToken || parsed?.systemAccessToken || parsed?.sessionCookie || ""),
|
|
1432
|
+
userId: String(parsed?.userId || ""),
|
|
1433
|
+
baseURL: typeof parsed?.baseURL === "string" && parsed.baseURL ? parsed.baseURL : DEFAULT_BASE_URL,
|
|
1434
|
+
activeModels: Array.isArray(parsed?.activeModels) ? parsed.activeModels.filter((m) => typeof m === "string") : []
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
async function readLegacyConfig() {
|
|
1438
|
+
try {
|
|
1439
|
+
const raw = await fsp2.readFile(legacyConfigFile(), "utf8");
|
|
1440
|
+
const legacy = normalizeLegacy(JSON.parse(raw));
|
|
1441
|
+
return {
|
|
1442
|
+
baseURL: legacy.baseURL,
|
|
1443
|
+
apiKey: legacy.apiKey,
|
|
1444
|
+
accessToken: legacy.accessToken || void 0,
|
|
1445
|
+
userId: legacy.userId || void 0,
|
|
1446
|
+
activeModels: legacy.activeModels
|
|
1447
|
+
};
|
|
1448
|
+
} catch {
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
function buildA6apiBlock(baseURL, modelIds) {
|
|
1453
|
+
const models = modelIds.map((id) => {
|
|
1454
|
+
const entry = getCatalogEntry(id);
|
|
1455
|
+
const m = { id };
|
|
1456
|
+
if (entry?.name) m.name = entry.name;
|
|
1457
|
+
if (entry?.contextWindow != null) m.contextWindow = entry.contextWindow;
|
|
1458
|
+
if (entry?.maxTokens != null) m.maxTokens = entry.maxTokens;
|
|
1459
|
+
if (entry?.input && entry.input.length > 0) m.input = [...entry.input];
|
|
1460
|
+
if (entry?.reasoningEfforts !== void 0 && entry.reasoningEfforts !== null) {
|
|
1461
|
+
m.reasoningEfforts = entry.reasoningEfforts;
|
|
1462
|
+
}
|
|
1463
|
+
return m;
|
|
1464
|
+
});
|
|
1465
|
+
return {
|
|
1466
|
+
displayName: "A6API",
|
|
1467
|
+
apiKeyEnv: A6API_CRED_REF,
|
|
1468
|
+
api: "openai-completions",
|
|
1469
|
+
baseURL: baseURL.endsWith("/v1") ? baseURL : `${baseURL.replace(/\/+$/, "")}/v1`,
|
|
1470
|
+
models
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
function createConfigAccess(ctx) {
|
|
1474
|
+
let migration = null;
|
|
1475
|
+
const ensureMigrated = () => {
|
|
1476
|
+
if (!migration) {
|
|
1477
|
+
migration = doMigrate().catch((err) => {
|
|
1478
|
+
console.warn("[dsh-a6api] \u65E7\u914D\u7F6E\u8FC1\u79FB\u5931\u8D25\uFF08\u4FDD\u7559\u65E7\u6587\u4EF6\u8BFB\u53D6\u515C\u5E95\uFF09:", err?.message || err);
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
return migration;
|
|
1482
|
+
};
|
|
1483
|
+
const resolveRef = async (creds, ref) => {
|
|
1484
|
+
try {
|
|
1485
|
+
if (creds && typeof creds.resolve === "function") {
|
|
1486
|
+
const r = await creds.resolve(ref);
|
|
1487
|
+
return r && typeof r.value === "string" ? r.value : "";
|
|
1338
1488
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1489
|
+
} catch (err) {
|
|
1490
|
+
console.warn(`[dsh-a6api] credentials.resolve(${ref}) failed:`, err?.message || err);
|
|
1491
|
+
}
|
|
1492
|
+
return await readCredentialKey(ref) || "";
|
|
1493
|
+
};
|
|
1494
|
+
const readA6apiBlock = async (settings) => {
|
|
1495
|
+
try {
|
|
1496
|
+
if (settings && typeof settings.get === "function") {
|
|
1497
|
+
const llm = settings.get(SETTINGS_NS);
|
|
1498
|
+
const block = llm && llm.providers ? llm.providers[PROVIDER_KEY] : void 0;
|
|
1499
|
+
if (block && typeof block === "object") {
|
|
1500
|
+
return {
|
|
1501
|
+
baseURL: typeof block.baseURL === "string" ? block.baseURL : void 0,
|
|
1502
|
+
models: Array.isArray(block.models) ? block.models.map((m) => typeof m === "string" ? m : m && typeof m.id === "string" ? m.id : "").filter(Boolean) : []
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1342
1505
|
}
|
|
1506
|
+
} catch (err) {
|
|
1507
|
+
console.warn("[dsh-a6api] settings.get(llm-pi-ai) failed:", err?.message || err);
|
|
1343
1508
|
}
|
|
1344
|
-
return
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1509
|
+
return null;
|
|
1510
|
+
};
|
|
1511
|
+
const readConfig = async () => {
|
|
1512
|
+
await ensureMigrated();
|
|
1513
|
+
const creds = getCredentials(ctx);
|
|
1514
|
+
const settings = getSettings(ctx);
|
|
1515
|
+
const apiKey = await resolveRef(creds, A6API_CRED_REF);
|
|
1516
|
+
const accessToken = await resolveRef(creds, A6API_TOKEN_REF);
|
|
1517
|
+
const userId = await resolveRef(creds, A6API_USER_REF);
|
|
1518
|
+
let baseURL = DEFAULT_BASE_URL;
|
|
1519
|
+
let activeModels = [];
|
|
1520
|
+
const block = await readA6apiBlock(settings);
|
|
1521
|
+
if (block) {
|
|
1522
|
+
if (block.baseURL) baseURL = stripV1(block.baseURL) || DEFAULT_BASE_URL;
|
|
1523
|
+
activeModels = block.models;
|
|
1524
|
+
} else {
|
|
1525
|
+
const rawBase = await readRawA6apiBaseURL();
|
|
1526
|
+
if (rawBase) baseURL = stripV1(rawBase) || DEFAULT_BASE_URL;
|
|
1527
|
+
activeModels = await readRawConfiguredModels();
|
|
1528
|
+
}
|
|
1529
|
+
if (!apiKey && !accessToken && fs2.existsSync(legacyConfigFile())) {
|
|
1530
|
+
const legacy = await readLegacyConfig();
|
|
1531
|
+
if (legacy) {
|
|
1532
|
+
return {
|
|
1533
|
+
baseURL: baseURL || legacy.baseURL,
|
|
1534
|
+
apiKey: apiKey || legacy.apiKey,
|
|
1535
|
+
accessToken: accessToken || legacy.accessToken,
|
|
1536
|
+
userId: userId || legacy.userId,
|
|
1537
|
+
activeModels: activeModels.length > 0 ? activeModels : legacy.activeModels
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
return { baseURL, apiKey, accessToken, userId, activeModels };
|
|
1542
|
+
};
|
|
1543
|
+
const writeConfig = async (parts) => {
|
|
1544
|
+
await ensureMigrated();
|
|
1545
|
+
const creds = getCredentials(ctx);
|
|
1546
|
+
const entries = [
|
|
1547
|
+
[A6API_CRED_REF, parts.apiKey],
|
|
1548
|
+
[A6API_TOKEN_REF, parts.accessToken],
|
|
1549
|
+
[A6API_USER_REF, parts.userId]
|
|
1550
|
+
];
|
|
1551
|
+
for (const [ref, value] of entries) {
|
|
1552
|
+
if (value === void 0) continue;
|
|
1553
|
+
const v = value.trim();
|
|
1554
|
+
try {
|
|
1555
|
+
if (creds && typeof creds.set === "function" && typeof creds.unset === "function") {
|
|
1556
|
+
if (v) await creds.set(ref, v);
|
|
1557
|
+
else await creds.unset(ref);
|
|
1558
|
+
} else {
|
|
1559
|
+
await writeCredentialKey(ref, v);
|
|
1560
|
+
}
|
|
1561
|
+
} catch (err) {
|
|
1562
|
+
console.warn(`[dsh-a6api] \u5199\u5165\u51ED\u636E ${ref} \u5931\u8D25\uFF08\u5DF2\u8DF3\u8FC7\uFF09:`, err?.message || err);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
const syncModels = async (baseURL, modelIds) => {
|
|
1567
|
+
const settings = getSettings(ctx);
|
|
1568
|
+
if (modelIds.length === 0) {
|
|
1569
|
+
if (settings && typeof settings.mutate === "function") {
|
|
1570
|
+
try {
|
|
1571
|
+
await settings.mutate(SETTINGS_NS, [{ op: "unset", path: ["providers", PROVIDER_KEY] }]);
|
|
1572
|
+
return;
|
|
1573
|
+
} catch (err) {
|
|
1574
|
+
console.warn("[dsh-a6api] settings.mutate(llm-pi-ai) \u79FB\u9664 a6api \u5757\u5931\u8D25\uFF0C\u56DE\u9000\u88F8\u5199 settings.yaml:", err?.message || err);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
await removeRawA6apiBlock();
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
const block = buildA6apiBlock(baseURL, modelIds);
|
|
1581
|
+
if (settings && typeof settings.update === "function") {
|
|
1582
|
+
try {
|
|
1583
|
+
await settings.update(SETTINGS_NS, { providers: { [PROVIDER_KEY]: block } });
|
|
1584
|
+
return;
|
|
1585
|
+
} catch (err) {
|
|
1586
|
+
console.warn("[dsh-a6api] settings.update(llm-pi-ai) \u5931\u8D25\uFF0C\u56DE\u9000\u88F8\u5199 settings.yaml:", err?.message || err);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
await writeRawA6apiBlock(baseURL, modelIds);
|
|
1590
|
+
};
|
|
1591
|
+
const getDshConfiguredModels = async () => {
|
|
1592
|
+
const block = await readA6apiBlock(getSettings(ctx));
|
|
1593
|
+
if (block) return block.models;
|
|
1594
|
+
return readRawConfiguredModels();
|
|
1595
|
+
};
|
|
1596
|
+
const fillRef = async (creds, ref, value) => {
|
|
1597
|
+
if (!value) return true;
|
|
1598
|
+
try {
|
|
1599
|
+
const current = await resolveRef(creds, ref);
|
|
1600
|
+
if (current) return true;
|
|
1601
|
+
if (creds && typeof creds.set === "function") await creds.set(ref, value);
|
|
1602
|
+
else await writeCredentialKey(ref, value);
|
|
1603
|
+
return true;
|
|
1604
|
+
} catch (err) {
|
|
1605
|
+
console.warn(`[dsh-a6api] \u8FC1\u79FB ${ref} \u5931\u8D25\uFF08\u8DF3\u8FC7\uFF0C\u4FDD\u7559\u65E7\u6587\u4EF6\uFF09:`, err?.message || err);
|
|
1606
|
+
return false;
|
|
1607
|
+
}
|
|
1608
|
+
};
|
|
1609
|
+
const archiveLegacy = async (filePath) => {
|
|
1610
|
+
try {
|
|
1611
|
+
await fsp2.rename(filePath, `${filePath}.bak`);
|
|
1612
|
+
console.log("[dsh-a6api] \u65E7\u914D\u7F6E\u5DF2\u8FC1\u79FB\u81F3 DSH \u539F\u751F\u914D\u7F6E\u5E76\u5F52\u6863: dsh-a6api-config.json.bak");
|
|
1613
|
+
} catch (err) {
|
|
1614
|
+
console.warn("[dsh-a6api] \u65E7\u914D\u7F6E\u5F52\u6863\u5931\u8D25\uFF08\u8FC1\u79FB\u503C\u5DF2\u5199\u5165\uFF0C\u65E7\u6587\u4EF6\u4FDD\u7559\uFF0C\u4E0B\u6B21\u542F\u52A8\u91CD\u8BD5\uFF09:", err?.message || err);
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
const doMigrate = async () => {
|
|
1618
|
+
const filePath = legacyConfigFile();
|
|
1619
|
+
let raw = "";
|
|
1620
|
+
try {
|
|
1621
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
1622
|
+
} catch {
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
let parsed;
|
|
1626
|
+
try {
|
|
1627
|
+
parsed = JSON.parse(raw);
|
|
1628
|
+
} catch (err) {
|
|
1629
|
+
console.warn("[dsh-a6api] \u65E7\u914D\u7F6E\u6587\u4EF6\u635F\u574F\uFF0C\u8DF3\u8FC7\u8FC1\u79FB\u5E76\u5F52\u6863:", err?.message || err);
|
|
1630
|
+
await archiveLegacy(filePath);
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
const legacy = normalizeLegacy(parsed);
|
|
1634
|
+
const creds = getCredentials(ctx);
|
|
1635
|
+
const fillResults = [
|
|
1636
|
+
await fillRef(creds, A6API_CRED_REF, legacy.apiKey),
|
|
1637
|
+
await fillRef(creds, A6API_TOKEN_REF, legacy.accessToken),
|
|
1638
|
+
await fillRef(creds, A6API_USER_REF, legacy.userId)
|
|
1639
|
+
];
|
|
1640
|
+
if (fillResults.some((ok) => !ok)) {
|
|
1641
|
+
console.warn("[dsh-a6api] \u51ED\u636E\u8FC1\u79FB\u672A\u5168\u90E8\u6210\u529F\uFF0C\u8DF3\u8FC7\u5F52\u6863\uFF0C\u4FDD\u7559\u65E7\u6587\u4EF6\u8BFB\u53D6\u515C\u5E95\uFF08\u4E0B\u6B21\u542F\u52A8\u91CD\u8BD5\uFF09");
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
const block = await readA6apiBlock(getSettings(ctx));
|
|
1645
|
+
const blockExists = Boolean(block) || (await readRawConfiguredModels()).length > 0;
|
|
1646
|
+
if (!blockExists && legacy.activeModels.length > 0) {
|
|
1647
|
+
await syncModels(legacy.baseURL, legacy.activeModels);
|
|
1648
|
+
}
|
|
1649
|
+
await archiveLegacy(filePath);
|
|
1650
|
+
};
|
|
1651
|
+
return { ensureMigrated, readConfig, writeConfig, syncModels, getDshConfiguredModels };
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
// src/types.ts
|
|
1655
|
+
var THINKING_LEVEL_KEYS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
1656
|
+
function validateReasoningEfforts(value) {
|
|
1657
|
+
if (value === false) return { ok: true, value: false };
|
|
1658
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1659
|
+
return { ok: false, error: "reasoningEfforts \u5FC5\u987B\u662F\u6863\u4F4D\u5B57\u5178\u6216 false" };
|
|
1660
|
+
}
|
|
1661
|
+
const dict = value;
|
|
1662
|
+
const keys = Object.keys(dict);
|
|
1663
|
+
if (keys.length === 0) return { ok: false, error: "reasoningEfforts \u4E0D\u80FD\u4E3A\u7A7A\u5B57\u5178" };
|
|
1664
|
+
const out = {};
|
|
1665
|
+
for (const k of keys) {
|
|
1666
|
+
if (!THINKING_LEVEL_KEYS.includes(k)) {
|
|
1667
|
+
return { ok: false, error: `reasoningEfforts \u952E "${k}" \u4E0D\u662F DSH \u652F\u6301\u7684\u6863\u4F4D\uFF08off/minimal/low/medium/high/xhigh/max\uFF09` };
|
|
1668
|
+
}
|
|
1669
|
+
const v = dict[k];
|
|
1670
|
+
if (v === null) {
|
|
1671
|
+
if (k !== "off") {
|
|
1672
|
+
return { ok: false, error: `reasoningEfforts.${k} \u5FC5\u987B\u63D0\u4F9B wire \u503C\uFF0C\u4EC5 off \u5141\u8BB8\u7559\u7A7A` };
|
|
1673
|
+
}
|
|
1674
|
+
out[k] = null;
|
|
1675
|
+
} else if (typeof v === "string" && v.length > 0) {
|
|
1676
|
+
out[k] = v;
|
|
1677
|
+
} else {
|
|
1678
|
+
return { ok: false, error: `reasoningEfforts.${k} \u7684\u503C\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6216 null` };
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
if (!keys.some((k) => k !== "off")) {
|
|
1682
|
+
return { ok: false, error: "reasoningEfforts \u9664 off \u5916\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u601D\u8003\u6863\u4F4D\uFF08\u975E\u63A8\u7406\u6A21\u578B\u8BF7\u7528 false\uFF09" };
|
|
1347
1683
|
}
|
|
1684
|
+
return { ok: true, value: out };
|
|
1348
1685
|
}
|
|
1349
1686
|
|
|
1350
1687
|
// src/index.ts
|
|
@@ -1356,11 +1693,10 @@ function maskConfig(c) {
|
|
|
1356
1693
|
return {
|
|
1357
1694
|
...c,
|
|
1358
1695
|
apiKey: c.apiKey ? MASK : "",
|
|
1359
|
-
accessToken: c.accessToken
|
|
1360
|
-
sessionCookie: "",
|
|
1696
|
+
accessToken: c.accessToken ? MASK : "",
|
|
1361
1697
|
userId: c.userId ? MASK : "",
|
|
1362
1698
|
hasApiKey: Boolean(c.apiKey),
|
|
1363
|
-
hasToken: Boolean(c.accessToken
|
|
1699
|
+
hasToken: Boolean(c.accessToken)
|
|
1364
1700
|
};
|
|
1365
1701
|
}
|
|
1366
1702
|
function sendJson(res, status, body) {
|
|
@@ -1372,7 +1708,7 @@ function sendJson(res, status, body) {
|
|
|
1372
1708
|
res.end(JSON.stringify(body));
|
|
1373
1709
|
}
|
|
1374
1710
|
function readBody(req) {
|
|
1375
|
-
return new Promise((
|
|
1711
|
+
return new Promise((resolve2, reject) => {
|
|
1376
1712
|
let data = "";
|
|
1377
1713
|
req.on("data", (chunk) => {
|
|
1378
1714
|
data += chunk;
|
|
@@ -1381,7 +1717,7 @@ function readBody(req) {
|
|
|
1381
1717
|
reject(new Error("Body too large"));
|
|
1382
1718
|
}
|
|
1383
1719
|
});
|
|
1384
|
-
req.on("end", () =>
|
|
1720
|
+
req.on("end", () => resolve2(data.trim()));
|
|
1385
1721
|
req.on("error", reject);
|
|
1386
1722
|
});
|
|
1387
1723
|
}
|
|
@@ -1396,7 +1732,68 @@ async function parseJsonBody(req) {
|
|
|
1396
1732
|
}
|
|
1397
1733
|
var merchantCardCache = /* @__PURE__ */ new Map();
|
|
1398
1734
|
var MERCHANT_CARD_TTL_MS = 15 * 60 * 1e3;
|
|
1735
|
+
var tokenResolveCache = null;
|
|
1736
|
+
var TOKEN_RESOLVE_TTL_MS = 10 * 60 * 1e3;
|
|
1737
|
+
async function resolveTokenId(config) {
|
|
1738
|
+
if (tokenResolveCache && Date.now() - tokenResolveCache.at < TOKEN_RESOLVE_TTL_MS) {
|
|
1739
|
+
return tokenResolveCache.tokenId;
|
|
1740
|
+
}
|
|
1741
|
+
const token = config.accessToken || "";
|
|
1742
|
+
if (!config.userId || !token) return null;
|
|
1743
|
+
try {
|
|
1744
|
+
const tokens = await fetchTokens(config.userId, token);
|
|
1745
|
+
let tokenId = null;
|
|
1746
|
+
const key = (config.apiKey || "").trim();
|
|
1747
|
+
if (key) {
|
|
1748
|
+
const hit = tokens.find((t) => t.key && t.key === key);
|
|
1749
|
+
if (hit) tokenId = hit.id;
|
|
1750
|
+
}
|
|
1751
|
+
if (!tokenId && tokens.length === 1) tokenId = tokens[0].id;
|
|
1752
|
+
if (tokenId && tokenId > 0) {
|
|
1753
|
+
tokenResolveCache = { tokenId, at: Date.now() };
|
|
1754
|
+
return tokenId;
|
|
1755
|
+
}
|
|
1756
|
+
} catch (err) {
|
|
1757
|
+
console.warn("[dsh-a6api] resolveTokenId error:", err);
|
|
1758
|
+
}
|
|
1759
|
+
return null;
|
|
1760
|
+
}
|
|
1761
|
+
function webAuthOf(config) {
|
|
1762
|
+
return { userId: config.userId || void 0, token: config.accessToken || void 0 };
|
|
1763
|
+
}
|
|
1764
|
+
function cachedMerchantOf(modelName) {
|
|
1765
|
+
const entry = merchantCardCache.get(modelName.toLowerCase());
|
|
1766
|
+
return entry && Date.now() - entry.at < MERCHANT_CARD_TTL_MS ? entry.card : void 0;
|
|
1767
|
+
}
|
|
1768
|
+
function overlayPinsOnModels(models, pins, tokenId) {
|
|
1769
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
1770
|
+
for (const p of pins) {
|
|
1771
|
+
const key = (p.model_name || "").toLowerCase();
|
|
1772
|
+
if (!key) continue;
|
|
1773
|
+
const list = byModel.get(key);
|
|
1774
|
+
if (list) list.push(p);
|
|
1775
|
+
else byModel.set(key, [p]);
|
|
1776
|
+
}
|
|
1777
|
+
return models.map((m) => {
|
|
1778
|
+
const list = byModel.get(m.model_name.toLowerCase());
|
|
1779
|
+
if (!list || list.length === 0) return m;
|
|
1780
|
+
const pick = (tokenId ? list.find((p) => Number(p.token_id) === tokenId) : void 0) || list[0];
|
|
1781
|
+
const cardChannel = m.merchant?.channel_id;
|
|
1782
|
+
const pinChannel = pick.channel_id;
|
|
1783
|
+
return {
|
|
1784
|
+
...m,
|
|
1785
|
+
// Number() 归一化:官方接口可能返回字符串渠道 ID,严格相等会误判
|
|
1786
|
+
pinStatus: pinChannel ? cardChannel && Number(cardChannel) === Number(pinChannel) ? "pin_here" : "pin_elsewhere" : void 0,
|
|
1787
|
+
pinnedChannelId: pinChannel,
|
|
1788
|
+
pinnedSupplierName: pick.supplier_nickname || pick.supplier_name,
|
|
1789
|
+
pinnedFallback: pick.fallback_to_smart_routing,
|
|
1790
|
+
pinTokenMatched: Boolean(tokenId && Number(pick.token_id) === tokenId)
|
|
1791
|
+
};
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1399
1794
|
function apply(ctx) {
|
|
1795
|
+
const configAccess = createConfigAccess(ctx);
|
|
1796
|
+
void configAccess.ensureMigrated();
|
|
1400
1797
|
const webServer = ctx.webServer || (ctx.get ? ctx.get("webServer") : null);
|
|
1401
1798
|
if (webServer && typeof webServer.register === "function") {
|
|
1402
1799
|
ctx.effect(() => {
|
|
@@ -1410,24 +1807,29 @@ function apply(ctx) {
|
|
|
1410
1807
|
res.writeHead(204);
|
|
1411
1808
|
return res.end();
|
|
1412
1809
|
}
|
|
1810
|
+
if (req.method === "POST" && !String(req.headers["content-type"] || "").toLowerCase().includes("application/json")) {
|
|
1811
|
+
return sendJson(res, 415, { ok: false, error: "Content-Type must be application/json" });
|
|
1812
|
+
}
|
|
1413
1813
|
try {
|
|
1414
1814
|
if (pathname === "/state" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1415
|
-
const config = await
|
|
1416
|
-
const token = config.accessToken ||
|
|
1417
|
-
const balance
|
|
1815
|
+
const config = await configAccess.readConfig();
|
|
1816
|
+
const token = config.accessToken || "";
|
|
1817
|
+
const [balance, dshConfiguredModels, modelIdsRaw, allLogs, pins] = await Promise.all([
|
|
1818
|
+
fetchBalance(config.baseURL, config.apiKey, config.userId, token),
|
|
1819
|
+
configAccess.getDshConfiguredModels(),
|
|
1820
|
+
config.apiKey ? fetchTokenModels(config.baseURL, config.apiKey) : Promise.resolve([]),
|
|
1821
|
+
fetchRecentLogs(config.userId, token, 100),
|
|
1822
|
+
config.userId && token ? fetchMarketplacePins(config.userId, token).catch(() => []) : Promise.resolve([])
|
|
1823
|
+
]);
|
|
1418
1824
|
if (balance?.userId && String(balance.userId) !== config.userId) {
|
|
1825
|
+
tokenResolveCache = null;
|
|
1419
1826
|
config.userId = String(balance.userId);
|
|
1420
|
-
await
|
|
1421
|
-
}
|
|
1422
|
-
const dshConfiguredModels = await getDshConfiguredModels();
|
|
1423
|
-
let modelIds = [];
|
|
1424
|
-
if (config.apiKey) {
|
|
1425
|
-
modelIds = await fetchTokenModels(config.baseURL, config.apiKey);
|
|
1827
|
+
await configAccess.writeConfig({ userId: config.userId });
|
|
1426
1828
|
}
|
|
1829
|
+
let modelIds = modelIdsRaw;
|
|
1427
1830
|
if (modelIds.length === 0) {
|
|
1428
1831
|
modelIds = [
|
|
1429
1832
|
.../* @__PURE__ */ new Set([
|
|
1430
|
-
...config.activeModels,
|
|
1431
1833
|
...dshConfiguredModels,
|
|
1432
1834
|
"gpt-5.6-sol",
|
|
1433
1835
|
"gpt-5.6-terra",
|
|
@@ -1438,7 +1840,6 @@ function apply(ctx) {
|
|
|
1438
1840
|
])
|
|
1439
1841
|
];
|
|
1440
1842
|
}
|
|
1441
|
-
const allLogs = await fetchRecentLogs(config.userId, token, 100);
|
|
1442
1843
|
allLogs.sort((a, b) => (Number(b.created_at) || 0) - (Number(a.created_at) || 0));
|
|
1443
1844
|
if (config.userId || token) {
|
|
1444
1845
|
const missing = modelIds.filter((m) => {
|
|
@@ -1450,7 +1851,7 @@ function apply(ctx) {
|
|
|
1450
1851
|
try {
|
|
1451
1852
|
found = await Promise.race([
|
|
1452
1853
|
getKnownMerchantsFromLogs(config.userId, token, missing, allLogs),
|
|
1453
|
-
new Promise((
|
|
1854
|
+
new Promise((resolve2) => setTimeout(() => resolve2({}), 1e4))
|
|
1454
1855
|
]);
|
|
1455
1856
|
} catch {
|
|
1456
1857
|
found = {};
|
|
@@ -1470,7 +1871,7 @@ function apply(ctx) {
|
|
|
1470
1871
|
}
|
|
1471
1872
|
}
|
|
1472
1873
|
const dshSet = new Set(dshConfiguredModels);
|
|
1473
|
-
|
|
1874
|
+
let models = modelIds.map((mId) => {
|
|
1474
1875
|
const meta = resolveModelMeta(mId);
|
|
1475
1876
|
const cacheEntry = merchantCardCache.get(mId.toLowerCase());
|
|
1476
1877
|
const cachedCard = cacheEntry && Date.now() - cacheEntry.at < MERCHANT_CARD_TTL_MS ? cacheEntry.card : void 0;
|
|
@@ -1489,57 +1890,120 @@ function apply(ctx) {
|
|
|
1489
1890
|
lastRoutedText: routedAt ? formatRelativeTime(routedAt) : void 0
|
|
1490
1891
|
};
|
|
1491
1892
|
});
|
|
1893
|
+
const resolvedTokenId = pins.length > 0 ? await resolveTokenId(config) : null;
|
|
1894
|
+
models = overlayPinsOnModels(models, pins, resolvedTokenId);
|
|
1895
|
+
const rePointTargets = models.filter(
|
|
1896
|
+
(m) => m.pinStatus === "pin_elsewhere" && m.pinTokenMatched === true && m.pinnedChannelId && m.pinnedChannelId > 0
|
|
1897
|
+
).map((m) => ({ modelName: m.model_name, channelId: m.pinnedChannelId }));
|
|
1898
|
+
if (rePointTargets.length > 0 && config.userId && token) {
|
|
1899
|
+
try {
|
|
1900
|
+
await Promise.race([
|
|
1901
|
+
(async () => {
|
|
1902
|
+
for (let i = 0; i < rePointTargets.length; i += 4) {
|
|
1903
|
+
const batch = rePointTargets.slice(i, i + 4);
|
|
1904
|
+
await Promise.all(
|
|
1905
|
+
batch.map(async ({ modelName, channelId }) => {
|
|
1906
|
+
try {
|
|
1907
|
+
const pinnedCard = await fetchChannelDetails(
|
|
1908
|
+
channelId,
|
|
1909
|
+
config.userId,
|
|
1910
|
+
token,
|
|
1911
|
+
modelName
|
|
1912
|
+
);
|
|
1913
|
+
if (pinnedCard && Number(pinnedCard.channel_id) === Number(channelId)) {
|
|
1914
|
+
merchantCardCache.set(modelName.toLowerCase(), { card: pinnedCard, at: Date.now() });
|
|
1915
|
+
}
|
|
1916
|
+
} catch {
|
|
1917
|
+
}
|
|
1918
|
+
})
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
})(),
|
|
1922
|
+
new Promise((resolve2) => setTimeout(() => resolve2(), 1e4))
|
|
1923
|
+
]);
|
|
1924
|
+
} catch {
|
|
1925
|
+
}
|
|
1926
|
+
models = models.map((m) => {
|
|
1927
|
+
if (m.pinStatus !== "pin_elsewhere" || m.pinTokenMatched !== true) return m;
|
|
1928
|
+
const entry = merchantCardCache.get(m.model_name.toLowerCase());
|
|
1929
|
+
const card = entry && Date.now() - entry.at < MERCHANT_CARD_TTL_MS ? entry.card : void 0;
|
|
1930
|
+
if (card && Number(card.channel_id) === Number(m.pinnedChannelId)) {
|
|
1931
|
+
const pinnedLog = allLogs.find(
|
|
1932
|
+
(l) => l.model_name?.toLowerCase() === m.model_name.toLowerCase() && Number(l.channel) === m.pinnedChannelId
|
|
1933
|
+
);
|
|
1934
|
+
const pinnedAt = pinnedLog ? Number(pinnedLog.created_at) || 0 : void 0;
|
|
1935
|
+
return {
|
|
1936
|
+
...m,
|
|
1937
|
+
merchant: card,
|
|
1938
|
+
pinStatus: "pin_here",
|
|
1939
|
+
probeStatus: "success",
|
|
1940
|
+
lastRoutedAt: pinnedAt,
|
|
1941
|
+
lastRoutedText: pinnedAt ? formatRelativeTime(pinnedAt) : void 0
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
return m;
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1492
1947
|
const recentLogs = allLogs.slice(0, 20);
|
|
1493
1948
|
const response = {
|
|
1494
1949
|
config: maskConfig(config),
|
|
1495
1950
|
balance,
|
|
1496
1951
|
models,
|
|
1497
1952
|
dshConfiguredModels,
|
|
1498
|
-
recentLogs
|
|
1953
|
+
recentLogs,
|
|
1954
|
+
pins
|
|
1499
1955
|
};
|
|
1500
1956
|
return sendJson(res, 200, { ok: true, data: response });
|
|
1501
1957
|
}
|
|
1502
1958
|
if (pathname === "/config" && req.method === "POST") {
|
|
1503
1959
|
const body = await parseJsonBody(req);
|
|
1504
|
-
const current = await
|
|
1505
|
-
const rawToken = body.accessToken !== void 0 && body.accessToken !== MASK ? body.accessToken : body.sessionCookie !== void 0 && body.sessionCookie !== MASK ? body.sessionCookie : current.accessToken
|
|
1960
|
+
const current = await configAccess.readConfig();
|
|
1961
|
+
const rawToken = body.accessToken !== void 0 && body.accessToken !== MASK ? body.accessToken : body.sessionCookie !== void 0 && body.sessionCookie !== MASK ? body.sessionCookie : current.accessToken;
|
|
1506
1962
|
const newApiKey = body.apiKey !== void 0 && body.apiKey !== MASK ? body.apiKey : current.apiKey;
|
|
1963
|
+
const credChanged = newApiKey !== current.apiKey || rawToken !== (current.accessToken || "") || body.userId !== void 0 && body.userId !== MASK && body.userId !== current.userId;
|
|
1964
|
+
if (credChanged) {
|
|
1965
|
+
tokenResolveCache = null;
|
|
1966
|
+
}
|
|
1507
1967
|
const updated = {
|
|
1508
1968
|
baseURL: body.baseURL !== void 0 ? body.baseURL : current.baseURL,
|
|
1509
1969
|
apiKey: newApiKey,
|
|
1510
1970
|
accessToken: rawToken,
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
activeModels: Array.isArray(body.activeModels) ? body.activeModels : current.activeModels,
|
|
1514
|
-
customBaseURL: body.customBaseURL !== void 0 ? body.customBaseURL : current.customBaseURL
|
|
1971
|
+
userId: body.userId !== void 0 && body.userId !== MASK ? body.userId : current.userId,
|
|
1972
|
+
activeModels: Array.isArray(body.activeModels) ? body.activeModels : current.activeModels
|
|
1515
1973
|
};
|
|
1516
1974
|
const balance = await fetchBalance(updated.baseURL, updated.apiKey, updated.userId, updated.accessToken);
|
|
1517
1975
|
if (balance?.userId) {
|
|
1518
1976
|
updated.userId = String(balance.userId);
|
|
1519
1977
|
}
|
|
1520
|
-
|
|
1978
|
+
const credWrites = {};
|
|
1979
|
+
if (updated.apiKey !== current.apiKey) credWrites.apiKey = updated.apiKey;
|
|
1980
|
+
if ((updated.accessToken || "") !== (current.accessToken || "")) credWrites.accessToken = updated.accessToken;
|
|
1981
|
+
if ((updated.userId || "") !== (current.userId || "")) credWrites.userId = updated.userId;
|
|
1982
|
+
if (Object.keys(credWrites).length > 0) {
|
|
1983
|
+
await configAccess.writeConfig(credWrites);
|
|
1984
|
+
}
|
|
1521
1985
|
if (updated.activeModels.length > 0) {
|
|
1522
|
-
await
|
|
1986
|
+
await configAccess.syncModels(updated.baseURL, updated.activeModels);
|
|
1523
1987
|
}
|
|
1524
1988
|
return sendJson(res, 200, { ok: true, config: maskConfig(updated), balance });
|
|
1525
1989
|
}
|
|
1526
1990
|
if (pathname === "/balance" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1527
|
-
const config = await
|
|
1528
|
-
const token = config.accessToken ||
|
|
1991
|
+
const config = await configAccess.readConfig();
|
|
1992
|
+
const token = config.accessToken || "";
|
|
1529
1993
|
const balance = await fetchBalance(config.baseURL, config.apiKey, config.userId, token);
|
|
1530
1994
|
const recentLogs = await fetchRecentLogs(config.userId, token, 20);
|
|
1531
1995
|
return sendJson(res, 200, { ok: true, balance, recentLogs });
|
|
1532
1996
|
}
|
|
1533
1997
|
if (pathname === "/logs" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1534
|
-
const config = await
|
|
1535
|
-
const token = config.accessToken ||
|
|
1998
|
+
const config = await configAccess.readConfig();
|
|
1999
|
+
const token = config.accessToken || "";
|
|
1536
2000
|
const recentLogs = await fetchRecentLogs(config.userId, token, 30);
|
|
1537
2001
|
return sendJson(res, 200, { ok: true, logs: recentLogs });
|
|
1538
2002
|
}
|
|
1539
2003
|
if (pathname === "/probe" && req.method === "POST") {
|
|
1540
2004
|
const body = await parseJsonBody(req);
|
|
1541
|
-
const config = await
|
|
1542
|
-
const token = config.accessToken ||
|
|
2005
|
+
const config = await configAccess.readConfig();
|
|
2006
|
+
const token = config.accessToken || "";
|
|
1543
2007
|
const modelName = body.modelName;
|
|
1544
2008
|
if (modelName && modelName !== "all") {
|
|
1545
2009
|
const result = await probeSingleModel(config.baseURL, config.apiKey, config.userId, token, modelName);
|
|
@@ -1553,7 +2017,7 @@ function apply(ctx) {
|
|
|
1553
2017
|
modelIds = await fetchTokenModels(config.baseURL, config.apiKey);
|
|
1554
2018
|
}
|
|
1555
2019
|
if (modelIds.length === 0) {
|
|
1556
|
-
modelIds =
|
|
2020
|
+
modelIds = await configAccess.getDshConfiguredModels();
|
|
1557
2021
|
}
|
|
1558
2022
|
const results = [];
|
|
1559
2023
|
for (const m of modelIds) {
|
|
@@ -1567,27 +2031,285 @@ function apply(ctx) {
|
|
|
1567
2031
|
}
|
|
1568
2032
|
if (pathname === "/sync-models" && req.method === "POST") {
|
|
1569
2033
|
const body = await parseJsonBody(req);
|
|
1570
|
-
|
|
2034
|
+
await configAccess.ensureMigrated();
|
|
2035
|
+
const config = await configAccess.readConfig();
|
|
1571
2036
|
const modelIds = Array.isArray(body.modelIds) ? body.modelIds : [];
|
|
1572
2037
|
const baseURL = body.baseURL || config.baseURL;
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
await savePluginConfig(config);
|
|
1576
|
-
await syncToDshSettings(baseURL, modelIds);
|
|
1577
|
-
const dshConfiguredModels = await getDshConfiguredModels();
|
|
2038
|
+
await configAccess.syncModels(baseURL, modelIds);
|
|
2039
|
+
const dshConfiguredModels = await configAccess.getDshConfiguredModels();
|
|
1578
2040
|
return sendJson(res, 200, { ok: true, dshConfiguredModels });
|
|
1579
2041
|
}
|
|
2042
|
+
if (pathname === "/pin" && req.method === "POST") {
|
|
2043
|
+
const body = await parseJsonBody(req);
|
|
2044
|
+
const config = await configAccess.readConfig();
|
|
2045
|
+
const modelName = String(body.modelName || "").trim();
|
|
2046
|
+
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
2047
|
+
const { userId, token } = webAuthOf(config);
|
|
2048
|
+
if (!userId || !token) {
|
|
2049
|
+
return sendJson(res, 400, { ok: false, error: "\u9700\u5728\u300C\u57FA\u7840\u914D\u7F6E\u300D\u586B\u5199\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C/\u4F1A\u8BDD\u540E\u624D\u80FD\u56FA\u5B9A\u5546\u5BB6" });
|
|
2050
|
+
}
|
|
2051
|
+
let card = cachedMerchantOf(modelName);
|
|
2052
|
+
let tokenId = await resolveTokenId(config);
|
|
2053
|
+
if ((!card || !tokenId) && config.apiKey) {
|
|
2054
|
+
try {
|
|
2055
|
+
const probe = await probeSingleModel(config.baseURL, config.apiKey, userId, token, modelName);
|
|
2056
|
+
if (!tokenId && probe.tokenId && Number(probe.tokenId) > 0) tokenId = Number(probe.tokenId);
|
|
2057
|
+
if (!card && probe.merchant) {
|
|
2058
|
+
card = probe.merchant;
|
|
2059
|
+
merchantCardCache.set(modelName.toLowerCase(), { card, at: Date.now() });
|
|
2060
|
+
}
|
|
2061
|
+
} catch {
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
if (!card) {
|
|
2065
|
+
return sendJson(res, 400, { ok: false, error: "\u8BE5\u6A21\u578B\u6682\u65E0\u5546\u5BB6\u6570\u636E\uFF0C\u8BF7\u5148\u300C\u63A2\u6D4B\u5546\u5BB6\u300D" });
|
|
2066
|
+
}
|
|
2067
|
+
if (!tokenId) {
|
|
2068
|
+
return sendJson(res, 400, { ok: false, error: "\u65E0\u6CD5\u81EA\u52A8\u89E3\u6790 API Key \u5BF9\u5E94\u7684\u4EE4\u724C ID\uFF0C\u8BF7\u68C0\u67E5\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C\u662F\u5426\u6709\u6548\uFF0C\u6216\u5230\u5B98\u7F51\u300C\u4EE4\u724C\u300D\u9875\u624B\u52A8\u56FA\u5B9A" });
|
|
2069
|
+
}
|
|
2070
|
+
if (!card.channel_id) {
|
|
2071
|
+
return sendJson(res, 400, { ok: false, error: "\u5546\u5BB6\u5361\u7247\u7F3A\u5C11\u6E20\u9053 ID\uFF0C\u8BF7\u91CD\u65B0\u63A2\u6D4B" });
|
|
2072
|
+
}
|
|
2073
|
+
const pinResult = await marketplacePin(userId, token, {
|
|
2074
|
+
token_id: tokenId,
|
|
2075
|
+
channel_id: card.channel_id,
|
|
2076
|
+
model_name: modelName,
|
|
2077
|
+
// 平台默认兜底:渠道异常时自动切换智能优选(不暴露 UI 开关)
|
|
2078
|
+
fallback_to_smart_routing: true
|
|
2079
|
+
});
|
|
2080
|
+
if (!pinResult.ok) {
|
|
2081
|
+
return sendJson(res, 400, { ok: false, error: pinResult.message || "\u56FA\u5B9A\u5931\u8D25" });
|
|
2082
|
+
}
|
|
2083
|
+
tokenResolveCache = { tokenId, at: Date.now() };
|
|
2084
|
+
merchantCardCache.set(modelName.toLowerCase(), {
|
|
2085
|
+
card: { ...card, is_pinned: true, pin_status: "pin_here" },
|
|
2086
|
+
at: Date.now()
|
|
2087
|
+
});
|
|
2088
|
+
const pinList = await fetchMarketplacePins(userId, token);
|
|
2089
|
+
return sendJson(res, 200, { ok: true, message: `\u5DF2\u56FA\u5B9A ${modelName} \u81F3\u5546\u6237 #${card.channel_id}`, pins: pinList, tokenId });
|
|
2090
|
+
}
|
|
2091
|
+
if (pathname === "/unpin" && req.method === "POST") {
|
|
2092
|
+
const body = await parseJsonBody(req);
|
|
2093
|
+
const config = await configAccess.readConfig();
|
|
2094
|
+
const modelName = String(body.modelName || "").trim();
|
|
2095
|
+
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
2096
|
+
const { userId, token } = webAuthOf(config);
|
|
2097
|
+
if (!userId || !token) {
|
|
2098
|
+
return sendJson(res, 400, { ok: false, error: "\u9700\u5728\u300C\u57FA\u7840\u914D\u7F6E\u300D\u586B\u5199\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C/\u4F1A\u8BDD\u540E\u624D\u80FD\u53D6\u6D88\u56FA\u5B9A" });
|
|
2099
|
+
}
|
|
2100
|
+
const tokenId = await resolveTokenId(config);
|
|
2101
|
+
if (!tokenId) {
|
|
2102
|
+
return sendJson(res, 400, { ok: false, error: "\u65E0\u6CD5\u89E3\u6790 API Key \u5BF9\u5E94\u7684\u4EE4\u724C ID\uFF0C\u8BF7\u68C0\u67E5\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C\u662F\u5426\u6709\u6548" });
|
|
2103
|
+
}
|
|
2104
|
+
const unpinResult = await marketplaceUnpin(userId, token, { token_id: tokenId, model_name: modelName });
|
|
2105
|
+
if (!unpinResult.ok) {
|
|
2106
|
+
return sendJson(res, 400, { ok: false, error: unpinResult.message || "\u53D6\u6D88\u56FA\u5B9A\u5931\u8D25" });
|
|
2107
|
+
}
|
|
2108
|
+
const card = cachedMerchantOf(modelName);
|
|
2109
|
+
if (card) {
|
|
2110
|
+
merchantCardCache.set(modelName.toLowerCase(), {
|
|
2111
|
+
card: { ...card, is_pinned: false, pin_status: void 0 },
|
|
2112
|
+
at: Date.now()
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
const pinList = await fetchMarketplacePins(userId, token);
|
|
2116
|
+
return sendJson(res, 200, { ok: true, message: `\u5DF2\u53D6\u6D88\u56FA\u5B9A ${modelName}`, pins: pinList, tokenId });
|
|
2117
|
+
}
|
|
2118
|
+
if (pathname === "/disable" && req.method === "POST") {
|
|
2119
|
+
const body = await parseJsonBody(req);
|
|
2120
|
+
const config = await configAccess.readConfig();
|
|
2121
|
+
const modelName = String(body.modelName || "").trim();
|
|
2122
|
+
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
2123
|
+
let card = cachedMerchantOf(modelName);
|
|
2124
|
+
if (!card && config.apiKey) {
|
|
2125
|
+
try {
|
|
2126
|
+
const probe = await probeSingleModel(config.baseURL, config.apiKey, config.userId, config.accessToken || "", modelName);
|
|
2127
|
+
if (probe.merchant) {
|
|
2128
|
+
card = probe.merchant;
|
|
2129
|
+
merchantCardCache.set(modelName.toLowerCase(), { card, at: Date.now() });
|
|
2130
|
+
}
|
|
2131
|
+
} catch {
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
if (!card || !card.channel_id) {
|
|
2135
|
+
return sendJson(res, 400, { ok: false, error: "\u8BE5\u6A21\u578B\u6682\u65E0\u5546\u5BB6\u6570\u636E\uFF0C\u8BF7\u5148\u300C\u63A2\u6D4B\u5546\u5BB6\u300D" });
|
|
2136
|
+
}
|
|
2137
|
+
const { userId, token } = webAuthOf(config);
|
|
2138
|
+
if (!userId || !token) {
|
|
2139
|
+
return sendJson(res, 400, { ok: false, error: "\u9700\u5728\u300C\u57FA\u7840\u914D\u7F6E\u300D\u586B\u5199\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C/\u4F1A\u8BDD\u540E\u624D\u80FD\u7981\u7528\u5546\u5BB6" });
|
|
2140
|
+
}
|
|
2141
|
+
const disableResult = await marketplaceDisableChannel(userId, token, card.channel_id, modelName);
|
|
2142
|
+
if (!disableResult.ok) {
|
|
2143
|
+
return sendJson(res, 400, { ok: false, error: disableResult.message || "\u7981\u7528\u5931\u8D25" });
|
|
2144
|
+
}
|
|
2145
|
+
merchantCardCache.set(modelName.toLowerCase(), {
|
|
2146
|
+
card: { ...card, user_channel_disabled: true },
|
|
2147
|
+
at: Date.now()
|
|
2148
|
+
});
|
|
2149
|
+
return sendJson(res, 200, { ok: true, message: `\u5DF2\u7981\u7528\u5546\u6237 #${card.channel_id} \u5BF9\u8BE5\u6A21\u578B\u7684\u670D\u52A1` });
|
|
2150
|
+
}
|
|
2151
|
+
if (pathname === "/restore" && req.method === "POST") {
|
|
2152
|
+
const body = await parseJsonBody(req);
|
|
2153
|
+
const config = await configAccess.readConfig();
|
|
2154
|
+
const modelName = String(body.modelName || "").trim();
|
|
2155
|
+
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
2156
|
+
let card = cachedMerchantOf(modelName);
|
|
2157
|
+
if (!card && config.apiKey) {
|
|
2158
|
+
try {
|
|
2159
|
+
const probe = await probeSingleModel(config.baseURL, config.apiKey, config.userId, config.accessToken || "", modelName);
|
|
2160
|
+
if (probe.merchant) {
|
|
2161
|
+
card = probe.merchant;
|
|
2162
|
+
merchantCardCache.set(modelName.toLowerCase(), { card, at: Date.now() });
|
|
2163
|
+
}
|
|
2164
|
+
} catch {
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
if (!card || !card.channel_id) {
|
|
2168
|
+
return sendJson(res, 400, { ok: false, error: "\u8BE5\u6A21\u578B\u6682\u65E0\u5546\u5BB6\u6570\u636E\uFF0C\u8BF7\u5148\u300C\u63A2\u6D4B\u5546\u5BB6\u300D" });
|
|
2169
|
+
}
|
|
2170
|
+
const { userId, token } = webAuthOf(config);
|
|
2171
|
+
if (!userId || !token) {
|
|
2172
|
+
return sendJson(res, 400, { ok: false, error: "\u9700\u5728\u300C\u57FA\u7840\u914D\u7F6E\u300D\u586B\u5199\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C/\u4F1A\u8BDD\u540E\u624D\u80FD\u6062\u590D\u5546\u5BB6" });
|
|
2173
|
+
}
|
|
2174
|
+
const restoreResult = await marketplaceRestoreChannel(userId, token, card.channel_id, modelName);
|
|
2175
|
+
if (!restoreResult.ok) {
|
|
2176
|
+
return sendJson(res, 400, { ok: false, error: restoreResult.message || "\u6062\u590D\u5931\u8D25" });
|
|
2177
|
+
}
|
|
2178
|
+
merchantCardCache.set(modelName.toLowerCase(), {
|
|
2179
|
+
card: { ...card, user_channel_disabled: false },
|
|
2180
|
+
at: Date.now()
|
|
2181
|
+
});
|
|
2182
|
+
return sendJson(res, 200, { ok: true, message: `\u5DF2\u6062\u590D\u5546\u6237 #${card.channel_id} \u5BF9\u8BE5\u6A21\u578B\u7684\u670D\u52A1` });
|
|
2183
|
+
}
|
|
1580
2184
|
if (pathname === "/price-fluctuation" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1581
|
-
const config = await
|
|
1582
|
-
const token = config.accessToken ||
|
|
2185
|
+
const config = await configAccess.readConfig();
|
|
2186
|
+
const token = config.accessToken || "";
|
|
1583
2187
|
if (!token || !config.userId) {
|
|
1584
2188
|
return sendJson(res, 200, { ok: true, data: { pendingCount: 0, unseenCount: 0, totalCount: 0, hasAuth: false, authError: false, updatedAt: Date.now() } });
|
|
1585
2189
|
}
|
|
1586
|
-
const result = await fetchPriceFluctuation(config.userId, token
|
|
2190
|
+
const result = await fetchPriceFluctuation(config.userId, token);
|
|
1587
2191
|
const { notices, ...counts } = result;
|
|
1588
2192
|
const hasAuth = !counts.authError;
|
|
1589
2193
|
return sendJson(res, 200, { ok: true, data: { pendingCount: counts.pendingCount, unseenCount: counts.unseenCount, totalCount: counts.totalCount, hasAuth, authError: Boolean(counts.authError), updatedAt: Date.now() } });
|
|
1590
2194
|
}
|
|
2195
|
+
if (pathname === "/pins" && (req.method === "GET" || req.method === "HEAD")) {
|
|
2196
|
+
const config = await configAccess.readConfig();
|
|
2197
|
+
const { userId, token } = webAuthOf(config);
|
|
2198
|
+
if (!userId || !token) {
|
|
2199
|
+
return sendJson(res, 200, { ok: true, pins: [] });
|
|
2200
|
+
}
|
|
2201
|
+
try {
|
|
2202
|
+
const pins = await fetchMarketplacePins(userId, token);
|
|
2203
|
+
return sendJson(res, 200, { ok: true, pins });
|
|
2204
|
+
} catch (err) {
|
|
2205
|
+
console.warn("[dsh-a6api] GET /pins error:", err);
|
|
2206
|
+
return sendJson(res, 200, { ok: true, pins: [] });
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
if (pathname === "/catalog" && (req.method === "GET" || req.method === "HEAD")) {
|
|
2210
|
+
return sendJson(res, 200, { ok: true, catalog: getCatalog() });
|
|
2211
|
+
}
|
|
2212
|
+
if (pathname === "/catalog/clear" && req.method === "POST") {
|
|
2213
|
+
await clearCatalog();
|
|
2214
|
+
return sendJson(res, 200, { ok: true });
|
|
2215
|
+
}
|
|
2216
|
+
if (pathname === "/catalog/fetch-models" && req.method === "POST") {
|
|
2217
|
+
const config = await configAccess.readConfig();
|
|
2218
|
+
const { userId, token } = webAuthOf(config);
|
|
2219
|
+
let result;
|
|
2220
|
+
try {
|
|
2221
|
+
result = await fetchMarketplaceModels(userId, token);
|
|
2222
|
+
} catch (err) {
|
|
2223
|
+
return sendJson(res, 400, { ok: false, error: err?.message || "\u83B7\u53D6\u5E02\u573A\u6A21\u578B\u5931\u8D25" });
|
|
2224
|
+
}
|
|
2225
|
+
const models = result.models;
|
|
2226
|
+
const before = new Set(getCatalog().map((e) => e.id.toLowerCase()));
|
|
2227
|
+
let added = 0;
|
|
2228
|
+
for (const m of models) {
|
|
2229
|
+
if (!before.has(m.id.toLowerCase())) added++;
|
|
2230
|
+
}
|
|
2231
|
+
await upsertCatalogEntries(
|
|
2232
|
+
models.map((m) => ({ id: m.id, brand: m.brand, reasoningEfforts: m.reasoningEfforts }))
|
|
2233
|
+
);
|
|
2234
|
+
return sendJson(res, 200, {
|
|
2235
|
+
ok: true,
|
|
2236
|
+
total: models.length,
|
|
2237
|
+
added,
|
|
2238
|
+
failedPages: result.failedPages
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
if (pathname === "/catalog/query-openrouter" && req.method === "POST") {
|
|
2242
|
+
const body = await parseJsonBody(req);
|
|
2243
|
+
const catalog = getCatalog();
|
|
2244
|
+
const modelIds = Array.isArray(body.modelIds) && body.modelIds.length > 0 ? body.modelIds.map((s) => String(s)) : catalog.map((e) => e.id);
|
|
2245
|
+
if (modelIds.length === 0) {
|
|
2246
|
+
return sendJson(res, 400, { ok: false, error: "\u76EE\u5F55\u4E3A\u7A7A\uFF0C\u8BF7\u5148\u300C\u4ECE A6API \u83B7\u53D6\u5E02\u573A\u6A21\u578B\u300D" });
|
|
2247
|
+
}
|
|
2248
|
+
const result = await queryOpenRouter(modelIds);
|
|
2249
|
+
return sendJson(res, 200, {
|
|
2250
|
+
ok: true,
|
|
2251
|
+
updated: result.updated.length,
|
|
2252
|
+
notFound: result.notFound
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
if (pathname === "/catalog/update" && req.method === "POST") {
|
|
2256
|
+
const body = await parseJsonBody(req);
|
|
2257
|
+
const id = String(body.id || "").trim();
|
|
2258
|
+
if (!id) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B ID" });
|
|
2259
|
+
const patch = {};
|
|
2260
|
+
if (body.name !== void 0) {
|
|
2261
|
+
if (body.name === null) {
|
|
2262
|
+
patch.name = null;
|
|
2263
|
+
} else if (typeof body.name !== "string") {
|
|
2264
|
+
return sendJson(res, 400, { ok: false, error: "name \u5FC5\u987B\u662F\u5B57\u7B26\u4E32" });
|
|
2265
|
+
} else {
|
|
2266
|
+
const name2 = body.name.trim();
|
|
2267
|
+
patch.name = name2 || null;
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
for (const key of ["contextWindow", "maxTokens"]) {
|
|
2271
|
+
if (body[key] === null) {
|
|
2272
|
+
patch[key] = null;
|
|
2273
|
+
} else if (body[key] !== void 0) {
|
|
2274
|
+
const n = Number(body[key]);
|
|
2275
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
2276
|
+
return sendJson(res, 400, { ok: false, error: `${key} \u5FC5\u987B\u662F\u6B63\u6574\u6570` });
|
|
2277
|
+
}
|
|
2278
|
+
patch[key] = n;
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
if (body.input !== void 0) {
|
|
2282
|
+
if (body.input === null) {
|
|
2283
|
+
patch.input = null;
|
|
2284
|
+
} else if (!Array.isArray(body.input)) {
|
|
2285
|
+
return sendJson(res, 400, { ok: false, error: "input \u5FC5\u987B\u662F\u6570\u7EC4" });
|
|
2286
|
+
} else {
|
|
2287
|
+
const mods = body.input.filter((m) => m === "text" || m === "image");
|
|
2288
|
+
patch.input = mods.length > 0 ? mods : null;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
if (body.reasoningEfforts !== void 0) {
|
|
2292
|
+
if (body.reasoningEfforts === null) {
|
|
2293
|
+
patch.reasoningEfforts = null;
|
|
2294
|
+
} else {
|
|
2295
|
+
const v = validateReasoningEfforts(body.reasoningEfforts);
|
|
2296
|
+
if (!v.ok) return sendJson(res, 400, { ok: false, error: v.error });
|
|
2297
|
+
patch.reasoningEfforts = v.value;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
const entry = await updateCatalogEntry(id, patch);
|
|
2301
|
+
if (!entry) return sendJson(res, 404, { ok: false, error: "\u76EE\u5F55\u4E2D\u4E0D\u5B58\u5728\u8BE5\u6A21\u578B" });
|
|
2302
|
+
try {
|
|
2303
|
+
const config = await configAccess.readConfig();
|
|
2304
|
+
const dshModels = await configAccess.getDshConfiguredModels();
|
|
2305
|
+
if (dshModels.some((m) => m.toLowerCase() === entry.id.toLowerCase()) && config.activeModels.length > 0) {
|
|
2306
|
+
await configAccess.syncModels(config.baseURL, config.activeModels);
|
|
2307
|
+
}
|
|
2308
|
+
} catch (err) {
|
|
2309
|
+
console.warn("[dsh-a6api] catalog update: resync settings failed:", err?.message || err);
|
|
2310
|
+
}
|
|
2311
|
+
return sendJson(res, 200, { ok: true, entry });
|
|
2312
|
+
}
|
|
1591
2313
|
return sendJson(res, 404, { ok: false, error: "Not found" });
|
|
1592
2314
|
} catch (err) {
|
|
1593
2315
|
console.error("[dsh-a6api] API error:", err);
|
|
@@ -1602,19 +2324,27 @@ function apply(ctx) {
|
|
|
1602
2324
|
}
|
|
1603
2325
|
}
|
|
1604
2326
|
export {
|
|
1605
|
-
|
|
2327
|
+
A6API_CRED_REF,
|
|
2328
|
+
A6API_TOKEN_REF,
|
|
2329
|
+
A6API_USER_REF,
|
|
1606
2330
|
apply,
|
|
2331
|
+
clearCatalog,
|
|
2332
|
+
createConfigAccess,
|
|
1607
2333
|
fetchBalance,
|
|
1608
2334
|
fetchChannelDetails,
|
|
2335
|
+
fetchMarketplaceModels,
|
|
2336
|
+
fetchMarketplacePins,
|
|
1609
2337
|
fetchRecentLogs,
|
|
1610
2338
|
fetchTokenModels,
|
|
2339
|
+
getCatalog,
|
|
2340
|
+
getCatalogEntry,
|
|
1611
2341
|
getKnownMerchantsFromLogs,
|
|
2342
|
+
inferBrand,
|
|
1612
2343
|
inject,
|
|
1613
2344
|
name,
|
|
1614
2345
|
probeSingleModel,
|
|
1615
|
-
|
|
2346
|
+
queryOpenRouter,
|
|
1616
2347
|
resolveModelMeta,
|
|
1617
|
-
|
|
1618
|
-
syncToDshSettings
|
|
2348
|
+
updateCatalogEntry
|
|
1619
2349
|
};
|
|
1620
2350
|
//# sourceMappingURL=index.js.map
|