foliko 2.0.17 → 2.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +1 -3
- 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 +1 -1
- package/plugins/ambient/ExplorerLoop.js +10 -10
- package/plugins/core/scheduler/index.js +1 -1
- package/plugins/core/session/index.js +1 -0
- 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/messaging/feishu/index.js +5 -5
- package/plugins/messaging/qq/index.js +3 -3
- package/plugins/messaging/telegram/index.js +3 -3
- package/plugins/messaging/weixin/index.js +5 -5
- package/skills/find-skills/SKILL.md +133 -133
- package/src/agent/chat.js +124 -15
- package/src/session/session.js +2 -1
- package/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.jsonl +36 -0
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* choice stage executor
|
|
3
|
-
* Conditional branching (if/else/switch)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { evaluateInSandbox } = require('../../../../src/utils/sandbox');
|
|
7
|
-
|
|
8
|
-
class ChoiceStage {
|
|
9
|
-
constructor(config) {
|
|
10
|
-
this.name = config.name;
|
|
11
|
-
this.choice = config.choice || []; // Array of { when, do } or { case, do }
|
|
12
|
-
this.default = config.default; // Default stage name
|
|
13
|
-
this._log = require('../../../../src/common/logger').logger.child('ChoiceStage');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Execute the choice stage
|
|
18
|
-
* @param {FlowContext} context
|
|
19
|
-
* @returns {Promise<string|null>} next stage name
|
|
20
|
-
*/
|
|
21
|
-
async execute(context) {
|
|
22
|
-
this._log.debug(`Evaluating choice: ${this.name}`);
|
|
23
|
-
|
|
24
|
-
// Try each when/case condition
|
|
25
|
-
for (const branch of this.choice) {
|
|
26
|
-
const condition = branch.when || branch.case;
|
|
27
|
-
if (!condition) continue;
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
// Resolve condition with variable interpolation
|
|
31
|
-
const resolvedCondition = context.resolveValue(condition);
|
|
32
|
-
|
|
33
|
-
// Evaluate condition
|
|
34
|
-
let matched = false;
|
|
35
|
-
if (typeof resolvedCondition === 'boolean') {
|
|
36
|
-
matched = resolvedCondition;
|
|
37
|
-
} else if (typeof resolvedCondition === 'string') {
|
|
38
|
-
// Use sandbox to evaluate the condition expression
|
|
39
|
-
// Extract stage results to top level so they can be accessed directly
|
|
40
|
-
// e.g., "init.value > 100" can access init from stage results
|
|
41
|
-
// Also spread context.data so each-loop variables (e.g. "fruit") work
|
|
42
|
-
// in choice conditions.
|
|
43
|
-
const stageResults = Object.fromEntries(context._results);
|
|
44
|
-
const evalContext = {
|
|
45
|
-
...stageResults, // Spread stage results to top level
|
|
46
|
-
...context.data, // Spread data so loop variables are reachable
|
|
47
|
-
input: context.input,
|
|
48
|
-
data: context.data,
|
|
49
|
-
previousResult: context._sentData,
|
|
50
|
-
};
|
|
51
|
-
matched = evaluateInSandbox(resolvedCondition, evalContext, { timeout: 5000 });
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (matched) {
|
|
55
|
-
this._log.debug(`Choice matched: ${condition} -> ${branch.do}`);
|
|
56
|
-
return branch.do; // Return next stage name
|
|
57
|
-
}
|
|
58
|
-
} catch (err) {
|
|
59
|
-
this._log.warn(`Condition evaluation error for "${condition}": ${err.message}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// No condition matched, use default
|
|
64
|
-
if (this.default) {
|
|
65
|
-
this._log.debug(`No choice matched, using default: ${this.default}`);
|
|
66
|
-
return this.default;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
this._log.debug(`No choice matched and no default for: ${this.name}`);
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
module.exports = { ChoiceStage };
|
|
1
|
+
/**
|
|
2
|
+
* choice stage executor
|
|
3
|
+
* Conditional branching (if/else/switch)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { evaluateInSandbox } = require('../../../../src/utils/sandbox');
|
|
7
|
+
|
|
8
|
+
class ChoiceStage {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.name = config.name;
|
|
11
|
+
this.choice = config.choice || []; // Array of { when, do } or { case, do }
|
|
12
|
+
this.default = config.default; // Default stage name
|
|
13
|
+
this._log = require('../../../../src/common/logger').logger.child('ChoiceStage');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Execute the choice stage
|
|
18
|
+
* @param {FlowContext} context
|
|
19
|
+
* @returns {Promise<string|null>} next stage name
|
|
20
|
+
*/
|
|
21
|
+
async execute(context) {
|
|
22
|
+
this._log.debug(`Evaluating choice: ${this.name}`);
|
|
23
|
+
|
|
24
|
+
// Try each when/case condition
|
|
25
|
+
for (const branch of this.choice) {
|
|
26
|
+
const condition = branch.when || branch.case;
|
|
27
|
+
if (!condition) continue;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// Resolve condition with variable interpolation
|
|
31
|
+
const resolvedCondition = context.resolveValue(condition);
|
|
32
|
+
|
|
33
|
+
// Evaluate condition
|
|
34
|
+
let matched = false;
|
|
35
|
+
if (typeof resolvedCondition === 'boolean') {
|
|
36
|
+
matched = resolvedCondition;
|
|
37
|
+
} else if (typeof resolvedCondition === 'string') {
|
|
38
|
+
// Use sandbox to evaluate the condition expression
|
|
39
|
+
// Extract stage results to top level so they can be accessed directly
|
|
40
|
+
// e.g., "init.value > 100" can access init from stage results
|
|
41
|
+
// Also spread context.data so each-loop variables (e.g. "fruit") work
|
|
42
|
+
// in choice conditions.
|
|
43
|
+
const stageResults = Object.fromEntries(context._results);
|
|
44
|
+
const evalContext = {
|
|
45
|
+
...stageResults, // Spread stage results to top level
|
|
46
|
+
...context.data, // Spread data so loop variables are reachable
|
|
47
|
+
input: context.input,
|
|
48
|
+
data: context.data,
|
|
49
|
+
previousResult: context._sentData,
|
|
50
|
+
};
|
|
51
|
+
matched = evaluateInSandbox(resolvedCondition, evalContext, { timeout: 5000 });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (matched) {
|
|
55
|
+
this._log.debug(`Choice matched: ${condition} -> ${branch.do}`);
|
|
56
|
+
return branch.do; // Return next stage name
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
this._log.warn(`Condition evaluation error for "${condition}": ${err.message}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// No condition matched, use default
|
|
64
|
+
if (this.default) {
|
|
65
|
+
this._log.debug(`No choice matched, using default: ${this.default}`);
|
|
66
|
+
return this.default;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this._log.debug(`No choice matched and no default for: ${this.name}`);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = { ChoiceStage };
|
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* each stage executor
|
|
3
|
-
* Loop over items
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
class EachStage {
|
|
7
|
-
constructor(config) {
|
|
8
|
-
this.name = config.name;
|
|
9
|
-
this.each = config.each; // Array to iterate over
|
|
10
|
-
this.as = config.as || 'item'; // Item variable name
|
|
11
|
-
this.index = config.index || 'index'; // Index variable name
|
|
12
|
-
this.stages = config.stages || []; // Child stages to execute for each item
|
|
13
|
-
this.maxIterations = config.maxIterations || 100;
|
|
14
|
-
this.until = config.until; // Early exit condition
|
|
15
|
-
this._log = require('../../../../src/common/logger').logger.child('EachStage');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Execute the each stage
|
|
20
|
-
* @param {FlowContext} context
|
|
21
|
-
* @returns {Promise<Array>} results for each iteration
|
|
22
|
-
*/
|
|
23
|
-
async execute(context) {
|
|
24
|
-
this._log.debug(`Executing each loop: ${this.name}`);
|
|
25
|
-
|
|
26
|
-
// Resolve the array to iterate
|
|
27
|
-
let array;
|
|
28
|
-
if (typeof this.each === 'string') {
|
|
29
|
-
// Strip optional surrounding {{ }} so users can write either
|
|
30
|
-
// "each": "items" (bare identifier)
|
|
31
|
-
// "each": "{{items}}" (templated)
|
|
32
|
-
// "each": "{{init.items}}" (stage.field)
|
|
33
|
-
let path = this.each.trim();
|
|
34
|
-
const m = path.match(/^\{\{([^}]+)\}\}$/);
|
|
35
|
-
if (m) path = m[1].trim();
|
|
36
|
-
|
|
37
|
-
// Use the low-level _resolvePath so we get the actual array/object back
|
|
38
|
-
// (String.prototype.replace in resolveValue would coerce non-strings.)
|
|
39
|
-
let resolved = context._resolvePath(path);
|
|
40
|
-
if (resolved === undefined && path.startsWith('[')) {
|
|
41
|
-
// Support inline array literal: each: "['a','b']"
|
|
42
|
-
try { resolved = JSON.parse(path); } catch {}
|
|
43
|
-
}
|
|
44
|
-
if (Array.isArray(resolved)) {
|
|
45
|
-
array = resolved;
|
|
46
|
-
} else if (resolved && typeof resolved === 'object') {
|
|
47
|
-
// Convert object to entries (so {{k, v}} style iteration works)
|
|
48
|
-
array = Object.entries(resolved);
|
|
49
|
-
} else {
|
|
50
|
-
throw new Error(`each "${this.each}" resolved to non-iterable: ${typeof resolved}`);
|
|
51
|
-
}
|
|
52
|
-
} else if (Array.isArray(this.each)) {
|
|
53
|
-
array = this.each;
|
|
54
|
-
} else {
|
|
55
|
-
throw new Error(`each stage "${this.name}" requires an array to iterate`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const results = [];
|
|
59
|
-
const maxIter = Math.min(this.maxIterations, array.length);
|
|
60
|
-
|
|
61
|
-
for (let i = 0; i < maxIter; i++) {
|
|
62
|
-
// Set loop variables
|
|
63
|
-
context.data[this.as] = array[i];
|
|
64
|
-
context.data[this.index] = i;
|
|
65
|
-
context.data.loopIndex = i; // Legacy alias
|
|
66
|
-
|
|
67
|
-
this._log.debug(`Each iteration ${i}: ${this.as}=`, array[i]);
|
|
68
|
-
|
|
69
|
-
// Execute child stages sequentially
|
|
70
|
-
let stageResults = [];
|
|
71
|
-
for (const stageConfig of this.stages) {
|
|
72
|
-
const stageExecutor = context._stageExecutor;
|
|
73
|
-
if (stageExecutor) {
|
|
74
|
-
const result = await stageExecutor.executeStage(stageConfig, context);
|
|
75
|
-
stageResults.push(result);
|
|
76
|
-
|
|
77
|
-
// Check for early exit
|
|
78
|
-
if (context._destroyed) {
|
|
79
|
-
this._log.debug('Flow destroyed, stopping each loop');
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
results.push(stageResults);
|
|
85
|
-
|
|
86
|
-
// Check until condition
|
|
87
|
-
if (this.until) {
|
|
88
|
-
try {
|
|
89
|
-
const { evaluateInSandbox } = require('../../../../../src/utils/sandbox');
|
|
90
|
-
const evalContext = {
|
|
91
|
-
input: context.input,
|
|
92
|
-
data: context.data,
|
|
93
|
-
variables: Object.fromEntries(context._results),
|
|
94
|
-
previousResult: context._sentData,
|
|
95
|
-
item: array[i],
|
|
96
|
-
index: i,
|
|
97
|
-
};
|
|
98
|
-
const shouldStop = evaluateInSandbox(this.until, evalContext, { timeout: 5000 });
|
|
99
|
-
if (shouldStop) {
|
|
100
|
-
this._log.debug(`Each loop terminated at iteration ${i} by until condition`);
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
} catch (err) {
|
|
104
|
-
this._log.warn(`until condition error: ${err.message}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Cleanup loop variables
|
|
110
|
-
delete context.data[this.as];
|
|
111
|
-
delete context.data[this.index];
|
|
112
|
-
delete context.data.loopIndex;
|
|
113
|
-
|
|
114
|
-
// Store results by name if this stage has a name
|
|
115
|
-
if (this.name) {
|
|
116
|
-
context.setResult(this.name, results);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return results;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
module.exports = { EachStage };
|
|
1
|
+
/**
|
|
2
|
+
* each stage executor
|
|
3
|
+
* Loop over items
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class EachStage {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.name = config.name;
|
|
9
|
+
this.each = config.each; // Array to iterate over
|
|
10
|
+
this.as = config.as || 'item'; // Item variable name
|
|
11
|
+
this.index = config.index || 'index'; // Index variable name
|
|
12
|
+
this.stages = config.stages || []; // Child stages to execute for each item
|
|
13
|
+
this.maxIterations = config.maxIterations || 100;
|
|
14
|
+
this.until = config.until; // Early exit condition
|
|
15
|
+
this._log = require('../../../../src/common/logger').logger.child('EachStage');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Execute the each stage
|
|
20
|
+
* @param {FlowContext} context
|
|
21
|
+
* @returns {Promise<Array>} results for each iteration
|
|
22
|
+
*/
|
|
23
|
+
async execute(context) {
|
|
24
|
+
this._log.debug(`Executing each loop: ${this.name}`);
|
|
25
|
+
|
|
26
|
+
// Resolve the array to iterate
|
|
27
|
+
let array;
|
|
28
|
+
if (typeof this.each === 'string') {
|
|
29
|
+
// Strip optional surrounding {{ }} so users can write either
|
|
30
|
+
// "each": "items" (bare identifier)
|
|
31
|
+
// "each": "{{items}}" (templated)
|
|
32
|
+
// "each": "{{init.items}}" (stage.field)
|
|
33
|
+
let path = this.each.trim();
|
|
34
|
+
const m = path.match(/^\{\{([^}]+)\}\}$/);
|
|
35
|
+
if (m) path = m[1].trim();
|
|
36
|
+
|
|
37
|
+
// Use the low-level _resolvePath so we get the actual array/object back
|
|
38
|
+
// (String.prototype.replace in resolveValue would coerce non-strings.)
|
|
39
|
+
let resolved = context._resolvePath(path);
|
|
40
|
+
if (resolved === undefined && path.startsWith('[')) {
|
|
41
|
+
// Support inline array literal: each: "['a','b']"
|
|
42
|
+
try { resolved = JSON.parse(path); } catch {}
|
|
43
|
+
}
|
|
44
|
+
if (Array.isArray(resolved)) {
|
|
45
|
+
array = resolved;
|
|
46
|
+
} else if (resolved && typeof resolved === 'object') {
|
|
47
|
+
// Convert object to entries (so {{k, v}} style iteration works)
|
|
48
|
+
array = Object.entries(resolved);
|
|
49
|
+
} else {
|
|
50
|
+
throw new Error(`each "${this.each}" resolved to non-iterable: ${typeof resolved}`);
|
|
51
|
+
}
|
|
52
|
+
} else if (Array.isArray(this.each)) {
|
|
53
|
+
array = this.each;
|
|
54
|
+
} else {
|
|
55
|
+
throw new Error(`each stage "${this.name}" requires an array to iterate`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const results = [];
|
|
59
|
+
const maxIter = Math.min(this.maxIterations, array.length);
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < maxIter; i++) {
|
|
62
|
+
// Set loop variables
|
|
63
|
+
context.data[this.as] = array[i];
|
|
64
|
+
context.data[this.index] = i;
|
|
65
|
+
context.data.loopIndex = i; // Legacy alias
|
|
66
|
+
|
|
67
|
+
this._log.debug(`Each iteration ${i}: ${this.as}=`, array[i]);
|
|
68
|
+
|
|
69
|
+
// Execute child stages sequentially
|
|
70
|
+
let stageResults = [];
|
|
71
|
+
for (const stageConfig of this.stages) {
|
|
72
|
+
const stageExecutor = context._stageExecutor;
|
|
73
|
+
if (stageExecutor) {
|
|
74
|
+
const result = await stageExecutor.executeStage(stageConfig, context);
|
|
75
|
+
stageResults.push(result);
|
|
76
|
+
|
|
77
|
+
// Check for early exit
|
|
78
|
+
if (context._destroyed) {
|
|
79
|
+
this._log.debug('Flow destroyed, stopping each loop');
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
results.push(stageResults);
|
|
85
|
+
|
|
86
|
+
// Check until condition
|
|
87
|
+
if (this.until) {
|
|
88
|
+
try {
|
|
89
|
+
const { evaluateInSandbox } = require('../../../../../src/utils/sandbox');
|
|
90
|
+
const evalContext = {
|
|
91
|
+
input: context.input,
|
|
92
|
+
data: context.data,
|
|
93
|
+
variables: Object.fromEntries(context._results),
|
|
94
|
+
previousResult: context._sentData,
|
|
95
|
+
item: array[i],
|
|
96
|
+
index: i,
|
|
97
|
+
};
|
|
98
|
+
const shouldStop = evaluateInSandbox(this.until, evalContext, { timeout: 5000 });
|
|
99
|
+
if (shouldStop) {
|
|
100
|
+
this._log.debug(`Each loop terminated at iteration ${i} by until condition`);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
this._log.warn(`until condition error: ${err.message}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Cleanup loop variables
|
|
110
|
+
delete context.data[this.as];
|
|
111
|
+
delete context.data[this.index];
|
|
112
|
+
delete context.data.loopIndex;
|
|
113
|
+
|
|
114
|
+
// Store results by name if this stage has a name
|
|
115
|
+
if (this.name) {
|
|
116
|
+
context.setResult(this.name, results);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return results;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { EachStage };
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* parallel stage executor
|
|
3
|
-
* Run stages concurrently
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
class ParallelStage {
|
|
7
|
-
constructor(config) {
|
|
8
|
-
this.name = config.name;
|
|
9
|
-
this.parallel = config.parallel || []; // Array of stages to run in parallel
|
|
10
|
-
this._log = require('../../../../src/common/logger').logger.child('ParallelStage');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Execute the parallel stage
|
|
15
|
-
* @param {FlowContext} context
|
|
16
|
-
* @returns {Promise<Array>} results from all parallel stages
|
|
17
|
-
*/
|
|
18
|
-
async execute(context) {
|
|
19
|
-
this._log.debug(`Executing parallel: ${this.name}`);
|
|
20
|
-
|
|
21
|
-
if (!this.parallel || this.parallel.length === 0) {
|
|
22
|
-
return [];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const stageExecutor = context._stageExecutor;
|
|
26
|
-
if (!stageExecutor) {
|
|
27
|
-
throw new Error('Stage executor not set in context');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Execute all stages concurrently
|
|
31
|
-
const promises = this.parallel.map(async (stageConfig) => {
|
|
32
|
-
try {
|
|
33
|
-
return await stageExecutor.executeStage(stageConfig, context);
|
|
34
|
-
} catch (err) {
|
|
35
|
-
this._log.warn(`Parallel stage error: ${err.message}`);
|
|
36
|
-
return { error: err.message };
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const results = await Promise.all(promises);
|
|
41
|
-
|
|
42
|
-
// Build result object keyed by stage name (not plain array)
|
|
43
|
-
const resultByName = {};
|
|
44
|
-
for (let i = 0; i < this.parallel.length; i++) {
|
|
45
|
-
const stageConfig = this.parallel[i];
|
|
46
|
-
const name = stageConfig.name || String(i);
|
|
47
|
-
resultByName[name] = results[i];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Store named results in context
|
|
51
|
-
// If the parallel block itself has a name, store the keyed object
|
|
52
|
-
if (this.name) {
|
|
53
|
-
context.setResult(this.name, resultByName);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Also store individual named results for access via {{stageName}}
|
|
57
|
-
for (let i = 0; i < this.parallel.length; i++) {
|
|
58
|
-
const stageConfig = this.parallel[i];
|
|
59
|
-
if (stageConfig.name) {
|
|
60
|
-
context.setResult(stageConfig.name, results[i]);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
this._log.debug(`Parallel completed: ${results.length} stages`);
|
|
65
|
-
return resultByName;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
module.exports = { ParallelStage };
|
|
1
|
+
/**
|
|
2
|
+
* parallel stage executor
|
|
3
|
+
* Run stages concurrently
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class ParallelStage {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.name = config.name;
|
|
9
|
+
this.parallel = config.parallel || []; // Array of stages to run in parallel
|
|
10
|
+
this._log = require('../../../../src/common/logger').logger.child('ParallelStage');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Execute the parallel stage
|
|
15
|
+
* @param {FlowContext} context
|
|
16
|
+
* @returns {Promise<Array>} results from all parallel stages
|
|
17
|
+
*/
|
|
18
|
+
async execute(context) {
|
|
19
|
+
this._log.debug(`Executing parallel: ${this.name}`);
|
|
20
|
+
|
|
21
|
+
if (!this.parallel || this.parallel.length === 0) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const stageExecutor = context._stageExecutor;
|
|
26
|
+
if (!stageExecutor) {
|
|
27
|
+
throw new Error('Stage executor not set in context');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Execute all stages concurrently
|
|
31
|
+
const promises = this.parallel.map(async (stageConfig) => {
|
|
32
|
+
try {
|
|
33
|
+
return await stageExecutor.executeStage(stageConfig, context);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
this._log.warn(`Parallel stage error: ${err.message}`);
|
|
36
|
+
return { error: err.message };
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const results = await Promise.all(promises);
|
|
41
|
+
|
|
42
|
+
// Build result object keyed by stage name (not plain array)
|
|
43
|
+
const resultByName = {};
|
|
44
|
+
for (let i = 0; i < this.parallel.length; i++) {
|
|
45
|
+
const stageConfig = this.parallel[i];
|
|
46
|
+
const name = stageConfig.name || String(i);
|
|
47
|
+
resultByName[name] = results[i];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Store named results in context
|
|
51
|
+
// If the parallel block itself has a name, store the keyed object
|
|
52
|
+
if (this.name) {
|
|
53
|
+
context.setResult(this.name, resultByName);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Also store individual named results for access via {{stageName}}
|
|
57
|
+
for (let i = 0; i < this.parallel.length; i++) {
|
|
58
|
+
const stageConfig = this.parallel[i];
|
|
59
|
+
if (stageConfig.name) {
|
|
60
|
+
context.setResult(stageConfig.name, results[i]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this._log.debug(`Parallel completed: ${results.length} stages`);
|
|
65
|
+
return resultByName;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = { ParallelStage };
|
|
@@ -221,7 +221,7 @@ class FeishuPlugin extends Plugin {
|
|
|
221
221
|
case 'history':
|
|
222
222
|
if (this._sessionPlugin) {
|
|
223
223
|
const session = this._sessionPlugin.getSession(`feishu_${openId}`)
|
|
224
|
-
const sessions = this._sessionPlugin.listSessions().filter(s => s.id.startsWith('feishu_'))
|
|
224
|
+
const sessions = (await this._sessionPlugin.listSessions()).filter(s => s.id.startsWith('feishu_'))
|
|
225
225
|
await this._sendMessage(openId,
|
|
226
226
|
`📊 当前状态\n\n会话数:${sessions.length}\n历史消息:${session?.messages?.length || 0}\n最后活跃:${session?.lastActive?.toLocaleString() || '无'}`,
|
|
227
227
|
originalMsg)
|
|
@@ -312,7 +312,7 @@ class FeishuPlugin extends Plugin {
|
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
if (this._sessionPlugin) {
|
|
315
|
-
const sessions = this._sessionPlugin.listSessions()
|
|
315
|
+
const sessions = (await this._sessionPlugin.listSessions())
|
|
316
316
|
.filter(s => s.id.startsWith('feishu_'))
|
|
317
317
|
.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
318
318
|
|
|
@@ -370,7 +370,7 @@ class FeishuPlugin extends Plugin {
|
|
|
370
370
|
openId = effectiveSessionId.replace('feishu_', '')
|
|
371
371
|
} else if (this._sessionPlugin) {
|
|
372
372
|
// 获取最近的 feishu 会话
|
|
373
|
-
const sessions = this._sessionPlugin.listSessions()
|
|
373
|
+
const sessions = (await this._sessionPlugin.listSessions())
|
|
374
374
|
.filter(s => s.id.startsWith('feishu_'))
|
|
375
375
|
.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
376
376
|
if (sessions.length > 0) {
|
|
@@ -587,10 +587,10 @@ class FeishuPlugin extends Plugin {
|
|
|
587
587
|
}
|
|
588
588
|
}
|
|
589
589
|
|
|
590
|
-
getStatus() {
|
|
590
|
+
async getStatus() {
|
|
591
591
|
let sessions = []
|
|
592
592
|
if (this._sessionPlugin) {
|
|
593
|
-
sessions = this._sessionPlugin.listSessions()
|
|
593
|
+
sessions = (await this._sessionPlugin.listSessions())
|
|
594
594
|
.filter(s => s.id.startsWith('feishu_'))
|
|
595
595
|
.map(s => ({
|
|
596
596
|
openId: s.id.replace('feishu_', ''),
|
|
@@ -958,7 +958,7 @@ class QQPlugin extends Plugin {
|
|
|
958
958
|
identifier = sessionId.replace('qq_c2c_', '')
|
|
959
959
|
}
|
|
960
960
|
} else if (this._sessionPlugin) {
|
|
961
|
-
const sessions = this._sessionPlugin.listSessions()
|
|
961
|
+
const sessions = (await this._sessionPlugin.listSessions())
|
|
962
962
|
.filter(s => s.id.startsWith('qq_'))
|
|
963
963
|
.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
964
964
|
if (sessions.length > 0) {
|
|
@@ -1000,10 +1000,10 @@ class QQPlugin extends Plugin {
|
|
|
1000
1000
|
/**
|
|
1001
1001
|
* 获取插件状态
|
|
1002
1002
|
*/
|
|
1003
|
-
getStatus() {
|
|
1003
|
+
async getStatus() {
|
|
1004
1004
|
let sessions = []
|
|
1005
1005
|
if (this._sessionPlugin) {
|
|
1006
|
-
const allSessions = this._sessionPlugin.listSessions()
|
|
1006
|
+
const allSessions = await this._sessionPlugin.listSessions()
|
|
1007
1007
|
sessions = allSessions
|
|
1008
1008
|
.filter(s => s.id.startsWith('qq_'))
|
|
1009
1009
|
.map(s => ({
|
|
@@ -157,7 +157,7 @@ class TelegramPlugin extends Plugin {
|
|
|
157
157
|
chatId = effectiveSessionId.replace('telegram_', '')
|
|
158
158
|
} else if (this._sessionPlugin) {
|
|
159
159
|
// 获取最近的 telegram 会话
|
|
160
|
-
const sessions = this._sessionPlugin.listSessions()
|
|
160
|
+
const sessions = (await this._sessionPlugin.listSessions())
|
|
161
161
|
.filter(s => s.id.startsWith('telegram_'))
|
|
162
162
|
.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
163
163
|
if (sessions.length > 0) {
|
|
@@ -607,10 +607,10 @@ class TelegramPlugin extends Plugin {
|
|
|
607
607
|
return this._bot.sendMessage(chatId, text, { reply_to_message_id: replyToMessageId })
|
|
608
608
|
}
|
|
609
609
|
|
|
610
|
-
getStatus() {
|
|
610
|
+
async getStatus() {
|
|
611
611
|
let sessions = []
|
|
612
612
|
if (this._sessionPlugin) {
|
|
613
|
-
sessions = this._sessionPlugin.listSessions()
|
|
613
|
+
sessions = (await this._sessionPlugin.listSessions())
|
|
614
614
|
.filter(s => s.id.startsWith('telegram_'))
|
|
615
615
|
.map(s => ({
|
|
616
616
|
chatId: s.id.replace('telegram_', ''),
|
|
@@ -905,7 +905,7 @@ class WeixinPlugin extends Plugin {
|
|
|
905
905
|
|
|
906
906
|
// 其他情况(包括 null 或其他 sessionId),发送到最近的 WeChat 会话
|
|
907
907
|
if (this._sessionPlugin) {
|
|
908
|
-
const allSessions = this._sessionPlugin
|
|
908
|
+
const allSessions = await this._sessionPlugin?.listSessions()
|
|
909
909
|
const weixinSessions = allSessions
|
|
910
910
|
.filter(s => s.id.startsWith('weixin_'))
|
|
911
911
|
.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
@@ -977,8 +977,8 @@ class WeixinPlugin extends Plugin {
|
|
|
977
977
|
userId = effectiveSessionId.replace('weixin_', '')
|
|
978
978
|
} else if (this._sessionPlugin) {
|
|
979
979
|
// 获取最近的 weixin 会话
|
|
980
|
-
const
|
|
981
|
-
|
|
980
|
+
const allSessions=await this._sessionPlugin?.listSessions()
|
|
981
|
+
const sessions = (allSessions||[]).filter(s => s.id.startsWith('weixin_'))
|
|
982
982
|
.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
983
983
|
if (sessions.length > 0) {
|
|
984
984
|
userId = sessions[0].id.replace('weixin_', '')
|
|
@@ -1011,11 +1011,11 @@ class WeixinPlugin extends Plugin {
|
|
|
1011
1011
|
/**
|
|
1012
1012
|
* 获取插件状态
|
|
1013
1013
|
*/
|
|
1014
|
-
getStatus() {
|
|
1014
|
+
async getStatus() {
|
|
1015
1015
|
// 从 SessionPlugin 获取 WeChat 相关会话
|
|
1016
1016
|
let sessions = []
|
|
1017
1017
|
if (this._sessionPlugin) {
|
|
1018
|
-
const allSessions = this._sessionPlugin.listSessions()
|
|
1018
|
+
const allSessions = await this._sessionPlugin.listSessions()
|
|
1019
1019
|
sessions = allSessions
|
|
1020
1020
|
.filter(s => s.id.startsWith('weixin_'))
|
|
1021
1021
|
.map(s => ({
|