foliko 1.0.73 → 1.0.75
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/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
- package/.agent/ARCHITECTURE.md +288 -0
- package/.agent/agents/ambient-agent.md +57 -0
- package/.agent/agents/debugger.md +55 -0
- package/.agent/agents/email-assistant.md +49 -0
- package/.agent/agents/file-manager.md +42 -0
- package/.agent/agents/python-developer.md +60 -0
- package/.agent/agents/scheduler.md +59 -0
- package/.agent/agents/web-developer.md +45 -0
- package/.agent/data/default.json +29 -0
- package/.agent/data/plugins-state.json +255 -0
- package/.agent/mcp_config.json +4 -0
- package/.agent/mcp_config_updated.json +12 -0
- package/.agent/plugins.json +5 -0
- package/.agent/rules/GEMINI.md +273 -0
- package/.agent/rules/allow-rule.md +77 -0
- package/.agent/rules/log-rule.md +83 -0
- package/.agent/rules/security-rule.md +93 -0
- package/.agent/scripts/auto_preview.py +148 -0
- package/.agent/scripts/checklist.py +217 -0
- package/.agent/scripts/session_manager.py +120 -0
- package/.agent/scripts/verify_all.py +327 -0
- package/.agent/skills/api-patterns/SKILL.md +81 -0
- package/.agent/skills/api-patterns/api-style.md +42 -0
- package/.agent/skills/api-patterns/auth.md +24 -0
- package/.agent/skills/api-patterns/documentation.md +26 -0
- package/.agent/skills/api-patterns/graphql.md +41 -0
- package/.agent/skills/api-patterns/rate-limiting.md +31 -0
- package/.agent/skills/api-patterns/response.md +37 -0
- package/.agent/skills/api-patterns/rest.md +40 -0
- package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
- package/.agent/skills/api-patterns/security-testing.md +122 -0
- package/.agent/skills/api-patterns/trpc.md +41 -0
- package/.agent/skills/api-patterns/versioning.md +22 -0
- package/.agent/skills/app-builder/SKILL.md +75 -0
- package/.agent/skills/app-builder/agent-coordination.md +71 -0
- package/.agent/skills/app-builder/feature-building.md +53 -0
- package/.agent/skills/app-builder/project-detection.md +34 -0
- package/.agent/skills/app-builder/scaffolding.md +118 -0
- package/.agent/skills/app-builder/tech-stack.md +40 -0
- package/.agent/skills/app-builder/templates/SKILL.md +39 -0
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
- package/.agent/skills/architecture/SKILL.md +55 -0
- package/.agent/skills/architecture/context-discovery.md +43 -0
- package/.agent/skills/architecture/examples.md +94 -0
- package/.agent/skills/architecture/pattern-selection.md +68 -0
- package/.agent/skills/architecture/patterns-reference.md +50 -0
- package/.agent/skills/architecture/trade-off-analysis.md +77 -0
- package/.agent/skills/clean-code/SKILL.md +201 -0
- package/.agent/skills/doc.md +177 -0
- package/.agent/skills/frontend-design/SKILL.md +418 -0
- package/.agent/skills/frontend-design/animation-guide.md +331 -0
- package/.agent/skills/frontend-design/color-system.md +311 -0
- package/.agent/skills/frontend-design/decision-trees.md +418 -0
- package/.agent/skills/frontend-design/motion-graphics.md +306 -0
- package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
- package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/.agent/skills/frontend-design/typography-system.md +345 -0
- package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
- package/.agent/skills/frontend-design/visual-effects.md +383 -0
- package/.agent/skills/i18n-localization/SKILL.md +154 -0
- package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
- package/.agent/skills/mcp-builder/SKILL.md +176 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
- package/.agent/workflows/brainstorm.md +113 -0
- package/.agent/workflows/create.md +59 -0
- package/.agent/workflows/debug.md +103 -0
- package/.agent/workflows/deploy.md +176 -0
- package/.agent/workflows/enhance.md +63 -0
- package/.agent/workflows/orchestrate.md +237 -0
- package/.agent/workflows/plan.md +89 -0
- package/.agent/workflows/preview.md +81 -0
- package/.agent/workflows/simple-test.md +42 -0
- package/.agent/workflows/status.md +86 -0
- package/.agent/workflows/structured-orchestrate.md +180 -0
- package/.agent/workflows/test.md +144 -0
- package/.agent/workflows/ui-ux-pro-max.md +296 -0
- package/.claude/settings.local.json +157 -149
- package/.editorconfig +56 -0
- package/.husky/pre-commit +4 -0
- package/.lintstagedrc +7 -0
- package/.prettierignore +29 -0
- package/.prettierrc +11 -0
- package/CLAUDE.md +2 -0
- package/README.md +64 -55
- package/SPEC.md +102 -61
- package/cli/bin/foliko.js +4 -4
- package/cli/src/commands/chat.js +53 -51
- package/cli/src/commands/list.js +40 -37
- package/cli/src/index.js +18 -18
- package/cli/src/ui/chat-ui.js +78 -76
- package/cli/src/utils/ansi.js +15 -15
- package/cli/src/utils/markdown.js +112 -116
- package/docker-compose.yml +1 -1
- package/docs/ai-sdk-optimization.md +655 -636
- package/docs/features.md +80 -80
- package/docs/quick-reference.md +49 -46
- package/docs/user-manual.md +411 -380
- package/examples/ambient-example.js +95 -97
- package/examples/basic.js +115 -110
- package/examples/bootstrap.js +52 -43
- package/examples/mcp-example.js +56 -53
- package/examples/skill-example.js +49 -49
- package/examples/test-chat.js +60 -58
- package/examples/test-mcp.js +49 -43
- package/examples/test-reload.js +38 -40
- package/examples/test-telegram.js +3 -3
- package/examples/test-tg-bot.js +7 -4
- package/examples/test-tg-simple.js +4 -3
- package/examples/test-tg.js +3 -3
- package/examples/test-think.js +13 -7
- package/examples/test-web-plugin.js +61 -56
- package/examples/test-weixin-feishu.js +40 -37
- package/examples/workflow.js +49 -49
- package/foliko-1.0.75.tgz +0 -0
- package/package.json +37 -3
- package/plugins/ai-plugin.js +7 -5
- package/plugins/ambient-agent/EventWatcher.js +113 -0
- package/plugins/ambient-agent/ExplorerLoop.js +640 -0
- package/plugins/ambient-agent/GoalManager.js +197 -0
- package/plugins/ambient-agent/Reflector.js +95 -0
- package/plugins/ambient-agent/StateStore.js +90 -0
- package/plugins/ambient-agent/constants.js +101 -0
- package/plugins/ambient-agent/index.js +579 -0
- package/plugins/default-plugins.js +62 -49
- package/plugins/email/constants.js +64 -0
- package/plugins/email/handlers.js +461 -0
- package/plugins/email/index.js +278 -0
- package/plugins/email/monitor.js +269 -0
- package/plugins/email/parser.js +138 -0
- package/plugins/email/reply.js +151 -0
- package/plugins/email/utils.js +124 -0
- package/plugins/feishu-plugin.js +23 -19
- package/plugins/file-system-plugin.js +469 -120
- package/plugins/install-plugin.js +6 -4
- package/plugins/python-executor-plugin.js +3 -1
- package/plugins/python-plugin-loader.js +10 -8
- package/plugins/rules-plugin.js +5 -3
- package/plugins/scheduler-plugin.js +18 -16
- package/plugins/session-plugin.js +3 -1
- package/plugins/storage-plugin.js +5 -3
- package/plugins/subagent-plugin.js +152 -92
- package/plugins/telegram-plugin.js +26 -19
- package/plugins/think-plugin.js +4 -2
- package/plugins/tools-plugin.js +3 -1
- package/plugins/web-plugin.js +15 -13
- package/plugins/weixin-plugin.js +43 -36
- package/reports/system-health-report-20260401.md +79 -0
- package/skills/ambient-agent/SKILL.md +49 -39
- package/skills/foliko-dev/AGENTS.md +64 -61
- package/skills/foliko-dev/SKILL.md +125 -119
- package/skills/mcp-usage/SKILL.md +19 -17
- package/skills/python-plugin-dev/SKILL.md +16 -15
- package/skills/skill-guide/SKILL.md +12 -12
- package/skills/subagent-guide/SKILL.md +237 -0
- package/skills/workflow-guide/SKILL.md +90 -45
- package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
- package/skills/workflow-troubleshooting/SKILL.md +156 -79
- package/src/capabilities/index.js +4 -4
- package/src/capabilities/skill-manager.js +211 -197
- package/src/capabilities/workflow-engine.js +461 -547
- package/src/core/agent-chat.js +426 -279
- package/src/core/agent.js +453 -248
- package/src/core/framework.js +183 -149
- package/src/core/index.js +8 -8
- package/src/core/plugin-base.js +52 -52
- package/src/core/plugin-manager.js +377 -281
- package/src/core/provider.js +35 -32
- package/src/core/sub-agent-config.js +264 -0
- package/src/core/system-prompt-builder.js +120 -0
- package/src/core/tool-registry.js +416 -33
- package/src/core/tool-router.js +149 -68
- package/src/executors/executor-base.js +58 -58
- package/src/executors/mcp-executor.js +269 -257
- package/src/index.js +5 -17
- package/src/utils/circuit-breaker.js +301 -0
- package/src/utils/error-boundary.js +363 -0
- package/src/utils/error.js +374 -0
- package/src/utils/event-emitter.js +20 -20
- package/src/utils/id.js +133 -0
- package/src/utils/index.js +217 -3
- package/src/utils/logger.js +181 -0
- package/src/utils/plugin-helpers.js +90 -0
- package/src/utils/retry.js +122 -0
- package/src/utils/sandbox.js +292 -0
- package/test/tool-registry-validation.test.js +218 -0
- package/test_report.md +70 -0
- package/website/docs/api.html +169 -107
- package/website/docs/configuration.html +296 -144
- package/website/docs/plugin-development.html +154 -85
- package/website/docs/project-structure.html +110 -109
- package/website/docs/skill-development.html +117 -61
- package/website/index.html +209 -205
- package/website/script.js +20 -17
- package/website/styles.css +1 -1
- package/plugins/ambient-agent-plugin.js +0 -1565
- package/plugins/email.js +0 -1142
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
* WorkflowEngine 工作流引擎
|
|
3
3
|
* 支持结构化工作流定义和执行
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
5
|
+
const { EventEmitter } = require('../utils/event-emitter');
|
|
6
|
+
const { Plugin } = require('../core/plugin-base');
|
|
7
|
+
const { logger } = require('../utils/logger');
|
|
8
|
+
const log = logger.child('Workflow');
|
|
9
|
+
const { z } = require('zod');
|
|
10
|
+
const { runScriptSafely, evaluateInSandbox, runWorkflowFileSafely, runWorkflowSafely } = require('../utils/sandbox');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const crypto = require('crypto');
|
|
13
|
+
const path = require('path');
|
|
12
14
|
/**
|
|
13
15
|
* 工作流步骤类型
|
|
14
16
|
*/
|
|
@@ -27,41 +29,38 @@ const StepType = {
|
|
|
27
29
|
OUTPUT: 'output',
|
|
28
30
|
MESSAGE: 'message',
|
|
29
31
|
THINK: 'think',
|
|
30
|
-
WORKFLOW: 'workflow'
|
|
31
|
-
}
|
|
32
|
-
|
|
32
|
+
WORKFLOW: 'workflow', // 嵌套工作流
|
|
33
|
+
};
|
|
33
34
|
/**
|
|
34
35
|
* StepExecutor - 统一的步骤执行器
|
|
35
36
|
* 同时被 WorkflowEngine 和 ExplorerLoop 使用
|
|
36
37
|
*/
|
|
37
38
|
class StepExecutor {
|
|
38
39
|
constructor(framework) {
|
|
39
|
-
this.framework = framework
|
|
40
|
+
this.framework = framework;
|
|
41
|
+
this._log = logger.child('StepExecutor');
|
|
40
42
|
}
|
|
41
|
-
|
|
42
43
|
/**
|
|
43
44
|
* 执行单个步骤
|
|
44
45
|
*/
|
|
45
46
|
async executeStep(step, context) {
|
|
46
|
-
const handler = this._getStepHandler(step.type)
|
|
47
|
+
const handler = this._getStepHandler(step.type);
|
|
47
48
|
if (!handler) {
|
|
48
|
-
throw new Error(`Unknown step type: ${step.type}`)
|
|
49
|
+
throw new Error(`Unknown step type: ${step.type}`);
|
|
49
50
|
}
|
|
50
|
-
return await handler.call(this, step, context)
|
|
51
|
+
return await handler.call(this, step, context);
|
|
51
52
|
}
|
|
52
|
-
|
|
53
53
|
/**
|
|
54
54
|
* 执行多个步骤(顺序)
|
|
55
55
|
*/
|
|
56
56
|
async executeSteps(steps, context) {
|
|
57
|
-
const results = []
|
|
57
|
+
const results = [];
|
|
58
58
|
for (const step of steps) {
|
|
59
|
-
const result = await this.executeStep(step, context)
|
|
60
|
-
results.push(result)
|
|
59
|
+
const result = await this.executeStep(step, context);
|
|
60
|
+
results.push(result);
|
|
61
61
|
}
|
|
62
|
-
return results
|
|
62
|
+
return results;
|
|
63
63
|
}
|
|
64
|
-
|
|
65
64
|
/**
|
|
66
65
|
* 获取步骤类型处理器
|
|
67
66
|
*/
|
|
@@ -78,378 +77,324 @@ class StepExecutor {
|
|
|
78
77
|
[StepType.SEQUENTIAL]: this.executeSequential,
|
|
79
78
|
[StepType.MESSAGE]: this.executeMessage,
|
|
80
79
|
[StepType.THINK]: this.executeThink,
|
|
81
|
-
[StepType.WORKFLOW]: this.executeWorkflowStep
|
|
82
|
-
}
|
|
83
|
-
return handlers[type]
|
|
80
|
+
[StepType.WORKFLOW]: this.executeWorkflowStep,
|
|
81
|
+
};
|
|
82
|
+
return handlers[type];
|
|
84
83
|
}
|
|
85
|
-
|
|
86
84
|
/**
|
|
87
85
|
* 执行 switch 步骤
|
|
88
86
|
*/
|
|
89
87
|
async executeSwitch(step, context) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
const branches = step.branches || []
|
|
94
|
-
|
|
88
|
+
this._log.debug(` Executing switch: ${step.name || step.id}`);
|
|
89
|
+
const value = this._resolveValue(step.value, context);
|
|
90
|
+
const branches = step.branches || [];
|
|
95
91
|
for (const branch of branches) {
|
|
96
|
-
const caseValue = this._resolveValue(branch.case, context)
|
|
92
|
+
const caseValue = this._resolveValue(branch.case, context);
|
|
97
93
|
if (value === caseValue) {
|
|
98
|
-
|
|
94
|
+
this._log.debug(` Switch matched case: ${caseValue}`);
|
|
99
95
|
if (branch.steps && branch.steps.length > 0) {
|
|
100
|
-
return await this.executeSteps(branch.steps, context)
|
|
96
|
+
return await this.executeSteps(branch.steps, context);
|
|
101
97
|
}
|
|
102
|
-
return { matched: caseValue }
|
|
98
|
+
return { matched: caseValue };
|
|
103
99
|
}
|
|
104
100
|
}
|
|
105
|
-
|
|
106
101
|
// 默认分支
|
|
107
102
|
if (step.default && step.default.steps) {
|
|
108
|
-
|
|
109
|
-
return await this.executeSteps(step.default.steps, context)
|
|
103
|
+
this._log.debug(` Executing switch default branch`);
|
|
104
|
+
return await this.executeSteps(step.default.steps, context);
|
|
110
105
|
}
|
|
111
|
-
|
|
112
|
-
return { matched: null, value }
|
|
106
|
+
return { matched: null, value };
|
|
113
107
|
}
|
|
114
|
-
|
|
115
108
|
/**
|
|
116
109
|
* 执行 try-catch 步骤
|
|
117
110
|
*/
|
|
118
111
|
async executeTry(step, context) {
|
|
119
|
-
|
|
120
|
-
|
|
112
|
+
this._log.debug(` Executing try-catch: ${step.name || step.id}`);
|
|
121
113
|
try {
|
|
122
114
|
if (step.try && step.try.steps) {
|
|
123
|
-
const result = await this.executeSteps(step.try.steps, context)
|
|
124
|
-
return { success: true, result }
|
|
115
|
+
const result = await this.executeSteps(step.try.steps, context);
|
|
116
|
+
return { success: true, result };
|
|
125
117
|
}
|
|
126
|
-
return { success: true }
|
|
118
|
+
return { success: true };
|
|
127
119
|
} catch (err) {
|
|
128
|
-
|
|
120
|
+
this._log.debug(` Try block failed, executing catch: ${err.message}`);
|
|
129
121
|
if (step.catch && step.catch.steps) {
|
|
130
|
-
const catchResult = await this.executeSteps(step.catch.steps, context)
|
|
131
|
-
return { success: false, error: err.message, caught: catchResult }
|
|
122
|
+
const catchResult = await this.executeSteps(step.catch.steps, context);
|
|
123
|
+
return { success: false, error: err.message, caught: catchResult };
|
|
132
124
|
}
|
|
133
|
-
return { success: false, error: err.message }
|
|
125
|
+
return { success: false, error: err.message };
|
|
134
126
|
}
|
|
135
127
|
}
|
|
136
|
-
|
|
137
128
|
/**
|
|
138
129
|
* 执行嵌套工作流步骤
|
|
139
130
|
*/
|
|
140
131
|
async executeWorkflowStep(step, context) {
|
|
141
|
-
|
|
142
|
-
|
|
132
|
+
this._log.debug(` Executing nested workflow: ${step.name || step.id}`);
|
|
143
133
|
if (!step.workflow) {
|
|
144
|
-
throw new Error('Workflow step requires a workflow definition')
|
|
134
|
+
throw new Error('Workflow step requires a workflow definition');
|
|
145
135
|
}
|
|
146
|
-
|
|
147
136
|
// 执行嵌套工作流
|
|
148
|
-
const result = await this.framework.pluginManager
|
|
149
|
-
|
|
150
|
-
step.input || {},
|
|
151
|
-
context.variables._sessionId
|
|
152
|
-
)
|
|
153
|
-
|
|
137
|
+
const result = await this.framework.pluginManager
|
|
138
|
+
.get('workflow')
|
|
139
|
+
.executeWorkflow(step.workflow, step.input || {}, context.variables._sessionId);
|
|
154
140
|
// 将嵌套工作流的输出合并到当前上下文
|
|
155
141
|
if (result.output) {
|
|
156
142
|
for (const [key, value] of Object.entries(result.output)) {
|
|
157
|
-
context.variables[key] = value
|
|
143
|
+
context.variables[key] = value;
|
|
158
144
|
}
|
|
159
145
|
}
|
|
160
|
-
|
|
161
|
-
return result
|
|
146
|
+
return result;
|
|
162
147
|
}
|
|
163
|
-
|
|
164
148
|
/**
|
|
165
149
|
* 执行并行步骤
|
|
166
150
|
*/
|
|
167
151
|
async executeParallel(step, context) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const steps = step.steps || []
|
|
152
|
+
this._log.debug(` Executing parallel: ${step.name || step.id}`);
|
|
153
|
+
const steps = step.steps || [];
|
|
171
154
|
if (steps.length === 0) {
|
|
172
|
-
return []
|
|
155
|
+
return [];
|
|
173
156
|
}
|
|
174
|
-
|
|
175
157
|
// 并行执行所有步骤
|
|
176
|
-
const promises = steps.map(stepConfig => {
|
|
177
|
-
return this.executeStep(stepConfig, context)
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return results
|
|
158
|
+
const promises = steps.map((stepConfig) => {
|
|
159
|
+
return this.executeStep(stepConfig, context);
|
|
160
|
+
});
|
|
161
|
+
const results = await Promise.all(promises);
|
|
162
|
+
return results;
|
|
182
163
|
}
|
|
183
|
-
|
|
184
164
|
/**
|
|
185
165
|
* 执行工具步骤
|
|
186
166
|
*/
|
|
187
167
|
async executeTool(step, context) {
|
|
188
168
|
if (!step.tool) {
|
|
189
|
-
throw new Error('Tool step requires a tool name')
|
|
169
|
+
throw new Error('Tool step requires a tool name');
|
|
190
170
|
}
|
|
191
|
-
|
|
192
|
-
console.log(`[StepExecutor] Executing tool: ${step.tool}`)
|
|
193
|
-
|
|
171
|
+
this._log.debug(` Executing tool: ${step.tool}`);
|
|
194
172
|
// 解析工具参数,支持变量引用
|
|
195
|
-
let resolvedArgs = this._resolveArgs(step.args || {}, context)
|
|
196
|
-
|
|
173
|
+
let resolvedArgs = this._resolveArgs(step.args || {}, context);
|
|
197
174
|
// 兼容旧格式:处理 step.input 字段(${stepId.output} 引用)
|
|
198
175
|
// 将 input 注入到 args.input_data,供 python-execute 使用
|
|
199
176
|
if (step.input) {
|
|
200
|
-
const resolvedInput = this._resolveArgs(step.input, context)
|
|
201
|
-
resolvedArgs.input_data = resolvedInput
|
|
177
|
+
const resolvedInput = this._resolveArgs(step.input, context);
|
|
178
|
+
resolvedArgs.input_data = resolvedInput;
|
|
202
179
|
}
|
|
203
|
-
|
|
204
180
|
// 获取当前 sessionId
|
|
205
|
-
let sessionId = context.variables?._sessionId
|
|
181
|
+
let sessionId = context.variables?._sessionId;
|
|
206
182
|
if (!sessionId && this.framework.getExecutionContext) {
|
|
207
|
-
const ctx = this.framework.getExecutionContext()
|
|
208
|
-
sessionId = ctx?.sessionId
|
|
183
|
+
const ctx = this.framework.getExecutionContext();
|
|
184
|
+
sessionId = ctx?.sessionId;
|
|
209
185
|
}
|
|
210
|
-
|
|
211
186
|
// 执行工具
|
|
212
|
-
let result
|
|
187
|
+
let result;
|
|
213
188
|
if (sessionId && this.framework.runWithContext) {
|
|
214
189
|
result = await this.framework.runWithContext(
|
|
215
190
|
{ sessionId },
|
|
216
191
|
async () => await this.framework.executeTool(step.tool, resolvedArgs)
|
|
217
|
-
)
|
|
192
|
+
);
|
|
218
193
|
} else {
|
|
219
|
-
result = await this.framework.executeTool(step.tool, resolvedArgs)
|
|
194
|
+
result = await this.framework.executeTool(step.tool, resolvedArgs);
|
|
220
195
|
}
|
|
221
|
-
|
|
222
196
|
// 保存结果到变量
|
|
223
197
|
// 支持 output 作为 outputVariable 的别名
|
|
224
|
-
const outputVar = step.outputVariable || step.output
|
|
198
|
+
const outputVar = step.outputVariable || step.output;
|
|
225
199
|
if (outputVar) {
|
|
226
|
-
context.variables[outputVar] = result
|
|
200
|
+
context.variables[outputVar] = result;
|
|
227
201
|
}
|
|
228
|
-
context.lastResult = result
|
|
229
|
-
|
|
202
|
+
context.lastResult = result;
|
|
230
203
|
if (result && result.error) {
|
|
231
|
-
|
|
204
|
+
this._log.warn(` Tool ${step.tool} returned error: ${result.error}`);
|
|
232
205
|
}
|
|
233
|
-
|
|
234
|
-
return result
|
|
206
|
+
return result;
|
|
235
207
|
}
|
|
236
|
-
|
|
237
208
|
/**
|
|
238
209
|
* 执行脚本步骤
|
|
239
210
|
*/
|
|
240
211
|
async executeScript(step, context) {
|
|
241
|
-
|
|
242
|
-
|
|
212
|
+
this._log.debug(` Executing script step: ${step.name || step.id}`);
|
|
213
|
+
// 安全警告:使用 new Function() 执行用户脚本存在风险
|
|
214
|
+
if (typeof step.script === 'string') {
|
|
215
|
+
this._log.warn(
|
|
216
|
+
` [SECURITY WARNING] Executing user script via new Function() - ensure script is from trusted source`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
243
219
|
try {
|
|
244
220
|
// 兼容旧格式:处理 step.input 字段(${stepId.output} 引用)
|
|
245
|
-
let resolvedInput = context.input || {}
|
|
221
|
+
let resolvedInput = context.input || {};
|
|
246
222
|
if (step.input && typeof step.input === 'object') {
|
|
247
|
-
resolvedInput = this._resolveArgs(step.input, context)
|
|
223
|
+
resolvedInput = this._resolveArgs(step.input, context);
|
|
248
224
|
}
|
|
249
|
-
|
|
250
225
|
const scriptContext = {
|
|
251
226
|
input: resolvedInput,
|
|
252
|
-
input_data: resolvedInput,
|
|
227
|
+
input_data: resolvedInput, // 兼容 input_data 变量名
|
|
253
228
|
variables: context.variables,
|
|
254
229
|
previousResult: context.lastResult,
|
|
255
230
|
console: {
|
|
256
|
-
log: (...args) =>
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
231
|
+
log: (...args) => {
|
|
232
|
+
const s = logger.child('Script');
|
|
233
|
+
s.debug(`[\${step.id}]`, ...args);
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
let result;
|
|
261
238
|
if (typeof step.script === 'function') {
|
|
262
|
-
result = await step.script(scriptContext)
|
|
239
|
+
result = await step.script(scriptContext);
|
|
263
240
|
} else if (typeof step.script === 'string') {
|
|
264
|
-
|
|
265
|
-
result = await
|
|
241
|
+
// 使用沙箱执行用户脚本,防止恶意代码执行
|
|
242
|
+
result = await runScriptSafely(step.script, scriptContext, { timeout: 10000 });
|
|
266
243
|
}
|
|
267
|
-
|
|
268
244
|
// 支持 output 作为 outputVariable 的别名
|
|
269
|
-
const outputVar = step.outputVariable || step.output
|
|
245
|
+
const outputVar = step.outputVariable || step.output;
|
|
270
246
|
if (outputVar) {
|
|
271
|
-
context.variables[outputVar] = result
|
|
247
|
+
context.variables[outputVar] = result;
|
|
272
248
|
}
|
|
273
|
-
context.lastResult = result
|
|
274
|
-
|
|
275
|
-
return result
|
|
249
|
+
context.lastResult = result;
|
|
250
|
+
return result;
|
|
276
251
|
} catch (err) {
|
|
277
|
-
throw new Error(`Script execution failed: ${err.message}`)
|
|
252
|
+
throw new Error(`Script execution failed: ${err.message}`);
|
|
278
253
|
}
|
|
279
254
|
}
|
|
280
|
-
|
|
281
255
|
/**
|
|
282
256
|
* 执行条件步骤
|
|
283
257
|
*/
|
|
284
258
|
async executeCondition(step, context) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
for (const branch of (step.branches || [])) {
|
|
259
|
+
this._log.debug(` Evaluating condition: ${step.name || step.id}`);
|
|
260
|
+
for (const branch of step.branches || []) {
|
|
288
261
|
try {
|
|
289
|
-
let conditionValue
|
|
262
|
+
let conditionValue;
|
|
290
263
|
if (typeof branch.condition === 'function') {
|
|
291
|
-
conditionValue = branch.condition(context)
|
|
264
|
+
conditionValue = branch.condition(context);
|
|
292
265
|
} else if (typeof branch.condition === 'string') {
|
|
293
|
-
|
|
294
|
-
conditionValue =
|
|
266
|
+
// 使用沙箱评估条件,防止恶意代码执行
|
|
267
|
+
conditionValue = await evaluateInSandbox(branch.condition, context, { timeout: 5000 });
|
|
295
268
|
}
|
|
296
|
-
|
|
297
269
|
if (conditionValue) {
|
|
298
|
-
|
|
299
|
-
|
|
270
|
+
this._log.debug(` Condition matched: ${branch.name || branch.stepId}`);
|
|
300
271
|
if (branch.steps && branch.steps.length > 0) {
|
|
301
|
-
await this.executeSteps(branch.steps, context)
|
|
272
|
+
await this.executeSteps(branch.steps, context);
|
|
302
273
|
}
|
|
303
|
-
|
|
304
|
-
return branch.stepId || branch.name
|
|
274
|
+
return branch.stepId || branch.name;
|
|
305
275
|
}
|
|
306
276
|
} catch (err) {
|
|
307
|
-
|
|
277
|
+
this._log.warn(` Branch condition error:`, err.message);
|
|
308
278
|
}
|
|
309
279
|
}
|
|
310
|
-
|
|
311
280
|
// 执行默认分支
|
|
312
281
|
if (step.defaultBranch && step.defaultBranch.steps) {
|
|
313
|
-
|
|
314
|
-
await this.executeSteps(step.defaultBranch.steps, context)
|
|
282
|
+
this._log.debug(` Executing default branch`);
|
|
283
|
+
await this.executeSteps(step.defaultBranch.steps, context);
|
|
315
284
|
}
|
|
316
|
-
|
|
317
|
-
return null
|
|
285
|
+
return null;
|
|
318
286
|
}
|
|
319
|
-
|
|
320
287
|
/**
|
|
321
288
|
* 执行循环步骤
|
|
322
289
|
*/
|
|
323
290
|
async executeLoop(step, context) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
const loopVariable = step.loopVariable || 'loopIndex'
|
|
329
|
-
|
|
291
|
+
this._log.debug(` Executing loop: ${step.name || step.id}`);
|
|
292
|
+
const results = [];
|
|
293
|
+
const maxIterations = step.maxIterations || 10;
|
|
294
|
+
const loopVariable = step.loopVariable || 'loopIndex';
|
|
330
295
|
for (let i = 0; i < maxIterations; i++) {
|
|
331
|
-
context.variables[loopVariable] = i
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
iterationResults.push(result)
|
|
296
|
+
context.variables[loopVariable] = i;
|
|
297
|
+
this._log.debug(` Loop iteration ${i}`);
|
|
298
|
+
const iterationResults = [];
|
|
299
|
+
for (const stepConfig of step.steps || []) {
|
|
300
|
+
const result = await this.executeStep(stepConfig, context);
|
|
301
|
+
iterationResults.push(result);
|
|
338
302
|
}
|
|
339
|
-
results.push(iterationResults)
|
|
340
|
-
|
|
303
|
+
results.push(iterationResults);
|
|
341
304
|
// 检查终止条件
|
|
342
305
|
if (step.until) {
|
|
343
|
-
let shouldStop = false
|
|
306
|
+
let shouldStop = false;
|
|
344
307
|
if (typeof step.until === 'function') {
|
|
345
|
-
shouldStop = step.until(context)
|
|
308
|
+
shouldStop = step.until(context);
|
|
346
309
|
} else if (typeof step.until === 'string') {
|
|
347
|
-
|
|
348
|
-
|
|
310
|
+
// 使用沙箱评估循环条件,防止恶意代码执行
|
|
311
|
+
shouldStop = await evaluateInSandbox(step.until, context, { timeout: 5000 });
|
|
349
312
|
}
|
|
350
|
-
|
|
351
313
|
if (shouldStop) {
|
|
352
|
-
|
|
353
|
-
break
|
|
314
|
+
this._log.debug(` Loop terminated at iteration ${i}`);
|
|
315
|
+
break;
|
|
354
316
|
}
|
|
355
317
|
}
|
|
356
318
|
}
|
|
357
|
-
|
|
358
|
-
return results
|
|
319
|
+
return results;
|
|
359
320
|
}
|
|
360
|
-
|
|
361
321
|
/**
|
|
362
322
|
* 执行延迟步骤
|
|
363
323
|
*/
|
|
364
324
|
async executeDelay(step, context) {
|
|
365
|
-
const delayMs = step.delayMs || 1000
|
|
366
|
-
|
|
367
|
-
await new Promise(resolve => setTimeout(resolve, delayMs))
|
|
368
|
-
return null
|
|
325
|
+
const delayMs = step.delayMs || 1000;
|
|
326
|
+
this._log.debug(` Delaying ${delayMs}ms`);
|
|
327
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
328
|
+
return null;
|
|
369
329
|
}
|
|
370
|
-
|
|
371
330
|
/**
|
|
372
331
|
* 执行顺序步骤
|
|
373
332
|
*/
|
|
374
333
|
async executeSequential(step, context) {
|
|
375
|
-
|
|
376
|
-
return await this.executeSteps(step.steps || [], context)
|
|
334
|
+
this._log.debug(` Executing sequential: ${step.name || step.id}`);
|
|
335
|
+
return await this.executeSteps(step.steps || [], context);
|
|
377
336
|
}
|
|
378
|
-
|
|
379
337
|
/**
|
|
380
338
|
* 执行消息步骤(ExplorerLoop 专用)
|
|
381
339
|
*/
|
|
382
340
|
async executeMessage(step, context) {
|
|
383
|
-
|
|
384
|
-
|
|
341
|
+
this._log.debug(` Executing message step`);
|
|
385
342
|
// 使用子Agent处理消息,避免阻塞主agent
|
|
386
343
|
const messageAgent = this.framework.createSubAgent({
|
|
387
344
|
name: 'workflow_message',
|
|
388
|
-
role: '工作流任务执行助手,专注于处理工作流中的消息任务'
|
|
389
|
-
})
|
|
390
|
-
|
|
391
|
-
let content = step.content || ''
|
|
345
|
+
role: '工作流任务执行助手,专注于处理工作流中的消息任务',
|
|
346
|
+
});
|
|
347
|
+
let content = step.content || '';
|
|
392
348
|
// 支持变量引用
|
|
393
|
-
content = this._resolveValue(content, context)
|
|
394
|
-
|
|
349
|
+
content = this._resolveValue(content, context);
|
|
395
350
|
// 如果有事件上下文,附加到消息
|
|
396
351
|
if (context.variables?._event) {
|
|
397
|
-
content = `${content}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]
|
|
352
|
+
content = `${content}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`;
|
|
398
353
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
return { success: true, result }
|
|
354
|
+
const result = await messageAgent.chat(content);
|
|
355
|
+
return { success: true, result };
|
|
402
356
|
}
|
|
403
|
-
|
|
404
357
|
/**
|
|
405
358
|
* 执行思考步骤(ExplorerLoop 专用)
|
|
406
359
|
*/
|
|
407
360
|
async executeThink(step, context) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const thinkPlugin = this.framework.pluginManager?.get('think')
|
|
361
|
+
this._log.debug(` Executing think step`);
|
|
362
|
+
const thinkPlugin = this.framework.pluginManager?.get('think');
|
|
411
363
|
if (!thinkPlugin) {
|
|
412
|
-
return { success: false, error: '思考插件不可用' }
|
|
364
|
+
return { success: false, error: '思考插件不可用' };
|
|
413
365
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
topic = this._resolveValue(topic, context)
|
|
417
|
-
|
|
366
|
+
let topic = step.topic || 'Ambient代理反思';
|
|
367
|
+
topic = this._resolveValue(topic, context);
|
|
418
368
|
if (context.variables?._event) {
|
|
419
|
-
topic = `${topic}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]
|
|
369
|
+
topic = `${topic}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`;
|
|
420
370
|
}
|
|
421
|
-
|
|
422
371
|
const result = await thinkPlugin._triggerThinking({
|
|
423
372
|
topic,
|
|
424
373
|
mode: step.mode || 'reflect',
|
|
425
|
-
depth: step.depth || 2
|
|
426
|
-
})
|
|
427
|
-
|
|
428
|
-
return result
|
|
374
|
+
depth: step.depth || 2,
|
|
375
|
+
});
|
|
376
|
+
return result;
|
|
429
377
|
}
|
|
430
|
-
|
|
431
378
|
/**
|
|
432
379
|
* 获取活跃代理
|
|
433
380
|
*/
|
|
434
381
|
_getActiveAgent() {
|
|
435
382
|
if (this.framework._mainAgent) {
|
|
436
|
-
return this.framework._mainAgent
|
|
383
|
+
return this.framework._mainAgent;
|
|
437
384
|
}
|
|
438
|
-
const agents = this.framework._agents || []
|
|
439
|
-
return agents.length > 0 ? agents[agents.length - 1] : null
|
|
385
|
+
const agents = this.framework._agents || [];
|
|
386
|
+
return agents.length > 0 ? agents[agents.length - 1] : null;
|
|
440
387
|
}
|
|
441
|
-
|
|
442
388
|
/**
|
|
443
389
|
* 解析参数中的变量引用
|
|
444
390
|
*/
|
|
445
391
|
_resolveArgs(args, context) {
|
|
446
|
-
const resolved = {}
|
|
392
|
+
const resolved = {};
|
|
447
393
|
for (const [key, value] of Object.entries(args)) {
|
|
448
|
-
resolved[key] = this._resolveValue(value, context)
|
|
394
|
+
resolved[key] = this._resolveValue(value, context);
|
|
449
395
|
}
|
|
450
|
-
return resolved
|
|
396
|
+
return resolved;
|
|
451
397
|
}
|
|
452
|
-
|
|
453
398
|
_resolveValue(value, context) {
|
|
454
399
|
if (typeof value === 'string') {
|
|
455
400
|
// 支持多种引用格式:
|
|
@@ -460,290 +405,267 @@ class StepExecutor {
|
|
|
460
405
|
// {{lastResult}} - 上一步结果(result 的别名)
|
|
461
406
|
// ${stepId.output} - 引用指定 id 步骤的输出(兼容旧格式)
|
|
462
407
|
// ${stepId.output.path} - 从指定步骤输出提取字段
|
|
463
|
-
let result = value
|
|
464
|
-
|
|
408
|
+
let result = value;
|
|
465
409
|
// 先处理 ${stepId.output} 格式(兼容旧格式)
|
|
466
410
|
result = result.replace(/\$\{([^}]+)\}/g, (match, path) => {
|
|
467
|
-
const stepOutputs = context.variables._stepOutputs || {}
|
|
411
|
+
const stepOutputs = context.variables._stepOutputs || {};
|
|
468
412
|
if (path === 'output') {
|
|
469
413
|
// ${output} 等同于 ${stepId.output} 引用上一步
|
|
470
|
-
return context.lastResult ?? match
|
|
414
|
+
return context.lastResult ?? match;
|
|
471
415
|
}
|
|
472
416
|
if (path.includes('.')) {
|
|
473
417
|
// ${stepId.output.field} 格式
|
|
474
|
-
const [stepId, ...fieldParts] = path.split('.')
|
|
475
|
-
const stepOutput = stepOutputs[stepId]
|
|
418
|
+
const [stepId, ...fieldParts] = path.split('.');
|
|
419
|
+
const stepOutput = stepOutputs[stepId];
|
|
476
420
|
if (stepOutput !== undefined) {
|
|
477
|
-
const val = this._getNestedValue(stepOutput, fieldParts.join('.'))
|
|
478
|
-
if (val !== undefined) return val
|
|
421
|
+
const val = this._getNestedValue(stepOutput, fieldParts.join('.'));
|
|
422
|
+
if (val !== undefined) return val;
|
|
479
423
|
}
|
|
480
424
|
} else {
|
|
481
425
|
// ${stepId.output} 格式
|
|
482
|
-
const stepOutput = stepOutputs[path]
|
|
483
|
-
if (stepOutput !== undefined) return stepOutput
|
|
426
|
+
const stepOutput = stepOutputs[path];
|
|
427
|
+
if (stepOutput !== undefined) return stepOutput;
|
|
484
428
|
}
|
|
485
|
-
return match
|
|
486
|
-
})
|
|
487
|
-
|
|
429
|
+
return match;
|
|
430
|
+
});
|
|
488
431
|
// 再处理 {{...}} 格式(支持 _event.email.from 等嵌套属性)
|
|
489
432
|
result = result.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
|
|
490
433
|
// result 和 lastResult 都指向 context.lastResult
|
|
491
434
|
if (path === 'result' || path === 'lastResult') {
|
|
492
|
-
return context.lastResult ?? match
|
|
435
|
+
return context.lastResult ?? match;
|
|
493
436
|
}
|
|
494
437
|
if (path.startsWith('result.') || path.startsWith('lastResult.')) {
|
|
495
|
-
const nestedPath = path.replace(/^(result|lastResult)\./, '')
|
|
496
|
-
const val = this._getNestedValue(context.lastResult, nestedPath)
|
|
497
|
-
if (val !== undefined) return val
|
|
498
|
-
return match
|
|
438
|
+
const nestedPath = path.replace(/^(result|lastResult)\./, '');
|
|
439
|
+
const val = this._getNestedValue(context.lastResult, nestedPath);
|
|
440
|
+
if (val !== undefined) return val;
|
|
441
|
+
return match;
|
|
499
442
|
}
|
|
500
443
|
if (path.startsWith('variables.')) {
|
|
501
|
-
return this._getNestedValue(context, path) ?? match
|
|
444
|
+
return this._getNestedValue(context, path) ?? match;
|
|
502
445
|
} else if (path.startsWith('context.')) {
|
|
503
|
-
return this._getNestedValue(context, path) ?? match
|
|
446
|
+
return this._getNestedValue(context, path) ?? match;
|
|
504
447
|
} else {
|
|
505
448
|
// 先从 variables 查找,再从 context 根属性查找
|
|
506
|
-
const val = this._getNestedValue(context.variables, path)
|
|
507
|
-
if (val !== undefined) return val
|
|
508
|
-
return this._getNestedValue(context, path) ?? match
|
|
449
|
+
const val = this._getNestedValue(context.variables, path);
|
|
450
|
+
if (val !== undefined) return val;
|
|
451
|
+
return this._getNestedValue(context, path) ?? match;
|
|
509
452
|
}
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
return result
|
|
453
|
+
});
|
|
454
|
+
return result;
|
|
513
455
|
} else if (Array.isArray(value)) {
|
|
514
|
-
return value.map(v => this._resolveValue(v, context))
|
|
456
|
+
return value.map((v) => this._resolveValue(v, context));
|
|
515
457
|
} else if (typeof value === 'object' && value !== null) {
|
|
516
|
-
const resolved = {}
|
|
458
|
+
const resolved = {};
|
|
517
459
|
for (const [k, v] of Object.entries(value)) {
|
|
518
|
-
resolved[k] = this._resolveValue(v, context)
|
|
460
|
+
resolved[k] = this._resolveValue(v, context);
|
|
519
461
|
}
|
|
520
|
-
return resolved
|
|
462
|
+
return resolved;
|
|
521
463
|
}
|
|
522
|
-
return value
|
|
464
|
+
return value;
|
|
523
465
|
}
|
|
524
|
-
|
|
525
466
|
_getNestedValue(obj, path) {
|
|
526
|
-
if (!obj) return undefined
|
|
527
|
-
const parts = path.split('.')
|
|
528
|
-
let current = obj
|
|
467
|
+
if (!obj) return undefined;
|
|
468
|
+
const parts = path.split('.');
|
|
469
|
+
let current = obj;
|
|
529
470
|
for (const part of parts) {
|
|
530
|
-
if (current === null || current === undefined) return undefined
|
|
471
|
+
if (current === null || current === undefined) return undefined;
|
|
531
472
|
// 如果当前值是字符串,尝试解析为 JSON
|
|
532
473
|
if (typeof current === 'string' && part !== '0' && part !== 'length') {
|
|
533
474
|
try {
|
|
534
|
-
current = JSON.parse(current)
|
|
475
|
+
current = JSON.parse(current);
|
|
535
476
|
} catch {
|
|
536
477
|
// 解析失败,继续用原值
|
|
537
478
|
}
|
|
538
479
|
}
|
|
539
|
-
current = current[part]
|
|
480
|
+
current = current[part];
|
|
540
481
|
}
|
|
541
|
-
return current
|
|
482
|
+
return current;
|
|
542
483
|
}
|
|
543
484
|
}
|
|
544
|
-
|
|
545
485
|
/**
|
|
546
486
|
* 工作流步骤基类
|
|
547
487
|
*/
|
|
548
488
|
class WorkflowStep {
|
|
549
489
|
constructor(config) {
|
|
550
|
-
this.id = config.id || `step_${Date.now()}_${
|
|
551
|
-
this.type = config.type
|
|
552
|
-
this.name = config.name || ''
|
|
553
|
-
this.description = config.description || ''
|
|
554
|
-
this.condition = config.condition || null
|
|
555
|
-
this.timeout = config.timeout || 30000
|
|
556
|
-
this.retry = config.retry || { enabled: false, maxAttempts: 3, delay: 1000 }
|
|
557
|
-
this.onSuccess = config.onSuccess || null
|
|
558
|
-
this.onFailure = config.onFailure || null
|
|
490
|
+
this.id = config.id || `step_${Date.now()}_${crypto.randomBytes(6).toString('base64url')}`;
|
|
491
|
+
this.type = config.type;
|
|
492
|
+
this.name = config.name || '';
|
|
493
|
+
this.description = config.description || '';
|
|
494
|
+
this.condition = config.condition || null;
|
|
495
|
+
this.timeout = config.timeout || 30000;
|
|
496
|
+
this.retry = config.retry || { enabled: false, maxAttempts: 3, delay: 1000 };
|
|
497
|
+
this.onSuccess = config.onSuccess || null;
|
|
498
|
+
this.onFailure = config.onFailure || null;
|
|
559
499
|
}
|
|
560
|
-
|
|
561
500
|
async execute(context, engine) {
|
|
562
|
-
throw new Error('execute() must be implemented by subclass')
|
|
501
|
+
throw new Error('execute() must be implemented by subclass');
|
|
563
502
|
}
|
|
564
|
-
|
|
565
503
|
validate() {
|
|
566
504
|
if (!this.type) {
|
|
567
|
-
throw new Error('Step must have a type')
|
|
505
|
+
throw new Error('Step must have a type');
|
|
568
506
|
}
|
|
569
|
-
return true
|
|
507
|
+
return true;
|
|
570
508
|
}
|
|
571
509
|
}
|
|
572
|
-
|
|
573
510
|
/**
|
|
574
511
|
* 脚本步骤
|
|
575
512
|
*/
|
|
576
513
|
class ScriptStep extends WorkflowStep {
|
|
577
514
|
constructor(config) {
|
|
578
|
-
super({ ...config, type: StepType.SCRIPT })
|
|
579
|
-
this.script = config.script
|
|
580
|
-
this.outputVariable = config.outputVariable || null
|
|
515
|
+
super({ ...config, type: StepType.SCRIPT });
|
|
516
|
+
this.script = config.script;
|
|
517
|
+
this.outputVariable = config.outputVariable || null;
|
|
581
518
|
}
|
|
582
|
-
|
|
583
519
|
async execute(context, engine) {
|
|
584
520
|
// 使用 StepExecutor 执行脚本逻辑
|
|
585
|
-
const executor = new StepExecutor(engine.framework)
|
|
521
|
+
const executor = new StepExecutor(engine.framework);
|
|
586
522
|
const stepConfig = {
|
|
587
523
|
id: this.id,
|
|
588
524
|
name: this.name,
|
|
589
525
|
type: StepType.SCRIPT,
|
|
590
526
|
script: this.script,
|
|
591
|
-
outputVariable: this.outputVariable
|
|
592
|
-
}
|
|
593
|
-
return await executor.executeScript(stepConfig, context)
|
|
527
|
+
outputVariable: this.outputVariable,
|
|
528
|
+
};
|
|
529
|
+
return await executor.executeScript(stepConfig, context);
|
|
594
530
|
}
|
|
595
531
|
}
|
|
596
|
-
|
|
597
532
|
/**
|
|
598
533
|
* 条件步骤
|
|
599
534
|
*/
|
|
600
535
|
class ConditionStep extends WorkflowStep {
|
|
601
536
|
constructor(config) {
|
|
602
|
-
super({ ...config, type: StepType.CONDITION })
|
|
603
|
-
this.branches = config.branches || []
|
|
604
|
-
this.defaultBranch = config.defaultBranch || null
|
|
537
|
+
super({ ...config, type: StepType.CONDITION });
|
|
538
|
+
this.branches = config.branches || [];
|
|
539
|
+
this.defaultBranch = config.defaultBranch || null;
|
|
605
540
|
}
|
|
606
|
-
|
|
607
541
|
async execute(context, engine) {
|
|
608
|
-
const executor = new StepExecutor(engine.framework)
|
|
542
|
+
const executor = new StepExecutor(engine.framework);
|
|
609
543
|
const stepConfig = {
|
|
610
544
|
id: this.id,
|
|
611
545
|
name: this.name,
|
|
612
546
|
type: StepType.CONDITION,
|
|
613
547
|
branches: this.branches,
|
|
614
|
-
defaultBranch: this.defaultBranch
|
|
615
|
-
}
|
|
616
|
-
return await executor.executeCondition(stepConfig, context)
|
|
548
|
+
defaultBranch: this.defaultBranch,
|
|
549
|
+
};
|
|
550
|
+
return await executor.executeCondition(stepConfig, context);
|
|
617
551
|
}
|
|
618
552
|
}
|
|
619
|
-
|
|
620
553
|
/**
|
|
621
554
|
* 并行步骤
|
|
622
555
|
*/
|
|
623
556
|
class ParallelStep extends WorkflowStep {
|
|
624
557
|
constructor(config) {
|
|
625
|
-
super({ ...config, type: StepType.PARALLEL })
|
|
626
|
-
this.steps = config.steps || []
|
|
558
|
+
super({ ...config, type: StepType.PARALLEL });
|
|
559
|
+
this.steps = config.steps || [];
|
|
627
560
|
}
|
|
628
|
-
|
|
629
561
|
async execute(context, engine) {
|
|
630
|
-
const executor = new StepExecutor(engine.framework)
|
|
562
|
+
const executor = new StepExecutor(engine.framework);
|
|
631
563
|
const stepConfig = {
|
|
632
564
|
id: this.id,
|
|
633
565
|
name: this.name,
|
|
634
566
|
type: StepType.PARALLEL,
|
|
635
|
-
steps: this.steps
|
|
636
|
-
}
|
|
637
|
-
return await executor.executeParallel(stepConfig, context)
|
|
567
|
+
steps: this.steps,
|
|
568
|
+
};
|
|
569
|
+
return await executor.executeParallel(stepConfig, context);
|
|
638
570
|
}
|
|
639
571
|
}
|
|
640
|
-
|
|
641
572
|
/**
|
|
642
573
|
* Switch 步骤
|
|
643
574
|
*/
|
|
644
575
|
class SwitchStep extends WorkflowStep {
|
|
645
576
|
constructor(config) {
|
|
646
|
-
super({ ...config, type: StepType.SWITCH })
|
|
647
|
-
this.value = config.value
|
|
648
|
-
this.branches = config.branches || []
|
|
649
|
-
this.default = config.default || null
|
|
577
|
+
super({ ...config, type: StepType.SWITCH });
|
|
578
|
+
this.value = config.value;
|
|
579
|
+
this.branches = config.branches || [];
|
|
580
|
+
this.default = config.default || null;
|
|
650
581
|
}
|
|
651
|
-
|
|
652
582
|
async execute(context, engine) {
|
|
653
|
-
const executor = new StepExecutor(engine.framework)
|
|
583
|
+
const executor = new StepExecutor(engine.framework);
|
|
654
584
|
const stepConfig = {
|
|
655
585
|
id: this.id,
|
|
656
586
|
name: this.name,
|
|
657
587
|
type: StepType.SWITCH,
|
|
658
588
|
value: this.value,
|
|
659
589
|
branches: this.branches,
|
|
660
|
-
default: this.default
|
|
661
|
-
}
|
|
662
|
-
return await executor.executeSwitch(stepConfig, context)
|
|
590
|
+
default: this.default,
|
|
591
|
+
};
|
|
592
|
+
return await executor.executeSwitch(stepConfig, context);
|
|
663
593
|
}
|
|
664
594
|
}
|
|
665
|
-
|
|
666
595
|
/**
|
|
667
596
|
* Try-Catch 步骤
|
|
668
597
|
*/
|
|
669
598
|
class TryStep extends WorkflowStep {
|
|
670
599
|
constructor(config) {
|
|
671
|
-
super({ ...config, type: StepType.TRY })
|
|
672
|
-
this.try = config.try || {}
|
|
673
|
-
this.catch = config.catch || {}
|
|
600
|
+
super({ ...config, type: StepType.TRY });
|
|
601
|
+
this.try = config.try || {};
|
|
602
|
+
this.catch = config.catch || {};
|
|
674
603
|
}
|
|
675
|
-
|
|
676
604
|
async execute(context, engine) {
|
|
677
|
-
const executor = new StepExecutor(engine.framework)
|
|
605
|
+
const executor = new StepExecutor(engine.framework);
|
|
678
606
|
const stepConfig = {
|
|
679
607
|
id: this.id,
|
|
680
608
|
name: this.name,
|
|
681
609
|
type: StepType.TRY,
|
|
682
610
|
try: this.try,
|
|
683
|
-
catch: this.catch
|
|
684
|
-
}
|
|
685
|
-
return await executor.executeTry(stepConfig, context)
|
|
611
|
+
catch: this.catch,
|
|
612
|
+
};
|
|
613
|
+
return await executor.executeTry(stepConfig, context);
|
|
686
614
|
}
|
|
687
615
|
}
|
|
688
|
-
|
|
689
616
|
/**
|
|
690
617
|
* 嵌套工作流步骤
|
|
691
618
|
*/
|
|
692
619
|
class NestedWorkflowStep extends WorkflowStep {
|
|
693
620
|
constructor(config) {
|
|
694
|
-
super({ ...config, type: StepType.WORKFLOW })
|
|
695
|
-
this.workflow = config.workflow
|
|
696
|
-
this.input = config.input || {}
|
|
621
|
+
super({ ...config, type: StepType.WORKFLOW });
|
|
622
|
+
this.workflow = config.workflow;
|
|
623
|
+
this.input = config.input || {};
|
|
697
624
|
}
|
|
698
|
-
|
|
699
625
|
async execute(context, engine) {
|
|
700
|
-
const executor = new StepExecutor(engine.framework)
|
|
626
|
+
const executor = new StepExecutor(engine.framework);
|
|
701
627
|
const stepConfig = {
|
|
702
628
|
id: this.id,
|
|
703
629
|
name: this.name,
|
|
704
630
|
type: StepType.WORKFLOW,
|
|
705
631
|
workflow: this.workflow,
|
|
706
|
-
input: this.input
|
|
707
|
-
}
|
|
708
|
-
return await executor.executeWorkflowStep(stepConfig, context)
|
|
632
|
+
input: this.input,
|
|
633
|
+
};
|
|
634
|
+
return await executor.executeWorkflowStep(stepConfig, context);
|
|
709
635
|
}
|
|
710
636
|
}
|
|
711
|
-
|
|
712
637
|
/**
|
|
713
638
|
* 顺序步骤
|
|
714
639
|
*/
|
|
715
640
|
class SequentialStep extends WorkflowStep {
|
|
716
641
|
constructor(config) {
|
|
717
|
-
super({ ...config, type: StepType.SEQUENTIAL })
|
|
718
|
-
this.steps = config.steps || []
|
|
642
|
+
super({ ...config, type: StepType.SEQUENTIAL });
|
|
643
|
+
this.steps = config.steps || [];
|
|
719
644
|
}
|
|
720
|
-
|
|
721
645
|
async execute(context, engine) {
|
|
722
|
-
const executor = new StepExecutor(engine.framework)
|
|
646
|
+
const executor = new StepExecutor(engine.framework);
|
|
723
647
|
const stepConfig = {
|
|
724
648
|
id: this.id,
|
|
725
649
|
name: this.name,
|
|
726
650
|
type: StepType.SEQUENTIAL,
|
|
727
|
-
steps: this.steps
|
|
728
|
-
}
|
|
729
|
-
return await executor.executeSequential(stepConfig, context)
|
|
651
|
+
steps: this.steps,
|
|
652
|
+
};
|
|
653
|
+
return await executor.executeSequential(stepConfig, context);
|
|
730
654
|
}
|
|
731
655
|
}
|
|
732
|
-
|
|
733
656
|
/**
|
|
734
657
|
* 循环步骤
|
|
735
658
|
*/
|
|
736
659
|
class LoopStep extends WorkflowStep {
|
|
737
660
|
constructor(config) {
|
|
738
|
-
super({ ...config, type: StepType.LOOP })
|
|
739
|
-
this.maxIterations = config.maxIterations || 10
|
|
740
|
-
this.loopVariable = config.loopVariable || 'loopIndex'
|
|
741
|
-
this.steps = config.steps || []
|
|
742
|
-
this.until = config.until || null
|
|
661
|
+
super({ ...config, type: StepType.LOOP });
|
|
662
|
+
this.maxIterations = config.maxIterations || 10;
|
|
663
|
+
this.loopVariable = config.loopVariable || 'loopIndex';
|
|
664
|
+
this.steps = config.steps || [];
|
|
665
|
+
this.until = config.until || null;
|
|
743
666
|
}
|
|
744
|
-
|
|
745
667
|
async execute(context, engine) {
|
|
746
|
-
const executor = new StepExecutor(engine.framework)
|
|
668
|
+
const executor = new StepExecutor(engine.framework);
|
|
747
669
|
const stepConfig = {
|
|
748
670
|
id: this.id,
|
|
749
671
|
name: this.name,
|
|
@@ -751,48 +673,44 @@ class LoopStep extends WorkflowStep {
|
|
|
751
673
|
maxIterations: this.maxIterations,
|
|
752
674
|
loopVariable: this.loopVariable,
|
|
753
675
|
steps: this.steps,
|
|
754
|
-
until: this.until
|
|
755
|
-
}
|
|
756
|
-
return await executor.executeLoop(stepConfig, context)
|
|
676
|
+
until: this.until,
|
|
677
|
+
};
|
|
678
|
+
return await executor.executeLoop(stepConfig, context);
|
|
757
679
|
}
|
|
758
680
|
}
|
|
759
|
-
|
|
760
681
|
/**
|
|
761
682
|
* 延迟步骤
|
|
762
683
|
*/
|
|
763
684
|
class DelayStep extends WorkflowStep {
|
|
764
685
|
constructor(config) {
|
|
765
|
-
super({ ...config, type: StepType.DELAY })
|
|
766
|
-
this.delayMs = config.delayMs || 1000
|
|
686
|
+
super({ ...config, type: StepType.DELAY });
|
|
687
|
+
this.delayMs = config.delayMs || 1000;
|
|
767
688
|
}
|
|
768
|
-
|
|
769
689
|
async execute(context, engine) {
|
|
770
|
-
const executor = new StepExecutor(engine.framework)
|
|
690
|
+
const executor = new StepExecutor(engine.framework);
|
|
771
691
|
const stepConfig = {
|
|
772
692
|
id: this.id,
|
|
773
693
|
name: this.name,
|
|
774
694
|
type: StepType.DELAY,
|
|
775
|
-
delayMs: this.delayMs
|
|
776
|
-
}
|
|
777
|
-
return await executor.executeDelay(stepConfig, context)
|
|
695
|
+
delayMs: this.delayMs,
|
|
696
|
+
};
|
|
697
|
+
return await executor.executeDelay(stepConfig, context);
|
|
778
698
|
}
|
|
779
699
|
}
|
|
780
|
-
|
|
781
700
|
/**
|
|
782
701
|
* 工具步骤
|
|
783
702
|
*/
|
|
784
703
|
class ToolStep extends WorkflowStep {
|
|
785
704
|
constructor(config) {
|
|
786
|
-
super({ ...config, type: StepType.TOOL })
|
|
787
|
-
this.tool = config.tool
|
|
788
|
-
this.args = config.args || {}
|
|
789
|
-
this.outputVariable = config.outputVariable || null
|
|
790
|
-
this.output = config.output || null
|
|
791
|
-
this.input = config.input || null
|
|
705
|
+
super({ ...config, type: StepType.TOOL });
|
|
706
|
+
this.tool = config.tool;
|
|
707
|
+
this.args = config.args || {};
|
|
708
|
+
this.outputVariable = config.outputVariable || null;
|
|
709
|
+
this.output = config.output || null; // 兼容旧格式
|
|
710
|
+
this.input = config.input || null; // 兼容旧格式:input 字段
|
|
792
711
|
}
|
|
793
|
-
|
|
794
712
|
async execute(context, engine) {
|
|
795
|
-
const executor = new StepExecutor(engine.framework)
|
|
713
|
+
const executor = new StepExecutor(engine.framework);
|
|
796
714
|
const stepConfig = {
|
|
797
715
|
id: this.id,
|
|
798
716
|
name: this.name,
|
|
@@ -800,31 +718,28 @@ class ToolStep extends WorkflowStep {
|
|
|
800
718
|
tool: this.tool,
|
|
801
719
|
args: this.args,
|
|
802
720
|
outputVariable: this.outputVariable,
|
|
803
|
-
output: this.output,
|
|
804
|
-
input: this.input
|
|
805
|
-
}
|
|
806
|
-
return await executor.executeTool(stepConfig, context)
|
|
721
|
+
output: this.output, // 传递 output
|
|
722
|
+
input: this.input, // 传递 input
|
|
723
|
+
};
|
|
724
|
+
return await executor.executeTool(stepConfig, context);
|
|
807
725
|
}
|
|
808
726
|
}
|
|
809
|
-
|
|
810
727
|
/**
|
|
811
728
|
* 工作流引擎
|
|
812
729
|
*/
|
|
813
730
|
class WorkflowEngine extends EventEmitter {
|
|
814
731
|
constructor(framework) {
|
|
815
|
-
super()
|
|
816
|
-
this.framework = framework
|
|
817
|
-
this._steps = new Map()
|
|
732
|
+
super();
|
|
733
|
+
this.framework = framework;
|
|
734
|
+
this._steps = new Map();
|
|
818
735
|
}
|
|
819
|
-
|
|
820
736
|
/**
|
|
821
737
|
* 注册自定义步骤类型
|
|
822
738
|
*/
|
|
823
739
|
registerStepType(type, StepClass) {
|
|
824
|
-
this._steps.set(type, StepClass)
|
|
740
|
+
this._steps.set(type, StepClass);
|
|
825
741
|
}
|
|
826
|
-
|
|
827
|
-
/**
|
|
742
|
+
/**
|
|
828
743
|
* 创建步骤实例
|
|
829
744
|
* 自动推断步骤类型:
|
|
830
745
|
* - 有 tool 字段 -> tool 类型
|
|
@@ -839,51 +754,47 @@ class WorkflowEngine extends EventEmitter {
|
|
|
839
754
|
* - 有 workflow 字段 -> workflow 类型
|
|
840
755
|
*/
|
|
841
756
|
createStep(config) {
|
|
842
|
-
let type = config.type
|
|
843
|
-
|
|
757
|
+
let type = config.type;
|
|
844
758
|
// 自动推断类型
|
|
845
759
|
if (!type) {
|
|
846
760
|
if (config.tool) {
|
|
847
|
-
type = StepType.TOOL
|
|
761
|
+
type = StepType.TOOL;
|
|
848
762
|
} else if (config.code || config.script) {
|
|
849
|
-
type = StepType.SCRIPT
|
|
763
|
+
type = StepType.SCRIPT;
|
|
850
764
|
} else if (config.try || config.catch) {
|
|
851
|
-
type = StepType.TRY
|
|
765
|
+
type = StepType.TRY;
|
|
852
766
|
} else if (config.value !== undefined && config.branches) {
|
|
853
|
-
type = StepType.SWITCH
|
|
767
|
+
type = StepType.SWITCH;
|
|
854
768
|
} else if (config.parallel === true || config.mode === 'parallel') {
|
|
855
|
-
type = StepType.PARALLEL
|
|
769
|
+
type = StepType.PARALLEL;
|
|
856
770
|
} else if (config.workflow) {
|
|
857
|
-
type = StepType.WORKFLOW
|
|
771
|
+
type = StepType.WORKFLOW;
|
|
858
772
|
} else if (config.branches) {
|
|
859
|
-
type = StepType.CONDITION
|
|
773
|
+
type = StepType.CONDITION;
|
|
860
774
|
} else if (config.steps && (config.loopVariable || config.maxIterations)) {
|
|
861
|
-
type = StepType.LOOP
|
|
775
|
+
type = StepType.LOOP;
|
|
862
776
|
} else if (config.steps) {
|
|
863
|
-
type = StepType.SEQUENTIAL
|
|
777
|
+
type = StepType.SEQUENTIAL;
|
|
864
778
|
} else if (config.delayMs !== undefined) {
|
|
865
|
-
type = StepType.DELAY
|
|
779
|
+
type = StepType.DELAY;
|
|
866
780
|
} else if (config.message) {
|
|
867
|
-
type = StepType.MESSAGE
|
|
781
|
+
type = StepType.MESSAGE;
|
|
868
782
|
} else if (config.topic) {
|
|
869
|
-
type = StepType.THINK
|
|
783
|
+
type = StepType.THINK;
|
|
870
784
|
}
|
|
871
785
|
}
|
|
872
|
-
|
|
873
786
|
if (!type) {
|
|
874
|
-
throw new Error(
|
|
787
|
+
throw new Error(
|
|
788
|
+
`Cannot infer step type for step: ${JSON.stringify(config).substring(0, 100)}`
|
|
789
|
+
);
|
|
875
790
|
}
|
|
876
|
-
|
|
877
|
-
const StepClass = this._steps.get(type)
|
|
878
|
-
|
|
791
|
+
const StepClass = this._steps.get(type);
|
|
879
792
|
if (!StepClass) {
|
|
880
|
-
throw new Error(`Unknown step type: ${type}`)
|
|
793
|
+
throw new Error(`Unknown step type: ${type}`);
|
|
881
794
|
}
|
|
882
|
-
|
|
883
795
|
// 确保配置有 type 字段
|
|
884
|
-
return new StepClass({ ...config, type })
|
|
796
|
+
return new StepClass({ ...config, type });
|
|
885
797
|
}
|
|
886
|
-
|
|
887
798
|
/**
|
|
888
799
|
* 创建工作流上下文
|
|
889
800
|
*/
|
|
@@ -892,290 +803,293 @@ class WorkflowEngine extends EventEmitter {
|
|
|
892
803
|
input,
|
|
893
804
|
variables,
|
|
894
805
|
lastResult: null,
|
|
895
|
-
results: []
|
|
896
|
-
}
|
|
806
|
+
results: [],
|
|
807
|
+
};
|
|
897
808
|
}
|
|
898
809
|
}
|
|
899
|
-
|
|
900
810
|
/**
|
|
901
811
|
* WorkflowManagerPlugin
|
|
902
812
|
*/
|
|
903
813
|
class WorkflowPlugin extends Plugin {
|
|
904
814
|
constructor(config = {}) {
|
|
905
|
-
super()
|
|
906
|
-
this.name = 'workflow'
|
|
907
|
-
this.version = '1.0.0'
|
|
908
|
-
this.description = '工作流引擎,支持结构化工作流定义和执行'
|
|
909
|
-
this.priority = 6
|
|
910
|
-
this.system = true
|
|
911
|
-
|
|
912
|
-
this.
|
|
913
|
-
this.
|
|
914
|
-
this.
|
|
915
|
-
this.
|
|
916
|
-
this._workflowTools = new Map()
|
|
815
|
+
super();
|
|
816
|
+
this.name = 'workflow';
|
|
817
|
+
this.version = '1.0.0';
|
|
818
|
+
this.description = '工作流引擎,支持结构化工作流定义和执行';
|
|
819
|
+
this.priority = 6;
|
|
820
|
+
this.system = true;
|
|
821
|
+
this._framework = null;
|
|
822
|
+
this._engine = null;
|
|
823
|
+
this._workflowsDir = config.workflowsDir || '.agent/workflows';
|
|
824
|
+
this._workflows = new Map();
|
|
825
|
+
this._workflowTools = new Map();
|
|
917
826
|
}
|
|
918
|
-
|
|
919
827
|
install(framework) {
|
|
920
|
-
this._framework = framework
|
|
921
|
-
this._engine = new WorkflowEngine(framework)
|
|
922
|
-
|
|
828
|
+
this._framework = framework;
|
|
829
|
+
this._engine = new WorkflowEngine(framework);
|
|
923
830
|
// 注册内置步骤类型
|
|
924
|
-
this._engine.registerStepType(StepType.SCRIPT, ScriptStep)
|
|
925
|
-
this._engine.registerStepType(StepType.CONDITION, ConditionStep)
|
|
926
|
-
this._engine.registerStepType(StepType.SWITCH, SwitchStep)
|
|
927
|
-
this._engine.registerStepType(StepType.TRY, TryStep)
|
|
928
|
-
this._engine.registerStepType(StepType.PARALLEL, ParallelStep)
|
|
929
|
-
this._engine.registerStepType(StepType.SEQUENTIAL, SequentialStep)
|
|
930
|
-
this._engine.registerStepType(StepType.LOOP, LoopStep)
|
|
931
|
-
this._engine.registerStepType(StepType.DELAY, DelayStep)
|
|
932
|
-
this._engine.registerStepType(StepType.TOOL, ToolStep)
|
|
933
|
-
this._engine.registerStepType(StepType.WORKFLOW, NestedWorkflowStep)
|
|
934
|
-
|
|
831
|
+
this._engine.registerStepType(StepType.SCRIPT, ScriptStep);
|
|
832
|
+
this._engine.registerStepType(StepType.CONDITION, ConditionStep);
|
|
833
|
+
this._engine.registerStepType(StepType.SWITCH, SwitchStep);
|
|
834
|
+
this._engine.registerStepType(StepType.TRY, TryStep);
|
|
835
|
+
this._engine.registerStepType(StepType.PARALLEL, ParallelStep);
|
|
836
|
+
this._engine.registerStepType(StepType.SEQUENTIAL, SequentialStep);
|
|
837
|
+
this._engine.registerStepType(StepType.LOOP, LoopStep);
|
|
838
|
+
this._engine.registerStepType(StepType.DELAY, DelayStep);
|
|
839
|
+
this._engine.registerStepType(StepType.TOOL, ToolStep);
|
|
840
|
+
this._engine.registerStepType(StepType.WORKFLOW, NestedWorkflowStep);
|
|
935
841
|
// 注册工作流执行工具
|
|
936
842
|
framework.registerTool({
|
|
937
843
|
name: 'execute_workflow',
|
|
938
844
|
description: '执行指定的工作流',
|
|
939
845
|
inputSchema: z.object({
|
|
940
846
|
workflow: z.string().describe('工作流定义(JSON 或 JavaScript 代码)'),
|
|
941
|
-
input: z.object({}).optional().describe('工作流输入参数')
|
|
847
|
+
input: z.object({}).optional().describe('工作流输入参数'),
|
|
942
848
|
}),
|
|
943
849
|
execute: async (args, framework) => {
|
|
944
850
|
// 获取当前 sessionId
|
|
945
|
-
let sessionId = null
|
|
946
|
-
const ctx = framework.getExecutionContext()
|
|
851
|
+
let sessionId = null;
|
|
852
|
+
const ctx = framework.getExecutionContext();
|
|
947
853
|
if (ctx?.sessionId) {
|
|
948
|
-
sessionId = ctx.sessionId
|
|
854
|
+
sessionId = ctx.sessionId;
|
|
949
855
|
}
|
|
950
|
-
return await this.executeWorkflow(args.workflow, args.input || {}, sessionId)
|
|
951
|
-
}
|
|
952
|
-
})
|
|
953
|
-
|
|
954
|
-
return this
|
|
856
|
+
return await this.executeWorkflow(args.workflow, args.input || {}, sessionId);
|
|
857
|
+
},
|
|
858
|
+
});
|
|
859
|
+
return this;
|
|
955
860
|
}
|
|
956
|
-
|
|
957
861
|
start(framework) {
|
|
958
|
-
this._loadWorkflows()
|
|
959
|
-
this._registerWorkflowTools()
|
|
960
|
-
|
|
862
|
+
this._loadWorkflows();
|
|
863
|
+
this._registerWorkflowTools();
|
|
961
864
|
// 注册 reloadWorkflows 工具
|
|
962
865
|
framework.registerTool({
|
|
963
866
|
name: 'reloadWorkflows',
|
|
964
867
|
description: '重载所有工作流,当用户添加或修改工作流后调用此工具',
|
|
965
868
|
inputSchema: z.object({}),
|
|
966
869
|
execute: async () => {
|
|
967
|
-
this.reload(this._framework)
|
|
870
|
+
this.reload(this._framework);
|
|
968
871
|
return {
|
|
969
872
|
success: true,
|
|
970
873
|
message: `Workflows reloaded. Total: ${this._workflows.size}`,
|
|
971
|
-
workflows: Array.from(this._workflows.keys())
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
})
|
|
975
|
-
|
|
976
|
-
return this
|
|
874
|
+
workflows: Array.from(this._workflows.keys()),
|
|
875
|
+
};
|
|
876
|
+
},
|
|
877
|
+
});
|
|
878
|
+
return this;
|
|
977
879
|
}
|
|
978
|
-
|
|
979
880
|
/**
|
|
980
881
|
* 加载目录中的工作流定义
|
|
981
882
|
*/
|
|
982
883
|
_loadWorkflows() {
|
|
983
|
-
const dir = path.resolve(process.cwd(), this._workflowsDir)
|
|
884
|
+
const dir = path.resolve(process.cwd(), this._workflowsDir);
|
|
984
885
|
if (!fs.existsSync(dir)) {
|
|
985
|
-
fs.mkdirSync(dir, { recursive: true })
|
|
986
|
-
|
|
987
|
-
return
|
|
886
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
887
|
+
log.info(` Created workflows directory: ${dir}`);
|
|
888
|
+
return;
|
|
988
889
|
}
|
|
989
|
-
|
|
990
890
|
try {
|
|
991
|
-
const files = fs.readdirSync(dir)
|
|
891
|
+
const files = fs.readdirSync(dir);
|
|
992
892
|
for (const file of files) {
|
|
993
|
-
if (!file.endsWith('.json') && !file.endsWith('.js')) continue
|
|
994
|
-
|
|
995
|
-
const
|
|
996
|
-
const workflowName = path.basename(file, path.extname(file))
|
|
997
|
-
|
|
893
|
+
if (!file.endsWith('.json') && !file.endsWith('.js')) continue;
|
|
894
|
+
const filePath = path.join(dir, file);
|
|
895
|
+
const workflowName = path.basename(file, path.extname(file));
|
|
998
896
|
// 跳过已存在的工作流
|
|
999
|
-
if (this._workflows.has(workflowName)) continue
|
|
1000
|
-
|
|
897
|
+
if (this._workflows.has(workflowName)) continue;
|
|
1001
898
|
try {
|
|
1002
|
-
const content = fs.readFileSync(filePath, 'utf-8')
|
|
1003
|
-
let workflowDef
|
|
1004
|
-
|
|
899
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
900
|
+
let workflowDef;
|
|
1005
901
|
if (file.endsWith('.js')) {
|
|
1006
902
|
// 执行 JS 文件获取工作流定义
|
|
1007
|
-
//
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
903
|
+
// 使用沙箱加载工作流文件,防止恶意代码执行
|
|
904
|
+
workflowDef = runWorkflowFileSafely(content, { timeout: 10000 });
|
|
905
|
+
if (typeof workflowDef === 'function') {
|
|
906
|
+
workflowDef = workflowDef(this._engine || {});
|
|
907
|
+
}
|
|
908
|
+
workflowDef = workflowDef.default || workflowDef;
|
|
1012
909
|
} else {
|
|
1013
|
-
workflowDef = JSON.parse(content)
|
|
910
|
+
workflowDef = JSON.parse(content);
|
|
1014
911
|
}
|
|
1015
|
-
|
|
1016
912
|
if (workflowDef && workflowDef.steps) {
|
|
1017
|
-
this._workflows.set(workflowName, workflowDef)
|
|
1018
|
-
//
|
|
913
|
+
this._workflows.set(workflowName, workflowDef);
|
|
914
|
+
//log.info(` Loaded: ${workflowName}`)
|
|
1019
915
|
}
|
|
1020
916
|
} catch (err) {
|
|
1021
|
-
|
|
917
|
+
log.error(` Failed to load ${file}:`, err.message);
|
|
1022
918
|
}
|
|
1023
919
|
}
|
|
1024
920
|
} catch (err) {
|
|
1025
|
-
|
|
921
|
+
log.error(' Failed to read workflows directory:', err.message);
|
|
1026
922
|
}
|
|
1027
923
|
}
|
|
1028
|
-
|
|
1029
924
|
/**
|
|
1030
925
|
* 注册工作流为工具
|
|
1031
926
|
*/
|
|
1032
927
|
_registerWorkflowTools() {
|
|
1033
928
|
for (const [name, workflow] of this._workflows) {
|
|
1034
|
-
if (this._workflowTools.has(name)) continue
|
|
1035
|
-
|
|
1036
|
-
const
|
|
1037
|
-
const description = workflow.description || `执行工作流: ${name}`
|
|
1038
|
-
|
|
929
|
+
if (this._workflowTools.has(name)) continue;
|
|
930
|
+
const toolName = `workflow_${name}`;
|
|
931
|
+
const description = workflow.description || `执行工作流: ${name}`;
|
|
1039
932
|
this._framework.registerTool({
|
|
1040
933
|
name: toolName,
|
|
1041
934
|
description,
|
|
1042
935
|
inputSchema: z.object({
|
|
1043
|
-
input: z.object({}).optional().describe('工作流输入参数')
|
|
936
|
+
input: z.object({}).optional().describe('工作流输入参数'),
|
|
1044
937
|
}),
|
|
1045
|
-
execute: async (args) => {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
938
|
+
execute: async (args, framework) => {
|
|
939
|
+
// Get current sessionId to ensure notifications reach the correct session
|
|
940
|
+
let sessionId = null;
|
|
941
|
+
const ctx = framework?.getExecutionContext?.();
|
|
942
|
+
if (ctx?.sessionId) {
|
|
943
|
+
sessionId = ctx.sessionId;
|
|
944
|
+
}
|
|
945
|
+
return await this.executeWorkflow(workflow, args.input || {}, sessionId);
|
|
946
|
+
},
|
|
947
|
+
});
|
|
948
|
+
this._workflowTools.set(name, toolName);
|
|
949
|
+
log.info(` Registered tool: ${toolName}`);
|
|
1052
950
|
}
|
|
1053
951
|
}
|
|
1054
|
-
|
|
1055
952
|
/**
|
|
1056
953
|
* 重载工作流
|
|
1057
954
|
*/
|
|
1058
955
|
reload(framework) {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
956
|
+
// 清除模块缓存,确保重新加载最新代码
|
|
957
|
+
const modulePath = require.resolve('./src/capabilities/workflow-engine');
|
|
958
|
+
if (require.cache[modulePath]) {
|
|
959
|
+
delete require.cache[modulePath];
|
|
960
|
+
}
|
|
961
|
+
log.info(' Reloading...');
|
|
962
|
+
this._framework = framework;
|
|
1062
963
|
// 重新创建 engine(确保使用最新代码)
|
|
1063
|
-
this._engine = new (require('./src/capabilities/workflow-engine').WorkflowEngine)(framework)
|
|
1064
|
-
|
|
964
|
+
this._engine = new (require('./src/capabilities/workflow-engine').WorkflowEngine)(framework);
|
|
1065
965
|
// 注册内置步骤类型
|
|
1066
|
-
this._engine.registerStepType(
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
this._engine.registerStepType(
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
this._engine.registerStepType(
|
|
1075
|
-
|
|
1076
|
-
|
|
966
|
+
this._engine.registerStepType(
|
|
967
|
+
require('./src/capabilities/workflow-engine').StepType.SCRIPT,
|
|
968
|
+
require('./src/capabilities/workflow-engine').ScriptStep
|
|
969
|
+
);
|
|
970
|
+
this._engine.registerStepType(
|
|
971
|
+
require('./src/capabilities/workflow-engine').StepType.TOOL,
|
|
972
|
+
require('./src/capabilities/workflow-engine').ToolStep
|
|
973
|
+
);
|
|
974
|
+
this._engine.registerStepType(
|
|
975
|
+
require('./src/capabilities/workflow-engine').StepType.CONDITION,
|
|
976
|
+
require('./src/capabilities/workflow-engine').ConditionStep
|
|
977
|
+
);
|
|
978
|
+
this._engine.registerStepType(
|
|
979
|
+
require('./src/capabilities/workflow-engine').StepType.SWITCH,
|
|
980
|
+
require('./src/capabilities/workflow-engine').SwitchStep
|
|
981
|
+
);
|
|
982
|
+
this._engine.registerStepType(
|
|
983
|
+
require('./src/capabilities/workflow-engine').StepType.TRY,
|
|
984
|
+
require('./src/capabilities/workflow-engine').TryStep
|
|
985
|
+
);
|
|
986
|
+
this._engine.registerStepType(
|
|
987
|
+
require('./src/capabilities/workflow-engine').StepType.PARALLEL,
|
|
988
|
+
require('./src/capabilities/workflow-engine').ParallelStep
|
|
989
|
+
);
|
|
990
|
+
this._engine.registerStepType(
|
|
991
|
+
require('./src/capabilities/workflow-engine').StepType.SEQUENTIAL,
|
|
992
|
+
require('./src/capabilities/workflow-engine').SequentialStep
|
|
993
|
+
);
|
|
994
|
+
this._engine.registerStepType(
|
|
995
|
+
require('./src/capabilities/workflow-engine').StepType.LOOP,
|
|
996
|
+
require('./src/capabilities/workflow-engine').LoopStep
|
|
997
|
+
);
|
|
998
|
+
this._engine.registerStepType(
|
|
999
|
+
require('./src/capabilities/workflow-engine').StepType.DELAY,
|
|
1000
|
+
require('./src/capabilities/workflow-engine').DelayStep
|
|
1001
|
+
);
|
|
1002
|
+
this._engine.registerStepType(
|
|
1003
|
+
require('./src/capabilities/workflow-engine').StepType.WORKFLOW,
|
|
1004
|
+
require('./src/capabilities/workflow-engine').NestedWorkflowStep
|
|
1005
|
+
);
|
|
1077
1006
|
// 清除已注册的工具
|
|
1078
1007
|
for (const toolName of this._workflowTools.values()) {
|
|
1079
1008
|
// 工具注销需要框架支持,这里只清理内部状态
|
|
1080
1009
|
}
|
|
1081
|
-
this._workflowTools.clear()
|
|
1082
|
-
this._workflows.clear()
|
|
1083
|
-
|
|
1010
|
+
this._workflowTools.clear();
|
|
1011
|
+
this._workflows.clear();
|
|
1084
1012
|
// 重新加载
|
|
1085
|
-
this._loadWorkflows()
|
|
1086
|
-
this._registerWorkflowTools()
|
|
1087
|
-
|
|
1088
|
-
console.log(`[Workflow] Reloaded. Total workflows: ${this._workflows.size}`)
|
|
1013
|
+
this._loadWorkflows();
|
|
1014
|
+
this._registerWorkflowTools();
|
|
1015
|
+
log.info(` Reloaded. Total workflows: ${this._workflows.size}`);
|
|
1089
1016
|
}
|
|
1090
|
-
|
|
1091
1017
|
/**
|
|
1092
1018
|
* 执行工作流
|
|
1093
1019
|
*/
|
|
1094
1020
|
async executeWorkflow(workflowDef, input = {}, sessionId = null) {
|
|
1095
1021
|
try {
|
|
1096
|
-
let workflow
|
|
1097
|
-
|
|
1022
|
+
let workflow;
|
|
1098
1023
|
if (typeof workflowDef === 'string') {
|
|
1099
1024
|
// 尝试作为工作流名称加载(先检查是否已加载的工作流)
|
|
1100
|
-
const workflowName = workflowDef.trim()
|
|
1025
|
+
const workflowName = workflowDef.trim();
|
|
1101
1026
|
if (this._workflows.has(workflowName)) {
|
|
1102
|
-
workflow = this._workflows.get(workflowName)
|
|
1027
|
+
workflow = this._workflows.get(workflowName);
|
|
1103
1028
|
} else {
|
|
1104
1029
|
// 尝试解析 JSON 或执行代码
|
|
1105
1030
|
try {
|
|
1106
|
-
workflow = JSON.parse(workflowDef)
|
|
1031
|
+
workflow = JSON.parse(workflowDef);
|
|
1107
1032
|
} catch {
|
|
1108
|
-
//
|
|
1109
|
-
|
|
1110
|
-
workflow = fn(this._engine)
|
|
1033
|
+
// 使用沙箱解析工作流定义,防止恶意代码执行
|
|
1034
|
+
workflow = runWorkflowSafely(workflowDef, this._engine, { timeout: 5000 });
|
|
1111
1035
|
}
|
|
1112
1036
|
}
|
|
1113
1037
|
} else {
|
|
1114
|
-
workflow = workflowDef
|
|
1038
|
+
workflow = workflowDef;
|
|
1115
1039
|
}
|
|
1116
|
-
|
|
1117
|
-
const context = this._engine.createContext(input, {})
|
|
1118
|
-
|
|
1040
|
+
const context = this._engine.createContext(input, {});
|
|
1119
1041
|
// 将 sessionId 存储到上下文变量,供工具步骤使用
|
|
1120
1042
|
if (sessionId) {
|
|
1121
|
-
context.variables._sessionId = sessionId
|
|
1043
|
+
context.variables._sessionId = sessionId;
|
|
1122
1044
|
}
|
|
1123
|
-
|
|
1124
1045
|
// 执行工作流步骤
|
|
1125
1046
|
if (workflow.steps && Array.isArray(workflow.steps)) {
|
|
1126
|
-
const results = []
|
|
1047
|
+
const results = [];
|
|
1127
1048
|
// 按 id 存储步骤输出,供 ${id.output} 引用
|
|
1128
|
-
context.variables._stepOutputs = {}
|
|
1049
|
+
context.variables._stepOutputs = {};
|
|
1129
1050
|
for (const stepConfig of workflow.steps) {
|
|
1130
|
-
const step = this._engine.createStep(stepConfig)
|
|
1131
|
-
const result = await step.execute(context, this._engine)
|
|
1132
|
-
results.push(result)
|
|
1051
|
+
const step = this._engine.createStep(stepConfig);
|
|
1052
|
+
const result = await step.execute(context, this._engine);
|
|
1053
|
+
results.push(result);
|
|
1133
1054
|
// 如果步骤有 id,存储其输出
|
|
1134
1055
|
if (stepConfig.id) {
|
|
1135
|
-
context.variables._stepOutputs[stepConfig.id] = result
|
|
1056
|
+
context.variables._stepOutputs[stepConfig.id] = result;
|
|
1136
1057
|
}
|
|
1137
1058
|
}
|
|
1138
|
-
|
|
1139
1059
|
// 只返回最后一步的结果(包含最终输出)和成功状态
|
|
1140
1060
|
// 不返回所有中间结果,避免上下文超限
|
|
1141
|
-
const lastResult = results.length > 0 ? results[results.length - 1] : null
|
|
1142
|
-
|
|
1061
|
+
const lastResult = results.length > 0 ? results[results.length - 1] : null;
|
|
1143
1062
|
// 从 context.variables 中提取非下划线开头的用户变量
|
|
1144
1063
|
// 下划线开头的是内部变量(如 _stepOutputs),不需要返回
|
|
1145
|
-
const userVariables = {}
|
|
1064
|
+
const userVariables = {};
|
|
1146
1065
|
for (const [key, value] of Object.entries(context.variables)) {
|
|
1147
1066
|
if (!key.startsWith('_')) {
|
|
1148
|
-
userVariables[key] = value
|
|
1067
|
+
userVariables[key] = value;
|
|
1149
1068
|
}
|
|
1150
1069
|
}
|
|
1151
|
-
|
|
1152
1070
|
return {
|
|
1153
1071
|
success: true,
|
|
1154
1072
|
stepCount: workflow.steps.length,
|
|
1155
1073
|
result: lastResult,
|
|
1156
|
-
output: userVariables
|
|
1157
|
-
}
|
|
1074
|
+
output: userVariables,
|
|
1075
|
+
};
|
|
1158
1076
|
}
|
|
1159
|
-
|
|
1160
|
-
return { success: true, output: {} }
|
|
1077
|
+
return { success: true, output: {} };
|
|
1161
1078
|
} catch (err) {
|
|
1162
|
-
return { success: false, error: err.message }
|
|
1079
|
+
return { success: false, error: err.message };
|
|
1163
1080
|
}
|
|
1164
1081
|
}
|
|
1165
|
-
|
|
1166
1082
|
/**
|
|
1167
1083
|
* 获取引擎
|
|
1168
1084
|
*/
|
|
1169
1085
|
getEngine() {
|
|
1170
|
-
return this._engine
|
|
1086
|
+
return this._engine;
|
|
1171
1087
|
}
|
|
1172
|
-
|
|
1173
1088
|
uninstall(framework) {
|
|
1174
|
-
this._engine = null
|
|
1175
|
-
this._framework = null
|
|
1089
|
+
this._engine = null;
|
|
1090
|
+
this._framework = null;
|
|
1176
1091
|
}
|
|
1177
1092
|
}
|
|
1178
|
-
|
|
1179
1093
|
module.exports = {
|
|
1180
1094
|
WorkflowPlugin,
|
|
1181
1095
|
WorkflowEngine,
|
|
@@ -1191,5 +1105,5 @@ module.exports = {
|
|
|
1191
1105
|
SequentialStep,
|
|
1192
1106
|
LoopStep,
|
|
1193
1107
|
DelayStep,
|
|
1194
|
-
ToolStep
|
|
1195
|
-
}
|
|
1108
|
+
ToolStep,
|
|
1109
|
+
};
|