dsh-packycode-api 1.3.1 → 1.3.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/AGENTS.md +7 -4
- package/CHANGELOG.md +12 -0
- package/README.en.md +14 -13
- package/README.md +14 -13
- package/lib/client.js +63 -131
- package/lib/index.js +56 -7
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -17,6 +17,7 @@ This file governs the entire `dsh-packycode-api` repository.
|
|
|
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
19
|
- 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.
|
|
20
|
+
- 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
21
|
|
|
21
22
|
## Security requirements
|
|
22
23
|
|
|
@@ -90,17 +91,19 @@ Never reuse, move, delete, or overwrite a published npm version or Git tag. If a
|
|
|
90
91
|
|
|
91
92
|
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
93
|
|
|
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
|
|
94
|
+
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
95
|
|
|
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
|
|
96
|
+
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 `next` version once that version is 25 hours old, and `mark-latest` then marks its 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
97
|
|
|
97
98
|
```sh
|
|
98
99
|
git tag -a v<version> -m "dsh-packycode-api v<version>"
|
|
99
100
|
git push origin v<version>
|
|
100
101
|
```
|
|
101
102
|
|
|
102
|
-
`workflow_dispatch` on the same workflow
|
|
103
|
+
`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.
|
|
103
104
|
|
|
104
|
-
8. Confirm that both jobs of the publish
|
|
105
|
+
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.
|
|
106
|
+
|
|
107
|
+
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
108
|
|
|
106
109
|
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,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.3.3
|
|
4
|
+
|
|
5
|
+
- **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.
|
|
6
|
+
- **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.
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
9
|
+
## 1.3.2
|
|
10
|
+
|
|
11
|
+
- **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.
|
|
12
|
+
- **Settings no longer adds keys up.** The 合计剩余 / 合计已用 / 合计额度 tiles at the top of **Accounts and keys** and of the image card are removed, with their notes about unread and stale keys. Each key row already shows its own remaining, used and limit, its read failure and its stale reading, so the tiles only repeated those figures. With an unlimited key stored, the combined limit could only say 不限额度. The image card's hint no longer says it totals image keys. `sumBalances`, its copy and the `.pkc-stat*` styles are gone; `quotaTotalUsd` still gives each key's limit as remaining plus used.
|
|
13
|
+
- Validation, 2026-10-17: 392 offline tests (one new for GPT title routing; the totals tests now cover each key's own limit), both rebuilt bundles and the versioned tarball. Live through the built route, `gpt-5.6-sol` answered at `low`, `medium`, `high`, `xhigh` and `max`; each request carried that `reasoning.effort`, and direct requests showed the gateway echoing it back with more reasoning tokens at `max`. `off` was refused before any request, and direct requests at `none` and `minimal` came back at `medium`. `scripts/live-check.mjs` passed: all ten models answered, the key's groups showed `gpt-5.6-sol` as reachable, and key rotation and the RPC projections were unchanged. The settings change is covered by rendering tests of the built bundle; it was not viewed in the DSH desktop app. No DSH restart.
|
|
14
|
+
|
|
3
15
|
## 1.3.1
|
|
4
16
|
|
|
5
17
|
- **The balance read no longer requests PackyCode's billing endpoints.** Every conversation balance refresh called `/v1/dashboard/billing/subscription` and `/v1/dashboard/billing/usage` and treated the answers as account-wide billing. PackyCode answers them per key: a live check on 2026-10-17 returned each key's own grant as `hard_limit_usd`, its own spend in cents as `total_usage` (`1387.9536` for $13.8795 used), and the placeholder `hard_limit_usd: 100000000` for an unlimited key. Nothing has displayed these figures since the account line was removed — its large negative remaining came from reading cents as dollars — so the requests only delayed each refresh. An API key cannot read the account's own balance at all; that sits behind the dashboard API, which takes a login session. An unlimited key therefore still shows only its own used amount.
|
package/README.en.md
CHANGED
|
@@ -28,7 +28,7 @@ The `PackyCode` model group ships with the plugin. No YAML edits and no separate
|
|
|
28
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 |
|
|
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
|
-
| **Visible balance** | Settings lists each key's remaining, used, limit, reset period, and a usage bar
|
|
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
32
|
| **Claude inside the plugin** | All three Claude models use HTTP Messages with DSH tools, permissions and conversation history; no local CLI is needed |
|
|
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 |
|
|
@@ -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.
|
|
50
|
+
dsh-packycode-api@1.3.3
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
Choose the **official** install source, then click **Install**. `1.3.
|
|
53
|
+
Choose the **official** install source, then click **Install**. `1.3.3` 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
|
|
|
@@ -64,14 +64,14 @@ Open **Settings → PackyCode**. Paste the API key under **Add a key**. A label
|
|
|
64
64
|
|
|
65
65
|
### 3. Select a model
|
|
66
66
|
|
|
67
|
-
Choose a model from the `PackyCode` group. All
|
|
67
|
+
Choose a model from the `PackyCode` group. All ten models use the plugin’s HTTP route. Your key’s groups must cover the selected model.
|
|
68
68
|
|
|
69
69
|
<details>
|
|
70
70
|
<summary>Verify the install / other profiles / a local checkout</summary>
|
|
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.
|
|
74
|
+
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.3`. 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
|
|
|
@@ -87,7 +87,7 @@ A profile without a browser can still use the model route. It just has no settin
|
|
|
87
87
|
|
|
88
88
|
**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. Check Claude group coverage per model; do not assume `cc-sale` serves all three models; GPT needs `codex` / `codex-sale`; DeepSeek needs `deepseek-officially` / `deepseek-sale`.
|
|
89
89
|
|
|
90
|
-
**Read the balance.**
|
|
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
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.
|
|
93
93
|
|
|
@@ -135,7 +135,7 @@ PackyCode has no dedicated search endpoint: one search is one model call with a
|
|
|
135
135
|
|
|
136
136
|
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.
|
|
137
137
|
|
|
138
|
-
The image card shows each image key's remaining
|
|
138
|
+
The image card shows each image key's remaining, used and limit, its reset period and a usage bar, without adding the keys up. It refreshes every minute or with **Refresh**, preserving a dated last-good reading when a refresh fails. Image key changes and successful generation invalidate the independent balance cache. Only image key quotas are queried, shown apart from conversation key balances.
|
|
139
139
|
|
|
140
140
|
Choose the image model and quality, then click **Create image** in a PackyCode conversation or explicitly request `packycode_image_generate`. Attach references and describe changes to use `/v1/images/edits`; new images use `/v1/images/generations`. The image model is independent of the conversation model and other image plugins can coexist.
|
|
141
141
|
|
|
@@ -160,6 +160,7 @@ Live checks on 2026-10-15 passed generation and editing at `low` on all three en
|
|
|
160
160
|
| `deepseek-flash` | HTTP, chat/completions, images allowed | Can be off; `low` / `high` / `max` | `deepseek-officially`, `deepseek-sale` |
|
|
161
161
|
| `gpt-6.1-sol` | HTTP, Responses, images allowed | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | `codex`, `codex-sale` |
|
|
162
162
|
| `gpt-6-astra` | Same | Same | Same |
|
|
163
|
+
| `gpt-5.6-sol` | Same | Same | Same |
|
|
163
164
|
| `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 |
|
|
164
165
|
| `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` |
|
|
165
166
|
| `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` |
|
|
@@ -169,7 +170,7 @@ Live checks on 2026-10-15 passed generation and editing at `low` on all three en
|
|
|
169
170
|
|
|
170
171
|
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.
|
|
171
172
|
|
|
172
|
-
All
|
|
173
|
+
All ten models in the current catalog declare text and image input. DeepSeek Flash follows the [official vision guide](https://api-docs.deepseek.com/guides/vision/); images are read through DSH's attachment service and sent with the Chat Completions request.
|
|
173
174
|
|
|
174
175
|
Gemini capabilities and limits follow Google's model pages: [Gemini 4 Argon](https://ai.google.dev/gemini-api/docs/models/gemini-4-argon), [Gemini 3.8 Flash](https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash). Both models use the native Gemini protocol through `/v1beta/models/{model}:streamGenerateContent`, streaming text, thinking and tool calls; images are read through DSH's attachment service. Thinking levels are sent with `thinkingLevel`. Both routes use the single-stream output limit without automatic continuation beyond 65,536 tokens. Available endpoints and groups follow the [PackyAPI live model catalog](https://www.packyapi.com/api/pricing).
|
|
175
176
|
|
|
@@ -182,7 +183,7 @@ Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit
|
|
|
182
183
|
<details>
|
|
183
184
|
<summary>Protocol, group and client compatibility</summary>
|
|
184
185
|
|
|
185
|
-
- All
|
|
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.
|
|
186
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.
|
|
187
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.
|
|
188
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/).
|
|
@@ -190,7 +191,7 @@ Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit
|
|
|
190
191
|
- 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.
|
|
191
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.
|
|
192
193
|
- 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.
|
|
193
|
-
- 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; Astra titles
|
|
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.
|
|
194
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.
|
|
195
196
|
- Codex Responses requests still need `prompt_cache_key`; the plugin supplies a missing session identity. Other Responses families omit it.
|
|
196
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.
|
|
@@ -200,7 +201,7 @@ Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit
|
|
|
200
201
|
|
|
201
202
|
## Update and uninstall
|
|
202
203
|
|
|
203
|
-
**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.
|
|
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. 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.3`); without it you may get a version that is merely the newest one published at least 24 hours ago.
|
|
204
205
|
|
|
205
206
|
**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.
|
|
206
207
|
|
|
@@ -216,7 +217,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
|
|
|
216
217
|
|
|
217
218
|
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.
|
|
218
219
|
|
|
219
|
-
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.
|
|
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.3`), 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.
|
|
220
221
|
|
|
221
222
|
**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:
|
|
222
223
|
|
|
@@ -232,7 +233,7 @@ Recovery: quit and reopen the desktop application, and the old version works. Th
|
|
|
232
233
|
|
|
233
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.
|
|
234
235
|
|
|
235
|
-
**The balance does not match the account bill?** Settings
|
|
236
|
+
**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.
|
|
236
237
|
|
|
237
238
|
**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.
|
|
238
239
|
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
| **自选显示的模型** | 设置页的模型列表默认全选;取消勾选后,切换列表立即隐藏该模型,已有会话仍可继续使用 |
|
|
29
29
|
| **多 Key 按顺序使用** | 额度用尽或 Key 被拒绝时自动切到下一个可用 Key;失败的那一步由重试接手 |
|
|
30
30
|
| **按分组选 Key** | 每个 Key 后面列出它所在的 PackyCode 分组和能用的模型;当前 Key 的分组用不了所选模型(例如没有 cc 分组却选了 Claude)时,这一轮自动改用分组合适的 Key |
|
|
31
|
-
| **余额一眼可见** | 设置页列出每个 Key
|
|
31
|
+
| **余额一眼可见** | 设置页列出每个 Key 的剩余、已用、额度、重置周期和用量条;输入框右侧显示当前 Key 的剩余额度 |
|
|
32
32
|
| **Claude 插件内直连** | 三个 Claude 模型通过 HTTP Messages 调用,使用 DSH 的工具、权限和完整对话历史,无需本机 CLI |
|
|
33
33
|
| **网页搜索可走 PackyCode** | 可选:DSH 的 `web_search` 改用 PackyCode 搜索,后端在 DeepSeek 与 GPT 之间切换;可只对 PackyCode 模型生效 |
|
|
34
34
|
| **图片生成与编辑** | 独立图片 Key,支持 GPT Image 2 / 2.5 Flare / 2.5 Sunburst、型号与质量设置、参考图编辑和 PNG 原图保存;已通过网关生图与编辑实测 |
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
打开桌面应用的**插件**页,点击**添加插件**,在**包名或地址**中填入**带版本号**的包名:
|
|
48
48
|
|
|
49
49
|
```text
|
|
50
|
-
dsh-packycode-api@1.3.
|
|
50
|
+
dsh-packycode-api@1.3.3
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
在**安装源**中选择**官方源**,然后点击**安装**。`1.3.
|
|
53
|
+
在**安装源**中选择**官方源**,然后点击**安装**。`1.3.3` 是写这份说明时的最新版,当前最新版本号以 [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
|
|
|
@@ -64,14 +64,14 @@ dsh-packycode-api@1.3.1
|
|
|
64
64
|
|
|
65
65
|
### 3. 选择模型
|
|
66
66
|
|
|
67
|
-
回到对话,在模型选择器里选 `PackyCode`
|
|
67
|
+
回到对话,在模型选择器里选 `PackyCode` 分组下的模型。十个模型都通过插件内 HTTP 调用;Key 的分组必须覆盖所选模型。
|
|
68
68
|
|
|
69
69
|
<details>
|
|
70
70
|
<summary>检查安装 / 其它 profile / 本地目录</summary>
|
|
71
71
|
|
|
72
72
|
桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
|
|
73
73
|
|
|
74
|
-
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.
|
|
74
|
+
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.3`。不要对 `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
|
|
|
@@ -87,7 +87,7 @@ dsh-packycode-api@1.3.1
|
|
|
87
87
|
|
|
88
88
|
**看分组。** 「账户与 Key」里每个 Key 下面列出它所在的 PackyCode 分组(鼠标悬停可看分组说明),以及能用哪些模型:✓ 可用、◐ 部分可用、✕ 不可用。分组由网关自己报出(对一个不存在的模型 id 的拒绝里会写明「已选分组」,不产生费用),再对照 PackyCode 公开的模型分组表。Claude 的分组覆盖要按具体模型确认,不能假定 `cc-sale` 支持全部三个型号;GPT 需要 `codex` / `codex-sale`;DeepSeek 需要 `deepseek-officially` / `deepseek-sale`。
|
|
89
89
|
|
|
90
|
-
**看余额。** 「账户与 Key
|
|
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
92
|
**用 Claude。** 直接选择三个 Claude 模型之一即可,无需安装 CLI。思考档位会发送给模型;对话历史、附件、工具调用、计划模式和权限均由 DSH 管理。
|
|
93
93
|
|
|
@@ -135,7 +135,7 @@ PackyCode 没有专用搜索接口:一次搜索就是一次带服务端搜索
|
|
|
135
135
|
|
|
136
136
|
在 **设置 → PackyCode → 图片生成与编辑 → 添加图片 Key** 填入专用 Key。它与「账户与 Key」里的对话 Key 分开保存;图片请求只用此处选择的 Key。添加 Key 不会自动发起付费测试。
|
|
137
137
|
|
|
138
|
-
图片卡片会显示每个图片 Key
|
|
138
|
+
图片卡片会显示每个图片 Key 的剩余、已用、额度、重置周期和使用进度,不汇总合计。余额每分钟更新,也可点卡片右上角的「刷新」;读取失败时保留上次余额并标出时间。图片 Key 变更或生图成功后会清理图片余额缓存。仅查询专用图片 Key 的额度,与对话 Key 的余额分开显示。
|
|
139
139
|
|
|
140
140
|
选择请求型号与质量后,可在 PackyCode 对话里点 **创建图片** 补充描述,或明确要求使用 `packycode_image_generate`。上传参考图后描述修改要求会调用 `/v1/images/edits`;新图调用 `/v1/images/generations`。图片型号独立于对话型号,其他插件的图片工具仍可并存。
|
|
141
141
|
|
|
@@ -160,6 +160,7 @@ Claude 与其它模型一样,通过 DSH 的 `packycode_image_generate` 工具
|
|
|
160
160
|
| `deepseek-flash` | HTTP,chat/completions,可带图 | 可关;`low` / `high` / `max` | `deepseek-officially`、`deepseek-sale` |
|
|
161
161
|
| `gpt-6.1-sol` | HTTP,Responses,可带图 | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | `codex`、`codex-sale` |
|
|
162
162
|
| `gpt-6-astra` | 同上 | 同上 | 同上 |
|
|
163
|
+
| `gpt-5.6-sol` | 同上 | 同上 | 同上 |
|
|
163
164
|
| `grok-4.7`(Grok 4.7) | HTTP,Responses,可带图;500k 上下文 | 不能关;`low` / `medium` / `high` / `xhigh` | 以设置页从网关读取的 Grok 分组覆盖为准 |
|
|
164
165
|
| `gemini-4-argon`(Gemini 4 Argon) | HTTP,Gemini 原生流式,可带图;1,048,576 上下文,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
|
|
165
166
|
| `gemini-3.8-flash`(Gemini 3.8 Flash) | HTTP,Gemini 原生流式,可带图;1,048,576 输入,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
|
|
@@ -169,7 +170,7 @@ Claude 与其它模型一样,通过 DSH 的 `packycode_image_generate` 工具
|
|
|
169
170
|
|
|
170
171
|
列表里看得到模型,不代表当前令牌分组一定放行。换分组时不要把对应分组一起去掉。
|
|
171
172
|
|
|
172
|
-
当前目录中的
|
|
173
|
+
当前目录中的 10 个模型都声明了文字和图像输入。DeepSeek Flash 的识图能力按 [官方图像理解文档](https://api-docs.deepseek.com/zh-cn/guides/vision/) 配置;图片通过 DSH 附件服务读取,再随 chat/completions 请求发送。
|
|
173
174
|
|
|
174
175
|
Gemini 的能力与限制来自 Google 官方模型页:[Gemini 4 Argon](https://ai.google.dev/gemini-api/docs/models/gemini-4-argon)、[Gemini 3.8 Flash](https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash)。两款模型统一使用 Gemini 原生协议,通过 `/v1beta/models/{model}:streamGenerateContent` 流式返回文字、思考和工具调用,图片仍由 DSH 附件服务读取。思考档位通过 `thinkingLevel` 传入。两者均使用单次流式输出上限,不执行超过 65,536 token 的自动续写。可用端点与分组以 [PackyAPI 实时模型目录](https://www.packyapi.com/api/pricing) 为准。
|
|
175
176
|
|
|
@@ -182,7 +183,7 @@ Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses
|
|
|
182
183
|
<details>
|
|
183
184
|
<summary>协议、分组与客户端兼容性</summary>
|
|
184
185
|
|
|
185
|
-
-
|
|
186
|
+
- 十个模型均在插件内通过 HTTP 调用。Claude 使用 `anthropic-messages`,base URL 不带 `/v1`;SDK 会拼出 `/v1/messages`。模型、协议、上下文和档位映射保持不变。
|
|
186
187
|
- Claude 请求带上实测通过的 CLI 兼容请求头、Bearer 认证、会话元数据和身份系统提示前缀,并保留原始 DSH 系统说明、工具 schema、附件及签名回放。兼容版本固定为 `2.1.295`,无需检测或启动本机 CLI。
|
|
187
188
|
- Bearer 只来自插件保存并按模型选中的 Key;不读取 CLI 登录状态、外部配置或环境 Key,不切换其它提供商。
|
|
188
189
|
- 这组兼容处理不代表服务端取消了分组的客户端限制。当前 Key 仍需覆盖 Claude;客户端政策拒绝会直接说明原因,不禁用 Key,也不自动改用 CLI。服务端规则变化可能使兼容处理失效,详见 [PackyCode 分组政策](https://docs.packyapi.ai/docs/token/)。
|
|
@@ -190,7 +191,7 @@ Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses
|
|
|
190
191
|
- Claude 流收到 `message_stop` 后即可完成,不再等网关关闭 HTTP 连接;跨分片的 CRLF 换行保持完整。流内错误与取消仍会返回,未收到响应头的等待也不会被当作成功。
|
|
191
192
|
- DSH 的 bash/read/write/edit/glob/grep 在 HTTP 请求中使用已声明的 CLI 标准名称(如 `Bash`、`Read`),其它工具使用 MCP 命名;回到 DSH 时仅还原本次实际提供的工具名称,未知名称不会获得执行权限。工具参数、调用 id、结果和历史签名不变。思考展示采用 CLI 默认:Haiku 为 `omitted`,Sonnet / Opus 为 `updates`(附带相应 beta)。所选推理强度和输出上限不变,空思考块的签名仍完整回传;明确指定的显示模式优先。
|
|
192
193
|
- Claude 请求中的旧工作目录句式会在与当前会话元数据一致时转换为单独的 JSON 路径,避免把提示语的句号复制进路径。真实目录名中的点、Windows 盘符、UNC、空格和反斜杠保持不变;命令参数不会被自动修剪。命令工具优先使用当前目录或 `workdir`,Windows 的 PowerShell 工具保持其原有语法。
|
|
193
|
-
- 普通 Claude 对话保留完整 DSH 工具循环、权限及计划模式;网页搜索和生图使用宿主工具。会话标题改由 `claude-haiku-5-5` 通过 HTTP 生成;Astra
|
|
194
|
+
- 普通 Claude 对话保留完整 DSH 工具循环、权限及计划模式;网页搜索和生图使用宿主工具。会话标题改由 `claude-haiku-5-5` 通过 HTTP 生成;GPT-6 Astra 与 GPT-5.6 Sol 的标题交给 GPT-6.1 Sol。
|
|
194
195
|
- 已移除旧 `claude_code` 委派工具、CLI 运行器、检测与安装界面、配置字段及本机 MCP 桥。旧配置中的 `claudeCliPath`、`claudeCliMode` 和 `claudeCliCwd` 不再生效;用户独立安装的 Claude Code 不受影响。
|
|
195
196
|
- Codex 的 Responses 请求仍需要 `prompt_cache_key`;插件补上缺失的会话标识,其它 Responses 模型省略该字段。
|
|
196
197
|
- 构建后可用 `node scripts/claude-check.mjs` 验证三个 Claude 模型的订单核算、工单归类、库存补货、只读库存查询工具回传、商品色卡标注和中文标题,并核对业务答案。`node scripts/claude-stability.mjs --start --hours 6 --key-label <备注名>` 开始有截止时间和请求上限的采样;定时执行同一脚本继续采样,用 `--status` 查看记录。业务场景版本和每轮构建分别记录;测试消息拒绝将对应检查记为失败,其余预定检查继续,真实凭据或额度错误仍会停止整个窗口。测试不改变已保存的凭据,也不启动 DSH。
|
|
@@ -200,7 +201,7 @@ Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses
|
|
|
200
201
|
|
|
201
202
|
## 更新与卸载
|
|
202
203
|
|
|
203
|
-
**更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json`
|
|
204
|
+
**更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。安装遵守 pnpm 的发布冷却策略,不会绕过它:默认情况下 pnpm 会直接装上这个版本,并把它记进 profile 的 `minimumReleaseAgeExclude`;如果你给 pnpm 配了更严格的策略,按钮会提示什么时候可以更新。新版本先发布到 npm 的 `next` 标签,约 25 小时后才成为 `latest`,按钮和不带版本号的安装都要到那时才会看到它;等不及可以在插件页填写精确版本安装。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.3.3`),否则装上的可能是发布满 24 小时的旧版。
|
|
204
205
|
|
|
205
206
|
**从本地路径安装切回官方源时,必须先卸载。** 「更新」是 `pnpm add`,它只重写包目录里的文件,不会删除更早一次本地路径安装留在包目录内的 `node_modules`;那份旧依赖会继续挡住 DSH 自带的包,插件行加载失败且看不出原因。「卸载」执行的是 `pnpm remove`,会删掉整个包目录,重装才是干净的。用 `node scripts/install-check.mjs` 可以确认当前 profile 是否处于这个状态。
|
|
206
207
|
|
|
@@ -216,7 +217,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
|
|
|
216
217
|
|
|
217
218
|
有输出就是残留:从 npm 安装的包不会自带 `node_modules`,这一份通常来自更早一次「本地路径」安装(`file:` 或直接写路径,而不是 `link:`)。它位于 profile 目录内部,而 DSH 解析插件导入时优先使用 profile 内的副本,所以插件拿到的仍是那份旧依赖 —— 例如 `@deepseek-ai/schemastery` 3.18.2 没有 `Schema.volatile()`,模块在导入阶段就抛错。导入失败时 Loader 不会为这一行创建 fiber,客户端 bundle 也不会被组合,于是模型路由、RPC 通道和设置页全都不会出现。
|
|
218
219
|
|
|
219
|
-
修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.3.
|
|
220
|
+
修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.3.3`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
|
|
220
221
|
|
|
221
222
|
**首次安装后,设置页每张卡片都显示 `transport failure for /packycode-api/…: HTTP 405`?** 实际装上的是 1.1.6 或更早的版本,通常是因为安装时只填了包名:安装提示显示的是最新版,pnpm 实际装的却是发布满 24 小时的旧版(见「安装插件」)。这些旧版要等 DSH 重启、重建它的 `connection` 服务之后,才能注册设置通道。所以首次安装后 `/packycode-api` 通道还不存在,请求落到静态页面,返回 405;模型路由和设置页本身照常加载。1.1.7 起首次安装不需要重启。先确认装上的版本:
|
|
222
223
|
|
|
@@ -230,7 +231,7 @@ grep '"version"' ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/package.
|
|
|
230
231
|
|
|
231
232
|
**Claude 报「只接受官方 Claude Code CLI」或「客户端存在异常」?** 这是网关的客户端政策拒绝,不等于 Key 失效。先在「账户与 Key」和 PackyCode 模型广场确认所选模型的分组覆盖;确认覆盖后仍被拒绝,可用诊断信息定位。插件不会因此禁用 Key、切换提供商或启动 CLI。
|
|
232
233
|
|
|
233
|
-
**余额对不上账号账单?**
|
|
234
|
+
**余额对不上账号账单?** 设置页只显示这里列出的每个 Key 自己的额度,不含同账号下没添加到本插件的其它令牌。API Key 读不到账号的总余额,请到 PackyCode 控制台查看。个别 Key 读失败时会在它那一行注明。
|
|
234
235
|
|
|
235
236
|
**余额显示 `读取失败: https://www.packyapi.com: fetch failed`?** 这是 1.0.4 及更早版本的默认余额接口在国内网络下被 DNS 污染,开了代理才能连上,所以有时正常、有时失败。新版本默认改用 `https://www.packyapi.ai`。模型列表是插件内置的,不需要向 PackyCode 查询,不受影响。
|
|
236
237
|
|
package/lib/client.js
CHANGED
|
@@ -44,7 +44,7 @@ window.__ModuleLoader__.load({
|
|
|
44
44
|
imagesBalanceFailed: "图片余额读取失败",
|
|
45
45
|
imagesComposerLabel: "图片剩余额度",
|
|
46
46
|
imagesBalanceKey: "当前图片 Key",
|
|
47
|
-
imagesBalanceHint: "
|
|
47
|
+
imagesBalanceHint: "这里只显示图片 Key 的余额,每分钟自动更新,也可手动刷新;对话 Key 的余额单独显示。",
|
|
48
48
|
imagesIntro: "使用独立图片 Key 调用 PackyCode 图片接口。生成新图或上传图片后描述修改要求;图片模型独立于对话模型。原始 PNG 保存在本机,聊天中显示图片预览。",
|
|
49
49
|
imagesEnabled: "图片生成与编辑",
|
|
50
50
|
imagesOn: "启用",
|
|
@@ -80,11 +80,6 @@ window.__ModuleLoader__.load({
|
|
|
80
80
|
imagesDownloadFailed: "原图保存失败。请确认本机原图仍存在,然后点击“下载原图”重试。",
|
|
81
81
|
refresh: "刷新",
|
|
82
82
|
refreshing: "刷新中…",
|
|
83
|
-
totalRemaining: "合计剩余",
|
|
84
|
-
totalUsed: "合计已用",
|
|
85
|
-
totalLimit: "合计额度",
|
|
86
|
-
totalPartial: "有 {count} 个 Key 未读到余额,未计入合计。",
|
|
87
|
-
totalStale: "其中 {count} 个 Key 本次读取失败,显示的是上次读到的余额。",
|
|
88
83
|
staleNote: "上次读取于 {time},本次读取失败:{error}",
|
|
89
84
|
empty: "还没有 Key。添加一个即可在模型选择器里看到 PackyCode 的模型。",
|
|
90
85
|
inviteLead: "还没有 PackyCode 的 API Key?可以通过邀请链接注册:",
|
|
@@ -157,6 +152,8 @@ window.__ModuleLoader__.load({
|
|
|
157
152
|
versionUpdate: "更新插件",
|
|
158
153
|
versionUpdating: "正在更新…",
|
|
159
154
|
versionUpdateFailed: "更新失败,请重试,或在插件页重新安装。",
|
|
155
|
+
versionCooling: "v{version} 发布不久,DSH 的 pnpm 要等新版本过了发布冷却期才会安装,请稍后再更新。",
|
|
156
|
+
versionCoolingUntil: "v{version} 发布不久,DSH 的 pnpm 要等新版本过了发布冷却期才会安装,{time} 之后可以更新。",
|
|
160
157
|
versionUpdated: "已更新到 v{version}。",
|
|
161
158
|
versionUpdatedHint: "新版本需要完全退出并重新打开桌面应用才会生效;只刷新页面不会重载 Host 里的插件。",
|
|
162
159
|
versionRestart: "如何重启",
|
|
@@ -220,7 +217,7 @@ window.__ModuleLoader__.load({
|
|
|
220
217
|
imagesBalanceFailed: "Could not read image balances",
|
|
221
218
|
imagesComposerLabel: "Image balance",
|
|
222
219
|
imagesBalanceKey: "Current image key",
|
|
223
|
-
imagesBalanceHint: "Only image key
|
|
220
|
+
imagesBalanceHint: "Only image key balances appear here. They update every minute, or use Refresh. Conversation key balances are shown separately.",
|
|
224
221
|
imagesIntro: "Use a dedicated image key with PackyCode’s Images API. Describe a new image or attach images with editing instructions. The image engine is independent of the conversation model. Original PNGs are saved locally and previews appear in chat.",
|
|
225
222
|
imagesEnabled: "Generate and edit images",
|
|
226
223
|
imagesOn: "Enabled",
|
|
@@ -256,11 +253,6 @@ window.__ModuleLoader__.load({
|
|
|
256
253
|
imagesDownloadFailed: "Could not save the original. Check that the original file still exists on this device, then try Download original again.",
|
|
257
254
|
refresh: "Refresh",
|
|
258
255
|
refreshing: "Refreshing…",
|
|
259
|
-
totalRemaining: "Total remaining",
|
|
260
|
-
totalUsed: "Total used",
|
|
261
|
-
totalLimit: "Total limit",
|
|
262
|
-
totalPartial: "{count} key(s) could not be read and are not counted here.",
|
|
263
|
-
totalStale: "{count} of these keys failed this read and show their last read balance.",
|
|
264
256
|
staleNote: "Last read at {time}; this read failed: {error}",
|
|
265
257
|
empty: "No keys yet. Add one and the PackyCode models appear in the model picker.",
|
|
266
258
|
inviteLead: "No PackyCode API key yet? You can register through this invite link:",
|
|
@@ -325,6 +317,8 @@ window.__ModuleLoader__.load({
|
|
|
325
317
|
versionUpdate: "Update plugin",
|
|
326
318
|
versionUpdating: "Updating…",
|
|
327
319
|
versionUpdateFailed: "The update failed. Try again, or reinstall from the Plugins page.",
|
|
320
|
+
versionCooling: "v{version} was published recently. DSH’s pnpm installs a new version only after its release cooldown; update again later.",
|
|
321
|
+
versionCoolingUntil: "v{version} was published recently. DSH’s pnpm installs a new version only after its release cooldown; update after {time}.",
|
|
328
322
|
versionUpdated: "Updated to v{version}.",
|
|
329
323
|
versionUpdatedHint: "Quit and reopen the desktop application. Refreshing the page does not reload the plugin inside the Host.",
|
|
330
324
|
versionRestart: "How to restart",
|
|
@@ -449,10 +443,6 @@ window.__ModuleLoader__.load({
|
|
|
449
443
|
.pkc-dot[data-tone=warn]{background:var(--dsw-alias-state-warn-primary)}
|
|
450
444
|
.pkc-dot[data-tone=danger]{background:var(--dsw-alias-state-error-primary)}
|
|
451
445
|
|
|
452
|
-
.pkc-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
|
|
453
|
-
.pkc-stat{display:flex;flex-direction:column;gap:2px;padding:10px 12px;border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-interactive-bg-hover)}
|
|
454
|
-
.pkc-stat-label{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
455
|
-
.pkc-stat-value{font-size:20px;font-weight:600;line-height:28px;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
456
446
|
|
|
457
447
|
.pkc-list{display:flex;flex-direction:column}
|
|
458
448
|
.pkc-row{display:flex;flex-direction:column;gap:8px;padding:12px 0;border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
@@ -507,7 +497,6 @@ window.__ModuleLoader__.load({
|
|
|
507
497
|
.pkc-composer{display:inline-flex;flex:0 0 auto;align-items:center;box-sizing:border-box;height:28px;color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:20px;font-variant-numeric:tabular-nums;white-space:nowrap;user-select:none}
|
|
508
498
|
|
|
509
499
|
@media (max-width:640px){
|
|
510
|
-
.pkc-stats{grid-template-columns:1fr}
|
|
511
500
|
.pkc-setting{grid-template-columns:1fr}
|
|
512
501
|
.pkc-models{grid-template-columns:1fr}
|
|
513
502
|
.pkc-setting>.pkc-label{padding-top:0}
|
|
@@ -589,12 +578,13 @@ window.__ModuleLoader__.load({
|
|
|
589
578
|
//#endregion
|
|
590
579
|
//#region src/totals.js
|
|
591
580
|
/**
|
|
592
|
-
*
|
|
581
|
+
* The quota total one key shows.
|
|
593
582
|
*
|
|
594
|
-
* The per-key quota (`/api/usage/token/`) is what a key can spend
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
583
|
+
* The per-key quota (`/api/usage/token/`) is what a key can spend. Its
|
|
584
|
+
* displayed total is remaining plus used, so a key without a periodic cap
|
|
585
|
+
* still shows one. Settings shows each key on its own row and adds nothing
|
|
586
|
+
* across keys: an API key cannot read the PackyCode account's own balance,
|
|
587
|
+
* and an unlimited key has no amount to add.
|
|
598
588
|
*
|
|
599
589
|
* @module dsh-packycode-api/totals
|
|
600
590
|
*/
|
|
@@ -607,42 +597,6 @@ window.__ModuleLoader__.load({
|
|
|
607
597
|
if (remaining === void 0 || used === void 0) return void 0;
|
|
608
598
|
return amount(Number((remaining + used).toFixed(6)));
|
|
609
599
|
}
|
|
610
|
-
/**
|
|
611
|
-
* Add up the balance rows the host returned.
|
|
612
|
-
* @param rows - the `keys` array of a balance projection.
|
|
613
|
-
* @returns summed amounts plus the conditions that make a sum incomplete:
|
|
614
|
-
* keys whose read failed, and keys with unlimited quota.
|
|
615
|
-
*/
|
|
616
|
-
function sumBalances(rows) {
|
|
617
|
-
const total = {
|
|
618
|
-
keys: 0,
|
|
619
|
-
counted: 0,
|
|
620
|
-
unread: 0,
|
|
621
|
-
unlimited: 0,
|
|
622
|
-
remainingUsd: 0,
|
|
623
|
-
usedUsd: 0,
|
|
624
|
-
limitUsd: 0
|
|
625
|
-
};
|
|
626
|
-
for (const row of Array.isArray(rows) ? rows : []) {
|
|
627
|
-
total.keys += 1;
|
|
628
|
-
const usage = row?.usage;
|
|
629
|
-
if (usage === void 0) {
|
|
630
|
-
total.unread += 1;
|
|
631
|
-
continue;
|
|
632
|
-
}
|
|
633
|
-
total.counted += 1;
|
|
634
|
-
if (usage.unlimitedQuota === true) {
|
|
635
|
-
total.unlimited += 1;
|
|
636
|
-
continue;
|
|
637
|
-
}
|
|
638
|
-
const remaining = amount(usage.availableUsd);
|
|
639
|
-
const used = amount(usage.usedUsd);
|
|
640
|
-
if (remaining !== void 0) total.remainingUsd += remaining;
|
|
641
|
-
if (used !== void 0) total.usedUsd += used;
|
|
642
|
-
}
|
|
643
|
-
total.limitUsd = Number((total.remainingUsd + total.usedUsd).toFixed(6));
|
|
644
|
-
return total;
|
|
645
|
-
}
|
|
646
600
|
//#endregion
|
|
647
601
|
//#region src/client-balance.jsx
|
|
648
602
|
const fill = (text, values) => text.replace(/\{(\w+)\}/gu, (match, name) => values[name] === void 0 ? match : String(values[name]));
|
|
@@ -760,50 +714,6 @@ window.__ModuleLoader__.load({
|
|
|
760
714
|
})]
|
|
761
715
|
});
|
|
762
716
|
}
|
|
763
|
-
/** Only this card's keys contribute; account-wide billing is never added. */
|
|
764
|
-
function BalanceTotals({ rows, t }) {
|
|
765
|
-
const totals = sumBalances(rows);
|
|
766
|
-
const stale = rows.filter((row) => row.stale !== void 0).length;
|
|
767
|
-
const known = (field) => rows.some((row) => row.usage?.unlimitedQuota !== true && Number.isFinite(row.usage?.[field]));
|
|
768
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
769
|
-
className: "pkc-stats",
|
|
770
|
-
children: [
|
|
771
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
772
|
-
className: "pkc-stat",
|
|
773
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
774
|
-
className: "pkc-stat-label",
|
|
775
|
-
children: t("totalRemaining")
|
|
776
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
777
|
-
className: "pkc-stat-value",
|
|
778
|
-
children: [known("availableUsd") ? formatUsd(totals.remainingUsd) : totals.unlimited > 0 ? t("keyUnlimited") : "—", known("availableUsd") && totals.unlimited > 0 && ` + ${t("keyUnlimited")}`]
|
|
779
|
-
})]
|
|
780
|
-
}),
|
|
781
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
782
|
-
className: "pkc-stat",
|
|
783
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
784
|
-
className: "pkc-stat-label",
|
|
785
|
-
children: t("totalUsed")
|
|
786
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
787
|
-
className: "pkc-stat-value",
|
|
788
|
-
children: known("usedUsd") ? formatUsd(totals.usedUsd) : "—"
|
|
789
|
-
})]
|
|
790
|
-
}),
|
|
791
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
792
|
-
className: "pkc-stat",
|
|
793
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
794
|
-
className: "pkc-stat-label",
|
|
795
|
-
children: t("totalLimit")
|
|
796
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
797
|
-
className: "pkc-stat-value",
|
|
798
|
-
children: totals.unlimited > 0 ? t("keyUnlimited") : known("availableUsd") && known("usedUsd") ? formatUsd(totals.limitUsd) : "—"
|
|
799
|
-
})]
|
|
800
|
-
})
|
|
801
|
-
]
|
|
802
|
-
}), (totals.unread > 0 || stale > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
803
|
-
className: "pkc-note",
|
|
804
|
-
children: [totals.unread > 0 ? fill(t("totalPartial"), { count: totals.unread }) : void 0, stale > 0 ? fill(t("totalStale"), { count: stale }) : void 0].filter(Boolean).join(" ")
|
|
805
|
-
})] });
|
|
806
|
-
}
|
|
807
717
|
//#endregion
|
|
808
718
|
//#region src/client-image-balance.jsx
|
|
809
719
|
/** Mirror the host's next-usable image key using only its public balance rows. */
|
|
@@ -1372,13 +1282,10 @@ window.__ModuleLoader__.load({
|
|
|
1372
1282
|
className: "pkc-note",
|
|
1373
1283
|
children: t("imagesQualityHint")
|
|
1374
1284
|
}),
|
|
1375
|
-
(state.keys ?? []).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1376
|
-
rows,
|
|
1377
|
-
t
|
|
1378
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1285
|
+
(state.keys ?? []).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1379
1286
|
className: "pkc-note",
|
|
1380
1287
|
children: balanceLoading ? t("imagesBalanceLoading") : t("imagesBalanceHint")
|
|
1381
|
-
})
|
|
1288
|
+
}),
|
|
1382
1289
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1383
1290
|
className: "pkc-list",
|
|
1384
1291
|
children: (state.keys ?? []).map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1900,7 +1807,11 @@ window.__ModuleLoader__.load({
|
|
|
1900
1807
|
/** The catalog's models by family, for the "reaches" line under each key. */
|
|
1901
1808
|
const MODEL_FAMILIES = [
|
|
1902
1809
|
["DeepSeek", ["deepseek-flash"]],
|
|
1903
|
-
["GPT", [
|
|
1810
|
+
["GPT", [
|
|
1811
|
+
"gpt-6.1-sol",
|
|
1812
|
+
"gpt-6-astra",
|
|
1813
|
+
"gpt-5.6-sol"
|
|
1814
|
+
]],
|
|
1904
1815
|
["Grok", ["grok-4.7"]],
|
|
1905
1816
|
["Gemini", ["gemini-4-argon", "gemini-3.8-flash"]],
|
|
1906
1817
|
["Claude", [
|
|
@@ -2210,30 +2121,23 @@ window.__ModuleLoader__.load({
|
|
|
2210
2121
|
keys.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2211
2122
|
className: "pkc-note",
|
|
2212
2123
|
children: t("empty")
|
|
2213
|
-
}), status !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegisterInvite, { t })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2214
|
-
|
|
2215
|
-
|
|
2124
|
+
}), status !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegisterInvite, { t })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2125
|
+
className: "pkc-list",
|
|
2126
|
+
children: keys.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyRow, {
|
|
2127
|
+
entry,
|
|
2128
|
+
row: rows.find((row) => row.id === entry.id),
|
|
2129
|
+
group: groups?.keys?.find((group) => group.id === entry.id),
|
|
2130
|
+
descriptions: groups?.descriptions,
|
|
2131
|
+
groupsLoading,
|
|
2132
|
+
busy,
|
|
2133
|
+
rename,
|
|
2134
|
+
onAction: mutate,
|
|
2216
2135
|
t
|
|
2217
|
-
})
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
row: rows.find((row) => row.id === entry.id),
|
|
2223
|
-
group: groups?.keys?.find((group) => group.id === entry.id),
|
|
2224
|
-
descriptions: groups?.descriptions,
|
|
2225
|
-
groupsLoading,
|
|
2226
|
-
busy,
|
|
2227
|
-
rename,
|
|
2228
|
-
onAction: mutate,
|
|
2229
|
-
t
|
|
2230
|
-
}, entry.id))
|
|
2231
|
-
}),
|
|
2232
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2233
|
-
className: "pkc-note",
|
|
2234
|
-
children: t("keysHint")
|
|
2235
|
-
})
|
|
2236
|
-
] }),
|
|
2136
|
+
}, entry.id))
|
|
2137
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2138
|
+
className: "pkc-note",
|
|
2139
|
+
children: t("keysHint")
|
|
2140
|
+
})] }),
|
|
2237
2141
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
2238
2142
|
className: "pkc-add",
|
|
2239
2143
|
onSubmit: addKey,
|
|
@@ -2575,12 +2479,27 @@ window.__ModuleLoader__.load({
|
|
|
2575
2479
|
]
|
|
2576
2480
|
});
|
|
2577
2481
|
}
|
|
2482
|
+
/** A release-age wait can end tomorrow, so the date is part of the time. */
|
|
2483
|
+
function readDateTime(ms) {
|
|
2484
|
+
try {
|
|
2485
|
+
return new Date(ms).toLocaleString(void 0, {
|
|
2486
|
+
month: "numeric",
|
|
2487
|
+
day: "numeric",
|
|
2488
|
+
hour: "2-digit",
|
|
2489
|
+
minute: "2-digit"
|
|
2490
|
+
});
|
|
2491
|
+
} catch {
|
|
2492
|
+
return new Date(ms).toISOString().slice(0, 16).replace("T", " ");
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2578
2495
|
function VersionCard({ call, t }) {
|
|
2579
2496
|
const [info, setInfo] = react.useState();
|
|
2580
2497
|
const [loading, setLoading] = react.useState(true);
|
|
2581
2498
|
const [error, setError] = react.useState(false);
|
|
2582
2499
|
const [updating, setUpdating] = react.useState(false);
|
|
2583
2500
|
const [updateFailed, setUpdateFailed] = react.useState(false);
|
|
2501
|
+
/** A version pnpm still holds back under the profile's release-age policy. */
|
|
2502
|
+
const [pending, setPending] = react.useState();
|
|
2584
2503
|
const [updated, setUpdated] = react.useState();
|
|
2585
2504
|
const [restartHint, setRestartHint] = react.useState(false);
|
|
2586
2505
|
const generation = react.useRef(0);
|
|
@@ -2605,7 +2524,12 @@ window.__ModuleLoader__.load({
|
|
|
2605
2524
|
const update = () => {
|
|
2606
2525
|
setUpdating(true);
|
|
2607
2526
|
setUpdateFailed(false);
|
|
2527
|
+
setPending(void 0);
|
|
2608
2528
|
call("plugin/update").then((value) => {
|
|
2529
|
+
if (value?.pending === true) {
|
|
2530
|
+
setPending(value);
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2609
2533
|
setUpdated(value);
|
|
2610
2534
|
setRestartHint(false);
|
|
2611
2535
|
}).catch(() => setUpdateFailed(true)).finally(() => setUpdating(false));
|
|
@@ -2667,6 +2591,14 @@ window.__ModuleLoader__.load({
|
|
|
2667
2591
|
role: "alert",
|
|
2668
2592
|
children: t("versionUpdateFailed")
|
|
2669
2593
|
}),
|
|
2594
|
+
pending !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2595
|
+
className: "pkc-note",
|
|
2596
|
+
role: "status",
|
|
2597
|
+
children: typeof pending.availableAt === "number" ? fill(t("versionCoolingUntil"), {
|
|
2598
|
+
version: pending.version,
|
|
2599
|
+
time: readDateTime(pending.availableAt)
|
|
2600
|
+
}) : fill(t("versionCooling"), { version: pending.version })
|
|
2601
|
+
}),
|
|
2670
2602
|
updated !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2671
2603
|
className: "pkc-callout",
|
|
2672
2604
|
"data-tone": "info",
|
package/lib/index.js
CHANGED
|
@@ -700,9 +700,9 @@ function createDiagnostics({ sessionQuery, readEvents, ring, groups, secrets = a
|
|
|
700
700
|
*
|
|
701
701
|
* Effort levels come from each vendor's documentation, because an effort the
|
|
702
702
|
* model does not support is refused before the request is sent: Claude Opus
|
|
703
|
-
* 5.5 (platform.claude.com effort docs), GPT-6.1 Sol and GPT-6
|
|
704
|
-
* `reasoning.effort` (developers.openai.com model pages), DeepSeek
|
|
705
|
-
* mode (`thinking` toggle plus `low|high|max` effort), Grok 4.7
|
|
703
|
+
* 5.5 (platform.claude.com effort docs), GPT-6.1 Sol, GPT-6 Astra and GPT-5.6
|
|
704
|
+
* Sol `reasoning.effort` (developers.openai.com model pages), DeepSeek
|
|
705
|
+
* thinking mode (`thinking` toggle plus `low|high|max` effort), Grok 4.7
|
|
706
706
|
* (docs.x.ai/developers/grok-4-7), and Gemini 4 Argon
|
|
707
707
|
* (ai.google.dev/gemini-api/docs/models/gemini-4-argon and docs/openai).
|
|
708
708
|
*
|
|
@@ -735,7 +735,8 @@ const DEEPSEEK_LEVELS = Object.freeze({
|
|
|
735
735
|
});
|
|
736
736
|
/**
|
|
737
737
|
* Claude 5.5 models, GPT-6.1 Sol, and GPT-6 Astra cannot disable reasoning, so `off` is
|
|
738
|
-
* pinned unsupported.
|
|
738
|
+
* pinned unsupported; GPT-5.6 Sol is kept on the same levels. Claude effort is
|
|
739
|
+
* sent through the native Messages API.
|
|
739
740
|
*/
|
|
740
741
|
const ALWAYS_THINKING_LEVELS = Object.freeze({
|
|
741
742
|
off: null,
|
|
@@ -803,6 +804,19 @@ const PACKYCODE_MODELS = Object.freeze([
|
|
|
803
804
|
maxTokens: 128e3,
|
|
804
805
|
thinkingLevelMap: ALWAYS_THINKING_LEVELS
|
|
805
806
|
}),
|
|
807
|
+
Object.freeze({
|
|
808
|
+
id: "gpt-5.6-sol",
|
|
809
|
+
name: "GPT-5.6 Sol",
|
|
810
|
+
api: "openai-responses",
|
|
811
|
+
provider: PROVIDER,
|
|
812
|
+
baseUrl: RESPONSES_BASE,
|
|
813
|
+
reasoning: true,
|
|
814
|
+
input: Object.freeze(["text", "image"]),
|
|
815
|
+
cost: NO_COST,
|
|
816
|
+
contextWindow: 105e4,
|
|
817
|
+
maxTokens: 128e3,
|
|
818
|
+
thinkingLevelMap: ALWAYS_THINKING_LEVELS
|
|
819
|
+
}),
|
|
806
820
|
Object.freeze({
|
|
807
821
|
id: "grok-4.7",
|
|
808
822
|
name: "Grok 4.7",
|
|
@@ -2528,7 +2542,11 @@ function createModelSelectionController({ readPreference, writePreference, isWri
|
|
|
2528
2542
|
* The running version comes from this package. The latest version comes from
|
|
2529
2543
|
* the npm registry. An in-place update is only offered for a registry install,
|
|
2530
2544
|
* and it installs that exact version with the host's own pnpm — never through
|
|
2531
|
-
* `dsh plugin --profile desktop`, which the desktop app refuses.
|
|
2545
|
+
* `dsh plugin --profile desktop`, which the desktop app refuses. The install
|
|
2546
|
+
* runs under the profile's own `minimumReleaseAge`: a version installed past
|
|
2547
|
+
* it would sit in the profile's lockfile, and pnpm then refuses every later
|
|
2548
|
+
* plugin install in that profile until the version is old enough. A version
|
|
2549
|
+
* pnpm still holds back is reported as pending, with when it clears. Registry
|
|
2532
2550
|
* bodies and package-manager output stay host-side.
|
|
2533
2551
|
*
|
|
2534
2552
|
* @module dsh-packycode-api/plugin-version
|
|
@@ -2538,6 +2556,28 @@ const NPM_REGISTRY_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/late
|
|
|
2538
2556
|
const DEFAULT_VERSION_TTL_MS = 5 * 6e4;
|
|
2539
2557
|
const DEFAULT_VERSION_TIMEOUT_MS = 1e4;
|
|
2540
2558
|
const DEFAULT_UPDATE_TIMEOUT_MS = 18e4;
|
|
2559
|
+
const RELEASE_AGE_ERROR = /ERR_PNPM_(?:NO_MATURE_MATCHING_VERSION|MINIMUM_RELEASE_AGE_VIOLATION)/u;
|
|
2560
|
+
const RELEASE_AGE_ENTRY = /(\S+@\S+) was published at (\S+?), within the minimumReleaseAge cutoff \((\S+?)\)/gu;
|
|
2561
|
+
/**
|
|
2562
|
+
* Recognize pnpm refusing a version that is younger than its
|
|
2563
|
+
* `minimumReleaseAge`, whether the version was requested or already sits in
|
|
2564
|
+
* the lockfile. Each refused entry reads `<name>@<version> was published at
|
|
2565
|
+
* <time>, within the minimumReleaseAge cutoff (<time>)`.
|
|
2566
|
+
* @param output - the install's stdout and stderr.
|
|
2567
|
+
* @param finishedAt - when the install ended; pnpm drew its cutoff then.
|
|
2568
|
+
* @returns undefined when this is not a release-age refusal; otherwise
|
|
2569
|
+
* `{ availableAt }`, when the youngest refused entry clears, if pnpm said.
|
|
2570
|
+
*/
|
|
2571
|
+
function releaseAgeRefusal(output, finishedAt) {
|
|
2572
|
+
const text = String(output ?? "").replace(/\u001b\[[0-9;]*m/gu, "");
|
|
2573
|
+
if (!RELEASE_AGE_ERROR.test(text)) return void 0;
|
|
2574
|
+
let availableAt;
|
|
2575
|
+
for (const [, , published, cutoff] of text.matchAll(RELEASE_AGE_ENTRY)) {
|
|
2576
|
+
const clears = Date.parse(published) + (finishedAt - Date.parse(cutoff));
|
|
2577
|
+
if (Number.isFinite(clears) && (availableAt === void 0 || clears > availableAt)) availableAt = clears;
|
|
2578
|
+
}
|
|
2579
|
+
return availableAt === void 0 ? {} : { availableAt };
|
|
2580
|
+
}
|
|
2541
2581
|
/** Parse a strict `x.y.z[-pre][+build]` version; anything else is undefined. */
|
|
2542
2582
|
function parseSemver(value) {
|
|
2543
2583
|
if (typeof value !== "string") return void 0;
|
|
@@ -2812,7 +2852,6 @@ function createPackyCodePluginManager({ fetchImpl = ambientFetch, runCommand = s
|
|
|
2812
2852
|
...launcher.prefix,
|
|
2813
2853
|
"add",
|
|
2814
2854
|
"--save-exact",
|
|
2815
|
-
`--config.minimumReleaseAge=0`,
|
|
2816
2855
|
`${PACKAGE_NAME}@${latest}`
|
|
2817
2856
|
],
|
|
2818
2857
|
cwd: profileDir
|
|
@@ -2824,7 +2863,16 @@ function createPackyCodePluginManager({ fetchImpl = ambientFetch, runCommand = s
|
|
|
2824
2863
|
if (signal?.aborted) throw error;
|
|
2825
2864
|
throw new Error("PackyCode plugin update failed");
|
|
2826
2865
|
}
|
|
2827
|
-
if (result.code !== 0)
|
|
2866
|
+
if (result.code !== 0) {
|
|
2867
|
+
const refusal = releaseAgeRefusal(`${result.stdout ?? ""}\n${result.stderr ?? ""}`, now());
|
|
2868
|
+
if (refusal !== void 0) return {
|
|
2869
|
+
version: latest,
|
|
2870
|
+
profile: install.profile,
|
|
2871
|
+
pending: true,
|
|
2872
|
+
...refusal
|
|
2873
|
+
};
|
|
2874
|
+
throw new Error("PackyCode plugin update failed");
|
|
2875
|
+
}
|
|
2828
2876
|
await confirmInstalled({
|
|
2829
2877
|
profileDir,
|
|
2830
2878
|
expected: latest,
|
|
@@ -3833,6 +3881,7 @@ const REQUEST_LIMITS = Object.freeze({
|
|
|
3833
3881
|
/** Short titles use a smaller model in the same supplier/key group. */
|
|
3834
3882
|
const TITLE_MODELS = Object.freeze({
|
|
3835
3883
|
"gpt-6-astra": "gpt-6.1-sol",
|
|
3884
|
+
"gpt-5.6-sol": "gpt-6.1-sol",
|
|
3836
3885
|
"claude-opus-5-5": "claude-haiku-5-5",
|
|
3837
3886
|
"claude-sonnet-5-5": "claude-haiku-5-5"
|
|
3838
3887
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-packycode-api",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "Use PackyCode API keys in DeepSeek Harness with multi-protocol models, image generation and editing, separate key rings, and balance display",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|