@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.
- package/index.mjs +17 -4
- 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."))
|