dsh-all-usage 1.1.2 → 1.1.4

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/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
+ }