@magpie-community/opencode-mimo-auth 0.1.4 → 0.1.5
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 +5 -4
- package/index.mjs +53 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,10 +29,11 @@ Chat completions go to `https://mimo-server-<region>.xiaomimimo.com/api/route/ch
|
|
|
29
29
|
|
|
30
30
|
`mimo-auto`, the app's default model, is requested as `mimo-pro`.
|
|
31
31
|
|
|
32
|
-
The session is renewed with the passToken a day after it was issued.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
sign in
|
|
32
|
+
The session is renewed with the passToken a day after it was issued. A model
|
|
33
|
+
request the server turns away (a 401) is answered as the server answered it,
|
|
34
|
+
as magpie's built-in MiMo account did; reading usage renews the session once
|
|
35
|
+
when the server turns it away. If the passToken no longer works, sign in
|
|
36
|
+
again.
|
|
36
37
|
|
|
37
38
|
## Where the sign-in is kept
|
|
38
39
|
|
package/index.mjs
CHANGED
|
@@ -117,7 +117,11 @@ async function session(creds, base) {
|
|
|
117
117
|
const headers = appHeaders()
|
|
118
118
|
const cookie = jar.header(url)
|
|
119
119
|
if (cookie) headers.Cookie = cookie
|
|
120
|
-
|
|
120
|
+
try {
|
|
121
|
+
res = await fetch(url, { headers, redirect: "manual", signal })
|
|
122
|
+
} catch (e) {
|
|
123
|
+
throw new Error("Xiaomi MiMo sign-in: " + (e?.message ?? e))
|
|
124
|
+
}
|
|
121
125
|
jar.take(res, url)
|
|
122
126
|
const loc = res.headers.get("location")
|
|
123
127
|
if (res.status < 300 || res.status >= 400 || !loc) break
|
|
@@ -184,6 +188,15 @@ function fromAuth(auth) {
|
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
|
|
191
|
+
// said is an answer with magpie's X-Magpie-Sign-In, which tells what it
|
|
192
|
+
// means for the account's sign-in whatever its status: "expired" marks it
|
|
193
|
+
// lapsed, "kept" leaves it be. magpie takes it off before the agent sees it.
|
|
194
|
+
function said(res, v) {
|
|
195
|
+
const h = new Headers(res.headers)
|
|
196
|
+
h.set("X-Magpie-Sign-In", v)
|
|
197
|
+
return new Response(res.body, { status: res.status, statusText: res.statusText, headers: h })
|
|
198
|
+
}
|
|
199
|
+
|
|
187
200
|
// ---- the sign-in ------------------------------------------------------------
|
|
188
201
|
|
|
189
202
|
// serviceOf is the sid and callback the MiMo server sends a browser with
|
|
@@ -336,7 +349,8 @@ export const MimoAuthPlugin = async ({ client }) => {
|
|
|
336
349
|
|
|
337
350
|
// fresh is the account with a live session, signed on again when it is
|
|
338
351
|
// older than the app lets one get, or when force is set (the server
|
|
339
|
-
// turned the last one away)
|
|
352
|
+
// turned the last one away); renewed when it was signed on again, which
|
|
353
|
+
// took the built-in's lapse mark off
|
|
340
354
|
const fresh = async (getAuth, force) => {
|
|
341
355
|
const a = fromAuth(await getAuth())
|
|
342
356
|
if (!a) throw new Error("Xiaomi MiMo: not signed in")
|
|
@@ -349,7 +363,7 @@ export const MimoAuthPlugin = async ({ client }) => {
|
|
|
349
363
|
const s = await session(a.creds, a.creds.base)
|
|
350
364
|
const issued = Date.now()
|
|
351
365
|
await save(toAuth(a.creds, s.cookies, issued))
|
|
352
|
-
return { creds: a.creds, cookies: s.cookies, expires: issued + RENEW_AFTER }
|
|
366
|
+
return { creds: a.creds, cookies: s.cookies, expires: issued + RENEW_AFTER, renewed: true }
|
|
353
367
|
} catch (e) {
|
|
354
368
|
// a hiccup: the session in hand may still do
|
|
355
369
|
if (!(e instanceof Lapsed) && !force && Object.keys(a.cookies).length) return a
|
|
@@ -364,16 +378,22 @@ export const MimoAuthPlugin = async ({ client }) => {
|
|
|
364
378
|
}
|
|
365
379
|
|
|
366
380
|
// page asks the MiMo server for one of the account's pages, signing on
|
|
367
|
-
// again once when it is turned away, and gives its data.
|
|
368
|
-
|
|
381
|
+
// again once when it is turned away, and gives its data. read.signIn is
|
|
382
|
+
// what that did to the built-in's lapse mark: signed on again took it
|
|
383
|
+
// off, a sign-in Xiaomi no longer takes (mimoLapse) put it on.
|
|
384
|
+
const page = async (getAuth, path, read) => {
|
|
369
385
|
for (let t = 0; ; t++) {
|
|
370
386
|
let s
|
|
371
387
|
try {
|
|
372
388
|
s = await fresh(getAuth, t > 0)
|
|
373
389
|
} catch (e) {
|
|
374
|
-
if (e instanceof Lapsed)
|
|
390
|
+
if (e instanceof Lapsed) {
|
|
391
|
+
read.signIn = "expired"
|
|
392
|
+
throw new SignInGone(`${fromAuth(await getAuth())?.creds.userId}: the Xiaomi MiMo sign-in has expired — sign in again`)
|
|
393
|
+
}
|
|
375
394
|
throw e
|
|
376
395
|
}
|
|
396
|
+
if (s.renewed) read.signIn = "renewed"
|
|
377
397
|
const res = await fetch(s.creds.base.replace(/\/+$/, "") + path, {
|
|
378
398
|
headers: { Cookie: cookieHeader(s.cookies), ...appHeaders() },
|
|
379
399
|
signal: AbortSignal.timeout(20_000),
|
|
@@ -387,6 +407,7 @@ export const MimoAuthPlugin = async ({ client }) => {
|
|
|
387
407
|
if (res.status === 401 || (res.status === 200 && bad)) {
|
|
388
408
|
// a session gone stale, or a redirect to Xiaomi's sign-in
|
|
389
409
|
if (t === 0) continue
|
|
410
|
+
read.signIn = "expired"
|
|
390
411
|
throw new SignInGone(`${s.creds.userId}: the Xiaomi MiMo sign-in has expired — sign in again`)
|
|
391
412
|
}
|
|
392
413
|
if (res.status !== 200 || bad) throw new Error(`Xiaomi MiMo ${path}: ${vendorError(text, statusLine(res.status))}`)
|
|
@@ -395,13 +416,16 @@ export const MimoAuthPlugin = async ({ client }) => {
|
|
|
395
416
|
}
|
|
396
417
|
}
|
|
397
418
|
|
|
398
|
-
// usage is the plan and the week's allowance, magpie's own hook
|
|
419
|
+
// usage is the plan and the week's allowance, magpie's own hook; signIn
|
|
420
|
+
// is the lapse mark as the built-in's reads left it, a clean read with
|
|
421
|
+
// no signing on again leaving it be
|
|
399
422
|
const usage = async (getAuth) => {
|
|
423
|
+
const read = { signIn: "kept" }
|
|
400
424
|
let self
|
|
401
425
|
try {
|
|
402
|
-
self = await page(getAuth, "/user/xiaomi/subscription/self")
|
|
426
|
+
self = await page(getAuth, "/user/xiaomi/subscription/self", read)
|
|
403
427
|
} catch (e) {
|
|
404
|
-
return { error: e?.message ?? String(e) }
|
|
428
|
+
return { error: e?.message ?? String(e), signIn: read.signIn }
|
|
405
429
|
}
|
|
406
430
|
const c = self?.current
|
|
407
431
|
const out = { plan: "Free" }
|
|
@@ -415,12 +439,13 @@ export const MimoAuthPlugin = async ({ client }) => {
|
|
|
415
439
|
}
|
|
416
440
|
let use
|
|
417
441
|
try {
|
|
418
|
-
use = await page(getAuth, "/user/usage")
|
|
442
|
+
use = await page(getAuth, "/user/usage", read)
|
|
419
443
|
} catch (e) {
|
|
420
444
|
if (e instanceof SignInGone) out.error = e.message
|
|
421
445
|
// the plan alone, when the week's allowance can't be read
|
|
422
|
-
return out
|
|
446
|
+
return { ...out, signIn: read.signIn }
|
|
423
447
|
}
|
|
448
|
+
out.signIn = read.signIn
|
|
424
449
|
const percent = use?.percent
|
|
425
450
|
const reset = use?.resetDate
|
|
426
451
|
// no plan: the free offer shows no allowance
|
|
@@ -467,31 +492,29 @@ export const MimoAuthPlugin = async ({ client }) => {
|
|
|
467
492
|
if (j?.model === "mimo-auto") body = JSON.stringify({ ...j, model: "mimo-pro" })
|
|
468
493
|
} catch {}
|
|
469
494
|
}
|
|
470
|
-
|
|
471
|
-
const s = await fresh(getAuth, force)
|
|
472
|
-
const headers = new Headers(init.headers ?? req?.headers)
|
|
473
|
-
headers.delete("authorization")
|
|
474
|
-
headers.set("Cookie", cookieHeader(s.cookies))
|
|
475
|
-
headers.set("X-Mimo-Source", SOURCE)
|
|
476
|
-
headers.set("User-Agent", UA)
|
|
477
|
-
headers.set("X-Client-Version", APP_VERSION)
|
|
478
|
-
return fetch(url, { ...init, method: init.method ?? req?.method ?? "POST", headers, body })
|
|
479
|
-
}
|
|
495
|
+
let s
|
|
480
496
|
try {
|
|
481
|
-
|
|
482
|
-
// a session gone stale, or a redirect to Xiaomi's sign-in page
|
|
483
|
-
const stale = res.status === 401 || (res.status === 200 && /text\/html/i.test(res.headers.get("content-type") ?? ""))
|
|
484
|
-
if (!stale || (body != null && typeof body !== "string")) return res
|
|
485
|
-
await res.arrayBuffer().catch(() => {})
|
|
486
|
-
return await send(true)
|
|
497
|
+
s = await fresh(getAuth, false)
|
|
487
498
|
} catch (e) {
|
|
488
|
-
// the passToken no longer signs the account on: the
|
|
489
|
-
//
|
|
499
|
+
// the passToken no longer signs the account on: the built-in
|
|
500
|
+
// failed the request (magpie's 502) in mimoLapse's words and
|
|
501
|
+
// marked the account lapsed
|
|
490
502
|
if (!(e instanceof Lapsed)) throw e
|
|
491
503
|
const who = fromAuth(await getAuth())?.creds.userId ?? a.creds.userId
|
|
492
504
|
const message = `${who}: the Xiaomi MiMo sign-in has expired — sign in again`
|
|
493
|
-
return new Response(JSON.stringify({ error: { message, type: "
|
|
505
|
+
return said(new Response(JSON.stringify({ error: { message, type: "api_error", code: null } }), { status: 502, headers: { "content-type": "application/json" } }), "expired")
|
|
494
506
|
}
|
|
507
|
+
const headers = new Headers(init.headers ?? req?.headers)
|
|
508
|
+
headers.delete("authorization")
|
|
509
|
+
headers.set("Cookie", cookieHeader(s.cookies))
|
|
510
|
+
headers.set("X-Mimo-Source", SOURCE)
|
|
511
|
+
headers.set("User-Agent", UA)
|
|
512
|
+
headers.set("X-Client-Version", APP_VERSION)
|
|
513
|
+
// the server's answer goes through as it is, a 401 too, as the
|
|
514
|
+
// built-in's did: it took the lapse off only when it signed on
|
|
515
|
+
// again, whatever the server then answered
|
|
516
|
+
const res = await fetch(url, { ...init, method: init.method ?? req?.method ?? "POST", headers, body })
|
|
517
|
+
return said(res, s.renewed ? "renewed" : "kept")
|
|
495
518
|
},
|
|
496
519
|
}
|
|
497
520
|
},
|
package/package.json
CHANGED