dsh-packycode-api 1.3.2 → 1.3.4

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/AGENTS.md CHANGED
@@ -16,7 +16,9 @@ This file governs the entire `dsh-packycode-api` repository.
16
16
  - Host code belongs in `src/index.js` and its sibling modules; browser UI belongs in `src/client.jsx` and is built through the client wrapper in `tsdown.config.mjs`.
17
17
  - The route is self-contained: the plugin builds its own pi-ai provider, catalog, key ring, and balance reader. It must never require the user to edit `cordis.patch.yml` or `llm-pi-ai` provider YAML.
18
18
  - Never introduce a silent fallback to another provider, another credential source, or an ambient `PACKY_CODE_API_KEY`. Only keys the user added through this plugin may authenticate a request.
19
+ - Claude requests on keys in the `cc` group go through the official Claude Code CLI (`src/claude-cli-*.js`, `src/claude-mcp-bridge.js`); every other key uses HTTP. The CLI is only a transport: it must keep all of its own tools disabled, receive DSH's tools through the MCP bridge, and leave execution, permissions and plan mode to DSH. Never let it read the user's Claude settings, login or ambient keys, and never fall back between the CLI and HTTP routes on a failure. Changing the CLI's flags, environment or the continuation logic requires `node scripts/claude-check.mjs --transport cli` with a `cc` key.
19
20
  - The catalog declares protocols and effort levels from vendor documentation. Changing a model's `api`, `baseUrl`, or `thinkingLevelMap` requires a note in `CHANGELOG.md` and a live check.
21
+ - The in-plugin update installs under the profile's own pnpm release-age policy. Never pass `--config.minimumReleaseAge` or edit `minimumReleaseAgeExclude` from the plugin. Bypassing the policy leaves a lockfile entry pnpm later rejects, and pnpm then refuses every plugin install in that profile until the version is 24 hours old. pnpm records an exact version it accepts early itself; a version it holds back is reported as pending.
20
22
 
21
23
  ## Security requirements
22
24
 
@@ -51,6 +53,15 @@ Live gateway behavior is verified with `node scripts/live-check.mjs` after a bui
51
53
 
52
54
  Never reuse, move, delete, or overwrite a published npm version or Git tag. If a published version needs a correction, create a new patch version.
53
55
 
56
+ ## Staged releases
57
+
58
+ Releases are staged through npm's `next` dist-tag. A tag publishes there, and `latest` follows about 25 hours later (checklist step 7). Read this before iterating on new versions:
59
+
60
+ - Release as often as needed. Each release joins `next`; the hourly promote job moves `latest` to the newest staged version that has aged 25 hours, so `latest` trails the newest release by about a day.
61
+ - `latest` is what the in-plugin update button and DSH's Plugins page install. Update buttons up to 1.3.2 bypass pnpm's 24-hour release-age policy, so a younger version on `latest` blocks every later plugin install in those users' profiles. Never publish straight to `latest`, never move `latest` by hand, and never remove `--tag next` or the `promote` and `mark-latest` jobs unless the maintainer asks. The maintainer plans to switch back to direct `latest` releases around 2026-10-25, once 1.3.3 or later is `latest`.
62
+ - To keep a broken release from reaching `latest`, deprecate that version on npm. The promote job never promotes a deprecated version.
63
+ - Anyone who needs a staged version now installs its exact version from the DSH Plugins page (for example `dsh-packycode-api@1.3.4`). pnpm installs it and records it in the profile's `minimumReleaseAgeExclude`.
64
+
54
65
  ## Release checklist
55
66
 
56
67
  1. Synchronize and confirm a clean base:
@@ -90,17 +101,19 @@ Never reuse, move, delete, or overwrite a published npm version or Git tag. If a
90
101
 
91
102
  5. Commit the release and push `main` without force. [`.github/workflows/publish.yml`](.github/workflows/publish.yml) must already be on the commit that will be tagged. Do not push the tag in this step.
92
103
 
93
- 6. Confirm the package has a GitHub Actions trusted publisher before pushing the tag: repository `BaronCyrus/dsh-packycode-api`, workflow filename `publish.yml`, environment empty. Never request that a token, password, or one-time code be pasted into chat.
104
+ 6. Confirm the package has a GitHub Actions trusted publisher before pushing the tag: repository `BaronCyrus/dsh-packycode-api`, workflow filename `publish.yml`, environment empty, with **Allow npm dist-tag** enabled so the promote job can move `latest`. Never request that a token, password, or one-time code be pasted into chat.
94
105
 
95
- 7. Create the annotated tag and push it. That push publishes with GitHub OIDC. The workflow's `release` job then creates the GitHub Release from `docs/releases/v<version>.md` and attaches the tarball npm published. It is the only job with `contents: write`, and it runs no dependency code. Do not run `npm publish` locally for a release. A local publish cannot attach provenance. Do not set `NODE_AUTH_TOKEN` or a setup-node `registry-url` in the publish workflow; npm treats either as token auth and skips OIDC.
106
+ 7. Create the annotated tag and push it. That push publishes to npm's `next` dist-tag with GitHub OIDC. The workflow's `release` job then creates the GitHub Release from `docs/releases/v<version>.md`, attaches the tarball npm published, and leaves it without the Latest mark. `latest` moves later: the hourly `promote` job runs `scripts/promote-release.mjs`, which moves `latest` to the newest version after it, up to `next`, that is 25 hours old and not deprecated, and `mark-latest` then marks that version's GitHub Release Latest. pnpm refuses versions younger than 24 hours, and the update buttons already installed (up to 1.3.2) bypass that refusal, so `latest` must never name a younger version; never move it by hand earlier. `release` and `mark-latest` are the only jobs with `contents: write`, and neither runs dependency code. Do not run `npm publish` locally for a release. A local publish cannot attach provenance. Do not set `NODE_AUTH_TOKEN` or a setup-node `registry-url` in the publish workflow; npm treats either as token auth and skips OIDC.
96
107
 
97
108
  ```sh
98
109
  git tag -a v<version> -m "dsh-packycode-api v<version>"
99
110
  git push origin v<version>
100
111
  ```
101
112
 
102
- `workflow_dispatch` on the same workflow only prints the OIDC identity npm will match. It does not publish.
113
+ `workflow_dispatch` on the same workflow prints the OIDC identity npm will match and does not publish. With `promote` checked, it runs the promote job instead, which still waits out the 25 hours.
114
+
115
+ 8. Confirm that both jobs of the tag's publish run succeeded. Then check that npm's `next` serves the version (`npm view dsh-packycode-api dist-tags --prefer-online`; without `--prefer-online` a cached 404 can linger) and that `gh release view v<version>` shows the notes and the `dsh-packycode-api-<version>.tgz` asset. On an OIDC `package not found` error, fix the trusted publisher and re-run the same workflow. Do not move the tag. If the tagged tree itself must change, publish a new patch version.
103
116
 
104
- 8. Confirm that both jobs of the publish workflow succeeded for that tag before declaring the release public. Then check that npm serves the version and that `gh release view v<version>` shows the notes, the `dsh-packycode-api-<version>.tgz` asset and, for the newest version, the Latest mark. On an OIDC `package not found` error, fix the trusted publisher and re-run the same workflow. Do not move the tag. If the tagged tree itself must change, publish a new patch version.
117
+ The release reaches the update button only when `latest` moves. About 25–26 hours after publishing, confirm that a scheduled `promote` run moved `latest` and that `gh release list --json tagName,isLatest` marks the version Latest. If the promote job fails on the dist-tag, enable **Allow npm dist-tag** on the trusted publisher and run the workflow with `promote`. If no run happened, check that the scheduled workflow is enabled (GitHub disables schedules in a repository without activity for 60 days).
105
118
 
106
119
  If npm published but the release job failed, re-run only that failed job. If the Release must be made by hand, attach the tarball npm serves, not a local `.artifacts` build: CI packs its own, so the local bytes differ. Fetch it with `npm pack dsh-packycode-api@<version>`, and compare its SHA-512 with the registry's `dist.integrity`. Then run `gh release create v<version> <tarball> --verify-tag --title "<the notes' title>" --notes-file <the notes' body>`. The release job leaves an existing Release unchanged.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.4
