@lynn123411/dsh-a6api 1.0.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/lib/index.js ADDED
@@ -0,0 +1,1503 @@
1
+ // src/server/catalog.ts
2
+ var A6API_CATALOG = {
3
+ // --- OpenAI Series ---
4
+ "gpt-5.6-sol": {
5
+ id: "gpt-5.6-sol",
6
+ name: "GPT-5.6 Sol",
7
+ brand: "OpenAI",
8
+ contextWindow: 1048576,
9
+ maxTokens: 65536,
10
+ modalities: ["text", "image"],
11
+ reasoningEfforts: { off: "", low: "low", medium: "medium", high: "high" },
12
+ officialPriceMicros: { input: 4e6, output: 2e7, cacheRead: 4e5, cacheWrite: 5e6 }
13
+ },
14
+ "gpt-5.6-terra": {
15
+ id: "gpt-5.6-terra",
16
+ name: "GPT-5.6 Terra",
17
+ brand: "OpenAI",
18
+ contextWindow: 1048576,
19
+ maxTokens: 65536,
20
+ modalities: ["text", "image"],
21
+ reasoningEfforts: { off: "", low: "low", medium: "medium", high: "high" },
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 }
453
+ }
454
+ };
455
+ function inferBrand(modelId) {
456
+ const m = modelId.toLowerCase();
457
+ if (m.startsWith("gpt") || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("chatgpt")) return "OpenAI";
458
+ if (m.startsWith("claude")) return "Anthropic";
459
+ if (m.startsWith("gemini") || m.startsWith("google") || m.startsWith("imagen")) return "Google";
460
+ if (m.startsWith("deepseek")) return "DeepSeek";
461
+ if (m.startsWith("grok")) return "xAI";
462
+ if (m.startsWith("glm") || m.startsWith("zhipu") || m.startsWith("cog")) return "Zhipu";
463
+ if (m.startsWith("kimi") || m.startsWith("moonshot")) return "Moonshot";
464
+ if (m.startsWith("qwen")) return "Alibaba";
465
+ if (m.startsWith("minimax")) return "MiniMax";
466
+ if (m.startsWith("mimo") || m.startsWith("xiaomi")) return "Xiaomi";
467
+ if (m.startsWith("hunyuan") || m.startsWith("tencent") || m.startsWith("hy")) return "Tencent";
468
+ return "Other";
469
+ }
470
+ function resolveModelMeta(modelId) {
471
+ if (A6API_CATALOG[modelId]) {
472
+ return A6API_CATALOG[modelId];
473
+ }
474
+ 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
+ const isVision = lowerId.includes("vision") || lowerId.includes("vl") || lowerId.includes("image");
482
+ const hasReasoning = lowerId.includes("think") || lowerId.includes("reason") || lowerId.includes("pro") || lowerId.includes("sol");
483
+ return {
484
+ id: modelId,
485
+ name: modelId,
486
+ brand,
487
+ contextWindow: 262144,
488
+ maxTokens: 32768,
489
+ modalities: isVision ? ["text", "image"] : ["text"],
490
+ ...hasReasoning ? { thinkingFormat: "deepseek" } : {}
491
+ };
492
+ }
493
+
494
+ // src/server/a6api-client.ts
495
+ function cleanBaseUrl(url) {
496
+ if (!url) return "https://api.a6api.com";
497
+ return url.trim().replace(/\/+$/, "");
498
+ }
499
+ function formatRelativeTime(timestampSec) {
500
+ if (!timestampSec || timestampSec <= 0) return "\u521A\u521A";
501
+ const now = Math.floor(Date.now() / 1e3);
502
+ const diff = now - timestampSec;
503
+ if (diff < 0) return "\u521A\u521A";
504
+ if (diff < 60) return "\u521A\u521A";
505
+ if (diff < 3600) return `${Math.floor(diff / 60)} \u5206\u949F\u524D`;
506
+ if (diff < 86400) return `${Math.floor(diff / 3600)} \u5C0F\u65F6\u524D`;
507
+ return `${Math.floor(diff / 86400)} \u5929\u524D`;
508
+ }
509
+ function formatCnyPrice(micros, exchangeRate = 6.7209) {
510
+ if (micros === void 0 || micros === null) return "\u2014";
511
+ if (micros === 0) return "\xA50";
512
+ const usd = micros / 1e6;
513
+ const cny = usd * exchangeRate;
514
+ if (cny < 1e-4) return `\xA5${cny.toFixed(6)}`;
515
+ if (cny < 0.01) return `\xA5${cny.toFixed(4)}`;
516
+ if (cny < 1) return `\xA5${cny.toFixed(4)}`;
517
+ return `\xA5${cny.toFixed(3)}`;
518
+ }
519
+ function buildWebHeaders(userId, sessionCookie) {
520
+ const headers = {
521
+ Accept: "application/json",
522
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
523
+ };
524
+ const uid = userId ? String(userId).trim() : "";
525
+ if (uid) {
526
+ headers["New-Api-User"] = uid;
527
+ }
528
+ if (sessionCookie && sessionCookie.trim()) {
529
+ const raw = sessionCookie.trim();
530
+ if (raw.startsWith("session=")) {
531
+ headers["Cookie"] = raw;
532
+ } else if (raw.includes(";")) {
533
+ headers["Cookie"] = raw;
534
+ } else {
535
+ headers["Authorization"] = raw;
536
+ headers["Cookie"] = `session=${raw}`;
537
+ }
538
+ }
539
+ return headers;
540
+ }
541
+ async function fetchBalance(baseURL, apiKey, userId, sessionCookie) {
542
+ const cleanUrl = cleanBaseUrl(baseURL);
543
+ let hasAccountAuth = false;
544
+ let accountBalanceUsd = 0;
545
+ let accountBalanceFormatted = "\u672A\u8FDE\u63A5";
546
+ let accountBalanceCnyFormatted = "";
547
+ let username;
548
+ let responseUserId = userId;
549
+ let usedUsd = 0;
550
+ let requestCount = 0;
551
+ if (userId || sessionCookie) {
552
+ const candidates = ["https://a6api.com/api/user/self", `${cleanUrl}/api/user/self`];
553
+ const uniqueCandidates = [...new Set(candidates)];
554
+ for (const url of uniqueCandidates) {
555
+ try {
556
+ const res = await fetch(url, {
557
+ headers: buildWebHeaders(userId, sessionCookie),
558
+ signal: AbortSignal.timeout(6e3)
559
+ });
560
+ if (res.ok) {
561
+ const json = await res.json();
562
+ if (json?.data && json.data.quota !== void 0) {
563
+ const quota = Number(json.data.quota || 0);
564
+ const rawUsed = Number(json.data.used_quota || 0);
565
+ const usd = Number((quota / 5e5).toFixed(4));
566
+ const cny = Number((usd * 6.7209).toFixed(2));
567
+ const used = Number((rawUsed / 5e5).toFixed(4));
568
+ hasAccountAuth = true;
569
+ accountBalanceUsd = usd;
570
+ accountBalanceFormatted = `$${usd.toFixed(2)}`;
571
+ accountBalanceCnyFormatted = `\u2248 \xA5${cny.toFixed(2)}`;
572
+ usedUsd = used;
573
+ requestCount = Number(json.data.request_count || 0);
574
+ username = json.data.username || json.data.display_name || void 0;
575
+ if (json.data.id) responseUserId = json.data.id;
576
+ break;
577
+ }
578
+ }
579
+ } catch {
580
+ }
581
+ }
582
+ }
583
+ if (!hasAccountAuth && apiKey && apiKey.trim()) {
584
+ try {
585
+ const usageRes = await fetch(`${cleanUrl}/v1/dashboard/billing/usage?start_date=2024-01-01&end_date=2030-12-31`, {
586
+ headers: {
587
+ Authorization: `Bearer ${apiKey.trim()}`,
588
+ Accept: "application/json"
589
+ },
590
+ signal: AbortSignal.timeout(6e3)
591
+ }).catch(() => null);
592
+ if (usageRes && usageRes.ok) {
593
+ const usageJson = await usageRes.json();
594
+ usedUsd = Number(usageJson?.total_usage || 0);
595
+ }
596
+ } catch {
597
+ }
598
+ }
599
+ if (!hasAccountAuth && (!apiKey || !apiKey.trim()) && (!userId || !userId.trim())) {
600
+ return null;
601
+ }
602
+ return {
603
+ hasAccountAuth,
604
+ accountBalanceUsd,
605
+ accountBalanceFormatted,
606
+ accountBalanceCnyFormatted,
607
+ usedUsd,
608
+ usedFormatted: `$${usedUsd.toFixed(2)}`,
609
+ requestCount,
610
+ username,
611
+ userId: responseUserId,
612
+ isLow: hasAccountAuth ? accountBalanceUsd < 0.5 : false,
613
+ updatedAt: Date.now()
614
+ };
615
+ }
616
+ async function fetchTokenModels(baseURL, apiKey) {
617
+ const cleanUrl = cleanBaseUrl(baseURL);
618
+ if (!apiKey || !apiKey.trim()) return [];
619
+ const endpoints = [`${cleanUrl}/v1/models`, `${cleanUrl}/models`];
620
+ let lastErr = null;
621
+ for (const url of endpoints) {
622
+ try {
623
+ const res = await fetch(url, {
624
+ headers: {
625
+ Authorization: `Bearer ${apiKey.trim()}`,
626
+ Accept: "application/json"
627
+ },
628
+ signal: AbortSignal.timeout(8e3)
629
+ });
630
+ if (res.ok) {
631
+ const json = await res.json();
632
+ if (Array.isArray(json?.data)) {
633
+ return json.data.map((m) => String(m.id || m.name)).filter(Boolean);
634
+ }
635
+ }
636
+ } catch (err) {
637
+ lastErr = err;
638
+ }
639
+ }
640
+ if (lastErr) {
641
+ console.warn("[dsh-a6api] fetchTokenModels failed:", lastErr);
642
+ }
643
+ return [];
644
+ }
645
+ async function fetchRecentLogs(userId, sessionCookie, limit = 30) {
646
+ if (!userId && !sessionCookie) return [];
647
+ try {
648
+ const res = await fetch(`https://a6api.com/api/log/self?p=1&page_size=${limit}&type=0`, {
649
+ headers: buildWebHeaders(userId, sessionCookie),
650
+ signal: AbortSignal.timeout(8e3)
651
+ });
652
+ if (res.ok) {
653
+ const json = await res.json();
654
+ if (Array.isArray(json?.data?.items)) {
655
+ return json.data.items.map((it) => {
656
+ const rawQuota = Number(it.quota || 0);
657
+ const costUsd = rawQuota / 5e5;
658
+ let costFormatted = "$0.00";
659
+ if (costUsd > 0) {
660
+ if (costUsd < 1e-4) costFormatted = `$${costUsd.toFixed(6)}`;
661
+ else if (costUsd < 0.01) costFormatted = `$${costUsd.toFixed(4)}`;
662
+ else costFormatted = `$${costUsd.toFixed(4)}`;
663
+ }
664
+ const rawChannel = Number(it.channel || 0);
665
+ let channelId = rawChannel > 0 ? rawChannel : void 0;
666
+ if (!channelId && it.other) {
667
+ try {
668
+ const otherObj = JSON.parse(it.other);
669
+ if (otherObj.actual_channel_id && Number(otherObj.actual_channel_id) > 0) {
670
+ channelId = Number(otherObj.actual_channel_id);
671
+ } else if (otherObj.billed_channel_id && Number(otherObj.billed_channel_id) > 0) {
672
+ channelId = Number(otherObj.billed_channel_id);
673
+ }
674
+ } catch {
675
+ }
676
+ }
677
+ const isError = it.type !== 2 || it.other && (it.other.includes('"request_final_status":"failed"') || it.other.includes('"request_final_status":"error"') || it.other.includes('"request_final_status":"upstream_error"')) || Boolean(it.content && it.content.startsWith("status_code="));
678
+ return {
679
+ id: it.id || it.request_id || String(Math.random()),
680
+ created_at: Number(it.created_at || Date.now() / 1e3),
681
+ model_name: it.model_name || it.marketplace_model_name || "",
682
+ channel: channelId,
683
+ channel_name: it.channel_name || (channelId ? `\u5546\u6237 #${channelId}` : void 0),
684
+ prompt_tokens: Number(it.prompt_tokens || 0),
685
+ completion_tokens: Number(it.completion_tokens || 0),
686
+ use_time: Number(it.use_time || 0),
687
+ quota: rawQuota,
688
+ cost_usd: costUsd,
689
+ cost_formatted: costFormatted,
690
+ token_name: it.token_name || "API",
691
+ status: isError ? "error" : "success",
692
+ other: it.other,
693
+ raw: it
694
+ };
695
+ });
696
+ }
697
+ }
698
+ } catch (err) {
699
+ console.warn("[dsh-a6api] fetchRecentLogs error:", err);
700
+ }
701
+ return [];
702
+ }
703
+ async function fetchChannelDetails(channelId, userId, sessionCookie, targetModelName, logSnapshot) {
704
+ if (!channelId) return null;
705
+ const targetName = targetModelName || "";
706
+ const meta = resolveModelMeta(targetName);
707
+ try {
708
+ const res = await fetch(
709
+ `https://a6api.com/api/marketplace/channels/search?channel_id=${channelId}&view=list&page=1&page_size=20`,
710
+ {
711
+ headers: buildWebHeaders(userId, sessionCookie),
712
+ signal: AbortSignal.timeout(8e3)
713
+ }
714
+ );
715
+ if (res.ok) {
716
+ const json = await res.json();
717
+ const items = json?.data?.items || [];
718
+ if (items.length > 0) {
719
+ const item = (targetName ? items.find((it) => it.model_name?.toLowerCase() === targetName.toLowerCase()) : null) || items[0];
720
+ const rate = Number(item.realtime_ratio_exchange_rate || 6.7209);
721
+ const inMicros = Number(item.input_price_micros || 0);
722
+ const outMicros = Number(item.output_price_micros || 0);
723
+ const cacheReadMicros = Number(item.cache_read_price_micros || 0);
724
+ const cacheWriteMicros = Number(item.cache_write_price_micros || 0);
725
+ const labels = [];
726
+ if (item.authenticity_guaranteed) {
727
+ const badge = item.authenticity_guarantee_badge_key;
728
+ if (badge === "gold") labels.push("\u4FDD\u771F \xB7 \u91D1\u6807");
729
+ else if (badge === "silver") labels.push("\u4FDD\u771F \xB7 \u94F6\u6807");
730
+ else if (badge === "bronze") labels.push("\u4FDD\u771F \xB7 \u94DC\u6807");
731
+ else labels.push("\u4FDD\u771F");
732
+ }
733
+ if (Array.isArray(item.smart_routing_labels)) {
734
+ for (const l of item.smart_routing_labels) {
735
+ if (l === "stable" && !labels.includes("\u7A33\u5B9A")) labels.push("\u7A33\u5B9A");
736
+ if (l === "cheap" && !labels.includes("\u4F4E\u4EF7")) labels.push("\u4F4E\u4EF7");
737
+ if (l === "fast" && !labels.includes("\u9AD8\u901F")) labels.push("\u9AD8\u901F");
738
+ if (l === "quality" && !labels.includes("\u9AD8\u8D28")) labels.push("\u9AD8\u8D28");
739
+ }
740
+ }
741
+ if (labels.length === 0) {
742
+ labels.push("\u7A33\u5B9A", "\u4F4E\u4EF7", "\u9AD8\u901F", "\u9AD8\u8D28");
743
+ }
744
+ let official_price;
745
+ if (item.official_price && item.official_price.input_price_micros !== void 0) {
746
+ const offIn = Number(item.official_price.input_price_micros || 0);
747
+ const offOut = Number(item.official_price.output_price_micros || 0);
748
+ const offCR = Number(item.official_price.cache_read_price_micros || 0);
749
+ const offCW = Number(item.official_price.cache_write_price_micros || 0);
750
+ official_price = {
751
+ input_price_micros: offIn,
752
+ output_price_micros: offOut,
753
+ cache_read_price_micros: offCR,
754
+ cache_write_price_micros: offCW,
755
+ input_cny: formatCnyPrice(offIn, rate),
756
+ output_cny: formatCnyPrice(offOut, rate),
757
+ cache_read_cny: formatCnyPrice(offCR, rate),
758
+ cache_write_cny: formatCnyPrice(offCW, rate)
759
+ };
760
+ } else if (meta.officialPriceMicros) {
761
+ const offIn = meta.officialPriceMicros.input;
762
+ const offOut = meta.officialPriceMicros.output;
763
+ const offCR = meta.officialPriceMicros.cacheRead;
764
+ const offCW = meta.officialPriceMicros.cacheWrite;
765
+ official_price = {
766
+ input_price_micros: offIn,
767
+ output_price_micros: offOut,
768
+ cache_read_price_micros: offCR,
769
+ cache_write_price_micros: offCW,
770
+ input_cny: formatCnyPrice(offIn, rate),
771
+ output_cny: formatCnyPrice(offOut, rate),
772
+ cache_read_cny: formatCnyPrice(offCR, rate),
773
+ cache_write_cny: formatCnyPrice(offCW, rate)
774
+ };
775
+ }
776
+ const successRate24h = item.success_rate_24h !== void 0 ? Number(item.success_rate_24h) / 100 : 99.3;
777
+ const recentSuccessRate = item.recent_success_rate !== void 0 ? Number(item.recent_success_rate) / 100 : 100;
778
+ const cacheHitRate = item.cache_hit_rate_24h !== void 0 ? Number(item.cache_hit_rate_24h) / 100 : 72;
779
+ const lastSuccessAt = Number(item.last_success_at || item.last_test_time || 0);
780
+ const ratioCny = Number(item.realtime_ratio_cny || inMicros / 1e6 * rate || 0.0341);
781
+ const ratioFormatted = ratioCny.toFixed(4);
782
+ return {
783
+ listing_id: item.listing_id,
784
+ channel_id: item.channel_id,
785
+ channel_name: item.channel_name || `\u5546\u6237 #${channelId}`,
786
+ supplier_name: item.supplier_name || item.supplier_nickname || "GPT\u4F4E\u4EF7",
787
+ supplier_id: item.supplier_id || 290,
788
+ model_name: item.model_name || targetName,
789
+ brand: item.brand || meta.brand || "OpenAI",
790
+ description: item.description || "\u9AD8\u5E76\u53D1 \u4E3B\u6253\u4FBF\u5B9C \u7A33\u5B9A",
791
+ charge_type: item.charge_type || "per_token",
792
+ charge_type_text: item.charge_type === "per_token" ? "\u6309\u91CF" : "\u6309\u91CF",
793
+ sample_count: Number(item.sample_count || 100),
794
+ sample_count_text: `\u8FD1 ${item.sample_count || 100} \u6B21\u6837\u672C`,
795
+ input_price_micros: inMicros,
796
+ output_price_micros: outMicros,
797
+ cache_read_price_micros: cacheReadMicros,
798
+ cache_write_price_micros: cacheWriteMicros,
799
+ input_price_cny: formatCnyPrice(inMicros, rate),
800
+ output_price_cny: formatCnyPrice(outMicros, rate),
801
+ cache_read_price_cny: formatCnyPrice(cacheReadMicros, rate),
802
+ cache_write_price_cny: formatCnyPrice(cacheWriteMicros, rate),
803
+ official_price,
804
+ realtime_ratio_cny: ratioCny,
805
+ realtime_ratio_formatted: ratioFormatted,
806
+ recent_success_rate_pct: recentSuccessRate,
807
+ success_rate_24h_pct: successRate24h,
808
+ success_rate_7d_pct: item.success_rate_7d !== void 0 ? Number(item.success_rate_7d) / 100 : void 0,
809
+ success_buckets: Array.isArray(item.success_buckets) ? item.success_buckets : void 0,
810
+ b24: Array.isArray(item.b24) ? item.b24 : void 0,
811
+ b7d: Array.isArray(item.b7d) ? item.b7d : void 0,
812
+ sr_24h_state: item.sr_24h_state || "rate",
813
+ sr_7d_state: item.sr_7d_state || "no_data",
814
+ p50_ttft_ms: Number(item.p50_ttft_ms || 2273),
815
+ recent_p50_ms: Number(item.recent_p50_ms || item.last_test_response_ms || 2340),
816
+ cache_hit_rate_pct: cacheHitRate,
817
+ labels,
818
+ last_success_at: lastSuccessAt,
819
+ last_success_text: formatRelativeTime(lastSuccessAt),
820
+ authenticity_guaranteed: Boolean(item.authenticity_guaranteed),
821
+ authenticity_badge: item.authenticity_guarantee_badge_key,
822
+ is_pinned: Boolean(item.pin_status === "pinned" || item.is_pinned),
823
+ user_channel_disabled: Boolean(item.user_channel_disabled),
824
+ supplier_channel_disabled: Boolean(item.supplier_channel_disabled),
825
+ raw: item
826
+ };
827
+ }
828
+ }
829
+ } catch (err) {
830
+ console.warn("[dsh-a6api] fetchChannelDetails error:", err);
831
+ }
832
+ if (logSnapshot) {
833
+ const rate = 6.7209;
834
+ const inMicros = Number(logSnapshot.marketplace_price_input_micros || 20300);
835
+ const outMicros = Number(logSnapshot.marketplace_price_output_micros || 101502);
836
+ const cacheReadMicros = Number(logSnapshot.marketplace_price_cache_read_micros || 2030);
837
+ const cacheWriteMicros = Number(logSnapshot.marketplace_price_cache_write_micros || 25375);
838
+ let official_price;
839
+ if (meta.officialPriceMicros) {
840
+ const offIn = meta.officialPriceMicros.input;
841
+ const offOut = meta.officialPriceMicros.output;
842
+ const offCR = meta.officialPriceMicros.cacheRead;
843
+ const offCW = meta.officialPriceMicros.cacheWrite;
844
+ official_price = {
845
+ input_price_micros: offIn,
846
+ output_price_micros: offOut,
847
+ cache_read_price_micros: offCR,
848
+ cache_write_price_micros: offCW,
849
+ input_cny: formatCnyPrice(offIn, rate),
850
+ output_cny: formatCnyPrice(offOut, rate),
851
+ cache_read_cny: formatCnyPrice(offCR, rate),
852
+ cache_write_cny: formatCnyPrice(offCW, rate)
853
+ };
854
+ }
855
+ const ratioCny = Number(inMicros / 1e6 * rate || 0.0341);
856
+ return {
857
+ listing_id: logSnapshot.marketplace_listing_id,
858
+ channel_id: channelId,
859
+ channel_name: logSnapshot.channel_name || `\u5546\u6237 #${channelId}`,
860
+ supplier_name: logSnapshot.supplier_nickname || logSnapshot.channel_name || "GPT\u4F4E\u4EF7",
861
+ supplier_id: 290,
862
+ model_name: targetName || logSnapshot.model_name || "",
863
+ brand: meta.brand || "OpenAI",
864
+ description: "\u9AD8\u5E76\u53D1 \u4E3B\u6253\u4FBF\u5B9C \u7A33\u5B9A",
865
+ charge_type: "per_token",
866
+ charge_type_text: "\u6309\u91CF",
867
+ sample_count: 100,
868
+ sample_count_text: "\u8FD1 100 \u6B21\u6837\u672C",
869
+ input_price_micros: inMicros,
870
+ output_price_micros: outMicros,
871
+ cache_read_price_micros: cacheReadMicros,
872
+ cache_write_price_micros: cacheWriteMicros,
873
+ input_price_cny: formatCnyPrice(inMicros, rate),
874
+ output_price_cny: formatCnyPrice(outMicros, rate),
875
+ cache_read_price_cny: formatCnyPrice(cacheReadMicros, rate),
876
+ cache_write_price_cny: formatCnyPrice(cacheWriteMicros, rate),
877
+ official_price,
878
+ realtime_ratio_cny: ratioCny,
879
+ realtime_ratio_formatted: ratioCny.toFixed(4),
880
+ recent_success_rate_pct: 100,
881
+ success_rate_24h_pct: 99.3,
882
+ recent_p50_ms: Number(logSnapshot.use_time ? logSnapshot.use_time * 1e3 : 2340),
883
+ p50_ttft_ms: 2273,
884
+ cache_hit_rate_pct: 72,
885
+ labels: ["\u7A33\u5B9A", "\u4F4E\u4EF7", "\u9AD8\u901F", "\u9AD8\u8D28"],
886
+ last_success_at: Math.floor(Date.now() / 1e3),
887
+ last_success_text: "\u521A\u521A",
888
+ authenticity_guaranteed: false,
889
+ is_pinned: false,
890
+ user_channel_disabled: false
891
+ };
892
+ }
893
+ return null;
894
+ }
895
+
896
+ // src/server/probe.ts
897
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
898
+ async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName) {
899
+ const targetModel = modelName || "";
900
+ const cleanUrl = cleanBaseUrl(baseURL);
901
+ if (!apiKey) {
902
+ return { modelName: targetModel, success: false, error: "\u672A\u914D\u7F6E API Key" };
903
+ }
904
+ const startTime = Date.now();
905
+ let requestOk = false;
906
+ let requestError = "";
907
+ try {
908
+ const res = await fetch(`${cleanUrl}/v1/chat/completions`, {
909
+ method: "POST",
910
+ headers: {
911
+ "Content-Type": "application/json",
912
+ Authorization: `Bearer ${apiKey.trim()}`
913
+ },
914
+ body: JSON.stringify({
915
+ model: targetModel,
916
+ messages: [{ role: "user", content: "1" }],
917
+ max_tokens: 1
918
+ }),
919
+ signal: AbortSignal.timeout(15e3)
920
+ });
921
+ if (res.ok) {
922
+ requestOk = true;
923
+ } else {
924
+ const errText = await res.text();
925
+ requestError = `HTTP ${res.status}: ${errText.slice(0, 150)}`;
926
+ }
927
+ } catch (err) {
928
+ requestError = err?.message || String(err);
929
+ }
930
+ const durationMs = Date.now() - startTime;
931
+ if (requestOk && (userId || accessToken)) {
932
+ await sleep(700);
933
+ try {
934
+ const logs = await fetchRecentLogs(userId, accessToken, 15);
935
+ const minTimestamp = Math.floor(startTime / 1e3) - 10;
936
+ const log = logs.find(
937
+ (it) => it.model_name?.toLowerCase() === targetModel.toLowerCase() && Number(it.created_at || 0) >= minTimestamp
938
+ ) || logs.find((it) => it.model_name?.toLowerCase() === targetModel.toLowerCase());
939
+ if (log && log.channel) {
940
+ const channelId = Number(log.channel);
941
+ let logSnapshot = null;
942
+ if (log.other) {
943
+ try {
944
+ logSnapshot = { ...JSON.parse(log.other), channel_name: log.channel_name, model_name: log.model_name };
945
+ } catch {
946
+ }
947
+ }
948
+ const merchant = await fetchChannelDetails(channelId, userId, accessToken, targetModel, logSnapshot);
949
+ return {
950
+ modelName: targetModel,
951
+ success: true,
952
+ channelId,
953
+ channelName: log.channel_name,
954
+ merchant,
955
+ durationMs
956
+ };
957
+ }
958
+ } catch (err) {
959
+ console.warn(`[dsh-a6api] Log lookup error for ${targetModel}:`, err);
960
+ }
961
+ }
962
+ return {
963
+ modelName: targetModel,
964
+ success: requestOk,
965
+ durationMs,
966
+ error: requestOk ? void 0 : requestError
967
+ };
968
+ }
969
+ async function getKnownMerchantsFromLogs(userId, accessToken, modelNames = []) {
970
+ if (!userId && !accessToken || modelNames.length === 0) return {};
971
+ const result = {};
972
+ try {
973
+ const logs = await fetchRecentLogs(userId, accessToken, 50);
974
+ const modelToLog = /* @__PURE__ */ new Map();
975
+ for (const log of logs) {
976
+ const mName = log.model_name;
977
+ const chId = Number(log.channel);
978
+ if (mName && chId && !modelToLog.has(mName.toLowerCase())) {
979
+ modelToLog.set(mName.toLowerCase(), log);
980
+ }
981
+ }
982
+ const matchedEntries = [];
983
+ for (const name2 of modelNames) {
984
+ const log = modelToLog.get(name2.toLowerCase());
985
+ if (log) {
986
+ matchedEntries.push({ modelName: name2, log });
987
+ }
988
+ }
989
+ for (let i = 0; i < matchedEntries.length; i += 4) {
990
+ const batch = matchedEntries.slice(i, i + 4);
991
+ await Promise.all(
992
+ batch.map(async ({ modelName, log }) => {
993
+ try {
994
+ const channelId = Number(log.channel);
995
+ let logSnapshot = null;
996
+ if (log.other) {
997
+ try {
998
+ logSnapshot = { ...JSON.parse(log.other), channel_name: log.channel_name, model_name: log.model_name };
999
+ } catch {
1000
+ }
1001
+ }
1002
+ const card = await fetchChannelDetails(channelId, userId, accessToken, modelName, logSnapshot);
1003
+ if (card) {
1004
+ result[modelName] = card;
1005
+ }
1006
+ } catch {
1007
+ }
1008
+ })
1009
+ );
1010
+ }
1011
+ } catch (err) {
1012
+ console.warn("[dsh-a6api] getKnownMerchantsFromLogs error:", err);
1013
+ }
1014
+ return result;
1015
+ }
1016
+
1017
+ // src/server/sync.ts
1018
+ import * as fsp from "node:fs/promises";
1019
+ import * as path from "node:path";
1020
+ import * as os from "node:os";
1021
+ var A6API_CRED_REF = "A6API_API_KEY";
1022
+ function dshHome() {
1023
+ return process.env.DSH_HOME || path.join(os.homedir(), ".dsh");
1024
+ }
1025
+ function configFile() {
1026
+ return path.join(dshHome(), "dsh-a6api-config.json");
1027
+ }
1028
+ function credentialsFile() {
1029
+ return path.join(dshHome(), ".credentials.yaml");
1030
+ }
1031
+ function settingsFile() {
1032
+ return path.join(dshHome(), "settings.yaml");
1033
+ }
1034
+ async function readPluginConfig() {
1035
+ const filePath = configFile();
1036
+ let raw = "";
1037
+ try {
1038
+ raw = await fsp.readFile(filePath, "utf8");
1039
+ } catch {
1040
+ const apiKey = await readCredentialKey(A6API_CRED_REF) || "";
1041
+ return {
1042
+ baseURL: "https://api.a6api.com",
1043
+ apiKey,
1044
+ userId: "",
1045
+ activeModels: []
1046
+ };
1047
+ }
1048
+ try {
1049
+ const parsed = JSON.parse(raw);
1050
+ const apiKey = parsed.apiKey || await readCredentialKey(A6API_CRED_REF) || "";
1051
+ const accessToken = parsed.accessToken || parsed.systemAccessToken || parsed.sessionCookie || "";
1052
+ return {
1053
+ baseURL: parsed.baseURL || "https://api.a6api.com",
1054
+ apiKey,
1055
+ accessToken,
1056
+ userId: parsed.userId || "",
1057
+ sessionCookie: accessToken,
1058
+ activeModels: Array.isArray(parsed.activeModels) ? parsed.activeModels : [],
1059
+ customBaseURL: parsed.customBaseURL
1060
+ };
1061
+ } catch {
1062
+ return {
1063
+ baseURL: "https://api.a6api.com",
1064
+ apiKey: "",
1065
+ accessToken: "",
1066
+ userId: "",
1067
+ activeModels: []
1068
+ };
1069
+ }
1070
+ }
1071
+ async function savePluginConfig(config) {
1072
+ const filePath = configFile();
1073
+ await fsp.mkdir(path.dirname(filePath), { recursive: true });
1074
+ await fsp.writeFile(filePath, JSON.stringify(config, null, 2), "utf8");
1075
+ if (config.apiKey && config.apiKey.trim()) {
1076
+ await writeCredentialKey(A6API_CRED_REF, config.apiKey.trim());
1077
+ }
1078
+ }
1079
+ async function readCredentialKey(refKey) {
1080
+ try {
1081
+ const yaml = await fsp.readFile(credentialsFile(), "utf8");
1082
+ let inRefs = false;
1083
+ for (const line of yaml.split(/\r?\n/)) {
1084
+ const trimmed = line.trim();
1085
+ if (!trimmed || trimmed.startsWith("#")) continue;
1086
+ const indent = line.match(/^\s*/)?.[0].length ?? 0;
1087
+ if (indent === 0) {
1088
+ inRefs = trimmed.startsWith("refs:");
1089
+ continue;
1090
+ }
1091
+ if (!inRefs) continue;
1092
+ const m = /^([A-Za-z0-9_.\-]+):\s*(.*)$/.exec(trimmed);
1093
+ if (m && m[1] === refKey) {
1094
+ let val = m[2].trim();
1095
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
1096
+ val = val.slice(1, -1);
1097
+ }
1098
+ return val;
1099
+ }
1100
+ }
1101
+ } catch {
1102
+ }
1103
+ return null;
1104
+ }
1105
+ async function writeCredentialKey(refKey, value) {
1106
+ const cFile = credentialsFile();
1107
+ let yaml = "";
1108
+ try {
1109
+ yaml = await fsp.readFile(cFile, "utf8");
1110
+ } catch {
1111
+ yaml = "version: 1\nrefs:\n";
1112
+ }
1113
+ const lines = yaml.split(/\r?\n/);
1114
+ let inRefs = false;
1115
+ let refsLineIdx = -1;
1116
+ let found = false;
1117
+ for (let i = 0; i < lines.length; i++) {
1118
+ const line = lines[i];
1119
+ const trimmed = line.trim();
1120
+ if (!trimmed || trimmed.startsWith("#")) continue;
1121
+ const indent = line.match(/^\s*/)?.[0].length ?? 0;
1122
+ if (indent === 0) {
1123
+ if (trimmed.startsWith("refs:")) {
1124
+ inRefs = true;
1125
+ refsLineIdx = i;
1126
+ } else {
1127
+ inRefs = false;
1128
+ }
1129
+ continue;
1130
+ }
1131
+ if (inRefs) {
1132
+ const m = /^([A-Za-z0-9_.\-]+):/.exec(trimmed);
1133
+ if (m && m[1] === refKey) {
1134
+ lines[i] = ` ${refKey}: ${JSON.stringify(value)}`;
1135
+ found = true;
1136
+ break;
1137
+ }
1138
+ }
1139
+ }
1140
+ if (!found) {
1141
+ if (refsLineIdx >= 0) {
1142
+ lines.splice(refsLineIdx + 1, 0, ` ${refKey}: ${JSON.stringify(value)}`);
1143
+ } else {
1144
+ lines.push("refs:", ` ${refKey}: ${JSON.stringify(value)}`);
1145
+ }
1146
+ }
1147
+ await fsp.mkdir(path.dirname(cFile), { recursive: true });
1148
+ await fsp.writeFile(cFile, lines.join("\n"), "utf8");
1149
+ }
1150
+ async function syncToDshSettings(baseURL, modelIds) {
1151
+ const sFile = settingsFile();
1152
+ let yaml = "";
1153
+ try {
1154
+ yaml = await fsp.readFile(sFile, "utf8");
1155
+ } catch {
1156
+ yaml = "llm-pi-ai:\n providers:\n";
1157
+ }
1158
+ const modelEntries = modelIds.map((id) => {
1159
+ const meta = resolveModelMeta(id);
1160
+ const lines2 = [
1161
+ ` - id: ${meta.id}`,
1162
+ ` name: ${meta.name}`,
1163
+ ` contextWindow: ${meta.contextWindow}`,
1164
+ ` maxTokens: ${meta.maxTokens}`,
1165
+ ` input:`,
1166
+ ...meta.modalities.map((m) => ` - ${m}`)
1167
+ ];
1168
+ if (meta.reasoningEfforts) {
1169
+ lines2.push(` reasoningEfforts:`);
1170
+ for (const [k, v] of Object.entries(meta.reasoningEfforts)) {
1171
+ if (v) lines2.push(` ${k}: ${v}`);
1172
+ }
1173
+ }
1174
+ return lines2.join("\n");
1175
+ });
1176
+ const dshBaseUrl = baseURL.endsWith("/v1") ? baseURL : `${baseURL.replace(/\/+$/, "")}/v1`;
1177
+ const a6apiBlockLines = [
1178
+ ` a6api:`,
1179
+ ` displayName: A6API`,
1180
+ ` apiKeyEnv: ${A6API_CRED_REF}`,
1181
+ ` api: openai-completions`,
1182
+ ` baseURL: ${dshBaseUrl}`,
1183
+ ` models:`,
1184
+ ...modelEntries.length > 0 ? modelEntries : [` []`]
1185
+ ];
1186
+ const lines = yaml.split(/\r?\n/);
1187
+ let inLlm = false;
1188
+ let inProviders = false;
1189
+ let inA6 = false;
1190
+ let a6Start = -1;
1191
+ let a6End = -1;
1192
+ let providersLineIdx = -1;
1193
+ let llmLineIdx = -1;
1194
+ for (let i = 0; i < lines.length; i++) {
1195
+ const line = lines[i];
1196
+ const trimmed = line.trim();
1197
+ if (!trimmed || trimmed.startsWith("#")) continue;
1198
+ const indent = line.match(/^\s*/)?.[0].length ?? 0;
1199
+ if (indent === 0) {
1200
+ inLlm = trimmed.startsWith("llm-pi-ai:");
1201
+ if (inLlm) llmLineIdx = i;
1202
+ inProviders = false;
1203
+ inA6 = false;
1204
+ continue;
1205
+ }
1206
+ if (inLlm && indent === 2 && trimmed.startsWith("providers:")) {
1207
+ inProviders = true;
1208
+ providersLineIdx = i;
1209
+ inA6 = false;
1210
+ continue;
1211
+ }
1212
+ if (inProviders && indent === 4) {
1213
+ if (trimmed.startsWith("a6api:")) {
1214
+ inA6 = true;
1215
+ a6Start = i;
1216
+ a6End = i + 1;
1217
+ } else {
1218
+ if (inA6) {
1219
+ a6End = i;
1220
+ inA6 = false;
1221
+ }
1222
+ }
1223
+ continue;
1224
+ }
1225
+ if (inA6 && indent > 4) {
1226
+ a6End = i + 1;
1227
+ } else if (inA6 && indent <= 4) {
1228
+ a6End = i;
1229
+ inA6 = false;
1230
+ }
1231
+ }
1232
+ if (a6Start >= 0) {
1233
+ lines.splice(a6Start, a6End - a6Start, ...a6apiBlockLines);
1234
+ } else if (providersLineIdx >= 0) {
1235
+ lines.splice(providersLineIdx + 1, 0, ...a6apiBlockLines);
1236
+ } else if (llmLineIdx >= 0) {
1237
+ lines.splice(llmLineIdx + 1, 0, ` providers:`, ...a6apiBlockLines);
1238
+ } else {
1239
+ lines.push(`llm-pi-ai:`, ` providers:`, ...a6apiBlockLines);
1240
+ }
1241
+ await fsp.mkdir(path.dirname(sFile), { recursive: true });
1242
+ await fsp.writeFile(sFile, lines.join("\n"), "utf8");
1243
+ }
1244
+ async function getDshConfiguredModels() {
1245
+ try {
1246
+ const yaml = await fsp.readFile(settingsFile(), "utf8");
1247
+ const lines = yaml.split(/\r?\n/);
1248
+ let inA6 = false;
1249
+ let inModels = false;
1250
+ const modelIds = [];
1251
+ for (const line of lines) {
1252
+ const trimmed = line.trim();
1253
+ const indent = line.match(/^\s*/)?.[0].length ?? 0;
1254
+ if (indent === 4 && trimmed.startsWith("a6api:")) {
1255
+ inA6 = true;
1256
+ inModels = false;
1257
+ continue;
1258
+ }
1259
+ if (inA6 && indent <= 4 && !trimmed.startsWith("a6api:")) {
1260
+ inA6 = false;
1261
+ inModels = false;
1262
+ }
1263
+ if (inA6 && indent === 6 && trimmed.startsWith("models:")) {
1264
+ inModels = true;
1265
+ continue;
1266
+ }
1267
+ if (inModels && indent === 8 && trimmed.startsWith("- id:")) {
1268
+ const id = trimmed.replace(/^- id:\s*/, "").trim();
1269
+ if (id) modelIds.push(id);
1270
+ }
1271
+ }
1272
+ return modelIds;
1273
+ } catch {
1274
+ return [];
1275
+ }
1276
+ }
1277
+
1278
+ // src/index.ts
1279
+ var name = "@lynn123411/dsh-a6api";
1280
+ var inject = ["webServer"];
1281
+ var PREFIX = "/api/dsh-a6api";
1282
+ function sendJson(res, status, body) {
1283
+ res.writeHead(status, {
1284
+ "content-type": "application/json; charset=utf-8",
1285
+ "cache-control": "no-cache",
1286
+ "access-control-allow-origin": "*",
1287
+ "access-control-allow-headers": "*",
1288
+ "access-control-allow-methods": "GET, POST, OPTIONS"
1289
+ });
1290
+ res.end(JSON.stringify(body));
1291
+ }
1292
+ function readBody(req) {
1293
+ return new Promise((resolve, reject) => {
1294
+ let data = "";
1295
+ req.on("data", (chunk) => {
1296
+ data += chunk;
1297
+ if (data.length > 2 * 1024 * 1024) {
1298
+ req.destroy();
1299
+ reject(new Error("Body too large"));
1300
+ }
1301
+ });
1302
+ req.on("end", () => resolve(data.trim()));
1303
+ req.on("error", reject);
1304
+ });
1305
+ }
1306
+ async function parseJsonBody(req) {
1307
+ const text = await readBody(req);
1308
+ if (!text) return {};
1309
+ try {
1310
+ return JSON.parse(text);
1311
+ } catch {
1312
+ throw new Error("Invalid JSON body");
1313
+ }
1314
+ }
1315
+ var merchantCardCache = /* @__PURE__ */ new Map();
1316
+ function apply(ctx) {
1317
+ const webServer = ctx.webServer || (ctx.get ? ctx.get("webServer") : null);
1318
+ if (webServer && typeof webServer.register === "function") {
1319
+ ctx.effect(() => {
1320
+ const unregister = webServer.register({
1321
+ kind: "prefix",
1322
+ path: PREFIX,
1323
+ handler: async (req, res) => {
1324
+ const url = new URL(req.url || "/", "http://localhost");
1325
+ const pathname = url.pathname.replace(PREFIX, "") || "/";
1326
+ if (req.method === "OPTIONS") {
1327
+ res.writeHead(204, {
1328
+ "access-control-allow-origin": "*",
1329
+ "access-control-allow-headers": "*",
1330
+ "access-control-allow-methods": "GET, POST, OPTIONS"
1331
+ });
1332
+ return res.end();
1333
+ }
1334
+ try {
1335
+ if (pathname === "/state" && (req.method === "GET" || req.method === "HEAD")) {
1336
+ const config = await readPluginConfig();
1337
+ const token = config.accessToken || config.sessionCookie || "";
1338
+ const balance = await fetchBalance(config.baseURL, config.apiKey, config.userId, token);
1339
+ if (balance?.userId && String(balance.userId) !== config.userId) {
1340
+ config.userId = String(balance.userId);
1341
+ await savePluginConfig(config);
1342
+ }
1343
+ const dshConfiguredModels = await getDshConfiguredModels();
1344
+ let modelIds = [];
1345
+ if (config.apiKey) {
1346
+ modelIds = await fetchTokenModels(config.baseURL, config.apiKey);
1347
+ }
1348
+ if (modelIds.length === 0) {
1349
+ modelIds = [
1350
+ .../* @__PURE__ */ new Set([
1351
+ ...config.activeModels,
1352
+ ...dshConfiguredModels,
1353
+ "gpt-5.6-sol",
1354
+ "gpt-5.6-terra",
1355
+ "gpt-5.6-luna",
1356
+ "claude-fable-5",
1357
+ "claude-opus-5",
1358
+ "grok-4.6"
1359
+ ])
1360
+ ];
1361
+ }
1362
+ if (config.userId || token) {
1363
+ const missing = modelIds.filter((m) => !merchantCardCache.has(m.toLowerCase()));
1364
+ if (missing.length > 0) {
1365
+ const found = await getKnownMerchantsFromLogs(config.userId, token, missing);
1366
+ for (const [mName, card] of Object.entries(found)) {
1367
+ merchantCardCache.set(mName.toLowerCase(), card);
1368
+ }
1369
+ }
1370
+ }
1371
+ const dshSet = new Set(dshConfiguredModels);
1372
+ const models = modelIds.map((mId) => {
1373
+ const meta = resolveModelMeta(mId);
1374
+ const cachedCard = merchantCardCache.get(mId.toLowerCase());
1375
+ return {
1376
+ model_name: mId,
1377
+ brand: meta.brand,
1378
+ contextWindow: meta.contextWindow,
1379
+ maxTokens: meta.maxTokens,
1380
+ modalities: meta.modalities,
1381
+ hasReasoning: Boolean(meta.reasoningEfforts || meta.thinkingFormat),
1382
+ inDsh: dshSet.has(mId),
1383
+ merchant: cachedCard,
1384
+ probeStatus: cachedCard ? "success" : "idle"
1385
+ };
1386
+ });
1387
+ const recentLogs = await fetchRecentLogs(config.userId, token, 20);
1388
+ const response = {
1389
+ config,
1390
+ balance,
1391
+ models,
1392
+ dshConfiguredModels,
1393
+ recentLogs
1394
+ };
1395
+ return sendJson(res, 200, { ok: true, data: response });
1396
+ }
1397
+ if (pathname === "/config" && req.method === "POST") {
1398
+ const body = await parseJsonBody(req);
1399
+ const current = await readPluginConfig();
1400
+ const rawToken = body.accessToken !== void 0 ? body.accessToken : body.sessionCookie !== void 0 ? body.sessionCookie : current.accessToken || current.sessionCookie || "";
1401
+ const updated = {
1402
+ baseURL: body.baseURL !== void 0 ? body.baseURL : current.baseURL,
1403
+ apiKey: body.apiKey !== void 0 ? body.apiKey : current.apiKey,
1404
+ accessToken: rawToken,
1405
+ sessionCookie: rawToken,
1406
+ userId: body.userId !== void 0 ? body.userId : current.userId,
1407
+ activeModels: Array.isArray(body.activeModels) ? body.activeModels : current.activeModels,
1408
+ customBaseURL: body.customBaseURL !== void 0 ? body.customBaseURL : current.customBaseURL
1409
+ };
1410
+ const balance = await fetchBalance(updated.baseURL, updated.apiKey, updated.userId, updated.accessToken);
1411
+ if (balance?.userId) {
1412
+ updated.userId = String(balance.userId);
1413
+ }
1414
+ await savePluginConfig(updated);
1415
+ if (updated.activeModels.length > 0) {
1416
+ await syncToDshSettings(updated.baseURL, updated.activeModels);
1417
+ }
1418
+ return sendJson(res, 200, { ok: true, config: updated, balance });
1419
+ }
1420
+ if (pathname === "/balance" && (req.method === "GET" || req.method === "HEAD")) {
1421
+ const config = await readPluginConfig();
1422
+ const token = config.accessToken || config.sessionCookie || "";
1423
+ const balance = await fetchBalance(config.baseURL, config.apiKey, config.userId, token);
1424
+ const recentLogs = await fetchRecentLogs(config.userId, token, 20);
1425
+ return sendJson(res, 200, { ok: true, balance, recentLogs });
1426
+ }
1427
+ if (pathname === "/logs" && (req.method === "GET" || req.method === "HEAD")) {
1428
+ const config = await readPluginConfig();
1429
+ const token = config.accessToken || config.sessionCookie || "";
1430
+ const recentLogs = await fetchRecentLogs(config.userId, token, 30);
1431
+ return sendJson(res, 200, { ok: true, logs: recentLogs });
1432
+ }
1433
+ if (pathname === "/probe" && req.method === "POST") {
1434
+ const body = await parseJsonBody(req);
1435
+ const config = await readPluginConfig();
1436
+ const token = config.accessToken || config.sessionCookie || "";
1437
+ const modelName = body.modelName;
1438
+ if (modelName && modelName !== "all") {
1439
+ const result = await probeSingleModel(config.baseURL, config.apiKey, config.userId, token, modelName);
1440
+ if (result.merchant) {
1441
+ merchantCardCache.set(modelName.toLowerCase(), result.merchant);
1442
+ }
1443
+ return sendJson(res, 200, { ok: true, result });
1444
+ }
1445
+ let modelIds = body.modelNames;
1446
+ if (!Array.isArray(modelIds) || modelIds.length === 0) {
1447
+ modelIds = await fetchTokenModels(config.baseURL, config.apiKey);
1448
+ }
1449
+ if (modelIds.length === 0) {
1450
+ modelIds = config.activeModels;
1451
+ }
1452
+ const results = [];
1453
+ for (const m of modelIds) {
1454
+ const r = await probeSingleModel(config.baseURL, config.apiKey, config.userId, token, m);
1455
+ if (r.merchant) {
1456
+ merchantCardCache.set(m.toLowerCase(), r.merchant);
1457
+ }
1458
+ results.push(r);
1459
+ }
1460
+ return sendJson(res, 200, { ok: true, results });
1461
+ }
1462
+ if (pathname === "/sync-models" && req.method === "POST") {
1463
+ const body = await parseJsonBody(req);
1464
+ const config = await readPluginConfig();
1465
+ const modelIds = Array.isArray(body.modelIds) ? body.modelIds : [];
1466
+ const baseURL = body.baseURL || config.baseURL;
1467
+ config.activeModels = modelIds;
1468
+ config.baseURL = baseURL;
1469
+ await savePluginConfig(config);
1470
+ await syncToDshSettings(baseURL, modelIds);
1471
+ const dshConfiguredModels = await getDshConfiguredModels();
1472
+ return sendJson(res, 200, { ok: true, dshConfiguredModels });
1473
+ }
1474
+ return sendJson(res, 404, { ok: false, error: "Not found" });
1475
+ } catch (err) {
1476
+ console.error("[dsh-a6api] API error:", err);
1477
+ return sendJson(res, 500, { ok: false, error: err?.message || String(err) });
1478
+ }
1479
+ }
1480
+ });
1481
+ return () => {
1482
+ if (typeof unregister === "function") unregister();
1483
+ };
1484
+ }, "dsh-a6api: web API router");
1485
+ }
1486
+ }
1487
+ export {
1488
+ A6API_CATALOG,
1489
+ apply,
1490
+ fetchBalance,
1491
+ fetchChannelDetails,
1492
+ fetchRecentLogs,
1493
+ fetchTokenModels,
1494
+ getKnownMerchantsFromLogs,
1495
+ inject,
1496
+ name,
1497
+ probeSingleModel,
1498
+ readPluginConfig,
1499
+ resolveModelMeta,
1500
+ savePluginConfig,
1501
+ syncToDshSettings
1502
+ };
1503
+ //# sourceMappingURL=index.js.map