@magpie-community/opencode-workbuddy-auth 0.2.10 → 0.2.11

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 (3) hide show
  1. package/README.md +11 -0
  2. package/index.mjs +71 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -344,6 +344,17 @@ client reads. The daily Free Plan slice isn't counted: it ends at the same
344
344
  midnight on every account. magpie's smart routing spends the account whose
345
345
  credits run out first.
346
346
 
347
+ The plan's own credits and the rest are two windows. "Credits" is the
348
+ packages whose credits come back each cycle, as WorkBuddy's client lists
349
+ them (Pro monthly or yearly, youth, advanced, flagship, the plan's bonus,
350
+ the free plan's monthly base, the Pro trials, or the subscription the
351
+ summary names), with the plan's cycle, so its pace line is the month's.
352
+ "Extra credits" is the packages that only lapse: gifted and activity
353
+ credits (赠送积分) and bought credit packs. Only one of the two stops the
354
+ account, since WorkBuddy serves it while any credits are left: the extra
355
+ window is set aside until the plan's credits are spent. A summary with
356
+ only one kind keeps the one "Credits" window (akic404 on Discord).
357
+
347
358
  An enterprise (team) account's resource summary lists no packages. Its
348
359
  credits are read from the enterprise meter
349
360
  (`/billing/meter/get-enterprise-user-usage`), the one WorkBuddy's own usage
