dsh-codearts 0.2.1 → 0.3.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 { S as codeartsAccountsPath, _ as FALLBACK_CODEARTS_MODELS, b as CodeArtsAccountStore, l as fetchCallerIdentity, n as codeartsUsagePath, r as discoverModels, t as CodeArtsUsageTracker, y as isBenefitModel } from "./usage-Cn1Bzmxj.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,16 @@ 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;
58
70
  [key: string]: string;
59
71
  }
60
72
  //#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
+ "fieldLabel": "DZPfya_fieldLabel",
22
+ "danger": "DZPfya_danger",
23
+ "card": "DZPfya_card",
24
+ "hint": "DZPfya_hint",
25
+ "header": "DZPfya_header",
26
+ "link": "DZPfya_link",
21
27
  "body": "DZPfya_body",
28
+ "headText": "DZPfya_headText",
22
29
  "name": "DZPfya_name",
30
+ "meta": "DZPfya_meta",
31
+ "section": "DZPfya_section",
32
+ "sectionLabel": "DZPfya_sectionLabel",
33
+ "error": "DZPfya_error",
34
+ "choices": "DZPfya_choices",
35
+ "desc": "DZPfya_desc",
36
+ "sectionHead": "DZPfya_sectionHead",
37
+ "choiceRow": "DZPfya_choiceRow",
38
+ "check": "DZPfya_check",
23
39
  "manual": "DZPfya_manual",
24
40
  "manualSummary": "DZPfya_manualSummary",
25
41
  "statusLine": "DZPfya_statusLine",
26
- "actions": "DZPfya_actions",
27
- "sectionLabel": "DZPfya_sectionLabel",
28
42
  "field": "DZPfya_field",
29
- "count": "DZPfya_count",
30
- "danger": "DZPfya_danger",
43
+ "actions": "DZPfya_actions",
31
44
  "area": "DZPfya_area",
32
- "error": "DZPfya_error",
33
- "hint": "DZPfya_hint",
34
- "desc": "DZPfya_desc",
35
- "meta": "DZPfya_meta",
36
- "choiceRow": "DZPfya_choiceRow",
37
- "choices": "DZPfya_choices",
38
- "sectionHead": "DZPfya_sectionHead",
39
- "fieldLabel": "DZPfya_fieldLabel",
40
- "card": "DZPfya_card",
41
- "link": "DZPfya_link",
42
- "header": "DZPfya_header",
43
- "headText": "DZPfya_headText",
44
- "section": "DZPfya_section",
45
- "check": "DZPfya_check"
45
+ "headActions": "DZPfya_headActions",
46
+ "count": "DZPfya_count"
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,87 @@ 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 }) {
94
+ if (credits === void 0 && error === void 0) return null;
95
+ const c = credits?.credits;
96
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
97
+ className: CodeArtsPluginCard_module_css_default.section,
98
+ children: [
99
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
100
+ className: CodeArtsPluginCard_module_css_default.sectionHead,
101
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
102
+ className: CodeArtsPluginCard_module_css_default.sectionLabel,
103
+ children: t("creditsTitle")
104
+ }), c !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
105
+ className: CodeArtsPluginCard_module_css_default.count,
106
+ children: t("creditsRemaining", {
107
+ remain: formatCredits(c.total.remain),
108
+ total: formatCredits(c.total.total)
109
+ })
110
+ })]
111
+ }),
112
+ error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
113
+ className: CodeArtsPluginCard_module_css_default.hint,
114
+ children: t("creditsUnavailable", { message: error })
115
+ }),
116
+ c !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
117
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
118
+ className: CodeArtsPluginCard_module_css_default.choiceRow,
119
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
120
+ className: CodeArtsPluginCard_module_css_default.name,
121
+ children: c.planName
122
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
123
+ className: CodeArtsPluginCard_module_css_default.meta,
124
+ children: t("creditsUsed", { used: formatCredits(c.total.used) })
125
+ })]
126
+ }),
127
+ c.periodEnd !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
128
+ className: CodeArtsPluginCard_module_css_default.choiceRow,
129
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
130
+ className: CodeArtsPluginCard_module_css_default.name,
131
+ children: t("creditsReset", { date: c.periodEnd })
132
+ }), c.total.expiring > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
133
+ className: CodeArtsPluginCard_module_css_default.meta,
134
+ children: t("creditsExpiring", {
135
+ expiring: formatCredits(c.total.expiring),
136
+ date: c.periodEnd
137
+ })
138
+ })]
139
+ }),
140
+ credits?.tokens !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
141
+ className: CodeArtsPluginCard_module_css_default.choiceRow,
142
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
143
+ className: CodeArtsPluginCard_module_css_default.name,
144
+ children: t("creditsTokensToday", {
145
+ used: formatCredits(credits.tokens.dailyUsed),
146
+ limit: formatCredits(credits.tokens.dailyLimit)
147
+ })
148
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
149
+ className: CodeArtsPluginCard_module_css_default.meta,
150
+ children: t("creditsTokensBalance", { balance: formatCredits(credits.tokens.balance) })
151
+ })]
152
+ })
153
+ ] })
154
+ ]
155
+ });
156
+ }
74
157
  /**
75
158
  * 用量区块。
76
159
  *
77
- * 只展示**本机经插件发出**的请求:上游没有额度/积分接口(README 与 codearts2api
78
- * 都是这个结论),所以这里明确写出这一点并给出官方明细页链接,别让用户误以为
79
- * 这是账号总额度。会话槽位是真事实 —— 每账号 3 个并发会话、被占时要等约 52 秒
80
- * 才释放,这也正是「打断后重新提问要等」的原因。
160
+ * 只展示**本机经插件发出**的请求(不等于账号总额度 —— 账号额度见上方积分区块);
161
+ * 会话槽位是真事实 —— 每账号 3 个并发会话、被占时要等约 52 秒才释放,这也正是
162
+ * 「打断后重新提问要等」的原因。
81
163
  */
