@magpie-community/opencode-workbuddy-auth 0.1.29 → 0.1.31
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 +21 -2
- package/index.mjs +213 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,6 +56,22 @@ Desktop's sign-in is refreshed only when the app hasn't refreshed it
|
|
|
56
56
|
itself, and the new token is kept in memory, never written back, as magpie's
|
|
57
57
|
built-in WorkBuddy does.
|
|
58
58
|
|
|
59
|
+
A chat or usage read WorkBuddy answers 401 (since 0.1.31) is renewed once,
|
|
60
|
+
whatever the token's time, and sent once more. A token another process
|
|
61
|
+
saved since is taken first instead of spending the refresh token again. A
|
|
62
|
+
desktop sign-in is never renewed for it: the app's file is read again, and
|
|
63
|
+
its token is used if the app signed in again. A 401 again, or no new
|
|
64
|
+
token, ends in a 401 that says to sign in again (Providers → WorkBuddy →
|
|
65
|
+
this account's Sign in again in magpie; WorkBuddy desktop for a desktop
|
|
66
|
+
sign-in). The plugin marks the account expired in magpie
|
|
67
|
+
(`X-Magpie-Sign-In: expired`, usage's `signIn`) only on WorkBuddy's word
|
|
68
|
+
that the sign-in is gone: the refresh token refused (401, `invalid_grant`),
|
|
69
|
+
or the access token refused (`WWW-Authenticate: … error="invalid_token"`)
|
|
70
|
+
with no refresh token left to renew it with or just after renewing. A
|
|
71
|
+
failed renewal for another reason, or a bare 401 without `invalid_token`,
|
|
72
|
+
leaves the account as it is; a desktop sign-in is never marked. A 401
|
|
73
|
+
after a renewal takes a mark off (`renewed`).
|
|
74
|
+
|
|
59
75
|
## Requests
|
|
60
76
|
|
|
61
77
|
Chats are chat completions (`@ai-sdk/openai-compatible`) at `<API>/chat/completions`.
|
|
@@ -92,8 +108,11 @@ WorkBuddy's default system prompt in its place: WorkBuddy checks only the
|
|
|
92
108
|
system and assistant messages, and the line it matched is then in neither.
|
|
93
109
|
A system message later in the chat becomes a user message where it stands.
|
|
94
110
|
The plugin remembers each refused system prompt while it is loaded, so that
|
|
95
|
-
agent's next chats go moved at once.
|
|
96
|
-
|
|
111
|
+
agent's next chats go moved at once. The move can't reach an assistant
|
|
112
|
+
message, so words the model quotes back into one keep that chat refused
|
|
113
|
+
until they are in the list reworded in every message (since 0.1.30, the "Web GUI whose URL the runtime
|
|
114
|
+
context names" line, #291); otherwise only a new chat clears it. A refusal
|
|
115
|
+
that stands after the move ends in a hint that names no one client
|
|
97
116
|
(since 0.1.28): WorkBuddy refused words in this client's prompt; use it from
|
|
98
117
|
Hermes, OpenCode or Pi, or add another provider to the group. Neither
|
|
99
118
|
refusal rests the account.
|
package/index.mjs
CHANGED
|
@@ -467,6 +467,11 @@ const FLAGGED = [
|
|
|
467
467
|
// WorkBuddy refuses as 11128 (magpie-community/plugins#279; the same
|
|
468
468
|
// refusal traced in smart-open/TraeWorkAssistant#125).
|
|
469
469
|
[/You are an AI agent powered by DeepSeek Harness\.?/gi, "You are an AI agent that helps the user with programming tasks."],
|
|
470
|
+
// A line WorkBuddy refuses in a long DeepSeek Harness chat, measured 400
|
|
471
|
+
// then 200 with this wording by tearslee (magpie-community/plugins#291). The
|
|
472
|
+
// model can quote it back into an assistant message, which the move to
|
|
473
|
+
// the user's instructions can't reach, so it is reworded in every message.
|
|
474
|
+
[/Web GUI whose URL the runtime context names\.\s+When the user refers/gi, "Web GUI whose URL the runtime context names. The interface, when the user refers"],
|
|
470
475
|
]
|
|
471
476
|
|
|
472
477
|
function unflagged(text) {
|
|
@@ -1118,6 +1123,8 @@ function readDesktop(site) {
|
|
|
1118
1123
|
uid: acct.uid,
|
|
1119
1124
|
...(typeof acct.enterpriseId === "string" && acct.enterpriseId.trim() ? { enterpriseId: acct.enterpriseId.trim() } : {}),
|
|
1120
1125
|
name: acct.nickname || acct.phoneNumber || acct.uid,
|
|
1126
|
+
// what the errors below say to do goes by it: sign in to the app again
|
|
1127
|
+
source: "desktop",
|
|
1121
1128
|
}
|
|
1122
1129
|
} catch {
|
|
1123
1130
|
return null
|
|
@@ -1232,23 +1239,143 @@ async function unanswered(site, a, body, err, signal) {
|
|
|
1232
1239
|
const size = byteLength(body)
|
|
1233
1240
|
if (size <= EDGE_READS) throw err
|
|
1234
1241
|
let status = 0
|
|
1242
|
+
let challenge = ""
|
|
1235
1243
|
try {
|
|
1236
1244
|
const res = await meterAsked(site, a, "/billing/meter/get-user-resource-summary", {}, AbortSignal.timeout(20000))
|
|
1237
1245
|
status = res.status
|
|
1246
|
+
challenge = res.headers.get("www-authenticate") ?? ""
|
|
1238
1247
|
await res.body?.cancel()
|
|
1239
1248
|
} catch {
|
|
1240
1249
|
throw err
|
|
1241
1250
|
}
|
|
1242
1251
|
if (status !== 401) throw err
|
|
1243
1252
|
const mb = (size / (1024 * 1024)).toFixed(1)
|
|
1253
|
+
const note = `It closes the connection on a refused request over 2 MiB (this one was ${mb} MiB) instead of answering, which is why it read as ECONNRESET.`
|
|
1244
1254
|
const message =
|
|
1245
|
-
`${site.name} refused this account's sign-in (401).
|
|
1246
|
-
`(this one was ${mb} MiB) instead of answering, which is why it read as ECONNRESET. ` +
|
|
1255
|
+
`${site.name} refused this account's sign-in (401). ${note} ` +
|
|
1247
1256
|
(a.source === "desktop" ? `Sign in to ${site.name} desktop again.` : `Sign in to ${site.name} again in magpie.`)
|
|
1257
|
+
// the billing API's challenge goes with it: it says whether the token
|
|
1258
|
+
// itself was refused (invalidToken), as the chat's own 401 would have
|
|
1259
|
+
const headers = { "content-type": "application/json", ...(challenge ? { "www-authenticate": challenge } : {}) }
|
|
1260
|
+
const res = new Response(JSON.stringify({ error: { message, type: "authentication_error", code: 401 } }), {
|
|
1261
|
+
status: 401,
|
|
1262
|
+
statusText: "Unauthorized",
|
|
1263
|
+
headers,
|
|
1264
|
+
})
|
|
1265
|
+
edgeNotes.set(res, note)
|
|
1266
|
+
return res
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
// edgeNotes is what unanswered's 401s say of the reset, for signedOut to
|
|
1270
|
+
// say again when the sign-in can't be renewed.
|
|
1271
|
+
const edgeNotes = new WeakMap()
|
|
1272
|
+
|
|
1273
|
+
// ---- a refused sign-in ----------------------------------------------------------
|
|
1274
|
+
|
|
1275
|
+
// invalidToken says res is WorkBuddy refusing the access token itself. Its
|
|
1276
|
+
// front door (APISIX, on both sites) answers a token it doesn't take with
|
|
1277
|
+
// 401 and WWW-Authenticate: Bearer realm="copilot", error="invalid_token",
|
|
1278
|
+
// on a chat (streamed or not) and on the billing API alike; measured on
|
|
1279
|
+
// www.workbuddy.ai on 2026-10-23 with a real account's token cut short,
|
|
1280
|
+
// and with "garbage". (Its /v3/config answers 200 to any token, so the
|
|
1281
|
+
// models list never meets a 401.) A 401 without it is not taken as the
|
|
1282
|
+
// sign-in's: WorkBuddy answers some paths with a bare 401 to an accepted
|
|
1283
|
+
// sign-in (magpie's check-in, モモコ on Discord).
|
|
1284
|
+
const invalidToken = (res) => res?.status === 401 && /invalid_token/i.test(res.headers.get("www-authenticate") ?? "")
|
|
1285
|
+
|
|
1286
|
+
// refreshRefused says e is WorkBuddy turning the refresh token itself
|
|
1287
|
+
// away: HTTP 401 with code 12153, "refresh token failed:400 Bad Request:
|
|
1288
|
+
// invalid_grant: Invalid refresh token" for a spent or unknown one, or
|
|
1289
|
+
// "10000:token format error" (measured 2026-10-23). Anything else (a 5xx,
|
|
1290
|
+
// a timeout) says nothing of the sign-in.
|
|
1291
|
+
const refreshRefused = (e) => e instanceof WBError && (e.status === 401 || /invalid_grant/i.test(e.message))
|
|
1292
|
+
|
|
1293
|
+
// reissued is the sign-in to send a request WorkBuddy refused a's access
|
|
1294
|
+
// token for (401) once more with, before that token's time: WorkBuddy can
|
|
1295
|
+
// refuse one long before it ends (a browser sign-in's lasts as long as
|
|
1296
|
+
// its refresh token, about 340 days), and renewing only by time left such
|
|
1297
|
+
// an account refused on every request for months. It is
|
|
1298
|
+
// { a }: a sign-in with another access token, to send the request with
|
|
1299
|
+
// once more;
|
|
1300
|
+
// { why, kind }: none to be had, why, and of what kind: "refresh
|
|
1301
|
+
// refused" (WorkBuddy turned the refresh token away too), "nothing to
|
|
1302
|
+
// renew with" (no refresh token, or one past its end), or "unknown"
|
|
1303
|
+
// (the refresh failed for another reason, the desktop's token is the
|
|
1304
|
+
// one refused).
|
|
1305
|
+
// began is the access token the request read at its start: a stored one
|
|
1306
|
+
// that isn't it was saved since by someone else (magpie's renewal,
|
|
1307
|
+
// another of its processes), and is taken rather than renewing again.
|
|
1308
|
+
//
|
|
1309
|
+
// A desktop sign-in is the app's: its file is read again, and what was
|
|
1310
|
+
// renewed here (desktopHeld) is let go, but its refresh token is never
|
|
1311
|
+
// spent for a refusal, which would sign the app itself out (see
|
|
1312
|
+
// desktopSignIn).
|
|
1313
|
+
async function reissued(site, client, getAuth, a, began) {
|
|
1314
|
+
if (a.source === "desktop") {
|
|
1315
|
+
desktopHeld.delete(site.id + "|" + a.uid)
|
|
1316
|
+
const d = readDesktop(site)
|
|
1317
|
+
if (!d) return { why: notDesktop(site), kind: "unknown" }
|
|
1318
|
+
if (d.access !== a.access) {
|
|
1319
|
+
desktopHeld.set(site.id + "|" + d.uid, d)
|
|
1320
|
+
return { a: d }
|
|
1321
|
+
}
|
|
1322
|
+
return { why: `${site.name} desktop's own sign-in is the one refused`, kind: "unknown" }
|
|
1323
|
+
}
|
|
1324
|
+
const newer = async () => {
|
|
1325
|
+
const now = await getAuth().catch(() => null)
|
|
1326
|
+
return now?.type === "oauth" && now.source !== "desktop" && now.access && now.access !== began && now.access !== a.access ? now : null
|
|
1327
|
+
}
|
|
1328
|
+
const saved = await newer()
|
|
1329
|
+
if (saved) return { a: saved }
|
|
1330
|
+
if (!refreshable(a)) return { why: a.refresh ? "its refresh token has ended" : "there is no refresh token to renew it with", kind: "nothing to renew with" }
|
|
1331
|
+
try {
|
|
1332
|
+
const next = await renew(site, a, async (n) => {
|
|
1333
|
+
try {
|
|
1334
|
+
await client?.auth?.set?.({ path: { id: site.id }, body: n })
|
|
1335
|
+
} catch {}
|
|
1336
|
+
})
|
|
1337
|
+
return next.access !== a.access ? { a: next } : { why: "renewing it gave the same token", kind: "unknown" }
|
|
1338
|
+
} catch (e) {
|
|
1339
|
+
if (refreshRefused(e)) {
|
|
1340
|
+
// spent by someone else, who saved what it gave
|
|
1341
|
+
const saved = await newer()
|
|
1342
|
+
if (saved) return { a: saved }
|
|
1343
|
+
return { why: `it refused the refresh token too (${e.said ?? e.message})`, kind: "refresh refused" }
|
|
1344
|
+
}
|
|
1345
|
+
return { why: `renewing it failed: ${e?.said ?? e?.message ?? e}`, kind: "unknown" }
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// gone says whether a sign-in reissued (r) couldn't renew is gone, on
|
|
1350
|
+
// WorkBuddy's word: it turned the refresh token away, or refused the access
|
|
1351
|
+
// token itself (tokenRefused, invalidToken) with none to renew it with. A
|
|
1352
|
+
// desktop sign-in never is: the app's file is read on each request, and
|
|
1353
|
+
// signing in to the app again is all it takes.
|
|
1354
|
+
const gone = (a, r, tokenRefused) =>
|
|
1355
|
+
a.source !== "desktop" && (r.kind === "refresh refused" || (r.kind === "nothing to renew with" && !!tokenRefused))
|
|
1356
|
+
|
|
1357
|
+
// refusedText says WorkBuddy refused a's sign-in, why it wasn't renewed,
|
|
1358
|
+
// and what to do.
|
|
1359
|
+
function refusedText(site, a, why, note) {
|
|
1360
|
+
return (
|
|
1361
|
+
`${site.name} refused this account's sign-in (401)${why ? "; " + why : ""}. ${note ? note + " " : ""}` +
|
|
1362
|
+
(a.source === "desktop"
|
|
1363
|
+
? `Sign in to ${site.name} desktop again; magpie reads its sign-in on the next request.`
|
|
1364
|
+
: `Sign in to it again in magpie: Providers → ${site.name} → this account's Sign in again.`)
|
|
1365
|
+
)
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
// signedOut is the answer to a request WorkBuddy refused this account's
|
|
1369
|
+
// sign-in for, renewed or not: a 401 that says so and what to do, which
|
|
1370
|
+
// WorkBuddy's own (an HTML page, its {code, msg}) doesn't. expired marks
|
|
1371
|
+
// the account in magpie (X-Magpie-Sign-In), out of routing and "sign in
|
|
1372
|
+
// again" on its row, till it is signed in again; else it is kept.
|
|
1373
|
+
function signedOut(site, a, why, expired, note) {
|
|
1374
|
+
const message = refusedText(site, a, why, note)
|
|
1248
1375
|
return new Response(JSON.stringify({ error: { message, type: "authentication_error", code: 401 } }), {
|
|
1249
1376
|
status: 401,
|
|
1250
1377
|
statusText: "Unauthorized",
|
|
1251
|
-
headers: { "content-type": "application/json" },
|
|
1378
|
+
headers: { "content-type": "application/json", "X-Magpie-Sign-In": expired ? "expired" : "kept" },
|
|
1252
1379
|
})
|
|
1253
1380
|
}
|
|
1254
1381
|
|
|
@@ -1262,9 +1389,12 @@ async function meter(site, a, path, body = {}) {
|
|
|
1262
1389
|
} catch {}
|
|
1263
1390
|
const code = Number.parseInt(env.code, 10) || 0
|
|
1264
1391
|
if (!res.ok) {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1392
|
+
// the status and whether the token itself was refused go with it, for
|
|
1393
|
+
// usage's one renewal on a 401 (reissued)
|
|
1394
|
+
const said = { status: res.status, invalidToken: invalidToken(res) }
|
|
1395
|
+
if (code) throw Object.assign(new Error(env.msg || `error ${code}`), said)
|
|
1396
|
+
if (env.msg) throw Object.assign(new Error(env.msg), said)
|
|
1397
|
+
throw Object.assign(new Error(statusText(res.status)), said)
|
|
1268
1398
|
}
|
|
1269
1399
|
if (code) throw new Error(env.msg || `error ${code}`)
|
|
1270
1400
|
return env.data ?? null
|
|
@@ -1666,9 +1796,9 @@ function makePlugin(site) {
|
|
|
1666
1796
|
baseURL: api(site),
|
|
1667
1797
|
apiKey: "",
|
|
1668
1798
|
async fetch(input, init) {
|
|
1669
|
-
|
|
1670
|
-
if (
|
|
1671
|
-
a = await current(site, client,
|
|
1799
|
+
const stored = await getAuth()
|
|
1800
|
+
if (stored?.type !== "oauth") throw new Error(`${site.name} isn't signed in`)
|
|
1801
|
+
let a = await current(site, client, stored)
|
|
1672
1802
|
const req = input instanceof Request ? input : null
|
|
1673
1803
|
const headers = new Headers(init?.headers ?? req?.headers)
|
|
1674
1804
|
headers.delete("authorization")
|
|
@@ -1682,8 +1812,44 @@ function makePlugin(site) {
|
|
|
1682
1812
|
const fit = fitted(body)
|
|
1683
1813
|
body = normalizeToolChoice(fit.body)
|
|
1684
1814
|
attend(headers, body, session)
|
|
1685
|
-
const
|
|
1815
|
+
const post = (b) =>
|
|
1686
1816
|
fetch(req ? req.url : input, { ...init, method: init?.method ?? req?.method, headers, body: b }).catch((e) => unanswered(site, a, b, e, init?.signal))
|
|
1817
|
+
// a 401 is answered once, on whichever try meets it: by a
|
|
1818
|
+
// sign-in with another access token (reissued), the request
|
|
1819
|
+
// sent once more with it, never again after that; else, or
|
|
1820
|
+
// when that is refused too, by signedOut. The body is a
|
|
1821
|
+
// string here (or bytes), which goes again as it is; one that
|
|
1822
|
+
// can't be read twice isn't sent again.
|
|
1823
|
+
//
|
|
1824
|
+
// The account is marked (expired) only on WorkBuddy's word
|
|
1825
|
+
// that the sign-in is gone: it turned the refresh token away,
|
|
1826
|
+
// or refused the access token itself (invalidToken) with none
|
|
1827
|
+
// to renew it with, or refused even the token just renewed. A
|
|
1828
|
+
// desktop sign-in is never marked: the app's file is read on
|
|
1829
|
+
// each request, so signing in to the app again is enough.
|
|
1830
|
+
let renewed = false
|
|
1831
|
+
const ours = new WeakSet() // the answers signedOut gave, passed on as they are
|
|
1832
|
+
const out = (why, expired, res) => {
|
|
1833
|
+
const r = signedOut(site, a, why, expired && a.source !== "desktop", edgeNotes.get(res))
|
|
1834
|
+
ours.add(r)
|
|
1835
|
+
return r
|
|
1836
|
+
}
|
|
1837
|
+
const resendable = (b) => b == null || typeof b === "string" || b instanceof ArrayBuffer || ArrayBuffer.isView(b)
|
|
1838
|
+
const send = async (b) => {
|
|
1839
|
+
const res = await post(b)
|
|
1840
|
+
if (res.status !== 401) return res
|
|
1841
|
+
if (renewed) return out("it refused the token renewed for it too", invalidToken(res), res)
|
|
1842
|
+
if (!resendable(b)) return res
|
|
1843
|
+
await res.body?.cancel().catch(() => {})
|
|
1844
|
+
const r = await reissued(site, client, getAuth, a, stored.access)
|
|
1845
|
+
if (!r.a) return out(r.why, gone(a, r, invalidToken(res)), res)
|
|
1846
|
+
renewed = true
|
|
1847
|
+
a = r.a
|
|
1848
|
+
sign(site, a, headers)
|
|
1849
|
+
const again = await post(b)
|
|
1850
|
+
if (again.status !== 401) return again
|
|
1851
|
+
return out("it refused the token renewed for it too", invalidToken(again), again)
|
|
1852
|
+
}
|
|
1687
1853
|
// a model known to turn tool results then the user's message away
|
|
1688
1854
|
// gets the empty reply between them up front (see endsAfterTools)
|
|
1689
1855
|
const model = chatOf(body)?.model
|
|
@@ -1711,20 +1877,43 @@ function makePlugin(site) {
|
|
|
1711
1877
|
res = new Response(text, { status: res.status, statusText: res.statusText, headers: res.headers })
|
|
1712
1878
|
}
|
|
1713
1879
|
}
|
|
1714
|
-
|
|
1880
|
+
if (ours.has(res)) return res
|
|
1881
|
+
// renewed here: a mark the account had comes off
|
|
1882
|
+
return res.status >= 400 ? explained(res) : kept(await renamed(res, fit.names), renewed ? "renewed" : "kept")
|
|
1715
1883
|
},
|
|
1716
1884
|
}
|
|
1717
1885
|
},
|
|
1718
1886
|
// the account's credits, as magpie's built-in showed them; each
|
|
1719
1887
|
// read keeps the sign-in (signIn), as the built-in's neither marked
|
|
1720
|
-
// an account lapsed nor cleared it
|
|
1888
|
+
// an account lapsed nor cleared it, but for a 401 (gone, below)
|
|
1721
1889
|
async usage(getAuth) {
|
|
1722
1890
|
const auth = await getAuth()
|
|
1723
1891
|
if (auth?.type !== "oauth" || !(auth.access || auth.source)) return { error: "not signed in", signIn: "kept" }
|
|
1724
1892
|
try {
|
|
1725
|
-
|
|
1893
|
+
let a = await current(site, client, auth)
|
|
1726
1894
|
// the meter has no /v2 prefix, on either site
|
|
1727
|
-
const
|
|
1895
|
+
const SUMMARY = "/billing/meter/get-user-resource-summary"
|
|
1896
|
+
// a 401 is answered as a chat's is: once, by a sign-in with
|
|
1897
|
+
// another access token, else the card says WorkBuddy refused
|
|
1898
|
+
// the sign-in and what to do; renewed, a mark comes off
|
|
1899
|
+
let said = "kept"
|
|
1900
|
+
let sum
|
|
1901
|
+
try {
|
|
1902
|
+
sum = await meter(site, a, SUMMARY)
|
|
1903
|
+
} catch (e) {
|
|
1904
|
+
if (e?.status !== 401) throw e
|
|
1905
|
+
const r = await reissued(site, client, getAuth, a, auth.access)
|
|
1906
|
+
if (!r.a) return { error: refusedText(site, a, r.why), signIn: gone(a, r, e.invalidToken) ? "expired" : "kept" }
|
|
1907
|
+
a = r.a
|
|
1908
|
+
try {
|
|
1909
|
+
sum = await meter(site, a, SUMMARY)
|
|
1910
|
+
} catch (e) {
|
|
1911
|
+
if (e?.status !== 401) throw e
|
|
1912
|
+
const why = "it refused the token renewed for it too"
|
|
1913
|
+
return { error: refusedText(site, a, why), signIn: a.source !== "desktop" && e.invalidToken ? "expired" : "kept" }
|
|
1914
|
+
}
|
|
1915
|
+
said = "renewed"
|
|
1916
|
+
}
|
|
1728
1917
|
const out = usageOf(sum, auth.plan)
|
|
1729
1918
|
// an enterprise account's summary has no packages: its credits
|
|
1730
1919
|
// are the enterprise meter's (#1904). A personal account's
|
|
@@ -1735,7 +1924,7 @@ function makePlugin(site) {
|
|
|
1735
1924
|
try {
|
|
1736
1925
|
ent = enterpriseUsageOf(await meter(site, a, "/billing/meter/get-enterprise-user-usage"))
|
|
1737
1926
|
} catch {}
|
|
1738
|
-
if (ent) return { plan: auth.plan || "Enterprise", windows: await spareFree(site, a, [ent]), signIn:
|
|
1927
|
+
if (ent) return { plan: auth.plan || "Enterprise", windows: await spareFree(site, a, [ent]), signIn: said }
|
|
1739
1928
|
}
|
|
1740
1929
|
// when the credits left run out, for routing to spend them first;
|
|
1741
1930
|
// a list that can't be read leaves it unknown, the card stands
|
|
@@ -1746,7 +1935,7 @@ function makePlugin(site) {
|
|
|
1746
1935
|
} catch {}
|
|
1747
1936
|
}
|
|
1748
1937
|
await spareFree(site, a, out.windows)
|
|
1749
|
-
return { ...out, signIn:
|
|
1938
|
+
return { ...out, signIn: said }
|
|
1750
1939
|
} catch (e) {
|
|
1751
1940
|
return { error: e?.message ?? String(e), signIn: "kept" }
|
|
1752
1941
|
}
|
|
@@ -1801,17 +1990,19 @@ const EXHAUSTED_HINT =
|
|
|
1801
1990
|
// another error, and insufficient_quota would rest the account as out of credit.
|
|
1802
1991
|
const EXHAUSTED_TEXT = /credits exhausted|额度已用尽|["']code["']\s*:\s*["']?14018(?![\d.])/i
|
|
1803
1992
|
|
|
1804
|
-
// kept is an answer that went through, saying the sign-in is kept
|
|
1805
|
-
// built-in never cleared a WorkBuddy account's mark, as it never set one
|
|
1806
|
-
|
|
1993
|
+
// kept is an answer that went through, saying the sign-in is kept (the
|
|
1994
|
+
// built-in never cleared a WorkBuddy account's mark, as it never set one),
|
|
1995
|
+
// or "renewed" when it went through on a sign-in renewed for a 401, which
|
|
1996
|
+
// takes a mark off (see signedOut).
|
|
1997
|
+
function kept(res, said = "kept") {
|
|
1807
1998
|
const headers = new Headers(res.headers)
|
|
1808
|
-
headers.set("X-Magpie-Sign-In",
|
|
1999
|
+
headers.set("X-Magpie-Sign-In", said)
|
|
1809
2000
|
return new Response(res.body, { status: res.status, statusText: res.statusText, headers })
|
|
1810
2001
|
}
|
|
1811
2002
|
|
|
1812
2003
|
// explained is res with REFUSED_HINT, RESTRICTED_HINT or EXHAUSTED_HINT added to that refusal's message.
|
|
1813
|
-
// Each refusal says the sign-in is kept:
|
|
1814
|
-
//
|
|
2004
|
+
// Each refusal says the sign-in is kept: a 401 never comes here, it is
|
|
2005
|
+
// signedOut's.
|
|
1815
2006
|
async function explained(res) {
|
|
1816
2007
|
const text = await res.text()
|
|
1817
2008
|
const again = (b) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-workbuddy-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
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",
|