foliko 2.0.5 → 2.0.7
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/.claude/settings.local.json +4 -1
- package/.cli_default_systemPrompt.md +291 -0
- package/CLAUDE.md +3 -0
- package/README.md +20 -3
- package/docs/architecture.md +34 -2
- package/docs/extensions.md +199 -0
- package/docs/migration.md +100 -0
- package/docs/public-api.md +280 -24
- package/docs/usage.md +122 -30
- package/package.json +1 -1
- package/plugins/core/audit/index.js +1 -1
- package/plugins/core/default/bootstrap.js +44 -19
- package/plugins/core/python-loader/index.js +43 -25
- package/plugins/core/scheduler/index.js +1 -0
- package/plugins/core/skill-manager/PROMPT.md +6 -0
- package/plugins/core/skill-manager/index.js +402 -115
- package/plugins/core/sub-agent/PROMPT.md +10 -0
- package/plugins/core/sub-agent/index.js +36 -3
- package/plugins/core/think/index.js +1 -0
- package/plugins/core/workflow/context.js +941 -0
- package/plugins/core/workflow/engine.js +66 -0
- package/plugins/core/workflow/examples/01-basic.js +42 -0
- package/plugins/core/workflow/examples/01-basic.json +30 -0
- package/plugins/core/workflow/examples/02-choice.js +75 -0
- package/plugins/core/workflow/examples/02-choice.json +59 -0
- package/plugins/core/workflow/examples/03-chain-style.js +114 -0
- package/plugins/core/workflow/examples/03-each.json +41 -0
- package/plugins/core/workflow/examples/04-parallel.js +52 -0
- package/plugins/core/workflow/examples/04-parallel.json +51 -0
- package/plugins/core/workflow/examples/05-chain-style.json +68 -0
- package/plugins/core/workflow/examples/05-each.js +65 -0
- package/plugins/core/workflow/examples/06-script.js +82 -0
- package/plugins/core/workflow/examples/07-module-export.js +43 -0
- package/plugins/core/workflow/examples/08-default-export.js +29 -0
- package/plugins/core/workflow/examples/09-next-with-args.js +50 -0
- package/plugins/core/workflow/examples/10-logger.js +34 -0
- package/plugins/core/workflow/examples/11-storage.js +68 -0
- package/plugins/core/workflow/examples/simple.js +77 -0
- package/plugins/core/workflow/examples/simple.json +75 -0
- package/plugins/core/workflow/index.js +204 -78
- package/plugins/core/workflow/js-runner.js +318 -0
- package/plugins/core/workflow/json-runner.js +323 -0
- package/plugins/core/workflow/stages/action.js +211 -0
- package/plugins/core/workflow/stages/choice.js +74 -0
- package/plugins/core/workflow/stages/delay.js +73 -0
- package/plugins/core/workflow/stages/each.js +123 -0
- package/plugins/core/workflow/stages/parallel.js +69 -0
- package/plugins/core/workflow/stages/try.js +142 -0
- package/plugins/executors/data-splitter/PROMPT.md +13 -0
- package/plugins/executors/data-splitter/index.js +8 -6
- package/plugins/executors/extension/extension-registry.js +145 -0
- package/plugins/executors/extension/index.js +405 -437
- package/plugins/executors/extension/prompt-builder.js +359 -0
- package/plugins/executors/extension/skill-helper.js +143 -0
- package/plugins/messaging/feishu/index.js +5 -3
- package/plugins/messaging/qq/index.js +6 -4
- package/plugins/messaging/telegram/index.js +6 -3
- package/plugins/messaging/weixin/index.js +5 -3
- package/plugins/tools/PROMPT.md +26 -0
- package/plugins/tools/index.js +6 -5
- package/sandbox/check-context.js +5 -0
- package/sandbox/test-context.js +27 -0
- package/sandbox/test-fixes.js +40 -0
- package/sandbox/test-hello-js.js +46 -0
- package/skills/foliko/AGENTS.md +196 -43
- package/skills/foliko/SKILL.md +157 -28
- package/skills/mcp/SKILL.md +77 -118
- package/skills/plugins/SKILL.md +89 -3
- package/skills/python/SKILL.md +57 -39
- package/skills/skill-guide/SKILL.md +42 -34
- package/skills/workflows/SKILL.md +753 -436
- package/src/agent/chat.js +56 -28
- package/src/agent/main.js +39 -14
- package/src/agent/prompt-registry.js +56 -16
- package/src/agent/prompts/PROMPT.md +3 -0
- package/src/agent/sub.js +1 -1
- package/src/cli/ui/chat-ui-old.js +5 -2
- package/src/cli/ui/chat-ui.js +9 -5
- package/src/common/constants.js +12 -0
- package/src/common/error-capture.js +91 -0
- package/src/common/json-safe.js +20 -0
- package/src/common/logger.js +2 -2
- package/src/context/compressor.js +6 -2
- package/src/executors/mcp-executor.js +105 -125
- package/src/framework/framework.js +78 -12
- package/src/index.js +4 -0
- package/src/plugin/base.js +908 -5
- package/src/plugin/manager.js +124 -9
- package/src/tool/schema.js +32 -9
- package/src/utils/sandbox.js +1 -1
- package/website/index.html +821 -0
- package/skills/workflows/workflow-troubleshooting/DEBUGGING.md +0 -197
- package/skills/workflows/workflow-troubleshooting/SKILL.md +0 -391
|
@@ -287,33 +287,93 @@ class StepExecutor {
|
|
|
287
287
|
}
|
|
288
288
|
/**
|
|
289
289
|
* 执行循环步骤
|
|
290
|
+
* 支持两种模式:
|
|
291
|
+
* 1. 固定次数循环:maxIterations + loopVariable
|
|
292
|
+
* 2. 数组迭代循环:over + as + index
|
|
290
293
|
*/
|
|
291
294
|
async executeLoop(step, context) {
|
|
292
295
|
this._log.debug(` Executing loop: ${step.name || step.id}`);
|
|
293
296
|
const results = [];
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
297
|
+
|
|
298
|
+
// 检查是否是数组迭代模式 (over + as)
|
|
299
|
+
if (step.over && step.as) {
|
|
300
|
+
// 数组迭代模式
|
|
301
|
+
const arrayPath = step.over;
|
|
302
|
+
const itemVar = step.as;
|
|
303
|
+
const indexVar = step.index || 'index';
|
|
304
|
+
|
|
305
|
+
// 解析数组路径 - 特殊处理 input.xxx 路径
|
|
306
|
+
let array;
|
|
307
|
+
if (arrayPath.startsWith('input.')) {
|
|
308
|
+
// 直接从 context.input 获取
|
|
309
|
+
const key = arrayPath.substring(6); // 去掉 'input.' 前缀
|
|
310
|
+
array = context.input ? context.input[key] : undefined;
|
|
311
|
+
} else {
|
|
312
|
+
array = this._resolveValue(`{{${arrayPath}}}`, context);
|
|
303
313
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
314
|
+
|
|
315
|
+
if (!Array.isArray(array)) {
|
|
316
|
+
this._log.warn(` Loop over: '${arrayPath}' is not an array, skipping. Found:`, typeof array);
|
|
317
|
+
return results;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const maxIterations = step.maxIterations || array.length;
|
|
321
|
+
for (let i = 0; i < Math.min(maxIterations, array.length); i++) {
|
|
322
|
+
// 设置循环变量
|
|
323
|
+
context.variables[indexVar] = i;
|
|
324
|
+
context.variables[itemVar] = array[i];
|
|
325
|
+
context.variables.loopIndex = i; // 兼容旧代码
|
|
326
|
+
|
|
327
|
+
this._log.debug(` Loop iteration ${i}: ${itemVar}=`, array[i]);
|
|
328
|
+
|
|
329
|
+
// 执行循环体
|
|
330
|
+
const iterationResults = [];
|
|
331
|
+
for (const stepConfig of step.steps || []) {
|
|
332
|
+
const result = await this.executeStep(stepConfig, context);
|
|
333
|
+
iterationResults.push(result);
|
|
313
334
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
335
|
+
results.push(iterationResults);
|
|
336
|
+
|
|
337
|
+
// 检查终止条件
|
|
338
|
+
if (step.until) {
|
|
339
|
+
let shouldStop = false;
|
|
340
|
+
if (typeof step.until === 'function') {
|
|
341
|
+
shouldStop = step.until(context);
|
|
342
|
+
} else if (typeof step.until === 'string') {
|
|
343
|
+
shouldStop = await evaluateInSandbox(step.until, context, { timeout: 5000 });
|
|
344
|
+
}
|
|
345
|
+
if (shouldStop) {
|
|
346
|
+
this._log.debug(` Loop terminated at iteration ${i}`);
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
} else {
|
|
352
|
+
// 固定次数循环模式(原有逻辑)
|
|
353
|
+
const maxIterations = step.maxIterations || 10;
|
|
354
|
+
const loopVariable = step.loopVariable || 'loopIndex';
|
|
355
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
356
|
+
context.variables[loopVariable] = i;
|
|
357
|
+
context.variables.loopIndex = i; // 兼容
|
|
358
|
+
this._log.debug(` Loop iteration ${i}`);
|
|
359
|
+
const iterationResults = [];
|
|
360
|
+
for (const stepConfig of step.steps || []) {
|
|
361
|
+
const result = await this.executeStep(stepConfig, context);
|
|
362
|
+
iterationResults.push(result);
|
|
363
|
+
}
|
|
364
|
+
results.push(iterationResults);
|
|
365
|
+
// 检查终止条件
|
|
366
|
+
if (step.until) {
|
|
367
|
+
let shouldStop = false;
|
|
368
|
+
if (typeof step.until === 'function') {
|
|
369
|
+
shouldStop = step.until(context);
|
|
370
|
+
} else if (typeof step.until === 'string') {
|
|
371
|
+
shouldStop = await evaluateInSandbox(step.until, context, { timeout: 5000 });
|
|
372
|
+
}
|
|
373
|
+
if (shouldStop) {
|
|
374
|
+
this._log.debug(` Loop terminated at iteration ${i}`);
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
317
377
|
}
|
|
318
378
|
}
|
|
319
379
|
}
|
|
@@ -773,7 +833,7 @@ class WorkflowEngine extends EventEmitter {
|
|
|
773
833
|
type = StepType.WORKFLOW;
|
|
774
834
|
} else if (config.branches) {
|
|
775
835
|
type = StepType.CONDITION;
|
|
776
|
-
} else if (config.steps && (config.loopVariable || config.maxIterations)) {
|
|
836
|
+
} else if (config.steps && (config.loopVariable || config.maxIterations || config.over)) {
|
|
777
837
|
type = StepType.LOOP;
|
|
778
838
|
} else if (config.steps) {
|
|
779
839
|
type = StepType.SEQUENTIAL;
|
|
@@ -847,7 +907,11 @@ class WorkflowPlugin extends Plugin {
|
|
|
847
907
|
description: '执行指定的工作流',
|
|
848
908
|
inputSchema: z.object({
|
|
849
909
|
workflow: z.string().describe('工作流定义(JSON 或 JavaScript 代码)'),
|
|
850
|
-
|
|
910
|
+
// Use record(z.any()) rather than object({}) so callers can pass an
|
|
911
|
+
// object with arbitrary fields; default z.object({}) strips unknown
|
|
912
|
+
// keys (and here the schema is empty, so input would always end up
|
|
913
|
+
// as `{}` regardless of what was sent).
|
|
914
|
+
input: z.record(z.any()).optional().describe('工作流输入参数'),
|
|
851
915
|
}),
|
|
852
916
|
execute: async (args, framework) => {
|
|
853
917
|
// 获取当前 sessionId
|
|
@@ -870,7 +934,16 @@ class WorkflowPlugin extends Plugin {
|
|
|
870
934
|
description: '重载所有工作流,当用户添加或修改工作流后调用此工具',
|
|
871
935
|
inputSchema: z.object({}),
|
|
872
936
|
execute: async () => {
|
|
873
|
-
|
|
937
|
+
// Forward to pluginManager.reload so the framework has a chance to
|
|
938
|
+
// refresh the plugin prototype (workflow_reload used to call
|
|
939
|
+
// this.reload() directly, but instance.reload() always runs against
|
|
940
|
+
// the prototype captured at construction time, ignoring code changes
|
|
941
|
+
// until the process restarts).
|
|
942
|
+
if (typeof framework.reloadPlugin === 'function') {
|
|
943
|
+
await framework.reloadPlugin('workflow');
|
|
944
|
+
} else {
|
|
945
|
+
this.reload(this._framework);
|
|
946
|
+
}
|
|
874
947
|
return {
|
|
875
948
|
success: true,
|
|
876
949
|
data: `Workflows reloaded. Total: ${this._workflows.size}`,
|
|
@@ -934,15 +1007,48 @@ class WorkflowPlugin extends Plugin {
|
|
|
934
1007
|
let workflowDef;
|
|
935
1008
|
if (file.endsWith('.js')) {
|
|
936
1009
|
workflowDef = runWorkflowFileSafely(content, { timeout: 10000 });
|
|
1010
|
+
if (workflowDef && typeof workflowDef === 'object' && typeof workflowDef.default === 'function') {
|
|
1011
|
+
// ES module-style default export: { default: function($){...} }
|
|
1012
|
+
workflowDef = workflowDef.default;
|
|
1013
|
+
}
|
|
937
1014
|
if (typeof workflowDef === 'function') {
|
|
938
|
-
|
|
1015
|
+
// Two JS workflow patterns are supported:
|
|
1016
|
+
// 1. FLOW(function($) { $.push(...); $.next('start'); })
|
|
1017
|
+
// -> the exported value is a *factory* that must be called with engine
|
|
1018
|
+
// 2. module.exports = function($) { $.push(...); $.next('start'); }
|
|
1019
|
+
// -> the exported value is the workflow function itself (not a factory)
|
|
1020
|
+
// -> it must NOT be called here; v2 JsRunner will invoke it with $ directly.
|
|
1021
|
+
// -> FLOW() can also export a factory directly if there's no surrounding
|
|
1022
|
+
// function body (raw expression form).
|
|
1023
|
+
// We detect the factory form by looking for the literal `FLOW(` token
|
|
1024
|
+
// in the file's source. When FLOW() is detected, we invoke it as factory.
|
|
1025
|
+
if (/\bFLOW\s*\(/.test(content)) {
|
|
1026
|
+
try {
|
|
1027
|
+
workflowDef = workflowDef(this._engine || {});
|
|
1028
|
+
if (workflowDef && typeof workflowDef === 'object' && typeof workflowDef.default === 'function') {
|
|
1029
|
+
workflowDef = workflowDef.default;
|
|
1030
|
+
}
|
|
1031
|
+
} catch (err) {
|
|
1032
|
+
log.error(` Failed to call FLOW() factory for ${file}:`, err.message);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
// else: leave workflowDef as-is (it's already the workflow function)
|
|
939
1036
|
}
|
|
940
|
-
workflowDef = workflowDef.default || workflowDef;
|
|
941
1037
|
} else {
|
|
942
1038
|
workflowDef = JSON.parse(content);
|
|
943
1039
|
}
|
|
944
|
-
|
|
1040
|
+
// Accept either:
|
|
1041
|
+
// - JSON-style workflow with stages/flow field
|
|
1042
|
+
// - JS workflow exported as a function (Total.js style: FLOW(function($){...})
|
|
1043
|
+
// or module.exports = function($){...})
|
|
1044
|
+
if (workflowDef && (workflowDef.stages || workflowDef.flow)) {
|
|
1045
|
+
this._workflows.set(workflowName, workflowDef);
|
|
1046
|
+
} else if (typeof workflowDef === 'function') {
|
|
1047
|
+
// Store the JS function directly; v2 FlowEngine detects function type
|
|
1048
|
+
// and dispatches to JsRunner.
|
|
945
1049
|
this._workflows.set(workflowName, workflowDef);
|
|
1050
|
+
} else {
|
|
1051
|
+
log.warn(`Skipping ${file}: not a recognized workflow definition (no stages/flow, and not a function)`);
|
|
946
1052
|
}
|
|
947
1053
|
} catch (err) {
|
|
948
1054
|
log.error(` Failed to load ${file}:`, err.message);
|
|
@@ -958,9 +1064,17 @@ class WorkflowPlugin extends Plugin {
|
|
|
958
1064
|
*/
|
|
959
1065
|
_registerWorkflowTools() {
|
|
960
1066
|
for (const [name, workflow] of this._workflows) {
|
|
961
|
-
if (this._workflowTools.has(name)) continue;
|
|
962
1067
|
const toolName = `workflow_${name}`;
|
|
963
1068
|
const description = workflow.description || `执行工作流: ${name}`;
|
|
1069
|
+
// 注意:framework.registerTool 不做去重,导致 reload 后会有两个同名
|
|
1070
|
+
// `workflow_<name>` 工具。getTool 返回第一个(旧的)。每次注册前先清掉。
|
|
1071
|
+
if (this._framework?.toolRegistry?.unregisterTool) {
|
|
1072
|
+
try {
|
|
1073
|
+
this._framework.toolRegistry.unregisterTool(toolName);
|
|
1074
|
+
} catch (e) {
|
|
1075
|
+
// noop
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
964
1078
|
this._framework.registerTool({
|
|
965
1079
|
name: toolName,
|
|
966
1080
|
description,
|
|
@@ -968,13 +1082,24 @@ class WorkflowPlugin extends Plugin {
|
|
|
968
1082
|
input: z.object({}).optional().describe('工作流输入参数'),
|
|
969
1083
|
}),
|
|
970
1084
|
execute: async (args, framework) => {
|
|
971
|
-
//
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1085
|
+
// 每次执行都从 this._workflows 重新查最新处理函数。
|
|
1086
|
+
// 这样即使 ChatHandler 缓存了上个版本的 execute,我们仍能闭包到本对象上。
|
|
1087
|
+
// 注意:ToolRegistry._tools.set 被调用后都是一个新的 execute 对象(闭包重新生成),
|
|
1088
|
+
// 旧 execute 会因为 ChatHandler._aiToolsCache 不清而一直残留。
|
|
1089
|
+
// 修复点:framework.registerTool现在会同步重设 ChatHandler 相关缓存。
|
|
1090
|
+
let liveWorkflow;
|
|
1091
|
+
try {
|
|
1092
|
+
liveWorkflow = this._workflows.get(name);
|
|
1093
|
+
if (!liveWorkflow) {
|
|
1094
|
+
return { success: false, error: `Workflow '${name}' not loaded` };
|
|
1095
|
+
}
|
|
1096
|
+
let sessionId = null;
|
|
1097
|
+
const ctx = framework?.getExecutionContext?.();
|
|
1098
|
+
if (ctx?.sessionId) sessionId = ctx.sessionId;
|
|
1099
|
+
return await this.executeWorkflow(liveWorkflow, args.input || {}, sessionId);
|
|
1100
|
+
} catch (e) {
|
|
1101
|
+
return { success: false, error: e.message, stack: e.stack };
|
|
976
1102
|
}
|
|
977
|
-
return await this.executeWorkflow(workflow, args.input || {}, sessionId);
|
|
978
1103
|
},
|
|
979
1104
|
});
|
|
980
1105
|
this._workflowTools.set(name, toolName);
|
|
@@ -1009,10 +1134,35 @@ class WorkflowPlugin extends Plugin {
|
|
|
1009
1134
|
* 重载工作流
|
|
1010
1135
|
*/
|
|
1011
1136
|
reload(framework) {
|
|
1012
|
-
//
|
|
1013
|
-
|
|
1137
|
+
// 清除所有 workflow 相关模块的 require 缓存(action.js / engine.js / json-runner.js / context.js)
|
|
1138
|
+
// 否则即使代码已修改,Node.js 仍会使用旧的模块引用
|
|
1139
|
+
const workflowDir = path.dirname(__filename);
|
|
1140
|
+
// framework 根目录(用于清除 src/utils 等跨插件共享模块)
|
|
1141
|
+
const frameworkRoot = path.resolve(workflowDir, '..', '..', '..');
|
|
1142
|
+
const modulesToClear = [
|
|
1143
|
+
__filename,
|
|
1144
|
+
path.join(workflowDir, 'engine.js'),
|
|
1145
|
+
path.join(workflowDir, 'json-runner.js'),
|
|
1146
|
+
path.join(workflowDir, 'js-runner.js'),
|
|
1147
|
+
path.join(workflowDir, 'context.js'),
|
|
1148
|
+
path.join(workflowDir, 'stages', 'action.js'),
|
|
1149
|
+
path.join(workflowDir, 'stages', 'choice.js'),
|
|
1150
|
+
path.join(workflowDir, 'stages', 'each.js'),
|
|
1151
|
+
path.join(workflowDir, 'stages', 'parallel.js'),
|
|
1152
|
+
path.join(frameworkRoot, 'src', 'utils', 'sandbox.js'),
|
|
1153
|
+
];
|
|
1154
|
+
for (const modPath of modulesToClear) {
|
|
1155
|
+
delete require.cache[modPath];
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// 销毁 v2 引擎实例(懒加载缓存),强制下次执行时重建
|
|
1159
|
+
this._flowEngineV2 = null;
|
|
1160
|
+
|
|
1014
1161
|
this._framework = framework;
|
|
1015
1162
|
const fresh = require(__filename);
|
|
1163
|
+
// 重现现有实例的 prototype,让后续方法调用走最新代码
|
|
1164
|
+
// (实例属性如 _workflows/_workflowTools/_engine 保持,prototype 方法切换为 fresh)
|
|
1165
|
+
Object.setPrototypeOf(this, fresh.WorkflowPlugin.prototype);
|
|
1016
1166
|
// 重新创建 engine(确保使用最新代码)
|
|
1017
1167
|
this._engine = new fresh.WorkflowEngine(framework);
|
|
1018
1168
|
// 注册内置步骤类型
|
|
@@ -1027,8 +1177,12 @@ class WorkflowPlugin extends Plugin {
|
|
|
1027
1177
|
this._engine.registerStepType(fresh.StepType.DELAY, fresh.DelayStep);
|
|
1028
1178
|
this._engine.registerStepType(fresh.StepType.WORKFLOW, fresh.NestedWorkflowStep);
|
|
1029
1179
|
// 清除已注册的工具
|
|
1180
|
+
// framework.registerTool/unregisterTool 会同步重置 ChatHandler._aiToolsCache。
|
|
1030
1181
|
for (const toolName of this._workflowTools.values()) {
|
|
1031
|
-
|
|
1182
|
+
try {
|
|
1183
|
+
if (this._framework?.unregisterTool) this._framework.unregisterTool(toolName);
|
|
1184
|
+
else this._framework?.toolRegistry?.unregister?.(toolName);
|
|
1185
|
+
} catch (_) {}
|
|
1032
1186
|
}
|
|
1033
1187
|
this._workflowTools.clear();
|
|
1034
1188
|
this._workflows.clear();
|
|
@@ -1044,8 +1198,15 @@ class WorkflowPlugin extends Plugin {
|
|
|
1044
1198
|
}
|
|
1045
1199
|
/**
|
|
1046
1200
|
* 执行工作流
|
|
1201
|
+
* 支持 v2 JSON 和 JS 格式
|
|
1047
1202
|
*/
|
|
1048
1203
|
async executeWorkflow(workflowDef, input = {}, sessionId = null) {
|
|
1204
|
+
// Lazy load v2 engine to avoid circular require
|
|
1205
|
+
if (!this._flowEngineV2) {
|
|
1206
|
+
const { FlowEngine } = require('./engine');
|
|
1207
|
+
this._flowEngineV2 = new FlowEngine(this._framework);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1049
1210
|
try {
|
|
1050
1211
|
let workflow;
|
|
1051
1212
|
if (typeof workflowDef === 'string') {
|
|
@@ -1054,55 +1215,20 @@ class WorkflowPlugin extends Plugin {
|
|
|
1054
1215
|
if (this._workflows.has(workflowName)) {
|
|
1055
1216
|
workflow = this._workflows.get(workflowName);
|
|
1056
1217
|
} else {
|
|
1057
|
-
// 尝试解析 JSON
|
|
1218
|
+
// 尝试解析 JSON
|
|
1058
1219
|
try {
|
|
1059
1220
|
workflow = JSON.parse(workflowDef);
|
|
1060
1221
|
} catch {
|
|
1061
|
-
//
|
|
1062
|
-
workflow =
|
|
1222
|
+
// JSON 解析失败,视为 JS 代码
|
|
1223
|
+
workflow = workflowDef;
|
|
1063
1224
|
}
|
|
1064
1225
|
}
|
|
1065
1226
|
} else {
|
|
1066
1227
|
workflow = workflowDef;
|
|
1067
1228
|
}
|
|
1068
|
-
|
|
1069
|
-
//
|
|
1070
|
-
|
|
1071
|
-
context.variables._sessionId = sessionId;
|
|
1072
|
-
}
|
|
1073
|
-
// 执行工作流步骤
|
|
1074
|
-
if (workflow.steps && Array.isArray(workflow.steps)) {
|
|
1075
|
-
const results = [];
|
|
1076
|
-
// 按 id 存储步骤输出,供 ${id.output} 引用
|
|
1077
|
-
context.variables._stepOutputs = {};
|
|
1078
|
-
for (const stepConfig of workflow.steps) {
|
|
1079
|
-
const step = this._engine.createStep(stepConfig);
|
|
1080
|
-
const result = await step.execute(context, this._engine);
|
|
1081
|
-
results.push(result);
|
|
1082
|
-
// 如果步骤有 id,存储其输出
|
|
1083
|
-
if (stepConfig.id) {
|
|
1084
|
-
context.variables._stepOutputs[stepConfig.id] = result;
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
// 只返回最后一步的结果(包含最终输出)和成功状态
|
|
1088
|
-
// 不返回所有中间结果,避免上下文超限
|
|
1089
|
-
const lastResult = results.length > 0 ? results[results.length - 1] : null;
|
|
1090
|
-
// 从 context.variables 中提取非下划线开头的用户变量
|
|
1091
|
-
// 下划线开头的是内部变量(如 _stepOutputs),不需要返回
|
|
1092
|
-
const userVariables = {};
|
|
1093
|
-
for (const [key, value] of Object.entries(context.variables)) {
|
|
1094
|
-
if (!key.startsWith('_')) {
|
|
1095
|
-
userVariables[key] = value;
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
return {
|
|
1099
|
-
success: true,
|
|
1100
|
-
stepCount: workflow.steps.length,
|
|
1101
|
-
result: lastResult,
|
|
1102
|
-
output: userVariables,
|
|
1103
|
-
};
|
|
1104
|
-
}
|
|
1105
|
-
return { success: true, output: {} };
|
|
1229
|
+
|
|
1230
|
+
// 委托给 v2 引擎执行
|
|
1231
|
+
return await this._flowEngineV2.execute(workflow, input, sessionId);
|
|
1106
1232
|
} catch (err) {
|
|
1107
1233
|
return { success: false, error: err.message };
|
|
1108
1234
|
}
|