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.
@@ -1,18 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CLASSIC_NODE_DESCRIPTORS = exports.LEGACY_HANDLE_NAME_MAP = exports.SWITCH_NODE_PACKAGE_KEY = exports.SWITCH_DEFAULT_BRANCH_HANDLE = void 0;
3
+ exports.CLASSIC_PORT_TYPES = exports.LEGACY_HANDLE_NAME_MAP = void 0;
4
4
  exports.normalizeLegacyHandleName = normalizeLegacyHandleName;
5
- exports.isClassicNodeType = isClassicNodeType;
5
+ exports.resolveSwitchBranchOutputs = resolveSwitchBranchOutputs;
6
6
  exports.resolveClassicNodePorts = resolveClassicNodePorts;
7
- exports.readSwitchBranches = readSwitchBranches;
8
- exports.deriveSwitchOutputPorts = deriveSwitchOutputPorts;
9
- exports.resolveClassicNodeSchema = resolveClassicNodeSchema;
10
- exports.getAutoConnectRulesForNodeType = getAutoConnectRulesForNodeType;
11
- exports.applySwitchBranchesToSchema = applySwitchBranchesToSchema;
12
- exports.listClassicNodeTypes = listClassicNodeTypes;
13
- exports.SWITCH_DEFAULT_BRANCH_HANDLE = 'default-branch';
14
- /** NodePackage switch 的 action_key(dip.core.flow.switch) */
15
- exports.SWITCH_NODE_PACKAGE_KEY = 'dip.core.flow.switch';
7
+ exports.isClassicSelfConnectionAllowed = isClassicSelfConnectionAllowed;
8
+ const nodeEnvelope_1 = require("@/utils/nodeEnvelope");
9
+ const switchBranch_1 = require("@/utils/switchBranch");
10
+ /**
11
+ * 经典节点静态端口表(对齐 rcs nodeTypeRegistry.tsx CLASSIC_DESCRIPTORS)。
12
+ *
13
+ * 覆盖 legacy 节点(无 action_key/action_ver)的端口校验回退:
14
+ * - ConditionNode:出桩 true/false(max=1)
15
+ * - LoopNode / SelfLoopNode:出桩 done/loop(max=1),loop→input 自环允许
16
+ * - AgentNode / BrowserNode:底部 ai_model/ai_memory/ai_tool slave 入桩
17
+ * - MCP 系:MCPServer/MCP_TRIGGER_NODE(builtin_mcp 出桩)、BuiltinMCP、
18
+ * MCP_TOOL/MCP_TOOLS_NODE(builtin_mcp 入桩 + main 出桩)
19
+ * - SwitchNode:动态分支端口 = branches[].id + default-branch
20
+ */
16
21
  /**
17
22
  * 废弃 handle 名迁移表(对齐 RCS useDataLoading REPLACE_HANDLE_MAP):
18
23
  * 旧数据的 sourceHandle/targetHandle 可能仍是 chat_model/memory/tools,
@@ -29,385 +34,179 @@ function normalizeLegacyHandleName(handle) {
29
34
  return undefined;
30
35
  return exports.LEGACY_HANDLE_NAME_MAP[handle] ?? handle;
31
36
  }
32
- /** Loop / SelfLoop 的 loop → input 自环规则(NodePackage 节点也按 node_type 兜底注入) */
33
- const LOOP_AUTO_CONNECT = [{ source: 'loop', target: 'input' }];
34
- const EMPTY_PORTS = [];
35
- const ACTION_INPUTS = [
36
- { name: 'input', displayName: '输入', maxConnections: 1 },
37
- ];
38
- const ACTION_OUTPUTS = [
39
- { name: 'output', displayName: '输出', maxConnections: 1 },
40
- ];
41
- const TRIGGER_OUTPUTS = [
42
- { name: 'output', displayName: '输出', maxConnections: 1 },
43
- ];
44
- const CONDITION_OUTPUTS = [
45
- { name: 'true', displayName: 'true', maxConnections: 1 },
46
- { name: 'false', displayName: 'false', maxConnections: 1 },
47
- ];
48
- const LOOP_OUTPUTS = [
49
- { name: 'done', displayName: 'done', maxConnections: 1 },
50
- { name: 'loop', displayName: 'loop', maxConnections: 1 },
51
- ];
52
- const SWITCH_DEFAULT_OUTPUTS = [
53
- { name: exports.SWITCH_DEFAULT_BRANCH_HANDLE, displayName: 'default', maxConnections: 1 },
54
- ];
55
- /**
56
- * Agent 底部 slave 桩为 SOURCE(RCS 82b5976 方向翻转):连线从 Agent 出发,
57
- * 指向 AI 子节点顶部 input 桩。ai_tool 允许多协议(临时兼容,对齐 RCS)
58
- */
59
- const AGENT_INPUTS = [{ name: 'input', displayName: '输入' }];
60
- const AGENT_OUTPUTS = [
61
- { name: 'output', displayName: '输出', maxConnections: 1 },
62
- { name: 'ai_model', displayName: 'Chat Model', type: 'AI_LANGUAGE_MODEL', maxConnections: 1 },
63
- { name: 'ai_memory', displayName: 'Memory', type: 'AI_MEMORY', maxConnections: 1 },
37
+ /** rcs NodeProtocol(enums/node.ts)取值 */
38
+ exports.CLASSIC_PORT_TYPES = {
39
+ MAIN: 'main',
40
+ AI_LANGUAGE_MODEL: 'AI_LANGUAGE_MODEL',
41
+ AI_VECTOR_STORE: 'AI_VECTORSTORE',
42
+ AI_MEMORY: 'AI_MEMORY',
43
+ AI_TOOL: 'AI_TOOL',
44
+ BUILTIN_MCP: 'BUILTIN_MCP',
45
+ MCP_SERVER: 'MCP_SERVER',
46
+ };
47
+ const port = (p) => ({
48
+ name: p.name,
49
+ type: p.type ?? exports.CLASSIC_PORT_TYPES.MAIN,
50
+ maxConnections: p.max === undefined ? null : p.max,
51
+ });
52
+ const ports = (list) => list.map(port);
53
+ const MAIN_IN = { name: 'input', max: 1 };
54
+ const MAIN_OUT = { name: 'output', max: 1 };
55
+ const LOOP_SELF_CONNECTIONS = [{ source: 'loop', target: 'input' }];
56
+ /** Agent/Browser 底部 slave 入桩(协议对齐 rcs AGENT_INPUTS/BROWSER_INPUTS)。
57
+ * ai_tool 为多协议桩(RCS 6fc6de5 后):AI_TOOL 与 BUILTIN_MCP 链路均合法 */
58
+ const AI_SLAVE_INPUTS = [
59
+ MAIN_IN,
60
+ { name: 'ai_model', type: exports.CLASSIC_PORT_TYPES.AI_LANGUAGE_MODEL, max: 1 },
61
+ { name: 'ai_memory', type: exports.CLASSIC_PORT_TYPES.AI_MEMORY, max: 1 },
64
62
  {
65
63
  name: 'ai_tool',
66
- displayName: 'Tools',
67
- type: 'AI_TOOL,KNOWLEDGE,MCP_SERVER,BUILTIN_MCP',
68
- maxConnections: 10,
64
+ type: `${exports.CLASSIC_PORT_TYPES.AI_TOOL},${exports.CLASSIC_PORT_TYPES.BUILTIN_MCP}`,
65
+ max: 1,
69
66
  },
70
67
  ];
71
- /** BrowserNode(RCS 0f824ad 方向翻转):ai_model slave 桩为连线源,指向 AISubNode 顶部 input 桩 */
72
- const BROWSER_INPUTS = [{ name: 'input', displayName: '输入' }];
73
- const BROWSER_OUTPUTS = [
74
- { name: 'output', displayName: '输出' },
75
- { name: 'ai_model', displayName: 'Chat Model', type: 'AI_LANGUAGE_MODEL', maxConnections: 1 },
68
+ /** MCPServer / MCP_TRIGGER_NODE:顶部 MCP_SERVER 入桩,底部 BUILTIN_MCP 出桩(不限连接数) */
69
+ const MCP_SERVER_INPUTS = [{ name: 'input', type: exports.CLASSIC_PORT_TYPES.MCP_SERVER }];
70
+ const MCP_SERVER_OUTPUTS = [
71
+ { name: 'builtin_mcp', type: exports.CLASSIC_PORT_TYPES.BUILTIN_MCP },
76
72
  ];
77
- /** AI 子节点顶部入桩(RCS 82b5976 方向翻转后为 TARGET):端口名固定 input,协议随类型 */
78
- const aiSubInput = (protocol, maxConnections = 1) => ({
79
- name: 'input',
80
- displayName: '输入',
81
- type: protocol,
82
- maxConnections,
83
- });
84
- /**
85
- * MCP 网关系列(RCS 6fc6de5 后):builtin_mcp 顶部入桩(TARGET,BUILTIN_MCP 协议,max 1),
86
- * 连线源为 Agent ai_tool(多协议含 BUILTIN_MCP)等
87
- */
88
- const MCP_BUILTIN_MCP_INPUTS = [
89
- { name: 'builtin_mcp', displayName: '输入', type: 'BUILTIN_MCP', maxConnections: 1 },
90
- ];
91
- /** BuiltinMCP:builtin_mcp 入桩 max 1,output 主出桩 max 10(协议 main) */
73
+ /** BuiltinMCP:顶部 main 入桩,底部 BUILTIN_MCP 出桩(max=1) */
74
+ const BUILTIN_MCP_INPUTS = [MAIN_IN];
92
75
  const BUILTIN_MCP_OUTPUTS = [
93
- { name: 'output', displayName: '输出', maxConnections: 10 },
94
- ];
95
- /** MCPTool / MCP_TOOLS_NODE:BUILTIN_MCP 入桩,main 出桩 */
96
- const MCP_TOOL_INPUTS = [
97
- { name: 'input', displayName: '输入', type: 'BUILTIN_MCP' },
76
+ { name: 'builtin_mcp', type: exports.CLASSIC_PORT_TYPES.BUILTIN_MCP, max: 1 },
98
77
  ];
99
- const MCP_TOOL_OUTPUTS = [
100
- { name: 'output', displayName: '输出', maxConnections: 1 },
101
- ];
102
- /** MCP return-variable 等形态:无出桩 */
103
- const NO_OUTPUT_PROTOCOLS = {
104
- BUILTIN_MCP: { outputs: EMPTY_PORTS },
105
- MCP_SERVER: { outputs: EMPTY_PORTS },
106
- };
107
- /**
108
- * classic 静态端口表(唯一事实源,数据对齐 RCS CLASSIC_DESCRIPTORS)。
109
- */
110
- exports.CLASSIC_NODE_DESCRIPTORS = {
111
- StartEventNode: {
112
- nodeType: 'StartEventNode',
113
- label: '触发器节点',
114
- description: '流程触发起点,无输入端口,单输出口',
115
- inputs: EMPTY_PORTS,
116
- outputs: TRIGGER_OUTPUTS,
117
- },
118
- ActionNode: {
119
- nodeType: 'ActionNode',
120
- label: '动作节点',
121
- description: '常规动作节点;MCP return-variable 协议形态无输出端口',
122
- inputs: ACTION_INPUTS,
123
- outputs: ACTION_OUTPUTS,
124
- byProtocol: NO_OUTPUT_PROTOCOLS,
125
- },
126
- ConditionNode: {
127
- nodeType: 'ConditionNode',
128
- label: '条件节点',
129
- description: '条件分支,true/false 双出口',
130
- inputs: ACTION_INPUTS,
131
- outputs: CONDITION_OUTPUTS,
132
- },
133
- LoopNode: {
134
- nodeType: 'LoopNode',
135
- label: '循环节点',
136
- description: '循环控制,done 出口走完流程,loop 出口配合 input 形成自环',
137
- inputs: ACTION_INPUTS,
138
- outputs: LOOP_OUTPUTS,
139
- autoConnect: LOOP_AUTO_CONNECT,
140
- },
141
- SelfLoopNode: {
142
- nodeType: 'SelfLoopNode',
143
- label: '自环节点',
144
- description: '自循环控制,loop → input 自环边为必需结构',
145
- inputs: ACTION_INPUTS,
146
- outputs: LOOP_OUTPUTS,
147
- autoConnect: LOOP_AUTO_CONNECT,
148
- },
149
- SwitchNode: {
150
- nodeType: 'SwitchNode',
151
- label: '多分支节点',
152
- description: '多路由分支;输出端口来自 properties.branches[].id(实例派生),default-branch 固定兜底',
153
- inputs: ACTION_INPUTS,
154
- outputs: SWITCH_DEFAULT_OUTPUTS,
155
- },
156
- AgentNode: {
157
- nodeType: 'AgentNode',
158
- label: 'Agent 节点',
159
- description: 'AI Agent;底部 slave 桩(ai_model/ai_memory/ai_tool)为连线源,指向 AI 子节点顶部 input 桩;ai_tool 支持多协议(AI_TOOL,KNOWLEDGE,MCP_SERVER,BUILTIN_MCP)',
160
- inputs: AGENT_INPUTS,
161
- outputs: AGENT_OUTPUTS,
162
- },
163
- BrowserNode: {
164
- nodeType: 'BrowserNode',
165
- label: '浏览器节点',
166
- description: 'AI 浏览器操作;ai_model slave 桩为连线源,指向 AISubNode 顶部 input 桩(协议 AI_LANGUAGE_MODEL)',
167
- inputs: BROWSER_INPUTS,
168
- outputs: BROWSER_OUTPUTS,
169
- },
170
- AISubNode: {
171
- nodeType: 'AISubNode',
172
- label: 'AI 配置子节点',
173
- description: 'AI 配置子节点(模型/记忆/工具);顶部 input 桩接收 Agent slave 桩连线,协议随类型(AI_TOOL 形态 max 10)',
174
- inputs: [aiSubInput('AI_LANGUAGE_MODEL', 1)],
175
- outputs: EMPTY_PORTS,
176
- byProtocol: {
177
- AI_MEMORY: { inputs: [aiSubInput('AI_MEMORY')] },
178
- AI_TOOL: { inputs: [aiSubInput('AI_TOOL', 10)] },
179
- },
180
- },
181
- VectorStoreNode: {
182
- nodeType: 'VectorStoreNode',
183
- label: '向量存储节点',
184
- description: '向量存储子节点;protocol=AI_VECTORSTORE 时顶部 input 桩接收 Agent slave 桩连线',
185
- inputs: EMPTY_PORTS,
186
- outputs: EMPTY_PORTS,
187
- byProtocol: {
188
- // 注意拼写:对齐 RCS NodeProtocol.AI_VECTOR_STORE = 'AI_VECTORSTORE'
189
- AI_VECTORSTORE: { inputs: [aiSubInput('AI_VECTORSTORE')] },
190
- },
191
- },
192
- MCPServerNode: {
193
- nodeType: 'MCPServerNode',
194
- label: 'MCP 服务节点',
195
- description: '旧 MCP 网关服务;builtin_mcp 入桩(BUILTIN_MCP 协议)max 1,无出桩',
196
- inputs: MCP_BUILTIN_MCP_INPUTS,
197
- outputs: EMPTY_PORTS,
198
- },
199
- MCP_TRIGGER_NODE: {
200
- nodeType: 'MCP_TRIGGER_NODE',
201
- label: 'MCP 网关触发节点',
202
- description: '旧 MCP 网关触发形态;builtin_mcp 入桩(BUILTIN_MCP 协议)max 1,无出桩',
203
- inputs: MCP_BUILTIN_MCP_INPUTS,
204
- outputs: EMPTY_PORTS,
205
- },
206
- BuiltinMCPNode: {
207
- nodeType: 'BuiltinMCPNode',
208
- label: '内置 MCP 节点',
209
- description: '内置 MCP 工具节点;builtin_mcp 入桩(BUILTIN_MCP 协议)max 1,output 主出桩 max 10',
210
- inputs: MCP_BUILTIN_MCP_INPUTS,
211
- outputs: BUILTIN_MCP_OUTPUTS,
212
- },
213
- MCPToolNode: {
214
- nodeType: 'MCPToolNode',
215
- label: 'MCP 工具节点',
216
- description: 'MCP 工具调用;BUILTIN_MCP 入桩,MCP return-variable 形态无出桩',
217
- inputs: MCP_TOOL_INPUTS,
218
- outputs: MCP_TOOL_OUTPUTS,
219
- byProtocol: NO_OUTPUT_PROTOCOLS,
220
- },
221
- MCP_TOOLS_NODE: {
222
- nodeType: 'MCP_TOOLS_NODE',
223
- label: 'MCP 网关工具列表节点',
224
- description: 'MCP 网关工具列表形态,端口与 MCPToolNode 一致',
225
- inputs: MCP_TOOL_INPUTS,
226
- outputs: MCP_TOOL_OUTPUTS,
227
- byProtocol: NO_OUTPUT_PROTOCOLS,
228
- },
229
- StickyNoteNode: {
230
- nodeType: 'StickyNoteNode',
231
- label: '便签节点',
232
- description: '画布便签,无端口',
233
- inputs: EMPTY_PORTS,
234
- outputs: EMPTY_PORTS,
235
- },
236
- };
237
- function toNodeSchemaPort(spec) {
238
- return {
239
- name: spec.name,
240
- display_name: spec.displayName,
241
- type: spec.type ?? 'main',
242
- ...(spec.maxConnections !== undefined && spec.maxConnections !== null
243
- ? { max_connections: spec.maxConnections }
244
- : {}),
245
- };
246
- }
247
- /** node_type 是否命中内置表 */
248
- function isClassicNodeType(nodeType) {
249
- return (Boolean(nodeType) &&
250
- Object.prototype.hasOwnProperty.call(exports.CLASSIC_NODE_DESCRIPTORS, nodeType));
251
- }
252
- /**
253
- * 按 (node_type, protocol) 解析端口声明。
254
- * protocol 缺失或未命中 byProtocol 时回退 type 级默认;未知 node_type 返回 null。
255
- */
256
- function resolveClassicNodePorts(node) {
257
- const descriptor = node.node_type ? exports.CLASSIC_NODE_DESCRIPTORS[node.node_type] : undefined;
258
- if (!descriptor)
259
- return null;
260
- const protocol = node.protocol?.trim();
261
- if (protocol) {
262
- const override = descriptor.byProtocol?.[protocol];
263
- if (override) {
264
- return {
265
- inputs: override.inputs ?? descriptor.inputs,
266
- outputs: override.outputs ?? descriptor.outputs,
267
- };
268
- }
269
- }
270
- return { inputs: descriptor.inputs, outputs: descriptor.outputs };
78
+ /** MCP_TOOL / MCP_TOOLS_NODE:顶部 BUILTIN_MCP 入桩,底部 main 出桩 */
79
+ const MCP_TOOL_INPUTS = [{ name: 'input', type: exports.CLASSIC_PORT_TYPES.BUILTIN_MCP }];
80
+ const MCP_TOOL_OUTPUTS = [MAIN_OUT];
81
+ const NO_PORTS = { inputs: [], outputs: [], selfConnections: [] };
82
+ /** protocol 覆盖:MCP return-variable 等形态无出桩(rcs byProtocol 语义) */
83
+ const EMPTY_OUTPUTS_PROTOCOLS = new Set([
84
+ exports.CLASSIC_PORT_TYPES.BUILTIN_MCP,
85
+ exports.CLASSIC_PORT_TYPES.MCP_SERVER,
86
+ ]);
87
+ function basePorts(inputs, outputs, selfConnections = []) {
88
+ return { inputs: ports(inputs), outputs: ports(outputs), selfConnections };
271
89
  }
272
- /** 探测 + 规范化节点 branches(RCS switchNodePackage 语义:三容器探测 + id 修复) */
273
- function readSwitchBranches(data) {
274
- const raw = unwrapSwitchProperties(data);
275
- const branches = Array.isArray(raw?.branches) ? raw.branches : [];
276
- const normalized = [];
277
- const usedIds = new Set([exports.SWITCH_DEFAULT_BRANCH_HANDLE]);
278
- let migrated = 0;
279
- for (const item of branches) {
280
- if (!item || typeof item !== 'object')
281
- continue;
282
- const branch = item;
283
- let id = String(branch.id ?? '');
284
- const name = String(branch.name ?? '').trim() || `分支 ${normalized.length + 1}`;
285
- if (!id.startsWith('branch') || id === exports.SWITCH_DEFAULT_BRANCH_HANDLE || usedIds.has(id)) {
286
- do {
287
- migrated++;
288
- id = `branch-migrated-${migrated}`;
289
- } while (usedIds.has(id));
290
- }
291
- usedIds.add(id);
292
- normalized.push({ id, name });
90
+ /** 节点 data.protocol → 端口覆盖(rcs resolveNodePorts byProtocol 分支) */
91
+ function applyProtocolOverride(base, protocol) {
92
+ if (!protocol || !EMPTY_OUTPUTS_PROTOCOLS.has(protocol)) {
93
+ return base;
293
94
  }
294
- return normalized;
95
+ return { ...base, outputs: [] };
295
96
  }
296
- /** 按序探测 config_schema → properties → params,各解包一层 properties/params */
297
- function unwrapSwitchProperties(data) {
298
- const candidates = [data.config_schema, data.properties, data.params];
299
- for (const candidate of candidates) {
300
- if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate))
301
- continue;
302
- const unwrapped = unwrapPropertiesLayer(candidate);
303
- if (Array.isArray(unwrapped?.branches))
304
- return unwrapped;
305
- }
306
- return undefined;
97
+ function readProtocol(node) {
98
+ const raw = (node.properties ?? {});
99
+ const protocol = raw.protocol;
100
+ return typeof protocol === 'string' && protocol ? protocol : undefined;
307
101
  }
308
- function unwrapPropertiesLayer(value) {
309
- if (Array.isArray(value.branches))
310
- return value;
311
- const nested = value.properties;
312
- if (nested && typeof nested === 'object' && !Array.isArray(nested)) {
313
- const unwrapped = nested;
314
- if (Array.isArray(unwrapped.branches))
315
- return unwrapped;
316
- }
317
- const params = value.params;
318
- if (params && typeof params === 'object' && !Array.isArray(params)) {
319
- const unwrapped = params;
320
- if (Array.isArray(unwrapped.branches))
321
- return unwrapped;
102
+ /** 从 properties(含 NodePackage 信封形态)提取 branches[].id(rcs switchNodePackage 语义) */
103
+ function readSwitchBranchIds(node) {
104
+ const props = (node.properties ?? {});
105
+ if (!props || typeof props !== 'object')
106
+ return [];
107
+ let container = props;
108
+ if (!Array.isArray(container.branches)) {
109
+ const unwrapped = (0, nodeEnvelope_1.unwrapNodePackagePropertiesEnvelope)(props);
110
+ if (unwrapped !== props &&
111
+ unwrapped &&
112
+ typeof unwrapped === 'object' &&
113
+ Array.isArray(unwrapped.branches)) {
114
+ container = unwrapped;
115
+ }
322
116
  }
323
- return value;
117
+ if (!Array.isArray(container.branches))
118
+ return [];
119
+ return container.branches
120
+ .map((branch) => {
121
+ if (!branch || typeof branch !== 'object' || Array.isArray(branch))
122
+ return null;
123
+ const id = branch.id;
124
+ return typeof id === 'string' && id ? id : null;
125
+ })
126
+ .filter((id) => id !== null);
324
127
  }
325
128
  /**
326
- * branches → 输出端口。classic SwitchNode 用静态模板(main/max1);
327
- * NodePackage switch 通过 options 传 schema 模板以继承约束。
328
- * default-branch 固定追加在末尾;branches 为空时仅返回 [defaultPort]。
129
+ * SwitchNode 动态分支端口派生:端口 = branches[].id + default-branch
130
+ * (对齐 rcs resolveSwitchNodePackagePortSchema,分支端口 max=1)
329
131
  */
330
- function deriveSwitchOutputPorts(branches, options) {
331
- const template = options?.template ?? {
332
- name: 'branch-1',
333
- display_name: '分支',
334
- type: 'main',
335
- max_connections: 1,
336
- };
337
- const defaultPort = options?.defaultPort ?? {
338
- name: exports.SWITCH_DEFAULT_BRANCH_HANDLE,
339
- display_name: 'default',
340
- type: 'main',
341
- max_connections: 1,
342
- };
132
+ function resolveSwitchBranchOutputs(branchIds) {
343
133
  return [
344
- ...branches.map((branch) => ({
345
- ...template,
346
- name: branch.id,
347
- display_name: branch.name,
348
- })),
349
- defaultPort,
134
+ ...branchIds.map((id) => port({ name: id, max: 1 })),
135
+ port({ name: switchBranch_1.DEFAULT_BRANCH_HANDLE, max: 1 }),
350
136
  ];
351
137
  }
352
138
  /**
353
- * 完整解析 classic 节点的内置 schema(含 SwitchNode 实例分支派生)。
354
- * 未知 node_type 返回 null。
139
+ * 按 (node_type, protocol, branches) 解析经典节点端口表。
140
+ *
141
+ * node_type 为 rcs NodeType 枚举值(如 'ConditionNode'、'LoopNode')。
142
+ * 未知类型返回 null(调用方回退行为不回退)。
355
143
  */
356
- function resolveClassicNodeSchema(node) {
357
- const descriptor = node.node_type ? exports.CLASSIC_NODE_DESCRIPTORS[node.node_type] : undefined;
358
- if (!descriptor)
359
- return null;
360
- const ports = resolveClassicNodePorts(node) ?? {
361
- inputs: descriptor.inputs,
362
- outputs: descriptor.outputs,
363
- };
364
- // Switch 分支端口由 deriveSwitchOutputPorts 产出(已是 NodeSchemaPort),不能再过 toNodeSchemaPort
365
- const outputs = node.node_type === 'SwitchNode'
366
- ? deriveSwitchOutputPorts(readSwitchBranches(node))
367
- : (ports.outputs ?? []).map(toNodeSchemaPort);
368
- return {
369
- schemaKey: node.node_type,
370
- inputs: (ports.inputs ?? []).map(toNodeSchemaPort),
371
- outputs,
372
- autoConnect: descriptor.autoConnect ?? [],
373
- };
374
- }
375
- /** Loop/SelfLoop 的 loop→input 兜底(NodePackage 节点也按 node_type 注入,对齐 RCS nodeAutoConnect) */
376
- function getAutoConnectRulesForNodeType(nodeType) {
377
- if (!nodeType)
378
- return [];
379
- const descriptor = exports.CLASSIC_NODE_DESCRIPTORS[nodeType];
380
- return descriptor?.autoConnect ?? [];
144
+ function resolveClassicNodePorts(node) {
145
+ const nodeType = node.node_type || 'ActionNode';
146
+ const protocol = readProtocol(node);
147
+ let base;
148
+ switch (nodeType) {
149
+ case 'ActionNode':
150
+ case 'dynamic':
151
+ case 'custom':
152
+ base = basePorts([MAIN_IN], [MAIN_OUT]);
153
+ break;
154
+ case 'StartEventNode':
155
+ base = basePorts([], [MAIN_OUT]);
156
+ break;
157
+ case 'ConditionNode':
158
+ base = basePorts([MAIN_IN], [
159
+ { name: 'true', max: 1 },
160
+ { name: 'false', max: 1 },
161
+ ]);
162
+ break;
163
+ case 'LoopNode':
164
+ case 'SelfLoopNode':
165
+ base = basePorts([MAIN_IN], [
166
+ { name: 'done', max: 1 },
167
+ { name: 'loop', max: 1 },
168
+ ], LOOP_SELF_CONNECTIONS);
169
+ break;
170
+ case 'SwitchNode': {
171
+ const branchIds = readSwitchBranchIds(node);
172
+ base = basePorts([MAIN_IN], resolveSwitchBranchOutputs(branchIds));
173
+ break;
174
+ }
175
+ case 'AgentNode':
176
+ case 'BrowserNode':
177
+ base = basePorts(AI_SLAVE_INPUTS, [MAIN_OUT]);
178
+ break;
179
+ case 'AISubNode':
180
+ case 'VectorStoreNode':
181
+ // AI 子节点只有出桩;协议由 data.protocol 决定(默认 AI_LANGUAGE_MODEL)
182
+ base = basePorts([], [{ name: 'ai_model', type: protocol ?? exports.CLASSIC_PORT_TYPES.AI_LANGUAGE_MODEL, max: 1 }]);
183
+ break;
184
+ case 'StickyNoteNode':
185
+ return NO_PORTS;
186
+ case 'MCPServerNode':
187
+ case 'MCP_TRIGGER_NODE':
188
+ base = basePorts(MCP_SERVER_INPUTS, MCP_SERVER_OUTPUTS);
189
+ break;
190
+ case 'BuiltinMCPNode':
191
+ base = basePorts(BUILTIN_MCP_INPUTS, BUILTIN_MCP_OUTPUTS);
192
+ break;
193
+ case 'MCPToolNode':
194
+ case 'MCP_TOOLS_NODE':
195
+ base = basePorts(MCP_TOOL_INPUTS, MCP_TOOL_OUTPUTS);
196
+ break;
197
+ default:
198
+ return null;
199
+ }
200
+ return applyProtocolOverride(base, protocol);
381
201
  }
382
202
  /**
383
- * NodePackage switch schema 应用实例分支(schema.key === 'dip.core.flow.switch' 时,
384
- * 模板继承 schema 第一个非 default 输出的约束);其余 schema 原样返回。
203
+ * 判断自环边是否被节点 auto_connect 规则声明
204
+ * (对齐 rcs connectionCheck.ts / nodeAutoConnect.ts isSelfConnectionAllowed 语义)。
385
205
  */
386
- function applySwitchBranchesToSchema(schema, node) {
387
- if (schema.key !== exports.SWITCH_NODE_PACKAGE_KEY)
388
- return schema;
389
- const branches = readSwitchBranches(node);
390
- if (branches.length === 0)
391
- return schema;
392
- const schemaOutputs = schema.outputs ?? [];
393
- const template = schemaOutputs.find((port) => port.name !== exports.SWITCH_DEFAULT_BRANCH_HANDLE) ??
394
- { name: 'branch-1', display_name: '分支', type: 'main' };
395
- const defaultPort = schemaOutputs.find((port) => port.name === exports.SWITCH_DEFAULT_BRANCH_HANDLE) ??
396
- {
397
- name: exports.SWITCH_DEFAULT_BRANCH_HANDLE,
398
- display_name: 'Default',
399
- type: 'main',
400
- };
401
- return {
402
- ...schema,
403
- outputs: deriveSwitchOutputPorts(branches, { template, defaultPort }),
404
- };
405
- }
406
- /** skills 渲染用:全部内置类型(保持表定义顺序) */
407
- function listClassicNodeTypes() {
408
- return Object.values(exports.CLASSIC_NODE_DESCRIPTORS).map((d) => ({
409
- nodeType: d.nodeType,
410
- label: d.label,
411
- }));
206
+ function isClassicSelfConnectionAllowed(node, sourceHandle, targetHandle) {
207
+ if (!sourceHandle || !targetHandle)
208
+ return false;
209
+ const portsTable = resolveClassicNodePorts(node);
210
+ return Boolean(portsTable?.selfConnections.some((rule) => rule.source === sourceHandle && rule.target === targetHandle));
412
211
  }
413
212
  //# sourceMappingURL=classicNodePorts.js.map