@magpie-community/opencode-cursor-auth 0.1.8 → 0.1.10
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 +14 -1
- package/index.mjs +282 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,7 +54,20 @@ plugin's `fetch` answers them on Cursor's agent API:
|
|
|
54
54
|
or not. Cursor's `input_tokens` counts the cached prompt too: the cache
|
|
55
55
|
read and written is taken out of it, and given as `cached_tokens` and
|
|
56
56
|
`cache_write_tokens`, so it is counted once; its reasoning tokens are
|
|
57
|
-
`reasoning_tokens`.
|
|
57
|
+
`reasoning_tokens`. A step that calls tools gets no usage in its Run
|
|
58
|
+
(Cursor tells it only once the tools' results come back in the same
|
|
59
|
+
Run), so once the Run is closed its usage is read from the dashboard's
|
|
60
|
+
usage events, by the Run's conversation id: it shows in about 2.5 s,
|
|
61
|
+
and is looked for up to 6 s. Far from Cursor it shows later than that
|
|
62
|
+
(yetone/magpie#1053), so the step says 0 used and the conversation owes
|
|
63
|
+
it: its next steps collect the event in the background and count it
|
|
64
|
+
with their own, and a step that ends the turn waits for what is still
|
|
65
|
+
owed, so the conversation adds up to what Cursor counted. An account
|
|
66
|
+
whose events miss the wait 3 steps in a row stops waiting for them.
|
|
67
|
+
With nothing naming the session (so no later step to collect it), or
|
|
68
|
+
an account that can't read its usage events, the step is estimated as
|
|
69
|
+
before.
|
|
70
|
+
Failures keep their statuses: 401 to sign in again, 429 at the
|
|
58
71
|
usage limit, 400 for a prompt too long, 403 for a region refusal.
|
|
59
72
|
|
|
60
73
|
## Models
|
package/index.mjs
CHANGED
|
@@ -1164,6 +1164,230 @@ function usageOf(uf) {
|
|
|
1164
1164
|
return { input: Math.max(inp - cr - cw, 0), output: pbNum(uf, 2), cacheRead: cr, cacheWrite: cw, reasoning: pbNum(uf, 5) }
|
|
1165
1165
|
}
|
|
1166
1166
|
|
|
1167
|
+
// A step that calls the caller's tools has no TurnEndedUpdate: Cursor ends
|
|
1168
|
+
// the turn, and tells what it used, only once the client has run the tools
|
|
1169
|
+
// and sent their results in the same Run, and the caller runs them after
|
|
1170
|
+
// the Run is closed (magpie #676). Before that it sends nothing more than
|
|
1171
|
+
// the conversation's checkpoint and heartbeats, and a cancelled Run ends
|
|
1172
|
+
// with an error and no usage either. The dashboard's usage events count
|
|
1173
|
+
// every Run, by its conversation_id, the uncached input apart from what
|
|
1174
|
+
// was read from the cache and written to it, as TurnEndedUpdate does; one
|
|
1175
|
+
// shows about 2.5 s after the Run is closed. STEP_WAIT is how long a step's
|
|
1176
|
+
// is looked for: first after that long, then every so often until the
|
|
1177
|
+
// end (each ask given `ask` to answer).
|
|
1178
|
+
//
|
|
1179
|
+
// Far from Cursor none shows in that time (yetone/magpie#1053: a round
|
|
1180
|
+
// trip of 4.2 s, the event 4–5 s after the Run closes), and waiting longer
|
|
1181
|
+
// would hold every tool step longer. So a tool step's Run is owed by its
|
|
1182
|
+
// conversation (owed): the step counts every event of the conversation's
|
|
1183
|
+
// owed Runs that has shown by the end of its wait, its own or earlier
|
|
1184
|
+
// ones, and what hasn't shown is collected by the conversation's next
|
|
1185
|
+
// steps, in the background from `owedAt` after the Run closed. A step
|
|
1186
|
+
// whose count is still owed says 0 used; the conversation's steps add up
|
|
1187
|
+
// to what Cursor counted, some of it a step or two late. A step that ends
|
|
1188
|
+
// the turn waits for what is still owed, as nothing may follow it soon.
|
|
1189
|
+
// After `skipAfter` steps in a row of an account's left something owed,
|
|
1190
|
+
// its steps stop waiting (but every `retryEvery`th), and leave it to the
|
|
1191
|
+
// next. A debt not collected in `owedFor` is let go. With no
|
|
1192
|
+
// conversation_id naming the session nothing later could collect a Run's
|
|
1193
|
+
// event: its step looks for it as before, and is guessed without it.
|
|
1194
|
+
const STEP_WAIT = { first: 1500, every: 500, until: 6000, ask: 5000, skipAfter: 3, retryEvery: 10, owedAt: 5000, owedFor: 600_000 }
|
|
1195
|
+
|
|
1196
|
+
// claimed are the usage events already counted, by conversation and time,
|
|
1197
|
+
// so that two steps of one conversation never count the same one.
|
|
1198
|
+
const claimed = new Map()
|
|
1199
|
+
|
|
1200
|
+
// owed are what each conversation still owes, by conversation_id: n Runs
|
|
1201
|
+
// whose events are to be counted, the earliest started at since, looked
|
|
1202
|
+
// for from ready on; and carry, a count collected that no step gave out.
|
|
1203
|
+
const owed = new Map()
|
|
1204
|
+
|
|
1205
|
+
// flights are each conversation's collections still going, which a step
|
|
1206
|
+
// that ends the turn waits for.
|
|
1207
|
+
const flights = new Map()
|
|
1208
|
+
|
|
1209
|
+
// unreadable are the accounts (by token) whose usage events Cursor won't
|
|
1210
|
+
// give (a 4xx), for owedFor: their steps are guessed, as before, and owe
|
|
1211
|
+
// nothing. misses are how many steps in a row of an account's left
|
|
1212
|
+
// something owed.
|
|
1213
|
+
const unreadable = new Map()
|
|
1214
|
+
const misses = new Map()
|
|
1215
|
+
const tokKey = (tok) => createHash("sha256").update(tok).digest("hex")
|
|
1216
|
+
function canRead(tok) {
|
|
1217
|
+
const at = unreadable.get(tokKey(tok))
|
|
1218
|
+
if (at === undefined) return true
|
|
1219
|
+
if (Date.now() - at < STEP_WAIT.owedFor) return false
|
|
1220
|
+
unreadable.delete(tokKey(tok))
|
|
1221
|
+
return true
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
const ZERO = () => ({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0 })
|
|
1225
|
+
function addUsage(a, b) {
|
|
1226
|
+
if (!b) return a
|
|
1227
|
+
if (!a) return { ...b }
|
|
1228
|
+
for (const k of Object.keys(a)) a[k] += b[k] ?? 0
|
|
1229
|
+
return a
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// owe adds to conversation conv's debt.
|
|
1233
|
+
function owe(conv, { since = Date.now(), n = 0, ready = Date.now(), carry = null }) {
|
|
1234
|
+
for (const [k, d] of owed) if (Date.now() - d.since > STEP_WAIT.owedFor) owed.delete(k)
|
|
1235
|
+
if (n <= 0 && !carry) return
|
|
1236
|
+
const d = owed.get(conv)
|
|
1237
|
+
if (!d) return void owed.set(conv, { since, n, ready, carry: carry && { ...carry } })
|
|
1238
|
+
d.since = Math.min(d.since, since)
|
|
1239
|
+
d.n += n
|
|
1240
|
+
d.ready = Math.min(d.ready, ready)
|
|
1241
|
+
d.carry = addUsage(d.carry, carry)
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// usageEvents are the dashboard's events of conversation conv from since
|
|
1245
|
+
// on, the `want` oldest not counted yet, summed: usage (null when none),
|
|
1246
|
+
// how many were found (got), and refused when the account can't read them.
|
|
1247
|
+
// Polling, they are looked for as STEP_WAIT says, from `first` on, until
|
|
1248
|
+
// all show; else asked for once.
|
|
1249
|
+
async function usageEvents(tok, conv, since, want, { poll = false, first = STEP_WAIT.first, signal } = {}) {
|
|
1250
|
+
const end = Date.now() + STEP_WAIT.until
|
|
1251
|
+
const wait = (ms) => new Promise((r) => {
|
|
1252
|
+
const t = setTimeout(r, ms)
|
|
1253
|
+
signal?.addEventListener("abort", () => (clearTimeout(t), r()), { once: true })
|
|
1254
|
+
})
|
|
1255
|
+
for (const [k, at] of claimed) if (Date.now() - at > 2 * STEP_WAIT.owedFor) claimed.delete(k)
|
|
1256
|
+
const num = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0)
|
|
1257
|
+
let usage = null
|
|
1258
|
+
let got = 0
|
|
1259
|
+
if (poll) await wait(first)
|
|
1260
|
+
while (!signal?.aborted) {
|
|
1261
|
+
try {
|
|
1262
|
+
const res = await fetch(API + "/aiserver.v1.DashboardService/GetFilteredUsageEvents", {
|
|
1263
|
+
method: "POST",
|
|
1264
|
+
headers: await headersFor(tok),
|
|
1265
|
+
body: JSON.stringify({ teamId: 0, startDate: String(since - 60_000), endDate: String(Date.now() + 60_000), page: 1, pageSize: 50 }),
|
|
1266
|
+
signal: AbortSignal.timeout(STEP_WAIT.ask),
|
|
1267
|
+
})
|
|
1268
|
+
if (res.status >= 400 && res.status < 500 && res.status !== 429) {
|
|
1269
|
+
// not this account's to read
|
|
1270
|
+
unreadable.set(tokKey(tok), Date.now())
|
|
1271
|
+
return { usage, got, refused: true }
|
|
1272
|
+
}
|
|
1273
|
+
const evs = res.ok ? ((await res.json())?.usageEventsDisplay ?? []) : []
|
|
1274
|
+
const found = evs
|
|
1275
|
+
.filter((e) => e?.conversationId === conv && e.tokenUsage && num(e.timestamp) >= since - 1000 && !claimed.has(conv + "\0" + e.timestamp))
|
|
1276
|
+
.sort((a, b) => num(a.timestamp) - num(b.timestamp))
|
|
1277
|
+
.slice(0, want - got)
|
|
1278
|
+
for (const ev of found) {
|
|
1279
|
+
claimed.set(conv + "\0" + ev.timestamp, Date.now())
|
|
1280
|
+
const t = ev.tokenUsage
|
|
1281
|
+
usage = addUsage(usage ?? ZERO(), { input: num(t.inputTokens), output: num(t.outputTokens), cacheRead: num(t.cacheReadTokens), cacheWrite: num(t.cacheWriteTokens) })
|
|
1282
|
+
got++
|
|
1283
|
+
}
|
|
1284
|
+
if (got >= want) break
|
|
1285
|
+
} catch {}
|
|
1286
|
+
if (!poll || Date.now() + STEP_WAIT.every > end) break
|
|
1287
|
+
await wait(STEP_WAIT.every)
|
|
1288
|
+
}
|
|
1289
|
+
return { usage, got, refused: false }
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// stepUsage is the usage of the Run of conversation conv started at since,
|
|
1293
|
+
// as the dashboard's usage events count it; null when none shows in time.
|
|
1294
|
+
async function stepUsage(tok, conv, since, signal) {
|
|
1295
|
+
return (await usageEvents(tok, conv, since, 1, { poll: true, signal })).usage
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// collect takes conversation conv's debt and asks for its events (polling
|
|
1299
|
+
// when poll): what was carried and what was found (usage), and whether
|
|
1300
|
+
// any is still owed (short), which is owed on. What comes back once the
|
|
1301
|
+
// Run that asked is over (over() true) is carried to the next instead.
|
|
1302
|
+
function collect(tok, conv, { poll = false, first, signal, over = () => false } = {}) {
|
|
1303
|
+
const d = owed.get(conv)
|
|
1304
|
+
if (!d) return Promise.resolve({ usage: null, short: false })
|
|
1305
|
+
owed.delete(conv)
|
|
1306
|
+
const p = (async () => {
|
|
1307
|
+
let r = { usage: null, got: 0 }
|
|
1308
|
+
if (d.n > 0 && canRead(tok)) r = await usageEvents(tok, conv, d.since, d.n, { poll, first, signal }).catch(() => r)
|
|
1309
|
+
const short = d.n > r.got && canRead(tok)
|
|
1310
|
+
if (short) owe(conv, { since: d.since, n: d.n - r.got, ready: d.ready })
|
|
1311
|
+
const usage = addUsage(d.carry, r.usage)
|
|
1312
|
+
if (!over()) return { usage, short }
|
|
1313
|
+
owe(conv, { carry: usage })
|
|
1314
|
+
return { usage: null, short }
|
|
1315
|
+
})()
|
|
1316
|
+
const set = flights.get(conv) ?? new Set()
|
|
1317
|
+
flights.set(conv, set.add(p))
|
|
1318
|
+
p.finally(() => {
|
|
1319
|
+
set.delete(p)
|
|
1320
|
+
if (!set.size && flights.get(conv) === set) flights.delete(conv)
|
|
1321
|
+
})
|
|
1322
|
+
return p
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// ledger is how a Run of conversation conv, started at since, has its
|
|
1326
|
+
// usage counted, for decode; kept when the conversation_id names the
|
|
1327
|
+
// session, so that a later Run of it can collect what this one owes. A
|
|
1328
|
+
// debt the conversation has is collected in the background. own() is a
|
|
1329
|
+
// step's count once it has called tools: its Run owed, what has shown of
|
|
1330
|
+
// the debt by the end of the wait (or at once, when the account's never
|
|
1331
|
+
// show in time); false when nothing has, null when the step is to be
|
|
1332
|
+
// guessed (not kept, and its event not found, or an account that can't
|
|
1333
|
+
// read its events). owed(final) is what the Run collected otherwise,
|
|
1334
|
+
// all that can be had when final. done() ends the Run: what was collected
|
|
1335
|
+
// and not given out goes back.
|
|
1336
|
+
function ledger(tok, conv, since, signal, kept) {
|
|
1337
|
+
const key = tokKey(tok)
|
|
1338
|
+
let pending = null // collected, not given out yet
|
|
1339
|
+
let over = false
|
|
1340
|
+
const gather = (opts) =>
|
|
1341
|
+
collect(tok, conv, { ...opts, over: () => over }).then((r) => {
|
|
1342
|
+
pending = addUsage(pending, r.usage)
|
|
1343
|
+
return r
|
|
1344
|
+
})
|
|
1345
|
+
let collecting = null
|
|
1346
|
+
const debt = kept ? owed.get(conv) : undefined
|
|
1347
|
+
const timer = debt ? setTimeout(() => (collecting = gather()), Math.max(0, debt.ready - Date.now())) : null
|
|
1348
|
+
timer?.unref?.()
|
|
1349
|
+
const take = () => {
|
|
1350
|
+
const u = pending
|
|
1351
|
+
pending = null
|
|
1352
|
+
return u
|
|
1353
|
+
}
|
|
1354
|
+
return {
|
|
1355
|
+
async own() {
|
|
1356
|
+
if (!canRead(tok)) return null
|
|
1357
|
+
const m = misses.get(key) ?? 0
|
|
1358
|
+
const waits = m < STEP_WAIT.skipAfter || m % STEP_WAIT.retryEvery === 0
|
|
1359
|
+
if (!kept) {
|
|
1360
|
+
const r = waits ? await usageEvents(tok, conv, since, 1, { poll: true, signal }).catch(() => ({})) : {}
|
|
1361
|
+
if (r.usage) misses.delete(key)
|
|
1362
|
+
else if (!r.refused) misses.set(key, m + 1)
|
|
1363
|
+
return r.usage ?? null
|
|
1364
|
+
}
|
|
1365
|
+
owe(conv, { since, n: 1, ready: Date.now() + STEP_WAIT.owedAt })
|
|
1366
|
+
if (waits) {
|
|
1367
|
+
const r = await gather({ poll: true, signal })
|
|
1368
|
+
if (!canRead(tok)) return null
|
|
1369
|
+
if (r.short) misses.set(key, m + 1)
|
|
1370
|
+
else misses.delete(key)
|
|
1371
|
+
} else misses.set(key, m + 1)
|
|
1372
|
+
return take() ?? false
|
|
1373
|
+
},
|
|
1374
|
+
async owed(final) {
|
|
1375
|
+
if (!kept) return null
|
|
1376
|
+
if (final) {
|
|
1377
|
+
await collecting
|
|
1378
|
+
await Promise.all([...(flights.get(conv) ?? [])])
|
|
1379
|
+
if (owed.has(conv)) await gather({ poll: owed.get(conv).n > 0, first: 0, signal })
|
|
1380
|
+
}
|
|
1381
|
+
return take()
|
|
1382
|
+
},
|
|
1383
|
+
done() {
|
|
1384
|
+
over = true
|
|
1385
|
+
clearTimeout(timer)
|
|
1386
|
+
if (pending) owe(conv, { carry: take() })
|
|
1387
|
+
},
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1167
1391
|
// buildRun is the Run's first message, an AgentClientMessage with its
|
|
1168
1392
|
// run_request, and the blobs it names. The conversation state is the
|
|
1169
1393
|
// messages and one turn, which the server wants there to sample at all.
|
|
@@ -1275,7 +1499,15 @@ function open(base, headers, signal) {
|
|
|
1275
1499
|
// runOnce is one Run of the chat on the agent API at base: an async
|
|
1276
1500
|
// iterator of the answer's pieces, or the error it failed with before any.
|
|
1277
1501
|
async function runOnce({ tok, base, id, tools, conv, convID, signal, maxMode, params }) {
|
|
1278
|
-
|
|
1502
|
+
// the Run's conversation_id, a new one when nothing names the session:
|
|
1503
|
+
// its usage event is found by it
|
|
1504
|
+
const cid = convID || randomUUID()
|
|
1505
|
+
const since = Date.now()
|
|
1506
|
+
// how its usage is counted: a debt of the conversation's is collected
|
|
1507
|
+
// while it goes, which only a conversation_id that names the session
|
|
1508
|
+
// could have run up
|
|
1509
|
+
const book = ledger(tok, cid, since, signal, !!convID)
|
|
1510
|
+
const { first, blobs } = buildRun(conv.msgs, conv.last, tools, id, cid, { maxMode, params })
|
|
1279
1511
|
const o = await open(
|
|
1280
1512
|
base,
|
|
1281
1513
|
{
|
|
@@ -1291,7 +1523,7 @@ async function runOnce({ tok, base, id, tools, conv, convID, signal, maxMode, pa
|
|
|
1291
1523
|
},
|
|
1292
1524
|
signal,
|
|
1293
1525
|
)
|
|
1294
|
-
if (o.error) return o
|
|
1526
|
+
if (o.error) return book.done(), o
|
|
1295
1527
|
const { session, req } = o
|
|
1296
1528
|
let closed = false
|
|
1297
1529
|
const send = (msg) => {
|
|
@@ -1316,20 +1548,30 @@ async function runOnce({ tok, base, id, tools, conv, convID, signal, maxMode, pa
|
|
|
1316
1548
|
for await (const c of req) chunks.push(c)
|
|
1317
1549
|
} catch {}
|
|
1318
1550
|
close()
|
|
1551
|
+
book.done()
|
|
1319
1552
|
return { error: failure(o.status, Buffer.concat(chunks).toString("utf8").slice(0, 1 << 20)) }
|
|
1320
1553
|
}
|
|
1321
|
-
const it = decode(frames(req), {
|
|
1554
|
+
const it = decode(frames(req), {
|
|
1555
|
+
send,
|
|
1556
|
+
blobs,
|
|
1557
|
+
tools,
|
|
1558
|
+
estimate: conv.estimate,
|
|
1559
|
+
close,
|
|
1560
|
+
stepUsage: book.own,
|
|
1561
|
+
owedUsage: book.owed,
|
|
1562
|
+
})
|
|
1322
1563
|
const wrapped = (async function* () {
|
|
1323
1564
|
try {
|
|
1324
1565
|
yield* it
|
|
1325
1566
|
} finally {
|
|
1326
1567
|
close()
|
|
1568
|
+
book.done()
|
|
1327
1569
|
}
|
|
1328
1570
|
})()
|
|
1329
1571
|
// an error comes before anything of the answer: out of quota, a model the
|
|
1330
1572
|
// plan doesn't have — answered with its own status
|
|
1331
1573
|
const head = await wrapped.next()
|
|
1332
|
-
if (head.done) return { error: { status: 502, message: "an empty reply" } }
|
|
1574
|
+
if (head.done) return book.done(), { error: { status: 502, message: "an empty reply" } }
|
|
1333
1575
|
if (head.value.error) {
|
|
1334
1576
|
await wrapped.return()
|
|
1335
1577
|
return { error: head.value.error }
|
|
@@ -1344,18 +1586,28 @@ async function runOnce({ tok, base, id, tools, conv, convID, signal, maxMode, pa
|
|
|
1344
1586
|
|
|
1345
1587
|
// decode follows the server's messages into the answer's pieces until the
|
|
1346
1588
|
// turn ends, or the model has made its tool calls.
|
|
1347
|
-
async function* decode(stream, { send, blobs, tools, estimate }) {
|
|
1589
|
+
async function* decode(stream, { send, blobs, tools, estimate, close, stepUsage, owedUsage }) {
|
|
1348
1590
|
let calls = 0
|
|
1591
|
+
// ended: Cursor said what the turn used
|
|
1592
|
+
let ended = false
|
|
1349
1593
|
let listed = 0
|
|
1350
1594
|
let said = 0
|
|
1351
1595
|
// wrote: the reply's text alone; a turn of thinking only says nothing
|
|
1352
1596
|
let wrote = 0
|
|
1353
1597
|
let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0 }
|
|
1354
|
-
|
|
1355
|
-
|
|
1598
|
+
// counted: the step's usage is the dashboard's events, its own counted
|
|
1599
|
+
// with it or by a later step (ledger), never guessed
|
|
1600
|
+
let counted = false
|
|
1601
|
+
// finish is the step's end, with what earlier steps owed (more), which
|
|
1602
|
+
// is no reason not to guess the step's own
|
|
1603
|
+
const finish = (more) => {
|
|
1604
|
+
if (!counted && !usage.output) usage.output = Math.floor((said + 3) / 4)
|
|
1356
1605
|
// a guess only when Cursor counted nothing: a prompt read whole from
|
|
1357
|
-
// the cache leaves no uncached rest, and is no reason to guess
|
|
1358
|
-
|
|
1606
|
+
// the cache leaves no uncached rest, and is no reason to guess; nor is
|
|
1607
|
+
// a prompt counted from the events
|
|
1608
|
+
if (!counted && !usage.input && !usage.cacheRead && !usage.cacheWrite) usage.input = estimate
|
|
1609
|
+
// the events' output is their steps', counted here as their prompt is
|
|
1610
|
+
if (more) for (const k of ["input", "output", "cacheRead", "cacheWrite"]) usage[k] += more[k]
|
|
1359
1611
|
const prompt = usage.input + usage.cacheRead + usage.cacheWrite
|
|
1360
1612
|
return {
|
|
1361
1613
|
stop: calls > 0 ? "tool_calls" : "stop",
|
|
@@ -1368,6 +1620,19 @@ async function* decode(stream, { send, blobs, tools, estimate }) {
|
|
|
1368
1620
|
},
|
|
1369
1621
|
}
|
|
1370
1622
|
}
|
|
1623
|
+
// settled is finish once a step that called tools has its usage: the
|
|
1624
|
+
// dashboard's count, the Run closed first, when the turn didn't end (or
|
|
1625
|
+
// owed); and with what the conversation's earlier steps owed, which a
|
|
1626
|
+
// step that ends the turn waits for
|
|
1627
|
+
const settled = async () => {
|
|
1628
|
+
if (calls > 0 && !ended && stepUsage) {
|
|
1629
|
+
close?.()
|
|
1630
|
+
const u = await stepUsage().catch(() => null)
|
|
1631
|
+
if (u) usage = u
|
|
1632
|
+
counted = u === false || !!u
|
|
1633
|
+
}
|
|
1634
|
+
return finish(owedUsage ? await owedUsage(calls === 0).catch(() => null) : null)
|
|
1635
|
+
}
|
|
1371
1636
|
const closeExec = (id) => send(pb().bytes(5, pb().bytes(1, pb().varint(1, id))).done())
|
|
1372
1637
|
try {
|
|
1373
1638
|
for await (const f of stream) {
|
|
@@ -1375,7 +1640,7 @@ async function* decode(stream, { send, blobs, tools, estimate }) {
|
|
|
1375
1640
|
const e = failure(200, f.data)
|
|
1376
1641
|
if (e.status < 200 || e.status > 299) yield { error: e }
|
|
1377
1642
|
else if (wrote === 0 && calls === 0) yield { error: { status: 502, message: "an empty reply" } }
|
|
1378
|
-
else yield
|
|
1643
|
+
else yield await settled()
|
|
1379
1644
|
return
|
|
1380
1645
|
}
|
|
1381
1646
|
for (const m of fields(f.data)) {
|
|
@@ -1405,17 +1670,18 @@ async function* decode(stream, { send, blobs, tools, estimate }) {
|
|
|
1405
1670
|
}
|
|
1406
1671
|
case 14: // turn ended, with what it used
|
|
1407
1672
|
usage = usageOf(uf)
|
|
1673
|
+
ended = true
|
|
1408
1674
|
if (calls === 0) {
|
|
1409
1675
|
// nothing written and nothing called: an empty reply,
|
|
1410
1676
|
// which the client asks again, not a finished turn
|
|
1411
|
-
yield wrote === 0 ? { error: { status: 502, message: "an empty reply" } } :
|
|
1677
|
+
yield wrote === 0 ? { error: { status: 502, message: "an empty reply" } } : await settled()
|
|
1412
1678
|
return
|
|
1413
1679
|
}
|
|
1414
1680
|
break
|
|
1415
1681
|
case 27: // how many tool calls the step makes, sent before them
|
|
1416
1682
|
listed = pbNum(uf, 1)
|
|
1417
1683
|
if (listed > 0 && calls >= listed) {
|
|
1418
|
-
yield
|
|
1684
|
+
yield await settled()
|
|
1419
1685
|
return
|
|
1420
1686
|
}
|
|
1421
1687
|
break
|
|
@@ -1431,7 +1697,7 @@ async function* decode(stream, { send, blobs, tools, estimate }) {
|
|
|
1431
1697
|
}
|
|
1432
1698
|
if (listed > 0 && calls >= listed) {
|
|
1433
1699
|
// every call made: the caller runs them
|
|
1434
|
-
yield
|
|
1700
|
+
yield await settled()
|
|
1435
1701
|
return
|
|
1436
1702
|
}
|
|
1437
1703
|
break
|
|
@@ -1457,13 +1723,13 @@ async function* decode(stream, { send, blobs, tools, estimate }) {
|
|
|
1457
1723
|
}
|
|
1458
1724
|
}
|
|
1459
1725
|
if (calls > 0) {
|
|
1460
|
-
yield
|
|
1726
|
+
yield await settled()
|
|
1461
1727
|
return
|
|
1462
1728
|
}
|
|
1463
1729
|
yield { error: { status: 502, message: "the reply broke off: EOF" } }
|
|
1464
1730
|
} catch (e) {
|
|
1465
1731
|
if (calls > 0 && /EOF/.test(e?.message ?? "")) {
|
|
1466
|
-
yield
|
|
1732
|
+
yield await settled()
|
|
1467
1733
|
return
|
|
1468
1734
|
}
|
|
1469
1735
|
yield { error: { status: 502, message: "the reply broke off: " + (e?.message ?? e) } }
|
|
@@ -1733,4 +1999,4 @@ export async function CursorAuthPlugin() {
|
|
|
1733
1999
|
}
|
|
1734
2000
|
|
|
1735
2001
|
// for tests
|
|
1736
|
-
export const _internal = { catalog, usable, maxRequired, conversationID, usageOf, sessionOf, SESSION, errorResponse, kept, poolBase, splitID, families, byEffort, offered, modelID, conversation, buildRun, decode, exec, fields, pb, pbValue, pbAny, frame, frames, failure, toolsOf, expiry }
|
|
2002
|
+
export const _internal = { STEP_WAIT, stepUsage, ledger, owed, flights, misses, unreadable, catalog, usable, maxRequired, conversationID, usageOf, sessionOf, SESSION, errorResponse, kept, poolBase, splitID, families, byEffort, offered, modelID, conversation, buildRun, decode, exec, fields, pb, pbValue, pbAny, frame, frames, failure, toolsOf, expiry }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-cursor-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
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",
|