dsh-packycode-api 1.0.4 → 1.0.6

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.6
4
+
5
+ - New model `gpt-6-astra` (GPT-6 Astra), on the same route as `gpt-6.1-sol`: `openai-responses` at `https://cf.api.fan/v1`, text and image input, 1,050,000-token context, 128,000 output tokens, and `reasoning.effort` `low` / `medium` / `high` / `xhigh` / `max` (reasoning cannot be turned off). All of these are from OpenAI's [model page](https://developers.openai.com/api/docs/models/gpt-6-astra). The gateway lists it in `/v1/models` as `gpt-6-astra`, and it needs a key whose group covers Codex (`codex`, `codex-sale`). Verified live through the built bundle: `live-check` answered `ok` from it alongside `deepseek-flash` and `gpt-6.1-sol`, and each of the five effort levels answered `ok`. One `medium` request first hit a transient `Connection error` and answered on retry.
6
+
7
+ ## 1.0.5
8
+
9
+ - **Balance on mainland networks.** The default balance host is now `https://www.packyapi.ai` (PackyAPI's current site). `www.packyapi.com`, the default through 1.0.4, is DNS-poisoned on mainland networks: every public resolver checked (AliDNS, DNSPod, 1.1.1.1, 8.8.8.8, even AliDNS over HTTPS) returned an unrelated foreign IP, and a direct Node fetch failed with `ECONNREFUSED`. Without a proxy every key read `fetch failed` from it, which is what a Windows user saw. With a proxy it worked, so the failure came and went. A profile that saved the old host keeps it as a fallback, but `.ai` is tried first. Verified live without a proxy: `.ai` answers in about 1.3 s.
10
+ - A failed balance read keeps each key's last good balance (and the last account figures), marked `stale` with the time of that read and this read's error. Settings shows a note, and the composer shows `≈US$…`. These values stay in the host process: a page refresh keeps them and a restart clears them. A transport failure now names its cause (`fetch failed (ENOTFOUND)`, `no answer within 20s`), and a 429 says the gateway is rate limiting. The composer says "余额读取失败" instead of "无可用 Key" when a usable key's read failed. The total limit shows `—` instead of "不限额度" when no key was read.
11
+ - **Claude Code CLI identity.** In `-p` mode the official CLI picks its identity line from whether it got appended instructions. With them, it tells the model "You are Claude Code, Anthropic's official CLI for Claude, running within the Claude Agent SDK."; without them, "You are a Claude agent, built on Anthropic's Claude Agent SDK." A delegated turn ran without them and was refused upstream with `403 Request blocked: this endpoint only accepts requests from the official Claude Code CLI`. Every run now passes `--append-system-prompt`: the delegation instructions (moved out of the prompt) or, for the `claude_code` tool, a short delegation note. This is the binary's own selection (read from its code and confirmed by capturing its request); the plugin does not rewrite the CLI's system prompt. That wording is also recognised as a client-policy refusal, explained without the bare 403, and never disables a key.
12
+ - **Prompt over stdin; images in CLI turns.** The CLI now reads its input from stdin instead of argv. A streaming turn sends one `stream-json` user message, the `claude_code` tool sends plain text, and stdin is closed afterwards. A Windows command line is capped near 32K characters and cannot carry an image. Images in a CLI-backed turn used to become the text `[图片]`; they are now read from the attachment store at the HTTP route's request size (2048×2048, about 1 MiB), sent as Anthropic image blocks where they appeared, and labelled with their read-only host copy. A turn carries at most 20 images and 20 MiB of base64, dropping the oldest first. An image left out, or unreadable, is described with its path instead of failing the turn. Uploaded files are named with their host copy, and structured tool results keep their text. Verified with the real CLI 2.1.295 against a local gateway: the image reached `/v1/messages` as a base64 image block.
13
+ - **No more silent CLI waits.** A delegated turn now opens its reasoning block with `▸ 启动 Claude Code CLI…`, shows the CLI's version and model once it is up, and shows each retry the CLI makes on its own (`↻ 网关返回 HTTP 503(server_error),CLI 自动重试 1/10,1.0 秒后`). Before, a gateway answering `503 packy_concurrency_queue_full` (账号并发排队已满) or 429 kept the CLI retrying for minutes with nothing on screen. When the retries run out, the turn says PackyCode's Claude upstream is busy and how many retries were made, keeping the gateway's words and request id. A streaming CLI that prints nothing within 2 minutes of starting is stopped and reported with its stderr tail. Verified with the real CLI against a local gateway that answers 503, both recovering after two retries and giving up.
14
+
3
15
  ## 1.0.4
4
16
 
5
17
  - Settings can check for and install a newer npm release. **Update plugin** installs the exact published version into the owning profile with the host's bundled pnpm, then reads `package.json` back before reporting success. A `link:` checkout is left alone. The desktop app still needs a full restart afterwards.
package/README.en.md CHANGED
@@ -42,7 +42,7 @@ Open the desktop application's **Plugins** page, click **Add plugin**, and enter
42
42
  dsh-packycode-api
43
43
  ```
44
44
 
45
- Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.0.4`.
45
+ Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.0.6`.
46
46
 
47
47
  Then **manually restart the desktop application**. Refreshing the page does not reload the plugin inside the Host process.
48
48
 
@@ -54,14 +54,14 @@ Open **Settings → PackyCode**. Paste the API key under **Add a key**. A label
54
54
 
55
55
  ### 3. Select a model
56
56
 
57
- Choose a model from the `PackyCode` group. `deepseek-flash` and `gpt-6.1-sol` work once a key is stored. The three Claude models also need the Claude Code CLI on this device; see below.
57
+ Choose a model from the `PackyCode` group. `deepseek-flash`, `gpt-6.1-sol`, and `gpt-6-astra` work once a key is stored. The three Claude models also need the Claude Code CLI on this device; see below.
58
58
 
59
59
  <details>
60
60
  <summary>Verify the install / other profiles / a local checkout</summary>
61
61
 
62
62
  The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
63
63
 
64
- **For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.0.4`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
64
+ **For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.0.6`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
65
65
 
66
66
  A local checkout can be linked with `dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`.
67
67
 
@@ -73,7 +73,7 @@ A profile without a browser can still use the model route. It just has no settin
73
73
 
74
74
  **Switch keys.** Keys are used in the order they were added. **Use this key** selects one; **Rename** changes its label (at most 60 characters, empty names are refused); **Re-enable** puts a disabled key back after a top-up or replacement.
75
75
 
76
- **Read the balance.** The settings total is the remaining, used, and limit of **the keys listed here**, not the bill for every token on the PackyCode account. A key that could not be read is left out of the total and counted in a note. 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.com`; both can be changed under **Advanced: endpoints**.
76
+ **Read the balance.** The settings total is the remaining, used, and limit of **the keys listed here**, not the bill for every token on the PackyCode account. A key that could not be read is left out of the total and counted in a note; a key whose read failed this time but succeeded before keeps showing its last balance with the time it was read (the composer shows it as `≈US$…`) until DSH restarts. The composer shows the active key's remaining balance only while a PackyCode model is selected. **Refresh** bypasses the roughly 60-second cache. The default model host is `https://cf.api.fan` and the balance host is `https://www.packyapi.ai`; both can be changed under **Advanced: endpoints**. The old default, `https://www.packyapi.com`, is DNS-poisoned on mainland networks and answers `fetch failed` without a proxy; even when settings still name it, `.ai` is tried first.
77
77
 
78
78
  **Use Claude.** The **Claude Code CLI** card at the top of Settings checks this device. If the CLI is missing, **Install Claude Code CLI** runs `npm install -g @anthropic-ai/claude-code` (Node.js 18+). No DSH restart is required after that install. Windows also needs [Git for Windows](https://git-scm.com/downloads/win).
79
79
 
@@ -92,6 +92,7 @@ A profile without a browser can still use the model route. It just has no settin
92
92
  | --- | --- | --- | --- |
93
93
  | `deepseek-flash` | HTTP, chat/completions | Can be off; `low` / `high` / `max` | `deepseek-officially`, `deepseek-sale` |
94
94
  | `gpt-6.1-sol` | HTTP, Responses, images allowed | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | `codex`, `codex-sale` |
95
+ | `gpt-6-astra` | Same | Same | Same |
95
96
  | `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 |
96
97
  | `claude-sonnet-5-5` | Same | Same | Same |
97
98
  | `claude-haiku-5-5` | Same; session titles always use this model | Same | Same |
@@ -101,7 +102,7 @@ A model in the list does not mean the current token group allows it. When changi
101
102
  <details>
102
103
  <summary>Protocol, group, and CLI constraints</summary>
103
104
 
104
- - OpenAI / Codex traffic must use the Responses protocol. `gpt-6.1-sol` is pinned to `openai-responses` at `https://cf.api.fan/v1`. Do not put `/responses` in the base URL.
105
+ - OpenAI / Codex traffic must use the Responses protocol. `gpt-6.1-sol` and `gpt-6-astra` are pinned to `openai-responses` at `https://cf.api.fan/v1`. Do not put `/responses` in the base URL.
105
106
  - Codex-group Responses requests must include `prompt_cache_key` or the gateway returns 403. The harness supplies the session id; the plugin adds a fallback when a caller does not.
106
107
  - The Anthropic base URL has no `/v1` (the client appends `/v1/messages`). The three Claude models declare the Messages protocol in the catalog, but their requests are made by the local CLI.
107
108
  - 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.
@@ -109,8 +110,11 @@ A model in the list does not mean the current token group allows it. When changi
109
110
  - 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.
110
111
  - 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`. An empty `claudeCliPath` is looked up automatically; an empty `claudeCliCwd` uses the session workspace.
111
112
  - 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.
113
+ - 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.
114
+ - 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.
115
+ - Every run carries `--append-system-prompt` (the delegation note). In `-p` mode the official CLI introduces itself as "You are Claude Code, Anthropic's official CLI for Claude …" when it has appended instructions and as "You are a Claude agent, built on Anthropic's Claude Agent SDK." when it has none; the latter was refused upstream as not coming from the official CLI (403 `only accepts requests from the official Claude Code CLI`). That is the official CLI's own behavior; the plugin does not rewrite its system prompt.
112
116
  - While the session model is a Claude model, titles are generated with `claude-haiku-5-5`, read-only, in a temporary directory, instead of a full Opus turn.
113
- - The `claude_code` tool can delegate one task to the local CLI while the session model is `gpt-6.1-sol` or `deepseek-flash`. The requested `mode` cannot exceed what the session preset allows.
117
+ - The `claude_code` tool can delegate one task to the local CLI while the session model is `gpt-6.1-sol`, `gpt-6-astra`, or `deepseek-flash`. The requested `mode` cannot exceed what the session preset allows.
114
118
  - An effort level the model does not support is rejected before the request is sent. Do not edit a model's effort map casually.
115
119
 
116
120
  </details>
@@ -127,6 +131,10 @@ A model in the list does not mean the current token group allows it. When changi
127
131
 
128
132
  **The balance does not match the account bill?** Settings totals only the keys stored here, not other tokens on the same PackyCode account. A key that failed to read is noted separately and is not treated as zero.
129
133
 
134
+ **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.
135
+
136
+ **A Claude model sits on "thinking" with no answer?** Read the reasoning block: `↻ 网关返回 HTTP 503/429` lines mean PackyCode's Claude upstream is queueing or throttling and the CLI is retrying. `▸ 启动 Claude Code CLI…` with no "started" line after it means the local CLI did not come up (on Windows, check Git for Windows and run `claude --version` in a terminal).
137
+
130
138
  **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**.
131
139
 
132
140
  **Installing from the Plugins page fails with `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION`?** pnpm refuses packages published less than 24 hours ago and checks the whole profile lockfile. 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.
package/README.md CHANGED
@@ -42,7 +42,7 @@
42
42
  dsh-packycode-api
43
43
  ```
44
44
 
45
- 在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.0.4`。
45
+ 在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.0.6`。
46
46
 
47
47
  完成后**手动重启桌面应用**。仅刷新页面不会重新加载 Host 里的插件。
48
48
 
@@ -54,14 +54,14 @@ dsh-packycode-api
54
54
 
55
55
  ### 3. 选择模型
56
56
 
57
- 回到对话,在模型选择器里选 `PackyCode` 分组下的模型。`deepseek-flash` 和 `gpt-6.1-sol` 添加 Key 后即可用。三个 Claude 模型还需要本机装有 Claude Code CLI,见下文。
57
+ 回到对话,在模型选择器里选 `PackyCode` 分组下的模型。`deepseek-flash`、`gpt-6.1-sol` 和 `gpt-6-astra` 添加 Key 后即可用。三个 Claude 模型还需要本机装有 Claude Code CLI,见下文。
58
58
 
59
59
  <details>
60
60
  <summary>检查安装 / 其它 profile / 本地目录</summary>
61
61
 
62
62
  桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
63
63
 
64
- **仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.0.4`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
64
+ **仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.0.6`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
65
65
 
66
66
  本地开发可以用绝对路径:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。
67
67
 
@@ -73,7 +73,7 @@ dsh-packycode-api
73
73
 
74
74
  **换 Key。** Key 按添加顺序使用。点「切到此 Key」可以指定当前 Key;「重命名」改备注(最多 60 字,不能为空);充值或更换后点「重新启用」。
75
75
 
76
- **看余额。** 设置页的合计是**已列出的这些 Key** 的剩余、已用与额度,不是 PackyCode 账号下全部令牌的账单。读失败的 Key 不计入合计,并会注明个数。输入框右侧只在选中 PackyCode 模型时显示当前 Key 的剩余额度。「刷新」会跳过约 60 秒的缓存。默认模型接口是 `https://cf.api.fan`,余额接口是 `https://www.packyapi.com`,可在「高级:接口地址」里改。
76
+ **看余额。** 设置页的合计是**已列出的这些 Key** 的剩余、已用与额度,不是 PackyCode 账号下全部令牌的账单。读失败的 Key 不计入合计,并会注明个数;某个 Key 这次读失败但之前读到过,会继续显示上次的余额并注明读取时间(输入框里显示为 `≈US$…`),重启 DSH 后才清空。输入框右侧只在选中 PackyCode 模型时显示当前 Key 的剩余额度。「刷新」会跳过约 60 秒的缓存。默认模型接口是 `https://cf.api.fan`,余额接口是 `https://www.packyapi.ai`,可在「高级:接口地址」里改。旧默认值 `https://www.packyapi.com` 在国内网络下 DNS 被污染、直连会 `fetch failed`,即使设置里保存的是它,插件也会先试 `.ai`。
77
77
 
78
78
  **用 Claude。** 设置页顶部的「Claude Code CLI」卡片会检测本机 CLI。没装时点「安装 Claude Code CLI」,本机执行 `npm install -g @anthropic-ai/claude-code`(需要 Node.js 18+)。装好后不用重启 DSH。Windows 上另外需要 [Git for Windows](https://git-scm.com/downloads/win)。
79
79
 
@@ -92,6 +92,7 @@ dsh-packycode-api
92
92
  | --- | --- | --- | --- |
93
93
  | `deepseek-flash` | HTTP,chat/completions | 可关;`low` / `high` / `max` | `deepseek-officially`、`deepseek-sale` |
94
94
  | `gpt-6.1-sol` | HTTP,Responses,可带图 | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | `codex`、`codex-sale` |
95
+ | `gpt-6-astra` | 同上 | 同上 | 同上 |
95
96
  | `claude-opus-5-5` | 本机 Claude Code CLI | 选择器档位同上,实际档位由 CLI 决定 | 要能给官方 CLI 用的分组,见下 |
96
97
  | `claude-sonnet-5-5` | 同上 | 同上 | 同上 |
97
98
  | `claude-haiku-5-5` | 同上;会话标题也固定走它 | 同上 | 同上 |
@@ -101,7 +102,7 @@ dsh-packycode-api
101
102
  <details>
102
103
  <summary>协议、分组和 CLI 的具体约束</summary>
103
104
 
104
- - OpenAI / Codex 必须走 Responses。`gpt-6.1-sol` 已固定为 `openai-responses`,base URL 是 `https://cf.api.fan/v1`,不要把 `/responses` 写进根地址。
105
+ - OpenAI / Codex 必须走 Responses。`gpt-6.1-sol` 和 `gpt-6-astra` 已固定为 `openai-responses`,base URL 是 `https://cf.api.fan/v1`,不要把 `/responses` 写进根地址。
105
106
  - Codex 分组的 Responses 请求必须带 `prompt_cache_key`,否则 403。harness 会带会话 id;没有会话 id 时插件补一个兜底值。
106
107
  - Anthropic 的 base URL 不带 `/v1`(客户端自己拼 `/v1/messages`)。三个 Claude 模型虽然目录里声明了 Messages 协议,实际请求由本机 CLI 发出。
107
108
  - `cc`(Claude Code 专用)只接受官方 Claude CLI。用普通 API 客户端调 Claude 会 403:`This API endpoint is only accessible via the official Claude CLI`。本插件因此调用本机 `claude`,而不是改去 `cc-sale`,也不是伪造客户端身份。令牌如果没有开放给官方 CLI 的分组,这三个模型仍然不可用。
@@ -109,8 +110,11 @@ dsh-packycode-api
109
110
  - CLI 使用插件配置的地址和当前 Key(`ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN`),以及独立配置目录 `~/.dsh/packycode-claude-cli`。不隔离的话,`~/.claude/settings.json` 里的 `env` 会盖掉插件 Key。
110
111
  - CLI 权限默认跟随当前会话:`read-only` → 只读,`workspace-write` → 可编辑,`danger-full-access` 且不再审批 → `full`。CLI 不能向 DSH 弹审批,所以只要会话还要审批,就不会给它 `full`。也可把设置项 `claudeCliMode` 固定成 `read-only` / `edit` / `full`。`claudeCliPath` 留空则自动查找;`claudeCliCwd` 留空则用当前会话工作区。
111
112
  - 思考过程和工具调用(`▸ Bash`、`▸ Read`,失败时带 `✗`)会出现在这一回合的思考区。它们是进度,不是 DSH 的工具调用。CLI 自己的工具不受 DSH 沙箱约束。
113
+ - 思考区第一行是「▸ 启动 Claude Code CLI…」,CLI 起来后显示版本和模型。网关限流或繁忙(429、503 `账号并发排队已满` 等)时,CLI 会自己重试最多 10 次,每次重试都会显示成 `↻ 网关返回 HTTP 503,CLI 自动重试 n/10`,不再像卡住。重试用完仍失败时,会说明是 PackyCode 的 Claude 上游繁忙,不是 Key 的问题。CLI 启动后 2 分钟内没有任何输出会被停止并报告原因。
114
+ - 对话记录和图片通过 stdin 交给 CLI(`--input-format stream-json`),不走命令行参数:Windows 命令行长度有上限,而且命令行带不了图片。对话里的图片会以图片形式发给 Claude(与 HTTP 路由相同的压缩规格:每张不超过 2048×2048、约 1 MiB;一回合最多 20 张、合计 20 MiB,超出时先舍弃最早的)。没随消息发出的图片会写明本机只读副本的路径,CLI 需要时可以用 Read 读取。
115
+ - 每次运行都带 `--append-system-prompt`(委派说明)。在 `-p` 模式下,官方 CLI 有追加说明时会以 "You are Claude Code, Anthropic's official CLI for Claude …" 自我介绍,没有时是 "You are a Claude agent, built on Anthropic's Claude Agent SDK.";后者曾被上游当成非官方 CLI 拒绝(403 `only accepts requests from the official Claude Code CLI`)。这是官方 CLI 自己的行为,插件没有改写它的系统提示词。
112
116
  - 会话跑在 Claude 模型上时,生成标题改用 `claude-haiku-5-5`,只读、临时目录,避免为起标题跑一整轮 Opus。
113
- - 另有 `claude_code` 工具:当前模型是 `gpt-6.1-sol` 或 `deepseek-flash` 时,可以把一个任务委派给本机 Claude Code。请求的 `mode` 不会超过会话权限允许的上限。
117
+ - 另有 `claude_code` 工具:当前模型是 `gpt-6.1-sol`、`gpt-6-astra` 或 `deepseek-flash` 时,可以把一个任务委派给本机 Claude Code。请求的 `mode` 不会超过会话权限允许的上限。
114
118
  - 声明了模型不支持的推理档位会被 harness 在发请求前拒绝,不要随手改档位映射。
115
119
 
116
120
  </details>
@@ -127,6 +131,10 @@ dsh-packycode-api
127
131
 
128
132
  **余额对不上账号账单?** 设置页合计的是这里列出的 Key,不含同账号下没添加到本插件的其它令牌。个别 Key 读失败时会单独注明,不会被算成 0。
129
133
 
134
+ **余额显示 `读取失败: https://www.packyapi.com: fetch failed`?** 这是 1.0.4 及更早版本的默认余额接口在国内网络下被 DNS 污染,开了代理才能连上,所以有时正常、有时失败。新版本默认改用 `https://www.packyapi.ai`。模型列表是插件内置的,不需要向 PackyCode 查询,不受影响。
135
+
136
+ **选了 Claude 模型一直显示「深度求索中」?** 看思考区:有 `↻ 网关返回 HTTP 503/429` 说明 PackyCode 的 Claude 上游在排队或限流,CLI 正在自动重试;只有「▸ 启动 Claude Code CLI…」没有「已启动」,说明本机 CLI 没起来(Windows 上先确认装了 Git for Windows,并在终端执行 `claude --version`)。
137
+
130
138
  **限速之后 Key 被停用了?** 不该发生。429 不会切换 Key。额度用尽或 401 才会停用;充值后点「重新启用」。
131
139
 
132
140
  **插件页安装报 `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION`?** pnpm 默认拒绝发布不满 24 小时的包,并且会检查 profile 里的整个 lockfile。报错里点名的往往是另一个刚更新过的插件,不是本插件。等它发布满 24 小时再重试,或把报错里的 `包名@版本` 加到 profile 目录 `pnpm-workspace.yaml` 的 `minimumReleaseAgeExclude` 下。已安装本插件后,设置页的「更新插件」不受这个限制。
package/lib/client.js CHANGED
@@ -102,6 +102,9 @@ window.__ModuleLoader__.load({
102
102
  totalUsed: "合计已用",
103
103
  totalLimit: "合计额度",
104
104
  totalPartial: "有 {count} 个 Key 未读到余额,未计入合计。",
105
+ totalStale: "其中 {count} 个 Key 本次读取失败,显示的是上次读到的余额。",
106
+ staleNote: "上次读取于 {time},本次读取失败:{error}",
107
+ composerStale: "上次读取于 {time},本次读取失败",
105
108
  accountLimit: "额度",
106
109
  refresh: "刷新",
107
110
  refreshing: "刷新中…",
@@ -139,6 +142,7 @@ window.__ModuleLoader__.load({
139
142
  notWritable: "当前 profile 不允许写入设置,改动只在本次运行内生效。",
140
143
  composerLabel: "PackyCode 剩余额度",
141
144
  composerNoKey: "无可用 Key",
145
+ composerUnread: "余额读取失败",
142
146
  error: "读取 PackyCode 状态失败",
143
147
  loadFailed: "读取失败",
144
148
  cliTitle: "Claude Code CLI",
@@ -229,6 +233,10 @@ window.__ModuleLoader__.load({
229
233
  notWritable: "This profile does not allow settings writes; changes apply to this run only.",
230
234
  composerLabel: "PackyCode remaining balance",
231
235
  composerNoKey: "No usable key",
236
+ composerUnread: "Balance unavailable",
237
+ totalStale: "{count} of these keys failed this read and show their last read balance.",
238
+ staleNote: "Last read at {time}; this read failed: {error}",
239
+ composerStale: "last read at {time}; this read failed",
232
240
  error: "Could not read PackyCode status",
233
241
  loadFailed: "Read failed",
234
242
  cliTitle: "Claude Code CLI",
@@ -341,6 +349,18 @@ window.__ModuleLoader__.load({
341
349
  if (balance === void 0) return void 0;
342
350
  return balance.keys?.find((entry) => entry.id === balance.activeId) ?? balance.keys?.[0];
343
351
  }
352
+ /** The clock time of an epoch-millisecond reading, for a "last read at" note. */
353
+ function readTime(ms) {
354
+ if (typeof ms !== "number" || !Number.isFinite(ms)) return "—";
355
+ try {
356
+ return new Date(ms).toLocaleTimeString(void 0, {
357
+ hour: "2-digit",
358
+ minute: "2-digit"
359
+ });
360
+ } catch {
361
+ return new Date(ms).toISOString().slice(11, 16);
362
+ }
363
+ }
344
364
  function clearMessage(error) {
345
365
  const text = typeof error?.message === "string" && error.message.length > 0 ? error.message : String(error);
346
366
  return text.length > 200 ? text.slice(0, 200) : text;
@@ -719,23 +739,30 @@ window.__ModuleLoader__.load({
719
739
  className: "packyCodeRow",
720
740
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
721
741
  className: "packyCodeRowMain",
722
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
723
- className: "packyCodeRowName",
724
- children: [
725
- row.label,
726
- row.id === status?.activeId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
727
- className: "packyCodeChip packyCodeChipActive",
728
- children: t("keyActive")
729
- }),
730
- row.disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
731
- className: "packyCodeChip",
732
- children: t("keyDisabled")
733
- })
734
- ]
735
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
736
- className: "packyCodeRowMeta",
737
- children: figures.length > 0 ? [tokenName, ...figures].filter(Boolean).join(" · ") : row.error === void 0 ? t("keyNoBalance") : `${t("loadFailed")}: ${row.error}`
738
- })]
742
+ children: [
743
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
744
+ className: "packyCodeRowName",
745
+ children: [
746
+ row.label,
747
+ row.id === status?.activeId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
748
+ className: "packyCodeChip packyCodeChipActive",
749
+ children: t("keyActive")
750
+ }),
751
+ row.disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
752
+ className: "packyCodeChip",
753
+ children: t("keyDisabled")
754
+ })
755
+ ]
756
+ }),
757
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
758
+ className: "packyCodeRowMeta",
759
+ children: figures.length > 0 ? [tokenName, ...figures].filter(Boolean).join(" · ") : row.error === void 0 ? t("keyNoBalance") : `${t("loadFailed")}: ${row.error}`
760
+ }),
761
+ row.stale !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
762
+ className: "packyCodeRowMeta",
763
+ children: t("staleNote").replace("{time}", readTime(row.stale.fetchedAt)).replace("{error}", row.stale.error ?? "")
764
+ })
765
+ ]
739
766
  })
