dsh-codearts 1.2.10 → 1.2.11

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.
@@ -565,7 +565,9 @@ function summarizeAccount(id, cred) {
565
565
  ...cred.expiration === void 0 ? {} : { expiration: cred.expiration },
566
566
  ...cred.storedAt === void 0 ? {} : { storedAt: cred.storedAt },
567
567
  stale: cred.stale === true,
568
- signable: typeof cred.accessKeyId === "string" && cred.accessKeyId !== "" && typeof cred.secretAccessKey === "string" && cred.secretAccessKey !== ""
568
+ signable: typeof cred.accessKeyId === "string" && cred.accessKeyId !== "" && typeof cred.secretAccessKey === "string" && cred.secretAccessKey !== "",
569
+ paused: cred.paused === true,
570
+ pinned: cred.pinned === true
569
571
  };
570
572
  }
571
573
  /**
@@ -840,6 +842,58 @@ var CodeArtsAccountStore = class {
840
842
  doc.order = doc.order.filter((existing) => existing !== id);
841
843
  await this.persistDoc();
842
844
  }
845
+ /**
846
+ * 暂停 / 恢复一个账号。
847
+ *
848
+ * 只改标记,不碰凭证:恢复时不需要重新登录。重新装配槽位是因为 AccountSlot
849
+ * 缓存的 `_cred` 是旧对象,续期回写会把它盖回来 —— 换了对象,续期写到新文档里。
850
+ */
851
+ async setPaused(id, paused) {
852
+ await this.loaded();
853
+ const doc = this._doc;
854
+ const cred = doc.accounts[id];
855
+ if (cred === void 0) return false;
856
+ doc.accounts[id] = {
857
+ ...cred,
858
+ paused
859
+ };
860
+ this._slots.get(id)?.invalidate();
861
+ this._slots.set(id, new AccountSlot(doc.accounts[id], this.persisterFor(id)));
862
+ await this.persistDoc();
863
+ return true;
864
+ }
865
+ /** 置顶 / 取消置顶:只改变轮转先后,不绕过冷却与并发上限。 */
866
+ async setPinned(id, pinned) {
867
+ await this.loaded();
868
+ const doc = this._doc;
869
+ const cred = doc.accounts[id];
870
+ if (cred === void 0) return false;
871
+ doc.accounts[id] = {
872
+ ...cred,
873
+ pinned
874
+ };
875
+ this._slots.get(id)?.invalidate();
876
+ this._slots.set(id, new AccountSlot(doc.accounts[id], this.persisterFor(id)));
877
+ await this.persistDoc();
878
+ return true;
879
+ }
880
+ /** shim 用:参与轮转的账号 id(排除用户暂停的)。 */
881
+ async dispatchIds() {
882
+ const paused = new Set(await this.pausedIds());
883
+ return (await this.ids()).filter((id) => !paused.has(id));
884
+ }
885
+ /** 被用户暂停的账号 id(账号池据此保留状态但不派活)。 */
886
+ async pausedIds() {
887
+ await this.loaded();
888
+ const doc = this._doc;
889
+ return (await this.ids()).filter((id) => doc.accounts[id]?.paused === true);
890
+ }
891
+ /** 置顶账号的 id 列表(给账号池排序用)。 */
892
+ async pinnedIds() {
893
+ await this.loaded();
894
+ const doc = this._doc;
895
+ return (await this.ids()).filter((id) => doc.accounts[id]?.pinned === true);
896
+ }
843
897
  /** 清空全部账号(整体登出)。 */
