@magpie-community/opencode-kiro-auth 0.1.0 → 0.1.2

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 +57 -6
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -266,13 +266,18 @@ function regionOf(profile, signedIn) {
266
266
  return String(signedIn ?? "").startsWith("eu-") ? "eu-central-1" : "us-east-1"
267
267
  }
268
268
 
269
+ // STATUS_TEXT is Go's http.StatusText for the answers Kiro gives.
270
+ const STATUS_TEXT = { 400: "Bad Request", 401: "Unauthorized", 403: "Forbidden", 404: "Not Found", 408: "Request Timeout",
271
+ 409: "Conflict", 413: "Request Entity Too Large", 429: "Too Many Requests", 500: "Internal Server Error", 502: "Bad Gateway",
272
+ 503: "Service Unavailable", 504: "Gateway Timeout" }
273
+
269
274
  class KiroError extends Error {
270
275
  constructor(status, body) {
271
276
  let msg = ""
272
277
  try {
273
278
  msg = JSON.parse(body)?.message ?? ""
274
279
  } catch {}
275
- super(msg ? `Kiro: ${msg} (${status})` : `Kiro: HTTP ${status}`)
280
+ super(msg ? `Kiro: ${msg} (${status})` : `Kiro: ${STATUS_TEXT[status] ?? ""}`)
276
281
  this.status = status
277
282
  }
278
283
  }
