@bty/customer-service-cli 0.6.7 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.6.9 (2026-08-24)
4
4
 
5
+ - 新增 `channel-metric` 命令组,查询千牛等渠道店铺客服的每日排行指标(`channel_metric_snapshot`):
6
+ - `channel-metric query --workspace-id <ws> --start <date> --end <date> [--agent <id>] [--platform <channel>]`:按 workspace + 日期区间(+ 可选 agent / 渠道)查询指标,按日期×渠道分组输出。
7
+ - `channel-metric backfill --channel <channel> --start <date> --end <date> [--workspace-id <ws>...]`:手动补数据,采集指定日期区间的渠道指标;`--workspace-id` 可重复传,缺省采集所有已开启配置的 workspace。
5
8
  - 新增 `media upload` 命令与 `product supplement` 子命令组,支持商品「补充知识」图片/视频维护,与前端上传行为一致:
6
9
  - `media upload --file <path> --type image|video [--path-prefix /ai/knowledge/supplement] [--dry-run]`:本地图片/视频先经 `/v1/upload_sts` 取 STS 凭证上传到公开 OSS(`resource_bucket`),返回公网 URL;已是 http(s) URL 直接拒绝(无需上传)。上传 key 与前端一致:`${prefix}/${timestamp}_${filename}`。图片上限 10MB、视频上限 200MB,超限/类型不符在发起上传请求前直接失败。
7
10
  - `product supplement list --agent <id> --product-id <id>`:读取并解析商品「补充知识」条目(容错解析数组 / JSON 字符串 / 多层编码)。
@@ -9,6 +12,10 @@
9
12
  - `product supplement append-media --agent <id> --product-id <id> --index <n> [--image ...] [--video ...] [--dry-run] [--backup <path>]`:仅向目标下标条目追加媒体,其它条目原样保留;去重合并,未提供任何媒体时报错。
10
13
  - Dashboard 命令使用客服 API 原域名下的 `/observability` 网关前缀,无需新增地址配置。
11
14
 
15
+ ## 0.6.8 (2026-08-10)
16
+
17
+ - `conversation transfer-human` 新增 `--only-abnormal`:默认仍返回全部转人工会话;开启后仅保留异常会话,排除正常 SOP 转人工、`专家agent无法回答`、`存在无法处理的消息类型:...` 三类正常情况。
18
+
12
19
  ## 0.6.7 (2026-07-30)
13
20
 
14
21
  > 版本号说明:registry 实际发布序列 0.6.2 → 0.6.4 → 0.6.5 → 0.6.6(0.6.3 从未发布,0.6.4–0.6.6 由其它分支占用),故本条目落 0.6.7。
package/README.md CHANGED
@@ -520,7 +520,7 @@ Agent 关联的通用规则知识库(`suffix_type=_common`),与 FAQ / 商
520
520
  | `conversation list [--agent <id>] [--equipment-id <设备ID>] [--user <用户名>] [--start <日期>] [--end <日期>]` | 搜索会话 |
521
521
  | `conversation records <conversation_id> [--page-size N] [--direction prev|next]` | 获取会话聊天记录 |
522
522
  | `conversation context-search --query <文本> [--start <时间>] [--end <时间>]` | 通过上下文内容模糊搜索对话记录(返回匹配消息详情,时间范围最多 3 天) |
523
- | `conversation transfer-human --agent <customer_agent_config_id> --start <时间> --end <时间>` | 查询转人工会话及对应转人工消息记录(时间范围最多 7 天,自动 cursor 翻页) |
523
+ | `conversation transfer-human --agent <customer_agent_config_id> --start <时间> --end <时间> [--only-abnormal]` | 查询转人工会话及对应转人工消息记录(时间范围最多 7 天,自动 cursor 翻页;`--only-abnormal` 仅保留异常会话) |
524
524
 
525
525
 
526
526
  ### Agent 调试 (`debug`)
package/dist/bin.js CHANGED
@@ -526,6 +526,7 @@ async function listActivities(opts) {
526
526
  page_size: opts.pageSize ?? 20
527
527
  };
528
528
  if (opts.search) query.search = opts.search;
