dsh-codearts 0.4.1 → 0.4.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/lib/bin.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
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";
2
+ import { C as isBenefitModel, E as codeartsAccountsPath, n as codeartsUsagePath, r as discoverModels, t as CodeArtsUsageTracker, u as fetchCallerIdentity, w as CodeArtsAccountStore, x as FALLBACK_CODEARTS_MODELS } from "./usage-B4vQb1sP.mjs";
3
3
  import { existsSync, statSync } from "node:fs";
4
4
  //#region src/version.ts
5
- const CODEARTS_VERSION = "0.4.1";
5
+ const CODEARTS_VERSION = "0.4.2";
6
6
  //#endregion
7
7
  //#region src/bin.ts
8
8
  /**
@@ -67,8 +67,14 @@ interface CodeArtsSettingsKey {
67
67
  creditsUnavailable: string;
68
68
  creditsDock: string;
69
69
  creditsDockTitle: string;
70
- signInButton: string;
71
- signInHint: string;
70
+ claimButton: string;
71
+ claimButtonDone: string;
72
+ claimPending: string;
73
+ claimHint: string;
74
+ claimDone: string;
75
+ claimAlready: string;
76
+ claimUnavailable: string;
77
+ claimFailed: string;
72
78
  [key: string]: string;
73
79
  }
74
80
  //#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",
21
+ "name": "DZPfya_name",
22
+ "actions": "DZPfya_actions",
24
23
  "choiceRow": "DZPfya_choiceRow",
25
- "fieldLabel": "DZPfya_fieldLabel",
26
- "desc": "DZPfya_desc",
27
- "card": "DZPfya_card",
28
- "field": "DZPfya_field",
29
- "header": "DZPfya_header",
30
- "link": "DZPfya_link",
31
24
  "manualSummary": "DZPfya_manualSummary",
32
- "section": "DZPfya_section",
33
- "sectionLabel": "DZPfya_sectionLabel",
34
- "headText": "DZPfya_headText",
35
- "statusLine": "DZPfya_statusLine",
25
+ "field": "DZPfya_field",
36
26
  "meta": "DZPfya_meta",
37
27
  "headActions": "DZPfya_headActions",
38
- "body": "DZPfya_body",
28
+ "link": "DZPfya_link",
29
+ "manual": "DZPfya_manual",
30
+ "area": "DZPfya_area",
31
+ "headText": "DZPfya_headText",
39
32
  "sectionHead": "DZPfya_sectionHead",
40
- "name": "DZPfya_name",
41
- "actions": "DZPfya_actions",
42
- "danger": "DZPfya_danger",
43
- "count": "DZPfya_count",
33
+ "body": "DZPfya_body",
44
34
  "hint": "DZPfya_hint",
35
+ "statusLine": "DZPfya_statusLine",
45
36
  "choices": "DZPfya_choices",
46
- "check": "DZPfya_check"
37
+ "check": "DZPfya_check",
38
+ "desc": "DZPfya_desc",
39
+ "count": "DZPfya_count",
40
+ "sectionLabel": "DZPfya_sectionLabel",
41
+ "fieldLabel": "DZPfya_fieldLabel",
42
+ "header": "DZPfya_header",
43
+ "card": "DZPfya_card",
44
+ "error": "DZPfya_error",
45
+ "danger": "DZPfya_danger",
46
+ "section": "DZPfya_section"
47
47
  };
48
48
  //#endregion
49
49
  //#region src/client/CodeArtsPluginCard.tsx
@@ -84,21 +84,16 @@ function formatCredits(value) {
84
84
  return Number.isFinite(value) ? new Intl.NumberFormat().format(Math.round(value)) : "0";
85
85
  }
86
86
  /**
87
- * 签到领积分的入口页。
87
+ * 领取结果 → 文案。
88
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:它开通的是「限时福利免费模型」渠道,不是积分。
89
+ * 本地状态(claimed/already/没有这个活动)用界面自己的话;只有 failed 才透出
90
+ * 上游给的 reason —— 那句往往才是排障要的东西(签名失败、404、非 JSON…)。
95
91
  */
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");
92
+ function claimText(data, t) {
93
+ if (data?.claimed === true) return t("claimDone", { amount: formatCredits(data.amount ?? 0) });
94
+ if (data?.state === "already") return t("claimAlready");
95
+ if (data?.state === "unavailable") return t("claimUnavailable");
96
+ return t("claimFailed", { message: typeof data?.message === "string" && data.message !== "" ? data.message : "request failed" });
102
97
  }
