@goodandready/dsh-subscriptions 0.6.12 → 0.6.13
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/index.js +169 -15
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { createSubscriptionsService } from './subscriptions.js'
|
|
|
20
20
|
import { encryptWithPassphrase, decryptWithPassphrase } from './crypto.js'
|
|
21
21
|
import { quotaSnapshot } from './ratelimit.js'
|
|
22
22
|
import { createAccountStore, normalizeSlots, vendorConfig } from './accounts.js'
|
|
23
|
+
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
|
23
24
|
import { startLoopback } from './loopback.js'
|
|
24
25
|
import { OllamaAdapter, ollamaAlive, ollamaModels, ollamaBase } from './ollama.js'
|
|
25
26
|
import { createResetCreditService } from './reset-credits.js'
|
|
@@ -57,6 +58,11 @@ export function apply(ctx, config) {
|
|
|
57
58
|
const scope = sctx.settings.register(NS, Config, { base: config })
|
|
58
59
|
settingsApi = scope
|
|
59
60
|
getConfig = () => scope.get() ?? config
|
|
61
|
+
Promise.resolve().then(() => {
|
|
62
|
+
reconcileSlots().catch((err) => {
|
|
63
|
+
if (logger && logger.warn) logger.warn('[dsh-subscriptions] reconcileSlots initial error: ' + (err && err.message || err))
|
|
64
|
+
})
|
|
65
|
+
})
|
|
60
66
|
sctx.effect(() => () => {
|
|
61
67
|
settingsApi = undefined
|
|
62
68
|
getConfig = () => config
|
|
@@ -243,23 +249,126 @@ export function apply(ctx, config) {
|
|
|
243
249
|
})
|
|
244
250
|
|
|
245
251
|
let handle
|
|
252
|
+
let registeredByUs = new Set()
|
|
253
|
+
function disposeAdapterHandle(h) {
|
|
254
|
+
if (!h) return
|
|
255
|
+
try {
|
|
256
|
+
if (typeof h === 'function') h()
|
|
257
|
+
else if (typeof h.dispose === 'function') h.dispose()
|
|
258
|
+
} catch (err) {
|
|
259
|
+
if (logger && logger.debug) logger.debug('[dsh-subscriptions] dispose handle error: ' + (err && err.message || err))
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
246
263
|
async function syncAdapter() {
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
264
|
+
const desired = await store.loggedInProviders()
|
|
265
|
+
const globalProviders = ctx.llm && typeof ctx.llm.listProviders === 'function'
|
|
266
|
+
? ctx.llm.listProviders().map((p) => p.id)
|
|
267
|
+
: []
|
|
268
|
+
const globalSet = new Set(globalProviders)
|
|
269
|
+
|
|
270
|
+
const claimable = desired.filter((p) => !globalSet.has(p) || registeredByUs.has(p))
|
|
271
|
+
const skipped = desired.filter((p) => globalSet.has(p) && !registeredByUs.has(p))
|
|
272
|
+
|
|
273
|
+
if (skipped.length && logger && logger.info) {
|
|
274
|
+
logger.info(`[dsh-subscriptions] syncAdapter skipping already-bound providers: ${skipped.join(', ')}`)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!handle && claimable.length) {
|
|
278
|
+
handle = ctx.llm.registerAdapter(claimable, adapter)
|
|
279
|
+
registeredByUs = new Set(claimable)
|
|
250
280
|
return
|
|
251
281
|
}
|
|
252
|
-
if (!handle)
|
|
253
|
-
|
|
254
|
-
|
|
282
|
+
if (!handle) {
|
|
283
|
+
registeredByUs.clear()
|
|
284
|
+
return
|
|
285
|
+
}
|
|
286
|
+
if (!claimable.length) {
|
|
287
|
+
disposeAdapterHandle(handle)
|
|
255
288
|
handle = undefined
|
|
289
|
+
registeredByUs.clear()
|
|
256
290
|
return
|
|
257
291
|
}
|
|
292
|
+
if (typeof handle.replace === 'function') {
|
|
293
|
+
try {
|
|
294
|
+
handle.replace(claimable)
|
|
295
|
+
registeredByUs = new Set(claimable)
|
|
296
|
+
return
|
|
297
|
+
} catch (err) {
|
|
298
|
+
if (logger && logger.warn) logger.warn('[dsh-subscriptions] handle.replace failed: ' + (err && err.message || err))
|
|
299
|
+
disposeAdapterHandle(handle)
|
|
300
|
+
handle = undefined
|
|
301
|
+
registeredByUs.clear()
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
disposeAdapterHandle(handle)
|
|
305
|
+
handle = undefined
|
|
306
|
+
registeredByUs.clear()
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const currentGlobal = new Set((ctx.llm && typeof ctx.llm.listProviders === 'function' ? ctx.llm.listProviders() : []).map((p) => p.id))
|
|
310
|
+
const reClaimable = desired.filter((p) => !currentGlobal.has(p))
|
|
311
|
+
if (reClaimable.length) {
|
|
312
|
+
handle = ctx.llm.registerAdapter(reClaimable, adapter)
|
|
313
|
+
registeredByUs = new Set(reClaimable)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function reconcileSlots() {
|
|
318
|
+
const api = settingsApi
|
|
319
|
+
if (!api || typeof api.replace !== 'function') return
|
|
320
|
+
const creds = ctx.credentials
|
|
321
|
+
if (!creds || typeof creds.describe !== 'function') return
|
|
322
|
+
|
|
258
323
|
try {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
324
|
+
const existing = normalizeSlots(live().slots)
|
|
325
|
+
const existingRefs = new Set(existing.map((s) => s.ref))
|
|
326
|
+
const candidateProviders = PROVIDERS.filter((p) => isProvider(p))
|
|
327
|
+
const additions = []
|
|
328
|
+
|
|
329
|
+
for (const provider of candidateProviders) {
|
|
330
|
+
for (let index = 1; index <= 10; index++) {
|
|
331
|
+
const ref = oauthRef(provider, index)
|
|
332
|
+
if (existingRefs.has(ref)) continue
|
|
333
|
+
let d
|
|
334
|
+
try {
|
|
335
|
+
d = await creds.describe(credentialRef(ref))
|
|
336
|
+
} catch (err) {
|
|
337
|
+
if (logger && logger.debug) logger.debug(`[dsh-subscriptions reconcile] describe ${ref} error: ` + (err && err.message || err))
|
|
338
|
+
continue
|
|
339
|
+
}
|
|
340
|
+
if (d && d.configured) {
|
|
341
|
+
let label = d.label || ''
|
|
342
|
+
try {
|
|
343
|
+
const blob = await store.loadBlob(ref).catch(() => null)
|
|
344
|
+
if (blob && (blob.email || blob.label)) {
|
|
345
|
+
label = blob.email || blob.label || label
|
|
346
|
+
}
|
|
347
|
+
} catch (loadErr) {
|
|
348
|
+
if (logger && logger.debug) logger.debug(`[dsh-subscriptions reconcile] loadBlob ${ref} error: ` + (loadErr && loadErr.message || loadErr))
|
|
349
|
+
}
|
|
350
|
+
additions.push({ provider, index, label })
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (!additions.length) return
|
|
356
|
+
if (logger && logger.info) {
|
|
357
|
+
logger.info(`[dsh-subscriptions reconcile] restoring orphaned vault slots: ${additions.map((a) => `${a.provider}#${a.index}`).join(', ')}`)
|
|
358
|
+
}
|
|
359
|
+
const currentSlots = Array.isArray(live().slots) ? live().slots : []
|
|
360
|
+
const newSlots = [...currentSlots, ...additions]
|
|
361
|
+
const parsed = Config({ ...live(), slots: stripLegacySlots(newSlots) })
|
|
362
|
+
await api.replace(parsed)
|
|
363
|
+
syncCustomVendors()
|
|
364
|
+
await syncAdapter()
|
|
365
|
+
refreshModels().catch((err) => {
|
|
366
|
+
if (logger && logger.debug) logger.debug('[dsh-subscriptions reconcile] refreshModels error: ' + (err && err.message || err))
|
|
367
|
+
})
|
|
368
|
+
} catch (e) {
|
|
369
|
+
if (logger && logger.warn) {
|
|
370
|
+
logger.warn('[dsh-subscriptions reconcile] failed: ' + (e && e.message || e))
|
|
371
|
+
}
|
|
263
372
|
}
|
|
264
373
|
}
|
|
265
374
|
|
|
@@ -471,22 +580,67 @@ export function apply(ctx, config) {
|
|
|
471
580
|
}
|
|
472
581
|
|
|
473
582
|
ctx.effect(() => {
|
|
474
|
-
syncAdapter().catch(() => {
|
|
475
|
-
|
|
583
|
+
syncAdapter().catch((err) => {
|
|
584
|
+
if (logger && logger.debug) logger.debug('[dsh-subscriptions] syncAdapter initial error: ' + (err && err.message || err))
|
|
585
|
+
})
|
|
586
|
+
syncOllama().catch((err) => {
|
|
587
|
+
if (logger && logger.debug) logger.debug('[dsh-subscriptions] syncOllama initial error: ' + (err && err.message || err))
|
|
588
|
+
})
|
|
589
|
+
reconcileSlots().catch((err) => {
|
|
590
|
+
if (logger && logger.debug) logger.debug('[dsh-subscriptions] reconcileSlots effect error: ' + (err && err.message || err))
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
let retries = 0
|
|
594
|
+
let retryTimer
|
|
595
|
+
const scheduleRetry = () => {
|
|
596
|
+
if (retries >= 30) return
|
|
597
|
+
retryTimer = setTimeout(retrySync, 1000)
|
|
598
|
+
if (retryTimer && typeof retryTimer.unref === 'function') retryTimer.unref()
|
|
599
|
+
}
|
|
600
|
+
const retrySync = async () => {
|
|
601
|
+
try {
|
|
602
|
+
const loggedIn = await store.loggedInProviders()
|
|
603
|
+
if (!loggedIn.length) return
|
|
604
|
+
const registered = ctx.llm && typeof ctx.llm.listProviders === 'function'
|
|
605
|
+
? ctx.llm.listProviders().map((p) => p.id)
|
|
606
|
+
: []
|
|
607
|
+
const regSet = new Set(registered)
|
|
608
|
+
const pending = loggedIn.filter((p) => !regSet.has(p))
|
|
609
|
+
if (pending.length === 0) return
|
|
610
|
+
retries += 1
|
|
611
|
+
await syncAdapter()
|
|
612
|
+
if (retries < 30) {
|
|
613
|
+
scheduleRetry()
|
|
614
|
+
}
|
|
615
|
+
} catch (err) {
|
|
616
|
+
if (logger && logger.debug) logger.debug('[dsh-subscriptions] retrySync error: ' + (err && err.message || err))
|
|
617
|
+
if (retries < 30) {
|
|
618
|
+
scheduleRetry()
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
scheduleRetry()
|
|
623
|
+
|
|
476
624
|
// #75: eager usage refresh at startup so the windows (5h/7d) land in the blob immediately
|
|
477
625
|
// and the active subscription chip shows 5h/7d/... right away instead of waiting for probeInterval.
|
|
478
626
|
const eager = async () => {
|
|
479
627
|
try {
|
|
480
628
|
for (const slot of normalizeSlots(live().slots)) {
|
|
481
|
-
store.refreshUsage(slot.provider).catch(() => {
|
|
629
|
+
store.refreshUsage(slot.provider).catch((err) => {
|
|
630
|
+
if (logger && logger.debug) logger.debug('[dsh-subscriptions] eager usage error: ' + (err && err.message || err))
|
|
631
|
+
})
|
|
482
632
|
}
|
|
483
|
-
} catch (err) {
|
|
633
|
+
} catch (err) {
|
|
634
|
+
if (logger && logger.debug) logger.debug('[dsh-subscriptions] eager refresh error: ' + (err && err.message || err))
|
|
635
|
+
}
|
|
484
636
|
}
|
|
485
637
|
eager()
|
|
486
638
|
return () => {
|
|
639
|
+
if (retryTimer) clearTimeout(retryTimer)
|
|
487
640
|
if (handle) {
|
|
488
|
-
|
|
641
|
+
disposeAdapterHandle(handle)
|
|
489
642
|
handle = undefined
|
|
643
|
+
registeredByUs.clear()
|
|
490
644
|
}
|
|
491
645
|
}
|
|
492
646
|
}, 'dsh-subscriptions: llm adapter')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.13",
|
|
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",
|