digitalsee-ai-flow-cli 0.9.24-beta → 0.9.29-beta

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.
@@ -32,16 +32,23 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.parseActionIdRef = parseActionIdRef;
37
40
  exports.resolveAction = resolveAction;
38
41
  exports.registerFlowNode = registerFlowNode;
39
42
  const fs = __importStar(require("fs"));
43
+ const chalk_1 = __importDefault(require("chalk"));
40
44
  const flow_1 = require("@/api/flow");
41
45
  const node_1 = require("@/api/node");
42
46
  const knowledge_1 = require("@/api/knowledge");
43
47
  const flowAnalyzer_1 = require("@/services/flowAnalyzer");
44
48
  const format_1 = require("@/utils/format");
49
+ const nodeEnvelope_1 = require("@/utils/nodeEnvelope");
50
+ const switchBranch_1 = require("@/utils/switchBranch");
51
+ const node_type_1 = require("@/constants/node-type");
45
52
  const helpers_1 = require("@/commands/flow/helpers");
46
53
  const output_1 = require("@/utils/output");
47
54
  const exit_code_1 = require("@/constants/exit-code");
@@ -49,7 +56,7 @@ const ACTION_ID_HELP = '动作 ID:纯数字(legacy 动作 id)、action_key
49
56
  /**
50
57
  * 解析 --action-id 的三种形式:
51
58
  * - `action_key@version` → NodePackage 精确版本
52
- * - 纯数字 → legacy 动作 id
59
+ * - 纯数字 → 先按 legacy 动作 id,未命中回退按 action_key(两个数字空间,形态无法区分)
53
60
  * - `action_key` → NodePackage 最新版(version 需经列表解析)
54
61
  */
