@magpie-community/opencode-kiro-auth 0.1.11 → 0.1.13
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/README.md +6 -0
- package/index.mjs +39 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,12 @@ Power) and makes its requests, in OpenCode and in magpie. Provider id:
|
|
|
19
19
|
the browser to AWS, and trades the code AWS returns.
|
|
20
20
|
- The account is named by the email Kiro has for it (`Get-Usage-Limits`),
|
|
21
21
|
and its plan is read from the same call.
|
|
22
|
+
- Kiro keeps a Google, a GitHub, a Builder ID and an Identity Center
|
|
23
|
+
sign-in of one email as different accounts. The sign-in carries a `uid`
|
|
24
|
+
(`google`, `github`, `builderid`, `enterprise`), so magpie keeps each one
|
|
25
|
+
beside the others, and a sign-in again the same way replaces only its
|
|
26
|
+
own account. An account signed in before 0.1.13 is given its `uid` on
|
|
27
|
+
its first request, model listing, usage read or renewal.
|
|
22
28
|
- A company's own identity provider (`external_idp`) is not supported
|
|
23
29
|
here. Sign in with `kiro-cli login` and use the next method instead.
|
|
24
30
|
- **Kiro CLI's or Kiro IDE's sign-in**: uses the account kiro-cli is signed
|
package/index.mjs
CHANGED
|
@@ -50,6 +50,12 @@ const SIGN_IN_TIMEOUT = 10 * 60 * 1000
|
|
|
50
50
|
const BUILDER_ID_START = "https://view.awsapps.com/start"
|
|
51
51
|
const BUILDER_ID_PROFILE = "arn:aws:codewhisperer:us-east-1:638616132270:profile/AAAACCCCXXXX"
|
|
52
52
|
const isBuilderID = (provider) => String(provider ?? "").toLowerCase() === "builderid"
|
|
53
|
+
// uidOf is what tells two Kiro accounts of one email apart: the way it was
|
|
54
|
+
// signed in to. Kiro keeps a Google and a GitHub sign-in of one email (or
|
|
55
|
+
// Builder ID's, or Identity Center's) as different accounts, and magpie
|
|
56
|
+
// keeps a sign-in of an email already signed in as that account unless
|
|
57
|
+
// both have a uid and the two differ (#309). "" when it isn't known.
|
|
58
|
+
const uidOf = (loginProvider) => String(loginProvider ?? "").trim().toLowerCase()
|
|
53
59
|
|
|
54
60
|
const env = (k) => process.env[k] ?? ""
|
|
55
61
|
const home = () => env("HOME") || homedir()
|
|
@@ -438,11 +444,15 @@ function account(client) {
|
|
|
438
444
|
lock = run.catch(() => {})
|
|
439
445
|
return run
|
|
440
446
|
}
|
|
447
|
+
// an account signed in here before its uid was kept is given it, so a
|
|
448
|
+
// sign-in with another way to the same email no longer replaces it
|
|
449
|
+
const uidSaved = new Set()
|
|
450
|
+
const uidField = (auth) => (!auth?.uid && uidOf(auth?.loginProvider) ? { uid: uidOf(auth.loginProvider) } : {})
|
|
441
451
|
const save = async (auth, c) => {
|
|
442
452
|
if (!c.own || !client?.auth?.set) return
|
|
443
453
|
const { type: _t, ...rest } = auth
|
|
444
454
|
await client.auth.set({ path: { id: ID }, body: { ...rest, type: "oauth", access: c.access, refresh: c.refresh, expires: c.expires,
|
|
445
|
-
profileArn: c.profile, region: c.region } }).catch(() => {})
|
|
455
|
+
profileArn: c.profile, region: c.region, ...uidField(auth) } }).catch(() => {})
|
|
446
456
|
}
|
|
447
457
|
const creds = (auth, stale = false) =>
|
|
448
458
|
locked(async () => {
|
|
@@ -460,6 +470,7 @@ function account(client) {
|
|
|
460
470
|
}
|
|
461
471
|
read = newest(read)
|
|
462
472
|
let c = held?.id === id ? held.c : null
|
|
473
|
+
let saved = false
|
|
463
474
|
if (!c || read.access !== c.access || !fresh(c) || stale) {
|
|
464
475
|
if (!read.profile && c && read.access === c.access) read.profile = c.profile
|
|
465
476
|
if (stale && c && read.access !== c.access && fresh(read)) stale = false // the owner refreshed it already
|
|
@@ -468,11 +479,20 @@ function account(client) {
|
|
|
468
479
|
if (c.method === "apikey") throw gone("Kiro turned down the API key saved on the provider")
|
|
469
480
|
c = renewed(c, await refresh(c))
|
|
470
481
|
await save(auth, c)
|
|
482
|
+
saved = true
|
|
471
483
|
}
|
|
472
484
|
}
|
|
473
485
|
if (!c.profile) {
|
|
474
486
|
c.profile = await profileOf(c)
|
|
475
|
-
if (!auth?.profileArn
|
|
487
|
+
if (!auth?.profileArn && !saved) {
|
|
488
|
+
await save(auth, c)
|
|
489
|
+
saved = true
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
// once per token: a save that didn't take isn't tried on every request
|
|
493
|
+
if (!saved && c.own && uidField(auth).uid && !uidSaved.has(c.access)) {
|
|
494
|
+
uidSaved.add(c.access)
|
|
495
|
+
await save(auth, c)
|
|
476
496
|
}
|
|
477
497
|
held = { id, c }
|
|
478
498
|
return { token: c.access, tokenType: tokenType(c), profile: c.profile, region: regionOf(c.profile, c.region) }
|
|
@@ -493,7 +513,7 @@ function account(client) {
|
|
|
493
513
|
const out = { access: c.access, expires: c.expires }
|
|
494
514
|
if (c.refresh !== auth.refresh) out.refresh = c.refresh
|
|
495
515
|
if (c.profile && c.profile !== auth.profileArn) out.profileArn = c.profile
|
|
496
|
-
return out
|
|
516
|
+
return { ...out, ...uidField(auth) }
|
|
497
517
|
})
|
|
498
518
|
return creds
|
|
499
519
|
}
|
|
@@ -645,6 +665,17 @@ function thinking(req, model) {
|
|
|
645
665
|
return 20000
|
|
646
666
|
}
|
|
647
667
|
|
|
668
|
+
// formatText is the structured output a request asks for, said in words:
|
|
669
|
+
// Anthropic's output_config.format (or the AI SDK's output_format) with a
|
|
670
|
+
// JSON schema, "" when it asks for none. A JSON object with no schema
|
|
671
|
+
// reaches the plugin already told in the system prompt, as Messages has no
|
|
672
|
+
// field for it.
|
|
673
|
+
function formatText(req) {
|
|
674
|
+
const f = req.output_config?.format ?? req.output_format
|
|
675
|
+
if (f?.type !== "json_schema" || !f.schema || typeof f.schema !== "object") return ""
|
|
676
|
+
return "Respond with a single JSON value and nothing else, matching this JSON schema:\n" + JSON.stringify(f.schema) + "."
|
|
677
|
+
}
|
|
678
|
+
|
|
648
679
|
// buildKiro is the body of a generateAssistantResponse call.
|
|
649
680
|
function buildKiro(req, model, profile, budget) {
|
|
650
681
|
const entries = []
|
|
@@ -756,6 +787,10 @@ function buildKiro(req, model, profile, budget) {
|
|
|
756
787
|
for (const e of entries) if (e.assistantResponseMessage && !e.assistantResponseMessage.toolUses.length) delete e.assistantResponseMessage.toolUses
|
|
757
788
|
// the instructions go at the head of the first message
|
|
758
789
|
let system = typeof req.system === "string" ? req.system : textOf(req.system)
|
|
790
|
+
// Kiro has no field for the answer's format: it is told in words, at the
|
|
791
|
+
// end of the instructions, as magpie's built-in tells it (inSystem)
|
|
792
|
+
const format = formatText(req)
|
|
793
|
+
if (format) system = system ? system + "\n\n" + format : format
|
|
759
794
|
if (budget) system = `<thinking_mode>enabled</thinking_mode><max_thinking_length>${budget}</max_thinking_length>` + (system ? "\n" + system : "")
|
|
760
795
|
if (system) entries[0].userInputMessage.content = system + "\n\n" + entries[0].userInputMessage.content
|
|
761
796
|
if (Array.isArray(req.system)) mark(entries[0].userInputMessage, req.system)
|
|
@@ -1390,6 +1425,7 @@ async function browserSignIn() {
|
|
|
1390
1425
|
const who = await whoIs(c)
|
|
1391
1426
|
const user = who.email || "Kiro account"
|
|
1392
1427
|
finish({ type: "success", provider: ID, refresh: c.refresh, access: c.access, expires: c.expires, accountId: user,
|
|
1428
|
+
...(uidOf(c.loginProvider) ? { uid: uidOf(c.loginProvider) } : {}),
|
|
1393
1429
|
method: c.method, loginProvider: c.loginProvider, region: c.region, profileArn: who.profile,
|
|
1394
1430
|
...(c.clientId ? { clientId: c.clientId, clientSecret: c.clientSecret } : {}), ...(who.plan ? { plan: who.plan } : {}) })
|
|
1395
1431
|
html(200, page(true, "You're signed in", `${user} is signed in. You can close this tab.`))
|