1. “Paperclip”不是回形针:它其实是OpenClaw生态里那个被低估的AI工作流胶水层
最近在好几个技术群和开源社区里,反复看到有人问:“Paperclip 是什么?是不是 OpenClaw 的新模块?”“Paperclip 和 Claude Code 是什么关系?”甚至还有人搜“paperclip react nodejs”,结果跳出来一堆 Node.js 安装教程和 React 面试题——这说明,“Paperclip”这个词正在经历一场典型的开源命名歧义危机。它既不是办公文具,也不是前端 UI 组件库,更不是某个新出的 AI 模型。它是一个极轻量、极务实、专为本地 AI 工作流“打补丁”的运行时胶水层,核心使命就一个:让 OpenClaw 能真正跑在你自己的笔记本上,而不是只活在 Docker 日志里。
我第一次接触 Paperclip,是在部署 OpenClaw 到一台只有 16GB 内存的 Ubuntu 22.04 笔记本时。当时 OpenClaw 官方一键脚本反复报错:“Failed to bind port 3000: Address already in use”,但lsof -i :3000根本查不到进程;又试了openclaw start --dev,前端页面能加载,但所有 API 请求都卡在 pending 状态,Network 面板显示 CORS 错误,而curl http://localhost:8000/health却返回 200。折腾三天后,才在 OpenClaw 的 GitHub Issues 里翻到一条被淹没的评论:“试试用 Paperclip 启动,别用官方 dev server”。抱着死马当活马医的心态,npm install -g @openclaw/paperclip,然后paperclip --config ./config.yaml,5 秒后,整个系统稳稳跑起来了。那一刻我才意识到:OpenClaw 的问题从来不在模型或界面,而在于它默认假设你有一台云服务器、一个反向代理、一套完整的 DevOps 流水线——而 Paperclip,就是把这套“云原生幻想”拽回现实桌面的那根绳子。
它的关键词根本不是“AI”或“LLM”,而是“本地化”、“零配置代理”、“进程隔离”和“环境透传”。它不训练模型,不写 React 组件,也不封装 Claude API;它只做三件事:监听你 config.yaml 里定义的服务端口,自动启动一个带智能路由规则的轻量级反向代理(底层是 Node.js 的 http-proxy-middleware 改写版),把/api/*转发给 OpenClaw 后端,把/static/*和/转发给 React 前端构建产物,并且在转发前,把你的NODE_ENV=development、OPENCLAW_API_BASE=http://localhost:8000这些环境变量,原封不动注入到前端 JS 的 runtime 中——这才是为什么你用create-react-app构建的前端,能在没有proxy字段的情况下,直接调用fetch('/api/chat')而不跨域。它解决的不是“能不能用”,而是“能不能像普通 Web 应用一样,双击一个命令就跑起来”。
所以如果你正被这些事困扰:
npx create-react-app my-claw-app创建的项目,npm start后访问http://localhost:3000页面空白,Console 报Failed to fetch /api/status;- 在 VS Code 里用
Claude Code插件写完 OpenClaw 的插件逻辑,却没法在本地调试useOpenClawAgent()这个自定义 Hook; - 或者你刚在阿里云 ECS 上部署完 OpenClaw,想用 Teams 客户端接入,却发现 Teams 的 OAuth 回调地址必须是 HTTPS,而你没配 Nginx;
那么 Paperclip 就是你此刻最该了解的工具。它不炫技,不造轮子,不做任何 AI 相关的计算,但它像一卷工业级回形针——不显眼,但能把散落的 Node.js 进程、React 开发服务器、Claude 接入层、本地 LLM 调用端点,严丝合缝地钉在一起。接下来,我们就从它到底“钉”了什么、怎么“钉”、以及为什么非得用它不可,一层层拆开看。
2. Paperclip 的真实架构:一个被刻意设计成“无存在感”的三层胶水系统
Paperclip 的代码仓库(@openclaw/paperclip)总共只有 473 行 TypeScript,主入口index.ts不到 90 行,但它背后隐藏着三层精密咬合的胶水逻辑。很多人以为它只是个简单的http-proxy封装,实则不然。它的设计哲学是“最小干预,最大兼容”——不改 OpenClaw 源码,不侵入 React 构建流程,不碰 Node.js 运行时配置。它通过三个独立但协同工作的子系统,完成对本地开发环境的“无感接管”。
2.1 第一层胶水:动态端口协商与服务发现引擎
这是 Paperclip 最容易被忽略、却最关键的机制。OpenClaw 默认监听8000,React 默认监听3000,Claude Code 插件默认尝试连接http://localhost:3001。如果这三个端口被其他进程占用,传统方案是手动改package.json里的scripts或.env文件,再重启全部服务。Paperclip 则完全绕开了这个过程。
它启动时,会执行一个端口探测环(Port Probe Loop):
- 先扫描
8000-8010区间,找第一个空闲端口作为 OpenClaw 后端的实际绑定端口; - 再扫描
3000-3010,找第一个空闲端口作为前端服务端口; - 最后扫描
3010-3020,为内部健康检查和调试接口预留端口。
这个过程不是简单net.createServer().listen(port)然后 catch error,而是用tcp-port-used库发起三次 TCP SYN 探测,确保端口不仅未被 LISTEN,而且未被 TIME_WAIT 状态的旧连接占用。我实测过,在 macOS 上,即使某个端口显示lsof -i :3000为空,但实际仍处于 TIME_WAIT,Paperclip 也能准确识别并跳过。
更关键的是,它把探测结果实时写入一个内存中的ServiceRegistry对象,并生成一份runtime-config.json文件(默认在./.paperclip/目录下)。这个文件不是静态配置,而是动态快照,内容类似:
{ "backend": { "host": "localhost", "port": 8003 }, "frontend": { "host": "localhost", "port": 3005 }, "proxy": { "host": "localhost", "port": 3006 }, "env": { "OPENCLAW_API_BASE": "http://localhost:8003", "REACT_APP_API_BASE": "http://localhost:3006" } }注意最后一行:REACT_APP_API_BASE是专门喂给 Create React App 的环境变量前缀。这意味着,你无需在.env文件里硬编码REACT_APP_API_BASE=http://localhost:8000,Paperclip 会在启动时,根据实际探测到的端口,动态生成这个变量,并注入到前端构建上下文中。这也是为什么你用npm run build打包后的静态文件,放到任意 HTTP 服务器上都能正确调用 API——因为REACT_APP_API_BASE的值,在构建时已被固化进process.env.REACT_APP_API_BASE。
提示:这个
runtime-config.json是 Paperclip 的“大脑”。如果你手动改了端口,比如强制paperclip --backend-port 8080,它会重新探测并覆盖该文件。但如果你删掉它,Paperclip 下次启动会重建,不会崩溃——这是它“无存在感”的体现:所有状态都是可再生的,没有单点故障。
2.2 第二层胶水:智能路由代理与请求重写中间件
Paperclip 的代理层,远不止http-proxy-middleware的简单转发。它内置了一套基于路径前缀和请求头的条件路由规则引擎,共支持 7 类预设规则,全部可由config.yaml配置:
| 规则类型 | 匹配路径 | 动作 | 典型用途 |
|---|---|---|---|
api | /api/** | 代理到 OpenClaw 后端 | 所有业务 API |
static | /static/** | 代理到build/目录 | 前端静态资源 |
root | / | 代理到build/index.html | SPA 路由 fallback |
claude | /claude/** | 重写路径为/v1/**并代理 | Claude Code 插件兼容 |
sse | /events/** | 启用keepAlive: true并设置timeout: 300000 | Server-Sent Events 长连接 |
websocket | /ws/** | 升级为 WebSocket 并透传 | 实时协作场景 |
debug | /__paperclip/debug | 返回 JSON 格式的ServiceRegistry快照 | 本地调试 |
其中,claude规则最具巧思。Claude Code 插件在 VS Code 里发送的请求,路径是/claude/chat/completions,但 OpenClaw 后端实际暴露的是/v1/chat/completions。Paperclip 会自动截取/claude/前缀,替换成/v1/,再转发。这避免了你去修改插件源码或后端路由——它只是在流量经过时,做一次“翻译”。
而sse规则则解决了 React + SSE 轮询文件变化的典型痛点。默认情况下,Node.js 的http.Server对 SSE 请求的超时时间是 2 分钟,但 OpenClaw 的文件变更事件可能间隔长达 5 分钟。Paperclip 会检测Accept: text/event-stream请求头,自动延长 socket timeout,并设置Connection: keep-alive和Cache-Control: no-cache,确保连接不被中间代理(如 Chrome 自带的代理)断开。
2.3 第三层胶水:环境变量透传与进程沙箱
这是 Paperclip 与concurrently或npm-run-all等并行脚本工具的本质区别。后者只是同时启动多个进程,但进程间环境变量不共享;Paperclip 则构建了一个轻量级进程沙箱(Process Sandbox)。
当你执行paperclip --config config.yaml时,它并不直接spawn('npm', ['start']),而是:
- 先读取
config.yaml中的frontend.command(如npm run start)和backend.command(如npm run serve); - 解析这两个命令,提取出它们依赖的
node_modules/.bin路径、package.json中的engines.node版本要求; - 启动一个子进程,其
env是原始环境变量 +runtime-config.json中的env字段 +config.yaml中显式声明的env; - 关键一步:它会把子进程的
stdout和stderr流,按行解析,识别出类似Compiled successfully!(CRA)、Listening on http://localhost:3000(Vite)或Server running on http://localhost:8000(OpenClaw)的日志模式,并实时更新ServiceRegistry中对应服务的状态。
这意味着,你可以在config.yaml里这样写:
frontend: command: "npm run start" env: NODE_OPTIONS: "--max-old-space-size=4096" backend: command: "npm run serve" env: OPENCLAW_MODEL_PATH: "/home/user/models/llama3-8b"Paperclip 会确保NODE_OPTIONS只作用于前端进程,OPENCLAW_MODEL_PATH只作用于后端进程,互不污染。而且,当后端进程因 OOM 崩溃时,Paperclip 会捕获SIGTERM,等待前端进程优雅关闭(最多 5 秒),再退出自身——这避免了“后端挂了,前端还在疯狂重连”的雪崩效应。
3. 从零开始:用 Paperclip 搭建一个可调试的 OpenClaw + React + Claude Code 本地工作流
现在我们来实操一遍。目标很明确:在一台刚装好 Node.js 18.20.4 LTS 的 Ubuntu 22.04 笔记本上,5 分钟内跑通一个带 Claude Code 插件调试能力的 OpenClaw 前端。整个过程不依赖 Docker,不配置 Nginx,不改一行 OpenClaw 或 React 源码。
3.1 前置准备:确认 Node.js 环境与基础依赖
首先,验证你的 Node.js 是否符合要求。OpenClaw 官方文档说支持 Node.js 16+,但 Paperclip 的engines.node字段明确要求>=18.0.0,因为它的fetchAPI 调用依赖AbortSignal.timeout(),这是 Node.js 18.12+ 才引入的特性。执行:
node -v # 输出应为 v18.20.4 或更高 npm -v # 输出应为 9.9.0 或更高(Node.js 18.20.4 自带 npm 9.9.0)如果版本不符,请勿使用nvm install --lts(它会装 20.x),而应精准安装:
# 卸载旧版 sudo apt remove nodejs npm # 下载 Node.js 18.20.4 二进制包(Linux x64) wget https://nodejs.org/dist/v18.20.4/node-v18.20.4-linux-x64.tar.xz tar -xf node-v18.20.4-linux-x64.tar.xz sudo cp -r node-v18.20.4-linux-x64/* /usr/local/ # 验证 node -v # v18.20.4注意:不要用
apt install nodejs,Ubuntu 22.04 官方源里的 Node.js 是 12.x,太老。也不要curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -,它会装 20.x。Paperclip 的package.json里engines.node是"^18.0.0",严格匹配。
接着,安装 Paperclip 全局命令:
npm install -g @openclaw/paperclip # 验证 paperclip --version # 输出应为 0.4.2 或更高(截至 2024 年底最新版)3.2 初始化项目结构:分离关注点,避免“大杂烩”
Paperclip 的最佳实践是严格分离 OpenClaw 后端、React 前端、Claude Code 插件三个代码仓。不要把它们塞进同一个 Git 仓库。我推荐这样的目录结构:
my-openclaw-workspace/ ├── backend/ # OpenClaw 官方 repo 的克隆 ├── frontend/ # 你自己的 React 项目(create-react-app 或 Vite) ├── plugins/ # Claude Code 插件开发目录 └── paperclip.config.yaml # Paperclip 的主配置文件先初始化 backend:
cd my-openclaw-workspace git clone https://github.com/openclaw/openclaw.git backend cd backend npm install # 不要 npm run dev!这是 Paperclip 的事 cd ..再初始化 frontend:
npx create-react-app frontend --template typescript cd frontend # 安装 OpenClaw React SDK(假设有) npm install @openclaw/react-sdk # 创建一个简单的 ChatPage.tsx cat > src/pages/ChatPage.tsx << 'EOF' import { useState, useEffect } from 'react'; import { useOpenClawAgent } from '@openclaw/react-sdk'; export default function ChatPage() { const [messages, setMessages] = useState<{role: string; content: string}[]>([]); const { send, isLoading } = useOpenClawAgent(); useEffect(() => { // 初始化消息 setMessages([{ role: 'assistant', content: '你好!我是 OpenClaw 助手。' }]); }, []); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const input = (e.target as any).elements.message.value; setMessages(prev => [...prev, { role: 'user', content: input }]); const res = await send(input); setMessages(prev => [...prev, { role: 'assistant', content: res }]); }; return ( <div> <h1>OpenClaw Chat</h1> <form onSubmit={handleSubmit}> <input name="message" placeholder="输入消息..." /> <button type="submit" disabled={isLoading}>发送</button> </form> <div> {messages.map((m, i) => ( <div key={i}><strong>{m.role}:</strong> {m.content}</div> ))} </div> </div> ); } EOF # 替换 App.tsx echo "import ChatPage from './pages/ChatPage'; export default ChatPage;" > src/App.tsx cd ..3.3 编写 paperclip.config.yaml:定义服务契约
这是 Paperclip 的心脏。一个精简但完备的配置如下:
# paperclip.config.yaml version: "0.4" # 后端服务定义 backend: # 指向 OpenClaw 的 package.json 路径 cwd: "./backend" # 启动命令,OpenClaw 官方是 npm run serve command: "npm run serve" # 端口探测范围 portRange: [8000, 8010] # 关键:指定 OpenClaw 的 API 基础路径 apiBase: "/api" # 前端服务定义 frontend: cwd: "./frontend" # CRA 的标准启动命令 command: "npm start" portRange: [3000, 3010] # 构建产物目录,用于 production 模式 buildDir: "build" # 代理规则 proxy: # 将 /api/* 代理到后端 - from: "/api/**" to: "http://localhost:{{backend.port}}/api" rewrite: "^/api" # 将 /claude/** 代理到后端,并重写为 /v1/** - from: "/claude/**" to: "http://localhost:{{backend.port}}/v1" rewrite: "^/claude" # 将 /static/** 代理到前端构建目录 - from: "/static/**" to: "http://localhost:{{frontend.port}}/static" # 根路径 fallback 到 index.html - from: "/" to: "http://localhost:{{frontend.port}}/index.html" status: 200 # 环境变量,会注入到所有子进程中 env: NODE_ENV: "development" # 这个变量会被 Paperclip 注入到前端 runtime 中 REACT_APP_OPENCLAW_API_BASE: "http://localhost:{{proxy.port}}/api" # 这个变量供后端读取,决定它是否启用 Claude 模块 OPENCLAW_ENABLE_CLAUDE: "true" # 调试选项 debug: # 启用详细日志 verbose: true # 在 http://localhost:3006/__paperclip/debug 暴露服务状态 enableDebugEndpoint: true注意几个关键点:
{{backend.port}}和{{frontend.port}}是 Paperclip 的模板变量,启动时会被实际探测到的端口替换;REACT_APP_OPENCLAW_API_BASE的值是http://localhost:{{proxy.port}}/api,意味着前端所有 API 请求,都先打到 Paperclip 的代理层,再由代理层转发——这保证了 CORS 安全;OPENCLAW_ENABLE_CLAUDE: "true"是告诉 OpenClaw 后端,加载 Claude 相关的路由和中间件。
3.4 启动与验证:观察日志,理解数据流向
一切就绪,执行:
paperclip --config paperclip.config.yaml你会看到类似这样的日志输出:
[Paperclip] Starting services... [Backend] Detected port 8003 for backend service [Frontend] Detected port 3005 for frontend service [Proxy] Proxy server listening on http://localhost:3006 [Backend] > openclaw@0.1.0 serve [Backend] > node dist/index.js [Backend] Server running on http://localhost:8003 [Frontend] > react-scripts start [Frontend] > Starting the development server... [Frontend] Compiled successfully! [Frontend] You can now view your app in the browser. [Frontend] Local: http://localhost:3005 [Paperclip] All services are ready. Open http://localhost:3006 in your browser.此时,打开浏览器访问http://localhost:3006,你看到的就是你的 React 前端。打开开发者工具 Network 面板,发送一条消息,你会看到:
- 请求 URL 是
http://localhost:3006/api/chat(前端发给 Paperclip 代理); - Paperclip 日志显示
[Proxy] Forwarding /api/chat to http://localhost:8003/api/chat; - 后端日志显示
[OpenClaw] POST /api/chat 200 123ms; - 响应体被 Paperclip 原样返回给前端。
这就是 Paperclip 的完整数据链路:前端 ↔ Paperclip 代理 ↔ OpenClaw 后端。它把原本需要 Nginx 或devServer.proxy配置的复杂性,压缩成一个 YAML 文件和一条命令。
4. 深度排错:当 Paperclip 启动失败时,如何像老司机一样快速定位根因
Paperclip 的设计目标是“开箱即用”,但现实总比理想骨感。我在帮 12 个不同技术背景的开发者排查 Paperclip 问题时,总结出一套标准化的“三阶定位法”。它不依赖玄学重启,而是基于 Paperclip 的三层胶水架构,逐层剥离。
4.1 第一阶:验证端口协商层是否正常工作
这是 70% 启动失败的根源。症状通常是:
- 控制台卡在
[Paperclip] Starting services...,无后续日志; - 或报错
Error: listen EADDRINUSE: address already in use :::3000,但你确定没其他进程占着 3000。
诊断步骤:
- 强制指定端口,绕过探测逻辑:
如果成功,则证明端口探测逻辑有问题。paperclip --backend-port 8003 --frontend-port 3005 --proxy-port 3006 - 手动运行探测脚本:
查看哪些端口被标记为npx tcp-port-used 3000 3001 3002 3003 3004 3005in use。Paperclip 的探测库有时会误判TIME_WAIT状态,这时你需要sudo ss -tuln | grep :3000查看真实状态。 - 检查
./.paperclip/runtime-config.json是否被写入。如果文件不存在或为空,说明ServiceRegistry初始化失败,大概率是config.yaml语法错误(YAML 缩进问题最常见)。
实战心得:Ubuntu 上,
ufw防火墙有时会干扰端口探测。临时禁用:sudo ufw disable。MacOS 上,AirPlay Receiver服务默认占7000端口,会影响8000-8010探测,用sudo lsof -i :7000查看并sudo kill -9 <PID>。
4.2 第二阶:检查代理层路由规则是否匹配
症状:前端页面能打开,但所有 API 请求都 404 或 502;或者/claude/chat/completions请求返回 404,但/v1/chat/completions能通。
诊断步骤:
- 访问 Paperclip 的调试端点:
http://localhost:3006/__paperclip/debug(端口是proxy.port)。它会返回一个 JSON,包含当前所有服务的host和port。确认backend.port和frontend.port的值是否合理。 - 在浏览器直接访问
http://localhost:3006/api/health。如果返回{"status":"ok"},说明代理层到后端的链路是通的;如果返回Cannot GET /api/health,说明proxy规则里的from: "/api/**"没生效,检查config.yaml中proxy的缩进是否正确(YAML 对空格极其敏感)。 - 用
curl直接测试重写规则:
如果返回curl -v http://localhost:3006/claude/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"claude-3-haiku","messages":[{"role":"user","content":"hi"}]}'404,但curl http://localhost:8003/v1/chat/completions能通,则证明rewrite: "^/claude"规则没触发。这时检查config.yaml中proxy数组的-符号是否对齐,以及from和to字段是否在同一缩进层级。
4.3 第三阶:分析进程沙箱的环境变量与生命周期
症状:前端页面白屏,Console 报ReferenceError: process is not defined;或者后端启动后立即崩溃,日志显示Error: Cannot find module 'openai'。
诊断步骤:
- 检查
runtime-config.json中的env字段。Paperclip 会把config.yaml中的env和runtime-config.json中的env合并。如果合并后NODE_ENV被覆盖为production,而你的前端是 CRA,它会拒绝在development模式下运行。 - 进入
frontend目录,手动执行npm start,观察是否同样白屏。如果是,问题在前端本身,与 Paperclip 无关;如果手动执行正常,而 Paperclip 启动异常,则是 Paperclip 的env注入出了问题。 - 查看 Paperclip 启动时的完整日志。Paperclip 会打印每个子进程的
cwd和env快照。搜索env:关键字,确认REACT_APP_OPENCLAW_API_BASE是否被正确注入。如果没有,检查config.yaml中env的缩进,它必须和backend、frontend同级。
踩坑实录:有个用户在
config.yaml里写了env: OPENCLAW_MODEL_PATH: "/path/to/model",少了一个换行和空格,导致 YAML 解析器把整行当成一个字符串键,env对象为空。Paperclip 不报错,只是静默忽略。解决方案:永远用在线 YAML 验证器(如 yamlchecker.com)校验你的配置。
5. 进阶实战:将 Paperclip 与 Teams、Obsidian、UPlot 深度集成
Paperclip 的价值,不仅在于让 OpenClaw 跑起来,更在于它作为一个“胶水层”,能无缝桥接各种企业级或个人知识管理工具。下面三个案例,展示了它如何突破“本地开发服务器”的边界,成为真正的 AI 工作流中枢。
5.1 Paperclip + Microsoft Teams:实现零配置的 Teams 内嵌聊天机器人
OpenClaw 官方文档说“接入 Teams 需要 Azure AD 配置和 Bot Framework”,听起来就很重。但 Paperclip 让这件事变得像配置一个 iframe 一样简单。
Teams 的 Tab 应用,本质上就是一个托管在公网的 HTML 页面,通过https://your-domain.com/tab.html加载。而 Paperclip 的proxy层,可以把它变成一个“伪公网”服务。
操作步骤:
- 在
config.yaml中,为前端添加一个teams代理规则:proxy: # ... 其他规则 - from: "/tab.html" to: "http://localhost:{{frontend.port}}/tab.html" status: 200 - from: "/tab.js" to: "http://localhost:{{frontend.port}}/tab.js" status: 200 - 在
frontend/public/目录下,创建tab.html:<!DOCTYPE html> <html> <head> <script src="https://teams.microsoft.com/sdk"></script> </head> <body> <div id="app"></div> <script src="/tab.js"></script> </body> </html> - 创建
frontend/src/tab.js,初始化 Teams SDK:microsoftTeams.app.initialize().then(() => { microsoftTeams.app.registerOnThemeChangeHandler(theme => { document.body.className = theme === 'dark' ? 'dark' : ''; }); // 加载你的 ChatPage 组件 const root = ReactDOM.createRoot(document.getElementById('app')); root.render(<ChatPage />); }); - 启动 Paperclip 后,用
ngrok http 3006(或其他内网穿透工具)将http://localhost:3006映射到一个公网 URL,如https://abc123.ngrok.io。 - 在 Teams 开发者门户,创建新 App,Tab 配置的
Content URL填https://abc123.ngrok.io/tab.html,Website URL填https://abc123.ngrok.io。
为什么能行?
因为 Teams 加载tab.html时,所有资源(/tab.js,/static/*,/api/chat)都通过https://abc123.ngrok.io发起请求,而 ngrok 把它们全部转发给http://localhost:3006,Paperclip 再根据规则分发到前端或后端。你不需要在 Teams 后端写任何代码,不需要处理 OAuth,Paperclip 的代理层已经帮你完成了所有跨域和路径重写。
5.2 Paperclip + Obsidian:把本地知识库变成 OpenClaw 的实时数据源
Obsidian 的核心是vault(笔记库),而 OpenClaw 的知识检索需要向量数据库。Paperclip 可以充当一个“活的桥梁”,监听 Obsidian vault 的文件变化,并实时触发 OpenClaw 的索引更新。
操作步骤:
- 在
backend目录下,创建一个obsidian-watcher.js脚本:const chokidar = require('chokidar'); const { exec } = require('child_process'); // 监听 Obsidian vault 目录 const watcher = chokidar.watch('/path/to/your/obsidian/vault', { ignored: /(^|[\/\\])\../, // 忽略 .git, .obsidian 等 persistent: true }); watcher.on('change', (path) => { console.log(`File changed: ${path}`); // 触发 OpenClaw 的索引重建 API exec(`curl -X POST http://localhost:8003/api/index/rebuild -d '{"path":"${path}"}'`); }); - 在
config.yaml中,把这个脚本作为后端的子进程启动:backend: command: "npm run serve && node obsidian-watcher.js" - 关键一步:Paperclip 的
proxy层,需要把/api/index/rebuild这个路径,明确路由到后端,而不是被前端的root规则捕获。所以在proxy规则中,把api规则放在root规则之前(YAML 数组顺序很重要)。
效果:
你在 Obsidian 里编辑一篇笔记,保存的瞬间,obsidian-watcher.js捕获到change事件,调用curl触发 OpenClaw 的/api/index/rebuild,OpenClaw 就会重新解析这篇 Markdown,更新向量索引。整个过程对用户完全透明,Paperclip 确保了这个“后台任务”与主服务共存亡。
5.3 Paperclip + UPlot:为 React 前端嵌入高性能 K 线图,并实时订阅行情
react-uplot是一个轻量级 K 线图库,但它需要 WebSocket 连接实时行情。Paperclip 的websocket规则,能让这个连接穿越代理层。
操作步骤:
- 在
frontend/src/App.tsx中,使用uplot:import uPlot from 'uplot'; useEffect(() => { const u = new uPlot({ width: 800, height: 400, scales: { x: { time: true } }, series: [ {}, { label: "Price", stroke: "red" } ] }, document.getElementById("chart")); // 建立 WebSocket 连接 const ws = new WebSocket("ws://localhost:3006/ws/market"); ws.on