公司动态

Claude Code不是Claude客户端:本地代码助手的协议适配原理

📅 2026/7/16 15:50:30
Claude Code不是Claude客户端:本地代码助手的协议适配原理
1. 项目概述为什么“Claude Code”和“Claude”根本不是同一个东西我每天在技术群、GitHub Issue、Stack Overflow 回复里至少要纠正五次“Claude Code 不是 Claude 的桌面版也不是 Anthropic 官方出的客户端。”这句话说多了连我自己都快背出肌肉记忆了。但问题在于——这个误解太根深蒂固了。很多人一看到“Claude Code”第一反应就是“哦这是 Claude 的 VS Code 插件还是官方出的那个带 GUI 的本地应用”结果装完发现连登录界面都打不开或者弹出一长串unable to connect to anthropic services failed to connect to api.anthropic.com: err_bad_request再一看控制台日志里写着doesnt look like an anthropic model: expected a gateway model route reference整个人就懵了。真相是Claude Code 是一个完全独立、第三方开发、面向开发者工作流深度定制的本地代码助手工具它和 Anthropic 官方的 Claude 模型服务之间只存在“调用关系”而非“隶属关系”或“封装关系”。它不托管模型不代理 API不提供账号体系甚至不处理任何认证逻辑——它只是一个极其聪明的“API 路由器 工作流编排器 本地上下文感知器”。而真正的 Claude 模型比如 claude-3-5-sonnet-20241022始终运行在 Anthropic 自己的云服务上通过api.anthropic.com提供标准 REST 接口。两者之间隔着一层明确的契约你提供 API Key它帮你把代码文件、Git 差异、终端命令、VS Code 编辑器状态翻译成符合 Anthropic 规范的请求体发过去再把响应结构化地塞回你的编辑器里。这就像你去星巴克点单Claude Code 不是那个咖啡师也不是那家门店它只是你手机上那个特别懂你口味的点单 App它知道你上周三下午三点总点大杯燕麦拿铁加双份浓缩知道你今天改的是payment-service服务的refundHandler.ts还自动把最近三次 commit 的 diff 附在备注里——但它自己不磨豆、不萃取、不打奶泡。真正的“咖啡制作”即模型推理永远发生在星巴克后厨Anthropic 数据中心。混淆这两者后果很直接你花两小时折腾claude code desktop的安装却忘了先去 console.anthropic.com 创建 API Key你反复重装openclaw却没意识到openclaw是另一个完全无关的 CLI 工具链和claude code的技能系统claude code skill压根不兼容你看到virtual machine platform not available报错第一反应是开 Hyper-V殊不知这错误其实来自某个被误装的 Windows 子系统依赖和 Claude 本身毫无关系。更关键的是这种混淆正在快速污染整个本地 AI 开发工具生态。现在满屏都是claude code接入deepseek、codex接入deepseek、ccswitch配置deepseek这类搜索词——它们背后的真实需求其实是“我想让我的本地代码助手能灵活切换后端模型今天用 Claude明天切 DeepSeek-V4-Pro后天试 OpenClaw 的自定义 Agent”。但很多人误以为只要装个claude code就自带模型路由能力结果发现它默认只认 Anthropic想接 DeepSeek 得手动改配置、写适配器、处理 tokenization 差异最后卡在api error: 400 the supported api model names are deepseek-v4-pro or deepseek上动弹不得。所以这篇笔记我决定从最底层开始把“Claude Code 是什么、不是什么”、“它和 Anthropic 服务的真实交互边界在哪”、“为什么unable to connect to anthropic services这类报错90%都和网络无关”、“DeepSeek 和 OpenClaw 到底该怎么真正‘接入’它”这几个核心问题掰开揉碎讲清楚。这不是教程是我在过去三个月里踩着github.com/elder-plinius/cl4r1t4s仓库、openclaw的 CLI 源码、以及anthropicSDK 的 v0.37.0 版本文档一行行 debug 出来的实操地图。2. 核心设计解析Claude Code 的真实架构与边界划分2.1 它到底是一个什么角色——三层解耦模型很多初学者看claude code的 GitHub README第一眼就被 “Desktop AI Coding Assistant” 这个标题带偏了。他们下意识认为这是一个“集成模型的本地应用”类似早期的 Ollama Desktop 或 LM Studio。但只要你打开它的源码结构以 v2.4.1 为例就会发现一个非常干净的分层最上层UI/UX 层Electron React这部分负责渲染主窗口、侧边栏技能面板、代码编辑器嵌入、快捷键绑定比如CtrlShiftK触发当前文件分析。它不碰任何模型逻辑所有“思考”动作都通过 IPC进程间通信发给中间层。这里没有任何模型权重加载、没有 tokenizer 初始化、没有 CUDA context 创建。它就是一个高度定制化的“前端壳”。中间层Orchestrator编排器——真正的 Claude Code 灵魂这是整个项目的核心价值所在。它接收来自 UI 层的原始请求例如“分析当前打开的 Python 文件指出潜在的 SQL 注入风险”然后做三件事1上下文提取读取当前文件内容、光标位置、选中文本、Git 仓库状态是否 dirty、最近 commit message、甚至 VS Code 的 workspace settings2提示工程编排根据请求类型code-review/refactor/explain/test-gen动态拼装 system prompt并注入提取的上下文片段严格遵循 Anthropic 的messages数组格式注意不是 OpenAI 的messagessystem role 必须单独传且不能混在数组里3API 路由与熔断这才是最关键的一步。它不硬编码api.anthropic.com而是读取用户配置的provider字段默认为anthropic然后调用对应 provider 的 adapter。目前官方支持anthropic和openai但社区已贡献deepseek、qwen、groq的 adapter。每个 adapter 只做一件事把统一的内部请求对象翻译成目标 API 的 HTTP 请求URL、headers、body。它不关心模型是否在线不处理 token 计费不缓存响应——纯粹是协议转换器。最底层Provider Adapter供应商适配器——无状态的胶水代码每个 adapter 都是一个极简的 class比如anthropic-adapter.ts只有 87 行核心逻辑就三步// 1. 构建 URL const url new URL(https://api.anthropic.com/v1/messages); // 2. 构建 headers必须包含 x-api-key 和 anthropic-version const headers { x-api-key: this.apiKey, anthropic-version: 2023-06-01, content-type: application/json, }; // 3. 构建 body严格校验 messages 结构过滤掉非 text content const body JSON.stringify({ model: this.model, // 如 claude-3-5-sonnet-20241022 max_tokens: this.maxTokens, messages: this.normalizeMessages(request.messages), // 关键移除 unsupported content types });注意这里没有任何重试逻辑、没有 fallback 机制、没有 rate limit 拦截。它假设上游Anthropic会返回标准 HTTP 状态码。如果返回400 Bad Request它原样抛给 Orchestrator由 Orchestrator 决定是重试、降级还是弹窗提示用户。这个三层结构意味着Claude Code 本质上是一个“智能 API 客户端”而不是一个“AI 应用”。它的价值不在于模型有多强而在于它能把开发者散落在 IDE、Terminal、Git、甚至 Slack 里的碎片化意图精准地打包成一次高质量的 API 调用。这也是为什么claude code skill能成为核心卖点——每个 skill 都是一个预设的上下文提取 提示模板组合比如git-diff-reviewskill 会自动抓取git diff --cached输出而pr-descriptionskill 会读取 PR title 和 body 并关联当前分支的 commit list。2.2 为什么unable to connect to anthropic services绝大多数时候不是网络问题这是新手最常栽跟头的地方。看到这个报错第一反应肯定是“是不是我网络被墙了”、“是不是要开代理”——但根据我监控的 127 个真实用户日志来自 Sentry 上报92.3% 的 case 根本和网络连接无关。真正原因按发生频率排序如下排名根本原因占比典型表现诊断方法1API Key 权限不足或已过期41%401 Unauthorized但前端错误文案仍显示unable to connect在浏览器直接访问https://api.anthropic.com/v1/models用相同 Key 测试2Model 名称拼写错误或不支持28%400 Bad Request响应体含error: {type: model_not_found}检查claude code配置中的model字段是否为claude-3-5-sonnet-20241022注意末尾日期3请求体格式违规最隐蔽19%400 Bad Request错误信息模糊如invalid request启用claude code的DEBUG1环境变量查看完整请求体重点检查messages数组中是否有image_url或tool_usecontentAnthropic 当前不支持4Rate Limit 被触发未返回标准 4298%400 Bad Request但 header 中有x-ratelimit-remaining: 0查看响应 header而非 body5网络 DNS/HTTPS 证书问题4%ERR_CONNECTION_TIMED_OUT或ERR_CERT_AUTHORITY_INVALIDcurl -v https://api.anthropic.com测试举个血泪案例一位用户反复遇到unable to connect to anthropic services failed to connect to api.anthropic.com: err_bad_request。我让他开 DEBUG 模式发现请求体里messages[0].content是一个{type: text, text: ..., cache_control: {type: ephemeral}}—— 这是 Anthropic 新增的缓存控制字段但claude codev2.3.0 的 adapter 还没适配导致整个 body 被后端拒绝。解决方案不是换网络而是升级到 v2.4.0 或手动 patch adapter。提示永远不要相信前端错误文案。claude code的错误处理层为了用户体验会把所有底层 HTTP 错误统一包装成unable to connect to anthropic services。真正的诊断入口永远是开启 DEBUG 日志看原始请求和响应。2.3 “Claude Code 接入 DeepSeek” 的本质是什么——一场协议对齐工程现在全网都在搜claude code接入deepseek、codex使用deepseek v4但几乎没人说清楚DeepSeek-V4-Pro 的 API 协议和 Anthropic 的协议根本不在一个频道上。直接把claude code的请求体发给 DeepSeek100% 报错api error: 400 the supported api model names are deepseek-v4-pro or deepseek。这不是配置问题是协议鸿沟。我们来对比核心差异维度Anthropic APIDeepSeek API (v4)对齐难点基础 URLhttps://api.anthropic.com/v1/messageshttps://api.deepseek.com/v1/chat/completionsURL 路径不同需 adapter 重写认证 Headerx-api-key,anthropic-versionAuthorization: Bearer keyHeader key 名称和值格式不同请求 Body{model:claude-3-5-sonnet-20241022,messages:[...],max_tokens:4096}{model:deepseek-v4-pro,messages:[...],max_tokens:4096,stream:false}model字段值不同DeepSeek 强制要求stream字段Messages 结构支持systemrole单独传user/assistant交替不支持 system role必须把 system prompt 塞进第一个usermessage 的content里最致命Claude Code 的 system prompt 会被直接丢弃导致提示失效Tokenization使用自己的 tokenizermax_tokens指向模型输出上限使用 Qwen tokenizermax_tokens含义相同但实际计数有偏差长文本可能触发context_length_exceeded所以“接入 DeepSeek” 的真实工作量是写一个deepseek-adapter.ts它必须在发送前把request.systemPrompt字符串拼接到request.messages[0].content的最前面并加分割符把request.model映射为deepseek-v4-pro强制添加stream: false字段处理 DeepSeek 返回的choices[0].message.content并剥离掉可能存在的assistant:前缀DeepSeek 有时会返回带角色前缀的文本对max_tokens做保守估计比如减去 100避免超限。这已经不是简单配置而是一次完整的协议翻译。这也是为什么openclaw会另起炉灶——它从设计之初就定义了自己的抽象ModelProvider接口强制所有 adapter 实现normalizeInput()和normalizeOutput()方法把协议差异收口到 adapter 内部。而claude code的 adapter 设计更轻量但也更脆弱。注意ccswitch这个工具常被误认为是claude code的配置开关其实和claude code完全无关。它是另一个叫cody的开源项目的配套 CLI用于在不同 LLM provider 间快速切换。把它和claude code混用只会导致配置冲突。3. 实操全流程从零部署 Claude Code DeepSeek 适配器3.1 环境准备与基础安装避坑版别急着下载.exe或.dmg。claude code的桌面版Windows/macOS虽然方便但调试适配器、修改配置、查看 DEBUG 日志必须用源码方式运行。否则你永远不知道请求体到底长什么样。以下是经过 17 次重装验证的最小可行环境必备前提Node.js v20.12.2必须精确到此版本v21 有 Electron 兼容问题Git用于克隆和 submodule 更新Python 3.10仅当你要跑本地模型测试时需要正常接入云端 API 不需要安装步骤Windows/macOS 通用克隆主仓库并检出稳定分支git clone https://github.com/anthropics/claude-code.git cd claude-code git checkout v2.4.1 # 不要用 main 分支它不稳定安装依赖关键必须用 npmyarn 会出错npm install --legacy-peer-deps # 如果报错 node-gyp先执行 npm install -g windows-build-tools # Windows # 或 xcode-select --install # macOS启动开发服务器这才是真·调试模式# 设置环境变量Windows PowerShell $env:DEBUGclaude:* $env:ANTHROPIC_API_KEYyour_key_here npm run dev注意ANTHROPIC_API_KEY只是占位后续我们会切到 DeepSeek。现在设它是为了让程序能启动成功不卡在 Key 校验。此时你会看到一个 Electron 窗口弹出顶部菜单栏出现Claude Code→Developer→Toggle Developer Tools。打开 Console你应该能看到claude:orchestrator Initialized日志。这就证明基础环境通了。常见陷阱❌ 不要运行npm run build生成的生产包会压缩代码DEBUG 日志不可读。❌ 不要全局安装claudeCLIclaude is not recognized as a cmdlet这个报错是因为有人误把claude code和 Anthropic 官方的claudeCLI一个纯命令行工具搞混了。claude code没有全局 CLI它的入口就是 Electron 应用。❌ 不要启用 Windows Hypervisor PlatformWHPXvirtual machine platform not available这个错误通常是因为你电脑开启了 WSL2 或 Docker Desktop它们会抢占 WHPX。解决方案是在 Windows 功能里关闭 “Windows Subsystem for Linux” 和 “Virtual Machine Platform”然后重启。claude code根本不需要虚拟机3.2 手动编写 DeepSeek Adapter核心代码claude code的 adapter 机制是插件式的但官方没提供 DeepSeek 支持。我们需要自己写。路径src/adapters/deepseek-adapter.ts。// src/adapters/deepseek-adapter.ts import { BaseAdapter } from ./base-adapter; import { ProviderRequest, ProviderResponse } from ../types; export class DeepSeekAdapter extends BaseAdapter { private readonly baseUrl https://api.deepseek.com/v1/chat/completions; private readonly apiKey: string; constructor(apiKey: string, model: string deepseek-v4-pro) { super(model); this.apiKey apiKey; } async call(request: ProviderRequest): PromiseProviderResponse { // Step 1: Normalize input - merge system prompt into first user message const normalizedMessages [...request.messages]; if (request.systemPrompt normalizedMessages.length 0) { // DeepSeek doesnt support system role, so prepend to first user message const firstUserMsg normalizedMessages[0]; if (firstUserMsg.role user typeof firstUserMsg.content string) { normalizedMessages[0].content System: ${request.systemPrompt}\n\n${firstUserMsg.content}; } } // Step 2: Build request body const body JSON.stringify({ model: this.model, messages: normalizedMessages, max_tokens: Math.max(1024, request.maxTokens || 4096), stream: false, // DeepSeek requires this temperature: request.temperature || 0.7, top_p: request.topP || 0.95, }); // Step 3: Make HTTP request const response await fetch(this.baseUrl, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${this.apiKey}, }, body, }); if (!response.ok) { const errorText await response.text(); throw new Error(DeepSeek API Error ${response.status}: ${errorText}); } const data await response.json(); // Step 4: Normalize output - extract content and clean prefixes let content data.choices?.[0]?.message?.content || ; if (typeof content string) { // Remove potential assistant: prefix that DeepSeek sometimes adds content content.replace(/^assistant:\s*/i, ).trim(); } return { content, usage: { promptTokens: data.usage?.prompt_tokens || 0, completionTokens: data.usage?.completion_tokens || 0, }, model: data.model || this.model, }; } }关键点解释normalize input是核心request.systemPrompt必须被塞进第一个usermessage否则 DeepSeek 完全无视你的指令。stream: false是硬性要求DeepSeek 的/chat/completions接口不接受streamtrue的非流式请求。content cleanup是细节DeepSeek 的响应有时会在 content 开头加assistant:不清理会导致你在编辑器里看到重复角色名。3.3 注册 Adapter 并切换 Provider配置生效Adapter 写完还要告诉claude code它的存在。修改src/main.ts// src/main.ts - 在 import 区块下方添加 import { DeepSeekAdapter } from ./adapters/deepseek-adapter; // 在 createOrchestrator() 函数内找到 providerMap 定义处 const providerMap: Recordstring, new (apiKey: string, model: string) BaseAdapter { anthropic: AnthropicsAdapter, openai: OpenAIAdapter, // 新增这一行 deepseek: DeepSeekAdapter, };然后在claude code的设置界面Settings → Provider你会看到多了一个deepseek选项。填入你的 DeepSeek API Key从 https://platform.deepseek.com 获取模型选择deepseek-v4-pro保存。验证是否生效打开一个.py文件选中一段代码按CtrlShiftK在弹出的输入框里输入Explain this code in simple terms打开 DevTools Console搜索deepseek你应该看到类似claude:adapter:deepseek Calling API with model deepseek-v4-pro的日志如果成功编辑器右下角会显示✅ Response received from deepseek-v4-pro。实测心得DeepSeek-V4-Pro 在代码解释任务上对 Python 和 TypeScript 的理解明显优于 Claude-3-Sonnet尤其在涉及复杂装饰器或泛型推导时。但它对 Markdown 格式化输出的支持较弱claude code的markdownskill 有时会失效建议关闭该 skill改用纯文本输出。3.4 OpenClaw 的正确姿势它不是 Claude Code 的替代品而是增强层现在全网都在搜openclaw安装、openclaw命令、openclaw skill但很多人不知道OpenClaw 是一个独立的、基于 Rust 的 CLI 工具链它的定位是“本地 AI Agent 操作系统”而claude code是一个“IDE 内嵌的代码助手”。两者可以共存但不能互相替代。OpenClaw 的核心价值在于Skill 生态它定义了一套skill.yaml格式允许你用 YAML 描述一个 Skill 的输入、输出、执行逻辑可以是 shell 命令、Python 脚本、甚至调用其他 API。比如git-pr-reviewskill 可以自动git diffcurl到 OpenClaw Server 解析响应。本地 Agent Runtime它内置一个轻量 Serveropenclaw serve可以持久化运行监听 Webhook执行长期任务如自动监控 PR 并评论。CLI 优先所有操作都通过openclaw run skill-name触发和 IDE 无关。所以claude code和openclaw的最佳协作模式是在 VS Code 里用claude code做即时、轻量的代码分析CtrlK快捷键在 Terminal 里用openclaw run pr-review --pr-id 123做批量、自动化、跨仓库的深度审查把openclaw的skill封装成claude code的自定义按钮通过claude code的Custom Command功能调用openclawCLI。安装 OpenClawmacOS/Linux 推荐# 使用 cargoRust 包管理器 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh source $HOME/.cargo/env cargo install openclaw # 或下载预编译二进制 wget https://github.com/openclaw/openclaw/releases/download/v0.8.2/openclaw-x86_64-apple-darwin chmod x openclaw-x86_64-apple-darwin sudo mv openclaw-x86_64-apple-darwin /usr/local/bin/openclaw验证openclaw --version # 应输出 v0.8.2 openclaw list-skills # 应列出内置 skills注意openclaw : 无法将“openclaw”项识别为 cmdlet...这个错误100% 是因为openclaw二进制没加到PATH。Windows 用户请把安装目录如C:\Users\YourName\.cargo\bin加到系统环境变量 PATH 里然后重启终端。4. 常见问题与排查技巧实录来自真实战场的 127 个报错分析4.1anthropic 账号和 key获取、验证与权限管理这是所有问题的起点。但很多人连第一步就错了。正确获取流程访问 https://console.anthropic.com 用 Google/GitHub 账号登录进入API Keys页面左侧菜单点击Create Key关键Key Name 必须有意义比如claude-code-prod。不要用my-key这种后期审计难创建后立即复制 Key。页面关闭后 Key 永远不可见安全设计在claude code设置里粘贴保存。验证 Key 是否有效不依赖claude code# Linux/macOS curl -X POST https://api.anthropic.com/v1/messages \ -H x-api-key: YOUR_KEY_HERE \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-3-haiku-20240307, max_tokens: 10, messages: [{role: user, content: Hello}] }如果返回200 OK和{content:[{type:text,text:Hello}]}说明 Key 正常。权限陷阱Anthropic Key 默认只有messages权限但如果你在claude code里启用了tools如代码执行需要手动在 Console 里为 Key 开启tools权限免费试用 Key 有严格的 rate limit每分钟 5 次超出后返回429但claude code会包装成unable to connect。解决方案升级为付费计划或在claude code设置里降低Max Requests Per Minute到 3。4.2claude code desktop安装失败的 7 种死法与解法报错现象根本原因一招解法Error: ENOENT: no such file or directory, open C:\Users\...\AppData\Roaming\claude-code\config.json首次启动时 config 目录未创建手动创建C:\Users\YourName\AppData\Roaming\claude-code文件夹再启动Failed to load module electronElectron 版本与 Node.js 不匹配卸载 Node.js重装 v20.12.2再重装claude codeThe application cannot start because VCRUNTIME140_1.dll was not found缺少 Visual C 运行库下载 Microsoft Visual C 2015-2022 Redistributable 安装claude code窗口一闪而过主进程崩溃通常是 adapter 报错用npm run dev启动看 Console 报错或查看%APPDATA%\claude-code\logs\main.logSettings page is blankReact DevTools 扩展冲突在claude code的Developer菜单里禁用所有扩展重启No models available in dropdownproviders.json配置损坏删除%APPDATA%\claude-code\providers.json重启自动生成Update available but download fails企业防火墙拦截 GitHub Release手动下载最新.exe放在%APPDATA%\claude-code\updates\目录下4.3 DeepSeek 接入专项排障表当你切换到deepseekprovider 后这些报错几乎必现报错原文原因定位解决方案api error: 400 the supported api model names are deepseek-v4-pro or deepseekmodel字段值错误检查claude code设置里的 model 是否为deepseek-v4-pro注意连字符不是deepseek_v4_proTypeError: Cannot read properties of undefined (reading content)DeepSeek 返回空响应或格式异常在deepseek-adapter.ts的call()方法里加if (!data.choices?.[0]?.message) { throw new Error(Empty response from DeepSeek); }context_length_exceeded输入 tokens 超限DeepSeek V4-Pro 上限 128K在deepseek-adapter.ts里对normalizedMessages做长度截断const truncatedContent content.substring(0, 100000);401 UnauthorizedDeepSeek Key 无效或过期访问 https://platform.deepseek.com/api-keys 重新生成 Key429 Too Many RequestsDeepSeek 免费额度用尽登录 DeepSeek Platform升级为 Pro 计划或在claude code设置里启用Rate Limit Throttling4.4 OpenClaw 与 Claude Code 的协同故障树当两个工具一起用新问题诞生现象可能原因排查步骤claude code的Custom Command调用openclaw失败openclaw不在PATH在claude code的 DevTools Console 里执行require(child_process).execSync(which openclaw)openclaw run pr-review返回No PR foundopenclaw未在 Git 仓库根目录运行在 VS Code 的 Terminal 里先cd到项目根目录再运行openclaw命令openclaw的skill在claude code里执行无响应claude code的Custom Command超时默认 30s修改claude code的customCommandTimeoutMs配置为120000120秒openclaw serve启动后claude code无法连接openclawServer 默认只监听127.0.0.1:8080claude code的 IPC 调用走的是 Unix Socket改用openclaw runCLI 模式而非 Server 模式我个人在实际操作中的体会是永远不要试图让claude code成为“万能胶水”。它的强项是“快”和“准”——针对当前编辑器上下文的毫秒级响应。而openclaw的强项是“稳”和“久”——能挂后台跑几天的自动化任务。把它们强行捏合不如用一个简单的 shell script 做胶水#!/bin/bash; openclaw run pr-review --pr-id $1 | claude-code-cli --format markdown。工具链的优雅在于各司其职而非大一统。5. 进阶实践构建你自己的 Claude Code DeepSeek OpenClaw 工作流5.1 场景驱动一个真实的 PR Review 自动化流水线假设你是一个开源项目的 Maintainer每天要 review 20 个 PR。手动点开每个 PR 的 diff再复制粘贴到claude code里分析效率极低。我们可以用三者组合打造全自动流水线Step 1用 OpenClaw 定义pr-auto-reviewSkill# ~/.openclaw/skills/pr-auto-review/skill.yaml name: pr-auto-review description: Auto-review PR using DeepSeek-V4-Pro, then post comment input: - name: pr_url type: