@puyinkai

旺小宝

OpenClaw plugin for 旺小宝 (wangxiaobao) — bundles auth / audio-sync / switch-project skills with shared OAuth device flow and token store

当前版本
v0.1.16
code-plugin社区source-linked

openclaw-xiaobao

License: MIT Node.js Version

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」。


目录


背景

旺小宝身份服务(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 toolxiaobao_*):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.jsonagents.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.tsresolveConfig(api.config) 合并 plugin 配置 + 环境变量;应用级 OAuth 凭据(client_id / secret)硬编码——参考 lark 的设计,应用 secret 是 plugin 作者拥有的
src/core/token-store.tsreadToken / writeToken / clearToken / isAccessTokenValid / decodeJwtPayload;按 workspaceDir 解析路径,落在 <workspaceDir>/.state/wangxiaobao/token.json mode 0600(无 workspaceDir 时回退到 ~/.openclaw/state/wangxiaobao/token.json
src/core/device-flow.tsinitiateDeviceAuthorization(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.tsxbApiFetch(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 直到用户在浏览器同意或超时。

参数

字段类型说明
forceboolean?强制清除缓存重新走 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
pathstring相对路径,例如 /saas/v2/estate/...
queryRecord<string,string>?query string
bodyunknown?request body(对象 → JSON,字符串 → 原文)
headersRecord<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_projects
  • tenantName (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 开始,默认 1
  • size (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.dataaudioId / 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),< 4KB
  • metadata (object?) — 元数据透传(一般不用)
  • extra (object?) — 其他底层字段透传(一般不用)

返回:响应体里最终文本在 data.data.answerthread_id 用于多轮续接。

历史:旧版 xiaobao_sync_audioPOST /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-auth skill 已删除——它的全部功能(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 流程:

  1. 调 plugin tool xiaobao_list_projects 拿扁平化列表
  2. 多个项目时让用户挑编号 + 二次确认;只有一个时直接确认
  3. 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 流程:

  1. xiaobao_whoami 检查登录态,过期就 xiaobao_authorize
  2. tool 内部读 active-project;遇到 NO_ACTIVE_PROJECT 引导走 wangxiaobao-switch-project skill
  3. 解析时间范围(用户给的 / WB_SYNC_CURSOR / 默认 7 天)→ 切成 3 小时窗口
  4. 每个窗口循环调 xiaobao_list_audio 翻页(page 从 1 开始),拿到 audioId 列表
  5. 对每个 audioId 调 xiaobao_get_audio_text 拿转录文本
  6. 用 Write 把"元数据 + 文本"归档到 ./wiki/projects/{projectId}-{projectName}/raw/audio/{userId}-{saleName}/{yyyy-MM-dd}/{audioId}.md幂等跳过同名文件
  7. 每个窗口跑完立刻把 ./.envWB_SYNC_CURSOR 推进到 winEnd
  8. 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 流程:

  1. 解析意图 → 时间范围 + userIdList? 过滤
  2. xiaobao_list_audio 翻页,渲染列表给用户
  3. 用户选某条 → 调 xiaobao_get_audio_text 渲染转录文本
  4. 写文件、改任何状态

wangxiaobao-quick-qa

旺小宝业务数据问数 skill —— 用自然语言问当前激活项目的客户 / 来访 / 挖需率 / 销冠 / 录音覆盖率等。零副作用,鼓励放心调用。threadId 在 agent 上下文里短期记忆做多轮续接。

适合场景:

  • "今天到访了多少组客户" / "首访 vs 复访比例"
  • "本月销冠是谁,业绩多少"
  • "本周录音覆盖率怎么样,哪些销售覆盖低"
  • "挖需率 / 成交转化率多少"

LLM 流程:

  1. 解析用户问题 → prompt业务语言原样传,不要先去查 ID 拼参数)
  2. xiaobao_quick_qa { prompt, threadId? }
  3. 渲染 resp.data.data.answer 给用户;多轮时 agent 自己存 thread_id
  4. 写文件

安装

需要先装好 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

这条命令做了什么

  1. 读插件 manifest openclaw.plugin.jsoncontracts.tools 字段,拿到 12 个 tool 名
  2. ~/.openclaw/openclaw.json,定位 agents.list[].tools.alsoAllow 数组
  3. 把这 12 个 tool 名追加到目标 agent 的 alsoAllow(已存在则跳过——幂等)
  4. 写回配置文件

升级 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.com
  • apiBase (string) — 默认 https://open-ai.wangxiaobao.com
  • scopes (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),有两条路:

  1. plugin 配置(推荐):通过上面的 plugins.openclaw-xiaobao.config 覆盖 authBase / apiBase / scopesclientId / clientSecret 不在 schema 里, 因为它们是 plugin 作者拥有的应用级 secret。
  2. 修改源码: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_CURSORcwd ./.envwangxiaobao-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

  1. src/tools/ 新建 your-tool.ts,参考 whoami.ts 的最简结构:
    • Type.Object({...}) 用 typebox 写参数 schema
    • 必须有 label / description 字段
    • 实现 execute(toolCallId, params) 返回 formatToolResult(...)
  2. index.tsregister(api) 里调 registerYourTool(api)
  3. openclaw.plugin.jsoncontracts.tools 里加 tool 名
  4. pnpm typecheck && pnpm build 验证

调试 device flow

想指向本地 phoenix(例如 http://localhost:9001),改 src/core/config.tsDEFAULT_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.jsonname 改成发布名(例如 @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 registrynpm 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_* 调用:

  1. ensureAccessToken()token.json,如果未过期直接用
  2. 过期但有 refresh_token → POST /oauth2/token (grant_type=refresh_token) 拿新 access_token,写回 token.json
  3. 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.jsonagents.list[].workspace):

  • main — workspace ~/.openclaw/workspace,token ~/.openclaw/workspace/.state/wangxiaobao/token.json
  • product-agent — workspace ~/.openclaw/workspace/product-agent,token ~/.openclaw/workspace/product-agent/.state/wangxiaobao/token.json
  • backend-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

源码与版本

源码仓库

puyinkai/openclaw-xiaobao

打开仓库

源码提交

a2c8856af7de806b3bd38d0c16634758574c133d

查看提交

安装命令

openclaw plugins install clawhub:@puyinkai/openclaw-xiaobao

元数据

  • 包名: @puyinkai/openclaw-xiaobao
  • 创建时间: 2026/05/09
  • 更新时间: 2026/05/13
  • 执行代码:
  • 源码标签: a2c8856af7de806b3bd38d0c16634758574c133d

兼容性

  • 构建于 OpenClaw: 2026.4.24
  • 插件 API 范围: >=2026.3.22
  • 标签: latest
  • 文件数: 14