@lynn123411/dsh-a6api 1.2.0 → 1.4.0

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