@bty/customer-service-cli 0.6.6 → 0.6.7

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,20 @@
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.7 (2026-07-30)
13
+
14
+ > 版本号说明: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。
15
+
16
+ - `tool workflow` 子组新增 `create` / `update` / `delete` / `describe`,直连 BetterYeah 平台(`ai-api`)程序化管理工作流(flow)本体(既有 `list` / `detail` 走 servhub 代理不变)。CODE 节点只支持 JavaScript:
17
+ - `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`)。
18
+ - `tool workflow update <flow_id> --code <code|@file> [--no-publish]`:先 `GET /v1/applications/{id}` 取 config,定位唯一 CODE 节点替换其代码(`config` 以对象提交,与 create 的字符串相反),默认取草稿后发布;非唯一 CODE 节点 / config 形状异常在发更新前报清晰错误,不发请求。
19
+ - `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` 一致。
20
+ - `tool workflow delete <flow_id> --yes`:硬删除(`DELETE /v1/applications/{id}`),缺 `--yes` 拒绝并提示。
21
+ - `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` 运行时对象。
22
+ - `tool workflow create/update` 新增 `--version-desc <text>`:发布时透传平台 `version_desc`(版本历史可见,便于区分 CLI 改动);不传则不带该键,`--no-publish` 时忽略。
23
+ - `--code` / `--input-vars` 的值支持 `@文件路径` 读取;文件不存在时给出指向 `@` 前缀语义的可操作报错,不发请求。
24
+ - 新增 `flow-skeleton`(纯函数骨架生成器)+ `flow-api` 客户端封装;URL path 段的 `flow_id` 统一 `encodeURIComponent`(对齐本仓既有防注入约定)。CLI 不改任何后端,不复刻 servhub 受保护模板名单——受保护 flow 由平台在 update/delete 时拒绝,CLI 如实透传。
25
+
12
26
  ## 0.6.5 (2026-07-14)
13
27
 
14
28
  - `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 +37,17 @@
23
37
 
24
38
  - 修复 `product update` / `product update-sku` 在商品数超过 100 时,对位置靠后的商品报「未找到商品 product_id=xxx」的问题:`findProduct` 由只取第一页(`page:1, pageSize:100`)改为翻页查找,直到命中目标商品或翻完所有页。
25
39
 
40
+ ## 0.6.3 (2026-06-24 · 未发布,tool 命令组实际随 0.6.7 发布)
41
+
42
+ - 新增 `tool` 命令组,包装 customer-servhub-api 现有 `/v1/tool` 接口管理 Agent 的自定义/覆盖工具(增删改查):
43
+ - `tool list --agent <id>`:列出该 Agent 配置已绑定的工具(custom + system)。
44
+ - `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` 由当前工作空间回填。
45
+ - `tool update <tool_id> [...]`:局部更新,仅透传实际提供的字段(蛇形键名映射,未提供字段不进请求体)。
46
+ - `tool delete <tool_id>`:软删除工具。
47
+ - `tool workflow list [--keyword]` / `tool workflow detail <flow_id>`:发现可绑定工作流及其入参,辅助 `create` 取 `source_id` / 填 `--input-vars`。
48
+ - 非法 `--invocation-mode` 在发请求前本地报错(可选值 `must_invoke` / `on_demand` / `on_demand_async`)。
49
+ - 新增 `tool-api` 客户端封装,仅消费现有接口;CLI 不改变服务端工具表或 sop-engine 的工具执行逻辑。
50
+
26
51
  ## 0.6.2 (2026-06-15)
27
52
 
28
53
  - `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
  | 命令 | 说明 |
package/dist/bin.js CHANGED
@@ -6781,6 +6781,563 @@ function registerTestsetCommand(program2) {
6781
6781
  registerExportCommand(program2, testset);
6782
6782
  }
6783
6783
 
