dsh-all-usage 1.1.2 → 1.1.3
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 +66 -0
- package/README.md +193 -19
- package/fixtures/usage-events.json +172 -0
- package/lib/aggregation.js +1002 -0
- package/lib/balance.js +112 -0
- package/lib/client.js +1 -2906
- package/lib/http.js +305 -0
- package/lib/index.js +2 -2119
- package/lib/ledger.js +464 -0
- package/lib/plugin.js +276 -0
- package/lib/pricing-runtime.js +282 -0
- package/lib/pricing.js +299 -36
- package/lib/session-sync.js +589 -0
- package/lib/usage-core.js +127 -0
- package/package.json +28 -3
- package/scripts/replay-fixture.mjs +155 -0
package/lib/balance.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export function createBalance(host) {
|
|
2
|
+
const { state } = host
|
|
3
|
+
const { credentials, settings } = host.services
|
|
4
|
+
|
|
5
|
+
async function requestBalance(url, key) {
|
|
6
|
+
let controller = null
|
|
7
|
+
let timer = null
|
|
8
|
+
try {
|
|
9
|
+
if (typeof AbortController === 'function') {
|
|
10
|
+
controller = new AbortController()
|
|
11
|
+
timer = setTimeout(() => controller.abort(), 30000)
|
|
12
|
+
}
|
|
13
|
+
const response = await fetch(url, {
|
|
14
|
+
method: 'GET',
|
|
15
|
+
headers: { accept: 'application/json', authorization: 'Bearer ' + key },
|
|
16
|
+
...(controller === null ? {} : { signal: controller.signal }),
|
|
17
|
+
})
|
|
18
|
+
return { ok: response.ok, status: response.status, text: await response.text() }
|
|
19
|
+
} catch (err) {
|
|
20
|
+
return { ok: false, status: 0, text: '', error: 'network request failed' }
|
|
21
|
+
} finally {
|
|
22
|
+
if (timer !== null) clearTimeout(timer)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function moneyOf(v) {
|
|
26
|
+
if (typeof v === 'number' && Number.isFinite(v)) return v
|
|
27
|
+
if (typeof v === 'string' && v.trim() !== '') {
|
|
28
|
+
const n = parseFloat(v)
|
|
29
|
+
if (Number.isFinite(n)) return n
|
|
30
|
+
}
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
function parseBalance(text) {
|
|
34
|
+
let obj = null
|
|
35
|
+
try {
|
|
36
|
+
obj = JSON.parse(String(text).replace(/^\uFEFF/, ''))
|
|
37
|
+
} catch (err) {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
if (obj === null || typeof obj !== 'object') return null
|
|
41
|
+
if (obj.is_available === false) return { unavailable: true, currencies: [] }
|
|
42
|
+
const infos = (Array.isArray(obj.balance_infos) && obj.balance_infos) || (Array.isArray(obj.balance) && obj.balance) || null
|
|
43
|
+
if (!infos) return null
|
|
44
|
+
const out = []
|
|
45
|
+
for (const info of infos) {
|
|
46
|
+
if (info === null || typeof info !== 'object') continue
|
|
47
|
+
if (typeof info.currency !== 'string') continue
|
|
48
|
+
out.push({
|
|
49
|
+
currency: info.currency,
|
|
50
|
+
total: moneyOf(info.total_balance !== undefined ? info.total_balance : info.balance),
|
|
51
|
+
granted: moneyOf(info.granted_balance),
|
|
52
|
+
toppedUp: moneyOf(info.topped_up_balance),
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
return { unavailable: false, currencies: out }
|
|
56
|
+
}
|
|
57
|
+
async function fetchBalance(force) {
|
|
58
|
+
const now = Date.now()
|
|
59
|
+
if (force !== true && state.balanceCache.payload !== null && now - state.balanceCache.fetchedAt < 300000) return state.balanceCache.payload
|
|
60
|
+
let ref = 'DEEPSEEK_API_KEY'
|
|
61
|
+
if (settings !== undefined) {
|
|
62
|
+
try {
|
|
63
|
+
const section = settings.get('llm-deepseek')
|
|
64
|
+
if (section !== null && typeof section === 'object' && typeof section.apiKeyEnv === 'string' && section.apiKeyEnv.length > 0) ref = section.apiKeyEnv
|
|
65
|
+
} catch (err) { /* default ref */ }
|
|
66
|
+
}
|
|
67
|
+
let key
|
|
68
|
+
if (credentials !== undefined) {
|
|
69
|
+
try {
|
|
70
|
+
const hit = await credentials.resolve(ref)
|
|
71
|
+
if (hit !== null && hit !== undefined && typeof hit.value === 'string' && hit.value.length > 0) key = hit.value
|
|
72
|
+
} catch (err) { /* unconfigured */ }
|
|
73
|
+
}
|
|
74
|
+
if (key === undefined) {
|
|
75
|
+
const payload = { status: 'missing-key' }
|
|
76
|
+
state.balanceCache = { fetchedAt: now, payload }
|
|
77
|
+
return payload
|
|
78
|
+
}
|
|
79
|
+
if (typeof fetch !== 'function') {
|
|
80
|
+
const payload = { status: 'error', message: '当前 DSH 运行时不支持余额查询' }
|
|
81
|
+
state.balanceCache = { fetchedAt: now, payload }
|
|
82
|
+
return payload
|
|
83
|
+
}
|
|
84
|
+
const result = await requestBalance('https://api.deepseek.com/user/balance', key)
|
|
85
|
+
const body = (result.text || '').trim()
|
|
86
|
+
if (result.ok && body.length > 0) {
|
|
87
|
+
const parsed = parseBalance(body)
|
|
88
|
+
if (parsed !== null && parsed.unavailable) {
|
|
89
|
+
const payload = { status: 'unavailable', message: 'DeepSeek 接口返回余额不可用(is_available=false)' }
|
|
90
|
+
state.balanceCache = { fetchedAt: now, payload }
|
|
91
|
+
return payload
|
|
92
|
+
}
|
|
93
|
+
if (parsed !== null && parsed.currencies.length > 0) {
|
|
94
|
+
const payload = { status: 'ok', currencies: parsed.currencies, fetchedAt: now }
|
|
95
|
+
state.balanceCache = { fetchedAt: now, payload }
|
|
96
|
+
return payload
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const detail = body.length > 0 ? body.slice(0, 300) : (result.status > 0 ? 'HTTP ' + result.status : result.error || 'network request failed')
|
|
100
|
+
const payload = { status: 'error', message: '余额查询失败', detail }
|
|
101
|
+
state.balanceCache = { fetchedAt: now, payload }
|
|
102
|
+
return payload
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
requestBalance,
|
|
108
|
+
moneyOf,
|
|
109
|
+
parseBalance,
|
|
110
|
+
fetchBalance
|
|
111
|
+
}
|
|
112
|
+
}
|