dsh-desktop-statusbar 2.1.1 → 2.2.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.en.md +132 -108
- package/README.md +130 -108
- package/docs/pricing.en.png +0 -0
- package/docs/pricing.png +0 -0
- package/docs/settings.en.png +0 -0
- package/docs/settings.png +0 -0
- package/lib/balance-api.js +60 -47
- package/lib/client.js +1604 -216
- package/lib/index.js +917 -303
- package/lib/release-age.js +509 -0
- package/lib/self-update.js +368 -0
- package/package.json +8 -2
- package/tools/plugin-dir.cjs +112 -0
- package/tools/shot.cjs +365 -0
- package/tools/sync-from-plugin.cjs +49 -0
- package/tools/sync-to-plugin.cjs +71 -0
- package/tools/test.cjs +99 -0
package/lib/balance-api.js
CHANGED
|
@@ -242,7 +242,38 @@ const adapters = [
|
|
|
242
242
|
if (Number.isFinite(total) !== true) return null;
|
|
243
243
|
/* 积分不是货币:currency 留空让客户端不拼货币符号;不给 parts,
|
|
244
244
|
气泡因此只显示总数、不列套餐构成。 */
|
|
245
|
-
|
|
245
|
+
const out = { currency: '', total: formatCredits(total), parts: [], isCredits: true };
|
|
246
|
+
/* 套餐列表:新插件(dsh-connect-workbuddy)的 /usage 带 credits.packages,就是官方客户端
|
|
247
|
+
「积分套餐」那一栏的数据。老插件 /status 只有 credits.total,**不产生 rewards** ——
|
|
248
|
+
客户端据此保持现在的纯总数显示,不会因为字段缺失去猜。 */
|
|
249
|
+
const packages = Array.isArray(credits.packages) ? credits.packages : null;
|
|
250
|
+
if (packages === null) return out;
|
|
251
|
+
const rewards = [];
|
|
252
|
+
for (let i = 0; i < packages.length; i += 1) {
|
|
253
|
+
const one = packages[i];
|
|
254
|
+
if (one === null || one === undefined || typeof one !== 'object') continue;
|
|
255
|
+
const size = Number(one.size);
|
|
256
|
+
const remain = Number(one.remain);
|
|
257
|
+
/* size/remain 缺一个就没法算「已用/总量」:跳过这个包,
|
|
258
|
+
不让一条坏数据把整条气泡弄崩(旧插件/上游漂移都可能缺字段)。 */
|
|
259
|
+
if (Number.isFinite(size) !== true || Number.isFinite(remain) !== true || size < 0) continue;
|
|
260
|
+
const name = typeof one.packageName === 'string' ? one.packageName : '';
|
|
261
|
+
if (name.length === 0) continue;
|
|
262
|
+
const monthly = one.monthly === true;
|
|
263
|
+
rewards.push({
|
|
264
|
+
name,
|
|
265
|
+
/* 「已用」与官方同口径:总量 - 剩余(connect 插件已按 CapacitySize/Remain 算好);
|
|
266
|
+
剩余超过总量这种异常数据夹住到 0,不显示负数。 */
|
|
267
|
+
used: Math.max(0, size - remain),
|
|
268
|
+
total: size,
|
|
269
|
+
/* 一次性包看到期日;月度包看「下期刷新」(官方对月度包不显示「到期」,别编造到期时间) */
|
|
270
|
+
expireAt: monthly ? null : dateOfMs(one.expiresAtMs),
|
|
271
|
+
refreshAt: monthly ? dateOfMs(one.cycleRefreshMs) : null,
|
|
272
|
+
monthly,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
out.rewards = rewards;
|
|
276
|
+
return out;
|
|
246
277
|
}
|
|
247
278
|
}
|
|
248
279
|
];
|
|
@@ -262,6 +293,23 @@ function formatCredits(value) {
|
|
|
262
293
|
return whole.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + rest;
|
|
263
294
|
}
|
|
264
295
|
|
|
296
|
+
/**
|
|
297
|
+
* 毫秒时间戳 → 北京时间 'YYYY-MM-DD'(套餐气泡里的到期日/刷新日)。
|
|
298
|
+
*
|
|
299
|
+
* 为什么固定按北京时间算、不看运行环境时区:这个项目的所有「日」口径(峰谷计价、
|
|
300
|
+
* 签到日)都按北京时间;套餐到期时间给用户看的是**中国的日期**,跟着机器时区走
|
|
301
|
+
* 会在海外机器上差一天(服务端实测值如 1794056975000 = 北京时间 2026-11-07 21:09)。
|
|
302
|
+
* 取不到/非法值给 null —— 客户端显示「—」,绝不猜一个日期。
|
|
303
|
+
*/
|
|
304
|
+
function dateOfMs(value) {
|
|
305
|
+
const ms = Number(value);
|
|
306
|
+
if (Number.isFinite(ms) !== true || ms <= 0) return null;
|
|
307
|
+
const bj = new Date(ms + 8 * 3600 * 1000);
|
|
308
|
+
const month = String(bj.getUTCMonth() + 1).padStart(2, '0');
|
|
309
|
+
const day = String(bj.getUTCDate()).padStart(2, '0');
|
|
310
|
+
return bj.getUTCFullYear() + '-' + month + '-' + day;
|
|
311
|
+
}
|
|
312
|
+
|
|
265
313
|
/**
|
|
266
314
|
* 会话线索 → 平台适配器。
|
|
267
315
|
*
|
|
@@ -371,52 +419,17 @@ function pluginRoutesIn(table) {
|
|
|
371
419
|
* · qoder :`usage.userQuota.total` 是**配额容量**,`remaining` 才是余额(语义正好相反)
|
|
372
420
|
* · trae :同时有 `total` / `available` / `workAvailable`
|
|
373
421
|
* 若写成"找任意名为 total 的数字",在 qoder 上会稳定显示一个**看起来合理的错数**。
|
|
374
|
-
*
|
|
375
|
-
*
|
|
422
|
+
* 与 workbuddy 适配器注释里那条实测教训(拿错平台的 key 去问余额,稳定 401)是同一个原则:
|
|
423
|
+
* 宁可不显示,也不显示错数。
|
|
376
424
|
*
|
|
377
|
-
*
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if (Number.isFinite(total) !== true) return null;
|
|
386
|
-
return { currency: '', total: formatCredits(total), parts: [], isCredits: true };
|
|
387
|
-
},
|
|
388
|
-
/* 配额百分比(智谱那条路的形状):明确带 limits 数组才算,避免把别的 total 当配额 */
|
|
389
|
-
(data) => {
|
|
390
|
-
const inner = data !== null && data !== undefined && data.data !== null && data.data !== undefined
|
|
391
|
-
? data.data
|
|
392
|
-
: null;
|
|
393
|
-
if (inner === null || inner === undefined || Array.isArray(inner.limits) !== true) return null;
|
|
394
|
-
let percent = null;
|
|
395
|
-
for (let i = 0; i < inner.limits.length; i += 1) {
|
|
396
|
-
const one = inner.limits[i];
|
|
397
|
-
if (one === null || one === undefined) continue;
|
|
398
|
-
const kind = typeof one.type === 'string' ? one.type.toUpperCase() : '';
|
|
399
|
-
if (kind !== 'TOKENS_LIMIT' && kind !== 'CREDIT_LIMIT') continue;
|
|
400
|
-
const value = Number(one.percentage);
|
|
401
|
-
if (Number.isFinite(value) !== true) continue;
|
|
402
|
-
if (percent === null || value > percent) percent = value;
|
|
403
|
-
}
|
|
404
|
-
if (percent === null) return null;
|
|
405
|
-
return { currency: '', total: percent.toFixed(2) + '%', parts: [], isPercent: true };
|
|
406
|
-
}
|
|
407
|
-
];
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* 本机路由文档 → 余额。抽取器全部不命中就给 null(**不猜**)。
|
|
411
|
-
* 导出是为了让测试能直接钉住"qoder 那种结构必须给 null"这条防线。
|
|
425
|
+
* 这条防线**唯一的实现处就是各适配器自己的 `parse`**:`index.js` 的 `queryLocalAdapter`
|
|
426
|
+
* 拿到本机路由响应后直接调 `adapter.parse(data)` —— workbuddy 只认 `credits.total`、
|
|
427
|
+
* 智谱配额只认带 `limits` 数组的形状,认不出一律给 null(既不猜字段,也不硬凑)。
|
|
428
|
+
* 回归测试(tests/verify-balance-api.js)打的就是这条生产路径。
|
|
429
|
+
*
|
|
430
|
+
* 历史上这里还有一套 `LOCAL_SHAPE_EXTRACTORS` + `balanceFromLocalDoc`:它只被测试引用、
|
|
431
|
+
* 生产从未调用(2026-10 删除)。测试打生产 `parse` 才是真防线 —— 打只给测试用的旁路函数,
|
|
432
|
+
* 绿了也不代表线上安全。
|
|
412
433
|
*/
|
|
413
|
-
function balanceFromLocalDoc(data) {
|
|
414
|
-
if (data === null || data === undefined || typeof data !== 'object') return null;
|
|
415
|
-
for (let i = 0; i < LOCAL_SHAPE_EXTRACTORS.length; i += 1) {
|
|
416
|
-
const value = LOCAL_SHAPE_EXTRACTORS[i](data);
|
|
417
|
-
if (value !== null) return value;
|
|
418
|
-
}
|
|
419
|
-
return null;
|
|
420
|
-
}
|
|
421
434
|
|
|
422
|
-
export { adapters, adapterFor, adaptersFor, pluginRoutesIn,
|
|
435
|
+
export { adapters, adapterFor, adaptersFor, pluginRoutesIn, LOCAL_READONLY_VERBS, LOCAL_NEVER_PROBE };
|