dsh-packycode-api 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.1
4
+
5
+ - **The balance read no longer requests PackyCode's billing endpoints.** Every conversation balance refresh called `/v1/dashboard/billing/subscription` and `/v1/dashboard/billing/usage` and treated the answers as account-wide billing. PackyCode answers them per key: a live check on 2026-10-17 returned each key's own grant as `hard_limit_usd`, its own spend in cents as `total_usage` (`1387.9536` for $13.8795 used), and the placeholder `hard_limit_usd: 100000000` for an unlimited key. Nothing has displayed these figures since the account line was removed — its large negative remaining came from reading cents as dollars — so the requests only delayed each refresh. An API key cannot read the account's own balance at all; that sits behind the dashboard API, which takes a login session. An unlimited key therefore still shows only its own used amount.
6
+ - The `balance` RPC no longer returns an `account` field. `createBalanceReader` drops its `includeAccount` option, and `parseBillingSubscription`, `parseBillingUsage` and `accountHostOrder` are no longer exported. A 402/403 on a key's balance read now says the gateway refused that read rather than naming the account. `scripts/live-check.mjs` prints the key's own used amount instead of the account line.
7
+ - Validation, 2026-10-17: 393 offline tests (three account-only cases removed with the code, one added), both rebuilt bundles and the versioned tarball. Live, before the change: for three stored keys the billing endpoints echoed each key's own grant and its spend in cents, the unlimited key answered `hard_limit_usd: 100000000`, and `www.packyapi.ai/api/user/self` answered a scripted request with a Cloudflare 403. After it: the built reader made one `/api/usage/token/` request per stored key and no billing request, and read the unlimited key as `unlimitedQuota: true`. `scripts/live-check.mjs` passed: every HTTP model answered, and key rotation and the RPC projections were unchanged. No DSH restart.
8
+
3
9
  ## 1.3.0
4
10
 
5
11
  - **Claude runs directly inside the plugin.** Opus, Sonnet and Haiku now use the native Anthropic Messages HTTP route with DSH tools, attachments, permissions, plan mode and durable history. Claude session titles use Haiku over HTTP. No Claude Code installation, login or subprocess is required.
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.0
50
+ dsh-packycode-api@1.3.1
51
51
  ```
52
52
 
53
- Choose the **official** install source, then click **Install**. `1.3.0` was the latest release when this was written; the [npm page](https://www.npmjs.com/package/dsh-packycode-api) shows the current one.
53
+ Choose the **official** install source, then click **Install**. `1.3.1` 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 nine models use the plugin’s HT
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.0`. Do not use it for `desktop` — the CLI refuses it (`profile "desktop" is managed exclusively by the Electron application`).
74
+ **For non-desktop profiles only:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.1`. 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
 
@@ -200,7 +200,7 @@ Only GPT Responses requests keep `prompt_cache_key`; other Responses models omit
200
200
 
201
201
  ## Update and uninstall
202
202
 
203
- **Update:** open **Settings → PackyCode** and, when a newer version is listed, click **Update plugin**. It installs that exact version into the owning profile with DSH's bundled pnpm and reads `package.json` back before reporting success. A local `link:` checkout does not get the button. Then quit and reopen the desktop application. A page refresh does not reload the Host. Reinstalling from the **Plugins** page works too, again with the version in the package name (for example `dsh-packycode-api@1.3.0`); without it you may get a version that is merely the newest one published at least 24 hours ago.
203
+ **Update:** open **Settings → PackyCode** and, when a newer version is listed, click **Update plugin**. It installs that exact version into the owning profile with DSH's bundled pnpm and reads `package.json` back before reporting success. A local `link:` checkout does not get the button. Then quit and reopen the desktop application. A page refresh does not reload the Host. Reinstalling from the **Plugins** page works too, again with the version in the package name (for example `dsh-packycode-api@1.3.1`); without it you may get a version that is merely the newest one published at least 24 hours ago.
204
204
 
205
205
  **Coming back from a local-path install, uninstall first.** An update is a `pnpm add`: it rewrites the files inside the package directory and leaves a `node_modules` that an earlier local-path install left there. That older tree keeps shadowing the packages the DSH installation provides, so the plugin row fails to load with no visible reason. **Uninstall** runs `pnpm remove`, which deletes the package directory outright — only a reinstall after that is clean. `node scripts/install-check.mjs` reports whether a profile is in that state.
206
206
 
@@ -216,7 +216,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
216
216
 
217
217
  Output means a leftover tree: a registry install ships no `node_modules`, so this one usually comes from an earlier local-path install (`file:`, or a bare path instead of `link:`). It sits inside the profile directory, and DSH resolves a plugin's imports from inside the profile first, so the plugin gets those old copies — for example `@deepseek-ai/schemastery` 3.18.2, which has no `Schema.volatile()`, so the module throws while it is imported. When an import fails the Loader creates no fiber for that row and no client bundle is composed either, so the model route, the RPC channel, and the settings panel all stay absent.
218
218
 
219
- Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.3.0`), 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.
219
+ Recovery: **uninstall** the plugin on the **Plugins** page, **install** it again with the version in the package name (for example `dsh-packycode-api@1.3.1`), then quit and reopen the desktop application. Uninstall runs `pnpm remove` and deletes the whole package directory; reinstalling or clicking **Update** only rewrites files and keeps the leftover tree. Deleting that `node_modules` by hand and restarting works too.
220
220
 
