dsh-account-pool 0.1.2 → 0.2.1
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 +91 -8
- package/lib/index.js +57 -4
- package/lib/trae-upstream.js +4 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -453,6 +453,27 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
453
453
|
h("td", { style: Object.assign({}, TD_STYLE, { fontSize: "12px", opacity: 0.7 }) }, note || ""),
|
|
454
454
|
);
|
|
455
455
|
|
|
456
|
+
/**
|
|
457
|
+
* 「今日获得积分」统计行。
|
|
458
|
+
*
|
|
459
|
+
* 数据来源是执行前后两次余额的差值(签到接口不回奖励数量,
|
|
460
|
+
* Trae 的任务流也没有计费字段)。所以:
|
|
461
|
+
* - 没执行过 → 显示「未执行」,不猜
|
|
462
|
+
* - 执行过但算式缺数据 → 显示「—」
|
|
463
|
+
* - 有数字 → 显示合计,部分账号缺数据时加注
|
|
464
|
+
*/
|
|
465
|
+
const earnedRow = () => h("tr", { key: "earned" },
|
|
466
|
+
h("td", { style: Object.assign({}, TD_STYLE, { fontWeight: 600 }) }, "今日获得积分"),
|
|
467
|
+
h("td", { style: Object.assign({}, TD_STYLE, NUM_STYLE, { color: "var(--dsw-alias-state-success-primary)" }) },
|
|
468
|
+
typeof lastRun?.totalEarned === "number"
|
|
469
|
+
? "+" + formatNumber(lastRun.totalEarned)
|
|
470
|
+
: h("span", { style: { opacity: 0.5 } }, lastRun ? "—" : "未执行")),
|
|
471
|
+
h("td", { style: Object.assign({}, TD_STYLE, { fontSize: "12px", opacity: 0.7 }) },
|
|
472
|
+
typeof lastRun?.totalEarned === "number"
|
|
473
|
+
? (lastRun.earnedPartial ? "部分账号余额未取到,合计可能偏低" : "执行前后余额之差")
|
|
474
|
+
: "点「全部执行」后统计"),
|
|
475
|
+
);
|
|
476
|
+
|
|
456
477
|
return h("div", null,
|
|
457
478
|
h("div", { style: CARD_STYLE },
|
|
458
479
|
h("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "10px" } },
|
|
@@ -479,8 +500,10 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
479
500
|
// 直接调用会 ReferenceError(跨作用域)。
|
|
480
501
|
const map = {};
|
|
481
502
|
for (const r of out.results || []) map[r.accountId] = r;
|
|
482
|
-
|
|
483
|
-
|
|
503
|
+
// 把「本次获得的总积分」也带进 lastRun,供统计行显示
|
|
504
|
+
onRunDone(map, { totalEarned: out.totalEarned, earnedPartial: out.earnedPartial });
|
|
505
|
+
// 提示里带上实际到手的积分,比干巴巴一句"已完成"有用
|
|
506
|
+
setNotice(region, "ok", grantedText(out.totalEarned, "每日任务"));
|
|
484
507
|
// 重新拉只读状态,让旅行/抽奖/连登的完成数同步更新
|
|
485
508
|
await reload();
|
|
486
509
|
} catch (error) {
|
|
@@ -512,6 +535,7 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
512
535
|
taskRow("猫猫旅行", travelDone, "到站领奖 · 空闲派出", "今日已派或在途都算完成"),
|
|
513
536
|
taskRow("连登兑换", redeemDone, "满 7/14/28 天可兑换", redeemPending > 0 ? redeemPending + " 个账号有可兑换档位" : "没有待兑换的档位"),
|
|
514
537
|
taskRow("抽奖", lotteryDone, "次数来自连登兑换", lotteryPending > 0 ? lotteryPending + " 个账号有抽奖次数" : "暂无抽奖次数"),
|
|
538
|
+
earnedRow(),
|
|
515
539
|
),
|
|
516
540
|
),
|
|
517
541
|
|
|
@@ -597,6 +621,18 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
597
621
|
* 设备已签到 本账号没领,但同设备今天签过(did_checked_in=true)
|
|
598
622
|
* 未签到 都没签,可以执行
|
|
599
623
|
*/
|
|
624
|
+
/**
|
|
625
|
+
* 执行完成的提示文案,带上本次获得的积分。
|
|
626
|
+
*
|
|
627
|
+
* 拿不到数字时不编造——只说完成了几个账号。
|
|
628
|
+
* 部分账号算不出差值时加「部分」字样,避免让人以为这是全部。
|
|
629
|
+
*/
|
|
630
|
+
function grantedText(totalEarned, what) {
|
|
631
|
+
if (typeof totalEarned !== "number") return "已完成" + what;
|
|
632
|
+
if (totalEarned === 0) return "已完成" + what + "(本次没有新增积分)";
|
|
633
|
+
return what + "完成,获得 " + formatNumber(totalEarned) + " 积分";
|
|
634
|
+
}
|
|
635
|
+
|
|
600
636
|
function checkinCell(account) {
|
|
601
637
|
if (account.checkinError) {
|
|
602
638
|
return h("span", { style: { color: "var(--dsw-alias-state-error-primary)", fontSize: "12px" } }, "查询失败");
|
|
@@ -615,7 +651,7 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
615
651
|
return h("span", { style: { opacity: 0.7 } }, "未签到");
|
|
616
652
|
}
|
|
617
653
|
|
|
618
|
-
function TraeTasksView({ region, data, busy, setBusy, setNotice, notice, reload, onRunDone }) {
|
|
654
|
+
function TraeTasksView({ region, data, busy, setBusy, setNotice, notice, lastRun, reload, onRunDone }) {
|
|
619
655
|
const accounts = data.accounts || [];
|
|
620
656
|
const total = accounts.length;
|
|
621
657
|
|
|
@@ -629,6 +665,24 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
629
665
|
|
|
630
666
|
const sumCredits = accounts.reduce((sum, a) => sum + (typeof a.credits === "number" ? a.credits : 0), 0);
|
|
631
667
|
|
|
668
|
+
/**
|
|
669
|
+
* 「今日获得积分」统计行。
|
|
670
|
+
*
|
|
671
|
+
* 与 WorkBuddy 同源:执行前后两次余额的差值。Trae 的签到接口
|
|
672
|
+
* 只回成功/失败,任务流也没有计费字段,差值是真值。
|
|
673
|
+
*/
|
|
674
|
+
const earnedRow = () => h("tr", { key: "earned" },
|
|
675
|
+
h("td", { style: Object.assign({}, TD_STYLE, { fontWeight: 600 }) }, "今日获得积分"),
|
|
676
|
+
h("td", { style: Object.assign({}, TD_STYLE, NUM_STYLE, { color: "var(--dsw-alias-state-success-primary)" }) },
|
|
677
|
+
typeof lastRun?.totalEarned === "number"
|
|
678
|
+
? "+" + formatNumber(lastRun.totalEarned)
|
|
679
|
+
: h("span", { style: { opacity: 0.5 } }, lastRun ? "—" : "未执行")),
|
|
680
|
+
h("td", { style: Object.assign({}, TD_STYLE, { fontSize: "12px", opacity: 0.7 }) },
|
|
681
|
+
typeof lastRun?.totalEarned === "number"
|
|
682
|
+
? (lastRun.earnedPartial ? "部分账号余额未取到,合计可能偏低" : "执行前后余额之差")
|
|
683
|
+
: "点「全部执行」后统计"),
|
|
684
|
+
);
|
|
685
|
+
|
|
632
686
|
const taskRow = (name, done, note) => h("tr", { key: name },
|
|
633
687
|
h("td", { style: TD_STYLE }, name),
|
|
634
688
|
h("td", { style: TD_STYLE },
|
|
@@ -661,8 +715,8 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
661
715
|
if (!out.ok) throw new Error(out.error || "执行失败");
|
|
662
716
|
const map = {};
|
|
663
717
|
for (const r of out.results || []) map[r.accountId] = r;
|
|
664
|
-
onRunDone(map);
|
|
665
|
-
setNotice(region, "ok",
|
|
718
|
+
onRunDone(map, { totalEarned: out.totalEarned, earnedPartial: out.earnedPartial });
|
|
719
|
+
setNotice(region, "ok", grantedText(out.totalEarned, "签到"));
|
|
666
720
|
await reload();
|
|
667
721
|
} catch (error) {
|
|
668
722
|
setNotice(region, "error", error.message);
|
|
@@ -691,6 +745,7 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
691
745
|
h("tbody", null,
|
|
692
746
|
taskRow("每日签到", checkedIn, "每天一次,先查状态再领取,不会重复领"),
|
|
693
747
|
taskRow("积分查询", withCredits, total > 0 ? "当前合计 " + formatNumber(sumCredits) + " 积分" : "只读查询,不消耗额度"),
|
|
748
|
+
earnedRow(),
|
|
694
749
|
),
|
|
695
750
|
),
|
|
696
751
|
),
|
|
@@ -898,6 +953,9 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
898
953
|
const t = usage.totals || {};
|
|
899
954
|
const models = usage.byModel || [];
|
|
900
955
|
const totalCalls = t.requests || 0;
|
|
956
|
+
// 上游是否逐次上报积分消耗。默认按「支持」走,避免老数据缺字段时
|
|
957
|
+
// 把指标整块藏掉;只有后端明确说 false 才隐藏。
|
|
958
|
+
const reportsCredits = usage.reportsPerRequestCredits !== false;
|
|
901
959
|
const failRate = totalCalls > 0 ? (t.errors || 0) / totalCalls : 0;
|
|
902
960
|
|
|
903
961
|
/** 核心指标块:大号数字 + 小标签。 */
|
|
@@ -996,7 +1054,8 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
996
1054
|
// 用 grid 而不是 flex-wrap:wrap 在窄面板里会把第 4 块挤到下一行,
|
|
997
1055
|
// grid 的 1fr 会按可用宽度均分,永远保持一行。
|
|
998
1056
|
h("div", {
|
|
999
|
-
|
|
1057
|
+
// 少一个指标时用 3 列网格,避免留下一个空位
|
|
1058
|
+
className: reportsCredits ? "wb-kpi-grid-4" : "wb-kpi-grid-3",
|
|
1000
1059
|
style: {
|
|
1001
1060
|
display: "grid",
|
|
1002
1061
|
gridTemplateColumns: "repeat(4, minmax(0, 1fr))",
|
|
@@ -1013,7 +1072,15 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
1013
1072
|
kpi("总 tokens", formatCompact(t.totalTokens), { full: formatNumber(t.totalTokens) }),
|
|
1014
1073
|
// 积分精确到个位、不缩写:它是能对账的数字,
|
|
1015
1074
|
// 显示成 8.4K 会让人不知道到底扣了多少。
|
|
1016
|
-
|
|
1075
|
+
//
|
|
1076
|
+
// 上游不逐次上报消耗时整块隐藏(Trae 的对话流没有计费
|
|
1077
|
+
// 字段,累加恒为 0)。显示一个恒为 0 的指标比不显示更糟:
|
|
1078
|
+
// 用户会以为「没消耗」或「统计坏了」,而真相是上游不给。
|
|
1079
|
+
// 也不拿积分接口的累计值来凑数——那是账号总量,不是本页
|
|
1080
|
+
// 消耗,混着展示会直接误导。
|
|
1081
|
+
reportsCredits
|
|
1082
|
+
? kpi("积分消耗", formatNumber(t.credits), { full: formatNumber(t.credits) + " 积分" })
|
|
1083
|
+
: null,
|
|
1017
1084
|
// 延迟只统计成功请求。没有成功请求时显示 —,
|
|
1018
1085
|
// 而不是误导性的 0(或把失败请求的快速返回当成"很快")。
|
|
1019
1086
|
h("div", {
|
|
@@ -1684,7 +1751,23 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
1684
1751
|
return () => clearInterval(timer);
|
|
1685
1752
|
}, []);
|
|
1686
1753
|
// 上次每日任务的执行结果:签到没有只读接口,靠它显示完成情况。
|
|
1687
|
-
|
|
1754
|
+
// 除了每账号结果(key 是 accountId),还带两个汇总结算字段:
|
|
1755
|
+
// totalEarned 本次获得的总积分(执行前后余额之差)
|
|
1756
|
+
// earnedPartial 是否有账号算不出差值(合计可能偏低)
|
|
1757
|
+
const [lastRun, setLastRunState] = useState(null);
|
|
1758
|
+
/**
|
|
1759
|
+
* 保存执行结果。
|
|
1760
|
+
*
|
|
1761
|
+
* 按 accountId 建立索引,同时把汇总字段挂在同一个对象上——
|
|
1762
|
+
* 这样「按账号取结果」和「读合计」共用一份状态,不用两个 state
|
|
1763
|
+
* 引起额外的重渲染。
|
|
1764
|
+
*/
|
|
1765
|
+
const setLastRun = useCallback((results, summary) => {
|
|
1766
|
+
setLastRunState(Object.assign({}, results, {
|
|
1767
|
+
totalEarned: summary?.totalEarned,
|
|
1768
|
+
earnedPartial: summary?.earnedPartial === true,
|
|
1769
|
+
}));
|
|
1770
|
+
}, []);
|
|
1688
1771
|
const pollTimer = useRef(null);
|
|
1689
1772
|
|
|
1690
1773
|
/** 给某个区域设一条提示信息。 */
|
package/lib/index.js
CHANGED
|
@@ -355,6 +355,16 @@ async function createRegionStack({ region, dshHome, config, logger, ctx }) {
|
|
|
355
355
|
region,
|
|
356
356
|
providerId,
|
|
357
357
|
isTrae,
|
|
358
|
+
/**
|
|
359
|
+
* 上游是否**逐次上报**积分消耗。
|
|
360
|
+
*
|
|
361
|
+
* WorkBuddy 的响应里带 credit 字段,能按请求累加;
|
|
362
|
+
* Trae 的对话流只有六种事件(metadata/timing_cost/output/
|
|
363
|
+
* extra_info/token_usage/done),没有任何计费字段——它只在
|
|
364
|
+
* 积分接口给一个累计值。那个累计值不能当「本页消耗」展示
|
|
365
|
+
* (含义不同,会误导),所以这种情况下界面直接不显示该指标。
|
|
366
|
+
*/
|
|
367
|
+
reportsPerRequestCredits: !isTrae,
|
|
358
368
|
runTasks,
|
|
359
369
|
// 只读状态:Trae 用 readStatus,WorkBuddy 用现有的那组查询
|
|
360
370
|
readStatus: isTrae ? traeReadStatus : undefined,
|
|
@@ -944,6 +954,13 @@ export function apply(ctx, config = {}) {
|
|
|
944
954
|
const accounts = await stack.store.list()
|
|
945
955
|
if (accounts.length === 0) { json(res, 200, { ok: true, results: [] }); return }
|
|
946
956
|
|
|
957
|
+
// 跑任务前先取一次积分:跑完再取一次,差值就是这次**实际获得的积分**。
|
|
958
|
+
//
|
|
959
|
+
// 为什么不直接读接口返回值:签到接口只回成功/失败,不回奖励数量;
|
|
960
|
+
// Trae 的对话/任务流也没有任何计费字段(实测只有六种事件)。
|
|
961
|
+
// 唯一可靠的真值就是「积分余额的前后差」。
|
|
962
|
+
const before = await stack.loadCredits(true).catch(() => ({}))
|
|
963
|
+
|
|
947
964
|
// 账号间串行:上游对短时间内的密集请求有风控,串行更稳。
|
|
948
965
|
const results = []
|
|
949
966
|
for (const account of accounts) {
|
|
@@ -960,9 +977,39 @@ export function apply(ctx, config = {}) {
|
|
|
960
977
|
}
|
|
961
978
|
}
|
|
962
979
|
|
|
963
|
-
//
|
|
964
|
-
await stack.loadCredits(true).catch(() => {})
|
|
965
|
-
|
|
980
|
+
// 任务跑完刷新积分:这里的结果既是缓存更新,也是「跑完后」的快照
|
|
981
|
+
const after = await stack.loadCredits(true).catch(() => ({}))
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* 算出每个账号这次获得的积分。
|
|
985
|
+
*
|
|
986
|
+
* 只把**正差值**当作「获得」:
|
|
987
|
+
* - 差值为正 → 签到/活动奖励到账
|
|
988
|
+
* - 差值为负 → 期间有别的消耗(对话),不算获得,记 0
|
|
989
|
+
* - 两边都取不到 → 标 unknown,界面显示 —,不编造数字
|
|
990
|
+
*/
|
|
991
|
+
const earnedOf = (accountId) => {
|
|
992
|
+
const b = before[accountId]?.total
|
|
993
|
+
const a = after[accountId]?.total
|
|
994
|
+
if (typeof b !== 'number' || typeof a !== 'number') return undefined
|
|
995
|
+
const delta = a - b
|
|
996
|
+
// 负差值可能是「期间用了额度」,不算获得;只取正收益
|
|
997
|
+
return delta > 0 ? Math.round(delta * 100) / 100 : 0
|
|
998
|
+
}
|
|
999
|
+
for (const r of results) {
|
|
1000
|
+
const earned = earnedOf(r.accountId)
|
|
1001
|
+
if (earned !== undefined) r.earnedCredits = earned
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
const totalEarned = results.reduce(
|
|
1005
|
+
(sum, r) => sum + (typeof r.earnedCredits === 'number' ? r.earnedCredits : 0), 0)
|
|
1006
|
+
json(res, 200, {
|
|
1007
|
+
ok: true,
|
|
1008
|
+
results,
|
|
1009
|
+
totalEarned: Math.round(totalEarned * 100) / 100,
|
|
1010
|
+
// 有账号算不出差值时,界面要对「合计」加注说明,避免误以为是全部
|
|
1011
|
+
earnedPartial: results.some(r => typeof r.earnedCredits !== 'number'),
|
|
1012
|
+
})
|
|
966
1013
|
} catch (error) {
|
|
967
1014
|
json(res, 500, { ok: false, error: safeMessage(error) })
|
|
968
1015
|
}
|
|
@@ -1045,7 +1092,13 @@ export function apply(ctx, config = {}) {
|
|
|
1045
1092
|
const list = await ready
|
|
1046
1093
|
const out = {}
|
|
1047
1094
|
for (const stack of list) {
|
|
1048
|
-
|
|
1095
|
+
// 只给「本页时间窗口内按请求累加」这一种口径。
|
|
1096
|
+
// 上游不逐次上报时(Trae),该指标没有真实数据可展示——
|
|
1097
|
+
// 界面据此隐藏它,而不是显示恒为 0 的数字。
|
|
1098
|
+
out[stack.region] = {
|
|
1099
|
+
...stack.usage.snapshot(hours),
|
|
1100
|
+
reportsPerRequestCredits: stack.reportsPerRequestCredits,
|
|
1101
|
+
}
|
|
1049
1102
|
}
|
|
1050
1103
|
json(res, 200, { ok: true, regions: out })
|
|
1051
1104
|
} catch (error) {
|
package/lib/trae-upstream.js
CHANGED
|
@@ -422,6 +422,10 @@ export async function fetchCredits(credential) {
|
|
|
422
422
|
if (Number.isFinite(totalAmount) && Number.isFinite(consumed)) {
|
|
423
423
|
// 保留两位小数:桌面显示也是两位(1621.73)。
|
|
424
424
|
// size = 总量,供界面悬停展示「总量 / 已用 / 剩余」。
|
|
425
|
+
//
|
|
426
|
+
// 不返回 consumed:Trae 的对话流没有计费字段,用量统计拿不到
|
|
427
|
+
// 单次消耗;累计值含义不同(账号总量 ≠ 本页消耗),给了只会
|
|
428
|
+
// 诱导误用,所以不提供。
|
|
425
429
|
return {
|
|
426
430
|
total: Math.round((totalAmount - consumed) * 100) / 100,
|
|
427
431
|
size: totalAmount,
|