@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.
- package/lib/adapter-registry.js +9 -14
- package/lib/alias-resolver.js +2 -2
- package/lib/client.js +11 -11
- package/lib/generic-adapter.js +59 -7
- package/lib/http.js +3 -4
- package/lib/models.js +2 -5
- package/lib/reliability.js +0 -3
- package/lib/synchronizer-transfer.js +1 -1
- package/package.json +6 -3
package/lib/adapter-registry.js
CHANGED
|
@@ -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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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')
|
package/lib/alias-resolver.js
CHANGED
|
@@ -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(
|
|
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.
|
|
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
|
|
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
|
|
36
|
-
'.dms-btn{margin:0!important;border:1px solid var(--dsw-alias-border-l2
|
|
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
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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,
|
package/lib/generic-adapter.js
CHANGED
|
@@ -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
|
|
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 >
|
|
15
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
16
16
|
throw new Error('model catalog response is too large')
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
150
|
-
|
|
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
|
package/lib/reliability.js
CHANGED
|
@@ -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.
|
|
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/
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
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
|
}
|