4
+
5
+ - **Keys in `cc` send Claude requests through the official Claude Code CLI again; `cc-sale`, `cc-expensive`, `claude-officially` and every other group keep the native HTTP route.** PackyCode's `cc` group only serves the official client, so the route now reads the request key's groups and hands those Claude requests to this device's Claude Code CLI. The delisted `claude-sale` group is not included until it returns. Unlike the 1.2 delegation, the CLI is only the transport: it runs with all of its own tools disabled, DSH's system prompt appended to its own, and DSH's tools for the turn listed through a per-process MCP bridge on 127.0.0.1 as `mcp__dsh__<name>`. Every call the model makes is executed by DSH — permissions, sandbox, plan mode and its review, questions, todos, subagents, image generation and tool cards are the same as over HTTP — and the result answers the call the CLI is holding open. The request itself is built once for both routes (DSH history, attachments, signed thinking and tool schemas through pi-ai), and the CLI's raw stream events come back as the same Anthropic SSE stream, so usage, replay, error classification and key rotation are unchanged.
6
+ - **One live CLI process per conversation.** A DSH session keeps one CLI process per model: the next step's tool results and the user's next turn continue it, so its requests share one cacheable prefix. A request that does not continue it (compaction, a plan-mode switch, another key or effort) gets a process resumed from DSH's own history through a CLI transcript; a mid-turn resume uses the CLI's interrupted-turn continuation. Session titles and compaction run in one-shot processes. Idle processes stop after 20 minutes, at most eight stay alive, and all stop when the plugin unloads.
7
+ - **The CLI is isolated and adds no behavior of its own.** It runs with its own config home (`~/.dsh/packycode-claude-cli`), no user/project settings, CLAUDE.md, auto-memory, slash commands, session persistence, auto-update or telemetry, and with inherited `ANTHROPIC_*` / `CLAUDE_*` variables removed. Only the plugin's selected key reaches it, as `ANTHROPIC_AUTH_TOKEN`. Its own retries, model and refusal fallbacks and compaction are off: one DSH step is one API request and retries follow DSH's policy. MCP idle timeouts and auto-backgrounding are disabled so a call can wait on a user's approval or plan review (a 400-second hold was verified).
8
+ - **Settings → PackyCode → Claude Code CLI** is back: install status and version, a one-click npm install, the live-process counters, and the Claude route — by group (default), always CLI, or always HTTP. Every key that reaches Claude shows which route its Claude requests take. `claudeTransport` and `claudeCliPath` are volatile settings; the 1.2 `claudeCliMode` / `claudeCliCwd` and the `claude_code` delegation tool stay removed.
9
+ - **CLI failures say what to do, once.** A missing CLI, a broken launcher or an outdated CLI that rejects an option are explained without a status code, so DSH does not retry them twenty times. A request the official CLI sent that the gateway still refuses as a client anomaly gets its own explanation with the gateway's words; quota and invalid-key failures relayed by the CLI rotate keys exactly as over HTTP. Nothing ever switches route or provider by itself.
10
+ - Validation, 2026-10-17: the real Claude Code CLI 2.1.295 was probed against a local gateway for every flag and behavior relied on (MCP `_meta` tool-use ids, sequential held calls, `--resume <file>`, interrupted-turn resume, stream-json events with signature deltas, error reporting, retries off). Live with a `cc` key through the CLI: Opus, Sonnet and Haiku each passed text, a native tool roundtrip continuing the same process, image input, a Chinese title and plan mode (inventory lookup → `exit_plan_mode` → approval) — 15 of 15 checks. The full plugin wiring in `auto` read the key's groups (`cc`, …), routed Claude through the CLI and passed Haiku and Sonnet tool roundtrips; the same key pinned to HTTP still passed. Offline: tests for the route policy, transcript, tool names, MCP bridge, process lifecycle through a protocol-faithful fake CLI, plugin wiring and the settings card.
11
+ - **Back-to-back releases no longer hold `latest` back.** The promote job only looked at the version `next` names, so a release published before the previous one was 25 hours old reset the wait, and a day of iteration left `latest` where it was. It now promotes the newest version after `latest`, up to `next`, that has aged 25 hours, so `latest` trails the newest release by about a day. A version deprecated on npm is never promoted, which is how a broken release is held back. AGENTS.md gains a "Staged releases" section so anyone iterating knows releases go through `next` and must not be published or moved to `latest` by hand.
12
+ - **DSH 0.2.1 (pi-ai 1.1.0) compatibility.** DSH 0.2.1 asks the route for a key before it lists models, and lists none without one. That question now only checks the ring for a usable key: with two or more keys, listing no longer waits up to six seconds for the first group reading (which held up DSH's whole model catalog, since it lists every provider together) and no longer sends the group probes. pi-ai 1.1.0 also hands the provider a transcript whose system message declares the offered tools as `toolsAdded` instead of `context.tools`, so the one-time replay of the gateway's captured Haiku/Sonnet client-anomaly and test-message refusals stopped recognizing a first tool request; it recognizes both shapes again. Without a usable key DSH 0.2.1 shows no PackyCode models; adding, removing or auto-disabling a key refreshes its model list through DSH's credential-record event. Live on 2026-10-17 against the packages DeepSeek Harness 0.2.1-rc.2 ships, its patched pi-ai included: `scripts/live-check.mjs` passed for all ten models (Claude over HTTP), key rotation and the RPC projections, and `scripts/claude-check.mjs --transport cli` passed all 15 checks (text, tool, image, title and plan on Opus, Sonnet and Haiku).
13
+ - **The plugin declares and is tested on the DSH 0.2.1 host.** The peer ranges add `@deepseek-ai/cordis` 4.0.5 and `@earendil-works/pi-ai` 1.1.0, and `compatibility.json` records 0.2.1-rc.2 as the latest tested host. The development dependencies are now the packages DeepSeek Harness 0.2.1-rc.2 ships (dsh-* 0.2.1-rc.2, cordis 4.0.5, schemastery 3.18.5, pi-ai 1.1.0); the connection and chat-image tests also run on 0.2.0-rc.2 through the host fixture, and their DSH 0.1.5 rows are gone. Tests that call the provider directly now hand it the context pi-ai's own `Models` would, and the host fixtures follow 0.2.1's PTC runtime, browser-auth and Chat bundle changes.
14
+ - Validation, 2026-10-17: 456 offline tests on the DSH 0.2.1-rc.2 packages (0.2.0-rc.2 through the host fixture), both rebuilt bundles and the versioned tarball; the same suite also passed against the packages extracted from the desktop app. Live through the built route on those packages: `scripts/live-check.mjs` for all ten models, key rotation and the RPC projections, and `scripts/claude-check.mjs --transport cli` with a `cc` key, 15 of 15. The maintainer then tried this build in DeepSeek Harness 0.2.1-rc.2 through the linked desktop profile.
15
+
16
+ ## 1.3.3
17
+
18
+ - **The update button no longer bypasses pnpm's release-age policy.** It ran `pnpm add --config.minimumReleaseAge=0`, which switched the policy off for that one install, so pnpm never recorded the early version as an exception. The next plugin install in that profile then failed with `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION` on the PackyCode entry until the version was 24 hours old; a user's `dsh-chat-manager` install was refused this way. Reproduced with DSH's bundled pnpm, which ran as 11.7.0 with a 24-hour default window. The button now installs the exact version under the profile's own policy. By default pnpm installs it at once and adds `dsh-packycode-api@<version>` to the profile's `minimumReleaseAgeExclude`, and later installs pass. Under a stricter policy pnpm refuses, and the button reports the version as pending, with the time it clears, instead of failing.
19
+ - **Releases reach `latest` a day later.** A tag now publishes to npm's `next` tag. An hourly job moves `latest` once the version is 25 hours old, then marks its GitHub Release Latest. Update buttons up to 1.3.2 still pass the override and install whatever `latest` names, so `latest` must never name a version pnpm would still refuse. The promotion uses trusted publishing's dist-tag permission (npm CLI 11.21.0 or later) and installs no dependencies. A new version can still be installed sooner by entering its exact version on the Plugins page.
20
+ - Validation, 2026-10-17: 401 offline tests (new: the release-age refusal parser, a pending update, the version card's three outcomes, and the promotion rules and workflow shape), both rebuilt bundles and the versioned tarball. With DSH's bundled pnpm, running as 11.7.0, in scratch DSH homes, the plugin's own `update()` behaved as intended in three cases. Under the default policy it installed 1.3.2 at once, pnpm added `dsh-packycode-api@1.3.2` to `minimumReleaseAgeExclude`, and the next unrelated install passed. Under an explicit `minimumReleaseAge: 1440` it installed nothing and returned pending until 2026-10-17T19:16Z. In a profile the old button had blocked, the same update recorded the exception and unblocked it, after which `dsh-chat-manager@1.5.13` installed with its own exception. `scripts/promote-release.mjs --dry-run` read the registry and found nothing to promote. `scripts/live-check.mjs` passed for all ten models, key rotation and the RPC projections. No DSH restart.
21
+
3
22
  ## 1.3.2
4
23
 
5
24
  - **GPT-5.6 Sol joins the catalog.** `gpt-5.6-sol` takes GPT-6.1 Sol's route: `openai-responses` at `https://cf.api.fan/v1`, text and image input, 1,050,000 context and 128,000 output, per its developers.openai.com model page. PackyCode serves it to the same groups as GPT-6.1 Sol (`codex`, `codex-sale`, `azure-officially`). OpenAI documents `reasoning.effort` `none | low | medium | high | xhigh | max`, but `off` is not offered: through PackyCode, `none` and `minimal` both answered at effort `medium`. The model therefore gets GPT-6.1 Sol's levels, `low` to `max`. Its session titles run on GPT-6.1 Sol, as GPT-6 Astra's do, and the settings panel counts it under GPT.
package/README.en.md CHANGED
@@ -29,7 +29,7 @@ The `PackyCode` model group ships with the plugin. No YAML edits and no separate
29
29
  | **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 |
30
30
  | **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 |
31
31
  | **Visible balance** | Settings lists each key's remaining, used, limit, reset period, and a usage bar. The composer shows the active key's remaining balance |
32
- | **Claude inside the plugin** | All three Claude models use HTTP Messages with DSH tools, permissions and conversation history; no local CLI is needed |
32
+ | **Claude route by group** | The `cc` group only serves the official client, so Claude requests on `cc` keys go through this device's Claude Code CLI; `cc-sale`, `cc-expensive`, `claude-officially` and other groups use the plugin's HTTP route. Either way DSH runs the tools and permissions, and plan mode, attachments and the full history behave the same |
33
33
  | **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 |
34
34
  | **Image generation and editing** | Separate image keys, GPT Image 2 / 2.5 Flare / 2.5 Sunburst, model and quality settings, reference edits and original PNG storage; generation and editing verified against the gateway |
35
35
  | **Credentials stay on the machine** | Keys live in the DSH credential file. The browser receives labels, state, and balances only |
@@ -47,10 +47,10 @@ You need **DeepSeek Harness** and a **PackyCode (PackyAPI) API key**. For DSH se
47
47
  Open the desktop application's **Plugins** page, click **Add plugin**, and enter the package name **with a version** as the **package name or address**:
48
48
 
49
49
  ```text
50
- dsh-packycode-api@1.3.2
50
+ dsh-packycode-api@1.3.4
51
51
  ```
52
52
 
53
- Choose the **official** install source, then click **Install**. `1.3.2` was the latest release when this was written; the [npm page](https://www.npmjs.com/package/dsh-packycode-api) shows the current one.
53
+ Choose the **official** install source, then click **Install**. `1.3.4` was the latest release when this was written; the [npm page](https://www.npmjs.com/package/dsh-packycode-api) shows the current one.
54
54
 
55
55
  > **Always include the version.** With a bare `dsh-packycode-api`, the install prompt shows the newest version on npm, but DSH's bundled pnpm by default installs only versions published at least 24 hours ago, so you can end up several releases behind with no sign of it in the prompt. With an exact version, pnpm installs that version and adds it to the profile's `minimumReleaseAgeExclude` itself. Afterwards, check **Current version** under **Settings → PackyCode → Plugin version**.
56
56
 
@@ -71,7 +71,7 @@ Choose a model from the `PackyCode` group. All ten models use the plugin’s HTT
71
71
 
72
72
  The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
73
73
 
74
- **For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.2`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
74
+ **For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.4`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
75
75
 
76
76
  For a local checkout, keep the `link:` prefix: `dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`. **Do not pass a bare path or `file:`** — that install copies the whole checkout, `node_modules` included, into the profile, where it then shadows the packages the DSH installation itself provides and the plugin looks uninstalled; see the FAQ. If you already installed that way, uninstall first and reinstall as above.
77
77
 
@@ -89,7 +89,7 @@ A profile without a browser can still use the model route. It just has no settin
89
89
 
90
90
  **Read the balance.** Each key under **Accounts and keys** shows its own remaining, used, and limit (limit = remaining + used); an unlimited key shows **No quota limit**. Keys are not added up into a total. An API key can read only its own quota, not the PackyCode account's balance, so check the account balance in the PackyCode console. A key that could not be read says why on its own row; 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.
91
91
 
92
- **Use Claude.** Select any Claude model directly; no CLI installation is needed. Reasoning effort is sent to the model. DSH owns the history, attachments, tool loop, plan mode and permissions.
92
+ **Use Claude.** Select any Claude model. When the key is in the `cc` group, the plugin sends the request with this device's official Claude Code CLI, so install it first under **Settings → PackyCode → Claude Code CLI** (or run `npm install -g @anthropic-ai/claude-code`); keys in `cc-sale`, `cc-expensive`, `claude-officially` and other groups use the plugin's HTTP route and need no CLI. Under **Accounts and keys**, every key that reaches Claude is marked “Claude via official CLI” or “Claude via HTTP”, and the CLI card can pin either route. On both routes DSH executes every tool under its own permissions, and plan mode, questions, todos, subagents, attachments, reasoning effort and history behave as for any other model. See “Claude Code CLI route” below.
93
93
 
94
94
  **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:
95
95
 
@@ -125,12 +125,24 @@ PackyCode has no dedicated search endpoint: one search is one model call with a
125
125
  <summary>When a key is rotated</summary>
126
126
 
127
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 (an explicit Claude Code delegation 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.
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. The active key stays active for every other model. Only evidence counts: a key whose groups are not read yet is used as before.
129
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. The separate 403 saying the client is already authorized and should not send connectivity test messages is classified as `GATEWAY_TEST_MESSAGE`. It explains the refusal without disabling the key. Only Haiku tool context may replay it once under the bounds below.
130
130
  - When every stored key is disabled, the request fails and says so. There is no fallback credential.
131
131
 
132
132
  </details>
133
133
 
134
+ ### Claude Code CLI route
135
+
136
+ PackyCode's `cc` group (“claude code专用”) only serves the official Claude Code client. The plugin hands Claude requests on keys in that group to the real Claude Code CLI on this device. **The CLI is only the transport; DSH stays the agent:**
137
+
138
+ - **DSH executes every tool.** The CLI's own Bash, Read, Edit and other tools are all off. The tools DSH offers for the turn (including `exit_plan_mode`, `ask_user_question`, todos, subagents, MCP and image generation) reach the request as `mcp__dsh__<name>` through a local MCP bridge bound to 127.0.0.1. Each call the model makes comes back to DSH, with the same permission prompts, sandbox, plan review and tool cards as over HTTP, and its result goes to the CLI that is waiting for it.
139
+ - **DSH's prompt and history, unchanged.** DSH's system prompt is appended to the CLI's own; the history (text, images, signed thinking, tool calls and results) is written as a CLI transcript and resumed; the reasoning effort is passed as `--effort`.
140
+ - **One long-lived process per conversation.** A DSH session keeps one CLI process per model: tool results and the next user turn continue it, so requests keep one cacheable prefix. After compaction, a plan-mode switch or a key change, a new process is resumed from DSH's full history; session titles and compaction use one-shot processes. A process idle for 20 minutes stops; at most 8 stay alive.
141
+ - **Isolated from your own Claude Code.** It runs with its own config home, `~/.dsh/packycode-claude-cli`, and never reads `~/.claude` settings, CLAUDE.md, MCP configuration or logins; only the key stored in the plugin is passed to the child. Auto-update, telemetry, auto-compaction, auto-memory and model fallback are off, and retries are left to DSH's retry policy, which rotates keys.
142
+ - **Failures say why.** A missing CLI, an outdated CLI, and a request from the official CLI that the gateway still refuses each get their own explanation; nothing switches to another route or provider, and no key is disabled.
143
+
144
+ Known differences: starting a process takes about 0.3–1 s; when a process is resumed in the middle of a tool turn, the CLI adds “Continue from where you left off.” after the tool results, as the official client does. Requires Claude Code 2.1 or later (tested with 2.1.295). On Windows, Claude Code also needs Git for Windows.
145
+
134
146
  ### Image generation and editing
135
147
 
136
148
  Add the dedicated key under **Settings → PackyCode → Image generation and editing → Add an image key**. This credential record is separate from conversation keys. Image calls use only the selected image key. Adding one does not start a paid test.
@@ -164,7 +176,7 @@ Live checks on 2026-10-15 passed generation and editing at `low` on all three en
164
176
  | `grok-4.7` (Grok 4.7) | HTTP, Responses, text/image input; 500K context | Cannot be off; `low` / `medium` / `high` / `xhigh` | The Grok coverage read from the gateway in Settings |
165
177
  | `gemini-4-argon` (Gemini 4 Argon) | HTTP, native Gemini streaming, text/image input; 1,048,576 context, 65,536 output | Cannot be off; `low` / `medium` / `high` | `gemini-slb` |
166
178
  | `gemini-3.8-flash` (Gemini 3.8 Flash) | HTTP, native Gemini streaming, text/image input; 1,048,576 input, 65,536 output | Cannot be off; `low` / `medium` / `high` | `gemini-slb` |
167
- | `claude-opus-5-5` | HTTP, Anthropic Messages, images and native tools | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | Claude-group coverage reported in Settings |
179
+ | `claude-opus-5-5` | Anthropic Messages, images and native tools: through this device's Claude Code CLI for `cc` keys, HTTP otherwise | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | Claude-group coverage reported in Settings |
168
180
  | `claude-sonnet-5-5` | Same | Same | Same |
169
181
  | `claude-haiku-5-5` | Same; also generates Claude session titles | Same | Same |
170
182
 
@@ -183,25 +195,25 @@ Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit
183
195
  <details>
184
196
  <summary>Protocol, group and client compatibility</summary>
185
197
 
186
- - All ten models use HTTP inside the plugin. Claude keeps `anthropic-messages`, with a base URL without `/v1`; the SDK appends `/v1/messages`. Model ids, protocols, context limits and effort mappings are unchanged.
187
- - Claude requests use the live-verified CLI-compatible headers, bearer authentication, session metadata and identity system prefix while preserving DSH instructions, tool schemas, images and signed replay. The compatibility version is pinned to `2.1.295`; no local CLI is detected or started for a model turn.
188
- - The bearer comes only from the plugin’s stored key chosen for the model. No CLI login, external configuration, ambient key or alternate provider is used.
189
- - This compatibility envelope does not remove the gateway’s group restrictions. The key still needs Claude coverage. Client-policy refusals are explained without disabling a key or falling back to a CLI. Server changes may invalidate the envelope; see [PackyCode group policy](https://docs.packyapi.ai/docs/token/).
198
+ - Every model uses HTTP inside the plugin, except Claude requests on `cc` keys, which this device's Claude Code CLI sends. Claude keeps `anthropic-messages`, with a base URL without `/v1`; the SDK appends `/v1/messages`. Model ids, protocols, context limits and effort mappings are unchanged. Both routes share one request builder: DSH's history, attachments, tool schemas and signed replay become a Messages request first, then HTTP or the CLI sends it, and the CLI's raw stream events are parsed by the same protocol code.
199
+ - Claude requests over HTTP use the live-verified CLI-compatible headers, bearer authentication, session metadata and identity system prefix while preserving DSH instructions, tool schemas, images and signed replay. The compatibility version is pinned to `2.1.295`.
200
+ - The bearer comes only from the plugin’s stored key chosen for the model; the CLI route passes that same key to the child as `ANTHROPIC_AUTH_TOKEN` and clears inherited `ANTHROPIC_*` / `CLAUDE_*` variables. No CLI login, external configuration, ambient key or alternate provider is used.
201
+ - The key's groups decide the route (or the pin in the CLI card); a refusal never switches routes on its own. A key whose groups are not read yet uses HTTP, and a Claude request waits up to about 6 seconds for the first group reading. Client-policy refusals are explained without disabling a key. Server changes may invalidate the envelope; see [PackyCode group policy](https://docs.packyapi.ai/docs/token/).
190
202
  - Identical Haiku and Sonnet tool requests have been both refused with the captured client-anomaly 403 and accepted. The plugin waits one second and repeats the unchanged request once for that response. Haiku also covers the captured already-authorized/test-message 403, sharing the same one-replay limit. A second refusal remains visible. Opus, plain text, ordinary official-CLI-only refusals, 429s and accepted streams do not trigger this replay. Records retain the original failures; this does not establish a repair of the gateway's internal classifier.
191
203
  - Claude streams finish at `message_stop` without waiting for the gateway to close the HTTP body, and CRLF delimiters remain intact across network chunks. Stream errors and cancellation still surface; waiting for response headers is not counted as success.
192
- - Offered DSH bash/read/write/edit/glob/grep tools use canonical CLI names such as `Bash` and `Read` over HTTP; other tools use MCP names. Only aliases declared for the current request regain their original names before DSH dispatch; unknown names never grant execution. Arguments, call IDs, results and signed history remain intact. Thinking display follows the CLI defaults: `omitted` for Haiku and `updates` with its required beta for Sonnet and Opus. Reasoning effort and output limits remain unchanged, signatures on empty thinking blocks survive replay, and explicit display options take precedence.
204
+ - Offered DSH bash/read/write/edit/glob/grep tools use canonical CLI names such as `Bash` and `Read` over HTTP; other tools use MCP names. Through the CLI every DSH tool is offered as `mcp__dsh__<name>`, and the end of the system prompt says the CLI's own tools are unavailable. Only aliases declared for the current request regain their original names before DSH dispatch; unknown names never grant execution. Arguments, call IDs, results and signed history remain intact. Thinking display follows the CLI defaults: `omitted` for Haiku and `updates` with its required beta for Sonnet and Opus. Reasoning effort and output limits remain unchanged, signatures on empty thinking blocks survive replay, and explicit display options take precedence.
193
205
  - When the legacy working-directory sentence agrees with the initiating session metadata, Claude receives the exact path as a separate JSON string. Sentence punctuation stays outside the path; real trailing dots, Windows drive/UNC paths, spaces and backslashes remain intact. Commands are never trimmed or rewritten. Shell guidance prefers the current directory or `workdir`, preserving PowerShell syntax for Windows tools.
194
- - Native Claude turns retain the full DSH tool loop, permissions and plan mode, using host search and image tools. Claude titles use Haiku over HTTP; GPT-6 Astra and GPT-5.6 Sol titles use GPT-6.1 Sol.
195
- - The old `claude_code` delegation tool, CLI runner, detection/installation UI, configuration fields and local MCP bridges have been removed. Old `claudeCliPath`, `claudeCliMode` and `claudeCliCwd` settings no longer affect requests. Separately installed Claude Code remains untouched.
206
+ - Claude turns on both routes retain the full DSH tool loop, permissions and plan mode, using host search and image tools. Claude titles use Haiku on the same route as the conversation; GPT-6 Astra and GPT-5.6 Sol titles use GPT-6.1 Sol.
207
+ - The 1.2 `claude_code` delegation tool and the `claudeCliMode` / `claudeCliCwd` settings do not return: the CLI is now a transport and never replaces DSH's tools with its own. `claudeCliPath` applies again and names the CLI (empty means the usual install places, then PATH); `claudeTransport` is `auto`, `cli` or `http`. Separately installed Claude Code is never modified.
196
208
  - Codex Responses requests still need `prompt_cache_key`; the plugin supplies a missing session identity. Other Responses families omit it.
197
- - After building, `node scripts/claude-check.mjs` checks all three Claude models on order totals, support triage, replenishment, read-only inventory tool roundtrips, product color labels and Chinese titles, validating the business answers. `node scripts/claude-stability.mjs --start --hours 6 --key-label <label>` starts a deadline- and request-budget-limited window. Invoke the same script on a schedule to sample again; `--status` reads the record. Each round records its build and scenario version. A test-message policy refusal fails that check while the other scheduled checks continue; real credential or quota failures still stop the window. Checks never change stored credentials or start DSH.
209
+ - After building, `node scripts/claude-check.mjs` checks all three Claude models on order totals, support triage, replenishment, read-only inventory tool roundtrips, product color labels, Chinese titles and plan mode (read-only lookup → plan submitted with `exit_plan_mode` → approval), validating the business answers; `--transport cli` sends all of it through this device's Claude Code CLI. `node scripts/claude-stability.mjs --start --hours 6 --key-label <label>` starts a deadline- and request-budget-limited window. Invoke the same script on a schedule to sample again; `--status` reads the record. Each round records its build and scenario version. A test-message policy refusal fails that check while the other scheduled checks continue; real credential or quota failures still stop the window. Checks never change stored credentials or start DSH.
198
210
  - For continuous dialogs, use `node scripts/claude-conversation-check.mjs --model claude-haiku-5-5 --rounds 3 --key-label <label> --output .artifacts/claude-conversation.json`. One plugin adapter handles independent sessions; each group retains six user turns and full tool history, validates arithmetic, final JSON and unexpected characters, and stops on a failure without discarding evidence. Requests default to 65 seconds and 2048 output tokens. Diagnostic overrides use `--timeout-ms` (5000–300000) and `--max-tokens` (1024–128000). `--capture-wire` records the synthetic business requests, non-authentication headers and partial stream events for exact replay, without keys. Reports include scenario and adapter lifecycle, configured limits, response-header latency and total response time. These options do not alter the plugin's runtime settings.
199
211
 
200
212
  </details>
201
213
 
202
214
  ## Update and uninstall
203
215
 
204
- **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 from the **Plugins** page works too, again with the version in the package name (for example `dsh-packycode-api@1.3.2`); without it you may get a version that is merely the newest one published at least 24 hours ago.
216
+ **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. The install keeps pnpm's release-age policy instead of overriding it: by default pnpm installs the version at once and records it in the profile's `minimumReleaseAgeExclude`; under a stricter pnpm policy the button says when the update can run. A new version is published to npm's `next` tag first and becomes `latest` about 25 hours later; the button and installs without a version see it only then. To install it sooner, enter the exact version on the Plugins page. 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 from the **Plugins** page works too, again with the version in the package name (for example `dsh-packycode-api@1.3.4`); without it you may get a version that is merely the newest one published at least 24 hours ago.
205
217
 
206
218
  **Coming back from a local-path install, uninstall first.** An update is a `pnpm add`: it rewrites the files inside the package directory and leaves a `node_modules` that an earlier local-path install left there. That older tree keeps shadowing the packages the DSH installation provides, so the plugin row fails to load with no visible reason. **Uninstall** runs `pnpm remove`, which deletes the package directory outright — only a reinstall after that is clean. `node scripts/install-check.mjs` reports whether a profile is in that state.
207
219
 
@@ -217,7 +229,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
217
229
 
218
230
  Output means a leftover tree: a registry install ships no `node_modules`, so this one usually comes from an earlier local-path install (`file:`, or a bare path instead of `link:`). It sits inside the profile directory, and DSH resolves a plugin's imports from inside the profile first, so the plugin gets those old copies — for example `@deepseek-ai/schemastery` 3.18.2, which has no `Schema.volatile()`, so the module throws while it is imported. When an import fails the Loader creates no fiber for that row and no client bundle is composed either, so the model route, the RPC channel, and the settings panel all stay absent.
219
231
 
220
- Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.3.2`), then quit and reopen the desktop application. Uninstall runs `pnpm remove` and deletes the whole package directory; reinstalling or clicking **Update** only rewrites files and keeps the leftover tree. Deleting that `node_modules` by hand and restarting works too.
232
+ Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.3.4`), then quit and reopen the desktop application. Uninstall runs `pnpm remove` and deletes the whole package directory; reinstalling or clicking **Update** only rewrites files and keeps the leftover tree. Deleting that `node_modules` by hand and restarting works too.
221
233
 
222
234
  **Right after a first install, every card in Settings shows `transport failure for /packycode-api/…: HTTP 405`?** The installed version is 1.1.6 or earlier, usually because only the bare package name was entered: the install prompt shows the newest version, but pnpm installs the newest one published at least 24 hours ago (see **Install the plugin**). Those older versions can register their settings channel only after DSH restarts and rebuilds its `connection` service. So right after a first install `/packycode-api` does not exist yet, requests fall through to the static page, and it answers 405; the model route and the settings panel itself load normally. From 1.1.7 on, a first install needs no restart. Check which version was installed:
223
235
 
@@ -231,25 +243,27 @@ Recovery: quit and reopen the desktop application, and the old version works. Th
231
243
 
232
244
  **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.
233
245
 
234
- **Claude reports an official-client-only or client-anomaly refusal?** This is a gateway client-policy refusal, not proof of an invalid key. Check the selected model’s group coverage under **Accounts and keys** and in PackyCode’s model catalog, then collect diagnostics if a covered model is still refused. The plugin does not disable the key, switch providers or start a CLI for this refusal.
246
+ **Claude reports an official-client-only or client-anomaly refusal?** This is a gateway client-policy refusal, not proof of an invalid key. See whether the key is marked “Claude via HTTP” or “via official CLI”: a `cc` key should use the CLI, so if the CLI card is pinned to “Always HTTP”, set it back to “By group”. When a request the official CLI sent is still refused, the error carries the gateway's own words (with the request id); update Claude Code (`claude update`) and retry, then send it to PackyCode support if it persists. The plugin does not disable the key or switch providers.
247
+
248
+ **“Claude Code CLI is not available”?** The key is in `cc`, which needs the CLI on this device. Under **Settings → PackyCode → Claude Code CLI** click Install, or run `npm install -g @anthropic-ai/claude-code` and click Check again; set `claudeCliPath` for an unusual location. Otherwise use a `cc-sale` / `cc-expensive` key.
235
249
 
236
250
  **The balance does not match the account bill?** Settings shows only each key stored here, with its own quota, not other tokens on the same PackyCode account. An API key cannot read the account's balance; check it in the PackyCode console. A key that failed to read says so on its own row.
237
251
 
238
252
  **The balance reads `fetch failed` from `https://www.packyapi.com`?** That host, the default through 1.0.4, is DNS-poisoned on mainland networks and only answers through a proxy, so it works on and off. The default is now `https://www.packyapi.ai`. The model list is built into the plugin and never queried from PackyCode, so it is unaffected.
239
253
 
240
- **A Claude model stays on “thinking”?** Check DSH errors or diagnostics. HTTP 429/503 means gateway queueing or throttling and follows the host retry policy; client-policy refusals are explained directly. Native model turns never start a local CLI.
254
+ **A Claude model stays on “thinking”?** Check DSH errors or diagnostics. HTTP 429/503 means gateway queueing or throttling and follows the host retry policy; client-policy refusals are explained directly. Only Claude requests on the CLI route start the local Claude Code CLI, which is stopped and reported if it prints nothing for 90 seconds.
241
255
 
242
256
  **A rate limit disabled the key?** It should not. HTTP 429 does not rotate. Exhausted quota or HTTP 401 does; after a top-up, click **Re-enable**.
243
257
 
244
258
  **Installing from the Plugins page fails with `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION`?** By default pnpm accepts only versions published at least 24 hours ago and checks the whole profile lockfile. (Entering only this plugin's bare name does not raise this error; it silently installs an older version that has passed 24 hours instead, see **Install the plugin**.) The package it names is often another plugin that was just updated, not this one. Retry once it is 24 hours old, or add the reported `name@version` under `minimumReleaseAgeExclude` in the profile's `pnpm-workspace.yaml`. Once this plugin is installed, **Update plugin** in Settings is not subject to that check.
245
259
 
246
- **Do I still need the previously installed Claude Code?** This plugin no longer runs, installs or delegates to a CLI. Claude models use HTTP directly. Keep Claude Code if you use it separately; this plugin does not modify its installation or configuration.
260
+ **Do I need Claude Code installed?** Only for Claude models on a `cc` key (or with the CLI card pinned to “Always CLI”). The plugin runs it with its own config home and never modifies your Claude Code installation, login or settings; other groups and models do not need it.
247
261
 
248
262
  **Replies stop at the output token limit (已达到输出 token 上限), or requests keep failing — how do I report it?** Open **Settings → PackyCode → Diagnostics**, pick the session (the 20 most recent, newest first, labelled with their folder name), click **Generate diagnostics** and then **Copy**, and paste the text into an [Issue](https://github.com/BaronCyrus/dsh-packycode-api/issues) or send it to the plugin author. It holds settings and numbers only: the plugin version, the session id and start time, each key's position and groups (no labels, no keys), each request's model, reasoning effort and output limit, the latest steps' token usage and finish reasons, and retry and failure codes (messages redacted and shortened). It contains no conversation text, file paths or keys. Where copying is unavailable, the text is selected instead; press ⌘C or Ctrl+C.
249
263
 
250
264
  ## Security and scope
251
265
 
252
- This is a community plugin, not affiliated with or endorsed by DeepSeek or PackyCode. Model requests go to the PackyCode host you configured. Keeping credentials out of browser responses does not mean inference is local. Claude also uses the plugin’s native HTTP route.
266
+ This is a community plugin, not affiliated with or endorsed by DeepSeek or PackyCode. Model requests go to the PackyCode host you configured. Keeping credentials out of browser responses does not mean inference is local. Claude requests on `cc` keys are sent by a local Claude Code CLI child process: the key reaches it only through its environment, DSH's tools are offered through an MCP bridge bound to 127.0.0.1 behind a random capability, and the tools themselves still run in DSH on the host.
253
267
 
254
268
  Keys stay in the local DSH credential file (`~/.dsh/.credentials.yaml`, owner-only as enforced by DSH). They are not written into settings, logs, or the transcript. Balance requests run only on the host, with a timeout and redirects refused.
255
269
 
package/README.md CHANGED
@@ -29,7 +29,7 @@
29
29
  | **多 Key 按顺序使用** | 额度用尽或 Key 被拒绝时自动切到下一个可用 Key;失败的那一步由重试接手 |
30
30
  | **按分组选 Key** | 每个 Key 后面列出它所在的 PackyCode 分组和能用的模型;当前 Key 的分组用不了所选模型(例如没有 cc 分组却选了 Claude)时,这一轮自动改用分组合适的 Key |
31
31
  | **余额一眼可见** | 设置页列出每个 Key 的剩余、已用、额度、重置周期和用量条;输入框右侧显示当前 Key 的剩余额度 |
32
- | **Claude 插件内直连** | 三个 Claude 模型通过 HTTP Messages 调用,使用 DSH 的工具、权限和完整对话历史,无需本机 CLI |
32
+ | **Claude 按分组选通道** | `cc` 分组只接受官方客户端,这些 Key 的 Claude 请求由本机 Claude Code CLI 发出;`cc-sale`、`cc-expensive`、`claude-officially` 等分组走插件内 HTTP。两条通道都由 DSH 执行工具、审批权限,计划模式、附件和完整对话历史完全一致 |
33
33
  | **网页搜索可走 PackyCode** | 可选:DSH 的 `web_search` 改用 PackyCode 搜索,后端在 DeepSeek 与 GPT 之间切换;可只对 PackyCode 模型生效 |
34
34
  | **图片生成与编辑** | 独立图片 Key,支持 GPT Image 2 / 2.5 Flare / 2.5 Sunburst、型号与质量设置、参考图编辑和 PNG 原图保存;已通过网关生图与编辑实测 |
35
35
  | **凭据不离开本机** | Key 只存在 DSH 凭据文件里。浏览器只能看到备注、状态和余额,拿不到 Key |
@@ -47,10 +47,10 @@
47
47
  打开桌面应用的**插件**页,点击**添加插件**,在**包名或地址**中填入**带版本号**的包名:
48
48
 
49
49
  ```text
50
- dsh-packycode-api@1.3.2
50
+ dsh-packycode-api@1.3.4
51
51
  ```
52
52
 
53
- 在**安装源**中选择**官方源**,然后点击**安装**。`1.3.2` 是写这份说明时的最新版,当前最新版本号以 [npm 页面](https://www.npmjs.com/package/dsh-packycode-api) 为准。
53
+ 在**安装源**中选择**官方源**,然后点击**安装**。`1.3.4` 是写这份说明时的最新版,当前最新版本号以 [npm 页面](https://www.npmjs.com/package/dsh-packycode-api) 为准。
54
54
 
55
55
  > **一定要带版本号。** 只填 `dsh-packycode-api` 时,安装前的提示显示的是 npm 上的最新版,但 DSH 自带的 pnpm 默认只安装发布满 24 小时的版本,实际装上的可能是好几个版本之前的旧版,而安装提示里看不出这一点。填写精确版本时,pnpm 会直接安装这个版本,并自动把它加入 profile 的 `minimumReleaseAgeExclude`。装好后可以在 **设置 → PackyCode → 插件版本** 核对「当前版本」。
56
56
 
@@ -71,7 +71,7 @@ dsh-packycode-api@1.3.2
71
71
 
72
72
  桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
73
73
 
74
- **仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.2`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
74
+ **仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.4`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
75
75
 
76
76
  本地开发要加 `link:` 前缀:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。**不要直接写路径(或 `file:`)** —— 那样的安装会把整个仓库目录(连同 `node_modules`)复制进 profile,之后它会在 profile 内部挡住 DSH 自带的包,插件看起来像没装,见「常见问题」。已经那样装过的话,先卸载再按上面的方式重装。
77
77
 
@@ -89,7 +89,7 @@ dsh-packycode-api@1.3.2
89
89
 
90
90
  **看余额。** 「账户与 Key」里每个 Key 显示它自己的剩余、已用与额度(额度 = 剩余 + 已用),不限额的 Key 显示「不限额度」;多个 Key 不相加成合计。API Key 只能读到自己的额度,读不到 PackyCode 账号的总余额,账号余额请到 PackyCode 控制台查看。读失败的 Key 会在它那一行注明原因;某个 Key 这次读失败但之前读到过,会继续显示上次的余额并注明读取时间(输入框里显示为 `≈US$…`),重启 DSH 后才清空。输入框右侧只在选中 PackyCode 模型时显示当前 Key 的剩余额度。「刷新」会跳过约 60 秒的缓存。默认模型接口是 `https://cf.api.fan`,余额接口是 `https://www.packyapi.ai`,可在「高级:接口地址」里改。旧默认值 `https://www.packyapi.com` 在国内网络下 DNS 被污染、直连会 `fetch failed`,即使设置里保存的是它,插件也会先试 `.ai`。
91
91
 
92
- **用 Claude。** 直接选择三个 Claude 模型之一即可,无需安装 CLI。思考档位会发送给模型;对话历史、附件、工具调用、计划模式和权限均由 DSH 管理。
92
+ **用 Claude。** 直接选择三个 Claude 模型之一。Key 在 `cc` 分组时,插件用本机的官方 Claude Code CLI 发出请求,需要先在 **设置 → PackyCode → Claude Code CLI** 一键安装(或自行 `npm install -g @anthropic-ai/claude-code`);`cc-sale`、`cc-expensive`、`claude-officially` 等分组直接走插件内 HTTP,不需要 CLI。「账户与 Key」里每个能用 Claude 的 Key 会标出「Claude 经官方 CLI」或「Claude 经 HTTP」,也可以在 CLI 卡片里固定为「始终用 CLI」或「始终用 HTTP」。无论哪条通道,工具都由 DSH 执行并按 DSH 的权限审批,计划模式、提问、待办、子代理、附件、思考档位和历史都和其它模型一样。详见下方「Claude Code CLI 通道」。
93
93
 
94
94
  **网页搜索。** DSH 的 `web_search` 默认调用 DeepSeek 官方接口(用 `DEEPSEEK_API_KEY` 计费)。在 **设置 → PackyCode → 网页搜索** 里可以改走 PackyCode:
95
95
 
@@ -125,12 +125,24 @@ PackyCode 没有专用搜索接口:一次搜索就是一次带服务端搜索
125
125
  <summary>什么时候会自动切换 Key</summary>
126
126
 
127
127
  - **会切换并停用当前 Key:** 额度用尽(网关报额度、余额、insufficient quota 之类),或 Key 被拒绝(HTTP 401 / `无效的令牌`)。失败的那一步由 harness 重试,从而用上新 Key。
128
- - **按分组改用,不停用:** 分组读数显示当前 Key 用不了这一轮的模型、而另一个 Key 能用时,这一轮直接用那个 Key(Claude 轮次会在思考区写明「当前 Key「A」的分组不能用 …,本轮改用 Key「B」」)。「使用中」的 Key 不变,其它模型照常用它。只在有证据时才改用:分组还没读到的 Key 照旧使用。
128
+ - **按分组改用,不停用:** 分组读数显示当前 Key 用不了这一轮的模型、而另一个 Key 能用时,这一轮直接用那个 Key。「使用中」的 Key 不变,其它模型照常用它。只在有证据时才改用:分组还没读到的 Key 照旧使用。
129
129
  - **不会切换:** 限速(429);网关把上游渠道故障误报成 400(`当前令牌未覆盖供应商 …`);以及「只允许官方客户端」的 403。网关表示「客户端已授权,无需发送连通性测试消息」的 403 单独记为 `GATEWAY_TEST_MESSAGE`,直接说明请求被当作测试消息拒绝,不禁用 Key。仅 Haiku 工具上下文可按下方规则原样重发一次。
130
130
  - 全部 Key 都停用时,请求失败并说明没有可用 Key,不会回退到别的凭据。
131
131
 
132
132
  </details>
133
133
 
134
+ ### Claude Code CLI 通道
135
+
136
+ PackyCode 的 `cc` 分组(「claude code专用」)只接受官方 Claude Code 客户端。插件把这个分组的 Key 的 Claude 请求交给本机真实的 Claude Code CLI 发出,**CLI 只负责传输,Agent 仍然是 DSH**:
137
+
138
+ - **工具全部由 DSH 执行。** CLI 自带的 Bash、Read、Edit 等工具全部关闭;DSH 本轮提供的工具(包括 `exit_plan_mode`、`ask_user_question`、待办、子代理、MCP、生图等)通过只监听 127.0.0.1 的本机 MCP 桥,以 `mcp__dsh__<工具名>` 出现在请求里。模型发起的调用交回 DSH 执行,权限审批、沙箱、计划模式的审阅界面和工具卡片都与 HTTP 通道相同,结果再交给等待中的 CLI。
139
+ - **提示与历史保持 DSH 原样。** DSH 的系统提示追加在 CLI 官方提示之后;历史(文字、图片、带签名的思考、工具调用与结果)由插件写成 CLI 的会话记录后续接,思考档位以 `--effort` 传入。
140
+ - **一个会话一个常驻进程。** 同一个 DSH 会话、同一模型复用一个 CLI 进程:工具结果和下一轮消息直接续接,请求前缀不变,网关缓存照常命中。历史被压缩、切换计划模式或换 Key 时,用 DSH 的完整历史恢复一个新进程;会话标题和压缩用一次性进程。空闲 20 分钟的进程自动结束,最多同时保留 8 个。
141
+ - **与你自己的 Claude Code 隔离。** 使用独立配置目录 `~/.dsh/packycode-claude-cli`,不读取 `~/.claude` 的设置、CLAUDE.md、MCP 配置和登录状态;只把插件保存的 Key 交给子进程。自动更新、遥测、自动压缩、自动记忆和模型回退都关闭,失败重试交还给 DSH 的重试策略(会按 Key 轮换)。
142
+ - **出错会说清原因。** 未安装 CLI、CLI 版本过旧、官方 CLI 发出的请求仍被网关拒绝,分别给出说明和下一步,不会改走其它通道或提供商,也不会停用 Key。
143
+
144
+ 已知差异:新进程启动约 0.3–1 秒;在一轮工具调用中途恢复进程时,CLI 会按官方行为在工具结果后附上一句 “Continue from where you left off.”。需要 Claude Code 2.1 以上(实测 2.1.295)。Windows 上 Claude Code 还需要 Git for Windows。
145
+
134
146
  ### 图片生成与编辑
135
147
 
136
148
  在 **设置 → PackyCode → 图片生成与编辑 → 添加图片 Key** 填入专用 Key。它与「账户与 Key」里的对话 Key 分开保存;图片请求只用此处选择的 Key。添加 Key 不会自动发起付费测试。
@@ -164,7 +176,7 @@ Claude 与其它模型一样,通过 DSH 的 `packycode_image_generate` 工具
164
176
  | `grok-4.7`(Grok 4.7) | HTTP,Responses,可带图;500k 上下文 | 不能关;`low` / `medium` / `high` / `xhigh` | 以设置页从网关读取的 Grok 分组覆盖为准 |
165
177
  | `gemini-4-argon`(Gemini 4 Argon) | HTTP,Gemini 原生流式,可带图;1,048,576 上下文,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
166
178
  | `gemini-3.8-flash`(Gemini 3.8 Flash) | HTTP,Gemini 原生流式,可带图;1,048,576 输入,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
167
- | `claude-opus-5-5` | HTTP,Anthropic Messages,可带图和原生工具 | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | 以设置页读取的 Claude 分组覆盖为准 |
179
+ | `claude-opus-5-5` | Anthropic Messages,可带图和原生工具:`cc` 分组经本机 Claude Code CLI,其它分组走 HTTP | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | 以设置页读取的 Claude 分组覆盖为准 |
168
180
  | `claude-sonnet-5-5` | 同上 | 同上 | 同上 |
169
181
  | `claude-haiku-5-5` | 同上;Claude 会话标题固定走它 | 同上 | 同上 |
170
182
 
@@ -183,25 +195,25 @@ Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses
183
195
  <details>
184
196
  <summary>协议、分组与客户端兼容性</summary>
185
197
 
186
- - 十个模型均在插件内通过 HTTP 调用。Claude 使用 `anthropic-messages`,base URL 不带 `/v1`;SDK 会拼出 `/v1/messages`。模型、协议、上下文和档位映射保持不变。
187
- - Claude 请求带上实测通过的 CLI 兼容请求头、Bearer 认证、会话元数据和身份系统提示前缀,并保留原始 DSH 系统说明、工具 schema、附件及签名回放。兼容版本固定为 `2.1.295`,无需检测或启动本机 CLI。
188
- - Bearer 只来自插件保存并按模型选中的 Key;不读取 CLI 登录状态、外部配置或环境 Key,不切换其它提供商。
189
- - 这组兼容处理不代表服务端取消了分组的客户端限制。当前 Key 仍需覆盖 Claude;客户端政策拒绝会直接说明原因,不禁用 Key,也不自动改用 CLI。服务端规则变化可能使兼容处理失效,详见 [PackyCode 分组政策](https://docs.packyapi.ai/docs/token/)。
198
+ - 除 `cc` 分组 Key 的 Claude 请求经本机 Claude Code CLI 发出外,其余模型均在插件内通过 HTTP 调用。Claude 使用 `anthropic-messages`,base URL 不带 `/v1`;SDK 会拼出 `/v1/messages`。模型、协议、上下文和档位映射保持不变。两条通道共用同一套请求构建:DSH 历史、附件、工具 schema 和签名回放先由插件转换成 Messages 请求,再交给 HTTP 或 CLI 发送,CLI 返回的原始流事件按同一协议解析。
199
+ - 走 HTTP 的 Claude 请求带上实测通过的 CLI 兼容请求头、Bearer 认证、会话元数据和身份系统提示前缀,并保留原始 DSH 系统说明、工具 schema、附件及签名回放。兼容版本固定为 `2.1.295`。
200
+ - Bearer 只来自插件保存并按模型选中的 Key;CLI 通道把同一个 Key 以 `ANTHROPIC_AUTH_TOKEN` 交给子进程,并清除继承环境里的 `ANTHROPIC_*` / `CLAUDE_*` 变量。不读取 CLI 登录状态、外部配置或环境 Key,不切换其它提供商。
201
+ - 通道由当前 Key 的分组决定(或在 CLI 卡片里固定),不会因为某次拒绝而自动换通道。分组还没读到时按 HTTP 发送;带 Claude 的请求会先等待最多约 6 秒的首次分组读取。客户端政策拒绝会直接说明原因,不禁用 Key。服务端规则变化可能使兼容处理失效,详见 [PackyCode 分组政策](https://docs.packyapi.ai/docs/token/)。
190
202
  - Haiku 与 Sonnet 的工具请求都已出现「客户端异常」403 后相同请求成功的情况。插件只对这一特定响应等待 1 秒、原样重发一次;Haiku 另覆盖已捕获的「客户端已授权,无需发送测试消息」403,两种错误共用一次上限。再次拒绝仍会报错。Opus、纯文本请求、普通「只允许官方 CLI」拒绝、429 和已经开始的响应流不触发这次重发。原始失败保留在实测记录中;这不是网关内部判定已得到修复的证明。
191
203
  - Claude 流收到 `message_stop` 后即可完成,不再等网关关闭 HTTP 连接;跨分片的 CRLF 换行保持完整。流内错误与取消仍会返回,未收到响应头的等待也不会被当作成功。
192
- - DSH 的 bash/read/write/edit/glob/grep 在 HTTP 请求中使用已声明的 CLI 标准名称(如 `Bash`、`Read`),其它工具使用 MCP 命名;回到 DSH 时仅还原本次实际提供的工具名称,未知名称不会获得执行权限。工具参数、调用 id、结果和历史签名不变。思考展示采用 CLI 默认:Haiku 为 `omitted`,Sonnet / Opus 为 `updates`(附带相应 beta)。所选推理强度和输出上限不变,空思考块的签名仍完整回传;明确指定的显示模式优先。
204
+ - DSH 的 bash/read/write/edit/glob/grep 在 HTTP 请求中使用已声明的 CLI 标准名称(如 `Bash`、`Read`),其它工具使用 MCP 命名;经 CLI 时所有 DSH 工具都以 `mcp__dsh__<名称>` 提供,并在系统提示末尾说明 CLI 自带工具不可用。回到 DSH 时仅还原本次实际提供的工具名称,未知名称不会获得执行权限。工具参数、调用 id、结果和历史签名不变。思考展示采用 CLI 默认:Haiku 为 `omitted`,Sonnet / Opus 为 `updates`(附带相应 beta)。所选推理强度和输出上限不变,空思考块的签名仍完整回传;明确指定的显示模式优先。
193
205
  - Claude 请求中的旧工作目录句式会在与当前会话元数据一致时转换为单独的 JSON 路径,避免把提示语的句号复制进路径。真实目录名中的点、Windows 盘符、UNC、空格和反斜杠保持不变;命令参数不会被自动修剪。命令工具优先使用当前目录或 `workdir`,Windows 的 PowerShell 工具保持其原有语法。
194
- - 普通 Claude 对话保留完整 DSH 工具循环、权限及计划模式;网页搜索和生图使用宿主工具。会话标题改由 `claude-haiku-5-5` 通过 HTTP 生成;GPT-6 Astra 与 GPT-5.6 Sol 的标题交给 GPT-6.1 Sol。
195
- - 已移除旧 `claude_code` 委派工具、CLI 运行器、检测与安装界面、配置字段及本机 MCP 桥。旧配置中的 `claudeCliPath`、`claudeCliMode` 和 `claudeCliCwd` 不再生效;用户独立安装的 Claude Code 不受影响。
206
+ - 两条通道的 Claude 对话都保留完整 DSH 工具循环、权限及计划模式;网页搜索和生图使用宿主工具。会话标题由 `claude-haiku-5-5` 生成(与对话走同一条通道);GPT-6 Astra 与 GPT-5.6 Sol 的标题交给 GPT-6.1 Sol。
207
+ - 1.2.x 的 `claude_code` 委派工具和 `claudeCliMode`、`claudeCliCwd` 设置不再恢复:CLI 现在只是传输通道,不再用自带工具代替 DSH 执行任务。`claudeCliPath` 重新生效,用于指定 CLI 位置(留空则按常见安装位置和 PATH 查找);`claudeTransport` 为 `auto` / `cli` / `http`。用户独立安装的 Claude Code 不会被改动。
196
208
  - Codex 的 Responses 请求仍需要 `prompt_cache_key`;插件补上缺失的会话标识,其它 Responses 模型省略该字段。
197
- - 构建后可用 `node scripts/claude-check.mjs` 验证三个 Claude 模型的订单核算、工单归类、库存补货、只读库存查询工具回传、商品色卡标注和中文标题,并核对业务答案。`node scripts/claude-stability.mjs --start --hours 6 --key-label <备注名>` 开始有截止时间和请求上限的采样;定时执行同一脚本继续采样,用 `--status` 查看记录。业务场景版本和每轮构建分别记录;测试消息拒绝将对应检查记为失败,其余预定检查继续,真实凭据或额度错误仍会停止整个窗口。测试不改变已保存的凭据,也不启动 DSH。
209
+ - 构建后可用 `node scripts/claude-check.mjs` 验证三个 Claude 模型的订单核算、工单归类、库存补货、只读库存查询工具回传、商品色卡标注、中文标题和计划模式(只读查询 → `exit_plan_mode` 提交计划 → 审批),并核对业务答案;加 `--transport cli` 则全部经本机 Claude Code CLI 发出。`node scripts/claude-stability.mjs --start --hours 6 --key-label <备注名>` 开始有截止时间和请求上限的采样;定时执行同一脚本继续采样,用 `--status` 查看记录。业务场景版本和每轮构建分别记录;测试消息拒绝将对应检查记为失败,其余预定检查继续,真实凭据或额度错误仍会停止整个窗口。测试不改变已保存的凭据,也不启动 DSH。
198
210
  - 连续对话可用 `node scripts/claude-conversation-check.mjs --model claude-haiku-5-5 --rounds 3 --key-label <备注名> --output .artifacts/claude-conversation.json`:同一插件适配器处理独立会话,每组保留六轮对话和完整工具历史,检查追问中的数值一致性、最终 JSON 和异常字符;遇到失败停止该批次并保留记录。单请求默认 65 秒、输出上限 2048 token;诊断可通过 `--timeout-ms`(5000–300000)和 `--max-tokens`(1024–128000)显式调整。`--capture-wire` 记录这组模拟业务的完整请求、非认证头和中断前的流事件,用于精确重放;不记录 Key。报告包含场景版本、适配器生命周期、所用上限、首响应和完整响应耗时。这些选项不修改插件的运行参数。
199
211
 
200
212
  </details>
201
213
 
202
214
  ## 更新与卸载
203
215
 
204
- **更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.3.2`),否则装上的可能是发布满 24 小时的旧版。
216
+ **更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。安装遵守 pnpm 的发布冷却策略,不会绕过它:默认情况下 pnpm 会直接装上这个版本,并把它记进 profile 的 `minimumReleaseAgeExclude`;如果你给 pnpm 配了更严格的策略,按钮会提示什么时候可以更新。新版本先发布到 npm 的 `next` 标签,约 25 小时后才成为 `latest`,按钮和不带版本号的安装都要到那时才会看到它;等不及可以在插件页填写精确版本安装。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.3.4`),否则装上的可能是发布满 24 小时的旧版。
205
217
 
206
218
  **从本地路径安装切回官方源时,必须先卸载。** 「更新」是 `pnpm add`,它只重写包目录里的文件,不会删除更早一次本地路径安装留在包目录内的 `node_modules`;那份旧依赖会继续挡住 DSH 自带的包,插件行加载失败且看不出原因。「卸载」执行的是 `pnpm remove`,会删掉整个包目录,重装才是干净的。用 `node scripts/install-check.mjs` 可以确认当前 profile 是否处于这个状态。
207
219
 
@@ -217,7 +229,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
217
229
 
218
230
  有输出就是残留:从 npm 安装的包不会自带 `node_modules`,这一份通常来自更早一次「本地路径」安装(`file:` 或直接写路径,而不是 `link:`)。它位于 profile 目录内部,而 DSH 解析插件导入时优先使用 profile 内的副本,所以插件拿到的仍是那份旧依赖 —— 例如 `@deepseek-ai/schemastery` 3.18.2 没有 `Schema.volatile()`,模块在导入阶段就抛错。导入失败时 Loader 不会为这一行创建 fiber,客户端 bundle 也不会被组合,于是模型路由、RPC 通道和设置页全都不会出现。
219
231
 
220
- 修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.3.2`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
232
+ 修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.3.4`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
221
233
 
222
234
  **首次安装后,设置页每张卡片都显示 `transport failure for /packycode-api/…: HTTP 405`?** 实际装上的是 1.1.6 或更早的版本,通常是因为安装时只填了包名:安装提示显示的是最新版,pnpm 实际装的却是发布满 24 小时的旧版(见「安装插件」)。这些旧版要等 DSH 重启、重建它的 `connection` 服务之后,才能注册设置通道。所以首次安装后 `/packycode-api` 通道还不存在,请求落到静态页面,返回 405;模型路由和设置页本身照常加载。1.1.7 起首次安装不需要重启。先确认装上的版本:
223
235
 
@@ -229,13 +241,15 @@ grep '"version"' ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/package.
229
241
 
230
242
  **某个模型不可用,但 Key 是有效的?** 看「账户与 Key」里这个 Key 的分组和 ✓ / ✕:分组不覆盖这个模型时,换一个分组合适的 Key,或在 PackyCode 控制台给 Key 加上对应分组。存了分组合适的 Key 时,插件会自动用它跑这个模型。分组拒绝不会停用 Key。
231
243
 
232
- **Claude 报「只接受官方 Claude Code CLI」或「客户端存在异常」?** 这是网关的客户端政策拒绝,不等于 Key 失效。先在「账户与 Key」和 PackyCode 模型广场确认所选模型的分组覆盖;确认覆盖后仍被拒绝,可用诊断信息定位。插件不会因此禁用 Key、切换提供商或启动 CLI。
244
+ **Claude 报「只接受官方 Claude Code CLI」或「客户端存在异常」?** 这是网关的客户端政策拒绝,不等于 Key 失效。看这个 Key 下方标的是「Claude 经 HTTP」还是「经官方 CLI」:`cc` 分组的 Key 应该走 CLI,若 CLI 卡片被固定成了「始终用 HTTP」,改回「按分组自动」。经官方 CLI 发出的请求仍被拒时,错误里会附上网关原文(含 request id),先把 Claude Code 更新到最新版(`claude update`)再试,仍失败可发给 PackyCode 客服。插件不会因此禁用 Key 或切换提供商。
245
+
246
+ **提示「本机没有可用的 Claude Code CLI」?** 当前 Key 在 `cc` 分组,需要本机 CLI。到 **设置 → PackyCode → Claude Code CLI** 点「安装」,或在终端执行 `npm install -g @anthropic-ai/claude-code` 后点「重新检测」;装在非常规位置时可在配置里填 `claudeCliPath`。不想装的话,换用 `cc-sale` / `cc-expensive` 分组的 Key。
233
247
 
234
248
  **余额对不上账号账单?** 设置页只显示这里列出的每个 Key 自己的额度,不含同账号下没添加到本插件的其它令牌。API Key 读不到账号的总余额,请到 PackyCode 控制台查看。个别 Key 读失败时会在它那一行注明。
235
249
 
236
250
  **余额显示 `读取失败: https://www.packyapi.com: fetch failed`?** 这是 1.0.4 及更早版本的默认余额接口在国内网络下被 DNS 污染,开了代理才能连上,所以有时正常、有时失败。新版本默认改用 `https://www.packyapi.ai`。模型列表是插件内置的,不需要向 PackyCode 查询,不受影响。
237
251
 
238
- **选了 Claude 模型一直显示「深度求索中」?** 查看 DSH 的错误或诊断信息。429/503 表示网关排队或限流,按宿主重试策略处理;客户端政策拒绝会直接说明原因。普通模型对话不会启动本机 CLI。
252
+ **选了 Claude 模型一直显示「深度求索中」?** 查看 DSH 的错误或诊断信息。429/503 表示网关排队或限流,按宿主重试策略处理;客户端政策拒绝会直接说明原因。只有走 CLI 通道的 Claude 请求会启动本机 Claude Code CLI,它在 90 秒内毫无输出会被结束并报告。
239
253
 
240
254
  **限速之后 Key 被停用了?** 不该发生。429 不会切换 Key。额度用尽或 401 才会停用;充值后点「重新启用」。
241
255
 
@@ -243,13 +257,13 @@ grep '"version"' ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/package.
243
257
 
244
258
  **网页搜索报「no stored API key's group covers …」?** 所选搜索后端的模型不在你的令牌分组里(或网关暂时把它路由到了别的供应商)。在 **设置 → PackyCode → 网页搜索** 换一个后端,或添加分组覆盖它的 Key。
245
259
 
246
- **以前安装的 Claude Code 还需要吗?** 本插件已经移除 CLI 运行、安装和委派功能,Claude 模型直接通过 HTTP 调用。独立使用 Claude Code 的用户可继续保留它;本插件不会改动其安装或配置。
260
+ **需要安装 Claude Code 吗?** 只有 Key 在 `cc` 分组、又要用 Claude 模型时需要(或在 CLI 卡片里固定了「始终用 CLI」)。插件用独立配置目录运行它,不改动你自己的 Claude Code 安装、登录和设置;其它分组和其它模型都不需要。
247
261
 
248
262
  **回答被截断(「已达到输出 token 上限」)、请求反复失败,怎么反馈?** 打开 **设置 → PackyCode → 诊断信息**,选出问题的会话(最近 20 个,按时间倒序,标着所在文件夹名),点「生成诊断信息」再点「复制」,把内容贴到 [Issue](https://github.com/BaronCyrus/dsh-packycode-api/issues) 或发给插件作者。内容只有设置和数字:插件版本、会话 id 与创建时间、每个 Key 的序号与分组(不含备注名和 Key 本身)、每次请求用的模型、推理档位和输出上限、最近若干步的 token 用量与结束原因,以及重试和失败的错误码(错误信息已脱敏并截短)。不含对话内容、文件路径和 Key。复制不可用时,文本会被全选,按 ⌘C 或 Ctrl+C 即可。
249
263
 
250
264
  ## 安全边界
251
265
 
252
- 本项目是社区插件,与 DeepSeek、PackyCode 无隶属或背书关系。模型请求会发到你配置的 PackyCode 接口;凭据不返回浏览器,不代表推理在本地完成。Claude 也通过插件内的 HTTP 路由调用。
266
+ 本项目是社区插件,与 DeepSeek、PackyCode 无隶属或背书关系。模型请求会发到你配置的 PackyCode 接口;凭据不返回浏览器,不代表推理在本地完成。`cc` 分组的 Claude 请求由本机 Claude Code CLI 子进程发出:Key 只以环境变量交给它,DSH 工具通过只监听 127.0.0.1、带随机凭证的 MCP 桥提供,工具本身仍由 DSH 在宿主侧执行。
253
267
 
254
268
  Key 存在本机 DSH 凭据文件(`~/.dsh/.credentials.yaml`,权限由 DSH 设为仅所有者可读),不进设置文档、日志或对话记录。余额请求只在宿主侧发出,带超时并禁止重定向。
255
269
 
package/SECURITY.md CHANGED
@@ -13,12 +13,21 @@
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 plugin's pi-ai adapter. Protocol-specific compatibility headers are applied host-side; credentials remain scoped to the selected plugin key.
15
15
 
16
- ## Native Claude requests
16
+ ## Claude requests
17
17
 
18
- - Claude uses the plugin's HTTP Messages route. The plugin has no Claude CLI runner, installer, delegation tool or local MCP bridge. Existing standalone Claude Code installations and configuration are untouched.
19
- - The compatibility headers and session metadata do not load a CLI login or external configuration. Bearer authentication uses only the plugin's selected conversation key; images keep their separate credential record.
20
- - DSH owns tool execution, permissions, plan mode, attachments and history. Wire tool aliases are restored only for tools offered on that request; unknown aliases are not decoded into host tools. Saved images from older CLI-backed sessions remain readable without executing their old tools.
21
- - Captured client-policy refusals do not disable keys or switch providers. The specific pre-stream anomalies covered by the native transport may replay the exact bytes and credential once; ordinary policy refusals, HTTP 429 and accepted streams are excluded. Cancellation aborts the replay.
18
+ - Claude requests use one of two transports, chosen by the request key's PackyCode groups (or the user's pin): keys in `cc` send them through this device's official Claude Code CLI; every other key uses the plugin's HTTP Messages route. Both carry the same payload; neither has any fallback to the other, to another provider, or to another credential.
19
+ - The HTTP route's compatibility headers and session metadata do not load a CLI login or external configuration. Bearer authentication uses only the plugin's selected conversation key; images keep their separate credential record.
20
+ - DSH owns tool execution, permissions, plan mode, attachments and history on both routes. Wire tool aliases are restored only for tools offered on that request; unknown aliases are not decoded into host tools. Saved images from older CLI-backed sessions remain readable without executing their old tools.
21
+ - Captured client-policy refusals do not disable keys or switch providers or transports. The specific pre-stream anomalies covered by the HTTP route may replay the exact bytes and credential once; ordinary policy refusals, HTTP 429 and accepted streams are excluded. Cancellation aborts the replay.
22
+
23
+ ## The Claude Code CLI transport
24
+
25
+ - The CLI is a transport, not an agent: it runs with all of its own tools disabled (`--tools ""`), so it cannot read, write or execute anything itself. The tools it can see are the ones DSH offered for that request, served by an MCP bridge; every call is answered with the result DSH produced after running the tool under its own permissions and sandbox.
26
+ - The bridge is a per-process Streamable HTTP server bound to `127.0.0.1` on a random port. It requires a random 256-bit capability on every request, rejects foreign `Host` and `Origin` headers, and closes with its process. The capability lives only in an owner-only (`0600`) config file inside an owner-only run directory under the CLI home; it never appears in a command line.
27
+ - The CLI runs with an isolated config home (`~/.dsh/packycode-claude-cli`), `--setting-sources ""`, `--strict-mcp-config`, `--disable-slash-commands`, `--no-session-persistence`, and CLAUDE.md, auto-memory, auto-update, telemetry and non-essential traffic disabled. Inherited `ANTHROPIC_*`, `CLAUDE_*`, `MCP_*`, `DISABLE_*` and `ENABLE_*` variables are removed, so the user's own Claude Code login, API key, base URL, model and settings never reach it.
28
+ - The request key reaches the child only as `ANTHROPIC_AUTH_TOKEN` in its environment, with the endpoint as `ANTHROPIC_BASE_URL`; it is never written to a file, an argument, a transcript or a log. The DSH system prompt and the conversation transcript the CLI resumes are written to that owner-only run directory and removed when the process ends; directories left by a crashed host are removed after a day.
29
+ - The CLI's own retries, model fallback, refusal fallback and compaction are disabled: one DSH step is one API request, and retries follow DSH's policy. Processes are stopped on cancellation, after 20 idle minutes, beyond eight live conversations, and when the plugin unloads.
30
+ - Installing the CLI from the settings panel runs `npm install -g @anthropic-ai/claude-code` with constant arguments; on Windows the npm launcher is resolved to the program it forwards to, so no shell parses user text.
22
31
 
23
32
  ## Reporting
24
33
 
@@ -1,13 +1,15 @@
1
1
  {
2
- "latestTested": "0.1.5-rc.2",
2
+ "latestTested": "0.2.1-rc.2",
3
3
  "acceptedRuntimeRange": ">=0.1.5-rc.1 <0.3.0-0",
4
4
  "supported": [
5
5
  "0.1.5-rc.2",
6
- "0.2.0-rc.2"
6
+ "0.2.0-rc.2",
7
+ "0.2.1-rc.2"
7
8
  ],
8
- "piAi": "0.85.1",
9
+ "piAi": "1.1.0",
9
10
  "validation": {
10
11
  "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; 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
+ "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.",
13
+ "0.2.1-rc.2": "The development host since 2026-10-17: devDependencies pin the packages DeepSeek Harness 0.2.1-rc.2 ships (dsh-* 0.2.1-rc.2, cordis 4.0.5, schemastery 3.18.5, pi-ai 1.1.0), and the client and connection tests also run on 0.2.0-rc.2 through the host-v020 fixture. Before the switch the whole suite also passed against the packages extracted from the desktop app's app.asar. What changed for this plugin: PiAiAdapter.listModels resolves the key first and lists nothing on MISSING_CREDENTIAL; pi-ai 1.x Models folds systemPrompt and tools into a leading system message (toolsAdded) before the provider sees a request; dsh-tools runs PTC programs on ptcRuntime with workingDirectory; Connection admission asks browser auth for authenticateApi; credential-record changes refresh the browser's model catalog. Through the real host adapter, request bodies matched the pi-ai 0.85 baseline for 9 of 10 models (deepseek-flash's tool loses strict: false) and only SDK version headers differ. Not yet checked live or in a running DSH."
12
14
  }
13
15
  }