@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,12 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import type { AgentNodeConfig, NodeDefinition, WorkflowActivationPolicy, WorkflowDefinition } from "@codemation/core";
|
|
2
|
+
import {
|
|
3
|
+
AgentConfigInspector,
|
|
4
|
+
AgentConnectionNodeCollector,
|
|
5
|
+
CoreTokens,
|
|
6
|
+
inject,
|
|
7
|
+
injectable,
|
|
8
|
+
type AgentConnectionNodeDescriptor,
|
|
7
9
|
} from "@codemation/core";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
+
import type {
|
|
11
|
+
WorkflowDto,
|
|
12
|
+
WorkflowEdgeDto,
|
|
13
|
+
WorkflowNodeDto,
|
|
14
|
+
WorkflowSummary,
|
|
15
|
+
} from "../contracts/WorkflowViewContracts";
|
|
10
16
|
import type { DataMapper } from "./DataMapper";
|
|
11
17
|
import { WorkflowPolicyUiPresentationFactory } from "./WorkflowPolicyUiPresentationFactory";
|
|
12
18
|
|
|
@@ -55,17 +61,23 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
55
61
|
return map;
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
private agentHasConnectionMetadata(workflow: WorkflowDefinition, agentNodeId: string): boolean {
|
|
59
|
-
return (workflow.connections ?? []).some((c) => c.parentNodeId === agentNodeId);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
64
|
private toNodes(workflow: WorkflowDefinition): ReadonlyArray<WorkflowNodeDto> {
|
|
63
65
|
const connectionChildMeta = this.buildConnectionChildMeta(workflow);
|
|
66
|
+
const materializedConnectionNodeIds = new Set(connectionChildMeta.keys());
|
|
64
67
|
const nodes: WorkflowNodeDto[] = [];
|
|
65
68
|
for (const node of workflow.nodes) {
|
|
66
69
|
const conn = connectionChildMeta.get(node.id);
|
|
67
70
|
if (conn) {
|
|
68
|
-
const
|
|
71
|
+
const parentNode = workflow.nodes.find((n) => n.id === conn.parentNodeId);
|
|
72
|
+
let role: string = conn.connectionName === "llm" ? "languageModel" : "tool";
|
|
73
|
+
if (parentNode && AgentConfigInspector.isAgentNodeConfig(parentNode.config)) {
|
|
74
|
+
const descriptor = AgentConnectionNodeCollector.collect(conn.parentNodeId, parentNode.config).find(
|
|
75
|
+
(d) => d.nodeId === node.id,
|
|
76
|
+
);
|
|
77
|
+
if (descriptor) {
|
|
78
|
+
role = descriptor.role;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
69
81
|
nodes.push({
|
|
70
82
|
id: node.id,
|
|
71
83
|
kind: node.kind,
|
|
@@ -89,71 +101,98 @@ export class WorkflowDefinitionMapper implements DataMapper<WorkflowDefinition,
|
|
|
89
101
|
retryPolicySummary: this.policyUi.nodeRetrySummary(node.config),
|
|
90
102
|
hasNodeErrorHandler: this.policyUi.nodeHasErrorHandler(node.config),
|
|
91
103
|
});
|
|
92
|
-
if (AgentConfigInspector.isAgentNodeConfig(node.config)
|
|
93
|
-
|
|
94
|
-
for (const toolConfig of node.config.tools ?? []) {
|
|
95
|
-
nodes.push(this.createToolNode(node, toolConfig));
|
|
96
|
-
}
|
|
104
|
+
if (AgentConfigInspector.isAgentNodeConfig(node.config)) {
|
|
105
|
+
this.appendVirtualConnectionNodes(node.id, node.config, materializedConnectionNodeIds, nodes);
|
|
97
106
|
}
|
|
98
107
|
}
|
|
99
108
|
return nodes;
|
|
100
109
|
}
|
|
101
110
|
|
|
102
111
|
private toEdges(workflow: WorkflowDefinition): WorkflowDto["edges"] {
|
|
103
|
-
const
|
|
112
|
+
const connectionChildMeta = this.buildConnectionChildMeta(workflow);
|
|
113
|
+
const materializedConnectionNodeIds = new Set(connectionChildMeta.keys());
|
|
114
|
+
const edges: WorkflowEdgeDto[] = [...workflow.edges];
|
|
115
|
+
const edgeKeys = new Set(edges.map((edge) => this.edgeKey(edge.from.nodeId, edge.to.nodeId, edge.to.input)));
|
|
116
|
+
this.appendMaterializedConnectionEdges(workflow, edgeKeys, edges);
|
|
104
117
|
for (const node of workflow.nodes) {
|
|
105
118
|
if (!AgentConfigInspector.isAgentNodeConfig(node.config)) {
|
|
106
119
|
continue;
|
|
107
120
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
this.appendVirtualConnectionEdges(node.id, node.config, materializedConnectionNodeIds, edgeKeys, edges);
|
|
122
|
+
}
|
|
123
|
+
return edges;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private appendMaterializedConnectionEdges(
|
|
127
|
+
workflow: WorkflowDefinition,
|
|
128
|
+
edgeKeys: Set<string>,
|
|
129
|
+
edges: WorkflowEdgeDto[],
|
|
130
|
+
): void {
|
|
131
|
+
for (const connection of workflow.connections ?? []) {
|
|
132
|
+
for (const childNodeId of connection.childNodeIds) {
|
|
133
|
+
const key = this.edgeKey(connection.parentNodeId, childNodeId, "in");
|
|
134
|
+
if (edgeKeys.has(key)) {
|
|
135
|
+
continue;
|
|
119
136
|
}
|
|
137
|
+
edges.push({
|
|
138
|
+
from: { nodeId: connection.parentNodeId, output: "main" },
|
|
139
|
+
to: { nodeId: childNodeId, input: "in" },
|
|
140
|
+
});
|
|
141
|
+
edgeKeys.add(key);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private appendVirtualConnectionNodes(
|
|
147
|
+
rootAgentNodeId: string,
|
|
148
|
+
agentConfig: AgentNodeConfig<any, any>,
|
|
149
|
+
materializedConnectionNodeIds: ReadonlySet<string>,
|
|
150
|
+
nodes: WorkflowNodeDto[],
|
|
151
|
+
): void {
|
|
152
|
+
for (const connectionNode of AgentConnectionNodeCollector.collect(rootAgentNodeId, agentConfig)) {
|
|
153
|
+
if (materializedConnectionNodeIds.has(connectionNode.nodeId)) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
nodes.push(this.createConnectionNode(connectionNode));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private appendVirtualConnectionEdges(
|
|
161
|
+
rootAgentNodeId: string,
|
|
162
|
+
agentConfig: AgentNodeConfig<any, any>,
|
|
163
|
+
materializedConnectionNodeIds: ReadonlySet<string>,
|
|
164
|
+
edgeKeys: Set<string>,
|
|
165
|
+
edges: WorkflowEdgeDto[],
|
|
166
|
+
): void {
|
|
167
|
+
for (const connectionNode of AgentConnectionNodeCollector.collect(rootAgentNodeId, agentConfig)) {
|
|
168
|
+
if (materializedConnectionNodeIds.has(connectionNode.nodeId)) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const key = this.edgeKey(connectionNode.parentNodeId, connectionNode.nodeId, "in");
|
|
172
|
+
if (edgeKeys.has(key)) {
|
|
120
173
|
continue;
|
|
121
174
|
}
|
|
122
175
|
edges.push({
|
|
123
|
-
from: { nodeId:
|
|
124
|
-
to: { nodeId:
|
|
176
|
+
from: { nodeId: connectionNode.parentNodeId, output: "main" },
|
|
177
|
+
to: { nodeId: connectionNode.nodeId, input: "in" },
|
|
125
178
|
});
|
|
126
|
-
|
|
127
|
-
edges.push({
|
|
128
|
-
from: { nodeId: node.id, output: "main" },
|
|
129
|
-
to: { nodeId: ConnectionNodeIdFactory.toolConnectionNodeId(node.id, toolConfig.name), input: "in" },
|
|
130
|
-
});
|
|
131
|
-
}
|
|
179
|
+
edgeKeys.add(key);
|
|
132
180
|
}
|
|
133
|
-
return edges;
|
|
134
181
|
}
|
|
135
182
|
|
|
136
|
-
private
|
|
137
|
-
return {
|
|
138
|
-
id: ConnectionNodeIdFactory.languageModelConnectionNodeId(node.id),
|
|
139
|
-
kind: "node",
|
|
140
|
-
name: chatModel.presentation?.label ?? chatModel.name,
|
|
141
|
-
type: chatModel.name,
|
|
142
|
-
role: "languageModel",
|
|
143
|
-
icon: chatModel.presentation?.icon,
|
|
144
|
-
parentNodeId: node.id,
|
|
145
|
-
};
|
|
183
|
+
private edgeKey(fromNodeId: string, toNodeId: string, toInput: string): string {
|
|
184
|
+
return `${fromNodeId}\0${toNodeId}\0${toInput}`;
|
|
146
185
|
}
|
|
147
186
|
|
|
148
|
-
private
|
|
187
|
+
private createConnectionNode(connectionNode: AgentConnectionNodeDescriptor): WorkflowNodeDto {
|
|
149
188
|
return {
|
|
150
|
-
id:
|
|
189
|
+
id: connectionNode.nodeId,
|
|
151
190
|
kind: "node",
|
|
152
|
-
name:
|
|
153
|
-
type:
|
|
154
|
-
role:
|
|
155
|
-
icon:
|
|
156
|
-
parentNodeId:
|
|
191
|
+
name: connectionNode.name,
|
|
192
|
+
type: connectionNode.typeName,
|
|
193
|
+
role: connectionNode.role,
|
|
194
|
+
icon: connectionNode.icon,
|
|
195
|
+
parentNodeId: connectionNode.parentNodeId,
|
|
157
196
|
};
|
|
158
197
|
}
|
|
159
198
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NodeDefinition, RetryPolicySpec, WorkflowDefinition } from "@codemation/core";
|
|
1
|
+
import type { NodeDefinition, RetryPolicySpec, WorkflowDefinition } from "@codemation/core/browser";
|
|
2
2
|
|
|
3
3
|
/** UI-facing policy labels derived from workflow/node definitions (live or hydrated snapshot). */
|
|
4
4
|
export class WorkflowPolicyUiPresentationFactory {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { WorkflowRunDetailDto } from "@codemation/core";
|
|
2
|
+
import { inject } from "@codemation/core";
|
|
3
|
+
import { ApplicationTokens } from "../../applicationTokens";
|
|
4
|
+
import type { WorkflowRunRepository } from "../../domain/runs/WorkflowRunRepository";
|
|
5
|
+
import { HandlesQuery } from "../../infrastructure/di/HandlesQueryRegistry";
|
|
6
|
+
import { QueryHandler } from "../bus/QueryHandler";
|
|
7
|
+
import { GetWorkflowRunDetailQuery } from "./GetWorkflowRunDetailQuery";
|
|
8
|
+
|
|
9
|
+
@HandlesQuery.for(GetWorkflowRunDetailQuery)
|
|
10
|
+
export class GetWorkflowRunDetailQueryHandler extends QueryHandler<
|
|
11
|
+
GetWorkflowRunDetailQuery,
|
|
12
|
+
WorkflowRunDetailDto | undefined
|
|
13
|
+
> {
|
|
14
|
+
constructor(
|
|
15
|
+
@inject(ApplicationTokens.WorkflowRunRepository)
|
|
16
|
+
private readonly workflowRunRepository: WorkflowRunRepository,
|
|
17
|
+
) {
|
|
18
|
+
super();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async execute(query: GetWorkflowRunDetailQuery): Promise<WorkflowRunDetailDto | undefined> {
|
|
22
|
+
return await this.workflowRunRepository.loadRunDetail?.(query.runId);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { GetRunBinaryAttachmentQueryHandler } from "./GetRunBinaryAttachmentQueryHandler";
|
|
2
2
|
export { GetRunStateQueryHandler } from "./GetRunStateQueryHandler";
|
|
3
|
+
export { GetWorkflowRunDetailQueryHandler } from "./GetWorkflowRunDetailQueryHandler";
|
|
3
4
|
export { GetWorkflowDebuggerOverlayQueryHandler } from "./GetWorkflowDebuggerOverlayQueryHandler";
|
|
4
5
|
export { GetWorkflowDetailQueryHandler } from "./GetWorkflowDetailQueryHandler";
|
|
5
6
|
export { GetWorkflowOverlayBinaryAttachmentQueryHandler } from "./GetWorkflowOverlayBinaryAttachmentQueryHandler";
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { BinaryStorage, Clock, RunId, WorkflowId } from "@codemation/core";
|
|
2
|
-
import { CoreTokens
|
|
2
|
+
import { CoreTokens } from "@codemation/core";
|
|
3
3
|
import { inject, injectable } from "@codemation/core";
|
|
4
4
|
import type { Logger } from "../logging/Logger";
|
|
5
|
-
import { RunStateBinaryStorageKeysCollector } from "../binary/RunStateBinaryStorageKeysCollector";
|
|
6
5
|
import { ApplicationTokens } from "../../applicationTokens";
|
|
7
6
|
import type { AppConfig } from "../../presentation/config/AppConfig";
|
|
8
7
|
import type { WorkflowRunRepository } from "../../domain/runs/WorkflowRunRepository";
|
|
@@ -17,7 +16,6 @@ import { ServerLoggerFactory } from "../../infrastructure/logging/ServerLoggerFa
|
|
|
17
16
|
export class WorkflowRunRetentionPruneScheduler {
|
|
18
17
|
private timer: ReturnType<typeof setInterval> | undefined;
|
|
19
18
|
private readonly logger: Logger;
|
|
20
|
-
private readonly binaryKeysCollector = new RunStateBinaryStorageKeysCollector();
|
|
21
19
|
|
|
22
20
|
constructor(
|
|
23
21
|
@inject(ApplicationTokens.Clock) private readonly clock: Clock,
|
|
@@ -54,36 +52,24 @@ export class WorkflowRunRetentionPruneScheduler {
|
|
|
54
52
|
/** Exposed for tests; production path is the interval started by {@link start}. */
|
|
55
53
|
async runOnce(): Promise<void> {
|
|
56
54
|
this.logger.debug("Run retention prune: starting check");
|
|
57
|
-
|
|
58
55
|
const defaultRetentionSec = Number(this.appConfig.env.CODEMATION_RUN_RETENTION_DEFAULT_SECONDS ?? 86_400);
|
|
59
|
-
const
|
|
60
|
-
const
|
|
56
|
+
const beforeIso = new Date(this.clock.now().getTime() - defaultRetentionSec * 1000).toISOString();
|
|
57
|
+
const summaries = await this.runs.listRunsOlderThan?.({ beforeIso, limit: 500 });
|
|
58
|
+
const candidates =
|
|
59
|
+
summaries ??
|
|
60
|
+
(await this.runs.listRuns({ limit: 500 })).filter(
|
|
61
|
+
(summary) => summary.status === "completed" || summary.status === "failed",
|
|
62
|
+
);
|
|
61
63
|
|
|
62
64
|
let foundCount = 0;
|
|
63
65
|
let prunedCount = 0;
|
|
64
|
-
for (const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
const state = await this.runs.load(s.runId);
|
|
69
|
-
if (!state) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
const retentionSec = state.policySnapshot?.retentionSeconds ?? defaultRetentionSec;
|
|
73
|
-
const finishedAt = RunFinishedAtFactory.resolveIso(state) ?? s.finishedAt;
|
|
74
|
-
if (!finishedAt) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
const ageMs = nowMs - Date.parse(finishedAt);
|
|
78
|
-
if (ageMs <= retentionSec * 1000) {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const runId = s.runId as RunId;
|
|
83
|
-
const workflowId = s.workflowId as WorkflowId;
|
|
66
|
+
for (const candidate of candidates) {
|
|
67
|
+
const runId = candidate.runId as RunId;
|
|
68
|
+
const workflowId = candidate.workflowId as WorkflowId;
|
|
84
69
|
foundCount += 1;
|
|
85
70
|
|
|
86
|
-
const storageKeys =
|
|
71
|
+
const storageKeys =
|
|
72
|
+
(await this.runs.listBinaryStorageKeys?.(runId)) ?? (await this.loadStorageKeysFromRunStateFallback(runId));
|
|
87
73
|
for (const key of storageKeys) {
|
|
88
74
|
await this.binaryStorage.delete(key);
|
|
89
75
|
}
|
|
@@ -95,4 +81,43 @@ export class WorkflowRunRetentionPruneScheduler {
|
|
|
95
81
|
this.logger.info(`Run retention prune: found ${foundCount} run(s) to prune`);
|
|
96
82
|
this.logger.info(`Run retention prune: pruned ${prunedCount} run(s)`);
|
|
97
83
|
}
|
|
84
|
+
|
|
85
|
+
private async loadStorageKeysFromRunStateFallback(runId: RunId): Promise<ReadonlyArray<string>> {
|
|
86
|
+
const state = await this.runs.load(runId);
|
|
87
|
+
if (!state) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
const keys = new Set<string>();
|
|
91
|
+
this.collectStorageKeysFromValue(state.outputsByNode, keys);
|
|
92
|
+
this.collectStorageKeysFromValue(state.nodeSnapshotsByNodeId, keys);
|
|
93
|
+
this.collectStorageKeysFromValue(state.mutableState, keys);
|
|
94
|
+
return [...keys];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private collectStorageKeysFromValue(value: unknown, keys: Set<string>): void {
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
for (const entry of value) {
|
|
100
|
+
this.collectStorageKeysFromValue(entry, keys);
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (!value || typeof value !== "object") {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const record = value as Record<string, unknown>;
|
|
108
|
+
if (
|
|
109
|
+
typeof record.id === "string" &&
|
|
110
|
+
typeof record.storageKey === "string" &&
|
|
111
|
+
typeof record.mimeType === "string" &&
|
|
112
|
+
typeof record.size === "number"
|
|
113
|
+
) {
|
|
114
|
+
if (record.storageKey.length > 0) {
|
|
115
|
+
keys.add(record.storageKey);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const child of Object.values(record)) {
|
|
120
|
+
this.collectStorageKeysFromValue(child, keys);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
98
123
|
}
|