openclaw-xiaobao
OpenClaw 的官方旺小宝插件。把分散在三个 skill 里的 OAuth 鉴权、token 管理与 API 调用逻辑统一到一个 plugin 中,让 LLM 可以通过 typed tool 直接 invoke, token 按 agent workspace 隔离,多 agent 互不冲突。
Quick Start
# 1. 安装(本地路径示例;npm / ClawHub 详见「安装」章节)
cd /path/to/openclaw-xiaobao
pnpm install && pnpm build
openclaw plugins install file://$(pwd)
# 1.5 (ClawHub 发布版用法)下载 + 装:
# clawhub package download @puyinkai/openclaw-xiaobao
# openclaw plugins install --force ~/.openclaw/workspace/puyinkai-openclaw-xiaobao-<version>.tgz
# 2. 把 7 个 xiaobao_* tool 注册到 agent 的 alsoAllow(必做,否则 agent 调不到 tool)
node ~/.openclaw/extensions/openclaw-xiaobao/bin/register-tools.js register --all-agents
# 或只注册到 main:... bin/register-tools.js register --agent main
# 3. (可选)配置 plugin 指向测试环境,编辑 ~/.openclaw/openclaw.json
# plugins.entries.openclaw-xiaobao.config.{authBase, apiBase}
# 4. 验证:在 OpenClaw 对话里让 agent 跑 xiaobao_whoami → 应返回 logged_in:false
# 然后 xiaobao_authorize 走 device flow 登录
OpenClaw 不会自动放行 plugin tool——必须跑第 2 步把 tool 名写进 agent 的
tools.alsoAllow,这是 lark 这类 channel plugin 不需要、tool plugin 必须做 的一步。详见「把 tool 注册到 agent」。
目录
- Quick Start
- 背景
- 项目结构
- Plugin Tools
- Bundled Skills
- 安装
- 把 tool 注册到 agent
- 环境变量与配置
- 在 OpenClaw 中使用
- 开发
- 发布到 ClawHub
- 架构与时序
- 常见问题
背景
旺小宝身份服务(phoenix,基于 Spring Authorization Server 1.1.0)已经支持 OAuth 2.0 Device Authorization Grant(RFC 8628)。客户端侧原本由三个独立的 OpenClaw skill 各自维护脚本(Node device flow、Python 录音同步、Python 项目 切换),每个 skill 各跑各的 token / config / 环境变量解析,团队多人分发升级 都麻烦。
本 plugin 把它们整合成一个 OpenClaw 标准插件:
- 12 个 typed tool(
xiaobao_*):LLM 直接 invoke。token 生命周期 (authorize / whoami / logout)、通用 API (api)、项目枚举与切换 (list_projects / switch_project)、授权顾问列表 (list_consultants)、录音元数据 + 单条文本 (list_audio / get_audio_text)、客户分页 + 来访分页 (list_customers / list_visits)、LangChain 短问答 (quick_qa) - 6 个 SKILL.md(无脚本):以 lark plugin 同款风格,告诉 LLM 何时用哪个
plugin tool 拼出完整工作流
wangxiaobao-switch-project— 让用户挑租户/项目,调xiaobao_switch_project落地wangxiaobao-audio-wiki— 拉录音 → 写wiki/projects/.../raw/audio/→ 推进游标wangxiaobao-audio-query— 只读探查录音元数据 / 抽样取文本预览wangxiaobao-customer-query— 客户分页(顾问 / 姓名 / 手机 / 画像 / 最后来访)wangxiaobao-visit-query— 来访分页(客户 ID / 姓名 / 时间范围)wangxiaobao-quick-qa— 旺小宝业务数据自然语言问数(开放式,只读)
- token 按当前 agent 的 workspace 隔离:
<agent.workspaceDir>/.state/wangxiaobao/token.json - 激活项目:全局单份
~/.openclaw/state/wangxiaobao/active-project.json(0600),所有需要 tenant/project 上下文的 tool 都从这里读,参数里不再有tenantId/projectId
用户隔离语义(workspace-scoped)
每个 agent 一份 token:路径
<agent.workspaceDir>/.state/wangxiaobao/token.json。
OpenClaw 给每个 agent 配置独立的 workspace(参见 ~/.openclaw/openclaw.json
的 agents.list[].workspace 字段)。本插件把 token 写在 agent 的 workspace
目录里,所以:
main/product-agent/backend-developer/frontend-developer各自一份 token,互不干扰- 同一个 agent 在不同会话间共享 token(refresh 还在)
- 同一个 OS 用户切换 agent 时不需要重登(每个 agent 独立首登一次)
实现细节:OpenClaw 不在 tool.execute(toolCallId, params, ...) 签名里
传 caller agent 信息;只有 before_tool_call hook 的 ctx 里才有 agentId。
插件在 hook 里把 agentId 解析成 workspaceDir(通过 runtime.agent.resolveAgentWorkspaceDir)
并按 toolCallId 缓存,execute 内再读出来用。
fallback:若 hook 拿不到 agentId(比如某些 trigger 路径),插件回退到
全局路径 ~/.openclaw/state/wangxiaobao/token.json。
项目结构
openclaw-xiaobao/
├── package.json # npm 元数据 + openclaw 块
├── openclaw.plugin.json # plugin manifest:tools / skills / configSchema
├── tsconfig.json
├── tsdown.config.ts # 构建配置(rolldown)
├── .prettierrc.json
├── index.ts # 默认导出 plugin object,register(api) 注册 tool
├── src/
│ ├── core/ # 与 LLM 无关的纯逻辑层
│ │ ├── runtime-store.ts # singleton:保存 register(api) 拿到的 api/runtime
│ │ ├── config.ts # plugin config + env 解析;硬编码 client_id/secret
│ │ ├── token-store.ts # JSON 文件 mode 0600,按 workspaceDir 隔离
│ │ ├── project-store.ts # active-project 全局状态(~/.openclaw/state/wangxiaobao/active-project.json)
│ │ ├── device-flow.ts # RFC 8628 device flow + refresh + revoke
│ │ └── api-client.ts # 带 Bearer 的 fetch + 401 自动 refresh
│ └── tools/ # 12 个 OpenClaw tool 注册入口
│ ├── helpers.ts # formatToolResult / formatToolError / tenantProjectHeaders
│ ├── authorize.ts → xiaobao_authorize
│ ├── whoami.ts → xiaobao_whoami
│ ├── logout.ts → xiaobao_logout
│ ├── api.ts → xiaobao_api
│ ├── list-projects.ts → xiaobao_list_projects
│ ├── switch-project.ts → xiaobao_switch_project
│ ├── list-consultants.ts → xiaobao_list_consultants
│ ├── list-audio.ts → xiaobao_list_audio
│ ├── get-audio-text.ts → xiaobao_get_audio_text
│ ├── list-customers.ts → xiaobao_list_customers
│ ├── list-visits.ts → xiaobao_list_visits
│ └── quick-qa.ts → xiaobao_quick_qa
├── skills/ # plugin manifest 的 "skills" 字段指向这里(纯 SKILL.md)
│ ├── wangxiaobao-switch-project/ # 列项目 → 用户选 → 调 xiaobao_switch_project tool
│ ├── wangxiaobao-audio-wiki/ # 拉录音 → 写 wiki/projects/.../raw/audio/ → 推游标
│ │ ├── SKILL.md
│ │ └── references/{audio-wiki-schema.md, llm-wiki-ingest.md}
│ ├── wangxiaobao-audio-query/ # 只读探查录音元数据,按需取单条文本预览
│ ├── wangxiaobao-customer-query/ # 客户分页(顾问/姓名/手机/画像/最后来访)
│ ├── wangxiaobao-visit-query/ # 来访分页(客户ID/姓名/时间范围)
│ └── wangxiaobao-quick-qa/ # 业务数据自然语言问数(客户/来访/销冠/录音覆盖率 ...)
└── dist/ # 构建产物(pnpm build 生成)
├── index.mjs
└── index.d.mts
模块职责
| 文件 | 职责 |
|---|---|
src/core/runtime-store.ts | 单例存储 OpenClaw 注入的 api / runtime / 解析后的 config,避免 tool 之间循环依赖 |
src/core/config.ts | resolveConfig(api.config) 合并 plugin 配置 + 环境变量;应用级 OAuth 凭据(client_id / secret)硬编码——参考 lark 的设计,应用 secret 是 plugin 作者拥有的 |
src/core/token-store.ts | readToken / writeToken / clearToken / isAccessTokenValid / decodeJwtPayload;按 workspaceDir 解析路径,落在 <workspaceDir>/.state/wangxiaobao/token.json mode 0600(无 workspaceDir 时回退到 ~/.openclaw/state/wangxiaobao/token.json) |
src/core/device-flow.ts | initiateDeviceAuthorization(RFC 8628 §3.1)/ pollForToken(§3.4,处理 authorization_pending / slow_down / access_denied / expired_token)/ refreshToken(RFC 6749 §6)/ revokeRefreshToken(RFC 7009) |
src/core/api-client.ts | xbApiFetch(method, path, opts) 自动注入 Authorization: Bearer,401 时调 refreshToken 重试一次 |
index.ts | 入口:register(api) 把 6 个 tool 注册到 OpenClaw plugin runtime |
Plugin Tools
LLM 通过 OpenClaw agent 直接 invoke。所有 tool 名以 xiaobao_ 前缀。
1. xiaobao_authorize
发起 OAuth 2.0 设备授权流程。优先使用缓存或 refresh,否则启动 device flow
并返回带 markdown 链接的 awaiting_user 响应;后台线程继续轮询 token endpoint
直到用户在浏览器同意或超时。
参数:
| 字段 | 类型 | 说明 |
|---|---|---|
force | boolean? | 强制清除缓存重新走 device flow,默认 false |
返回示例(缓存命中):
{
"source": "cache",
"expires_at": 1715184000000,
"scope": "openid profile read write"
}
返回示例(device flow 启动):
{
"awaiting_user": true,
"markdown_message": "请点击以下链接登录并授权访问你的旺小宝账号:\n\n🔗 [点此完成授权](https://...)\n...",
"verification_uri_complete": "https://admin.wangxiaobao.com/oauth2/device_verification?user_code=ABCD-EFGH",
"user_code": "ABCD-EFGH",
"expires_in": 300,
"interval": 5
}
重要:当
awaiting_user: true时,agent 必须把markdown_message字段原样发给用户——它是预拼好的 markdown 链接。不要自己代替用户打开浏览器;不要把 URL 改成裸文本。
2. xiaobao_whoami
读本地缓存的 id_token 并解码 JWT payload。纯本地操作,无 HTTP 调用。
参数:无
返回示例:
{
"logged_in": true,
"user": { "sub": "1234", "name": "张三", "phone_number": "13800000000" },
"token_expires_at": 1715184000000,
"token_expires_in_seconds": 3580,
"scope": "openid profile read write"
}
3. xiaobao_logout
清除本地 token 缓存,尽力撤销服务端 refresh_token。
参数:无
返回:{ "message": "Logged out", "remote_revoked": true }
4. xiaobao_api
通用旺小宝开放 API 调用器。自动注入 Authorization: Bearer <token>,
HTTP 401 时自动 refresh 并重试一次。
参数:
| 字段 | 类型 | 说明 |
|---|---|---|
method | "GET"|"POST"|"PUT"|"PATCH"|"DELETE" | HTTP method |
path | string | 相对路径,例如 /saas/v2/estate/... |
query | Record<string,string>? | query string |
body | unknown? | request body(对象 → JSON,字符串 → 原文) |
headers | Record<string,string>? | 额外 header |
返回:{ "status": 200, "ok": true, "data": <api response> }
5. xiaobao_list_projects
封装 GET /saas/v2/estate/tenant-and-estate/by-user-id,返回扁平化的
{tenantId, tenantName, projectId, projectName} 列表。
参数:无
返回:{ "projects": [...], "count": 7 }
6. xiaobao_switch_project
把激活的「租户 + 项目」持久化到 plugin 全局状态文件
~/.openclaw/state/wangxiaobao/active-project.json(权限 0600)。后续所有需要
tenant/project 上下文的 tool 都从这里读,不再要求传 tenantId/projectId 入参。
参数:
tenantId(string, 必填) — 来自xiaobao_list_projectstenantName(string, 必填)projectId(string, 必填) — 同上projectName(string, 必填)
返回:{ success: true, activeProject: {...}, message: "已切换到「...」" }
7. xiaobao_list_audio
封装 POST /ai-open/audio/page,分页查询录音元数据(不含文本)。
参数:
fromDate(string, 必填) —yyyy-MM-dd HH:mm:ss(空格分隔),如2026-05-08 00:00:00。也接受 ISO 形式2026-05-08T00:00:00,plugin 会自动转toDate(string, 必填) — 同上userIdList(array?, 可选) — 归属顾问 user id 列表,过滤特定销售page(integer?, 可选) — 页码,从 1 开始,默认 1size(integer?, 可选) — 页容量,默认 10,最大 500
tenant/project 取自 plugin 全局 active-project 状态(先调
xiaobao_switch_project)。无激活项目时返回error: 'NO_ACTIVE_PROJECT'。
返回:Result<PageResult<AudioPageResp>>,data.data.content[] 是元数据数组(audioId / fileId / startTime / endTime / duration / userId / saleName / fileUrl / saleInfo)。
8. xiaobao_get_audio_text
封装 GET /ai-open/audio/text/{audioId},按 audioId 取单条录音的转录文本。
参数:
audioId(string | integer, 必填) — 来自xiaobao_list_audio返回的content[].audioId
tenant/project 同上,取自 active-project。
返回:Result<AudioTextResp>,data.data 含 audioId / talkRatios[] / texts[]。
9. xiaobao_quick_qa
旺小宝业务数据问数 tool —— 用自然语言询问当前激活项目的客户 / 来访 /
挖需率 / 销冠 / 录音覆盖率 / 业务指标等。封装
POST /ai-open/langchain/quick-qa/query。只读,无副作用。
参数:
prompt(string, 必填) — 用户的业务问题(自然语言)threadId(string?) — 多轮 thread ID(来自上一轮响应的thread_id),不传走单轮context(object?) — 额外业务上下文透传(如customer_id/recent_audio_ids),< 4KBmetadata(object?) — 元数据透传(一般不用)extra(object?) — 其他底层字段透传(一般不用)
返回:响应体里最终文本在 data.data.answer,thread_id 用于多轮续接。
历史:旧版
xiaobao_sync_audio(POST /audio/text/page)已经被上游拆成list_audio+get_audio_text两个接口。所有 ai-open 后端接口在 open-ai-gateway 层统一前缀/ai-open/**(gateway 自动剥前缀),新增接口 零网关改动。
Bundled Skills
Plugin manifest 的 "skills": ["./skills"] 字段把以下 SKILL.md 打包进插件。
它们不再带任何脚本——参考 openclaw-lark
的工程惯例,skill 就是一段告诉 LLM「何时调哪个 plugin tool、参数怎么传、
结果怎么处理」的文档,所有副作用(HTTP / 文件读写)都通过 plugin tool 或
内置 Read / Write / Edit 工具完成。
原
wangxiaobao-authskill 已删除——它的全部功能(authorize / whoami / logout / api)都被 plugin tool 直接覆盖。原 audio-wiki / switch-project 的 Python 脚本也已删除,改由 LLM 跟随 SKILL.md 指引调 plugin tool 拼出。
wangxiaobao-switch-project
让用户挑选当前要操作的「租户 + 项目」,结果通过 xiaobao_switch_project tool
写入 plugin 全局状态文件 ~/.openclaw/state/wangxiaobao/active-project.json
(权限 0600)。不再写 cwd .env——所有需要 tenant/project 上下文的 tool
都从这个全局状态读,不再要求参数透传。
LLM 流程:
- 调 plugin tool
xiaobao_list_projects拿扁平化列表 - 多个项目时让用户挑编号 + 二次确认;只有一个时直接确认
- 调
xiaobao_switch_project { tenantId, tenantName, projectId, projectName }落地
只想看列表不切换 → 直接让 LLM 调 xiaobao_list_projects,不需要走本 skill。
wangxiaobao-audio-wiki
完整录音同步 + ingest 工作流,按 项目 / 顾问 / 日期 / 录音 四层归档到
./wiki/projects/{projectId}-{projectName}/raw/audio/...。游标
WB_SYNC_CURSOR 仍写在 cwd ./.env(per-workspace 同步进度),tenant /
project 取自 plugin 全局 active-project 状态。
LLM 流程:
xiaobao_whoami检查登录态,过期就xiaobao_authorize- tool 内部读 active-project;遇到
NO_ACTIVE_PROJECT引导走wangxiaobao-switch-projectskill - 解析时间范围(用户给的 /
WB_SYNC_CURSOR/ 默认 7 天)→ 切成 3 小时窗口 - 每个窗口循环调
xiaobao_list_audio翻页(page 从 1 开始),拿到 audioId 列表 - 对每个 audioId 调
xiaobao_get_audio_text拿转录文本 - 用 Write 把"元数据 + 文本"归档到
./wiki/projects/{projectId}-{projectName}/raw/audio/{userId}-{saleName}/{yyyy-MM-dd}/{audioId}.md,幂等跳过同名文件 - 每个窗口跑完立刻把
./.env的WB_SYNC_CURSOR推进到winEnd - ingest 阶段:把 raw/ 提炼为
consultants/customers/topics/scripts/四类 Layer 2 知识页(详见skills/wangxiaobao-audio-wiki/references/audio-wiki-schema.md)
wangxiaobao-audio-query
录音只读查询 skill——按时间 / 销售 / 翻页查录音元数据,按需取单条文本预览。
不写 wiki 文件、不动游标,跟 wangxiaobao-audio-wiki 区分清楚。
适合场景:估个量、看几条样本、临时筛选某销售的录音。
LLM 流程:
- 解析意图 → 时间范围 +
userIdList?过滤 - 调
xiaobao_list_audio翻页,渲染列表给用户 - 用户选某条 → 调
xiaobao_get_audio_text渲染转录文本 - 不写文件、不改任何状态
wangxiaobao-quick-qa
旺小宝业务数据问数 skill —— 用自然语言问当前激活项目的客户 / 来访 /
挖需率 / 销冠 / 录音覆盖率等。零副作用,鼓励放心调用。threadId 在 agent
上下文里短期记忆做多轮续接。
适合场景:
- "今天到访了多少组客户" / "首访 vs 复访比例"
- "本月销冠是谁,业绩多少"
- "本周录音覆盖率怎么样,哪些销售覆盖低"
- "挖需率 / 成交转化率多少"
LLM 流程:
- 解析用户问题 →
prompt(业务语言原样传,不要先去查 ID 拼参数) - 调
xiaobao_quick_qa { prompt, threadId? } - 渲染
resp.data.data.answer给用户;多轮时 agent 自己存thread_id - 不写文件
安装
需要先装好 OpenClaw 主体(>=2026.3.22):
npm install -g openclaw
openclaw -v
三种安装方式
1. 本地路径(开发期 / 内网部署推荐)
适合本仓库 clone 后直接装:
cd /path/to/xiaobao-auth/openclaw-xiaobao
pnpm install
pnpm build
openclaw plugins install file://$(pwd)
或者通过 absolute path:
openclaw plugins install file:///Users/puyinkai/IdeaProjects/xiaobao-auth/openclaw-xiaobao
2. ClawHub(发布后)
已发布到 ClawHub,但 openclaw plugins install clawhub:... 当前有
"archive integrity mismatch" bug(community 频道 + npm-protocol 重算
SHA 跟 ClawHub 服务器记录对不上)。推荐走 download + 本地安装两步:
# 1. 全局装 clawhub CLI(一次性)
npm i -g clawhub
clawhub login # 浏览器交互登录一次
# 2. 下载 + 装(每次升级跑这两条)
clawhub package download @puyinkai/openclaw-xiaobao
# ↑ 默认下载到 ~/.openclaw/workspace/<name>-<version>.tgz,输出会显示路径
openclaw plugins install --force ~/.openclaw/workspace/puyinkai-openclaw-xiaobao-0.1.6.tgz
# ↑ 替换为上一步输出的实际 tgz 路径
OpenClaw 看到本地 .tgz 路径就直接解压安装,不走 npm protocol,
绕开 SHA 校验 bug。
❌ 不要直接
openclaw plugins install clawhub:@puyinkai/openclaw-xiaobao—— 当前会报 "ClawHub archive integrity mismatch"。等 openclaw/openclaw 修复 npm-protocol SHA reproducibility 问题后会切回clawhub:spec 写法。
3. npm(过渡期)
如果先发到 npm registry:
openclaw plugins install openclaw-xiaobao
验证安装
openclaw plugins list # 应该看到 openclaw-xiaobao
openclaw plugins info openclaw-xiaobao
装完插件后 agent 还不能 invoke
xiaobao_*—— 还要跑下面一段把 tool 名注册到 agent 的 alsoAllow。
把 tool 注册到 agent
OpenClaw 不会自动放行 tool plugin 的工具——必须把 12 个 xiaobao_* 写进每个
要用它们的 agent 的 agents.list[].tools.alsoAllow。本插件自带一个幂等 CLI
帮你做这件事,装完插件之后必跑一次。
调用方式(任选一种)
# 方式 A — 装完后直接 node 跑安装目录里的脚本(最直接)
node ~/.openclaw/extensions/openclaw-xiaobao/bin/register-tools.js register --all-agents
# 方式 B — 从源码目录跑(开发期 / clone 后第一次装)
cd /path/to/xiaobao-auth/openclaw-xiaobao && node bin/register-tools.js register --all-agents
# 方式 C — 一次性 npm link 后全局 alias(推荐长期使用)
cd /path/to/xiaobao-auth/openclaw-xiaobao && npm link
openclaw-xiaobao register --all-agents
不要用
npx openclaw-xiaobao—— OpenClaw 的 plugin install 不会把bin链接到任何node_modules/.bin,npx 找不到本地包就会去 npm registry 拉, 拿到 404(除非将来发到 npm registry)。
子命令一览
# 装到所有 agent
register-tools.js register --all-agents
# 或只装到指定 agent
register-tools.js register --agent main --agent product-agent
# 干跑预览(不写文件)
register-tools.js register --all-agents --dry-run
# 卸载(从所有 agent alsoAllow 移除)
register-tools.js unregister --all-agents
# 查看插件暴露了哪些 tool
register-tools.js list
这条命令做了什么:
- 读插件 manifest
openclaw.plugin.json的contracts.tools字段,拿到 12 个 tool 名 - 读
~/.openclaw/openclaw.json,定位agents.list[].tools.alsoAllow数组 - 把这 12 个 tool 名追加到目标 agent 的 alsoAllow(已存在则跳过——幂等)
- 写回配置文件
升级 plugin 加了新 tool 后 重跑一次 register --all-agents,新 tool 会被
追加;旧的不会重复加。
环境变量 OPENCLAW_CONFIG_PATH / OPENCLAW_HOME 可以指向非默认的 config 位置
(多 OpenClaw workspace 场景)。
为什么要这一步?lark 是 channel plugin,OpenClaw 在启用 channel 时自动给 bound agent 暴露 channel 关联 tool;本插件是 tool plugin,没有这条特殊 路径,所以提供 register CLI 来逼近"装即用"的体验。
之后在 OpenClaw 对话里让 main agent 跑 xiaobao_whoami,能返回
{ logged_in: false, ... } 就说明 tool 已经放行成功。
环境变量与配置
Plugin configSchema(推荐)
通过 OpenClaw 的 config 文件配置(~/.openclaw/workspace/openclaw.json
或类似):
{
"plugins": {
"openclaw-xiaobao": {
"config": {
"authBase": "https://admin.staging.wangxiaobao.com",
"apiBase": "https://open-ai.staging.wangxiaobao.com",
"scopes": "openid profile read write"
}
}
}
}
字段:
authBase(string) — 默认https://admin.wangxiaobao.comapiBase(string) — 默认https://open-ai.wangxiaobao.comscopes(string) — 默认openid profile read write(必须是 phoenix 端 client 已注册的子集)
环境变量(兜底)
重要:plugin 与
_lib.mjs都不再读process.env。OpenClaw 的插件 安全扫描器会拦截"读环境变量 + 调网络请求在同一文件"的代码模式(视为可能 的凭据收集)。因此 plugin 只通过 OpenClaw plugin config 接收覆盖值;应用级 凭据(clientId/clientSecret)作为常量直接写在src/core/config.ts中。
如果想换 phoenix 部署(例如指向 staging),有两条路:
- plugin 配置(推荐):通过上面的
plugins.openclaw-xiaobao.config覆盖authBase/apiBase/scopes。clientId/clientSecret不在 schema 里, 因为它们是 plugin 作者拥有的应用级 secret。 - 修改源码:fork 仓库后改
src/core/config.ts中的DEFAULT_*常量。
业务上下文(两条存储路径)
激活的「租户 + 项目」用 plugin 全局状态,sync 游标用 cwd .env——
分开是因为它们的作用域不同:
| 状态 | 位置 | 写入方 | 读取方 | 作用域 |
|---|---|---|---|---|
tenantId / tenantName / projectId / projectName | ~/.openclaw/state/wangxiaobao/active-project.json(0600) | xiaobao_switch_project tool | 所有需要 tenant/project 的 tool(list_audio / get_audio_text / quick_qa)自动从这里读,不走 schema 入参 | 全局单份(每 OS 用户) |
WB_SYNC_CURSOR | cwd ./.env | wangxiaobao-audio-wiki skill | 同 skill(断点续传) | per-workspace(多项目并行用) |
为什么激活项目放全局而不是 cwd?用户的心智是"切一次项目,后续所有 tool/skill 都用这个"——全局单份匹配这个意图,无需跨 skill 拼参数。 游标
WB_SYNC_CURSOR是 sync 进度,per-workspace 让用户能在不同目录 并行管理多个项目的 wiki。
在 OpenClaw 中使用
直接让 LLM invoke tool
跟 OpenClaw agent 对话时,LLM 看到 plugin 注册的 12 个 typed tool 就可以直接调:
你:帮我看下当前登录的旺小宝账号是谁
agent:(自动 invoke xiaobao_whoami,返回结果)
你:还没登录的话先登录
agent:(invoke xiaobao_authorize → 拿到 awaiting_user)
请点击以下链接登录并授权访问你的旺小宝账号:
🔗 [点此完成授权](https://admin.wangxiaobao.com/oauth2/device_verification?user_code=ABCD-EFGH)
...
你:(点链接 → 浏览器登录 → 同意)
agent:(后台轮询完成,token 已缓存)
你:列出我能访问的项目
agent:(invoke xiaobao_list_projects)
让 agent 跟随 SKILL.md 完整工作流
涉及多步状态变更或文件落盘的流程(例如 audio-wiki 拉一批录音 → 写 wiki → 推进游标)走 skill。Agent 读 SKILL.md 后会自己编排 plugin tool + 内置 Read / Write / Edit 工具完成:
你:把今天的旺小宝录音同步到 wiki
agent:(读 wangxiaobao-audio-wiki SKILL.md)
1. xiaobao_whoami → 已登录 ✓
2. 调 xiaobao_list_audio(自动用 active-project;若返回
NO_ACTIVE_PROJECT,先走 wangxiaobao-switch-project skill)
3. 切窗口循环 xiaobao_list_audio 拿元数据
4. 对每条 audioId 调 xiaobao_get_audio_text 取文本
5. Write wiki/projects/{projectId}-{projectName}/raw/audio/...
6. Edit ./.env 推进 WB_SYNC_CURSOR
完成:新增 N 条,跳过 M 条,游标推进到 ...
没有独立命令行入口
本 plugin 不再提供 CLI 脚本——所有操作都通过 OpenClaw agent 进行。
理由:plugin tool 已经覆盖所有原 CLI 能力,且 agent 模式天然支持
device flow 的 awaiting_user 等待与浏览器跳转。
如果你确实需要在 shell 里直接调,让 agent 帮你跑一次 xiaobao_api
即可(参数是任意 HTTP method + path)。
开发
# 安装依赖
pnpm install
# 类型检查
pnpm typecheck
# 构建(产出 dist/index.mjs + index.d.mts)
pnpm build
# Lint
pnpm lint
pnpm lint:fix
# 格式化
pnpm format
# 单测(如果将来加 tests/)
pnpm test
添加新 tool
- 在
src/tools/新建your-tool.ts,参考whoami.ts的最简结构:Type.Object({...})用 typebox 写参数 schema- 必须有
label/description字段 - 实现
execute(toolCallId, params)返回formatToolResult(...)
- 在
index.ts的register(api)里调registerYourTool(api) - 在
openclaw.plugin.json的contracts.tools里加 tool 名 pnpm typecheck && pnpm build验证
调试 device flow
想指向本地 phoenix(例如 http://localhost:9001),改 src/core/config.ts
的 DEFAULT_AUTH_BASE 常量后 pnpm build,重新装 plugin。或者通过 OpenClaw
plugin config 覆盖(不需要 rebuild):
{
"plugins": {
"openclaw-xiaobao": {
"config": { "authBase": "http://localhost:9001", "apiBase": "http://localhost:9002" }
}
}
}
发布到 ClawHub
ClawHub 是 OpenClaw 官方的插件 registry(类似 npm 之于 Node)。发布流程:
1. 准备工作
package.json的name改成发布名(例如@wangxiaobao/openclaw-xiaobao)- 确认
version已经 bump(语义化版本) - 确认
openclaw.plugin.json内容跟package.json一致 pnpm build产出dist/
2. dry-run 校验
clawhub package publish wangxiaobao/openclaw-xiaobao --dry-run
ClawHub CLI 会校验 manifest 格式 / 命名冲突 / dependencies 等,但不真正发布。
3. 正式发布
clawhub package publish wangxiaobao/openclaw-xiaobao
发布成功后任何 OpenClaw 用户可以装:
openclaw plugins install clawhub:@puyinkai/openclaw-xiaobao # ⚠️ 当前有 integrity bug,见上文 workaround
4. 内部发布(不走 ClawHub)
如果 plugin 是公司内部工具,不希望外部可见,可选:
- 私有 npm registry:
npm publish到内网 npm,用户openclaw plugins install <package-name>安装 - Git 直装:用户
openclaw plugins install git+https://gitlab.internal/...(如果 OpenClaw 支持) - 本地路径分发:把
dist/+openclaw.plugin.json+skills/压缩成 tar,分发后openclaw plugins install file://...
架构与时序
Device flow 时序
┌────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ OpenClaw agent │ │ openclaw-xiaobao │ │ phoenix │
│ (LLM) │ │ plugin │ │ (Spring AS) │
└────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
│ invoke xiaobao_authorize │ │
├─────────────────────────►│ │
│ │ POST /oauth2/device_authorization
│ ├───────────────────────────►│
│ │◄───────────────────────────┤
│ │ device_code, user_code, │
│ │ verification_uri_complete │
│ │ │
│ awaiting_user: true, │ │
│ markdown_message, │ │
│ verification_uri_complete│ │
│◄─────────────────────────┤ │
│ │ (background polling) │
│ 把 markdown_message │ POST /oauth2/token │
│ 转发给用户 ├───────────────────────────►│
│ │ authorization_pending │
│ │◄───────────────────────────┤
│ │ ... 重复 ... │
┌─────────────────────────────────────────────────────┐
│ 用户在浏览器: │
│ 1. 点击 verification_uri_complete │
│ 2. 跳到 phoenix /oauth2/login(服务器渲染登录页) │
│ 3. 输入旺小宝账密 │
│ 4. 跳到 /oauth2/device_consent,勾选 scope,点同意 │
│ 5. 跳到 /oauth2/device_success │
└─────────────────────────────────────────────────────┘
│ │ POST /oauth2/token │
│ ├───────────────────────────►│
│ │◄───────────────────────────┤
│ │ access_token + refresh_token
│ │ │
│ │ writeToken() → <workspaceDir>/.state/wangxiaobao/token.json
┌──────────────────────────────────────┐
│ 后续任意 xiaobao_api / xiaobao_* │
│ tool 都能从同一个 token.json 读到 │
│ access_token(自动 refresh) │
└──────────────────────────────────────┘
Token refresh 时序
任何 xiaobao_api / xiaobao_* 调用:
ensureAccessToken()读token.json,如果未过期直接用- 过期但有
refresh_token→ POST/oauth2/token(grant_type=refresh_token) 拿新 access_token,写回token.json - refresh 失败 → 抛
NotAuthenticatedError,提示用户重跑xiaobao_authorize
API 调用过程中如果 phoenix 返回 401(即使本地认为 token 没过期),api-client
会再做一次强制 refresh 重试。
常见问题
Q: token.json 在哪?怎么按 agent 隔离?
按 agent 的 workspace 目录隔离:<agent.workspaceDir>/.state/wangxiaobao/token.json,
mode 0600。每个 agent 有自己的目录,所以同 OS 用户下 4 个 agent 各持一份
独立 token,互不干扰。
具体路径示例(来自 ~/.openclaw/openclaw.json 的 agents.list[].workspace):
main— workspace~/.openclaw/workspace,token~/.openclaw/workspace/.state/wangxiaobao/token.jsonproduct-agent— workspace~/.openclaw/workspace/product-agent,token~/.openclaw/workspace/product-agent/.state/wangxiaobao/token.jsonbackend-developer— workspace~/.openclaw/workspace/backend-developer,token~/.openclaw/workspace/backend-developer/.state/wangxiaobao/token.json
fallback 路径:插件无法识别当前 agent(极少数边缘场景)时回退到
~/.openclaw/state/wangxiaobao/token.json。
多 OS 用户:天然不冲突($HOME 不同)。多 OpenClaw 实例同 OS 用户:
让它们用不同 $HOME(启动脚本里 export HOME=...)。
Q: plugin 改了代码后 OpenClaw 没生效?
pnpm build 重新生成 dist/,然后:
openclaw plugins reload openclaw-xiaobao
如果不行,卸载重装:
openclaw plugins uninstall openclaw-xiaobao
openclaw plugins install file:///path/to/openclaw-xiaobao
Q: 配置了 plugin config,环境变量还有用吗?
有。优先级:plugin config > 环境变量 > 默认值。两个 skill 现在都通过 plugin
tool 走网络,自然继承 plugin config。./.env 里的变量是业务上下文
(租户 / 项目 / 游标),不是连接配置。
Q: 用户授权过一次还需要重新授权吗?
不需要。token.json 里的 refresh_token 会自动续期;除非:
refresh_token也过期(默认 30 天,phoenix 端配置)- 用户主动
xiaobao_logout或在 phoenix 端 revoke - token.json 文件被删
Q: device flow 在 IM bot / 远程 OpenClaw 实例下能跑吗?
能。device flow 不依赖任何回调;plugin 把 verification_uri_complete 推给
agent,agent 再发到 channel(飞书 / 钉钉 / Web 等),用户在自己浏览器里完成
登录即可。这正是 plugin 用 device flow 而不是 authorization code 的原因。
Q: 不装 plugin 能用 skill 吗?
不能。两个 skill 现在都是纯 SKILL.md,所有副作用通过 plugin 注册的
xiaobao_* tool 完成。没装 plugin 就没有这些 tool,skill 没东西可调。
要在 shell 里手搓 device flow,请参考 src/core/device-flow.ts 移植一份。
Q: 我换了工作目录,原来的项目设置怎么办?
激活的「租户 + 项目」是全局单份(~/.openclaw/state/wangxiaobao/active-project.json),
跟 cwd 无关——换目录后 active-project 自动延续,所有 tool 仍然知道当前项目。
要换项目重跑 wangxiaobao-switch-project 即可。
游标 WB_SYNC_CURSOR 是 cwd-local(在 ./.env)的——换目录就没了。两种做法:
(1) 在新目录重新指定同步起点(让 LLM 跑 audio-wiki 时给 --from);
(2) 把旧目录的 .env 复制 / 软链过来。
License
MIT