55
62
  function parseActionIdRef(actionId) {
@@ -69,10 +76,16 @@ function parseActionIdRef(actionId) {
69
76
  /**
70
77
  * 按解析结果获取动作(以 RCS 为标准):
71
78
  * a. `key@version` → getActionNodeSchema(NodePackage 权威 schema)
72
- * b. 纯数字 → getActionDetail(legacy /acm/actions/:id,Long id 直接作创建身份)
79
+ * b. 纯数字 → 先按 legacy 后端 id 走 getActionDetail(/acm/actions/:id,Long id
80
+ * 直接作创建身份);未命中(空 data 信封 / 查询失败)回退按 action_key 走
81
+ * connectable——后端 id 与 action_key 是两个数字空间,钉钉等连接器的
82
+ * action_key 即长数字形态,仅凭形态无法区分归属
73
83
  * c. `key`(需 linkId)→ connectable 按 link_id 解析(RCS loadConnectableNodeActions 语义),
74
84
  * 创建身份遵循 RCS resolveClassicCreateActionId:Long id 直接用,否则 '0' 哨兵
75
85
  * (真实身份是 action_key + action_ver)
86
+ *
87
+ * ctx.scene 为 connectable 检索场景值:MCP 流(含 MCP_TRIGGER_NODE)需传 'mcp',
88
+ * 缺省空串(常规自动化)。
76
89
  */
77
90
  async function resolveAction(actionId, ctx) {
78
91
  const ref = parseActionIdRef(actionId);
@@ -82,18 +95,21 @@ async function resolveAction(actionId, ctx) {
82
95
  }
83
96
  if (!ref.key) {
84
97
  try {
85
- return await (0, node_1.getActionDetail)(actionId);
98
+ const legacy = await (0, node_1.getActionDetail)(actionId);
99
+ if (legacy)
100
+ return legacy;
86
101
  }
87
102
  catch {
88
- return undefined;
103
+ // legacy 数字 id 查询失败:可能是 action_key 形态的长数字,回退 connectable
89
104
  }
105
+ ref.key = actionId;
90
106
  }
91
107
  if (!ctx.linkId) {
92
108
  return undefined;
93
109
  }
94
110
  const result = await (0, knowledge_1.searchNodesByCategory)({
95
111
  mode: 'independent',
96
- scene: '',
112
+ scene: ctx.scene ?? '',
97
113
  linkId: ctx.linkId,
98
114
  flowId: ctx.flowId,
99
115
  trigger: ctx.trigger || false,
@@ -109,9 +125,16 @@ async function resolveAction(actionId, ctx) {
109
125
  }
110
126
  return matched;
111
127
  }
112
- /** RCS classicNodeActionIdentity:创建接口需要 Long action_id;非 Long(字符串 key 等)用 '0' 哨兵 */
128
+ /**
129
+ * RCS classicNodeActionIdentity:创建接口需要 Long 后端 action_id;非 Long 用 '0' 哨兵
130
+ * (真实身份是 action_key + action_ver)。
131
+ * connectable 归一化把 id 改写为 action_key(UI 身份),真后端 id 在 backend_action_id,
132
+ * 因此优先取后者;legacy getActionDetail 等未改写语义的数据源回退取 id。
133
+ * 不可误取被改写后的 id:钉钉等连接器的 action_key 是纯数字长串,会骗过 /^\d+$/,
134
+ * 服务端按实体 id 查不到动作直接 NPE。
135
+ */
113
136
  function toClassicCreateActionId(action) {
114
- const raw = action.id;
137
+ const raw = action.backend_action_id ?? action.id;
115
138
  if (raw !== undefined && raw !== null && /^\d+$/.test(String(raw))) {
116
139
  return String(raw);
117
140
  }
@@ -154,12 +177,28 @@ function registerFlowNode(flowCmd) {
154
177
  return;
155
178
  }
156
179
  if (options.json) {
157
- (0, output_1.printStructuredResult)({ ok: true, data: nodeDetail });
180
+ // NodePackage 信封解包:properties 解包为业务字段,
181
+ // proxy_id / mcp_tool_description 单独识别(envelope 字段)
182
+ const envelope = (0, nodeEnvelope_1.unwrapNodeDetailEnvelope)(nodeDetail);
183
+ const data = { ...nodeDetail };
184
+ if (Object.keys(envelope.properties).length > 0) {
185
+ data.properties = envelope.properties;
186
+ }
187
+ if (envelope.proxyId !== undefined) {
188
+ data.proxy_id = envelope.proxyId;
189
+ }
190
+ if (envelope.mcpToolDescription !== undefined) {
191
+ data.mcp_tool_description = envelope.mcpToolDescription;
192
+ }
193
+ (0, output_1.printStructuredResult)({ ok: true, data });
158
194
  return;
159
195
  }
160
196
  const nd = nodeDetail;
161
197
  const pos = nd.position;
162
198
  const cs = nd.config_schema;
199
+ // NodePackage 信封解包:properties 可能被包裹为
200
+ // { properties: {...}, account_id, proxy_id, mcp_tool_description }
201
+ const envelope = (0, nodeEnvelope_1.unwrapNodeDetailEnvelope)(nd);
163
202
  (0, output_1.printHeader)(`节点详情: ${nd.id || nodeId}`);
164
203
  (0, output_1.printKeyValue)([
165
204
  ['节点 ID', String(nd.n_id || nd.id || '-')],
@@ -172,7 +211,21 @@ function registerFlowNode(flowCmd) {
172
211
  ['描述', String(nd.description || '(无)')],
173
212
  ['账号 ID', String(nd.account_id || '(无)')],
174
213
  ['账号 Schema ID', String(cs?.account_schema_id || nd.account_schema_id || '(无)')],
214
+ [
215
+ '代理 ID',
216
+ envelope.proxyId ? (envelope.proxyId === '0' ? '(不使用)' : envelope.proxyId) : '(无)',
217
+ ],
175
218
  ]);
219
+ if (envelope.mcpToolDescription) {
220
+ (0, output_1.printSubHeader)('MCP 工具描述');
221
+ console.log(envelope.mcpToolDescription);
222
+ }
223
+ // SwitchNode 端口约定提示:分支 id 即输出端口名,编辑名称/条件不影响端口
224
+ if (nd.node_type === 'SwitchNode') {
225
+ (0, output_1.printSubHeader)('Switch 分支端口说明');
226
+ (0, output_1.printInfo)('分支 id 即输出端口名(连线目标端口),修改分支名称或条件不会改变端口名');
227
+ (0, output_1.printInfo)(`分支 id 必须以 "branch" 开头且不得为 "default-branch"(默认分支为固定端口),修改 id 会导致原有连线断链`);
228
+ }
176
229
  if (cs?.sub_params && Array.isArray(cs.sub_params) && cs.sub_params.length > 0) {
177
230
  const subParams = cs.sub_params;
178
231
  (0, output_1.printSubHeader)(`配置参数 (${subParams.length})`);
@@ -202,9 +255,11 @@ function registerFlowNode(flowCmd) {
202
255
  });
203
256
  (0, output_1.printTable)(headers, rows);
204
257
  }
205
- if (nodeDetail.properties && Object.keys(nodeDetail.properties).length > 0) {
258
+ if (nodeDetail.properties &&
259
+ Object.keys(nodeDetail.properties).length > 0 &&
260
+ Object.keys(envelope.properties).length > 0) {
206
261
  (0, output_1.printSubHeader)('当前属性值');
207
- (0, output_1.printJson)(nodeDetail.properties);
262
+ (0, output_1.printJson)(envelope.properties);
208
263
  }
209
264
  if (nodeDetail.error_handler) {
210
265
  (0, output_1.printSubHeader)('错误处理配置');
@@ -270,8 +325,8 @@ function registerFlowNode(flowCmd) {
270
325
  .requiredOption('--flow-id <flowId>', '连接流 ID')
271
326
  .requiredOption('--connector-id <connectorId>', '连接器 ID')
272
327
  .requiredOption('--action-id <actionId>', ACTION_ID_HELP)
273
- .option('--trigger', '作为触发器节点(流中第一个节点建议启用)', false)
274
- .option('--node-type <type>', '节点类型或动作类型')
328
+ .option('--trigger', '作为触发器节点(等价于 --node-type StartEventNode,二者不可同时指定;流中第一个节点建议启用)', false)
329
+ .option('--node-type <type>', `节点类型(${helpers_1.VALID_CREATE_NODE_TYPES.join(' / ')};默认 ActionNode,触发器为 StartEventNode)`)
275
330
  .option('--account-id <id>', '账号实例 ID(需要账号鉴权时指定)')
276
331
  .option('--error-handler-file <path>', '错误处理配置 JSON 文件')
277
332
  .option('--name <name>', '节点名称,描述节点用途')
@@ -288,13 +343,44 @@ function registerFlowNode(flowCmd) {
288
343
  const flowId = options.flowId;
289
344
  const connectorId = options.connectorId;
290
345
  const actionId = options.actionId;
346
+ // --trigger 与 --node-type 互斥:--trigger 等价于 --node-type StartEventNode,
347
+ // 同时指定时语义冲突(trigger 推断与显式类型可能矛盾),发请求前提前拦截
348
+ if (options.trigger && options.nodeType) {
349
+ const msg = '--trigger 与 --node-type 不能同时指定(--trigger 等价于 --node-type StartEventNode,二者只需其一)';
350
+ if (options.json) {
351
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
352
+ }
353
+ else {
354
+ (0, output_1.printError)(msg);
355
+ }
356
+ process.exitCode = exit_code_1.ExitCode.FAILURE;
357
+ return;
358
+ }
359
+ // --node-type 是显式用户输入,入口处即校验合法性(无需等动作解析,失败更快)
360
+ if (options.nodeType) {
361
+ const nodeTypeError = (0, helpers_1.validateNodeTypeValue)(options.nodeType, '--node-type 参数');
362
+ if (nodeTypeError) {
363
+ if (options.json) {
364
+ (0, output_1.printStructuredResult)({
365
+ ok: false,
366
+ message: nodeTypeError,
367
+ validNodeTypes: [...helpers_1.VALID_CREATE_NODE_TYPES],
368
+ });
369
+ }
370
+ else {
371
+ (0, output_1.printError)(nodeTypeError);
372
+ }
373
+ process.exitCode = exit_code_1.ExitCode.FAILURE;
374
+ return;
375
+ }
376
+ }
291
377
  (0, output_1.printInfo)(`获取连接流 ${flowId} 的详情...`);
292
378
  const flow = await (0, flow_1.getFlowDetail)(flowId);
293
379
  if ((0, helpers_1.checkFlowNotOnline)(flow, !!options.json))
294
380
  return;
295
381
  const nodes = flow.nodes || [];
296
382
  const isFirstNode = nodes.length === 0;
297
- if (isFirstNode && !options.trigger && options.node_type !== 'StartEventNode') {
383
+ if (isFirstNode && !options.trigger && !(0, node_type_1.isTriggerNodeType)(options.nodeType ?? '')) {
298
384
  const msg = '流中尚无节点,建议添加 --trigger 标志创建触发器节点';
299
385
  if (options.json) {
300
386
  (0, output_1.printStructuredResult)({ ok: false, message: msg });
@@ -307,12 +393,19 @@ function registerFlowNode(flowCmd) {
307
393
  const defaultPos = (0, helpers_1.calcDefaultPosition)(nodes);
308
394
  let posX = options.positionX !== undefined ? Number(options.positionX) : defaultPos.x;
309
395
  let posY = options.positionY !== undefined ? Number(options.positionY) : defaultPos.y;
310
- const trigger = options.trigger || options.node_type === 'StartEventNode' || false;
396
+ // 触发器推断:--trigger 或显式指定触发器类节点类型
397
+ // (StartEventNode / UserSyncAiNode / UserPushAiNode,见 constants/node-type)
398
+ const trigger = options.trigger || (0, node_type_1.isTriggerNodeType)(options.nodeType ?? '') || false;
399
+ // MCP 流场景值:流中含 MCP_TRIGGER_NODE 即 MCP 流,动作检索 scene 需传 'mcp'
400
+ // (本次显式创建 MCP_TRIGGER_NODE 时同样按 MCP 场景检索)
401
+ const isMcpScene = nodes.some((n) => n.node_type === 'MCP_TRIGGER_NODE') ||
402
+ options.nodeType === 'MCP_TRIGGER_NODE';
311
403
  (0, output_1.printInfo)(`解析动作 ${actionId}...`);
312
404
  const action = await resolveAction(actionId, {
313
405
  linkId: connectorId,
314
406
  flowId,
315
407
  trigger,
408
+ scene: isMcpScene ? 'mcp' : '',
316
409
  });
317
410
  if (action && !action.id) {
318
411
  // legacy 哨兵:创建接口要求必传 action_id,NodePackage 动作数字 id <=0,
@@ -369,13 +462,18 @@ function registerFlowNode(flowCmd) {
369
462
  return;
370
463
  }
371
464
  else {
372
- const msg = '未找到可用账号实例,请先创建账号';
465
+ const msg = `未找到可用账号实例 (account_schema_id: ${accountSchemaId}),可先运行: ai-flow flow account create ${connectorId} --account-schema-id ${accountSchemaId} --data '<凭据JSON>'`;
373
466
  if (options.json) {
374
- (0, output_1.printStructuredResult)({ ok: false, message: msg });
467
+ (0, output_1.printStructuredResult)({
468
+ ok: false,
469
+ message: msg,
470
+ hint: `flow account create ${connectorId} --account-schema-id ${accountSchemaId}`,
471
+ });
375
472
  }
376
473
  else {
377
474
  (0, output_1.printError)(msg);
378
475
  }
476
+ process.exitCode = exit_code_1.ExitCode.FAILURE;
379
477
  return;
380
478
  }
381
479
  }
@@ -403,19 +501,60 @@ function registerFlowNode(flowCmd) {
403
501
  posY = Number(ovPos.y) || posY;
404
502
  }
405
503
  let config;
504
+ let appliedDefaults = [];
406
505
  if (hasNodeSchema) {
407
- // node_schema 模式:properties 直接透传,不经过 deepMerge
408
- config = {
409
- properties: overrideValues?.properties ?? {},
410
- };
506
+ // node_schema 模式:递归填充 input_fields 默认值后,浅合并用户 override
507
+ const inputFields = (action.node_schema?.input_fields ?? []);
508
+ // PROXY 字段误写拦截:对照 input_fields 中 type=PROXY 的字段
509
+ const proxyMisuse = (0, helpers_1.findProxyFieldMisuse)(overrideValues?.properties, inputFields);
510
+ if (proxyMisuse.length > 0) {
511
+ const msg = proxyMisuse.join('\n');
512
+ if (options.json) {
513
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
514
+ }
515
+ else {
516
+ (0, output_1.printError)(msg);
517
+ }
518
+ process.exitCode = exit_code_1.ExitCode.FAILURE;
519
+ return;
520
+ }
521
+ const { properties, applied } = (0, helpers_1.buildNodeSchemaDefaults)(inputFields);
522
+ const userProps = overrideValues?.properties ?? {};
523
+ const userKeys = new Set(Object.keys(userProps));
524
+ config = { properties: { ...properties, ...userProps } };
525
+ appliedDefaults = applied.filter((e) => !userKeys.has(e.field));
411
526
  }
412
527
  else {
413
- config = (0, format_1.deepMerge)({ properties: action.config_schema }, overrideValues ?? {});
528
+ // classic 模式:先按注册表填充默认值,再 deepMerge 用户 override(override 优先)
529
+ const built = (0, helpers_1.buildClassicConfigWithDefaults)(action.config_schema ?? {}, overrideValues);
530
+ config = built.config;
531
+ appliedDefaults = built.applied;
414
532
  }
415
- const defaultNodeType = trigger ? 'StartEventNode' : 'ActionNode';
416
- const node_type = options.nodeType || action.action_type || defaultNodeType;
417
- if (isFirstNode && node_type !== 'StartEventNode') {
418
- const msg = '首个节点 nodeType 应该是 StartEventNode';
533
+ // legacy 详情接口的 action_type 是 DB 原值,不能直接当 nodeType,按 v2 规则推算
534
+ // (connectable/v2 的 action_type 已是终值,推算幂等;--node-type 显式指定优先)
535
+ const node_type = options.nodeType ||
536
+ (0, helpers_1.deriveNodeTypeFromAction)(action.action_type, action.node_struct, trigger);
537
+ // --node-type 已在入口校验;此处只兜底推算结果(推算输出恒为合法值,
538
+ // 此检查作为 payload 正确性的最后防线保留)
539
+ if (!options.nodeType) {
540
+ const nodeTypeError = (0, helpers_1.validateNodeTypeValue)(node_type, 'action_type 推算');
541
+ if (nodeTypeError) {
542
+ if (options.json) {
543
+ (0, output_1.printStructuredResult)({
544
+ ok: false,
545
+ message: nodeTypeError,
546
+ validNodeTypes: [...helpers_1.VALID_CREATE_NODE_TYPES],
547
+ });
548
+ }
549
+ else {
550
+ (0, output_1.printError)(nodeTypeError);
551
+ }
552
+ process.exitCode = exit_code_1.ExitCode.FAILURE;
553
+ return;
554
+ }
555
+ }
556
+ if (isFirstNode && !(0, node_type_1.isTriggerNodeType)(node_type)) {
557
+ const msg = `首个节点应为触发器类型(${[...node_type_1.TRIGGER_NODE_TYPES].join(' / ')})`;
419
558
  if (options.json) {
420
559
  (0, output_1.printStructuredResult)({ ok: false, message: msg });
421
560
  }
@@ -424,6 +563,13 @@ function registerFlowNode(flowCmd) {
424
563
  }
425
564
  return;
426
565
  }
566
+ // NodePackage 信封对齐:PROXY 字段提升为顶层 proxy_id('0'=不使用)。
567
+ // 默认值填充可能注入 PROXY 字段(注册表默认 ''),此处统一剥除。
568
+ if (hasNodeSchema) {
569
+ const inputFields = (action.node_schema?.input_fields ?? []);
570
+ const extracted = (0, nodeEnvelope_1.extractProxyField)(config.properties ?? {}, inputFields);
571
+ config = { properties: extracted.properties };
572
+ }
427
573
  const payload = {
428
574
  ...config,
429
575
  flow_id: flowId,
@@ -437,6 +583,13 @@ function registerFlowNode(flowCmd) {
437
583
  if (accountId) {
438
584
  payload.account_id = accountId;
439
585
  }
586
+ // 信封顶层宿主字段:--data 中的 proxy_id / mcp_tool_description 自动转发
587
+ if (overrideValues?.proxy_id !== undefined) {
588
+ payload.proxy_id = (0, nodeEnvelope_1.normalizeProxyId)(overrideValues.proxy_id);
589
+ }
590
+ if (overrideValues?.mcp_tool_description !== undefined) {
591
+ payload.mcp_tool_description = String(overrideValues.mcp_tool_description);
592
+ }
440
593
  if (hasNodeSchema) {
441
594
  // node_schema.key/version 是权威身份;action_ver 直接字段兜底
442
595
  // (node_schema 缺失但 action_ver 存在的场景)
@@ -462,15 +615,34 @@ function registerFlowNode(flowCmd) {
462
615
  }
463
616
  }
464
617
  else if (configSchema) {
465
- if ((0, helpers_1.checkPropertyValidation)((0, format_1.validateNodePropertyValues)(configSchema), !!options.json))
618
+ // 校验填充默认值后的 schema(value 已填充,必填校验更准确);
619
+ // 类型/格式仅校验用户显式提供的字段——schema 预置值不拦 create
620
+ // (实测:successExp 类 JSON 控件预置 JS 表达式默认值曾被误拦)
621
+ if ((0, helpers_1.checkPropertyValidation)((0, format_1.validateNodePropertyValues)(config.properties ?? configSchema, { userFields: (0, format_1.collectUserFieldNames)(overrideValues?.properties) }), !!options.json))
466
622
  return;
467
623
  }
468
624
  if (!hasNodeSchema && configSchema) {
625
+ // rcs 对齐:is_null / is_not_null 条件 value 提交前归一为空字符串
626
+ if (node_type === 'SwitchNode') {
627
+ (0, switchBranch_1.normalizeSwitchBranchNullConditionValues)(payload);
628
+ }
469
629
  if ((0, helpers_1.checkSpecialNodeValidation)((0, format_1.validateSpecialNodeType)(node_type, payload, configSchema), !!options.json))
470
630
  return;
471
631
  }
472
632
  (0, output_1.printInfo)('正在创建节点...');
473
633
  const result = await (0, node_1.createNode)(payload);
634
+ // 服务端 200 但未返回节点数据(信封既无失败信号也无节点体):不能当作创建成功
635
+ if (!result || typeof result !== 'object') {
636
+ const msg = '服务端未返回节点数据(节点可能未创建),请用 flow node list --flow-id 读回确认';
637
+ if (options.json) {
638
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
639
+ }
640
+ else {
641
+ (0, output_1.printError)(msg);
642
+ }
643
+ process.exitCode = exit_code_1.ExitCode.FAILURE;
644
+ return;
645
+ }
474
646
  const resultObj = result;
475
647
  // Loop/SelfLoop 需要自环边(loop → input),提示补连而不自动写流(CLI 无状态,避免隐式副作用)
476
648
  const selfLoopHint = payload.node_type === 'LoopNode' || payload.node_type === 'SelfLoopNode'
@@ -493,6 +665,19 @@ function registerFlowNode(flowCmd) {
493
665
  jsonResult.customName = options.name;
494
666
  if (options.note !== undefined)
495
667
  jsonResult.note = options.note;
668
+ if (payload.proxy_id !== undefined)
669
+ jsonResult.proxyId = payload.proxy_id;
670
+ if (payload.mcp_tool_description !== undefined) {
671
+ jsonResult.mcpToolDescription = payload.mcp_tool_description;
672
+ }
673
+ if (appliedDefaults.length > 0) {
674
+ // 区分「已填默认值」与「用户覆盖」:appliedDefaults 仅含未被 override 的默认填充
675
+ jsonResult.appliedDefaults = appliedDefaults;
676
+ jsonResult.appliedDefaultsCount = appliedDefaults.length;
677
+ }
678
+ if (overrideValues?.properties) {
679
+ jsonResult.userOverrides = Object.keys(overrideValues.properties);
680
+ }
496
681
  (0, output_1.printStructuredResult)(jsonResult);
497
682
  }
498
683
  else {
@@ -516,6 +701,15 @@ function registerFlowNode(flowCmd) {
516
701
  if (accountId) {
517
702
  pairs.push(['账号 ID', accountId]);
518
703
  }
704
+ if (payload.proxy_id !== undefined) {
705
+ pairs.push([
706
+ '代理 ID',
707
+ payload.proxy_id === '0' ? '(不使用)' : String(payload.proxy_id),
708
+ ]);
709
+ }
710
+ if (payload.mcp_tool_description !== undefined) {
711
+ pairs.push(['MCP 工具描述', String(payload.mcp_tool_description)]);
712
+ }
519
713
  if (options.errorHandlerFile) {
520
714
  pairs.push(['错误处理', options.errorHandlerFile]);
521
715
  }
@@ -523,6 +717,13 @@ function registerFlowNode(flowCmd) {
523
717
  pairs.push(['配置文件', options.dataFile]);
524
718
  else if (options.data)
525
719
  pairs.push(['配置数据', '--data']);
720
+ if (appliedDefaults.length > 0) {
721
+ (0, output_1.printSubHeader)(`已填默认值 (${appliedDefaults.length})`);
722
+ for (const d of appliedDefaults) {
723
+ const displayVal = typeof d.value === 'object' ? JSON.stringify(d.value) : String(d.value);
724
+ console.log(` ${chalk_1.default.cyan(d.path)} = ${displayVal}${chalk_1.default.gray(' (默认值)')}`);
725
+ }
726
+ }
526
727
  (0, output_1.printKeyValue)(pairs);
527
728
  if (selfLoopHint)
528
729
  (0, output_1.printInfo)(selfLoopHint);
@@ -618,6 +819,10 @@ function registerFlowNode(flowCmd) {
618
819
  if ((0, helpers_1.checkFlowNotOnline)(nodeFlow, !!options.json))
619
820
  return;
620
821
  const nd = nodeDetail;
822
+ // NodePackage 信封解包:properties 可能被包裹为
823
+ // { properties: {...}, account_id, proxy_id, mcp_tool_description }
824
+ const existingEnvelope = (0, nodeEnvelope_1.unwrapNodeDetailEnvelope)(nd);
825
+ const existingProps = existingEnvelope.properties;
621
826
  const hasNodeSchema = !!(nd.action_key && nd.action_ver);
622
827
  // node_schema 模式:优先 getActionNodeSchema 拿权威 schema(action_key@action_ver)
623
828
  let nodeSchema;
@@ -629,13 +834,17 @@ function registerFlowNode(flowCmd) {
629
834
  // 获取失败则回退到旧模式
630
835
  }
631
836
  }
632
- // --action-id 指定时按新身份解析(三种形式)
837
+ // --action-id 指定时按新身份解析(三种形式);
838
+ // 触发器节点与 MCP 流(含 MCP_TRIGGER_NODE)分别影响检索的 trigger / scene 参数
633
839
  let replacedAction;
634
840
  if (options.actionId) {
841
+ const isMcpScene = (nodeFlow.nodes || []).some((n) => n.node_type === 'MCP_TRIGGER_NODE') ||
842
+ nd.node_type === 'MCP_TRIGGER_NODE';
635
843
  replacedAction = await resolveAction(options.actionId, {
636
844
  linkId: options.connectorId || nd.connector_id || undefined,
637
845
  flowId,
638
- trigger: nd.node_type === 'StartEventNode',
846
+ trigger: (0, node_type_1.isTriggerNodeType)(String(nd.node_type || '')),
847
+ scene: isMcpScene ? 'mcp' : '',
639
848
  });
640
849
  if (!replacedAction) {
641
850
  const msg = `未找到动作 ${options.actionId}`;
@@ -651,7 +860,6 @@ function registerFlowNode(flowCmd) {
651
860
  }
652
861
  let updateData;
653
862
  if (nodeSchema) {
654
- const existingProps = nd.properties ?? {};
655
863
  const pos = nd.position ?? { x: 0, y: 0 };
656
864
  updateData = {
657
865
  flow_id: flowId,
@@ -666,6 +874,13 @@ function registerFlowNode(flowCmd) {
666
874
  ? (replacedAction.node_schema?.version ?? replacedAction.action_ver)
667
875
  : nodeSchema.version,
668
876
  };
877
+ // 信封顶层宿主字段回传:proxy_id('0'=不使用)/ mcp_tool_description
878
+ if (existingEnvelope.proxyId !== undefined) {
879
+ updateData.proxy_id = existingEnvelope.proxyId;
880
+ }
881
+ if (existingEnvelope.mcpToolDescription !== undefined) {
882
+ updateData.mcp_tool_description = existingEnvelope.mcpToolDescription;
883
+ }
669
884
  }
670
885
  else {
671
886
  updateData = {
@@ -686,16 +901,38 @@ function registerFlowNode(flowCmd) {
686
901
  const configValues = (0, helpers_1.resolveDataOption)(options, helpers_1.NODE_UPDATE_DATA_SCHEMA);
687
902
  if (configValues) {
688
903
  if (hasNodeSchema && configValues.properties) {
904
+ // PROXY 字段误写拦截:对照权威 schema 的 input_fields
905
+ const proxyMisuse = (0, helpers_1.findProxyFieldMisuse)(configValues.properties, nodeSchema?.input_fields ?? []);
906
+ if (proxyMisuse.length > 0) {
907
+ const msg = proxyMisuse.join('\n');
908
+ if (options.json) {
909
+ (0, output_1.printStructuredResult)({ ok: false, message: msg });
910
+ }
911
+ else {
912
+ (0, output_1.printError)(msg);
913
+ }
914
+ process.exitCode = exit_code_1.ExitCode.FAILURE;
915
+ return;
916
+ }
689
917
  // node_schema 模式:properties 浅合并,不经过 deepMerge
690
- const existingProps = updateData.properties ?? {};
918
+ const currentProps = updateData.properties ?? {};
691
919
  updateData.properties = {
692
- ...existingProps,
920
+ ...currentProps,
693
921
  ...configValues.properties,
694
922
  };
695
923
  }
696
924
  else {
697
925
  updateData = (0, format_1.deepMerge)(updateData, configValues);
698
926
  }
927
+ // 信封顶层宿主字段:patch 中的 proxy_id / mcp_tool_description 提升为信封字段
928
+ if (hasNodeSchema) {
929
+ if (configValues.proxy_id !== undefined) {
930
+ updateData.proxy_id = (0, nodeEnvelope_1.normalizeProxyId)(configValues.proxy_id);
931
+ }
932
+ if (configValues.mcp_tool_description !== undefined) {
933
+ updateData.mcp_tool_description = String(configValues.mcp_tool_description);
934
+ }
935
+ }
699
936
  }
700
937
  if (options.validateVars && configValues) {
701
938
  if (await (0, helpers_1.validateVariableRefs)(configValues, flowId, nodeId, !!options.json))
@@ -735,17 +972,27 @@ function registerFlowNode(flowCmd) {
735
972
  if (updateData.properties) {
736
973
  if (hasNodeSchema) {
737
974
  const inputFields = (nodeSchema?.input_fields ?? []);
975
+ // 信封对齐:properties 中不允许残留 PROXY 字段(提交前统一剥除)
976
+ updateData.properties = (0, nodeEnvelope_1.extractProxyField)(updateData.properties, inputFields).properties;
738
977
  if (inputFields.length > 0) {
739
978
  if ((0, helpers_1.checkPropertyValidation)((0, helpers_1.validateNodeSchemaProperties)(updateData.properties, inputFields), !!options.json))
740
979
  return;
741
980
  }
742
981
  }
743
982
  else {
744
- if ((0, helpers_1.checkPropertyValidation)((0, format_1.validateNodePropertyValues)(updateData.properties), !!options.json))
983
+ // 类型/格式仅校验 patch 显式触达的字段:节点既有值(含 schema 预置)
984
+ // 由平台负责,不因历史遗留值拦死 update
985
+ if ((0, helpers_1.checkPropertyValidation)((0, format_1.validateNodePropertyValues)(updateData.properties, {
986
+ userFields: (0, format_1.collectUserFieldNames)(configValues?.properties),
987
+ }), !!options.json))
745
988
  return;
746
989
  }
747
990
  }
748
991
  if (!hasNodeSchema) {
992
+ // rcs 对齐:is_null / is_not_null 条件 value 提交前归一为空字符串
993
+ if (String(nd.node_type) === 'SwitchNode') {
994
+ (0, switchBranch_1.normalizeSwitchBranchNullConditionValues)(updateData);
995
+ }
749
996
  if ((0, helpers_1.checkSpecialNodeValidation)((0, format_1.validateSpecialNodeType)(String(nd.node_type), updateData, nd.config_schema), !!options.json))
750
997
  return;
751
998
  }
@@ -781,6 +1028,11 @@ function registerFlowNode(flowCmd) {
781
1028
  result.actionId = options.actionId;
782
1029
  if (options.accountId)
783
1030
  result.accountId = options.accountId;
1031
+ if (updateData.proxy_id !== undefined)
1032
+ result.proxyId = updateData.proxy_id;
1033
+ if (updateData.mcp_tool_description !== undefined) {
1034
+ result.mcpToolDescription = updateData.mcp_tool_description;
1035
+ }
784
1036
  if (sampleOutput !== null)
785
1037
  result.sampleOutput = sampleOutput;
786
1038
  (0, output_1.printStructuredResult)(result);
@@ -807,6 +1059,12 @@ function registerFlowNode(flowCmd) {
807
1059
  pairs.push(['动作', options.actionId]);
808
1060
  if (options.accountId)
809
1061
  pairs.push(['账号 ID', options.accountId]);
1062
+ if (updateData.proxy_id !== undefined) {
1063
+ pairs.push([
1064
+ '代理 ID',
1065
+ updateData.proxy_id === '0' ? '(不使用)' : String(updateData.proxy_id),
1066
+ ]);
1067
+ }
810
1068
  if (options.errorHandlerFile)
811
1069
  pairs.push(['错误处理', options.errorHandlerFile]);
812
1070
  if (sampleOutput !== null)
@@ -35,7 +35,8 @@ function registerFlowOffline(flowCmd) {
35
35
  const flow = await (0, flow_1.getFlowDetail)(flowId);
36
36
  const currentStatus = flow.op_status;
37
37
  if (currentStatus === 'OFFLINE' || currentStatus === 'UNPUBLISHED') {
38
- const msg = `流程已处于 ${(0, helpers_1.formatOpStatus)(currentStatus)} 状态,无需重复下线`;
38
+ // message 需保持纯文本(--json 模式禁止 ANSI 颜色码),着色交给 printWarning/printError
39
+ const msg = `流程已处于 ${helpers_1.OP_STATUS_MAP[currentStatus] || currentStatus} 状态,无需重复下线`;
39
40
  if (options.json) {
40
41
  (0, output_1.printStructuredResult)({ ok: false, message: msg });
41
42
  }
@@ -45,7 +46,7 @@ function registerFlowOffline(flowCmd) {
45
46
  return;
46
47
  }
47
48
  if (currentStatus === 'ONLINE_ING') {
48
- const msg = `流程正在升级中(${(0, helpers_1.formatOpStatus)(currentStatus)}),请等待升级完成后再执行下线`;
49
+ const msg = `流程正在升级中(${helpers_1.OP_STATUS_MAP[currentStatus] || currentStatus}),请等待升级完成后再执行下线`;
49
50
  if (options.json) {
50
51
  (0, output_1.printStructuredResult)({ ok: false, message: msg });
51
52
  }