@huai_xin/dsh-workbuddy 0.1.18 → 0.1.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huai_xin/dsh-workbuddy",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "WorkBuddy model provider for DeepSeek Harness (international + China deployments)",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -78,15 +78,20 @@ function readFile(path) {
78
78
  if (typeof parsed.savedAt !== 'number' || !Number.isFinite(parsed.savedAt)) return undefined
79
79
  return {
80
80
  // Volatile fields are dropped on read too: a snapshot written before they
81
- // were excluded must not leak a stale rate either. The admission policy
82
- // is applied as well — the cache appends, so an id excluded later has to
83
- // be filtered when reading. Because `credits` is never cached, the policy
84
- // reduces to "whitelisted" for cached entries, which is exactly the part
85
- // of the list that does not depend on a live rate.
81
+ // were excluded must not leak a stale rate either. The admission policy is
82
+ // applied as well — the cache appends, so an id excluded later has to be
83
+ // filtered when reading.
84
+ //
85
+ // It is the *structural* half of the policy, not `keepModel`: that one also
86
+ // demands a live credits rate, and rates are never cached. Applying it here
87
+ // filtered out every entry a `/v3/config` read had produced, so the fallback
88
+ // only worked for ids the account-scoped payload had touched too. What a cached
89
+ // entry proves is that the deployment lists it — which is exactly what the cache
90
+ // is for.
86
91
  entries: parsed.entries
87
92
  .filter((entry) => isCatalogModelId(entry?.id))
88
93
  .map((entry) => Object.fromEntries(Object.entries(entry).filter(([key]) => !VOLATILE_FIELDS.has(key))))
89
- .filter(keepModel),
94
+ .filter((entry) => entry.preset !== true),
90
95
  source: typeof parsed.source === 'string' ? parsed.source : 'unknown',
91
96
  savedAt: parsed.savedAt,
92
97
  /** How many models the read behind this snapshot actually returned. */
package/src/catalog.js CHANGED
@@ -294,6 +294,14 @@ export function withConsistentEfforts(entry, candidates = []) {
294
294
  * fields the primary source left empty are taken, so scoped stays authoritative
295
295
  * for what it does declare.
296
296
  */
297
+ /** The first argument that is a non-empty string, or undefined. */
298
+ function firstNonEmpty(...values) {
299
+ for (const value of values) {
300
+ if (typeof value === 'string' && value.trim().length > 0) return value
301
+ }
302
+ return undefined
303
+ }
304
+
297
305
  export function mergeEntries(entries, extra) {
298
306
  if (extra.length === 0) return entries
299
307
  const byId = new Map(extra.map((entry) => [entry.id, entry]))
@@ -304,6 +312,11 @@ export function mergeEntries(entries, extra) {
304
312
  .sort(byEffortOrder)
305
313
  const merged = {
306
314
  ...entry,
315
+ // A name is required by the harness (`listModels` rejects the whole provider with
316
+ // INVALID_CATALOG on an empty one), and the two sources disagree on which models
317
+ // carry one. Take the non-empty side, and fall back to the id so a sparse record
318
+ // can never blank out a catalog.
319
+ name: firstNonEmpty(entry.name, other.name, entry.id),
307
320
  ...(entry.descriptionZh === undefined && other.descriptionZh !== undefined ? { descriptionZh: other.descriptionZh } : {}),
308
321
  ...(entry.descriptionEn === undefined && other.descriptionEn !== undefined ? { descriptionEn: other.descriptionEn } : {}),
309
322
  ...(entry.credits === undefined && other.credits !== undefined ? { credits: other.credits } : {}),