mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 22:02:08 +03:00
Notion AI has no public inference API (see closed request #3272), so this adds it as a new entry in the established web-cookie provider category (chatgpt-web, claude-web, grok-web, ...): cookie-based auth via the token_v2 session cookie posted to Notion's undocumented internal POST /api/v3/runInferenceTranscript endpoint, translating its NDJSON transcript-patch stream into OpenAI-compatible chat completions. - NotionWebExecutor (open-sse/executors/notion-web.ts): resolves the token_v2 cookie (+ optional space_id/notion_browser_id), builds a Notion transcript from the chat messages, parses the NDJSON response (cumulative-snapshot semantics, mirroring gemini-web.ts's handling of #7163), and returns a chat.completion or pseudo-streamed SSE response. All error paths route through makeExecutorErrorResult (sanitized). - RegistryEntry under open-sse/config/providers/registry/notion-web/, registered in providers/index.ts REGISTRY and executors/index.ts (alias "nw"). - WEB_COOKIE_PROVIDERS entry (src/shared/constants/providers/web-cookie.ts) with subscriptionRisk + webCookie risk notice, clearly labeled "(Unofficial/Experimental)". - Cookie-probe validator (validateNotionWebProvider) against Notion's getSpaces endpoint, and a webSessionCredentials.ts UI entry for the "Add session cookie" flow. - Regenerated docs/reference/PROVIDER_REFERENCE.md and the provider/translate-path golden snapshot (purely additive diffs); synced the "251 providers" count across README/AGENTS/CLAUDE.md (check:docs-counts STRICT gate). Tests: tests/unit/executor-notion-web.test.ts (22 cases — registry consistency, mocked-upstream request/response translation, NDJSON snapshot parsing, cookie resolution, sanitized error paths) plus the existing executor-web-cookie-sweep, provider-alias-uniqueness, check-provider-consistency, web-session-credentials, and provider-translate-path-golden suites all pass with notion-web included.
This commit is contained in:
committed by
GitHub
parent
7b564ab5db
commit
df1ed57876
@@ -52,6 +52,7 @@ import {
|
||||
validateJulesProvider,
|
||||
validateDevinCloudAgentProvider,
|
||||
validateInnerAiProvider,
|
||||
validateNotionWebProvider,
|
||||
} from "./validation/webProvidersB";
|
||||
import {
|
||||
validateHerokuProvider,
|
||||
@@ -532,6 +533,7 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
|
||||
"adapta-web": validateAdaptaWebProvider,
|
||||
"claude-web": validateClaudeWebProvider,
|
||||
"gemini-web": validateGeminiWebProvider,
|
||||
"notion-web": validateNotionWebProvider,
|
||||
"copilot-m365-web": validateCopilotM365WebProvider,
|
||||
"copilot-web": validateCopilotWebProvider,
|
||||
"t3-web": validateT3WebProvider,
|
||||
|
||||
@@ -486,6 +486,57 @@ export async function validateDevinCloudAgentProvider({ apiKey }: { apiKey: stri
|
||||
}
|
||||
}
|
||||
|
||||
// ── Notion AI Web (Unofficial/Experimental) cookie validator ──
|
||||
// #6758: no public Notion inference API exists; validate by probing a stable,
|
||||
// low-privilege authenticated Notion endpoint (getSpaces) with the session
|
||||
// cookie rather than the experimental runInferenceTranscript endpoint itself
|
||||
// (a live inference call is expensive and unnecessary just to confirm the
|
||||
// session is valid).
|
||||
export async function validateNotionWebProvider({ apiKey, providerSpecificData = {} }: any) {
|
||||
try {
|
||||
const raw = String(apiKey || "").trim();
|
||||
if (!raw) {
|
||||
return { valid: false, error: "Paste your token_v2 cookie value from notion.so" };
|
||||
}
|
||||
|
||||
const cookieHeader = raw.includes("=") ? raw : `token_v2=${raw}`;
|
||||
|
||||
const response = await validationWrite("https://www.notion.so/api/v3/getSpaces", {
|
||||
method: "POST",
|
||||
headers: applyCustomUserAgent(
|
||||
{
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
Cookie: cookieHeader,
|
||||
Origin: "https://www.notion.so",
|
||||
Referer: "https://www.notion.so/",
|
||||
},
|
||||
providerSpecificData
|
||||
),
|
||||
body: "{}",
|
||||
});
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
return {
|
||||
valid: false,
|
||||
error: "Invalid or expired token_v2 cookie — re-paste from notion.so DevTools → Cookies",
|
||||
};
|
||||
}
|
||||
|
||||
if (response.status >= 500) {
|
||||
return { valid: false, error: `Notion unavailable (${response.status})` };
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
return { valid: true, error: null };
|
||||
}
|
||||
|
||||
return { valid: false, error: `Notion validation failed (${response.status})` };
|
||||
} catch (error: any) {
|
||||
return toValidationErrorResult(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateInnerAiProvider({ apiKey, providerSpecificData = {} }: any) {
|
||||
try {
|
||||
const raw = typeof apiKey === "string" ? apiKey.trim() : "";
|
||||
|
||||
@@ -342,6 +342,25 @@ export const WEB_COOKIE_PROVIDERS = {
|
||||
riskNoticeVariant: "webCookie",
|
||||
authHint: "Paste the full Cookie header from chat.z.ai (must include the token=<JWT> cookie)",
|
||||
},
|
||||
"notion-web": {
|
||||
id: "notion-web",
|
||||
alias: "nw",
|
||||
name: "Notion AI Web (Unofficial/Experimental)",
|
||||
icon: "auto_awesome",
|
||||
color: "#000000",
|
||||
textIcon: "NW",
|
||||
website: "https://www.notion.so",
|
||||
// #6758: Notion has no public inference API (see closed request #3272) — this
|
||||
// reverse-engineers the same undocumented internal endpoint two independent
|
||||
// open-source projects already use. Undocumented endpoints can change without
|
||||
// notice; label clearly so operators understand the risk before pasting a
|
||||
// session cookie of an account they already pay for.
|
||||
subscriptionRisk: true,
|
||||
riskNoticeVariant: "webCookie",
|
||||
authHint:
|
||||
"Paste your token_v2 cookie value from notion.so (DevTools → Application → Cookies). " +
|
||||
"Optionally append `; space_id=...` and/or `; notion_browser_id=...` if your workspace requires them.",
|
||||
},
|
||||
};
|
||||
|
||||
/** Resolved public site for a web-session provider (href + display host). */
|
||||
|
||||
@@ -53,6 +53,13 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
|
||||
acceptsFullCookieHeader: true,
|
||||
storageKeys: ["cookie", "__Secure-1PSID", "__Secure-1PSIDTS"],
|
||||
},
|
||||
"notion-web": {
|
||||
kind: "cookie",
|
||||
credentialName: "token_v2 (optional: space_id, notion_browser_id)",
|
||||
placeholder: "token_v2=...; space_id=...; notion_browser_id=...",
|
||||
acceptsFullCookieHeader: true,
|
||||
storageKeys: ["cookie", "token_v2", "space_id", "notion_browser_id"],
|
||||
},
|
||||
"gemini-business": {
|
||||
kind: "cookie",
|
||||
credentialName: "__Secure-1PSID (optional: __Secure-1PSIDTS)",
|
||||
|
||||
Reference in New Issue
Block a user