529
+ if (opts.state) query.state = opts.state;
529
530
  return request(getCustomerServiceUrl(), `${PATH_PREFIX}/list`, {
530
531
  method: "GET",
531
532
  query
@@ -615,6 +616,11 @@ function parseEnabledFlag(raw) {
615
616
  if (raw === "false") return false;
616
617
  throw new Error("--enabled \u4EC5\u63A5\u53D7 true | false");
617
618
  }
619
+ var VALID_LIST_STATES = ["all", "pending", "ongoing", "ended"];
620
+ function parseStateFlag(raw) {
621
+ if (VALID_LIST_STATES.includes(raw)) return raw;
622
+ throw new Error(`--state \u4EC5\u63A5\u53D7 ${VALID_LIST_STATES.join(" | ")}`);
623
+ }
618
624
  function parseActivityId(raw) {
619
625
  const n = Number(raw);
620
626
  if (!Number.isInteger(n) || n <= 0) {
@@ -627,14 +633,18 @@ function registerActivityCommand(program2) {
627
633
  "\u6D3B\u52A8 V2 \u7BA1\u7406 \u2014\u2014 \u589E\u5220\u6539\u67E5\u6D3B\u52A8\u3001\u6309\u5546\u54C1\u53CD\u67E5\u3001\u5355\u6761\u542F\u7528 toggle\u3001per-tenant V2 \u603B\u5F00\u5173\u3002\u670D\u52A1\u7AEF\uFF1Acustomer-servhub-api /v1/activity*\uFF1B\u5951\u7EA6\u53C2\u8003 docs/devkit/specs/2026-05-28-activity-v2-api-reference.md"
628
634
  );
629
635
  activity.command("list").description(
630
- "\u5217\u51FA\u6D3B\u52A8\uFF08\u5206\u9875 + \u540D\u79F0\u641C\u7D22\uFF09\u3002\u8FD4\u56DE total / v2_activity_enabled / items[]\uFF0Citems \u542B id\u3001activity_name\u3001effective_type\u3001enabled\u3001product_scope\u3001products \u7B49"
631
- ).requiredOption("--agent <id>", "customer_agent_config_id\uFF08agent \u914D\u7F6E ID\uFF0C\u4ECE agent list \u83B7\u53D6\uFF09").option("--page <number>", "\u9875\u7801\uFF08\u22651\uFF09", "1").option("--page-size <number>", "\u6BCF\u9875\u6570\u91CF\uFF081-100\uFF09", "20").option("--search <text>", "\u6309\u6D3B\u52A8\u540D\u6A21\u7CCA\u5339\u914D").action(async (opts) => {
636
+ "\u5217\u51FA\u6D3B\u52A8\uFF08\u5206\u9875 + \u540D\u79F0\u641C\u7D22 + \u72B6\u6001\u8FC7\u6EE4\uFF09\u3002\u8FD4\u56DE total / v2_activity_enabled / items[]\uFF0Citems \u542B id\u3001activity_name\u3001effective_type\u3001enabled\u3001product_scope\u3001products \u7B49\uFF1Btotal \u4E0E state/search \u8FC7\u6EE4\u540C\u53E3\u5F84"
637
+ ).requiredOption("--agent <id>", "customer_agent_config_id\uFF08agent \u914D\u7F6E ID\uFF0C\u4ECE agent list \u83B7\u53D6\uFF09").option("--page <number>", "\u9875\u7801\uFF08\u22651\uFF09", "1").option("--page-size <number>", "\u6BCF\u9875\u6570\u91CF\uFF081-100\uFF09", "20").option("--search <text>", "\u6309\u6D3B\u52A8\u540D\u6A21\u7CCA\u5339\u914D").option(
638
+ "--state <state>",
639
+ "\u5C55\u793A\u72B6\u6001\u8FC7\u6EE4\uFF1Aall | pending | ongoing | ended\uFF08\u7F3A\u7701=all\uFF0C\u542B\u65F6\u95F4\u5F02\u5E38\u6761\u76EE\uFF1Blong_term \u6052 ongoing\uFF09"
640
+ ).action(async (opts) => {
632
641
  try {
633
642
  const data = await listActivities({
634
643
  customerAgentConfigId: opts.agent,
635
644
  page: Number(opts.page),
636
645
  pageSize: Number(opts.pageSize),
637
- search: opts.search
646
+ search: opts.search,
647
+ state: opts.state === void 0 ? void 0 : parseStateFlag(opts.state)
638
648
  });
639
649
  formatOutput({ success: true, data }, program2.opts().table);
640
650
  } catch (err) {
@@ -1085,6 +1095,115 @@ function registerChangeConsumerCommand(program2) {
1085
1095
  });
1086
1096
  }
1087
1097
 
1098
+ // src/commands/channel-metric.ts
1099
+ import Table2 from "cli-table3";
1100
+
1101
+ // src/client/channel-metric-api.ts
1102
+ var CHANNEL_METRIC_PREFIX = "/v1/channel-metric";
1103
+ async function backfillChannelMetrics(opts) {
1104
+ const request = createRequest();
1105
+ const body = {
1106
+ channel: opts.channel,
1107
+ start_date: opts.startDate,
1108
+ end_date: opts.endDate
1109
+ };
1110
+ if (opts.workspaceIds && opts.workspaceIds.length > 0) {
1111
+ body.workspace_ids = opts.workspaceIds;
1112
+ }
1113
+ return request(getObservabilityUrl(), `${CHANNEL_METRIC_PREFIX}/backfill`, {
1114
+ method: "POST",
1115
+ body
1116
+ });
1117
+ }
1118
+ async function queryChannelMetrics(opts) {
1119
+ const request = createRequest();
1120
+ const query = {
1121
+ workspace_id: opts.workspaceId,
1122
+ start_date: opts.startDate,
1123
+ end_date: opts.endDate
1124
+ };
1125
+ if (opts.configId) query.config_id = opts.configId;
1126
+ if (opts.channel) query.channel = opts.channel;
1127
+ return request(getObservabilityUrl(), `${CHANNEL_METRIC_PREFIX}/metrics`, {
1128
+ method: "GET",
1129
+ query
1130
+ });
1131
+ }
1132
+
1133
+ // src/commands/channel-metric.ts
1134
+ function collect(value, previous) {
1135
+ return previous.concat([value]);
1136
+ }
1137
+ function renderGroupedTable(groups) {
1138
+ let total = 0;
1139
+ for (const group of groups) {
1140
+ process.stdout.write(`
1141
+ === ${group.stat_date} \xB7 ${group.channel}\uFF08${group.items.length} \u4E2A\u8D26\u53F7\uFF09===
1142
+ `);
1143
+ const first = group.items[0];
1144
+ const headers = (first?.metrics_json ?? []).map((field) => field.label);
1145
+ const table = new Table2({ head: ["account_id", ...headers] });
1146
+ for (const row of group.items) {
1147
+ table.push([
1148
+ String(row.account_id),
1149
+ ...row.metrics_json.map(
1150
+ (field) => field.value === void 0 || field.value === null ? "-" : String(field.value)
1151
+ )
1152
+ ]);
1153
+ }
1154
+ process.stdout.write(`${table.toString()}
1155
+ `);
1156
+ total += group.items.length;
1157
+ }
1158
+ process.stdout.write(`
1159
+ \u5171 ${total} \u6761\u8BB0\u5F55
1160
+ `);
1161
+ }
1162
+ function registerChannelMetricCommand(program2) {
1163
+ const channelMetric = program2.command("channel-metric").description("\u6E20\u9053\u76D1\u63A7\u6307\u6807\u67E5\u8BE2 \u2014\u2014 \u67E5\u8BE2\u5343\u725B\u7B49\u6E20\u9053\u5E97\u94FA\u5BA2\u670D\u7684\u6BCF\u65E5\u6392\u884C\u6307\u6807\uFF08channel_metric_snapshot\uFF09");
1164
+ channelMetric.command("query").description(
1165
+ "\u6309 workspace + \u65E5\u671F\u533A\u95F4\uFF08+ \u53EF\u9009 agent / \u6E20\u9053\uFF09\u67E5\u8BE2\u6307\u6807\uFF0C\u6309\u65E5\u671F\xD7\u6E20\u9053\u5206\u7EC4\u8F93\u51FA\u3002\u4E0D\u4F20 --agent \u8F93\u51FA\u8BE5 workspace \u4E0B\u5168\u90E8 agent\uFF0C\u4E0D\u4F20 --platform \u8F93\u51FA\u5168\u90E8\u6E20\u9053"
1166
+ ).requiredOption("--workspace-id <workspace_id>", "\u5DE5\u4F5C\u7A7A\u95F4 ID\uFF08\u5FC5\u4F20\uFF0C\u4E0E\u670D\u52A1\u7AEF\u5F52\u5C5E\u6821\u9A8C\u4E00\u81F4\uFF09").option("--agent <agent_id>", "Agent \u914D\u7F6E ID\uFF1B\u7F3A\u7701\u67E5\u8BE2\u8BE5 workspace \u4E0B\u5168\u90E8 agent").requiredOption("--start <date>", "\u8D77\u59CB\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").option("--platform <channel>", "\u6E20\u9053\uFF08platform\uFF09\uFF0C\u5982 qianniu\uFF1B\u7F3A\u7701\u67E5\u8BE2\u5168\u90E8\u6E20\u9053").action(async (opts) => {
1167
+ try {
1168
+ setRuntimeWorkspaceId(opts.workspaceId);
1169
+ const data = await queryChannelMetrics({
1170
+ workspaceId: opts.workspaceId,
1171
+ configId: opts.agent,
1172
+ startDate: opts.start,
1173
+ endDate: opts.end,
1174
+ channel: opts.platform
1175
+ });
1176
+ const groups = data?.groups ?? [];
1177
+ if (groups.length === 0) {
1178
+ process.stdout.write("(empty)\n");
1179
+ return;
1180
+ }
1181
+ renderGroupedTable(groups);
1182
+ } catch (err) {
1183
+ reportCaughtError(err);
1184
+ process.exit(toExitCode(err));
1185
+ }
1186
+ });
1187
+ channelMetric.command("backfill").description(
1188
+ "\u624B\u52A8\u8865\u6570\u636E\uFF1A\u91C7\u96C6\u6307\u5B9A\u65E5\u671F\u533A\u95F4\u7684\u6E20\u9053\u6307\u6807\u3002\u53EF\u6307\u5B9A workspace\uFF08\u53EF\u91CD\u590D\u4F20\uFF09\uFF1B\u4E0D\u4F20 --workspace-id = \u91C7\u96C6\u6240\u6709\u5DF2\u5F00\u542F\u914D\u7F6E\u7684 workspace"
1189
+ ).requiredOption("--channel <channel>", "\u6E20\u9053\uFF08platform\uFF09\uFF0C\u5982 qianniu").requiredOption("--start <date>", "\u8D77\u59CB\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").option("--workspace-id <workspace_id>", "\u5DE5\u4F5C\u7A7A\u95F4 ID\uFF08\u53EF\u91CD\u590D\u4F20\uFF1B\u7F3A\u7701 = \u91C7\u96C6\u6240\u6709\u5DF2\u5F00\u542F\u914D\u7F6E\u7684 ws\uFF09", collect).action(async (opts) => {
1190
+ try {
1191
+ const workspaceIds = opts.workspaceId?.length ? opts.workspaceId : void 0;
1192
+ setRuntimeWorkspaceId(workspaceIds?.[0]);
1193
+ const data = await backfillChannelMetrics({
1194
+ channel: opts.channel,
1195
+ startDate: opts.start,
1196
+ endDate: opts.end,
1197
+ workspaceIds
1198
+ });
1199
+ formatOutput({ success: true, data }, program2.opts().table);
1200
+ } catch (err) {
1201
+ reportCaughtError(err);
1202
+ process.exit(toExitCode(err));
1203
+ }
1204
+ });
1205
+ }
1206
+
1088
1207
  // src/client/chat-history-api.ts
1089
1208
  async function listChatHistory(opts) {
1090
1209
  const request = createRequest();
@@ -1228,6 +1347,11 @@ function registerConfigCommand(program2) {
1228
1347
  var MAX_CONTEXT_SEARCH_RANGE_MS = 3 * 24 * 60 * 60 * 1e3;
1229
1348
  var MAX_TRANSFER_HUMAN_RANGE_MS = 7 * 24 * 60 * 60 * 1e3;
1230
1349
  var MAX_TRANSFER_HUMAN_CURSOR_PAGES = 500;
1350
+ var NORMAL_TRANSFER_EVENTS = /* @__PURE__ */ new Set([
1351
+ "\u9700\u8981\u4EBA\u5DE5\u63A5\u7BA1, SOP\u5F15\u64CE\u8BF7\u6C42\u8F6C\u4EBA\u5DE5\u5904\u7406",
1352
+ "\u9700\u8981\u4EBA\u5DE5\u63A5\u7BA1, \u4E13\u5BB6agent\u65E0\u6CD5\u56DE\u7B54"
1353
+ ]);
1354
+ var NORMAL_TRANSFER_EVENT_PREFIXES = ["\u9700\u8981\u4EBA\u5DE5\u63A5\u7BA1, \u5B58\u5728\u65E0\u6CD5\u5904\u7406\u7684\u6D88\u606F\u7C7B\u578B\uFF1A"];
1231
1355
  function parseSearchTime(value, fieldName) {
1232
1356
  const parsed = new Date(value);
1233
1357
  if (Number.isNaN(parsed.getTime())) {
@@ -1256,6 +1380,22 @@ function validateTransferHumanRange(startTime, endTime) {
1256
1380
  throw new Error("\u67E5\u8BE2\u65F6\u95F4\u8303\u56F4\u4E0D\u80FD\u8D85\u8FC77\u5929");
1257
1381
  }
1258
1382
  }
1383
+ function shouldKeepTransferHumanConversation(item, onlyAbnormal) {
1384
+ if (!onlyAbnormal) return true;
1385
+ if (!item || typeof item !== "object") return true;
1386
+ const takeoverEvents = item.takeover_event_record;
1387
+ if (!Array.isArray(takeoverEvents) || takeoverEvents.length === 0) return true;
1388
+ const eventNames = takeoverEvents.map((event) => {
1389
+ if (!event || typeof event !== "object") return void 0;
1390
+ const eventName = event.event_name;
1391
+ return typeof eventName === "string" ? eventName : void 0;
1392
+ }).filter((eventName) => typeof eventName === "string");
1393
+ if (eventNames.length === 0) return true;
1394
+ return eventNames.some((eventName) => {
1395
+ if (NORMAL_TRANSFER_EVENTS.has(eventName)) return false;
1396
+ return !NORMAL_TRANSFER_EVENT_PREFIXES.some((prefix) => eventName.startsWith(prefix));
1397
+ });
1398
+ }
1259
1399
  async function contextSearchRecords(opts) {
1260
1400
  validateContextSearchRange(opts.startTime, opts.endTime);
1261
1401
  const wsId = getWorkspaceId(opts.workspaceId);
@@ -1320,7 +1460,8 @@ async function listTransferHumanConversations(opts) {
1320
1460
  }
1321
1461
  }
1322
1462
  );
1323
- items.push(...page.items ?? []);
1463
+ const pageItems = page.items ?? [];
1464
+ items.push(...pageItems.filter((item) => shouldKeepTransferHumanConversation(item, opts.onlyAbnormal ?? false)));
1324
1465
  pagination = page.pagination ?? {};
1325
1466
  const hasMore = pagination.has_more === true;
1326
1467
  const nextCursor = typeof pagination.next_cursor === "string" && pagination.next_cursor ? pagination.next_cursor : void 0;
@@ -1385,12 +1526,13 @@ function registerConversationCommand(program2) {
1385
1526
  process.exit(toExitCode(err));
1386
1527
  }
1387
1528
  });
1388
- conversation.command("transfer-human").description("\u67E5\u8BE2\u8F6C\u4EBA\u5DE5\u4F1A\u8BDD\u53CA\u5BF9\u5E94\u8F6C\u4EBA\u5DE5\u6D88\u606F\u8BB0\u5F55").requiredOption("--agent <config_id>", "Agent \u914D\u7F6E ID\uFF08customer_agent_config_id\uFF09").requiredOption("--start <date>", "\u5F00\u59CB\u65F6\u95F4\uFF08ISO \u683C\u5F0F\uFF0C\u5982 2026-05-20T00:00:00\uFF09").requiredOption("--end <date>", "\u7ED3\u675F\u65F6\u95F4\uFF08ISO \u683C\u5F0F\uFF0C\u5982 2026-05-20T23:59:59\uFF09").action(async (opts) => {
1529
+ conversation.command("transfer-human").description("\u67E5\u8BE2\u8F6C\u4EBA\u5DE5\u4F1A\u8BDD\u53CA\u5BF9\u5E94\u8F6C\u4EBA\u5DE5\u6D88\u606F\u8BB0\u5F55\uFF08\u53EF\u9009\u4EC5\u4FDD\u7559\u5F02\u5E38\u4F1A\u8BDD\uFF09").requiredOption("--agent <config_id>", "Agent \u914D\u7F6E ID\uFF08customer_agent_config_id\uFF09").requiredOption("--start <date>", "\u5F00\u59CB\u65F6\u95F4\uFF08ISO \u683C\u5F0F\uFF0C\u5982 2026-05-20T00:00:00\uFF09").requiredOption("--end <date>", "\u7ED3\u675F\u65F6\u95F4\uFF08ISO \u683C\u5F0F\uFF0C\u5982 2026-05-20T23:59:59\uFF09").option("--only-abnormal", "\u4EC5\u4FDD\u7559\u5F02\u5E38\u4F1A\u8BDD\uFF08\u6392\u9664\u6B63\u5E38 SOP \u8F6C\u4EBA\u5DE5\u3001agent \u65E0\u6CD5\u56DE\u7B54\u3001\u8F93\u5165/\u6D88\u606F\u7C7B\u578B\u5F02\u5E38\uFF09", false).action(async (opts) => {
1389
1530
  try {
1390
1531
  const data = await listTransferHumanConversations({
1391
1532
  customerAgentConfigId: opts.agent,
1392
1533
  startDate: opts.start,
1393
- endDate: opts.end
1534
+ endDate: opts.end,
1535
+ onlyAbnormal: opts.onlyAbnormal
1394
1536
  });
1395
1537
  formatOutput({ success: true, data }, program2.opts().table);
1396
1538
  } catch (err) {
@@ -1575,7 +1717,7 @@ async function writeBinaryToFile(filePath, buffer) {
1575
1717
  }
1576
1718
 
1577
1719
  // src/commands/dashboard.ts
1578
- function collect(value, previous) {
1720
+ function collect2(value, previous) {
1579
1721
  return previous.concat([value]);
1580
1722
  }
1581
1723
  function registerDashboardCommand(program2) {
@@ -1630,7 +1772,7 @@ function registerDashboardCommand(program2) {
1630
1772
  });
1631
1773
  dashboard.command("export").description(
1632
1774
  "\u5BFC\u51FA\u5E97\u94FA\u7EF4\u5EA6\u6BCF\u65E5\u660E\u7EC6\uFF08\u63A5\u5F85\u5BA2\u6237\u6570/AI\u72EC\u7ACB\u627F\u63A5/\u8F6C\u4EBA\u5DE5/AI\u56DE\u590D\u6D88\u606F\u6570\uFF09\u3002\u9ED8\u8BA4\u89E3\u6790\u4E3A JSON \u884C\uFF1B--out \u843D\u76D8\u539F\u59CB CSV\u3002\u53EF\u91CD\u590D --config-id \u9650\u5B9A\u5E97\u94FA"
1633
- ).requiredOption("--start <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u5FC5\u586B").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u5FC5\u586B\uFF0C\u4E0D\u80FD\u665A\u4E8E\u6628\u5929").option("--config-id <id>", "\u5E97\u94FA\u914D\u7F6E ID\uFF08\u53EF\u91CD\u590D\u4F20\uFF1B\u4E0D\u4F20=\u5168\u90E8\u5E97\u94FA\uFF09", collect, []).option("--channel <channel>", "\u6E20\u9053\u7B5B\u9009: qianniu | jingdong | pinduoduo | douyin").option("--out <path>", "\u843D\u76D8\u539F\u59CB CSV \u8DEF\u5F84\uFF08\u7236\u76EE\u5F55\u81EA\u52A8 mkdir -p\uFF09").action(async (opts) => {
1775
+ ).requiredOption("--start <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u5FC5\u586B").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u5FC5\u586B\uFF0C\u4E0D\u80FD\u665A\u4E8E\u6628\u5929").option("--config-id <id>", "\u5E97\u94FA\u914D\u7F6E ID\uFF08\u53EF\u91CD\u590D\u4F20\uFF1B\u4E0D\u4F20=\u5168\u90E8\u5E97\u94FA\uFF09", collect2, []).option("--channel <channel>", "\u6E20\u9053\u7B5B\u9009: qianniu | jingdong | pinduoduo | douyin").option("--out <path>", "\u843D\u76D8\u539F\u59CB CSV \u8DEF\u5F84\uFF08\u7236\u76EE\u5F55\u81EA\u52A8 mkdir -p\uFF09").action(async (opts) => {
1634
1776
  try {
1635
1777
  const result = await exportShopData({
1636
1778
  startDate: opts.start,
@@ -7437,6 +7579,7 @@ registerMediaCommand(program);
7437
7579
  registerKnowledgeCommand(program);
7438
7580
  registerActivityCommand(program);
7439
7581
  registerIssueCommand(program);
7582
+ registerChannelMetricCommand(program);
7440
7583
  registerChatHistoryCommand(program);
7441
7584
  registerFaqCommand(program);
7442
7585
  registerConversationCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bty/customer-service-cli",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "AI Customer Service CLI - Agent friendly",
5
5
  "type": "module",
6
6
  "main": "./dist/bin.js",