digitalsee-ai-flow-cli 0.1.9 → 0.2.0

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.
Files changed (114) hide show
  1. package/README.md +349 -66
  2. package/dist/api/client.d.ts.map +1 -1
  3. package/dist/api/client.js +93 -17
  4. package/dist/api/client.js.map +1 -1
  5. package/dist/api/dip.d.ts.map +1 -0
  6. package/dist/api/dip.js +14 -0
  7. package/dist/api/dip.js.map +1 -0
  8. package/dist/api/flow-log.d.ts.map +1 -0
  9. package/dist/api/flow-log.js +34 -0
  10. package/dist/api/flow-log.js.map +1 -0
  11. package/dist/api/link.d.ts.map +1 -0
  12. package/dist/api/link.js +42 -0
  13. package/dist/api/link.js.map +1 -0
  14. package/dist/api/sync.d.ts.map +1 -0
  15. package/dist/api/sync.js +42 -0
  16. package/dist/api/sync.js.map +1 -0
  17. package/dist/commands/flow/account.d.ts.map +1 -1
  18. package/dist/commands/flow/account.js +10 -3
  19. package/dist/commands/flow/account.js.map +1 -1
  20. package/dist/commands/flow/analyze.d.ts.map +1 -1
  21. package/dist/commands/flow/analyze.js +40 -3
  22. package/dist/commands/flow/analyze.js.map +1 -1
  23. package/dist/commands/flow/create.d.ts.map +1 -1
  24. package/dist/commands/flow/create.js +33 -2
  25. package/dist/commands/flow/create.js.map +1 -1
  26. package/dist/commands/flow/draft.d.ts.map +1 -0
  27. package/dist/commands/flow/draft.js +173 -0
  28. package/dist/commands/flow/draft.js.map +1 -0
  29. package/dist/commands/flow/edge.d.ts.map +1 -1
  30. package/dist/commands/flow/edge.js +71 -12
  31. package/dist/commands/flow/edge.js.map +1 -1
  32. package/dist/commands/flow/helpers.d.ts.map +1 -1
  33. package/dist/commands/flow/helpers.js +99 -2
  34. package/dist/commands/flow/helpers.js.map +1 -1
  35. package/dist/commands/flow/log.d.ts.map +1 -0
  36. package/dist/commands/flow/log.js +325 -0
  37. package/dist/commands/flow/log.js.map +1 -0
  38. package/dist/commands/flow/node.d.ts.map +1 -1
  39. package/dist/commands/flow/node.js +292 -185
  40. package/dist/commands/flow/node.js.map +1 -1
  41. package/dist/commands/flow/test.d.ts.map +1 -1
  42. package/dist/commands/flow/test.js +58 -18
  43. package/dist/commands/flow/test.js.map +1 -1
  44. package/dist/commands/flow/validate.d.ts.map +1 -0
  45. package/dist/commands/flow/validate.js +85 -0
  46. package/dist/commands/flow/validate.js.map +1 -0
  47. package/dist/commands/flow/variables.d.ts.map +1 -1
  48. package/dist/commands/flow/variables.js +18 -5
  49. package/dist/commands/flow/variables.js.map +1 -1
  50. package/dist/commands/flow.d.ts.map +1 -1
  51. package/dist/commands/flow.js +6 -0
  52. package/dist/commands/flow.js.map +1 -1
  53. package/dist/commands/knowledge.d.ts.map +1 -1
  54. package/dist/commands/knowledge.js +17 -2
  55. package/dist/commands/knowledge.js.map +1 -1
  56. package/dist/commands/link/create.d.ts.map +1 -0
  57. package/dist/commands/link/create.js +38 -0
  58. package/dist/commands/link/create.js.map +1 -0
  59. package/dist/commands/link/helpers.d.ts.map +1 -0
  60. package/dist/commands/link/helpers.js +176 -0
  61. package/dist/commands/link/helpers.js.map +1 -0
  62. package/dist/commands/link/list.d.ts.map +1 -0
  63. package/dist/commands/link/list.js +42 -0
  64. package/dist/commands/link/list.js.map +1 -0
  65. package/dist/commands/link/log.d.ts.map +1 -0
  66. package/dist/commands/link/log.js +208 -0
  67. package/dist/commands/link/log.js.map +1 -0
  68. package/dist/commands/link/reset.d.ts.map +1 -0
  69. package/dist/commands/link/reset.js +23 -0
  70. package/dist/commands/link/reset.js.map +1 -0
  71. package/dist/commands/link/templates-cmd.d.ts.map +1 -0
  72. package/dist/commands/link/templates-cmd.js +26 -0
  73. package/dist/commands/link/templates-cmd.js.map +1 -0
  74. package/dist/commands/link/templates.d.ts.map +1 -0
  75. package/dist/commands/link/templates.js +104 -0
  76. package/dist/commands/link/templates.js.map +1 -0
  77. package/dist/commands/link.d.ts.map +1 -0
  78. package/dist/commands/link.js +17 -0
  79. package/dist/commands/link.js.map +1 -0
  80. package/dist/commands/sync/create.d.ts.map +1 -0
  81. package/dist/commands/sync/create.js +38 -0
  82. package/dist/commands/sync/create.js.map +1 -0
  83. package/dist/commands/sync/list.d.ts.map +1 -0
  84. package/dist/commands/sync/list.js +42 -0
  85. package/dist/commands/sync/list.js.map +1 -0
  86. package/dist/commands/sync/log.d.ts.map +1 -0
  87. package/dist/commands/sync/log.js +207 -0
  88. package/dist/commands/sync/log.js.map +1 -0
  89. package/dist/commands/sync/reset.d.ts.map +1 -0
  90. package/dist/commands/sync/reset.js +23 -0
  91. package/dist/commands/sync/reset.js.map +1 -0
  92. package/dist/commands/sync/templates-cmd.d.ts.map +1 -0
  93. package/dist/commands/sync/templates-cmd.js +26 -0
  94. package/dist/commands/sync/templates-cmd.js.map +1 -0
  95. package/dist/commands/sync.d.ts.map +1 -0
  96. package/dist/commands/sync.js +17 -0
  97. package/dist/commands/sync.js.map +1 -0
  98. package/dist/index.js +44 -0
  99. package/dist/index.js.map +1 -1
  100. package/dist/types/index.d.ts.map +1 -1
  101. package/dist/utils/common.d.ts.map +1 -0
  102. package/dist/utils/common.js +14 -0
  103. package/dist/utils/common.js.map +1 -0
  104. package/dist/utils/format.d.ts.map +1 -1
  105. package/dist/utils/format.js +24 -3
  106. package/dist/utils/format.js.map +1 -1
  107. package/dist/utils/mask.d.ts.map +1 -0
  108. package/dist/utils/mask.js +102 -0
  109. package/dist/utils/mask.js.map +1 -0
  110. package/dist/utils/output.d.ts.map +1 -1
  111. package/dist/utils/output.js +132 -27
  112. package/dist/utils/output.js.map +1 -1
  113. package/package.json +1 -1
  114. package/CHANGELOG.md +0 -84
