@goodandready/dsh-subscriptions 0.4.9 → 0.4.11

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
@@ -45,6 +45,12 @@ window.__ModuleLoader__.load({
45
45
  '.dsub-chipWarn{color:var(--dsw-alias-state-warning-primary);border-color:currentColor}' +
46
46
  '.dsub-chipDanger{color:var(--dsw-alias-state-error-primary);border-color:currentColor}' +
47
47
  '.dsub-chipDot{width:6px;height:6px;border-radius:999px;background:currentColor;flex:none}' +
48
+ '.dsub-chipActive{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 10px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);font-size:12px;font-weight:600;line-height:1;white-space:nowrap;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-3)}' +
49
+ '.dsub-chipActiveWarn{color:var(--dsw-alias-state-warning-primary);border-color:currentColor}' +
50
+ '.dsub-chipActiveDanger{color:var(--dsw-alias-state-error-primary);border-color:currentColor}' +
51
+ '.dsub-chipActiveOk{color:var(--dsw-alias-state-success-primary);border-color:currentColor}' +
52
+ '.dsub-chipActiveBar{height:4px;width:48px;background:var(--dsw-alias-bg-layer-1);border-radius:2px;flex:none;overflow:hidden}' +
53
+ '.dsub-chipActiveFill{height:100%;border-radius:2px;background:currentColor}' +
48
54
 
49
55
  '.dsub-link{background:none;border:none;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:12px;padding:0}' +
50
56
  '.dsub-bar{position:relative;height:6px;border-radius:3px;background:var(--dsw-alias-bg-layer-1);overflow:hidden;flex:1;min-width:80px}' +
@@ -630,13 +636,14 @@ function PluginCard(props) {
630
636
  expiresAt: (data && data.expiresAt) || {},
631
637
  labels: (data && data.labels) || {},
632
638
  expiryNotifyDays: (data && data.expiryNotifyDays) || 7,
639
+ active: (data && data.active) || null,
633
640
  }
634
- } catch { return { logged: [], usage: 0, expiresAt: {}, labels: {}, expiryNotifyDays: 7 } }
641
+ } catch { return { logged: [], usage: 0, expiresAt: {}, labels: {}, expiryNotifyDays: 7, active: null } }
635
642
  }
636
643
 
637
644
  // #52: компактный чип в шапке сессии со статусом подключённых подписок.
638
645
  function HeaderChip(props) {
639
- const [state, setState] = React.useState({ loading: true, count: 0, usage: 0, expiry: null })
646
+ const [state, setState] = React.useState({ loading: true, count: 0, usage: 0, expiry: null, active: null })
640
647
  const t = (props && props.t) || ((k) => k)
641
648
  React.useEffect(() => {
642
649
  const pull = () => {
@@ -653,13 +660,45 @@ function PluginCard(props) {
653
660
  if (!expiry || at < expiry.at) expiry = { at, label: r.labels[p] || p }
654
661
  }
655
662
  }
656
- setState({ loading: false, count: r.logged.length, usage: r.usage, expiry })
657
- }).catch(() => setState({ loading: false, count: 0, usage: 0, expiry: null }))
663
+ setState({ loading: false, count: r.logged.length, usage: r.usage, expiry, active: r.active })
664
+ }).catch(() => setState({ loading: false, count: 0, usage: 0, expiry: null, active: null }))
658
665
  }
659
666
  pull()
660
667
  const id = setInterval(pull, 60 * 1000)
661
668
  return () => clearInterval(id)
662
669
  }, [])
