@magpie-community/opencode-workbuddy-auth 0.2.15 → 0.2.17

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 -7
  2. package/index.mjs +105 -36
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -349,15 +349,19 @@ midnight on every account. magpie's smart routing spends the account whose
349
349
  credits run out first.
350
350
 
351
351
  The plan's own credits and the rest are two windows. "Credits" is the
352
- packages whose credits come back each cycle, as WorkBuddy's client lists
353
- them (Pro monthly or yearly, youth, advanced, flagship, the plan's bonus,
354
- the free plan's monthly base, the Pro trials, or the subscription the
355
- summary names), with the plan's cycle, so its pace line is the month's.
356
- "Extra credits" is the packages that only lapse: gifted and activity
357
- credits (赠送积分) and bought credit packs. Only one of the two stops the
352
+ plan's base credits: what the subscription grants each cycle (Pro monthly
353
+ or yearly, youth, advanced, flagship, the Pro trials, the free plan's
354
+ monthly base, or the subscription the summary names), with the plan's
355
+ cycle, so its pace line is the month's. "Extra credits" is every other
356
+ kind WorkBuddy's own usage panel lists: the plan's bonus, top-ups (bought
357
+ credit packs) and platform rewards (gifted and activity credits,
358
+ 赠送积分). The summary's CreditGroups say which is which (plan_base
359
+ against plan_bonus, addon and benefit); a summary without them is split
360
+ by package code as WorkBuddy's client does. Only one of the two stops the
358
361
  account, since WorkBuddy serves it while any credits are left: the extra
359
362
  window is set aside until the plan's credits are spent. A summary with
