@goodandready/dsh-model-sync 0.5.7 → 0.5.8

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.
@@ -1,8 +1,6 @@
1
1
  import { catalogRequestError } from './reliability.js'
2
- import { canUseGenericAdapter, createGenericAdapter } from './generic-adapter.js'
2
+ import { canUseGenericAdapter, createGenericAdapter, readBoundedText, MAX_RESPONSE_BYTES } from './generic-adapter.js'
3
3
  import { normalizeModels, parseClinePassModels } from './models.js'
4
-
5
- const MAX_RESPONSE_BYTES = 4 * 1024 * 1024
6
4
  const SAFE_AUTH = new Set(['bearer', 'x-api-key', 'query-key', 'none'])
7
5
  const SAFE_PARSERS = new Set(['openai', 'google', 'clinebot-plan'])
8
6
  const CAPABILITIES = new Set(['vision', 'tools', 'reasoning', 'embeddings', 'code'])
@@ -214,17 +212,14 @@ async function fetchSpecific(profile, descriptor, options) {
214
212
  }
215
213
  const respEtag = response.headers?.get?.('etag') || undefined
216
214
  const respLastMod = response.headers?.get?.('last-modified') || undefined
217
- const declared = Number(response.headers?.get?.('content-length') ?? NaN)
218
- if (Number.isFinite(declared) && declared > MAX_RESPONSE_BYTES) {
219
- const error = new Error('model catalog response is too large')
220
- error.adapterCode = 'schema'
221
- throw error
222
- }
223
- const text = await response.text()
224
- if (Buffer.byteLength(text, 'utf8') > MAX_RESPONSE_BYTES) {
225
- const error = new Error('model catalog response is too large')
226
- error.adapterCode = 'schema'
227
- throw error
215
+ let text
216
+ try {
217
+ text = await readBoundedText(response, MAX_RESPONSE_BYTES)
218
+ } catch (err) {
219
+ if (err.message === 'model catalog response is too large') {
220
+ err.adapterCode = 'schema'
221
+ }
222
+ throw err
228
223
  }
229
224
  try { payload = JSON.parse(text) } catch {
230
225
  const error = new Error('model catalog response is not valid JSON')
@@ -15,7 +15,7 @@ export function validateAliasName(name) {
15
15
  return /^@?[a-zA-Z0-9_\-.]{1,64}$/.test(clean)
16
16
  }
17
17
 
18
- function modelScoreForCode(provider, model) {
18
+ function modelScoreForCode(model) {
19
19
  let score = 0
20
20
  const id = String(model?.id ?? '').toLowerCase()
21
21
  const name = String(model?.name ?? '').toLowerCase()
@@ -208,7 +208,7 @@ export function resolveAlias(aliasName, {
208
208
  if (strategy === 'best-code') {
209
209
  const scored = available.map((item) => ({
210
210
  ...item,
211
- score: modelScoreForCode(item.provider, item.model),
211
+ score: modelScoreForCode(item.model),
212
212
  }))
213
213
  scored.sort((a, b) => b.score - a.score)
214
214
  const best = scored[0]
package/lib/client.js CHANGED
@@ -25,23 +25,23 @@ window.__ModuleLoader__.load({
25
25
  '.dms-card-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px;display:flex;flex-direction:column;gap:10px;box-sizing:border-box}' +
26
26
  '.dms-wrap{display:flex;flex-direction:column;gap:14px;max-width:760px;box-sizing:border-box;color:var(--dsw-alias-label-primary,inherit)}' +
27
27
  '.dms-wrap>p{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55}' +
28
- '.dms-toolbar,.dms-section{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;gap:10px;padding:13px 14px;border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-primary));border-radius:10px;background:var(--dsw-alias-bg-layer-1,transparent);box-sizing:border-box}' +
28
+ '.dms-toolbar,.dms-section{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;gap:10px;padding:13px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1,transparent);box-sizing:border-box}' +
29
29
  '.dms-toolbar>select{flex:1 1 190px;min-width:190px}' +
30
30
  '.dms-toolbar>.dms-actions{flex-basis:100%}' +
31
31
  '.dms-toolbar>label{display:inline-flex;align-items:flex-start;gap:7px;font-size:12px;line-height:1.35;color:var(--dsw-alias-label-secondary)}' +
32
32
  '.dms-toolbar>select,.dms-editor input,.dms-editor textarea,.dms-input{box-sizing:border-box;min-width:0;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-2,transparent));border:1px solid var(--dsw-alias-border-l1,var(--dsw-alias-border-l2));border-radius:6px;color:var(--dsw-alias-label-primary);font:inherit;padding:7px 9px}' +
33
33
  '.dms-toolbar>label input{accent-color:var(--dsw-alias-brand-primary,var(--dsw-alias-button-info-fill))}' +
34
34
  '.dms-actions{display:flex;align-items:center;flex-wrap:wrap;gap:7px}' +
35
- '.dms-wrap>.dms-actions{padding:10px 14px;border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-primary));border-radius:10px;background:var(--dsw-alias-bg-layer-1,transparent)}' +
36
- '.dms-btn{margin:0!important;border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-primary));border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:1.35;padding:6px 10px;cursor:pointer;white-space:normal}' +
35
+ '.dms-wrap>.dms-actions{padding:10px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1,transparent)}' +
36
+ '.dms-btn{margin:0!important;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:1.35;padding:6px 10px;cursor:pointer;white-space:normal}' +
37
37
  '.dms-btn:hover:not(:disabled){border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-2)}' +
38
38
  '.dms-btn:disabled{opacity:.45;cursor:default}' +
39
39
  '.dms-section>strong{display:block;font-size:13px;font-weight:650}' +
40
40
  '.dms-provider-wrap{margin:0 0 7px}' +
41
- '.dms-provider{display:grid!important;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:10px;padding:10px 11px;margin:0;border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-primary));border-radius:8px;background:var(--dsw-alias-bg-layer-2,transparent)}' +
41
+ '.dms-provider{display:grid!important;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:10px;padding:10px 11px;margin:0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2,transparent)}' +
42
42
  '.dms-provider>span{min-width:0;overflow-wrap:anywhere;line-height:1.4}' +
43
43
  '.dms-section>div[style]{padding:8px 0;border-top:1px solid var(--dsw-alias-border-l2)}' +
44
- '.dms-editor{display:flex;flex-direction:column;align-items:stretch;gap:10px;padding:13px 14px;border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-primary));border-radius:10px;background:var(--dsw-alias-bg-layer-1,transparent);box-sizing:border-box;border-color:var(--dsw-alias-brand-primary,var(--dsw-alias-border-l2))}' +
44
+ '.dms-editor{display:flex;flex-direction:column;align-items:stretch;gap:10px;padding:13px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1,transparent);box-sizing:border-box;border-color:var(--dsw-alias-brand-primary,var(--dsw-alias-border-l2))}' +
45
45
  '.dms-editor strong{font-size:13px}' +
46
46
  '.dms-editor label{display:block;gap:5px;margin-top:8px;color:var(--dsw-alias-label-secondary);font-size:12px}' +
47
47
  '.dms-editor label input,.dms-editor label textarea{width:100%}' +
@@ -53,10 +53,10 @@ window.__ModuleLoader__.load({
53
53
  '.dms-grid-2{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px}' +
54
54
  '.dms-badge{font-size:11px;padding:2px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);display:inline-flex;align-items:center;gap:4px;font-weight:500}' +
55
55
  '.dms-badge-ok{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary);background:var(--dsw-alias-state-success-secondary,var(--dsw-alias-bg-layer-2))}' +
56
- '.dms-badge-warn{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:var(--dsw-alias-state-warning-secondary,var(--dsw-alias-bg-layer-2))}' +
56
+ '.dms-badge-warn{border-color:var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-state-warn-secondary,var(--dsw-alias-bg-layer-2))}' +
57
57
  '.dms-badge-bad{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-state-error-secondary,var(--dsw-alias-bg-layer-2))}' +
58
58
  '.dms-input{height:34px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border-radius:6px;padding:0 10px;font-size:13px;box-sizing:border-box}' +
59
- '.dms-input:focus{outline:none;border-color:var(--dsw-alias-state-brand-primary)}' +
59
+ '.dms-input:focus{outline:none;border-color:var(--dsw-alias-brand-primary)}' +
60
60
  '.dms-btn-primary{background:var(--dsw-alias-label-primary)!important;color:var(--dsw-alias-bg-layer-3)!important;border-color:transparent!important;font-weight:600}' +
61
61
  '.dms-btn-primary:hover:not(:disabled){opacity:0.88}' +
62
62
  '.dms-btn-danger{color:var(--dsw-alias-state-error-primary)!important;border-color:var(--dsw-alias-state-error-primary)!important}' +
@@ -517,7 +517,7 @@ window.__ModuleLoader__.load({
517
517
  }, updaterChecking ? t.checkingUpdates : (updaterStatus ? t.upToDate : t.checkForUpdates))
518
518
  )
519
519
  ),
520
- updaterNotice ? React.createElement('div', { style: { color: 'var(--dsw-alias-state-success)', marginTop: 8, fontSize: 12 } }, updaterNotice) : null,
520
+ updaterNotice ? React.createElement('div', { style: { color: 'var(--dsw-alias-state-success-primary)', marginTop: 8, fontSize: 12 } }, updaterNotice) : null,
521
521
  updaterError ? React.createElement('div', { style: { color: 'var(--dsw-alias-state-error-primary)', marginTop: 8, fontSize: 12 } }, updaterError) : null
522
522
  )
523
523
  }
@@ -1117,7 +1117,7 @@ window.__ModuleLoader__.load({
1117
1117
  return React.createElement('div', { key: p.name, style: { display: 'flex', flexDirection: 'column', gap: 4, padding: '8px 10px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 6, background: 'var(--dsw-alias-bg-layer-2)' } },
1118
1118
  React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
1119
1119
  React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
1120
- React.createElement('strong', { style: { fontFamily: 'monospace', fontSize: 13, color: 'var(--dsw-alias-state-brand-primary)' } }, p.name),
1120
+ React.createElement('strong', { style: { fontFamily: 'monospace', fontSize: 13, color: 'var(--dsw-alias-brand-primary)' } }, p.name),
1121
1121
  React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, p.label)
1122
1122
  ),
1123
1123
  button(isResolving ? t.resolvingAlias : t.resolveAlias, () => resolveSmartAlias(p.name), 'secondary')
@@ -1318,8 +1318,8 @@ window.__ModuleLoader__.load({
1318
1318
  React.createElement('input', { type: 'checkbox', checked: selectionDraft.includes(model.id), onChange: (e) => setSelectionDraft((current) => e.target.checked ? [...current, model.id] : current.filter((id) => id !== model.id)) }),
1319
1319
  ` ${model.name ?? model.id} (${model.id})`,
1320
1320
  isNewModel(model, row) ? React.createElement('span', { style: { marginLeft: 6, fontSize: 11, padding: '1px 5px', borderRadius: 4, background: 'var(--dsw-alias-state-business-primary)', color: 'white' } }, 'NEW') : null,
1321
- deprecationDays !== null ? React.createElement('span', { style: { marginLeft: 6, fontSize: 10, padding: '1px 5px', borderRadius: 4, background: 'var(--dsw-alias-state-warning-primary)', color: 'white' } }, deprecationDays > 0 ? t.deprecatesIn.replace('%d', deprecationDays) : t.deprecated) : null,
1322
- (model.contextWindow && maxContext > 0 && model.contextWindow >= maxContext) ? React.createElement('span', { style: { marginLeft: 4, fontSize: 10, padding: '1px 4px', borderRadius: 3, background: 'var(--dsw-alias-state-warning-primary)', color: 'white' } }, '★') : null,
1321
+ deprecationDays !== null ? React.createElement('span', { style: { marginLeft: 6, fontSize: 10, padding: '1px 5px', borderRadius: 4, background: 'var(--dsw-alias-state-warn-primary)', color: 'white' } }, deprecationDays > 0 ? t.deprecatesIn.replace('%d', deprecationDays) : t.deprecated) : null,
1322
+ (model.contextWindow && maxContext > 0 && model.contextWindow >= maxContext) ? React.createElement('span', { style: { marginLeft: 4, fontSize: 10, padding: '1px 4px', borderRadius: 3, background: 'var(--dsw-alias-state-warn-primary)', color: 'white' } }, '★') : null,
1323
1323
  (model.pricing && minPrice !== Infinity && model.pricing.inputPerToken === minPrice) ? React.createElement('span', { style: { marginLeft: 4, fontSize: 10, padding: '1px 4px', borderRadius: 3, background: 'var(--dsw-alias-state-success-primary)', color: 'white' } }, '$') : null,
1324
1324
  model.tags && model.tags.length ? React.createElement('small', { style: { marginLeft: 6, color: 'var(--dsw-alias-label-secondary)' } }, model.tags.join(', ')) : null,
1325
1325
  model.pricing ? React.createElement('small', { style: { marginLeft: 6, color: 'var(--dsw-alias-label-secondary)', fontSize: 11 } }, `in $${(model.pricing.inputPerToken * 1000000).toFixed(2)}/1M` + (model.pricing.cacheReadPerToken !== undefined ? ` / cache $${(model.pricing.cacheReadPerToken * 1000000).toFixed(2)}` : '') + (model.pricing.outputPerToken !== undefined ? ` / out $${(model.pricing.outputPerToken * 1000000).toFixed(2)}` : '')) : null,
@@ -1,7 +1,7 @@
1
1
  import { catalogRequestError } from './reliability.js'
2
2
  import { normalizeModels } from './models.js'
3
3
 
4
- const MAX_RESPONSE_BYTES = 4 * 1024 * 1024
4
+ export const MAX_RESPONSE_BYTES = 4 * 1024 * 1024
5
5
  const SUPPORTED_APIS = new Set(['openai-completions', 'openai-responses'])
6
6
 
7
7
  function endpointFor(baseURL) {
@@ -10,16 +10,68 @@ function endpointFor(baseURL) {
10
10
  return base.endsWith('/models') ? base : `${base}/models`
11
11
  }
12
12
 
13
- async function readBody(response) {
13
+ export async function readBoundedText(response, maxBytes = MAX_RESPONSE_BYTES) {
14
14
  const declared = Number(response.headers?.get?.('content-length') ?? NaN)
15
- if (Number.isFinite(declared) && declared > MAX_RESPONSE_BYTES) {
15
+ if (Number.isFinite(declared) && declared > maxBytes) {
16
16
  throw new Error('model catalog response is too large')
17
17
  }
18
- const text = await response.text()
19
- if (Buffer.byteLength(text, 'utf8') > MAX_RESPONSE_BYTES) {
20
- throw new Error('model catalog response is too large')
18
+
19
+ if (response.body && typeof response.body[Symbol.asyncIterator] === 'function') {
20
+ const chunks = []
21
+ let totalBytes = 0
22
+ for await (const chunk of response.body) {
23
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
24
+ totalBytes += buf.byteLength
25
+ if (totalBytes > maxBytes) {
26
+ if (typeof response.body.cancel === 'function') {
27
+ try { await response.body.cancel('response too large') } catch (_err) { /* best-effort */ }
28
+ } else if (typeof response.body.destroy === 'function') {
29
+ try { response.body.destroy() } catch (_err) { /* best-effort */ }
30
+ }
31
+ throw new Error('model catalog response is too large')
32
+ }
33
+ chunks.push(buf)
34
+ }
35
+ return Buffer.concat(chunks).toString('utf8')
21
36
  }
22
- return text
37
+
38
+ if (response.body && typeof response.body.getReader === 'function') {
39
+ const reader = response.body.getReader()
40
+ const chunks = []
41
+ let totalBytes = 0
42
+ try {
43
+ while (true) {
44
+ const { done, value } = await reader.read()
45
+ if (done) break
46
+ if (value) {
47
+ const buf = Buffer.isBuffer(value) ? value : Buffer.from(value)
48
+ totalBytes += buf.byteLength
49
+ if (totalBytes > maxBytes) {
50
+ try { await reader.cancel('response too large') } catch (_err) { /* best-effort */ }
51
+ throw new Error('model catalog response is too large')
52
+ }
53
+ chunks.push(buf)
54
+ }
55
+ }
56
+ } finally {
57
+ try { reader.releaseLock?.() } catch (_err) { /* best-effort */ }
58
+ }
59
+ return Buffer.concat(chunks).toString('utf8')
60
+ }
61
+
62
+ if (typeof response.text === 'function') {
63
+ const text = await response.text()
64
+ if (Buffer.byteLength(text, 'utf8') > maxBytes) {
65
+ throw new Error('model catalog response is too large')
66
+ }
67
+ return text
68
+ }
69
+
70
+ return ''
71
+ }
72
+
73
+ async function readBody(response) {
74
+ return readBoundedText(response, MAX_RESPONSE_BYTES)
23
75
  }
24
76
 
25
77
  function rowsFrom(payload) {
package/lib/http.js CHANGED
@@ -1,4 +1,7 @@
1
+ import crypto from 'node:crypto'
1
2
  import { validateAliasName } from './alias-resolver.js'
3
+ import { validatePolicy } from './policy.js'
4
+
2
5
  function normalizeWebhookTestRequest(body) {
3
6
  if (!body || typeof body !== "object") throw Object.assign(new Error("body is required"), { code: "INVALID_REQUEST" })
4
7
  if (typeof body.url !== "string" || !body.url.trim()) throw Object.assign(new Error("url is required"), { code: "INVALID_REQUEST" })
@@ -30,8 +33,6 @@ function normalizeWebhookTestRequest(body) {
30
33
  secret: typeof body.secret === "string" ? body.secret.trim() : "",
31
34
  }
32
35
  }
33
- import crypto from 'node:crypto'
34
- import { validatePolicy } from './policy.js'
35
36
 
36
37
  const BASE = '/dsh-model-sync'
37
38
 
@@ -82,8 +83,6 @@ export function trusted(req, { requireLoopback = false } = {}) {
82
83
  }
83
84
  // If sec-fetch-site is same-origin or same-site, allow
84
85
  if (fetchSite === 'same-origin' || fetchSite === 'same-site') return true
85
- // If sec-fetch-mode is present but origin/referer missing on a cross-site request, reject
86
- if (req.headers?.['sec-fetch-mode'] && !host) return false
87
86
  // For requests requiring loopback or POST without any browser origin/referer headers, require explicit loopback
88
87
  if ((requireLoopback || req?.method === 'POST') && !origin && !referer) {
89
88
  if (!isLoopbackAddress(remoteAddress ?? (req?.headers?.host ? req.headers.host.split(':')[0] : ''))) return false
package/lib/models.js CHANGED
@@ -146,11 +146,8 @@ function normalizeCapabilities(row) {
146
146
  if (!result.code && (/(^|[_-])(coder|code|starcoder|codellama)($|[_-])/i.test(idStr) || idStr.includes('coder') || idStr.includes('code-'))) {
147
147
  result.code = true
148
148
  }
149
- if (!result.vision && (/(^|[_-])(vision|vl|multimodal|4v|4o|flash)($|[_-])/i.test(idStr) || idStr.includes('-vl') || idStr.includes('-vision'))) {
150
- // Only set vision if clearly indicated by vision/vl tags
151
- if (/(^|[_-])(vision|vl)($|[_-])/i.test(idStr) || idStr.includes('-vl') || idStr.includes('-vision')) {
152
- result.vision = true
153
- }
149
+ if (!result.vision && (/(^|[_-])(vision|vl)($|[_-])/i.test(idStr) || idStr.includes('-vl') || idStr.includes('-vision'))) {
150
+ result.vision = true
154
151
  }
155
152
 
156
153
  return Object.keys(result).length > 0 ? result : undefined
@@ -110,13 +110,11 @@ export async function retryWithBackoff(operation, {
110
110
  onRetry,
111
111
  } = {}) {
112
112
  const total = Math.max(1, Math.floor(Number(attempts) || 1))
113
- let lastError
114
113
  for (let attempt = 1; attempt <= total; attempt += 1) {
115
114
  if (signal?.aborted) throw signal.reason ?? abortError()
116
115
  try {
117
116
  return await operation({ attempt })
118
117
  } catch (error) {
119
- lastError = error
120
118
  if (attempt >= total || !isRetryableError(error)) throw error
121
119
  const exponential = Math.max(0, Number(baseDelayMs) || 0) * (2 ** (attempt - 1))
122
120
  // Full jitter: randomized between [0.5, 1.0] of calculated exponential backoff
@@ -130,7 +128,6 @@ export async function retryWithBackoff(operation, {
130
128
  await delay(delayMs, signal)
131
129
  }
132
130
  }
133
- throw lastError
134
131
  }
135
132
 
136
133
  export async function mapWithConcurrency(items, worker, concurrency = 4) {
@@ -54,7 +54,7 @@ export function createConfigTransfer({
54
54
  importedProviders.push(pname)
55
55
  }
56
56
  if (Array.isArray(pdata.selectedModels)) {
57
- currentSelections[pname] = pdata.selectedModels.filter((m) => typeof m === 'string')
57
+ currentSelections[pname] = [...new Set(pdata.selectedModels.filter((m) => typeof m === 'string' && m.trim()).map((m) => m.trim()))].slice(0, 1000)
58
58
  importedProviders.push(pname)
59
59
  }
60
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-model-sync",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "Automatic model catalog synchronization for API-key DeepSeek Harness providers.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -53,7 +53,10 @@
53
53
  "license": "MIT",
54
54
  "peerDependencies": {
55
55
  "@deepseek-ai/cordis": "^4.0.1",
56
- "@deepseek-ai/schemastery": "^3.18.4",
57
- "@deepseek-ai/dsh-llm": "^0.1.7-rc.2 || ^0.2.0-rc.1"
56
+ "@deepseek-ai/dsh-credentials": "^0.1.7-rc.2 || ^0.2.0-rc.1",
57
+ "@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.2 || ^0.2.0-rc.1",
58
+ "@deepseek-ai/dsh-llm": "^0.1.7-rc.2 || ^0.2.0-rc.1",
59
+ "@deepseek-ai/dsh-settings": "^0.1.7-rc.2 || ^0.2.0-rc.1",
60
+ "@deepseek-ai/schemastery": "^3.18.4"
58
61
  }
59
62
  }