@moguiyu/dsh-tavily 0.1.6 → 0.1.8

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
@@ -7,9 +7,9 @@ window.__ModuleLoader__.load({
7
7
  const inject = ['slots']
8
8
 
9
9
  const STRATEGIES = [
10
- { id: 'rotate', label: 'Rotate each key', hint: 'Keys are tried in turn (round-robin); on 401/429 the next key is used.' },
11
- { id: 'low-usage-first', label: 'Lowest usage first', hint: 'On save, keys are re-ordered by current Tavily usage (least used first).' },
12
- { id: 'high-usage-first', label: 'Highest usage first', hint: 'On save, keys are re-ordered by current Tavily usage (most used first).' }
10
+ { id: 'rotate', label: 'Round-robin', hint: 'Use keys in turn; on HTTP 401/429 the next key is tried automatically.' },
11
+ { id: 'low-usage-first', label: 'Lowest usage first', hint: 'Re-orders keys by current Tavily usage, least-used first. The first key becomes primary.' },
12
+ { id: 'high-usage-first', label: 'Highest usage first', hint: 'Re-orders keys by current Tavily usage, most-used first. The first key becomes primary.' }
13
13
  ]
14
14
 
15
15
  const ICONS = {
@@ -338,7 +338,9 @@ window.__ModuleLoader__.load({
338
338
  const isRemoved = removing[masked] === true
339
339
  const isReplacing = replacing[masked] === true
340
340
  const isRevealed = typeof revealed[masked] === 'string'
341
- const usageRow = usage !== null && usage.ok === true ? usage.perKey[index] : null
341
+ const usageRow = usage !== null && usage.ok === true
342
+ ? (usage.perKey.find((row) => row.masked === masked) || usage.perKey[index])
343
+ : null
342
344
  const pct = usageRow && usageRow.ok && usageRow.planLimit != null && usageRow.planLimit > 0 && usageRow.planUsage != null
343
345
  ? Math.min(100, Math.round((usageRow.planUsage / usageRow.planLimit) * 100))
344
346
  : null
@@ -422,7 +424,7 @@ window.__ModuleLoader__.load({
422
424
  notice !== null && react.createElement('span', { style: { fontSize: 13, color: notice.ok ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)' } }, notice.ok ? notice.ok : String(notice.error))
423
425
  ),
424
426
  react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
425
- (STRATEGIES.find((option) => option.id === strategy) || STRATEGIES[0]).hint + ' Saved immediately when selected; the first key then becomes primary for web_search.'
427
+ (STRATEGIES.find((option) => option.id === strategy) || STRATEGIES[0]).hint + ' Saved immediately when selected.'
426
428
  )
427
429
  ),
428
430
  usageError !== null && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usageError)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moguiyu/dsh-tavily",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Tavily Search for DSH: multiple API keys, rotation/failover, usage gauge, settings card, and web_search toggle in one plugin.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/client.js CHANGED
@@ -12,9 +12,9 @@ export function factory(require) {
12
12
  const inject = ['slots']
13
13
 
14
14
  const STRATEGIES = [
15
- { id: 'rotate', label: 'Rotate each key', hint: 'Keys are tried in turn (round-robin); on 401/429 the next key is used.' },
16
- { id: 'low-usage-first', label: 'Lowest usage first', hint: 'On save, keys are re-ordered by current Tavily usage (least used first).' },
17
- { id: 'high-usage-first', label: 'Highest usage first', hint: 'On save, keys are re-ordered by current Tavily usage (most used first).' }
15
+ { id: 'rotate', label: 'Round-robin', hint: 'Use keys in turn; on HTTP 401/429 the next key is tried automatically.' },
16
+ { id: 'low-usage-first', label: 'Lowest usage first', hint: 'Re-orders keys by current Tavily usage, least-used first. The first key becomes primary.' },
17
+ { id: 'high-usage-first', label: 'Highest usage first', hint: 'Re-orders keys by current Tavily usage, most-used first. The first key becomes primary.' }
18
18
  ]
19
19
 
20
20
  const ICONS = {
@@ -343,7 +343,9 @@ export function factory(require) {
343
343
  const isRemoved = removing[masked] === true
344
344
  const isReplacing = replacing[masked] === true
345
345
  const isRevealed = typeof revealed[masked] === 'string'
346
- const usageRow = usage !== null && usage.ok === true ? usage.perKey[index] : null
346
+ const usageRow = usage !== null && usage.ok === true
347
+ ? (usage.perKey.find((row) => row.masked === masked) || usage.perKey[index])
348
+ : null
347
349
  const pct = usageRow && usageRow.ok && usageRow.planLimit != null && usageRow.planLimit > 0 && usageRow.planUsage != null
348
350
  ? Math.min(100, Math.round((usageRow.planUsage / usageRow.planLimit) * 100))
349
351
  : null
@@ -427,7 +429,7 @@ export function factory(require) {
427
429
  notice !== null && react.createElement('span', { style: { fontSize: 13, color: notice.ok ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)' } }, notice.ok ? notice.ok : String(notice.error))
428
430
  ),
429
431
  react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
430
- (STRATEGIES.find((option) => option.id === strategy) || STRATEGIES[0]).hint + ' Saved immediately when selected; the first key then becomes primary for web_search.'
432
+ (STRATEGIES.find((option) => option.id === strategy) || STRATEGIES[0]).hint + ' Saved immediately when selected.'
431
433
  )
432
434
  ),
433
435
  usageError !== null && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usageError)),
package/src/index.js CHANGED
@@ -306,7 +306,7 @@ function readBody(req) {
306
306
  })
307
307
  }
308
308
 
309
- async function fetchUsageFor(key) {
309
+ async function fetchUsageDetailsFor(key) {
310
310
  try {
311
311
  const controller = new AbortController()
312
312
  const timer = setTimeout(() => controller.abort(), 15000)
@@ -323,14 +323,32 @@ async function fetchUsageFor(key) {
323
323
  const json = await response.json()
324
324
  const account = json !== null && typeof json === 'object' ? json.account : undefined
325
325
  const keyUsage = json !== null && typeof json === 'object' ? json.key : undefined
326
- if (account !== undefined && typeof account.plan_usage === 'number') return account.plan_usage
327
- if (keyUsage !== undefined && typeof keyUsage.usage === 'number') return keyUsage.usage
328
- return null
326
+ const usage = keyUsage !== undefined && typeof keyUsage.usage === 'number'
327
+ ? keyUsage.usage
328
+ : (account !== undefined && typeof account.plan_usage === 'number' ? account.plan_usage : null)
329
+ const planUsage = account !== undefined && typeof account.plan_usage === 'number'
330
+ ? account.plan_usage
331
+ : (keyUsage !== undefined && typeof keyUsage.usage === 'number' ? keyUsage.usage : null)
332
+ const planLimit = account !== undefined && typeof account.plan_limit === 'number'
333
+ ? account.plan_limit
334
+ : (keyUsage !== undefined && typeof keyUsage.limit === 'number' ? keyUsage.limit
335
+ : (keyUsage !== undefined && typeof keyUsage.plan_limit === 'number' ? keyUsage.plan_limit : null))
336
+ const currentPlan = account !== undefined && typeof account.plan === 'string'
337
+ ? account.plan
338
+ : (keyUsage !== undefined && typeof keyUsage.plan === 'string' ? keyUsage.plan : null)
339
+ if (usage === null && planUsage === null) return null
340
+ return { usage, planUsage, planLimit, currentPlan }
329
341
  } catch {
330
342
  return null
331
343
  }
332
344
  }
333
345
 
346
+ async function fetchUsageFor(key) {
347
+ const details = await fetchUsageDetailsFor(key)
348
+ if (details === null) return null
349
+ return details.usage !== null ? details.usage : details.planUsage
350
+ }
351
+
334
352
  async function collectStoredKeys(credentials) {
335
353
  const out = []
336
354
  const seen = new Set()
@@ -382,11 +400,24 @@ function applyBackend(ctx) {
382
400
  }
383
401
  }
384
402
  }
403
+ const primaryMasked = keys.length > 0 ? maskValue(keys[0]) : null
404
+ const display = keys.map((value) => {
405
+ const masked = maskValue(value)
406
+ return {
407
+ masked,
408
+ savedAt: keySavedAt[masked] !== undefined ? keySavedAt[masked] : null,
409
+ }
410
+ }).sort((a, b) => {
411
+ if (a.savedAt === b.savedAt) return 0
412
+ if (a.savedAt === null) return 1
413
+ if (b.savedAt === null) return -1
414
+ return a.savedAt < b.savedAt ? -1 : 1
415
+ })
385
416
  return {
386
- keys: keys.map((value, index) => ({
387
- masked: maskValue(value),
388
- savedAt: keySavedAt[maskValue(value)] !== undefined ? keySavedAt[maskValue(value)] : null,
389
- primary: index === 0,
417
+ keys: display.map((entry) => ({
418
+ masked: entry.masked,
419
+ savedAt: entry.savedAt,
420
+ primary: entry.masked === primaryMasked,
390
421
  })),
391
422
  strategy: isValidStrategy(state.strategy) ? state.strategy : 'rotate',
392
423
  }
@@ -419,15 +450,25 @@ function applyBackend(ctx) {
419
450
  try {
420
451
  const keys = await collectStoredKeys(credentials)
421
452
  if (keys.length === 0) return send(res, 200, { ok: false, error: 'no Tavily API key configured' })
453
+ const state = readState(MANAGER_STATE)
454
+ const keySavedAt = state.keySavedAt !== undefined && typeof state.keySavedAt === 'object' ? state.keySavedAt : {}
455
+ const orderedKeys = keys.slice().sort((a, b) => {
456
+ const am = keySavedAt[maskValue(a)] !== undefined ? keySavedAt[maskValue(a)] : null
457
+ const bm = keySavedAt[maskValue(b)] !== undefined ? keySavedAt[maskValue(b)] : null
458
+ if (am === bm) return 0
459
+ if (am === null) return 1
460
+ if (bm === null) return -1
461
+ return am < bm ? -1 : 1
462
+ })
422
463
  const perKey = []
423
- for (const key of keys) {
464
+ for (const key of orderedKeys) {
424
465
  try {
425
- const usage = await fetchUsageFor(key)
466
+ const usage = await fetchUsageDetailsFor(key)
426
467
  perKey.push(usage !== null
427
- ? { ok: true, usage, planUsage: usage, planLimit: null, currentPlan: null }
428
- : { ok: false, error: 'usage unavailable' })
468
+ ? { ok: true, masked: maskValue(key), ...usage }
469
+ : { ok: false, masked: maskValue(key), error: 'usage unavailable' })
429
470
  } catch (error) {
430
- perKey.push({ ok: false, error: String(error && error.message ? error.message : error) })
471
+ perKey.push({ ok: false, masked: maskValue(key), error: String(error && error.message ? error.message : error) })
431
472
  }
432
473
  }
433
474
  const okRows = perKey.filter((row) => row.ok)
@@ -438,8 +479,8 @@ function applyBackend(ctx) {
438
479
  keys: keys.length,
439
480
  okKeys: okRows.length,
440
481
  usage: okRows.reduce((sum, row) => sum + (row.usage || 0), 0),
441
- planUsage: null,
442
- planLimit: null,
482
+ planUsage: okRows.reduce((sum, row) => sum + (row.planUsage || 0), 0),
483
+ planLimit: okRows.every((row) => row.planLimit !== null) ? okRows.reduce((sum, row) => sum + row.planLimit, 0) : null,
443
484
  },
444
485
  })
445
486
  } catch (error) {