@magpie-community/opencode-kiro-auth 0.1.4 → 0.1.5

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 +38 -20
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -188,11 +188,14 @@ async function saveBack(c) {
188
188
 
189
189
  // ---- tokens --------------------------------------------------------------------
190
190
 
191
+ // EXPIRED is the built-in's words for a sign-in Kiro refused, or one with
192
+ // nothing to refresh it.
191
193
  const EXPIRED = "Kiro's sign-in has expired; sign in again"
194
+ const EXPIRED_CLI = EXPIRED + " with `kiro-cli login` or the Kiro IDE"
192
195
 
193
196
  // 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.
197
+ // there. The built-in marked no Kiro account lapsed, not even then, nor
198
+ // cleared one: every answer and usage read says the account is kept.
196
199
  const gone = (msg) => Object.assign(new Error(msg), { gone: true })
197
200
  const fresh = (c) => c.method === "apikey" || !c.expires || c.expires - Date.now() > 2 * 60 * 1000
198
201
 
@@ -206,8 +209,8 @@ async function post(url, contentType, body, headers = {}) {
206
209
  }
207
210
  const text = await res.text()
208
211
  if (!res.ok) {
209
- if ([400, 401, 403].includes(res.status)) throw gone(EXPIRED)
210
- throw new Error(`refreshing Kiro's sign-in: ${res.status}`)
212
+ if ([400, 401, 403].includes(res.status)) throw gone(EXPIRED_CLI)
213
+ throw new Error(`refreshing Kiro's sign-in: ${res.status} ${STATUS_TEXT[res.status] ?? ""}`.trimEnd()) // Go's res.Status
211
214
  }
212
215
  return JSON.parse(text)
213
216
  }
@@ -223,7 +226,7 @@ async function refresh(c) {
223
226
  if (n && n.dbKey === c.dbKey && n.access !== c.access && fresh(n)) return n
224
227
  }
225
228
  }
226
- if (!c.refresh) throw gone(EXPIRED)
229
+ if (!c.refresh) throw gone(EXPIRED_CLI)
227
230
  let out
228
231
  switch (c.method) {
229
232
  case "social": {
@@ -246,9 +249,9 @@ async function refresh(c) {
246
249
  break
247
250
  }
248
251
  default:
249
- throw gone(EXPIRED)
252
+ throw gone("Kiro's sign-in has expired")
250
253
  }
251
- if (!out.access) throw gone(EXPIRED)
254
+ if (!out.access) throw gone(EXPIRED_CLI)
252
255
  const n = { ...c, access: out.access, expires: Date.now() + (out.expiresIn > 0 ? out.expiresIn : 3600) * 1000 }
253
256
  if (out.refresh) n.refresh = out.refresh
254
257
  await saveBack(n)
@@ -315,7 +318,7 @@ async function profileOf(c) {
315
318
  signal: AbortSignal.timeout(20_000),
316
319
  })
317
320
  if (!res.ok) {
318
- const msg = `Kiro didn't take the API key: ${res.status}`
321
+ const msg = `Kiro didn't take the API key: ${res.status} ${STATUS_TEXT[res.status] ?? ""}`.trimEnd()
319
322
  throw res.status === 401 || res.status === 403 ? gone(msg) : new Error(msg)
320
323
  }
321
324
  const arn = (await res.json().catch(() => null))?.profile?.arn
