foliko 2.0.21 → 2.0.23
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/.editorconfig +56 -56
- package/.lintstagedrc +7 -7
- package/.prettierignore +29 -29
- package/.prettierrc +11 -11
- package/Dockerfile +63 -63
- package/install.ps1 +129 -129
- package/install.sh +121 -121
- package/package.json +3 -5
- package/plugins/core/skill-manager/index.js +34 -23
- package/plugins/core/sub-agent/index.js +103 -14
- package/plugins/core/workflow/context.js +941 -941
- package/plugins/core/workflow/engine.js +66 -66
- package/plugins/core/workflow/js-runner.js +318 -318
- package/plugins/core/workflow/json-runner.js +323 -323
- package/plugins/core/workflow/stages/choice.js +74 -74
- package/plugins/core/workflow/stages/each.js +123 -123
- package/plugins/core/workflow/stages/parallel.js +69 -69
- package/plugins/executors/extension/extension-registry.js +72 -1
- package/plugins/executors/extension/index.js +68 -9
- package/plugins/io/file-system/index.js +377 -153
- package/skills/find-skills/SKILL.md +133 -133
- package/src/agent/chat.js +207 -222
- package/src/agent/sub.js +29 -26
- package/src/agent/tool-loop.js +648 -0
- package/src/llm/provider.js +12 -28
- package/src/plugin/base.js +17 -14
- package/src/plugin/manager.js +19 -0
- package/src/tool/router.js +2 -2
- package/src/utils/message-validator.js +186 -0
- package/tests/core/chat-tool.test.js +187 -0
- package/tests/core/disable-thinking.test.js +64 -0
- package/tests/core/edit-file.test.js +194 -0
- package/tests/core/ext-call-empty-args.test.js +136 -0
- package/tests/core/reasoning-content.test.js +129 -0
- package/tests/core/sanitize-for-llm.test.js +152 -0
- package/tests/core/search.test.js +212 -0
- package/tests/core/skill-input-schema.test.js +150 -0
- package/tests/core/strip-stale-tool-calls.test.js +154 -0
- package/tests/core/sub-agent-parse.test.js +247 -0
- package/tests/core/sub-agent-skills.test.js +197 -0
- package/tests/core/tool-loop.test.js +208 -0
- package/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.jsonl +0 -76
|
@@ -1,318 +1,318 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JS Workflow Runner - Total.js Style (Secondary)
|
|
3
|
-
* Executes workflows defined in JavaScript format using $.push(), $.next(), $.send() API
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { runScriptSafely } = require('../../../src/utils/sandbox');
|
|
7
|
-
|
|
8
|
-
class JsRunner {
|
|
9
|
-
constructor(framework) {
|
|
10
|
-
this.framework = framework;
|
|
11
|
-
this._log = require('../../../src/common/logger').logger.child('JsRunner');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Execute a JS workflow
|
|
16
|
-
* @param {string|Function} workflowCode - Workflow code or exported function
|
|
17
|
-
* @param {Object} input - Input data
|
|
18
|
-
* @param {string} sessionId - Session ID for tool execution
|
|
19
|
-
* @returns {Promise<Object>} execution result
|
|
20
|
-
*/
|
|
21
|
-
async execute(workflowCode, input = {}, sessionId = null) {
|
|
22
|
-
this._log.debug('Executing JS workflow');
|
|
23
|
-
|
|
24
|
-
const { FlowContext } = require('./context');
|
|
25
|
-
const context = new FlowContext(input, sessionId);
|
|
26
|
-
context.setFramework(this.framework);
|
|
27
|
-
|
|
28
|
-
// Create stage executor and attach to context
|
|
29
|
-
const stageExecutor = new JsStageExecutor(this.framework);
|
|
30
|
-
context._stageExecutor = stageExecutor;
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
// Get the workflow function
|
|
34
|
-
let workflowFn;
|
|
35
|
-
if (typeof workflowCode === 'function') {
|
|
36
|
-
workflowFn = workflowCode;
|
|
37
|
-
} else if (typeof workflowCode === 'string') {
|
|
38
|
-
workflowFn = this._parseWorkflow(workflowCode);
|
|
39
|
-
} else {
|
|
40
|
-
throw new Error('Workflow must be a function or string');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Create the $ object for Total.js style API
|
|
44
|
-
const $ = this._createFlowApi(context, stageExecutor);
|
|
45
|
-
|
|
46
|
-
// Execute the workflow function
|
|
47
|
-
const flowPromise = workflowFn($);
|
|
48
|
-
|
|
49
|
-
// If it returns a promise, wait for it
|
|
50
|
-
if (flowPromise && typeof flowPromise.then === 'function') {
|
|
51
|
-
await flowPromise;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Execute stages until destroyed or no more pending next
|
|
55
|
-
await this._runStages(context, stageExecutor);
|
|
56
|
-
|
|
57
|
-
// Build output
|
|
58
|
-
const output = {};
|
|
59
|
-
for (const [key, value] of context._results) {
|
|
60
|
-
output[key] = value;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
success: !context._destroyed,
|
|
65
|
-
output,
|
|
66
|
-
result: context._sentData || output,
|
|
67
|
-
};
|
|
68
|
-
} catch (err) {
|
|
69
|
-
this._log.error(`JS Workflow execution error: ${err.message}`);
|
|
70
|
-
return {
|
|
71
|
-
success: false,
|
|
72
|
-
error: err.message,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Parse workflow code string into a function
|
|
79
|
-
* Supports:
|
|
80
|
-
* FLOW(function($) { ... }) - Direct function
|
|
81
|
-
* module.exports = function($) { ... } - CommonJS export
|
|
82
|
-
* module.exports.default = function($) { ... } - Default export
|
|
83
|
-
*/
|
|
84
|
-
_parseWorkflow(code) {
|
|
85
|
-
const sandboxContext = {
|
|
86
|
-
console: { log() {}, error() {}, warn() {}, info() {} },
|
|
87
|
-
Math,
|
|
88
|
-
JSON,
|
|
89
|
-
Promise,
|
|
90
|
-
Array,
|
|
91
|
-
Object,
|
|
92
|
-
Date,
|
|
93
|
-
RegExp,
|
|
94
|
-
Error,
|
|
95
|
-
Symbol,
|
|
96
|
-
Map,
|
|
97
|
-
Set,
|
|
98
|
-
module: { exports: {} },
|
|
99
|
-
exports: {},
|
|
100
|
-
FlowContext: require('./context').FlowContext,
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
const ctx = require('vm').createContext(sandboxContext);
|
|
105
|
-
|
|
106
|
-
// Check which format the code uses
|
|
107
|
-
const isModuleExport = code.includes('module.exports');
|
|
108
|
-
let fn;
|
|
109
|
-
|
|
110
|
-
if (isModuleExport) {
|
|
111
|
-
// CommonJS export format: module.exports = function($) { ... }
|
|
112
|
-
const wrapped = `
|
|
113
|
-
(function(module, exports, FlowContext) {
|
|
114
|
-
${code}
|
|
115
|
-
return module.exports;
|
|
116
|
-
})
|
|
117
|
-
`;
|
|
118
|
-
const factory = require('vm').runInContext(wrapped, ctx, { timeout: 5000 });
|
|
119
|
-
fn = factory(ctx.module, ctx.exports, require('./context').FlowContext);
|
|
120
|
-
} else if (code.includes('FLOW(')) {
|
|
121
|
-
// FLOW wrapper format: FLOW(function($) { ... })
|
|
122
|
-
const wrapped = `(function(FlowContext) { return (${code}) })`;
|
|
123
|
-
fn = require('vm').runInContext(wrapped, ctx, { timeout: 5000 });
|
|
124
|
-
fn = fn(require('./context').FlowContext);
|
|
125
|
-
} else {
|
|
126
|
-
// Assume it's a function directly
|
|
127
|
-
const wrapped = `(function(FlowContext) { return (${code}) })`;
|
|
128
|
-
fn = require('vm').runInContext(wrapped, ctx, { timeout: 5000 });
|
|
129
|
-
fn = fn(require('./context').FlowContext);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Handle module.exports.default
|
|
133
|
-
if (fn && fn.default) {
|
|
134
|
-
fn = fn.default;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return fn;
|
|
138
|
-
} catch (err) {
|
|
139
|
-
throw new Error(`Failed to parse workflow: ${err.message}`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Create the $ API object for Total.js style
|
|
145
|
-
*/
|
|
146
|
-
_createFlowApi(context, stageExecutor) {
|
|
147
|
-
const $ = {
|
|
148
|
-
// Register a stage
|
|
149
|
-
push: (name, fn) => {
|
|
150
|
-
context.push(name, fn);
|
|
151
|
-
return $;
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
// Go to next stage (sync) - 支持直接传参
|
|
155
|
-
next: (name, ...args) => {
|
|
156
|
-
context.next(name, ...args);
|
|
157
|
-
return $;
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
// Go to next stage (async helper)
|
|
161
|
-
next2: (name, ...args) => {
|
|
162
|
-
return () => {
|
|
163
|
-
context.next(name, ...args);
|
|
164
|
-
};
|
|
165
|
-
},
|
|
166
|
-
|
|
167
|
-
// Send data to next stage
|
|
168
|
-
send: (data) => {
|
|
169
|
-
context.send(data);
|
|
170
|
-
return $;
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
// Input from previous stage (返回 sentData �?workflow input)
|
|
174
|
-
get input() {
|
|
175
|
-
return context._sentData !== undefined ? context._sentData : context.input;
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
// Flow-level persistent data
|
|
179
|
-
data: context.data,
|
|
180
|
-
|
|
181
|
-
// Built-in delay
|
|
182
|
-
wait: (ms) => context.wait(ms),
|
|
183
|
-
|
|
184
|
-
// $.tool - Tool accessor with list(), execute(), register(), get()
|
|
185
|
-
get tool() { return context.tool; },
|
|
186
|
-
|
|
187
|
-
// $.extension - Extension accessor with list(), register(), remove(), execute(), get()
|
|
188
|
-
get extension() { return context.extension; },
|
|
189
|
-
|
|
190
|
-
// $.workflow - Workflow accessor with list(), execute(), get()
|
|
191
|
-
get workflow() { return context.workflow; },
|
|
192
|
-
|
|
193
|
-
// $.agent - Agent accessor with create(), chat(), list()
|
|
194
|
-
get agent() { return context.agent; },
|
|
195
|
-
|
|
196
|
-
// $.prompt - Prompt accessor with send(), chat()
|
|
197
|
-
get prompt() { return context.prompt; },
|
|
198
|
-
|
|
199
|
-
// $.skill - Skill accessor with list(), execute(), get()
|
|
200
|
-
get skill() { return context.skill; },
|
|
201
|
-
|
|
202
|
-
// Execute script
|
|
203
|
-
script: (code) => context.script(code),
|
|
204
|
-
|
|
205
|
-
// End workflow
|
|
206
|
-
destroy: () => context.destroy(),
|
|
207
|
-
|
|
208
|
-
// Current stage info
|
|
209
|
-
get current() { return context.current; },
|
|
210
|
-
get prev() { return context.prev; },
|
|
211
|
-
|
|
212
|
-
// Stage registry
|
|
213
|
-
_stages: context._stages,
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
return $;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Run stages sequentially based on $.next() calls
|
|
221
|
-
*/
|
|
222
|
-
async _runStages(context, stageExecutor) {
|
|
223
|
-
// Create $ API object for this run
|
|
224
|
-
const $ = this._createFlowApi(context, stageExecutor);
|
|
225
|
-
|
|
226
|
-
while (!context._destroyed && context._pendingNext) {
|
|
227
|
-
const nextName = context._pendingNext;
|
|
228
|
-
|
|
229
|
-
// Get args that were passed via $.next(name, ...args)
|
|
230
|
-
const args = context._sentData;
|
|
231
|
-
context._sentData = null;
|
|
232
|
-
context._pendingNext = null;
|
|
233
|
-
|
|
234
|
-
const fn = context._stages?.get(nextName);
|
|
235
|
-
if (!fn) {
|
|
236
|
-
this._log.warn(`Stage "${nextName}" not found`);
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
context.prev = context.current;
|
|
241
|
-
context.current = nextName;
|
|
242
|
-
|
|
243
|
-
this._log.debug(`Executing JS stage: ${nextName}`);
|
|
244
|
-
|
|
245
|
-
try {
|
|
246
|
-
// Call fn with $ as first arg, then unpack args
|
|
247
|
-
// Supports: fn($) or fn($, a, b, c)
|
|
248
|
-
const fnArgs = args !== undefined
|
|
249
|
-
? (Array.isArray(args) ? [$, ...args] : [$, args])
|
|
250
|
-
: [$];
|
|
251
|
-
|
|
252
|
-
const result = fn(...fnArgs);
|
|
253
|
-
if (result && typeof result.then === 'function') {
|
|
254
|
-
await result;
|
|
255
|
-
}
|
|
256
|
-
} catch (err) {
|
|
257
|
-
this._log.error(`Stage "${nextName}" error: ${err.message}`);
|
|
258
|
-
context.destroy();
|
|
259
|
-
throw err;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* JS Stage Executor - handles stage execution in JS workflows
|
|
267
|
-
*/
|
|
268
|
-
class JsStageExecutor {
|
|
269
|
-
constructor(framework) {
|
|
270
|
-
this.framework = framework;
|
|
271
|
-
this._log = require('../../../src/common/logger').logger.child('JsStageExecutor');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Execute a stage from JSON config within a JS workflow
|
|
276
|
-
*/
|
|
277
|
-
async executeStage(config, context) {
|
|
278
|
-
const { ActionStage } = require('./stages/action');
|
|
279
|
-
const { ChoiceStage } = require('./stages/choice');
|
|
280
|
-
const { EachStage } = require('./stages/each');
|
|
281
|
-
const { ParallelStage } = require('./stages/parallel');
|
|
282
|
-
const type = this._getStageType(config);
|
|
283
|
-
|
|
284
|
-
switch (type) {
|
|
285
|
-
case 'action': {
|
|
286
|
-
const stage = new ActionStage(config);
|
|
287
|
-
return await stage.execute(context);
|
|
288
|
-
}
|
|
289
|
-
case 'choice': {
|
|
290
|
-
const stage = new ChoiceStage(config);
|
|
291
|
-
const nextStage = await stage.execute(context);
|
|
292
|
-
if (nextStage) {
|
|
293
|
-
context._pendingNext = nextStage;
|
|
294
|
-
}
|
|
295
|
-
return { nextStage };
|
|
296
|
-
}
|
|
297
|
-
case 'each': {
|
|
298
|
-
const stage = new EachStage(config);
|
|
299
|
-
return await stage.execute(context);
|
|
300
|
-
}
|
|
301
|
-
case 'parallel': {
|
|
302
|
-
const stage = new ParallelStage(config);
|
|
303
|
-
return await stage.execute(context);
|
|
304
|
-
}
|
|
305
|
-
default:
|
|
306
|
-
throw new Error(`Unknown stage type: ${type}`);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
_getStageType(config) {
|
|
311
|
-
if (config.choice || config.when || config.default) return 'choice';
|
|
312
|
-
if (config.each || config.over) return 'each';
|
|
313
|
-
if (config.parallel) return 'parallel';
|
|
314
|
-
return 'action';
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
module.exports = { JsRunner };
|
|
1
|
+
/**
|
|
2
|
+
* JS Workflow Runner - Total.js Style (Secondary)
|
|
3
|
+
* Executes workflows defined in JavaScript format using $.push(), $.next(), $.send() API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { runScriptSafely } = require('../../../src/utils/sandbox');
|
|
7
|
+
|
|
8
|
+
class JsRunner {
|
|
9
|
+
constructor(framework) {
|
|
10
|
+
this.framework = framework;
|
|
11
|
+
this._log = require('../../../src/common/logger').logger.child('JsRunner');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Execute a JS workflow
|
|
16
|
+
* @param {string|Function} workflowCode - Workflow code or exported function
|
|
17
|
+
* @param {Object} input - Input data
|
|
18
|
+
* @param {string} sessionId - Session ID for tool execution
|
|
19
|
+
* @returns {Promise<Object>} execution result
|
|
20
|
+
*/
|
|
21
|
+
async execute(workflowCode, input = {}, sessionId = null) {
|
|
22
|
+
this._log.debug('Executing JS workflow');
|
|
23
|
+
|
|
24
|
+
const { FlowContext } = require('./context');
|
|
25
|
+
const context = new FlowContext(input, sessionId);
|
|
26
|
+
context.setFramework(this.framework);
|
|
27
|
+
|
|
28
|
+
// Create stage executor and attach to context
|
|
29
|
+
const stageExecutor = new JsStageExecutor(this.framework);
|
|
30
|
+
context._stageExecutor = stageExecutor;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Get the workflow function
|
|
34
|
+
let workflowFn;
|
|
35
|
+
if (typeof workflowCode === 'function') {
|
|
36
|
+
workflowFn = workflowCode;
|
|
37
|
+
} else if (typeof workflowCode === 'string') {
|
|
38
|
+
workflowFn = this._parseWorkflow(workflowCode);
|
|
39
|
+
} else {
|
|
40
|
+
throw new Error('Workflow must be a function or string');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Create the $ object for Total.js style API
|
|
44
|
+
const $ = this._createFlowApi(context, stageExecutor);
|
|
45
|
+
|
|
46
|
+
// Execute the workflow function
|
|
47
|
+
const flowPromise = workflowFn($);
|
|
48
|
+
|
|
49
|
+
// If it returns a promise, wait for it
|
|
50
|
+
if (flowPromise && typeof flowPromise.then === 'function') {
|
|
51
|
+
await flowPromise;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Execute stages until destroyed or no more pending next
|
|
55
|
+
await this._runStages(context, stageExecutor);
|
|
56
|
+
|
|
57
|
+
// Build output
|
|
58
|
+
const output = {};
|
|
59
|
+
for (const [key, value] of context._results) {
|
|
60
|
+
output[key] = value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
success: !context._destroyed,
|
|
65
|
+
output,
|
|
66
|
+
result: context._sentData || output,
|
|
67
|
+
};
|
|
68
|
+
} catch (err) {
|
|
69
|
+
this._log.error(`JS Workflow execution error: ${err.message}`);
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: err.message,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Parse workflow code string into a function
|
|
79
|
+
* Supports:
|
|
80
|
+
* FLOW(function($) { ... }) - Direct function
|
|
81
|
+
* module.exports = function($) { ... } - CommonJS export
|
|
82
|
+
* module.exports.default = function($) { ... } - Default export
|
|
83
|
+
*/
|
|
84
|
+
_parseWorkflow(code) {
|
|
85
|
+
const sandboxContext = {
|
|
86
|
+
console: { log() {}, error() {}, warn() {}, info() {} },
|
|
87
|
+
Math,
|
|
88
|
+
JSON,
|
|
89
|
+
Promise,
|
|
90
|
+
Array,
|
|
91
|
+
Object,
|
|
92
|
+
Date,
|
|
93
|
+
RegExp,
|
|
94
|
+
Error,
|
|
95
|
+
Symbol,
|
|
96
|
+
Map,
|
|
97
|
+
Set,
|
|
98
|
+
module: { exports: {} },
|
|
99
|
+
exports: {},
|
|
100
|
+
FlowContext: require('./context').FlowContext,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const ctx = require('vm').createContext(sandboxContext);
|
|
105
|
+
|
|
106
|
+
// Check which format the code uses
|
|
107
|
+
const isModuleExport = code.includes('module.exports');
|
|
108
|
+
let fn;
|
|
109
|
+
|
|
110
|
+
if (isModuleExport) {
|
|
111
|
+
// CommonJS export format: module.exports = function($) { ... }
|
|
112
|
+
const wrapped = `
|
|
113
|
+
(function(module, exports, FlowContext) {
|
|
114
|
+
${code}
|
|
115
|
+
return module.exports;
|
|
116
|
+
})
|
|
117
|
+
`;
|
|
118
|
+
const factory = require('vm').runInContext(wrapped, ctx, { timeout: 5000 });
|
|
119
|
+
fn = factory(ctx.module, ctx.exports, require('./context').FlowContext);
|
|
120
|
+
} else if (code.includes('FLOW(')) {
|
|
121
|
+
// FLOW wrapper format: FLOW(function($) { ... })
|
|
122
|
+
const wrapped = `(function(FlowContext) { return (${code}) })`;
|
|
123
|
+
fn = require('vm').runInContext(wrapped, ctx, { timeout: 5000 });
|
|
124
|
+
fn = fn(require('./context').FlowContext);
|
|
125
|
+
} else {
|
|
126
|
+
// Assume it's a function directly
|
|
127
|
+
const wrapped = `(function(FlowContext) { return (${code}) })`;
|
|
128
|
+
fn = require('vm').runInContext(wrapped, ctx, { timeout: 5000 });
|
|
129
|
+
fn = fn(require('./context').FlowContext);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Handle module.exports.default
|
|
133
|
+
if (fn && fn.default) {
|
|
134
|
+
fn = fn.default;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return fn;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
throw new Error(`Failed to parse workflow: ${err.message}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Create the $ API object for Total.js style
|
|
145
|
+
*/
|
|
146
|
+
_createFlowApi(context, stageExecutor) {
|
|
147
|
+
const $ = {
|
|
148
|
+
// Register a stage
|
|
149
|
+
push: (name, fn) => {
|
|
150
|
+
context.push(name, fn);
|
|
151
|
+
return $;
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Go to next stage (sync) - 支持直接传参
|
|
155
|
+
next: (name, ...args) => {
|
|
156
|
+
context.next(name, ...args);
|
|
157
|
+
return $;
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// Go to next stage (async helper)
|
|
161
|
+
next2: (name, ...args) => {
|
|
162
|
+
return () => {
|
|
163
|
+
context.next(name, ...args);
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
// Send data to next stage
|
|
168
|
+
send: (data) => {
|
|
169
|
+
context.send(data);
|
|
170
|
+
return $;
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
// Input from previous stage (返回 sentData �?workflow input)
|
|
174
|
+
get input() {
|
|
175
|
+
return context._sentData !== undefined ? context._sentData : context.input;
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
// Flow-level persistent data
|
|
179
|
+
data: context.data,
|
|
180
|
+
|
|
181
|
+
// Built-in delay
|
|
182
|
+
wait: (ms) => context.wait(ms),
|
|
183
|
+
|
|
184
|
+
// $.tool - Tool accessor with list(), execute(), register(), get()
|
|
185
|
+
get tool() { return context.tool; },
|
|
186
|
+
|
|
187
|
+
// $.extension - Extension accessor with list(), register(), remove(), execute(), get()
|
|
188
|
+
get extension() { return context.extension; },
|
|
189
|
+
|
|
190
|
+
// $.workflow - Workflow accessor with list(), execute(), get()
|
|
191
|
+
get workflow() { return context.workflow; },
|
|
192
|
+
|
|
193
|
+
// $.agent - Agent accessor with create(), chat(), list()
|
|
194
|
+
get agent() { return context.agent; },
|
|
195
|
+
|
|
196
|
+
// $.prompt - Prompt accessor with send(), chat()
|
|
197
|
+
get prompt() { return context.prompt; },
|
|
198
|
+
|
|
199
|
+
// $.skill - Skill accessor with list(), execute(), get()
|
|
200
|
+
get skill() { return context.skill; },
|
|
201
|
+
|
|
202
|
+
// Execute script
|
|
203
|
+
script: (code) => context.script(code),
|
|
204
|
+
|
|
205
|
+
// End workflow
|
|
206
|
+
destroy: () => context.destroy(),
|
|
207
|
+
|
|
208
|
+
// Current stage info
|
|
209
|
+
get current() { return context.current; },
|
|
210
|
+
get prev() { return context.prev; },
|
|
211
|
+
|
|
212
|
+
// Stage registry
|
|
213
|
+
_stages: context._stages,
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
return $;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Run stages sequentially based on $.next() calls
|
|
221
|
+
*/
|
|
222
|
+
async _runStages(context, stageExecutor) {
|
|
223
|
+
// Create $ API object for this run
|
|
224
|
+
const $ = this._createFlowApi(context, stageExecutor);
|
|
225
|
+
|
|
226
|
+
while (!context._destroyed && context._pendingNext) {
|
|
227
|
+
const nextName = context._pendingNext;
|
|
228
|
+
|
|
229
|
+
// Get args that were passed via $.next(name, ...args)
|
|
230
|
+
const args = context._sentData;
|
|
231
|
+
context._sentData = null;
|
|
232
|
+
context._pendingNext = null;
|
|
233
|
+
|
|
234
|
+
const fn = context._stages?.get(nextName);
|
|
235
|
+
if (!fn) {
|
|
236
|
+
this._log.warn(`Stage "${nextName}" not found`);
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
context.prev = context.current;
|
|
241
|
+
context.current = nextName;
|
|
242
|
+
|
|
243
|
+
this._log.debug(`Executing JS stage: ${nextName}`);
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
// Call fn with $ as first arg, then unpack args
|
|
247
|
+
// Supports: fn($) or fn($, a, b, c)
|
|
248
|
+
const fnArgs = args !== undefined
|
|
249
|
+
? (Array.isArray(args) ? [$, ...args] : [$, args])
|
|
250
|
+
: [$];
|
|
251
|
+
|
|
252
|
+
const result = fn(...fnArgs);
|
|
253
|
+
if (result && typeof result.then === 'function') {
|
|
254
|
+
await result;
|
|
255
|
+
}
|
|
256
|
+
} catch (err) {
|
|
257
|
+
this._log.error(`Stage "${nextName}" error: ${err.message}`);
|
|
258
|
+
context.destroy();
|
|
259
|
+
throw err;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* JS Stage Executor - handles stage execution in JS workflows
|
|
267
|
+
*/
|
|
268
|
+
class JsStageExecutor {
|
|
269
|
+
constructor(framework) {
|
|
270
|
+
this.framework = framework;
|
|
271
|
+
this._log = require('../../../src/common/logger').logger.child('JsStageExecutor');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Execute a stage from JSON config within a JS workflow
|
|
276
|
+
*/
|
|
277
|
+
async executeStage(config, context) {
|
|
278
|
+
const { ActionStage } = require('./stages/action');
|
|
279
|
+
const { ChoiceStage } = require('./stages/choice');
|
|
280
|
+
const { EachStage } = require('./stages/each');
|
|
281
|
+
const { ParallelStage } = require('./stages/parallel');
|
|
282
|
+
const type = this._getStageType(config);
|
|
283
|
+
|
|
284
|
+
switch (type) {
|
|
285
|
+
case 'action': {
|
|
286
|
+
const stage = new ActionStage(config);
|
|
287
|
+
return await stage.execute(context);
|
|
288
|
+
}
|
|
289
|
+
case 'choice': {
|
|
290
|
+
const stage = new ChoiceStage(config);
|
|
291
|
+
const nextStage = await stage.execute(context);
|
|
292
|
+
if (nextStage) {
|
|
293
|
+
context._pendingNext = nextStage;
|
|
294
|
+
}
|
|
295
|
+
return { nextStage };
|
|
296
|
+
}
|
|
297
|
+
case 'each': {
|
|
298
|
+
const stage = new EachStage(config);
|
|
299
|
+
return await stage.execute(context);
|
|
300
|
+
}
|
|
301
|
+
case 'parallel': {
|
|
302
|
+
const stage = new ParallelStage(config);
|
|
303
|
+
return await stage.execute(context);
|
|
304
|
+
}
|
|
305
|
+
default:
|
|
306
|
+
throw new Error(`Unknown stage type: ${type}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_getStageType(config) {
|
|
311
|
+
if (config.choice || config.when || config.default) return 'choice';
|
|
312
|
+
if (config.each || config.over) return 'each';
|
|
313
|
+
if (config.parallel) return 'parallel';
|
|
314
|
+
return 'action';
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
module.exports = { JsRunner };
|