360
- only one kind keeps the one "Credits" window (akic404 on Discord).
363
+ only one kind keeps the one "Credits" window (akic404 on Discord; since
364
+ 0.2.16 the plan's bonus is extra too, Jiabubu on Discord).
361
365
 
362
366
  An enterprise (team) account's resource summary lists no packages. Its
363
367
  credits are read from the enterprise meter
package/index.mjs CHANGED
@@ -2868,12 +2868,15 @@ function pinning() {
2868
2868
  // The plan's own credits and the rest are two windows (akic404 on
2869
2869
  // Discord): summed, the gifted and activity packages' credits used long
2870
2870
  // ago put the card at most of its allowance on the first day of the
2871
- // month, and the plan's pace line said nothing. "Credits" is the packages
2872
- // whose credits come back each cycle — WorkBuddy's client's own
2873
- // onlyRefreshCodes (PLAN_CODES), or the subscription the summary names —
2874
- // and "Extra credits" the ones that only lapse (gift, activity, bought
2875
- // credit packs, any code the client doesn't list). A summary with only
2876
- // one kind keeps the one "Credits" window, as before.
2871
+ // month, and the plan's pace line said nothing. "Credits" is the plan's
2872
+ // base credits and "Extra credits" every other kind, as WorkBuddy's own
2873
+ // usage panel breaks them down (Jiabubu on Discord: a plan's bonus and
2874
+ // bought credits were nowhere on the card). The summary's CreditGroups
2875
+ // say it in WorkBuddy's words: plan_base ("Plan Base Credits") is the
2876
+ // plan's, plan_bonus, addon ("Top-up Credits") and benefit ("Platform
2877
+ // Reward Credits") are extra. A summary without them is split by code as
2878
+ // the client's resolveCreditGroup does (PLAN_CODES, see planCodes). A
2879
+ // summary with only one kind keeps the one "Credits" window, as before.
2877
2880
  //
2878
2881
  // Only one of the two stops the account at a time: WorkBuddy serves it
2879
2882
  // while any credits are left, so the one set aside is the one whose
@@ -2881,36 +2884,79 @@ function pinning() {
2881
2884
  // spent and extra ones are left.
2882
2885
  function usageOf(sum, plan, site) {
2883
2886
  const out = { plan: plan || (sum?.IsPaidUser ? "Pro" : "Free plan"), windows: [] }
2884
- const ofPlan = planCodes(sum, site)
2885
- const kinds = { plan: { total: 0, used: 0 }, extra: { total: 0, used: 0 } }
2886
- let left = 0, bags = 0
2887
- for (const p of sum?.Packages ?? []) {
2888
- const hasTotal = p?.CycleTotalCapacity != null && p.CycleTotalCapacity !== ""
2889
- if (hasTotal) {
2890
- const k = kinds[ofPlan(p?.PackageCode) ? "plan" : "extra"]
2891
- k.total += capacity(p.CycleTotalCapacity)
2892
- k.used += capacity(p?.CycleUsedCapacity)
2893
- } else if (p?.CycleRemainCapacity != null && p.CycleRemainCapacity !== "") {
2894
- left += capacity(p.CycleRemainCapacity)
2895
- bags++
2896
- }
2897
- }
2898
- const window = (name, { used, total }) => ({ name, used: (100 * used) / total, display: `${compact(used)} / ${compact(total)}`, amount: used, limit: total, unit: "credits" })
2899
- const { plan: mine, extra } = kinds
2887
+ const { plan: mine, extra, left, bags } = kindsOf(sum, site)
2900
2888
  if (mine.total > 0 && extra.total > 0) {
2901
- const credits = window("Credits", mine), more = window("Extra credits", extra)
2889
+ const credits = creditWindow("Credits", mine), more = creditWindow("Extra credits", extra)
2902
2890
  if (mine.used >= mine.total && extra.used < extra.total) credits.aside = true
2903
2891
  else more.aside = true
2904
2892
  out.windows.push(credits, more)
2905
2893
  } else {
2906
2894
  const total = mine.total + extra.total, used = mine.used + extra.used
2907
- if (total > 0) out.windows.push(window("Credits", { used, total }))
2895
+ if (total > 0) out.windows.push(creditWindow("Credits", { used, total }))
2908
2896
  }
2909
2897
  const total = mine.total + extra.total
2910
2898
  if (bags > 0 && (total === 0 || left > 0)) out.windows.push({ name: "Prepaid credits", balance: left, display: `${compact(left)} credits`, unit: "credits" })
2911
2899
  return out
2912
2900
  }
2913
2901
 
2902
+ // creditWindow is a window of credits used of a total.
2903
+ const creditWindow = (name, { used, total }) => ({ name, used: (100 * used) / total, display: `${compact(used)} / ${compact(total)}`, amount: used, limit: total, unit: "credits" })
2904
+
2905
+ // kindsOf is the summary's credits, the plan's and the extra ones (see
2906
+ // usageOf), and what is left in its packages with no total (a prepaid
2907
+ // balance).
2908
+ function kindsOf(sum, site) {
2909
+ const ofPlan = planCodes(sum, site)
2910
+ const kinds = { plan: { total: 0, used: 0 }, extra: { total: 0, used: 0 }, left: 0, bags: 0 }
2911
+ const hasTotal = (p) => p?.CycleTotalCapacity != null && p.CycleTotalCapacity !== ""
2912
+ const add = (k, p) => ((k.total += capacity(p.CycleTotalCapacity)), (k.used += capacity(p?.CycleUsedCapacity)))
2913
+ const groups = (Array.isArray(sum?.CreditGroups) ? sum.CreditGroups : []).filter(hasTotal)
2914
+ for (const g of groups) add(kinds[g?.Kind === "plan_base" ? "plan" : "extra"], g)
2915
+ for (const p of sum?.Packages ?? []) {
2916
+ if (hasTotal(p)) {
2917
+ if (!groups.length) add(kinds[ofPlan(p?.PackageCode) ? "plan" : "extra"], p)
2918
+ } else if (p?.CycleRemainCapacity != null && p.CycleRemainCapacity !== "") {
2919
+ kinds.left += capacity(p.CycleRemainCapacity)
2920
+ kinds.bags++
2921
+ }
2922
+ }
2923
+ return kinds
2924
+ }
2925
+
2926
+ // listedExtra is the extra credits of the detail lists (packagesOf) that
2927
+ // the summary doesn't list at all, as WorkBuddy's own usage panel counts
2928
+ // them (jiabubu on Discord, magpie 0.1.2774, a China-site free account:
2929
+ // magpie's card read "0 / 500" alone, while the panel shows the extra
2930
+ // kinds beside the plan's). The panel's groups come from the detail
2931
+ // lists, not the summary (buildCreditGroups over fetchUserResources' paid
2932
+ // and free lists), and leave out a package past its end; a slice package
2933
+ // (CapacityType 4) counts the day's slice, as resolveResourceCapacity
2934
+ // does. null when there are none with credits.
2935
+ function listedExtra(sum, packages, site, now = Date.now()) {
2936
+ const summed = new Set((sum?.Packages ?? []).map((p) => p?.PackageCode))
2937
+ const codes = site?.ai ? [...EXTRA_CODES, ...EXTRA_AI] : EXTRA_CODES
2938
+ let total = 0, left = 0, at = 0, lapsing = 0
2939
+ for (const p of packages ?? []) {
2940
+ const code = p?.PackageCode
2941
+ if (!codes.includes(code) || summed.has(code)) continue
2942
+ const end = endOf(p)
2943
+ if (end > 0 && end <= now) continue
2944
+ const slice = String(p?.CapacityType) === SLICE ? p?.SlicePeriodUsageDetails?.[0] : null
2945
+ const of = (v) => { const n = Number(v); return Number.isFinite(n) && n > 0 ? n : 0 }
2946
+ const t = of(slice ? slice.SlicePeriodCapacitySizePrecise : p?.CycleCapacitySizePrecise)
2947
+ const l = Math.min(t, of(slice ? slice.SlicePeriodCapacityRemainPrecise : p?.CycleCapacityRemainPrecise))
2948
+ if (!(t > 0)) continue
2949
+ total += t
2950
+ left += l
2951
+ if (l > 0 && end > 0 && !slice) {
2952
+ if (!at || end < at) at = end, lapsing = l
2953
+ else if (end === at) lapsing += l
2954
+ }
2955
+ }
2956
+ if (!(total > 0)) return null
2957
+ return { total, used: total - left, ...(at ? { runsOutAt: new Date(at).toISOString(), runsOut: lapsing } : {}) }
2958
+ }
2959
+
2914
2960
  // enterpriseUsageOf is an enterprise (team) account's credits, from
2915
2961
  // /billing/meter/get-enterprise-user-usage, the meter WorkBuddy's own usage
2916
2962
  // page (www.workbuddy.cn/profile/usage) reads for one (magpie #1904,
@@ -2953,7 +2999,7 @@ async function spareFree(site, a, windows) {
2953
2999
  // detail list without them (10001 "PackageCodes required").
2954
3000
  const CODE = (n, s) => `TCACA_code_${n}_${s}`
2955
3001
  const PAID_CODES = [CODE("002", "AkiJS3ZHF5"), CODE("005", "maRGyrHhw1"), CODE("003", "FAnt7lcmRT"), CODE("023", "4xbGhMrE6q"),
2956
- CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("009", "0XmEQc2xOf"), CODE("038", "OhvqZtiPKr"), CODE("036", "lupO5WgNdG")]
3002
+ CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("043", "6oIkxT7Z0S"), CODE("009", "0XmEQc2xOf"), CODE("038", "OhvqZtiPKr"), CODE("036", "lupO5WgNdG")]
2957
3003
  const DAILY_CODE = CODE("001", "PqouKr6QWV")
2958
3004
  const FREE_CODES = [DAILY_CODE, CODE("008", "cfWoLwvjU4"), CODE("035", "ArVxJcGDsm"), CODE("006", "DbXS0lrypC"), CODE("039", "KRcQj7wUat"),
2959
3005
  CODE("040", "mi9rCYg46x"), CODE("007", "nzdH5h4Nl0"), CODE("028", "NtpWi0jzXs"), CODE("037", "WxOD3MpI2o"), CODE("029", "6wCGEWquYy"),
@@ -2962,19 +3008,30 @@ const FREE_CODES = [DAILY_CODE, CODE("008", "cfWoLwvjU4"), CODE("035", "ArVxJcGD
2962
3008
  // lost at the same midnight on every account, so they tell none apart
2963
3009
  const SLICE = "4"
2964
3010
 
2965
- // PLAN_CODES are the packages whose credits come back each cycle, as
2966
- // WorkBuddy's client (5.9.x) lists them (onlyRefreshCodes): Pro monthly,
2967
- // Pro monthly plus, Pro yearly, youth, advanced, flagship, the plan's
2968
- // bonus (bonus28; bonusIntl, "Plan Bonus Quota"), the international free
2969
- // plan (freeMonIntl), the Pro trials, and on the China build alone the
2970
- // free plan's monthly base (freeMon, 套餐基础积分). Every other code only
2971
- // lapses: gift (006, "Bonus Pack" / Pro trial), activity (007, 赠送积分),
2972
- // bonus29/30, and the credit packs (009, 036, 038).
3011
+ // PLAN_CODES are the plan's base credits, as WorkBuddy's client (WorkBuddy
3012
+ // AI 5.9.4) groups them in its usage panel (resolveCreditGroup's
3013
+ // planBase): Pro monthly, Pro monthly plus, Pro yearly, youth, advanced,
3014
+ // flagship, buddyOneVip, the international free plan (freeMonIntl), the
3015
+ // Pro trials, and on the China build alone the free plan's monthly base
3016
+ // (freeMon, 套餐基础积分). The plan's bonus (bonus28; bonusIntl, "Plan
3017
+ // Bonus Quota") comes back each cycle too, but the client says it apart
3018
+ // (planBonus), as it does the credit packs (009, 036, 038: addon) and
3019
+ // activity, bonus29/30 and gift (benefit).
2973
3020
  const PLAN_CODES = [CODE("002", "AkiJS3ZHF5"), CODE("005", "maRGyrHhw1"), CODE("003", "FAnt7lcmRT"), CODE("023", "4xbGhMrE6q"),
2974
- CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("028", "NtpWi0jzXs"), CODE("035", "ArVxJcGDsm"),
2975
- CODE("039", "KRcQj7wUat"), CODE("040", "mi9rCYg46x"), CODE("037", "WxOD3MpI2o")]
3021
+ CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("043", "6oIkxT7Z0S"), CODE("035", "ArVxJcGDsm"),
3022
+ CODE("039", "KRcQj7wUat"), CODE("040", "mi9rCYg46x")]
2976
3023
  const FREE_MON = CODE("008", "cfWoLwvjU4")
2977
3024
 
3025
+ // EXTRA_CODES are the kinds of extra credits WorkBuddy's own usage panel
3026
+ // shows beside the plan's base (WorkBuddy 5.5.6, the China build's
3027
+ // resolveCreditGroup): the plan's bonus (bonus28, bonusIntl), top-ups
3028
+ // (extra 009, extra38 038, extraIntl 036) and platform rewards (activity
3029
+ // 007, bonus29, bonus30), and on WorkBuddy AI the free month (008) and
3030
+ // gift (006) too. The China build puts gift in no group, so it shows none.
3031
+ const EXTRA_CODES = [CODE("028", "NtpWi0jzXs"), CODE("037", "WxOD3MpI2o"), CODE("009", "0XmEQc2xOf"), CODE("038", "OhvqZtiPKr"),
3032
+ CODE("036", "lupO5WgNdG"), CODE("007", "nzdH5h4Nl0"), CODE("029", "6wCGEWquYy"), CODE("030", "BjSt89qTvr")]
3033
+ const EXTRA_AI = [FREE_MON, CODE("006", "DbXS0lrypC")]
3034
+
2978
3035
  // planCodes says whether a package of the summary is the plan's own: one
2979
3036
  // of PLAN_CODES on that site, or the subscription the summary names.
2980
3037
  function planCodes(sum, site) {
@@ -3811,6 +3868,18 @@ function makePlugin(site) {
3811
3868
  Object.assign(stops, runsOutOf(sum, packages) ?? {})
3812
3869
  const aside = extra ? (stops === credits ? extra : credits) : null
3813
3870
  if (aside) Object.assign(aside, runsOutOf(sum, packages, Date.now(), (code) => ofPlan(code) === (aside === credits)) ?? {})
3871
+ // the summary of the plan's credits alone: the extra ones
3872
+ // only the detail lists have (listedExtra) are their window,
3873
+ // always set aside. Whether WorkBuddy spends them once the
3874
+ // plan's are gone isn't seen, so the plan's window stops the
3875
+ // account as it did before.
3876
+ const kinds = !extra && kindsOf(sum, site)
3877
+ const more = kinds && kinds.plan.total > 0 && kinds.extra.total === 0 ? listedExtra(sum, packages, site) : null
3878
+ if (more) {
3879
+ const { runsOutAt, runsOut, ...counts } = more
3880
+ const w = { ...creditWindow("Extra credits", counts), aside: true, ...(runsOutAt ? { runsOutAt, runsOut } : {}) }
3881
+ out.windows.splice(out.windows.indexOf(credits) + 1, 0, w)
3882
+ }
3814
3883
  } catch {}
3815
3884
  }
3816
3885
  await spareFree(site, a, out.windows)
@@ -3968,4 +4037,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
3968
4037
  export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
3969
4038
 
3970
4039
  // for tests
3971
- export const _internal = { planCodes, idFor, OFF_TAKEN, replyAfterToolsOn, withDefaultEffort, SMALL_BUDGET, 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, pinning, rowTime, rowAccount, ofAccount, windowOffset, windowReset, FREE_WINDOW_MODELS, FREE_WINDOW_CN, FREE_WINDOW_AI, FREE_WINDOW_SPAN, FREE_WINDOW_GAP, FREE_WINDOW_LEAD, checkinOf, API_MESSAGES: NPM_MESSAGES, API_CHAT: NPM }
4040
+ export const _internal = { planCodes, listedExtra, idFor, OFF_TAKEN, replyAfterToolsOn, withDefaultEffort, SMALL_BUDGET, 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, pinning, rowTime, rowAccount, ofAccount, windowOffset, windowReset, FREE_WINDOW_MODELS, FREE_WINDOW_CN, FREE_WINDOW_AI, 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.15",
3
+ "version": "0.2.17",
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",