@@ -1,40 +1,6 @@
1
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.registerFlowNode = registerFlowNode;
37
- const fs = __importStar(require("fs"));
38
4
  const flow_1 = require("@/api/flow");
39
5
  const node_1 = require("@/api/node");
40
6
  const flowAnalyzer_1 = require("@/services/flowAnalyzer");
@@ -46,16 +12,23 @@ function registerFlowNode(flowCmd) {
46
12
  nodeCmd
47
13
  .command('show <nodeId>')
48
14
  .description('获取节点详细信息')
49
- .option('--format <format>', '输出格式: table | json', 'table')
15
+ .option('--json', '以 JSON 格式输出')
50
16
  .action(async (nodeId, options) => {
51
17
  try {
18
+ if (options.json)
19
+ (0, output_1.setJsonMode)(true);
52
20
  (0, output_1.printInfo)(`正在获取节点 ${nodeId} 的详情...`);
53
21
  const nodeDetail = await (0, node_1.getNodeDetail)(nodeId);
54
22
  if (!nodeDetail) {
55
- (0, output_1.printError)(`未找到节点 ${nodeId}`);
23
+ if (options.json) {
24
+ (0, output_1.printStructuredResult)({ ok: false, message: `未找到节点 ${nodeId}` });
25
+ }
26
+ else {
27
+ (0, output_1.printError)(`未找到节点 ${nodeId}`);
28
+ }
56
29
  return;
57
30
  }
58
- if (options.format === 'json') {
31
+ if (options.json) {
59
32
  (0, output_1.printJson)(nodeDetail);
60
33
  return;
61
34
  }
@@ -118,6 +91,54 @@ function registerFlowNode(flowCmd) {
118
91
  (0, output_1.printError)(`获取节点信息失败: ${error.message}`);
119
92
  }
120
93
  });
94
+ nodeCmd
95
+ .command('list')
96
+ .description('列出流中的所有节点')
97
+ .requiredOption('--flow-id <flowId>', '连接流 ID')
98
+ .option('--json', '以 JSON 格式输出')
99
+ .action(async (options) => {
100
+ try {
101
+ if (options.json)
102
+ (0, output_1.setJsonMode)(true);
103
+ const flowId = options.flowId;
104
+ (0, output_1.printInfo)(`获取连接流 ${flowId} 的节点列表...`);
105
+ const flow = await (0, flow_1.getFlowDetail)(flowId);
106
+ const nodes = flow.nodes || [];
107
+ if (options.json) {
108
+ const list = nodes.map((n) => ({
109
+ id: n.n_id || n.id,
110
+ name: n.connector_name || n.action_name || '-',
111
+ type: n.node_type,
112
+ connectorId: n.connector_id,
113
+ actionId: n.action_id,
114
+ status: n.closed === 1 ? 'disabled' : n.status || 'normal',
115
+ position: n.position,
116
+ }));
117
+ (0, output_1.printJson)(list);
118
+ return;
119
+ }
120
+ if (nodes.length === 0) {
121
+ (0, output_1.printInfo)('流中无节点');
122
+ return;
123
+ }
124
+ const headers = ['节点ID', '名称', '类型', '状态'];
125
+ const rows = nodes.map((n) => [
126
+ String(n.n_id || n.id || '-'),
127
+ String(n.connector_name || n.action_name || '-'),
128
+ String(n.node_type || '-'),
129
+ n.closed === 1 ? '已禁用' : String(n.status || '正常'),
130
+ ]);
131
+ (0, output_1.printTable)(headers, rows);
132
+ }
133
+ catch (error) {
134
+ if (options.json) {
135
+ (0, output_1.printStructuredResult)({ ok: false, message: error.message });
136
+ }
137
+ else {
138
+ (0, output_1.printError)(`获取节点列表失败: ${error.message}`);
139
+ }
140
+ }
141
+ });
121
142
  nodeCmd
122
143
  .command('create')
123
144
  .description('在连接流中创建新节点')
@@ -132,8 +153,11 @@ function registerFlowNode(flowCmd) {
132
153
  .option('--data <json>', 'JSON 字符串,格式 {"properties": {...}},用于覆盖节点配置值')
133
154
  .option('--position-x <number>', '节点 X 坐标(默认根据已有节点自动计算)')
134
155
  .option('--position-y <number>', '节点 Y 坐标(默认根据已有节点自动计算)')
156
+ .option('--json', '以 JSON 格式输出')
135
157
  .action(async (options) => {
136
158
  try {
159
+ if (options.json)
160
+ (0, output_1.setJsonMode)(true);
137
161
  const flowId = options.flowId;
138
162
  const connectorId = options.connectorId;
139
163
  const actionId = options.actionId;
@@ -142,7 +166,13 @@ function registerFlowNode(flowCmd) {
142
166
  const nodes = flow.nodes || [];
143
167
  const isFirstNode = nodes.length === 0;
144
168
  if (isFirstNode && !options.trigger && options.node_type !== 'StartEventNode') {
145
- (0, output_1.printWarning)('流中尚无节点,建议添加 --trigger 标志创建触发器节点');
169
+ const msg = '流中尚无节点,建议添加 --trigger 标志创建触发器节点';
170
+ if (options.json) {
171
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
172
+ }
173
+ else {
174
+ (0, output_1.printWarning)(msg);
175
+ }
146
176
  return;
147
177
  }
148
178
  const defaultPos = (0, helpers_1.calcDefaultPosition)(nodes);
@@ -157,38 +187,75 @@ function registerFlowNode(flowCmd) {
157
187
  });
158
188
  const action = actions.find((a) => a.id === actionId);
159
189
  if (!action) {
160
- (0, output_1.printError)(`未找到动作 ${actionId}`);
190
+ const msg = `未找到动作 ${actionId}`;
191
+ if (options.json) {
192
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
193
+ }
194
+ else {
195
+ (0, output_1.printError)(msg);
196
+ }
161
197
  return;
162
198
  }
163
199
  let accountId = options.accountId || null;
164
200
  const accountSchemaId = action.config_schema
165
201
  ?.account_schema_id;
166
202
  if (accountSchemaId && !accountId) {
167
- (0, output_1.printWarning)(`该动作需要配置账号 (account_schema_id: ${accountSchemaId})`);
168
203
  try {
169
204
  const accounts = await (0, node_1.getAccountInstances)(connectorId, accountSchemaId);
170
- if (accounts.length > 0) {
171
- (0, output_1.printInfo)('可用账号实例:');
172
- const headers = ['ID', '名称', '认证类型'];
173
- const rows = accounts.map((a) => {
174
- const acc = a;
175
- return [
176
- String(acc.id || '-'),
177
- String(acc.name || '-'),
178
- String(acc.auth_type || '-'),
179
- ];
180
- });
181
- (0, output_1.printTable)(headers, rows);
205
+ if (accounts.length === 1) {
206
+ accountId = accounts[0].id;
207
+ (0, output_1.printInfo)(`自动选择账号: ${accounts[0].name || accountId}`);
208
+ }
209
+ else if (accounts.length > 1) {
210
+ if (options.json) {
211
+ (0, output_1.printStructuredResult)({
212
+ ok: false,
213
+ message: `该动作需要配置账号 (account_schema_id: ${accountSchemaId}),请通过 --account-id 指定`,
214
+ availableAccounts: accounts.map((a) => ({
215
+ id: a.id,
216
+ name: a.name,
217
+ auth_type: a.auth_type,
218
+ })),
219
+ });
220
+ }
221
+ else {
222
+ (0, output_1.printError)(`该动作需要配置账号 (account_schema_id: ${accountSchemaId}),可用账号:`);
223
+ const headers = ['ID', '名称', '认证类型'];
224
+ const rows = accounts.map((a) => {
225
+ const acc = a;
226
+ return [
227
+ String(acc.id || '-'),
228
+ String(acc.name || '-'),
229
+ String(acc.auth_type || '-'),
230
+ ];
231
+ });
232
+ (0, output_1.printTable)(headers, rows);
233
+ (0, output_1.printError)('请通过 --account-id <id> 指定账号实例 ID');
234
+ }
235
+ process.exitCode = 1;
236
+ return;
182
237
  }
183
238
  else {
184
- (0, output_1.printWarning)('未找到可用账号实例,请先创建账号');
239
+ const msg = '未找到可用账号实例,请先创建账号';
240
+ if (options.json) {
241
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
242
+ }
243
+ else {
244
+ (0, output_1.printError)(msg);
245
+ }
246
+ return;
185
247
  }
186
248
  }
187
249
  catch {
188
- (0, output_1.printWarning)('获取账号实例失败');
250
+ const msg = '获取账号实例失败,请通过 --account-id <id> 指定账号实例 ID';
251
+ if (options.json) {
252
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
253
+ }
254
+ else {
255
+ (0, output_1.printError)(msg);
256
+ }
257
+ return;
189
258
  }
190
- (0, output_1.printError)('请通过 --account-id <id> 指定账号实例 ID');
191
- return;
192
259
  }
193
260
  let configSchema = action.config_schema || {};
194
261
  const overrideValues = (0, helpers_1.resolveDataOption)(options, helpers_1.NODE_CREATE_DATA_SCHEMA);
@@ -200,9 +267,14 @@ function registerFlowNode(flowCmd) {
200
267
  const config = (0, format_1.deepMerge)({ properties: action.config_schema }, overrideValues ?? {});
201
268
  const defaultNodeType = trigger ? 'StartEventNode' : 'ActionNode';
202
269
  const node_type = options.nodeType || action.action_type || defaultNodeType;
203
- //此处逻辑仅因为服务端数据 trigger中的 nodeType 不正确
204
270
  if (isFirstNode && node_type !== 'StartEventNode') {
205
- (0, output_1.printError)('首个节点 nodeType 应该是 StartEventNode');
271
+ const msg = '首个节点 nodeType 应该是 StartEventNode';
272
+ if (options.json) {
273
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
274
+ }
275
+ else {
276
+ (0, output_1.printError)(msg);
277
+ }
206
278
  return;
207
279
  }
208
280
  const payload = {
@@ -218,73 +290,75 @@ function registerFlowNode(flowCmd) {
218
290
  if (accountId) {
219
291
  payload.account_id = accountId;
220
292
  }
221
- if (options.errorHandlerFile) {
222
- const ehPath = options.errorHandlerFile;
223
- if (!fs.existsSync(ehPath)) {
224
- (0, output_1.printError)(`文件不存在: ${ehPath}`);
225
- return;
226
- }
227
- try {
228
- payload.error_handler = JSON.parse(fs.readFileSync(ehPath, 'utf-8'));
229
- }
230
- catch {
231
- (0, output_1.printError)(`错误处理 JSON 文件格式无效: ${ehPath}`);
232
- return;
233
- }
234
- }
293
+ const eh = (0, helpers_1.resolveErrorHandlerFile)(options.errorHandlerFile, !!options.json);
294
+ if (eh === undefined)
295
+ return;
296
+ if (eh)
297
+ payload.error_handler = eh;
235
298
  if (configSchema) {
236
- const validationErrors = (0, format_1.validateNodePropertyValues)(configSchema);
237
- if (validationErrors.length > 0) {
238
- (0, output_1.printWarning)(`节点配置值校验发现 ${validationErrors.length} 个问题:`);
239
- for (const err of validationErrors) {
240
- const ruleLabel = err.rule === 'required' ? '必填' : '类型';
241
- (0, output_1.printWarning)(` [${ruleLabel}] ${err.message}`);
242
- }
243
- }
244
- }
245
- const specialErrors = (0, format_1.validateSpecialNodeType)(node_type, payload);
246
- if (specialErrors.length > 0) {
247
- (0, output_1.printWarning)(`特殊节点参数校验发现 ${specialErrors.length} 个问题:`);
248
- for (const err of specialErrors) {
249
- const ruleLabel = err.rule === 'required' ? '必填' : '类型';
250
- (0, output_1.printWarning)(` [${ruleLabel}] ${err.message}`);
251
- }
299
+ if ((0, helpers_1.checkPropertyValidation)((0, format_1.validateNodePropertyValues)(configSchema), !!options.json))
300
+ return;
252
301
  }
302
+ if ((0, helpers_1.checkSpecialNodeValidation)((0, format_1.validateSpecialNodeType)(node_type, payload, configSchema), !!options.json))
303
+ return;
253
304
  (0, output_1.printInfo)('正在创建节点...');
254
305
  const result = await (0, node_1.createNode)(payload);
255
- (0, output_1.printSuccess)('节点创建成功');
256
- const nPayload = payload;
257
- const nPos = nPayload.position;
258
- const pairs = [
259
- ['节点 ID', String(result.id || '-')],
260
- ['动作名称', action.name],
261
- ['连接器', action.connector_id],
262
- ['节点类型', String(nPayload.node_type)],
263
- ['触发器', trigger ? '是' : '否'],
264
- ['位置', `(${nPos.x}, ${nPos.y})`],
265
- ];
266
- if (accountId) {
267
- pairs.push(['账号 ID', accountId]);
306
+ const resultObj = result;
307
+ if (options.json) {
308
+ (0, output_1.printStructuredResult)({
309
+ ok: true,
310
+ id: String(resultObj.id || ''),
311
+ message: '节点创建成功',
312
+ nodeType: String(payload.node_type),
313
+ actionName: action.name,
314
+ connectorId: action.connector_id,
315
+ trigger,
316
+ position: { x: posX, y: posY },
317
+ });
268
318
  }
269
- if (options.errorHandlerFile) {
270
- pairs.push(['错误处理', options.errorHandlerFile]);
319
+ else {
320
+ (0, output_1.printSuccess)('节点创建成功');
321
+ const nPayload = payload;
322
+ const nPos = nPayload.position;
323
+ const pairs = [
324
+ ['节点 ID', String(resultObj.id || '-')],
325
+ ['动作名称', action.name],
326
+ ['连接器', action.connector_id],
327
+ ['节点类型', String(nPayload.node_type)],
328
+ ['触发器', trigger ? '是' : '否'],
329
+ ['位置', `(${nPos.x}, ${nPos.y})`],
330
+ ];
331
+ if (accountId) {
332
+ pairs.push(['账号 ID', accountId]);
333
+ }
334
+ if (options.errorHandlerFile) {
335
+ pairs.push(['错误处理', options.errorHandlerFile]);
336
+ }
337
+ if (options.dataFile)
338
+ pairs.push(['配置文件', options.dataFile]);
339
+ else if (options.data)
340
+ pairs.push(['配置数据', '--data']);
341
+ (0, output_1.printKeyValue)(pairs);
271
342
  }
272
- if (options.dataFile)
273
- pairs.push(['配置文件', options.dataFile]);
274
- else if (options.data)
275
- pairs.push(['配置数据', '--data']);
276
- (0, output_1.printKeyValue)(pairs);
277
343
  }
278
344
  catch (error) {
279
- (0, output_1.printError)(`创建节点失败: ${error.message}`);
345
+ if (options.json) {
346
+ (0, output_1.printStructuredResult)({ ok: false, message: error.message });
347
+ }
348
+ else {
349
+ (0, output_1.printError)(`创建节点失败: ${error.message}`);
350
+ }
280
351
  }
281
352
  });
282
353
  nodeCmd
283
354
  .command('update <nodeId>')
284
355
  .description('更新连接流中的节点配置')
285
356
  .requiredOption('--flow-id <flowId>', '连接流 ID')
286
- .option('--data-file <path>', 'JSON 文件路径,格式 {"properties": {...}},用于更新节点配置值')
287
- .option('--data <json>', 'JSON 字符串,格式 {"properties": {...}},用于更新节点配置值')
357
+ .option('--patch <json>', 'JSON 字符串,格式 {"properties": {...}},用于 patch 更新节点配置值')
358
+ .option('--patch-file <path>', 'JSON 文件路径,格式 {"properties": {...}},用于 patch 更新节点配置值')
359
+ .option('--data <json>', '(deprecated) 使用 --patch 代替')
360
+ .option('--data-file <path>', '(deprecated) 使用 --patch-file 代替')
361
+ .option('--validate-vars', '校验 patch 中的变量引用合法性', false)
288
362
  .option('--position-x <number>', '节点 X 坐标')
289
363
  .option('--position-y <number>', '节点 Y 坐标')
290
364
  .option('--note <text>', '节点备注')
@@ -292,16 +366,31 @@ function registerFlowNode(flowCmd) {
292
366
  .option('--action-id <actionId>', '更换动作 ID')
293
367
  .option('--account-id <id>', '更换账号实例 ID')
294
368
  .option('--error-handler-file <path>', '错误处理配置 JSON 文件')
369
+ .option('--json', '以 JSON 格式输出')
295
370
  .action(async (nodeId, options) => {
296
371
  try {
372
+ if (options.json)
373
+ (0, output_1.setJsonMode)(true);
297
374
  const flowId = options.flowId;
375
+ if (options.patch || options.patchFile) {
376
+ options.data = options.patch;
377
+ options.dataFile = options.patchFile;
378
+ }
379
+ else if (options.data || options.dataFile) {
380
+ (0, output_1.printWarning)('--data/--data-file 已废弃,请使用 --patch/--patch-file');
381
+ }
298
382
  (0, output_1.printInfo)(`正在获取节点 ${nodeId} 的详情...`);
299
383
  const nodeDetail = await (0, node_1.getNodeDetail)(nodeId);
300
384
  if (!nodeDetail) {
301
- (0, output_1.printError)(`未找到节点 ${nodeId}`);
385
+ const msg = `未找到节点 ${nodeId}`;
386
+ if (options.json) {
387
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
388
+ }
389
+ else {
390
+ (0, output_1.printError)(msg);
391
+ }
302
392
  return;
303
393
  }
304
- // 以 nodeDetail 为基准初始化 updateData
305
394
  const nd = nodeDetail;
306
395
  let updateData = {
307
396
  flow_id: flowId,
@@ -321,7 +410,10 @@ function registerFlowNode(flowCmd) {
321
410
  if (configValues) {
322
411
  updateData = (0, format_1.deepMerge)(updateData, configValues);
323
412
  }
324
- // CLI options 覆盖
413
+ if (options.validateVars && configValues) {
414
+ if (await (0, helpers_1.validateVariableRefs)(configValues, flowId, nodeId, !!options.json))
415
+ return;
416
+ }
325
417
  if (options.positionX !== undefined || options.positionY !== undefined) {
326
418
  const udPos = updateData.position;
327
419
  updateData.position = {
@@ -337,103 +429,118 @@ function registerFlowNode(flowCmd) {
337
429
  updateData.action_id = options.actionId;
338
430
  if (options.accountId)
339
431
  updateData.account_id = options.accountId;
340
- if (options.errorHandlerFile) {
341
- const ehPath = options.errorHandlerFile;
342
- if (!fs.existsSync(ehPath)) {
343
- (0, output_1.printError)(`文件不存在: ${ehPath}`);
344
- return;
432
+ const eh = (0, helpers_1.resolveErrorHandlerFile)(options.errorHandlerFile, !!options.json);
433
+ if (eh === undefined)
434
+ return;
435
+ if (eh)
436
+ updateData.error_handler = eh;
437
+ if (Object.keys(updateData).length === 0) {
438
+ const msg = '未指定任何更新参数';
439
+ if (options.json) {
440
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
345
441
  }
346
- try {
347
- updateData.error_handler = JSON.parse(fs.readFileSync(ehPath, 'utf-8'));
442
+ else {
443
+ (0, output_1.printWarning)(msg);
348
444
  }
349
- catch {
350
- (0, output_1.printError)(`错误处理 JSON 文件格式无效: ${ehPath}`);
351
- return;
352
- }
353
- }
354
- if (Object.keys(updateData).length === 0) {
355
- (0, output_1.printWarning)('未指定任何更新参数');
356
445
  return;
357
446
  }
358
- // 校验 properties 中各字段值与 page_control 类型匹配
359
447
  if (updateData.properties) {
360
- const validationErrors = (0, format_1.validateNodePropertyValues)(updateData.properties);
361
- if (validationErrors.length > 0) {
362
- (0, output_1.printWarning)(`节点配置值校验发现 ${validationErrors.length} 个问题:`);
363
- for (const err of validationErrors) {
364
- const ruleLabel = err.rule === 'required' ? '必填' : '类型';
365
- (0, output_1.printWarning)(` [${ruleLabel}] ${err.message}`);
366
- }
448
+ if ((0, helpers_1.checkPropertyValidation)((0, format_1.validateNodePropertyValues)(updateData.properties), !!options.json))
367
449
  return;
368
- }
369
450
  }
370
- const specialErrors = (0, format_1.validateSpecialNodeType)(String(nd.node_type), updateData);
371
- if (specialErrors.length > 0) {
372
- (0, output_1.printWarning)(`特殊节点参数校验发现 ${specialErrors.length} 个问题:`);
373
- for (const err of specialErrors) {
374
- const ruleLabel = err.rule === 'required' ? '必填' : '类型';
375
- (0, output_1.printWarning)(` [${ruleLabel}] ${err.message}`);
376
- }
451
+ if ((0, helpers_1.checkSpecialNodeValidation)((0, format_1.validateSpecialNodeType)(String(nd.node_type), updateData, nd.config_schema), !!options.json))
377
452
  return;
378
- }
379
453
  (0, output_1.printInfo)('正在更新节点...');
380
454
  await (0, node_1.updateNode)(nodeId, updateData);
381
- (0, output_1.printSuccess)('节点更新成功');
382
- const pairs = [['节点 ID', nodeId]];
383
- if (options.dataFile)
384
- pairs.push(['配置文件', options.dataFile]);
385
- else if (options.data)
386
- pairs.push(['配置数据', '--data']);
387
- if (updateData.position) {
388
- const udPos = updateData.position;
389
- pairs.push(['位置', `(${udPos.x}, ${udPos.y})`]);
455
+ if (options.json) {
456
+ const result = {
457
+ ok: true,
458
+ id: nodeId,
459
+ message: '节点更新成功',
460
+ };
461
+ if (updateData.position) {
462
+ const udPos = updateData.position;
463
+ result.position = { x: udPos.x, y: udPos.y };
464
+ }
465
+ if (options.note !== undefined)
466
+ result.note = options.note;
467
+ if (options.connectorId)
468
+ result.connectorId = options.connectorId;
469
+ if (options.actionId)
470
+ result.actionId = options.actionId;
471
+ if (options.accountId)
472
+ result.accountId = options.accountId;
473
+ (0, output_1.printStructuredResult)(result);
474
+ }
475
+ else {
476
+ (0, output_1.printSuccess)('节点更新成功');
477
+ const pairs = [['节点 ID', nodeId]];
478
+ if (options.dataFile)
479
+ pairs.push(['配置文件', options.dataFile]);
480
+ else if (options.data)
481
+ pairs.push(['配置数据', '--data']);
482
+ if (updateData.position) {
483
+ const udPos = updateData.position;
484
+ pairs.push(['位置', `(${udPos.x}, ${udPos.y})`]);
485
+ }
486
+ if (options.note !== undefined)
487
+ pairs.push(['备注', options.note]);
488
+ if (options.connectorId)
489
+ pairs.push(['连接器', options.connectorId]);
490
+ if (options.actionId)
491
+ pairs.push(['动作', options.actionId]);
492
+ if (options.accountId)
493
+ pairs.push(['账号 ID', options.accountId]);
494
+ if (options.errorHandlerFile)
495
+ pairs.push(['错误处理', options.errorHandlerFile]);
496
+ (0, output_1.printKeyValue)(pairs);
390
497
  }
391
- if (options.note !== undefined)
392
- pairs.push(['备注', options.note]);
393
- if (options.connectorId)
394
- pairs.push(['连接器', options.connectorId]);
395
- if (options.actionId)
396
- pairs.push(['动作', options.actionId]);
397
- if (options.accountId)
398
- pairs.push(['账号 ID', options.accountId]);
399
- if (options.errorHandlerFile)
400
- pairs.push(['错误处理', options.errorHandlerFile]);
401
- (0, output_1.printKeyValue)(pairs);
402
498
  }
403
499
  catch (error) {
404
- (0, output_1.printError)(`更新节点失败: ${error.message}`);
500
+ if (options.json) {
501
+ (0, output_1.printStructuredResult)({ ok: false, message: error.message });
502
+ }
503
+ else {
504
+ (0, output_1.printError)(`更新节点失败: ${error.message}`);
505
+ }
405
506
  }
406
507
  });
407
508
  nodeCmd
408
509
  .command('delete <nodeId>')
409
510
  .description('删除连接流中的节点')
410
511
  .requiredOption('--flow-id <flowId>', '连接流 ID')
411
- .option('--force', '跳过确认提示,直接删除', false)
512
+ .option('--yes', '跳过确认,直接删除')
513
+ .option('--json', '以 JSON 格式输出')
412
514
  .action(async (nodeId, options) => {
413
515
  try {
414
- if (!options.force) {
415
- (0, output_1.printWarning)(`确认删除节点 ${nodeId}?`);
416
- const readline = require('readline').createInterface({
417
- input: process.stdin,
418
- output: process.stdout,
419
- });
420
- const answer = await new Promise((resolve) => {
421
- readline.question('输入 y 确认: ', (ans) => {
422
- readline.close();
423
- resolve(ans);
424
- });
425
- });
426
- if (answer.toLowerCase() !== 'y') {
427
- (0, output_1.printInfo)('已取消删除');
428
- return;
429
- }
516
+ if (options.json)
517
+ (0, output_1.setJsonMode)(true);
518
+ if (!options.yes) {
519
+ const msg = {
520
+ ok: false,
521
+ message: `确认删除节点 ${nodeId}?`,
522
+ suggestion: `使用 --yes 标志直接删除: ai-flow flow node delete ${nodeId} --flow-id ${options.flowId} --yes`,
523
+ };
524
+ (0, output_1.printStructuredResult)(msg);
525
+ process.exitCode = 1;
526
+ return;
430
527
  }
431
528
  (0, output_1.printInfo)(`正在删除节点 ${nodeId}...`);
432
529
  await (0, node_1.deleteNode)(nodeId);
433
- (0, output_1.printSuccess)(`节点 ${nodeId} 已删除`);
530
+ if (options.json) {
531
+ (0, output_1.printStructuredResult)({ ok: true, id: nodeId, message: `节点 ${nodeId} 已删除` });
532
+ }
533
+ else {
534
+ (0, output_1.printSuccess)(`节点 ${nodeId} 已删除`);
535
+ }
434
536
  }
435
537
  catch (error) {
436
- (0, output_1.printError)(`删除节点失败: ${error.message}`);
538
+ if (options.json) {
539
+ (0, output_1.printStructuredResult)({ ok: false, message: error.message });
540
+ }
541
+ else {
542
+ (0, output_1.printError)(`删除节点失败: ${error.message}`);
543
+ }
437
544
  }
438
545
  });
439
546
  }