@mars-sea/dsh-commandcode-provider 0.11.0 → 0.11.2
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 +25 -0
- package/README.md +6 -5
- package/README.zh-CN.md +6 -5
- package/lib/client.js +181 -188
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +64 -2
- package/lib/index.js +401 -21
- package/lib/index.js.map +1 -1
- package/package.json +42 -38
package/lib/client.js
CHANGED
|
@@ -36,150 +36,6 @@ window.__ModuleLoader__.load({
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
//#endregion
|
|
39
|
-
//#region src/command-locales.ts
|
|
40
|
-
const commandcodeCommand = {
|
|
41
|
-
zh: {
|
|
42
|
-
title: "📊 Command Code 用量{account}",
|
|
43
|
-
accountTitle: "📊 {label}{badges}",
|
|
44
|
-
accountSeparator: "────────────────────",
|
|
45
|
-
activeBadge: " ✅ 当前使用",
|
|
46
|
-
invalidCredentialBadge: " ⛔ 密钥无效",
|
|
47
|
-
cooldownBadge: " ⏳ 限额冷却中,重置 {when}",
|
|
48
|
-
rateLimitBadge: " ⏳ 已达限额(等待窗口探测)",
|
|
49
|
-
unconfigured: " (未配置 API 密钥)",
|
|
50
|
-
blockedInvalidKey: "⛔ API 密钥无效或已过期 — 服务端拒绝了全部请求(401),请检查该账户的密钥配置",
|
|
51
|
-
blockedServiceUnavailable: "⚠️ Command Code 服务暂时不可用(5xx),稍后重试",
|
|
52
|
-
blockedNetwork: "⚠️ 无法连接 Command Code 服务 — 请检查网络或 API 地址",
|
|
53
|
-
planLine: " 📦 套餐 {name}{status}{period}",
|
|
54
|
-
planPeriodSuffix: " · 账期截止 {date}",
|
|
55
|
-
usageHeader: "── 请求 ──────────────────────────────",
|
|
56
|
-
requestsLine: " 💬 请求 {n} 次 / 失败 {f} 成功率 {r}%",
|
|
57
|
-
costLine: " 💰 花费 {money} ({credits} credits)",
|
|
58
|
-
tokensLine: " 🔤 Token {in} 入 / {out} 出",
|
|
59
|
-
creditsHeader: "── 信用 ──────────────────────────────",
|
|
60
|
-
monthlyLine: " 💳 月额度 {monthly} (已购 {purchased} / 赠送 {free})",
|
|
61
|
-
barLine: " └ {bar} {pct}%",
|
|
62
|
-
windowsHeader: "── 窗口用量 ──────────────────────────",
|
|
63
|
-
fiveHourLine: " ⏱ 5 小时 {used} / {cap}{warn}",
|
|
64
|
-
weeklyLine: " 📅 每周 {used} / {cap}{warn}",
|
|
65
|
-
windowBarLine: " └ {bar} 重置 {when}",
|
|
66
|
-
exceededWarning: " ⚠️ 超限!",
|
|
67
|
-
resetSuffix: "重置 {when}",
|
|
68
|
-
partialFailures: "⚠️ 部分端点失败: {list}",
|
|
69
|
-
noData: "(无数据 — 请检查 API 密钥)",
|
|
70
|
-
errorText: "获取 Command Code 用量失败:{message}",
|
|
71
|
-
imageGate: "当前会话已包含图片,而模型 {model} 不支持图片输入;请选择支持图片的模型,或先移除会话中的图片。"
|
|
72
|
-
},
|
|
73
|
-
en: {
|
|
74
|
-
title: "📊 Command Code usage{account}",
|
|
75
|
-
accountTitle: "📊 {label}{badges}",
|
|
76
|
-
accountSeparator: "────────────────────",
|
|
77
|
-
activeBadge: " ✅ active",
|
|
78
|
-
invalidCredentialBadge: " ⛔ invalid key",
|
|
79
|
-
cooldownBadge: " ⏳ cooling down, resets {when}",
|
|
80
|
-
rateLimitBadge: " ⏳ rate-limited (waiting for window probe)",
|
|
81
|
-
unconfigured: " (no API key configured)",
|
|
82
|
-
blockedInvalidKey: "⛔ API key invalid or expired — the server rejected every request (401); check the key configured for this account",
|
|
83
|
-
blockedServiceUnavailable: "⚠️ Command Code service temporarily unavailable (5xx); try again later",
|
|
84
|
-
blockedNetwork: "⚠️ could not reach the Command Code service — check your network or the API base setting",
|
|
85
|
-
planLine: " 📦 Plan {name}{status}{period}",
|
|
86
|
-
planPeriodSuffix: " · period ends {date}",
|
|
87
|
-
usageHeader: "── Requests ──────────────────────────",
|
|
88
|
-
requestsLine: " 💬 Requests {n} / failed {f} success rate {r}%",
|
|
89
|
-
costLine: " 💰 Spend {money} ({credits} credits)",
|
|
90
|
-
tokensLine: " 🔤 Tokens {in} in / {out} out",
|
|
91
|
-
creditsHeader: "── Credits ───────────────────────────",
|
|
92
|
-
monthlyLine: " 💳 Monthly {monthly} (purchased {purchased} / free {free})",
|
|
93
|
-
barLine: " └ {bar} {pct}%",
|
|
94
|
-
windowsHeader: "── Window usage ──────────────────────",
|
|
95
|
-
fiveHourLine: " ⏱ 5-hour {used} / {cap}{warn}",
|
|
96
|
-
weeklyLine: " 📅 Weekly {used} / {cap}{warn}",
|
|
97
|
-
windowBarLine: " └ {bar} resets {when}",
|
|
98
|
-
exceededWarning: " ⚠️ exceeded!",
|
|
99
|
-
resetSuffix: "resets {when}",
|
|
100
|
-
partialFailures: "⚠️ some endpoints failed: {list}",
|
|
101
|
-
noData: "(no data — check your API key)",
|
|
102
|
-
errorText: "Could not fetch Command Code usage: {message}",
|
|
103
|
-
imageGate: "This session already contains images, and model {model} does not accept image input; please select an image-capable model, or remove the images from the session first."
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
//#endregion
|
|
107
|
-
//#region src/client/sessions.ts
|
|
108
|
-
/**
|
|
109
|
-
* Friendly-error wrapper for the harness's image-session gate.
|
|
110
|
-
*
|
|
111
|
-
* The host rejects switching to a text-only model while the session already
|
|
112
|
-
* contains images with a `model-unavailable` error
|
|
113
|
-
* (`dsh-host-apiproxy`'s `session.selectModel` handler). That rejection is
|
|
114
|
-
* intentional and cannot be relaxed from the plugin side — the adapter's
|
|
115
|
-
* `inputModalities` is exactly what makes the guard work. What we CAN do is
|
|
116
|
-
* make the error message friendlier: wrap the shared
|
|
117
|
-
* `connection.api.sessions.selectModel` face so a `model-unavailable`
|
|
118
|
-
* rejection shows a clear, actionable hint (with the requested model name)
|
|
119
|
-
* instead of the raw English harness message.
|
|
120
|
-
*
|
|
121
|
-
* The wrapper is deliberately narrow: only the `model-unavailable` code is
|
|
122
|
-
* rewritten, only when the message matches the image-session gate, and only
|
|
123
|
-
* the message text changes — the error code and details pass through
|
|
124
|
-
* untouched so any caller that switches on `error.code` keeps working.
|
|
125
|
-
*
|
|
126
|
-
* The wire types are spelled structurally here (not imported from
|
|
127
|
-
* `@deepseek-ai/dsh-host-apiproxy`) so this client bundle does not drag an
|
|
128
|
-
* extra peer dependency into the package; the shapes are stable and the
|
|
129
|
-
* client build inlines them anyway.
|
|
130
|
-
*
|
|
131
|
-
* The wrapper takes a `getLocale` thunk because it is reached from a
|
|
132
|
-
* non-React path that has no `t` in scope; the supplied thunk reads the
|
|
133
|
-
* active locale at call time (typically `() => ctx.locale.getLocale().active`
|
|
134
|
-
* in the client entry), and the message template lives in the shared
|
|
135
|
-
* `commandcodeCommand` dictionary used by the Host-side `/commandcode`
|
|
136
|
-
* command — the same bilingual surface serves both.
|
|
137
|
-
*
|
|
138
|
-
* This module is deliberately free of React and other client-platform
|
|
139
|
-
* imports so the node test runner can exercise it directly.
|
|
140
|
-
*/
|
|
141
|
-
/** Whether a selectModel rejection is the harness's image-session gate. */
|
|
142
|
-
function isImageSessionRejection(result) {
|
|
143
|
-
return !result.result.ok && result.result.error.code === "model-unavailable" && result.result.error.message.includes("does not accept image input");
|
|
144
|
-
}
|
|
145
|
-
/** Wrap the shared sessions API so selectModel failures read friendlier. */
|
|
146
|
-
function withFriendlyImageError(sessions, getLocale) {
|
|
147
|
-
const selectModel = sessions.selectModel.bind(sessions);
|
|
148
|
-
return {
|
|
149
|
-
...sessions,
|
|
150
|
-
selectModel: async (payload, signal) => {
|
|
151
|
-
const result = await selectModel(payload, signal);
|
|
152
|
-
if (!isImageSessionRejection(result)) return result;
|
|
153
|
-
const model = result.result.error.details?.model ?? payload.model;
|
|
154
|
-
const template = commandcodeCommand[getLocale()].imageGate ?? commandcodeCommand.en.imageGate;
|
|
155
|
-
return {
|
|
156
|
-
...result,
|
|
157
|
-
result: {
|
|
158
|
-
...result.result,
|
|
159
|
-
error: {
|
|
160
|
-
...result.result.error,
|
|
161
|
-
message: template.replace("{model}", model)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Install the friendly-error wrapper when a legacy sessions face is present.
|
|
170
|
-
*
|
|
171
|
-
* @returns whether the wrapper was installed. The rewrite is only UX polish;
|
|
172
|
-
* a 0.1.2 connection has no `api.sessions`, and its absence must never block
|
|
173
|
-
* the plugin from mounting its settings, credential, or usage surfaces.
|
|
174
|
-
*/
|
|
175
|
-
function installFriendlyImageError(connection, getLocale) {
|
|
176
|
-
const api = connection.api;
|
|
177
|
-
const sessions = api?.sessions;
|
|
178
|
-
if (api === void 0 || sessions === void 0 || typeof sessions.selectModel !== "function") return false;
|
|
179
|
-
api.sessions = withFriendlyImageError(sessions, getLocale);
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
//#endregion
|
|
183
39
|
//#region src/client/settings.ts
|
|
184
40
|
/**
|
|
185
41
|
* Browser controller for the "Command Code" settings page.
|
|
@@ -303,6 +159,7 @@ window.__ModuleLoader__.load({
|
|
|
303
159
|
}),
|
|
304
160
|
booleanField$1("filterModelsByPlan"),
|
|
305
161
|
booleanField$1("webSearch"),
|
|
162
|
+
booleanField$1("showSidebarQuota"),
|
|
306
163
|
textField("activeAccount")
|
|
307
164
|
];
|
|
308
165
|
/** Whether two model-id lists are equal as sets (order-insensitive). */
|
|
@@ -455,6 +312,8 @@ window.__ModuleLoader__.load({
|
|
|
455
312
|
streamIdleTimeoutMs: this.field("streamIdleTimeoutMs"),
|
|
456
313
|
filterModelsByPlan: this.field("filterModelsByPlan"),
|
|
457
314
|
webSearch: this.field("webSearch"),
|
|
315
|
+
showSidebarQuota: this.field("showSidebarQuota"),
|
|
316
|
+
sidebarQuota: this.sectionValue("showSidebarQuota") === true,
|
|
458
317
|
activeAccount: this.field("activeAccount"),
|
|
459
318
|
accounts,
|
|
460
319
|
accountsRemoving: [...this.removedRefs],
|
|
@@ -2291,7 +2150,7 @@ window.__ModuleLoader__.load({
|
|
|
2291
2150
|
}
|
|
2292
2151
|
//#endregion
|
|
2293
2152
|
//#region package.json
|
|
2294
|
-
var version = "0.11.
|
|
2153
|
+
var version = "0.11.2";
|
|
2295
2154
|
var repository = {
|
|
2296
2155
|
"type": "git",
|
|
2297
2156
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -2491,7 +2350,8 @@ window.__ModuleLoader__.load({
|
|
|
2491
2350
|
"requestTimeoutMs",
|
|
2492
2351
|
"streamIdleTimeoutMs",
|
|
2493
2352
|
"filterModelsByPlan",
|
|
2494
|
-
"webSearch"
|
|
2353
|
+
"webSearch",
|
|
2354
|
+
"showSidebarQuota"
|
|
2495
2355
|
];
|
|
2496
2356
|
/** One labelled field row in the page body. */
|
|
2497
2357
|
function Field({ id, label, hint, state, disabled, numeric, placeholder, onEdit, onReset, t }) {
|
|
@@ -2543,11 +2403,12 @@ window.__ModuleLoader__.load({
|
|
|
2543
2403
|
return t("invalidNumber");
|
|
2544
2404
|
}
|
|
2545
2405
|
/**
|
|
2546
|
-
* The collapsed "Advanced" card: API base, working dir, both timeouts,
|
|
2547
|
-
*
|
|
2548
|
-
*
|
|
2549
|
-
*
|
|
2550
|
-
* visible (a number field's error
|
|
2406
|
+
* The collapsed "Advanced" card: API base, working dir, both timeouts, the
|
|
2407
|
+
* plan filter, the web-search backend and the sidebar quota card live here so
|
|
2408
|
+
* the page leads with the facts a user actually touches. Starts collapsed on
|
|
2409
|
+
* every visit; expands on demand. While collapsed, a badge names the
|
|
2410
|
+
* customized count so a nonzero override stays visible (a number field's error
|
|
2411
|
+
* blocks save and must be reachable).
|
|
2551
2412
|
*/
|
|
2552
2413
|
function AdvancedSection({ state, disabled, t, onEdit, onReset }) {
|
|
2553
2414
|
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
@@ -2654,6 +2515,17 @@ window.__ModuleLoader__.load({
|
|
|
2654
2515
|
onEdit: (text) => onEdit("webSearch", text),
|
|
2655
2516
|
onReset: () => onReset("webSearch"),
|
|
2656
2517
|
t
|
|
2518
|
+
}),
|
|
2519
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
2520
|
+
id: "cc-show-sidebar-quota",
|
|
2521
|
+
label: t("showSidebarQuota"),
|
|
2522
|
+
hint: t("showSidebarQuotaHint"),
|
|
2523
|
+
state: state.showSidebarQuota,
|
|
2524
|
+
disabled,
|
|
2525
|
+
defaultChecked: false,
|
|
2526
|
+
onEdit: (text) => onEdit("showSidebarQuota", text),
|
|
2527
|
+
onReset: () => onReset("showSidebarQuota"),
|
|
2528
|
+
t
|
|
2657
2529
|
})
|
|
2658
2530
|
]
|
|
2659
2531
|
}) : null,
|
|
@@ -3965,12 +3837,68 @@ window.__ModuleLoader__.load({
|
|
|
3965
3837
|
}
|
|
3966
3838
|
//#endregion
|
|
3967
3839
|
//#region src/client/panel-copy.ts
|
|
3968
|
-
/** The panel
|
|
3969
|
-
const
|
|
3840
|
+
/** The locale namespace both panel slots bind their `t` seat to. */
|
|
3841
|
+
const PANEL_LOCALE_NS = "panel.commandcode";
|
|
3842
|
+
/** The panel's Simplified Chinese string table (the key-set source of truth). */
|
|
3843
|
+
const PANEL_COPY_ZH = {
|
|
3844
|
+
nav: "Command Code",
|
|
3845
|
+
subtitle: "套餐、额度与配额窗口",
|
|
3846
|
+
refresh: "刷新",
|
|
3847
|
+
refreshing: "刷新中…",
|
|
3848
|
+
close: "关闭",
|
|
3849
|
+
closeHint: "返回会话",
|
|
3850
|
+
loading: "正在获取账户用量…",
|
|
3851
|
+
noKey: "尚未配置 API 密钥",
|
|
3852
|
+
noKeyHint: "在 设置 → Command Code 中粘贴密钥或登录后,点击刷新。",
|
|
3853
|
+
plan: "套餐",
|
|
3854
|
+
credits: "额度",
|
|
3855
|
+
limits: "配额窗口",
|
|
3856
|
+
usage: "用量",
|
|
3857
|
+
monthly: "月额度",
|
|
3858
|
+
monthlyLimit: "月额度上限",
|
|
3859
|
+
monthlyUsed: "本月已用",
|
|
3860
|
+
remaining: "剩余",
|
|
3861
|
+
purchased: "已购",
|
|
3862
|
+
free: "赠送",
|
|
3863
|
+
fiveHour: "5 小时窗口",
|
|
3864
|
+
weekly: "每周窗口",
|
|
3865
|
+
fiveHourShort: "5 小时",
|
|
3866
|
+
weeklyShort: "每周",
|
|
3867
|
+
windowUnlimited: "不限",
|
|
3868
|
+
exceeded: "已超限",
|
|
3869
|
+
exhausted: "已用尽",
|
|
3870
|
+
resets: "重置于",
|
|
3871
|
+
requests: "请求",
|
|
3872
|
+
failed: "失败",
|
|
3873
|
+
successRate: "成功率",
|
|
3874
|
+
spend: "花费",
|
|
3875
|
+
tokens: "Token",
|
|
3876
|
+
tokensIn: "入",
|
|
3877
|
+
tokensOut: "出",
|
|
3878
|
+
periodEnds: "账期截止",
|
|
3879
|
+
updated: "更新于",
|
|
3880
|
+
partial: "部分端点数据不可用",
|
|
3881
|
+
active: "当前使用",
|
|
3882
|
+
coolingDown: "限额冷却中",
|
|
3883
|
+
invalidKey: "密钥无效",
|
|
3884
|
+
unconfigured: "未配置",
|
|
3885
|
+
unavailable: "无数据",
|
|
3886
|
+
errorInvalidKey: "API 密钥无效或已过期",
|
|
3887
|
+
errorInvalidKeyHint: "服务端拒绝了全部请求(401)。请检查该账户的密钥,或到 commandcode.ai 控制台重新生成。",
|
|
3888
|
+
errorServiceUnavailable: "Command Code 服务暂时不可用",
|
|
3889
|
+
errorServiceUnavailableHint: "服务端返回错误(5xx),请稍后点击刷新重试。",
|
|
3890
|
+
errorNetwork: "无法连接 Command Code 服务",
|
|
3891
|
+
errorNetworkHint: "所有请求都没有到达服务端。请检查网络连接或 API 地址设置。",
|
|
3892
|
+
errorGeneric: "用量获取失败"
|
|
3893
|
+
};
|
|
3894
|
+
/** The panel's English string table (must mirror {@link PANEL_COPY_ZH}). */
|
|
3895
|
+
const PANEL_COPY_EN = {
|
|
3970
3896
|
nav: "Command Code",
|
|
3971
3897
|
subtitle: "Plans, credits and quota windows",
|
|
3972
3898
|
refresh: "Refresh",
|
|
3973
3899
|
refreshing: "Refreshing…",
|
|
3900
|
+
close: "Close",
|
|
3901
|
+
closeHint: "Back to the conversation",
|
|
3974
3902
|
loading: "Loading account usage…",
|
|
3975
3903
|
noKey: "No API key configured",
|
|
3976
3904
|
noKeyHint: "Paste a key — or sign in — under Settings → Command Code, then refresh.",
|
|
@@ -4015,9 +3943,15 @@ window.__ModuleLoader__.load({
|
|
|
4015
3943
|
errorNetworkHint: "No request reached the server. Check your network connection or the API base setting.",
|
|
4016
3944
|
errorGeneric: "Could not fetch account usage"
|
|
4017
3945
|
};
|
|
4018
|
-
/**
|
|
4019
|
-
|
|
4020
|
-
|
|
3946
|
+
/** Every panel key, in the English table's declaration order. */
|
|
3947
|
+
const PANEL_KEYS = Object.keys(PANEL_COPY_EN);
|
|
3948
|
+
/**
|
|
3949
|
+
* The English translator. Used as the fallback when no locale seat is
|
|
3950
|
+
* available (a defensive path: every supported engine supplies one) and as the
|
|
3951
|
+
* default in tests that only assert the projection's figures.
|
|
3952
|
+
*/
|
|
3953
|
+
function panelTextEN(key) {
|
|
3954
|
+
return PANEL_COPY_EN[key] ?? key;
|
|
4021
3955
|
}
|
|
4022
3956
|
//#endregion
|
|
4023
3957
|
//#region src/client/panel.ts
|
|
@@ -4115,7 +4049,7 @@ window.__ModuleLoader__.load({
|
|
|
4115
4049
|
};
|
|
4116
4050
|
}
|
|
4117
4051
|
/** Build one account's view. */
|
|
4118
|
-
function accountView(entry) {
|
|
4052
|
+
function accountView(entry, t) {
|
|
4119
4053
|
const { report } = entry;
|
|
4120
4054
|
const account = report.account;
|
|
4121
4055
|
const plan = report.plan;
|
|
@@ -4126,7 +4060,7 @@ window.__ModuleLoader__.load({
|
|
|
4126
4060
|
stats.push({
|
|
4127
4061
|
label: "requests",
|
|
4128
4062
|
value: String(usage.completedCount),
|
|
4129
|
-
sub: `${usage.failedCount} ${
|
|
4063
|
+
sub: `${usage.failedCount} ${t("failed")}`
|
|
4130
4064
|
});
|
|
4131
4065
|
stats.push({
|
|
4132
4066
|
label: "successRate",
|
|
@@ -4141,7 +4075,7 @@ window.__ModuleLoader__.load({
|
|
|
4141
4075
|
stats.push({
|
|
4142
4076
|
label: "tokens",
|
|
4143
4077
|
value: formatTokensCompact(usage.totalTokensIn + usage.totalTokensOut),
|
|
4144
|
-
sub: `${formatTokensCompact(usage.totalTokensIn)} ${
|
|
4078
|
+
sub: `${formatTokensCompact(usage.totalTokensIn)} ${t("tokensIn")} / ${formatTokensCompact(usage.totalTokensOut)} ${t("tokensOut")}`
|
|
4145
4079
|
});
|
|
4146
4080
|
}
|
|
4147
4081
|
const windows = [];
|
|
@@ -4192,15 +4126,14 @@ window.__ModuleLoader__.load({
|
|
|
4192
4126
|
};
|
|
4193
4127
|
}
|
|
4194
4128
|
/**
|
|
4195
|
-
* Every panel string, resolved once per projection
|
|
4196
|
-
* (rather than per-field lookups in the components)
|
|
4197
|
-
* the render sites in lockstep: a key cannot be read
|
|
4198
|
-
* {@link
|
|
4129
|
+
* Every panel string, resolved once per projection through the translator.
|
|
4130
|
+
* One object with all keys (rather than per-field lookups in the components)
|
|
4131
|
+
* keeps the copy table and the render sites in lockstep: a key cannot be read
|
|
4132
|
+
* from `text` unless {@link PANEL_KEYS} declares it.
|
|
4199
4133
|
*/
|
|
4200
|
-
function panelStrings() {
|
|
4201
|
-
const keys = Object.keys(PANEL_COPY);
|
|
4134
|
+
function panelStrings(t) {
|
|
4202
4135
|
const out = {};
|
|
4203
|
-
for (const key of
|
|
4136
|
+
for (const key of PANEL_KEYS) out[key] = t(key);
|
|
4204
4137
|
return out;
|
|
4205
4138
|
}
|
|
4206
4139
|
/**
|
|
@@ -4212,6 +4145,7 @@ window.__ModuleLoader__.load({
|
|
|
4212
4145
|
*/
|
|
4213
4146
|
function buildPanelView(input) {
|
|
4214
4147
|
const { usage } = input;
|
|
4148
|
+
const t = input.t ?? panelTextEN;
|
|
4215
4149
|
const hidden = new Set(input.removingIds ?? []);
|
|
4216
4150
|
const seen = /* @__PURE__ */ new Set();
|
|
4217
4151
|
const entries = (usage.report?.accounts ?? []).filter((entry) => {
|
|
@@ -4220,7 +4154,7 @@ window.__ModuleLoader__.load({
|
|
|
4220
4154
|
seen.add(entry.id);
|
|
4221
4155
|
return true;
|
|
4222
4156
|
});
|
|
4223
|
-
const accounts = entries.map(accountView);
|
|
4157
|
+
const accounts = entries.map((entry) => accountView(entry, t));
|
|
4224
4158
|
const selectedEntry = entries.find((entry) => entry.active) ?? entries[0];
|
|
4225
4159
|
const selectedView = accounts.find((view) => view.id === selectedEntry?.id);
|
|
4226
4160
|
const footerBars = [];
|
|
@@ -4239,18 +4173,18 @@ window.__ModuleLoader__.load({
|
|
|
4239
4173
|
const totalCost = selectedEntry?.report.usage?.totalCost;
|
|
4240
4174
|
const cost = totalCost === void 0 ? "" : money(totalCost);
|
|
4241
4175
|
let status = "";
|
|
4242
|
-
if (entries.length > 0 && !entries.some((entry) => entry.configured)) status =
|
|
4243
|
-
else if (selectedView?.mark !== void 0 && selectedView.mark !== "active") status =
|
|
4244
|
-
else if (selectedEntry === void 0) status =
|
|
4245
|
-
const planName = selectedView !== void 0 && selectedView.planName !== "" ? selectedView.planName :
|
|
4246
|
-
const titleParts = planName ===
|
|
4176
|
+
if (entries.length > 0 && !entries.some((entry) => entry.configured)) status = t("unconfigured");
|
|
4177
|
+
else if (selectedView?.mark !== void 0 && selectedView.mark !== "active") status = t(selectedView.mark);
|
|
4178
|
+
else if (selectedEntry === void 0) status = t("unavailable");
|
|
4179
|
+
const planName = selectedView !== void 0 && selectedView.planName !== "" ? selectedView.planName : t("nav");
|
|
4180
|
+
const titleParts = planName === t("nav") ? [planName] : [t("nav"), planName];
|
|
4247
4181
|
for (const bar of footerBars) {
|
|
4248
4182
|
const figures = bar.detail === "" ? "" : ` ${bar.detail}`;
|
|
4249
|
-
titleParts.push(`${
|
|
4183
|
+
titleParts.push(`${t(bar.label)}${figures} (${bar.percent})`);
|
|
4250
4184
|
}
|
|
4251
|
-
if (cost !== "") titleParts.push(`${
|
|
4185
|
+
if (cost !== "") titleParts.push(`${t("spend")} ${cost}`);
|
|
4252
4186
|
return {
|
|
4253
|
-
text: panelStrings(),
|
|
4187
|
+
text: panelStrings(t),
|
|
4254
4188
|
planName,
|
|
4255
4189
|
status,
|
|
4256
4190
|
footerBars,
|
|
@@ -4336,11 +4270,11 @@ window.__ModuleLoader__.load({
|
|
|
4336
4270
|
* the sidebar footer card and the dashboard it opens in the center column.
|
|
4337
4271
|
*
|
|
4338
4272
|
* Both render one {@link PanelView} projected by `./panel.ts` — no fact is
|
|
4339
|
-
* derived here. Strings arrive
|
|
4340
|
-
*
|
|
4341
|
-
*
|
|
4342
|
-
*
|
|
4343
|
-
*
|
|
4273
|
+
* derived here. Strings arrive already localized in `view.text`: the slots
|
|
4274
|
+
* declare the `panel.commandcode` locale namespace, so the renderer hands this
|
|
4275
|
+
* component a `t` seat that is passed into `buildPanelView` — the panel follows
|
|
4276
|
+
* the harness's active language (a language switch mints a new `t`, so the
|
|
4277
|
+
* surfaces re-render without an extra channel).
|
|
4344
4278
|
*
|
|
4345
4279
|
* The footer card is the panel's home: the sidebar shell renders it in the foot
|
|
4346
4280
|
* area directly above the Settings seat, so each quota window's own spend and
|
|
@@ -4362,7 +4296,8 @@ window.__ModuleLoader__.load({
|
|
|
4362
4296
|
return buildPanelView({
|
|
4363
4297
|
usage,
|
|
4364
4298
|
apiKeyConfigured: settings.anyAccountConfigured,
|
|
4365
|
-
removingIds: settings.accountsRemoving
|
|
4299
|
+
removingIds: settings.accountsRemoving,
|
|
4300
|
+
t: props.t ?? panelTextEN
|
|
4366
4301
|
});
|
|
4367
4302
|
}
|
|
4368
4303
|
/**
|
|
@@ -4688,6 +4623,18 @@ window.__ModuleLoader__.load({
|
|
|
4688
4623
|
disabled: view.loading,
|
|
4689
4624
|
onClick: () => props.refresh(),
|
|
4690
4625
|
children: view.loading ? text("refreshing") : text("refresh")
|
|
4626
|
+
}),
|
|
4627
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4628
|
+
variant: "ghost",
|
|
4629
|
+
size: "sm",
|
|
4630
|
+
className: "ccp-close",
|
|
4631
|
+
"aria-label": text("close"),
|
|
4632
|
+
title: text("closeHint"),
|
|
4633
|
+
onClick: () => props.close(),
|
|
4634
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4635
|
+
"aria-hidden": "true",
|
|
4636
|
+
children: "×"
|
|
4637
|
+
})
|
|
4691
4638
|
})
|
|
4692
4639
|
]
|
|
4693
4640
|
}),
|
|
@@ -4773,11 +4720,23 @@ window.__ModuleLoader__.load({
|
|
|
4773
4720
|
* matching the shell's own rail geometry. `wide` comes from the shell as an
|
|
4774
4721
|
* owner prop — unlike the old `sidebar.panellist` row, this slot really does
|
|
4775
4722
|
* supply it.
|
|
4723
|
+
*
|
|
4724
|
+
* Rendering is gated on the settings page's `showSidebarQuota` toggle, which
|
|
4725
|
+
* defaults OFF: a fresh install shows no quota surface in the sidebar and runs
|
|
4726
|
+
* no background poll. The gate reads the STORED document (the settings
|
|
4727
|
+
* controller's `sidebarQuota` fact), so an unsaved draft cannot show the card,
|
|
4728
|
+
* while a landed save flips it live. The entry is still REGISTERED — this is a
|
|
4729
|
+
* render decision, not a slot registration, so nothing churns the slot ledger.
|
|
4776
4730
|
*/
|
|
4777
4731
|
function CommandCodeFooterEntry(props) {
|
|
4732
|
+
const visible = props.useCommandCodeSettings((snapshot) => snapshot.sidebarQuota);
|
|
4778
4733
|
const view = usePanelView(props);
|
|
4779
4734
|
const startAutoRefresh = props.startAutoRefresh;
|
|
4780
|
-
(0, react.useEffect)(() =>
|
|
4735
|
+
(0, react.useEffect)(() => {
|
|
4736
|
+
if (!visible) return void 0;
|
|
4737
|
+
return startAutoRefresh();
|
|
4738
|
+
}, [visible, startAutoRefresh]);
|
|
4739
|
+
if (!visible) return null;
|
|
4781
4740
|
const text = (key) => view.text[key] ?? key;
|
|
4782
4741
|
const headline = view.footerBars[0];
|
|
4783
4742
|
const title = view.footTitle;
|
|
@@ -5710,6 +5669,11 @@ window.__ModuleLoader__.load({
|
|
|
5710
5669
|
.ccp-subtitle{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}
|
|
5711
5670
|
.ccp-spacer{flex:1}
|
|
5712
5671
|
.ccp-meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.5;font-variant-numeric:tabular-nums}
|
|
5672
|
+
/* The dashboard's exit: an icon-sized glyph button. The primitive's own
|
|
5673
|
+
geometry is kept (size="sm"); only the glyph is enlarged and optically
|
|
5674
|
+
centred, so it reads as a dismiss control rather than a text button. */
|
|
5675
|
+
.ccp-close{min-width:28px;justify-content:center;padding-left:0;padding-right:0}
|
|
5676
|
+
.ccp-close span{font-size:16px;line-height:1}
|
|
5713
5677
|
.ccp-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
5714
5678
|
|
|
5715
5679
|
/* Notices: the no-key guidance, a blocked report, and a stale-data error. */
|
|
@@ -5787,7 +5751,7 @@ window.__ModuleLoader__.load({
|
|
|
5787
5751
|
streamIdleTimeoutMs: "流空闲超时(毫秒)",
|
|
5788
5752
|
streamIdleTimeoutMsHint: "生成流停滞多久视为断连;默认 300000(长思考模型可静默数分钟,默认值刻意放宽)。",
|
|
5789
5753
|
advancedSettings: "高级设置",
|
|
5790
|
-
advancedSettingsHint: "API
|
|
5754
|
+
advancedSettingsHint: "API 地址、工作目录、超时、模型过滤、联网搜索与侧边栏额度卡片等不常修改的选项。",
|
|
5791
5755
|
advancedOverriddenOne: "已自定义 1 项",
|
|
5792
5756
|
advancedOverriddenMany: "已自定义 {count} 项",
|
|
5793
5757
|
advancedInvalid: "高级设置中有未填好的数字,请展开修正后再保存。",
|
|
@@ -5795,6 +5759,8 @@ window.__ModuleLoader__.load({
|
|
|
5795
5759
|
filterModelsByPlanHint: "开启后,模型选择器只列出当前套餐可用的模型;账户持有按需余额时会显示全部。",
|
|
5796
5760
|
webSearch: "用 Command Code 承载联网搜索",
|
|
5797
5761
|
webSearchHint: "开启后,dsh 的 web_search 工具由 Command Code 承担(复用同一个 API key 与地址),并优先于其他搜索后端;关闭则把选择权交还给之前的后端(如 modsearch),而不是强制回退到 DeepSeek 搜索。",
|
|
5762
|
+
showSidebarQuota: "在侧边栏显示额度卡片",
|
|
5763
|
+
showSidebarQuotaHint: "开启后,侧边栏底部(Settings 上方)显示 Command Code 套餐与配额卡片,点击可在中间栏打开完整仪表盘。默认关闭:左侧不显示任何额度信息,也不会为其后台刷新用量。保存后立即生效。",
|
|
5798
5764
|
accountsTitle: "多账户轮换",
|
|
5799
5765
|
accountsHint: "当前账户达到用量限额(429)或密钥失效(401)时,请求自动切换到下一个账户;全部耗尽时会提示最早的重置时间。",
|
|
5800
5766
|
accountAdd: "添加账户",
|
|
@@ -5919,7 +5885,7 @@ window.__ModuleLoader__.load({
|
|
|
5919
5885
|
streamIdleTimeoutMs: "Stream idle timeout (ms)",
|
|
5920
5886
|
streamIdleTimeoutMsHint: "How long a stalled stream is treated as dead; default 300000 (deliberately generous — long-thinking models can stay silent for minutes).",
|
|
5921
5887
|
advancedSettings: "Advanced",
|
|
5922
|
-
advancedSettingsHint: "Rarely touched options: API base URL, working directory, timeouts, and
|
|
5888
|
+
advancedSettingsHint: "Rarely touched options: API base URL, working directory, timeouts, model filtering, web search, and the sidebar quota card.",
|
|
5923
5889
|
advancedOverriddenOne: "1 customized",
|
|
5924
5890
|
advancedOverriddenMany: "{count} customized",
|
|
5925
5891
|
advancedInvalid: "A number in Advanced settings is not ready to save; expand to fix it.",
|
|
@@ -5927,6 +5893,8 @@ window.__ModuleLoader__.load({
|
|
|
5927
5893
|
filterModelsByPlanHint: "When on, the model picker lists only models your subscription includes; any on-demand credit balance shows the full catalog.",
|
|
5928
5894
|
webSearch: "Serve dsh web search with Command Code",
|
|
5929
5895
|
webSearchHint: "When on, the model-facing web_search tool is backed by Command Code (same API key and base URL as chat), winning over other search backends. Off hands the selection back to the previous backend (e.g. modsearch) instead of forcing the shipped DeepSeek search.",
|
|
5896
|
+
showSidebarQuota: "Show the quota card in the sidebar",
|
|
5897
|
+
showSidebarQuotaHint: "When on, a Command Code plan & quota card sits at the bottom of the sidebar (above Settings) and opens the full dashboard in the centre column. Off by default: nothing is shown on the left, and no background usage poll runs for it. Applies on save.",
|
|
5930
5898
|
accountsTitle: "Account rotation",
|
|
5931
5899
|
accountsHint: "When the active account hits its usage limit (429) or its key fails (401), requests switch to the next account; when every account is exhausted the error names the earliest window reset.",
|
|
5932
5900
|
accountAdd: "Add account",
|
|
@@ -6199,6 +6167,15 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6199
6167
|
*/
|
|
6200
6168
|
const PANEL_ID = "commandcode-panel";
|
|
6201
6169
|
/**
|
|
6170
|
+
* ui-conversation's reserved `main` key, used only as the exit fallback for a
|
|
6171
|
+
* layout whose `selectPanel` predates the `null` "show the Conversation"
|
|
6172
|
+
* selection (see `close` in {@link applyClientSurfaces}). Declared locally for
|
|
6173
|
+
* the same reason as {@link PANEL_ID}: ui-conversation is not a dependency of
|
|
6174
|
+
* this bundle, and the key is a published contract of the layout (its README
|
|
6175
|
+
* names `conversation` as reserved for the Conversation).
|
|
6176
|
+
*/
|
|
6177
|
+
const CONVERSATION_PANEL_ID = "conversation";
|
|
6178
|
+
/**
|
|
6202
6179
|
* The composer figure's entry id in `conversation.composer.dock`. Its own id,
|
|
6203
6180
|
* not the shipped `stats` cell's: reusing `stats` would REPLACE the tokens /
|
|
6204
6181
|
* cache-hit / throughput readout rather than inject into it, and that readout
|
|
@@ -6213,12 +6190,15 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6213
6190
|
*/
|
|
6214
6191
|
function apply(ctx) {
|
|
6215
6192
|
injectPageCss();
|
|
6216
|
-
const connection = ctx.get("connection");
|
|
6217
|
-
if (connection !== void 0) installFriendlyImageError(connection, () => ctx.locale.getLocale().active === "zh" ? "zh" : "en");
|
|
6218
6193
|
ctx.effect(() => ctx.locale.register("settings.commandcode", {
|
|
6219
6194
|
zh,
|
|
6220
6195
|
en
|
|
6221
6196
|
}), "dsh-commandcode-provider: page copy");
|
|
6197
|
+
ctx.effect(() => ctx.locale.register(PANEL_LOCALE_NS, {
|
|
6198
|
+
zh: PANEL_COPY_ZH,
|
|
6199
|
+
en: PANEL_COPY_EN
|
|
6200
|
+
}), "dsh-commandcode-provider: panel copy");
|
|
6201
|
+
const connection = ctx.get("connection");
|
|
6222
6202
|
const legacyApi = adaptLegacyCredentials(connection?.api?.credentials);
|
|
6223
6203
|
if (legacyApi !== void 0) {
|
|
6224
6204
|
applyClientSurfaces(ctx, legacyApi, connection?.hostDescription);
|
|
@@ -6431,6 +6411,19 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6431
6411
|
open: () => {
|
|
6432
6412
|
const layout = ctx.get("layout");
|
|
6433
6413
|
if (typeof layout?.selectPanel === "function") layout.selectPanel(PANEL_ID);
|
|
6414
|
+
},
|
|
6415
|
+
close: () => {
|
|
6416
|
+
const layout = ctx.get("layout");
|
|
6417
|
+
if (typeof layout?.selectPanel !== "function") return;
|
|
6418
|
+
try {
|
|
6419
|
+
layout.selectPanel(null);
|
|
6420
|
+
} catch {
|
|
6421
|
+
try {
|
|
6422
|
+
layout.selectPanel(CONVERSATION_PANEL_ID);
|
|
6423
|
+
} catch (error) {
|
|
6424
|
+
console.error("[dsh-commandcode-provider] could not close the plans & quota panel:", error);
|
|
6425
|
+
}
|
|
6426
|
+
}
|
|
6434
6427
|
}
|
|
6435
6428
|
});
|
|
6436
6429
|
ctx.effect(() => injectPanelCss(), "dsh-commandcode-provider: panel styles");
|
|
@@ -6438,6 +6431,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6438
6431
|
ctx.slots.inject("main", () => ctx.slots.register({
|
|
6439
6432
|
name: "main",
|
|
6440
6433
|
key: PANEL_ID,
|
|
6434
|
+
locale: PANEL_LOCALE_NS,
|
|
6441
6435
|
inject: panelFace
|
|
6442
6436
|
}, CommandCodePanel));
|
|
6443
6437
|
} catch (error) {
|
|
@@ -6450,6 +6444,7 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6450
6444
|
name: "sidebar.footer.action",
|
|
6451
6445
|
id: PANEL_ID,
|
|
6452
6446
|
order: 1,
|
|
6447
|
+
locale: PANEL_LOCALE_NS,
|
|
6453
6448
|
inject: panelFace
|
|
6454
6449
|
}, CommandCodeFooterEntry));
|
|
6455
6450
|
} catch (error) {
|
|
@@ -6477,8 +6472,6 @@ select.cc-input{appearance:none;-webkit-appearance:none;-moz-appearance:none;box
|
|
|
6477
6472
|
//#endregion
|
|
6478
6473
|
exports.apply = apply;
|
|
6479
6474
|
exports.inject = inject;
|
|
6480
|
-
exports.isImageSessionRejection = isImageSessionRejection;
|
|
6481
|
-
exports.withFriendlyImageError = withFriendlyImageError;
|
|
6482
6475
|
return module.exports;
|
|
6483
6476
|
}
|
|
6484
6477
|
});
|