dsh-codearts 0.3.0 → 0.4.1

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/DEV.md CHANGED
@@ -40,6 +40,35 @@ pnpm run hmr:off # 关掉,回到 base 默认(只监听 profile 配置
40
40
 
41
41
  改完这个 patch 通常不用重启:profile 的 patch 文件本身也在 HMR 的监听范围内(profile 里 `patchReload: live`),条目会重新加载。若发现没生效,重启一次 DSH 即可,之后就一直有效。
42
42
 
43
+ > ⚠️ **开了模块监听就别用插件管理器/npm 升级本插件。** 宿主为热重载监视着
44
+ > `node_modules/dsh-codearts`,目录被占用后 pnpm 替换版本时会失败:
45
+ >
46
+ > ```
47
+ > [ERR_PNPM_EPERM] [importPackage ...\node_modules\dsh-codearts]
48
+ > EPERM: operation not permitted, rename
49
+ > '...\dsh-codearts_tmp_20976_1' -> '...\dsh-codearts'
50
+ > ```
51
+ >
52
+ > 更麻烦的是失败后**插件管理器会回滚**:把 `dsh-codearts` 从 profile 的
53
+ > `dependencies` 和 `bundles` 里一起删掉,表现为「插件列表里它没了」。升级前的正确顺序是
54
+ > `pnpm run hmr:off` → 升级 → (要接着开发再)`pnpm run hmr:on`。
55
+ >
56
+ > 这条只影响**跑过 `hmr:on` 的开发机**:该覆盖行写在 profile 的
57
+ > `cordis.patch.yml` 里,插件包自带的 patch 只有 `insert: llm-codearts`,
58
+ > 普通用户从 dshmarket 直接升级不会碰到。DSH base 的 desktop bundle 里默认是
59
+ > **不监视模块**的(原文,`app.asar` 内 base 的 patch):
60
+ >
61
+ > ```yaml
62
+ > # Profile configuration reloads by default; module roots are opt-in.
63
+ > - id: hmr
64
+ > name: '@deepseek-ai/dsh-hmr'
65
+ > disabled: !!js "!ctx.get('profileContext')"
66
+ > config:
67
+ > root: []
68
+ > ```
69
+ >
70
+ > 即 `root` 默认空表 —— 没人去开它,就没有句柄占用 `node_modules/dsh-codearts`。
71
+
43
72
  ## 日常循环
44
73
 
