digitalsee-ai-flow-cli 0.7.23-beta.1 → 0.7.24-beta.2

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/README.md CHANGED
@@ -188,6 +188,22 @@ ai-flow profile show --name prod # 指定 Profile
188
188
  ai-flow profile show --json
189
189
  ```
190
190
 
191
+ 输出示例(当前 Profile):
192
+ ```
193
+ ==================== Profile: dev ====================
194
+ baseUrl: https://dev.example.com
195
+ token: abcd****1234
196
+ insecure: false
197
+ Node Package: 支持
198
+ ```
199
+
200
+ `--json` 输出示例:
201
+ ```json
202
+ { "ok": true, "data": { "baseUrl": "https://dev.example.com", "token": "abcd****1234", "insecure": false, "nodePackageSupported": true } }
203
+ ```
204
+
205
+ > Node Package 状态在登录后自动检测缓存。查看非当前 Profile 时该字段为 `undefined`(不检测)。
206
+
191
207
  ### profile delete / rename
192
208
 
193
209
  删除或重命名 Profile。
@@ -1343,8 +1359,7 @@ ai-flow service-log stream --json # NDJSON 输出(每行一条 { ok, data }
1343
1359
 
1344
1360
  ```bash
1345
1361
  ai-flow knowledge categories
1346
- ai-flow knowledge categories --type action
1347
- ai-flow knowledge categories --type trigger
1362
+ ai-flow knowledge categories --trigger
1348
1363
  ai-flow knowledge categories --json
1349
1364
  ```
1350
1365
 
@@ -1355,7 +1370,7 @@ ai-flow knowledge categories --json
1355
1370
  ```bash
1356
1371
  ai-flow knowledge nodes
1357
1372
  ai-flow knowledge nodes <category>
1358
- ai-flow knowledge nodes --name "sql" --flow-id <flowId>
1373
+ ai-flow knowledge nodes --name "sql"
1359
1374
  ai-flow knowledge nodes --name "HTTP" --trigger --page 1 --size 20
1360
1375
  ai-flow knowledge nodes --json
1361
1376
  ```
@@ -1395,7 +1410,7 @@ ai-flow knowledge actions <linkId> --json # JSON 格式输出(含 outpu
1395
1410
 
1396
1411
  ### knowledge generate-skills
1397
1412
 
1398
- 遍历所有分类(action / trigger / tool / mcp / model / memory)下的所有节点和动作,为每个动作生成 Markdown 文档和 JSON 结构化数据文件,按分类类型存储。
1413
+ 遍历所有分类下的所有节点和动作,为每个动作生成 Markdown 文档和 JSON 结构化数据文件,按分类类型和分类名组织存储。支持 `node_schema` 模式(包含 input/output fields 详情)。
1399
1414
 
1400
1415
  ```bash
1401
1416
  ai-flow knowledge generate-skills ./docs
@@ -1407,26 +1422,20 @@ ai-flow knowledge generate-skills ./docs --json
1407
1422
  输出:
1408
1423
  ```
1409
1424
  docs/
1410
- ├── action/
1411
- │ ├── markdown/
1425
+ ├── normal/
1426
+ │ ├── HTTP/
1412
1427
  │ │ ├── HTTP_发送请求.md
1413
- │ │ └── ...
1414
- │ └── json/
1415
- │ ├── HTTP_发送请求.json
1416
- │ └── ...
1417
- ├── trigger/
1418
- │ ├── markdown/
1419
- │ │ ├── Webhook_接收数据v2.md
1420
- │ │ └── ...
1421
- │ └── json/
1422
- │ └── ...
1423
- ├── tool/
1424
- │ └── ...
1428
+ │ │ └── HTTP_发送请求.json
1429
+ │ └── MySQL/
1430
+ │ ├── MySQL_执行SQL查询.md
1431
+ │ └── MySQL_执行SQL查询.json
1432
+ ├── normal-trigger/
1433
+ │ └── Webhook/
1434
+ │ ├── Webhook_接收数据v2.md
1435
+ │ └── Webhook_接收数据v2.json
1425
1436
  ├── mcp/
1426
1437
  │ └── ...
1427
- ├── model/
1428
- │ └── ...
1429
- └── memory/
1438
+ └── mcp-trigger/
1430
1439
  └── ...
1431
1440
  ```
1432
1441
 
@@ -6,21 +6,23 @@ exports.getConnectorDetail = getConnectorDetail;
6
6
  exports.getAllConnectors = getAllConnectors;
7
7
  const client_1 = require("@/api/client");
8
8
  async function getNodeCategories(params) {
9
- return (0, client_1.apiGet)('/acm/links/domain/v2', { type: params?.type });
9
+ return (0, client_1.apiPost)('/acm/node-package/categories/connectable', {
10
+ scene: params?.scene || 'normal',
11
+ trigger: params?.trigger || false,
12
+ });
10
13
  }
11
14
  async function searchNodesByCategory(params) {
12
15
  const body = {
16
+ scene: 'normal',
13
17
  page: params.page || 1,
14
18
  size: params.size || 50,
15
19
  trigger: params.trigger,
16
20
  };
17
- if (params.name)
18
- body.name = params.name;
19
- if (params.flow_id)
20
- body.flow_id = params.flow_id;
21
- if (params.app_domain_category)
22
- body.app_domain_category = params.app_domain_category;
23
- const data = await (0, client_1.apiPost)('/acm/links/search', body);
21
+ if (params.keyword)
22
+ body.keyword = params.keyword;
23
+ if (params.category_name)
24
+ body.category_name = params.category_name;
25
+ const data = await (0, client_1.apiPost)('/acm/node-package/plugins/connectable', body);
24
26
  return data || { items: [], total: 0, page: params.page || 1, size: params.size || 50 };
25
27
  }
