@burdenoff/fluidgrids-fe-sdk 2026.1014.5 → 2026.1014.6
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/README.md +20 -0
- package/dist/{WorkflowBuilder-QFTEKT37.js → WorkflowBuilder-HBTESJQ3.js} +2 -2
- package/dist/{chunk-RXIRAKAC.js → chunk-H6QV7B5Q.js} +1 -1
- package/dist/{chunk-LK44C6IE.js → chunk-L2GL2QS3.js} +1 -1
- package/dist/{chunk-6JDHLCAQ.js → chunk-VJVAK5JC.js} +1881 -1654
- package/dist/{chunk-XKEUZUKM.js → chunk-ZPPYSIHI.js} +1 -1
- package/dist/index.js +7 -7
- package/dist/templates.js +2 -2
- package/dist/workflow-builder.js +3 -3
- package/package.json +1 -1
|
@@ -981,8 +981,8 @@ function isAdaptiveCard(data) {
|
|
|
981
981
|
return data.type === "AdaptiveCard";
|
|
982
982
|
}
|
|
983
983
|
function isCustomFormat(data) {
|
|
984
|
-
const
|
|
985
|
-
return
|
|
984
|
+
const record2 = data;
|
|
985
|
+
return record2.type !== "AdaptiveCard" && "inputs" in record2;
|
|
986
986
|
}
|
|
987
987
|
function expandTemplate(obj, data) {
|
|
988
988
|
if (typeof obj === "string") {
|
|
@@ -2454,643 +2454,339 @@ function isNodeTrigger(node) {
|
|
|
2454
2454
|
return false;
|
|
2455
2455
|
}
|
|
2456
2456
|
|
|
2457
|
-
// src/
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2457
|
+
// src/builder/lib/field-schema.ts
|
|
2458
|
+
function isRecord3(value) {
|
|
2459
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2460
|
+
}
|
|
2461
|
+
function isIdentifier(key) {
|
|
2462
|
+
return /^[A-Za-z_$][\w$]*$/.test(key);
|
|
2463
|
+
}
|
|
2464
|
+
function joinPath(base, key) {
|
|
2465
|
+
if (isIdentifier(key)) return base ? `${base}.${key}` : key;
|
|
2466
|
+
const bracket = `[${JSON.stringify(key)}]`;
|
|
2467
|
+
return base ? `${base}${bracket}` : bracket;
|
|
2468
|
+
}
|
|
2469
|
+
function pathToExpression(path) {
|
|
2470
|
+
if (!path) return "${output}";
|
|
2471
|
+
const accessor = path.startsWith("[") ? `output${path}` : `output.${path}`;
|
|
2472
|
+
return "${" + accessor + "}";
|
|
2473
|
+
}
|
|
2474
|
+
function fieldFromSpec(key, path, spec) {
|
|
2475
|
+
if (typeof spec === "string") {
|
|
2476
|
+
return { key, path, type: spec };
|
|
2474
2477
|
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2478
|
+
if (isRecord3(spec)) {
|
|
2479
|
+
const declaredType2 = typeof spec.type === "string" ? spec.type : void 0;
|
|
2480
|
+
const description = typeof spec.description === "string" ? spec.description : void 0;
|
|
2481
|
+
const required = spec.required === true;
|
|
2482
|
+
if (isRecord3(spec.properties)) {
|
|
2483
|
+
const children = Object.entries(spec.properties).map(
|
|
2484
|
+
([k, v]) => fieldFromSpec(k, joinPath(path, k), v)
|
|
2485
|
+
);
|
|
2486
|
+
return { key, path, type: "object", required, description, children };
|
|
2479
2487
|
}
|
|
2480
|
-
if (
|
|
2481
|
-
const
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2488
|
+
if (spec.items !== void 0) {
|
|
2489
|
+
const element = fieldFromSpec("0", `${path}[0]`, spec.items);
|
|
2490
|
+
return {
|
|
2491
|
+
key,
|
|
2492
|
+
path,
|
|
2493
|
+
type: "array",
|
|
2494
|
+
required,
|
|
2495
|
+
description,
|
|
2496
|
+
children: [element]
|
|
2497
|
+
};
|
|
2487
2498
|
}
|
|
2499
|
+
return { key, path, type: declaredType2 ?? "any", required, description };
|
|
2488
2500
|
}
|
|
2489
|
-
return
|
|
2501
|
+
return { key, path, type: "any" };
|
|
2490
2502
|
}
|
|
2491
|
-
function
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
const loadRequestRef = useRef(0);
|
|
2496
|
-
const workflow = useFlowStore((s) => s.workflow);
|
|
2497
|
-
const startExecution = useFlowStore(
|
|
2498
|
-
(s) => s.actions.execution.startExecution
|
|
2499
|
-
);
|
|
2500
|
-
const setRunMetadata = useFlowStore(
|
|
2501
|
-
(s) => s.actions.execution.setRunMetadata
|
|
2502
|
-
);
|
|
2503
|
-
const setStepPlan = useFlowStore((s) => s.actions.execution.setStepPlan);
|
|
2504
|
-
const addLog = useFlowStore((s) => s.actions.execution.addLog);
|
|
2505
|
-
const updateLog = useFlowStore((s) => s.actions.execution.updateLog);
|
|
2506
|
-
const setNodeStatus = useFlowStore((s) => s.actions.execution.setNodeStatus);
|
|
2507
|
-
const setNodeItemCount = useFlowStore(
|
|
2508
|
-
(s) => s.actions.execution.setNodeItemCount
|
|
2503
|
+
function fieldsFromIO(io, root = "") {
|
|
2504
|
+
if (!isRecord3(io)) return [];
|
|
2505
|
+
return Object.entries(io).map(
|
|
2506
|
+
([key, spec]) => fieldFromSpec(key, joinPath(root, key), spec)
|
|
2509
2507
|
);
|
|
2510
|
-
|
|
2511
|
-
|
|
2508
|
+
}
|
|
2509
|
+
function typeOfValue(value) {
|
|
2510
|
+
if (value === null || value === void 0) return "any";
|
|
2511
|
+
if (Array.isArray(value)) return "array";
|
|
2512
|
+
if (typeof value === "object") return "object";
|
|
2513
|
+
if (typeof value === "number") return "number";
|
|
2514
|
+
if (typeof value === "boolean") return "boolean";
|
|
2515
|
+
if (typeof value === "string") return "string";
|
|
2516
|
+
return "any";
|
|
2517
|
+
}
|
|
2518
|
+
function fieldFromValue(key, path, value) {
|
|
2519
|
+
if (Array.isArray(value)) {
|
|
2520
|
+
const children = value.length > 0 ? [fieldFromValue("0", `${path}[0]`, value[0])] : void 0;
|
|
2521
|
+
return { key, path, type: "array", children };
|
|
2522
|
+
}
|
|
2523
|
+
if (isRecord3(value)) {
|
|
2524
|
+
const children = Object.entries(value).map(
|
|
2525
|
+
([k, v]) => fieldFromValue(k, joinPath(path, k), v)
|
|
2526
|
+
);
|
|
2527
|
+
return { key, path, type: "object", children };
|
|
2528
|
+
}
|
|
2529
|
+
return { key, path, type: typeOfValue(value) };
|
|
2530
|
+
}
|
|
2531
|
+
function fieldsFromValue(value, root = "") {
|
|
2532
|
+
if (!isRecord3(value)) return [];
|
|
2533
|
+
return Object.entries(value).map(
|
|
2534
|
+
([key, v]) => fieldFromValue(key, joinPath(root, key), v)
|
|
2512
2535
|
);
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
const
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2536
|
+
}
|
|
2537
|
+
function declaredOutputFields(nodeType, op) {
|
|
2538
|
+
const name = op?.name;
|
|
2539
|
+
if (!name || nodeType?.startsWith("internal:")) {
|
|
2540
|
+
return fieldsFromIO(op?.output_fields);
|
|
2541
|
+
}
|
|
2542
|
+
const children = fieldsFromIO(op?.output_fields, name);
|
|
2543
|
+
if (children.length === 0) return [];
|
|
2544
|
+
return [{ key: name, path: name, type: "object", children }];
|
|
2545
|
+
}
|
|
2546
|
+
function normalizeOperations(raw) {
|
|
2547
|
+
if (Array.isArray(raw)) return raw;
|
|
2548
|
+
if (isRecord3(raw) && Array.isArray(raw.operations)) {
|
|
2549
|
+
return raw.operations;
|
|
2550
|
+
}
|
|
2551
|
+
return [];
|
|
2552
|
+
}
|
|
2553
|
+
function resolveNodeOperation(data, fallbackOperations2) {
|
|
2554
|
+
const nodeData = isRecord3(data?.nodeData) ? data?.nodeData : void 0;
|
|
2555
|
+
let operations = normalizeOperations(nodeData?.operations);
|
|
2556
|
+
if (operations.length === 0) {
|
|
2557
|
+
operations = normalizeOperations(data?.operations);
|
|
2558
|
+
}
|
|
2559
|
+
if (operations.length === 0 && fallbackOperations2 !== void 0) {
|
|
2560
|
+
operations = normalizeOperations(fallbackOperations2);
|
|
2561
|
+
}
|
|
2562
|
+
const opName = typeof data?.operation_name === "string" ? data.operation_name : void 0;
|
|
2563
|
+
const selected = operations.find((op) => op.name === opName) ?? operations[0];
|
|
2564
|
+
return { operations, selected };
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
// src/builder/lib/parameter-values.ts
|
|
2568
|
+
function isRecord4(value) {
|
|
2569
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2570
|
+
}
|
|
2571
|
+
function record(value) {
|
|
2572
|
+
return isRecord4(value) ? value : void 0;
|
|
2573
|
+
}
|
|
2574
|
+
function declaredType(schema) {
|
|
2575
|
+
const type = typeof schema === "string" ? schema : record(schema)?.type;
|
|
2576
|
+
return typeof type === "string" ? type.toLowerCase().trim() : "";
|
|
2577
|
+
}
|
|
2578
|
+
function structuredParameterType(schema) {
|
|
2579
|
+
const type = declaredType(schema);
|
|
2580
|
+
if (type === "array" || /^array<.+>$/.test(type)) return "array";
|
|
2581
|
+
if (type === "object") return "object";
|
|
2582
|
+
return void 0;
|
|
2583
|
+
}
|
|
2584
|
+
function itemSchema(schema) {
|
|
2585
|
+
return record(schema)?.items ?? declaredType(schema).match(/^array<(.+)>$/)?.[1];
|
|
2586
|
+
}
|
|
2587
|
+
function matchesSchema(value, schema) {
|
|
2588
|
+
const type = declaredType(schema);
|
|
2589
|
+
const structured = structuredParameterType(schema);
|
|
2590
|
+
if (structured === "array") {
|
|
2591
|
+
return Array.isArray(value) && value.every((item) => matchesSchema(item, itemSchema(schema)));
|
|
2592
|
+
}
|
|
2593
|
+
if (structured === "object") {
|
|
2594
|
+
const object = record(value);
|
|
2595
|
+
if (!object) return false;
|
|
2596
|
+
const definition = record(schema);
|
|
2597
|
+
const required = definition?.required;
|
|
2598
|
+
if (Array.isArray(required) && required.some(
|
|
2599
|
+
(key) => typeof key === "string" && !Object.hasOwn(object, key)
|
|
2600
|
+
)) {
|
|
2601
|
+
return false;
|
|
2528
2602
|
}
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
(node) => !nodesWithIncomingEdges.has(node.id)
|
|
2603
|
+
return Object.entries(record(definition?.properties) ?? {}).every(
|
|
2604
|
+
([key, property]) => Object.hasOwn(object, key) ? matchesSchema(object[key], property) : record(property)?.required !== true
|
|
2532
2605
|
);
|
|
2533
|
-
|
|
2534
|
-
|
|
2606
|
+
}
|
|
2607
|
+
if (type === "string") return typeof value === "string";
|
|
2608
|
+
if (type === "number")
|
|
2609
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
2610
|
+
if (type === "integer")
|
|
2611
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
2612
|
+
if (type === "boolean") return typeof value === "boolean";
|
|
2613
|
+
if (type === "null") return value === null;
|
|
2614
|
+
return true;
|
|
2615
|
+
}
|
|
2616
|
+
function parseParameterValue(schema, value) {
|
|
2617
|
+
const type = structuredParameterType(schema);
|
|
2618
|
+
if (!type) return { valid: true, value };
|
|
2619
|
+
if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
|
|
2620
|
+
return { valid: true, value: void 0 };
|
|
2621
|
+
}
|
|
2622
|
+
let parsed = value;
|
|
2623
|
+
if (typeof value === "string") {
|
|
2624
|
+
const text = value.trim();
|
|
2625
|
+
const elementType = declaredType(itemSchema(schema));
|
|
2626
|
+
const canSplit = type === "array" && (!elementType || elementType === "string");
|
|
2627
|
+
if (canSplit && !["[", "{", '"', "]", "}"].includes(text[0])) {
|
|
2628
|
+
parsed = text.split(",").map((item) => item.trim()).filter(Boolean);
|
|
2629
|
+
} else {
|
|
2630
|
+
try {
|
|
2631
|
+
parsed = JSON.parse(text);
|
|
2632
|
+
} catch {
|
|
2633
|
+
return { valid: false, error: `Enter valid JSON for this ${type}.` };
|
|
2634
|
+
}
|
|
2535
2635
|
}
|
|
2536
|
-
|
|
2537
|
-
|
|
2636
|
+
}
|
|
2637
|
+
if (type === "array" ? !Array.isArray(parsed) : !record(parsed)) {
|
|
2638
|
+
return { valid: false, error: `Expected a JSON ${type}.` };
|
|
2639
|
+
}
|
|
2640
|
+
if (!matchesSchema(parsed, schema)) {
|
|
2641
|
+
return {
|
|
2642
|
+
valid: false,
|
|
2643
|
+
error: "The value does not match the declared field types."
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
return { valid: true, value: parsed };
|
|
2647
|
+
}
|
|
2648
|
+
function formatParameterValue(value) {
|
|
2649
|
+
if (value === void 0 || value === null) return "";
|
|
2650
|
+
return typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
2651
|
+
}
|
|
2652
|
+
function normalizeNodeParameters(data, fallbackOperations2, mappedFields = /* @__PURE__ */ new Set()) {
|
|
2653
|
+
const operation = resolveNodeOperation(data, fallbackOperations2).selected;
|
|
2654
|
+
const fields = record(operation?.input_fields);
|
|
2655
|
+
const args = record(data.args);
|
|
2656
|
+
const state = record(args?.state) ?? {};
|
|
2657
|
+
if (!fields) return data;
|
|
2658
|
+
const structuredFields = Object.entries(fields).filter(
|
|
2659
|
+
([name, schema]) => structuredParameterType(schema) && !mappedFields.has(name)
|
|
2660
|
+
);
|
|
2661
|
+
if (structuredFields.length === 0) return data;
|
|
2662
|
+
const normalized = new Map(Object.entries(state));
|
|
2663
|
+
for (const [name, schema] of structuredFields) {
|
|
2664
|
+
const result = parseParameterValue(
|
|
2665
|
+
schema,
|
|
2666
|
+
Object.hasOwn(state, name) ? state[name] : void 0
|
|
2667
|
+
);
|
|
2668
|
+
if (!result.valid) throw new Error(`${name}: ${result.error}`);
|
|
2669
|
+
if (result.value === void 0) normalized.delete(name);
|
|
2670
|
+
else normalized.set(name, result.value);
|
|
2671
|
+
}
|
|
2672
|
+
return { ...data, args: { ...args, state: Object.fromEntries(normalized) } };
|
|
2673
|
+
}
|
|
2674
|
+
function normalizeWorkflowParameters(nodes, edges, fallbackOperations2) {
|
|
2675
|
+
return nodes.map((node) => {
|
|
2676
|
+
if (node.data.disabled === true) return node;
|
|
2677
|
+
const mappedFields = new Set(
|
|
2678
|
+
edges.filter((edge) => edge.target === node.id).flatMap(
|
|
2679
|
+
(edge) => Object.entries(
|
|
2680
|
+
record(record(record(edge)?.mapping)?.definition) ?? {}
|
|
2681
|
+
).filter(
|
|
2682
|
+
([, definition]) => typeof definition === "string" ? definition.trim().length > 0 : definition !== void 0
|
|
2683
|
+
).map(([name]) => name)
|
|
2684
|
+
)
|
|
2685
|
+
);
|
|
2686
|
+
try {
|
|
2687
|
+
return {
|
|
2688
|
+
...node,
|
|
2689
|
+
data: normalizeNodeParameters(
|
|
2690
|
+
node.data,
|
|
2691
|
+
fallbackOperations2(node.type),
|
|
2692
|
+
mappedFields
|
|
2693
|
+
)
|
|
2694
|
+
};
|
|
2695
|
+
} catch (error) {
|
|
2696
|
+
throw new Error(
|
|
2697
|
+
`Node ${node.id}: ${error instanceof Error ? error.message : "Invalid parameter value"}`
|
|
2698
|
+
);
|
|
2538
2699
|
}
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
const
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
const
|
|
2547
|
-
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
function useDeleteNode() {
|
|
2703
|
+
const { getNode, getEdges, deleteElements } = useReactFlow();
|
|
2704
|
+
const deleteNodes = useFlowStore((state) => state.actions.nodes.deleteNodes);
|
|
2705
|
+
return useCallback(
|
|
2706
|
+
(id) => {
|
|
2707
|
+
const node = getNode(id);
|
|
2708
|
+
if (!node) return;
|
|
2709
|
+
const edges = getEdges();
|
|
2710
|
+
const connectedEdges = getConnectedEdges([node], edges);
|
|
2711
|
+
deleteElements({ nodes: [node], edges: connectedEdges }).then();
|
|
2712
|
+
deleteNodes([node]);
|
|
2548
2713
|
},
|
|
2549
|
-
[
|
|
2714
|
+
[deleteElements, getEdges, getNode, deleteNodes]
|
|
2550
2715
|
);
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
} else {
|
|
2591
|
-
if (existingLog.status !== status || status === "completed" && !existingLog.output) {
|
|
2592
|
-
updateLog(logId, {
|
|
2593
|
-
status,
|
|
2594
|
-
inputArgs: nodeState.inputArgs ?? existingLog.inputArgs,
|
|
2595
|
-
output: nodeState.output ?? existingLog.output,
|
|
2596
|
-
error: nodeState.error ?? existingLog.error,
|
|
2597
|
-
duration: nodeState.duration ?? existingLog.duration,
|
|
2598
|
-
endTime: nodeState.endTime ?? existingLog.endTime,
|
|
2599
|
-
usedPinnedData: nodeState.usedPinnedData ?? existingLog.usedPinnedData,
|
|
2600
|
-
reusedFromRunId: nodeState.reusedFromRunId ?? existingLog.reusedFromRunId
|
|
2601
|
-
});
|
|
2602
|
-
}
|
|
2603
|
-
}
|
|
2604
|
-
}
|
|
2605
|
-
},
|
|
2606
|
-
[setNodeStatus, setNodeItemCount, addLog, updateLog, getNodeName]
|
|
2607
|
-
);
|
|
2608
|
-
const pollRunUpdates = useCallback(
|
|
2609
|
-
(runId) => {
|
|
2610
|
-
const client = getFluidGridsClient();
|
|
2611
|
-
pollStartRef.current = Date.now();
|
|
2612
|
-
activeRunIdRef.current = runId;
|
|
2613
|
-
const doPoll = async () => {
|
|
2614
|
-
if (activeRunIdRef.current !== runId) {
|
|
2615
|
-
return;
|
|
2616
|
-
}
|
|
2617
|
-
try {
|
|
2618
|
-
const result = await client.query({
|
|
2619
|
-
query: WORKFLOW_RUN,
|
|
2620
|
-
variables: { id: runId },
|
|
2621
|
-
fetchPolicy: "network-only"
|
|
2622
|
-
});
|
|
2623
|
-
if (activeRunIdRef.current !== runId) {
|
|
2624
|
-
return;
|
|
2625
|
-
}
|
|
2626
|
-
const run = result.data?.workflowRun;
|
|
2627
|
-
if (!run) {
|
|
2628
|
-
console.warn(
|
|
2629
|
-
"[useNodeExecution] Poll returned no run data for",
|
|
2630
|
-
runId
|
|
2631
|
-
);
|
|
2632
|
-
if (Date.now() - pollStartRef.current < MAX_RUN_CREATION_WAIT) {
|
|
2633
|
-
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL);
|
|
2634
|
-
} else {
|
|
2635
|
-
console.warn(
|
|
2636
|
-
"[useNodeExecution] Run data not found after timeout for",
|
|
2637
|
-
runId
|
|
2638
|
-
);
|
|
2639
|
-
completeExecution();
|
|
2640
|
-
cleanup();
|
|
2641
|
-
}
|
|
2642
|
-
return;
|
|
2643
|
-
}
|
|
2644
|
-
setRunMetadata({
|
|
2645
|
-
runId,
|
|
2646
|
-
workflowKey: run.workflowKey,
|
|
2647
|
-
version: run.version,
|
|
2648
|
-
sourceMode: run.mode
|
|
2649
|
-
});
|
|
2650
|
-
if (run.nodeStates?.length) {
|
|
2651
|
-
processNodeStates(runId, run.nodeStates);
|
|
2652
|
-
}
|
|
2653
|
-
if (TERMINAL_STATUSES.has(run.status)) {
|
|
2654
|
-
completeExecution();
|
|
2655
|
-
cleanup();
|
|
2656
|
-
return;
|
|
2657
|
-
}
|
|
2658
|
-
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL);
|
|
2659
|
-
} catch (error) {
|
|
2660
|
-
console.error("[useNodeExecution] Poll error:", error);
|
|
2661
|
-
if (activeRunIdRef.current !== runId) {
|
|
2662
|
-
return;
|
|
2663
|
-
}
|
|
2664
|
-
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL * 2);
|
|
2665
|
-
}
|
|
2666
|
-
};
|
|
2667
|
-
pollTimerRef.current = setTimeout(doPoll, 800);
|
|
2668
|
-
},
|
|
2669
|
-
[processNodeStates, completeExecution, cleanup, setRunMetadata]
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
// src/builder/contants/symbols.ts
|
|
2719
|
+
var NODE_TYPE_DRAG_DATA_FORMAT = "application/flow-builder.node-type";
|
|
2720
|
+
var NODE_OPERATION_DRAG_DATA_FORMAT = "application/flow-builder.node-operation";
|
|
2721
|
+
var STICKY_NOTE_TYPE = "sticky-note";
|
|
2722
|
+
var STICKY_COLORS = {
|
|
2723
|
+
yellow: "#F2C94C",
|
|
2724
|
+
orange: "#EB8934",
|
|
2725
|
+
blue: "#3B82F6",
|
|
2726
|
+
green: "#10B981",
|
|
2727
|
+
red: "#EF4444",
|
|
2728
|
+
purple: "#8B5CF6",
|
|
2729
|
+
gray: "#9CA3AF"
|
|
2730
|
+
};
|
|
2731
|
+
var DEFAULT_STICKY_COLOR = "yellow";
|
|
2732
|
+
var STICKY_DEFAULT_SIZE = { width: 260, height: 170 };
|
|
2733
|
+
var STICKY_MIN_SIZE = { width: 160, height: 90 };
|
|
2734
|
+
function ToolbarButton({
|
|
2735
|
+
title,
|
|
2736
|
+
onClick,
|
|
2737
|
+
danger,
|
|
2738
|
+
children
|
|
2739
|
+
}) {
|
|
2740
|
+
return /* @__PURE__ */ jsx(
|
|
2741
|
+
"button",
|
|
2742
|
+
{
|
|
2743
|
+
type: "button",
|
|
2744
|
+
title,
|
|
2745
|
+
onClick: (e) => {
|
|
2746
|
+
e.stopPropagation();
|
|
2747
|
+
onClick();
|
|
2748
|
+
},
|
|
2749
|
+
className: cn(
|
|
2750
|
+
"nodrag flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
2751
|
+
danger && "text-[var(--color-status-error-text)] hover:text-[var(--color-status-error-text)]"
|
|
2752
|
+
),
|
|
2753
|
+
children
|
|
2754
|
+
}
|
|
2670
2755
|
);
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
})
|
|
2756
|
+
}
|
|
2757
|
+
function StickyNoteNodeComponent({
|
|
2758
|
+
id,
|
|
2759
|
+
selected,
|
|
2760
|
+
data
|
|
2761
|
+
}) {
|
|
2762
|
+
const tr = useTr();
|
|
2763
|
+
const { setNodes, getNode } = useReactFlow();
|
|
2764
|
+
const zustandSetNodes = useFlowStore((s) => s.actions.nodes.setNodes);
|
|
2765
|
+
const deleteNode = useDeleteNode();
|
|
2766
|
+
const [editing, setEditing] = useState(false);
|
|
2767
|
+
const [showPalette, setShowPalette] = useState(false);
|
|
2768
|
+
const [draft, setDraft] = useState(String(data.content ?? ""));
|
|
2769
|
+
const textareaRef = useRef(null);
|
|
2770
|
+
const rootRef = useRef(null);
|
|
2771
|
+
useEffect(() => {
|
|
2772
|
+
const wrapper = rootRef.current?.closest(".react-flow__node");
|
|
2773
|
+
if (wrapper && wrapper.style.zIndex !== "-1") {
|
|
2774
|
+
wrapper.style.zIndex = "-1";
|
|
2775
|
+
}
|
|
2776
|
+
});
|
|
2777
|
+
const colorKey = typeof data.color === "string" && STICKY_COLORS[data.color] ? data.color : DEFAULT_STICKY_COLOR;
|
|
2778
|
+
const accent = STICKY_COLORS[colorKey];
|
|
2779
|
+
const patchData = useCallback(
|
|
2780
|
+
(patch) => {
|
|
2781
|
+
setNodes((nds) => {
|
|
2782
|
+
const updated = nds.map(
|
|
2783
|
+
(n) => n.id === id ? { ...n, data: { ...n.data, ...patch } } : n
|
|
2700
2784
|
);
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
startExecution(nodeId, backendRunId, {
|
|
2705
|
-
workflowKey: stepResult.workflowKey,
|
|
2706
|
-
version: stepResult.version,
|
|
2707
|
-
sourceMode: "TEST"
|
|
2708
|
-
});
|
|
2709
|
-
setStepPlan({
|
|
2710
|
-
runId: backendRunId,
|
|
2711
|
-
...stepResult.plan,
|
|
2712
|
-
sourceRunId: stepResult.plan.sourceRunId ?? null
|
|
2713
|
-
});
|
|
2714
|
-
const initialLog = {
|
|
2715
|
-
id: `${backendRunId}-${nodeId}`,
|
|
2716
|
-
runId: backendRunId,
|
|
2717
|
-
nodeId,
|
|
2718
|
-
nodeType: workflow.nodes.find((node) => node.id === nodeId)?.type || "unknown",
|
|
2719
|
-
nodeName: getNodeName(nodeId),
|
|
2720
|
-
status: "running",
|
|
2721
|
-
inputArgs: null,
|
|
2722
|
-
output: null,
|
|
2723
|
-
error: null,
|
|
2724
|
-
duration: null,
|
|
2725
|
-
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2726
|
-
endTime: null
|
|
2727
|
-
};
|
|
2728
|
-
addLog(initialLog);
|
|
2729
|
-
pollRunUpdates(backendRunId);
|
|
2730
|
-
return { success: true, runId: backendRunId, plan: stepResult.plan };
|
|
2731
|
-
}
|
|
2732
|
-
console.error("[useNodeExecution] No result from executeStep");
|
|
2733
|
-
setNodeStatus(nodeId, "error");
|
|
2734
|
-
return { success: false, error: "No result from executeStep" };
|
|
2735
|
-
} catch (error) {
|
|
2736
|
-
console.error("[useNodeExecution] Error executing step:", error);
|
|
2737
|
-
const errorMessage = describeOperationError(error);
|
|
2738
|
-
const errorRunId = nanoid$1();
|
|
2739
|
-
const errorLog = {
|
|
2740
|
-
id: `${errorRunId}-${nodeId}`,
|
|
2741
|
-
runId: errorRunId,
|
|
2742
|
-
nodeId,
|
|
2743
|
-
nodeType: workflow.nodes.find((node) => node.id === nodeId)?.type || "unknown",
|
|
2744
|
-
nodeName: getNodeName(nodeId),
|
|
2745
|
-
status: "failed",
|
|
2746
|
-
inputArgs: null,
|
|
2747
|
-
output: null,
|
|
2748
|
-
error: errorMessage,
|
|
2749
|
-
duration: null,
|
|
2750
|
-
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2751
|
-
endTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
2752
|
-
};
|
|
2753
|
-
addLog(errorLog);
|
|
2754
|
-
setNodeStatus(nodeId, "error");
|
|
2755
|
-
completeExecution();
|
|
2756
|
-
return { success: false, error: errorMessage };
|
|
2757
|
-
}
|
|
2785
|
+
zustandSetNodes(updated);
|
|
2786
|
+
return updated;
|
|
2787
|
+
});
|
|
2758
2788
|
},
|
|
2759
|
-
[
|
|
2760
|
-
cleanup,
|
|
2761
|
-
clearLogs,
|
|
2762
|
-
startExecution,
|
|
2763
|
-
setStepPlan,
|
|
2764
|
-
workflow,
|
|
2765
|
-
getNodeName,
|
|
2766
|
-
addLog,
|
|
2767
|
-
setNodeStatus,
|
|
2768
|
-
pollRunUpdates,
|
|
2769
|
-
completeExecution
|
|
2770
|
-
]
|
|
2771
|
-
);
|
|
2772
|
-
const executeFullWorkflow = useCallback(
|
|
2773
|
-
async (customTriggerData) => {
|
|
2774
|
-
const triggerNode = findTriggerNode();
|
|
2775
|
-
if (!triggerNode) {
|
|
2776
|
-
console.error("[useNodeExecution] No trigger node found in workflow");
|
|
2777
|
-
return { success: false, error: "No trigger node found" };
|
|
2778
|
-
}
|
|
2779
|
-
cleanup();
|
|
2780
|
-
clearLogs();
|
|
2781
|
-
const workflowKey = workflow.workflowKey || workflow.id;
|
|
2782
|
-
const version = workflow.version || "1.0.0";
|
|
2783
|
-
if (!workflowKey) {
|
|
2784
|
-
console.error(
|
|
2785
|
-
"[useNodeExecution] Workflow must be saved before execution. No workflowKey found."
|
|
2786
|
-
);
|
|
2787
|
-
return {
|
|
2788
|
-
success: false,
|
|
2789
|
-
error: "Workflow must be saved before execution"
|
|
2790
|
-
};
|
|
2791
|
-
}
|
|
2792
|
-
setNodeStatus(triggerNode.id, "running");
|
|
2793
|
-
try {
|
|
2794
|
-
const client = getFluidGridsClient();
|
|
2795
|
-
const result = throwIfResultError(
|
|
2796
|
-
await client.mutate({
|
|
2797
|
-
mutation: TRIGGER_WORKFLOW,
|
|
2798
|
-
variables: {
|
|
2799
|
-
input: {
|
|
2800
|
-
workflowKey,
|
|
2801
|
-
version,
|
|
2802
|
-
triggerNodeId: triggerNode.id,
|
|
2803
|
-
triggerData: customTriggerData || {},
|
|
2804
|
-
context: {},
|
|
2805
|
-
// Builder "Test workflow" is always a manual/debug run.
|
|
2806
|
-
// Pinned node data only applies to TEST executions, never a
|
|
2807
|
-
// production trigger (webhook, schedule, API).
|
|
2808
|
-
mode: "TEST"
|
|
2809
|
-
}
|
|
2810
|
-
}
|
|
2811
|
-
})
|
|
2812
|
-
);
|
|
2813
|
-
const triggerResult = result.data?.triggerWorkflow;
|
|
2814
|
-
if (triggerResult) {
|
|
2815
|
-
const backendRunId = triggerResult.runId;
|
|
2816
|
-
startExecution(triggerNode.id, backendRunId, {
|
|
2817
|
-
workflowKey: triggerResult.workflowKey,
|
|
2818
|
-
version: triggerResult.version,
|
|
2819
|
-
sourceMode: "TEST"
|
|
2820
|
-
});
|
|
2821
|
-
pollRunUpdates(backendRunId);
|
|
2822
|
-
return { success: true, runId: backendRunId };
|
|
2823
|
-
}
|
|
2824
|
-
console.error("[useNodeExecution] No result from trigger");
|
|
2825
|
-
setNodeStatus(triggerNode.id, "error");
|
|
2826
|
-
return { success: false, error: "No result from trigger" };
|
|
2827
|
-
} catch (error) {
|
|
2828
|
-
console.error("[useNodeExecution] Error triggering workflow:", error);
|
|
2829
|
-
const errorMessage = describeOperationError(error);
|
|
2830
|
-
const errorRunId = nanoid$1();
|
|
2831
|
-
const errorLog = {
|
|
2832
|
-
id: `${errorRunId}-${triggerNode.id}`,
|
|
2833
|
-
runId: errorRunId,
|
|
2834
|
-
nodeId: triggerNode.id,
|
|
2835
|
-
nodeType: triggerNode.type || "unknown",
|
|
2836
|
-
nodeName: getNodeName(triggerNode.id),
|
|
2837
|
-
status: "failed",
|
|
2838
|
-
inputArgs: null,
|
|
2839
|
-
output: null,
|
|
2840
|
-
error: errorMessage,
|
|
2841
|
-
duration: null,
|
|
2842
|
-
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2843
|
-
endTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
2844
|
-
};
|
|
2845
|
-
addLog(errorLog);
|
|
2846
|
-
setNodeStatus(triggerNode.id, "error");
|
|
2847
|
-
completeExecution();
|
|
2848
|
-
return { success: false, error: errorMessage };
|
|
2849
|
-
}
|
|
2850
|
-
},
|
|
2851
|
-
[
|
|
2852
|
-
findTriggerNode,
|
|
2853
|
-
cleanup,
|
|
2854
|
-
clearLogs,
|
|
2855
|
-
startExecution,
|
|
2856
|
-
workflow,
|
|
2857
|
-
getNodeName,
|
|
2858
|
-
addLog,
|
|
2859
|
-
setNodeStatus,
|
|
2860
|
-
pollRunUpdates,
|
|
2861
|
-
completeExecution
|
|
2862
|
-
]
|
|
2863
|
-
);
|
|
2864
|
-
const executeFromNode = useCallback(
|
|
2865
|
-
async (sourceRunId, nodeId) => {
|
|
2866
|
-
cleanup();
|
|
2867
|
-
clearLogs();
|
|
2868
|
-
setNodeStatus(nodeId, "running");
|
|
2869
|
-
try {
|
|
2870
|
-
const client = getFluidGridsClient();
|
|
2871
|
-
const result = throwIfResultError(
|
|
2872
|
-
await client.mutate({
|
|
2873
|
-
mutation: RUN_FROM_NODE,
|
|
2874
|
-
variables: {
|
|
2875
|
-
input: {
|
|
2876
|
-
sourceRunId,
|
|
2877
|
-
nodeId,
|
|
2878
|
-
// Run-from-node is a debug affordance — always TEST, same
|
|
2879
|
-
// rule as the other builder debug execution paths.
|
|
2880
|
-
mode: "TEST"
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
2883
|
-
})
|
|
2884
|
-
);
|
|
2885
|
-
const runFromNodeResult = result.data?.runFromNode;
|
|
2886
|
-
if (runFromNodeResult) {
|
|
2887
|
-
const backendRunId = runFromNodeResult.runId;
|
|
2888
|
-
startExecution(nodeId, backendRunId, {
|
|
2889
|
-
workflowKey: runFromNodeResult.workflowKey,
|
|
2890
|
-
version: runFromNodeResult.version,
|
|
2891
|
-
sourceMode: "TEST"
|
|
2892
|
-
});
|
|
2893
|
-
pollRunUpdates(backendRunId);
|
|
2894
|
-
return { success: true, runId: backendRunId };
|
|
2895
|
-
}
|
|
2896
|
-
console.error("[useNodeExecution] No result from runFromNode");
|
|
2897
|
-
setNodeStatus(nodeId, "error");
|
|
2898
|
-
return { success: false, error: "No result from runFromNode" };
|
|
2899
|
-
} catch (error) {
|
|
2900
|
-
console.error("[useNodeExecution] Error running from node:", error);
|
|
2901
|
-
const errorMessage = describeOperationError(error);
|
|
2902
|
-
setNodeStatus(nodeId, "error");
|
|
2903
|
-
completeExecution();
|
|
2904
|
-
return { success: false, error: errorMessage };
|
|
2905
|
-
}
|
|
2906
|
-
},
|
|
2907
|
-
[
|
|
2908
|
-
cleanup,
|
|
2909
|
-
clearLogs,
|
|
2910
|
-
setNodeStatus,
|
|
2911
|
-
startExecution,
|
|
2912
|
-
pollRunUpdates,
|
|
2913
|
-
completeExecution
|
|
2914
|
-
]
|
|
2915
|
-
);
|
|
2916
|
-
const loadExecution = useCallback(
|
|
2917
|
-
async (runId) => {
|
|
2918
|
-
cleanup();
|
|
2919
|
-
const requestId = ++loadRequestRef.current;
|
|
2920
|
-
try {
|
|
2921
|
-
const client = getFluidGridsClient();
|
|
2922
|
-
const result = throwIfResultError(
|
|
2923
|
-
await client.query({
|
|
2924
|
-
query: EXECUTION_DETAIL,
|
|
2925
|
-
variables: { id: runId },
|
|
2926
|
-
fetchPolicy: "network-only"
|
|
2927
|
-
})
|
|
2928
|
-
);
|
|
2929
|
-
const run = result.data?.workflowRun;
|
|
2930
|
-
if (!run) {
|
|
2931
|
-
return { success: false, error: "Execution not found" };
|
|
2932
|
-
}
|
|
2933
|
-
if (run.mode !== "TEST") {
|
|
2934
|
-
return {
|
|
2935
|
-
success: false,
|
|
2936
|
-
error: "Only TEST runs can be loaded for continuation."
|
|
2937
|
-
};
|
|
2938
|
-
}
|
|
2939
|
-
if (!TERMINAL_STATUSES.has(run.status)) {
|
|
2940
|
-
return {
|
|
2941
|
-
success: false,
|
|
2942
|
-
error: "Only terminal TEST runs can be loaded for continuation."
|
|
2943
|
-
};
|
|
2944
|
-
}
|
|
2945
|
-
if (requestId !== loadRequestRef.current) {
|
|
2946
|
-
return { success: false, error: "Execution load superseded." };
|
|
2947
|
-
}
|
|
2948
|
-
const currentWorkflow = useFlowStore.getState().workflow;
|
|
2949
|
-
const currentWorkflowKey = currentWorkflow.workflowKey || currentWorkflow.id;
|
|
2950
|
-
if (run.workflowKey !== currentWorkflowKey || run.version !== currentWorkflow.version) {
|
|
2951
|
-
return {
|
|
2952
|
-
success: false,
|
|
2953
|
-
error: "The selected run belongs to a different workflow version."
|
|
2954
|
-
};
|
|
2955
|
-
}
|
|
2956
|
-
const logs = (run.nodeStates ?? []).map((nodeState) => {
|
|
2957
|
-
const rawStatus = nodeState.status.toLowerCase();
|
|
2958
|
-
const status = rawStatus === "completed" ? "completed" : rawStatus === "failed" || rawStatus === "timeout" ? "failed" : rawStatus === "running" || rawStatus === "waiting" || rawStatus === "delayed" || rawStatus === "waiting_subworkflow" ? "running" : "pending";
|
|
2959
|
-
return {
|
|
2960
|
-
id: `${runId}-${nodeState.nodeId}`,
|
|
2961
|
-
runId,
|
|
2962
|
-
nodeId: nodeState.nodeId,
|
|
2963
|
-
nodeType: nodeState.nodeType,
|
|
2964
|
-
nodeName: getNodeName(nodeState.nodeId),
|
|
2965
|
-
status,
|
|
2966
|
-
inputArgs: nodeState.inputArgs ?? null,
|
|
2967
|
-
output: nodeState.output ?? null,
|
|
2968
|
-
error: nodeState.error ?? null,
|
|
2969
|
-
duration: nodeState.duration ?? null,
|
|
2970
|
-
startTime: nodeState.startTime ?? run.startedAt ?? "",
|
|
2971
|
-
endTime: nodeState.endTime ?? null,
|
|
2972
|
-
usedPinnedData: nodeState.usedPinnedData ?? false,
|
|
2973
|
-
reusedFromRunId: nodeState.reusedFromRunId ?? null
|
|
2974
|
-
};
|
|
2975
|
-
});
|
|
2976
|
-
loadDebugRun(runId, logs, {
|
|
2977
|
-
workflowKey: run.workflowKey,
|
|
2978
|
-
version: run.version,
|
|
2979
|
-
sourceMode: run.mode
|
|
2980
|
-
});
|
|
2981
|
-
return { success: true, runId };
|
|
2982
|
-
} catch (error) {
|
|
2983
|
-
console.error("[useNodeExecution] Error loading execution:", error);
|
|
2984
|
-
return { success: false, error: describeOperationError(error) };
|
|
2985
|
-
}
|
|
2986
|
-
},
|
|
2987
|
-
[cleanup, getNodeName, loadDebugRun]
|
|
2988
|
-
);
|
|
2989
|
-
useEffect(() => {
|
|
2990
|
-
return () => {
|
|
2991
|
-
if (pollTimerRef.current) {
|
|
2992
|
-
completeExecution();
|
|
2993
|
-
}
|
|
2994
|
-
cleanup();
|
|
2995
|
-
};
|
|
2996
|
-
}, [cleanup, completeExecution]);
|
|
2997
|
-
return {
|
|
2998
|
-
executeStep,
|
|
2999
|
-
executeFullWorkflow,
|
|
3000
|
-
executeFromNode,
|
|
3001
|
-
loadExecution,
|
|
3002
|
-
cleanup,
|
|
3003
|
-
findTriggerNode
|
|
3004
|
-
};
|
|
3005
|
-
}
|
|
3006
|
-
function useDeleteNode() {
|
|
3007
|
-
const { getNode, getEdges, deleteElements } = useReactFlow();
|
|
3008
|
-
const deleteNodes = useFlowStore((state) => state.actions.nodes.deleteNodes);
|
|
3009
|
-
return useCallback(
|
|
3010
|
-
(id) => {
|
|
3011
|
-
const node = getNode(id);
|
|
3012
|
-
if (!node) return;
|
|
3013
|
-
const edges = getEdges();
|
|
3014
|
-
const connectedEdges = getConnectedEdges([node], edges);
|
|
3015
|
-
deleteElements({ nodes: [node], edges: connectedEdges }).then();
|
|
3016
|
-
deleteNodes([node]);
|
|
3017
|
-
},
|
|
3018
|
-
[deleteElements, getEdges, getNode, deleteNodes]
|
|
3019
|
-
);
|
|
3020
|
-
}
|
|
3021
|
-
|
|
3022
|
-
// src/builder/contants/symbols.ts
|
|
3023
|
-
var NODE_TYPE_DRAG_DATA_FORMAT = "application/flow-builder.node-type";
|
|
3024
|
-
var NODE_OPERATION_DRAG_DATA_FORMAT = "application/flow-builder.node-operation";
|
|
3025
|
-
var STICKY_NOTE_TYPE = "sticky-note";
|
|
3026
|
-
var STICKY_COLORS = {
|
|
3027
|
-
yellow: "#F2C94C",
|
|
3028
|
-
orange: "#EB8934",
|
|
3029
|
-
blue: "#3B82F6",
|
|
3030
|
-
green: "#10B981",
|
|
3031
|
-
red: "#EF4444",
|
|
3032
|
-
purple: "#8B5CF6",
|
|
3033
|
-
gray: "#9CA3AF"
|
|
3034
|
-
};
|
|
3035
|
-
var DEFAULT_STICKY_COLOR = "yellow";
|
|
3036
|
-
var STICKY_DEFAULT_SIZE = { width: 260, height: 170 };
|
|
3037
|
-
var STICKY_MIN_SIZE = { width: 160, height: 90 };
|
|
3038
|
-
function ToolbarButton({
|
|
3039
|
-
title,
|
|
3040
|
-
onClick,
|
|
3041
|
-
danger,
|
|
3042
|
-
children
|
|
3043
|
-
}) {
|
|
3044
|
-
return /* @__PURE__ */ jsx(
|
|
3045
|
-
"button",
|
|
3046
|
-
{
|
|
3047
|
-
type: "button",
|
|
3048
|
-
title,
|
|
3049
|
-
onClick: (e) => {
|
|
3050
|
-
e.stopPropagation();
|
|
3051
|
-
onClick();
|
|
3052
|
-
},
|
|
3053
|
-
className: cn(
|
|
3054
|
-
"nodrag flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
3055
|
-
danger && "text-[var(--color-status-error-text)] hover:text-[var(--color-status-error-text)]"
|
|
3056
|
-
),
|
|
3057
|
-
children
|
|
3058
|
-
}
|
|
3059
|
-
);
|
|
3060
|
-
}
|
|
3061
|
-
function StickyNoteNodeComponent({
|
|
3062
|
-
id,
|
|
3063
|
-
selected,
|
|
3064
|
-
data
|
|
3065
|
-
}) {
|
|
3066
|
-
const tr = useTr();
|
|
3067
|
-
const { setNodes, getNode } = useReactFlow();
|
|
3068
|
-
const zustandSetNodes = useFlowStore((s) => s.actions.nodes.setNodes);
|
|
3069
|
-
const deleteNode = useDeleteNode();
|
|
3070
|
-
const [editing, setEditing] = useState(false);
|
|
3071
|
-
const [showPalette, setShowPalette] = useState(false);
|
|
3072
|
-
const [draft, setDraft] = useState(String(data.content ?? ""));
|
|
3073
|
-
const textareaRef = useRef(null);
|
|
3074
|
-
const rootRef = useRef(null);
|
|
3075
|
-
useEffect(() => {
|
|
3076
|
-
const wrapper = rootRef.current?.closest(".react-flow__node");
|
|
3077
|
-
if (wrapper && wrapper.style.zIndex !== "-1") {
|
|
3078
|
-
wrapper.style.zIndex = "-1";
|
|
3079
|
-
}
|
|
3080
|
-
});
|
|
3081
|
-
const colorKey = typeof data.color === "string" && STICKY_COLORS[data.color] ? data.color : DEFAULT_STICKY_COLOR;
|
|
3082
|
-
const accent = STICKY_COLORS[colorKey];
|
|
3083
|
-
const patchData = useCallback(
|
|
3084
|
-
(patch) => {
|
|
3085
|
-
setNodes((nds) => {
|
|
3086
|
-
const updated = nds.map(
|
|
3087
|
-
(n) => n.id === id ? { ...n, data: { ...n.data, ...patch } } : n
|
|
3088
|
-
);
|
|
3089
|
-
zustandSetNodes(updated);
|
|
3090
|
-
return updated;
|
|
3091
|
-
});
|
|
3092
|
-
},
|
|
3093
|
-
[id, setNodes, zustandSetNodes]
|
|
2789
|
+
[id, setNodes, zustandSetNodes]
|
|
3094
2790
|
);
|
|
3095
2791
|
const startEditing = useCallback(() => {
|
|
3096
2792
|
setDraft(String(getNode(id)?.data?.content ?? ""));
|
|
@@ -3865,8 +3561,8 @@ function resolveOutputHandles(data) {
|
|
|
3865
3561
|
if (!spec) return DEFAULT_HANDLES;
|
|
3866
3562
|
if (isDynamicSpec(spec)) {
|
|
3867
3563
|
const args = data?.args;
|
|
3868
|
-
const
|
|
3869
|
-
const dynamic = Object.entries(
|
|
3564
|
+
const record2 = args?.state?.[spec.fromStateRecord] ?? data?.[spec.fromStateRecord] ?? {};
|
|
3565
|
+
const dynamic = Object.entries(record2).filter(([, handleId]) => typeof handleId === "string" && handleId).map(([label, handleId]) => ({ id: handleId, label }));
|
|
3870
3566
|
return [...dynamic, ...spec.extra ?? []];
|
|
3871
3567
|
}
|
|
3872
3568
|
if (spec.length === 0) return DEFAULT_HANDLES;
|
|
@@ -4283,7 +3979,11 @@ function useSaveWorkflow() {
|
|
|
4283
3979
|
const partitioned = partitionStickyNotes(workflow.nodes ?? []);
|
|
4284
3980
|
const preparedWorkflow = {
|
|
4285
3981
|
...workflow,
|
|
4286
|
-
nodes:
|
|
3982
|
+
nodes: normalizeWorkflowParameters(
|
|
3983
|
+
partitioned.nodes,
|
|
3984
|
+
workflow.edges ?? [],
|
|
3985
|
+
(type) => NODE_CONFIGS.find((config2) => config2.type === type)?.operations
|
|
3986
|
+
).map((n) => {
|
|
4287
3987
|
const slimData = slimNodeDataToCurrentOperation(
|
|
4288
3988
|
n.data ?? {}
|
|
4289
3989
|
);
|
|
@@ -5155,11 +4855,11 @@ function canRenderOperationPanel(selectedOperation, supportedOperations) {
|
|
|
5155
4855
|
const operationName = resolveOperationName(selectedOperation, "");
|
|
5156
4856
|
return operationName === "" || supportedOperations.includes(operationName);
|
|
5157
4857
|
}
|
|
5158
|
-
function
|
|
4858
|
+
function isRecord5(value) {
|
|
5159
4859
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5160
4860
|
}
|
|
5161
4861
|
function readRecord2(value) {
|
|
5162
|
-
return
|
|
4862
|
+
return isRecord5(value) ? value : void 0;
|
|
5163
4863
|
}
|
|
5164
4864
|
function readOperations(value) {
|
|
5165
4865
|
const operations = Array.isArray(value) ? value : readRecord2(value)?.operations;
|
|
@@ -5185,955 +4885,1515 @@ function synchronizeOperationUpdate(currentData, updates, registeredOperations)
|
|
|
5185
4885
|
nodeData: { ...manifest, operations: selected ? [selected] : [] }
|
|
5186
4886
|
};
|
|
5187
4887
|
}
|
|
5188
|
-
function isAutoManagedConnectionAdapter(nodeType, adapter) {
|
|
5189
|
-
const isBigConsoleDataSinkAutoAdapter = nodeType === "store/bigconsole_datasink" && adapter.name === "bigconsole" && adapter.provider === "bigconsole";
|
|
5190
|
-
return isBigConsoleDataSinkAutoAdapter || adapter.mode === "auto" || adapter.autoProvision === true || adapter.autoManaged === true || adapter.hidden === true;
|
|
4888
|
+
function isAutoManagedConnectionAdapter(nodeType, adapter) {
|
|
4889
|
+
const isBigConsoleDataSinkAutoAdapter = nodeType === "store/bigconsole_datasink" && adapter.name === "bigconsole" && adapter.provider === "bigconsole";
|
|
4890
|
+
return isBigConsoleDataSinkAutoAdapter || adapter.mode === "auto" || adapter.autoProvision === true || adapter.autoManaged === true || adapter.hidden === true;
|
|
4891
|
+
}
|
|
4892
|
+
function getVisibleConnectionAdapters(nodeType, operationName, operations, nodeData) {
|
|
4893
|
+
const ops = (() => {
|
|
4894
|
+
const arr = Array.isArray(operations) ? operations : operations?.operations ?? [];
|
|
4895
|
+
return arr;
|
|
4896
|
+
})();
|
|
4897
|
+
const op = operationName ? ops.find((o) => o.name === operationName) : void 0;
|
|
4898
|
+
const adapters = op?.connectionAdapters ?? op?.credentialAdapters ?? nodeData?.connectionAdapters ?? nodeData?.credentialAdapters ?? [];
|
|
4899
|
+
return adapters.filter(
|
|
4900
|
+
(adapter) => !isAutoManagedConnectionAdapter(nodeType, adapter)
|
|
4901
|
+
);
|
|
4902
|
+
}
|
|
4903
|
+
function isAdapterCompatible(adapterFilter, adapterType, targetNodeKind, targetAdapterOutputType, targetNodeType) {
|
|
4904
|
+
if (adapterType && targetAdapterOutputType && adapterType !== targetAdapterOutputType) {
|
|
4905
|
+
return false;
|
|
4906
|
+
}
|
|
4907
|
+
if (adapterFilter?.kind && adapterFilter.kind.length > 0) {
|
|
4908
|
+
if (!targetNodeKind || !adapterFilter.kind.every((k) => targetNodeKind.includes(k))) {
|
|
4909
|
+
return false;
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4912
|
+
if (adapterFilter?.excludeNodes && targetNodeType) {
|
|
4913
|
+
if (adapterFilter.excludeNodes.includes(targetNodeType)) {
|
|
4914
|
+
return false;
|
|
4915
|
+
}
|
|
4916
|
+
}
|
|
4917
|
+
return true;
|
|
4918
|
+
}
|
|
4919
|
+
function useCompatibleSubNodes(adapter) {
|
|
4920
|
+
return useMemo(() => {
|
|
4921
|
+
const out = [];
|
|
4922
|
+
for (const cfg of NODE_CONFIGS) {
|
|
4923
|
+
const nodeData = cfg.defaultData?.nodeData;
|
|
4924
|
+
if (!nodeData?.adapterOutputType && !nodeData?.supportsAdapterMode) {
|
|
4925
|
+
continue;
|
|
4926
|
+
}
|
|
4927
|
+
if (!isAdapterCompatible(
|
|
4928
|
+
adapter.filter,
|
|
4929
|
+
adapter.type,
|
|
4930
|
+
nodeData?.kind,
|
|
4931
|
+
nodeData?.adapterOutputType,
|
|
4932
|
+
cfg.type
|
|
4933
|
+
)) {
|
|
4934
|
+
continue;
|
|
4935
|
+
}
|
|
4936
|
+
const ops = cfg.operations ?? [];
|
|
4937
|
+
out.push({
|
|
4938
|
+
type: cfg.type,
|
|
4939
|
+
title: cfg.detail?.title || nodeData?.name || cfg.type,
|
|
4940
|
+
icon: cfg.detail?.icon || nodeData?.icon || "mynaui:puzzle",
|
|
4941
|
+
description: cfg.detail?.description || nodeData?.description,
|
|
4942
|
+
adapterOperation: ops.find((o) => o.isAdapterOperation)?.name
|
|
4943
|
+
});
|
|
4944
|
+
}
|
|
4945
|
+
return out.sort((a, b) => a.title.localeCompare(b.title));
|
|
4946
|
+
}, [adapter.filter, adapter.type]);
|
|
4947
|
+
}
|
|
4948
|
+
function AdapterPort({
|
|
4949
|
+
nodeId,
|
|
4950
|
+
adapter,
|
|
4951
|
+
index,
|
|
4952
|
+
total
|
|
4953
|
+
}) {
|
|
4954
|
+
const tr = useTr();
|
|
4955
|
+
const edges = useStore((s) => s.edges);
|
|
4956
|
+
const { getNode } = useReactFlow();
|
|
4957
|
+
const insertNode = useInsertNode();
|
|
4958
|
+
const onConnect = useFlowStore((s) => s.actions.edges.onConnect);
|
|
4959
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
4960
|
+
const candidates = useCompatibleSubNodes(adapter);
|
|
4961
|
+
const connectionCount = edges.filter(
|
|
4962
|
+
(e) => e.source === nodeId && e.sourceHandle === adapter.name
|
|
4963
|
+
).length;
|
|
4964
|
+
const isAtMaxConnections = adapter.maxConnections != null && adapter.maxConnections !== void 0 && connectionCount >= adapter.maxConnections;
|
|
4965
|
+
const hasConnection = connectionCount > 0;
|
|
4966
|
+
const diamondColor = hasConnection ? "bg-[var(--color-accent-primary)]" : "bg-[var(--color-text-muted)]";
|
|
4967
|
+
const lineColor = hasConnection ? "before:bg-[var(--color-accent-primary)]" : "before:bg-[var(--color-border-strong)]";
|
|
4968
|
+
const addSubNode = useCallback(
|
|
4969
|
+
(candidate) => {
|
|
4970
|
+
setPickerOpen(false);
|
|
4971
|
+
const parent = getNode(nodeId);
|
|
4972
|
+
const origin = parent?.position ?? { x: 0, y: 0 };
|
|
4973
|
+
const parentHeight = parent?.measured?.height ?? 180;
|
|
4974
|
+
const SPACING = 180;
|
|
4975
|
+
const position = {
|
|
4976
|
+
x: origin.x + (index - (total - 1) / 2) * SPACING,
|
|
4977
|
+
y: origin.y + parentHeight + 190
|
|
4978
|
+
};
|
|
4979
|
+
const created = insertNode(
|
|
4980
|
+
candidate.type,
|
|
4981
|
+
position,
|
|
4982
|
+
candidate.adapterOperation
|
|
4983
|
+
);
|
|
4984
|
+
if (!created?.id) return;
|
|
4985
|
+
created.data = {
|
|
4986
|
+
...created.data,
|
|
4987
|
+
isAdapterInstance: true
|
|
4988
|
+
};
|
|
4989
|
+
onConnect({
|
|
4990
|
+
source: nodeId,
|
|
4991
|
+
sourceHandle: adapter.name,
|
|
4992
|
+
target: created.id,
|
|
4993
|
+
targetHandle: "target"
|
|
4994
|
+
});
|
|
4995
|
+
},
|
|
4996
|
+
[getNode, nodeId, index, total, insertNode, onConnect, adapter.name]
|
|
4997
|
+
);
|
|
4998
|
+
return /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsxs(
|
|
4999
|
+
"div",
|
|
5000
|
+
{
|
|
5001
|
+
className: `p-0.5 m-0.5 text-center before:content-[''] before:h-[95%] before:left-1/2 before:-translate-x-1/2 before:w-0.5 ${lineColor} before:absolute before:top-0 before:pointer-events-none`,
|
|
5002
|
+
children: [
|
|
5003
|
+
/* @__PURE__ */ jsx("div", { className: "absolute -top-3 left-1/2 transform -translate-x-1/2 pointer-events-none", children: /* @__PURE__ */ jsx(
|
|
5004
|
+
"div",
|
|
5005
|
+
{
|
|
5006
|
+
className: `w-3 h-3 ${diamondColor} rotate-45 transition-transform hover:scale-110`
|
|
5007
|
+
}
|
|
5008
|
+
) }),
|
|
5009
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 z-10 absolute left-1/2 transform -translate-x-1/2 font-medium bg-surface h-fit pointer-events-none", children: /* @__PURE__ */ jsxs("span", { className: "text-xs max-w-fit relative font-medium text-[var(--color-text-secondary)] whitespace-nowrap", children: [
|
|
5010
|
+
adapter?.name,
|
|
5011
|
+
adapter?.required && /* @__PURE__ */ jsx("span", { className: "text-[var(--color-status-error-text)] absolute -right-2 top-[1px]", children: "*" })
|
|
5012
|
+
] }) }),
|
|
5013
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-16 flex flex-col items-center gap-1.5", children: [
|
|
5014
|
+
!isAtMaxConnections && /* @__PURE__ */ jsx(
|
|
5015
|
+
"button",
|
|
5016
|
+
{
|
|
5017
|
+
type: "button",
|
|
5018
|
+
title: `Add ${adapter.name}`,
|
|
5019
|
+
"aria-label": `Add ${adapter.name}`,
|
|
5020
|
+
onClick: (e) => {
|
|
5021
|
+
e.stopPropagation();
|
|
5022
|
+
setPickerOpen((v) => !v);
|
|
5023
|
+
},
|
|
5024
|
+
className: "w-5 h-5 border-2 border-[var(--color-border-strong)] rounded text-[var(--color-text-secondary)] text-lg leading-none font-bold bg-surface flex items-center justify-center transition-colors hover:border-[var(--color-accent-primary)] hover:text-[var(--color-accent-primary)] cursor-pointer",
|
|
5025
|
+
children: "+"
|
|
5026
|
+
}
|
|
5027
|
+
),
|
|
5028
|
+
isAtMaxConnections && /* @__PURE__ */ jsx(
|
|
5029
|
+
"div",
|
|
5030
|
+
{
|
|
5031
|
+
title: `${adapter.name} is full`,
|
|
5032
|
+
className: "w-5 h-5 border-2 border-[var(--color-border-subtle)] rounded text-[var(--color-text-disabled)] text-lg leading-none font-bold bg-surface flex items-center justify-center",
|
|
5033
|
+
children: "\u2022"
|
|
5034
|
+
}
|
|
5035
|
+
),
|
|
5036
|
+
/* @__PURE__ */ jsx(
|
|
5037
|
+
Handle,
|
|
5038
|
+
{
|
|
5039
|
+
type: "source",
|
|
5040
|
+
position: Position.Bottom,
|
|
5041
|
+
id: adapter?.name,
|
|
5042
|
+
className: "!relative !top-auto !left-auto !right-auto !bottom-auto !transform-none !w-2.5 !h-2.5 !min-w-0 !min-h-0 !rounded-full !border-0 bg-[var(--color-accent-primary)]",
|
|
5043
|
+
isConnectable: !isAtMaxConnections,
|
|
5044
|
+
isValidConnection: (connection) => {
|
|
5045
|
+
if (!adapter.type && !adapter.filter) return true;
|
|
5046
|
+
const targetNode = getNode(connection.target);
|
|
5047
|
+
if (!targetNode) return true;
|
|
5048
|
+
const targetData = targetNode.data;
|
|
5049
|
+
const targetNodeData = targetData?.nodeData;
|
|
5050
|
+
return isAdapterCompatible(
|
|
5051
|
+
adapter.filter,
|
|
5052
|
+
adapter.type,
|
|
5053
|
+
targetNodeData?.kind,
|
|
5054
|
+
targetNodeData?.adapterOutputType,
|
|
5055
|
+
targetNode.type
|
|
5056
|
+
);
|
|
5057
|
+
}
|
|
5058
|
+
}
|
|
5059
|
+
)
|
|
5060
|
+
] }),
|
|
5061
|
+
pickerOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5062
|
+
/* @__PURE__ */ jsx(
|
|
5063
|
+
"div",
|
|
5064
|
+
{
|
|
5065
|
+
className: "fixed inset-0 z-40",
|
|
5066
|
+
onClick: () => setPickerOpen(false)
|
|
5067
|
+
}
|
|
5068
|
+
),
|
|
5069
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute z-50 top-[86px] left-1/2 -translate-x-1/2 w-60 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-surface-elevated)] shadow-xl overflow-hidden text-left nodrag nowheel", children: [
|
|
5070
|
+
/* @__PURE__ */ jsxs("div", { className: "px-3 py-2 border-b border-[var(--color-border-subtle)]", children: [
|
|
5071
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs font-semibold text-[var(--color-text-primary)]", children: [
|
|
5072
|
+
tr("fluidgrids.builder.adapterPort.addPrefix", "Add"),
|
|
5073
|
+
" ",
|
|
5074
|
+
adapter.name
|
|
5075
|
+
] }),
|
|
5076
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-[var(--color-text-muted)]", children: [
|
|
5077
|
+
candidates.length,
|
|
5078
|
+
" compatible node",
|
|
5079
|
+
candidates.length === 1 ? "" : "s"
|
|
5080
|
+
] })
|
|
5081
|
+
] }),
|
|
5082
|
+
/* @__PURE__ */ jsxs("div", { className: "max-h-56 overflow-y-auto", children: [
|
|
5083
|
+
candidates.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-3 py-3 text-[11px] text-[var(--color-text-muted)]", children: tr(
|
|
5084
|
+
"fluidgrids.builder.adapterPort.noCompatibleNodes",
|
|
5085
|
+
"No compatible node installed. Publish and install one from the Node Store first."
|
|
5086
|
+
) }),
|
|
5087
|
+
candidates.map((c) => /* @__PURE__ */ jsxs(
|
|
5088
|
+
"button",
|
|
5089
|
+
{
|
|
5090
|
+
type: "button",
|
|
5091
|
+
onClick: (e) => {
|
|
5092
|
+
e.stopPropagation();
|
|
5093
|
+
addSubNode(c);
|
|
5094
|
+
},
|
|
5095
|
+
className: "w-full flex items-start gap-2 px-3 py-2 hover:bg-[var(--color-bg-elevated)] transition-colors text-left",
|
|
5096
|
+
children: [
|
|
5097
|
+
/* @__PURE__ */ jsx(
|
|
5098
|
+
NodeTypeIcon,
|
|
5099
|
+
{
|
|
5100
|
+
icon: c.icon,
|
|
5101
|
+
alt: c.title,
|
|
5102
|
+
className: "size-4 mt-0.5 shrink-0 text-[var(--color-accent-primary)]"
|
|
5103
|
+
}
|
|
5104
|
+
),
|
|
5105
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
5106
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs text-[var(--color-text-primary)] truncate", children: c.title }),
|
|
5107
|
+
c.description && /* @__PURE__ */ jsx("span", { className: "block text-[10px] text-[var(--color-text-muted)] line-clamp-2", children: c.description })
|
|
5108
|
+
] })
|
|
5109
|
+
]
|
|
5110
|
+
},
|
|
5111
|
+
c.type
|
|
5112
|
+
))
|
|
5113
|
+
] })
|
|
5114
|
+
] })
|
|
5115
|
+
] })
|
|
5116
|
+
]
|
|
5117
|
+
}
|
|
5118
|
+
) });
|
|
5119
|
+
}
|
|
5120
|
+
function AdapterConnectors({
|
|
5121
|
+
nodeId,
|
|
5122
|
+
adapters
|
|
5123
|
+
}) {
|
|
5124
|
+
if (!adapters || adapters.length === 0) return null;
|
|
5125
|
+
return /* @__PURE__ */ jsx("div", { className: "left-0 w-full flex justify-around mt-[6.5px]", children: adapters.map((adapter, i) => /* @__PURE__ */ jsx(
|
|
5126
|
+
AdapterPort,
|
|
5127
|
+
{
|
|
5128
|
+
nodeId,
|
|
5129
|
+
adapter,
|
|
5130
|
+
index: i,
|
|
5131
|
+
total: adapters.length
|
|
5132
|
+
},
|
|
5133
|
+
adapter.name
|
|
5134
|
+
)) });
|
|
5191
5135
|
}
|
|
5192
|
-
function
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5136
|
+
function SubNodeComponent({
|
|
5137
|
+
id,
|
|
5138
|
+
isConnectable,
|
|
5139
|
+
selected,
|
|
5140
|
+
data,
|
|
5141
|
+
nodeType
|
|
5142
|
+
}) {
|
|
5143
|
+
const meta = useMemo(() => getNodeDetail(nodeType), [nodeType]);
|
|
5144
|
+
const deleteNode = useDeleteNode();
|
|
5145
|
+
const [showNodePropertiesOf] = useFlowStore(
|
|
5146
|
+
useShallow((s) => [s.actions.sidebar.showNodePropertiesOf])
|
|
5147
|
+
);
|
|
5148
|
+
const openNdv = useFlowStore((s) => s.actions.ndv.open);
|
|
5149
|
+
const [nodeStatuses] = useFlowStore(
|
|
5150
|
+
useShallow((s) => [s.execution.nodeStatuses])
|
|
5151
|
+
);
|
|
5152
|
+
const nodeStatus = nodeStatuses[id] || "idle";
|
|
5153
|
+
const statusRing = nodeStatus === "success" ? "ring-2 ring-[var(--color-status-success-border)]" : nodeStatus === "error" ? "ring-2 ring-[var(--color-status-error-border)]" : nodeStatus === "running" ? "ring-2 ring-[var(--color-status-info-border)] animate-pulse" : "";
|
|
5154
|
+
const handleShowNodeProperties = useCallback(() => {
|
|
5155
|
+
openNdv({ nodeId: id, type: nodeType });
|
|
5156
|
+
showNodePropertiesOf({ id, type: nodeType });
|
|
5157
|
+
}, [id, nodeType, openNdv, showNodePropertiesOf]);
|
|
5158
|
+
return (
|
|
5159
|
+
// `relative` anchors the absolute delete button; `group` enables its hover reveal.
|
|
5160
|
+
/* @__PURE__ */ jsxs("div", { className: "relative group flex flex-col items-center", children: [
|
|
5161
|
+
/* @__PURE__ */ jsx(
|
|
5162
|
+
Handle,
|
|
5163
|
+
{
|
|
5164
|
+
type: "target",
|
|
5165
|
+
position: Position.Top,
|
|
5166
|
+
id: "target",
|
|
5167
|
+
isConnectable,
|
|
5168
|
+
className: "size-2.5 bg-[var(--color-accent-primary)] border-2 border-[var(--color-border-strong)]"
|
|
5169
|
+
}
|
|
5170
|
+
),
|
|
5171
|
+
/* @__PURE__ */ jsx("div", { className: "w-0.5 h-4 border-l-2 border-dashed border-[var(--color-border-strong)]" }),
|
|
5172
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-[var(--color-text-muted)] font-medium mb-1", children: meta.title.includes("Model") ? "Model" : meta.title.includes("Memory") ? "Memory" : meta.title.includes("Tool") ? "Tool" : meta.title.includes("Parser") ? "Parser" : "Adapter" }),
|
|
5173
|
+
/* @__PURE__ */ jsx(
|
|
5174
|
+
"div",
|
|
5175
|
+
{
|
|
5176
|
+
className: `w-14 h-14 rounded-full bg-[var(--color-surface-elevated)] border-2 border-[var(--color-border-strong)] flex items-center justify-center shadow-lg cursor-pointer transition-all hover:border-[var(--color-accent-primary)] hover:shadow-xl ${statusRing}`,
|
|
5177
|
+
onDoubleClick: handleShowNodeProperties,
|
|
5178
|
+
"data-selected": selected,
|
|
5179
|
+
children: /* @__PURE__ */ jsx(
|
|
5180
|
+
NodeTypeIcon,
|
|
5181
|
+
{
|
|
5182
|
+
icon: meta.icon,
|
|
5183
|
+
alt: meta.title,
|
|
5184
|
+
className: "w-7 h-7 text-accent opacity-80"
|
|
5185
|
+
}
|
|
5186
|
+
)
|
|
5187
|
+
}
|
|
5188
|
+
),
|
|
5189
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1.5 text-xs text-[var(--color-text-secondary)] font-medium text-center max-w-24 leading-tight", children: data?.label || data?.args?.state?.operation || meta.title }),
|
|
5190
|
+
/* @__PURE__ */ jsx(
|
|
5191
|
+
"button",
|
|
5192
|
+
{
|
|
5193
|
+
onClick: () => deleteNode(id),
|
|
5194
|
+
className: "absolute -top-1 -right-1 w-4 h-4 rounded-full bg-[var(--color-status-error)] text-white text-[10px] flex items-center justify-center opacity-0 group-hover:opacity-100 hover:bg-[var(--color-status-error)] transition-opacity",
|
|
5195
|
+
children: "\xD7"
|
|
5196
|
+
}
|
|
5197
|
+
),
|
|
5198
|
+
/* @__PURE__ */ jsx(AdapterConnectors, { nodeId: id, adapters: data?.nodeData?.nodeAdapters })
|
|
5199
|
+
] })
|
|
5201
5200
|
);
|
|
5202
5201
|
}
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5202
|
+
var SINGLE_CLICK_DELAY_MS = 230;
|
|
5203
|
+
var getNodeMetadata = (value) => {
|
|
5204
|
+
function NodeComponent(props) {
|
|
5205
|
+
const { id, isConnectable, selected, data, dragging } = props;
|
|
5206
|
+
const tr = useTr();
|
|
5207
|
+
const docsUrl = useNodeDocsUrl(
|
|
5208
|
+
value?.type,
|
|
5209
|
+
data?.nodeData?.version || data?.version
|
|
5210
|
+
);
|
|
5211
|
+
const { edges, nodeLookup } = useStore(({ edges: edges2, nodeLookup: nodeLookup2 }) => ({
|
|
5212
|
+
edges: edges2,
|
|
5213
|
+
nodeLookup: nodeLookup2
|
|
5214
|
+
}));
|
|
5215
|
+
const isAdapterSubNode = useMemo(() => {
|
|
5216
|
+
if (data?.isAdapterInstance) return true;
|
|
5217
|
+
const incomingEdges = edges.filter((e) => e.target === id);
|
|
5218
|
+
for (const edge of incomingEdges) {
|
|
5219
|
+
if (!edge.sourceHandle || edge.sourceHandle === "source") continue;
|
|
5220
|
+
const sourceNode = nodeLookup.get(edge.source);
|
|
5221
|
+
if (!sourceNode) continue;
|
|
5222
|
+
const sourceNodeData = sourceNode.data;
|
|
5223
|
+
const sourceAdapters = sourceNodeData?.nodeData?.nodeAdapters;
|
|
5224
|
+
if (sourceAdapters && Array.isArray(sourceAdapters)) {
|
|
5225
|
+
const adapterNames = sourceAdapters.map((a) => a.name);
|
|
5226
|
+
if (adapterNames.includes(edge.sourceHandle)) {
|
|
5227
|
+
return true;
|
|
5228
|
+
}
|
|
5229
|
+
}
|
|
5230
|
+
}
|
|
5214
5231
|
return false;
|
|
5215
|
-
}
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
}
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5232
|
+
}, [data?.isAdapterInstance, edges, id, nodeLookup]);
|
|
5233
|
+
const nodeType = value?.type;
|
|
5234
|
+
const meta = useMemo(() => getNodeDetail(nodeType), [nodeType]);
|
|
5235
|
+
const { setNodes } = useReactFlow();
|
|
5236
|
+
const [
|
|
5237
|
+
isExecuting,
|
|
5238
|
+
executingNodeId,
|
|
5239
|
+
nodeStatuses,
|
|
5240
|
+
nodeItemCounts,
|
|
5241
|
+
zustandSetNodes,
|
|
5242
|
+
renamingNodeId,
|
|
5243
|
+
toggleNodeDisabled,
|
|
5244
|
+
renameNode,
|
|
5245
|
+
startRename,
|
|
5246
|
+
endRename
|
|
5247
|
+
] = useFlowStore(
|
|
5248
|
+
useShallow((s) => [
|
|
5249
|
+
s.execution.isExecuting,
|
|
5250
|
+
s.execution.executingNodeId,
|
|
5251
|
+
s.execution.nodeStatuses,
|
|
5252
|
+
s.execution.nodeItemCounts,
|
|
5253
|
+
s.actions.nodes.setNodes,
|
|
5254
|
+
s.workflow.renamingNodeId,
|
|
5255
|
+
s.actions.nodes.toggleDisabled,
|
|
5256
|
+
s.actions.nodes.renameNode,
|
|
5257
|
+
s.actions.nodes.startRename,
|
|
5258
|
+
s.actions.nodes.endRename
|
|
5259
|
+
])
|
|
5260
|
+
);
|
|
5261
|
+
useEffect(() => {
|
|
5262
|
+
if (nodeType !== "integration/channel") return;
|
|
5263
|
+
const nodeData = data;
|
|
5264
|
+
const explicitRole = nodeData?.args?.state?.role;
|
|
5265
|
+
const hasIncomingEdge = edges.some((e) => e.target === id);
|
|
5266
|
+
const isAction = explicitRole === "action" || !explicitRole && hasIncomingEdge;
|
|
5267
|
+
const expectedOp = resolveOperationName(
|
|
5268
|
+
nodeData?.operation_name,
|
|
5269
|
+
isAction ? "sendChannelMessage" : "onChannelMessageReceived"
|
|
5270
|
+
);
|
|
5271
|
+
if (nodeData?.operation_name === expectedOp) return;
|
|
5272
|
+
setNodes((nds) => {
|
|
5273
|
+
const updated = nds.map(
|
|
5274
|
+
(n) => n.id === id ? {
|
|
5275
|
+
...n,
|
|
5276
|
+
data: {
|
|
5277
|
+
...n.data,
|
|
5278
|
+
...synchronizeOperationUpdate(
|
|
5279
|
+
n.data,
|
|
5280
|
+
{ operation_name: expectedOp },
|
|
5281
|
+
NODE_CONFIGS.find((config2) => config2.type === nodeType)?.operations
|
|
5282
|
+
)
|
|
5283
|
+
}
|
|
5284
|
+
} : n
|
|
5285
|
+
);
|
|
5286
|
+
zustandSetNodes(updated);
|
|
5287
|
+
return updated;
|
|
5288
|
+
});
|
|
5289
|
+
}, [nodeType, id, edges, data, setNodes, zustandSetNodes]);
|
|
5290
|
+
const { executeStep } = useNodeExecution();
|
|
5291
|
+
const deleteNode = useDeleteNode();
|
|
5292
|
+
const handleDeleteNode = () => {
|
|
5293
|
+
deleteNode(id);
|
|
5294
|
+
};
|
|
5295
|
+
const handleToggleDisabled = useCallback(() => {
|
|
5296
|
+
toggleNodeDisabled(id);
|
|
5297
|
+
}, [id, toggleNodeDisabled]);
|
|
5298
|
+
const isRenamingThisNode = renamingNodeId === id;
|
|
5299
|
+
const handleStartRename = useCallback(() => {
|
|
5300
|
+
startRename(id);
|
|
5301
|
+
}, [id, startRename]);
|
|
5302
|
+
const handleCommitRename = useCallback(
|
|
5303
|
+
(value2) => {
|
|
5304
|
+
renameNode(id, value2);
|
|
5305
|
+
},
|
|
5306
|
+
[id, renameNode]
|
|
5307
|
+
);
|
|
5308
|
+
const handleCancelRename = useCallback(() => {
|
|
5309
|
+
endRename();
|
|
5310
|
+
}, [endRename]);
|
|
5311
|
+
const openNdv = useFlowStore((s) => s.actions.ndv.open);
|
|
5312
|
+
const openQuickConfig = useFlowStore((s) => s.actions.quickConfig.open);
|
|
5313
|
+
const clickTimerRef = useRef(null);
|
|
5314
|
+
const wasDraggedRef = useRef(false);
|
|
5315
|
+
useEffect(() => {
|
|
5316
|
+
if (dragging) wasDraggedRef.current = true;
|
|
5317
|
+
}, [dragging]);
|
|
5318
|
+
useEffect(
|
|
5319
|
+
() => () => {
|
|
5320
|
+
if (clickTimerRef.current !== null)
|
|
5321
|
+
window.clearTimeout(clickTimerRef.current);
|
|
5322
|
+
},
|
|
5323
|
+
[]
|
|
5324
|
+
);
|
|
5325
|
+
const handleCardClick = useCallback(() => {
|
|
5326
|
+
if (wasDraggedRef.current) {
|
|
5327
|
+
wasDraggedRef.current = false;
|
|
5328
|
+
return;
|
|
5226
5329
|
}
|
|
5227
|
-
if (
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5330
|
+
if (clickTimerRef.current !== null)
|
|
5331
|
+
window.clearTimeout(clickTimerRef.current);
|
|
5332
|
+
clickTimerRef.current = window.setTimeout(() => {
|
|
5333
|
+
clickTimerRef.current = null;
|
|
5334
|
+
openQuickConfig({
|
|
5335
|
+
nodeId: id,
|
|
5336
|
+
type: value?.type
|
|
5337
|
+
});
|
|
5338
|
+
}, SINGLE_CLICK_DELAY_MS);
|
|
5339
|
+
}, [id, openQuickConfig]);
|
|
5340
|
+
const handleOpenNdv = useCallback(() => {
|
|
5341
|
+
if (clickTimerRef.current !== null) {
|
|
5342
|
+
window.clearTimeout(clickTimerRef.current);
|
|
5343
|
+
clickTimerRef.current = null;
|
|
5235
5344
|
}
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
adapterOperation: ops.find((o) => o.isAdapterOperation)?.name
|
|
5345
|
+
openNdv({ nodeId: id, type: value?.type });
|
|
5346
|
+
}, [id, openNdv]);
|
|
5347
|
+
const handleExecuteStep = useCallback(() => {
|
|
5348
|
+
executeStep(id).then((_result) => {
|
|
5349
|
+
}).catch((error) => {
|
|
5350
|
+
console.error("[NodeComponent] Execute step error:", error);
|
|
5243
5351
|
});
|
|
5244
|
-
}
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
index,
|
|
5252
|
-
total
|
|
5253
|
-
}) {
|
|
5254
|
-
const tr = useTr();
|
|
5255
|
-
const edges = useStore((s) => s.edges);
|
|
5256
|
-
const { getNode } = useReactFlow();
|
|
5257
|
-
const insertNode = useInsertNode();
|
|
5258
|
-
const onConnect = useFlowStore((s) => s.actions.edges.onConnect);
|
|
5259
|
-
const [pickerOpen, setPickerOpen] = useState(false);
|
|
5260
|
-
const candidates = useCompatibleSubNodes(adapter);
|
|
5261
|
-
const connectionCount = edges.filter(
|
|
5262
|
-
(e) => e.source === nodeId && e.sourceHandle === adapter.name
|
|
5263
|
-
).length;
|
|
5264
|
-
const isAtMaxConnections = adapter.maxConnections != null && adapter.maxConnections !== void 0 && connectionCount >= adapter.maxConnections;
|
|
5265
|
-
const hasConnection = connectionCount > 0;
|
|
5266
|
-
const diamondColor = hasConnection ? "bg-[var(--color-accent-primary)]" : "bg-[var(--color-text-muted)]";
|
|
5267
|
-
const lineColor = hasConnection ? "before:bg-[var(--color-accent-primary)]" : "before:bg-[var(--color-border-strong)]";
|
|
5268
|
-
const addSubNode = useCallback(
|
|
5269
|
-
(candidate) => {
|
|
5270
|
-
setPickerOpen(false);
|
|
5271
|
-
const parent = getNode(nodeId);
|
|
5272
|
-
const origin = parent?.position ?? { x: 0, y: 0 };
|
|
5273
|
-
const parentHeight = parent?.measured?.height ?? 180;
|
|
5274
|
-
const SPACING = 180;
|
|
5275
|
-
const position = {
|
|
5276
|
-
x: origin.x + (index - (total - 1) / 2) * SPACING,
|
|
5277
|
-
y: origin.y + parentHeight + 190
|
|
5278
|
-
};
|
|
5279
|
-
const created = insertNode(
|
|
5280
|
-
candidate.type,
|
|
5281
|
-
position,
|
|
5282
|
-
candidate.adapterOperation
|
|
5352
|
+
}, [executeStep, id]);
|
|
5353
|
+
const missingCredentials = useMemo(() => {
|
|
5354
|
+
const visibleAdapters = getVisibleConnectionAdapters(
|
|
5355
|
+
String(value?.type ?? ""),
|
|
5356
|
+
data?.operation_name,
|
|
5357
|
+
value?.operations ?? data?.nodeData?.operations,
|
|
5358
|
+
data?.nodeData
|
|
5283
5359
|
);
|
|
5284
|
-
if (
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
{
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
candidates.length === 1 ? "" : "s"
|
|
5380
|
-
] })
|
|
5381
|
-
] }),
|
|
5382
|
-
/* @__PURE__ */ jsxs("div", { className: "max-h-56 overflow-y-auto", children: [
|
|
5383
|
-
candidates.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-3 py-3 text-[11px] text-[var(--color-text-muted)]", children: tr(
|
|
5384
|
-
"fluidgrids.builder.adapterPort.noCompatibleNodes",
|
|
5385
|
-
"No compatible node installed. Publish and install one from the Node Store first."
|
|
5386
|
-
) }),
|
|
5387
|
-
candidates.map((c) => /* @__PURE__ */ jsxs(
|
|
5388
|
-
"button",
|
|
5389
|
-
{
|
|
5390
|
-
type: "button",
|
|
5391
|
-
onClick: (e) => {
|
|
5392
|
-
e.stopPropagation();
|
|
5393
|
-
addSubNode(c);
|
|
5394
|
-
},
|
|
5395
|
-
className: "w-full flex items-start gap-2 px-3 py-2 hover:bg-[var(--color-bg-elevated)] transition-colors text-left",
|
|
5396
|
-
children: [
|
|
5397
|
-
/* @__PURE__ */ jsx(
|
|
5398
|
-
NodeTypeIcon,
|
|
5399
|
-
{
|
|
5400
|
-
icon: c.icon,
|
|
5401
|
-
alt: c.title,
|
|
5402
|
-
className: "size-4 mt-0.5 shrink-0 text-[var(--color-accent-primary)]"
|
|
5403
|
-
}
|
|
5404
|
-
),
|
|
5405
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
5406
|
-
/* @__PURE__ */ jsx("span", { className: "block text-xs text-[var(--color-text-primary)] truncate", children: c.title }),
|
|
5407
|
-
c.description && /* @__PURE__ */ jsx("span", { className: "block text-[10px] text-[var(--color-text-muted)] line-clamp-2", children: c.description })
|
|
5408
|
-
] })
|
|
5409
|
-
]
|
|
5410
|
-
},
|
|
5411
|
-
c.type
|
|
5412
|
-
))
|
|
5413
|
-
] })
|
|
5414
|
-
] })
|
|
5415
|
-
] })
|
|
5416
|
-
]
|
|
5360
|
+
if (visibleAdapters.length === 0) return false;
|
|
5361
|
+
const selected2 = data?.args?.connections ?? data?.args?.credentials ?? [];
|
|
5362
|
+
return visibleAdapters.some(
|
|
5363
|
+
(adapter) => !selected2.some((sel) => sel.adapter === adapter.name)
|
|
5364
|
+
);
|
|
5365
|
+
}, [data]);
|
|
5366
|
+
if (isAdapterSubNode) {
|
|
5367
|
+
return /* @__PURE__ */ jsx(SubNodeComponent, { ...props, nodeType: value?.type });
|
|
5368
|
+
}
|
|
5369
|
+
const isThisNodeExecuting = isExecuting && executingNodeId === id;
|
|
5370
|
+
const nodeStatus = nodeStatuses[id] || "idle";
|
|
5371
|
+
const executionStatus = nodeStatus === "success" ? "success" : nodeStatus === "error" ? "error" : nodeStatus === "running" ? "running" : "idle";
|
|
5372
|
+
const itemCount = nodeItemCounts[id];
|
|
5373
|
+
const isDisabled = data?.disabled === true;
|
|
5374
|
+
const nodeLabel = data?.label || "";
|
|
5375
|
+
const nodeKinds = data?.nodeData?.kind;
|
|
5376
|
+
const isTrigger = isNodeTrigger({ type: value?.type, data });
|
|
5377
|
+
const savedManifest = data?.nodeData;
|
|
5378
|
+
const isRegistryFallback = meta.title === value?.type;
|
|
5379
|
+
const appName = (isRegistryFallback ? savedManifest?.name : meta.title) || meta.title || nodeType || tr("fluidgrids.builder.nodeCard.defaultAppName", "Node");
|
|
5380
|
+
const cardIcon = (isRegistryFallback ? savedManifest?.icon : meta.icon) || meta.icon;
|
|
5381
|
+
const operationDisplay = resolveOperationName(
|
|
5382
|
+
data?.operation_name,
|
|
5383
|
+
tr("fluidgrids.builder.nodeCard.notConfigured", "Not configured")
|
|
5384
|
+
);
|
|
5385
|
+
const nodeDescription = data?.nodeData?.description || meta?.description || "";
|
|
5386
|
+
const nodeInfoMetadata = {
|
|
5387
|
+
icon: meta.icon,
|
|
5388
|
+
title: meta.title,
|
|
5389
|
+
description: nodeDescription,
|
|
5390
|
+
nodeType: value?.type,
|
|
5391
|
+
version: data?.nodeData?.version || data?.version,
|
|
5392
|
+
kind: nodeKinds,
|
|
5393
|
+
operation: data?.operation_name,
|
|
5394
|
+
docsUrl
|
|
5395
|
+
};
|
|
5396
|
+
const isLogicNode = /(^|[:/])(if|switch|merge|loop|filter|branch|router|condition|aggregate)/.test(
|
|
5397
|
+
(value?.type || "").toLowerCase()
|
|
5398
|
+
);
|
|
5399
|
+
const roleAccent = isTrigger ? "#10b981" : isLogicNode ? "#f59e0b" : "#3b82f6";
|
|
5400
|
+
const outputHandles = resolveOutputHandles(
|
|
5401
|
+
data
|
|
5402
|
+
);
|
|
5403
|
+
const inputHandles = resolveInputHandles(
|
|
5404
|
+
data
|
|
5405
|
+
);
|
|
5406
|
+
const handleSpacer = outputHandlesSpacerHeight(
|
|
5407
|
+
Math.max(outputHandles.length, inputHandles.length)
|
|
5408
|
+
);
|
|
5409
|
+
const manifestKind = data?.nodeData?.kind;
|
|
5410
|
+
const isControlFlowNode = (value?.type || "").startsWith("internal:") && (Array.isArray(manifestKind) ? manifestKind.includes("control") : isLogicNode);
|
|
5411
|
+
if (isControlFlowNode) {
|
|
5412
|
+
const argsState = data?.args?.state;
|
|
5413
|
+
const rules = argsState?.rules;
|
|
5414
|
+
const mode = argsState?.mode;
|
|
5415
|
+
const logicSubtitle = Array.isArray(rules) ? `${rules.length} rule${rules.length === 1 ? "" : "s"}` : typeof mode === "string" ? mode : void 0;
|
|
5416
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
5417
|
+
/* @__PURE__ */ jsx(
|
|
5418
|
+
LogicNodeCard,
|
|
5419
|
+
{
|
|
5420
|
+
nodeId: id,
|
|
5421
|
+
title: data?.label || meta.title,
|
|
5422
|
+
subtitle: logicSubtitle,
|
|
5423
|
+
icon: meta.icon,
|
|
5424
|
+
accent: roleAccent,
|
|
5425
|
+
selected: !!selected,
|
|
5426
|
+
executionStatus,
|
|
5427
|
+
itemCount,
|
|
5428
|
+
isTrigger,
|
|
5429
|
+
isExecuting: isThisNodeExecuting,
|
|
5430
|
+
inputHandles,
|
|
5431
|
+
outputHandles,
|
|
5432
|
+
isConnectable: typeof isConnectable === "boolean" ? isConnectable : void 0,
|
|
5433
|
+
onClick: handleCardClick,
|
|
5434
|
+
onDoubleClick: handleOpenNdv,
|
|
5435
|
+
onSettings: handleOpenNdv,
|
|
5436
|
+
onExecute: handleExecuteStep,
|
|
5437
|
+
onDelete: handleDeleteNode,
|
|
5438
|
+
infoButton: /* @__PURE__ */ jsx(NodeInfoButtonWithModal, { metadata: nodeInfoMetadata }),
|
|
5439
|
+
disabled: isDisabled,
|
|
5440
|
+
onToggleDisabled: handleToggleDisabled,
|
|
5441
|
+
isRenaming: isRenamingThisNode,
|
|
5442
|
+
onStartRename: handleStartRename,
|
|
5443
|
+
onCommitRename: handleCommitRename,
|
|
5444
|
+
onCancelRename: handleCancelRename
|
|
5445
|
+
}
|
|
5446
|
+
),
|
|
5447
|
+
/* @__PURE__ */ jsx(
|
|
5448
|
+
AdapterConnectors,
|
|
5449
|
+
{
|
|
5450
|
+
nodeId: id,
|
|
5451
|
+
adapters: data?.nodeData?.nodeAdapters
|
|
5452
|
+
}
|
|
5453
|
+
)
|
|
5454
|
+
] });
|
|
5417
5455
|
}
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
nodeId,
|
|
5422
|
-
adapters
|
|
5423
|
-
}) {
|
|
5424
|
-
if (!adapters || adapters.length === 0) return null;
|
|
5425
|
-
return /* @__PURE__ */ jsx("div", { className: "left-0 w-full flex justify-around mt-[6.5px]", children: adapters.map((adapter, i) => /* @__PURE__ */ jsx(
|
|
5426
|
-
AdapterPort,
|
|
5427
|
-
{
|
|
5428
|
-
nodeId,
|
|
5429
|
-
adapter,
|
|
5430
|
-
index: i,
|
|
5431
|
-
total: adapters.length
|
|
5432
|
-
},
|
|
5433
|
-
adapter.name
|
|
5434
|
-
)) });
|
|
5435
|
-
}
|
|
5436
|
-
function SubNodeComponent({
|
|
5437
|
-
id,
|
|
5438
|
-
isConnectable,
|
|
5439
|
-
selected,
|
|
5440
|
-
data,
|
|
5441
|
-
nodeType
|
|
5442
|
-
}) {
|
|
5443
|
-
const meta = useMemo(() => getNodeDetail(nodeType), [nodeType]);
|
|
5444
|
-
const deleteNode = useDeleteNode();
|
|
5445
|
-
const [showNodePropertiesOf] = useFlowStore(
|
|
5446
|
-
useShallow((s) => [s.actions.sidebar.showNodePropertiesOf])
|
|
5447
|
-
);
|
|
5448
|
-
const openNdv = useFlowStore((s) => s.actions.ndv.open);
|
|
5449
|
-
const [nodeStatuses] = useFlowStore(
|
|
5450
|
-
useShallow((s) => [s.execution.nodeStatuses])
|
|
5451
|
-
);
|
|
5452
|
-
const nodeStatus = nodeStatuses[id] || "idle";
|
|
5453
|
-
const statusRing = nodeStatus === "success" ? "ring-2 ring-[var(--color-status-success-border)]" : nodeStatus === "error" ? "ring-2 ring-[var(--color-status-error-border)]" : nodeStatus === "running" ? "ring-2 ring-[var(--color-status-info-border)] animate-pulse" : "";
|
|
5454
|
-
const handleShowNodeProperties = useCallback(() => {
|
|
5455
|
-
openNdv({ nodeId: id, type: nodeType });
|
|
5456
|
-
showNodePropertiesOf({ id, type: nodeType });
|
|
5457
|
-
}, [id, nodeType, openNdv, showNodePropertiesOf]);
|
|
5458
|
-
return (
|
|
5459
|
-
// `relative` anchors the absolute delete button; `group` enables its hover reveal.
|
|
5460
|
-
/* @__PURE__ */ jsxs("div", { className: "relative group flex flex-col items-center", children: [
|
|
5461
|
-
/* @__PURE__ */ jsx(
|
|
5462
|
-
Handle,
|
|
5463
|
-
{
|
|
5464
|
-
type: "target",
|
|
5465
|
-
position: Position.Top,
|
|
5466
|
-
id: "target",
|
|
5467
|
-
isConnectable,
|
|
5468
|
-
className: "size-2.5 bg-[var(--color-accent-primary)] border-2 border-[var(--color-border-strong)]"
|
|
5469
|
-
}
|
|
5470
|
-
),
|
|
5471
|
-
/* @__PURE__ */ jsx("div", { className: "w-0.5 h-4 border-l-2 border-dashed border-[var(--color-border-strong)]" }),
|
|
5472
|
-
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-[var(--color-text-muted)] font-medium mb-1", children: meta.title.includes("Model") ? "Model" : meta.title.includes("Memory") ? "Memory" : meta.title.includes("Tool") ? "Tool" : meta.title.includes("Parser") ? "Parser" : "Adapter" }),
|
|
5473
|
-
/* @__PURE__ */ jsx(
|
|
5474
|
-
"div",
|
|
5456
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
5457
|
+
/* @__PURE__ */ jsxs(
|
|
5458
|
+
NodeCard,
|
|
5475
5459
|
{
|
|
5476
|
-
className: `w-14 h-14 rounded-full bg-[var(--color-surface-elevated)] border-2 border-[var(--color-border-strong)] flex items-center justify-center shadow-lg cursor-pointer transition-all hover:border-[var(--color-accent-primary)] hover:shadow-xl ${statusRing}`,
|
|
5477
|
-
onDoubleClick: handleShowNodeProperties,
|
|
5478
5460
|
"data-selected": selected,
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5461
|
+
selected,
|
|
5462
|
+
onClick: handleCardClick,
|
|
5463
|
+
onDoubleClick: handleOpenNdv,
|
|
5464
|
+
executionStatus,
|
|
5465
|
+
itemCount,
|
|
5466
|
+
isTrigger,
|
|
5467
|
+
accent: roleAccent,
|
|
5468
|
+
isExecuting: isThisNodeExecuting,
|
|
5469
|
+
onSettings: handleOpenNdv,
|
|
5470
|
+
onExecute: handleExecuteStep,
|
|
5471
|
+
onDelete: handleDeleteNode,
|
|
5472
|
+
infoButton: /* @__PURE__ */ jsx(NodeInfoButtonWithModal, { metadata: nodeInfoMetadata }),
|
|
5473
|
+
disabled: isDisabled,
|
|
5474
|
+
onToggleDisabled: handleToggleDisabled,
|
|
5475
|
+
children: [
|
|
5476
|
+
/* @__PURE__ */ jsx(
|
|
5477
|
+
NodeCardHeader,
|
|
5478
|
+
{
|
|
5479
|
+
icon: cardIcon,
|
|
5480
|
+
appName,
|
|
5481
|
+
accentKey: value?.type || meta.title,
|
|
5482
|
+
missingCredentials,
|
|
5483
|
+
label: nodeLabel,
|
|
5484
|
+
isRenaming: isRenamingThisNode,
|
|
5485
|
+
onStartRename: handleStartRename,
|
|
5486
|
+
onCommitRename: handleCommitRename,
|
|
5487
|
+
onCancelRename: handleCancelRename
|
|
5488
|
+
}
|
|
5489
|
+
),
|
|
5490
|
+
operationDisplay === "execute" && (value?.type || "").startsWith("internal:") || isTrigger && !resolveOperationName(data?.operation_name, "") ? /* @__PURE__ */ jsx("div", { className: "rounded-b-xl border-t border-border/60 px-3 pb-3 pt-2.5", children: /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-xs leading-snug text-muted-foreground", children: nodeDescription }) }) : /* @__PURE__ */ jsx(
|
|
5491
|
+
NodeCardBody,
|
|
5492
|
+
{
|
|
5493
|
+
operationName: operationDisplay,
|
|
5494
|
+
description: nodeDescription
|
|
5495
|
+
}
|
|
5496
|
+
),
|
|
5497
|
+
handleSpacer > 0 && /* @__PURE__ */ jsx("div", { style: { height: handleSpacer } }),
|
|
5498
|
+
!isTrigger && /* @__PURE__ */ jsx(
|
|
5499
|
+
InputHandles,
|
|
5500
|
+
{
|
|
5501
|
+
nodeId: id,
|
|
5502
|
+
handles: inputHandles,
|
|
5503
|
+
accent: roleAccent,
|
|
5504
|
+
isConnectable: typeof isConnectable === "boolean" ? isConnectable : void 0
|
|
5505
|
+
}
|
|
5506
|
+
),
|
|
5507
|
+
/* @__PURE__ */ jsx(
|
|
5508
|
+
OutputHandles,
|
|
5509
|
+
{
|
|
5510
|
+
nodeId: id,
|
|
5511
|
+
handles: outputHandles,
|
|
5512
|
+
accent: roleAccent,
|
|
5513
|
+
isConnectable: typeof isConnectable === "boolean" ? isConnectable : void 0
|
|
5514
|
+
}
|
|
5515
|
+
)
|
|
5516
|
+
]
|
|
5487
5517
|
}
|
|
5488
5518
|
),
|
|
5489
|
-
/* @__PURE__ */ jsx("span", { className: "mt-1.5 text-xs text-[var(--color-text-secondary)] font-medium text-center max-w-24 leading-tight", children: data?.label || data?.args?.state?.operation || meta.title }),
|
|
5490
5519
|
/* @__PURE__ */ jsx(
|
|
5491
|
-
|
|
5520
|
+
AdapterConnectors,
|
|
5492
5521
|
{
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
children: "\xD7"
|
|
5522
|
+
nodeId: id,
|
|
5523
|
+
adapters: data?.nodeData?.nodeAdapters
|
|
5496
5524
|
}
|
|
5497
|
-
)
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5525
|
+
)
|
|
5526
|
+
] });
|
|
5527
|
+
}
|
|
5528
|
+
return {
|
|
5529
|
+
type: value?.type,
|
|
5530
|
+
node: memo(NodeComponent),
|
|
5531
|
+
detail: {
|
|
5532
|
+
icon: "mynaui:message-solid",
|
|
5533
|
+
title: "Gmail",
|
|
5534
|
+
description: "Send a text message to the user using different messaging platforms like WhatsApp, Messenger, etc."
|
|
5535
|
+
},
|
|
5536
|
+
connection: {
|
|
5537
|
+
inputs: 1,
|
|
5538
|
+
outputs: 1
|
|
5539
|
+
},
|
|
5540
|
+
defaultData: {
|
|
5541
|
+
operation_name: "",
|
|
5542
|
+
operation_type: "",
|
|
5543
|
+
version: value.defaultData?.version,
|
|
5544
|
+
nodeData: value.defaultData?.nodeData,
|
|
5545
|
+
args: value.defaultData?.args
|
|
5546
|
+
},
|
|
5547
|
+
propertyPanel: NodePropertyPanel
|
|
5548
|
+
};
|
|
5549
|
+
};
|
|
5550
|
+
|
|
5551
|
+
// src/builder/components/flow-builder/components/blocks/index.ts
|
|
5552
|
+
var NODE_ENDPOINT_CONFIGS = NodeEndpointConfigsDocument;
|
|
5553
|
+
var NODE_MANIFEST = NodeManifestDocument;
|
|
5554
|
+
var NODE_CONFIGS = [];
|
|
5555
|
+
async function fetchNodeManifest(nodeType, nodeVersion) {
|
|
5556
|
+
try {
|
|
5557
|
+
const client = getFluidGridsClient();
|
|
5558
|
+
const { data } = await client.query({
|
|
5559
|
+
query: NODE_MANIFEST,
|
|
5560
|
+
variables: { nodeType, version: nodeVersion }
|
|
5561
|
+
});
|
|
5562
|
+
return data?.nodeManifest ?? null;
|
|
5563
|
+
} catch (error) {
|
|
5564
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5565
|
+
console.error(
|
|
5566
|
+
`Error fetching manifest for ${nodeType} v${nodeVersion}:`,
|
|
5567
|
+
message
|
|
5568
|
+
);
|
|
5569
|
+
return null;
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
function pushStoreNodeConfig(manifest, publishType = "STORE") {
|
|
5573
|
+
if (NODE_CONFIGS.some((c) => c.type === manifest.nodeType)) return;
|
|
5574
|
+
const manifestOps = manifest.operations;
|
|
5575
|
+
const operations = Array.isArray(manifestOps) ? manifestOps : manifestOps?.operations ?? manifestOps;
|
|
5576
|
+
NODE_CONFIGS.push({
|
|
5577
|
+
type: manifest.nodeType,
|
|
5578
|
+
detail: {
|
|
5579
|
+
icon: manifest.icon || "mynaui:keyboard-solid",
|
|
5580
|
+
title: manifest.name,
|
|
5581
|
+
description: manifest.description,
|
|
5582
|
+
gradientColor: "yellow"
|
|
5583
|
+
},
|
|
5584
|
+
defaultData: {
|
|
5585
|
+
operation_name: "",
|
|
5586
|
+
operation_type: "",
|
|
5587
|
+
nodeData: manifest,
|
|
5588
|
+
version: manifest.version,
|
|
5589
|
+
args: {
|
|
5590
|
+
context: {
|
|
5591
|
+
node: {
|
|
5592
|
+
id: manifest.nodeType,
|
|
5593
|
+
kind: manifest.kind,
|
|
5594
|
+
type: manifest.nodeType
|
|
5528
5595
|
}
|
|
5529
|
-
}
|
|
5596
|
+
},
|
|
5597
|
+
state: {},
|
|
5598
|
+
credentials: []
|
|
5530
5599
|
}
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
s
|
|
5559
|
-
])
|
|
5560
|
-
);
|
|
5561
|
-
useEffect(() => {
|
|
5562
|
-
if (nodeType !== "integration/channel") return;
|
|
5563
|
-
const nodeData = data;
|
|
5564
|
-
const explicitRole = nodeData?.args?.state?.role;
|
|
5565
|
-
const hasIncomingEdge = edges.some((e) => e.target === id);
|
|
5566
|
-
const isAction = explicitRole === "action" || !explicitRole && hasIncomingEdge;
|
|
5567
|
-
const expectedOp = resolveOperationName(
|
|
5568
|
-
nodeData?.operation_name,
|
|
5569
|
-
isAction ? "sendChannelMessage" : "onChannelMessageReceived"
|
|
5600
|
+
},
|
|
5601
|
+
operations,
|
|
5602
|
+
publishType
|
|
5603
|
+
});
|
|
5604
|
+
}
|
|
5605
|
+
async function loadInstalledStoreNodes() {
|
|
5606
|
+
const globalClient = getGlobalStoreClient();
|
|
5607
|
+
if (!globalClient) return;
|
|
5608
|
+
const { workspaceId } = getFluidGridsConfig();
|
|
5609
|
+
if (!workspaceId) return;
|
|
5610
|
+
try {
|
|
5611
|
+
const { data } = await globalClient.query({
|
|
5612
|
+
query: GetMyNodeInstallationsDocument,
|
|
5613
|
+
variables: { workspaceId, limit: 200, offset: 0 },
|
|
5614
|
+
fetchPolicy: "network-only"
|
|
5615
|
+
});
|
|
5616
|
+
const installations = data?.myStoreAppInstallations?.items ?? [];
|
|
5617
|
+
let loaded = 0;
|
|
5618
|
+
for (const installation of installations) {
|
|
5619
|
+
if (installation.status !== "ACTIVE") continue;
|
|
5620
|
+
const manifest = installation.manifest;
|
|
5621
|
+
if (!manifest?.nodeType) continue;
|
|
5622
|
+
pushStoreNodeConfig(manifest, "STORE");
|
|
5623
|
+
loaded++;
|
|
5624
|
+
}
|
|
5625
|
+
if (loaded > 0) {
|
|
5626
|
+
console.log(
|
|
5627
|
+
`[Workflows] Loaded ${loaded} installed store node(s) from workspace installations.`
|
|
5570
5628
|
);
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5629
|
+
}
|
|
5630
|
+
} catch (error) {
|
|
5631
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5632
|
+
console.warn("[Workflows] Could not load installed store nodes:", message);
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
async function initializeNodeConfigs() {
|
|
5636
|
+
try {
|
|
5637
|
+
const client = await waitForFluidGridsClient(15e3, 150);
|
|
5638
|
+
if (!client) {
|
|
5639
|
+
console.warn(
|
|
5640
|
+
"[Workflows] Apollo client not available after timeout \u2014 loading store nodes only."
|
|
5641
|
+
);
|
|
5642
|
+
await loadInstalledStoreNodes();
|
|
5643
|
+
return;
|
|
5644
|
+
}
|
|
5645
|
+
const { data } = await client.query({
|
|
5646
|
+
query: NODE_ENDPOINT_CONFIGS,
|
|
5647
|
+
variables: {
|
|
5648
|
+
status: "AVAILABLE",
|
|
5649
|
+
pagination: { limit: 100 }
|
|
5650
|
+
}
|
|
5651
|
+
});
|
|
5652
|
+
const nodes = data?.nodeEndpointConfigs?.configs ?? [];
|
|
5653
|
+
if (!Array.isArray(nodes) || nodes.length === 0) {
|
|
5654
|
+
console.warn("No nodes found or received data is not an array.");
|
|
5655
|
+
await loadInstalledStoreNodes();
|
|
5656
|
+
return;
|
|
5657
|
+
}
|
|
5658
|
+
const manifestPromises = nodes.map(async (node) => {
|
|
5659
|
+
if (node?.nodeType && node?.nodeVersion) {
|
|
5660
|
+
try {
|
|
5661
|
+
return await fetchNodeManifest(node.nodeType, node.nodeVersion);
|
|
5662
|
+
} catch {
|
|
5663
|
+
console.error(
|
|
5664
|
+
`Failed to fetch manifest for node ${node.id} (${node.nodeType} v${node.nodeVersion})`
|
|
5665
|
+
);
|
|
5666
|
+
return null;
|
|
5667
|
+
}
|
|
5668
|
+
} else {
|
|
5669
|
+
console.warn(
|
|
5670
|
+
`Node ${node?.id ?? "unknown"} is missing nodeType or nodeVersion. Skipping manifest fetch.`
|
|
5585
5671
|
);
|
|
5586
|
-
|
|
5587
|
-
|
|
5672
|
+
return null;
|
|
5673
|
+
}
|
|
5674
|
+
});
|
|
5675
|
+
const manifests = await Promise.all(manifestPromises);
|
|
5676
|
+
const successfulManifests = manifests.filter(
|
|
5677
|
+
(m) => m !== null
|
|
5678
|
+
);
|
|
5679
|
+
successfulManifests.forEach((manifest, index) => {
|
|
5680
|
+
const node = nodes[index];
|
|
5681
|
+
const manifestOps = manifest.operations;
|
|
5682
|
+
const operations = Array.isArray(manifestOps) ? manifestOps : manifestOps?.operations ?? manifestOps;
|
|
5683
|
+
NODE_CONFIGS.push({
|
|
5684
|
+
type: manifest.nodeType,
|
|
5685
|
+
// Use camelCase nodeType
|
|
5686
|
+
detail: {
|
|
5687
|
+
icon: manifest.icon || "mynaui:keyboard-solid",
|
|
5688
|
+
title: manifest.name,
|
|
5689
|
+
description: manifest.description,
|
|
5690
|
+
gradientColor: "yellow"
|
|
5691
|
+
// Default gradient for dynamic nodes
|
|
5692
|
+
},
|
|
5693
|
+
defaultData: {
|
|
5694
|
+
operation_name: "",
|
|
5695
|
+
operation_type: "",
|
|
5696
|
+
nodeData: manifest,
|
|
5697
|
+
version: manifest.version,
|
|
5698
|
+
args: {
|
|
5699
|
+
context: {
|
|
5700
|
+
node: {
|
|
5701
|
+
id: manifest.nodeType,
|
|
5702
|
+
kind: manifest.kind,
|
|
5703
|
+
type: manifest.nodeType
|
|
5704
|
+
}
|
|
5705
|
+
},
|
|
5706
|
+
state: {},
|
|
5707
|
+
credentials: []
|
|
5708
|
+
}
|
|
5709
|
+
},
|
|
5710
|
+
operations,
|
|
5711
|
+
publishType: node.publishType
|
|
5712
|
+
// Preserve publishType from GraphQL query
|
|
5588
5713
|
});
|
|
5589
|
-
}
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
const
|
|
5593
|
-
|
|
5714
|
+
});
|
|
5715
|
+
await loadInstalledStoreNodes();
|
|
5716
|
+
} catch (error) {
|
|
5717
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5718
|
+
console.error("Failed to fetch nodes or manifests:", message);
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
async function refetchStoreNodes() {
|
|
5722
|
+
try {
|
|
5723
|
+
NODE_CONFIGS.length = 0;
|
|
5724
|
+
NODES.length = 0;
|
|
5725
|
+
AVAILABLE_NODES.length = 0;
|
|
5726
|
+
for (const key of Object.keys(NODE_TYPES)) delete NODE_TYPES[key];
|
|
5727
|
+
for (const key of Object.keys(NODES_METADATA))
|
|
5728
|
+
delete NODES_METADATA[key];
|
|
5729
|
+
isInitialized = false;
|
|
5730
|
+
initPromise = null;
|
|
5731
|
+
const fallback = getNodeMetadata({ type: "__default_dynamic__" });
|
|
5732
|
+
if (fallback?.node) {
|
|
5733
|
+
NODE_TYPES["default"] = fallback.node;
|
|
5734
|
+
}
|
|
5735
|
+
NODE_TYPES[STICKY_NOTE_TYPE] = StickyNoteNode;
|
|
5736
|
+
await initializeNodeConfigs();
|
|
5737
|
+
rebuildNodeArrays();
|
|
5738
|
+
} catch (error) {
|
|
5739
|
+
console.error("[Blocks] Failed to refetch nodes:", error);
|
|
5740
|
+
}
|
|
5741
|
+
}
|
|
5742
|
+
var NODES = [];
|
|
5743
|
+
var NODE_TYPES = {};
|
|
5744
|
+
var NODES_METADATA = {};
|
|
5745
|
+
var AVAILABLE_NODES = [];
|
|
5746
|
+
var initPromise = null;
|
|
5747
|
+
var isInitialized = false;
|
|
5748
|
+
var nodeTypesListeners = /* @__PURE__ */ new Set();
|
|
5749
|
+
function onNodeTypesUpdate(listener) {
|
|
5750
|
+
nodeTypesListeners.add(listener);
|
|
5751
|
+
return () => nodeTypesListeners.delete(listener);
|
|
5752
|
+
}
|
|
5753
|
+
function notifyNodeTypesListeners() {
|
|
5754
|
+
nodeTypesListeners.forEach((listener) => listener());
|
|
5755
|
+
}
|
|
5756
|
+
function ensureNodeConfigsLoaded() {
|
|
5757
|
+
if (isInitialized) {
|
|
5758
|
+
return Promise.resolve();
|
|
5759
|
+
}
|
|
5760
|
+
if (!initPromise) {
|
|
5761
|
+
initPromise = initializeNodeConfigs().then(() => {
|
|
5762
|
+
if (NODE_CONFIGS.length > 0) {
|
|
5763
|
+
isInitialized = true;
|
|
5764
|
+
} else {
|
|
5765
|
+
initPromise = null;
|
|
5766
|
+
}
|
|
5767
|
+
rebuildNodeArrays();
|
|
5768
|
+
});
|
|
5769
|
+
}
|
|
5770
|
+
return initPromise;
|
|
5771
|
+
}
|
|
5772
|
+
function isNodeConfigsInitialized() {
|
|
5773
|
+
return isInitialized;
|
|
5774
|
+
}
|
|
5775
|
+
function rebuildNodeArrays() {
|
|
5776
|
+
NODE_CONFIGS.forEach((config2) => {
|
|
5777
|
+
const data = getNodeMetadata(config2);
|
|
5778
|
+
const nodeMetadata = {
|
|
5779
|
+
...data,
|
|
5780
|
+
detail: { ...config2.detail }
|
|
5594
5781
|
};
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
);
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
}
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5782
|
+
if (!NODES.some((n) => n.type === nodeMetadata.type)) {
|
|
5783
|
+
NODES.push(nodeMetadata);
|
|
5784
|
+
NODE_TYPES[nodeMetadata.type] = nodeMetadata.node;
|
|
5785
|
+
NODES_METADATA[nodeMetadata.type] = {
|
|
5786
|
+
...nodeMetadata,
|
|
5787
|
+
__details: { type: nodeMetadata.type, ...nodeMetadata.detail }
|
|
5788
|
+
};
|
|
5789
|
+
}
|
|
5790
|
+
});
|
|
5791
|
+
const newAvailableNodes = NODES.filter(
|
|
5792
|
+
(node) => node.available === void 0 || node.available
|
|
5793
|
+
).map((node) => {
|
|
5794
|
+
const nodeConfig = NODE_CONFIGS.find((config2) => config2.type === node.type);
|
|
5795
|
+
return {
|
|
5796
|
+
type: node.type,
|
|
5797
|
+
icon: node.detail.icon,
|
|
5798
|
+
title: node.detail.title,
|
|
5799
|
+
description: node.detail.description,
|
|
5800
|
+
gradientColor: node.detail.gradientColor ?? "gray",
|
|
5801
|
+
manifest: node.defaultData?.nodeData,
|
|
5802
|
+
publishType: nodeConfig?.publishType
|
|
5803
|
+
// Include publishType
|
|
5804
|
+
};
|
|
5805
|
+
});
|
|
5806
|
+
AVAILABLE_NODES.length = 0;
|
|
5807
|
+
AVAILABLE_NODES.push(...newAvailableNodes);
|
|
5808
|
+
notifyNodeTypesListeners();
|
|
5809
|
+
}
|
|
5810
|
+
ensureNodeConfigsLoaded().catch((err) => {
|
|
5811
|
+
console.warn(
|
|
5812
|
+
"[Workflows] Background node config initialization failed:",
|
|
5813
|
+
err
|
|
5814
|
+
);
|
|
5815
|
+
});
|
|
5816
|
+
var fallbackDynamic = getNodeMetadata({ type: "__default_dynamic__" });
|
|
5817
|
+
if (fallbackDynamic?.node) {
|
|
5818
|
+
NODE_TYPES["default"] = fallbackDynamic.node;
|
|
5819
|
+
}
|
|
5820
|
+
NODE_TYPES[STICKY_NOTE_TYPE] = StickyNoteNode;
|
|
5821
|
+
function registerWorkflowNodeTypes(workflowNodes) {
|
|
5822
|
+
for (const node of workflowNodes) {
|
|
5823
|
+
if (node.type && !NODE_TYPES[node.type]) {
|
|
5824
|
+
const meta = getNodeMetadata({
|
|
5825
|
+
type: node.type,
|
|
5826
|
+
defaultData: {
|
|
5827
|
+
version: node.data?.version,
|
|
5828
|
+
nodeData: node.data?.nodeData
|
|
5829
|
+
},
|
|
5830
|
+
operations: node.data?.operations
|
|
5831
|
+
});
|
|
5832
|
+
if (meta?.node) {
|
|
5833
|
+
NODE_TYPES[node.type] = meta.node;
|
|
5629
5834
|
}
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5835
|
+
}
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
|
|
5839
|
+
// src/hooks/useNodeExecution.ts
|
|
5840
|
+
var TRIGGER_WORKFLOW = TriggerWorkflowDocument;
|
|
5841
|
+
var EXECUTE_STEP = ExecuteStepDocument;
|
|
5842
|
+
var WORKFLOW_RUN = WorkflowRunDocument;
|
|
5843
|
+
var RUN_FROM_NODE = RunFromNodeDocument;
|
|
5844
|
+
var EXECUTION_DETAIL = ExecutionDetailDocument;
|
|
5845
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
5846
|
+
"COMPLETED",
|
|
5847
|
+
"FAILED",
|
|
5848
|
+
"CANCELLED",
|
|
5849
|
+
"TERMINATED"
|
|
5850
|
+
]);
|
|
5851
|
+
var POLL_INTERVAL = 1500;
|
|
5852
|
+
var MAX_RUN_CREATION_WAIT = 6e4;
|
|
5853
|
+
function calculateItemCount(output) {
|
|
5854
|
+
if (!output || Object.keys(output).length === 0) {
|
|
5855
|
+
return 0;
|
|
5856
|
+
}
|
|
5857
|
+
const values = Object.values(output);
|
|
5858
|
+
for (const value of values) {
|
|
5859
|
+
if (Array.isArray(value)) {
|
|
5860
|
+
return value.length;
|
|
5861
|
+
}
|
|
5862
|
+
if (typeof value === "object" && value !== null) {
|
|
5863
|
+
const nestedValues = Object.values(value);
|
|
5864
|
+
for (const nestedValue of nestedValues) {
|
|
5865
|
+
if (Array.isArray(nestedValue)) {
|
|
5866
|
+
return nestedValue.length;
|
|
5867
|
+
}
|
|
5644
5868
|
}
|
|
5645
|
-
openNdv({ nodeId: id, type: value?.type });
|
|
5646
|
-
}, [id, openNdv]);
|
|
5647
|
-
const handleExecuteStep = useCallback(() => {
|
|
5648
|
-
executeStep(id).then((_result) => {
|
|
5649
|
-
}).catch((error) => {
|
|
5650
|
-
console.error("[NodeComponent] Execute step error:", error);
|
|
5651
|
-
});
|
|
5652
|
-
}, [executeStep, id]);
|
|
5653
|
-
const missingCredentials = useMemo(() => {
|
|
5654
|
-
const visibleAdapters = getVisibleConnectionAdapters(
|
|
5655
|
-
String(value?.type ?? ""),
|
|
5656
|
-
data?.operation_name,
|
|
5657
|
-
value?.operations ?? data?.nodeData?.operations,
|
|
5658
|
-
data?.nodeData
|
|
5659
|
-
);
|
|
5660
|
-
if (visibleAdapters.length === 0) return false;
|
|
5661
|
-
const selected2 = data?.args?.connections ?? data?.args?.credentials ?? [];
|
|
5662
|
-
return visibleAdapters.some(
|
|
5663
|
-
(adapter) => !selected2.some((sel) => sel.adapter === adapter.name)
|
|
5664
|
-
);
|
|
5665
|
-
}, [data]);
|
|
5666
|
-
if (isAdapterSubNode) {
|
|
5667
|
-
return /* @__PURE__ */ jsx(SubNodeComponent, { ...props, nodeType: value?.type });
|
|
5668
5869
|
}
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
)
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
const
|
|
5707
|
-
|
|
5870
|
+
}
|
|
5871
|
+
return 1;
|
|
5872
|
+
}
|
|
5873
|
+
function useNodeExecution() {
|
|
5874
|
+
const pollTimerRef = useRef(null);
|
|
5875
|
+
const pollStartRef = useRef(0);
|
|
5876
|
+
const activeRunIdRef = useRef(null);
|
|
5877
|
+
const loadRequestRef = useRef(0);
|
|
5878
|
+
const workflow = useFlowStore((s) => s.workflow);
|
|
5879
|
+
const startExecution = useFlowStore(
|
|
5880
|
+
(s) => s.actions.execution.startExecution
|
|
5881
|
+
);
|
|
5882
|
+
const setRunMetadata = useFlowStore(
|
|
5883
|
+
(s) => s.actions.execution.setRunMetadata
|
|
5884
|
+
);
|
|
5885
|
+
const setStepPlan = useFlowStore((s) => s.actions.execution.setStepPlan);
|
|
5886
|
+
const addLog = useFlowStore((s) => s.actions.execution.addLog);
|
|
5887
|
+
const updateLog = useFlowStore((s) => s.actions.execution.updateLog);
|
|
5888
|
+
const setNodeStatus = useFlowStore((s) => s.actions.execution.setNodeStatus);
|
|
5889
|
+
const setNodeItemCount = useFlowStore(
|
|
5890
|
+
(s) => s.actions.execution.setNodeItemCount
|
|
5891
|
+
);
|
|
5892
|
+
const completeExecution = useFlowStore(
|
|
5893
|
+
(s) => s.actions.execution.completeExecution
|
|
5894
|
+
);
|
|
5895
|
+
const clearLogs = useFlowStore((s) => s.actions.execution.clearLogs);
|
|
5896
|
+
const loadDebugRun = useFlowStore((s) => s.actions.execution.loadDebugRun);
|
|
5897
|
+
const cleanup = useCallback(() => {
|
|
5898
|
+
loadRequestRef.current += 1;
|
|
5899
|
+
if (pollTimerRef.current) {
|
|
5900
|
+
clearTimeout(pollTimerRef.current);
|
|
5901
|
+
pollTimerRef.current = null;
|
|
5902
|
+
}
|
|
5903
|
+
activeRunIdRef.current = null;
|
|
5904
|
+
}, []);
|
|
5905
|
+
const findTriggerNode = useCallback(() => {
|
|
5906
|
+
const { nodes, edges } = workflow;
|
|
5907
|
+
const explicitTrigger = nodes.find(isNodeTrigger);
|
|
5908
|
+
if (explicitTrigger) {
|
|
5909
|
+
return explicitTrigger;
|
|
5910
|
+
}
|
|
5911
|
+
const nodesWithIncomingEdges = new Set(edges.map((e) => e.target));
|
|
5912
|
+
const firstNode = nodes.find(
|
|
5913
|
+
(node) => !nodesWithIncomingEdges.has(node.id)
|
|
5708
5914
|
);
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
onToggleDisabled: handleToggleDisabled,
|
|
5741
|
-
isRenaming: isRenamingThisNode,
|
|
5742
|
-
onStartRename: handleStartRename,
|
|
5743
|
-
onCommitRename: handleCommitRename,
|
|
5744
|
-
onCancelRename: handleCancelRename
|
|
5915
|
+
if (firstNode) {
|
|
5916
|
+
return firstNode;
|
|
5917
|
+
}
|
|
5918
|
+
if (nodes.length > 0) {
|
|
5919
|
+
return nodes[0];
|
|
5920
|
+
}
|
|
5921
|
+
console.error("[useNodeExecution] No nodes found in workflow");
|
|
5922
|
+
return null;
|
|
5923
|
+
}, [workflow]);
|
|
5924
|
+
const getNodeName = useCallback(
|
|
5925
|
+
(nodeId) => {
|
|
5926
|
+
const node = workflow.nodes.find((n) => n.id === nodeId);
|
|
5927
|
+
if (!node) return `Node ${nodeId}`;
|
|
5928
|
+
const data = node.data;
|
|
5929
|
+
return data?.operation_name || data?.name || node.type || `Node ${nodeId}`;
|
|
5930
|
+
},
|
|
5931
|
+
[workflow.nodes]
|
|
5932
|
+
);
|
|
5933
|
+
const processNodeStates = useCallback(
|
|
5934
|
+
(runId, nodeStates) => {
|
|
5935
|
+
for (const nodeState of nodeStates) {
|
|
5936
|
+
const logId = `${runId}-${nodeState.nodeId}`;
|
|
5937
|
+
const rawStatus = nodeState.status.toLowerCase();
|
|
5938
|
+
const status = rawStatus === "completed" ? "completed" : rawStatus === "failed" || rawStatus === "timeout" ? "failed" : rawStatus === "running" || rawStatus === "waiting" || rawStatus === "delayed" || rawStatus === "waiting_subworkflow" ? "running" : "pending";
|
|
5939
|
+
if (status === "completed") {
|
|
5940
|
+
setNodeStatus(nodeState.nodeId, "success");
|
|
5941
|
+
if (nodeState.output) {
|
|
5942
|
+
const itemCount = calculateItemCount(
|
|
5943
|
+
nodeState.output
|
|
5944
|
+
);
|
|
5945
|
+
setNodeItemCount(nodeState.nodeId, itemCount);
|
|
5745
5946
|
}
|
|
5746
|
-
)
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5947
|
+
} else if (status === "failed") {
|
|
5948
|
+
setNodeStatus(nodeState.nodeId, "error");
|
|
5949
|
+
} else if (status === "running") {
|
|
5950
|
+
setNodeStatus(nodeState.nodeId, "running");
|
|
5951
|
+
}
|
|
5952
|
+
const existingLog = useFlowStore.getState().execution.logs.find((l) => l.id === logId);
|
|
5953
|
+
if (!existingLog) {
|
|
5954
|
+
if (status === "pending") continue;
|
|
5955
|
+
const newLog = {
|
|
5956
|
+
id: logId,
|
|
5957
|
+
runId,
|
|
5958
|
+
nodeId: nodeState.nodeId,
|
|
5959
|
+
nodeType: nodeState.nodeType,
|
|
5960
|
+
nodeName: getNodeName(nodeState.nodeId),
|
|
5961
|
+
status,
|
|
5962
|
+
inputArgs: nodeState.inputArgs ?? null,
|
|
5963
|
+
output: nodeState.output ?? null,
|
|
5964
|
+
error: nodeState.error ?? null,
|
|
5965
|
+
duration: nodeState.duration ?? null,
|
|
5966
|
+
startTime: nodeState.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5967
|
+
endTime: nodeState.endTime ?? null,
|
|
5968
|
+
usedPinnedData: nodeState.usedPinnedData ?? false,
|
|
5969
|
+
reusedFromRunId: nodeState.reusedFromRunId ?? null
|
|
5970
|
+
};
|
|
5971
|
+
addLog(newLog);
|
|
5972
|
+
} else {
|
|
5973
|
+
if (existingLog.status !== status || status === "completed" && !existingLog.output) {
|
|
5974
|
+
updateLog(logId, {
|
|
5975
|
+
status,
|
|
5976
|
+
inputArgs: nodeState.inputArgs ?? existingLog.inputArgs,
|
|
5977
|
+
output: nodeState.output ?? existingLog.output,
|
|
5978
|
+
error: nodeState.error ?? existingLog.error,
|
|
5979
|
+
duration: nodeState.duration ?? existingLog.duration,
|
|
5980
|
+
endTime: nodeState.endTime ?? existingLog.endTime,
|
|
5981
|
+
usedPinnedData: nodeState.usedPinnedData ?? existingLog.usedPinnedData,
|
|
5982
|
+
reusedFromRunId: nodeState.reusedFromRunId ?? existingLog.reusedFromRunId
|
|
5983
|
+
});
|
|
5752
5984
|
}
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
}
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5985
|
+
}
|
|
5986
|
+
}
|
|
5987
|
+
},
|
|
5988
|
+
[setNodeStatus, setNodeItemCount, addLog, updateLog, getNodeName]
|
|
5989
|
+
);
|
|
5990
|
+
const pollRunUpdates = useCallback(
|
|
5991
|
+
(runId) => {
|
|
5992
|
+
const client = getFluidGridsClient();
|
|
5993
|
+
pollStartRef.current = Date.now();
|
|
5994
|
+
activeRunIdRef.current = runId;
|
|
5995
|
+
const doPoll = async () => {
|
|
5996
|
+
if (activeRunIdRef.current !== runId) {
|
|
5997
|
+
return;
|
|
5998
|
+
}
|
|
5999
|
+
try {
|
|
6000
|
+
const result = await client.query({
|
|
6001
|
+
query: WORKFLOW_RUN,
|
|
6002
|
+
variables: { id: runId },
|
|
6003
|
+
fetchPolicy: "network-only"
|
|
6004
|
+
});
|
|
6005
|
+
if (activeRunIdRef.current !== runId) {
|
|
6006
|
+
return;
|
|
6007
|
+
}
|
|
6008
|
+
const run = result.data?.workflowRun;
|
|
6009
|
+
if (!run) {
|
|
6010
|
+
console.warn(
|
|
6011
|
+
"[useNodeExecution] Poll returned no run data for",
|
|
6012
|
+
runId
|
|
6013
|
+
);
|
|
6014
|
+
if (Date.now() - pollStartRef.current < MAX_RUN_CREATION_WAIT) {
|
|
6015
|
+
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL);
|
|
6016
|
+
} else {
|
|
6017
|
+
console.warn(
|
|
6018
|
+
"[useNodeExecution] Run data not found after timeout for",
|
|
6019
|
+
runId
|
|
6020
|
+
);
|
|
6021
|
+
completeExecution();
|
|
6022
|
+
cleanup();
|
|
6023
|
+
}
|
|
6024
|
+
return;
|
|
6025
|
+
}
|
|
6026
|
+
setRunMetadata({
|
|
6027
|
+
runId,
|
|
6028
|
+
workflowKey: run.workflowKey,
|
|
6029
|
+
version: run.version,
|
|
6030
|
+
sourceMode: run.mode
|
|
6031
|
+
});
|
|
6032
|
+
if (run.nodeStates?.length) {
|
|
6033
|
+
processNodeStates(runId, run.nodeStates);
|
|
6034
|
+
}
|
|
6035
|
+
if (TERMINAL_STATUSES.has(run.status)) {
|
|
6036
|
+
completeExecution();
|
|
6037
|
+
cleanup();
|
|
6038
|
+
return;
|
|
6039
|
+
}
|
|
6040
|
+
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL);
|
|
6041
|
+
} catch (error) {
|
|
6042
|
+
console.error("[useNodeExecution] Poll error:", error);
|
|
6043
|
+
if (activeRunIdRef.current !== runId) {
|
|
6044
|
+
return;
|
|
6045
|
+
}
|
|
6046
|
+
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL * 2);
|
|
6047
|
+
}
|
|
6048
|
+
};
|
|
6049
|
+
pollTimerRef.current = setTimeout(doPoll, 800);
|
|
6050
|
+
},
|
|
6051
|
+
[processNodeStates, completeExecution, cleanup, setRunMetadata]
|
|
6052
|
+
);
|
|
6053
|
+
const executeStep = useCallback(
|
|
6054
|
+
async (nodeId) => {
|
|
6055
|
+
cleanup();
|
|
6056
|
+
clearLogs();
|
|
6057
|
+
const workflowKey = workflow.workflowKey || workflow.id;
|
|
6058
|
+
const version = workflow.version || "1.0.0";
|
|
6059
|
+
if (!workflowKey) {
|
|
6060
|
+
console.error(
|
|
6061
|
+
"[useNodeExecution] Workflow must be saved before step execution. No workflowKey found."
|
|
6062
|
+
);
|
|
6063
|
+
return {
|
|
6064
|
+
success: false,
|
|
6065
|
+
error: "Workflow must be saved before execution"
|
|
6066
|
+
};
|
|
6067
|
+
}
|
|
6068
|
+
setNodeStatus(nodeId, "running");
|
|
6069
|
+
try {
|
|
6070
|
+
normalizeWorkflowParameters(
|
|
6071
|
+
workflow.nodes.filter((node) => node.id === nodeId),
|
|
6072
|
+
workflow.edges,
|
|
6073
|
+
(type) => NODE_CONFIGS.find((config2) => config2.type === type)?.operations
|
|
6074
|
+
);
|
|
6075
|
+
const client = getFluidGridsClient();
|
|
6076
|
+
const result = throwIfResultError(
|
|
6077
|
+
await client.mutate({
|
|
6078
|
+
mutation: EXECUTE_STEP,
|
|
6079
|
+
variables: {
|
|
6080
|
+
input: {
|
|
6081
|
+
workflowKey,
|
|
6082
|
+
version,
|
|
6083
|
+
nodeId
|
|
5814
6084
|
}
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
6085
|
+
}
|
|
6086
|
+
})
|
|
6087
|
+
);
|
|
6088
|
+
const stepResult = result.data?.executeStep;
|
|
6089
|
+
if (stepResult) {
|
|
6090
|
+
const backendRunId = stepResult.runId;
|
|
6091
|
+
startExecution(nodeId, backendRunId, {
|
|
6092
|
+
workflowKey: stepResult.workflowKey,
|
|
6093
|
+
version: stepResult.version,
|
|
6094
|
+
sourceMode: "TEST"
|
|
6095
|
+
});
|
|
6096
|
+
setStepPlan({
|
|
6097
|
+
runId: backendRunId,
|
|
6098
|
+
...stepResult.plan,
|
|
6099
|
+
sourceRunId: stepResult.plan.sourceRunId ?? null
|
|
6100
|
+
});
|
|
6101
|
+
const initialLog = {
|
|
6102
|
+
id: `${backendRunId}-${nodeId}`,
|
|
6103
|
+
runId: backendRunId,
|
|
6104
|
+
nodeId,
|
|
6105
|
+
nodeType: workflow.nodes.find((node) => node.id === nodeId)?.type || "unknown",
|
|
6106
|
+
nodeName: getNodeName(nodeId),
|
|
6107
|
+
status: "running",
|
|
6108
|
+
inputArgs: null,
|
|
6109
|
+
output: null,
|
|
6110
|
+
error: null,
|
|
6111
|
+
duration: null,
|
|
6112
|
+
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6113
|
+
endTime: null
|
|
6114
|
+
};
|
|
6115
|
+
addLog(initialLog);
|
|
6116
|
+
pollRunUpdates(backendRunId);
|
|
6117
|
+
return { success: true, runId: backendRunId, plan: stepResult.plan };
|
|
5824
6118
|
}
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
};
|
|
5850
|
-
|
|
5851
|
-
// src/builder/components/flow-builder/components/blocks/index.ts
|
|
5852
|
-
var NODE_ENDPOINT_CONFIGS = NodeEndpointConfigsDocument;
|
|
5853
|
-
var NODE_MANIFEST = NodeManifestDocument;
|
|
5854
|
-
var NODE_CONFIGS = [];
|
|
5855
|
-
async function fetchNodeManifest(nodeType, nodeVersion) {
|
|
5856
|
-
try {
|
|
5857
|
-
const client = getFluidGridsClient();
|
|
5858
|
-
const { data } = await client.query({
|
|
5859
|
-
query: NODE_MANIFEST,
|
|
5860
|
-
variables: { nodeType, version: nodeVersion }
|
|
5861
|
-
});
|
|
5862
|
-
return data?.nodeManifest ?? null;
|
|
5863
|
-
} catch (error) {
|
|
5864
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5865
|
-
console.error(
|
|
5866
|
-
`Error fetching manifest for ${nodeType} v${nodeVersion}:`,
|
|
5867
|
-
message
|
|
5868
|
-
);
|
|
5869
|
-
return null;
|
|
5870
|
-
}
|
|
5871
|
-
}
|
|
5872
|
-
function pushStoreNodeConfig(manifest, publishType = "STORE") {
|
|
5873
|
-
if (NODE_CONFIGS.some((c) => c.type === manifest.nodeType)) return;
|
|
5874
|
-
const manifestOps = manifest.operations;
|
|
5875
|
-
const operations = Array.isArray(manifestOps) ? manifestOps : manifestOps?.operations ?? manifestOps;
|
|
5876
|
-
NODE_CONFIGS.push({
|
|
5877
|
-
type: manifest.nodeType,
|
|
5878
|
-
detail: {
|
|
5879
|
-
icon: manifest.icon || "mynaui:keyboard-solid",
|
|
5880
|
-
title: manifest.name,
|
|
5881
|
-
description: manifest.description,
|
|
5882
|
-
gradientColor: "yellow"
|
|
5883
|
-
},
|
|
5884
|
-
defaultData: {
|
|
5885
|
-
operation_name: "",
|
|
5886
|
-
operation_type: "",
|
|
5887
|
-
nodeData: manifest,
|
|
5888
|
-
version: manifest.version,
|
|
5889
|
-
args: {
|
|
5890
|
-
context: {
|
|
5891
|
-
node: {
|
|
5892
|
-
id: manifest.nodeType,
|
|
5893
|
-
kind: manifest.kind,
|
|
5894
|
-
type: manifest.nodeType
|
|
5895
|
-
}
|
|
5896
|
-
},
|
|
5897
|
-
state: {},
|
|
5898
|
-
credentials: []
|
|
6119
|
+
console.error("[useNodeExecution] No result from executeStep");
|
|
6120
|
+
setNodeStatus(nodeId, "error");
|
|
6121
|
+
return { success: false, error: "No result from executeStep" };
|
|
6122
|
+
} catch (error) {
|
|
6123
|
+
console.error("[useNodeExecution] Error executing step:", error);
|
|
6124
|
+
const errorMessage = describeOperationError(error);
|
|
6125
|
+
const errorRunId = nanoid$1();
|
|
6126
|
+
const errorLog = {
|
|
6127
|
+
id: `${errorRunId}-${nodeId}`,
|
|
6128
|
+
runId: errorRunId,
|
|
6129
|
+
nodeId,
|
|
6130
|
+
nodeType: workflow.nodes.find((node) => node.id === nodeId)?.type || "unknown",
|
|
6131
|
+
nodeName: getNodeName(nodeId),
|
|
6132
|
+
status: "failed",
|
|
6133
|
+
inputArgs: null,
|
|
6134
|
+
output: null,
|
|
6135
|
+
error: errorMessage,
|
|
6136
|
+
duration: null,
|
|
6137
|
+
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6138
|
+
endTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
6139
|
+
};
|
|
6140
|
+
addLog(errorLog);
|
|
6141
|
+
setNodeStatus(nodeId, "error");
|
|
6142
|
+
completeExecution();
|
|
6143
|
+
return { success: false, error: errorMessage };
|
|
5899
6144
|
}
|
|
5900
6145
|
},
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
const manifest = installation.manifest;
|
|
5921
|
-
if (!manifest?.nodeType) continue;
|
|
5922
|
-
pushStoreNodeConfig(manifest, "STORE");
|
|
5923
|
-
loaded++;
|
|
5924
|
-
}
|
|
5925
|
-
if (loaded > 0) {
|
|
5926
|
-
console.log(
|
|
5927
|
-
`[Workflows] Loaded ${loaded} installed store node(s) from workspace installations.`
|
|
5928
|
-
);
|
|
5929
|
-
}
|
|
5930
|
-
} catch (error) {
|
|
5931
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5932
|
-
console.warn("[Workflows] Could not load installed store nodes:", message);
|
|
5933
|
-
}
|
|
5934
|
-
}
|
|
5935
|
-
async function initializeNodeConfigs() {
|
|
5936
|
-
try {
|
|
5937
|
-
const client = await waitForFluidGridsClient(15e3, 150);
|
|
5938
|
-
if (!client) {
|
|
5939
|
-
console.warn(
|
|
5940
|
-
"[Workflows] Apollo client not available after timeout \u2014 loading store nodes only."
|
|
5941
|
-
);
|
|
5942
|
-
await loadInstalledStoreNodes();
|
|
5943
|
-
return;
|
|
5944
|
-
}
|
|
5945
|
-
const { data } = await client.query({
|
|
5946
|
-
query: NODE_ENDPOINT_CONFIGS,
|
|
5947
|
-
variables: {
|
|
5948
|
-
status: "AVAILABLE",
|
|
5949
|
-
pagination: { limit: 100 }
|
|
6146
|
+
[
|
|
6147
|
+
cleanup,
|
|
6148
|
+
clearLogs,
|
|
6149
|
+
startExecution,
|
|
6150
|
+
setStepPlan,
|
|
6151
|
+
workflow,
|
|
6152
|
+
getNodeName,
|
|
6153
|
+
addLog,
|
|
6154
|
+
setNodeStatus,
|
|
6155
|
+
pollRunUpdates,
|
|
6156
|
+
completeExecution
|
|
6157
|
+
]
|
|
6158
|
+
);
|
|
6159
|
+
const executeFullWorkflow = useCallback(
|
|
6160
|
+
async (customTriggerData) => {
|
|
6161
|
+
const triggerNode = findTriggerNode();
|
|
6162
|
+
if (!triggerNode) {
|
|
6163
|
+
console.error("[useNodeExecution] No trigger node found in workflow");
|
|
6164
|
+
return { success: false, error: "No trigger node found" };
|
|
5950
6165
|
}
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
}
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
`Node ${node?.id ?? "unknown"} is missing nodeType or nodeVersion. Skipping manifest fetch.`
|
|
6166
|
+
cleanup();
|
|
6167
|
+
clearLogs();
|
|
6168
|
+
const workflowKey = workflow.workflowKey || workflow.id;
|
|
6169
|
+
const version = workflow.version || "1.0.0";
|
|
6170
|
+
if (!workflowKey) {
|
|
6171
|
+
console.error(
|
|
6172
|
+
"[useNodeExecution] Workflow must be saved before execution. No workflowKey found."
|
|
6173
|
+
);
|
|
6174
|
+
return {
|
|
6175
|
+
success: false,
|
|
6176
|
+
error: "Workflow must be saved before execution"
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
6179
|
+
setNodeStatus(triggerNode.id, "running");
|
|
6180
|
+
try {
|
|
6181
|
+
normalizeWorkflowParameters(
|
|
6182
|
+
workflow.nodes.filter((node) => node.data.disabled !== true),
|
|
6183
|
+
workflow.edges,
|
|
6184
|
+
(type) => NODE_CONFIGS.find((config2) => config2.type === type)?.operations
|
|
5971
6185
|
);
|
|
5972
|
-
|
|
6186
|
+
const client = getFluidGridsClient();
|
|
6187
|
+
const result = throwIfResultError(
|
|
6188
|
+
await client.mutate({
|
|
6189
|
+
mutation: TRIGGER_WORKFLOW,
|
|
6190
|
+
variables: {
|
|
6191
|
+
input: {
|
|
6192
|
+
workflowKey,
|
|
6193
|
+
version,
|
|
6194
|
+
triggerNodeId: triggerNode.id,
|
|
6195
|
+
triggerData: customTriggerData || {},
|
|
6196
|
+
context: {},
|
|
6197
|
+
// Builder "Test workflow" is always a manual/debug run.
|
|
6198
|
+
// Pinned node data only applies to TEST executions, never a
|
|
6199
|
+
// production trigger (webhook, schedule, API).
|
|
6200
|
+
mode: "TEST"
|
|
6201
|
+
}
|
|
6202
|
+
}
|
|
6203
|
+
})
|
|
6204
|
+
);
|
|
6205
|
+
const triggerResult = result.data?.triggerWorkflow;
|
|
6206
|
+
if (triggerResult) {
|
|
6207
|
+
const backendRunId = triggerResult.runId;
|
|
6208
|
+
startExecution(triggerNode.id, backendRunId, {
|
|
6209
|
+
workflowKey: triggerResult.workflowKey,
|
|
6210
|
+
version: triggerResult.version,
|
|
6211
|
+
sourceMode: "TEST"
|
|
6212
|
+
});
|
|
6213
|
+
pollRunUpdates(backendRunId);
|
|
6214
|
+
return { success: true, runId: backendRunId };
|
|
6215
|
+
}
|
|
6216
|
+
console.error("[useNodeExecution] No result from trigger");
|
|
6217
|
+
setNodeStatus(triggerNode.id, "error");
|
|
6218
|
+
return { success: false, error: "No result from trigger" };
|
|
6219
|
+
} catch (error) {
|
|
6220
|
+
console.error("[useNodeExecution] Error triggering workflow:", error);
|
|
6221
|
+
const errorMessage = describeOperationError(error);
|
|
6222
|
+
const errorRunId = nanoid$1();
|
|
6223
|
+
const errorLog = {
|
|
6224
|
+
id: `${errorRunId}-${triggerNode.id}`,
|
|
6225
|
+
runId: errorRunId,
|
|
6226
|
+
nodeId: triggerNode.id,
|
|
6227
|
+
nodeType: triggerNode.type || "unknown",
|
|
6228
|
+
nodeName: getNodeName(triggerNode.id),
|
|
6229
|
+
status: "failed",
|
|
6230
|
+
inputArgs: null,
|
|
6231
|
+
output: null,
|
|
6232
|
+
error: errorMessage,
|
|
6233
|
+
duration: null,
|
|
6234
|
+
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6235
|
+
endTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
6236
|
+
};
|
|
6237
|
+
addLog(errorLog);
|
|
6238
|
+
setNodeStatus(triggerNode.id, "error");
|
|
6239
|
+
completeExecution();
|
|
6240
|
+
return { success: false, error: errorMessage };
|
|
5973
6241
|
}
|
|
5974
|
-
}
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6242
|
+
},
|
|
6243
|
+
[
|
|
6244
|
+
findTriggerNode,
|
|
6245
|
+
cleanup,
|
|
6246
|
+
clearLogs,
|
|
6247
|
+
startExecution,
|
|
6248
|
+
workflow,
|
|
6249
|
+
getNodeName,
|
|
6250
|
+
addLog,
|
|
6251
|
+
setNodeStatus,
|
|
6252
|
+
pollRunUpdates,
|
|
6253
|
+
completeExecution
|
|
6254
|
+
]
|
|
6255
|
+
);
|
|
6256
|
+
const executeFromNode = useCallback(
|
|
6257
|
+
async (sourceRunId, nodeId) => {
|
|
6258
|
+
cleanup();
|
|
6259
|
+
clearLogs();
|
|
6260
|
+
setNodeStatus(nodeId, "running");
|
|
6261
|
+
try {
|
|
6262
|
+
const client = getFluidGridsClient();
|
|
6263
|
+
const result = throwIfResultError(
|
|
6264
|
+
await client.mutate({
|
|
6265
|
+
mutation: RUN_FROM_NODE,
|
|
6266
|
+
variables: {
|
|
6267
|
+
input: {
|
|
6268
|
+
sourceRunId,
|
|
6269
|
+
nodeId,
|
|
6270
|
+
// Run-from-node is a debug affordance — always TEST, same
|
|
6271
|
+
// rule as the other builder debug execution paths.
|
|
6272
|
+
mode: "TEST"
|
|
6004
6273
|
}
|
|
6005
|
-
}
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
}
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6274
|
+
}
|
|
6275
|
+
})
|
|
6276
|
+
);
|
|
6277
|
+
const runFromNodeResult = result.data?.runFromNode;
|
|
6278
|
+
if (runFromNodeResult) {
|
|
6279
|
+
const backendRunId = runFromNodeResult.runId;
|
|
6280
|
+
startExecution(nodeId, backendRunId, {
|
|
6281
|
+
workflowKey: runFromNodeResult.workflowKey,
|
|
6282
|
+
version: runFromNodeResult.version,
|
|
6283
|
+
sourceMode: "TEST"
|
|
6284
|
+
});
|
|
6285
|
+
pollRunUpdates(backendRunId);
|
|
6286
|
+
return { success: true, runId: backendRunId };
|
|
6287
|
+
}
|
|
6288
|
+
console.error("[useNodeExecution] No result from runFromNode");
|
|
6289
|
+
setNodeStatus(nodeId, "error");
|
|
6290
|
+
return { success: false, error: "No result from runFromNode" };
|
|
6291
|
+
} catch (error) {
|
|
6292
|
+
console.error("[useNodeExecution] Error running from node:", error);
|
|
6293
|
+
const errorMessage = describeOperationError(error);
|
|
6294
|
+
setNodeStatus(nodeId, "error");
|
|
6295
|
+
completeExecution();
|
|
6296
|
+
return { success: false, error: errorMessage };
|
|
6297
|
+
}
|
|
6298
|
+
},
|
|
6299
|
+
[
|
|
6300
|
+
cleanup,
|
|
6301
|
+
clearLogs,
|
|
6302
|
+
setNodeStatus,
|
|
6303
|
+
startExecution,
|
|
6304
|
+
pollRunUpdates,
|
|
6305
|
+
completeExecution
|
|
6306
|
+
]
|
|
6307
|
+
);
|
|
6308
|
+
const loadExecution = useCallback(
|
|
6309
|
+
async (runId) => {
|
|
6310
|
+
cleanup();
|
|
6311
|
+
const requestId = ++loadRequestRef.current;
|
|
6312
|
+
try {
|
|
6313
|
+
const client = getFluidGridsClient();
|
|
6314
|
+
const result = throwIfResultError(
|
|
6315
|
+
await client.query({
|
|
6316
|
+
query: EXECUTION_DETAIL,
|
|
6317
|
+
variables: { id: runId },
|
|
6318
|
+
fetchPolicy: "network-only"
|
|
6319
|
+
})
|
|
6320
|
+
);
|
|
6321
|
+
const run = result.data?.workflowRun;
|
|
6322
|
+
if (!run) {
|
|
6323
|
+
return { success: false, error: "Execution not found" };
|
|
6324
|
+
}
|
|
6325
|
+
if (run.mode !== "TEST") {
|
|
6326
|
+
return {
|
|
6327
|
+
success: false,
|
|
6328
|
+
error: "Only TEST runs can be loaded for continuation."
|
|
6329
|
+
};
|
|
6330
|
+
}
|
|
6331
|
+
if (!TERMINAL_STATUSES.has(run.status)) {
|
|
6332
|
+
return {
|
|
6333
|
+
success: false,
|
|
6334
|
+
error: "Only terminal TEST runs can be loaded for continuation."
|
|
6335
|
+
};
|
|
6336
|
+
}
|
|
6337
|
+
if (requestId !== loadRequestRef.current) {
|
|
6338
|
+
return { success: false, error: "Execution load superseded." };
|
|
6339
|
+
}
|
|
6340
|
+
const currentWorkflow = useFlowStore.getState().workflow;
|
|
6341
|
+
const currentWorkflowKey = currentWorkflow.workflowKey || currentWorkflow.id;
|
|
6342
|
+
if (run.workflowKey !== currentWorkflowKey || run.version !== currentWorkflow.version) {
|
|
6343
|
+
return {
|
|
6344
|
+
success: false,
|
|
6345
|
+
error: "The selected run belongs to a different workflow version."
|
|
6346
|
+
};
|
|
6347
|
+
}
|
|
6348
|
+
const logs = (run.nodeStates ?? []).map((nodeState) => {
|
|
6349
|
+
const rawStatus = nodeState.status.toLowerCase();
|
|
6350
|
+
const status = rawStatus === "completed" ? "completed" : rawStatus === "failed" || rawStatus === "timeout" ? "failed" : rawStatus === "running" || rawStatus === "waiting" || rawStatus === "delayed" || rawStatus === "waiting_subworkflow" ? "running" : "pending";
|
|
6351
|
+
return {
|
|
6352
|
+
id: `${runId}-${nodeState.nodeId}`,
|
|
6353
|
+
runId,
|
|
6354
|
+
nodeId: nodeState.nodeId,
|
|
6355
|
+
nodeType: nodeState.nodeType,
|
|
6356
|
+
nodeName: getNodeName(nodeState.nodeId),
|
|
6357
|
+
status,
|
|
6358
|
+
inputArgs: nodeState.inputArgs ?? null,
|
|
6359
|
+
output: nodeState.output ?? null,
|
|
6360
|
+
error: nodeState.error ?? null,
|
|
6361
|
+
duration: nodeState.duration ?? null,
|
|
6362
|
+
startTime: nodeState.startTime ?? run.startedAt ?? "",
|
|
6363
|
+
endTime: nodeState.endTime ?? null,
|
|
6364
|
+
usedPinnedData: nodeState.usedPinnedData ?? false,
|
|
6365
|
+
reusedFromRunId: nodeState.reusedFromRunId ?? null
|
|
6366
|
+
};
|
|
6367
|
+
});
|
|
6368
|
+
loadDebugRun(runId, logs, {
|
|
6369
|
+
workflowKey: run.workflowKey,
|
|
6370
|
+
version: run.version,
|
|
6371
|
+
sourceMode: run.mode
|
|
6372
|
+
});
|
|
6373
|
+
return { success: true, runId };
|
|
6374
|
+
} catch (error) {
|
|
6375
|
+
console.error("[useNodeExecution] Error loading execution:", error);
|
|
6376
|
+
return { success: false, error: describeOperationError(error) };
|
|
6066
6377
|
}
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
}
|
|
6070
|
-
return initPromise;
|
|
6071
|
-
}
|
|
6072
|
-
function isNodeConfigsInitialized() {
|
|
6073
|
-
return isInitialized;
|
|
6074
|
-
}
|
|
6075
|
-
function rebuildNodeArrays() {
|
|
6076
|
-
NODE_CONFIGS.forEach((config2) => {
|
|
6077
|
-
const data = getNodeMetadata(config2);
|
|
6078
|
-
const nodeMetadata = {
|
|
6079
|
-
...data,
|
|
6080
|
-
detail: { ...config2.detail }
|
|
6081
|
-
};
|
|
6082
|
-
if (!NODES.some((n) => n.type === nodeMetadata.type)) {
|
|
6083
|
-
NODES.push(nodeMetadata);
|
|
6084
|
-
NODE_TYPES[nodeMetadata.type] = nodeMetadata.node;
|
|
6085
|
-
NODES_METADATA[nodeMetadata.type] = {
|
|
6086
|
-
...nodeMetadata,
|
|
6087
|
-
__details: { type: nodeMetadata.type, ...nodeMetadata.detail }
|
|
6088
|
-
};
|
|
6089
|
-
}
|
|
6090
|
-
});
|
|
6091
|
-
const newAvailableNodes = NODES.filter(
|
|
6092
|
-
(node) => node.available === void 0 || node.available
|
|
6093
|
-
).map((node) => {
|
|
6094
|
-
const nodeConfig = NODE_CONFIGS.find((config2) => config2.type === node.type);
|
|
6095
|
-
return {
|
|
6096
|
-
type: node.type,
|
|
6097
|
-
icon: node.detail.icon,
|
|
6098
|
-
title: node.detail.title,
|
|
6099
|
-
description: node.detail.description,
|
|
6100
|
-
gradientColor: node.detail.gradientColor ?? "gray",
|
|
6101
|
-
manifest: node.defaultData?.nodeData,
|
|
6102
|
-
publishType: nodeConfig?.publishType
|
|
6103
|
-
// Include publishType
|
|
6104
|
-
};
|
|
6105
|
-
});
|
|
6106
|
-
AVAILABLE_NODES.length = 0;
|
|
6107
|
-
AVAILABLE_NODES.push(...newAvailableNodes);
|
|
6108
|
-
notifyNodeTypesListeners();
|
|
6109
|
-
}
|
|
6110
|
-
ensureNodeConfigsLoaded().catch((err) => {
|
|
6111
|
-
console.warn(
|
|
6112
|
-
"[Workflows] Background node config initialization failed:",
|
|
6113
|
-
err
|
|
6378
|
+
},
|
|
6379
|
+
[cleanup, getNodeName, loadDebugRun]
|
|
6114
6380
|
);
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
if (
|
|
6118
|
-
|
|
6119
|
-
}
|
|
6120
|
-
NODE_TYPES[STICKY_NOTE_TYPE] = StickyNoteNode;
|
|
6121
|
-
function registerWorkflowNodeTypes(workflowNodes) {
|
|
6122
|
-
for (const node of workflowNodes) {
|
|
6123
|
-
if (node.type && !NODE_TYPES[node.type]) {
|
|
6124
|
-
const meta = getNodeMetadata({
|
|
6125
|
-
type: node.type,
|
|
6126
|
-
defaultData: {
|
|
6127
|
-
version: node.data?.version,
|
|
6128
|
-
nodeData: node.data?.nodeData
|
|
6129
|
-
},
|
|
6130
|
-
operations: node.data?.operations
|
|
6131
|
-
});
|
|
6132
|
-
if (meta?.node) {
|
|
6133
|
-
NODE_TYPES[node.type] = meta.node;
|
|
6381
|
+
useEffect(() => {
|
|
6382
|
+
return () => {
|
|
6383
|
+
if (pollTimerRef.current) {
|
|
6384
|
+
completeExecution();
|
|
6134
6385
|
}
|
|
6135
|
-
|
|
6136
|
-
|
|
6386
|
+
cleanup();
|
|
6387
|
+
};
|
|
6388
|
+
}, [cleanup, completeExecution]);
|
|
6389
|
+
return {
|
|
6390
|
+
executeStep,
|
|
6391
|
+
executeFullWorkflow,
|
|
6392
|
+
executeFromNode,
|
|
6393
|
+
loadExecution,
|
|
6394
|
+
cleanup,
|
|
6395
|
+
findTriggerNode
|
|
6396
|
+
};
|
|
6137
6397
|
}
|
|
6138
6398
|
var FluidGridsContext = createContext(
|
|
6139
6399
|
null
|
|
@@ -20485,116 +20745,6 @@ var ChatPanel = () => {
|
|
|
20485
20745
|
}
|
|
20486
20746
|
);
|
|
20487
20747
|
};
|
|
20488
|
-
|
|
20489
|
-
// src/builder/lib/field-schema.ts
|
|
20490
|
-
function isRecord4(value) {
|
|
20491
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20492
|
-
}
|
|
20493
|
-
function isIdentifier(key) {
|
|
20494
|
-
return /^[A-Za-z_$][\w$]*$/.test(key);
|
|
20495
|
-
}
|
|
20496
|
-
function joinPath(base, key) {
|
|
20497
|
-
if (isIdentifier(key)) return base ? `${base}.${key}` : key;
|
|
20498
|
-
const bracket = `[${JSON.stringify(key)}]`;
|
|
20499
|
-
return base ? `${base}${bracket}` : bracket;
|
|
20500
|
-
}
|
|
20501
|
-
function pathToExpression(path) {
|
|
20502
|
-
if (!path) return "${output}";
|
|
20503
|
-
const accessor = path.startsWith("[") ? `output${path}` : `output.${path}`;
|
|
20504
|
-
return "${" + accessor + "}";
|
|
20505
|
-
}
|
|
20506
|
-
function fieldFromSpec(key, path, spec) {
|
|
20507
|
-
if (typeof spec === "string") {
|
|
20508
|
-
return { key, path, type: spec };
|
|
20509
|
-
}
|
|
20510
|
-
if (isRecord4(spec)) {
|
|
20511
|
-
const declaredType = typeof spec.type === "string" ? spec.type : void 0;
|
|
20512
|
-
const description = typeof spec.description === "string" ? spec.description : void 0;
|
|
20513
|
-
const required = spec.required === true;
|
|
20514
|
-
if (isRecord4(spec.properties)) {
|
|
20515
|
-
const children = Object.entries(spec.properties).map(
|
|
20516
|
-
([k, v]) => fieldFromSpec(k, joinPath(path, k), v)
|
|
20517
|
-
);
|
|
20518
|
-
return { key, path, type: "object", required, description, children };
|
|
20519
|
-
}
|
|
20520
|
-
if (spec.items !== void 0) {
|
|
20521
|
-
const element = fieldFromSpec("0", `${path}[0]`, spec.items);
|
|
20522
|
-
return {
|
|
20523
|
-
key,
|
|
20524
|
-
path,
|
|
20525
|
-
type: "array",
|
|
20526
|
-
required,
|
|
20527
|
-
description,
|
|
20528
|
-
children: [element]
|
|
20529
|
-
};
|
|
20530
|
-
}
|
|
20531
|
-
return { key, path, type: declaredType ?? "any", required, description };
|
|
20532
|
-
}
|
|
20533
|
-
return { key, path, type: "any" };
|
|
20534
|
-
}
|
|
20535
|
-
function fieldsFromIO(io, root = "") {
|
|
20536
|
-
if (!isRecord4(io)) return [];
|
|
20537
|
-
return Object.entries(io).map(
|
|
20538
|
-
([key, spec]) => fieldFromSpec(key, joinPath(root, key), spec)
|
|
20539
|
-
);
|
|
20540
|
-
}
|
|
20541
|
-
function typeOfValue(value) {
|
|
20542
|
-
if (value === null || value === void 0) return "any";
|
|
20543
|
-
if (Array.isArray(value)) return "array";
|
|
20544
|
-
if (typeof value === "object") return "object";
|
|
20545
|
-
if (typeof value === "number") return "number";
|
|
20546
|
-
if (typeof value === "boolean") return "boolean";
|
|
20547
|
-
if (typeof value === "string") return "string";
|
|
20548
|
-
return "any";
|
|
20549
|
-
}
|
|
20550
|
-
function fieldFromValue(key, path, value) {
|
|
20551
|
-
if (Array.isArray(value)) {
|
|
20552
|
-
const children = value.length > 0 ? [fieldFromValue("0", `${path}[0]`, value[0])] : void 0;
|
|
20553
|
-
return { key, path, type: "array", children };
|
|
20554
|
-
}
|
|
20555
|
-
if (isRecord4(value)) {
|
|
20556
|
-
const children = Object.entries(value).map(
|
|
20557
|
-
([k, v]) => fieldFromValue(k, joinPath(path, k), v)
|
|
20558
|
-
);
|
|
20559
|
-
return { key, path, type: "object", children };
|
|
20560
|
-
}
|
|
20561
|
-
return { key, path, type: typeOfValue(value) };
|
|
20562
|
-
}
|
|
20563
|
-
function fieldsFromValue(value, root = "") {
|
|
20564
|
-
if (!isRecord4(value)) return [];
|
|
20565
|
-
return Object.entries(value).map(
|
|
20566
|
-
([key, v]) => fieldFromValue(key, joinPath(root, key), v)
|
|
20567
|
-
);
|
|
20568
|
-
}
|
|
20569
|
-
function declaredOutputFields(nodeType, op) {
|
|
20570
|
-
const name = op?.name;
|
|
20571
|
-
if (!name || nodeType?.startsWith("internal:")) {
|
|
20572
|
-
return fieldsFromIO(op?.output_fields);
|
|
20573
|
-
}
|
|
20574
|
-
const children = fieldsFromIO(op?.output_fields, name);
|
|
20575
|
-
if (children.length === 0) return [];
|
|
20576
|
-
return [{ key: name, path: name, type: "object", children }];
|
|
20577
|
-
}
|
|
20578
|
-
function normalizeOperations(raw) {
|
|
20579
|
-
if (Array.isArray(raw)) return raw;
|
|
20580
|
-
if (isRecord4(raw) && Array.isArray(raw.operations)) {
|
|
20581
|
-
return raw.operations;
|
|
20582
|
-
}
|
|
20583
|
-
return [];
|
|
20584
|
-
}
|
|
20585
|
-
function resolveNodeOperation(data, fallbackOperations2) {
|
|
20586
|
-
const nodeData = isRecord4(data?.nodeData) ? data?.nodeData : void 0;
|
|
20587
|
-
let operations = normalizeOperations(nodeData?.operations);
|
|
20588
|
-
if (operations.length === 0) {
|
|
20589
|
-
operations = normalizeOperations(data?.operations);
|
|
20590
|
-
}
|
|
20591
|
-
if (operations.length === 0 && fallbackOperations2 !== void 0) {
|
|
20592
|
-
operations = normalizeOperations(fallbackOperations2);
|
|
20593
|
-
}
|
|
20594
|
-
const opName = typeof data?.operation_name === "string" ? data.operation_name : void 0;
|
|
20595
|
-
const selected = operations.find((op) => op.name === opName) ?? operations[0];
|
|
20596
|
-
return { operations, selected };
|
|
20597
|
-
}
|
|
20598
20748
|
var FIELD_PATH_MIME = "application/x-fg-field-path";
|
|
20599
20749
|
var TYPE_ICON = {
|
|
20600
20750
|
string: "mdi:format-text",
|
|
@@ -21705,6 +21855,65 @@ function NodeLoadOptionsSelect({
|
|
|
21705
21855
|
}
|
|
21706
21856
|
);
|
|
21707
21857
|
}
|
|
21858
|
+
function StructuredParameterInput({
|
|
21859
|
+
name,
|
|
21860
|
+
schema,
|
|
21861
|
+
value,
|
|
21862
|
+
onChange,
|
|
21863
|
+
className,
|
|
21864
|
+
testId
|
|
21865
|
+
}) {
|
|
21866
|
+
const tr = useTr();
|
|
21867
|
+
const descriptionId = useId();
|
|
21868
|
+
const [draft, setDraft] = useState(() => ({
|
|
21869
|
+
value,
|
|
21870
|
+
text: formatParameterValue(value)
|
|
21871
|
+
}));
|
|
21872
|
+
const text = Object.is(draft.value, value) ? draft.text : formatParameterValue(value);
|
|
21873
|
+
const result = parseParameterValue(schema, text);
|
|
21874
|
+
const type = structuredParameterType(schema);
|
|
21875
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21876
|
+
/* @__PURE__ */ jsx(
|
|
21877
|
+
"textarea",
|
|
21878
|
+
{
|
|
21879
|
+
"aria-label": name,
|
|
21880
|
+
"aria-describedby": descriptionId,
|
|
21881
|
+
"aria-invalid": !result.valid,
|
|
21882
|
+
value: text,
|
|
21883
|
+
rows: 4,
|
|
21884
|
+
spellCheck: false,
|
|
21885
|
+
className,
|
|
21886
|
+
"data-testid": testId,
|
|
21887
|
+
placeholder: type === "array" ? "[]" : "{}",
|
|
21888
|
+
onChange: (event) => {
|
|
21889
|
+
const nextText = event.target.value;
|
|
21890
|
+
const nextResult = parseParameterValue(schema, nextText);
|
|
21891
|
+
const nextValue = nextResult.valid ? nextResult.value : nextText;
|
|
21892
|
+
setDraft({ value: nextValue, text: nextText });
|
|
21893
|
+
onChange(nextValue);
|
|
21894
|
+
}
|
|
21895
|
+
}
|
|
21896
|
+
),
|
|
21897
|
+
/* @__PURE__ */ jsx(
|
|
21898
|
+
"span",
|
|
21899
|
+
{
|
|
21900
|
+
id: descriptionId,
|
|
21901
|
+
role: result.valid ? void 0 : "alert",
|
|
21902
|
+
className: result.valid ? "text-[11px] text-muted-foreground" : "text-[11px] text-destructive",
|
|
21903
|
+
children: result.valid ? type === "array" ? tr(
|
|
21904
|
+
"fluidgrids.builder.nodeParamsForm.arrayHint",
|
|
21905
|
+
"Enter a JSON array. String lists also accept comma-separated values."
|
|
21906
|
+
) : tr(
|
|
21907
|
+
"fluidgrids.builder.nodeParamsForm.objectHint",
|
|
21908
|
+
"Enter a JSON object."
|
|
21909
|
+
) : tr(
|
|
21910
|
+
"fluidgrids.builder.nodeParamsForm.structuredInputInvalid",
|
|
21911
|
+
result.error
|
|
21912
|
+
)
|
|
21913
|
+
}
|
|
21914
|
+
)
|
|
21915
|
+
] });
|
|
21916
|
+
}
|
|
21708
21917
|
|
|
21709
21918
|
// src/builder/lib/mapping-values.ts
|
|
21710
21919
|
function isMappingValue(value) {
|
|
@@ -21822,6 +22031,7 @@ function paramsFromOperation(op) {
|
|
|
21822
22031
|
description: typeof rec?.description === "string" ? rec.description : void 0,
|
|
21823
22032
|
options,
|
|
21824
22033
|
defaultValue: rec?.default,
|
|
22034
|
+
schema: spec,
|
|
21825
22035
|
loadOptions
|
|
21826
22036
|
};
|
|
21827
22037
|
});
|
|
@@ -21986,18 +22196,24 @@ function useNodeConfig(target, options) {
|
|
|
21986
22196
|
};
|
|
21987
22197
|
const writeFixed = (field, value) => {
|
|
21988
22198
|
if (!nodeId) return;
|
|
22199
|
+
const parameter = params.find((candidate) => candidate.name === field);
|
|
22200
|
+
const parsed = parseParameterValue(
|
|
22201
|
+
parameter?.schema ?? parameter?.type,
|
|
22202
|
+
value
|
|
22203
|
+
);
|
|
22204
|
+
const nextValue = parsed.valid ? parsed.value : value;
|
|
21989
22205
|
const store = useFlowStore.getState();
|
|
21990
22206
|
const next = store.workflow.nodes.map((n) => {
|
|
21991
22207
|
if (n.id !== nodeId) return n;
|
|
21992
22208
|
const data = { ...asRecord(n.data) ?? {} };
|
|
21993
22209
|
const args = { ...asRecord(data.args) ?? {} };
|
|
21994
22210
|
const state = { ...asRecord(args.state) ?? {} };
|
|
21995
|
-
if (
|
|
22211
|
+
if (nextValue === void 0 || nextValue === null || nextValue === "") {
|
|
21996
22212
|
delete state[field];
|
|
22213
|
+
args.state = state;
|
|
21997
22214
|
} else {
|
|
21998
|
-
state[field]
|
|
22215
|
+
args.state = { ...state, [field]: nextValue };
|
|
21999
22216
|
}
|
|
22000
|
-
args.state = state;
|
|
22001
22217
|
data.args = args;
|
|
22002
22218
|
return { ...n, data };
|
|
22003
22219
|
});
|
|
@@ -22436,6 +22652,17 @@ function NodeParamsForm({
|
|
|
22436
22652
|
onChange: (v) => writeFixed(p.name, v),
|
|
22437
22653
|
"data-testid": `${testIdPrefix}-param-${p.name}`
|
|
22438
22654
|
}
|
|
22655
|
+
) : structuredParameterType(p.schema ?? p.type) ? /* @__PURE__ */ jsx(
|
|
22656
|
+
StructuredParameterInput,
|
|
22657
|
+
{
|
|
22658
|
+
name: p.name,
|
|
22659
|
+
schema: p.schema ?? p.type,
|
|
22660
|
+
value: fixedValue,
|
|
22661
|
+
onChange: (value) => writeFixed(p.name, value),
|
|
22662
|
+
className: cn(inputCx2, "font-mono"),
|
|
22663
|
+
testId: `${testIdPrefix}-param-${p.name}`
|
|
22664
|
+
},
|
|
22665
|
+
`${config2.node?.id}:${config2.currentOp?.name}:${p.name}`
|
|
22439
22666
|
) : p.loadOptions ? /* @__PURE__ */ jsx(
|
|
22440
22667
|
NodeLoadOptionsSelect,
|
|
22441
22668
|
{
|
|
@@ -24371,7 +24598,7 @@ function QuickConfigPanelInner({
|
|
|
24371
24598
|
}
|
|
24372
24599
|
|
|
24373
24600
|
// src/utils/workflow-definition-diff.ts
|
|
24374
|
-
function
|
|
24601
|
+
function isRecord6(value) {
|
|
24375
24602
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24376
24603
|
}
|
|
24377
24604
|
function equalJson(left, right) {
|
|
@@ -24379,7 +24606,7 @@ function equalJson(left, right) {
|
|
|
24379
24606
|
if (Array.isArray(left) && Array.isArray(right)) {
|
|
24380
24607
|
return left.length === right.length && left.every((value, index) => equalJson(value, right[index]));
|
|
24381
24608
|
}
|
|
24382
|
-
if (
|
|
24609
|
+
if (isRecord6(left) && isRecord6(right)) {
|
|
24383
24610
|
const leftKeys = Object.keys(left).sort((a, b) => a.localeCompare(b));
|
|
24384
24611
|
const rightKeys = Object.keys(right).sort((a, b) => a.localeCompare(b));
|
|
24385
24612
|
return leftKeys.length === rightKeys.length && leftKeys.every(
|
|
@@ -24392,7 +24619,7 @@ function diffWorkflowDefinitionJson(before, after, maxEntries = 2e3) {
|
|
|
24392
24619
|
const entries = [];
|
|
24393
24620
|
const visit = (left, right, path) => {
|
|
24394
24621
|
if (entries.length >= maxEntries || equalJson(left, right)) return;
|
|
24395
|
-
if (
|
|
24622
|
+
if (isRecord6(left) && isRecord6(right)) {
|
|
24396
24623
|
const keys = [
|
|
24397
24624
|
.../* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)])
|
|
24398
24625
|
].sort((a, b) => a.localeCompare(b));
|