dsh-codearts 0.2.1 → 0.4.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/lib/bin.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { _ as CodeArtsAccountStore, c as fetchCallerIdentity, g as isBenefitModel, m as FALLBACK_CODEARTS_MODELS, n as codeartsUsagePath, r as discoverModels, t as CodeArtsUsageTracker, y as codeartsAccountsPath } from "./usage-ClRZWUS4.mjs";
2
+ import { C as codeartsAccountsPath, b as isBenefitModel, n as codeartsUsagePath, r as discoverModels, t as CodeArtsUsageTracker, u as fetchCallerIdentity, v as FALLBACK_CODEARTS_MODELS, x as CodeArtsAccountStore } from "./usage-3W88T_H1.mjs";
3
3
  import { existsSync, statSync } from "node:fs";
4
4
  //#region src/version.ts
5
- const CODEARTS_VERSION = "0.2.1";
5
+ const CODEARTS_VERSION = "0.3.0";
6
6
  //#endregion
7
7
  //#region src/bin.ts
8
8
  /**
@@ -23,8 +23,8 @@ const CODEARTS_VERSION = "0.2.1";
23
23
  * benefit and agent-center sources) instead of printing the hand-maintained seed —
24
24
  * that is the first thing to run when a model is missing from the picker.
25
25
  *
26
- * `usage` reads the plugin's own ledger of requests this machine sent. CodeArts has
27
- * no quota API, so it is not an account balance.
26
+ * `usage` reads the plugin's own ledger of requests this machine sent — not an
27
+ * account balance (account credits live in the card / `credits` route).
28
28
  *
29
29
  * `identity` asks the gateway which account each stored credential belongs to. The
30
30
  * login response carries no user id, so credentials used to be keyed by their DPoP
@@ -204,7 +204,7 @@ async function usage(jsonOutput) {
204
204
  process.stdout.write("today by account:\n");
205
205
  for (const [id, totals] of accounts) process.stdout.write(` - ${id.slice(0, 12)}… ${totals.requests} req in ${totals.promptTokens} / out ${totals.completionTokens}\n`);
206
206
  }
207
- process.stdout.write("note: CodeArts exposes no quota API; this counts requests this machine sent through the plugin\n");
207
+ process.stdout.write("note: this counts requests this machine sent through the plugin; account credits are in the card\n");
208
208
  return 0;
209
209
  }
210
210
  /**
@@ -19,6 +19,8 @@ interface CodeArtsSettingsKey {
19
19
  saveToken: string;
20
20
  expiredHint: string;
21
21
  accountsTitle: string;
22
+ accountsShow: string;
23
+ accountsHide: string;
22
24
  accountRemove: string;
23
25
  addAccount: string;
24
26
  accountsRotateHint: string;
@@ -55,6 +57,20 @@ interface CodeArtsSettingsKey {
55
57
  usageStateDisabled: string;
56
58
  usageNoQuota: string;
57
59
  usageEmpty: string;
60
+ creditsTitle: string;
61
+ creditsRemaining: string;
62
+ creditsUsed: string;
63
+ creditsExpiring: string;
64
+ creditsReset: string;
65
+ creditsTokensToday: string;
66
+ creditsTokensBalance: string;
67
+ creditsUnavailable: string;
68
+ creditsDock: string;
69
+ creditsDockTitle: string;
70
+ claimButton: string;
71
+ claiming: string;
72
+ claimSuccess: string;
73
+ claimFailed: string;
58
74
  [key: string]: string;
59
75
  }
60
76
  //#endregion
@@ -8,7 +8,7 @@ let react = require("react");
8
8
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
10
  //#region \0dsh-css:F:\centos\dsh-codearts\src\client\CodeArtsPluginCard.module.css.mjs
11
- const css = ".DZPfya_card{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-settings-card-fill);max-width:720px}.DZPfya_header{align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.DZPfya_headText{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.DZPfya_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}.DZPfya_desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.DZPfya_statusLine{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.DZPfya_meta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.DZPfya_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 16px}.DZPfya_section{margin-top:16px}.DZPfya_sectionHead{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.DZPfya_sectionLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500}.DZPfya_count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:12px}.DZPfya_hint{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px;line-height:20px}.DZPfya_error{color:var(--dsw-alias-state-error-primary);margin:10px 0 0;font-size:13px;line-height:20px}.DZPfya_actions{flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;display:flex}.DZPfya_danger{color:var(--dsw-alias-state-error-primary)}.DZPfya_link{appearance:none;color:var(--dsw-alias-brand-text);font:inherit;text-align:left;word-break:break-all;cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}.DZPfya_link:hover{text-decoration:underline}.DZPfya_choices{flex-direction:column;gap:2px;margin-top:8px;display:flex}.DZPfya_choiceRow{border-radius:var(--dsw-radius-sm);align-items:center;gap:8px;margin:0 -6px;padding:5px 6px;display:flex}.DZPfya_choiceRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.DZPfya_check{flex:1;min-width:0}.DZPfya_check>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.DZPfya_manual{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:0 12px}.DZPfya_manual[open]{padding-bottom:12px}.DZPfya_manualSummary{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;padding:9px 0;font-size:13px;list-style:none;display:flex}.DZPfya_manualSummary::-webkit-details-marker{display:none}.DZPfya_field{flex-direction:column;gap:5px;margin-top:10px;display:flex}.DZPfya_fieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px}.DZPfya_area{box-sizing:border-box;resize:vertical;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:20px}.DZPfya_area::placeholder{color:var(--dsw-alias-label-dimmed)}.DZPfya_area:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}";
11
+ const css = ".DZPfya_card{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-settings-card-fill);max-width:720px}.DZPfya_header{align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.DZPfya_headText{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.DZPfya_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}.DZPfya_desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.DZPfya_statusLine{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.DZPfya_meta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.DZPfya_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 16px}.DZPfya_section{margin-top:16px}.DZPfya_sectionHead{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.DZPfya_sectionLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500}.DZPfya_headActions{flex:none;align-items:center;gap:8px;display:flex}.DZPfya_count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:12px}.DZPfya_hint{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px;line-height:20px}.DZPfya_error{color:var(--dsw-alias-state-error-primary);margin:10px 0 0;font-size:13px;line-height:20px}.DZPfya_actions{flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;display:flex}.DZPfya_danger{color:var(--dsw-alias-state-error-primary)}.DZPfya_link{appearance:none;color:var(--dsw-alias-brand-text);font:inherit;text-align:left;word-break:break-all;cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}.DZPfya_link:hover{text-decoration:underline}.DZPfya_choices{flex-direction:column;gap:2px;margin-top:8px;display:flex}.DZPfya_choiceRow{border-radius:var(--dsw-radius-sm);align-items:center;gap:8px;margin:0 -6px;padding:5px 6px;display:flex}.DZPfya_choiceRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.DZPfya_check{flex:1;min-width:0}.DZPfya_check>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.DZPfya_manual{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:0 12px}.DZPfya_manual[open]{padding-bottom:12px}.DZPfya_manualSummary{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;padding:9px 0;font-size:13px;list-style:none;display:flex}.DZPfya_manualSummary::-webkit-details-marker{display:none}.DZPfya_field{flex-direction:column;gap:5px;margin-top:10px;display:flex}.DZPfya_fieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px}.DZPfya_area{box-sizing:border-box;resize:vertical;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:20px}.DZPfya_area::placeholder{color:var(--dsw-alias-label-dimmed)}.DZPfya_area:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}";
12
12
  const tagId = "dsh-codearts/CodeArtsPluginCard.module.css";
13
13
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
14
14
  const tag = document.createElement("style");
@@ -18,31 +18,32 @@ if (typeof document !== "undefined" && document.querySelector("style[data-plugin
18
18
  document.head.appendChild(tag);
19
19
  }
20
20
  var CodeArtsPluginCard_module_css_default = {
21
- "body": "DZPfya_body",
22
- "name": "DZPfya_name",
23
- "manual": "DZPfya_manual",
24
- "manualSummary": "DZPfya_manualSummary",
25
- "statusLine": "DZPfya_statusLine",
21
+ "choices": "DZPfya_choices",
26
22
  "actions": "DZPfya_actions",
27
- "sectionLabel": "DZPfya_sectionLabel",
23
+ "choiceRow": "DZPfya_choiceRow",
24
+ "header": "DZPfya_header",
25
+ "manualSummary": "DZPfya_manualSummary",
28
26
  "field": "DZPfya_field",
29
- "count": "DZPfya_count",
30
- "danger": "DZPfya_danger",
27
+ "meta": "DZPfya_meta",
28
+ "link": "DZPfya_link",
31
29
  "area": "DZPfya_area",
32
- "error": "DZPfya_error",
30
+ "headActions": "DZPfya_headActions",
33
31
  "hint": "DZPfya_hint",
32
+ "statusLine": "DZPfya_statusLine",
33
+ "check": "DZPfya_check",
34
+ "manual": "DZPfya_manual",
34
35
  "desc": "DZPfya_desc",
35
- "meta": "DZPfya_meta",
36
- "choiceRow": "DZPfya_choiceRow",
37
- "choices": "DZPfya_choices",
38
- "sectionHead": "DZPfya_sectionHead",
39
36
  "fieldLabel": "DZPfya_fieldLabel",
37
+ "body": "DZPfya_body",
38
+ "section": "DZPfya_section",
40
39
  "card": "DZPfya_card",
41
- "link": "DZPfya_link",
42
- "header": "DZPfya_header",
40
+ "name": "DZPfya_name",
41
+ "sectionHead": "DZPfya_sectionHead",
42
+ "sectionLabel": "DZPfya_sectionLabel",
43
+ "count": "DZPfya_count",
43
44
  "headText": "DZPfya_headText",
44
- "section": "DZPfya_section",
45
- "check": "DZPfya_check"
45
+ "error": "DZPfya_error",
46
+ "danger": "DZPfya_danger"
46
47
  };
47
48
  //#endregion
48
49
  //#region src/client/CodeArtsPluginCard.tsx
@@ -59,8 +60,15 @@ const TRAILING_FREE = /\s*[((]\s*免费\s*[))]\s*$/;
59
60
  function modelName(choice) {
60
61
  return choice.free === true ? choice.name.replace(TRAILING_FREE, "") : choice.name;
61
62
  }
62
- /** 官方用量明细页(CodeArts 没有额度接口,只能给链接)。 */
63
+ /** 官方用量明细页。 */
63
64
  const OFFICIAL_USAGE_URL = "https://codearts.huaweicloud.com/portal/settings/personal-usage";
