@puyinkai/xiaobao-cli 0.1.0 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/{api-Ccj5dLMo.mjs → api-CX99_4K7.mjs} +22 -5
  2. package/dist/{api-client-DwySN6x-.mjs → api-client-zDBFgk0y.mjs} +5 -3
  3. package/dist/{audio-VmDQMq59.mjs → audio-ShfvRomP.mjs} +2 -2
  4. package/dist/{auth-ChHvqjCS.mjs → auth-DpK81Vp5.mjs} +3 -3
  5. package/dist/cli.mjs +15 -11
  6. package/dist/{consultant-ABFTL_jx.mjs → consultant-CS9B42d3.mjs} +1 -1
  7. package/dist/current-CfDExInT.mjs +33 -0
  8. package/dist/{customer-DtSnQqy_.mjs → customer-B1cxRbJ9.mjs} +1 -1
  9. package/dist/{focus-BDYuP2vZ.mjs → focus-DfNPPhm5.mjs} +1 -1
  10. package/dist/{format-BZvv8lYc.mjs → format-yjmxrbNm.mjs} +15 -7
  11. package/dist/{list-Bkyi95YO.mjs → list-CD08BRLk.mjs} +6 -6
  12. package/dist/{list-CzB9_Jho.mjs → list-CKlx8YuL.mjs} +6 -6
  13. package/dist/{list-DW6KAAaC.mjs → list-CnlFIFiY.mjs} +6 -6
  14. package/dist/{list-iwd9cH1I.mjs → list-Cu6WzPpA.mjs} +5 -5
  15. package/dist/{list-CVKSGNu4.mjs → list-D56gEqiB.mjs} +6 -6
  16. package/dist/{list--o5Q_PI8.mjs → list-DCcCS6LM.mjs} +6 -6
  17. package/dist/{list-CGACp0y-.mjs → list-DI1OD95x.mjs} +3 -3
  18. package/dist/login-CUUcjWPd.mjs +156 -0
  19. package/dist/{logout-BJm9pjje.mjs → logout-DP7REhCd.mjs} +10 -7
  20. package/dist/{project-Ba05YzJ5.mjs → project-BuQQewOs.mjs} +3 -2
  21. package/dist/{project-store-Bz5kf-EI.mjs → project-store-CKVJa54T.mjs} +4 -3
  22. package/dist/{qa-D-B9FV9W.mjs → qa-CKsKiQIm.mjs} +5 -5
  23. package/dist/{resistance-Cf7zsdB3.mjs → resistance-DsE-Ha6Y.mjs} +1 -1
  24. package/dist/{text-BLH4R3xv.mjs → text-C3jhdPoe.mjs} +5 -5
  25. package/dist/{token-store-CHZ_rJQk.mjs → token-store-CXizLE2_.mjs} +46 -6
  26. package/dist/{use-DV9Ii3Tn.mjs → use-BZES4FZC.mjs} +2 -2
  27. package/dist/{visit-Ct3Vea8I.mjs → visit-CX0Mc1n6.mjs} +1 -1
  28. package/dist/{whoami-DwTHvI4B.mjs → whoami-CZVez-rm.mjs} +2 -2
  29. package/package.json +2 -2
  30. package/skills/wangxiaobao-audio-query/SKILL.md +51 -106
  31. package/skills/wangxiaobao-audio-wiki/SKILL.md +31 -77
  32. package/skills/wangxiaobao-audio-wiki/references/audio-wiki-schema.md +386 -0
  33. package/skills/wangxiaobao-audio-wiki/references/llm-wiki-ingest.md +450 -0
  34. package/skills/wangxiaobao-customer-focus-query/SKILL.md +37 -87
  35. package/skills/wangxiaobao-customer-query/SKILL.md +41 -90
  36. package/skills/wangxiaobao-customer-resistance-query/SKILL.md +32 -86
  37. package/skills/wangxiaobao-quick-qa/SKILL.md +41 -92
  38. package/skills/wangxiaobao-shared/SKILL.md +194 -0
  39. package/skills/wangxiaobao-switch-project/SKILL.md +51 -77
  40. package/skills/wangxiaobao-visit-query/SKILL.md +41 -88
  41. package/dist/login-BwkrByMm.mjs +0 -91
  42. package/dist/{device-flow-BgsZipYA.mjs → device-flow-D6JVcEap.mjs} +1 -1
  43. /package/dist/{headers-D79npewp.mjs → headers-DSnR4sjk.mjs} +0 -0
  44. /package/dist/{util-DgwkUfV9.mjs → util-CaPQBlNa.mjs} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
- import { homedir } from "node:os";
3
2
  import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
