@moguiyu/dsh-tavily 0.1.4 → 0.1.5

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
@@ -144,10 +144,6 @@ window.__ModuleLoader__.load({
144
144
  }
145
145
  }
146
146
 
147
- const hasPending = Object.values(removing).some(Boolean)
148
- || Object.values(replacing).some(Boolean)
149
- || adds.some((item) => item.value.trim().length > 0)
150
-
151
147
  const saveStrategy = async (next) => {
152
148
  setBusy(true)
153
149
  setNotice(null)
@@ -191,29 +187,46 @@ window.__ModuleLoader__.load({
191
187
  }
192
188
  }
193
189
 
194
- const save = async () => {
190
+ const saveRemove = async (masked) => {
195
191
  setBusy(true)
196
192
  setNotice(null)
197
193
  try {
198
- const addValues = adds.map((item) => item.value.trim()).filter((value) => value.length > 0)
199
- const removeMasked = [
200
- ...Object.keys(removing).filter((masked) => removing[masked] === true),
201
- ...Object.keys(replacing).filter((masked) => replacing[masked] === true)
202
- ]
203
194
  const response = await fetch('/api/tavily-manager', {
204
195
  method: 'POST',
205
196
  headers: { 'content-type': 'application/json' },
206
- body: JSON.stringify({ add: addValues, remove: removeMasked, strategy })
197
+ body: JSON.stringify({ add: [], remove: [masked], strategy })
207
198
  })
208
199
  const data = await response.json()
209
- if (!data.ok) { setNotice({ error: data.error || 'Save failed' }); return }
210
- setNotice({ ok: 'Saved — effective on the next search.' })
211
- setRemoving({})
212
- setReplacing({})
213
- setReplaceDrafts({})
214
- setAdds([])
215
- setRevealed({})
216
- setConfirm({})
200
+ if (!data.ok) { setNotice({ error: data.error || 'Delete failed' }); return }
201
+ setNotice({ ok: 'Key removed.' })
202
+ setConfirm((current) => Object.assign({}, current, { [masked]: false }))
203
+ setRemoving((current) => Object.assign({}, current, { [masked]: false }))
204
+ setRevealed((current) => Object.assign({}, current, { [masked]: false }))
205
+ await refresh()
206
+ } catch (error) {
207
+ setNotice({ error: String(error && error.message ? error.message : error) })
208
+ } finally {
209
+ setBusy(false)
210
+ }
211
+ }
212
+
213
+ const saveReplace = async (masked) => {
214
+ const value = typeof replaceDrafts[masked] === 'string' ? replaceDrafts[masked].trim() : ''
215
+ if (!value) return
216
+ setBusy(true)
217
+ setNotice(null)
218
+ try {
219
+ const response = await fetch('/api/tavily-manager', {
220
+ method: 'POST',
221
+ headers: { 'content-type': 'application/json' },
222
+ body: JSON.stringify({ add: [value], remove: [masked], strategy })
223
+ })
224
+ const data = await response.json()
225
+ if (!data.ok) { setNotice({ error: data.error || 'Update failed' }); return }
226
+ setNotice({ ok: 'Key updated.' })
227
+ setReplacing((current) => Object.assign({}, current, { [masked]: false }))
228
+ setReplaceDrafts((current) => Object.assign({}, current, { [masked]: '' }))
229
+ setRevealed((current) => Object.assign({}, current, { [masked]: false }))
217
230
  await refresh()
218
231
  } catch (error) {
219
232
  setNotice({ error: String(error && error.message ? error.message : error) })
@@ -249,6 +262,7 @@ window.__ModuleLoader__.load({
249
262
  setConfirm((current) => Object.assign({}, current, { [masked]: false }))
250
263
  setRemoving((current) => Object.assign({}, current, { [masked]: true }))
251
264
  setRevealed((current) => Object.assign({}, current, { [masked]: false }))
265
+ saveRemove(masked)
252
266
  }
253
267
 
254
268
  const restore = (masked) => {
@@ -314,16 +328,21 @@ window.__ModuleLoader__.load({
314
328
  react.createElement('thead', null,
315
329
  react.createElement('tr', null,
316
330
  react.createElement('th', { style: headStyle }, 'Key'),
331
+ react.createElement('th', { style: headStyle }, 'Usage'),
317
332
  react.createElement('th', { style: headStyle }, 'Saved'),
318
333
  react.createElement('th', { style: headStyle }, 'Actions')
319
334
  )
320
335
  ),
321
336
  react.createElement('tbody', null,
322
- (server !== null ? server.keys : []).map((key) => {
337
+ (server !== null ? server.keys : []).map((key, index) => {
323
338
  const masked = key.masked
324
339
  const isRemoved = removing[masked] === true
325
340
  const isReplacing = replacing[masked] === true
326
341
  const isRevealed = typeof revealed[masked] === 'string'
342
+ const usageRow = usage !== null && usage.ok === true ? usage.perKey[index] : null
343
+ const pct = usageRow && usageRow.ok && usageRow.planLimit != null && usageRow.planLimit > 0 && usageRow.planUsage != null
344
+ ? Math.min(100, Math.round((usageRow.planUsage / usageRow.planLimit) * 100))
345
+ : null
327
346
  return react.createElement('tr', { key: masked, style: { borderBottom: '1px solid var(--dsw-alias-border-l2)', opacity: isRemoved ? 0.45 : 1 } },
328
347
  react.createElement('td', { style: Object.assign({}, cellStyle, { fontFamily: 'var(--ds-font-family-code, monospace)', fontSize: 12, wordBreak: 'break-all' }) },
329
348
  isReplacing
@@ -342,11 +361,14 @@ window.__ModuleLoader__.load({
342
361
  })
343
362
  )
344
363
  ),
364
+ react.createElement('td', { style: cellStyle },
365
+ react.createElement(UsageCircle, { percent: pct, label: pct != null ? pct + '%' : (usageRow && usageRow.ok && usageRow.planUsage != null ? String(usageRow.planUsage) : '—') })
366
+ ),
345
367
  react.createElement('td', { style: Object.assign({}, cellStyle, { color: 'var(--dsw-alias-label-tertiary)', whiteSpace: 'nowrap' }) }, formatDate(key.savedAt)),
346
368
  react.createElement('td', { style: cellStyle },
347
369
  isReplacing
348
370
  ? react.createElement('span', { style: { display: 'inline-flex', gap: 2 } },
349
- react.createElement(IconButton, { icon: 'check', title: 'Keep', onClick: () => { setReplacing((current) => Object.assign({}, current, { [masked]: false })) }, disabled: busy }),
371
+ react.createElement(IconButton, { icon: 'check', title: 'Save key', onClick: () => saveReplace(masked), disabled: busy || (typeof replaceDrafts[masked] === 'string' ? replaceDrafts[masked].trim().length === 0 : true) }),
350
372
  react.createElement(IconButton, { icon: 'close', title: 'Cancel', onClick: () => cancelReplace(masked), disabled: busy })
351
373
  )
352
374
  : react.createElement('span', { style: { display: 'inline-flex', gap: 2 } },
@@ -376,6 +398,7 @@ window.__ModuleLoader__.load({
376
398
  })
377
399
  ),
378
400
  react.createElement('td', { style: cellStyle }, '—'),
401
+ react.createElement('td', { style: cellStyle }, '—'),
379
402
  react.createElement('td', { style: cellStyle },
380
403
  react.createElement('span', { style: { display: 'inline-flex', gap: 2 } },
381
404
  react.createElement(IconButton, { icon: 'check', title: 'Save key', onClick: () => saveAdd(item), disabled: busy || item.value.trim().length === 0 }),
@@ -385,63 +408,27 @@ window.__ModuleLoader__.load({
385
408
  ))
386
409
  )
387
410
  ),
388
- react.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
389
- react.createElement('button', { type: 'button', style: btn, onClick: () => setAdds((current) => [...current, { id: 'add-' + Date.now() + '-' + Math.random().toString(36).slice(2, 7), value: '' }]), disabled: busy }, '+ Add key')
411
+ react.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' } },
412
+ react.createElement('button', { type: 'button', style: btn, onClick: () => setAdds((current) => [...current, { id: 'add-' + Date.now() + '-' + Math.random().toString(36).slice(2, 7), value: '' }]), disabled: busy }, '+ Add key'),
413
+ react.createElement('button', { type: 'button', style: btn, onClick: refresh, disabled: busy }, 'Refresh usage')
390
414
  ),
391
- react.createElement('div', { style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 12, padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 8 } },
392
- react.createElement('label', { style: { fontWeight: 500, fontSize: 13 } }, 'Key usage strategy'),
393
- react.createElement('select', {
394
- style: { maxWidth: 280, height: 32, border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, background: 'var(--dsw-alias-bg-layer-1)', color: 'var(--dsw-alias-label-primary)', padding: '0 10px', fontSize: 13 },
395
- value: strategy,
396
- onChange: (event) => saveStrategy(event.target.value),
397
- disabled: busy
398
- }, STRATEGIES.map((option) => react.createElement('option', { key: option.id, value: option.id }, option.label))),
399
- react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
400
- (STRATEGIES.find((option) => option.id === strategy) || STRATEGIES[0]).hint + ' The strategy is saved immediately when you select it; the first key then becomes primary for web_search.'
401
- )
402
- ),
403
- react.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
404
- react.createElement('button', { type: 'button', style: Object.assign({}, btn, { height: 32, background: 'var(--dsw-alias-button-primary-fill)', color: 'var(--dsw-alias-label-primary-foreground)' }), onClick: save, disabled: busy || !hasPending }, 'Save key changes'),
405
- 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))
406
- ),
407
- react.createElement('div', { style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 12, padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 8 } },
408
- react.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, justifyContent: 'space-between' } },
409
- react.createElement('span', { style: { fontWeight: 500, fontSize: 13 } }, 'Tavily Usage'),
410
- react.createElement('button', { type: 'button', style: btn, onClick: refresh, disabled: busy }, 'Refresh')
415
+ react.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 6 } },
416
+ react.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } },
417
+ react.createElement('label', { style: { fontWeight: 500, fontSize: 13 } }, 'Key usage strategy'),
418
+ react.createElement('select', {
419
+ style: { maxWidth: 280, height: 32, border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, background: 'var(--dsw-alias-bg-layer-1)', color: 'var(--dsw-alias-label-primary)', padding: '0 10px', fontSize: 13 },
420
+ value: strategy,
421
+ onChange: (event) => saveStrategy(event.target.value),
422
+ disabled: busy
423
+ }, STRATEGIES.map((option) => react.createElement('option', { key: option.id, value: option.id }, option.label))),
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))
411
425
  ),
