@magpie-community/opencode-cursor-auth 0.2.10 → 0.2.12
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.
- package/README.md +7 -3
- package/index.mjs +65 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,9 +76,13 @@ plugin's `fetch` answers them on Cursor's agent API:
|
|
|
76
76
|
usage events, by the Run's conversation id: it shows in about 2.5 s,
|
|
77
77
|
and is looked for up to 6 s. Far from Cursor it shows later than that
|
|
78
78
|
(yetone/magpie#1053), so the step says 0 used and the conversation owes
|
|
79
|
-
it: its next steps collect the event in the background and
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
it: its next steps collect the event in the background, and a step
|
|
80
|
+
that ends the turn waits for what is still owed, so the conversation
|
|
81
|
+
adds up to what Cursor counted. What a step collected of earlier steps
|
|
82
|
+
is not added to its own count, which a client reads as how full its
|
|
83
|
+
context is: it is `usage.magpie_deferred` (`prompt_tokens`,
|
|
84
|
+
`completion_tokens`, `cached_tokens`, `cache_write_tokens`), which
|
|
85
|
+
magpie bills with the step and clients ignore. An account
|
|
82
86
|
whose events miss the wait 3 steps in a row stops waiting for them.
|
|
83
87
|
With nothing naming the session (so no later step to collect it), or
|
|
84
88
|
an account that can't read its usage events, the step is estimated as
|
package/index.mjs
CHANGED
|
@@ -1408,7 +1408,8 @@ function owe(conv, { since = Date.now(), n = 0, ready = Date.now(), carry = null
|
|
|
1408
1408
|
|
|
1409
1409
|
// usageEvents are the dashboard's events of conversation conv from since
|
|
1410
1410
|
// on, the `want` oldest not counted yet, summed: usage (null when none),
|
|
1411
|
-
// how many were found (got), and refused
|
|
1411
|
+
// each with its time (events), how many were found (got), and refused
|
|
1412
|
+
// when the account can't read them.
|
|
1412
1413
|
// Polling, they are looked for as STEP_WAIT says, from `first` on, until
|
|
1413
1414
|
// all show; else asked for once.
|
|
1414
1415
|
async function usageEvents(tok, conv, since, want, { poll = false, first = STEP_WAIT.first, signal } = {}) {
|
|
@@ -1421,6 +1422,7 @@ async function usageEvents(tok, conv, since, want, { poll = false, first = STEP_
|
|
|
1421
1422
|
const num = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0)
|
|
1422
1423
|
let usage = null
|
|
1423
1424
|
let got = 0
|
|
1425
|
+
const events = []
|
|
1424
1426
|
if (poll) await wait(first)
|
|
1425
1427
|
while (!signal?.aborted) {
|
|
1426
1428
|
try {
|
|
@@ -1433,7 +1435,7 @@ async function usageEvents(tok, conv, since, want, { poll = false, first = STEP_
|
|
|
1433
1435
|
if (res.status >= 400 && res.status < 500 && res.status !== 429) {
|
|
1434
1436
|
// not this account's to read
|
|
1435
1437
|
unreadable.set(tokKey(tok), Date.now())
|
|
1436
|
-
return { usage, got, refused: true }
|
|
1438
|
+
return { usage, events, got, refused: true }
|
|
1437
1439
|
}
|
|
1438
1440
|
const evs = res.ok ? ((await res.json())?.usageEventsDisplay ?? []) : []
|
|
1439
1441
|
const found = evs
|
|
@@ -1443,7 +1445,9 @@ async function usageEvents(tok, conv, since, want, { poll = false, first = STEP_
|
|
|
1443
1445
|
for (const ev of found) {
|
|
1444
1446
|
claimed.set(conv + "\0" + ev.timestamp, Date.now())
|
|
1445
1447
|
const t = ev.tokenUsage
|
|
1446
|
-
|
|
1448
|
+
const u = { input: num(t.inputTokens), output: num(t.outputTokens), cacheRead: num(t.cacheReadTokens), cacheWrite: num(t.cacheWriteTokens), reasoning: 0 }
|
|
1449
|
+
usage = addUsage(usage ?? ZERO(), u)
|
|
1450
|
+
events.push({ at: num(ev.timestamp), usage: u })
|
|
1447
1451
|
got++
|
|
1448
1452
|
}
|
|
1449
1453
|
if (got >= want) break
|
|
@@ -1451,7 +1455,7 @@ async function usageEvents(tok, conv, since, want, { poll = false, first = STEP_
|
|
|
1451
1455
|
if (!poll || Date.now() + STEP_WAIT.every > end) break
|
|
1452
1456
|
await wait(STEP_WAIT.every)
|
|
1453
1457
|
}
|
|
1454
|
-
return { usage, got, refused: false }
|
|
1458
|
+
return { usage, events, got, refused: false }
|
|
1455
1459
|
}
|
|
1456
1460
|
|
|
1457
1461
|
// stepUsage is the usage of the Run of conversation conv started at since,
|
|
@@ -1461,22 +1465,24 @@ async function stepUsage(tok, conv, since, signal) {
|
|
|
1461
1465
|
}
|
|
1462
1466
|
|
|
1463
1467
|
// collect takes conversation conv's debt and asks for its events (polling
|
|
1464
|
-
// when poll): what was carried and what was found (usage),
|
|
1465
|
-
//
|
|
1466
|
-
//
|
|
1468
|
+
// when poll): what was carried and what was found (usage), what was
|
|
1469
|
+
// carried alone (carry) and the events found (events), and whether any is
|
|
1470
|
+
// still owed (short), which is owed on. What comes back once the Run that
|
|
1471
|
+
// asked is over (over() true) is carried to the next instead.
|
|
1467
1472
|
function collect(tok, conv, { poll = false, first, signal, over = () => false } = {}) {
|
|
1468
1473
|
const d = owed.get(conv)
|
|
1469
|
-
if (!d) return Promise.resolve({ usage: null, short: false })
|
|
1474
|
+
if (!d) return Promise.resolve({ usage: null, carry: null, events: [], short: false })
|
|
1470
1475
|
owed.delete(conv)
|
|
1471
1476
|
const p = (async () => {
|
|
1472
|
-
let r = { usage: null, got: 0 }
|
|
1477
|
+
let r = { usage: null, events: [], got: 0 }
|
|
1473
1478
|
if (d.n > 0 && canRead(tok)) r = await usageEvents(tok, conv, d.since, d.n, { poll, first, signal }).catch(() => r)
|
|
1474
1479
|
const short = d.n > r.got && canRead(tok)
|
|
1475
1480
|
if (short) owe(conv, { since: d.since, n: d.n - r.got, ready: d.ready })
|
|
1481
|
+
const carry = d.carry && { ...d.carry }
|
|
1476
1482
|
const usage = addUsage(d.carry, r.usage)
|
|
1477
|
-
if (!over()) return { usage, short }
|
|
1483
|
+
if (!over()) return { usage, carry, events: r.events ?? [], short }
|
|
1478
1484
|
owe(conv, { carry: usage })
|
|
1479
|
-
return { usage: null, short }
|
|
1485
|
+
return { usage: null, carry: null, events: [], short }
|
|
1480
1486
|
})()
|
|
1481
1487
|
const set = flights.get(conv) ?? new Set()
|
|
1482
1488
|
flights.set(conv, set.add(p))
|
|
@@ -1498,22 +1504,41 @@ function collect(tok, conv, { poll = false, first, signal, over = () => false }
|
|
|
1498
1504
|
// read its events). owed(final) is what the Run collected otherwise,
|
|
1499
1505
|
// all that can be had when final. done() ends the Run: what was collected
|
|
1500
1506
|
// and not given out goes back.
|
|
1507
|
+
//
|
|
1508
|
+
// What a Run collects is told apart by its event's time: Cursor stamps a
|
|
1509
|
+
// Run's event as the Run begins (1.4–3.4 s after since), so one from since
|
|
1510
|
+
// on (less the second usageEvents allows) is this Run's own (mine), and one
|
|
1511
|
+
// before it, or a count carried, an earlier Run's (theirs). own() gives out only mine, as the step's usage,
|
|
1512
|
+
// which a client reads as how full its context is; theirs goes out by
|
|
1513
|
+
// owed(), apart. A Run of its conversation begins only once the one
|
|
1514
|
+
// before has ended, so the two never meet; were the time off, the step's
|
|
1515
|
+
// count would be told in the wrong field, never twice nor lost.
|
|
1501
1516
|
function ledger(tok, conv, since, signal, kept) {
|
|
1502
1517
|
const key = tokKey(tok)
|
|
1503
|
-
let
|
|
1518
|
+
let mine = null // this Run's own, collected, not given out yet
|
|
1519
|
+
let theirs = null // earlier Runs', collected, not given out yet
|
|
1504
1520
|
let over = false
|
|
1505
1521
|
const gather = (opts) =>
|
|
1506
1522
|
collect(tok, conv, { ...opts, over: () => over }).then((r) => {
|
|
1507
|
-
|
|
1523
|
+
theirs = addUsage(theirs, r.carry)
|
|
1524
|
+
for (const e of r.events ?? []) {
|
|
1525
|
+
if (e.at >= since - 1000) mine = addUsage(mine, e.usage)
|
|
1526
|
+
else theirs = addUsage(theirs, e.usage)
|
|
1527
|
+
}
|
|
1508
1528
|
return r
|
|
1509
1529
|
})
|
|
1510
1530
|
let collecting = null
|
|
1511
1531
|
const debt = kept ? owed.get(conv) : undefined
|
|
1512
1532
|
const timer = debt ? setTimeout(() => (collecting = gather()), Math.max(0, debt.ready - Date.now())) : null
|
|
1513
1533
|
timer?.unref?.()
|
|
1534
|
+
const takeMine = () => {
|
|
1535
|
+
const u = mine
|
|
1536
|
+
mine = null
|
|
1537
|
+
return u
|
|
1538
|
+
}
|
|
1514
1539
|
const take = () => {
|
|
1515
|
-
const u =
|
|
1516
|
-
|
|
1540
|
+
const u = addUsage(theirs, mine)
|
|
1541
|
+
mine = theirs = null
|
|
1517
1542
|
return u
|
|
1518
1543
|
}
|
|
1519
1544
|
return {
|
|
@@ -1534,7 +1559,7 @@ function ledger(tok, conv, since, signal, kept) {
|
|
|
1534
1559
|
if (r.short) misses.set(key, m + 1)
|
|
1535
1560
|
else misses.delete(key)
|
|
1536
1561
|
} else misses.set(key, m + 1)
|
|
1537
|
-
return
|
|
1562
|
+
return takeMine() ?? false
|
|
1538
1563
|
},
|
|
1539
1564
|
async owed(final) {
|
|
1540
1565
|
if (!kept) return null
|
|
@@ -1548,7 +1573,8 @@ function ledger(tok, conv, since, signal, kept) {
|
|
|
1548
1573
|
done() {
|
|
1549
1574
|
over = true
|
|
1550
1575
|
clearTimeout(timer)
|
|
1551
|
-
|
|
1576
|
+
const u = take()
|
|
1577
|
+
if (u) owe(conv, { carry: u })
|
|
1552
1578
|
},
|
|
1553
1579
|
}
|
|
1554
1580
|
}
|
|
@@ -1918,16 +1944,19 @@ async function* decode(stream, { send, blobs, tools, estimate, close, stepUsage,
|
|
|
1918
1944
|
// with it or by a later step (ledger), never guessed
|
|
1919
1945
|
let counted = false
|
|
1920
1946
|
// finish is the step's end, with what earlier steps owed (more), which
|
|
1921
|
-
// is no reason not to guess the step's own
|
|
1947
|
+
// is no reason not to guess the step's own. The step's usage is its own
|
|
1948
|
+
// Run's alone: a client reads its prompt_tokens as how full the context
|
|
1949
|
+
// is (pi, OpenCode), and an earlier step's prompt added to it read twice
|
|
1950
|
+
// the context. What earlier steps owed goes apart, as magpie_deferred,
|
|
1951
|
+
// which magpie counts with the step and no client reads.
|
|
1922
1952
|
const finish = (more) => {
|
|
1923
1953
|
if (!counted && !usage.output) usage.output = Math.floor((said + 3) / 4)
|
|
1924
1954
|
// a guess only when Cursor counted nothing: a prompt read whole from
|
|
1925
1955
|
// the cache leaves no uncached rest, and is no reason to guess; nor is
|
|
1926
1956
|
// a prompt counted from the events
|
|
1927
1957
|
if (!counted && !usage.input && !usage.cacheRead && !usage.cacheWrite) usage.input = estimate
|
|
1928
|
-
// the events' output is their steps', counted here as their prompt is
|
|
1929
|
-
if (more) for (const k of ["input", "output", "cacheRead", "cacheWrite"]) usage[k] += more[k]
|
|
1930
1958
|
const prompt = usage.input + usage.cacheRead + usage.cacheWrite
|
|
1959
|
+
const deferred = more && more.input + more.output + more.cacheRead + more.cacheWrite > 0
|
|
1931
1960
|
return {
|
|
1932
1961
|
stop: calls > 0 ? "tool_calls" : "stop",
|
|
1933
1962
|
usage: {
|
|
@@ -1936,6 +1965,16 @@ async function* decode(stream, { send, blobs, tools, estimate, close, stepUsage,
|
|
|
1936
1965
|
total_tokens: prompt + usage.output,
|
|
1937
1966
|
prompt_tokens_details: { cached_tokens: usage.cacheRead, cache_write_tokens: usage.cacheWrite },
|
|
1938
1967
|
...(usage.reasoning ? { completion_tokens_details: { reasoning_tokens: usage.reasoning } } : {}),
|
|
1968
|
+
...(deferred
|
|
1969
|
+
? {
|
|
1970
|
+
magpie_deferred: {
|
|
1971
|
+
prompt_tokens: more.input + more.cacheRead + more.cacheWrite,
|
|
1972
|
+
completion_tokens: more.output,
|
|
1973
|
+
cached_tokens: more.cacheRead,
|
|
1974
|
+
cache_write_tokens: more.cacheWrite,
|
|
1975
|
+
},
|
|
1976
|
+
}
|
|
1977
|
+
: {}),
|
|
1939
1978
|
},
|
|
1940
1979
|
}
|
|
1941
1980
|
}
|
|
@@ -2310,8 +2349,12 @@ export async function CursorAuthPlugin() {
|
|
|
2310
2349
|
return { error: e.message, windows: [], signIn: "kept" }
|
|
2311
2350
|
}
|
|
2312
2351
|
const [u, plan, bot] = await Promise.all([usage(tok, Object.keys(provider?.models ?? {})), planOf(tok), botUsage(tok)])
|
|
2313
|
-
//
|
|
2314
|
-
|
|
2352
|
+
// Bot goes among magpie's three menu-bar rings: before Total, and
|
|
2353
|
+
// before an On-demand with no limit, which is only a dollar figure on
|
|
2354
|
+
// a ring that never fills (magpie #2213, Morganwan90). An On-demand
|
|
2355
|
+
// with a limit stays ahead of it: that is what can stop the account.
|
|
2356
|
+
const last = (w) => w.name === "Total" || (w.name === "On-demand" && w.aside)
|
|
2357
|
+
const windows = [...u.windows.filter((w) => !last(w)), ...bot, ...u.windows.filter(last)]
|
|
2315
2358
|
return { ...u, windows, ...(plan ? { plan } : {}), signIn: "kept" }
|
|
2316
2359
|
},
|
|
2317
2360
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-cursor-auth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
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",
|