@puyinkai/xiaobao-cli 0.1.6 → 0.1.9

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 (51) hide show
  1. package/dist/{api-client-DWbwtQbl.mjs → api-client-CDNjkArS.mjs} +2 -2
  2. package/dist/{api-JZnABA4y.mjs → api-fsHHWWa3.mjs} +5 -4
  3. package/dist/{audio-BHWH6uhw.mjs → audio-CRSMqaKo.mjs} +2 -2
  4. package/dist/{auth-BpzWivxf.mjs → auth-_3hBh87U.mjs} +3 -3
  5. package/dist/cli.mjs +10 -10
  6. package/dist/{consultant-kV5r8Wj8.mjs → consultant-E33hTGUO.mjs} +1 -1
  7. package/dist/{customer-B2OV8BTR.mjs → customer-DcQ83oKD.mjs} +1 -1
  8. package/dist/{focus-BjWD5PRV.mjs → focus-XMRxnHPw.mjs} +1 -1
  9. package/dist/{list-C30zFN6h.mjs → list-B3nN4pd2.mjs} +1 -1
  10. package/dist/{list-CHububT5.mjs → list-CXquxzex.mjs} +1 -1
  11. package/dist/{list-FM3vkQJa.mjs → list-Cc4zcaNz.mjs} +1 -1
  12. package/dist/{list-VpZkzTEE.mjs → list-CnlQdzPp.mjs} +1 -1
  13. package/dist/{list-Bw8dFf8a.mjs → list-CrMOrnS-.mjs} +1 -1
  14. package/dist/{list-h1nuSJiN.mjs → list-DFdDcHFq.mjs} +1 -1
  15. package/dist/{list-CVMSGK08.mjs → list-dm9YaCG1.mjs} +1 -1
  16. package/dist/login-CUUcjWPd.mjs +156 -0
  17. package/dist/{logout-DVJX8_x6.mjs → logout-DP7REhCd.mjs} +2 -1
  18. package/dist/{project-CJkTgGOq.mjs → project-BD0g4oAW.mjs} +1 -1
  19. package/dist/{qa-CQDRFw-g.mjs → qa-CdMcxCE_.mjs} +1 -1
  20. package/dist/{resistance-Ss-eUZoN.mjs → resistance-6_V6At3T.mjs} +1 -1
  21. package/dist/{text-5q8lIWi8.mjs → text-C3Ap6L5n.mjs} +1 -1
  22. package/dist/{token-store-D2umpIk-.mjs → token-store-CXizLE2_.mjs} +37 -1
  23. package/dist/{visit-CUI2ov-H.mjs → visit-DoeECi8w.mjs} +1 -1
  24. package/dist/{whoami-Uv7GBtGB.mjs → whoami-z-76WaCP.mjs} +2 -2
  25. package/package.json +5 -1
  26. package/skills/wangxiaobao-audio-query/SKILL.md +2 -2
  27. package/skills/wangxiaobao-audio-wiki/SKILL.md +4 -4
  28. package/skills/wangxiaobao-customer-focus-query/SKILL.md +2 -2
  29. package/skills/wangxiaobao-customer-query/SKILL.md +2 -2
  30. package/skills/wangxiaobao-customer-resistance-query/SKILL.md +2 -2
  31. package/skills/wangxiaobao-html-to-pdf/SKILL.md +116 -0
  32. package/skills/wangxiaobao-html-to-pdf/scripts/html2pdf.py +132 -0
  33. package/skills/wangxiaobao-html-to-pdf/scripts/html2pdf_exact.py +92 -0
  34. package/skills/wangxiaobao-project-daily-report/SKILL.md +170 -0
  35. package/skills/wangxiaobao-project-daily-report/_meta.json +9 -0
  36. package/skills/wangxiaobao-project-daily-report/references/report_template.html +308 -0
  37. package/skills/wangxiaobao-project-daily-report/scripts/fetch_data.py +219 -0
  38. package/skills/wangxiaobao-project-daily-report/scripts/generate_report.py +1029 -0
  39. package/skills/wangxiaobao-project-daily-report/scripts/run.py +34 -0
  40. package/skills/wangxiaobao-project-weekly-report/SKILL.md +169 -0
  41. package/skills/wangxiaobao-project-weekly-report/scripts/generate_report.py +308 -0
  42. package/skills/wangxiaobao-project-weekly-report/scripts/html2pdf_fast.py +161 -0
  43. package/skills/wangxiaobao-project-weekly-report/scripts/logo_wangai.png +0 -0
  44. package/skills/wangxiaobao-project-weekly-report/scripts/logo_wangxiaobao.png +0 -0
  45. package/skills/wangxiaobao-project-weekly-report/scripts/prepare_data.py +682 -0
  46. package/skills/wangxiaobao-project-weekly-report/scripts/template.py +925 -0
  47. package/skills/wangxiaobao-quick-qa/SKILL.md +2 -2
  48. package/skills/wangxiaobao-shared/SKILL.md +40 -7
  49. package/skills/wangxiaobao-switch-project/SKILL.md +3 -3
  50. package/skills/wangxiaobao-visit-query/SKILL.md +2 -2
  51. package/dist/login-DtDxISE8.mjs +0 -91
