dsh-codearts 1.2.3 → 1.2.4
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/CHANGELOG.md +43 -0
- package/lib/bin.mjs +2 -2
- package/lib/client/index.d.cts +19 -6
- package/lib/client/index.js +354 -203
- package/lib/index.d.mts +28 -0
- package/lib/index.mjs +26 -4
- package/lib/{usage-CzJjkqOp.mjs → usage-DGIuT79_.mjs} +101 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,49 @@
|
|
|
3
3
|
本文件记录本插件的重要改动。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
4
4
|
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
5
5
|
|
|
6
|
+
## [1.2.4] - 2026-11-03
|
|
7
|
+
|
|
8
|
+
### 变更
|
|
9
|
+
|
|
10
|
+
- **登录回调页不再停在一行「可关闭」提示上,标签页会自己关掉**:华为授权完成后浏览器
|
|
11
|
+
必然落回 `http://127.0.0.1:<port>/oauth/callback` —— redirect_uri 由华为侧指定,登录成功
|
|
12
|
+
的标志就是这次 302,华为那边没有自己的「登录成功」页可停留;这一页也没有任何后续逻辑,
|
|
13
|
+
`code` 在响应发出的同一时刻就交给了服务端,token 交换、身份探测与卡片刷新都不依赖它。
|
|
14
|
+
现在页面先显示「正在返回 DeepSeek Harness…」,300ms 后调用 `window.close()` 自动关闭
|
|
15
|
+
标签页(卡片用 `window.open` 打开授权页,标签页可自关);只有从卡片的手动链接打开、
|
|
16
|
+
或浏览器拦截脚本关窗时,才会在 1.2s 后退化成「登录成功,本页可关闭」的提示。
|
|
17
|
+
|
|
18
|
+
### 新增
|
|
19
|
+
|
|
20
|
+
- **用量里记「缓存命中的输入 token」,弹层保持两列、不摊开**:部分上游在流末尾的
|
|
21
|
+
usage 帧里带 `prompt_tokens_details.cached_tokens`(或平铺的 `cached_tokens` /
|
|
22
|
+
`cache_tokens` / `prompt_cache_hit_tokens`),这块以前被丢掉,只留下一个合计。
|
|
23
|
+
现在解析并存进账本(缓存是输入的子集,脏数据会钳到输入以内),设置卡片的「今日」
|
|
24
|
+
与「近 30 天」显示成 `输入 x · 输出 y · 缓存 z`;缓存段**始终显示**(没有命中就是
|
|
25
|
+
`缓存 0`,命中率仅在有命中时附上)—— 隐藏 0 会让「没命中」和「上游没下发该字段」
|
|
26
|
+
看起来一样,反而分不清功能有没有生效。旧账本(没有 `cachedTokens` 字段)读取时按
|
|
27
|
+
0 补齐,不需要迁移。
|
|
28
|
+
底部积分弹层只回答「哪个号还有多少积分」,这些用量数字到设置卡片的「用量」页里看。
|
|
29
|
+
`/api/codearts/debug` 另外返回最近 5 帧 usage 的原样内容
|
|
30
|
+
(`usages`),用来判断上游到底有没有下发缓存字段。
|
|
31
|
+
- **底部的积分挪进输入框工具行、权限控制右边**:输入框下方那行还挤着宿主的 tok/s、
|
|
32
|
+
缓存命中与上下文圆环,模型名一长(`DeepSeek-V4.1-Flash-20261017`)就压不住。现在
|
|
33
|
+
积分段挂在 `conversation.input.left`(宿主契约里的「composer 工具行左侧紧凑控件」,
|
|
34
|
+
渲染在权限 + 计划之后),Provider / 模型 / 倍率 / 排队仍留在输入框下方那行 —— 同一个
|
|
35
|
+
组件的两个实例,用注入的 `zone` 区分,共用同一份数据和两个显隐开关。点开仍是逐号
|
|
36
|
+
明细:每个号一行「名字 + 余额」,下面一行 11px 灰字 `入 · 出 · 命中 · 条数`(思考
|
|
37
|
+
token 放到设置卡片里),原来那个悬停才出的原生 title 和跟着走的尾巴 `·` 一并去掉。
|
|
38
|
+
- **设置卡片的「用量」页:数字块 + 横条 + 悬停卡片**:顶部三个数字块(今日 token /
|
|
39
|
+
缓存命中 / 今日请求)替掉原来的「今日合计 / 近 30 天」两行文字;下面仍按账号、按模型
|
|
40
|
+
出横向柱状图,悬停任意一行弹出明细卡片(输入 / 输出 / 思考 / 缓存命中 / 请求 /
|
|
41
|
+
命中率,数字精确到个位带千分位),版式照 `agent-acta` 的 `.tok-tip`:名称打头、
|
|
42
|
+
分隔线分段、数字等宽右对齐。上游没下发缓存字段时写「上游未下发」,不显示成 0%。
|
|
43
|
+
- **同时记下「思考 token」**:上游在 `completion_tokens_details.reasoning_tokens`
|
|
44
|
+
(平铺的 `reasoning_tokens` 也认)里单列推理模型的思考量 —— 实测华为每帧都带,
|
|
45
|
+
以前整块丢掉,只看得到一个偏大的「输出」。现在存进账本(它是输出的子集,脏数据钳到
|
|
46
|
+
输出以内),界面统一显示成 `输出 697(思考 505)`,上游不给就只是 `输出 697`;
|
|
47
|
+
0 不单独成段,也不显示 `思考 0`。旧账本同样按 0 补齐。
|
|
48
|
+
|
|
6
49
|
## [1.2.3] - 2026-11-02
|
|
7
50
|
|
|
8
51
|
### 新增
|
package/lib/bin.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { R as isBenefitModel, V as CodeArtsAccountStore, W as codeartsAccountsPath, d as fetchCallerIdentity, n as codeartsUsagePath, r as discoverModels, t as CodeArtsUsageTracker } from "./usage-
|
|
2
|
+
import { R as isBenefitModel, V as CodeArtsAccountStore, W as codeartsAccountsPath, d as fetchCallerIdentity, n as codeartsUsagePath, r as discoverModels, t as CodeArtsUsageTracker } from "./usage-DGIuT79_.mjs";
|
|
3
3
|
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
//#region src/version.ts
|
|
5
|
-
const CODEARTS_VERSION = "1.2.
|
|
5
|
+
const CODEARTS_VERSION = "1.2.4";
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/bin.ts
|
|
8
8
|
/**
|
package/lib/client/index.d.cts
CHANGED
|
@@ -62,12 +62,28 @@ interface CodeArtsSettingsKey {
|
|
|
62
62
|
modelsLoading: string;
|
|
63
63
|
usageTitle: string;
|
|
64
64
|
usageToday: string;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
usageRange: string;
|
|
66
|
+
usageStatTokens: string;
|
|
67
|
+
usageStatIn: string;
|
|
68
|
+
usageStatThinking: string;
|
|
69
|
+
usageTipInput: string;
|
|
70
|
+
usageTipOutput: string;
|
|
71
|
+
usageTipReasoning: string;
|
|
72
|
+
usageTipCached: string;
|
|
73
|
+
usageTipNoCache: string;
|
|
74
|
+
usageTipRequests: string;
|
|
75
|
+
usageTipHit: string;
|
|
76
|
+
usageStatHit: string;
|
|
77
|
+
usageStatRequests: string;
|
|
68
78
|
usageRequests: string;
|
|
79
|
+
usageByAccount: string;
|
|
69
80
|
usageRequestCount: string;
|
|
70
81
|
usageTokens: string;
|
|
82
|
+
/** 带缓存命中输入的那一版(上游不给缓存字段时仍用 usageTokens)。 */
|
|
83
|
+
usageCompact: string;
|
|
84
|
+
/** 缓存命中率(`命中 76%`)。 */
|
|
85
|
+
usageCacheHit: string;
|
|
86
|
+
/** 接在输出数字后面的思考 token(`输出 697(思考 505)`);没有思考量时不拼。 */
|
|
71
87
|
usageAllTime: string;
|
|
72
88
|
usageByModel: string;
|
|
73
89
|
usageSlots: string;
|
|
@@ -94,9 +110,6 @@ interface CodeArtsSettingsKey {
|
|
|
94
110
|
creditsDockPanelTitle: string;
|
|
95
111
|
creditsDockRefresh: string;
|
|
96
112
|
creditsDockRefreshing: string;
|
|
97
|
-
creditsDockColAccount: string;
|
|
98
|
-
creditsDockColBalance: string;
|
|
99
|
-
creditsDockColTokens: string;
|
|
100
113
|
creditsDockNoTokens: string;
|
|
101
114
|
creditsDockEmpty: string;
|
|
102
115
|
creditsDockExpiring: string;
|
package/lib/client/index.js
CHANGED
|
@@ -23,7 +23,7 @@ function stripModelSuffixes(name) {
|
|
|
23
23
|
}
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region \0dsh-css:/home/runner/work/dsh-codearts/dsh-codearts/src/client/CodeArtsPluginCard.module.css.mjs
|
|
26
|
-
const css = ".xoziZG_card{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-settings-card-fill);max-width:720px}.xoziZG_header{align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.xoziZG_headText{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.xoziZG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}.xoziZG_desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.xoziZG_statusLine{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.xoziZG_meta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.xoziZG_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 16px}.xoziZG_tabs{margin-top:12px}.xoziZG_tabPanel{padding-top:2px}.xoziZG_accountToolbar{gap:8px;margin-top:12px;display:flex}.xoziZG_accountToolbar>*{flex:1}.xoziZG_manualForm{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:12px}.xoziZG_accountCard{flex-direction:column;gap:10px;margin-top:12px;display:flex}.xoziZG_accountEntry{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-2)}.xoziZG_accountRow{align-items:center;gap:10px;padding:12px;display:flex}.xoziZG_claimAllRow{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;padding:10px 12px 0;display:flex}.xoziZG_avatar{background:var(--dsw-alias-bg-layer-3);width:32px;height:32px;color:var(--dsw-alias-label-primary);border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:inline-flex}.xoziZG_accountInfo{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.xoziZG_accountNameRow{align-items:center;gap:6px;min-width:0;display:flex}.xoziZG_accountName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:600;line-height:20px;overflow:hidden}.xoziZG_accountRowActions{flex:none;gap:6px;display:flex}.xoziZG_accountHint{color:var(--dsw-alias-label-tertiary);margin:0;padding:0 12px 12px;font-size:12px;line-height:18px}.xoziZG_creditBlock{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px}.xoziZG_balanceRow{flex-wrap:wrap;align-items:baseline;gap:6px;display:flex}.xoziZG_balance{color:var(--dsw-alias-label-primary);letter-spacing:-.02em;font-variant-numeric:tabular-nums;font-size:28px;font-weight:600;line-height:34px}.xoziZG_balanceUnit{color:var(--dsw-alias-label-secondary);font-size:12px}.xoziZG_balanceMeta{color:var(--dsw-alias-label-tertiary);margin-left:2px;font-size:12px}.xoziZG_updated{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;margin-left:auto;font-size:12px}.xoziZG_creditRows{flex-direction:column;gap:4px;margin-top:8px;display:flex}.xoziZG_creditRow{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;justify-content:space-between;align-items:baseline;gap:12px;font-size:12px;line-height:18px;display:flex}.xoziZG_usageBar{background:var(--dsw-alias-border-l2);border-radius:2px;height:4px;margin-top:4px;display:block;overflow:hidden}.xoziZG_usageFill{background:var(--dsw-alias-brand-primary);border-radius:2px;height:100%;transition:width .2s;display:block}.xoziZG_expiryRow{margin-top:2px}.xoziZG_expiryLabel{color:var(--dsw-alias-state-warn-label)}.xoziZG_bar{background:var(--dsw-alias-bg-layer-3);border-radius:2px;height:4px;margin-top:6px;display:block;overflow:hidden}.xoziZG_barFill{background:var(--dsw-alias-state-warn-primary);border-radius:2px;height:100%;display:block}.xoziZG_creditLink{margin-top:8px;display:inline-block}.xoziZG_creditFooter{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:8px;margin-top:12px;padding-top:10px;display:flex}.xoziZG_creditHint{color:var(--dsw-alias-label-tertiary);margin:8px 0 0;font-size:12px;line-height:18px}.xoziZG_section{margin-top:16px}.xoziZG_sectionHead{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.xoziZG_sectionLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500}.xoziZG_count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:12px}.xoziZG_hint{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px;line-height:20px}.xoziZG_error{color:var(--dsw-alias-state-error-primary);margin:10px 0 0;font-size:13px;line-height:20px}.xoziZG_actions{flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;display:flex}.xoziZG_danger{color:var(--dsw-alias-state-error-primary)}.xoziZG_link{appearance:none;color:var(--dsw-alias-brand-text);font:inherit;text-align:left;word-break:break-all;cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}.xoziZG_link:hover{text-decoration:underline}.xoziZG_choices{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_choiceRow{border-radius:var(--dsw-radius-sm);align-items:center;gap:8px;margin:0 -6px;padding:5px 6px;display:flex}.xoziZG_choiceRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.xoziZG_modelChart{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_modelRow{font-variant-numeric:tabular-nums;grid-template-columns:minmax(0,1.2fr) minmax(48px,1fr) 56px 52px 64px;align-items:center;gap:8px;padding:4px 0;display:grid}.xoziZG_modelName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;overflow:hidden}.xoziZG_modelTrack{background:var(--dsw-alias-border-l2);border-radius:4px;height:8px;display:block;overflow:hidden}.xoziZG_modelFill{background:var(--dsw-alias-brand-primary);border-radius:4px;height:100%;transition:width .16s;display:block}.xoziZG_modelTokens,.xoziZG_modelShare,.xoziZG_modelRequests{color:var(--dsw-alias-label-secondary);text-align:right;white-space:nowrap;font-size:11px;line-height:16px}.xoziZG_modelRequests{color:var(--dsw-alias-label-tertiary)}.xoziZG_check{flex:1;min-width:0}.xoziZG_check>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.xoziZG_manual{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:0 12px}.xoziZG_manual[open]{padding-bottom:12px}.xoziZG_manualSummary{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;padding:9px 0;font-size:13px;list-style:none;display:flex}.xoziZG_manualSummary::-webkit-details-marker{display:none}.xoziZG_field{flex-direction:column;gap:5px;margin-top:10px;display:flex}.xoziZG_fieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px}.xoziZG_area{box-sizing:border-box;resize:vertical;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:20px}.xoziZG_area::placeholder{color:var(--dsw-alias-label-dimmed)}.xoziZG_area:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}";
|
|
26
|
+
const css = ".xoziZG_card{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-settings-card-fill);max-width:720px}.xoziZG_header{align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.xoziZG_headText{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.xoziZG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}.xoziZG_desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.xoziZG_statusLine{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.xoziZG_meta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.xoziZG_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 16px}.xoziZG_tabs{margin-top:12px}.xoziZG_tabPanel{padding-top:2px}.xoziZG_accountToolbar{gap:8px;margin-top:12px;display:flex}.xoziZG_accountToolbar>*{flex:1}.xoziZG_manualForm{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:12px}.xoziZG_accountCard{flex-direction:column;gap:10px;margin-top:12px;display:flex}.xoziZG_accountEntry{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-2)}.xoziZG_accountRow{align-items:center;gap:10px;padding:12px;display:flex}.xoziZG_claimAllRow{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;padding:10px 12px 0;display:flex}.xoziZG_avatar{background:var(--dsw-alias-bg-layer-3);width:32px;height:32px;color:var(--dsw-alias-label-primary);border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:inline-flex}.xoziZG_accountInfo{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.xoziZG_accountNameRow{align-items:center;gap:6px;min-width:0;display:flex}.xoziZG_accountName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:600;line-height:20px;overflow:hidden}.xoziZG_accountRowActions{flex:none;gap:6px;display:flex}.xoziZG_accountHint{color:var(--dsw-alias-label-tertiary);margin:0;padding:0 12px 12px;font-size:12px;line-height:18px}.xoziZG_creditBlock{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px}.xoziZG_balanceRow{flex-wrap:wrap;align-items:baseline;gap:6px;display:flex}.xoziZG_balance{color:var(--dsw-alias-label-primary);letter-spacing:-.02em;font-variant-numeric:tabular-nums;font-size:28px;font-weight:600;line-height:34px}.xoziZG_balanceUnit{color:var(--dsw-alias-label-secondary);font-size:12px}.xoziZG_balanceMeta{color:var(--dsw-alias-label-tertiary);margin-left:2px;font-size:12px}.xoziZG_updated{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;margin-left:auto;font-size:12px}.xoziZG_creditRows{flex-direction:column;gap:4px;margin-top:8px;display:flex}.xoziZG_creditRow{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;justify-content:space-between;align-items:baseline;gap:12px;font-size:12px;line-height:18px;display:flex}.xoziZG_usageBar{background:var(--dsw-alias-border-l2);border-radius:2px;height:4px;margin-top:4px;display:block;overflow:hidden}.xoziZG_usageFill{background:var(--dsw-alias-brand-primary);border-radius:2px;height:100%;transition:width .2s;display:block}.xoziZG_expiryRow{margin-top:2px}.xoziZG_expiryLabel{color:var(--dsw-alias-state-warn-label)}.xoziZG_bar{background:var(--dsw-alias-bg-layer-3);border-radius:2px;height:4px;margin-top:6px;display:block;overflow:hidden}.xoziZG_barFill{background:var(--dsw-alias-state-warn-primary);border-radius:2px;height:100%;display:block}.xoziZG_creditLink{margin-top:8px;display:inline-block}.xoziZG_creditFooter{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:8px;margin-top:12px;padding-top:10px;display:flex}.xoziZG_creditHint{color:var(--dsw-alias-label-tertiary);margin:8px 0 0;font-size:12px;line-height:18px}.xoziZG_section{margin-top:16px}.xoziZG_sectionHead{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.xoziZG_sectionLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500}.xoziZG_count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:12px}.xoziZG_hint{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px;line-height:20px}.xoziZG_error{color:var(--dsw-alias-state-error-primary);margin:10px 0 0;font-size:13px;line-height:20px}.xoziZG_actions{flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;display:flex}.xoziZG_danger{color:var(--dsw-alias-state-error-primary)}.xoziZG_link{appearance:none;color:var(--dsw-alias-brand-text);font:inherit;text-align:left;word-break:break-all;cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}.xoziZG_link:hover{text-decoration:underline}.xoziZG_choices{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_choiceRow{border-radius:var(--dsw-radius-sm);align-items:center;gap:8px;margin:0 -6px;padding:5px 6px;display:flex}.xoziZG_choiceRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.xoziZG_statGrid{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:10px 0 14px;display:grid}.xoziZG_statCard{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);flex-direction:column;gap:1px;padding:8px 10px;display:flex}.xoziZG_statLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.xoziZG_statValue{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:15px;line-height:20px}.xoziZG_statSub{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:16px}.xoziZG_modelChart{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_modelRow{font-variant-numeric:tabular-nums;border-radius:6px;grid-template-columns:minmax(0,1.2fr) minmax(48px,1fr) 56px 52px 64px;align-items:center;gap:8px;padding:4px 0;display:grid;position:relative}.xoziZG_modelRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.xoziZG_usageTip{z-index:5;border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);min-width:180px;max-width:260px;color:var(--dsw-alias-label-secondary);text-align:left;font-variant-numeric:tabular-nums;pointer-events:none;padding:8px 10px;font-size:12px;line-height:1.9;position:absolute;bottom:calc(100% + 6px);right:0;box-shadow:0 10px 28px #00000073}.xoziZG_usageTipName{max-width:240px;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.xoziZG_usageTipSep{background:var(--dsw-alias-border-l2);height:1px;margin:4px 0}.xoziZG_usageTipRow{justify-content:space-between;gap:24px;display:flex}.xoziZG_usageTipRow span:last-child{color:var(--dsw-alias-label-primary);font-weight:600}.xoziZG_modelName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;overflow:hidden}.xoziZG_modelTrack{background:var(--dsw-alias-border-l2);border-radius:4px;height:8px;display:block;overflow:hidden}.xoziZG_modelFill{background:var(--dsw-alias-brand-primary);border-radius:4px;height:100%;transition:width .16s;display:block}.xoziZG_modelTokens,.xoziZG_modelShare,.xoziZG_modelRequests{color:var(--dsw-alias-label-secondary);text-align:right;white-space:nowrap;font-size:11px;line-height:16px}.xoziZG_modelRequests{color:var(--dsw-alias-label-tertiary)}.xoziZG_check{flex:1;min-width:0}.xoziZG_check>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.xoziZG_manual{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:0 12px}.xoziZG_manual[open]{padding-bottom:12px}.xoziZG_manualSummary{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;padding:9px 0;font-size:13px;list-style:none;display:flex}.xoziZG_manualSummary::-webkit-details-marker{display:none}.xoziZG_field{flex-direction:column;gap:5px;margin-top:10px;display:flex}.xoziZG_fieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px}.xoziZG_area{box-sizing:border-box;resize:vertical;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:20px}.xoziZG_area::placeholder{color:var(--dsw-alias-label-dimmed)}.xoziZG_area:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}";
|
|
27
27
|
const tagId = "dsh-codearts/CodeArtsPluginCard.module.css";
|
|
28
28
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
29
29
|
const tag = document.createElement("style");
|
|
@@ -33,70 +33,79 @@ if (typeof document !== "undefined" && document.querySelector("style[data-plugin
|
|
|
33
33
|
document.head.appendChild(tag);
|
|
34
34
|
}
|
|
35
35
|
var CodeArtsPluginCard_module_css_default = {
|
|
36
|
+
"field": "xoziZG_field",
|
|
37
|
+
"manualForm": "xoziZG_manualForm",
|
|
38
|
+
"accountRow": "xoziZG_accountRow",
|
|
39
|
+
"section": "xoziZG_section",
|
|
40
|
+
"expiryLabel": "xoziZG_expiryLabel",
|
|
41
|
+
"check": "xoziZG_check",
|
|
42
|
+
"balance": "xoziZG_balance",
|
|
43
|
+
"accountToolbar": "xoziZG_accountToolbar",
|
|
44
|
+
"barFill": "xoziZG_barFill",
|
|
45
|
+
"statLabel": "xoziZG_statLabel",
|
|
46
|
+
"meta": "xoziZG_meta",
|
|
36
47
|
"accountNameRow": "xoziZG_accountNameRow",
|
|
37
|
-
"balanceRow": "xoziZG_balanceRow",
|
|
38
|
-
"modelFill": "xoziZG_modelFill",
|
|
39
|
-
"danger": "xoziZG_danger",
|
|
40
48
|
"expiryRow": "xoziZG_expiryRow",
|
|
49
|
+
"balanceUnit": "xoziZG_balanceUnit",
|
|
50
|
+
"accountInfo": "xoziZG_accountInfo",
|
|
51
|
+
"hint": "xoziZG_hint",
|
|
52
|
+
"count": "xoziZG_count",
|
|
53
|
+
"choices": "xoziZG_choices",
|
|
54
|
+
"usageFill": "xoziZG_usageFill",
|
|
55
|
+
"card": "xoziZG_card",
|
|
56
|
+
"usageTipName": "xoziZG_usageTipName",
|
|
57
|
+
"balanceRow": "xoziZG_balanceRow",
|
|
58
|
+
"creditBlock": "xoziZG_creditBlock",
|
|
59
|
+
"header": "xoziZG_header",
|
|
60
|
+
"statSub": "xoziZG_statSub",
|
|
61
|
+
"usageTip": "xoziZG_usageTip",
|
|
62
|
+
"modelRequests": "xoziZG_modelRequests",
|
|
63
|
+
"usageBar": "xoziZG_usageBar",
|
|
64
|
+
"bar": "xoziZG_bar",
|
|
65
|
+
"creditRow": "xoziZG_creditRow",
|
|
66
|
+
"sectionLabel": "xoziZG_sectionLabel",
|
|
41
67
|
"accountEntry": "xoziZG_accountEntry",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"body": "xoziZG_body",
|
|
45
|
-
"barFill": "xoziZG_barFill",
|
|
68
|
+
"modelTrack": "xoziZG_modelTrack",
|
|
69
|
+
"link": "xoziZG_link",
|
|
46
70
|
"updated": "xoziZG_updated",
|
|
47
|
-
"error": "xoziZG_error",
|
|
48
|
-
"avatar": "xoziZG_avatar",
|
|
49
|
-
"balanceMeta": "xoziZG_balanceMeta",
|
|
50
71
|
"desc": "xoziZG_desc",
|
|
51
|
-
"
|
|
72
|
+
"avatar": "xoziZG_avatar",
|
|
73
|
+
"creditHint": "xoziZG_creditHint",
|
|
52
74
|
"tabPanel": "xoziZG_tabPanel",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"choiceRow": "xoziZG_choiceRow",
|
|
58
|
-
"balance": "xoziZG_balance",
|
|
59
|
-
"creditLink": "xoziZG_creditLink",
|
|
60
|
-
"accountRow": "xoziZG_accountRow",
|
|
61
|
-
"usageFill": "xoziZG_usageFill",
|
|
62
|
-
"modelTrack": "xoziZG_modelTrack",
|
|
75
|
+
"fieldLabel": "xoziZG_fieldLabel",
|
|
76
|
+
"modelName": "xoziZG_modelName",
|
|
77
|
+
"danger": "xoziZG_danger",
|
|
78
|
+
"statValue": "xoziZG_statValue",
|
|
63
79
|
"area": "xoziZG_area",
|
|
64
|
-
"
|
|
80
|
+
"claimAllRow": "xoziZG_claimAllRow",
|
|
81
|
+
"tabs": "xoziZG_tabs",
|
|
82
|
+
"usageTipSep": "xoziZG_usageTipSep",
|
|
83
|
+
"name": "xoziZG_name",
|
|
65
84
|
"headText": "xoziZG_headText",
|
|
66
85
|
"accountRowActions": "xoziZG_accountRowActions",
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"header": "xoziZG_header",
|
|
80
|
-
"fieldLabel": "xoziZG_fieldLabel",
|
|
81
|
-
"choices": "xoziZG_choices",
|
|
82
|
-
"creditBlock": "xoziZG_creditBlock",
|
|
83
|
-
"creditRow": "xoziZG_creditRow",
|
|
84
|
-
"field": "xoziZG_field",
|
|
85
|
-
"hint": "xoziZG_hint",
|
|
86
|
-
"count": "xoziZG_count",
|
|
86
|
+
"sectionHead": "xoziZG_sectionHead",
|
|
87
|
+
"modelRow": "xoziZG_modelRow",
|
|
88
|
+
"modelFill": "xoziZG_modelFill",
|
|
89
|
+
"accountHint": "xoziZG_accountHint",
|
|
90
|
+
"modelChart": "xoziZG_modelChart",
|
|
91
|
+
"error": "xoziZG_error",
|
|
92
|
+
"body": "xoziZG_body",
|
|
93
|
+
"manualSummary": "xoziZG_manualSummary",
|
|
94
|
+
"balanceMeta": "xoziZG_balanceMeta",
|
|
95
|
+
"statGrid": "xoziZG_statGrid",
|
|
96
|
+
"choiceRow": "xoziZG_choiceRow",
|
|
97
|
+
"modelShare": "xoziZG_modelShare",
|
|
87
98
|
"accountName": "xoziZG_accountName",
|
|
88
|
-
"
|
|
89
|
-
"
|
|
99
|
+
"modelTokens": "xoziZG_modelTokens",
|
|
100
|
+
"creditLink": "xoziZG_creditLink",
|
|
90
101
|
"creditFooter": "xoziZG_creditFooter",
|
|
91
|
-
"modelRow": "xoziZG_modelRow",
|
|
92
102
|
"manual": "xoziZG_manual",
|
|
93
|
-
"modelChart": "xoziZG_modelChart",
|
|
94
|
-
"modelRequests": "xoziZG_modelRequests",
|
|
95
|
-
"accountToolbar": "xoziZG_accountToolbar",
|
|
96
103
|
"statusLine": "xoziZG_statusLine",
|
|
97
104
|
"creditRows": "xoziZG_creditRows",
|
|
98
|
-
"
|
|
99
|
-
"
|
|
105
|
+
"statCard": "xoziZG_statCard",
|
|
106
|
+
"accountCard": "xoziZG_accountCard",
|
|
107
|
+
"usageTipRow": "xoziZG_usageTipRow",
|
|
108
|
+
"actions": "xoziZG_actions"
|
|
100
109
|
};
|
|
101
110
|
//#endregion
|
|
102
111
|
//#region src/client/CodeArtsPluginCard.tsx
|
|
@@ -128,6 +137,10 @@ function formatCount(value) {
|
|
|
128
137
|
function formatCredits(value) {
|
|
129
138
|
return Number.isFinite(value) ? new Intl.NumberFormat().format(Math.round(value)) : "0";
|
|
130
139
|
}
|
|
140
|
+
/** 浮层里的明细数字要精确到个位并带千分位,不用 k/M 缩写。 */
|
|
141
|
+
function exactCount(value) {
|
|
142
|
+
return formatCredits(value);
|
|
143
|
+
}
|
|
131
144
|
/** 批量领取的 claimingId 哨兵(真实账号 id 不会等于它)。 */
|
|
132
145
|
const CLAIM_ALL = "*";
|
|
133
146
|
/** 一次领取操作的文案:优先用后端逐号结果,老后端只回一条时按 id 归位。 */
|
|
@@ -358,12 +371,25 @@ function ManualTokenForm({ t, token, label, busy, onToken, onLabel, onSubmit })
|
|
|
358
371
|
});
|
|
359
372
|
}
|
|
360
373
|
/**
|
|
374
|
+
* 缓存命中率:缓存输入 / 全部输入,钳在 0~100。
|
|
375
|
+
*
|
|
376
|
+
* 上游没给缓存字段、或今天压根没有输入时返回 undefined —— 统计块里显示「—」,
|
|
377
|
+
* 不要把「没有数据」写成「0%」。
|
|
378
|
+
*/
|
|
379
|
+
function hitPercentOf(totals) {
|
|
380
|
+
const cached = totals.cachedTokens ?? 0;
|
|
381
|
+
if (cached <= 0 || totals.promptTokens <= 0) return void 0;
|
|
382
|
+
return Math.min(100, Math.round(cached / totals.promptTokens * 100));
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
361
385
|
* 横向条形列表:名称 / 条 / token / 占比 / 请求数。
|
|
362
386
|
*
|
|
363
387
|
* 条长按「占组内总量的比例」画(不是按最大值归一化),所以几条加起来就是全部。
|
|
364
388
|
* token 全 0 的日子(只有请求数、没收到 usage 帧)退回按请求数画。
|
|
365
389
|
*/
|
|
366
390
|
function UsageBars({ rows, t }) {
|
|
391
|
+
/** 当前悬停的那一行;卡片浮层只在它上面出现。 */
|
|
392
|
+
const [hovered, setHovered] = (0, react.useState)(null);
|
|
367
393
|
const tokenMode = rows.some((row) => row.totals.totalTokens > 0);
|
|
368
394
|
const valueOf = (row) => tokenMode ? row.totals.totalTokens : row.totals.requests;
|
|
369
395
|
const sum = rows.reduce((total, row) => total + valueOf(row), 0);
|
|
@@ -374,13 +400,18 @@ function UsageBars({ rows, t }) {
|
|
|
374
400
|
children: top.map((row) => {
|
|
375
401
|
const value = valueOf(row);
|
|
376
402
|
const share = sum > 0 ? value / sum * 100 : 0;
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
403
|
+
const totals = row.totals;
|
|
404
|
+
const cached = totals.cachedTokens;
|
|
405
|
+
const pct = hitPercentOf(totals);
|
|
406
|
+
const detail = [t("usageRequests", { n: formatCount(totals.requests) }), t("usageTokens", {
|
|
407
|
+
prompt: formatTokens$1(totals.promptTokens),
|
|
408
|
+
completion: formatTokens$1(totals.completionTokens)
|
|
380
409
|
})].join(" · ");
|
|
381
410
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
382
411
|
className: CodeArtsPluginCard_module_css_default.modelRow,
|
|
383
412
|
title: `${row.label} · ${detail}`,
|
|
413
|
+
onMouseEnter: () => setHovered(row.id),
|
|
414
|
+
onMouseLeave: () => setHovered((prev) => prev === row.id ? null : prev),
|
|
384
415
|
children: [
|
|
385
416
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
386
417
|
className: CodeArtsPluginCard_module_css_default.modelName,
|
|
@@ -403,7 +434,43 @@ function UsageBars({ rows, t }) {
|
|
|
403
434
|
}),
|
|
404
435
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
405
436
|
className: CodeArtsPluginCard_module_css_default.modelRequests,
|
|
406
|
-
children: t("usageRequestCount", { n: formatCount(
|
|
437
|
+
children: t("usageRequestCount", { n: formatCount(totals.requests) })
|
|
438
|
+
}),
|
|
439
|
+
hovered === row.id && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
440
|
+
className: CodeArtsPluginCard_module_css_default.usageTip,
|
|
441
|
+
role: "tooltip",
|
|
442
|
+
children: [
|
|
443
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
444
|
+
className: CodeArtsPluginCard_module_css_default.usageTipName,
|
|
445
|
+
children: row.label
|
|
446
|
+
}),
|
|
447
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: CodeArtsPluginCard_module_css_default.usageTipSep }),
|
|
448
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
449
|
+
className: CodeArtsPluginCard_module_css_default.usageTipRow,
|
|
450
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("usageTipInput") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: exactCount(totals.promptTokens) })]
|
|
451
|
+
}),
|
|
452
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
453
|
+
className: CodeArtsPluginCard_module_css_default.usageTipRow,
|
|
454
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("usageTipOutput") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: exactCount(totals.completionTokens) })]
|
|
455
|
+
}),
|
|
456
|
+
(totals.reasoningTokens ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
457
|
+
className: CodeArtsPluginCard_module_css_default.usageTipRow,
|
|
458
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("usageTipReasoning") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: exactCount(totals.reasoningTokens ?? 0) })]
|
|
459
|
+
}),
|
|
460
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
461
|
+
className: CodeArtsPluginCard_module_css_default.usageTipRow,
|
|
462
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("usageTipCached") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: cached === void 0 ? t("usageTipNoCache") : exactCount(cached) })]
|
|
463
|
+
}),
|
|
464
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: CodeArtsPluginCard_module_css_default.usageTipSep }),
|
|
465
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
466
|
+
className: CodeArtsPluginCard_module_css_default.usageTipRow,
|
|
467
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("usageTipRequests") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("usageRequestCount", { n: formatCount(totals.requests) }) })]
|
|
468
|
+
}),
|
|
469
|
+
pct !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
470
|
+
className: CodeArtsPluginCard_module_css_default.usageTipRow,
|
|
471
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("usageTipHit") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [pct, "%"] })]
|
|
472
|
+
})
|
|
473
|
+
]
|
|
407
474
|
})
|
|
408
475
|
]
|
|
409
476
|
}, row.id);
|
|
@@ -418,7 +485,6 @@ function UsageBars({ rows, t }) {
|
|
|
418
485
|
* 「打断后重新提问要等」的原因。
|
|
419
486
|
*/
|
|
420
487
|
function UsageSection({ usage, accounts, t }) {
|
|
421
|
-
const total = usage.today.requests;
|
|
422
488
|
const stateLabel = {
|
|
423
489
|
ok: t("usageStateOk"),
|
|
424
490
|
capped: t("usageStateCapped"),
|
|
@@ -436,7 +502,9 @@ function UsageSection({ usage, accounts, t }) {
|
|
|
436
502
|
label: entry.id.split(" · ")[0],
|
|
437
503
|
totals: entry.totals
|
|
438
504
|
}));
|
|
439
|
-
const
|
|
505
|
+
const today = usage.today;
|
|
506
|
+
const hasUsage = usage.allTime.requests > 0 || today.requests > 0;
|
|
507
|
+
const hit = hitPercentOf(today);
|
|
440
508
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
441
509
|
className: CodeArtsPluginCard_module_css_default.section,
|
|
442
510
|
children: [
|
|
@@ -447,7 +515,10 @@ function UsageSection({ usage, accounts, t }) {
|
|
|
447
515
|
children: t("usageTitle")
|
|
448
516
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
449
517
|
className: CodeArtsPluginCard_module_css_default.count,
|
|
450
|
-
children: t("
|
|
518
|
+
children: t("usageRange", {
|
|
519
|
+
today: formatCount(today.requests),
|
|
520
|
+
last30: formatCount(usage.last30.requests)
|
|
521
|
+
})
|
|
451
522
|
})]
|
|
452
523
|
}),
|
|
453
524
|
!hasUsage ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -455,38 +526,46 @@ function UsageSection({ usage, accounts, t }) {
|
|
|
455
526
|
children: t("usageEmpty")
|
|
456
527
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
457
528
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
458
|
-
className: CodeArtsPluginCard_module_css_default.
|
|
459
|
-
children: [
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
529
|
+
className: CodeArtsPluginCard_module_css_default.statGrid,
|
|
530
|
+
children: [
|
|
531
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
532
|
+
className: CodeArtsPluginCard_module_css_default.statCard,
|
|
533
|
+
children: [
|
|
534
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
535
|
+
className: CodeArtsPluginCard_module_css_default.statLabel,
|
|
536
|
+
children: t("usageStatTokens")
|
|
537
|
+
}),
|
|
538
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
539
|
+
className: CodeArtsPluginCard_module_css_default.statValue,
|
|
540
|
+
children: formatTokens$1(today.totalTokens)
|
|
541
|
+
}),
|
|
542
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
543
|
+
className: CodeArtsPluginCard_module_css_default.statSub,
|
|
544
|
+
children: [t("usageStatIn", { n: formatTokens$1(today.promptTokens) }), (today.reasoningTokens ?? 0) > 0 ? ` · ${t("usageStatThinking", { n: formatTokens$1(today.reasoningTokens ?? 0) })}` : ""]
|
|
545
|
+
})
|
|
546
|
+
]
|
|
547
|
+
}),
|
|
548
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
549
|
+
className: CodeArtsPluginCard_module_css_default.statCard,
|
|
550
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
551
|
+
className: CodeArtsPluginCard_module_css_default.statLabel,
|
|
552
|
+
children: t("usageStatHit")
|
|
553
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
554
|
+
className: CodeArtsPluginCard_module_css_default.statValue,
|
|
555
|
+
children: hit === void 0 ? "—" : `${hit}%`
|
|
556
|
+
})]
|
|
557
|
+
}),
|
|
558
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
559
|
+
className: CodeArtsPluginCard_module_css_default.statCard,
|
|
560
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
561
|
+
className: CodeArtsPluginCard_module_css_default.statLabel,
|
|
562
|
+
children: t("usageStatRequests")
|
|
563
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
564
|
+
className: CodeArtsPluginCard_module_css_default.statValue,
|
|
565
|
+
children: formatCount(today.requests)
|
|
566
|
+
})]
|
|
567
|
+
})
|
|
568
|
+
]
|
|
490
569
|
}),
|
|
491
570
|
accounts.length > 1 && accountRows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
492
571
|
className: CodeArtsPluginCard_module_css_default.hint,
|
|
@@ -1494,10 +1573,27 @@ const QUEUE_PATH = "/api/codearts/queue";
|
|
|
1494
1573
|
const REFRESH_INTERVAL_MS = 6e4;
|
|
1495
1574
|
/** 排队位置变得快:官方客户端也是 5 秒一次;这是本机路由,打不疼上游。 */
|
|
1496
1575
|
const QUEUE_INTERVAL_MS = 5e3;
|
|
1497
|
-
/**
|
|
1498
|
-
|
|
1576
|
+
/**
|
|
1577
|
+
* 弹层宽度:只放「账号 + 积分」两列,所以按同类插件(dsh-connect-trae 的
|
|
1578
|
+
* `min(248px, …)`)取一档窄宽度 —— 这一层回答的是「哪个号还有多少积分」,
|
|
1579
|
+
* 用量数字不该把它撑成一张报表。
|
|
1580
|
+
*/
|
|
1581
|
+
const PANEL_WIDTH = 260;
|
|
1499
1582
|
/** 弹层与那一行之间的缝隙。 */
|
|
1500
1583
|
const PANEL_GAP = 8;
|
|
1584
|
+
/**
|
|
1585
|
+
* 弹层里每行的列模板:账号 | 积分。
|
|
1586
|
+
*
|
|
1587
|
+
* 数字列用 `auto`:它跟着最宽的那个余额自适应,面板不必为「12,600」预留固定宽度。
|
|
1588
|
+
* 输入 / 输出 / 缓存这些用量数字不进表格,收进整行的悬浮提示({@link usageLine}),
|
|
1589
|
+
* 完整明细在设置卡片里看。
|
|
1590
|
+
*/
|
|
1591
|
+
const PANEL_ROW_COLUMNS = "minmax(0, 1fr) auto";
|
|
1592
|
+
/** 数字格统一右对齐 + 等宽数字,列才对得齐。 */
|
|
1593
|
+
const PANEL_NUM_CELL = {
|
|
1594
|
+
justifySelf: "end",
|
|
1595
|
+
fontVariantNumeric: "tabular-nums"
|
|
1596
|
+
};
|
|
1501
1597
|
/** 那一行在输入框下方,往上开才够空间;给弹层留的最小高度,不够就翻到下面。 */
|
|
1502
1598
|
const PANEL_MIN_ABOVE = 260;
|
|
1503
1599
|
function readSelection(useProjection) {
|
|
@@ -1535,6 +1631,24 @@ function formatClock(ts) {
|
|
|
1535
1631
|
return "";
|
|
1536
1632
|
}
|
|
1537
1633
|
}
|
|
1634
|
+
/**
|
|
1635
|
+
* 弹层里那行用量:`入 12.3k · 出 697(思 505)· 命中 94% · 37 条`。
|
|
1636
|
+
*
|
|
1637
|
+
* 刻意比设置卡片里的完整版短:面板只有 260px,把「缓存 11.5k」也列上就会折成
|
|
1638
|
+
* 两三行,比原来那个悬浮提示还难看。缓存值本身由命中率表达(它是输入的占比),
|
|
1639
|
+
* 思考 token 也不在这里——它是输出的子集,放在设置卡片的统计块里。上游不给缓存
|
|
1640
|
+
* 字段就不显示命中率:显示成 0% 会被当成「没命中」而不是「没数据」。
|
|
1641
|
+
*/
|
|
1642
|
+
function usageLine(usage, t) {
|
|
1643
|
+
const cached = usage.cachedTokens ?? 0;
|
|
1644
|
+
const parts = [t("usageCompact", {
|
|
1645
|
+
prompt: formatTokens(usage.promptTokens),
|
|
1646
|
+
completion: formatTokens(usage.completionTokens)
|
|
1647
|
+
})];
|
|
1648
|
+
if (cached > 0 && usage.promptTokens > 0) parts.push(t("usageCacheHit", { pct: Math.min(100, Math.round(cached / usage.promptTokens * 100)) }));
|
|
1649
|
+
parts.push(t("usageRequestCount", { n: formatNumber(usage.requests) }));
|
|
1650
|
+
return parts.join(" · ");
|
|
1651
|
+
}
|
|
1538
1652
|
/** 账号名与设置卡片同一套取法:备注 > 用户名 > id。 */
|
|
1539
1653
|
function accountName(account) {
|
|
1540
1654
|
return account.label?.trim() || account.userName?.trim() || account.id;
|
|
@@ -1566,21 +1680,20 @@ function rowsOf(data) {
|
|
|
1566
1680
|
};
|
|
1567
1681
|
});
|
|
1568
1682
|
}
|
|
1569
|
-
/**
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1683
|
+
/**
|
|
1684
|
+
* 弹层往上开还是往下开。
|
|
1685
|
+
*
|
|
1686
|
+
* 位置不在这里算:fixed 元素遇到带 transform / backdrop-filter 的祖先时,坐标会
|
|
1687
|
+
* 相对那个祖先而不是视口(工具行就长在输入框卡片里),所以真实偏移要渲染后实测
|
|
1688
|
+
* (见组件里的 layout effect),这里只决定朝哪边翻。
|
|
1689
|
+
*/
|
|
1690
|
+
function placeAbove(rect) {
|
|
1691
|
+
return rect.top > PANEL_MIN_ABOVE;
|
|
1578
1692
|
}
|
|
1579
|
-
function CodeArtsCreditDock({ useProjection, t }) {
|
|
1693
|
+
function CodeArtsCreditDock({ useProjection, t, zone = "dock" }) {
|
|
1580
1694
|
const selection = readSelection(useProjection);
|
|
1581
1695
|
const active = isCodeArts(selection);
|
|
1582
1696
|
const [remain, setRemain] = (0, react.useState)(null);
|
|
1583
|
-
const [total, setTotal] = (0, react.useState)(0);
|
|
1584
1697
|
const [rows, setRows] = (0, react.useState)([]);
|
|
1585
1698
|
/**
|
|
1586
1699
|
* 模型 id -> 目录条目(显示名 + 已格式化的倍率)。
|
|
@@ -1595,6 +1708,15 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1595
1708
|
});
|
|
1596
1709
|
const [queued, setQueued] = (0, react.useState)(null);
|
|
1597
1710
|
const [anchor, setAnchor] = (0, react.useState)(null);
|
|
1711
|
+
/**
|
|
1712
|
+
* 弹层的包含块原点(视口坐标)。
|
|
1713
|
+
*
|
|
1714
|
+
* fixed 只有在祖先没有 transform / backdrop-filter 时才是视口坐标;工具行长在输入框
|
|
1715
|
+
* 卡片里,一旦撞上这类祖先,left/top 就会相对那个祖先算,弹层会飞到窗口外。做法是
|
|
1716
|
+
* 先按 0,0 渲染但不可见,量出面板实际落在哪(就是包含块原点),再把目标坐标减掉它;
|
|
1717
|
+
* 视口情形下量到 (0,0),等于没动。
|
|
1718
|
+
*/
|
|
1719
|
+
const [origin, setOrigin] = (0, react.useState)(null);
|
|
1598
1720
|
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
1599
1721
|
const mounted = (0, react.useRef)(true);
|
|
1600
1722
|
const triggerRef = (0, react.useRef)(null);
|
|
@@ -1632,7 +1754,6 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1632
1754
|
return;
|
|
1633
1755
|
}
|
|
1634
1756
|
setRemain(Math.round(pool.reduce((sum, item) => sum + item.credits.total.remain, 0)));
|
|
1635
|
-
setTotal(Math.round(pool.reduce((sum, item) => sum + item.credits.total.total, 0)));
|
|
1636
1757
|
} catch {}
|
|
1637
1758
|
}, []);
|
|
1638
1759
|
(0, react.useEffect)(() => {
|
|
@@ -1695,6 +1816,19 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1695
1816
|
window.removeEventListener("scroll", close, true);
|
|
1696
1817
|
};
|
|
1697
1818
|
}, [anchor, close]);
|
|
1819
|
+
(0, react.useLayoutEffect)(() => {
|
|
1820
|
+
if (anchor === null) {
|
|
1821
|
+
setOrigin(null);
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
const el = panelRef.current;
|
|
1825
|
+
if (el === null) return;
|
|
1826
|
+
const rect = el.getBoundingClientRect();
|
|
1827
|
+
setOrigin((prev) => prev !== null && Math.abs(prev.x - rect.left) < .5 && Math.abs(prev.y - rect.top) < .5 ? prev : {
|
|
1828
|
+
x: rect.left,
|
|
1829
|
+
y: rect.top
|
|
1830
|
+
});
|
|
1831
|
+
}, [anchor]);
|
|
1698
1832
|
(0, react.useEffect)(() => {
|
|
1699
1833
|
if (!active) return;
|
|
1700
1834
|
const self = rootRef.current;
|
|
@@ -1719,26 +1853,18 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1719
1853
|
if (!active) return null;
|
|
1720
1854
|
const showCredits = flags.credits && remain !== null;
|
|
1721
1855
|
const showQueue = flags.queue && queued !== null;
|
|
1856
|
+
if (zone === "bar" && !showCredits) return null;
|
|
1722
1857
|
const modelId = currentOf(selection)?.model;
|
|
1723
1858
|
const entry = modelId === void 0 ? void 0 : modelInfo[modelId];
|
|
1724
1859
|
const modelLabel = entry === void 0 ? modelId ?? "" : stripModelSuffixes(entry.name);
|
|
1725
1860
|
const ratio = entry?.ratio;
|
|
1726
1861
|
const line = buildDockLine({
|
|
1727
|
-
...showCredits ? { credits: t("creditsDock", { remain: formatNumber(remain ?? 0) }) } : {},
|
|
1862
|
+
...zone === "bar" && showCredits ? { credits: t("creditsDock", { remain: formatNumber(remain ?? 0) }) } : {},
|
|
1728
1863
|
provider: "CodeArts",
|
|
1729
1864
|
...modelLabel === "" ? {} : { model: modelLabel },
|
|
1730
1865
|
...ratio === void 0 ? {} : { ratio },
|
|
1731
|
-
...showQueue && queued !== null ? { queue: t("queueDock", { position: queued.position }) } : {}
|
|
1866
|
+
...zone === "dock" && showQueue && queued !== null ? { queue: t("queueDock", { position: queued.position }) } : {}
|
|
1732
1867
|
}, t);
|
|
1733
|
-
const queueTitle = queued === null ? void 0 : t("queueDockTitle", {
|
|
1734
|
-
model: queued.model,
|
|
1735
|
-
position: queued.position,
|
|
1736
|
-
length: queued.length
|
|
1737
|
-
});
|
|
1738
|
-
const creditsTitle = showCredits ? t("creditsDockTitle", {
|
|
1739
|
-
remain: remain ?? 0,
|
|
1740
|
-
total
|
|
1741
|
-
}) : void 0;
|
|
1742
1868
|
const fetchedAt = rows.reduce((latest, row) => Math.max(latest, row.fetchedAt ?? 0), 0);
|
|
1743
1869
|
const lastUpdated = fetchedAt > 0 ? formatClock(fetchedAt) : "";
|
|
1744
1870
|
const toggle = (event) => {
|
|
@@ -1746,7 +1872,13 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1746
1872
|
close();
|
|
1747
1873
|
return;
|
|
1748
1874
|
}
|
|
1749
|
-
|
|
1875
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
1876
|
+
const above = placeAbove(rect);
|
|
1877
|
+
setAnchor({
|
|
1878
|
+
left: rect.left,
|
|
1879
|
+
top: above ? rect.top - PANEL_GAP : rect.bottom + PANEL_GAP,
|
|
1880
|
+
above
|
|
1881
|
+
});
|
|
1750
1882
|
};
|
|
1751
1883
|
const forceRefresh = async () => {
|
|
1752
1884
|
setRefreshing(true);
|
|
@@ -1770,7 +1902,6 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1770
1902
|
flex: "0 0 auto",
|
|
1771
1903
|
minWidth: "max-content"
|
|
1772
1904
|
},
|
|
1773
|
-
title: queueTitle ?? creditsTitle,
|
|
1774
1905
|
children: [
|
|
1775
1906
|
line.credits !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1776
1907
|
ref: triggerRef,
|
|
@@ -1778,7 +1909,6 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1778
1909
|
"aria-haspopup": "dialog",
|
|
1779
1910
|
"aria-expanded": anchor !== null,
|
|
1780
1911
|
"aria-label": t("creditsDockPanelTitle"),
|
|
1781
|
-
title: creditsTitle,
|
|
1782
1912
|
onClick: toggle,
|
|
1783
1913
|
style: {
|
|
1784
1914
|
all: "unset",
|
|
@@ -1790,20 +1920,21 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1790
1920
|
whiteSpace: "inherit"
|
|
1791
1921
|
},
|
|
1792
1922
|
children: line.credits
|
|
1793
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1923
|
+
}), zone === "dock" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1794
1924
|
"aria-hidden": "true",
|
|
1795
1925
|
children: " · "
|
|
1796
1926
|
})] }),
|
|
1797
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: line.rest }),
|
|
1927
|
+
zone === "dock" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: line.rest }),
|
|
1798
1928
|
anchor !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1799
1929
|
ref: panelRef,
|
|
1800
1930
|
role: "dialog",
|
|
1801
1931
|
"aria-label": t("creditsDockPanelTitle"),
|
|
1802
1932
|
style: {
|
|
1803
1933
|
position: "fixed",
|
|
1804
|
-
left: anchor.left,
|
|
1805
|
-
top: anchor.top,
|
|
1806
|
-
transform: anchor.above ? "translateY(-100%)" : void 0,
|
|
1934
|
+
left: origin === null ? 0 : anchor.left - origin.x,
|
|
1935
|
+
top: origin === null ? 0 : anchor.top - origin.y,
|
|
1936
|
+
transform: anchor.above && origin !== null ? "translateY(-100%)" : void 0,
|
|
1937
|
+
visibility: origin === null ? "hidden" : "visible",
|
|
1807
1938
|
width: PANEL_WIDTH,
|
|
1808
1939
|
maxWidth: "calc(100vw - 16px)",
|
|
1809
1940
|
zIndex: 1e3,
|
|
@@ -1842,73 +1973,60 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1842
1973
|
}),
|
|
1843
1974
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1844
1975
|
style: {
|
|
1845
|
-
display: "
|
|
1846
|
-
|
|
1847
|
-
gap:
|
|
1848
|
-
alignItems: "center",
|
|
1976
|
+
display: "flex",
|
|
1977
|
+
flexDirection: "column",
|
|
1978
|
+
gap: 10,
|
|
1849
1979
|
borderTop: "0.5px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.22))",
|
|
1850
1980
|
paddingTop: 8,
|
|
1981
|
+
fontSize: 12,
|
|
1851
1982
|
color: "var(--dsw-alias-label-tertiary)"
|
|
1852
1983
|
},
|
|
1853
|
-
children: [
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
children: row.usage === void 0 ? "—" : formatTokens(row.usage.totalTokens)
|
|
1900
|
-
}),
|
|
1901
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1902
|
-
style: {
|
|
1903
|
-
justifySelf: "end",
|
|
1904
|
-
fontVariantNumeric: "tabular-nums"
|
|
1905
|
-
},
|
|
1906
|
-
children: row.remain === null ? "—" : formatNumber(row.remain)
|
|
1907
|
-
})
|
|
1908
|
-
]
|
|
1909
|
-
}, row.id);
|
|
1910
|
-
})
|
|
1911
|
-
]
|
|
1984
|
+
children: [rows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("creditsDockEmpty") }), rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1985
|
+
style: {
|
|
1986
|
+
display: "flex",
|
|
1987
|
+
flexDirection: "column",
|
|
1988
|
+
gap: 2,
|
|
1989
|
+
minWidth: 0
|
|
1990
|
+
},
|
|
1991
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1992
|
+
style: {
|
|
1993
|
+
display: "grid",
|
|
1994
|
+
gridTemplateColumns: PANEL_ROW_COLUMNS,
|
|
1995
|
+
gap: "0 8px",
|
|
1996
|
+
alignItems: "center",
|
|
1997
|
+
minWidth: 0
|
|
1998
|
+
},
|
|
1999
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2000
|
+
style: {
|
|
2001
|
+
display: "flex",
|
|
2002
|
+
alignItems: "center",
|
|
2003
|
+
gap: 5,
|
|
2004
|
+
minWidth: 0
|
|
2005
|
+
},
|
|
2006
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: row.remain === null ? "warning" : "done" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2007
|
+
style: {
|
|
2008
|
+
overflow: "hidden",
|
|
2009
|
+
textOverflow: "ellipsis",
|
|
2010
|
+
whiteSpace: "nowrap",
|
|
2011
|
+
color: "var(--dsw-alias-label-primary)"
|
|
2012
|
+
},
|
|
2013
|
+
title: row.error === void 0 ? row.name : row.error,
|
|
2014
|
+
children: row.name
|
|
2015
|
+
})]
|
|
2016
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2017
|
+
style: PANEL_NUM_CELL,
|
|
2018
|
+
children: row.remain === null ? "—" : formatNumber(row.remain)
|
|
2019
|
+
})]
|
|
2020
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2021
|
+
style: {
|
|
2022
|
+
paddingLeft: 11,
|
|
2023
|
+
fontSize: 11,
|
|
2024
|
+
lineHeight: "15px",
|
|
2025
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
2026
|
+
},
|
|
2027
|
+
children: row.usage === void 0 ? t("creditsDockNoTokens") : `${t("usageToday")} ${usageLine(row.usage, t)}`
|
|
2028
|
+
})]
|
|
2029
|
+
}, row.id))]
|
|
1912
2030
|
}),
|
|
1913
2031
|
rows.filter((row) => row.expiring > 0).map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1914
2032
|
style: {
|
|
@@ -1993,12 +2111,25 @@ const zh = {
|
|
|
1993
2111
|
modelsLoading: "模型勾选区加载中…(若长期不出现,请确认已登录且 DSH 已完全重启)",
|
|
1994
2112
|
usageTitle: "本机用量",
|
|
1995
2113
|
usageToday: "今日",
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
2114
|
+
usageRange: "今日 {today} 条 · 近 30 天 {last30} 条",
|
|
2115
|
+
usageStatTokens: "今日 token",
|
|
2116
|
+
usageStatIn: "输入 {n}",
|
|
2117
|
+
usageStatThinking: "思考 {n}",
|
|
2118
|
+
usageTipInput: "输入",
|
|
2119
|
+
usageTipOutput: "输出",
|
|
2120
|
+
usageTipReasoning: "思考",
|
|
2121
|
+
usageTipCached: "缓存命中",
|
|
2122
|
+
usageTipNoCache: "上游未下发",
|
|
2123
|
+
usageTipRequests: "请求",
|
|
2124
|
+
usageTipHit: "命中率",
|
|
2125
|
+
usageStatHit: "缓存命中",
|
|
2126
|
+
usageStatRequests: "今日请求",
|
|
1999
2127
|
usageRequests: "{n} 次请求",
|
|
2128
|
+
usageByAccount: "今日按账号",
|
|
2000
2129
|
usageRequestCount: "{n} 条",
|
|
2001
2130
|
usageTokens: "输入 {prompt} · 输出 {completion}",
|
|
2131
|
+
usageCompact: "入 {prompt} · 出 {completion}",
|
|
2132
|
+
usageCacheHit: "命中 {pct}%",
|
|
2002
2133
|
usageAllTime: "累计({days} 天)",
|
|
2003
2134
|
usageByModel: "今日按模型",
|
|
2004
2135
|
usageSlots: "会话槽位",
|
|
@@ -2025,9 +2156,6 @@ const zh = {
|
|
|
2025
2156
|
creditsDockPanelTitle: "CodeArts 积分",
|
|
2026
2157
|
creditsDockRefresh: "刷新积分",
|
|
2027
2158
|
creditsDockRefreshing: "刷新中…",
|
|
2028
|
-
creditsDockColAccount: "账号",
|
|
2029
|
-
creditsDockColBalance: "剩余积分",
|
|
2030
|
-
creditsDockColTokens: "今日 Token",
|
|
2031
2159
|
creditsDockNoTokens: "今天还没有经插件发出的请求",
|
|
2032
2160
|
creditsDockEmpty: "暂时没有积分数据",
|
|
2033
2161
|
creditsDockExpiring: "{account}:{expiring} 积分将在本周期结束时作废",
|
|
@@ -2036,9 +2164,9 @@ const zh = {
|
|
|
2036
2164
|
dockModel: "Model {model}",
|
|
2037
2165
|
dockNoModel: "未选择模型",
|
|
2038
2166
|
tabDisplay: "显示",
|
|
2039
|
-
displayTitle: "
|
|
2167
|
+
displayTitle: "输入框两侧显示",
|
|
2040
2168
|
displayHint: "这两项只管输入框下方那一行的积分与排队两段;中间那段「Provider CodeArts · Model 模型 · 倍率」一直显示,不受开关影响。底部那条每 60 秒读一次设置,所以关掉最多要等一分钟才见效。",
|
|
2041
|
-
displayCreditsToggle: "
|
|
2169
|
+
displayCreditsToggle: "在输入框工具行显示剩余积分(点开可看各账号明细)",
|
|
2042
2170
|
displayQueueToggle: "显示上游排队位置",
|
|
2043
2171
|
displayNotWritable: "这个宿主没给插件开配置存储,开关只能看不能改。",
|
|
2044
2172
|
displayFailed: "保存失败:{message}",
|
|
@@ -2116,12 +2244,25 @@ const en = {
|
|
|
2116
2244
|
modelsLoading: "Loading model selection… (if it never appears, make sure you are signed in and DSH was fully restarted)",
|
|
2117
2245
|
usageTitle: "Local usage",
|
|
2118
2246
|
usageToday: "Today",
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2247
|
+
usageRange: "Today {today} · last 30 days {last30}",
|
|
2248
|
+
usageStatTokens: "Today tokens",
|
|
2249
|
+
usageStatIn: "in {n}",
|
|
2250
|
+
usageStatThinking: "thinking {n}",
|
|
2251
|
+
usageTipInput: "Input",
|
|
2252
|
+
usageTipOutput: "Output",
|
|
2253
|
+
usageTipReasoning: "Reasoning",
|
|
2254
|
+
usageTipCached: "Cached",
|
|
2255
|
+
usageTipNoCache: "not reported",
|
|
2256
|
+
usageTipRequests: "Requests",
|
|
2257
|
+
usageTipHit: "Hit rate",
|
|
2258
|
+
usageStatHit: "Cache hit",
|
|
2259
|
+
usageStatRequests: "Today requests",
|
|
2122
2260
|
usageRequests: "{n} request(s)",
|
|
2261
|
+
usageByAccount: "Today by account",
|
|
2123
2262
|
usageRequestCount: "{n} reqs",
|
|
2124
2263
|
usageTokens: "in {prompt} · out {completion}",
|
|
2264
|
+
usageCompact: "in {prompt} · out {completion}",
|
|
2265
|
+
usageCacheHit: "{pct}% cached",
|
|
2125
2266
|
usageAllTime: "All time ({days} days)",
|
|
2126
2267
|
usageByModel: "Today by model",
|
|
2127
2268
|
usageSlots: "Session slots",
|
|
@@ -2148,9 +2289,6 @@ const en = {
|
|
|
2148
2289
|
creditsDockPanelTitle: "CodeArts credits",
|
|
2149
2290
|
creditsDockRefresh: "Refresh credits",
|
|
2150
2291
|
creditsDockRefreshing: "Refreshing…",
|
|
2151
|
-
creditsDockColAccount: "Account",
|
|
2152
|
-
creditsDockColBalance: "Credits left",
|
|
2153
|
-
creditsDockColTokens: "Tokens today",
|
|
2154
2292
|
creditsDockNoTokens: "No requests through the plugin today yet",
|
|
2155
2293
|
creditsDockEmpty: "No credit data yet",
|
|
2156
2294
|
creditsDockExpiring: "{account}: {expiring} credits expire when this cycle ends",
|
|
@@ -2159,9 +2297,9 @@ const en = {
|
|
|
2159
2297
|
dockModel: "Model {model}",
|
|
2160
2298
|
dockNoModel: "No model selected",
|
|
2161
2299
|
tabDisplay: "Display",
|
|
2162
|
-
displayTitle: "
|
|
2300
|
+
displayTitle: "Around the chat input box",
|
|
2163
2301
|
displayHint: "These two only control the credits and queue segments of that line under the input box; the middle “Provider CodeArts · Model … · rate” segments always show. The bar re-reads settings every 60 seconds, so hiding the rest can take up to a minute.",
|
|
2164
|
-
displayCreditsToggle: "Show remaining credits (click
|
|
2302
|
+
displayCreditsToggle: "Show remaining credits in the composer tool row (click for per-account details)",
|
|
2165
2303
|
displayQueueToggle: "Show the upstream queue position",
|
|
2166
2304
|
displayNotWritable: "This host gives the plugin nowhere to persist settings, so the switches are read-only.",
|
|
2167
2305
|
displayFailed: "Failed to save: {message}",
|
|
@@ -2194,12 +2332,25 @@ function apply(ctx) {
|
|
|
2194
2332
|
key: "dsh-codearts",
|
|
2195
2333
|
locale: namespace
|
|
2196
2334
|
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodeArtsPluginCard, { t })));
|
|
2335
|
+
ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
|
|
2336
|
+
name: "conversation.input.left",
|
|
2337
|
+
id: "codearts-credits-bar",
|
|
2338
|
+
order: 20,
|
|
2339
|
+
locale: namespace,
|
|
2340
|
+
inject: () => ({
|
|
2341
|
+
t,
|
|
2342
|
+
zone: "bar"
|
|
2343
|
+
})
|
|
2344
|
+
}, CodeArtsCreditDock));
|
|
2197
2345
|
ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register({
|
|
2198
2346
|
name: "conversation.composer.dock",
|
|
2199
2347
|
id: "codearts-credits",
|
|
2200
2348
|
order: 20,
|
|
2201
2349
|
locale: namespace,
|
|
2202
|
-
inject: () => ({
|
|
2350
|
+
inject: () => ({
|
|
2351
|
+
t,
|
|
2352
|
+
zone: "dock"
|
|
2353
|
+
})
|
|
2203
2354
|
}, CodeArtsCreditDock));
|
|
2204
2355
|
} catch (error) {
|
|
2205
2356
|
console.error("[dsh-codearts] client card failed to load (host provider unaffected):", error);
|
package/lib/index.d.mts
CHANGED
|
@@ -1296,12 +1296,40 @@ interface UsageTotals {
|
|
|
1296
1296
|
promptTokens: number;
|
|
1297
1297
|
completionTokens: number;
|
|
1298
1298
|
totalTokens: number;
|
|
1299
|
+
/**
|
|
1300
|
+
* 命中上游 prompt 缓存的输入 token(OpenAI 兼容口径)。
|
|
1301
|
+
*
|
|
1302
|
+
* 上游若在 usage 帧里带 `prompt_tokens_details.cached_tokens`(也有平铺成
|
|
1303
|
+
* `cached_tokens` / `cache_tokens` / `prompt_cache_hit_tokens` 的),这里才可能
|
|
1304
|
+
* 是正数;不给就恒为 0。UI 据此决定显不显示「缓存」那一段 —— 不臆造。
|
|
1305
|
+
* 它是 `promptTokens` 的**子集**,不是额外的输入量。
|
|
1306
|
+
*/
|
|
1307
|
+
cachedTokens: number;
|
|
1308
|
+
/**
|
|
1309
|
+
* 其中属于「思考」的输出 token(OpenAI 兼容口径:`completion_tokens_details.reasoning_tokens`,
|
|
1310
|
+
* 平铺成顶层 `reasoning_tokens` 的也认)。
|
|
1311
|
+
*
|
|
1312
|
+
* 推理模型(deepseek-v4.1-flash 这类)会把它单列出来,通常照样计费;不单独记就
|
|
1313
|
+
* 只能看见一个偏大的「输出」。它是 `completionTokens` 的**子集**,不是额外的输出量。
|
|
1314
|
+
* 上游不给就恒为 0。
|
|
1315
|
+
*/
|
|
1316
|
+
reasoningTokens: number;
|
|
1299
1317
|
}
|
|
1300
1318
|
/** 上游 usage 帧里我们关心的字段。 */
|
|
1301
1319
|
interface RawUsage {
|
|
1302
1320
|
prompt_tokens?: unknown;
|
|
1303
1321
|
completion_tokens?: unknown;
|
|
1304
1322
|
total_tokens?: unknown;
|
|
1323
|
+
/** OpenAI 兼容的嵌套写法:`prompt_tokens_details: { cached_tokens }`。 */
|
|
1324
|
+
prompt_tokens_details?: unknown;
|
|
1325
|
+
/** 平铺写法,几家兼容层各用各的名字,都认。 */
|
|
1326
|
+
cached_tokens?: unknown;
|
|
1327
|
+
cache_tokens?: unknown;
|
|
1328
|
+
prompt_cache_hit_tokens?: unknown;
|
|
1329
|
+
/** 推理模型的嵌套写法:`completion_tokens_details: { reasoning_tokens }`。 */
|
|
1330
|
+
completion_tokens_details?: unknown;
|
|
1331
|
+
/** 平铺的思考 token 别名。 */
|
|
1332
|
+
reasoning_tokens?: unknown;
|
|
1305
1333
|
}
|
|
1306
1334
|
interface UsageSnapshot {
|
|
1307
1335
|
/** 今天的合计。 */
|
package/lib/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as upstreamErrorMessage, B as modelUnavailableReason, C as isWelfareClaimedStatus, D as prepareChatBody, E as pickDailyWelfareCampaign, F as filterEnabledModels, G as codeartsAuthPath, H as CodeArtsCredentialStore, I as forgetModelUnavailable, K as startLogin, L as isBenefitBlocked, M as FALLBACK_CODEARTS_MODELS, N as clearBenefitBlocked, O as rateLimitFrame, P as creditRatioLabel, R as isBenefitModel, S as isTpmLimit, T as modelUnavailableReasonText, U as MANUAL_ACCOUNT_ID, V as CodeArtsAccountStore, W as codeartsAccountsPath, _ as isIamTokenError, a as SNAP_HOST, b as isParamInvalidFrame, c as claimDailyWelfare, d as fetchCallerIdentity, f as fetchModels, g as isConcurrencyCap, h as firstFrameError, i as refreshCatalog, j as CodeArtsCatalog, k as signedGet, l as clearQueueStatus, m as fetchWelfareCampaigns, n as codeartsUsagePath, o as canonicalModel, p as fetchQueueStatus, r as discoverModels, s as chatStream, t as CodeArtsUsageTracker, u as degradeTokenCeiling, v as isModelNotAccessible, w as maxTokensCeilingFor, x as isQueueableRate, y as isNotMultimodal, z as markModelUnavailable } from "./usage-
|
|
1
|
+
import { A as upstreamErrorMessage, B as modelUnavailableReason, C as isWelfareClaimedStatus, D as prepareChatBody, E as pickDailyWelfareCampaign, F as filterEnabledModels, G as codeartsAuthPath, H as CodeArtsCredentialStore, I as forgetModelUnavailable, K as startLogin, L as isBenefitBlocked, M as FALLBACK_CODEARTS_MODELS, N as clearBenefitBlocked, O as rateLimitFrame, P as creditRatioLabel, R as isBenefitModel, S as isTpmLimit, T as modelUnavailableReasonText, U as MANUAL_ACCOUNT_ID, V as CodeArtsAccountStore, W as codeartsAccountsPath, _ as isIamTokenError, a as SNAP_HOST, b as isParamInvalidFrame, c as claimDailyWelfare, d as fetchCallerIdentity, f as fetchModels, g as isConcurrencyCap, h as firstFrameError, i as refreshCatalog, j as CodeArtsCatalog, k as signedGet, l as clearQueueStatus, m as fetchWelfareCampaigns, n as codeartsUsagePath, o as canonicalModel, p as fetchQueueStatus, r as discoverModels, s as chatStream, t as CodeArtsUsageTracker, u as degradeTokenCeiling, v as isModelNotAccessible, w as maxTokensCeilingFor, x as isQueueableRate, y as isNotMultimodal, z as markModelUnavailable } from "./usage-DGIuT79_.mjs";
|
|
2
2
|
import { i as lazyApi, r as createProvider } from "./models-CgKCibpk.mjs";
|
|
3
3
|
import z from "@deepseek-ai/schemastery";
|
|
4
4
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
@@ -962,6 +962,13 @@ const recentShapes = [];
|
|
|
962
962
|
function recentRequestShapes() {
|
|
963
963
|
return [...recentShapes].reverse();
|
|
964
964
|
}
|
|
965
|
+
const recentUsages = [];
|
|
966
|
+
/** 探针只留最近 5 帧:够看形状,又不至于把内存堆成日志。 */
|
|
967
|
+
const USAGE_PROBE_LIMIT = 5;
|
|
968
|
+
/** 最近的 usage 原帧,最新在前。 */
|
|
969
|
+
function recentUsageShapes() {
|
|
970
|
+
return [...recentUsages].reverse();
|
|
971
|
+
}
|
|
965
972
|
function noteRequestShape(raw, model) {
|
|
966
973
|
let parsed;
|
|
967
974
|
try {
|
|
@@ -1406,7 +1413,16 @@ function createCodeArtsShim(options) {
|
|
|
1406
1413
|
if (payload === "" || payload === "[DONE]") continue;
|
|
1407
1414
|
try {
|
|
1408
1415
|
const parsed = JSON.parse(payload);
|
|
1409
|
-
if (parsed.usage !== void 0 && parsed.usage !== null)
|
|
1416
|
+
if (parsed.usage !== void 0 && parsed.usage !== null) {
|
|
1417
|
+
usage.noteUsage(attribution.accountId, attribution.model, parsed.usage);
|
|
1418
|
+
recentUsages.push({
|
|
1419
|
+
at: Date.now(),
|
|
1420
|
+
accountId: attribution.accountId,
|
|
1421
|
+
model: attribution.model,
|
|
1422
|
+
usage: parsed.usage
|
|
1423
|
+
});
|
|
1424
|
+
while (recentUsages.length > USAGE_PROBE_LIMIT) recentUsages.shift();
|
|
1425
|
+
}
|
|
1410
1426
|
} catch {}
|
|
1411
1427
|
}
|
|
1412
1428
|
}
|
|
@@ -1619,7 +1635,9 @@ function usageOf(deps) {
|
|
|
1619
1635
|
requests: 0,
|
|
1620
1636
|
promptTokens: 0,
|
|
1621
1637
|
completionTokens: 0,
|
|
1622
|
-
totalTokens: 0
|
|
1638
|
+
totalTokens: 0,
|
|
1639
|
+
cachedTokens: 0,
|
|
1640
|
+
reasoningTokens: 0
|
|
1623
1641
|
};
|
|
1624
1642
|
return {
|
|
1625
1643
|
today: snapshot?.today ?? noTotals,
|
|
@@ -2129,7 +2147,10 @@ function debugHandler(deps) {
|
|
|
2129
2147
|
json(res, 403, { error: "request-not-trusted" });
|
|
2130
2148
|
return;
|
|
2131
2149
|
}
|
|
2132
|
-
json(res, 200, {
|
|
2150
|
+
json(res, 200, {
|
|
2151
|
+
requests: deps.recentRequests?.() ?? [],
|
|
2152
|
+
usages: deps.recentUsages?.() ?? []
|
|
2153
|
+
});
|
|
2133
2154
|
};
|
|
2134
2155
|
}
|
|
2135
2156
|
/** Mount the status / models / token routes. */
|
|
@@ -2481,6 +2502,7 @@ function apply(ctx, rawConfig) {
|
|
|
2481
2502
|
usage: () => usage.snapshot(),
|
|
2482
2503
|
queue: () => queue.snapshot(),
|
|
2483
2504
|
recentRequests: () => recentRequestShapes(),
|
|
2505
|
+
recentUsages: () => recentUsageShapes(),
|
|
2484
2506
|
credits: (signal) => loadCredits(signal),
|
|
2485
2507
|
refreshCredits: (signal) => {
|
|
2486
2508
|
credits.invalidate();
|
|
@@ -207,6 +207,41 @@ function parseToken(json, dpopJWK, clientId, prev) {
|
|
|
207
207
|
dpopPrivateKey: dpopJWK
|
|
208
208
|
};
|
|
209
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* 华为登录后浏览器落回本机回调的落地页。
|
|
212
|
+
*
|
|
213
|
+
* 这一页本身没有任何后续逻辑:`code` 在响应发出的同一时刻就已交给等待队列
|
|
214
|
+
* (见 startLogin 里的 finish),token 交换完全在服务端做,页面关不关都不影响
|
|
215
|
+
* 登录结果。它只是 redirect_uri 必然要渲染的一个落点 —— 华为侧没有「登录成功」
|
|
216
|
+
* 页可停留,成功标志就是这一次 302 到 127.0.0.1。
|
|
217
|
+
*
|
|
218
|
+
* 卡片用 `window.open` 打开授权页,所以这个标签页能自己关掉;用户从卡片的
|
|
219
|
+
* 手动链接打开、或浏览器拦脚本关窗时 close 会失败,1.2s 后把提示换成「可关闭」。
|
|
220
|
+
*/
|
|
221
|
+
const CALLBACK_HTML = `<!doctype html>
|
|
222
|
+
<html lang="zh-CN">
|
|
223
|
+
<head>
|
|
224
|
+
<meta charset="utf-8">
|
|
225
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
226
|
+
<title>CodeArts 登录</title>
|
|
227
|
+
<style>
|
|
228
|
+
html,body{height:100%}
|
|
229
|
+
body{margin:0;display:flex;align-items:center;justify-content:center;background:#fff;color:#1f2329;
|
|
230
|
+
font:15px/1.6 system-ui,-apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif}
|
|
231
|
+
p{margin:0;padding:24px;text-align:center}
|
|
232
|
+
</style>
|
|
233
|
+
</head>
|
|
234
|
+
<body>
|
|
235
|
+
<p id="msg">正在返回 DeepSeek Harness…</p>
|
|
236
|
+
<script>
|
|
237
|
+
setTimeout(function () { try { window.close() } catch (e) {} }, 300)
|
|
238
|
+
setTimeout(function () {
|
|
239
|
+
var el = document.getElementById('msg')
|
|
240
|
+
if (el) el.textContent = 'CodeArts 登录成功,本页可关闭,请回到 DeepSeek Harness。'
|
|
241
|
+
}, 1200)
|
|
242
|
+
<\/script>
|
|
243
|
+
</body>
|
|
244
|
+
</html>`;
|
|
210
245
|
async function startLogin(cfg = defaultLoginConfig()) {
|
|
211
246
|
const ticketId = randomHex(16);
|
|
212
247
|
const { verifier, challenge, method } = pkce();
|
|
@@ -236,8 +271,11 @@ async function startLogin(cfg = defaultLoginConfig()) {
|
|
|
236
271
|
}
|
|
237
272
|
finish(code);
|
|
238
273
|
function finish(c) {
|
|
239
|
-
res.writeHead(200, {
|
|
240
|
-
|
|
274
|
+
res.writeHead(200, {
|
|
275
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
276
|
+
"Cache-Control": "no-store"
|
|
277
|
+
});
|
|
278
|
+
res.end(CALLBACK_HTML);
|
|
241
279
|
if (c) for (const w of waiters.splice(0)) w(c);
|
|
242
280
|
}
|
|
243
281
|
});
|
|
@@ -2709,7 +2747,9 @@ function emptyTotals() {
|
|
|
2709
2747
|
requests: 0,
|
|
2710
2748
|
promptTokens: 0,
|
|
2711
2749
|
completionTokens: 0,
|
|
2712
|
-
totalTokens: 0
|
|
2750
|
+
totalTokens: 0,
|
|
2751
|
+
cachedTokens: 0,
|
|
2752
|
+
reasoningTokens: 0
|
|
2713
2753
|
};
|
|
2714
2754
|
}
|
|
2715
2755
|
function addTotals(into, add) {
|
|
@@ -2717,6 +2757,8 @@ function addTotals(into, add) {
|
|
|
2717
2757
|
into.promptTokens += add.promptTokens ?? 0;
|
|
2718
2758
|
into.completionTokens += add.completionTokens ?? 0;
|
|
2719
2759
|
into.totalTokens += add.totalTokens ?? 0;
|
|
2760
|
+
into.cachedTokens += add.cachedTokens ?? 0;
|
|
2761
|
+
into.reasoningTokens += add.reasoningTokens ?? 0;
|
|
2720
2762
|
}
|
|
2721
2763
|
function mergeTotals(into, from) {
|
|
2722
2764
|
addTotals(into, from);
|
|
@@ -2730,12 +2772,41 @@ function localDateKey(at = Date.now()) {
|
|
|
2730
2772
|
function positiveInt(value) {
|
|
2731
2773
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
|
|
2732
2774
|
}
|
|
2733
|
-
/**
|
|
2775
|
+
/**
|
|
2776
|
+
* 从上游 usage 帧里取缓存命中数。
|
|
2777
|
+
*
|
|
2778
|
+
* 位置见过两种:OpenAI 的 `prompt_tokens_details.cached_tokens`,以及兼容层直接
|
|
2779
|
+
* 平铺在顶层的几种别名。取第一个能读到的。缓存输入必然是输入的子集,所以比
|
|
2780
|
+
* `promptTokens` 还大的脏数据直接钳掉 —— 否则界面上会出现 >100% 的命中率。
|
|
2781
|
+
*/
|
|
2782
|
+
function cachedTokensOf(raw, promptTokens) {
|
|
2783
|
+
if (promptTokens <= 0) return 0;
|
|
2784
|
+
const cached = positiveInt(nestedField(raw.prompt_tokens_details, "cached_tokens") ?? raw.cached_tokens ?? raw.cache_tokens ?? raw.prompt_cache_hit_tokens);
|
|
2785
|
+
return Math.min(cached, promptTokens);
|
|
2786
|
+
}
|
|
2787
|
+
/**
|
|
2788
|
+
* 从上游 usage 帧里取思考 token。
|
|
2789
|
+
*
|
|
2790
|
+
* 口径与 {@link cachedTokensOf} 对称:嵌套 `completion_tokens_details.reasoning_tokens`
|
|
2791
|
+
* 优先,顶层 `reasoning_tokens` 兜底,超过输出总量就钳掉(它含在输出里)。
|
|
2792
|
+
*/
|
|
2793
|
+
function reasoningTokensOf(raw, completionTokens) {
|
|
2794
|
+
if (completionTokens <= 0) return 0;
|
|
2795
|
+
const reasoning = positiveInt(nestedField(raw.completion_tokens_details, "reasoning_tokens") ?? raw.reasoning_tokens);
|
|
2796
|
+
return Math.min(reasoning, completionTokens);
|
|
2797
|
+
}
|
|
2798
|
+
/** 读 `*_tokens_details` 这类嵌套对象里的一个字段;不是对象(或缺字段)给 undefined。 */
|
|
2799
|
+
function nestedField(details, key) {
|
|
2800
|
+
return typeof details === "object" && details !== null ? details[key] : void 0;
|
|
2801
|
+
}
|
|
2802
|
+
/** 从上游 usage 帧里取计数(缺哪个算 0,total 缺失时用 prompt+completion 兜底)。 */
|
|
2734
2803
|
function normalizeUsage(raw) {
|
|
2735
2804
|
if (raw === void 0) return {
|
|
2736
2805
|
promptTokens: 0,
|
|
2737
2806
|
completionTokens: 0,
|
|
2738
|
-
totalTokens: 0
|
|
2807
|
+
totalTokens: 0,
|
|
2808
|
+
cachedTokens: 0,
|
|
2809
|
+
reasoningTokens: 0
|
|
2739
2810
|
};
|
|
2740
2811
|
const promptTokens = positiveInt(raw.prompt_tokens);
|
|
2741
2812
|
const completionTokens = positiveInt(raw.completion_tokens);
|
|
@@ -2743,9 +2814,31 @@ function normalizeUsage(raw) {
|
|
|
2743
2814
|
return {
|
|
2744
2815
|
promptTokens,
|
|
2745
2816
|
completionTokens,
|
|
2746
|
-
totalTokens: total > 0 ? total : promptTokens + completionTokens
|
|
2817
|
+
totalTokens: total > 0 ? total : promptTokens + completionTokens,
|
|
2818
|
+
cachedTokens: cachedTokensOf(raw, promptTokens),
|
|
2819
|
+
reasoningTokens: reasoningTokensOf(raw, completionTokens)
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
/** 读一项累计值,缺字段(旧账本没有 cachedTokens / reasoningTokens)就按 0 补齐。 */
|
|
2823
|
+
function normalizeTotals(raw) {
|
|
2824
|
+
return {
|
|
2825
|
+
requests: positiveInt(raw?.requests),
|
|
2826
|
+
promptTokens: positiveInt(raw?.promptTokens),
|
|
2827
|
+
completionTokens: positiveInt(raw?.completionTokens),
|
|
2828
|
+
totalTokens: positiveInt(raw?.totalTokens),
|
|
2829
|
+
cachedTokens: positiveInt(raw?.cachedTokens),
|
|
2830
|
+
reasoningTokens: positiveInt(raw?.reasoningTokens)
|
|
2747
2831
|
};
|
|
2748
2832
|
}
|
|
2833
|
+
/** 整张「模型/账号 -> 累计」表补齐字段。 */
|
|
2834
|
+
function normalizeStore(store) {
|
|
2835
|
+
const out = {};
|
|
2836
|
+
for (const [key, totals] of Object.entries(store ?? {})) {
|
|
2837
|
+
if (typeof totals !== "object" || totals === null) continue;
|
|
2838
|
+
out[key] = normalizeTotals(totals);
|
|
2839
|
+
}
|
|
2840
|
+
return out;
|
|
2841
|
+
}
|
|
2749
2842
|
/**
|
|
2750
2843
|
* 用量账本。
|
|
2751
2844
|
*
|
|
@@ -2784,8 +2877,8 @@ var CodeArtsUsageTracker = class {
|
|
|
2784
2877
|
if (parsed.version !== 1 || typeof parsed.days !== "object" || parsed.days === null) return;
|
|
2785
2878
|
for (const [date, day] of Object.entries(parsed.days)) {
|
|
2786
2879
|
if (typeof day !== "object" || day === null) continue;
|
|
2787
|
-
const models = day.models
|
|
2788
|
-
const accounts = day.accounts
|
|
2880
|
+
const models = normalizeStore(day.models);
|
|
2881
|
+
const accounts = normalizeStore(day.accounts);
|
|
2789
2882
|
this.days.set(date, {
|
|
2790
2883
|
models,
|
|
2791
2884
|
accounts
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-codearts",
|
|
3
3
|
"displayName": "DSH CodeArts Connect",
|
|
4
4
|
"description": "把华为云 CodeArts 接入 DeepSeek Harness —— 内置 OAuth2 登录(PKCE + DPoP),登录后即可在 DSH 对话窗口使用 CodeArts 的模型。",
|
|
5
|
-
"version": "1.2.
|
|
5
|
+
"version": "1.2.4",
|
|
6
6
|
"packageManager": "pnpm@11.24.0",
|
|
7
7
|
"author": "elegant01",
|
|
8
8
|
"keywords": [
|