@puyinkai/xiaobao-cli 0.4.0 → 0.6.0

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 (83) hide show
  1. package/README.md +13 -0
  2. package/dist/add-CCuWFy1O.mjs +21 -0
  3. package/dist/{admin-BzMFazEa.mjs → admin-BOL_otwc.mjs} +2 -2
  4. package/dist/analyze-mjHjOnz1.mjs +72 -0
  5. package/dist/{api-ChuNquZJ.mjs → api-BaX_r5S5.mjs} +3 -3
  6. package/dist/{api-client-ifJHc7hu.mjs → api-client-D3sCv6L3.mjs} +1 -1
  7. package/dist/{audio-CeNKUqeU.mjs → audio-CDt1rpEL.mjs} +3 -2
  8. package/dist/{auth-CdWYrfte.mjs → auth-sQwZSTNA.mjs} +4 -3
  9. package/dist/beautx-DytYVFvT.mjs +16 -0
  10. package/dist/cli.mjs +18 -13
  11. package/dist/config-GuXMTecJ.mjs +11 -0
  12. package/dist/{consultant-CO9zaJ7I.mjs → consultant-elv5arLG.mjs} +1 -1
  13. package/dist/{current-BYpcshKV.mjs → current-ABh6R3vp.mjs} +1 -1
  14. package/dist/{customer-COMf1yZb.mjs → customer-CZ-nG9ku.mjs} +1 -1
  15. package/dist/date-BcQMKhIT.mjs +21 -0
  16. package/dist/deal-analysis-Dqz34gLw.mjs +10 -0
  17. package/dist/{device-flow-DC1kqcmR.mjs → device-flow-j10PFOQL.mjs} +42 -31
  18. package/dist/diff-8I1_L-nU.mjs +21 -0
  19. package/dist/{doc-NfMyo-fF.mjs → doc-CJT7BuhU.mjs} +3 -3
  20. package/dist/{doc-content-BKaZ016e.mjs → doc-content-_SCykNvs.mjs} +3 -3
  21. package/dist/{docs-CheVzIbW.mjs → docs-BHtwTAJ4.mjs} +3 -3
  22. package/dist/fields-BRE3oWMD.mjs +54 -0
  23. package/dist/{focus-AWSxlbBZ.mjs → focus-C4egr05X.mjs} +1 -1
  24. package/dist/follow-LwYQd9Bv.mjs +14 -0
  25. package/dist/{format-HWx3JWdS.mjs → format-DfVypHID.mjs} +1 -1
  26. package/dist/get-CbovJWSq.mjs +66 -0
  27. package/dist/{kb-DkSAvuBG.mjs → kb-CyPqbezN.mjs} +4 -4
  28. package/dist/last-7-days-VKMMIzDz.mjs +5 -0
  29. package/dist/{list-BbU4R0_z.mjs → list-2qqSxkDM.mjs} +3 -3
  30. package/dist/{list-CMFp28Dm.mjs → list-CT4t_FJs.mjs} +4 -4
  31. package/dist/{list-Czym-Sla.mjs → list-Ca5uJXDH.mjs} +3 -3
  32. package/dist/{list-bBAaEvT7.mjs → list-CiNgcIXS.mjs} +4 -4
  33. package/dist/{list-BxWqQllQ2.mjs → list-DWv6toPT.mjs} +4 -4
  34. package/dist/list-DXOlqJog.mjs +76 -0
  35. package/dist/{list-D7Fmqk0G.mjs → list-FQpZk3vH.mjs} +4 -4
  36. package/dist/{list-QIEQgIb3.mjs → list-OYeOGI0g.mjs} +4 -4
  37. package/dist/list-XIIj_ryX.mjs +43 -0
  38. package/dist/{login-DYAlK1aL.mjs → login-Bp2W2Hnj.mjs} +9 -2
  39. package/dist/{logout-S3GMy_ns.mjs → logout-BnoEQxKZ.mjs} +2 -2
  40. package/dist/now-Ce3ViNOw.mjs +9 -0
  41. package/dist/plan-C35h8U-w.mjs +10 -0
  42. package/dist/portrait-BEDGkOx1.mjs +14 -0
  43. package/dist/portrait-tags-B9hPFDIF.mjs +10 -0
  44. package/dist/{project-CiltZzqF.mjs → project-urShHaAN.mjs} +3 -3
  45. package/dist/{qa-DDzTuZ8K.mjs → qa-CaV_tDBc.mjs} +3 -3
  46. package/dist/{quantum-CgAcmQAL.mjs → quantum-Ds7rlag3.mjs} +5 -5
  47. package/dist/range-CSJiXTp3.mjs +18 -0
  48. package/dist/{resistance-YwKC91E0.mjs → resistance-DMesV4lG.mjs} +1 -1
  49. package/dist/{sales-report-XQ_Oy9ht.mjs → sales-report-BIfyfucO.mjs} +3 -3
  50. package/dist/{search-CxOFpH18.mjs → search-B0wA0YCI.mjs} +4 -4
  51. package/dist/shared-BEkL4JRB.mjs +11 -0
  52. package/dist/shared-Bbxvn4ZI.mjs +92 -0
  53. package/dist/shared-CYALikq5.mjs +277 -0
  54. package/dist/status-CrpCtf5U.mjs +65 -0
  55. package/dist/subtract-B4XTXD_X.mjs +21 -0
  56. package/dist/{summary-CfbHddby.mjs → summary-BIx7F4AE.mjs} +4 -4
  57. package/dist/summary-BkxVcv8a.mjs +65 -0
  58. package/dist/{text-R3KjyXSK.mjs → text-CqmsXzad.mjs} +3 -3
  59. package/dist/this-month-BRdfmc1s.mjs +5 -0
  60. package/dist/this-week-2GoqEaXU.mjs +5 -0
  61. package/dist/today-BhQnSMJH.mjs +5 -0
  62. package/dist/today-CdlU96lq.mjs +9 -0
  63. package/dist/today-so-far-D2g98rKv.mjs +5 -0
  64. package/dist/{token-usage-BDnEMHvB.mjs → token-usage-y46b3_7C.mjs} +3 -3
  65. package/dist/tomorrow-BgSEBV69.mjs +9 -0
  66. package/dist/{use-DgCwov4y.mjs → use-rAR4OVC7.mjs} +1 -1
  67. package/dist/{visit-Beb2o8zF.mjs → visit-CmEtfs7n.mjs} +2 -2
  68. package/dist/visit-evaluate-LdAg9iP5.mjs +9 -0
  69. package/dist/weekday-CgEg7n7U.mjs +14 -0
  70. package/dist/{whoami-H8AdUnQY.mjs → whoami-oRc8gILN.mjs} +1 -1
  71. package/dist/yesterday-CHAh1DlJ.mjs +5 -0
  72. package/dist/yesterday-DW-YiRTt.mjs +9 -0
  73. package/package.json +3 -2
  74. package/skills/wangxiaobao-audio-analyze/SKILL.md +42 -0
  75. package/skills/wangxiaobao-beautx/SKILL.md +42 -0
  76. package/skills/wangxiaobao-customer-portrait/SKILL.md +45 -0
  77. package/skills/wangxiaobao-customer-query/SKILL.md +1 -1
  78. package/skills/wangxiaobao-follow-query/SKILL.md +45 -0
  79. package/skills/wangxiaobao-knowledge-base/SKILL.md +1 -1
  80. package/skills/wangxiaobao-quantum-stats/SKILL.md +3 -1
  81. package/skills/wangxiaobao-quantum-stats/references/quantum-metrics.md +3 -2
  82. package/skills/wangxiaobao-shared/SKILL.md +5 -1
  83. /package/dist/{util-pyTg7qwY.mjs → util-BuoFIeLS.mjs} +0 -0
