dsh-packycode-api 1.3.2 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +7 -4
- package/CHANGELOG.md +6 -0
- package/README.en.md +5 -5
- package/README.md +5 -5
- package/lib/client.js +32 -0
- package/lib/index.js +37 -3
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -17,6 +17,7 @@ This file governs the entire `dsh-packycode-api` repository.
|
|
|
17
17
|
- The route is self-contained: the plugin builds its own pi-ai provider, catalog, key ring, and balance reader. It must never require the user to edit `cordis.patch.yml` or `llm-pi-ai` provider YAML.
|
|
18
18
|
- Never introduce a silent fallback to another provider, another credential source, or an ambient `PACKY_CODE_API_KEY`. Only keys the user added through this plugin may authenticate a request.
|
|
19
19
|
- The catalog declares protocols and effort levels from vendor documentation. Changing a model's `api`, `baseUrl`, or `thinkingLevelMap` requires a note in `CHANGELOG.md` and a live check.
|
|
20
|
+
- The in-plugin update installs under the profile's own pnpm release-age policy. Never pass `--config.minimumReleaseAge` or edit `minimumReleaseAgeExclude` from the plugin. Bypassing the policy leaves a lockfile entry pnpm later rejects, and pnpm then refuses every plugin install in that profile until the version is 24 hours old. pnpm records an exact version it accepts early itself; a version it holds back is reported as pending.
|
|
20
21
|
|
|
21
22
|
## Security requirements
|
|
22
23
|
|
|
@@ -90,17 +91,19 @@ Never reuse, move, delete, or overwrite a published npm version or Git tag. If a
|
|
|
90
91
|
|
|
91
92
|
5. Commit the release and push `main` without force. [`.github/workflows/publish.yml`](.github/workflows/publish.yml) must already be on the commit that will be tagged. Do not push the tag in this step.
|
|
92
93
|
|
|
93
|
-
6. Confirm the package has a GitHub Actions trusted publisher before pushing the tag: repository `BaronCyrus/dsh-packycode-api`, workflow filename `publish.yml`, environment empty
|
|
94
|
+
6. Confirm the package has a GitHub Actions trusted publisher before pushing the tag: repository `BaronCyrus/dsh-packycode-api`, workflow filename `publish.yml`, environment empty, with **Allow npm dist-tag** enabled so the promote job can move `latest`. Never request that a token, password, or one-time code be pasted into chat.
|
|
94
95
|
|
|
95
|
-
7. Create the annotated tag and push it. That push publishes with GitHub OIDC. The workflow's `release` job then creates the GitHub Release from `docs/releases/v<version>.md
|
|
96
|
+
7. Create the annotated tag and push it. That push publishes to npm's `next` dist-tag with GitHub OIDC. The workflow's `release` job then creates the GitHub Release from `docs/releases/v<version>.md`, attaches the tarball npm published, and leaves it without the Latest mark. `latest` moves later: the hourly `promote` job runs `scripts/promote-release.mjs`, which moves `latest` to the `next` version once that version is 25 hours old, and `mark-latest` then marks its GitHub Release Latest. pnpm refuses versions younger than 24 hours, and the update buttons already installed (up to 1.3.2) bypass that refusal, so `latest` must never name a younger version; never move it by hand earlier. `release` and `mark-latest` are the only jobs with `contents: write`, and neither runs dependency code. Do not run `npm publish` locally for a release. A local publish cannot attach provenance. Do not set `NODE_AUTH_TOKEN` or a setup-node `registry-url` in the publish workflow; npm treats either as token auth and skips OIDC.
|
|
96
97
|
|
|
97
98
|
```sh
|
|
98
99
|
git tag -a v<version> -m "dsh-packycode-api v<version>"
|
|
99
100
|
git push origin v<version>
|
|
100
101
|
```
|
|
101
102
|
|
|
102
|
-
`workflow_dispatch` on the same workflow
|
|
103
|
+
`workflow_dispatch` on the same workflow prints the OIDC identity npm will match and does not publish. With `promote` checked, it runs the promote job instead, which still waits out the 25 hours.
|
|
103
104
|
|
|
104
|
-
8. Confirm that both jobs of the publish
|
|
105
|
+
8. Confirm that both jobs of the tag's publish run succeeded. Then check that npm's `next` serves the version (`npm view dsh-packycode-api dist-tags --prefer-online`; without `--prefer-online` a cached 404 can linger) and that `gh release view v<version>` shows the notes and the `dsh-packycode-api-<version>.tgz` asset. On an OIDC `package not found` error, fix the trusted publisher and re-run the same workflow. Do not move the tag. If the tagged tree itself must change, publish a new patch version.
|
|
106
|
+
|
|
107
|
+
The release reaches the update button only when `latest` moves. About 25–26 hours after publishing, confirm that a scheduled `promote` run moved `latest` and that `gh release list --json tagName,isLatest` marks the version Latest. If the promote job fails on the dist-tag, enable **Allow npm dist-tag** on the trusted publisher and run the workflow with `promote`. If no run happened, check that the scheduled workflow is enabled (GitHub disables schedules in a repository without activity for 60 days).
|
|
105
108
|
|
|
106
109
|
If npm published but the release job failed, re-run only that failed job. If the Release must be made by hand, attach the tarball npm serves, not a local `.artifacts` build: CI packs its own, so the local bytes differ. Fetch it with `npm pack dsh-packycode-api@<version>`, and compare its SHA-512 with the registry's `dist.integrity`. Then run `gh release create v<version> <tarball> --verify-tag --title "<the notes' title>" --notes-file <the notes' body>`. The release job leaves an existing Release unchanged.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.3.3
|
|
4
|
+
|
|
5
|
+
- **The update button no longer bypasses pnpm's release-age policy.** It ran `pnpm add --config.minimumReleaseAge=0`, which switched the policy off for that one install, so pnpm never recorded the early version as an exception. The next plugin install in that profile then failed with `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION` on the PackyCode entry until the version was 24 hours old; a user's `dsh-chat-manager` install was refused this way. Reproduced with DSH's bundled pnpm, which ran as 11.7.0 with a 24-hour default window. The button now installs the exact version under the profile's own policy. By default pnpm installs it at once and adds `dsh-packycode-api@<version>` to the profile's `minimumReleaseAgeExclude`, and later installs pass. Under a stricter policy pnpm refuses, and the button reports the version as pending, with the time it clears, instead of failing.
|
|
6
|
+
- **Releases reach `latest` a day later.** A tag now publishes to npm's `next` tag. An hourly job moves `latest` once the version is 25 hours old, then marks its GitHub Release Latest. Update buttons up to 1.3.2 still pass the override and install whatever `latest` names, so `latest` must never name a version pnpm would still refuse. The promotion uses trusted publishing's dist-tag permission (npm CLI 11.21.0 or later) and installs no dependencies. A new version can still be installed sooner by entering its exact version on the Plugins page.
|
|
7
|
+
- Validation, 2026-10-17: 401 offline tests (new: the release-age refusal parser, a pending update, the version card's three outcomes, and the promotion rules and workflow shape), both rebuilt bundles and the versioned tarball. With DSH's bundled pnpm, running as 11.7.0, in scratch DSH homes, the plugin's own `update()` behaved as intended in three cases. Under the default policy it installed 1.3.2 at once, pnpm added `dsh-packycode-api@1.3.2` to `minimumReleaseAgeExclude`, and the next unrelated install passed. Under an explicit `minimumReleaseAge: 1440` it installed nothing and returned pending until 2026-10-17T19:16Z. In a profile the old button had blocked, the same update recorded the exception and unblocked it, after which `dsh-chat-manager@1.5.13` installed with its own exception. `scripts/promote-release.mjs --dry-run` read the registry and found nothing to promote. `scripts/live-check.mjs` passed for all ten models, key rotation and the RPC projections. No DSH restart.
|
|
8
|
+
|
|
3
9
|
## 1.3.2
|
|
4
10
|
|
|
5
11
|
- **GPT-5.6 Sol joins the catalog.** `gpt-5.6-sol` takes GPT-6.1 Sol's route: `openai-responses` at `https://cf.api.fan/v1`, text and image input, 1,050,000 context and 128,000 output, per its developers.openai.com model page. PackyCode serves it to the same groups as GPT-6.1 Sol (`codex`, `codex-sale`, `azure-officially`). OpenAI documents `reasoning.effort` `none | low | medium | high | xhigh | max`, but `off` is not offered: through PackyCode, `none` and `minimal` both answered at effort `medium`. The model therefore gets GPT-6.1 Sol's levels, `low` to `max`. Its session titles run on GPT-6.1 Sol, as GPT-6 Astra's do, and the settings panel counts it under GPT.
|
package/README.en.md
CHANGED
|
@@ -47,10 +47,10 @@ You need **DeepSeek Harness** and a **PackyCode (PackyAPI) API key**. For DSH se
|
|
|
47
47
|
Open the desktop application's **Plugins** page, click **Add plugin**, and enter the package name **with a version** as the **package name or address**:
|
|
48
48
|
|
|
49
49
|
```text
|
|
50
|
-
dsh-packycode-api@1.3.
|
|
50
|
+
dsh-packycode-api@1.3.3
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
Choose the **official** install source, then click **Install**. `1.3.
|
|
53
|
+
Choose the **official** install source, then click **Install**. `1.3.3` was the latest release when this was written; the [npm page](https://www.npmjs.com/package/dsh-packycode-api) shows the current one.
|
|
54
54
|
|
|
55
55
|
> **Always include the version.** With a bare `dsh-packycode-api`, the install prompt shows the newest version on npm, but DSH's bundled pnpm by default installs only versions published at least 24 hours ago, so you can end up several releases behind with no sign of it in the prompt. With an exact version, pnpm installs that version and adds it to the profile's `minimumReleaseAgeExclude` itself. Afterwards, check **Current version** under **Settings → PackyCode → Plugin version**.
|
|
56
56
|
|
|
@@ -71,7 +71,7 @@ Choose a model from the `PackyCode` group. All ten models use the plugin’s HTT
|
|
|
71
71
|
|
|
72
72
|
The **Plugins** page shows the installed state and version, and is also where you enable, disable, or uninstall.
|
|
73
73
|
|
|
74
|
-
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.
|
|
74
|
+
**For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.3`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
|
|
75
75
|
|
|
76
76
|
For a local checkout, keep the `link:` prefix: `dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`. **Do not pass a bare path or `file:`** — that install copies the whole checkout, `node_modules` included, into the profile, where it then shadows the packages the DSH installation itself provides and the plugin looks uninstalled; see the FAQ. If you already installed that way, uninstall first and reinstall as above.
|
|
77
77
|
|
|
@@ -201,7 +201,7 @@ Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit
|
|
|
201
201
|
|
|
202
202
|
## Update and uninstall
|
|
203
203
|
|
|
204
|
-
**Update:** open **Settings → PackyCode** and, when a newer version is listed, click **Update plugin**. It installs that exact version into the owning profile with DSH's bundled pnpm and reads `package.json` back before reporting success. A local `link:` checkout does not get the button. Then quit and reopen the desktop application. A page refresh does not reload the Host. Reinstalling from the **Plugins** page works too, again with the version in the package name (for example `dsh-packycode-api@1.3.
|
|
204
|
+
**Update:** open **Settings → PackyCode** and, when a newer version is listed, click **Update plugin**. It installs that exact version into the owning profile with DSH's bundled pnpm and reads `package.json` back before reporting success. The install keeps pnpm's release-age policy instead of overriding it: by default pnpm installs the version at once and records it in the profile's `minimumReleaseAgeExclude`; under a stricter pnpm policy the button says when the update can run. A new version is published to npm's `next` tag first and becomes `latest` about 25 hours later; the button and installs without a version see it only then. To install it sooner, enter the exact version on the Plugins page. A local `link:` checkout does not get the button. Then quit and reopen the desktop application. A page refresh does not reload the Host. Reinstalling from the **Plugins** page works too, again with the version in the package name (for example `dsh-packycode-api@1.3.3`); without it you may get a version that is merely the newest one published at least 24 hours ago.
|
|
205
205
|
|
|
206
206
|
**Coming back from a local-path install, uninstall first.** An update is a `pnpm add`: it rewrites the files inside the package directory and leaves a `node_modules` that an earlier local-path install left there. That older tree keeps shadowing the packages the DSH installation provides, so the plugin row fails to load with no visible reason. **Uninstall** runs `pnpm remove`, which deletes the package directory outright — only a reinstall after that is clean. `node scripts/install-check.mjs` reports whether a profile is in that state.
|
|
207
207
|
|
|
@@ -217,7 +217,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
|
|
|
217
217
|
|
|
218
218
|
Output means a leftover tree: a registry install ships no `node_modules`, so this one usually comes from an earlier local-path install (`file:`, or a bare path instead of `link:`). It sits inside the profile directory, and DSH resolves a plugin's imports from inside the profile first, so the plugin gets those old copies — for example `@deepseek-ai/schemastery` 3.18.2, which has no `Schema.volatile()`, so the module throws while it is imported. When an import fails the Loader creates no fiber for that row and no client bundle is composed either, so the model route, the RPC channel, and the settings panel all stay absent.
|
|
219
219
|
|
|
220
|
-
Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.3.
|
|
220
|
+
Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.3.3`), then quit and reopen the desktop application. Uninstall runs `pnpm remove` and deletes the whole package directory; reinstalling or clicking **Update** only rewrites files and keeps the leftover tree. Deleting that `node_modules` by hand and restarting works too.
|
|
221
221
|
|
|
222
222
|
**Right after a first install, every card in Settings shows `transport failure for /packycode-api/…: HTTP 405`?** The installed version is 1.1.6 or earlier, usually because only the bare package name was entered: the install prompt shows the newest version, but pnpm installs the newest one published at least 24 hours ago (see **Install the plugin**). Those older versions can register their settings channel only after DSH restarts and rebuilds its `connection` service. So right after a first install `/packycode-api` does not exist yet, requests fall through to the static page, and it answers 405; the model route and the settings panel itself load normally. From 1.1.7 on, a first install needs no restart. Check which version was installed:
|
|
223
223
|
|
package/README.md
CHANGED
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
打开桌面应用的**插件**页,点击**添加插件**,在**包名或地址**中填入**带版本号**的包名:
|
|
48
48
|
|
|
49
49
|
```text
|
|
50
|
-
dsh-packycode-api@1.3.
|
|
50
|
+
dsh-packycode-api@1.3.3
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
在**安装源**中选择**官方源**,然后点击**安装**。`1.3.
|
|
53
|
+
在**安装源**中选择**官方源**,然后点击**安装**。`1.3.3` 是写这份说明时的最新版,当前最新版本号以 [npm 页面](https://www.npmjs.com/package/dsh-packycode-api) 为准。
|
|
54
54
|
|
|
55
55
|
> **一定要带版本号。** 只填 `dsh-packycode-api` 时,安装前的提示显示的是 npm 上的最新版,但 DSH 自带的 pnpm 默认只安装发布满 24 小时的版本,实际装上的可能是好几个版本之前的旧版,而安装提示里看不出这一点。填写精确版本时,pnpm 会直接安装这个版本,并自动把它加入 profile 的 `minimumReleaseAgeExclude`。装好后可以在 **设置 → PackyCode → 插件版本** 核对「当前版本」。
|
|
56
56
|
|
|
@@ -71,7 +71,7 @@ dsh-packycode-api@1.3.2
|
|
|
71
71
|
|
|
72
72
|
桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
|
|
73
73
|
|
|
74
|
-
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.
|
|
74
|
+
**仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.3`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
|
|
75
75
|
|
|
76
76
|
本地开发要加 `link:` 前缀:`dsh plugin --profile <name> add link:/path/to/dsh-packycode-api`。**不要直接写路径(或 `file:`)** —— 那样的安装会把整个仓库目录(连同 `node_modules`)复制进 profile,之后它会在 profile 内部挡住 DSH 自带的包,插件看起来像没装,见「常见问题」。已经那样装过的话,先卸载再按上面的方式重装。
|
|
77
77
|
|
|
@@ -201,7 +201,7 @@ Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses
|
|
|
201
201
|
|
|
202
202
|
## 更新与卸载
|
|
203
203
|
|
|
204
|
-
**更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json`
|
|
204
|
+
**更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。安装遵守 pnpm 的发布冷却策略,不会绕过它:默认情况下 pnpm 会直接装上这个版本,并把它记进 profile 的 `minimumReleaseAgeExclude`;如果你给 pnpm 配了更严格的策略,按钮会提示什么时候可以更新。新版本先发布到 npm 的 `next` 标签,约 25 小时后才成为 `latest`,按钮和不带版本号的安装都要到那时才会看到它;等不及可以在插件页填写精确版本安装。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.3.3`),否则装上的可能是发布满 24 小时的旧版。
|
|
205
205
|
|
|
206
206
|
**从本地路径安装切回官方源时,必须先卸载。** 「更新」是 `pnpm add`,它只重写包目录里的文件,不会删除更早一次本地路径安装留在包目录内的 `node_modules`;那份旧依赖会继续挡住 DSH 自带的包,插件行加载失败且看不出原因。「卸载」执行的是 `pnpm remove`,会删掉整个包目录,重装才是干净的。用 `node scripts/install-check.mjs` 可以确认当前 profile 是否处于这个状态。
|
|
207
207
|
|
|
@@ -217,7 +217,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
|
|
|
217
217
|
|
|
218
218
|
有输出就是残留:从 npm 安装的包不会自带 `node_modules`,这一份通常来自更早一次「本地路径」安装(`file:` 或直接写路径,而不是 `link:`)。它位于 profile 目录内部,而 DSH 解析插件导入时优先使用 profile 内的副本,所以插件拿到的仍是那份旧依赖 —— 例如 `@deepseek-ai/schemastery` 3.18.2 没有 `Schema.volatile()`,模块在导入阶段就抛错。导入失败时 Loader 不会为这一行创建 fiber,客户端 bundle 也不会被组合,于是模型路由、RPC 通道和设置页全都不会出现。
|
|
219
219
|
|
|
220
|
-
修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.3.
|
|
220
|
+
修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.3.3`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
|
|
221
221
|
|
|
222
222
|
**首次安装后,设置页每张卡片都显示 `transport failure for /packycode-api/…: HTTP 405`?** 实际装上的是 1.1.6 或更早的版本,通常是因为安装时只填了包名:安装提示显示的是最新版,pnpm 实际装的却是发布满 24 小时的旧版(见「安装插件」)。这些旧版要等 DSH 重启、重建它的 `connection` 服务之后,才能注册设置通道。所以首次安装后 `/packycode-api` 通道还不存在,请求落到静态页面,返回 405;模型路由和设置页本身照常加载。1.1.7 起首次安装不需要重启。先确认装上的版本:
|
|
223
223
|
|
package/lib/client.js
CHANGED
|
@@ -152,6 +152,8 @@ window.__ModuleLoader__.load({
|
|
|
152
152
|
versionUpdate: "更新插件",
|
|
153
153
|
versionUpdating: "正在更新…",
|
|
154
154
|
versionUpdateFailed: "更新失败,请重试,或在插件页重新安装。",
|
|
155
|
+
versionCooling: "v{version} 发布不久,DSH 的 pnpm 要等新版本过了发布冷却期才会安装,请稍后再更新。",
|
|
156
|
+
versionCoolingUntil: "v{version} 发布不久,DSH 的 pnpm 要等新版本过了发布冷却期才会安装,{time} 之后可以更新。",
|
|
155
157
|
versionUpdated: "已更新到 v{version}。",
|
|
156
158
|
versionUpdatedHint: "新版本需要完全退出并重新打开桌面应用才会生效;只刷新页面不会重载 Host 里的插件。",
|
|
157
159
|
versionRestart: "如何重启",
|
|
@@ -315,6 +317,8 @@ window.__ModuleLoader__.load({
|
|
|
315
317
|
versionUpdate: "Update plugin",
|
|
316
318
|
versionUpdating: "Updating…",
|
|
317
319
|
versionUpdateFailed: "The update failed. Try again, or reinstall from the Plugins page.",
|
|
320
|
+
versionCooling: "v{version} was published recently. DSH’s pnpm installs a new version only after its release cooldown; update again later.",
|
|
321
|
+
versionCoolingUntil: "v{version} was published recently. DSH’s pnpm installs a new version only after its release cooldown; update after {time}.",
|
|
318
322
|
versionUpdated: "Updated to v{version}.",
|
|
319
323
|
versionUpdatedHint: "Quit and reopen the desktop application. Refreshing the page does not reload the plugin inside the Host.",
|
|
320
324
|
versionRestart: "How to restart",
|
|
@@ -2475,12 +2479,27 @@ window.__ModuleLoader__.load({
|
|
|
2475
2479
|
]
|
|
2476
2480
|
});
|
|
2477
2481
|
}
|
|
2482
|
+
/** A release-age wait can end tomorrow, so the date is part of the time. */
|
|
2483
|
+
function readDateTime(ms) {
|
|
2484
|
+
try {
|
|
2485
|
+
return new Date(ms).toLocaleString(void 0, {
|
|
2486
|
+
month: "numeric",
|
|
2487
|
+
day: "numeric",
|
|
2488
|
+
hour: "2-digit",
|
|
2489
|
+
minute: "2-digit"
|
|
2490
|
+
});
|
|
2491
|
+
} catch {
|
|
2492
|
+
return new Date(ms).toISOString().slice(0, 16).replace("T", " ");
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2478
2495
|
function VersionCard({ call, t }) {
|
|
2479
2496
|
const [info, setInfo] = react.useState();
|
|
2480
2497
|
const [loading, setLoading] = react.useState(true);
|
|
2481
2498
|
const [error, setError] = react.useState(false);
|
|
2482
2499
|
const [updating, setUpdating] = react.useState(false);
|
|
2483
2500
|
const [updateFailed, setUpdateFailed] = react.useState(false);
|
|
2501
|
+
/** A version pnpm still holds back under the profile's release-age policy. */
|
|
2502
|
+
const [pending, setPending] = react.useState();
|
|
2484
2503
|
const [updated, setUpdated] = react.useState();
|
|
2485
2504
|
const [restartHint, setRestartHint] = react.useState(false);
|
|
2486
2505
|
const generation = react.useRef(0);
|
|
@@ -2505,7 +2524,12 @@ window.__ModuleLoader__.load({
|
|
|
2505
2524
|
const update = () => {
|
|
2506
2525
|
setUpdating(true);
|
|
2507
2526
|
setUpdateFailed(false);
|
|
2527
|
+
setPending(void 0);
|
|
2508
2528
|
call("plugin/update").then((value) => {
|
|
2529
|
+
if (value?.pending === true) {
|
|
2530
|
+
setPending(value);
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2509
2533
|
setUpdated(value);
|
|
2510
2534
|
setRestartHint(false);
|
|
2511
2535
|
}).catch(() => setUpdateFailed(true)).finally(() => setUpdating(false));
|
|
@@ -2567,6 +2591,14 @@ window.__ModuleLoader__.load({
|
|
|
2567
2591
|
role: "alert",
|
|
2568
2592
|
children: t("versionUpdateFailed")
|
|
2569
2593
|
}),
|
|
2594
|
+
pending !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2595
|
+
className: "pkc-note",
|
|
2596
|
+
role: "status",
|
|
2597
|
+
children: typeof pending.availableAt === "number" ? fill(t("versionCoolingUntil"), {
|
|
2598
|
+
version: pending.version,
|
|
2599
|
+
time: readDateTime(pending.availableAt)
|
|
2600
|
+
}) : fill(t("versionCooling"), { version: pending.version })
|
|
2601
|
+
}),
|
|
2570
2602
|
updated !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2571
2603
|
className: "pkc-callout",
|
|
2572
2604
|
"data-tone": "info",
|
package/lib/index.js
CHANGED
|
@@ -2542,7 +2542,11 @@ function createModelSelectionController({ readPreference, writePreference, isWri
|
|
|
2542
2542
|
* The running version comes from this package. The latest version comes from
|
|
2543
2543
|
* the npm registry. An in-place update is only offered for a registry install,
|
|
2544
2544
|
* and it installs that exact version with the host's own pnpm — never through
|
|
2545
|
-
* `dsh plugin --profile desktop`, which the desktop app refuses.
|
|
2545
|
+
* `dsh plugin --profile desktop`, which the desktop app refuses. The install
|
|
2546
|
+
* runs under the profile's own `minimumReleaseAge`: a version installed past
|
|
2547
|
+
* it would sit in the profile's lockfile, and pnpm then refuses every later
|
|
2548
|
+
* plugin install in that profile until the version is old enough. A version
|
|
2549
|
+
* pnpm still holds back is reported as pending, with when it clears. Registry
|
|
2546
2550
|
* bodies and package-manager output stay host-side.
|
|
2547
2551
|
*
|
|
2548
2552
|
* @module dsh-packycode-api/plugin-version
|
|
@@ -2552,6 +2556,28 @@ const NPM_REGISTRY_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/late
|
|
|
2552
2556
|
const DEFAULT_VERSION_TTL_MS = 5 * 6e4;
|
|
2553
2557
|
const DEFAULT_VERSION_TIMEOUT_MS = 1e4;
|
|
2554
2558
|
const DEFAULT_UPDATE_TIMEOUT_MS = 18e4;
|
|
2559
|
+
const RELEASE_AGE_ERROR = /ERR_PNPM_(?:NO_MATURE_MATCHING_VERSION|MINIMUM_RELEASE_AGE_VIOLATION)/u;
|
|
2560
|
+
const RELEASE_AGE_ENTRY = /(\S+@\S+) was published at (\S+?), within the minimumReleaseAge cutoff \((\S+?)\)/gu;
|
|
2561
|
+
/**
|
|
2562
|
+
* Recognize pnpm refusing a version that is younger than its
|
|
2563
|
+
* `minimumReleaseAge`, whether the version was requested or already sits in
|
|
2564
|
+
* the lockfile. Each refused entry reads `<name>@<version> was published at
|
|
2565
|
+
* <time>, within the minimumReleaseAge cutoff (<time>)`.
|
|
2566
|
+
* @param output - the install's stdout and stderr.
|
|
2567
|
+
* @param finishedAt - when the install ended; pnpm drew its cutoff then.
|
|
2568
|
+
* @returns undefined when this is not a release-age refusal; otherwise
|
|
2569
|
+
* `{ availableAt }`, when the youngest refused entry clears, if pnpm said.
|
|
2570
|
+
*/
|
|
2571
|
+
function releaseAgeRefusal(output, finishedAt) {
|
|
2572
|
+
const text = String(output ?? "").replace(/\u001b\[[0-9;]*m/gu, "");
|
|
2573
|
+
if (!RELEASE_AGE_ERROR.test(text)) return void 0;
|
|
2574
|
+
let availableAt;
|
|
2575
|
+
for (const [, , published, cutoff] of text.matchAll(RELEASE_AGE_ENTRY)) {
|
|
2576
|
+
const clears = Date.parse(published) + (finishedAt - Date.parse(cutoff));
|
|
2577
|
+
if (Number.isFinite(clears) && (availableAt === void 0 || clears > availableAt)) availableAt = clears;
|
|
2578
|
+
}
|
|
2579
|
+
return availableAt === void 0 ? {} : { availableAt };
|
|
2580
|
+
}
|
|
2555
2581
|
/** Parse a strict `x.y.z[-pre][+build]` version; anything else is undefined. */
|
|
2556
2582
|
function parseSemver(value) {
|
|
2557
2583
|
if (typeof value !== "string") return void 0;
|
|
@@ -2826,7 +2852,6 @@ function createPackyCodePluginManager({ fetchImpl = ambientFetch, runCommand = s
|
|
|
2826
2852
|
...launcher.prefix,
|
|
2827
2853
|
"add",
|
|
2828
2854
|
"--save-exact",
|
|
2829
|
-
`--config.minimumReleaseAge=0`,
|
|
2830
2855
|
`${PACKAGE_NAME}@${latest}`
|
|
2831
2856
|
],
|
|
2832
2857
|
cwd: profileDir
|
|
@@ -2838,7 +2863,16 @@ function createPackyCodePluginManager({ fetchImpl = ambientFetch, runCommand = s
|
|
|
2838
2863
|
if (signal?.aborted) throw error;
|
|
2839
2864
|
throw new Error("PackyCode plugin update failed");
|
|
2840
2865
|
}
|
|
2841
|
-
if (result.code !== 0)
|
|
2866
|
+
if (result.code !== 0) {
|
|
2867
|
+
const refusal = releaseAgeRefusal(`${result.stdout ?? ""}\n${result.stderr ?? ""}`, now());
|
|
2868
|
+
if (refusal !== void 0) return {
|
|
2869
|
+
version: latest,
|
|
2870
|
+
profile: install.profile,
|
|
2871
|
+
pending: true,
|
|
2872
|
+
...refusal
|
|
2873
|
+
};
|
|
2874
|
+
throw new Error("PackyCode plugin update failed");
|
|
2875
|
+
}
|
|
2842
2876
|
await confirmInstalled({
|
|
2843
2877
|
profileDir,
|
|
2844
2878
|
expected: latest,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-packycode-api",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "Use PackyCode API keys in DeepSeek Harness with multi-protocol models, image generation and editing, separate key rings, and balance display",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|