@moguiyu/dsh-tavily 0.2.8 → 0.3.1

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/README.md CHANGED
@@ -1,32 +1,32 @@
1
1
  # @moguiyu/dsh-tavily
2
2
 
3
- Combined plugin: the opt-in advanced Tavily model tools (`tavily_search`, `tavily_extract`,
4
- `tavily_map`, `tavily_crawl`), the **Tavily Search** settings card, and the local HTTP backend
5
- for key/usage management.
3
+ Everything in one package: the advanced Tavily model tools (`tavily_search`, `tavily_extract`,
4
+ `tavily_map`, `tavily_crawl`), the key/usage HTTP routes, and the browser card on the **Plugins**
5
+ page.
6
6
 
7
7
  - **`tavily_search`** — full Tavily surface (`max_results`, `search_depth`, `topic`, `days`,
8
- `include_answer`, `include_raw_content`, `include_domains`, `exclude_domains`). Opt-in and
9
- off by default.
8
+ `include_answer`, `include_raw_content`, `include_domains`, `exclude_domains`).
10
9
  - **`tavily_extract` / `tavily_map` / `tavily_crawl`** — read a known URL's content, map a
11
10
  site's links, or crawl a site and return its pages. Same key-rotation/failover as search.
12
- - **Settings card** — key list, usage gauge, strategy selector, and the advanced-tool switch.
13
- - **Plugin-config settings card** — the Host installs the `tavily-search` settings section
14
- and the card is keyed by it (keyed `settings.plugin.item`), so the Plugins configuration tab
15
- serves the card exactly when this plugin is composed (needs DSH **0.1.0-rc.7 or newer**,
16
- including `0.1.2-alpha.x`, the `0.1.3` line, and the `0.1.5`/current `0.1.6` lines — both seam
17
- generations are detected at runtime).
18
- The switch writes the namespace and restarts the row; the choice is mirrored to
19
- `~/.dsh/tavily-tool.json`.
11
+ - **Card** — key list, usage gauge, and the strategy selector. It registers into the Plugins
12
+ page's `plugins.bundle.config` slot, keyed `@moguiyu/dsh-tavily`, and needs DSH
13
+ **0.1.6-alpha.2 or newer** — the release that retired `settings.plugin.item` and moved plugin
14
+ configuration off the Settings page. On older lines the tools and routes work; there is simply
15
+ no card.
20
16
  - **`web_search` is never replaced** — no `ctx.web` provider is registered and
21
- `web.searchProvider` is never rewritten; the built-in DeepSeek `web_search` keeps its
22
- native provider and schema. Tavily is an extra, opt-in search option.
17
+ `web.searchProvider` is never rewritten; the built-in `web_search` keeps its native provider
18
+ and schema.
23
19
 
24
- Host half: plain ESM (`src/index.js`). Client half: prebuilt `window.__ModuleLoader__` bundle
25
- at `lib/client.js`, generated from `src/client.js` via `pnpm build`.
20
+ Host half: plain ESM (`src/index.js`, `src/tools.js`, `src/backend.js`, `src/lib.js`). Client
21
+ half: prebuilt `window.__ModuleLoader__` bundle at `lib/client.js`, generated from
22
+ `src/client.js` via `pnpm build`.
26
23
 
27
- The host half composes the two standalone packages instead of carrying a second copy of the
28
- implementation: the tool comes from [`@moguiyu/dsh-tool-tavily-search`](../dsh-tool-tavily-search)
29
- and the routes/switch pipeline from [`@moguiyu/dsh-tavily-backend`](../dsh-tavily-backend).
30
- Both are regular dependencies, installed automatically with this package.
24
+ Earlier releases split this across `@moguiyu/dsh-tool-tavily-search` and
25
+ `@moguiyu/dsh-tavily-backend`, which this package imported as regular dependencies. From 0.3.0
26
+ both are folded in here: one package means no dependency range can drift behind the contract,
27
+ which is exactly how the old tool-level switch broke. Both old packages are deprecated.
28
+
29
+ There is no tool-level on/off switch. Composing the plugin registers the tools, and the Plugins
30
+ page's own bundle toggle is the only on/off.
31
31
 
