@codemation/host 0.1.0 → 0.1.2
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 +24 -0
- package/dist/{AppConfigFactory-Ciz9YKWx.js → AppConfigFactory-ByT1D8dM.js} +253 -15
- package/dist/{AppConfigFactory-Ciz9YKWx.js.map → AppConfigFactory-ByT1D8dM.js.map} +1 -1
- package/dist/{AppConfigFactory-CiBPHleh.d.ts → AppConfigFactory-_fqSok1J.d.ts} +6565 -177
- package/dist/{AppContainerFactory-DH88oxpg.js → AppContainerFactory-BRU02PTm.js} +838 -145
- package/dist/AppContainerFactory-BRU02PTm.js.map +1 -0
- package/dist/{CodemationConfig-DfK1KLvO.d.ts → CodemationConfig-CNfytKR6.d.ts} +2 -2
- package/dist/{CodemationConfigNormalizer-BKgIOeLm.d.ts → CodemationConfigNormalizer-BuKWVNEq.d.ts} +2 -2
- package/dist/{CodemationConsumerConfigLoader-bdhJsBKt.d.ts → CodemationConsumerConfigLoader-Mv4cywWu.d.ts} +2 -2
- package/dist/{CodemationPluginListMerger-BFZeO0WG.d.ts → CodemationPluginListMerger-BD5mR6gK.d.ts} +11 -5
- package/dist/{CredentialServices-aKIwHEhf.d.ts → CredentialServices-BQsEtctT.d.ts} +8 -7
- package/dist/{CredentialServices-DNb3CZwW.js → CredentialServices-xVxVA9Tq.js} +60 -114
- package/dist/CredentialServices-xVxVA9Tq.js.map +1 -0
- package/dist/{PublicFrontendBootstrapFactory-6ahaU0XM.d.ts → PublicFrontendBootstrapFactory-kTyAJdHI.d.ts} +2 -2
- package/dist/consumer.d.ts +4 -4
- package/dist/credentials.d.ts +3 -3
- package/dist/credentials.js +1 -1
- package/dist/devServerSidecar.d.ts +1 -1
- package/dist/{index-BYbzmUwS.d.ts → index-CX752QE9.d.ts} +68 -4
- package/dist/index.d.ts +10 -10
- package/dist/index.js +5 -5
- package/dist/nextServer.d.ts +20 -15
- package/dist/nextServer.js +35 -58
- package/dist/nextServer.js.map +1 -1
- package/dist/{persistenceServer-DL8yBGDU.d.ts → persistenceServer-CLY4qtMo.d.ts} +2 -2
- package/dist/{persistenceServer-CuAqL_fF.js → persistenceServer-DMvIOGW8.js} +2 -2
- package/dist/{persistenceServer-CuAqL_fF.js.map → persistenceServer-DMvIOGW8.js.map} +1 -1
- package/dist/persistenceServer.d.ts +5 -5
- package/dist/persistenceServer.js +2 -2
- package/dist/{server-EbxQft_X.js → server-ChTCEc6R.js} +4 -4
- package/dist/{server-EbxQft_X.js.map → server-ChTCEc6R.js.map} +1 -1
- package/dist/{server-BceIfIJf.d.ts → server-DwpcwzFb.d.ts} +6 -5
- package/dist/server.d.ts +8 -8
- package/dist/server.js +5 -5
- package/package.json +5 -5
- package/prisma/migrations/20260407140000_run_normalized_persistence/migration.sql +327 -0
- package/prisma/migrations/20260407193000_rename_run_projection_to_run_slot_projection/migration.sql +10 -0
- package/prisma/migrations.sqlite/20260407140000_run_normalized_persistence/migration.sql +326 -0
- package/prisma/migrations.sqlite/20260407193000_rename_run_projection_to_run_slot_projection/migration.sql +38 -0
- package/prisma/schema.postgresql.prisma +100 -1
- package/prisma/schema.sqlite.prisma +101 -1
- package/scripts/integration-database-global-setup.mjs +0 -3
- package/src/application/mapping/WorkflowDefinitionMapper.ts +95 -56
- package/src/application/mapping/WorkflowPolicyUiPresentationFactory.ts +1 -1
- package/src/application/queries/GetWorkflowRunDetailQuery.ts +8 -0
- package/src/application/queries/GetWorkflowRunDetailQueryHandler.ts +24 -0
- package/src/application/queries/WorkflowQueryHandlers.ts +1 -0
- package/src/application/runs/WorkflowRunRetentionPruneScheduler.ts +52 -27
- package/src/domain/credentials/WorkflowCredentialNodeResolver.ts +113 -158
- package/src/domain/runs/WorkflowRunRepository.ts +7 -1
- package/src/infrastructure/persistence/InMemoryWorkflowRunRepository.ts +123 -1
- package/src/infrastructure/persistence/PrismaMigrationDeployer.ts +226 -6
- package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +796 -109
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/edge.js +85 -5
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index-browser.js +81 -1
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index.d.ts +7107 -237
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index.js +85 -5
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/package.json +1 -1
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/schema.prisma +101 -1
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/edge.js +85 -5
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index-browser.js +81 -1
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index.d.ts +7104 -242
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index.js +85 -5
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/package.json +1 -1
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/schema.prisma +101 -1
- package/src/presentation/http/ApiPaths.ts +4 -0
- package/src/presentation/http/hono/registrars/RunHonoApiRouteRegistrar.ts +1 -0
- package/src/presentation/http/routeHandlers/RunHttpRouteHandler.ts +13 -0
- package/dist/AppContainerFactory-DH88oxpg.js.map +0 -1
- package/dist/CredentialServices-DNb3CZwW.js.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { CredentialRequirement, WorkflowDefinition } from "@codemation/core";
|
|
2
2
|
import {
|
|
3
3
|
AgentConfigInspector,
|
|
4
|
+
AgentConnectionNodeCollector,
|
|
5
|
+
type AgentConnectionNodeDescriptor,
|
|
4
6
|
ConnectionNodeIdFactory,
|
|
5
|
-
WorkflowExecutableNodeClassifierFactory,
|
|
6
7
|
} from "@codemation/core";
|
|
7
8
|
|
|
8
9
|
import { injectable } from "@codemation/core";
|
|
@@ -27,48 +28,18 @@ export class WorkflowCredentialNodeResolver {
|
|
|
27
28
|
if (direct) {
|
|
28
29
|
return direct.name ?? direct.config.name ?? direct.id;
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const agentLabel = parent?.name ?? parentId ?? "Agent";
|
|
34
|
-
return `${agentLabel} › Language model`;
|
|
31
|
+
const recursive = this.findRecursiveConnectionNode(workflow, nodeId);
|
|
32
|
+
if (!recursive) {
|
|
33
|
+
return nodeId;
|
|
35
34
|
}
|
|
36
|
-
|
|
37
|
-
const parsed = this.parseToolConnectionNodeId(nodeId);
|
|
38
|
-
if (!parsed) {
|
|
39
|
-
return nodeId;
|
|
40
|
-
}
|
|
41
|
-
const parent = workflow.nodes.find((n) => n.id === parsed.parentNodeId);
|
|
42
|
-
const agentLabel = parent?.name ?? parsed.parentNodeId;
|
|
43
|
-
const toolConfig =
|
|
44
|
-
parent && AgentConfigInspector.isAgentNodeConfig(parent.config)
|
|
45
|
-
? parent.config.tools?.find(
|
|
46
|
-
(tool) => ConnectionNodeIdFactory.normalizeToolName(tool.name) === parsed.normalizedToolName,
|
|
47
|
-
)
|
|
48
|
-
: undefined;
|
|
49
|
-
const toolLabel = toolConfig?.presentation?.label ?? toolConfig?.name ?? parsed.normalizedToolName;
|
|
50
|
-
return `${agentLabel} › ${toolLabel}`;
|
|
51
|
-
}
|
|
52
|
-
return nodeId;
|
|
35
|
+
return this.buildRecursiveDisplayLabel(recursive.rootAgentLabel, recursive.entry, recursive.entriesById);
|
|
53
36
|
}
|
|
54
37
|
|
|
55
38
|
isCredentialNodeIdInWorkflow(workflow: WorkflowDefinition, nodeId: string): boolean {
|
|
56
39
|
if (workflow.nodes.some((n) => n.id === nodeId)) {
|
|
57
40
|
return true;
|
|
58
41
|
}
|
|
59
|
-
|
|
60
|
-
const parent = this.parseParentForLanguageModelConnectionNodeId(nodeId);
|
|
61
|
-
if (parent && workflow.nodes.some((n) => n.id === parent)) {
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (ConnectionNodeIdFactory.isToolConnectionNodeId(nodeId)) {
|
|
66
|
-
const parsed = this.parseToolConnectionNodeId(nodeId);
|
|
67
|
-
if (parsed && workflow.nodes.some((n) => n.id === parsed.parentNodeId)) {
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return false;
|
|
42
|
+
return this.findRecursiveConnectionNode(workflow, nodeId) !== undefined;
|
|
72
43
|
}
|
|
73
44
|
|
|
74
45
|
findRequirement(
|
|
@@ -80,83 +51,33 @@ export class WorkflowCredentialNodeResolver {
|
|
|
80
51
|
if (direct) {
|
|
81
52
|
return direct;
|
|
82
53
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const fromConn = this.findLanguageModelRequirement(workflow, parent, slotKey);
|
|
87
|
-
if (fromConn) {
|
|
88
|
-
return fromConn;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (ConnectionNodeIdFactory.isToolConnectionNodeId(nodeId)) {
|
|
93
|
-
const parsed = this.parseToolConnectionNodeId(nodeId);
|
|
94
|
-
if (parsed) {
|
|
95
|
-
const fromConn = this.findToolRequirement(workflow, parsed.parentNodeId, parsed.normalizedToolName, slotKey);
|
|
96
|
-
if (fromConn) {
|
|
97
|
-
return fromConn;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
54
|
+
const recursive = this.findRecursiveConnectionNode(workflow, nodeId);
|
|
55
|
+
if (!recursive) {
|
|
56
|
+
return undefined;
|
|
100
57
|
}
|
|
101
|
-
|
|
58
|
+
const requirement = recursive.entry.credentialSource
|
|
59
|
+
.getCredentialRequirements?.()
|
|
60
|
+
?.find((entry) => entry.slotKey === slotKey);
|
|
61
|
+
return requirement ? { nodeName: recursive.entry.name, requirement } : undefined;
|
|
102
62
|
}
|
|
103
63
|
|
|
104
64
|
listSlots(workflow: WorkflowDefinition): ReadonlyArray<WorkflowCredentialSlotRef> {
|
|
105
|
-
const
|
|
106
|
-
const classifier = WorkflowExecutableNodeClassifierFactory.create(workflow);
|
|
107
|
-
const hasConnectionMetadata = (workflow.connections?.length ?? 0) > 0;
|
|
65
|
+
const slotsByKey = new Map<string, WorkflowCredentialSlotRef>();
|
|
108
66
|
|
|
109
67
|
for (const node of workflow.nodes) {
|
|
110
|
-
if (classifier.isConnectionOwnedNodeId(node.id)) {
|
|
111
|
-
for (const requirement of node.config.getCredentialRequirements?.() ?? []) {
|
|
112
|
-
slots.push({
|
|
113
|
-
workflowId: workflow.id,
|
|
114
|
-
nodeId: node.id,
|
|
115
|
-
nodeName: node.name ?? node.config.name ?? node.id,
|
|
116
|
-
requirement,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
68
|
if (AgentConfigInspector.isAgentNodeConfig(node.config)) {
|
|
123
|
-
|
|
124
|
-
const lmNodeId = ConnectionNodeIdFactory.languageModelConnectionNodeId(node.id);
|
|
125
|
-
const lmLabel = node.config.chatModel.presentation?.label ?? node.config.chatModel.name;
|
|
126
|
-
for (const requirement of node.config.chatModel.getCredentialRequirements?.() ?? []) {
|
|
127
|
-
slots.push({
|
|
128
|
-
workflowId: workflow.id,
|
|
129
|
-
nodeId: lmNodeId,
|
|
130
|
-
nodeName: lmLabel,
|
|
131
|
-
requirement,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
for (const toolConfig of node.config.tools ?? []) {
|
|
135
|
-
const toolNodeId = ConnectionNodeIdFactory.toolConnectionNodeId(node.id, toolConfig.name);
|
|
136
|
-
const toolLabel = toolConfig.presentation?.label ?? toolConfig.name;
|
|
137
|
-
for (const requirement of toolConfig.getCredentialRequirements?.() ?? []) {
|
|
138
|
-
slots.push({
|
|
139
|
-
workflowId: workflow.id,
|
|
140
|
-
nodeId: toolNodeId,
|
|
141
|
-
nodeName: toolLabel,
|
|
142
|
-
requirement,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
69
|
+
this.addRecursiveAgentSlots(workflow.id, node.id, node.config, slotsByKey);
|
|
147
70
|
continue;
|
|
148
71
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
return slots;
|
|
72
|
+
this.addSlotsForRequirements(
|
|
73
|
+
workflow.id,
|
|
74
|
+
node.id,
|
|
75
|
+
node.name ?? node.config.name ?? node.id,
|
|
76
|
+
node.config.getCredentialRequirements?.() ?? [],
|
|
77
|
+
slotsByKey,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return [...slotsByKey.values()];
|
|
160
81
|
}
|
|
161
82
|
|
|
162
83
|
private findDirectRequirement(
|
|
@@ -175,72 +96,106 @@ export class WorkflowCredentialNodeResolver {
|
|
|
175
96
|
return { nodeName: node.name ?? node.config.name ?? node.id, requirement };
|
|
176
97
|
}
|
|
177
98
|
|
|
178
|
-
private
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
99
|
+
private addRecursiveAgentSlots(
|
|
100
|
+
workflowId: string,
|
|
101
|
+
rootAgentNodeId: string,
|
|
102
|
+
agentConfig: Parameters<typeof AgentConnectionNodeCollector.collect>[1],
|
|
103
|
+
slotsByKey: Map<string, WorkflowCredentialSlotRef>,
|
|
104
|
+
): void {
|
|
105
|
+
for (const entry of AgentConnectionNodeCollector.collect(rootAgentNodeId, agentConfig)) {
|
|
106
|
+
this.addSlotsForRequirements(
|
|
107
|
+
workflowId,
|
|
108
|
+
entry.nodeId,
|
|
109
|
+
entry.name,
|
|
110
|
+
entry.credentialSource.getCredentialRequirements?.() ?? [],
|
|
111
|
+
slotsByKey,
|
|
112
|
+
);
|
|
186
113
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private addSlotsForRequirements(
|
|
117
|
+
workflowId: string,
|
|
118
|
+
nodeId: string,
|
|
119
|
+
nodeName: string,
|
|
120
|
+
requirements: ReadonlyArray<CredentialRequirement>,
|
|
121
|
+
slotsByKey: Map<string, WorkflowCredentialSlotRef>,
|
|
122
|
+
): void {
|
|
123
|
+
for (const requirement of requirements) {
|
|
124
|
+
const key = `${nodeId}\0${requirement.slotKey}`;
|
|
125
|
+
if (slotsByKey.has(key)) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
slotsByKey.set(key, {
|
|
129
|
+
workflowId,
|
|
130
|
+
nodeId,
|
|
131
|
+
nodeName,
|
|
132
|
+
requirement,
|
|
133
|
+
});
|
|
192
134
|
}
|
|
193
|
-
const nodeName =
|
|
194
|
-
parent.config.chatModel.presentation?.label ?? parent.config.chatModel.name ?? parent.name ?? parent.id;
|
|
195
|
-
return { nodeName, requirement };
|
|
196
135
|
}
|
|
197
136
|
|
|
198
|
-
private
|
|
137
|
+
private findRecursiveConnectionNode(
|
|
199
138
|
workflow: WorkflowDefinition,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
139
|
+
nodeId: string,
|
|
140
|
+
):
|
|
141
|
+
| Readonly<{
|
|
142
|
+
rootAgentNodeId: string;
|
|
143
|
+
rootAgentLabel: string;
|
|
144
|
+
entry: AgentConnectionNodeDescriptor;
|
|
145
|
+
entriesById: ReadonlyMap<string, AgentConnectionNodeDescriptor>;
|
|
146
|
+
}>
|
|
147
|
+
| undefined {
|
|
148
|
+
if (
|
|
149
|
+
!ConnectionNodeIdFactory.isLanguageModelConnectionNodeId(nodeId) &&
|
|
150
|
+
!ConnectionNodeIdFactory.isToolConnectionNodeId(nodeId)
|
|
151
|
+
) {
|
|
212
152
|
return undefined;
|
|
213
153
|
}
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
154
|
+
for (const node of workflow.nodes) {
|
|
155
|
+
if (!AgentConfigInspector.isAgentNodeConfig(node.config)) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const entries = AgentConnectionNodeCollector.collect(node.id, node.config);
|
|
159
|
+
const entriesById = new Map(entries.map((entry) => [entry.nodeId, entry]));
|
|
160
|
+
const entry = entriesById.get(nodeId);
|
|
161
|
+
if (!entry) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
rootAgentNodeId: node.id,
|
|
166
|
+
rootAgentLabel: node.name ?? node.config.name ?? node.id,
|
|
167
|
+
entry,
|
|
168
|
+
entriesById,
|
|
169
|
+
};
|
|
217
170
|
}
|
|
218
|
-
|
|
219
|
-
return { nodeName, requirement };
|
|
171
|
+
return undefined;
|
|
220
172
|
}
|
|
221
173
|
|
|
222
|
-
private
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
174
|
+
private buildRecursiveDisplayLabel(
|
|
175
|
+
rootAgentLabel: string,
|
|
176
|
+
entry: AgentConnectionNodeDescriptor,
|
|
177
|
+
entriesById: ReadonlyMap<string, AgentConnectionNodeDescriptor>,
|
|
178
|
+
): string {
|
|
179
|
+
const labels = [rootAgentLabel, ...this.collectAncestorToolLabels(entry.parentNodeId, entriesById)];
|
|
180
|
+
labels.push(entry.role === "languageModel" ? "Language model" : entry.name);
|
|
181
|
+
return labels.join(" › ");
|
|
228
182
|
}
|
|
229
183
|
|
|
230
|
-
private
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
184
|
+
private collectAncestorToolLabels(
|
|
185
|
+
parentNodeId: string,
|
|
186
|
+
entriesById: ReadonlyMap<string, AgentConnectionNodeDescriptor>,
|
|
187
|
+
): ReadonlyArray<string> {
|
|
188
|
+
const labels: string[] = [];
|
|
189
|
+
let currentNodeId = parentNodeId;
|
|
190
|
+
while (true) {
|
|
191
|
+
const parentEntry = entriesById.get(currentNodeId);
|
|
192
|
+
if (!parentEntry) {
|
|
193
|
+
return labels.reverse();
|
|
194
|
+
}
|
|
195
|
+
if (parentEntry.role === "tool" || parentEntry.role === "nestedAgent") {
|
|
196
|
+
labels.push(parentEntry.name);
|
|
197
|
+
}
|
|
198
|
+
currentNodeId = parentEntry.parentNodeId;
|
|
243
199
|
}
|
|
244
|
-
return { parentNodeId, normalizedToolName };
|
|
245
200
|
}
|
|
246
201
|
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import type { PersistedRunState, RunId, RunSummary } from "@codemation/core";
|
|
1
|
+
import type { PersistedRunState, RunId, RunPruneCandidate, RunSummary, WorkflowRunDetailDto } from "@codemation/core";
|
|
2
2
|
|
|
3
3
|
export interface WorkflowRunRepository {
|
|
4
4
|
load(runId: string): Promise<PersistedRunState | undefined>;
|
|
5
5
|
|
|
6
|
+
loadRunDetail?(runId: string): Promise<WorkflowRunDetailDto | undefined>;
|
|
7
|
+
|
|
6
8
|
save(state: PersistedRunState): Promise<void>;
|
|
7
9
|
|
|
8
10
|
listRuns(args: Readonly<{ workflowId?: string; limit?: number }>): Promise<ReadonlyArray<RunSummary>>;
|
|
9
11
|
|
|
12
|
+
listRunsOlderThan?(args: Readonly<{ beforeIso: string; limit?: number }>): Promise<ReadonlyArray<RunPruneCandidate>>;
|
|
13
|
+
|
|
14
|
+
listBinaryStorageKeys?(runId: RunId): Promise<ReadonlyArray<string>>;
|
|
15
|
+
|
|
10
16
|
deleteRun(runId: RunId): Promise<void>;
|
|
11
17
|
}
|
|
@@ -3,10 +3,14 @@ import {
|
|
|
3
3
|
type NodeId,
|
|
4
4
|
type NodeOutputs,
|
|
5
5
|
type ParentExecutionRef,
|
|
6
|
+
type PersistedRunSchedulingState,
|
|
6
7
|
type PersistedRunState,
|
|
7
8
|
type RunId,
|
|
8
9
|
type RunPruneCandidate,
|
|
9
10
|
type RunSummary,
|
|
11
|
+
type ExecutionInstanceDto,
|
|
12
|
+
type SlotExecutionStateDto,
|
|
13
|
+
type WorkflowRunDetailDto,
|
|
10
14
|
type WorkflowExecutionRepository,
|
|
11
15
|
type WorkflowId,
|
|
12
16
|
} from "@codemation/core";
|
|
@@ -34,6 +38,7 @@ export class InMemoryWorkflowRunRepository implements WorkflowRunRepository, Wor
|
|
|
34
38
|
runId: args.runId,
|
|
35
39
|
workflowId: args.workflowId,
|
|
36
40
|
startedAt: args.startedAt,
|
|
41
|
+
revision: 0,
|
|
37
42
|
parent: args.parent,
|
|
38
43
|
executionOptions: args.executionOptions,
|
|
39
44
|
control: args.control,
|
|
@@ -53,8 +58,90 @@ export class InMemoryWorkflowRunRepository implements WorkflowRunRepository, Wor
|
|
|
53
58
|
return this.runs.get(decodeURIComponent(runId) as RunId);
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
async loadSchedulingState(runId: string): Promise<PersistedRunSchedulingState | undefined> {
|
|
62
|
+
const state = await this.load(runId);
|
|
63
|
+
if (!state) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
pending: state.pending ? { ...state.pending } : undefined,
|
|
68
|
+
queue: state.queue.map((entry) => ({ ...entry })),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
async save(state: PersistedRunState): Promise<void> {
|
|
57
|
-
this.runs.set(state.runId, state);
|
|
73
|
+
this.runs.set(state.runId, { ...state, revision: (state.revision ?? 0) + 1 });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async loadRunDetail(runId: string): Promise<WorkflowRunDetailDto | undefined> {
|
|
77
|
+
const state = await this.load(runId);
|
|
78
|
+
if (!state) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const slotStates: SlotExecutionStateDto[] = Object.entries(state.nodeSnapshotsByNodeId).map(
|
|
82
|
+
([slotNodeId, snapshot]) => ({
|
|
83
|
+
slotNodeId,
|
|
84
|
+
latestInstanceId: snapshot.activationId
|
|
85
|
+
? `${state.runId}:node:${slotNodeId}:${snapshot.activationId}`
|
|
86
|
+
: undefined,
|
|
87
|
+
latestTerminalInstanceId:
|
|
88
|
+
snapshot.status === "completed" || snapshot.status === "failed"
|
|
89
|
+
? snapshot.activationId
|
|
90
|
+
? `${state.runId}:node:${slotNodeId}:${snapshot.activationId}`
|
|
91
|
+
: undefined
|
|
92
|
+
: undefined,
|
|
93
|
+
latestRunningInstanceId:
|
|
94
|
+
snapshot.status === "queued" || snapshot.status === "running"
|
|
95
|
+
? snapshot.activationId
|
|
96
|
+
? `${state.runId}:node:${slotNodeId}:${snapshot.activationId}`
|
|
97
|
+
: undefined
|
|
98
|
+
: undefined,
|
|
99
|
+
status: snapshot.status,
|
|
100
|
+
invocationCount: (state.connectionInvocations ?? []).filter((inv) => inv.connectionNodeId === slotNodeId)
|
|
101
|
+
.length,
|
|
102
|
+
runCount: 1,
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
const executionInstances: ExecutionInstanceDto[] = Object.entries(state.nodeSnapshotsByNodeId).map(
|
|
106
|
+
([slotNodeId, snapshot]) => ({
|
|
107
|
+
instanceId: `${state.runId}:node:${slotNodeId}:${snapshot.activationId ?? "na"}`,
|
|
108
|
+
slotNodeId,
|
|
109
|
+
workflowNodeId: slotNodeId,
|
|
110
|
+
kind: "workflowNodeActivation",
|
|
111
|
+
runIndex: 1,
|
|
112
|
+
batchId: state.pending?.batchId ?? "batch_1",
|
|
113
|
+
activationId: snapshot.activationId,
|
|
114
|
+
status: snapshot.status,
|
|
115
|
+
queuedAt: snapshot.queuedAt,
|
|
116
|
+
startedAt: snapshot.startedAt,
|
|
117
|
+
finishedAt: snapshot.finishedAt,
|
|
118
|
+
itemCount: Object.values(snapshot.inputsByPort ?? {}).reduce((count, items) => count + items.length, 0),
|
|
119
|
+
inputJson: snapshot.inputsByPort as never,
|
|
120
|
+
outputJson: snapshot.outputs as never,
|
|
121
|
+
error: snapshot.error,
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
return {
|
|
125
|
+
runId: state.runId,
|
|
126
|
+
workflowId: state.workflowId,
|
|
127
|
+
startedAt: state.startedAt,
|
|
128
|
+
finishedAt: state.finishedAt,
|
|
129
|
+
status: state.status,
|
|
130
|
+
workflowSnapshot: state.workflowSnapshot,
|
|
131
|
+
mutableState: state.mutableState,
|
|
132
|
+
slotStates,
|
|
133
|
+
executionInstances,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async listBinaryStorageKeys(runId: RunId): Promise<ReadonlyArray<string>> {
|
|
138
|
+
const state = await this.load(runId);
|
|
139
|
+
if (!state) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
const keys = new Set<string>();
|
|
143
|
+
this.collectBinaryKeysFromRunState(state, keys);
|
|
144
|
+
return [...keys].sort((left, right) => left.localeCompare(right));
|
|
58
145
|
}
|
|
59
146
|
|
|
60
147
|
async deleteRun(runId: RunId): Promise<void> {
|
|
@@ -91,4 +178,39 @@ export class InMemoryWorkflowRunRepository implements WorkflowRunRepository, Wor
|
|
|
91
178
|
out.sort((a, b) => a.finishedAt.localeCompare(b.finishedAt));
|
|
92
179
|
return out.slice(0, limit);
|
|
93
180
|
}
|
|
181
|
+
|
|
182
|
+
private collectBinaryKeysFromRunState(state: PersistedRunState, keys: Set<string>): void {
|
|
183
|
+
for (const outputs of Object.values(state.outputsByNode)) {
|
|
184
|
+
for (const items of Object.values(outputs)) {
|
|
185
|
+
this.collectBinaryKeysFromItems(items, keys);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const snapshot of Object.values(state.nodeSnapshotsByNodeId)) {
|
|
189
|
+
for (const items of Object.values(snapshot.inputsByPort ?? {})) {
|
|
190
|
+
this.collectBinaryKeysFromItems(items, keys);
|
|
191
|
+
}
|
|
192
|
+
for (const items of Object.values(snapshot.outputs ?? {})) {
|
|
193
|
+
this.collectBinaryKeysFromItems(items, keys);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
for (const nodeState of Object.values(state.mutableState?.nodesById ?? {})) {
|
|
197
|
+
for (const items of Object.values(nodeState.pinnedOutputsByPort ?? {})) {
|
|
198
|
+
this.collectBinaryKeysFromItems(items, keys);
|
|
199
|
+
}
|
|
200
|
+
this.collectBinaryKeysFromItems(nodeState.lastDebugInput, keys);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private collectBinaryKeysFromItems(
|
|
205
|
+
items: PersistedRunState["outputsByNode"][string][string] | undefined,
|
|
206
|
+
keys: Set<string>,
|
|
207
|
+
): void {
|
|
208
|
+
for (const item of items ?? []) {
|
|
209
|
+
for (const attachment of Object.values(item.binary ?? {})) {
|
|
210
|
+
if (attachment.storageKey.length > 0) {
|
|
211
|
+
keys.add(attachment.storageKey);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
94
216
|
}
|