dsh-bottom-info-bar 1.3.1 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -113,6 +113,7 @@ function installStyles() {
113
113
  .bi-offpeak { color: var(--dsw-alias-state-success-primary, #16a34a); font-weight: 700; }
114
114
  .bi-err { color: var(--dsw-alias-state-error-primary, #dc2626); }
115
115
  .bi-stale{ color: var(--dsw-alias-state-warn-primary, #d97706); }
116
+ .bi-update{ color: var(--dsw-alias-state-error-primary, #dc2626); }
116
117
  `;
117
118
  document.head.appendChild(style);
118
119
  return function () { style.remove(); };
@@ -193,7 +194,8 @@ module.exports = {
193
194
  loading: true, balance: null, pricing: null, usage: null, billingMode: null, sub: null,
194
195
  errors: { balance: null, pricing: null, usage: null, billingMode: null, sub: null },
195
196
  });
196
- const [now, setNow] = React.useState(Date.now());
197
+ const [updateInfo, setUpdateInfo] = React.useState(null);
198
+ const [now, setNow] = React.useState(Date.now());
197
199
 
198
200
  // 当前会话 ID 多路获取:slotProps 标准 kit → session 快照 → 运行时 sessions 服务
199
201
  // (DSH 各版本注入方式不同,任一路可用即拿到真实会话 ID,避免回退到上一会话的账)
@@ -242,7 +244,16 @@ module.exports = {
242
244
  return function () { window.clearInterval(id); };
243
245
  }, [load]);
244
246
 
245
- // 模型/服务商切换秒级同步:getBillingMode 为 host 端纯本地计算(零网络开销),每 2 秒轮询一次;
247
+ // 版本检查由 host 在进程启动时完成;这里仅读取一次缓存,不轮询 NPM。
248
+ React.useEffect(function () {
249
+ let active = true;
250
+ rpc('getUpdateInfo').then(function (info) {
251
+ if (active && info && info.available === true && typeof info.latest === 'string') setUpdateInfo(info);
252
+ }).catch(function () { /* 版本检查失败静默,不影响信息栏 */ });
253
+ return function () { active = false; };
254
+ }, []);
255
+
256
+ // 模型/服务商切换秒级同步:getBillingMode 为 host 端纯本地计算(零网络开销),每 2 秒轮询一次;
246
257
  // mode/provider/model 任一变化(即切换了模型/服务商)→ 立即完整 load(),不等 30s 主轮询。
247
258
  // 注意:本轮询不触碰订阅接口——getSubscriptionSnapshot 仍仅由 load 调用(惰性门控 + 60s 周期不变)
248
259
  React.useEffect(function () {
@@ -590,7 +601,11 @@ module.exports = {
590
601
  '⚠ 部分数据刷新失败'));
591
602
  }
592
603
 
593
- // ---- 组装 ----
604
+ if (updateInfo && updateInfo.available === true) {
605
+ groups.push(React.createElement('span', { className: 'bi-update', key: 'update' }, '↑ ' + updateInfo.latest));
606
+ }
607
+
608
+ // ---- 组装 ----
594
609
  const sepNodes = [];
595
610
  const nodes = [];
596
611
  for (let i = 0; i < groups.length; i++) {
package/lib/index.js CHANGED
@@ -13,6 +13,53 @@ import { dirname, join } from 'node:path'
13
13
 
14
14
  const DATA_DIR = process.env.DSH_BOTTOM_INFO_BAR_DATA_DIR || join(homedir(), '.dsh', 'dsh-bottom-info-bar')
15
15
  const DATA_FILE = join(DATA_DIR, 'usage-records.json')
16
+ const PACKAGE_FILE = new URL('../package.json', import.meta.url)
17
+ const UPDATE_REGISTRY_URL = 'https://registry.npmjs.org/dsh-bottom-info-bar/latest'
18
+ const UPDATE_CHECK_TIMEOUT_MS = 5000
19
+
20
+ function packageVersion() {
21
+ try {
22
+ const pkg = JSON.parse(readFileSync(PACKAGE_FILE, 'utf8'))
23
+ return typeof pkg.version === 'string' ? pkg.version : '0.0.0'
24
+ } catch {
25
+ return '0.0.0'
26
+ }
27
+ }
28
+
29
+ function stableVersion(value) {
30
+ const match = typeof value === 'string' && value.trim().match(/^v?(\d+)\.(\d+)\.(\d+)$/)
31
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null
32
+ }
33
+
34
+ function compareVersions(left, right) {
35
+ const a = stableVersion(left)
36
+ const b = stableVersion(right)
37
+ if (!a || !b) return 0
38
+ for (let i = 0; i < 3; i++) {
39
+ if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1
40
+ }
41
+ return 0
42
+ }
43
+
44
+ async function checkLatestVersion() {
45
+ const current = packageVersion()
46
+ const controller = new AbortController()
47
+ const timer = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS)
48
+ try {
49
+ const response = await fetch(UPDATE_REGISTRY_URL, {
50
+ headers: { accept: 'application/json' },
51
+ signal: controller.signal,
52
+ })
53
+ if (!response.ok) return { available: false, current: current, latest: null }
54
+ const body = await response.json()
55
+ const latest = body && typeof body.version === 'string' ? body.version : null
56
+ return { available: !!latest && compareVersions(latest, current) > 0, current: current, latest: latest }
57
+ } catch {
58
+ return { available: false, current: current, latest: null }
59
+ } finally {
60
+ clearTimeout(timer)
61
+ }
62
+ }
16
63
 
17
64
  // ---------- 双模式(余额制 / 订阅制)配置 ----------
18
65
  // 订阅制 provider 集合:这些 provider 走"额度窗口"显示而非余额(可在此增删)
@@ -222,6 +269,9 @@ function loadUsageRecords() {
222
269
  export default {
223
270
  inject: ['credentials', 'timer'],
224
271
  apply(ctx) {
272
+ // 版本检查只在 host 进程启动时发起一次;客户端后续只读取这个缓存结果。
273
+ const updateInfoPromise = checkLatestVersion()
274
+
225
275
  // ---------- 定价表(元/美元 · 百万 tokens;DeepSeek 官方 2026-08-17;OpenAI 为 2026 官方价示例) ----------
226
276
  const PRICING = {
227
277
  'deepseek-v4-flash': {
@@ -1124,6 +1174,9 @@ export default {
1124
1174
  // ---------- RPC 路由(webServer HTTP,替代动态沙箱 harness.handle) ----------
1125
1175
  const ROUTE_PREFIX = '/_dsh/dsh-bottom-info-bar';
1126
1176
  const ROUTES = {
1177
+ getUpdateInfo: function () {
1178
+ return updateInfoPromise
1179
+ },
1127
1180
  getBalanceSnapshot: function (args) {
1128
1181
  const pid = args && typeof args === 'object' && args.provider ? String(args.provider) : '';
1129
1182
  return activeBalanceSummary(pid || undefined, Date.now());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-bottom-info-bar",
3
- "version": "1.3.1",
3
+ "version": "1.4.1",
4
4
  "description": "DeepSeek Harness 底部信息栏插件:替换对话输入框下方的原生统计栏,显示服务商/具体模型、真实余额、高峰(琥珀)/空闲(绿)价与倒计时、本对话·今日·近一月·全部花费。安装一次,每次打开 DSH 自动生效。",
5
5
  "keywords": [
6
6
  "deepseek",