dsh-packycode-api 1.1.2 → 1.1.3
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 +4 -0
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/lib/index.js +60 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.3
|
|
4
|
+
|
|
5
|
+
- **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.
|
|
6
|
+
|
|
3
7
|
## 1.1.2
|
|
4
8
|
|
|
5
9
|
- **Claude CLI follows DSH plan mode.** While the current session's plan mode is on (including a selection that applies on this step but is not logged yet), a Claude model turn and the `claude_code` tool run the local CLI with `--permission-mode plan`. That wins over the session permission preset and over an explicit `claudeCliMode` pin. The CLI may explore read-only; other permission prompts are denied, and after eight denials the run stops instead of hanging. `ExitPlanMode` is never allowed inside the CLI process. A submitted plan becomes the assistant's `exit_plan_mode` tool call, so DSH's existing review approves it and the next step implements. The delegation tool returns the plan text for the parent model to submit. Session titles stay read-only. A host without the plan-mode service behaves as before.
|
package/README.en.md
CHANGED
|
@@ -44,7 +44,7 @@ Open the desktop application's **Plugins** page, click **Add plugin**, and enter
|
|
|
44
44
|
dsh-packycode-api
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.1.
|
|
47
|
+
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.1.3`.
|
|
48
48
|
|
|
49
49
|
Then **manually restart the desktop application**. Refreshing the page does not reload the plugin inside the Host process.
|
|
50
50
|
|
|
@@ -63,7 +63,7 @@ Choose a model from the `PackyCode` group. `deepseek-flash`, `gpt-6.1-sol`, and
|
|
|
63
63
|
|
|
64
64
|
The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
|
|
65
65
|
|
|
66
|
-
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.
|
|
66
|
+
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.3`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
|
|
67
67
|
|
|
68
68
|
A local checkout can be linked with `dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`.
|
|
69
69
|
|
|
@@ -153,7 +153,7 @@ A model in the list does not mean the current token group allows it. When changi
|
|
|
153
153
|
- 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
154
|
- 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
155
|
- 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
|
|
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`. Calling `ExitPlanMode`, or returning after writing the plan file when that tool is absent, submits the plan to DSH's review instead of editing in the same process. The previous mode returns after approval or `/plan off`. Session titles stay out of plan mode. An empty `claudeCliPath` is looked up automatically; an empty `claudeCliCwd` uses the session workspace.
|
|
157
157
|
- 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
158
|
- 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
159
|
- 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.
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
dsh-packycode-api
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.1.
|
|
51
|
+
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.1.3`。
|
|
52
52
|
|
|
53
53
|
完成后**手动重启桌面应用**。仅刷新页面不会重新加载 Host 里的插件。
|
|
54
54
|
|
|
@@ -67,7 +67,7 @@ dsh-packycode-api
|
|
|
67
67
|
|
|
68
68
|
桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
|
|
69
69
|
|
|
70
|
-
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.
|
|
70
|
+
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.3`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
|
|
71
71
|
|
|
72
72
|
本地开发可以用绝对路径:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。
|
|
73
73
|
|
|
@@ -157,7 +157,7 @@ Claude Code CLI → 本机临时 MCP web_search → 插件选定的 DeepSeek/GPT
|
|
|
157
157
|
- `cc`(Claude Code 专用)只接受官方 Claude CLI。用普通 API 客户端调 Claude 会 403:`This API endpoint is only accessible via the official Claude CLI`。本插件因此调用本机 `claude`,而不是改去 `cc-sale`,也不是伪造客户端身份。令牌如果没有开放给官方 CLI 的分组,这三个模型仍然不可用。
|
|
158
158
|
- 选了 Claude 模型但本机没有 CLI 时,这一回合会直接说明未安装,并指向设置页的安装按钮。
|
|
159
159
|
- 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`
|
|
160
|
+
- CLI 权限默认跟随当前会话:`read-only` → 只读,`workspace-write` → 可编辑,`danger-full-access` 且不再审批 → `full`。CLI 不能向 DSH 弹审批,所以只要会话还要审批,就不会给它 `full`。也可把设置项 `claudeCliMode` 固定成 `read-only` / `edit` / `full`。DSH 计划模式优先于该映射和这个固定档:CLI 以 `--permission-mode plan` 只读探索。它调用 `ExitPlanMode`,或在没有该工具时把计划写进计划文件后返回,都会交给 DSH 审阅;批准或 `/plan off` 之后才恢复原来的档位。会话标题不进入计划模式。`claudeCliPath` 留空则自动查找;`claudeCliCwd` 留空则用当前会话工作区。
|
|
161
161
|
- 思考过程和工具调用(`▸ Bash`、`▸ Read`,失败时带 `✗`)会出现在这一回合的思考区。它们是进度,不是 DSH 的工具调用。CLI 自己的工具不受 DSH 沙箱约束。
|
|
162
162
|
- 思考区第一行是「▸ 启动 Claude Code CLI…」,CLI 起来后显示版本和模型。网关限流或繁忙(429、503 `账号并发排队已满` 等)时,CLI 会自己重试最多 10 次,每次重试都会显示成 `↻ 网关返回 HTTP 503,CLI 自动重试 n/10`,不再像卡住。重试用完仍失败时,会说明是 PackyCode 的 Claude 上游繁忙,不是 Key 的问题。CLI 启动后 2 分钟内没有任何输出会被停止并报告原因。
|
|
163
163
|
- 对话记录和图片通过 stdin 交给 CLI(`--input-format stream-json`),不走命令行参数:Windows 命令行长度有上限,而且命令行带不了图片。对话里的图片会以图片形式发给 Claude(与 HTTP 路由相同的压缩规格:每张不超过 2048×2048、约 1 MiB;一回合最多 20 张、合计 20 MiB,超出时先舍弃最早的)。没随消息发出的图片会写明本机只读副本的路径,CLI 需要时可以用 Read 读取。
|
package/lib/index.js
CHANGED
|
@@ -1430,7 +1430,7 @@ const plainDescription = (part) => part.kind === "image" ? `[图片 ${attachment
|
|
|
1430
1430
|
function delegationInstructions(options, cwd, { plan = false } = {}) {
|
|
1431
1431
|
const system = typeof options?.system === "string" && options.system.trim() !== "" ? options.system.trim() : "";
|
|
1432
1432
|
return [
|
|
1433
|
-
plan ? "本回合 DeepSeek Harness 处于计划模式。只做只读探索,把完整实施计划写成以一级标题开头的 Markdown,写入 CLI
|
|
1433
|
+
plan ? "本回合 DeepSeek Harness 处于计划模式。只做只读探索,把完整实施计划写成以一级标题开头的 Markdown,写入 CLI 指定的计划文件。若有 ExitPlanMode 就调用它;没有也在写完后停下来。不要修改项目、不要开始实施。DeepSeek Harness 会用这份计划打开审阅。用户消息是 DeepSeek Harness 会话的近期对话记录,最后一条是当前请求;其中的图片已作为图片随消息附上,「只读副本」路径也可以用 Read 工具读取。" : "本回合由 DeepSeek Harness 委派给 Claude Code 执行。用户消息是 DeepSeek Harness 会话的近期对话记录,最后一条是当前请求;其中的图片已作为图片随消息附上,「只读副本」路径也可以用 Read 工具读取。请用你自己的工具完成任务,直接输出最终结果;不要反问、不要复述流程。",
|
|
1434
1434
|
cwd === void 0 ? void 0 : `工作目录:${cwd}`,
|
|
1435
1435
|
system === "" ? void 0 : `补充说明(来自 DeepSeek Harness,其中提到的工具你未必拥有):${system.slice(0, 2e3)}`
|
|
1436
1436
|
].filter((part) => part !== void 0).join("\n");
|
|
@@ -2408,6 +2408,51 @@ function readSubmittedPlan(input, { configDir, startedAt = Date.now(), home = ho
|
|
|
2408
2408
|
}
|
|
2409
2409
|
}
|
|
2410
2410
|
/**
|
|
2411
|
+
* The plan file a print-mode CLI wrote and then returned without calling
|
|
2412
|
+
* `ExitPlanMode`. `-p` often has no interactive plan tool, so the file is
|
|
2413
|
+
* what DSH reviews. Only a markdown file under this run's config `plans`
|
|
2414
|
+
* directory or `~/.claude/plans`, newer than the run, is eligible. The newest
|
|
2415
|
+
* one wins.
|
|
2416
|
+
* @param options - config home, when the run started, and file-system seams.
|
|
2417
|
+
* @returns plan markdown, or undefined when this run wrote none.
|
|
2418
|
+
*/
|
|
2419
|
+
function findPlanWrittenDuringRun({ configDir, startedAt = Date.now(), home = homedir(), readdir = readdirSync, readFile = (file) => readFileSync(file, "utf8"), stat = statSync, realpath = realpathSync } = {}) {
|
|
2420
|
+
const roots = [typeof configDir === "string" && configDir !== "" ? join(configDir, "plans") : "", join(home, ".claude", "plans")].filter((root) => root !== "");
|
|
2421
|
+
let newest;
|
|
2422
|
+
let newestMtime = -Infinity;
|
|
2423
|
+
for (const root of roots) {
|
|
2424
|
+
let names;
|
|
2425
|
+
try {
|
|
2426
|
+
names = readdir(root);
|
|
2427
|
+
} catch {
|
|
2428
|
+
continue;
|
|
2429
|
+
}
|
|
2430
|
+
for (const name of names) {
|
|
2431
|
+
if (typeof name !== "string" || !name.toLowerCase().endsWith(".md")) continue;
|
|
2432
|
+
const candidate = join(root, name);
|
|
2433
|
+
let info;
|
|
2434
|
+
try {
|
|
2435
|
+
info = stat(candidate);
|
|
2436
|
+
} catch {
|
|
2437
|
+
continue;
|
|
2438
|
+
}
|
|
2439
|
+
if (!info.isFile() || info.mtimeMs < startedAt - PLAN_FILE_CLOCK_SKEW_MS || info.mtimeMs < newestMtime) continue;
|
|
2440
|
+
const text = readSubmittedPlan({ planFilePath: candidate }, {
|
|
2441
|
+
configDir,
|
|
2442
|
+
startedAt,
|
|
2443
|
+
home,
|
|
2444
|
+
readFile,
|
|
2445
|
+
stat,
|
|
2446
|
+
realpath
|
|
2447
|
+
});
|
|
2448
|
+
if (text === void 0) continue;
|
|
2449
|
+
newest = text;
|
|
2450
|
+
newestMtime = info.mtimeMs;
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
return newest;
|
|
2454
|
+
}
|
|
2455
|
+
/**
|
|
2411
2456
|
* Build what the CLI reads from stdin for one run.
|
|
2412
2457
|
*
|
|
2413
2458
|
* A streaming run reads one `stream-json` user message, whose content may mix
|
|
@@ -2680,6 +2725,7 @@ function createClaudeRunner({ binary = findClaudeBinary(), configDir = DEFAULT_C
|
|
|
2680
2725
|
if (disposed || signal?.aborted) throw new Error("claude_code was aborted");
|
|
2681
2726
|
const cancellation = new AbortController();
|
|
2682
2727
|
const runSignal = signal === void 0 ? cancellation.signal : AbortSignal.any([signal, cancellation.signal]);
|
|
2728
|
+
const startedAt = Date.now();
|
|
2683
2729
|
const state = {
|
|
2684
2730
|
stdout: "",
|
|
2685
2731
|
stderr: "",
|
|
@@ -2687,9 +2733,9 @@ function createClaudeRunner({ binary = findClaudeBinary(), configDir = DEFAULT_C
|
|
|
2687
2733
|
silent: false,
|
|
2688
2734
|
sawOutput: false,
|
|
2689
2735
|
settled: false,
|
|
2690
|
-
denies: 0
|
|
2736
|
+
denies: 0,
|
|
2737
|
+
startedAt
|
|
2691
2738
|
};
|
|
2692
|
-
const startedAt = Date.now();
|
|
2693
2739
|
let child;
|
|
2694
2740
|
let bridge;
|
|
2695
2741
|
let bridgeClosing;
|
|
@@ -3167,6 +3213,17 @@ function createClaudeRunner({ binary = findClaudeBinary(), configDir = DEFAULT_C
|
|
|
3167
3213
|
yield* drain();
|
|
3168
3214
|
}
|
|
3169
3215
|
await completion;
|
|
3216
|
+
if (buffer.trim().startsWith("{")) try {
|
|
3217
|
+
const event = readClaudeStreamEvents(JSON.parse(scrub(buffer.trim()))).find((item) => item.type === "result");
|
|
3218
|
+
if (event !== void 0) sawResult = event.result;
|
|
3219
|
+
} catch {}
|
|
3220
|
+
if (mode === "plan" && typeof state.plan !== "string" && exitStatus?.code === 0) {
|
|
3221
|
+
const written = findPlanWrittenDuringRun({
|
|
3222
|
+
configDir,
|
|
3223
|
+
startedAt: state.startedAt
|
|
3224
|
+
});
|
|
3225
|
+
if (written !== void 0) state.plan = scrub(written);
|
|
3226
|
+
}
|
|
3170
3227
|
if (typeof state.plan === "string") {
|
|
3171
3228
|
if (deltaKind !== void 0) {
|
|
3172
3229
|
const tail = deltaRedactor.flush({ boundary: true });
|
|
@@ -3196,10 +3253,6 @@ function createClaudeRunner({ binary = findClaudeBinary(), configDir = DEFAULT_C
|
|
|
3196
3253
|
};
|
|
3197
3254
|
return;
|
|
3198
3255
|
}
|
|
3199
|
-
if (buffer.trim().startsWith("{")) try {
|
|
3200
|
-
const event = readClaudeStreamEvents(JSON.parse(scrub(buffer.trim()))).find((item) => item.type === "result");
|
|
3201
|
-
if (event !== void 0) sawResult = event.result;
|
|
3202
|
-
} catch {}
|
|
3203
3256
|
if (sawResult === void 0) throw new Error(failureMessage(void 0, void 0, state.stderr));
|
|
3204
3257
|
if (sawResult.is_error === true || exitStatus?.code !== 0) throw new Error(failureMessage(sawResult, exitStatus?.code ?? exitStatus?.killSignal, state.stderr));
|
|
3205
3258
|
const usage = sawResult.usage ?? {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-packycode-api",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"packageManager": "pnpm@10.32.1",
|
|
5
5
|
"description": "Use a PackyCode (PackyAPI) API key in DeepSeek Harness through a built-in multi-protocol model route with key rotation and balance display",
|
|
6
6
|
"type": "module",
|