jsdvjx/claude-remote-protocol

GitHub: jsdvjx/claude-remote-protocol

一个逆向工程 Claude.ai 网页端 WebSocket 协议的 TypeScript 客户端,允许通过浏览器凭证程序化操控 Claude 对话。

Stars: 0 | Forks: 0

# claude-remote-protocol 针对 **Claude Code Remote (CCR)** WebSocket 协议的逆向工程 TypeScript 客户端。通过 Chrome DevTools Protocol 分析 `claude.ai` 前端流量构建。 ## 功能特性 - 完整的 WebSocket 会话管理,支持自动重连 - 用于会话、事件和环境的 HTTP REST API 客户端 - 流式助手消息(thinking、text、tool_use) - 工具权限处理(允许/拒绝及输入修改) - AskUserQuestion 流程(单选 & 多选) - Plan 模式支持(EnterPlanMode / ExitPlanMode) - MCP 服务器状态和消息转发 - Keep-alive 和空闲超时管理 - 针对整个协议的完整 TypeScript 类型 ## 安装 ``` npm install claude-remote-protocol ``` 需要 Node.js >= 18(使用原生 `fetch` 和 `WebSocket`)。 ## 快速开始 ``` import { ClaudeClient } from "claude-remote-protocol"; // 1. Create client (pass credentials once) const client = new ClaudeClient({ organizationUuid: "your-org-uuid", sessionKey: "sk-ant-sid02-...", cfClearance: "...", userAgent: "Mozilla/5.0 ...", }); // 2. Connect to an existing session const session = await client.connect("session_01...", { onAssistantMessage(msg) { for (const block of msg.message.content) { if (block.type === "text") process.stdout.write(block.text ?? ""); } }, onResult(msg) { console.log(`Done: ${msg.num_turns} turns, $${msg.total_cost_usd}`); }, }); await session.sendMessage("Hello, Claude!"); // 3. Or create a new session (auto-selects active environment) const newSession = await client.create({ model: "claude-sonnet-4-6", title: "My Task", onAssistantMessage(msg) { /* ... */ }, }); await newSession.sendMessage("Start working on the task."); // 4. Multiple sessions in parallel const [s1, s2] = await Promise.all([ client.connect("session_A", { onResult(m) { console.log("A done"); } }), client.connect("session_B", { onResult(m) { console.log("B done"); } }), ]); // 5. Cleanup client.disconnectAll(); ``` ## 架构 该协议采用**双通道**设计: | 通道 | 方向 | 用途 | |---------|-----------|---------| | **WebSocket** (`/v1/sessions/ws/{id}/subscribe`) | 服务器 → 客户端 | 流式响应、控制请求 | | **HTTP POST** (`/v1/sessions/{id}/events`) | 客户端 → 服务器 | 用户消息、工具权限、中断 | WebSocket 上的消息是**换行符分隔的 JSON**。 ### 消息流 ``` Client Server │ │ │──── HTTP POST (user msg) ─────▶│ │ │ │◀──── WS: assistant (stream) ──│ (thinking → text → tool_use) │◀──── WS: assistant (stream) ──│ (same msg_id, incremental) │◀──── WS: control_request ─────│ (can_use_tool permission) │ │ │──── WS: control_response ─────▶│ (allow/deny) │ │ │◀──── WS: assistant (stream) ──│ (tool_result → more text) │◀──── WS: result ──────────────│ (execution summary) ``` ## API 参考 ### `ClaudeClient` 顶层入口点。只需传递一次凭证,即可管理多个会话。 ``` const client = new ClaudeClient({ organizationUuid: string, sessionKey: string, // sk-ant-sid02-... cfClearance: string, // Cloudflare cf_clearance cookie userAgent: string, // must match browser that generated cfClearance cookie?: string, // full cookie override (sessionKey/cfClearance ignored) baseUrl?: string, // default: "https://claude.ai" }); // Connect to existing session (returns connected SessionManager) const session = await client.connect(sessionId, { replay?: boolean, maxRetries?: number, idleTimeout?: number, onAssistantMessage?, onResult?, onToolPermission?, onError?, }); // Create new session + connect (auto-selects environment if omitted) const session = await client.create({ environmentId?: string, model?: string, // default: "claude-sonnet-4-6" title?: string, sessionContext?: Partial, // ...same callbacks as connect }); await client.listSessions(); await client.listEnvironments(); client.getConnected(sessionId); // get a previously connected session client.disconnect(sessionId); // disconnect one client.disconnectAll(); // disconnect all ``` ### `ClaudeApi` 用于会话管理的 HTTP REST 客户端(也可通过 `client.api` 访问)。 ``` const api = new ClaudeApi({ organizationUuid: string, sessionKey: string, cfClearance: string, userAgent: string, cookie?: string, // full cookie override baseUrl?: string, }); await api.listSessions(); await api.getSession(sessionId); await api.createSession({ environment_id, session_context }); await api.updateSession(sessionId, { title?, session_status? }); await api.listEvents(sessionId, afterId?); await api.sendMessage({ sessionId, uuid, message }); await api.interrupt(sessionId); await api.respondToolPermission({ sessionId, requestId, toolName, decision }); await api.setPermissionMode(sessionId, mode); await api.listEnvironments(); ``` ### `SessionManager` 高级 WebSocket 会话管理器。 ``` const session = new SessionManager({ // Required organizationUuid: string, sessionKey: string, cfClearance: string, userAgent: string, sessionId: string, // Optional replay?: boolean, // Replay missed messages on connect maxRetries?: number, // Reconnect attempts (default: 5) idleTimeout?: number, // Idle disconnect in ms (default: 300000) baseUrl?: string, wsHost?: string, // Custom WebSocket host // Callbacks onAssistantMessage?: (msg: WsAssistantMessage) => void, onResult?: (msg: WsResultMessage) => void, onToolPermission?: ToolPermissionHandler, onHookCallback?: HookCallbackHandler, onStateChange?: (state: ConnectionState) => void, onError?: (error: Error) => void, }); await session.connect(); await session.sendMessage("Hello!"); await session.interrupt(); await session.setModel("claude-sonnet-4-6"); await session.setPermissionMode("acceptEdits"); await session.setMaxThinkingTokens(10000); session.disconnect(); ``` ### 流式模式 助手消息以相同的 `msg_id` 增量到达。内容块随时间累积: 1. 第一条消息:`[thinking]` 2. 下一条:`[thinking, text]` 3. 下一条:`[thinking, text, tool_use]` 4. 最终:`stop_reason` 从 `null` 变为 `"end_turn"` 或 `"tool_use"` ### 工具权限流程 当 Claude 想要使用工具时,服务器会发送 `can_use_tool` 控制请求。您的处理程序决定允许还是拒绝: ``` onToolPermission: async (toolName, input, { toolUseID, signal, suggestions }) => { if (toolName === "Bash") { return { behavior: "deny", message: "Bash not allowed" }; } return { behavior: "allow", updatedInput: input }; }, ``` ### 内容块类型 | 类型 | 字段 | 描述 | |------|--------|-------------| | `text` | `text` | 纯文本输出 | | `thinking` | `thinking`, `signature` | 扩展思维(加密签名) | | `tool_use` | `id`, `name`, `input` | 工具调用 | | `tool_result` | `tool_use_id`, `content`, `is_error` | 工具执行结果 | | `tool_reference` | `tool_name` | 工具引用 | | `image` | | 图片内容 | | `resource` | | 资源引用 | ## 协议文档 有关包含所有消息类型和流程的完整协议规范,请参阅 [PROTOCOL.md](./PROTOCOL.md)。 ## 认证 所有凭证都可以从 **任意 `claude.ai` 页面**提取(主页、设置、任意对话 —— 无需打开特定会话)。 ### 提取步骤 **1. 打开 DevTools** — 在任意 `claude.ai` 页面按 `F12` **2. 从 Cookies 获取 `sessionKey` 和 `cfClearance`:** - 进入 **Application** 标签页 → **Cookies** → `https://claude.ai` - 找到 `sessionKey` — 值以 `sk-ant-sid02-...` 开头 - 找到 `cf_clearance` — 一个长的字母数字字符串 **3. 获取 `organizationUuid`:** - 选项 A:在 **Application** → **Cookies** 中,找到 `lastActiveOrg` — 那就是您的组织 UUID - 选项 B:在 **Network** 标签页中,点击任意发往 `claude.ai/v1/...` 的请求,检查 `x-organization-uuid` 请求头 **4. 获取 `userAgent`:** - 进入 **Console** 标签页,输入 `navigator.userAgent` 并按回车 - 复制完整字符串(例如 `Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...`) - **重要:** 这必须是生成了 `cf_clearance` cookie 的浏览器的确切 User-Agent。使用不同的 User-Agent 将导致 Cloudflare 403 错误。 ### 注意事项 - `cf_clearance` 会定期过期(通常在 Cloudflare 重新质询时)。如果遇到 403 错误,请重新提取。 - `sessionKey` 是长期有效的,但可能因登出而失效。 - 所有这四个 cookie 都设置在 `claude.ai` 域名级别,因此在每个页面上都是相同的。 ### 用法 ``` import { ClaudeClient } from "claude-remote-protocol"; const client = new ClaudeClient({ organizationUuid: "ed81b697-...", sessionKey: "sk-ant-sid02-...", cfClearance: "DrW9nrPr...", userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...", }); ``` ### 编程式提取 (CDP) 如果您有一个启用了远程调试的 Chrome 实例(`--remote-debugging-port=9222`),您可以通过编程方式提取凭证: ``` // Get cookies via Chrome DevTools Protocol const resp = await fetch("http://localhost:9222/json"); const [tab] = await resp.json(); // Connect to tab via WebSocket, then: // - Network.getCookies({ urls: ["https://claude.ai"] }) → sessionKey, cf_clearance, lastActiveOrg // - Browser.getVersion() → userAgent ``` ## 许可证 MIT
标签:AI 客户端, Anthropic, Chrome DevTools, CIS基准, Claude API, GNU通用公共许可证, IP 地址批量处理, LLM 工具, MITM代理, Node.js, NPM 包, TypeScript, WebSocket 协议, 云资产清单, 协议分析, 安全插件, 开源库, 搜索引擎爬虫, 机器人开发, 权限提升, 流式消息, 浏览器自动化, 网络通信, 自动化交互, 自动化攻击, 足迹分析, 远程控制, 逆向工程