Files
OmniRoute/open-sse/executors/maxai/constants.ts
Armin Anton” ∴ cabbbe410a feat(providers): add MaxAI — signed OpenAI-compatible provider (chat, tools, vision, image-gen, doc-RAG) (#11461)
MaxAI joins as a first-class signed provider: 13 chat models discovered live from /models/get_config plus 6 image models, routed through the standard /v1 endpoints with per-request X-Authorization signing, browserless onboarding, prompted tool-calling, vision input, image generation and document RAG.

Reconciled on merge — worth reading, because the branch forked 227 commits back and 77 files conflicted. Only five carried MaxAI content; the rest was drift from the older release line and took the tip's side, taking the diff from 113 files to 37 (then 93 as counted against the current base).

- executors/index.ts: the tip has since refactored the executor map to lazy dynamic imports, so MaxAI is registered in that shape rather than the branch's static import.
- imageRegistry.ts: kept only the maxai block. The branch still carried microsoft-designer-web, which #11754 retired.
- models/route.ts: the conflicting hunk was an unrelated Vertex/Anthropic URL change, not MaxAI — tip's side.
- volcengine agent-plan/coding-plan registries: git auto-merged both sides and produced a duplicated supportsVision key, which TypeScript rejects (TS1117). Removed.

One real integration break that only the combined state shows: the MaxAI entry declared no serviceKinds, which #11392 made required a few hours ago. Provider validation threw at load time and check:provider-consistency crashed outright. Declared ["llm"] — the image kinds derive from imageRegistry, per the convention in that PR's backfill.

Every count was measured rather than taken from the branch, and each would have been wrong: reserved prefixes are 402, not the 397 the branch computed from its stale 395 base; providers are 353, not 354. PROVIDER_REFERENCE.md regenerated, the count updated across README/AGENTS.md/llm.txt and its 42 mirrors, package.json and 6 SVGs — every changed line in those files is a digit substitution and nothing else, verified by masking digits and comparing the removed and added sets (90 lines, identical). The executor-map golden snapshot was regenerated: keyCount 133 -> 134.

The branch's file-size-baseline.json predates #12411's ratchet re-tightening, so it was discarded rather than merged — taking it would have silently undone that. The three files this PR grows (proxyFetch.ts +20 for the Windows/firefox_150 TLS profile, imageGeneration.ts +12, models/route.ts +48) were entered against the current baseline under one _rebaseline annotation; no other cap moves.

Verified: typecheck:core clean, check:provider-consistency OK (269 REGISTRY entries, 353 canonical providers), check:docs-counts exit 0, check-file-size OK, check:cycles OK, and 79/79 across the MaxAI suites plus 21/21 reserved-prefix and 2/2 executor-map-golden.

Thanks @arminanton — the provider work itself is thorough; it was the 227 commits of base that needed the attention.
2026-09-02 01:55:42 -03:00

428 lines
16 KiB
TypeScript

/**
* MaxAI web-app signing constants — extracted live from the public JS bundle.
*
* MaxAI's request signer needs a small set of CLIENT-SIDE constants that its own
* front-end ships VERBATIM in the public `www.maxai.co` JavaScript bundle
* (identical for every visitor, no per-user or server secret). OmniRoute EXTRACTS
* them from the live bundle and persists them, so if MaxAI ever rotates a value —
* or a Next.js rebuild renumbers its chunks — the provider self-heals on the next
* login or daily refresh instead of hard-failing every signed call.
*
* NOTHING id/key/version-shaped is hardcoded anywhere (source OR tests). Every
* such value (hmacKey, aesKey, docIdKey, ctxKey, appVersion) is discovered at
* runtime and validated; the repo carries no scannable secret and no build-
* specific chunk number.
*
* WHAT is extracted, and from WHERE (all are plain, public static assets):
* pages/_app-*.js — the Next.js app-entry chunk (framework-STABLE name, not a
* MaxAI chunk number). Webpack module 69319 inside it defines the constants as
* export getters we follow to their string literals:
* - hmacKey export `Mn` → a hex string (HMAC-SHA1 → SM3 keying)
* - aesKey export `Rl` → a hex string (CryptoJS AES passphrase)
* - docIdKey export `U0` → a UUID (doc-upload HMAC key)
* - appVersion the sole `webpage_x.y.z` literal (folded into the sign_str)
* the SIGNER chunk — a NUMBERED chunk whose id changes across builds, so it is
* located by CONTENT FINGERPRINT (never by number): the chunk that assembles
* the signed payload, recognised by the ctx-slot pattern `"<40hex>":{a:…}` next
* to the `(0,r.nj)("<hex>")` header-name decoders. From it we read:
* - ctxKey the 40-hex payload content-slot label
* - headerNames the `nj("<hex>")` calls = hex→ASCII header/slot names
*
* The extracted set is SHAPE-validated (hex/UUID/version regexes) before it is
* trusted; the ULTIMATE validation is the first live signed call (a wrong value
* is rejected by MaxAI, which triggers a re-extract). Only the plain, non-secret
* HTTP header NAMES (e.g. "X-Authorization") keep in-code defaults, so a transient
* miss on the signer chunk can't break a signer that already has valid keys;
* extraction still overrides them when present.
*/
import { createHmac, createHash } from "node:crypto";
/** The public bundle base. `/app/` is the SPA entry that references the chunks. */
export const MAXAI_WEBAPP_ORIGIN = "https://www.maxai.co";
export const MAXAI_WEBAPP_APP_PATH = "/app/";
/** Settings key under which the extracted constants bundle is persisted. */
export const MAXAI_CONSTANTS_SETTINGS_KEY = "maxaiSigningConstants";
/** Firefox-150 UA used for the (unauthenticated) static-asset fetches. */
const FETCH_UA =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0";
/**
* The header/slot NAMES the signer emits. These are standard HTTP header names
* (not secrets, not id/key/version-shaped), so in-code defaults are appropriate;
* extraction overrides any that the signer chunk exposes.
*/
export interface MaxaiHeaderNames {
authorization: string; // "X-Authorization"
clientDomain: string; // "X-Client-Domain"
clientPath: string; // "X-Client-Path"
random: string; // "X-Random"
browserName: string; // "X-Browser-Name"
browserVersion: string; // "X-Browser-Version"
browserMajor: string; // "X-Browser-Major"
appVersionHeader: string; // "X-App-Version"
appEnvHeader: string; // "X-App-Env"
appEnvValue: string; // "MaxAI-Browser-Extension"
tSlot: string; // "t"
pSlot: string; // "p"
dSlot: string; // "d"
}
/** The full set of signing constants the MaxAI signer depends on. */
export interface MaxaiSigningConstants {
/** HMAC-SHA1 → SM3 keying material (extracted; no in-code default). */
hmacKey: string;
/** CryptoJS AES passphrase (extracted; no in-code default). */
aesKey: string;
/** Version string folded into the signature `sign_str` (extracted). */
appVersion: string;
/** Payload content-slot label, 40-hex (extracted; no in-code default). */
ctxKey: string;
/** Doc-upload HMAC key, UUID (extracted; no in-code default). */
docIdKey: string;
/** Header/slot names emitted by the signer. */
headerNames: MaxaiHeaderNames;
/** Provenance for the persisted record. */
source?: "extracted";
extractedAt?: number;
}
/**
* Default HTTP header NAMES (standard, non-secret labels). Extraction overrides
* any the signer chunk exposes; these keep a signer with valid keys working even
* if the signer chunk momentarily can't be located.
*/
export const MAXAI_DEFAULT_HEADER_NAMES: MaxaiHeaderNames = {
authorization: "X-Authorization",
clientDomain: "X-Client-Domain",
clientPath: "X-Client-Path",
random: "X-Random",
browserName: "X-Browser-Name",
browserVersion: "X-Browser-Version",
browserMajor: "X-Browser-Major",
appVersionHeader: "X-App-Version",
appEnvHeader: "X-App-Env",
appEnvValue: "MaxAI-Browser-Extension",
tSlot: "t",
pSlot: "p",
dSlot: "d",
};
/** Raw pieces the parser can pull from the two chunks (any may be absent). */
export interface MaxaiParsedConstants {
hmacKey: string | null;
aesKey: string | null;
appVersion: string | null;
ctxKey: string | null;
docIdKey: string | null;
headerNames: Partial<MaxaiHeaderNames>;
}
/** Resolve a webpack export getter `Name:function(){return VAR}` → the `VAR="…"` literal. */
export function resolveWebpackGetter(src: string, exportName: string): string | null {
const getter = new RegExp(
`${exportName}\\s*:\\s*function\\s*\\(\\)\\s*\\{\\s*return\\s+([A-Za-z_$][\\w$]*)\\s*\\}`
);
let m = src.match(getter);
if (!m) {
const arrow = new RegExp(`${exportName}\\s*:\\s*\\(\\)\\s*=>\\s*([A-Za-z_$][\\w$]*)`);
m = src.match(arrow);
}
if (!m) return null;
const varName = m[1];
const assign = new RegExp(`\\b${varName}\\s*=\\s*"([^"]+)"`);
const am = src.match(assign);
return am ? am[1] : null;
}
/** Decode the `(0,r.nj)("<hex>")` header-name calls (nj = hex→ASCII). */
export function decodeNjHeaderNames(signerChunk: string): string[] {
const out = new Set<string>();
for (const m of signerChunk.matchAll(/nj\)\("([0-9a-f]+)"\)/g)) {
try {
const decoded = Buffer.from(m[1], "hex").toString("utf8");
// Keep only printable ASCII header-ish tokens (drop numeric ja3 codes etc).
if (/^[\x20-\x7e]+$/.test(decoded)) out.add(decoded);
} catch {
// skip malformed hex
}
}
return [...out];
}
/** Map the decoded header-name list onto the structured MaxaiHeaderNames slots. */
function mapHeaderNames(decoded: string[]): Partial<MaxaiHeaderNames> {
const has = (v: string) => decoded.includes(v);
const out: Partial<MaxaiHeaderNames> = {};
if (has("X-Authorization")) out.authorization = "X-Authorization";
if (has("X-Client-Domain")) out.clientDomain = "X-Client-Domain";
if (has("X-Client-Path")) out.clientPath = "X-Client-Path";
if (has("X-Random")) out.random = "X-Random";
if (has("X-Browser-Name")) out.browserName = "X-Browser-Name";
if (has("X-Browser-Version")) out.browserVersion = "X-Browser-Version";
if (has("X-Browser-Major")) out.browserMajor = "X-Browser-Major";
if (has("X-App-Version")) out.appVersionHeader = "X-App-Version";
if (has("X-App-Env")) out.appEnvHeader = "X-App-Env";
if (has("MaxAI-Browser-Extension")) out.appEnvValue = "MaxAI-Browser-Extension";
return out;
}
/** Extract the 40-hex payload content-slot label from the signer chunk. */
export function extractCtxKey(signerChunk: string): string | null {
return (signerChunk.match(/"([0-9a-f]{40})"\s*:\s*\{\s*a\s*:/) || [])[1] ?? null;
}
/**
* Content fingerprint for the SIGNER chunk (build-independent). The signer chunk
* is the one that both (a) carries the ctx payload slot `"<40hex>":{a:…}` and
* (b) decodes header names via `(0,r.nj)("<hex>")`. Matching BOTH avoids a false
* positive on any unrelated chunk that merely contains a 40-hex string.
*/
export function looksLikeSignerChunk(js: string): boolean {
return extractCtxKey(js) !== null && /nj\)\("[0-9a-f]+"\)/.test(js);
}
/**
* Parse the two bundle chunks into raw constants. Pure (no network) so it is
* unit-tested directly against synthetic fixtures.
*/
export function parseMaxaiConstants(
appChunk: string,
signerChunk: string
): MaxaiParsedConstants {
const decoded = decodeNjHeaderNames(signerChunk);
return {
hmacKey: resolveWebpackGetter(appChunk, "Mn"),
aesKey: resolveWebpackGetter(appChunk, "Rl"),
docIdKey: resolveWebpackGetter(appChunk, "U0"),
appVersion: (appChunk.match(/"(webpage_\d+\.\d+\.\d+)"/) || [])[1] ?? null,
ctxKey: extractCtxKey(signerChunk),
headerNames: mapHeaderNames(decoded),
};
}
/** A MaxAI signing key is a 40+ char lowercase hex string. */
function isHexKey(v: string | null | undefined): boolean {
return typeof v === "string" && /^[0-9a-f]{40,}$/.test(v);
}
/** A doc-id key is a UUID (v4-shaped). */
function isUuidKey(v: string | null | undefined): boolean {
return typeof v === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(v);
}
/** A MaxAI app_version tag looks like `webpage_x.y.z`. */
function isAppVersion(v: string | null | undefined): boolean {
return typeof v === "string" && /^webpage_\d+\.\d+\.\d+$/.test(v);
}
/**
* Fold parsed pieces into a full constants object. The five extracted values
* (hmacKey, aesKey, ctxKey, docIdKey, appVersion) are ALL required and must be
* well-formed — return null otherwise, so we never persist a half-configured
* signer. Only the plain HTTP header names fall back to the standard defaults.
*/
export function assembleMaxaiConstants(
parsed: MaxaiParsedConstants
): MaxaiSigningConstants | null {
if (!isHexKey(parsed.hmacKey) || !isHexKey(parsed.aesKey)) return null;
if (!isHexKey(parsed.ctxKey)) return null;
if (!isUuidKey(parsed.docIdKey)) return null;
if (!isAppVersion(parsed.appVersion)) return null;
return {
hmacKey: parsed.hmacKey as string,
aesKey: parsed.aesKey as string,
appVersion: parsed.appVersion as string,
ctxKey: parsed.ctxKey as string,
docIdKey: parsed.docIdKey as string,
headerNames: { ...MAXAI_DEFAULT_HEADER_NAMES, ...parsed.headerNames },
source: "extracted",
extractedAt: Date.now(),
};
}
/** True when a constants object is structurally well-formed (all 5 values valid). */
export function isValidConstantsShape(c: MaxaiSigningConstants | null | undefined): boolean {
if (!c) return false;
return (
isHexKey(c.hmacKey) &&
isHexKey(c.aesKey) &&
isHexKey(c.ctxKey) &&
isUuidKey(c.docIdKey) &&
isAppVersion(c.appVersion) &&
!!c.headerNames
);
}
/**
* A signature vector: a (path, reqTime, userId, appVersion) tuple and the SM3
* proof it should produce. Used to prove the signing ALGORITHM in unit tests with
* mock keys — the runtime does NOT embed any real vector (its trust anchor is the
* live signed probe). `reproduceProof` is a pure helper over the same math.
*/
export interface MaxaiSignatureVector {
path: string;
reqTime: number;
userId: string;
appVersion: string;
expectedProof: string;
}
/** Reproduce the SM3 proof `p` for a (path, reqTime, userId, appVersion) under a key. */
export function reproduceProof(
hmacKey: string,
vector: Omit<MaxaiSignatureVector, "expectedProof">
): string {
const signStr = `${vector.appVersion}:${vector.reqTime}:${vector.path}:${vector.userId}`;
const sha1 = createHmac("sha1", Buffer.from(`${vector.reqTime}:${hmacKey}`, "utf8"))
.update(Buffer.from(signStr, "utf8"))
.digest("hex");
return createHash("sm3")
.update(Buffer.from(`${vector.reqTime}:${sha1}:${hmacKey}`, "utf8"))
.digest("hex");
}
/**
* Runtime validation of an extracted/stored constants set. SHAPE-based on purpose:
* we carry no real signature vector in source, so the definitive check is the
* first live signed call (a wrong value is rejected by MaxAI → re-extract). An
* optional `vector` enables proof-based checking in tests with mock keys.
*/
export function validateMaxaiConstants(
constants: MaxaiSigningConstants,
vector?: MaxaiSignatureVector
): boolean {
if (!isValidConstantsShape(constants)) return false;
if (!vector) return true;
try {
return reproduceProof(constants.hmacKey, vector) === vector.expectedProof;
} catch {
return false;
}
}
/**
* Fetch a text asset with the Firefox UA through the ambient (residential) fetch.
* Injectable for tests. Returns "" on any failure (caller treats empty as miss).
*/
async function fetchText(
url: string,
fetchImpl: typeof fetch,
signal?: AbortSignal | null
): Promise<string> {
try {
const res = await fetchImpl(url, {
headers: { "User-Agent": FETCH_UA, Accept: "*/*" },
signal: signal ?? undefined,
});
if (!res.ok) return "";
return await res.text();
} catch {
return "";
}
}
/** All `/_next/static/chunks/...js` URLs referenced by the app HTML, in order. */
export function allChunkUrls(html: string): string[] {
const seen = new Set<string>();
const out: string[] = [];
for (const m of html.matchAll(/\/_next\/static\/chunks\/[A-Za-z0-9/_-]+\.js/g)) {
if (!seen.has(m[0])) {
seen.add(m[0]);
out.push(m[0]);
}
}
return out;
}
/**
* From the `/app/` HTML, resolve the app-entry chunk (by its stable Next.js
* `pages/_app-*.js` name) and the list of candidate numbered chunks to scan for
* the signer chunk BY CONTENT. No specific chunk number is ever assumed.
*/
export function findChunkUrls(html: string): {
appChunk: string | null;
candidateChunks: string[];
} {
const urls = allChunkUrls(html);
let appChunk: string | null = null;
const candidateChunks: string[] = [];
for (const p of urls) {
if (/\/pages\/_app-[a-z0-9]+\.js$/i.test(p)) {
appChunk = p;
} else if (/\/chunks\/[A-Za-z0-9]+-[a-z0-9]+\.js$/i.test(p)) {
// Any hashed vendor/number chunk is a signer-chunk candidate; we identify
// the real one by content, not by its (build-specific) name.
candidateChunks.push(p);
}
}
return { appChunk, candidateChunks };
}
export interface FetchConstantsOptions {
fetchImpl?: typeof fetch;
signal?: AbortSignal | null;
/** Override the origin (tests). */
origin?: string;
/** Cap on how many candidate chunks to scan for the signer chunk (default 80). */
maxScanChunks?: number;
}
/**
* Locate + fetch the signer chunk text by CONTENT (never by number): scan the
* candidate chunks referenced in the app HTML and return the first whose content
* matches the signer fingerprint (ctx slot + nj header decoders). A MaxAI-side
* chunk renumber is therefore self-healing, not a break.
*/
async function fetchSignerChunk(
origin: string,
candidates: string[],
fetchImpl: typeof fetch,
signal: AbortSignal | null | undefined,
maxScan: number
): Promise<string> {
for (const c of candidates.slice(0, maxScan)) {
const js = await fetchText(origin + c, fetchImpl, signal);
if (js && looksLikeSignerChunk(js)) return js;
}
return "";
}
/**
* Fetch + parse the live constants from MaxAI's public bundle. Returns a fully
* assembled, SHAPE-validated constants object, or null on any failure (network,
* missing chunk, unparseable, malformed values). Never throws. The definitive
* key validation is the caller's first live signed call.
*/
export async function fetchMaxaiConstants(
opts: FetchConstantsOptions = {}
): Promise<MaxaiSigningConstants | null> {
const fetchImpl = opts.fetchImpl ?? fetch;
const origin = opts.origin ?? MAXAI_WEBAPP_ORIGIN;
const maxScan = opts.maxScanChunks ?? 80;
const html = await fetchText(origin + MAXAI_WEBAPP_APP_PATH, fetchImpl, opts.signal);
if (!html) return null;
const { appChunk, candidateChunks } = findChunkUrls(html);
if (!appChunk) return null;
const appJs = await fetchText(origin + appChunk, fetchImpl, opts.signal);
if (!appJs) return null;
const signerJs = await fetchSignerChunk(
origin,
candidateChunks,
fetchImpl,
opts.signal,
maxScan
);
const parsed = parseMaxaiConstants(appJs, signerJs);
const assembled = assembleMaxaiConstants(parsed);
if (!assembled) return null;
if (!validateMaxaiConstants(assembled)) return null;
return assembled;
}