844
898
  async clear() {
845
899
  await this.loaded();
@@ -2405,6 +2459,7 @@ async function fetchModels(credential, signal) {
2405
2459
  return {
2406
2460
  ...model,
2407
2461
  ...thinking === void 0 ? {} : { thinking },
2462
+ contextWindowKnown: model.contextWindow > 0,
2408
2463
  contextWindow: model.contextWindow > 0 ? model.contextWindow : UNKNOWN_CONTEXT_WINDOW,
2409
2464
  maxTokens: Math.min(model.maxTokens > 0 ? model.maxTokens : UNKNOWN_MAX_TOKENS, maxTokensCeilingFor(model.id))
2410
2465
  };
@@ -2742,6 +2797,21 @@ function codeartsUsagePath(home) {
2742
2797
  const base = home ?? process.env.DSH_HOME ?? join(homedir(), ".dsh");
2743
2798
  return join(base, ".codearts-usage.json");
2744
2799
  }
2800
+ /** 首字延迟分桶的上界(毫秒);落进最后一档表示「超过 30 秒」。 */
2801
+ const TTFT_BUCKETS_MS = [
2802
+ 200,
2803
+ 500,
2804
+ 1e3,
2805
+ 2e3,
2806
+ 3e3,
2807
+ 5e3,
2808
+ 8e3,
2809
+ 12e3,
2810
+ 2e4,
2811
+ 3e4
2812
+ ];
2813
+ /** 直方图桶数(含溢出档)。 */
2814
+ const TTFT_BUCKET_COUNT = TTFT_BUCKETS_MS.length + 1;
2745
2815
  function emptyTotals() {
2746
2816
  return {
2747
2817
  requests: 0,
@@ -2847,6 +2917,22 @@ function normalizeStore(store) {
2847
2917
  }
2848
2918
  return out;
2849
2919
  }
2920
+ /** 计数表(失败类别 / 账号失败数)补齐:只留正数,空表给 undefined。 */
2921
+ function normalizeCounts(raw) {
2922
+ if (typeof raw !== "object" || raw === null) return void 0;
2923
+ const out = {};
2924
+ for (const [key, value] of Object.entries(raw)) {
2925
+ const count = positiveInt(value);
2926
+ if (count > 0) out[key] = count;
2927
+ }
2928
+ return Object.keys(out).length === 0 ? void 0 : out;
2929
+ }
2930
+ /** 首字延迟直方图补齐到固定桶数;旧账本/脏数组一律按空表。 */
2931
+ function normalizeBuckets(raw) {
2932
+ if (!Array.isArray(raw)) return void 0;
2933
+ const out = Array.from({ length: TTFT_BUCKET_COUNT }, (_, index) => positiveInt(raw[index]));
2934
+ return out.some((count) => count > 0) ? out : void 0;
2935
+ }
2850
2936
  /**
2851
2937
  * 用量账本。
2852
2938
  *
@@ -2885,17 +2971,19 @@ var CodeArtsUsageTracker = class {
2885
2971
  if (parsed.version !== 1 || typeof parsed.days !== "object" || parsed.days === null) return;
2886
2972
  for (const [date, day] of Object.entries(parsed.days)) {
2887
2973
  if (typeof day !== "object" || day === null) continue;
2888
- const models = normalizeStore(day.models);
2889
- const accounts = normalizeStore(day.accounts);
2890
- const hours = day.hours;
2891
- this.days.set(date, hours === void 0 ? {
2892
- models,
2893
- accounts
2894
- } : {
2895
- models,
2896
- accounts,
2897
- hours: normalizeStore(hours)
2898
- });
2974
+ const stored = day;
2975
+ const restored = {
2976
+ models: normalizeStore(stored.models),
2977
+ accounts: normalizeStore(stored.accounts)
2978
+ };
2979
+ if (stored.hours !== void 0) restored.hours = normalizeStore(stored.hours);
2980
+ const failures = normalizeCounts(stored.failures);
2981
+ if (failures !== void 0) restored.failures = failures;
2982
+ const failuresByAccount = normalizeCounts(stored.failuresByAccount);
2983
+ if (failuresByAccount !== void 0) restored.failuresByAccount = failuresByAccount;
2984
+ const ttft = normalizeBuckets(stored.ttft);
2985
+ if (ttft !== void 0) restored.ttft = ttft;
2986
+ this.days.set(date, restored);
2899
2987
  }
2900
2988
  this.prune(this.now());
2901
2989
  } catch {
@@ -2951,6 +3039,42 @@ var CodeArtsUsageTracker = class {
2951
3039
  return day.hours;
2952
3040
  }
2953
3041
  /**
3042
+ * 记一次失败请求。
3043
+ *
3044
+ * 只在「这次请求最终回给用户一个错误」时调用一次 —— 插件内部换号重试不算失败,
3045
+ * 否则一个排队重试就能把失败率刷成好几倍。`kind` 用 shim 的失败出口标识。
3046
+ */
3047
+ noteFailure(kind, accountId, at = this.now()) {
3048
+ if (kind === "") return;
3049
+ const day = this.day(localDateKey(at));
3050
+ day.failures ??= {};
3051
+ day.failures[kind] = (day.failures[kind] ?? 0) + 1;
3052
+ if (accountId !== void 0 && accountId !== "") {
3053
+ day.failuresByAccount ??= {};
3054
+ day.failuresByAccount[accountId] = (day.failuresByAccount[accountId] ?? 0) + 1;
3055
+ }
3056
+ this.lastAt = at;
3057
+ this.prune(at);
3058
+ this.scheduleSave();
3059
+ }
3060
+ /**
3061
+ * 记一次首字延迟(毫秒),落进直方图。
3062
+ *
3063
+ * 从请求进入 shim 算起,到上游第一帧真的写给 DSH 为止 —— 排队和换号的时间都算进去,
3064
+ * 因为那正是用户盯着转圈等掉的时长。
3065
+ */
3066
+ noteLatency(ms, at = this.now()) {
3067
+ if (!Number.isFinite(ms) || ms < 0) return;
3068
+ const day = this.day(localDateKey(at));
3069
+ day.ttft ??= new Array(TTFT_BUCKET_COUNT).fill(0);
3070
+ let index = TTFT_BUCKETS_MS.findIndex((limit) => ms <= limit);
3071
+ if (index < 0) index = TTFT_BUCKET_COUNT - 1;
3072
+ day.ttft[index] += 1;
3073
+ this.lastAt = at;
3074
+ this.prune(at);
3075
+ this.scheduleSave();
3076
+ }
3077
+ /**
2954
3078
  * 丢弃保留期之外的天(按日期键比较即可,YYYY-MM-DD 是字典序)。
2955
3079
  *
2956
3080
  * 顺带把两天之前的**小时桶**清掉:逐小时数据只有「今天按小时」在画,留着只是
@@ -2975,6 +3099,10 @@ var CodeArtsUsageTracker = class {
2975
3099
  if (date >= cutoff) mergeTotals(last30, totals);
2976
3100
  }
2977
3101
  const models = today?.models ?? {};
3102
+ const failuresByKind = today?.failures ?? {};
3103
+ const ttft = today?.ttft ?? [];
3104
+ const p50 = percentileOf(ttft, .5);
3105
+ const p95 = percentileOf(ttft, .95);
2978
3106
  return {
2979
3107
  today: {
2980
3108
  ...emptyTotals(),
@@ -2982,6 +3110,17 @@ var CodeArtsUsageTracker = class {
2982
3110
  },
2983
3111
  allTime,
2984
3112
  last30,
3113
+ failuresToday: {
3114
+ total: Object.values(failuresByKind).reduce((sum, count) => sum + count, 0),
3115
+ byKind: { ...failuresByKind }
3116
+ },
3117
+ failuresByAccountToday: { ...today?.failuresByAccount ?? {} },
3118
+ latencyToday: {
3119
+ samples: ttft.reduce((sum, count) => sum + count, 0),
3120
+ ...p50.value === void 0 ? {} : { p50: p50.value },
3121
+ ...p95.value === void 0 ? {} : { p95: p95.value },
3122
+ overflow: p95.overflow
3123
+ },
2985
3124
  byModelToday: Object.entries(models).map(([id, totals]) => ({
2986
3125
  id,
2987
3126
  totals: { ...totals }
@@ -3051,5 +3190,29 @@ function sumOf(store) {
3051
3190
  for (const totals of Object.values(store)) mergeTotals(out, totals);
3052
3191
  return out;
3053
3192
  }
3193
+ /**
3194
+ * 直方图分位。
3195
+ *
3196
+ * 返回的是「落进的那一档的上界」而不是精确毫秒 —— 界面上显示 `≤200ms` 这种口径,
3197
+ * 比伪造一个精确值诚实。落进溢出档时 `value` 缺省、`overflow` 为真。
3198
+ */
3199
+ function percentileOf(buckets, ratio) {
3200
+ const total = buckets.reduce((sum, count) => sum + count, 0);
3201
+ if (total === 0) return { overflow: false };
3202
+ const target = Math.ceil(total * ratio);
3203
+ let seen = 0;
3204
+ for (let index = 0; index < buckets.length; index += 1) {
3205
+ seen += buckets[index] ?? 0;
3206
+ if (seen < target) continue;
3207
+ return index < TTFT_BUCKETS_MS.length ? {
3208
+ value: TTFT_BUCKETS_MS[index],
3209
+ overflow: false
3210
+ } : { overflow: true };
3211
+ }
3212
+ return { overflow: true };
3213
+ }
3214
+ //#endregion
3215
+ //#region src/version.ts
3216
+ const CODEARTS_VERSION = "1.2.11";
3054
3217
  //#endregion
3055
- export { upstreamErrorMessage as A, modelUnavailableReason as B, isWelfareClaimedStatus as C, prepareChatBody as D, pickDailyWelfareCampaign as E, filterEnabledModels as F, codeartsAuthPath as G, CodeArtsCredentialStore as H, forgetModelUnavailable as I, credentialFromOAuth as K, isBenefitBlocked as L, FALLBACK_CODEARTS_MODELS as M, clearBenefitBlocked as N, rateLimitFrame as O, creditRatioLabel as P, isBenefitModel as R, isTpmLimit as S, modelUnavailableReasonText as T, MANUAL_ACCOUNT_ID as U, CodeArtsAccountStore as V, codeartsAccountsPath as W, isIamTokenError as _, SNAP_HOST as a, isParamInvalidFrame as b, claimDailyWelfare as c, fetchCallerIdentity as d, fetchModels as f, isConcurrencyCap as g, firstFrameError as h, refreshCatalog as i, CodeArtsCatalog as j, signedGet as k, clearQueueStatus as l, fetchWelfareCampaigns as m, codeartsUsagePath as n, canonicalModel as o, fetchQueueStatus as p, startLogin as q, discoverModels as r, chatStream as s, CodeArtsUsageTracker as t, degradeTokenCeiling as u, isModelNotAccessible as v, maxTokensCeilingFor as w, isQueueableRate as x, isNotMultimodal as y, markModelUnavailable as z };
3218
+ export { signedGet as A, markModelUnavailable as B, isTpmLimit as C, pickDailyWelfareCampaign as D, modelUnavailableReasonText as E, creditRatioLabel as F, codeartsAccountsPath as G, CodeArtsAccountStore as H, filterEnabledModels as I, startLogin as J, codeartsAuthPath as K, forgetModelUnavailable as L, CodeArtsCatalog as M, FALLBACK_CODEARTS_MODELS as N, prepareChatBody as O, clearBenefitBlocked as P, isBenefitBlocked as R, isQueueableRate as S, maxTokensCeilingFor as T, CodeArtsCredentialStore as U, modelUnavailableReason as V, MANUAL_ACCOUNT_ID as W, isConcurrencyCap as _, refreshCatalog as a, isNotMultimodal as b, chatStream as c, degradeTokenCeiling as d, fetchCallerIdentity as f, firstFrameError as g, fetchWelfareCampaigns as h, discoverModels as i, upstreamErrorMessage as j, rateLimitFrame as k, claimDailyWelfare as l, fetchQueueStatus as m, CodeArtsUsageTracker as n, SNAP_HOST as o, fetchModels as p, credentialFromOAuth as q, codeartsUsagePath as r, canonicalModel as s, CODEARTS_VERSION as t, clearQueueStatus as u, isIamTokenError as v, isWelfareClaimedStatus as w, isParamInvalidFrame as x, isModelNotAccessible as y, isBenefitModel as z };
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": "1.2.10",
5
+ "version": "1.2.11",
6
6
  "packageManager": "pnpm@11.24.0",
7
7
  "author": "elegant01",
8
8
  "keywords": [