@synergenius/flow-weaver 0.27.4 → 0.28.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/query.d.ts +3 -1
- package/dist/api/query.js +25 -1
- package/dist/cli/commands/init-personas.js +8 -8
- package/dist/cli/commands/init.js +18 -12
- package/dist/cli/commands/run.js +6 -0
- package/dist/cli/flow-weaver.mjs +124 -66
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/generator/scope-function-generator.js +68 -43
- package/dist/parser.js +4 -0
- package/package.json +9 -3
package/dist/api/query.d.ts
CHANGED
|
@@ -299,7 +299,9 @@ export declare function findPath(ast: TWorkflowAST, fromNodeId: string, toNodeId
|
|
|
299
299
|
* }
|
|
300
300
|
* ```
|
|
301
301
|
*/
|
|
302
|
-
export declare function getTopologicalOrder(ast: TWorkflowAST
|
|
302
|
+
export declare function getTopologicalOrder(ast: TWorkflowAST, options?: {
|
|
303
|
+
includeScopedChildren?: boolean;
|
|
304
|
+
}): string[];
|
|
303
305
|
/**
|
|
304
306
|
* Group nodes by execution level (nodes at same level can execute in parallel)
|
|
305
307
|
*
|
package/dist/api/query.js
CHANGED
|
@@ -521,7 +521,7 @@ export function findPath(ast, fromNodeId, toNodeId) {
|
|
|
521
521
|
* }
|
|
522
522
|
* ```
|
|
523
523
|
*/
|
|
524
|
-
export function getTopologicalOrder(ast) {
|
|
524
|
+
export function getTopologicalOrder(ast, options) {
|
|
525
525
|
const mainInstances = getMainFlowInstances(ast);
|
|
526
526
|
const mainConnections = getMainFlowConnections(ast);
|
|
527
527
|
const inDegree = new Map();
|
|
@@ -565,6 +565,30 @@ export function getTopologicalOrder(ast) {
|
|
|
565
565
|
if (result.length !== mainInstances.length) {
|
|
566
566
|
throw new Error('Cannot compute topological order: workflow contains cycles');
|
|
567
567
|
}
|
|
568
|
+
// Optionally append scoped children (for debug: breakpoints need to know about them)
|
|
569
|
+
if (options?.includeScopedChildren) {
|
|
570
|
+
const scopedChildren = ast.instances.filter((inst) => isPerPortScopedChild(inst, ast, ast.nodeTypes));
|
|
571
|
+
// Append scoped children after their parent node in the result
|
|
572
|
+
const expanded = [];
|
|
573
|
+
const scopedByParent = new Map();
|
|
574
|
+
for (const child of scopedChildren) {
|
|
575
|
+
if (child.parent) {
|
|
576
|
+
const parentId = child.parent.id;
|
|
577
|
+
if (!scopedByParent.has(parentId)) {
|
|
578
|
+
scopedByParent.set(parentId, []);
|
|
579
|
+
}
|
|
580
|
+
scopedByParent.get(parentId).push(child.id);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
for (const nodeId of result) {
|
|
584
|
+
expanded.push(nodeId);
|
|
585
|
+
const children = scopedByParent.get(nodeId);
|
|
586
|
+
if (children) {
|
|
587
|
+
expanded.push(...children);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return expanded;
|
|
591
|
+
}
|
|
568
592
|
return result;
|
|
569
593
|
}
|
|
570
594
|
/**
|
|
@@ -306,9 +306,9 @@ function printNocodeGuidance(_projectName) {
|
|
|
306
306
|
logger.newline();
|
|
307
307
|
logger.log(` ${logger.bold('Useful commands')}`);
|
|
308
308
|
logger.newline();
|
|
309
|
-
logger.log(` fw run src/*.ts ${logger.dim('Run your workflow')}`);
|
|
310
|
-
logger.log(` fw diagram src/*.ts ${logger.dim('See a visual diagram')}`);
|
|
311
|
-
logger.log(` fw mcp-setup ${logger.dim('Connect more AI editors')}`);
|
|
309
|
+
logger.log(` npx fw run src/*.ts ${logger.dim('Run your workflow')}`);
|
|
310
|
+
logger.log(` npx fw diagram src/*.ts ${logger.dim('See a visual diagram')}`);
|
|
311
|
+
logger.log(` npx fw mcp-setup ${logger.dim('Connect more AI editors')}`);
|
|
312
312
|
}
|
|
313
313
|
function printVibecoderGuidance() {
|
|
314
314
|
logger.newline();
|
|
@@ -326,17 +326,17 @@ function printLowcodeGuidance() {
|
|
|
326
326
|
logger.newline();
|
|
327
327
|
logger.log(` ${logger.bold('Explore and customize')}`);
|
|
328
328
|
logger.newline();
|
|
329
|
-
logger.log(` fw templates ${logger.dim('List all 16 workflow templates')}`);
|
|
330
|
-
logger.log(` fw describe src/*.ts ${logger.dim('See the workflow structure')}`);
|
|
331
|
-
logger.log(` fw docs annotations ${logger.dim('Annotation reference')}`);
|
|
329
|
+
logger.log(` npx fw templates ${logger.dim('List all 16 workflow templates')}`);
|
|
330
|
+
logger.log(` npx fw describe src/*.ts ${logger.dim('See the workflow structure')}`);
|
|
331
|
+
logger.log(` npx fw docs annotations ${logger.dim('Annotation reference')}`);
|
|
332
332
|
logger.newline();
|
|
333
333
|
logger.log(` Your project includes an example in ${logger.highlight('examples/')} to study.`);
|
|
334
334
|
logger.log(` With MCP connected, AI can help modify nodes and connections.`);
|
|
335
335
|
}
|
|
336
336
|
function printExpertGuidance() {
|
|
337
337
|
logger.newline();
|
|
338
|
-
logger.log(` fw mcp-setup ${logger.dim('Connect AI editors (Claude, Cursor, VS Code)')}`);
|
|
339
|
-
logger.log(` fw docs ${logger.dim('Browse reference docs')}`);
|
|
338
|
+
logger.log(` npx fw mcp-setup ${logger.dim('Connect AI editors (Claude, Cursor, VS Code)')}`);
|
|
339
|
+
logger.log(` npx fw docs ${logger.dim('Browse reference docs')}`);
|
|
340
340
|
}
|
|
341
341
|
/** Pad a filename to align descriptions */
|
|
342
342
|
function pad(displayName, width) {
|
|
@@ -350,16 +350,19 @@ export function generateProjectFiles(projectName, template, format = 'esm', pers
|
|
|
350
350
|
'',
|
|
351
351
|
`import { ${workflowName} } from './${workflowJsFile}';`,
|
|
352
352
|
'',
|
|
353
|
-
'
|
|
354
|
-
` const result = ${workflowName}(true, { data: { message: 'hello world' } });`,
|
|
355
|
-
' console.log(result);',
|
|
356
|
-
'}
|
|
353
|
+
'async function main() {',
|
|
354
|
+
` const result = await ${workflowName}(true, { data: { message: 'hello world' } });`,
|
|
355
|
+
' console.log(JSON.stringify(result, null, 2));',
|
|
356
|
+
'}',
|
|
357
|
+
'',
|
|
358
|
+
'main().catch((e) => {',
|
|
357
359
|
" if (e instanceof Error && e.message.startsWith('Compile with:')) {",
|
|
358
360
|
" console.error('Workflow not compiled yet. Run: npm run dev');",
|
|
359
361
|
' process.exit(1);',
|
|
360
362
|
' }',
|
|
361
|
-
'
|
|
362
|
-
'
|
|
363
|
+
' console.error(e);',
|
|
364
|
+
' process.exit(1);',
|
|
365
|
+
'});',
|
|
363
366
|
'',
|
|
364
367
|
].join('\n');
|
|
365
368
|
}
|
|
@@ -376,16 +379,19 @@ export function generateProjectFiles(projectName, template, format = 'esm', pers
|
|
|
376
379
|
'',
|
|
377
380
|
`const { ${workflowName} } = require('./${workflowJsFile}');`,
|
|
378
381
|
'',
|
|
379
|
-
'
|
|
380
|
-
` const result = ${workflowName}(true, { data: { message: 'hello world' } });`,
|
|
381
|
-
' console.log(result);',
|
|
382
|
-
'}
|
|
382
|
+
'async function main() {',
|
|
383
|
+
` const result = await ${workflowName}(true, { data: { message: 'hello world' } });`,
|
|
384
|
+
' console.log(JSON.stringify(result, null, 2));',
|
|
385
|
+
'}',
|
|
386
|
+
'',
|
|
387
|
+
'main().catch((e) => {',
|
|
383
388
|
" if (e instanceof Error && e.message.startsWith('Compile with:')) {",
|
|
384
389
|
" console.error('Workflow not compiled yet. Run: npm run dev');",
|
|
385
390
|
' process.exit(1);',
|
|
386
391
|
' }',
|
|
387
|
-
'
|
|
388
|
-
'
|
|
392
|
+
' console.error(e);',
|
|
393
|
+
' process.exit(1);',
|
|
394
|
+
'});',
|
|
389
395
|
'',
|
|
390
396
|
].join('\n');
|
|
391
397
|
}
|
package/dist/cli/commands/run.js
CHANGED
|
@@ -339,6 +339,12 @@ async function runCommandInner(input, options) {
|
|
|
339
339
|
logger.newline();
|
|
340
340
|
logger.section('Result');
|
|
341
341
|
logger.log(JSON.stringify(result.result, null, 2));
|
|
342
|
+
// Hint when the workflow failed and no params were provided
|
|
343
|
+
const resultObj = result.result;
|
|
344
|
+
if (resultObj?.onFailure === true && !options.params && !options.paramsFile) {
|
|
345
|
+
logger.newline();
|
|
346
|
+
logger.warn('Tip: use --params to provide input. Run `fw describe <file>` to see expected inputs.');
|
|
347
|
+
}
|
|
342
348
|
// Show trace summary only when --trace is explicitly set (not on --stream, which already printed live)
|
|
343
349
|
if (options.trace && !options.stream && result.trace && result.trace.length > 0) {
|
|
344
350
|
logger.newline();
|
package/dist/cli/flow-weaver.mjs
CHANGED
|
@@ -5987,7 +5987,7 @@ var VERSION;
|
|
|
5987
5987
|
var init_generated_version = __esm({
|
|
5988
5988
|
"src/generated-version.ts"() {
|
|
5989
5989
|
"use strict";
|
|
5990
|
-
VERSION = "0.
|
|
5990
|
+
VERSION = "0.28.0";
|
|
5991
5991
|
}
|
|
5992
5992
|
});
|
|
5993
5993
|
|
|
@@ -6645,18 +6645,28 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
|
|
|
6645
6645
|
}
|
|
6646
6646
|
const safeChildId = toValidIdentifier(child.id);
|
|
6647
6647
|
const awaitPrefix = isAsync2 ? "await " : "";
|
|
6648
|
+
const emitDebugHooks = !production;
|
|
6649
|
+
let childIndent = " ";
|
|
6648
6650
|
lines.push(``);
|
|
6649
6651
|
lines.push(` // Execute: ${child.id} (${child.nodeType})`);
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
lines.push(
|
|
6657
|
-
|
|
6658
|
-
lines.push(
|
|
6659
|
-
lines.push(
|
|
6652
|
+
if (emitDebugHooks) {
|
|
6653
|
+
const awaitHook = isAsync2 ? "await " : "";
|
|
6654
|
+
lines.push(` let ${safeChildId}Idx: number;`);
|
|
6655
|
+
lines.push(` if (${awaitHook}__ctrl__.beforeNode('${child.id}', scopedCtx)) {`);
|
|
6656
|
+
childIndent = " ";
|
|
6657
|
+
}
|
|
6658
|
+
lines.push(`${childIndent}scopedCtx.checkAborted('${child.id}');`);
|
|
6659
|
+
const idxDecl = emitDebugHooks ? "" : "const ";
|
|
6660
|
+
lines.push(`${childIndent}${idxDecl}${safeChildId}Idx = scopedCtx.addExecution('${child.id}');`);
|
|
6661
|
+
lines.push(`${childIndent}if (typeof globalThis !== 'undefined') (globalThis as unknown as { __fw_current_node_id__?: string }).__fw_current_node_id__ = '${child.id}';`);
|
|
6662
|
+
lines.push(`${childIndent}${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
|
|
6663
|
+
lines.push(`${childIndent} nodeTypeName: '${child.nodeType}',`);
|
|
6664
|
+
lines.push(`${childIndent} id: '${child.id}',`);
|
|
6665
|
+
lines.push(`${childIndent} executionIndex: ${safeChildId}Idx,`);
|
|
6666
|
+
lines.push(`${childIndent} status: 'RUNNING',`);
|
|
6667
|
+
lines.push(`${childIndent}});`);
|
|
6668
|
+
lines.push(`${childIndent}try {`);
|
|
6669
|
+
const tryIndent = `${childIndent} `;
|
|
6660
6670
|
const argLines = [];
|
|
6661
6671
|
const getCall = isAsync2 ? "await scopedCtx.getVariable" : "scopedCtx.getVariable";
|
|
6662
6672
|
const childSetCall = isAsync2 ? `await scopedCtx.setVariable` : `scopedCtx.setVariable`;
|
|
@@ -6673,10 +6683,10 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
|
|
|
6673
6683
|
const targetPortDef = childNodeType.inputs[targetPort];
|
|
6674
6684
|
const portType = targetPortDef ? mapToTypeScript(targetPortDef.dataType, targetPortDef.tsType) : "unknown";
|
|
6675
6685
|
argLines.push(
|
|
6676
|
-
|
|
6686
|
+
`${tryIndent}const ${varName} = ${getCall}({ id: '${parentNodeId}', portName: '${conn.from.port}', executionIndex: ${scopeParamIdxVar} }) as ${portType};`
|
|
6677
6687
|
);
|
|
6678
6688
|
argLines.push(
|
|
6679
|
-
|
|
6689
|
+
`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${targetPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${varName});`
|
|
6680
6690
|
);
|
|
6681
6691
|
preHandledPorts.add(targetPort);
|
|
6682
6692
|
}
|
|
@@ -6686,7 +6696,7 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
|
|
|
6686
6696
|
workflow: scopeWorkflow,
|
|
6687
6697
|
id: child.id,
|
|
6688
6698
|
lines: argLines,
|
|
6689
|
-
indent:
|
|
6699
|
+
indent: tryIndent,
|
|
6690
6700
|
getCall,
|
|
6691
6701
|
isAsync: isAsync2,
|
|
6692
6702
|
instanceParent: child.parent ? `${child.parent.id}.${child.parent.scope}` : void 0,
|
|
@@ -6699,11 +6709,11 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
|
|
|
6699
6709
|
argLines.forEach((line) => lines.push(line));
|
|
6700
6710
|
if (childNodeType.expression) {
|
|
6701
6711
|
lines.push(
|
|
6702
|
-
|
|
6712
|
+
`${tryIndent}const ${safeChildId}Result = ${awaitPrefix}${child.nodeType}(${args.join(", ")});`
|
|
6703
6713
|
);
|
|
6704
6714
|
} else {
|
|
6705
6715
|
lines.push(
|
|
6706
|
-
|
|
6716
|
+
`${tryIndent}const ${safeChildId}Result = ${awaitPrefix}${child.nodeType}(${args.join(", ")});`
|
|
6707
6717
|
);
|
|
6708
6718
|
}
|
|
6709
6719
|
if (childNodeType.expression) {
|
|
@@ -6711,49 +6721,56 @@ function generateScopeFunctionClosure(scopeName, parentNodeId, parentNodeType, w
|
|
|
6711
6721
|
const portDef = childNodeType.outputs[outPort];
|
|
6712
6722
|
if (portDef.failure || isFailurePort(outPort)) {
|
|
6713
6723
|
lines.push(
|
|
6714
|
-
|
|
6724
|
+
`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, false);`
|
|
6715
6725
|
);
|
|
6716
6726
|
} else if (portDef.isControlFlow || isSuccessPort(outPort)) {
|
|
6717
6727
|
lines.push(
|
|
6718
|
-
|
|
6728
|
+
`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, true);`
|
|
6719
6729
|
);
|
|
6720
6730
|
} else {
|
|
6721
6731
|
lines.push(
|
|
6722
|
-
|
|
6732
|
+
`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${safeChildId}Result.${outPort});`
|
|
6723
6733
|
);
|
|
6724
6734
|
}
|
|
6725
6735
|
});
|
|
6726
6736
|
} else {
|
|
6727
6737
|
Object.keys(childNodeType.outputs || {}).forEach((outPort) => {
|
|
6728
6738
|
lines.push(
|
|
6729
|
-
|
|
6739
|
+
`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${safeChildId}Result.${outPort});`
|
|
6730
6740
|
);
|
|
6731
6741
|
});
|
|
6732
6742
|
}
|
|
6733
|
-
lines.push(
|
|
6734
|
-
lines.push(
|
|
6735
|
-
lines.push(
|
|
6736
|
-
lines.push(
|
|
6737
|
-
lines.push(
|
|
6738
|
-
lines.push(
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
lines.push(
|
|
6744
|
-
lines.push(
|
|
6745
|
-
lines.push(`
|
|
6746
|
-
lines.push(
|
|
6747
|
-
lines.push(
|
|
6748
|
-
lines.push(
|
|
6749
|
-
lines.push(
|
|
6750
|
-
lines.push(
|
|
6751
|
-
lines.push(
|
|
6752
|
-
lines.push(
|
|
6753
|
-
lines.push(
|
|
6754
|
-
lines.push(
|
|
6755
|
-
lines.push(
|
|
6756
|
-
lines.push(
|
|
6743
|
+
lines.push(`${tryIndent}${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
|
|
6744
|
+
lines.push(`${tryIndent} nodeTypeName: '${child.nodeType}',`);
|
|
6745
|
+
lines.push(`${tryIndent} id: '${child.id}',`);
|
|
6746
|
+
lines.push(`${tryIndent} executionIndex: ${safeChildId}Idx,`);
|
|
6747
|
+
lines.push(`${tryIndent} status: 'SUCCEEDED',`);
|
|
6748
|
+
lines.push(`${tryIndent}});`);
|
|
6749
|
+
if (emitDebugHooks) {
|
|
6750
|
+
const awaitHook = isAsync2 ? "await " : "";
|
|
6751
|
+
lines.push(`${tryIndent}${awaitHook}__ctrl__.afterNode('${child.id}', scopedCtx);`);
|
|
6752
|
+
}
|
|
6753
|
+
lines.push(`${childIndent}} catch (error: unknown) {`);
|
|
6754
|
+
lines.push(`${tryIndent}const isCancellation = CancellationError.isCancellationError(error);`);
|
|
6755
|
+
lines.push(`${tryIndent}${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
|
|
6756
|
+
lines.push(`${tryIndent} nodeTypeName: '${child.nodeType}',`);
|
|
6757
|
+
lines.push(`${tryIndent} id: '${child.id}',`);
|
|
6758
|
+
lines.push(`${tryIndent} executionIndex: ${safeChildId}Idx,`);
|
|
6759
|
+
lines.push(`${tryIndent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
|
|
6760
|
+
lines.push(`${tryIndent}});`);
|
|
6761
|
+
lines.push(`${tryIndent}if (!isCancellation) {`);
|
|
6762
|
+
lines.push(`${tryIndent} scopedCtx.sendLogErrorEvent({`);
|
|
6763
|
+
lines.push(`${tryIndent} nodeTypeName: '${child.nodeType}',`);
|
|
6764
|
+
lines.push(`${tryIndent} id: '${child.id}',`);
|
|
6765
|
+
lines.push(`${tryIndent} executionIndex: ${safeChildId}Idx,`);
|
|
6766
|
+
lines.push(`${tryIndent} error: error instanceof Error ? error.message : String(error),`);
|
|
6767
|
+
lines.push(`${tryIndent} });`);
|
|
6768
|
+
lines.push(`${tryIndent}}`);
|
|
6769
|
+
lines.push(`${tryIndent}throw error;`);
|
|
6770
|
+
lines.push(`${childIndent}}`);
|
|
6771
|
+
if (emitDebugHooks) {
|
|
6772
|
+
lines.push(` }`);
|
|
6773
|
+
}
|
|
6757
6774
|
});
|
|
6758
6775
|
lines.push(``);
|
|
6759
6776
|
}
|
|
@@ -29005,6 +29022,10 @@ ${fn.getText()}` : fn.getText();
|
|
|
29005
29022
|
const nextInputs = getInputPorts(nextId);
|
|
29006
29023
|
for (const [inputName] of Object.entries(nextInputs)) {
|
|
29007
29024
|
if (isControlFlowPort(inputName)) continue;
|
|
29025
|
+
const alreadyConnected = connections.some(
|
|
29026
|
+
(c) => c.to.node === nextId && c.to.port === inputName
|
|
29027
|
+
);
|
|
29028
|
+
if (alreadyConnected) continue;
|
|
29008
29029
|
for (let j2 = i; j2 >= 0; j2--) {
|
|
29009
29030
|
const ancestorId = steps[j2].node;
|
|
29010
29031
|
const ancestorOutputs = getOutputPorts(ancestorId);
|
|
@@ -37919,7 +37940,7 @@ function getDependents(ast, nodeId) {
|
|
|
37919
37940
|
});
|
|
37920
37941
|
return Array.from(dependents);
|
|
37921
37942
|
}
|
|
37922
|
-
function getTopologicalOrder(ast) {
|
|
37943
|
+
function getTopologicalOrder(ast, options) {
|
|
37923
37944
|
const mainInstances = getMainFlowInstances(ast);
|
|
37924
37945
|
const mainConnections = getMainFlowConnections(ast);
|
|
37925
37946
|
const inDegree = /* @__PURE__ */ new Map();
|
|
@@ -37958,6 +37979,30 @@ function getTopologicalOrder(ast) {
|
|
|
37958
37979
|
if (result.length !== mainInstances.length) {
|
|
37959
37980
|
throw new Error("Cannot compute topological order: workflow contains cycles");
|
|
37960
37981
|
}
|
|
37982
|
+
if (options?.includeScopedChildren) {
|
|
37983
|
+
const scopedChildren = ast.instances.filter(
|
|
37984
|
+
(inst) => isPerPortScopedChild(inst, ast, ast.nodeTypes)
|
|
37985
|
+
);
|
|
37986
|
+
const expanded = [];
|
|
37987
|
+
const scopedByParent = /* @__PURE__ */ new Map();
|
|
37988
|
+
for (const child of scopedChildren) {
|
|
37989
|
+
if (child.parent) {
|
|
37990
|
+
const parentId = child.parent.id;
|
|
37991
|
+
if (!scopedByParent.has(parentId)) {
|
|
37992
|
+
scopedByParent.set(parentId, []);
|
|
37993
|
+
}
|
|
37994
|
+
scopedByParent.get(parentId).push(child.id);
|
|
37995
|
+
}
|
|
37996
|
+
}
|
|
37997
|
+
for (const nodeId of result) {
|
|
37998
|
+
expanded.push(nodeId);
|
|
37999
|
+
const children = scopedByParent.get(nodeId);
|
|
38000
|
+
if (children) {
|
|
38001
|
+
expanded.push(...children);
|
|
38002
|
+
}
|
|
38003
|
+
}
|
|
38004
|
+
return expanded;
|
|
38005
|
+
}
|
|
37961
38006
|
return result;
|
|
37962
38007
|
}
|
|
37963
38008
|
function findIsolatedNodes(ast) {
|
|
@@ -58608,9 +58653,9 @@ function printNocodeGuidance(_projectName) {
|
|
|
58608
58653
|
logger.newline();
|
|
58609
58654
|
logger.log(` ${logger.bold("Useful commands")}`);
|
|
58610
58655
|
logger.newline();
|
|
58611
|
-
logger.log(` fw run src/*.ts ${logger.dim("Run your workflow")}`);
|
|
58612
|
-
logger.log(` fw diagram src/*.ts ${logger.dim("See a visual diagram")}`);
|
|
58613
|
-
logger.log(` fw mcp-setup ${logger.dim("Connect more AI editors")}`);
|
|
58656
|
+
logger.log(` npx fw run src/*.ts ${logger.dim("Run your workflow")}`);
|
|
58657
|
+
logger.log(` npx fw diagram src/*.ts ${logger.dim("See a visual diagram")}`);
|
|
58658
|
+
logger.log(` npx fw mcp-setup ${logger.dim("Connect more AI editors")}`);
|
|
58614
58659
|
}
|
|
58615
58660
|
function printVibecoderGuidance() {
|
|
58616
58661
|
logger.newline();
|
|
@@ -58628,17 +58673,17 @@ function printLowcodeGuidance() {
|
|
|
58628
58673
|
logger.newline();
|
|
58629
58674
|
logger.log(` ${logger.bold("Explore and customize")}`);
|
|
58630
58675
|
logger.newline();
|
|
58631
|
-
logger.log(` fw templates ${logger.dim("List all 16 workflow templates")}`);
|
|
58632
|
-
logger.log(` fw describe src/*.ts ${logger.dim("See the workflow structure")}`);
|
|
58633
|
-
logger.log(` fw docs annotations ${logger.dim("Annotation reference")}`);
|
|
58676
|
+
logger.log(` npx fw templates ${logger.dim("List all 16 workflow templates")}`);
|
|
58677
|
+
logger.log(` npx fw describe src/*.ts ${logger.dim("See the workflow structure")}`);
|
|
58678
|
+
logger.log(` npx fw docs annotations ${logger.dim("Annotation reference")}`);
|
|
58634
58679
|
logger.newline();
|
|
58635
58680
|
logger.log(` Your project includes an example in ${logger.highlight("examples/")} to study.`);
|
|
58636
58681
|
logger.log(` With MCP connected, AI can help modify nodes and connections.`);
|
|
58637
58682
|
}
|
|
58638
58683
|
function printExpertGuidance() {
|
|
58639
58684
|
logger.newline();
|
|
58640
|
-
logger.log(` fw mcp-setup ${logger.dim("Connect AI editors (Claude, Cursor, VS Code)")}`);
|
|
58641
|
-
logger.log(` fw docs ${logger.dim("Browse reference docs")}`);
|
|
58685
|
+
logger.log(` npx fw mcp-setup ${logger.dim("Connect AI editors (Claude, Cursor, VS Code)")}`);
|
|
58686
|
+
logger.log(` npx fw docs ${logger.dim("Browse reference docs")}`);
|
|
58642
58687
|
}
|
|
58643
58688
|
function pad(displayName, width) {
|
|
58644
58689
|
const padding = Math.max(1, width - displayName.length);
|
|
@@ -59588,16 +59633,19 @@ ${workflowCode}`;
|
|
|
59588
59633
|
"",
|
|
59589
59634
|
`import { ${workflowName} } from './${workflowJsFile}';`,
|
|
59590
59635
|
"",
|
|
59591
|
-
"
|
|
59592
|
-
` const result = ${workflowName}(true, { data: { message: 'hello world' } });`,
|
|
59593
|
-
" console.log(result);",
|
|
59594
|
-
"}
|
|
59636
|
+
"async function main() {",
|
|
59637
|
+
` const result = await ${workflowName}(true, { data: { message: 'hello world' } });`,
|
|
59638
|
+
" console.log(JSON.stringify(result, null, 2));",
|
|
59639
|
+
"}",
|
|
59640
|
+
"",
|
|
59641
|
+
"main().catch((e) => {",
|
|
59595
59642
|
" if (e instanceof Error && e.message.startsWith('Compile with:')) {",
|
|
59596
59643
|
" console.error('Workflow not compiled yet. Run: npm run dev');",
|
|
59597
59644
|
" process.exit(1);",
|
|
59598
59645
|
" }",
|
|
59599
|
-
"
|
|
59600
|
-
"
|
|
59646
|
+
" console.error(e);",
|
|
59647
|
+
" process.exit(1);",
|
|
59648
|
+
"});",
|
|
59601
59649
|
""
|
|
59602
59650
|
].join("\n");
|
|
59603
59651
|
} else {
|
|
@@ -59613,16 +59661,19 @@ ${workflowCode}`;
|
|
|
59613
59661
|
"",
|
|
59614
59662
|
`const { ${workflowName} } = require('./${workflowJsFile}');`,
|
|
59615
59663
|
"",
|
|
59616
|
-
"
|
|
59617
|
-
` const result = ${workflowName}(true, { data: { message: 'hello world' } });`,
|
|
59618
|
-
" console.log(result);",
|
|
59619
|
-
"}
|
|
59664
|
+
"async function main() {",
|
|
59665
|
+
` const result = await ${workflowName}(true, { data: { message: 'hello world' } });`,
|
|
59666
|
+
" console.log(JSON.stringify(result, null, 2));",
|
|
59667
|
+
"}",
|
|
59668
|
+
"",
|
|
59669
|
+
"main().catch((e) => {",
|
|
59620
59670
|
" if (e instanceof Error && e.message.startsWith('Compile with:')) {",
|
|
59621
59671
|
" console.error('Workflow not compiled yet. Run: npm run dev');",
|
|
59622
59672
|
" process.exit(1);",
|
|
59623
59673
|
" }",
|
|
59624
|
-
"
|
|
59625
|
-
"
|
|
59674
|
+
" console.error(e);",
|
|
59675
|
+
" process.exit(1);",
|
|
59676
|
+
"});",
|
|
59626
59677
|
""
|
|
59627
59678
|
].join("\n");
|
|
59628
59679
|
}
|
|
@@ -84560,6 +84611,13 @@ async function runCommandInner(input, options) {
|
|
|
84560
84611
|
logger.newline();
|
|
84561
84612
|
logger.section("Result");
|
|
84562
84613
|
logger.log(JSON.stringify(result.result, null, 2));
|
|
84614
|
+
const resultObj = result.result;
|
|
84615
|
+
if (resultObj?.onFailure === true && !options.params && !options.paramsFile) {
|
|
84616
|
+
logger.newline();
|
|
84617
|
+
logger.warn(
|
|
84618
|
+
"Tip: use --params to provide input. Run `fw describe <file>` to see expected inputs."
|
|
84619
|
+
);
|
|
84620
|
+
}
|
|
84563
84621
|
if (options.trace && !options.stream && result.trace && result.trace.length > 0) {
|
|
84564
84622
|
logger.newline();
|
|
84565
84623
|
logger.section("Trace");
|
|
@@ -88468,7 +88526,7 @@ function parseIntStrict(value) {
|
|
|
88468
88526
|
// src/cli/index.ts
|
|
88469
88527
|
init_logger();
|
|
88470
88528
|
init_error_utils();
|
|
88471
|
-
var version2 = true ? "0.
|
|
88529
|
+
var version2 = true ? "0.28.0" : "0.0.0-dev";
|
|
88472
88530
|
var program2 = new Command();
|
|
88473
88531
|
program2.name("fw").description("Flow Weaver Annotations - Compile and validate workflow files").option("-v, --version", "Output the current version").option("--no-color", "Disable colors").option("--color", "Force colors").on("option:version", () => {
|
|
88474
88532
|
logger.banner(version2);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.28.0";
|
|
2
2
|
//# sourceMappingURL=generated-version.d.ts.map
|
|
@@ -187,18 +187,34 @@ export function generateScopeFunctionClosure(scopeName, parentNodeId, parentNode
|
|
|
187
187
|
}
|
|
188
188
|
const safeChildId = toValidIdentifier(child.id);
|
|
189
189
|
const awaitPrefix = isAsync ? 'await ' : '';
|
|
190
|
+
const emitDebugHooks = !production;
|
|
191
|
+
// Indentation increases when debug hooks wrap the child block
|
|
192
|
+
let childIndent = ' ';
|
|
190
193
|
lines.push(``);
|
|
191
194
|
lines.push(` // Execute: ${child.id} (${child.nodeType})`);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
lines.push(
|
|
195
|
+
// Debug controller: beforeNode hook for scoped children
|
|
196
|
+
// When enabled, wraps the child execution so breakpoints can pause on scoped nodes.
|
|
197
|
+
if (emitDebugHooks) {
|
|
198
|
+
const awaitHook = isAsync ? 'await ' : '';
|
|
199
|
+
// Hoist Idx declaration before the if block so it stays in scope after
|
|
200
|
+
lines.push(` let ${safeChildId}Idx: number;`);
|
|
201
|
+
lines.push(` if (${awaitHook}__ctrl__.beforeNode('${child.id}', scopedCtx)) {`);
|
|
202
|
+
childIndent = ' ';
|
|
203
|
+
}
|
|
204
|
+
lines.push(`${childIndent}scopedCtx.checkAborted('${child.id}');`);
|
|
205
|
+
// Use assignment when debug hooks hoist the declaration, const otherwise
|
|
206
|
+
const idxDecl = emitDebugHooks ? '' : 'const ';
|
|
207
|
+
lines.push(`${childIndent}${idxDecl}${safeChildId}Idx = scopedCtx.addExecution('${child.id}');`);
|
|
208
|
+
lines.push(`${childIndent}if (typeof globalThis !== 'undefined') (globalThis as unknown as { __fw_current_node_id__?: string }).__fw_current_node_id__ = '${child.id}';`);
|
|
209
|
+
lines.push(`${childIndent}${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
|
|
210
|
+
lines.push(`${childIndent} nodeTypeName: '${child.nodeType}',`);
|
|
211
|
+
lines.push(`${childIndent} id: '${child.id}',`);
|
|
212
|
+
lines.push(`${childIndent} executionIndex: ${safeChildId}Idx,`);
|
|
213
|
+
lines.push(`${childIndent} status: 'RUNNING',`);
|
|
214
|
+
lines.push(`${childIndent}});`);
|
|
215
|
+
lines.push(`${childIndent}try {`);
|
|
216
|
+
// Inner indentation: inside try block (childIndent + 2 spaces for try body)
|
|
217
|
+
const tryIndent = `${childIndent} `;
|
|
202
218
|
// Pre-handle connections from parent scoped OUTPUT ports with correct index variables
|
|
203
219
|
const argLines = [];
|
|
204
220
|
const getCall = isAsync ? 'await scopedCtx.getVariable' : 'scopedCtx.getVariable';
|
|
@@ -218,9 +234,9 @@ export function generateScopeFunctionClosure(scopeName, parentNodeId, parentNode
|
|
|
218
234
|
const portType = targetPortDef
|
|
219
235
|
? mapToTypeScript(targetPortDef.dataType, targetPortDef.tsType)
|
|
220
236
|
: 'unknown';
|
|
221
|
-
argLines.push(
|
|
237
|
+
argLines.push(`${tryIndent}const ${varName} = ${getCall}({ id: '${parentNodeId}', portName: '${conn.from.port}', executionIndex: ${scopeParamIdxVar} }) as ${portType};`);
|
|
222
238
|
// Emit VARIABLE_SET for the child's INPUT port so breakpoints and inspection work
|
|
223
|
-
argLines.push(
|
|
239
|
+
argLines.push(`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${targetPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${varName});`);
|
|
224
240
|
preHandledPorts.add(targetPort);
|
|
225
241
|
}
|
|
226
242
|
});
|
|
@@ -230,7 +246,7 @@ export function generateScopeFunctionClosure(scopeName, parentNodeId, parentNode
|
|
|
230
246
|
workflow: scopeWorkflow,
|
|
231
247
|
id: child.id,
|
|
232
248
|
lines: argLines,
|
|
233
|
-
indent:
|
|
249
|
+
indent: tryIndent,
|
|
234
250
|
getCall,
|
|
235
251
|
isAsync,
|
|
236
252
|
instanceParent: child.parent ? `${child.parent.id}.${child.parent.scope}` : undefined,
|
|
@@ -245,11 +261,11 @@ export function generateScopeFunctionClosure(scopeName, parentNodeId, parentNode
|
|
|
245
261
|
// Call the child node function
|
|
246
262
|
if (childNodeType.expression) {
|
|
247
263
|
// Expression nodes use original signature (positional args, no execute)
|
|
248
|
-
lines.push(
|
|
264
|
+
lines.push(`${tryIndent}const ${safeChildId}Result = ${awaitPrefix}${child.nodeType}(${args.join(', ')});`);
|
|
249
265
|
}
|
|
250
266
|
else {
|
|
251
267
|
// Regular node call with positional arguments
|
|
252
|
-
lines.push(
|
|
268
|
+
lines.push(`${tryIndent}const ${safeChildId}Result = ${awaitPrefix}${child.nodeType}(${args.join(', ')});`);
|
|
253
269
|
}
|
|
254
270
|
// Store outputs (including onSuccess/onFailure for debugging)
|
|
255
271
|
// Expression nodes don't return onSuccess/onFailure — hardcode them
|
|
@@ -258,48 +274,57 @@ export function generateScopeFunctionClosure(scopeName, parentNodeId, parentNode
|
|
|
258
274
|
const portDef = childNodeType.outputs[outPort];
|
|
259
275
|
if (portDef.failure || isFailurePort(outPort)) {
|
|
260
276
|
// Failure ports always false on success (expression nodes always succeed)
|
|
261
|
-
lines.push(
|
|
277
|
+
lines.push(`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, false);`);
|
|
262
278
|
}
|
|
263
279
|
else if (portDef.isControlFlow || isSuccessPort(outPort)) {
|
|
264
280
|
// Success control flow ports always true (expression nodes always succeed)
|
|
265
|
-
lines.push(
|
|
281
|
+
lines.push(`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, true);`);
|
|
266
282
|
}
|
|
267
283
|
else {
|
|
268
284
|
// Data outputs read from result object
|
|
269
|
-
lines.push(
|
|
285
|
+
lines.push(`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${safeChildId}Result.${outPort});`);
|
|
270
286
|
}
|
|
271
287
|
});
|
|
272
288
|
}
|
|
273
289
|
else {
|
|
274
290
|
Object.keys(childNodeType.outputs || {}).forEach((outPort) => {
|
|
275
|
-
lines.push(
|
|
291
|
+
lines.push(`${tryIndent}${childSetCall}({ id: '${child.id}', portName: '${outPort}', executionIndex: ${safeChildId}Idx, nodeTypeName: '${child.nodeType}' }, ${safeChildId}Result.${outPort});`);
|
|
276
292
|
});
|
|
277
293
|
}
|
|
278
294
|
// Add SUCCEEDED status event
|
|
279
|
-
lines.push(
|
|
280
|
-
lines.push(
|
|
281
|
-
lines.push(
|
|
282
|
-
lines.push(
|
|
283
|
-
lines.push(
|
|
284
|
-
lines.push(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
lines.push(
|
|
291
|
-
lines.push(
|
|
292
|
-
lines.push(`
|
|
293
|
-
lines.push(
|
|
294
|
-
lines.push(
|
|
295
|
-
lines.push(
|
|
296
|
-
lines.push(
|
|
297
|
-
lines.push(
|
|
298
|
-
lines.push(
|
|
299
|
-
lines.push(`
|
|
300
|
-
lines.push(
|
|
301
|
-
lines.push(
|
|
302
|
-
lines.push(
|
|
295
|
+
lines.push(`${tryIndent}${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
|
|
296
|
+
lines.push(`${tryIndent} nodeTypeName: '${child.nodeType}',`);
|
|
297
|
+
lines.push(`${tryIndent} id: '${child.id}',`);
|
|
298
|
+
lines.push(`${tryIndent} executionIndex: ${safeChildId}Idx,`);
|
|
299
|
+
lines.push(`${tryIndent} status: 'SUCCEEDED',`);
|
|
300
|
+
lines.push(`${tryIndent}});`);
|
|
301
|
+
// Debug controller: afterNode hook for scoped children
|
|
302
|
+
if (emitDebugHooks) {
|
|
303
|
+
const awaitHook = isAsync ? 'await ' : '';
|
|
304
|
+
lines.push(`${tryIndent}${awaitHook}__ctrl__.afterNode('${child.id}', scopedCtx);`);
|
|
305
|
+
}
|
|
306
|
+
lines.push(`${childIndent}} catch (error: unknown) {`);
|
|
307
|
+
lines.push(`${tryIndent}const isCancellation = CancellationError.isCancellationError(error);`);
|
|
308
|
+
lines.push(`${tryIndent}${awaitPrefix}scopedCtx.sendStatusChangedEvent({`);
|
|
309
|
+
lines.push(`${tryIndent} nodeTypeName: '${child.nodeType}',`);
|
|
310
|
+
lines.push(`${tryIndent} id: '${child.id}',`);
|
|
311
|
+
lines.push(`${tryIndent} executionIndex: ${safeChildId}Idx,`);
|
|
312
|
+
lines.push(`${tryIndent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
|
|
313
|
+
lines.push(`${tryIndent}});`);
|
|
314
|
+
lines.push(`${tryIndent}if (!isCancellation) {`);
|
|
315
|
+
lines.push(`${tryIndent} scopedCtx.sendLogErrorEvent({`);
|
|
316
|
+
lines.push(`${tryIndent} nodeTypeName: '${child.nodeType}',`);
|
|
317
|
+
lines.push(`${tryIndent} id: '${child.id}',`);
|
|
318
|
+
lines.push(`${tryIndent} executionIndex: ${safeChildId}Idx,`);
|
|
319
|
+
lines.push(`${tryIndent} error: error instanceof Error ? error.message : String(error),`);
|
|
320
|
+
lines.push(`${tryIndent} });`);
|
|
321
|
+
lines.push(`${tryIndent}}`);
|
|
322
|
+
lines.push(`${tryIndent}throw error;`);
|
|
323
|
+
lines.push(`${childIndent}}`);
|
|
324
|
+
// Close debug controller beforeNode if-block
|
|
325
|
+
if (emitDebugHooks) {
|
|
326
|
+
lines.push(` }`);
|
|
327
|
+
}
|
|
303
328
|
});
|
|
304
329
|
lines.push(``);
|
|
305
330
|
}
|
package/dist/parser.js
CHANGED
|
@@ -1653,6 +1653,10 @@ export class AnnotationParser {
|
|
|
1653
1653
|
for (const [inputName] of Object.entries(nextInputs)) {
|
|
1654
1654
|
if (isControlFlowPort(inputName))
|
|
1655
1655
|
continue;
|
|
1656
|
+
// Skip auto-wiring if a manual @connect already targets this input port
|
|
1657
|
+
const alreadyConnected = connections.some(c => c.to.node === nextId && c.to.port === inputName);
|
|
1658
|
+
if (alreadyConnected)
|
|
1659
|
+
continue;
|
|
1656
1660
|
// Walk backward through path steps to find nearest ancestor with same-name output
|
|
1657
1661
|
for (let j = i; j >= 0; j--) {
|
|
1658
1662
|
const ancestorId = steps[j].node;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synergenius/flow-weaver",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.28.0",
|
|
4
|
+
"description": "Flow Weaver: deterministic TypeScript workflow compiler. Define workflows with JSDoc annotations, compile to standalone functions with zero runtime dependencies.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -138,12 +138,18 @@
|
|
|
138
138
|
"keywords": [
|
|
139
139
|
"flow-weaver",
|
|
140
140
|
"workflow",
|
|
141
|
+
"workflow-engine",
|
|
142
|
+
"workflow-automation",
|
|
143
|
+
"orchestration",
|
|
141
144
|
"ai-agent",
|
|
142
145
|
"llm",
|
|
143
146
|
"mcp",
|
|
144
|
-
"deterministic",
|
|
145
147
|
"typescript",
|
|
146
148
|
"compiler",
|
|
149
|
+
"code-generation",
|
|
150
|
+
"visual-programming",
|
|
151
|
+
"node-graph",
|
|
152
|
+
"durable-execution",
|
|
147
153
|
"inngest",
|
|
148
154
|
"serverless"
|
|
149
155
|
],
|