@@ -0,0 +1,277 @@
1
+ import { n as writeResult, t as writeError } from "./format-DfVypHID.mjs";
2
+ import { defineCommand } from "citty";
3
+ //#region src/core/date-util.ts
4
+ /**
5
+ * Local date/time helpers for `xiaobao-cli date *` (no network).
6
+ *
7
+ * Default timezone: Asia/Shanghai (or TZ env). Weekday numbering: 1=Monday … 7=Sunday.
8
+ */
9
+ const DEFAULT_TIMEZONE = "Asia/Shanghai";
10
+ const DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
11
+ const WEEKDAY_SHORT_TO_NUM = {
12
+ Mon: 1,
13
+ Tue: 2,
14
+ Wed: 3,
15
+ Thu: 4,
16
+ Fri: 5,
17
+ Sat: 6,
18
+ Sun: 7
19
+ };
20
+ var DateInputError = class extends Error {
21
+ code = "INVALID_DATE";
22
+ hint = "日期须为 YYYY-MM-DD;days 须为非负整数";
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = "DateInputError";
26
+ }
27
+ };
28
+ function resolveTimezone(input) {
29
+ const tz = (input ?? process.env.TZ ?? "Asia/Shanghai").trim();
30
+ if (!tz) return DEFAULT_TIMEZONE;
31
+ try {
32
+ Intl.DateTimeFormat(void 0, { timeZone: tz });
33
+ return tz;
34
+ } catch {
35
+ throw new DateInputError(`无效时区: ${tz}`);
36
+ }
37
+ }
38
+ function parseYmd(date) {
39
+ const m = DATE_RE.exec(date.trim());
40
+ if (!m) throw new DateInputError(`无效日期: ${date}`);
41
+ const year = Number(m[1]);
42
+ const month = Number(m[2]);
43
+ const day = Number(m[3]);
44
+ const probe = new Date(Date.UTC(year, month - 1, day));
45
+ if (probe.getUTCFullYear() !== year || probe.getUTCMonth() + 1 !== month || probe.getUTCDate() !== day) throw new DateInputError(`无效日期: ${date}`);
46
+ return {
47
+ year,
48
+ month,
49
+ day
50
+ };
51
+ }
52
+ function pad2(n) {
53
+ return String(n).padStart(2, "0");
54
+ }
55
+ function formatYmd(ymd) {
56
+ return `${ymd.year}-${pad2(ymd.month)}-${pad2(ymd.day)}`;
57
+ }
58
+ function formatDateTime(parts) {
59
+ return `${formatYmd(parts)} ${pad2(parts.hour)}:${pad2(parts.minute)}:${pad2(parts.second)}`;
60
+ }
61
+ function getZonedParts(instant, timeZone) {
62
+ const parts = new Intl.DateTimeFormat("en-US", {
63
+ timeZone,
64
+ year: "numeric",
65
+ month: "2-digit",
66
+ day: "2-digit",
67
+ hour: "2-digit",
68
+ minute: "2-digit",
69
+ second: "2-digit",
70
+ weekday: "short",
71
+ hour12: false,
72
+ hourCycle: "h23"
73
+ }).formatToParts(instant);
74
+ const pick = (type) => parts.find((p) => p.type === type)?.value ?? "";
75
+ const hourRaw = Number(pick("hour"));
76
+ const hour = hourRaw === 24 ? 0 : hourRaw;
77
+ return {
78
+ year: Number(pick("year")),
79
+ month: Number(pick("month")),
80
+ day: Number(pick("day")),
81
+ hour,
82
+ minute: Number(pick("minute")),
83
+ second: Number(pick("second")),
84
+ weekdayShort: pick("weekday")
85
+ };
86
+ }
87
+ function weekdayNumFromShort(short) {
88
+ const num = WEEKDAY_SHORT_TO_NUM[short];
89
+ if (!num) throw new DateInputError(`无法解析星期: ${short}`);
90
+ return num;
91
+ }
92
+ function weekdayZh(instant, timeZone) {
93
+ return new Intl.DateTimeFormat("zh-CN", {
94
+ timeZone,
95
+ weekday: "long"
96
+ }).format(instant);
97
+ }
98
+ function zonedInstant(ymd, hms, timeZone) {
99
+ const [hh, mm, ss] = hms.split(":").map(Number);
100
+ const utcGuess = Date.UTC(ymd.year, ymd.month - 1, ymd.day, hh, mm, ss);
101
+ const offset = getTimezoneOffsetMs(timeZone, new Date(utcGuess));
102
+ return new Date(utcGuess - offset);
103
+ }
104
+ function getTimezoneOffsetMs(timeZone, date) {
105
+ const parts = getZonedParts(date, timeZone);
106
+ return Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second) - date.getTime();
107
+ }
108
+ function addCalendarDays(ymd, offset) {
109
+ const dt = new Date(Date.UTC(ymd.year, ymd.month - 1, ymd.day + offset));
110
+ return {
111
+ year: dt.getUTCFullYear(),
112
+ month: dt.getUTCMonth() + 1,
113
+ day: dt.getUTCDate()
114
+ };
115
+ }
116
+ function buildDateInfo(parts, timeZone, instant) {
117
+ const weekdayNum = weekdayNumFromShort(parts.weekdayShort);
118
+ return {
119
+ date: formatYmd(parts),
120
+ datetime: formatDateTime(parts),
121
+ timezone: timeZone,
122
+ weekday: weekdayZh(instant, timeZone),
123
+ weekdayNum
124
+ };
125
+ }
126
+ function getNow(timeZoneInput) {
127
+ const timeZone = resolveTimezone(timeZoneInput);
128
+ const instant = /* @__PURE__ */ new Date();
129
+ return buildDateInfo(getZonedParts(instant, timeZone), timeZone, instant);
130
+ }
131
+ function getDateInfo(date, timeZoneInput) {
132
+ const timeZone = resolveTimezone(timeZoneInput);
133
+ const instant = zonedInstant(parseYmd(date), "00:00:00", timeZone);
134
+ const parts = getZonedParts(instant, timeZone);
135
+ return {
136
+ date: formatYmd(parts),
137
+ timezone: timeZone,
138
+ weekday: weekdayZh(instant, timeZone),
139
+ weekdayNum: weekdayNumFromShort(parts.weekdayShort)
140
+ };
141
+ }
142
+ function getRelativeDay(offsetDays, timeZoneInput) {
143
+ const timeZone = resolveTimezone(timeZoneInput);
144
+ return getDateInfo(formatYmd(addCalendarDays(getZonedParts(/* @__PURE__ */ new Date(), timeZone), offsetDays)), timeZone);
145
+ }
146
+ function addDays(date, offset, timeZoneInput) {
147
+ const timeZone = resolveTimezone(timeZoneInput);
148
+ return getDateInfo(formatYmd(addCalendarDays(parseYmd(date), offset)), timeZone);
149
+ }
150
+ function dateDiff(startDate, endDate) {
151
+ const start = parseYmd(startDate);
152
+ const end = parseYmd(endDate);
153
+ const startMs = Date.UTC(start.year, start.month - 1, start.day);
154
+ const endMs = Date.UTC(end.year, end.month - 1, end.day);
155
+ return {
156
+ startDate: formatYmd(start),
157
+ endDate: formatYmd(end),
158
+ days: Math.round((endMs - startMs) / 864e5)
159
+ };
160
+ }
161
+ function startOfWeekMonday(ymd, timeZone) {
162
+ return addCalendarDays(ymd, -(weekdayNumFromShort(getZonedParts(zonedInstant(ymd, "12:00:00", timeZone), timeZone).weekdayShort) - 1));
163
+ }
164
+ function startOfMonth(ymd) {
165
+ return {
166
+ year: ymd.year,
167
+ month: ymd.month,
168
+ day: 1
169
+ };
170
+ }
171
+ function nextMonthStart(ymd) {
172
+ if (ymd.month === 12) return {
173
+ year: ymd.year + 1,
174
+ month: 1,
175
+ day: 1
176
+ };
177
+ return {
178
+ year: ymd.year,
179
+ month: ymd.month + 1,
180
+ day: 1
181
+ };
182
+ }
183
+ function getRange(preset, timeZoneInput) {
184
+ const timeZone = resolveTimezone(timeZoneInput);
185
+ const todayParts = getZonedParts(/* @__PURE__ */ new Date(), timeZone);
186
+ const today = formatYmd(todayParts);
187
+ const tomorrow = formatYmd(addCalendarDays(todayParts, 1));
188
+ const yesterday = formatYmd(addCalendarDays(todayParts, -1));
189
+ let from;
190
+ let to;
191
+ switch (preset) {
192
+ case "today":
193
+ from = `${today} 00:00:00`;
194
+ to = `${tomorrow} 00:00:00`;
195
+ break;
196
+ case "yesterday":
197
+ from = `${yesterday} 00:00:00`;
198
+ to = `${today} 00:00:00`;
199
+ break;
200
+ case "today-so-far":
201
+ from = `${today} 00:00:00`;
202
+ to = formatDateTime(todayParts);
203
+ break;
204
+ case "this-week": {
205
+ const monday = formatYmd(startOfWeekMonday(todayParts, timeZone));
206
+ const nextMonday = formatYmd(addCalendarDays(parseYmd(monday), 7));
207
+ from = `${monday} 00:00:00`;
208
+ to = `${nextMonday} 00:00:00`;
209
+ break;
210
+ }
211
+ case "this-month": {
212
+ const monthStart = formatYmd(startOfMonth(todayParts));
213
+ const nextMonth = formatYmd(nextMonthStart(todayParts));
214
+ from = `${monthStart} 00:00:00`;
215
+ to = `${nextMonth} 00:00:00`;
216
+ break;
217
+ }
218
+ case "last-7-days":
219
+ from = `${formatYmd(addCalendarDays(todayParts, -6))} 00:00:00`;
220
+ to = `${tomorrow} 00:00:00`;
221
+ break;
222
+ default: throw new DateInputError(`未知 preset: ${preset}`);
223
+ }
224
+ const anchor = getDateInfo(today, timeZone);
225
+ return {
226
+ range: preset,
227
+ timezone: timeZone,
228
+ from,
229
+ to,
230
+ date: anchor.date,
231
+ weekday: anchor.weekday,
232
+ weekdayNum: anchor.weekdayNum
233
+ };
234
+ }
235
+ //#endregion
236
+ //#region src/commands/date/shared.ts
237
+ /**
238
+ * Shared helpers for `xiaobao-cli date *`.
239
+ */
240
+ const timezoneArg = { timezone: {
241
+ type: "string",
242
+ description: "IANA 时区,默认 Asia/Shanghai(或 TZ 环境变量)"
243
+ } };
244
+ function parsePositiveDays(raw, label = "days") {
245
+ if (raw === void 0 || raw === "") throw new DateInputError(`缺少 ${label}`);
246
+ const days = Number(raw);
247
+ if (!Number.isInteger(days) || days < 0) throw new DateInputError(`${label} 须为非负整数: ${raw}`);
248
+ return days;
249
+ }
250
+ function defineDateCommand(def) {
251
+ return defineCommand({
252
+ meta: {
253
+ name: def.name,
254
+ description: def.description
255
+ },
256
+ args: {
257
+ format: {
258
+ type: "string",
259
+ default: "toon",
260
+ description: "Stdout format: toon (default) / json / table"
261
+ },
262
+ ...timezoneArg,
263
+ ...def.args
264
+ },
265
+ async run({ args }) {
266
+ const format = typeof args.format === "string" ? args.format : void 0;
267
+ try {
268
+ writeResult(def.run({ args }), format);
269
+ } catch (err) {
270
+ writeError(err, format);
271
+ process.exit(1);
272
+ }
273
+ }
274
+ });
275
+ }
276
+ //#endregion
277
+ export { getDateInfo as a, getRelativeDay as c, dateDiff as i, parsePositiveDays as n, getNow as o, addDays as r, getRange as s, defineDateCommand as t };
@@ -0,0 +1,65 @@
1
+ import { i as refreshToken, o as resolveConfig, t as exchangeDeviceCode } from "./device-flow-j10PFOQL.mjs";
2
+ import { a as isAccessTokenValid, i as decodeJwtPayload, l as writeToken, n as clearPendingAuth, o as readPendingAuth, r as clearToken, s as readToken } from "./token-store-BB7n-fwJ.mjs";
3
+ import { defineCommand } from "citty";
4
+ //#region src/commands/auth/status.ts
5
+ /**
6
+ * `xiaobao-cli auth status` — host-friendly auth check (WorkBuddy cli.json
7
+ * `status` command; also handy for scripts).
8
+ *
9
+ * Contract (WorkBuddy Connector spec §12.4): exit 0 + "Logged in as ..." on
10
+ * stdout when authenticated, non-zero otherwise; must return within seconds
11
+ * and be safe to poll every 3s.
12
+ *
13
+ * If a device authorization is pending (`auth login` was started and the host
14
+ * killed it after reading the URL), one token-endpoint attempt finishes the
15
+ * exchange here — this is what lets WorkBuddy's "kill auth, poll status"
16
+ * flow complete without the auth process staying alive.
17
+ */
18
+ function loggedIn(token) {
19
+ const c = decodeJwtPayload(token.id_token) ?? {};
20
+ const who = c.name ?? c.preferred_username ?? c.sub ?? "unknown";
21
+ process.stdout.write(`Logged in as ${String(who)}\n`);
22
+ process.exit(0);
23
+ }
24
+ function notLoggedIn(reason) {
25
+ process.stdout.write(`Not authenticated: ${reason}\n`);
26
+ process.exit(1);
27
+ }
28
+ var status_default = defineCommand({
29
+ meta: {
30
+ name: "status",
31
+ description: "认证状态检测(exit 0 = 已登录;可完成待授权的 device flow)"
32
+ },
33
+ args: { "auth-base": {
34
+ type: "string",
35
+ description: "覆盖 auth base URL"
36
+ } },
37
+ async run({ args }) {
38
+ const config = resolveConfig({ authBase: args["auth-base"] });
39
+ const cached = await readToken();
40
+ if (isAccessTokenValid(cached)) loggedIn(cached);
41
+ if (cached?.refresh_token) try {
42
+ loggedIn(await writeToken(await refreshToken(config, cached.refresh_token)));
43
+ } catch {
44
+ await clearToken();
45
+ }
46
+ const pending = await readPendingAuth();
47
+ if (!pending) notLoggedIn("run `xiaobao-cli auth login`");
48
+ if (pending.expires_at <= Date.now()) {
49
+ await clearPendingAuth();
50
+ notLoggedIn("device authorization expired, run `xiaobao-cli auth login` again");
51
+ }
52
+ try {
53
+ const result = await exchangeDeviceCode(config, pending.device_code);
54
+ if (result === "pending" || result === "slow_down") notLoggedIn("awaiting authorization in browser");
55
+ const saved = await writeToken(result);
56
+ await clearPendingAuth();
57
+ loggedIn(saved);
58
+ } catch (err) {
59
+ await clearPendingAuth();
60
+ notLoggedIn(`device authorization failed (${err?.code ?? "error"}), run \`xiaobao-cli auth login\` again`);
61
+ }
62
+ }
63
+ });
64
+ //#endregion
65
+ export { status_default as default };
@@ -0,0 +1,21 @@
1
+ import { n as parsePositiveDays, r as addDays, t as defineDateCommand } from "./shared-CYALikq5.mjs";
2
+ //#region src/commands/date/subtract.ts
3
+ var subtract_default = defineDateCommand({
4
+ name: "subtract",
5
+ description: "日期往前推 N 天",
6
+ args: {
7
+ date: {
8
+ type: "positional",
9
+ required: true,
10
+ description: "基准日期 YYYY-MM-DD"
11
+ },
12
+ days: {
13
+ type: "positional",
14
+ required: true,
15
+ description: "往前推的天数(非负整数)"
16
+ }
17
+ },
18
+ run: ({ args }) => addDays(args.date, -parsePositiveDays(args.days, "days"), args.timezone)
19
+ });
20
+ //#endregion
21
+ export { subtract_default as default };
@@ -1,9 +1,9 @@
1
- import { a as resolveConfig } from "./device-flow-DC1kqcmR.mjs";
2
- import { t as xbApiFetch } from "./api-client-ifJHc7hu.mjs";
1
+ import { o as resolveConfig } from "./device-flow-j10PFOQL.mjs";
2
+ import { t as xbApiFetch } from "./api-client-D3sCv6L3.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CudTRvbA.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
5
- import { n as writeResult, t as writeError } from "./format-HWx3JWdS.mjs";
6
- import { r as toLocalDateTime } from "./util-pyTg7qwY.mjs";
5
+ import { n as writeResult, t as writeError } from "./format-DfVypHID.mjs";
6
+ import { r as toLocalDateTime } from "./util-BuoFIeLS.mjs";
7
7
  import { defineCommand } from "citty";
