dsh-packycode-api 1.4.7 → 1.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.en.md +14 -9
- package/README.md +15 -10
- package/docs/research/image-api.md +61 -1
- package/lib/client.js +151 -58
- package/lib/index.js +122 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.8
|
|
4
|
+
|
|
5
|
+
- **Claude Fable 5.2 joins the catalog.** On 2026-11-11 `/api/pricing` lists `claude-fable-5-2` with the `anthropic` endpoint alone, in `cc`, `cc-sale`, `cc-expensive`, `claude-officially` and `aws-q`. It takes the route of the other Claude models: `anthropic-messages` at `https://cf.api.fan`, text and image input, 1,000,000 context and 128,000 output, first in the picker's Claude models. Anthropic's [Claude Fable 5.2 overview](https://platform.claude.com/docs/en/models/fable-5-2/overview) gives the 1M context window (its default and maximum) and 128K output, thinking that is always on and adaptive, `output_config.effort` `low` to `max` as the only depth control, and no `temperature`; so it offers `low` / `medium` / `high` / `xhigh` / `max` and cannot be turned off, like the 5.5 models. A key in `cc` runs it through the Claude Code CLI as the agent; the other Claude groups use HTTP, where it asks for progress-update thinking (`display: "updates"` with the `thinking-display-updates-2026-08-18` beta) as Opus and Sonnet 5.5 do. Its session titles run on `claude-haiku-5-5`. The settings page counts it in each key's Claude coverage, and `scripts/claude-check.mjs` and `scripts/claude-agent-check.mjs` include it; the six-hour `scripts/claude-stability.mjs` window still samples the three 5.5 models, which its request budget is sized for.
|
|
6
|
+
- **MiniMax H3 Image joins the image engines.** On 2026-11-11 `/api/pricing` lists `MiniMax-H3-Image` and `MiniMax-H3-Image-Highspeed` in the `image` group, with the `image-generation` endpoint (`/v1/images/generations` and `/v1/images/edits`). Both are offered in the settings card and the `packycode_image_generate` tool. MiniMax's [text-to-image](https://platform.minimax.io/docs/api-reference/image-generation-h3-generations) and [image edit](https://platform.minimax.io/docs/api-reference/image-generation-h3-edits) references give these paths their own fields, so the request differs from GPT Image's (`requestFields` in `src/images.js`):
|
|
7
|
+
- There is no `quality` or `background`: the only quality is `auto`, and neither field is sent.
|
|
8
|
+
- `output_format: "png"` and `response_format: "b64_json"` are sent, because MiniMax answers with a JPEG link by default and the plugin stores the PNG it is given.
|
|
9
|
+
- `size: auto` goes with `area: "2K"`: the model picks the shape from the prompt or the references, at about 4 million pixels. MiniMax renders its 4K tier when neither a size nor an area is given, at four times 2K's pixels and price and past the size the plugin stores. An explicit size is sent without `area` (MiniMax refuses the two together) and keeps the existing limits (3840 per edge, 8,294,400 pixels), so the 4K tier is not offered.
|
|
10
|
+
- A transparent background and a mask, which MiniMax does not support, are refused before the request instead of being dropped. A tool call that names a MiniMax engine while the saved quality belongs to another engine runs at `auto`; an explicit quality it does not have is still refused.
|
|
11
|
+
- **The image card shows each image key's groups and offers only the engines the key in use is served.** PackyCode lists the MiniMax engines in `image` and not in `image-sale`, and nothing in the card said which group an image key was in. The image key ring now gets the conversation keys' group reader (`createGroupReader` with `modelIds`, new RPC `images/groups`): the gateway's refusal of a model id it cannot route names the key's groups, `/v1/models` lists what the key reaches, and the public `/api/pricing` lists each engine's groups. None is billed, and the browser receives group names, their descriptions and per-engine booleans only. Each key row shows its groups. The model list leaves out engines the key in use is not served, and a note names them. A saved engine the key is not served stays in the list, marked, with a warning and the group it needs; the setting is never changed to another engine. With no reading, nothing is hidden. `/api/pricing` answers by where the request comes from (42 models and 13 groups to a direct connection from mainland China, with no GPT Image or Claude models, against 85 and 26 through a proxy), so an engine is hidden only when neither the groups nor the key's own model list reach it.
|
|
12
|
+
- **The group refusal no longer invites a model switch.** The gateway's routing refusal (`error.code: "model_not_found"`, `分组 image-sale 下模型 MiniMax-H3-Image 无可用渠道`) read as "check model, quality, and image key permissions". In the desktop app, DeepSeek Flash answered a first wording of the new message ("choose another model or use an image key…") by retrying with another engine on its own. The message now says the image key's group does not serve the selected engine, not to retry with another model, and to have the user pick a served engine in the settings or add a key in a group that lists it. The gateway's text is still never shown.
|
|
13
|
+
- `scripts/image-check.mjs` runs an engine without quality levels at `auto`, takes `--size` (`auto` or `WIDTHxHEIGHT`, `1024x1024` by default), and with `--all` reports an engine that fails and goes on to the next; the edit runs on the last engine that produced an image.
|
|
14
|
+
- Validation, 2026-11-11:
|
|
15
|
+
- **Offline:** 569 tests (new for Fable: its catalog entry, protocol, efforts and wire dialect; its native efforts and client envelope over HTTP; `updates` thinking with the beta header, and an explicit `summarized` kept; direct and prepared titles on Haiku; an HTTP turn on a `cc-sale` key; pricing and coverage for its groups. New for MiniMax: the exact generation body for both engines at `auto` (with `area`) and at an explicit size (without it); a quality, a transparent background, a mask and an out-of-range size refused before any request; a multipart edit with two references, `area` and no `quality`, `background` or `mask`; the group refusal's message, without the gateway text and telling the model not to switch engines; the saved quality not inherited by a MiniMax call; a real PNG mask refused and the same edit sent without it; both engines as saved choices with `auto` alone, in the Config and in the built settings card. New for image key groups: the reader over an `image-sale`, an `image` and a rejected key with the image engines as its models; the `images/groups` RPC's projection, its failure message and its invalidation on a key added or removed; in the built card, the group tag on the key row, engines hidden with their note, the saved engine kept and marked with its warning, everything offered again for an `image` key or an unread one, and a rejected key), both rebuilt bundles and the versioned tarball. The suite also passed on Node 22.23.1.
|
|
16
|
+
- **Live, `scripts/claude-check.mjs --transport cli --model claude-fable-5-2` with the test key (`cc` among its groups), Claude Code 2.1.295:** 7 of 7 checks passed in 16 requests with no client-anomaly refusal: a read-only lookup, an approved edit, a denied delete, plan mode through DSH's plan review, a question, an image named `red`, and a title from `claude-haiku-5-5`. `scripts/live-check.mjs --model claude-fable-5-2` read the key's coverage as reaching it and answered the order-total scenario through the registered route.
|
|
17
|
+
- **Live, `scripts/image-check.mjs` with the image key:** while the key was in `image-sale`, both MiniMax engines were refused by the gateway before any upstream (HTTP 503 `model_not_found`) and no image was made. After the user moved it to `image`, 6 of 6 requests returned PNGs with no retry: `MiniMax-H3-Image` generated and edited at `1024x1024` in 11 and 16 s, `MiniMax-H3-Image-Highspeed` in 8 and 10 s, and `MiniMax-H3-Image` at `auto` returned 2048×2048 for both in 15 and 26 s. The generated teapot was blue and the edited one orange. A direct request with `size: auto`, `area: "1K"` and a wide landscape prompt returned 1568×672, so the gateway forwards `area` and the model picks the shape. `gpt-image-2.6-flare` at `low` still generated (34 s) and edited (49 s), reporting `quality: low`; both PNGs came back 1254×1254 for a `1024x1024` request, where the same request returned 1024×1024 on 2026-11-05.
|
|
18
|
+
- **Live, the image key's groups:** the probe was refused with `分组 image 下模型 dsh-packycode-group-probe 无可用渠道`, `/v1/models` listed seven image models, and the built reader took 2.7 s and marked all five engines served, with no key material in the reading.
|
|
19
|
+
- **Not checked:** the settings card in the desktop app (its rendering is covered by the built-bundle test, not by a look at the app), MiniMax's 4K tier, several references in one edit, and a request longer than the plugin's 5-minute timeout (MiniMax's own guidance is 600 s).
|
|
20
|
+
- **Not checked live:** Fable's HTTP route. No stored key is in `cc-sale`, `cc-expensive` or `claude-officially`, and a `cc` key over HTTP is refused. Anthropic enforces preserved thinking's history check on Claude Fable 5.2 for every account, where the 5.5 models enforce it only for accounts created since 2026-08-31, so a session whose earlier messages, system prompt or tools change between turns can be refused there with `The block is bound to a different conversation`.
|
|
21
|
+
|
|
3
22
|
## 1.4.7
|
|
4
23
|
|
|
5
24
|
- **Images move to GPT Image 2.6.** PackyCode added `gpt-image-2.6-flare` and `gpt-image-2.6-sunburst`: on 2026-11-05 `/api/pricing` lists them in the `image` group beside the 2.5 pair, with the `image-generation` endpoint. They replace `gpt-image-2.5-flare` and `gpt-image-2.5-sunburst` in the settings card, the `packycode_image_generate` tool and `scripts/image-check.mjs`, whose default is now 2.6 Sunburst. OpenAI's [image generation guide](https://developers.openai.com/api/docs/guides/image-generation) gives "GPT Image 2.6 and 2.5 Sunburst and Flare" one quality list, `low` to `max`, and its [Sunburst](https://developers.openai.com/api/docs/models/gpt-image-2.6-sunburst) and [Flare](https://developers.openai.com/api/docs/models/gpt-image-2.6-flare) pages list `/v1/images/edits`, so both keep `auto`, `low`, `medium`, `high`, `xhigh` and `max`, and requests, sizes and edits are sent as before. `gpt-image-2` is unchanged.
|
package/README.en.md
CHANGED
|
@@ -48,10 +48,10 @@ You need **DeepSeek Harness** and a **PackyCode (PackyAPI) API key**. For DSH se
|
|
|
48
48
|
Open the desktop application's **Plugins** page, click **Add plugin**, and enter the package name **with a version** as the **package name or address**:
|
|
49
49
|
|
|
50
50
|
```text
|
|
51
|
-
dsh-packycode-api@1.4.
|
|
51
|
+
dsh-packycode-api@1.4.8
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
Choose the **official** install source, then click **Install**. `1.4.
|
|
54
|
+
Choose the **official** install source, then click **Install**. `1.4.8` was the latest release when this was written; the [npm page](https://www.npmjs.com/package/dsh-packycode-api) shows the current one.
|
|
55
55
|
|
|
56
56
|
> **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**.
|
|
57
57
|
|
|
@@ -72,7 +72,7 @@ Choose a model from the `PackyCode` group. Of the thirteen models, Claude runs t
|
|
|
72
72
|
|
|
73
73
|
The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
|
|
74
74
|
|
|
75
|
-
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.4.
|
|
75
|
+
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.4.8`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
|
|
76
76
|
|
|
77
77
|
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.
|
|
78
78
|
|
|
@@ -163,9 +163,13 @@ Tool callers can set `model` and `quality` to `settings` to follow the saved pre
|
|
|
163
163
|
|
|
164
164
|
Image 2 accepts `auto / low / medium / high`; both 2.6 models also accept `xhigh / max`. A saved 2.5 Flare or Sunburst setting is read as the 2.6 engine of the same name, with its quality kept. These are Images API `quality` values, separate from text reasoning effort. There is no automatic model substitution, quality downgrade or paid retry. Each call returns one PNG; original bytes are saved under `~/.dsh/packycode-api/images/` (following `DSH_HOME` when set), alongside the chat preview. References and optional PNG masks must belong to the current session; file paths cannot replace attachment references.
|
|
165
165
|
|
|
166
|
+
`MiniMax-H3-Image` and `MiniMax-H3-Image-Highspeed` (faster, at a higher price) use the same two endpoints with different parameters. They have no quality levels: the setting offers only Auto and no `quality` is sent. They do not support transparent backgrounds or masks; a request with either is refused before it is sent, never rendered without it. MiniMax bills by output pixels and renders 4K when no size is given, so for `auto` the plugin adds `area: "2K"`: the model picks the shape from the description or the references, at about 4 million pixels. A size can also be given in the conversation (for example `2720x1536`), within the same limits as GPT Image (3840 per edge, 8.29 million pixels).
|
|
167
|
+
|
|
168
|
+
The image card shows each image key's groups. The model list offers only the engines that the key in use is served, and a note names the ones hidden because its groups do not serve them. A saved engine the key is not served stays in the list, marked as not served, with a warning and the group it needs; it is never changed to another engine for you. PackyCode lists the two MiniMax engines in the `image` group only, for example, so a key in `image-sale` does not see them. When the groups could not be read, nothing is hidden.
|
|
169
|
+
|
|
166
170
|
Click a generated image to open the in-app large preview. **Download original**, immediately left of Close, opens a save-location picker for the complete preserved PNG. Cancelling is harmless; a moved or deleted original reports a retryable error instead of downloading the chat thumbnail. Saved conversations also support this as long as their original file is still on this host.
|
|
167
171
|
|
|
168
|
-
After adding the image key, `node scripts/image-check.mjs --all --edit` checks
|
|
172
|
+
After adding the image key, `node scripts/image-check.mjs --all --edit` checks every engine and one edit (an engine the key's group does not serve reports its own error and the others still run). Use `--model <id> --edit` for generation and editing on a specific engine, adding `--quality xhigh` or `--quality max` to check a quality. This consumes image quota, saves outputs under `.artifacts/`, and prints only a masked key tail plus the gateway-reported model/quality.
|
|
169
173
|
|
|
170
174
|
Live checks on 2026-11-05 passed generation and editing at `low`, `xhigh` and `max` on both 2.6 Flare and 2.6 Sunburst, with matching quality values in the responses; `gpt-image-2` passed generation at `low`, and the gateway reported Sunburst's `auto` as `low`. All 15 requests succeeded without retries and returned 1024×1024 PNGs; visual inspection confirmed the requested color edits with composition preserved. Generate/edit times were about 14/15 seconds at xhigh and 24/26 seconds at max on Flare, and 51/64 and 96/97 seconds on Sunburst. These are built-client gateway checks; no desktop conversation was created to verify the full chat flow in this run. See the [protocol research and gateway results](docs/research/image-api.md) for scope and evidence.
|
|
171
175
|
|
|
@@ -230,7 +234,8 @@ The card shows whether DSH's plugin is on, how many reviews this plugin answered
|
|
|
230
234
|
| `hy4` (Hy4, Tencent Hunyuan) | HTTP, Responses, text/image input; 1024K context (at most 960K input), 64K output | Can be off; `high` only | `hunyuan-officially` |
|
|
231
235
|
| `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` |
|
|
232
236
|
| `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` |
|
|
233
|
-
| `claude-
|
|
237
|
+
| `claude-fable-5-2` (Claude Fable 5.2) | Anthropic Messages, images and native tools: this device's Claude Code CLI as the agent for `cc` keys, HTTP otherwise; 1M context, 128K output | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | Claude-group coverage reported in Settings |
|
|
238
|
+
| `claude-opus-5-5` | Same | Same | Same |
|
|
234
239
|
| `claude-sonnet-5-5` | Same | Same | Same |
|
|
235
240
|
| `claude-haiku-5-5` | Same; also generates Claude session titles | Same | Same |
|
|
236
241
|
|
|
@@ -268,16 +273,16 @@ DeepSeek Flash uses the gateway's Responses endpoint (`/api/pricing` lists `open
|
|
|
268
273
|
- Claude turns on both routes retain the full DSH tool loop, permissions and plan mode, using host search and image tools. Claude titles use Haiku on the same route as the conversation; GPT-6 Astra and GPT-5.6 Sol titles use GPT-6.1 Sol, and DeepSeek V4 Pro titles use DeepSeek Flash.
|
|
269
274
|
- The 1.2 `claudeCliMode` / `claudeCliCwd` settings do not return: Claude Code's permissions follow the session's DSH permission preset, and its working directory is the session's. The tool named `claude_code` is now the mirror tool that shows Claude Code's tool calls in cc sessions; only the plugin uses it, and it is not the 1.2 delegation tool. `claudeCliPath` applies again and names the CLI (empty means the usual install places, then PATH). 1.3.4's `claudeTransport` is removed, and a value left in a profile is ignored. Separately installed Claude Code is only modified when you click **Update Claude Code CLI** in the CLI card.
|
|
270
275
|
- Codex Responses requests still need `prompt_cache_key`; the plugin supplies a missing session identity. Other Responses families omit it.
|
|
271
|
-
- After building, `node scripts/claude-check.mjs` checks all
|
|
276
|
+
- After building, `node scripts/claude-check.mjs` checks all four Claude models on order totals, support triage, replenishment, read-only inventory tool roundtrips, product color labels, Chinese titles and plan mode (read-only lookup → plan submitted with `exit_plan_mode` → approval), validating the business answers; `--transport cli` runs the cc route's checks instead (`scripts/claude-agent-check.mjs`): in a scratch directory Claude Code reads a file and computes, edits one (approved), deletes one (refused), plans and then implements after review, asks a question, reads an image and names a session, and the files and answers are checked, with the gateway's client-anomaly and anti-abuse refusals counted. `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.
|
|
272
277
|
- For continuous dialogs, use `node scripts/claude-conversation-check.mjs --model claude-haiku-5-5 --rounds 3 --key-label <label> --output .artifacts/claude-conversation.json`. One plugin adapter handles independent sessions; each group retains six user turns and full tool history, validates arithmetic, final JSON and unexpected characters, and stops on a failure without discarding evidence. Requests default to 65 seconds and 2048 output tokens. Diagnostic overrides use `--timeout-ms` (5000–300000) and `--max-tokens` (1024–128000). `--capture-wire` records the synthetic business requests, non-authentication headers and partial stream events for exact replay, without keys. Reports include scenario and adapter lifecycle, configured limits, response-header latency and total response time. These options do not alter the plugin's runtime settings.
|
|
273
278
|
|
|
274
279
|
</details>
|
|
275
280
|
|
|
276
281
|
## Update and uninstall
|
|
277
282
|
|
|
278
|
-
> **Upgrading from 1.3.2 or earlier:** install `dsh-packycode-api@1.4.
|
|
283
|
+
> **Upgrading from 1.3.2 or earlier:** install `dsh-packycode-api@1.4.8` on the Plugins page instead of clicking **Update plugin** in the old settings card. The old button bypasses pnpm's release-age policy: if the version it installs is less than 24 hours old, the profile refuses every other plugin install until that version is 24 hours old. After the install and a restart, the button is the fixed one, and later updates can use it.
|
|
279
284
|
|
|
280
|
-
**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. The newer version is read from npm, while pnpm downloads it from the profile's own install source: right after a release that source (a regional mirror in particular) may not have it yet, and the button asks you to update again in a few minutes; when the source cannot be reached, it asks you to check the network or proxy. 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.4.
|
|
285
|
+
**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. The newer version is read from npm, while pnpm downloads it from the profile's own install source: right after a release that source (a regional mirror in particular) may not have it yet, and the button asks you to update again in a few minutes; when the source cannot be reached, it asks you to check the network or proxy. 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.4.8`); without it you may get a version that is merely the newest one published at least 24 hours ago.
|
|
281
286
|
|
|
282
287
|
**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.
|
|
283
288
|
|
|
@@ -293,7 +298,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
|
|
|
293
298
|
|
|
294
299
|
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.
|
|
295
300
|
|
|
296
|
-
Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.4.
|
|
301
|
+
Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.4.8`), 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.
|
|
297
302
|
|
|
298
303
|
**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:
|
|
299
304
|
|
package/README.md
CHANGED
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
打开桌面应用的**插件**页,点击**添加插件**,在**包名或地址**中填入**带版本号**的包名:
|
|
49
49
|
|
|
50
50
|
```text
|
|
51
|
-
dsh-packycode-api@1.4.
|
|
51
|
+
dsh-packycode-api@1.4.8
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
在**安装源**中选择**官方源**,然后点击**安装**。`1.4.
|
|
54
|
+
在**安装源**中选择**官方源**,然后点击**安装**。`1.4.8` 是写这份说明时的最新版,当前最新版本号以 [npm 页面](https://www.npmjs.com/package/dsh-packycode-api) 为准。
|
|
55
55
|
|
|
56
56
|
> **一定要带版本号。** 只填 `dsh-packycode-api` 时,安装前的提示显示的是 npm 上的最新版,但 DSH 自带的 pnpm 默认只安装发布满 24 小时的版本,实际装上的可能是好几个版本之前的旧版,而安装提示里看不出这一点。填写精确版本时,pnpm 会直接安装这个版本,并自动把它加入 profile 的 `minimumReleaseAgeExclude`。装好后可以在 **设置 → PackyCode → 插件版本** 核对「当前版本」。
|
|
57
57
|
|
|
@@ -72,7 +72,7 @@ dsh-packycode-api@1.4.7
|
|
|
72
72
|
|
|
73
73
|
桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
|
|
74
74
|
|
|
75
|
-
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.4.
|
|
75
|
+
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.4.8`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
|
|
76
76
|
|
|
77
77
|
本地开发要加 `link:` 前缀:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。**不要直接写路径(或 `file:`)** —— 那样的安装会把整个仓库目录(连同 `node_modules`)复制进 profile,之后它会在 profile 内部挡住 DSH 自带的包,插件看起来像没装,见「常见问题」。已经那样装过的话,先卸载再按上面的方式重装。
|
|
78
78
|
|
|
@@ -90,7 +90,7 @@ dsh-packycode-api@1.4.7
|
|
|
90
90
|
|
|
91
91
|
**看余额。** 「账户与 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`。
|
|
92
92
|
|
|
93
|
-
**用 Claude。**
|
|
93
|
+
**用 Claude。** 直接选择四个 Claude 模型之一。Key 在 `cc` 分组时,这个会话由本机的官方 Claude Code CLI 完成,需要先在 **设置 → PackyCode → Claude Code CLI** 一键安装(或自行 `npm install -g @anthropic-ai/claude-code --allow-scripts=@anthropic-ai/claude-code`),版本过低或有新版本时在同一处点「更新 Claude Code CLI」;`cc-sale`、`cc-expensive`、`claude-officially` 等分组直接走插件内 HTTP,不需要 CLI。「账户与 Key」里每个能用 Claude 的 Key 会标出「Claude 经官方 CLI」或「Claude 经 HTTP」,通道由分组决定,不能手动切换;分组还没读到时先走 CLI。经 HTTP 时工具由 DSH 执行,计划模式、提问、待办、子代理、附件、思考档位和历史都和其它模型一样;经 CLI 时由 Claude Code 自己执行,差别见下方「Claude Code CLI 通道」。
|
|
94
94
|
|
|
95
95
|
**网页搜索。** DSH 的 `web_search` 默认调用 DeepSeek 官方接口(用 `DEEPSEEK_API_KEY` 计费)。在 **设置 → PackyCode → 网页搜索** 里可以改走 PackyCode:
|
|
96
96
|
|
|
@@ -163,9 +163,13 @@ Claude 与其它模型一样,通过 DSH 的 `packycode_image_generate` 工具
|
|
|
163
163
|
|
|
164
164
|
`gpt-image-2` 的质量为 `auto / low / medium / high`;两款 2.6 另支持 `xhigh / max`。设置里原来选的 2.5 Flare / Sunburst 会按同名的 2.6 型号读取,质量不变,无需重新设置。质量按 Images API 的 `quality` 原样发送,不作为文字推理强度;不会自动换型号、降档或重试付费请求。输出先采用 PNG,每次一张,聊天预览之外的原图保存在 `~/.dsh/packycode-api/images/`(设置了 `DSH_HOME` 时跟随该目录)。参考图与可选 PNG mask 必须来自当前会话;不能用路径替代附件引用。
|
|
165
165
|
|
|
166
|
+
`MiniMax-H3-Image` 和 `MiniMax-H3-Image-Highspeed`(出图更快、单价更高)走同样的两个端点,但参数不同:没有质量档位(设置里只有「自动」,请求不带 `quality`),不支持透明背景和 mask,带这两项的请求会在发出前被拒绝,不会丢掉后照常生图。MiniMax 按输出像素计费,不给尺寸时默认出 4K;插件在尺寸为 `auto` 时附带 `area: "2K"`,由模型按描述或参考图选比例、约 400 万像素出图,也可以在对话里指定尺寸(如 `2720x1536`),上限与 GPT Image 相同(单边 3840、829 万像素)。
|
|
167
|
+
|
|
168
|
+
图片卡片会在每个图片 Key 下显示它的分组。型号列表只列出当前使用的 Key 所在分组支持的型号,并在下方说明哪些型号因分组不支持而隐藏;已保存的型号若不被当前 Key 支持,会保留在列表里并标注「当前 Key 不支持」,同时给出警告和所需分组,不会自动改成别的型号。例如 MiniMax 两个型号在 PackyCode 只开给 `image` 分组,`image-sale` 分组的 Key 看不到它们。分组没读到时不隐藏任何型号。
|
|
169
|
+
|
|
166
170
|
点击生成图可在应用内查看大图,右上角关闭按钮左侧的「下载原图」可选择保存位置,另存本机保留的完整 PNG。取消保存不会报错;若原图已被移走或删除,会提示失败,不会改为下载聊天缩略图。已有会话也可使用此功能,前提是记录对应的本机原图仍存在。
|
|
167
171
|
|
|
168
|
-
用户填入图片 Key 后,可运行 `node scripts/image-check.mjs --all --edit`
|
|
172
|
+
用户填入图片 Key 后,可运行 `node scripts/image-check.mjs --all --edit` 验证全部型号与一次编辑(Key 所在分组不提供的型号会单独报错,不影响其它型号);用 `--model <型号> --edit` 单独验证某个型号的生图和编辑,搭配 `--quality xhigh` 或 `--quality max` 验证质量。测试会消耗图片额度,输出写入 `.artifacts/`,日志只显示 Key 的掩码尾号,并记录网关回报的型号/质量。
|
|
169
173
|
|
|
170
174
|
2026-11-05 实测:2.6 Flare 和 2.6 Sunburst 都通过 `low`、`xhigh`、`max` 的生图与编辑,响应回报的 quality 与请求一致;`gpt-image-2` 通过 `low` 生图,Sunburst 的 `auto` 被网关回报为 `low`。合计 15 次请求全部成功,无重试,均返回 1024×1024 PNG;编辑正确改色并保留构图。Flare 的 xhigh 生图/编辑约 14/15 秒、max 约 24/26 秒;Sunburst 的 xhigh 约 51/64 秒、max 约 96/97 秒。这是已构建图片 client 的网关验证;本轮未新建 DSH 对话验证完整聊天流程。完整范围和记录见[协议核查与网关实测](docs/research/image-api.md)。
|
|
171
175
|
|
|
@@ -230,7 +234,8 @@ DSH 自带一个实验性插件「自动授权审查」,默认关闭。开启
|
|
|
230
234
|
| `hy4`(Hy4,腾讯混元) | HTTP,Responses,可带图;1024K 上下文(输入最多 960K),64K 输出 | 可关;只有 `high` | `hunyuan-officially` |
|
|
231
235
|
| `gemini-4-argon`(Gemini 4 Argon) | HTTP,Gemini 原生流式,可带图;1,048,576 上下文,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
|
|
232
236
|
| `gemini-3.8-flash`(Gemini 3.8 Flash) | HTTP,Gemini 原生流式,可带图;1,048,576 输入,65,536 输出 | 不能关;`low` / `medium` / `high` | `gemini-slb` |
|
|
233
|
-
| `claude-
|
|
237
|
+
| `claude-fable-5-2`(Claude Fable 5.2) | Anthropic Messages,可带图和原生工具:`cc` 分组由本机 Claude Code CLI 当 agent,其它分组走 HTTP;1M 上下文,128K 输出 | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | 以设置页读取的 Claude 分组覆盖为准 |
|
|
238
|
+
| `claude-opus-5-5` | 同上 | 同上 | 同上 |
|
|
234
239
|
| `claude-sonnet-5-5` | 同上 | 同上 | 同上 |
|
|
235
240
|
| `claude-haiku-5-5` | 同上;Claude 会话标题固定走它 | 同上 | 同上 |
|
|
236
241
|
|
|
@@ -268,16 +273,16 @@ DeepSeek Flash 走网关的 Responses 接口(`/api/pricing` 列出 `openai-res
|
|
|
268
273
|
- 两条通道的 Claude 对话都保留完整 DSH 工具循环、权限及计划模式;网页搜索和生图使用宿主工具。会话标题由 `claude-haiku-5-5` 生成(与对话走同一条通道);GPT-6 Astra 与 GPT-5.6 Sol 的标题交给 GPT-6.1 Sol,DeepSeek V4 Pro 的标题交给 DeepSeek Flash。
|
|
269
274
|
- 1.2.x 的 `claudeCliMode`、`claudeCliCwd` 设置不再恢复:Claude Code 的权限由会话的 DSH 权限预设决定,工作目录就是会话目录。名为 `claude_code` 的工具现在是 cc 会话里显示 Claude Code 工具调用的镜像工具,只由插件使用,不是 1.2.x 的委派工具。`claudeCliPath` 重新生效,用于指定 CLI 位置(留空则按常见安装位置和 PATH 查找)。1.3.4 的 `claudeTransport` 已移除,profile 里残留的值会被忽略。除非你在 CLI 卡片里点「更新 Claude Code CLI」,用户独立安装的 Claude Code 不会被改动。
|
|
270
275
|
- Codex 的 Responses 请求仍需要 `prompt_cache_key`;插件补上缺失的会话标识,其它 Responses 模型省略该字段。
|
|
271
|
-
- 构建后可用 `node scripts/claude-check.mjs`
|
|
276
|
+
- 构建后可用 `node scripts/claude-check.mjs` 验证四个 Claude 模型的订单核算、工单归类、库存补货、只读库存查询工具回传、商品色卡标注、中文标题和计划模式(只读查询 → `exit_plan_mode` 提交计划 → 审批),并核对业务答案;加 `--transport cli` 则改跑 cc 通道的检查(`scripts/claude-agent-check.mjs`):在临时目录里让 Claude Code 自己读文件计算、改文件(授权批准)、删文件(授权拒绝)、计划审阅后实施、提问、看图和起标题,核对文件和答案,并统计网关的「客户端异常」和「反滥用」拒绝次数。`node scripts/claude-stability.mjs --start --hours 6 --key-label <备注名>` 开始有截止时间和请求上限的采样;定时执行同一脚本继续采样,用 `--status` 查看记录。业务场景版本和每轮构建分别记录;测试消息拒绝将对应检查记为失败,其余预定检查继续,真实凭据或额度错误仍会停止整个窗口。测试不改变已保存的凭据,也不启动 DSH。
|
|
272
277
|
- 连续对话可用 `node scripts/claude-conversation-check.mjs --model claude-haiku-5-5 --rounds 3 --key-label <备注名> --output .artifacts/claude-conversation.json`:同一插件适配器处理独立会话,每组保留六轮对话和完整工具历史,检查追问中的数值一致性、最终 JSON 和异常字符;遇到失败停止该批次并保留记录。单请求默认 65 秒、输出上限 2048 token;诊断可通过 `--timeout-ms`(5000–300000)和 `--max-tokens`(1024–128000)显式调整。`--capture-wire` 记录这组模拟业务的完整请求、非认证头和中断前的流事件,用于精确重放;不记录 Key。报告包含场景版本、适配器生命周期、所用上限、首响应和完整响应耗时。这些选项不修改插件的运行参数。
|
|
273
278
|
|
|
274
279
|
</details>
|
|
275
280
|
|
|
276
281
|
## 更新与卸载
|
|
277
282
|
|
|
278
|
-
> **从 1.3.2 或更早版本升级:** 请在插件页填写 `dsh-packycode-api@1.4.
|
|
283
|
+
> **从 1.3.2 或更早版本升级:** 请在插件页填写 `dsh-packycode-api@1.4.8` 安装,不要点旧版设置页里的「更新插件」。旧按钮安装时会绕过 pnpm 的发布冷却期:装到的版本如果发布还不满 24 小时,这个 profile 在它满 24 小时之前就装不了其他插件。装好并重启后,按钮就换成了修好的版本,以后直接点即可。
|
|
279
284
|
|
|
280
|
-
**更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。安装遵守 pnpm 的发布冷却策略,不会绕过它:默认情况下 pnpm 会直接装上这个版本,并把它记进 profile 的 `minimumReleaseAgeExclude`;如果你给 pnpm 配了更严格的策略,按钮会提示什么时候可以更新。新版本号是从 npm 读到的,pnpm 却从 profile 配置的安装源下载:刚发布的版本,安装源(尤其是国内镜像)可能还没同步到,这时按钮会提示过几分钟再更新;连不上安装源时,会提示检查网络或代理。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.4.
|
|
285
|
+
**更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。安装遵守 pnpm 的发布冷却策略,不会绕过它:默认情况下 pnpm 会直接装上这个版本,并把它记进 profile 的 `minimumReleaseAgeExclude`;如果你给 pnpm 配了更严格的策略,按钮会提示什么时候可以更新。新版本号是从 npm 读到的,pnpm 却从 profile 配置的安装源下载:刚发布的版本,安装源(尤其是国内镜像)可能还没同步到,这时按钮会提示过几分钟再更新;连不上安装源时,会提示检查网络或代理。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.4.8`),否则装上的可能是发布满 24 小时的旧版。
|
|
281
286
|
|
|
282
287
|
**从本地路径安装切回官方源时,必须先卸载。** 「更新」是 `pnpm add`,它只重写包目录里的文件,不会删除更早一次本地路径安装留在包目录内的 `node_modules`;那份旧依赖会继续挡住 DSH 自带的包,插件行加载失败且看不出原因。「卸载」执行的是 `pnpm remove`,会删掉整个包目录,重装才是干净的。用 `node scripts/install-check.mjs` 可以确认当前 profile 是否处于这个状态。
|
|
283
288
|
|
|
@@ -293,7 +298,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
|
|
|
293
298
|
|
|
294
299
|
有输出就是残留:从 npm 安装的包不会自带 `node_modules`,这一份通常来自更早一次「本地路径」安装(`file:` 或直接写路径,而不是 `link:`)。它位于 profile 目录内部,而 DSH 解析插件导入时优先使用 profile 内的副本,所以插件拿到的仍是那份旧依赖 —— 例如 `@deepseek-ai/schemastery` 3.18.2 没有 `Schema.volatile()`,模块在导入阶段就抛错。导入失败时 Loader 不会为这一行创建 fiber,客户端 bundle 也不会被组合,于是模型路由、RPC 通道和设置页全都不会出现。
|
|
295
300
|
|
|
296
|
-
修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.4.
|
|
301
|
+
修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.4.8`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
|
|
297
302
|
|
|
298
303
|
**首次安装后,设置页每张卡片都显示 `transport failure for /packycode-api/…: HTTP 405`?** 实际装上的是 1.1.6 或更早的版本,通常是因为安装时只填了包名:安装提示显示的是最新版,pnpm 实际装的却是发布满 24 小时的旧版(见「安装插件」)。这些旧版要等 DSH 重启、重建它的 `connection` 服务之后,才能注册设置通道。所以首次安装后 `/packycode-api` 通道还不存在,请求落到静态页面,返回 405;模型路由和设置页本身照常加载。1.1.7 起首次安装不需要重启。先确认装上的版本:
|
|
299
304
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GPT Image 2 / 2.5 / 2.6 Images API 核查
|
|
1
|
+
# GPT Image 2 / 2.5 / 2.6 与 MiniMax H3 Image:Images API 核查
|
|
2
2
|
|
|
3
3
|
核查日期:2026-10-15(Asia/Shanghai)。协议核查部分只查阅公开资料;末尾另记录用户添加独立图片 Key 并授权测试后的 PackyCode 网关结果。协议能力与实际网关结果分开记录,不记录任何完整凭据。
|
|
4
4
|
|
|
@@ -98,3 +98,63 @@ JSON 编辑的字段为 `images: [{ image_url: "…" }]` 或 `images: [{ file_id
|
|
|
98
98
|
| `gpt-image-2.6-sunburst` | `max` | 通过 | 通过 | `max` | 95.9 / 96.6 秒 |
|
|
99
99
|
|
|
100
100
|
共 15 次请求全部成功,均返回 1024×1024 PNG。抽查的生图为白底蓝色茶壶,编辑后茶壶变橙、构图和白底保留。`auto` 发出后网关回报 `low`。响应没有可识别的 `model` 字段,所以结论仍是这些型号在当前 Key 和网关下可用、回报的 quality 与请求一致,不独立证明网关内部的模型身份。本轮没有在 DSH 桌面对话里跑完整的模型 → 工具 → 聊天预览流程。PNG 保存在本地 `.artifacts/image-check-*/`,不进入 Git 或 npm 包。
|
|
101
|
+
|
|
102
|
+
## MiniMax H3 Image 接入(2026-11-11)
|
|
103
|
+
|
|
104
|
+
2026-11-11 的 `/api/pricing` 在 `image` 分组列出 `MiniMax-H3-Image` 和 `MiniMax-H3-Image-Highspeed`,端点类型为 `image-generation`(`/v1/images/generations`,附加路径 `/v1/images/edits`),计费方式 `image_billing: "token"`;两款都不在 `image-sale` 分组。站内公告(`/api/status` 的 `announcements`,同日发布)写两个端点两款都支持、编辑支持 1~16 张参考图、「两款模型参数与限制相同,Highspeed 版本出图更快」。PackyCode 的文档仓库还没有这两个型号的页面。
|
|
105
|
+
|
|
106
|
+
### MiniMax 官方协议
|
|
107
|
+
|
|
108
|
+
来源:MiniMax 的[生图指南](https://platform.minimax.io/docs/guides/image-generation)、[文生图参考](https://platform.minimax.io/docs/api-reference/image-generation-h3-generations)和[图片编辑参考](https://platform.minimax.io/docs/api-reference/image-generation-h3-edits)。H3 Image 的原生接口就是这两个 Images API 路径,字段与 GPT Image 不同:
|
|
109
|
+
|
|
110
|
+
| 字段 | MiniMax H3 Image |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| `prompt` | 必填,最长 32000 字符 |
|
|
113
|
+
| `n` | 1–10,默认 1 |
|
|
114
|
+
| `size` | `auto`(默认)或 `WIDTHxHEIGHT`:面积 655,360–20,000,000 像素,宽高比 1:3 至 3:1,不缩放不取整,越界返回 400;没有 16 的倍数要求 |
|
|
115
|
+
| `area` | `1K` / `2K` / `4K`,**默认 `4K`**;与 `WIDTHxHEIGHT` 的 `size` 同时发送返回 400 |
|
|
116
|
+
| `aspect_ratio` | `auto` 或 `X:Y`;与 `WIDTHxHEIGHT` 的 `size` 同时发送返回 400 |
|
|
117
|
+
| `output_format` | `png` / `jpeg` / `jpg` / `webp`,**默认 `jpeg`** |
|
|
118
|
+
| `response_format` | `url`(默认)或 `b64_json`;`b64_json` 时 PNG 总面积上限 20,000,000 像素 |
|
|
119
|
+
| `quality`、`background` | schema 里没有;指南把透明背景列为不支持 |
|
|
120
|
+
| `mask` | 不支持,返回 400 |
|
|
121
|
+
| 编辑参考图 | multipart 的 `image` 或 `image[]`,1–16 张,JPG / PNG / WEBP / HEIC / HEIF,单张不超过 64 MiB,边长 128–8192 |
|
|
122
|
+
|
|
123
|
+
响应为 `data[].b64_json`(或 `url`)加每张图的实际 `size` 和 `revised_prompt`,顶层有 `output_format` 和 `usage`,没有 `quality` 字段。官方写服务端最多等待 600 秒,接口不幂等,重试会再次生成并计费。两款参数与限制相同;Highspeed 的输入图片和输出单价是标准版的两倍。
|
|
124
|
+
|
|
125
|
+
### 插件的请求形状
|
|
126
|
+
|
|
127
|
+
- 两款进入 `IMAGE_MODELS`,质量只有 `auto`,请求不带 `quality` 和 `background`。
|
|
128
|
+
- 显式发送 `output_format: "png"` 和 `response_format: "b64_json"`:插件只保存 PNG 原图,不下载网关给的链接。
|
|
129
|
+
- `size` 为 `auto` 时附带 `area: "2K"`(`MINIMAX_AUTO_AREA`),比例由模型按描述或参考图决定。不这样做,MiniMax 按默认 `4K` 出图,像素和费用是 2K 的四倍,并超过插件保存图片的上限(单边 3840、8,294,400 像素)。显式尺寸不带 `area`(两者同发返回 400)。
|
|
130
|
+
- 显式尺寸沿用原有校验(两边为 16 的倍数、单边不超过 3840、宽高比不超过 3:1、655,360–8,294,400 像素),是 MiniMax 允许范围的子集;4K 档暂不提供。
|
|
131
|
+
- 透明背景和 mask 在发出请求前拒绝,不丢弃后照常生图。工具调用为本次请求点名 MiniMax、而设置里保存的是另一型号的质量时,按 `auto` 处理;显式传入它没有的质量仍被拒绝。
|
|
132
|
+
- 网关的路由拒绝(`error.code: "model_not_found"`,如 `分组 image-sale 下模型 MiniMax-H3-Image 无可用渠道`)改为提示图片 Key 所在分组不提供该型号,并明确要求不要换型号重试、让用户在设置里选型号或换 Key,不回显网关原文。
|
|
133
|
+
- 超时仍是插件原有的 5 分钟,低于官方的 600 秒建议;实测 2K 生图和编辑都在 30 秒内。
|
|
134
|
+
|
|
135
|
+
### 网关实测(2026-11-11)
|
|
136
|
+
|
|
137
|
+
已构建的图片 client 调用 `https://cf.api.fan`,Key 只从 `packycode-api/image-keys` 读取(尾号 …ECMQ),提示和编辑指令同前(白底蓝色茶壶;编辑为改成橙色),每次 `n: 1`、PNG,无重试。
|
|
138
|
+
|
|
139
|
+
这把 Key 当天先在 `image-sale` 分组:两款 MiniMax 型号都被网关在到达上游前拒绝(HTTP 503 `model_not_found`),没有产生图片。用户把它改到 `image` 分组后:
|
|
140
|
+
|
|
141
|
+
| 请求型号 | 请求尺寸 | 生图 | 编辑 | 返回尺寸 | 生图 / 编辑耗时 |
|
|
142
|
+
| --- | --- | --- | --- | --- | --- |
|
|
143
|
+
| `MiniMax-H3-Image` | `1024x1024` | 通过 | 通过 | 1024×1024 | 11.4 / 16.3 秒 |
|
|
144
|
+
| `MiniMax-H3-Image-Highspeed` | `1024x1024` | 通过 | 通过 | 1024×1024 | 8.1 / 9.8 秒 |
|
|
145
|
+
| `MiniMax-H3-Image` | `auto`(附带 `area: "2K"`) | 通过 | 通过 | 2048×2048 | 14.6 / 25.6 秒 |
|
|
146
|
+
|
|
147
|
+
6 次请求全部返回可解码的 PNG(`data[].b64_json`),尺寸与请求一致。抽查的生图为白底蓝色茶壶,编辑后茶壶为橙色。响应没有 `model` 和 `quality` 字段,带每张图的 `size`、`revised_prompt` 和 `usage`。
|
|
148
|
+
|
|
149
|
+
另用一次直接请求确认网关转发 `area`:JSON 生图发 `size: "auto"` 加 `area: "1K"` 和一段宽幅风景描述,返回 1568×672 的 PNG(约 105 万像素,比例由模型选定),`usage.output_tokens` 为 4116。multipart 编辑同样转发 `area`(上表第三行的编辑返回 2048×2048)。1K 这张图的输出 token 与 2K 档的起点(约 4096)接近,所以 `auto` 选 2K。本轮没有测透明背景和 mask 的上游行为(插件在发出前拒绝)、4K 档、多张参考图和 5 分钟以上的请求。
|
|
150
|
+
|
|
151
|
+
同日用同一把 Key 回归 `gpt-image-2.6-flare` 的 `low`:生图 34.1 秒、编辑 48.5 秒,都成功,响应回报 `model: gpt-image-2.6-flare` 和 `quality: low`;请求的是 `1024x1024`,两次返回的 PNG 都是 1254×1254(2026-11-05 同样的请求返回 1024×1024)。GPT Image 的请求字段本次没有改动。
|
|
152
|
+
|
|
153
|
+
### 图片 Key 的分组(2026-11-11)
|
|
154
|
+
|
|
155
|
+
图片 Key 所在分组决定它能用哪些型号:`gpt-image-*` 同时列在 `image` 和 `image-sale`,MiniMax 两款只列在 `image`。桌面对话里用 `image-sale` 的 Key 选 MiniMax 生图时,工具返回分组提示,对话模型(DeepSeek Flash)随即自行换了型号重试,这违背「不自动换型号」。因此:
|
|
156
|
+
|
|
157
|
+
- 插件对图片 Key 复用对话 Key 的分组读取器(`createGroupReader`,`modelIds` 传图片型号):向 `/v1/chat/completions` 发一个网关无法路由的模型名,从拒绝文本里读出分组;读 `/v1/models` 得到这把 Key 能用的模型;读公开的 `/api/pricing` 得到每个型号所在的分组。三个读取都不计费、有超时、拒绝重定向,返回浏览器的只有分组名、分组说明和每个型号的布尔值。
|
|
158
|
+
- 实测(Key 在 `image` 分组):拒绝文本为 `分组 image 下模型 dsh-packycode-group-probe 无可用渠道(distributor)`,`/v1/models` 列出 `MiniMax-H3-Image`、`MiniMax-H3-Image-Highspeed`、`gpt-image-2`、`gpt-image-2.5-flare`、`gpt-image-2.5-sunburst`、`gpt-image-2.6-flare`、`gpt-image-2.6-sunburst`,已构建的读取器 2.7 秒读完,五个型号都判为可用。
|
|
159
|
+
- `/api/pricing` 的内容随请求出口变化:同一时刻从本机直连只返回 42 个模型、13 个分组,没有 Claude 和 `gpt-image-*`,也没有 `image-sale`;经代理返回 85 个模型、26 个分组。所以型号是否可用以「分组命中」或「`/v1/models` 列出」任一为准,只有两者都有反证时才判为不可用;定价里查不到型号时,设置卡不显示「需要分组」。
|
|
160
|
+
- 设置卡在每个图片 Key 下显示分组;型号列表只列当前使用的 Key 可用的型号,下方说明被隐藏的型号;已保存的型号若不可用,保留在列表里并标注,另给警告,不自动改设置。分组没读到时不隐藏任何型号。图片请求只用当前使用的 Key,不会换到另一把 Key。
|
package/lib/client.js
CHANGED
|
@@ -57,7 +57,13 @@ window.__ModuleLoader__.load({
|
|
|
57
57
|
imagesQuality_high: "高",
|
|
58
58
|
imagesQuality_xhigh: "超高(xhigh)",
|
|
59
59
|
imagesQuality_max: "最高(max)",
|
|
60
|
-
imagesQualityHint: "2.6 支持额外的 xhigh / max
|
|
60
|
+
imagesQualityHint: "GPT Image 2.6 支持额外的 xhigh / max;质量越高,耗时和费用通常越高。MiniMax H3 Image 没有质量档位,按尺寸计费,未指定尺寸时按 2K(约 400 万像素)出图、比例由模型决定。型号列表只显示当前图片 Key 的分组支持的型号。网关实际支持情况需用图片 Key 验证。不会自动换型号或降低质量。",
|
|
61
|
+
imagesModelUnservedMark: "(当前 Key 不支持)",
|
|
62
|
+
imagesModelUnserved: "当前图片 Key 的分组({groups})不支持 {model},用它生图会被网关拒绝。请换一个型号,或添加分组包含它的图片 Key。",
|
|
63
|
+
imagesModelRequired: "需要分组:{required}。",
|
|
64
|
+
imagesModelKeyRejected: "当前图片 Key 被网关拒绝,{model} 和其它图片型号都用不了。请检查这个 Key,或换用另一个图片 Key。",
|
|
65
|
+
imagesModelsHidden: "当前图片 Key 的分组({groups})不支持这些图片型号,已从上面的列表隐藏:{models}。",
|
|
66
|
+
listSeparator: "、",
|
|
61
67
|
imagesAddKey: "添加图片 Key",
|
|
62
68
|
imagesKeyPlaceholder: "粘贴专用的图片 API Key",
|
|
63
69
|
imagesNoKey: "尚未添加图片 Key。添加后可使用图片工具;对话 Key 不会用于图片请求。",
|
|
@@ -136,7 +142,7 @@ window.__ModuleLoader__.load({
|
|
|
136
142
|
cliPath: "位置",
|
|
137
143
|
cliRecheck: "重新检测",
|
|
138
144
|
cliMissing: "未检测到 Claude Code CLI",
|
|
139
|
-
cliMissingHint: "cc 分组的 Key 需要它才能使用 Claude 模型(claude-opus-5-5、claude-sonnet-5-5、claude-haiku-5-5)。其它分组和其它模型不受影响。",
|
|
145
|
+
cliMissingHint: "cc 分组的 Key 需要它才能使用 Claude 模型(claude-fable-5-2、claude-opus-5-5、claude-sonnet-5-5、claude-haiku-5-5)。其它分组和其它模型不受影响。",
|
|
140
146
|
cliConfiguredMissing: "设置里指定的 CLI 路径不存在:{path}",
|
|
141
147
|
cliBroken: "找到了 CLI,但无法运行",
|
|
142
148
|
cliInstall: "安装 Claude Code CLI",
|
|
@@ -298,7 +304,13 @@ window.__ModuleLoader__.load({
|
|
|
298
304
|
imagesQuality_high: "High",
|
|
299
305
|
imagesQuality_xhigh: "Extra high (xhigh)",
|
|
300
306
|
imagesQuality_max: "Maximum (max)",
|
|
301
|
-
imagesQualityHint: "The 2.6 engines also support xhigh / max. Higher quality usually takes longer and costs more. Gateway support needs testing with an image key. Models and quality are never changed automatically.",
|
|
307
|
+
imagesQualityHint: "The GPT Image 2.6 engines also support xhigh / max. Higher quality usually takes longer and costs more. MiniMax H3 Image has no quality levels: it is billed by size and, when no size is given, renders at 2K (about 4 million pixels) in a shape the model picks. The model list shows only what the image key in use is served. Gateway support needs testing with an image key. Models and quality are never changed automatically.",
|
|
308
|
+
imagesModelUnservedMark: " (not served by this key)",
|
|
309
|
+
imagesModelUnserved: "The image key in use is in {groups}, which does not serve {model}: the gateway will refuse images made with it. Pick another model, or add an image key in a group that lists it.",
|
|
310
|
+
imagesModelRequired: " It needs: {required}.",
|
|
311
|
+
imagesModelKeyRejected: "The gateway rejected the image key in use, so {model} and every other image model are unavailable. Check this key, or use another image key.",
|
|
312
|
+
imagesModelsHidden: "The image key in use is in {groups}, which does not serve these image models; they are hidden from the list above: {models}.",
|
|
313
|
+
listSeparator: ", ",
|
|
302
314
|
imagesAddKey: "Add an image key",
|
|
303
315
|
imagesKeyPlaceholder: "Paste the dedicated image API key",
|
|
304
316
|
imagesNoKey: "No image key yet. Add one to enable the image tool. Conversation keys are never used for images.",
|
|
@@ -377,7 +389,7 @@ window.__ModuleLoader__.load({
|
|
|
377
389
|
cliPath: "Location",
|
|
378
390
|
cliRecheck: "Check again",
|
|
379
391
|
cliMissing: "Claude Code CLI not found",
|
|
380
|
-
cliMissingHint: "Keys in the cc group need it to use the Claude models (claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5). Other groups and models are unaffected.",
|
|
392
|
+
cliMissingHint: "Keys in the cc group need it to use the Claude models (claude-fable-5-2, claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5). Other groups and models are unaffected.",
|
|
381
393
|
cliConfiguredMissing: "The CLI path set in settings does not exist: {path}",
|
|
382
394
|
cliBroken: "The CLI was found but could not run",
|
|
383
395
|
cliInstall: "Install Claude Code CLI",
|
|
@@ -1255,7 +1267,36 @@ window.__ModuleLoader__.load({
|
|
|
1255
1267
|
}
|
|
1256
1268
|
//#endregion
|
|
1257
1269
|
//#region src/client-images.jsx
|
|
1258
|
-
const imageModelLabel = (id) => id.replace("gpt-image-", "GPT Image ").replace("-flare", " Flare").replace("-sunburst", " Sunburst");
|
|
1270
|
+
const imageModelLabel = (id) => id.startsWith("MiniMax-") ? id.replaceAll("-", " ") : id.replace("gpt-image-", "GPT Image ").replace("-flare", " Flare").replace("-sunburst", " Sunburst");
|
|
1271
|
+
/** An image key's groups, as the gateway named them. */
|
|
1272
|
+
function ImageKeyGroups({ group, descriptions, loading, t }) {
|
|
1273
|
+
if (group === void 0) return loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1274
|
+
className: "pkc-meta",
|
|
1275
|
+
children: t("groupsChecking")
|
|
1276
|
+
}) : null;
|
|
1277
|
+
if (group.rejected === true) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1278
|
+
className: "pkc-error",
|
|
1279
|
+
children: t("groupsRejected")
|
|
1280
|
+
});
|
|
1281
|
+
const names = Array.isArray(group.groups) ? group.groups : [];
|
|
1282
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1283
|
+
className: "pkc-groups",
|
|
1284
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1285
|
+
className: "pkc-label",
|
|
1286
|
+
children: t("groupsTitle")
|
|
1287
|
+
}), names.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1288
|
+
className: "pkc-meta",
|
|
1289
|
+
children: group.error === void 0 ? "—" : `${t("groupsUnknown")}: ${group.error}`
|
|
1290
|
+
}) : names.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1291
|
+
className: "pkc-tag",
|
|
1292
|
+
title: descriptions?.[name] ?? name,
|
|
1293
|
+
children: name
|
|
1294
|
+
}, name))]
|
|
1295
|
+
}), group.stale !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1296
|
+
className: "pkc-meta",
|
|
1297
|
+
children: t("groupsStale")
|
|
1298
|
+
})] });
|
|
1299
|
+
}
|
|
1259
1300
|
/** Only projections come back from the host; the password draft is cleared on success. */
|
|
1260
1301
|
function PackyCodeImagesCard({ call, t }) {
|
|
1261
1302
|
const [state, setState] = react.useState();
|
|
@@ -1269,7 +1310,23 @@ window.__ModuleLoader__.load({
|
|
|
1269
1310
|
const [balanceError, setBalanceError] = react.useState();
|
|
1270
1311
|
const [balanceLoading, setBalanceLoading] = react.useState(true);
|
|
1271
1312
|
const balanceGeneration = react.useRef(0);
|
|
1313
|
+
const [groups, setGroups] = react.useState();
|
|
1314
|
+
const [groupsLoading, setGroupsLoading] = react.useState(true);
|
|
1315
|
+
const groupsGeneration = react.useRef(0);
|
|
1272
1316
|
const mounted = react.useRef(false);
|
|
1317
|
+
const readGroups = async (force = false) => {
|
|
1318
|
+
if (!mounted.current) return;
|
|
1319
|
+
const generation = ++groupsGeneration.current;
|
|
1320
|
+
setGroupsLoading(true);
|
|
1321
|
+
try {
|
|
1322
|
+
const value = await call("images/groups", { force });
|
|
1323
|
+
if (mounted.current && groupsGeneration.current === generation) setGroups(value);
|
|
1324
|
+
} catch {
|
|
1325
|
+
if (mounted.current && groupsGeneration.current === generation) setGroups(void 0);
|
|
1326
|
+
} finally {
|
|
1327
|
+
if (mounted.current && groupsGeneration.current === generation) setGroupsLoading(false);
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1273
1330
|
const readBalance = async (force = false) => {
|
|
1274
1331
|
if (!mounted.current) return;
|
|
1275
1332
|
const generation = ++balanceGeneration.current;
|
|
@@ -1292,12 +1349,14 @@ window.__ModuleLoader__.load({
|
|
|
1292
1349
|
if (mounted.current) setError(t("imagesLoadFailed"));
|
|
1293
1350
|
});
|
|
1294
1351
|
readBalance();
|
|
1352
|
+
readGroups();
|
|
1295
1353
|
const timer = window.setInterval(() => {
|
|
1296
1354
|
readBalance();
|
|
1297
1355
|
}, 6e4);
|
|
1298
1356
|
return () => {
|
|
1299
1357
|
mounted.current = false;
|
|
1300
1358
|
balanceGeneration.current += 1;
|
|
1359
|
+
groupsGeneration.current += 1;
|
|
1301
1360
|
window.clearInterval(timer);
|
|
1302
1361
|
};
|
|
1303
1362
|
}, [call, t]);
|
|
@@ -1310,6 +1369,7 @@ window.__ModuleLoader__.load({
|
|
|
1310
1369
|
if (mounted.current && endpoint.startsWith("images/keys/")) {
|
|
1311
1370
|
setBalance(void 0);
|
|
1312
1371
|
readBalance(true);
|
|
1372
|
+
readGroups(endpoint === "images/keys/add" || endpoint === "images/keys/remove");
|
|
1313
1373
|
}
|
|
1314
1374
|
window.dispatchEvent(new Event("packycode-images-updated"));
|
|
1315
1375
|
return true;
|
|
@@ -1327,6 +1387,13 @@ window.__ModuleLoader__.load({
|
|
|
1327
1387
|
const disabled = busy || state?.writable !== true;
|
|
1328
1388
|
const models = state?.models ?? [];
|
|
1329
1389
|
const qualities = models.find((model) => model.id === state?.imageModel)?.qualities ?? [];
|
|
1390
|
+
const activeGroup = groups?.keys?.find((entry) => entry.id === (state?.keys ?? []).find((key) => key.active)?.id);
|
|
1391
|
+
const unserved = (id) => activeGroup?.coverage?.[id] === false;
|
|
1392
|
+
const groupNames = (activeGroup?.groups ?? []).join(" / ") || "—";
|
|
1393
|
+
const hidden = models.filter((model) => unserved(model.id) && model.id !== state?.imageModel);
|
|
1394
|
+
const offered = models.filter((model) => !hidden.includes(model));
|
|
1395
|
+
const savedUnserved = state !== void 0 && unserved(state.imageModel);
|
|
1396
|
+
const required = groups?.requirements?.[state?.imageModel] ?? [];
|
|
1330
1397
|
const rows = (state?.keys ?? []).map((entry) => balance?.keys?.find((row) => row.id === entry.id) ?? { id: entry.id });
|
|
1331
1398
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1332
1399
|
className: "pkc-card",
|
|
@@ -1344,6 +1411,7 @@ window.__ModuleLoader__.load({
|
|
|
1344
1411
|
disabled: busy || balanceLoading,
|
|
1345
1412
|
onClick: () => {
|
|
1346
1413
|
readBalance(true);
|
|
1414
|
+
readGroups(true);
|
|
1347
1415
|
},
|
|
1348
1416
|
children: balanceLoading ? t("refreshing") : t("refresh")
|
|
1349
1417
|
})
|
|
@@ -1390,12 +1458,26 @@ window.__ModuleLoader__.load({
|
|
|
1390
1458
|
imageQuality: "auto"
|
|
1391
1459
|
});
|
|
1392
1460
|
},
|
|
1393
|
-
children:
|
|
1461
|
+
children: offered.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1394
1462
|
value: model.id,
|
|
1395
|
-
children: imageModelLabel(model.id)
|
|
1463
|
+
children: `${imageModelLabel(model.id)}${unserved(model.id) ? t("imagesModelUnservedMark") : ""}`
|
|
1396
1464
|
}, model.id))
|
|
1397
1465
|
})]
|
|
1398
1466
|
}),
|
|
1467
|
+
savedUnserved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1468
|
+
className: "pkc-warn",
|
|
1469
|
+
children: `${fill(t(activeGroup.rejected === true ? "imagesModelKeyRejected" : "imagesModelUnserved"), {
|
|
1470
|
+
model: imageModelLabel(state.imageModel),
|
|
1471
|
+
groups: groupNames
|
|
1472
|
+
})}${activeGroup.rejected !== true && required.length > 0 ? fill(t("imagesModelRequired"), { required: required.join(" / ") }) : ""}`
|
|
1473
|
+
}),
|
|
1474
|
+
hidden.length > 0 && activeGroup.rejected !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1475
|
+
className: "pkc-note",
|
|
1476
|
+
children: fill(t("imagesModelsHidden"), {
|
|
1477
|
+
groups: groupNames,
|
|
1478
|
+
models: hidden.map((model) => imageModelLabel(model.id)).join(t("listSeparator"))
|
|
1479
|
+
})
|
|
1480
|
+
}),
|
|
1399
1481
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1400
1482
|
className: "pkc-setting",
|
|
1401
1483
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1426,58 +1508,67 @@ window.__ModuleLoader__.load({
|
|
|
1426
1508
|
className: "pkc-list",
|
|
1427
1509
|
children: (state.keys ?? []).map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1428
1510
|
className: "pkc-row",
|
|
1429
|
-
children: [
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
rename,
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1511
|
+
children: [
|
|
1512
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1513
|
+
className: "pkc-row-top",
|
|
1514
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1515
|
+
className: "pkc-row-name",
|
|
1516
|
+
children: [rename.renaming?.id === entry.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyRenameForm, {
|
|
1517
|
+
rename,
|
|
1518
|
+
busy,
|
|
1519
|
+
t
|
|
1520
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1521
|
+
className: "pkc-name",
|
|
1522
|
+
children: entry.label
|
|
1523
|
+
}), entry.active && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1524
|
+
className: "pkc-chip",
|
|
1525
|
+
"data-tone": "success",
|
|
1526
|
+
children: t("keyActive")
|
|
1527
|
+
})]
|
|
1528
|
+
}), rename.renaming?.id !== entry.id && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1529
|
+
className: "pkc-actions",
|
|
1530
|
+
children: [
|
|
1531
|
+
!entry.active && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1532
|
+
className: "pkc-btn",
|
|
1533
|
+
type: "button",
|
|
1534
|
+
disabled: busy,
|
|
1535
|
+
onClick: () => {
|
|
1536
|
+
mutate("images/keys/select", { id: entry.id });
|
|
1537
|
+
},
|
|
1538
|
+
children: t("keyUse")
|
|
1539
|
+
}),
|
|
1540
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1541
|
+
className: "pkc-btn",
|
|
1542
|
+
"data-variant": "ghost",
|
|
1543
|
+
type: "button",
|
|
1544
|
+
disabled: busy,
|
|
1545
|
+
onClick: () => rename.start(entry),
|
|
1546
|
+
children: t("keyRename")
|
|
1547
|
+
}),
|
|
1548
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1549
|
+
className: "pkc-btn",
|
|
1550
|
+
"data-variant": "danger",
|
|
1551
|
+
type: "button",
|
|
1552
|
+
disabled: busy,
|
|
1553
|
+
onClick: () => {
|
|
1554
|
+
mutate("images/keys/remove", { id: entry.id });
|
|
1555
|
+
},
|
|
1556
|
+
children: t("keyRemove")
|
|
1557
|
+
})
|
|
1558
|
+
]
|
|
1444
1559
|
})]
|
|
1445
|
-
}),
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1458
|
-
className: "pkc-btn",
|
|
1459
|
-
"data-variant": "ghost",
|
|
1460
|
-
type: "button",
|
|
1461
|
-
disabled: busy,
|
|
1462
|
-
onClick: () => rename.start(entry),
|
|
1463
|
-
children: t("keyRename")
|
|
1464
|
-
}),
|
|
1465
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1466
|
-
className: "pkc-btn",
|
|
1467
|
-
"data-variant": "danger",
|
|
1468
|
-
type: "button",
|
|
1469
|
-
disabled: busy,
|
|
1470
|
-
onClick: () => {
|
|
1471
|
-
mutate("images/keys/remove", { id: entry.id });
|
|
1472
|
-
},
|
|
1473
|
-
children: t("keyRemove")
|
|
1474
|
-
})
|
|
1475
|
-
]
|
|
1476
|
-
})]
|
|
1477
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyBalance, {
|
|
1478
|
-
row: rows.find((row) => row.id === entry.id),
|
|
1479
|
-
t
|
|
1480
|
-
})]
|
|
1560
|
+
}),
|
|
1561
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageKeyGroups, {
|
|
1562
|
+
group: groups?.keys?.find((group) => group.id === entry.id),
|
|
1563
|
+
descriptions: groups?.descriptions,
|
|
1564
|
+
loading: groupsLoading,
|
|
1565
|
+
t
|
|
1566
|
+
}),
|
|
1567
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyBalance, {
|
|
1568
|
+
row: rows.find((row) => row.id === entry.id),
|
|
1569
|
+
t
|
|
1570
|
+
})
|
|
1571
|
+
]
|
|
1481
1572
|
}, entry.id))
|
|
1482
1573
|
}),
|
|
1483
1574
|
(state.keys ?? []).length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -1937,6 +2028,7 @@ window.__ModuleLoader__.load({
|
|
|
1937
2028
|
/** The auto review counts are re-read this often while the page is open. */
|
|
1938
2029
|
const REVIEW_POLL_MS = 5e3;
|
|
1939
2030
|
const CLAUDE_MODELS = [
|
|
2031
|
+
"claude-fable-5-2",
|
|
1940
2032
|
"claude-opus-5-5",
|
|
1941
2033
|
"claude-sonnet-5-5",
|
|
1942
2034
|
"claude-haiku-5-5"
|
|
@@ -1962,6 +2054,7 @@ window.__ModuleLoader__.load({
|
|
|
1962
2054
|
["Hunyuan", ["hy4"]],
|
|
1963
2055
|
["Gemini", ["gemini-4-argon", "gemini-3.8-flash"]],
|
|
1964
2056
|
["Claude", [
|
|
2057
|
+
"claude-fable-5-2",
|
|
1965
2058
|
"claude-opus-5-5",
|
|
1966
2059
|
"claude-sonnet-5-5",
|
|
1967
2060
|
"claude-haiku-5-5"
|
package/lib/index.js
CHANGED
|
@@ -1078,8 +1078,8 @@ const DEFAULT_REASONING_LEVEL = "high";
|
|
|
1078
1078
|
* ends early. So every model pins the protocol its vendor serves here.
|
|
1079
1079
|
*
|
|
1080
1080
|
* Effort levels come from each vendor's documentation, because an effort the
|
|
1081
|
-
* model does not support is refused before the request is sent: Claude
|
|
1082
|
-
* 5.5 (platform.claude.com effort docs), GPT-6.1 Sol, GPT-6.1 Terra, GPT-6
|
|
1081
|
+
* model does not support is refused before the request is sent: Claude Fable
|
|
1082
|
+
* 5.2 and Opus 5.5 (platform.claude.com effort docs), GPT-6.1 Sol, GPT-6.1 Terra, GPT-6
|
|
1083
1083
|
* Astra and GPT-5.6 Sol `reasoning.effort` (developers.openai.com model
|
|
1084
1084
|
* pages), DeepSeek thinking mode (`low|high|max` effort, or off, for Flash
|
|
1085
1085
|
* and V4 Pro alike), Grok 4.7
|
|
@@ -1125,7 +1125,7 @@ const DEEPSEEK_LEVELS = Object.freeze({
|
|
|
1125
1125
|
max: "max"
|
|
1126
1126
|
});
|
|
1127
1127
|
/**
|
|
1128
|
-
* Claude 5.5 models, GPT-6.1 Sol, and GPT-6 Astra cannot disable reasoning, so `off` is
|
|
1128
|
+
* Claude Fable 5.2, the Claude 5.5 models, GPT-6.1 Sol, and GPT-6 Astra cannot disable reasoning, so `off` is
|
|
1129
1129
|
* pinned unsupported; GPT-5.6 Sol is kept on the same levels. Claude effort is
|
|
1130
1130
|
* sent through the native Messages API.
|
|
1131
1131
|
*/
|
|
@@ -1315,6 +1315,23 @@ const PACKYCODE_MODELS = Object.freeze([
|
|
|
1315
1315
|
maxTokens: 65536,
|
|
1316
1316
|
thinkingLevelMap: GEMINI_LEVELS
|
|
1317
1317
|
}),
|
|
1318
|
+
Object.freeze({
|
|
1319
|
+
id: "claude-fable-5-2",
|
|
1320
|
+
name: "Claude Fable 5.2",
|
|
1321
|
+
api: "anthropic-messages",
|
|
1322
|
+
provider: PROVIDER,
|
|
1323
|
+
baseUrl: MESSAGES_BASE,
|
|
1324
|
+
reasoning: true,
|
|
1325
|
+
input: Object.freeze(["text", "image"]),
|
|
1326
|
+
cost: NO_COST,
|
|
1327
|
+
contextWindow: 1e6,
|
|
1328
|
+
maxTokens: 128e3,
|
|
1329
|
+
thinkingLevelMap: ALWAYS_THINKING_LEVELS,
|
|
1330
|
+
compat: Object.freeze({
|
|
1331
|
+
forceAdaptiveThinking: true,
|
|
1332
|
+
supportsTemperature: false
|
|
1333
|
+
})
|
|
1334
|
+
}),
|
|
1318
1335
|
Object.freeze({
|
|
1319
1336
|
id: "claude-opus-5-5",
|
|
1320
1337
|
name: "Claude Opus 5.5",
|
|
@@ -1510,13 +1527,15 @@ function parseSelectedGroups(message) {
|
|
|
1510
1527
|
* Normalize `/api/pricing`: the groups each covered model is enabled in, and
|
|
1511
1528
|
* each group's public description.
|
|
1512
1529
|
* @param payload - the parsed pricing body.
|
|
1530
|
+
* @param modelIds - the models to keep: the conversation catalog's, or the
|
|
1531
|
+
* image engines' for the image key ring.
|
|
1513
1532
|
* @returns `{ modelGroups, descriptions }`, or undefined when unrecognized.
|
|
1514
1533
|
*/
|
|
1515
|
-
function parsePricing(payload) {
|
|
1534
|
+
function parsePricing(payload, modelIds = PACKYCODE_COVERED_MODEL_IDS) {
|
|
1516
1535
|
if (!record$5(payload) || !Array.isArray(payload.data)) return void 0;
|
|
1517
1536
|
const modelGroups = {};
|
|
1518
1537
|
for (const item of payload.data) {
|
|
1519
|
-
if (!record$5(item) || !
|
|
1538
|
+
if (!record$5(item) || !modelIds.includes(item.model_name) || !Array.isArray(item.enable_groups)) continue;
|
|
1520
1539
|
modelGroups[item.model_name] = [...new Set(item.enable_groups.filter((name) => typeof name === "string" && GROUP_NAME.test(name)))].sort();
|
|
1521
1540
|
}
|
|
1522
1541
|
const descriptions = {};
|
|
@@ -1534,12 +1553,13 @@ function parsePricing(payload) {
|
|
|
1534
1553
|
* Which covered models a key can reach. Either source decides: a model the
|
|
1535
1554
|
* key's groups are enabled for, or one its model list shows. A model is only
|
|
1536
1555
|
* marked unreachable on evidence; with neither source it is left unknown.
|
|
1537
|
-
* @param options - the key's groups, its model ids,
|
|
1556
|
+
* @param options - the key's groups, its model ids, pricing's model groups,
|
|
1557
|
+
* and the models to decide (the conversation catalog's by default).
|
|
1538
1558
|
* @returns covered model id → reachable, for the models with evidence.
|
|
1539
1559
|
*/
|
|
1540
|
-
function catalogCoverage({ groups, models, modelGroups }) {
|
|
1560
|
+
function catalogCoverage({ groups, models, modelGroups, modelIds = PACKYCODE_COVERED_MODEL_IDS }) {
|
|
1541
1561
|
const coverage = {};
|
|
1542
|
-
for (const id of
|
|
1562
|
+
for (const id of modelIds) {
|
|
1543
1563
|
const enabled = modelGroups?.[id];
|
|
1544
1564
|
const byGroups = groups === void 0 || enabled === void 0 ? void 0 : enabled.some((name) => groups.includes(name));
|
|
1545
1565
|
const byModels = models === void 0 ? void 0 : models.has(id);
|
|
@@ -1565,10 +1585,12 @@ function errorMessage(text) {
|
|
|
1565
1585
|
}
|
|
1566
1586
|
/**
|
|
1567
1587
|
* Cached, single-flight group reader.
|
|
1568
|
-
* @param options - key listing, host resolution,
|
|
1588
|
+
* @param options - key listing, host resolution, the models whose coverage
|
|
1589
|
+
* is read (`modelIds`: the conversation catalog's by default, the image
|
|
1590
|
+
* engines' for the image key ring), and transport knobs.
|
|
1569
1591
|
* @returns `{ read, snapshot, covers, requiredGroups, invalidate }`.
|
|
1570
1592
|
*/
|
|
1571
|
-
function createGroupReader({ listKeys, getHosts, fetchImpl = ambientFetch, now = Date.now, ttlMs = DEFAULT_GROUP_TTL_MS, retryMs = FAILED_GROUP_RETRY_MS, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS } = {}) {
|
|
1593
|
+
function createGroupReader({ listKeys, getHosts, modelIds = PACKYCODE_COVERED_MODEL_IDS, fetchImpl = ambientFetch, now = Date.now, ttlMs = DEFAULT_GROUP_TTL_MS, retryMs = FAILED_GROUP_RETRY_MS, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS } = {}) {
|
|
1572
1594
|
if (typeof listKeys !== "function") throw new Error("PackyCode group reader requires listKeys");
|
|
1573
1595
|
if (typeof getHosts !== "function") throw new Error("PackyCode group reader requires getHosts");
|
|
1574
1596
|
/** Last good reading per key id, kept when a later read of that key fails. */
|
|
@@ -1584,7 +1606,7 @@ function createGroupReader({ listKeys, getHosts, fetchImpl = ambientFetch, now =
|
|
|
1584
1606
|
for (const host of statusHostOrder(hosts)) try {
|
|
1585
1607
|
const response = await request(`${host}/api/pricing`, { headers: { Accept: "application/json" } }, signal);
|
|
1586
1608
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1587
|
-
const pricing = parsePricing(await response.json());
|
|
1609
|
+
const pricing = parsePricing(await response.json(), modelIds);
|
|
1588
1610
|
if (pricing === void 0) throw new Error("unrecognized pricing payload");
|
|
1589
1611
|
return pricing;
|
|
1590
1612
|
} catch (error) {
|
|
@@ -1669,7 +1691,7 @@ function createGroupReader({ listKeys, getHosts, fetchImpl = ambientFetch, now =
|
|
|
1669
1691
|
return {
|
|
1670
1692
|
id: entry.id,
|
|
1671
1693
|
rejected: true,
|
|
1672
|
-
coverage: Object.fromEntries(
|
|
1694
|
+
coverage: Object.fromEntries(modelIds.map((id) => [id, false])),
|
|
1673
1695
|
error: new RejectedKeyError().message
|
|
1674
1696
|
};
|
|
1675
1697
|
}
|
|
@@ -1680,7 +1702,8 @@ function createGroupReader({ listKeys, getHosts, fetchImpl = ambientFetch, now =
|
|
|
1680
1702
|
coverage: catalogCoverage({
|
|
1681
1703
|
groups: value.groups,
|
|
1682
1704
|
models: value.models,
|
|
1683
|
-
modelGroups: pricing?.modelGroups
|
|
1705
|
+
modelGroups: pricing?.modelGroups,
|
|
1706
|
+
modelIds
|
|
1684
1707
|
})
|
|
1685
1708
|
};
|
|
1686
1709
|
lastGood.set(entry.id, {
|
|
@@ -3320,7 +3343,8 @@ const UPDATES_BETA = "thinking-display-updates-2026-08-18";
|
|
|
3320
3343
|
const THINKING_DISPLAY = Object.freeze({
|
|
3321
3344
|
"claude-haiku-5-5": "omitted",
|
|
3322
3345
|
"claude-sonnet-5-5": "updates",
|
|
3323
|
-
"claude-opus-5-5": "updates"
|
|
3346
|
+
"claude-opus-5-5": "updates",
|
|
3347
|
+
"claude-fable-5-2": "updates"
|
|
3324
3348
|
});
|
|
3325
3349
|
const IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude, running within the Claude Agent SDK.";
|
|
3326
3350
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
@@ -6550,10 +6574,19 @@ const IMAGE_MODELS = Object.freeze({
|
|
|
6550
6574
|
"high",
|
|
6551
6575
|
"xhigh",
|
|
6552
6576
|
"max"
|
|
6553
|
-
])
|
|
6577
|
+
]),
|
|
6578
|
+
"MiniMax-H3-Image": Object.freeze(["auto"]),
|
|
6579
|
+
"MiniMax-H3-Image-Highspeed": Object.freeze(["auto"])
|
|
6554
6580
|
});
|
|
6555
6581
|
const IMAGE_MODEL_IDS = Object.freeze(Object.keys(IMAGE_MODELS));
|
|
6556
6582
|
/**
|
|
6583
|
+
* MiniMax's H3 Image models take the Images API paths with their own fields
|
|
6584
|
+
* (platform.minimax.io/docs/api-reference/image-generation-h3-generations and
|
|
6585
|
+
* -h3-edits): no `quality`, `background` or `mask`, a JPEG link unless PNG and
|
|
6586
|
+
* `b64_json` are asked for, and 4K output unless a size is given.
|
|
6587
|
+
*/
|
|
6588
|
+
const isMiniMaxImageModel = (model) => typeof model === "string" && model.startsWith("MiniMax-H3-Image");
|
|
6589
|
+
/**
|
|
6557
6590
|
* Engines the settings once offered, mapped to the ones that replaced them.
|
|
6558
6591
|
* A saved setting still names them, so the Config schema keeps them as hidden
|
|
6559
6592
|
* values (dropping them would fail the plugin's Config at load) and reading
|
|
@@ -6579,6 +6612,7 @@ function validateImageOptions({ model = DEFAULT_IMAGE_MODEL, quality = "auto", s
|
|
|
6579
6612
|
"transparent",
|
|
6580
6613
|
"opaque"
|
|
6581
6614
|
].includes(background)) throw new Error("PackyCode image background is not supported");
|
|
6615
|
+
if (background === "transparent" && isMiniMaxImageModel(model)) throw new Error("PackyCode image transparent background is not supported by the selected model");
|
|
6582
6616
|
if (size !== "auto") {
|
|
6583
6617
|
const match = typeof size === "string" ? /^(\d{1,4})x(\d{1,4})$/u.exec(size) : null;
|
|
6584
6618
|
const width = Number(match?.[1]);
|
|
@@ -6607,7 +6641,7 @@ function readImagePreference(value = {}) {
|
|
|
6607
6641
|
};
|
|
6608
6642
|
}
|
|
6609
6643
|
/** No request is sent by a settings or credential mutation. */
|
|
6610
|
-
function createImageController({ ring, balance, readPreference, writePreference, isWritable }) {
|
|
6644
|
+
function createImageController({ ring, balance, groups, readPreference, writePreference, isWritable }) {
|
|
6611
6645
|
let mounted;
|
|
6612
6646
|
let disposeTool;
|
|
6613
6647
|
let generation = 0;
|
|
@@ -6639,6 +6673,7 @@ function createImageController({ ring, balance, readPreference, writePreference,
|
|
|
6639
6673
|
preference,
|
|
6640
6674
|
status,
|
|
6641
6675
|
invalidateBalance: () => balance?.invalidate(),
|
|
6676
|
+
invalidateGroups: () => groups?.invalidate(),
|
|
6642
6677
|
async readBalance({ force = false, signal } = {}) {
|
|
6643
6678
|
if (balance === void 0) throw new Error("PackyCode image balance is unavailable in this runtime");
|
|
6644
6679
|
try {
|
|
@@ -6654,6 +6689,25 @@ function createImageController({ ring, balance, readPreference, writePreference,
|
|
|
6654
6689
|
throw new Error("PackyCode image balance could not be read");
|
|
6655
6690
|
}
|
|
6656
6691
|
},
|
|
6692
|
+
/**
|
|
6693
|
+
* Each image key's groups and the image engines they serve. Group names,
|
|
6694
|
+
* their public descriptions and per-engine booleans only; no key material.
|
|
6695
|
+
*/
|
|
6696
|
+
async readGroups({ force = false, signal } = {}) {
|
|
6697
|
+
if (groups === void 0) throw new Error("PackyCode image key groups are unavailable in this runtime");
|
|
6698
|
+
try {
|
|
6699
|
+
const [value, state] = await Promise.all([groups.read({
|
|
6700
|
+
force,
|
|
6701
|
+
signal
|
|
6702
|
+
}), ring.list()]);
|
|
6703
|
+
return {
|
|
6704
|
+
...value,
|
|
6705
|
+
activeId: state.activeId
|
|
6706
|
+
};
|
|
6707
|
+
} catch {
|
|
6708
|
+
throw new Error("PackyCode image key groups could not be read");
|
|
6709
|
+
}
|
|
6710
|
+
},
|
|
6657
6711
|
reconcile,
|
|
6658
6712
|
mount(register) {
|
|
6659
6713
|
mounted = register;
|
|
@@ -6704,6 +6758,7 @@ function createImageController({ ring, balance, readPreference, writePreference,
|
|
|
6704
6758
|
if (!Object.hasOwn(operations, operation)) throw new Error("Unknown PackyCode image key operation");
|
|
6705
6759
|
await operations[operation]();
|
|
6706
6760
|
balance?.invalidate();
|
|
6761
|
+
if (operation === "add" || operation === "remove") groups?.invalidate();
|
|
6707
6762
|
await reconcile();
|
|
6708
6763
|
return status();
|
|
6709
6764
|
}
|
|
@@ -6862,8 +6917,33 @@ function decodeImagePng(encoded, maximum = MAX_IMAGE_BYTES) {
|
|
|
6862
6917
|
pngDimensions(data);
|
|
6863
6918
|
return data;
|
|
6864
6919
|
}
|
|
6920
|
+
/**
|
|
6921
|
+
* The fields each engine's Images API takes. GPT Image gets the validated
|
|
6922
|
+
* options as they are. MiniMax H3 Image has no `quality` or `background`,
|
|
6923
|
+
* answers with a JPEG link unless PNG and `b64_json` are asked for, and
|
|
6924
|
+
* renders 4K unless told otherwise, so `auto` carries the 2K `area` tier.
|
|
6925
|
+
*/
|
|
6926
|
+
function requestFields(options, prompt) {
|
|
6927
|
+
if (!isMiniMaxImageModel(options.model)) return {
|
|
6928
|
+
...options,
|
|
6929
|
+
prompt,
|
|
6930
|
+
n: 1,
|
|
6931
|
+
output_format: "png"
|
|
6932
|
+
};
|
|
6933
|
+
return {
|
|
6934
|
+
model: options.model,
|
|
6935
|
+
prompt,
|
|
6936
|
+
n: 1,
|
|
6937
|
+
size: options.size,
|
|
6938
|
+
...options.size === "auto" ? { area: "2K" } : {},
|
|
6939
|
+
output_format: "png",
|
|
6940
|
+
response_format: "b64_json"
|
|
6941
|
+
};
|
|
6942
|
+
}
|
|
6865
6943
|
function imageFailure(status, value) {
|
|
6866
|
-
|
|
6944
|
+
const code = value?.error?.code;
|
|
6945
|
+
if (code === "model_not_found") return "PackyCode image key is in a group that does not serve the selected image model. Do not retry with another model: tell the user to pick a model their key serves under Settings → PackyCode → Images, or to add an image key in a group that lists this one";
|
|
6946
|
+
if (code === "moderation_blocked") return "PackyCode image request was blocked by the image safety check; revise the prompt or references";
|
|
6867
6947
|
if (status === 401 || status === 403) return "PackyCode image key was rejected; check the separate image key and its model permissions";
|
|
6868
6948
|
if (status === 429) return "PackyCode image quota or rate limit is unavailable; check the separate image key";
|
|
6869
6949
|
return `PackyCode image request failed (HTTP ${status}); check model, quality, and image key permissions`;
|
|
@@ -6876,6 +6956,7 @@ function createPackyCodeImageClient({ resolveKey, getHost, fetchImpl = ambientFe
|
|
|
6876
6956
|
if (prompt === void 0 || prompt.length > 32e3) throw new ImageRequestError("PackyCode image prompt must contain 1-32000 characters");
|
|
6877
6957
|
if (images !== void 0 && (!Array.isArray(images) || images.length === 0 || images.length > MAX_REFERENCE_IMAGES)) throw new ImageRequestError("PackyCode image edits require 1-16 reference images");
|
|
6878
6958
|
if (mask !== void 0 && images === void 0) throw new ImageRequestError("PackyCode image mask requires a reference image");
|
|
6959
|
+
if (mask !== void 0 && isMiniMaxImageModel(options.model)) throw new ImageRequestError("PackyCode image masks are not supported by the selected model; omit maskImage or choose a GPT Image model");
|
|
6879
6960
|
const timeout = AbortSignal.timeout(timeoutMs);
|
|
6880
6961
|
const requestSignal = signal === void 0 ? timeout : AbortSignal.any([signal, timeout]);
|
|
6881
6962
|
let selected;
|
|
@@ -6887,12 +6968,7 @@ function createPackyCodeImageClient({ resolveKey, getHost, fetchImpl = ambientFe
|
|
|
6887
6968
|
throw new ImageRequestError("PackyCode image credentials could not be read");
|
|
6888
6969
|
}
|
|
6889
6970
|
if (typeof selected?.key !== "string" || selected.key === "") throw new ImageRequestError("PackyCode images need a separate image key; add one under Settings → PackyCode → Images");
|
|
6890
|
-
const fields =
|
|
6891
|
-
...options,
|
|
6892
|
-
prompt,
|
|
6893
|
-
n: 1,
|
|
6894
|
-
output_format: "png"
|
|
6895
|
-
};
|
|
6971
|
+
const fields = requestFields(options, prompt);
|
|
6896
6972
|
let body;
|
|
6897
6973
|
const headers = {
|
|
6898
6974
|
authorization: `Bearer ${selected.key}`,
|
|
@@ -7106,11 +7182,11 @@ function createPackyCodeImageTool({ client, attachments, getPreference, getSessi
|
|
|
7106
7182
|
quality: {
|
|
7107
7183
|
type: "string",
|
|
7108
7184
|
enum: ["settings", ...IMAGE_QUALITIES],
|
|
7109
|
-
description: "Use settings (or omit) to follow the user image setting. auto is an explicit API quality, not a request to follow settings. xhigh and max require a 2.6 model
|
|
7185
|
+
description: "Use settings (or omit) to follow the user image setting. auto is an explicit API quality, not a request to follow settings. xhigh and max require a GPT Image 2.6 model. MiniMax H3 Image takes no quality: leave it to settings or auto. Never downgrade automatically."
|
|
7110
7186
|
},
|
|
7111
7187
|
size: {
|
|
7112
7188
|
type: "string",
|
|
7113
|
-
description: "auto or widthxheight, e.g. 1024x1024; both edges multiples of 16, each <=3840, ratio <=3:1, pixels 655360..8294400."
|
|
7189
|
+
description: "auto or widthxheight, e.g. 1024x1024; both edges multiples of 16, each <=3840, ratio <=3:1, pixels 655360..8294400. On MiniMax H3 Image auto lets the model pick the shape at about 4 million pixels (2K); give a size to fix it, e.g. 2048x2048, 2720x1536 or 1536x2720."
|
|
7114
7190
|
},
|
|
7115
7191
|
background: {
|
|
7116
7192
|
type: "string",
|
|
@@ -7118,7 +7194,8 @@ function createPackyCodeImageTool({ client, attachments, getPreference, getSessi
|
|
|
7118
7194
|
"auto",
|
|
7119
7195
|
"opaque",
|
|
7120
7196
|
"transparent"
|
|
7121
|
-
]
|
|
7197
|
+
],
|
|
7198
|
+
description: "transparent needs a GPT Image model; MiniMax H3 Image does not support it."
|
|
7122
7199
|
},
|
|
7123
7200
|
referenceImages: {
|
|
7124
7201
|
type: "array",
|
|
@@ -7127,7 +7204,7 @@ function createPackyCodeImageTool({ client, attachments, getPreference, getSessi
|
|
|
7127
7204
|
},
|
|
7128
7205
|
maskImage: {
|
|
7129
7206
|
oneOf: [{ type: "null" }, referenceSchema],
|
|
7130
|
-
description: "Omit or use null for new images and ordinary reference-image edits, including refining a sketch. Only supply an actual PNG mask provided for masking, matching the first reference and under 4 MB; alpha=0 marks the area to edit. Do not copy an ordinary reference into this field or invent a mask."
|
|
7207
|
+
description: "Omit or use null for new images and ordinary reference-image edits, including refining a sketch. Only supply an actual PNG mask provided for masking, matching the first reference and under 4 MB; alpha=0 marks the area to edit. Do not copy an ordinary reference into this field or invent a mask. MiniMax H3 Image does not support masks."
|
|
7131
7208
|
}
|
|
7132
7209
|
},
|
|
7133
7210
|
output: {
|
|
@@ -7164,10 +7241,12 @@ function createPackyCodeImageTool({ client, attachments, getPreference, getSessi
|
|
|
7164
7241
|
const repeatedNonPng = repeatedIndex >= 0 && images[repeatedIndex].mediaType !== "image/png";
|
|
7165
7242
|
const masks = maskImage === void 0 || repeatedNonPng ? void 0 : await readReferences([maskImage], attachments, messages, signal);
|
|
7166
7243
|
if (!getPreference().imageEnabled) throw new ImageRequestError("PackyCode images are disabled in settings");
|
|
7244
|
+
const model = args.model === "settings" ? preference.imageModel : args.model ?? preference.imageModel;
|
|
7245
|
+
const savedQuality = IMAGE_MODELS[model]?.length === 1 ? IMAGE_MODELS[model][0] : preference.imageQuality;
|
|
7167
7246
|
const result = await client.request({
|
|
7168
7247
|
...args,
|
|
7169
|
-
model
|
|
7170
|
-
quality: args.quality === "settings" ?
|
|
7248
|
+
model,
|
|
7249
|
+
quality: args.quality === "settings" ? savedQuality : args.quality ?? savedQuality
|
|
7171
7250
|
}, {
|
|
7172
7251
|
signal,
|
|
7173
7252
|
images,
|
|
@@ -8418,6 +8497,7 @@ const TITLE_MODELS = Object.freeze({
|
|
|
8418
8497
|
"deepseek-v4-pro": "deepseek-flash",
|
|
8419
8498
|
"gpt-6-astra": "gpt-6.1-sol",
|
|
8420
8499
|
"gpt-5.6-sol": "gpt-6.1-sol",
|
|
8500
|
+
"claude-fable-5-2": "claude-haiku-5-5",
|
|
8421
8501
|
"claude-opus-5-5": "claude-haiku-5-5",
|
|
8422
8502
|
"claude-sonnet-5-5": "claude-haiku-5-5"
|
|
8423
8503
|
});
|
|
@@ -8745,6 +8825,7 @@ function createPackyCodeRpc({ ring, balance, groups, hosts, preference, claude,
|
|
|
8745
8825
|
balance.invalidate();
|
|
8746
8826
|
groups?.invalidate();
|
|
8747
8827
|
images?.invalidateBalance?.();
|
|
8828
|
+
images?.invalidateGroups?.();
|
|
8748
8829
|
return status();
|
|
8749
8830
|
},
|
|
8750
8831
|
"claude-cli/status": (payload) => cli().status({ force: payload?.force === true }),
|
|
@@ -8775,6 +8856,10 @@ function createPackyCodeRpc({ ring, balance, groups, hosts, preference, claude,
|
|
|
8775
8856
|
force: payload?.force === true,
|
|
8776
8857
|
signal
|
|
8777
8858
|
}),
|
|
8859
|
+
"images/groups": (payload, signal) => imageSupport().readGroups({
|
|
8860
|
+
force: payload?.force === true,
|
|
8861
|
+
signal
|
|
8862
|
+
}),
|
|
8778
8863
|
"images/original": (payload, signal) => originals().info(payload, signal),
|
|
8779
8864
|
"images/original/chunk": (payload, signal) => originals().chunk(payload, signal),
|
|
8780
8865
|
"images/update": (payload) => imageSupport().update(payload),
|
|
@@ -10121,9 +10206,16 @@ function apply(ctx, config) {
|
|
|
10121
10206
|
set: void 0
|
|
10122
10207
|
};
|
|
10123
10208
|
const dshHome = typeof process.env.DSH_HOME === "string" && process.env.DSH_HOME !== "" ? process.env.DSH_HOME : join(homedir(), ".dsh");
|
|
10209
|
+
/** The image keys' groups, read when the settings card asks: an engine may be listed in `image` and not in `image-sale`. */
|
|
10210
|
+
const imageGroups = createGroupReader({
|
|
10211
|
+
listKeys: () => imageRing.secrets(),
|
|
10212
|
+
getHosts: hosts,
|
|
10213
|
+
modelIds: IMAGE_MODEL_IDS
|
|
10214
|
+
});
|
|
10124
10215
|
const images = createImageController({
|
|
10125
10216
|
ring: imageRing,
|
|
10126
10217
|
balance: imageBalance,
|
|
10218
|
+
groups: imageGroups,
|
|
10127
10219
|
readPreference: () => source() ?? {},
|
|
10128
10220
|
writePreference: (patch) => imagePreference.set(patch),
|
|
10129
10221
|
isWritable: () => imagePreference.writable === true && typeof imagePreference.set === "function"
|
|
@@ -10193,6 +10285,7 @@ function apply(ctx, config) {
|
|
|
10193
10285
|
balance.invalidate();
|
|
10194
10286
|
imageBalance.invalidate();
|
|
10195
10287
|
groups.invalidate();
|
|
10288
|
+
imageGroups.invalidate();
|
|
10196
10289
|
modelSelection.reconcile();
|
|
10197
10290
|
images.reconcile().catch(() => void 0);
|
|
10198
10291
|
search.reconcile();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-packycode-api",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.8",
|
|
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",
|