@synergenius/flow-weaver 0.20.7 → 0.21.0
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/dist/api/command-runner.d.ts +13 -0
- package/dist/api/command-runner.js +217 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/cli/flow-weaver.mjs +24064 -38374
- package/dist/cli/index.js +0 -60
- package/dist/doc-metadata/extractors/cli-commands.js +37 -56
- package/dist/doc-metadata/extractors/mcp-tools.d.ts +1 -1
- package/dist/doc-metadata/extractors/mcp-tools.js +1 -213
- package/dist/doc-metadata/types.d.ts +2 -0
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/mcp/index.d.ts +1 -5
- package/dist/mcp/index.js +0 -4
- package/dist/mcp/server.js +3 -55
- package/dist/mcp/types.d.ts +0 -50
- package/dist/mcp/types.js +1 -7
- package/dist/mcp/workflow-executor.js +23 -1
- package/dist/parser.js +5 -1
- package/package.json +1 -2
- package/dist/cli/commands/listen.d.ts +0 -16
- package/dist/cli/commands/listen.js +0 -39
- package/dist/cli/commands/tunnel.d.ts +0 -19
- package/dist/cli/commands/tunnel.js +0 -119
- package/dist/cli/commands/ui.d.ts +0 -16
- package/dist/cli/commands/ui.js +0 -130
- package/dist/cli/tunnel/dispatch.d.ts +0 -18
- package/dist/cli/tunnel/dispatch.js +0 -36
- package/dist/cli/tunnel/file-lock.d.ts +0 -9
- package/dist/cli/tunnel/file-lock.js +0 -36
- package/dist/cli/tunnel/handlers/ast-ops.d.ts +0 -10
- package/dist/cli/tunnel/handlers/ast-ops.js +0 -252
- package/dist/cli/tunnel/handlers/execution.d.ts +0 -7
- package/dist/cli/tunnel/handlers/execution.js +0 -89
- package/dist/cli/tunnel/handlers/file-ops.d.ts +0 -7
- package/dist/cli/tunnel/handlers/file-ops.js +0 -204
- package/dist/cli/tunnel/handlers/mutations.d.ts +0 -7
- package/dist/cli/tunnel/handlers/mutations.js +0 -285
- package/dist/cli/tunnel/handlers/stubs.d.ts +0 -7
- package/dist/cli/tunnel/handlers/stubs.js +0 -143
- package/dist/cli/tunnel/handlers/templates.d.ts +0 -7
- package/dist/cli/tunnel/handlers/templates.js +0 -123
- package/dist/cli/tunnel/path-resolver.d.ts +0 -17
- package/dist/cli/tunnel/path-resolver.js +0 -54
- package/dist/defaults.d.ts +0 -3
- package/dist/defaults.js +0 -3
- package/dist/mcp/editor-connection.d.ts +0 -52
- package/dist/mcp/editor-connection.js +0 -142
- package/dist/mcp/event-buffer.d.ts +0 -62
- package/dist/mcp/event-buffer.js +0 -150
- package/dist/mcp/resources.d.ts +0 -14
- package/dist/mcp/resources.js +0 -55
- package/dist/mcp/tools-editor.d.ts +0 -5
- package/dist/mcp/tools-editor.js +0 -283
package/dist/mcp/tools-editor.js
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { makeToolResult, makeErrorResult } from './response-utils.js';
|
|
3
|
-
import { executeWorkflowFromFile } from './workflow-executor.js';
|
|
4
|
-
import { AgentChannel } from './agent-channel.js';
|
|
5
|
-
import { storePendingRun, getPendingRun, removePendingRun, listPendingRuns } from './run-registry.js';
|
|
6
|
-
/**
|
|
7
|
-
* Unwrap Studio ack responses to flatten double-nested results.
|
|
8
|
-
* Studio returns { requestId, success, result: { actualData } },
|
|
9
|
-
* we extract the `result` field to avoid double-nesting in MCP output.
|
|
10
|
-
*/
|
|
11
|
-
function unwrapAckResult(ack) {
|
|
12
|
-
if (ack && typeof ack === 'object' && 'result' in ack && ack.result !== undefined) {
|
|
13
|
-
return ack.result;
|
|
14
|
-
}
|
|
15
|
-
return ack;
|
|
16
|
-
}
|
|
17
|
-
export function registerEditorTools(mcp, connection, buffer) {
|
|
18
|
-
mcp.tool('fw_check_events', 'Get buffered Studio events. Returns and clears the event buffer unless peek=true.', { peek: z.boolean().optional().describe('If true, read events without clearing the buffer') }, async (args) => {
|
|
19
|
-
const events = args.peek ? buffer.peek() : buffer.drain();
|
|
20
|
-
return makeToolResult(events);
|
|
21
|
-
});
|
|
22
|
-
mcp.tool('fw_get_state', 'Get the current Studio/workflow state from Flow Weaver.', {}, async () => {
|
|
23
|
-
if (!connection.isConnected) {
|
|
24
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
25
|
-
}
|
|
26
|
-
const result = await connection.sendCommand('get-state', {});
|
|
27
|
-
return makeToolResult(unwrapAckResult(result));
|
|
28
|
-
});
|
|
29
|
-
mcp.tool('fw_focus_node', 'Select and center a node in Flow Weaver Studio.', { nodeId: z.string().describe('The ID of the node to focus') }, async (args) => {
|
|
30
|
-
if (!connection.isConnected) {
|
|
31
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
32
|
-
}
|
|
33
|
-
const result = await connection.sendCommand('focus-node', { nodeId: args.nodeId });
|
|
34
|
-
return makeToolResult(unwrapAckResult(result));
|
|
35
|
-
});
|
|
36
|
-
mcp.tool('fw_add_node', 'Add a new node to the workflow in Flow Weaver Studio.', {
|
|
37
|
-
nodeTypeName: z.string().describe('The name of the node type to add'),
|
|
38
|
-
nodeTypeDefinition: z
|
|
39
|
-
.record(z.unknown())
|
|
40
|
-
.optional()
|
|
41
|
-
.describe('Optional node type definition object'),
|
|
42
|
-
}, async (args) => {
|
|
43
|
-
if (!connection.isConnected) {
|
|
44
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
45
|
-
}
|
|
46
|
-
const params = { nodeTypeName: args.nodeTypeName };
|
|
47
|
-
if (args.nodeTypeDefinition) {
|
|
48
|
-
params.nodeTypeDefinition = args.nodeTypeDefinition;
|
|
49
|
-
}
|
|
50
|
-
const result = await connection.sendCommand('add-node', params);
|
|
51
|
-
return makeToolResult(unwrapAckResult(result));
|
|
52
|
-
});
|
|
53
|
-
mcp.tool('fw_open_workflow', 'Open a workflow file in Flow Weaver Studio.', { filePath: z.string().describe('The path to the workflow file to open') }, async (args) => {
|
|
54
|
-
if (!connection.isConnected) {
|
|
55
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
56
|
-
}
|
|
57
|
-
const result = await connection.sendCommand('open-workflow', { filePath: args.filePath });
|
|
58
|
-
return makeToolResult(unwrapAckResult(result));
|
|
59
|
-
});
|
|
60
|
-
mcp.tool('fw_send_command', 'Send a generic command to Flow Weaver Studio.', {
|
|
61
|
-
action: z.string().describe('The command action name'),
|
|
62
|
-
params: z.record(z.unknown()).optional().describe('Optional parameters for the command'),
|
|
63
|
-
}, async (args) => {
|
|
64
|
-
if (!connection.isConnected) {
|
|
65
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
66
|
-
}
|
|
67
|
-
const result = await connection.sendCommand(args.action, args.params ?? {});
|
|
68
|
-
return makeToolResult(unwrapAckResult(result));
|
|
69
|
-
});
|
|
70
|
-
mcp.tool('fw_batch', 'Execute a batch of commands with auto-snapshot rollback support.', {
|
|
71
|
-
commands: z
|
|
72
|
-
.array(z.object({
|
|
73
|
-
action: z.string(),
|
|
74
|
-
params: z.record(z.unknown()).optional(),
|
|
75
|
-
}))
|
|
76
|
-
.describe('Array of commands to execute as a batch'),
|
|
77
|
-
}, async (args) => {
|
|
78
|
-
if (!connection.isConnected) {
|
|
79
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
80
|
-
}
|
|
81
|
-
const result = await connection.sendBatch(args.commands);
|
|
82
|
-
return makeToolResult(unwrapAckResult(result));
|
|
83
|
-
});
|
|
84
|
-
// --- New tools ---
|
|
85
|
-
mcp.tool('fw_remove_node', 'Remove a node and its connections from the workflow.', { nodeName: z.string().describe('The name/ID of the node to remove') }, async (args) => {
|
|
86
|
-
if (!connection.isConnected) {
|
|
87
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
88
|
-
}
|
|
89
|
-
const result = await connection.sendCommand('remove-node', { nodeName: args.nodeName });
|
|
90
|
-
return makeToolResult(unwrapAckResult(result));
|
|
91
|
-
});
|
|
92
|
-
mcp.tool('fw_connect', 'Add or remove a connection between ports.', {
|
|
93
|
-
action: z.enum(['add', 'remove']).describe('Whether to add or remove the connection'),
|
|
94
|
-
connection: z
|
|
95
|
-
.object({
|
|
96
|
-
sourceNode: z.string().describe('Source node ID'),
|
|
97
|
-
sourcePort: z.string().describe('Source port name'),
|
|
98
|
-
targetNode: z.string().describe('Target node ID'),
|
|
99
|
-
targetPort: z.string().describe('Target port name'),
|
|
100
|
-
})
|
|
101
|
-
.describe('Connection specification'),
|
|
102
|
-
}, async (args) => {
|
|
103
|
-
if (!connection.isConnected) {
|
|
104
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
105
|
-
}
|
|
106
|
-
const bridgeAction = args.action === 'add' ? 'add-connection' : 'remove-connection';
|
|
107
|
-
const result = await connection.sendCommand(bridgeAction, {
|
|
108
|
-
connection: args.connection,
|
|
109
|
-
});
|
|
110
|
-
return makeToolResult(unwrapAckResult(result));
|
|
111
|
-
});
|
|
112
|
-
mcp.tool('fw_undo_redo', 'Undo or redo the last workflow change.', { action: z.enum(['undo', 'redo']).describe('Whether to undo or redo') }, async (args) => {
|
|
113
|
-
if (!connection.isConnected) {
|
|
114
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
115
|
-
}
|
|
116
|
-
const result = await connection.sendCommand(args.action, {});
|
|
117
|
-
return makeToolResult(unwrapAckResult(result));
|
|
118
|
-
});
|
|
119
|
-
mcp.tool('fw_execute_workflow', 'Run the current workflow with optional parameters and return the result. ' +
|
|
120
|
-
'Includes per-node execution trace by default (STATUS_CHANGED, VARIABLE_SET events) — ' +
|
|
121
|
-
'use includeTrace: false to disable. If the workflow pauses at a waitForAgent node, ' +
|
|
122
|
-
'returns immediately with status "waiting" and a runId — use fw_resume_workflow to continue.', {
|
|
123
|
-
filePath: z
|
|
124
|
-
.string()
|
|
125
|
-
.optional()
|
|
126
|
-
.describe('Path to workflow file. When provided, compiles and executes directly (no Studio needed)'),
|
|
127
|
-
workflowName: z
|
|
128
|
-
.string()
|
|
129
|
-
.optional()
|
|
130
|
-
.describe('Specific workflow function name (for multi-workflow files)'),
|
|
131
|
-
params: z.record(z.unknown()).optional().describe('Optional execution parameters'),
|
|
132
|
-
includeTrace: z
|
|
133
|
-
.boolean()
|
|
134
|
-
.optional()
|
|
135
|
-
.describe('Include execution trace events (default: true)'),
|
|
136
|
-
}, async (args, extra) => {
|
|
137
|
-
// When filePath is provided, compile and execute directly (no Studio needed)
|
|
138
|
-
if (args.filePath) {
|
|
139
|
-
try {
|
|
140
|
-
const channel = new AgentChannel();
|
|
141
|
-
const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
142
|
-
// Send progress notifications for trace events when client supports it
|
|
143
|
-
const progressToken = extra._meta?.progressToken;
|
|
144
|
-
let eventCount = 0;
|
|
145
|
-
const onEvent = progressToken
|
|
146
|
-
? (event) => {
|
|
147
|
-
eventCount++;
|
|
148
|
-
extra.sendNotification({
|
|
149
|
-
method: 'notifications/progress',
|
|
150
|
-
params: {
|
|
151
|
-
progressToken,
|
|
152
|
-
progress: eventCount,
|
|
153
|
-
message: event.type === 'STATUS_CHANGED'
|
|
154
|
-
? `${event.data?.id ?? ''}: ${event.data?.status ?? ''}`
|
|
155
|
-
: event.type,
|
|
156
|
-
},
|
|
157
|
-
}).catch(() => { });
|
|
158
|
-
}
|
|
159
|
-
: undefined;
|
|
160
|
-
const execPromise = executeWorkflowFromFile(args.filePath, args.params, {
|
|
161
|
-
workflowName: args.workflowName,
|
|
162
|
-
includeTrace: args.includeTrace,
|
|
163
|
-
agentChannel: channel,
|
|
164
|
-
onEvent,
|
|
165
|
-
});
|
|
166
|
-
// Race between workflow completing and workflow pausing
|
|
167
|
-
const raceResult = await Promise.race([
|
|
168
|
-
execPromise.then((r) => ({ type: 'completed', result: r })),
|
|
169
|
-
channel.onPause().then((req) => ({ type: 'paused', request: req })),
|
|
170
|
-
]);
|
|
171
|
-
if (raceResult.type === 'paused') {
|
|
172
|
-
// Store the pending run for later resumption
|
|
173
|
-
storePendingRun({
|
|
174
|
-
runId,
|
|
175
|
-
filePath: args.filePath,
|
|
176
|
-
workflowName: args.workflowName,
|
|
177
|
-
executionPromise: execPromise,
|
|
178
|
-
agentChannel: channel,
|
|
179
|
-
request: raceResult.request,
|
|
180
|
-
createdAt: Date.now(),
|
|
181
|
-
tmpFiles: [], // executor manages its own cleanup
|
|
182
|
-
});
|
|
183
|
-
return makeToolResult({
|
|
184
|
-
status: 'waiting',
|
|
185
|
-
runId,
|
|
186
|
-
request: raceResult.request,
|
|
187
|
-
message: 'Workflow paused at waitForAgent node. Use fw_resume_workflow to continue.',
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
// Completed without pausing, return flat result
|
|
191
|
-
return makeToolResult(raceResult.result);
|
|
192
|
-
}
|
|
193
|
-
catch (err) {
|
|
194
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
195
|
-
// Distinguish compile errors from execution errors
|
|
196
|
-
const code = message.includes('Parse errors') ? 'COMPILE_ERROR' : 'EXECUTION_ERROR';
|
|
197
|
-
return makeErrorResult(code, message);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
// No filePath: delegate to Studio via Socket.io (existing behavior)
|
|
201
|
-
if (!connection.isConnected) {
|
|
202
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
203
|
-
}
|
|
204
|
-
const result = await connection.sendCommand('execute-workflow', args.params ?? {});
|
|
205
|
-
return makeToolResult(unwrapAckResult(result));
|
|
206
|
-
});
|
|
207
|
-
mcp.tool('fw_resume_workflow', 'Resume a paused workflow that is waiting for agent input. ' +
|
|
208
|
-
'Use this after fw_execute_workflow returns status "waiting".', {
|
|
209
|
-
runId: z.string().describe('The runId from the waiting execution result'),
|
|
210
|
-
result: z.record(z.unknown()).describe('The agent result to send back to the workflow'),
|
|
211
|
-
}, async (args) => {
|
|
212
|
-
const run = getPendingRun(args.runId);
|
|
213
|
-
if (!run) {
|
|
214
|
-
return makeErrorResult('RUN_NOT_FOUND', `No pending run found with ID "${args.runId}". It may have already completed or been cancelled.`);
|
|
215
|
-
}
|
|
216
|
-
try {
|
|
217
|
-
// Resume the workflow by resolving the agent channel's Promise
|
|
218
|
-
run.agentChannel.resume(args.result);
|
|
219
|
-
// Wait for the workflow to either complete or pause again
|
|
220
|
-
const raceResult = await Promise.race([
|
|
221
|
-
run.executionPromise.then((r) => ({ type: 'completed', result: r })),
|
|
222
|
-
run.agentChannel.onPause().then((req) => ({ type: 'paused', request: req })),
|
|
223
|
-
]);
|
|
224
|
-
if (raceResult.type === 'paused') {
|
|
225
|
-
// Workflow paused again at another waitForAgent node
|
|
226
|
-
run.request = raceResult.request;
|
|
227
|
-
return makeToolResult({
|
|
228
|
-
status: 'waiting',
|
|
229
|
-
runId: args.runId,
|
|
230
|
-
request: raceResult.request,
|
|
231
|
-
message: 'Workflow paused again at another waitForAgent node.',
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
// Workflow completed
|
|
235
|
-
removePendingRun(args.runId);
|
|
236
|
-
return makeToolResult({ status: 'completed', result: raceResult.result });
|
|
237
|
-
}
|
|
238
|
-
catch (err) {
|
|
239
|
-
removePendingRun(args.runId);
|
|
240
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
241
|
-
return makeErrorResult('EXECUTION_ERROR', message);
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
mcp.tool('fw_list_pending_runs', 'List workflows that are currently paused waiting for agent input.', {}, async () => {
|
|
245
|
-
const runs = listPendingRuns();
|
|
246
|
-
return makeToolResult(runs);
|
|
247
|
-
});
|
|
248
|
-
mcp.tool('fw_get_workflow_details', 'Get full workflow structure including nodes, connections, types, and positions.', {}, async () => {
|
|
249
|
-
if (!connection.isConnected) {
|
|
250
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
251
|
-
}
|
|
252
|
-
const result = await connection.sendCommand('get-workflow-details', {});
|
|
253
|
-
return makeToolResult(unwrapAckResult(result));
|
|
254
|
-
});
|
|
255
|
-
mcp.tool('fw_configure_events', 'Configure event include/exclude filters, dedup window, and buffer size. Returns the active config after applying updates.', {
|
|
256
|
-
include: z
|
|
257
|
-
.array(z.string())
|
|
258
|
-
.optional()
|
|
259
|
-
.describe('Event patterns to include (empty = all). Supports trailing * for prefix match'),
|
|
260
|
-
exclude: z
|
|
261
|
-
.array(z.string())
|
|
262
|
-
.optional()
|
|
263
|
-
.describe('Event patterns to exclude (applied after include)'),
|
|
264
|
-
dedupeWindowMs: z
|
|
265
|
-
.number()
|
|
266
|
-
.optional()
|
|
267
|
-
.describe('Collapse same-type events within this window in ms (0 = disabled)'),
|
|
268
|
-
maxBufferSize: z.number().optional().describe('Max events before oldest are evicted'),
|
|
269
|
-
}, async (args) => {
|
|
270
|
-
const partial = {};
|
|
271
|
-
if (args.include !== undefined)
|
|
272
|
-
partial.include = args.include;
|
|
273
|
-
if (args.exclude !== undefined)
|
|
274
|
-
partial.exclude = args.exclude;
|
|
275
|
-
if (args.dedupeWindowMs !== undefined)
|
|
276
|
-
partial.dedupeWindowMs = args.dedupeWindowMs;
|
|
277
|
-
if (args.maxBufferSize !== undefined)
|
|
278
|
-
partial.maxBufferSize = args.maxBufferSize;
|
|
279
|
-
const config = buffer.setFilter(partial);
|
|
280
|
-
return makeToolResult(config);
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
//# sourceMappingURL=tools-editor.js.map
|