feat(xiaomi-mimo): server-assisted desktop login, five account clusters, v2.6 models

Reproduce the MiMo Desktop login surface server-side so headless/Docker
deployments can link a Xiaomi account without the Desktop client. The
account session (passToken) is captured during the proxied login and
stored per connection.

- Five account clusters (cn/sgp/ams/ru/in): per-region mimo-server host
  and SSO sid, unknown region falls back to sgp
- mimo-v2.6-pro/flash/pro-ultraspeed dual-route models: account-service
  route when desktop credentials exist, cloud API (sk- key) otherwise;
  drops obsolete mimo-x-*-preview ids
- Desktop ServiceTokenManager 2-phase handshake (single serviceLogin with
  target sid, raw 64-bit nonce preserved), per-region session cache
- reasoning_effort bridged to output_config.effort; i18n runtime now
  observes characterData mutations so React text rewrites get translated
- Security hardening on the login proxy: session travels only in the
  httpOnly cookie (never in the URL), proxy branch requires dashboard
  auth, authorization/proxy-authorization never forwarded upstream, and
  upstream Set-Cookie is not replayed onto the app origin
This commit is contained in:
wismyzhizi
2026-09-23 09:43:39 +07:00
committed by decolua
parent 6af26a9ee8
commit 910db749aa
16 changed files with 1687 additions and 352 deletions

View File