32
32
  See the [workspace README](../../README.md) for install and configuration.
package/lib/client.js CHANGED
@@ -2,7 +2,6 @@ window.__ModuleLoader__.load({
2
2
  id: "@moguiyu/dsh-tavily",
3
3
  factory: function factory(require) {
4
4
  const react = require('react')
5
- const _primitives = require('@deepseek-ai/dsh-client-ui-primitives')
6
5
 
7
6
  const inject = ['slots']
8
7
 
@@ -52,18 +51,6 @@ window.__ModuleLoader__.load({
52
51
  }, react.createElement(SvgIcon, { name: icon, size: 13 }))
53
52
  }
54
53
 
55
- function Switch({ checked, disabled, label, onChange }) {
56
- return react.createElement('button', {
57
- type: 'button',
58
- role: 'switch',
59
- 'aria-checked': checked === true,
60
- 'aria-label': label,
61
- disabled: disabled || checked === null,
62
- onClick: onChange,
63
- style: { width: 36, height: 20, borderRadius: 10, border: '1px solid var(--dsw-alias-border-l2)', background: checked === true ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-bg-layer-1)', cursor: 'pointer', padding: 0, position: 'relative', flex: 'none', opacity: disabled ? 0.5 : 1 }
64
- }, react.createElement('span', { style: { position: 'absolute', top: 2, left: checked === true ? 18 : 2, width: 14, height: 14, borderRadius: 7, background: '#fff', transition: 'left .12s ease' } }))
65
- }
66
-
67
54
  function UsageCircle({ percent, size, label, onClick }) {
68
55
  const radius = 15
69
56
  const circumference = 2 * Math.PI * radius
@@ -79,8 +66,6 @@ window.__ModuleLoader__.load({
79
66
  }
80
67
 
81
68
  function TavilySettingsCard() {
82
- const [enabled, setEnabled] = react.useState(null)
83
- const [expanded, setExpanded] = react.useState(false)
84
69
  const [server, setServer] = react.useState(null)
85
70
  const [loadError, setLoadError] = react.useState(null)
86
71
  const [strategy, setStrategy] = react.useState('rotate')
@@ -96,13 +81,6 @@ window.__ModuleLoader__.load({
96
81
  const [usageError, setUsageError] = react.useState(null)
97
82
 
98
83
  const refresh = react.useCallback(async () => {
99
- try {
100
- const response = await fetch('/api/tavily-tool', { cache: 'no-store' })
101
- const data = await response.json()
102
- if (data.ok) { setEnabled(data.enabled); setLoadError(null) } else { setLoadError(data.error || 'Failed to load settings') }
103
- } catch (error) {
104
- setLoadError(String(error && error.message ? error.message : error))
105
- }
106
84
  try {
107
85
  const response = await fetch('/api/tavily-manager', { cache: 'no-store' })
108
86
  const data = await response.json()
@@ -122,27 +100,6 @@ window.__ModuleLoader__.load({
122
100
 
123
101
  react.useEffect(() => { refresh() }, [refresh])
124
102
 
125
- const toggleEnabled = async () => {
126
- const next = enabled !== true
127
- setBusy(true)
128
- setNotice(null)
129
- try {
130
- const response = await fetch('/api/tavily-tool', {
131
- method: 'POST',
132
- headers: { 'content-type': 'application/json' },
133
- body: JSON.stringify({ enabled: next })
134
- })
135
- const data = await response.json()
136
- if (!data.ok) { setNotice({ error: data.error || 'Switch failed' }); return }
137
- setEnabled(data.enabled)
138
- setNotice({ ok: data.enabled ? 'Advanced tavily_search tool enabled.' : 'Advanced tavily_search tool disabled — built-in web_search is unchanged.' })
139
- } catch (error) {
140
- setNotice({ error: String(error && error.message ? error.message : error) })
141
- } finally {
142
- setBusy(false)
143
- }
144
- }
145
-
146
103
  const saveStrategy = async (next) => {
147
104
  setBusy(true)
148
105
  setNotice(null)
@@ -283,49 +240,19 @@ window.__ModuleLoader__.load({
283
240
  const headStyle = { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid var(--dsw-alias-border-l2)', fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' }
284
241
  const cellStyle = { padding: '8px', verticalAlign: 'top' }
285
242
 
286
- const isOff = enabled === false
287
-
288
- // Card box mirrors the native plugin-config card (PluginCard.module.css,
289
- // ui-settings-plugins): bg-layer-3 fill, .5px border-l4, 16px radius,
290
- // hover/open border lift, and the open state's bg-layer-2 upgrade — so
291
- // the card sits at the same elevation as native cards in dark mode too.
292
- return react.createElement('div', { className: expanded ? 'dts-card dts-card-open' : 'dts-card', style: { minWidth: 0 } },
293
- react.createElement('style', null, '.dts-card{border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;background:var(--dsw-alias-bg-layer-3);transition:border-color .16s,background .16s}.dts-card:hover{border-color:var(--dsw-alias-label-dimmed)}.dts-card-open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.dts-icon-btn:hover{background:var(--dsw-alias-interactive-bg-hover)}.dts-icon-btn:disabled{opacity:.4;cursor:default}.dts-icon-btn-danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger)}'),
294
- react.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12, padding: '12px 14px', minHeight: 52, boxSizing: 'border-box' } },
295
- react.createElement('button', {
296
- type: 'button',
297
- 'aria-expanded': expanded,
298
- onClick: () => setExpanded((current) => !current),
299
- style: { flex: 1, minWidth: 0, textAlign: 'left', background: 'none', border: 'none', padding: 0, cursor: 'pointer', display: 'flex', flexDirection: 'column', gap: 2, font: 'inherit', color: 'inherit' }
300
- },
301
- react.createElement('strong', { style: { fontSize: 14, fontWeight: 600, lineHeight: '20px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, 'Tavily Search'),
302
- react.createElement('span', { style: { fontSize: 12, lineHeight: '16px', color: 'var(--dsw-alias-label-tertiary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, 'Advanced tavily_search tool · key rotation · usage')
303
- ),
304
- react.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6, flex: 'none' } },
305
- react.createElement(Switch, { checked: enabled, disabled: busy, label: 'Advanced tavily_search tool enabled', onChange: toggleEnabled }),
306
- react.createElement('button', {
307
- type: 'button',
308
- className: 'dts-icon-btn',
309
- title: expanded ? 'Collapse' : 'Expand',
310
- 'aria-label': expanded ? 'Collapse' : 'Expand',
311
- onClick: () => setExpanded((current) => !current),
312
- style: { width: 26, height: 26, borderRadius: 6, border: 'none', background: 'transparent', color: 'var(--dsw-alias-label-tertiary)', cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: 0 }
313
- },
314
- react.createElement('span', { style: { display: 'inline-flex', transform: expanded ? 'rotate(180deg)' : undefined, transition: 'transform .14s var(--ds-ease-in-out)' } },
315
- react.createElement(_primitives.IconChevronDownOutline14, { size: 12, 'aria-hidden': 'true' })
316
- )
317
- )
318
- )
319
- ),
320
- expanded && react.createElement('div', { style: { borderTop: '1px solid var(--dsw-alias-border-l2)', padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 16 } },
321
- isOff
322
- ? react.createElement('p', { style: { margin: 0, fontSize: 13, color: 'var(--dsw-alias-label-tertiary)' } },
323
- 'The advanced tavily_search tool is off. The built-in web_search keeps its native provider and is never replaced.'
324
- )
325
- : react.createElement(react.Fragment, null,
243
+ // The Plugins page draws the title, the icon, and the crumb — the
244
+ // `plugins.bundle.config` contract puts the form alone in the entry — so
245
+ // this renders no card chrome, no second heading, and no collapse.
246
+ return react.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 16, minWidth: 0 } },
247
+ react.createElement('style', null, '.dts-icon-btn:hover{background:var(--dsw-alias-interactive-bg-hover)}.dts-icon-btn:disabled{opacity:.4;cursor:default}.dts-icon-btn-danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger)}'),
248
+ react.createElement(react.Fragment, null,
326
249
  react.createElement('p', { style: { margin: 0, fontSize: 13, color: 'var(--dsw-alias-label-tertiary)' } },
327
250
  'All keys are listed below; the green dot marks the first (primary) key. The tavily_search tool uses all keys according to the strategy. Built-in web_search is unaffected.'
328
251
  ),
252
+ server !== null && server.writable !== undefined && server.writable.keys === false &&
253
+ react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
254
+ 'Read-only: these keys come from the environment that launched dsh, so this card cannot change them. Unset TAVILY_API_KEYS / TAVILY_API_KEY in that shell and restart dsh to manage keys here.'
255
+ ),
329
256
  loadError !== null && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(loadError)),
330
257
  react.createElement('table', { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 } },
331
258
  react.createElement('thead', null,
@@ -381,11 +308,15 @@ window.__ModuleLoader__.load({
381
308
  !isRemoved && react.createElement(IconButton, { icon: 'pencil', title: 'Edit', onClick: () => startReplace(masked), disabled: busy }),
382
309
  react.createElement(IconButton, {
383
310
  icon: isRemoved ? 'restore' : (confirm[masked] === true ? 'check' : 'trash'),
384
- title: isRemoved ? 'Restore' : (confirm[masked] === true ? 'Click again to confirm' : 'Delete'),
311
+ // A key the launching environment supplies is read-only, so the
312
+ // server would refuse. Say so on the control instead.
313
+ title: key.removable === false
314
+ ? 'Read-only: this key comes from the environment that launched dsh. Unset it there and restart dsh to change it here.'
315
+ : (isRemoved ? 'Restore' : (confirm[masked] === true ? 'Click again to confirm' : 'Delete')),
385
316
  danger: !isRemoved,
386
317
  className: isRemoved ? 'dts-icon-btn' : 'dts-icon-btn dts-icon-btn-danger',
387
318
  onClick: () => isRemoved ? restore(masked) : markRemoved(masked),
388
- disabled: busy
319
+ disabled: busy || (key.removable === false && !isRemoved)
389
320
  })
390
321
  )
391
322
  )
@@ -434,35 +365,27 @@ window.__ModuleLoader__.load({
434
365
  usageError !== null && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usageError)),
435
366
  usage !== null && usage.ok === false && react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, String(usage.error))
436
367
  )
437
- )
438
368
  )
439
369
  }
440
370
 
441
- // The card the host renders. 0.1.6-alpha.2 asks a bundle-config entry for
442
- // `view: 'page'` only; the legacy keyed settings slot passes no view at all.
371
+ // The entry the Plugins page renders. The contract asks for two views:
372
+ // `summary` for the one-liner under the title, and `page` for the form with
373
+ // its own save control. The page supplies the title, icon, and crumb, so the
374
+ // summary is a plain string and the page view is the form alone — matching
375
+ // how the shipped cards (WebSearchCard and friends) are written.
443
376
  function TavilyCard(props) {
444
- if (props && props.view === 'summary') return react.createElement(TavilySummary, null)
377
+ if (props && props.view === 'summary') {
378
+ return 'Manage Tavily API keys and the key-usage strategy. The built-in web_search is never replaced.'
379
+ }
445
380
  return react.createElement(TavilySettingsCard, null)
446
381
  }
447
382
 
448
- // One-liner for the summary view. Deliberately static and cheap: the page
449
- // draws the title, the icon, and the crumb itself, and never asks this entry
450
- // for anything but `page`.
451
- function TavilySummary() {
452
- return react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
453
- 'Rotating Tavily API keys for the advanced search tools. The built-in web_search keeps its native provider and is never replaced.'
454
- )
455
- }
456
-
457
383
  function apply(ctx) {
458
- // 0.1.6-alpha.2 moved plugin configuration off the Settings page and
459
- // replaced the keyed `settings.plugin.item` slot with the Plugins page's
460
- // `plugins.*` family (`dsh-client-ui-plugin-manager` declares it).
461
- // `slots.inject` fires only for a slot the host actually declares, so
462
- // injecting both IS the feature detection: exactly one mounts per harness
463
- // line, and neither throws on the other. Keep both -- dropping the retired
464
- // name silently removes the card from rc.7 through 0.1.6-alpha.1, and
465
- // dropping the new one silently removes it from 0.1.6-alpha.2 onward.
384
+ // ONE slot. 0.1.6-alpha.2 moved plugin configuration to the sidebar
385
+ // Plugins page and retired `settings.plugin.item`. This package no longer
386
+ // registers a settings namespace, so the retired keyed slot has no key it
387
+ // could claim. On rc.7 through 0.1.6-alpha.1 the tools and routes work but
388
+ // this card has no host — see `docs/agents/plugin-design.md`.
466
389
  ctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({
467
390
  name: 'plugins.bundle.config',
468
391
  // Keyed by the bundle's package name. The Plugins page renders this on
@@ -470,18 +393,6 @@ window.__ModuleLoader__.load({
470
393
  key: '@moguiyu/dsh-tavily',
471
394
  id: '@moguiyu/dsh-tavily'
472
395
  }, TavilyCard))
473
-
474
- ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({
475
- name: 'settings.plugin.item',
476
- // rc.7 through 0.1.6-alpha.1: keyed by the settings namespace the Host
477
- // serves (`tavily-search`); the Plugins configuration tab dispatches it
478
- // when the namespace is present. The id/order/label props keep rc.6
479
- // list-slot deployments rendering the card as well.
480
- key: 'tavily-search',
481
- id: 'tavily-search',
482
- order: 30,
483
- label: 'Tavily Search'
484
- }, TavilyCard))
485
396
  }
486
397
 
487
398
  return { apply, inject }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@moguiyu/dsh-tavily",
3
- "version": "0.2.8",
4
- "description": "Opt-in advanced Tavily model tools for DeepSeek Harness with the Tavily Search settings card (rc.7/rc.8 and the 0.1.2-0.1.6 plugin management lines). The built-in web_search is never replaced.",
3
+ "version": "0.3.1",
4
+ "description": "Advanced Tavily model tools for DeepSeek Harness (tavily_search, tavily_extract, tavily_map, tavily_crawl) with a key and usage management card on the Plugins page. The built-in web_search is never replaced.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "exports": {
@@ -43,8 +43,6 @@
43
43
  "client": {
44
44
  "platform": "web",
45
45
  "inject": [
46
- "@deepseek-ai/dsh-client-ui-settings",
47
- "@deepseek-ai/dsh-client-ui-settings-plugins",
48
46
  "@deepseek-ai/dsh-client-ui-plugin-manager"
49
47
  ]
50
48
  }
@@ -68,16 +66,9 @@
68
66
  "peerDependencies": {
69
67
  "@deepseek-ai/cordis": "^4.0.1",
70
68
  "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.3-alpha.1 || ^0.1.5-0 || ^0.1.6-0",
71
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.3-alpha.1 || ^0.1.5-0 || ^0.1.6-0",
72
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.3-alpha.1 || ^0.1.5-0 || ^0.1.6-0"
73
- },
74
- "dependencies": {
75
- "@deepseek-ai/schemastery": "^3.18.2",
76
- "@moguiyu/dsh-tavily-backend": "^0.2.1",
77
- "@moguiyu/dsh-tool-tavily-search": "^0.2.1"
69
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.3-alpha.1 || ^0.1.5-0 || ^0.1.6-0"
78
70
  },
79
71
  "devDependencies": {
80
- "@deepseek-ai/dsh-settings": "^0.1.2-alpha.2",
81
72
  "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.2"
82
73
  }
83
74
  }