@feiyang666/dsh-usage-plugin 1.11.0 → 1.11.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/CHANGELOG.md CHANGED
@@ -23,6 +23,14 @@
23
23
 
24
24
  ---
25
25
 
26
+ ## v1.11.1 (2026-08-23)
27
+
28
+ - **修复:数据加载后页面空白**:加载动画三目分支误用逗号表达式,只返回最后一个表达式值(`null`),导致加载完成后各子视图全部不渲染;改为将四个子视图作为容器 `div` 的并列子元素,加载完成后正常显示。
29
+ - **计费说明改为面板级固定显示**:「高峰 / 空闲时段说明」固定在用量面板子页签下方,概览、用量日历、缓存命中列表、价格表四个页面切换时始终可见(含数据加载期间)。
30
+ - **当前时段精确显示**:用量面板顶部徽标与价格表页顶部新增「当前 · 工作日高峰时段 / 工作日空闲时段 / 周末空闲时段」动态状态并附说明文字;周末(自 2026-08-23 起)全天按空闲价计费,规则生效前仍按原规则区分。
31
+
32
+ ---
33
+
26
34
  ## v1.11.0 (2026-08-23)
27
35
 
28
36
  - **高峰 / 空闲时段说明**:概览、用量日历、缓存命中列表、价格表四个页面顶部统一新增「高峰 / 空闲时段说明」,清楚区分**工作日高峰**(周一至周五 9:00–12:00、14:00–18:00)、**工作日空闲**(其余时间)与**周末全天空闲**(自 2026-08-23 起按空闲价,此前仍按原规则)。
package/lib/client.js CHANGED
@@ -82,6 +82,7 @@ window.__ModuleLoader__.load({
82
82
  selDayTitle: { fontSize: fs(13), fontWeight: 600, marginTop: 14 },
83
83
  billHint: { border: "1px solid rgba(90,140,255,.35)", background: "rgba(90,140,255,.08)", borderRadius: 8, padding: "10px 14px", marginTop: 10, fontSize: fs(12), lineHeight: 1.9 },
84
84
  billHintTitle: { fontWeight: 600, marginBottom: 4, color: "#5a8cff" },
85
+ nowPeriod: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginTop: 10, fontSize: fs(12) },
85
86
  loading: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 12, padding: "48px 0", color: "inherit" },
86
87
  spinner: { width: 28, height: 28, borderRadius: "50%", border: "3px solid rgba(90,140,255,.22)", borderTopColor: "#5a8cff", animation: "dshUsageSpin .8s linear infinite", flexShrink: 0 },
87
88
  loadText: { fontSize: fs(12), opacity: 0.65 }