65
+ /**
66
+ * 账号身份默认遮蔽后的占位符。
67
+ *
68
+ * 卡片经常被截图贴进问题反馈,账号名(label / userName / id)与获取、过期时间
69
+ * 没有出现在截图里的必要,所以默认遮住,点一下才展开。
70
+ */
71
+ const ACCOUNT_MASK = "*******";
64
72
  /** token 数按人类习惯缩写:12345 -> 12.3k。 */
65
73
  function formatTokens(value) {
66
74
  if (!Number.isFinite(value)) return "0";
@@ -71,13 +79,133 @@ function formatTokens(value) {
71
79
  function formatCount(value) {
72
80
  return Number.isFinite(value) ? String(Math.round(value)) : "0";
73
81
  }
82
+ /** 积分/token 千分位格式化(2000 → "2,000")。 */
83
+ function formatCredits(value) {
84
+ return Number.isFinite(value) ? new Intl.NumberFormat().format(Math.round(value)) : "0";
85
+ }
86
+ /**
87
+ * 积分区块。
88
+ *
89
+ * 2026-10 从官方 CodeArts Agent 客户端挖出积分接口并实测通过(见 src/credits.ts),
90
+ * 所以这里能直接显示账号总额度 —— 与下面的「本机用量」互补:一个是账号级、一个是
91
+ * 本机级。拉不到时显示原因(creditsError),不静默吞掉。
92
+ */
93
+ function CreditsSection({ credits, error, t, onClaimed }) {
94
+ const [claimState, setClaimState] = (0, react.useState)(() => {
95
+ try {
96
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
97
+ if (typeof localStorage !== "undefined" && localStorage.getItem("codearts-claim-date") === today) return "success";
98
+ } catch {}
99
+ return "idle";
100
+ });
101
+ const [claimMessage, setClaimMessage] = (0, react.useState)("");
102
+ if (credits === void 0 && error === void 0) return null;
103
+ const handleClaim = async () => {
104
+ setClaimState("loading");
105
+ try {
106
+ const res = await fetch("/api/codearts/claim", {
107
+ method: "POST",
108
+ headers: { Accept: "application/json" },
109
+ credentials: "same-origin"
110
+ });
111
+ const data = await res.json().catch(() => void 0);
112
+ if (res.ok && data?.ok === true) {
113
+ setClaimState("success");
114
+ try {
115
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
116
+ if (typeof localStorage !== "undefined") localStorage.setItem("codearts-claim-date", today);
117
+ } catch {}
118
+ onClaimed?.();
119
+ } else {
120
+ setClaimState("error");
121
+ setClaimMessage(data?.message ?? `HTTP ${res.status}`);
122
+ }
123
+ } catch (err) {
124
+ setClaimState("error");
125
+ setClaimMessage(err instanceof Error ? err.message : String(err));
126
+ }
127
+ };
128
+ const c = credits?.credits;
129
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
130
+ className: CodeArtsPluginCard_module_css_default.section,
131
+ children: [
132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
133
+ className: CodeArtsPluginCard_module_css_default.sectionHead,
134
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
135
+ className: CodeArtsPluginCard_module_css_default.sectionLabel,
136
+ children: t("creditsTitle")
137
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
138
+ className: CodeArtsPluginCard_module_css_default.headActions,
139
+ children: [c !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
140
+ className: CodeArtsPluginCard_module_css_default.count,
141
+ children: t("creditsRemaining", {
142
+ remain: formatCredits(c.total.remain),
143
+ total: formatCredits(c.total.total)
144
+ })
145
+ }), onClaimed !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
146
+ variant: "outline",
147
+ size: "sm",
148
+ disabled: claimState === "loading" || claimState === "success",
149
+ onClick: () => void handleClaim(),
150
+ children: claimState === "loading" ? t("claiming") : claimState === "success" ? t("claimSuccess") : t("claimButton")
151
+ })]
152
+ })]
153
+ }),
154
+ error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
155
+ className: CodeArtsPluginCard_module_css_default.hint,
156
+ children: t("creditsUnavailable", { message: error })
157
+ }),
158
+ claimState === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
159
+ className: CodeArtsPluginCard_module_css_default.hint,
160
+ children: t("claimFailed", { message: claimMessage })
161
+ }),
162
+ c !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
163
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
164
+ className: CodeArtsPluginCard_module_css_default.choiceRow,
165
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
166
+ className: CodeArtsPluginCard_module_css_default.name,
167
+ children: c.planName
168
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
169
+ className: CodeArtsPluginCard_module_css_default.meta,
170
+ children: t("creditsUsed", { used: formatCredits(c.total.used) })
171
+ })]
172
+ }),
173
+ c.periodEnd !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
174
+ className: CodeArtsPluginCard_module_css_default.choiceRow,
175
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
176
+ className: CodeArtsPluginCard_module_css_default.name,
177
+ children: t("creditsReset", { date: c.periodEnd })
178
+ }), c.total.expiring > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
179
+ className: CodeArtsPluginCard_module_css_default.meta,
180
+ children: t("creditsExpiring", {
181
+ expiring: formatCredits(c.total.expiring),
182
+ date: c.periodEnd
183
+ })
184
+ })]
185
+ }),
186
+ credits?.tokens !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
187
+ className: CodeArtsPluginCard_module_css_default.choiceRow,
188
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
189
+ className: CodeArtsPluginCard_module_css_default.name,
190
+ children: t("creditsTokensToday", {
191
+ used: formatCredits(credits.tokens.dailyUsed),
192
+ limit: formatCredits(credits.tokens.dailyLimit)
193
+ })
194
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
195
+ className: CodeArtsPluginCard_module_css_default.meta,
196
+ children: t("creditsTokensBalance", { balance: formatCredits(credits.tokens.balance) })
197
+ })]
198
+ })
199
+ ] })
200
+ ]
201
+ });
202
+ }
74
203
  /**
75
204
  * 用量区块。
76
205
  *
77
- * 只展示**本机经插件发出**的请求:上游没有额度/积分接口(README 与 codearts2api
78
- * 都是这个结论),所以这里明确写出这一点并给出官方明细页链接,别让用户误以为
79
- * 这是账号总额度。会话槽位是真事实 —— 每账号 3 个并发会话、被占时要等约 52 秒
80
- * 才释放,这也正是「打断后重新提问要等」的原因。
206
+ * 只展示**本机经插件发出**的请求(不等于账号总额度 —— 账号额度见上方积分区块);
207
+ * 会话槽位是真事实 —— 每账号 3 个并发会话、被占时要等约 52 秒才释放,这也正是
208
+ * 「打断后重新提问要等」的原因。
81
209
  */
