dsh-any-deepseek 0.0.13 → 0.0.14

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/lib/balance.d.ts CHANGED
@@ -119,6 +119,25 @@ export declare function parseOllamaBalance(body: unknown): BalanceReading | unde
119
119
  * @returns the reading, or undefined when neither body is this shape.
120
120
  */
121
121
  export declare function parseOpenAIBilling(subscription: unknown, usage: unknown): BalanceReading | undefined;
122
+ /**
123
+ * Parse OpenCode Go's `GET /zen/go/v1/usage`.
124
+ *
125
+ * 🔴 **This surface reports WINDOWS, never money.** The owner remembered correctly that OpenCode
126
+ * exposes a usage query — and it does; what it does not expose (to an API key) is the credit
127
+ * wallet, which lives behind a logged-in browser session. The only key-authenticated money signal
128
+ * is the error thrown at $0 (anomalyco/opencode#44189), which is why the feature request asks for
129
+ * a `balance` object to be added to this very endpoint.
130
+ *
131
+ * Two response shapes are accepted, because a monitor should not break when the provider ships
132
+ * the requested addition:
133
+ * · `{ usage: { rolling: {percent, resetsAt}, weekly: … , monthly: … } }`
134
+ * · the same keys hoisted to the top level.
135
+ * A `balance` object is read IF PRESENT — so the day they merge that request, the number appears
136
+ * with no code change here.
137
+ * @param body - the parsed JSON body.
138
+ * @returns the reading, or undefined when the body carries no recognizable window.
139
+ */
140
+ export declare function parseOpencodeWindows(body: unknown): BalanceReading | undefined;
122
141
  /**
123
142
  * Map one channel's raw bodies onto a reading, dispatching on the declared shape.
124
143
  * @param probe - the preset's declared probe.
package/lib/index.js CHANGED
@@ -278,7 +278,25 @@ const CHANNEL_PRESETS = {
278
278
  ...V41_FLASH_CAPS,
279
279
  compat: { ...DEEPSEEK_THINKING_COMPAT },
280
280
  headers: { ...OPENCODE_HEADERS },
281
- note: "Requires the OpenCode session headers; shipped with the preset. No balance API (subscription usage only)."
281
+ /**
282
+ * 🔴 **The owner was right that OpenCode has a query surface — and it reports WINDOWS, not
283
+ * money.** Both halves of that sentence were measured, in that order:
284
+ *
285
+ * 1. A route exists at `/zen/go/v1/usage`. Proven by status code discrimination without a
286
+ * key: a made-up path under the same prefix answers **404**, while `/usage` answers
287
+ * **401** (route present, auth required). `/zen/v1/balance` answers 404 — that one really
288
+ * does not exist (it is what anomalyco/opencode#44189 is asking for).
289
+ * 2. What it returns is Go-subscription usage windows (rolling / weekly / monthly), **not**
290
+ * the credit wallet. The balance is only reachable from a logged-in browser session
291
+ * (`BillingTable.balance` behind a SolidStart server function), so a key cannot read it —
292
+ * the only key-authenticated money signal is the error thrown at $0.
293
+ *
294
+ * ⇒ Declared as a `windows` shape, which is exactly what this endpoint honestly provides.
295
+ * The credit balance stays absent rather than invented; the panel shows the windows and does
296
+ * not pretend a currency amount exists.
297
+ */
298
+ balance: { shape: "opencode-windows" },
299
+ note: "Requires the OpenCode session headers; shipped with the preset. Usage windows only — the credit balance is not readable with an API key."
282
300
  },
