@magpie-community/opencode-qoder-auth 0.2.0 → 0.2.2

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.
Files changed (2) hide show
  1. package/index.mjs +49 -6
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -291,13 +291,51 @@ const CN_MODELS = [
291
291
  { id: "lite", name: "Lite" },
292
292
  ]
293
293
 
294
- // freeOf reads whether the listing marks a model free, as Qoder's client
295
- // reads it (internal/qoder/models.go): is_free true, or a price_factor (the
296
- // credits a request costs, as a multiple) of 0, either in snake or camel case.
294
+ // freeOf reads whether a model costs the plan no credits: a price_factor
295
+ // (the credits a request costs, as a multiple; priceFactor in camel case)
296
+ // of 0, the price Qoder's own client shows ("0×"). is_free is no word on
297
+ // that: Qoder's listing has it true on Qwen3.8-Max at 0.5×, an off-peak
298
+ // discount (错峰 4 折) on it, and Qoder's client shows the price, not it.
299
+ // It is taken only from a listing with no price at all. A price of 0 for
300
+ // the while an active promotion lasts, with a price before it, is a
301
+ // discount, not a free model; a limited-time free model (Qwen3.8-Flash:
302
+ // 0×, its original_price_factor 0.1 struck through) is free while it is.
297
303
  function freeOf(raw) {
298
- const is = raw.is_free ?? raw.isFree
299
304
  const price = raw.price_factor ?? raw.priceFactor
300
- return is === true || (typeof price === "number" && price === 0)
305
+ if (typeof price === "number" && Number.isFinite(price)) {
306
+ if (price !== 0) return false
307
+ const p = raw.promotion ?? raw.prommotion
308
+ const before = p?.before_promotion_price_factor ?? p?.beforePromotionPriceFactor
309
+ return !(p?.active === true && typeof before === "number" && before > 0)
310
+ }
311
+ return (raw.is_free ?? raw.isFree) === true
312
+ }
313
+
314
+ // rateOf reads a model's price as Qoder's client shows it beside the model
315
+ // (magpie shows it in its lists, so the cheap ones can be picked without
316
+ // opening Qoder): rate, the price_factor (0.5×), and rateWas, the price
317
+ // struck through beside it while a discount runs: an active promotion's
318
+ // before_promotion_price_factor, else an original_price_factor above the
319
+ // price (Qwen3.8-Flash: 0×, 0.1× struck through). A promotion's 0 that
320
+ // isn't free is its price before times its discount_factor. A listing with
321
+ // no price gives none (0). The same rule as magpie's built-in
322
+ // (internal/qoder/models.go, ModelInfo.free).
323
+ function rateOf(raw) {
324
+ const price = raw.price_factor ?? raw.priceFactor
325
+ if (typeof price !== "number" || !Number.isFinite(price)) return { rate: 0, rateWas: 0 }
326
+ const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : 0)
327
+ const p = raw.promotion ?? raw.prommotion
328
+ const before = p?.before_promotion_price_factor ?? p?.beforePromotionPriceFactor
329
+ const discount = num(p?.discount_factor) || num(p?.discountFactor)
330
+ const discounted = p?.active === true && typeof before === "number" && before > 0
331
+ let rate = Math.max(price, 0)
332
+ let rateWas = Math.max(num(raw.original_price_factor), num(raw.originalPriceFactor))
333
+ if (discounted) {
334
+ rateWas = before
335
+ if (rate === 0) rate = before * discount
336
+ }
337
+ if (rateWas <= rate) rateWas = 0
338
+ return { rate, rateWas }
301
339
  }
302
340
 
303
341
  // modelInfo reads one entry of the listing's "chat" array: its efforts and
@@ -314,6 +352,7 @@ function modelInfo(raw) {
314
352
  efforts: Array.isArray(raw.reasoning_efforts) ? [...raw.reasoning_efforts] : [],
315
353
  defaultEffort: "",
316
354
  free: freeOf(raw),
355
+ ...rateOf(raw),
317
356
  config: raw,
318
357
  }
319
358
  const tc = raw.thinking_config
@@ -383,6 +422,10 @@ function runtimeModel(site, m) {
383
422
  variants: Object.fromEntries((m.efforts ?? []).map((e) => [e, { reasoningEffort: e }])),
384
423
  // magpie's own: served at no cost to the plan's credits
385
424
  free: !!m.free,
425
+ // magpie's own: the credits a request costs, as a multiple, and the
426
+ // price before a discount running now (0: none)
427
+ rate: m.rate ?? 0,
428
+ rateWas: m.rateWas ?? 0,
386
429
  }
387
430
  }
388
431
 
@@ -1132,7 +1175,7 @@ const makePlugin = (site) => async ({ client }) => {
1132
1175
  const ms = await models(cred, true)
1133
1176
  if (!ms.length) return provider.models
1134
1177
  return Object.fromEntries(
1135
- ms.map((m) => [m.key, runtimeModel(site, { id: m.key, name: m.name, context: m.context, images: m.images, efforts: m.thinks ? m.efforts : [], free: m.free })]),
1178
+ ms.map((m) => [m.key, runtimeModel(site, { id: m.key, name: m.name, context: m.context, images: m.images, efforts: m.thinks ? m.efforts : [], free: m.free, rate: m.rate, rateWas: m.rateWas })]),
1136
1179
  )
1137
1180
  } catch {
1138
1181
  return provider.models
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-qoder-auth",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Qoder subscriptions in OpenCode and magpie: Qoder (qoder.com) and Qoder CN (qoder.cn)",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",