@goodandready/dsh-clinebot 0.5.18 → 0.5.19
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/CHANGELOG.md +6 -0
- package/lib/account-pool.js +14 -11
- package/lib/client.js +76 -6
- package/lib/config.js +3 -0
- package/lib/proxy-token.js +78 -0
- package/lib/routes/proxy.js +408 -391
- package/lib/session-router.js +43 -6
- package/package.json +1 -1
package/lib/session-router.js
CHANGED
|
@@ -130,6 +130,30 @@ export function getAccountUsageMetric(acc) {
|
|
|
130
130
|
return 50
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
let roundRobinCursor = 0
|
|
134
|
+
|
|
135
|
+
export function getRoundRobinCursor() {
|
|
136
|
+
return roundRobinCursor
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function resetRoundRobinCursor() {
|
|
140
|
+
roundRobinCursor = 0
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function getNextRoundRobinAccount(accounts = []) {
|
|
144
|
+
if (!Array.isArray(accounts) || accounts.length === 0) {
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
const available = accounts.filter(acc => acc && acc.present !== false && !isAccountBlocked(acc))
|
|
148
|
+
const pool = available.length > 0 ? available : accounts.filter(acc => acc && acc.present !== false)
|
|
149
|
+
if (pool.length === 0) {
|
|
150
|
+
return accounts[0] || null
|
|
151
|
+
}
|
|
152
|
+
const chosen = pool[Math.abs(roundRobinCursor) % pool.length]
|
|
153
|
+
roundRobinCursor = (roundRobinCursor + 1) % pool.length
|
|
154
|
+
return chosen
|
|
155
|
+
}
|
|
156
|
+
|
|
133
157
|
export function selectLeastUsedAccount(accounts = []) {
|
|
134
158
|
if (!Array.isArray(accounts) || accounts.length === 0) {
|
|
135
159
|
return null
|
|
@@ -164,7 +188,7 @@ export function selectLeastUsedAccount(accounts = []) {
|
|
|
164
188
|
* @param {string} [defaultActiveId] Explicit active account ID, envName or 'auto'
|
|
165
189
|
* @returns {{ account: Object|null, isReassigned: boolean, reason: string }}
|
|
166
190
|
*/
|
|
167
|
-
export function resolveSessionAccount(sessionId, accounts = [], defaultActiveId = '') {
|
|
191
|
+
export function resolveSessionAccount(sessionId, accounts = [], defaultActiveId = '', accountMode = 'least_used') {
|
|
168
192
|
const safeAccounts = Array.isArray(accounts) ? accounts.filter(a => a && a.present !== false) : []
|
|
169
193
|
if (safeAccounts.length === 0) {
|
|
170
194
|
const fallback = Array.isArray(accounts) && accounts.length > 0 ? accounts[0] : null
|
|
@@ -185,13 +209,24 @@ export function resolveSessionAccount(sessionId, accounts = [], defaultActiveId
|
|
|
185
209
|
|
|
186
210
|
const pinnedAvailable = pinnedAccount && !isAccountBlocked(pinnedAccount) ? pinnedAccount : null
|
|
187
211
|
|
|
212
|
+
const pickCandidate = () => {
|
|
213
|
+
if (accountMode === 'round_robin') {
|
|
214
|
+
return getNextRoundRobinAccount(safeAccounts) || safeAccounts[0]
|
|
215
|
+
}
|
|
216
|
+
return selectLeastUsedAccount(safeAccounts) || safeAccounts[0]
|
|
217
|
+
}
|
|
218
|
+
|
|
188
219
|
// If no sessionId provided:
|
|
189
220
|
if (!sessionId) {
|
|
190
221
|
if (pinnedAvailable) {
|
|
191
222
|
return { account: pinnedAvailable, isReassigned: false, reason: 'pinned_active' }
|
|
192
223
|
}
|
|
193
|
-
const
|
|
194
|
-
return {
|
|
224
|
+
const chosen = pickCandidate()
|
|
225
|
+
return {
|
|
226
|
+
account: chosen,
|
|
227
|
+
isReassigned: false,
|
|
228
|
+
reason: pinnedAccount ? 'pinned_in_cooldown_fallback' : (accountMode === 'round_robin' ? 'round_robin_ad_hoc' : 'no_session_id')
|
|
229
|
+
}
|
|
195
230
|
}
|
|
196
231
|
|
|
197
232
|
const now = Date.now()
|
|
@@ -235,8 +270,8 @@ export function resolveSessionAccount(sessionId, accounts = [], defaultActiveId
|
|
|
235
270
|
// Otherwise, failover is needed
|
|
236
271
|
}
|
|
237
272
|
|
|
238
|
-
// Pick
|
|
239
|
-
const chosen =
|
|
273
|
+
// Pick candidate according to accountMode
|
|
274
|
+
const chosen = pickCandidate()
|
|
240
275
|
if (chosen) {
|
|
241
276
|
sessions.set(sessionId, {
|
|
242
277
|
accountId: chosen.apiKeyEnv || chosen.id,
|
|
@@ -248,7 +283,7 @@ export function resolveSessionAccount(sessionId, accounts = [], defaultActiveId
|
|
|
248
283
|
return {
|
|
249
284
|
account: chosen,
|
|
250
285
|
isReassigned: Boolean(existing),
|
|
251
|
-
reason: existing ? 'mid_session_failover' : (pinnedAccount ? 'pinned_in_cooldown_fallback' : 'new_session_assigned')
|
|
286
|
+
reason: existing ? 'mid_session_failover' : (pinnedAccount ? 'pinned_in_cooldown_fallback' : (accountMode === 'round_robin' ? 'round_robin_assigned' : 'new_session_assigned'))
|
|
252
287
|
}
|
|
253
288
|
}
|
|
254
289
|
|
|
@@ -269,6 +304,7 @@ export function releaseSession(sessionId) {
|
|
|
269
304
|
export function resetSessionRouter() {
|
|
270
305
|
sessions.clear()
|
|
271
306
|
cooldowns.clear()
|
|
307
|
+
roundRobinCursor = 0
|
|
272
308
|
}
|
|
273
309
|
|
|
274
310
|
/**
|
|
@@ -279,6 +315,7 @@ export function getSessionRouterStatus() {
|
|
|
279
315
|
activeSessionsCount: sessions.size,
|
|
280
316
|
maxSessions: MAX_SESSIONS,
|
|
281
317
|
sessionTtlMs: SESSION_TTL_MS,
|
|
318
|
+
roundRobinCursor,
|
|
282
319
|
cooldownsCount: cooldowns.size,
|
|
283
320
|
cooldowns: Object.fromEntries(
|
|
284
321
|
Array.from(cooldowns.entries()).map(([k, v]) => [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-clinebot",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.19",
|
|
4
4
|
"description": "DeepSeek Harness companion for ClineBot / ClinePass: dynamic subscription models sync, quota exhaustion warnings, session metrics, dedicated settings page, live usage limits, and /cline slash-command.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|