45
74
  ```sh
package/lib/bin.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
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";
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-D6EeUWiQ.mjs";
3
3
  import { existsSync, statSync } from "node:fs";
4
4
  //#region src/version.ts
5
- const CODEARTS_VERSION = "0.3.0";
5
+ const CODEARTS_VERSION = "0.4.1";
6
6
  //#endregion
7
7
  //#region src/bin.ts
8
8
  /**
@@ -67,6 +67,8 @@ interface CodeArtsSettingsKey {
67
67
  creditsUnavailable: string;
68
68
  creditsDock: string;
69
69
  creditsDockTitle: string;
70
+ signInButton: string;
71
+ signInHint: string;
70
72
  [key: string]: string;
71
73
  }
72
74
  //#endregion
@@ -18,32 +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
+ "manual": "DZPfya_manual",
22
+ "area": "DZPfya_area",
23
+ "error": "DZPfya_error",
24
+ "choiceRow": "DZPfya_choiceRow",
21
25
  "fieldLabel": "DZPfya_fieldLabel",
22
- "danger": "DZPfya_danger",
26
+ "desc": "DZPfya_desc",
23
27
  "card": "DZPfya_card",
24
- "hint": "DZPfya_hint",
28
+ "field": "DZPfya_field",
25
29
  "header": "DZPfya_header",
26
30
  "link": "DZPfya_link",
27
- "body": "DZPfya_body",
28
- "headText": "DZPfya_headText",
29
- "name": "DZPfya_name",
30
- "meta": "DZPfya_meta",
31
+ "manualSummary": "DZPfya_manualSummary",
31
32
  "section": "DZPfya_section",
32
33
  "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",
39
- "manual": "DZPfya_manual",
40
- "manualSummary": "DZPfya_manualSummary",
34
+ "headText": "DZPfya_headText",
41
35
  "statusLine": "DZPfya_statusLine",
42
- "field": "DZPfya_field",
43
- "actions": "DZPfya_actions",
44
- "area": "DZPfya_area",
36
+ "meta": "DZPfya_meta",
45
37
  "headActions": "DZPfya_headActions",
46
- "count": "DZPfya_count"
38
+ "body": "DZPfya_body",
39
+ "sectionHead": "DZPfya_sectionHead",
40
+ "name": "DZPfya_name",
41
+ "actions": "DZPfya_actions",
42
+ "danger": "DZPfya_danger",
43
+ "count": "DZPfya_count",
44
+ "hint": "DZPfya_hint",
45
+ "choices": "DZPfya_choices",
46
+ "check": "DZPfya_check"
47
47
  };
48
48
  //#endregion
49
49
  //#region src/client/CodeArtsPluginCard.tsx
@@ -84,6 +84,23 @@ function formatCredits(value) {
84
84
  return Number.isFinite(value) ? new Intl.NumberFormat().format(Math.round(value)) : "0";
85
85
  }
86
86
  /**
87
+ * 签到领积分的入口页。
88
+ *
89
+ * 官方 CodeArts Agent 客户端里,用户菜单的「活动福利(签到领 1000 积分)」点击后
90
+ * **不发任何接口请求**,只是用系统浏览器打开这个页面(SnapChat 的 isActivity 分支 →
91
+ * `jumpToBrowser(INIT_CONFIG.personalUsageSettings, isOutSideOpen: true)`,URL 来自
92
+ * `vscode-codebot/product.json`)。也就是说积分的签到动作只在那一页上完成,AK/SK
93
+ * 网关那套(benefit/claim、statistics/plugin)里没有任何发积分的接口 —— 详见
94
+ * src/upstream.ts 的 claimBenefit:它开通的是「限时福利免费模型」渠道,不是积分。
95
+ */
96
+ const CODEARTS_SIGN_IN_URL = "https://codearts.huaweicloud.com/portal/settings/personal-usage";
97
+ /** 打开签到页;带 locale 参数,与官方客户端的拼法一致。 */
98
+ function openSignInPage() {
99
+ if (typeof window === "undefined") return;
100
+ const locale = (typeof navigator === "undefined" ? "" : navigator.language ?? "").toLowerCase().startsWith("en") ? "en-us" : "zh-cn";
101
+ window.open(`${CODEARTS_SIGN_IN_URL}?locale=${locale}`, "_blank", "noopener");
102
+ }
103
+ /**
87
104
  * 积分区块。
88
105
  *
89
106
  * 2026-10 从官方 CodeArts Agent 客户端挖出积分接口并实测通过(见 src/credits.ts),
@@ -101,18 +118,30 @@ function CreditsSection({ credits, error, t }) {
101
118
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
102
119
  className: CodeArtsPluginCard_module_css_default.sectionLabel,
103
120
  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
- })
121
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
122
+ className: CodeArtsPluginCard_module_css_default.headActions,
123
+ children: [c !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
124
+ className: CodeArtsPluginCard_module_css_default.count,
125
+ children: t("creditsRemaining", {
126
+ remain: formatCredits(c.total.remain),
127
+ total: formatCredits(c.total.total)
128
+ })
129
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
130
+ variant: "outline",
131
+ size: "sm",
132
+ onClick: () => openSignInPage(),
133
+ children: t("signInButton")
134
+ })]
110
135
  })]
111
136
  }),
112
137
  error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
113
138
  className: CodeArtsPluginCard_module_css_default.hint,
114
139
  children: t("creditsUnavailable", { message: error })
115
140
  }),
141
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
142
+ className: CodeArtsPluginCard_module_css_default.hint,
143
+ children: t("signInHint")
144
+ }),
116
145
  c !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
117
146
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
118
147
  className: CodeArtsPluginCard_module_css_default.choiceRow,
@@ -900,7 +929,9 @@ const zh = {
900
929
  creditsTokensBalance: "余额 {balance}",
901
930
  creditsUnavailable: "积分不可用:{message}",
902
931
  creditsDock: "积分 {remain}",
903
- creditsDockTitle: "CodeArts 剩余积分 {remain} / {total}"
932
+ creditsDockTitle: "CodeArts 剩余积分 {remain} / {total}",
933
+ signInButton: "去签到领积分",
934
+ signInHint: "积分由 CodeArts 官网活动页发放,签到要在官网页面完成(和官方客户端同一入口)。"
904
935
  };
905
936
  const en = {
906
937
  tabLabel: "CodeArts",
@@ -968,7 +999,9 @@ const en = {
968
999
  creditsTokensBalance: "{balance} left",
969
1000
  creditsUnavailable: "Credits unavailable: {message}",
970
1001
  creditsDock: "Credits {remain}",
971
- creditsDockTitle: "CodeArts credits left {remain} / {total}"
1002
+ creditsDockTitle: "CodeArts credits left {remain} / {total}",
1003
+ signInButton: "Check in for credits",
1004
+ signInHint: "Credits are issued on the CodeArts portal; check-in happens on that page (same entry as the official client)."
972
1005
  };
973
1006
  //#endregion
974
1007
  //#region src/client/index.tsx
package/lib/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { C as codeartsAuthPath, S as codeartsAccountsPath, _ as FALLBACK_CODEARTS_MODELS, a as SNAP_HOST, b as CodeArtsAccountStore, c as degradeTokenCeiling, d as isConcurrencyCap, f as isParamInvalidFrame, g as CodeArtsCatalog, h as signedGet, i as refreshCatalog, l as fetchCallerIdentity, m as prepareChatBody, n as codeartsUsagePath, o as canonicalModel, p as maxTokensCeilingFor, r as discoverModels, s as chatStream, t as CodeArtsUsageTracker, u as fetchModels, v as filterEnabledModels, w as startLogin, x as CodeArtsCredentialStore, y as isBenefitModel } from "./usage-Cn1Bzmxj.mjs";
1
+ import { C as codeartsAccountsPath, S as CodeArtsCredentialStore, T as startLogin, _ as CodeArtsCatalog, a as SNAP_HOST, b as isBenefitModel, c as claimBenefit, d as fetchModels, f as isConcurrencyCap, g as signedGet, h as prepareChatBody, i as refreshCatalog, l as degradeTokenCeiling, m as maxTokensCeilingFor, n as codeartsUsagePath, o as canonicalModel, p as isParamInvalidFrame, r as discoverModels, s as chatStream, t as CodeArtsUsageTracker, u as fetchCallerIdentity, v as FALLBACK_CODEARTS_MODELS, w as codeartsAuthPath, x as CodeArtsAccountStore, y as filterEnabledModels } from "./usage-D6EeUWiQ.mjs";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { randomBytes, timingSafeEqual } from "node:crypto";
4
4
  import { createServer } from "node:http";
@@ -999,6 +999,8 @@ const CODEARTS_LOGIN_PATH = "/api/codearts/login";
999
999
  const CODEARTS_LOGIN_STATUS_PATH = "/api/codearts/login/status";
1000
1000
  /** 删除某个账号:DELETE /api/codearts/accounts?id=<accountId> */
1001
1001
  const CODEARTS_ACCOUNTS_PATH = "/api/codearts/accounts";
1002
+ /** POST /api/codearts/claim — 开通「限时福利」免费模型渠道(幂等);**不发积分**。 */
1003
+ const CODEARTS_CLAIM_PATH = "/api/codearts/claim";
1002
1004
  /**
1003
1005
  * 由完整目录 + 已存 enabled 计算 selection。
1004
1006
  * - enabled 为空/未存 => 全部勾选(restricted=false)
@@ -1331,6 +1333,36 @@ function accountsHandler(deps) {
1331
1333
  }
1332
1334
  };
1333
1335
  }
1336
+ /** POST /api/codearts/claim — 开通「限时福利」免费模型渠道(幂等);**不发积分**。 */
1337
+ function claimHandler(deps) {
1338
+ return async (req, res) => {
1339
+ if (req.method !== "POST") {
1340
+ json(res, 405, { error: "method not allowed" });
1341
+ return;
1342
+ }
1343
+ if (deps.claim === void 0) {
1344
+ json(res, 404, { error: "claim not available" });
1345
+ return;
1346
+ }
1347
+ if (!loopbackRequest(req)) {
1348
+ json(res, 403, { error: "request-not-trusted" });
1349
+ return;
1350
+ }
1351
+ try {
1352
+ const result = await deps.claim();
1353
+ if (result.ok) json(res, 200, { ok: true });
1354
+ else json(res, 500, {
1355
+ ok: false,
1356
+ message: result.message
1357
+ });
1358
+ } catch (error) {
1359
+ json(res, 500, {
1360
+ ok: false,
1361
+ message: safeMessage(error)
1362
+ });
1363
+ }
1364
+ };
1365
+ }
1334
1366
  /** Mount the status / models / token routes. */
1335
1367
  function registerCodeArtsStatusRoute(ctx, deps) {
1336
1368
  ctx.effect(() => {
@@ -1372,6 +1404,11 @@ function registerCodeArtsStatusRoute(ctx, deps) {
1372
1404
  path: CODEARTS_ACCOUNTS_PATH,
1373
1405
  handler: accountsHandler(deps)
1374
1406
  });
1407
+ const disposeClaim = ctx.webServer.register({
1408
+ kind: "exact",
1409
+ path: CODEARTS_CLAIM_PATH,
1410
+ handler: claimHandler(deps)
1411
+ });
1375
1412
  return () => {
1376
1413
  disposeStatus();
1377
1414
  disposeModels();
@@ -1380,6 +1417,7 @@ function registerCodeArtsStatusRoute(ctx, deps) {
1380
1417
  disposeLoginStart();
1381
1418
  disposeLoginStatus();
1382
1419
  disposeAccounts();
1420
+ disposeClaim();
1383
1421
  for (const s of activeLogins.values()) s.close();
1384
1422
  activeLogins.clear();
1385
1423
  };
@@ -1607,7 +1645,29 @@ function apply(ctx, rawConfig) {
1607
1645
  setEnabledModels,
1608
1646
  pool: () => pool.list(),
1609
1647
  usage: () => usage.snapshot(),
1610
- credits: (signal) => loadCredits(signal)
1648
+ credits: (signal) => loadCredits(signal),
1649
+ claim: async () => {
1650
+ try {
1651
+ const ids = await store.ids();
1652
+ for (const id of ids) {
1653
+ const stored = await store.credential(id);
1654
+ if (stored === void 0 || stored.stale === true) continue;
1655
+ const resolved = await store.resolve(id);
1656
+ if (resolved === void 0) continue;
1657
+ await claimBenefit(resolved, AbortSignal.timeout(1e4));
1658
+ return { ok: true };
1659
+ }
1660
+ return {
1661
+ ok: false,
1662
+ message: "no usable credential"
1663
+ };
1664
+ } catch (error) {
1665
+ return {
1666
+ ok: false,
1667
+ message: error instanceof Error ? error.message : String(error)
1668
+ };
1669
+ }
1670
+ }
1611
1671
  }));
1612
1672
  installSettingsSectionCompat(ctx, CODEARTS_SETTINGS_NS, Config, config, {
1613
1673
  setSource(source) {
@@ -1883,9 +1883,14 @@ async function fetchModels(credential, signal) {
1883
1883
  };
1884
1884
  }
1885
1885
  /**
1886
- * 领取限时福利(幂等:已领取返回成功)。官方客户端打开模型菜单即调用。
1887
- * 不领取时福利模型调用必报 InferHub.4004.200 benefit not found。
1886
+ * 开通「限时福利」(免费模型)渠道,幂等:已开通就原样返回。官方客户端打开模型菜单
1887
+ * 即调用(GET),用户点「领取」时也调(POST)——两种方法实测都返回 200/0000。
1888
+ * 不开通时福利模型调用必报 InferHub.4004.200 benefit not found。
1888
1889
  * 需要 AK/SK 签名(凭证要带 accessKeyId/secretAccessKey)。
1890
+ *
1891
+ * **它不发积分。** 返回体只是一条渠道开通记录(channel / create_time / update_time,
1892
+ * 幂等所以时间戳是旧的),账号积分由套餐决定(见 credits.ts),领取动作在官网签到页。
1893
+ * 别把它当「领取积分」用 —— 早先卡片这么用,结果就是「点了显示已领取、积分没变」。
1889
1894
  */
1890
1895
  async function claimBenefit(credential, abort) {
1891
1896
  if (!credential.accessKeyId || !credential.secretAccessKey) return;
@@ -1908,13 +1913,13 @@ async function claimBenefit(credential, abort) {
1908
1913
  const resp = await fetch(req, { signal: abort });
1909
1914
  const raw = await resp.text().catch(() => "");
1910
1915
  if (!resp.ok) throw new Error(`claim benefit failed: ${resp.status} ${raw.slice(0, 200)}`);
1916
+ let json;
1911
1917
  try {
1912
- const json = JSON.parse(raw);
1913
- if (json.error_code && json.error_code !== "0000") throw new Error(`claim benefit rejected: ${json.error_code} ${json.error_msg ?? ""}`);
1914
- } catch (e) {
1915
- if (e instanceof SyntaxError) return;
1916
- throw e;
1918
+ json = JSON.parse(raw);
1919
+ } catch {
1920
+ throw new Error(`claim benefit: unexpected non-JSON response: ${raw.slice(0, 200)}`);
1917
1921
  }
1922
+ if (json.error_code !== "0000") throw new Error(`claim benefit rejected: ${json.error_code ?? "(no error_code)"} ${json.error_msg ?? ""}`.trim());
1918
1923
  }
1919
1924
  //#endregion
1920
1925
  //#region src/discovery.ts
@@ -2220,4 +2225,4 @@ function sumOf(store) {
2220
2225
  return out;
2221
2226
  }
2222
2227
  //#endregion
2223
- export { codeartsAuthPath as C, codeartsAccountsPath as S, FALLBACK_CODEARTS_MODELS as _, SNAP_HOST as a, CodeArtsAccountStore as b, degradeTokenCeiling as c, isConcurrencyCap as d, isParamInvalidFrame as f, CodeArtsCatalog as g, signedGet as h, refreshCatalog as i, fetchCallerIdentity as l, prepareChatBody as m, codeartsUsagePath as n, canonicalModel as o, maxTokensCeilingFor as p, discoverModels as r, chatStream as s, CodeArtsUsageTracker as t, fetchModels as u, filterEnabledModels as v, startLogin as w, CodeArtsCredentialStore as x, isBenefitModel as y };
2228
+ export { codeartsAccountsPath as C, CodeArtsCredentialStore as S, startLogin as T, CodeArtsCatalog as _, SNAP_HOST as a, isBenefitModel as b, claimBenefit as c, fetchModels as d, isConcurrencyCap as f, signedGet as g, prepareChatBody as h, refreshCatalog as i, degradeTokenCeiling as l, maxTokensCeilingFor as m, codeartsUsagePath as n, canonicalModel as o, isParamInvalidFrame as p, discoverModels as r, chatStream as s, CodeArtsUsageTracker as t, fetchCallerIdentity as u, FALLBACK_CODEARTS_MODELS as v, codeartsAuthPath as w, CodeArtsAccountStore as x, filterEnabledModels as y };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-codearts",
3
3
  "displayName": "DSH CodeArts Connect",
4
4
  "description": "把华为云 CodeArts 接入 DeepSeek Harness —— 内置 OAuth2 登录(PKCE + DPoP),登录后即可在 DSH 对话窗口使用 CodeArts 的模型。",
5
- "version": "0.3.0",
5
+ "version": "0.4.1",
6
6
  "author": "elegant01",
7
7
  "keywords": [
8
8
  "dsh-plugin",