@cassius0924/dsh-usage-dashboard 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -10
- package/lib/client.js +176 -55
- package/lib/client.js.map +3 -3
- package/lib/index.js +181 -25
- package/lib/index.js.map +3 -3
- package/lib/types/client/dom.d.ts +18 -1
- package/lib/types/client/locales.d.ts +17 -8
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/context.d.ts +37 -1
- package/lib/types/contract.d.ts +50 -10
- package/lib/types/pricing.d.ts +46 -15
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -60,32 +60,71 @@ function isTrustedApiRequest(req) {
|
|
|
60
60
|
|
|
61
61
|
// src/pricing.ts
|
|
62
62
|
var PEAK_PRICING_FROM_MS = Date.UTC(2026, 7, 16, 16, 0, 0);
|
|
63
|
+
var V41_FLASH_PRICING_FROM_MS = Date.UTC(2026, 8, 10, 4, 0, 0);
|
|
64
|
+
var PRO_ROUTED_TO_FLASH_FROM_MS = Date.UTC(2026, 8, 14, 4, 0, 0);
|
|
63
65
|
var PEAK_WINDOWS = [[9, 12], [14, 18]];
|
|
64
66
|
var BEIJING_OFFSET_MS = 8 * 36e5;
|
|
65
|
-
var
|
|
67
|
+
var FLAT_RATES = {
|
|
66
68
|
pro: { cacheHit: 0.025, input: 3, output: 6 },
|
|
67
|
-
flash: { cacheHit: 0.02, input: 1, output: 2 }
|
|
69
|
+
flash: { cacheHit: 0.02, input: 1, output: 2 },
|
|
70
|
+
// The vision model billed at flash rates from day one, so its pre-switch
|
|
71
|
+
// history is costed at the same flat prices as flash.
|
|
72
|
+
vision: { cacheHit: 0.02, input: 1, output: 2 }
|
|
68
73
|
};
|
|
69
|
-
var
|
|
74
|
+
var PEAK_RATES_08_17 = {
|
|
70
75
|
pro: { cacheHit: 0.3, input: 9, output: 27 },
|
|
71
|
-
flash: { cacheHit: 0.1, input: 3, output: 9 }
|
|
76
|
+
flash: { cacheHit: 0.1, input: 3, output: 9 },
|
|
77
|
+
// Official policy: the vision model is priced identically to flash; images
|
|
78
|
+
// cost tokens, not their own multiplier.
|
|
79
|
+
vision: { cacheHit: 0.1, input: 3, output: 9 }
|
|
72
80
|
};
|
|
81
|
+
var PEAK_RATES_09_10 = {
|
|
82
|
+
pro: { cacheHit: 0.3, input: 9, output: 27 },
|
|
83
|
+
flash: { cacheHit: 0.04, input: 2, output: 8 },
|
|
84
|
+
vision: { cacheHit: 0.04, input: 2, output: 8 }
|
|
85
|
+
};
|
|
86
|
+
var PRICE_ERAS = [
|
|
87
|
+
{ fromMs: PEAK_PRICING_FROM_MS, peak: PEAK_RATES_08_17, weekdaysOnly: false },
|
|
88
|
+
{ fromMs: V41_FLASH_PRICING_FROM_MS, peak: PEAK_RATES_09_10, weekdaysOnly: true }
|
|
89
|
+
];
|
|
90
|
+
function eraAt(timeMs) {
|
|
91
|
+
let era = PRICE_ERAS[0];
|
|
92
|
+
for (const candidate of PRICE_ERAS) if (timeMs >= candidate.fromMs) era = candidate;
|
|
93
|
+
return era;
|
|
94
|
+
}
|
|
73
95
|
var halved = (rates) => ({
|
|
74
96
|
cacheHit: rates.cacheHit / 2,
|
|
75
97
|
input: rates.input / 2,
|
|
76
98
|
output: rates.output / 2
|
|
77
99
|
});
|
|
78
100
|
function tierOf(model) {
|
|
79
|
-
|
|
101
|
+
const name2 = model.toLowerCase();
|
|
102
|
+
if (name2.includes("vision")) return "vision";
|
|
103
|
+
if (name2.includes("flash")) return "flash";
|
|
104
|
+
return "pro";
|
|
105
|
+
}
|
|
106
|
+
function billedTierOf(timeMs, model) {
|
|
107
|
+
const tier = tierOf(model);
|
|
108
|
+
return tier === "pro" && timeMs >= PRO_ROUTED_TO_FLASH_FROM_MS ? "flash" : tier;
|
|
109
|
+
}
|
|
110
|
+
function isBeijingWeekday(timeMs) {
|
|
111
|
+
const day = new Date(timeMs + BEIJING_OFFSET_MS).getUTCDay();
|
|
112
|
+
return day >= 1 && day <= 5;
|
|
113
|
+
}
|
|
114
|
+
function estimateImageTokens(width, height) {
|
|
115
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return 0;
|
|
116
|
+
const area = width * height;
|
|
117
|
+
const scaledArea = Math.min(800 * 800, Math.max(384 * 384, area));
|
|
118
|
+
return 384 * (scaledArea / (800 * 800));
|
|
80
119
|
}
|
|
81
120
|
function isPeak(timeMs) {
|
|
121
|
+
if (eraAt(timeMs).weekdaysOnly && !isBeijingWeekday(timeMs)) return false;
|
|
82
122
|
const hour = Math.floor(((timeMs + BEIJING_OFFSET_MS) % 864e5 + 864e5) % 864e5 / 36e5);
|
|
83
123
|
return PEAK_WINDOWS.some(([from, to]) => hour >= from && hour < to);
|
|
84
124
|
}
|
|
85
125
|
function ratesAt(timeMs, model) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const peak = PEAK_RATES[tier];
|
|
126
|
+
if (timeMs < PEAK_PRICING_FROM_MS) return FLAT_RATES[tierOf(model)];
|
|
127
|
+
const peak = eraAt(timeMs).peak[billedTierOf(timeMs, model)];
|
|
89
128
|
return isPeak(timeMs) ? peak : halved(peak);
|
|
90
129
|
}
|
|
91
130
|
function cacheSavingOf(timeMs, model, cache) {
|
|
@@ -97,23 +136,19 @@ function costOf(timeMs, model, input, cache, output) {
|
|
|
97
136
|
return applyRates(ratesAt(timeMs, model), input, cache, output);
|
|
98
137
|
}
|
|
99
138
|
function costUnderPeakEra(timeMs, model, input, cache, output, forceOffPeak = false) {
|
|
100
|
-
const peak =
|
|
139
|
+
const peak = PRICE_ERAS[PRICE_ERAS.length - 1].peak[billedTierOf(timeMs, model)];
|
|
101
140
|
const rates = !forceOffPeak && isPeak(timeMs) ? peak : halved(peak);
|
|
102
141
|
return applyRates(rates, input, cache, output);
|
|
103
142
|
}
|
|
104
143
|
function pricingInfo(nowMs) {
|
|
105
|
-
const
|
|
144
|
+
const era = eraAt(nowMs);
|
|
145
|
+
const row = (model, tier) => ({ model, peak: era.peak[tier], offPeak: halved(era.peak[tier]) });
|
|
106
146
|
return {
|
|
107
147
|
currency: "CNY",
|
|
108
148
|
switchDate: "2026-08-17",
|
|
109
|
-
|
|
110
|
-
inPeakNow: split && isPeak(nowMs),
|
|
149
|
+
inPeakNow: isPeak(nowMs),
|
|
111
150
|
peakWindows: ["09:00\u201312:00", "14:00\u201318:00"],
|
|
112
|
-
tiers: ["
|
|
113
|
-
model: tier === "pro" ? "deepseek-v4-pro" : "deepseek-v4-flash",
|
|
114
|
-
peak: split ? PEAK_RATES[tier] : LEGACY_RATES[tier],
|
|
115
|
-
offPeak: split ? halved(PEAK_RATES[tier]) : null
|
|
116
|
-
}))
|
|
151
|
+
tiers: [row("deepseek-flash", "flash"), row("deepseek-v4-pro", "pro")]
|
|
117
152
|
};
|
|
118
153
|
}
|
|
119
154
|
|
|
@@ -175,6 +210,18 @@ function bump(map, key, input, output, cache, cost) {
|
|
|
175
210
|
map.set(key, bucket);
|
|
176
211
|
}
|
|
177
212
|
var emptyOverall = () => ({ ...emptyBucket(), reasoning: 0, cacheSavings: 0 });
|
|
213
|
+
var emptyVision = () => ({ images: 0, imageTokens: 0, cost: 0, bytes: 0 });
|
|
214
|
+
function bumpVisionInto(acc, images, imageTokens, cost, bytes) {
|
|
215
|
+
acc.images += images;
|
|
216
|
+
acc.imageTokens += imageTokens;
|
|
217
|
+
acc.cost += cost;
|
|
218
|
+
acc.bytes += bytes;
|
|
219
|
+
}
|
|
220
|
+
function bumpVisionMap(map, key, images, imageTokens, cost, bytes) {
|
|
221
|
+
const acc = map.get(key) ?? emptyVision();
|
|
222
|
+
bumpVisionInto(acc, images, imageTokens, cost, bytes);
|
|
223
|
+
map.set(key, acc);
|
|
224
|
+
}
|
|
178
225
|
var SESSION_TOP_N = 6;
|
|
179
226
|
function titleOf(events, id) {
|
|
180
227
|
let title = "";
|
|
@@ -217,6 +264,7 @@ function addUsageEvent(events, onEvent) {
|
|
|
217
264
|
if (events === void 0) return result;
|
|
218
265
|
let provider = "";
|
|
219
266
|
let model = "";
|
|
267
|
+
const pendingImages = [];
|
|
220
268
|
for (const ev of events) {
|
|
221
269
|
if (ev?.type === "request/header") {
|
|
222
270
|
const cfg = ev.data?.header?.config;
|
|
@@ -226,6 +274,7 @@ function addUsageEvent(events, onEvent) {
|
|
|
226
274
|
}
|
|
227
275
|
continue;
|
|
228
276
|
}
|
|
277
|
+
pendingImages.push(...imagesInEvent(ev));
|
|
229
278
|
if (ev?.type !== "assistant/message") continue;
|
|
230
279
|
const usage = ev.data?.usage;
|
|
231
280
|
if (usage === void 0) continue;
|
|
@@ -239,11 +288,56 @@ function addUsageEvent(events, onEvent) {
|
|
|
239
288
|
const messageId = typeof rawMessageId === "string" && rawMessageId !== "" ? rawMessageId : void 0;
|
|
240
289
|
const rawTurn = ev.data?.turn;
|
|
241
290
|
const turn = typeof rawTurn === "number" && Number.isFinite(rawTurn) ? rawTurn : void 0;
|
|
242
|
-
onEvent(ev.time, input, output, cache, reasoning, provider, model, messageId, turn);
|
|
291
|
+
onEvent(ev.time, input, output, cache, reasoning, provider, model, messageId, turn, pendingImages);
|
|
292
|
+
pendingImages.length = 0;
|
|
243
293
|
result.usageRecords += 1;
|
|
244
294
|
}
|
|
245
295
|
return result;
|
|
246
296
|
}
|
|
297
|
+
function imageBlocks(blocks) {
|
|
298
|
+
const found = [];
|
|
299
|
+
const walkAll = (items) => {
|
|
300
|
+
for (const item of items ?? []) {
|
|
301
|
+
if (item?.type === "image" && item.attachment !== void 0 && item.attachment !== null) {
|
|
302
|
+
found.push(item.attachment);
|
|
303
|
+
}
|
|
304
|
+
if (Array.isArray(item?.content)) walkAll(item.content);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
walkAll(blocks);
|
|
308
|
+
return found;
|
|
309
|
+
}
|
|
310
|
+
function imagesInEvent(ev) {
|
|
311
|
+
if (ev === void 0) return [];
|
|
312
|
+
return [
|
|
313
|
+
...imageBlocks(ev.data?.content),
|
|
314
|
+
...imageBlocks(ev.data?.message?.content)
|
|
315
|
+
];
|
|
316
|
+
}
|
|
317
|
+
async function readSessionEvents(persistence, id) {
|
|
318
|
+
if (typeof persistence.open === "function") {
|
|
319
|
+
const handle = await persistence.open(id, "read");
|
|
320
|
+
try {
|
|
321
|
+
const events2 = [];
|
|
322
|
+
for (; ; ) {
|
|
323
|
+
const slice = await handle.read(events2.length);
|
|
324
|
+
const batch = slice.events ?? [];
|
|
325
|
+
if (batch.length === 0) return events2;
|
|
326
|
+
for (const event of batch) events2.push(event);
|
|
327
|
+
}
|
|
328
|
+
} finally {
|
|
329
|
+
try {
|
|
330
|
+
await handle.close();
|
|
331
|
+
} catch {
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (persistence.readFrom === void 0) {
|
|
336
|
+
throw new Error("\u5BBF\u4E3B sessionPersistence \u7F3A\u5C11 open() \u4E0E readFrom()\uFF0C\u65E0\u6CD5\u8BFB\u53D6\u4F1A\u8BDD\u65E5\u5FD7");
|
|
337
|
+
}
|
|
338
|
+
const { events } = await persistence.readFrom(id, 0);
|
|
339
|
+
return events;
|
|
340
|
+
}
|
|
247
341
|
async function fetchUsage(persistence, nowMs = Date.now()) {
|
|
248
342
|
if (persistence === void 0) return { ok: false, error: "\u4F1A\u8BDD\u6301\u4E45\u5316\u670D\u52A1\u4E0D\u53EF\u7528" };
|
|
249
343
|
let headers;
|
|
@@ -259,6 +353,9 @@ async function fetchUsage(persistence, nowMs = Date.now()) {
|
|
|
259
353
|
const modelDays = /* @__PURE__ */ new Map();
|
|
260
354
|
const modelHours = /* @__PURE__ */ new Map();
|
|
261
355
|
const sessions = [];
|
|
356
|
+
const vision = emptyVision();
|
|
357
|
+
const visionDays = /* @__PURE__ */ new Map();
|
|
358
|
+
const visionSessions = /* @__PURE__ */ new Map();
|
|
262
359
|
const coverage = {
|
|
263
360
|
scope: "local-dsh-session-logs",
|
|
264
361
|
listedSessions: 0,
|
|
@@ -287,7 +384,10 @@ async function fetchUsage(persistence, nowMs = Date.now()) {
|
|
|
287
384
|
modelTotals: /* @__PURE__ */ new Map(),
|
|
288
385
|
modelDays: /* @__PURE__ */ new Map(),
|
|
289
386
|
modelHours: /* @__PURE__ */ new Map(),
|
|
290
|
-
sessions: []
|
|
387
|
+
sessions: [],
|
|
388
|
+
vision: emptyVision(),
|
|
389
|
+
visionDays: /* @__PURE__ */ new Map(),
|
|
390
|
+
visionSessions: /* @__PURE__ */ new Map()
|
|
291
391
|
}));
|
|
292
392
|
const bumpOverall = (bucket, input, output, cache, reasoning, cost, cacheSavings) => {
|
|
293
393
|
bucket.input += input;
|
|
@@ -320,15 +420,17 @@ async function fetchUsage(persistence, nowMs = Date.now()) {
|
|
|
320
420
|
session.calls += 1;
|
|
321
421
|
if (time > session.lastActive) session.lastActive = time;
|
|
322
422
|
};
|
|
323
|
-
const sessionIds = headers.map((header) => header.id ?? header.sessionId).filter((sid) => sid !== void 0 && sid !== "");
|
|
423
|
+
const sessionIds = headers.map((header) => header.id ?? header.sessionId ?? header.header?.id ?? header.header?.sessionId).filter((sid) => sid !== void 0 && sid !== "");
|
|
324
424
|
coverage.listedSessions = sessionIds.length;
|
|
325
425
|
for (const sid of sessionIds) {
|
|
326
426
|
try {
|
|
327
|
-
const
|
|
427
|
+
const events = await readSessionEvents(persistence, sid);
|
|
328
428
|
coverage.scannedSessions += 1;
|
|
329
429
|
const session = { id: sid, title: "", total: 0, cost: 0, calls: 0, lastActive: 0 };
|
|
330
430
|
const windowSessions = windowAggregates.map(() => ({ id: sid, title: "", total: 0, cost: 0, calls: 0, lastActive: 0 }));
|
|
331
|
-
const
|
|
431
|
+
const sessionVision = { ...emptyVision(), title: "" };
|
|
432
|
+
const windowVisionSessions = windowAggregates.map(() => ({ ...emptyVision(), title: "" }));
|
|
433
|
+
const scanned = addUsageEvent(events, (time, input, output, cache, reasoning, provider, model, _messageId, _turn, images) => {
|
|
332
434
|
if (coverage.earliestAt === null || time < coverage.earliestAt) coverage.earliestAt = time;
|
|
333
435
|
if (coverage.latestAt === null || time > coverage.latestAt) coverage.latestAt = time;
|
|
334
436
|
const d = new Date(time);
|
|
@@ -343,6 +445,29 @@ async function fetchUsage(persistence, nowMs = Date.now()) {
|
|
|
343
445
|
bumpModel(modelTotals, modelDays, modelHours, modelKey, dayKey, hourKey, input, output, cache, cost);
|
|
344
446
|
bumpOverall(overall, input, output, cache, reasoning, cost, cacheSavings);
|
|
345
447
|
bumpSession(session, time, eventTotal, cost);
|
|
448
|
+
if (images.length > 0) {
|
|
449
|
+
let imageTokensSum = 0;
|
|
450
|
+
let imageCostSum = 0;
|
|
451
|
+
let imageBytesSum = 0;
|
|
452
|
+
for (const image of images) {
|
|
453
|
+
const width = typeof image.width === "number" ? image.width : 0;
|
|
454
|
+
const height = typeof image.height === "number" ? image.height : 0;
|
|
455
|
+
const tokens = estimateImageTokens(width, height);
|
|
456
|
+
imageTokensSum += tokens;
|
|
457
|
+
imageCostSum += costOf(time, model, tokens, 0, 0);
|
|
458
|
+
imageBytesSum += typeof image.bytes === "number" ? image.bytes : 0;
|
|
459
|
+
}
|
|
460
|
+
bumpVisionInto(vision, images.length, imageTokensSum, imageCostSum, imageBytesSum);
|
|
461
|
+
bumpVisionMap(visionDays, dayKey, images.length, imageTokensSum, imageCostSum, imageBytesSum);
|
|
462
|
+
bumpVisionInto(sessionVision, images.length, imageTokensSum, imageCostSum, imageBytesSum);
|
|
463
|
+
for (let i = 0; i < windowAggregates.length; i++) {
|
|
464
|
+
const aggregate = windowAggregates[i];
|
|
465
|
+
if (dayKey < aggregate.startKey || dayKey > aggregate.endKey) continue;
|
|
466
|
+
bumpVisionInto(aggregate.vision, images.length, imageTokensSum, imageCostSum, imageBytesSum);
|
|
467
|
+
bumpVisionMap(aggregate.visionDays, dayKey, images.length, imageTokensSum, imageCostSum, imageBytesSum);
|
|
468
|
+
bumpVisionInto(windowVisionSessions[i], images.length, imageTokensSum, imageCostSum, imageBytesSum);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
346
471
|
for (let i = 0; i < windowAggregates.length; i++) {
|
|
347
472
|
const aggregate = windowAggregates[i];
|
|
348
473
|
if (dayKey < aggregate.startKey || dayKey > aggregate.endKey) continue;
|
|
@@ -372,6 +497,16 @@ async function fetchUsage(persistence, nowMs = Date.now()) {
|
|
|
372
497
|
windowAggregates[i].sessions.push(windowSession);
|
|
373
498
|
}
|
|
374
499
|
}
|
|
500
|
+
if (sessionVision.images > 0) {
|
|
501
|
+
sessionVision.title = titleOf(events, sid);
|
|
502
|
+
visionSessions.set(sid, sessionVision);
|
|
503
|
+
for (let i = 0; i < windowAggregates.length; i++) {
|
|
504
|
+
const windowVision = windowVisionSessions[i];
|
|
505
|
+
if (windowVision.images === 0) continue;
|
|
506
|
+
windowVision.title = sessionVision.title;
|
|
507
|
+
windowAggregates[i].visionSessions.set(sid, windowVision);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
375
510
|
} catch {
|
|
376
511
|
coverage.failedSessions += 1;
|
|
377
512
|
}
|
|
@@ -427,6 +562,22 @@ async function fetchUsage(persistence, nowMs = Date.now()) {
|
|
|
427
562
|
const b = dayMap.get(key) ?? emptyBucket();
|
|
428
563
|
heatmap.push({ date: key, total: b.total, cost: b.cost, calls: b.calls });
|
|
429
564
|
}
|
|
565
|
+
const makeVisionDaily = (map, count) => {
|
|
566
|
+
const result = [];
|
|
567
|
+
for (let i = count - 1; i >= 0; i--) {
|
|
568
|
+
const key = dayKeyOf(dateBefore(i));
|
|
569
|
+
const b = map.get(key) ?? emptyVision();
|
|
570
|
+
result.push({ date: key, images: b.images, imageTokens: b.imageTokens, cost: b.cost });
|
|
571
|
+
}
|
|
572
|
+
return result;
|
|
573
|
+
};
|
|
574
|
+
const makeVisionSessions = (map) => [...map.entries()].map(([id, acc]) => ({
|
|
575
|
+
id,
|
|
576
|
+
title: acc.title !== "" ? acc.title : `\u4F1A\u8BDD ${id.slice(0, 8)}`,
|
|
577
|
+
images: acc.images,
|
|
578
|
+
imageTokens: acc.imageTokens,
|
|
579
|
+
cost: acc.cost
|
|
580
|
+
})).sort((a, b) => b.images - a.images).slice(0, SESSION_TOP_N);
|
|
430
581
|
const models = makeModels(modelTotals, modelDays, modelHours, 30);
|
|
431
582
|
const windows = windowAggregates.map((aggregate) => ({
|
|
432
583
|
days: aggregate.days,
|
|
@@ -435,7 +586,10 @@ async function fetchUsage(persistence, nowMs = Date.now()) {
|
|
|
435
586
|
totals: { ...aggregate.overall },
|
|
436
587
|
models: makeModels(aggregate.modelTotals, aggregate.modelDays, aggregate.modelHours, aggregate.days),
|
|
437
588
|
sessions: [...aggregate.sessions].sort((a, b) => b.cost - a.cost).slice(0, SESSION_TOP_N),
|
|
438
|
-
sessionCount: aggregate.sessions.length
|
|
589
|
+
sessionCount: aggregate.sessions.length,
|
|
590
|
+
vision: { ...aggregate.vision },
|
|
591
|
+
visionDaily: makeVisionDaily(aggregate.visionDays, aggregate.days),
|
|
592
|
+
visionSessions: makeVisionSessions(aggregate.visionSessions)
|
|
439
593
|
}));
|
|
440
594
|
return {
|
|
441
595
|
ok: true,
|
|
@@ -451,6 +605,9 @@ async function fetchUsage(persistence, nowMs = Date.now()) {
|
|
|
451
605
|
sessionCount: sessions.length,
|
|
452
606
|
coverage,
|
|
453
607
|
windows,
|
|
608
|
+
vision: { ...vision },
|
|
609
|
+
visionDaily: makeVisionDaily(visionDays, 30),
|
|
610
|
+
visionSessions: makeVisionSessions(visionSessions),
|
|
454
611
|
totals: {
|
|
455
612
|
input: overall.input,
|
|
456
613
|
output: overall.output,
|
|
@@ -470,8 +627,7 @@ async function fetchSessionUsage(persistence, sessionId) {
|
|
|
470
627
|
if (!isValidSessionId(sessionId)) return { ok: false, error: "\u4F1A\u8BDD id \u683C\u5F0F\u4E0D\u5408\u6CD5" };
|
|
471
628
|
let events;
|
|
472
629
|
try {
|
|
473
|
-
|
|
474
|
-
events = result.events;
|
|
630
|
+
events = await readSessionEvents(persistence, sessionId);
|
|
475
631
|
} catch (err) {
|
|
476
632
|
return { ok: false, error: `\u8BFB\u53D6\u4F1A\u8BDD\u65E5\u5FD7\u5931\u8D25\uFF1A${errorMessage(err)}` };
|
|
477
633
|
}
|