82
164
  function UsageSection({ usage, t }) {
83
165
  const total = usage.today.requests;
@@ -198,6 +280,8 @@ function CodeArtsPluginCard({ t }) {
198
280
  const [savingModels, setSavingModels] = (0, react.useState)(false);
199
281
  const [savedModels, setSavedModels] = (0, react.useState)(false);
200
282
  const [modelError, setModelError] = (0, react.useState)(null);
283
+ /** 账号身份是否展开:默认遮蔽(false = 显示 *******)。 */
284
+ const [accountsRevealed, setAccountsRevealed] = (0, react.useState)(false);
201
285
  async function refresh() {
202
286
  try {
203
287
  const data = await (await fetch("/api/codearts/status", { headers: { Accept: "application/json" } })).json();
@@ -357,6 +441,8 @@ function CodeArtsPluginCard({ t }) {
357
441
  const expired = status?.status === "expired";
358
442
  const accounts = status?.accounts ?? [];
359
443
  const accountName = (account) => account.label?.trim() || account.userName?.trim() || account.id;
444
+ /** 遮蔽态下账号身份一律换成占位符:原值连 DOM 都不进。 */
445
+ const reveal = (value) => accountsRevealed ? value : ACCOUNT_MASK;
360
446
  /**
361
447
  * 每个账号的「获取时间 + 过期状态」。同一账号重复登录可能留下多条记录(上游不给
362
448
  * 稳定身份时的妥协),把时间摆出来用户才能一眼认出哪条是旧的、放心删。
@@ -373,7 +459,7 @@ function CodeArtsPluginCard({ t }) {
373
459
  const statusText = signedIn ? t("statusSignedIn") : expired ? t("statusExpired") : t("statusSignedOut");
374
460
  const storedTime = formatStored(status?.storedAt);
375
461
  const storedLabel = status?.label?.trim() ?? "";
376
- const meta = !signedIn || storedTime === "" ? "" : storedLabel === "" ? t("storedMetaNoLabel", { time: storedTime }) : t("storedMeta", {
462
+ const meta = !signedIn || storedTime === "" ? "" : storedLabel === "" || !accountsRevealed ? t("storedMetaNoLabel", { time: storedTime }) : t("storedMeta", {
377
463
  label: storedLabel,
378
464
  time: storedTime
379
465
  });
@@ -419,9 +505,18 @@ function CodeArtsPluginCard({ t }) {
419
505
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
420
506
  className: CodeArtsPluginCard_module_css_default.sectionLabel,
421
507
  children: t("accountsTitle")
422
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
423
- className: CodeArtsPluginCard_module_css_default.count,
424
- children: accounts.length
508
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
509
+ className: CodeArtsPluginCard_module_css_default.headActions,
510
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
511
+ className: CodeArtsPluginCard_module_css_default.count,
512
+ children: accounts.length
513
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
514
+ variant: "outline",
515
+ size: "sm",
516
+ "aria-pressed": accountsRevealed,
517
+ onClick: () => setAccountsRevealed((revealed) => !revealed),
518
+ children: accountsRevealed ? t("accountsHide") : t("accountsShow")
519
+ })]
425
520
  })]
426
521
  }),
427
522
  accounts.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -435,11 +530,11 @@ function CodeArtsPluginCard({ t }) {
435
530
  children: [
436
531
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
437
532
  className: CodeArtsPluginCard_module_css_default.name,
438
- children: accountName(account)
533
+ children: reveal(accountName(account))
439
534
  }),
440
- meta !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
535
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
441
536
  className: CodeArtsPluginCard_module_css_default.meta,
442
- children: meta
537
+ children: reveal(meta)
443
538
  }),
444
539
  account.stale && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, { children: t("accountNeedsSignIn") }),
445
540
  !account.signable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, { children: t("accountNoKey") }),
@@ -636,6 +731,11 @@ function CodeArtsPluginCard({ t }) {
636
731
  className: CodeArtsPluginCard_module_css_default.hint,
637
732
  children: t("modelsLoading")
638
733
  }),
734
+ signedIn && (status?.credits !== void 0 || status?.creditsError !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreditsSection, {
735
+ credits: status?.credits,
736
+ error: status?.creditsError,
737
+ t
738
+ }),
639
739
  status?.usage !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageSection, {
640
740
  usage: status.usage,
641
741
  t
@@ -645,6 +745,94 @@ function CodeArtsPluginCard({ t }) {
645
745
  });
646
746
  }
647
747
  //#endregion
748
+ //#region src/client/CodeArtsCreditDock.tsx
749
+ /**
750
+ * 对话底部的积分条。
751
+ *
752
+ * 用户最初的诉求就是「对话底部要显示积分」—— 这一条挂在
753
+ * `conversation.composer.dock` 槽位上(与宿主 session-stats strip 同位)。
754
+ * 只在当前 provider 是 codearts 时才拉取并显示一行「积分 N」,别的 provider 下
755
+ * 完全不渲染,不抢别人的行。
756
+ *
757
+ * 积分数据走插件自己的 `/api/codearts/status`(带 TTL 缓存,见 src/credits.ts),
758
+ * 不会因为这条 dock 让上游多挨一次请求。
759
+ */
760
+ /** 与 src/adapter.ts 的 CODEARTS_PROVIDER 对齐 —— client 不能 import host 模块。 */
761
+ const CODEARTS_PROVIDER = "codearts";
762
+ const STATUS_PATH = "/api/codearts/status";
763
+ /** 轮询间隔:与缓存 TTL(60s)对齐,再短也只是反复命中缓存。 */
764
+ const REFRESH_INTERVAL_MS = 6e4;
765
+ function readSelection(useProjection) {
766
+ if (useProjection === void 0) return void 0;
767
+ try {
768
+ return useProjection("modelSelection");
769
+ } catch {
770
+ return;
771
+ }
772
+ }
773
+ function isCodeArts(selection) {
774
+ return (selection?.next ?? selection?.lastUsed)?.provider === CODEARTS_PROVIDER;
775
+ }
776
+ function CodeArtsCreditDock({ useProjection, t }) {
777
+ const active = isCodeArts(readSelection(useProjection));
778
+ const [remain, setRemain] = (0, react.useState)(null);
779
+ const [total, setTotal] = (0, react.useState)(0);
780
+ const mounted = (0, react.useRef)(true);
781
+ (0, react.useEffect)(() => {
782
+ mounted.current = true;
783
+ return () => {
784
+ mounted.current = false;
785
+ };
786
+ }, []);
787
+ (0, react.useEffect)(() => {
788
+ if (!active) {
789
+ setRemain(null);
790
+ return;
791
+ }
792
+ let timer;
793
+ const refresh = async () => {
794
+ try {
795
+ const res = await fetch(STATUS_PATH, {
796
+ headers: { Accept: "application/json" },
797
+ credentials: "same-origin"
798
+ });
799
+ if (!res.ok) return;
800
+ const data = await res.json().catch(() => void 0);
801
+ if (data === void 0 || data.credits === void 0) return;
802
+ if (mounted.current) {
803
+ setRemain(Math.round(data.credits.credits.total.remain));
804
+ setTotal(Math.round(data.credits.credits.total.total));
805
+ }
806
+ } catch {}
807
+ };
808
+ refresh();
809
+ timer = window.setInterval(refresh, REFRESH_INTERVAL_MS);
810
+ return () => {
811
+ if (timer !== void 0) window.clearInterval(timer);
812
+ };
813
+ }, [active]);
814
+ if (!active || remain === null) return null;
815
+ const text = t("creditsDock", { remain: new Intl.NumberFormat().format(remain) });
816
+ const title = t("creditsDockTitle", {
817
+ remain,
818
+ total
819
+ });
820
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
821
+ style: {
822
+ display: "inline-flex",
823
+ alignItems: "center",
824
+ gap: 6,
825
+ color: "var(--dsw-alias-label-tertiary)",
826
+ fontSize: "var(--dsh-content-font-size-secondary, 13px)",
827
+ lineHeight: "18px",
828
+ fontVariantNumeric: "tabular-nums",
829
+ whiteSpace: "nowrap"
830
+ },
831
+ title,
832
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text })
833
+ });
834
+ }
835
+ //#endregion
648
836
  //#region src/client/locales.ts
649
837
  const zh = {
650
838
  tabLabel: "CodeArts",
@@ -665,6 +853,8 @@ const zh = {
665
853
  saveToken: "保存 token",
666
854
  expiredHint: "凭证已过期,请重新登录以继续使用;已勾选的模型设置会保留。",
667
855
  accountsTitle: "账号",
856
+ accountsShow: "显示账号",
857
+ accountsHide: "隐藏账号",
668
858
  accountRemove: "删除",
669
859
  addAccount: "添加账号",
670
860
  accountsRotateHint: "已存多个账号:请求会自动轮转,某个账号忙时改用下一个,不用干等。",
@@ -699,8 +889,18 @@ const zh = {
699
889
  usageStateCapped: "撞上限,优先避开",
700
890
  usageStateCooling: "冷却中",
701
891
  usageStateDisabled: "已停用",
702
- usageNoQuota: "CodeArts 没有额度接口,这里统计的是本机经插件发出的请求;官方用量明细见",
703
- usageEmpty: "还没有记录:发一条消息就会开始累计。"
892
+ usageNoQuota: "这里是本机经插件发出的请求统计(不等于账号总额度);官方用量明细见",
893
+ usageEmpty: "还没有记录:发一条消息就会开始累计。",
894
+ creditsTitle: "积分",
895
+ creditsRemaining: "剩余 {remain} / {total}",
896
+ creditsUsed: "已用 {used}",
897
+ creditsExpiring: "其中 {expiring} 将在 {date} 重置时作废",
898
+ creditsReset: "额度重置 {date}",
899
+ creditsTokensToday: "今日 token {used} / {limit}",
900
+ creditsTokensBalance: "余额 {balance}",
901
+ creditsUnavailable: "积分不可用:{message}",
902
+ creditsDock: "积分 {remain}",
903
+ creditsDockTitle: "CodeArts 剩余积分 {remain} / {total}"
704
904
  };
705
905
  const en = {
706
906
  tabLabel: "CodeArts",
@@ -721,6 +921,8 @@ const en = {
721
921
  saveToken: "Save token",
722
922
  expiredHint: "Your credential has expired. Sign in again to continue; your model selection is kept.",
723
923
  accountsTitle: "Accounts",
924
+ accountsShow: "Show accounts",
925
+ accountsHide: "Hide accounts",
724
926
  accountRemove: "Remove",
725
927
  addAccount: "Add account",
726
928
  accountsRotateHint: "Multiple accounts stored: requests rotate automatically, so a busy account hands over to the next one instead of making you wait.",
@@ -755,8 +957,18 @@ const en = {
755
957
  usageStateCapped: "Cap hit, avoiding",
756
958
  usageStateCooling: "Cooling down",
757
959
  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."
960
+ usageNoQuota: "Counts the requests this machine sent through the plugin (not the account balance). Official usage:",
961
+ usageEmpty: "Nothing recorded yet: send a message to start counting.",
962
+ creditsTitle: "Credits",
963
+ creditsRemaining: "{remain} of {total} left",
964
+ creditsUsed: "{used} used",
965
+ creditsExpiring: "{expiring} of them expire at the next reset on {date}",
966
+ creditsReset: "Quota resets {date}",
967
+ creditsTokensToday: "Today {used} / {limit} tokens",
968
+ creditsTokensBalance: "{balance} left",
969
+ creditsUnavailable: "Credits unavailable: {message}",
970
+ creditsDock: "Credits {remain}",
971
+ creditsDockTitle: "CodeArts credits left {remain} / {total}"
760
972
  };
761
973
  //#endregion
762
974
  //#region src/client/index.tsx
@@ -775,6 +987,13 @@ function apply(ctx) {
775
987
  key: "dsh-codearts",
776
988
  locale: namespace
777
989
  }, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodeArtsPluginCard, { t })));
990
+ ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register({
991
+ name: "conversation.composer.dock",
992
+ id: "codearts-credits",
993
+ order: 20,
994
+ locale: namespace,
995
+ inject: () => ({ t })
996
+ }, CodeArtsCreditDock));
778
997
  } catch (error) {
779
998
  console.error("[dsh-codearts] client card failed to load (host provider unaffected):", error);
780
999
  }