@goodandready/dsh-subscriptions 0.6.88 → 0.6.90
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 +32 -123
- package/lib/http.js +0 -58
- package/lib/providers.js +64 -0
- package/lib/refs.js +9 -65
- package/lib/routes/oauth.js +6 -0
- package/lib/vendors/grok.js +1 -98
- package/lib/vendors/index.js +7 -3
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -726,6 +726,27 @@ window.__ModuleLoader__.load({
|
|
|
726
726
|
return label ? h('span', { className: 'dsub-planBadge' }, label) : null
|
|
727
727
|
}
|
|
728
728
|
|
|
729
|
+
function pad2(n) {
|
|
730
|
+
return String(n).padStart(2, '0')
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function formatMmSs(totalSeconds) {
|
|
734
|
+
const total = Math.max(0, Math.ceil(Number(totalSeconds) || 0))
|
|
735
|
+
return pad2(Math.floor(total / 60)) + ':' + pad2(total % 60)
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function barTone(pct, normalColor = 'var(--dsw-alias-state-success-primary)') {
|
|
739
|
+
const p = Number(pct) || 0
|
|
740
|
+
if (p >= 90) return { cls: 'dsub-barFull', tag: 'dsub-tag-bad', color: 'var(--dsw-alias-state-error-primary)' }
|
|
741
|
+
if (p >= 70) return { cls: 'dsub-barWarn', tag: 'dsub-tag-warn', color: 'var(--dsw-alias-state-warn-primary)' }
|
|
742
|
+
return { cls: '', tag: 'dsub-tag-ok', color: normalColor }
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function ledClass(pct) {
|
|
746
|
+
const p = Number(pct) || 0
|
|
747
|
+
return p >= 90 ? 'dsub-ledBad' : (p >= 70 ? 'dsub-ledWarn' : 'dsub-ledOk')
|
|
748
|
+
}
|
|
749
|
+
|
|
729
750
|
function formatRelativeReset(resetAt, lang, now) {
|
|
730
751
|
if (!resetAt || !Number.isFinite(resetAt) || resetAt <= 0) return ''
|
|
731
752
|
var delta = resetAt - (now || Date.now())
|
|
@@ -752,17 +773,14 @@ window.__ModuleLoader__.load({
|
|
|
752
773
|
if (ms <= 0) return null
|
|
753
774
|
const total = Math.floor(ms / 1000)
|
|
754
775
|
const d = new Date(props.resetAt)
|
|
755
|
-
const
|
|
756
|
-
const timeStr = pad(d.getHours()) + ':' + pad(d.getMinutes())
|
|
776
|
+
const timeStr = pad2(d.getHours()) + ':' + pad2(d.getMinutes())
|
|
757
777
|
const relStr = formatRelativeReset(props.resetAt, t('lang'), now)
|
|
758
778
|
if (total > 3600) {
|
|
759
779
|
return h('span', { className: 'dsub-sub', style: { fontVariantNumeric: 'tabular-nums' } },
|
|
760
780
|
'⏳ ' + t('windowResetsAt') + ' ' + timeStr + ' (' + relStr + ')')
|
|
761
781
|
}
|
|
762
|
-
const m = Math.floor(total / 60)
|
|
763
|
-
const sec = total % 60
|
|
764
782
|
return h('span', { className: 'dsub-sub', style: { fontVariantNumeric: 'tabular-nums' } },
|
|
765
|
-
'⏳ ' + t('windowResetsAt') + ' ' + timeStr + ' (' + relStr + ' · ' +
|
|
783
|
+
'⏳ ' + t('windowResetsAt') + ' ' + timeStr + ' (' + relStr + ' · ' + formatMmSs(total) + ')')
|
|
766
784
|
}
|
|
767
785
|
|
|
768
786
|
function badgeFor(account, t) {
|
|
@@ -776,10 +794,7 @@ window.__ModuleLoader__.load({
|
|
|
776
794
|
}
|
|
777
795
|
if (account.cooldownUntil && account.cooldownUntil > Date.now()) {
|
|
778
796
|
const leftSec = Math.max(0, Math.ceil((account.cooldownUntil - Date.now()) / 1000))
|
|
779
|
-
|
|
780
|
-
const s = leftSec % 60
|
|
781
|
-
const pad = (n) => String(n).padStart(2, "0")
|
|
782
|
-
return t("coolingDown") + " (" + pad(m) + ":" + pad(s) + ")"
|
|
797
|
+
return t("coolingDown") + " (" + formatMmSs(leftSec) + ")"
|
|
783
798
|
}
|
|
784
799
|
if (account.usagePercent != null && account.usagePercent >= 100) return t('usageFull')
|
|
785
800
|
return t('connected')
|
|
@@ -2767,8 +2782,9 @@ window.__ModuleLoader__.load({
|
|
|
2767
2782
|
|
|
2768
2783
|
function renderItem(label, pct, detail){
|
|
2769
2784
|
var p = Math.min(100, Math.max(0, Math.round(pct)))
|
|
2770
|
-
var
|
|
2771
|
-
var
|
|
2785
|
+
var tone = barTone(p)
|
|
2786
|
+
var tagCls = tone.tag
|
|
2787
|
+
var fillBg = tone.color
|
|
2772
2788
|
return h('div', { key: label, className: 'dsub-usage-card' },
|
|
2773
2789
|
h('div', { className: 'dsub-usage-head' },
|
|
2774
2790
|
h('span', null, label),
|
|
@@ -3173,108 +3189,6 @@ window.__ModuleLoader__.load({
|
|
|
3173
3189
|
return { label: (p.charAt(0).toUpperCase() || 'P'), cls: 'dsub-brandCodex', icon: '●' }
|
|
3174
3190
|
}
|
|
3175
3191
|
|
|
3176
|
-
const PLUGIN_LABELS = new Set(['plugins', '插件', 'plugin'])
|
|
3177
|
-
|
|
3178
|
-
function resolvePluginLabels(customCtx) {
|
|
3179
|
-
const labels = new Set(PLUGIN_LABELS)
|
|
3180
|
-
const loc = (customCtx && customCtx.locale) || (typeof window !== 'undefined' && window.__dshCtx && window.__dshCtx.locale)
|
|
3181
|
-
if (loc && typeof loc.bind === 'function') {
|
|
3182
|
-
try {
|
|
3183
|
-
const sp = loc.bind('settings.plugins')
|
|
3184
|
-
const nav = typeof sp === 'function' ? sp('nav') : null
|
|
3185
|
-
if (nav && typeof nav === 'string') labels.add(nav.trim().toLowerCase())
|
|
3186
|
-
} catch { /* bestEffort */ }
|
|
3187
|
-
try {
|
|
3188
|
-
const pm = loc.bind('pluginManager')
|
|
3189
|
-
const panel = typeof pm === 'function' ? (pm('panel') || pm('title')) : null
|
|
3190
|
-
if (panel && typeof panel === 'string') labels.add(panel.trim().toLowerCase())
|
|
3191
|
-
} catch { /* bestEffort */ }
|
|
3192
|
-
}
|
|
3193
|
-
return labels
|
|
3194
|
-
}
|
|
3195
|
-
|
|
3196
|
-
function matchesPluginLabel(el, customLabels) {
|
|
3197
|
-
if (!el) return false
|
|
3198
|
-
const dataTab = (el.getAttribute('data-tab') || '').trim().toLowerCase()
|
|
3199
|
-
const dataSection = (el.getAttribute('data-section') || '').trim().toLowerCase()
|
|
3200
|
-
const dataKey = (el.getAttribute('data-key') || '').trim().toLowerCase()
|
|
3201
|
-
const id = (el.getAttribute('id') || '').trim().toLowerCase()
|
|
3202
|
-
if (dataTab === 'plugins' || dataSection === 'plugins' || dataKey === 'plugins' || id === 'plugins') return true
|
|
3203
|
-
|
|
3204
|
-
if (el.querySelector && el.querySelector('path[d*="M10.3232"]')) return true
|
|
3205
|
-
|
|
3206
|
-
const txt = (el.textContent || '').trim().toLowerCase()
|
|
3207
|
-
const aria = (el.getAttribute('aria-label') || '').trim().toLowerCase()
|
|
3208
|
-
const title = (el.getAttribute('title') || '').trim().toLowerCase()
|
|
3209
|
-
const labels = customLabels || PLUGIN_LABELS
|
|
3210
|
-
return labels.has(txt) || labels.has(aria) || labels.has(title) || labels.has(dataTab)
|
|
3211
|
-
}
|
|
3212
|
-
|
|
3213
|
-
function findAndClickCard(doc) {
|
|
3214
|
-
for (const key of [PKG, 'dsh-subscriptions', '@goodandready/dsh-subscriptions']) {
|
|
3215
|
-
const card = doc.querySelector(`[data-plugin-package="${key}"]`) || doc.querySelector(`[data-plugin-item="${ROW_ID}"]`)
|
|
3216
|
-
if (card) {
|
|
3217
|
-
const open = [...card.querySelectorAll('button')].filter((b) => b.getAttribute('role') !== 'switch')
|
|
3218
|
-
const target = open.length >= 1 ? open[0] : card
|
|
3219
|
-
if (typeof target.click === 'function') {
|
|
3220
|
-
target.click()
|
|
3221
|
-
return true
|
|
3222
|
-
}
|
|
3223
|
-
}
|
|
3224
|
-
}
|
|
3225
|
-
return false
|
|
3226
|
-
}
|
|
3227
|
-
|
|
3228
|
-
let pluginSettingsGeneration = 0
|
|
3229
|
-
function openPluginSettings(doc = document, schedule = (run, ms) => window.setTimeout(run, ms), navCtx) {
|
|
3230
|
-
try {
|
|
3231
|
-
if (findAndClickCard(doc)) return
|
|
3232
|
-
const generation = ++pluginSettingsGeneration
|
|
3233
|
-
const runIfCurrent = (fn, ms) => schedule(() => {
|
|
3234
|
-
if (generation !== pluginSettingsGeneration) return
|
|
3235
|
-
fn()
|
|
3236
|
-
}, ms)
|
|
3237
|
-
|
|
3238
|
-
const activeLabels = resolvePluginLabels(navCtx)
|
|
3239
|
-
const checkEl = (el) => matchesPluginLabel(el, activeLabels)
|
|
3240
|
-
|
|
3241
|
-
const buttons = [...doc.querySelectorAll('button, a, div[role="button"], div[role="tab"]')]
|
|
3242
|
-
const panel = buttons.find(checkEl)
|
|
3243
|
-
if (panel) {
|
|
3244
|
-
panel.click()
|
|
3245
|
-
const poll = (left) => {
|
|
3246
|
-
if (findAndClickCard(doc)) return
|
|
3247
|
-
if (left > 0) runIfCurrent(() => poll(left - 1), 100)
|
|
3248
|
-
}
|
|
3249
|
-
runIfCurrent(() => poll(40), 100)
|
|
3250
|
-
return
|
|
3251
|
-
}
|
|
3252
|
-
|
|
3253
|
-
const triggers = buttons.filter((b) => b.getAttribute('aria-haspopup') === 'dialog' && b.hasAttribute('aria-expanded'))
|
|
3254
|
-
if (triggers.length > 0) {
|
|
3255
|
-
const trigger = triggers.find((b) => b.getAttribute('aria-expanded') !== 'true') || triggers[0]
|
|
3256
|
-
if (trigger && typeof trigger.click === 'function') {
|
|
3257
|
-
trigger.click()
|
|
3258
|
-
const pollSection = (left) => {
|
|
3259
|
-
try {
|
|
3260
|
-
const section = [...doc.querySelectorAll('button, a, div[role="tab"], [role="button"]')].find(checkEl)
|
|
3261
|
-
if (section && typeof section.click === 'function') {
|
|
3262
|
-
section.click()
|
|
3263
|
-
const poll = (cLeft) => {
|
|
3264
|
-
if (findAndClickCard(doc)) return
|
|
3265
|
-
if (cLeft > 0) runIfCurrent(() => poll(cLeft - 1), 100)
|
|
3266
|
-
}
|
|
3267
|
-
runIfCurrent(() => poll(40), 100)
|
|
3268
|
-
return
|
|
3269
|
-
}
|
|
3270
|
-
} catch { /* bestEffort */ }
|
|
3271
|
-
if (left > 0) runIfCurrent(() => pollSection(left - 1), 80)
|
|
3272
|
-
}
|
|
3273
|
-
runIfCurrent(() => pollSection(20), 80)
|
|
3274
|
-
}
|
|
3275
|
-
}
|
|
3276
|
-
} catch { /* bestEffort */ }
|
|
3277
|
-
}
|
|
3278
3192
|
function SubsPill(props) {
|
|
3279
3193
|
const t = (props && props.t) || ((k) => k)
|
|
3280
3194
|
const [state, setState] = React.useState({ logged: [], usage: 0, accounts: [], open: false, active: null, loading: false })
|
|
@@ -3349,7 +3263,7 @@ window.__ModuleLoader__.load({
|
|
|
3349
3263
|
|
|
3350
3264
|
const n = state.logged.length
|
|
3351
3265
|
const total = state.accounts.length || n
|
|
3352
|
-
const led = n === 0 ? 'dsub-ledOff' : (state.usage
|
|
3266
|
+
const led = n === 0 ? 'dsub-ledOff' : ledClass(state.usage)
|
|
3353
3267
|
const now = Date.now()
|
|
3354
3268
|
|
|
3355
3269
|
// Header Pill Label
|
|
@@ -3399,7 +3313,7 @@ window.__ModuleLoader__.load({
|
|
|
3399
3313
|
className: 'dsub-barFillGrad',
|
|
3400
3314
|
style: {
|
|
3401
3315
|
width: Math.min(100, Math.max(0, primPct)) + '%',
|
|
3402
|
-
background:
|
|
3316
|
+
background: barTone(primPct).color,
|
|
3403
3317
|
},
|
|
3404
3318
|
}),
|
|
3405
3319
|
),
|
|
@@ -3414,7 +3328,7 @@ window.__ModuleLoader__.load({
|
|
|
3414
3328
|
className: 'dsub-barFillGrad',
|
|
3415
3329
|
style: {
|
|
3416
3330
|
width: Math.min(100, Math.max(0, secPct)) + '%',
|
|
3417
|
-
background:
|
|
3331
|
+
background: barTone(secPct, 'var(--dsw-alias-brand-primary)').color,
|
|
3418
3332
|
},
|
|
3419
3333
|
}),
|
|
3420
3334
|
),
|
|
@@ -3458,11 +3372,8 @@ window.__ModuleLoader__.load({
|
|
|
3458
3372
|
} else if (cooled) {
|
|
3459
3373
|
statusClass = 'dsub-statusWarn'
|
|
3460
3374
|
const leftSec = Math.max(0, Math.ceil((a.cooldownUntil - now) / 1000))
|
|
3461
|
-
const m = Math.floor(leftSec / 60)
|
|
3462
|
-
const s = leftSec % 60
|
|
3463
|
-
const pad = (n) => String(n).padStart(2, '0')
|
|
3464
3375
|
const fams = Array.isArray(a.cooldownFamilies) && a.cooldownFamilies.length ? ' · ' + a.cooldownFamilies.join('/') : ''
|
|
3465
|
-
statusText = t('subsCooldown') + ' (' +
|
|
3376
|
+
statusText = t('subsCooldown') + ' (' + formatMmSs(leftSec) + ')' + fams
|
|
3466
3377
|
} else if (isLogged) {
|
|
3467
3378
|
if (pct >= 90) { statusClass = 'dsub-statusBad'; statusText = '90%+ ' + t('subsUsed') }
|
|
3468
3379
|
else if (pct >= 50) { statusClass = 'dsub-statusWarn'; statusText = pct + '% ' + t('subsUsed') }
|
|
@@ -3485,7 +3396,7 @@ window.__ModuleLoader__.load({
|
|
|
3485
3396
|
className: 'dsub-barFillGrad',
|
|
3486
3397
|
style: {
|
|
3487
3398
|
width: Math.min(100, Math.max(0, pct)) + '%',
|
|
3488
|
-
background:
|
|
3399
|
+
background: barTone(pct).color,
|
|
3489
3400
|
},
|
|
3490
3401
|
}),
|
|
3491
3402
|
) : null,
|
|
@@ -3570,8 +3481,6 @@ window.__ModuleLoader__.load({
|
|
|
3570
3481
|
layout.selectPanel('plugins')
|
|
3571
3482
|
}
|
|
3572
3483
|
} catch { /* bestEffort */ }
|
|
3573
|
-
|
|
3574
|
-
openPluginSettings(document, undefined, clientCtx)
|
|
3575
3484
|
},
|
|
3576
3485
|
}, t('subsOpenSettings')),
|
|
3577
3486
|
),
|
package/lib/http.js
CHANGED
|
@@ -366,64 +366,6 @@ export async function fetchWithTimeout(fetchImpl, url, init = {}, { timeoutMs =
|
|
|
366
366
|
return res
|
|
367
367
|
}
|
|
368
368
|
if (typeof timer?.unref === 'function') timer.unref()
|
|
369
|
-
const origJson = typeof res.json === 'function' ? res.json.bind(res) : null
|
|
370
|
-
const origText = typeof res.text === 'function' ? res.text.bind(res) : null
|
|
371
|
-
if (origJson) {
|
|
372
|
-
res.json = () => {
|
|
373
|
-
if (typeof timer?.ref === 'function') timer.ref()
|
|
374
|
-
if (controller.signal.aborted) {
|
|
375
|
-
return Promise.reject(new DOMException('The operation was aborted due to timeout', 'TimeoutError'))
|
|
376
|
-
}
|
|
377
|
-
return new Promise((resolve, reject) => {
|
|
378
|
-
const onAbort = () => reject(new DOMException('The operation was aborted due to timeout', 'TimeoutError'))
|
|
379
|
-
controller.signal.addEventListener('abort', onAbort, { once: true })
|
|
380
|
-
origJson().then(
|
|
381
|
-
(val) => {
|
|
382
|
-
controller.signal.removeEventListener('abort', onAbort)
|
|
383
|
-
clearTimeout(timer)
|
|
384
|
-
resolve(val)
|
|
385
|
-
},
|
|
386
|
-
(err) => {
|
|
387
|
-
controller.signal.removeEventListener('abort', onAbort)
|
|
388
|
-
clearTimeout(timer)
|
|
389
|
-
if (controller.signal.aborted) {
|
|
390
|
-
reject(new DOMException('The operation was aborted due to timeout', 'TimeoutError'))
|
|
391
|
-
} else {
|
|
392
|
-
reject(err)
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
)
|
|
396
|
-
})
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (origText) {
|
|
400
|
-
res.text = () => {
|
|
401
|
-
if (typeof timer?.ref === 'function') timer.ref()
|
|
402
|
-
if (controller.signal.aborted) {
|
|
403
|
-
return Promise.reject(new DOMException('The operation was aborted due to timeout', 'TimeoutError'))
|
|
404
|
-
}
|
|
405
|
-
return new Promise((resolve, reject) => {
|
|
406
|
-
const onAbort = () => reject(new DOMException('The operation was aborted due to timeout', 'TimeoutError'))
|
|
407
|
-
controller.signal.addEventListener('abort', onAbort, { once: true })
|
|
408
|
-
origText().then(
|
|
409
|
-
(val) => {
|
|
410
|
-
controller.signal.removeEventListener('abort', onAbort)
|
|
411
|
-
clearTimeout(timer)
|
|
412
|
-
resolve(val)
|
|
413
|
-
},
|
|
414
|
-
(err) => {
|
|
415
|
-
controller.signal.removeEventListener('abort', onAbort)
|
|
416
|
-
clearTimeout(timer)
|
|
417
|
-
if (controller.signal.aborted) {
|
|
418
|
-
reject(new DOMException('The operation was aborted due to timeout', 'TimeoutError'))
|
|
419
|
-
} else {
|
|
420
|
-
reject(err)
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
)
|
|
424
|
-
})
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
369
|
return res
|
|
428
370
|
} catch (err) {
|
|
429
371
|
clearTimeout(timer)
|
package/lib/providers.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export const BUILTIN_PROVIDERS = Object.freeze([
|
|
2
|
+
'codex',
|
|
3
|
+
'claude',
|
|
4
|
+
'grok',
|
|
5
|
+
'antigravity',
|
|
6
|
+
'kimi',
|
|
7
|
+
'glm',
|
|
8
|
+
'cursor',
|
|
9
|
+
'kiro',
|
|
10
|
+
'copilot',
|
|
11
|
+
'qwen',
|
|
12
|
+
'ernie',
|
|
13
|
+
'spark',
|
|
14
|
+
'jetbrains',
|
|
15
|
+
'perplexity',
|
|
16
|
+
'replit',
|
|
17
|
+
'cody',
|
|
18
|
+
])
|
|
19
|
+
|
|
20
|
+
const DISPLAY = {
|
|
21
|
+
codex: 'ChatGPT Codex',
|
|
22
|
+
claude: 'Claude',
|
|
23
|
+
grok: 'Grok',
|
|
24
|
+
antigravity: 'Antigravity',
|
|
25
|
+
kimi: 'Moonshot Kimi',
|
|
26
|
+
glm: 'Zhipu GLM',
|
|
27
|
+
cursor: 'Cursor',
|
|
28
|
+
kiro: 'AWS Kiro',
|
|
29
|
+
copilot: 'GitHub Copilot',
|
|
30
|
+
perplexity: 'Perplexity Pro',
|
|
31
|
+
jetbrains: 'JetBrains AI',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const dynamicIds = new Set()
|
|
35
|
+
const displayExtra = {}
|
|
36
|
+
|
|
37
|
+
export let PROVIDERS = [...BUILTIN_PROVIDERS]
|
|
38
|
+
|
|
39
|
+
export function registerCustomProviderIds(ids) {
|
|
40
|
+
let changed = false
|
|
41
|
+
for (const id of ids || []) {
|
|
42
|
+
if (typeof id === 'string' && /^[a-z][a-z0-9_]*$/.test(id) && !dynamicIds.has(id)) {
|
|
43
|
+
dynamicIds.add(id)
|
|
44
|
+
changed = true
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (changed) PROVIDERS = listProviders()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function listProviders() {
|
|
51
|
+
return [...BUILTIN_PROVIDERS, ...dynamicIds]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function registerDisplayName(id, name) {
|
|
55
|
+
if (id && name) displayExtra[id] = name
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function isProvider(value) {
|
|
59
|
+
return BUILTIN_PROVIDERS.includes(value) || dynamicIds.has(value)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function displayName(provider) {
|
|
63
|
+
return DISPLAY[provider] || displayExtra[provider] || provider
|
|
64
|
+
}
|
package/lib/refs.js
CHANGED
|
@@ -1,68 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
'kiro',
|
|
11
|
-
'copilot',
|
|
12
|
-
'qwen',
|
|
13
|
-
'ernie',
|
|
14
|
-
'spark',
|
|
15
|
-
'jetbrains',
|
|
16
|
-
'perplexity',
|
|
17
|
-
'replit',
|
|
18
|
-
'cody',
|
|
19
|
-
])
|
|
20
|
-
|
|
21
|
-
const dynamicIds = new Set()
|
|
22
|
-
const displayExtra = {}
|
|
23
|
-
|
|
24
|
-
export let PROVIDERS = [...BUILTIN_PROVIDERS]
|
|
25
|
-
|
|
26
|
-
export function registerCustomProviderIds(ids) {
|
|
27
|
-
let changed = false
|
|
28
|
-
for (const id of ids || []) {
|
|
29
|
-
if (typeof id === 'string' && /^[a-z][a-z0-9_]*$/.test(id) && !dynamicIds.has(id)) {
|
|
30
|
-
dynamicIds.add(id)
|
|
31
|
-
changed = true
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
if (changed) PROVIDERS = listProviders()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function listProviders() {
|
|
38
|
-
return [...BUILTIN_PROVIDERS, ...dynamicIds]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function registerDisplayName(id, name) {
|
|
42
|
-
if (id && name) displayExtra[id] = name
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function isProvider(value) {
|
|
46
|
-
return BUILTIN_PROVIDERS.includes(value) || dynamicIds.has(value)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const DISPLAY = {
|
|
50
|
-
codex: 'ChatGPT Codex',
|
|
51
|
-
claude: 'Claude',
|
|
52
|
-
grok: 'Grok',
|
|
53
|
-
antigravity: 'Antigravity',
|
|
54
|
-
kimi: 'Moonshot Kimi',
|
|
55
|
-
glm: 'Zhipu GLM',
|
|
56
|
-
cursor: 'Cursor',
|
|
57
|
-
kiro: 'AWS Kiro',
|
|
58
|
-
copilot: 'GitHub Copilot',
|
|
59
|
-
perplexity: 'Perplexity Pro',
|
|
60
|
-
jetbrains: 'JetBrains AI',
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function displayName(provider) {
|
|
64
|
-
return DISPLAY[provider] || displayExtra[provider] || provider
|
|
65
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
BUILTIN_PROVIDERS,
|
|
3
|
+
PROVIDERS,
|
|
4
|
+
registerCustomProviderIds,
|
|
5
|
+
listProviders,
|
|
6
|
+
registerDisplayName,
|
|
7
|
+
displayName,
|
|
8
|
+
isProvider,
|
|
9
|
+
} from './providers.js'
|
|
66
10
|
|
|
67
11
|
/**
|
|
68
12
|
* @param {string} provider
|
package/lib/routes/oauth.js
CHANGED
|
@@ -224,6 +224,12 @@ export function registerOauthRoutes(ctx, state) {
|
|
|
224
224
|
writeJson(res, 405, { ok: false, error: { code: 'method', message: 'GET only' } })
|
|
225
225
|
return
|
|
226
226
|
}
|
|
227
|
+
const secFetchMode = String((req.headers && req.headers['sec-fetch-mode']) || '').toLowerCase()
|
|
228
|
+
const isNavigation = secFetchMode === 'navigate'
|
|
229
|
+
if (!isNavigation && !isTrustedSettingsRequest(req, ctx)) {
|
|
230
|
+
writeHtml(res, 403, '<!doctype html><meta charset="utf-8"><title>Subscriptions</title><p>Forbidden: untrusted OAuth callback request.</p>')
|
|
231
|
+
return
|
|
232
|
+
}
|
|
227
233
|
const q = queryOf(req)
|
|
228
234
|
const code = q.get('code') || ''
|
|
229
235
|
const state = q.get('state') || ''
|
package/lib/vendors/grok.js
CHANGED
|
@@ -262,26 +262,7 @@ export async function usage(blob, cfg, fetchImpl) {
|
|
|
262
262
|
headers: { ...identityHeaders(blob, cfg), Accept: 'application/json' },
|
|
263
263
|
})
|
|
264
264
|
const json = await readJson(res)
|
|
265
|
-
|
|
266
|
-
if (pct == null) {
|
|
267
|
-
// Fallback to gRPC-web GetGrokCreditsConfig
|
|
268
|
-
try {
|
|
269
|
-
const gRes = await impl('https://grok.com/rest/app-chat/get-grok-credits-config', {
|
|
270
|
-
method: 'POST',
|
|
271
|
-
headers: {
|
|
272
|
-
...identityHeaders(blob, cfg),
|
|
273
|
-
'Content-Type': 'application/grpc-web+proto',
|
|
274
|
-
'X-User-Agent': 'grpc-web-javascript/0.1',
|
|
275
|
-
},
|
|
276
|
-
body: Buffer.from([0, 0, 0, 0, 0]),
|
|
277
|
-
})
|
|
278
|
-
if (gRes.ok) {
|
|
279
|
-
const buf = await gRes.arrayBuffer()
|
|
280
|
-
const decoded = decodeGrokCreditsFrame(Buffer.from(buf))
|
|
281
|
-
if (decoded != null) pct = decoded
|
|
282
|
-
}
|
|
283
|
-
} catch { /* ignore grok usage capture error */ }
|
|
284
|
-
}
|
|
265
|
+
const pct = grokBillingPercent(json)
|
|
285
266
|
const snap = asUsageSnapshot(pct)
|
|
286
267
|
if (snap) snap.windows = usageWindows(json)
|
|
287
268
|
return snap
|
|
@@ -394,81 +375,3 @@ export async function* streamOnce({ blob, options, fetchImpl, headers, config, s
|
|
|
394
375
|
if (!res.ok) throw httpError(res.status, await readErrorBody(res))
|
|
395
376
|
yield* codexResponsesStream(res.body)
|
|
396
377
|
}
|
|
397
|
-
|
|
398
|
-
function readVarint(bytes, offset) {
|
|
399
|
-
let value = 0, shift = 0, index = offset
|
|
400
|
-
while (index < bytes.length) {
|
|
401
|
-
const byte = bytes[index]
|
|
402
|
-
index += 1
|
|
403
|
-
value += (byte & 0x7f) * (2 ** shift)
|
|
404
|
-
if ((byte & 0x80) === 0) return { value, next: index }
|
|
405
|
-
shift += 7
|
|
406
|
-
if (shift > 63) return undefined
|
|
407
|
-
}
|
|
408
|
-
return undefined
|
|
409
|
-
}
|
|
410
|
-
function readLength(bytes, offset, size) {
|
|
411
|
-
if (offset + size > bytes.length) return undefined
|
|
412
|
-
return { bytes: bytes.subarray(offset, offset + size), next: offset + size }
|
|
413
|
-
}
|
|
414
|
-
function decodeFields(bytes) {
|
|
415
|
-
const fields = new Map()
|
|
416
|
-
let offset = 0
|
|
417
|
-
while (offset < bytes.length) {
|
|
418
|
-
const tag = readVarint(bytes, offset)
|
|
419
|
-
if (!tag) break
|
|
420
|
-
const fieldNumber = Math.floor(tag.value / 8)
|
|
421
|
-
const wireType = tag.value % 8
|
|
422
|
-
offset = tag.next
|
|
423
|
-
if (fieldNumber <= 0) return undefined
|
|
424
|
-
let value
|
|
425
|
-
if (wireType === 0) {
|
|
426
|
-
const next = readVarint(bytes, offset)
|
|
427
|
-
if (!next) return undefined
|
|
428
|
-
value = { wireType, value: next.value }
|
|
429
|
-
offset = next.next
|
|
430
|
-
} else if (wireType === 5) {
|
|
431
|
-
const next = readLength(bytes, offset, 4)
|
|
432
|
-
if (!next) return undefined
|
|
433
|
-
value = { wireType, bytes: next.bytes }
|
|
434
|
-
offset = next.next
|
|
435
|
-
} else if (wireType === 1) {
|
|
436
|
-
const next = readLength(bytes, offset, 8)
|
|
437
|
-
if (!next) return undefined
|
|
438
|
-
value = { wireType, bytes: next.bytes }
|
|
439
|
-
offset = next.next
|
|
440
|
-
} else if (wireType === 2) {
|
|
441
|
-
const length = readVarint(bytes, offset)
|
|
442
|
-
if (!length) return undefined
|
|
443
|
-
const next = readLength(bytes, length.next, length.value)
|
|
444
|
-
if (!next) return undefined
|
|
445
|
-
value = { wireType, bytes: next.bytes }
|
|
446
|
-
offset = next.next
|
|
447
|
-
} else return undefined
|
|
448
|
-
if (!fields.has(fieldNumber)) fields.set(fieldNumber, value)
|
|
449
|
-
}
|
|
450
|
-
return fields
|
|
451
|
-
}
|
|
452
|
-
function float32Le(field) {
|
|
453
|
-
if (!field || field.wireType !== 5 || !field.bytes || field.bytes.length < 4) return undefined
|
|
454
|
-
const val = Buffer.from(field.bytes).readFloatLE(0)
|
|
455
|
-
return Number.isFinite(val) ? val : undefined
|
|
456
|
-
}
|
|
457
|
-
export function decodeGrokCreditsFrame(buffer) {
|
|
458
|
-
try {
|
|
459
|
-
const bytes = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer)
|
|
460
|
-
if (!bytes.length) return undefined
|
|
461
|
-
const payload = bytes.length > 5 && bytes[0] === 0 ? bytes.subarray(5) : bytes
|
|
462
|
-
const top = decodeFields(payload)
|
|
463
|
-
if (!top) return undefined
|
|
464
|
-
const nested = top.get(1)?.wireType === 2 ? decodeFields(top.get(1).bytes) : undefined
|
|
465
|
-
const credits = nested ?? top
|
|
466
|
-
if (!credits) return undefined
|
|
467
|
-
const ratio = float32Le(credits.get(1))
|
|
468
|
-
if (ratio == null) return undefined
|
|
469
|
-
const pct = ratio <= 1 ? Math.round(ratio * 100) : Math.round(ratio)
|
|
470
|
-
return Math.max(0, Math.min(100, pct))
|
|
471
|
-
} catch {
|
|
472
|
-
return undefined
|
|
473
|
-
}
|
|
474
|
-
}
|
package/lib/vendors/index.js
CHANGED
|
@@ -14,8 +14,9 @@ import * as grok from './grok.js'
|
|
|
14
14
|
import * as antigravity from './antigravity.js'
|
|
15
15
|
import * as kimi from './kimi.js'
|
|
16
16
|
import * as glm from './glm.js'
|
|
17
|
+
import { BUILTIN_PROVIDERS, isProvider as isRegisteredProvider, registerCustomProviderIds } from '../providers.js'
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
+
const BUILTIN_MODULES = {
|
|
19
20
|
codex,
|
|
20
21
|
claude,
|
|
21
22
|
grok,
|
|
@@ -31,14 +32,17 @@ const builtins = {
|
|
|
31
32
|
jetbrains,
|
|
32
33
|
perplexity,
|
|
33
34
|
replit,
|
|
34
|
-
cody
|
|
35
|
+
cody,
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
const builtins = Object.fromEntries(BUILTIN_PROVIDERS.map((id) => [id, BUILTIN_MODULES[id]]))
|
|
39
|
+
|
|
37
40
|
const customVendors = new Map()
|
|
38
41
|
|
|
39
42
|
export function registerCustomVendor(vendor) {
|
|
40
43
|
if (!vendor || !vendor.id) throw new Error('custom vendor needs id')
|
|
41
44
|
customVendors.set(vendor.id, vendor)
|
|
45
|
+
registerCustomProviderIds([vendor.id])
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
export function clearCustomVendors() {
|
|
@@ -62,7 +66,7 @@ const vendors = new Proxy({}, {
|
|
|
62
66
|
})
|
|
63
67
|
|
|
64
68
|
export function isProvider(value) {
|
|
65
|
-
return Boolean(vendors[value])
|
|
69
|
+
return isRegisteredProvider(value) && Boolean(vendors[value])
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
export function getVendor(provider) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.90",
|
|
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",
|