103
98
  /**
104
99
  * 积分区块。
@@ -107,9 +102,10 @@ function openSignInPage() {
107
102
  * 所以这里能直接显示账号总额度 —— 与下面的「本机用量」互补:一个是账号级、一个是
108
103
  * 本机级。拉不到时显示原因(creditsError),不静默吞掉。
109
104
  */
110
- function CreditsSection({ credits, error, t }) {
105
+ function CreditsSection({ credits, error, t, onClaim, claiming, claimResult, justClaimed }) {
111
106
  if (credits === void 0 && error === void 0) return null;
112
107
  const c = credits?.credits;
108
+ const claimedToday = justClaimed || credits?.welfare?.claimedToday === true;
113
109
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
114
110
  className: CodeArtsPluginCard_module_css_default.section,
115
111
  children: [
@@ -129,8 +125,9 @@ function CreditsSection({ credits, error, t }) {
129
125
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
130
126
  variant: "outline",
131
127
  size: "sm",
132
- onClick: () => openSignInPage(),
133
- children: t("signInButton")
128
+ disabled: claiming || claimedToday,
129
+ onClick: onClaim,
130
+ children: claiming ? t("claimPending") : claimedToday ? t("claimButtonDone") : t("claimButton")
134
131
  })]
135
132
  })]
136
133
  }),
@@ -140,7 +137,7 @@ function CreditsSection({ credits, error, t }) {
140
137
  }),
141
138
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
142
139
  className: CodeArtsPluginCard_module_css_default.hint,
143
- children: t("signInHint")
140
+ children: claimResult ?? t("claimHint")
144
141
  }),
145
142
  c !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