26
28
  async function getConnectorDetail(connectorId) {
package/dist/api/node.js CHANGED
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getActionDetail = getActionDetail;
3
4
  exports.getActions = getActions;
5
+ exports.searchActions = searchActions;
6
+ exports.isNodePackageSupported = isNodePackageSupported;
4
7
  exports.getNodeDetail = getNodeDetail;
5
8
  exports.createNode = createNode;
6
9
  exports.updateNode = updateNode;
@@ -9,7 +12,11 @@ exports.deleteNode = deleteNode;
9
12
  exports.testRunNode = testRunNode;
10
13
  exports.cancelDebugNode = cancelDebugNode;
11
14
  const client_1 = require("@/api/client");
15
+ const knowledge_1 = require("@/api/knowledge");
12
16
  const encoding_1 = require("@/utils/encoding");
17
+ async function getActionDetail(id, version) {
18
+ return await (0, client_1.apiGet)(`/acm/actions/${id}${version ? '/' + version : ''}`);
19
+ }
13
20
  async function getActions(params) {
14
21
  const data = await (0, client_1.apiGet)('/acm/actions/v2', params);
15
22
  if (Array.isArray(data))
@@ -22,6 +29,39 @@ async function getActions(params) {
22
29
  return data.data;
23
30
  return [];
24
31
  }
32
+ async function searchActions(params) {
33
+ const data = await (0, client_1.apiPost)('/acm/links/search', params);
34
+ const actions = [];
35
+ if (data && data.items && Array.isArray(data.items)) {
36
+ for (const item of data.items) {
37
+ if (item.matched_actions && Array.isArray(item.matched_actions)) {
38
+ actions.push(...item.matched_actions);
39
+ }
40
+ }
41
+ }
42
+ return actions;
43
+ }
44
+ async function isNodePackageSupported() {
45
+ try {
46
+ const categories = await (0, knowledge_1.getNodeCategories)();
47
+ const firstName = categories[0]?.name;
48
+ if (!firstName)
49
+ return false;
50
+ const { items } = await (0, knowledge_1.searchNodesByCategory)({
51
+ category_name: firstName,
52
+ trigger: true,
53
+ size: 1,
54
+ });
55
+ const linkId = items?.[0]?.connector?.id;
56
+ if (!linkId)
57
+ return false;
58
+ const actions = await getActions({ linkId });
59
+ return actions.some((action) => action.hasOwnProperty('node_schema'));
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
25
65
  async function getNodeDetail(nodeId) {
26
66
  return (0, client_1.apiGet)(`/acm/flows/nodes/${nodeId}`);
27
67
  }
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.registerAuthCommand = registerAuthCommand;
7
7
  const config_1 = require("@/utils/config");
8
+ const nodePackage_1 = require("@/utils/nodePackage");
8
9
  const oauth_1 = require("@/utils/oauth");
9
10
  const output_1 = require("@/utils/output");
10
11
  const chalk_1 = __importDefault(require("chalk"));
@@ -64,6 +65,7 @@ function registerAuthCommand(program) {
64
65
  };
65
66
  (0, config_1.saveGlobalConfig)(saveData);
66
67
  (0, output_1.printSuccess)('登录成功');
68
+ (0, nodePackage_1.checkAndCacheNodePackageSupport)().catch(() => { });
67
69
  server.close();
68
70
  }
69
71
  catch (error) {
@@ -51,6 +51,8 @@ exports.resolveErrorHandlerFile = resolveErrorHandlerFile;
51
51
  exports.checkPropertyValidation = checkPropertyValidation;
52
52
  exports.checkSpecialNodeValidation = checkSpecialNodeValidation;
53
53
  exports.validateVariableRefs = validateVariableRefs;
54
+ exports.validateNodeSchemaProperties = validateNodeSchemaProperties;
55
+ exports.printNodeSchemaFields = printNodeSchemaFields;
54
56
  const fs = __importStar(require("fs"));
55
57
  const chalk_1 = __importDefault(require("chalk"));
56
58
  const output_1 = require("@/utils/output");
@@ -263,14 +265,23 @@ function printNodeConfigDetail(node) {
263
265
  ];
264
266
  (0, output_1.printKeyValue)(pairs, ' ');
265
267
  const subParams = node.config_schema?.sub_params;
266
- if (subParams && subParams.length > 0) {
268
+ const hasSubParams = subParams && subParams.length > 0;
269
+ const hasFlatProperties = !hasSubParams && node.properties && Object.keys(node.properties).length > 0;
270
+ if (hasSubParams) {
267
271
  console.log(chalk_1.default.gray(` 配置字段 (${subParams.length}):`));
268
272
  for (const p of subParams) {
269
273
  const required = p.required ? chalk_1.default.red('*') : ' ';
270
274
  console.log(` ${required} ${chalk_1.default.cyan(p.name)} ${chalk_1.default.gray(`[${p.page_control || p.type}]`)} ${p.display_name || ''} ${p.description ? `- ${p.description}` : ''}`);
271
275
  }
272
276
  }
273
- if (node.properties && Object.keys(node.properties).length > 0) {
277
+ if (hasFlatProperties) {
278
+ console.log(chalk_1.default.gray(` 当前配置值:`));
279
+ for (const [key, val] of Object.entries(node.properties)) {
280
+ const displayVal = typeof val === 'object' ? JSON.stringify(val) : String(val);
281
+ console.log(` ${chalk_1.default.yellow(key)} = ${displayVal}`);
282
+ }
283
+ }
284
+ else if (node.properties && Object.keys(node.properties).length > 0) {
274
285
  console.log(chalk_1.default.gray(` 当前配置值:`));
275
286
  const subParamMap = new Map();
276
287
  for (const p of subParams ?? []) {
@@ -402,4 +413,69 @@ async function validateVariableRefs(configValues, flowId, nodeId, json) {
402
413
  (0, output_1.printInfo)('变量引用验证通过');
403
414
  return false;
404
415
  }
416
+ /**
417
+ * 校验 properties 是否符合 node_schema.input_fields 定义
418
+ */
419
+ function validateNodeSchemaProperties(properties, inputFields) {
420
+ const errors = [];
421
+ for (const field of inputFields) {
422
+ const value = properties[field.key];
423
+ const hasValue = value !== undefined && value !== null;
424
+ if (field.required && !hasValue) {
425
+ errors.push({
426
+ field: field.key,
427
+ rule: 'required',
428
+ message: `${field.label} (${field.key}) 是必填字段`,
429
+ sample: '',
430
+ });
431
+ }
432
+ if (hasValue) {
433
+ const type = field.type.toUpperCase();
434
+ if ((type === 'NUMBER' || type === 'INTEGER') &&
435
+ typeof value !== 'number' &&
436
+ (typeof value !== 'string' || isNaN(Number(value)))) {
437
+ errors.push({
438
+ field: field.key,
439
+ rule: 'type',
440
+ message: `${field.label} (${field.key}) 应为数字类型,实际为 ${typeof value}`,
441
+ sample: '',
442
+ });
443
+ }
444
+ else if (type === 'BOOLEAN' && typeof value !== 'boolean') {
445
+ errors.push({
446
+ field: field.key,
447
+ rule: 'type',
448
+ message: `${field.label} (${field.key}) 应为布尔类型,实际为 ${typeof value}`,
449
+ sample: '',
450
+ });
451
+ }
452
+ else if (type === 'SELECT' && typeof value !== 'string') {
453
+ errors.push({
454
+ field: field.key,
455
+ rule: 'type',
456
+ message: `${field.label} (${field.key}) 应为字符串类型 (SELECT),实际为 ${typeof value}`,
457
+ sample: '',
458
+ });
459
+ }
460
+ }
461
+ }
462
+ return errors;
463
+ }
464
+ /**
465
+ * 打印 node_schema.input_fields 字段列表
466
+ */
467
+ function printNodeSchemaFields(inputFields) {
468
+ if (!inputFields || inputFields.length === 0)
469
+ return;
470
+ console.log(chalk_1.default.gray(` 配置字段 (${inputFields.length}):`));
471
+ for (const field of inputFields) {
472
+ const required = field.required ? chalk_1.default.red('*') : ' ';
473
+ const typeLabel = field.type.toLowerCase();
474
+ const defaultStr = field.default_value !== undefined && field.default_value !== null
475
+ ? chalk_1.default.dim(` [默认: ${field.default_value}]`)
476
+ : '';
477
+ const helpStr = field.help_text ? chalk_1.default.gray(` - ${field.help_text}`) : '';
478
+ console.log(` ${required} ${chalk_1.default.cyan(field.key)} ${chalk_1.default.gray(`[${typeLabel}]`)} ${field.label}${defaultStr}${helpStr}`);
479
+ }
480
+ }
405
481
  //# sourceMappingURL=helpers.js.map
@@ -218,13 +218,26 @@ function registerFlowNode(flowCmd) {
218
218
  let posX = options.positionX !== undefined ? Number(options.positionX) : defaultPos.x;
219
219
  let posY = options.positionY !== undefined ? Number(options.positionY) : defaultPos.y;
220
220
  const trigger = options.trigger || options.node_type === 'StartEventNode' || false;
221
- (0, output_1.printInfo)(`获取连接器 ${connectorId} 的动作列表...`);
222
- const actions = await (0, node_1.getActions)({
223
- linkId: connectorId,
224
- flowId,
225
- trigger,
226
- });
227
- const action = actions.find((a) => a.id === actionId);
221
+ let action;
222
+ try {
223
+ action = await (0, node_1.getActionDetail)(connectorId);
224
+ }
225
+ catch {
226
+ // 获取失败则回退到列表查找
227
+ }
228
+ if (action) {
229
+ // 兼容旧 API:非 node package 节点要求必传 action_id,此处设为 '0' 占位
230
+ action.id = '0';
231
+ }
232
+ else {
233
+ (0, output_1.printInfo)(`获取连接器 ${connectorId} 的动作列表...`);
234
+ const actions = await (0, node_1.getActions)({
235
+ linkId: connectorId,
236
+ flowId,
237
+ trigger,
238
+ });
239
+ action = actions.find((a) => a.id === actionId);
240
+ }
228
241
  if (!action) {
229
242
  const msg = `未找到动作 ${actionId}`;
230
243
  if (options.json) {
@@ -296,14 +309,28 @@ function registerFlowNode(flowCmd) {
296
309
  return;
297
310
  }
298
311
  }
299
- let configSchema = action.config_schema || {};
312
+ const hasNodeSchema = !!action.node_schema;
313
+ let configSchema = null;
314
+ if (!hasNodeSchema) {
315
+ configSchema =
316
+ action.config_schema || {};
317
+ }
300
318
  const overrideValues = (0, helpers_1.resolveDataOption)(options, helpers_1.NODE_CREATE_DATA_SCHEMA);
301
319
  if (overrideValues && overrideValues.position) {
302
320
  const ovPos = overrideValues.position;
303
321
  posX = Number(ovPos.x) || posX;
304
322
  posY = Number(ovPos.y) || posY;
305
323
  }
306
- const config = (0, format_1.deepMerge)({ properties: action.config_schema }, overrideValues ?? {});
324
+ let config;
325
+ if (hasNodeSchema) {
326
+ // node_schema 模式:properties 直接透传,不经过 deepMerge
327
+ config = {
328
+ properties: overrideValues?.properties ?? {},
329
+ };
330
+ }
331
+ else {
332
+ config = (0, format_1.deepMerge)({ properties: action.config_schema }, overrideValues ?? {});
333
+ }
307
334
  const defaultNodeType = trigger ? 'StartEventNode' : 'ActionNode';
308
335
  const node_type = options.nodeType || action.action_type || defaultNodeType;
309
336
  if (isFirstNode && node_type !== 'StartEventNode') {
@@ -329,6 +356,10 @@ function registerFlowNode(flowCmd) {
329
356
  if (accountId) {
330
357
  payload.account_id = accountId;
331
358
  }
359
+ if (hasNodeSchema && action.node_schema) {
360
+ payload.action_key = action.node_schema.key;
361
+ payload.action_ver = action.node_schema.version;
362
+ }
332
363
  if (options.name) {
333
364
  payload.action_name = options.name;
334
365
  }
@@ -340,12 +371,21 @@ function registerFlowNode(flowCmd) {
340
371
  return;
341
372
  if (eh)
342
373
  payload.error_handler = eh;
343
- if (configSchema) {
374
+ if (hasNodeSchema && action.node_schema) {
375
+ const inputFields = action.node_schema.input_fields;
376
+ if (inputFields && inputFields.length > 0) {
377
+ if ((0, helpers_1.checkPropertyValidation)((0, helpers_1.validateNodeSchemaProperties)(config.properties ?? {}, inputFields), !!options.json))
378
+ return;
379
+ }
380
+ }
381
+ else if (configSchema) {
344
382
  if ((0, helpers_1.checkPropertyValidation)((0, format_1.validateNodePropertyValues)(configSchema), !!options.json))
345
383
  return;
346
384
  }
347
- if ((0, helpers_1.checkSpecialNodeValidation)((0, format_1.validateSpecialNodeType)(node_type, payload, configSchema), !!options.json))
348
- return;
385
+ if (!hasNodeSchema && configSchema) {
386
+ if ((0, helpers_1.checkSpecialNodeValidation)((0, format_1.validateSpecialNodeType)(node_type, payload, configSchema), !!options.json))
387
+ return;
388
+ }
349
389
  (0, output_1.printInfo)('正在创建节点...');
350
390
  const result = await (0, node_1.createNode)(payload);
351
391
  const resultObj = result;
@@ -373,7 +413,7 @@ function registerFlowNode(flowCmd) {
373
413
  const pairs = [
374
414
  ['节点 ID', String(resultObj.id || '-')],
375
415
  ['动作名称', action.name],
376
- ['连接器', action.connector_id],
416
+ action.connector_id ? ['连接器', action.connector_id] : ['package', action.action_key],
377
417
  ['节点类型', String(nPayload.node_type)],
378
418
  ['触发器', trigger ? '是' : '否'],
379
419
  ['位置', `(${nPos.x}, ${nPos.y})`],
@@ -487,23 +527,70 @@ function registerFlowNode(flowCmd) {
487
527
  if ((0, helpers_1.checkFlowNotOnline)(nodeFlow, !!options.json))
488
528
  return;
489
529
  const nd = nodeDetail;
490
- let updateData = {
491
- flow_id: flowId,
492
- ...nd,
493
- };
530
+ const hasNodeSchema = !!(nd.action_key && nd.action_ver);
531
+ // node_schema 模式:获取 action 以拿到 action_key / action_ver
532
+ let nodeSchemaAction;
533
+ if (hasNodeSchema) {
534
+ try {
535
+ const params = {
536
+ name: nd.action_key,
537
+ flow_id: flowId,
538
+ trigger: nd.node_type === 'StartEventNode',
539
+ page: 1,
540
+ size: 20,
541
+ };
542
+ const actions = await (0, node_1.searchActions)(params);
543
+ nodeSchemaAction = actions.find((a) => a.action_key === nd.action_key);
544
+ }
545
+ catch (e) {
546
+ (0, output_1.printError)(`获取 node_schema action 失败: ${e.message}`);
547
+ // 获取失败则回退到旧模式
548
+ }
549
+ }
550
+ let updateData;
551
+ if (hasNodeSchema && nodeSchemaAction?.node_schema) {
552
+ const ns = nodeSchemaAction.node_schema;
553
+ const existingProps = nd.properties ?? {};
554
+ const pos = nd.position ?? { x: 0, y: 0 };
555
+ updateData = {
556
+ flow_id: flowId,
557
+ properties: { ...existingProps },
558
+ position: { ...pos },
559
+ action_id: options.actionId || nd.action_id,
560
+ account_id: options.accountId ?? nd.account_id ?? null,
561
+ action_key: ns.key,
562
+ action_ver: ns.version,
563
+ };
564
+ }
565
+ else {
566
+ updateData = {
567
+ flow_id: flowId,
568
+ ...nd,
569
+ };
570
+ if (nd.action_id)
571
+ updateData.action_id = nd.action_id;
572
+ if (nd.connector_id)
573
+ updateData.connector_id = nd.connector_id;
574
+ }
494
575
  if (nd.position)
495
576
  updateData.position = { ...nd.position };
496
- if (nd.action_id)
497
- updateData.action_id = nd.action_id;
498
- if (nd.connector_id)
499
- updateData.connector_id = nd.connector_id;
500
577
  if (nd.account_id)
501
578
  updateData.account_id = nd.account_id;
502
579
  if (nd.note != null)
503
580
  updateData.note = nd.note;
504
581
  const configValues = (0, helpers_1.resolveDataOption)(options, helpers_1.NODE_UPDATE_DATA_SCHEMA);
505
582
  if (configValues) {
506
- updateData = (0, format_1.deepMerge)(updateData, configValues);
583
+ if (hasNodeSchema && configValues.properties) {
584
+ // node_schema 模式:properties 浅合并,不经过 deepMerge
585
+ const existingProps = updateData.properties ?? {};
586
+ updateData.properties = {
587
+ ...existingProps,
588
+ ...configValues.properties,
589
+ };
590
+ }
591
+ else {
592
+ updateData = (0, format_1.deepMerge)(updateData, configValues);
593
+ }
507
594
  }
508
595
  if (options.validateVars && configValues) {
509
596
  if (await (0, helpers_1.validateVariableRefs)(configValues, flowId, nodeId, !!options.json))
@@ -541,11 +628,23 @@ function registerFlowNode(flowCmd) {
541
628
  return;
542
629
  }
543
630
  if (updateData.properties) {
544
- if ((0, helpers_1.checkPropertyValidation)((0, format_1.validateNodePropertyValues)(updateData.properties), !!options.json))
631
+ if (hasNodeSchema) {
632
+ const inputFields = (nodeSchemaAction?.node_schema?.input_fields ??
633
+ []);
634
+ if (inputFields.length > 0) {
635
+ if ((0, helpers_1.checkPropertyValidation)((0, helpers_1.validateNodeSchemaProperties)(updateData.properties, inputFields), !!options.json))
636
+ return;
637
+ }
638
+ }
639
+ else {
640
+ if ((0, helpers_1.checkPropertyValidation)((0, format_1.validateNodePropertyValues)(updateData.properties), !!options.json))
641
+ return;
642
+ }
643
+ }
644
+ if (!hasNodeSchema) {
645
+ if ((0, helpers_1.checkSpecialNodeValidation)((0, format_1.validateSpecialNodeType)(String(nd.node_type), updateData, nd.config_schema), !!options.json))
545
646
  return;
546
647
  }
547
- if ((0, helpers_1.checkSpecialNodeValidation)((0, format_1.validateSpecialNodeType)(String(nd.node_type), updateData, nd.config_schema), !!options.json))
548
- return;
549
648
  if (hasUpdateChanges) {
550
649
  (0, output_1.printInfo)('正在更新节点...');
551
650
  await (0, node_1.updateNode)(nodeId, updateData);
@@ -9,19 +9,24 @@ const node_1 = require("@/api/node");
9
9
  const skillGenerator_1 = require("@/services/skillGenerator");
10
10
  const output_1 = require("@/utils/output");
11
11
  const format_1 = require("@/utils/format");
12
+ const helpers_1 = require("@/commands/flow/helpers");
12
13
  const chalk_1 = __importDefault(require("chalk"));
13
14
  function registerKnowledgeCommand(program) {
14
15
  const knowledgeCmd = program.command('knowledge').description('获取节点类型知识');
15
16
  knowledgeCmd
16
17
  .command('categories')
17
18
  .description('列出所有节点分类')
18
- .option('--type <type>', '分类类型: trigger|action|tool|mcp|model|memory')
19
+ .option('--type [type]', 'normal|mcp', 'normal')
20
+ .option('--trigger', '仅 Trigger 分类')
19
21
  .option('--json', '以 JSON 格式输出')
20
22
  .action(async (options) => {
21
23
  try {
22
24
  if (options.json)
23
25
  (0, output_1.setJsonMode)(true);
24
- const categories = await (0, knowledge_1.getNodeCategories)({ type: options.type });
26
+ const categories = await (0, knowledge_1.getNodeCategories)({
27
+ trigger: options.trigger,
28
+ scene: options.type,
29
+ });
25
30
  if (options.json) {
26
31
  (0, output_1.printStructuredResult)({ ok: true, data: categories });
27
32
  return;
@@ -56,7 +61,6 @@ function registerKnowledgeCommand(program) {
56
61
  .description('搜索节点,展示匹配的 connector 及 actions(含 nodeType)')
57
62
  .option('--name <name>', '按名称搜索')
58
63
  .option('--trigger', '仅触发器')
59
- .option('--flow-id <flowId>', '连接流 ID')
60
64
  .option('--page <number>', '页码', '1')
61
65
  .option('--size <number>', '每页数量', '50')
62
66
  .option('--json', '以 JSON 格式输出')
@@ -66,10 +70,9 @@ function registerKnowledgeCommand(program) {
66
70
  if (opts.json)
67
71
  (0, output_1.setJsonMode)(true);
68
72
  const result = await (0, knowledge_1.searchNodesByCategory)({
69
- app_domain_category: category,
70
- name: opts.name,
73
+ category_name: category,
74
+ keyword: opts.name,
71
75
  trigger: opts.trigger || false,
72
- flow_id: opts.flowId,
73
76
  page: Number(opts.page),
74
77
  size: Number(opts.size),
75
78
  });
@@ -154,20 +157,39 @@ function registerKnowledgeCommand(program) {
154
157
  ['状态', action.status],
155
158
  ];
156
159
  (0, output_1.printKeyValue)(pairs, ' ');
157
- const params = (0, format_1.configSchemaToParams)(action.config_schema?.sub_params);
158
- if (params.length > 0) {
159
- console.log(chalk_1.default.gray(` 配置参数 (${params.length}):`));
160
- for (const p of params) {
161
- const required = p.required ? chalk_1.default.red(' *') : ' ';
162
- console.log(` ${required} ${chalk_1.default.cyan(p.name)} ${chalk_1.default.gray(`[${p.type}]`)} ${p.display_name} — ${p.description || '-'}`);
163
- if (p.options && p.options.length > 0) {
164
- const optStr = p.options.map((o) => `${o.key}:${o.label}`).join(', ');
165
- console.log(` ${chalk_1.default.dim('可选值:')} ${chalk_1.default.dim(optStr)}`);
160
+ const isNodePackageSupported = !!action.node_schema;
161
+ if (isNodePackageSupported) {
162
+ const inputFields = action.node_schema?.input_fields ?? [];
163
+ if (inputFields.length > 0) {
164
+ (0, helpers_1.printNodeSchemaFields)(inputFields);
165
+ }
166
+ else {
167
+ (0, output_1.printInfo)(' 无配置参数');
168
+ }
169
+ const outputFields = action.node_schema?.output_fields ?? [];
170
+ if (outputFields.length > 0) {
171
+ console.log(chalk_1.default.gray(` 输出结果 (${outputFields.length}):`));
172
+ for (const o of outputFields) {
173
+ console.log(` ${chalk_1.default.cyan(o.key)} ${chalk_1.default.gray(`[${o.type}]`)} ${o.label} - ${o.description ?? ''}`);
166
174
  }
167
175
  }
168
176
  }
169
177
  else {
170
- (0, output_1.printInfo)(' 无配置参数');
178
+ const params = (0, format_1.configSchemaToParams)(action.config_schema?.sub_params);
179
+ if (params.length > 0) {
180
+ console.log(chalk_1.default.gray(` 配置参数 (${params.length}):`));
181
+ for (const p of params) {
182
+ const required = p.required ? chalk_1.default.red(' *') : ' ';
183
+ console.log(` ${required} ${chalk_1.default.cyan(p.name)} ${chalk_1.default.gray(`[${p.type}]`)} ${p.display_name} — ${p.description || '-'}`);
184
+ if (p.options && p.options.length > 0) {
185
+ const optStr = p.options.map((o) => `${o.key}:${o.label}`).join(', ');
186
+ console.log(` ${chalk_1.default.dim('可选值:')} ${chalk_1.default.dim(optStr)}`);
187
+ }
188
+ }
189
+ }
190
+ else {
191
+ (0, output_1.printInfo)(' 无配置参数');
192
+ }
171
193
  }
172
194
  }
173
195
  }
@@ -8,6 +8,7 @@ const config_1 = require("@/utils/config");
8
8
  const output_1 = require("@/utils/output");
9
9
  const exit_code_1 = require("@/constants/exit-code");
10
10
  const client_1 = require("@/api/client");
11
+ const nodePackage_1 = require("@/utils/nodePackage");
11
12
  const chalk_1 = __importDefault(require("chalk"));
12
13
  function registerProfileCommand(program) {
13
14
  const profileCmd = program.command('profile').description('管理多配置环境(Profile)');
@@ -59,7 +60,7 @@ function registerProfileCommand(program) {
59
60
  .description('显示 Profile 的详细配置')
60
61
  .option('--name <name>', '指定要查看的 Profile')
61
62
  .option('--json', '以 JSON 格式输出')
62
- .action((options) => {
63
+ .action(async (options) => {
63
64
  if (options.json)
64
65
  (0, output_1.setJsonMode)(true);
65
66
  const profileName = options.name || (0, config_1.getActiveProfile)();
@@ -77,6 +78,10 @@ function registerProfileCommand(program) {
77
78
  }
78
79
  }
79
80
  const config = (0, config_1.loadConfig)(options.name || undefined);
81
+ const isActiveProfile = profileName === (0, config_1.getActiveProfile)();
82
+ const nodePackageSupported = isActiveProfile
83
+ ? await (0, nodePackage_1.checkAndCacheNodePackageSupport)()
84
+ : undefined;
80
85
  if (options.json) {
81
86
  (0, output_1.printStructuredResult)({
82
87
  ok: true,
@@ -84,14 +89,21 @@ function registerProfileCommand(program) {
84
89
  baseUrl: config.baseUrl,
85
90
  token: (0, output_1.truncateToken)((0, config_1.getAccessToken)(config)),
86
91
  insecure: config.insecure,
92
+ nodePackageSupported,
87
93
  });
88
94
  return;
89
95
  }
90
96
  (0, output_1.printHeader)(`Profile: ${profileName}`);
97
+ const npStatus = nodePackageSupported === undefined
98
+ ? '未检测'
99
+ : nodePackageSupported
100
+ ? chalk_1.default.green('支持')
101
+ : chalk_1.default.red('不支持');
91
102
  (0, output_1.printKeyValue)([
92
103
  ['baseUrl', config.baseUrl],
93
104
  ['token', (0, output_1.truncateToken)((0, config_1.getAccessToken)(config)) || '(未设置)'],
94
105
  ['insecure', String(config.insecure)],
106
+ ['Node Package', npStatus],
95
107
  ]);
96
108
  });
97
109
  profileCmd
@@ -51,24 +51,23 @@ async function generateAllSkills(outputDir) {
51
51
  skillsGenerated: 0,
52
52
  errors: [],
53
53
  };
54
- //TODO 此部分目前硬编码 后续需要根据实际调整
55
54
  const categoryTypes = [
56
- { type: 'action', trigger: false, label: '动作' },
57
- { type: 'trigger', trigger: true, label: '触发器' },
58
- // { type: "tool", trigger: false, label: "工具" },
59
- // { type: "mcp", trigger: false, label: "MCP" },
60
- // { type: "model", trigger: false, label: "模型" },
61
- // { type: "memory", trigger: false, label: "记忆" },
55
+ { type: 'normal', trigger: false, label: '自动化' },
56
+ { type: 'normal-trigger', trigger: true, label: '自动化触发器' },
57
+ { type: 'mcp', trigger: true, label: 'MCP' },
58
+ { type: 'mcp-trigger', trigger: true, label: 'MCP触发器' },
62
59
  ];
63
60
  for (const ct of categoryTypes) {
64
61
  try {
65
- const categories = await (0, knowledge_1.getNodeCategories)({ type: ct.type });
62
+ const categories = await (0, knowledge_1.getNodeCategories)({ trigger: ct.trigger });
66
63
  progress.totalCategories += categories.length;
67
64
  (0, output_1.printInfo)(`发现 ${categories.length} 个${ct.label}分类`);
68
65
  const mdDir = path.join(outputDir, ct.type);
69
66
  fs.mkdirSync(mdDir, { recursive: true });
70
67
  for (const category of categories) {
71
- await processCategory(category, mdDir, progress, ct.trigger);
68
+ const categoryMdDir = path.join(mdDir, category.name);
69
+ fs.mkdirSync(categoryMdDir, { recursive: true });
70
+ await processCategory(category, categoryMdDir, progress, ct.trigger);
72
71
  }
73
72
  }
74
73
  catch (error) {
@@ -87,7 +86,7 @@ async function processCategory(category, mdDir, progress, trigger = false) {
87
86
  let hasMore = true;
88
87
  while (hasMore) {
89
88
  const result = await (0, knowledge_1.searchNodesByCategory)({
90
- app_domain_category: categoryKey,
89
+ category_name: categoryKey,
91
90
  trigger,
92
91
  page,
93
92
  size: batchSize,
@@ -130,7 +129,10 @@ async function processConnector(connector, category, mdDir, progress, trigger =
130
129
  }
131
130
  }
132
131
  async function generateSkillFile(action, category, connector, mdDir, progress) {
133
- const parameters = (0, format_1.configSchemaToParams)(action.config_schema?.sub_params);
132
+ const hasNodeSchema = !!action.node_schema;
133
+ const parameters = hasNodeSchema
134
+ ? (0, format_1.nodeSchemaToParams)(action.node_schema?.input_fields)
135
+ : (0, format_1.configSchemaToParams)(action.config_schema?.sub_params);
134
136
  const scenarios = (0, format_1.inferScenarios)(action.name, action.description);
135
137
  const skill = {
136
138
  name: action.name,
@@ -142,7 +144,7 @@ async function generateSkillFile(action, category, connector, mdDir, progress) {
142
144
  desc: category.desc || '',
143
145
  },
144
146
  action_type: action.action_type || 'action',
145
- action_key: action.action_key,
147
+ action_key: hasNodeSchema ? action.node_schema?.key || action.action_key : action.action_key,
146
148
  connector_id: action.connector_id,
147
149
  link_id: connector.id,
148
150
  action_id: action.id,
@@ -156,6 +158,21 @@ async function generateSkillFile(action, category, connector, mdDir, progress) {
156
158
  },
157
159
  parameters,
158
160
  scenarios,
161
+ node_schema: hasNodeSchema && action.node_schema
162
+ ? {
163
+ key: action.node_schema.key,
164
+ version: action.node_schema.version,
165
+ name: action.node_schema.name,
166
+ description: action.node_schema.description,
167
+ input_fields: (0, format_1.nodeSchemaToParams)(action.node_schema.input_fields),
168
+ output_fields: (action.node_schema.output_fields || []).map((o) => ({
169
+ key: o.key,
170
+ label: o.label,
171
+ type: o.type,
172
+ description: o.description ?? undefined,
173
+ })),
174
+ }
175
+ : undefined,
159
176
  };
160
177
  const safeName = (0, format_1.sanitizeFileName)(`${connector.name}_${action.name}`);
161
178
  try {
@@ -171,7 +188,39 @@ function skillToMarkdown(skill) {
171
188
  if (skill.description) {
172
189
  lines.push('## 功能描述', '', skill.description, '');
173
190
  }
174
- if (skill.parameters.length > 0) {
191
+ if (skill.node_schema) {
192
+ const ns = skill.node_schema;
193
+ lines.push('## Node Package 信息', '');
194
+ lines.push(`- **Schema Key**: \`${ns.key}\``);
195
+ lines.push(`- **Version**: ${ns.version}`);
196
+ if (ns.name)
197
+ lines.push(`- **Name**: ${ns.name}`);
198
+ if (ns.description)
199
+ lines.push(`- **Description**: ${ns.description}`);
200
+ lines.push('');
201
+ if (ns.input_fields.length > 0) {
202
+ lines.push('## 配置参数 (Input Fields)', '');
203
+ lines.push('| 字段名 | 显示名 | 类型 | 必填 | 说明 | 默认值 | 可选值 |');
204
+ lines.push('|--------|--------|------|------|------|--------|--------|');
205
+ for (const param of ns.input_fields) {
206
+ const options = (param.options || [])
207
+ ?.map((o) => (o.label !== o.key ? `\`${o.key}\` (${o.label})` : `\`${o.key}\``))
208
+ .join(', ') || '-';
209
+ lines.push(`| \`${param.name}\` | ${param.display_name} | ${param.type} | ${param.required ? '是' : '否'} | ${param.description || '-'} | ${param.default ?? '-'} | ${options} |`);
210
+ }
211
+ lines.push('');
212
+ }
213
+ if (ns.output_fields.length > 0) {
214
+ lines.push('## 输出字段', '');
215
+ lines.push('| 字段名 | 显示名 | 类型 | 说明 |');
216
+ lines.push('|--------|--------|------|------|');
217
+ for (const o of ns.output_fields) {
218
+ lines.push(`| \`${o.key}\` | ${o.label} | ${o.type} | ${o.description || '-'} |`);
219
+ }
220
+ lines.push('');
221
+ }
222
+ }
223
+ else if (skill.parameters.length > 0) {
175
224
  lines.push('## 配置参数', '');
176
225
  lines.push('| 字段名 | 显示名 | 类型 | 必填 | 说明 | 默认值 | 可选值 |');
177
226
  lines.push('|--------|--------|------|------|------|--------|--------|');
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.configSchemaToParams = configSchemaToParams;
4
+ exports.nodeSchemaToParams = nodeSchemaToParams;
4
5
  exports.inferScenarios = inferScenarios;
5
6
  exports.sanitizeFileName = sanitizeFileName;
6
7
  exports.fillConfigSchemaDefaults = fillConfigSchemaDefaults;
@@ -51,6 +52,19 @@ function inferFieldType(param) {
51
52
  }
52
53
  return 'string';
53
54
  }
55
+ function nodeSchemaToParams(inputFields) {
56
+ if (!inputFields || !Array.isArray(inputFields))
57
+ return [];
58
+ return inputFields.map((f) => ({
59
+ name: f.key,
60
+ type: f.type,
61
+ required: f.required ?? false,
62
+ description: f.help_text || '',
63
+ display_name: f.label,
64
+ options: f.options?.map((o) => ({ key: o.value, label: o.label })),
65
+ default: f.default_value,
66
+ }));
67
+ }
54
68
  function inferScenarios(actionName, description) {
55
69
  const scenarios = [];
56
70
  const text = `${actionName} ${description || ''}`.toLowerCase();
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getCachedNodePackageSupport = getCachedNodePackageSupport;
37
+ exports.getNodePackageSupportFor = getNodePackageSupportFor;
38
+ exports.checkAndCacheNodePackageSupport = checkAndCacheNodePackageSupport;
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const os = __importStar(require("os"));
42
+ const node_1 = require("@/api/node");
43
+ const config_1 = require("@/utils/config");
44
+ const STATE_DIR = path.join(os.homedir(), '.ai-flow-cli');
45
+ function getStateFile() {
46
+ return path.join(STATE_DIR, `node-package-${(0, config_1.getActiveProfile)()}.json`);
47
+ }
48
+ function ensureStateDir() {
49
+ if (!fs.existsSync(STATE_DIR)) {
50
+ fs.mkdirSync(STATE_DIR, { recursive: true });
51
+ }
52
+ }
53
+ function getCachedNodePackageSupport() {
54
+ try {
55
+ const stateFile = getStateFile();
56
+ if (fs.existsSync(stateFile)) {
57
+ const content = fs.readFileSync(stateFile, 'utf-8');
58
+ const state = JSON.parse(content);
59
+ return state.supported;
60
+ }
61
+ }
62
+ catch {
63
+ return undefined;
64
+ }
65
+ return undefined;
66
+ }
67
+ function getNodePackageSupportFor(profileName) {
68
+ try {
69
+ const stateFile = path.join(STATE_DIR, `node-package-${profileName}.json`);
70
+ if (fs.existsSync(stateFile)) {
71
+ const content = fs.readFileSync(stateFile, 'utf-8');
72
+ const state = JSON.parse(content);
73
+ return state.supported;
74
+ }
75
+ }
76
+ catch {
77
+ return undefined;
78
+ }
79
+ return undefined;
80
+ }
81
+ async function checkAndCacheNodePackageSupport() {
82
+ const supported = await (0, node_1.isNodePackageSupported)();
83
+ ensureStateDir();
84
+ fs.writeFileSync(getStateFile(), JSON.stringify({ supported, checkedAt: Date.now() }), 'utf-8');
85
+ return supported;
86
+ }
87
+ //# sourceMappingURL=nodePackage.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "digitalsee-ai-flow-cli",
3
- "version": "0.7.23-beta.1",
3
+ "version": "0.7.24-beta.2",
4
4
  "description": "AI Flow CLI - manage connection flows and node configurations",
5
5
  "main": "dist/index.js",
6
6
  "bin": {