@magpie-community/opencode-cursor-auth 0.2.29 → 0.2.30

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 +75 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -254,6 +254,17 @@ own spends the team's pool: On-demand shows the pool's spend and limit,
254
254
  with the member's own spend beside it, and the account is used up when
255
255
  the pool is.
256
256
 
257
+ An Enterprise account's period has no Cursor Models, Other Models or Total
258
+ in `GetCurrentPeriodUsage`: Cursor reports its overall allowance through
259
+ the website's `usage-summary` instead. Magpie shows that as Enterprise,
260
+ for example `49.45 / 800 USD · 6.2% used`, or
261
+ `750.55 / 800 USD · 93.8% left` with Magpie's Used / Left choice, reset at
262
+ the billing cycle's end. When the summary says team on-demand is enabled
263
+ and unlimited or still has room, the Enterprise window is informational:
264
+ reaching its included allowance doesn't count the account as used up.
265
+ On-demand also shows the team's spend when known, with its limit when it
266
+ has one. A reached on-demand limit counts the account as used up.
267
+
257
268
  Sign in to the Cursor account linked in Grok Bot. A linked SuperGrok
258
269
  subscription grants Bot access without a paid Cursor plan; see
259
270
  [Grok Bot plans and billing](https://cursor.com/help/grok-bot/plans).
package/index.mjs CHANGED
@@ -532,14 +532,79 @@ async function hardLimit(tok) {
532
532
  // dollars, thousands grouped: a team's pool runs to six figures ($451,200.00)
533
533
  const dollars = (n) => "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
534
534
 
535
- // usage is the account's windows this billing period; an enterprise plan
536
- // reports spend instead, and gets none. ids are the models it can be
537
- // asked for, each counted by the pool Cursor bills it to. An account that
538
- // may spend on-demand once its included usage is gone (fottencity on
535
+ // enterpriseUsage is the allowance GetCurrentPeriodUsage leaves out on an
536
+ // Enterprise account. That call answers with only the billing cycle there;
537
+ // the website's usage-summary has individualUsage.overall, in cents, and
538
+ // the account's membership. The CLI JWT is also the website's dashboard
539
+ // session, as botUsage builds it. undefined when the summary says nothing
540
+ // this can show, so a failed read stays an empty card, as before.
541
+ async function enterpriseUsage(tok) {
542
+ try {
543
+ const subject = JSON.parse(Buffer.from(tok.split(".")[1], "base64url").toString()).sub
544
+ if (typeof subject !== "string") return undefined
545
+ const user = subject.split("|").filter(Boolean).at(-1)
546
+ if (!user || !/^[a-zA-Z0-9._-]+$/.test(user)) return undefined
547
+ const res = await fetch(WEBSITE + "/api/usage-summary", {
548
+ method: "GET",
549
+ headers: {
550
+ Accept: "application/json",
551
+ Origin: WEBSITE,
552
+ Cookie: "WorkosCursorSessionToken=" + encodeURIComponent(user + "::" + tok),
553
+ },
554
+ redirect: "error",
555
+ signal: AbortSignal.timeout(15_000),
556
+ })
557
+ if (!res.ok) return undefined
558
+ const data = await res.json()
559
+ if (String(data?.membershipType).toLowerCase() !== "enterprise") return undefined
560
+ const overall = data?.individualUsage?.overall
561
+ if (overall?.enabled !== true) return undefined
562
+ const { used, limit } = overall
563
+ if (typeof used !== "number" || !Number.isFinite(used) || used < 0) return undefined
564
+ if (typeof limit !== "number" || !Number.isFinite(limit) || limit <= 0) return undefined
565
+ const end = Date.parse(data.billingCycleEnd ?? "")
566
+ const resets = Number.isFinite(end) ? { resetsAt: new Date(end).toISOString() } : {}
567
+ const od = data.teamUsage?.onDemand
568
+ const spent = typeof od?.used === "number" && Number.isFinite(od.used) && od.used >= 0
569
+ const unlimited = od?.enabled === true && od.limit === null
570
+ const fixed = od?.enabled === true && typeof od.limit === "number" && Number.isFinite(od.limit) && od.limit > 0 && spent
571
+ // Only an explicitly unlimited budget or a measured budget with room
572
+ // lets a spent included allowance stay available to magpie's routing.
573
+ const aside = unlimited || (fixed && od.used < od.limit)
574
+ const windows = [{
575
+ name: "Enterprise",
576
+ used: Math.max(0, Math.min(100, (100 * used) / limit)),
577
+ display: `${dollars(used / 100)} / ${dollars(limit / 100)}`,
578
+ amount: used / 100,
579
+ limit: limit / 100,
580
+ unit: "USD",
581
+ ...resets,
582
+ ...(aside ? { aside: true } : {}),
583
+ }]
584
+ if (fixed) windows.push({
585
+ name: "On-demand",
586
+ used: Math.min(100, (100 * od.used) / od.limit),
587
+ display: `${dollars(od.used / 100)} / ${dollars(od.limit / 100)}`,
588
+ amount: od.used / 100,
589
+ limit: od.limit / 100,
590
+ unit: "USD",
591
+ ...resets,
592
+ })
593
+ else if (unlimited && spent) windows.push({ name: "On-demand", used: 0, display: dollars(od.used / 100), ...resets, aside: true })
594
+ return windows
595
+ } catch {
596
+ return undefined
597
+ }
598
+ }
599
+
600
+ // usage is the account's windows this billing period. ids are the models it
601
+ // can be asked for, each counted by the pool Cursor bills it to. An account
602
+ // that may spend on-demand once its included usage is gone (fottencity on
539
603
  // Discord: a team that pays for more) is still served when the pools are
540
604
  // spent, so they are shown aside, and its On-demand spend is what can run
541
605
  // out: at its limit (a team member with none of their own: the team's
542
- // pool), or never when it has none.
606
+ // pool), or never when it has none. An Enterprise account's period has no
607
+ // planUsage; its allowance comes from usage-summary instead.
543
608
  async function usage(tok, ids) {
544
609
  const [res, hard] = await Promise.all([
545
610
  fetch(API + "/aiserver.v1.DashboardService/GetCurrentPeriodUsage", {
@@ -553,7 +618,11 @@ async function usage(tok, ids) {
553
618
  if (!res.ok) return { error: statusText(res.status), windows: [] }
554
619
  const data = await res.json()
555
620
  const u = data?.planUsage
556
- if (!u) return { windows: [] }
621
+ if (!u) {
622
+ // Prefer measured Enterprise usage before any reset-date-only fallback.
623
+ const windows = await enterpriseUsage(tok)
624
+ return windows ? { windows } : { windows: [] }
625
+ }
557
626
  const ms = /^[+-]?\d+$/.test(data.billingCycleEnd ?? "") ? Number(data.billingCycleEnd) : 0
558
627
  const resets = ms > 0 ? { resetsAt: new Date(ms).toISOString() } : {}
559
628
  const bucket = new Set((data.autoBucketModels ?? []).map(poolBase))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-cursor-auth",
3
- "version": "0.2.29",
3
+ "version": "0.2.30",
4
4
  "description": "Cursor subscriptions (Pro, Pro+, Ultra, Teams) in OpenCode and magpie, through the API cursor-agent talks to",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",