dsh-account-pool 0.1.2 → 0.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/lib/client.js +106 -7
- package/lib/index.js +66 -4
- package/lib/trae-upstream.js +6 -1
- 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
|
),
|
|
@@ -900,6 +955,28 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
900
955
|
const totalCalls = t.requests || 0;
|
|
901
956
|
const failRate = totalCalls > 0 ? (t.errors || 0) / totalCalls : 0;
|
|
902
957
|
|
|
958
|
+
/**
|
|
959
|
+
* 积分消耗指标。
|
|
960
|
+
*
|
|
961
|
+
* 优先用「按请求累加」的值;它为 0 而上游给了累计消耗时,改用
|
|
962
|
+
* 累计值并注明口径不同——这两个数字含义不一样,不能混着看:
|
|
963
|
+
* 按请求累加:本页时间窗口内消耗了多少
|
|
964
|
+
* 累计已消耗:账号从开通到现在的总量(不受时间窗口影响)
|
|
965
|
+
*/
|
|
966
|
+
const creditsKpi = (totals, whole) => {
|
|
967
|
+
const perRequest = typeof totals.credits === "number" ? totals.credits : 0;
|
|
968
|
+
const upstream = whole.upstreamConsumed;
|
|
969
|
+
if (perRequest > 0) {
|
|
970
|
+
return kpi("积分消耗", formatNumber(perRequest), { full: formatNumber(perRequest) + " 积分" });
|
|
971
|
+
}
|
|
972
|
+
if (typeof upstream === "number" && upstream > 0) {
|
|
973
|
+
return kpi("累计消耗", formatNumber(upstream), {
|
|
974
|
+
full: "上游未上报单次消耗,这里显示账号累计已消耗 " + formatNumber(upstream) + " 积分",
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
return kpi("积分消耗", formatNumber(perRequest), { full: formatNumber(perRequest) + " 积分" });
|
|
978
|
+
};
|
|
979
|
+
|
|
903
980
|
/** 核心指标块:大号数字 + 小标签。 */
|
|
904
981
|
const kpi = (label, value, opts = {}) => h("div", {
|
|
905
982
|
style: {
|
|
@@ -1013,7 +1090,13 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
1013
1090
|
kpi("总 tokens", formatCompact(t.totalTokens), { full: formatNumber(t.totalTokens) }),
|
|
1014
1091
|
// 积分精确到个位、不缩写:它是能对账的数字,
|
|
1015
1092
|
// 显示成 8.4K 会让人不知道到底扣了多少。
|
|
1016
|
-
|
|
1093
|
+
//
|
|
1094
|
+
// 两种口径:
|
|
1095
|
+
// 按请求累加(t.credits)—— 上游逐次上报时可用(WorkBuddy)
|
|
1096
|
+
// 累计已消耗(upstreamConsumed)—— 上游不报单次时用(Trae)
|
|
1097
|
+
// Trae 的对话流没有计费字段,所以逐次累加恒为 0;那种情况下
|
|
1098
|
+
// 显示上游累计值,并在悬停里说明口径,避免误读成"没消耗"。
|
|
1099
|
+
creditsKpi(t, usage),
|
|
1017
1100
|
// 延迟只统计成功请求。没有成功请求时显示 —,
|
|
1018
1101
|
// 而不是误导性的 0(或把失败请求的快速返回当成"很快")。
|
|
1019
1102
|
h("div", {
|
|
@@ -1684,7 +1767,23 @@ window.__ModuleLoader__.load({ id: "dsh-account-pool", factory: (require) => {
|
|
|
1684
1767
|
return () => clearInterval(timer);
|
|
1685
1768
|
}, []);
|
|
1686
1769
|
// 上次每日任务的执行结果:签到没有只读接口,靠它显示完成情况。
|
|
1687
|
-
|
|
1770
|
+
// 除了每账号结果(key 是 accountId),还带两个汇总结算字段:
|
|
1771
|
+
// totalEarned 本次获得的总积分(执行前后余额之差)
|
|
1772
|
+
// earnedPartial 是否有账号算不出差值(合计可能偏低)
|
|
1773
|
+
const [lastRun, setLastRunState] = useState(null);
|
|
1774
|
+
/**
|
|
1775
|
+
* 保存执行结果。
|
|
1776
|
+
*
|
|
1777
|
+
* 按 accountId 建立索引,同时把汇总字段挂在同一个对象上——
|
|
1778
|
+
* 这样「按账号取结果」和「读合计」共用一份状态,不用两个 state
|
|
1779
|
+
* 引起额外的重渲染。
|
|
1780
|
+
*/
|
|
1781
|
+
const setLastRun = useCallback((results, summary) => {
|
|
1782
|
+
setLastRunState(Object.assign({}, results, {
|
|
1783
|
+
totalEarned: summary?.totalEarned,
|
|
1784
|
+
earnedPartial: summary?.earnedPartial === true,
|
|
1785
|
+
}));
|
|
1786
|
+
}, []);
|
|
1688
1787
|
const pollTimer = useRef(null);
|
|
1689
1788
|
|
|
1690
1789
|
/** 给某个区域设一条提示信息。 */
|
package/lib/index.js
CHANGED
|
@@ -944,6 +944,13 @@ export function apply(ctx, config = {}) {
|
|
|
944
944
|
const accounts = await stack.store.list()
|
|
945
945
|
if (accounts.length === 0) { json(res, 200, { ok: true, results: [] }); return }
|
|
946
946
|
|
|
947
|
+
// 跑任务前先取一次积分:跑完再取一次,差值就是这次**实际获得的积分**。
|
|
948
|
+
//
|
|
949
|
+
// 为什么不直接读接口返回值:签到接口只回成功/失败,不回奖励数量;
|
|
950
|
+
// Trae 的对话/任务流也没有任何计费字段(实测只有六种事件)。
|
|
951
|
+
// 唯一可靠的真值就是「积分余额的前后差」。
|
|
952
|
+
const before = await stack.loadCredits(true).catch(() => ({}))
|
|
953
|
+
|
|
947
954
|
// 账号间串行:上游对短时间内的密集请求有风控,串行更稳。
|
|
948
955
|
const results = []
|
|
949
956
|
for (const account of accounts) {
|
|
@@ -960,9 +967,39 @@ export function apply(ctx, config = {}) {
|
|
|
960
967
|
}
|
|
961
968
|
}
|
|
962
969
|
|
|
963
|
-
//
|
|
964
|
-
await stack.loadCredits(true).catch(() => {})
|
|
965
|
-
|
|
970
|
+
// 任务跑完刷新积分:这里的结果既是缓存更新,也是「跑完后」的快照
|
|
971
|
+
const after = await stack.loadCredits(true).catch(() => ({}))
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* 算出每个账号这次获得的积分。
|
|
975
|
+
*
|
|
976
|
+
* 只把**正差值**当作「获得」:
|
|
977
|
+
* - 差值为正 → 签到/活动奖励到账
|
|
978
|
+
* - 差值为负 → 期间有别的消耗(对话),不算获得,记 0
|
|
979
|
+
* - 两边都取不到 → 标 unknown,界面显示 —,不编造数字
|
|
980
|
+
*/
|
|
981
|
+
const earnedOf = (accountId) => {
|
|
982
|
+
const b = before[accountId]?.total
|
|
983
|
+
const a = after[accountId]?.total
|
|
984
|
+
if (typeof b !== 'number' || typeof a !== 'number') return undefined
|
|
985
|
+
const delta = a - b
|
|
986
|
+
// 负差值可能是「期间用了额度」,不算获得;只取正收益
|
|
987
|
+
return delta > 0 ? Math.round(delta * 100) / 100 : 0
|
|
988
|
+
}
|
|
989
|
+
for (const r of results) {
|
|
990
|
+
const earned = earnedOf(r.accountId)
|
|
991
|
+
if (earned !== undefined) r.earnedCredits = earned
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
const totalEarned = results.reduce(
|
|
995
|
+
(sum, r) => sum + (typeof r.earnedCredits === 'number' ? r.earnedCredits : 0), 0)
|
|
996
|
+
json(res, 200, {
|
|
997
|
+
ok: true,
|
|
998
|
+
results,
|
|
999
|
+
totalEarned: Math.round(totalEarned * 100) / 100,
|
|
1000
|
+
// 有账号算不出差值时,界面要对「合计」加注说明,避免误以为是全部
|
|
1001
|
+
earnedPartial: results.some(r => typeof r.earnedCredits !== 'number'),
|
|
1002
|
+
})
|
|
966
1003
|
} catch (error) {
|
|
967
1004
|
json(res, 500, { ok: false, error: safeMessage(error) })
|
|
968
1005
|
}
|
|
@@ -1045,7 +1082,32 @@ export function apply(ctx, config = {}) {
|
|
|
1045
1082
|
const list = await ready
|
|
1046
1083
|
const out = {}
|
|
1047
1084
|
for (const stack of list) {
|
|
1048
|
-
|
|
1085
|
+
const snapshot = stack.usage.snapshot(hours)
|
|
1086
|
+
/**
|
|
1087
|
+
* 上游不报单次消耗时,用积分接口的**累计已消耗**做真值。
|
|
1088
|
+
*
|
|
1089
|
+
* Trae 的对话流没有任何计费字段(实测只有 metadata /
|
|
1090
|
+
* timing_cost / output / extra_info / token_usage / done
|
|
1091
|
+
* 六种事件),所以按请求累加的 credits 恒为 0。它的积分
|
|
1092
|
+
* 接口能给出累计已消耗,虽然分不到「哪次请求」,但至少
|
|
1093
|
+
* 是真实数字,比显示 0 有意义。
|
|
1094
|
+
*
|
|
1095
|
+
* 用缓存(不强制刷新)以免打开用量页就打一轮上游。
|
|
1096
|
+
*/
|
|
1097
|
+
const credits = await stack.loadCredits().catch(() => ({}))
|
|
1098
|
+
let consumedTotal = 0
|
|
1099
|
+
let hasConsumed = false
|
|
1100
|
+
for (const account of Object.values(credits)) {
|
|
1101
|
+
if (typeof account?.consumed === 'number') {
|
|
1102
|
+
consumedTotal += account.consumed
|
|
1103
|
+
hasConsumed = true
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
out[stack.region] = {
|
|
1107
|
+
...snapshot,
|
|
1108
|
+
// 上游累计口径(目前只有 Trae 有);undefined 表示这家不支持
|
|
1109
|
+
upstreamConsumed: hasConsumed ? Math.round(consumedTotal * 100) / 100 : undefined,
|
|
1110
|
+
}
|
|
1049
1111
|
}
|
|
1050
1112
|
json(res, 200, { ok: true, regions: out })
|
|
1051
1113
|
} catch (error) {
|
package/lib/trae-upstream.js
CHANGED
|
@@ -421,10 +421,15 @@ export async function fetchCredits(credential) {
|
|
|
421
421
|
const consumed = Number(summary?.consumed_amount)
|
|
422
422
|
if (Number.isFinite(totalAmount) && Number.isFinite(consumed)) {
|
|
423
423
|
// 保留两位小数:桌面显示也是两位(1621.73)。
|
|
424
|
-
// size
|
|
424
|
+
// size = 总量,供界面悬停展示「总量 / 已用 / 剩余」。
|
|
425
|
+
// consumed = 累计已消耗。Trae 的对话流**不报告单次消耗**
|
|
426
|
+
// (实测上游只有 metadata/timing_cost/output/extra_info/
|
|
427
|
+
// token_usage/done 六种事件,没有任何计费字段),
|
|
428
|
+
// 所以用量统计只能靠这个累计值算差值。
|
|
425
429
|
return {
|
|
426
430
|
total: Math.round((totalAmount - consumed) * 100) / 100,
|
|
427
431
|
size: totalAmount,
|
|
432
|
+
consumed: Math.round(consumed * 100) / 100,
|
|
428
433
|
}
|
|
429
434
|
}
|
|
430
435
|
return { total: 0 }
|