dsh-connect-workbuddy 2.0.10 → 2.0.12

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 { A as isCredentialRejectedError, B as fetchAtRestKeyPayload, C as parseWorkBuddyAuth, D as WorkBuddyCredentialRejectedError, E as CREDENTIAL_REJECTED_CODE, F as regionOf, G as workbuddyAppExecutableCandidates, H as isEncryptedFieldWrapper, I as WORKBUDDY_APP_EXECUTABLE_ENV, L as clearAtRestKeyCache, M as parseReasoning, N as parseUpstreamModel, O as WorkBuddyUpstreamClient, P as prepareChatBody, R as deriveAtRestKey, S as legacyWorkbuddyOwnAuthPath, T as workbuddyOwnAuthPath, U as openEncryptedField, V as findWorkbuddyAppExecutable, W as readAtRestKey, _ as authFileName, a as readHostHeartbeat, b as defaultDesktopAuthPath, c as WORKBUDDY_CONNECT_VERSION, d as WorkBuddyCatalog, f as deriveCatalog, g as WorkBuddyCredentialStore, h as WORKBUDDY_AUTH_FILE_ENV, i as processStartTimeMs, j as parseCreditMultiplier, k as classifyUpstreamError, l as FALLBACK_WORKBUDDY_MODELS, m as WORKBUDDY_AUTH_FILENAME, n as clearHostHeartbeat, o as workbuddyHostHeartbeatPath, p as fallbackModelsFor, r as isHeartbeatProcessAlive, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, u as FALLBACK_WORKBUDDY_MODELS_GLOBAL, v as defaultDesktopAuthCandidates, w as workbuddyAccountId, x as hasEncryptedCredentialFields, y as defaultDesktopAuthDirs, z as deriveAtRestKeyId } from "./host-heartbeat-DPbL9oi2.js";
1
+ import { A as WorkBuddyCredentialRejectedError, B as clearAtRestKeyCache, C as hasEncryptedCredentialFields, D as workbuddyAccountId, E as parseWorkBuddyAuth, F as parseReasoning, G as isEncryptedFieldWrapper, H as deriveAtRestKeyId, I as parseUpstreamModel, J as macosNestedAppBundles, K as isWorkbuddyBundle, L as prepareChatBody, M as classifyUpstreamError, N as isCredentialRejectedError, O as workbuddyOwnAuthPath, P as parseCreditMultiplier, R as regionOf, S as defaultDesktopAuthPath, T as legacyWorkbuddyOwnAuthPath, U as fetchAtRestKeyPayload, V as deriveAtRestKey, W as findWorkbuddyAppExecutable, X as readAtRestKey, Y as openEncryptedField, Z as workbuddyAppExecutableCandidates, _ as WorkBuddyCredentialStore, a as readHostHeartbeat, b as defaultDesktopAuthCandidates, c as WORKBUDDY_CONNECT_VERSION, d as WorkBuddyCatalog, f as deriveCatalog, g as WORKBUDDY_AUTH_FILE_ENV, h as WORKBUDDY_AUTH_FILENAME, i as processStartTimeMs, j as WorkBuddyUpstreamClient, k as CREDENTIAL_REJECTED_CODE, l as FALLBACK_WORKBUDDY_MODELS, m as ENCRYPTED_CREDENTIAL_CODE, n as clearHostHeartbeat, o as workbuddyHostHeartbeatPath, p as fallbackModelsFor, q as macosBundleExecutable, r as isHeartbeatProcessAlive, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, u as FALLBACK_WORKBUDDY_MODELS_GLOBAL, v as WorkBuddyEncryptedCredentialError, w as isEncryptedCredentialError, x as defaultDesktopAuthDirs, y as authFileName, z as WORKBUDDY_APP_EXECUTABLE_ENV } from "./host-heartbeat-ChtMu37E.js";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { randomBytes, timingSafeEqual } from "node:crypto";
4
4
  import { createProvider } from "@earendil-works/pi-ai";
