dsh-packycode-api 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.en.md +14 -4
- package/README.md +14 -4
- package/lib/client.js +118 -0
- package/lib/index.js +212 -98
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.6
|
|
4
|
+
|
|
5
|
+
- Fixed the missing `image` input capability on `deepseek-flash`, following [DeepSeek's official vision guide](https://api-docs.deepseek.com/guides/vision/). DSH can now send image attachments through the existing Chat Completions route. Audited the two GPT models, Grok 4.7 and three Claude models against their vendor documentation; all six already declare image input.
|
|
6
|
+
- Added a regression that sends an image through the registered host route and checks the actual Chat Completions `image_url` payload. `scripts/live-check.mjs --with-vision` also checks DeepSeek's answers to two color fixtures without revealing the colors in the prompt.
|
|
7
|
+
- Validation: 278 offline tests, both rebuilt bundles and the versioned tarball. The built route passed all four HTTP model checks, key rotation and secret-free RPC projections. DeepSeek correctly identified both 512×512 red and blue PNGs through the attachment service seam.
|
|
8
|
+
|
|
9
|
+
## 1.1.5
|
|
10
|
+
|
|
11
|
+
- Added `grok-4.7`, displayed as **Grok 4.7**, to the PackyCode `openai-responses` route at `https://cf.api.fan/v1`. [The official Grok 4.7 guide](https://docs.x.ai/developers/grok-4-7) declares a 500,000-token context, text/image input, text output, and `low` / `medium` / `high` / `xhigh` reasoning (default `high`, no off or max). There is no separate documented text-output cap; the numeric pi-ai ceiling is 500,000, bounded by remaining context per request. Only GPT Responses requests keep `prompt_cache_key`: the gateway's Grok route rejects this field with a misleading `Incorrect API key provided` error. Non-GPT Responses requests use pi-ai's `cacheRetention: 'none'` to omit it; the model, protocol, effort and plugin-owned credential stay the same.
|
|
12
|
+
- Settings now has a model checkbox list with Select all / Select none. All models, including newly added catalog entries, are visible by default. The profile stores exclusions, and DSH's picker refreshes when they change. Hiding a model does not prevent an existing session or prepared turn from using it.
|
|
13
|
+
- Fixed saving Auto web search on DSH 0.2 (including the reported macOS Intel installation): require `@deepseek-ai/schemastery` 3.18.4+, which declares volatile preference fields. The previous 3.18.2 dependency silently left them non-volatile, so DSH 0.2 refused the write. All RPC failures now carry Connection's required `code`, `message`, and empty `details`, preserving a safe error instead of `connection: invalid server-response failure`.
|
|
14
|
+
- The live checker now reads only the plugin-owned `packycode-api/keys` credential record, selecting the active usable key or `--key-label LABEL`; legacy references and environment credentials never substitute for it. Invalid YAML produces a safe error without quoting secret-bearing source lines, and checks never modify the stored record.
|
|
15
|
+
- Validation: 276 offline tests, both regenerated bundles and the versioned tarball; built settings UI checkbox/all/none/save-failure checks; the bundled DSH 0.2 Connection parser accepts the fixed failure. Built live gateway checks answered through DeepSeek Flash, GPT-6.1 Sol, GPT-6 Astra and Grok 4.7 and passed key rotation and secret-free projections. All four Grok efforts returned `ok` with a plugin-stored Grok-group key; xhigh first encountered a transient upstream 400 and succeeded on an identical retry. The user confirmed the local desktop build passed testing. The physical Intel desktop UI has not been retested.
|
|
16
|
+
|
|
3
17
|
## 1.1.4
|
|
4
18
|
|
|
5
19
|
- **Carry the reviewed plan into the next CLI turn.** The plan previously existed only in `exit_plan_mode` arguments, which the delegated transcript dropped; a fresh CLI then saw only “Plan approved”. The latest complete plan and its matching review result are now recovered from durable native or adapter-generated PTC tool calls, so approval, revision, resume and fork keep their constraints. They are reserved before trimming older chat; an oversized plan/request fails explicitly rather than silently losing constraints. Unrelated tool arguments and arbitrary PTC programs are not forwarded.
|
package/README.en.md
CHANGED
|
@@ -25,6 +25,7 @@ The `PackyCode` model group ships with the plugin. No YAML edits and no separate
|
|
|
25
25
|
| Capability | What it does |
|
|
26
26
|
| --- | --- |
|
|
27
27
|
| **Built-in model route** | Adds a `PackyCode` group to the model picker. No provider configuration and no `cordis.patch.yml` edits |
|
|
28
|
+
| **Choose visible models** | All models are selected by default in Settings. Unchecking a model immediately hides it from the picker; existing sessions can keep using it |
|
|
28
29
|
| **Keys used in order** | An exhausted or rejected key is disabled and the next usable key takes over; the failed step is retried on that key |
|
|
29
30
|
| **Keys chosen by group** | Each key lists its PackyCode groups and the models they reach; when the active key's groups cannot reach the selected model (no `cc` group for Claude, say), that turn runs on a stored key whose groups can |
|
|
30
31
|
| **Visible balance** | Settings lists each key's remaining, used, limit, reset period, and a usage bar, plus a total of those keys. The composer shows the active key's remaining balance |
|
|
@@ -44,7 +45,7 @@ Open the desktop application's **Plugins** page, click **Add plugin**, and enter
|
|
|
44
45
|
dsh-packycode-api
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.1.
|
|
48
|
+
Choose the **official** install source, then click **Install**. To pin a version, enter `dsh-packycode-api@1.1.6`.
|
|
48
49
|
|
|
49
50
|
Then **manually restart the desktop application**. Refreshing the page does not reload the plugin inside the Host process.
|
|
50
51
|
|
|
@@ -56,14 +57,14 @@ Open **Settings → PackyCode**. Paste the API key under **Add a key**. A label
|
|
|
56
57
|
|
|
57
58
|
### 3. Select a model
|
|
58
59
|
|
|
59
|
-
Choose a model from the `PackyCode` group. `deepseek-flash`, `gpt-6.1-sol`,
|
|
60
|
+
Choose a model from the `PackyCode` group. `deepseek-flash`, `gpt-6.1-sol`, `gpt-6-astra`, and `grok-4.7` use HTTP. The three Claude models also need the Claude Code CLI on this device; see below. Your key's groups must cover the selected model.
|
|
60
61
|
|
|
61
62
|
<details>
|
|
62
63
|
<summary>Verify the install / other profiles / a local checkout</summary>
|
|
63
64
|
|
|
64
65
|
The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
|
|
65
66
|
|
|
66
|
-
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.
|
|
67
|
+
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.6`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
|
|
67
68
|
|
|
68
69
|
A local checkout can be linked with `dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`.
|
|
69
70
|
|
|
@@ -73,6 +74,8 @@ A profile without a browser can still use the model route. It just has no settin
|
|
|
73
74
|
|
|
74
75
|
## Daily use
|
|
75
76
|
|
|
77
|
+
**Choose visible models.** Under **Settings → PackyCode → Visible models**, check the models you want in the picker. All are selected by default, with Select all / Select none controls. The selection is saved in the current profile and applies immediately; unchecking a model does not interrupt an existing session. New catalog models are selected by default.
|
|
78
|
+
|
|
76
79
|
**Switch keys.** Keys are used in the order they were added. **Use this key** picks the preferred key, effective from the next turn; **Rename** changes its label (at most 60 characters, empty names are refused); **Re-enable** puts a disabled key back after a top-up or replacement.
|
|
77
80
|
|
|
78
81
|
**See groups.** Under **Accounts and keys**, each key lists its PackyCode groups (hover for the group's description) and which models they reach: ✓ yes, ◐ partly, ✕ no. The groups come from the gateway itself (its refusal of a model id it cannot route names the token's groups; nothing is billed), matched against PackyCode's public model-group table. Claude needs one of `cc` / `cc-sale` / `cc-expensive` / `claude-officially` (`claude-haiku-5-5` only `cc` and `claude-officially`); GPT needs `codex` / `codex-sale`; DeepSeek needs `deepseek-officially` / `deepseek-sale`.
|
|
@@ -135,15 +138,22 @@ Claude Code CLI → temporary local MCP web_search → selected PackyCode DeepSe
|
|
|
135
138
|
|
|
136
139
|
| Model | How it runs | Effort | Token group |
|
|
137
140
|
| --- | --- | --- | --- |
|
|
138
|
-
| `deepseek-flash` | HTTP, chat/completions | Can be off; `low` / `high` / `max` | `deepseek-officially`, `deepseek-sale` |
|
|
141
|
+
| `deepseek-flash` | HTTP, chat/completions, images allowed | Can be off; `low` / `high` / `max` | `deepseek-officially`, `deepseek-sale` |
|
|
139
142
|
| `gpt-6.1-sol` | HTTP, Responses, images allowed | Cannot be off; `low` / `medium` / `high` / `xhigh` / `max` | `codex`, `codex-sale` |
|
|
140
143
|
| `gpt-6-astra` | Same | Same | Same |
|
|
144
|
+
| `grok-4.7` (Grok 4.7) | HTTP, Responses, text/image input; 500K context | Cannot be off; `low` / `medium` / `high` / `xhigh` | The Grok coverage read from the gateway in Settings |
|
|
141
145
|
| `claude-opus-5-5` | Local Claude Code CLI | The picker offers the same levels; the CLI chooses the actual effort | A group the official CLI is allowed to use; see below |
|
|
142
146
|
| `claude-sonnet-5-5` | Same | Same | Same |
|
|
143
147
|
| `claude-haiku-5-5` | Same; session titles always use this model | Same | Same |
|
|
144
148
|
|
|
145
149
|
A model in the list does not mean the current token group allows it. When changing groups, do not drop the group that serves the model you still want.
|
|
146
150
|
|
|
151
|
+
All seven 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.
|
|
152
|
+
|
|
153
|
+
Grok 4.7's context and reasoning levels follow the [official guide](https://docs.x.ai/developers/grok-4-7). There is no separate text-output limit; the plugin uses the context size as its numeric output ceiling, bounded by the remaining context per request.
|
|
154
|
+
|
|
155
|
+
Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit it by default. This gateway's Grok route reports a misleading `Incorrect API key provided` error when the field is present.
|
|
156
|
+
|
|
147
157
|
<details>
|
|
148
158
|
<summary>Protocol, group, and CLI constraints</summary>
|
|
149
159
|
|
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
| 能力 | 使用体验 |
|
|
26
26
|
| --- | --- |
|
|
27
27
|
| **自带模型路由** | 安装后模型选择器出现 `PackyCode` 分组,不需要配置 provider,也不用改 `cordis.patch.yml` |
|
|
28
|
+
| **自选显示的模型** | 设置页的模型列表默认全选;取消勾选后,切换列表立即隐藏该模型,已有会话仍可继续使用 |
|
|
28
29
|
| **多 Key 按顺序使用** | 额度用尽或 Key 被拒绝时自动切到下一个可用 Key;失败的那一步由重试接手 |
|
|
29
30
|
| **按分组选 Key** | 每个 Key 后面列出它所在的 PackyCode 分组和能用的模型;当前 Key 的分组用不了所选模型(例如没有 cc 分组却选了 Claude)时,这一轮自动改用分组合适的 Key |
|
|
30
31
|
| **余额一眼可见** | 设置页列出每个 Key 的剩余、已用、额度、重置周期和用量条,并给出这些 Key 的合计;输入框右侧显示当前 Key 的剩余额度 |
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
dsh-packycode-api
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.1.
|
|
52
|
+
在**安装源**中选择**官方源**,然后点击**安装**。需要固定版本时填入 `dsh-packycode-api@1.1.6`。
|
|
52
53
|
|
|
53
54
|
完成后**手动重启桌面应用**。仅刷新页面不会重新加载 Host 里的插件。
|
|
54
55
|
|
|
@@ -60,14 +61,14 @@ dsh-packycode-api
|
|
|
60
61
|
|
|
61
62
|
### 3. 选择模型
|
|
62
63
|
|
|
63
|
-
回到对话,在模型选择器里选 `PackyCode` 分组下的模型。`deepseek-flash`、`gpt-6.1-sol` 和 `
|
|
64
|
+
回到对话,在模型选择器里选 `PackyCode` 分组下的模型。`deepseek-flash`、`gpt-6.1-sol`、`gpt-6-astra` 和 `grok-4.7` 通过 HTTP 调用;三个 Claude 模型还需要本机装有 Claude Code CLI,见下文。Key 的分组必须覆盖所选模型。
|
|
64
65
|
|
|
65
66
|
<details>
|
|
66
67
|
<summary>检查安装 / 其它 profile / 本地目录</summary>
|
|
67
68
|
|
|
68
69
|
桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
|
|
69
70
|
|
|
70
|
-
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.
|
|
71
|
+
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.1.6`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
|
|
71
72
|
|
|
72
73
|
本地开发可以用绝对路径:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。
|
|
73
74
|
|
|
@@ -77,6 +78,8 @@ dsh-packycode-api
|
|
|
77
78
|
|
|
78
79
|
## 日常使用
|
|
79
80
|
|
|
81
|
+
**选择显示的模型。** 在 **设置 → PackyCode → 显示的模型** 中勾选要在切换列表里看到的模型,默认全选,也支持「全选」和「全不选」。选择保存在当前 profile,修改后立即生效;取消勾选不会中断已有会话。后续新增的模型默认勾选。
|
|
82
|
+
|
|
80
83
|
**换 Key。** Key 按添加顺序使用。点「切到此 Key」指定优先使用的 Key,下一轮就生效;「重命名」改备注(最多 60 字,不能为空);充值或更换后点「重新启用」。
|
|
81
84
|
|
|
82
85
|
**看分组。** 「账户与 Key」里每个 Key 下面列出它所在的 PackyCode 分组(鼠标悬停可看分组说明),以及能用哪些模型:✓ 可用、◐ 部分可用、✕ 不可用。分组由网关自己报出(对一个不存在的模型 id 的拒绝里会写明「已选分组」,不产生费用),再对照 PackyCode 公开的模型分组表。Claude 需要 `cc` / `cc-sale` / `cc-expensive` / `claude-officially` 之一(`claude-haiku-5-5` 只在 `cc` 和 `claude-officially`);GPT 需要 `codex` / `codex-sale`;DeepSeek 需要 `deepseek-officially` / `deepseek-sale`。
|
|
@@ -139,15 +142,22 @@ Claude Code CLI → 本机临时 MCP web_search → 插件选定的 DeepSeek/GPT
|
|
|
139
142
|
|
|
140
143
|
| 模型 | 怎么调用 | 推理档位 | 适合的令牌分组 |
|
|
141
144
|
| --- | --- | --- | --- |
|
|
142
|
-
| `deepseek-flash` | HTTP,chat/completions | 可关;`low` / `high` / `max` | `deepseek-officially`、`deepseek-sale` |
|
|
145
|
+
| `deepseek-flash` | HTTP,chat/completions,可带图 | 可关;`low` / `high` / `max` | `deepseek-officially`、`deepseek-sale` |
|
|
143
146
|
| `gpt-6.1-sol` | HTTP,Responses,可带图 | 不能关;`low` / `medium` / `high` / `xhigh` / `max` | `codex`、`codex-sale` |
|
|
144
147
|
| `gpt-6-astra` | 同上 | 同上 | 同上 |
|
|
148
|
+
| `grok-4.7`(Grok 4.7) | HTTP,Responses,可带图;500k 上下文 | 不能关;`low` / `medium` / `high` / `xhigh` | 以设置页从网关读取的 Grok 分组覆盖为准 |
|
|
145
149
|
| `claude-opus-5-5` | 本机 Claude Code CLI | 选择器档位同上,实际档位由 CLI 决定 | 能给官方 CLI 用的分组,比如cc分组 |
|
|
146
150
|
| `claude-sonnet-5-5` | 同上 | 同上 | 同上 |
|
|
147
151
|
| `claude-haiku-5-5` | 同上;会话标题也固定走它 | 同上 | 同上 |
|
|
148
152
|
|
|
149
153
|
列表里看得到模型,不代表当前令牌分组一定放行。换分组时不要把对应分组一起去掉。
|
|
150
154
|
|
|
155
|
+
当前目录中的 7 个模型都声明了文字和图像输入。DeepSeek Flash 的识图能力按 [官方图像理解文档](https://api-docs.deepseek.com/zh-cn/guides/vision/) 配置;图片通过 DSH 附件服务读取,再随 chat/completions 请求发送。
|
|
156
|
+
|
|
157
|
+
Grok 4.7 的上下文与推理档位来自 [官方文档](https://docs.x.ai/developers/grok-4-7)。官方没有单独限制文字输出长度;插件将输出上限设为上下文大小,并按每次请求的剩余上下文收紧。
|
|
158
|
+
|
|
159
|
+
Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses 模型默认省略。当前网关的 Grok 路由会将这个字段误报成 `Incorrect API key provided`。
|
|
160
|
+
|
|
151
161
|
<details>
|
|
152
162
|
<summary>协议、分组和 CLI 的具体约束</summary>
|
|
153
163
|
|
package/lib/client.js
CHANGED
|
@@ -84,6 +84,14 @@ window.__ModuleLoader__.load({
|
|
|
84
84
|
groupsMiss: "不可用",
|
|
85
85
|
groupsPartial: "部分",
|
|
86
86
|
groupsStale: "分组为上次读取的结果",
|
|
87
|
+
modelsTitle: "显示的模型",
|
|
88
|
+
modelsIntro: "切换模型时只显示已勾选的 PackyCode 模型,默认全选。修改后立即生效;已有会话仍可继续使用原模型。",
|
|
89
|
+
modelsChecking: "正在读取模型设置…",
|
|
90
|
+
modelsFailed: "模型设置读取或保存失败",
|
|
91
|
+
modelsAll: "全选",
|
|
92
|
+
modelsNone: "全不选",
|
|
93
|
+
modelsCount: "已选 {count} / {total}",
|
|
94
|
+
modelsEmpty: "未勾选任何模型,切换列表将隐藏 PackyCode 的模型。",
|
|
87
95
|
advanced: "高级:接口地址",
|
|
88
96
|
apiHost: "模型接口地址",
|
|
89
97
|
statusHost: "余额接口地址",
|
|
@@ -162,6 +170,14 @@ window.__ModuleLoader__.load({
|
|
|
162
170
|
};
|
|
163
171
|
const en = {
|
|
164
172
|
nav: "PackyCode",
|
|
173
|
+
modelsTitle: "Visible models",
|
|
174
|
+
modelsIntro: "Only selected PackyCode models appear when switching models. All are selected by default. Changes apply immediately; existing sessions can keep their current model.",
|
|
175
|
+
modelsChecking: "Reading model settings…",
|
|
176
|
+
modelsFailed: "Could not read or save model settings",
|
|
177
|
+
modelsAll: "Select all",
|
|
178
|
+
modelsNone: "Select none",
|
|
179
|
+
modelsCount: "{count} / {total} selected",
|
|
180
|
+
modelsEmpty: "No models selected. PackyCode models are hidden from the picker.",
|
|
165
181
|
intro: "Paste a PackyCode (PackyAPI) API key to use the built-in model route. Keys are used in order: an exhausted or rejected key hands over to the next one, and a key whose groups cannot reach the selected model is passed over for one whose groups can.",
|
|
166
182
|
accountsTitle: "Accounts and keys",
|
|
167
183
|
refresh: "Refresh",
|
|
@@ -364,6 +380,13 @@ window.__ModuleLoader__.load({
|
|
|
364
380
|
.pkc-reach>span[data-state=no]{color:var(--dsw-alias-label-caption);text-decoration:line-through}
|
|
365
381
|
.pkc-add{display:flex;flex-direction:column;gap:8px;padding-top:14px;border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
366
382
|
|
|
383
|
+
.pkc-models{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin:0;padding:0;min-width:0;border:0}
|
|
384
|
+
.pkc-model{display:flex;align-items:center;gap:10px;min-width:0;padding:10px 12px;border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-interactive-bg-hover);cursor:pointer}
|
|
385
|
+
.pkc-model>input{flex:none;width:16px;height:16px;margin:0;accent-color:var(--dsw-alias-state-business-primary)}
|
|
386
|
+
.pkc-model>span{display:flex;flex-direction:column;min-width:0;gap:2px}
|
|
387
|
+
.pkc-model b{font-size:13px;font-weight:500}
|
|
388
|
+
.pkc-models:disabled .pkc-model{opacity:.5;cursor:not-allowed}
|
|
389
|
+
|
|
367
390
|
.pkc-setting{display:grid;grid-template-columns:112px minmax(0,1fr);gap:6px 16px;align-items:start}
|
|
368
391
|
.pkc-setting>.pkc-label{padding-top:5px}
|
|
369
392
|
.pkc-setting>div{display:flex;flex-direction:column;gap:6px;min-width:0}
|
|
@@ -389,6 +412,7 @@ window.__ModuleLoader__.load({
|
|
|
389
412
|
@media (max-width:640px){
|
|
390
413
|
.pkc-stats{grid-template-columns:1fr}
|
|
391
414
|
.pkc-setting{grid-template-columns:1fr}
|
|
415
|
+
.pkc-models{grid-template-columns:1fr}
|
|
392
416
|
.pkc-setting>.pkc-label{padding-top:0}
|
|
393
417
|
}
|
|
394
418
|
`;
|
|
@@ -473,6 +497,7 @@ window.__ModuleLoader__.load({
|
|
|
473
497
|
const MODEL_FAMILIES = [
|
|
474
498
|
["DeepSeek", ["deepseek-flash"]],
|
|
475
499
|
["GPT", ["gpt-6.1-sol", "gpt-6-astra"]],
|
|
500
|
+
["Grok", ["grok-4.7"]],
|
|
476
501
|
["Claude", [
|
|
477
502
|
"claude-opus-5-5",
|
|
478
503
|
"claude-sonnet-5-5",
|
|
@@ -1006,6 +1031,95 @@ window.__ModuleLoader__.load({
|
|
|
1006
1031
|
]
|
|
1007
1032
|
});
|
|
1008
1033
|
}
|
|
1034
|
+
function ModelsCard({ call, t }) {
|
|
1035
|
+
const [state, setState] = react.useState();
|
|
1036
|
+
const [busy, setBusy] = react.useState(false);
|
|
1037
|
+
const [error, setError] = react.useState();
|
|
1038
|
+
react.useEffect(() => {
|
|
1039
|
+
let live = true;
|
|
1040
|
+
call("models/status").then((value) => {
|
|
1041
|
+
if (live) setState(value);
|
|
1042
|
+
}).catch((failure) => {
|
|
1043
|
+
if (live) setError(clearMessage(failure));
|
|
1044
|
+
});
|
|
1045
|
+
return () => {
|
|
1046
|
+
live = false;
|
|
1047
|
+
};
|
|
1048
|
+
}, [call]);
|
|
1049
|
+
const models = state?.models ?? [];
|
|
1050
|
+
const selectedIds = models.filter((model) => model.selected).map((model) => model.id);
|
|
1051
|
+
const disabled = busy || state?.writable !== true;
|
|
1052
|
+
const update = (ids) => {
|
|
1053
|
+
if (disabled) return;
|
|
1054
|
+
setBusy(true);
|
|
1055
|
+
setError(void 0);
|
|
1056
|
+
call("models/update", { selectedIds: ids }).then(setState).catch((failure) => setError(clearMessage(failure))).finally(() => setBusy(false));
|
|
1057
|
+
};
|
|
1058
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, {
|
|
1059
|
+
title: t("modelsTitle"),
|
|
1060
|
+
aside: state !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1061
|
+
className: "pkc-meta",
|
|
1062
|
+
children: fill(t("modelsCount"), {
|
|
1063
|
+
count: selectedIds.length,
|
|
1064
|
+
total: models.length
|
|
1065
|
+
})
|
|
1066
|
+
}),
|
|
1067
|
+
children: [
|
|
1068
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1069
|
+
className: "pkc-note",
|
|
1070
|
+
children: t("modelsIntro")
|
|
1071
|
+
}),
|
|
1072
|
+
state === void 0 && error === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1073
|
+
className: "pkc-note",
|
|
1074
|
+
role: "status",
|
|
1075
|
+
children: t("modelsChecking")
|
|
1076
|
+
}),
|
|
1077
|
+
state !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1078
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1079
|
+
className: "pkc-actions",
|
|
1080
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1081
|
+
disabled: disabled || selectedIds.length === models.length,
|
|
1082
|
+
onClick: () => update(models.map((model) => model.id)),
|
|
1083
|
+
children: t("modelsAll")
|
|
1084
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1085
|
+
disabled: disabled || selectedIds.length === 0,
|
|
1086
|
+
onClick: () => update([]),
|
|
1087
|
+
children: t("modelsNone")
|
|
1088
|
+
})]
|
|
1089
|
+
}),
|
|
1090
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("fieldset", {
|
|
1091
|
+
className: "pkc-models",
|
|
1092
|
+
"aria-label": t("modelsTitle"),
|
|
1093
|
+
disabled,
|
|
1094
|
+
children: models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1095
|
+
className: "pkc-model",
|
|
1096
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1097
|
+
type: "checkbox",
|
|
1098
|
+
checked: model.selected,
|
|
1099
|
+
onChange: (event) => update(event.target.checked ? [...selectedIds, model.id] : selectedIds.filter((id) => id !== model.id))
|
|
1100
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: model.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1101
|
+
className: "pkc-meta pkc-mono",
|
|
1102
|
+
children: model.id
|
|
1103
|
+
})] })]
|
|
1104
|
+
}, model.id))
|
|
1105
|
+
}),
|
|
1106
|
+
selectedIds.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1107
|
+
className: "pkc-note",
|
|
1108
|
+
children: t("modelsEmpty")
|
|
1109
|
+
}),
|
|
1110
|
+
state.writable !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1111
|
+
className: "pkc-note",
|
|
1112
|
+
children: t("searchNotWritable")
|
|
1113
|
+
})
|
|
1114
|
+
] }),
|
|
1115
|
+
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1116
|
+
className: "pkc-error",
|
|
1117
|
+
role: "alert",
|
|
1118
|
+
children: `${t("modelsFailed")}: ${error}`
|
|
1119
|
+
})
|
|
1120
|
+
]
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1009
1123
|
/** Web search routing and backend, written through the host. */
|
|
1010
1124
|
function SearchCard({ call, t }) {
|
|
1011
1125
|
const [state, setState] = react.useState();
|
|
@@ -1469,6 +1583,10 @@ window.__ModuleLoader__.load({
|
|
|
1469
1583
|
call,
|
|
1470
1584
|
t
|
|
1471
1585
|
}),
|
|
1586
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelsCard, {
|
|
1587
|
+
call,
|
|
1588
|
+
t
|
|
1589
|
+
}),
|
|
1472
1590
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchCard, {
|
|
1473
1591
|
call,
|
|
1474
1592
|
t
|
package/lib/index.js
CHANGED
|
@@ -544,7 +544,8 @@ function createBalanceReader({ listKeys, getHosts, fetchImpl = ambientFetch, now
|
|
|
544
544
|
* model does not support is refused before the request is sent: Claude Opus
|
|
545
545
|
* 5.5 (platform.claude.com effort docs), GPT-6.1 Sol and GPT-6 Astra
|
|
546
546
|
* `reasoning.effort` (developers.openai.com model pages), DeepSeek thinking
|
|
547
|
-
* mode (`thinking` toggle plus `low|high|max` effort).
|
|
547
|
+
* mode (`thinking` toggle plus `low|high|max` effort), and Grok 4.7
|
|
548
|
+
* (docs.x.ai/developers/grok-4-7).
|
|
548
549
|
*
|
|
549
550
|
* @module dsh-packycode-api/catalog
|
|
550
551
|
*/
|
|
@@ -586,6 +587,11 @@ const ALWAYS_THINKING_LEVELS = Object.freeze({
|
|
|
586
587
|
xhigh: "xhigh",
|
|
587
588
|
max: "max"
|
|
588
589
|
});
|
|
590
|
+
/** Grok 4.7 supports low | medium | high | xhigh, with no off or max. */
|
|
591
|
+
const GROK_LEVELS = Object.freeze({
|
|
592
|
+
...ALWAYS_THINKING_LEVELS,
|
|
593
|
+
max: null
|
|
594
|
+
});
|
|
589
595
|
const PACKYCODE_MODELS = Object.freeze([
|
|
590
596
|
Object.freeze({
|
|
591
597
|
id: "deepseek-flash",
|
|
@@ -594,7 +600,7 @@ const PACKYCODE_MODELS = Object.freeze([
|
|
|
594
600
|
provider: PROVIDER,
|
|
595
601
|
baseUrl: COMPLETIONS_BASE,
|
|
596
602
|
reasoning: true,
|
|
597
|
-
input: Object.freeze(["text"]),
|
|
603
|
+
input: Object.freeze(["text", "image"]),
|
|
598
604
|
cost: NO_COST,
|
|
599
605
|
contextWindow: 1e6,
|
|
600
606
|
maxTokens: 384e3,
|
|
@@ -632,6 +638,20 @@ const PACKYCODE_MODELS = Object.freeze([
|
|
|
632
638
|
maxTokens: 128e3,
|
|
633
639
|
thinkingLevelMap: ALWAYS_THINKING_LEVELS
|
|
634
640
|
}),
|
|
641
|
+
Object.freeze({
|
|
642
|
+
id: "grok-4.7",
|
|
643
|
+
name: "Grok 4.7",
|
|
644
|
+
api: "openai-responses",
|
|
645
|
+
provider: PROVIDER,
|
|
646
|
+
baseUrl: RESPONSES_BASE,
|
|
647
|
+
reasoning: true,
|
|
648
|
+
input: Object.freeze(["text", "image"]),
|
|
649
|
+
cost: NO_COST,
|
|
650
|
+
contextWindow: 5e5,
|
|
651
|
+
maxTokens: 5e5,
|
|
652
|
+
thinkingLevelMap: GROK_LEVELS,
|
|
653
|
+
compat: Object.freeze({ supportsLongCacheRetention: false })
|
|
654
|
+
}),
|
|
635
655
|
Object.freeze({
|
|
636
656
|
id: "claude-opus-5-5",
|
|
637
657
|
name: "Claude Opus 5.5",
|
|
@@ -4896,6 +4916,134 @@ var PackyCodeKeyRing = class {
|
|
|
4896
4916
|
}
|
|
4897
4917
|
};
|
|
4898
4918
|
//#endregion
|
|
4919
|
+
//#region src/preferences.js
|
|
4920
|
+
/**
|
|
4921
|
+
* Where the plugin's preferences are read from and written to, on both
|
|
4922
|
+
* settings generations DSH has shipped.
|
|
4923
|
+
*
|
|
4924
|
+
* - DSH 0.1.5 installs a settings *section*: the section hands back a live
|
|
4925
|
+
* source and writes go through `settings.update(namespace, patch)`.
|
|
4926
|
+
* - DSH 0.2 has no sections: a preference is a *volatile* Config field of the
|
|
4927
|
+
* plugin's own row, read through a live reference, written through
|
|
4928
|
+
* `settings.update(rowId, patch)`, and announced by `loader/volatile-update`.
|
|
4929
|
+
* Only volatile fields are writable there, so the host overrides stay
|
|
4930
|
+
* read-only while web search and model visibility are editable.
|
|
4931
|
+
*
|
|
4932
|
+
* @module dsh-packycode-api/preferences
|
|
4933
|
+
*/
|
|
4934
|
+
/**
|
|
4935
|
+
* Mark a preference editable without restarting the plugin. DSH 0.2 stores
|
|
4936
|
+
* plugin preferences as volatile Config fields and writes only those. The
|
|
4937
|
+
* peer dependency requires the schema version that implements `volatile()`.
|
|
4938
|
+
*/
|
|
4939
|
+
const live = (schema) => schema.volatile();
|
|
4940
|
+
/** Read a Config field that may arrive as a volatile reference. */
|
|
4941
|
+
const fieldValue = (value) => value !== null && typeof value === "object" && typeof value.get === "function" ? value.get() : value;
|
|
4942
|
+
/** A preference target nothing can write to yet. */
|
|
4943
|
+
function unwritable(message) {
|
|
4944
|
+
return {
|
|
4945
|
+
writable: false,
|
|
4946
|
+
set: async () => {
|
|
4947
|
+
throw new Error(message);
|
|
4948
|
+
}
|
|
4949
|
+
};
|
|
4950
|
+
}
|
|
4951
|
+
/**
|
|
4952
|
+
* Connect the preferences to the settings service the host provides.
|
|
4953
|
+
* @param settingsCtx - the scope the `settings` inject handed over.
|
|
4954
|
+
* @param options - the plugin context and Config, the section namespace, the
|
|
4955
|
+
* current source and how to replace it, what to do when the section changes,
|
|
4956
|
+
* and the preference targets this fills in (host overrides, search, models).
|
|
4957
|
+
*/
|
|
4958
|
+
function connectSettings(settingsCtx, { ctx, Config, namespace, source, setSource, onChange, hostPreference, searchPreference, modelPreference }) {
|
|
4959
|
+
const settings = settingsCtx.settings;
|
|
4960
|
+
if (typeof settings?.installSection === "function") {
|
|
4961
|
+
try {
|
|
4962
|
+
settings.installSection(ctx, namespace, Config, source(), {
|
|
4963
|
+
setSource: (current) => setSource(typeof current === "function" ? () => current() ?? {} : () => current ?? {}),
|
|
4964
|
+
onChange
|
|
4965
|
+
});
|
|
4966
|
+
if (typeof settings.update === "function") {
|
|
4967
|
+
const write = (patch) => settings.update(namespace, patch);
|
|
4968
|
+
const target = {
|
|
4969
|
+
writable: settings.writable !== false,
|
|
4970
|
+
set: write
|
|
4971
|
+
};
|
|
4972
|
+
Object.assign(hostPreference, target);
|
|
4973
|
+
Object.assign(searchPreference, target);
|
|
4974
|
+
Object.assign(modelPreference, target);
|
|
4975
|
+
}
|
|
4976
|
+
} catch (error) {
|
|
4977
|
+
ctx.logger?.warn?.("packycode-api: host overrides unavailable, defaults stand: %s", error.message);
|
|
4978
|
+
}
|
|
4979
|
+
return;
|
|
4980
|
+
}
|
|
4981
|
+
if (typeof settings?.update !== "function") return;
|
|
4982
|
+
const rowId = ctx.fiber?.entry?.options?.id ?? namespace;
|
|
4983
|
+
const target = {
|
|
4984
|
+
writable: settings.writable !== false,
|
|
4985
|
+
set: (patch) => settings.update(rowId, patch)
|
|
4986
|
+
};
|
|
4987
|
+
Object.assign(searchPreference, target);
|
|
4988
|
+
Object.assign(modelPreference, target);
|
|
4989
|
+
if (typeof settings.configure === "function") {
|
|
4990
|
+
const scope = typeof settingsCtx.effect === "function" ? settingsCtx : ctx;
|
|
4991
|
+
try {
|
|
4992
|
+
scope.effect(() => settings.configure({ auto: false }, ctx.fiber), "packycode-api: settings page policy");
|
|
4993
|
+
} catch (error) {
|
|
4994
|
+
ctx.logger?.warn?.("packycode-api: could not hide the generated settings page: %s", error.message);
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4998
|
+
//#endregion
|
|
4999
|
+
//#region src/model-selection.js
|
|
5000
|
+
/** Profile preferences for the models advertised in PackyCode's picker. */
|
|
5001
|
+
/** Store exclusions so new catalog models are selected by default as well. */
|
|
5002
|
+
function readHiddenModels(preference) {
|
|
5003
|
+
const value = fieldValue(preference?.hiddenModels);
|
|
5004
|
+
if (!Array.isArray(value)) return [];
|
|
5005
|
+
const hidden = new Set(value);
|
|
5006
|
+
return PACKYCODE_MODEL_IDS.filter((id) => hidden.has(id));
|
|
5007
|
+
}
|
|
5008
|
+
function createModelSelectionController({ readPreference, writePreference, isWritable, onChange }) {
|
|
5009
|
+
const hidden = () => readHiddenModels(readPreference());
|
|
5010
|
+
let advertised = hidden().join(",");
|
|
5011
|
+
const reconcile = () => {
|
|
5012
|
+
const next = hidden().join(",");
|
|
5013
|
+
if (next === advertised) return;
|
|
5014
|
+
onChange?.();
|
|
5015
|
+
advertised = next;
|
|
5016
|
+
};
|
|
5017
|
+
const status = () => {
|
|
5018
|
+
const excluded = new Set(hidden());
|
|
5019
|
+
return {
|
|
5020
|
+
writable: isWritable() === true,
|
|
5021
|
+
models: PACKYCODE_MODELS.map(({ id, name }) => ({
|
|
5022
|
+
id,
|
|
5023
|
+
name,
|
|
5024
|
+
selected: !excluded.has(id)
|
|
5025
|
+
}))
|
|
5026
|
+
};
|
|
5027
|
+
};
|
|
5028
|
+
return Object.freeze({
|
|
5029
|
+
status,
|
|
5030
|
+
reconcile,
|
|
5031
|
+
isVisible: (id) => !hidden().includes(id),
|
|
5032
|
+
async update({ selectedIds } = {}) {
|
|
5033
|
+
if (!Array.isArray(selectedIds) || selectedIds.some((id) => !PACKYCODE_MODEL_IDS.includes(id))) throw new Error("That PackyCode model selection is not supported");
|
|
5034
|
+
if (isWritable() !== true) throw new Error("PackyCode settings are not writable in this profile");
|
|
5035
|
+
const selected = new Set(selectedIds);
|
|
5036
|
+
try {
|
|
5037
|
+
await writePreference({ hiddenModels: PACKYCODE_MODEL_IDS.filter((id) => !selected.has(id)) });
|
|
5038
|
+
} catch {
|
|
5039
|
+
throw new Error("PackyCode could not save the model selection");
|
|
5040
|
+
}
|
|
5041
|
+
reconcile();
|
|
5042
|
+
return status();
|
|
5043
|
+
}
|
|
5044
|
+
});
|
|
5045
|
+
}
|
|
5046
|
+
//#endregion
|
|
4899
5047
|
//#region src/plugin-version.js
|
|
4900
5048
|
/**
|
|
4901
5049
|
* Version self-knowledge for the settings panel.
|
|
@@ -5218,85 +5366,6 @@ function createPackyCodePluginManager({ fetchImpl = ambientFetch, runCommand = s
|
|
|
5218
5366
|
});
|
|
5219
5367
|
}
|
|
5220
5368
|
//#endregion
|
|
5221
|
-
//#region src/preferences.js
|
|
5222
|
-
/**
|
|
5223
|
-
* Where the plugin's preferences are read from and written to, on both
|
|
5224
|
-
* settings generations DSH has shipped.
|
|
5225
|
-
*
|
|
5226
|
-
* - DSH 0.1.5 installs a settings *section*: the section hands back a live
|
|
5227
|
-
* source and writes go through `settings.update(namespace, patch)`.
|
|
5228
|
-
* - DSH 0.2 has no sections: a preference is a *volatile* Config field of the
|
|
5229
|
-
* plugin's own row, read through a live reference, written through
|
|
5230
|
-
* `settings.update(rowId, patch)`, and announced by `loader/volatile-update`.
|
|
5231
|
-
* Only volatile fields are writable there, so the host overrides stay
|
|
5232
|
-
* read-only while the web search preference is editable.
|
|
5233
|
-
*
|
|
5234
|
-
* @module dsh-packycode-api/preferences
|
|
5235
|
-
*/
|
|
5236
|
-
/**
|
|
5237
|
-
* Mark a preference editable without restarting the plugin. DSH 0.2 stores
|
|
5238
|
-
* plugin preferences as volatile Config fields and writes only those; an older
|
|
5239
|
-
* schema library has no `volatile()` and keeps a plain field.
|
|
5240
|
-
*/
|
|
5241
|
-
const live = (schema) => typeof schema.volatile === "function" ? schema.volatile() : schema;
|
|
5242
|
-
/** Read a Config field that may arrive as a volatile reference. */
|
|
5243
|
-
const fieldValue = (value) => value !== null && typeof value === "object" && typeof value.get === "function" ? value.get() : value;
|
|
5244
|
-
/** A preference target nothing can write to yet. */
|
|
5245
|
-
function unwritable(message) {
|
|
5246
|
-
return {
|
|
5247
|
-
writable: false,
|
|
5248
|
-
set: async () => {
|
|
5249
|
-
throw new Error(message);
|
|
5250
|
-
}
|
|
5251
|
-
};
|
|
5252
|
-
}
|
|
5253
|
-
/**
|
|
5254
|
-
* Connect the preferences to the settings service the host provides.
|
|
5255
|
-
* @param settingsCtx - the scope the `settings` inject handed over.
|
|
5256
|
-
* @param options - the plugin context and Config, the section namespace, the
|
|
5257
|
-
* current source and how to replace it, what to do when the section changes,
|
|
5258
|
-
* and the two preference targets this fills in (host overrides, search).
|
|
5259
|
-
*/
|
|
5260
|
-
function connectSettings(settingsCtx, { ctx, Config, namespace, source, setSource, onChange, hostPreference, searchPreference }) {
|
|
5261
|
-
const settings = settingsCtx.settings;
|
|
5262
|
-
if (typeof settings?.installSection === "function") {
|
|
5263
|
-
try {
|
|
5264
|
-
settings.installSection(ctx, namespace, Config, source(), {
|
|
5265
|
-
setSource: (current) => setSource(typeof current === "function" ? () => current() ?? {} : () => current ?? {}),
|
|
5266
|
-
onChange
|
|
5267
|
-
});
|
|
5268
|
-
if (typeof settings.update === "function") {
|
|
5269
|
-
const write = (patch) => settings.update(namespace, patch);
|
|
5270
|
-
Object.assign(hostPreference, {
|
|
5271
|
-
writable: true,
|
|
5272
|
-
set: write
|
|
5273
|
-
});
|
|
5274
|
-
Object.assign(searchPreference, {
|
|
5275
|
-
writable: true,
|
|
5276
|
-
set: write
|
|
5277
|
-
});
|
|
5278
|
-
}
|
|
5279
|
-
} catch (error) {
|
|
5280
|
-
ctx.logger?.warn?.("packycode-api: host overrides unavailable, defaults stand: %s", error.message);
|
|
5281
|
-
}
|
|
5282
|
-
return;
|
|
5283
|
-
}
|
|
5284
|
-
if (typeof settings?.update !== "function") return;
|
|
5285
|
-
const rowId = ctx.fiber?.entry?.options?.id ?? namespace;
|
|
5286
|
-
Object.assign(searchPreference, {
|
|
5287
|
-
writable: settings.writable !== false,
|
|
5288
|
-
set: (patch) => settings.update(rowId, patch)
|
|
5289
|
-
});
|
|
5290
|
-
if (typeof settings.configure === "function") {
|
|
5291
|
-
const scope = typeof settingsCtx.effect === "function" ? settingsCtx : ctx;
|
|
5292
|
-
try {
|
|
5293
|
-
scope.effect(() => settings.configure({ auto: false }, ctx.fiber), "packycode-api: settings page policy");
|
|
5294
|
-
} catch (error) {
|
|
5295
|
-
ctx.logger?.warn?.("packycode-api: could not hide the generated settings page: %s", error.message);
|
|
5296
|
-
}
|
|
5297
|
-
}
|
|
5298
|
-
}
|
|
5299
|
-
//#endregion
|
|
5300
5369
|
//#region src/provider.js
|
|
5301
5370
|
/**
|
|
5302
5371
|
* The PackyCode pi-ai provider, built in code so the user configures no route.
|
|
@@ -5326,7 +5395,7 @@ function keyRingAuth() {
|
|
|
5326
5395
|
}
|
|
5327
5396
|
/** Build the route's pi-ai provider (unguarded; the plugin wraps its streams). */
|
|
5328
5397
|
function createPackyCodeProvider() {
|
|
5329
|
-
|
|
5398
|
+
const provider = createProvider({
|
|
5330
5399
|
id: PROVIDER,
|
|
5331
5400
|
name: DISPLAY_NAME,
|
|
5332
5401
|
baseUrl: DEFAULT_API_HOST,
|
|
@@ -5338,6 +5407,15 @@ function createPackyCodeProvider() {
|
|
|
5338
5407
|
"anthropic-messages": anthropicMessagesApi()
|
|
5339
5408
|
}
|
|
5340
5409
|
});
|
|
5410
|
+
const requestOptions = (model, options) => model.api === "openai-responses" && !model.id.startsWith("gpt-") ? {
|
|
5411
|
+
...options,
|
|
5412
|
+
cacheRetention: "none"
|
|
5413
|
+
} : options;
|
|
5414
|
+
return Object.freeze({
|
|
5415
|
+
...provider,
|
|
5416
|
+
stream: (model, context, options) => provider.stream(model, context, requestOptions(model, options)),
|
|
5417
|
+
streamSimple: (model, context, options) => provider.streamSimple(model, context, requestOptions(model, options))
|
|
5418
|
+
});
|
|
5341
5419
|
}
|
|
5342
5420
|
/**
|
|
5343
5421
|
* Observe a pi-ai event stream and hand a key-level failure to the plugin.
|
|
@@ -5418,7 +5496,7 @@ function guardKeyRotation(stream, { onKeyFailure, key } = {}) {
|
|
|
5418
5496
|
//#region src/route.js
|
|
5419
5497
|
/**
|
|
5420
5498
|
* The HTTP model route: the pi-ai provider profile and the adapter that
|
|
5421
|
-
* serves the catalog's HTTP models (DeepSeek and
|
|
5499
|
+
* serves the catalog's HTTP models (DeepSeek, GPT and Grok) with the ring's keys.
|
|
5422
5500
|
*
|
|
5423
5501
|
* @module dsh-packycode-api/route
|
|
5424
5502
|
*/
|
|
@@ -5508,7 +5586,7 @@ function createPackyCodeProfile(provider) {
|
|
|
5508
5586
|
* supplies none, and the attachment store image input is read from.
|
|
5509
5587
|
* @returns a `PiAiAdapter` serving the `packycode` route.
|
|
5510
5588
|
*/
|
|
5511
|
-
function createRouteAdapter({ ring, keys, fallbackSessionId, resolveAttachments }) {
|
|
5589
|
+
function createRouteAdapter({ ring, keys, fallbackSessionId, resolveAttachments, isModelVisible = () => true }) {
|
|
5512
5590
|
const raw = createPackyCodeProvider();
|
|
5513
5591
|
/**
|
|
5514
5592
|
* The gateway's Codex-group check refuses a Responses request that carries
|
|
@@ -5541,7 +5619,11 @@ function createRouteAdapter({ ring, keys, fallbackSessionId, resolveAttachments
|
|
|
5541
5619
|
streamSimple: guarded("streamSimple")
|
|
5542
5620
|
});
|
|
5543
5621
|
const profiles = /* @__PURE__ */ new Map([[PROVIDER, createPackyCodeProfile(provider)]]);
|
|
5544
|
-
return new PiAiAdapter
|
|
5622
|
+
return new class extends PiAiAdapter {
|
|
5623
|
+
async listModels(provider) {
|
|
5624
|
+
return (await super.listModels(provider)).filter((model) => isModelVisible(model.id));
|
|
5625
|
+
}
|
|
5626
|
+
}({
|
|
5545
5627
|
profiles: () => profiles,
|
|
5546
5628
|
resolveApiKey: async () => {
|
|
5547
5629
|
const active = await keys.active();
|
|
@@ -5566,7 +5648,7 @@ function createRouteAdapter({ ring, keys, fallbackSessionId, resolveAttachments
|
|
|
5566
5648
|
* Every reply carries only what this plugin owns: key labels and state, the
|
|
5567
5649
|
* balance and group projections the host built, host overrides, whether this
|
|
5568
5650
|
* device has the Claude Code CLI, the plugin version, and the web search
|
|
5569
|
-
* preference. Key material never crosses this boundary.
|
|
5651
|
+
* preference and model visibility. Key material never crosses this boundary.
|
|
5570
5652
|
*
|
|
5571
5653
|
* @module dsh-packycode-api/rpc
|
|
5572
5654
|
*/
|
|
@@ -5576,7 +5658,11 @@ const ok = (value) => ({
|
|
|
5576
5658
|
});
|
|
5577
5659
|
const failed = (message) => ({
|
|
5578
5660
|
ok: false,
|
|
5579
|
-
error: {
|
|
5661
|
+
error: {
|
|
5662
|
+
code: "packycode/request-failed",
|
|
5663
|
+
message,
|
|
5664
|
+
details: {}
|
|
5665
|
+
}
|
|
5580
5666
|
});
|
|
5581
5667
|
/** Only sentences this plugin authored may reach the browser. */
|
|
5582
5668
|
const SAFE_MESSAGE = /^(?:PackyCode|That |Unknown |PackyCode's |add |the )[\s\S]{0,180}$/u;
|
|
@@ -5588,7 +5674,7 @@ const keyId = (payload) => String(payload?.id ?? "");
|
|
|
5588
5674
|
* web search controller (without one, its endpoints refuse).
|
|
5589
5675
|
* @returns a handler for `connection.rpc.handle(CHANNEL, handler)`.
|
|
5590
5676
|
*/
|
|
5591
|
-
function createPackyCodeRpc({ ring, balance, groups, hosts, preference, claudeCli, pluginManager, search }) {
|
|
5677
|
+
function createPackyCodeRpc({ ring, balance, groups, hosts, preference, claudeCli, pluginManager, search, modelSelection }) {
|
|
5592
5678
|
const need = (service, message) => {
|
|
5593
5679
|
if (service === void 0) throw new Error(message);
|
|
5594
5680
|
return service;
|
|
@@ -5597,6 +5683,7 @@ function createPackyCodeRpc({ ring, balance, groups, hosts, preference, claudeCl
|
|
|
5597
5683
|
const manager = () => need(pluginManager, "PackyCode plugin version is unavailable");
|
|
5598
5684
|
const webSearch = () => need(search, "PackyCode web search is unavailable in this runtime");
|
|
5599
5685
|
const groupReader = () => need(groups, "PackyCode key groups are unavailable in this runtime");
|
|
5686
|
+
const models = () => need(modelSelection, "PackyCode model selection is unavailable in this runtime");
|
|
5600
5687
|
const status = async () => ({
|
|
5601
5688
|
...await ring.list(),
|
|
5602
5689
|
hosts: hosts(),
|
|
@@ -5661,7 +5748,9 @@ function createPackyCodeRpc({ ring, balance, groups, hosts, preference, claudeCl
|
|
|
5661
5748
|
"search/update": (payload) => webSearch().update({
|
|
5662
5749
|
searchProvider: payload?.searchProvider,
|
|
5663
5750
|
searchBackend: payload?.searchBackend
|
|
5664
|
-
})
|
|
5751
|
+
}),
|
|
5752
|
+
"models/status": () => models().status(),
|
|
5753
|
+
"models/update": (payload) => models().update({ selectedIds: payload?.selectedIds })
|
|
5665
5754
|
};
|
|
5666
5755
|
return async (endpoint, payload, signal) => {
|
|
5667
5756
|
try {
|
|
@@ -6406,9 +6495,11 @@ const FOREIGN_ROUTERS = Object.freeze({
|
|
|
6406
6495
|
});
|
|
6407
6496
|
/** Normalize a stored preference; anything unknown reads as the default. */
|
|
6408
6497
|
function readSearchChoice(value) {
|
|
6498
|
+
const provider = fieldValue(value?.searchProvider);
|
|
6499
|
+
const selectedBackend = fieldValue(value?.searchBackend);
|
|
6409
6500
|
return {
|
|
6410
|
-
mode: SEARCH_MODES.includes(
|
|
6411
|
-
backend: SEARCH_BACKENDS.includes(
|
|
6501
|
+
mode: SEARCH_MODES.includes(provider) ? provider : SEARCH_MODE_DEFAULT,
|
|
6502
|
+
backend: SEARCH_BACKENDS.includes(selectedBackend) ? selectedBackend : DEFAULT_SEARCH_BACKEND
|
|
6412
6503
|
};
|
|
6413
6504
|
}
|
|
6414
6505
|
/** Only sentences this plugin wrote reach the browser. */
|
|
@@ -6592,7 +6683,9 @@ const Config = z.object({
|
|
|
6592
6683
|
*/
|
|
6593
6684
|
searchProvider: live(z.union([...SEARCH_MODES]).default(SEARCH_MODE_DEFAULT)),
|
|
6594
6685
|
/** Which PackyCode model runs a search: `deepseek` or `gpt`. */
|
|
6595
|
-
searchBackend: live(z.union([...SEARCH_BACKENDS]).default(DEFAULT_SEARCH_BACKEND))
|
|
6686
|
+
searchBackend: live(z.union([...SEARCH_BACKENDS]).default(DEFAULT_SEARCH_BACKEND)),
|
|
6687
|
+
/** Only the picker is filtered; an existing session can keep its model. */
|
|
6688
|
+
hiddenModels: live(z.array(z.string()).default([]))
|
|
6596
6689
|
});
|
|
6597
6690
|
function apply(ctx, config) {
|
|
6598
6691
|
const ring = new PackyCodeKeyRing(ctx.credentials);
|
|
@@ -6600,7 +6693,8 @@ function apply(ctx, config) {
|
|
|
6600
6693
|
apiHost: normalizeHost(config?.apiHost, DEFAULT_API_HOST),
|
|
6601
6694
|
statusHost: normalizeHost(config?.statusHost, DEFAULT_STATUS_HOST),
|
|
6602
6695
|
searchProvider: fieldValue(config?.searchProvider),
|
|
6603
|
-
searchBackend: fieldValue(config?.searchBackend)
|
|
6696
|
+
searchBackend: fieldValue(config?.searchBackend),
|
|
6697
|
+
hiddenModels: fieldValue(config?.hiddenModels)
|
|
6604
6698
|
});
|
|
6605
6699
|
const hosts = () => {
|
|
6606
6700
|
const current = source() ?? {};
|
|
@@ -6646,6 +6740,17 @@ function apply(ctx, config) {
|
|
|
6646
6740
|
};
|
|
6647
6741
|
const fallbackSessionId = `${PLUGIN_ID}-${randomUUID()}`;
|
|
6648
6742
|
const attachments = () => ctx.get?.("attachments") ?? ctx.attachments;
|
|
6743
|
+
const modelPreference = {
|
|
6744
|
+
writable: false,
|
|
6745
|
+
set: void 0
|
|
6746
|
+
};
|
|
6747
|
+
let refreshModels = () => {};
|
|
6748
|
+
const modelSelection = createModelSelectionController({
|
|
6749
|
+
readPreference: () => source() ?? {},
|
|
6750
|
+
writePreference: (patch) => modelPreference.set(patch),
|
|
6751
|
+
isWritable: () => modelPreference.writable === true && typeof modelPreference.set === "function",
|
|
6752
|
+
onChange: () => refreshModels()
|
|
6753
|
+
});
|
|
6649
6754
|
/**
|
|
6650
6755
|
* The Claude Code CLI runner. PackyCode serves its Claude models only to the
|
|
6651
6756
|
* official CLI, so CLI-backed models are run by that CLI itself — with this
|
|
@@ -6704,12 +6809,13 @@ function apply(ctx, config) {
|
|
|
6704
6809
|
const configured = config?.claudeCliMode;
|
|
6705
6810
|
return configured === void 0 || configured === "auto" ? claudeModeForSession(ctx, session) : configured;
|
|
6706
6811
|
};
|
|
6707
|
-
ctx.llm.registerAdapter([PROVIDER], createClaudeCliAdapter({
|
|
6812
|
+
const registration = ctx.llm.registerAdapter([PROVIDER], createClaudeCliAdapter({
|
|
6708
6813
|
inner: createRouteAdapter({
|
|
6709
6814
|
ring,
|
|
6710
6815
|
keys,
|
|
6711
6816
|
fallbackSessionId,
|
|
6712
|
-
resolveAttachments: attachments
|
|
6817
|
+
resolveAttachments: attachments,
|
|
6818
|
+
isModelVisible: modelSelection.isVisible
|
|
6713
6819
|
}),
|
|
6714
6820
|
runner: claudeRunner,
|
|
6715
6821
|
resolveMode: () => {
|
|
@@ -6720,6 +6826,10 @@ function apply(ctx, config) {
|
|
|
6720
6826
|
resolveAttachments: attachments,
|
|
6721
6827
|
explainKey: (runKey) => keys.describe(runKey)
|
|
6722
6828
|
}));
|
|
6829
|
+
refreshModels = () => {
|
|
6830
|
+
if (typeof registration?.replace === "function") registration.replace([PROVIDER]);
|
|
6831
|
+
else ctx.emit?.("llm/adapters-updated");
|
|
6832
|
+
};
|
|
6723
6833
|
ctx.llm.registerConfigurableProviders([{
|
|
6724
6834
|
provider: PROVIDER,
|
|
6725
6835
|
displayName: DISPLAY_NAME,
|
|
@@ -6771,12 +6881,15 @@ function apply(ctx, config) {
|
|
|
6771
6881
|
onChange: () => {
|
|
6772
6882
|
balance.invalidate();
|
|
6773
6883
|
groups.invalidate();
|
|
6884
|
+
modelSelection.reconcile();
|
|
6774
6885
|
search.reconcile();
|
|
6775
6886
|
},
|
|
6776
6887
|
hostPreference,
|
|
6777
|
-
searchPreference
|
|
6888
|
+
searchPreference,
|
|
6889
|
+
modelPreference
|
|
6778
6890
|
}));
|
|
6779
6891
|
ctx.on?.("loader/volatile-update", () => {
|
|
6892
|
+
modelSelection.reconcile();
|
|
6780
6893
|
search.reconcile();
|
|
6781
6894
|
});
|
|
6782
6895
|
/**
|
|
@@ -6798,7 +6911,8 @@ function apply(ctx, config) {
|
|
|
6798
6911
|
preference: hostPreference,
|
|
6799
6912
|
claudeCli,
|
|
6800
6913
|
pluginManager: createPackyCodePluginManager(),
|
|
6801
|
-
search
|
|
6914
|
+
search,
|
|
6915
|
+
modelSelection
|
|
6802
6916
|
});
|
|
6803
6917
|
/**
|
|
6804
6918
|
* Connection registers a channel through `owner.webServer`, read on its own
|
|
@@ -6820,4 +6934,4 @@ function apply(ctx, config) {
|
|
|
6820
6934
|
});
|
|
6821
6935
|
}
|
|
6822
6936
|
//#endregion
|
|
6823
|
-
export { AUTO_CLAUDE_MODE, CHANNEL, CLAUDE_CLI_MODES, CLAUDE_INSTALL_COMMAND, CLAUDE_MISSING_MESSAGE, CLAUDE_NPM_PACKAGE, CLAUDE_TOOL_INSTRUCTIONS, CLI_BACKED_MODEL_IDS, CLI_IMAGE_LIMITS, Config, DEFAULT_API_HOST, DEFAULT_APPEND_SYSTEM_PROMPT, DEFAULT_SEARCH_BACKEND, DEFAULT_STATUS_HOST, DISPLAY_NAME, LEGACY_STATUS_HOSTS, PACKYCODE_AUTO_SEARCH_PROVIDER_ID, PACKYCODE_MODELS, PACKYCODE_MODEL_IDS, PACKYCODE_SEARCH_PROVIDER_ID, PLUGIN_ID, PROVIDER, PackyCodeKeyRing, SEARCH_BACKENDS, SEARCH_BACKEND_MODELS, SEARCH_MODES, SETTINGS_NAMESPACE, TITLE_MODEL_ID, TITLE_MODEL_ROUTES, accountHostOrder, apply, assertApiKey, buildClaudeArgs, buildClaudeEnvironment, buildClaudeInput, buildDelegatedContent, buildSearchRequest, capClaudeMode, catalogCoverage, classifyClientPolicy, classifyGatewayFailure, claudeBinaryCandidates, claudeModeFor, claudeModeForSession, cliSearchInstructions, createBalanceReader, createClaudeCliAdapter, createClaudeCliManager, createClaudeCodeTool, createClaudeRunner, createClaudeSearchBridge, createGroupReader, createKeyChooser, createPackyCodeAutoSearchProvider, createPackyCodeProfile, createPackyCodeProvider, createPackyCodeRpc, createPackyCodeSearchProvider, createProfileSearchComposition, createRouteAdapter, createSearchComposition, createSearchController, delegationInstructions, describeRunKey, explainCliClientPolicyFailure, explainCliGroupFailure, explainClientPolicyFailure, explainNpmFailure, findClaudeBinary, guardKeyRotation, inject, keyNote, locateClaudeBinary, locateNpm, name, normalizeFailureMessage, normalizeHost, npmCandidates, parseBillingSubscription, parseBillingUsage, parseClaudeResult, parseClaudeVersion, parseMessagesSearchResponse, parsePricing, parseResponsesSearchResponse, parseSelectedGroups, parseTokenUsage, pickKeyForModel, planModeForSession, planModeForToolExecution, readClaudeStreamEvents, readSearchChoice, renderAuxiliaryPrompt, renderDelegatedPrompt, resolveSpawnCommand, statusHostOrder, summarizeToolUse };
|
|
6937
|
+
export { AUTO_CLAUDE_MODE, CHANNEL, CLAUDE_CLI_MODES, CLAUDE_INSTALL_COMMAND, CLAUDE_MISSING_MESSAGE, CLAUDE_NPM_PACKAGE, CLAUDE_TOOL_INSTRUCTIONS, CLI_BACKED_MODEL_IDS, CLI_IMAGE_LIMITS, Config, DEFAULT_API_HOST, DEFAULT_APPEND_SYSTEM_PROMPT, DEFAULT_SEARCH_BACKEND, DEFAULT_STATUS_HOST, DISPLAY_NAME, LEGACY_STATUS_HOSTS, PACKYCODE_AUTO_SEARCH_PROVIDER_ID, PACKYCODE_MODELS, PACKYCODE_MODEL_IDS, PACKYCODE_SEARCH_PROVIDER_ID, PLUGIN_ID, PROVIDER, PackyCodeKeyRing, SEARCH_BACKENDS, SEARCH_BACKEND_MODELS, SEARCH_MODES, SETTINGS_NAMESPACE, TITLE_MODEL_ID, TITLE_MODEL_ROUTES, accountHostOrder, apply, assertApiKey, buildClaudeArgs, buildClaudeEnvironment, buildClaudeInput, buildDelegatedContent, buildSearchRequest, capClaudeMode, catalogCoverage, classifyClientPolicy, classifyGatewayFailure, claudeBinaryCandidates, claudeModeFor, claudeModeForSession, cliSearchInstructions, createBalanceReader, createClaudeCliAdapter, createClaudeCliManager, createClaudeCodeTool, createClaudeRunner, createClaudeSearchBridge, createGroupReader, createKeyChooser, createModelSelectionController, createPackyCodeAutoSearchProvider, createPackyCodeProfile, createPackyCodeProvider, createPackyCodeRpc, createPackyCodeSearchProvider, createProfileSearchComposition, createRouteAdapter, createSearchComposition, createSearchController, delegationInstructions, describeRunKey, explainCliClientPolicyFailure, explainCliGroupFailure, explainClientPolicyFailure, explainNpmFailure, findClaudeBinary, guardKeyRotation, inject, keyNote, locateClaudeBinary, locateNpm, name, normalizeFailureMessage, normalizeHost, npmCandidates, parseBillingSubscription, parseBillingUsage, parseClaudeResult, parseClaudeVersion, parseMessagesSearchResponse, parsePricing, parseResponsesSearchResponse, parseSelectedGroups, parseTokenUsage, pickKeyForModel, planModeForSession, planModeForToolExecution, readClaudeStreamEvents, readHiddenModels, readSearchChoice, renderAuxiliaryPrompt, renderDelegatedPrompt, resolveSpawnCommand, statusHostOrder, summarizeToolUse };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-packycode-api",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "Use a PackyCode (PackyAPI) API key in DeepSeek Harness through a built-in multi-protocol model route with key rotation and balance display",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"@deepseek-ai/dsh-llm-pi-ai": ">=0.1.5-rc.1 <0.3.0-0",
|
|
77
77
|
"@deepseek-ai/dsh-settings": ">=0.1.5-rc.1 <0.3.0-0",
|
|
78
78
|
"@deepseek-ai/dsh-tools": ">=0.1.5-rc.1 <0.3.0-0",
|
|
79
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
79
|
+
"@deepseek-ai/schemastery": "^3.18.4",
|
|
80
80
|
"@earendil-works/pi-ai": "0.85.1 || 0.86.0 || 0.86.1 || 0.87.0 || 0.87.1",
|
|
81
81
|
"react": "^18.2.0"
|
|
82
82
|
},
|
|
@@ -87,10 +87,11 @@
|
|
|
87
87
|
"@deepseek-ai/dsh-llm-pi-ai": "0.1.5-rc.2",
|
|
88
88
|
"@deepseek-ai/dsh-settings": "0.1.5-rc.2",
|
|
89
89
|
"@deepseek-ai/dsh-tools": "0.1.5-rc.2",
|
|
90
|
-
"@deepseek-ai/schemastery": "3.18.
|
|
90
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
91
91
|
"@earendil-works/pi-ai": "0.85.1",
|
|
92
92
|
"react": "18.3.1",
|
|
93
|
-
"tsdown": "0.22.2"
|
|
93
|
+
"tsdown": "0.22.2",
|
|
94
|
+
"yaml": "2.9.1"
|
|
94
95
|
},
|
|
95
96
|
"scripts": {
|
|
96
97
|
"build": "tsdown --config tsdown.config.mjs",
|