@@ -141,6 +142,24 @@ window.__ModuleLoader__.load({
141
142
  var t = d.getUTCHours() * 60 + d.getUTCMinutes();
142
143
  return (t >= 9 * 60 && t < 12 * 60) || (t >= 14 * 60 && t < 18 * 60);
143
144
  };
145
+ // 返回当前计费时段 { peak, label, desc },区分:工作日高峰 / 工作日空闲 / 周末空闲(周末自 2026-08-23 起全天按空闲价)
146
+ var periodNow = function (ts) {
147
+ ts = ts || Date.now();
148
+ var d = new Date(ts + 8 * 3600 * 1000);
149
+ var wd = d.getUTCDay(); // 0=周日 1=周一 … 6=周六
150
+ var isWeekend = wd === 0 || wd === 6;
151
+ var t = d.getUTCHours() * 60 + d.getUTCMinutes();
152
+ var inPeakHours = (t >= 9 * 60 && t < 12 * 60) || (t >= 14 * 60 && t < 18 * 60);
153
+ if (isWeekend) {
154
+ if (ts >= WEEKEND_FLAT_AT) return { peak: false, label: "周末空闲时段", desc: "周末(周六、周日)全天按空闲价计费" };
155
+ return inPeakHours
156
+ ? { peak: true, label: "周末高峰时段", desc: "2026-08-23 前仍按原规则分峰谷" }
157
+ : { peak: false, label: "周末空闲时段", desc: "2026-08-23 前仍按原规则分峰谷" };
158
+ }
159
+ return inPeakHours
160
+ ? { peak: true, label: "工作日高峰时段", desc: "周一至周五 9:00–12:00、14:00–18:00" }
161
+ : { peak: false, label: "工作日空闲时段", desc: "工作日其余时间" };
162
+ };
144
163
  // 计费档位选择:auto 用 autoCost(按生效日期自动切换),base 用 baseCost,其余用 peakValleyCost
145
164
  // 计费档位显示文案
146
165
  var regimeLabel = function (regime) {
@@ -592,7 +611,6 @@ window.__ModuleLoader__.load({
592
611
  function setCustomTo(v) { setToDate(v); setPreset("custom"); }
593
612
 
594
613
  return el("div", null,
595
- el(BillingHint, null),
596
614
  el("div", { style: st.calBar },
597
615
  el("div", { style: st.seg },
598
616
  presetBtn("today", "今天"), presetBtn("7d", "近7天"), presetBtn("30d", "近30天"), presetBtn("all", "全部")
@@ -774,7 +792,6 @@ window.__ModuleLoader__.load({
774
792
  function goToday() { setYm({ y: nowY, m: nowM }); setSelectedDay(bjKey(Date.now())); }
775
793
 
776
794
  return el("div", null,
777
- el(BillingHint, null),
778
795
  el("div", { style: st.calBar },
779
796
  el("button", { style: st.btn, onClick: prevMonth }, "‹ 上月"),
780
797
  el("span", { style: { fontSize: fs(14), fontWeight: 600, minWidth: 120, textAlign: "center" } }, y + "年" + (m + 1) + "月"),
@@ -924,7 +941,6 @@ window.__ModuleLoader__.load({
924
941
  var sums = splitTotals(filtered, regime);
925
942
 
926
943
  return el("div", null,
927
- el(BillingHint, null),
928
944
  el("div", { style: st.calBar },
929
945
  el("div", { style: st.seg },
930
946
  presetBtn("today", "今天"), presetBtn("7d", "近7天"), presetBtn("30d", "近30天"), presetBtn("all", "全部")
@@ -1041,7 +1057,10 @@ window.__ModuleLoader__.load({
1041
1057
  });
1042
1058
 
1043
1059
  return el("div", null,
1044
- el(BillingHint, null),
1060
+ el("div", { style: st.nowPeriod },
1061
+ el("span", { style: periodNow().peak ? st.badgePeak : st.badgeValley }, "当前 · " + periodNow().label),
1062
+ el("span", { style: st.sub }, periodNow().desc)
1063
+ ),
1045
1064
  el("div", { style: st.sec }, "今日 $ / ¥ 汇率"),
1046
1065
  el("div", { style: st.cards, marginTop: 8 },
1047
1066
  el(Card, { label: "$ → ¥", value: fx.rate ? ("$1 = ¥" + Number(fx.rate).toFixed(4)) : "暂不可用", hint: fx.date ? ("汇率日期 " + fx.date) : "等待获取" }),
@@ -1232,7 +1251,7 @@ window.__ModuleLoader__.load({
1232
1251
 
1233
1252
  var records = data.records || [];
1234
1253
  var dataPath = data.dataPath || "";
1235
- var nowPeak = isPeakNow(Date.now());
1254
+ var nowPeriod = periodNow(Date.now());
1236
1255
  var effectiveAt = data.effectiveAt || 0;
1237
1256
  var nowEffective = effectiveAt ? (Date.now() >= effectiveAt) : true;
1238
1257
  return el("div", { style: st.root },
@@ -1243,7 +1262,7 @@ window.__ModuleLoader__.load({
1243
1262
  el("div", { style: st.title }, "用量与消耗"),
1244
1263
  el("div", { style: st.sub }, "记录插件激活后的每一次模型调用")
1245
1264
  ),
1246
- el("span", { style: nowPeak ? st.badgePeak : st.badgeValley }, nowPeak ? "当前 · 高峰时段" : "当前 · 空闲时段"),
1265
+ el("span", { style: nowPeriod.peak ? st.badgePeak : st.badgeValley }, "当前 · " + nowPeriod.label),
1247
1266
  el("span", { style: nowEffective ? st.badgeHit : st.badgeValley }, nowEffective ? "新价格已生效" : "新价格未生效")
1248
1267
  ),
1249
1268
  el("div", { style: st.actions },
@@ -1260,15 +1279,18 @@ window.__ModuleLoader__.load({
1260
1279
  }, t.t);
1261
1280
  })
1262
1281
  ),
1282
+ el(BillingHint, null),
1263
1283
  el("div", { style: st.note, marginTop: 6 },
1264
1284
  "计价说明:DeepSeek 官方与 SiliconFlow 按人民币价格;DigitalOcean 按美元官方价 × USD/CNY 汇率折算人民币;千问暂不计费;AMD GPU Cloud DeepSeek V4 Flash 免费按 ¥0。"
1265
1285
  ),
1266
1286
  loading
1267
1287
  ? el(LoadingView, null)
1268
- : (tab === "overview" ? el(OverviewView, { records: records, regime: "auto" }) : null,
1269
- tab === "calendar" ? el(CalendarView, { records: records, regime: "auto", days: data.days || [] }) : null,
1270
- tab === "cache" ? el(CacheListView, { records: records, regime: "auto" }) : null,
1271
- tab === "prices" ? el(PriceView, { pricing: data.pricing, effectiveAt: data.effectiveAt, fx: data.fx || {}, onFxRefresh: function () { api({ action: "fxRefresh" }).then(refresh).catch(function () {}); }, onChanged: refresh }) : null),
1288
+ : el("div", null,
1289
+ tab === "overview" ? el(OverviewView, { records: records, regime: "auto" }) : null,
1290
+ tab === "calendar" ? el(CalendarView, { records: records, regime: "auto", days: data.days || [] }) : null,
1291
+ tab === "cache" ? el(CacheListView, { records: records, regime: "auto" }) : null,
1292
+ tab === "prices" ? el(PriceView, { pricing: data.pricing, effectiveAt: data.effectiveAt, fx: data.fx || {}, onFxRefresh: function () { api({ action: "fxRefresh" }).then(refresh).catch(function () {}); }, onChanged: refresh }) : null
1293
+ ),
1272
1294
  el("div", { style: st.actions, marginTop: 4 },
1273
1295
  el("button", { style: st.btn, onClick: function () { doExport("csv"); } }, "导出 CSV"),
1274
1296
  el("button", { style: st.btn, onClick: function () { doExport("json"); } }, "导出 JSON"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feiyang666/dsh-usage-plugin",
3
- "version": "1.11.0",
3
+ "version": "1.11.1",
4
4
  "description": "DeepSeek Harness usage & cost tracker plugin: per-call token/cache-hit stats, peak/off-peak billing, DeepSeek balance query, CSV/JSON/PNG export with custom destination, and persistent local storage. Ships a host half plus a web client half in one npm package; installs into a DSH profile as a dsh.bundle with one command (dsh plugin --profile web add @feiyang666/dsh-usage-plugin).",
5
5
  "keywords": [
6
6
  "deepseek",
@@ -4,7 +4,7 @@ import fs from 'node:fs'
4
4
  import path from 'node:path'
5
5
 
6
6
  const REPO = 'feiyang-dev/dsh-usage-plugin'
7
- const TAG = 'v1.11.0'
7
+ const TAG = 'v1.11.1'
8
8
 
9
9
  function getCredential() {
10
10
  const input = 'protocol=https\nhost=github.com\n\n'
@@ -76,9 +76,9 @@ if (created.status !== 201) {
76
76
  const release = JSON.parse(created.body)
77
77
  console.log('release id:', release.id, 'url:', release.html_url)
78
78
 
79
- // 打包并上传 tarball 作为附件(Windows 下 npm 命令名为 npm.cmd,spawnSync 需要完整命令名)
79
+ // 打包并上传 tarball 作为附件(Windows 下 npm npm.cmd 批处理脚本,需经 shell 执行)
80
80
  const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'
81
- const pack = spawnSync(npmCmd, ['pack'], { encoding: 'utf8' })
81
+ const pack = spawnSync(npmCmd, ['pack'], { encoding: 'utf8', shell: process.platform === 'win32' })
82
82
  const tgzName = (pack.stdout || '').trim().split('\n').pop()
83
83
  if (tgzName && fs.existsSync(tgzName)) {
84
84
  const buf = fs.readFileSync(tgzName)
@@ -3,7 +3,7 @@ import https from 'node:https'
3
3
  import fs from 'node:fs'
4
4
 
5
5
  const REPO = 'feiyang-dev/dsh-usage-plugin'
6
- const RELEASE_ID = 375097521
6
+ const RELEASE_ID = 375098880
7
7
 
8
8
  function getCredential() {
9
9
  const input = 'protocol=https\nhost=github.com\n\n'