283
301
  commandcode: {
284
302
  displayName: "CommandCode",
@@ -304,8 +322,7 @@ const CHANNEL_PRESETS = {
304
322
  api: "openai-completions",
305
323
  baseURL: "https://amr-link.open-design.ai/v1",
306
324
  model: "deepseek-v4.1-flash",
307
- ...V41_FLASH_CAPS,
308
- balance: { shape: "openai-billing" }
325
+ ...V41_FLASH_CAPS
309
326
  },
310
327
  "deepseek-official": {
311
328
  displayName: "DeepSeek Official",
@@ -1792,6 +1809,7 @@ function balanceUrls(probe, channelBaseURL) {
1792
1809
  switch (probe.shape) {
1793
1810
  case "deepseek": return [`${root}/user/balance`];
1794
1811
  case "ollama": return [`${root}/api/balance`];
1812
+ case "opencode-windows": return [`${root}/usage`];
1795
1813
  case "openai-billing": return [`${root}/dashboard/billing/subscription`, `${root}/dashboard/billing/usage`];
1796
1814
  }
1797
1815
  }
@@ -1951,6 +1969,58 @@ function parseOpenAIBilling(subscription, usage) {
1951
1969
  };
1952
1970
  }
1953
1971
  /**
1972
+ * Parse OpenCode Go's `GET /zen/go/v1/usage`.
1973
+ *
1974
+ * 🔴 **This surface reports WINDOWS, never money.** The owner remembered correctly that OpenCode
1975
+ * exposes a usage query — and it does; what it does not expose (to an API key) is the credit
1976
+ * wallet, which lives behind a logged-in browser session. The only key-authenticated money signal
1977
+ * is the error thrown at $0 (anomalyco/opencode#44189), which is why the feature request asks for
1978
+ * a `balance` object to be added to this very endpoint.
1979
+ *
1980
+ * Two response shapes are accepted, because a monitor should not break when the provider ships
1981
+ * the requested addition:
1982
+ * · `{ usage: { rolling: {percent, resetsAt}, weekly: … , monthly: … } }`
1983
+ * · the same keys hoisted to the top level.
1984
+ * A `balance` object is read IF PRESENT — so the day they merge that request, the number appears
1985
+ * with no code change here.
1986
+ * @param body - the parsed JSON body.
1987
+ * @returns the reading, or undefined when the body carries no recognizable window.
1988
+ */
1989
+ function parseOpencodeWindows(body) {
1990
+ const root = asRecord(body);
1991
+ if (root === void 0) return void 0;
1992
+ const usage = asRecord(root.usage) ?? root;
1993
+ const windows = [];
1994
+ for (const name of [
1995
+ "rolling",
1996
+ "weekly",
1997
+ "monthly"
1998
+ ]) {
1999
+ const window = asRecord(usage[name]);
2000
+ if (window === void 0) continue;
2001
+ const used = num(window.percent);
2002
+ if (used === void 0) continue;
2003
+ windows.push({
2004
+ name,
2005
+ remainingPercent: Math.max(0, Math.min(100, 100 - used)),
2006
+ ...text(window.resetsAt) === void 0 ? {} : { resetsAt: text(window.resetsAt) }
2007
+ });
2008
+ }
2009
+ const amounts = [];
2010
+ const balance = asRecord(root.balance);
2011
+ const usd = num(balance?.usd);
2012
+ if (usd !== void 0) amounts.push({
2013
+ currency: text(balance?.currency) ?? "USD",
2014
+ remaining: usd
2015
+ });
2016
+ if (windows.length === 0 && amounts.length === 0) return void 0;
2017
+ return {
2018
+ amounts,
2019
+ windows,
2020
+ note: amounts.length === 0 ? "subscription usage windows; the credit balance is not readable with an API key" : void 0
2021
+ };
2022
+ }
2023
+ /**
1954
2024
  * Map one channel's raw bodies onto a reading, dispatching on the declared shape.
1955
2025
  * @param probe - the preset's declared probe.
1956
2026
  * @param bodies - response bodies in the order {@link balanceUrls} returned URLs.
@@ -1960,6 +2030,7 @@ function readBalance(probe, bodies) {
1960
2030
  switch (probe.shape) {
1961
2031
  case "deepseek": return parseDeepSeekBalance(bodies[0]);
1962
2032
  case "ollama": return parseOllamaBalance(bodies[0]);
2033
+ case "opencode-windows": return parseOpencodeWindows(bodies[0]);
1963
2034
  case "openai-billing": return parseOpenAIBilling(bodies[0], bodies[1]);
1964
2035
  }
1965
2036
  }
@@ -2200,11 +2271,29 @@ async function fetchBalance(channel, key, signal) {
2200
2271
  ...signal === void 0 ? {} : { signal }
2201
2272
  });
2202
2273
  const text = await response.text();
2203
- if (!response.ok) return {
2204
- status: "unavailable",
2205
- code: `HTTP_${response.status}`,
2206
- detail: `balance endpoint answered HTTP ${response.status}`
2207
- };
2274
+ if (!response.ok) {
2275
+ /**
2276
+ * 🔴 **404 means "this relay has no such route", not "the read failed".**
2277
+ *
2278
+ * The distinction decides what the user does next: `unsupported` says "this endpoint
2279
+ * does not publish a balance — stop looking", while `unavailable` says "it should have
2280
+ * but did not answer — try again". Reporting a 404 as a failure invites a retry loop
2281
+ * against a route that will never exist.
2282
+ *
2283
+ * Measured: Open Design answers 404 on every billing candidate while `/v1/models`
2284
+ * answers 401 (route exists, auth required) — that difference is how the two were told
2285
+ * apart without a key.
2286
+ */
2287
+ if (response.status === 404) return {
2288
+ status: "unsupported",
2289
+ detail: "this relay has no balance route (HTTP 404)"
2290
+ };
2291
+ return {
2292
+ status: "unavailable",
2293
+ code: `HTTP_${response.status}`,
2294
+ detail: `balance endpoint answered HTTP ${response.status}`
2295
+ };
2296
+ }
2208
2297
  try {
2209
2298
  bodies.push(JSON.parse(text));
2210
2299
  } catch {
package/lib/presets.d.ts CHANGED
@@ -32,23 +32,26 @@ export type ChannelModality = 'text' | 'image';
32
32
  * How to read one endpoint's remaining balance.
33
33
  *
34
34
  * Owner request (2026-10-23): 「其它渠道的查看方式你需要找到并实现它们的」. There is no single
35
- * answer — every provider invented its own surface, and some have none at all. This type is that
36
- * fact written down instead of guessed: a preset either names a known shape or says it has none.
37
- *
38
- * Three shapes were verified against real endpoints (see `balance.ts` for the response mapping):
39
- * · `deepseek` — DeepSeek's own `GET /user/balance`.
40
- * · `ollama` — `GET /api/balance` (note: under `/api`, NOT the `/v1` used for inference).
41
- * · `openai-billing` — the OpenAI-legacy billing pair that new-api / one-api relays clone:
35
+ * answer — every provider invented its own surface, and some report windows instead of money.
36
+ * This type is that fact written down instead of guessed: a preset either names a known shape or
37
+ * says it has none.
38
+ *
39
+ * Four shapes were verified against real endpoints (see `balance.ts` for the response mapping):
40
+ * · `deepseek` — DeepSeek's own `GET /user/balance`.
41
+ * · `ollama` — `GET /api/balance` (note: under `/api`, NOT the `/v1` used for inference).
42
+ * · `opencode-windows` — OpenCode Go's `GET /zen/go/v1/usage`: rolling/weekly/monthly WINDOWS.
43
+ * No currency amount — the credit wallet is not API-readable.
44
+ * · `openai-billing` — the OpenAI-legacy billing pair that new-api / one-api relays clone:
42
45
  * `GET /v1/dashboard/billing/subscription` + `GET /v1/dashboard/billing/usage`.
43
46
  *
44
- * 🔴 **A fourth answer is legitimate and must be representable: "this endpoint has no balance
45
- * API".** OpenCode Zen is exactly that case — its owner has an open feature request asking for
46
- * one, because today the only key-authenticated signal is the error thrown at $0
47
- * (anomalyco/opencode#44189). Pretending otherwise would mean showing a number nobody measured.
47
+ * 🔴 **"This endpoint has no balance API" must be representable, and is**: a preset simply omits
48
+ * `balance`. Open Design is a measured case (every billing candidate answers 404 while
49
+ * `/v1/models` answers 401, i.e. inference only). Pretending otherwise would mean showing a
50
+ * number nobody measured.
48
51
  */
49
52
  export interface BalanceProbe {
50
53
  /** Which response shape to read. */
51
- shape: 'deepseek' | 'ollama' | 'openai-billing';
54
+ shape: 'deepseek' | 'ollama' | 'opencode-windows' | 'openai-billing';
52
55
  /**
53
56
  * Overrides the base URL for the balance call.
54
57
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-any-deepseek",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "One DeepSeek V4.1 Flash provider for DeepSeek Harness: paste a key per channel, switch channels manually, token usage booked per real upstream.",
5
5
  "license": "MIT",
6
6
  "repository": {