dsh-opencode-routing 2.2.1 → 2.3.0
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/CONTRIBUTING.md +46 -0
- package/README.ja.md +111 -0
- package/README.ko.md +154 -0
- package/README.md +96 -73
- package/README.ru.md +166 -0
- package/README.zh-CN.md +141 -0
- package/README.zh-TW.md +111 -0
- package/client.js +1221 -1123
- package/package.json +11 -1
- package/test.mjs +17 -0
- package/usage.js +45 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-opencode-routing",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "dsh plugin: OpenCode account pool with automatic A-to-B failover and quota routing, plus a per-account / per-model usage dashboard, in one Settings page.",
|
|
6
6
|
"exports": {
|
|
@@ -16,6 +16,13 @@
|
|
|
16
16
|
"client.js",
|
|
17
17
|
"cordis.patch.yml",
|
|
18
18
|
"README.md",
|
|
19
|
+
"README.zh-CN.md",
|
|
20
|
+
"README.zh-TW.md",
|
|
21
|
+
"README.ja.md",
|
|
22
|
+
"README.ko.md",
|
|
23
|
+
"README.ru.md",
|
|
24
|
+
"CONTRIBUTING.md",
|
|
25
|
+
"LICENSE",
|
|
19
26
|
"test.mjs",
|
|
20
27
|
"locale/*.json",
|
|
21
28
|
"icon.svg"
|
|
@@ -42,6 +49,9 @@
|
|
|
42
49
|
"multi-account",
|
|
43
50
|
"quota",
|
|
44
51
|
"router",
|
|
52
|
+
"routing",
|
|
53
|
+
"failover",
|
|
54
|
+
"i18n",
|
|
45
55
|
"billing"
|
|
46
56
|
],
|
|
47
57
|
"repository": {
|
package/test.mjs
CHANGED
|
@@ -357,6 +357,23 @@ await sleep(300)
|
|
|
357
357
|
assert.equal(freeRow.remaining, undefined, 'free models have no remaining quota')
|
|
358
358
|
assert.equal(freeRow.inputs.includes('video'), true)
|
|
359
359
|
assert.equal(data.peak.plan.leverage, 10)
|
|
360
|
+
|
|
361
|
+
// Every model breaks down into the three lines DeepSeek bills on, cheapest first
|
|
362
|
+
const peakFlash = data.peak.models.find((entry) => entry.model === 'deepseek-v4.1-flash')
|
|
363
|
+
assert.deepEqual(peakFlash.rows.map((line) => line.kind), ['cacheRead', 'input', 'output'])
|
|
364
|
+
assert.equal(peakFlash.rows[0].cheaper, 'go', 'the cached line is still cheaper on OpenCode')
|
|
365
|
+
assert.equal(Math.round(peakFlash.rows[0].ratio * 100) / 100, 0.1)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// A plan change is a config edit, not a code change: the discount is derived
|
|
369
|
+
{
|
|
370
|
+
const offPeak = Date.UTC(2026, 10, 3, 16, 0)
|
|
371
|
+
const cheaper = peakSnapshot(7.1, offPeak, 'go', { priceUsd: 10, allowanceUsd: 50 })
|
|
372
|
+
assert.equal(cheaper.plan.leverage, 5, 'a smaller allowance halves the discount')
|
|
373
|
+
assert.equal(cheaper.plan.overridden, true)
|
|
374
|
+
const dearer = peakSnapshot(7.1, offPeak, 'go', { priceUsd: 50, allowanceUsd: 100 })
|
|
375
|
+
assert.equal(dearer.plan.leverage, 2)
|
|
376
|
+
assert.equal(dearer.models[0].rows[0].cheaper, 'go', 'still cheaper, just less so')
|
|
360
377
|
}
|
|
361
378
|
console.log('usage view ok')
|
|
362
379
|
|
package/usage.js
CHANGED
|
@@ -230,6 +230,11 @@ function normalize(config = {}) {
|
|
|
230
230
|
// our own schedule, not DeepSeek's holiday calendar"), so this stays off by
|
|
231
231
|
// default — the dashboard is about the Go bill.
|
|
232
232
|
holidayAware: config.holidayAware === true,
|
|
233
|
+
// The subscription price and its monthly allowance can be overridden here,
|
|
234
|
+
// so a plan change from OpenCode needs a config edit, not a patch: the
|
|
235
|
+
// discount shown on the page is derived from these two numbers.
|
|
236
|
+
planPriceUsd: Number.isFinite(config.planPriceUsd) && config.planPriceUsd > 0 ? config.planPriceUsd : undefined,
|
|
237
|
+
planAllowanceUsd: Number.isFinite(config.planAllowanceUsd) && config.planAllowanceUsd > 0 ? config.planAllowanceUsd : undefined,
|
|
233
238
|
limits: LIMITS[plan],
|
|
234
239
|
}
|
|
235
240
|
}
|
|
@@ -292,40 +297,60 @@ export function peakMultiplier(model, at, holidayAware = false) {
|
|
|
292
297
|
* discount — Go pays $10 for $100 of list-priced usage, i.e. 10x. */
|
|
293
298
|
const PLAN_PRICES = { go: 10, 'go-plus': 40, 'go-teams': 20 }
|
|
294
299
|
|
|
300
|
+
/** The three lines DeepSeek bills on, in the order the page shows them: a cached
|
|
301
|
+
* prompt token is by far the cheapest, so it leads. */
|
|
302
|
+
export const BILLING_KINDS = ['cacheRead', 'input', 'output']
|
|
303
|
+
|
|
295
304
|
/** Which route is cheaper at this instant, expressed in the money you actually
|
|
296
|
-
* hand over:
|
|
297
|
-
* DeepSeek's API bills the list price directly.
|
|
298
|
-
|
|
305
|
+
* hand over: OpenCode charges the list price against a discounted allowance,
|
|
306
|
+
* while DeepSeek's API bills the list price directly.
|
|
307
|
+
*
|
|
308
|
+
* `overrides` lets the settings page (or a future price change) replace the
|
|
309
|
+
* plan price and its allowance without touching this file: the discount is
|
|
310
|
+
* derived, so editing either number re-derives the verdict on its own. */
|
|
311
|
+
export function peakSnapshot(cnyPerUsd, at = Date.now(), plan = 'go', overrides = {}) {
|
|
299
312
|
const fx = Number.isFinite(cnyPerUsd) && cnyPerUsd > 0 ? cnyPerUsd : OFFICIAL.defaultCnyPerUsd
|
|
300
|
-
const priceUsd =
|
|
301
|
-
|
|
313
|
+
const priceUsd = Number.isFinite(overrides.priceUsd) && overrides.priceUsd > 0
|
|
314
|
+
? overrides.priceUsd
|
|
315
|
+
: PLAN_PRICES[plan] ?? PLAN_PRICES.go
|
|
316
|
+
const listed = LIMITS[plan]?.['deepseek-v4.1-flash'] ?? LIMITS.go['deepseek-v4.1-flash']
|
|
317
|
+
const allowanceUsd = Number.isFinite(overrides.allowanceUsd) && overrides.allowanceUsd > 0
|
|
318
|
+
? overrides.allowanceUsd
|
|
319
|
+
: listed
|
|
302
320
|
const leverage = allowanceUsd / priceUsd
|
|
303
321
|
const routes = []
|
|
304
322
|
for (const [model, official] of Object.entries(OFFICIAL.prices)) {
|
|
305
323
|
const goFactor = peakMultiplier(model, at, false)
|
|
306
324
|
const officialFactor = peakMultiplier(model, at, true)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
325
|
+
const rows = BILLING_KINDS.map((kind) => {
|
|
326
|
+
// One million tokens of this kind is the yardstick; output dominates a
|
|
327
|
+
// coding bill, so the headline verdict still reads off the output line.
|
|
328
|
+
const list = official[kind]
|
|
329
|
+
const burn = list * goFactor // what the request subtracts from the allowance
|
|
330
|
+
const goPay = burn / leverage // what it actually costs you, given the discount
|
|
331
|
+
const officialPay = list * officialFactor
|
|
332
|
+
return { kind, list, go: { factor: goFactor, burn, pay: goPay }, official: { factor: officialFactor, pay: officialPay }, cheaper: goPay <= officialPay ? 'go' : 'official', ratio: goPay / officialPay }
|
|
333
|
+
})
|
|
334
|
+
const headline = rows[rows.length - 1]
|
|
312
335
|
routes.push({
|
|
313
336
|
model,
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
337
|
+
rows,
|
|
338
|
+
list: headline.list,
|
|
339
|
+
go: headline.go,
|
|
340
|
+
official: headline.official,
|
|
341
|
+
cheaper: headline.cheaper,
|
|
342
|
+
ratio: headline.ratio,
|
|
319
343
|
})
|
|
320
344
|
}
|
|
321
345
|
return {
|
|
322
346
|
at,
|
|
323
347
|
cnyPerUsd: fx,
|
|
324
348
|
holiday: isChinaHoliday(at),
|
|
325
|
-
plan: { id: plan, priceUsd, priceCny: priceUsd * fx, allowanceUsd, allowanceCny: allowanceUsd * fx, leverage },
|
|
349
|
+
plan: { id: plan, priceUsd, priceCny: priceUsd * fx, allowanceUsd, allowanceCny: allowanceUsd * fx, leverage, overridden: Boolean(overrides.priceUsd || overrides.allowanceUsd) },
|
|
326
350
|
go: { peak: peakMultiplier('deepseek-v4.1-flash', at, false) === PEAK.factor },
|
|
327
351
|
official: { peak: peakMultiplier('deepseek-v4.1-flash', at, true) === PEAK.factor },
|
|
328
352
|
factor: PEAK.factor,
|
|
353
|
+
kinds: BILLING_KINDS,
|
|
329
354
|
models: routes,
|
|
330
355
|
}
|
|
331
356
|
}
|
|
@@ -659,7 +684,10 @@ export function apply(ctx, config = {}) {
|
|
|
659
684
|
startedAt,
|
|
660
685
|
windowStarts: starts,
|
|
661
686
|
holidayAware: cfg.holidayAware,
|
|
662
|
-
peak: peakSnapshot(cfg.cnyPerUsd, Date.now(), detectedPlan ?? cfg.plan
|
|
687
|
+
peak: peakSnapshot(cfg.cnyPerUsd, Date.now(), detectedPlan ?? cfg.plan, {
|
|
688
|
+
priceUsd: cfg.planPriceUsd,
|
|
689
|
+
allowanceUsd: cfg.planAllowanceUsd,
|
|
690
|
+
}),
|
|
663
691
|
usage: usageSnapshot,
|
|
664
692
|
usageError,
|
|
665
693
|
accounts,
|