146
143
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -311,6 +308,30 @@ function CodeArtsPluginCard({ t }) {
311
308
  const [modelError, setModelError] = (0, react.useState)(null);
312
309
  /** 账号身份是否展开:默认遮蔽(false = 显示 *******)。 */
313
310
  const [accountsRevealed, setAccountsRevealed] = (0, react.useState)(false);
311
+ const [claiming, setClaiming] = (0, react.useState)(false);
312
+ const [claimResult, setClaimResult] = (0, react.useState)(null);
313
+ const [justClaimed, setJustClaimed] = (0, react.useState)(false);
314
+ /**
315
+ * 领一次每日签到积分。
316
+ *
317
+ * 除了这一次刚领成,「今天已领」都来自上游的福利活动状态(积分快照里的 welfare),
318
+ * 所以刷新、重开窗口都不会把按钮说错。领完不记日期:去重在上游按天做。
319
+ */
320
+ async function claim() {
321
+ setClaiming(true);
322
+ try {
323
+ const data = await (await fetch("/api/codearts/claim", { method: "POST" })).json().catch(() => null);
324
+ setClaimResult(claimText(data, t));
325
+ if (data?.claimed === true) {
326
+ setJustClaimed(true);
327
+ await refresh();
328
+ }
329
+ } catch (error) {
330
+ setClaimResult(t("claimFailed", { message: error instanceof Error ? error.message : String(error) }));
331
+ } finally {
332
+ setClaiming(false);
333
+ }
334
+ }
314
335
  async function refresh() {
315
336
  try {
316
337
  const data = await (await fetch("/api/codearts/status", { headers: { Accept: "application/json" } })).json();
@@ -763,7 +784,11 @@ function CodeArtsPluginCard({ t }) {
763
784
  signedIn && (status?.credits !== void 0 || status?.creditsError !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreditsSection, {
764
785
  credits: status?.credits,
765
786
  error: status?.creditsError,
766
- t
787
+ t,
788
+ onClaim: () => void claim(),
789
+ claiming,
790
+ claimResult,
791
+ justClaimed
767
792
  }),
768
793
  status?.usage !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageSection, {
769
794
  usage: status.usage,
@@ -930,8 +955,14 @@ const zh = {
930
955
  creditsUnavailable: "积分不可用:{message}",
931
956
  creditsDock: "积分 {remain}",
932
957
  creditsDockTitle: "CodeArts 剩余积分 {remain} / {total}",
933
- signInButton: "去签到领积分",
934
- signInHint: "积分由 CodeArts 官网活动页发放,签到要在官网页面完成(和官方客户端同一入口)。"
958
+ claimButton: "签到领积分",
959
+ claimButtonDone: "今日已领",
960
+ claimPending: "领取中…",
961
+ claimHint: "每天领一次 1000 积分,走官方福利中心接口(与客户端里那个「每日签到」同一件事)。",
962
+ claimDone: "已领取 {amount} 积分",
963
+ claimAlready: "今日已领,明天再来",
964
+ claimUnavailable: "这个账号当前没有可领的签到活动",
965
+ claimFailed: "领取失败:{message}"
935
966
  };
936
967
  const en = {
937
968
  tabLabel: "CodeArts",
@@ -1000,8 +1031,14 @@ const en = {
1000
1031
  creditsUnavailable: "Credits unavailable: {message}",
1001
1032
  creditsDock: "Credits {remain}",
1002
1033
  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)."
1034
+ claimButton: "Claim daily credits",
1035
+ claimButtonDone: "Claimed today",
1036
+ claimPending: "Claiming…",
1037
+ claimHint: "Claim 1,000 credits once a day through CodeArts’ welfare centre — the same check-in the official client does.",
1038
+ claimDone: "Claimed {amount} credits",
1039
+ claimAlready: "Already claimed today — come back tomorrow",
1040
+ claimUnavailable: "No check-in campaign available for this account",
1041
+ claimFailed: "Claim failed: {message}"
1005
1042
  };
1006
1043
  //#endregion
1007
1044
  //#region src/client/index.tsx
package/lib/index.d.mts CHANGED
@@ -581,10 +581,24 @@ interface CodeArtsTokenQuota {
581
581
  /** 总额度。 */
582
582
  quota: number;
583
583
  }
584
+ /** 每日签到活动当前状态(来自福利中心 `/v1/ops/delivery`)。 */
585
+ interface CodeArtsDailyWelfare {
586
+ /** 上游说这条活动现在点得动吗。 */
587
+ claimable: boolean;
588
+ /**
589
+ * 「今天已经领过了」。单独判出来给卡片改按钮文案用 —— 不可领的原因有好几种
590
+ * (已领 / 要先去官网登录 / 账号没资格),只有这一种该把按钮变成「今日已领」。
591
+ */
592
+ claimedToday: boolean;
593
+ /** 领一次给多少积分。 */
594
+ amount: number;
595
+ }
584
596
  interface CodeArtsCreditSnapshot {
585
597
  credits: CodeArtsCredits;
586
598
  /** 拿不到(接口失败/不支持)时为 undefined,不影响积分本身。 */
587
599
  tokens?: CodeArtsTokenQuota;
600
+ /** 没有这个活动(或账号是 token 套餐)时为 undefined。 */
601
+ welfare?: CodeArtsDailyWelfare;
588
602
  }
589
603
  /** 缓存里的一个结果:成功给值,失败给消息 —— 失败也要能被卡片看见。 */
590
604
  type CodeArtsCreditState = {
package/lib/index.mjs CHANGED
@@ -1,4 +1,4 @@
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";
1
+ import { C as isBenefitModel, D as codeartsAuthPath, E as codeartsAccountsPath, O as startLogin, S as filterEnabledModels, T as CodeArtsCredentialStore, _ as pickDailyWelfareCampaign, a as SNAP_HOST, b as CodeArtsCatalog, c as claimDailyWelfare, d as fetchModels, f as fetchWelfareCampaigns, g as maxTokensCeilingFor, h as isWelfareClaimedStatus, i as refreshCatalog, l as degradeTokenCeiling, m as isParamInvalidFrame, n as codeartsUsagePath, o as canonicalModel, p as isConcurrencyCap, r as discoverModels, s as chatStream, t as CodeArtsUsageTracker, u as fetchCallerIdentity, v as prepareChatBody, w as CodeArtsAccountStore, x as FALLBACK_CODEARTS_MODELS, y as signedGet } from "./usage-B4vQb1sP.mjs";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { randomBytes, timingSafeEqual } from "node:crypto";
4
4
  import { createServer } from "node:http";
@@ -273,18 +273,31 @@ function errorOf(action, result) {
273
273
  * 它失败只让 `tokens` 缺席 —— 别让一个次要接口把主数字一起拖没。
274
274
  */
275
275
  async function fetchCredits(credential, signal) {
276
- const [statistics, balance] = await Promise.all([signedGet(`${SNAP_HOST}${EP_STATISTICS_PLUGIN}`, credential, void 0, signal), signedGet(`${BENEFIT_GATEWAY_HOST}${EP_TOKEN_BALANCE}`, credential, void 0, signal).catch(() => ({
277
- ok: false,
278
- status: 0,
279
- message: "request failed"
280
- }))]);
276
+ const [statistics, balance, welfare] = await Promise.all([
277
+ signedGet(`${SNAP_HOST}${EP_STATISTICS_PLUGIN}`, credential, void 0, signal),
278
+ signedGet(`${BENEFIT_GATEWAY_HOST}${EP_TOKEN_BALANCE}`, credential, void 0, signal).catch(() => ({
279
+ ok: false,
280
+ status: 0,
281
+ message: "request failed"
282
+ })),
283
+ fetchWelfareCampaigns(credential, signal).then((campaigns) => {
284
+ const daily = pickDailyWelfareCampaign(campaigns);
285
+ if (daily === void 0) return void 0;
286
+ return {
287
+ claimable: daily.claimable,
288
+ claimedToday: !daily.claimable && isWelfareClaimedStatus(daily.status),
289
+ amount: daily.amount
290
+ };
291
+ }).catch(() => void 0)
292
+ ]);
281
293
  if (!statistics.ok) throw errorOf("statistics", statistics);
282
294
  const credits = parseCreditStatistics(safeJson(statistics.text));
283
295
  if (credits === void 0) throw new Error("statistics response carries no credit metrics");
284
296
  const tokens = balance.ok ? parseTokenBalance(safeJson(balance.text)) : void 0;
285
297
  return {
286
298
  credits,
287
- ...tokens === void 0 ? {} : { tokens }
299
+ ...tokens === void 0 ? {} : { tokens },
300
+ ...welfare === void 0 ? {} : { welfare }
288
301
  };
289
302
  }
290
303
  function safeJson(text) {
@@ -999,7 +1012,7 @@ const CODEARTS_LOGIN_PATH = "/api/codearts/login";
999
1012
  const CODEARTS_LOGIN_STATUS_PATH = "/api/codearts/login/status";
1000
1013
  /** 删除某个账号:DELETE /api/codearts/accounts?id=<accountId> */
1001
1014
  const CODEARTS_ACCOUNTS_PATH = "/api/codearts/accounts";
1002
- /** POST /api/codearts/claim — 开通「限时福利」免费模型渠道(幂等);**不发积分**。 */
1015
+ /** POST /api/codearts/claim — 领一次福利中心的每日签到积分(幂等由上游按天去重)。 */
1003
1016
  const CODEARTS_CLAIM_PATH = "/api/codearts/claim";
1004
1017
  /**
1005
1018
  * 由完整目录 + 已存 enabled 计算 selection。
@@ -1333,7 +1346,7 @@ function accountsHandler(deps) {
1333
1346
  }
1334
1347
  };
1335
1348
  }
1336
- /** POST /api/codearts/claim — 开通「限时福利」免费模型渠道(幂等);**不发积分**。 */
1349
+ /** POST /api/codearts/claim — 领一次每日签到积分;未领到也回 200(看 claimed/state)。 */
1337
1350
  function claimHandler(deps) {
1338
1351
  return async (req, res) => {
1339
1352
  if (req.method !== "POST") {
@@ -1350,14 +1363,13 @@ function claimHandler(deps) {
1350
1363
  }
1351
1364
  try {
1352
1365
  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
- });
1366
+ json(res, result.ok ? 200 : 500, result);
1358
1367
  } catch (error) {
1359
1368
  json(res, 500, {
1360
1369
  ok: false,
1370
+ claimed: false,
1371
+ state: "failed",
1372
+ amount: 0,
1361
1373
  message: safeMessage(error)
1362
1374
  });
1363
1375
  }
@@ -1654,16 +1666,29 @@ function apply(ctx, rawConfig) {
1654
1666
  if (stored === void 0 || stored.stale === true) continue;
1655
1667
  const resolved = await store.resolve(id);
1656
1668
  if (resolved === void 0) continue;
1657
- await claimBenefit(resolved, AbortSignal.timeout(1e4));
1658
- return { ok: true };
1669
+ const outcome = await claimDailyWelfare(resolved, AbortSignal.timeout(2e4));
1670
+ if (outcome.state === "claimed") credits.invalidate();
1671
+ return {
1672
+ ok: outcome.state !== "failed",
1673
+ claimed: outcome.state === "claimed",
1674
+ state: outcome.state,
1675
+ amount: outcome.amount,
1676
+ message: outcome.message
1677
+ };
1659
1678
  }
1660
1679
  return {
1661
1680
  ok: false,
1681
+ claimed: false,
1682
+ state: "failed",
1683
+ amount: 0,
1662
1684
  message: "no usable credential"
1663
1685
  };
1664
1686
  } catch (error) {
1665
1687
  return {
1666
1688
  ok: false,
1689
+ claimed: false,
1690
+ state: "failed",
1691
+ amount: 0,
1667
1692
  message: error instanceof Error ? error.message : String(error)
1668
1693
  };
1669
1694
  }
@@ -1168,13 +1168,17 @@ var upstream_exports = /* @__PURE__ */ __exportAll({
1168
1168
  canonicalModel: () => canonicalModel,
1169
1169
  chatStream: () => chatStream,
1170
1170
  claimBenefit: () => claimBenefit,
1171
+ claimDailyWelfare: () => claimDailyWelfare,
1171
1172
  degradeTokenCeiling: () => degradeTokenCeiling,
1172
1173
  estimateTokens: () => estimateTokens,
1173
1174
  fetchCallerIdentity: () => fetchCallerIdentity,
1174
1175
  fetchModels: () => fetchModels,
1176
+ fetchWelfareCampaigns: () => fetchWelfareCampaigns,
1175
1177
  isConcurrencyCap: () => isConcurrencyCap,
1176
1178
  isParamInvalidFrame: () => isParamInvalidFrame,
1179
+ isWelfareClaimedStatus: () => isWelfareClaimedStatus,
1177
1180
  maxTokensCeilingFor: () => maxTokensCeilingFor,
1181
+ pickDailyWelfareCampaign: () => pickDailyWelfareCampaign,
1178
1182
  prepareChatBody: () => prepareChatBody,
1179
1183
  signedGet: () => signedGet
1180
1184
  });
@@ -1921,6 +1925,181 @@ async function claimBenefit(credential, abort) {
1921
1925
  }
1922
1926
  if (json.error_code !== "0000") throw new Error(`claim benefit rejected: ${json.error_code ?? "(no error_code)"} ${json.error_msg ?? ""}`.trim());
1923
1927
  }
1928
+ /**
1929
+ * 福利中心(活动积分)接口:26.9.901 起应用内「每日签到领 1000 积分」走这套。
1930
+ *
1931
+ * host 与积分统计是同一个 snap-access 网关,路径前缀 `/v1/ops`,鉴权头对齐官方客户端
1932
+ * 的 `Pz()`(`x-auth-token` + `Agent-Type: PromptCenter`),再补上我们直连必须的
1933
+ * AK/SK 签名。三个实测出来的坑:
1934
+ *
1935
+ * 1. bundle 里 URL 有两套变体,带前缀的 `/PromptCenterService/v1/ops/*` 在这个网关上
1936
+ * 回 **404 APIG.0101(未发布)** —— 只有裸 `/v1/ops/*` 能用;
1937
+ * 2. 只带 `x-auth-token` 会被拒(`APIG.0301 decrypt token fail`);
1938
+ * 3. **业务失败也回 HTTP 200**,成败只看 body 的 `code`(0 才是成功)。
1939
+ * 今天已领时是 `code:40001 尚未到达权益刷新时间,暂时无法领取`。
1940
+ *
1941
+ * 领的是「活动」而不是额度:`/ops/delivery` 给活动清单(含 campaignId / status /
1942
+ * claimable),`/ops/claim` 按 campaignId 领。`idempotentKey` 上游不返回,官方客户端
1943
+ * 自己生成随机串(与 `x-snap-traceid` 同一个生成器),按天去重落在服务端。
1944
+ */
1945
+ const EP_OPS_DELIVERY = "/v1/ops/delivery";
1946
+ const EP_OPS_CLAIM = "/v1/ops/claim";
1947
+ /** 每日签到活动的 type(实测为 campaignId 1,benefitAmount 1000 CREDIT)。 */
1948
+ const WELFARE_DAILY_TYPE = "USER_LOGIN";
1949
+ /** 上游按钮文案的状态机:这几个都表示「今天已经领过了」。 */
1950
+ const WELFARE_ALREADY_STATES = /* @__PURE__ */ new Set([
1951
+ "CLAIMED",
1952
+ "CONFIRMED",
1953
+ "CONSUMED"
1954
+ ]);
1955
+ /** 活动状态是不是「今天已经领过了」(区别于「要先登录」「没资格」这些同样不可领的情况)。 */
1956
+ function isWelfareClaimedStatus(status) {
1957
+ return WELFARE_ALREADY_STATES.has(status);
1958
+ }
1959
+ /**
1960
+ * 「今日已领」的业务码。
1961
+ *
1962
+ * 这个分支不是冗余的:领取成功后的几十秒里 delivery 有可能还回一份**陈旧**的
1963
+ * `claimable:true / ELIGIBLE`(实测 2026-10-16,领完立刻读会看到),于是会真的
1964
+ * POST 第二次 —— 服务端按天去重回这个码,所以按「已领」处理,不能当失败。
1965
+ */
1966
+ const WELFARE_CODE_NOT_YET = 40001;
1967
+ /** 福利接口的请求:签名 + 官方那套头,返回解析后的 body。 */
1968
+ async function welfareRequest(url, method, credential, body, signal) {
1969
+ const req = new Request(url, {
1970
+ method,
1971
+ headers: {
1972
+ "Content-Type": "application/json",
1973
+ "X-Language": "zh-cn",
1974
+ "x-snap-traceid": hex32(),
1975
+ "x-auth-token": credential.token,
1976
+ "Agent-Type": "PromptCenter",
1977
+ "X-Security-Token": credential.securityToken
1978
+ },
1979
+ ...body === void 0 ? {} : { body }
1980
+ });
1981
+ const bodyBuf = Buffer.from(body ?? "");
1982
+ if (credential.accessKeyId !== void 0 && credential.secretAccessKey !== void 0) signRequest(req, bodyBuf, {
1983
+ accessKeyId: credential.accessKeyId,
1984
+ secretAccessKey: credential.secretAccessKey,
1985
+ securityToken: credential.securityToken
1986
+ });
1987
+ let resp;
1988
+ try {
1989
+ resp = await fetch(req, { signal });
1990
+ } catch (error) {
1991
+ return {
1992
+ ok: false,
1993
+ message: error instanceof Error ? error.message : String(error)
1994
+ };
1995
+ }
1996
+ const text = await resp.text().catch(() => "");
1997
+ if (!resp.ok) return {
1998
+ ok: false,
1999
+ message: `HTTP ${resp.status} ${text.slice(0, 200)}`
2000
+ };
2001
+ let parsed;
2002
+ try {
2003
+ parsed = JSON.parse(text);
2004
+ } catch {
2005
+ return {
2006
+ ok: false,
2007
+ message: `unexpected non-JSON response: ${text.slice(0, 200)}`
2008
+ };
2009
+ }
2010
+ const envelope = asRecord(parsed);
2011
+ return {
2012
+ ok: true,
2013
+ code: typeof envelope.code === "number" ? envelope.code : -1,
2014
+ message: firstString(envelope.message),
2015
+ data: envelope.data
2016
+ };
2017
+ }
2018
+ /** 活动清单(`GET /v1/ops/delivery?channel=IDE`)。 */
2019
+ async function fetchWelfareCampaigns(credential, signal) {
2020
+ const resp = await welfareRequest(`${SNAP_HOST}${EP_OPS_DELIVERY}?channel=IDE`, "GET", credential, void 0, signal);
2021
+ if (!resp.ok) throw new Error(`welfare delivery: ${resp.message}`);
2022
+ return asArray(asRecord(resp.data).items).flatMap((item) => {
2023
+ const raw = asRecord(item);
2024
+ const campaignId = typeof raw.campaignId === "number" ? raw.campaignId : NaN;
2025
+ if (Number.isFinite(campaignId) === false) return [];
2026
+ return [{
2027
+ campaignId,
2028
+ title: firstString(raw.title),
2029
+ type: firstString(raw.type),
2030
+ amount: typeof raw.benefitAmount === "number" ? raw.benefitAmount : 0,
2031
+ unit: firstString(raw.benefitUnit),
2032
+ claimable: raw.claimable === true,
2033
+ status: firstString(raw.status)
2034
+ }];
2035
+ });
2036
+ }
2037
+ /**
2038
+ * 从活动清单里挑「每日签到」那条。
2039
+ *
2040
+ * 同一个 type 理论上可能有多条(上游换活动的时候),优先挑当前可领的那条。
2041
+ */
2042
+ function pickDailyWelfareCampaign(campaigns) {
2043
+ const daily = campaigns.filter((c) => c.type === WELFARE_DAILY_TYPE && c.unit === "CREDIT");
2044
+ return daily.find((c) => c.claimable) ?? daily[0];
2045
+ }
2046
+ /**
2047
+ * 领一次每日签到积分。
2048
+ *
2049
+ * 不抛异常:这是卡片上一个按钮的后续,失败要变成能显示的文案而不是断掉的请求。
2050
+ */
2051
+ async function claimDailyWelfare(credential, signal) {
2052
+ if (credential.accessKeyId === void 0 || credential.secretAccessKey === void 0) return {
2053
+ state: "failed",
2054
+ amount: 0,
2055
+ message: "no AK/SK to sign"
2056
+ };
2057
+ let campaigns;
2058
+ try {
2059
+ campaigns = await fetchWelfareCampaigns(credential, signal);
2060
+ } catch (error) {
2061
+ return {
2062
+ state: "failed",
2063
+ amount: 0,
2064
+ message: error instanceof Error ? error.message : String(error)
2065
+ };
2066
+ }
2067
+ const target = pickDailyWelfareCampaign(campaigns);
2068
+ if (target === void 0) return {
2069
+ state: "unavailable",
2070
+ amount: 0,
2071
+ message: "no daily check-in campaign"
2072
+ };
2073
+ if (!target.claimable) return isWelfareClaimedStatus(target.status) ? {
2074
+ state: "already",
2075
+ amount: 0,
2076
+ message: ""
2077
+ } : {
2078
+ state: "unavailable",
2079
+ amount: 0,
2080
+ message: target.status
2081
+ };
2082
+ const resp = await welfareRequest(`${SNAP_HOST}${EP_OPS_CLAIM}`, "POST", credential, JSON.stringify({
2083
+ campaignId: target.campaignId,
2084
+ idempotentKey: hex32(),
2085
+ channel: "IDE"
2086
+ }), signal);
2087
+ if (!resp.ok) return {
2088
+ state: "failed",
2089
+ amount: 0,
2090
+ message: resp.message
2091
+ };
2092
+ if (resp.code === 0) return {
2093
+ state: "claimed",
2094
+ amount: target.amount,
2095
+ message: resp.message
2096
+ };
2097
+ return {
2098
+ state: resp.code === WELFARE_CODE_NOT_YET ? "already" : "failed",
2099
+ amount: 0,
2100
+ message: resp.message
2101
+ };
2102
+ }
1924
2103
  //#endregion
1925
2104
  //#region src/discovery.ts
1926
2105
  /**
@@ -2225,4 +2404,4 @@ function sumOf(store) {
2225
2404
  return out;
2226
2405
  }
2227
2406
  //#endregion
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 };
2407
+ export { isBenefitModel as C, codeartsAuthPath as D, codeartsAccountsPath as E, startLogin as O, filterEnabledModels as S, CodeArtsCredentialStore as T, pickDailyWelfareCampaign as _, SNAP_HOST as a, CodeArtsCatalog as b, claimDailyWelfare as c, fetchModels as d, fetchWelfareCampaigns as f, maxTokensCeilingFor as g, isWelfareClaimedStatus as h, refreshCatalog as i, degradeTokenCeiling as l, isParamInvalidFrame as m, codeartsUsagePath as n, canonicalModel as o, isConcurrencyCap as p, discoverModels as r, chatStream as s, CodeArtsUsageTracker as t, fetchCallerIdentity as u, prepareChatBody as v, CodeArtsAccountStore as w, FALLBACK_CODEARTS_MODELS as x, signedGet 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.4.1",
5
+ "version": "0.4.2",
6
6
  "author": "elegant01",
7
7
  "keywords": [
8
8
  "dsh-plugin",