@codemation/core 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +2 -0
- package/dist/{EngineRuntimeRegistration.types-0sgV2XL2.d.ts → EngineRuntimeRegistration.types-Bjeo7Sfq.d.ts} +2 -2
- package/dist/{EngineWorkflowRunnerService-Dx7bJsJR.d.cts → EngineWorkflowRunnerService-Dd4yD31l.d.cts} +2 -2
- package/dist/{InMemoryRunDataFactory-qIYQEar7.d.cts → InMemoryRunDataFactory-OUzDmAHt.d.cts} +2 -2
- package/dist/{RunIntentService-BCvGdOSY.d.ts → RunIntentService-BAKikN8h.d.ts} +80 -12
- package/dist/{RunIntentService-CV8izV8t.d.cts → RunIntentService-Bkg4oYrM.d.cts} +74 -5
- package/dist/bootstrap/index.cjs +31 -31
- package/dist/bootstrap/index.d.cts +5 -3
- package/dist/bootstrap/index.d.ts +3 -3
- package/dist/bootstrap/index.js +2 -2
- package/dist/bootstrap-BD6CobHl.js +215 -0
- package/dist/bootstrap-BD6CobHl.js.map +1 -0
- package/dist/bootstrap-DwS5S7s9.cjs +240 -0
- package/dist/bootstrap-DwS5S7s9.cjs.map +1 -0
- package/dist/{index-CueSzHsf.d.ts → index-uCm9l0nw.d.ts} +64 -15
- package/dist/index.cjs +114 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -21
- package/dist/index.d.ts +3 -3
- package/dist/index.js +83 -2
- package/dist/index.js.map +1 -1
- package/dist/{RunIntentService-BFA48UpH.js → runtime-Cy-3FTI_.js} +1224 -94
- package/dist/runtime-Cy-3FTI_.js.map +1 -0
- package/dist/{RunIntentService-DcxXf_AM.cjs → runtime-ZJUpWmPH.cjs} +1251 -132
- package/dist/runtime-ZJUpWmPH.cjs.map +1 -0
- package/dist/testing.cjs +74 -29
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +55 -3
- package/dist/testing.d.ts +55 -3
- package/dist/testing.js +46 -3
- package/dist/testing.js.map +1 -1
- package/dist/workflowActivationPolicy-B8HzTk3o.js.map +1 -1
- package/dist/workflowActivationPolicy-BzyzXLa_.cjs.map +1 -1
- package/package.json +1 -1
- package/src/ai/AgentMessageConfigNormalizerFactory.ts +43 -0
- package/src/ai/AgentToolFactory.ts +2 -2
- package/src/ai/AiHost.ts +10 -9
- package/src/ai/NodeBackedToolConfig.ts +1 -1
- package/src/authoring/defineNode.types.ts +153 -10
- package/src/authoring/index.ts +3 -1
- package/src/contracts/runtimeTypes.ts +26 -0
- package/src/contracts/workflowTypes.ts +67 -5
- package/src/execution/ActivationEnqueueService.ts +8 -5
- package/src/execution/NodeActivationRequestInputPreparer.ts +89 -0
- package/src/execution/NodeExecutor.ts +38 -1
- package/src/execution/NodeInputContractError.ts +13 -0
- package/src/execution/index.ts +2 -0
- package/src/orchestration/RunContinuationService.ts +181 -50
- package/src/planning/RunQueuePlanner.ts +12 -1
- package/src/runtime/EngineFactory.ts +3 -0
- package/src/testing/ItemHarnessNode.ts +27 -0
- package/src/testing/ItemHarnessNodeConfig.ts +43 -0
- package/src/testing/RegistrarEngineTestKitFactory.ts +2 -0
- package/src/testing.ts +2 -0
- package/src/workflow/dsl/ChainCursorResolver.ts +1 -1
- package/src/workflow/dsl/workflowBuilderTypes.ts +8 -5
- package/dist/RunIntentService-BFA48UpH.js.map +0 -1
- package/dist/RunIntentService-DcxXf_AM.cjs.map +0 -1
- package/dist/bootstrap-D67Sf2BF.js +0 -1136
- package/dist/bootstrap-D67Sf2BF.js.map +0 -1
- package/dist/bootstrap-DoQHAEQJ.cjs +0 -1203
- package/dist/bootstrap-DoQHAEQJ.cjs.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
import { container as container$1, delay, inject, injectAll, injectable, instanceCachingFactory, instancePerContainerCachingFactory, predicateAwareClassFactory, registry, singleton } from "tsyringe";
|
|
3
|
+
import { ZodError, z } from "zod";
|
|
3
4
|
import { createHash } from "node:crypto";
|
|
4
5
|
import { ReadableStream } from "node:stream/web";
|
|
5
6
|
|
|
@@ -487,10 +488,11 @@ var NodeInputsByPortFactory = class {
|
|
|
487
488
|
//#endregion
|
|
488
489
|
//#region src/execution/ActivationEnqueueService.ts
|
|
489
490
|
var ActivationEnqueueService = class {
|
|
490
|
-
constructor(activationScheduler, workflowExecutionRepository, nodeEventPublisher) {
|
|
491
|
+
constructor(activationScheduler, workflowExecutionRepository, nodeEventPublisher, nodeActivationRequestInputPreparer) {
|
|
491
492
|
this.activationScheduler = activationScheduler;
|
|
492
493
|
this.workflowExecutionRepository = workflowExecutionRepository;
|
|
493
494
|
this.nodeEventPublisher = nodeEventPublisher;
|
|
495
|
+
this.nodeActivationRequestInputPreparer = nodeActivationRequestInputPreparer;
|
|
494
496
|
}
|
|
495
497
|
async enqueueActivation(args) {
|
|
496
498
|
const { result, queuedSnapshot } = await this.enqueueActivationWithSnapshot(args);
|
|
@@ -498,9 +500,10 @@ var ActivationEnqueueService = class {
|
|
|
498
500
|
return result;
|
|
499
501
|
}
|
|
500
502
|
async enqueueActivationWithSnapshot(args) {
|
|
501
|
-
const
|
|
502
|
-
const
|
|
503
|
-
const
|
|
503
|
+
const preparedRequest = await this.nodeActivationRequestInputPreparer.prepare(args.request);
|
|
504
|
+
const preparedDispatch = await this.activationScheduler.prepareDispatch(preparedRequest);
|
|
505
|
+
const inputsByPort = NodeInputsByPortFactory.fromRequest(preparedRequest);
|
|
506
|
+
const itemsIn = preparedRequest.kind === "multi" ? args.planner.sumItemsByPort(preparedRequest.inputsByPort) : preparedRequest.input.length;
|
|
504
507
|
const enqueuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
505
508
|
const pending = {
|
|
506
509
|
runId: args.runId,
|
|
@@ -561,6 +564,75 @@ var ActivationEnqueueService = class {
|
|
|
561
564
|
}
|
|
562
565
|
};
|
|
563
566
|
|
|
567
|
+
//#endregion
|
|
568
|
+
//#region src/execution/NodeInputContractError.ts
|
|
569
|
+
var NodeInputContractError = class extends Error {
|
|
570
|
+
constructor(message, nodeId, activationId, cause) {
|
|
571
|
+
super(message);
|
|
572
|
+
this.nodeId = nodeId;
|
|
573
|
+
this.activationId = activationId;
|
|
574
|
+
this.cause = cause;
|
|
575
|
+
this.name = "NodeInputContractError";
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
//#endregion
|
|
580
|
+
//#region src/execution/NodeActivationRequestInputPreparer.ts
|
|
581
|
+
/**
|
|
582
|
+
* Maps and validates per-item inputs for {@link ItemNode} before enqueue persistence.
|
|
583
|
+
*/
|
|
584
|
+
var NodeActivationRequestInputPreparer = class {
|
|
585
|
+
constructor(workflowNodeInstanceFactory) {
|
|
586
|
+
this.workflowNodeInstanceFactory = workflowNodeInstanceFactory;
|
|
587
|
+
}
|
|
588
|
+
async prepare(request) {
|
|
589
|
+
if (request.kind !== "single") return request;
|
|
590
|
+
const nodeInstance = this.workflowNodeInstanceFactory.createByType(request.ctx.config.type);
|
|
591
|
+
if (!this.hasExecuteOne(nodeInstance)) return request;
|
|
592
|
+
const inputSchema = this.resolveInputSchema(nodeInstance, request.ctx.config);
|
|
593
|
+
const config = request.ctx.config;
|
|
594
|
+
const mappedItems = [];
|
|
595
|
+
for (let i = 0; i < request.input.length; i++) {
|
|
596
|
+
const item = request.input[i];
|
|
597
|
+
try {
|
|
598
|
+
const mappedRaw = config.mapInput ? await Promise.resolve(config.mapInput({
|
|
599
|
+
item,
|
|
600
|
+
itemIndex: i,
|
|
601
|
+
items: request.input,
|
|
602
|
+
ctx: request.ctx
|
|
603
|
+
})) : item.json;
|
|
604
|
+
const parsed = inputSchema.parse(mappedRaw);
|
|
605
|
+
mappedItems.push({
|
|
606
|
+
...item,
|
|
607
|
+
json: parsed
|
|
608
|
+
});
|
|
609
|
+
} catch (cause) {
|
|
610
|
+
const message = this.formatContractFailure(cause);
|
|
611
|
+
throw new NodeInputContractError(`Node ${request.nodeId} activation ${request.activationId}: input contract failed: ${message}`, request.nodeId, request.activationId, cause);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
615
|
+
...request,
|
|
616
|
+
input: mappedItems
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
hasExecuteOne(nodeInstance) {
|
|
620
|
+
return typeof nodeInstance === "object" && nodeInstance !== null && typeof nodeInstance.executeOne === "function";
|
|
621
|
+
}
|
|
622
|
+
resolveInputSchema(nodeInstance, config) {
|
|
623
|
+
const fromInstance = nodeInstance.inputSchema;
|
|
624
|
+
if (fromInstance && typeof fromInstance.parse === "function") return fromInstance;
|
|
625
|
+
const fromConfig = config.inputSchema;
|
|
626
|
+
if (fromConfig && typeof fromConfig.parse === "function") return fromConfig;
|
|
627
|
+
return z.unknown();
|
|
628
|
+
}
|
|
629
|
+
formatContractFailure(cause) {
|
|
630
|
+
if (cause instanceof ZodError) return cause.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`).join("; ");
|
|
631
|
+
if (cause instanceof Error) return cause.message;
|
|
632
|
+
return String(cause);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
|
|
564
636
|
//#endregion
|
|
565
637
|
//#region src/execution/CredentialResolverFactory.ts
|
|
566
638
|
var CredentialResolverFactory = class {
|
|
@@ -806,10 +878,32 @@ var NodeExecutor = class {
|
|
|
806
878
|
return await multiInputNode.executeMulti(request.inputsByPort, request.ctx);
|
|
807
879
|
}
|
|
808
880
|
async executeSingleInputNode(request, node$1) {
|
|
881
|
+
if (this.hasExecuteOne(node$1)) return await this.executeItemNode(request, node$1);
|
|
809
882
|
const singleInputNode = node$1;
|
|
810
883
|
if (typeof singleInputNode.execute !== "function") throw new Error(`Node ${request.nodeId} does not support execute but received single-input activation`);
|
|
811
884
|
return await singleInputNode.execute(request.input, request.ctx);
|
|
812
885
|
}
|
|
886
|
+
hasExecuteOne(node$1) {
|
|
887
|
+
return typeof node$1 === "object" && node$1 !== null && typeof node$1.executeOne === "function";
|
|
888
|
+
}
|
|
889
|
+
async executeItemNode(request, node$1) {
|
|
890
|
+
const out = [];
|
|
891
|
+
for (let i = 0; i < request.input.length; i++) {
|
|
892
|
+
const item = request.input[i];
|
|
893
|
+
const outputJson = await Promise.resolve(node$1.executeOne({
|
|
894
|
+
input: item.json,
|
|
895
|
+
item,
|
|
896
|
+
itemIndex: i,
|
|
897
|
+
items: request.input,
|
|
898
|
+
ctx: request.ctx
|
|
899
|
+
}));
|
|
900
|
+
out.push({
|
|
901
|
+
...item,
|
|
902
|
+
json: outputJson
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
return { main: out };
|
|
906
|
+
}
|
|
813
907
|
};
|
|
814
908
|
|
|
815
909
|
//#endregion
|
|
@@ -1624,15 +1718,15 @@ var RunContinuationService = class {
|
|
|
1624
1718
|
finalStatus: "completed",
|
|
1625
1719
|
finishedAt: completedAt
|
|
1626
1720
|
});
|
|
1627
|
-
const result
|
|
1721
|
+
const result = {
|
|
1628
1722
|
runId: state.runId,
|
|
1629
1723
|
workflowId: state.workflowId,
|
|
1630
1724
|
startedAt: state.startedAt,
|
|
1631
1725
|
status: "completed",
|
|
1632
1726
|
outputs: this.semantics.resolveResultOutputs(wf, state.control?.stopCondition, data.dump())
|
|
1633
1727
|
};
|
|
1634
|
-
this.waiters.resolveRunCompletion(result
|
|
1635
|
-
return result
|
|
1728
|
+
this.waiters.resolveRunCompletion(result);
|
|
1729
|
+
return result;
|
|
1636
1730
|
}
|
|
1637
1731
|
const batchId = pendingExecution.batchId ?? "batch_1";
|
|
1638
1732
|
const queue = (schedulingState?.queue ?? []).map((q) => ({
|
|
@@ -1691,15 +1785,15 @@ var RunContinuationService = class {
|
|
|
1691
1785
|
finalStatus: "completed",
|
|
1692
1786
|
finishedAt: completedAt
|
|
1693
1787
|
});
|
|
1694
|
-
const result
|
|
1788
|
+
const result = {
|
|
1695
1789
|
runId: state.runId,
|
|
1696
1790
|
workflowId: state.workflowId,
|
|
1697
1791
|
startedAt: state.startedAt,
|
|
1698
1792
|
status: "completed",
|
|
1699
1793
|
outputs
|
|
1700
1794
|
};
|
|
1701
|
-
this.waiters.resolveRunCompletion(result
|
|
1702
|
-
return result
|
|
1795
|
+
this.waiters.resolveRunCompletion(result);
|
|
1796
|
+
return result;
|
|
1703
1797
|
}
|
|
1704
1798
|
if (completedActivations >= maxNodeActivations) {
|
|
1705
1799
|
const message = `Run exceeded maxNodeActivations (${maxNodeActivations}) after ${completedActivations} completed node activations (next would be ${next.nodeId}).`;
|
|
@@ -1720,15 +1814,15 @@ var RunContinuationService = class {
|
|
|
1720
1814
|
finalStatus: "failed",
|
|
1721
1815
|
finishedAt: completedAt
|
|
1722
1816
|
});
|
|
1723
|
-
const result
|
|
1817
|
+
const result = {
|
|
1724
1818
|
runId: state.runId,
|
|
1725
1819
|
workflowId: state.workflowId,
|
|
1726
1820
|
startedAt: state.startedAt,
|
|
1727
1821
|
status: "failed",
|
|
1728
1822
|
error: { message }
|
|
1729
1823
|
};
|
|
1730
|
-
this.waiters.resolveRunCompletion(result
|
|
1731
|
-
return result
|
|
1824
|
+
this.waiters.resolveRunCompletion(result);
|
|
1825
|
+
return result;
|
|
1732
1826
|
}
|
|
1733
1827
|
const def = topology.defsById.get(next.nodeId);
|
|
1734
1828
|
if (!def || def.kind !== "node") throw new Error(`Node ${next.nodeId} is not a runnable node`);
|
|
@@ -1742,26 +1836,43 @@ var RunContinuationService = class {
|
|
|
1742
1836
|
executionOptions: state.executionOptions,
|
|
1743
1837
|
nodeDefinition: def
|
|
1744
1838
|
});
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1839
|
+
try {
|
|
1840
|
+
const { queuedSnapshot, result } = await this.activationEnqueueService.enqueueActivationWithSnapshot({
|
|
1841
|
+
runId: state.runId,
|
|
1842
|
+
workflowId: state.workflowId,
|
|
1843
|
+
startedAt: state.startedAt,
|
|
1844
|
+
parent: state.parent,
|
|
1845
|
+
executionOptions: state.executionOptions,
|
|
1846
|
+
control: state.control,
|
|
1847
|
+
workflowSnapshot: state.workflowSnapshot,
|
|
1848
|
+
mutableState: state.mutableState,
|
|
1849
|
+
policySnapshot: state.policySnapshot,
|
|
1850
|
+
pendingQueue: queue,
|
|
1851
|
+
request,
|
|
1852
|
+
previousNodeSnapshotsByNodeId: nextNodeSnapshotsByNodeId,
|
|
1853
|
+
planner,
|
|
1854
|
+
engineCounters,
|
|
1855
|
+
connectionInvocations: state.connectionInvocations ?? []
|
|
1856
|
+
});
|
|
1857
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
1858
|
+
await this.nodeEventPublisher.publish("nodeQueued", queuedSnapshot);
|
|
1859
|
+
return result;
|
|
1860
|
+
} catch (cause) {
|
|
1861
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
1862
|
+
return await this.terminateRunAfterActivationEnqueueRejected({
|
|
1863
|
+
wf,
|
|
1864
|
+
state,
|
|
1865
|
+
queue,
|
|
1866
|
+
nextNodeId: next.nodeId,
|
|
1867
|
+
request,
|
|
1868
|
+
completedSnapshot,
|
|
1869
|
+
nextNodeSnapshotsByNodeId,
|
|
1870
|
+
outputsByNode: data.dump(),
|
|
1871
|
+
engineCounters,
|
|
1872
|
+
error,
|
|
1873
|
+
completedAt
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1765
1876
|
}
|
|
1766
1877
|
async resumeFromNodeError(args) {
|
|
1767
1878
|
const [state, schedulingState] = await Promise.all([this.workflowExecutionRepository.load(args.runId), this.workflowExecutionRepository.loadSchedulingState(args.runId)]);
|
|
@@ -1938,15 +2049,15 @@ var RunContinuationService = class {
|
|
|
1938
2049
|
runStatus: "completed",
|
|
1939
2050
|
response: args.signal.responseItems
|
|
1940
2051
|
});
|
|
1941
|
-
const result
|
|
2052
|
+
const result = {
|
|
1942
2053
|
runId: args.state.runId,
|
|
1943
2054
|
workflowId: args.state.workflowId,
|
|
1944
2055
|
startedAt: args.state.startedAt,
|
|
1945
2056
|
status: "completed",
|
|
1946
2057
|
outputs: this.semantics.resolveResultOutputs(args.workflow, args.state.control?.stopCondition, data.dump())
|
|
1947
2058
|
};
|
|
1948
|
-
this.waiters.resolveRunCompletion(result
|
|
1949
|
-
return result
|
|
2059
|
+
this.waiters.resolveRunCompletion(result);
|
|
2060
|
+
return result;
|
|
1950
2061
|
}
|
|
1951
2062
|
if (args.signal.kind === "respondNow") {
|
|
1952
2063
|
const completedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
@@ -1969,7 +2080,7 @@ var RunContinuationService = class {
|
|
|
1969
2080
|
finalStatus: "completed",
|
|
1970
2081
|
finishedAt: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
|
|
1971
2082
|
});
|
|
1972
|
-
const result
|
|
2083
|
+
const result = {
|
|
1973
2084
|
runId: args.state.runId,
|
|
1974
2085
|
workflowId: args.state.workflowId,
|
|
1975
2086
|
startedAt: args.state.startedAt,
|
|
@@ -1983,8 +2094,8 @@ var RunContinuationService = class {
|
|
|
1983
2094
|
runStatus: "completed",
|
|
1984
2095
|
response: args.signal.responseItems
|
|
1985
2096
|
});
|
|
1986
|
-
this.waiters.resolveRunCompletion(result
|
|
1987
|
-
return result
|
|
2097
|
+
this.waiters.resolveRunCompletion(result);
|
|
2098
|
+
return result;
|
|
1988
2099
|
}
|
|
1989
2100
|
const batchId = args.pendingExecution.batchId ?? "batch_1";
|
|
1990
2101
|
const queue = (args.schedulingState?.queue ?? []).map((entry) => ({
|
|
@@ -2020,7 +2131,7 @@ var RunContinuationService = class {
|
|
|
2020
2131
|
finalStatus: "completed",
|
|
2021
2132
|
finishedAt: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
|
|
2022
2133
|
});
|
|
2023
|
-
const result
|
|
2134
|
+
const result = {
|
|
2024
2135
|
runId: args.state.runId,
|
|
2025
2136
|
workflowId: args.state.workflowId,
|
|
2026
2137
|
startedAt: args.state.startedAt,
|
|
@@ -2034,8 +2145,8 @@ var RunContinuationService = class {
|
|
|
2034
2145
|
runStatus: "completed",
|
|
2035
2146
|
response: args.signal.responseItems
|
|
2036
2147
|
});
|
|
2037
|
-
this.waiters.resolveRunCompletion(result
|
|
2038
|
-
return result
|
|
2148
|
+
this.waiters.resolveRunCompletion(result);
|
|
2149
|
+
return result;
|
|
2039
2150
|
}
|
|
2040
2151
|
if (completedActivations >= maxNodeActivations) {
|
|
2041
2152
|
const message = `Run exceeded maxNodeActivations (${maxNodeActivations}) after ${completedActivations} completed node activations (next would be ${next.nodeId}).`;
|
|
@@ -2059,7 +2170,7 @@ var RunContinuationService = class {
|
|
|
2059
2170
|
finalStatus: "failed",
|
|
2060
2171
|
finishedAt: completedSnapshot.finishedAt ?? completedSnapshot.updatedAt
|
|
2061
2172
|
});
|
|
2062
|
-
const result
|
|
2173
|
+
const result = {
|
|
2063
2174
|
runId: args.state.runId,
|
|
2064
2175
|
workflowId: args.state.workflowId,
|
|
2065
2176
|
startedAt: args.state.startedAt,
|
|
@@ -2073,8 +2184,8 @@ var RunContinuationService = class {
|
|
|
2073
2184
|
runStatus: "pending",
|
|
2074
2185
|
response: args.signal.responseItems
|
|
2075
2186
|
});
|
|
2076
|
-
this.waiters.resolveRunCompletion(result
|
|
2077
|
-
return result
|
|
2187
|
+
this.waiters.resolveRunCompletion(result);
|
|
2188
|
+
return result;
|
|
2078
2189
|
}
|
|
2079
2190
|
const nextDefinition = topology.defsById.get(next.nodeId);
|
|
2080
2191
|
if (!nextDefinition || nextDefinition.kind !== "node") throw new Error(`Node ${next.nodeId} is not a runnable node`);
|
|
@@ -2100,36 +2211,63 @@ var RunContinuationService = class {
|
|
|
2100
2211
|
executionOptions: args.state.executionOptions,
|
|
2101
2212
|
nodeDefinition: nextDefinition
|
|
2102
2213
|
});
|
|
2103
|
-
const
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2214
|
+
const mergedSnapshots = {
|
|
2215
|
+
...args.state.nodeSnapshotsByNodeId ?? {},
|
|
2216
|
+
[args.args.nodeId]: completedSnapshot
|
|
2217
|
+
};
|
|
2218
|
+
try {
|
|
2219
|
+
const { queuedSnapshot, result } = await this.activationEnqueueService.enqueueActivationWithSnapshot({
|
|
2220
|
+
runId: args.state.runId,
|
|
2221
|
+
workflowId: args.state.workflowId,
|
|
2222
|
+
startedAt: args.state.startedAt,
|
|
2223
|
+
parent: args.state.parent,
|
|
2224
|
+
executionOptions: args.state.executionOptions,
|
|
2225
|
+
control: args.state.control,
|
|
2226
|
+
workflowSnapshot: args.state.workflowSnapshot,
|
|
2227
|
+
mutableState: args.state.mutableState,
|
|
2228
|
+
policySnapshot: args.state.policySnapshot,
|
|
2229
|
+
pendingQueue: queue,
|
|
2230
|
+
request,
|
|
2231
|
+
previousNodeSnapshotsByNodeId: mergedSnapshots,
|
|
2232
|
+
planner,
|
|
2233
|
+
engineCounters,
|
|
2234
|
+
connectionInvocations: args.state.connectionInvocations ?? []
|
|
2235
|
+
});
|
|
2236
|
+
await this.nodeEventPublisher.publish("nodeCompleted", completedSnapshot);
|
|
2237
|
+
await this.nodeEventPublisher.publish("nodeQueued", queuedSnapshot);
|
|
2238
|
+
this.waiters.resolveWebhookResponse({
|
|
2239
|
+
runId: args.state.runId,
|
|
2240
|
+
workflowId: args.state.workflowId,
|
|
2241
|
+
startedAt: args.state.startedAt,
|
|
2242
|
+
runStatus: "pending",
|
|
2243
|
+
response: args.signal.responseItems
|
|
2244
|
+
});
|
|
2245
|
+
return result;
|
|
2246
|
+
} catch (cause) {
|
|
2247
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
2248
|
+
const finishedAt = completedSnapshot.finishedAt ?? completedSnapshot.updatedAt;
|
|
2249
|
+
const result = await this.terminateRunAfterActivationEnqueueRejected({
|
|
2250
|
+
wf: args.workflow,
|
|
2251
|
+
state: args.state,
|
|
2252
|
+
queue,
|
|
2253
|
+
nextNodeId: next.nodeId,
|
|
2254
|
+
request,
|
|
2255
|
+
completedSnapshot,
|
|
2256
|
+
nextNodeSnapshotsByNodeId: mergedSnapshots,
|
|
2257
|
+
outputsByNode: data.dump(),
|
|
2258
|
+
engineCounters,
|
|
2259
|
+
error,
|
|
2260
|
+
completedAt: finishedAt
|
|
2261
|
+
});
|
|
2262
|
+
this.waiters.resolveWebhookResponse({
|
|
2263
|
+
runId: args.state.runId,
|
|
2264
|
+
workflowId: args.state.workflowId,
|
|
2265
|
+
startedAt: args.state.startedAt,
|
|
2266
|
+
runStatus: "pending",
|
|
2267
|
+
response: args.signal.responseItems
|
|
2268
|
+
});
|
|
2269
|
+
return result;
|
|
2270
|
+
}
|
|
2133
2271
|
}
|
|
2134
2272
|
asWebhookControlSignal(error) {
|
|
2135
2273
|
const candidate = error;
|
|
@@ -2187,6 +2325,66 @@ var RunContinuationService = class {
|
|
|
2187
2325
|
engineMaxSubworkflowDepth: state.executionOptions?.maxSubworkflowDepth ?? fb.maxSubworkflowDepth
|
|
2188
2326
|
};
|
|
2189
2327
|
}
|
|
2328
|
+
/**
|
|
2329
|
+
* Next activation could not be enqueued (e.g. input contract / mapping failed in the preparer).
|
|
2330
|
+
* Marks the target node failed and terminates the run.
|
|
2331
|
+
*/
|
|
2332
|
+
async terminateRunAfterActivationEnqueueRejected(args) {
|
|
2333
|
+
const finishedAt = args.completedAt;
|
|
2334
|
+
const inputsByPort = NodeInputsByPortFactory.fromRequest(args.request);
|
|
2335
|
+
const failedSnapshot = NodeExecutionSnapshotFactory.failed({
|
|
2336
|
+
previous: void 0,
|
|
2337
|
+
runId: args.state.runId,
|
|
2338
|
+
workflowId: args.state.workflowId,
|
|
2339
|
+
nodeId: args.nextNodeId,
|
|
2340
|
+
activationId: args.request.activationId,
|
|
2341
|
+
parent: args.state.parent,
|
|
2342
|
+
finishedAt,
|
|
2343
|
+
inputsByPort,
|
|
2344
|
+
error: args.error
|
|
2345
|
+
});
|
|
2346
|
+
const failedState = this.persistedRunStateTerminalBuilder.mergeTerminal({
|
|
2347
|
+
state: args.state,
|
|
2348
|
+
engineCounters: args.engineCounters,
|
|
2349
|
+
status: "failed",
|
|
2350
|
+
queue: args.queue.map((q) => ({ ...q })),
|
|
2351
|
+
outputsByNode: args.outputsByNode,
|
|
2352
|
+
nodeSnapshotsByNodeId: {
|
|
2353
|
+
...args.nextNodeSnapshotsByNodeId,
|
|
2354
|
+
[args.nextNodeId]: failedSnapshot
|
|
2355
|
+
},
|
|
2356
|
+
finishedAtIso: finishedAt
|
|
2357
|
+
});
|
|
2358
|
+
await this.workflowExecutionRepository.save(failedState);
|
|
2359
|
+
await this.nodeEventPublisher.publish("nodeCompleted", args.completedSnapshot);
|
|
2360
|
+
await this.nodeEventPublisher.publish("nodeFailed", failedSnapshot);
|
|
2361
|
+
const wfErr = this.policyErrorServices.resolveWorkflowErrorHandler(args.wf.workflowErrorHandler);
|
|
2362
|
+
if (wfErr) await Promise.resolve(wfErr.onError({
|
|
2363
|
+
runId: args.state.runId,
|
|
2364
|
+
workflowId: args.state.workflowId,
|
|
2365
|
+
workflow: args.wf,
|
|
2366
|
+
failedNodeId: args.nextNodeId,
|
|
2367
|
+
error: args.error,
|
|
2368
|
+
startedAt: args.state.startedAt,
|
|
2369
|
+
finishedAt
|
|
2370
|
+
}));
|
|
2371
|
+
await this.terminalPersistence.maybeDeleteAfterTerminalState({
|
|
2372
|
+
workflow: args.wf,
|
|
2373
|
+
state: failedState,
|
|
2374
|
+
finalStatus: "failed",
|
|
2375
|
+
finishedAt
|
|
2376
|
+
});
|
|
2377
|
+
const message = args.error.message ?? String(args.error);
|
|
2378
|
+
const result = {
|
|
2379
|
+
runId: args.state.runId,
|
|
2380
|
+
workflowId: args.state.workflowId,
|
|
2381
|
+
startedAt: args.state.startedAt,
|
|
2382
|
+
status: "failed",
|
|
2383
|
+
error: { message }
|
|
2384
|
+
};
|
|
2385
|
+
this.waiters.resolveRunCompletion(result);
|
|
2386
|
+
return result;
|
|
2387
|
+
}
|
|
2190
2388
|
formatNodeLabel(args) {
|
|
2191
2389
|
const tokenName = typeof args.definition?.type === "function" ? args.definition.type.name : "Node";
|
|
2192
2390
|
return args.definition?.name ? `"${args.definition.name}" (${tokenName}:${args.nodeId})` : `${tokenName}:${args.nodeId}`;
|
|
@@ -2361,7 +2559,7 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
2361
2559
|
const expectedInputs = this.topology.expectedInputsByNode.get(nodeId) ?? [];
|
|
2362
2560
|
if (expectedInputs.length !== 1 || expectedInputs[0] !== "in") {
|
|
2363
2561
|
const received = {};
|
|
2364
|
-
for (const input$
|
|
2562
|
+
for (const input$2 of expectedInputs) received[input$2] = this.resolveInput(currentState, nodeId, input$2);
|
|
2365
2563
|
return [{
|
|
2366
2564
|
nodeId,
|
|
2367
2565
|
input: [],
|
|
@@ -2372,12 +2570,12 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
2372
2570
|
}
|
|
2373
2571
|
}];
|
|
2374
2572
|
}
|
|
2375
|
-
const input = expectedInputs[0] ?? "in";
|
|
2376
|
-
const incomingEdge = incomingEdges.find((edge) => edge.input === input);
|
|
2573
|
+
const input$1 = expectedInputs[0] ?? "in";
|
|
2574
|
+
const incomingEdge = incomingEdges.find((edge) => edge.input === input$1);
|
|
2377
2575
|
return [{
|
|
2378
2576
|
nodeId,
|
|
2379
|
-
input: this.resolveInput(currentState, nodeId, input),
|
|
2380
|
-
toInput: input,
|
|
2577
|
+
input: this.resolveInput(currentState, nodeId, input$1),
|
|
2578
|
+
toInput: input$1,
|
|
2381
2579
|
batchId: "batch_1",
|
|
2382
2580
|
from: incomingEdge?.from
|
|
2383
2581
|
}];
|
|
@@ -2393,16 +2591,16 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
2393
2591
|
isNodeSatisfiedByOutputsOnly(currentState, nodeId) {
|
|
2394
2592
|
return this.hasOutputs(currentState, nodeId) && !this.hasCompletedSnapshot(currentState, nodeId);
|
|
2395
2593
|
}
|
|
2396
|
-
isEdgeSatisfied(currentState, nodeId, input) {
|
|
2397
|
-
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.input === input);
|
|
2594
|
+
isEdgeSatisfied(currentState, nodeId, input$1) {
|
|
2595
|
+
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.input === input$1);
|
|
2398
2596
|
if (!incomingEdge) return false;
|
|
2399
2597
|
if (!this.hasOutputPort(currentState, incomingEdge.from.nodeId, incomingEdge.from.output)) return false;
|
|
2400
2598
|
if (this.usesCollect(nodeId)) return true;
|
|
2401
2599
|
if (this.resolveOutputItems(currentState, incomingEdge.from.nodeId, incomingEdge.from.output).length > 0) return true;
|
|
2402
2600
|
return this.shouldContinueAfterEmptyOutputFromSource(incomingEdge.from.nodeId);
|
|
2403
2601
|
}
|
|
2404
|
-
resolveInput(currentState, nodeId, input) {
|
|
2405
|
-
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.input === input);
|
|
2602
|
+
resolveInput(currentState, nodeId, input$1) {
|
|
2603
|
+
const incomingEdge = (this.topology.incomingByNode.get(nodeId) ?? []).find((edge) => edge.input === input$1);
|
|
2406
2604
|
if (!incomingEdge) return [];
|
|
2407
2605
|
return this.resolveOutputItems(currentState, incomingEdge.from.nodeId, incomingEdge.from.output);
|
|
2408
2606
|
}
|
|
@@ -2413,13 +2611,13 @@ var CurrentStateFrontierPlanner = class CurrentStateFrontierPlanner {
|
|
|
2413
2611
|
const snapshot = currentState.nodeSnapshotsByNodeId[nodeId];
|
|
2414
2612
|
return snapshot?.status === "completed" || snapshot?.status === "skipped";
|
|
2415
2613
|
}
|
|
2416
|
-
hasOutputPort(currentState, nodeId, output) {
|
|
2614
|
+
hasOutputPort(currentState, nodeId, output$1) {
|
|
2417
2615
|
const outputs = currentState.outputsByNode[nodeId];
|
|
2418
2616
|
if (!outputs) return false;
|
|
2419
|
-
return Object.prototype.hasOwnProperty.call(outputs, output);
|
|
2617
|
+
return Object.prototype.hasOwnProperty.call(outputs, output$1);
|
|
2420
2618
|
}
|
|
2421
|
-
resolveOutputItems(currentState, nodeId, output) {
|
|
2422
|
-
return currentState.outputsByNode[nodeId]?.[output] ?? [];
|
|
2619
|
+
resolveOutputItems(currentState, nodeId, output$1) {
|
|
2620
|
+
return currentState.outputsByNode[nodeId]?.[output$1] ?? [];
|
|
2423
2621
|
}
|
|
2424
2622
|
usesCollect(nodeId) {
|
|
2425
2623
|
const expectedInputs = this.topology.expectedInputsByNode.get(nodeId) ?? [];
|
|
@@ -3077,6 +3275,67 @@ var EngineExecutionLimitsPolicy = class {
|
|
|
3077
3275
|
}
|
|
3078
3276
|
};
|
|
3079
3277
|
|
|
3278
|
+
//#endregion
|
|
3279
|
+
//#region src/policies/storage/RunTerminalPersistenceCoordinator.ts
|
|
3280
|
+
var RunTerminalPersistenceCoordinator = class {
|
|
3281
|
+
constructor(runRepository, storageEvaluator) {
|
|
3282
|
+
this.runRepository = runRepository;
|
|
3283
|
+
this.storageEvaluator = storageEvaluator;
|
|
3284
|
+
}
|
|
3285
|
+
async maybeDeleteAfterTerminalState(args) {
|
|
3286
|
+
if (await this.storageEvaluator.shouldPersist(args.workflow, args.state.policySnapshot, {
|
|
3287
|
+
runId: args.state.runId,
|
|
3288
|
+
workflowId: args.state.workflowId,
|
|
3289
|
+
workflow: args.workflow,
|
|
3290
|
+
finalStatus: args.finalStatus,
|
|
3291
|
+
startedAt: args.state.startedAt,
|
|
3292
|
+
finishedAt: args.finishedAt
|
|
3293
|
+
})) return;
|
|
3294
|
+
if (!this.runRepository.deleteRun) return;
|
|
3295
|
+
await this.runRepository.deleteRun(args.state.runId);
|
|
3296
|
+
}
|
|
3297
|
+
};
|
|
3298
|
+
|
|
3299
|
+
//#endregion
|
|
3300
|
+
//#region src/policies/WorkflowPolicyErrorServices.ts
|
|
3301
|
+
var WorkflowPolicyErrorServices = class {
|
|
3302
|
+
constructor(nodeResolver) {
|
|
3303
|
+
this.nodeResolver = nodeResolver;
|
|
3304
|
+
}
|
|
3305
|
+
resolveNodeErrorHandler(spec) {
|
|
3306
|
+
if (!spec) return void 0;
|
|
3307
|
+
if (typeof spec === "object" && spec !== null && "handle" in spec && typeof spec.handle === "function") return spec;
|
|
3308
|
+
return this.nodeResolver.resolve(spec);
|
|
3309
|
+
}
|
|
3310
|
+
resolveWorkflowErrorHandler(spec) {
|
|
3311
|
+
if (!spec) return void 0;
|
|
3312
|
+
if (typeof spec === "object" && spec !== null && "onError" in spec && typeof spec.onError === "function") return spec;
|
|
3313
|
+
return this.nodeResolver.resolve(spec);
|
|
3314
|
+
}
|
|
3315
|
+
};
|
|
3316
|
+
|
|
3317
|
+
//#endregion
|
|
3318
|
+
//#region src/policies/storage/WorkflowStoragePolicyEvaluator.ts
|
|
3319
|
+
var WorkflowStoragePolicyEvaluator = class {
|
|
3320
|
+
constructor(nodeResolver) {
|
|
3321
|
+
this.nodeResolver = nodeResolver;
|
|
3322
|
+
}
|
|
3323
|
+
async shouldPersist(workflow, snapshot, args) {
|
|
3324
|
+
const spec = workflow.storagePolicy;
|
|
3325
|
+
if (spec === void 0) return this.modeMatches(snapshot?.storagePolicy ?? "ALL", args);
|
|
3326
|
+
if (typeof spec === "string") return this.modeMatches(spec, args);
|
|
3327
|
+
const resolver = this.nodeResolver.resolve(spec);
|
|
3328
|
+
return Boolean(await resolver.shouldPersist(args));
|
|
3329
|
+
}
|
|
3330
|
+
modeMatches(mode, args) {
|
|
3331
|
+
if (mode === "ALL") return true;
|
|
3332
|
+
if (mode === "NEVER") return false;
|
|
3333
|
+
if (mode === "SUCCESS") return args.finalStatus === "completed";
|
|
3334
|
+
if (mode === "ERROR") return args.finalStatus === "failed";
|
|
3335
|
+
return true;
|
|
3336
|
+
}
|
|
3337
|
+
};
|
|
3338
|
+
|
|
3080
3339
|
//#endregion
|
|
3081
3340
|
//#region src/runStorage/BinaryBodyBufferReader.ts
|
|
3082
3341
|
var BinaryBodyBufferReader = class {
|
|
@@ -3191,11 +3450,11 @@ var InMemoryRunData = class {
|
|
|
3191
3450
|
getOutputs(nodeId) {
|
|
3192
3451
|
return this.byNode.get(nodeId);
|
|
3193
3452
|
}
|
|
3194
|
-
getOutputItems(nodeId, output = "main") {
|
|
3195
|
-
return this.byNode.get(nodeId)?.[output] ?? [];
|
|
3453
|
+
getOutputItems(nodeId, output$1 = "main") {
|
|
3454
|
+
return this.byNode.get(nodeId)?.[output$1] ?? [];
|
|
3196
3455
|
}
|
|
3197
|
-
getOutputItem(nodeId, itemIndex, output = "main") {
|
|
3198
|
-
return this.getOutputItems(nodeId, output)[itemIndex];
|
|
3456
|
+
getOutputItem(nodeId, itemIndex, output$1 = "main") {
|
|
3457
|
+
return this.getOutputItems(nodeId, output$1)[itemIndex];
|
|
3199
3458
|
}
|
|
3200
3459
|
dump() {
|
|
3201
3460
|
const out = {};
|
|
@@ -3225,6 +3484,775 @@ var RunFinishedAtFactory = class {
|
|
|
3225
3484
|
}
|
|
3226
3485
|
};
|
|
3227
3486
|
|
|
3487
|
+
//#endregion
|
|
3488
|
+
//#region src/orchestration/NodeExecutionRequestHandlerService.ts
|
|
3489
|
+
var NodeExecutionRequestHandlerService = class {
|
|
3490
|
+
constructor(workflowExecutionRepository, workflowSnapshotResolver, runDataFactory, runExecutionContextFactory, nodeStatePublisherFactory, nodeActivationRequestComposer, nodeExecutor, continuation, executionLimitsPolicy) {
|
|
3491
|
+
this.workflowExecutionRepository = workflowExecutionRepository;
|
|
3492
|
+
this.workflowSnapshotResolver = workflowSnapshotResolver;
|
|
3493
|
+
this.runDataFactory = runDataFactory;
|
|
3494
|
+
this.runExecutionContextFactory = runExecutionContextFactory;
|
|
3495
|
+
this.nodeStatePublisherFactory = nodeStatePublisherFactory;
|
|
3496
|
+
this.nodeActivationRequestComposer = nodeActivationRequestComposer;
|
|
3497
|
+
this.nodeExecutor = nodeExecutor;
|
|
3498
|
+
this.continuation = continuation;
|
|
3499
|
+
this.executionLimitsPolicy = executionLimitsPolicy;
|
|
3500
|
+
}
|
|
3501
|
+
async handleNodeExecutionRequest(request) {
|
|
3502
|
+
const [state, schedulingState] = await Promise.all([this.workflowExecutionRepository.load(request.runId), this.workflowExecutionRepository.loadSchedulingState(request.runId)]);
|
|
3503
|
+
if (!state) throw new Error(`Unknown runId: ${request.runId}`);
|
|
3504
|
+
if (state.workflowId !== request.workflowId) throw new Error(`workflowId mismatch for run ${request.runId}: ${state.workflowId} vs ${request.workflowId}`);
|
|
3505
|
+
const pendingExecution = schedulingState?.pending;
|
|
3506
|
+
if (state.status !== "pending" || !pendingExecution) return;
|
|
3507
|
+
if (pendingExecution.activationId !== request.activationId || pendingExecution.nodeId !== request.nodeId) return;
|
|
3508
|
+
const workflow = this.resolvePersistedWorkflow(state);
|
|
3509
|
+
if (!workflow) throw new Error(`Unknown workflowId: ${state.workflowId}`);
|
|
3510
|
+
const definition = workflow.nodes.find((node$1) => node$1.id === request.nodeId);
|
|
3511
|
+
if (!definition) throw new Error(`Unknown nodeId: ${request.nodeId}`);
|
|
3512
|
+
if (definition.kind !== "node") throw new Error(`Node ${request.nodeId} is not runnable`);
|
|
3513
|
+
const resolvedParent = request.parent ?? state.parent;
|
|
3514
|
+
const data = this.runDataFactory.create(state.outputsByNode);
|
|
3515
|
+
const limits = this.resolveEngineLimitsFromState(state);
|
|
3516
|
+
const persistedInput = pendingExecution.inputsByPort.in ?? request.input;
|
|
3517
|
+
const base = this.runExecutionContextFactory.create({
|
|
3518
|
+
runId: state.runId,
|
|
3519
|
+
workflowId: state.workflowId,
|
|
3520
|
+
nodeId: request.nodeId,
|
|
3521
|
+
parent: resolvedParent,
|
|
3522
|
+
subworkflowDepth: state.executionOptions?.subworkflowDepth ?? 0,
|
|
3523
|
+
engineMaxNodeActivations: limits.engineMaxNodeActivations,
|
|
3524
|
+
engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
|
|
3525
|
+
data,
|
|
3526
|
+
nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, resolvedParent)
|
|
3527
|
+
});
|
|
3528
|
+
const activationRequest = this.nodeActivationRequestComposer.createSingleFromDefinitionWithActivation({
|
|
3529
|
+
activationId: request.activationId,
|
|
3530
|
+
runId: request.runId,
|
|
3531
|
+
workflowId: request.workflowId,
|
|
3532
|
+
parent: resolvedParent,
|
|
3533
|
+
executionOptions: request.executionOptions ?? state.executionOptions,
|
|
3534
|
+
base,
|
|
3535
|
+
data,
|
|
3536
|
+
definition: {
|
|
3537
|
+
id: definition.id,
|
|
3538
|
+
config: definition.config
|
|
3539
|
+
},
|
|
3540
|
+
batchId: pendingExecution.batchId ?? "batch_1",
|
|
3541
|
+
input: persistedInput
|
|
3542
|
+
});
|
|
3543
|
+
await this.continuation.markNodeRunning({
|
|
3544
|
+
runId: activationRequest.runId,
|
|
3545
|
+
activationId: activationRequest.activationId,
|
|
3546
|
+
nodeId: activationRequest.nodeId,
|
|
3547
|
+
inputsByPort: pendingExecution.inputsByPort
|
|
3548
|
+
});
|
|
3549
|
+
let outputs;
|
|
3550
|
+
try {
|
|
3551
|
+
outputs = await this.nodeExecutor.execute(activationRequest);
|
|
3552
|
+
} catch (error) {
|
|
3553
|
+
await this.resumeAfterExecutionError(activationRequest, this.asError(error));
|
|
3554
|
+
return;
|
|
3555
|
+
}
|
|
3556
|
+
await this.resumeAfterExecutionResult(activationRequest, outputs ?? {});
|
|
3557
|
+
}
|
|
3558
|
+
resolvePersistedWorkflow(state) {
|
|
3559
|
+
return this.workflowSnapshotResolver.resolve({
|
|
3560
|
+
workflowId: state.workflowId,
|
|
3561
|
+
workflowSnapshot: state.workflowSnapshot
|
|
3562
|
+
});
|
|
3563
|
+
}
|
|
3564
|
+
resolveEngineLimitsFromState(state) {
|
|
3565
|
+
const fallback = this.executionLimitsPolicy.createRootExecutionOptions();
|
|
3566
|
+
return {
|
|
3567
|
+
engineMaxNodeActivations: state.executionOptions?.maxNodeActivations ?? fallback.maxNodeActivations,
|
|
3568
|
+
engineMaxSubworkflowDepth: state.executionOptions?.maxSubworkflowDepth ?? fallback.maxSubworkflowDepth
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
async resumeAfterExecutionResult(request, outputs) {
|
|
3572
|
+
try {
|
|
3573
|
+
await this.continuation.resumeFromNodeResult({
|
|
3574
|
+
runId: request.runId,
|
|
3575
|
+
activationId: request.activationId,
|
|
3576
|
+
nodeId: request.nodeId,
|
|
3577
|
+
outputs
|
|
3578
|
+
});
|
|
3579
|
+
} catch (error) {
|
|
3580
|
+
this.rethrowUnlessIgnorableContinuationError(error);
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
async resumeAfterExecutionError(request, error) {
|
|
3584
|
+
try {
|
|
3585
|
+
await this.continuation.resumeFromNodeError({
|
|
3586
|
+
runId: request.runId,
|
|
3587
|
+
activationId: request.activationId,
|
|
3588
|
+
nodeId: request.nodeId,
|
|
3589
|
+
error
|
|
3590
|
+
});
|
|
3591
|
+
} catch (continuationError) {
|
|
3592
|
+
this.rethrowUnlessIgnorableContinuationError(continuationError);
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
asError(error) {
|
|
3596
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
3597
|
+
}
|
|
3598
|
+
rethrowUnlessIgnorableContinuationError(error) {
|
|
3599
|
+
if (this.isIgnorableContinuationError(error)) return;
|
|
3600
|
+
throw this.asError(error);
|
|
3601
|
+
}
|
|
3602
|
+
isIgnorableContinuationError(error) {
|
|
3603
|
+
const message = this.asError(error).message;
|
|
3604
|
+
return message.includes(" is not pending") || message.includes("activationId mismatch") || message.includes("nodeId mismatch");
|
|
3605
|
+
}
|
|
3606
|
+
};
|
|
3607
|
+
|
|
3608
|
+
//#endregion
|
|
3609
|
+
//#region src/planning/RunQueuePlanner.ts
|
|
3610
|
+
var RunQueuePlanner = class {
|
|
3611
|
+
constructor(topology, nodeInstances) {
|
|
3612
|
+
this.topology = topology;
|
|
3613
|
+
this.nodeInstances = nodeInstances;
|
|
3614
|
+
}
|
|
3615
|
+
validateNodeKinds() {
|
|
3616
|
+
for (const [toNodeId, inputs] of this.topology.expectedInputsByNode.entries()) {
|
|
3617
|
+
if (inputs.length <= 1) {
|
|
3618
|
+
const only = inputs[0];
|
|
3619
|
+
if (only && only !== "in") {
|
|
3620
|
+
const inst$1 = this.nodeInstances.get(toNodeId);
|
|
3621
|
+
if (!this.isMultiInputNode(inst$1)) throw new Error(`Node ${toNodeId} only supports input 'in' (got '${only}').`);
|
|
3622
|
+
}
|
|
3623
|
+
continue;
|
|
3624
|
+
}
|
|
3625
|
+
const inst = this.nodeInstances.get(toNodeId);
|
|
3626
|
+
if (!this.isMultiInputNode(inst)) throw new Error(`Node ${toNodeId} has ${inputs.length} inbound edges. Insert a Merge node to combine branches.`);
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
seedFromTrigger(args) {
|
|
3630
|
+
const queue = [];
|
|
3631
|
+
for (const e of this.topology.outgoingByNode.get(args.startNodeId) ?? []) {
|
|
3632
|
+
if (e.output !== "main") continue;
|
|
3633
|
+
this.enqueueEdge(queue, {
|
|
3634
|
+
batchId: args.batchId,
|
|
3635
|
+
to: e.to,
|
|
3636
|
+
from: {
|
|
3637
|
+
nodeId: args.startNodeId,
|
|
3638
|
+
output: "main"
|
|
3639
|
+
},
|
|
3640
|
+
items: args.items
|
|
3641
|
+
});
|
|
3642
|
+
}
|
|
3643
|
+
return queue;
|
|
3644
|
+
}
|
|
3645
|
+
applyOutputs(queue, args) {
|
|
3646
|
+
for (const e of this.topology.outgoingByNode.get(args.fromNodeId) ?? []) {
|
|
3647
|
+
const outItems = args.outputs[e.output] ?? [];
|
|
3648
|
+
this.enqueueEdge(queue, {
|
|
3649
|
+
batchId: args.batchId,
|
|
3650
|
+
to: e.to,
|
|
3651
|
+
from: {
|
|
3652
|
+
nodeId: args.fromNodeId,
|
|
3653
|
+
output: e.output
|
|
3654
|
+
},
|
|
3655
|
+
items: outItems
|
|
3656
|
+
});
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
nextActivation(queue) {
|
|
3660
|
+
const readyCollect = this.resolveReadyCollect(queue);
|
|
3661
|
+
if (readyCollect) return readyCollect;
|
|
3662
|
+
const jobIdx = queue.findIndex((q) => !q.collect);
|
|
3663
|
+
if (jobIdx === -1) {
|
|
3664
|
+
if (queue.length === 0) return null;
|
|
3665
|
+
const sealedCollect = this.resolveSealedCollect(queue);
|
|
3666
|
+
if (sealedCollect) return sealedCollect;
|
|
3667
|
+
const stuck = queue[0];
|
|
3668
|
+
throw new Error(this.describeUnsatisfiedCollect(stuck));
|
|
3669
|
+
}
|
|
3670
|
+
const job = queue.splice(jobIdx, 1)[0];
|
|
3671
|
+
const def = this.topology.defsById.get(job.nodeId);
|
|
3672
|
+
if (!def || def.kind !== "node") return this.nextActivation(queue);
|
|
3673
|
+
return {
|
|
3674
|
+
kind: "single",
|
|
3675
|
+
nodeId: job.nodeId,
|
|
3676
|
+
input: job.input,
|
|
3677
|
+
batchId: job.batchId ?? "batch_1"
|
|
3678
|
+
};
|
|
3679
|
+
}
|
|
3680
|
+
sumItemsByPort(inputsByPort) {
|
|
3681
|
+
let n = 0;
|
|
3682
|
+
for (const v of Object.values(inputsByPort)) n += v?.length ?? 0;
|
|
3683
|
+
return n;
|
|
3684
|
+
}
|
|
3685
|
+
resolveReadyCollect(queue) {
|
|
3686
|
+
for (let i = 0; i < queue.length; i++) {
|
|
3687
|
+
const ready = this.tryDequeueCollect(queue, i);
|
|
3688
|
+
if (ready) return ready;
|
|
3689
|
+
}
|
|
3690
|
+
return null;
|
|
3691
|
+
}
|
|
3692
|
+
resolveSealedCollect(queue) {
|
|
3693
|
+
for (let i = 0; i < queue.length; i++) {
|
|
3694
|
+
const queueEntry = queue[i];
|
|
3695
|
+
if (!queueEntry.collect) continue;
|
|
3696
|
+
const received = queueEntry.collect.received;
|
|
3697
|
+
if (Object.keys(received).length === 0) continue;
|
|
3698
|
+
this.fillMissingCollectInputs(queueEntry);
|
|
3699
|
+
const ready = this.tryDequeueCollect(queue, i);
|
|
3700
|
+
if (ready) return ready;
|
|
3701
|
+
}
|
|
3702
|
+
return null;
|
|
3703
|
+
}
|
|
3704
|
+
tryDequeueCollect(queue, index) {
|
|
3705
|
+
const queueEntry = queue[index];
|
|
3706
|
+
if (!queueEntry.collect) return null;
|
|
3707
|
+
const batchId = queueEntry.batchId ?? "batch_1";
|
|
3708
|
+
const expected = queueEntry.collect.expectedInputs ?? [];
|
|
3709
|
+
const received = queueEntry.collect.received;
|
|
3710
|
+
for (const input$1 of expected) if (!(input$1 in received)) return null;
|
|
3711
|
+
queue.splice(index, 1);
|
|
3712
|
+
return {
|
|
3713
|
+
kind: "multi",
|
|
3714
|
+
nodeId: queueEntry.nodeId,
|
|
3715
|
+
inputsByPort: received,
|
|
3716
|
+
batchId
|
|
3717
|
+
};
|
|
3718
|
+
}
|
|
3719
|
+
fillMissingCollectInputs(queueEntry) {
|
|
3720
|
+
if (!queueEntry.collect) return;
|
|
3721
|
+
const received = queueEntry.collect.received;
|
|
3722
|
+
for (const input$1 of queueEntry.collect.expectedInputs ?? []) if (!(input$1 in received)) received[input$1] = [];
|
|
3723
|
+
}
|
|
3724
|
+
/**
|
|
3725
|
+
* Matches `CurrentStateFrontierPlanner.buildFrontierQueue`: anything that is not exactly one input
|
|
3726
|
+
* port named `in` participates in multi-port collect (Merge after `If` branches, etc.). Routing must
|
|
3727
|
+
* not depend solely on `nodeInstances.get(toNodeId)?.executeMulti`, or a Merge can be enqueued as a
|
|
3728
|
+
* single-input job and `NodeExecutor` will call `execute` on a multi-input-only implementation.
|
|
3729
|
+
*/
|
|
3730
|
+
usesTopologyCollectMerge(toNodeId) {
|
|
3731
|
+
const expectedInputs = this.topology.expectedInputsByNode.get(toNodeId) ?? [];
|
|
3732
|
+
return expectedInputs.length !== 1 || expectedInputs[0] !== "in";
|
|
3733
|
+
}
|
|
3734
|
+
enqueueEdge(queue, args) {
|
|
3735
|
+
const target = this.nodeInstances.get(args.to.nodeId);
|
|
3736
|
+
if (!(this.usesTopologyCollectMerge(args.to.nodeId) || this.isMultiInputNode(target))) {
|
|
3737
|
+
if (args.items.length === 0) {
|
|
3738
|
+
if (this.shouldContinueAfterEmptyOutputFromSource(args.from.nodeId)) {
|
|
3739
|
+
queue.push({
|
|
3740
|
+
nodeId: args.to.nodeId,
|
|
3741
|
+
input: args.items,
|
|
3742
|
+
toInput: args.to.input,
|
|
3743
|
+
batchId: args.batchId,
|
|
3744
|
+
from: args.from
|
|
3745
|
+
});
|
|
3746
|
+
return;
|
|
3747
|
+
}
|
|
3748
|
+
this.propagateEmptyPath(queue, args.to.nodeId, args.batchId);
|
|
3749
|
+
return;
|
|
3750
|
+
}
|
|
3751
|
+
queue.push({
|
|
3752
|
+
nodeId: args.to.nodeId,
|
|
3753
|
+
input: args.items,
|
|
3754
|
+
toInput: args.to.input,
|
|
3755
|
+
batchId: args.batchId,
|
|
3756
|
+
from: args.from
|
|
3757
|
+
});
|
|
3758
|
+
return;
|
|
3759
|
+
}
|
|
3760
|
+
const expected = this.topology.expectedInputsByNode.get(args.to.nodeId) ?? [];
|
|
3761
|
+
let collect = queue.find((q) => q.nodeId === args.to.nodeId && (q.batchId ?? "batch_1") === args.batchId && !!q.collect);
|
|
3762
|
+
if (!collect) {
|
|
3763
|
+
collect = {
|
|
3764
|
+
nodeId: args.to.nodeId,
|
|
3765
|
+
input: [],
|
|
3766
|
+
batchId: args.batchId,
|
|
3767
|
+
collect: {
|
|
3768
|
+
expectedInputs: expected,
|
|
3769
|
+
received: {}
|
|
3770
|
+
}
|
|
3771
|
+
};
|
|
3772
|
+
queue.push(collect);
|
|
3773
|
+
}
|
|
3774
|
+
const received = collect.collect.received;
|
|
3775
|
+
received[args.to.input] = args.items;
|
|
3776
|
+
}
|
|
3777
|
+
shouldContinueAfterEmptyOutputFromSource(fromNodeId) {
|
|
3778
|
+
const def = this.topology.defsById.get(fromNodeId);
|
|
3779
|
+
if (!def) return false;
|
|
3780
|
+
return def.config.continueWhenEmptyOutput === true;
|
|
3781
|
+
}
|
|
3782
|
+
propagateEmptyPath(queue, nodeId, batchId) {
|
|
3783
|
+
for (const edge of this.topology.outgoingByNode.get(nodeId) ?? []) this.enqueueEdge(queue, {
|
|
3784
|
+
batchId,
|
|
3785
|
+
to: edge.to,
|
|
3786
|
+
from: {
|
|
3787
|
+
nodeId,
|
|
3788
|
+
output: edge.output
|
|
3789
|
+
},
|
|
3790
|
+
items: []
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
isMultiInputNode(n) {
|
|
3794
|
+
return typeof n?.executeMulti === "function";
|
|
3795
|
+
}
|
|
3796
|
+
describeUnsatisfiedCollect(queueEntry) {
|
|
3797
|
+
const batchId = queueEntry.batchId ?? "batch_1";
|
|
3798
|
+
const expectedInputs = queueEntry.collect?.expectedInputs ?? [];
|
|
3799
|
+
const receivedInputs = Object.keys(queueEntry.collect?.received ?? {});
|
|
3800
|
+
const missingInputs = expectedInputs.filter((input$1) => !receivedInputs.includes(input$1));
|
|
3801
|
+
const mergeNodeLabel = this.formatNodeLabel(queueEntry.nodeId);
|
|
3802
|
+
const receivedSummary = this.describeReceivedInputs(queueEntry);
|
|
3803
|
+
const missingSummary = this.describeMissingInputs(queueEntry.nodeId, missingInputs);
|
|
3804
|
+
return [
|
|
3805
|
+
`Multi-input collect is stuck at ${mergeNodeLabel} (batchId=${batchId}).`,
|
|
3806
|
+
`Expected inputs: ${this.formatInputList(expectedInputs)}.`,
|
|
3807
|
+
`Received inputs: ${receivedSummary}.`,
|
|
3808
|
+
`Missing inputs: ${missingSummary}.`
|
|
3809
|
+
].join(" ");
|
|
3810
|
+
}
|
|
3811
|
+
describeReceivedInputs(queueEntry) {
|
|
3812
|
+
const received = queueEntry.collect?.received ?? {};
|
|
3813
|
+
const receivedEntries = Object.entries(received);
|
|
3814
|
+
if (receivedEntries.length === 0) return "none";
|
|
3815
|
+
return receivedEntries.map(([input$1, items]) => `${input$1} (${items.length} item${items.length === 1 ? "" : "s"})`).join(", ");
|
|
3816
|
+
}
|
|
3817
|
+
describeMissingInputs(nodeId, missingInputs) {
|
|
3818
|
+
if (missingInputs.length === 0) return "none";
|
|
3819
|
+
return missingInputs.map((input$1) => {
|
|
3820
|
+
const sources = this.findSources(nodeId, input$1);
|
|
3821
|
+
if (sources.length === 0) return input$1;
|
|
3822
|
+
return `${input$1} from ${sources.join(" or ")}`;
|
|
3823
|
+
}).join(", ");
|
|
3824
|
+
}
|
|
3825
|
+
findSources(nodeId, input$1) {
|
|
3826
|
+
const matches = [];
|
|
3827
|
+
for (const [sourceNodeId, edges] of this.topology.outgoingByNode.entries()) for (const edge of edges) if (edge.to.nodeId === nodeId && edge.to.input === input$1) matches.push(this.formatNodeLabel(sourceNodeId));
|
|
3828
|
+
return matches;
|
|
3829
|
+
}
|
|
3830
|
+
formatInputList(inputs) {
|
|
3831
|
+
return inputs.length > 0 ? `[${inputs.join(", ")}]` : "[]";
|
|
3832
|
+
}
|
|
3833
|
+
formatNodeLabel(nodeId) {
|
|
3834
|
+
const definition = this.topology.defsById.get(nodeId);
|
|
3835
|
+
const instance = this.nodeInstances.get(nodeId);
|
|
3836
|
+
const typeName = definition?.type && typeof definition.type === "function" ? definition.type.name : instance && typeof instance === "object" && "constructor" in instance ? instance.constructor.name ?? "Node" : "Node";
|
|
3837
|
+
return definition?.name ? `"${definition.name}" (${typeName}:${nodeId})` : `${typeName}:${nodeId}`;
|
|
3838
|
+
}
|
|
3839
|
+
};
|
|
3840
|
+
|
|
3841
|
+
//#endregion
|
|
3842
|
+
//#region src/planning/EngineWorkflowPlanningFactory.ts
|
|
3843
|
+
var EngineWorkflowPlanningFactory = class {
|
|
3844
|
+
constructor(workflowNodeInstanceFactory) {
|
|
3845
|
+
this.workflowNodeInstanceFactory = workflowNodeInstanceFactory;
|
|
3846
|
+
}
|
|
3847
|
+
create(workflow) {
|
|
3848
|
+
this.validateAcyclic(workflow);
|
|
3849
|
+
const topology = WorkflowTopology.fromWorkflow(workflow);
|
|
3850
|
+
const planner = new RunQueuePlanner(topology, this.workflowNodeInstanceFactory.createNodes(workflow));
|
|
3851
|
+
planner.validateNodeKinds();
|
|
3852
|
+
return {
|
|
3853
|
+
topology,
|
|
3854
|
+
planner
|
|
3855
|
+
};
|
|
3856
|
+
}
|
|
3857
|
+
validateAcyclic(workflow) {
|
|
3858
|
+
const classifier = WorkflowExecutableNodeClassifierFactory.create(workflow);
|
|
3859
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
3860
|
+
const visitState = /* @__PURE__ */ new Map();
|
|
3861
|
+
for (const node$1 of workflow.nodes) if (classifier.isExecutableNodeId(node$1.id)) visitState.set(node$1.id, "unvisited");
|
|
3862
|
+
for (const edge of workflow.edges) {
|
|
3863
|
+
if (!classifier.isExecutableNodeId(edge.from.nodeId) || !classifier.isExecutableNodeId(edge.to.nodeId)) continue;
|
|
3864
|
+
const destinations = outgoing.get(edge.from.nodeId) ?? [];
|
|
3865
|
+
destinations.push(edge.to.nodeId);
|
|
3866
|
+
outgoing.set(edge.from.nodeId, destinations);
|
|
3867
|
+
}
|
|
3868
|
+
for (const node$1 of workflow.nodes) if (classifier.isExecutableNodeId(node$1.id) && visitState.get(node$1.id) === "unvisited") this.depthFirstSearch(node$1.id, outgoing, visitState);
|
|
3869
|
+
}
|
|
3870
|
+
depthFirstSearch(nodeId, outgoing, visitState) {
|
|
3871
|
+
visitState.set(nodeId, "visiting");
|
|
3872
|
+
for (const toNodeId of outgoing.get(nodeId) ?? []) {
|
|
3873
|
+
const state = visitState.get(toNodeId);
|
|
3874
|
+
if (state === "visiting") throw new Error(`Workflow graph contains a directed cycle (edge ${nodeId} -> ${toNodeId}).`);
|
|
3875
|
+
if (state === "unvisited") this.depthFirstSearch(toNodeId, outgoing, visitState);
|
|
3876
|
+
}
|
|
3877
|
+
visitState.set(nodeId, "done");
|
|
3878
|
+
}
|
|
3879
|
+
};
|
|
3880
|
+
|
|
3881
|
+
//#endregion
|
|
3882
|
+
//#region src/orchestration/TriggerRuntimeService.ts
|
|
3883
|
+
var TriggerRuntimeService = class {
|
|
3884
|
+
credentialResolverFactory;
|
|
3885
|
+
triggerCleanupHandlesByKey = /* @__PURE__ */ new Map();
|
|
3886
|
+
constructor(workflowRepository, workflowActivationPolicy, runIdFactory, runDataFactory, executionContextFactory, credentialResolverFactory, nodeExecutionStatePublisherFactory, nodeResolver, triggerSetupStateRepository, emitHandler, executionLimitsPolicy, diagnostics) {
|
|
3887
|
+
this.workflowRepository = workflowRepository;
|
|
3888
|
+
this.workflowActivationPolicy = workflowActivationPolicy;
|
|
3889
|
+
this.runIdFactory = runIdFactory;
|
|
3890
|
+
this.runDataFactory = runDataFactory;
|
|
3891
|
+
this.executionContextFactory = executionContextFactory;
|
|
3892
|
+
this.nodeExecutionStatePublisherFactory = nodeExecutionStatePublisherFactory;
|
|
3893
|
+
this.nodeResolver = nodeResolver;
|
|
3894
|
+
this.triggerSetupStateRepository = triggerSetupStateRepository;
|
|
3895
|
+
this.emitHandler = emitHandler;
|
|
3896
|
+
this.executionLimitsPolicy = executionLimitsPolicy;
|
|
3897
|
+
this.diagnostics = diagnostics;
|
|
3898
|
+
this.credentialResolverFactory = credentialResolverFactory;
|
|
3899
|
+
}
|
|
3900
|
+
async startTriggers() {
|
|
3901
|
+
for (const wf of this.workflowRepository.list()) {
|
|
3902
|
+
if (!this.workflowActivationPolicy.isActive(wf.id)) {
|
|
3903
|
+
const summaries = this.formatTriggerSummaries(wf);
|
|
3904
|
+
if (summaries.length > 0) this.logInfo(`Workflow "${wf.name}" (${wf.id}) is inactive; skipping trigger setup — ${summaries.join("; ")}.`);
|
|
3905
|
+
continue;
|
|
3906
|
+
}
|
|
3907
|
+
await this.startTriggersForWorkflow(wf);
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
async syncWorkflowTriggersForActivation(workflowId) {
|
|
3911
|
+
const wf = this.workflowRepository.get(workflowId);
|
|
3912
|
+
if (!wf) return;
|
|
3913
|
+
const summaries = this.formatTriggerSummaries(wf);
|
|
3914
|
+
if (summaries.length > 0) this.logInfo(`Workflow "${wf.name}" (${wf.id}): stopping triggers — ${summaries.join("; ")}.`);
|
|
3915
|
+
await this.stopTriggersForWorkflow(wf);
|
|
3916
|
+
if (this.workflowActivationPolicy.isActive(workflowId)) {
|
|
3917
|
+
if (summaries.length > 0) this.logInfo(`Workflow "${wf.name}" (${wf.id}): activation on; starting triggers — ${summaries.join("; ")}.`);
|
|
3918
|
+
await this.startTriggersForWorkflow(wf);
|
|
3919
|
+
} else this.logInfo(`Workflow "${wf.name}" (${wf.id}): activation off; triggers not started.`);
|
|
3920
|
+
}
|
|
3921
|
+
async stop() {
|
|
3922
|
+
for (const workflow of this.workflowRepository.list()) await this.stopTriggersForWorkflow(workflow);
|
|
3923
|
+
}
|
|
3924
|
+
async createTriggerTestItems(args) {
|
|
3925
|
+
const definition = args.workflow.nodes.find((node$2) => node$2.id === args.nodeId);
|
|
3926
|
+
if (!definition) throw new Error(`Unknown trigger nodeId: ${args.nodeId}`);
|
|
3927
|
+
if (definition.kind !== "trigger") throw new Error(`Node ${args.nodeId} is not a trigger`);
|
|
3928
|
+
const node$1 = this.nodeResolver.resolve(definition.type);
|
|
3929
|
+
if (!this.isTestableTriggerNode(node$1)) return;
|
|
3930
|
+
const data = this.runDataFactory.create();
|
|
3931
|
+
const runId = this.runIdFactory.makeRunId();
|
|
3932
|
+
const trigger = {
|
|
3933
|
+
workflowId: args.workflow.id,
|
|
3934
|
+
nodeId: definition.id
|
|
3935
|
+
};
|
|
3936
|
+
const previousState = await this.triggerSetupStateRepository.load(trigger);
|
|
3937
|
+
return await node$1.getTestItems({
|
|
3938
|
+
...this.createExecutionContext({
|
|
3939
|
+
runId,
|
|
3940
|
+
workflowId: args.workflow.id,
|
|
3941
|
+
nodeId: definition.id,
|
|
3942
|
+
data
|
|
3943
|
+
}),
|
|
3944
|
+
trigger,
|
|
3945
|
+
nodeId: definition.id,
|
|
3946
|
+
config: definition.config,
|
|
3947
|
+
previousState: previousState?.state
|
|
3948
|
+
});
|
|
3949
|
+
}
|
|
3950
|
+
async startTriggersForWorkflow(wf) {
|
|
3951
|
+
for (const def of wf.nodes) {
|
|
3952
|
+
if (def.kind !== "trigger") continue;
|
|
3953
|
+
const node$1 = this.nodeResolver.resolve(def.type);
|
|
3954
|
+
const data = this.runDataFactory.create();
|
|
3955
|
+
const triggerRunId = this.runIdFactory.makeRunId();
|
|
3956
|
+
const trigger = {
|
|
3957
|
+
workflowId: wf.id,
|
|
3958
|
+
nodeId: def.id
|
|
3959
|
+
};
|
|
3960
|
+
await this.stopTrigger(trigger);
|
|
3961
|
+
const previousState = await this.triggerSetupStateRepository.load(trigger);
|
|
3962
|
+
let nextState;
|
|
3963
|
+
try {
|
|
3964
|
+
nextState = await node$1.setup({
|
|
3965
|
+
...this.createExecutionContext({
|
|
3966
|
+
runId: triggerRunId,
|
|
3967
|
+
workflowId: wf.id,
|
|
3968
|
+
nodeId: def.id,
|
|
3969
|
+
data
|
|
3970
|
+
}),
|
|
3971
|
+
trigger,
|
|
3972
|
+
config: def.config,
|
|
3973
|
+
previousState: previousState?.state,
|
|
3974
|
+
registerCleanup: (cleanup) => {
|
|
3975
|
+
this.registerTriggerCleanupHandle(trigger, cleanup);
|
|
3976
|
+
},
|
|
3977
|
+
emit: async (items) => {
|
|
3978
|
+
await this.emitHandler.emit(wf, def.id, items);
|
|
3979
|
+
}
|
|
3980
|
+
});
|
|
3981
|
+
} catch (triggerError) {
|
|
3982
|
+
await this.stopTrigger(trigger);
|
|
3983
|
+
const message = triggerError instanceof Error ? triggerError.message : String(triggerError);
|
|
3984
|
+
this.logWarn(`Skipping trigger setup for workflow ${wf.id} node ${def.id}: ${message}`);
|
|
3985
|
+
continue;
|
|
3986
|
+
}
|
|
3987
|
+
if (nextState === void 0) await this.triggerSetupStateRepository.delete(trigger);
|
|
3988
|
+
else await this.triggerSetupStateRepository.save({
|
|
3989
|
+
trigger,
|
|
3990
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3991
|
+
state: nextState
|
|
3992
|
+
});
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
async stopTriggersForWorkflow(workflow) {
|
|
3996
|
+
for (const node$1 of workflow.nodes) {
|
|
3997
|
+
if (node$1.kind !== "trigger") continue;
|
|
3998
|
+
await this.stopTrigger({
|
|
3999
|
+
workflowId: workflow.id,
|
|
4000
|
+
nodeId: node$1.id
|
|
4001
|
+
});
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
createExecutionContext(args) {
|
|
4005
|
+
const nodeState = this.nodeExecutionStatePublisherFactory.create(args.runId, args.workflowId, void 0);
|
|
4006
|
+
const rootLimits = this.executionLimitsPolicy.createRootExecutionOptions();
|
|
4007
|
+
return this.executionContextFactory.create({
|
|
4008
|
+
runId: args.runId,
|
|
4009
|
+
workflowId: args.workflowId,
|
|
4010
|
+
parent: void 0,
|
|
4011
|
+
subworkflowDepth: rootLimits.subworkflowDepth ?? 0,
|
|
4012
|
+
engineMaxNodeActivations: rootLimits.maxNodeActivations,
|
|
4013
|
+
engineMaxSubworkflowDepth: rootLimits.maxSubworkflowDepth,
|
|
4014
|
+
data: args.data,
|
|
4015
|
+
nodeState,
|
|
4016
|
+
getCredential: this.credentialResolverFactory.create(args.workflowId, args.nodeId)
|
|
4017
|
+
});
|
|
4018
|
+
}
|
|
4019
|
+
registerTriggerCleanupHandle(trigger, cleanup) {
|
|
4020
|
+
const key = this.toTriggerKey(trigger);
|
|
4021
|
+
const cleanups = this.triggerCleanupHandlesByKey.get(key) ?? [];
|
|
4022
|
+
cleanups.push(cleanup);
|
|
4023
|
+
this.triggerCleanupHandlesByKey.set(key, cleanups);
|
|
4024
|
+
}
|
|
4025
|
+
async stopTrigger(trigger) {
|
|
4026
|
+
const key = this.toTriggerKey(trigger);
|
|
4027
|
+
const cleanups = this.triggerCleanupHandlesByKey.get(key) ?? [];
|
|
4028
|
+
this.triggerCleanupHandlesByKey.delete(key);
|
|
4029
|
+
for (const cleanup of [...cleanups].reverse()) await cleanup.stop();
|
|
4030
|
+
}
|
|
4031
|
+
toTriggerKey(trigger) {
|
|
4032
|
+
return `${trigger.workflowId}:${trigger.nodeId}`;
|
|
4033
|
+
}
|
|
4034
|
+
formatTriggerSummaries(wf) {
|
|
4035
|
+
const out = [];
|
|
4036
|
+
for (const def of wf.nodes) {
|
|
4037
|
+
if (def.kind !== "trigger") continue;
|
|
4038
|
+
out.push(this.describeTriggerNode(def));
|
|
4039
|
+
}
|
|
4040
|
+
return out;
|
|
4041
|
+
}
|
|
4042
|
+
describeTriggerNode(def) {
|
|
4043
|
+
const label = def.name !== void 0 && def.name.trim().length > 0 ? def.name.trim() : String(def.id);
|
|
4044
|
+
const cfg = def.config;
|
|
4045
|
+
if (typeof cfg.endpointKey === "string" && cfg.endpointKey.trim().length > 0) return `${label} (webhook "${cfg.endpointKey.trim()}")`;
|
|
4046
|
+
return label;
|
|
4047
|
+
}
|
|
4048
|
+
logInfo(message) {
|
|
4049
|
+
if (this.diagnostics) this.diagnostics.info(message);
|
|
4050
|
+
}
|
|
4051
|
+
logWarn(message) {
|
|
4052
|
+
if (this.diagnostics) this.diagnostics.warn(message);
|
|
4053
|
+
else console.warn(`[engine] ${message}`);
|
|
4054
|
+
}
|
|
4055
|
+
isTestableTriggerNode(node$1) {
|
|
4056
|
+
return typeof node$1.getTestItems === "function";
|
|
4057
|
+
}
|
|
4058
|
+
};
|
|
4059
|
+
|
|
4060
|
+
//#endregion
|
|
4061
|
+
//#region src/orchestration/EngineWaiters.ts
|
|
4062
|
+
var EngineWaiters = class {
|
|
4063
|
+
completionWaiters = /* @__PURE__ */ new Map();
|
|
4064
|
+
webhookResponseWaiters = /* @__PURE__ */ new Map();
|
|
4065
|
+
waitForCompletion(runId) {
|
|
4066
|
+
return new Promise((resolve) => {
|
|
4067
|
+
const list = this.completionWaiters.get(runId) ?? [];
|
|
4068
|
+
list.push(resolve);
|
|
4069
|
+
this.completionWaiters.set(runId, list);
|
|
4070
|
+
});
|
|
4071
|
+
}
|
|
4072
|
+
waitForWebhookResponse(runId) {
|
|
4073
|
+
return new Promise((resolve) => {
|
|
4074
|
+
const list = this.webhookResponseWaiters.get(runId) ?? [];
|
|
4075
|
+
list.push(resolve);
|
|
4076
|
+
this.webhookResponseWaiters.set(runId, list);
|
|
4077
|
+
});
|
|
4078
|
+
}
|
|
4079
|
+
resolveRunCompletion(result) {
|
|
4080
|
+
if (result.status !== "completed" && result.status !== "failed") return;
|
|
4081
|
+
const list = this.completionWaiters.get(result.runId);
|
|
4082
|
+
if (!list || list.length === 0) return;
|
|
4083
|
+
this.completionWaiters.delete(result.runId);
|
|
4084
|
+
for (const r of list) r(result);
|
|
4085
|
+
}
|
|
4086
|
+
resolveWebhookResponse(result) {
|
|
4087
|
+
const list = this.webhookResponseWaiters.get(result.runId);
|
|
4088
|
+
if (!list || list.length === 0) return;
|
|
4089
|
+
this.webhookResponseWaiters.delete(result.runId);
|
|
4090
|
+
for (const resolve of list) resolve(result);
|
|
4091
|
+
}
|
|
4092
|
+
};
|
|
4093
|
+
|
|
4094
|
+
//#endregion
|
|
4095
|
+
//#region src/orchestration/Engine.ts
|
|
4096
|
+
/**
|
|
4097
|
+
* Runtime facade for orchestration, continuation, triggers, and webhook routing.
|
|
4098
|
+
* Prefer {@link import("../intents/RunIntentService").RunIntentService} for host/HTTP invocation boundaries.
|
|
4099
|
+
* The class token is exported from `@codemation/core/bootstrap` (not the main `@codemation/core` barrel).
|
|
4100
|
+
*/
|
|
4101
|
+
var Engine = class {
|
|
4102
|
+
constructor(deps) {
|
|
4103
|
+
this.deps = deps;
|
|
4104
|
+
}
|
|
4105
|
+
loadWorkflows(workflows) {
|
|
4106
|
+
this.deps.tokenRegistry.registerFromWorkflows?.(workflows);
|
|
4107
|
+
this.deps.liveWorkflowRepository.setWorkflows(workflows);
|
|
4108
|
+
this.deps.webhookTriggerMatcher.onEngineWorkflowsLoaded?.();
|
|
4109
|
+
}
|
|
4110
|
+
getTokenRegistry() {
|
|
4111
|
+
return this.deps.tokenRegistry;
|
|
4112
|
+
}
|
|
4113
|
+
resolveWorkflowSnapshot(args) {
|
|
4114
|
+
return this.deps.workflowSnapshotResolver.resolve(args);
|
|
4115
|
+
}
|
|
4116
|
+
async startTriggers() {
|
|
4117
|
+
return await this.deps.triggerRuntime.startTriggers();
|
|
4118
|
+
}
|
|
4119
|
+
async syncWorkflowTriggersForActivation(workflowId) {
|
|
4120
|
+
await this.deps.triggerRuntime.syncWorkflowTriggersForActivation(workflowId);
|
|
4121
|
+
this.deps.webhookTriggerMatcher.reloadWebhookRoutes?.();
|
|
4122
|
+
}
|
|
4123
|
+
async start(workflows) {
|
|
4124
|
+
await this.stop();
|
|
4125
|
+
this.loadWorkflows(workflows);
|
|
4126
|
+
await this.startTriggers();
|
|
4127
|
+
}
|
|
4128
|
+
async stop() {
|
|
4129
|
+
await this.deps.triggerRuntime.stop();
|
|
4130
|
+
this.deps.webhookTriggerMatcher.onEngineStopped?.();
|
|
4131
|
+
}
|
|
4132
|
+
resolveWebhookTrigger(args) {
|
|
4133
|
+
const entry = this.deps.webhookTriggerMatcher.lookup(args.endpointPath);
|
|
4134
|
+
if (!entry) return { status: "notFound" };
|
|
4135
|
+
if (!entry.methods.includes(args.method)) return {
|
|
4136
|
+
status: "methodNotAllowed",
|
|
4137
|
+
match: entry
|
|
4138
|
+
};
|
|
4139
|
+
return {
|
|
4140
|
+
status: "ok",
|
|
4141
|
+
match: entry
|
|
4142
|
+
};
|
|
4143
|
+
}
|
|
4144
|
+
async createTriggerTestItems(args) {
|
|
4145
|
+
return await this.deps.triggerRuntime.createTriggerTestItems(args);
|
|
4146
|
+
}
|
|
4147
|
+
async runWorkflow(wf, startAt, items, parent, executionOptions, persistedStateOverrides) {
|
|
4148
|
+
return await this.deps.runStartService.runWorkflow(wf, startAt, items, parent, executionOptions, persistedStateOverrides);
|
|
4149
|
+
}
|
|
4150
|
+
async runWorkflowFromState(request) {
|
|
4151
|
+
return await this.deps.runStartService.runWorkflowFromState(request);
|
|
4152
|
+
}
|
|
4153
|
+
async markNodeRunning(args) {
|
|
4154
|
+
return await this.deps.runContinuationService.markNodeRunning(args);
|
|
4155
|
+
}
|
|
4156
|
+
async resumeFromNodeResult(args) {
|
|
4157
|
+
return await this.deps.runContinuationService.resumeFromNodeResult(args);
|
|
4158
|
+
}
|
|
4159
|
+
async resumeFromNodeError(args) {
|
|
4160
|
+
return await this.deps.runContinuationService.resumeFromNodeError(args);
|
|
4161
|
+
}
|
|
4162
|
+
async resumeFromStepResult(args) {
|
|
4163
|
+
return await this.deps.runContinuationService.resumeFromStepResult(args);
|
|
4164
|
+
}
|
|
4165
|
+
async resumeFromStepError(args) {
|
|
4166
|
+
return await this.deps.runContinuationService.resumeFromStepError(args);
|
|
4167
|
+
}
|
|
4168
|
+
async waitForCompletion(runId) {
|
|
4169
|
+
return await this.deps.runContinuationService.waitForCompletion(runId);
|
|
4170
|
+
}
|
|
4171
|
+
async waitForWebhookResponse(runId) {
|
|
4172
|
+
return await this.deps.runContinuationService.waitForWebhookResponse(runId);
|
|
4173
|
+
}
|
|
4174
|
+
async handleNodeExecutionRequest(request) {
|
|
4175
|
+
await this.deps.nodeExecutionRequestHandler.handleNodeExecutionRequest(request);
|
|
4176
|
+
}
|
|
4177
|
+
};
|
|
4178
|
+
|
|
4179
|
+
//#endregion
|
|
4180
|
+
//#region src/runtime/EngineFactory.ts
|
|
4181
|
+
/**
|
|
4182
|
+
* Composes the {@link Engine} graph from {@link EngineCompositionDeps}. Production wiring usually goes through
|
|
4183
|
+
* {@link import("../bootstrap/runtime/EngineRuntimeRegistrar").EngineRuntimeRegistrar}; this factory remains for tests and custom composition.
|
|
4184
|
+
* Exported from `@codemation/core/bootstrap` (not the main `@codemation/core` barrel).
|
|
4185
|
+
*/
|
|
4186
|
+
var EngineFactory = class {
|
|
4187
|
+
create(deps) {
|
|
4188
|
+
const waiters = new EngineWaiters();
|
|
4189
|
+
const credentialResolverFactory = new CredentialResolverFactory(deps.credentialSessions);
|
|
4190
|
+
const nodeEventPublisher = new NodeEventPublisher(deps.eventBus);
|
|
4191
|
+
const nodeStatePublisherFactory = new NodeRunStateWriterFactory(deps.workflowExecutionRepository, nodeEventPublisher);
|
|
4192
|
+
const planningFactory = new EngineWorkflowPlanningFactory(deps.workflowNodeInstanceFactory);
|
|
4193
|
+
const executionLimitsPolicy = deps.executionLimitsPolicy ?? new EngineExecutionLimitsPolicy();
|
|
4194
|
+
const workflowSnapshotCodec = deps.workflowSnapshotCodec ?? new WorkflowSnapshotCodec(deps.tokenRegistry);
|
|
4195
|
+
const missingRuntimeFallbacks = deps.missingRuntimeFallbacks ?? new MissingRuntimeFallbacks();
|
|
4196
|
+
const workflowSnapshotResolver = new WorkflowSnapshotResolver(deps.workflowRepository, deps.tokenRegistry, workflowSnapshotCodec, missingRuntimeFallbacks);
|
|
4197
|
+
const semantics = new RunStateSemantics(new MissingRuntimeExecutionMarker());
|
|
4198
|
+
const nodeActivationRequestInputPreparer = new NodeActivationRequestInputPreparer(deps.workflowNodeInstanceFactory);
|
|
4199
|
+
const activationEnqueueService = new ActivationEnqueueService(deps.activationScheduler, deps.workflowExecutionRepository, nodeEventPublisher, nodeActivationRequestInputPreparer);
|
|
4200
|
+
const runExecutionContextFactory = new WorkflowRunExecutionContextFactory(deps.executionContextFactory, credentialResolverFactory);
|
|
4201
|
+
const nodeActivationRequestComposer = new NodeActivationRequestComposer(deps.activationIdFactory, credentialResolverFactory);
|
|
4202
|
+
const persistedRunStateTerminalBuilder = new PersistedRunStateTerminalBuilder();
|
|
4203
|
+
const storagePolicyEvaluator = new WorkflowStoragePolicyEvaluator(deps.nodeResolver);
|
|
4204
|
+
const terminalPersistence = new RunTerminalPersistenceCoordinator(deps.workflowExecutionRepository, storagePolicyEvaluator);
|
|
4205
|
+
const policyErrorServices = new WorkflowPolicyErrorServices(deps.nodeResolver);
|
|
4206
|
+
const runStartService = new RunStartService(deps.runIdFactory, deps.workflowExecutionRepository, deps.runDataFactory, workflowSnapshotCodec, planningFactory, nodeStatePublisherFactory, runExecutionContextFactory, nodeActivationRequestComposer, activationEnqueueService, semantics, waiters, deps.workflowPolicyRuntimeDefaults, executionLimitsPolicy);
|
|
4207
|
+
const runContinuationService = new RunContinuationService(deps.activationIdFactory, deps.workflowExecutionRepository, deps.runDataFactory, runExecutionContextFactory, workflowSnapshotResolver, planningFactory, nodeStatePublisherFactory, credentialResolverFactory, nodeActivationRequestComposer, persistedRunStateTerminalBuilder, activationEnqueueService, nodeEventPublisher, semantics, waiters, policyErrorServices, terminalPersistence, executionLimitsPolicy);
|
|
4208
|
+
const nodeExecutionRequestHandler = new NodeExecutionRequestHandlerService(deps.workflowExecutionRepository, workflowSnapshotResolver, deps.runDataFactory, runExecutionContextFactory, nodeStatePublisherFactory, nodeActivationRequestComposer, deps.nodeExecutor, runContinuationService, executionLimitsPolicy);
|
|
4209
|
+
const triggerRuntime = new TriggerRuntimeService(deps.workflowRepository, deps.workflowActivationPolicy, deps.runIdFactory, deps.runDataFactory, deps.executionContextFactory, credentialResolverFactory, nodeStatePublisherFactory, deps.nodeResolver, deps.triggerSetupStateRepository, { emit: async (workflow, triggerNodeId, items) => {
|
|
4210
|
+
await runStartService.runWorkflow(workflow, triggerNodeId, items, void 0);
|
|
4211
|
+
} }, executionLimitsPolicy, deps.triggerRuntimeDiagnostics);
|
|
4212
|
+
const engine = new Engine({
|
|
4213
|
+
liveWorkflowRepository: deps.liveWorkflowRepository,
|
|
4214
|
+
tokenRegistry: deps.tokenRegistry,
|
|
4215
|
+
webhookTriggerMatcher: deps.webhookTriggerMatcher,
|
|
4216
|
+
workflowSnapshotResolver,
|
|
4217
|
+
triggerRuntime,
|
|
4218
|
+
runStartService,
|
|
4219
|
+
runContinuationService,
|
|
4220
|
+
nodeExecutionRequestHandler
|
|
4221
|
+
});
|
|
4222
|
+
deps.activationScheduler.setContinuation?.(engine);
|
|
4223
|
+
return engine;
|
|
4224
|
+
}
|
|
4225
|
+
};
|
|
4226
|
+
|
|
4227
|
+
//#endregion
|
|
4228
|
+
//#region src/runtime/EngineWorkflowRunnerService.ts
|
|
4229
|
+
var EngineWorkflowRunnerService = class {
|
|
4230
|
+
constructor(engine, workflowRepository) {
|
|
4231
|
+
this.engine = engine;
|
|
4232
|
+
this.workflowRepository = workflowRepository;
|
|
4233
|
+
}
|
|
4234
|
+
async runById(args) {
|
|
4235
|
+
const { workflowId, startAt, items, parent } = args;
|
|
4236
|
+
const wf = this.workflowRepository.get(workflowId);
|
|
4237
|
+
if (!wf) throw new Error(`Unknown workflowId: ${workflowId}`);
|
|
4238
|
+
const startNodeId = startAt ?? this.findDefaultStartNodeId(wf);
|
|
4239
|
+
const scheduled = await this.engine.runWorkflow(wf, startNodeId, items, parent);
|
|
4240
|
+
if (scheduled.status !== "pending") return scheduled;
|
|
4241
|
+
return await this.engine.waitForCompletion(scheduled.runId);
|
|
4242
|
+
}
|
|
4243
|
+
findDefaultStartNodeId(wf) {
|
|
4244
|
+
return WorkflowExecutableNodeClassifierFactory.create(wf).findDefaultExecutableStartNodeId(wf);
|
|
4245
|
+
}
|
|
4246
|
+
};
|
|
4247
|
+
|
|
4248
|
+
//#endregion
|
|
4249
|
+
//#region src/runtime/EngineWorkflowRunnerServiceFactory.ts
|
|
4250
|
+
var EngineWorkflowRunnerServiceFactory = class {
|
|
4251
|
+
create(engine, workflowRepository) {
|
|
4252
|
+
return new EngineWorkflowRunnerService(engine, workflowRepository);
|
|
4253
|
+
}
|
|
4254
|
+
};
|
|
4255
|
+
|
|
3228
4256
|
//#endregion
|
|
3229
4257
|
//#region src/runtime/InMemoryLiveWorkflowRepository.ts
|
|
3230
4258
|
var InMemoryLiveWorkflowRepository = class {
|
|
@@ -3369,5 +4397,107 @@ var RunIntentService = class {
|
|
|
3369
4397
|
};
|
|
3370
4398
|
|
|
3371
4399
|
//#endregion
|
|
3372
|
-
|
|
3373
|
-
|
|
4400
|
+
//#region src/runtime/RunIntentServiceFactory.ts
|
|
4401
|
+
var RunIntentServiceFactory = class {
|
|
4402
|
+
create(engine, workflowRepository) {
|
|
4403
|
+
return new RunIntentService(engine, workflowRepository);
|
|
4404
|
+
}
|
|
4405
|
+
};
|
|
4406
|
+
|
|
4407
|
+
//#endregion
|
|
4408
|
+
//#region src/runtime/WorkflowRepositoryWebhookTriggerMatcher.ts
|
|
4409
|
+
/**
|
|
4410
|
+
* Resolves webhook HTTP routes from the live workflow repository (no trigger setup / registration).
|
|
4411
|
+
* Maintains an in-memory index keyed by user-defined endpoint path for O(1) lookups after reload.
|
|
4412
|
+
*/
|
|
4413
|
+
var WorkflowRepositoryWebhookTriggerMatcher = class {
|
|
4414
|
+
routeByPath = /* @__PURE__ */ new Map();
|
|
4415
|
+
engineRoutesActive = false;
|
|
4416
|
+
constructor(workflowRepository, workflowActivationPolicy, diagnostics) {
|
|
4417
|
+
this.workflowRepository = workflowRepository;
|
|
4418
|
+
this.workflowActivationPolicy = workflowActivationPolicy;
|
|
4419
|
+
this.diagnostics = diagnostics;
|
|
4420
|
+
}
|
|
4421
|
+
onEngineWorkflowsLoaded() {
|
|
4422
|
+
this.engineRoutesActive = true;
|
|
4423
|
+
this.rebuildRouteIndex();
|
|
4424
|
+
}
|
|
4425
|
+
onEngineStopped() {
|
|
4426
|
+
this.engineRoutesActive = false;
|
|
4427
|
+
this.routeByPath.clear();
|
|
4428
|
+
}
|
|
4429
|
+
reloadWebhookRoutes() {
|
|
4430
|
+
if (!this.engineRoutesActive) return;
|
|
4431
|
+
this.rebuildRouteIndex();
|
|
4432
|
+
}
|
|
4433
|
+
lookup(endpointPath) {
|
|
4434
|
+
if (!this.engineRoutesActive) return;
|
|
4435
|
+
const normalized = this.normalizeEndpointPath(endpointPath);
|
|
4436
|
+
return this.routeByPath.get(normalized);
|
|
4437
|
+
}
|
|
4438
|
+
match(args) {
|
|
4439
|
+
const entry = this.lookup(args.endpointPath);
|
|
4440
|
+
if (!entry) return;
|
|
4441
|
+
return entry.methods.includes(args.method) ? entry : void 0;
|
|
4442
|
+
}
|
|
4443
|
+
rebuildRouteIndex() {
|
|
4444
|
+
this.routeByPath.clear();
|
|
4445
|
+
for (const workflow of this.workflowRepository.list()) {
|
|
4446
|
+
if (!this.workflowActivationPolicy.isActive(workflow.id)) {
|
|
4447
|
+
if (workflow.nodes.filter((n) => n.kind === "trigger").length > 0) {
|
|
4448
|
+
const paths = this.collectWebhookEndpointPaths(workflow);
|
|
4449
|
+
if (paths.length > 0) this.diagnostics?.info?.(`Workflow "${workflow.name}" (${workflow.id}) is inactive; webhook routes not registered: ${paths.map((p) => `"${p}"`).join(", ")}`);
|
|
4450
|
+
else this.diagnostics?.info?.(`Workflow "${workflow.name}" (${workflow.id}) is inactive; no repository webhook routes for its triggers (other trigger kinds are unchanged).`);
|
|
4451
|
+
}
|
|
4452
|
+
continue;
|
|
4453
|
+
}
|
|
4454
|
+
for (const def of workflow.nodes) {
|
|
4455
|
+
const match = this.tryMatchFromTriggerNode(workflow, def);
|
|
4456
|
+
if (!match) continue;
|
|
4457
|
+
const key = this.normalizeEndpointPath(match.endpointPath);
|
|
4458
|
+
const existing = this.routeByPath.get(key);
|
|
4459
|
+
if (existing) this.diagnostics?.warn(`Duplicate webhook endpoint path "${key}" (workflows "${existing.workflowId}" and "${match.workflowId}"); using "${match.workflowId}".`);
|
|
4460
|
+
this.routeByPath.set(key, match);
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
collectWebhookEndpointPaths(workflow) {
|
|
4465
|
+
const paths = [];
|
|
4466
|
+
for (const def of workflow.nodes) {
|
|
4467
|
+
if (def.kind !== "trigger") continue;
|
|
4468
|
+
const match = this.tryMatchFromTriggerNode(workflow, def);
|
|
4469
|
+
if (match) paths.push(match.endpointPath);
|
|
4470
|
+
}
|
|
4471
|
+
return paths;
|
|
4472
|
+
}
|
|
4473
|
+
tryMatchFromTriggerNode(workflow, def) {
|
|
4474
|
+
if (def.kind !== "trigger") return;
|
|
4475
|
+
const config = def.config;
|
|
4476
|
+
if (typeof config.endpointKey !== "string" || config.endpointKey.length === 0) return;
|
|
4477
|
+
if (!Array.isArray(config.methods) || config.methods.length === 0) return;
|
|
4478
|
+
const methods = config.methods;
|
|
4479
|
+
const parseJsonBody = typeof config.parseJsonBody === "function" ? config.parseJsonBody.bind(config) : void 0;
|
|
4480
|
+
return {
|
|
4481
|
+
endpointPath: config.endpointKey,
|
|
4482
|
+
workflowId: workflow.id,
|
|
4483
|
+
nodeId: def.id,
|
|
4484
|
+
methods: [...methods],
|
|
4485
|
+
parseJsonBody
|
|
4486
|
+
};
|
|
4487
|
+
}
|
|
4488
|
+
normalizeEndpointPath(endpointPath) {
|
|
4489
|
+
return endpointPath.trim();
|
|
4490
|
+
}
|
|
4491
|
+
};
|
|
4492
|
+
|
|
4493
|
+
//#endregion
|
|
4494
|
+
//#region src/runtime/WorkflowRepositoryWebhookTriggerMatcherFactory.ts
|
|
4495
|
+
var WorkflowRepositoryWebhookTriggerMatcherFactory = class {
|
|
4496
|
+
create(workflowRepository, workflowActivationPolicy, diagnostics) {
|
|
4497
|
+
return new WorkflowRepositoryWebhookTriggerMatcher(workflowRepository, workflowActivationPolicy, diagnostics);
|
|
4498
|
+
}
|
|
4499
|
+
};
|
|
4500
|
+
|
|
4501
|
+
//#endregion
|
|
4502
|
+
export { injectAll as $, NodeExecutor as A, WorkflowExecutableNodeClassifier as B, RunPolicySnapshotFactory as C, PersistedWorkflowTokenRegistry as D, WorkflowSnapshotCodec as E, CredentialResolverFactory as F, tool as G, chatModel as H, DefaultExecutionBinaryService as I, StackTraceCallSitePathResolver as J, InjectableRuntimeDecoratorComposer as K, UnavailableBinaryStorage as L, InProcessRetryRunner as M, DefaultExecutionContextFactory as N, MissingRuntimeTriggerToken as O, DefaultAsyncSleeper as P, inject as Q, NodeEventPublisher as R, ConfigDrivenOffloadPolicy as S, NodeInstanceFactory as T, getPersistedRuntimeTypeMetadata as U, ConnectionNodeIdFactory as V, node as W, container$1 as X, PersistedRuntimeTypeNameResolver as Y, delay as Z, EngineExecutionLimitsPolicy as _, InMemoryLiveWorkflowRepository as a, singleton as at, HintOnlyOffloadPolicy as b, EngineFactory as c, InMemoryRunDataFactory as d, injectable as et, InMemoryBinaryStorage as f, ENGINE_EXECUTION_LIMITS_DEFAULTS as g, RunTerminalPersistenceCoordinator as h, RunIntentService as i, registry as it, InProcessRetryRunnerFactory as j, NodeExecutorFactory as k, Engine as l, WorkflowPolicyErrorServices as m, WorkflowRepositoryWebhookTriggerMatcher as n, instancePerContainerCachingFactory as nt, EngineWorkflowRunnerServiceFactory as o, CoreTokens as ot, WorkflowStoragePolicyEvaluator as p, PersistedRuntimeTypeMetadataStore as q, RunIntentServiceFactory as r, predicateAwareClassFactory as rt, EngineWorkflowRunnerService as s, WorkflowRepositoryWebhookTriggerMatcherFactory as t, instanceCachingFactory as tt, RunFinishedAtFactory as u, LocalOnlyScheduler as v, NodeInstanceFactoryFactory as w, DefaultDrivingScheduler as x, InlineDrivingScheduler as y, WorkflowExecutableNodeClassifierFactory as z };
|
|
4503
|
+
//# sourceMappingURL=runtime-Cy-3FTI_.js.map
|