@goodandready/dsh-subscriptions 0.7.30 → 0.7.31

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 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
- setDraft(JSON.parse(JSON.stringify((data && data.config) || {})))
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,
@@ -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.30",
3
+ "version": "0.7.31",
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",