@@ -431,7 +434,7 @@ function account(client) {
431
434
  if (stale && c && read.access !== c.access && fresh(read)) stale = false // the owner refreshed it already
432
435
  c = read
433
436
  if (!fresh(c) || stale) {
434
- if (c.method === "apikey") throw gone("Kiro turned down the API key")
437
+ if (c.method === "apikey") throw gone("Kiro turned down the API key saved on the provider")
435
438
  c = await refresh(c)
436
439
  await save(auth, c)
437
440
  }
@@ -712,7 +715,7 @@ function failure(status, body) {
712
715
  const e = JSON.parse(body)
713
716
  if (e?.message) msg = e.message + (e.reason ? ` (${e.reason})` : "")
714
717
  } catch {}
715
- msg ||= `HTTP ${status}`
718
+ msg ||= STATUS_TEXT[status] ?? ""
716
719
  if (msg.includes("CONTENT_LENGTH_EXCEEDS_THRESHOLD") || msg.toLowerCase().includes("input is too long"))
717
720
  return { status: 400, message: "input is too long for the model's context: " + msg }
718
721
  if (msg.includes("INSUFFICIENT_MODEL_CAPACITY")) return { status: 503, message: msg }
@@ -723,12 +726,23 @@ function failure(status, body) {
723
726
  const ERROR_TYPES = { 400: "invalid_request_error", 401: "authentication_error", 402: "billing_error", 403: "permission_error",
724
727
  404: "not_found_error", 413: "request_too_large", 429: "rate_limit_error", 503: "overloaded_error", 529: "overloaded_error" }
725
728
  const errorType = (status) => ERROR_TYPES[status] ?? "api_error"
729
+ // Each failure says X-Magpie-Sign-In: kept, as the built-in never marked
730
+ // an account lapsed (see gone).
726
731
  const errorResponse = (status, message) =>
727
732
  new Response(JSON.stringify({ type: "error", error: { type: errorType(status), message } }), {
728
733
  status,
729
- headers: { "Content-Type": "application/json" },
734
+ headers: { "Content-Type": "application/json", "X-Magpie-Sign-In": "kept" },
730
735
  })
731
736
 
737
+ // kept is res saying the account is kept: a success too, which the
738
+ // built-in never took for a sign-in come back.
739
+ function kept(res) {
740
+ if (res.headers.has("X-Magpie-Sign-In")) return res
741
+ const headers = new Headers(res.headers)
742
+ headers.set("X-Magpie-Sign-In", "kept")
743
+ return new Response(res.body, { status: res.status, statusText: res.statusText, headers })
744
+ }
745
+
732
746
  // ---- the reply: an AWS event stream → Messages ---------------------------------------
733
747
 
734
748
  // frames reads an AWS event stream: each message its length, its headers'
@@ -1087,7 +1101,9 @@ async function reply(it, model, stream) {
1087
1101
  }
1088
1102
 
1089
1103
  // generate answers a Messages request through Kiro's API: once more,
1090
- // refreshed, when Kiro turns the token down (403).
1104
+ // refreshed, when Kiro turns the token down (403). As the built-in: no
1105
+ // credentials is a 401, a failed refresh after the 403 a 502; none marks
1106
+ // the account.
1091
1107
  async function generate(creds, auth, req, signal) {
1092
1108
  const model = String(req.model ?? "auto")
1093
1109
  const budget = thinking(req, model)
@@ -1095,7 +1111,7 @@ async function generate(creds, auth, req, signal) {
1095
1111
  try {
1096
1112
  a = await creds(auth)
1097
1113
  } catch (e) {
1098
- return errorResponse(e?.gone ? 401 : 502, e.message)
1114
+ return errorResponse(401, e.message)
1099
1115
  }
1100
1116
  let res
1101
1117
  try {
@@ -1109,7 +1125,7 @@ async function generate(creds, auth, req, signal) {
1109
1125
  try {
1110
1126
  a = await creds(auth, true)
1111
1127
  } catch (e) {
1112
- return errorResponse(e?.gone ? 401 : 502, e.message)
1128
+ return errorResponse(502, e.message)
1113
1129
  }
1114
1130
  try {
1115
1131
  res = await sendKiro(a, buildKiro(req, model, a.profile, budget), signal)
@@ -1119,10 +1135,11 @@ async function generate(creds, auth, req, signal) {
1119
1135
  }
1120
1136
  }
1121
1137
  if (!res.ok) {
1138
+ // Kiro's own 401 is passed on, as the built-in did, the sign-in kept
1122
1139
  const f = failure(res.status, (await res.text()).slice(0, 1 << 20))
1123
1140
  return errorResponse(f.status, f.message)
1124
1141
  }
1125
- return reply(events(res.body, model, budget), model, req.stream === true)
1142
+ return kept(await reply(events(res.body, model, budget), model, req.stream === true))
1126
1143
  }
1127
1144
 
1128
1145
  // ---- signing in ----------------------------------------------------------------------
@@ -1329,14 +1346,15 @@ export async function KiroAuthPlugin({ client } = {}) {
1329
1346
  },
1330
1347
  }
1331
1348
  },
1332
- // the account's credits, as magpie's built-in showed them
1349
+ // the account's credits, as magpie's built-in showed them; a read,
1350
+ // clean or failed, marked the built-in's account nothing
1333
1351
  async usage(getAuth) {
1334
1352
  const auth = await getAuth()
1335
- if (!(auth?.type === "api" && auth.key) && auth?.type !== "oauth") return { error: "not signed in" }
1353
+ if (!(auth?.type === "api" && auth.key) && auth?.type !== "oauth") return { error: "not signed in", signIn: "kept" }
1336
1354
  try {
1337
- return usageOf(await usageLimits(await creds(auth), true))
1355
+ return { ...usageOf(await usageLimits(await creds(auth), true)), signIn: "kept" }
1338
1356
  } catch (e) {
1339
- return { error: e.message }
1357
+ return { error: e.message, signIn: "kept" }
1340
1358
  }
1341
1359
  },
1342
1360
  methods: [
@@ -1373,4 +1391,4 @@ export async function KiroAuthPlugin({ client } = {}) {
1373
1391
  }
1374
1392
 
1375
1393
  // for tests
1376
- export const _internal = { usageOf, buildKiro, events, reply, failure, toolID, thinking, readCLI, readIDE, regionOf, planName, frames }
1394
+ export const _internal = { generate, refresh, usageOf, buildKiro, events, reply, failure, toolID, thinking, readCLI, readIDE, regionOf, planName, frames }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-kiro-auth",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Kiro subscriptions (Free, Pro, Pro+, Power) in OpenCode and magpie",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",