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.
- package/README.md +146 -2
- package/dist/api/client.js +9 -0
- package/dist/api/flow.js +4 -1
- package/dist/api/node.js +78 -7
- package/dist/api/trigger.js +242 -0
- package/dist/commands/flow/account.js +439 -1
- package/dist/commands/flow/analyze.js +10 -2
- package/dist/commands/flow/export.js +29 -4
- package/dist/commands/flow/helpers.js +439 -53
- package/dist/commands/flow/node.js +290 -32
- package/dist/commands/flow/offline.js +3 -2
- package/dist/commands/flow/trigger.js +190 -0
- package/dist/commands/flow/validate.js +57 -0
- package/dist/commands/flow/variables.js +1 -0
- package/dist/commands/flow.js +2 -0
- package/dist/constants/node-type.js +23 -0
- package/dist/services/classicNodePorts.js +167 -368
- package/dist/services/fieldTypeRegistry.js +496 -0
- package/dist/services/flowAnalyzer.js +8 -1
- package/dist/services/flowValidator.js +145 -0
- package/dist/services/portValidator.js +76 -70
- package/dist/services/skillGenerator.js +236 -194
- package/dist/services/triggerConfig.js +252 -0
- package/dist/utils/conditionRules.js +128 -0
- package/dist/utils/format.js +268 -168
- package/dist/utils/nodeEnvelope.js +128 -0
- package/dist/utils/switchBranch.js +83 -0
- package/dist/utils/updateCheck.js +50 -6
- package/package.json +1 -1
|
@@ -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. 纯数字 →
|
|
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
|
-
|
|
98
|
+
const legacy = await (0, node_1.getActionDetail)(actionId);
|
|
99
|
+
if (legacy)
|
|
100
|
+
return legacy;
|
|
86
101
|
}
|
|
87
102
|
catch {
|
|
88
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
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 &&
|
|
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)(
|
|
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', '
|
|
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.
|
|
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
|
-
|
|
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)({
|
|
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
|
|
408
|
-
|
|
409
|
-
|
|
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
|
-
|
|
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
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
918
|
+
const currentProps = updateData.properties ?? {};
|
|
691
919
|
updateData.properties = {
|
|
692
|
-
...
|
|
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
|
-
|
|
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
|
-
|
|
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 = `流程正在升级中(${
|
|
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
|
}
|