@goodandready/dsh-subscriptions 0.6.44 → 0.6.45
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/accounts.js +48 -1
- package/lib/adapter-manager.js +3 -0
- package/lib/index.js +1 -0
- package/lib/rotate.js +10 -2
- package/lib/stream-rotate.js +1 -0
- package/package.json +1 -1
package/lib/accounts.js
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync } from "node:fs"
|
|
2
|
+
import { homedir } from "node:os"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
|
|
5
|
+
function resolveQuarantineFilePath() {
|
|
6
|
+
const base = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
7
|
+
return join(base, 'subscriptions', 'quarantines.json')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function loadQuarantinesFromFile(map) {
|
|
11
|
+
try {
|
|
12
|
+
const p = resolveQuarantineFilePath()
|
|
13
|
+
const content = readFileSync(p, 'utf8')
|
|
14
|
+
const json = JSON.parse(content)
|
|
15
|
+
const now = Date.now()
|
|
16
|
+
if (json && typeof json === 'object') {
|
|
17
|
+
for (const [k, v] of Object.entries(json)) {
|
|
18
|
+
if (v && Number(v.until) > now) {
|
|
19
|
+
map.set(k, v)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} catch { /* best-effort */ }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function saveQuarantinesToFile(map) {
|
|
27
|
+
try {
|
|
28
|
+
const p = resolveQuarantineFilePath()
|
|
29
|
+
mkdirSync(join(p, '..'), { recursive: true })
|
|
30
|
+
const obj = {}
|
|
31
|
+
const now = Date.now()
|
|
32
|
+
for (const [k, v] of map.entries()) {
|
|
33
|
+
if (v && Number(v.until) > now) {
|
|
34
|
+
obj[k] = v
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
writeFileSync(p, JSON.stringify(obj), 'utf8')
|
|
38
|
+
} catch { /* best-effort */ }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const sharedQuarantines = new Map()
|
|
42
|
+
loadQuarantinesFromFile(sharedQuarantines)
|
|
43
|
+
|
|
1
44
|
import { needsWarmupProbe, enterProbing, resolveWarmupSuccess, resolveWarmupFailure, STATUS_QUARANTINE, STATUS_PROBING } from "./quarantine.js"
|
|
2
45
|
import { fetchWithTimeout, isTimeoutError } from "./http.js"
|
|
3
46
|
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
|
@@ -62,7 +105,7 @@ export function vendorConfig(provider, cfg) {
|
|
|
62
105
|
export function createAccountStore({ credentials, getConfig, fetchImpl, fetchForRef, onLimitNotice }) {
|
|
63
106
|
const cooldowns = new Map()
|
|
64
107
|
const quotas = new Map()
|
|
65
|
-
const quarantines =
|
|
108
|
+
const quarantines = sharedQuarantines
|
|
66
109
|
const writeQueues = new Map()
|
|
67
110
|
const credentialVersions = new Map()
|
|
68
111
|
const clearedRefs = new Set()
|
|
@@ -183,6 +226,7 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
183
226
|
quarantineReason: quarantines.get(ref) ? quarantines.get(ref).reason : null,
|
|
184
227
|
cooldownFamilies: cooldowns.get(ref) && cooldowns.get(ref).families ? cooldowns.get(ref).families : null,
|
|
185
228
|
quota: quotas.get(ref) || null,
|
|
229
|
+
pacePerHour: quotas.get(ref)?.pacePerHour || 0,
|
|
186
230
|
usage: windows.get(ref) || base.usage || null,
|
|
187
231
|
health: health.get(ref) || null,
|
|
188
232
|
healthScore: computeHealthScore(health.get(ref) || null),
|
|
@@ -218,6 +262,7 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
218
262
|
quarantineUntil: inQ ? q.until : 0,
|
|
219
263
|
quarantineReason: inQ ? q.reason : null,
|
|
220
264
|
label: slot.label || info.label,
|
|
265
|
+
pacePerHour: info.pacePerHour || 0,
|
|
221
266
|
})
|
|
222
267
|
}
|
|
223
268
|
return out
|
|
@@ -417,6 +462,7 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
417
462
|
attempts: Number(attempts) || 1,
|
|
418
463
|
status: STATUS_QUARANTINE,
|
|
419
464
|
})
|
|
465
|
+
saveQuarantinesToFile(quarantines)
|
|
420
466
|
},
|
|
421
467
|
getQuarantine(ref, now = Date.now()) {
|
|
422
468
|
const q = quarantines.get(ref)
|
|
@@ -456,6 +502,7 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
|
|
|
456
502
|
},
|
|
457
503
|
releaseQuarantine(ref) {
|
|
458
504
|
quarantines.delete(ref)
|
|
505
|
+
saveQuarantinesToFile(quarantines)
|
|
459
506
|
},
|
|
460
507
|
rememberRequest(ref) { requestCounts.set(ref, (requestCounts.get(ref) || 0) + 1) },
|
|
461
508
|
getRequestCount(ref) { return requestCounts.get(ref) || 0 },
|
package/lib/adapter-manager.js
CHANGED
|
@@ -18,6 +18,7 @@ export function createAdapterManager({
|
|
|
18
18
|
syncCustomVendors,
|
|
19
19
|
syncOllama,
|
|
20
20
|
stripLegacySlots,
|
|
21
|
+
ollamaFallback,
|
|
21
22
|
}) {
|
|
22
23
|
let handle
|
|
23
24
|
let adapterDisposed = false
|
|
@@ -58,6 +59,8 @@ export function createAdapterManager({
|
|
|
58
59
|
fetchImpl: fetch,
|
|
59
60
|
fetchForRef,
|
|
60
61
|
hideDeprecatedModels: () => Boolean(live().hideDeprecatedModels),
|
|
62
|
+
cascadingFallback: (...args) => cascadeFallbackStream(...args),
|
|
63
|
+
ollamaFallback: typeof ollamaFallback === 'function' ? ollamaFallback : async function* ({ err }) { throw err },
|
|
61
64
|
})
|
|
62
65
|
|
|
63
66
|
async function* cascadeFallbackStream({ options, provider, err, visited = new Set() }) {
|
package/lib/index.js
CHANGED
|
@@ -302,6 +302,7 @@ export function apply(ctx, rawConfig) {
|
|
|
302
302
|
syncCustomVendors,
|
|
303
303
|
syncOllama,
|
|
304
304
|
stripLegacySlots,
|
|
305
|
+
ollamaFallback: ollamaFallbackStream,
|
|
305
306
|
})
|
|
306
307
|
const { syncAdapter, reconcileSlots, refreshModels, cascadeFallbackStream } = adapterManager
|
|
307
308
|
adapterManager.registerAdapterLifecycle()
|
package/lib/rotate.js
CHANGED
|
@@ -81,7 +81,11 @@ export function pickAccount(accounts, nowMs, opts) {
|
|
|
81
81
|
const pinned = list.find((a) => (a.ref || a.id) === pinnedRef)
|
|
82
82
|
if (pinned && pinned.hasToken && !isQuarantined(pinned, now)) {
|
|
83
83
|
const isCooldown = pinned.cooldownUntil && Number(pinned.cooldownUntil) > now && cooldownBlocks(pinned, opts && opts.family)
|
|
84
|
-
|
|
84
|
+
const isPinnedZero = Boolean(pinned.quota && (
|
|
85
|
+
(pinned.quota.remaining != null && Number(pinned.quota.remaining) <= 0) ||
|
|
86
|
+
(pinned.quota.remainingPercent != null && Number(pinned.quota.remainingPercent) <= 0)
|
|
87
|
+
) && (!pinned.quota.resetAt || Number(pinned.quota.resetAt) > now))
|
|
88
|
+
if (!isCooldown && !isPinnedZero && !isQuotaExhausted(pinned) && !isUsageExhausted(pinned)) {
|
|
85
89
|
return pinned
|
|
86
90
|
}
|
|
87
91
|
}
|
|
@@ -116,8 +120,12 @@ export function pickAccount(accounts, nowMs, opts) {
|
|
|
116
120
|
const isCooldown = acc.cooldownUntil && Number(acc.cooldownUntil) > now && cooldownBlocks(acc, opts && opts.family)
|
|
117
121
|
const inQuarantine = isQuarantined(acc, now)
|
|
118
122
|
const isHardExhausted = (acc.usagePercent != null && Number(acc.usagePercent) >= 100 && (!acc.quota || !acc.quota.resetAt || Number(acc.quota.resetAt) > now))
|
|
123
|
+
const isZeroQuota = Boolean(acc.quota && (
|
|
124
|
+
(acc.quota.remaining != null && Number(acc.quota.remaining) <= 0) ||
|
|
125
|
+
(acc.quota.remainingPercent != null && Number(acc.quota.remainingPercent) <= 0)
|
|
126
|
+
) && (!acc.quota.resetAt || Number(acc.quota.resetAt) > now))
|
|
119
127
|
|
|
120
|
-
if (isCooldown || inQuarantine || isHardExhausted) {
|
|
128
|
+
if (isCooldown || inQuarantine || isHardExhausted || isZeroQuota) {
|
|
121
129
|
continue
|
|
122
130
|
}
|
|
123
131
|
|
package/lib/stream-rotate.js
CHANGED
|
@@ -27,6 +27,7 @@ export async function* streamWithRotation({
|
|
|
27
27
|
sessionId: options && options.sessionId,
|
|
28
28
|
tag: options && options.tag,
|
|
29
29
|
vip: options && options.vip,
|
|
30
|
+
autoPacing: options && (options.autoPacing !== undefined ? options.autoPacing : (options.config && options.config.autoPacing)),
|
|
30
31
|
})
|
|
31
32
|
|
|
32
33
|
if (!account || tried.has(account.ref || account.id)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.45",
|
|
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",
|