@lynn123411/dsh-a6api 1.2.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 +10 -3
- package/lib/client.js +2154 -284
- package/lib/client.js.map +4 -4
- package/lib/index.js +1317 -639
- 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 +2 -0
- package/lib/types/server/sync.d.ts +28 -9
- package/lib/types/types.d.ts +71 -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
|
};
|
|
@@ -1082,77 +1058,56 @@ async function getKnownMerchantsFromLogs(userId, accessToken, modelNames = [], l
|
|
|
1082
1058
|
}
|
|
1083
1059
|
|
|
1084
1060
|
// src/server/sync.ts
|
|
1085
|
-
import * as
|
|
1086
|
-
import * as
|
|
1087
|
-
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";
|
|
1088
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";
|
|
1089
1072
|
async function atomicWriteFile(filePath, content, mode = 384) {
|
|
1090
|
-
const dir =
|
|
1091
|
-
await
|
|
1092
|
-
const tmpPath =
|
|
1093
|
-
await
|
|
1094
|
-
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);
|
|
1095
1078
|
}
|
|
1096
|
-
function
|
|
1097
|
-
return process.env.DSH_HOME ||
|
|
1079
|
+
function dshHome2() {
|
|
1080
|
+
return process.env.DSH_HOME || path2.join(os2.homedir(), ".dsh");
|
|
1098
1081
|
}
|
|
1099
|
-
function
|
|
1100
|
-
return
|
|
1082
|
+
function legacyConfigFile() {
|
|
1083
|
+
return path2.join(dshHome2(), LEGACY_CONFIG_NAME);
|
|
1101
1084
|
}
|
|
1102
1085
|
function credentialsFile() {
|
|
1103
|
-
return
|
|
1086
|
+
return path2.join(dshHome2(), ".credentials.yaml");
|
|
1104
1087
|
}
|
|
1105
1088
|
function settingsFile() {
|
|
1106
|
-
return
|
|
1089
|
+
return path2.join(dshHome2(), "settings.yaml");
|
|
1107
1090
|
}
|
|
1108
|
-
|
|
1109
|
-
const filePath = configFile();
|
|
1110
|
-
let raw = "";
|
|
1091
|
+
function getCredentials(ctx) {
|
|
1111
1092
|
try {
|
|
1112
|
-
|
|
1093
|
+
if (ctx && typeof ctx.get === "function") return ctx.get("credentials");
|
|
1113
1094
|
} catch {
|
|
1114
|
-
const apiKey = await readCredentialKey(A6API_CRED_REF) || "";
|
|
1115
|
-
return {
|
|
1116
|
-
baseURL: "https://api.a6api.com",
|
|
1117
|
-
apiKey,
|
|
1118
|
-
userId: "",
|
|
1119
|
-
activeModels: []
|
|
1120
|
-
};
|
|
1121
1095
|
}
|
|
1096
|
+
return void 0;
|
|
1097
|
+
}
|
|
1098
|
+
function getSettings(ctx) {
|
|
1122
1099
|
try {
|
|
1123
|
-
|
|
1124
|
-
const apiKey = parsed.apiKey || await readCredentialKey(A6API_CRED_REF) || "";
|
|
1125
|
-
const accessToken = parsed.accessToken || parsed.systemAccessToken || parsed.sessionCookie || "";
|
|
1126
|
-
return {
|
|
1127
|
-
baseURL: parsed.baseURL || "https://api.a6api.com",
|
|
1128
|
-
apiKey,
|
|
1129
|
-
accessToken,
|
|
1130
|
-
userId: parsed.userId || "",
|
|
1131
|
-
sessionCookie: accessToken,
|
|
1132
|
-
activeModels: Array.isArray(parsed.activeModels) ? parsed.activeModels : [],
|
|
1133
|
-
customBaseURL: parsed.customBaseURL
|
|
1134
|
-
};
|
|
1100
|
+
if (ctx && typeof ctx.get === "function") return ctx.get("settings");
|
|
1135
1101
|
} catch {
|
|
1136
|
-
return {
|
|
1137
|
-
baseURL: "https://api.a6api.com",
|
|
1138
|
-
apiKey: "",
|
|
1139
|
-
accessToken: "",
|
|
1140
|
-
userId: "",
|
|
1141
|
-
activeModels: []
|
|
1142
|
-
};
|
|
1143
1102
|
}
|
|
1103
|
+
return void 0;
|
|
1144
1104
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
const { apiKey: _apiKey, ...safeConfig } = config;
|
|
1148
|
-
await atomicWriteFile(filePath, JSON.stringify(safeConfig, null, 2));
|
|
1149
|
-
if (config.apiKey && config.apiKey.trim()) {
|
|
1150
|
-
await writeCredentialKey(A6API_CRED_REF, config.apiKey.trim());
|
|
1151
|
-
}
|
|
1105
|
+
function stripV1(baseURL) {
|
|
1106
|
+
return baseURL.replace(/\/v1\/?$/, "");
|
|
1152
1107
|
}
|
|
1153
1108
|
async function readCredentialKey(refKey) {
|
|
1154
1109
|
try {
|
|
1155
|
-
const yaml = await
|
|
1110
|
+
const yaml = await fsp2.readFile(credentialsFile(), "utf8");
|
|
1156
1111
|
let inRefs = false;
|
|
1157
1112
|
for (const line of yaml.split(/\r?\n/)) {
|
|
1158
1113
|
const trimmed = line.trim();
|
|
@@ -1169,7 +1124,7 @@ async function readCredentialKey(refKey) {
|
|
|
1169
1124
|
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
1170
1125
|
val = val.slice(1, -1);
|
|
1171
1126
|
}
|
|
1172
|
-
return val;
|
|
1127
|
+
return val || null;
|
|
1173
1128
|
}
|
|
1174
1129
|
}
|
|
1175
1130
|
} catch {
|
|
@@ -1180,14 +1135,14 @@ async function writeCredentialKey(refKey, value) {
|
|
|
1180
1135
|
const cFile = credentialsFile();
|
|
1181
1136
|
let yaml = "";
|
|
1182
1137
|
try {
|
|
1183
|
-
yaml = await
|
|
1138
|
+
yaml = await fsp2.readFile(cFile, "utf8");
|
|
1184
1139
|
} catch {
|
|
1185
1140
|
yaml = "version: 1\nrefs:\n";
|
|
1186
1141
|
}
|
|
1187
1142
|
const lines = yaml.split(/\r?\n/);
|
|
1188
1143
|
let inRefs = false;
|
|
1189
1144
|
let refsLineIdx = -1;
|
|
1190
|
-
let
|
|
1145
|
+
let foundIdx = -1;
|
|
1191
1146
|
for (let i = 0; i < lines.length; i++) {
|
|
1192
1147
|
const line = lines[i];
|
|
1193
1148
|
const trimmed = line.trim();
|
|
@@ -1205,58 +1160,81 @@ async function writeCredentialKey(refKey, value) {
|
|
|
1205
1160
|
if (inRefs) {
|
|
1206
1161
|
const m = /^([A-Za-z0-9_.\-]+):/.exec(trimmed);
|
|
1207
1162
|
if (m && m[1] === refKey) {
|
|
1208
|
-
|
|
1209
|
-
found = true;
|
|
1163
|
+
foundIdx = i;
|
|
1210
1164
|
break;
|
|
1211
1165
|
}
|
|
1212
1166
|
}
|
|
1213
1167
|
}
|
|
1214
|
-
if (
|
|
1215
|
-
if (
|
|
1216
|
-
|
|
1168
|
+
if (value === "") {
|
|
1169
|
+
if (foundIdx >= 0) lines.splice(foundIdx, 1);
|
|
1170
|
+
} else {
|
|
1171
|
+
if (foundIdx >= 0) {
|
|
1172
|
+
lines[foundIdx] = ` ${refKey}: ${JSON.stringify(value)}`;
|
|
1217
1173
|
} else {
|
|
1218
|
-
|
|
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
|
+
}
|
|
1219
1179
|
}
|
|
1220
1180
|
}
|
|
1221
1181
|
await atomicWriteFile(cFile, lines.join("\n"), 384);
|
|
1222
1182
|
}
|
|
1223
|
-
async function
|
|
1224
|
-
const sFile = settingsFile();
|
|
1225
|
-
let yaml = "";
|
|
1183
|
+
async function readRawConfiguredModels() {
|
|
1226
1184
|
try {
|
|
1227
|
-
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;
|
|
1228
1212
|
} catch {
|
|
1229
|
-
|
|
1213
|
+
return [];
|
|
1230
1214
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
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, "");
|
|
1245
1231
|
}
|
|
1246
1232
|
}
|
|
1247
|
-
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
` displayName: A6API`,
|
|
1253
|
-
` apiKeyEnv: ${A6API_CRED_REF}`,
|
|
1254
|
-
` api: openai-completions`,
|
|
1255
|
-
` baseURL: ${dshBaseUrl}`,
|
|
1256
|
-
` models:`,
|
|
1257
|
-
...modelEntries.length > 0 ? modelEntries : [` []`]
|
|
1258
|
-
];
|
|
1259
|
-
const lines = yaml.split(/\r?\n/);
|
|
1233
|
+
} catch {
|
|
1234
|
+
}
|
|
1235
|
+
return "";
|
|
1236
|
+
}
|
|
1237
|
+
function scanA6apiBlockRange(lines) {
|
|
1260
1238
|
let inLlm = false;
|
|
1261
1239
|
let inProviders = false;
|
|
1262
1240
|
let inA6 = false;
|
|
@@ -1302,6 +1280,46 @@ async function syncToDshSettings(baseURL, modelIds) {
|
|
|
1302
1280
|
inA6 = false;
|
|
1303
1281
|
}
|
|
1304
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);
|
|
1305
1323
|
if (a6Start >= 0) {
|
|
1306
1324
|
lines.splice(a6Start, a6End - a6Start, ...a6apiBlockLines);
|
|
1307
1325
|
} else if (providersLineIdx >= 0) {
|
|
@@ -1313,38 +1331,305 @@ async function syncToDshSettings(baseURL, modelIds) {
|
|
|
1313
1331
|
}
|
|
1314
1332
|
await atomicWriteFile(sFile, lines.join("\n"), 420);
|
|
1315
1333
|
}
|
|
1316
|
-
async function
|
|
1334
|
+
async function removeRawA6apiBlock() {
|
|
1335
|
+
const sFile = settingsFile();
|
|
1336
|
+
let yaml = "";
|
|
1317
1337
|
try {
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
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;
|
|
1330
1356
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
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;
|
|
1334
1370
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
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 : "";
|
|
1338
1436
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
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
|
+
}
|
|
1342
1453
|
}
|
|
1454
|
+
} catch (err) {
|
|
1455
|
+
console.warn("[dsh-a6api] settings.get(llm-pi-ai) failed:", err?.message || err);
|
|
1343
1456
|
}
|
|
1344
|
-
return
|
|
1345
|
-
}
|
|
1346
|
-
|
|
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" };
|
|
1347
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 };
|
|
1348
1633
|
}
|
|
1349
1634
|
|
|
1350
1635
|
// src/index.ts
|
|
@@ -1356,11 +1641,10 @@ function maskConfig(c) {
|
|
|
1356
1641
|
return {
|
|
1357
1642
|
...c,
|
|
1358
1643
|
apiKey: c.apiKey ? MASK : "",
|
|
1359
|
-
accessToken: c.accessToken
|
|
1360
|
-
sessionCookie: "",
|
|
1644
|
+
accessToken: c.accessToken ? MASK : "",
|
|
1361
1645
|
userId: c.userId ? MASK : "",
|
|
1362
1646
|
hasApiKey: Boolean(c.apiKey),
|
|
1363
|
-
hasToken: Boolean(c.accessToken
|
|
1647
|
+
hasToken: Boolean(c.accessToken)
|
|
1364
1648
|
};
|
|
1365
1649
|
}
|
|
1366
1650
|
function sendJson(res, status, body) {
|
|
@@ -1396,7 +1680,68 @@ async function parseJsonBody(req) {
|
|
|
1396
1680
|
}
|
|
1397
1681
|
var merchantCardCache = /* @__PURE__ */ new Map();
|
|
1398
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
|
+
}
|
|
1399
1742
|
function apply(ctx) {
|
|
1743
|
+
const configAccess = createConfigAccess(ctx);
|
|
1744
|
+
void configAccess.ensureMigrated();
|
|
1400
1745
|
const webServer = ctx.webServer || (ctx.get ? ctx.get("webServer") : null);
|
|
1401
1746
|
if (webServer && typeof webServer.register === "function") {
|
|
1402
1747
|
ctx.effect(() => {
|
|
@@ -1410,24 +1755,29 @@ function apply(ctx) {
|
|
|
1410
1755
|
res.writeHead(204);
|
|
1411
1756
|
return res.end();
|
|
1412
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
|
+
}
|
|
1413
1761
|
try {
|
|
1414
1762
|
if (pathname === "/state" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1415
|
-
const config = await
|
|
1416
|
-
const token = config.accessToken ||
|
|
1417
|
-
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
|
+
]);
|
|
1418
1772
|
if (balance?.userId && String(balance.userId) !== config.userId) {
|
|
1773
|
+
tokenResolveCache = null;
|
|
1419
1774
|
config.userId = String(balance.userId);
|
|
1420
|
-
await
|
|
1421
|
-
}
|
|
1422
|
-
const dshConfiguredModels = await getDshConfiguredModels();
|
|
1423
|
-
let modelIds = [];
|
|
1424
|
-
if (config.apiKey) {
|
|
1425
|
-
modelIds = await fetchTokenModels(config.baseURL, config.apiKey);
|
|
1775
|
+
await configAccess.writeConfig({ userId: config.userId });
|
|
1426
1776
|
}
|
|
1777
|
+
let modelIds = modelIdsRaw;
|
|
1427
1778
|
if (modelIds.length === 0) {
|
|
1428
1779
|
modelIds = [
|
|
1429
1780
|
.../* @__PURE__ */ new Set([
|
|
1430
|
-
...config.activeModels,
|
|
1431
1781
|
...dshConfiguredModels,
|
|
1432
1782
|
"gpt-5.6-sol",
|
|
1433
1783
|
"gpt-5.6-terra",
|
|
@@ -1438,7 +1788,6 @@ function apply(ctx) {
|
|
|
1438
1788
|
])
|
|
1439
1789
|
];
|
|
1440
1790
|
}
|
|
1441
|
-
const allLogs = await fetchRecentLogs(config.userId, token, 100);
|
|
1442
1791
|
allLogs.sort((a, b) => (Number(b.created_at) || 0) - (Number(a.created_at) || 0));
|
|
1443
1792
|
if (config.userId || token) {
|
|
1444
1793
|
const missing = modelIds.filter((m) => {
|
|
@@ -1470,7 +1819,7 @@ function apply(ctx) {
|
|
|
1470
1819
|
}
|
|
1471
1820
|
}
|
|
1472
1821
|
const dshSet = new Set(dshConfiguredModels);
|
|
1473
|
-
|
|
1822
|
+
let models = modelIds.map((mId) => {
|
|
1474
1823
|
const meta = resolveModelMeta(mId);
|
|
1475
1824
|
const cacheEntry = merchantCardCache.get(mId.toLowerCase());
|
|
1476
1825
|
const cachedCard = cacheEntry && Date.now() - cacheEntry.at < MERCHANT_CARD_TTL_MS ? cacheEntry.card : void 0;
|
|
@@ -1489,57 +1838,120 @@ function apply(ctx) {
|
|
|
1489
1838
|
lastRoutedText: routedAt ? formatRelativeTime(routedAt) : void 0
|
|
1490
1839
|
};
|
|
1491
1840
|
});
|
|
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
|
+
}
|
|
1492
1895
|
const recentLogs = allLogs.slice(0, 20);
|
|
1493
1896
|
const response = {
|
|
1494
1897
|
config: maskConfig(config),
|
|
1495
1898
|
balance,
|
|
1496
1899
|
models,
|
|
1497
1900
|
dshConfiguredModels,
|
|
1498
|
-
recentLogs
|
|
1901
|
+
recentLogs,
|
|
1902
|
+
pins
|
|
1499
1903
|
};
|
|
1500
1904
|
return sendJson(res, 200, { ok: true, data: response });
|
|
1501
1905
|
}
|
|
1502
1906
|
if (pathname === "/config" && req.method === "POST") {
|
|
1503
1907
|
const body = await parseJsonBody(req);
|
|
1504
|
-
const current = await
|
|
1505
|
-
const rawToken = body.accessToken !== void 0 && body.accessToken !== MASK ? body.accessToken : body.sessionCookie !== void 0 && body.sessionCookie !== MASK ? body.sessionCookie : current.accessToken
|
|
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;
|
|
1506
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
|
+
}
|
|
1507
1915
|
const updated = {
|
|
1508
1916
|
baseURL: body.baseURL !== void 0 ? body.baseURL : current.baseURL,
|
|
1509
1917
|
apiKey: newApiKey,
|
|
1510
1918
|
accessToken: rawToken,
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
activeModels: Array.isArray(body.activeModels) ? body.activeModels : current.activeModels,
|
|
1514
|
-
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
|
|
1515
1921
|
};
|
|
1516
1922
|
const balance = await fetchBalance(updated.baseURL, updated.apiKey, updated.userId, updated.accessToken);
|
|
1517
1923
|
if (balance?.userId) {
|
|
1518
1924
|
updated.userId = String(balance.userId);
|
|
1519
1925
|
}
|
|
1520
|
-
|
|
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
|
+
}
|
|
1521
1933
|
if (updated.activeModels.length > 0) {
|
|
1522
|
-
await
|
|
1934
|
+
await configAccess.syncModels(updated.baseURL, updated.activeModels);
|
|
1523
1935
|
}
|
|
1524
1936
|
return sendJson(res, 200, { ok: true, config: maskConfig(updated), balance });
|
|
1525
1937
|
}
|
|
1526
1938
|
if (pathname === "/balance" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1527
|
-
const config = await
|
|
1528
|
-
const token = config.accessToken ||
|
|
1939
|
+
const config = await configAccess.readConfig();
|
|
1940
|
+
const token = config.accessToken || "";
|
|
1529
1941
|
const balance = await fetchBalance(config.baseURL, config.apiKey, config.userId, token);
|
|
1530
1942
|
const recentLogs = await fetchRecentLogs(config.userId, token, 20);
|
|
1531
1943
|
return sendJson(res, 200, { ok: true, balance, recentLogs });
|
|
1532
1944
|
}
|
|
1533
1945
|
if (pathname === "/logs" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1534
|
-
const config = await
|
|
1535
|
-
const token = config.accessToken ||
|
|
1946
|
+
const config = await configAccess.readConfig();
|
|
1947
|
+
const token = config.accessToken || "";
|
|
1536
1948
|
const recentLogs = await fetchRecentLogs(config.userId, token, 30);
|
|
1537
1949
|
return sendJson(res, 200, { ok: true, logs: recentLogs });
|
|
1538
1950
|
}
|
|
1539
1951
|
if (pathname === "/probe" && req.method === "POST") {
|
|
1540
1952
|
const body = await parseJsonBody(req);
|
|
1541
|
-
const config = await
|
|
1542
|
-
const token = config.accessToken ||
|
|
1953
|
+
const config = await configAccess.readConfig();
|
|
1954
|
+
const token = config.accessToken || "";
|
|
1543
1955
|
const modelName = body.modelName;
|
|
1544
1956
|
if (modelName && modelName !== "all") {
|
|
1545
1957
|
const result = await probeSingleModel(config.baseURL, config.apiKey, config.userId, token, modelName);
|
|
@@ -1553,7 +1965,7 @@ function apply(ctx) {
|
|
|
1553
1965
|
modelIds = await fetchTokenModels(config.baseURL, config.apiKey);
|
|
1554
1966
|
}
|
|
1555
1967
|
if (modelIds.length === 0) {
|
|
1556
|
-
modelIds =
|
|
1968
|
+
modelIds = await configAccess.getDshConfiguredModels();
|
|
1557
1969
|
}
|
|
1558
1970
|
const results = [];
|
|
1559
1971
|
for (const m of modelIds) {
|
|
@@ -1567,27 +1979,285 @@ function apply(ctx) {
|
|
|
1567
1979
|
}
|
|
1568
1980
|
if (pathname === "/sync-models" && req.method === "POST") {
|
|
1569
1981
|
const body = await parseJsonBody(req);
|
|
1570
|
-
|
|
1982
|
+
await configAccess.ensureMigrated();
|
|
1983
|
+
const config = await configAccess.readConfig();
|
|
1571
1984
|
const modelIds = Array.isArray(body.modelIds) ? body.modelIds : [];
|
|
1572
1985
|
const baseURL = body.baseURL || config.baseURL;
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
await savePluginConfig(config);
|
|
1576
|
-
await syncToDshSettings(baseURL, modelIds);
|
|
1577
|
-
const dshConfiguredModels = await getDshConfiguredModels();
|
|
1986
|
+
await configAccess.syncModels(baseURL, modelIds);
|
|
1987
|
+
const dshConfiguredModels = await configAccess.getDshConfiguredModels();
|
|
1578
1988
|
return sendJson(res, 200, { ok: true, dshConfiguredModels });
|
|
1579
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
|
+
}
|
|
1580
2132
|
if (pathname === "/price-fluctuation" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1581
|
-
const config = await
|
|
1582
|
-
const token = config.accessToken ||
|
|
2133
|
+
const config = await configAccess.readConfig();
|
|
2134
|
+
const token = config.accessToken || "";
|
|
1583
2135
|
if (!token || !config.userId) {
|
|
1584
2136
|
return sendJson(res, 200, { ok: true, data: { pendingCount: 0, unseenCount: 0, totalCount: 0, hasAuth: false, authError: false, updatedAt: Date.now() } });
|
|
1585
2137
|
}
|
|
1586
|
-
const result = await fetchPriceFluctuation(config.userId, token
|
|
2138
|
+
const result = await fetchPriceFluctuation(config.userId, token);
|
|
1587
2139
|
const { notices, ...counts } = result;
|
|
1588
2140
|
const hasAuth = !counts.authError;
|
|
1589
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() } });
|
|
1590
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
|
+
}
|
|
1591
2261
|
return sendJson(res, 404, { ok: false, error: "Not found" });
|
|
1592
2262
|
} catch (err) {
|
|
1593
2263
|
console.error("[dsh-a6api] API error:", err);
|
|
@@ -1602,19 +2272,27 @@ function apply(ctx) {
|
|
|
1602
2272
|
}
|
|
1603
2273
|
}
|
|
1604
2274
|
export {
|
|
1605
|
-
|
|
2275
|
+
A6API_CRED_REF,
|
|
2276
|
+
A6API_TOKEN_REF,
|
|
2277
|
+
A6API_USER_REF,
|
|
1606
2278
|
apply,
|
|
2279
|
+
clearCatalog,
|
|
2280
|
+
createConfigAccess,
|
|
1607
2281
|
fetchBalance,
|
|
1608
2282
|
fetchChannelDetails,
|
|
2283
|
+
fetchMarketplaceModels,
|
|
2284
|
+
fetchMarketplacePins,
|
|
1609
2285
|
fetchRecentLogs,
|
|
1610
2286
|
fetchTokenModels,
|
|
2287
|
+
getCatalog,
|
|
2288
|
+
getCatalogEntry,
|
|
1611
2289
|
getKnownMerchantsFromLogs,
|
|
2290
|
+
inferBrand,
|
|
1612
2291
|
inject,
|
|
1613
2292
|
name,
|
|
1614
2293
|
probeSingleModel,
|
|
1615
|
-
|
|
2294
|
+
queryOpenRouter,
|
|
1616
2295
|
resolveModelMeta,
|
|
1617
|
-
|
|
1618
|
-
syncToDshSettings
|
|
2296
|
+
updateCatalogEntry
|
|
1619
2297
|
};
|
|
1620
2298
|
//# sourceMappingURL=index.js.map
|