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/README.md +82 -51
- package/lib/client.js +458 -136
- package/lib/index.js +26 -13
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -196,24 +196,28 @@ export function apply(ctx) {
|
|
|
196
196
|
return null
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
/**
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
263
|
-
|
|
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
|
-
|
|
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: '
|
|
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: '
|
|
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.
|
|
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",
|