fix(muse-spark-web): document the ecto1: WS auth token requirement in credential hint, spec, and error message (#9502)

Closes #9502
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-05 16:49:04 -03:00
committed by GitHub
parent d931b907bf
commit dc06bf558e
8 changed files with 88 additions and 14 deletions

View File

@@ -0,0 +1 @@
- fix(muse-spark-web): document the ecto1: WS auth token requirement in the credential hint, spec, and error message (#9502)

View File

@@ -1,16 +1,16 @@
---
title: "Provider Reference"
version: 3.8.50
lastUpdated: 2026-07-30
lastUpdated: 2026-08-05
---
# Provider Reference
> **Auto-generated** from `src/shared/constants/providers.ts` — do not edit by hand.
> Regenerate with: `npm run gen:provider-reference`
> **Last generated:** 2026-07-30
> **Last generated:** 2026-08-05
Total providers: **290**. See category breakdown below.
Total providers: **291**. See category breakdown below.
## Categories
@@ -84,7 +84,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `kimi-web` | `kimi-web` | Kimi Web | Web cookie | [link](https://www.kimi.com/code?aff=omniroute) | Paste access_token from www.kimi.com DevTools → Application → Local Storage. A legacy kimi-auth cookie is also accepted. | — |
| `lmarena` | `lma` | Arena (Free) | Web cookie | [link](https://arena.ai) | Paste the full Cookie header from arena.ai (DevTools → Network → request → Cookie). Include arena-auth-prod-v1.0/.1… and cf_clearance/__cf_bm when present. OmniRoute uses Chrome TLS impersonation; if Arena still 403s, set providerSpecificData.recaptchaV3Token from a live browser session. | — |
| `microsoft-designer-web` | `msdesigner` | Microsoft Designer (Image Generation) | Web cookie | [link](https://designer.microsoft.com) | Sign in at designer.microsoft.com, then open DevTools → Network, generate an image, and find the request to DallE.ashx?action=GetDallEImagesCogSci. Copy the value of its Authorization: Bearer header (the access_token — no 'Bearer ' prefix). The token is short-lived; this is an unofficial, reverse-engineered integration. | — |
| `muse-spark-web` | `ms-web` | Muse Spark Web (Meta AI) | Web cookie | [link](https://www.meta.ai) | Paste your ecto_1_sess value or full cookie header from meta.ai | emulated |
| `muse-spark-web` | `ms-web` | Muse Spark Web (Meta AI) | Web cookie | [link](https://www.meta.ai) | Paste your ecto_1_sess cookie AND the ecto1:... WS auth token from meta.ai. Capture the ecto1: token in DevTools → Network → WS → the clippy request's Authorization query param. Example: ecto_1_sess=4240a308...NVDg0; ecto1:ABCD... | emulated |
| `notion-web` | `nw` | Notion AI Web (Unofficial/Experimental) | Web cookie | [link](https://www.notion.so) | Paste only the token_v2 cookie VALUE from app.notion.com (DevTools → Application → Cookies → token_v2). Do not paste token_v2= or the full Cookie header. Workspace is auto-detected; space_id / notion_user_id are optional. | — |
| `perplexity-web` | `pplx-web` | Perplexity Web (Pro/Max) | Web cookie | [link](https://www.perplexity.ai) | Paste your __Secure-next-auth.session-token cookie value from perplexity.ai | emulated |
| `poe-web` | `poe` | Poe Web (Subscription) | Web cookie | [link](https://poe.com) | Paste your p-b cookie value from poe.com (DevTools → Application → Cookies → p-b) | — |
@@ -97,7 +97,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `zai-web` | `zw` | Z.ai Web (Free) | Web cookie | [link](https://chat.z.ai) | Paste the full Cookie header from chat.z.ai (must include the token=<JWT> cookie) | — |
| `zenmux-free` | `zmf` | ZenMux Free (Web) | Web cookie | [link](https://zenmux.ai) | Login at zenmux.ai, then export all cookies using EditThisCookie or Cookie-Editor and paste the full Cookie header string here. Refresh every ~30 days. | — |
## API Key Providers (paid / paid-with-free-credits) (195)
## API Key Providers (paid / paid-with-free-credits) (196)
| ID | Alias | Name | Tags | Website | Notes |
|----|-------|------|------|---------|-------|
@@ -130,6 +130,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `bytez` | `bytez` | Bytez | API key | [link](https://bytez.com) | $1 free credits, refreshes every 4 weeks |
| `cerebras` | `cerebras` | Cerebras | API key | [link](https://inference.cerebras.ai) | Free Trial: 1M tokens/day, 30K TPM, 5 RPM — no credit card. |
| `charm-hyper` | `charm-hyper` | Charm Hyper | API key | [link](https://hyper.charm.land) | 100 free monthly Hypercredits on signup |
| `cheaperinference` | `cinf` | Cheaper Inference | API key | [link](https://cheaperinference.com/?utm_source=omniroute) | — |
| `chenzk` | `chenzk` | Chenzk API | API key | [link](https://chenzk.top) | — |
| `chutes` | `chutes` | Chutes.ai | API key, aggregator | [link](https://chutes.ai) | Bearer API key for the Chutes OpenAI-compatible gateway. |
| `clarifai` | `clarifai` | Clarifai | API key, enterprise | [link](https://docs.clarifai.com) | Use your Clarifai PAT or app-specific API key. OmniRoute targets the OpenAI-compatible endpoint at https://api.clarifai.com/v2/ext/openai/v1 and authenticates with Authorization: Key <token>. |

View File

@@ -1287,7 +1287,7 @@ export class MuseSparkWebExecutor extends BaseExecutor {
if (!authorization) {
return errorResult(
400,
"Missing Authorization for Meta AI WebSocket — your cookie must include an ecto1:... auth token.",
"Missing Authorization for Meta AI WebSocket — paste the ecto1:... WS auth token from meta.ai DevTools (Network → WS → clippy request Authorization param), alongside your ecto_1_sess cookie.",
"missing_authorization",
{},
body

View File

@@ -50,14 +50,14 @@ export async function validateMuseSparkWebProvider({ apiKey, providerSpecificDat
if (response.status === 401 || response.status === 403) {
return {
valid: false,
error: "Invalid Meta AI session cookie — re-paste abra_sess from meta.ai",
error: "Invalid Meta AI session cookie — re-paste ecto_1_sess from meta.ai",
};
}
if (/authentication required to send messages|login is required|sign in/i.test(responseText)) {
return {
valid: false,
error: "Invalid Meta AI session cookie — re-paste abra_sess from meta.ai",
error: "Invalid Meta AI session cookie — re-paste ecto_1_sess from meta.ai",
};
}

View File

@@ -81,7 +81,10 @@ export const WEB_COOKIE_PROVIDERS = {
website: "https://www.meta.ai",
hasFree: true,
freeNote: "Free with login — Meta AI platform with Llama models.",
authHint: "Paste your ecto_1_sess value or full cookie header from meta.ai",
authHint:
"Paste your ecto_1_sess cookie AND the ecto1:... WS auth token from meta.ai. " +
"Capture the ecto1: token in DevTools → Network → WS → the clippy request's Authorization query param. " +
"Example: ecto_1_sess=4240a308...NVDg0; ecto1:ABCD...",
toolCalling: "emulated",
},
"claude-web": {

View File

@@ -98,10 +98,14 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
},
"muse-spark-web": {
kind: "cookie",
credentialName: "abra_sess",
placeholder: "abra_sess=...; other=value",
// #9502: the WS protocol (#7528) needs both the ecto_1_sess cookie (GraphQL
// warmup/mode-switch) and a separate ecto1:... WS auth token (Authorization
// query param on wss://gateway.meta.ai/ws/clippy). The executor extracts the
// ecto1: token from the apiKey field via /ecto1:[^\s;]+/i.
credentialName: "ecto_1_sess + ecto1: WS auth token",
placeholder: "ecto_1_sess=...; ecto1:... (WS auth token from meta.ai DevTools → Network → WS → clippy)",
acceptsFullCookieHeader: true,
storageKeys: ["cookie", "abra_sess"],
storageKeys: ["cookie", "ecto_1_sess", "abra_sess"],
},
"hailuo-web": {
kind: "token",

View File

@@ -19,12 +19,15 @@ const webCookie = readFileSync(
const executor = readFileSync(join(root, "open-sse", "executors", "muse-spark-web.ts"), "utf8");
test("provider form hint points at the live ecto_1_sess cookie, not retired abra_sess", () => {
// #9502: the hint now names BOTH the ecto_1_sess cookie and the ecto1: WS auth
// token; the live-cookie-name guard (ecto_1_sess present, retired abra_sess
// absent) still holds.
assert.ok(
webCookie.includes("Paste your ecto_1_sess value"),
webCookie.includes("ecto_1_sess"),
"muse-spark authHint must name ecto_1_sess"
);
assert.ok(
!webCookie.includes("Paste your abra_sess"),
!/Paste your abra_sess/.test(webCookie),
"muse-spark authHint must not name the retired abra_sess cookie"
);
});

View File

@@ -0,0 +1,62 @@
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
import { MuseSparkWebExecutor } from "../../open-sse/executors/muse-spark-web.ts";
// #9502: the WS migration (#7528) requires a separate ecto1:... auth token the
// guidance never mentions, so a cookie-only credential (the documented input)
// always fails with 400 "Missing Authorization".
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, "..", "..");
const webCookie = readFileSync(
join(root, "src", "shared", "constants", "providers", "web-cookie.ts"),
"utf8"
);
const webSessionCredentials = readFileSync(
join(root, "src", "shared", "providers", "webSessionCredentials.ts"),
"utf8"
);
const executor = readFileSync(join(root, "open-sse", "executors", "muse-spark-web.ts"), "utf8");
test("#9502: provider authHint mentions the ecto1: WS auth token, not only the ecto_1_sess cookie", () => {
// Extract the muse-spark-web block from the first `"muse-spark-web": {` (the
// key declaration, not the `id:` value) up to the next top-level provider key.
const startIdx = webCookie.indexOf('"muse-spark-web": {');
assert.ok(startIdx >= 0, "muse-spark-web block not found");
const museSection = webCookie.slice(startIdx, webCookie.indexOf('"claude-web"', startIdx));
assert.match(museSection, /ecto1/, "authHint must mention the ecto1: WS auth token");
});
test("#9502: web-session credential spec for muse-spark-web mentions the ecto1: WS auth token", () => {
const startIdx = webSessionCredentials.indexOf('"muse-spark-web": {');
assert.ok(startIdx >= 0, "muse-spark-web credential spec not found");
const museSection = webSessionCredentials.slice(
startIdx,
webSessionCredentials.indexOf('"hailuo-web"', startIdx)
);
assert.match(museSection, /ecto1/, "credential spec must mention the ecto1: WS auth token");
});
test("#9502: the Missing Authorization error message guides the user to the ecto1: token", () => {
assert.ok(/Missing Authorization.*ecto1:/.test(executor), "missing-auth error must name the ecto1: token");
});
test("#9502: a cookie-only credential (no ecto1: token) is rejected with 400 — the actual user failure", async () => {
const exec = new MuseSparkWebExecutor();
const result = await exec.execute({
model: "muse-spark",
body: { messages: [{ role: "user", content: "hello" }] },
stream: false,
credentials: { apiKey: "ecto_1_sess=4240a308abcdefNVDg0", connectionId: "conn-9502" },
signal: null,
log: null,
upstreamExtraHeaders: undefined,
} as Parameters<typeof exec.execute>[0]);
assert.equal(result.response.status, 400, "cookie-only credential is rejected");
const body = await result.response.json();
assert.match(body.error.message, /Missing Authorization for Meta AI WebSocket/);
});