4
  import { chmod, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
5
5
  //#region src/core/token-store.ts
6
6
  /**
@@ -19,6 +19,7 @@ import { chmod, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
19
19
  const PRIMARY_DIR = join(homedir(), ".xiaobao");
20
20
  const PRIMARY_TOKEN_FILE = join(PRIMARY_DIR, "token.json");
21
21
  const LEGACY_TOKEN_FILE = join(homedir(), ".openclaw", "state", "wangxiaobao", "token.json");
22
+ const PENDING_AUTH_FILE = join(PRIMARY_DIR, "pending-auth.json");
22
23
  /**
23
24
  * Read order: primary → legacy fallback. Returns null if both missing.
24
25
  */
@@ -58,12 +59,16 @@ async function writeToken(resp) {
58
59
  return tokenInfo;
59
60
  }
60
61
  /**
61
- * Only deletes the primary file. Legacy openclaw path is left intact so a
62
- * `xiaobao-cli auth logout` doesn't silently nuke the plugin's token.
62
+ * Clears both the primary file AND the legacy openclaw fallback path. After
63
+ * `xiaobao-cli auth logout` the user expects to be fully logged out across all
64
+ * known token locations — leaving the legacy file behind would cause
65
+ * `auth whoami` to read it back via fallback and report `logged_in: true`,
66
+ * confusing users ("logout 没用"). The refresh_token is best-effort revoked
67
+ * server-side before this call, so the legacy file is already a dead token.
63
68
  */
64
69
  async function clearToken() {
65
- try {
66
- await unlink(PRIMARY_TOKEN_FILE);
70
+ for (const f of [PRIMARY_TOKEN_FILE, LEGACY_TOKEN_FILE]) try {
71
+ await unlink(f);
67
72
  } catch (e) {
68
73
  if (e.code !== "ENOENT") throw e;
69
74
  }
@@ -81,5 +86,40 @@ function decodeJwtPayload(jwt) {
81
86
  return null;
82
87
  }
83
88
  }
89
+ async function writePendingAuth(p) {
90
+ await mkdir(PRIMARY_DIR, {
91
+ recursive: true,
92
+ mode: 448
93
+ });
94
+ await writeFile(PENDING_AUTH_FILE, JSON.stringify(p, null, 2), { mode: 384 });
95
+ try {
96
+ await chmod(PENDING_AUTH_FILE, 384);
97
+ } catch {}
98
+ }
99
+ async function readPendingAuth() {
100
+ if (!existsSync(PENDING_AUTH_FILE)) return null;
101
+ try {
102
+ return JSON.parse(await readFile(PENDING_AUTH_FILE, "utf-8"));
103
+ } catch (e) {
104
+ if (e.code === "ENOENT") return null;
105
+ throw e;
106
+ }
107
+ }
108
+ async function clearPendingAuth() {
109
+ try {
110
+ await unlink(PENDING_AUTH_FILE);
111
+ } catch (e) {
112
+ if (e.code !== "ENOENT") throw e;
113
+ }
114
+ }
115
+ /** Thrown by `auth login --resume` when there is no usable pending device_code. */
116
+ var NoPendingAuthError = class extends Error {
117
+ code = "NO_PENDING_AUTH";
118
+ hint = "先跑 `xiaobao-cli auth login --no-wait` 发起设备授权,在浏览器完成授权后再跑 `xiaobao-cli auth login --resume`。";
119
+ constructor(message = "没有待完成的设备授权(从未发起,或 device_code 已过期)") {
120
+ super(message);
121
+ this.name = "NoPendingAuthError";
122
+ }
123
+ };
84
124
  //#endregion
85
- export { writeToken as a, readToken as i, decodeJwtPayload as n, isAccessTokenValid as r, clearToken as t };
125
+ export { isAccessTokenValid as a, writePendingAuth as c, decodeJwtPayload as i, writeToken as l, clearPendingAuth as n, readPendingAuth as o, clearToken as r, readToken as s, NoPendingAuthError as t };
@@ -1,5 +1,5 @@
1
- import { n as writeResult, t as writeError } from "./format-BZvv8lYc.mjs";
2
- import { n as saveActiveProject } from "./project-store-Bz5kf-EI.mjs";
1
+ import { r as saveActiveProject } from "./project-store-CKVJa54T.mjs";
2
+ import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  //#region src/commands/project/use.ts
5
5
  /**
@@ -5,7 +5,7 @@ var visit_default = defineCommand({
5
5
  name: "visit",
6
6
  description: "客户来访分页查询"
7
7
  },
8
- subCommands: { list: () => import("./list-Bkyi95YO.mjs").then((m) => m.default) }
8
+ subCommands: { list: () => import("./list-CD08BRLk.mjs").then((m) => m.default) }
9
9
  });
10
10
  //#endregion
11
11
  export { visit_default as default };
@@ -1,5 +1,5 @@
1
- import { i as readToken, n as decodeJwtPayload } from "./token-store-CHZ_rJQk.mjs";
2
- import { n as writeResult, t as writeError } from "./format-BZvv8lYc.mjs";
1
+ import { i as decodeJwtPayload, s as readToken } from "./token-store-CXizLE2_.mjs";
2
+ import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  //#region src/commands/auth/whoami.ts
5
5
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puyinkai/xiaobao-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.8",
4
4
  "description": "旺小宝 CLI — host-agnostic 14 tools (openclaw-xiaobao plugin 的 CLI 等价物)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  "dist/**/*.mjs",
11
11
  "dist/**/*.d.mts",
12
- "skills/**/SKILL.md",
12
+ "skills/**/*.md",
13
13
  "README.md"
14
14
  ],
15
15
  "engines": {
@@ -1,83 +1,47 @@
1
1
  ---
2
2
  name: wangxiaobao-audio-query
3
- description: |
4
- 旺小宝录音只读查询技能。按时间 / 销售 / 翻页查询录音元数据列表,可选地抽样取单条转录文本预览。**不写文件、不动游标**——是 `wangxiaobao-audio-wiki` 的"探查 / 抽样"版。
5
-
6
- **当以下情况时使用此 Skill**:
7
- (1) 用户提到"看看有哪些录音"、"查录音"、"列录音"、"哪些销售有几条录音"、"录音多不多"
8
- (2) 用户想抽样几条录音文本预览,但不想批量入库
9
- (3) 用户在 sync 之前想估个量、看一下数据形状
10
- (4) 用户问"张三/某销售本周打了几个电话"、"上周末有几条客户对话"
11
- (5) 任何"看一眼录音"而**不**触发"同步 / 入库 / 归档"意图的场景
3
+ version: 0.1.0
4
+ description: "旺小宝录音只读查询:按时间窗 + 可选顾问过滤分页查录音元数据列表(audioId / fileId / startTime / duration / 销售 / 签名 fileUrl),或按 audioId 查单条转录文本(含 talkRatios 说话人占比 + texts 分段 ASR)。不写文件、不动游标,是 wangxiaobao-audio-wiki 的探查/抽样版。高频命令: xiaobao-cli audio list --from --to [--user-id <id>] [--user-id-list a,b,c] [--page N] [--size N]、xiaobao-cli audio text <audio-id>。何时用:用户说看看有哪些录音/查录音/列录音/某销售本周打了几个电话/抽样几条录音文本预览/估个量/看一眼录音;任何看一眼录音而不触发同步入库归档意图的场景。"
5
+ metadata:
6
+ requires:
7
+ bins: ["xiaobao-cli"]
8
+ cliHelp: "xiaobao-cli audio --help"
12
9
  ---
13
10
 
14
- > **Host-agnostic CLI skill** — 本 skill 假设 `xiaobao-cli` 已装到 PATH
15
- > (`npm i -g @puyinkai/xiaobao-cli` 或 `npx -y @puyinkai/xiaobao-cli`)。
16
- > Agent 通过 shell 工具(Bash / Run / Shell)执行命令、读 **stdout JSON** 消费;
17
- > stderr 是进度/错误提示。退出码 0 = 成功,非 0 = 业务/网络错(错误对象同时打到 stdout 可解析)。
18
- >
19
- > CLI 14 个子命令跟 openclaw-xiaobao plugin 14 个 tool **1:1 等价**,返回 JSON
20
- > 结构完全一致(`{status, ok, data: {...}}`)。skill 里看到的 `resp.data.data.xxx`
21
- > 取数路径直接对 stdout JSON 用 `jq` / `JSON.parse` 即可。
22
- >
23
- > **plugin tool → CLI 命令翻译表(数组参数走逗号分隔)**:
24
- >
25
- > | plugin tool | CLI 命令 |
26
- > | --- | --- |
27
- > | `xiaobao_authorize { force? }` | `xiaobao-cli auth login [--force]` |
28
- > | `xiaobao_whoami` | `xiaobao-cli auth whoami` |
29
- > | `xiaobao_logout` | `xiaobao-cli auth logout` |
30
- > | `xiaobao_list_projects { keyword? }` | `xiaobao-cli project list [--keyword <kw>]` |
31
- > | `xiaobao_switch_project { tenantId, tenantName, projectId, projectName }` | `xiaobao-cli project use --tenant-id ... --tenant-name ... --project-id ... --project-name ...` |
32
- > | `xiaobao_list_consultants` | `xiaobao-cli consultant list` |
33
- > | `xiaobao_list_audio { fromDate, toDate, userId?, userIdList?, page, size }` | `xiaobao-cli audio list --from "..." --to "..." [--user-id ...] [--user-id-list a,b,c] [--page N] [--size N]` |
34
- > | `xiaobao_get_audio_text { audioId }` | `xiaobao-cli audio text <audioId>` |
35
- > | `xiaobao_list_customers { ... }` | `xiaobao-cli customer list [--user-id] [--user-name] [--customer-name] [--customer-phone] [--portrait] [--from] [--to] [--page] [--size]` |
36
- > | `xiaobao_list_visits { ... }` | `xiaobao-cli visit list [--customer-id] [--customer-name] [--from] [--to] [--page] [--size]` |
37
- > | `xiaobao_list_customer_focus { visitIds, customerIds, audioIds, category, classification, fromDate, toDate, ... }` | `xiaobao-cli focus list [--visit-ids a,b] [--customer-ids a,b] [--audio-ids a,b] [--category ...] [--classification ...] [--from ...] [--to ...]` |
38
- > | `xiaobao_list_customer_resistance { ... }` | `xiaobao-cli resistance list [同 focus]` |
39
- > | `xiaobao_quick_qa { prompt, threadId? }` | `xiaobao-cli qa "<prompt>" [--thread-id ...]` |
40
- > | `xiaobao_api { method, path, query, body, headers }` | `xiaobao-cli api <METHOD> <PATH> [--query k=v] [--body '<json>'] [--headers k=v]` |
41
- >
42
- > 用 `--format toon` 切到 TOON(uniform 数组省 30-50% token,LLM 上下文优化);
43
- > 用 `--format json`(默认)保持 JSON。state 路径:`~/.xiaobao/`(fallback 读 `~/.openclaw/state/wangxiaobao/`)。
44
-
45
-
46
11
  # 旺小宝录音查询
47
12
 
13
+ > **CRITICAL** —— 跑命令前 MUST 先用 Read tool 读取 [`../wangxiaobao-shared/SKILL.md`](../wangxiaobao-shared/SKILL.md)(一份共享文档讲清安装 / 登录 / 选项目 / 错误码 / 输出协议等所有 xiaobao-cli 命令通用的前置约定)。
14
+
48
15
  只读探查录音。**没有副作用**——不写 wiki / 不推游标,
49
16
  所以用错也没成本,鼓励放心调用。
50
17
 
51
- > 想要"完整入库流程"(拉全部文本 → 写 wiki/raw/audio/ → 推进游标),切到
52
- > `wangxiaobao-audio-wiki` skill。本 skill 跟那个的边界就是**有没有副作用**。
53
-
54
18
  ## 执行前必读
55
19
 
56
- - 必须有有效 token:先调 `xiaobao_whoami`,未登录 / 过期就调 `xiaobao_authorize`
57
- - **必须有激活项目**:tenant/project 由 tool 内部从
58
- `~/.openclaw/state/wangxiaobao/active-project.json` 读,调用方不需要传任何
59
- tenant/project 参数。如果 tool 返回 `error: 'NO_ACTIVE_PROJECT'`,
20
+ - 必须有有效 token:先调 `xiaobao-cli auth whoami`,未登录 / 过期就调 `xiaobao-cli auth login`
21
+ - **必须有激活项目**:tenant/project 由 命令内部从
22
+ `~/.xiaobao/active-project.json` 读,调用方不需要传任何
23
+ tenant/project 参数。如果 命令 返回 `error: 'NO_ACTIVE_PROJECT'`,
60
24
  让用户跑 `wangxiaobao-switch-project` skill 后再重试
61
25
  - **绝不要**写文件、推游标。即使用户随口说"顺便存一下"也要拦:
62
26
  那是 `wangxiaobao-audio-wiki` 的事,本 skill 一旦写盘就破坏了"只读"承诺
63
27
  - LocalDateTime 字段格式:`yyyy-MM-dd HH:mm:ss`(**空格**分隔,不带时区)。
64
- pierre 框架的 Jackson 反序列化只认这个格式;带 `T` 或 `+08:00` 会被 plugin
28
+ pierre 框架的 Jackson 反序列化只认这个格式;带 `T` 或 `+08:00` 会被 CLI
65
29
  容错处理,但传空格形式最稳
66
30
 
67
31
  ---
68
32
 
69
33
  ## 快速索引:意图 → 工具
70
34
 
71
- | 用户意图 | plugin tool | 关键参数 |
35
+ | 用户意图 | 命令 | 关键参数 |
72
36
  | ---------------------- | -------------------------- | ----------------------------------------- |
73
- | 列某时段所有录音元数据 | `xiaobao_list_audio` | `fromDate` / `toDate` |
74
- | 列某销售的录音 | `xiaobao_list_audio` | `+ userId: <uid>`(多个销售用 `userIdList`) |
75
- | 反查销售 user-id | `xiaobao_list_consultants` | 无参(拿当前用户授权范围内的所有顾问) |
76
- | 看总数 / 估个量 | `xiaobao_list_audio` | `page: 1, size: 1`,只读 `total` |
77
- | 翻页继续看 | `xiaobao_list_audio` | `page += 1` |
78
- | 看某条录音转录预览 | `xiaobao_get_audio_text` | `audioId`(从 list_audio 结果拿) |
79
-
80
- **tool 参数里没有** `tenantId` / `projectId` —— plugin 内部从激活项目状态读。
37
+ | 列某时段所有录音元数据 | `xiaobao-cli audio list` | `--from` / `--to` |
38
+ | 列某销售的录音 | `xiaobao-cli audio list` | `+ --user-id <uid>`(多个销售用 `--user-id-list`) |
39
+ | 反查销售 user-id | `xiaobao-cli consultant list` | 无参(拿当前用户授权范围内的所有顾问) |
40
+ | 看总数 / 估个量 | `xiaobao-cli audio list` | `--page 1 --size 1`,只读 `total` |
41
+ | 翻页继续看 | `xiaobao-cli audio list` | `--page` 递增 |
42
+ | 看某条录音转录预览 | `xiaobao-cli audio text` | `--audio-id`(从 list_audio 结果拿) |
43
+
44
+ **命令 参数里没有** `tenantId` / `projectId` —— CLI 内部从激活项目状态读。
81
45
  如果还没激活过项目,跑 `wangxiaobao-switch-project` skill 先设一次。
82
46
 
83
47
  ---
@@ -100,33 +64,33 @@ description: |
100
64
 
101
65
  ### 2. 分页:page 从 **1** 开始
102
66
 
103
- PageParam 默认 `page: 1, size: 10`,传 `page: 0` 上游会报错。读 `total` +
67
+ PageParam 默认 `--page 1 --size 10`,传 `page: 0` 上游会报错。读 `total` +
104
68
  当前 `page * size` 判断是否还有下一页,需要时翻页;浏览概览时第 1 页通常
105
- 够用。`size` 上限 500(schema 已限制),预览展示建议 20。
69
+ 够用。`--size` 上限 500(schema 已限制),预览展示建议 20。
106
70
 
107
71
  ### 3. 按销售筛选:先反查 user-id 再过滤
108
72
 
109
- 不带 `userId` / `userIdList` 就拉**所有人**的录音。要筛某个销售:
73
+ 不带 `--user-id` / `--user-id-list` 就拉**所有人**的录音。要筛某个销售:
110
74
 
111
- - 用户能直接给 user-id(数字)→ 用 `userId: <id>`(单个)或
75
+ - 用户能直接给 user-id(数字)→ 用 `--user-id <id>`(单个)或
112
76
  `userIdList: [<id1>, <id2>, ...]`(多个)
113
- - 只知道名字 → 调 **`xiaobao_list_consultants`**(无参),拿到当前用户
114
- 授权范围内的所有顾问(含 `userName` / `userId`),按名字 match 后取 `userId`
115
- 传给 `xiaobao_list_audio`
77
+ - 只知道名字 → 调 **`xiaobao-cli consultant list`**(无参),拿到当前用户
78
+ 授权范围内的所有顾问(含 `--user-name` / `--user-id`),按名字 match 后取 `--user-id`
79
+ 传给 `xiaobao-cli audio list`
116
80
 
117
81
  > 已弃用做法:"先 list_audio 不带 filter,再扫 content[].saleName 反查"——
118
82
  > 这种 hack 在录音数据稀疏 / 时间窗短时找不到目标销售。**改用
119
- > `xiaobao_list_consultants` 反查**,精确且快。
83
+ > `xiaobao-cli consultant list` 反查**,精确且快。
120
84
 
121
85
  ### 4. 单条文本预览:按需调,不要全量
122
86
 
123
- `xiaobao_get_audio_text` 一次拿一条录音的转录。**只在用户挑了具体某条之
87
+ `xiaobao-cli audio text` 一次拿一条录音的转录。**只在用户挑了具体某条之
124
88
  后再调**,不要 list 完顺手把每条都 get_audio_text 一遍——那是 sync skill
125
89
  的事。一次会话 1-3 条样本预览足够。
126
90
 
127
91
  ### 5. 响应外层结构
128
92
 
129
- `xiaobao_list_audio` 和 `xiaobao_get_audio_text` 都返回 pierre 框架的
93
+ `xiaobao-cli audio list` 和 `xiaobao-cli audio text` 都返回 pierre 框架的
130
94
  `Result<T>` 包:
131
95
 
132
96
  ```jsonc
@@ -139,7 +103,7 @@ PageParam 默认 `page: 1, size: 10`,传 `page: 0` 上游会报错。读 `tota
139
103
 
140
104
  list_audio 里 `data` 又是 `PageResult<AudioPageResp>`:
141
105
  `{ page, size, total, content[] }`,所以**取元数据数组**要写
142
- `resp.data.data.content`(plugin tool 不展开 envelope,原样返回)。
106
+ `resp.data.data.content`(命令 不展开 envelope,原样返回)。
143
107
 
144
108
  ---
145
109
 
@@ -147,14 +111,8 @@ list_audio 里 `data` 又是 `PageResult<AudioPageResp>`:
147
111
 
148
112
  ### 场景 1:列今天所有录音
149
113
 
150
- ```jsonc
151
- // xiaobao_list_audio
152
- {
153
- "fromDate": "2026-05-12 00:00:00",
154
- "toDate": "2026-05-12 23:59:59",
155
- "page": 1,
156
- "size": 20
157
- }
114
+ ```bash
115
+ xiaobao-cli audio list --from "2026-05-12 00:00:00" --to "2026-05-12 23:59:59" --page 1 --size 20
158
116
  ```
159
117
 
160
118
  渲染给用户(按 startTime desc,audioId 必须显示——后面取文本要用):
@@ -172,13 +130,8 @@ list_audio 里 `data` 又是 `PageResult<AudioPageResp>`:
172
130
 
173
131
  只关心 `total`,第 1 页就够:
174
132
 
175
- ```jsonc
176
- {
177
- "fromDate": "2026-05-10 00:00:00",
178
- "toDate": "2026-05-12 00:00:00",
179
- "page": 1,
180
- "size": 1
181
- }
133
+ ```bash
134
+ xiaobao-cli audio list --from "2026-05-10 00:00:00" --to "2026-05-12 00:00:00" --page 1 --size 1
182
135
  ```
183
136
 
184
137
  回复:「周末两天共 23 条录音,主要集中在周日下午」。
@@ -187,9 +140,8 @@ list_audio 里 `data` 又是 `PageResult<AudioPageResp>`:
187
140
 
188
141
  用户从场景 1 列表里挑了序号 1(audioId=12345):
189
142
 
190
- ```jsonc
191
- // xiaobao_get_audio_text
192
- { "audioId": 12345 }
143
+ ```bash
144
+ xiaobao-cli audio text 12345
193
145
  ```
194
146
 
195
147
  渲染:
@@ -209,24 +161,17 @@ audioId 12345 · 张三 · 2026-05-12 10:23:11(12 分钟)
209
161
 
210
162
  用户说"张三本周打了几个"——只知道名字:
211
163
 
212
- 1. 调 `xiaobao_list_consultants`(无参)→ 拿到 `[{ userId, userName, ... }, ...]`
213
- 2. 在结果里找 `userName === "张三"` 的 `userId`
214
- 3. 调 `xiaobao_list_audio` 带 `userId: <张三的 userId>` + 本周时间窗
164
+ 1. 调 `xiaobao-cli consultant list`(无参)→ 拿到 `[{ userId, userName, ... }, ...]`
165
+ 2. 在结果里找 `userName === "张三"` 的 `--user-id`
166
+ 3. 调 `xiaobao-cli audio list` 带 `--user-id <张三的 userId>` + 本周时间窗
215
167
 
216
- ```jsonc
217
- // step 1
218
- // xiaobao_list_consultants
219
- {}
168
+ ```bash
169
+ # step 1: 反查张三的 user-id
170
+ xiaobao-cli consultant list
220
171
 
221
- // step 2 ——找到 张三 的 userId = 100
222
- // step 3
223
- // xiaobao_list_audio
224
- {
225
- "fromDate": "2026-05-06 00:00:00",
226
- "toDate": "2026-05-13 00:00:00",
227
- "userId": 100,
228
- "page": 1, "size": 20
229
- }
172
+ # step 2: 假设找到张三的 userId = 100
173
+ # step 3: 用 --user-id 过滤
174
+ xiaobao-cli audio list --from "2026-05-06 00:00:00" --to "2026-05-13 00:00:00" --user-id 100 --page 1 --size 20
230
175
  ```
231
176
 
232
177
  如果 list-consultants 里**找不到张三**——说明当前登录用户没权限看张三
@@ -239,14 +184,14 @@ audioId 12345 · 张三 · 2026-05-12 10:23:11(12 分钟)
239
184
 
240
185
  - **`code: "400"` / `msg: "消息转换异常"`** — fromDate/toDate 格式不对。
241
186
  必须 `yyyy-MM-dd HH:mm:ss` 空格分隔,不带时区。
242
- - **401 / token 过期** — 调 `xiaobao_authorize { force: true }` 重登,重试一次。
187
+ - **401 / token 过期** — 调 `xiaobao-cli auth login --force` 重登,重试一次。
243
188
  - **`total: 0`** — 时间窗口里确实没数据,或激活的租户/项目错了。
244
189
  让用户跑 `wangxiaobao-switch-project` skill 确认或重新激活项目。
245
190
  - **`error: 'NO_ACTIVE_PROJECT'`** — 还没设过激活项目。让用户跑
246
191
  `wangxiaobao-switch-project` skill 后重试本 skill。
247
- - **翻页拿不到下一页** — 检查 `page` 是不是从 1 开始(不是 0),
192
+ - **翻页拿不到下一页** — 检查 `--page` 是不是从 1 开始(不是 0),
248
193
  `page * size > total` 时已经到末页。
249
- - **`saleName` 是空的** — 上游 user info 缺失,可以只用 `userId` 标识,
194
+ - **`saleName` 是空的** — 上游 user info 缺失,可以只用 `--user-id` 标识,
250
195
  跟用户说"该录音的销售信息上游缺失"。
251
196
  - **想"批量转文本看看"** — 拦住:批量取文本是 `wangxiaobao-audio-wiki`
252
197
  的事(带写文件 + 推游标),本 skill 设计上每次会话最多预览 1-3 条。
@@ -1,82 +1,42 @@
1
1
  ---
2
2
  name: wangxiaobao-audio-wiki
3
- description: |
4
- 旺小宝录音入库技能。把指定时间窗口的录音同步到本地,按 **项目/顾问/日期/录音** 四层目录归档到 llm-wiki 知识库(`wiki/projects/{projectId}-{projectName}/raw/audio/...`),并引导后续 ingest 阶段提炼出 **顾问画像 / 客户 / 话题 / 话术** 四类 Layer 2 知识页。每个旺小宝项目对应一个独立 wiki 目录,切项目不串味。一站式工作流:sync 阶段拉数据写 raw/,ingest 阶段提炼为可检索的销售知识。
5
-
6
- **当以下情况时使用此 Skill**:
7
- (1) 用户提到"拉录音"、"同步录音"、"旺小宝录音"、"音频归档"、"录音入库"、"建录音知识库"
8
- (2) 用户希望把旺小宝的录音长期沉淀成可检索的销售知识 wiki
9
- (3) 用户给定了时间范围或希望"从上次同步到现在"继续拉取
10
- (4) 用户想为某位置业顾问建立画像、统计客户跟进、整理话术
11
- (5) 用户在 query skill 看完一眼后想"完整存下来 + 提炼成知识"
3
+ version: 0.1.0
4
+ description: "旺小宝录音入库:把指定时间窗的录音同步到本地,按 项目/顾问/日期/录音 四层目录归档到 llm-wiki 知识库(wiki/projects/{projectId}-{projectName}/raw/audio/...),再 ingest 提炼为 顾问画像 / 客户 / 话题 / 话术 四类 Layer 2 知识页。每个旺小宝项目对应一个独立 wiki 目录不串味;一站式 sync→ingest 工作流。底层依赖: xiaobao-cli audio list 拉元数据 + xiaobao-cli audio text 拉文本,配合 Read/Write 工具落盘 + 维护 .cursor 增量游标。何时用:用户说拉录音/同步录音/旺小宝录音/音频归档/录音入库/建录音知识库;要为某位置业顾问建立画像、统计客户跟进、整理话术;要把旺小宝录音长期沉淀成可检索的销售知识 wiki。"
5
+ metadata:
6
+ requires:
7
+ bins: ["xiaobao-cli"]
8
+ cliHelp: "xiaobao-cli audio --help"
12
9
  ---
13
10
 
14
- > **Host-agnostic CLI skill** — 本 skill 假设 `xiaobao-cli` 已装到 PATH
15
- > (`npm i -g @puyinkai/xiaobao-cli` 或 `npx -y @puyinkai/xiaobao-cli`)。
16
- > Agent 通过 shell 工具(Bash / Run / Shell)执行命令、读 **stdout JSON** 消费;
17
- > stderr 是进度/错误提示。退出码 0 = 成功,非 0 = 业务/网络错(错误对象同时打到 stdout 可解析)。
18
- >
19
- > CLI 14 个子命令跟 openclaw-xiaobao plugin 14 个 tool **1:1 等价**,返回 JSON
20
- > 结构完全一致(`{status, ok, data: {...}}`)。skill 里看到的 `resp.data.data.xxx`
21
- > 取数路径直接对 stdout JSON 用 `jq` / `JSON.parse` 即可。
22
- >
23
- > **plugin tool → CLI 命令翻译表(数组参数走逗号分隔)**:
24
- >
25
- > | plugin tool | CLI 命令 |
26
- > | --- | --- |
27
- > | `xiaobao_authorize { force? }` | `xiaobao-cli auth login [--force]` |
28
- > | `xiaobao_whoami` | `xiaobao-cli auth whoami` |
29
- > | `xiaobao_logout` | `xiaobao-cli auth logout` |
30
- > | `xiaobao_list_projects { keyword? }` | `xiaobao-cli project list [--keyword <kw>]` |
31
- > | `xiaobao_switch_project { tenantId, tenantName, projectId, projectName }` | `xiaobao-cli project use --tenant-id ... --tenant-name ... --project-id ... --project-name ...` |
32
- > | `xiaobao_list_consultants` | `xiaobao-cli consultant list` |
33
- > | `xiaobao_list_audio { fromDate, toDate, userId?, userIdList?, page, size }` | `xiaobao-cli audio list --from "..." --to "..." [--user-id ...] [--user-id-list a,b,c] [--page N] [--size N]` |
34
- > | `xiaobao_get_audio_text { audioId }` | `xiaobao-cli audio text <audioId>` |
35
- > | `xiaobao_list_customers { ... }` | `xiaobao-cli customer list [--user-id] [--user-name] [--customer-name] [--customer-phone] [--portrait] [--from] [--to] [--page] [--size]` |
36
- > | `xiaobao_list_visits { ... }` | `xiaobao-cli visit list [--customer-id] [--customer-name] [--from] [--to] [--page] [--size]` |
37
- > | `xiaobao_list_customer_focus { visitIds, customerIds, audioIds, category, classification, fromDate, toDate, ... }` | `xiaobao-cli focus list [--visit-ids a,b] [--customer-ids a,b] [--audio-ids a,b] [--category ...] [--classification ...] [--from ...] [--to ...]` |
38
- > | `xiaobao_list_customer_resistance { ... }` | `xiaobao-cli resistance list [同 focus]` |
39
- > | `xiaobao_quick_qa { prompt, threadId? }` | `xiaobao-cli qa "<prompt>" [--thread-id ...]` |
40
- > | `xiaobao_api { method, path, query, body, headers }` | `xiaobao-cli api <METHOD> <PATH> [--query k=v] [--body '<json>'] [--headers k=v]` |
41
- >
42
- > 用 `--format toon` 切到 TOON(uniform 数组省 30-50% token,LLM 上下文优化);
43
- > 用 `--format json`(默认)保持 JSON。state 路径:`~/.xiaobao/`(fallback 读 `~/.openclaw/state/wangxiaobao/`)。
44
-
45
-
46
11
  # 旺小宝录音入库
47
12
 
13
+ > **CRITICAL** —— 跑命令前 MUST 先用 Read tool 读取 [`../wangxiaobao-shared/SKILL.md`](../wangxiaobao-shared/SKILL.md)(一份共享文档讲清安装 / 登录 / 选项目 / 错误码 / 输出协议等所有 xiaobao-cli 命令通用的前置约定)。
14
+
48
15
  完整工作流:**登录 → 选项目 → 翻页拉元数据 → 逐条取文本 → 按
49
16
  **项目 / 顾问 / 日期 / 录音** 分层写 wiki → 推进游标 → ingest 提炼为
50
17
  Layer 2 知识页**。
51
18
 
52
- > 每个旺小宝项目落到独立的 `wiki/projects/{projectId}-{projectName}/`
53
- > 目录,**切项目不会污染另一个项目的录音/顾问画像/客户档案**。同 cwd 下
54
- > 可并存多个项目的 wiki。
55
-
56
- > 只想看一眼录音元数据列表(不写文件、不推游标)请用 `wangxiaobao-audio-query`
57
- > skill。本 skill 跟它的边界是 **写入 vs 只读**。
58
-
59
19
  ## 上游接口
60
20
 
61
- | 接口 | plugin tool | 用途 |
21
+ | 接口 | 命令 | 用途 |
62
22
  | --- | --- | --- |
63
- | `POST /ai-open/audio/page` | `xiaobao_list_audio` | 分页列录音元数据 |
64
- | `GET /ai-open/audio/text/{audioId}` | `xiaobao_get_audio_text` | 单条录音转录文本 + 说话人占比 |
23
+ | `POST /ai-open/audio/page` | `xiaobao-cli audio list` | 分页列录音元数据 |
24
+ | `GET /ai-open/audio/text/{audioId}` | `xiaobao-cli audio text` | 单条录音转录文本 + 说话人占比 |
65
25
 
66
26
  ## 执行前必读
67
27
 
68
- 1. **登录**:先调 `xiaobao_whoami`;未登录 / 过期就调 `xiaobao_authorize`。
69
- 2. **激活项目**:tool 内部从 `~/.openclaw/state/wangxiaobao/active-project.json`
70
- 读 tenant/project。如果任何 tool 返回 `error: 'NO_ACTIVE_PROJECT'`,
28
+ 1. **登录**:先调 `xiaobao-cli auth whoami`;未登录 / 过期就调 `xiaobao-cli auth login`。
29
+ 2. **激活项目**:命令内部从 `~/.xiaobao/active-project.json`
30
+ 读 tenant/project。如果任何 命令 返回 `error: 'NO_ACTIVE_PROJECT'`,
71
31
  先走 `wangxiaobao-switch-project` skill 让用户选。
72
32
  3. **拿到 projectId / projectName**(写 wiki 路径用):调一次
73
- `xiaobao_list_projects` 后定位当前 active-project 对应行,或者直接复用
74
- `xiaobao_switch_project` 上一次的返回值里的 `activeProject` 字段。
33
+ `xiaobao-cli project list` 后定位当前 active-project 对应行,或者直接复用
34
+ `xiaobao-cli project use` 上一次的返回值里的 `activeProject` 字段。
75
35
  4. **wiki 输出目录**:默认 `./wiki/`(cwd 下)。如果用户在 cwd `./.env`
76
36
  配了 `WIKI_DIR`,用 `${WIKI_DIR}/`。raw/ 子目录是 llm-wiki Layer 1,
77
37
  **不可变**。
78
38
  5. **游标变量名**:`WB_SYNC_CURSOR`,**仍写到 cwd `./.env`**(游标是 per-workspace
79
- 的同步进度,不是 plugin 全局状态,跟 active-project 不一样),格式
39
+ 的同步进度,不是 CLI 全局状态,跟 active-project 不一样),格式
80
40
  `yyyy-MM-dd HH:mm:ss`。
81
41
  6. **wiki schema 必读**:业务知识页结构详见
82
42
  [`references/audio-wiki-schema.md`](references/audio-wiki-schema.md)
@@ -88,7 +48,7 @@ Layer 2 知识页**。
88
48
  `yyyy-MM-dd HH:mm:ss`(中间空格分隔,**不是** ISO 的 `T`)。
89
49
 
90
50
  - ✅ `"2026-05-12 12:00:00"`
91
- - ⚠️ `"2026-05-12T12:00:00"`(plugin tool 内部会自动转空格,但 SKILL 里直接传空格更稳)
51
+ - ⚠️ `"2026-05-12T12:00:00"`(命令 内部会自动转空格,但 SKILL 里直接传空格更稳)
92
52
  - ❌ `"2026-05-12T12:00:00+08:00"`(带时区会被剥掉,避免传)
93
53
 
94
54
  ## 工作流
@@ -109,12 +69,12 @@ Layer 2 知识页**。
109
69
  for each window [winStart, winEnd):
110
70
  page = 1 # ⚠️ PageParam 默认从 1 开始
111
71
  loop:
112
- resp = xiaobao_list_audio {
72
+ resp = xiaobao-cli audio list {
113
73
  fromDate: winStart,
114
74
  toDate: winEnd,
115
75
  page,
116
76
  size: 50
117
- # ⚠️ 没有 tenantId / projectId —— plugin 自己从 active-project 读
77
+ # ⚠️ 没有 tenantId / projectId —— CLI 自己从 active-project 读
118
78
  }
119
79
  pageResult = resp.data.data # Result<PageResult<...>> 拨开两层 envelope
120
80
  records = pageResult.content
@@ -124,7 +84,7 @@ for each window [winStart, winEnd):
124
84
  否则 page += 1
125
85
  ```
126
86
 
127
- tool 返回 401 → 调 `xiaobao_authorize { force: true }` 后重试当前窗口
87
+ 命令 返回 401 → 调 `xiaobao-cli auth login --force` 后重试当前窗口
128
88
  (最多重试一次)。
129
89
 
130
90
  ### 第 3 步:逐条取文本
@@ -132,7 +92,7 @@ for each window [winStart, winEnd):
132
92
  每条 record(有 audioId)单独调一次:
133
93
 
134
94
  ```
135
- text = xiaobao_get_audio_text {
95
+ text = xiaobao-cli audio text {
136
96
  audioId: record.audioId
137
97
  # ⚠️ 没有 tenantId / projectId
138
98
  }
@@ -157,12 +117,6 @@ wiki/projects/{projectId}-{projectName}/raw/audio/{userId}-{saleName}/{yyyy-MM-d
157
117
  - 第 2 层 `{yyyy-MM-dd}/` — 录音发生时的日期(从 `record.startTime` 拿)
158
118
  - 第 3 层 `{audioId}.md` — 单条录音
159
119
 
160
- > projectId / projectName 取自 plugin 全局 active-project 状态
161
- > (`~/.openclaw/state/wangxiaobao/active-project.json`,由
162
- > `wangxiaobao-switch-project` skill / `xiaobao_switch_project` tool 写入)。
163
- > 本 skill **不读** cwd `.env` 里的 tenant/project,但 sync 游标
164
- > `WB_SYNC_CURSOR` 仍写在 cwd `.env`。
165
-
166
120
  **frontmatter 必填**:
167
121
 
168
122
  ```yaml
@@ -196,7 +150,7 @@ parent_consultant: "[[consultants/100-张三]]"
196
150
  正文:把 `text.texts[]` 按 "speaker: content" 逐行排好,行首带时间戳。
197
151
 
198
152
  **写入策略**:
199
- - 用 Write tool 创建文件,**同名文件已存在就跳过**(raw 幂等不可变)
153
+ - 用 Write tool 创建文件(Claude Code 内置写文件工具),**同名文件已存在就跳过**(raw 幂等不可变)
200
154
  - 写完一个窗口报"本窗口写入 N 条 / 跳过 M 条同名 / 跳过 K 条无文本"
201
155
 
202
156
  > 完整 frontmatter 字段含义 + saleName sanitize 规则见
@@ -279,7 +233,7 @@ wiki/projects/{projectId}-{projectName}/
279
233
  | "从游标继续" / "继续同步" | from = WB_SYNC_CURSOR, to = now |
280
234
  | "重跑某天" | 临时用指定窗口,**不**回退游标 |
281
235
  | "干跑 / dry-run" | 走前 3 步但跳过第 4-5 步的写盘和游标更新 |
282
- | "只看某个销售的" | 先调 `xiaobao_list_consultants` 反查 user-id,再 list_audio 带 `userId: <sale-user-id>` |
236
+ | "只看某个销售的" | 先调 `xiaobao-cli consultant list` 反查 user-id,再 list_audio 带 `--user-id <sale-user-id>` |
283
237
  | "把张三本周的录音存下来" | 同上 + 时间窗口限定本周 |
284
238
  | "ingest 录音建知识库" | 跳到第 7 步(前提:raw/ 已经有数据) |
285
239
  | "拉完顺便 ingest" | 走完整 7 步(sync + ingest 一站式) |
@@ -289,8 +243,8 @@ wiki/projects/{projectId}-{projectName}/
289
243
  ## 完整链路
290
244
 
291
245
  ```
292
- 1. xiaobao_authorize — 拿到 token
293
- 2. wangxiaobao-switch-project — 选好租户/项目,写 ~/.openclaw/state/wangxiaobao/active-project.json
246
+ 1. xiaobao-cli auth login — 拿到 token
247
+ 2. wangxiaobao-switch-project — 选好租户/项目,写 ~/.xiaobao/active-project.json
294
248
  3. 本 skill - sync 阶段 — 翻页拉 → 逐条取文本 → 按 顾问/日期 写 raw → 推进游标
295
249
  4. 本 skill - ingest 阶段 — 读 raw/ → 提炼 4 层 Layer 2 知识页 → 更新 index/log
296
250
  ```
@@ -305,9 +259,9 @@ wiki/projects/{projectId}-{projectName}/
305
259
  - Token 有效期由 phoenix 端 client 配置决定(默认 1h access + 30d refresh)。
306
260
  - 游标为空时默认从 7 天前开始。
307
261
  - 首次跑或拉历史范围大时建议先 dry-run。
308
- - API 路径已封装在 plugin tool 里,**不要**用 `xiaobao_api` 自己拼
262
+ - API 路径已封装在 命令 里,**不要**用 `xiaobao-cli api` 自己拼
309
263
  `/audio/page` / `/audio/text/{id}`。
310
- - 激活项目状态在 `~/.openclaw/state/wangxiaobao/active-project.json`(plugin
264
+ - 激活项目状态在 `~/.xiaobao/active-project.json`(CLI
311
265
  全局单份,跟 cwd 无关);游标 `WB_SYNC_CURSOR` 在 cwd `.env`(per-workspace)。
312
266
  - `wiki/projects/.../raw/audio/` 是 Layer 1 不可变,**不要**回写、改名、删除。
313
267
  - **批量取文本要节制并发**:一个窗口可能几十条录音,串行调(不要 parallel
@@ -317,13 +271,13 @@ wiki/projects/{projectId}-{projectName}/
317
271
 
318
272
  ## 常见错误与排查
319
273
 
320
- - **`xiaobao_list_audio` 返回 401** — token 过期 → `xiaobao_authorize { force: true }` 重登
321
- - **`xiaobao_get_audio_text` 返回 `data: null` / `texts: []`** — 转录未就绪 / 录音异常 → 跳过该条
274
+ - **`xiaobao-cli audio list` 返回 401** — token 过期 → `xiaobao-cli auth login --force` 重登
275
+ - **`xiaobao-cli audio text` 返回 `data: null` / `texts: []`** — 转录未就绪 / 录音异常 → 跳过该条
322
276
  - **page 翻不动 / 重复返回同一页** — 检查 page 是否从 1 开始;`total = 0` 直接结束窗口
323
277
  - **fromDate/toDate 报参数错** — 用空格分隔的 `yyyy-MM-dd HH:mm:ss`,不带时区
324
278
  - **游标没推进** — 窗口中途报错被跳过 → 查哪个窗口失败、修复后从上次成功窗口的末尾重跑
325
279
  - **`error: 'NO_ACTIVE_PROJECT'`** — 还没设过激活项目 → 跑
326
- `wangxiaobao-switch-project` skill。注意:plugin active-project 是全局单份,
280
+ `wangxiaobao-switch-project` skill。注意:CLI active-project 是全局单份,
327
281
  跟 cwd 无关;游标 `WB_SYNC_CURSOR` 在 cwd `.env` 里,用户切了 cwd 会丢失游标
328
282
  上下文,需要从指定时间继续或重新拉
329
283
  - **raw/ 写文件冲突** — 同名 audioId 已存在 → 跳过(raw 幂等)