@@ -1,5 +1,5 @@
1
1
  import { r as refreshToken } from "./device-flow-D6JVcEap.mjs";
2
- import { a as writeToken, i as readToken, r as isAccessTokenValid } from "./token-store-D2umpIk-.mjs";
2
+ import { a as isAccessTokenValid, l as writeToken, s as readToken } from "./token-store-CXizLE2_.mjs";
3
3
  //#region src/core/api-client.ts
4
4
  /**
5
5
  * HTTP client for wangxiaobao open API endpoints.
@@ -13,7 +13,7 @@ import { a as writeToken, i as readToken, r as isAccessTokenValid } from "./toke
13
13
  */
14
14
  var NotAuthenticatedError = class extends Error {
15
15
  code = "NOT_AUTHENTICATED";
16
- hint = "运行 `xiaobao-cli auth login` 完成 OAuth device flow 登录。";
16
+ hint = "运行 `xiaobao-cli auth login --no-wait` 发起登录,授权后运行 `xiaobao-cli auth login --resume` 完成。";
17
17
  constructor() {
18
18
  super("当前未登录或 token 已过期");
19
19
  this.name = "NotAuthenticatedError";
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { t as loadActiveProject } from "./project-store-CKVJa54T.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-DSnR4sjk.mjs";
5
5
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
@@ -60,9 +60,10 @@ var api_default = defineCommand({
60
60
  type: "string",
61
61
  description: "自定义 headers,`K1=V1&K2=V2` 或 `K=V,K=V`。与默认注入的 tenant/project headers 合并(同名 key 用户传入优先)"
62
62
  },
63
- "no-default-headers": {
63
+ "default-headers": {
64
64
  type: "boolean",
65
- description: "不要默认注入 X-Tenant-Id / X-Project-Id headers(默认会按当前激活项目自动注入,调 /ai-open/* 开箱即用)"
65
+ default: true,
66
+ description: "默认按当前激活项目自动注入 X-Tenant-Id / X-Project-Id headers(调 /ai-open/* 开箱即用);`--no-default-headers` 关闭注入"
66
67
  },
67
68
  "api-base": { type: "string" },
68
69
  "auth-base": { type: "string" },
@@ -93,7 +94,7 @@ var api_default = defineCommand({
93
94
  }
94
95
  let mergedHeaders;
95
96
  const userHeaders = parseKV(args.headers);
96
- if (!args["no-default-headers"]) {
97
+ if (args["default-headers"]) {
97
98
  const active = await loadActiveProject();
98
99
  if (active) mergedHeaders = {
99
100
  ...tenantProjectHeaders(active),
@@ -6,8 +6,8 @@ var audio_default = defineCommand({
6
6
  description: "录音元数据分页 / 单条文本查询"
7
7
  },
8
8
  subCommands: {
9
- list: () => import("./list-VpZkzTEE.mjs").then((m) => m.default),
10
- text: () => import("./text-5q8lIWi8.mjs").then((m) => m.default)
9
+ list: () => import("./list-CnlQdzPp.mjs").then((m) => m.default),
10
+ text: () => import("./text-C3Ap6L5n.mjs").then((m) => m.default)
11
11
  }
12
12
  });
13
13
  //#endregion
@@ -6,9 +6,9 @@ var auth_default = defineCommand({
6
6
  description: "OAuth 登录 / 身份查询 / 登出(device flow)"
7
7
  },
8
8
  subCommands: {
9
- login: () => import("./login-DtDxISE8.mjs").then((m) => m.default),
10
- whoami: () => import("./whoami-Uv7GBtGB.mjs").then((m) => m.default),
11
- logout: () => import("./logout-DVJX8_x6.mjs").then((m) => m.default)
9
+ login: () => import("./login-CUUcjWPd.mjs").then((m) => m.default),
10
+ whoami: () => import("./whoami-z-76WaCP.mjs").then((m) => m.default),
11
+ logout: () => import("./logout-DP7REhCd.mjs").then((m) => m.default)
12
12
  }
13
13
  });
14
14
  //#endregion
package/dist/cli.mjs CHANGED
@@ -18,16 +18,16 @@ runMain(defineCommand({
18
18
  description: "旺小宝 CLI — host-agnostic 14 tools (openclaw-xiaobao plugin 的 CLI 等价物)"
19
19
  },
20
20
  subCommands: {
21
- auth: () => import("./auth-BpzWivxf.mjs").then((m) => m.default),
22
- project: () => import("./project-CJkTgGOq.mjs").then((m) => m.default),
23
- consultant: () => import("./consultant-kV5r8Wj8.mjs").then((m) => m.default),
24
- audio: () => import("./audio-BHWH6uhw.mjs").then((m) => m.default),
25
- customer: () => import("./customer-B2OV8BTR.mjs").then((m) => m.default),
26
- visit: () => import("./visit-CUI2ov-H.mjs").then((m) => m.default),
27
- focus: () => import("./focus-BjWD5PRV.mjs").then((m) => m.default),
28
- resistance: () => import("./resistance-Ss-eUZoN.mjs").then((m) => m.default),
29
- qa: () => import("./qa-CQDRFw-g.mjs").then((m) => m.default),
30
- api: () => import("./api-JZnABA4y.mjs").then((m) => m.default)
21
+ auth: () => import("./auth-_3hBh87U.mjs").then((m) => m.default),
22
+ project: () => import("./project-BD0g4oAW.mjs").then((m) => m.default),
23
+ consultant: () => import("./consultant-E33hTGUO.mjs").then((m) => m.default),
24
+ audio: () => import("./audio-CRSMqaKo.mjs").then((m) => m.default),
25
+ customer: () => import("./customer-DcQ83oKD.mjs").then((m) => m.default),
26
+ visit: () => import("./visit-DoeECi8w.mjs").then((m) => m.default),
27
+ focus: () => import("./focus-XMRxnHPw.mjs").then((m) => m.default),
28
+ resistance: () => import("./resistance-6_V6At3T.mjs").then((m) => m.default),
29
+ qa: () => import("./qa-CdMcxCE_.mjs").then((m) => m.default),
30
+ api: () => import("./api-fsHHWWa3.mjs").then((m) => m.default)
31
31
  }
32
32
  }));
33
33
  //#endregion
@@ -5,7 +5,7 @@ var consultant_default = defineCommand({
5
5
  name: "consultant",
6
6
  description: "查询有权限的置业顾问列表"
7
7
  },
8
- subCommands: { list: () => import("./list-CVMSGK08.mjs").then((m) => m.default) }
8
+ subCommands: { list: () => import("./list-dm9YaCG1.mjs").then((m) => m.default) }
9
9
  });
10
10
  //#endregion
11
11
  export { consultant_default as default };
@@ -5,7 +5,7 @@ var customer_default = defineCommand({
5
5
  name: "customer",
6
6
  description: "客户画像分页查询"
7
7
  },
8
- subCommands: { list: () => import("./list-h1nuSJiN.mjs").then((m) => m.default) }
8
+ subCommands: { list: () => import("./list-DFdDcHFq.mjs").then((m) => m.default) }
9
9
  });
10
10
  //#endregion
11
11
  export { customer_default as default };
@@ -5,7 +5,7 @@ var focus_default = defineCommand({
5
5
  name: "focus",
6
6
  description: "客户关注点标签分页查询"
7
7
  },
8
- subCommands: { list: () => import("./list-Bw8dFf8a.mjs").then((m) => m.default) }
8
+ subCommands: { list: () => import("./list-CrMOrnS-.mjs").then((m) => m.default) }
9
9
  });
10
10
  //#endregion
11
11
  export { focus_default as default };
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  //#region src/commands/project/list.ts
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CKVJa54T.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-DSnR4sjk.mjs";
5
5
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CKVJa54T.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-DSnR4sjk.mjs";
5
5
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CKVJa54T.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-DSnR4sjk.mjs";
5
5
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CKVJa54T.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-DSnR4sjk.mjs";
5
5
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CKVJa54T.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-DSnR4sjk.mjs";
5
5
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CKVJa54T.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-DSnR4sjk.mjs";
5
5
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
@@ -0,0 +1,156 @@
1
+ import { a as resolveConfig, n as pollForToken, r as refreshToken, t as initiateDeviceAuthorization } from "./device-flow-D6JVcEap.mjs";
2
+ import { a as isAccessTokenValid, c as writePendingAuth, l as writeToken, n as clearPendingAuth, o as readPendingAuth, r as clearToken, s as readToken, t as NoPendingAuthError } from "./token-store-CXizLE2_.mjs";
3
+ import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
4
+ import { defineCommand } from "citty";
5
+ //#region src/commands/auth/login.ts
6
+ /**
7
+ * `xiaobao-cli auth login` — OAuth 2.0 Device Authorization Grant.
8
+ *
9
+ * Three modes:
10
+ *
11
+ * 1. Default (blocking) — cache hit / refresh / else initiate the device
12
+ * flow and synchronously poll until success/denial/expiry. Best for a
13
+ * human running it directly in a terminal.
14
+ *
15
+ * 2. `--no-wait` (non-blocking, agent-friendly split-flow step 1) — cache
16
+ * hit / refresh as usual; otherwise initiate the device flow, persist
17
+ * the device_code locally (~/.xiaobao/pending-auth.json, mode 0600) and
18
+ * return immediately with the verification URL — WITHOUT polling and
19
+ * WITHOUT exposing the credential-grade device_code on stdout. The agent
20
+ * forwards the URL to the user, ends its turn, then runs step 2.
21
+ *
22
+ * 3. `--resume` (split-flow step 2) — read the locally-saved device_code
23
+ * and exchange it for a token; no argument needed. `--device-code <code>`
24
+ * remains as an explicit escape hatch and takes precedence when given.
25
+ */
26
+ var login_default = defineCommand({
27
+ meta: {
28
+ name: "login",
29
+ description: "走 OAuth device flow 登录(--no-wait 非阻塞发起 / --resume 完成)"
30
+ },
31
+ args: {
32
+ force: {
33
+ type: "boolean",
34
+ description: "强制清除缓存并重新发起设备授权流程"
35
+ },
36
+ wait: {
37
+ type: "boolean",
38
+ default: true,
39
+ description: "默认阻塞轮询直到授权完成。`--no-wait` 则发起 device flow 后立即返回 verification_uri 不轮询(device_code 安全存本地,不进 stdout);用户授权后 再用 `auth login --resume` 完成换 token。"
40
+ },
41
+ resume: {
42
+ type: "boolean",
43
+ description: "split-flow 第二步:用上次 `--no-wait` 本地保存的 device_code 换 token,用户在浏览器授权后跑,无需手动传 device_code。"
44
+ },
45
+ "device-code": {
46
+ type: "string",
47
+ description: "(高级 escape hatch)显式传入 device_code 换 token;一般用 `--resume` 即可,CLI 会自动读取本地保存的 device_code。"
48
+ },
49
+ "api-base": {
50
+ type: "string",
51
+ description: "覆盖 API base URL"
52
+ },
53
+ "auth-base": {
54
+ type: "string",
55
+ description: "覆盖 auth base URL"
56
+ },
57
+ format: {
58
+ type: "string",
59
+ default: "json"
60
+ }
61
+ },
62
+ async run({ args }) {
63
+ const config = resolveConfig({
64
+ apiBase: args["api-base"],
65
+ authBase: args["auth-base"]
66
+ });
67
+ const fmt = args.format;
68
+ try {
69
+ if (args.resume || args["device-code"]) {
70
+ let deviceCode;
71
+ let remainingSeconds = 300;
72
+ if (args["device-code"]) deviceCode = String(args["device-code"]);
73
+ else {
74
+ const pending = await readPendingAuth();
75
+ if (!pending) throw new NoPendingAuthError();
76
+ if (pending.expires_at <= Date.now()) {
77
+ await clearPendingAuth();
78
+ throw new NoPendingAuthError("设备授权已过期,device_code 失效");
79
+ }
80
+ deviceCode = pending.device_code;
81
+ remainingSeconds = Math.max(1, Math.ceil((pending.expires_at - Date.now()) / 1e3));
82
+ }
83
+ const saved = await writeToken(await pollForToken(config, deviceCode, 2, remainingSeconds));
84
+ await clearPendingAuth();
85
+ writeResult({
86
+ source: "device-flow",
87
+ expires_at: saved.expires_at,
88
+ scope: saved.scope,
89
+ message: "登录成功,可以正常使用别的命令了"
90
+ }, fmt);
91
+ return;
92
+ }
93
+ if (!args.force) {
94
+ const cached = await readToken();
95
+ if (isAccessTokenValid(cached)) {
96
+ writeResult({
97
+ source: "cache",
98
+ expires_at: cached.expires_at,
99
+ scope: cached.scope
100
+ }, fmt);
101
+ return;
102
+ }
103
+ if (cached?.refresh_token) try {
104
+ const saved = await writeToken(await refreshToken(config, cached.refresh_token));
105
+ writeResult({
106
+ source: "refresh",
107
+ expires_at: saved.expires_at,
108
+ scope: saved.scope
109
+ }, fmt);
110
+ return;
111
+ } catch {
112
+ await clearToken();
113
+ }
114
+ } else await clearToken();
115
+ const init = await initiateDeviceAuthorization(config);
116
+ const verificationUri = init.verification_uri_complete || init.verification_uri;
117
+ const expiresMinutes = Math.max(1, Math.round(init.expires_in / 60));
118
+ const interval = init.interval ?? 5;
119
+ if (!args.wait) {
120
+ await writePendingAuth({
121
+ device_code: init.device_code,
122
+ user_code: init.user_code,
123
+ verification_uri: verificationUri,
124
+ interval,
125
+ expires_at: Date.now() + init.expires_in * 1e3
126
+ });
127
+ writeResult({
128
+ awaiting_authorization: true,
129
+ verification_uri: verificationUri,
130
+ user_code: init.user_code,
131
+ expires_in: init.expires_in,
132
+ interval,
133
+ message: "请在浏览器打开 verification_uri 完成授权,然后跑 `xiaobao-cli auth login --resume` 完成登录。device_code 已安全保存在本地,无需手动传。"
134
+ }, fmt);
135
+ return;
136
+ }
137
+ process.stderr.write(`
138
+ 请在浏览器中打开以下链接完成登录并授权访问你的旺小宝账号:
139
+ \n 🔗 ${verificationUri}\n\n 设备验证码: ${init.user_code}\n 有效期约 ${expiresMinutes} 分钟\n\n等待你在浏览器中确认 ...
140
+ `);
141
+ const saved = await writeToken(await pollForToken(config, init.device_code, interval, init.expires_in));
142
+ await clearPendingAuth();
143
+ writeResult({
144
+ source: "device-flow",
145
+ expires_at: saved.expires_at,
146
+ scope: saved.scope,
147
+ message: "登录成功,token 已缓存到 ~/.xiaobao/token.json"
148
+ }, fmt);
149
+ } catch (err) {
150
+ writeError(err, fmt);
151
+ process.exit(1);
152
+ }
153
+ }
154
+ });
155
+ //#endregion
156
+ export { login_default as default };
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig, i as revokeRefreshToken } from "./device-flow-D6JVcEap.mjs";
2
- import { i as readToken, t as clearToken } from "./token-store-D2umpIk-.mjs";
2
+ import { n as clearPendingAuth, r as clearToken, s as readToken } from "./token-store-CXizLE2_.mjs";
3
3
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  //#region src/commands/auth/logout.ts
@@ -33,6 +33,7 @@ var logout_default = defineCommand({
33
33
  let revoked = false;
34
34
  if (cached?.refresh_token) revoked = await revokeRefreshToken(config, cached.refresh_token);
35
35
  await clearToken();
36
+ await clearPendingAuth();
36
37
  writeResult({
37
38
  message: "Logged out",
38
39
  remote_revoked: revoked,
@@ -6,7 +6,7 @@ var project_default = defineCommand({
6
6
  description: "查询 / 切换激活项目"
7
7
  },
8
8
  subCommands: {
9
- list: () => import("./list-C30zFN6h.mjs").then((m) => m.default),
9
+ list: () => import("./list-B3nN4pd2.mjs").then((m) => m.default),
10
10
  use: () => import("./use-BZES4FZC.mjs").then((m) => m.default),
11
11
  current: () => import("./current-CfDExInT.mjs").then((m) => m.default)
12
12
  }
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CKVJa54T.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-DSnR4sjk.mjs";
5
5
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
@@ -5,7 +5,7 @@ var resistance_default = defineCommand({
5
5
  name: "resistance",
6
6
  description: "客户抗性点标签分页查询"
7
7
  },
8
- subCommands: { list: () => import("./list-FM3vkQJa.mjs").then((m) => m.default) }
8
+ subCommands: { list: () => import("./list-Cc4zcaNz.mjs").then((m) => m.default) }
9
9
  });
10
10
  //#endregion
11
11
  export { resistance_default as default };
@@ -1,5 +1,5 @@
1
1
  import { a as resolveConfig } from "./device-flow-D6JVcEap.mjs";
2
- import { t as xbApiFetch } from "./api-client-DWbwtQbl.mjs";
2
+ import { t as xbApiFetch } from "./api-client-CDNjkArS.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CKVJa54T.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-DSnR4sjk.mjs";
5
5
  import { n as writeResult, t as writeError } from "./format-yjmxrbNm.mjs";
@@ -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
  */
@@ -85,5 +86,40 @@ function decodeJwtPayload(jwt) {
85
86
  return null;
86
87
  }
87
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
+ };
88
124
  //#endregion
89
- 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 };
@@ -5,7 +5,7 @@ var visit_default = defineCommand({
5
5
  name: "visit",
6
6
  description: "客户来访分页查询"
7
7
  },
8
- subCommands: { list: () => import("./list-CHububT5.mjs").then((m) => m.default) }
8
+ subCommands: { list: () => import("./list-CXquxzex.mjs").then((m) => m.default) }
9
9
  });
10
10
  //#endregion
11
11
  export { visit_default as default };
@@ -1,4 +1,4 @@
1
- import { i as readToken, n as decodeJwtPayload } from "./token-store-D2umpIk-.mjs";
1
+ import { i as decodeJwtPayload, s as readToken } from "./token-store-CXizLE2_.mjs";
2
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
@@ -23,7 +23,7 @@ var whoami_default = defineCommand({
23
23
  if (!token) {
24
24
  writeResult({
25
25
  logged_in: false,
26
- message: "No token cached. Run `xiaobao-cli auth login` to log in."
26
+ message: "No token cached. Run `xiaobao-cli auth login --no-wait` then `xiaobao-cli auth login --resume` to log in."
27
27
  }, args.format);
28
28
  return;
29
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puyinkai/xiaobao-cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "旺小宝 CLI — host-agnostic 14 tools (openclaw-xiaobao plugin 的 CLI 等价物)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,10 @@
10
10
  "dist/**/*.mjs",
11
11
  "dist/**/*.d.mts",
12
12
  "skills/**/*.md",
13
+ "skills/**/*.py",
14
+ "skills/**/*.html",
15
+ "skills/**/*.png",
16
+ "skills/**/*.json",
13
17
  "README.md"
14
18
  ],
15
19
  "engines": {
@@ -17,7 +17,7 @@ metadata:
17
17
 
18
18
  ## 执行前必读
19
19
 
20
- - 必须有有效 token:先调 `xiaobao-cli auth whoami`,未登录 / 过期就调 `xiaobao-cli auth login`
20
+ - 必须有有效 token:先调 `xiaobao-cli auth whoami`,未登录 / 过期就走 `auth login --no-wait` split-flow(见 wangxiaobao-shared)
21
21
  - **必须有激活项目**:tenant/project 由 命令内部从
22
22
  `~/.xiaobao/active-project.json` 读,调用方不需要传任何
23
23
  tenant/project 参数。如果 命令 返回 `error: 'NO_ACTIVE_PROJECT'`,
@@ -184,7 +184,7 @@ xiaobao-cli audio list --from "2026-05-06 00:00:00" --to "2026-05-13 00:00:00" -
184
184
 
185
185
  - **`code: "400"` / `msg: "消息转换异常"`** — fromDate/toDate 格式不对。
186
186
  必须 `yyyy-MM-dd HH:mm:ss` 空格分隔,不带时区。
187
- - **401 / token 过期** — `xiaobao-cli auth login --force` 重登,重试一次。
187
+ - **401 / token 过期** — `auth login --no-wait --force` split-flow 重登,重试一次。
188
188
  - **`total: 0`** — 时间窗口里确实没数据,或激活的租户/项目错了。
189
189
  让用户跑 `wangxiaobao-switch-project` skill 确认或重新激活项目。
190
190
  - **`error: 'NO_ACTIVE_PROJECT'`** — 还没设过激活项目。让用户跑
@@ -25,7 +25,7 @@ Layer 2 知识页**。
25
25
 
26
26
  ## 执行前必读
27
27
 
28
- 1. **登录**:先调 `xiaobao-cli auth whoami`;未登录 / 过期就调 `xiaobao-cli auth login`。
28
+ 1. **登录**:先调 `xiaobao-cli auth whoami`;未登录 / 过期就走 `auth login --no-wait` split-flow(见 wangxiaobao-shared)。
29
29
  2. **激活项目**:命令内部从 `~/.xiaobao/active-project.json`
30
30
  读 tenant/project。如果任何 命令 返回 `error: 'NO_ACTIVE_PROJECT'`,
31
31
  先走 `wangxiaobao-switch-project` skill 让用户选。
@@ -84,7 +84,7 @@ for each window [winStart, winEnd):
84
84
  否则 page += 1
85
85
  ```
86
86
 
87
- 若 命令 返回 401 → `xiaobao-cli auth login --force` 后重试当前窗口
87
+ 若 命令 返回 401 → `auth login --no-wait --force` split-flow 重登后重试当前窗口
88
88
  (最多重试一次)。
89
89
 
90
90
  ### 第 3 步:逐条取文本
@@ -243,7 +243,7 @@ wiki/projects/{projectId}-{projectName}/
243
243
  ## 完整链路
244
244
 
245
245
  ```
246
- 1. xiaobao-cli auth login — 拿到 token
246
+ 1. xiaobao-cli auth login --no-wait → --resume — 拿到 token
247
247
  2. wangxiaobao-switch-project — 选好租户/项目,写 ~/.xiaobao/active-project.json
248
248
  3. 本 skill - sync 阶段 — 翻页拉 → 逐条取文本 → 按 顾问/日期 写 raw → 推进游标
249
249
  4. 本 skill - ingest 阶段 — 读 raw/ → 提炼 4 层 Layer 2 知识页 → 更新 index/log
@@ -271,7 +271,7 @@ wiki/projects/{projectId}-{projectName}/
271
271
 
272
272
  ## 常见错误与排查
273
273
 
274
- - **`xiaobao-cli audio list` 返回 401** — token 过期 → `xiaobao-cli auth login --force` 重登
274
+ - **`xiaobao-cli audio list` 返回 401** — token 过期 → `auth login --no-wait --force` split-flow 重登
275
275
  - **`xiaobao-cli audio text` 返回 `data: null` / `texts: []`** — 转录未就绪 / 录音异常 → 跳过该条
276
276
  - **page 翻不动 / 重复返回同一页** — 检查 page 是否从 1 开始;`total = 0` 直接结束窗口
277
277
  - **fromDate/toDate 报参数错** — 用空格分隔的 `yyyy-MM-dd HH:mm:ss`,不带时区
@@ -17,7 +17,7 @@ metadata:
17
17
 
18
18
  ## 执行前必读
19
19
 
20
- - 必须有有效 token:先调 `xiaobao-cli auth whoami`;未登录就 `xiaobao-cli auth login`
20
+ - 必须有有效 token:先调 `xiaobao-cli auth whoami`;未登录就走 `auth login --no-wait` split-flow(见 wangxiaobao-shared)
21
21
  - **必须有激活项目**:命令内部自动读,缺失返回 `NO_ACTIVE_PROJECT`
22
22
  - **数据权限隔离**:按当前用户授权可见顾问范围过滤(普通顾问看自己 /
23
23
  团队长看团队 / 项目管理员看全项目)
@@ -156,7 +156,7 @@ xiaobao-cli focus list --audio-ids 507494047338729472 --page 1 --size 50
156
156
  ## 常见错误与排查
157
157
 
158
158
  - **`error: 'NO_ACTIVE_PROJECT'`** — 跑 `wangxiaobao-switch-project` skill
159
- - **401 / token 过期** — `xiaobao-cli auth login --force` 重登
159
+ - **401 / token 过期** — `auth login --no-wait --force` split-flow 重登
160
160
  - **`total: 0`** — 过滤条件没匹配 / 该时段没数据 / 当前用户授权范围内没匹配的接待顾问
161
161
  - **传入的 ID 找不到对应数据** — 数据权限隔离把不属于授权顾问的 tag 过滤掉了;
162
162
  这是预期行为,不是 bug
@@ -17,7 +17,7 @@ metadata:
17
17
 
18
18
  ## 执行前必读
19
19
 
20
- - 必须有有效 token:先调 `xiaobao-cli auth whoami`;未登录就 `xiaobao-cli auth login`
20
+ - 必须有有效 token:先调 `xiaobao-cli auth whoami`;未登录就走 `auth login --no-wait` split-flow(见 wangxiaobao-shared)
21
21
  - **必须有激活项目**:命令内部自动读,如果返回 `NO_ACTIVE_PROJECT` 就跑
22
22
  `wangxiaobao-switch-project` skill
23
23
  - **数据权限隔离**:后端按"当前用户授权可见的顾问范围"过滤(普通顾问看自己 /
@@ -158,7 +158,7 @@ xiaobao-cli customer list --page 1 --size 1
158
158
  ## 常见错误与排查
159
159
 
160
160
  - **`error: 'NO_ACTIVE_PROJECT'`** — 跑 `wangxiaobao-switch-project` skill
161
- - **401 / token 过期** — `xiaobao-cli auth login --force` 重登
161
+ - **401 / token 过期** — `auth login --no-wait --force` split-flow 重登
162
162
  - **`total: 0`** — 时间窗口 / 过滤条件没匹配到,**或**当前用户授权范围为空
163
163
  (新人顾问刚入职 / 团队成员被移走等)
164
164
  - **顾问名找不到** — 不在当前用户授权可见范围内;不要硬调 list_customers 试
@@ -18,7 +18,7 @@ metadata:
18
18
 
19
19
  ## 执行前必读
20
20
 
21
- - 必须有有效 token:先调 `xiaobao-cli auth whoami`;未登录就 `xiaobao-cli auth login`
21
+ - 必须有有效 token:先调 `xiaobao-cli auth whoami`;未登录就走 `auth login --no-wait` split-flow(见 wangxiaobao-shared)
22
22
  - **必须有激活项目**:命令内部自动读,缺失返回 `NO_ACTIVE_PROJECT`
23
23
  - **数据权限隔离**:按当前用户授权可见顾问范围过滤(普通顾问看自己 /
24
24
  团队长看团队 / 项目管理员看全项目)
@@ -150,7 +150,7 @@ xiaobao-cli resistance list --category 户型 --from "2026-05-01 00:00:00" --to
150
150
  ## 常见错误与排查
151
151
 
152
152
  - **`error: 'NO_ACTIVE_PROJECT'`** — 跑 `wangxiaobao-switch-project` skill
153
- - **401 / token 过期** — `xiaobao-cli auth login --force` 重登
153
+ - **401 / token 过期** — `auth login --no-wait --force` split-flow 重登
154
154
  - **`total: 0`** — 过滤条件没匹配 / 该时段没数据 / 当前用户授权范围内没匹配的接待顾问
155
155
  - **抗性点 vs 关注点**:跟 LLM 强调"抗性"是负面(疑虑/反对),"关注"是中性偏正
156
156
  (主动关心);用户说"嫌弃 / 担心 / 不满"走抗性,"在意 / 关心 / 偏好"走关注