@gehennawu/dsh-service 0.25.0 → 0.26.0
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 +1 -1
- package/README.md +1 -1
- package/client.js +51 -20
- package/index.js +6 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -44,7 +44,7 @@ The plugin also appears under **Plugins → Plugin configuration**, with six hos
|
|
|
44
44
|
|
|
45
45
|
### Quota lookup
|
|
46
46
|
|
|
47
|
-
- A dedicated "Quota lookup" tab shows adapted providers as separate cards, each window rendered as a percentage with its own bar and a reset countdown on its own line, plus a refresh icon beside the updated time that force-refreshes that provider on click (bypassing the poll interval);
|
|
47
|
+
- A dedicated "Quota lookup" tab shows adapted providers as separate cards, each window rendered as a percentage with its own bar and a reset countdown on its own line, plus a refresh icon beside the updated time that force-refreshes that provider on click (bypassing the poll interval); adapted card titles link to the provider's official usage page (DeepSeek Platform, Zhipu GLM Coding Plan, OpenCode Go) in a new tab; a "Reorder" toggle at the top-right of the card list (shown with two or more cards) reveals ↑/↓ buttons on each card header (auto-disabled at the ends), and clicking it again hides them; the order is remembered in this browser and newly seen providers append at the end; unadapted providers take no space — a "Manual adapt" row at the bottom enables one by picking its type, and each card's footer can switch type, fall back to auto-detect, or disable lookup at any time
|
|
48
48
|
- A quota ring inside the conversation composer follows the provider selected by the current session and shows the tightest budget window as a percentage (green below 80%, amber at or above); clicking opens a panel headed by the provider name, with a bar and used percentage for each window and reset times on their own lines; on narrow viewports (phones) the panel switches to a viewport-centered floating layer — fully visible, scrolling internally when too tall — and rotates or resizing wider flips it back to the anchored placement above the ring
|
|
49
49
|
- A "Show a remote quota entry in the settings left navigation" switch lives in the Quota lookup tab (off by default), mirroring the Restart entry
|
|
50
50
|
- Built-in adaptations: **OpenCode Go** (`{baseURL}/usage`), **Zhipu GLM Coding Plan / zai-coding-cn** (official monitor `quota/limit` endpoint with three windows — 5-hour rolling tokens, weekly tokens, monthly MCP quota; an idle 5-hour window hides its reset time, matching the official console), **OpenRouter** (credits used %), **Kimi/Moonshot** and **SiliconFlow** (CNY balance text), **DeepSeek Platform** (official balance, see next bullet); dialects that natively report remaining percentage flip the panel word to "Remaining" and invert the warn threshold; transient network errors retry automatically and the Zhipu dual-domain candidate chain switches automatically; the provider-to-kind mapping lives in `DSH_HOME/dsh-service-quota.json`, and known services are auto-detected from their baseURL (e.g. opencode.ai, bigmodel.cn, api.deepseek.com) with no manual picking; no upstream request is ever made for unadapted or disabled providers; disabling can be done via "Disable" in a card's footer or by writing `"<provider>": null` in the config file (both are equivalent). Zhipu reset cards have no API-key-queryable endpoint yet — you can add any number of them via each provider card's "Add reset card" button on the tab (name plus expiry time, down to the minute), and remove each one independently — the ring panel shows them too (stored in the same config file); expired cards are flagged automatically
|
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
|
|
45
45
|
### 额度查询
|
|
46
46
|
|
|
47
|
-
-
|
|
47
|
+
- 独立的「额度查询」标签:以卡片分区展示已适配的供应商,每个窗口显示百分比、独立进度条,重置时间单独一行;卡片头部更新时间旁有刷新图标,点击即强制重拉该供应商(不受轮询间隔限制);已适配卡片的标题链接到对应官网用量页(DeepSeek 平台、智谱 GLM Coding Plan、OpenCode Go),点击新标签页打开;卡片列表右上角有「调整排序」开关(至少两张卡时显示),点击后各卡头部出现 ↑/↓ 按钮(首末卡对应方向自动禁用),再点一次收起;顺序记忆在本浏览器,新出现的供应商排在末尾;未适配的不占位置,统一收进底部「手动适配」行选择类型启用,卡片脚部可随时切换适配类型、回退自动识别或停用查询
|
|
48
48
|
- 对话输入框内一枚额度圆环,跟随当前会话所选模型的供应商,显示最紧预算窗口的已用百分比(<80% 绿色、≥80% 黄色);点击弹出面板——头部标明供应商,各窗口带独立进度条与已用百分比、重置时间单独一行;手机等窄屏上面板自动切换为视口居中的浮层(完整可见、超高时内部滚动),旋转或拖宽窗口即时切回圆环上方锚定
|
|
49
49
|
- 可在「额度查询」标签开启「设置页左列显示入口」开关(默认关闭),开启后在设置页左侧标签列底部显示「额度查询」快捷入口(与「重启」入口同模式)
|
|
50
50
|
- 内置适配:**OpenCode Go**(`{baseURL}/usage`)、**智谱 GLM Coding Plan / zai-coding-cn**(官方监控端点 `quota/limit`,含 5 小时滚动 Token、每周 Token、MCP 月度配额三个窗口,5 小时窗口空闲时与官网一致地不显示重置时间)、**OpenRouter**(credits 已用%)、**Kimi/Moonshot** 与 **硅基流动**(人民币余额)、**DeepSeek 开放平台**(官方余额,见下一条);原生报「剩余百分比」的方言会自动把面板头部切换为「剩余」并把预警阈值反向;上游瞬时网络错误自动重试,智谱双域候选链自动切换;供应商与适配类型的对应关系保存在 `DSH_HOME/dsh-service-quota.json`,已知服务商按 baseURL 自动识别适配(如 opencode.ai、bigmodel.cn、api.deepseek.com),无需手选;未适配或已停用的供应商不会被发起任何上游请求,停用可在卡片脚部选「停用查询」或在配置文件写 `"<provider>": null`(两者等价)。智谱的重置卡暂无 API Key 可查的接口,可在「额度查询」标签内点各供应商卡片的「添加重置卡」填写名称与到期时间(可精确到分钟),可连续添加多条、每条独立移除;圆环面板同步显示(数据存入同一配置文件),过期自动标注
|
package/client.js
CHANGED
|
@@ -406,6 +406,8 @@ window.__ModuleLoader__.load({
|
|
|
406
406
|
'quota.refresh': '刷新',
|
|
407
407
|
'quota.card.moveUp': '上移',
|
|
408
408
|
'quota.card.moveDown': '下移',
|
|
409
|
+
'quota.reorder': '调整排序',
|
|
410
|
+
'quota.usageLink': '打开官网用量页',
|
|
409
411
|
'quota.adapt': '适配',
|
|
410
412
|
'quota.kind.opencode-go': 'OpenCode Go',
|
|
411
413
|
'quota.kind.zai-coding-cn': '智谱 GLM Coding Plan',
|
|
@@ -860,6 +862,8 @@ window.__ModuleLoader__.load({
|
|
|
860
862
|
'quota.refresh': 'Refresh',
|
|
861
863
|
'quota.card.moveUp': 'Move up',
|
|
862
864
|
'quota.card.moveDown': 'Move down',
|
|
865
|
+
'quota.reorder': 'Reorder',
|
|
866
|
+
'quota.usageLink': 'Open the official usage page',
|
|
863
867
|
'quota.adapt': 'Adapt',
|
|
864
868
|
'quota.kind.opencode-go': 'OpenCode Go',
|
|
865
869
|
'quota.kind.zai-coding-cn': 'Zhipu GLM Coding Plan',
|
|
@@ -2929,6 +2933,8 @@ window.__ModuleLoader__.load({
|
|
|
2929
2933
|
const clearAdaptedKind = (providerName) => requestQuotaConfig({ provider: providerName, clear: true })
|
|
2930
2934
|
// 卡片手动排序:localStorage 名单驱动展示序(纯客户端,宿主契约不动);新供应商自然排末尾。
|
|
2931
2935
|
const [cardOrder, setCardOrder] = useState(readQuotaCardOrder())
|
|
2936
|
+
// 排序模式开关(用户点名):平时不显示 ↑↓,点「调整排序」才出现,避免卡片头部常驻小钮。
|
|
2937
|
+
const [reorderMode, setReorderMode] = useState(false)
|
|
2932
2938
|
// 卡片分区(v0.20):只展示已适配供应商;未适配/已停用的不渲染灰行,统一收进底部「手动适配」行。
|
|
2933
2939
|
const adaptedRows = applyQuotaCardOrder(providers.filter((row) => row.adapted === true), cardOrder)
|
|
2934
2940
|
const candidateRows = providers.filter((row) => row.adapted !== true)
|
|
@@ -2981,12 +2987,34 @@ window.__ModuleLoader__.load({
|
|
|
2981
2987
|
style: { width: '34px', height: '20px', borderRadius: '10px', padding: 0, flexShrink: 0, position: 'relative', border: `1px solid ${quotaNav ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-border-l2)'}`, background: quotaNav ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-bg-layer-2)', cursor: 'pointer', lineHeight: 0 },
|
|
2982
2988
|
}, React.createElement('span', { style: { position: 'absolute', top: '1px', left: quotaNav ? '15px' : '1px', width: '16px', height: '16px', borderRadius: '50%', background: quotaNav ? '#fff' : 'var(--dsw-alias-label-tertiary)' } }))),
|
|
2983
2989
|
configError !== '' ? React.createElement('p', { 'data-testid': 'quota-config-error', style: Object.assign({}, hint, { color: 'var(--dsw-alias-state-error-primary)' }) }, configError) : null,
|
|
2990
|
+
// 「调整排序」开关(≥2 张卡才有意义):进入后卡片头部出现 ↑↓,再点一次收起。
|
|
2991
|
+
...(adaptedRows.length >= 2
|
|
2992
|
+
? [React.createElement('div', { key: 'quota-reorder-row', style: { display: 'flex', justifyContent: 'flex-end', margin: '2px 0 8px' } },
|
|
2993
|
+
React.createElement('button', {
|
|
2994
|
+
type: 'button',
|
|
2995
|
+
'data-testid': 'quota-reorder-toggle',
|
|
2996
|
+
'aria-pressed': String(reorderMode),
|
|
2997
|
+
title: translate('quota.reorder'),
|
|
2998
|
+
onClick: () => setReorderMode(!reorderMode),
|
|
2999
|
+
style: { fontSize: '12px', lineHeight: '20px', padding: '2px 12px', borderRadius: 999, border: `1px solid ${reorderMode ? 'var(--dsw-alias-brand-primary)' : 'var(--dsw-alias-border-l2)'}`, background: 'transparent', color: reorderMode ? 'var(--dsw-alias-brand-primary)' : 'var(--dsw-alias-label-secondary)', cursor: 'pointer' },
|
|
3000
|
+
}, translate('quota.reorder')))]
|
|
3001
|
+
: []),
|
|
2984
3002
|
adaptedRows.length === 0
|
|
2985
3003
|
? React.createElement('p', { style: hint }, translate('quota.noAdapted'))
|
|
2986
3004
|
: React.createElement('div', { 'data-testid': 'quota-card-list', style: { display: 'flex', flexDirection: 'column', gap: '10px' } },
|
|
2987
3005
|
adaptedRows.map((row, index) => {
|
|
2988
3006
|
const nameNode = React.createElement('span', { style: { fontWeight: 600, fontSize: '12px', overflowWrap: 'anywhere' } },
|
|
2989
|
-
|
|
3007
|
+
// 官网用量页链接(用户点名):宿主按 kind 下发 usageUrl 时,展示名本身即外链。
|
|
3008
|
+
typeof row.usageUrl === 'string' && row.usageUrl !== ''
|
|
3009
|
+
? React.createElement('a', {
|
|
3010
|
+
'data-testid': `quota-usage-link-${row.provider}`,
|
|
3011
|
+
href: row.usageUrl,
|
|
3012
|
+
target: '_blank',
|
|
3013
|
+
rel: 'noreferrer',
|
|
3014
|
+
title: translate('quota.usageLink'),
|
|
3015
|
+
style: { color: 'var(--dsw-alias-brand-primary)', textDecoration: 'underline' },
|
|
3016
|
+
}, row.displayName || row.provider)
|
|
3017
|
+
: (row.displayName || row.provider),
|
|
2990
3018
|
row.kindSource === 'auto'
|
|
2991
3019
|
? React.createElement('span', {
|
|
2992
3020
|
'data-testid': `quota-auto-tag-${row.provider}`,
|
|
@@ -3048,25 +3076,28 @@ window.__ModuleLoader__.load({
|
|
|
3048
3076
|
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: '10px' } },
|
|
3049
3077
|
nameNode,
|
|
3050
3078
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '5px' } },
|
|
3051
|
-
//
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
'
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
'
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3079
|
+
// 手动排序(用户点名):仅在「调整排序」模式下出现;↑↓ 与相邻卡换位,
|
|
3080
|
+
// 首/末卡对应方向禁用;顺序存本浏览器。
|
|
3081
|
+
...(reorderMode ? [
|
|
3082
|
+
React.createElement('button', {
|
|
3083
|
+
type: 'button',
|
|
3084
|
+
'data-testid': `quota-move-up-${row.provider}`,
|
|
3085
|
+
'aria-label': translate('quota.card.moveUp'),
|
|
3086
|
+
title: translate('quota.card.moveUp'),
|
|
3087
|
+
disabled: index === 0,
|
|
3088
|
+
onClick: () => moveQuotaCard(row.provider, -1),
|
|
3089
|
+
style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '16px', height: '16px', padding: 0, border: 'none', background: 'transparent', color: index === 0 ? 'var(--dsw-alias-label-tertiary)' : 'var(--dsw-alias-label-secondary)', cursor: index === 0 ? 'default' : 'pointer', opacity: index === 0 ? 0.45 : 1, fontSize: '12px', lineHeight: '16px' },
|
|
3090
|
+
}, '↑'),
|
|
3091
|
+
React.createElement('button', {
|
|
3092
|
+
type: 'button',
|
|
3093
|
+
'data-testid': `quota-move-down-${row.provider}`,
|
|
3094
|
+
'aria-label': translate('quota.card.moveDown'),
|
|
3095
|
+
title: translate('quota.card.moveDown'),
|
|
3096
|
+
disabled: index === adaptedRows.length - 1,
|
|
3097
|
+
onClick: () => moveQuotaCard(row.provider, 1),
|
|
3098
|
+
style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '16px', height: '16px', padding: 0, border: 'none', background: 'transparent', color: index === adaptedRows.length - 1 ? 'var(--dsw-alias-label-tertiary)' : 'var(--dsw-alias-label-secondary)', cursor: index === adaptedRows.length - 1 ? 'default' : 'pointer', opacity: index === adaptedRows.length - 1 ? 0.45 : 1, fontSize: '12px', lineHeight: '16px' },
|
|
3099
|
+
}, '↓'),
|
|
3100
|
+
] : []),
|
|
3070
3101
|
// 手动刷新:SVG 图标按钮,点击强制该 provider 重拉上游;在途时置灰防重入。
|
|
3071
3102
|
React.createElement('button', {
|
|
3072
3103
|
type: 'button',
|
package/index.js
CHANGED
|
@@ -79,6 +79,8 @@ const KIND_REGISTRY = {
|
|
|
79
79
|
parser: normalizeOpencodeUsage,
|
|
80
80
|
keyHints: ['OPENCODE_GO_API_KEY', 'OPENCODE_API_KEY'],
|
|
81
81
|
hosts: ['opencode.ai'],
|
|
82
|
+
// 官网用户页(卡片标题外链;宿主常量,零浏览器输入)。
|
|
83
|
+
usageUrl: 'https://opencode.ai/',
|
|
82
84
|
},
|
|
83
85
|
'zai-coding-cn': {
|
|
84
86
|
parser: normalizeZaiCodingUsage,
|
|
@@ -88,6 +90,7 @@ const KIND_REGISTRY = {
|
|
|
88
90
|
],
|
|
89
91
|
keyHints: ['ZAI_CODING_CN_API_KEY', 'ZAI_API_KEY', 'BIGMODEL_API_KEY'],
|
|
90
92
|
hosts: ['open.bigmodel.cn', 'bigmodel.cn'],
|
|
93
|
+
usageUrl: 'https://open.bigmodel.cn/coding-plan/personal/usage',
|
|
91
94
|
},
|
|
92
95
|
openrouter: {
|
|
93
96
|
parser: normalizeOpenRouterCredits,
|
|
@@ -114,6 +117,7 @@ const KIND_REGISTRY = {
|
|
|
114
117
|
endpoints: ['https://api.deepseek.com/user/balance'],
|
|
115
118
|
keyHints: ['DEEPSEEK_API_KEY'],
|
|
116
119
|
hosts: ['api.deepseek.com', 'deepseek.com'],
|
|
120
|
+
usageUrl: 'https://platform.deepseek.com/usage',
|
|
117
121
|
},
|
|
118
122
|
// CLIProxyAPI(router-for-me/CLIProxyAPI)管理面:查它托管的 OAuth 上游账号官方额度。
|
|
119
123
|
// 无固定 endpoints / 全局 hosts——每个部署一个域名,保存适配时把 settings baseURL 的
|
|
@@ -3677,6 +3681,8 @@ function apply(ctx) {
|
|
|
3677
3681
|
nextAllowedAt: view.nextAllowedAt,
|
|
3678
3682
|
...(providerResetCards.length > 0 ? { resetCards: providerResetCards } : {}),
|
|
3679
3683
|
...(credentialHints !== undefined ? { credentialHints } : {}),
|
|
3684
|
+
// 官网用户页余额网址(宿主常量白名单随 kind 下发;无则缺省)。
|
|
3685
|
+
...(typeof KIND_REGISTRY[kind].usageUrl === 'string' && KIND_REGISTRY[kind].usageUrl !== '' ? { usageUrl: KIND_REGISTRY[kind].usageUrl } : {}),
|
|
3680
3686
|
})
|
|
3681
3687
|
}
|
|
3682
3688
|
return { ok: true, value: { providers: rows, serverTime: Date.now() } }
|