flowmind 1.5.2 → 1.5.3
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/core/adapters/mcp-adapter.js +25 -0
- package/core/adapters/workflow-adapter.js +26 -0
- package/core/component-registry.js +19 -1
- package/core/config-manager.js +7 -2
- package/core/index.js +14 -1
- package/core/mcp-http-client.js +63 -0
- package/core/providers/aliyun/rds-query-adapter.js +70 -0
- package/core/providers/friday/flow-adapter.js +19 -30
- package/core/sdd-agent-sync.js +240 -16
- package/core/source-inference.js +324 -0
- package/package.json +1 -1
- package/skills/auto-flow/index.js +528 -52
- package/skills/data-logic-validation/index.js +133 -12
- package/skills/resource-bind/index.js +61 -18
- package/tui/app.jsx +2 -1
- package/tui/format-result.js +43 -4
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const BaseAdapter = require('./base-adapter');
|
|
7
|
+
const { callMcpTool } = require('../mcp-http-client');
|
|
7
8
|
|
|
8
9
|
class McpAdapter extends BaseAdapter {
|
|
9
10
|
constructor(providerName, config = {}) {
|
|
@@ -60,6 +61,30 @@ class McpAdapter extends BaseAdapter {
|
|
|
60
61
|
tools: this.getToolMappings()
|
|
61
62
|
};
|
|
62
63
|
}
|
|
64
|
+
|
|
65
|
+
getTransportConfig() {
|
|
66
|
+
return this.config.transport || this.config.mcpServerConfig || this.config.mcpTransport || {};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async callMcpTool(localName, args = {}) {
|
|
70
|
+
const tool = this.resolveTool(localName);
|
|
71
|
+
if (!tool) {
|
|
72
|
+
throw new Error(`Unknown MCP tool mapping: ${localName}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const transport = this.getTransportConfig();
|
|
76
|
+
const serverName = this.mcpServer || this.providerName;
|
|
77
|
+
const response = await callMcpTool({
|
|
78
|
+
url: transport.url || transport.endpoint || transport.baseUrl,
|
|
79
|
+
headers: transport.headers || {},
|
|
80
|
+
tool,
|
|
81
|
+
args,
|
|
82
|
+
timeoutMs: transport.timeoutMs || 60000,
|
|
83
|
+
serverName
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return response;
|
|
87
|
+
}
|
|
63
88
|
}
|
|
64
89
|
|
|
65
90
|
module.exports = McpAdapter;
|
|
@@ -29,6 +29,32 @@ class WorkflowAdapter extends McpAdapter {
|
|
|
29
29
|
throw new Error('Subclasses must implement listPipelines()');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Query the current iterate metadata used by workflow lookup flows.
|
|
34
|
+
* @returns {Promise<object>}
|
|
35
|
+
*/
|
|
36
|
+
async getCurrentIterate() {
|
|
37
|
+
throw new Error('Subclasses must implement getCurrentIterate()');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* List deploy checklist records that contain service-to-pipeline mappings.
|
|
42
|
+
* @param {object} params
|
|
43
|
+
* @returns {Promise<object>}
|
|
44
|
+
*/
|
|
45
|
+
async listDeployChecklists(params) {
|
|
46
|
+
throw new Error('Subclasses must implement listDeployChecklists()');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Query historical flow orders that can be used as a fallback pipeline source.
|
|
51
|
+
* @param {object} params
|
|
52
|
+
* @returns {Promise<object>}
|
|
53
|
+
*/
|
|
54
|
+
async orderList(params) {
|
|
55
|
+
throw new Error('Subclasses must implement orderList()');
|
|
56
|
+
}
|
|
57
|
+
|
|
32
58
|
/**
|
|
33
59
|
* Start a pipeline run.
|
|
34
60
|
* @param {string} pipelineId
|
|
@@ -41,6 +41,10 @@ class ComponentRegistry {
|
|
|
41
41
|
const AliyunDmsAdapter = require('./providers/aliyun/dms-adapter');
|
|
42
42
|
return new AliyunDmsAdapter(this.getProviderConfig('databaseManager', 'aliyun-dms'));
|
|
43
43
|
});
|
|
44
|
+
this.registerFactory('aliyun-rds-query', () => {
|
|
45
|
+
const AliyunRdsQueryAdapter = require('./providers/aliyun/rds-query-adapter');
|
|
46
|
+
return new AliyunRdsQueryAdapter(this.getProviderConfig('databaseQuery', 'aliyun-rds-query'));
|
|
47
|
+
});
|
|
44
48
|
this.registerFactory('aliyun-redis', () => {
|
|
45
49
|
const AliyunRedisAdapter = require('./providers/aliyun/redis-adapter');
|
|
46
50
|
return new AliyunRedisAdapter(this.getProviderConfig('redisMonitor', 'aliyun-redis'));
|
|
@@ -79,7 +83,21 @@ class ComponentRegistry {
|
|
|
79
83
|
const components = this.config.get('components', {});
|
|
80
84
|
const typeConfig = components[componentType] || {};
|
|
81
85
|
const providers = typeConfig.providers || {};
|
|
82
|
-
|
|
86
|
+
const baseConfig = providers[providerName] || {};
|
|
87
|
+
const mcpServerName = McpCompatibility.getMcpServer(componentType, providerName);
|
|
88
|
+
const mcpServers = this.config.get('mcpServers', {});
|
|
89
|
+
const transport = mcpServerName ? mcpServers[mcpServerName] : null;
|
|
90
|
+
|
|
91
|
+
if (!transport) {
|
|
92
|
+
return baseConfig;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
...baseConfig,
|
|
97
|
+
transport: {
|
|
98
|
+
...transport
|
|
99
|
+
}
|
|
100
|
+
};
|
|
83
101
|
}
|
|
84
102
|
|
|
85
103
|
/**
|
package/core/config-manager.js
CHANGED
|
@@ -82,7 +82,10 @@ class ConfigManager {
|
|
|
82
82
|
if (await fs.pathExists(resourceConfigPath)) {
|
|
83
83
|
try {
|
|
84
84
|
const resourceConfig = await fs.readJson(resourceConfigPath);
|
|
85
|
-
this.mergeConfig({
|
|
85
|
+
this.mergeConfig({
|
|
86
|
+
resources: resourceConfig.resources || {},
|
|
87
|
+
mcpServers: resourceConfig.mcpServers || {}
|
|
88
|
+
});
|
|
86
89
|
} catch (error) {
|
|
87
90
|
console.warn('Failed to load resource config:', error.message);
|
|
88
91
|
}
|
|
@@ -216,7 +219,9 @@ class ConfigManager {
|
|
|
216
219
|
redis: { enabled: false },
|
|
217
220
|
logs: { enabled: false },
|
|
218
221
|
apiDocs: { enabled: false }
|
|
219
|
-
}
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
mcpServers: {}
|
|
220
225
|
};
|
|
221
226
|
}
|
|
222
227
|
|
package/core/index.js
CHANGED
|
@@ -11,6 +11,8 @@ const ConfigManager = require('./config-manager');
|
|
|
11
11
|
const ComponentRegistry = require('./component-registry');
|
|
12
12
|
const ModelManager = require('./ai/model-manager');
|
|
13
13
|
const eventBus = require('./event-bus');
|
|
14
|
+
const { autoSyncSddAgentToFlowMind } = require('./sdd-agent-sync');
|
|
15
|
+
const { inferSourceContext } = require('./source-inference');
|
|
14
16
|
|
|
15
17
|
class FlowMind {
|
|
16
18
|
constructor(options = {}) {
|
|
@@ -32,6 +34,7 @@ class FlowMind {
|
|
|
32
34
|
async init() {
|
|
33
35
|
if (this.initialized) return this;
|
|
34
36
|
|
|
37
|
+
await autoSyncSddAgentToFlowMind();
|
|
35
38
|
await this.config.load();
|
|
36
39
|
|
|
37
40
|
// Validate configuration
|
|
@@ -191,15 +194,25 @@ class FlowMind {
|
|
|
191
194
|
// Get learning rules for this skill
|
|
192
195
|
const learnings = await this.learning.getSkillLearnings(skill.name);
|
|
193
196
|
const resourceBinding = await this.learning.matchResourceBinding(input, skill.name);
|
|
197
|
+
let sourceInference = context.sourceInference || null;
|
|
198
|
+
if (!sourceInference) {
|
|
199
|
+
try {
|
|
200
|
+
sourceInference = await inferSourceContext(input);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
sourceInference = null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
194
205
|
|
|
195
206
|
// Apply learning rules to context
|
|
196
207
|
const enhancedContext = {
|
|
197
208
|
...context,
|
|
198
209
|
flowmind: context.flowmind || this,
|
|
210
|
+
componentRegistry: context.componentRegistry || this.components,
|
|
199
211
|
currentSkill: context.currentSkill || skill.name,
|
|
200
212
|
learnings: learnings,
|
|
201
213
|
preferences: await this.learning.getPreferences(skill.name),
|
|
202
|
-
resourceBinding
|
|
214
|
+
resourceBinding,
|
|
215
|
+
sourceInference
|
|
203
216
|
};
|
|
204
217
|
|
|
205
218
|
// Execute skill
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal JSON-RPC MCP HTTP client.
|
|
3
|
+
* Sends tools/call requests to an MCP gateway over HTTP.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
async function callMcpTool({ url, headers = {}, tool, args = {}, timeoutMs = 60000, serverName = 'mcp' }) {
|
|
7
|
+
if (!url) {
|
|
8
|
+
throw new Error(`MCP transport URL not configured for ${serverName}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const requestId = Date.now();
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch(url, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
'User-Agent': 'flowmind',
|
|
21
|
+
...headers
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify({
|
|
24
|
+
jsonrpc: '2.0',
|
|
25
|
+
id: requestId,
|
|
26
|
+
method: 'tools/call',
|
|
27
|
+
params: {
|
|
28
|
+
name: tool,
|
|
29
|
+
arguments: args
|
|
30
|
+
}
|
|
31
|
+
}),
|
|
32
|
+
signal: controller.signal
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const text = await response.text();
|
|
36
|
+
let payload = null;
|
|
37
|
+
|
|
38
|
+
if (text) {
|
|
39
|
+
try {
|
|
40
|
+
payload = JSON.parse(text);
|
|
41
|
+
} catch (parseError) {
|
|
42
|
+
throw new Error(`Invalid MCP response from ${serverName}: ${text.slice(0, 200)}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
const errorMessage = payload?.error?.message || payload?.message || response.statusText;
|
|
48
|
+
throw new Error(`MCP request failed for ${serverName}: ${errorMessage}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (payload?.error) {
|
|
52
|
+
throw new Error(payload.error.message || `MCP tool call failed for ${serverName}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return payload?.result ?? payload ?? null;
|
|
56
|
+
} finally {
|
|
57
|
+
clearTimeout(timeout);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
callMcpTool
|
|
63
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aliyun RDS Direct Query Adapter
|
|
3
|
+
* Wraps the friday-rds-redis-query MCP server for SQL and Redis reads.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const DatabaseQueryAdapter = require('../../adapters/database-query-adapter');
|
|
7
|
+
|
|
8
|
+
class AliyunRdsQueryAdapter extends DatabaseQueryAdapter {
|
|
9
|
+
constructor(config = {}) {
|
|
10
|
+
super('aliyun-rds-query', config);
|
|
11
|
+
|
|
12
|
+
this.registerTool('fetchSources', 'mcpFetchSource');
|
|
13
|
+
this.registerTool('fetchDatabases', 'mcpFetchBase');
|
|
14
|
+
this.registerTool('fetchTables', 'mcpFetchTables');
|
|
15
|
+
this.registerTool('fetchFields', 'mcpFetchFields');
|
|
16
|
+
this.registerTool('queryExec', 'mcpQueryExec');
|
|
17
|
+
this.registerTool('redisSources', 'mcpRedisSource');
|
|
18
|
+
this.registerTool('redisKeys', 'mcpRedisKeys');
|
|
19
|
+
this.registerTool('redisKeyGet', 'mcpRedisKeyGet');
|
|
20
|
+
this.registerTool('redisScan', 'mcpRedisScan');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get mcpServer() {
|
|
24
|
+
return 'friday-rds-redis-query';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async fetchSources(params) {
|
|
28
|
+
return this.callMcpTool('fetchSources', params || {});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async fetchDatabases(sourceIdOrParams) {
|
|
32
|
+
const params = typeof sourceIdOrParams === 'object'
|
|
33
|
+
? sourceIdOrParams
|
|
34
|
+
: { source_id: sourceIdOrParams };
|
|
35
|
+
return this.callMcpTool('fetchDatabases', params || {});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async fetchTables(sourceIdOrParams, schema) {
|
|
39
|
+
const params = typeof sourceIdOrParams === 'object'
|
|
40
|
+
? sourceIdOrParams
|
|
41
|
+
: { source_id: sourceIdOrParams, schema };
|
|
42
|
+
return this.callMcpTool('fetchTables', params || {});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async fetchFields(params) {
|
|
46
|
+
return this.callMcpTool('fetchFields', params || {});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async queryExec(params) {
|
|
50
|
+
return this.callMcpTool('queryExec', params || {});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async redisSources(params) {
|
|
54
|
+
return this.callMcpTool('redisSources', params || {});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async redisKeys(params) {
|
|
58
|
+
return this.callMcpTool('redisKeys', params || {});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async redisKeyGet(params) {
|
|
62
|
+
return this.callMcpTool('redisKeyGet', params || {});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async redisScan(params) {
|
|
66
|
+
return this.callMcpTool('redisScan', params || {});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = AliyunRdsQueryAdapter;
|
|
@@ -26,6 +26,7 @@ class FridayFlowAdapter extends WorkflowAdapter {
|
|
|
26
26
|
this.registerTool('logPipelineJobRun', 'flowLogPipelineJobRun');
|
|
27
27
|
this.registerTool('passPipelineValidate', 'flowPassPipelineValidate');
|
|
28
28
|
this.registerTool('refusePipelineValidate', 'flowRefusePipelineValidate');
|
|
29
|
+
this.registerTool('listDeployChecklists', 'listDeployChecklists');
|
|
29
30
|
|
|
30
31
|
// Order/task tools
|
|
31
32
|
this.registerTool('orderList', 'orderList');
|
|
@@ -42,51 +43,39 @@ class FridayFlowAdapter extends WorkflowAdapter {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
async listPipelineGroups(params) {
|
|
45
|
-
return {
|
|
46
|
-
mcpServer: this.mcpServer,
|
|
47
|
-
tool: this.resolveTool('listPipelineGroups'),
|
|
48
|
-
params: params || { perPage: '20', page: '1' }
|
|
49
|
-
};
|
|
46
|
+
return this.callMcpTool('listPipelineGroups', params || { perPage: '20', page: '1' });
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
async listPipelines(params) {
|
|
53
|
-
return {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
};
|
|
50
|
+
return this.callMcpTool('listPipelines', params || {});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async getCurrentIterate() {
|
|
54
|
+
return this.callMcpTool('getCurrentIterate', {});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async listDeployChecklists(params) {
|
|
58
|
+
return this.callMcpTool('listDeployChecklists', params || {});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async orderList(params) {
|
|
62
|
+
return this.callMcpTool('orderList', params || {});
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
async startPipelineRun(pipelineId) {
|
|
61
|
-
return {
|
|
62
|
-
mcpServer: this.mcpServer,
|
|
63
|
-
tool: this.resolveTool('startPipelineRun'),
|
|
64
|
-
params: { pipelineId }
|
|
65
|
-
};
|
|
66
|
+
return this.callMcpTool('startPipelineRun', { pipelineId });
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
async startBatchPipelineRun(params) {
|
|
69
|
-
return {
|
|
70
|
-
mcpServer: this.mcpServer,
|
|
71
|
-
tool: this.resolveTool('startBatchPipelineRun'),
|
|
72
|
-
params: params || {}
|
|
73
|
-
};
|
|
70
|
+
return this.callMcpTool('startBatchPipelineRun', params || {});
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
async getPipelineRun(pipelineId, runId) {
|
|
77
|
-
return {
|
|
78
|
-
mcpServer: this.mcpServer,
|
|
79
|
-
tool: this.resolveTool('getPipelineRun'),
|
|
80
|
-
params: { pipelineId, pipelineRunId: runId }
|
|
81
|
-
};
|
|
74
|
+
return this.callMcpTool('getPipelineRun', { pipelineId, pipelineRunId: runId });
|
|
82
75
|
}
|
|
83
76
|
|
|
84
77
|
async listPipelineRuns(pipelineId, params) {
|
|
85
|
-
return {
|
|
86
|
-
mcpServer: this.mcpServer,
|
|
87
|
-
tool: this.resolveTool('listPipelineRuns'),
|
|
88
|
-
params: { pipelineId, ...(params || {}) }
|
|
89
|
-
};
|
|
78
|
+
return this.callMcpTool('listPipelineRuns', { pipelineId, ...(params || {}) });
|
|
90
79
|
}
|
|
91
80
|
}
|
|
92
81
|
|