@bty/customer-service-cli 0.5.3 → 0.5.4

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,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.4 (2026-06-03)
4
+
5
+ - `ops-agent` 新增 `conversations` 子命令:`ops-agent conversations [--agent-id <id>] [--customer-id <id>] [--page N] [--page-size N]`,对接 `GET /v1/ops-agent/conversations`,按当前 workspace 查询客服助手会话列表,返回 `workspace_id`、`customer_id`、`title`、`agent_id`、`created_at`、`updated_at`。
6
+ - 补充 `ops-agent` 命令注册测试与 API 客户端测试,覆盖会话列表查询命令注册、请求路径、workspace 透传与筛选参数。
7
+ - `product` 新增 `identify-all` 子命令:`product identify-all --agent <id>`,对接 `POST /v1/knowledge/products/identify/all`,为当前 Agent 一键提交所有待增强商品进入增强学习队列。
8
+ - 补充 `product` CLI 测试与 API 客户端测试,覆盖增强学习命令注册、请求路径与请求体。
9
+ - `testset` 新增 `create` 子命令:`testset create --customer-agent-config-id <id> --file <cases.xlsx> [--name <text>] [--description <text>] [--use-type <text>]`,对接 `POST /v1/test_sets/file/upload`,通过 xlsx 模板上传创建测试集。
10
+
3
11
  ## 0.5.3 (2026-05-28)
4
12
 