8
8
  //#region src/commands/visit/summary.ts
9
9
  /**
@@ -0,0 +1,65 @@
1
+ import { o as resolveConfig } from "./device-flow-j10PFOQL.mjs";
2
+ import { t as xbApiFetch } from "./api-client-D3sCv6L3.mjs";
3
+ import { n as requireActiveProject } from "./project-store-CudTRvbA.mjs";
4
+ import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
5
+ import { n as writeResult, t as writeError } from "./format-DfVypHID.mjs";
6
+ import { r as toLocalDateTime, t as csvToArray } from "./util-BuoFIeLS.mjs";
7
+ import { defineCommand } from "citty";
8
+ //#region src/commands/follow/summary.ts
9
+ /**
10
+ * `xiaobao-cli follow summary` — 旺跟进汇总(电话/微信指标)。
11
+ *
12
+ * 同 follow list 的筛选条件,返回电话呼入/呼出次数与接通率、通话时长、
13
+ * 微信通话与消息数等指标(不分页)。调 POST /ai-open/follows/summary。
14
+ */
15
+ var summary_default = defineCommand({
16
+ meta: {
17
+ name: "summary",
18
+ description: "旺跟进汇总:电话呼入/呼出/接通率/时长、微信通话与消息数"
19
+ },
20
+ args: {
21
+ "user-ids": {
22
+ type: "string",
23
+ description: "CSV: 跟进人 userId 过滤;不传按当前授权范围"
24
+ },
25
+ "customer-ids": {
26
+ type: "string",
27
+ description: "CSV: 客户 wang_id 过滤"
28
+ },
29
+ from: {
30
+ type: "string",
31
+ description: "开始时间(含)"
32
+ },
33
+ to: {
34
+ type: "string",
35
+ description: "结束时间(不含)"
36
+ },
37
+ "api-base": { type: "string" },
38
+ "auth-base": { type: "string" },
39
+ format: {
40
+ type: "string",
41
+ default: "toon"
42
+ }
43
+ },
44
+ async run({ args }) {
45
+ try {
46
+ writeResult(await xbApiFetch(resolveConfig({
47
+ apiBase: args["api-base"],
48
+ authBase: args["auth-base"]
49
+ }), "POST", "/ai-open/follows/summary", {
50
+ headers: tenantProjectHeaders(await requireActiveProject()),
51
+ body: {
52
+ userIds: csvToArray(args["user-ids"])?.map(Number),
53
+ customerIds: csvToArray(args["customer-ids"])?.map(Number),
54
+ fromDate: toLocalDateTime(args.from),
55
+ toDate: toLocalDateTime(args.to)
56
+ }
57
+ }), args.format);
58
+ } catch (err) {
59
+ writeError(err, args.format);
60
+ process.exit(1);
61
+ }
62
+ }
63
+ });
64
+ //#endregion
65
+ export { summary_default as default };
@@ -1,8 +1,8 @@
1
- import { a as resolveConfig } from "./device-flow-DC1kqcmR.mjs";
2
- import { t as xbApiFetch } from "./api-client-ifJHc7hu.mjs";
1
+ import { o as resolveConfig } from "./device-flow-j10PFOQL.mjs";
2
+ import { t as xbApiFetch } from "./api-client-D3sCv6L3.mjs";
3
3
  import { n as requireActiveProject } from "./project-store-CudTRvbA.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
