dsh-packycode-api 1.1.0 → 1.1.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/CHANGELOG.md +8 -0
- package/README.en.md +16 -2
- package/README.md +16 -2
- package/SECURITY.md +9 -0
- package/lib/client.js +12 -6
- package/lib/index.js +752 -144
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.1
|
|
4
|
+
|
|
5
|
+
- **Claude CLI search through MCP.** CLI-backed Claude turns and the `claude_code` delegation tool now get a temporary, authenticated, loopback HTTP MCP `web_search` tool calling the plugin's selected DeepSeek/GPT backend. Native CLI `WebSearch` is explicitly denied in every permission mode: it makes a separate Claude server-tool request that PackyCode can refuse even when ordinary turns work. The official CLI/system identity is preserved; no vendor headers or identities are forged. Backend selection applies to CLI searches even when DSH routing is set to Off/Do not take over; the DSH routing mode remains independent, and headless profiles need no `web` service. MCP callbacks capture their originating session ID before losing agent context and always call the direct PackyCode provider, never an auto-router fallback.
|
|
6
|
+
- **Bridge boundaries.** Each run has a collision-resistant server/tool name, an independent random capability, and an owner-only temporary MCP config; search API keys stay in the host key ring, not MCP config, argv or results. Only the exact search tool is added to read-only/edit auto-approvals; shell/file privileges, existing MCP configuration, and user/managed deny rules remain unchanged. No `--strict-mcp-config` or wildcard MCP approval. Queries are bounded at 2000 characters, bodies at 16 KiB, sources at 8, concurrency at 2, calls at 8 per run, and each logical search at 60 seconds including retries/key resolution. Tool failures are explicit, sanitized errors, with no automatic backend/native-search fallback. Results are marked as untrusted external content.
|
|
7
|
+
- **Lifetime and redaction fixes.** MCP capability is revoked immediately on cancellation, including while binding/writing config; late filesystem resources remain owned until cleanup completes. Normal/error exits, spawn/stdin failures, startup watchdog, timeout, early iterator close, and plugin unload clean up. Fixed pre-existing runner races where cancellation during credential/binary setup could still spawn a child and returning at the first labeled-key yield left a child running. Streaming now checks nonzero process exits even after a successful-looking result. Redaction handles JSON-escaped keys and credentials/capabilities fragmented across text/thinking deltas. Title generation allocates no MCP server/search grant.
|
|
8
|
+
- The settings card explains CLI backend selection versus DSH routing. Added `scripts/cli-search-check.mjs`: real official CLI + a local mocked Claude gateway verifies actual MCP transport/permissions and absence of native search in all modes; `--live --backend deepseek|gpt` performs one real search through that backend. The mock gateway is a test fixture only, never a replacement for DSH or the production gateway.
|
|
9
|
+
- Validation: complete suite on Node 24 and Node 22.20.0; real CLI 2.1.39 and 2.1.295 both called the built bridge in read-only/edit/full modes, with official identity retained and no Claude native-search child call. Real DeepSeek and GPT searches via the bridge returned 10 and 20 sources; one initial GPT backend attempt failed explicitly, and a later attempt succeeded. Ordinary HTTP models, key rotation/failure classification and secret-free RPC projections passed the live check. Claude main-model responses in the MCP test were mocked (the available stored key has no Claude group); actual Claude upstream/desktop DSH integration is not claimed from these checks.
|
|
10
|
+
|
|
3
11
|
## 1.1.0
|
|
4
12
|
|
|
5
13
|
- **Keys are chosen by their groups.** A colleague's Claude turns kept failing with "PackyCode 的上游拒绝了这次 Claude Code CLI 请求……请更新 CLI" because the active key had no Claude Code group; PackyCode answers such a key with the same "only the official Claude Code CLI" wording as a real client fault. The plugin now reads each key's groups and routes by them:
|
package/README.en.md
CHANGED
|
@@ -44,7 +44,7 @@ Open the desktop application's **Plugins** page, click **Add plugin**, and enter
|
|
|
44
44
|
dsh-packycode-api
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.1.
|
|
47
|
+
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.1.1`.
|
|
48
48
|
|
|
49
49
|
Then **manually restart the desktop application**. Refreshing the page does not reload the plugin inside the Host process.
|
|
50
50
|
|
|
@@ -63,7 +63,7 @@ Choose a model from the `PackyCode` group. `deepseek-flash`, `gpt-6.1-sol`, and
|
|
|
63
63
|
|
|
64
64
|
The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
|
|
65
65
|
|
|
66
|
-
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.
|
|
66
|
+
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.1`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
|
|
67
67
|
|
|
68
68
|
A local checkout can be linked with `dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`.
|
|
69
69
|
|
|
@@ -96,6 +96,18 @@ A profile without a browser can still use the model route. It just has no settin
|
|
|
96
96
|
|
|
97
97
|
PackyCode has no dedicated search endpoint: one search is one model call with a server-side search tool, billed to the keys stored here. An exhausted or rejected key rotates exactly as on the model route.
|
|
98
98
|
|
|
99
|
+
**Claude CLI search.** Both CLI-backed Claude models and the `claude_code` delegation tool use a temporary MCP search tool calling the DeepSeek/GPT backend selected above. Native `WebSearch` is disabled: it sends an additional Claude server-tool subrequest that PackyCode's Claude upstream may reject as an unofficial client even when ordinary turns work.
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
Claude Code CLI → temporary local MCP web_search → selected PackyCode DeepSeek/GPT backend
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- **Backend selection applies to CLI; routing selection only applies to DSH.** Even with Off/Do not take over selected, CLI searches use the selected backend, not DSH's default. A backend change applies to the next search.
|
|
106
|
+
- Ordinary Claude requests still need a Claude-group key. Search needs a plugin-stored key covering DeepSeek or GPT; it can be another key. Failures are returned to the CLI, with no invented success or automatic switch to another backend.
|
|
107
|
+
- Read-only/edit modes auto-approve only this specific read-only MCP search tool, without adding shell or file-write privileges. User/managed denials still apply. CLI `--allowedTools` is an additive auto-approval list, not an exclusive tool whitelist; existing other permissions are preserved.
|
|
108
|
+
- At most 8 searches per invocation, 2 concurrently, 2000 characters per query and 8 sources per result, with a 60-second end-to-end search deadline. Title generation allocates no MCP server or search grant.
|
|
109
|
+
- Reasoning reports `▸ 网页搜索:PackyCode DeepSeek(MCP,原生 WebSearch 已禁用)` (or GPT). The temporary server binds only `127.0.0.1`; its private config contains an independent one-run capability, never a search API key. CLI completion/cancellation/timeout/plugin unload revokes it and removes the private config, leaving existing user MCP configurations untouched.
|
|
110
|
+
|
|
99
111
|
<details>
|
|
100
112
|
<summary>How web search is wired into DSH</summary>
|
|
101
113
|
|
|
@@ -200,6 +212,8 @@ pnpm run check
|
|
|
200
212
|
|
|
201
213
|
`node scripts/live-check.mjs` checks the live gateway through the same route the plugin registers: balance and groups, one answer per HTTP model, a rotation rehearsal, and the failure classification, printing only the key's last four characters. `--with-cli` also runs the three Claude models through the local Claude Code CLI, and `--with-search` runs one search per backend; the default run spends a small amount of quota.
|
|
202
214
|
|
|
215
|
+
`node scripts/cli-search-check.mjs` runs the real CLI against a local test gateway and MCP bridge; Claude responses and search data are mocked by default, with no quota consumed. It checks all three permission modes, native-search exclusion and temporary-resource cleanup. `--live --backend deepseek` or `--live --backend gpt` sends the read-only mode's one search to the real backend (edit/full remain mocked). This does not verify actual Claude upstream turns or a running DSH integration.
|
|
216
|
+
|
|
203
217
|
Read [AGENTS.md](AGENTS.md) before agent-assisted changes or a release. Publishing is the [Publish workflow](.github/workflows/publish.yml), triggered by pushing a `v*` tag. Do not run `npm publish` locally.
|
|
204
218
|
|
|
205
219
|
## License
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
dsh-packycode-api
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.1.
|
|
51
|
+
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.1.1`。
|
|
52
52
|
|
|
53
53
|
完成后**手动重启桌面应用**。仅刷新页面不会重新加载 Host 里的插件。
|
|
54
54
|
|
|
@@ -67,7 +67,7 @@ dsh-packycode-api
|
|
|
67
67
|
|
|
68
68
|
桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
|
|
69
69
|
|
|
70
|
-
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.
|
|
70
|
+
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.1`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
|
|
71
71
|
|
|
72
72
|
本地开发可以用绝对路径:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。
|
|
73
73
|
|
|
@@ -100,6 +100,18 @@ dsh-packycode-api
|
|
|
100
100
|
|
|
101
101
|
PackyCode 没有专用搜索接口:一次搜索就是一次带服务端搜索工具的模型调用,用这里的 Key 计费,额度用尽或 Key 被拒绝时同样切到下一个 Key。
|
|
102
102
|
|
|
103
|
+
**Claude CLI 的搜索。** Claude 模型和 `claude_code` 委派工具都通过临时 MCP 搜索工具使用上表选定的 DeepSeek/GPT 后端,原生 `WebSearch` 被禁用。原因是原生搜索会额外发一个 Claude 服务端搜索子请求,PackyCode 的 Claude 上游可能把它拒为「不是官方 CLI」,即使普通对话能正常工作。新的调用链是:
|
|
104
|
+
|
|
105
|
+
```text
|
|
106
|
+
Claude Code CLI → 本机临时 MCP web_search → 插件选定的 DeepSeek/GPT 后端
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- **后端选择对 CLI 生效;路由选择只影响 DSH。** 即使选了「不接管」,CLI 搜索仍用这里选的后端,不转到 DSH 默认搜索。更改后端后下一次搜索即使用新选择。
|
|
110
|
+
- Claude 普通对话仍需要 Claude 分组;搜索需要存入插件的 Key 覆盖 DeepSeek 或 GPT 分组,可以是另一个 Key。搜索错误会返回给 CLI,不伪造成功、不自动切另一个后端。
|
|
111
|
+
- 只读、编辑模式单独自动允许这个只读 MCP 搜索工具,不增加 Shell 或文件写权限;用户/管理员的拒绝规则仍有效。CLI 的 `--allowedTools` 是自动批准列表,不是可用工具的排他列表,已有其他工具权限保持原样。
|
|
112
|
+
- 每轮最多 8 次搜索,最多同时 2 次;查询不超过 2000 字符,每次最多 8 个来源,整体搜索预算 60 秒。标题生成不启动 MCP 或进行搜索。
|
|
113
|
+
- 思考区会显示 `▸ 网页搜索:PackyCode DeepSeek(MCP,原生 WebSearch 已禁用)`(或 GPT)。临时服务只监听 `127.0.0.1`,配置中仅含本机一次性凭证,不含搜索 API Key;CLI 结束、取消、超时或插件卸载时关闭并清理,不修改用户现有 MCP 配置。
|
|
114
|
+
|
|
103
115
|
<details>
|
|
104
116
|
<summary>网页搜索是怎么接进 DSH 的</summary>
|
|
105
117
|
|
|
@@ -204,6 +216,8 @@ pnpm run check
|
|
|
204
216
|
|
|
205
217
|
真机网关用 `node scripts/live-check.mjs` 验证:它通过插件注册的同一条路由,读余额和分组、让每个 HTTP 模型回答一次、演练 Key 轮换和失败分类,只打印 Key 的末 4 位。加 `--with-cli` 会用本机 Claude Code CLI 跑三个 Claude 模型,加 `--with-search` 会每个搜索后端各搜一次;默认运行只消耗少量额度。
|
|
206
218
|
|
|
219
|
+
Claude CLI 的 MCP 接口验证用 `node scripts/cli-search-check.mjs`:运行真实 CLI,普通 Claude 响应和搜索数据默认由本地测试服务模拟,不消费额度;会检查三种权限模式、原生搜索禁用和临时资源清理。加 `--live --backend deepseek` 或 `--live --backend gpt` 会让只读模式的那一次搜索走真实后端(其它两种模式仍模拟)。这不等于验证了真实 Claude 上游对话或运行中的 DSH 集成。
|
|
220
|
+
|
|
207
221
|
使用 Agent 修改或发布时,先读 [AGENTS.md](AGENTS.md)。发布由推送 `v*` 标签触发 [Publish workflow](.github/workflows/publish.yml),不要在本机执行 `npm publish`。
|
|
208
222
|
|
|
209
223
|
## License
|
package/SECURITY.md
CHANGED
|
@@ -13,6 +13,15 @@
|
|
|
13
13
|
- Balance requests carry a timeout (`AbortSignal.timeout`) and `redirect: 'error'`, and go only to the configured hosts plus the PackyAPI fallback host.
|
|
14
14
|
- Model traffic goes to the configured model host through the host's own pi-ai adapter, with the harness attribution headers, exactly like a configured provider route.
|
|
15
15
|
|
|
16
|
+
## Claude CLI MCP search bridge
|
|
17
|
+
|
|
18
|
+
- Each CLI invocation owns a temporary HTTP MCP server bound exclusively to `127.0.0.1` on a random port. Every method requires an independent cryptographically random capability; Host/Origin validation rejects rebinding and cross-origin browser requests. No CORS access is granted.
|
|
19
|
+
- The owner-only temporary MCP config (private directory, file mode 0600 on POSIX) contains only this local capability. The search API keys remain in the host's key ring and are never placed in MCP configuration, argv, schemas, tool results or raw diagnostics. Result/CLI redaction handles escaped keys and secrets split across text deltas.
|
|
20
|
+
- The endpoint offers only bounded `web_search`; models cannot supply backend, API host, credentials, session IDs, shell commands or arbitrary fetch requests. Input size, query size, concurrency, search count, output size and an end-to-end timeout are bounded. Run stop and tool cancellation abort the provider.
|
|
21
|
+
- Native CLI WebSearch is denied even in full mode. Read-only/edit modes add only the exact MCP search tool to their auto-approval lists. Existing MCP/settings and user/managed deny rules remain effective; `--allowedTools` is not an exclusive whitelist. Hooks, other trusted MCP servers and full-permission commands remain part of the CLI's existing trust boundary.
|
|
22
|
+
- The local capability and owner-only files reduce accidental exposure; they do **not** isolate the service from a compromised process or customization running as the same OS user. Do not run the delegated CLI in an untrusted workspace with hostile hooks/MCP configuration.
|
|
23
|
+
- Startup cancellation also revokes an already-bound listener while filesystem setup is pending. Completed/failed/abandoned invocations and plugin unload close the server and clean up only their own temporary config directory.
|
|
24
|
+
|
|
16
25
|
## Reporting
|
|
17
26
|
|
|
18
27
|
Report a security issue through the repository's GitHub issues rather than a public discussion of a live credential. Never paste a key, token, or credential-store value into an issue, a log, or a chat.
|
package/lib/client.js
CHANGED
|
@@ -135,10 +135,11 @@ window.__ModuleLoader__.load({
|
|
|
135
135
|
versionLater: "稍后",
|
|
136
136
|
searchTitle: "网页搜索",
|
|
137
137
|
searchScope: "DSH 全局设置",
|
|
138
|
-
searchIntro: "
|
|
138
|
+
searchIntro: "DSH 的 web_search 和 Claude CLI 的 MCP 搜索可使用这里选择的 DeepSeek/GPT 后端。每次搜索是独立的模型调用,用插件保存的 Key 计费。",
|
|
139
|
+
searchCliHint: "Claude CLI 始终通过 MCP 使用这里的搜索后端,原生 WebSearch 已禁用。下面的路由选项只控制 DSH 全局搜索,不影响 CLI。",
|
|
139
140
|
searchChecking: "正在读取搜索设置…",
|
|
140
141
|
searchLoadFailed: "搜索设置读取或保存失败",
|
|
141
|
-
searchModeTitle: "
|
|
142
|
+
searchModeTitle: "DSH 路由",
|
|
142
143
|
searchModeDefault: "不接管",
|
|
143
144
|
searchModeDefaultHint: "不改动 DSH 的搜索路由(默认)。切回此项会移除本插件写入 profile 的搜索补丁。",
|
|
144
145
|
searchModeAuto: "按模型",
|
|
@@ -155,7 +156,7 @@ window.__ModuleLoader__.load({
|
|
|
155
156
|
searchApplyFailed: "写入搜索补丁失败:{error}",
|
|
156
157
|
searchForeign: "检测到 {names} 订阅插件:它们接管的模型仍按各自的搜索设置,其余请求才会到达这里的选择。",
|
|
157
158
|
searchOverridden: "基础搜索现在是 {base},不是 PackyCode:可能还在热加载,或被其他插件的补丁覆盖。",
|
|
158
|
-
searchUnavailable: "当前运行环境没有 DSH
|
|
159
|
+
searchUnavailable: "当前运行环境没有 DSH 网页搜索服务,全局路由暂不生效;Claude CLI 的 MCP 搜索不受影响。",
|
|
159
160
|
searchNotWritable: "当前 profile 不允许写入设置。",
|
|
160
161
|
searchUnknown: "未知"
|
|
161
162
|
};
|
|
@@ -259,10 +260,11 @@ window.__ModuleLoader__.load({
|
|
|
259
260
|
versionLater: "Later",
|
|
260
261
|
searchTitle: "Web search",
|
|
261
262
|
searchScope: "DSH-wide setting",
|
|
262
|
-
searchIntro: "
|
|
263
|
+
searchIntro: "DSH’s web_search and Claude CLI’s MCP search can use the DeepSeek/GPT backend selected here. Each search is a separate model call billed to the plugin-stored keys.",
|
|
264
|
+
searchCliHint: "Claude CLI always uses this backend through MCP; native WebSearch is disabled. The routing options below only control DSH-wide search, not CLI search.",
|
|
263
265
|
searchChecking: "Reading the search preference…",
|
|
264
266
|
searchLoadFailed: "Could not read or save the search preference",
|
|
265
|
-
searchModeTitle: "
|
|
267
|
+
searchModeTitle: "DSH routing",
|
|
266
268
|
searchModeDefault: "Off",
|
|
267
269
|
searchModeDefaultHint: "Leave DSH search routing untouched (the default). Switching back here removes the search patch this plugin wrote into the profile.",
|
|
268
270
|
searchModeAuto: "By model",
|
|
@@ -279,7 +281,7 @@ window.__ModuleLoader__.load({
|
|
|
279
281
|
searchApplyFailed: "Could not write the search patch: {error}",
|
|
280
282
|
searchForeign: "{names} subscription plugin detected: the models it routes keep its own search choice; only the other requests reach the choice made here.",
|
|
281
283
|
searchOverridden: "The base search is {base}, not PackyCode: the reload may still be running, or another plugin’s patch overrides it.",
|
|
282
|
-
searchUnavailable: "This runtime has no DSH web search service;
|
|
284
|
+
searchUnavailable: "This runtime has no DSH web search service; global routing has no effect. Claude CLI’s MCP search is unaffected.",
|
|
283
285
|
searchNotWritable: "This profile does not allow settings writes.",
|
|
284
286
|
searchUnknown: "unknown"
|
|
285
287
|
};
|
|
@@ -1062,6 +1064,10 @@ window.__ModuleLoader__.load({
|
|
|
1062
1064
|
className: "pkc-note",
|
|
1063
1065
|
children: t("searchIntro")
|
|
1064
1066
|
}),
|
|
1067
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1068
|
+
className: "pkc-note",
|
|
1069
|
+
children: t("searchCliHint")
|
|
1070
|
+
}),
|
|
1065
1071
|
state === void 0 && error === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1066
1072
|
className: "pkc-note",
|
|
1067
1073
|
role: "status",
|