412
- usageError !== null && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usageError)),
413
- usage !== null && usage.ok === false && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usage.error)),
414
- usage !== null && usage.ok === true && react.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8 } },
415
- react.createElement('div', { style: { fontSize: 13 } },
416
- usage.totals.keys + ' key(s) (' + usage.totals.okKeys + ' readable)' + (usage.totals.planLimit !== null ? ' · plan usage ' + usage.totals.planUsage + ' / ' + usage.totals.planLimit : '')
417
- ),
418
- react.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } },
419
- react.createElement('thead', null,
420
- react.createElement('tr', null,
421
- react.createElement('th', { style: headStyle }, 'API Key'),
422
- react.createElement('th', { style: headStyle }, 'Usage')
423
- )
424
- ),
425
- react.createElement('tbody', null,
426
- (server !== null ? server.keys : []).map((key, index) => {
427
- const row = usage.perKey[index]
428
- const pct = row && row.ok && row.planLimit != null && row.planLimit > 0 && row.planUsage != null
429
- ? Math.min(100, Math.round((row.planUsage / row.planLimit) * 100))
430
- : null
431
- return react.createElement('tr', { key: key.masked, style: { borderBottom: '1px solid var(--dsw-alias-border-l2)' } },
432
- react.createElement('td', { style: Object.assign({}, cellStyle, { fontFamily: 'var(--ds-font-family-code, monospace)', fontSize: 12, wordBreak: 'break-all' }) }, key.masked),
433
- react.createElement('td', { style: cellStyle },
434
- react.createElement(UsageCircle, { percent: pct, label: pct != null ? pct + '%' : (row && row.ok && row.planUsage != null ? String(row.planUsage) : '—') })
435
- )
436
- )
437
- })
438
- )
439
- )
426
+ react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
427
+ (STRATEGIES.find((option) => option.id === strategy) || STRATEGIES[0]).hint + ' Saved immediately when selected; the first key then becomes primary for web_search.'
440
428
  )
