@lynn123411/dsh-a6api 1.1.0 → 1.3.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 +11 -3
- package/lib/client.js +2404 -266
- package/lib/client.js.map +4 -4
- package/lib/index.js +1345 -644
- 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 +43 -4
- package/lib/types/server/probe.d.ts +4 -2
- package/lib/types/server/sync.d.ts +28 -9
- package/lib/types/types.d.ts +75 -5
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1,457 +1,110 @@
|
|
|
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
|
-
officialPriceMicros: { input: 4e6, output: 2e7, cacheRead: 4e5, cacheWrite: 5e6 }
|
|
23
|
-
},
|
|
24
|
-
"gpt-5.6-luna": {
|
|
25
|
-
id: "gpt-5.6-luna",
|
|
26
|
-
name: "GPT-5.6 Luna",
|
|
27
|
-
brand: "OpenAI",
|
|
28
|
-
contextWindow: 524288,
|
|
29
|
-
maxTokens: 32768,
|
|
30
|
-
modalities: ["text", "image"],
|
|
31
|
-
reasoningEfforts: { off: "", low: "low", medium: "medium", high: "high" },
|
|
32
|
-
officialPriceMicros: { input: 2e6, output: 1e7, cacheRead: 2e5, cacheWrite: 25e5 }
|
|
33
|
-
},
|
|
34
|
-
"gpt-5.6": {
|
|
35
|
-
id: "gpt-5.6",
|
|
36
|
-
name: "GPT-5.6",
|
|
37
|
-
brand: "OpenAI",
|
|
38
|
-
contextWindow: 1048576,
|
|
39
|
-
maxTokens: 65536,
|
|
40
|
-
modalities: ["text", "image"],
|
|
41
|
-
reasoningEfforts: { off: "", low: "low", medium: "medium", high: "high" },
|
|
42
|
-
officialPriceMicros: { input: 4e6, output: 2e7, cacheRead: 4e5, cacheWrite: 5e6 }
|
|
43
|
-
},
|
|
44
|
-
"gpt-5.5": {
|
|
45
|
-
id: "gpt-5.5",
|
|
46
|
-
name: "GPT-5.5",
|
|
47
|
-
brand: "OpenAI",
|
|
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
|
+
import os from "node:os";
|
|
6
|
+
var CATALOG_VERSION = 1;
|
|
7
|
+
function dshHome() {
|
|
8
|
+
return process.env.DSH_HOME || path.join(os.homedir(), ".dsh");
|
|
9
|
+
}
|
|
10
|
+
function catalogFile() {
|
|
11
|
+
return path.join(dshHome(), "dsh-a6api-catalog.json");
|
|
12
|
+
}
|
|
13
|
+
var catalogCache = null;
|
|
14
|
+
function ensureLoaded() {
|
|
15
|
+
if (catalogCache) return catalogCache;
|
|
16
|
+
try {
|
|
17
|
+
const raw = fs.readFileSync(catalogFile(), "utf8");
|
|
18
|
+
const j = JSON.parse(raw);
|
|
19
|
+
catalogCache = Array.isArray(j?.entries) ? j.entries.filter((e) => e && typeof e.id === "string") : [];
|
|
20
|
+
} catch {
|
|
21
|
+
catalogCache = [];
|
|
453
22
|
}
|
|
23
|
+
const loaded = catalogCache;
|
|
24
|
+
return loaded;
|
|
25
|
+
}
|
|
26
|
+
function getCatalog() {
|
|
27
|
+
return ensureLoaded();
|
|
28
|
+
}
|
|
29
|
+
function getCatalogEntry(id) {
|
|
30
|
+
const t = id.toLowerCase();
|
|
31
|
+
return ensureLoaded().find((e) => e.id.toLowerCase() === t);
|
|
32
|
+
}
|
|
33
|
+
var writeChain = Promise.resolve();
|
|
34
|
+
function enqueueWrite(fn) {
|
|
35
|
+
const next = writeChain.then(fn, fn);
|
|
36
|
+
writeChain = next.then(
|
|
37
|
+
() => {
|
|
38
|
+
},
|
|
39
|
+
() => {
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
return next;
|
|
43
|
+
}
|
|
44
|
+
async function writeCatalog(entries) {
|
|
45
|
+
const file = catalogFile();
|
|
46
|
+
await fsp.mkdir(path.dirname(file), { recursive: true });
|
|
47
|
+
const tmp = `${file}.tmp`;
|
|
48
|
+
await fsp.writeFile(tmp, JSON.stringify({ version: CATALOG_VERSION, entries }, null, 2), {
|
|
49
|
+
encoding: "utf8",
|
|
50
|
+
mode: 420
|
|
51
|
+
});
|
|
52
|
+
await fsp.rename(tmp, file);
|
|
53
|
+
catalogCache = entries;
|
|
54
|
+
}
|
|
55
|
+
async function upsertCatalogEntries(entries) {
|
|
56
|
+
await enqueueWrite(async () => {
|
|
57
|
+
const cur = ensureLoaded();
|
|
58
|
+
const map = /* @__PURE__ */ new Map();
|
|
59
|
+
for (const e of cur) map.set(e.id.toLowerCase(), e);
|
|
60
|
+
for (const e of entries) {
|
|
61
|
+
const clean = Object.fromEntries(Object.entries(e).filter(([, v]) => v !== void 0));
|
|
62
|
+
const key = clean.id.toLowerCase();
|
|
63
|
+
const prev = map.get(key);
|
|
64
|
+
map.set(key, prev ? { ...prev, ...clean } : clean);
|
|
65
|
+
}
|
|
66
|
+
await writeCatalog([...map.values()]);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async function clearCatalog() {
|
|
70
|
+
await enqueueWrite(async () => {
|
|
71
|
+
await writeCatalog([]);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async function updateCatalogEntry(id, patch) {
|
|
75
|
+
let result = null;
|
|
76
|
+
await enqueueWrite(async () => {
|
|
77
|
+
const cur = ensureLoaded();
|
|
78
|
+
const idx = cur.findIndex((e) => e.id.toLowerCase() === id.toLowerCase());
|
|
79
|
+
if (idx < 0) return;
|
|
80
|
+
const next = { ...cur[idx], ...patch, id: cur[idx].id, updatedAt: Date.now() };
|
|
81
|
+
for (const k of Object.keys(patch)) {
|
|
82
|
+
if (patch[k] === null) delete next[k];
|
|
83
|
+
}
|
|
84
|
+
const list = cur.slice();
|
|
85
|
+
list[idx] = next;
|
|
86
|
+
await writeCatalog(list);
|
|
87
|
+
result = next;
|
|
88
|
+
});
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
var BRAND_NORMALIZE = {
|
|
92
|
+
meituan: "MeiTuan",
|
|
93
|
+
tencent: "Tencent",
|
|
94
|
+
xiaomi: "Xiaomi",
|
|
95
|
+
openai: "OpenAI",
|
|
96
|
+
anthropic: "Anthropic",
|
|
97
|
+
google: "Google",
|
|
98
|
+
deepseek: "DeepSeek",
|
|
99
|
+
zhipu: "Zhipu",
|
|
100
|
+
moonshot: "Moonshot",
|
|
101
|
+
alibaba: "Alibaba",
|
|
102
|
+
minimax: "MiniMax"
|
|
454
103
|
};
|
|
104
|
+
function normalizeBrand(raw) {
|
|
105
|
+
const k = String(raw || "").toLowerCase();
|
|
106
|
+
return BRAND_NORMALIZE[k] || raw || "Other";
|
|
107
|
+
}
|
|
455
108
|
function inferBrand(modelId) {
|
|
456
109
|
const m = modelId.toLowerCase();
|
|
457
110
|
if (m.startsWith("gpt") || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("chatgpt")) return "OpenAI";
|
|
@@ -467,29 +120,194 @@ function inferBrand(modelId) {
|
|
|
467
120
|
if (m.startsWith("hunyuan") || m.startsWith("tencent") || m.startsWith("hy")) return "Tencent";
|
|
468
121
|
return "Other";
|
|
469
122
|
}
|
|
123
|
+
var DEFAULT_REASONING_EFFORTS = {
|
|
124
|
+
off: null,
|
|
125
|
+
minimal: "minimal",
|
|
126
|
+
low: "low",
|
|
127
|
+
medium: "medium",
|
|
128
|
+
high: "high",
|
|
129
|
+
xhigh: "xhigh",
|
|
130
|
+
max: "max"
|
|
131
|
+
};
|
|
470
132
|
function resolveModelMeta(modelId) {
|
|
471
|
-
|
|
472
|
-
|
|
133
|
+
const entry = getCatalogEntry(modelId);
|
|
134
|
+
if (entry) {
|
|
135
|
+
return {
|
|
136
|
+
id: entry.id,
|
|
137
|
+
name: entry.name || entry.id,
|
|
138
|
+
brand: entry.brand || inferBrand(entry.id),
|
|
139
|
+
contextWindow: entry.contextWindow ?? 262144,
|
|
140
|
+
maxTokens: entry.maxTokens ?? 32768,
|
|
141
|
+
modalities: entry.input && entry.input.length > 0 ? [...entry.input] : ["text"],
|
|
142
|
+
...entry.reasoningEfforts && typeof entry.reasoningEfforts === "object" && Object.keys(entry.reasoningEfforts).length > 0 ? {
|
|
143
|
+
reasoningEfforts: Object.fromEntries(
|
|
144
|
+
Object.entries(entry.reasoningEfforts).filter(([, v]) => v !== null && v !== void 0)
|
|
145
|
+
)
|
|
146
|
+
} : {}
|
|
147
|
+
};
|
|
473
148
|
}
|
|
474
149
|
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
150
|
const isVision = lowerId.includes("vision") || lowerId.includes("vl") || lowerId.includes("image");
|
|
482
151
|
const hasReasoning = lowerId.includes("think") || lowerId.includes("reason") || lowerId.includes("pro") || lowerId.includes("sol");
|
|
483
152
|
return {
|
|
484
153
|
id: modelId,
|
|
485
154
|
name: modelId,
|
|
486
|
-
brand,
|
|
155
|
+
brand: inferBrand(modelId),
|
|
487
156
|
contextWindow: 262144,
|
|
488
157
|
maxTokens: 32768,
|
|
489
158
|
modalities: isVision ? ["text", "image"] : ["text"],
|
|
490
159
|
...hasReasoning ? { thinkingFormat: "deepseek" } : {}
|
|
491
160
|
};
|
|
492
161
|
}
|
|
162
|
+
var MARKET_SEARCH = "https://a6api.com/api/marketplace/channels/search";
|
|
163
|
+
var PAGE_SIZE = 500;
|
|
164
|
+
var CONCURRENCY = 6;
|
|
165
|
+
function buildWebHeaders(userId, accessToken) {
|
|
166
|
+
const headers = {
|
|
167
|
+
Accept: "application/json",
|
|
168
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
|
|
169
|
+
};
|
|
170
|
+
const uid = userId ? String(userId).trim() : "";
|
|
171
|
+
const token = accessToken ? String(accessToken).trim() : "";
|
|
172
|
+
if (uid) headers["New-Api-User"] = uid;
|
|
173
|
+
if (token) {
|
|
174
|
+
headers["Authorization"] = token;
|
|
175
|
+
headers["Cookie"] = `session=${token}`;
|
|
176
|
+
}
|
|
177
|
+
return headers;
|
|
178
|
+
}
|
|
179
|
+
async function fetchMarketplaceModels(userId, accessToken) {
|
|
180
|
+
if (!userId && !accessToken) {
|
|
181
|
+
throw new Error("\u9700\u5148\u914D\u7F6E\u7CFB\u7EDF\u8BBF\u95EE\u4EE4\u724C\u624D\u80FD\u83B7\u53D6\u5E02\u573A\u6A21\u578B");
|
|
182
|
+
}
|
|
183
|
+
const headers = buildWebHeaders(userId, accessToken);
|
|
184
|
+
const first = await (async () => {
|
|
185
|
+
const res = await fetch(`${MARKET_SEARCH}?view=list&page=1&page_size=${PAGE_SIZE}`, {
|
|
186
|
+
headers,
|
|
187
|
+
signal: AbortSignal.timeout(15e3)
|
|
188
|
+
});
|
|
189
|
+
if (!res.ok) throw new Error(`A6API \u5E02\u573A\u63A5\u53E3 HTTP ${res.status}`);
|
|
190
|
+
return res.json();
|
|
191
|
+
})();
|
|
192
|
+
const total = Number(first?.data?.total || 0);
|
|
193
|
+
const pages = Math.max(1, Math.ceil(total / PAGE_SIZE));
|
|
194
|
+
const all = [...first?.data?.items || []];
|
|
195
|
+
let failedPages = 0;
|
|
196
|
+
let idx = 2;
|
|
197
|
+
const worker = async () => {
|
|
198
|
+
while (idx <= pages) {
|
|
199
|
+
const p = idx++;
|
|
200
|
+
try {
|
|
201
|
+
const res = await fetch(`${MARKET_SEARCH}?view=list&page=${p}&page_size=${PAGE_SIZE}`, {
|
|
202
|
+
headers,
|
|
203
|
+
signal: AbortSignal.timeout(15e3)
|
|
204
|
+
});
|
|
205
|
+
const j = await res.json();
|
|
206
|
+
all.push(...j?.data?.items || []);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
failedPages++;
|
|
209
|
+
console.warn("[dsh-a6api] fetchMarketplaceModels page", p, "failed:", err?.message || err);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
await Promise.all(Array.from({ length: CONCURRENCY }, () => worker()));
|
|
214
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
215
|
+
for (const it of all) {
|
|
216
|
+
const name2 = it?.model_name;
|
|
217
|
+
if (!name2) continue;
|
|
218
|
+
if (!byModel.has(name2)) {
|
|
219
|
+
byModel.set(name2, {
|
|
220
|
+
id: String(name2),
|
|
221
|
+
brand: normalizeBrand(it?.brand),
|
|
222
|
+
// 默认声明 DSH 全部思考档位(用户可修改;upsert 仅补缺失字段,已有自定义不受影响)
|
|
223
|
+
reasoningEfforts: { ...DEFAULT_REASONING_EFFORTS }
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const models = [...byModel.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
228
|
+
return { models, failedPages };
|
|
229
|
+
}
|
|
230
|
+
var OPENROUTER_URL = "https://openrouter.ai/api/v1/models";
|
|
231
|
+
var OR_TTL_MS = 60 * 60 * 1e3;
|
|
232
|
+
var orCache = null;
|
|
233
|
+
async function getOpenRouterModels() {
|
|
234
|
+
if (orCache && Date.now() - orCache.at < OR_TTL_MS) return orCache.models;
|
|
235
|
+
try {
|
|
236
|
+
const res = await fetch(OPENROUTER_URL, {
|
|
237
|
+
headers: { Accept: "application/json", "User-Agent": "Mozilla/5.0" },
|
|
238
|
+
signal: AbortSignal.timeout(2e4)
|
|
239
|
+
});
|
|
240
|
+
if (!res.ok) throw new Error(`OpenRouter HTTP ${res.status}`);
|
|
241
|
+
const j = await res.json();
|
|
242
|
+
if (!Array.isArray(j?.data)) throw new Error("OpenRouter \u8FD4\u56DE\u683C\u5F0F\u5F02\u5E38");
|
|
243
|
+
orCache = { at: Date.now(), models: j.data };
|
|
244
|
+
return orCache.models;
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (orCache) return orCache.models;
|
|
247
|
+
throw err;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
var tailOf = (s) => String(s).split("/").pop() || "";
|
|
251
|
+
function matchOpenRouter(models, target) {
|
|
252
|
+
const t = target.toLowerCase();
|
|
253
|
+
const tTail = tailOf(t);
|
|
254
|
+
const norm = (s) => s.toLowerCase().replace(/[\s_.]+/g, "-");
|
|
255
|
+
const byTail = models.find((m) => tailOf(String(m.id)).toLowerCase() === tTail);
|
|
256
|
+
if (byTail) return byTail;
|
|
257
|
+
const nTail = norm(tTail);
|
|
258
|
+
const byNorm = models.find((m) => norm(tailOf(String(m.id))) === nTail);
|
|
259
|
+
if (byNorm) return byNorm;
|
|
260
|
+
const stripped = tTail.replace(/-(?:\d{4}-\d{2}-\d{2}|\d{8}|-\d{2,4}|\d\.\d+)$/, "");
|
|
261
|
+
if (stripped && stripped !== tTail) {
|
|
262
|
+
const byStripped = models.find((m) => tailOf(String(m.id)).toLowerCase() === stripped);
|
|
263
|
+
if (byStripped) return byStripped;
|
|
264
|
+
}
|
|
265
|
+
const tSeg = t.split("/").pop() || t;
|
|
266
|
+
return models.find((m) => {
|
|
267
|
+
const id = String(m.id).toLowerCase();
|
|
268
|
+
if (!id.includes(t)) return false;
|
|
269
|
+
const tailSeg = id.split("/").pop() || "";
|
|
270
|
+
return tailSeg === t || tailSeg.split("-").includes(tSeg);
|
|
271
|
+
}) || null;
|
|
272
|
+
}
|
|
273
|
+
function orModalities(m) {
|
|
274
|
+
const set = /* @__PURE__ */ new Set();
|
|
275
|
+
const input = m?.architecture?.input_modalities || [];
|
|
276
|
+
for (const mod of input) {
|
|
277
|
+
const k = String(mod).toLowerCase();
|
|
278
|
+
if (k === "text") set.add("text");
|
|
279
|
+
else if (k === "image") set.add("image");
|
|
280
|
+
}
|
|
281
|
+
return [...set];
|
|
282
|
+
}
|
|
283
|
+
async function queryOpenRouter(ids) {
|
|
284
|
+
const uniq = [...new Set(ids.map((s) => String(s).trim()).filter(Boolean))];
|
|
285
|
+
if (uniq.length === 0) return { updated: [], notFound: [] };
|
|
286
|
+
const models = await getOpenRouterModels();
|
|
287
|
+
const updated = [];
|
|
288
|
+
const notFound = [];
|
|
289
|
+
for (const id of uniq) {
|
|
290
|
+
const hit = matchOpenRouter(models, id);
|
|
291
|
+
if (!hit) {
|
|
292
|
+
notFound.push(id);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const patch = {
|
|
296
|
+
updatedAt: Date.now()
|
|
297
|
+
};
|
|
298
|
+
const ctx = hit.context_length ?? hit.top_provider?.context_length;
|
|
299
|
+
if (ctx != null && Number(ctx) > 0) patch.contextWindow = Number(ctx);
|
|
300
|
+
const maxOut = hit.top_provider?.max_completion_tokens ?? hit.max_completion_tokens;
|
|
301
|
+
if (maxOut != null && Number(maxOut) > 0) patch.maxTokens = Number(maxOut);
|
|
302
|
+
const mods = orModalities(hit);
|
|
303
|
+
if (mods.length > 0) patch.input = mods;
|
|
304
|
+
updated.push({ id, ...patch });
|
|
305
|
+
}
|
|
306
|
+
if (updated.length > 0) {
|
|
307
|
+
await upsertCatalogEntries(updated);
|
|
308
|
+
}
|
|
309
|
+
return { updated, notFound };
|
|
310
|
+
}
|
|
493
311
|
|
|
494
312
|
// src/server/a6api-client.ts
|
|
495
313
|
function cleanBaseUrl(url) {
|
|
@@ -516,7 +334,7 @@ function formatCnyPrice(micros, exchangeRate = 6.7209) {
|
|
|
516
334
|
if (cny < 1) return `\xA5${cny.toFixed(4)}`;
|
|
517
335
|
return `\xA5${cny.toFixed(3)}`;
|
|
518
336
|
}
|
|
519
|
-
function
|
|
337
|
+
function buildWebHeaders2(userId, accessToken) {
|
|
520
338
|
const headers = {
|
|
521
339
|
Accept: "application/json",
|
|
522
340
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
|
|
@@ -525,8 +343,8 @@ function buildWebHeaders(userId, sessionCookie) {
|
|
|
525
343
|
if (uid) {
|
|
526
344
|
headers["New-Api-User"] = uid;
|
|
527
345
|
}
|
|
528
|
-
if (
|
|
529
|
-
const raw =
|
|
346
|
+
if (accessToken && accessToken.trim()) {
|
|
347
|
+
const raw = accessToken.trim();
|
|
530
348
|
if (raw.startsWith("session=")) {
|
|
531
349
|
headers["Cookie"] = raw;
|
|
532
350
|
} else if (raw.includes(";")) {
|
|
@@ -538,7 +356,7 @@ function buildWebHeaders(userId, sessionCookie) {
|
|
|
538
356
|
}
|
|
539
357
|
return headers;
|
|
540
358
|
}
|
|
541
|
-
async function fetchBalance(baseURL, apiKey, userId,
|
|
359
|
+
async function fetchBalance(baseURL, apiKey, userId, accessToken) {
|
|
542
360
|
const cleanUrl = cleanBaseUrl(baseURL);
|
|
543
361
|
let hasAccountAuth = false;
|
|
544
362
|
let accountBalanceUsd = 0;
|
|
@@ -548,13 +366,13 @@ async function fetchBalance(baseURL, apiKey, userId, sessionCookie) {
|
|
|
548
366
|
let responseUserId = userId;
|
|
549
367
|
let usedUsd = 0;
|
|
550
368
|
let requestCount = 0;
|
|
551
|
-
if (userId ||
|
|
369
|
+
if (userId || accessToken) {
|
|
552
370
|
const candidates = ["https://a6api.com/api/user/self", `${cleanUrl}/api/user/self`];
|
|
553
371
|
const uniqueCandidates = [...new Set(candidates)];
|
|
554
372
|
for (const url of uniqueCandidates) {
|
|
555
373
|
try {
|
|
556
374
|
const res = await fetch(url, {
|
|
557
|
-
headers:
|
|
375
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
558
376
|
signal: AbortSignal.timeout(6e3)
|
|
559
377
|
});
|
|
560
378
|
if (res.ok) {
|
|
@@ -642,11 +460,11 @@ async function fetchTokenModels(baseURL, apiKey) {
|
|
|
642
460
|
}
|
|
643
461
|
return [];
|
|
644
462
|
}
|
|
645
|
-
async function fetchRecentLogs(userId,
|
|
646
|
-
if (!userId && !
|
|
463
|
+
async function fetchRecentLogs(userId, accessToken, limit = 30) {
|
|
464
|
+
if (!userId && !accessToken) return [];
|
|
647
465
|
try {
|
|
648
466
|
const res = await fetch(`https://a6api.com/api/log/self?p=1&page_size=${limit}&type=0`, {
|
|
649
|
-
headers:
|
|
467
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
650
468
|
signal: AbortSignal.timeout(8e3)
|
|
651
469
|
});
|
|
652
470
|
if (res.ok) {
|
|
@@ -681,6 +499,7 @@ async function fetchRecentLogs(userId, sessionCookie, limit = 30) {
|
|
|
681
499
|
model_name: it.model_name || it.marketplace_model_name || "",
|
|
682
500
|
channel: channelId,
|
|
683
501
|
channel_name: it.channel_name || (channelId ? `\u5546\u6237 #${channelId}` : void 0),
|
|
502
|
+
token_id: it.token_id !== void 0 && it.token_id !== null ? Number(it.token_id) : void 0,
|
|
684
503
|
prompt_tokens: Number(it.prompt_tokens || 0),
|
|
685
504
|
completion_tokens: Number(it.completion_tokens || 0),
|
|
686
505
|
use_time: Number(it.use_time || 0),
|
|
@@ -700,7 +519,7 @@ async function fetchRecentLogs(userId, sessionCookie, limit = 30) {
|
|
|
700
519
|
}
|
|
701
520
|
return [];
|
|
702
521
|
}
|
|
703
|
-
async function fetchChannelDetails(channelId, userId,
|
|
522
|
+
async function fetchChannelDetails(channelId, userId, accessToken, targetModelName, logSnapshot) {
|
|
704
523
|
if (!channelId) return null;
|
|
705
524
|
const targetName = targetModelName || "";
|
|
706
525
|
const meta = resolveModelMeta(targetName);
|
|
@@ -708,7 +527,7 @@ async function fetchChannelDetails(channelId, userId, sessionCookie, targetModel
|
|
|
708
527
|
const res = await fetch(
|
|
709
528
|
`https://a6api.com/api/marketplace/channels/search?channel_id=${channelId}&view=list&page=1&page_size=20`,
|
|
710
529
|
{
|
|
711
|
-
headers:
|
|
530
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
712
531
|
signal: AbortSignal.timeout(8e3)
|
|
713
532
|
}
|
|
714
533
|
);
|
|
@@ -781,7 +600,8 @@ async function fetchChannelDetails(channelId, userId, sessionCookie, targetModel
|
|
|
781
600
|
const ratioFormatted = ratioCny.toFixed(4);
|
|
782
601
|
return {
|
|
783
602
|
listing_id: item.listing_id,
|
|
784
|
-
channel_id
|
|
603
|
+
// 归一化为数字,避免官方返回字符串 channel_id 导致严格相等比较失效(接管/固定判定依赖)
|
|
604
|
+
channel_id: item.channel_id !== void 0 && item.channel_id !== null ? Number(item.channel_id) : channelId,
|
|
785
605
|
channel_name: item.channel_name || `\u5546\u6237 #${channelId}`,
|
|
786
606
|
supplier_name: item.supplier_name || item.supplier_nickname || "GPT\u4F4E\u4EF7",
|
|
787
607
|
supplier_id: item.supplier_id || 290,
|
|
@@ -819,8 +639,10 @@ async function fetchChannelDetails(channelId, userId, sessionCookie, targetModel
|
|
|
819
639
|
last_success_text: formatRelativeTime(lastSuccessAt),
|
|
820
640
|
authenticity_guaranteed: Boolean(item.authenticity_guaranteed),
|
|
821
641
|
authenticity_badge: item.authenticity_guarantee_badge_key,
|
|
822
|
-
|
|
823
|
-
|
|
642
|
+
// 官方固定状态值是 pin_here / pin_elsewhere(早期代码误用 'pinned',已修正)
|
|
643
|
+
is_pinned: item.pin_status === "pin_here" || item.route_status === "pin_here",
|
|
644
|
+
pin_status: typeof item.pin_status === "string" ? item.pin_status : void 0,
|
|
645
|
+
user_channel_disabled: Boolean(item.user_channel_disabled || item.route_status === "user_disabled"),
|
|
824
646
|
supplier_channel_disabled: Boolean(item.supplier_channel_disabled),
|
|
825
647
|
raw: item
|
|
826
648
|
};
|
|
@@ -892,13 +714,13 @@ async function fetchChannelDetails(channelId, userId, sessionCookie, targetModel
|
|
|
892
714
|
}
|
|
893
715
|
return null;
|
|
894
716
|
}
|
|
895
|
-
async function fetchPriceFluctuation(userId,
|
|
896
|
-
const token = (accessToken ||
|
|
717
|
+
async function fetchPriceFluctuation(userId, accessToken) {
|
|
718
|
+
const token = (accessToken || "").trim();
|
|
897
719
|
const uid = (userId || "").trim();
|
|
898
720
|
if (!uid && !token) {
|
|
899
721
|
return { pendingCount: 0, unseenCount: 0, totalCount: 0, authError: false };
|
|
900
722
|
}
|
|
901
|
-
const headers =
|
|
723
|
+
const headers = buildWebHeaders2(uid || void 0, token || void 0);
|
|
902
724
|
const url = "https://a6api.com/api/marketplace/price-notices";
|
|
903
725
|
try {
|
|
904
726
|
const res = await fetch(url, { headers, signal: AbortSignal.timeout(8e3) });
|
|
@@ -952,6 +774,158 @@ async function fetchPriceFluctuation(userId, sessionCookie, accessToken) {
|
|
|
952
774
|
return { pendingCount: 0, unseenCount: 0, totalCount: 0 };
|
|
953
775
|
}
|
|
954
776
|
}
|
|
777
|
+
function parseMarketplaceResult(json) {
|
|
778
|
+
if (!json) return { ok: false, message: "\u7A7A\u54CD\u5E94" };
|
|
779
|
+
const top = json.success === false ? json : null;
|
|
780
|
+
const inner = json.data && json.data.success === false ? json.data : null;
|
|
781
|
+
if (top) return { ok: false, message: top.message || "\u64CD\u4F5C\u5931\u8D25" };
|
|
782
|
+
if (inner) return { ok: false, message: inner.message || "\u64CD\u4F5C\u5931\u8D25" };
|
|
783
|
+
return { ok: true, data: json.data };
|
|
784
|
+
}
|
|
785
|
+
function extractArray(json) {
|
|
786
|
+
if (!json) return [];
|
|
787
|
+
if (Array.isArray(json)) return json;
|
|
788
|
+
if (Array.isArray(json.data)) return json.data;
|
|
789
|
+
if (json.data && Array.isArray(json.data.data)) return json.data.data;
|
|
790
|
+
if (json.data && Array.isArray(json.data.items)) return json.data.items;
|
|
791
|
+
if (json.data && json.data.data && Array.isArray(json.data.data.items)) return json.data.data.items;
|
|
792
|
+
return [];
|
|
793
|
+
}
|
|
794
|
+
async function fetchMarketplacePins(userId, accessToken) {
|
|
795
|
+
if (!userId && !accessToken) return [];
|
|
796
|
+
try {
|
|
797
|
+
const res = await fetch("https://a6api.com/api/marketplace/pins", {
|
|
798
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
799
|
+
signal: AbortSignal.timeout(8e3)
|
|
800
|
+
});
|
|
801
|
+
if (!res.ok) {
|
|
802
|
+
console.warn("[dsh-a6api] fetchMarketplacePins HTTP", res.status);
|
|
803
|
+
return [];
|
|
804
|
+
}
|
|
805
|
+
const json = await res.json().catch(() => null);
|
|
806
|
+
if (!json || json.success === false) return [];
|
|
807
|
+
return extractArray(json).filter((it) => it && it.model_name).map((it) => ({
|
|
808
|
+
id: it.id !== void 0 ? Number(it.id) : void 0,
|
|
809
|
+
token_id: Number(it.token_id || 0),
|
|
810
|
+
token_name: it.token_name || void 0,
|
|
811
|
+
model_name: String(it.model_name),
|
|
812
|
+
channel_id: it.channel_id !== void 0 && Number(it.channel_id) > 0 ? Number(it.channel_id) : void 0,
|
|
813
|
+
channel_name: it.channel_name || void 0,
|
|
814
|
+
supplier_name: it.supplier_name || void 0,
|
|
815
|
+
supplier_nickname: it.supplier_nickname || void 0,
|
|
816
|
+
fallback_to_smart_routing: it.fallback_to_smart_routing !== void 0 ? Boolean(it.fallback_to_smart_routing) : void 0,
|
|
817
|
+
created_at: it.created_at !== void 0 ? Number(it.created_at) : void 0
|
|
818
|
+
// 不下发 raw:客户端 60s 轮询用 JSON 对比判断变化,raw 含易变字段会导致误判整页刷新
|
|
819
|
+
}));
|
|
820
|
+
} catch (err) {
|
|
821
|
+
console.warn("[dsh-a6api] fetchMarketplacePins error:", err);
|
|
822
|
+
return [];
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
async function fetchTokens(userId, accessToken) {
|
|
826
|
+
if (!userId && !accessToken) return [];
|
|
827
|
+
try {
|
|
828
|
+
const res = await fetch("https://a6api.com/api/token/?p=1&size=100", {
|
|
829
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
830
|
+
signal: AbortSignal.timeout(8e3)
|
|
831
|
+
});
|
|
832
|
+
if (!res.ok) return [];
|
|
833
|
+
const json = await res.json().catch(() => null);
|
|
834
|
+
if (!json || json.success === false) return [];
|
|
835
|
+
return extractArray(json).filter((it) => it && Number(it.id) > 0).map((it) => ({
|
|
836
|
+
id: Number(it.id),
|
|
837
|
+
name: it.name || void 0,
|
|
838
|
+
key: typeof it.key === "string" ? it.key : void 0,
|
|
839
|
+
status: it.status !== void 0 ? Number(it.status) : void 0,
|
|
840
|
+
raw: it
|
|
841
|
+
}));
|
|
842
|
+
} catch (err) {
|
|
843
|
+
console.warn("[dsh-a6api] fetchTokens error:", err);
|
|
844
|
+
return [];
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
function friendlyActionError(err) {
|
|
848
|
+
const raw = err?.message || String(err);
|
|
849
|
+
if (raw.includes("aborted due to timeout") || err?.name === "TimeoutError") {
|
|
850
|
+
return "\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u91CD\u8BD5";
|
|
851
|
+
}
|
|
852
|
+
return raw;
|
|
853
|
+
}
|
|
854
|
+
async function marketplacePin(userId, accessToken, payload) {
|
|
855
|
+
try {
|
|
856
|
+
const res = await fetch("https://a6api.com/api/marketplace/pin", {
|
|
857
|
+
method: "POST",
|
|
858
|
+
headers: {
|
|
859
|
+
"Content-Type": "application/json",
|
|
860
|
+
...buildWebHeaders2(userId, accessToken)
|
|
861
|
+
},
|
|
862
|
+
body: JSON.stringify(payload),
|
|
863
|
+
signal: AbortSignal.timeout(1e4)
|
|
864
|
+
});
|
|
865
|
+
const json = await res.json().catch(() => null);
|
|
866
|
+
const result = parseMarketplaceResult(json);
|
|
867
|
+
if (!res.ok && !json) return { ok: false, message: `HTTP ${res.status}` };
|
|
868
|
+
return result;
|
|
869
|
+
} catch (err) {
|
|
870
|
+
console.warn("[dsh-a6api] marketplacePin error:", err);
|
|
871
|
+
return { ok: false, message: friendlyActionError(err) };
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
async function marketplaceUnpin(userId, accessToken, payload) {
|
|
875
|
+
try {
|
|
876
|
+
const res = await fetch("https://a6api.com/api/marketplace/unpin", {
|
|
877
|
+
method: "POST",
|
|
878
|
+
headers: {
|
|
879
|
+
"Content-Type": "application/json",
|
|
880
|
+
...buildWebHeaders2(userId, accessToken)
|
|
881
|
+
},
|
|
882
|
+
body: JSON.stringify(payload),
|
|
883
|
+
signal: AbortSignal.timeout(1e4)
|
|
884
|
+
});
|
|
885
|
+
const json = await res.json().catch(() => null);
|
|
886
|
+
if (!res.ok && !json) return { ok: false, message: `HTTP ${res.status}` };
|
|
887
|
+
return parseMarketplaceResult(json);
|
|
888
|
+
} catch (err) {
|
|
889
|
+
console.warn("[dsh-a6api] marketplaceUnpin error:", err);
|
|
890
|
+
return { ok: false, message: friendlyActionError(err) };
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
async function marketplaceDisableChannel(userId, accessToken, channelId, model) {
|
|
894
|
+
try {
|
|
895
|
+
const res = await fetch(
|
|
896
|
+
`https://a6api.com/api/marketplace/channels/${channelId}/disable?model=${encodeURIComponent(model)}`,
|
|
897
|
+
{
|
|
898
|
+
method: "POST",
|
|
899
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
900
|
+
signal: AbortSignal.timeout(1e4)
|
|
901
|
+
}
|
|
902
|
+
);
|
|
903
|
+
const json = await res.json().catch(() => null);
|
|
904
|
+
if (!res.ok && !json) return { ok: false, message: `HTTP ${res.status}` };
|
|
905
|
+
return parseMarketplaceResult(json);
|
|
906
|
+
} catch (err) {
|
|
907
|
+
console.warn("[dsh-a6api] marketplaceDisableChannel error:", err);
|
|
908
|
+
return { ok: false, message: friendlyActionError(err) };
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
async function marketplaceRestoreChannel(userId, accessToken, channelId, model) {
|
|
912
|
+
try {
|
|
913
|
+
const res = await fetch(
|
|
914
|
+
`https://a6api.com/api/marketplace/channels/${channelId}/restore?model=${encodeURIComponent(model)}`,
|
|
915
|
+
{
|
|
916
|
+
method: "POST",
|
|
917
|
+
headers: buildWebHeaders2(userId, accessToken),
|
|
918
|
+
signal: AbortSignal.timeout(1e4)
|
|
919
|
+
}
|
|
920
|
+
);
|
|
921
|
+
const json = await res.json().catch(() => null);
|
|
922
|
+
if (!res.ok && !json) return { ok: false, message: `HTTP ${res.status}` };
|
|
923
|
+
return parseMarketplaceResult(json);
|
|
924
|
+
} catch (err) {
|
|
925
|
+
console.warn("[dsh-a6api] marketplaceRestoreChannel error:", err);
|
|
926
|
+
return { ok: false, message: friendlyActionError(err) };
|
|
927
|
+
}
|
|
928
|
+
}
|
|
955
929
|
|
|
956
930
|
// src/server/probe.ts
|
|
957
931
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -1017,6 +991,8 @@ async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName)
|
|
|
1017
991
|
success: true,
|
|
1018
992
|
channelId,
|
|
1019
993
|
channelName: log.channel_name,
|
|
994
|
+
// 该次请求由 API Key 发起,日志中记录的 token_id 即该 Key 的令牌 ID
|
|
995
|
+
tokenId: log.token_id !== void 0 && Number(log.token_id) > 0 ? Number(log.token_id) : void 0,
|
|
1020
996
|
merchant,
|
|
1021
997
|
durationMs
|
|
1022
998
|
};
|
|
@@ -1032,13 +1008,14 @@ async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName)
|
|
|
1032
1008
|
error: requestOk ? void 0 : requestError
|
|
1033
1009
|
};
|
|
1034
1010
|
}
|
|
1035
|
-
async function getKnownMerchantsFromLogs(userId, accessToken, modelNames = []) {
|
|
1011
|
+
async function getKnownMerchantsFromLogs(userId, accessToken, modelNames = [], logs) {
|
|
1036
1012
|
if (!userId && !accessToken || modelNames.length === 0) return {};
|
|
1037
1013
|
const result = {};
|
|
1038
1014
|
try {
|
|
1039
|
-
const
|
|
1015
|
+
const items = logs !== void 0 ? logs : await fetchRecentLogs(userId, accessToken, 50);
|
|
1016
|
+
const sorted = items.slice().sort((a, b) => (Number(b.created_at) || 0) - (Number(a.created_at) || 0));
|
|
1040
1017
|
const modelToLog = /* @__PURE__ */ new Map();
|
|
1041
|
-
for (const log of
|
|
1018
|
+
for (const log of sorted) {
|
|
1042
1019
|
const mName = log.model_name;
|
|
1043
1020
|
const chId = Number(log.channel);
|
|
1044
1021
|
if (mName && chId && !modelToLog.has(mName.toLowerCase())) {
|
|
@@ -1081,77 +1058,56 @@ async function getKnownMerchantsFromLogs(userId, accessToken, modelNames = []) {
|
|
|
1081
1058
|
}
|
|
1082
1059
|
|
|
1083
1060
|
// src/server/sync.ts
|
|
1084
|
-
import * as
|
|
1085
|
-
import * as
|
|
1086
|
-
import * as
|
|
1061
|
+
import * as fs2 from "node:fs";
|
|
1062
|
+
import * as fsp2 from "node:fs/promises";
|
|
1063
|
+
import * as path2 from "node:path";
|
|
1064
|
+
import * as os2 from "node:os";
|
|
1087
1065
|
var A6API_CRED_REF = "A6API_API_KEY";
|
|
1066
|
+
var A6API_TOKEN_REF = "A6API_ACCESS_TOKEN";
|
|
1067
|
+
var A6API_USER_REF = "A6API_USER_ID";
|
|
1068
|
+
var SETTINGS_NS = "llm-pi-ai";
|
|
1069
|
+
var PROVIDER_KEY = "a6api";
|
|
1070
|
+
var DEFAULT_BASE_URL = "https://api.a6api.com";
|
|
1071
|
+
var LEGACY_CONFIG_NAME = "dsh-a6api-config.json";
|
|
1088
1072
|
async function atomicWriteFile(filePath, content, mode = 384) {
|
|
1089
|
-
const dir =
|
|
1090
|
-
await
|
|
1091
|
-
const tmpPath =
|
|
1092
|
-
await
|
|
1093
|
-
await
|
|
1073
|
+
const dir = path2.dirname(filePath);
|
|
1074
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
1075
|
+
const tmpPath = path2.join(dir, `.${path2.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
1076
|
+
await fsp2.writeFile(tmpPath, content, { mode });
|
|
1077
|
+
await fsp2.rename(tmpPath, filePath);
|
|
1094
1078
|
}
|
|
1095
|
-
function
|
|
1096
|
-
return process.env.DSH_HOME ||
|
|
1079
|
+
function dshHome2() {
|
|
1080
|
+
return process.env.DSH_HOME || path2.join(os2.homedir(), ".dsh");
|
|
1097
1081
|
}
|
|
1098
|
-
function
|
|
1099
|
-
return
|
|
1082
|
+
function legacyConfigFile() {
|
|
1083
|
+
return path2.join(dshHome2(), LEGACY_CONFIG_NAME);
|
|
1100
1084
|
}
|
|
1101
1085
|
function credentialsFile() {
|
|
1102
|
-
return
|
|
1086
|
+
return path2.join(dshHome2(), ".credentials.yaml");
|
|
1103
1087
|
}
|
|
1104
1088
|
function settingsFile() {
|
|
1105
|
-
return
|
|
1089
|
+
return path2.join(dshHome2(), "settings.yaml");
|
|
1106
1090
|
}
|
|
1107
|
-
|
|
1108
|
-
const filePath = configFile();
|
|
1109
|
-
let raw = "";
|
|
1091
|
+
function getCredentials(ctx) {
|
|
1110
1092
|
try {
|
|
1111
|
-
|
|
1093
|
+
if (ctx && typeof ctx.get === "function") return ctx.get("credentials");
|
|
1112
1094
|
} catch {
|
|
1113
|
-
const apiKey = await readCredentialKey(A6API_CRED_REF) || "";
|
|
1114
|
-
return {
|
|
1115
|
-
baseURL: "https://api.a6api.com",
|
|
1116
|
-
apiKey,
|
|
1117
|
-
userId: "",
|
|
1118
|
-
activeModels: []
|
|
1119
|
-
};
|
|
1120
1095
|
}
|
|
1096
|
+
return void 0;
|
|
1097
|
+
}
|
|
1098
|
+
function getSettings(ctx) {
|
|
1121
1099
|
try {
|
|
1122
|
-
|
|
1123
|
-
const apiKey = parsed.apiKey || await readCredentialKey(A6API_CRED_REF) || "";
|
|
1124
|
-
const accessToken = parsed.accessToken || parsed.systemAccessToken || parsed.sessionCookie || "";
|
|
1125
|
-
return {
|
|
1126
|
-
baseURL: parsed.baseURL || "https://api.a6api.com",
|
|
1127
|
-
apiKey,
|
|
1128
|
-
accessToken,
|
|
1129
|
-
userId: parsed.userId || "",
|
|
1130
|
-
sessionCookie: accessToken,
|
|
1131
|
-
activeModels: Array.isArray(parsed.activeModels) ? parsed.activeModels : [],
|
|
1132
|
-
customBaseURL: parsed.customBaseURL
|
|
1133
|
-
};
|
|
1100
|
+
if (ctx && typeof ctx.get === "function") return ctx.get("settings");
|
|
1134
1101
|
} catch {
|
|
1135
|
-
return {
|
|
1136
|
-
baseURL: "https://api.a6api.com",
|
|
1137
|
-
apiKey: "",
|
|
1138
|
-
accessToken: "",
|
|
1139
|
-
userId: "",
|
|
1140
|
-
activeModels: []
|
|
1141
|
-
};
|
|
1142
1102
|
}
|
|
1103
|
+
return void 0;
|
|
1143
1104
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
const { apiKey: _apiKey, ...safeConfig } = config;
|
|
1147
|
-
await atomicWriteFile(filePath, JSON.stringify(safeConfig, null, 2));
|
|
1148
|
-
if (config.apiKey && config.apiKey.trim()) {
|
|
1149
|
-
await writeCredentialKey(A6API_CRED_REF, config.apiKey.trim());
|
|
1150
|
-
}
|
|
1105
|
+
function stripV1(baseURL) {
|
|
1106
|
+
return baseURL.replace(/\/v1\/?$/, "");
|
|
1151
1107
|
}
|
|
1152
1108
|
async function readCredentialKey(refKey) {
|
|
1153
1109
|
try {
|
|
1154
|
-
const yaml = await
|
|
1110
|
+
const yaml = await fsp2.readFile(credentialsFile(), "utf8");
|
|
1155
1111
|
let inRefs = false;
|
|
1156
1112
|
for (const line of yaml.split(/\r?\n/)) {
|
|
1157
1113
|
const trimmed = line.trim();
|
|
@@ -1168,7 +1124,7 @@ async function readCredentialKey(refKey) {
|
|
|
1168
1124
|
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
1169
1125
|
val = val.slice(1, -1);
|
|
1170
1126
|
}
|
|
1171
|
-
return val;
|
|
1127
|
+
return val || null;
|
|
1172
1128
|
}
|
|
1173
1129
|
}
|
|
1174
1130
|
} catch {
|
|
@@ -1179,14 +1135,14 @@ async function writeCredentialKey(refKey, value) {
|
|
|
1179
1135
|
const cFile = credentialsFile();
|
|
1180
1136
|
let yaml = "";
|
|
1181
1137
|
try {
|
|
1182
|
-
yaml = await
|
|
1138
|
+
yaml = await fsp2.readFile(cFile, "utf8");
|
|
1183
1139
|
} catch {
|
|
1184
1140
|
yaml = "version: 1\nrefs:\n";
|
|
1185
1141
|
}
|
|
1186
1142
|
const lines = yaml.split(/\r?\n/);
|
|
1187
1143
|
let inRefs = false;
|
|
1188
1144
|
let refsLineIdx = -1;
|
|
1189
|
-
let
|
|
1145
|
+
let foundIdx = -1;
|
|
1190
1146
|
for (let i = 0; i < lines.length; i++) {
|
|
1191
1147
|
const line = lines[i];
|
|
1192
1148
|
const trimmed = line.trim();
|
|
@@ -1204,58 +1160,81 @@ async function writeCredentialKey(refKey, value) {
|
|
|
1204
1160
|
if (inRefs) {
|
|
1205
1161
|
const m = /^([A-Za-z0-9_.\-]+):/.exec(trimmed);
|
|
1206
1162
|
if (m && m[1] === refKey) {
|
|
1207
|
-
|
|
1208
|
-
found = true;
|
|
1163
|
+
foundIdx = i;
|
|
1209
1164
|
break;
|
|
1210
1165
|
}
|
|
1211
1166
|
}
|
|
1212
1167
|
}
|
|
1213
|
-
if (
|
|
1214
|
-
if (
|
|
1215
|
-
|
|
1168
|
+
if (value === "") {
|
|
1169
|
+
if (foundIdx >= 0) lines.splice(foundIdx, 1);
|
|
1170
|
+
} else {
|
|
1171
|
+
if (foundIdx >= 0) {
|
|
1172
|
+
lines[foundIdx] = ` ${refKey}: ${JSON.stringify(value)}`;
|
|
1216
1173
|
} else {
|
|
1217
|
-
|
|
1174
|
+
if (refsLineIdx >= 0) {
|
|
1175
|
+
lines.splice(refsLineIdx + 1, 0, ` ${refKey}: ${JSON.stringify(value)}`);
|
|
1176
|
+
} else {
|
|
1177
|
+
lines.push("refs:", ` ${refKey}: ${JSON.stringify(value)}`);
|
|
1178
|
+
}
|
|
1218
1179
|
}
|
|
1219
1180
|
}
|
|
1220
1181
|
await atomicWriteFile(cFile, lines.join("\n"), 384);
|
|
1221
1182
|
}
|
|
1222
|
-
async function
|
|
1223
|
-
const sFile = settingsFile();
|
|
1224
|
-
let yaml = "";
|
|
1183
|
+
async function readRawConfiguredModels() {
|
|
1225
1184
|
try {
|
|
1226
|
-
yaml = await
|
|
1185
|
+
const yaml = await fsp2.readFile(settingsFile(), "utf8");
|
|
1186
|
+
const lines = yaml.split(/\r?\n/);
|
|
1187
|
+
let inA6 = false;
|
|
1188
|
+
let inModels = false;
|
|
1189
|
+
const modelIds = [];
|
|
1190
|
+
for (const line of lines) {
|
|
1191
|
+
const trimmed = line.trim();
|
|
1192
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
1193
|
+
if (indent === 4 && trimmed.startsWith("a6api:")) {
|
|
1194
|
+
inA6 = true;
|
|
1195
|
+
inModels = false;
|
|
1196
|
+
continue;
|
|
1197
|
+
}
|
|
1198
|
+
if (inA6 && indent <= 4 && !trimmed.startsWith("a6api:")) {
|
|
1199
|
+
inA6 = false;
|
|
1200
|
+
inModels = false;
|
|
1201
|
+
}
|
|
1202
|
+
if (inA6 && indent === 6 && trimmed.startsWith("models:")) {
|
|
1203
|
+
inModels = true;
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
if (inModels && indent === 8 && trimmed.startsWith("- id:")) {
|
|
1207
|
+
const id = trimmed.replace(/^- id:\s*/, "").trim();
|
|
1208
|
+
if (id) modelIds.push(id);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
return modelIds;
|
|
1227
1212
|
} catch {
|
|
1228
|
-
|
|
1213
|
+
return [];
|
|
1229
1214
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1215
|
+
}
|
|
1216
|
+
async function readRawA6apiBaseURL() {
|
|
1217
|
+
try {
|
|
1218
|
+
const yaml = await fsp2.readFile(settingsFile(), "utf8");
|
|
1219
|
+
let inA6 = false;
|
|
1220
|
+
for (const line of yaml.split(/\r?\n/)) {
|
|
1221
|
+
const trimmed = line.trim();
|
|
1222
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1223
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
1224
|
+
if (indent === 4 && trimmed.startsWith("a6api:")) {
|
|
1225
|
+
inA6 = true;
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
if (inA6 && indent <= 4 && !trimmed.startsWith("a6api:")) inA6 = false;
|
|
1229
|
+
if (inA6 && indent === 6 && trimmed.startsWith("baseURL:")) {
|
|
1230
|
+
return trimmed.replace(/^baseURL:\s*/, "").trim().replace(/^["']|["']$/g, "");
|
|
1244
1231
|
}
|
|
1245
1232
|
}
|
|
1246
|
-
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
` displayName: A6API`,
|
|
1252
|
-
` apiKeyEnv: ${A6API_CRED_REF}`,
|
|
1253
|
-
` api: openai-completions`,
|
|
1254
|
-
` baseURL: ${dshBaseUrl}`,
|
|
1255
|
-
` models:`,
|
|
1256
|
-
...modelEntries.length > 0 ? modelEntries : [` []`]
|
|
1257
|
-
];
|
|
1258
|
-
const lines = yaml.split(/\r?\n/);
|
|
1233
|
+
} catch {
|
|
1234
|
+
}
|
|
1235
|
+
return "";
|
|
1236
|
+
}
|
|
1237
|
+
function scanA6apiBlockRange(lines) {
|
|
1259
1238
|
let inLlm = false;
|
|
1260
1239
|
let inProviders = false;
|
|
1261
1240
|
let inA6 = false;
|
|
@@ -1301,6 +1280,46 @@ async function syncToDshSettings(baseURL, modelIds) {
|
|
|
1301
1280
|
inA6 = false;
|
|
1302
1281
|
}
|
|
1303
1282
|
}
|
|
1283
|
+
return { a6Start, a6End, providersLineIdx, llmLineIdx };
|
|
1284
|
+
}
|
|
1285
|
+
async function writeRawA6apiBlock(baseURL, modelIds) {
|
|
1286
|
+
const sFile = settingsFile();
|
|
1287
|
+
let yaml = "";
|
|
1288
|
+
try {
|
|
1289
|
+
yaml = await fsp2.readFile(sFile, "utf8");
|
|
1290
|
+
} catch {
|
|
1291
|
+
yaml = "llm-pi-ai:\n providers:\n";
|
|
1292
|
+
}
|
|
1293
|
+
const modelEntries = modelIds.map((id) => {
|
|
1294
|
+
const entry = getCatalogEntry(id);
|
|
1295
|
+
const lines2 = [` - id: ${id}`];
|
|
1296
|
+
if (entry?.name) lines2.push(` name: ${JSON.stringify(String(entry.name).replace(/\r?\n/g, " "))}`);
|
|
1297
|
+
if (entry?.contextWindow != null) lines2.push(` contextWindow: ${entry.contextWindow}`);
|
|
1298
|
+
if (entry?.maxTokens != null) lines2.push(` maxTokens: ${entry.maxTokens}`);
|
|
1299
|
+
if (entry?.input && entry.input.length > 0) {
|
|
1300
|
+
lines2.push(` input:`);
|
|
1301
|
+
for (const m of entry.input) lines2.push(` - ${m}`);
|
|
1302
|
+
}
|
|
1303
|
+
if (entry?.reasoningEfforts && typeof entry.reasoningEfforts === "object") {
|
|
1304
|
+
lines2.push(` reasoningEfforts:`);
|
|
1305
|
+
for (const [k, v] of Object.entries(entry.reasoningEfforts)) {
|
|
1306
|
+
lines2.push(v ? ` ${k}: ${v}` : ` ${k}: `);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return lines2.join("\n");
|
|
1310
|
+
});
|
|
1311
|
+
const dshBaseUrl = baseURL.endsWith("/v1") ? baseURL : `${baseURL.replace(/\/+$/, "")}/v1`;
|
|
1312
|
+
const a6apiBlockLines = [
|
|
1313
|
+
` a6api:`,
|
|
1314
|
+
` displayName: A6API`,
|
|
1315
|
+
` apiKeyEnv: ${A6API_CRED_REF}`,
|
|
1316
|
+
` api: openai-completions`,
|
|
1317
|
+
` baseURL: ${dshBaseUrl}`,
|
|
1318
|
+
` models:`,
|
|
1319
|
+
...modelEntries
|
|
1320
|
+
];
|
|
1321
|
+
const lines = yaml.split(/\r?\n/);
|
|
1322
|
+
const { a6Start, a6End, providersLineIdx, llmLineIdx } = scanA6apiBlockRange(lines);
|
|
1304
1323
|
if (a6Start >= 0) {
|
|
1305
1324
|
lines.splice(a6Start, a6End - a6Start, ...a6apiBlockLines);
|
|
1306
1325
|
} else if (providersLineIdx >= 0) {
|
|
@@ -1312,38 +1331,305 @@ async function syncToDshSettings(baseURL, modelIds) {
|
|
|
1312
1331
|
}
|
|
1313
1332
|
await atomicWriteFile(sFile, lines.join("\n"), 420);
|
|
1314
1333
|
}
|
|
1315
|
-
async function
|
|
1334
|
+
async function removeRawA6apiBlock() {
|
|
1335
|
+
const sFile = settingsFile();
|
|
1336
|
+
let yaml = "";
|
|
1316
1337
|
try {
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1338
|
+
yaml = await fsp2.readFile(sFile, "utf8");
|
|
1339
|
+
} catch {
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
const lines = yaml.split(/\r?\n/);
|
|
1343
|
+
const { a6Start, a6End, providersLineIdx, llmLineIdx } = scanA6apiBlockRange(lines);
|
|
1344
|
+
if (a6Start < 0) return;
|
|
1345
|
+
lines.splice(a6Start, a6End - a6Start);
|
|
1346
|
+
if (providersLineIdx >= 0) {
|
|
1347
|
+
let hasProvider = false;
|
|
1348
|
+
for (let i = providersLineIdx + 1; i < lines.length; i++) {
|
|
1349
|
+
const trimmed = lines[i].trim();
|
|
1350
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1351
|
+
const indent = lines[i].match(/^\s*/)?.[0].length ?? 0;
|
|
1352
|
+
if (indent <= 2) break;
|
|
1353
|
+
if (indent === 4) {
|
|
1354
|
+
hasProvider = true;
|
|
1355
|
+
break;
|
|
1329
1356
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1357
|
+
}
|
|
1358
|
+
if (!hasProvider) lines.splice(providersLineIdx, 1);
|
|
1359
|
+
}
|
|
1360
|
+
if (llmLineIdx >= 0) {
|
|
1361
|
+
let hasLlmKey = false;
|
|
1362
|
+
for (let i = llmLineIdx + 1; i < lines.length; i++) {
|
|
1363
|
+
const trimmed = lines[i].trim();
|
|
1364
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1365
|
+
const indent = lines[i].match(/^\s*/)?.[0].length ?? 0;
|
|
1366
|
+
if (indent === 0) break;
|
|
1367
|
+
if (indent === 2) {
|
|
1368
|
+
hasLlmKey = true;
|
|
1369
|
+
break;
|
|
1333
1370
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1371
|
+
}
|
|
1372
|
+
if (!hasLlmKey) lines.splice(llmLineIdx, 1);
|
|
1373
|
+
}
|
|
1374
|
+
await atomicWriteFile(sFile, lines.join("\n"), 420);
|
|
1375
|
+
}
|
|
1376
|
+
function normalizeLegacy(parsed) {
|
|
1377
|
+
return {
|
|
1378
|
+
apiKey: typeof parsed?.apiKey === "string" ? parsed.apiKey : "",
|
|
1379
|
+
accessToken: String(parsed?.accessToken || parsed?.systemAccessToken || parsed?.sessionCookie || ""),
|
|
1380
|
+
userId: String(parsed?.userId || ""),
|
|
1381
|
+
baseURL: typeof parsed?.baseURL === "string" && parsed.baseURL ? parsed.baseURL : DEFAULT_BASE_URL,
|
|
1382
|
+
activeModels: Array.isArray(parsed?.activeModels) ? parsed.activeModels.filter((m) => typeof m === "string") : []
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
async function readLegacyConfig() {
|
|
1386
|
+
try {
|
|
1387
|
+
const raw = await fsp2.readFile(legacyConfigFile(), "utf8");
|
|
1388
|
+
const legacy = normalizeLegacy(JSON.parse(raw));
|
|
1389
|
+
return {
|
|
1390
|
+
baseURL: legacy.baseURL,
|
|
1391
|
+
apiKey: legacy.apiKey,
|
|
1392
|
+
accessToken: legacy.accessToken || void 0,
|
|
1393
|
+
userId: legacy.userId || void 0,
|
|
1394
|
+
activeModels: legacy.activeModels
|
|
1395
|
+
};
|
|
1396
|
+
} catch {
|
|
1397
|
+
return null;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
function buildA6apiBlock(baseURL, modelIds) {
|
|
1401
|
+
const models = modelIds.map((id) => {
|
|
1402
|
+
const entry = getCatalogEntry(id);
|
|
1403
|
+
const m = { id };
|
|
1404
|
+
if (entry?.name) m.name = entry.name;
|
|
1405
|
+
if (entry?.contextWindow != null) m.contextWindow = entry.contextWindow;
|
|
1406
|
+
if (entry?.maxTokens != null) m.maxTokens = entry.maxTokens;
|
|
1407
|
+
if (entry?.input && entry.input.length > 0) m.input = [...entry.input];
|
|
1408
|
+
if (entry?.reasoningEfforts !== void 0 && entry.reasoningEfforts !== null) {
|
|
1409
|
+
m.reasoningEfforts = entry.reasoningEfforts;
|
|
1410
|
+
}
|
|
1411
|
+
return m;
|
|
1412
|
+
});
|
|
1413
|
+
return {
|
|
1414
|
+
displayName: "A6API",
|
|
1415
|
+
apiKeyEnv: A6API_CRED_REF,
|
|
1416
|
+
api: "openai-completions",
|
|
1417
|
+
baseURL: baseURL.endsWith("/v1") ? baseURL : `${baseURL.replace(/\/+$/, "")}/v1`,
|
|
1418
|
+
models
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
function createConfigAccess(ctx) {
|
|
1422
|
+
let migration = null;
|
|
1423
|
+
const ensureMigrated = () => {
|
|
1424
|
+
if (!migration) {
|
|
1425
|
+
migration = doMigrate().catch((err) => {
|
|
1426
|
+
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);
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
return migration;
|
|
1430
|
+
};
|
|
1431
|
+
const resolveRef = async (creds, ref) => {
|
|
1432
|
+
try {
|
|
1433
|
+
if (creds && typeof creds.resolve === "function") {
|
|
1434
|
+
const r = await creds.resolve(ref);
|
|
1435
|
+
return r && typeof r.value === "string" ? r.value : "";
|
|
1337
1436
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1437
|
+
} catch (err) {
|
|
1438
|
+
console.warn(`[dsh-a6api] credentials.resolve(${ref}) failed:`, err?.message || err);
|
|
1439
|
+
}
|
|
1440
|
+
return await readCredentialKey(ref) || "";
|
|
1441
|
+
};
|
|
1442
|
+
const readA6apiBlock = async (settings) => {
|
|
1443
|
+
try {
|
|
1444
|
+
if (settings && typeof settings.get === "function") {
|
|
1445
|
+
const llm = settings.get(SETTINGS_NS);
|
|
1446
|
+
const block = llm && llm.providers ? llm.providers[PROVIDER_KEY] : void 0;
|
|
1447
|
+
if (block && typeof block === "object") {
|
|
1448
|
+
return {
|
|
1449
|
+
baseURL: typeof block.baseURL === "string" ? block.baseURL : void 0,
|
|
1450
|
+
models: Array.isArray(block.models) ? block.models.map((m) => typeof m === "string" ? m : m && typeof m.id === "string" ? m.id : "").filter(Boolean) : []
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1341
1453
|
}
|
|
1454
|
+
} catch (err) {
|
|
1455
|
+
console.warn("[dsh-a6api] settings.get(llm-pi-ai) failed:", err?.message || err);
|
|
1342
1456
|
}
|
|
1343
|
-
return
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1457
|
+
return null;
|
|
1458
|
+
};
|
|
1459
|
+
const readConfig = async () => {
|
|
1460
|
+
await ensureMigrated();
|
|
1461
|
+
const creds = getCredentials(ctx);
|
|
1462
|
+
const settings = getSettings(ctx);
|
|
1463
|
+
const apiKey = await resolveRef(creds, A6API_CRED_REF);
|
|
1464
|
+
const accessToken = await resolveRef(creds, A6API_TOKEN_REF);
|
|
1465
|
+
const userId = await resolveRef(creds, A6API_USER_REF);
|
|
1466
|
+
let baseURL = DEFAULT_BASE_URL;
|
|
1467
|
+
let activeModels = [];
|
|
1468
|
+
const block = await readA6apiBlock(settings);
|
|
1469
|
+
if (block) {
|
|
1470
|
+
if (block.baseURL) baseURL = stripV1(block.baseURL) || DEFAULT_BASE_URL;
|
|
1471
|
+
activeModels = block.models;
|
|
1472
|
+
} else {
|
|
1473
|
+
const rawBase = await readRawA6apiBaseURL();
|
|
1474
|
+
if (rawBase) baseURL = stripV1(rawBase) || DEFAULT_BASE_URL;
|
|
1475
|
+
activeModels = await readRawConfiguredModels();
|
|
1476
|
+
}
|
|
1477
|
+
if (!apiKey && !accessToken && fs2.existsSync(legacyConfigFile())) {
|
|
1478
|
+
const legacy = await readLegacyConfig();
|
|
1479
|
+
if (legacy) {
|
|
1480
|
+
return {
|
|
1481
|
+
baseURL: baseURL || legacy.baseURL,
|
|
1482
|
+
apiKey: apiKey || legacy.apiKey,
|
|
1483
|
+
accessToken: accessToken || legacy.accessToken,
|
|
1484
|
+
userId: userId || legacy.userId,
|
|
1485
|
+
activeModels: activeModels.length > 0 ? activeModels : legacy.activeModels
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
return { baseURL, apiKey, accessToken, userId, activeModels };
|
|
1490
|
+
};
|
|
1491
|
+
const writeConfig = async (parts) => {
|
|
1492
|
+
await ensureMigrated();
|
|
1493
|
+
const creds = getCredentials(ctx);
|
|
1494
|
+
const entries = [
|
|
1495
|
+
[A6API_CRED_REF, parts.apiKey],
|
|
1496
|
+
[A6API_TOKEN_REF, parts.accessToken],
|
|
1497
|
+
[A6API_USER_REF, parts.userId]
|
|
1498
|
+
];
|
|
1499
|
+
for (const [ref, value] of entries) {
|
|
1500
|
+
if (value === void 0) continue;
|
|
1501
|
+
const v = value.trim();
|
|
1502
|
+
try {
|
|
1503
|
+
if (creds && typeof creds.set === "function" && typeof creds.unset === "function") {
|
|
1504
|
+
if (v) await creds.set(ref, v);
|
|
1505
|
+
else await creds.unset(ref);
|
|
1506
|
+
} else {
|
|
1507
|
+
await writeCredentialKey(ref, v);
|
|
1508
|
+
}
|
|
1509
|
+
} catch (err) {
|
|
1510
|
+
console.warn(`[dsh-a6api] \u5199\u5165\u51ED\u636E ${ref} \u5931\u8D25\uFF08\u5DF2\u8DF3\u8FC7\uFF09:`, err?.message || err);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
const syncModels = async (baseURL, modelIds) => {
|
|
1515
|
+
const settings = getSettings(ctx);
|
|
1516
|
+
if (modelIds.length === 0) {
|
|
1517
|
+
if (settings && typeof settings.mutate === "function") {
|
|
1518
|
+
try {
|
|
1519
|
+
await settings.mutate(SETTINGS_NS, [{ op: "unset", path: ["providers", PROVIDER_KEY] }]);
|
|
1520
|
+
return;
|
|
1521
|
+
} catch (err) {
|
|
1522
|
+
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);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
await removeRawA6apiBlock();
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
const block = buildA6apiBlock(baseURL, modelIds);
|
|
1529
|
+
if (settings && typeof settings.update === "function") {
|
|
1530
|
+
try {
|
|
1531
|
+
await settings.update(SETTINGS_NS, { providers: { [PROVIDER_KEY]: block } });
|
|
1532
|
+
return;
|
|
1533
|
+
} catch (err) {
|
|
1534
|
+
console.warn("[dsh-a6api] settings.update(llm-pi-ai) \u5931\u8D25\uFF0C\u56DE\u9000\u88F8\u5199 settings.yaml:", err?.message || err);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
await writeRawA6apiBlock(baseURL, modelIds);
|
|
1538
|
+
};
|
|
1539
|
+
const getDshConfiguredModels = async () => {
|
|
1540
|
+
const block = await readA6apiBlock(getSettings(ctx));
|
|
1541
|
+
if (block) return block.models;
|
|
1542
|
+
return readRawConfiguredModels();
|
|
1543
|
+
};
|
|
1544
|
+
const fillRef = async (creds, ref, value) => {
|
|
1545
|
+
if (!value) return true;
|
|
1546
|
+
try {
|
|
1547
|
+
const current = await resolveRef(creds, ref);
|
|
1548
|
+
if (current) return true;
|
|
1549
|
+
if (creds && typeof creds.set === "function") await creds.set(ref, value);
|
|
1550
|
+
else await writeCredentialKey(ref, value);
|
|
1551
|
+
return true;
|
|
1552
|
+
} catch (err) {
|
|
1553
|
+
console.warn(`[dsh-a6api] \u8FC1\u79FB ${ref} \u5931\u8D25\uFF08\u8DF3\u8FC7\uFF0C\u4FDD\u7559\u65E7\u6587\u4EF6\uFF09:`, err?.message || err);
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
const archiveLegacy = async (filePath) => {
|
|
1558
|
+
try {
|
|
1559
|
+
await fsp2.rename(filePath, `${filePath}.bak`);
|
|
1560
|
+
console.log("[dsh-a6api] \u65E7\u914D\u7F6E\u5DF2\u8FC1\u79FB\u81F3 DSH \u539F\u751F\u914D\u7F6E\u5E76\u5F52\u6863: dsh-a6api-config.json.bak");
|
|
1561
|
+
} catch (err) {
|
|
1562
|
+
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);
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
const doMigrate = async () => {
|
|
1566
|
+
const filePath = legacyConfigFile();
|
|
1567
|
+
let raw = "";
|
|
1568
|
+
try {
|
|
1569
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
1570
|
+
} catch {
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
let parsed;
|
|
1574
|
+
try {
|
|
1575
|
+
parsed = JSON.parse(raw);
|
|
1576
|
+
} catch (err) {
|
|
1577
|
+
console.warn("[dsh-a6api] \u65E7\u914D\u7F6E\u6587\u4EF6\u635F\u574F\uFF0C\u8DF3\u8FC7\u8FC1\u79FB\u5E76\u5F52\u6863:", err?.message || err);
|
|
1578
|
+
await archiveLegacy(filePath);
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
const legacy = normalizeLegacy(parsed);
|
|
1582
|
+
const creds = getCredentials(ctx);
|
|
1583
|
+
const fillResults = [
|
|
1584
|
+
await fillRef(creds, A6API_CRED_REF, legacy.apiKey),
|
|
1585
|
+
await fillRef(creds, A6API_TOKEN_REF, legacy.accessToken),
|
|
1586
|
+
await fillRef(creds, A6API_USER_REF, legacy.userId)
|
|
1587
|
+
];
|
|
1588
|
+
if (fillResults.some((ok) => !ok)) {
|
|
1589
|
+
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");
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
const block = await readA6apiBlock(getSettings(ctx));
|
|
1593
|
+
const blockExists = Boolean(block) || (await readRawConfiguredModels()).length > 0;
|
|
1594
|
+
if (!blockExists && legacy.activeModels.length > 0) {
|
|
1595
|
+
await syncModels(legacy.baseURL, legacy.activeModels);
|
|
1596
|
+
}
|
|
1597
|
+
await archiveLegacy(filePath);
|
|
1598
|
+
};
|
|
1599
|
+
return { ensureMigrated, readConfig, writeConfig, syncModels, getDshConfiguredModels };
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// src/types.ts
|
|
1603
|
+
var THINKING_LEVEL_KEYS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
1604
|
+
function validateReasoningEfforts(value) {
|
|
1605
|
+
if (value === false) return { ok: true, value: false };
|
|
1606
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1607
|
+
return { ok: false, error: "reasoningEfforts \u5FC5\u987B\u662F\u6863\u4F4D\u5B57\u5178\u6216 false" };
|
|
1346
1608
|
}
|
|
1609
|
+
const dict = value;
|
|
1610
|
+
const keys = Object.keys(dict);
|
|
1611
|
+
if (keys.length === 0) return { ok: false, error: "reasoningEfforts \u4E0D\u80FD\u4E3A\u7A7A\u5B57\u5178" };
|
|
1612
|
+
const out = {};
|
|
1613
|
+
for (const k of keys) {
|
|
1614
|
+
if (!THINKING_LEVEL_KEYS.includes(k)) {
|
|
1615
|
+
return { ok: false, error: `reasoningEfforts \u952E "${k}" \u4E0D\u662F DSH \u652F\u6301\u7684\u6863\u4F4D\uFF08off/minimal/low/medium/high/xhigh/max\uFF09` };
|
|
1616
|
+
}
|
|
1617
|
+
const v = dict[k];
|
|
1618
|
+
if (v === null) {
|
|
1619
|
+
if (k !== "off") {
|
|
1620
|
+
return { ok: false, error: `reasoningEfforts.${k} \u5FC5\u987B\u63D0\u4F9B wire \u503C\uFF0C\u4EC5 off \u5141\u8BB8\u7559\u7A7A` };
|
|
1621
|
+
}
|
|
1622
|
+
out[k] = null;
|
|
1623
|
+
} else if (typeof v === "string" && v.length > 0) {
|
|
1624
|
+
out[k] = v;
|
|
1625
|
+
} else {
|
|
1626
|
+
return { ok: false, error: `reasoningEfforts.${k} \u7684\u503C\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6216 null` };
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
if (!keys.some((k) => k !== "off")) {
|
|
1630
|
+
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" };
|
|
1631
|
+
}
|
|
1632
|
+
return { ok: true, value: out };
|
|
1347
1633
|
}
|
|
1348
1634
|
|
|
1349
1635
|
// src/index.ts
|
|
@@ -1355,11 +1641,10 @@ function maskConfig(c) {
|
|
|
1355
1641
|
return {
|
|
1356
1642
|
...c,
|
|
1357
1643
|
apiKey: c.apiKey ? MASK : "",
|
|
1358
|
-
accessToken: c.accessToken
|
|
1359
|
-
sessionCookie: "",
|
|
1644
|
+
accessToken: c.accessToken ? MASK : "",
|
|
1360
1645
|
userId: c.userId ? MASK : "",
|
|
1361
1646
|
hasApiKey: Boolean(c.apiKey),
|
|
1362
|
-
hasToken: Boolean(c.accessToken
|
|
1647
|
+
hasToken: Boolean(c.accessToken)
|
|
1363
1648
|
};
|
|
1364
1649
|
}
|
|
1365
1650
|
function sendJson(res, status, body) {
|
|
@@ -1395,7 +1680,68 @@ async function parseJsonBody(req) {
|
|
|
1395
1680
|
}
|
|
1396
1681
|
var merchantCardCache = /* @__PURE__ */ new Map();
|
|
1397
1682
|
var MERCHANT_CARD_TTL_MS = 15 * 60 * 1e3;
|
|
1683
|
+
var tokenResolveCache = null;
|
|
1684
|
+
var TOKEN_RESOLVE_TTL_MS = 10 * 60 * 1e3;
|
|
1685
|
+
async function resolveTokenId(config) {
|
|
1686
|
+
if (tokenResolveCache && Date.now() - tokenResolveCache.at < TOKEN_RESOLVE_TTL_MS) {
|
|
1687
|
+
return tokenResolveCache.tokenId;
|
|
1688
|
+
}
|
|
1689
|
+
const token = config.accessToken || "";
|
|
1690
|
+
if (!config.userId || !token) return null;
|
|
1691
|
+
try {
|
|
1692
|
+
const tokens = await fetchTokens(config.userId, token);
|
|
1693
|
+
let tokenId = null;
|
|
1694
|
+
const key = (config.apiKey || "").trim();
|
|
1695
|
+
if (key) {
|
|
1696
|
+
const hit = tokens.find((t) => t.key && t.key === key);
|
|
1697
|
+
if (hit) tokenId = hit.id;
|
|
1698
|
+
}
|
|
1699
|
+
if (!tokenId && tokens.length === 1) tokenId = tokens[0].id;
|
|
1700
|
+
if (tokenId && tokenId > 0) {
|
|
1701
|
+
tokenResolveCache = { tokenId, at: Date.now() };
|
|
1702
|
+
return tokenId;
|
|
1703
|
+
}
|
|
1704
|
+
} catch (err) {
|
|
1705
|
+
console.warn("[dsh-a6api] resolveTokenId error:", err);
|
|
1706
|
+
}
|
|
1707
|
+
return null;
|
|
1708
|
+
}
|
|
1709
|
+
function webAuthOf(config) {
|
|
1710
|
+
return { userId: config.userId || void 0, token: config.accessToken || void 0 };
|
|
1711
|
+
}
|
|
1712
|
+
function cachedMerchantOf(modelName) {
|
|
1713
|
+
const entry = merchantCardCache.get(modelName.toLowerCase());
|
|
1714
|
+
return entry && Date.now() - entry.at < MERCHANT_CARD_TTL_MS ? entry.card : void 0;
|
|
1715
|
+
}
|
|
1716
|
+
function overlayPinsOnModels(models, pins, tokenId) {
|
|
1717
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
1718
|
+
for (const p of pins) {
|
|
1719
|
+
const key = (p.model_name || "").toLowerCase();
|
|
1720
|
+
if (!key) continue;
|
|
1721
|
+
const list = byModel.get(key);
|
|
1722
|
+
if (list) list.push(p);
|
|
1723
|
+
else byModel.set(key, [p]);
|
|
1724
|
+
}
|
|
1725
|
+
return models.map((m) => {
|
|
1726
|
+
const list = byModel.get(m.model_name.toLowerCase());
|
|
1727
|
+
if (!list || list.length === 0) return m;
|
|
1728
|
+
const pick = (tokenId ? list.find((p) => Number(p.token_id) === tokenId) : void 0) || list[0];
|
|
1729
|
+
const cardChannel = m.merchant?.channel_id;
|
|
1730
|
+
const pinChannel = pick.channel_id;
|
|
1731
|
+
return {
|
|
1732
|
+
...m,
|
|
1733
|
+
// Number() 归一化:官方接口可能返回字符串渠道 ID,严格相等会误判
|
|
1734
|
+
pinStatus: pinChannel ? cardChannel && Number(cardChannel) === Number(pinChannel) ? "pin_here" : "pin_elsewhere" : void 0,
|
|
1735
|
+
pinnedChannelId: pinChannel,
|
|
1736
|
+
pinnedSupplierName: pick.supplier_nickname || pick.supplier_name,
|
|
1737
|
+
pinnedFallback: pick.fallback_to_smart_routing,
|
|
1738
|
+
pinTokenMatched: Boolean(tokenId && Number(pick.token_id) === tokenId)
|
|
1739
|
+
};
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1398
1742
|
function apply(ctx) {
|
|
1743
|
+
const configAccess = createConfigAccess(ctx);
|
|
1744
|
+
void configAccess.ensureMigrated();
|
|
1399
1745
|
const webServer = ctx.webServer || (ctx.get ? ctx.get("webServer") : null);
|
|
1400
1746
|
if (webServer && typeof webServer.register === "function") {
|
|
1401
1747
|
ctx.effect(() => {
|
|
@@ -1409,24 +1755,29 @@ function apply(ctx) {
|
|
|
1409
1755
|
res.writeHead(204);
|
|
1410
1756
|
return res.end();
|
|
1411
1757
|
}
|
|
1758
|
+
if (req.method === "POST" && !String(req.headers["content-type"] || "").toLowerCase().includes("application/json")) {
|
|
1759
|
+
return sendJson(res, 415, { ok: false, error: "Content-Type must be application/json" });
|
|
1760
|
+
}
|
|
1412
1761
|
try {
|
|
1413
1762
|
if (pathname === "/state" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1414
|
-
const config = await
|
|
1415
|
-
const token = config.accessToken ||
|
|
1416
|
-
const balance
|
|
1763
|
+
const config = await configAccess.readConfig();
|
|
1764
|
+
const token = config.accessToken || "";
|
|
1765
|
+
const [balance, dshConfiguredModels, modelIdsRaw, allLogs, pins] = await Promise.all([
|
|
1766
|
+
fetchBalance(config.baseURL, config.apiKey, config.userId, token),
|
|
1767
|
+
configAccess.getDshConfiguredModels(),
|
|
1768
|
+
config.apiKey ? fetchTokenModels(config.baseURL, config.apiKey) : Promise.resolve([]),
|
|
1769
|
+
fetchRecentLogs(config.userId, token, 100),
|
|
1770
|
+
config.userId && token ? fetchMarketplacePins(config.userId, token).catch(() => []) : Promise.resolve([])
|
|
1771
|
+
]);
|
|
1417
1772
|
if (balance?.userId && String(balance.userId) !== config.userId) {
|
|
1773
|
+
tokenResolveCache = null;
|
|
1418
1774
|
config.userId = String(balance.userId);
|
|
1419
|
-
await
|
|
1420
|
-
}
|
|
1421
|
-
const dshConfiguredModels = await getDshConfiguredModels();
|
|
1422
|
-
let modelIds = [];
|
|
1423
|
-
if (config.apiKey) {
|
|
1424
|
-
modelIds = await fetchTokenModels(config.baseURL, config.apiKey);
|
|
1775
|
+
await configAccess.writeConfig({ userId: config.userId });
|
|
1425
1776
|
}
|
|
1777
|
+
let modelIds = modelIdsRaw;
|
|
1426
1778
|
if (modelIds.length === 0) {
|
|
1427
1779
|
modelIds = [
|
|
1428
1780
|
.../* @__PURE__ */ new Set([
|
|
1429
|
-
...config.activeModels,
|
|
1430
1781
|
...dshConfiguredModels,
|
|
1431
1782
|
"gpt-5.6-sol",
|
|
1432
1783
|
"gpt-5.6-terra",
|
|
@@ -1437,23 +1788,42 @@ function apply(ctx) {
|
|
|
1437
1788
|
])
|
|
1438
1789
|
];
|
|
1439
1790
|
}
|
|
1791
|
+
allLogs.sort((a, b) => (Number(b.created_at) || 0) - (Number(a.created_at) || 0));
|
|
1440
1792
|
if (config.userId || token) {
|
|
1441
1793
|
const missing = modelIds.filter((m) => {
|
|
1442
1794
|
const entry = merchantCardCache.get(m.toLowerCase());
|
|
1443
1795
|
return !entry || Date.now() - entry.at >= MERCHANT_CARD_TTL_MS;
|
|
1444
1796
|
});
|
|
1445
1797
|
if (missing.length > 0) {
|
|
1446
|
-
|
|
1798
|
+
let found = {};
|
|
1799
|
+
try {
|
|
1800
|
+
found = await Promise.race([
|
|
1801
|
+
getKnownMerchantsFromLogs(config.userId, token, missing, allLogs),
|
|
1802
|
+
new Promise((resolve) => setTimeout(() => resolve({}), 1e4))
|
|
1803
|
+
]);
|
|
1804
|
+
} catch {
|
|
1805
|
+
found = {};
|
|
1806
|
+
}
|
|
1447
1807
|
for (const [mName, card] of Object.entries(found)) {
|
|
1448
1808
|
merchantCardCache.set(mName.toLowerCase(), { card, at: Date.now() });
|
|
1449
1809
|
}
|
|
1450
1810
|
}
|
|
1451
1811
|
}
|
|
1812
|
+
const lastRoutedMap = /* @__PURE__ */ new Map();
|
|
1813
|
+
for (const log of allLogs) {
|
|
1814
|
+
const mName = log.model_name;
|
|
1815
|
+
const chId = Number(log.channel);
|
|
1816
|
+
const ts = Number(log.created_at) || 0;
|
|
1817
|
+
if (mName && chId > 0 && ts > 0 && !lastRoutedMap.has(mName.toLowerCase())) {
|
|
1818
|
+
lastRoutedMap.set(mName.toLowerCase(), ts);
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1452
1821
|
const dshSet = new Set(dshConfiguredModels);
|
|
1453
|
-
|
|
1822
|
+
let models = modelIds.map((mId) => {
|
|
1454
1823
|
const meta = resolveModelMeta(mId);
|
|
1455
1824
|
const cacheEntry = merchantCardCache.get(mId.toLowerCase());
|
|
1456
1825
|
const cachedCard = cacheEntry && Date.now() - cacheEntry.at < MERCHANT_CARD_TTL_MS ? cacheEntry.card : void 0;
|
|
1826
|
+
const routedAt = lastRoutedMap.get(mId.toLowerCase());
|
|
1457
1827
|
return {
|
|
1458
1828
|
model_name: mId,
|
|
1459
1829
|
brand: meta.brand,
|
|
@@ -1463,60 +1833,125 @@ function apply(ctx) {
|
|
|
1463
1833
|
hasReasoning: Boolean(meta.reasoningEfforts || meta.thinkingFormat),
|
|
1464
1834
|
inDsh: dshSet.has(mId),
|
|
1465
1835
|
merchant: cachedCard,
|
|
1466
|
-
probeStatus: cachedCard ? "success" : "idle"
|
|
1836
|
+
probeStatus: cachedCard ? "success" : "idle",
|
|
1837
|
+
lastRoutedAt: routedAt,
|
|
1838
|
+
lastRoutedText: routedAt ? formatRelativeTime(routedAt) : void 0
|
|
1467
1839
|
};
|
|
1468
1840
|
});
|
|
1469
|
-
const
|
|
1841
|
+
const resolvedTokenId = pins.length > 0 ? await resolveTokenId(config) : null;
|
|
1842
|
+
models = overlayPinsOnModels(models, pins, resolvedTokenId);
|
|
1843
|
+
const rePointTargets = models.filter(
|
|
1844
|
+
(m) => m.pinStatus === "pin_elsewhere" && m.pinTokenMatched === true && m.pinnedChannelId && m.pinnedChannelId > 0
|
|
1845
|
+
).map((m) => ({ modelName: m.model_name, channelId: m.pinnedChannelId }));
|
|
1846
|
+
if (rePointTargets.length > 0 && config.userId && token) {
|
|
1847
|
+
try {
|
|
1848
|
+
await Promise.race([
|
|
1849
|
+
(async () => {
|
|
1850
|
+
for (let i = 0; i < rePointTargets.length; i += 4) {
|
|
1851
|
+
const batch = rePointTargets.slice(i, i + 4);
|
|
1852
|
+
await Promise.all(
|
|
1853
|
+
batch.map(async ({ modelName, channelId }) => {
|
|
1854
|
+
try {
|
|
1855
|
+
const pinnedCard = await fetchChannelDetails(
|
|
1856
|
+
channelId,
|
|
1857
|
+
config.userId,
|
|
1858
|
+
token,
|
|
1859
|
+
modelName
|
|
1860
|
+
);
|
|
1861
|
+
if (pinnedCard && Number(pinnedCard.channel_id) === Number(channelId)) {
|
|
1862
|
+
merchantCardCache.set(modelName.toLowerCase(), { card: pinnedCard, at: Date.now() });
|
|
1863
|
+
}
|
|
1864
|
+
} catch {
|
|
1865
|
+
}
|
|
1866
|
+
})
|
|
1867
|
+
);
|
|
1868
|
+
}
|
|
1869
|
+
})(),
|
|
1870
|
+
new Promise((resolve) => setTimeout(() => resolve(), 1e4))
|
|
1871
|
+
]);
|
|
1872
|
+
} catch {
|
|
1873
|
+
}
|
|
1874
|
+
models = models.map((m) => {
|
|
1875
|
+
if (m.pinStatus !== "pin_elsewhere" || m.pinTokenMatched !== true) return m;
|
|
1876
|
+
const entry = merchantCardCache.get(m.model_name.toLowerCase());
|
|
1877
|
+
const card = entry && Date.now() - entry.at < MERCHANT_CARD_TTL_MS ? entry.card : void 0;
|
|
1878
|
+
if (card && Number(card.channel_id) === Number(m.pinnedChannelId)) {
|
|
1879
|
+
const pinnedLog = allLogs.find(
|
|
1880
|
+
(l) => l.model_name?.toLowerCase() === m.model_name.toLowerCase() && Number(l.channel) === m.pinnedChannelId
|
|
1881
|
+
);
|
|
1882
|
+
const pinnedAt = pinnedLog ? Number(pinnedLog.created_at) || 0 : void 0;
|
|
1883
|
+
return {
|
|
1884
|
+
...m,
|
|
1885
|
+
merchant: card,
|
|
1886
|
+
pinStatus: "pin_here",
|
|
1887
|
+
probeStatus: "success",
|
|
1888
|
+
lastRoutedAt: pinnedAt,
|
|
1889
|
+
lastRoutedText: pinnedAt ? formatRelativeTime(pinnedAt) : void 0
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
return m;
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
const recentLogs = allLogs.slice(0, 20);
|
|
1470
1896
|
const response = {
|
|
1471
1897
|
config: maskConfig(config),
|
|
1472
1898
|
balance,
|
|
1473
1899
|
models,
|
|
1474
1900
|
dshConfiguredModels,
|
|
1475
|
-
recentLogs
|
|
1901
|
+
recentLogs,
|
|
1902
|
+
pins
|
|
1476
1903
|
};
|
|
1477
1904
|
return sendJson(res, 200, { ok: true, data: response });
|
|
1478
1905
|
}
|
|
1479
1906
|
if (pathname === "/config" && req.method === "POST") {
|
|
1480
1907
|
const body = await parseJsonBody(req);
|
|
1481
|
-
const current = await
|
|
1482
|
-
const rawToken = body.accessToken !== void 0 && body.accessToken !== MASK ? body.accessToken : body.sessionCookie !== void 0 && body.sessionCookie !== MASK ? body.sessionCookie : current.accessToken
|
|
1908
|
+
const current = await configAccess.readConfig();
|
|
1909
|
+
const rawToken = body.accessToken !== void 0 && body.accessToken !== MASK ? body.accessToken : body.sessionCookie !== void 0 && body.sessionCookie !== MASK ? body.sessionCookie : current.accessToken;
|
|
1483
1910
|
const newApiKey = body.apiKey !== void 0 && body.apiKey !== MASK ? body.apiKey : current.apiKey;
|
|
1911
|
+
const credChanged = newApiKey !== current.apiKey || rawToken !== (current.accessToken || "") || body.userId !== void 0 && body.userId !== MASK && body.userId !== current.userId;
|
|
1912
|
+
if (credChanged) {
|
|
1913
|
+
tokenResolveCache = null;
|
|
1914
|
+
}
|
|
1484
1915
|
const updated = {
|
|
1485
1916
|
baseURL: body.baseURL !== void 0 ? body.baseURL : current.baseURL,
|
|
1486
1917
|
apiKey: newApiKey,
|
|
1487
1918
|
accessToken: rawToken,
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
activeModels: Array.isArray(body.activeModels) ? body.activeModels : current.activeModels,
|
|
1491
|
-
customBaseURL: body.customBaseURL !== void 0 ? body.customBaseURL : current.customBaseURL
|
|
1919
|
+
userId: body.userId !== void 0 && body.userId !== MASK ? body.userId : current.userId,
|
|
1920
|
+
activeModels: Array.isArray(body.activeModels) ? body.activeModels : current.activeModels
|
|
1492
1921
|
};
|
|
1493
1922
|
const balance = await fetchBalance(updated.baseURL, updated.apiKey, updated.userId, updated.accessToken);
|
|
1494
1923
|
if (balance?.userId) {
|
|
1495
1924
|
updated.userId = String(balance.userId);
|
|
1496
1925
|
}
|
|
1497
|
-
|
|
1926
|
+
const credWrites = {};
|
|
1927
|
+
if (updated.apiKey !== current.apiKey) credWrites.apiKey = updated.apiKey;
|
|
1928
|
+
if ((updated.accessToken || "") !== (current.accessToken || "")) credWrites.accessToken = updated.accessToken;
|
|
1929
|
+
if ((updated.userId || "") !== (current.userId || "")) credWrites.userId = updated.userId;
|
|
1930
|
+
if (Object.keys(credWrites).length > 0) {
|
|
1931
|
+
await configAccess.writeConfig(credWrites);
|
|
1932
|
+
}
|
|
1498
1933
|
if (updated.activeModels.length > 0) {
|
|
1499
|
-
await
|
|
1934
|
+
await configAccess.syncModels(updated.baseURL, updated.activeModels);
|
|
1500
1935
|
}
|
|
1501
1936
|
return sendJson(res, 200, { ok: true, config: maskConfig(updated), balance });
|
|
1502
1937
|
}
|
|
1503
1938
|
if (pathname === "/balance" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1504
|
-
const config = await
|
|
1505
|
-
const token = config.accessToken ||
|
|
1939
|
+
const config = await configAccess.readConfig();
|
|
1940
|
+
const token = config.accessToken || "";
|
|
1506
1941
|
const balance = await fetchBalance(config.baseURL, config.apiKey, config.userId, token);
|
|
1507
1942
|
const recentLogs = await fetchRecentLogs(config.userId, token, 20);
|
|
1508
1943
|
return sendJson(res, 200, { ok: true, balance, recentLogs });
|
|
1509
1944
|
}
|
|
1510
1945
|
if (pathname === "/logs" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1511
|
-
const config = await
|
|
1512
|
-
const token = config.accessToken ||
|
|
1946
|
+
const config = await configAccess.readConfig();
|
|
1947
|
+
const token = config.accessToken || "";
|
|
1513
1948
|
const recentLogs = await fetchRecentLogs(config.userId, token, 30);
|
|
1514
1949
|
return sendJson(res, 200, { ok: true, logs: recentLogs });
|
|
1515
1950
|
}
|
|
1516
1951
|
if (pathname === "/probe" && req.method === "POST") {
|
|
1517
1952
|
const body = await parseJsonBody(req);
|
|
1518
|
-
const config = await
|
|
1519
|
-
const token = config.accessToken ||
|
|
1953
|
+
const config = await configAccess.readConfig();
|
|
1954
|
+
const token = config.accessToken || "";
|
|
1520
1955
|
const modelName = body.modelName;
|
|
1521
1956
|
if (modelName && modelName !== "all") {
|
|
1522
1957
|
const result = await probeSingleModel(config.baseURL, config.apiKey, config.userId, token, modelName);
|
|
@@ -1530,7 +1965,7 @@ function apply(ctx) {
|
|
|
1530
1965
|
modelIds = await fetchTokenModels(config.baseURL, config.apiKey);
|
|
1531
1966
|
}
|
|
1532
1967
|
if (modelIds.length === 0) {
|
|
1533
|
-
modelIds =
|
|
1968
|
+
modelIds = await configAccess.getDshConfiguredModels();
|
|
1534
1969
|
}
|
|
1535
1970
|
const results = [];
|
|
1536
1971
|
for (const m of modelIds) {
|
|
@@ -1544,27 +1979,285 @@ function apply(ctx) {
|
|
|
1544
1979
|
}
|
|
1545
1980
|
if (pathname === "/sync-models" && req.method === "POST") {
|
|
1546
1981
|
const body = await parseJsonBody(req);
|
|
1547
|
-
|
|
1982
|
+
await configAccess.ensureMigrated();
|
|
1983
|
+
const config = await configAccess.readConfig();
|
|
1548
1984
|
const modelIds = Array.isArray(body.modelIds) ? body.modelIds : [];
|
|
1549
1985
|
const baseURL = body.baseURL || config.baseURL;
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
await savePluginConfig(config);
|
|
1553
|
-
await syncToDshSettings(baseURL, modelIds);
|
|
1554
|
-
const dshConfiguredModels = await getDshConfiguredModels();
|
|
1986
|
+
await configAccess.syncModels(baseURL, modelIds);
|
|
1987
|
+
const dshConfiguredModels = await configAccess.getDshConfiguredModels();
|
|
1555
1988
|
return sendJson(res, 200, { ok: true, dshConfiguredModels });
|
|
1556
1989
|
}
|
|
1990
|
+
if (pathname === "/pin" && req.method === "POST") {
|
|
1991
|
+
const body = await parseJsonBody(req);
|
|
1992
|
+
const config = await configAccess.readConfig();
|
|
1993
|
+
const modelName = String(body.modelName || "").trim();
|
|
1994
|
+
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
1995
|
+
const { userId, token } = webAuthOf(config);
|
|
1996
|
+
if (!userId || !token) {
|
|
1997
|
+
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" });
|
|
1998
|
+
}
|
|
1999
|
+
let card = cachedMerchantOf(modelName);
|
|
2000
|
+
let tokenId = await resolveTokenId(config);
|
|
2001
|
+
if ((!card || !tokenId) && config.apiKey) {
|
|
2002
|
+
try {
|
|
2003
|
+
const probe = await probeSingleModel(config.baseURL, config.apiKey, userId, token, modelName);
|
|
2004
|
+
if (!tokenId && probe.tokenId && Number(probe.tokenId) > 0) tokenId = Number(probe.tokenId);
|
|
2005
|
+
if (!card && probe.merchant) {
|
|
2006
|
+
card = probe.merchant;
|
|
2007
|
+
merchantCardCache.set(modelName.toLowerCase(), { card, at: Date.now() });
|
|
2008
|
+
}
|
|
2009
|
+
} catch {
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
if (!card) {
|
|
2013
|
+
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" });
|
|
2014
|
+
}
|
|
2015
|
+
if (!tokenId) {
|
|
2016
|
+
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" });
|
|
2017
|
+
}
|
|
2018
|
+
if (!card.channel_id) {
|
|
2019
|
+
return sendJson(res, 400, { ok: false, error: "\u5546\u5BB6\u5361\u7247\u7F3A\u5C11\u6E20\u9053 ID\uFF0C\u8BF7\u91CD\u65B0\u63A2\u6D4B" });
|
|
2020
|
+
}
|
|
2021
|
+
const pinResult = await marketplacePin(userId, token, {
|
|
2022
|
+
token_id: tokenId,
|
|
2023
|
+
channel_id: card.channel_id,
|
|
2024
|
+
model_name: modelName,
|
|
2025
|
+
// 平台默认兜底:渠道异常时自动切换智能优选(不暴露 UI 开关)
|
|
2026
|
+
fallback_to_smart_routing: true
|
|
2027
|
+
});
|
|
2028
|
+
if (!pinResult.ok) {
|
|
2029
|
+
return sendJson(res, 400, { ok: false, error: pinResult.message || "\u56FA\u5B9A\u5931\u8D25" });
|
|
2030
|
+
}
|
|
2031
|
+
tokenResolveCache = { tokenId, at: Date.now() };
|
|
2032
|
+
merchantCardCache.set(modelName.toLowerCase(), {
|
|
2033
|
+
card: { ...card, is_pinned: true, pin_status: "pin_here" },
|
|
2034
|
+
at: Date.now()
|
|
2035
|
+
});
|
|
2036
|
+
const pinList = await fetchMarketplacePins(userId, token);
|
|
2037
|
+
return sendJson(res, 200, { ok: true, message: `\u5DF2\u56FA\u5B9A ${modelName} \u81F3\u5546\u6237 #${card.channel_id}`, pins: pinList, tokenId });
|
|
2038
|
+
}
|
|
2039
|
+
if (pathname === "/unpin" && req.method === "POST") {
|
|
2040
|
+
const body = await parseJsonBody(req);
|
|
2041
|
+
const config = await configAccess.readConfig();
|
|
2042
|
+
const modelName = String(body.modelName || "").trim();
|
|
2043
|
+
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
2044
|
+
const { userId, token } = webAuthOf(config);
|
|
2045
|
+
if (!userId || !token) {
|
|
2046
|
+
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" });
|
|
2047
|
+
}
|
|
2048
|
+
const tokenId = await resolveTokenId(config);
|
|
2049
|
+
if (!tokenId) {
|
|
2050
|
+
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" });
|
|
2051
|
+
}
|
|
2052
|
+
const unpinResult = await marketplaceUnpin(userId, token, { token_id: tokenId, model_name: modelName });
|
|
2053
|
+
if (!unpinResult.ok) {
|
|
2054
|
+
return sendJson(res, 400, { ok: false, error: unpinResult.message || "\u53D6\u6D88\u56FA\u5B9A\u5931\u8D25" });
|
|
2055
|
+
}
|
|
2056
|
+
const card = cachedMerchantOf(modelName);
|
|
2057
|
+
if (card) {
|
|
2058
|
+
merchantCardCache.set(modelName.toLowerCase(), {
|
|
2059
|
+
card: { ...card, is_pinned: false, pin_status: void 0 },
|
|
2060
|
+
at: Date.now()
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
const pinList = await fetchMarketplacePins(userId, token);
|
|
2064
|
+
return sendJson(res, 200, { ok: true, message: `\u5DF2\u53D6\u6D88\u56FA\u5B9A ${modelName}`, pins: pinList, tokenId });
|
|
2065
|
+
}
|
|
2066
|
+
if (pathname === "/disable" && req.method === "POST") {
|
|
2067
|
+
const body = await parseJsonBody(req);
|
|
2068
|
+
const config = await configAccess.readConfig();
|
|
2069
|
+
const modelName = String(body.modelName || "").trim();
|
|
2070
|
+
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
2071
|
+
let card = cachedMerchantOf(modelName);
|
|
2072
|
+
if (!card && config.apiKey) {
|
|
2073
|
+
try {
|
|
2074
|
+
const probe = await probeSingleModel(config.baseURL, config.apiKey, config.userId, config.accessToken || "", modelName);
|
|
2075
|
+
if (probe.merchant) {
|
|
2076
|
+
card = probe.merchant;
|
|
2077
|
+
merchantCardCache.set(modelName.toLowerCase(), { card, at: Date.now() });
|
|
2078
|
+
}
|
|
2079
|
+
} catch {
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
if (!card || !card.channel_id) {
|
|
2083
|
+
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" });
|
|
2084
|
+
}
|
|
2085
|
+
const { userId, token } = webAuthOf(config);
|
|
2086
|
+
if (!userId || !token) {
|
|
2087
|
+
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" });
|
|
2088
|
+
}
|
|
2089
|
+
const disableResult = await marketplaceDisableChannel(userId, token, card.channel_id, modelName);
|
|
2090
|
+
if (!disableResult.ok) {
|
|
2091
|
+
return sendJson(res, 400, { ok: false, error: disableResult.message || "\u7981\u7528\u5931\u8D25" });
|
|
2092
|
+
}
|
|
2093
|
+
merchantCardCache.set(modelName.toLowerCase(), {
|
|
2094
|
+
card: { ...card, user_channel_disabled: true },
|
|
2095
|
+
at: Date.now()
|
|
2096
|
+
});
|
|
2097
|
+
return sendJson(res, 200, { ok: true, message: `\u5DF2\u7981\u7528\u5546\u6237 #${card.channel_id} \u5BF9\u8BE5\u6A21\u578B\u7684\u670D\u52A1` });
|
|
2098
|
+
}
|
|
2099
|
+
if (pathname === "/restore" && req.method === "POST") {
|
|
2100
|
+
const body = await parseJsonBody(req);
|
|
2101
|
+
const config = await configAccess.readConfig();
|
|
2102
|
+
const modelName = String(body.modelName || "").trim();
|
|
2103
|
+
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
2104
|
+
let card = cachedMerchantOf(modelName);
|
|
2105
|
+
if (!card && config.apiKey) {
|
|
2106
|
+
try {
|
|
2107
|
+
const probe = await probeSingleModel(config.baseURL, config.apiKey, config.userId, config.accessToken || "", modelName);
|
|
2108
|
+
if (probe.merchant) {
|
|
2109
|
+
card = probe.merchant;
|
|
2110
|
+
merchantCardCache.set(modelName.toLowerCase(), { card, at: Date.now() });
|
|
2111
|
+
}
|
|
2112
|
+
} catch {
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
if (!card || !card.channel_id) {
|
|
2116
|
+
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" });
|
|
2117
|
+
}
|
|
2118
|
+
const { userId, token } = webAuthOf(config);
|
|
2119
|
+
if (!userId || !token) {
|
|
2120
|
+
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" });
|
|
2121
|
+
}
|
|
2122
|
+
const restoreResult = await marketplaceRestoreChannel(userId, token, card.channel_id, modelName);
|
|
2123
|
+
if (!restoreResult.ok) {
|
|
2124
|
+
return sendJson(res, 400, { ok: false, error: restoreResult.message || "\u6062\u590D\u5931\u8D25" });
|
|
2125
|
+
}
|
|
2126
|
+
merchantCardCache.set(modelName.toLowerCase(), {
|
|
2127
|
+
card: { ...card, user_channel_disabled: false },
|
|
2128
|
+
at: Date.now()
|
|
2129
|
+
});
|
|
2130
|
+
return sendJson(res, 200, { ok: true, message: `\u5DF2\u6062\u590D\u5546\u6237 #${card.channel_id} \u5BF9\u8BE5\u6A21\u578B\u7684\u670D\u52A1` });
|
|
2131
|
+
}
|
|
1557
2132
|
if (pathname === "/price-fluctuation" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1558
|
-
const config = await
|
|
1559
|
-
const token = config.accessToken ||
|
|
2133
|
+
const config = await configAccess.readConfig();
|
|
2134
|
+
const token = config.accessToken || "";
|
|
1560
2135
|
if (!token || !config.userId) {
|
|
1561
2136
|
return sendJson(res, 200, { ok: true, data: { pendingCount: 0, unseenCount: 0, totalCount: 0, hasAuth: false, authError: false, updatedAt: Date.now() } });
|
|
1562
2137
|
}
|
|
1563
|
-
const result = await fetchPriceFluctuation(config.userId, token
|
|
2138
|
+
const result = await fetchPriceFluctuation(config.userId, token);
|
|
1564
2139
|
const { notices, ...counts } = result;
|
|
1565
2140
|
const hasAuth = !counts.authError;
|
|
1566
2141
|
return sendJson(res, 200, { ok: true, data: { pendingCount: counts.pendingCount, unseenCount: counts.unseenCount, totalCount: counts.totalCount, hasAuth, authError: Boolean(counts.authError), updatedAt: Date.now() } });
|
|
1567
2142
|
}
|
|
2143
|
+
if (pathname === "/pins" && (req.method === "GET" || req.method === "HEAD")) {
|
|
2144
|
+
const config = await configAccess.readConfig();
|
|
2145
|
+
const { userId, token } = webAuthOf(config);
|
|
2146
|
+
if (!userId || !token) {
|
|
2147
|
+
return sendJson(res, 200, { ok: true, pins: [] });
|
|
2148
|
+
}
|
|
2149
|
+
try {
|
|
2150
|
+
const pins = await fetchMarketplacePins(userId, token);
|
|
2151
|
+
return sendJson(res, 200, { ok: true, pins });
|
|
2152
|
+
} catch (err) {
|
|
2153
|
+
console.warn("[dsh-a6api] GET /pins error:", err);
|
|
2154
|
+
return sendJson(res, 200, { ok: true, pins: [] });
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
if (pathname === "/catalog" && (req.method === "GET" || req.method === "HEAD")) {
|
|
2158
|
+
return sendJson(res, 200, { ok: true, catalog: getCatalog() });
|
|
2159
|
+
}
|
|
2160
|
+
if (pathname === "/catalog/clear" && req.method === "POST") {
|
|
2161
|
+
await clearCatalog();
|
|
2162
|
+
return sendJson(res, 200, { ok: true });
|
|
2163
|
+
}
|
|
2164
|
+
if (pathname === "/catalog/fetch-models" && req.method === "POST") {
|
|
2165
|
+
const config = await configAccess.readConfig();
|
|
2166
|
+
const { userId, token } = webAuthOf(config);
|
|
2167
|
+
let result;
|
|
2168
|
+
try {
|
|
2169
|
+
result = await fetchMarketplaceModels(userId, token);
|
|
2170
|
+
} catch (err) {
|
|
2171
|
+
return sendJson(res, 400, { ok: false, error: err?.message || "\u83B7\u53D6\u5E02\u573A\u6A21\u578B\u5931\u8D25" });
|
|
2172
|
+
}
|
|
2173
|
+
const models = result.models;
|
|
2174
|
+
const before = new Set(getCatalog().map((e) => e.id.toLowerCase()));
|
|
2175
|
+
let added = 0;
|
|
2176
|
+
for (const m of models) {
|
|
2177
|
+
if (!before.has(m.id.toLowerCase())) added++;
|
|
2178
|
+
}
|
|
2179
|
+
await upsertCatalogEntries(
|
|
2180
|
+
models.map((m) => ({ id: m.id, brand: m.brand, reasoningEfforts: m.reasoningEfforts }))
|
|
2181
|
+
);
|
|
2182
|
+
return sendJson(res, 200, {
|
|
2183
|
+
ok: true,
|
|
2184
|
+
total: models.length,
|
|
2185
|
+
added,
|
|
2186
|
+
failedPages: result.failedPages
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2189
|
+
if (pathname === "/catalog/query-openrouter" && req.method === "POST") {
|
|
2190
|
+
const body = await parseJsonBody(req);
|
|
2191
|
+
const catalog = getCatalog();
|
|
2192
|
+
const modelIds = Array.isArray(body.modelIds) && body.modelIds.length > 0 ? body.modelIds.map((s) => String(s)) : catalog.map((e) => e.id);
|
|
2193
|
+
if (modelIds.length === 0) {
|
|
2194
|
+
return sendJson(res, 400, { ok: false, error: "\u76EE\u5F55\u4E3A\u7A7A\uFF0C\u8BF7\u5148\u300C\u4ECE A6API \u83B7\u53D6\u5E02\u573A\u6A21\u578B\u300D" });
|
|
2195
|
+
}
|
|
2196
|
+
const result = await queryOpenRouter(modelIds);
|
|
2197
|
+
return sendJson(res, 200, {
|
|
2198
|
+
ok: true,
|
|
2199
|
+
updated: result.updated.length,
|
|
2200
|
+
notFound: result.notFound
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
if (pathname === "/catalog/update" && req.method === "POST") {
|
|
2204
|
+
const body = await parseJsonBody(req);
|
|
2205
|
+
const id = String(body.id || "").trim();
|
|
2206
|
+
if (!id) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B ID" });
|
|
2207
|
+
const patch = {};
|
|
2208
|
+
if (body.name !== void 0) {
|
|
2209
|
+
if (body.name === null) {
|
|
2210
|
+
patch.name = null;
|
|
2211
|
+
} else if (typeof body.name !== "string") {
|
|
2212
|
+
return sendJson(res, 400, { ok: false, error: "name \u5FC5\u987B\u662F\u5B57\u7B26\u4E32" });
|
|
2213
|
+
} else {
|
|
2214
|
+
const name2 = body.name.trim();
|
|
2215
|
+
patch.name = name2 || null;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
for (const key of ["contextWindow", "maxTokens"]) {
|
|
2219
|
+
if (body[key] === null) {
|
|
2220
|
+
patch[key] = null;
|
|
2221
|
+
} else if (body[key] !== void 0) {
|
|
2222
|
+
const n = Number(body[key]);
|
|
2223
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
2224
|
+
return sendJson(res, 400, { ok: false, error: `${key} \u5FC5\u987B\u662F\u6B63\u6574\u6570` });
|
|
2225
|
+
}
|
|
2226
|
+
patch[key] = n;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
if (body.input !== void 0) {
|
|
2230
|
+
if (body.input === null) {
|
|
2231
|
+
patch.input = null;
|
|
2232
|
+
} else if (!Array.isArray(body.input)) {
|
|
2233
|
+
return sendJson(res, 400, { ok: false, error: "input \u5FC5\u987B\u662F\u6570\u7EC4" });
|
|
2234
|
+
} else {
|
|
2235
|
+
const mods = body.input.filter((m) => m === "text" || m === "image");
|
|
2236
|
+
patch.input = mods.length > 0 ? mods : null;
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
if (body.reasoningEfforts !== void 0) {
|
|
2240
|
+
if (body.reasoningEfforts === null) {
|
|
2241
|
+
patch.reasoningEfforts = null;
|
|
2242
|
+
} else {
|
|
2243
|
+
const v = validateReasoningEfforts(body.reasoningEfforts);
|
|
2244
|
+
if (!v.ok) return sendJson(res, 400, { ok: false, error: v.error });
|
|
2245
|
+
patch.reasoningEfforts = v.value;
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
const entry = await updateCatalogEntry(id, patch);
|
|
2249
|
+
if (!entry) return sendJson(res, 404, { ok: false, error: "\u76EE\u5F55\u4E2D\u4E0D\u5B58\u5728\u8BE5\u6A21\u578B" });
|
|
2250
|
+
try {
|
|
2251
|
+
const config = await configAccess.readConfig();
|
|
2252
|
+
const dshModels = await configAccess.getDshConfiguredModels();
|
|
2253
|
+
if (dshModels.some((m) => m.toLowerCase() === entry.id.toLowerCase()) && config.activeModels.length > 0) {
|
|
2254
|
+
await configAccess.syncModels(config.baseURL, config.activeModels);
|
|
2255
|
+
}
|
|
2256
|
+
} catch (err) {
|
|
2257
|
+
console.warn("[dsh-a6api] catalog update: resync settings failed:", err?.message || err);
|
|
2258
|
+
}
|
|
2259
|
+
return sendJson(res, 200, { ok: true, entry });
|
|
2260
|
+
}
|
|
1568
2261
|
return sendJson(res, 404, { ok: false, error: "Not found" });
|
|
1569
2262
|
} catch (err) {
|
|
1570
2263
|
console.error("[dsh-a6api] API error:", err);
|
|
@@ -1579,19 +2272,27 @@ function apply(ctx) {
|
|
|
1579
2272
|
}
|
|
1580
2273
|
}
|
|
1581
2274
|
export {
|
|
1582
|
-
|
|
2275
|
+
A6API_CRED_REF,
|
|
2276
|
+
A6API_TOKEN_REF,
|
|
2277
|
+
A6API_USER_REF,
|
|
1583
2278
|
apply,
|
|
2279
|
+
clearCatalog,
|
|
2280
|
+
createConfigAccess,
|
|
1584
2281
|
fetchBalance,
|
|
1585
2282
|
fetchChannelDetails,
|
|
2283
|
+
fetchMarketplaceModels,
|
|
2284
|
+
fetchMarketplacePins,
|
|
1586
2285
|
fetchRecentLogs,
|
|
1587
2286
|
fetchTokenModels,
|
|
2287
|
+
getCatalog,
|
|
2288
|
+
getCatalogEntry,
|
|
1588
2289
|
getKnownMerchantsFromLogs,
|
|
2290
|
+
inferBrand,
|
|
1589
2291
|
inject,
|
|
1590
2292
|
name,
|
|
1591
2293
|
probeSingleModel,
|
|
1592
|
-
|
|
2294
|
+
queryOpenRouter,
|
|
1593
2295
|
resolveModelMeta,
|
|
1594
|
-
|
|
1595
|
-
syncToDshSettings
|
|
2296
|
+
updateCatalogEntry
|
|
1596
2297
|
};
|
|
1597
2298
|
//# sourceMappingURL=index.js.map
|