670
+ // #69/#72: если есть активная подписка — показываем её (провайдер + индекс + windows с процентами).
671
+ if (state.active) {
672
+ const a = state.active
673
+ const wins = Array.isArray(a.windows) ? a.windows : []
674
+ // макс-процент среди окон для цвета полоски
675
+ let maxPct = -1
676
+ for (const w of wins) {
677
+ if (w && typeof w.usedPercent === 'number' && w.usedPercent > maxPct) maxPct = w.usedPercent
678
+ }
679
+ if (maxPct < 0 && typeof a.usagePercent === 'number') maxPct = a.usagePercent
680
+ const u = maxPct >= 0 ? maxPct : null
681
+ let acls = 'dsub-chipActive'
682
+ if (u != null) acls += u >= 100 ? ' dsub-chipActiveDanger' : (u >= 80 ? ' dsub-chipActiveDanger' : (u >= 50 ? ' dsub-chipActiveWarn' : ' dsub-chipActiveOk'))
683
+ const prov = t('provider' + a.provider.charAt(0).toUpperCase() + a.provider.slice(1)) || a.provider
684
+ const idx = a.index != null ? (' #' + a.index) : ''
685
+ const plan = a.plan ? (' ' + a.plan) : ''
686
+ const label = prov + idx + plan
687
+ // windows: "5h 12% 7d 8%"
688
+ const winLabels = wins.map((w) => (w.label || w.id) + ' ' + Math.round(w.usedPercent) + '%').join(' ')
689
+ return React.createElement('span', {
690
+ className: acls,
691
+ title: a.model ? (prov + idx + ' · ' + a.model) : (prov + idx),
692
+ },
693
+ React.createElement('span', { className: 'dsub-chipDot' }),
694
+ label,
695
+ u != null
696
+ ? React.createElement('span', { className: 'dsub-chipActiveBar' },
697
+ React.createElement('span', { className: 'dsub-chipActiveFill', style: { width: Math.min(100, Math.max(0, u)) + '%' } }))
698
+ : null,
699
+ winLabels || null,
700
+ )
701
+ }
663
702
  const on = state.count > 0
664
703
  // #66: цвет чипа по максимальному usagePercent (50/80/100).
665
704
  const u = state.usage
package/lib/index.js CHANGED
@@ -535,6 +535,40 @@ export function apply(ctx, config) {
535
535
  }
536
536
  } catch {}
537
537
  }
538
+ // #69/#72: активная подписка = последний успешный запрос (новые сверху).
539
+ let active = null
540
+ const last = history.recent(1)
541
+ if (last.length) {
542
+ const lastRow = last[0]
543
+ const accts = await store.listAccounts(lastRow.provider)
544
+ const acct = accts.find((a) => a.ref === lastRow.ref) || accts[0]
545
+ let plan = ''
546
+ let index = acct && acct.ref ? Number(String(acct.ref).split('_').pop()) || null : null
547
+ let status = 'ok'
548
+ let windows = []
549
+ try {
550
+ const info = await store.describeRef(lastRow.ref)
551
+ plan = info.paidTierName || ''
552
+ if (info.validationUrl) status = 'verify'
553
+ else if (info.cooldownUntil && info.cooldownUntil > Date.now()) status = 'cooldown'
554
+ if (Array.isArray(info.usage)) {
555
+ windows = info.usage
556
+ .filter((w) => w && typeof w.usedPercent === 'number')
557
+ .map((w) => ({ id: w.id || w.en || w.ru, label: w.en || w.ru || w.id, usedPercent: w.usedPercent }))
558
+ }
559
+ } catch {}
560
+ active = {
561
+ provider: lastRow.provider,
562
+ index,
563
+ model: lastRow.model || null,
564
+ path: lastRow.path || null,
565
+ plan,
566
+ windows,
567
+ usagePercent: usage[lastRow.provider] != null ? usage[lastRow.provider] : null,
568
+ status,
569
+ at: lastRow.ts,
570
+ }
571
+ }
538
572
  writeJson(res, 200, {
539
573
  ok: true,
540
574
  loggedIn: Object.fromEntries(PROVIDERS.map((id) => [id, logged.includes(id)])),
@@ -542,6 +576,7 @@ export function apply(ctx, config) {
542
576
  expiresAt: expires,
543
577
  labels,
544
578
  expiryNotifyDays: live().expiryNotifyDays,
579
+ active,
545
580
  })
546
581
  },
547
582
  }), 'dsh-subscriptions: /status')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-subscriptions",
3
- "version": "0.4.9",
3
+ "version": "0.4.11",
4
4
  "description": "Use ChatGPT Codex, Claude, Grok, and Antigravity subscriptions as DeepSeek Harness LLM providers via OAuth.",
5
5
  "license": "MIT",
6
6
  "type": "module",