441
429
  ),
442
- react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
443
- 'Usage data comes from the Tavily /usage endpoint (fetched locally; no keys are exposed).'
444
- )
430
+ usageError !== null && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usageError)),
431
+ usage !== null && usage.ok === false && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usage.error))
445
432
  )
446
433
  )
447
434
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moguiyu/dsh-tavily",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
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
@@ -149,10 +149,6 @@ export function factory(require) {
149
149
  }
150
150
  }
151
151
 
152
- const hasPending = Object.values(removing).some(Boolean)
153
- || Object.values(replacing).some(Boolean)
154
- || adds.some((item) => item.value.trim().length > 0)
155
-
156
152
  const saveStrategy = async (next) => {
157
153
  setBusy(true)
158
154
  setNotice(null)
@@ -196,29 +192,46 @@ export function factory(require) {
196
192
  }
197
193
  }
198
194
 
199
- const save = async () => {
195
+ const saveRemove = async (masked) => {
200
196
  setBusy(true)
201
197
  setNotice(null)
202
198
  try {
203
- const addValues = adds.map((item) => item.value.trim()).filter((value) => value.length > 0)
204
- const removeMasked = [
205
- ...Object.keys(removing).filter((masked) => removing[masked] === true),
206
- ...Object.keys(replacing).filter((masked) => replacing[masked] === true)
207
- ]
208
199
  const response = await fetch('/api/tavily-manager', {
209
200
  method: 'POST',
210
201
  headers: { 'content-type': 'application/json' },
211
- body: JSON.stringify({ add: addValues, remove: removeMasked, strategy })
202
+ body: JSON.stringify({ add: [], remove: [masked], strategy })
212
203
  })
213
204
  const data = await response.json()
214
- if (!data.ok) { setNotice({ error: data.error || 'Save failed' }); return }
215
- setNotice({ ok: 'Saved — effective on the next search.' })
216
- setRemoving({})
217
- setReplacing({})
218
- setReplaceDrafts({})
219
- setAdds([])
220
- setRevealed({})
221
- setConfirm({})
205
+ if (!data.ok) { setNotice({ error: data.error || 'Delete failed' }); return }
206
+ setNotice({ ok: 'Key removed.' })
207
+ setConfirm((current) => Object.assign({}, current, { [masked]: false }))
208
+ setRemoving((current) => Object.assign({}, current, { [masked]: false }))
209
+ setRevealed((current) => Object.assign({}, current, { [masked]: false }))
210
+ await refresh()
211
+ } catch (error) {
212
+ setNotice({ error: String(error && error.message ? error.message : error) })
213
+ } finally {
214
+ setBusy(false)
215
+ }
216
+ }
217
+
218
+ const saveReplace = async (masked) => {
219
+ const value = typeof replaceDrafts[masked] === 'string' ? replaceDrafts[masked].trim() : ''
220
+ if (!value) return
221
+ setBusy(true)
222
+ setNotice(null)
223
+ try {
224
+ const response = await fetch('/api/tavily-manager', {
225
+ method: 'POST',
226
+ headers: { 'content-type': 'application/json' },
227
+ body: JSON.stringify({ add: [value], remove: [masked], strategy })
228
+ })
229
+ const data = await response.json()
230
+ if (!data.ok) { setNotice({ error: data.error || 'Update failed' }); return }
231
+ setNotice({ ok: 'Key updated.' })
232
+ setReplacing((current) => Object.assign({}, current, { [masked]: false }))
233
+ setReplaceDrafts((current) => Object.assign({}, current, { [masked]: '' }))
234
+ setRevealed((current) => Object.assign({}, current, { [masked]: false }))
222
235
  await refresh()
223
236
  } catch (error) {
224
237
  setNotice({ error: String(error && error.message ? error.message : error) })
@@ -254,6 +267,7 @@ export function factory(require) {
254
267
  setConfirm((current) => Object.assign({}, current, { [masked]: false }))
255
268
  setRemoving((current) => Object.assign({}, current, { [masked]: true }))
256
269
  setRevealed((current) => Object.assign({}, current, { [masked]: false }))
270
+ saveRemove(masked)
257
271
  }
258
272
 
259
273
  const restore = (masked) => {
@@ -319,16 +333,21 @@ export function factory(require) {
319
333
  react.createElement('thead', null,
320
334
  react.createElement('tr', null,
321
335
  react.createElement('th', { style: headStyle }, 'Key'),
336
+ react.createElement('th', { style: headStyle }, 'Usage'),
322
337
  react.createElement('th', { style: headStyle }, 'Saved'),
323
338
  react.createElement('th', { style: headStyle }, 'Actions')
324
339
  )
325
340
  ),
326
341
  react.createElement('tbody', null,
327
- (server !== null ? server.keys : []).map((key) => {
342
+ (server !== null ? server.keys : []).map((key, index) => {
328
343
  const masked = key.masked
329
344
  const isRemoved = removing[masked] === true
330
345
  const isReplacing = replacing[masked] === true
331
346
  const isRevealed = typeof revealed[masked] === 'string'
347
+ const usageRow = usage !== null && usage.ok === true ? usage.perKey[index] : null
348
+ const pct = usageRow && usageRow.ok && usageRow.planLimit != null && usageRow.planLimit > 0 && usageRow.planUsage != null
349
+ ? Math.min(100, Math.round((usageRow.planUsage / usageRow.planLimit) * 100))
350
+ : null
332
351
  return react.createElement('tr', { key: masked, style: { borderBottom: '1px solid var(--dsw-alias-border-l2)', opacity: isRemoved ? 0.45 : 1 } },
333
352
  react.createElement('td', { style: Object.assign({}, cellStyle, { fontFamily: 'var(--ds-font-family-code, monospace)', fontSize: 12, wordBreak: 'break-all' }) },
334
353
  isReplacing
@@ -347,11 +366,14 @@ export function factory(require) {
347
366
  })
348
367
  )
349
368
  ),
369
+ react.createElement('td', { style: cellStyle },
370
+ react.createElement(UsageCircle, { percent: pct, label: pct != null ? pct + '%' : (usageRow && usageRow.ok && usageRow.planUsage != null ? String(usageRow.planUsage) : '—') })
371
+ ),
350
372
  react.createElement('td', { style: Object.assign({}, cellStyle, { color: 'var(--dsw-alias-label-tertiary)', whiteSpace: 'nowrap' }) }, formatDate(key.savedAt)),
351
373
  react.createElement('td', { style: cellStyle },
352
374
  isReplacing
353
375
  ? react.createElement('span', { style: { display: 'inline-flex', gap: 2 } },
354
- react.createElement(IconButton, { icon: 'check', title: 'Keep', onClick: () => { setReplacing((current) => Object.assign({}, current, { [masked]: false })) }, disabled: busy }),
376
+ react.createElement(IconButton, { icon: 'check', title: 'Save key', onClick: () => saveReplace(masked), disabled: busy || (typeof replaceDrafts[masked] === 'string' ? replaceDrafts[masked].trim().length === 0 : true) }),
355
377
  react.createElement(IconButton, { icon: 'close', title: 'Cancel', onClick: () => cancelReplace(masked), disabled: busy })
356
378
  )
357
379
  : react.createElement('span', { style: { display: 'inline-flex', gap: 2 } },
@@ -381,6 +403,7 @@ export function factory(require) {
381
403
  })
382
404
  ),
383
405
  react.createElement('td', { style: cellStyle }, '—'),
406
+ react.createElement('td', { style: cellStyle }, '—'),
384
407
  react.createElement('td', { style: cellStyle },
385
408
  react.createElement('span', { style: { display: 'inline-flex', gap: 2 } },
386
409
  react.createElement(IconButton, { icon: 'check', title: 'Save key', onClick: () => saveAdd(item), disabled: busy || item.value.trim().length === 0 }),
@@ -390,63 +413,27 @@ export function factory(require) {
390
413
  ))
391
414
  )
392
415
  ),
393
- react.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
394
- react.createElement('button', { type: 'button', style: btn, onClick: () => setAdds((current) => [...current, { id: 'add-' + Date.now() + '-' + Math.random().toString(36).slice(2, 7), value: '' }]), disabled: busy }, '+ Add key')
416
+ react.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' } },
417
+ react.createElement('button', { type: 'button', style: btn, onClick: () => setAdds((current) => [...current, { id: 'add-' + Date.now() + '-' + Math.random().toString(36).slice(2, 7), value: '' }]), disabled: busy }, '+ Add key'),
418
+ react.createElement('button', { type: 'button', style: btn, onClick: refresh, disabled: busy }, 'Refresh usage')
395
419
  ),
396
- react.createElement('div', { style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 12, padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 8 } },
397
- react.createElement('label', { style: { fontWeight: 500, fontSize: 13 } }, 'Key usage strategy'),
398
- react.createElement('select', {
399
- style: { maxWidth: 280, height: 32, border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, background: 'var(--dsw-alias-bg-layer-1)', color: 'var(--dsw-alias-label-primary)', padding: '0 10px', fontSize: 13 },
400
- value: strategy,
401
- onChange: (event) => saveStrategy(event.target.value),
402
- disabled: busy
403
- }, STRATEGIES.map((option) => react.createElement('option', { key: option.id, value: option.id }, option.label))),
404
- react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
405
- (STRATEGIES.find((option) => option.id === strategy) || STRATEGIES[0]).hint + ' The strategy is saved immediately when you select it; the first key then becomes primary for web_search.'
406
- )
407
- ),
408
- react.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
409
- react.createElement('button', { type: 'button', style: Object.assign({}, btn, { height: 32, background: 'var(--dsw-alias-button-primary-fill)', color: 'var(--dsw-alias-label-primary-foreground)' }), onClick: save, disabled: busy || !hasPending }, 'Save key changes'),
410
- 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))
411
- ),
412
- react.createElement('div', { style: { border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 12, padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 8 } },
413
- react.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, justifyContent: 'space-between' } },
414
- react.createElement('span', { style: { fontWeight: 500, fontSize: 13 } }, 'Tavily Usage'),
415
- react.createElement('button', { type: 'button', style: btn, onClick: refresh, disabled: busy }, 'Refresh')
420
+ react.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 6 } },
421
+ react.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } },
422
+ react.createElement('label', { style: { fontWeight: 500, fontSize: 13 } }, 'Key usage strategy'),
423
+ react.createElement('select', {
424
+ style: { maxWidth: 280, height: 32, border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, background: 'var(--dsw-alias-bg-layer-1)', color: 'var(--dsw-alias-label-primary)', padding: '0 10px', fontSize: 13 },
425
+ value: strategy,
426
+ onChange: (event) => saveStrategy(event.target.value),
427
+ disabled: busy
428
+ }, STRATEGIES.map((option) => react.createElement('option', { key: option.id, value: option.id }, option.label))),
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))
416
430
  ),
