dsh-packycode-api 1.0.7 → 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 +30 -0
- package/README.en.md +60 -9
- package/README.md +65 -10
- package/SECURITY.md +9 -0
- package/compatibility.json +1 -1
- package/lib/client.js +1161 -726
- package/lib/index.js +3544 -1213
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
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
|
+
|
|
11
|
+
## 1.1.0
|
|
12
|
+
|
|
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:
|
|
14
|
+
- A new group reader asks the gateway itself: a request for a model id no gateway routes (`dsh-packycode-group-probe`, `max_tokens: 1`) is refused before any upstream call with the token's groups (`已选分组=[codex deepseek-officially kimi-sale]`), nothing is billed. `/v1/models` adds the key's reachable models, and the public `/api/pricing` maps each catalog model to its groups (`claude-opus-5-5`: `cc` / `cc-expensive` / `cc-sale` / `claude-officially`; `claude-haiku-5-5` only `cc` and `claude-officially`) and gives each group's description. Pricing and every key are read concurrently, 4 keys at a time; the two per-key requests are independent evidence, and a key the gateway rejects (401 / 无效的令牌) counts as reaching nothing. Readings are cached 10 minutes, keep the last good groups when a read fails (marked stale), and never carry key material.
|
|
15
|
+
- When the reading shows the active key cannot reach a request's model and another stored key can, that request runs on that key, on all three paths: HTTP models (the stream wrapper swaps the resolved key per model), Claude CLI runs (`resolveCredential({ model })`), and web search (candidates ordered by coverage). The active pointer does not move, a key is passed over only on evidence, and with no better key the active one is used. A request with two or more usable keys waits at most 6 s for the first reading.
|
|
16
|
+
- Every Claude CLI turn names its key in the reasoning (`▸ 使用 Key「cc」(分组:cc)`, or `当前 Key「A」的分组不能用 claude-opus-5-5,本轮改用 Key「B」`, or a warning that no stored key has a Claude group) — by label only.
|
|
17
|
+
- A CLI refusal from a key without a Claude group — the 403 official-CLI wording or the 400 `当前令牌未覆盖供应商` — is explained as that: the key, its groups, the groups the model needs, and that the CLI does not need updating. The 400 names the groups itself; for the 403, a fresh reading is consulted when the run started before the groups were known. Status tokens are kept out of the sentence so no retry is spent on it. A key that does have a Claude group keeps the CLI explanation, now naming the key.
|
|
18
|
+
- New RPC endpoint `groups`; the reading is dropped when keys are added or removed or the hosts change.
|
|
19
|
+
- The key switch itself already applied per run: reproduced with the real CLI 2.1.39 against a capture gateway through the plugin's own wiring, the turn after **切到此 Key** sent the new key. A restart only appeared to fix it.
|
|
20
|
+
- **Settings panel redesign.** The balance list and the key list showed the same keys twice; one **账户与 Key** card now has the totals as tiles, then one row per key with its balance, a usage bar, its groups (hover for the description), a ✓ / ◐ / ✕ line per model family, and its actions. Web search uses segmented controls with the selected option's hint; the CLI card shows a status dot and a warning callout when the CLI is missing; the version card is one row. The stylesheet uses only host design tokens (`settings-card-fill/stroke`, `radius-*`, layer-1 inputs, primary/outline/danger buttons) so it follows light and dark themes; it referenced two tokens the host never defined (`--dsw-alias-border-default`, `--dsw-alias-state-warning-primary`) and painted cards, rows, buttons, and inputs the same grey. Copy and styles moved to `client-copy.js` and `client-style.js`; both languages carry the same keys, and English no longer shows full-width punctuation. Checked in a harness rendering the built bundle with DSH 0.2.0-rc.2's theme CSS, in both themes and languages.
|
|
21
|
+
- **Refactor.** `index.js` went from 714 to about 330 lines of composition: the HTTP route moved to `route.js`, key choice and rotation to `key-chooser.js`, the `claude_code` tool to `claude-tool.js`, both settings generations to `preferences.js`, and the profile-patch locator (patch path, base config, HMR transaction) next to the composition. The balance, group, and version readers share one cached single-flight reader (`cached-read.js`), and every host-side request goes through one bounded fetch (`request.js`: timeout plus caller signal, redirects refused, transport errors named). The balance reader now resolves fetch at call time like the others. Host helpers live in `hosts.js`; the coverage wording is shared from `failures.js`; test doubles live in `tests/helpers.mjs`. `scripts/live-check.mjs` now drives the plugin's own route composition instead of a hand-built copy, reports groups, separates the group pass-over from the rotation rehearsal, and takes `--with-search`. Public exports are unchanged apart from additions.
|
|
22
|
+
- `compatibility.json`: the 0.2.0-rc.2 note claimed `settings.installSection` exists on that line; it does not (its settings service only writes volatile fields), so host overrides are read-only there.
|
|
23
|
+
- Verified live through the built bundle: groups `codex, deepseek-officially, kimi-sale` with Claude unreachable; `deepseek-flash`, `gpt-6.1-sol`, and `gpt-6-astra` answered through the registered route; an invalid key in front was passed over with group evidence and disabled by rotation without; a Claude turn through the real CLI 2.1.295 with a key lacking a Claude group opened with the no-Claude-group warning and ended with the group explanation; both search backends answered. A full group reading took about 1 s (it took 15 s sequentially while `cf.api.fan` was resetting connections).
|
|
24
|
+
|
|
25
|
+
- **Web search through PackyCode.** The plugin registers two providers with DSH's `web` service: `packycode-api` searches through PackyCode for every request, and `packycode-api-auto` does so only for sessions running on a PackyCode model, handing every other request to the search provider the web row named before. **Settings → PackyCode → Web search** picks the routing (do not take over — the default — / auto / always) and the backend:
|
|
26
|
+
- `deepseek` (default): `deepseek-flash` over Anthropic Messages at `/v1/messages` with the `web_search_20250305` server tool, `max_uses` 3. Results come from `web_search_tool_result` blocks.
|
|
27
|
+
- `gpt`: `gpt-6.1-sol` over Responses at `/v1/responses` with the hosted `web_search` tool (`tool_choice: required`, `reasoning.effort: low`, list input, `prompt_cache_key`). Results come from `url_citation` annotations and the `web_search_call` actions.
|
|
28
|
+
- Both return the model's summary as `content`, with sources deduplicated by URL. A search uses only this plugin's ring keys, is bounded at 55 s, and refuses redirects. An exhausted or rejected key moves aside through the same sink as the model route and the next key is tried. A `model_not_found` / `当前令牌未覆盖供应商` refusal skips to the next key without disabling one, and finally says which backend to switch to. Gateway text is masked so a key never appears in an error.
|
|
29
|
+
- **How DSH reaches the providers.** Auto and Always maintain one marked `- id: web` entry in the running profile's `cordis.patch.yml` (`profileContext.patchPath`, or the owning profile found from the install), which DSH hot-applies. The entry is rewritten in place, written only when it changes, restates the web row's other scalar fields (a profile patch replaces the whole config), and records the base provider it replaced as `# fallback: …`. The runtime slot is never written: the Codex and Kimi subscription plugins rewrite it on every web restart, and their routers already fall back to the web row's base. A request that loops back to the auto provider from another router goes to `deepseek-official`. Writes run inside DSH's HMR transaction when one is free and unlocked when DSH refuses to nest it. Removing the entry (the default routing) leaves the rest of the file byte for byte.
|
|
30
|
+
- The preference is two new Config fields, `searchProvider` and `searchBackend`. On DSH 0.2 they are volatile fields written through `settings.update('packycode-api', …)` and followed through `loader/volatile-update`; the generated duplicate settings page is hidden. On DSH 0.1.5 they live in the installed settings section. New RPC endpoints `search/status` and `search/update` return only the preference, what is in effect, and whether Codex or Kimi routers are present.
|
|
31
|
+
- Verified live through the built bundle against `cf.api.fan`: the DeepSeek backend returned 10 sources in 5.4 s and the GPT backend 20 sources in 14.2 s for one query, with no key disabled. During the same session the gateway refused GPT requests from this key for a while as `当前令牌未覆盖供应商 "OpenAI"` and then served them again. Not yet exercised in a running DSH desktop session.
|
|
32
|
+
|
|
3
33
|
## 1.0.7
|
|
4
34
|
|
|
5
35
|
- A `gpt-6-astra` session's title now runs on `gpt-6.1-sol`, in the same way a Claude session's title runs on `claude-haiku-5-5`. DSH sends each title request to the session's own model, so Astra was titling its own sessions. The reroute applies only to requests with `purpose: 'session-title'`, on both the prepared and direct dispatch paths, and both models need the same Codex group. Verified live through the real host adapter with the gateway request observed: the title request went to `gpt-6.1-sol` ("为 PackyCode 添加 GPT-6 Astra并发布"), and an ordinary turn stayed on `gpt-6-astra`.
|
package/README.en.md
CHANGED
|
@@ -26,8 +26,10 @@ The `PackyCode` model group ships with the plugin. No YAML edits and no separate
|
|
|
26
26
|
| --- | --- |
|
|
27
27
|
| **Built-in model route** | Adds a `PackyCode` group to the model picker. No provider configuration and no `cordis.patch.yml` edits |
|
|
28
28
|
| **Keys used in order** | An exhausted or rejected key is disabled and the next usable key takes over; the failed step is retried on that key |
|
|
29
|
-
| **
|
|
29
|
+
| **Keys chosen by group** | Each key lists its PackyCode groups and the models they reach; when the active key's groups cannot reach the selected model (no `cc` group for Claude, say), that turn runs on a stored key whose groups can |
|
|
30
|
+
| **Visible balance** | Settings lists each key's remaining, used, limit, reset period, and a usage bar, plus a total of those keys. The composer shows the active key's remaining balance |
|
|
30
31
|
| **Claude through the local CLI** | `claude-opus-5-5`, `claude-sonnet-5-5`, and `claude-haiku-5-5` run the official Claude Code CLI on this device. Settings can detect it and install it |
|
|
32
|
+
| **Web search through PackyCode** | Optional: DSH's `web_search` searches through PackyCode, with a DeepSeek or GPT backend, for every request or for PackyCode models only |
|
|
31
33
|
| **Credentials stay on the machine** | Keys live in the DSH credential file. The browser receives labels, state, and balances only |
|
|
32
34
|
|
|
33
35
|
## Install
|
|
@@ -42,7 +44,7 @@ Open the desktop application's **Plugins** page, click **Add plugin**, and enter
|
|
|
42
44
|
dsh-packycode-api
|
|
43
45
|
```
|
|
44
46
|
|
|
45
|
-
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.
|
|
47
|
+
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.1.1`.
|
|
46
48
|
|
|
47
49
|
Then **manually restart the desktop application**. Refreshing the page does not reload the plugin inside the Host process.
|
|
48
50
|
|
|
@@ -61,7 +63,7 @@ Choose a model from the `PackyCode` group. `deepseek-flash`, `gpt-6.1-sol`, and
|
|
|
61
63
|
|
|
62
64
|
The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
|
|
63
65
|
|
|
64
|
-
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@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`).
|
|
65
67
|
|
|
66
68
|
A local checkout can be linked with `dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`.
|
|
67
69
|
|
|
@@ -71,16 +73,59 @@ A profile without a browser can still use the model route. It just has no settin
|
|
|
71
73
|
|
|
72
74
|
## Daily use
|
|
73
75
|
|
|
74
|
-
**Switch keys.** Keys are used in the order they were added. **Use this key**
|
|
76
|
+
**Switch keys.** Keys are used in the order they were added. **Use this key** picks the preferred key, effective from the next turn; **Rename** changes its label (at most 60 characters, empty names are refused); **Re-enable** puts a disabled key back after a top-up or replacement.
|
|
75
77
|
|
|
76
|
-
**
|
|
78
|
+
**See groups.** Under **Accounts and keys**, each key lists its PackyCode groups (hover for the group's description) and which models they reach: ✓ yes, ◐ partly, ✕ no. The groups come from the gateway itself (its refusal of a model id it cannot route names the token's groups; nothing is billed), matched against PackyCode's public model-group table. Claude needs one of `cc` / `cc-sale` / `cc-expensive` / `claude-officially` (`claude-haiku-5-5` only `cc` and `claude-officially`); GPT needs `codex` / `codex-sale`; DeepSeek needs `deepseek-officially` / `deepseek-sale`.
|
|
77
79
|
|
|
78
|
-
**
|
|
80
|
+
**Read the balance.** The total at the top of **Accounts and keys** is the remaining, used, and limit of **the keys listed here**, not the bill for every token on the PackyCode account. A key that could not be read is left out of the total and counted in a note; a key whose read failed this time but succeeded before keeps showing its last balance with the time it was read (the composer shows it as `≈US$…`) until DSH restarts. The composer shows the active key's remaining balance only while a PackyCode model is selected. **Refresh** bypasses the roughly 60-second cache. The default model host is `https://cf.api.fan` and the balance host is `https://www.packyapi.ai`; both can be changed under **Advanced: endpoints**. The old default, `https://www.packyapi.com`, is DNS-poisoned on mainland networks and answers `fetch failed` without a proxy; even when settings still name it, `.ai` is tried first.
|
|
81
|
+
|
|
82
|
+
**Use Claude.** The **Claude Code CLI** card in Settings checks this device. Every Claude turn's reasoning block names the key it ran with (by label only). If the CLI is missing, **Install Claude Code CLI** runs `npm install -g @anthropic-ai/claude-code` (Node.js 18+). No DSH restart is required after that install. Windows also needs [Git for Windows](https://git-scm.com/downloads/win).
|
|
83
|
+
|
|
84
|
+
**Web search.** DSH's `web_search` calls DeepSeek's official API by default (billed to `DEEPSEEK_API_KEY`). **Settings → PackyCode → Web search** can route it through PackyCode instead:
|
|
85
|
+
|
|
86
|
+
| Routing | Effect |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| Do not take over (default) | DSH's search settings stay as they are |
|
|
89
|
+
| Auto (route by model) | PackyCode models search through PackyCode; other models keep their search |
|
|
90
|
+
| Always use PackyCode search | Every request DSH's default search would have served goes through PackyCode |
|
|
91
|
+
|
|
92
|
+
| Backend | Model and protocol | Notes |
|
|
93
|
+
| --- | --- | --- |
|
|
94
|
+
| DeepSeek (default) | `deepseek-flash`, `/v1/messages` + `web_search_20250305` | A few thousand tokens per search; needs a DeepSeek group |
|
|
95
|
+
| GPT | `gpt-6.1-sol`, `/v1/responses` + `web_search` | Usually fresher and more precise, about 20–30K input tokens per search; needs the gateway to route this key's GPT requests to its group |
|
|
96
|
+
|
|
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
|
+
|
|
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
|
+
|
|
111
|
+
<details>
|
|
112
|
+
<summary>How web search is wired into DSH</summary>
|
|
113
|
+
|
|
114
|
+
- The plugin registers two search providers with DSH's `web` service: `packycode-api` (always PackyCode) and `packycode-api-auto` (routes by the initiating session's model). Registering them changes nothing by itself.
|
|
115
|
+
- Auto and Always append one `- id: web` entry, marked `# >>> dsh-packycode-api: web search provider`, to the end of the profile's `cordis.patch.yml`. It points the web row's base `searchProvider` at one of those providers; DSH hot-applies the file, no restart needed. Switching back to the default removes the entry and leaves everything else as it was.
|
|
116
|
+
- The plugin does not write the web runtime's search slot. Subscription plugins such as Codex and Kimi rewrite that slot whenever the web row restarts, so two writers would restart each other forever. They fall back to the web row's base config for models they do not own, so writing the base coexists with them: Codex models keep Codex search and only the remaining requests reach the choice made here.
|
|
117
|
+
- The entry records the base search it replaced as a comment (`# fallback: …`). In Auto, requests from non-PackyCode models go there; if that provider hands a request back to this plugin, it goes straight to DSH's built-in `deepseek-official` instead of looping.
|
|
118
|
+
- Results come from structured data only: DeepSeek's `web_search_tool_result` blocks, GPT's `url_citation` annotations and `web_search_call` records. The model's summary is returned as `content`.
|
|
119
|
+
- When a key's group does not cover the backend's model (the gateway says `当前令牌未覆盖供应商`), the search tries the next key without disabling this one, and finally suggests switching backends.
|
|
120
|
+
- Settings shows the base and runtime search provider in effect, and warns when another plugin's later patch overrides the base.
|
|
121
|
+
|
|
122
|
+
</details>
|
|
79
123
|
|
|
80
124
|
<details>
|
|
81
125
|
<summary>When a key is rotated</summary>
|
|
82
126
|
|
|
83
127
|
- **Rotates and disables the current key:** the gateway reports exhausted quota, or it rejects the key (HTTP 401 / invalid token). The harness retries the failed step, which is what actually sends it on the next key.
|
|
128
|
+
- **Passes over a key by its groups, without disabling it:** when the group reading shows the active key cannot reach this turn's model and another stored key can, the turn runs on that key (a Claude turn says so in its reasoning: “当前 Key「A」的分组不能用 …,本轮改用 Key「B」”). The active key stays active for every other model. Only evidence counts: a key whose groups are not read yet is used as before.
|
|
84
129
|
- **Does not rotate:** rate limits (429); a 400 that this gateway uses for a dead upstream channel; and a 403 that means the group accepts only the vendor's official client. That last case is explained directly instead of being shown as an invalid API key.
|
|
85
130
|
- When every stored key is disabled, the request fails and says so. There is no fallback credential.
|
|
86
131
|
|
|
@@ -124,11 +169,15 @@ A model in the list does not mean the current token group allows it. When changi
|
|
|
124
169
|
|
|
125
170
|
**Update:** open **Settings → PackyCode** and, when a newer version is listed, click **Update plugin**. It installs that exact version into the owning profile with DSH's bundled pnpm and reads `package.json` back before reporting success. A local `link:` checkout does not get the button. Then quit and reopen the desktop application. A page refresh does not reload the Host. Reinstalling `dsh-packycode-api` from the **Plugins** page does the same job.
|
|
126
171
|
|
|
127
|
-
**Uninstall:** remove this plugin on the **Plugins** page
|
|
172
|
+
**Uninstall:** if you used PackyCode web search, first switch **Settings → PackyCode → Web search** back to the default. Then remove this plugin on the **Plugins** page and restart the desktop application. Otherwise `cordis.patch.yml` keeps a web entry naming a provider that no longer exists, and `web_search` fails with `configured web provider "packycode-api-auto" is not registered`; delete the block marked `dsh-packycode-api: web search provider` by hand to recover. Uninstalling does not remove other plugins, the profile, or the token in the PackyCode console.
|
|
128
173
|
|
|
129
174
|
## FAQ
|
|
130
175
|
|
|
131
|
-
**
|
|
176
|
+
**Web search says "no stored API key's group covers …"?** The search backend's model is not in your token groups (or the gateway currently routes it to another supplier). Pick the other backend under **Settings → PackyCode → Web search**, or add a key whose group covers it.
|
|
177
|
+
|
|
178
|
+
**One model fails, but the key is valid?** Look at that key's groups and ✓ / ✕ under **Accounts and keys**. When its groups do not cover the model, use a key whose groups do, or add the group to the key in the PackyCode console. With such a key stored, the plugin runs that model on it automatically. A group refusal never disables a key.
|
|
179
|
+
|
|
180
|
+
**Claude says the gateway refused the request as not coming from the official Claude Code CLI?** The usual cause is that the turn's key has no Claude group: PackyCode answers such a key with that same wording (or a 400 `当前令牌未覆盖供应商`). The error now says so directly — the key, its groups, and the groups the model needs — and that the CLI does not need updating. Only a key that does have a Claude group and is still refused points at the CLI.
|
|
132
181
|
|
|
133
182
|
**The balance does not match the account bill?** Settings totals only the keys stored here, not other tokens on the same PackyCode account. A key that failed to read is noted separately and is not treated as zero.
|
|
134
183
|
|
|
@@ -161,7 +210,9 @@ pnpm run check
|
|
|
161
210
|
|
|
162
211
|
`pnpm run check` runs every test, rebuilds, and writes a tarball under `.artifacts/`. `lib/` is a committed build; regenerate it with `pnpm run build` after editing `src/`, and do not hand-edit it.
|
|
163
212
|
|
|
164
|
-
`node scripts/live-check.mjs` checks the live gateway
|
|
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.
|
|
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.
|
|
165
216
|
|
|
166
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.
|
|
167
218
|
|
package/README.md
CHANGED
|
@@ -26,10 +26,16 @@
|
|
|
26
26
|
| --- | --- |
|
|
27
27
|
| **自带模型路由** | 安装后模型选择器出现 `PackyCode` 分组,不需要配置 provider,也不用改 `cordis.patch.yml` |
|
|
28
28
|
| **多 Key 按顺序使用** | 额度用尽或 Key 被拒绝时自动切到下一个可用 Key;失败的那一步由重试接手 |
|
|
29
|
-
|
|
|
29
|
+
| **按分组选 Key** | 每个 Key 后面列出它所在的 PackyCode 分组和能用的模型;当前 Key 的分组用不了所选模型(例如没有 cc 分组却选了 Claude)时,这一轮自动改用分组合适的 Key |
|
|
30
|
+
| **余额一眼可见** | 设置页列出每个 Key 的剩余、已用、额度、重置周期和用量条,并给出这些 Key 的合计;输入框右侧显示当前 Key 的剩余额度 |
|
|
30
31
|
| **Claude 走本机 CLI** | `claude-opus-5-5`、`claude-sonnet-5-5`、`claude-haiku-5-5` 由本机官方 Claude Code CLI 执行;设置页可以检测并一键安装 |
|
|
32
|
+
| **网页搜索可走 PackyCode** | 可选:DSH 的 `web_search` 改用 PackyCode 搜索,后端在 DeepSeek 与 GPT 之间切换;可只对 PackyCode 模型生效 |
|
|
31
33
|
| **凭据不离开本机** | Key 只存在 DSH 凭据文件里。浏览器只能看到备注、状态和余额,拿不到 Key |
|
|
32
34
|
|
|
35
|
+
<img width="790" height="332" alt="截屏2026-10-13 10 08 27" src="https://github.com/user-attachments/assets/2faa7667-300c-4f12-bb94-aecc35c5cb7f" />
|
|
36
|
+
<img width="272" height="425" alt="截屏2026-10-13 10 07 46" src="https://github.com/user-attachments/assets/040fc57a-2dd2-4195-9bbc-5fa3357d1eb1" />
|
|
37
|
+
|
|
38
|
+
|
|
33
39
|
## 安装
|
|
34
40
|
|
|
35
41
|
先准备好 **DeepSeek Harness** 和 **PackyCode(PackyAPI)的 API Key**。尚未安装 DSH,可查看 [DSH 官方说明](https://github.com/deepseek-ai/deepseek-harness#run)。
|
|
@@ -42,7 +48,7 @@
|
|
|
42
48
|
dsh-packycode-api
|
|
43
49
|
```
|
|
44
50
|
|
|
45
|
-
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.
|
|
51
|
+
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.1.1`。
|
|
46
52
|
|
|
47
53
|
完成后**手动重启桌面应用**。仅刷新页面不会重新加载 Host 里的插件。
|
|
48
54
|
|
|
@@ -61,7 +67,7 @@ dsh-packycode-api
|
|
|
61
67
|
|
|
62
68
|
桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
|
|
63
69
|
|
|
64
|
-
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@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`)。
|
|
65
71
|
|
|
66
72
|
本地开发可以用绝对路径:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。
|
|
67
73
|
|
|
@@ -71,16 +77,59 @@ dsh-packycode-api
|
|
|
71
77
|
|
|
72
78
|
## 日常使用
|
|
73
79
|
|
|
74
|
-
**换 Key。** Key 按添加顺序使用。点「切到此 Key
|
|
80
|
+
**换 Key。** Key 按添加顺序使用。点「切到此 Key」指定优先使用的 Key,下一轮就生效;「重命名」改备注(最多 60 字,不能为空);充值或更换后点「重新启用」。
|
|
81
|
+
|
|
82
|
+
**看分组。** 「账户与 Key」里每个 Key 下面列出它所在的 PackyCode 分组(鼠标悬停可看分组说明),以及能用哪些模型:✓ 可用、◐ 部分可用、✕ 不可用。分组由网关自己报出(对一个不存在的模型 id 的拒绝里会写明「已选分组」,不产生费用),再对照 PackyCode 公开的模型分组表。Claude 需要 `cc` / `cc-sale` / `cc-expensive` / `claude-officially` 之一(`claude-haiku-5-5` 只在 `cc` 和 `claude-officially`);GPT 需要 `codex` / `codex-sale`;DeepSeek 需要 `deepseek-officially` / `deepseek-sale`。
|
|
75
83
|
|
|
76
|
-
**看余额。**
|
|
84
|
+
**看余额。** 「账户与 Key」顶部的合计是**已列出的这些 Key** 的剩余、已用与额度,不是 PackyCode 账号下全部令牌的账单。读失败的 Key 不计入合计,并会注明个数;某个 Key 这次读失败但之前读到过,会继续显示上次的余额并注明读取时间(输入框里显示为 `≈US$…`),重启 DSH 后才清空。输入框右侧只在选中 PackyCode 模型时显示当前 Key 的剩余额度。「刷新」会跳过约 60 秒的缓存。默认模型接口是 `https://cf.api.fan`,余额接口是 `https://www.packyapi.ai`,可在「高级:接口地址」里改。旧默认值 `https://www.packyapi.com` 在国内网络下 DNS 被污染、直连会 `fetch failed`,即使设置里保存的是它,插件也会先试 `.ai`。
|
|
77
85
|
|
|
78
|
-
**用 Claude。**
|
|
86
|
+
**用 Claude。** 设置页的「Claude Code CLI」卡片会检测本机 CLI。每一轮 Claude 对话的思考区都会写明这一轮用的是哪个 Key(只显示备注名)。没装时点「安装 Claude Code CLI」,本机执行 `npm install -g @anthropic-ai/claude-code`(需要 Node.js 18+)。装好后不用重启 DSH。Windows 上另外需要 [Git for Windows](https://git-scm.com/downloads/win)。
|
|
87
|
+
|
|
88
|
+
**网页搜索。** DSH 的 `web_search` 默认调用 DeepSeek 官方接口(用 `DEEPSEEK_API_KEY` 计费)。在 **设置 → PackyCode → 网页搜索** 里可以改走 PackyCode:
|
|
89
|
+
|
|
90
|
+
| 搜索路由 | 效果 |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| 不接管(默认) | 不改 DSH 的搜索设置 |
|
|
93
|
+
| 自动(按模型路由) | PackyCode 模型用 PackyCode 搜索,其他模型照旧 |
|
|
94
|
+
| 始终使用 PackyCode 搜索 | 原本走 DSH 默认搜索的请求都改走 PackyCode |
|
|
95
|
+
|
|
96
|
+
| 搜索后端 | 模型与协议 | 说明 |
|
|
97
|
+
| --- | --- | --- |
|
|
98
|
+
| DeepSeek(默认) | `deepseek-flash`,`/v1/messages` + `web_search_20250305` | 每次约几千 token;需要 DeepSeek 分组 |
|
|
99
|
+
| GPT | `gpt-6.1-sol`,`/v1/responses` + `web_search` | 结果通常更新、更准,每次约 2–3 万输入 token;需要网关把这个 Key 的 GPT 请求路由到它的分组 |
|
|
100
|
+
|
|
101
|
+
PackyCode 没有专用搜索接口:一次搜索就是一次带服务端搜索工具的模型调用,用这里的 Key 计费,额度用尽或 Key 被拒绝时同样切到下一个 Key。
|
|
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
|
+
|
|
115
|
+
<details>
|
|
116
|
+
<summary>网页搜索是怎么接进 DSH 的</summary>
|
|
117
|
+
|
|
118
|
+
- 插件向 DSH 的 `web` 服务注册两个搜索后端:`packycode-api`(始终走 PackyCode)和 `packycode-api-auto`(按发起会话的模型路由)。只注册不会改变任何行为。
|
|
119
|
+
- 选「自动」或「始终」时,插件在当前 profile 的 `cordis.patch.yml` 末尾写入一段带 `# >>> dsh-packycode-api: web search provider` 标记的 `- id: web` 配置,把 web 行的基础 `searchProvider` 指向上面的后端;DSH 会热加载这个文件,不用重启。切回「不接管」会删掉这段,其它内容原样保留。
|
|
120
|
+
- 不直接改 web 运行时的搜索槽位:Codex、Kimi 等订阅插件每次 web 重启都会重写那个槽位,两边都写会互相触发重启。它们对自己不负责的模型会回退到 web 行的基础配置,所以写基础配置能和它们共存:Codex 模型仍走 Codex 搜索,其余请求才轮到这里的选择。
|
|
121
|
+
- 这段配置用注释记下写入前的基础搜索(`# fallback: …`)。「自动」模式下非 PackyCode 模型的请求交给它;如果它又把请求转回本插件,就直接交给 DSH 内置的 `deepseek-official`,不会循环。
|
|
122
|
+
- 结果只取结构化数据:DeepSeek 用 `web_search_tool_result` 块,GPT 用 `url_citation` 标注和 `web_search_call` 记录;模型写的摘要作为 `content` 一并返回。
|
|
123
|
+
- 当前 Key 的分组覆盖不到所选后端的模型(网关报 `当前令牌未覆盖供应商`)时,会试下一个 Key,不停用 Key;都不行就提示切换后端。
|
|
124
|
+
- 设置页会显示当前生效的基础搜索和运行时搜索。如果另一个插件的补丁排在后面并覆盖了基础搜索,这里会提示。
|
|
125
|
+
|
|
126
|
+
</details>
|
|
79
127
|
|
|
80
128
|
<details>
|
|
81
129
|
<summary>什么时候会自动切换 Key</summary>
|
|
82
130
|
|
|
83
131
|
- **会切换并停用当前 Key:** 额度用尽(网关报额度、余额、insufficient quota 之类),或 Key 被拒绝(HTTP 401 / `无效的令牌`)。失败的那一步由 harness 重试,从而用上新 Key。
|
|
132
|
+
- **按分组改用,不停用:** 分组读数显示当前 Key 用不了这一轮的模型、而另一个 Key 能用时,这一轮直接用那个 Key(Claude 轮次会在思考区写明「当前 Key「A」的分组不能用 …,本轮改用 Key「B」」)。「使用中」的 Key 不变,其它模型照常用它。只在有证据时才改用:分组还没读到的 Key 照旧使用。
|
|
84
133
|
- **不会切换:** 限速(429);网关把上游渠道故障误报成 400(`当前令牌未覆盖供应商 …`);以及「只允许官方客户端」的 403。后一种会直接说明原因,而不是显示成 API Key 无效。
|
|
85
134
|
- 全部 Key 都停用时,请求失败并说明没有可用 Key,不会回退到别的凭据。
|
|
86
135
|
|
|
@@ -93,7 +142,7 @@ dsh-packycode-api
|
|
|
93
142
|
| `deepseek-flash` | HTTP,chat/completions | 可关;`low` / `high` / `max` | `deepseek-officially`、`deepseek-sale` |
|
|
94
143
|
| `gpt-6.1-sol` | HTTP,Responses,可带图 | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | `codex`、`codex-sale` |
|
|
95
144
|
| `gpt-6-astra` | 同上 | 同上 | 同上 |
|
|
96
|
-
| `claude-opus-5-5` | 本机 Claude Code CLI | 选择器档位同上,实际档位由 CLI 决定 |
|
|
145
|
+
| `claude-opus-5-5` | 本机 Claude Code CLI | 选择器档位同上,实际档位由 CLI 决定 | 能给官方 CLI 用的分组,比如cc分组 |
|
|
97
146
|
| `claude-sonnet-5-5` | 同上 | 同上 | 同上 |
|
|
98
147
|
| `claude-haiku-5-5` | 同上;会话标题也固定走它 | 同上 | 同上 |
|
|
99
148
|
|
|
@@ -124,11 +173,13 @@ dsh-packycode-api
|
|
|
124
173
|
|
|
125
174
|
**更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装 `dsh-packycode-api`。
|
|
126
175
|
|
|
127
|
-
**卸载:**
|
|
176
|
+
**卸载:** 用过 PackyCode 网页搜索的话,先在 **设置 → PackyCode → 网页搜索** 切回「不接管」,再到**插件**页卸载本插件,然后重启桌面应用。否则 `cordis.patch.yml` 里会留下指向已卸载后端的 web 配置,`web_search` 会报 `configured web provider "packycode-api-auto" is not registered`;这时手动删掉带 `dsh-packycode-api: web search provider` 标记的那一段即可。卸载不会删除其它插件、整个 profile,也不会撤销 PackyCode 控制台里的令牌。
|
|
128
177
|
|
|
129
178
|
## 常见问题
|
|
130
179
|
|
|
131
|
-
**某个模型不可用,但 Key 是有效的?**
|
|
180
|
+
**某个模型不可用,但 Key 是有效的?** 看「账户与 Key」里这个 Key 的分组和 ✓ / ✕:分组不覆盖这个模型时,换一个分组合适的 Key,或在 PackyCode 控制台给 Key 加上对应分组。存了分组合适的 Key 时,插件会自动用它跑这个模型。分组拒绝不会停用 Key。
|
|
181
|
+
|
|
182
|
+
**Claude 报「PackyCode 的上游拒绝了这次 Claude Code CLI 请求」或「只接受官方 Claude Code CLI」?** 最常见的原因是这一轮用的 Key 没有 Claude 分组:PackyCode 对这种 Key 也会回这句话(或 400 `当前令牌未覆盖供应商`)。现在报错会直接写明「本轮使用的 Key「…」所在分组(…)不包含能用 claude-… 的分组」,并说明不需要更新 CLI。只有 Key 确实有 Claude 分组时仍被拒,才需要按报错里的提示检查 CLI 版本或联系 PackyCode。
|
|
132
183
|
|
|
133
184
|
**余额对不上账号账单?** 设置页合计的是这里列出的 Key,不含同账号下没添加到本插件的其它令牌。个别 Key 读失败时会单独注明,不会被算成 0。
|
|
134
185
|
|
|
@@ -140,6 +191,8 @@ dsh-packycode-api
|
|
|
140
191
|
|
|
141
192
|
**插件页安装报 `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION`?** pnpm 默认拒绝发布不满 24 小时的包,并且会检查 profile 里的整个 lockfile。报错里点名的往往是另一个刚更新过的插件,不是本插件。等它发布满 24 小时再重试,或把报错里的 `包名@版本` 加到 profile 目录 `pnpm-workspace.yaml` 的 `minimumReleaseAgeExclude` 下。已安装本插件后,设置页的「更新插件」不受这个限制。
|
|
142
193
|
|
|
194
|
+
**网页搜索报「no stored API key's group covers …」?** 所选搜索后端的模型不在你的令牌分组里(或网关暂时把它路由到了别的供应商)。在 **设置 → PackyCode → 网页搜索** 换一个后端,或添加分组覆盖它的 Key。
|
|
195
|
+
|
|
143
196
|
**装了 CLI 还是不能用 Claude?** 从 Finder 或开始菜单启动时,`PATH` 往往比终端短。插件会再查官方安装位置、常见包管理器目录和 `npm prefix -g`。仍找不到就在设置里填 `claudeCliPath`,或点「重新检测」。改的是 Host 或重新安装了插件时,要完全退出并重启桌面应用。
|
|
144
197
|
|
|
145
198
|
## 安全边界
|
|
@@ -161,7 +214,9 @@ pnpm run check
|
|
|
161
214
|
|
|
162
215
|
`pnpm run check` 会跑全部测试、重新构建,并在 `.artifacts/` 打出 tarball。`lib/` 是已提交的构建产物,改 `src/` 后用 `pnpm run build` 生成,不要手改。
|
|
163
216
|
|
|
164
|
-
真机网关用 `node scripts/live-check.mjs`
|
|
217
|
+
真机网关用 `node scripts/live-check.mjs` 验证:它通过插件注册的同一条路由,读余额和分组、让每个 HTTP 模型回答一次、演练 Key 轮换和失败分类,只打印 Key 的末 4 位。加 `--with-cli` 会用本机 Claude Code CLI 跑三个 Claude 模型,加 `--with-search` 会每个搜索后端各搜一次;默认运行只消耗少量额度。
|
|
218
|
+
|
|
219
|
+
Claude CLI 的 MCP 接口验证用 `node scripts/cli-search-check.mjs`:运行真实 CLI,普通 Claude 响应和搜索数据默认由本地测试服务模拟,不消费额度;会检查三种权限模式、原生搜索禁用和临时资源清理。加 `--live --backend deepseek` 或 `--live --backend gpt` 会让只读模式的那一次搜索走真实后端(其它两种模式仍模拟)。这不等于验证了真实 Claude 上游对话或运行中的 DSH 集成。
|
|
165
220
|
|
|
166
221
|
使用 Agent 修改或发布时,先读 [AGENTS.md](AGENTS.md)。发布由推送 `v*` 标签触发 [Publish workflow](.github/workflows/publish.yml),不要在本机执行 `npm publish`。
|
|
167
222
|
|
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/compatibility.json
CHANGED
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"piAi": "0.85.1",
|
|
9
9
|
"validation": {
|
|
10
10
|
"0.1.5-rc.2": "36 offline tests against the published host packages: the key ring (add/select/disable/rotate/enable/remove and a hand-edited record), balance parsing and host fallback with a fake fetch, failure classification against the gateway's real Chinese wording (including that a raw 403 quota message must not read as auth), the catalog contract (protocol, base URL, and vendor effort levels per model), the stream guard (terminal events, pre-chunk throws, and that a mid-stream failure never blames a key), and the real PiAiAdapter resolving every advertised model and preparing its call without reading a credential or opening a stream. Live check against the gateway: balance read (account and per-key), one answer from each of the three models over its own protocol, a rotation rehearsal where an invalid key was disabled and the next attempt answered with the real key, and the host adapter classifying a rejected key as AUTH.",
|
|
11
|
-
"0.2.0-rc.2": "Declared in the accepted range; the
|
|
11
|
+
"0.2.0-rc.2": "Declared in the accepted range; checked against the bundled 0.2.0-rc.2 host packages, not yet in a live boot on this line. Present and used: llm.registerAdapter (fiber-scoped, duplicates refused), llm.registerConfigurableProviders, credential records (credentials-local keeps records in memory, updated on modifyRecord and by a file watcher), connection.rpc.handle, client slots, web.registerSearchProvider with web.searchProviders, loader.entries(), profileContext.patchPath, hmr.runExclusive (refuses nested transactions), and the SettingsForms service: settings.update(rowId, patch) for volatile Config fields, settings.configure, and loader/volatile-update. settings.installSection does NOT exist on this line (an earlier note said it did), so the host overrides (apiHost/statusHost, not volatile) are read-only here while the volatile web search preference is writable."
|
|
12
12
|
}
|
|
13
13
|
}
|