@goodandready/dsh-subscriptions 0.7.30 โ 0.7.32
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/account-reliability.js +68 -0
- package/lib/accounts-view.js +2 -0
- package/lib/accounts.js +28 -0
- package/lib/client.js +80 -1
- package/lib/index.js +3 -3
- package/lib/models-catalog.js +12 -0
- package/package.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
function text(value) {
|
|
2
|
+
return value == null ? '' : String(value).trim()
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* #827: decide whether a stored credential is usable without exposing it.
|
|
7
|
+
* Empty access/refresh tokens and non-finite expiry are treated as corrupt.
|
|
8
|
+
*/
|
|
9
|
+
export function credentialState(blob) {
|
|
10
|
+
if (!blob || typeof blob !== 'object') return { usable: false, reason: 'invalid_blob' }
|
|
11
|
+
const accessToken = text(blob.accessToken)
|
|
12
|
+
const refreshToken = text(blob.refreshToken)
|
|
13
|
+
if (!accessToken && !refreshToken && !text(blob.apiKey)) {
|
|
14
|
+
return { usable: false, reason: 'empty_credentials' }
|
|
15
|
+
}
|
|
16
|
+
if (blob.expiresAt != null && blob.expiresAt !== '' && !Number.isFinite(Number(blob.expiresAt))) {
|
|
17
|
+
return { usable: false, reason: 'invalid_expiry' }
|
|
18
|
+
}
|
|
19
|
+
return { usable: true, reason: null }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* #831: stable provider identity used for duplicate detection. Unknown
|
|
24
|
+
* identities return null and must never be merged automatically.
|
|
25
|
+
*/
|
|
26
|
+
export function accountIdentityKey(provider, blob) {
|
|
27
|
+
if (!blob || typeof blob !== 'object') return null
|
|
28
|
+
const id = text(blob.email || blob.login || blob.accountId || blob.userId)
|
|
29
|
+
if (!id) return null
|
|
30
|
+
return `${text(provider).toLowerCase()}:${id.toLowerCase()}`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* #818: aliases are display-only. Resolve either a stable ref or a legacy
|
|
35
|
+
* label within one provider back to the stable credential ref.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveAccountRef(slots, provider, value) {
|
|
38
|
+
const wanted = text(value)
|
|
39
|
+
if (!wanted) return null
|
|
40
|
+
const list = (Array.isArray(slots) ? slots : []).filter((slot) => slot && slot.provider === provider)
|
|
41
|
+
const byIndex = Number(wanted)
|
|
42
|
+
if (Number.isInteger(byIndex) && byIndex > 0) {
|
|
43
|
+
return list.find((slot) => Number(slot.index) === byIndex)?.ref || null
|
|
44
|
+
}
|
|
45
|
+
return list.find((slot) => slot.ref === wanted || text(slot.label) === wanted)?.ref || null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* #806: a delayed refresh may only write when its credential lineage is
|
|
50
|
+
* still current. A newer save/revoke bumps the version.
|
|
51
|
+
*/
|
|
52
|
+
export function refreshIsCurrent(currentVersion, expectedVersion) {
|
|
53
|
+
return Number(currentVersion) === Number(expectedVersion)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* #833: deterministic host-env vs plugin-proxy precedence.
|
|
58
|
+
* @param {{ hostProxy?: string, pluginProxy?: string, prefer?: 'host' | 'plugin' }} [options]
|
|
59
|
+
*/
|
|
60
|
+
export function proxyPrecedence(options = {}) {
|
|
61
|
+
const prefer = options.prefer || 'host'
|
|
62
|
+
const host = text(options.hostProxy)
|
|
63
|
+
const plugin = text(options.pluginProxy)
|
|
64
|
+
if (host && plugin && prefer === 'plugin') return { proxy: plugin, source: 'plugin', conflict: true }
|
|
65
|
+
if (host) return { proxy: host, source: 'host', conflict: Boolean(plugin) }
|
|
66
|
+
if (plugin) return { proxy: plugin, source: 'plugin', conflict: false }
|
|
67
|
+
return { proxy: '', source: 'none', conflict: false }
|
|
68
|
+
}
|
package/lib/accounts-view.js
CHANGED
|
@@ -169,6 +169,8 @@ export async function createAccountsView({ live, store, fetch, pmL, pmE, concurr
|
|
|
169
169
|
poolEnabled: slot.poolEnabled !== false,
|
|
170
170
|
isDefault: !!slot.isDefault,
|
|
171
171
|
...(Array.isArray(slot.poolModels) ? { poolModels: slot.poolModels } : {}),
|
|
172
|
+
credentialError: info.credentialError || null,
|
|
173
|
+
duplicateOf: info.duplicateOf || null,
|
|
172
174
|
capabilities,
|
|
173
175
|
billing,
|
|
174
176
|
renewsAt: billing.renewsAt,
|
package/lib/accounts.js
CHANGED
|
@@ -46,6 +46,7 @@ import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
|
|
46
46
|
import { oauthRef, isProvider } from './refs.js'
|
|
47
47
|
import { parseBlob, serializeBlob } from './blob.js'
|
|
48
48
|
import { encryptWithPassphrase, decryptWithPassphrase } from './crypto.js'
|
|
49
|
+
import { accountIdentityKey, credentialState } from './account-reliability.js'
|
|
49
50
|
import { recordSwitch, recordExhaust, recordBroken, computeHealthScore, healthBadge } from './health.js'
|
|
50
51
|
import { executeWithRetry } from './backoff.js'
|
|
51
52
|
import { getVendor } from './vendors/index.js'
|
|
@@ -308,6 +309,26 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
308
309
|
if (base.configured) {
|
|
309
310
|
try {
|
|
310
311
|
const blob = parseBlob(await resolveRaw(ref))
|
|
312
|
+
const state = credentialState(blob)
|
|
313
|
+
if (!state.usable) {
|
|
314
|
+
base.configured = false
|
|
315
|
+
base.credentialError = state.reason
|
|
316
|
+
base.label = blob.label || blob.email || ''
|
|
317
|
+
return {
|
|
318
|
+
...base,
|
|
319
|
+
cooldownUntil: 0,
|
|
320
|
+
quarantineUntil: 0,
|
|
321
|
+
cooldownFamilies: null,
|
|
322
|
+
cooldownModels: null,
|
|
323
|
+
quota: null,
|
|
324
|
+
isRevoked: false,
|
|
325
|
+
authWarning: '',
|
|
326
|
+
validationUrl: '',
|
|
327
|
+
validationMessage: '',
|
|
328
|
+
accountNotice: '',
|
|
329
|
+
paidTierName: '',
|
|
330
|
+
}
|
|
331
|
+
}
|
|
311
332
|
base.label = blob.label || blob.email || ''
|
|
312
333
|
base.email = blob.email || ''
|
|
313
334
|
base.plan = blob.plan || blob.paidTierName || ''
|
|
@@ -367,6 +388,7 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
367
388
|
latencyGrade: pings.get(ref)?.latencyGrade ?? null,
|
|
368
389
|
lastDoctorVerdict: doctorVerdicts.get(ref) || null,
|
|
369
390
|
availableModels: dynamicModels.get(ref)?.models || null,
|
|
391
|
+
credentialError: base.credentialError || null,
|
|
370
392
|
}
|
|
371
393
|
}
|
|
372
394
|
|
|
@@ -374,8 +396,12 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
374
396
|
const curConfig = getConfig()
|
|
375
397
|
const slots = normalizeSlots(curConfig.slots).filter((s) => s.provider === provider)
|
|
376
398
|
const out = []
|
|
399
|
+
const identities = new Map()
|
|
377
400
|
for (const slot of slots) {
|
|
378
401
|
const info = await describeRef(slot.ref, curConfig)
|
|
402
|
+
const identity = accountIdentityKey(provider, { email: info.email, accountId: info.accountId })
|
|
403
|
+
const duplicateOf = identity ? (identities.get(identity) || null) : null
|
|
404
|
+
if (identity && !duplicateOf) identities.set(identity, slot.ref)
|
|
379
405
|
const q = quarantines.get(slot.ref)
|
|
380
406
|
out.push({
|
|
381
407
|
ref: slot.ref,
|
|
@@ -400,6 +426,8 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
400
426
|
poolEnabled: slot.poolEnabled !== false,
|
|
401
427
|
isDefault: !!slot.isDefault,
|
|
402
428
|
...(Array.isArray(slot.poolModels) ? { poolModels: slot.poolModels } : {}),
|
|
429
|
+
credentialError: info.credentialError || null,
|
|
430
|
+
duplicateOf,
|
|
403
431
|
})
|
|
404
432
|
}
|
|
405
433
|
return out
|
package/lib/client.js
CHANGED
|
@@ -202,6 +202,10 @@ window.__ModuleLoader__.load({
|
|
|
202
202
|
"modelShowMore": "Show more",
|
|
203
203
|
"modelShowAll": "Show all",
|
|
204
204
|
"modelNoMatches": "No models match this search.",
|
|
205
|
+
"modelCatalogSourceHint": "Catalog source: live, cached or builtin",
|
|
206
|
+
"modelNamesTitle": "Model display names",
|
|
207
|
+
"modelNamesHint": "Presentation only: routing, reasoning and model ids stay unchanged. Reset restores the catalog name.",
|
|
208
|
+
"modelNameReset": "Reset",
|
|
205
209
|
"rotationDecisionsTitle": "Rotation & Routing Decisions",
|
|
206
210
|
"rotationDecisionsDesc": "Real-time decision trace explaining account selection, skips, and fallback reasons.",
|
|
207
211
|
"rotationAttempt": "attempt",
|
|
@@ -308,7 +312,10 @@ window.__ModuleLoader__.load({
|
|
|
308
312
|
"maxQueueSizeSlot": "Queue",
|
|
309
313
|
"save": "Save",
|
|
310
314
|
"saved": "Saved",
|
|
315
|
+
"cancel": "Cancel",
|
|
316
|
+
"unsavedChanges": "Unsaved changes",
|
|
311
317
|
"accountLabel": "Account",
|
|
318
|
+
"usageRemaining": "Remaining quota",
|
|
312
319
|
"plan": "Plan",
|
|
313
320
|
"storedAs": "Stored as",
|
|
314
321
|
"slashFast": "Toggle Codex Speed (fast/standard)",
|
|
@@ -594,6 +601,10 @@ window.__ModuleLoader__.load({
|
|
|
594
601
|
"modelShowMore": "ๆพ็คบๆดๅค",
|
|
595
602
|
"modelShowAll": "ๆพ็คบๅ
จ้จ",
|
|
596
603
|
"modelNoMatches": "ๆฒกๆๅน้
็ๆจกๅใ",
|
|
604
|
+
"modelCatalogSourceHint": "็ฎๅฝๆฅๆบ๏ผๅฎๆถใ็ผๅญๆๅ
็ฝฎ",
|
|
605
|
+
"modelNamesTitle": "ๆจกๅๆพ็คบๅ็งฐ",
|
|
606
|
+
"modelNamesHint": "ไป
ๅฝฑๅๆพ็คบ๏ผ่ทฏ็ฑใๆจ็ๅๆจกๅ ID ไธๅใ้็ฝฎๆขๅค็ฎๅฝๅ็งฐใ",
|
|
607
|
+
"modelNameReset": "้็ฝฎ",
|
|
597
608
|
"rotationDecisionsTitle": "่ฝฎ่ฏข่ฐๅบฆไธ่ทฏ็ฑๅณ็ญ",
|
|
598
609
|
"rotationDecisionsDesc": "ๅฎๆถๅณ็ญ่ฟฝ่ธช๏ผ้ๆๅฑ็คบๆฏๆฌก่ฏทๆฑ็่ดฆๅท้ๆฉใ่ทณ่ฟไธๅ้ๅๅ ใ",
|
|
599
610
|
"rotationAttempt": "ๆฌกๅฐ่ฏ",
|
|
@@ -700,7 +711,10 @@ window.__ModuleLoader__.load({
|
|
|
700
711
|
"maxQueueSizeSlot": "้ๅ",
|
|
701
712
|
"save": "ไฟๅญ",
|
|
702
713
|
"saved": "ๅทฒไฟๅญ",
|
|
714
|
+
"cancel": "ๅๆถ",
|
|
715
|
+
"unsavedChanges": "ๆๆชไฟๅญ็ๆดๆน",
|
|
703
716
|
"accountLabel": "่ดฆๆท",
|
|
717
|
+
"usageRemaining": "ๅฉไฝ้
้ข",
|
|
704
718
|
"plan": "ๅฅ้คๆนๆก",
|
|
705
719
|
"storedAs": "ๅญๅจๆ ่ฏ",
|
|
706
720
|
"slashFast": "ๅๆข Codex ๅ ้ๆจกๅผ๏ผๅฟซ้/ๆ ๅ๏ผ",
|
|
@@ -1419,6 +1433,7 @@ window.__ModuleLoader__.load({
|
|
|
1419
1433
|
}
|
|
1420
1434
|
const [checking, setChecking] = React.useState({})
|
|
1421
1435
|
const [saved, setSaved] = React.useState(false)
|
|
1436
|
+
const [savedSnapshot, setSavedSnapshot] = React.useState(null)
|
|
1422
1437
|
const [tokenDraft, setTokenDraft] = React.useState({})
|
|
1423
1438
|
const [openHelp, setOpenHelp] = React.useState({})
|
|
1424
1439
|
const [err, setErr] = React.useState('')
|
|
@@ -1440,7 +1455,9 @@ window.__ModuleLoader__.load({
|
|
|
1440
1455
|
|
|
1441
1456
|
const applyPayload = (data) => {
|
|
1442
1457
|
if (data && data.revision != null) setRevision(data.revision)
|
|
1443
|
-
|
|
1458
|
+
const next = JSON.parse(JSON.stringify((data && data.config) || {}))
|
|
1459
|
+
setDraft(next)
|
|
1460
|
+
setSavedSnapshot(JSON.parse(JSON.stringify(next)))
|
|
1444
1461
|
setAccounts((data && data.accounts) || [])
|
|
1445
1462
|
setProviders((data && data.providers) || [])
|
|
1446
1463
|
setModelsCatalog((data && data.modelsCatalog) || [])
|
|
@@ -1492,6 +1509,18 @@ window.__ModuleLoader__.load({
|
|
|
1492
1509
|
return () => timers.forEach(clearInterval)
|
|
1493
1510
|
}, [device])
|
|
1494
1511
|
|
|
1512
|
+
const dirty = Boolean(draft && savedSnapshot && JSON.stringify(draft) !== JSON.stringify(savedSnapshot))
|
|
1513
|
+
React.useEffect(() => {
|
|
1514
|
+
if (!dirty) return undefined
|
|
1515
|
+
const warn = (event) => {
|
|
1516
|
+
event.preventDefault()
|
|
1517
|
+
event.returnValue = ''
|
|
1518
|
+
return ''
|
|
1519
|
+
}
|
|
1520
|
+
window.addEventListener('beforeunload', warn)
|
|
1521
|
+
return () => window.removeEventListener('beforeunload', warn)
|
|
1522
|
+
}, [dirty])
|
|
1523
|
+
|
|
1495
1524
|
// #81: settings snapshot status - never render phantom inputs before the
|
|
1496
1525
|
// config snapshot arrives, and offer a retry when the store is unavailable.
|
|
1497
1526
|
if (!draft) {
|
|
@@ -2378,6 +2407,7 @@ h('div', { className: 'dsub-row' },
|
|
|
2378
2407
|
m.provider ? h('span', { className: 'dsub-brand dsub-brand-' + m.provider, style: { fontSize: '9px', padding: '1px 4px' } }, m.provider.toUpperCase()) : null,
|
|
2379
2408
|
h('code', null, m.name || m.id),
|
|
2380
2409
|
tags.length ? h('span', { className: 'dsub-model-tag' }, tags.join(' ยท ')) : null,
|
|
2410
|
+
Array.isArray(m.catalogSources) && m.catalogSources.length ? h('span', { className: 'dsub-model-tag', title: t('modelCatalogSourceHint') }, '๐ ' + m.catalogSources.join('/')) : null,
|
|
2381
2411
|
h('span', { className: 'dsub-model-tag ' + statusClass }, poolLabel),
|
|
2382
2412
|
)
|
|
2383
2413
|
})
|
|
@@ -2394,6 +2424,46 @@ h('div', { className: 'dsub-row' },
|
|
|
2394
2424
|
}, t('modelShowAll')),
|
|
2395
2425
|
) : null,
|
|
2396
2426
|
),
|
|
2427
|
+
h('details', { className: 'dsub-group', 'data-pane': 'models' },
|
|
2428
|
+
h('summary', { className: 'dsub-groupHead' }, '๐ท ' + t('modelNamesTitle')),
|
|
2429
|
+
h('div', { className: 'dsub-sub', style: { marginTop: 4 } }, t('modelNamesHint')),
|
|
2430
|
+
h('div', { style: { maxHeight: 260, overflow: 'auto', marginTop: 6 } },
|
|
2431
|
+
catalogShown.slice(0, 50).map((model) => {
|
|
2432
|
+
const current = (Array.isArray(draft.modelDisplayNames) ? draft.modelDisplayNames : [])
|
|
2433
|
+
.find((row) => row && row.provider === model.provider && String(row.id) === String(model.id))
|
|
2434
|
+
return h('div', { key: 'name:' + model.provider + ':' + model.id, className: 'dsub-row', style: { gap: 6 } },
|
|
2435
|
+
h('code', { style: { minWidth: 120, fontSize: 11 } }, model.id),
|
|
2436
|
+
h('input', {
|
|
2437
|
+
className: 'dsub-inp dsub-grow',
|
|
2438
|
+
value: current ? current.name : '',
|
|
2439
|
+
placeholder: model.name || model.id,
|
|
2440
|
+
onChange: (e) => {
|
|
2441
|
+
const value = e.target.value
|
|
2442
|
+
setDraft((d) => {
|
|
2443
|
+
const list = Array.isArray(d.modelDisplayNames) ? d.modelDisplayNames.slice() : []
|
|
2444
|
+
const idx = list.findIndex((row) => row && row.provider === model.provider && String(row.id) === String(model.id))
|
|
2445
|
+
if (value.trim()) {
|
|
2446
|
+
const next = { provider: model.provider, id: String(model.id), name: value.slice(0, 80) }
|
|
2447
|
+
if (idx >= 0) list[idx] = next
|
|
2448
|
+
else list.push(next)
|
|
2449
|
+
} else if (idx >= 0) {
|
|
2450
|
+
list.splice(idx, 1)
|
|
2451
|
+
}
|
|
2452
|
+
return Object.assign({}, d, { modelDisplayNames: list })
|
|
2453
|
+
})
|
|
2454
|
+
},
|
|
2455
|
+
}),
|
|
2456
|
+
h('button', {
|
|
2457
|
+
type: 'button', className: 'dsub-mini',
|
|
2458
|
+
onClick: () => setDraft((d) => Object.assign({}, d, {
|
|
2459
|
+
modelDisplayNames: (Array.isArray(d.modelDisplayNames) ? d.modelDisplayNames : [])
|
|
2460
|
+
.filter((row) => !(row && row.provider === model.provider && String(row.id) === String(model.id))),
|
|
2461
|
+
})),
|
|
2462
|
+
}, t('modelNameReset')),
|
|
2463
|
+
)
|
|
2464
|
+
})
|
|
2465
|
+
),
|
|
2466
|
+
),
|
|
2397
2467
|
h('details', { className: 'dsub-group', 'data-pane': 'models' },
|
|
2398
2468
|
h('summary', { className: 'dsub-groupHead' }, '๐ง ' + t('reasoningEffortTitle')),
|
|
2399
2469
|
h('div', { className: 'dsub-sub', style: { marginTop: '4px' } }, t('reasoningEffortDesc')),
|
|
@@ -2908,6 +2978,9 @@ h('div', { className: 'dsub-row' },
|
|
|
2908
2978
|
},
|
|
2909
2979
|
}, '\u00d7'),
|
|
2910
2980
|
),
|
|
2981
|
+
(account && (account.quota?.remainingPercent != null || account.usagePercent != null)) ? h('div', { className: 'dsub-sub', style: { marginTop: 4 } },
|
|
2982
|
+
'๐ ' + t('usageRemaining') + ' ' + Math.round(account.quota?.remainingPercent != null ? Number(account.quota.remainingPercent) : (100 - Number(account.usagePercent))) + '%'
|
|
2983
|
+
) : null,
|
|
2911
2984
|
(account && account.quarantineUntil && Number(account.quarantineUntil) > Date.now()) ? h('div', { className: 'dsub-row', style: { marginTop: 4 } },
|
|
2912
2985
|
h('span', { className: 'dsub-sub', style: { color: 'var(--dsw-alias-state-error-primary)', fontVariantNumeric: 'tabular-nums' } },
|
|
2913
2986
|
'โ ' + (account.quarantineReason || 'QUARANTINE') + ' ยท ' + t('resetsIn') + ' ' + formatRelativeReset(account.quarantineUntil, t('lang'), Date.now())
|
|
@@ -3538,6 +3611,12 @@ h('div', { className: 'dsub-row' },
|
|
|
3538
3611
|
type: 'button', className: 'dsub-save',
|
|
3539
3612
|
onClick: () => save().catch((e) => setErr(cleanErrorMessage(e.message || e))),
|
|
3540
3613
|
}, t('save')),
|
|
3614
|
+
h('button', {
|
|
3615
|
+
type: 'button', className: 'dsub-mini',
|
|
3616
|
+
disabled: !dirty,
|
|
3617
|
+
onClick: () => reload().catch((e) => setErr(cleanErrorMessage(e.message || e))),
|
|
3618
|
+
}, t('cancel')),
|
|
3619
|
+
dirty ? h('span', { className: 'dsub-sub' }, t('unsavedChanges')) : null,
|
|
3541
3620
|
saved ? h('span', { className: 'dsub-ok' }, t('saved')) : null,
|
|
3542
3621
|
info ? h('span', { className: 'dsub-ok' }, info) : null,
|
|
3543
3622
|
err ? h('span', { className: 'dsub-bad' }, err) : null,
|
package/lib/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { createSubscriptionsService } from './subscriptions.js'
|
|
|
18
18
|
import { registerSubscriptionTools } from './native-tools.js'
|
|
19
19
|
import { detectConflicts, readProfilePluginNames, setActiveConflicts } from './coexistence.js'
|
|
20
20
|
import { createAccountStore, defaultAccountRef, normalizeSlots, vendorConfig } from './accounts.js'
|
|
21
|
+
import { resolveAccountRef } from './account-reliability.js'
|
|
21
22
|
import { OllamaAdapter, ollamaAlive, ollamaModels, ollamaBase } from './ollama.js'
|
|
22
23
|
import { createResetCreditService } from './reset-credits.js'
|
|
23
24
|
import { maskEmail, maskLabel } from './mask.js'
|
|
@@ -276,9 +277,8 @@ export function apply(ctx, rawConfig) {
|
|
|
276
277
|
|
|
277
278
|
// #85: host-only reset credit service for Codex accounts.
|
|
278
279
|
const resetCredits = createResetCreditService({ loadBlob: (ref) => store.loadBlob(ref) })
|
|
279
|
-
function refForSlot(provider,
|
|
280
|
-
|
|
281
|
-
return slot ? slot.ref : null
|
|
280
|
+
function refForSlot(provider, indexOrAlias) {
|
|
281
|
+
return resolveAccountRef(normalizeSlots(live().slots), provider, indexOrAlias)
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
function stripLegacySlots(slots) {
|
package/lib/models-catalog.js
CHANGED
|
@@ -279,6 +279,17 @@ export function buildEnrichedCatalog({
|
|
|
279
279
|
|
|
280
280
|
const modelName = (typeof item === 'object' && (item.name || item.display_name)) ? (item.name || item.display_name) : modelId
|
|
281
281
|
const caps = inferModelCapabilities(provId, modelId)
|
|
282
|
+
const catalogSources = new Set()
|
|
283
|
+
for (const account of accounts) {
|
|
284
|
+
if (!account || String(account.provider || '').toLowerCase() !== String(provId).toLowerCase()) continue
|
|
285
|
+
const available = Array.isArray(account.availableModels) ? account.availableModels : []
|
|
286
|
+
const hasModel = available.some((model) => {
|
|
287
|
+
const id = typeof model === 'string' ? model : (model && (model.id || model.slug || model.name))
|
|
288
|
+
return String(id || '') === String(modelId)
|
|
289
|
+
})
|
|
290
|
+
if (hasModel) catalogSources.add(account.modelsSource || 'live')
|
|
291
|
+
}
|
|
292
|
+
if (!catalogSources.size) catalogSources.add('builtin')
|
|
282
293
|
|
|
283
294
|
result.push({
|
|
284
295
|
id: modelId,
|
|
@@ -288,6 +299,7 @@ export function buildEnrichedCatalog({
|
|
|
288
299
|
contextWindow: caps.contextWindow,
|
|
289
300
|
contextLabel: caps.contextLabel,
|
|
290
301
|
capabilities: caps.capabilities,
|
|
302
|
+
catalogSources: Array.from(catalogSources),
|
|
291
303
|
pool,
|
|
292
304
|
})
|
|
293
305
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.32",
|
|
4
4
|
"description": "Use ChatGPT Codex, Claude, Grok, Antigravity, Kimi, GLM, Cursor, Kiro, Copilot, Qwen, ERNIE, Spark, JetBrains, Perplexity, Replit, and Cody subscriptions as DeepSeek Harness LLM providers via OAuth.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|