@magpie-community/opencode-kiro-auth 0.1.1 → 0.1.3
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/index.mjs +27 -15
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -189,6 +189,11 @@ async function saveBack(c) {
|
|
|
189
189
|
// ---- tokens --------------------------------------------------------------------
|
|
190
190
|
|
|
191
191
|
const EXPIRED = "Kiro's sign-in has expired; sign in again"
|
|
192
|
+
|
|
193
|
+
// gone is an error saying the sign-in itself is gone — refused, or never
|
|
194
|
+
// there — which alone answers a request with the 401 magpie marks the
|
|
195
|
+
// account lapsed on; a refresh that timed out or met a 5xx may pass.
|
|
196
|
+
const gone = (msg) => Object.assign(new Error(msg), { gone: true })
|
|
192
197
|
const fresh = (c) => c.method === "apikey" || !c.expires || c.expires - Date.now() > 2 * 60 * 1000
|
|
193
198
|
|
|
194
199
|
async function post(url, contentType, body, headers = {}) {
|
|
@@ -201,7 +206,7 @@ async function post(url, contentType, body, headers = {}) {
|
|
|
201
206
|
}
|
|
202
207
|
const text = await res.text()
|
|
203
208
|
if (!res.ok) {
|
|
204
|
-
if ([400, 401, 403].includes(res.status)) throw
|
|
209
|
+
if ([400, 401, 403].includes(res.status)) throw gone(EXPIRED)
|
|
205
210
|
throw new Error(`refreshing Kiro's sign-in: ${res.status}`)
|
|
206
211
|
}
|
|
207
212
|
return JSON.parse(text)
|
|
@@ -218,7 +223,7 @@ async function refresh(c) {
|
|
|
218
223
|
if (n && n.dbKey === c.dbKey && n.access !== c.access && fresh(n)) return n
|
|
219
224
|
}
|
|
220
225
|
}
|
|
221
|
-
if (!c.refresh) throw
|
|
226
|
+
if (!c.refresh) throw gone(EXPIRED)
|
|
222
227
|
let out
|
|
223
228
|
switch (c.method) {
|
|
224
229
|
case "social": {
|
|
@@ -234,16 +239,16 @@ async function refresh(c) {
|
|
|
234
239
|
break
|
|
235
240
|
}
|
|
236
241
|
case "external-idp": {
|
|
237
|
-
if (!c.tokenURL) throw
|
|
242
|
+
if (!c.tokenURL) throw gone(EXPIRED + " with `kiro-cli login`")
|
|
238
243
|
const form = new URLSearchParams({ grant_type: "refresh_token", client_id: c.clientId ?? "", refresh_token: c.refresh })
|
|
239
244
|
const r = await post(c.tokenURL, "application/x-www-form-urlencoded", form.toString())
|
|
240
245
|
out = { access: r.access_token, refresh: r.refresh_token, expiresIn: r.expires_in }
|
|
241
246
|
break
|
|
242
247
|
}
|
|
243
248
|
default:
|
|
244
|
-
throw
|
|
249
|
+
throw gone(EXPIRED)
|
|
245
250
|
}
|
|
246
|
-
if (!out.access) throw
|
|
251
|
+
if (!out.access) throw gone(EXPIRED)
|
|
247
252
|
const n = { ...c, access: out.access, expires: Date.now() + (out.expiresIn > 0 ? out.expiresIn : 3600) * 1000 }
|
|
248
253
|
if (out.refresh) n.refresh = out.refresh
|
|
249
254
|
await saveBack(n)
|
|
@@ -309,7 +314,10 @@ async function profileOf(c) {
|
|
|
309
314
|
body: "{}",
|
|
310
315
|
signal: AbortSignal.timeout(20_000),
|
|
311
316
|
})
|
|
312
|
-
if (!res.ok)
|
|
317
|
+
if (!res.ok) {
|
|
318
|
+
const msg = `Kiro didn't take the API key: ${res.status}`
|
|
319
|
+
throw res.status === 401 || res.status === 403 ? gone(msg) : new Error(msg)
|
|
320
|
+
}
|
|
313
321
|
const arn = (await res.json().catch(() => null))?.profile?.arn
|
|
314
322
|
if (!arn) throw new Error("Kiro didn't say which profile the API key is for")
|
|
315
323
|
return arn
|
|
@@ -411,7 +419,11 @@ function account(client) {
|
|
|
411
419
|
const read = await credOf(auth)
|
|
412
420
|
if (!read) {
|
|
413
421
|
held = null
|
|
414
|
-
throw
|
|
422
|
+
throw gone(
|
|
423
|
+
auth?.source
|
|
424
|
+
? "Kiro isn't signed in; add the Kiro subscription in magpie, sign in with `kiro-cli login` or the Kiro IDE, or save a Kiro API key on the provider"
|
|
425
|
+
: "this Kiro account's sign-in is gone; add it again in magpie",
|
|
426
|
+
)
|
|
415
427
|
}
|
|
416
428
|
let c = held?.id === id ? held.c : null
|
|
417
429
|
if (!c || read.access !== c.access || !fresh(c) || stale) {
|
|
@@ -419,7 +431,7 @@ function account(client) {
|
|
|
419
431
|
if (stale && c && read.access !== c.access && fresh(read)) stale = false // the owner refreshed it already
|
|
420
432
|
c = read
|
|
421
433
|
if (!fresh(c) || stale) {
|
|
422
|
-
if (c.method === "apikey") throw
|
|
434
|
+
if (c.method === "apikey") throw gone("Kiro turned down the API key")
|
|
423
435
|
c = await refresh(c)
|
|
424
436
|
await save(auth, c)
|
|
425
437
|
}
|
|
@@ -712,7 +724,7 @@ const ERROR_TYPES = { 400: "invalid_request_error", 401: "authentication_error",
|
|
|
712
724
|
404: "not_found_error", 413: "request_too_large", 429: "rate_limit_error", 503: "overloaded_error", 529: "overloaded_error" }
|
|
713
725
|
const errorType = (status) => ERROR_TYPES[status] ?? "api_error"
|
|
714
726
|
const errorResponse = (status, message) =>
|
|
715
|
-
new Response(JSON.stringify({ type: "error", error: { type: errorType(status), message
|
|
727
|
+
new Response(JSON.stringify({ type: "error", error: { type: errorType(status), message } }), {
|
|
716
728
|
status,
|
|
717
729
|
headers: { "Content-Type": "application/json" },
|
|
718
730
|
})
|
|
@@ -1033,7 +1045,7 @@ async function reply(it, model, stream) {
|
|
|
1033
1045
|
break
|
|
1034
1046
|
case "error":
|
|
1035
1047
|
close()
|
|
1036
|
-
ctl.enqueue(sse("error", { error: { type: "api_error", message:
|
|
1048
|
+
ctl.enqueue(sse("error", { error: { type: "api_error", message: e.text } }))
|
|
1037
1049
|
return ctl.close()
|
|
1038
1050
|
case "stop":
|
|
1039
1051
|
close()
|
|
@@ -1058,7 +1070,7 @@ async function generate(creds, auth, req, signal) {
|
|
|
1058
1070
|
try {
|
|
1059
1071
|
a = await creds(auth)
|
|
1060
1072
|
} catch (e) {
|
|
1061
|
-
return errorResponse(401, e.message)
|
|
1073
|
+
return errorResponse(e?.gone ? 401 : 502, e.message)
|
|
1062
1074
|
}
|
|
1063
1075
|
let res
|
|
1064
1076
|
try {
|
|
@@ -1072,7 +1084,7 @@ async function generate(creds, auth, req, signal) {
|
|
|
1072
1084
|
try {
|
|
1073
1085
|
a = await creds(auth, true)
|
|
1074
1086
|
} catch (e) {
|
|
1075
|
-
return errorResponse(401, e.message)
|
|
1087
|
+
return errorResponse(e?.gone ? 401 : 502, e.message)
|
|
1076
1088
|
}
|
|
1077
1089
|
try {
|
|
1078
1090
|
res = await sendKiro(a, buildKiro(req, model, a.profile, budget), signal)
|
|
@@ -1171,7 +1183,7 @@ async function browserSignIn() {
|
|
|
1171
1183
|
const q = url.searchParams
|
|
1172
1184
|
if (!waiting) return html(200, page(false, "This sign-in is over", "Start it again."))
|
|
1173
1185
|
const fail = (msg) => {
|
|
1174
|
-
finish({ type: "failed" })
|
|
1186
|
+
finish({ type: "failed", error: msg })
|
|
1175
1187
|
html(200, page(false, "Sign-in didn't finish", msg))
|
|
1176
1188
|
}
|
|
1177
1189
|
if (q.get("error")) return fail(q.get("error_description") || q.get("error"))
|
|
@@ -1226,7 +1238,7 @@ async function browserSignIn() {
|
|
|
1226
1238
|
html(200, page(true, "You're signed in", `${user} is signed in. You can close this tab.`))
|
|
1227
1239
|
})
|
|
1228
1240
|
|
|
1229
|
-
const timer = setTimeout(() => finish({ type: "failed" }), SIGN_IN_TIMEOUT)
|
|
1241
|
+
const timer = setTimeout(() => finish({ type: "failed", error: "the sign-in timed out" }), SIGN_IN_TIMEOUT)
|
|
1230
1242
|
done.then(() => {
|
|
1231
1243
|
clearTimeout(timer)
|
|
1232
1244
|
setTimeout(() => server.close(), 10_000).unref?.()
|
|
@@ -1251,7 +1263,7 @@ async function ownSignIn() {
|
|
|
1251
1263
|
callback: async () => {
|
|
1252
1264
|
const cli = await readCLI()
|
|
1253
1265
|
const c = cli ?? (await readIDE())
|
|
1254
|
-
if (!c) return { type: "failed" }
|
|
1266
|
+
if (!c) return { type: "failed", error: "neither kiro-cli nor the Kiro IDE is signed in" }
|
|
1255
1267
|
const who = fresh(c) ? await whoIs(c) : { email: "", plan: "" }
|
|
1256
1268
|
return { type: "success", provider: ID, refresh: "", access: "", expires: 0, source: cli ? "kiro-cli" : "kiro-ide",
|
|
1257
1269
|
accountId: who.email || (cli ? "kiro-cli's account" : "Kiro IDE's account"), ...(who.plan ? { plan: who.plan } : {}) }
|