82
210
  function UsageSection({ usage, t }) {
83
211
  const total = usage.today.requests;
@@ -198,6 +326,8 @@ function CodeArtsPluginCard({ t }) {
198
326
  const [savingModels, setSavingModels] = (0, react.useState)(false);
199
327
  const [savedModels, setSavedModels] = (0, react.useState)(false);
200
328
  const [modelError, setModelError] = (0, react.useState)(null);
329
+ /** 账号身份是否展开:默认遮蔽(false = 显示 *******)。 */
330
+ const [accountsRevealed, setAccountsRevealed] = (0, react.useState)(false);
201
331
  async function refresh() {
202
332
  try {
203
333
  const data = await (await fetch("/api/codearts/status", { headers: { Accept: "application/json" } })).json();
@@ -357,6 +487,8 @@ function CodeArtsPluginCard({ t }) {
357
487
  const expired = status?.status === "expired";
358
488
  const accounts = status?.accounts ?? [];
359
489
  const accountName = (account) => account.label?.trim() || account.userName?.trim() || account.id;
490
+ /** 遮蔽态下账号身份一律换成占位符:原值连 DOM 都不进。 */
491
+ const reveal = (value) => accountsRevealed ? value : ACCOUNT_MASK;
360
492
  /**
361
493
  * 每个账号的「获取时间 + 过期状态」。同一账号重复登录可能留下多条记录(上游不给
362
494
  * 稳定身份时的妥协),把时间摆出来用户才能一眼认出哪条是旧的、放心删。
@@ -373,7 +505,7 @@ function CodeArtsPluginCard({ t }) {
373
505
  const statusText = signedIn ? t("statusSignedIn") : expired ? t("statusExpired") : t("statusSignedOut");
374
506
  const storedTime = formatStored(status?.storedAt);
375
507
  const storedLabel = status?.label?.trim() ?? "";
376
- const meta = !signedIn || storedTime === "" ? "" : storedLabel === "" ? t("storedMetaNoLabel", { time: storedTime }) : t("storedMeta", {
508
+ const meta = !signedIn || storedTime === "" ? "" : storedLabel === "" || !accountsRevealed ? t("storedMetaNoLabel", { time: storedTime }) : t("storedMeta", {
377
509
  label: storedLabel,
378
510
  time: storedTime
379
511
  });
@@ -419,9 +551,18 @@ function CodeArtsPluginCard({ t }) {
419
551
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
420
552
  className: CodeArtsPluginCard_module_css_default.sectionLabel,
421
553
  children: t("accountsTitle")
422
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
423
- className: CodeArtsPluginCard_module_css_default.count,
424
- children: accounts.length
554
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
555
+ className: CodeArtsPluginCard_module_css_default.headActions,
556
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
557
+ className: CodeArtsPluginCard_module_css_default.count,
558
+ children: accounts.length
559
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
560
+ variant: "outline",
561
+ size: "sm",
562
+ "aria-pressed": accountsRevealed,
563
+ onClick: () => setAccountsRevealed((revealed) => !revealed),
564
+ children: accountsRevealed ? t("accountsHide") : t("accountsShow")
565
+ })]
425
566
  })]
426
567
  }),
427
568
  accounts.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -435,11 +576,11 @@ function CodeArtsPluginCard({ t }) {
435
576
  children: [
436
577
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
437
578
  className: CodeArtsPluginCard_module_css_default.name,
438
- children: accountName(account)
579
+ children: reveal(accountName(account))
439
580
  }),
440
- meta !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
581
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
441
582
  className: CodeArtsPluginCard_module_css_default.meta,
442
- children: meta
583
+ children: reveal(meta)
443
584
  }),
444
585
  account.stale && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, { children: t("accountNeedsSignIn") }),
445
586
  !account.signable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, { children: t("accountNoKey") }),
@@ -636,6 +777,12 @@ function CodeArtsPluginCard({ t }) {
636
777
  className: CodeArtsPluginCard_module_css_default.hint,
637
778
  children: t("modelsLoading")
638
779
  }),
780
+ signedIn && (status?.credits !== void 0 || status?.creditsError !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreditsSection, {
781
+ credits: status?.credits,
782
+ error: status?.creditsError,
783
+ t,
784
+ onClaimed: () => void refresh()
785
+ }),
639
786
  status?.usage !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageSection, {
640
787
  usage: status.usage,
641
788
  t
@@ -645,6 +792,94 @@ function CodeArtsPluginCard({ t }) {
645
792
  });
646
793
  }
647
794
  //#endregion
795
+ //#region src/client/CodeArtsCreditDock.tsx
796
+ /**
797
+ * 对话底部的积分条。
798
+ *
799
+ * 用户最初的诉求就是「对话底部要显示积分」—— 这一条挂在
800
+ * `conversation.composer.dock` 槽位上(与宿主 session-stats strip 同位)。
801
+ * 只在当前 provider 是 codearts 时才拉取并显示一行「积分 N」,别的 provider 下
802
+ * 完全不渲染,不抢别人的行。
803
+ *
804
+ * 积分数据走插件自己的 `/api/codearts/status`(带 TTL 缓存,见 src/credits.ts),
805
+ * 不会因为这条 dock 让上游多挨一次请求。
806
+ */
807
+ /** 与 src/adapter.ts 的 CODEARTS_PROVIDER 对齐 —— client 不能 import host 模块。 */
808
+ const CODEARTS_PROVIDER = "codearts";
809
+ const STATUS_PATH = "/api/codearts/status";
810
+ /** 轮询间隔:与缓存 TTL(60s)对齐,再短也只是反复命中缓存。 */
811
+ const REFRESH_INTERVAL_MS = 6e4;
812
+ function readSelection(useProjection) {
813
+ if (useProjection === void 0) return void 0;
814
+ try {
815
+ return useProjection("modelSelection");
816
+ } catch {
817
+ return;
818
+ }
819
+ }
820
+ function isCodeArts(selection) {
821
+ return (selection?.next ?? selection?.lastUsed)?.provider === CODEARTS_PROVIDER;
822
+ }
823
+ function CodeArtsCreditDock({ useProjection, t }) {
824
+ const active = isCodeArts(readSelection(useProjection));
825
+ const [remain, setRemain] = (0, react.useState)(null);
826
+ const [total, setTotal] = (0, react.useState)(0);
827
+ const mounted = (0, react.useRef)(true);
828
+ (0, react.useEffect)(() => {
829
+ mounted.current = true;
830
+ return () => {
831
+ mounted.current = false;
832
+ };
833
+ }, []);
834
+ (0, react.useEffect)(() => {
835
+ if (!active) {
836
+ setRemain(null);
837
+ return;
838
+ }
839
+ let timer;
840
+ const refresh = async () => {
841
+ try {
842
+ const res = await fetch(STATUS_PATH, {
843
+ headers: { Accept: "application/json" },
844
+ credentials: "same-origin"
845
+ });
846
+ if (!res.ok) return;
847
+ const data = await res.json().catch(() => void 0);
848
+ if (data === void 0 || data.credits === void 0) return;
849
+ if (mounted.current) {
850
+ setRemain(Math.round(data.credits.credits.total.remain));
851
+ setTotal(Math.round(data.credits.credits.total.total));
852
+ }
853
+ } catch {}
854
+ };
855
+ refresh();
856
+ timer = window.setInterval(refresh, REFRESH_INTERVAL_MS);
857
+ return () => {
858
+ if (timer !== void 0) window.clearInterval(timer);
859
+ };
860
+ }, [active]);
861
+ if (!active || remain === null) return null;
862
+ const text = t("creditsDock", { remain: new Intl.NumberFormat().format(remain) });
863
+ const title = t("creditsDockTitle", {
864
+ remain,
865
+ total
866
+ });
867
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
868
+ style: {
869
+ display: "inline-flex",
870
+ alignItems: "center",
871
+ gap: 6,
872
+ color: "var(--dsw-alias-label-tertiary)",
873
+ fontSize: "var(--dsh-content-font-size-secondary, 13px)",
874
+ lineHeight: "18px",
875
+ fontVariantNumeric: "tabular-nums",
876
+ whiteSpace: "nowrap"
877
+ },
878
+ title,
879
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text })
880
+ });
881
+ }
882
+ //#endregion
648
883
  //#region src/client/locales.ts
649
884
  const zh = {
650
885
  tabLabel: "CodeArts",
@@ -665,6 +900,8 @@ const zh = {
665
900
  saveToken: "保存 token",
666
901
  expiredHint: "凭证已过期,请重新登录以继续使用;已勾选的模型设置会保留。",
667
902
  accountsTitle: "账号",
903
+ accountsShow: "显示账号",
904
+ accountsHide: "隐藏账号",
668
905
  accountRemove: "删除",
669
906
  addAccount: "添加账号",
670
907
  accountsRotateHint: "已存多个账号:请求会自动轮转,某个账号忙时改用下一个,不用干等。",
@@ -699,8 +936,22 @@ const zh = {
699
936
  usageStateCapped: "撞上限,优先避开",
700
937
  usageStateCooling: "冷却中",
701
938
  usageStateDisabled: "已停用",
702
- usageNoQuota: "CodeArts 没有额度接口,这里统计的是本机经插件发出的请求;官方用量明细见",
703
- usageEmpty: "还没有记录:发一条消息就会开始累计。"
939
+ usageNoQuota: "这里是本机经插件发出的请求统计(不等于账号总额度);官方用量明细见",
940
+ usageEmpty: "还没有记录:发一条消息就会开始累计。",
941
+ creditsTitle: "积分",
942
+ creditsRemaining: "剩余 {remain} / {total}",
943
+ creditsUsed: "已用 {used}",
944
+ creditsExpiring: "其中 {expiring} 将在 {date} 重置时作废",
945
+ creditsReset: "额度重置 {date}",
946
+ creditsTokensToday: "今日 token {used} / {limit}",
947
+ creditsTokensBalance: "余额 {balance}",
948
+ creditsUnavailable: "积分不可用:{message}",
949
+ creditsDock: "积分 {remain}",
950
+ creditsDockTitle: "CodeArts 剩余积分 {remain} / {total}",
951
+ claimButton: "领取积分",
952
+ claiming: "领取中…",
953
+ claimSuccess: "已领取",
954
+ claimFailed: "领取失败:{message}"
704
955
  };
705
956
  const en = {
706
957
  tabLabel: "CodeArts",
@@ -721,6 +972,8 @@ const en = {
721
972
  saveToken: "Save token",
722
973
  expiredHint: "Your credential has expired. Sign in again to continue; your model selection is kept.",
723
974
  accountsTitle: "Accounts",
975
+ accountsShow: "Show accounts",
976
+ accountsHide: "Hide accounts",
724
977
  accountRemove: "Remove",
725
978
  addAccount: "Add account",
726
979
  accountsRotateHint: "Multiple accounts stored: requests rotate automatically, so a busy account hands over to the next one instead of making you wait.",
@@ -755,8 +1008,22 @@ const en = {
755
1008
  usageStateCapped: "Cap hit, avoiding",
756
1009
  usageStateCooling: "Cooling down",
757
1010
  usageStateDisabled: "Disabled",
758
- usageNoQuota: "CodeArts exposes no quota API — this counts the requests this machine sent through the plugin. Official usage:",
759
- usageEmpty: "Nothing recorded yet: send a message to start counting."
1011
+ usageNoQuota: "Counts the requests this machine sent through the plugin (not the account balance). Official usage:",
1012
+ usageEmpty: "Nothing recorded yet: send a message to start counting.",
1013
+ creditsTitle: "Credits",
1014
+ creditsRemaining: "{remain} of {total} left",
1015
+ creditsUsed: "{used} used",
1016
+ creditsExpiring: "{expiring} of them expire at the next reset on {date}",
1017
+ creditsReset: "Quota resets {date}",
1018
+ creditsTokensToday: "Today {used} / {limit} tokens",
1019
+ creditsTokensBalance: "{balance} left",
1020
+ creditsUnavailable: "Credits unavailable: {message}",
1021
+ creditsDock: "Credits {remain}",
1022
+ creditsDockTitle: "CodeArts credits left {remain} / {total}",
1023
+ claimButton: "Claim credits",
1024
+ claiming: "Claiming…",
1025
+ claimSuccess: "Claimed",
1026
+ claimFailed: "Claim failed: {message}"
760
1027
  };
761
1028
  //#endregion
762
1029
  //#region src/client/index.tsx
@@ -775,6 +1042,13 @@ function apply(ctx) {
775
1042
  key: "dsh-codearts",
776
1043
  locale: namespace
777
1044
  }, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodeArtsPluginCard, { t })));
1045
+ ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register({
1046
+ name: "conversation.composer.dock",
1047
+ id: "codearts-credits",
1048
+ order: 20,
1049
+ locale: namespace,
1050
+ inject: () => ({ t })
1051
+ }, CodeArtsCreditDock));
778
1052
  } catch (error) {
779
1053
  console.error("[dsh-codearts] client card failed to load (host provider unaffected):", error);
780
1054
  }