740
767
  }, row.id);
741
768
  };
@@ -806,7 +833,7 @@ window.__ModuleLoader__.load({
806
833
  children: t("totalLimit")
807
834
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
808
835
  className: "packyCodeFigureValue",
809
- children: totals.counted === 0 || totals.unlimited > 0 ? t("keyUnlimited") : formatUsd(totals.limitUsd) ?? "—"
836
+ children: totals.counted === 0 ? "—" : totals.unlimited > 0 ? t("keyUnlimited") : formatUsd(totals.limitUsd) ?? "—"
810
837
  })]
811
838
  })
812
839
  ]
@@ -814,6 +841,10 @@ window.__ModuleLoader__.load({
814
841
  totals.unread > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
815
842
  className: "packyCodeNote",
816
843
  children: t("totalPartial").replace("{count}", String(totals.unread))
844
+ }),
845
+ rows.some((row) => row.stale !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
846
+ className: "packyCodeNote",
847
+ children: t("totalStale").replace("{count}", String(rows.filter((row) => row.stale !== void 0).length))
817
848
  })
818
849
  ] })]
819
850
  }),
@@ -1042,15 +1073,18 @@ window.__ModuleLoader__.load({
1042
1073
  };
1043
1074
  }, [call, enabled]);
1044
1075
  if (!enabled || balance === void 0) return null;
1045
- const usage = activeUsage(balance)?.usage;
1046
- const display = usage?.unlimitedQuota === true ? t("keyUnlimited") : formatUsd(usage?.availableUsd) ?? t("composerNoKey");
1047
- const label = `${t("composerLabel")}: ${display}`;
1076
+ const row = activeUsage(balance);
1077
+ const usage = row?.usage;
1078
+ const display = usage?.unlimitedQuota === true ? t("keyUnlimited") : formatUsd(usage?.availableUsd) ?? (row?.error === void 0 ? t("composerNoKey") : t("composerUnread"));
1079
+ const stale = row?.stale !== void 0 && usage !== void 0;
1080
+ const shown = stale ? `≈${display}` : display;
1081
+ const label = stale ? `${t("composerLabel")}: ${display}(${t("composerStale").replace("{time}", readTime(row.stale.fetchedAt))})` : `${t("composerLabel")}: ${display}`;
1048
1082
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1049
1083
  className: "packyCodeComposer",
1050
1084
  role: "status",
1051
1085
  "aria-label": label,
1052
1086
  title: label,
1053
- children: display
1087
+ children: shown
1054
1088
  });
1055
1089
  }
1056
1090
  const inject = [