@@ -715,6 +715,10 @@ async function searchedPaths(store) {
715
715
  return {};
716
716
  }
717
717
  }
718
+ /** Reads schemastery's internal `meta.volatile` marker for one top-level field. */
719
+ function volatileFlagOf(field) {
720
+ return Config.dict?.[field]?.meta?.volatile === true;
721
+ }
718
722
  /**
719
723
  * Assemble one region's card document. `region` is the tab the card is on;
720
724
  * the region-scoped store already answers with only that region's accounts,
@@ -775,6 +779,12 @@ async function workBuddyWebStatus(deps, region) {
775
779
  });
776
780
  return {
777
781
  status: "signed-in",
782
+ contextBudgets: Object.fromEntries(Object.entries(deps.contextBudgets(region)).filter(([, value]) => typeof value === "number")),
783
+ diagVolatile: {
784
+ regions: volatileFlagOf("regions"),
785
+ accounts: volatileFlagOf("accounts"),
786
+ authFile: volatileFlagOf("authFile")
787
+ },
778
788
  ...account,
779
789
  ...creditsResult.status === "fulfilled" ? { credits: toCredits(creditsResult.value) } : { creditsError: safeMessage(creditsResult.reason) },
780
790
  ...checkinResult.status === "fulfilled" ? { checkin: checkinResult.value } : { checkinError: safeMessage(checkinResult.reason) },
@@ -881,11 +891,59 @@ function registerWorkBuddyStatusRoute(ctx, deps) {
881
891
  }
882
892
  }
883
893
  });
894
+ const disposeDiagWrite = ctx.webServer.register({
895
+ kind: "exact",
896
+ path: "/plugins/dsh-connect-workbuddy/__save",
897
+ handler: async (req, res) => {
898
+ if (req.method !== "POST") return json(res, 405, { error: "method not allowed" });
899
+ if (!loopbackOrigin(req)) return json(res, 403, { error: "origin-not-trusted" });
900
+ const settings = ctx.get?.("settings");
901
+ if (settings === void 0) return json(res, 503, { error: "settings service unavailable to this fiber" });
902
+ try {
903
+ const body = await new Promise((resolve, reject) => {
904
+ const chunks = [];
905
+ req.on("data", (chunk) => chunks.push(chunk));
906
+ req.on("end", () => {
907
+ try {
908
+ resolve(JSON.parse(Buffer.concat(chunks).toString("utf8")));
909
+ } catch (e) {
910
+ reject(e);
911
+ }
912
+ });
913
+ req.on("error", reject);
914
+ });
915
+ const field = body.field;
916
+ if (field !== "regions" && field !== "accounts") return json(res, 400, { error: "field must be regions or accounts" });
917
+ const row = settings.describe().find((r) => String(r.ns).includes("workbuddy"));
918
+ if (row === void 0) return json(res, 503, { error: "workbuddy namespace missing from describe()" });
919
+ const current = row.value?.[field] ?? {};
920
+ const incoming = body.value ?? {};
921
+ const merged = {
922
+ ...current,
923
+ ...incoming
924
+ };
925
+ await settings.mutate(row.ns, [{
926
+ op: "set",
927
+ path: [field],
928
+ value: merged
929
+ }], void 0);
930
+ return json(res, 200, { ok: true });
931
+ } catch (error) {
932
+ const err = error;
933
+ return json(res, 500, {
934
+ ok: false,
935
+ errorName: err?.name ?? "unknown",
936
+ error: err?.message ?? String(error)
937
+ });
938
+ }
939
+ }
940
+ });
884
941
  return () => {
885
942
  disposeRefresh();
886
943
  disposeCheckin();
887
944
  disposeAccounts();
888
945
  disposeUsage();
946
+ disposeDiagWrite();
889
947
  };
890
948
  }, "dsh-connect-workbuddy: Web status route");
891
949
  }
@@ -914,11 +972,63 @@ const accountSelectionConfig = z.object({
914
972
  cn: z.string().description("Selected domestic (CN) account id (never a token)"),
915
973
  global: z.string().description("Selected international account id (never a token)")
916
974
  });
975
+ /**
976
+ * Mark a schema's field as volatile on the DSH lines that support it.
977
+ *
978
+ * `volatile()` exists from schemastery 3.18.3 (the DSH 0.1.7 line, which is the
979
+ * only line whose settings write gate reads the marker). Older pinning (3.18.2,
980
+ * the 0.1.5 line) has no such method, and the schema must stay byte-identical to
981
+ * the unmarked original there: hand-writing `meta.volatile = true` would bypass
982
+ * schemastery's own validateVolatileSchema checks and produce a schema no 0.1.5
983
+ * consumer understands — so on that line this degrades to an identity no-op.
984
+ *
985
+ * Exported so BOTH arms are testable on any machine. The capability is decided
986
+ * by whichever schemastery the dependency tree resolves, so without a seam the
987
+ * no-op arm would silently stop being exercised the moment the pinned version
988
+ * moved up — which is exactly how the volatile-path defect below went unseen.
989
+ */
990
+ function asVolatile(schema) {
991
+ if (typeof schema.volatile === "function") return schema.volatile();
992
+ return schema;
993
+ }
994
+ function unwrapVolatile(value) {
995
+ if (value !== null && typeof value === "object" && "get" in value && typeof value.get === "function") return value.get();
996
+ return value;
997
+ }
998
+ /**
999
+ * Deep copy of a config value with every `{get(): T}` live reference replaced
1000
+ * by the value it resolves to.
1001
+ *
1002
+ * {@link unwrapVolatile} only peels the ONE level a caller reads, which is all
1003
+ * the ordinary read paths need. Handing a config object to a settings service
1004
+ * is different: the service validates and `structuredClone`s the WHOLE object,
1005
+ * so a reference surviving anywhere inside it fails schema validation with a
1006
+ * message that names the field but not the cause —
1007
+ * `$.authFile expected string but got [object Object]`.
1008
+ *
1009
+ * That is exactly what happened on the 0.1.5 line once volatile marking became
1010
+ * active: `installSection` received the raw config, whose volatile fields were
1011
+ * live references, and every field it validated threw. The namespace never
1012
+ * registered, so the card's settings silently disappeared.
1013
+ *
1014
+ * Non-reference values are recursed into so a nested volatile field is caught
1015
+ * too — arrays are rebuilt rather than mutated, so the caller's config object is
1016
+ * never touched.
1017
+ */
1018
+ function unwrapVolatileDeep(value) {
1019
+ if (value === null || typeof value !== "object") return value;
1020
+ if (typeof value.get === "function") return unwrapVolatileDeep(value.get());
1021
+ if (Array.isArray(value)) return value.map((entry) => unwrapVolatileDeep(entry));
1022
+ const source = value;
1023
+ const out = {};
1024
+ for (const key of Object.keys(source)) out[key] = unwrapVolatileDeep(source[key]);
1025
+ return out;
1026
+ }
917
1027
  const Config = z.object({
918
- authFile: z.string().description("WorkBuddy desktop auth file (defaults to the app's own location)"),
1028
+ authFile: asVolatile(z.string().description("WorkBuddy desktop auth file (defaults to the app's own location)")),
919
1029
  accountId: z.string().description("Deprecated: pre-split account selector, attributed to its own region"),
920
- accounts: accountSelectionConfig.description("Per-region account selections, keyed cn | global"),
921
- regions: z.dict(regionStateConfig).default({}).description("Per-region model directory and selection, keyed cn | global"),
1030
+ accounts: asVolatile(accountSelectionConfig.default({}).description("Per-region account selections, keyed cn | global")),
1031
+ regions: asVolatile(z.dict(regionStateConfig).default({}).description("Per-region model directory and selection, keyed cn | global")),
922
1032
  lastCatalog: z.array(modelConfig).description("Deprecated: pre-region-split CN model directory"),
923
1033
  enabledModelIds: z.array(z.string()).default([]).description("Deprecated: pre-region-split CN selection"),
924
1034
  imageModelIds: z.array(z.string()).default([]).description("Deprecated: pre-region-split CN image opt-in"),
@@ -934,14 +1044,18 @@ const Config = z.object({
934
1044
  * picks.
935
1045
  */
936
1046
  function regionStateOf(config, region) {
937
- const stored = config.regions?.[region];
1047
+ const stored = unwrapVolatile(config.regions)?.[region];
938
1048
  if (stored !== void 0) return stored;
939
1049
  if (region !== "cn") return {};
1050
+ const lastCatalog = unwrapVolatile(config.lastCatalog);
1051
+ const enabledModelIds = unwrapVolatile(config.enabledModelIds);
1052
+ const imageModelIds = unwrapVolatile(config.imageModelIds);
1053
+ const contextBudgets = unwrapVolatile(config.contextBudgets);
940
1054
  return {
941
- ...config.lastCatalog === void 0 ? {} : { lastCatalog: config.lastCatalog },
942
- ...config.enabledModelIds === void 0 ? {} : { enabledModelIds: config.enabledModelIds },
943
- ...config.imageModelIds === void 0 ? {} : { imageModelIds: config.imageModelIds },
944
- ...config.contextBudgets === void 0 ? {} : { contextBudgets: config.contextBudgets }
1055
+ ...lastCatalog === void 0 ? {} : { lastCatalog },
1056
+ ...enabledModelIds === void 0 ? {} : { enabledModelIds },
1057
+ ...imageModelIds === void 0 ? {} : { imageModelIds },
1058
+ ...contextBudgets === void 0 ? {} : { contextBudgets }
945
1059
  };
946
1060
  }
947
1061
  /**
@@ -978,14 +1092,15 @@ const REGION_KEYS = ["cn", "global"];
978
1092
  * attribution yet) and every later one.
979
1093
  */
980
1094
  function selectAccountFor(region, value, legacyAccountRegion) {
981
- const configured = value.accounts?.[region];
1095
+ const configured = unwrapVolatile(value.accounts)?.[region];
982
1096
  if (configured === "") return void 0;
983
1097
  if (configured !== void 0) return configured;
984
- return legacyAccountRegion === region ? value.accountId : void 0;
1098
+ const accountId = unwrapVolatile(value.accountId);
1099
+ return legacyAccountRegion === region ? accountId : void 0;
985
1100
  }
986
1101
  /** Whether a region carries the Clear sentinel rather than a saved choice. */
987
1102
  function regionCleared(value, region) {
988
- return value.accounts?.[region] === "";
1103
+ return unwrapVolatile(value.accounts)?.[region] === "";
989
1104
  }
990
1105
  /**
991
1106
  * Which region owns the pre-split `accountId`, or `undefined` when it cannot
@@ -1021,9 +1136,10 @@ function apply(ctx, config) {
1021
1136
  const client = new WorkBuddyUpstreamClient();
1022
1137
  const stacks = {};
1023
1138
  for (const region of REGION_KEYS) {
1139
+ const authFile = unwrapVolatile(config.authFile);
1024
1140
  const store = new WorkBuddyCredentialStore({
1025
1141
  region,
1026
- ...config.authFile === void 0 ? {} : { desktopPath: config.authFile },
1142
+ ...authFile === void 0 ? {} : { desktopPath: authFile },
1027
1143
  refresh: (credential) => client.refreshToken(credential)
1028
1144
  });
1029
1145
  const catalog = new WorkBuddyCatalog(region);
@@ -1071,8 +1187,9 @@ function apply(ctx, config) {
1071
1187
  };
1072
1188
  /** Push the current config into every region's store selection and catalog. */
1073
1189
  const applySelection = (value) => {
1190
+ const authFile = unwrapVolatile(value.authFile);
1074
1191
  for (const region of REGION_KEYS) {
1075
- stacks[region].store.setDesktopPath(value.authFile);
1192
+ stacks[region].store.setDesktopPath(authFile);
1076
1193
  stacks[region].store.selectAccount(effectiveAccountFor(region, value));
1077
1194
  stacks[region].catalog.set(configuredModels(value, region));
1078
1195
  }
@@ -1153,13 +1270,26 @@ function apply(ctx, config) {
1153
1270
  if (stacks[region].catalog.setRegionUsable(usable)) invalidateCatalog();
1154
1271
  }
1155
1272
  }));
1156
- ctx.settings.installSection(ctx, WORKBUDDY_SETTINGS_NS, Config, config, {
1157
- setSource(source) {
1158
- current = source;
1159
- },
1160
- onChange() {
1161
- applySelection(current());
1273
+ ctx.inject(["settings"], (sctx) => {
1274
+ const settings = sctx.settings;
1275
+ if (typeof settings.configure === "function") {
1276
+ settings.configure({ auto: true }, ctx.fiber);
1277
+ return;
1162
1278
  }
1279
+ if (typeof settings.installSection === "function") {
1280
+ const entry = unwrapVolatileDeep(config);
1281
+ settings.installSection(ctx, WORKBUDDY_SETTINGS_NS, Config, entry, {
1282
+ setSource(source) {
1283
+ current = source;
1284
+ },
1285
+ onChange() {
1286
+ applySelection(current());
1287
+ }
1288
+ });
1289
+ }
1290
+ });
1291
+ ctx.on("loader/volatile-update", () => {
1292
+ applySelection(current());
1163
1293
  });
1164
1294
  applySelection(config);
1165
1295
  (async () => {
@@ -1270,4 +1400,4 @@ function apply(ctx, config) {
1270
1400
  });
1271
1401
  }
1272
1402
  //#endregion
1273
- export { CREDENTIAL_REJECTED_CODE, Config, FALLBACK_WORKBUDDY_MODELS, FALLBACK_WORKBUDDY_MODELS_GLOBAL, WORKBUDDY_ACCOUNTS_REFRESH_PATH, WORKBUDDY_APP_EXECUTABLE_ENV, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CHECKIN_PATH, WORKBUDDY_CONNECT_VERSION, WORKBUDDY_GLOBAL_PROVIDER, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_MODELS_REFRESH_PATH, WORKBUDDY_PROVIDER, WORKBUDDY_PROVIDERS, WORKBUDDY_PROVIDER_DISPLAY_NAMES, WORKBUDDY_REGIONS, WORKBUDDY_REGION_PARAM, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_USAGE_PATH, WorkBuddyCatalog, WorkBuddyCredentialRejectedError, WorkBuddyCredentialStore, WorkBuddyUpstreamClient, apply, authFileName, classifyUpstreamError, clearAtRestKeyCache, clearHostHeartbeat, createAccountUsabilityProbe, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthDirs, defaultDesktopAuthPath, deriveAtRestKey, deriveAtRestKeyId, deriveCatalog, fallbackModelsFor, fetchAtRestKeyPayload, findWorkbuddyAppExecutable, hasEncryptedCredentialFields, inject, isCredentialRejectedError, isEncryptedFieldWrapper, isHeartbeatProcessAlive, legacyAttributionRegion, legacyWorkbuddyOwnAuthPath, name, nextRegionEnabled, nextRegionSlots, openEncryptedField, parseCreditMultiplier, parseReasoning, parseUpstreamModel, parseWorkBuddyAuth, prepareChatBody, processStartTimeMs, readAtRestKey, readHostHeartbeat, regionCleared, regionEnabled, regionEnabledOf, regionOf, regionOfProvider, regionOfStatusUrl, regionStateOf, registerWorkBuddyStatusRoute, resolveCredentialRecovery, selectAccountFor, toPersistedWorkBuddyModel, withWorkBuddyRegion, workBuddyDisplayName, workBuddyModelInput, workBuddyThinkingLevelMap, workBuddyWebStatus, workbuddyAccountId, workbuddyAppExecutableCandidates, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, writeHostHeartbeat };
1403
+ export { CREDENTIAL_REJECTED_CODE, Config, ENCRYPTED_CREDENTIAL_CODE, FALLBACK_WORKBUDDY_MODELS, FALLBACK_WORKBUDDY_MODELS_GLOBAL, WORKBUDDY_ACCOUNTS_REFRESH_PATH, WORKBUDDY_APP_EXECUTABLE_ENV, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CHECKIN_PATH, WORKBUDDY_CONNECT_VERSION, WORKBUDDY_GLOBAL_PROVIDER, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_MODELS_REFRESH_PATH, WORKBUDDY_PROVIDER, WORKBUDDY_PROVIDERS, WORKBUDDY_PROVIDER_DISPLAY_NAMES, WORKBUDDY_REGIONS, WORKBUDDY_REGION_PARAM, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_USAGE_PATH, WorkBuddyCatalog, WorkBuddyCredentialRejectedError, WorkBuddyCredentialStore, WorkBuddyEncryptedCredentialError, WorkBuddyUpstreamClient, apply, asVolatile, authFileName, classifyUpstreamError, clearAtRestKeyCache, clearHostHeartbeat, createAccountUsabilityProbe, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthDirs, defaultDesktopAuthPath, deriveAtRestKey, deriveAtRestKeyId, deriveCatalog, fallbackModelsFor, fetchAtRestKeyPayload, findWorkbuddyAppExecutable, hasEncryptedCredentialFields, inject, isCredentialRejectedError, isEncryptedCredentialError, isEncryptedFieldWrapper, isHeartbeatProcessAlive, isWorkbuddyBundle, legacyAttributionRegion, legacyWorkbuddyOwnAuthPath, macosBundleExecutable, macosNestedAppBundles, name, nextRegionEnabled, nextRegionSlots, openEncryptedField, parseCreditMultiplier, parseReasoning, parseUpstreamModel, parseWorkBuddyAuth, prepareChatBody, processStartTimeMs, readAtRestKey, readHostHeartbeat, regionCleared, regionEnabled, regionEnabledOf, regionOf, regionOfProvider, regionOfStatusUrl, regionStateOf, registerWorkBuddyStatusRoute, resolveCredentialRecovery, selectAccountFor, toPersistedWorkBuddyModel, unwrapVolatileDeep, withWorkBuddyRegion, workBuddyDisplayName, workBuddyModelInput, workBuddyThinkingLevelMap, workBuddyWebStatus, workbuddyAccountId, workbuddyAppExecutableCandidates, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, writeHostHeartbeat };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-connect-workbuddy",
3
3
  "displayName": "DSH Connect WorkBuddy",
4
4
  "description": "Connect locally signed-in WorkBuddy models to DeepSeek Harness with a read-only credits overview and model management.",
5
- "version": "2.0.10",
5
+ "version": "2.0.12",
6
6
  "author": "dingminhua",
7
7
  "copyright": "Copyright (c) 2026 LaoDing",
8
8
  "license": "MIT",
@@ -32,6 +32,9 @@
32
32
  "./cordis.patch.yml": "./cordis.patch.yml",
33
33
  "./package.json": "./package.json"
34
34
  },
35
+ "dependencies": {
36
+ "@deepseek-ai/schemastery": "3.18.4"
37
+ },
35
38
  "files": [
36
39
  "lib",
37
40
  "docs/assets",