dsh-connect-workbuddy 5.7.0 → 5.8.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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as prepareChatBody, A as parseWorkBuddyAuth, At as readAtRestKey, B as declaredTools, C as defaultDesktopAuthCandidates, Ct as findWorkbuddyAppExecutable, D as isEncryptedCredentialError, Dt as macosBundleExecutable, E as hasEncryptedCredentialFields, Et as isWorkbuddyBundle, F as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, Ft as writeFileAtomic, G as isFirstMessageGateBody, H as isContextTooLongBody, I as WorkBuddyCredentialRejectedError, J as openingEvidenceOfBody, K as knownRegionOf, L as WorkBuddyUpstreamClient, M as workbuddyOwnAuthPath, N as CONTEXT_OVERFLOW_MARKER, Nt as resolveDshHome, O as isVaultAccountId, Ot as macosNestedAppBundles, P as CREDENTIAL_REJECTED_CODE, Pt as withFileLock, Q as perturbChannelFingerprints, R as channelGateMemberOf, S as authFileName, St as fetchAtRestKeyPayload, T as defaultDesktopAuthPath, Tt as isEncryptedFieldWrapper, U as isConversationIdGateBody, V as forceNeutralHead, W as isCredentialRejectedError, X as parseReasoning, Y as parseCreditMultiplier, Z as parseUpstreamModel, _ as ENCRYPTED_CREDENTIAL_CODE, _t as nativeModalityOf, a as readHostHeartbeat, at as lastRefusalFor, b as WorkBuddyCredentialStore, bt as deriveAtRestKey, c as WORKBUDDY_CONNECT_VERSION, ct as recordRefusal, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, dt as WorkBuddyImageError, et as regionOf, f as WorkBuddyCatalog, ft as pickImageModel, g as withoutRouters, gt as imageDefaultFor, h as fallbackModelsFor, ht as NATIVE_MODALITY_BY_MODEL_ID, i as processStartTimeMs, it as defaultOffModelIds, j as workbuddyAccountId, jt as workbuddyAppExecutableCandidates, k as legacyWorkbuddyOwnAuthPath, kt as openEncryptedField, l as piAiRuntimeInfo, lt as withEffectiveOff, m as deriveCatalog, mt as queueNoticeText, n as clearHostHeartbeat, nt as attachBusyLevels, o as workbuddyHostHeartbeatPath, ot as offDefaultFor, p as applyContextBudgets, pt as parseQueueAnswer, q as modelIdOfBody, r as isHeartbeatProcessAlive, rt as REFUSES_OFF_MODEL_IDS, s as writeHostHeartbeat, st as offRefused, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as requestEvidenceOf, u as FALLBACK_WORKBUDDY_MODELS, ut as DEFAULT_IMAGE_SIZE, v as WORKBUDDY_AUTH_FILENAME, vt as WORKBUDDY_APP_EXECUTABLE_ENV, w as defaultDesktopAuthDirs, x as WorkBuddyEncryptedCredentialError, xt as deriveAtRestKeyId, y as WORKBUDDY_AUTH_FILE_ENV, yt as clearAtRestKeyCache, z as classifyUpstreamError } from "./host-heartbeat-CVz2J0Xt.js";
1
+ import { $ as parseCreditMultiplier, A as isEncryptedCredentialError, At as isWorkbuddyBundle, B as WorkBuddyCredentialRejectedError, C as WorkBuddyCredentialStore, Ct as clearAtRestKeyCache, D as defaultDesktopAuthDirs, Dt as findWorkbuddyAppExecutable, E as defaultDesktopAuthCandidates, Et as fetchAtRestKeyPayload, F as workbuddyAccountId, Ft as workbuddyAppExecutableCandidates, G as forceNeutralHead, H as channelGateMemberOf, I as workbuddyOwnAuthPath, J as isCredentialRejectedError, K as isContextTooLongBody, L as CONTEXT_OVERFLOW_MARKER, Lt as resolveDshHome, M as legacyWorkbuddyOwnAuthPath, Mt as macosNestedAppBundles, N as parseWorkBuddyAuth, Nt as openEncryptedField, O as defaultDesktopAuthPath, P as preferredAccountName, Pt as readAtRestKey, Q as openingEvidenceOfBody, R as CREDENTIAL_REJECTED_CODE, Rt as withFileLock, S as WORKBUDDY_AUTH_FILE_ENV, St as WORKBUDDY_APP_EXECUTABLE_ENV, T as authFileName, Tt as deriveAtRestKeyId, U as classifyUpstreamError, V as WorkBuddyUpstreamClient, W as declaredTools, X as knownRegionOf, Y as isFirstMessageGateBody, Z as modelIdOfBody, _ as deriveCatalog, _t as parseQueueAnswer, a as readHostHeartbeat, at as requestEvidenceOf, b as ENCRYPTED_CREDENTIAL_CODE, bt as imageDefaultFor, c as WORKBUDDY_CONNECT_VERSION, ct as defaultOffModelIds, dt as offRefused, et as parseReasoning, f as piAiRuntimeInfo, ft as recordRefusal, g as applyContextBudgets, gt as pickImageModel, h as WorkBuddyCatalog, ht as WorkBuddyImageError, i as processStartTimeMs, it as regionOf, j as isVaultAccountId, jt as macosBundleExecutable, k as hasEncryptedCredentialFields, kt as isEncryptedFieldWrapper, l as createFileRosterSeenStore, lt as lastRefusalFor, m as FALLBACK_WORKBUDDY_MODELS_GLOBAL, mt as DEFAULT_IMAGE_SIZE, n as clearHostHeartbeat, nt as perturbChannelFingerprints, o as workbuddyHostHeartbeatPath, ot as attachBusyLevels, p as FALLBACK_WORKBUDDY_MODELS, pt as withEffectiveOff, q as isConversationIdGateBody, r as isHeartbeatProcessAlive, rt as prepareChatBody, s as writeHostHeartbeat, st as REFUSES_OFF_MODEL_IDS, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as parseUpstreamModel, ut as offDefaultFor, v as fallbackModelsFor, vt as queueNoticeText, w as WorkBuddyEncryptedCredentialError, wt as deriveAtRestKey, x as WORKBUDDY_AUTH_FILENAME, xt as nativeModalityOf, y as withoutRouters, yt as NATIVE_MODALITY_BY_MODEL_ID, z as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, zt as writeFileAtomic } from "./host-heartbeat-BiVFMXGE.js";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { appendFile, mkdir, open, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
4
4
  import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
@@ -3280,6 +3280,7 @@ const KIND_STATUS = {
3280
3280
  model_busy: 429,
3281
3281
  session_dead: 401,
3282
3282
  policy_reject: 403,
3283
+ account_restricted: 403,
3283
3284
  channel_reject: 400,
3284
3285
  model_queue: 429,
3285
3286
  not_found: 502,
@@ -3955,6 +3956,7 @@ function failoverReasonText(failure) {
3955
3956
  case "model_busy": return "该模型当前繁忙(不是账号问题)";
3956
3957
  case "session_dead": return "登录已失效";
3957
3958
  case "policy_reject": return "被服务端策略拒绝";
3959
+ case "account_restricted": return "账号被上游限制使用";
3958
3960
  case "channel_reject": return "被网关的渠道校验拦截(不是账号、额度或网络问题)";
3959
3961
  case "not_found": return "模型不可用";
3960
3962
  case "server": return "上游异常";
@@ -4219,6 +4221,14 @@ function createWorkBuddyShim(options) {
4219
4221
  const onRequestBody = options.onRequestBody;
4220
4222
  const onUpstreamUsage = options.onUpstreamUsage;
4221
4223
  const channelPerturbation = options.channelPerturbation ?? "failure";
4224
+ /**
4225
+ * Whether the harness's injected `user` blocks are moved above the real prompt.
4226
+ *
4227
+ * OFF by default (issue #132): the reorder changes the message ORDER the
4228
+ * upstream counts, so it must be the user's deliberate choice rather than a
4229
+ * silent difference after an upgrade.
4230
+ */
4231
+ const keepPromptLastEnabled = options.keepPromptLast === true;
4222
4232
  const SHARED_SECRET = randomBytes(32).toString("base64url");
4223
4233
  /**
4224
4234
  * Whether this process's prompts have been PROVEN to need the invisible
@@ -4372,7 +4382,7 @@ function createWorkBuddyShim(options) {
4372
4382
  return;
4373
4383
  }
4374
4384
  const raw = (await readBody(req)).toString("utf8");
4375
- const prepared = prepareChatBody(raw);
4385
+ const prepared = prepareChatBody(raw, { keepPromptLast: keepPromptLastEnabled });
4376
4386
  const requestModelId = modelIdOfBody(prepared);
4377
4387
  if (onRequestBody !== void 0) try {
4378
4388
  onRequestBody(prepared);
@@ -4534,13 +4544,22 @@ function createWorkBuddyShim(options) {
4534
4544
  writeOpenAIError(res, 429, "model_queue", `${queueNoticeText(queue)}(上游 code ${queue.code})${note}`, void 0, NO_RETRY_HEADERS);
4535
4545
  return;
4536
4546
  }
4547
+ if (result.kind === "account_restricted") {
4548
+ const detail = result.detail;
4549
+ const meta = ["账号被上游限制使用"];
4550
+ if (detail?.upstreamCode !== void 0) meta.push(`code ${detail.upstreamCode}`);
4551
+ if (detail?.requestId !== void 0) meta.push(`requestId ${detail.requestId}`);
4552
+ const message = `${detail?.displayMsg ?? clippedUpstreamText(result.message, 200)}(${meta.join(",")})${note}${why}。该账号已被 WorkBuddy 上游按账号限制,与本次请求内容、网络和额度无关;插件会记录该测量并改用其他可用账号。该限制是上游对该账号的判定:重新登录不改变插件认的这个账号,需要 WorkBuddy 官方渠道解除后,在卡片上手动「测试」该账号才会回池`;
4553
+ writeOpenAIError(res, KIND_STATUS[result.kind], result.kind, message, hostErrorCode(result.kind));
4554
+ return;
4555
+ }
4537
4556
  if (result.kind === "policy_reject") {
4538
4557
  const detail = result.detail;
4539
4558
  const lead = detail?.displayMsg ?? clippedUpstreamText(result.message, 200);
4540
4559
  const meta = ["服务端策略拒绝"];
4541
4560
  if (detail?.upstreamCode !== void 0) meta.push(`code ${detail.upstreamCode}`);
4542
4561
  if (detail?.requestId !== void 0) meta.push(`requestId ${detail.requestId}`);
4543
- const message = `${lead}(${meta.join(",")})${note}。该请求被 WorkBuddy 服务端策略拒绝,重新登录不会解决;可在 WorkBuddy 桌面端用同一账号验证,或切换区域/账号后重试`;
4562
+ const message = `${lead}(${meta.join(",")})${note}。该请求被 WorkBuddy 服务端策略拒绝;若上游的说明指向内容,重新登录不会解决,可在 WorkBuddy 桌面端用同一账号验证;若它指向账号本身,则需要走官方渠道处理,插件会在遇到该措辞时另行标明`;
4544
4563
  writeOpenAIError(res, KIND_STATUS[result.kind], result.kind, message, hostErrorCode(result.kind));
4545
4564
  return;
4546
4565
  }
@@ -7060,7 +7079,9 @@ function cooldownOf(input) {
7060
7079
  */
7061
7080
  function outcomeOfFailure(status, body) {
7062
7081
  if (status === 401 || status === 403) {
7063
- if (classifyUpstreamError(status, body) === "policy_reject") return "policy-rejected";
7082
+ const kind = classifyUpstreamError(status, body);
7083
+ if (kind === "policy_reject") return "policy-rejected";
7084
+ if (kind === "account_restricted") return "account-restricted";
7064
7085
  return "credential-rejected";
7065
7086
  }
7066
7087
  switch (classifyUpstreamError(status, body)) {
@@ -7068,6 +7089,7 @@ function outcomeOfFailure(status, body) {
7068
7089
  case "model_busy": return "model-busy";
7069
7090
  case "hard_credit": return "out-of-credit";
7070
7091
  case "session_dead": return "credential-rejected";
7092
+ case "account_restricted": return "account-restricted";
7071
7093
  case "channel_reject": return "channel-rejected";
7072
7094
  case "not_found": return "not-found";
7073
7095
  default: return status === 0 || status >= 500 ? "unavailable" : "failed";
@@ -7608,6 +7630,7 @@ function exclusionOf(probe, nowMs) {
7608
7630
  switch (probe.outcome) {
7609
7631
  case "ok": return;
7610
7632
  case "credential-rejected": return "credential-rejected";
7633
+ case "account-restricted": return "account-restricted";
7611
7634
  case "not-found": return "not-found";
7612
7635
  case "unavailable": return retryDue(probe, nowMs) ? void 0 : "unavailable";
7613
7636
  case "failed": return retryDue(probe, nowMs) ? void 0 : "failed";
@@ -8649,7 +8672,8 @@ function toWebAccount(account) {
8649
8672
  domain: account.domain,
8650
8673
  source: account.source,
8651
8674
  tokenExpiresAtMs: account.tokenExpiresAtMs,
8652
- selected: account.selected
8675
+ selected: account.selected,
8676
+ ...account.enterprise === true ? { enterprise: true } : {}
8653
8677
  };
8654
8678
  }
8655
8679
  /**
@@ -8759,6 +8783,9 @@ async function workBuddyWebStatus(deps, region, options = {}) {
8759
8783
  const creditPosition = deps.creditPosition?.(region) ?? "composer";
8760
8784
  const imageToolEnabled = deps.imageToolEnabled?.(region) ?? false;
8761
8785
  const cliBridgeEnabled = deps.cliBridgeEnabled?.() === true;
8786
+ const cliBridgeModel = deps.cliBridgeModel?.() ?? "";
8787
+ const cliBridgeEffort = deps.cliBridgeEffort?.() ?? "";
8788
+ const cliBridgeRegionOrder = deps.cliBridgeRegionOrder?.() ?? "";
8762
8789
  const busyLevels = deps.busyLevels?.(region) ?? {};
8763
8790
  const authStatus = await store.status();
8764
8791
  const selectionExplicit = store.hasExplicitSelection();
@@ -8770,6 +8797,9 @@ async function workBuddyWebStatus(deps, region, options = {}) {
8770
8797
  showCreditsInMainUi,
8771
8798
  imageToolEnabled,
8772
8799
  cliBridgeEnabled,
8800
+ cliBridgeModel,
8801
+ cliBridgeEffort,
8802
+ cliBridgeRegionOrder,
8773
8803
  busyLevels
8774
8804
  };
8775
8805
  let credential;
@@ -8788,6 +8818,9 @@ async function workBuddyWebStatus(deps, region, options = {}) {
8788
8818
  creditPosition,
8789
8819
  imageToolEnabled,
8790
8820
  cliBridgeEnabled,
8821
+ cliBridgeModel,
8822
+ cliBridgeEffort,
8823
+ cliBridgeRegionOrder,
8791
8824
  busyLevels,
8792
8825
  ...await store.selectionLost() ? { selectionLost: true } : {},
8793
8826
  ...webAccounts.length === 0 ? await searchedPaths(store) : {}
@@ -8796,7 +8829,7 @@ async function workBuddyWebStatus(deps, region, options = {}) {
8796
8829
  const selected = accounts.find((account) => account.selected);
8797
8830
  const account = {
8798
8831
  accountId: selected?.id ?? "",
8799
- accountName: credential.nickname ?? "",
8832
+ accountName: preferredAccountName(credential),
8800
8833
  ...credential.domain === "" ? {} : { domain: credential.domain },
8801
8834
  region,
8802
8835
  source: credential.source,
@@ -8807,6 +8840,9 @@ async function workBuddyWebStatus(deps, region, options = {}) {
8807
8840
  creditPosition,
8808
8841
  imageToolEnabled,
8809
8842
  cliBridgeEnabled,
8843
+ cliBridgeModel,
8844
+ cliBridgeEffort,
8845
+ cliBridgeRegionOrder,
8810
8846
  busyLevels,
8811
8847
  accounts: accounts.map(toWebAccount),
8812
8848
  models: deps.displayModels(region).map((model) => toWebModel(model, deps.contextBudgets(region))).map((model) => model.reasoning === void 0 || lastRefusalFor(model.id) === void 0 ? model : {
@@ -8821,6 +8857,7 @@ async function workBuddyWebStatus(deps, region, options = {}) {
8821
8857
  offModelIds: [...deps.offModelIds(region)]
8822
8858
  };
8823
8859
  const figures = fast ? {} : await liveFigures(deps, region, credential, selected);
8860
+ const rosterSeen = deps.rosterSeen === void 0 ? void 0 : await deps.rosterSeen(region);
8824
8861
  return {
8825
8862
  status: "signed-in",
8826
8863
  contextBudgets: Object.fromEntries(Object.entries(deps.contextBudgets(region)).filter(([, value]) => typeof value === "number")),
@@ -8830,6 +8867,10 @@ async function workBuddyWebStatus(deps, region, options = {}) {
8830
8867
  authFile: volatileFlagOf("authFile")
8831
8868
  },
8832
8869
  ...account,
8870
+ ...rosterSeen === void 0 ? {} : {
8871
+ newModelIds: [...rosterSeen.newModelIds],
8872
+ ...rosterSeen.versionNote === void 0 ? {} : { rosterSeenVersionNote: rosterSeen.versionNote }
8873
+ },
8833
8874
  ...figures,
8834
8875
  ...await poolSectionOf(deps, region, fast)
8835
8876
  };
@@ -8902,6 +8943,7 @@ async function workBuddyWebPool(deps, region, fast = false) {
8902
8943
  return {
8903
8944
  accountId: account.id,
8904
8945
  accountName: account.accountName,
8946
+ ...account.enterprise === true ? { enterprise: true } : {},
8905
8947
  ...measured?.credits === void 0 ? {} : {
8906
8948
  credits: measured.credits.total,
8907
8949
  expiringSoon: measured.credits.expiringSoon,
@@ -9084,6 +9126,7 @@ function registerWorkBuddyStatusRoute(ctx, deps) {
9084
9126
  try {
9085
9127
  const credential = await deps.store(region).credentialFor(accountId);
9086
9128
  if (credential === void 0) return json(res, 404, { error: "no credential for this account" });
9129
+ if (credential.enterpriseId !== void 0 && credential.enterpriseId !== "") return json(res, 409, { error: "enterprise-account-unsupported" });
9087
9130
  const target = (await deps.client.fetchBenefits(credential)).find((benefit) => benefit.kind === kind);
9088
9131
  if (target === void 0) return json(res, 404, { error: "benefit not available on this account" });
9089
9132
  if (target.claimed) return json(res, 409, {
@@ -9701,7 +9744,9 @@ const Config = z.object({
9701
9744
  z.const("off"),
9702
9745
  z.const("failure"),
9703
9746
  z.const("always")
9704
- ]).default("failure").description("When to perturb scanned messages with invisible characters: \"off\" only rescues after a channel-gate refusal, \"failure\" (default) learns from a successful rescue and starts later requests perturbed, \"always\" starts every request with the dense rescue for a gateway whose plain attempt is guaranteed to fail."))
9747
+ ]).default("failure").description("When to perturb scanned messages with invisible characters: \"off\" only rescues after a channel-gate refusal, \"failure\" (default) learns from a successful rescue and starts later requests perturbed, \"always\" starts every request with the dense rescue for a gateway whose plain attempt is guaranteed to fail.")),
9748
+ usageIdentity: asVolatile(z.boolean().default(false).description("Make the upstream's usage page record these calls like the official client's: add the X-IDE-* identity headers (which fill the 使用端 column) and send the conversation ids as 32-hex UUIDv7. Off by default — it is a display alignment measured by a reporter, not by this plugin, and the chat channel identity (User-Agent) and request body never change.")),
9749
+ keepPromptLast: asVolatile(z.boolean().default(false).description("Move the harness-injected runtime context above the real prompt, so the WorkBuddy usage page records what you actually typed instead of the injected block. Message count and text are preserved; the one edit is the recorded prompt's own leading <acp> marker. Off by default because it changes the message order the upstream counts."))
9705
9750
  });
9706
9751
  /**
9707
9752
  * One region's saved model state. A config written before the region split has
@@ -9804,7 +9849,7 @@ async function legacyAttributionRegion(value, accountsFor) {
9804
9849
  function apply(ctx, config) {
9805
9850
  const client = new WorkBuddyUpstreamClient((message) => {
9806
9851
  ctx.logger.warn(`dsh-connect-workbuddy: ${message}`);
9807
- });
9852
+ }, { usageIdentity: unwrapVolatile(config.usageIdentity) === true });
9808
9853
  /**
9809
9854
  * How long a check-in reading is reused before asking the upstream again.
9810
9855
  *
@@ -9842,6 +9887,7 @@ function apply(ctx, config) {
9842
9887
  refresh: (credential) => client.refreshToken(credential)
9843
9888
  });
9844
9889
  const catalog = new WorkBuddyCatalog(region);
9890
+ const rosterSeen = createFileRosterSeenStore(region);
9845
9891
  /**
9846
9892
  * Where the next upstream `usage` should be written.
9847
9893
  *
@@ -9862,6 +9908,7 @@ function apply(ctx, config) {
9862
9908
  catalog,
9863
9909
  logger: ctx.logger,
9864
9910
  channelPerturbation: unwrapVolatile(config.channelPerturbation) ?? "failure",
9911
+ keepPromptLast: unwrapVolatile(config.keepPromptLast) === true,
9865
9912
  /**
9866
9913
  * Pool routing, bound to THIS region.
9867
9914
  *
@@ -9894,7 +9941,8 @@ function apply(ctx, config) {
9894
9941
  onAccountFailure: (accountId, failure) => {
9895
9942
  recordAccountFailure(region, accountId, failure);
9896
9943
  }
9897
- })
9944
+ }),
9945
+ rosterSeen
9898
9946
  };
9899
9947
  }
9900
9948
  const accountUsabilityProbe = createAccountUsabilityProbe({
@@ -10310,6 +10358,15 @@ function apply(ctx, config) {
10310
10358
  offModelIds: (region) => savedOffModelIds(regionStateOf(current(), region), displayModels(current(), region)),
10311
10359
  contextBudgets: (region) => regionStateOf(current(), region).contextBudgets ?? {},
10312
10360
  discoverModels,
10361
+ /**
10362
+ * This region's first-sighting readout, the data behind the composer pill's
10363
+ * "· N 新模型" segment.
10364
+ *
10365
+ * READ-ONLY here: only the reconciler's `note` writes first-sighting
10366
+ * history, so no amount of polling by the pill or the card can change who
10367
+ * counts as new — the one property that keeps the marker honest.
10368
+ */
10369
+ rosterSeen: (region) => stacks[region].rosterSeen.readout(),
10313
10370
  regionEnabled: (region) => regionEnabled(current(), region),
10314
10371
  /**
10315
10372
  * The region's committed sidebar-credit flag, read from the same config the
@@ -10335,6 +10392,16 @@ function apply(ctx, config) {
10335
10392
  */
10336
10393
  cliBridgeEnabled: () => unwrapVolatile(current().cliBridge)?.enabled === true,
10337
10394
  /**
10395
+ * The model / reasoning effort the optional CodeBuddy CLI route runs on.
10396
+ *
10397
+ * `cliBridge` is volatile, hence the unwrap; absent keys read as `''` —
10398
+ * "the CLI's own default" — because an empty pin is a COMMITTED value the
10399
+ * card's pickers can render, not a missing one.
10400
+ */
10401
+ cliBridgeModel: () => unwrapVolatile(current().cliBridge)?.model ?? "",
10402
+ cliBridgeEffort: () => unwrapVolatile(current().cliBridge)?.effort ?? "",
10403
+ cliBridgeRegionOrder: () => unwrapVolatile(current().cliBridge)?.regionOrder ?? "",
10404
+ /**
10338
10405
  * WHERE the readout sits, resolved here rather than read from the browser
10339
10406
  * settings mirror, for the same measured reason as the flag above: on the
10340
10407
  * affected 0.1.7 deployments that mirror never picks up this plugin's
@@ -10498,7 +10565,8 @@ function apply(ctx, config) {
10498
10565
  const wanted = new Set(poolPreferencesOf(current(), region).memberAccountIds);
10499
10566
  return (await stacks[region].store.accounts()).filter((account) => !wanted.has(account.id)).map((account) => ({
10500
10567
  id: account.id,
10501
- accountName: account.accountName
10568
+ accountName: account.accountName,
10569
+ ...account.enterprise === true ? { enterprise: true } : {}
10502
10570
  }));
10503
10571
  },
10504
10572
  /**
@@ -10571,6 +10639,7 @@ function apply(ctx, config) {
10571
10639
  if (cached !== void 0 && now - cached.atMs < 18e5) return [account.id, cached.views];
10572
10640
  const credential = await store.credentialFor(account.id).catch(() => void 0);
10573
10641
  if (credential === void 0) return void 0;
10642
+ if (credential.enterpriseId !== void 0 && credential.enterpriseId !== "") return [account.id, []];
10574
10643
  const read = await client.fetchBenefits(credential).catch(() => void 0);
10575
10644
  if (read === void 0) return void 0;
10576
10645
  const views = claimableBenefits(read, now);
@@ -10882,6 +10951,7 @@ function apply(ctx, config) {
10882
10951
  case "out-of-credit": return "账号积分耗尽";
10883
10952
  case "model-busy": return "账号撞上模型繁忙";
10884
10953
  case "credential-rejected": return "账号登录已失效";
10954
+ case "account-restricted": return "账号被上游限制使用";
10885
10955
  case "not-found": return "账号已不存在";
10886
10956
  case "unusable": return "账号不可用";
10887
10957
  default: return "账号不可用";
@@ -10921,7 +10991,8 @@ function apply(ctx, config) {
10921
10991
  return {
10922
10992
  account: {
10923
10993
  id: account.id,
10924
- accountName: account.accountName
10994
+ accountName: account.accountName,
10995
+ ...account.enterprise === true ? { enterprise: true } : {}
10925
10996
  },
10926
10997
  ...reading === void 0 ? {} : { credits: reading },
10927
10998
  ...probes[account.id] === void 0 ? {} : { probe: probes[account.id] },
@@ -11061,7 +11132,8 @@ function apply(ctx, config) {
11061
11132
  return {
11062
11133
  account: {
11063
11134
  id: account.id,
11064
- accountName: account.accountName
11135
+ accountName: account.accountName,
11136
+ ...account.enterprise === true ? { enterprise: true } : {}
11065
11137
  },
11066
11138
  ...credits === void 0 ? {} : { credits: {
11067
11139
  total: credits.total,
@@ -11279,6 +11351,11 @@ function apply(ctx, config) {
11279
11351
  const state = regionStateOf(current(), region);
11280
11352
  stacks[region].catalog.set(withEffectiveOff(withImageSelection(deriveCatalog(models, new Set(state.enabledModelIds ?? []), state.contextBudgets ?? {}), new Set(state.imageModelIds ?? [])), new Set(savedOffModelIds(state, models))));
11281
11353
  adapters[region].invalidate();
11354
+ try {
11355
+ await stacks[region].rosterSeen.note(models.map((model) => model.id));
11356
+ } catch (error) {
11357
+ ctx.logger.warn(`dsh-connect-workbuddy: ${region} first-sighting history not updated`, error);
11358
+ }
11282
11359
  await regionsFieldHealed;
11283
11360
  await repairRegionCatalog(region, models);
11284
11361
  } catch (error) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-connect-workbuddy",
3
3
  "displayName": "DSH Connect WorkBuddy",
4
4
  "description": "把本机登录的 WorkBuddy 模型(国内版与国际版)接入 DeepSeek Harness:每次刷新按上游目录自动读取每个模型的积分倍率,支持逐模型图片输入开关、多账号切换与账号池自动换号,并提供每日签到、卡片与输入框里的胶囊积分读数等只读积分概览。",
5
- "version": "5.7.0",
5
+ "version": "5.8.0",
6
6
  "author": "dingminhua",
7
7
  "copyright": "Copyright (c) 2026 LaoDing",
8
8
  "license": "MIT",