@@ -1,6 +1,7 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { describe, it, expect, beforeEach, vi } from "vitest";
import { XiaomiMimoExecutor, __test__ } from "../../open-sse/executors/xiaomi-mimo.js";
import { getExecutor } from "../../open-sse/executors/index.js";
import * as mimoAccount from "../../open-sse/shared/mimoAccount.js";
const { bareModel, COOKIE_KEY } = __test__;
@@ -17,22 +18,52 @@ describe("xiaomi-mimo executor", () => {
expect(getExecutor("xiaomi-mimo")).toBeInstanceOf(XiaomiMimoExecutor);
});
it("routes Preview models to the account-service route regardless of transport", () => {
const expected = "https://mimo-server-cn.xiaomimimo.com/api/route/chat/completions";
expect(ex.buildUrl("mimo-x-pro-preview", true, 0, OPENAI_T)).toBe(expected);
expect(ex.buildUrl("mimo-x-pro-preview", true, 0, CLAUDE_T)).toBe(expected);
// body.model arrives as `xiaomi/<id>` via upstreamModelId
expect(ex.buildUrl("xiaomi/mimo-x-flash-preview", true, 0, OPENAI_T)).toBe(expected);
});
it("keeps the sourceFormat-matched endpoint for cloud models", () => {
// Regression: a Claude client must reach /anthropic/v1/messages, not /v1/chat/completions.
expect(ex.buildUrl("mimo-v2.5-pro", true, 0, CLAUDE_T)).toBe(CLAUDE_T.runtimeTransport.baseUrl);
expect(ex.buildUrl("mimo-v2.5-pro", true, 0, OPENAI_T)).toBe(OPENAI_T.runtimeTransport.baseUrl);
});
it("authenticates Preview calls with the account cookie", () => {
const headers = ex.buildHeaders({ [COOKIE_KEY]: "serviceToken=abc", accessToken: "sk-x" }, true, "u", "mimo-x-pro-preview");
it("routes v2.6 models to account route when desktop credentials are present", () => {
// No region → SGP default
const expected = "https://mimo-server-sgp.xiaomimimo.com/api/route/chat/completions";
const credsWithToken = { providerSpecificData: { mimoPassToken: "token123" } };
const credsWithCookie = { [COOKIE_KEY]: "serviceToken=abc" };
expect(ex.buildUrl("mimo-v2.6-flash", true, 0, credsWithToken)).toBe(expected);
expect(ex.buildUrl("mimo-v2.6-pro", true, 0, credsWithCookie)).toBe(expected);
expect(ex.buildUrl("xiaomi/mimo-v2.6-flash", true, 0, credsWithToken)).toBe(expected);
});
it("routes v2.6 models to cloud API when no desktop credentials are present", () => {
expect(ex.buildUrl("mimo-v2.6-flash", true, 0, OPENAI_T)).toBe(OPENAI_T.runtimeTransport.baseUrl);
expect(ex.buildUrl("mimo-v2.6-pro", true, 0, CLAUDE_T)).toBe(CLAUDE_T.runtimeTransport.baseUrl);
});
it("resolves the account-service cluster per connection region", () => {
const cn = "https://mimo-server-cn.xiaomimimo.com/api/route/chat/completions";
const sgp = "https://mimo-server-sgp.xiaomimimo.com/api/route/chat/completions";
const ams = "https://mimo-server-ams.xiaomimimo.com/api/route/chat/completions";
const ru = "https://mimo-server-ru.xiaomimimo.com/api/route/chat/completions";
const inRegion = "https://mimo-server-in.xiaomimimo.com/api/route/chat/completions";
// default (no region) falls back to SGP (the international cluster)
expect(ex.buildUrl("mimo-v2.6-flash", true, 0, { providerSpecificData: { mimoPassToken: "t" } })).toBe(sgp);
expect(ex.buildUrl("mimo-v2.6-pro", true, 0, { providerSpecificData: { region: "cn", mimoPassToken: "t" } })).toBe(cn);
expect(ex.buildUrl("mimo-v2.6-pro", true, 0, { providerSpecificData: { region: "sgp", mimoPassToken: "t" } })).toBe(sgp);
expect(ex.buildUrl("mimo-v2.6-flash", true, 0, { providerSpecificData: { region: "SGP", mimoPassToken: "t" } })).toBe(sgp);
expect(ex.buildUrl("mimo-v2.6-pro", true, 0, { providerSpecificData: { region: "ams", mimoPassToken: "t" } })).toBe(ams);
expect(ex.buildUrl("mimo-v2.6-pro", true, 0, { providerSpecificData: { region: "ru", mimoPassToken: "t" } })).toBe(ru);
expect(ex.buildUrl("mimo-v2.6-pro", true, 0, { providerSpecificData: { region: "in", mimoPassToken: "t" } })).toBe(inRegion);
// unknown region falls back to SGP
expect(ex.buildUrl("mimo-v2.6-pro", true, 0, { providerSpecificData: { region: "eu", mimoPassToken: "t" } })).toBe(sgp);
});
it("authenticates v2.6 calls with account cookie when on account route", () => {
const headers = ex.buildHeaders(
{ [COOKIE_KEY]: "serviceToken=abc", accessToken: "sk-x" },
true,
"u",
"mimo-v2.6-flash",
);
expect(headers.Cookie).toBe("serviceToken=abc");
expect(headers.Authorization).toBeUndefined();
});
@@ -43,38 +74,55 @@ describe("xiaomi-mimo executor", () => {
expect(headers.Cookie).toBeUndefined();
});
it("fails fast when a Preview call has no account session", async () => {
await expect(
ex.execute({ model: "mimo-x-pro-preview", body: {}, stream: true, credentials: {}, log: null }),
).rejects.toThrow(/account session unavailable/);
});
it("flattens content-part arrays to plain strings", () => {
it("preserves content-part arrays for multimodal inputs", () => {
const parts = [{ type: "image_url", image_url: { url: "data:image/png;base64,xyz" } }, { type: "text", text: "hi" }];
const out = ex.transformRequest(
"mimo-x-pro-preview",
{ messages: [{ role: "user", content: [{ type: "text", text: "a" }, { type: "text", text: "b" }] }] },
"mimo-v2.6-pro",
{ messages: [{ role: "user", content: parts }] },
true,
{},
{ providerSpecificData: { mimoPassToken: "token" } },
);
expect(out.messages[0].content).toBe("ab");
expect(out.messages[0].content).toEqual(parts);
});
it("applies Preview defaults without overriding explicit values", () => {
it("bridges reasoning_effort to official output_config.effort", () => {
const creds = { providerSpecificData: { mimoPassToken: "token" } };
const body = {
messages: [{ role: "user", content: "solve" }],
reasoning_effort: "high",
};
const out = ex.transformRequest("mimo-v2.6-pro", body, true, creds);
expect(out.reasoning_effort).toBeUndefined();
expect(out.output_config).toEqual({ effort: "high" });
});
it("normalizes xhigh reasoning_effort to high in output_config.effort", () => {
const creds = { providerSpecificData: { mimoPassToken: "token" } };
const body = {
messages: [{ role: "user", content: "complex" }],
reasoning_effort: "xhigh",
};
const out = ex.transformRequest("mimo-v2.6-pro", body, true, creds);
expect(out.reasoning_effort).toBeUndefined();
expect(out.output_config).toEqual({ effort: "high" });
});
it("applies defaults without overriding explicit values", () => {
const creds = { providerSpecificData: { mimoPassToken: "token" } };
const body = { messages: [{ role: "user", content: "hi" }], temperature: 0.2 };
const out = ex.transformRequest("mimo-x-pro-preview", body, true, {});
expect(out.temperature).toBe(0.2); // caller's value kept
expect(out.top_p).toBe(0.95); // default filled in
expect(out.max_tokens).toBe(4096);
const out = ex.transformRequest("mimo-v2.6-pro", body, true, creds);
expect(out.temperature).toBe(0.2);
expect(out.top_p).toBe(0.95);
});
it("leaves cloud bodies free of Preview defaults", () => {
it("leaves cloud bodies free of account defaults", () => {
const out = ex.transformRequest("mimo-v2.5-pro", { messages: [{ role: "user", content: "hi" }] }, true, {});
expect(out.thinking).toBeUndefined();
expect(out.max_tokens).toBeUndefined();
expect(out.output_config).toBeUndefined();
expect(out.temperature).toBeUndefined();
});
it("strips a provider/model prefix when testing preview ids", () => {
expect(bareModel("xiaomi/mimo-x-pro-preview")).toBe("mimo-x-pro-preview");
expect(bareModel("mimo-x-pro-preview")).toBe("mimo-x-pro-preview");
it("strips a provider/model prefix when testing model ids", () => {
expect(bareModel("xiaomi/mimo-v2.6-pro")).toBe("mimo-v2.6-pro");
expect(bareModel("mimo-v2.6-flash")).toBe("mimo-v2.6-flash");
});
});

View File

@@ -0,0 +1,40 @@
/**
* Security invariants of the server-assisted MiMo login proxy
* (src/lib/mimoLoginSession.js):
* - credentials bound to 9router's own origin are never forwarded upstream
* - upstream Set-Cookie is never replayed onto the app's own cookie jar
*/
import { describe, it, expect } from "vitest";
import { __test__ } from "../../src/lib/mimoLoginSession.js";
const { STRIP_UPSTREAM_HEADERS, buildBrowserResponse } = __test__;
describe("mimo login proxy security", () => {
it("strips auth credentials and session cookies before forwarding upstream", () => {
for (const h of ["authorization", "proxy-authorization", "cookie", "host"]) {
expect(STRIP_UPSTREAM_HEADERS.has(h)).toBe(true);
}
});
it("does not replay upstream Set-Cookie onto the app origin", async () => {
const upstream = new Response("ok", {
status: 200,
headers: {
"content-type": "text/html",
"set-cookie": "userId=123; Path=/", // plain object header: visible via getSetCookie
},
});
const out = await buildBrowserResponse({ jar: new Map() }, upstream, "http://localhost:20128", "/pass/");
expect(out.headers.getSetCookie()).toEqual([]);
});
it("keeps ordinary response headers intact", async () => {
const upstream = new Response("<html></html>", {
status: 200,
headers: { "content-type": "text/html" },
});
const out = await buildBrowserResponse({ jar: new Map() }, upstream, "http://localhost:20128", "/fe/");
expect(out.status).toBe(200);
expect(out.headers.get("content-type")).toBe("text/html");
});
});