5
- import { n as writeResult, t as writeError } from "./format-HWx3JWdS.mjs";
5
+ import { n as writeResult, t as writeError } from "./format-DfVypHID.mjs";
6
6
  import { defineCommand } from "citty";
7
7
  //#region src/commands/audio/text.ts
8
8
  /**
@@ -0,0 +1,5 @@
1
+ import { t as defineRangePresetCommand } from "./shared-BEkL4JRB.mjs";
2
+ //#region src/commands/date/range/this-month.ts
3
+ var this_month_default = defineRangePresetCommand("this-month", "本月:from=本月 1 日 00:00,to=下月 1 日 00:00");
4
+ //#endregion
5
+ export { this_month_default as default };
@@ -0,0 +1,5 @@
1
+ import { t as defineRangePresetCommand } from "./shared-BEkL4JRB.mjs";
2
+ //#region src/commands/date/range/this-week.ts
3
+ var this_week_default = defineRangePresetCommand("this-week", "本周:from=本周一 00:00,to=下周一 00:00");
4
+ //#endregion
5
+ export { this_week_default as default };
@@ -0,0 +1,5 @@
1
+ import { t as defineRangePresetCommand } from "./shared-BEkL4JRB.mjs";
2
+ //#region src/commands/date/range/today.ts
3
+ var today_default = defineRangePresetCommand("today", "今天全天:from=今天 00:00,to=明天 00:00");
4
+ //#endregion
5
+ export { today_default as default };
@@ -0,0 +1,9 @@
1
+ import { c as getRelativeDay, t as defineDateCommand } from "./shared-CYALikq5.mjs";
2
+ //#region src/commands/date/today.ts
3
+ var today_default = defineDateCommand({
4
+ name: "today",
5
+ description: "今天日期与星期",
6
+ run: ({ args }) => getRelativeDay(0, args.timezone)
7
+ });
8
+ //#endregion
9
+ export { today_default as default };
@@ -0,0 +1,5 @@
1
+ import { t as defineRangePresetCommand } from "./shared-BEkL4JRB.mjs";
2
+ //#region src/commands/date/range/today-so-far.ts
3
+ var today_so_far_default = defineRangePresetCommand("today-so-far", "今天至今:from=今天 00:00,to=当前时刻");
4
+ //#endregion
5
+ export { today_so_far_default as default };
@@ -1,8 +1,8 @@
1
- import { a as resolveConfig } from "./device-flow-DC1kqcmR.mjs";
2
- import { t as xbApiFetch } from "./api-client-ifJHc7hu.mjs";
1
+ import { o as resolveConfig } from "./device-flow-j10PFOQL.mjs";
2
+ import { t as xbApiFetch } from "./api-client-D3sCv6L3.mjs";
3
3
  import { t as loadActiveProject } from "./project-store-CudTRvbA.mjs";
4
4
  import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
5
- import { n as writeResult, t as writeError } from "./format-HWx3JWdS.mjs";
5
+ import { n as writeResult, t as writeError } from "./format-DfVypHID.mjs";
6
6
  import { defineCommand } from "citty";
7
7
  //#region src/commands/admin/token-usage.ts
8
8
  /**
@@ -0,0 +1,9 @@
1
+ import { c as getRelativeDay, t as defineDateCommand } from "./shared-CYALikq5.mjs";
2
+ //#region src/commands/date/tomorrow.ts
3
+ var tomorrow_default = defineDateCommand({
4
+ name: "tomorrow",
5
+ description: "明天日期与星期",
6
+ run: ({ args }) => getRelativeDay(1, args.timezone)
7
+ });
8
+ //#endregion
9
+ export { tomorrow_default as default };
@@ -1,5 +1,5 @@
1
1
  import { r as saveActiveProject } from "./project-store-CudTRvbA.mjs";
2
- import { n as writeResult, t as writeError } from "./format-HWx3JWdS.mjs";
2
+ import { n as writeResult, t as writeError } from "./format-DfVypHID.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  //#region src/commands/project/use.ts
5
5
  /**
@@ -6,8 +6,8 @@ var visit_default = defineCommand({
6
6
  description: "客户来访分页查询 / 来访接待总结"
7
7
  },
8
8
  subCommands: {
9
- list: () => import("./list-BxWqQllQ2.mjs").then((m) => m.default),
10
- summary: () => import("./summary-CfbHddby.mjs").then((m) => m.default)
9
+ list: () => import("./list-DWv6toPT.mjs").then((m) => m.default),
10
+ summary: () => import("./summary-BIx7F4AE.mjs").then((m) => m.default)
11
11
  }
12
12
  });
13
13
  //#endregion
@@ -0,0 +1,9 @@
1
+ import { t as defineBeautxCommand } from "./shared-Bbxvn4ZI.mjs";
2
+ //#region src/commands/beautx/visit-evaluate.ts
3
+ var visit_evaluate_default = defineBeautxCommand({
4
+ name: "visit-evaluate",
5
+ description: "医美接诊评价分页(总分/总评 + 5 项能力分与建议)",
6
+ path: "/ai-open/beautx/visit-evaluates"
7
+ });
8
+ //#endregion
9
+ export { visit_evaluate_default as default };
@@ -0,0 +1,14 @@
1
+ import { a as getDateInfo, t as defineDateCommand } from "./shared-CYALikq5.mjs";
2
+ //#region src/commands/date/weekday.ts
3
+ var weekday_default = defineDateCommand({
4
+ name: "weekday",
5
+ description: "查询指定日期是星期几",
6
+ args: { date: {
7
+ type: "string",
8
+ description: "YYYY-MM-DD",
9
+ required: true
10
+ } },
11
+ run: ({ args }) => getDateInfo(args.date, args.timezone)
12
+ });
13
+ //#endregion
14
+ export { weekday_default as default };
@@ -1,5 +1,5 @@
1
1
  import { i as decodeJwtPayload, s as readToken } from "./token-store-BB7n-fwJ.mjs";
2
- import { n as writeResult, t as writeError } from "./format-HWx3JWdS.mjs";
2
+ import { n as writeResult, t as writeError } from "./format-DfVypHID.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  //#region src/commands/auth/whoami.ts
5
5
  /**
@@ -0,0 +1,5 @@
1
+ import { t as defineRangePresetCommand } from "./shared-BEkL4JRB.mjs";
2
+ //#region src/commands/date/range/yesterday.ts
3
+ var yesterday_default = defineRangePresetCommand("yesterday", "昨天全天:from=昨天 00:00,to=今天 00:00");
4
+ //#endregion
5
+ export { yesterday_default as default };
@@ -0,0 +1,9 @@
1
+ import { c as getRelativeDay, t as defineDateCommand } from "./shared-CYALikq5.mjs";
2
+ //#region src/commands/date/yesterday.ts
3
+ var yesterday_default = defineDateCommand({
4
+ name: "yesterday",
5
+ description: "昨天日期与星期",
6
+ run: ({ args }) => getRelativeDay(-1, args.timezone)
7
+ });
8
+ //#endregion
9
+ export { yesterday_default as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puyinkai/xiaobao-cli",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "旺小宝 CLI — host-agnostic 业务数据 / 问数 / 来访总结 / 知识库 能力 (openclaw-xiaobao plugin 的 CLI 等价物)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,8 @@
23
23
  "scripts": {
24
24
  "build": "tsdown",
25
25
  "typecheck": "tsc --noEmit",
26
- "dev": "node --import tsx src/cli.ts"
26
+ "dev": "node --import tsx src/cli.ts",
27
+ "workbuddy:pack": "rm -rf dist-workbuddy && mkdir -p dist-workbuddy/wangxiaobao && cp workbuddy/connector-meta.json workbuddy/cli.json workbuddy/icon.svg dist-workbuddy/wangxiaobao/ && cp -R skills dist-workbuddy/wangxiaobao/skills && cd dist-workbuddy && zip -qr wangxiaobao-connector.zip wangxiaobao && echo dist-workbuddy/wangxiaobao-connector.zip"
27
28
  },
28
29
  "dependencies": {
29
30
  "citty": "^0.1.6"
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: wangxiaobao-audio-analyze
3
+ version: 0.1.0
4
+ description: "旺小宝录音批量分析(LLM 生成式):对指定客户/顾问范围的录音做全文分析或时间线分析,返回分析文本。只读但昂贵(LangGraph,最长可达数分钟)。高频命令: xiaobao-cli audio analyze \"<分析问题>\" --customer-id <wang_id> [--type general_analysis|timeline_analysis]。何时用:用户说 分析某客户的历史录音、总结某顾问接待话术/共性抗性、按时间线梳理对话要点。注:必须至少带 --customer-id/--customer-ids/--advisor-ids 之一收窄范围;查录音元数据用 audio list、看逐字稿用 audio text、统计类 KPI(录音覆盖率/盘客率)用 quantum job-performance——都不是这里。"
5
+ metadata:
6
+ requires:
7
+ bins: ["xiaobao-cli"]
8
+ cliHelp: "xiaobao-cli audio analyze --help"
9
+ ---
10
+
11
+ # 旺小宝录音批量分析(LLM)
12
+
13
+ > **CRITICAL** —— 跑命令前 MUST 先用 Read tool 读取 [`../wangxiaobao-shared/SKILL.md`](../wangxiaobao-shared/SKILL.md)。
14
+
15
+ ## 分工
16
+ | 需求 | 用什么 |
17
+ | --- | --- |
18
+ | 生成式分析(总结/抗性归纳/时间线) | `audio analyze`(本 skill,**昂贵**) |
19
+ | 录音元数据(列表/时长/文件) | `audio list` |
20
+ | 逐字稿原文 | `audio text <audio-id>` |
21
+ | 录音覆盖率/盘客率等 KPI | `quantum job-performance` |
22
+
23
+ ## 命令
24
+ ```bash
25
+ xiaobao-cli audio analyze "总结客户主要顾虑" --customer-id <wang_id>
26
+ xiaobao-cli audio analyze "按时间线梳理对话要点" --customer-ids id1,id2 --type timeline_analysis
27
+ xiaobao-cli audio analyze "总结这位顾问的话术问题" --advisor-ids 123
28
+ ```
29
+ 参数:`query` positional 必填(分析问题/指令);`--type` = `general_analysis`(默认)/ `timeline_analysis`;`--customer-id`(单个 wang_id)/ `--customer-ids`(CSV)/ `--advisor-ids`(CSV 顾问 userId)。
30
+
31
+ ## 响应字段
32
+ `data.answer`(**优先读这个**,最终分析文本)、`output`(上游原始输出,answer 空时再看)、`type`、`threadId` 等运行元数据。没有 `audio text` 那种分段 role 结构。
33
+
34
+ ## Agent 约束(控成本)
35
+ - **昂贵调用**:必须至少带 `--customer-id` / `--customer-ids` / `--advisor-ids` 之一,禁止无范围全项目分析
36
+ - 客户名 → 先 `customer list --customer-name` 反查 wang_id
37
+ - 多客户合并一次 `--customer-ids`,不要逐个 analyze
38
+ - 只需要看原文片段时用 `audio text` / `focus list` / `resistance list`,更轻
39
+
40
+ ## 常见错误
41
+ - 耗时长是正常(LLM 分析);超时/IO 异常 → 稍后重试或缩小范围
42
+ - `NO_ACTIVE_PROJECT`/401 → 见 shared