claude-mem Cowork 插件配对实战:mem-setup 技能如何把 cmem.ai 凭据安全接入云会话
本篇以 mem-setup 技能 为核心,讲解 claude-mem 的 Cowork 插件(claude-mem-cowork)如何完成"配对"(pairing):从 cmem.ai → Connect 获取 sync token、user id、SyncHub URL 三个值,写入插件配置,使 Hooks 能够在 Claude 官方 App 的云端会话中持续捕获并注入记忆。读完你可以独立完成一次完整的 Cowork 配对、理解凭据的多级回退解析机制,并掌握不泄露密钥的验证方法。
为什么 Cowork 需要单独的配对流程
本地 claude-mem 的模型是"用户机器上跑一个 worker 服务 + 本地 SQLite"。而 Cowork 会话运行在临时(ephemeral)云端容器里,本地 worker/SQLite 无法在那里持久化。Cowork 插件的做法是用一组"薄 HTTPS 垫片"替代 worker:Hooks 捕获工具调用并把原始片段流式上传到 cmem.ai,由云端 Pro 服务端运行 worker 和 observer;编译好的观察结果再被注入回每个新会话和每个派生的子 agent(见 cowork/README.md)。
正因为 worker 在云端,插件本身必须持有一组每用户的凭据才能工作。mem-setup 技能的核心定位就是:把用户自己在 cmem.ai → Connect 页面上看到的三个值配置进插件——"任何人都可以配对,凭据是每用户的配置,绝不硬编码在插件逻辑里"(原文档开篇即强调这一点)。
插件的钩子注册见 hooks/hooks.json,所有事件最终都落到同一个垫片脚本 scripts/cmem-hook.mjs 上,例如 SessionStart 触发 node "${CLAUDE_PLUGIN_ROOT}/scripts/cmem-hook.mjs" context、PostToolUse(matcher 为 *)触发 observation 事件。配对写好的凭据正是这些钩子发请求时的 Bearer API key——没有它,整个插件按设计静默空转(fail-soft)。
第一步:从 cmem.ai → Connect 收集三个值
原文档明确规定需要向用户收集的值(cmem.ai → Connect 页面提供):
- sync token(以
cm_开头)——作为 Bearer API key 使用; - user id(UUID);
- SyncHub URL(一个 workers.dev 或 cmem.ai 的 URL)。
操作要点:
- 如果用户直接粘贴了整段 Connect 说明文本,要从中提取出这三个值,而不是让用户逐个再报一遍;
- 如果三者不全,至少要拿到 sync token——另外两个是可选的。
这三个值与源码中的配置字段是一一对应的。查看 config.json 的默认结构:
{
"apiBase": "https://cmem.ai",
"apiKey": "",
"userId": "",
"syncHubUrl": "",
"inject": {
"sessionStart": true,
"agents": true,
"maxChars": 6000
},
"capture": {
"skipTools": []
}
}
apiKey对应 sync token(cm_...),是必需的;userId对应 Connect 页面的 UUID;syncHubUrl对应 SyncHub URL(workers.dev 或 cmem.ai 域名);apiBase是云端 API 根地址,默认https://cmem.ai,一般无需改。
密钥处理红线:不打印、不进 argv、不写日志
原文档把密钥处理列为"non-negotiable(不可妥协)",共两条铁律:
- 绝不在对话里回显 token、不把它放进 shell argv、不写进日志;
- 只通过文件写(Write/Edit 工具)和文件读来搬动它。
这条纪律直接映射到钩子脚本的安全设计里。cmem-hook.mjs 顶部的注释写明:失败重试用的 spool 文件放在 ~/.claude-mem 且权限 0600,"绝不用全局可读的临时目录(payload 里可能含工具输出)"。也就是说,token 一旦落盘就只以 config.json / settings.json 的文件形式存在,钩子进程运行时再从文件读入内存,任何 stdout/stderr 路径都不会吐出完整密钥——后面"验证"一节里的 status 命令也只回显 token 的最后 4 位。
第二步:写入插件 config.json
原文档第 1 步的操作:定位插件根目录(即 mem-setup 技能所在的这个插件本身),更新它的 config.json:
apiKey← sync tokenuserId← user idsyncHubUrl← SyncHub URL- 用户不要求就不动其他设置——
inject是开关类选项(sessionStart、agents、maxChars,默认全开、注入块上限 6000 字符); - 项目命名是自动的(
cmem_work_*前缀),不可配置。这一点在源码里也被刻意固死:cmem-hook.mjs 中resolveProject()从工作目录推导项目名,根目录会话落到cmem_work_root,项目文件夹会话得到cmem_work_<文件夹slug>(root、home、workspace等通用目录名统一归为root)。注释解释原因:claude-mem 是整个生态里更大的系统,这里手动覆盖命名会造成分叉、破坏下游一致性,所以"故意不设成可选项"。
从源码结构看,写入 config.json 之所以是"唯一持久的写入目标",是因为凭证解析顺序中它排在环境变量之后、本地 settings 之前——loadConfig() 的合并逻辑如下(cmem-hook.mjs 第 50–55 行):
const pick = (...vals) => vals.find(v => typeof v === 'string' && v.trim()) || '';
const cfg = {
apiBase: pick(process.env.CMEM_API_BASE, file.apiBase, local.apiBase) || 'https://cmem.ai',
apiKey: pick(process.env.CMEM_API_KEY, file.apiKey,
local.CLAUDE_MEM_CLOUD_SYNC_TOKEN, local.syncToken, local.apiKey, local.token),
userId: pick(process.env.CMEM_USER_ID, file.userId,
local.CLAUDE_MEM_CLOUD_SYNC_USER_ID, local.userId),
syncHubUrl: pick(process.env.CMEM_SYNC_HUB_URL, file.syncHubUrl,
local.CLAUDE_MEM_CLOUD_SYNC_HUB_URL, local.syncHubUrl, local.hubUrl),
...
};
即每个字段都是"环境变量 → 插件 config.json → ~/.claude-mem/settings.json"三级回退,且兼容旧版短键名(syncToken、hubUrl 等)。这解释了原文档的推荐顺序:Cowork 容器里改 config.json 是主路径;env 变量只在能控制容器环境时使用;本地 settings 文件是与本地安装的兼容通道。
第三步:理解临时容器的局限,必要时重新打包
原文档第 2 步点出了 Cowork 配对的一个关键陷阱:Cowork 容器是临时的,对已安装副本的编辑只在本会话有效。要让配对永久生效,需要重新打包——把插件目录压缩为 <plugin-name>.plugin 发送给用户重新安装(原文档称之为 "the cowork-plugin skill's packaging flow"),并明确告诉用户原因。
从 README.md 的"Fail-soft guarantees"一节可以印证临时性带来的行为边界:
- 未配置 API key → 所有钩子都是静默 no-op,会话永不中断(
ingest()里if (!CFG.apiKey) return;即此逻辑); - cmem.ai 不可达 → 捕获事件暂存到 spool 文件,后续钩子触发时批量 flush 到
/api/hooks/ingest; - 钩子端点尚未部署 → 上下文注入自动回退到
/api/mcp的实时memory_search; - 每个钩子无条件以退出码 0 结束——脚本
main入口的try { ... } catch { /* rule #1: never break the session */ } process.exit(0);就是这条"设计规则 #1"的实现。
也就是说:即便你忘了重打包,配对失效的表现是"记忆功能悄悄关闭",而不是会话报错——排查时应优先检查配置是否真的写进去了。
第四步(可选):同步到本地 claude-mem 的 settings.json
如果这台机器同时装有本地 claude-mem(判断标准:存在 ~/.claude-mem/ 目录),原文档第 3 步建议可选地把同样的三个值写入 ~/.claude-mem/settings.json,且文件权限设为 0600。使用的键名与本地 claude-mem 的 cloud-sync 配对完全一致:
{
"CLAUDE_MEM_CLOUD_SYNC_TOKEN": "cm_…",
"CLAUDE_MEM_CLOUD_SYNC_USER_ID": "<UUID>",
"CLAUDE_MEM_CLOUD_SYNC_HUB_URL": "https://sync.cmem.ai"
}
这三个键在本地主代码中同样是一等公民:src/shared/SettingsDefaultsManager.ts 中定义了 CLAUDE_MEM_CLOUD_SYNC_TOKEN、CLAUDE_MEM_CLOUD_SYNC_USER_ID、CLAUDE_MEM_CLOUD_SYNC_HUB_URL 三个字段,默认值全部为空字符串,并注释"Empty = sync OFF"(Hub URL 示例为 https://sync.cmem.ai)。Cowork 钩子脚本读取同一文件(local.CLAUDE_MEM_CLOUD_SYNC_*),目的正如脚本注释所说:"让同一套凭据同时服务云端和本地两个世界",本地 worker 与钩子脚本都会读它。
0600 权限不是建议性的:settings 里含 sync token,spool 文件同理(appendFileSync(..., { mode: 0o600 }))。在共享机器上这是硬性要求。
第五步:不泄露密钥的验证
原文档第 4 步给出的验证命令:
node "${CLAUDE_PLUGIN_ROOT}/scripts/cmem-hook.mjs" status
执行后报告脱敏后的输出。status 子命令的实现(cmem-hook.mjs 的 cliStatus())会打印五项:
| 输出行 | 含义 |
|---|---|
api base |
生效的 API 根地址(含 env 覆盖结果) |
project |
自动推导的项目名(cmem_work_*) |
api key |
configured (…xxxx) 仅显示后 4 位;MISSING 表示配置没写进去 |
/api/hooks/context |
Pro 端点的 HTTP 状态;404 属于预期——Pro 端点部署完成前会如此,搜索/注入仍走 /api/mcp |
/api/mcp memory_search |
实时检索通道是否可用(OK / unavailable) |
spool |
是否有积压事件(pending events at ... / empty) |
原文档对两种典型结果的解释值得原样记住:MISSING 说明上一步的文件写入没生效(重查 config.json 是否真的落地、是否被容器重置);/api/hooks/context 返回 404 不是故障,云端钩子端点尚未上线时的预期状态,插件会自动回退到 /api/mcp 的 memory_search 通道完成注入与检索。这一回退链在 PRO-ENDPOINT-SPEC.md 里也被明确为"client fallback to /api/mcp (already live)"。
备选通道:纯环境变量,零文件编辑
原文档最后给出的"Alternate source":环境变量覆盖一切,且完全不需要改文件:
CMEM_API_KEY # sync token(必需,最小集合)
CMEM_USER_ID # user id(可选)
CMEM_SYNC_HUB_URL # SyncHub URL(可选)
CMEM_API_BASE # API 根地址,默认 https://cmem.ai(可选)
从 loadConfig() 的 pick() 实现可以确认优先级:env 变量 > config.json > ~/.claude-mem/settings.json。适合能注入容器的托管环境;而在普通 Cowork 会话里,写入 config.json 才是"持久选项"(配合重新打包)。
配套能力:mem-search 技能与搜索纪律
配对完成后,同一插件还提供 mem-search 技能,用于在新会话里主动检索 cmem.ai 上的历史记忆(跨 Claude Code、Cowork、Codex 等所有 agent 的时间戳观察):
node "${CLAUDE_PLUGIN_ROOT}/scripts/cmem-hook.mjs" search "your query" --limit 20
检索遵循 claude-mem 的 Index → Timeline → Transcript 渐进式纪律:先用 1–3 次宽泛关键词搜索扫标题/摘要,再用第一步命中的最具体词(ID、精确短语)加更小的 --limit 收窄,最后基于命中的观察作答——用户问"什么时候"时引用时间戳。该技能同样强调两条安全规则:不要把密钥写进搜索词(查询会发送到 cmem.ai);不要向用户倾倒原始输出,只提炼相关观察。搜索无结果时用同一条 status 命令诊断。
隐私与数据卫生:捕获前发生了什么
虽然 mem-setup 技能本身只讲"配对",但配对的直接后果就是插件开始向 cmem.ai 流式上传工具使用片段。结合 README.md 的 Privacy notes 与钩子脚本实现,理解以下几点能让配对决策更踏实:
- 截断:
tool_input/tool_response每字段发送前截断到 16 KB(FIELD_CAP = 16000),用户/agent prompt 截断到 4 KB(PROMPT_CAP = 4000); - 脱敏:上传前对 payload 做单遍线性正则脱敏——PEM 私钥块、JWT、OpenAI/Anthropic 风格 key(
sk-/sk-ant-)、GitHub/GitLab token、Stripe 风格 key、Slack token、AWS key ID、Google API key、npm token、连接串 userinfo(postgres://user:pass@host中的user:pass)、Cookie/Authorization 头值、password:/api_key=式键值对,全部替换为[cmem-redacted];ssh://git@github.com这类无密码冒号的 userinfo 会保留,因为那是信号不是秘密; - 隐私标签:
<private>…</private>区域整体剥离,且剥离发生在截断之前——避免截断把闭合标签切掉后泄露半个私有区域; - 反馈环保护:
mcp__memory__*和mcp__cmem*自身的调用永不捕获(正则ALWAYS_SKIP),用户可在capture.skipTools中追加更多排除项。
这些行为的正确性由 cowork/test/run-tests.mjs 的测试矩阵验证,包括:observation 正确 POST 到 /api/hooks/ingest 且携带 Bearer 认证、10 万字符的响应被截断到 16 KB 左右、sk-ant- key / GitHub token / key=value 秘密 / 连接串密码被红掉而 https://api.example.com、API_KEY= 等信号保留、含 / 或 @ 的密码仍被完整覆盖等。
小结:配对的完整检查清单
按 mem-setup 技能的原始步骤,一次完整的 Cowork 配对可以浓缩为:
- 从 cmem.ai → Connect 拿到
cm_token、UUID、SyncHub URL(缺啥都要 token); - 全程不回显、不进 argv,只用文件工具写入;
- 更新插件
config.json的apiKey/userId/syncHubUrl,不动inject等其他项; - 意识到容器临时性:需要持久就重打包为
<plugin-name>.plugin让用户重装; - 有本地 claude-mem 时,可选写入
~/.claude-mem/settings.json(0600),复用CLAUDE_MEM_CLOUD_SYNC_*键; - 用
node "${CLAUDE_PLUGIN_ROOT}/scripts/cmem-hook.mjs" status验证:MISSING查文件,404 属预期,spool 为空即健康。
至此,插件的七个钩子(SessionStart / UserPromptSubmit / PostToolUse / PreToolUse on Task|Agent / SubagentStop / Stop / SessionEnd,见 hooks/hooks.json)开始以用户的身份向 cmem.ai 捕获并注入记忆,Cowork 云会话与本地 Claude Code 会话共享同一份跨平台记忆。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00