5
13
  - 新增 `activity` 主语,覆盖活动 V2(Issue #34)增删改查 + per-tenant 总开关。对接 `customer-servhub-api /v1/activity*`,契约详情参见 [`docs/devkit/specs/2026-05-28-activity-v2-api-reference.md`](../../docs/devkit/specs/2026-05-28-activity-v2-api-reference.md):
package/README.md CHANGED
@@ -224,6 +224,7 @@ cs-cli workspace points-consumes-daily --start 2026-04-01 --end 2026-04-23
224
224
  | `product update-sku --agent <id> (--sku <SKU名称> \| --sku-id <id>) --update <json\|@file>` | 更新 SKU 信息(`--sku-id` 优先于 `--sku`) |
225
225
  | `product add --agent <id> --data <json\|@file> [--source <type>] [--no-identify]` | 直接传入结构化商品数据(JSON 数组)异步学习(manual / identify 默认开启) |
226
226
  | `product learn --agent <id> --url <URL...> [--source <type>] [--no-identify]` | 通过商品 URL 异步学习(manual / identify 默认开启) |
227
+ | `product identify-all --agent <id>` | 一键增强学习当前 Agent 下所有待增强商品 |
227
228
  | `product sync-taobao --agent <id> [--skip-hash-check] [--sync-type <full\|incremental>]` | 触发淘宝店铺商品同步 |
228
229
  | `product sync-taobao-item --agent <id> --product-id <商品ID>` | 触发单个淘宝商品同步 |
229
230
 
@@ -233,6 +234,8 @@ cs-cli workspace points-consumes-daily --start 2026-04-01 --end 2026-04-23
233
234
 
234
235
  `product learn` 提交商品详情页 URL 进入异步学习队列(`/v1/knowledge/products/upload`),返回 `flow_id` 和 `knowledge_ids`。`--url` 可多值,用空格分隔;`--source` 默认 `manual`;传 `--no-identify` 可关闭自动识别。
235
236
 
237
+ `product identify-all` 对接 `POST /v1/knowledge/products/identify/all`,会为当前 Agent 一键提交所有“已可用但尚未增强”的商品进入增强学习队列。它不是全量重学;当没有待增强商品时,服务端会返回“暂无需要识图的商品”。
238
+
236
239
  `product sync-taobao` / `product sync-taobao-item` 调用 customer-servhub-api 的授权店铺同步接口,要求 `--agent` 已唯一绑定一个淘宝授权店铺。前者支持 `--sync-type full|incremental`,默认 `full`;后者按 `product_id` 补同步单个淘宝商品。
237
240
 
238
241
  注意:这两个命令仅对已开通“高级工具”的店铺 Agent 生效。未开通高级工具时,即使 `--agent` 已绑定淘宝授权店铺,请求也可能在服务端失败。
@@ -578,6 +581,7 @@ cs-cli change-consumer delivery complete <delivery_id> --status completed \
578
581
  | 命令 | 说明 |
579
582
  | --- | --- |
580
583
  | `testset list --customer-agent-config-id <id>` | 列出测试集(分页,Agent ID 必填) |
584
+ | `testset create --customer-agent-config-id <id> --file <cases.xlsx> [--name ...] [--description ...] [--use-type ...]` | 通过 xlsx 模板上传创建测试集 |
581
585
  | `testset show <id>` | 测试集详情 |
582
586
  | `testset update <id> [--name ...] [--description ...] [--data @file.json]` | 更新测试集元数据(至少一项) |
583
587
  | `testset delete <id>` | 软删除测试集 |
@@ -827,3 +831,19 @@ cs-cli ops-record update <record_id> --data '{"remark":"已完成配置更新","
827
831
  # 删除运维操作记录
828
832
  cs-cli ops-record delete <record_id>
829
833
  ```
834
+
835
+ ### Ops Agent (`ops-agent`)
836
+
837
+ | 命令 | 说明 |
838
+ | --- | --- |
839
+ | `ops-agent conversations [--agent-id <id>] [--customer-id <id>] [--page N] [--page-size N]` | 查询客服助手会话列表 |
840
+
841
+ `ops-agent conversations` 对接 `customer-servhub-api /v1/ops-agent/conversations`,返回字段固定为 `workspace_id`、`customer_id`、`title`、`agent_id`、`created_at`、`updated_at`。命令会自动带当前 `workspace-id`;`--agent-id` 对应 Ops Agent 配置 ID(`customer_agent_config_id`),`--customer-id` 可选。
842
+
843
+ ```bash
844
+ # 查询当前 workspace 下某个客服运维助手的会话
845
+ cs-cli ops-agent conversations --agent-id <customer_agent_config_id>
846
+
847
+ # 按 customer 进一步过滤
848
+ cs-cli ops-agent conversations --agent-id <customer_agent_config_id> --customer-id <customer_id>
849
+ ```
package/dist/bin.js CHANGED
@@ -276,7 +276,7 @@ function toExitCode(err) {
276
276
  return 1;
277
277
  }
278
278
  function createRequest(globalTimeout) {
279
- return async function request(baseUrl, path5, options) {
279
+ return async function request(baseUrl, path6, options) {
280
280
  const headers = {
281
281
  "Content-Type": "application/json",
282
282
  ...options.headers
@@ -304,7 +304,7 @@ function createRequest(globalTimeout) {
304
304
  if (workspaceId) {
305
305
  headers["workspace-id"] = workspaceId;
306
306
  }
307
- let url = `${baseUrl}${path5}`;
307
+ let url = `${baseUrl}${path6}`;
308
308
  if (options.query) {
309
309
  const params = new URLSearchParams();
310
310
  for (const [key, value] of Object.entries(options.query)) {
@@ -1343,22 +1343,22 @@ function registerConversationCommand(program2) {
1343
1343
  var DASHBOARD_PREFIX = "/v1/dashboard";
1344
1344
  async function getDashboardSummary(opts) {
1345
1345
  const request = createRequest();
1346
- const path5 = opts.configId ? `${DASHBOARD_PREFIX}/${opts.configId}/summary` : `${DASHBOARD_PREFIX}/summary`;
1346
+ const path6 = opts.configId ? `${DASHBOARD_PREFIX}/${opts.configId}/summary` : `${DASHBOARD_PREFIX}/summary`;
1347
1347
  const query = {};
1348
1348
  if (opts.startDate) query.start_date = opts.startDate;
1349
1349
  if (opts.endDate) query.end_date = opts.endDate;
1350
1350
  if (opts.channel) query.channel = opts.channel;
1351
- return request(getCustomerServiceUrl(), path5, { method: "GET", query });
1351
+ return request(getCustomerServiceUrl(), path6, { method: "GET", query });
1352
1352
  }
1353
1353
  async function getDashboardTrend(opts) {
1354
1354
  const request = createRequest();
1355
- const path5 = opts.configId ? `${DASHBOARD_PREFIX}/${opts.configId}/trend` : `${DASHBOARD_PREFIX}/trend`;
1355
+ const path6 = opts.configId ? `${DASHBOARD_PREFIX}/${opts.configId}/trend` : `${DASHBOARD_PREFIX}/trend`;
1356
1356
  const query = {};
1357
1357
  if (opts.startDate) query.start_date = opts.startDate;
1358
1358
  if (opts.endDate) query.end_date = opts.endDate;
1359
1359
  if (opts.granularity) query.granularity = opts.granularity;
1360
1360
  if (opts.channel) query.channel = opts.channel;
1361
- return request(getCustomerServiceUrl(), path5, { method: "GET", query });
1361
+ return request(getCustomerServiceUrl(), path6, { method: "GET", query });
1362
1362
  }
1363
1363
  async function listShopStatistics(opts) {
1364
1364
  const request = createRequest();
@@ -1578,12 +1578,12 @@ function registerDashboardCommand(program2) {
1578
1578
  channel: opts.channel
1579
1579
  });
1580
1580
  if (opts.out) {
1581
- const { path: path5, bytes } = await writeBinaryToFile(
1581
+ const { path: path6, bytes } = await writeBinaryToFile(
1582
1582
  opts.out,
1583
1583
  Buffer.from(result.csv, "utf-8")
1584
1584
  );
1585
1585
  formatOutput(
1586
- { success: true, data: { path: path5, bytes, count: result.rows.length } },
1586
+ { success: true, data: { path: path6, bytes, count: result.rows.length } },
1587
1587
  program2.opts().table
1588
1588
  );
1589
1589
  } else {
@@ -3769,6 +3769,44 @@ function registerMonitorCommand(program2) {
3769
3769
  });
3770
3770
  }
3771
3771
 
3772
+ // src/client/ops-agent-api.ts
3773
+ async function listOpsAgentConversations(opts) {
3774
+ const workspaceId = getWorkspaceId(opts.workspaceId);
3775
+ const request = createRequest();
3776
+ const query = {
3777
+ page: opts.page ?? 1,
3778
+ page_size: opts.pageSize ?? 20
3779
+ };
3780
+ if (opts.customerId) query.customer_id = opts.customerId;
3781
+ if (opts.agentId) query.agent_id = opts.agentId;
3782
+ return request(getCustomerServiceUrl(), "/v1/ops-agent/conversations", {
3783
+ method: "GET",
3784
+ query,
3785
+ headers: { "workspace-id": workspaceId }
3786
+ });
3787
+ }
3788
+
3789
+ // src/commands/ops-agent.ts
3790
+ function registerOpsAgentCommand(program2) {
3791
+ const opsAgent = program2.command("ops-agent").description("Ops Agent\uFF08\u5BA2\u670D\u52A9\u624B\uFF09\u67E5\u8BE2\u547D\u4EE4");
3792
+ opsAgent.command("conversations").description(
3793
+ "\u67E5\u8BE2\u5BA2\u670D\u52A9\u624B\u4F1A\u8BDD\u5217\u8868\u3002\u8FD4\u56DE workspace_id\u3001customer_id\u3001title\u3001agent_id\u3001created_at\u3001updated_at"
3794
+ ).option("--customer-id <id>", "\u6309 customer_id \u7B5B\u9009").option("--agent-id <id>", "\u6309 Ops Agent \u914D\u7F6E ID\uFF08customer_agent_config_id\uFF09\u7B5B\u9009").option("--page <number>", "\u9875\u7801", "1").option("--page-size <number>", "\u6BCF\u9875\u6570\u91CF", "20").action(async (opts) => {
3795
+ try {
3796
+ const data = await listOpsAgentConversations({
3797
+ customerId: opts.customerId,
3798
+ agentId: opts.agentId,
3799
+ page: Number(opts.page),
3800
+ pageSize: Number(opts.pageSize)
3801
+ });
3802
+ formatOutput({ success: true, data }, program2.opts().table);
3803
+ } catch (err) {
3804
+ reportCaughtError(err);
3805
+ process.exit(toExitCode(err));
3806
+ }
3807
+ });
3808
+ }
3809
+
3772
3810
  // src/client/operations-record-api.ts
3773
3811
  var PATH_PREFIX2 = "/v1/agent_operations_records";
3774
3812
  async function listOperationsRecords(opts) {
@@ -3981,6 +4019,15 @@ async function syncSingleTaobaoProduct(opts) {
3981
4019
  }
3982
4020
  });
3983
4021
  }
4022
+ async function identifyAllProducts(opts) {
4023
+ const request = createRequest();
4024
+ return request(getCustomerServiceUrl(), "/v1/knowledge/products/identify/all", {
4025
+ method: "POST",
4026
+ body: {
4027
+ customer_agent_config_id: opts.agentConfigId
4028
+ }
4029
+ });
4030
+ }
3984
4031
 
3985
4032
  // src/commands/product.ts
3986
4033
  async function getDbId(agentConfigId) {
@@ -4118,6 +4165,19 @@ function registerProductCommand(program2) {
4118
4165
  process.exit(toExitCode(err));
4119
4166
  }
4120
4167
  });
4168
+ product.command("identify-all").description(
4169
+ "\u4E00\u952E\u589E\u5F3A\u5B66\u4E60\u5F53\u524D Agent \u4E0B\u6240\u6709\u5F85\u589E\u5F3A\u5546\u54C1\u3002\u5BF9\u63A5 POST /v1/knowledge/products/identify/all\uFF0C\u5F02\u6B65\u63D0\u4EA4\u5546\u54C1\u589E\u5F3A\u5B66\u4E60\u4EFB\u52A1"
4170
+ ).requiredOption("--agent <config_id>", "Agent \u914D\u7F6E ID\uFF08\u4ECE agent list \u83B7\u53D6\uFF09").action(async (opts) => {
4171
+ try {
4172
+ const data = await identifyAllProducts({
4173
+ agentConfigId: opts.agent
4174
+ });
4175
+ formatOutput({ success: true, data }, program2.opts().table);
4176
+ } catch (err) {
4177
+ reportCaughtError(err);
4178
+ process.exit(toExitCode(err));
4179
+ }
4180
+ });
4121
4181
  product.command("sync-taobao").description(
4122
4182
  "\u89E6\u53D1\u6DD8\u5B9D\u5E97\u94FA\u5546\u54C1\u540C\u6B65\u3002\u8C03\u7528 customer-servhub-api \u7684\u6388\u6743\u5E97\u94FA\u540C\u6B65\u63A5\u53E3\uFF0C\u6309 Agent \u914D\u7F6E\u627E\u5230\u552F\u4E00\u7ED1\u5B9A\u5E97\u94FA\u540E\u5F02\u6B65\u542F\u52A8\u540C\u6B65\u4EFB\u52A1"
4123
4183
  ).requiredOption("--agent <config_id>", "Agent \u914D\u7F6E ID\uFF08\u5FC5\u987B\u5DF2\u552F\u4E00\u7ED1\u5B9A\u4E00\u4E2A\u6DD8\u5B9D\u6388\u6743\u5E97\u94FA\uFF09").option("--skip-hash-check", "\u8DF3\u8FC7\u54C8\u5E0C\u6821\u9A8C\uFF0C\u5F3A\u5236\u91CD\u65B0\u540C\u6B65\u5546\u54C1").option("--sync-type <type>", "\u540C\u6B65\u7C7B\u578B\uFF1Afull / incremental\uFF0C\u9ED8\u8BA4 full", "full").action(async (opts) => {
@@ -4467,10 +4527,132 @@ function registerSACommand(program2) {
4467
4527
  });
4468
4528
  }
4469
4529
 
4470
- // src/commands/testset.ts
4530
+ // src/commands/special-project.ts
4471
4531
  import fs9 from "fs";
4472
4532
 
4533
+ // src/client/special-project-api.ts
4534
+ async function createSpecialProject(input) {
4535
+ const request = createRequest();
4536
+ return request(getCsAdminUrl(), "/api/special-projects", {
4537
+ method: "POST",
4538
+ body: input
4539
+ });
4540
+ }
4541
+ async function listSpecialProjects(opts = {}) {
4542
+ const request = createRequest();
4543
+ const query = {};
4544
+ if (opts.ownerUserId !== void 0) query.ownerUserId = opts.ownerUserId;
4545
+ if (opts.status) query.status = opts.status;
4546
+ const result = await request(getCsAdminUrl(), "/api/special-projects", {
4547
+ method: "GET",
4548
+ query
4549
+ });
4550
+ return result.rows ?? [];
4551
+ }
4552
+ async function updateSpecialProject(projectId, patch) {
4553
+ const request = createRequest();
4554
+ return request(
4555
+ getCsAdminUrl(),
4556
+ `/api/special-projects/${projectId}`,
4557
+ { method: "PATCH", body: patch }
4558
+ );
4559
+ }
4560
+ async function closeSpecialProject(projectId) {
4561
+ return updateSpecialProject(projectId, { status: "\u5DF2\u5B8C\u6210" });
4562
+ }
4563
+ async function upsertSpecialProjects(items, apply) {
4564
+ const request = createRequest();
4565
+ return request(getCsAdminUrl(), "/api/special-projects/batch", {
4566
+ method: "POST",
4567
+ body: { items, apply }
4568
+ });
4569
+ }
4570
+
4571
+ // src/commands/special-project.ts
4572
+ function readJsonArg(value) {
4573
+ if (value.startsWith("@")) {
4574
+ return fs9.readFileSync(value.slice(1), "utf-8");
4575
+ }
4576
+ return value;
4577
+ }
4578
+ function registerSpecialProjectCommand(program2) {
4579
+ const sp = program2.command("special-project").description(
4580
+ "\u4E13\u9879\uFF08special_project\uFF09\u7BA1\u7406 \u2014\u2014 \u5DE5\u4F5C\u8D1F\u8F7D\u770B\u677F\u7684\u4E13\u9879\u6570\u636E\u3002\u4E3B\u529B\u5F55\u5165\u901A\u9053\uFF1A\u65E9\u4F1A\u9010\u5B57\u7A3F \u2192 Agent \u89E3\u6790 \u2192 upsert\uFF08\u5148\u770B diff\uFF0C\u786E\u8BA4\u540E --apply\uFF09"
4581
+ );
4582
+ sp.command("create").description("\u521B\u5EFA\u5355\u4E2A\u4E13\u9879").requiredOption("--name <name>", "\u4E13\u9879\u540D").requiredOption("--owner <id>", "\u627F\u63A5\u5DE5\u7A0B\u5E08 cs_admin_user_id").requiredOption("--daily-effort <personDays>", "\u9884\u4F30\u6BCF\u65E5\u6295\u5165\uFF08\u4EBA\u5929\uFF0C\u5982 0.5\uFF09").requiredOption("--start-date <YYYY-MM-DD>", "\u5F00\u59CB\u65E5\u671F").requiredOption("--duration-days <n>", "\u9884\u4F30\u6709\u6548\u6295\u5165\u5929\u6570").option("--workspace <id>", "\u5173\u8054\u5BA2\u6237 workspace_id\uFF08\u53EF\u7A7A\uFF0C\u5185\u90E8\u4E13\u9879\u4E0D\u586B\uFF09").option("--description <text>", "\u63CF\u8FF0").option("--source-ref <ref>", "\u7A33\u5B9A\u5916\u90E8\u952E\uFF08\u9010\u5B57\u7A3F\u6765\u6E90 id\uFF09\uFF0Cupsert \u5E42\u7B49\u7528").action(async (opts) => {
4583
+ try {
4584
+ const data = await createSpecialProject({
4585
+ name: opts.name,
4586
+ ownerUserId: Number(opts.owner),
4587
+ dailyEffort: Number(opts.dailyEffort),
4588
+ startDate: opts.startDate,
4589
+ durationDays: Number(opts.durationDays),
4590
+ workspaceId: opts.workspace,
4591
+ description: opts.description,
4592
+ sourceRef: opts.sourceRef
4593
+ });
4594
+ formatOutput({ success: true, data }, program2.opts().table);
4595
+ } catch (err) {
4596
+ reportCaughtError(err);
4597
+ process.exit(toExitCode(err));
4598
+ }
4599
+ });
4600
+ sp.command("list").description("\u5217\u51FA\u4E13\u9879").option("--owner <id>", "\u6309\u627F\u63A5\u5DE5\u7A0B\u5E08 cs_admin_user_id \u8FC7\u6EE4").option("--status <status>", "\u6309\u72B6\u6001\u8FC7\u6EE4\uFF08\u8FDB\u884C\u4E2D/\u6682\u505C/\u9700\u786E\u8BA4/\u5DF2\u5B8C\u6210\uFF09").action(async (opts) => {
4601
+ try {
4602
+ const data = await listSpecialProjects({
4603
+ ownerUserId: opts.owner !== void 0 ? Number(opts.owner) : void 0,
4604
+ status: opts.status
4605
+ });
4606
+ formatOutput({ success: true, data }, program2.opts().table);
4607
+ } catch (err) {
4608
+ reportCaughtError(err);
4609
+ process.exit(toExitCode(err));
4610
+ }
4611
+ });
4612
+ sp.command("update <id>").description("\u66F4\u65B0\u4E13\u9879\uFF08\u4EFB\u610F\u5B50\u96C6\uFF1B\u72B6\u6001\u8FC1\u79FB\u53D7\u670D\u52A1\u7AEF\u72B6\u6001\u673A\u7EA6\u675F\uFF09").option("--progress <n>", "\u8FDB\u5EA6 0-100").option("--status <status>", "\u72B6\u6001\uFF08\u8FDB\u884C\u4E2D/\u6682\u505C/\u9700\u786E\u8BA4/\u5DF2\u5B8C\u6210\uFF09").option("--daily-effort <personDays>", "\u6BCF\u65E5\u6295\u5165\uFF08\u4EBA\u5929\uFF09").option("--duration-days <n>", "\u6709\u6548\u6295\u5165\u5929\u6570").option("--description <text>", "\u63CF\u8FF0").action(async (id, opts) => {
4613
+ try {
4614
+ const patch = {};
4615
+ if (opts.progress !== void 0) patch.progress = Number(opts.progress);
4616
+ if (opts.status !== void 0) patch.status = opts.status;
4617
+ if (opts.dailyEffort !== void 0) patch.dailyEffort = Number(opts.dailyEffort);
4618
+ if (opts.durationDays !== void 0) patch.durationDays = Number(opts.durationDays);
4619
+ if (opts.description !== void 0) patch.description = opts.description;
4620
+ const data = await updateSpecialProject(Number(id), patch);
4621
+ formatOutput({ success: true, data }, program2.opts().table);
4622
+ } catch (err) {
4623
+ reportCaughtError(err);
4624
+ process.exit(toExitCode(err));
4625
+ }
4626
+ });
4627
+ sp.command("close <id>").description("\u5173\u95ED\u4E13\u9879\uFF08\u7F6E\u4E3A\u5DF2\u5B8C\u6210\uFF09").action(async (id) => {
4628
+ try {
4629
+ const data = await closeSpecialProject(Number(id));
4630
+ formatOutput({ success: true, data }, program2.opts().table);
4631
+ } catch (err) {
4632
+ reportCaughtError(err);
4633
+ process.exit(toExitCode(err));
4634
+ }
4635
+ });
4636
+ sp.command("upsert").description(
4637
+ "\u6279\u91CF upsert\uFF08\u9010\u5B57\u7A3F\u540C\u6B65\uFF09\u3002--items \u63A5 JSON \u6570\u7EC4\u6216 @\u6587\u4EF6\u3002\u9ED8\u8BA4\u53EA\u6253\u5370 diff \u4E0D\u5199\u5E93\uFF1B\u52A0 --apply \u624D\u843D\u5E93"
4638
+ ).requiredOption("--items <json|@file>", "items JSON \u6570\u7EC4\uFF0C\u6216 @path \u8BFB\u6587\u4EF6").option("--apply", "\u843D\u5E93\uFF08\u7F3A\u7701\u53EA\u7B97 diff \u4E0D\u5199\uFF09", false).action(async (opts) => {
4639
+ try {
4640
+ const items = JSON.parse(readJsonArg(opts.items));
4641
+ const data = await upsertSpecialProjects(items, opts.apply === true);
4642
+ formatOutput({ success: true, data }, program2.opts().table);
4643
+ } catch (err) {
4644
+ reportCaughtError(err);
4645
+ process.exit(toExitCode(err));
4646
+ }
4647
+ });
4648
+ }
4649
+
4650
+ // src/commands/testset.ts
4651
+ import fs11 from "fs";
4652
+
4473
4653
  // src/client/testset-api.ts
4654
+ import fs10 from "fs";
4655
+ import path5 from "path";
4474
4656
  function unwrapPaginated(raw, fallbackPageSize) {
4475
4657
  return {
4476
4658
  items: Array.isArray(raw?.data) ? raw?.data : [],
@@ -4496,6 +4678,28 @@ async function listTestSets(query) {
4496
4678
  );
4497
4679
  return unwrapPaginated(raw, query.pageSize ?? 20);
4498
4680
  }
4681
+ async function createTestSetFromFile(input) {
4682
+ const request = createRequest();
4683
+ const fileBytes = fs10.readFileSync(input.filePath);
4684
+ const formData = new FormData();
4685
+ formData.append(
4686
+ "file",
4687
+ new Blob([fileBytes], {
4688
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
4689
+ }),
4690
+ path5.basename(input.filePath)
4691
+ );
4692
+ return request(getCustomerServiceUrl(), "/v1/test_sets/file/upload", {
4693
+ method: "POST",
4694
+ query: {
4695
+ customer_agent_config_id: input.customerAgentConfigId,
4696
+ test_set_name: input.testSetName,
4697
+ test_set_description: input.testSetDescription,
4698
+ use_type: input.useType
4699
+ },
4700
+ body: formData
4701
+ });
4702
+ }
4499
4703
  async function getTestSet(id) {
4500
4704
  const request = createRequest();
4501
4705
  return request(getCustomerServiceUrl(), `/v1/test_sets/${id}`, {
@@ -4980,7 +5184,7 @@ function readPromptInput(value) {
4980
5184
  if (!filePath) {
4981
5185
  throw new Error("File path cannot be empty after @");
4982
5186
  }
4983
- return fs9.readFileSync(filePath, "utf-8");
5187
+ return fs11.readFileSync(filePath, "utf-8");
4984
5188
  }
4985
5189
  return value;
4986
5190
  }
@@ -5013,6 +5217,25 @@ function registerTestsetCommand(program2) {
5013
5217
  process.exit(toExitCode(err));
5014
5218
  }
5015
5219
  });
5220
+ testset.command("create").description("\u901A\u8FC7 xlsx \u6587\u4EF6\u521B\u5EFA\u6D4B\u8BD5\u96C6").requiredOption("--customer-agent-config-id <id>", "Agent \u914D\u7F6E ID\uFF08\u5FC5\u586B\uFF0C\u4E0E\u540E\u7AEF\u5951\u7EA6\u5BF9\u9F50\uFF09").requiredOption("--file <path>", "\u6D4B\u8BD5\u96C6 xlsx \u6587\u4EF6\u8DEF\u5F84").option("--name <text>", "\u6D4B\u8BD5\u96C6\u540D\u79F0\uFF1B\u4E0D\u4F20\u5219\u540E\u7AEF\u53D6\u6587\u4EF6\u540D").option("--description <text>", "\u6D4B\u8BD5\u96C6\u63CF\u8FF0").option("--use-type <text>", "\u6587\u4EF6\u7528\u9014\u6807\u8BC6").action(async (opts) => {
5221
+ try {
5222
+ if (!fs11.existsSync(opts.file)) {
5223
+ outputError(1, `\u6587\u4EF6\u4E0D\u5B58\u5728: ${opts.file}`);
5224
+ process.exit(1);
5225
+ }
5226
+ const data = await createTestSetFromFile({
5227
+ customerAgentConfigId: opts.customerAgentConfigId,
5228
+ filePath: opts.file,
5229
+ testSetName: opts.name,
5230
+ testSetDescription: opts.description,
5231
+ useType: opts.useType
5232
+ });
5233
+ formatOutput({ success: true, data }, program2.opts().table);
5234
+ } catch (err) {
5235
+ reportCaughtError(err);
5236
+ process.exit(toExitCode(err));
5237
+ }
5238
+ });
5016
5239
  testset.command("show").description("\u67E5\u770B\u5355\u4E2A\u6D4B\u8BD5\u96C6\u8BE6\u60C5").argument("<id>", "\u6D4B\u8BD5\u96C6 ID").action(async (id) => {
5017
5240
  try {
5018
5241
  const data = await getTestSet(id);
@@ -5165,6 +5388,7 @@ registerAuthCommand(program);
5165
5388
  registerConfigCommand(program);
5166
5389
  registerWorkspaceCommand(program);
5167
5390
  registerAgentCommand(program);
5391
+ registerOpsAgentCommand(program);
5168
5392
  registerSACommand(program);
5169
5393
  registerProductCommand(program);
5170
5394
  registerKnowledgeCommand(program);
@@ -5180,6 +5404,7 @@ registerRepairRecordCommand(program);
5180
5404
  registerOperationsRecordCommand(program);
5181
5405
  registerChangeConsumerCommand(program);
5182
5406
  registerTestsetCommand(program);
5407
+ registerSpecialProjectCommand(program);
5183
5408
  process.on("uncaughtException", (err) => {
5184
5409
  outputError(3, err.message);
5185
5410
  process.exit(3);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bty/customer-service-cli",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "AI Customer Service CLI - Agent friendly",
5
5
  "type": "module",
6
6
  "main": "./dist/bin.js",