@@ -333,6 +338,38 @@ function usageLimits(a, email) {
333
338
  const planName = (title) =>
334
339
  String(title ?? "").toLowerCase().split(/\s+/).filter(Boolean).map((w) => (w === "kiro" ? "Kiro" : w[0].toUpperCase() + w.slice(1))).join(" ")
335
340
 
341
+ // goG is Go's %g of x: an exponent from a million up.
342
+ function goG(x) {
343
+ const [m, e] = x.toExponential().split("e")
344
+ const exp = Number(e)
345
+ if (exp < -4 || exp >= 6) return `${m}e${exp < 0 ? "-" : "+"}${String(Math.abs(exp)).padStart(2, "0")}`
346
+ return String(x)
347
+ }
348
+
349
+ // usageOf is the allowance Get-Usage-Limits tells, as magpie's built-in
350
+ // said it: the account's email, the credits of each allowance, the month's and a free trial's
351
+ // while it runs.
352
+ function usageOf(l) {
353
+ const at = (secs) => (secs > 0 ? new Date(Math.trunc(secs) * 1000).toISOString() : undefined)
354
+ const count = (used, limit) => used.toFixed(2).replace(/0+$/, "").replace(/\.$/, "") + " / " + goG(limit)
355
+ const num = (x) => (typeof x === "number" && Number.isFinite(x) ? x : 0)
356
+ const windows = []
357
+ for (const u of l?.usageBreakdownList ?? []) {
358
+ const t = u?.freeTrialInfo
359
+ if (t && String(t.freeTrialStatus ?? "").toUpperCase() === "ACTIVE" && num(t.usageLimitWithPrecision) > 0) {
360
+ const used = num(t.currentUsageWithPrecision), limit = num(t.usageLimitWithPrecision)
361
+ windows.push({ name: "Free trial", used: (100 * used) / limit, resetsAt: at(num(t.freeTrialExpiry)), display: count(used, limit) })
362
+ }
363
+ const used = num(u?.currentUsageWithPrecision), limit = num(u?.usageLimitWithPrecision)
364
+ if (limit <= 0) continue
365
+ const reset = num(u?.nextDateReset) || num(l?.nextDateReset)
366
+ windows.push({ name: u?.displayNamePlural || u?.displayName || "Credits", used: (100 * used) / limit, resetsAt: at(reset),
367
+ display: count(used, limit), span: 30 * 24 * 3600 })
368
+ }
369
+ const email = l?.userInfo?.email
370
+ return { plan: planName(l?.subscriptionInfo?.subscriptionTitle), ...(typeof email === "string" && email ? { user: email } : {}), windows }
371
+ }
372
+
336
373
  // ---- the account, as the plugin holds it -------------------------------------------
337
374
 
338
375
  // credOf is the sign-in a stored auth names: a key, one signed in here, or
@@ -374,7 +411,11 @@ function account(client) {
374
411
  const read = await credOf(auth)
375
412
  if (!read) {
376
413
  held = null
377
- 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")
414
+ throw new Error(
415
+ auth?.source
416
+ ? "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"
417
+ : "this Kiro account's sign-in is gone; add it again in magpie",
418
+ )
378
419
  }
379
420
  let c = held?.id === id ? held.c : null
380
421
  if (!c || read.access !== c.access || !fresh(c) || stale) {
@@ -1134,7 +1175,7 @@ async function browserSignIn() {
1134
1175
  const q = url.searchParams
1135
1176
  if (!waiting) return html(200, page(false, "This sign-in is over", "Start it again."))
1136
1177
  const fail = (msg) => {
1137
- finish({ type: "failed" })
1178
+ finish({ type: "failed", error: msg })
1138
1179
  html(200, page(false, "Sign-in didn't finish", msg))
1139
1180
  }
1140
1181
  if (q.get("error")) return fail(q.get("error_description") || q.get("error"))
@@ -1189,7 +1230,7 @@ async function browserSignIn() {
1189
1230
  html(200, page(true, "You're signed in", `${user} is signed in. You can close this tab.`))
1190
1231
  })
1191
1232
 
1192
- const timer = setTimeout(() => finish({ type: "failed" }), SIGN_IN_TIMEOUT)
1233
+ const timer = setTimeout(() => finish({ type: "failed", error: "the sign-in timed out" }), SIGN_IN_TIMEOUT)
1193
1234
  done.then(() => {
1194
1235
  clearTimeout(timer)
1195
1236
  setTimeout(() => server.close(), 10_000).unref?.()
@@ -1214,7 +1255,7 @@ async function ownSignIn() {
1214
1255
  callback: async () => {
1215
1256
  const cli = await readCLI()
1216
1257
  const c = cli ?? (await readIDE())
1217
- if (!c) return { type: "failed" }
1258
+ if (!c) return { type: "failed", error: "neither kiro-cli nor the Kiro IDE is signed in" }
1218
1259
  const who = fresh(c) ? await whoIs(c) : { email: "", plan: "" }
1219
1260
  return { type: "success", provider: ID, refresh: "", access: "", expires: 0, source: cli ? "kiro-cli" : "kiro-ide",
1220
1261
  accountId: who.email || (cli ? "kiro-cli's account" : "Kiro IDE's account"), ...(who.plan ? { plan: who.plan } : {}) }
@@ -1255,6 +1296,16 @@ export async function KiroAuthPlugin({ client } = {}) {
1255
1296
  },
1256
1297
  }
1257
1298
  },
1299
+ // the account's credits, as magpie's built-in showed them
1300
+ async usage(getAuth) {
1301
+ const auth = await getAuth()
1302
+ if (!(auth?.type === "api" && auth.key) && auth?.type !== "oauth") return { error: "not signed in" }
1303
+ try {
1304
+ return usageOf(await usageLimits(await creds(auth), true))
1305
+ } catch (e) {
1306
+ return { error: e.message }
1307
+ }
1308
+ },
1258
1309
  methods: [
1259
1310
  { type: "oauth", label: "Kiro (Google, GitHub, AWS Builder ID, IAM Identity Center)", authorize: browserSignIn },
1260
1311
  { type: "oauth", label: "Kiro CLI's or Kiro IDE's sign-in", authorize: ownSignIn },
@@ -1289,4 +1340,4 @@ export async function KiroAuthPlugin({ client } = {}) {
1289
1340
  }
1290
1341
 
1291
1342
  // for tests
1292
- export const _internal = { buildKiro, events, reply, failure, toolID, thinking, readCLI, readIDE, regionOf, planName, frames }
1343
+ export const _internal = { 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.0",
3
+ "version": "0.1.2",
4
4
  "description": "Kiro subscriptions (Free, Pro, Pro+, Power) in OpenCode and magpie",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",