@puyinkai/wow-xiaobao-cli 0.0.0-stage → 1.0.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 (89) hide show
  1. package/README.md +42 -2
  2. package/dist/add-Da4a88Dn.mjs +21 -0
  3. package/dist/admin-nr9IIF2K.mjs +14 -0
  4. package/dist/alerts-DjMCQESW.mjs +71 -0
  5. package/dist/analyze-BJATzFtI.mjs +67 -0
  6. package/dist/api-C1s2co2-.mjs +113 -0
  7. package/dist/audio-BYm3m77S.mjs +15 -0
  8. package/dist/auth-JZ34ksvF.mjs +14 -0
  9. package/dist/beautx-D5y2iytj.mjs +16 -0
  10. package/dist/call-BfT4uEa7.mjs +67 -0
  11. package/dist/cli.d.mts +1 -0
  12. package/dist/cli.mjs +47 -0
  13. package/dist/config-DipFfMio.mjs +11 -0
  14. package/dist/config-DqUxKQ3m.mjs +131 -0
  15. package/dist/consultant-CJ0tH11c.mjs +11 -0
  16. package/dist/current-Di1OImPn.mjs +33 -0
  17. package/dist/customer-Bpfdfh0U.mjs +15 -0
  18. package/dist/date-C9LQUbfA.mjs +21 -0
  19. package/dist/deal-analysis-Q4GMijIy.mjs +10 -0
  20. package/dist/detail-Df2eLTSN.mjs +69 -0
  21. package/dist/detail-k-zGzX4n.mjs +71 -0
  22. package/dist/diff-DkyjJG49.mjs +21 -0
  23. package/dist/doc-content-DlRfFk_V.mjs +54 -0
  24. package/dist/doc-e7Hu-v3F.mjs +42 -0
  25. package/dist/docs-C2ukQfbY.mjs +41 -0
  26. package/dist/fields-CNQUQ3BI.mjs +49 -0
  27. package/dist/focus-BNA14pxJ.mjs +11 -0
  28. package/dist/follow-BYYbUVCh.mjs +15 -0
  29. package/dist/format-bDFUVuDQ.mjs +622 -0
  30. package/dist/get-ZFU7CjqO.mjs +61 -0
  31. package/dist/headers-Dy5gROBB.mjs +11 -0
  32. package/dist/intent-DF8cH6nw.mjs +63 -0
  33. package/dist/json-CXENUkN_.mjs +13 -0
  34. package/dist/kb-n84Hl0uL.mjs +16 -0
  35. package/dist/last-7-days-WPinYIJi.mjs +5 -0
  36. package/dist/list-BVk58WIy.mjs +30 -0
  37. package/dist/list-BwhH-ZlP.mjs +87 -0
  38. package/dist/list-CJx_zclT.mjs +68 -0
  39. package/dist/list-CL5W1jMc.mjs +75 -0
  40. package/dist/list-Cb2q7k36.mjs +36 -0
  41. package/dist/list-Cl0VBo_Z.mjs +71 -0
  42. package/dist/list-D27lkRNJ.mjs +86 -0
  43. package/dist/list-D7iMacYA.mjs +71 -0
  44. package/dist/list-DTiXpPZF.mjs +38 -0
  45. package/dist/list-Dv2BGPQa.mjs +71 -0
  46. package/dist/list-KTc5uu5P.mjs +86 -0
  47. package/dist/now-Rn6gT4Bn.mjs +9 -0
  48. package/dist/panke-_HDbr3xq.mjs +14 -0
  49. package/dist/plan-c3BW0aCB.mjs +10 -0
  50. package/dist/portrait-BopNLEQX.mjs +14 -0
  51. package/dist/portrait-tags-Dhwy6-6U.mjs +10 -0
  52. package/dist/project-CIOk-2wk.mjs +15 -0
  53. package/dist/project-store-B-2rqrgM.mjs +63 -0
  54. package/dist/qa-CxQZgKRF.mjs +59 -0
  55. package/dist/quantum-BSlMUFk6.mjs +178 -0
  56. package/dist/range-BsL08fkP.mjs +18 -0
  57. package/dist/resistance-CzbXP4Mj.mjs +11 -0
  58. package/dist/sales-report-BbNoueUk.mjs +65 -0
  59. package/dist/score-CVu51n3k.mjs +5 -0
  60. package/dist/score-yakz2AnG.mjs +5 -0
  61. package/dist/search-kLJrFOVI.mjs +70 -0
  62. package/dist/service-score-CiIxFGJ8.mjs +91 -0
  63. package/dist/shared-BCf9YDEa.mjs +11 -0
  64. package/dist/shared-COOwe14d.mjs +87 -0
  65. package/dist/shared-CdUFV4rc.mjs +277 -0
  66. package/dist/staff-DasX8vTs.mjs +14 -0
  67. package/dist/staff-client-B3hnEyNi.mjs +59 -0
  68. package/dist/status-BFJYeL2I.mjs +24 -0
  69. package/dist/subtract-wUG7Jzdu.mjs +21 -0
  70. package/dist/summary-Dgt4Wm5k.mjs +79 -0
  71. package/dist/summary-DtLL_XW3.mjs +60 -0
  72. package/dist/text-Dj9OFG5s.mjs +44 -0
  73. package/dist/this-month-BsrOeTBH.mjs +5 -0
  74. package/dist/this-week-tCzGJfrf.mjs +5 -0
  75. package/dist/today-Br-4_qMr.mjs +9 -0
  76. package/dist/today-CqrcQCGQ.mjs +5 -0
  77. package/dist/today-so-far-CSGFhwkN.mjs +5 -0
  78. package/dist/token-store-BGLVc825.mjs +33 -0
  79. package/dist/token-usage-BK5JIJKx.mjs +97 -0
  80. package/dist/tomorrow-DYhV_kGk.mjs +9 -0
  81. package/dist/use-89I-jHty.mjs +61 -0
  82. package/dist/util-BuoFIeLS.mjs +46 -0
  83. package/dist/visit-ChOnvK8M.mjs +16 -0
  84. package/dist/visit-evaluate-BnxsFnQc.mjs +9 -0
  85. package/dist/weekday-7aGLxWaZ.mjs +14 -0
  86. package/dist/whoami-Ci6-uJJd.mjs +51 -0
  87. package/dist/yesterday-BzT2U1_f.mjs +9 -0
  88. package/dist/yesterday-DgYoUKYd.mjs +5 -0
  89. package/package.json +35 -4
