@magpie-community/opencode-workbuddy-auth 0.2.16 → 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 (2) hide show
  1. package/index.mjs +82 -18
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -2884,9 +2884,30 @@ function pinning() {
2884
2884
  // spent and extra ones are left.
2885
2885
  function usageOf(sum, plan, site) {
2886
2886
  const out = { plan: plan || (sum?.IsPaidUser ? "Pro" : "Free plan"), windows: [] }
2887
+ const { plan: mine, extra, left, bags } = kindsOf(sum, site)
2888
+ if (mine.total > 0 && extra.total > 0) {
2889
+ const credits = creditWindow("Credits", mine), more = creditWindow("Extra credits", extra)
2890
+ if (mine.used >= mine.total && extra.used < extra.total) credits.aside = true
2891
+ else more.aside = true
2892
+ out.windows.push(credits, more)
2893
+ } else {
2894
+ const total = mine.total + extra.total, used = mine.used + extra.used
2895
+ if (total > 0) out.windows.push(creditWindow("Credits", { used, total }))
2896
+ }
2897
+ const total = mine.total + extra.total
2898
+ if (bags > 0 && (total === 0 || left > 0)) out.windows.push({ name: "Prepaid credits", balance: left, display: `${compact(left)} credits`, unit: "credits" })
2899
+ return out
2900
+ }
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) {
2887
2909
  const ofPlan = planCodes(sum, site)
2888
- const kinds = { plan: { total: 0, used: 0 }, extra: { total: 0, used: 0 } }
2889
- let left = 0, bags = 0
2910
+ const kinds = { plan: { total: 0, used: 0 }, extra: { total: 0, used: 0 }, left: 0, bags: 0 }
2890
2911
  const hasTotal = (p) => p?.CycleTotalCapacity != null && p.CycleTotalCapacity !== ""
2891
2912
  const add = (k, p) => ((k.total += capacity(p.CycleTotalCapacity)), (k.used += capacity(p?.CycleUsedCapacity)))
2892
2913
  const groups = (Array.isArray(sum?.CreditGroups) ? sum.CreditGroups : []).filter(hasTotal)
@@ -2895,24 +2916,45 @@ function usageOf(sum, plan, site) {
2895
2916
  if (hasTotal(p)) {
2896
2917
  if (!groups.length) add(kinds[ofPlan(p?.PackageCode) ? "plan" : "extra"], p)
2897
2918
  } else if (p?.CycleRemainCapacity != null && p.CycleRemainCapacity !== "") {
2898
- left += capacity(p.CycleRemainCapacity)
2899
- bags++
2919
+ kinds.left += capacity(p.CycleRemainCapacity)
2920
+ kinds.bags++
2900
2921
  }
2901
2922
  }
2902
- const window = (name, { used, total }) => ({ name, used: (100 * used) / total, display: `${compact(used)} / ${compact(total)}`, amount: used, limit: total, unit: "credits" })
2903
- const { plan: mine, extra } = kinds
2904
- if (mine.total > 0 && extra.total > 0) {
2905
- const credits = window("Credits", mine), more = window("Extra credits", extra)
2906
- if (mine.used >= mine.total && extra.used < extra.total) credits.aside = true
2907
- else more.aside = true
2908
- out.windows.push(credits, more)
2909
- } else {
2910
- const total = mine.total + extra.total, used = mine.used + extra.used
2911
- if (total > 0) out.windows.push(window("Credits", { used, total }))
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
+ }
2912
2955
  }
2913
- const total = mine.total + extra.total
2914
- if (bags > 0 && (total === 0 || left > 0)) out.windows.push({ name: "Prepaid credits", balance: left, display: `${compact(left)} credits`, unit: "credits" })
2915
- return out
2956
+ if (!(total > 0)) return null
2957
+ return { total, used: total - left, ...(at ? { runsOutAt: new Date(at).toISOString(), runsOut: lapsing } : {}) }
2916
2958
  }
2917
2959
 
2918
2960
  // enterpriseUsageOf is an enterprise (team) account's credits, from
@@ -2980,6 +3022,16 @@ const PLAN_CODES = [CODE("002", "AkiJS3ZHF5"), CODE("005", "maRGyrHhw1"), CODE("
2980
3022
  CODE("039", "KRcQj7wUat"), CODE("040", "mi9rCYg46x")]
2981
3023
  const FREE_MON = CODE("008", "cfWoLwvjU4")
2982
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
+
2983
3035
  // planCodes says whether a package of the summary is the plan's own: one
2984
3036
  // of PLAN_CODES on that site, or the subscription the summary names.
2985
3037
  function planCodes(sum, site) {
@@ -3816,6 +3868,18 @@ function makePlugin(site) {
3816
3868
  Object.assign(stops, runsOutOf(sum, packages) ?? {})
3817
3869
  const aside = extra ? (stops === credits ? extra : credits) : null
3818
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
+ }
3819
3883
  } catch {}
3820
3884
  }
3821
3885
  await spareFree(site, a, out.windows)
@@ -3973,4 +4037,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
3973
4037
  export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
3974
4038
 
3975
4039
  // for tests
3976
- 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.16",
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",