417
- usageError !== null && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usageError)),
418
- usage !== null && usage.ok === false && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usage.error)),
419
- usage !== null && usage.ok === true && react.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8 } },
420
- react.createElement('div', { style: { fontSize: 13 } },
421
- usage.totals.keys + ' key(s) (' + usage.totals.okKeys + ' readable)' + (usage.totals.planLimit !== null ? ' · plan usage ' + usage.totals.planUsage + ' / ' + usage.totals.planLimit : '')
422
- ),
423
- react.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } },
424
- react.createElement('thead', null,
425
- react.createElement('tr', null,
426
- react.createElement('th', { style: headStyle }, 'API Key'),
427
- react.createElement('th', { style: headStyle }, 'Usage')
428
- )
429
- ),
430
- react.createElement('tbody', null,
431
- (server !== null ? server.keys : []).map((key, index) => {
432
- const row = usage.perKey[index]
433
- const pct = row && row.ok && row.planLimit != null && row.planLimit > 0 && row.planUsage != null
434
- ? Math.min(100, Math.round((row.planUsage / row.planLimit) * 100))
435
- : null
436
- return react.createElement('tr', { key: key.masked, style: { borderBottom: '1px solid var(--dsw-alias-border-l2)' } },
437
- react.createElement('td', { style: Object.assign({}, cellStyle, { fontFamily: 'var(--ds-font-family-code, monospace)', fontSize: 12, wordBreak: 'break-all' }) }, key.masked),
438
- react.createElement('td', { style: cellStyle },
439
- react.createElement(UsageCircle, { percent: pct, label: pct != null ? pct + '%' : (row && row.ok && row.planUsage != null ? String(row.planUsage) : '—') })
440
- )
441
- )
442
- })
443
- )
444
- )
431
+ react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
432
+ (STRATEGIES.find((option) => option.id === strategy) || STRATEGIES[0]).hint + ' Saved immediately when selected; the first key then becomes primary for web_search.'
445
433
  )
446
434
  ),
447
- react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
448
- 'Usage data comes from the Tavily /usage endpoint (fetched locally; no keys are exposed).'
449
- )
435
+ usageError !== null && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usageError)),
436
+ usage !== null && usage.ok === false && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usage.error))
450
437
  )
451
438
  )
452
439
  )