@bty/customer-service-cli 0.6.9 → 0.6.11

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,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.11 (2026-08-27)
4
+
5
+ - `workspace points-consumes` 改用 `customer-observability-api` 备库 Cursor 接口,支持 `--cursor`、`--start`、`--end`、`--limit`,可继续读取超过 10000 条的历史积分流水。
6
+
7
+ ## 0.6.10 (2026-08-26)
8
+
9
+ - `context-search`:`query_content` 增加最少 2 个字符的本地校验,避免过短关键词触发服务端全量扫描。
10
+ - `channel-metric backfill`:对齐服务端权限收紧——`--workspace-id` 逐个鉴权,缺省不再采集所有 workspace,改为只采集当前登录 workspace;同时修复不传 `--workspace-id` 时 commander 解析崩溃的问题。
11
+
3
12
  ## 0.6.9 (2026-08-24)
4
13
 
5
14
  - 新增 `channel-metric` 命令组,查询千牛等渠道店铺客服的每日排行指标(`channel_metric_snapshot`):
package/dist/bin.js CHANGED
@@ -1185,8 +1185,14 @@ function registerChannelMetricCommand(program2) {
1185
1185
  }
1186
1186
  });
1187
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) => {
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\uFF0C\u9010\u4E2A\u9274\u6743\uFF09\uFF1B\u4E0D\u4F20 --workspace-id = \u53EA\u91C7\u96C6\u5F53\u524D workspace\uFF08\u767B\u5F55\u6001\u4E0B\uFF0C\u670D\u52A1\u7AEF\u9650\u5236\uFF09"
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(
1190
+ "--workspace-id <workspace_id>",
1191
+ "\u5DE5\u4F5C\u7A7A\u95F4 ID\uFF08\u53EF\u91CD\u590D\u4F20\uFF1B\u7F3A\u7701 = \u53EA\u91C7\u96C6\u5F53\u524D workspace\uFF09",
1192
+ collect,
1193
+ []
1194
+ // 默认空数组:commander 首次解析时 previous 为 undefined,无默认值会崩
1195
+ ).action(async (opts) => {
1190
1196
  try {
1191
1197
  const workspaceIds = opts.workspaceId?.length ? opts.workspaceId : void 0;
1192
1198
  setRuntimeWorkspaceId(workspaceIds?.[0]);
@@ -1252,16 +1258,31 @@ async function getDailyDeduction(opts) {
1252
1258
  }
1253
1259
  );
1254
1260
  }
1261
+ var MAX_POINTS_CURSOR_PAGES = 500;
1255
1262
  async function getPointsConsumes(opts) {
1256
1263
  const request = createRequest();
1257
- return request(
1258
- getCustomerServiceUrl(),
1259
- "/v1/workspace/points_consumes",
1260
- {
1261
- method: "POST",
1262
- body: { page: opts.page, page_size: opts.pageSize, debug: true }
1264
+ const items = [];
1265
+ let cursor = opts.cursor;
1266
+ let fetchedPages = 0;
1267
+ while (fetchedPages < MAX_POINTS_CURSOR_PAGES) {
1268
+ fetchedPages += 1;
1269
+ const page = await request(getObservabilityUrl(), "/v1/points/consumes", {
1270
+ method: "GET",
1271
+ query: {
1272
+ cursor,
1273
+ start_time: opts.startTime,
1274
+ end_time: opts.endTime,
1275
+ limit: opts.limit
1276
+ }
1277
+ });
1278
+ items.push(...page.items);
1279
+ const nextCursor = page.has_more && page.next_cursor ? page.next_cursor : void 0;
1280
+ if (!nextCursor) {
1281
+ return { ...page, items, next_cursor: null, has_more: false, fetched_pages: fetchedPages };
1263
1282
  }
1264
- );
1283
+ cursor = nextCursor;
1284
+ }
1285
+ throw new Error(`\u79EF\u5206\u6D41\u6C34\u5206\u9875\u8D85\u8FC7 ${MAX_POINTS_CURSOR_PAGES} \u9875\uFF0C\u8BF7\u7F29\u5C0F\u65F6\u95F4\u8303\u56F4`);
1265
1286
  }
1266
1287
 
1267
1288
  // src/commands/config.ts
@@ -1359,6 +1380,11 @@ function parseSearchTime(value, fieldName) {
1359
1380
  }
1360
1381
  return parsed;
1361
1382
  }
1383
+ function validateContextSearchQuery(queryContent) {
1384
+ if (queryContent.trim().length < 2) {
1385
+ throw new Error("query_content \u81F3\u5C11\u9700\u8981 2 \u4E2A\u5B57\u7B26");
1386
+ }
1387
+ }
1362
1388
  function validateContextSearchRange(startTime, endTime) {
1363
1389
  if (!startTime || !endTime) return;
1364
1390
  const start = parseSearchTime(startTime, "\u5F00\u59CB\u65F6\u95F4");
@@ -1397,6 +1423,7 @@ function shouldKeepTransferHumanConversation(item, onlyAbnormal) {
1397
1423
  });
1398
1424
  }
1399
1425
  async function contextSearchRecords(opts) {
1426
+ validateContextSearchQuery(opts.queryContent);
1400
1427
  validateContextSearchRange(opts.startTime, opts.endTime);
1401
1428
  const wsId = getWorkspaceId(opts.workspaceId);
1402
1429
  const request = createRequest();
@@ -7499,17 +7526,18 @@ function registerWorkspaceCommand(program2) {
7499
7526
  process.exit(toExitCode(err));
7500
7527
  }
7501
7528
  });