6784
+ // src/commands/tool.ts
6785
+ import fs14 from "fs";
6786
+
6787
+ // src/client/flow-api.ts
6788
+ import { randomUUID as randomUUID2 } from "crypto";
6789
+
6790
+ // src/client/flow-skeleton.ts
6791
+ import { randomUUID } from "crypto";
6792
+ var DEFAULT_FORM_CONFIG = [
6793
+ {
6794
+ type: "json",
6795
+ label: "\u4E0A\u4E0B\u6587",
6796
+ required: true,
6797
+ placeholder: "\u8BF7\u8F93\u5165",
6798
+ variableName: "dialogue",
6799
+ cascadeFields: [],
6800
+ isCascadeField: false
6801
+ }
6802
+ ];
6803
+ function buildEdge(source, target) {
6804
+ return {
6805
+ id: `${source}-${target}`,
6806
+ type: "insert",
6807
+ source,
6808
+ target,
6809
+ interactionWidth: 80,
6810
+ style: {
6811
+ stroke: "#C0C3D1",
6812
+ strokeWidth: 2,
6813
+ strokeLinecap: "round",
6814
+ strokeLinejoin: "round"
6815
+ }
6816
+ };
6817
+ }
6818
+ function buildFlowSkeleton(opts) {
6819
+ const startId = randomUUID();
6820
+ const codeId = randomUUID();
6821
+ const endId = randomUUID();
6822
+ const codeName = opts.codeNodeName ?? "javascript_1";
6823
+ const startNode = {
6824
+ id: startId,
6825
+ type: "START",
6826
+ actionType: "TRIGGER",
6827
+ name: "start",
6828
+ relation: null,
6829
+ isEnable: true,
6830
+ layout: { width: 420, height: 311, position: { x: 0, y: 0 } },
6831
+ data: {
6832
+ name: "start",
6833
+ type: "form",
6834
+ actionType: "TRIGGER",
6835
+ inputs: {},
6836
+ isEnable: true,
6837
+ relation: null,
6838
+ conditions: [],
6839
+ packageName: "",
6840
+ sortCopyIndex: -1,
6841
+ formConfig: opts.inputVars ?? DEFAULT_FORM_CONFIG
6842
+ }
6843
+ };
6844
+ const codeNode = {
6845
+ id: codeId,
6846
+ type: "CODE",
6847
+ actionType: "CODE",
6848
+ name: codeName,
6849
+ relation: null,
6850
+ isEnable: true,
6851
+ layout: { width: 420, height: 435, position: { x: 544, y: 0 } },
6852
+ data: {
6853
+ name: codeName,
6854
+ type: null,
6855
+ actionType: "CODE",
6856
+ inputs: {
6857
+ code: opts.code,
6858
+ desc: opts.description ?? "",
6859
+ version: 1,
6860
+ language: "javascript"
6861
+ },
6862
+ isEnable: true,
6863
+ relation: null,
6864
+ conditions: [],
6865
+ packageName: "",
6866
+ formConfig: null,
6867
+ sortCopyIndex: 1
6868
+ }
6869
+ };
6870
+ const endNode = {
6871
+ id: endId,
6872
+ type: "END",
6873
+ actionType: "END",
6874
+ name: "output",
6875
+ relation: null,
6876
+ isEnable: true,
6877
+ layout: { width: 420, height: 98, position: { x: 1088, y: 0 } },
6878
+ data: {
6879
+ name: "output",
6880
+ type: null,
6881
+ actionType: "END",
6882
+ inputs: {},
6883
+ isEnable: true,
6884
+ relation: null,
6885
+ conditions: [],
6886
+ packageName: "",
6887
+ formConfig: null,
6888
+ sortCopyIndex: -1
6889
+ }
6890
+ };
6891
+ return {
6892
+ nodes: [startNode, codeNode, endNode],
6893
+ edges: [buildEdge(startId, codeId), buildEdge(codeId, endId)]
6894
+ };
6895
+ }
6896
+
6897
+ // src/client/flow-api.ts
6898
+ var CODE_NODE_GUIDE = `# CODE \u8282\u70B9\u7F16\u5199\u6307\u5357\uFF08JavaScript\uFF09
6899
+
6900
+ \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
6901
+ \u5B98\u65B9\u6587\u6863\uFF1Ahttps://ai-docs.betteryeah.com/Flow/\u8282\u70B9\u4ECB\u7ECD\u4E0E\u914D\u7F6E/JavaScript&Python\u8282\u70B9.html
6902
+
6903
+ ## 1. \u5165\u53E3\uFF1Aasync function main()
6904
+
6905
+ \u5FC5\u987B\u7F16\u5199\u540D\u4E3A main \u7684\u5F02\u6B65\u5165\u53E3\u51FD\u6570\uFF08\u9876\u5C42 async function main()\uFF09\u3002
6906
+ 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
6907
+ \u6CA1\u6709 return \u5219\u8F93\u51FA\u4E3A null\u3002
6908
+
6909
+ ## 2. \u5165\u53C2\u4E0E\u53D8\u91CF\u6CE8\u5165
6910
+
6911
+ \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
6912
+ - START \u8282\u70B9 formConfig \u58F0\u660E\u7684\u6BCF\u4E2A variableName \u90FD\u4F1A\u6CE8\u5165\u4E3A\u540C\u540D\u5168\u5C40\u53D8\u91CF\u3002
6913
+ \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
6914
+ \u8981\u52A0\u5165\u53C2\u5C31\u6539 formConfig\uFF08\u521B\u5EFA\u65F6\u7528 --input-vars \u8986\u76D6\uFF09\u3002
6915
+ - \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
6916
+ \u6240\u4EE5\u901A\u5E38\u53EA\u7528\u5F97\u5230 formConfig \u91CC\u7684\u53D8\u91CF\u3002
6917
+
6918
+ ## 3. \u8FD4\u56DE\u503C
6919
+
6920
+ \u8FD4\u56DE**\u88F8\u7684** object / list \u5373\u53EF\uFF0C**\u4E0D\u8981\u518D\u5957\u4E00\u5C42 {data:{...}} \u6216 {data:{run_result:[...]}} \u4FE1\u5C01**\u3002
6921
+ \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
6922
+ \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
6923
+
6924
+ ## 4. \u5185\u7F6E\u5305\uFF08CommonJS\uFF09
6925
+
6926
+ \u73AF\u5883\u5185\u7F6E\u5E38\u7528\u5305\uFF0C\u7528 require \u5BFC\u5165\uFF08\u4E0D\u652F\u6301 ES Module \u7684 import\uFF09\uFF1A
6927
+
6928
+ const axios = require('axios'); // HTTP \u8BF7\u6C42
6929
+ const dayjs = require('dayjs'); // \u65E5\u671F\u5904\u7406
6930
+ const crypto = require('crypto'); // \u52A0\u5BC6/\u6458\u8981
6931
+
6932
+ ## 5. BetterYeah SDK
6933
+
6934
+ const Betteryeah = require('betteryeah').default;
6935
+ const betteryeah = new Betteryeah();
6936
+
6937
+ \u6838\u5FC3\u6A21\u5757\uFF08\u5168\u90E8 await \u8C03\u7528\uFF09\uFF1A
6938
+ - betteryeah.llm.chat({ systemPrompt, model, jsonMode, temperature, messages })\uFF1A\u8C03\u5927\u6A21\u578B
6939
+ - betteryeah.knowledge.searchKnowledge / insertKnowledge\uFF1A\u77E5\u8BC6\u5E93\u67E5\u8BE2/\u63D2\u5165
6940
+ - betteryeah.database.executeDatabase({ databaseId, executableSql })\uFF1A\u6267\u884C SQL
6941
+ - betteryeah.plugin.*\uFF1A\u641C\u7D22\uFF08search.googleSearch/bingSearch/aiSearch\uFF09\u3001\u89E3\u6790\uFF08parsing.web/excel/pdf/...\uFF09\u3001
6942
+ \u8BC6\u56FE\u751F\u56FE\uFF08image.vision/generate/ocr\uFF09\u7B49
6943
+ - betteryeah.subFlow.execute(flowId, parameter)\uFF1A\u8C03\u7528\u5176\u5B83\u5DF2\u53D1\u5E03\u5DE5\u4F5C\u6D41
6944
+
6945
+ SDK \u7EDF\u4E00\u54CD\u5E94\u5F62\u72B6 { code, success, message, data, usage }\uFF0C\u52A1\u5FC5\u68C0\u67E5 success\uFF1A
6946
+
6947
+ if (!result.success) throw new Error(result.message ?? '\u6267\u884C\u51FA\u9519');
6948
+
6949
+ ## 6. betterAI \u8FD0\u884C\u65F6\u5BF9\u8C61
6950
+
6951
+ - betterAI.store.set(key, value) / betterAI.store.get(key)\uFF1Aflow \u751F\u547D\u5468\u671F\u5185\u5171\u4EAB\u6570\u636E
6952
+ - betterAI.session.*\uFF1A\u4F1A\u8BDD\u5143\u4FE1\u606F\u4E0E\u6301\u4E45\u5316\u8BB0\u5FC6\uFF08sessionId / conversationId / metaData / \u804A\u5929\u8BB0\u5F55\uFF09
6953
+ - betterAI.agent.chat.*\uFF1A\u5411 Agent \u4F1A\u8BDD\u63A8\u9001\u6D88\u606F/\u901A\u77E5
6954
+
6955
+ ## 7. \u5B8C\u6574\u793A\u4F8B
6956
+
6957
+ async function main() {
6958
+ const ctx = dialogue; // \u6765\u81EA START formConfig \u7684\u5168\u5C40\u53D8\u91CF
6959
+ // ...\u4F60\u7684\u4E1A\u52A1\u903B\u8F91...
6960
+ return { ok: true }; // main \u7684\u8FD4\u56DE\u503C\u5373\u5DE5\u5177\u4EA7\u51FA\uFF08\u88F8\u5BF9\u8C61\uFF0C\u52FF\u5957\u4FE1\u5C01\uFF09
6961
+ }
6962
+
6963
+ ## 8. \u6CE8\u610F\u4E8B\u9879
6964
+
6965
+ - \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
6966
+ - SDK \u5404\u6A21\u5757\u5B8C\u6574\u53C2\u6570\u4E0E\u66F4\u591A\u793A\u4F8B\u4EE5\u4E0A\u65B9\u5B98\u65B9\u6587\u6863\u4E3A\u51C6\u3002
6967
+ `;
6968
+ async function createFlow(opts) {
6969
+ const { nodes, edges } = buildFlowSkeleton({
6970
+ code: opts.code,
6971
+ description: opts.description,
6972
+ inputVars: opts.inputVars,
6973
+ codeNodeName: opts.codeNodeName
6974
+ });
6975
+ const request = createRequest();
6976
+ const res = await request(
6977
+ getAiApiUrl(),
6978
+ "/v1/applications",
6979
+ {
6980
+ method: "POST",
6981
+ query: { workspaceId: getWorkspaceId() },
6982
+ headers: {
6983
+ accept: "application/json",
6984
+ "application-id": randomUUID2().replace(/-/g, "")
6985
+ },
6986
+ body: {
6987
+ icon: "\u{1F33C}",
6988
+ color: "#ADE9CE",
6989
+ description: opts.description ?? "\u6682\u65E0\u63CF\u8FF0",
6990
+ name: opts.name,
6991
+ applicationType: "AI",
6992
+ config: JSON.stringify({ nodes, edges }),
6993
+ channel_type: "flow"
6994
+ }
6995
+ }
6996
+ );
6997
+ const flowId = res.flowId;
6998
+ const versionId = res.versionId;
6999
+ const publish = opts.publish !== false;
7000
+ if (publish) {
7001
+ await request(getAiApiUrl(), "/v1/flow/publish", {
7002
+ method: "POST",
7003
+ body: {
7004
+ draftVersionId: versionId,
7005
+ ...opts.versionDesc ? { version_desc: opts.versionDesc } : {}
7006
+ }
7007
+ });
7008
+ }
7009
+ return { flow_id: flowId, version_id: versionId, published: publish };
7010
+ }
7011
+ async function getFlowConfig(opts) {
7012
+ const request = createRequest();
7013
+ return request(getAiApiUrl(), `/v1/applications/${encodeURIComponent(opts.flowId)}`, {
7014
+ method: "GET"
7015
+ });
7016
+ }
7017
+ async function updateFlowCode(opts) {
7018
+ const data = await getFlowConfig({ flowId: opts.flowId });
7019
+ const config = data.config;
7020
+ if (!config || typeof config !== "object" || !Array.isArray(config.nodes)) {
7021
+ throw new APIError(1, "flow config \u5F62\u72B6\u5F02\u5E38\uFF1A\u7F3A nodes \u6570\u7EC4\uFF0C\u65E0\u6CD5\u5B9A\u4F4D CODE \u8282\u70B9");
7022
+ }
7023
+ const codeNodes = config.nodes.filter(
7024
+ // biome-ignore lint/suspicious/noExplicitAny: 同上
7025
+ (n) => n.type === "CODE"
7026
+ );
7027
+ if (codeNodes.length !== 1) {
7028
+ throw new APIError(1, `\u672A\u627E\u5230\u552F\u4E00 CODE \u8282\u70B9\uFF08\u627E\u5230 ${codeNodes.length} \u4E2A\uFF09`);
7029
+ }
7030
+ codeNodes[0].data.inputs.code = opts.code;
7031
+ const request = createRequest();
7032
+ await request(getAiApiUrl(), "/v1/flow/update", {
7033
+ method: "POST",
7034
+ body: { appId: opts.flowId, config, flowId: opts.flowId }
7035
+ });
7036
+ const publish = opts.publish !== false;
7037
+ if (publish) {
7038
+ const draft = await request(
7039
+ getAiApiUrl(),
7040
+ `/v1/flow/draft/${encodeURIComponent(opts.flowId)}`,
7041
+ { method: "GET" }
7042
+ );
7043
+ await request(getAiApiUrl(), "/v1/flow/publish", {
7044
+ method: "POST",
7045
+ body: {
7046
+ draftVersionId: draft.version_id,
7047
+ ...opts.versionDesc ? { version_desc: opts.versionDesc } : {}
7048
+ }
7049
+ });
7050
+ }
7051
+ return { flow_id: opts.flowId, published: publish };
7052
+ }
7053
+ async function getWorkspaceAccessKey() {
7054
+ const request = createRequest();
7055
+ const workspaceId = getWorkspaceId();
7056
+ return request(
7057
+ getAiApiUrl(),
7058
+ `/v1/workspaces/getKey/${encodeURIComponent(workspaceId)}`,
7059
+ { method: "GET" }
7060
+ );
7061
+ }
7062
+ async function runFlow(opts) {
7063
+ const accessKey = await getWorkspaceAccessKey();
7064
+ const request = createRequest();
7065
+ return request(
7066
+ getAiApiUrl(),
7067
+ `/v1/public_api/rest_api/${encodeURIComponent(opts.flowId)}/execute_flow`,
7068
+ {
7069
+ method: "POST",
7070
+ headers: { "Access-Key": accessKey },
7071
+ body: { inputs: opts.inputs ?? {}, async_execute: false }
7072
+ }
7073
+ );
7074
+ }
7075
+ async function deleteFlow(opts) {
7076
+ const request = createRequest();
7077
+ await request(getAiApiUrl(), `/v1/applications/${encodeURIComponent(opts.flowId)}`, {
7078
+ method: "DELETE"
7079
+ });
7080
+ return { flow_id: opts.flowId };
7081
+ }
7082
+
7083
+ // src/client/tool-api.ts
7084
+ var VALID_INVOCATION_MODES = ["must_invoke", "on_demand", "on_demand_async"];
7085
+ function assertInvocationMode(mode) {
7086
+ if (!VALID_INVOCATION_MODES.includes(mode)) {
7087
+ throw new APIError(
7088
+ 1,
7089
+ "invocation_mode \u975E\u6CD5\uFF0C\u53EF\u9009\u503C\uFF1Amust_invoke / on_demand / on_demand_async"
7090
+ );
7091
+ }
7092
+ }
7093
+ async function listToolsByConfig(opts) {
7094
+ const request = createRequest();
7095
+ return request(getCustomerServiceUrl(), "/v1/tool/by-config", {
7096
+ method: "GET",
7097
+ query: { customer_agent_config_id: opts.agentConfigId }
7098
+ });
7099
+ }
7100
+ async function createCustomTool(opts) {
7101
+ if (opts.invocationMode !== void 0) {
7102
+ assertInvocationMode(opts.invocationMode);
7103
+ }
7104
+ const request = createRequest();
7105
+ return request(getCustomerServiceUrl(), "/v1/tool/custom", {
7106
+ method: "POST",
7107
+ body: {
7108
+ name: opts.name,
7109
+ key: opts.key,
7110
+ workspace_id: getWorkspaceId(),
7111
+ customer_agent_config_id: opts.agentConfigId,
7112
+ source_id: opts.sourceId,
7113
+ source_type: opts.sourceType ?? "flow",
7114
+ description: opts.description ?? "",
7115
+ invocation_mode: opts.invocationMode ?? "on_demand",
7116
+ input_var_descriptions: opts.inputVarDescriptions ?? {}
7117
+ }
7118
+ });
7119
+ }
7120
+ async function updateTool(opts) {
7121
+ if (opts.invocationMode !== void 0) {
7122
+ assertInvocationMode(opts.invocationMode);
7123
+ }
7124
+ const body = {};
7125
+ if (opts.name !== void 0) body.name = opts.name;
7126
+ if (opts.key !== void 0) body.key = opts.key;
7127
+ if (opts.description !== void 0) body.description = opts.description;
7128
+ if (opts.sourceId !== void 0) body.source_id = opts.sourceId;
7129
+ if (opts.invocationMode !== void 0) body.invocation_mode = opts.invocationMode;
7130
+ if (opts.inputVarDescriptions !== void 0) {
7131
+ body.input_var_descriptions = opts.inputVarDescriptions;
7132
+ }
7133
+ const request = createRequest();
7134
+ return request(getCustomerServiceUrl(), `/v1/tool/${opts.toolId}`, {
7135
+ method: "PUT",
7136
+ body
7137
+ });
7138
+ }
7139
+ async function deleteTool(opts) {
7140
+ const request = createRequest();
7141
+ return request(getCustomerServiceUrl(), `/v1/tool/${opts.toolId}`, {
7142
+ method: "DELETE"
7143
+ });
7144
+ }
7145
+ async function listWorkflows(opts) {
7146
+ const request = createRequest();
7147
+ return request(getCustomerServiceUrl(), "/v1/tool/workflow/list", {
7148
+ method: "GET",
7149
+ query: { workspace_id: getWorkspaceId(), keyword: opts.keyword ?? "" }
7150
+ });
7151
+ }
7152
+ async function getWorkflowDetail(opts) {
7153
+ const request = createRequest();
7154
+ return request(
7155
+ getCustomerServiceUrl(),
7156
+ `/v1/tool/workflow/${encodeURIComponent(opts.flowId)}/detail`,
7157
+ {
7158
+ method: "GET",
7159
+ query: { workspace_id: getWorkspaceId() }
7160
+ }
7161
+ );
7162
+ }
7163
+
7164
+ // src/commands/tool.ts
7165
+ function readArgMaybeFile(value) {
7166
+ if (value.startsWith("@")) {
7167
+ const filePath = value.slice(1);
7168
+ try {
7169
+ return fs14.readFileSync(filePath, "utf8");
7170
+ } catch {
7171
+ throw new APIError(
7172
+ 1,
7173
+ `\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`
7174
+ );
7175
+ }
7176
+ }
7177
+ return value;
7178
+ }
7179
+ function registerToolCommand(program2) {
7180
+ const tool = program2.command("tool").description(
7181
+ "\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"
7182
+ );
7183
+ 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) => {
7184
+ try {
7185
+ const data = await listToolsByConfig({ agentConfigId: opts.agent });
7186
+ formatOutput({ success: true, data }, program2.opts().table);
7187
+ } catch (err) {
7188
+ reportCaughtError(err);
7189
+ process.exit(toExitCode(err));
7190
+ }
7191
+ });
7192
+ 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(
7193
+ "--invocation-mode <mode>",
7194
+ "\u8C03\u7528\u6A21\u5F0F\uFF1Amust_invoke / on_demand / on_demand_async\uFF08\u9ED8\u8BA4 on_demand\uFF09"
7195
+ ).option("--input-vars <json>", "\u5165\u53C2\u63CF\u8FF0\uFF08JSON \u5B57\u7B26\u4E32\uFF09\uFF0C\u89E3\u6790\u4E3A input_var_descriptions").action(async (opts) => {
7196
+ try {
7197
+ const inputVarDescriptions = opts.inputVars !== void 0 ? JSON.parse(opts.inputVars) : void 0;
7198
+ const data = await createCustomTool({
7199
+ agentConfigId: opts.agent,
7200
+ name: opts.name,
7201
+ key: opts.key,
7202
+ sourceId: opts.sourceId,
7203
+ description: opts.description,
7204
+ sourceType: opts.sourceType,
7205
+ invocationMode: opts.invocationMode,
7206
+ inputVarDescriptions
7207
+ });
7208
+ formatOutput({ success: true, data }, program2.opts().table);
7209
+ } catch (err) {
7210
+ reportCaughtError(err);
7211
+ process.exit(toExitCode(err));
7212
+ }
7213
+ });
7214
+ 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) => {
7215
+ try {
7216
+ const inputVarDescriptions = opts.inputVars !== void 0 ? JSON.parse(opts.inputVars) : void 0;
7217
+ const data = await updateTool({
7218
+ toolId,
7219
+ name: opts.name,
7220
+ key: opts.key,
7221
+ description: opts.description,
7222
+ sourceId: opts.sourceId,
7223
+ invocationMode: opts.invocationMode,
7224
+ inputVarDescriptions
7225
+ });
7226
+ formatOutput({ success: true, data }, program2.opts().table);
7227
+ } catch (err) {
7228
+ reportCaughtError(err);
7229
+ process.exit(toExitCode(err));
7230
+ }
7231
+ });
7232
+ tool.command("delete <tool_id>").description("\u5220\u9664\uFF08\u8F6F\u5220\u9664\uFF09\u6307\u5B9A\u5DE5\u5177").action(async (toolId) => {
7233
+ try {
7234
+ const data = await deleteTool({ toolId });
7235
+ formatOutput({ success: true, data }, program2.opts().table);
7236
+ } catch (err) {
7237
+ reportCaughtError(err);
7238
+ process.exit(toExitCode(err));
7239
+ }
7240
+ });
7241
+ const workflow = tool.command("workflow").description(
7242
+ "\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"
7243
+ );
7244
+ 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) => {
7245
+ try {
7246
+ const data = await listWorkflows({ keyword: opts.keyword });
7247
+ formatOutput({ success: true, data }, program2.opts().table);
7248
+ } catch (err) {
7249
+ reportCaughtError(err);
7250
+ process.exit(toExitCode(err));
7251
+ }
7252
+ });
7253
+ 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) => {
7254
+ try {
7255
+ const data = await getWorkflowDetail({ flowId });
7256
+ formatOutput({ success: true, data }, program2.opts().table);
7257
+ } catch (err) {
7258
+ reportCaughtError(err);
7259
+ process.exit(toExitCode(err));
7260
+ }
7261
+ });
7262
+ workflow.command("create").description(
7263
+ "\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"
7264
+ ).requiredOption("--name <name>", "\u5DE5\u4F5C\u6D41\u540D\u79F0").requiredOption(
7265
+ "--code <code|@file>",
7266
+ "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"
7267
+ ).option("--description <text>", "\u5DE5\u4F5C\u6D41\u63CF\u8FF0").option(
7268
+ "--input-vars <json|@file>",
7269
+ "\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"
7270
+ ).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) => {
7271
+ try {
7272
+ const code = readArgMaybeFile(opts.code);
7273
+ const inputVars = opts.inputVars !== void 0 ? JSON.parse(readArgMaybeFile(opts.inputVars)) : void 0;
7274
+ const data = await createFlow({
7275
+ name: opts.name,
7276
+ code,
7277
+ description: opts.description,
7278
+ inputVars,
7279
+ codeNodeName: opts.codeNodeName,
7280
+ publish: opts.publish,
7281
+ versionDesc: opts.versionDesc
7282
+ });
7283
+ formatOutput({ success: true, data }, program2.opts().table);
7284
+ } catch (err) {
7285
+ reportCaughtError(err);
7286
+ process.exit(toExitCode(err));
7287
+ }
7288
+ });
7289
+ 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(
7290
+ "--code <code|@file>",
7291
+ "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"
7292
+ ).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) => {
7293
+ try {
7294
+ const code = readArgMaybeFile(opts.code);
7295
+ const data = await updateFlowCode({
7296
+ flowId,
7297
+ code,
7298
+ publish: opts.publish,
7299
+ versionDesc: opts.versionDesc
7300
+ });
7301
+ formatOutput({ success: true, data }, program2.opts().table);
7302
+ } catch (err) {
7303
+ reportCaughtError(err);
7304
+ process.exit(toExitCode(err));
7305
+ }
7306
+ });
7307
+ workflow.command("run <flow_id>").description(
7308
+ "\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"
7309
+ ).option(
7310
+ "--input <json|@file>",
7311
+ `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`
7312
+ ).action(async (flowId, opts) => {
7313
+ try {
7314
+ const inputs = opts.input !== void 0 ? JSON.parse(readArgMaybeFile(opts.input)) : void 0;
7315
+ const data = await runFlow({ flowId, inputs });
7316
+ formatOutput({ success: true, data }, program2.opts().table);
7317
+ } catch (err) {
7318
+ reportCaughtError(err);
7319
+ process.exit(toExitCode(err));
7320
+ }
7321
+ });
7322
+ 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) => {
7323
+ try {
7324
+ if (!opts.yes) {
7325
+ 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");
7326
+ }
7327
+ const data = await deleteFlow({ flowId });
7328
+ formatOutput({ success: true, data }, program2.opts().table);
7329
+ } catch (err) {
7330
+ reportCaughtError(err);
7331
+ process.exit(toExitCode(err));
7332
+ }
7333
+ });
7334
+ workflow.command("describe").description(
7335
+ "\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"
7336
+ ).action(() => {
7337
+ formatOutput({ success: true, data: { guide: CODE_NODE_GUIDE } }, program2.opts().table);
7338
+ });
7339
+ }
7340
+
6784
7341
  // src/commands/workspace.ts
6785
7342
  var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
6786
7343
  function toISODate(d) {
@@ -6874,6 +7431,7 @@ registerWorkspaceCommand(program);
6874
7431
  registerAgentCommand(program);
6875
7432
  registerOpsAgentCommand(program);
6876
7433
  registerSACommand(program);
7434
+ registerToolCommand(program);
6877
7435
  registerProductCommand(program);
6878
7436
  registerMediaCommand(program);
6879
7437
  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.7",
4
4
  "description": "AI Customer Service CLI - Agent friendly",
5
5
  "type": "module",
6
6
  "main": "./dist/bin.js",