dsh-cost-meter 1.5.10 → 1.5.12
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.en.md +5 -5
- package/README.md +5 -5
- package/lib/client.js +2 -2
- package/lib/index.js +18 -9
- package/lib/pricing.js +6 -4
- package/lib/store.js +31 -4
- package/lib/typert.host.js +6 -3
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Per-conversation cost · daily totals · OpenCode Go subscription quota display · budget with usage percentage · official account balance · custom provider balance · balance progress bar · history · peak/off-peak pricing hours display (peak hours UTC 01:00–04:00, 06:00–10:00) · one-click price sync from the official docs · Codex-style token usage heat grid · multi-vendor model pricing (built-in 90+ model price catalog with auto-matching) · mainstream Coding Plan quota queries & display (Anthropic / Z.ai / MiniMax / Kimi / OpenRouter / SiliconFlow)
|
|
8
8
|
|
|
9
|
-
[](https://github.com/Han-1413141/dsh-cost-meter)
|
|
10
10
|
[](https://www.npmjs.com/package/dsh-cost-meter)
|
|
11
11
|
[](LICENSE)
|
|
12
12
|
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
@@ -191,22 +191,22 @@ Real captures from an actual DSH sidebar of the period strip and collapsed verti
|
|
|
191
191
|
dsh plugin --profile web add dsh-cost-meter
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
-
**PowerShell one-click script** (copy the whole line, paste, press Enter; pnpm is provisioned automatically, git is auto-detected — no clone needed; the install chain is **pinned to the release tag `v1.5.
|
|
194
|
+
**PowerShell one-click script** (copy the whole line, paste, press Enter; pnpm is provisioned automatically, git is auto-detected — no clone needed; the install chain is **pinned to the release tag `v1.5.12`** — review the script before running):
|
|
195
195
|
|
|
196
196
|
```powershell
|
|
197
|
-
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.5.
|
|
197
|
+
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.5.12/install.ps1 | iex
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
**Or a plain command line** (the machine must already have pnpm and git; also pinned to the tag):
|
|
201
201
|
|
|
202
202
|
```sh
|
|
203
|
-
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.5.
|
|
203
|
+
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.5.12
|
|
204
204
|
```
|
|
205
205
|
|
|
206
206
|
Without git, use the GitHub tag archive:
|
|
207
207
|
|
|
208
208
|
```sh
|
|
209
|
-
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.5.
|
|
209
|
+
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.5.12.tar.gz
|
|
210
210
|
```
|
|
211
211
|
|
|
212
212
|
After installing, **restart** `dsh web` (plugin rows, the Typert manifest and the client bundle are all scanned at startup):
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
本会话费用 · 当日费用 · OpenCode Go 订阅额度显示 · 预算与已用百分比 · 官方账户余额 · 自定义 Provider 余额查询(可配任意 HTTP 端点) · 余额三段进度条 · 历史记录 · 峰谷计价时段显示(UTC 01:00–04:00、06:00–10:00 为峰时段) · 官方价格一键同步 · 类 Codex Token 用量热图 · 多厂商多模型价格计费(内置 90+ 模型价格目录与自动匹配) · 主流 Coding Plan 额度查询与显示(Anthropic / Z.ai / MiniMax / Kimi / OpenRouter / SiliconFlow 六家)
|
|
8
8
|
|
|
9
|
-
[](https://github.com/Han-1413141/dsh-cost-meter)
|
|
10
10
|
[](https://www.npmjs.com/package/dsh-cost-meter)
|
|
11
11
|
[](LICENSE)
|
|
12
12
|
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
@@ -193,22 +193,22 @@
|
|
|
193
193
|
dsh plugin --profile web add dsh-cost-meter
|
|
194
194
|
```
|
|
195
195
|
|
|
196
|
-
**PowerShell 一键脚本**(复制整行粘贴回车;自动补齐 pnpm、自动探测 git,无需克隆仓库;安装链**固定到发布 tag `v1.5.
|
|
196
|
+
**PowerShell 一键脚本**(复制整行粘贴回车;自动补齐 pnpm、自动探测 git,无需克隆仓库;安装链**固定到发布 tag `v1.5.12`**,建议先下载审阅再运行):
|
|
197
197
|
|
|
198
198
|
```powershell
|
|
199
|
-
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.5.
|
|
199
|
+
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.5.12/install.ps1 | iex
|
|
200
200
|
```
|
|
201
201
|
|
|
202
202
|
**或直接命令行**(机器上需已有 pnpm 与 git;同样固定到 tag):
|
|
203
203
|
|
|
204
204
|
```sh
|
|
205
|
-
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.5.
|
|
205
|
+
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.5.12
|
|
206
206
|
```
|
|
207
207
|
|
|
208
208
|
没有 git 时可用 GitHub tag 打包直链:
|
|
209
209
|
|
|
210
210
|
```sh
|
|
211
|
-
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.5.
|
|
211
|
+
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.5.12.tar.gz
|
|
212
212
|
```
|
|
213
213
|
|
|
214
214
|
安装后**重启** `dsh web`(插件行、Typert 清单与客户端 bundle 均在启动时扫描):
|
package/lib/client.js
CHANGED
|
@@ -1261,8 +1261,8 @@ window.__ModuleLoader__.load({
|
|
|
1261
1261
|
invocation: { kind: 'direct' },
|
|
1262
1262
|
parameters: [
|
|
1263
1263
|
{ name: 'limit', wire: 'limit', source: 'json', codec: { mode: 'strict', typeSymbol: 'dsh-cost-meter#SessionLimit', schema: limitCodec } },
|
|
1264
|
-
{ name: 'sort', wire: 'sort', source: 'json', codec: { mode: 'strict', typeSymbol: 'dsh-cost-meter#SessionSort', schema: sortCodec } },
|
|
1265
|
-
{ name: 'dir', wire: 'dir', source: 'json', codec: { mode: 'strict', typeSymbol: 'dsh-cost-meter#SessionSortDir', schema: sortCodec } },
|
|
1264
|
+
{ name: 'sort', wire: 'sort', source: 'json', codec: { mode: 'strict', typeSymbol: 'dsh-cost-meter#SessionSort', schema: sortCodec }, acceptsUndefined: true },
|
|
1265
|
+
{ name: 'dir', wire: 'dir', source: 'json', codec: { mode: 'strict', typeSymbol: 'dsh-cost-meter#SessionSortDir', schema: sortCodec }, acceptsUndefined: true },
|
|
1266
1266
|
],
|
|
1267
1267
|
result: { mode: 'strict', typeSymbol: 'dsh-cost-meter#TopSessions', schema: topSessionsCodec },
|
|
1268
1268
|
},
|
package/lib/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import fs from 'node:fs'
|
|
|
18
18
|
import { join } from 'node:path'
|
|
19
19
|
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
|
20
20
|
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths'
|
|
21
|
-
import { Ledger, applyConfigPatch, localDayKey, reconcileBalanceDelta, zeroDay } from './store.js'
|
|
21
|
+
import { Ledger, applyConfigPatch, localDayKey, pickBalanceInfo, reconcileBalanceDelta, zeroDay } from './store.js'
|
|
22
22
|
import { backfillLegacyLedger } from './backfill.js'
|
|
23
23
|
import { OFFICIAL_PRICING_URL, normalizePrice, parsePricingHtml, costOf, priceEntryFor, providerPriceEntryFor, buildPriceCatalog } from './pricing.js'
|
|
24
24
|
import { CODING_PLAN_PROVIDERS, CODING_PLAN_PROVIDER_IDS, queryCodingPlan, emptyCustomBalance, queryCustomBalance } from './coding-plans.js'
|
|
@@ -467,7 +467,8 @@ async function queryBalance(ctx, locale) {
|
|
|
467
467
|
})
|
|
468
468
|
if (!response.ok) throw new Error(tmsg(locale, 'balanceHttp', { code: String(response.status) }))
|
|
469
469
|
const data = await response.json()
|
|
470
|
-
|
|
470
|
+
// 多币种账号返回 CNY/USD 两条且顺序不稳定(#24/#25):按余额与币种挑选,不固定取首条。
|
|
471
|
+
const info = pickBalanceInfo(data?.balance_infos)
|
|
471
472
|
if (info === undefined) throw new Error(tmsg(locale, 'balanceNoInfos'))
|
|
472
473
|
const num = value => {
|
|
473
474
|
const parsed = Number(value)
|
|
@@ -525,10 +526,13 @@ function buildState(ledger, balance = emptyBalance(), goQuota = emptyGoQuota(),
|
|
|
525
526
|
const check = stateSchema.safeParse(state)
|
|
526
527
|
if (check.success) return state
|
|
527
528
|
console.warn('[dsh-cost-meter] state 与 codec 漂移,尝试降级恢复可用性:', JSON.stringify(check.error.issues?.slice(0, 3) ?? check.error))
|
|
529
|
+
// 注意剔除键必须用解构省略而非赋 undefined:priceCatalog 是 schema 声明的
|
|
530
|
+
// optional 键,显式 undefined 键会被网关 JSON 安全校验拒绝(值合法但属性不安全)。
|
|
531
|
+
const { priceCatalog: _dropped, ...stateNoCatalog } = state
|
|
528
532
|
const attempts = [
|
|
529
|
-
|
|
530
|
-
{ ...
|
|
531
|
-
{ ...
|
|
533
|
+
stateNoCatalog,
|
|
534
|
+
{ ...stateNoCatalog, codingPlans: {} },
|
|
535
|
+
{ ...stateNoCatalog, codingPlans: {}, balance: emptyBalance(), goQuota: emptyGoQuota(), customBalance: emptyCustomBalance() },
|
|
532
536
|
]
|
|
533
537
|
for (const fallback of attempts) {
|
|
534
538
|
if (stateSchema.safeParse(fallback).success) return fallback
|
|
@@ -926,11 +930,9 @@ function createService(ctx, ledger) {
|
|
|
926
930
|
if (sortKey === 'recent' && !asc) rows.reverse()
|
|
927
931
|
for (const s of rows) {
|
|
928
932
|
if (s === null || typeof s !== 'object') continue
|
|
929
|
-
|
|
933
|
+
const row = {
|
|
930
934
|
date,
|
|
931
935
|
id: String(s.id ?? ''),
|
|
932
|
-
title: typeof s.title === 'string' && s.title.length > 0 ? s.title : undefined,
|
|
933
|
-
at: Number.isFinite(Number(s.at)) && Number(s.at) > 0 ? Number(s.at) : undefined,
|
|
934
936
|
input: s.input ?? 0,
|
|
935
937
|
output: s.output ?? 0,
|
|
936
938
|
cacheRead: s.cacheRead ?? 0,
|
|
@@ -939,7 +941,14 @@ function createService(ctx, ledger) {
|
|
|
939
941
|
calls: s.calls ?? 0,
|
|
940
942
|
cost: s.cost ?? 0,
|
|
941
943
|
byProviderModel: s.byProviderModel ?? {},
|
|
942
|
-
}
|
|
944
|
+
}
|
|
945
|
+
// title/at 缺席时不得写入 undefined 键:网关对返回值做 JSON 安全校验,
|
|
946
|
+
// 显式 undefined 属性会被「undefined is not JSON-safe」拒绝,整个 RPC
|
|
947
|
+
// result-invalid,会话排行面板加载失败(未命名/无时间戳会话即触发)。
|
|
948
|
+
if (typeof s.title === 'string' && s.title.length > 0) row.title = s.title
|
|
949
|
+
const at = Number(s.at)
|
|
950
|
+
if (Number.isFinite(at) && at > 0) row.at = at
|
|
951
|
+
all.push(row)
|
|
943
952
|
}
|
|
944
953
|
}
|
|
945
954
|
if (sortKey === 'cost') all.sort((a, b) => asc ? a.cost - b.cost : b.cost - a.cost)
|
package/lib/pricing.js
CHANGED
|
@@ -248,11 +248,13 @@ export const PROVIDER_MODEL_FAMILIES = {
|
|
|
248
248
|
* 内置只读目录(含 DeepSeek 当前模型);「挂载」= 把条目复制进可编辑价格表。
|
|
249
249
|
*/
|
|
250
250
|
export function buildPriceCatalog() {
|
|
251
|
-
const catalog =
|
|
251
|
+
const catalog = Object.create(null)
|
|
252
|
+
const isUnsafeKey = (key) => key === '__proto__' || key === 'constructor' || key === 'prototype'
|
|
252
253
|
const put = (provider, id, entry) => {
|
|
253
254
|
const family = PROVIDER_MODEL_FAMILIES[provider]?.[id] ?? id
|
|
254
|
-
if (
|
|
255
|
-
if (catalog[provider]
|
|
255
|
+
if (isUnsafeKey(provider) || isUnsafeKey(family) || isUnsafeKey(id)) return
|
|
256
|
+
if (catalog[provider] === undefined) catalog[provider] = Object.create(null)
|
|
257
|
+
if (catalog[provider][family] === undefined) catalog[provider][family] = Object.create(null)
|
|
256
258
|
catalog[provider][family][id] = entry
|
|
257
259
|
}
|
|
258
260
|
for (const [id, entry] of Object.entries(DEFAULT_PRICE_TABLE.models)) put('deepseek', id, entry)
|
|
@@ -654,12 +656,12 @@ export function formatMoney(usdCost, display) {
|
|
|
654
656
|
function decodeEntities(text) {
|
|
655
657
|
return text
|
|
656
658
|
.replace(/ /g, ' ')
|
|
657
|
-
.replace(/&/g, '&')
|
|
658
659
|
.replace(/</g, '<')
|
|
659
660
|
.replace(/>/g, '>')
|
|
660
661
|
.replace(/"/g, '"')
|
|
661
662
|
.replace(/'/g, "'")
|
|
662
663
|
.replace(/—/g, '—')
|
|
664
|
+
.replace(/&/g, '&')
|
|
663
665
|
}
|
|
664
666
|
|
|
665
667
|
function stripTags(html) {
|
package/lib/store.js
CHANGED
|
@@ -723,7 +723,9 @@ export class Ledger {
|
|
|
723
723
|
const ref = parsed.balanceRef
|
|
724
724
|
if (ref !== null && typeof ref === 'object' && typeof ref.date === 'string'
|
|
725
725
|
&& Number.isFinite(ref.total) && Number.isFinite(ref.granted) && Number.isFinite(ref.topped)) {
|
|
726
|
-
|
|
726
|
+
// 旧账本无 currency 字段:保留 undefined,与新版首次拉取币种不等,
|
|
727
|
+
// reconcile 时自然重置基准一次(旧参考点可能录自 USD 0.00 误读,不可信)。
|
|
728
|
+
balanceRef = { date: ref.date, total: ref.total, granted: ref.granted, topped: ref.topped, currency: typeof ref.currency === 'string' ? ref.currency : undefined, at: Number(ref.at) || 0 }
|
|
727
729
|
}
|
|
728
730
|
}
|
|
729
731
|
}
|
|
@@ -962,9 +964,10 @@ export class Ledger {
|
|
|
962
964
|
/**
|
|
963
965
|
* 余额差交叉校验(issue #18 讨论):用官方余额当日变动反推消费,与本地账本今日合计比对。
|
|
964
966
|
* 仅当余额确实减少时才对账——订阅/Coding Plan 消费不动官方余额,若强行用余额差
|
|
965
|
-
*
|
|
966
|
-
*
|
|
967
|
-
* @param
|
|
967
|
+
* 替代今日费用会把订阅用户全天消费归零;充值/额度结构变动时重置参考点防误判;
|
|
968
|
+
* 参考点与本次拉取的币种不一致时(多币种条目挑选切换/旧账本无币种标记)同样重置。
|
|
969
|
+
* @param prevRef - 上一参考点({ date, total, granted, topped, currency, at })或 null。
|
|
970
|
+
* @param balance - 本次拉取结果({ currency, totalBalance, grantedBalance, toppedUpBalance })。
|
|
968
971
|
* @param todayCost - 本地账本今日合计费用。
|
|
969
972
|
* @param dayKey - 本地日期键(YYYY-MM-DD)。
|
|
970
973
|
* @param nowMs - 当前时刻。
|
|
@@ -979,12 +982,18 @@ export function reconcileBalanceDelta(prevRef, balance, todayCost, dayKey, nowMs
|
|
|
979
982
|
total: balance.totalBalance,
|
|
980
983
|
granted: Number.isFinite(balance.grantedBalance) ? balance.grantedBalance : 0,
|
|
981
984
|
topped: Number.isFinite(balance.toppedUpBalance) ? balance.toppedUpBalance : 0,
|
|
985
|
+
currency: typeof balance.currency === 'string' ? balance.currency : '',
|
|
982
986
|
at: nowMs,
|
|
983
987
|
}
|
|
984
988
|
// 新的一天(或首次/参考点形状异常):打基准,不对账。
|
|
985
989
|
if (prevRef === null || typeof prevRef !== 'object' || prevRef.date !== dayKey) {
|
|
986
990
|
return { ref: snap, event: { kind: 'baseline' } }
|
|
987
991
|
}
|
|
992
|
+
// 币种变化(#24/#25):选中的条目币种切换(或旧参考点无币种标记)时金额不可比,
|
|
993
|
+
// 重置基准不对账——否则 USD 0.00 误读会让余额差虚高,触发 drift 误报。
|
|
994
|
+
if (prevRef.currency !== snap.currency) {
|
|
995
|
+
return { ref: snap, event: { kind: 'structure-reset' } }
|
|
996
|
+
}
|
|
988
997
|
// 充值/额度授予变动:充值与授信只会让分项余额增加(消费只会减少),分项变大则旧参考点失效,重置不告警。
|
|
989
998
|
if (snap.granted > prevRef.granted + 0.009 || snap.topped > prevRef.topped + 0.009) {
|
|
990
999
|
return { ref: snap, event: { kind: 'structure-reset' } }
|
|
@@ -998,3 +1007,21 @@ export function reconcileBalanceDelta(prevRef, balance, todayCost, dayKey, nowMs
|
|
|
998
1007
|
// ok/drift 都保留当日首次基准,后续拉取继续与早间基线比对。
|
|
999
1008
|
return { ref: prevRef, event: { kind: 'ok', spent, todayCost } }
|
|
1000
1009
|
}
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* 从官方余额接口的 balance_infos 中挑选要展示的条目(issues #24/#25)。
|
|
1013
|
+
*
|
|
1014
|
+
* 多币种账号同时返回 CNY/USD 两条,且实测两条的排列顺序每次请求不稳定——
|
|
1015
|
+
* 固定取首条会在 USD 排前时读到 0.00,余额在正确值与 0 之间跳变。挑选规则:
|
|
1016
|
+
* - 优先取「有余额」的条目;同有余额时优先 CNY(开放平台主币种,确定性优先);
|
|
1017
|
+
* - 全部为零时优先 CNY(未充值/新账号,保证不随返回顺序跳变);
|
|
1018
|
+
* - 无 CNY 条目时兜底首条(单币种账号行为与旧版一致)。
|
|
1019
|
+
* @param infos - balance_infos 数组(元素含 currency / total_balance 等官方字段)。
|
|
1020
|
+
* @returns 选中的条目,或 undefined(无有效条目,调用方按缺 balance_infos 报错)。
|
|
1021
|
+
*/
|
|
1022
|
+
export function pickBalanceInfo(infos) {
|
|
1023
|
+
const list = Array.isArray(infos) ? infos.filter(entry => entry !== null && typeof entry === 'object') : []
|
|
1024
|
+
const positive = list.filter(entry => Number(entry.total_balance) > 0)
|
|
1025
|
+
const cnyFirst = entries => entries.find(entry => String(entry.currency).toUpperCase() === 'CNY')
|
|
1026
|
+
return cnyFirst(positive) ?? positive[0] ?? cnyFirst(list) ?? list[0]
|
|
1027
|
+
}
|
package/lib/typert.host.js
CHANGED
|
@@ -358,8 +358,11 @@ export const TYPERT = {
|
|
|
358
358
|
invocation: { kind: 'direct' },
|
|
359
359
|
parameters: [
|
|
360
360
|
{ name: 'limit', wire: 'limit', source: 'json', codec: _limit$codec },
|
|
361
|
-
|
|
362
|
-
|
|
361
|
+
// sort/dir 与服务端函数默认值(sort='cost', dir='desc')对应,声明可缺省:
|
|
362
|
+
// 网关对 args 字段做精确匹配,不声明 acceptsUndefined 时旧客户端单参数调用
|
|
363
|
+
// 会因 missing "sort"/"dir" 被拒,会话排行面板直接加载失败。
|
|
364
|
+
{ name: 'sort', wire: 'sort', source: 'json', codec: _sort$codec, acceptsUndefined: true },
|
|
365
|
+
{ name: 'dir', wire: 'dir', source: 'json', codec: _dir$codec, acceptsUndefined: true },
|
|
363
366
|
],
|
|
364
367
|
result: _topSessions$codec,
|
|
365
368
|
},
|
|
@@ -440,7 +443,7 @@ export const TYPERT = {
|
|
|
440
443
|
{
|
|
441
444
|
kind: 'method',
|
|
442
445
|
name: 'getTopSessions',
|
|
443
|
-
signature: 'getTopSessions(limit: number, sort
|
|
446
|
+
signature: 'getTopSessions(limit: number, sort?: string, dir?: string): TopSessions',
|
|
444
447
|
summary: '跨全部日期按指定排序返回前 N 个会话(不分日期视角)。Return the top N sessions across all days with the given sort.',
|
|
445
448
|
jsDoc: '/**\n * 跨全部日期返回前 N 个会话(每条带所属日期/标题/创建时刻)。\n * @param limit - 返回条数上限(服务端限制 1-500)。\n * @param sort - cost(费用) | time(创建时间) | recent(实时顺序)。\n * @param dir - asc | desc(默认 desc)。\n * @returns 会话列表(含 date/title/at 字段)。\n * Return the top N sessions across all days (each tagged with date/title/createdAt).\n * @param limit - Max rows to return (server clamps to 1-500).\n * @param sort - cost | time | recent (ledger/sidebar order).\n * @param dir - asc | desc (defaults to desc).\n * @returns Session list with date/title/at fields.\n */',
|
|
446
449
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-cost-meter",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.12",
|
|
4
4
|
"description": "DeepSeek Harness 会话费用统计插件:本会话成本、当日费用、历史记录与官方价格同步,支持多厂商多模型价格计费(内置 90+ 模型价格目录与自动匹配)、主流 Coding Plan 订阅额度查询与显示(6 家)、自定义 Provider 余额查询(可配任意 HTTP 端点)与余额进度条,界面中英双语。Session cost tracking plugin for DeepSeek Harness: per-conversation cost, daily totals, history and official price sync, with multi-vendor model pricing (built-in 90+ model catalog with auto-matching), Coding Plan quota queries & display for 6 vendors, custom provider balance lookup (configurable HTTP endpoint) and balance progress bar, in a bilingual (Chinese/English) UI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|