flowmind 1.5.3 → 1.5.4
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/CHANGELOG.md +14 -0
- package/bin/flowmind.js +85 -8
- package/core/adapters/mcp-adapter.js +9 -8
- package/core/ai/providers/mimo.js +1 -1
- package/core/index.js +139 -1
- package/core/providers/aliyun/dms-adapter.js +7 -35
- package/core/providers/aliyun/redis-adapter.js +4 -20
- package/core/providers/aliyun/sls-adapter.js +3 -10
- package/core/providers/friday/report-adapter.js +5 -25
- package/core/providers/yapi/yapi-adapter.js +6 -30
- package/core/providers/yuque/yuque-adapter.js +7 -35
- package/package.json +1 -1
- package/skills/auto-flow/index.js +397 -44
- package/skills/log-audit/index.js +94 -1
- package/skills/yapi-sync-interface/index.js +146 -13
- package/skills/yuque-sync-design/index.js +130 -11
- package/tui/app.jsx +13 -4
- package/tui/components/ChatPanel.jsx +1 -1
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const os = require('os');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const { callMcpTool } = require('../../core/mcp-http-client');
|
|
9
10
|
|
|
10
11
|
const BUILTIN_WORKFLOWS = {
|
|
11
12
|
'dev-workflow': {
|
|
@@ -72,19 +73,14 @@ module.exports = {
|
|
|
72
73
|
const workflow = createWorkflowClient(context);
|
|
73
74
|
|
|
74
75
|
if (params.action === 'define') {
|
|
76
|
+
const definition = buildWorkflowDefinitionSummary();
|
|
75
77
|
return {
|
|
76
78
|
type: 'result',
|
|
77
79
|
skill: 'auto-flow',
|
|
78
80
|
message: 'Define a workflow in YAML format',
|
|
79
81
|
data: {
|
|
80
82
|
format: 'YAML workflow definition',
|
|
81
|
-
|
|
82
|
-
name: 'My Workflow',
|
|
83
|
-
steps: [
|
|
84
|
-
{ id: 'step1', action: 'run-command', command: 'echo hello' },
|
|
85
|
-
{ id: 'step2', skill: 'code-review', depends_on: ['step1'] }
|
|
86
|
-
]
|
|
87
|
-
}
|
|
83
|
+
execution: definition
|
|
88
84
|
},
|
|
89
85
|
input,
|
|
90
86
|
timestamp: new Date().toISOString()
|
|
@@ -96,7 +92,7 @@ module.exports = {
|
|
|
96
92
|
}
|
|
97
93
|
|
|
98
94
|
if (params.action === 'status') {
|
|
99
|
-
await executeStatus(workflow, params, input);
|
|
95
|
+
const execution = await executeStatus(workflow, params, input);
|
|
100
96
|
return {
|
|
101
97
|
type: 'result',
|
|
102
98
|
skill: 'auto-flow',
|
|
@@ -106,7 +102,8 @@ module.exports = {
|
|
|
106
102
|
provider: workflow.provider,
|
|
107
103
|
binding: workflow.binding,
|
|
108
104
|
status: 'completed',
|
|
109
|
-
mcpServer: getWorkflowMcpServer(workflow)
|
|
105
|
+
mcpServer: getWorkflowMcpServer(workflow),
|
|
106
|
+
execution: summarizeWorkflowExecution('status', params, execution)
|
|
110
107
|
},
|
|
111
108
|
input,
|
|
112
109
|
timestamp: new Date().toISOString()
|
|
@@ -118,7 +115,7 @@ module.exports = {
|
|
|
118
115
|
}
|
|
119
116
|
|
|
120
117
|
if (params.action === 'list' || params.serviceNames.length > 0) {
|
|
121
|
-
await workflow.client.listPipelines(buildListPipelineParams(params));
|
|
118
|
+
const execution = await workflow.client.listPipelines(buildListPipelineParams(params));
|
|
122
119
|
return {
|
|
123
120
|
type: 'result',
|
|
124
121
|
skill: 'auto-flow',
|
|
@@ -128,7 +125,8 @@ module.exports = {
|
|
|
128
125
|
provider: workflow.provider,
|
|
129
126
|
binding: workflow.binding,
|
|
130
127
|
status: 'completed',
|
|
131
|
-
mcpServer: getWorkflowMcpServer(workflow)
|
|
128
|
+
mcpServer: getWorkflowMcpServer(workflow),
|
|
129
|
+
execution: summarizeWorkflowExecution('list', params, execution)
|
|
132
130
|
},
|
|
133
131
|
input,
|
|
134
132
|
timestamp: new Date().toISOString()
|
|
@@ -143,7 +141,7 @@ module.exports = {
|
|
|
143
141
|
message: `Workflow ready: ${workflowDef.name}`,
|
|
144
142
|
data: {
|
|
145
143
|
workflow: params.workflow,
|
|
146
|
-
|
|
144
|
+
execution: buildBuiltinWorkflowSummary(params.workflow, workflowDef),
|
|
147
145
|
provider: workflow.provider
|
|
148
146
|
},
|
|
149
147
|
input,
|
|
@@ -164,7 +162,8 @@ module.exports = {
|
|
|
164
162
|
],
|
|
165
163
|
builtinWorkflows: Object.keys(BUILTIN_WORKFLOWS),
|
|
166
164
|
provider: workflow.provider,
|
|
167
|
-
binding: workflow.binding
|
|
165
|
+
binding: workflow.binding,
|
|
166
|
+
execution: buildAutoFlowHintSummary(workflow)
|
|
168
167
|
},
|
|
169
168
|
input,
|
|
170
169
|
timestamp: new Date().toISOString()
|
|
@@ -191,40 +190,391 @@ function createWorkflowClient(context) {
|
|
|
191
190
|
return { client: null, provider: null, binding: null, transportBacked: false };
|
|
192
191
|
}
|
|
193
192
|
|
|
193
|
+
const transport = buildWorkflowTransport(binding);
|
|
194
|
+
if (!transport.url) {
|
|
195
|
+
return { client: null, provider: binding.provider || null, binding, transportBacked: false };
|
|
196
|
+
}
|
|
197
|
+
|
|
194
198
|
return {
|
|
195
199
|
client: {
|
|
196
200
|
providerName: binding.provider || 'workflow-binding',
|
|
197
201
|
async listPipelines(params) {
|
|
198
|
-
return
|
|
202
|
+
return callWorkflowMcp(binding, transport, TOOL_NAMES.listPipelines, params);
|
|
199
203
|
},
|
|
200
204
|
async getCurrentIterate() {
|
|
201
|
-
return
|
|
205
|
+
return callWorkflowMcp(binding, transport, 'getCurrentIterate', {});
|
|
202
206
|
},
|
|
203
207
|
async listDeployChecklists(params) {
|
|
204
|
-
return
|
|
208
|
+
return callWorkflowMcp(binding, transport, 'listDeployChecklists', params);
|
|
205
209
|
},
|
|
206
210
|
async orderList(params) {
|
|
207
|
-
return
|
|
211
|
+
return callWorkflowMcp(binding, transport, 'orderList', params);
|
|
208
212
|
},
|
|
209
213
|
async startPipelineRun(pipelineId) {
|
|
210
|
-
return
|
|
214
|
+
return callWorkflowMcp(binding, transport, TOOL_NAMES.startPipelineRun, { pipelineId });
|
|
211
215
|
},
|
|
212
216
|
async startBatchPipelineRun(params) {
|
|
213
|
-
return
|
|
217
|
+
return callWorkflowMcp(binding, transport, TOOL_NAMES.startBatchPipelineRun, params);
|
|
214
218
|
},
|
|
215
219
|
async getPipelineRun(pipelineId, runId) {
|
|
216
|
-
return
|
|
220
|
+
return callWorkflowMcp(binding, transport, TOOL_NAMES.getPipelineRun, { pipelineId, pipelineRunId: runId });
|
|
217
221
|
},
|
|
218
222
|
async listPipelineRuns(pipelineId, params) {
|
|
219
|
-
return
|
|
223
|
+
return callWorkflowMcp(binding, transport, TOOL_NAMES.listPipelineRuns, { pipelineId, ...(params || {}) });
|
|
220
224
|
}
|
|
221
225
|
},
|
|
222
226
|
provider: binding.provider || 'workflow-binding',
|
|
223
227
|
binding,
|
|
224
|
-
transportBacked:
|
|
228
|
+
transportBacked: true
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function buildWorkflowTransport(binding) {
|
|
233
|
+
const connection = binding?.connection || {};
|
|
234
|
+
const headers = {};
|
|
235
|
+
|
|
236
|
+
if (connection.token) {
|
|
237
|
+
headers.Authorization = `Bearer ${connection.token}`;
|
|
238
|
+
}
|
|
239
|
+
if (connection.apiKey) {
|
|
240
|
+
headers['X-API-Key'] = connection.apiKey;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
url: connection.url || connection.address || connection.endpoint || null,
|
|
245
|
+
headers,
|
|
246
|
+
timeoutMs: connection.timeoutMs || 60000
|
|
225
247
|
};
|
|
226
248
|
}
|
|
227
249
|
|
|
250
|
+
function callWorkflowMcp(binding, transport, tool, params) {
|
|
251
|
+
return callMcpTool({
|
|
252
|
+
url: transport.url,
|
|
253
|
+
headers: transport.headers,
|
|
254
|
+
tool,
|
|
255
|
+
args: params || {},
|
|
256
|
+
timeoutMs: transport.timeoutMs || 60000,
|
|
257
|
+
serverName: binding.mcpServer || binding.provider || 'workflow'
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function summarizeWorkflowExecution(action, params, execution) {
|
|
262
|
+
const payload = unwrapMcpResult(execution);
|
|
263
|
+
const records = extractRecords(payload);
|
|
264
|
+
const total = extractTotalCount(payload, records);
|
|
265
|
+
|
|
266
|
+
return compactObject({
|
|
267
|
+
action,
|
|
268
|
+
summary: buildWorkflowSummaryText(action, params, total, records),
|
|
269
|
+
total,
|
|
270
|
+
status: extractWorkflowStatus(payload, action),
|
|
271
|
+
target: buildWorkflowTargetLabel(params),
|
|
272
|
+
environment: params.environment || null,
|
|
273
|
+
items: records.slice(0, 5).map((record) => summarizeWorkflowRecord(record))
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function summarizeDeployExecution(params, execution, context = {}) {
|
|
278
|
+
const payload = unwrapMcpResult(execution);
|
|
279
|
+
const payloadList = Array.isArray(payload) ? payload : [payload];
|
|
280
|
+
const primary = payloadList.find((item) => item !== null && item !== undefined) || null;
|
|
281
|
+
const summaryTarget = buildWorkflowTargetLabel(params) || buildDeployTargetLabel(context) || 'deployment';
|
|
282
|
+
|
|
283
|
+
return compactObject({
|
|
284
|
+
action: 'deploy',
|
|
285
|
+
summary: buildDeploySummaryText(summaryTarget, primary, context, payloadList.length),
|
|
286
|
+
status: extractDeployStatus(primary),
|
|
287
|
+
target: summaryTarget,
|
|
288
|
+
pipelineId: context.pipelineId || params.pipelineId || (Array.isArray(context.pipelineIds) && context.pipelineIds.length === 1 ? context.pipelineIds[0] : null),
|
|
289
|
+
pipelineIds: Array.isArray(context.pipelineIds) && context.pipelineIds.length > 0 ? context.pipelineIds : null,
|
|
290
|
+
source: context.source || null,
|
|
291
|
+
runId: extractDeployRunId(primary),
|
|
292
|
+
count: payloadList.length > 1 ? payloadList.length : null,
|
|
293
|
+
details: summarizeDeployDetails(primary)
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function buildWorkflowDefinitionSummary() {
|
|
298
|
+
return {
|
|
299
|
+
action: 'define',
|
|
300
|
+
summary: 'Prepared a YAML workflow template',
|
|
301
|
+
format: 'YAML workflow definition',
|
|
302
|
+
example: {
|
|
303
|
+
name: 'My Workflow',
|
|
304
|
+
steps: [
|
|
305
|
+
{ id: 'step1', action: 'run-command', command: 'echo hello' },
|
|
306
|
+
{ id: 'step2', skill: 'code-review', depends_on: ['step1'] }
|
|
307
|
+
]
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function buildBuiltinWorkflowSummary(workflowName, workflowDef) {
|
|
313
|
+
return {
|
|
314
|
+
action: 'workflow',
|
|
315
|
+
summary: `Workflow ready: ${workflowDef.name}`,
|
|
316
|
+
workflow: workflowName,
|
|
317
|
+
name: workflowDef.name,
|
|
318
|
+
stepCount: Array.isArray(workflowDef.steps) ? workflowDef.steps.length : 0,
|
|
319
|
+
steps: (workflowDef.steps || []).map((step) => compactObject({
|
|
320
|
+
id: step.id,
|
|
321
|
+
skill: step.skill || null,
|
|
322
|
+
action: step.action || null,
|
|
323
|
+
name: step.name || null,
|
|
324
|
+
depends_on: Array.isArray(step.depends_on) ? step.depends_on : null
|
|
325
|
+
}))
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function buildAutoFlowHintSummary(workflow) {
|
|
330
|
+
return {
|
|
331
|
+
action: 'hint',
|
|
332
|
+
summary: workflow?.binding
|
|
333
|
+
? `Workflow service bound to ${workflow.binding.mcpServer}`
|
|
334
|
+
: 'Workflow service not configured',
|
|
335
|
+
provider: workflow?.provider || null,
|
|
336
|
+
binding: workflow?.binding || null,
|
|
337
|
+
configured: !!workflow?.client
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function buildWorkflowSummaryText(action, params, total, records) {
|
|
342
|
+
const target = buildWorkflowTargetLabel(params) || 'all workflows';
|
|
343
|
+
const count = typeof total === 'number' ? total : records.length;
|
|
344
|
+
|
|
345
|
+
if (action === 'status') {
|
|
346
|
+
return `Checked ${target}${count !== null ? `, ${count} record(s) found` : ''}`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (action === 'list') {
|
|
350
|
+
return `Listed workflows for ${target}${count !== null ? `, ${count} pipeline(s) found` : ''}`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return `Workflow ${action} completed`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function buildWorkflowTargetLabel(params) {
|
|
357
|
+
if (params.pipelineId && params.runId) {
|
|
358
|
+
return `pipeline ${params.pipelineId} / run ${params.runId}`;
|
|
359
|
+
}
|
|
360
|
+
if (params.pipelineId) {
|
|
361
|
+
return `pipeline ${params.pipelineId}`;
|
|
362
|
+
}
|
|
363
|
+
if (params.serviceNames?.length > 0) {
|
|
364
|
+
return params.serviceNames.join(', ');
|
|
365
|
+
}
|
|
366
|
+
if (params.workflow) {
|
|
367
|
+
return params.workflow;
|
|
368
|
+
}
|
|
369
|
+
return params.environment || null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function buildDeployTargetLabel(context) {
|
|
373
|
+
if (Array.isArray(context.pipelineIds) && context.pipelineIds.length > 0) {
|
|
374
|
+
return `pipeline ${context.pipelineIds.join(', ')}`;
|
|
375
|
+
}
|
|
376
|
+
if (context.pipelineId) {
|
|
377
|
+
return `pipeline ${context.pipelineId}`;
|
|
378
|
+
}
|
|
379
|
+
if (Array.isArray(context.services) && context.services.length > 0) {
|
|
380
|
+
return context.services.map((item) => item.serviceName || item.service || item.pipelineName || item.pipelineId).filter(Boolean).join(', ');
|
|
381
|
+
}
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function buildDeploySummaryText(target, payload, context, count = 1) {
|
|
386
|
+
const status = extractDeployStatus(payload) || 'submitted';
|
|
387
|
+
const runId = extractDeployRunId(payload);
|
|
388
|
+
const source = context.source ? ` via ${context.source}` : '';
|
|
389
|
+
const countSuffix = count > 1 ? ` (${count} items)` : '';
|
|
390
|
+
|
|
391
|
+
if (runId) {
|
|
392
|
+
return `Deployment ${status} for ${target}${source}, run ${runId}${countSuffix}`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return `Deployment ${status} for ${target}${source}${countSuffix}`;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function extractDeployStatus(payload) {
|
|
399
|
+
if (payload && typeof payload === 'object') {
|
|
400
|
+
const candidates = [
|
|
401
|
+
payload.status,
|
|
402
|
+
payload.state,
|
|
403
|
+
payload.result?.status,
|
|
404
|
+
payload.result?.state
|
|
405
|
+
].filter(Boolean);
|
|
406
|
+
if (candidates.length > 0) {
|
|
407
|
+
return String(candidates[0]);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (payload.submitted === true) {
|
|
411
|
+
return 'submitted';
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function extractDeployRunId(payload) {
|
|
419
|
+
if (!payload || typeof payload !== 'object') {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const candidates = [
|
|
424
|
+
payload.pipelineRunId,
|
|
425
|
+
payload.pipeline_run_id,
|
|
426
|
+
payload.runId,
|
|
427
|
+
payload.run_id,
|
|
428
|
+
payload.result?.pipelineRunId,
|
|
429
|
+
payload.result?.pipeline_run_id,
|
|
430
|
+
payload.result?.runId,
|
|
431
|
+
payload.result?.run_id
|
|
432
|
+
];
|
|
433
|
+
|
|
434
|
+
for (const candidate of candidates) {
|
|
435
|
+
if (candidate === null || candidate === undefined || candidate === '') continue;
|
|
436
|
+
return String(candidate);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function summarizeDeployDetails(payload) {
|
|
443
|
+
if (!payload || typeof payload !== 'object') {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const details = compactObject({
|
|
448
|
+
submitted: typeof payload.submitted === 'boolean' ? payload.submitted : null,
|
|
449
|
+
pipelineRunId: payload.pipelineRunId || payload.pipeline_run_id || null,
|
|
450
|
+
pipelineId: payload.pipelineId || payload.pipeline_id || null,
|
|
451
|
+
message: payload.message || payload.result?.message || null
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
return Object.keys(details).length > 0 ? details : null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function unwrapMcpResult(payload) {
|
|
458
|
+
if (!payload || typeof payload !== 'object') {
|
|
459
|
+
return payload;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const textContent = Array.isArray(payload.content) ? payload.content : payload.result?.content;
|
|
463
|
+
if (Array.isArray(textContent)) {
|
|
464
|
+
const textItem = textContent.find((item) => item?.type === 'text' && typeof item.text === 'string');
|
|
465
|
+
if (textItem?.text) {
|
|
466
|
+
try {
|
|
467
|
+
return JSON.parse(textItem.text);
|
|
468
|
+
} catch (error) {
|
|
469
|
+
return payload;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return payload;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function extractRecords(payload) {
|
|
478
|
+
const normalized = unwrapMcpResult(payload);
|
|
479
|
+
if (!normalized) return [];
|
|
480
|
+
if (Array.isArray(normalized)) return normalized;
|
|
481
|
+
|
|
482
|
+
const candidates = [
|
|
483
|
+
normalized.data,
|
|
484
|
+
normalized.rows,
|
|
485
|
+
normalized.list,
|
|
486
|
+
normalized.records,
|
|
487
|
+
normalized.result?.data,
|
|
488
|
+
normalized.result?.rows,
|
|
489
|
+
normalized.result?.list,
|
|
490
|
+
normalized.result?.records
|
|
491
|
+
];
|
|
492
|
+
|
|
493
|
+
for (const candidate of candidates) {
|
|
494
|
+
if (Array.isArray(candidate)) {
|
|
495
|
+
return candidate;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return [];
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function extractTotalCount(payload, records) {
|
|
503
|
+
if (payload && typeof payload === 'object') {
|
|
504
|
+
const candidates = [payload.total, payload.count, payload.result?.total, payload.result?.count];
|
|
505
|
+
for (const candidate of candidates) {
|
|
506
|
+
if (typeof candidate === 'number' && Number.isFinite(candidate)) {
|
|
507
|
+
return candidate;
|
|
508
|
+
}
|
|
509
|
+
if (typeof candidate === 'string' && candidate.trim() !== '' && !Number.isNaN(Number(candidate))) {
|
|
510
|
+
return Number(candidate);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return records.length;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function extractWorkflowStatus(payload, action) {
|
|
519
|
+
if (payload && typeof payload === 'object') {
|
|
520
|
+
const candidates = [
|
|
521
|
+
payload.status,
|
|
522
|
+
payload.state,
|
|
523
|
+
payload.result?.status,
|
|
524
|
+
payload.result?.state
|
|
525
|
+
].filter(Boolean);
|
|
526
|
+
if (candidates.length > 0) {
|
|
527
|
+
return String(candidates[0]);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return action === 'status' ? 'completed' : 'completed';
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function summarizeWorkflowRecord(record) {
|
|
535
|
+
const fields = parseWorkflowFields(record?.fields);
|
|
536
|
+
return compactObject({
|
|
537
|
+
pipelineName: record?.pipelineName || record?.name || fields?.pipelineName || fields?.name || null,
|
|
538
|
+
pipelineId: record?.pipelineId || fields?.pipelineId || extractIdFromFields(fields) || null,
|
|
539
|
+
service: extractWorkflowService(fields),
|
|
540
|
+
status: record?.status || fields?.status || fields?.state || null,
|
|
541
|
+
env: record?.env || fields?.env || fields?.environment || null
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function parseWorkflowFields(fields) {
|
|
546
|
+
if (!fields) return null;
|
|
547
|
+
if (typeof fields === 'object') return fields;
|
|
548
|
+
if (typeof fields !== 'string') return null;
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
return JSON.parse(fields);
|
|
552
|
+
} catch (error) {
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function extractWorkflowService(fields) {
|
|
558
|
+
if (!fields) return null;
|
|
559
|
+
if (Array.isArray(fields.service) && fields.service.length > 0) {
|
|
560
|
+
return fields.service[0];
|
|
561
|
+
}
|
|
562
|
+
if (typeof fields.service === 'string') {
|
|
563
|
+
return fields.service;
|
|
564
|
+
}
|
|
565
|
+
return fields.serviceName || fields.service_name || null;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function extractIdFromFields(fields) {
|
|
569
|
+
if (!fields || typeof fields !== 'object') return null;
|
|
570
|
+
const candidates = [fields.pipelineId, fields.pipeline_id, fields.id];
|
|
571
|
+
for (const candidate of candidates) {
|
|
572
|
+
if (candidate === null || candidate === undefined || candidate === '') continue;
|
|
573
|
+
return String(candidate);
|
|
574
|
+
}
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
|
|
228
578
|
function buildNoAdapterResult(input, params) {
|
|
229
579
|
if (params.workflow && BUILTIN_WORKFLOWS[params.workflow]) {
|
|
230
580
|
const workflow = BUILTIN_WORKFLOWS[params.workflow];
|
|
@@ -234,7 +584,7 @@ function buildNoAdapterResult(input, params) {
|
|
|
234
584
|
message: `Workflow ready: ${workflow.name}`,
|
|
235
585
|
data: {
|
|
236
586
|
workflow: params.workflow,
|
|
237
|
-
|
|
587
|
+
execution: buildBuiltinWorkflowSummary(params.workflow, workflow)
|
|
238
588
|
},
|
|
239
589
|
input,
|
|
240
590
|
timestamp: new Date().toISOString()
|
|
@@ -247,7 +597,12 @@ function buildNoAdapterResult(input, params) {
|
|
|
247
597
|
message: 'Workflow service not configured. Connect friday-auto-flow first.',
|
|
248
598
|
data: {
|
|
249
599
|
status: 'not_configured',
|
|
250
|
-
hint: 'Run `flowmind resource` to review current bindings, then save one like: `flowmind "绑定发布业务 mcp=friday-auto-flow token=xxx env=uat"`'
|
|
600
|
+
hint: 'Run `flowmind resource` to review current bindings, then save one like: `flowmind "绑定发布业务 mcp=friday-auto-flow token=xxx env=uat"`',
|
|
601
|
+
execution: buildAutoFlowHintSummary({
|
|
602
|
+
client: null,
|
|
603
|
+
provider: null,
|
|
604
|
+
binding: null
|
|
605
|
+
})
|
|
251
606
|
},
|
|
252
607
|
input,
|
|
253
608
|
timestamp: new Date().toISOString()
|
|
@@ -267,7 +622,10 @@ async function executeDeploy(workflow, params, input, context) {
|
|
|
267
622
|
binding: workflow.binding,
|
|
268
623
|
status: 'submitted',
|
|
269
624
|
mcpServer: getWorkflowMcpServer(workflow),
|
|
270
|
-
execution,
|
|
625
|
+
execution: summarizeDeployExecution(params, execution, {
|
|
626
|
+
source: 'direct-input',
|
|
627
|
+
pipelineId: params.pipelineId
|
|
628
|
+
}),
|
|
271
629
|
resolution: {
|
|
272
630
|
source: 'direct-input',
|
|
273
631
|
pipelineId: params.pipelineId
|
|
@@ -295,7 +653,11 @@ async function executeDeploy(workflow, params, input, context) {
|
|
|
295
653
|
binding: workflow.binding,
|
|
296
654
|
status: 'submitted',
|
|
297
655
|
mcpServer: getWorkflowMcpServer(workflow),
|
|
298
|
-
execution: executions.length === 1 ? executions[0] : executions,
|
|
656
|
+
execution: summarizeDeployExecution(params, executions.length === 1 ? executions[0] : executions, {
|
|
657
|
+
source: resolvedTargets.source,
|
|
658
|
+
pipelineIds: resolvedTargets.pipelineIds,
|
|
659
|
+
services: resolvedTargets.resolutions
|
|
660
|
+
}),
|
|
299
661
|
resolution: {
|
|
300
662
|
source: resolvedTargets.source,
|
|
301
663
|
pipelineIds: resolvedTargets.pipelineIds,
|
|
@@ -322,7 +684,10 @@ async function executeDeploy(workflow, params, input, context) {
|
|
|
322
684
|
binding: workflow.binding,
|
|
323
685
|
status: 'submitted',
|
|
324
686
|
mcpServer: getWorkflowMcpServer(workflow),
|
|
325
|
-
execution,
|
|
687
|
+
execution: summarizeDeployExecution(params, execution, {
|
|
688
|
+
source: 'batch-fallback',
|
|
689
|
+
lookup
|
|
690
|
+
}),
|
|
326
691
|
resolution: {
|
|
327
692
|
source: 'batch-fallback',
|
|
328
693
|
lookup
|
|
@@ -334,30 +699,18 @@ async function executeDeploy(workflow, params, input, context) {
|
|
|
334
699
|
}
|
|
335
700
|
|
|
336
701
|
async function executeStatus(workflow, params, input) {
|
|
702
|
+
let execution;
|
|
337
703
|
if (params.pipelineId && params.runId) {
|
|
338
|
-
await workflow.client.getPipelineRun(params.pipelineId, params.runId);
|
|
704
|
+
execution = await workflow.client.getPipelineRun(params.pipelineId, params.runId);
|
|
339
705
|
} else if (params.pipelineId) {
|
|
340
|
-
await workflow.client.listPipelineRuns(params.pipelineId, {
|
|
706
|
+
execution = await workflow.client.listPipelineRuns(params.pipelineId, {
|
|
341
707
|
env: params.environment
|
|
342
708
|
});
|
|
343
709
|
} else {
|
|
344
|
-
await workflow.client.listPipelines(buildListPipelineParams(params));
|
|
710
|
+
execution = await workflow.client.listPipelines(buildListPipelineParams(params));
|
|
345
711
|
}
|
|
346
712
|
|
|
347
|
-
return
|
|
348
|
-
type: 'result',
|
|
349
|
-
skill: 'auto-flow',
|
|
350
|
-
message: 'Workflow status: query completed',
|
|
351
|
-
data: {
|
|
352
|
-
action: 'status',
|
|
353
|
-
provider: workflow.provider,
|
|
354
|
-
binding: workflow.binding,
|
|
355
|
-
status: 'completed',
|
|
356
|
-
mcpServer: workflow.binding?.mcpServer || workflow.provider
|
|
357
|
-
},
|
|
358
|
-
input,
|
|
359
|
-
timestamp: new Date().toISOString()
|
|
360
|
-
};
|
|
713
|
+
return execution;
|
|
361
714
|
}
|
|
362
715
|
|
|
363
716
|
function parseFlowParams(input) {
|
|
@@ -29,6 +29,7 @@ module.exports = {
|
|
|
29
29
|
? logService.buildQueryParams(queryInput)
|
|
30
30
|
: queryInput;
|
|
31
31
|
const execution = await logService.queryLogs(queryParams);
|
|
32
|
+
const summary = summarizeLogExecution(execution);
|
|
32
33
|
|
|
33
34
|
return {
|
|
34
35
|
type: 'result',
|
|
@@ -40,7 +41,7 @@ module.exports = {
|
|
|
40
41
|
action: params.traceId ? 'trace' : 'query',
|
|
41
42
|
provider: logService.providerName,
|
|
42
43
|
queryParams,
|
|
43
|
-
execution
|
|
44
|
+
execution: summary
|
|
44
45
|
},
|
|
45
46
|
input,
|
|
46
47
|
timestamp: new Date().toISOString()
|
|
@@ -87,3 +88,95 @@ function buildLogQueryInput(params) {
|
|
|
87
88
|
line: params.limit || 100
|
|
88
89
|
};
|
|
89
90
|
}
|
|
91
|
+
|
|
92
|
+
function summarizeLogExecution(execution) {
|
|
93
|
+
const payload = unwrapPayload(execution);
|
|
94
|
+
const data = payload && typeof payload === 'object' ? payload : {};
|
|
95
|
+
const records = extractRecords(data);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
status: extractStatus(data) || 'ok',
|
|
99
|
+
total: extractTotal(data, records),
|
|
100
|
+
records: records.slice(0, 5).map(summarizeRecord),
|
|
101
|
+
requestId: data.requestId || data.traceId || data.id || null
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function unwrapPayload(payload) {
|
|
106
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
107
|
+
return payload;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const textPayload = extractContentText(payload);
|
|
111
|
+
if (textPayload) {
|
|
112
|
+
return parseMaybeJson(textPayload);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data)) {
|
|
116
|
+
return payload.data;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return payload;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function extractRecords(data) {
|
|
123
|
+
const candidates = [data.logs, data.records, data.items, data.result, data.data];
|
|
124
|
+
for (const candidate of candidates) {
|
|
125
|
+
if (Array.isArray(candidate)) {
|
|
126
|
+
return candidate;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function extractStatus(data) {
|
|
134
|
+
if (!data || typeof data !== 'object') {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (typeof data.status === 'string') {
|
|
139
|
+
return data.status;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (data.success === false) {
|
|
143
|
+
return 'error';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function extractTotal(data, records) {
|
|
150
|
+
return data.total || data.count || records.length || null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function summarizeRecord(record) {
|
|
154
|
+
if (!record || typeof record !== 'object') {
|
|
155
|
+
return record;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
time: record.time || record.timestamp || record.__time__ || null,
|
|
160
|
+
level: record.level || record.severity || null,
|
|
161
|
+
message: record.message || record.content || record.msg || null,
|
|
162
|
+
traceId: record.traceId || record.trace_id || null
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function extractContentText(payload) {
|
|
167
|
+
const content = Array.isArray(payload.content) ? payload.content : [];
|
|
168
|
+
const textItem = content.find((item) => item && typeof item.text === 'string');
|
|
169
|
+
return textItem ? textItem.text : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function parseMaybeJson(value) {
|
|
173
|
+
if (typeof value !== 'string') {
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
return JSON.parse(value);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
return { text: value };
|
|
181
|
+
}
|
|
182
|
+
}
|