dsh-consumption-badge 1.2.1 → 1.2.2

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/index.js CHANGED
@@ -196,24 +196,28 @@ export function apply(ctx) {
196
196
  return null
197
197
  }
198
198
 
199
- /** 用 curl 取余额:密钥走 stdin 上的 curl 配置,不进进程参数。 */
199
+ /**
200
+ * 用 curl 取余额:密钥走 stdin 上的 curl 配置,不进进程参数。
201
+ * 失败一律回 `{ ok:false, code, detail, error }`:`code` 是语言无关的错误码(浏览器半边负责
202
+ * 按当前语言翻译),`detail` 是原始细节,`error` 是英文兜底文案(code 未知时才会被显示)。
203
+ */
200
204
  async function fetchBalance() {
201
205
  const credentials = ctx.get('credentials')
202
206
  if (!credentials || typeof credentials.resolve !== 'function') {
203
- return { ok: false, error: 'credentials 服务不可用' }
207
+ return { ok: false, code: 'no-credentials', error: 'credentials service unavailable' }
204
208
  }
205
209
  let resolved = null
206
210
  try {
207
211
  resolved = await credentials.resolve(KEY_REF)
208
212
  } catch (e) {
209
- return { ok: false, error: '读取 API Key 失败:' + errText(e) }
213
+ return { ok: false, code: 'key-read-failed', detail: errText(e), error: 'failed to read the API key' }
210
214
  }
211
215
  if (!resolved || typeof resolved.value !== 'string' || resolved.value === '') {
212
- return { ok: false, error: '未配置 ' + KEY_REF }
216
+ return { ok: false, code: 'no-key', detail: KEY_REF, error: 'no API key configured' }
213
217
  }
214
218
  const subprocess = ctx.get('subprocess')
215
219
  if (!subprocess || typeof subprocess.spawn !== 'function') {
216
- return { ok: false, error: 'subprocess 服务不可用' }
220
+ return { ok: false, code: 'no-subprocess', error: 'subprocess service unavailable' }
217
221
  }
218
222
  let cwd = process.cwd()
219
223
  try {
@@ -229,6 +233,8 @@ export function apply(ctx) {
229
233
  ['curl.exe', '-sS', '--max-time', '12', '-K', '-'],
230
234
  ]
231
235
  let lastError = ''
236
+ let lastCode = ''
237
+ let lastDetail = ''
232
238
  for (let i = 0; i < candidates.length; i += 1) {
233
239
  let handle = null
234
240
  try {
@@ -243,14 +249,18 @@ export function apply(ctx) {
243
249
  graceMs: 3000,
244
250
  })
245
251
  } catch (e) {
246
- lastError = errText(e)
252
+ lastCode = 'spawn-failed'
253
+ lastDetail = errText(e)
254
+ lastError = 'spawn failed'
247
255
  continue
248
256
  }
249
257
  let outcome = null
250
258
  try {
251
259
  outcome = await handle.done
252
260
  } catch (e) {
253
- lastError = errText(e)
261
+ lastCode = 'spawn-failed'
262
+ lastDetail = errText(e)
263
+ lastError = 'spawn failed'
254
264
  continue
255
265
  }
256
266
  const collected = handle.collected || {}
@@ -259,27 +269,30 @@ export function apply(ctx) {
259
269
  const text = outRead && typeof outRead.text === 'string' ? outRead.text.trim() : ''
260
270
  const stderrText = errRead && typeof errRead.text === 'string' ? errRead.text.trim() : ''
261
271
  if (!text) {
262
- const code = outcome && typeof outcome.exitCode === 'number' ? '(退出码 ' + String(outcome.exitCode) + ')' : ''
263
- lastError = 'curl 无输出' + (stderrText ? ':' + stderrText.slice(0, 200) : '') + code
272
+ lastCode = 'curl-empty'
273
+ lastDetail = (stderrText ? stderrText.slice(0, 200) : '') + (outcome && typeof outcome.exitCode === 'number' ? ' (exit ' + String(outcome.exitCode) + ')' : '')
274
+ lastError = 'curl produced no output'
264
275
  continue
265
276
  }
266
277
  let json = null
267
278
  try {
268
279
  json = JSON.parse(text)
269
280
  } catch (e) {
270
- lastError = '响应解析失败:' + text.slice(0, 160)
281
+ lastCode = 'bad-response'
282
+ lastDetail = text.slice(0, 160)
283
+ lastError = 'could not parse the response'
271
284
  continue
272
285
  }
273
286
  return normalizeBalance(json)
274
287
  }
275
- return { ok: false, error: lastError || '余额接口请求失败' }
288
+ return { ok: false, code: lastCode || 'balance-request-failed', detail: lastDetail, error: lastError || 'balance request failed' }
276
289
  }
277
290
 
278
291
  /** 逐轮用量(带 length 版本缓存,日志没变就不重折)。 */
279
292
  async function handleTurns(sessionId) {
280
- if (!sessionId) return { ok: false, error: '缺少 sessionId', turns: [] }
293
+ if (!sessionId) return { ok: false, code: 'missing-session-id', error: 'missing sessionId', turns: [] }
281
294
  const events = await readEvents(sessionId)
282
- if (events === null) return { ok: false, error: '会话日志不可用', turns: [] }
295
+ if (events === null) return { ok: false, code: 'log-unavailable', error: 'session log unavailable', turns: [] }
283
296
  const cached = turnCache.get(sessionId)
284
297
  if (cached && cached.length === events.length) return { ok: true, cached: true, turns: cached.turns }
285
298
  const turns = foldTurns(events)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-consumption-badge",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "DSH Web UI plugin: a per-message consumption badge (tokens | estimated cost | peak/valley) plus a session detail modal with cumulative tokens, estimated cost, Beijing-time peak/off-peak pricing, a per-turn dual-axis chart, and the DeepSeek account balance",
5
5
  "keywords": [
6
6
  "deepseek-harness",