@goodandready/dsh-subscriptions 0.7.23 → 0.7.25
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/client.js +122 -10
- package/lib/http.js +10 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -186,6 +186,16 @@ window.__ModuleLoader__.load({
|
|
|
186
186
|
"toastCooldownRecovered": "is now available (cooldown expired)",
|
|
187
187
|
"modelsCatalogTitle": "Curated Subscription Models",
|
|
188
188
|
"modelsCatalogDesc": "Available model catalog across active subscriptions in DeepSeek Harness.",
|
|
189
|
+
"modelSearchPlaceholder": "Search models by id, name or alias",
|
|
190
|
+
"modelSortLabel": "Sort models",
|
|
191
|
+
"modelSortName": "Name",
|
|
192
|
+
"modelSortProvider": "Provider",
|
|
193
|
+
"modelSortContext": "Context window",
|
|
194
|
+
"modelSortAvailability": "Availability",
|
|
195
|
+
"modelListCounts": "{shown} of {matched} shown · {hidden} hidden · {total} total",
|
|
196
|
+
"modelShowMore": "Show more",
|
|
197
|
+
"modelShowAll": "Show all",
|
|
198
|
+
"modelNoMatches": "No models match this search.",
|
|
189
199
|
"rotationDecisionsTitle": "Rotation & Routing Decisions",
|
|
190
200
|
"rotationDecisionsDesc": "Real-time decision trace explaining account selection, skips, and fallback reasons.",
|
|
191
201
|
"rotationAttempt": "attempt",
|
|
@@ -550,6 +560,16 @@ window.__ModuleLoader__.load({
|
|
|
550
560
|
"toastCooldownRecovered": "已解除冷却恢复可用",
|
|
551
561
|
"modelsCatalogTitle": "精选订阅模型",
|
|
552
562
|
"modelsCatalogDesc": "当前 DeepSeek Harness 活跃订阅下可用模型一览。",
|
|
563
|
+
"modelSearchPlaceholder": "搜索模型 ID、名称或别名",
|
|
564
|
+
"modelSortLabel": "模型排序",
|
|
565
|
+
"modelSortName": "名称",
|
|
566
|
+
"modelSortProvider": "服务商",
|
|
567
|
+
"modelSortContext": "上下文窗口",
|
|
568
|
+
"modelSortAvailability": "可用性",
|
|
569
|
+
"modelListCounts": "已显示 {shown} / {matched} · 隐藏 {hidden} · 共 {total} 个",
|
|
570
|
+
"modelShowMore": "显示更多",
|
|
571
|
+
"modelShowAll": "显示全部",
|
|
572
|
+
"modelNoMatches": "没有匹配的模型。",
|
|
553
573
|
"rotationDecisionsTitle": "轮询调度与路由决策",
|
|
554
574
|
"rotationDecisionsDesc": "实时决策追踪,透明展示每次请求的账号选择、跳过与回退原因。",
|
|
555
575
|
"rotationAttempt": "次尝试",
|
|
@@ -948,7 +968,52 @@ window.__ModuleLoader__.load({
|
|
|
948
968
|
}
|
|
949
969
|
const Chevron = ChevronIcon || FallbackChevron
|
|
950
970
|
|
|
971
|
+
// #839 model-list helpers
|
|
972
|
+
function modelListSearchText(model) {
|
|
973
|
+
const parts = [model && model.id, model && model.name, model && model.alias, model && model.provider, model && model.providerName]
|
|
974
|
+
if (model && Array.isArray(model.aliases)) parts.push.apply(parts, model.aliases)
|
|
975
|
+
return parts.filter(Boolean).join(' ').toLowerCase()
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function filterModelList(models, query) {
|
|
979
|
+
const list = Array.isArray(models) ? models : []
|
|
980
|
+
const q = String(query || '').trim().toLowerCase()
|
|
981
|
+
if (!q) return list.slice()
|
|
982
|
+
return list.filter((model) => modelListSearchText(model).includes(q))
|
|
983
|
+
}
|
|
951
984
|
|
|
985
|
+
function modelAvailabilityRank(model) {
|
|
986
|
+
const status = String(model && model.pool && model.pool.status || 'unconfigured').toLowerCase()
|
|
987
|
+
const order = { available: 0, degraded: 1, cooldown: 2, quarantined: 3, exhausted: 4, unconfigured: 5 }
|
|
988
|
+
return order[status] != null ? order[status] : 6
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function sortModelList(models, sortKey) {
|
|
992
|
+
const list = Array.isArray(models) ? models.slice() : []
|
|
993
|
+
const key = String(sortKey || 'name')
|
|
994
|
+
const byName = (a, b) => String(a && (a.name || a.id) || '').localeCompare(String(b && (b.name || b.id) || ''), undefined, { numeric: true, sensitivity: 'base' })
|
|
995
|
+
list.sort((a, b) => {
|
|
996
|
+
if (key === 'provider') {
|
|
997
|
+
const byProvider = String(a && (a.providerName || a.provider) || '').localeCompare(String(b && (b.providerName || b.provider) || ''), undefined, { sensitivity: 'base' })
|
|
998
|
+
if (byProvider) return byProvider
|
|
999
|
+
} else if (key === 'context') {
|
|
1000
|
+
const delta = Number(b && b.contextWindow || 0) - Number(a && a.contextWindow || 0)
|
|
1001
|
+
if (delta) return delta
|
|
1002
|
+
} else if (key === 'availability') {
|
|
1003
|
+
const delta = modelAvailabilityRank(a) - modelAvailabilityRank(b)
|
|
1004
|
+
if (delta) return delta
|
|
1005
|
+
}
|
|
1006
|
+
return byName(a, b)
|
|
1007
|
+
})
|
|
1008
|
+
return list
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
function modelListCounts(all, shown) {
|
|
1012
|
+
const total = Array.isArray(all) ? all.length : 0
|
|
1013
|
+
const visible = Array.isArray(shown) ? shown.length : 0
|
|
1014
|
+
return { total, shown: visible, hidden: Math.max(0, total - visible) }
|
|
1015
|
+
}
|
|
1016
|
+
// #839 end model-list helpers
|
|
952
1017
|
|
|
953
1018
|
function SubsSection(props) {
|
|
954
1019
|
// The translator comes from the slot because its registration declares locale.
|
|
@@ -961,6 +1026,9 @@ window.__ModuleLoader__.load({
|
|
|
961
1026
|
const [accounts, setAccounts] = React.useState([])
|
|
962
1027
|
const [providers, setProviders] = React.useState([])
|
|
963
1028
|
const [modelsCatalog, setModelsCatalog] = React.useState([])
|
|
1029
|
+
const [modelQuery, setModelQuery] = React.useState('')
|
|
1030
|
+
const [modelSort, setModelSort] = React.useState('name')
|
|
1031
|
+
const [modelLimit, setModelLimit] = React.useState(40)
|
|
964
1032
|
const [paste, setPaste] = React.useState({})
|
|
965
1033
|
const [device, setDevice] = React.useState({}) // #90 key -> {state,userCode,authUrl,intervalMs,status}
|
|
966
1034
|
const [loginLinks, setLoginLinks] = React.useState({}) // #595 provider:index -> fallback auth URL when a popup is blocked
|
|
@@ -1676,6 +1744,20 @@ window.__ModuleLoader__.load({
|
|
|
1676
1744
|
|
|
1677
1745
|
const connectedCount = accounts.filter((a) => a.configured).length
|
|
1678
1746
|
|
|
1747
|
+
const catalogAll = Array.isArray(modelsCatalog) && modelsCatalog.length ? modelsCatalog : [
|
|
1748
|
+
{ id: 'gpt-4o', contextLabel: '128k', capabilities: ['vision'], pool: { status: t('sourceAvailable') } },
|
|
1749
|
+
{ id: 'o1 / o3-mini', contextLabel: '200k', capabilities: ['reasoning'], pool: { status: t('sourceAvailable') } },
|
|
1750
|
+
{ id: 'claude-3-7-sonnet', contextLabel: '200k', capabilities: ['reasoning'], pool: { status: t('sourceAvailable') } },
|
|
1751
|
+
{ id: 'claude-3-5-sonnet', contextLabel: '200k', capabilities: ['coding'], pool: { status: t('sourceAvailable') } },
|
|
1752
|
+
{ id: 'grok-beta', contextLabel: '128k', capabilities: ['fast'], pool: { status: t('sourceAvailable') } },
|
|
1753
|
+
{ id: 'gemini-2.5-pro', contextLabel: '1M', capabilities: ['vision'], pool: { status: t('sourceAvailable') } },
|
|
1754
|
+
{ id: 'kimi-k1.5', contextLabel: '128k', capabilities: ['math'], pool: { status: t('sourceAvailable') } },
|
|
1755
|
+
{ id: 'glm-4-plus', contextLabel: '128k', capabilities: ['chinese'], pool: { status: t('sourceAvailable') } },
|
|
1756
|
+
]
|
|
1757
|
+
const catalogSorted = sortModelList(filterModelList(catalogAll, modelQuery), modelSort)
|
|
1758
|
+
const catalogShown = catalogSorted.slice(0, modelLimit)
|
|
1759
|
+
const catalogCounts = modelListCounts(catalogAll, catalogShown)
|
|
1760
|
+
|
|
1679
1761
|
return h('div', { className: 'dsub-wrap', 'data-tab': tab },
|
|
1680
1762
|
h('div', { className: 'dsub-header-bar' },
|
|
1681
1763
|
h('div', { className: 'dsub-row', style: { marginTop: 0 } },
|
|
@@ -2197,17 +2279,36 @@ h('div', { className: 'dsub-row' },
|
|
|
2197
2279
|
h('details', { className: 'dsub-group', 'data-pane': 'models' },
|
|
2198
2280
|
h('summary', { className: 'dsub-groupHead' }, '🎯 ' + t('modelsCatalogTitle')),
|
|
2199
2281
|
h('div', { className: 'dsub-sub', style: { marginTop: '4px' } }, t('modelsCatalogDesc')),
|
|
2282
|
+
h('div', { className: 'dsub-row', style: { marginTop: 8, gap: 6, alignItems: 'center' } },
|
|
2283
|
+
h('input', {
|
|
2284
|
+
className: 'dsub-inp dsub-grow',
|
|
2285
|
+
value: modelQuery,
|
|
2286
|
+
placeholder: t('modelSearchPlaceholder'),
|
|
2287
|
+
'aria-label': t('modelSearchPlaceholder'),
|
|
2288
|
+
onChange: (e) => { setModelQuery(e.target.value); setModelLimit(40) },
|
|
2289
|
+
}),
|
|
2290
|
+
h('select', {
|
|
2291
|
+
className: 'dsub-select',
|
|
2292
|
+
value: modelSort,
|
|
2293
|
+
title: t('modelSortLabel'),
|
|
2294
|
+
'aria-label': t('modelSortLabel'),
|
|
2295
|
+
onChange: (e) => { setModelSort(e.target.value); setModelLimit(40) },
|
|
2296
|
+
},
|
|
2297
|
+
h('option', { value: 'name' }, t('modelSortName')),
|
|
2298
|
+
h('option', { value: 'provider' }, t('modelSortProvider')),
|
|
2299
|
+
h('option', { value: 'context' }, t('modelSortContext')),
|
|
2300
|
+
h('option', { value: 'availability' }, t('modelSortAvailability')),
|
|
2301
|
+
),
|
|
2302
|
+
),
|
|
2303
|
+
h('div', { className: 'dsub-sub', style: { marginTop: 4 } },
|
|
2304
|
+
t('modelListCounts')
|
|
2305
|
+
.replace('{shown}', String(catalogCounts.shown))
|
|
2306
|
+
.replace('{matched}', String(catalogSorted.length))
|
|
2307
|
+
.replace('{hidden}', String(catalogCounts.hidden))
|
|
2308
|
+
.replace('{total}', String(catalogCounts.total))
|
|
2309
|
+
),
|
|
2200
2310
|
h('div', { className: 'dsub-model-grid' },
|
|
2201
|
-
|
|
2202
|
-
{ id: 'gpt-4o', contextLabel: '128k', capabilities: ['vision'], pool: { status: t('sourceAvailable') } },
|
|
2203
|
-
{ id: 'o1 / o3-mini', contextLabel: '200k', capabilities: ['reasoning'], pool: { status: t('sourceAvailable') } },
|
|
2204
|
-
{ id: 'claude-3-7-sonnet', contextLabel: '200k', capabilities: ['reasoning'], pool: { status: t('sourceAvailable') } },
|
|
2205
|
-
{ id: 'claude-3-5-sonnet', contextLabel: '200k', capabilities: ['coding'], pool: { status: t('sourceAvailable') } },
|
|
2206
|
-
{ id: 'grok-beta', contextLabel: '128k', capabilities: ['fast'], pool: { status: t('sourceAvailable') } },
|
|
2207
|
-
{ id: 'gemini-2.5-pro', contextLabel: '1M', capabilities: ['vision'], pool: { status: t('sourceAvailable') } },
|
|
2208
|
-
{ id: 'kimi-k1.5', contextLabel: '128k', capabilities: ['math'], pool: { status: t('sourceAvailable') } },
|
|
2209
|
-
{ id: 'glm-4-plus', contextLabel: '128k', capabilities: ['chinese'], pool: { status: t('sourceAvailable') } },
|
|
2210
|
-
]).map((m, idx) => {
|
|
2311
|
+
catalogShown.map((m, idx) => {
|
|
2211
2312
|
const poolStatus = m.pool ? m.pool.status : 'unconfigured'
|
|
2212
2313
|
const statusClass = 'dsub-model-status-' + poolStatus
|
|
2213
2314
|
let poolLabel = ''
|
|
@@ -2244,6 +2345,17 @@ h('div', { className: 'dsub-row' },
|
|
|
2244
2345
|
)
|
|
2245
2346
|
})
|
|
2246
2347
|
),
|
|
2348
|
+
catalogSorted.length === 0 ? h('div', { className: 'dsub-sub', style: { marginTop: 6 } }, t('modelNoMatches')) : null,
|
|
2349
|
+
catalogCounts.shown < catalogSorted.length ? h('div', { className: 'dsub-row', style: { marginTop: 6, gap: 6 } },
|
|
2350
|
+
h('button', {
|
|
2351
|
+
type: 'button', className: 'dsub-mini',
|
|
2352
|
+
onClick: () => setModelLimit((n) => n + 40),
|
|
2353
|
+
}, t('modelShowMore')),
|
|
2354
|
+
h('button', {
|
|
2355
|
+
type: 'button', className: 'dsub-mini',
|
|
2356
|
+
onClick: () => setModelLimit(catalogSorted.length),
|
|
2357
|
+
}, t('modelShowAll')),
|
|
2358
|
+
) : null,
|
|
2247
2359
|
),
|
|
2248
2360
|
h('details', { className: 'dsub-group', 'data-pane': 'models' },
|
|
2249
2361
|
h('summary', { className: 'dsub-groupHead' }, '🧠 ' + t('reasoningEffortTitle')),
|
package/lib/http.js
CHANGED
|
@@ -103,6 +103,8 @@ function configuredTrustedHosts(targetCtx) {
|
|
|
103
103
|
return out
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
const SHELL_SESSION_COOKIE = /(?:^|;\s*)dsh-auth-[^=;\s]*=/
|
|
107
|
+
|
|
106
108
|
/**
|
|
107
109
|
* Reject cross-site and untrusted writes and sensitive reads.
|
|
108
110
|
* Fail-closed: must be loopback, same-origin/same-site, or authentic host credentials (#374).
|
|
@@ -117,7 +119,14 @@ export function isTrustedSettingsRequest(request, targetCtx) {
|
|
|
117
119
|
// not enough because the LAN bridge rewrites Host and connects over loopback.
|
|
118
120
|
const method = String(request?.method || '').toUpperCase()
|
|
119
121
|
const mutating = method !== '' && method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS'
|
|
120
|
-
if (mutating && !headers.origin && !headers.referer)
|
|
122
|
+
if (mutating && !headers.origin && !headers.referer) {
|
|
123
|
+
// #26: DSH Desktop validates dsh-app://app, then strips Origin/Referer
|
|
124
|
+
// before forwarding. The shell's session cookie is the only positive
|
|
125
|
+
// signal left. Presence is not a signature check: DSH still owns cookie
|
|
126
|
+
// validation, and this only relaxes the mutation gate for loopback.
|
|
127
|
+
const shellForwarded = SHELL_SESSION_COOKIE.test(String(headers.cookie || '')) && isLoopback(request)
|
|
128
|
+
if (!shellForwarded) return false
|
|
129
|
+
}
|
|
121
130
|
|
|
122
131
|
// 1. Explicit cross-site fetch is always rejected
|
|
123
132
|
const secFetchSite = String(headers['sec-fetch-site'] || '').toLowerCase()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.25",
|
|
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",
|