221
221
  **Right after a first install, every card in Settings shows `transport failure for /packycode-api/…: HTTP 405`?** The installed version is 1.1.6 or earlier, usually because only the bare package name was entered: the install prompt shows the newest version, but pnpm installs the newest one published at least 24 hours ago (see **Install the plugin**). Those older versions can register their settings channel only after DSH restarts and rebuilds its `connection` service. So right after a first install `/packycode-api` does not exist yet, requests fall through to the static page, and it answers 405; the model route and the settings panel itself load normally. From 1.1.7 on, a first install needs no restart. Check which version was installed:
222
222
 
package/README.md CHANGED
@@ -47,10 +47,10 @@
47
47
  打开桌面应用的**插件**页,点击**添加插件**,在**包名或地址**中填入**带版本号**的包名:
48
48
 
49
49
  ```text
50
- dsh-packycode-api@1.3.0
50
+ dsh-packycode-api@1.3.1
51
51
  ```
52
52
 
53
- 在**安装源**中选择**官方源**,然后点击**安装**。`1.3.0` 是写这份说明时的最新版,当前最新版本号以 [npm 页面](https://www.npmjs.com/package/dsh-packycode-api) 为准。
53
+ 在**安装源**中选择**官方源**,然后点击**安装**。`1.3.1` 是写这份说明时的最新版,当前最新版本号以 [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.0
71
71
 
72
72
  桌面应用的**插件**页可以看到已安装状态与版本,也可以在那里启用、停用或卸载。
73
73
 
74
- **仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.0`。不要对 `desktop` 使用它 —— CLI 会直接拒绝(`profile "desktop" is managed exclusively by the Electron application`)。
74
+ **仅用于非 desktop profile:** `dsh plugin --profile <name> add dsh-packycode-api@1.3.1`。不要对 `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
 
@@ -200,7 +200,7 @@ Responses 请求只有 GPT 模型保留 `prompt_cache_key`,其他 Responses
200
200
 
201
201
  ## 更新与卸载
202
202
 
203
- **更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.3.0`),否则装上的可能是发布满 24 小时的旧版。
203
+ **更新:** 打开 **设置 → PackyCode**,有新版本时点「更新插件」。它会在当前 profile 目录里用 DSH 自带的 pnpm 安装精确版本,并读回 `package.json` 确认版本落地。本地 `link:` 开发安装不会出现这个按钮。完成后完全退出并重新打开桌面应用。只刷新页面不会重载 Host。也可以在**插件**页重新安装,同样要填写带版本号的包名(例如 `dsh-packycode-api@1.3.1`),否则装上的可能是发布满 24 小时的旧版。
204
204
 
205
205
  **从本地路径安装切回官方源时,必须先卸载。** 「更新」是 `pnpm add`,它只重写包目录里的文件,不会删除更早一次本地路径安装留在包目录内的 `node_modules`;那份旧依赖会继续挡住 DSH 自带的包,插件行加载失败且看不出原因。「卸载」执行的是 `pnpm remove`,会删掉整个包目录,重装才是干净的。用 `node scripts/install-check.mjs` 可以确认当前 profile 是否处于这个状态。
206
206
 
@@ -216,7 +216,7 @@ ls ~/.dsh/profiles/desktop/node_modules/dsh-packycode-api/node_modules
216
216
 
217
217
  有输出就是残留:从 npm 安装的包不会自带 `node_modules`,这一份通常来自更早一次「本地路径」安装(`file:` 或直接写路径,而不是 `link:`)。它位于 profile 目录内部,而 DSH 解析插件导入时优先使用 profile 内的副本,所以插件拿到的仍是那份旧依赖 —— 例如 `@deepseek-ai/schemastery` 3.18.2 没有 `Schema.volatile()`,模块在导入阶段就抛错。导入失败时 Loader 不会为这一行创建 fiber,客户端 bundle 也不会被组合,于是模型路由、RPC 通道和设置页全都不会出现。
218
218
 
219
- 修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.3.0`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
219
+ 修复:到**插件**页**卸载**本插件,再用带版本号的包名重新**安装**(例如 `dsh-packycode-api@1.3.1`),然后完全退出并重新打开桌面应用。卸载执行的是 `pnpm remove`,会删掉整个包目录;直接重装或点「更新」只重写文件,不会清掉这份残留。也可以手动删掉上面那个 `node_modules` 再重启。
220
220
 
221
221
  **首次安装后,设置页每张卡片都显示 `transport failure for /packycode-api/…: HTTP 405`?** 实际装上的是 1.1.6 或更早的版本,通常是因为安装时只填了包名:安装提示显示的是最新版,pnpm 实际装的却是发布满 24 小时的旧版(见「安装插件」)。这些旧版要等 DSH 重启、重建它的 `connection` 服务之后,才能注册设置通道。所以首次安装后 `/packycode-api` 通道还不存在,请求落到静态页面,返回 405;模型路由和设置页本身照常加载。1.1.7 起首次安装不需要重启。先确认装上的版本:
222
222
 
package/lib/client.js CHANGED
@@ -592,10 +592,9 @@ window.__ModuleLoader__.load({
592
592
  * Totals across the keys a balance read returned.
593
593
  *
594
594
  * The per-key quota (`/api/usage/token/`) is what a key can spend, so the
595
- * combined figure is the sum of those rows. PackyCode also reports account
596
- * level billing (`/v1/dashboard/billing/*`), which is a different scope — it
597
- * counts every token on the account — and is deliberately not what this sum
598
- * shows.
595
+ * combined figure is the sum of those rows. An API key cannot read the
596
+ * PackyCode account's own balance, so tokens on the same account that are not
597
+ * stored here never appear in it.
599
598
  *
600
599
  * @module dsh-packycode-api/totals
601
600
  */
package/lib/index.js CHANGED
@@ -218,11 +218,10 @@ const DEFAULT_REASONING_LEVEL = "high";
218
218
  /**
219
219
  * Which hosts a read goes to.
220
220
  *
221
- * PackyCode serves models and the billing endpoints from `cf.api.fan`, and
222
- * the per-key `/api/*` family plus public pricing from the PackyAPI site
223
- * (`cf.api.fan` answers 404 there). A saved legacy status host
224
- * (`www.packyapi.com`, DNS-poisoned on mainland networks) is tried after the
225
- * current default, never before it.
221
+ * PackyCode serves models from `cf.api.fan`, and the per-key `/api/*` family
222
+ * plus public pricing from the PackyAPI site (`cf.api.fan` answers 404
223
+ * there). A saved legacy status host (`www.packyapi.com`, DNS-poisoned on
224
+ * mainland networks) is tried after the current default, never before it.
226
225
  *
227
226
  * @module dsh-packycode-api/hosts
228
227
  */
@@ -257,19 +256,6 @@ function statusHostOrder(hosts) {
257
256
  DEFAULT_API_HOST
258
257
  ]);
259
258
  }
260
- /**
261
- * The hosts the account read tries: the model hosts first (they serve the
262
- * billing endpoints), then the status hosts.
263
- * @param hosts - the resolved host settings.
264
- * @returns unique origins.
265
- */
266
- function accountHostOrder(hosts) {
267
- return unique([
268
- hosts?.apiHost,
269
- DEFAULT_API_HOST,
270
- ...statusHostOrder(hosts)
271
- ]);
272
- }
273
259
  //#endregion
274
260
  //#region src/secret-redactor.js
275
261
  /**
@@ -288,15 +274,20 @@ function redactSecrets(text, values) {
288
274
  //#endregion
289
275
  //#region src/balance.js
290
276
  /**
291
- * Account and per-key balance.
277
+ * Per-key balance.
292
278
  *
293
- * PackyCode is a new-api style relay: `/api/usage/token/` reports the quota of
294
- * the key that authorized the request, and `/v1/dashboard/billing/*` reports
295
- * the account. Only the second family exists on the model host (`cf.api.fan`
296
- * answers 404 for `/api/*`), so a read tries the configured host first and
297
- * falls back to the PackyAPI host — the same fallback the PackyCode status
298
- * tooling uses. A saved legacy status host (`www.packyapi.com`, DNS-poisoned
299
- * on mainland networks) is tried after the current default, not before it.
279
+ * PackyCode is a new-api style relay, and an API key can read only its own
280
+ * figures: `/api/usage/token/` reports the quota of the key that authorized
281
+ * the request. The `/v1/dashboard/billing/*` family is per key on this gateway
282
+ * as well — `hard_limit_usd` is the key's own grant, `total_usage` its own
283
+ * spend in cents, and an unlimited key reports the placeholder limit
284
+ * 100000000 — so it adds nothing and is not requested. The account's own
285
+ * balance sits behind the dashboard API, which takes a login session rather
286
+ * than a key. `cf.api.fan` answers 404 for the whole `/api/*` family, so a read
287
+ * tries the status host first and falls back through the other PackyCode
288
+ * hosts — the same fallback the PackyCode status tooling uses. A saved legacy
289
+ * status host (`www.packyapi.com`, DNS-poisoned on mainland networks) is tried
290
+ * after the current default, not before it.
300
291
  *
301
292
  * Credentials and response bodies stay host-side; the browser receives only
302
293
  * the projection built here.
@@ -354,25 +345,9 @@ function parseTokenUsage(payload) {
354
345
  ...expiresAt === void 0 || expiresAt <= 0 ? {} : { expiresAt }
355
346
  };
356
347
  }
357
- /** Normalize `/v1/dashboard/billing/subscription`. */
358
- function parseBillingSubscription(payload) {
359
- if (!record$2(payload)) return void 0;
360
- return {
361
- ...number(payload.soft_limit_usd) === void 0 ? {} : { softLimitUsd: number(payload.soft_limit_usd) },
362
- ...number(payload.hard_limit_usd) === void 0 ? {} : { hardLimitUsd: number(payload.hard_limit_usd) },
363
- ...number(payload.system_hard_limit_usd) === void 0 ? {} : { systemHardLimitUsd: number(payload.system_hard_limit_usd) },
364
- ...number(payload.access_until) === void 0 || number(payload.access_until) <= 0 ? {} : { accessUntil: number(payload.access_until) }
365
- };
366
- }
367
- /** Normalize `/v1/dashboard/billing/usage`. */
368
- function parseBillingUsage(payload) {
369
- if (!record$2(payload)) return void 0;
370
- const total = number(payload.total_usage);
371
- return total === void 0 ? {} : { totalUsageUsd: Math.round(total * 1e6) / 1e6 };
372
- }
373
348
  function statusMessage(status) {
374
349
  if (status === 401) return "this API key was rejected (401)";
375
- if (status === 402 || status === 403) return "the account cannot be read with this key (402/403)";
350
+ if (status === 402 || status === 403) return "the gateway refused this key's balance read (402/403)";
376
351
  if (status === 429) return "the gateway is rate limiting balance reads (429)";
377
352
  return `the gateway answered HTTP ${status}`;
378
353
  }
@@ -416,17 +391,16 @@ async function withFallback(hosts, path, key, options) {
416
391
  * A read that fails for one key — the gateway throttled it, a host was
417
392
  * unreachable — keeps showing the last balance that key did report, marked
418
393
  * `stale` with when it was read and why this read failed, instead of blanking
419
- * the row. The same holds for the account figures. These last-good values
420
- * live in the host process: a page refresh keeps them, a restart does not.
394
+ * the row. These last-good values live in the host process: a page refresh
395
+ * keeps them, a restart does not.
421
396
  * @param options - key listing, host resolution, and transport knobs.
422
397
  * @returns `{ read({force, signal}), invalidate() }`.
423
398
  */
424
- function createBalanceReader({ listKeys, getHosts, fetchImpl = ambientFetch, now = Date.now, ttlMs = DEFAULT_BALANCE_TTL_MS, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS, includeAccount = true } = {}) {
399
+ function createBalanceReader({ listKeys, getHosts, fetchImpl = ambientFetch, now = Date.now, ttlMs = DEFAULT_BALANCE_TTL_MS, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS } = {}) {
425
400
  if (typeof listKeys !== "function") throw new Error("PackyCode balance reader requires listKeys");
426
401
  if (typeof getHosts !== "function") throw new Error("PackyCode balance reader requires getHosts");
427
- /** Last successful per-key usage, by key id, and the last account figures. */
402
+ /** Last successful per-key usage, by key id. */
428
403
  const lastGood = /* @__PURE__ */ new Map();
429
- let lastAccount;
430
404
  const loadKey = async (entry, hosts, signal) => {
431
405
  const { payload } = await withFallback(statusHostOrder(hosts), "/api/usage/token/", entry.key, {
432
406
  fetchImpl,
@@ -437,43 +411,6 @@ function createBalanceReader({ listKeys, getHosts, fetchImpl = ambientFetch, now
437
411
  if (usage === void 0) throw new Error("the gateway returned an unrecognized usage payload");
438
412
  return usage;
439
413
  };
440
- /**
441
- * The account endpoints need one working key. Candidates are ordered by
442
- * evidence: a key whose per-key read just succeeded, then the key that
443
- * answered this read last time, then the rest — so an invalid key sitting
444
- * first in the ring cannot blank the account total.
445
- */
446
- let accountKeyId;
447
- const loadAccount = async (keys, hosts, signal, usableIds = []) => {
448
- const candidates = [.../* @__PURE__ */ new Set([
449
- ...usableIds,
450
- accountKeyId,
451
- ...keys.filter((entry) => entry.disabled !== true).map((entry) => entry.id)
452
- ])].map((id) => keys.find((entry) => entry.id === id)).filter((entry) => entry !== void 0);
453
- if (candidates.length === 0) throw new Error("add a PackyCode API key to read the account balance");
454
- const hostsToTry = accountHostOrder(hosts);
455
- const options = {
456
- fetchImpl,
457
- timeoutMs,
458
- signal
459
- };
460
- let lastError;
461
- for (const key of candidates) try {
462
- const subscription = parseBillingSubscription((await withFallback(hostsToTry, "/v1/dashboard/billing/subscription", key.key, options)).payload);
463
- const usage = parseBillingUsage((await withFallback(hostsToTry, "/v1/dashboard/billing/usage", key.key, options)).payload);
464
- const remaining = subscription?.hardLimitUsd === void 0 || usage?.totalUsageUsd === void 0 ? void 0 : Math.round((subscription.hardLimitUsd - usage.totalUsageUsd) * 1e6) / 1e6;
465
- accountKeyId = key.id;
466
- return {
467
- ...subscription,
468
- ...usage,
469
- ...remaining === void 0 ? {} : { remainingUsd: remaining },
470
- keyId: key.id
471
- };
472
- } catch (error) {
473
- lastError = error;
474
- }
475
- throw lastError;
476
- };
477
414
  const load = async (signal) => {
478
415
  signal?.throwIfAborted();
479
416
  const hosts = getHosts();
@@ -482,7 +419,6 @@ function createBalanceReader({ listKeys, getHosts, fetchImpl = ambientFetch, now
482
419
  const safe = (value) => redactSecrets(value, secrets);
483
420
  for (const id of lastGood.keys()) if (!keys.some((entry) => entry.id === id)) lastGood.delete(id);
484
421
  const entries = [];
485
- const usableIds = [];
486
422
  for (const entry of keys) {
487
423
  const base = {
488
424
  id: entry.id,
@@ -501,7 +437,6 @@ function createBalanceReader({ listKeys, getHosts, fetchImpl = ambientFetch, now
501
437
  ...base,
502
438
  usage
503
439
  });
504
- usableIds.push(entry.id);
505
440
  } catch (error) {
506
441
  signal?.throwIfAborted();
507
442
  const message = safe(error instanceof Error ? error.message : String(error));
@@ -519,27 +454,8 @@ function createBalanceReader({ listKeys, getHosts, fetchImpl = ambientFetch, now
519
454
  });
520
455
  }
521
456
  }
522
- let account;
523
- if (includeAccount) try {
524
- account = await loadAccount(keys, hosts, signal, usableIds);
525
- lastAccount = {
526
- account,
527
- fetchedAt: now()
528
- };
529
- } catch (error) {
530
- signal?.throwIfAborted();
531
- const message = safe(error instanceof Error ? error.message : String(error));
532
- account = lastAccount === void 0 || !keys.some((entry) => entry.id === lastAccount.account.keyId) ? { error: message } : {
533
- ...lastAccount.account,
534
- stale: {
535
- fetchedAt: lastAccount.fetchedAt,
536
- error: message
537
- }
538
- };
539
- }
540
457
  return {
541
458
  keys: entries,
542
- ...includeAccount ? { account } : {},
543
459
  hosts,
544
460
  fetchedAt: now()
545
461
  };
@@ -5183,8 +5099,7 @@ function apply(ctx, config) {
5183
5099
  });
5184
5100
  const imageBalance = createBalanceReader({
5185
5101
  listKeys: () => imageRing.secrets(),
5186
- getHosts: hosts,
5187
- includeAccount: false
5102
+ getHosts: hosts
5188
5103
  });
5189
5104
  /**
5190
5105
  * Which token groups each key is in, and so which models it can reach.
@@ -5403,4 +5318,4 @@ function apply(ctx, config) {
5403
5318
  });
5404
5319
  }
5405
5320
  //#endregion
5406
- export { CHANNEL, Config, DEFAULT_API_HOST, DEFAULT_IMAGE_MODEL, DEFAULT_SEARCH_BACKEND, DEFAULT_STATUS_HOST, DIAGNOSTICS_FORMAT, DIAGNOSTICS_LIMITS, DISPLAY_NAME, IMAGE_MODELS, IMAGE_MODEL_IDS, IMAGE_QUALITIES, IMAGE_TIMEOUT_MS, IMAGE_TOOL_NAME, LEGACY_STATUS_HOSTS, MAX_IMAGE_BYTES, 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, TEST_MESSAGE_POLICY_CODE, accountHostOrder, apply, assertApiKey, buildSearchRequest, catalogCoverage, classifyClientPolicy, classifyGatewayFailure, classifyTestMessagePolicy, classifyUpstreamServerError, createBalanceReader, createDiagnostics, createGroupReader, createImageController, createKeyChooser, createModelSelectionController, createPackyCodeAutoSearchProvider, createPackyCodeImageClient, createPackyCodeImageTool, createPackyCodeProfile, createPackyCodeProvider, createPackyCodeRpc, createPackyCodeSearchProvider, createProfileSearchComposition, createRouteAdapter, createSearchComposition, createSearchController, decodeImagePng, explainClientPolicyFailure, explainTestMessagePolicyFailure, guardKeyRotation, inject, name, normalizeFailureMessage, normalizeHost, parseBillingSubscription, parseBillingUsage, parseMessagesSearchResponse, parsePricing, parseResponsesSearchResponse, parseSelectedGroups, parseTokenUsage, pickKeyForModel, pngDimensions, readHiddenModels, readImagePreference, readSearchChoice, statusHostOrder, summarizeSessionEvents, validateImageOptions };
5321
+ export { CHANNEL, Config, DEFAULT_API_HOST, DEFAULT_IMAGE_MODEL, DEFAULT_SEARCH_BACKEND, DEFAULT_STATUS_HOST, DIAGNOSTICS_FORMAT, DIAGNOSTICS_LIMITS, DISPLAY_NAME, IMAGE_MODELS, IMAGE_MODEL_IDS, IMAGE_QUALITIES, IMAGE_TIMEOUT_MS, IMAGE_TOOL_NAME, LEGACY_STATUS_HOSTS, MAX_IMAGE_BYTES, 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, TEST_MESSAGE_POLICY_CODE, apply, assertApiKey, buildSearchRequest, catalogCoverage, classifyClientPolicy, classifyGatewayFailure, classifyTestMessagePolicy, classifyUpstreamServerError, createBalanceReader, createDiagnostics, createGroupReader, createImageController, createKeyChooser, createModelSelectionController, createPackyCodeAutoSearchProvider, createPackyCodeImageClient, createPackyCodeImageTool, createPackyCodeProfile, createPackyCodeProvider, createPackyCodeRpc, createPackyCodeSearchProvider, createProfileSearchComposition, createRouteAdapter, createSearchComposition, createSearchController, decodeImagePng, explainClientPolicyFailure, explainTestMessagePolicyFailure, guardKeyRotation, inject, name, normalizeFailureMessage, normalizeHost, parseMessagesSearchResponse, parsePricing, parseResponsesSearchResponse, parseSelectedGroups, parseTokenUsage, pickKeyForModel, pngDimensions, readHiddenModels, readImagePreference, readSearchChoice, statusHostOrder, summarizeSessionEvents, validateImageOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-packycode-api",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
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",