package/index.mjs CHANGED
@@ -2776,20 +2776,49 @@ function pinned(rows, account) {
2776
2776
  // CycleRemainCapacity at all leaves the window out. TotalCredits is not a
2777
2777
  // fallback: an account with no packages at all keeps its unknown allowance,
2778
2778
  // as before, so its free models are not skipped.
2779
- function usageOf(sum, plan) {
2779
+ //
2780
+ // The plan's own credits and the rest are two windows (akic404 on
2781
+ // Discord): summed, the gifted and activity packages' credits used long
2782
+ // ago put the card at most of its allowance on the first day of the
2783
+ // month, and the plan's pace line said nothing. "Credits" is the packages
2784
+ // whose credits come back each cycle — WorkBuddy's client's own
2785
+ // onlyRefreshCodes (PLAN_CODES), or the subscription the summary names —
2786
+ // and "Extra credits" the ones that only lapse (gift, activity, bought
2787
+ // credit packs, any code the client doesn't list). A summary with only
2788
+ // one kind keeps the one "Credits" window, as before.
2789
+ //
2790
+ // Only one of the two stops the account at a time: WorkBuddy serves it
2791
+ // while any credits are left, so the one set aside is the one whose
2792
+ // running out doesn't stop it — "Extra credits", until the plan's are
2793
+ // spent and extra ones are left.
2794
+ function usageOf(sum, plan, site) {
2780
2795
  const out = { plan: plan || (sum?.IsPaidUser ? "Pro" : "Free plan"), windows: [] }
2781
- let total = 0, used = 0, left = 0, bags = 0
2796
+ const ofPlan = planCodes(sum, site)
2797
+ const kinds = { plan: { total: 0, used: 0 }, extra: { total: 0, used: 0 } }
2798
+ let left = 0, bags = 0
2782
2799
  for (const p of sum?.Packages ?? []) {
2783
2800
  const hasTotal = p?.CycleTotalCapacity != null && p.CycleTotalCapacity !== ""
2784
2801
  if (hasTotal) {
2785
- total += capacity(p.CycleTotalCapacity)
2786
- used += capacity(p?.CycleUsedCapacity)
2802
+ const k = kinds[ofPlan(p?.PackageCode) ? "plan" : "extra"]
2803
+ k.total += capacity(p.CycleTotalCapacity)
2804
+ k.used += capacity(p?.CycleUsedCapacity)
2787
2805
  } else if (p?.CycleRemainCapacity != null && p.CycleRemainCapacity !== "") {
2788
2806
  left += capacity(p.CycleRemainCapacity)
2789
2807
  bags++
2790
2808
  }
2791
2809
  }
2792
- if (total > 0) out.windows.push({ name: "Credits", used: (100 * used) / total, display: `${compact(used)} / ${compact(total)}`, amount: used, limit: total, unit: "credits" })
2810
+ const window = (name, { used, total }) => ({ name, used: (100 * used) / total, display: `${compact(used)} / ${compact(total)}`, amount: used, limit: total, unit: "credits" })
2811
+ const { plan: mine, extra } = kinds
2812
+ if (mine.total > 0 && extra.total > 0) {
2813
+ const credits = window("Credits", mine), more = window("Extra credits", extra)
2814
+ if (mine.used >= mine.total && extra.used < extra.total) credits.aside = true
2815
+ else more.aside = true
2816
+ out.windows.push(credits, more)
2817
+ } else {
2818
+ const total = mine.total + extra.total, used = mine.used + extra.used
2819
+ if (total > 0) out.windows.push(window("Credits", { used, total }))
2820
+ }
2821
+ const total = mine.total + extra.total
2793
2822
  if (bags > 0 && (total === 0 || left > 0)) out.windows.push({ name: "Prepaid credits", balance: left, display: `${compact(left)} credits`, unit: "credits" })
2794
2823
  return out
2795
2824
  }
@@ -2845,6 +2874,26 @@ const FREE_CODES = [DAILY_CODE, CODE("008", "cfWoLwvjU4"), CODE("035", "ArVxJcGD
2845
2874
  // lost at the same midnight on every account, so they tell none apart
2846
2875
  const SLICE = "4"
2847
2876
 
2877
+ // PLAN_CODES are the packages whose credits come back each cycle, as
2878
+ // WorkBuddy's client (5.9.x) lists them (onlyRefreshCodes): Pro monthly,
2879
+ // Pro monthly plus, Pro yearly, youth, advanced, flagship, the plan's
2880
+ // bonus (bonus28; bonusIntl, "Plan Bonus Quota"), the international free
2881
+ // plan (freeMonIntl), the Pro trials, and on the China build alone the
2882
+ // free plan's monthly base (freeMon, 套餐基础积分). Every other code only
2883
+ // lapses: gift (006, "Bonus Pack" / Pro trial), activity (007, 赠送积分),
2884
+ // bonus29/30, and the credit packs (009, 036, 038).
2885
+ const PLAN_CODES = [CODE("002", "AkiJS3ZHF5"), CODE("005", "maRGyrHhw1"), CODE("003", "FAnt7lcmRT"), CODE("023", "4xbGhMrE6q"),
2886
+ CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("028", "NtpWi0jzXs"), CODE("035", "ArVxJcGDsm"),
2887
+ CODE("039", "KRcQj7wUat"), CODE("040", "mi9rCYg46x"), CODE("037", "WxOD3MpI2o")]
2888
+ const FREE_MON = CODE("008", "cfWoLwvjU4")
2889
+
2890
+ // planCodes says whether a package of the summary is the plan's own: one
2891
+ // of PLAN_CODES on that site, or the subscription the summary names.
2892
+ function planCodes(sum, site) {
2893
+ const sub = sum?.SubscriptionPackageCode
2894
+ return (code) => !!code && (code === sub || PLAN_CODES.includes(code) || (!site?.ai && code === FREE_MON))
2895
+ }
2896
+
2848
2897
  // beijing is WorkBuddy's own time ("2026-10-13 00:39:27" is UTC+8, as its
2849
2898
  // DeductionEndTime for the same package says).
2850
2899
  const beijing = (ms) => new Date(ms + 8 * 3600_000).toISOString().slice(0, 19).replace("T", " ")
@@ -2887,11 +2936,12 @@ function endOf(p) {
2887
2936
  // packages are lost, and how many (cdredfox on X): smart routing spends
2888
2937
  // the account whose credits expire first. Only packages the summary
2889
2938
  // counts, with credits left, and not the daily slice; null when none.
2890
- function runsOutOf(sum, packages, now = Date.now()) {
2939
+ // only narrows it to the packages of one window (the split's, usageOf).
2940
+ function runsOutOf(sum, packages, now = Date.now(), only = () => true) {
2891
2941
  const counted = new Set((sum?.Packages ?? []).map((p) => p?.PackageCode))
2892
2942
  let at = 0, amount = 0
2893
2943
  for (const p of packages ?? []) {
2894
- if (!counted.has(p?.PackageCode) || p?.PackageCode === DAILY_CODE || String(p?.CapacityType) === SLICE) continue
2944
+ if (!counted.has(p?.PackageCode) || !only(p?.PackageCode) || p?.PackageCode === DAILY_CODE || String(p?.CapacityType) === SLICE) continue
2895
2945
  const left = capacity(p?.CycleCapacityRemainPrecise ?? p?.CycleCapacityRemain)
2896
2946
  const end = endOf(p)
2897
2947
  if (!(left > 0) || end <= now) continue
@@ -3626,7 +3676,7 @@ function makePlugin(site) {
3626
3676
  }
3627
3677
  said = "renewed"
3628
3678
  }
3629
- const out = usageOf(sum, auth.plan)
3679
+ const out = usageOf(sum, auth.plan, site)
3630
3680
  // an enterprise account's summary has no packages: its credits
3631
3681
  // are the enterprise meter's (#1904). A personal account's
3632
3682
  // refusal of it (10001 "uid or enterpriseID is empty") leaves
@@ -3641,11 +3691,22 @@ function makePlugin(site) {
3641
3691
  // when the plan's credits start again (cycleOf), and when the credits
3642
3692
  // left run out, for routing to spend them first (runsOutOf);
3643
3693
  // a list that can't be read leaves it unknown, the card stands
3694
+ //
3695
+ // Split (usageOf), the plan's cycle is the plan window's alone;
3696
+ // the soonest credits to lapse, of any package, stay on the window
3697
+ // that stops the account, as routing reads none set aside, and
3698
+ // the window set aside says its own.
3644
3699
  const credits = out.windows.find((w) => w.name === "Credits")
3700
+ const extra = out.windows.find((w) => w.name === "Extra credits")
3645
3701
  if (credits) {
3646
3702
  try {
3647
3703
  const packages = await packagesOf(site, a)
3648
- Object.assign(credits, cycleOf(sum, packages) ?? {}, runsOutOf(sum, packages) ?? {})
3704
+ Object.assign(credits, cycleOf(sum, packages) ?? {})
3705
+ const ofPlan = planCodes(sum, site)
3706
+ const stops = extra && !extra.aside ? extra : credits
3707
+ Object.assign(stops, runsOutOf(sum, packages) ?? {})
3708
+ const aside = extra ? (stops === credits ? extra : credits) : null
3709
+ if (aside) Object.assign(aside, runsOutOf(sum, packages, Date.now(), (code) => ofPlan(code) === (aside === credits)) ?? {})
3649
3710
  } catch {}
3650
3711
  }
3651
3712
  await spareFree(site, a, out.windows)
@@ -3801,4 +3862,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
3801
3862
  export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
3802
3863
 
3803
3864
  // for tests
3804
- export const _internal = { idFor, OFF_TAKEN, replyAfterToolsOn, withDefaultEffort, defaultEffortOf, gzipped, FREE_ONLY, PRICE_TTL, keyAccount, remoteConfig, cycleOf, typeUnions, systemAsUser, systemKey, DEFAULT_SYSTEM, EDGE_READS, fitShape, replyAfterTools, AFTER_TOOLS_REFUSED, enterpriseUsageOf, accountName, notDesktop, textOnly, runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, asReasoning, normalizeToolChoice, fitTools, fitted, renamed, unsaid, FILTER_SAYS, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, RESTRICTED_HINT, EXHAUSTED_HINT, REFUSED_DOC, commaPaused, commaPausedOn, opened, freeCredits, creditsOf, priceOf, fresh, SITES, renewing, renewed, SESSION, attend, scopeOf, turnKey, conversationKey, signatureOf, isMessages, withoutUnsigned, withoutSignedThinking, prepared, answeredBefore, nativeOf, modelOf, configModels, freeWindowOf, tokensShort, freeWindowRows, freeWindowLedger, readLedgerTail, pinned, rowTime, rowAccount, ofAccount, windowOffset, windowReset, FREE_WINDOW_MODELS, FREE_WINDOW_SPAN, FREE_WINDOW_GAP, FREE_WINDOW_LEAD, checkinOf, API_MESSAGES: NPM_MESSAGES, API_CHAT: NPM }
3865
+ export const _internal = { planCodes, idFor, OFF_TAKEN, replyAfterToolsOn, withDefaultEffort, defaultEffortOf, gzipped, FREE_ONLY, PRICE_TTL, keyAccount, remoteConfig, cycleOf, typeUnions, systemAsUser, systemKey, DEFAULT_SYSTEM, EDGE_READS, fitShape, replyAfterTools, AFTER_TOOLS_REFUSED, enterpriseUsageOf, accountName, notDesktop, textOnly, runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, asReasoning, normalizeToolChoice, fitTools, fitted, renamed, unsaid, FILTER_SAYS, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, RESTRICTED_HINT, EXHAUSTED_HINT, REFUSED_DOC, commaPaused, commaPausedOn, opened, freeCredits, creditsOf, priceOf, fresh, SITES, renewing, renewed, SESSION, attend, scopeOf, turnKey, conversationKey, signatureOf, isMessages, withoutUnsigned, withoutSignedThinking, prepared, answeredBefore, nativeOf, modelOf, configModels, freeWindowOf, tokensShort, freeWindowRows, freeWindowLedger, readLedgerTail, pinned, rowTime, rowAccount, ofAccount, windowOffset, windowReset, FREE_WINDOW_MODELS, FREE_WINDOW_SPAN, FREE_WINDOW_GAP, FREE_WINDOW_LEAD, checkinOf, API_MESSAGES: NPM_MESSAGES, API_CHAT: NPM }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-workbuddy-auth",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "OpenCode provider plugins for WorkBuddy's plans: WorkBuddy (CodeBuddy, copilot.tencent.com) and WorkBuddy AI (www.workbuddy.ai)",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",