opencode-visual-cache 1.4.0 → 1.6.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.md +37 -4
- package/README_EN.md +37 -4
- package/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/balance-providers.d.ts +27 -0
- package/dist/balance-providers.js +131 -0
- package/dist/i18n.d.ts +102 -0
- package/dist/i18n.js +368 -0
- package/dist/index.js +585 -283
- package/dist/tui.js +1350 -453
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/balance-providers.ts +153 -0
- package/src/i18n.ts +380 -0
- package/src/index.tsx +687 -343
package/README.md
CHANGED
|
@@ -48,11 +48,13 @@
|
|
|
48
48
|
- **颜色自适应**:命中率 ≥85% 绿 · ≥70% 橙 · <70% 红,颜色从主题色自动去饱和
|
|
49
49
|
- **Token 分布**:按角色(系统提示 / 用户 / Agent 指令 / Tool 调用 / Tool 结果)展示估算 Token 占比
|
|
50
50
|
- **折叠记忆**:折叠状态持久化,重启后保持
|
|
51
|
-
-
|
|
51
|
+
- **语言适配**:支持 中文 / English / 日本語 / 한국어,自动检测系统语言,`/cache-lang` 运行时切换,偏好持久化优先
|
|
52
52
|
- **多币种**:通过 `/cache-currency` 切换货币,费用和节省同步换算
|
|
53
|
+
- **余额查询**:查询多家 AI 提供商的账户余额,支持自动切换跟随当前会话提供商
|
|
53
54
|
- **斜杠命令**:`/cache-session` `/cache-session-back` `/cache-rate` `/cache-section` `/cache-config` `/cache-lang` 动态配置面板
|
|
54
55
|
- **子代理缓存查看**:`/cache-session` 自动扫描并列出子代理,选择一个即可切换面板显示其缓存统计,支持 `/cache-session-back` 返回主会话
|
|
55
56
|
- **已加载技能**:检测 session 中 LLM 调用 `skill` tool 的记录,展示已加载技能名及估算 Token 占用
|
|
57
|
+
- **底部状态栏**:输入框提示行单行显示 命中率(含趋势)· Tokens · 余额,关闭侧边栏也能随时看到缓存统计,可经 `/cache-section` 隐藏
|
|
56
58
|
|
|
57
59
|
---
|
|
58
60
|
|
|
@@ -105,9 +107,11 @@ npm install -g opencode-visual-cache@latest
|
|
|
105
107
|
| `/cache-session-back` | 返回主会话统计 | 从子代理缓存视图切回主会话 |
|
|
106
108
|
| `/cache-currency` | 切换货币单位 | 从列表选择货币(USD / CNY / EUR / JPY / GBP / KRW),自动填入默认汇率 |
|
|
107
109
|
| `/cache-rate` | 调整汇率乘数 | 输入自定义汇率(如 `7.2`),用于费用换算 |
|
|
108
|
-
| `/cache-section` | 开关区块与边框 | 独立控制 Token 明细 / 模型与定价 / 估算 Token 分布 / 已加载技能 / 面板边框的显隐 |
|
|
110
|
+
| `/cache-section` | 开关区块与边框 | 独立控制 Token 明细 / 模型与定价 / 估算 Token 分布 / 已加载技能 / 余额 / 底部状态栏 / 面板边框的显隐 |
|
|
109
111
|
| `/cache-config` | 查看当前配置 | 弹出当前货币、汇率、区块可见性状态 |
|
|
110
112
|
| `/cache-lang` | 切换显示语言 | 从列表选择中文或 English,界面即时切换,无需重启 |
|
|
113
|
+
| `/cache-balance` | 余额查询设置 | 选择余额提供商(菜单标注 Key 来源:用户 key / OpenCode / 未配置)/ 开关自动切换 |
|
|
114
|
+
| `/cache-balance-key` | 设置余额 API Key | 两步流程:选择提供商 → 输入 API Key |
|
|
111
115
|
|
|
112
116
|
<div align="center">
|
|
113
117
|
<img src="https://raw.githubusercontent.com/Hotakus/opencode-visual-cache/master/assets/splash_cmd.png" alt="斜杠命令" width="49%"></img>
|
|
@@ -141,11 +145,40 @@ npm install -g opencode-visual-cache@latest
|
|
|
141
145
|
- **模型与定价**:费用 / 提供商 / 模型名 / 单价
|
|
142
146
|
- **估算 Token 分布**:按角色拆分的 Token 估算
|
|
143
147
|
- **已加载技能**:session 中 LLM 实际调用过的 Skill 名及估算 Token 占用
|
|
148
|
+
- **余额**:当前提供商账户余额(多提供商 + 自动切换)
|
|
149
|
+
- **底部状态栏**:输入框提示行的 命中率 · Tokens · 余额 单行统计
|
|
144
150
|
|
|
145
151
|
通过 `/cache-section` 切换后即时生效,无需重启。此外,该命令还可以开关面板的**外边框**——关闭后内容会顶格显示,释放额外空间。
|
|
146
152
|
|
|
147
153
|
> **关于 Token 分布数值**:分布面板中"总计"为最后一次 API 调用的精确 token 数,"系统提示"/"用户"等分项为字符级 BPE 估算值。分项之和通常小于总计,差值主要来自 OpenCode 运行时注入的系统提示组成部分,包括环境信息、Skill 目录、工具 Schema 定义等(详见 [`system.ts`](https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/session/system.ts)、[`tools.ts`](https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/session/tools.ts))。这些内容不在 agent 配置的 `prompt` 字段中,因此插件无法估算,属于预期行为。
|
|
148
154
|
|
|
155
|
+
### 4.4 余额查询
|
|
156
|
+
|
|
157
|
+
面板支持显示多家 AI 提供商的账户余额。开启**自动切换**后,余额查询会跟随当前会话正在使用的模型提供商自动切换。
|
|
158
|
+
|
|
159
|
+
已支持余额查询的提供商:
|
|
160
|
+
|
|
161
|
+
| 提供商 | 余额查询端点 | 币种 | Key 前缀 | 状态 |
|
|
162
|
+
|--------|-------------|------|---------|------|
|
|
163
|
+
| DeepSeek | `https://api.deepseek.com/user/balance` | CNY / USD | `sk-` | ✅ 已支持 |
|
|
164
|
+
| SiliconFlow | `https://api.siliconflow.cn/v1/user/info` | CNY | `sk-` | ✅ 已支持 |
|
|
165
|
+
| OpenRouter | `https://openrouter.ai/api/v1/credits` | USD | `sk-or-` | ✅ 已支持 |
|
|
166
|
+
| Moonshot | `https://api.moonshot.cn/v1/users/me/balance` | CNY | `sk-` | ✅ 已支持 |
|
|
167
|
+
| 智谱 GLM | 待接入(社区逆向端点,非官方) | CNY | — | ⏳ 希望支持 |
|
|
168
|
+
| xAI | 待接入(需 Management Key + Team ID) | USD | — | ⏳ 希望支持 |
|
|
169
|
+
|
|
170
|
+
> **Key 来源**:优先使用 `/cache-balance-key` 手动配置的 Key;未手动配置时自动复用 OpenCode 已认证的凭据(`/connect` 配置的 provider)。两者都没有的提供商无法查询余额。
|
|
171
|
+
>
|
|
172
|
+
> **Key 存储**:手动配置的 API Key 明文保存于插件持久化 KV,请勿在共享设备上使用。
|
|
173
|
+
>
|
|
174
|
+
> **自动切换**:默认开启;手动选择提供商后自动关闭,可在 `/cache-balance` 中重新开启。自动切换按当前会话的模型提供商匹配,未配置 Key 的提供商被选中时显示「未配置」提示。
|
|
175
|
+
>
|
|
176
|
+
> **希望支持**:已调研确认具备可行性的候选提供商,尚未实现。智谱 GLM 仅有社区逆向的非官方端点(无稳定性保障)。
|
|
177
|
+
>
|
|
178
|
+
> **统计口径**:命中率 = 缓存读 /(新鲜输入 + 缓存读 + 缓存写),与业界(OpenAI / Anthropic / Bedrock)口径一致;明细中「未命中」= 新鲜输入 + 缓存写。底部栏的 Tokens 为输入侧总量(不含输出)。未单独报告缓存写的提供商(如 DeepSeek)自动退化为 hit/miss 口径。
|
|
179
|
+
>
|
|
180
|
+
> **余额显示**:侧边栏与底部栏共享同一份余额数据,两处显示一致。当前提供商不支持余额查询时,侧边栏显示提示、底部栏隐藏余额项。
|
|
181
|
+
|
|
149
182
|
---
|
|
150
183
|
|
|
151
184
|
## 5. 更新
|
|
@@ -180,11 +213,11 @@ rm -rf ~/.cache/opencode/packages/opencode-visual-cache@latest
|
|
|
180
213
|
|
|
181
214
|
### 6.1 运行时切换(推荐)
|
|
182
215
|
|
|
183
|
-
在 TUI 中输入 `/cache-lang
|
|
216
|
+
在 TUI 中输入 `/cache-lang`,从弹窗选择 中文 / English / 日本語 / 한국어 即可即时切换,无需重启。偏好会自动持久化,下次启动优先恢复用户选择。
|
|
184
217
|
|
|
185
218
|
### 6.2 环境变量覆盖
|
|
186
219
|
|
|
187
|
-
启动前设置 `CACHE_TUI_LANG`
|
|
220
|
+
启动前设置 `CACHE_TUI_LANG` 环境变量可强制指定语言(`zh` / `en` / `ja` / `ko`):
|
|
188
221
|
|
|
189
222
|
```powershell
|
|
190
223
|
# Windows PowerShell
|
package/README_EN.md
CHANGED
|
@@ -48,11 +48,13 @@ Interested in sub-agent monitoring? Check out [opencode-subagent-magazine](https
|
|
|
48
48
|
- **Adaptive Colors**: ≥85% green · ≥70% orange · <70% red, auto-desaturated from current theme
|
|
49
49
|
- **Token Distribution**: Per-role (system / user / agent instr / tool call / tool result) estimated token breakdown
|
|
50
50
|
- **Persistent State**: Fold preferences and config remembered across restarts via api.kv
|
|
51
|
-
- **Language**:
|
|
51
|
+
- **Language**: Chinese / English / 日本語 / 한국어, auto-detects system locale, with `/cache-lang` for runtime switching — user preference takes priority over auto-detection
|
|
52
52
|
- **Multi-currency**: Switch via `/cache-currency` — costs, savings, and per-million rates convert in real time
|
|
53
|
+
- **Balance Query**: Query account balance across multiple AI providers, with auto-switch following the current session's provider
|
|
53
54
|
- **Slash Commands**: `/cache-session` `/cache-session-back` `/cache-rate` `/cache-section` `/cache-config` `/cache-lang` for live panel configuration
|
|
54
55
|
- **Sub-Agent Cache View**: `/cache-session` auto-scans and lists sub-agents; select one to switch the panel stats. Use `/cache-session-back` to return to the main session
|
|
55
56
|
- **Loaded Skills**: Detects `skill` tool calls in the session and displays loaded skill names with estimated token footprint
|
|
57
|
+
- **Bottom Status Bar**: single-line hit rate (with trend) · Tokens · Balance in the prompt hint row — visible even with the sidebar closed; hide it anytime via `/cache-section`
|
|
56
58
|
|
|
57
59
|
---
|
|
58
60
|
|
|
@@ -105,9 +107,11 @@ The plugin supports slash commands and command palette (`Ctrl + P`) for runtime
|
|
|
105
107
|
| `/cache-session-back` | Return to main session | Switch back to main session from sub-agent cache view |
|
|
106
108
|
| `/cache-currency` | Switch currency | Pick from a list (USD / CNY / EUR / JPY / GBP / KRW); default exchange rate auto-filled |
|
|
107
109
|
| `/cache-rate` | Adjust exchange rate | Enter a custom rate (e.g. `7.2` for CNY) |
|
|
108
|
-
| `/cache-section` | Toggle sections & border | Independently show/hide Detail, Model & Pricing, Token Distribution, Loaded Skills, or the panel border |
|
|
110
|
+
| `/cache-section` | Toggle sections & border | Independently show/hide Detail, Model & Pricing, Token Distribution, Loaded Skills, Balance, Bottom Bar, or the panel border |
|
|
109
111
|
| `/cache-config` | View current config | Displays currency, rate, and section visibility |
|
|
110
112
|
| `/cache-lang` | Switch display language | Pick Chinese or English from the dialog — takes effect immediately, no restart needed |
|
|
113
|
+
| `/cache-balance` | Balance query settings | Pick a balance provider (menu shows key source: user key / OpenCode / not set) / toggle auto-switch |
|
|
114
|
+
| `/cache-balance-key` | Set balance API key | Two-step flow: pick a provider → enter the API key |
|
|
111
115
|
|
|
112
116
|
<div align="center">
|
|
113
117
|
<img src="https://raw.githubusercontent.com/Hotakus/opencode-visual-cache/master/assets/splash_cmd.png" alt="Slash command" width="49%"></img>
|
|
@@ -141,9 +145,38 @@ Three sub-sections can be toggled independently to save sidebar space:
|
|
|
141
145
|
- **Model & Pricing**: cost / provider / model name / per-million rates
|
|
142
146
|
- **Estimated Token Dist.**: per-role token breakdown
|
|
143
147
|
- **Loaded Skills**: skill names the LLM actually loaded via the `skill` tool, with estimated token counts
|
|
148
|
+
- **Balance**: the selected provider's account balance (multi-provider with auto-switch)
|
|
149
|
+
- **Bottom Status Bar**: the single-line hit rate · Tokens · Balance stats in the prompt hint row
|
|
144
150
|
|
|
145
151
|
Toggled via `/cache-section` — takes effect instantly, no restart required. The same command also toggles the panel **border**; turning it off removes the outline and padding so content fills the full width.
|
|
146
152
|
|
|
153
|
+
### 4.4 Balance Query
|
|
154
|
+
|
|
155
|
+
The panel can display account balance from multiple AI providers. With **auto-switch** enabled, the balance query follows the model provider of the current session automatically.
|
|
156
|
+
|
|
157
|
+
Supported balance providers:
|
|
158
|
+
|
|
159
|
+
| Provider | Balance endpoint | Currency | Key prefix | Status |
|
|
160
|
+
|----------|-----------------|----------|------------|--------|
|
|
161
|
+
| DeepSeek | `https://api.deepseek.com/user/balance` | CNY / USD | `sk-` | ✅ Supported |
|
|
162
|
+
| SiliconFlow | `https://api.siliconflow.cn/v1/user/info` | CNY | `sk-` | ✅ Supported |
|
|
163
|
+
| OpenRouter | `https://openrouter.ai/api/v1/credits` | USD | `sk-or-` | ✅ Supported |
|
|
164
|
+
| Moonshot | `https://api.moonshot.cn/v1/users/me/balance` | CNY | `sk-` | ✅ Supported |
|
|
165
|
+
| Zhipu GLM | Pending (community-reversed endpoint, unofficial) | CNY | — | ⏳ Planned |
|
|
166
|
+
| xAI | Pending (requires Management Key + Team ID) | USD | — | ⏳ Planned |
|
|
167
|
+
|
|
168
|
+
> **Key source**: a key set manually via `/cache-balance-key` takes priority; otherwise the plugin reuses the credential OpenCode already authenticated (`/connect`-configured providers). Providers with neither cannot show a balance.
|
|
169
|
+
>
|
|
170
|
+
> **Key storage**: manually configured API keys are stored in plaintext in the plugin's persistent KV — avoid using on shared devices.
|
|
171
|
+
>
|
|
172
|
+
> **Auto-switch**: enabled by default; picking a provider manually disables it — re-enable anytime via `/cache-balance`. Auto-switch matches the current session's model provider; a provider without a key shows a "not set" hint when selected.
|
|
173
|
+
>
|
|
174
|
+
> **Planned**: candidates confirmed feasible by research, not yet implemented. Zhipu GLM only has a community-reversed unofficial endpoint (no stability guarantee).
|
|
175
|
+
>
|
|
176
|
+
> **Metric semantics**: hit rate = cache read / (fresh input + cache read + cache write), consistent with the industry (OpenAI / Anthropic / Bedrock). "Miss" in the detail view = fresh input + cache write. The bottom-bar Tokens is the input-side total (output excluded). Providers that do not report cache writes separately (e.g. DeepSeek) automatically fall back to the hit/miss formula.
|
|
177
|
+
>
|
|
178
|
+
> **Balance display**: the sidebar and bottom bar share the same balance data, so both show identical values. When the current provider has no balance adapter, the sidebar shows a hint and the bottom bar hides the balance segment.
|
|
179
|
+
|
|
147
180
|
---
|
|
148
181
|
|
|
149
182
|
## 5. Update
|
|
@@ -178,11 +211,11 @@ The plugin provides three ways to control the display language, listed by priori
|
|
|
178
211
|
|
|
179
212
|
### 6.1 Runtime Switching (recommended)
|
|
180
213
|
|
|
181
|
-
Type `/cache-lang` in the TUI and select
|
|
214
|
+
Type `/cache-lang` in the TUI and select Chinese / English / 日本語 / 한국어 from the dialog. The panel switches immediately without restarting. Your preference is persisted and takes priority over auto-detection on the next launch.
|
|
182
215
|
|
|
183
216
|
### 6.2 Environment Variable Override
|
|
184
217
|
|
|
185
|
-
Set the `CACHE_TUI_LANG` environment variable before launching to force a specific language:
|
|
218
|
+
Set the `CACHE_TUI_LANG` environment variable before launching to force a specific language (`zh` / `en` / `ja` / `ko`):
|
|
186
219
|
|
|
187
220
|
```powershell
|
|
188
221
|
# Windows PowerShell
|
package/dist/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PLUGIN_VERSION = "1.
|
|
1
|
+
export declare const PLUGIN_VERSION = "1.6.0";
|
package/dist/_version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// auto-generated
|
|
2
|
-
export const PLUGIN_VERSION = "1.
|
|
2
|
+
export const PLUGIN_VERSION = "1.6.0";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** 归一化后的余额条目——显示层与具体 provider 解耦。 */
|
|
2
|
+
export interface BalanceEntry {
|
|
3
|
+
currency: string;
|
|
4
|
+
total: string;
|
|
5
|
+
}
|
|
6
|
+
/** provider 统一错误:message 即错误码(401/403/EMPTY/…),显示层直接展示。 */
|
|
7
|
+
export declare class BalanceError extends Error {
|
|
8
|
+
}
|
|
9
|
+
/** 可插拔的余额 provider 适配器。 */
|
|
10
|
+
export interface BalanceProvider {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
keyPlaceholder?: string;
|
|
14
|
+
fetchBalance(apiKey: string, signal?: AbortSignal): Promise<BalanceEntry[]>;
|
|
15
|
+
}
|
|
16
|
+
/** 已注册的 provider 列表(按需追加新适配器)。 */
|
|
17
|
+
export declare const balanceProviders: BalanceProvider[];
|
|
18
|
+
/** 按 id 取 provider;未知 id 回退到第一个。 */
|
|
19
|
+
export declare function getBalanceProvider(id: string): BalanceProvider;
|
|
20
|
+
/**
|
|
21
|
+
* 按 OpenCode providerID 匹配余额 provider。
|
|
22
|
+
* 先精确匹配,再按前缀匹配(如 moonshotai-cn → moonshot);未命中返回 undefined。
|
|
23
|
+
* 比较不区分大小写,容忍 providerID 的大小写变体。
|
|
24
|
+
*/
|
|
25
|
+
export declare function matchBalanceProvider(providerId: string): BalanceProvider | undefined;
|
|
26
|
+
/** key 脱敏:保留头 5 尾 5 字符,中间用 * 填充。 */
|
|
27
|
+
export declare function maskKey(k: string): string;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Balance providers — pluggable account-balance query adapters.
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
/** provider 统一错误:message 即错误码(401/403/EMPTY/…),显示层直接展示。 */
|
|
5
|
+
export class BalanceError extends Error {
|
|
6
|
+
}
|
|
7
|
+
const siliconflowProvider = {
|
|
8
|
+
id: "siliconflow",
|
|
9
|
+
name: "SiliconFlow",
|
|
10
|
+
keyPlaceholder: "sk-...",
|
|
11
|
+
async fetchBalance(apiKey, signal) {
|
|
12
|
+
// 国内站 api.siliconflow.cn(CNY);国际站为 api.siliconflow.com(USD)
|
|
13
|
+
const res = await fetch("https://api.siliconflow.cn/v1/user/info", {
|
|
14
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
15
|
+
signal,
|
|
16
|
+
});
|
|
17
|
+
if (!res.ok) {
|
|
18
|
+
if (res.status === 401)
|
|
19
|
+
throw new BalanceError("401");
|
|
20
|
+
if (res.status === 403)
|
|
21
|
+
throw new BalanceError("403");
|
|
22
|
+
throw new BalanceError(String(res.status));
|
|
23
|
+
}
|
|
24
|
+
const json = await res.json();
|
|
25
|
+
// totalBalance 为总余额(含充值+赠送),缺失时回退 balance
|
|
26
|
+
const total = json.data?.totalBalance ?? json.data?.balance;
|
|
27
|
+
if (typeof total === "undefined" || total === null)
|
|
28
|
+
throw new BalanceError("EMPTY");
|
|
29
|
+
return [{ currency: "CNY", total: String(total) }];
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const deepseekProvider = {
|
|
33
|
+
id: "deepseek",
|
|
34
|
+
name: "DeepSeek",
|
|
35
|
+
keyPlaceholder: "sk-...",
|
|
36
|
+
async fetchBalance(apiKey, signal) {
|
|
37
|
+
const res = await fetch("https://api.deepseek.com/user/balance", {
|
|
38
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
39
|
+
signal,
|
|
40
|
+
});
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
if (res.status === 401)
|
|
43
|
+
throw new BalanceError("401");
|
|
44
|
+
if (res.status === 402 || res.status === 403)
|
|
45
|
+
throw new BalanceError("403");
|
|
46
|
+
throw new BalanceError(String(res.status));
|
|
47
|
+
}
|
|
48
|
+
const json = await res.json();
|
|
49
|
+
const infos = json.balance_infos ?? [];
|
|
50
|
+
if (infos.length === 0)
|
|
51
|
+
throw new BalanceError("EMPTY");
|
|
52
|
+
return infos.map((info) => ({
|
|
53
|
+
currency: info.currency ?? "CNY",
|
|
54
|
+
total: info.total_balance ?? "0",
|
|
55
|
+
}));
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const openrouterProvider = {
|
|
59
|
+
id: "openrouter",
|
|
60
|
+
name: "OpenRouter",
|
|
61
|
+
keyPlaceholder: "sk-or-...",
|
|
62
|
+
async fetchBalance(apiKey, signal) {
|
|
63
|
+
// 官方文档标注需 Management key,实测普通 API key 亦可查询账户余额
|
|
64
|
+
const res = await fetch("https://openrouter.ai/api/v1/credits", {
|
|
65
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
66
|
+
signal,
|
|
67
|
+
});
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
if (res.status === 401 || res.status === 403)
|
|
70
|
+
throw new BalanceError("403");
|
|
71
|
+
throw new BalanceError(String(res.status));
|
|
72
|
+
}
|
|
73
|
+
const json = await res.json();
|
|
74
|
+
const credits = json.data?.total_credits;
|
|
75
|
+
const usage = json.data?.total_usage;
|
|
76
|
+
if (typeof credits !== "number" || typeof usage !== "number")
|
|
77
|
+
throw new BalanceError("EMPTY");
|
|
78
|
+
// 剩余额度 = 充值总额 - 已用
|
|
79
|
+
return [{ currency: "USD", total: (credits - usage).toFixed(2) }];
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
const moonshotProvider = {
|
|
83
|
+
id: "moonshot",
|
|
84
|
+
name: "Moonshot",
|
|
85
|
+
keyPlaceholder: "sk-...",
|
|
86
|
+
async fetchBalance(apiKey, signal) {
|
|
87
|
+
// 国内站 api.moonshot.cn(CNY);国际站 api.moonshot.ai(USD)
|
|
88
|
+
const res = await fetch("https://api.moonshot.cn/v1/users/me/balance", {
|
|
89
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
90
|
+
signal,
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
if (res.status === 401)
|
|
94
|
+
throw new BalanceError("401");
|
|
95
|
+
if (res.status === 403)
|
|
96
|
+
throw new BalanceError("403");
|
|
97
|
+
throw new BalanceError(String(res.status));
|
|
98
|
+
}
|
|
99
|
+
const json = await res.json();
|
|
100
|
+
const balance = json.data?.available_balance;
|
|
101
|
+
if (typeof balance === "undefined" || balance === null)
|
|
102
|
+
throw new BalanceError("EMPTY");
|
|
103
|
+
return [{ currency: "CNY", total: String(balance) }];
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
/** 已注册的 provider 列表(按需追加新适配器)。 */
|
|
107
|
+
export const balanceProviders = [deepseekProvider, siliconflowProvider, openrouterProvider, moonshotProvider];
|
|
108
|
+
/** 按 id 取 provider;未知 id 回退到第一个。 */
|
|
109
|
+
export function getBalanceProvider(id) {
|
|
110
|
+
return balanceProviders.find((p) => p.id === id) ?? balanceProviders[0] ?? deepseekProvider;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 按 OpenCode providerID 匹配余额 provider。
|
|
114
|
+
* 先精确匹配,再按前缀匹配(如 moonshotai-cn → moonshot);未命中返回 undefined。
|
|
115
|
+
* 比较不区分大小写,容忍 providerID 的大小写变体。
|
|
116
|
+
*/
|
|
117
|
+
export function matchBalanceProvider(providerId) {
|
|
118
|
+
const id = providerId.toLowerCase();
|
|
119
|
+
const exact = balanceProviders.find((p) => p.id.toLowerCase() === id);
|
|
120
|
+
if (exact)
|
|
121
|
+
return exact;
|
|
122
|
+
return balanceProviders.find((p) => id.startsWith(p.id.toLowerCase()));
|
|
123
|
+
}
|
|
124
|
+
/** key 脱敏:保留头 5 尾 5 字符,中间用 * 填充。 */
|
|
125
|
+
export function maskKey(k) {
|
|
126
|
+
if (!k)
|
|
127
|
+
return "";
|
|
128
|
+
if (k.length <= 10)
|
|
129
|
+
return k.slice(0, 5) + "*".repeat(Math.max(3, k.length - 5));
|
|
130
|
+
return k.slice(0, 5) + "*".repeat(Math.max(3, k.length - 10)) + k.slice(-5);
|
|
131
|
+
}
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export type LangCode = "zh" | "en" | "ja" | "ko";
|
|
2
|
+
declare const ZH_T: {
|
|
3
|
+
readonly title: "缓存统计";
|
|
4
|
+
readonly hit: "命中率";
|
|
5
|
+
readonly totalHit: "总命中:";
|
|
6
|
+
readonly read: "缓存读:";
|
|
7
|
+
readonly write: "缓存写:";
|
|
8
|
+
readonly miss: "未命中:";
|
|
9
|
+
readonly out: "输出:";
|
|
10
|
+
readonly cost: "费用:";
|
|
11
|
+
readonly saved: "累计节省:";
|
|
12
|
+
readonly model: "模型:";
|
|
13
|
+
readonly provider: "提供商:";
|
|
14
|
+
readonly rate: "单价:";
|
|
15
|
+
readonly hitFolded: "命中";
|
|
16
|
+
readonly inputRate: "输入";
|
|
17
|
+
readonly cacheRate: "缓存";
|
|
18
|
+
readonly writeRate: "写入";
|
|
19
|
+
readonly noData: "等待缓存数据...";
|
|
20
|
+
readonly tok: "tok";
|
|
21
|
+
readonly distTitle: "估算 Token 分布";
|
|
22
|
+
readonly distSys: "系统提示:";
|
|
23
|
+
readonly distUser: "用户:";
|
|
24
|
+
readonly distAgent: "Agent 指令:";
|
|
25
|
+
readonly distTool: "Tool 调用:";
|
|
26
|
+
readonly distRes: "Tool 结果:";
|
|
27
|
+
readonly distTotal: "总计:";
|
|
28
|
+
readonly distOut: "输出:";
|
|
29
|
+
readonly secDetail: "明细";
|
|
30
|
+
readonly secModel: "模型";
|
|
31
|
+
readonly secSkills: "已加载技能";
|
|
32
|
+
readonly balTotal: "总余额:";
|
|
33
|
+
readonly balNoKey: "未配置 {p} API Key";
|
|
34
|
+
readonly balLoading: "查询中...";
|
|
35
|
+
readonly balError: "查询失败";
|
|
36
|
+
readonly balErr401: "API Key 无效";
|
|
37
|
+
readonly balErr403: "余额查询被拒绝";
|
|
38
|
+
readonly balErrEmpty: "未获取到余额数据";
|
|
39
|
+
readonly balErrTimeout: "查询超时";
|
|
40
|
+
readonly balUnsupported: "当前提供商不支持余额查询";
|
|
41
|
+
readonly barHit: "命中率";
|
|
42
|
+
readonly barBal: "余额";
|
|
43
|
+
readonly barTok: "Tokens";
|
|
44
|
+
readonly secToggle: "切换区块";
|
|
45
|
+
readonly secBalance: "余额";
|
|
46
|
+
readonly secBottom: "底部状态栏";
|
|
47
|
+
readonly secBorder: "面板边框";
|
|
48
|
+
readonly keySaved: "API Key 已保存,正在查询余额...";
|
|
49
|
+
readonly keyCleared: "API Key 已清除";
|
|
50
|
+
readonly currencySet: "币种: {v} ({s}), 汇率: {r}";
|
|
51
|
+
readonly rateSet: "汇率已设为 {r}";
|
|
52
|
+
readonly panelConfigTitle: "缓存面板配置";
|
|
53
|
+
readonly panelConfigMsg: "币种: {c} | 汇率: {r} | 明细: {d} | 模型: {m} | 分布: {t} | 技能: {k} | 余额: {b} | 底部: {f}";
|
|
54
|
+
readonly borderShown: "面板边框 已显示";
|
|
55
|
+
readonly borderHidden: "面板边框 已隐藏";
|
|
56
|
+
readonly sectionShown: "{s} 已显示";
|
|
57
|
+
readonly sectionHidden: "{s} 已隐藏";
|
|
58
|
+
readonly langSwitched: "语言已切换为中文";
|
|
59
|
+
readonly autoSwitchOn: "自动切换余额提供商: 开";
|
|
60
|
+
readonly autoSwitchOff: "自动切换余额提供商: 关";
|
|
61
|
+
readonly providerManual: "余额提供商: {p}(自动切换已关闭)";
|
|
62
|
+
readonly runInSession: "请在会话内运行此命令";
|
|
63
|
+
readonly backToMain: "已切回主会话";
|
|
64
|
+
readonly subAgentSwitched: "已切换至子代理: {s}";
|
|
65
|
+
readonly langTitle: "显示语言";
|
|
66
|
+
readonly subPrefix: "子代理: ";
|
|
67
|
+
readonly keyUser: "(用户 key)";
|
|
68
|
+
readonly keyOpenCode: "(OpenCode)";
|
|
69
|
+
readonly keyNotSet: "(未配置)";
|
|
70
|
+
readonly balKeyPrompt: "输入 {p} API Key 以显示账户余额(留空清除)";
|
|
71
|
+
readonly balProvTitle: "余额提供商 / 自动切换";
|
|
72
|
+
readonly autoSwitchOpt: "自动切换提供商";
|
|
73
|
+
readonly balSelectTitle: "选择余额提供商";
|
|
74
|
+
readonly backToMainTitle: "回到主会话";
|
|
75
|
+
readonly subSelectTitle: "选择子代理";
|
|
76
|
+
readonly subSwitchTitle: "切换子代理";
|
|
77
|
+
readonly subViewTitle: "查看子代理缓存";
|
|
78
|
+
readonly subNoFound: "未找到子代理,请手动粘贴 Session ID";
|
|
79
|
+
};
|
|
80
|
+
/** 结构约束:值放宽为 string,键集合来自中文表(新增语言缺 key 会编译报错)。 */
|
|
81
|
+
export type Translation = {
|
|
82
|
+
[K in keyof typeof ZH_T]: string;
|
|
83
|
+
};
|
|
84
|
+
export declare const LANGS: Record<LangCode, Translation>;
|
|
85
|
+
/** 语言元数据:/cache-lang 选项与自动检测共用。 */
|
|
86
|
+
export declare const LANG_META: {
|
|
87
|
+
code: LangCode;
|
|
88
|
+
label: string;
|
|
89
|
+
}[];
|
|
90
|
+
/**
|
|
91
|
+
* 模板参数替换:`{key}` 占位符统一在此处理。
|
|
92
|
+
* 未提供的参数保留原占位符,避免静默丢失。
|
|
93
|
+
*/
|
|
94
|
+
export declare function applyParams(tpl: string, params?: Record<string, string | number>): string;
|
|
95
|
+
/**
|
|
96
|
+
* 翻译查找函数工厂:`t("key")` 返回当前语言的文本,`t("key", { p })` 附带模板参数。
|
|
97
|
+
* `getCode` 读取语言信号——在 SolidJS 渲染/memo 上下文中调用时自动建立响应式依赖。
|
|
98
|
+
*/
|
|
99
|
+
export declare function createT(getCode: () => LangCode): (key: keyof Translation, params?: Record<string, string | number>) => string;
|
|
100
|
+
/** 按系统 locale 自动检测语言(zh → 中文,ja → 日语,ko → 韩语,其余 → 英文)。 */
|
|
101
|
+
export declare function detectLang(): LangCode;
|
|
102
|
+
export {};
|