7502
- workspace.command("points-consumes").description("\u5206\u9875\u67E5\u8BE2\u5F53\u524D\u5DE5\u4F5C\u7A7A\u95F4\u7684\u79EF\u5206\u8D26\u6237\u6D88\u8017\u6D41\u6C34\uFF0C\u56FA\u5B9A\u8FD4\u56DE extra \u8C03\u8BD5\u5B57\u6BB5").option("--page <number>", "\u9875\u7801\uFF0C\u9ED8\u8BA4 1", "1").option("--page-size <number>", "\u6BCF\u9875\u6570\u91CF\uFF0C\u9ED8\u8BA4 20", "20").action(async (opts) => {
7529
+ workspace.command("points-consumes").description("\u4F7F\u7528\u6E38\u6807\u67E5\u8BE2\u5F53\u524D\u5DE5\u4F5C\u7A7A\u95F4\u7684\u79EF\u5206\u6D41\u6C34\uFF08\u8BFB\u53D6\u89C2\u6D4B\u670D\u52A1\u5907\u5E93\uFF09").option("--cursor <token>", "\u4E0B\u4E00\u9875\u6E38\u6807").option("--start <ISO>", "\u8D77\u59CB\u65F6\u95F4\uFF08\u542B\uFF09").option("--end <ISO>", "\u7ED3\u675F\u65F6\u95F4\uFF08\u4E0D\u542B\uFF09").option("--limit <number>", "\u6BCF\u6279\u6570\u91CF\uFF0C\u9ED8\u8BA4 100", "100").action(async (opts) => {
7503
7530
  try {
7504
- const page = Number(opts.page);
7505
- const pageSize = Number(opts.pageSize);
7506
- if (!Number.isInteger(page) || page <= 0) {
7507
- throw new Error("--page \u5FC5\u987B\u4E3A\u6B63\u6574\u6570");
7508
- }
7509
- if (!Number.isInteger(pageSize) || pageSize <= 0) {
7510
- throw new Error("--page-size \u5FC5\u987B\u4E3A\u6B63\u6574\u6570");
7531
+ const limit = Number(opts.limit);
7532
+ if (!Number.isInteger(limit) || limit <= 0 || limit > 500) {
7533
+ throw new Error("--limit \u5FC5\u987B\u4E3A 1-500 \u7684\u6574\u6570");
7511
7534
  }
7512
- const data = await getPointsConsumes({ page, pageSize });
7535
+ const data = await getPointsConsumes({
7536
+ cursor: opts.cursor,
7537
+ startTime: opts.start,
7538
+ endTime: opts.end,
7539
+ limit
7540
+ });
7513
7541
  formatOutput({ success: true, data }, program2.opts().table);
7514
7542
  } catch (err) {
7515
7543
  reportCaughtError(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bty/customer-service-cli",
3
- "version": "0.6.9",
3
+ "version": "0.6.11",
4
4
  "description": "AI Customer Service CLI - Agent friendly",
5
5
  "type": "module",
6
6
  "main": "./dist/bin.js",