# dsh-wigolo

[English](./README.md)

**[DSH](https://github.com/deepseek-ai/dsh)（DeepSeek Harness）的私有部署联网搜索方案。** 本插件将自建的 [wigolo](https://github.com/KnockOutEZ/wigolo) 元搜索 daemon 集成到 DSH，用你自己的搜索基础设施替代内置的联网搜索——完全掌控搜索引擎、缓存策略和数据隐私。

> **核心定位：** 代理并替代 DSH 内置的 `web_search` / `web_fetch`，将 agent 的所有联网搜索请求转发到私有部署的 wigolo daemon。一个开关即可切换——无云依赖、无 API Key、零查询成本。

```
dsh web GUI ── 侧边栏面板 ── /api/dsh-wigolo/* ──┐
                                                  │
agent 工具 (wigolo_search, …) ── MCP streamable-http ──► wigolo daemon
web seam (web_search / web_fetch) ────────────────┘   18+ 引擎 · RRF 融合 · 本地缓存
```

## 功能

- **七个 agent 工具** —— `wigolo_search`、`wigolo_crawl`、`wigolo_extract`、`wigolo_research`、`wigolo_find_similar`、`wigolo_cache`、`wigolo_watch`，每个都基于 daemon 真实 schema 裁剪出模型高频参数面。写操作归 agent，只读缓存/监控浏览由默认关闭的「Wigolo 缓存」tab 承载。
- **接管可配置** —— 官方 `web_search` / `web_fetch` 是否走 wigolo，单开关切换（开 = 两者都由 wigolo 驱动，关 = 官方提供商），GUI 一键切换并自动管理 cordis 路由。
- **官方设置集成** —— `enabled`、`announceToAgent`、`guidance` 覆盖项位于官方 DSH 设置 UI（dsh-ssh 模式）。热更新：改设置无需重启。
- **侧边栏面板**（React，中英文双语）—— 四个 Tab：连接配置（实时测试 + 延迟）、接管与工具、关于、使用指南（面板内渲染 GUIDE.zh.md）。连接/token/工具开关/缓存 tab 开关，全部回环栅栏保护。token 可直接在面板粘贴写入（无需终端）。
- **热重配** —— 连接/token 修改即时生效（MCP 客户端热重配），仅接管开关和工具暴露变更需重启。
- **Fail-loud 配置校验** —— 未知配置键触发警告并附带「你是不是想说……」提示（编辑距离匹配），不会默默忽略。
- **密钥安全** —— token 独立存于 0600 文件，永不进配置 JSON，永不回传浏览器。
- **缓存时间时区转换** —— wigolo daemon 以无时区 UTC 存储时间戳，插件自动按配置的时区（`local`、数字偏移如 `+8`、或 IANA 名称如 `Asia/Shanghai`）转换，`wigolo_cache` 结果直接显示本地时间。

## 前置条件

一个运行中的 [wigolo daemon](https://knockoutez.github.io/wigolo/docs/)（v0.2+），HTTP + Bearer token 可达。本机（`127.0.0.1:3333`）是默认值，零额外配置。

## 安装

### 从 npm 安装

```bash
dsh plugin --profile web add @tianjiqx/dsh-wigolo
```

### 从 GitHub 安装

```bash
dsh plugin --profile web add github:tianjiqx/dsh-wigolo
```

两种方式都会安装插件并自动注册到 profile 的 bundle 列表（通过插件自带的 `cordis.patch.yml`），无需手动编辑。

### 本地开发（link 模式）

```bash
git clone https://github.com/tianjiqx/dsh-wigolo.git
cd dsh-wigolo
pnpm install
pnpm build
dsh plugin --profile web add link:$PWD
```

这会克隆仓库、构建插件，并通过 `link:` 方式自动注册到 profile 的 bundle 列表（无需手动编辑 `cordis.patch.yml`）。

### 安装后配置

把 daemon token 写入 `~/.dsh/wigolo-token`（首行，权限 0600）：

```bash
echo "YOUR_TOKEN" > ~/.dsh/wigolo-token && chmod 600 ~/.dsh/wigolo-token
```

重启 dsh，打开侧边栏 **Wigolo** 入口，点 **Test connection** 验证。

## 卸载

```bash
dsh plugin --profile web remove @tianjiqx/dsh-wigolo
```

这会移除插件及其 bundle 注册。token 文件 `~/.dsh/wigolo-token` 和配置 `~/.dsh/wigolo.json` 会保留（如需要可手动删除）。

详细使用场景和示例见 [使用指南](./GUIDE.zh.md)。

## 配置

### 界面配置（推荐）

所有设置都可以通过 **Wigolo 侧边栏面板** 完成（点击侧边栏的 Wigolo 图标）：

- **连接配置 Tab**：Host、端口、token、hostHeader、测试连接
- **接管与工具 Tab**：接管开关、工具启用/禁用、缓存 Tab 开关
- **关于 Tab**：版本信息、文档链接

修改即时生效（热重载），仅接管开关需要重启。

### 手动配置文件

对于界面未暴露的高级设置（如工具默认参数、超时覆盖），直接编辑 `~/.dsh/wigolo.json`：

```jsonc
{
  "version": 2,
  "connection": {
    "host": "127.0.0.1",       // daemon 地址
    "port": 3333,
    "hostHeader": "auto",       // auto | none | "<字面值>"
    "tokenFile": ""             // "" = ~/.dsh/wigolo-token
  },
  "takeover": false,            // true = wigolo 驱动 web_search + web_fetch；false = 官方提供商（默认）
  "tools": {
    "wigolo_search":  { "enabled": true,  "defaults": { "max_results": 10, "search_depth": "balanced" } },
    "wigolo_crawl":   { "enabled": true,  "defaults": { "max_pages": 50 }, "timeoutMs": 300000 },
    "wigolo_extract": { "enabled": true },
    "wigolo_research":{ "enabled": true,  "timeoutMs": 600000 },
    "wigolo_find_similar": { "enabled": false },
    "wigolo_cache":   { "enabled": true },
    "wigolo_watch":   { "enabled": true }
  },
  "cacheTab": { "enabled": false },   // 「Wigolo 缓存」只读 GUI tab（默认关；即时生效）
  "announceToAgent": true,
  "timezone": "local"               // 缓存时间戳时区："local" | "+8" | "-5" | "+5.5" | "Asia/Shanghai"
}
```

各工具的 `defaults` 合并在**模型显式参数之下**（模型始终优先）；`timeoutMs` 覆盖内置超时预算。

### 接管开关

`takeover` 是一个布尔开关：

| 值 | `web_search` | `web_fetch` | 说明 |
|------|--------------|--------------|------|
| `true` | wigolo | wigolo | 完全替代 |
| `false` | 官方 | 官方 | 仅 wigolo_* 工具（默认） |

为什么要开关：当多个 provider 同时注册进 web seam 且无显式路由时，`web_search` 会以 `WEB_PROVIDER_AMBIGUOUS` 失败。开启（`true`）时会向 `~/.dsh/cordis.patch.yml` 写入自管理块（`searchProvider: wigolo`，dsh-skin 式托管标记）；关闭（`false`）移除该块且不注册 provider，与官方 provider 安全共存。路由变更需**重启 dsh**——面板会提示。

### 时区配置

wigolo daemon 以无时区 UTC 格式（`"YYYY-MM-DD HH:MM:SS"`）持久化缓存时间戳。插件在返回给 agent 或渲染到 UI 之前，会按配置的时区进行转换。

| 值 | 示例 | 说明 |
|----|------|------|
| `"local"` | `"local"` | 使用 DSH 主机的系统时区（默认） |
| 数字偏移 | `"+8"`、`"-5"`、`"+5.5"` | 固定 UTC 偏移量；支持半小时时区（如印度 +5:30） |
| IANA 名称 | `"Asia/Shanghai"`、`"America/New_York"` | 完整时区规则，自动处理夏令时 |

修改 `timezone` 后需重启 dsh 生效。

### Host 头技术说明

wigolo 的 DNS-rebinding 防护会对 `Host` 值做白名单：`localhost`、回环字面量、以及自身 bind host。两个推论：

- 绑定 `0.0.0.0` 的局域网 daemon 接受 `Host: 0.0.0.0` 的请求。
- `fetch()` 按规范禁止设置 `Host`，插件因此使用 `node:http`（允许自定义）。

`hostHeader: "auto"`（默认）对本地 daemon **不发送**自定义头，对远程 daemon 使用 bind-host 技巧。仅当你的部署确有需要时才设字面值。

## Agent 工具

| 工具 | wigolo 能力 | 亮点 | 超时 |
|------|------------|------|------|
| `wigolo_search` | search | 分类 / 时间范围 / 域名过滤 / 深度档 / `"a \| b"` 多变体查询 | 60s |
| `wigolo_crawl` | crawl | 站点爬取（patterns / 策略 / 页数上限），每页入本地缓存 | 300s |
| `wigolo_extract` | extract | CSS 选择器或字段 schema 的结构化提取 | 60s |
| `wigolo_research` | research | 分解子查询、并行搜索、合成带引用的报告 | 600s |
| `wigolo_find_similar` | find_similar | 从 URL 或概念找相关内容（默认关） | 120s |
| `wigolo_cache` | cache | 触网**之前**先查本地缓存；stats / clear | 30s |
| `wigolo_watch` | watch | 持久 URL 变化监控；配合定时 agent 任务实现通知 | 120s |

所有超时均可通过配置中的 `timeoutMs` 按工具调整。

## 安全说明

- 面板路由**仅限回环**（远端地址 + Host + `sec-fetch-site` + origin 四重校验）——它们读写私有配置，绝不能暴露给 LAN 部署。
- token 存于 `~/.dsh/wigolo-token`（0600）；API 响应永不包含它。
- `wigolo_cache clear` 是破坏性操作；工具描述要求模型先确认。

## 开发

```bash
pnpm install
pnpm test          # vitest（61 个测试）
pnpm typecheck     # tsc --noEmit
pnpm build         # lib/index.mjs + lib/client.js（CSS 已内联）
node test/smoke-real-daemon.mjs   # 对真实 daemon 手动冒烟
```

## 许可

Apache-2.0