@magpie-community/opencode-kiro-auth 0.1.6 → 0.1.7

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 +17 -4
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -43,6 +43,13 @@ const SCOPES = ["codewhisperer:completions", "codewhisperer:analysis", "codewhis
43
43
  const IDE_UA = "KiroIDE-1.1.70-" + createHash("sha256").update("magpie:" + hostname()).digest("hex")
44
44
  const DESKTOP_UA = "Kiro-Desktop/0.2.13 (darwin; arm64)"
45
45
  const SIGN_IN_TIMEOUT = 10 * 60 * 1000
46
+ // A Builder ID sign-in has no profile of its own, and Kiro won't list one
47
+ // for it (List-Available-Profiles is a 403, "AWS Builder ID is not supported
48
+ // for this operation"); the Kiro IDE and kiro-cli name this service profile
49
+ // for it instead, so its models, usage and chat are asked with it too.
50
+ const BUILDER_ID_START = "https://view.awsapps.com/start"
51
+ const BUILDER_ID_PROFILE = "arn:aws:codewhisperer:us-east-1:638616132270:profile/AAAACCCCXXXX"
52
+ const isBuilderID = (provider) => String(provider ?? "").toLowerCase() === "builderid"
46
53
 
47
54
  const env = (k) => process.env[k] ?? ""
48
55
  const home = () => env("HOME") || homedir()
@@ -118,6 +125,8 @@ async function readCLI() {
118
125
  if (kind === "social") c.method = "social"
119
126
  else if (kind === "odic") {
120
127
  c.method = "idc"
128
+ // as kiro-cli tells them apart: Builder ID's start URL, or none
129
+ c.builderID = !m.start_url || m.start_url === BUILDER_ID_START
121
130
  const reg = get("kirocli:odic:device-registration")
122
131
  if (reg) [c.clientId, c.clientSecret] = [reg.client_id ?? "", reg.client_secret ?? ""]
123
132
  } else {
@@ -145,7 +154,7 @@ async function readIDE() {
145
154
  }
146
155
  if (!t?.accessToken) return null
147
156
  const c = { access: t.accessToken, refresh: t.refreshToken ?? "", expires: time(t.expiresAt), region: t.region || "us-east-1",
148
- profile: t.profileArn ?? "", method: "idc", idePath: path }
157
+ profile: t.profileArn ?? "", method: "idc", idePath: path, builderID: isBuilderID(t.provider) }
149
158
  if (t.clientIdHash) {
150
159
  try {
151
160
  const reg = JSON.parse(await readFile(join(ideDir(), t.clientIdHash + ".json"), "utf8"))
@@ -289,6 +298,7 @@ class KiroError extends Error {
289
298
  } catch {}
290
299
  super(msg ? `Kiro: ${msg} (${status})` : `Kiro: ${STATUS_TEXT[status] ?? ""}`)
291
300
  this.status = status
301
+ this.body = String(body ?? "")
292
302
  }
293
303
  }
294
304
 
@@ -309,7 +319,7 @@ async function managementCall(region, a, method, query, body) {
309
319
  }
310
320
 
311
321
  // profileOf finds the profile a sign-in that doesn't name one uses: an API
312
- // key's own, or the first Kiro lists in either of its regions.
322
+ // key's own, Builder ID's, or the first Kiro lists in either of its regions.
313
323
  async function profileOf(c) {
314
324
  if (c.method === "apikey") {
315
325
  const res = await fetch(management("us-east-1") + "/", {
@@ -327,6 +337,7 @@ async function profileOf(c) {
327
337
  if (!arn) throw new Error("Kiro didn't say which profile the API key is for")
328
338
  return arn
329
339
  }
340
+ if (c.builderID) return BUILDER_ID_PROFILE
330
341
  let last
331
342
  for (const region of ["us-east-1", "eu-central-1"]) {
332
343
  try {
@@ -334,6 +345,8 @@ async function profileOf(c) {
334
345
  const arn = (out?.profiles ?? []).find((p) => p?.arn)?.arn
335
346
  if (arn) return arn
336
347
  } catch (e) {
348
+ // a Builder ID sign-in the token didn't say was one
349
+ if (c.method === "idc" && e?.status === 403 && e.body.includes("Builder ID")) return BUILDER_ID_PROFILE
337
350
  last = e
338
351
  }
339
352
  }
@@ -396,7 +409,7 @@ async function credOf(auth) {
396
409
  if (!auth.access) return null
397
410
  return { access: auth.access, refresh: auth.refresh ?? "", expires: auth.expires ?? 0, method: auth.method || "social",
398
411
  region: auth.region || "us-east-1", profile: auth.profileArn ?? "", clientId: auth.clientId ?? "", clientSecret: auth.clientSecret ?? "",
399
- own: true }
412
+ builderID: isBuilderID(auth.loginProvider), own: true }
400
413
  }
401
414
 
402
415
  // Account holds the credentials in use, refreshing them when near their
@@ -1275,7 +1288,7 @@ async function browserSignIn() {
1275
1288
  grantType: "authorization_code", redirectUri: awsRedirect, code: q.get("code"), codeVerifier: aws.verifier })
1276
1289
  if (!t.accessToken) throw new Error("AWS sent back no token")
1277
1290
  c = { access: t.accessToken, refresh: t.refreshToken ?? "", expires: expiry(t.expiresIn), method: "idc", loginProvider: aws.provider,
1278
- region: aws.region, clientId: aws.clientId, clientSecret: aws.clientSecret, profile: "" }
1291
+ region: aws.region, clientId: aws.clientId, clientSecret: aws.clientSecret, profile: "", builderID: isBuilderID(aws.provider) }
1279
1292
  } else if (q.get("state") !== state) {
1280
1293
  // not ours: someone else's page, or a stale tab
1281
1294
  return html(200, page(false, "This link isn't from this sign-in", "Start it again."))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-kiro-auth",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Kiro subscriptions (Free, Pro, Pro+, Power) in OpenCode and magpie",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",