diff --git a/open-sse/executors/claude-web-auto-refresh.ts b/open-sse/executors/claude-web-auto-refresh.ts deleted file mode 100644 index 8544a66616..0000000000 --- a/open-sse/executors/claude-web-auto-refresh.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Claude Web Executor with Auto-Refresh cf_clearance - * - * Wraps the existing ClaudeWebExecutor with Turnstile solving capability. - * When cf_clearance is missing or invalid, automatically solves Cloudflare - * Turnstile challenge and injects fresh token. - */ - -import type { ExecuteInput } from "./base.ts"; -import { ClaudeWebExecutor } from "./claude-web.ts"; -import { getCfClearanceToken } from "../services/claudeTurnstileSolver.ts"; - -/** - * Enhanced executor with auto-refresh - */ -export class ClaudeWebAutoRefreshExecutor extends ClaudeWebExecutor { - /** - * Override execute to add cf_clearance auto-refresh - */ - async execute(input: ExecuteInput) { - const { credentials, log, signal } = input; - - // First attempt with provided credentials - let result = await super.execute(input); - - // Check if response is a 403 (Cloudflare challenge) or 401 (invalid cf_clearance) - if (result.response.status === 403 || result.response.status === 401) { - log?.warn?.( - "CLAUDE-WEB", - `HTTP ${result.response.status} - attempting to refresh cf_clearance` - ); - - try { - // Attempt to solve Turnstile and get fresh cf_clearance - const freshCfClearance = await getCfClearanceToken({ force: true }); - - // Update credentials with fresh cf_clearance - const updatedCreds = { - ...credentials, - cookie: credentials?.cookie - ? `${credentials.cookie}; cf_clearance=${freshCfClearance}` - : `cf_clearance=${freshCfClearance}`, - }; - - log?.info?.("CLAUDE-WEB", "cf_clearance refreshed, retrying request"); - - // Retry with fresh cookie - result = await super.execute({ - ...input, - credentials: updatedCreds, - }); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - log?.error?.("CLAUDE-WEB", `Failed to auto-refresh cf_clearance: ${message}`); - // Return original error response - } - } - - return result; - } - - /** - * Override testConnection to include cf_clearance check - */ - async testConnection( - credentials: Record, - signal?: AbortSignal - ): Promise { - try { - // Test with provided credentials first - const basicTest = await super.testConnection(credentials, signal); - if (basicTest) { - return true; - } - - // If basic test failed, try to refresh cf_clearance - const rawCookie = String((credentials as any)?.cookie || ""); - if (!rawCookie.trim()) { - return false; - } - - const freshCfClearance = await getCfClearanceToken(); - const updatedCreds = { - ...credentials, - cookie: `${rawCookie}; cf_clearance=${freshCfClearance}`, - }; - - return await super.testConnection(updatedCreds, signal); - } catch (error) { - return false; - } - } -} - -// Export singleton instance -export const claudeWebAutoRefresh = new ClaudeWebAutoRefreshExecutor(); diff --git a/open-sse/executors/claude-web.ts b/open-sse/executors/claude-web.ts index 0734556305..dc35c0c8d4 100644 --- a/open-sse/executors/claude-web.ts +++ b/open-sse/executors/claude-web.ts @@ -22,8 +22,7 @@ import { BaseExecutor, mergeAbortSignals, type ExecuteInput } from "./base.ts"; import { FETCH_TIMEOUT_MS } from "../config/constants.ts"; import { tlsFetchClaude } from "../services/claudeTlsClient.ts"; -import { createAutoRefreshMiddleware, refreshCookie } from "../services/claudeWebAutoRefresh.ts"; -import { getCfClearanceToken, getCacheStatus } from "../services/claudeTurnstileSolver.ts"; +import { getCfClearanceToken } from "../services/claudeTurnstileSolver.ts"; import { normalizeSessionCookieHeader } from "@/lib/providers/webCookieAuth"; import { randomUUID } from "crypto"; import { sanitizeErrorMessage } from "../utils/error.ts"; @@ -45,16 +44,6 @@ const DEFAULT_CLAUDE_MODEL = "claude-sonnet-4-6"; /** * Extended credentials to include organization and conversation context */ -interface ClaudeWebCredentials { - cookie: string; - deviceId?: string; - orgId?: string; - conversationId?: string; -} - -/** - * Full request payload matching real Claude Web API format - */ interface ClaudeWebRequestPayload { prompt: string; model: string; @@ -175,7 +164,8 @@ async function normalizeClaudeSessionCookieWithAutoRefresh( options?.log?.info?.("CLAUDE-WEB", "cf_clearance injected successfully"); } catch (error) { const message = error instanceof Error ? error.message : String(error); - // Continue anyway - request might fail, but that's OK + options?.log?.warn?.("CLAUDE-WEB", `cf_clearance injection failed: ${message}`); + // Continue anyway - the retry wrapper will handle 403 } } @@ -429,7 +419,7 @@ export class ClaudeWebExecutor extends BaseExecutor { /** * Get user's organization ID from session */ - async execute({ model, body, stream, credentials, signal, log }: ExecuteInput) { + async execute({ model, body, stream: _stream, credentials, signal, log }: ExecuteInput) { const bodyObj = (body || {}) as Record; try { @@ -479,7 +469,10 @@ export class ClaudeWebExecutor extends BaseExecutor { }; } - const cookieHeader = await normalizeClaudeSessionCookieWithAutoRefresh(rawCookie, { log }); + const cookieHeader = await normalizeClaudeSessionCookieWithAutoRefresh(rawCookie, { + allowAutoSolve: true, + log, + }); const deviceId = (credentials as any)?.deviceId as string | undefined; // Transform request to Claude format @@ -542,7 +535,7 @@ export class ClaudeWebExecutor extends BaseExecutor { log?.debug?.("CLAUDE-WEB", `Making request to ${completionUrl}`); - // Inject cf_clearance before calling tlsFetchClaude + // cf_clearance is already injected via normalizeClaudeSessionCookieWithAutoRefresh above const fetchResponse = await tlsFetchClaude(completionUrl, { method: "POST", diff --git a/src/app/(dashboard)/dashboard/providers/[id]/webSessionCredentials.ts b/src/app/(dashboard)/dashboard/providers/[id]/webSessionCredentials.ts index d479c1235f..7bfe694dea 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/webSessionCredentials.ts +++ b/src/app/(dashboard)/dashboard/providers/[id]/webSessionCredentials.ts @@ -69,12 +69,6 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = { placeholder: "access_token=... or a DevTools HAR export", acceptsFullCookieHeader: false, }, - "veoaifree-web": { - kind: "none", - credentialName: "", - placeholder: "", - acceptsFullCookieHeader: false, - }, "t3-web": { kind: "cookie", credentialName: "convex-session-id + Cookie header", @@ -93,12 +87,6 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = { placeholder: "token_value user@example.com", acceptsFullCookieHeader: false, }, - "duckduckgo-web": { - kind: "none", - credentialName: "", - placeholder: "", - acceptsFullCookieHeader: false, - }, huggingchat: { kind: "cookie", credentialName: "hf-chat", diff --git a/src/shared/constants/providers.ts b/src/shared/constants/providers.ts index f34e588a30..ece95f716f 100644 --- a/src/shared/constants/providers.ts +++ b/src/shared/constants/providers.ts @@ -45,6 +45,32 @@ export const NOAUTH_PROVIDERS = { text: "OpenCode Free uses the public OpenCode endpoint (https://opencode.ai/zen/v1). No signup or API key needed. Rate limits apply.", }, }, + "duckduckgo-web": { + id: "duckduckgo-web", + alias: "ddgw", + name: "DuckDuckGo AI Chat", + icon: "auto_awesome", + color: "#DE5833", + textIcon: "DDG", + website: "https://duckduckgo.com/duckchat", + noAuth: true, + hasFree: true, + freeNote: "Free — anonymous access to multiple AI models via DuckDuckGo.", + authHint: "No credentials required — DuckDuckGo AI Chat is anonymous and free.", + }, + "veoaifree-web": { + id: "veoaifree-web", + alias: "veo-free", + name: "Veo AI Free", + icon: "videocam", + color: "#8B5CF6", + textIcon: "VF", + website: "https://veoaifree.com", + noAuth: true, + hasFree: true, + freeNote: "Free video generation — VEO 3.1, Seedance. 6 requests/hour.", + authHint: "No auth required. Rate limited to 6 requests/hour per IP.", + }, }; export const FREE_APIKEY_PROVIDER_IDS = new Set(["qoder"]); @@ -365,18 +391,6 @@ export const WEB_COOKIE_PROVIDERS = { subscriptionRisk: true, riskNoticeVariant: "webCookie", }, - "veoaifree-web": { - id: "veoaifree-web", - alias: "veo-free", - name: "Veo AI Free", - icon: "videocam", - color: "#8B5CF6", - textIcon: "VF", - website: "https://veoaifree.com", - hasFree: true, - freeNote: "Free video generation — VEO 3.1, Seedance. 6 requests/hour.", - authHint: "No auth required. Rate limited to 6 requests/hour per IP.", - }, "t3-web": { id: "t3-web", alias: "t3chat", @@ -418,19 +432,6 @@ export const WEB_COOKIE_PROVIDERS = { authHint: "Paste your __client cookie value from .clerk.agent.adapta.one (DevTools → Application → Cookies)", }, - "duckduckgo-web": { - id: "duckduckgo-web", - alias: "ddgw", - name: "DuckDuckGo AI Chat", - icon: "auto_awesome", - color: "#DE5833", - textIcon: "DDG", - website: "https://duckduckgo.com/duckchat", - hasFree: true, - noAuth: true, - freeNote: "Free — anonymous access to multiple AI models via DuckDuckGo.", - authHint: "No credentials required — DuckDuckGo AI Chat is anonymous and free.", - }, huggingchat: { id: "huggingchat", alias: "hc",