@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.
Files changed (2) hide show
  1. package/index.mjs +27 -15
  2. 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 new Error(EXPIRED)
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 new Error(EXPIRED)
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 new Error(EXPIRED + " with `kiro-cli login`")
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 new Error(EXPIRED)
249
+ throw gone(EXPIRED)
245
250
  }
246
- if (!out.access) throw new Error(EXPIRED)
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) throw new Error(`Kiro didn't take the API key: ${res.status}`)
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 new Error(auth?.source ? "Kiro isn't signed in; sign in with `kiro-cli login` or the Kiro IDE" : "Kiro isn't signed in; sign in again")
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 new Error("Kiro turned down the API key")
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: "Kiro: " + 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: "Kiro: " + e.text } }))
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 } : {}) }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-kiro-auth",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Kiro subscriptions (Free, Pro, Pro+, Power) in OpenCode and magpie",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",