@someok/pi-deepseek-web-tools
pi 扩展:DeepSeek 网页搜索(ds_search)与网页抓取(ds_fetch),GitHub 链接自动 clone 仓库并渲染 PR/Issue
Package details
Install @someok/pi-deepseek-web-tools from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@someok/pi-deepseek-web-tools- Package
@someok/pi-deepseek-web-tools- Version
0.3.1- Published
- Nov 4, 2026
- Downloads
- 691/mo · 76/wk
- Author
- someokpub
- License
- MIT
- Types
- extension
- Size
- 133.4 KB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
],
"changelogPath": "CHANGELOG.md"
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-deepseek-web-tools
English | 简体中文
Two tools for pi: ds_search (web search) and ds_fetch (fetching). Ordinary pages go through the official DeepSeek Search / Fetch API. GitHub URLs take a different path: repositories are cloned locally so the model can read real source files, and PRs/issues are rendered into a structured document with the local gh CLI.
With a complete configuration both tools are available. Without a DeepSeek key, ds_search and ordinary page fetching are unavailable, but the GitHub half of ds_fetch keeps working.
Install
# Install from npm (recommended); loads on every start
pi install npm:@someok/pi-deepseek-web-tools
# Try once without writing settings
pi -e npm:@someok/pi-deepseek-web-tools
# Load from a local checkout while developing
pi -e ./
If you previously installed a local path with pi install ./, find that entry with pi list and remove it (pi remove <local path>) before installing the npm package. Two entry points load the same extension twice and pi fails with a tool name conflict.
Configuration
Both config files share the same structure; project level wins.
| Level | Path |
|---|---|
| Project | <project>/.pi/deepseek-web-tools.json |
| User | ~/.pi/agent/deepseek-web-tools.json (follows $PI_CODING_AGENT_DIR when set) |
| Environment | DEEPSEEK_API_KEY |
Project config is read only when the project is trusted, matching how pi handles .pi/.
Full example (also in deepseek-web-tools.example.json):
{
"enabled": true,
"apiKey": "sk-...",
"baseUrl": "https://api.deepseek.com",
"exposure": {
"search": "codemode",
"fetch": "direct"
},
"search": {
"timeoutMs": 30000,
"count": 10,
"needContent": false
},
"fetch": {
"timeoutMs": 90000
},
"github": {
"enabled": true,
"prIssue": true,
"maxRepoSizeMB": 350,
"cloneTimeoutSeconds": 30,
"clonePath": "~/pi-github-repos"
}
}
Merge rules: each field resolves project → user → default; apiKey additionally falls back to DEEPSEEK_API_KEY.
| Field | Default | Notes |
|---|---|---|
enabled |
enabled when an apiKey resolves | false forces the tools off, even if the env var has a key |
apiKey |
none | a blank string counts as missing |
baseUrl |
https://api.deepseek.com |
trailing slashes are stripped |
exposure |
{ "search": "codemode", "fetch": "direct" } |
per-tool declaration mode; codemode hides the tool so only scripts can call it, direct declares it to the model. A bare string ("direct" / "codemode") applies to both tools |
search.timeoutMs |
30000 |
1000–300000 |
search.count |
not sent (API default 10) | 1–50 |
search.needContent |
false |
include page body in search results |
fetch.timeoutMs |
90000 |
1000–300000 |
github.enabled |
true |
when false, ds_fetch stops using the GitHub-specific paths |
github.prIssue |
true |
render PRs/issues into a structured document with gh |
github.maxRepoSizeMB |
350 |
larger repos use the lightweight GitHub API view instead of a clone |
github.cloneTimeoutSeconds |
30 |
1–600; on timeout the whole git process group is killed |
github.clonePath |
pi-github-repos under the system temp dir |
~ and $HOME style paths are expanded |
A broken config (invalid JSON, wrong type, typo) never crashes the extension: the offending field is ignored with a warning shown at startup and in /ds-tool-config. Run /reload after editing.
Tools
ds_search
| Parameter | Required | Notes |
|---|---|---|
query |
yes | 1–128 characters; supports site:, -site:, "exact phrase", lang:, after:, before: |
count |
no | 1–50; falls back to search.count |
needContent |
no | include page body; falls back to search.needContent |
Returns a numbered result list (title / URL / site and publish date / summary). Results over 2000 lines or 50KB are truncated, the full JSON is written to a temp file, and its path is appended to the text.
ds_fetch
| Parameter | Required | Notes |
|---|---|---|
url |
yes | HTTP/HTTPS URL with a host, no credentials, at most 8192 characters |
Returns the page title plus Markdown body. The body is truncated the same way (2000 lines / 50KB), with the full content written to a temp file.
github.com URLs are routed to the dedicated paths described below.
GitHub URLs
When ds_fetch receives a github.com URL it stops using DeepSeek and dispatches on the URL shape:
| URL | What happens |
|---|---|
github.com/owner/repo |
cloned locally; returns the tree, README, and the local path |
.../tree/main/src |
returns that directory's listing with file sizes |
.../blob/main/src/index.ts |
returns the file contents; binary files only report type and size |
.../pull/123, .../issues/7 |
fetched with gh and rendered with state, branch, checks, reviews, files, commits, and comments |
repos over maxRepoSizeMB |
fall back to the lightweight GitHub API view (tree + README) |
.../commit/<sha> |
goes straight to the API instead of cloning for one commit |
Clones are shallow and single-branch (--depth 1 --single-branch), and repeat fetches of the same repo reuse the local directory.
Dependencies: git is required; gh is optional. Install it for full PR/issue views and private repositories. Without gh, PRs and issues fall back to ordinary page fetching. This path never spends DeepSeek credits.
How output is displayed
- Tool rows are collapsed by default and show only the result count or character count. Expanding them shows the full text the tool returned — no second truncation by line count.
/ds-searchand/ds-fetchentries are collapsed too. Expand withCtrl+Oor a mouse click; fetched pages render as Markdown (headings, lists, code blocks, links).- The DeepSeek Fetch API escapes every punctuation character in the body (
\#,\-,\,,\/). The extension unescapes it before rendering or handing it to the model, so no backslashes show up. LaTeX parsing is disabled, so(a 100K context is enough)is not treated as a formula. - The only hard limit is 2000 lines / 50KB (pi's convention for model-facing content). When exceeded, the body ends with
[output truncated: … full content: /tmp/…]; ask the model toreadthat file for the rest.
Commands
| Command | Purpose |
|---|---|
/ds-search <query> |
Run one search manually |
/ds-fetch <url> |
Fetch one page; the <https://...> paste form works |
/ds-tool-config |
Show the effective config: enabled state, key source (masked), timeouts, and whether each config file was read |
Only ds- prefixed commands are registered. Bare /search and /fetch are avoided because other extensions (such as pi-web-access) already use them, and pi renames duplicates with :1 / :2 suffixes, so you cannot tell which one you get.
Command results are written as custom entries: they render in the UI but never enter the model context, so the model cannot see what you fetched manually. They are stored in the session and remain visible after resuming.
Errors
| Status | Meaning | Hint |
|---|---|---|
| 400 | Invalid parameters | Check query length, count range, URL format |
| 401 | Missing or invalid key | Check the config files or DEEPSEEK_API_KEY |
| 402 | Insufficient balance | Top up first |
| 403 | Fetch blocked by safety policy | Try another site or skip it |
| 404 | Search path missing / Fetch got no body | Check baseUrl; or the page is not extractable |
| 408 / 504 | Timeout | Retry later |
| 413 | Request body too large | URL is too long |
| 429 | QPS limit hit | Retry later |
| 5xx | Server error | Retry later |
Timeouts come from search.timeoutMs / fetch.timeoutMs and abort the request locally. Commands report failures with an error notification; tool calls surface them as failed tool results.
Limits and billing
- Search and Fetch are each limited to 5 QPS per account; Fetch also has per-domain limits.
- Billing is per successful request: 5 CNY per 1000 requests. Searches with no results, fetches without body text, and failed requests are not billed.
- Per DeepSeek, direct third-party calls to Search / Fetch become metered on 2026-11-01.
Development
npm install
npm run check # tsc --noEmit + vitest
- No runtime npm dependencies: ordinary pages use Node's built-in
fetch, GitHub uses built-inchild_processto callgit/gh. - Tests inject a fake
fetchImplor exercise pure functions and never touch the network. src/config.tsis a pure function, so config merging is easy to test on its own.
Publishing to npm
npm login
npm version patch # optional: bump version, commit, tag
npm publish # or click "release" in VS Code's NPM Scripts panel
- The script is named
release, notpublish: npm treatspublishas a post-publish lifecycle hook, so"publish": "npm publish"would publish once and then again from the hook, and the second run fails because the version already exists. prepublishOnlyrunsnpm run checkfirst; a typecheck or test failure blocks the publish.- Rehearse with
npm run release -- --dry-run; nothing is uploaded. publishConfig.accessispublic(scoped packages default to private, which makes the publish fail).- Inspect the tarball with
npm pack --dry-run: currently 14 files —src/, both READMEs, LICENSE, and the example config; no tests or plan docs.
Security
apiKey only appears in the request header sent to DeepSeek. It is never written to tool output, details, structuredContent, or logs. /ds-tool-config shows a mask only (sk-****abcd). If a project config contains a key, add it to .gitignore (this repo already ignores .pi/deepseek-web-tools.json).