dsh-packycode-api 1.1.3 → 1.1.5
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 +19 -0
- package/README.en.md +14 -4
- package/README.md +14 -4
- package/lib/client.js +118 -0
- package/lib/index.js +627 -229
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.5
|
|
4
|
+
|
|
5
|
+
- Added `grok-4.7`, displayed as **Grok 4.7**, to the PackyCode `openai-responses` route at `https://cf.api.fan/v1`. [The official Grok 4.7 guide](https://docs.x.ai/developers/grok-4-7) declares a 500,000-token context, text/image input, text output, and `low` / `medium` / `high` / `xhigh` reasoning (default `high`, no off or max). There is no separate documented text-output cap; the numeric pi-ai ceiling is 500,000, bounded by remaining context per request. Only GPT Responses requests keep `prompt_cache_key`: the gateway's Grok route rejects this field with a misleading `Incorrect API key provided` error. Non-GPT Responses requests use pi-ai's `cacheRetention: 'none'` to omit it; the model, protocol, effort and plugin-owned credential stay the same.
|
|
6
|
+
- Settings now has a model checkbox list with Select all / Select none. All models, including newly added catalog entries, are visible by default. The profile stores exclusions, and DSH's picker refreshes when they change. Hiding a model does not prevent an existing session or prepared turn from using it.
|
|
7
|
+
- Fixed saving Auto web search on DSH 0.2 (including the reported macOS Intel installation): require `@deepseek-ai/schemastery` 3.18.4+, which declares volatile preference fields. The previous 3.18.2 dependency silently left them non-volatile, so DSH 0.2 refused the write. All RPC failures now carry Connection's required `code`, `message`, and empty `details`, preserving a safe error instead of `connection: invalid server-response failure`.
|
|
8
|
+
- The live checker now reads only the plugin-owned `packycode-api/keys` credential record, selecting the active usable key or `--key-label LABEL`; legacy references and environment credentials never substitute for it. Invalid YAML produces a safe error without quoting secret-bearing source lines, and checks never modify the stored record.
|
|
9
|
+
- Validation: 276 offline tests, both regenerated bundles and the versioned tarball; built settings UI checkbox/all/none/save-failure checks; the bundled DSH 0.2 Connection parser accepts the fixed failure. Built live gateway checks answered through DeepSeek Flash, GPT-6.1 Sol, GPT-6 Astra and Grok 4.7 and passed key rotation and secret-free projections. All four Grok efforts returned `ok` with a plugin-stored Grok-group key; xhigh first encountered a transient upstream 400 and succeeded on an identical retry. The user confirmed the local desktop build passed testing. The physical Intel desktop UI has not been retested.
|
|
10
|
+
|
|
11
|
+
## 1.1.4
|
|
12
|
+
|
|
13
|
+
- **Carry the reviewed plan into the next CLI turn.** The plan previously existed only in `exit_plan_mode` arguments, which the delegated transcript dropped; a fresh CLI then saw only “Plan approved”. The latest complete plan and its matching review result are now recovered from durable native or adapter-generated PTC tool calls, so approval, revision, resume and fork keep their constraints. They are reserved before trimming older chat; an oversized plan/request fails explicitly rather than silently losing constraints. Unrelated tool arguments and arbitrary PTC programs are not forwarded.
|
|
14
|
+
- **Use the actual CLI permission protocol.** Plan runs now request `--permission-prompt-tool stdio` as well as `--permission-mode plan`, making `ExitPlanMode` available to the official print-mode CLI and routing its permission request to the host. A targeted ask rule prevents an existing `ExitPlanMode` allow rule from skipping the handoff. Repeated permission refusals are bounded, including native plan-mode tool-result denials, without counting ordinary execution errors or counting one refusal twice.
|
|
15
|
+
- **Plans belong to one invocation.** Each plan run creates one private temporary planning-artifact directory inside its working directory (the official CLI rejects custom plan directories outside the project root), and supplies it through additive `plansDirectory` settings while preserving the credential/config home and other permission/MCP settings. It never changes project code or project settings; an unwritable workspace fails explicitly rather than using a shared fallback. File fallback and submitted paths are restricted to that directory; shared/global plans and a prior run's recent file are never candidates. Owned resources are cleaned on completion, cancellation, startup failure and unload.
|
|
16
|
+
- **Review follows the host's tool presentation.** Native catalogs receive `exit_plan_mode`; PTC catalogs receive the real `run_code` transport calling `tools.exit_plan_mode`. Missing review transports fail closed instead of inventing an unavailable direct tool call.
|
|
17
|
+
- **Leave plan mode at the correct boundary.** Model preparation can follow a selected pending mode, but `claude_code` execution reads the currently committed mode. Approval's pending exit no longer gives later tools in the same batch edit/full permission before the next accepted step.
|
|
18
|
+
- **Secret-free release preflight.** The OIDC diagnostic now prints only identity claims and the registry exchange status, never the returned credential body, and fails when the publisher is rejected. Truncating a body before regex redaction could previously expose a token prefix. Added regressions for long credential bodies and the registry's successful `201` response. Tag publication now packs and publishes the verified versioned tarball, matching the locally inspected artifact rather than independently repacking the workspace.
|
|
19
|
+
- Added `scripts/cli-plan-check.mjs`, a real-official-CLI/local-fake-gateway check for stdio handoff, private plan files, concurrent ownership, native denials, MCP search and cleanup. It uses no real credentials or quota and does not start DSH.
|
|
20
|
+
- Validation: full offline suite, both bundles and the versioned tarball; official CLI 2.1.295 against local fixtures passed stdio plan handoff, existing-allow-rule interception, concurrent/private plan ownership, native denial limits, MCP and cleanup, plus the existing read-only/edit/full search checks. Actual bundled DSH 0.2.0-rc.2 plan/tool controllers and its Node PTC subprocess passed 50 offline assertions. The built HTTP route answered through `deepseek-flash`, `gpt-6.1-sol` and `gpt-6-astra`, and key rotation and secret-free RPC projections passed the gateway check. The available key does not cover Claude: actual Claude upstream and a running desktop DSH UI integration are not claimed.
|
|
21
|
+
|
|
3
22
|
## 1.1.3
|
|
4
23
|
|
|
5
24
|
- **Plan review when the CLI has no ExitPlanMode.** Print mode often does not offer that tool, so a plan-mode turn wrote `~/.dsh/packycode-claude-cli/plans/*.md` and came back as ordinary text. A plan file written during the run is now submitted through DSH's `exit_plan_mode` when the CLI returns. An older file in that directory is ignored.
|
package/README.en.md
CHANGED
|
@@ -25,6 +25,7 @@ The `PackyCode` model group ships with the plugin. No YAML edits and no separate
|
|
|
25
25
|
| Capability | What it does |
|
|
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
|
+
| **Choose visible models** | All models are selected by default in Settings. Unchecking a model immediately hides it from the picker; existing sessions can keep using it |
|
|
28
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 |
|
|
29
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 |
|
|
30
31
|
| **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 |
|
|
@@ -44,7 +45,7 @@ Open the desktop application's **Plugins** page, click **Add plugin**, and enter
|
|
|
44
45
|
dsh-packycode-api
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.1.
|
|
48
|
+
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.1.5`.
|
|
48
49
|
|
|
49
50
|
Then **manually restart the desktop application**. Refreshing the page does not reload the plugin inside the Host process.
|
|
50
51
|
|
|
@@ -56,14 +57,14 @@ Open **Settings → PackyCode**. Paste the API key under **Add a key**. A label
|
|
|
56
57
|
|
|
57
58
|
### 3. Select a model
|
|
58
59
|
|
|
59
|
-
Choose a model from the `PackyCode` group. `deepseek-flash`, `gpt-6.1-sol`,
|
|
60
|
+
Choose a model from the `PackyCode` group. `deepseek-flash`, `gpt-6.1-sol`, `gpt-6-astra`, and `grok-4.7` use HTTP. The three Claude models also need the Claude Code CLI on this device; see below. Your key's groups must cover the selected model.
|
|
60
61
|
|
|
61
62
|
<details>
|
|
62
63
|
<summary>Verify the install / other profiles / a local checkout</summary>
|
|
63
64
|
|
|
64
65
|
The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
|
|
65
66
|
|
|
66
|
-
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.
|
|
67
|
+
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.5`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
|
|
67
68
|
|
|
68
69
|
A local checkout can be linked with `dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`.
|
|
69
70
|
|
|
@@ -73,6 +74,8 @@ A profile without a browser can still use the model route. It just has no settin
|
|
|
73
74
|
|
|
74
75
|
## Daily use
|
|
75
76
|
|
|
77
|
+
**Choose visible models.** Under **Settings → PackyCode → Visible models**, check the models you want in the picker. All are selected by default, with Select all / Select none controls. The selection is saved in the current profile and applies immediately; unchecking a model does not interrupt an existing session. New catalog models are selected by default.
|
|
78
|
+
|
|
76
79
|
**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.
|
|
77
80
|
|
|
78
81
|
**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`.
|
|
@@ -138,12 +141,17 @@ Claude Code CLI → temporary local MCP web_search → selected PackyCode DeepSe
|
|
|
138
141
|
| `deepseek-flash` | HTTP, chat/completions | Can be off; `low` / `high` / `max` | `deepseek-officially`, `deepseek-sale` |
|
|
139
142
|
| `gpt-6.1-sol` | HTTP, Responses, images allowed | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | `codex`, `codex-sale` |
|
|
140
143
|
| `gpt-6-astra` | Same | Same | Same |
|
|
144
|
+
| `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 |
|
|
141
145
|
| `claude-opus-5-5` | Local Claude Code CLI | The picker offers the same levels; the CLI chooses the actual effort | A group the official CLI is allowed to use; see below |
|
|
142
146
|
| `claude-sonnet-5-5` | Same | Same | Same |
|
|
143
147
|
| `claude-haiku-5-5` | Same; session titles always use this model | Same | Same |
|
|
144
148
|
|
|
145
149
|
A model in the list does not mean the current token group allows it. When changing groups, do not drop the group that serves the model you still want.
|
|
146
150
|
|
|
151
|
+
Grok 4.7's context and reasoning levels follow the [official guide](https://docs.x.ai/developers/grok-4-7). There is no separate text-output limit; the plugin uses the context size as its numeric output ceiling, bounded by the remaining context per request.
|
|
152
|
+
|
|
153
|
+
Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit it by default. This gateway's Grok route reports a misleading `Incorrect API key provided` error when the field is present.
|
|
154
|
+
|
|
147
155
|
<details>
|
|
148
156
|
<summary>Protocol, group, and CLI constraints</summary>
|
|
149
157
|
|
|
@@ -153,7 +161,7 @@ A model in the list does not mean the current token group allows it. When changi
|
|
|
153
161
|
- The `cc` group accepts only the official Claude CLI. A normal API client gets `403 This API endpoint is only accessible via the official Claude CLI`. This plugin runs the local `claude` binary for that group instead of switching the token to `cc-sale` or pretending to be the official client. If the token has no group the official CLI may use, the three Claude models still will not run.
|
|
154
162
|
- Selecting a Claude model when the CLI is missing fails that turn with an install hint. It does not surface as a missing binary name alone.
|
|
155
163
|
- The CLI receives this plugin's endpoint and current key (`ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN`) and an isolated config directory, `~/.dsh/packycode-claude-cli`. Without that isolation, `env` in `~/.claude/settings.json` would override the plugin key.
|
|
156
|
-
- CLI permission follows the session by default: `read-only` stays read-only, `workspace-write` may edit, and `danger-full-access` with approval disabled may use `full`. The CLI cannot ask the DSH user for approval, so a session that still asks never gets `full`. `claudeCliMode` can pin `read-only`, `edit`, or `full`. DSH plan mode wins over that mapping and over the pin: the CLI runs with `--permission-mode plan
|
|
164
|
+
- CLI permission follows the session by default: `read-only` stays read-only, `workspace-write` may edit, and `danger-full-access` with approval disabled may use `full`. The CLI cannot ask the DSH user for approval, so a session that still asks never gets `full`. `claudeCliMode` can pin `read-only`, `edit`, or `full`. DSH plan mode wins over that mapping and over the pin: the CLI runs read-only with `--permission-mode plan --permission-prompt-tool stdio`, using one private hidden temporary plan directory inside the working directory per invocation (the CLI rejects custom plan directories outside the project root). This stores only planning artifacts, changes no project code or settings, and fails explicitly for an unwritable workspace. `ExitPlanMode` hands the plan to DSH for review; it never starts implementation in that CLI process. File fallback reads only this invocation's directory, which is cleaned afterwards. Review works in native and PTC tool modes, and the complete plan and review feedback reach the next turn; exceeding the conversation budget fails explicitly instead of silently truncating constraints. Approval or `/plan off` takes effect from the next step; delegations in the same tool batch keep the current plan state. Session titles stay out of plan mode. An empty `claudeCliPath` is looked up automatically; an empty `claudeCliCwd` uses the session workspace.
|
|
157
165
|
- Thinking and tool activity (`▸ Bash`, `▸ Read`, and `✗` on failure) appear in that turn's reasoning block. They are progress, not DSH tool calls. The CLI's own tools are outside the DSH sandbox.
|
|
158
166
|
- The reasoning block opens with `▸ 启动 Claude Code CLI…` and then shows the CLI's version and model once it is up. When the gateway throttles or is busy (429, 503 `账号并发排队已满` / per-account wait queue full, and similar), the CLI retries up to 10 times on its own; each retry now shows as `↻ 网关返回 HTTP 503,CLI 自动重试 n/10` instead of looking hung. When the retries run out, the turn says PackyCode's Claude upstream is busy, not that the key is bad. A CLI that prints nothing for 2 minutes after starting is stopped and reported.
|
|
159
167
|
- The conversation and its images reach the CLI over stdin (`--input-format stream-json`), not argv: a Windows command line is length-limited and cannot carry an image. Images in the conversation are sent to Claude as images, at the HTTP route's request size (at most 2048×2048, about 1 MiB each; at most 20 images and 20 MiB per turn, dropping the oldest first). An image left out is named with its read-only local copy, which the CLI can open with Read.
|
|
@@ -214,6 +222,8 @@ pnpm run check
|
|
|
214
222
|
|
|
215
223
|
`node scripts/cli-search-check.mjs` runs the real CLI against a local test gateway and MCP bridge; Claude responses and search data are mocked by default, with no quota consumed. It checks all three permission modes, native-search exclusion and temporary-resource cleanup. `--live --backend deepseek` or `--live --backend gpt` sends the read-only mode's one search to the real backend (edit/full remain mocked). This does not verify actual Claude upstream turns or a running DSH integration.
|
|
216
224
|
|
|
225
|
+
`node scripts/cli-plan-check.mjs` checks plan mode with the real CLI and a local fake gateway: stdio handoff, concurrent plan ownership, invocation-only file fallback, stale-plan exclusion, actual denial limits and cleanup. It reads no real credentials, spends no quota and starts no DSH instance.
|
|
226
|
+
|
|
217
227
|
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.
|
|
218
228
|
|
|
219
229
|
## License
|
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
| 能力 | 使用体验 |
|
|
26
26
|
| --- | --- |
|
|
27
27
|
| **自带模型路由** | 安装后模型选择器出现 `PackyCode` 分组,不需要配置 provider,也不用改 `cordis.patch.yml` |
|
|
28
|
+
| **自选显示的模型** | 设置页的模型列表默认全选;取消勾选后,切换列表立即隐藏该模型,已有会话仍可继续使用 |
|
|
28
29
|
| **多 Key 按顺序使用** | 额度用尽或 Key 被拒绝时自动切到下一个可用 Key;失败的那一步由重试接手 |
|
|
29
30
|
| **按分组选 Key** | 每个 Key 后面列出它所在的 PackyCode 分组和能用的模型;当前 Key 的分组用不了所选模型(例如没有 cc 分组却选了 Claude)时,这一轮自动改用分组合适的 Key |
|
|
30
31
|
| **余额一眼可见** | 设置页列出每个 Key 的剩余、已用、额度、重置周期和用量条,并给出这些 Key 的合计;输入框右侧显示当前 Key 的剩余额度 |
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
dsh-packycode-api
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.1.
|
|
52
|
+
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.1.5`。
|
|
52
53
|
|
|
53
54
|
完成后**手动重启桌面应用**。仅刷新页面不会重新加载 Host 里的插件。
|
|
54
55
|
|
|
@@ -60,14 +61,14 @@ dsh-packycode-api
|
|
|
60
61
|
|
|
61
62
|
### 3. 选择模型
|
|
62
63
|
|
|
63
|
-
回到对话,在模型选择器里选 `PackyCode` 分组下的模型。`deepseek-flash`、`gpt-6.1-sol` 和 `
|
|
64
|
+
回到对话,在模型选择器里选 `PackyCode` 分组下的模型。`deepseek-flash`、`gpt-6.1-sol`、`gpt-6-astra` 和 `grok-4.7` 通过 HTTP 调用;三个 Claude 模型还需要本机装有 Claude Code CLI,见下文。Key 的分组必须覆盖所选模型。
|
|
64
65
|
|
|
65
66
|
<details>
|
|
66
67
|
<summary>检查安装 / 其它 profile / 本地目录</summary>
|
|
67
68
|
|
|
68
69
|
桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
|
|
69
70
|
|
|
70
|
-
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.
|
|
71
|
+
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.5`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
|
|
71
72
|
|
|
72
73
|
本地开发可以用绝对路径:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。
|
|
73
74
|
|
|
@@ -77,6 +78,8 @@ dsh-packycode-api
|
|
|
77
78
|
|
|
78
79
|
## 日常使用
|
|
79
80
|
|
|
81
|
+
**选择显示的模型。** 在 **设置 → PackyCode → 显示的模型** 中勾选要在切换列表里看到的模型,默认全选,也支持「全选」和「全不选」。选择保存在当前 profile,修改后立即生效;取消勾选不会中断已有会话。后续新增的模型默认勾选。
|
|
82
|
+
|
|
80
83
|
**换 Key。** Key 按添加顺序使用。点「切到此 Key」指定优先使用的 Key,下一轮就生效;「重命名」改备注(最多 60 字,不能为空);充值或更换后点「重新启用」。
|
|
81
84
|
|
|
82
85
|
**看分组。** 「账户与 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`。
|
|
@@ -142,12 +145,17 @@ Claude Code CLI → 本机临时 MCP web_search → 插件选定的 DeepSeek/GPT
|
|
|
142
145
|
| `deepseek-flash` | HTTP,chat/completions | 可关;`low` / `high` / `max` | `deepseek-officially`、`deepseek-sale` |
|
|
143
146
|
| `gpt-6.1-sol` | HTTP,Responses,可带图 | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | `codex`、`codex-sale` |
|
|
144
147
|
| `gpt-6-astra` | 同上 | 同上 | 同上 |
|
|
148
|
+
| `grok-4.7`(Grok 4.7) | HTTP,Responses,可带图;500k 上下文 | 不能关;`low` / `medium` / `high` / `xhigh` | 以设置页从网关读取的 Grok 分组覆盖为准 |
|
|
145
149
|
| `claude-opus-5-5` | 本机 Claude Code CLI | 选择器档位同上,实际档位由 CLI 决定 | 能给官方 CLI 用的分组,比如cc分组 |
|
|
146
150
|
| `claude-sonnet-5-5` | 同上 | 同上 | 同上 |
|
|
147
151
|
| `claude-haiku-5-5` | 同上;会话标题也固定走它 | 同上 | 同上 |
|
|
148
152
|
|
|
149
153
|
列表里看得到模型,不代表当前令牌分组一定放行。换分组时不要把对应分组一起去掉。
|
|
150
154
|
|
|
155
|
+
Grok 4.7 的上下文与推理档位来自 [官方文档](https://docs.x.ai/developers/grok-4-7)。官方没有单独限制文字输出长度;插件将输出上限设为上下文大小,并按每次请求的剩余上下文收紧。
|
|
156
|
+
|
|
157
|
+
Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses 模型默认省略。当前网关的 Grok 路由会将这个字段误报成 `Incorrect API key provided`。
|
|
158
|
+
|
|
151
159
|
<details>
|
|
152
160
|
<summary>协议、分组和 CLI 的具体约束</summary>
|
|
153
161
|
|
|
@@ -157,7 +165,7 @@ Claude Code CLI → 本机临时 MCP web_search → 插件选定的 DeepSeek/GPT
|
|
|
157
165
|
- `cc`(Claude Code 专用)只接受官方 Claude CLI。用普通 API 客户端调 Claude 会 403:`This API endpoint is only accessible via the official Claude CLI`。本插件因此调用本机 `claude`,而不是改去 `cc-sale`,也不是伪造客户端身份。令牌如果没有开放给官方 CLI 的分组,这三个模型仍然不可用。
|
|
158
166
|
- 选了 Claude 模型但本机没有 CLI 时,这一回合会直接说明未安装,并指向设置页的安装按钮。
|
|
159
167
|
- CLI 使用插件配置的地址和当前 Key(`ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN`),以及独立配置目录 `~/.dsh/packycode-claude-cli`。不隔离的话,`~/.claude/settings.json` 里的 `env` 会盖掉插件 Key。
|
|
160
|
-
- CLI 权限默认跟随当前会话:`read-only` → 只读,`workspace-write` → 可编辑,`danger-full-access` 且不再审批 → `full`。CLI 不能向 DSH 弹审批,所以只要会话还要审批,就不会给它 `full`。也可把设置项 `claudeCliMode` 固定成 `read-only` / `edit` / `full`。DSH 计划模式优先于该映射和这个固定档:CLI 以 `--permission-mode plan`
|
|
168
|
+
- CLI 权限默认跟随当前会话:`read-only` → 只读,`workspace-write` → 可编辑,`danger-full-access` 且不再审批 → `full`。CLI 不能向 DSH 弹审批,所以只要会话还要审批,就不会给它 `full`。也可把设置项 `claudeCliMode` 固定成 `read-only` / `edit` / `full`。DSH 计划模式优先于该映射和这个固定档:CLI 以 `--permission-mode plan --permission-prompt-tool stdio` 只读探索,每轮在工作目录内创建一个独占的隐藏临时计划目录(CLI 不接受工作区外的自定义计划目录);只存计划工件,不改项目代码或设置,工作区不可写时明确失败。`ExitPlanMode` 交给 DSH 审阅,不允许 CLI 在同一进程开始实施;只在本轮目录中查找未显式提交的计划,结束时清理。原生和 PTC 工具模式都支持审阅,完整计划及审阅反馈会带到下一轮;超过对话预算会明确报错,不静默截断约束。批准或 `/plan off` 在下一步生效,同一工具批次内的委派仍遵守当前计划状态。会话标题不进入计划模式。`claudeCliPath` 留空则自动查找;`claudeCliCwd` 留空则用当前会话工作区。
|
|
161
169
|
- 思考过程和工具调用(`▸ Bash`、`▸ Read`,失败时带 `✗`)会出现在这一回合的思考区。它们是进度,不是 DSH 的工具调用。CLI 自己的工具不受 DSH 沙箱约束。
|
|
162
170
|
- 思考区第一行是「▸ 启动 Claude Code CLI…」,CLI 起来后显示版本和模型。网关限流或繁忙(429、503 `账号并发排队已满` 等)时,CLI 会自己重试最多 10 次,每次重试都会显示成 `↻ 网关返回 HTTP 503,CLI 自动重试 n/10`,不再像卡住。重试用完仍失败时,会说明是 PackyCode 的 Claude 上游繁忙,不是 Key 的问题。CLI 启动后 2 分钟内没有任何输出会被停止并报告原因。
|
|
163
171
|
- 对话记录和图片通过 stdin 交给 CLI(`--input-format stream-json`),不走命令行参数:Windows 命令行长度有上限,而且命令行带不了图片。对话里的图片会以图片形式发给 Claude(与 HTTP 路由相同的压缩规格:每张不超过 2048×2048、约 1 MiB;一回合最多 20 张、合计 20 MiB,超出时先舍弃最早的)。没随消息发出的图片会写明本机只读副本的路径,CLI 需要时可以用 Read 读取。
|
|
@@ -218,6 +226,8 @@ pnpm run check
|
|
|
218
226
|
|
|
219
227
|
Claude CLI 的 MCP 接口验证用 `node scripts/cli-search-check.mjs`:运行真实 CLI,普通 Claude 响应和搜索数据默认由本地测试服务模拟,不消费额度;会检查三种权限模式、原生搜索禁用和临时资源清理。加 `--live --backend deepseek` 或 `--live --backend gpt` 会让只读模式的那一次搜索走真实后端(其它两种模式仍模拟)。这不等于验证了真实 Claude 上游对话或运行中的 DSH 集成。
|
|
220
228
|
|
|
229
|
+
计划模式另用 `node scripts/cli-plan-check.mjs` 验证:真实 CLI 对接本地模拟网关,检查 stdio 计划提交、并发计划归属、本轮文件回退、旧计划隔离、实际拒绝次数上限和清理;不读取真实凭据、不消费额度、不启动 DSH。
|
|
230
|
+
|
|
221
231
|
使用 Agent 修改或发布时,先读 [AGENTS.md](AGENTS.md)。发布由推送 `v*` 标签触发 [Publish workflow](.github/workflows/publish.yml),不要在本机执行 `npm publish`。
|
|
222
232
|
|
|
223
233
|
## License
|
package/lib/client.js
CHANGED
|
@@ -84,6 +84,14 @@ window.__ModuleLoader__.load({
|
|
|
84
84
|
groupsMiss: "不可用",
|
|
85
85
|
groupsPartial: "部分",
|
|
86
86
|
groupsStale: "分组为上次读取的结果",
|
|
87
|
+
modelsTitle: "显示的模型",
|
|
88
|
+
modelsIntro: "切换模型时只显示已勾选的 PackyCode 模型,默认全选。修改后立即生效;已有会话仍可继续使用原模型。",
|
|
89
|
+
modelsChecking: "正在读取模型设置…",
|
|
90
|
+
modelsFailed: "模型设置读取或保存失败",
|
|
91
|
+
modelsAll: "全选",
|
|
92
|
+
modelsNone: "全不选",
|
|
93
|
+
modelsCount: "已选 {count} / {total}",
|
|
94
|
+
modelsEmpty: "未勾选任何模型,切换列表将隐藏 PackyCode 的模型。",
|
|
87
95
|
advanced: "高级:接口地址",
|
|
88
96
|
apiHost: "模型接口地址",
|
|
89
97
|
statusHost: "余额接口地址",
|
|
@@ -162,6 +170,14 @@ window.__ModuleLoader__.load({
|
|
|
162
170
|
};
|
|
163
171
|
const en = {
|
|
164
172
|
nav: "PackyCode",
|
|
173
|
+
modelsTitle: "Visible models",
|
|
174
|
+
modelsIntro: "Only selected PackyCode models appear when switching models. All are selected by default. Changes apply immediately; existing sessions can keep their current model.",
|
|
175
|
+
modelsChecking: "Reading model settings…",
|
|
176
|
+
modelsFailed: "Could not read or save model settings",
|
|
177
|
+
modelsAll: "Select all",
|
|
178
|
+
modelsNone: "Select none",
|
|
179
|
+
modelsCount: "{count} / {total} selected",
|
|
180
|
+
modelsEmpty: "No models selected. PackyCode models are hidden from the picker.",
|
|
165
181
|
intro: "Paste a PackyCode (PackyAPI) API key to use the built-in model route. Keys are used in order: an exhausted or rejected key hands over to the next one, and a key whose groups cannot reach the selected model is passed over for one whose groups can.",
|
|
166
182
|
accountsTitle: "Accounts and keys",
|
|
167
183
|
refresh: "Refresh",
|
|
@@ -364,6 +380,13 @@ window.__ModuleLoader__.load({
|
|
|
364
380
|
.pkc-reach>span[data-state=no]{color:var(--dsw-alias-label-caption);text-decoration:line-through}
|
|
365
381
|
.pkc-add{display:flex;flex-direction:column;gap:8px;padding-top:14px;border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
366
382
|
|
|
383
|
+
.pkc-models{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin:0;padding:0;min-width:0;border:0}
|
|
384
|
+
.pkc-model{display:flex;align-items:center;gap:10px;min-width:0;padding:10px 12px;border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-interactive-bg-hover);cursor:pointer}
|
|
385
|
+
.pkc-model>input{flex:none;width:16px;height:16px;margin:0;accent-color:var(--dsw-alias-state-business-primary)}
|
|
386
|
+
.pkc-model>span{display:flex;flex-direction:column;min-width:0;gap:2px}
|
|
387
|
+
.pkc-model b{font-size:13px;font-weight:500}
|
|
388
|
+
.pkc-models:disabled .pkc-model{opacity:.5;cursor:not-allowed}
|
|
389
|
+
|
|
367
390
|
.pkc-setting{display:grid;grid-template-columns:112px minmax(0,1fr);gap:6px 16px;align-items:start}
|
|
368
391
|
.pkc-setting>.pkc-label{padding-top:5px}
|
|
369
392
|
.pkc-setting>div{display:flex;flex-direction:column;gap:6px;min-width:0}
|
|
@@ -389,6 +412,7 @@ window.__ModuleLoader__.load({
|
|
|
389
412
|
@media (max-width:640px){
|
|
390
413
|
.pkc-stats{grid-template-columns:1fr}
|
|
391
414
|
.pkc-setting{grid-template-columns:1fr}
|
|
415
|
+
.pkc-models{grid-template-columns:1fr}
|
|
392
416
|
.pkc-setting>.pkc-label{padding-top:0}
|
|
393
417
|
}
|
|
394
418
|
`;
|
|
@@ -473,6 +497,7 @@ window.__ModuleLoader__.load({
|
|
|
473
497
|
const MODEL_FAMILIES = [
|
|
474
498
|
["DeepSeek", ["deepseek-flash"]],
|
|
475
499
|
["GPT", ["gpt-6.1-sol", "gpt-6-astra"]],
|
|
500
|
+
["Grok", ["grok-4.7"]],
|
|
476
501
|
["Claude", [
|
|
477
502
|
"claude-opus-5-5",
|
|
478
503
|
"claude-sonnet-5-5",
|
|
@@ -1006,6 +1031,95 @@ window.__ModuleLoader__.load({
|
|
|
1006
1031
|
]
|
|
1007
1032
|
});
|
|
1008
1033
|
}
|
|
1034
|
+
function ModelsCard({ call, t }) {
|
|
1035
|
+
const [state, setState] = react.useState();
|
|
1036
|
+
const [busy, setBusy] = react.useState(false);
|
|
1037
|
+
const [error, setError] = react.useState();
|
|
1038
|
+
react.useEffect(() => {
|
|
1039
|
+
let live = true;
|
|
1040
|
+
call("models/status").then((value) => {
|
|
1041
|
+
if (live) setState(value);
|
|
1042
|
+
}).catch((failure) => {
|
|
1043
|
+
if (live) setError(clearMessage(failure));
|
|
1044
|
+
});
|
|
1045
|
+
return () => {
|
|
1046
|
+
live = false;
|
|
1047
|
+
};
|
|
1048
|
+
}, [call]);
|
|
1049
|
+
const models = state?.models ?? [];
|
|
1050
|
+
const selectedIds = models.filter((model) => model.selected).map((model) => model.id);
|
|
1051
|
+
const disabled = busy || state?.writable !== true;
|
|
1052
|
+
const update = (ids) => {
|
|
1053
|
+
if (disabled) return;
|
|
1054
|
+
setBusy(true);
|
|
1055
|
+
setError(void 0);
|
|
1056
|
+
call("models/update", { selectedIds: ids }).then(setState).catch((failure) => setError(clearMessage(failure))).finally(() => setBusy(false));
|
|
1057
|
+
};
|
|
1058
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, {
|
|
1059
|
+
title: t("modelsTitle"),
|
|
1060
|
+
aside: state !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1061
|
+
className: "pkc-meta",
|
|
1062
|
+
children: fill(t("modelsCount"), {
|
|
1063
|
+
count: selectedIds.length,
|
|
1064
|
+
total: models.length
|
|
1065
|
+
})
|
|
1066
|
+
}),
|
|
1067
|
+
children: [
|
|
1068
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1069
|
+
className: "pkc-note",
|
|
1070
|
+
children: t("modelsIntro")
|
|
1071
|
+
}),
|
|
1072
|
+
state === void 0 && error === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1073
|
+
className: "pkc-note",
|
|
1074
|
+
role: "status",
|
|
1075
|
+
children: t("modelsChecking")
|
|
1076
|
+
}),
|
|
1077
|
+
state !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1078
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1079
|
+
className: "pkc-actions",
|
|
1080
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1081
|
+
disabled: disabled || selectedIds.length === models.length,
|
|
1082
|
+
onClick: () => update(models.map((model) => model.id)),
|
|
1083
|
+
children: t("modelsAll")
|
|
1084
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1085
|
+
disabled: disabled || selectedIds.length === 0,
|
|
1086
|
+
onClick: () => update([]),
|
|
1087
|
+
children: t("modelsNone")
|
|
1088
|
+
})]
|
|
1089
|
+
}),
|
|
1090
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("fieldset", {
|
|
1091
|
+
className: "pkc-models",
|
|
1092
|
+
"aria-label": t("modelsTitle"),
|
|
1093
|
+
disabled,
|
|
1094
|
+
children: models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1095
|
+
className: "pkc-model",
|
|
1096
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1097
|
+
type: "checkbox",
|
|
1098
|
+
checked: model.selected,
|
|
1099
|
+
onChange: (event) => update(event.target.checked ? [...selectedIds, model.id] : selectedIds.filter((id) => id !== model.id))
|
|
1100
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: model.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1101
|
+
className: "pkc-meta pkc-mono",
|
|
1102
|
+
children: model.id
|
|
1103
|
+
})] })]
|
|
1104
|
+
}, model.id))
|
|
1105
|
+
}),
|
|
1106
|
+
selectedIds.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1107
|
+
className: "pkc-note",
|
|
1108
|
+
children: t("modelsEmpty")
|
|
1109
|
+
}),
|
|
1110
|
+
state.writable !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1111
|
+
className: "pkc-note",
|
|
1112
|
+
children: t("searchNotWritable")
|
|
1113
|
+
})
|
|
1114
|
+
] }),
|
|
1115
|
+
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1116
|
+
className: "pkc-error",
|
|
1117
|
+
role: "alert",
|
|
1118
|
+
children: `${t("modelsFailed")}: ${error}`
|
|
1119
|
+
})
|
|
1120
|
+
]
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1009
1123
|
/** Web search routing and backend, written through the host. */
|
|
1010
1124
|
function SearchCard({ call, t }) {
|
|
1011
1125
|
const [state, setState] = react.useState();
|
|
@@ -1469,6 +1583,10 @@ window.__ModuleLoader__.load({
|
|
|
1469
1583
|
call,
|
|
1470
1584
|
t
|
|
1471
1585
|
}),
|
|
1586
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelsCard, {
|
|
1587
|
+
call,
|
|
1588
|
+
t
|
|
1589
|
+
}),
|
|
1472
1590
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchCard, {
|
|
1473
1591
|
call,
|
|
1474
1592
|
t
|