@bty/customer-service-cli 0.6.6 → 0.6.8

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,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## 0.6.6 (2026-07-17)
3
+ ## Unreleased
4
4
 
5
5
  - 新增 `media upload` 命令与 `product supplement` 子命令组,支持商品「补充知识」图片/视频维护,与前端上传行为一致:
6
6
  - `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,超限/类型不符在发起上传请求前直接失败。
@@ -9,6 +9,24 @@
9
9
  - `product supplement append-media --agent <id> --product-id <id> --index <n> [--image ...] [--video ...] [--dry-run] [--backup <path>]`:仅向目标下标条目追加媒体,其它条目原样保留;去重合并,未提供任何媒体时报错。
10
10
  - Dashboard 命令使用客服 API 原域名下的 `/observability` 网关前缀,无需新增地址配置。
11
11
 
12
+ ## 0.6.8 (2026-08-10)
13
+
14
+ - `conversation transfer-human` 新增 `--only-abnormal`:默认仍返回全部转人工会话;开启后仅保留异常会话,排除正常 SOP 转人工、`专家agent无法回答`、`存在无法处理的消息类型:...` 三类正常情况。
15
+
16
+ ## 0.6.7 (2026-07-30)
17
+
18
+ > 版本号说明: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。
19
+
20
+ - `tool workflow` 子组新增 `create` / `update` / `delete` / `describe`,直连 BetterYeah 平台(`ai-api`)程序化管理工作流(flow)本体(既有 `list` / `detail` 走 servhub 代理不变)。CODE 节点只支持 JavaScript:
21
+ - `tool workflow create --name <名称> --code <code|@file> [--description] [--input-vars <json|@file>] [--no-publish] [--code-node-name]`:程序生成固定 START→CODE→END 三节点骨架(`POST /v1/applications`,`config` 以 JSON 字符串提交),把 JavaScript 代码写进 CODE 节点,默认自动发布;返回 `flow_id` / `version_id` 供 `tool create --source-id` 绑定。骨架结构以真实生产 flow 实测为准(dashed-UUID 节点 id、CODE 节点默认名 `javascript_1`)。
22
+ - `tool workflow update <flow_id> --code <code|@file> [--no-publish]`:先 `GET /v1/applications/{id}` 取 config,定位唯一 CODE 节点替换其代码(`config` 以对象提交,与 create 的字符串相反),默认取草稿后发布;非唯一 CODE 节点 / config 形状异常在发更新前报清晰错误,不发请求。
23
+ - `tool workflow run <flow_id> [--input <json|@file>]`:HTTP 同步运行已发布 flow——自动 `GET /v1/workspaces/getKey/{ws}`(登录态)换应用态 Access-Key,再 `POST /v1/public_api/rest_api/{flow_id}/execute_flow`,返回 `task_id` / `status` / `run_result`;替代平台画布的 wss 执行通道(`ws_execute_flow`),实测两通道 `run_result` 一致。
24
+ - `tool workflow delete <flow_id> --yes`:硬删除(`DELETE /v1/applications/{id}`),缺 `--yes` 拒绝并提示。
25
+ - `tool workflow describe`:输出 CODE 节点(JavaScript)使用文档,依据官方「JavaScript&Python 节点」文档整理——入口 `async main()`、变量注入(START formConfig 的 `dialogue` 等)、返回值约定(裸对象、可序列化、勿套 `{data:{...}}` 信封)、内置包(CommonJS `require`:axios/dayjs/crypto)、`betteryeah` SDK(llm/knowledge/database/plugin/subFlow)与 `betterAI` 运行时对象。
26
+ - `tool workflow create/update` 新增 `--version-desc <text>`:发布时透传平台 `version_desc`(版本历史可见,便于区分 CLI 改动);不传则不带该键,`--no-publish` 时忽略。
27
+ - `--code` / `--input-vars` 的值支持 `@文件路径` 读取;文件不存在时给出指向 `@` 前缀语义的可操作报错,不发请求。
28
+ - 新增 `flow-skeleton`(纯函数骨架生成器)+ `flow-api` 客户端封装;URL path 段的 `flow_id` 统一 `encodeURIComponent`(对齐本仓既有防注入约定)。CLI 不改任何后端,不复刻 servhub 受保护模板名单——受保护 flow 由平台在 update/delete 时拒绝,CLI 如实透传。
29
+
12
30
  ## 0.6.5 (2026-07-14)
13
31
 
14
32
  - `debug ask` / `debug reproduce` 新增 `--mock <json|@file>`(工具结果 mock,形如 `[{ tool_name, result }]`)与 `--data-overlay <json|@file>`(本轮 `debug_data_overlay` 数据覆盖),发送时透传给 `/v1/debug/chat/agent_message`;`reproduce --dry-run` 输出中同步包含 mock 与 overlay。
@@ -23,6 +41,17 @@
23
41
 
24
42
  - 修复 `product update` / `product update-sku` 在商品数超过 100 时,对位置靠后的商品报「未找到商品 product_id=xxx」的问题:`findProduct` 由只取第一页(`page:1, pageSize:100`)改为翻页查找,直到命中目标商品或翻完所有页。
25
43
 
44
+ ## 0.6.3 (2026-06-24 · 未发布,tool 命令组实际随 0.6.7 发布)
45
+
46
+ - 新增 `tool` 命令组,包装 customer-servhub-api 现有 `/v1/tool` 接口管理 Agent 的自定义/覆盖工具(增删改查):
47
+ - `tool list --agent <id>`:列出该 Agent 配置已绑定的工具(custom + system)。
48
+ - `tool create --agent <id> --name <名称> --key <标识> --source-id <flow_id> [--description] [--source-type] [--invocation-mode] [--input-vars <json>]`:创建绑定工作流(flow)的自定义工具;`source_type` 默认 `flow`、`invocation_mode` 默认 `on_demand`、`workspace_id` 由当前工作空间回填。
49
+ - `tool update <tool_id> [...]`:局部更新,仅透传实际提供的字段(蛇形键名映射,未提供字段不进请求体)。
50
+ - `tool delete <tool_id>`:软删除工具。
51
+ - `tool workflow list [--keyword]` / `tool workflow detail <flow_id>`:发现可绑定工作流及其入参,辅助 `create` 取 `source_id` / 填 `--input-vars`。
52
+ - 非法 `--invocation-mode` 在发请求前本地报错(可选值 `must_invoke` / `on_demand` / `on_demand_async`)。
53
+ - 新增 `tool-api` 客户端封装,仅消费现有接口;CLI 不改变服务端工具表或 sop-engine 的工具执行逻辑。
54
+
26
55
  ## 0.6.2 (2026-06-15)
27
56
 
28
57
  - `workspace` 新增 `points-consumes` 子命令:分页查询当前工作空间的积分账户消耗流水(`POST /v1/workspace/points_consumes`),支持 `--page` / `--page-size`,CLI 固定传 `debug: true`,返回中包含服务端 `extra` 调试字段。
package/README.md CHANGED
@@ -217,6 +217,26 @@ cs-cli workspace points-consumes-daily --start 2026-04-01 --end 2026-04-23
217
217
  | `sa delete --agent <id> --id <SA_ID> --first-label <一级标签> --second-label <二级标签>` | 删除场景动作 |
218
218
  | `sa versions --agent <id> --id <SA_ID>` | 查看修改记录 |
219
219
 
220
+ ### 工具 (`tool`)
221
+
222
+ | 命令 | 说明 |
223
+ | --- | --- |
224
+ | `tool list --agent <id>` | 列出该 Agent 配置已绑定的工具(custom + system) |
225
+ | `tool create --agent <id> --name <名称> --key <标识> --source-id <flow_id> [--description <描述>] [--source-type <类型>] [--invocation-mode <模式>] [--input-vars <json>]` | 创建绑定工作流(flow)的自定义工具 |
226
+ | `tool update <tool_id> [--name <名称>] [--key <标识>] [--description <描述>] [--source-id <flow_id>] [--invocation-mode <模式>] [--input-vars <json>]` | 更新工具(仅透传实际提供的字段) |
227
+ | `tool delete <tool_id>` | 删除(软删除)指定工具 |
228
+ | `tool workflow list [--keyword <关键词>]` | 搜索可绑定的工作流(flow),取 `source_id` |
229
+ | `tool workflow detail <flow_id>` | 查看工作流入参,辅助填 `--input-vars` |
230
+ | `tool workflow create --name <名称> --code <code\|@file> [--description <描述>] [--input-vars <json\|@file>] [--no-publish] [--code-node-name <名称>] [--version-desc <描述>]` | 创建 START→CODE→END 骨架工作流,CODE 节点承载工具代码(仅 JavaScript),默认自动发布 |
231
+ | `tool workflow update <flow_id> --code <code\|@file> [--no-publish] [--version-desc <描述>]` | 只更新工作流唯一 CODE 节点的代码(其它节点原样保留),默认自动发布 |
232
+ | `tool workflow run <flow_id> [--input <json\|@file>]` | 运行已发布的工作流(HTTP 同步执行),返回 `task_id` / `status` / `run_result` |
233
+ | `tool workflow delete <flow_id> --yes` | 硬删除工作流(不可恢复);缺 `--yes` 拒绝执行 |
234
+ | `tool workflow describe` | 输出 CODE 节点(JavaScript)使用文档:入口/变量注入/返回值/内置包/SDK,依据官方文档整理 |
235
+
236
+ `tool` 命令组包装 customer-servhub-api 现有 `/v1/tool` 接口,管理 Agent 的自定义/覆盖工具;CLI 不改变服务端工具表或 sop-engine 的工具执行逻辑。`--invocation-mode` 取值 `must_invoke` / `on_demand`(默认)/ `on_demand_async`,非法值在发请求前本地报错;`tool create --input-vars` 接受 JSON 字符串,形如 `{"order_id":{"description":"订单号","type":"string","required":true}}`,对应工作流入参描述;`workspace_id` 由当前工作空间自动回填。
237
+
238
+ `tool workflow create/update/run/delete` 直连 BetterYeah 平台(`ai-api`)程序化管理工作流本体:`create` 生成固定 START→CODE→END 三节点骨架并把代码写进 CODE 节点、默认发布,返回 `flow_id` 供 `tool create --source-id` 绑定;`update` 只暴露 CODE 节点代码的修改;`run` 是 HTTP 同步执行(自动经 `GET /v1/workspaces/getKey/{ws}` 用登录态换应用态 Access-Key 再调 `execute_flow`,替代平台画布的 wss 通道),`--input` 的键对应 START `formConfig` 的 `variableName`(默认 `{}`);`delete` 是硬删除、必须显式 `--yes`。create/update 发布时可用 `--version-desc` 附版本描述(平台版本历史里可见,便于区分 CLI 改动;`--no-publish` 时忽略)。CODE 节点只支持 JavaScript(CommonJS `require`,不支持 ES Module)。`--code` / `--input-vars` 的值以 `@` 开头时按文件路径读取(如 `@./tool.js`)。注意 `tool workflow create --input-vars` 覆盖的是 START 节点的 `formConfig` 入参声明,与 `tool create --input-vars`(工具记录的 `input_var_descriptions`)是不同字段。`tool workflow describe` 输出依据[官方 JavaScript&Python 节点文档](https://ai-docs.betteryeah.com/Flow/节点介绍与配置/JavaScript&Python节点.html)整理的 CODE 节点使用文档(入口 `main()`、变量注入、返回值约定、内置包、BetterYeah SDK),AI 编写工具代码前应先读。
239
+
220
240
  ### 商品管理 (`product`)
221
241
 
222
242
  | 命令 | 说明 |
@@ -500,7 +520,7 @@ Agent 关联的通用规则知识库(`suffix_type=_common`),与 FAQ / 商
500
520
  | `conversation list [--agent <id>] [--equipment-id <设备ID>] [--user <用户名>] [--start <日期>] [--end <日期>]` | 搜索会话 |
501
521
  | `conversation records <conversation_id> [--page-size N] [--direction prev|next]` | 获取会话聊天记录 |
502
522
  | `conversation context-search --query <文本> [--start <时间>] [--end <时间>]` | 通过上下文内容模糊搜索对话记录(返回匹配消息详情,时间范围最多 3 天) |
503
- | `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` 仅保留异常会话) |
504
524
 
505
525
 
506
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) {
@@ -1228,6 +1238,11 @@ function registerConfigCommand(program2) {
1228
1238
  var MAX_CONTEXT_SEARCH_RANGE_MS = 3 * 24 * 60 * 60 * 1e3;
1229
1239
  var MAX_TRANSFER_HUMAN_RANGE_MS = 7 * 24 * 60 * 60 * 1e3;
1230
1240
  var MAX_TRANSFER_HUMAN_CURSOR_PAGES = 500;
1241
+ var NORMAL_TRANSFER_EVENTS = /* @__PURE__ */ new Set([
1242
+ "\u9700\u8981\u4EBA\u5DE5\u63A5\u7BA1, SOP\u5F15\u64CE\u8BF7\u6C42\u8F6C\u4EBA\u5DE5\u5904\u7406",
1243
+ "\u9700\u8981\u4EBA\u5DE5\u63A5\u7BA1, \u4E13\u5BB6agent\u65E0\u6CD5\u56DE\u7B54"
1244
+ ]);
1245
+ var NORMAL_TRANSFER_EVENT_PREFIXES = ["\u9700\u8981\u4EBA\u5DE5\u63A5\u7BA1, \u5B58\u5728\u65E0\u6CD5\u5904\u7406\u7684\u6D88\u606F\u7C7B\u578B\uFF1A"];
1231
1246
  function parseSearchTime(value, fieldName) {
1232
1247
  const parsed = new Date(value);
1233
1248
  if (Number.isNaN(parsed.getTime())) {
@@ -1256,6 +1271,22 @@ function validateTransferHumanRange(startTime, endTime) {
1256
1271
  throw new Error("\u67E5\u8BE2\u65F6\u95F4\u8303\u56F4\u4E0D\u80FD\u8D85\u8FC77\u5929");
1257
1272
  }
1258
1273
  }
1274
+ function shouldKeepTransferHumanConversation(item, onlyAbnormal) {
1275
+ if (!onlyAbnormal) return true;
1276
+ if (!item || typeof item !== "object") return true;
1277
+ const takeoverEvents = item.takeover_event_record;
1278
+ if (!Array.isArray(takeoverEvents) || takeoverEvents.length === 0) return true;
1279
+ const eventNames = takeoverEvents.map((event) => {
1280
+ if (!event || typeof event !== "object") return void 0;
1281
+ const eventName = event.event_name;
1282
+ return typeof eventName === "string" ? eventName : void 0;
1283
+ }).filter((eventName) => typeof eventName === "string");
1284
+ if (eventNames.length === 0) return true;
1285
+ return eventNames.some((eventName) => {
1286
+ if (NORMAL_TRANSFER_EVENTS.has(eventName)) return false;
1287
+ return !NORMAL_TRANSFER_EVENT_PREFIXES.some((prefix) => eventName.startsWith(prefix));
1288
+ });
1289
+ }
1259
1290
  async function contextSearchRecords(opts) {
1260
1291
  validateContextSearchRange(opts.startTime, opts.endTime);
1261
1292
  const wsId = getWorkspaceId(opts.workspaceId);
@@ -1320,7 +1351,8 @@ async function listTransferHumanConversations(opts) {
1320
1351
  }
1321
1352
  }
1322
1353
  );
1323
- items.push(...page.items ?? []);
1354
+ const pageItems = page.items ?? [];
1355
+ items.push(...pageItems.filter((item) => shouldKeepTransferHumanConversation(item, opts.onlyAbnormal ?? false)));
1324
1356
  pagination = page.pagination ?? {};
1325
1357
  const hasMore = pagination.has_more === true;
1326
1358
  const nextCursor = typeof pagination.next_cursor === "string" && pagination.next_cursor ? pagination.next_cursor : void 0;
@@ -1385,12 +1417,13 @@ function registerConversationCommand(program2) {
1385
1417
  process.exit(toExitCode(err));
1386
1418
  }
1387
1419
  });
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) => {
1420
+ 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
1421
  try {
1390
1422
  const data = await listTransferHumanConversations({
1391
1423
  customerAgentConfigId: opts.agent,
1392
1424
  startDate: opts.start,
1393
- endDate: opts.end
1425
+ endDate: opts.end,
1426
+ onlyAbnormal: opts.onlyAbnormal
1394
1427
  });
1395
1428
  formatOutput({ success: true, data }, program2.opts().table);
1396
1429
  } catch (err) {
@@ -6781,6 +6814,563 @@ function registerTestsetCommand(program2) {
6781
6814
  registerExportCommand(program2, testset);
6782
6815
  }
6783
6816
 
6817
+ // src/commands/tool.ts
6818
+ import fs14 from "fs";
6819
+
6820
+ // src/client/flow-api.ts
6821
+ import { randomUUID as randomUUID2 } from "crypto";
6822
+
6823
+ // src/client/flow-skeleton.ts
6824
+ import { randomUUID } from "crypto";
6825
+ var DEFAULT_FORM_CONFIG = [
6826
+ {
6827
+ type: "json",
6828
+ label: "\u4E0A\u4E0B\u6587",
6829
+ required: true,
6830
+ placeholder: "\u8BF7\u8F93\u5165",
6831
+ variableName: "dialogue",
6832
+ cascadeFields: [],
6833
+ isCascadeField: false
6834
+ }
6835
+ ];
6836
+ function buildEdge(source, target) {
6837
+ return {
6838
+ id: `${source}-${target}`,
6839
+ type: "insert",
6840
+ source,
6841
+ target,
6842
+ interactionWidth: 80,
6843
+ style: {
6844
+ stroke: "#C0C3D1",
6845
+ strokeWidth: 2,
6846
+ strokeLinecap: "round",
6847
+ strokeLinejoin: "round"
6848
+ }
6849
+ };
6850
+ }
6851
+ function buildFlowSkeleton(opts) {
6852
+ const startId = randomUUID();
6853
+ const codeId = randomUUID();
6854
+ const endId = randomUUID();
6855
+ const codeName = opts.codeNodeName ?? "javascript_1";
6856
+ const startNode = {
6857
+ id: startId,
6858
+ type: "START",
6859
+ actionType: "TRIGGER",
6860
+ name: "start",
6861
+ relation: null,
6862
+ isEnable: true,
6863
+ layout: { width: 420, height: 311, position: { x: 0, y: 0 } },
6864
+ data: {
6865
+ name: "start",
6866
+ type: "form",
6867
+ actionType: "TRIGGER",
6868
+ inputs: {},
6869
+ isEnable: true,
6870
+ relation: null,
6871
+ conditions: [],
6872
+ packageName: "",
6873
+ sortCopyIndex: -1,
6874
+ formConfig: opts.inputVars ?? DEFAULT_FORM_CONFIG
6875
+ }
6876
+ };
6877
+ const codeNode = {
6878
+ id: codeId,
6879
+ type: "CODE",
6880
+ actionType: "CODE",
6881
+ name: codeName,
6882
+ relation: null,
6883
+ isEnable: true,
6884
+ layout: { width: 420, height: 435, position: { x: 544, y: 0 } },
6885
+ data: {
6886
+ name: codeName,
6887
+ type: null,
6888
+ actionType: "CODE",
6889
+ inputs: {
6890
+ code: opts.code,
6891
+ desc: opts.description ?? "",
6892
+ version: 1,
6893
+ language: "javascript"
6894
+ },
6895
+ isEnable: true,
6896
+ relation: null,
6897
+ conditions: [],
6898
+ packageName: "",
6899
+ formConfig: null,
6900
+ sortCopyIndex: 1
6901
+ }
6902
+ };
6903
+ const endNode = {
6904
+ id: endId,
6905
+ type: "END",
6906
+ actionType: "END",
6907
+ name: "output",
6908
+ relation: null,
6909
+ isEnable: true,
6910
+ layout: { width: 420, height: 98, position: { x: 1088, y: 0 } },
6911
+ data: {
6912
+ name: "output",
6913
+ type: null,
6914
+ actionType: "END",
6915
+ inputs: {},
6916
+ isEnable: true,
6917
+ relation: null,
6918
+ conditions: [],
6919
+ packageName: "",
6920
+ formConfig: null,
6921
+ sortCopyIndex: -1
6922
+ }
6923
+ };
6924
+ return {
6925
+ nodes: [startNode, codeNode, endNode],
6926
+ edges: [buildEdge(startId, codeId), buildEdge(codeId, endId)]
6927
+ };
6928
+ }
6929
+
6930
+ // src/client/flow-api.ts
6931
+ var CODE_NODE_GUIDE = `# CODE \u8282\u70B9\u7F16\u5199\u6307\u5357\uFF08JavaScript\uFF09
6932
+
6933
+ \u81EA\u5B9A\u4E49\u5DE5\u5177 = \u4E00\u4E2A flow\uFF0C\u6838\u5FC3\u662F\u5176\u4E2D\u7684 CODE \u8282\u70B9\u3002\u672C CLI \u521B\u5EFA/\u66F4\u65B0\u7684 CODE \u8282\u70B9\u53EA\u652F\u6301 JavaScript\u3002
6934
+ \u5B98\u65B9\u6587\u6863\uFF1Ahttps://ai-docs.betteryeah.com/Flow/\u8282\u70B9\u4ECB\u7ECD\u4E0E\u914D\u7F6E/JavaScript&Python\u8282\u70B9.html
6935
+
6936
+ ## 1. \u5165\u53E3\uFF1Aasync function main()
6937
+
6938
+ \u5FC5\u987B\u7F16\u5199\u540D\u4E3A main \u7684\u5F02\u6B65\u5165\u53E3\u51FD\u6570\uFF08\u9876\u5C42 async function main()\uFF09\u3002
6939
+ main() \u7684\u8FD4\u56DE\u503C\u5373\u5DE5\u5177\u4EA7\u51FA\uFF08\u4E1A\u52A1\u7ED3\u679C\uFF09\uFF0C\u4F1A\u88AB\u5E73\u53F0\u56DE\u586B\u7ED9 Agent \u4F5C\u4E3A\u8BE5\u5DE5\u5177\u7684\u8F93\u51FA\uFF1B
6940
+ \u6CA1\u6709 return \u5219\u8F93\u51FA\u4E3A null\u3002
6941
+
6942
+ ## 2. \u5165\u53C2\u4E0E\u53D8\u91CF\u6CE8\u5165
6943
+
6944
+ \u524D\u7F6E\u8282\u70B9\u7684\u53D8\u91CF\u5DF2\u5185\u7F6E\u5728\u4EE3\u7801\u6267\u884C\u73AF\u5883\u4E2D\uFF0C\u76F4\u63A5\u4EE5\u540C\u540D\u5168\u5C40\u53D8\u91CF\u4F7F\u7528\u3001\u65E0\u9700\u58F0\u660E\uFF1A
6945
+ - START \u8282\u70B9 formConfig \u58F0\u660E\u7684\u6BCF\u4E2A variableName \u90FD\u4F1A\u6CE8\u5165\u4E3A\u540C\u540D\u5168\u5C40\u53D8\u91CF\u3002
6946
+ \u9ED8\u8BA4\u53EA\u58F0\u660E\u4E00\u4E2A dialogue\uFF08\u4E0A\u4E0B\u6587\uFF0C\u7C7B\u578B json\uFF09\u2014\u2014\u5373\u6574\u6BB5\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u76F4\u63A5\u5F15\u7528 dialogue \u5373\u53EF\u3002
6947
+ \u8981\u52A0\u5165\u53C2\u5C31\u6539 formConfig\uFF08\u521B\u5EFA\u65F6\u7528 --input-vars \u8986\u76D6\uFF09\u3002
6948
+ - \u4E0A\u6E38\u5176\u5B83\u8282\u70B9\u7684\u8F93\u51FA\u540C\u6837\u6309\u8282\u70B9\u540D\u6CE8\u5165\uFF08\u5982 llm_1\uFF09\u3002\u672C CLI \u751F\u6210\u7684\u9AA8\u67B6\u53EA\u6709 START\u2192CODE\u2192END\uFF0C
6949
+ \u6240\u4EE5\u901A\u5E38\u53EA\u7528\u5F97\u5230 formConfig \u91CC\u7684\u53D8\u91CF\u3002
6950
+
6951
+ ## 3. \u8FD4\u56DE\u503C
6952
+
6953
+ \u8FD4\u56DE**\u88F8\u7684** object / list \u5373\u53EF\uFF0C**\u4E0D\u8981\u518D\u5957\u4E00\u5C42 {data:{...}} \u6216 {data:{run_result:[...]}} \u4FE1\u5C01**\u3002
6954
+ \u5E73\u53F0\u6309\u88F8\u8FD4\u56DE\u503C\u6D88\u8D39\uFF1B\u591A\u5957\u7684\u4FE1\u5C01\u4F1A\u8BA9\u4E0B\u6E38\u5BCC\u5316/\u89E3\u6790\u6309\u5B57\u9762 key \u53D6\u503C\u800C\u9759\u9ED8\u5931\u8D25\uFF08\u5DF2\u53D1\u751F\u8FC7\u7EBF\u4E0A\u4E8B\u6545\uFF09\u3002
6955
+ \u8FD4\u56DE\u503C\u5FC5\u987B\u53EF\u5E8F\u5217\u5316\u2014\u2014\u4E0D\u80FD\u662F\u6B63\u5219\u3001\u51FD\u6570\u7B49\u4E0D\u53EF\u5E8F\u5217\u5316\u5BF9\u8C61\u3002
6956
+
6957
+ ## 4. \u5185\u7F6E\u5305\uFF08CommonJS\uFF09
6958
+
6959
+ \u73AF\u5883\u5185\u7F6E\u5E38\u7528\u5305\uFF0C\u7528 require \u5BFC\u5165\uFF08\u4E0D\u652F\u6301 ES Module \u7684 import\uFF09\uFF1A
6960
+
6961
+ const axios = require('axios'); // HTTP \u8BF7\u6C42
6962
+ const dayjs = require('dayjs'); // \u65E5\u671F\u5904\u7406
6963
+ const crypto = require('crypto'); // \u52A0\u5BC6/\u6458\u8981
6964
+
6965
+ ## 5. BetterYeah SDK
6966
+
6967
+ const Betteryeah = require('betteryeah').default;
6968
+ const betteryeah = new Betteryeah();
6969
+
6970
+ \u6838\u5FC3\u6A21\u5757\uFF08\u5168\u90E8 await \u8C03\u7528\uFF09\uFF1A
6971
+ - betteryeah.llm.chat({ systemPrompt, model, jsonMode, temperature, messages })\uFF1A\u8C03\u5927\u6A21\u578B
6972
+ - betteryeah.knowledge.searchKnowledge / insertKnowledge\uFF1A\u77E5\u8BC6\u5E93\u67E5\u8BE2/\u63D2\u5165
6973
+ - betteryeah.database.executeDatabase({ databaseId, executableSql })\uFF1A\u6267\u884C SQL
6974
+ - betteryeah.plugin.*\uFF1A\u641C\u7D22\uFF08search.googleSearch/bingSearch/aiSearch\uFF09\u3001\u89E3\u6790\uFF08parsing.web/excel/pdf/...\uFF09\u3001
6975
+ \u8BC6\u56FE\u751F\u56FE\uFF08image.vision/generate/ocr\uFF09\u7B49
6976
+ - betteryeah.subFlow.execute(flowId, parameter)\uFF1A\u8C03\u7528\u5176\u5B83\u5DF2\u53D1\u5E03\u5DE5\u4F5C\u6D41
6977
+
6978
+ SDK \u7EDF\u4E00\u54CD\u5E94\u5F62\u72B6 { code, success, message, data, usage }\uFF0C\u52A1\u5FC5\u68C0\u67E5 success\uFF1A
6979
+
6980
+ if (!result.success) throw new Error(result.message ?? '\u6267\u884C\u51FA\u9519');
6981
+
6982
+ ## 6. betterAI \u8FD0\u884C\u65F6\u5BF9\u8C61
6983
+
6984
+ - betterAI.store.set(key, value) / betterAI.store.get(key)\uFF1Aflow \u751F\u547D\u5468\u671F\u5185\u5171\u4EAB\u6570\u636E
6985
+ - betterAI.session.*\uFF1A\u4F1A\u8BDD\u5143\u4FE1\u606F\u4E0E\u6301\u4E45\u5316\u8BB0\u5FC6\uFF08sessionId / conversationId / metaData / \u804A\u5929\u8BB0\u5F55\uFF09
6986
+ - betterAI.agent.chat.*\uFF1A\u5411 Agent \u4F1A\u8BDD\u63A8\u9001\u6D88\u606F/\u901A\u77E5
6987
+
6988
+ ## 7. \u5B8C\u6574\u793A\u4F8B
6989
+
6990
+ async function main() {
6991
+ const ctx = dialogue; // \u6765\u81EA START formConfig \u7684\u5168\u5C40\u53D8\u91CF
6992
+ // ...\u4F60\u7684\u4E1A\u52A1\u903B\u8F91...
6993
+ return { ok: true }; // main \u7684\u8FD4\u56DE\u503C\u5373\u5DE5\u5177\u4EA7\u51FA\uFF08\u88F8\u5BF9\u8C61\uFF0C\u52FF\u5957\u4FE1\u5C01\uFF09
6994
+ }
6995
+
6996
+ ## 8. \u6CE8\u610F\u4E8B\u9879
6997
+
6998
+ - \u77E5\u8BC6\u5E93 ID / \u6587\u4EF6 ID / \u6570\u636E\u5E93 ID \u7B49\u5728\u5E73\u53F0\u5DE6\u4FA7\u8D44\u6E90\u6811\u7528\u590D\u5236\u6309\u94AE\u83B7\u53D6\u3002
6999
+ - SDK \u5404\u6A21\u5757\u5B8C\u6574\u53C2\u6570\u4E0E\u66F4\u591A\u793A\u4F8B\u4EE5\u4E0A\u65B9\u5B98\u65B9\u6587\u6863\u4E3A\u51C6\u3002
7000
+ `;
7001
+ async function createFlow(opts) {
7002
+ const { nodes, edges } = buildFlowSkeleton({
7003
+ code: opts.code,
7004
+ description: opts.description,
7005
+ inputVars: opts.inputVars,
7006
+ codeNodeName: opts.codeNodeName
7007
+ });
7008
+ const request = createRequest();
7009
+ const res = await request(
7010
+ getAiApiUrl(),
7011
+ "/v1/applications",
7012
+ {
7013
+ method: "POST",
7014
+ query: { workspaceId: getWorkspaceId() },
7015
+ headers: {
7016
+ accept: "application/json",
7017
+ "application-id": randomUUID2().replace(/-/g, "")
7018
+ },
7019
+ body: {
7020
+ icon: "\u{1F33C}",
7021
+ color: "#ADE9CE",
7022
+ description: opts.description ?? "\u6682\u65E0\u63CF\u8FF0",
7023
+ name: opts.name,
7024
+ applicationType: "AI",
7025
+ config: JSON.stringify({ nodes, edges }),
7026
+ channel_type: "flow"
7027
+ }
7028
+ }
7029
+ );
7030
+ const flowId = res.flowId;
7031
+ const versionId = res.versionId;
7032
+ const publish = opts.publish !== false;
7033
+ if (publish) {
7034
+ await request(getAiApiUrl(), "/v1/flow/publish", {
7035
+ method: "POST",
7036
+ body: {
7037
+ draftVersionId: versionId,
7038
+ ...opts.versionDesc ? { version_desc: opts.versionDesc } : {}
7039
+ }
7040
+ });
7041
+ }
7042
+ return { flow_id: flowId, version_id: versionId, published: publish };
7043
+ }
7044
+ async function getFlowConfig(opts) {
7045
+ const request = createRequest();
7046
+ return request(getAiApiUrl(), `/v1/applications/${encodeURIComponent(opts.flowId)}`, {
7047
+ method: "GET"
7048
+ });
7049
+ }
7050
+ async function updateFlowCode(opts) {
7051
+ const data = await getFlowConfig({ flowId: opts.flowId });
7052
+ const config = data.config;
7053
+ if (!config || typeof config !== "object" || !Array.isArray(config.nodes)) {
7054
+ throw new APIError(1, "flow config \u5F62\u72B6\u5F02\u5E38\uFF1A\u7F3A nodes \u6570\u7EC4\uFF0C\u65E0\u6CD5\u5B9A\u4F4D CODE \u8282\u70B9");
7055
+ }
7056
+ const codeNodes = config.nodes.filter(
7057
+ // biome-ignore lint/suspicious/noExplicitAny: 同上
7058
+ (n) => n.type === "CODE"
7059
+ );
7060
+ if (codeNodes.length !== 1) {
7061
+ throw new APIError(1, `\u672A\u627E\u5230\u552F\u4E00 CODE \u8282\u70B9\uFF08\u627E\u5230 ${codeNodes.length} \u4E2A\uFF09`);
7062
+ }
7063
+ codeNodes[0].data.inputs.code = opts.code;
7064
+ const request = createRequest();
7065
+ await request(getAiApiUrl(), "/v1/flow/update", {
7066
+ method: "POST",
7067
+ body: { appId: opts.flowId, config, flowId: opts.flowId }
7068
+ });
7069
+ const publish = opts.publish !== false;
7070
+ if (publish) {
7071
+ const draft = await request(
7072
+ getAiApiUrl(),
7073
+ `/v1/flow/draft/${encodeURIComponent(opts.flowId)}`,
7074
+ { method: "GET" }
7075
+ );
7076
+ await request(getAiApiUrl(), "/v1/flow/publish", {
7077
+ method: "POST",
7078
+ body: {
7079
+ draftVersionId: draft.version_id,
7080
+ ...opts.versionDesc ? { version_desc: opts.versionDesc } : {}
7081
+ }
7082
+ });
7083
+ }
7084
+ return { flow_id: opts.flowId, published: publish };
7085
+ }
7086
+ async function getWorkspaceAccessKey() {
7087
+ const request = createRequest();
7088
+ const workspaceId = getWorkspaceId();
7089
+ return request(
7090
+ getAiApiUrl(),
7091
+ `/v1/workspaces/getKey/${encodeURIComponent(workspaceId)}`,
7092
+ { method: "GET" }
7093
+ );
7094
+ }
7095
+ async function runFlow(opts) {
7096
+ const accessKey = await getWorkspaceAccessKey();
7097
+ const request = createRequest();
7098
+ return request(
7099
+ getAiApiUrl(),
7100
+ `/v1/public_api/rest_api/${encodeURIComponent(opts.flowId)}/execute_flow`,
7101
+ {
7102
+ method: "POST",
7103
+ headers: { "Access-Key": accessKey },
7104
+ body: { inputs: opts.inputs ?? {}, async_execute: false }
7105
+ }
7106
+ );
7107
+ }
7108
+ async function deleteFlow(opts) {
7109
+ const request = createRequest();
7110
+ await request(getAiApiUrl(), `/v1/applications/${encodeURIComponent(opts.flowId)}`, {
7111
+ method: "DELETE"
7112
+ });
7113
+ return { flow_id: opts.flowId };
7114
+ }
7115
+
7116
+ // src/client/tool-api.ts
7117
+ var VALID_INVOCATION_MODES = ["must_invoke", "on_demand", "on_demand_async"];
7118
+ function assertInvocationMode(mode) {
7119
+ if (!VALID_INVOCATION_MODES.includes(mode)) {
7120
+ throw new APIError(
7121
+ 1,
7122
+ "invocation_mode \u975E\u6CD5\uFF0C\u53EF\u9009\u503C\uFF1Amust_invoke / on_demand / on_demand_async"
7123
+ );
7124
+ }
7125
+ }
7126
+ async function listToolsByConfig(opts) {
7127
+ const request = createRequest();
7128
+ return request(getCustomerServiceUrl(), "/v1/tool/by-config", {
7129
+ method: "GET",
7130
+ query: { customer_agent_config_id: opts.agentConfigId }
7131
+ });
7132
+ }
7133
+ async function createCustomTool(opts) {
7134
+ if (opts.invocationMode !== void 0) {
7135
+ assertInvocationMode(opts.invocationMode);
7136
+ }
7137
+ const request = createRequest();
7138
+ return request(getCustomerServiceUrl(), "/v1/tool/custom", {
7139
+ method: "POST",
7140
+ body: {
7141
+ name: opts.name,
7142
+ key: opts.key,
7143
+ workspace_id: getWorkspaceId(),
7144
+ customer_agent_config_id: opts.agentConfigId,
7145
+ source_id: opts.sourceId,
7146
+ source_type: opts.sourceType ?? "flow",
7147
+ description: opts.description ?? "",
7148
+ invocation_mode: opts.invocationMode ?? "on_demand",
7149
+ input_var_descriptions: opts.inputVarDescriptions ?? {}
7150
+ }
7151
+ });
7152
+ }
7153
+ async function updateTool(opts) {
7154
+ if (opts.invocationMode !== void 0) {
7155
+ assertInvocationMode(opts.invocationMode);
7156
+ }
7157
+ const body = {};
7158
+ if (opts.name !== void 0) body.name = opts.name;
7159
+ if (opts.key !== void 0) body.key = opts.key;
7160
+ if (opts.description !== void 0) body.description = opts.description;
7161
+ if (opts.sourceId !== void 0) body.source_id = opts.sourceId;
7162
+ if (opts.invocationMode !== void 0) body.invocation_mode = opts.invocationMode;
7163
+ if (opts.inputVarDescriptions !== void 0) {
7164
+ body.input_var_descriptions = opts.inputVarDescriptions;
7165
+ }
7166
+ const request = createRequest();
7167
+ return request(getCustomerServiceUrl(), `/v1/tool/${opts.toolId}`, {
7168
+ method: "PUT",
7169
+ body
7170
+ });
7171
+ }
7172
+ async function deleteTool(opts) {
7173
+ const request = createRequest();
7174
+ return request(getCustomerServiceUrl(), `/v1/tool/${opts.toolId}`, {
7175
+ method: "DELETE"
7176
+ });
7177
+ }
7178
+ async function listWorkflows(opts) {
7179
+ const request = createRequest();
7180
+ return request(getCustomerServiceUrl(), "/v1/tool/workflow/list", {
7181
+ method: "GET",
7182
+ query: { workspace_id: getWorkspaceId(), keyword: opts.keyword ?? "" }
7183
+ });
7184
+ }
7185
+ async function getWorkflowDetail(opts) {
7186
+ const request = createRequest();
7187
+ return request(
7188
+ getCustomerServiceUrl(),
7189
+ `/v1/tool/workflow/${encodeURIComponent(opts.flowId)}/detail`,
7190
+ {
7191
+ method: "GET",
7192
+ query: { workspace_id: getWorkspaceId() }
7193
+ }
7194
+ );
7195
+ }
7196
+
7197
+ // src/commands/tool.ts
7198
+ function readArgMaybeFile(value) {
7199
+ if (value.startsWith("@")) {
7200
+ const filePath = value.slice(1);
7201
+ try {
7202
+ return fs14.readFileSync(filePath, "utf8");
7203
+ } catch {
7204
+ throw new APIError(
7205
+ 1,
7206
+ `\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25\uFF1A${filePath}\uFF08@ \u524D\u7F00\u8868\u793A\u6587\u4EF6\u8DEF\u5F84\uFF1B\u8981\u4F20\u5B57\u9762\u5185\u5BB9\u8BF7\u53BB\u6389 @\uFF09`
7207
+ );
7208
+ }
7209
+ }
7210
+ return value;
7211
+ }
7212
+ function registerToolCommand(program2) {
7213
+ const tool = program2.command("tool").description(
7214
+ "\u81EA\u5B9A\u4E49/\u8986\u76D6\u5DE5\u5177\uFF08agent tool\uFF09\u7BA1\u7406 \u2014\u2014 \u5BF9\u67D0\u4E2A Agent \u914D\u7F6E\u7ED1\u5B9A\u7684\u81EA\u5B9A\u4E49\u5DE5\u5177\u505A\u589E\u5220\u6539\u67E5\uFF0C\u5E76\u53D1\u73B0\u53EF\u7ED1\u5B9A\u7684\u5DE5\u4F5C\u6D41\uFF08flow\uFF09\u3002\u5305\u88C5 servhub /v1/tool API"
7215
+ );
7216
+ tool.command("list").description("\u5217\u51FA\u8BE5 Agent \u914D\u7F6E\u5DF2\u7ED1\u5B9A\u7684\u5DE5\u5177\uFF08custom + system\uFF09").requiredOption("--agent <config_id>", "Agent \u914D\u7F6E ID\uFF08\u4ECE agent list \u83B7\u53D6\uFF09").action(async (opts) => {
7217
+ try {
7218
+ const data = await listToolsByConfig({ agentConfigId: opts.agent });
7219
+ formatOutput({ success: true, data }, program2.opts().table);
7220
+ } catch (err) {
7221
+ reportCaughtError(err);
7222
+ process.exit(toExitCode(err));
7223
+ }
7224
+ });
7225
+ tool.command("create").description("\u521B\u5EFA\u81EA\u5B9A\u4E49\u5DE5\u5177\uFF0C\u7ED1\u5B9A\u4E00\u4E2A\u5DE5\u4F5C\u6D41\uFF08flow\uFF09\u4F5C\u4E3A\u5DE5\u5177\u5B9E\u73B0").requiredOption("--agent <config_id>", "Agent \u914D\u7F6E ID\uFF08\u4ECE agent list \u83B7\u53D6\uFF09").requiredOption("--name <name>", "\u5DE5\u5177\u663E\u793A\u540D\u79F0").requiredOption("--key <key>", "\u5DE5\u5177 key\uFF08\u552F\u4E00\u6807\u8BC6\uFF09").requiredOption("--source-id <flow_id>", "\u7ED1\u5B9A\u7684\u5DE5\u4F5C\u6D41 ID\uFF08\u4ECE tool workflow list \u83B7\u53D6\uFF09").option("--description <text>", "\u5DE5\u5177\u63CF\u8FF0").option("--source-type <type>", "\u5DE5\u5177\u6765\u6E90\u7C7B\u578B\uFF08\u9ED8\u8BA4 flow\uFF09").option(
7226
+ "--invocation-mode <mode>",
7227
+ "\u8C03\u7528\u6A21\u5F0F\uFF1Amust_invoke / on_demand / on_demand_async\uFF08\u9ED8\u8BA4 on_demand\uFF09"
7228
+ ).option("--input-vars <json>", "\u5165\u53C2\u63CF\u8FF0\uFF08JSON \u5B57\u7B26\u4E32\uFF09\uFF0C\u89E3\u6790\u4E3A input_var_descriptions").action(async (opts) => {
7229
+ try {
7230
+ const inputVarDescriptions = opts.inputVars !== void 0 ? JSON.parse(opts.inputVars) : void 0;
7231
+ const data = await createCustomTool({
7232
+ agentConfigId: opts.agent,
7233
+ name: opts.name,
7234
+ key: opts.key,
7235
+ sourceId: opts.sourceId,
7236
+ description: opts.description,
7237
+ sourceType: opts.sourceType,
7238
+ invocationMode: opts.invocationMode,
7239
+ inputVarDescriptions
7240
+ });
7241
+ formatOutput({ success: true, data }, program2.opts().table);
7242
+ } catch (err) {
7243
+ reportCaughtError(err);
7244
+ process.exit(toExitCode(err));
7245
+ }
7246
+ });
7247
+ tool.command("update <tool_id>").description("\u66F4\u65B0\u5DE5\u5177\u7684\u4EFB\u610F\u5B50\u96C6\u5B57\u6BB5\uFF08\u4EC5\u900F\u4F20\u5B9E\u9645\u63D0\u4F9B\u7684 flag\uFF09").option("--name <name>", "\u5DE5\u5177\u663E\u793A\u540D\u79F0").option("--key <key>", "\u5DE5\u5177 key\uFF08\u552F\u4E00\u6807\u8BC6\uFF09").option("--description <text>", "\u5DE5\u5177\u63CF\u8FF0").option("--source-id <flow_id>", "\u7ED1\u5B9A\u7684\u5DE5\u4F5C\u6D41 ID").option("--invocation-mode <mode>", "\u8C03\u7528\u6A21\u5F0F\uFF1Amust_invoke / on_demand / on_demand_async").option("--input-vars <json>", "\u5165\u53C2\u63CF\u8FF0\uFF08JSON \u5B57\u7B26\u4E32\uFF09\uFF0C\u89E3\u6790\u4E3A input_var_descriptions").action(async (toolId, opts) => {
7248
+ try {
7249
+ const inputVarDescriptions = opts.inputVars !== void 0 ? JSON.parse(opts.inputVars) : void 0;
7250
+ const data = await updateTool({
7251
+ toolId,
7252
+ name: opts.name,
7253
+ key: opts.key,
7254
+ description: opts.description,
7255
+ sourceId: opts.sourceId,
7256
+ invocationMode: opts.invocationMode,
7257
+ inputVarDescriptions
7258
+ });
7259
+ formatOutput({ success: true, data }, program2.opts().table);
7260
+ } catch (err) {
7261
+ reportCaughtError(err);
7262
+ process.exit(toExitCode(err));
7263
+ }
7264
+ });
7265
+ tool.command("delete <tool_id>").description("\u5220\u9664\uFF08\u8F6F\u5220\u9664\uFF09\u6307\u5B9A\u5DE5\u5177").action(async (toolId) => {
7266
+ try {
7267
+ const data = await deleteTool({ toolId });
7268
+ formatOutput({ success: true, data }, program2.opts().table);
7269
+ } catch (err) {
7270
+ reportCaughtError(err);
7271
+ process.exit(toExitCode(err));
7272
+ }
7273
+ });
7274
+ const workflow = tool.command("workflow").description(
7275
+ "\u5DE5\u4F5C\u6D41\uFF08flow\uFF09\u7BA1\u7406\uFF1Alist / detail\uFF08\u53D1\u73B0\uFF09+ create / update / run / delete / describe\uFF08\u589E\u6539\u8DD1\u5220 + \u8BF4\u660E\uFF09"
7276
+ );
7277
+ workflow.command("list").description("\u5217\u51FA\u5F53\u524D\u5DE5\u4F5C\u7A7A\u95F4\u53EF\u7ED1\u5B9A\u7684\u5DE5\u4F5C\u6D41\uFF08flow\uFF09").option("--keyword <kw>", "\u6309\u5173\u952E\u8BCD\u6A21\u7CCA\u7B5B\u9009").action(async (opts) => {
7278
+ try {
7279
+ const data = await listWorkflows({ keyword: opts.keyword });
7280
+ formatOutput({ success: true, data }, program2.opts().table);
7281
+ } catch (err) {
7282
+ reportCaughtError(err);
7283
+ process.exit(toExitCode(err));
7284
+ }
7285
+ });
7286
+ workflow.command("detail <flow_id>").description("\u67E5\u770B\u6307\u5B9A\u5DE5\u4F5C\u6D41\u7684\u5165\u53C2\u5217\u8868\uFF08\u7528\u4E8E\u6784\u9020 input_var_descriptions\uFF09").action(async (flowId) => {
7287
+ try {
7288
+ const data = await getWorkflowDetail({ flowId });
7289
+ formatOutput({ success: true, data }, program2.opts().table);
7290
+ } catch (err) {
7291
+ reportCaughtError(err);
7292
+ process.exit(toExitCode(err));
7293
+ }
7294
+ });
7295
+ workflow.command("create").description(
7296
+ "\u521B\u5EFA\u5DE5\u4F5C\u6D41\uFF08flow\uFF09\uFF1ASTART \u2192 CODE \u2192 END \u9AA8\u67B6\uFF0CCODE \u8282\u70B9\u627F\u8F7D\u5DE5\u5177\u4EE3\u7801\uFF08\u4EC5\u652F\u6301 JavaScript\uFF09\u3002\u975E\u5E42\u7B49\u2014\u2014\u91CD\u8BD5\u4F1A\u5EFA\u51FA\u91CD\u590D flow\uFF0C\u91CD\u8BD5\u524D\u5148 tool workflow list --keyword <name> \u6838\u5BF9"
7297
+ ).requiredOption("--name <name>", "\u5DE5\u4F5C\u6D41\u540D\u79F0").requiredOption(
7298
+ "--code <code|@file>",
7299
+ "CODE \u8282\u70B9 JavaScript \u4EE3\u7801\uFF1B\u503C\u4EE5 @ \u5F00\u5934\u5219\u8BFB\u53D6\u8BE5\u6587\u4EF6\u8DEF\u5F84\uFF08\u5982 @./tool.js\uFF09\uFF0C\u5426\u5219\u6309\u5B57\u9762\u4EE3\u7801"
7300
+ ).option("--description <text>", "\u5DE5\u4F5C\u6D41\u63CF\u8FF0").option(
7301
+ "--input-vars <json|@file>",
7302
+ "\u8986\u76D6 START formConfig\uFF08@file \u540C --code\uFF09\uFF1B\u5F62\u6001\u662F formConfig \u6570\u7EC4 [{type,label,required,placeholder,variableName,...}]\uFF0C\u9ED8\u8BA4\u4EC5 dialogue\u3002\u6CE8\u610F\uFF1A\u4E0E tool create \u7684 --input-vars\uFF08input_var_descriptions\uFF09\u662F\u4E0D\u540C\u5B57\u6BB5"
7303
+ ).option("--no-publish", "\u53EA\u5EFA\u8349\u7A3F\u4E0D\u53D1\u5E03").option("--code-node-name <name>", "CODE \u8282\u70B9\u540D\uFF08\u9ED8\u8BA4 javascript_1\uFF09").option("--version-desc <text>", "\u53D1\u5E03\u7248\u672C\u63CF\u8FF0\uFF08\u5E73\u53F0\u7248\u672C\u5386\u53F2\u91CC\u53EF\u89C1\uFF1B--no-publish \u65F6\u5FFD\u7565\uFF09").action(async (opts) => {
7304
+ try {
7305
+ const code = readArgMaybeFile(opts.code);
7306
+ const inputVars = opts.inputVars !== void 0 ? JSON.parse(readArgMaybeFile(opts.inputVars)) : void 0;
7307
+ const data = await createFlow({
7308
+ name: opts.name,
7309
+ code,
7310
+ description: opts.description,
7311
+ inputVars,
7312
+ codeNodeName: opts.codeNodeName,
7313
+ publish: opts.publish,
7314
+ versionDesc: opts.versionDesc
7315
+ });
7316
+ formatOutput({ success: true, data }, program2.opts().table);
7317
+ } catch (err) {
7318
+ reportCaughtError(err);
7319
+ process.exit(toExitCode(err));
7320
+ }
7321
+ });
7322
+ workflow.command("update <flow_id>").description("\u66F4\u65B0\u5DE5\u4F5C\u6D41\u552F\u4E00 CODE \u8282\u70B9\u7684\u4EE3\u7801\uFF08\u5176\u5B83\u8282\u70B9\u539F\u6837\u4FDD\u7559\uFF09").requiredOption(
7323
+ "--code <code|@file>",
7324
+ "CODE \u8282\u70B9\u65B0\u4EE3\u7801\uFF08JavaScript\uFF09\uFF1B\u503C\u4EE5 @ \u5F00\u5934\u5219\u8BFB\u53D6\u8BE5\u6587\u4EF6\u8DEF\u5F84\uFF0C\u5426\u5219\u6309\u5B57\u9762\u4EE3\u7801"
7325
+ ).option("--no-publish", "\u53EA\u66F4\u65B0\u8349\u7A3F\u4E0D\u53D1\u5E03").option("--version-desc <text>", "\u53D1\u5E03\u7248\u672C\u63CF\u8FF0\uFF08\u5E73\u53F0\u7248\u672C\u5386\u53F2\u91CC\u53EF\u89C1\uFF1B--no-publish \u65F6\u5FFD\u7565\uFF09").action(async (flowId, opts) => {
7326
+ try {
7327
+ const code = readArgMaybeFile(opts.code);
7328
+ const data = await updateFlowCode({
7329
+ flowId,
7330
+ code,
7331
+ publish: opts.publish,
7332
+ versionDesc: opts.versionDesc
7333
+ });
7334
+ formatOutput({ success: true, data }, program2.opts().table);
7335
+ } catch (err) {
7336
+ reportCaughtError(err);
7337
+ process.exit(toExitCode(err));
7338
+ }
7339
+ });
7340
+ workflow.command("run <flow_id>").description(
7341
+ "\u8FD0\u884C\u5DF2\u53D1\u5E03\u7684\u5DE5\u4F5C\u6D41\uFF08HTTP \u540C\u6B65\u6267\u884C\uFF09\uFF1A\u81EA\u52A8\u53D6\u5F53\u524D\u7A7A\u95F4\u5E94\u7528\u6001 access key\uFF0C\u8FD4\u56DE run_result"
7342
+ ).option(
7343
+ "--input <json|@file>",
7344
+ `flow \u5165\u53C2 JSON \u5BF9\u8C61\uFF08\u9ED8\u8BA4 {}\uFF09\uFF0C\u952E\u5BF9\u5E94 START formConfig \u7684 variableName\uFF0C\u5982 '{"dialogue":...}'\uFF1B@file \u540C --code`
7345
+ ).action(async (flowId, opts) => {
7346
+ try {
7347
+ const inputs = opts.input !== void 0 ? JSON.parse(readArgMaybeFile(opts.input)) : void 0;
7348
+ const data = await runFlow({ flowId, inputs });
7349
+ formatOutput({ success: true, data }, program2.opts().table);
7350
+ } catch (err) {
7351
+ reportCaughtError(err);
7352
+ process.exit(toExitCode(err));
7353
+ }
7354
+ });
7355
+ workflow.command("delete <flow_id>").description("\u786C\u5220\u9664\u5DE5\u4F5C\u6D41\uFF08flow\uFF09\u2014\u2014\u4E0D\u53EF\u6062\u590D\uFF08\u533A\u522B\u4E8E tool delete \u7684\u8F6F\u5220\u9664\uFF09").option("--yes", "\u786E\u8BA4\u5220\u9664\uFF08\u5FC5\u987B\u663E\u5F0F\u52A0\uFF0C\u5426\u5219\u62D2\u7EDD\uFF09").action(async (flowId, opts) => {
7356
+ try {
7357
+ if (!opts.yes) {
7358
+ throw new APIError(1, "\u5220\u9664\u4F1A\u8BA9\u6307\u5411\u6B64 flow \u7684\u81EA\u5B9A\u4E49\u5DE5\u5177\u5931\u6548\uFF0C\u786E\u8BA4\u8BF7\u52A0 --yes");
7359
+ }
7360
+ const data = await deleteFlow({ flowId });
7361
+ formatOutput({ success: true, data }, program2.opts().table);
7362
+ } catch (err) {
7363
+ reportCaughtError(err);
7364
+ process.exit(toExitCode(err));
7365
+ }
7366
+ });
7367
+ workflow.command("describe").description(
7368
+ "\u8F93\u51FA CODE \u8282\u70B9\uFF08JavaScript\uFF09\u4F7F\u7528\u6587\u6863\uFF1A\u5165\u53E3/\u53D8\u91CF\u6CE8\u5165/\u8FD4\u56DE\u503C/\u5185\u7F6E\u5305/SDK\uFF0C\u4F9D\u636E\u5B98\u65B9\u6587\u6863\u6574\u7406"
7369
+ ).action(() => {
7370
+ formatOutput({ success: true, data: { guide: CODE_NODE_GUIDE } }, program2.opts().table);
7371
+ });
7372
+ }
7373
+
6784
7374
  // src/commands/workspace.ts
6785
7375
  var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
6786
7376
  function toISODate(d) {
@@ -6874,6 +7464,7 @@ registerWorkspaceCommand(program);
6874
7464
  registerAgentCommand(program);
6875
7465
  registerOpsAgentCommand(program);
6876
7466
  registerSACommand(program);
7467
+ registerToolCommand(program);
6877
7468
  registerProductCommand(program);
6878
7469
  registerMediaCommand(program);
6879
7470
  registerKnowledgeCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bty/customer-service-cli",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
4
4
  "description": "AI Customer Service CLI - Agent friendly",
5
5
  "type": "module",
6
6
  "main": "./dist/bin.js",