@magpie-community/opencode-kiro-auth 0.1.5 → 0.1.6
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 +17 -2
- package/index.mjs +53 -4
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -39,8 +39,23 @@ Power) and makes its requests, in OpenCode and in magpie. Provider id:
|
|
|
39
39
|
A browser sign-in is kept where OpenCode keeps sign-ins (`auth.json`; in
|
|
40
40
|
magpie, `plugin-auth.json`) as an `oauth` entry. The entry holds the
|
|
41
41
|
tokens, the profile ARN, the region and, for AWS, the registered client.
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
Refreshing such a sign-in:
|
|
43
|
+
|
|
44
|
+
- magpie renews the token 10 minutes before it expires, through the
|
|
45
|
+
plugin's `auth.refresh`, once for the account and before its requests,
|
|
46
|
+
models and usage need it. magpie saves the new token. A failed renewal,
|
|
47
|
+
even one Kiro refused, doesn't mark the account: it is tried again.
|
|
48
|
+
- OpenCode doesn't call that hook: there the token is refreshed 2 minutes
|
|
49
|
+
before it expires, before a request, and saved.
|
|
50
|
+
- Either way, a token Kiro turns down (a 403) is refreshed and the request
|
|
51
|
+
tried once more.
|
|
52
|
+
- Only one refresh runs at a time, magpie's renewal included, and a token
|
|
53
|
+
refreshed but not yet saved is used rather than spending the old refresh
|
|
54
|
+
token again.
|
|
55
|
+
- kiro-cli's and the IDE's sign-ins aren't renewed by magpie: they are
|
|
56
|
+
refreshed before a request, as above, and written back.
|
|
57
|
+
|
|
58
|
+
Tokens are refreshed through:
|
|
44
59
|
|
|
45
60
|
- Google/GitHub: through `prod.<region>.auth.desktop.kiro.dev/refreshToken`.
|
|
46
61
|
- Builder ID/Identity Center: through AWS's `oidc.<region>.amazonaws.com/token`.
|
package/index.mjs
CHANGED
|
@@ -197,7 +197,9 @@ const EXPIRED_CLI = EXPIRED + " with `kiro-cli login` or the Kiro IDE"
|
|
|
197
197
|
// there. The built-in marked no Kiro account lapsed, not even then, nor
|
|
198
198
|
// cleared one: every answer and usage read says the account is kept.
|
|
199
199
|
const gone = (msg) => Object.assign(new Error(msg), { gone: true })
|
|
200
|
-
const
|
|
200
|
+
const EARLY_MS = 2 * 60 * 1000 // a token this close to its end is refreshed before a request
|
|
201
|
+
const LEAD_MS = 10 * 60 * 1000 // and this close, magpie renews it ahead of time (auth.refresh)
|
|
202
|
+
const fresh = (c, early = EARLY_MS) => c.method === "apikey" || !c.expires || c.expires - Date.now() > early
|
|
201
203
|
|
|
202
204
|
async function post(url, contentType, body, headers = {}) {
|
|
203
205
|
let res
|
|
@@ -400,8 +402,23 @@ async function credOf(auth) {
|
|
|
400
402
|
// Account holds the credentials in use, refreshing them when near their
|
|
401
403
|
// end or when Kiro turned one down; one refresh at a time, as a refresh
|
|
402
404
|
// may replace the refresh token.
|
|
405
|
+
//
|
|
406
|
+
// after is what each refresh gave, by the access token it replaced: a
|
|
407
|
+
// sign-in read with that token (its save failed, or magpie hasn't yet
|
|
408
|
+
// saved what auth.refresh gave) goes on with the newer one rather than
|
|
409
|
+
// spending the replaced refresh token again.
|
|
403
410
|
function account(client) {
|
|
404
411
|
let held = null // { id, c }
|
|
412
|
+
const after = new Map()
|
|
413
|
+
const newest = (c) => {
|
|
414
|
+
for (let i = 0; i < 16 && after.has(c.access); i++) c = { ...after.get(c.access) }
|
|
415
|
+
return c
|
|
416
|
+
}
|
|
417
|
+
const renewed = (c, n) => {
|
|
418
|
+
after.set(c.access, n)
|
|
419
|
+
if (after.size > 16) after.delete(after.keys().next().value)
|
|
420
|
+
return n
|
|
421
|
+
}
|
|
405
422
|
let lock = Promise.resolve()
|
|
406
423
|
const locked = (fn) => {
|
|
407
424
|
const run = lock.then(fn, fn)
|
|
@@ -414,12 +431,12 @@ function account(client) {
|
|
|
414
431
|
await client.auth.set({ path: { id: ID }, body: { ...rest, type: "oauth", access: c.access, refresh: c.refresh, expires: c.expires,
|
|
415
432
|
profileArn: c.profile, region: c.region } }).catch(() => {})
|
|
416
433
|
}
|
|
417
|
-
|
|
434
|
+
const creds = (auth, stale = false) =>
|
|
418
435
|
locked(async () => {
|
|
419
436
|
const id = auth?.type === "api" ? "key:" + auth.key : "oauth:" + (auth?.source ?? "own")
|
|
420
437
|
// what its owner holds now: it may have refreshed it, signed out, or
|
|
421
438
|
// signed in to another account since
|
|
422
|
-
|
|
439
|
+
let read = await credOf(auth)
|
|
423
440
|
if (!read) {
|
|
424
441
|
held = null
|
|
425
442
|
throw gone(
|
|
@@ -428,6 +445,7 @@ function account(client) {
|
|
|
428
445
|
: "this Kiro account's sign-in is gone; add it again in magpie",
|
|
429
446
|
)
|
|
430
447
|
}
|
|
448
|
+
read = newest(read)
|
|
431
449
|
let c = held?.id === id ? held.c : null
|
|
432
450
|
if (!c || read.access !== c.access || !fresh(c) || stale) {
|
|
433
451
|
if (!read.profile && c && read.access === c.access) read.profile = c.profile
|
|
@@ -435,7 +453,7 @@ function account(client) {
|
|
|
435
453
|
c = read
|
|
436
454
|
if (!fresh(c) || stale) {
|
|
437
455
|
if (c.method === "apikey") throw gone("Kiro turned down the API key saved on the provider")
|
|
438
|
-
c = await refresh(c)
|
|
456
|
+
c = renewed(c, await refresh(c))
|
|
439
457
|
await save(auth, c)
|
|
440
458
|
}
|
|
441
459
|
}
|
|
@@ -446,6 +464,25 @@ function account(client) {
|
|
|
446
464
|
held = { id, c }
|
|
447
465
|
return { token: c.access, tokenType: tokenType(c), profile: c.profile, region: regionOf(c.profile, c.region) }
|
|
448
466
|
})
|
|
467
|
+
// renew is auth.refresh's: a sign-in kept here (not kiro-cli's or the
|
|
468
|
+
// IDE's, which their owners keep) renewed under the same lock as the
|
|
469
|
+
// requests' refresh, so the two never spend one refresh token. It saves
|
|
470
|
+
// nothing — magpie saves what it gives — and gives only what changed.
|
|
471
|
+
creds.renew = (auth) =>
|
|
472
|
+
locked(async () => {
|
|
473
|
+
const read = await credOf(auth)
|
|
474
|
+
if (!read?.own || !read.refresh) return undefined
|
|
475
|
+
let c = newest(read)
|
|
476
|
+
// renewed here already (a request's refresh, or this hook's whose
|
|
477
|
+
// result magpie hasn't saved yet), the store not yet saying so
|
|
478
|
+
if (c.access === read.access || !fresh(c, LEAD_MS)) c = renewed(c, await refresh(c))
|
|
479
|
+
held = { id: "oauth:own", c }
|
|
480
|
+
const out = { access: c.access, expires: c.expires }
|
|
481
|
+
if (c.refresh !== auth.refresh) out.refresh = c.refresh
|
|
482
|
+
if (c.profile && c.profile !== auth.profileArn) out.profileArn = c.profile
|
|
483
|
+
return out
|
|
484
|
+
})
|
|
485
|
+
return creds
|
|
449
486
|
}
|
|
450
487
|
|
|
451
488
|
// ---- models ----------------------------------------------------------------------
|
|
@@ -1325,6 +1362,18 @@ export async function KiroAuthPlugin({ client } = {}) {
|
|
|
1325
1362
|
return {
|
|
1326
1363
|
auth: {
|
|
1327
1364
|
provider: ID,
|
|
1365
|
+
// magpie renews a sign-in kept here LEAD_MS before its end, once for
|
|
1366
|
+
// the account, before its requests, models and usage ask for it; the
|
|
1367
|
+
// check before each request stays for OpenCode, which doesn't call
|
|
1368
|
+
// this. kiro-cli's and the IDE's sign-ins have no end magpie knows
|
|
1369
|
+
// (expires 0) and are never asked for.
|
|
1370
|
+
refreshLead: LEAD_MS,
|
|
1371
|
+
// A failure is thrown as it is, a refusal too: the built-in marked
|
|
1372
|
+
// no Kiro account lapsed (see gone).
|
|
1373
|
+
async refresh(auth) {
|
|
1374
|
+
if (auth?.type !== "oauth" || auth.source || !auth.access || !auth.refresh) return undefined
|
|
1375
|
+
return creds.renew(auth)
|
|
1376
|
+
},
|
|
1328
1377
|
async loader(getAuth) {
|
|
1329
1378
|
const auth = await getAuth()
|
|
1330
1379
|
if (!(auth?.type === "api" && auth.key) && auth?.type !== "oauth") return {}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-kiro-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Kiro subscriptions (Free, Pro, Pro+, Power) in OpenCode and magpie",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|
|
7
7
|
"exports": "./index.mjs",
|
|
8
8
|
"files": ["index.mjs", "README.md"],
|
|
9
9
|
"keywords": ["opencode", "opencode-plugin", "magpie", "kiro"],
|
|
10
|
-
"license": "MIT"
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": { "type": "git", "url": "git+https://github.com/magpie-community/plugins.git", "directory": "packages/kiro" }
|
|
11
12
|
}
|