@@ -0,0 +1,70 @@
1
+ import { n as xbApiFetch, t as resolveConfig } from "./config-DqUxKQ3m.mjs";
2
+ import { n as requireActiveProject } from "./project-store-B-2rqrgM.mjs";
3
+ import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
4
+ import { n as writeResult, t as writeError } from "./format-bDFUVuDQ.mjs";
5
+ import { t as csvToArray } from "./util-BuoFIeLS.mjs";
6
+ import { defineCommand } from "citty";
7
+ //#region src/commands/kb/search.ts
8
+ /**
9
+ * `wow-xiaobao kb search <query>` — semantic knowledge-base retrieval via
10
+ * POST /ai-open/kb/search. Returns ranked chunks (not full documents),
11
+ * scoped to the active project.
12
+ */
13
+ var search_default = defineCommand({
14
+ meta: {
15
+ name: "search",
16
+ description: "知识库语义检索(返回 chunk,按当前激活项目隔离)"
17
+ },
18
+ args: {
19
+ query: {
20
+ type: "positional",
21
+ required: true,
22
+ description: "检索 query"
23
+ },
24
+ k: {
25
+ type: "string",
26
+ description: "返回 chunk 数量,默认 5,最大 20"
27
+ },
28
+ "chunk-types": {
29
+ type: "string",
30
+ description: "CSV: chunk 类型过滤"
31
+ },
32
+ "doc-ids": {
33
+ type: "string",
34
+ description: "CSV: 文档 ID 过滤"
35
+ },
36
+ "enable-rerank": {
37
+ type: "boolean",
38
+ description: "是否启用 rerank"
39
+ },
40
+ "rerank-threshold": {
41
+ type: "string",
42
+ description: "rerank 阈值(小数)"
43
+ },
44
+ "api-base": { type: "string" },
45
+ format: {
46
+ type: "string",
47
+ default: "toon"
48
+ }
49
+ },
50
+ async run({ args }) {
51
+ try {
52
+ writeResult(await xbApiFetch(resolveConfig({ apiBase: args["api-base"] }), "POST", "/ai-open/kb/search", {
53
+ headers: tenantProjectHeaders(await requireActiveProject()),
54
+ body: {
55
+ query: String(args.query),
56
+ k: args.k != null ? Number(args.k) : void 0,
57
+ chunkTypes: csvToArray(args["chunk-types"]),
58
+ docIds: csvToArray(args["doc-ids"]),
59
+ enableRerank: args["enable-rerank"] ? true : void 0,
60
+ rerankThreshold: args["rerank-threshold"] != null ? Number(args["rerank-threshold"]) : void 0
61
+ }
62
+ }), args.format);
63
+ } catch (err) {
64
+ writeError(err, args.format);
65
+ process.exit(1);
66
+ }
67
+ }
68
+ });
69
+ //#endregion
70
+ export { search_default as default };
@@ -0,0 +1,91 @@
1
+ import { n as xbApiFetch, t as resolveConfig } from "./config-DqUxKQ3m.mjs";
2
+ import { n as requireActiveProject } from "./project-store-B-2rqrgM.mjs";
3
+ import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
4
+ import { n as writeResult, t as writeError } from "./format-bDFUVuDQ.mjs";
5
+ import { defineCommand } from "citty";
6
+ //#region src/commands/shared/service-score.ts
7
+ const scoreItems = [
8
+ "sales",
9
+ "demand",
10
+ "duration",
11
+ "interaction",
12
+ "icebreaking",
13
+ "communication"
14
+ ];
15
+ /** 两种维度复用参数校验和协议,业务 ID 始终按字符串发送以保留大整数精度。 */
16
+ function serviceScoreCommand(scope) {
17
+ const idArg = `${scope}-id`;
18
+ return defineCommand({
19
+ meta: {
20
+ name: "score",
21
+ description: `${scope === "customer" ? "客户综合" : "单次来访"}服务分,支持按评分项下钻(不含证据)`
22
+ },
23
+ args: {
24
+ [idArg]: {
25
+ type: "string",
26
+ required: true,
27
+ description: scope === "customer" ? "客户 wangId" : "来访 ID"
28
+ },
29
+ "include-details": {
30
+ type: "boolean",
31
+ default: false,
32
+ description: "返回明细,默认只返回总分及各评分项"
33
+ },
34
+ items: {
35
+ type: "string",
36
+ description: `明细范围,逗号分隔:${scoreItems.join(",")};需配合 --include-details`
37
+ },
38
+ "api-base": { type: "string" },
39
+ format: {
40
+ type: "string",
41
+ default: "json"
42
+ }
43
+ },
44
+ async run({ args }) {
45
+ try {
46
+ const id = String(args[idArg]);
47
+ const format = String(args.format);
48
+ const includeDetails = args["include-details"] === true;
49
+ const items = args.items === void 0 ? void 0 : String(args.items).split(",").map((item) => item.trim());
50
+ if (!/^[1-9]\d*$/.test(id) || BigInt(id) > 9223372036854775807n || items !== void 0 && (!includeDetails || items.some((item) => !scoreItems.includes(item)))) {
51
+ writeError({
52
+ code: "BAD_ARGUMENT",
53
+ message: `${idArg} 必须为有效正整数 ID;--items 需配合 --include-details,取值为 ${scoreItems.join(",")}`
54
+ }, format);
55
+ process.exit(2);
56
+ }
57
+ const config = resolveConfig({ apiBase: typeof args["api-base"] === "string" ? args["api-base"] : void 0 });
58
+ const active = await requireActiveProject();
59
+ const result = await xbApiFetch(config, "POST", `/ai-open/${scope === "customer" ? "customers" : "visits"}/score`, {
60
+ headers: tenantProjectHeaders(active),
61
+ body: {
62
+ [scope === "customer" ? "customerId" : "visitId"]: id,
63
+ includeDetails,
64
+ ...items === void 0 ? {} : { items: [...new Set(items)] }
65
+ }
66
+ });
67
+ const envelope = result.data;
68
+ if (!result.ok || !envelope || typeof envelope !== "object" || String(envelope.code) !== "0") throw {
69
+ code: "API_ERROR",
70
+ status: result.status,
71
+ message: envelope?.msg ?? "服务分查询失败",
72
+ body: result.data
73
+ };
74
+ if (typeof envelope.data === "string") throw {
75
+ code: "ACCESS_DENIED",
76
+ message: envelope.data
77
+ };
78
+ if (!envelope.data || typeof envelope.data !== "object") throw {
79
+ code: "API_ERROR",
80
+ message: "服务分返回数据为空"
81
+ };
82
+ writeResult(result, format);
83
+ } catch (err) {
84
+ writeError(err, String(args.format));
85
+ process.exit(1);
86
+ }
87
+ }
88
+ });
89
+ }
90
+ //#endregion
91
+ export { serviceScoreCommand as t };
@@ -0,0 +1,11 @@
1
+ import { s as getRange, t as defineDateCommand } from "./shared-CdUFV4rc.mjs";
2
+ //#region src/commands/date/range/shared.ts
3
+ function defineRangePresetCommand(preset, description) {
4
+ return defineDateCommand({
5
+ name: preset,
6
+ description,
7
+ run: ({ args }) => getRange(preset, args.timezone)
8
+ });
9
+ }
10
+ //#endregion
11
+ export { defineRangePresetCommand as t };
@@ -0,0 +1,87 @@
1
+ import { n as xbApiFetch, t as resolveConfig } from "./config-DqUxKQ3m.mjs";
2
+ import { n as requireActiveProject } from "./project-store-B-2rqrgM.mjs";
3
+ import { t as tenantProjectHeaders } from "./headers-Dy5gROBB.mjs";
4
+ import { n as writeResult, t as writeError } from "./format-bDFUVuDQ.mjs";
5
+ import { r as toLocalDateTime, t as csvToArray } from "./util-BuoFIeLS.mjs";
6
+ import { defineCommand } from "citty";
7
+ //#region src/commands/beautx/shared.ts
8
+ /**
9
+ * Shared args/body builder for `wow-xiaobao beautx *` — the four beautx (医美)
10
+ * queries share one request shape (visitIds/wangIds/beautxCustomerIds/时间窗/分页).
11
+ */
12
+ function defineBeautxCommand(def) {
13
+ return defineCommand({
14
+ meta: {
15
+ name: def.name,
16
+ description: def.description
17
+ },
18
+ args: {
19
+ "visit-ids": {
20
+ type: "string",
21
+ description: "CSV: 来访 ID 过滤"
22
+ },
23
+ "customer-ids": {
24
+ type: "string",
25
+ description: "CSV: 客户 wang_id 过滤"
26
+ },
27
+ "beautx-customer-ids": {
28
+ type: "string",
29
+ description: "CSV: 医美业务客户 ID 过滤"
30
+ },
31
+ ...def.supportsLatestOnly ? { "latest-only": {
32
+ type: "boolean",
33
+ description: "每客户只取最新一条"
34
+ } } : {},
35
+ ...def.supportsDimension ? { dimension: {
36
+ type: "string",
37
+ description: "画像标签维度名过滤"
38
+ } } : {},
39
+ from: {
40
+ type: "string",
41
+ description: "开始时间(含)"
42
+ },
43
+ to: {
44
+ type: "string",
45
+ description: "结束时间(不含)"
46
+ },
47
+ page: {
48
+ type: "string",
49
+ default: "1"
50
+ },
51
+ size: {
52
+ type: "string",
53
+ default: "10"
54
+ },
55
+ "api-base": { type: "string" },
56
+ format: {
57
+ type: "string",
58
+ default: "toon"
59
+ }
60
+ },
61
+ async run({ args }) {
62
+ try {
63
+ const config = resolveConfig({ apiBase: args["api-base"] });
64
+ const active = await requireActiveProject();
65
+ writeResult(await xbApiFetch(config, "POST", def.path, {
66
+ headers: tenantProjectHeaders(active),
67
+ body: {
68
+ visitIds: csvToArray(args["visit-ids"]),
69
+ customerIds: csvToArray(args["customer-ids"]),
70
+ beautxCustomerIds: csvToArray(args["beautx-customer-ids"]),
71
+ latestOnly: def.supportsLatestOnly ? args["latest-only"] : void 0,
72
+ dimensionName: def.supportsDimension ? args.dimension : void 0,
73
+ fromDate: toLocalDateTime(args.from),
74
+ toDate: toLocalDateTime(args.to),
75
+ page: Number(args.page),
76
+ size: Number(args.size)
77
+ }
78
+ }), args.format);
79
+ } catch (err) {
80
+ writeError(err, args.format);
81
+ process.exit(1);
82
+ }
83
+ }
84
+ });
85
+ }
86
+ //#endregion
87
+ export { defineBeautxCommand as t };
@@ -0,0 +1,277 @@
1
+ import { n as writeResult, t as writeError } from "./format-bDFUVuDQ.mjs";
2
+ import { defineCommand } from "citty";
3
+ //#region src/core/date-util.ts
4
+ /**
5
+ * Local date/time helpers for `wow-xiaobao 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 `wow-xiaobao 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,14 @@
1
+ import { defineCommand } from "citty";
2
+ //#region src/commands/staff/index.ts
3
+ var staff_default = defineCommand({
4
+ meta: {
5
+ name: "staff",
6
+ description: "查看和调用员工工具"
7
+ },
8
+ subCommands: {
9
+ list: () => import("./list-BVk58WIy.mjs").then((m) => m.default),
10
+ call: () => import("./call-BfT4uEa7.mjs").then((m) => m.default)
11
+ }
12
+ });
13
+ //#endregion
14
+ export { staff_default as default };
@@ -0,0 +1,59 @@
1
+ import { n as xbApiFetch } from "./config-DqUxKQ3m.mjs";
2
+ //#region src/core/staff-client.ts
3
+ /** Employee REST facade. Business payloads and errors retain the original staff CLI contract. */
4
+ const STAFF_TOOLS_PATH = "/ai-open/staff/tools";
5
+ function object(value) {
6
+ return value !== null && typeof value === "object" && !Array.isArray(value);
7
+ }
8
+ function unwrap(response) {
9
+ const result = response.data;
10
+ if (!response.ok) throw Object.assign(/* @__PURE__ */ new Error(`员工接口请求失败(HTTP ${response.status})`), {
11
+ code: "API_ERROR",
12
+ status: response.status,
13
+ details: result
14
+ });
15
+ if (!object(result) || String(result.code) !== "0") throw Object.assign(new Error(object(result) ? String(result.msg ?? "员工接口请求失败") : "员工接口响应格式错误"), {
16
+ code: "API_ERROR",
17
+ details: result
18
+ });
19
+ if (typeof result.data === "string") throw Object.assign(new Error(result.data), {
20
+ code: "ACCESS_DENIED",
21
+ details: result
22
+ });
23
+ return result.data;
24
+ }
25
+ async function fetchStaffTools(config, headers) {
26
+ const data = unwrap(await xbApiFetch(config, "GET", STAFF_TOOLS_PATH, {
27
+ headers,
28
+ timeoutMs: 9e4
29
+ }));
30
+ if (!object(data) || !Array.isArray(data.tools) || data.tools.some((tool) => !object(tool) || typeof tool.name !== "string" || !tool.name.startsWith("staff-"))) throw Object.assign(/* @__PURE__ */ new Error("员工工具目录响应格式错误"), {
31
+ code: "INVALID_API_RESPONSE",
32
+ details: data
33
+ });
34
+ return data.tools;
35
+ }
36
+ async function callStaffTool(config, headers, tool, variables, timeRange) {
37
+ const data = unwrap(await xbApiFetch(config, "POST", `${STAFF_TOOLS_PATH}/${encodeURIComponent(tool)}/call`, {
38
+ headers,
39
+ body: {
40
+ variables,
41
+ ...timeRange?.start || timeRange?.end ? { timeRange } : {}
42
+ },
43
+ timeoutMs: 9e4
44
+ }));
45
+ if (!object(data) || typeof data.isError !== "boolean" || !("payload" in data)) throw Object.assign(/* @__PURE__ */ new Error("员工工具响应格式错误"), {
46
+ code: "INVALID_API_RESPONSE",
47
+ details: data
48
+ });
49
+ if (data.isError) {
50
+ const error = data.payload;
51
+ throw Object.assign(new Error(object(error) ? String(error.message ?? error.msg ?? "员工工具调用失败") : "员工工具调用失败"), {
52
+ code: "STAFF_TOOL_ERROR",
53
+ details: error
54
+ });
55
+ }
56
+ return data.payload;
57
+ }
58
+ //#endregion
59
+ export { fetchStaffTools as n, callStaffTool as t };
@@ -0,0 +1,24 @@
1
+ import { n as isAccessTokenValid, r as readToken, t as decodeJwtPayload } from "./token-store-BGLVc825.mjs";
2
+ import { defineCommand } from "citty";
3
+ //#region src/commands/auth/status.ts
4
+ /**
5
+ * `wow-xiaobao auth status` — 授权状态检测:退出码 0 且输出 "Logged in as ..." 表示
6
+ * 平台放置的访问令牌可用,否则退出码 1。只读本地文件,不发请求。
7
+ */
8
+ var status_default = defineCommand({
9
+ meta: {
10
+ name: "status",
11
+ description: "授权状态检测(exit 0 = 令牌可用)"
12
+ },
13
+ async run() {
14
+ const token = await readToken();
15
+ if (!isAccessTokenValid(token)) {
16
+ process.stdout.write("Not authenticated: 请在旺喔 App 的连接器里授权旺小宝\n");
17
+ process.exit(1);
18
+ }
19
+ const c = decodeJwtPayload(token.id_token) ?? {};
20
+ process.stdout.write(`Logged in as ${String(c.name ?? c.preferred_username ?? c.sub ?? "unknown")}\n`);
21
+ }
22
+ });
23
+ //#endregion
24
+ export { status_default as default };
@@ -0,0 +1,21 @@
1
+ import { n as parsePositiveDays, r as addDays, t as defineDateCommand } from "./shared-CdUFV4rc.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 };