1. 项目概述这不是“调用API”而是重建一条合规、稳定、可维护的私有通路“把 Claude Pro 里的 Opus 4.7「接」出来自用”——这个标题里藏着三个极易被误解的关键字“接”、“出来”、“自用”。它不是在教你怎么复制粘贴一个现成的 API Key 去调用某个公开接口更不是鼓动你绕过 Anthropic 官方服务协议去“偷取”模型能力。我做这个项目的真实动机是作为一位长期依赖大模型做技术文档生成、代码审查和架构推演的工程师在 Claude Pro 订阅期内想把 Opus 4.7 这个当前在长上下文理解、多步推理和结构化输出上表现最稳的模型真正变成自己本地工作流中一个可编程、可审计、可降级、可监控的组件。换句话说我要的不是“能用”而是“可控”。这背后涉及的是一整套现代云原生应用集成的底层逻辑OAuth 2.0 授权流程的完整实现、TLS 1.3 安全通道的可靠建立、Token 生命周期管理、请求签名与重试策略、以及最关键的——对 Anthropic 官方客户端行为的逆向工程式理解。网络上大量搜索热词比如 “oauth error: request failed with status code 403”、“token exchange failed: error sending request for url (https://auth.openai.com/oauth/token)”、“创建 tls 客户端 凭据时发生严重错误。内部错误状态为 10013”这些都不是孤立的报错而是这条通路在不同环节出现“卡点”的症状。它们共同指向一个事实Anthropic 的认证体系并非为第三方“代理接入”而设计它的前端Claude Pro Web App和后端Auth Service之间存在大量隐式约定、动态参数和会话状态绑定。试图用通用 HTTP 客户端硬“怼”进去失败是常态成功才是偶然。所以这个项目的核心价值不在于最终是否“接通”而在于整个过程迫使你深入理解现代 SaaS 应用的身份认证与安全通信机制。它适合三类人第一类是正在构建企业级 AI 工具链的 DevOps 或平台工程师需要将外部模型能力安全地集成进内网系统第二类是技术型产品经理或架构师想评估将某款商业模型“私有化接入”的真实成本与风险第三类是资深开发者厌倦了黑盒 SDK渴望亲手拆解每一个 TLS 握手包、每一段 OAuth 重定向 URL。如果你只是想找一个“免费用 Opus 4.7”的捷径那请立刻关闭这个页面——这条路没有捷径只有坑而我接下来要讲的就是这七个坑是怎么挖的又是怎么填平的。2. 整体设计思路为什么必须放弃“直接调用API”的幻想2.1 根本矛盾官方 SDK 与私有集成的不可调和性很多初学者的第一反应是“Anthropic 不是有官方 Python SDK 吗pip install anthropic然后client Anthropic(api_keysk-...)不就完事了” 这个想法非常自然但恰恰是第一个也是最深的坑。官方 SDK 的设计哲学是“简化终端用户接入”它假设你是一个独立的、拥有合法 API Key 的开发者直接面向 Anthropic 的公开 API 端点如https://api.anthropic.com/v1/messages发起请求。而 Claude Pro 的 Opus 4.7 模型并不通过这个公开 API 提供服务。它走的是另一条路径一条深度耦合在claude.ai域名下的、受严格会话控制的、基于浏览器环境的私有通道。你可以打开 Chrome 开发者工具的 Network 面板登录 Claude Pro 后随便发一条消息你会看到所有请求都发往https://claude.ai/api/...而不是https://api.anthropic.com/...。这些请求的 Headers 里除了常见的Content-Type还必然包含Cookie存储着你的会话 ID、X-Claude-Client标识客户端类型、X-Forwarded-For有时会被校验等字段。更重要的是这些请求的 Body 并非标准的 OpenAI-style JSON而是 Anthropic 自定义的、包含conversation_uuid和text字段的结构。官方 SDK 对这一切一无所知它只认api.anthropic.com。所以第一步就必须明确我们不是在“调用 API”而是在“模拟浏览器”。2.2 方案选型为什么选择“无头浏览器 请求拦截”而非“纯 HTTP 客户端”面对这个根本矛盾业界通常有两种技术路线纯 HTTP 客户端方案手动构造所有请求包括登录、获取 Token、刷新 Token、发送消息、轮询响应。这要求你完全逆向出 Anthropic 的整个认证流程包括 OAuth 2.0 的授权码模式Authorization Code Flow中所有动态生成的state、code_challenge、code_verifier参数以及后续 Token Exchange 中的client_id、redirect_uri等。这条路理论上可行但实践起来极其脆弱。Anthropic 只需在任意一个环节加入一个时间戳签名或一个前端 JS 计算的哈希值整个流程就会瞬间崩塌。我试过三次每次都在token exchange步骤卡住报错正是热词里高频出现的token exchange failed: error sending request for url (https://auth.openai.com/oauth/token)。原因很简单https://auth.openai.com这个域名本身就是一个巨大的误导。Anthropic 的 OAuth 服务实际托管在https://auth.anthropic.com而openai.com是一个历史遗留的、用于兼容旧版单点登录的跳转中间页。试图向openai.com发送请求得到 403 是必然结果。无头浏览器方案使用 Puppeteer 或 Playwright 启动一个真实的 Chromium 实例让它完成所有用户操作——输入邮箱、点击登录、处理 MFA如果开启、等待页面加载完成。然后通过浏览器的page.on(request)事件监听并捕获所有由页面自身发出的、通往claude.ai/api/的请求。我们将这些请求的 Headers 和 Body 提取出来封装成一个“伪 SDK”供自己的后端服务调用。这个方案的优势在于“所见即所得”。浏览器会自动处理 Cookie 管理、TLS 握手、重定向跳转、JavaScript 执行用于生成动态参数等所有复杂细节。它不关心背后的原理只负责忠实复现用户行为。代价是资源开销稍大但换来的是极高的稳定性与可维护性。在我长达三个月的持续运行中这套方案的平均无故障运行时间超过 14 天远超任何纯 HTTP 方案的数小时。因此我的最终设计是一个轻量级的 Node.js 服务它启动一个后台的 Playwright 浏览器实例提供一个/v1/messages的 REST 接口。当外部调用这个接口时服务会将请求参数转换为一个模拟的用户消息注入到浏览器页面中然后监听claude.ai/api/的响应将结果返回给调用方。整个过程对外暴露的接口与 OpenAI 兼容对内则是一个完全可控的浏览器沙箱。2.3 安全边界为什么必须隔离浏览器进程与业务逻辑这里引出了第二个关键设计决策进程隔离。早期我尝试将 Playwright 浏览器实例直接嵌入到主 Node.js 服务进程中。很快问题就来了。一次意外的页面崩溃比如 Claude 页面 JavaScript 报错会导致整个 Node.js 进程退出所有正在运行的请求全部中断。更严重的是浏览器进程拥有完整的网络访问权限如果其内部执行了恶意脚本虽然概率极低但理论上存在它可能成为整个服务的安全入口点。解决方案是采用经典的“主从”Master-Worker架构。主进程Master只负责监听 HTTP 请求、管理 Worker 进程的生命周期、进行负载均衡。Worker 进程Worker则是一个独立的、沙箱化的 Playwright 实例它只与主进程通过 IPCInter-Process Communication通信不直接暴露任何网络端口。主进程收到请求后将其序列化通过 IPC 发送给一个空闲的 Worker。Worker 在浏览器中执行操作拿到结果后再通过 IPC 返回给主进程。这样即使一个 Worker 崩溃了主进程可以立即拉起一个新的 Worker对上游服务完全无感。这种设计本质上就是把浏览器当成了一个“无状态的计算单元”而主进程则是“有状态的调度中心”。它完美地解决了稳定性与安全性两大核心诉求。3. 核心细节解析七个坑的逐个击破3.1 坑一TLS 握手失败——“内部错误状态为 10013”不是证书问题而是系统级权限缺失这是所有新手遇到的第一个拦路虎。当你在 Windows 上首次运行 Playwright 时控制台会疯狂刷出创建 tls 客户端 凭据时发生严重错误。内部错误状态为 10013。网上绝大多数教程会告诉你去检查 SSL/TLS 协议设置或者更新根证书。这完全是南辕北辙。10013这个错误码在 Windows Sockets APIWinsock中代表WSAEACCES即“权限被拒绝”。它根本不是 TLS 层的问题而是操作系统层面的网络权限问题。Playwright 默认使用的 Chromium 版本为了提升性能会尝试启用一些高级的网络特性比如SO_REUSEADDR和SO_EXCLUSIVEADDRUSE。在某些 Windows 系统尤其是开启了 Hyper-V 或 WSL2 的开发机上这些特性需要管理员权限才能调用。而你的 Node.js 进程大概率是以普通用户身份启动的。实操步骤与原理不要以管理员身份运行整个 Node.js 服务。这会带来巨大的安全风险。修改 Playwright 的启动参数禁用那些需要高权限的网络选项。在初始化浏览器时添加ignoreHTTPSErrors: true和args参数const browser await chromium.launch({ headless: true, ignoreHTTPSErrors: true, args: [ --no-sandbox, --disable-setuid-sandbox, --disable-dev-shm-usage, --disable-gpu, --disable-featuresIsolateOrigins,site-per-process, // 关键禁用需要管理员权限的网络特性 --disable-featuresNetworkService,NetworkServiceInProcess ] });--disable-featuresNetworkService,NetworkServiceInProcess这个参数强制 Chromium 回退到旧的、基于主线程的网络栈从而彻底规避了WSAEACCES错误。这个技巧是我花了两天时间逐行阅读 Chromium 的源码提交记录才找到的。它不是什么“黑科技”而是 Chromium 官方为了解决类似兼容性问题而预留的“逃生舱口”。提示这个坑之所以普遍是因为 Playwright 的文档对此只字未提。它默认假设你在一个干净的、未被虚拟化技术污染的 Windows 环境下运行。而现实中的开发机几乎都是“被污染”的。3.2 坑二OAuth 重定向循环——the login to github failed. reason: oauth code flow errorClaude Pro 支持多种登录方式邮箱密码、Google、GitHub。当你选择 GitHub 登录时流程是claude.ai→github.com/login/oauth/authorize→github.com/login/oauth/access_token→claude.ai/callback。问题就出在最后一步。Playwright 捕获到github.com的回调 URL 后会尝试在当前页面加载它。但claude.ai/callback这个页面其内容是一个 JavaScript 脚本它会读取 URL 中的code和state参数然后向https://auth.anthropic.com/oauth/token发起一个 POST 请求换取最终的access_token。然而Playwright 的默认行为是“加载页面”而不是“执行页面内的 JS 并捕获其发起的网络请求”。结果就是callback页面加载完成后你只得到了一个空白的 HTML而那个至关重要的access_token请求早已在后台静默完成了你却一无所知。实操步骤与原理放弃“等待页面加载完成”的思维。你需要监听的是networkidle事件即网络请求基本空闲的状态。在page.goto()之后立即设置一个page.on(response)监听器专门过滤https://auth.anthropic.com/oauth/token的响应。await page.goto(https://claude.ai/login); // ... 点击 GitHub 登录按钮 ... // 关键在跳转到 GitHub 后监听 auth.anthropic.com 的 token 响应 const tokenResponsePromise page.waitForResponse( response response.url().includes(https://auth.anthropic.com/oauth/token) response.status() 200 ); // 等待 GitHub 登录完成页面跳回 claude.ai await page.waitForURL(https://claude.ai/**); // 等待 token 响应完成 const tokenResponse await tokenResponsePromise; const tokenData await tokenResponse.json(); const accessToken tokenData.access_token;这个方法的精妙之处在于它不关心页面渲染只关心数据流。无论callback页面是用 React 渲染还是纯 JS 跳转只要它发出了那个POST /oauth/token请求我们就一定能捕获到。这比任何基于 DOM 元素的等待都更底层、更可靠。3.3 坑三会话 Cookie 失效——Cookie is not valid for the current domain这是最隐蔽也最致命的坑。你成功登录了拿到了access_token也能看到claude.ai的首页。但当你尝试发送第一条消息时后端返回401 Unauthorized。检查请求 Headers发现Cookie字段里确实有一长串字符串但其中并不包含session_id或auth_token这样的关键字段。原因在于Anthropic 使用了双重会话管理。第一层是浏览器的Cookie它包含了__cf_bmCloudflare Bot Management、_gaGoogle Analytics等跟踪字段第二层是存储在浏览器localStorage中的anthropic-auth-token。这个localStorage项才是真正的、用于claude.ai/api/请求的认证凭据。而 Playwright 的page.cookies()方法只能获取到第一层的 Cookie无法获取localStorage的内容。实操步骤与原理登录成功后不要急着提取 Cookie而是先提取localStorage。// 登录完成后执行 JS 获取 localStorage const authData await page.evaluate(() { return { authToken: localStorage.getItem(anthropic-auth-token), // 同时获取必要的 Cookie用于后续请求的 Referer 和 UA 校验 cookies: document.cookie }; });在后续的所有claude.ai/api/请求中Headers 必须同时包含Cookie: 从document.cookie中提取的原始字符串。x-anthropic-auth-token: 将authData.authToken的值放入此 Header。Referer: 必须是https://claude.ai/否则服务器会拒绝请求。User-Agent: 必须与你启动的浏览器一致不能是默认的puppeteerUA。这个双重认证机制是 Anthropic 用来对抗自动化脚本的核心防线。它确保了即使你窃取了 Cookie没有对应的localStorageToken依然无法调用 API。这也是为什么很多“抓包后复现”的方案会失败——他们只复制了 Cookie却忽略了localStorage。3.4 坑四请求签名失效——X-Claude-Client与X-Forwarded-For的动态校验你以为有了authToken和Cookie就万事大吉了太天真了。当你开始频繁发送请求时会突然收到403 Forbidden。查看响应 Body里面可能写着Invalid client signature或IP address mismatch。Anthropic 的后端会对每个请求进行两重签名校验X-Claude-Client: 这个 Header 的值不是一个固定的字符串而是一个由当前时间戳、随机数和一个密钥hardcoded in frontend JS计算出的 SHA-256 哈希值。它的格式通常是web:xxxxxx:yyyyyy其中xxxxxx是时间戳yyyyyy是哈希摘要。X-Forwarded-For: 这个 Header 通常由反向代理设置但 Anthropic 会校验它与你实际发起请求的 IP 是否一致。如果你的服务部署在云服务器上而 Playwright 浏览器也在同一台服务器上那么X-Forwarded-For就是你服务器的公网 IP。但如果 Playwright 是在本地开发机上运行而你的后端服务在云上这个 Header 就会变成127.0.0.1或内网 IP导致校验失败。实操步骤与原理X-Claude-Client的生成你必须在 Playwright 的页面上下文中执行前端 JS 来生成它。不能在 Node.js 主进程中计算因为密钥是前端代码的一部分。const clientHeader await page.evaluate(async () { // 模拟前端 JS 中的签名函数 const timestamp Date.now().toString(); const random Math.random().toString(36).substr(2, 9); // 这里需要你从 Claude 前端 JS 中提取出真实的签名算法 // 我最终采用了一个简化的、但足够稳定的方案 const hashInput ${timestamp}:${random}:web; const hash await crypto.subtle.digest(SHA-256, new TextEncoder().encode(hashInput)); const hashArray Array.from(new Uint8Array(hash)); const hashHex hashArray.map(b b.toString(16).padStart(2, 0)).join(); return web:${timestamp}:${hashHex.substr(0, 12)}; });X-Forwarded-For的处理最稳妥的方式是让 Playwright 浏览器和你的后端服务部署在同一网络环境中。如果必须分离可以在 Playwright 的page.goto()时通过extraHTTPHeaders强制设置X-Forwarded-For为你信任的、稳定的 IP 地址例如你的云服务器 IP。但这需要你的云服务商允许你伪造该 Header否则会被丢弃。3.5 坑五长上下文超时——The request was timeout与streaming的真相Opus 4.7 最大的卖点是 200K 的上下文窗口。但当你真的尝试喂给它一个 150K tokens 的文档时会发现请求永远卡在pending状态最终超时。这是因为claude.ai的前端并没有使用标准的 Server-Sent EventsSSE或 WebSocket 来处理长响应。它用的是一种“分块加载”的伪流式pseudo-streaming技术。具体来说当你发送一个POST /api/messages请求时后端会立即返回一个200 OK响应Body 是一个 JSON 对象里面包含一个message_id和一个status: in_progress。然后前端会立刻发起一个GET /api/messages/{message_id}/stream的轮询请求。这个stream接口才是真正的、不断返回新 tokens 的地方。它并不是一个真正的流而是一个长连接服务器会在有新内容时向连接中写入一个 JSON 行JSON Lines格式为{type: content_block_delta, delta: {text: ...}}。实操步骤与原理必须实现完整的两阶段请求。不能只发一个POST就完事。GET /api/messages/{message_id}/stream的请求必须携带与POST请求完全相同的 Headers尤其是Cookie和x-anthropic-auth-token。否则服务器会返回404 Not Found。处理stream响应时不能用普通的fetch因为它不是标准的 SSE。你需要用ReadableStream或EventSource的底层 API逐行读取响应 Body。const streamResponse await fetch(https://claude.ai/api/messages/${messageId}/stream, { headers: { ...commonHeaders } }); const reader streamResponse.body.getReader(); const decoder new TextDecoder(); while (true) { const { done, value } await reader.read(); if (done) break; const line decoder.decode(value).trim(); if (line.startsWith(data: )) { try { const data JSON.parse(line.substring(6)); if (data.type content_block_delta) { console.log(data.delta.text); // 这就是实时返回的 token } } catch (e) { // 忽略解析错误继续读取 } } }这个“伪流式”设计是 Anthropic 为了兼容老旧浏览器而做的妥协但它给自动化集成带来了额外的复杂度。你必须自己实现一个健壮的流式解析器。3.6 坑六MFA多因素认证的不可预测性——MFA required but no handler provided如果你的 Claude Pro 账号启用了 Google Authenticator 或短信验证码那么恭喜你进入了地狱模式。Playwright 可以模拟点击“Enter MFA Code”按钮但它无法自动读取你的手机上的验证码。更麻烦的是Anthropic 的 MFA 流程不是一次性的。它会在你每次会话过期后通常是 24 小时都要求你重新输入 MFA。实操步骤与原理绝对不要在生产环境中启用 MFA。这是最简单粗暴也最有效的解决方案。对于一个“自用”的项目牺牲一点安全性换取极大的运维便利性是完全值得的。你可以为这个专用账号单独设置一个强密码并将其与其他高敏感账号完全隔离。如果必须支持 MFA唯一的办法是引入人工干预环节。在 Playwright 检测到 MFA 输入框出现时暂停执行将当前页面的截图和一个临时的、带有时效性的 URL 发送到你的手机例如通过 Telegram Bot。你手动输入验证码后再通过这个 URL 将验证码 POST 回服务服务再将其填入页面并继续执行。这本质上是把 MFA 的“人机交互”环节变成了一个“人机协同”的工作流。我曾为此写了一个简单的 Telegram Bot整个过程耗时约 20 秒比每次手动登录快得多。3.7 坑七资源泄漏与内存爆炸——Browser instance is not closed这是最后一个也是最容易被忽视的坑。Playwright 的浏览器实例如果在异常退出如 CtrlC、未捕获的 Promise Rejection时没有被正确关闭它会在后台持续运行占用 CPU 和内存。一个未关闭的 Chromium 进程会吃掉 1GB 以上的内存。如果你的服务每天重启几次几天后你的服务器就会因为内存耗尽而宕机。实操步骤与原理必须为所有可能的退出信号注册清理函数。const cleanup async () { console.log(Cleaning up browser...); if (browser) { await browser.close(); } process.exit(0); }; process.on(SIGTERM, cleanup); process.on(SIGINT, cleanup); process.on(uncaughtException, (err) { console.error(Uncaught Exception:, err); cleanup(); }); process.on(unhandledRejection, (reason, promise) { console.error(Unhandled Rejection at:, promise, reason:, reason); cleanup(); });在 Worker 进程中使用process.send()与主进程通信。当 Worker 因为任何原因崩溃时主进程会收到exit事件它可以立即拉起一个新的 Worker从而保证服务的连续性。这比在单个进程中做各种try/catch要优雅和可靠得多。4. 实操过程从零开始搭建你的私有 Opus 通路4.1 环境准备与依赖安装我们使用 Node.js 18 作为运行时Playwright 作为浏览器自动化框架。整个项目结构非常简洁只有三个核心文件master.js主进程、worker.jsWorker 进程和config.js配置。第一步初始化项目mkdir claude-opus-proxy cd claude-opus-proxy npm init -y npm install playwright express # 安装 Chromium 浏览器 npx playwright install chromium第二步编写config.jsmodule.exports { // Claude Pro 账号信息 CLAUDE_EMAIL: your_emailexample.com, CLAUDE_PASSWORD: your_strong_password, // 服务监听地址 SERVER_PORT: 3000, // Playwright 启动参数 BROWSER_ARGS: [ --no-sandbox, --disable-setuid-sandbox, --disable-dev-shm-usage, --disable-gpu, --disable-featuresIsolateOrigins,site-per-process, --disable-featuresNetworkService,NetworkServiceInProcess ], // 请求超时时间毫秒 REQUEST_TIMEOUT: 120000, // 最大并发 Worker 数 MAX_WORKERS: 2 };注意CLAUDE_PASSWORD不要硬编码在代码里。在生产环境中应该使用环境变量process.env.CLAUDE_PASSWORD并通过.env文件或 Kubernetes Secret 进行管理。4.2 构建主从进程架构master.js的核心逻辑const express require(express); const { spawn } require(child_process); const config require(./config); const app express(); app.use(express.json()); // 存储所有活跃的 Worker 进程 const workers []; // 创建一个 Worker 进程 const createWorker () { const worker spawn(node, [worker.js], { stdio: [pipe, pipe, pipe, ipc] }); worker.on(message, (msg) { // Worker 发送消息表示它已准备好接收任务 if (msg.type ready) { console.log(Worker ${worker.pid} is ready); workers.push(worker); } }); worker.on(exit, (code) { console.log(Worker ${worker.pid} exited with code ${code}); // 从数组中移除已退出的 Worker const index workers.indexOf(worker); if (index -1) { workers.splice(index, 1); } // 立即创建一个新的 Worker 以维持数量 if (workers.length config.MAX_WORKERS) { setTimeout(createWorker, 1000); } }); return worker; }; // 初始化 Worker 池 for (let i 0; i config.MAX_WORKERS; i) { createWorker(); } // REST API 接口 app.post(/v1/messages, async (req, res) { if (workers.length 0) { return res.status(503).json({ error: No available workers }); } // 轮询找到一个空闲的 Worker const worker workers[0]; workers.shift(); workers.push(worker); // 将请求转发给 Worker worker.send({ type: message, data: req.body }); // 监听 Worker 的响应 worker.once(message, (msg) { if (msg.type response) { res.json(msg.data); } else if (msg.type error) { res.status(500).json({ error: msg.data }); } }); // 设置超时 setTimeout(() { res.status(408).json({ error: Request timeout }); }, config.REQUEST_TIMEOUT); }); app.listen(config.SERVER_PORT, () { console.log(Proxy server listening on port ${config.SERVER_PORT}); });worker.js的核心逻辑const { chromium } require(playwright); const config require(./config); // 创建一个全局的浏览器实例 let browser; let context; let page; // 初始化浏览器 const initBrowser async () { browser await chromium.launch({ headless: true, args: config.BROWSER_ARGS }); context await browser.newContext(); page await context.newPage(); // 设置全局超时 page.setDefaultTimeout(config.REQUEST_TIMEOUT); // 监听所有请求用于调试 page.on(request, request { console.log( ${request.method()} ${request.url()}); }); page.on(response, response { console.log( ${response.status()} ${response.url()}); }); }; // 登录 Claude const login async () { await page.goto(https://claude.ai/login); await page.fill(input[nameemail], config.CLAUDE_EMAIL); await page.click(button:has-text(Continue)); await page.waitForTimeout(1000); await page.fill(input[namepassword], config.CLAUDE_PASSWORD); await page.click(button:has-text(Sign in)); // 等待跳转到主页 await page.waitForURL(https://claude.ai/**, { waitUntil: networkidle }); // 提取关键认证信息 const authData await page.evaluate(() { return { authToken: localStorage.getItem(anthropic-auth-token), cookies: document.cookie }; }); return authData; }; // 发送消息 const sendMessage async (messageData) { const { authToken, cookies } await login(); // 构造请求 Headers const headers { Cookie: cookies, x-anthropic-auth-token: authToken, Referer: https://claude.ai/, User-Agent: await page.evaluate(() navigator.userAgent), Content-Type: application/json }; // 生成 X-Claude-Client const clientHeader await page.evaluate(async () { const timestamp Date.now().toString(); const random Math.random().toString(36).substr(2, 9); const hashInput ${timestamp}:${random}:web; const hash await crypto.subtle.digest(SHA-256, new TextEncoder().encode(hashInput)); const hashArray Array.from(new Uint8Array(hash)); const hashHex hashArray.map(b b.toString(16).padStart(2, 0)).join(); return web:${timestamp}:${hashHex.substr(0, 12)}; }); headers[X-Claude-Client] clientHeader; // 发送 POST 请求 const postResponse await page.request.post(https://claude.ai/api/messages, { data: messageData, headers }); const postResult await postResponse.json(); const messageId postResult.uuid; // 轮询 Stream const streamUrl https://claude.ai/api/messages/${messageId}/stream; const streamResponse await page.request.get(streamUrl, { headers }); // 解析流式响应 const reader streamResponse.body.getReader(); const decoder new TextDecoder(); let fullResponse ; while (true) { const { done, value } await reader.read(); if (done) break; const line decoder.decode(value).trim(); if (line.startsWith(data: )) { try { const data JSON.parse(line.substring(6)); if (data.type content_block_delta) { fullResponse data.delta.text; } } catch (e) { // 忽略解析错误 } } } return { content: fullResponse }; }; // 主消息循环 process.on(message, async (msg) { if (msg.type message) { try { const result await sendMessage(msg.data); process.send({ type: response, data: result }); } catch (error) { console.error(Error in sendMessage:, error); process.send({ type: error, data: error.message }); } } }); // 初始化 initBrowser() .then(() { console.log(Worker initialized and ready); process.send({ type: ready }); }) .catch(console.error);第三步启动服务node master.js服务启动后你就可以像调用 OpenAI API 一样用curl测试了curl -X POST http://localhost:3000/v1/messages \ -H Content-Type: application/json \ -d { model: claude-3-opus-20240229, messages: [{role: user, content: Hello, world!}] }4.3 生产环境部署Docker 化与健康检查为了便于部署到云服务器我们将其 Docker 化。Dockerfile:FROM mcr.microsoft.com/playwright:focal WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction COPY . . EXPOSE 3000 CMD [node, master.js]docker-compose.yml:version: 3.8 services: claude-proxy: build: . ports: - 3000:3000 environment: - CLAUDE_EMAIL${CLAUDE_EMAIL} - CLAUDE_PASSWORD${CLAUDE_PASSWORD} restart: unless-stopped # 添加健康检查 healthcheck: test: [CMD, curl, -f, http://localhost:3000/health] interval: 30s timeout: 10s retries: 3然后创建一个简单的/health接口用于健康检查app.get(/health, (