@lynn123411/dsh-a6api 1.5.6 → 1.5.8
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/client.js +521 -315
- package/lib/client.js.map +2 -2
- package/lib/index.js +94 -38
- package/lib/index.js.map +4 -4
- package/lib/types/client/store.d.ts +2 -2
- package/lib/types/server/a6api-client.d.ts +3 -1
- package/lib/types/server/net.d.ts +27 -0
- package/package.json +4 -3
package/lib/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import fsp from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
|
|
6
|
-
// node_modules/.pnpm/@deepseek-ai+dsh-home-paths@0.1.
|
|
6
|
+
// node_modules/.pnpm/@deepseek-ai+dsh-home-paths@0.1.3-alpha.2_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-home-paths/lib/index.js
|
|
7
7
|
import { opendir, realpath } from "node:fs/promises";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { basename, dirname, join, resolve } from "node:path";
|
|
@@ -26,6 +26,41 @@ function dshHomePath(...segments) {
|
|
|
26
26
|
return join(resolveDshHome(), ...segments);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
// src/server/net.ts
|
|
30
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
31
|
+
function isFlakyNetworkError(err) {
|
|
32
|
+
if (err?.name === "TimeoutError" || err?.name === "AbortError") return false;
|
|
33
|
+
const cause = err?.cause;
|
|
34
|
+
const raw = `${err?.message || ""} ${cause?.code || ""} ${cause?.message || ""}`;
|
|
35
|
+
return /fetch failed|ECONNRESET|ECONNREFUSED|EHOSTUNREACH|ENETUNREACH|ENOTFOUND|EPIPE|socket hang up|other side closed|UND_ERR_SOCKET|UND_ERR_CONNECT_TIMEOUT|SSL_ERROR|ERR_SSL/i.test(
|
|
36
|
+
raw
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
async function fetchWithNetRetry(url, init = {}, opts) {
|
|
40
|
+
const retries = opts?.retries ?? 1;
|
|
41
|
+
const delayMs = opts?.delayMs ?? 800;
|
|
42
|
+
const { timeoutMs, ...rest } = init;
|
|
43
|
+
let lastErr;
|
|
44
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
45
|
+
try {
|
|
46
|
+
const signal = timeoutMs ? AbortSignal.timeout(timeoutMs) : rest.signal;
|
|
47
|
+
return await fetch(url, { ...rest, signal });
|
|
48
|
+
} catch (err) {
|
|
49
|
+
lastErr = err;
|
|
50
|
+
if (attempt < retries && isFlakyNetworkError(err)) {
|
|
51
|
+
console.warn(
|
|
52
|
+
`[dsh-a6api] \u4E0A\u6E38\u8FDE\u63A5\u77AC\u65AD(${err?.cause?.code || err?.message || "network"})\uFF0C${delayMs}ms \u540E\u91CD\u8BD5:`,
|
|
53
|
+
url.split("?")[0]
|
|
54
|
+
);
|
|
55
|
+
await sleep(delayMs);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw lastErr;
|
|
62
|
+
}
|
|
63
|
+
|
|
29
64
|
// src/server/catalog.ts
|
|
30
65
|
var CATALOG_VERSION = 1;
|
|
31
66
|
function catalogFile() {
|
|
@@ -234,9 +269,9 @@ async function fetchMarketplaceModels(userId, accessToken) {
|
|
|
234
269
|
}
|
|
235
270
|
const headers = buildWebHeaders(userId, accessToken);
|
|
236
271
|
const first = await (async () => {
|
|
237
|
-
const res = await
|
|
272
|
+
const res = await fetchWithNetRetry(`${MARKET_SEARCH}?view=list&page=1&page_size=${PAGE_SIZE}`, {
|
|
238
273
|
headers,
|
|
239
|
-
|
|
274
|
+
timeoutMs: 15e3
|
|
240
275
|
});
|
|
241
276
|
if (!res.ok) throw new Error(`A6API \u5E02\u573A\u63A5\u53E3 HTTP ${res.status}`);
|
|
242
277
|
return res.json();
|
|
@@ -250,9 +285,9 @@ async function fetchMarketplaceModels(userId, accessToken) {
|
|
|
250
285
|
while (idx <= pages) {
|
|
251
286
|
const p = idx++;
|
|
252
287
|
try {
|
|
253
|
-
const res = await
|
|
288
|
+
const res = await fetchWithNetRetry(`${MARKET_SEARCH}?view=list&page=${p}&page_size=${PAGE_SIZE}`, {
|
|
254
289
|
headers,
|
|
255
|
-
|
|
290
|
+
timeoutMs: 15e3
|
|
256
291
|
});
|
|
257
292
|
const j = await res.json();
|
|
258
293
|
all.push(...j?.data?.items || []);
|
|
@@ -285,9 +320,9 @@ var orCache = null;
|
|
|
285
320
|
async function getOpenRouterModels() {
|
|
286
321
|
if (orCache && Date.now() - orCache.at < OR_TTL_MS) return orCache.models;
|
|
287
322
|
try {
|
|
288
|
-
const res = await
|
|
323
|
+
const res = await fetchWithNetRetry(OPENROUTER_URL, {
|
|
289
324
|
headers: { Accept: "application/json", "User-Agent": "Mozilla/5.0" },
|
|
290
|
-
|
|
325
|
+
timeoutMs: 2e4
|
|
291
326
|
});
|
|
292
327
|
if (!res.ok) throw new Error(`OpenRouter HTTP ${res.status}`);
|
|
293
328
|
const j = await res.json();
|
|
@@ -434,9 +469,9 @@ async function fetchBalance(baseURL, apiKey, userId, accessToken) {
|
|
|
434
469
|
const uniqueCandidates = [...new Set(candidates)];
|
|
435
470
|
for (const url of uniqueCandidates) {
|
|
436
471
|
try {
|
|
437
|
-
const res = await
|
|
472
|
+
const res = await fetchWithNetRetry(url, {
|
|
438
473
|
headers: buildWebHeaders2(userId, accessToken),
|
|
439
|
-
|
|
474
|
+
timeoutMs: 6e3
|
|
440
475
|
});
|
|
441
476
|
if (res.ok) {
|
|
442
477
|
const json = await res.json();
|
|
@@ -463,12 +498,12 @@ async function fetchBalance(baseURL, apiKey, userId, accessToken) {
|
|
|
463
498
|
}
|
|
464
499
|
if (!hasAccountAuth && apiKey && apiKey.trim()) {
|
|
465
500
|
try {
|
|
466
|
-
const usageRes = await
|
|
501
|
+
const usageRes = await fetchWithNetRetry(`${cleanUrl}/v1/dashboard/billing/usage?start_date=2024-01-01&end_date=2030-12-31`, {
|
|
467
502
|
headers: {
|
|
468
503
|
Authorization: `Bearer ${apiKey.trim()}`,
|
|
469
504
|
Accept: "application/json"
|
|
470
505
|
},
|
|
471
|
-
|
|
506
|
+
timeoutMs: 6e3
|
|
472
507
|
}).catch(() => null);
|
|
473
508
|
if (usageRes && usageRes.ok) {
|
|
474
509
|
const usageJson = await usageRes.json();
|
|
@@ -501,12 +536,12 @@ async function fetchTokenModels(baseURL, apiKey) {
|
|
|
501
536
|
let lastErr = null;
|
|
502
537
|
for (const url of endpoints) {
|
|
503
538
|
try {
|
|
504
|
-
const res = await
|
|
539
|
+
const res = await fetchWithNetRetry(url, {
|
|
505
540
|
headers: {
|
|
506
541
|
Authorization: `Bearer ${apiKey.trim()}`,
|
|
507
542
|
Accept: "application/json"
|
|
508
543
|
},
|
|
509
|
-
|
|
544
|
+
timeoutMs: 8e3
|
|
510
545
|
});
|
|
511
546
|
if (res.ok) {
|
|
512
547
|
const json = await res.json();
|
|
@@ -526,9 +561,9 @@ async function fetchTokenModels(baseURL, apiKey) {
|
|
|
526
561
|
async function fetchRecentLogs(userId, accessToken, limit = 30) {
|
|
527
562
|
if (!userId && !accessToken) return [];
|
|
528
563
|
try {
|
|
529
|
-
const res = await
|
|
564
|
+
const res = await fetchWithNetRetry(`https://a6api.com/api/log/self?p=1&page_size=${limit}&type=0`, {
|
|
530
565
|
headers: buildWebHeaders2(userId, accessToken),
|
|
531
|
-
|
|
566
|
+
timeoutMs: 8e3
|
|
532
567
|
});
|
|
533
568
|
if (res.ok) {
|
|
534
569
|
const json = await res.json();
|
|
@@ -587,11 +622,11 @@ async function fetchChannelDetails(channelId, userId, accessToken, targetModelNa
|
|
|
587
622
|
const targetName = targetModelName || "";
|
|
588
623
|
const meta = resolveModelMeta(targetName);
|
|
589
624
|
try {
|
|
590
|
-
const res = await
|
|
625
|
+
const res = await fetchWithNetRetry(
|
|
591
626
|
`https://a6api.com/api/marketplace/channels/search?channel_id=${channelId}&view=list&page=1&page_size=20`,
|
|
592
627
|
{
|
|
593
628
|
headers: buildWebHeaders2(userId, accessToken),
|
|
594
|
-
|
|
629
|
+
timeoutMs: 8e3
|
|
595
630
|
}
|
|
596
631
|
);
|
|
597
632
|
if (res.ok) {
|
|
@@ -789,7 +824,7 @@ async function fetchPriceFluctuation(userId, accessToken) {
|
|
|
789
824
|
const headers = buildWebHeaders2(uid || void 0, token || void 0);
|
|
790
825
|
const url = "https://a6api.com/api/marketplace/price-notices";
|
|
791
826
|
try {
|
|
792
|
-
const res = await
|
|
827
|
+
const res = await fetchWithNetRetry(url, { headers, timeoutMs: 8e3 });
|
|
793
828
|
if (res.status === 401 || res.status === 403) {
|
|
794
829
|
console.warn("[dsh-a6api] fetchPriceFluctuation auth failed", res.status);
|
|
795
830
|
return { pendingCount: 0, unseenCount: 0, totalCount: 0, authError: true };
|
|
@@ -852,10 +887,17 @@ function parseMarketplaceResult(json) {
|
|
|
852
887
|
if (!json) return { ok: false, message: "\u7A7A\u54CD\u5E94" };
|
|
853
888
|
const top = json.success === false ? json : null;
|
|
854
889
|
const inner = json.data && json.data.success === false ? json.data : null;
|
|
855
|
-
if (top) return { ok: false, message: top.message
|
|
856
|
-
if (inner) return { ok: false, message: inner.message
|
|
890
|
+
if (top) return { ok: false, message: friendlyMarketMessage(top.message) };
|
|
891
|
+
if (inner) return { ok: false, message: friendlyMarketMessage(inner.message) };
|
|
857
892
|
return { ok: true, data: json.data };
|
|
858
893
|
}
|
|
894
|
+
function friendlyMarketMessage(msg) {
|
|
895
|
+
const s = String(msg || "");
|
|
896
|
+
if (s === "invalid_request") {
|
|
897
|
+
return "\u4E0A\u6E38\u62D2\u7EDD\u4E86\u8BE5\u64CD\u4F5C(invalid_request)\uFF1A\u53C2\u6570\u672A\u901A\u8FC7\u5E73\u53F0\u6821\u9A8C\uFF08\u53EF\u80FD\u662F\u5E73\u53F0\u63A5\u53E3\u8C03\u6574\u6216\u56FA\u5B9A\u8BB0\u5F55\u5DF2\u53D8\u5316\uFF09\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u6216\u5230\u5B98\u7F51\u5904\u7406";
|
|
898
|
+
}
|
|
899
|
+
return s || "\u64CD\u4F5C\u5931\u8D25";
|
|
900
|
+
}
|
|
859
901
|
function extractArray(json) {
|
|
860
902
|
if (!json) return [];
|
|
861
903
|
if (Array.isArray(json)) return json;
|
|
@@ -868,9 +910,9 @@ function extractArray(json) {
|
|
|
868
910
|
async function fetchMarketplacePins(userId, accessToken) {
|
|
869
911
|
if (!userId && !accessToken) return [];
|
|
870
912
|
try {
|
|
871
|
-
const res = await
|
|
913
|
+
const res = await fetchWithNetRetry("https://a6api.com/api/marketplace/pins", {
|
|
872
914
|
headers: buildWebHeaders2(userId, accessToken),
|
|
873
|
-
|
|
915
|
+
timeoutMs: 8e3
|
|
874
916
|
});
|
|
875
917
|
if (!res.ok) {
|
|
876
918
|
console.warn("[dsh-a6api] fetchMarketplacePins HTTP", res.status);
|
|
@@ -899,9 +941,9 @@ async function fetchMarketplacePins(userId, accessToken) {
|
|
|
899
941
|
async function fetchTokens(userId, accessToken) {
|
|
900
942
|
if (!userId && !accessToken) return [];
|
|
901
943
|
try {
|
|
902
|
-
const res = await
|
|
944
|
+
const res = await fetchWithNetRetry("https://a6api.com/api/token/?p=1&size=100", {
|
|
903
945
|
headers: buildWebHeaders2(userId, accessToken),
|
|
904
|
-
|
|
946
|
+
timeoutMs: 8e3
|
|
905
947
|
});
|
|
906
948
|
if (!res.ok) return [];
|
|
907
949
|
const json = await res.json().catch(() => null);
|
|
@@ -923,18 +965,21 @@ function friendlyActionError(err) {
|
|
|
923
965
|
if (raw.includes("aborted due to timeout") || err?.name === "TimeoutError") {
|
|
924
966
|
return "\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u91CD\u8BD5";
|
|
925
967
|
}
|
|
968
|
+
if (isFlakyNetworkError(err)) {
|
|
969
|
+
return "\u4E0E\u4E0A\u6E38\u8FDE\u63A5\u4E0D\u7A33\u5B9A\uFF08\u5DF2\u81EA\u52A8\u91CD\u8BD5\u4ECD\u5931\u8D25\uFF09\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
970
|
+
}
|
|
926
971
|
return raw;
|
|
927
972
|
}
|
|
928
973
|
async function marketplacePin(userId, accessToken, payload) {
|
|
929
974
|
try {
|
|
930
|
-
const res = await
|
|
975
|
+
const res = await fetchWithNetRetry("https://a6api.com/api/marketplace/pin", {
|
|
931
976
|
method: "POST",
|
|
932
977
|
headers: {
|
|
933
978
|
"Content-Type": "application/json",
|
|
934
979
|
...buildWebHeaders2(userId, accessToken)
|
|
935
980
|
},
|
|
936
981
|
body: JSON.stringify(payload),
|
|
937
|
-
|
|
982
|
+
timeoutMs: 1e4
|
|
938
983
|
});
|
|
939
984
|
const json = await res.json().catch(() => null);
|
|
940
985
|
const result = parseMarketplaceResult(json);
|
|
@@ -947,14 +992,14 @@ async function marketplacePin(userId, accessToken, payload) {
|
|
|
947
992
|
}
|
|
948
993
|
async function marketplaceUnpin(userId, accessToken, payload) {
|
|
949
994
|
try {
|
|
950
|
-
const res = await
|
|
995
|
+
const res = await fetchWithNetRetry("https://a6api.com/api/marketplace/unpin", {
|
|
951
996
|
method: "POST",
|
|
952
997
|
headers: {
|
|
953
998
|
"Content-Type": "application/json",
|
|
954
999
|
...buildWebHeaders2(userId, accessToken)
|
|
955
1000
|
},
|
|
956
1001
|
body: JSON.stringify(payload),
|
|
957
|
-
|
|
1002
|
+
timeoutMs: 1e4
|
|
958
1003
|
});
|
|
959
1004
|
const json = await res.json().catch(() => null);
|
|
960
1005
|
if (!res.ok && !json) return { ok: false, message: `HTTP ${res.status}` };
|
|
@@ -966,12 +1011,12 @@ async function marketplaceUnpin(userId, accessToken, payload) {
|
|
|
966
1011
|
}
|
|
967
1012
|
async function marketplaceDisableChannel(userId, accessToken, channelId, model) {
|
|
968
1013
|
try {
|
|
969
|
-
const res = await
|
|
1014
|
+
const res = await fetchWithNetRetry(
|
|
970
1015
|
`https://a6api.com/api/marketplace/channels/${channelId}/disable?model=${encodeURIComponent(model)}`,
|
|
971
1016
|
{
|
|
972
1017
|
method: "POST",
|
|
973
1018
|
headers: buildWebHeaders2(userId, accessToken),
|
|
974
|
-
|
|
1019
|
+
timeoutMs: 1e4
|
|
975
1020
|
}
|
|
976
1021
|
);
|
|
977
1022
|
const json = await res.json().catch(() => null);
|
|
@@ -984,12 +1029,12 @@ async function marketplaceDisableChannel(userId, accessToken, channelId, model)
|
|
|
984
1029
|
}
|
|
985
1030
|
async function marketplaceRestoreChannel(userId, accessToken, channelId, model) {
|
|
986
1031
|
try {
|
|
987
|
-
const res = await
|
|
1032
|
+
const res = await fetchWithNetRetry(
|
|
988
1033
|
`https://a6api.com/api/marketplace/channels/${channelId}/restore?model=${encodeURIComponent(model)}`,
|
|
989
1034
|
{
|
|
990
1035
|
method: "POST",
|
|
991
1036
|
headers: buildWebHeaders2(userId, accessToken),
|
|
992
|
-
|
|
1037
|
+
timeoutMs: 1e4
|
|
993
1038
|
}
|
|
994
1039
|
);
|
|
995
1040
|
const json = await res.json().catch(() => null);
|
|
@@ -1002,7 +1047,7 @@ async function marketplaceRestoreChannel(userId, accessToken, channelId, model)
|
|
|
1002
1047
|
}
|
|
1003
1048
|
|
|
1004
1049
|
// src/server/probe.ts
|
|
1005
|
-
var
|
|
1050
|
+
var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1006
1051
|
async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName) {
|
|
1007
1052
|
const targetModel = modelName || "";
|
|
1008
1053
|
const cleanUrl = cleanBaseUrl(baseURL);
|
|
@@ -1013,7 +1058,7 @@ async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName)
|
|
|
1013
1058
|
let requestOk = false;
|
|
1014
1059
|
let requestError = "";
|
|
1015
1060
|
try {
|
|
1016
|
-
const res = await
|
|
1061
|
+
const res = await fetchWithNetRetry(`${cleanUrl}/v1/chat/completions`, {
|
|
1017
1062
|
method: "POST",
|
|
1018
1063
|
headers: {
|
|
1019
1064
|
"Content-Type": "application/json",
|
|
@@ -1024,8 +1069,9 @@ async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName)
|
|
|
1024
1069
|
messages: [{ role: "user", content: "1" }],
|
|
1025
1070
|
max_tokens: 1
|
|
1026
1071
|
}),
|
|
1027
|
-
// 推理模型(如 grok-4.6)实测单次响应可达 40-90s
|
|
1028
|
-
|
|
1072
|
+
// 推理模型(如 grok-4.6)实测单次响应可达 40-90s+,阈值过短会被频繁掐断导致探测失败;
|
|
1073
|
+
// 走 fetchWithNetRetry:每次尝试独立 180s 窗口,传输层瞬断(ECONNRESET)自动重试一次
|
|
1074
|
+
timeoutMs: 18e4
|
|
1029
1075
|
});
|
|
1030
1076
|
if (res.ok) {
|
|
1031
1077
|
requestOk = true;
|
|
@@ -1037,13 +1083,15 @@ async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName)
|
|
|
1037
1083
|
const raw = err?.message || String(err);
|
|
1038
1084
|
if (raw.includes("aborted due to timeout") || err?.name === "TimeoutError") {
|
|
1039
1085
|
requestError = "\u63A2\u6D4B\u8D85\u65F6(\u9608\u503C180\u79D2) \u2014 \u63A8\u7406\u6A21\u578B\u54CD\u5E94\u8F83\u6162,\u5DF2\u4FDD\u7559\u4E0A\u6B21\u5546\u6237\u6570\u636E,\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
1086
|
+
} else if (isFlakyNetworkError(err)) {
|
|
1087
|
+
requestError = "\u4E0E\u4E0A\u6E38\u8FDE\u63A5\u4E0D\u7A33\u5B9A(fetch failed\uFF0C\u5DF2\u81EA\u52A8\u91CD\u8BD5\u4ECD\u5931\u8D25)\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
1040
1088
|
} else {
|
|
1041
1089
|
requestError = raw;
|
|
1042
1090
|
}
|
|
1043
1091
|
}
|
|
1044
1092
|
const durationMs = Date.now() - startTime;
|
|
1045
1093
|
if (requestOk && (userId || accessToken)) {
|
|
1046
|
-
await
|
|
1094
|
+
await sleep2(1200);
|
|
1047
1095
|
try {
|
|
1048
1096
|
const logs = await fetchRecentLogs(userId, accessToken, 15);
|
|
1049
1097
|
const minTimestamp = Math.floor(startTime / 1e3) - 10;
|
|
@@ -2188,7 +2236,15 @@ function apply(ctx) {
|
|
|
2188
2236
|
if (!tokenId) {
|
|
2189
2237
|
return sendJson(res, 400, { ok: false, error: "\u65E0\u6CD5\u89E3\u6790 API Key \u5BF9\u5E94\u7684\u4EE4\u724C ID\uFF08\u591A\u4EE4\u724C\u8D26\u53F7\u9700\u4FDD\u8BC1\u4EE4\u724C\u5217\u8868\u53EF\u8BFB\uFF09\uFF1B\u53EF\u5148\u300C\u63A2\u6D4B\u5546\u5BB6\u300D\u4E00\u6B21\u540E\u91CD\u8BD5\uFF0C\u6216\u5230\u5B98\u7F51\u624B\u52A8\u53D6\u6D88" });
|
|
2190
2238
|
}
|
|
2191
|
-
|
|
2239
|
+
let channelId = Number(body.channelId) > 0 ? Number(body.channelId) : 0;
|
|
2240
|
+
if (!channelId) {
|
|
2241
|
+
const card2 = cachedMerchantOf(modelName);
|
|
2242
|
+
if (card2?.channel_id) channelId = Number(card2.channel_id);
|
|
2243
|
+
}
|
|
2244
|
+
if (!channelId) {
|
|
2245
|
+
return sendJson(res, 400, { ok: false, error: "\u672A\u80FD\u786E\u5B9A\u8BE5\u6A21\u578B\u56FA\u5B9A\u6240\u5C5E\u7684\u5546\u5BB6\u6E20\u9053\uFF08\u4E0A\u6E38\u53D6\u6D88\u56FA\u5B9A\u9700\u8981\u6E20\u9053 ID\uFF09\uFF1B\u8BF7\u5148\u5237\u65B0\u5217\u8868\u6216\u300C\u63A2\u6D4B\u5546\u5BB6\u300D\u4E00\u6B21\u540E\u91CD\u8BD5\uFF0C\u6216\u5230\u5B98\u7F51\u624B\u52A8\u53D6\u6D88" });
|
|
2246
|
+
}
|
|
2247
|
+
const unpinResult = await marketplaceUnpin(userId, token, { token_id: tokenId, channel_id: channelId, model_name: modelName });
|
|
2192
2248
|
if (!unpinResult.ok) {
|
|
2193
2249
|
return sendJson(res, 400, { ok: false, error: unpinResult.message || "\u53D6\u6D88\u56FA\u5B9A\u5931\u8D25" });
|
|
2194
2250
|
}
|