@codemation/host 0.2.5 → 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 +30 -0
- package/dist/{AppConfigFactory-CqKWXqOm.js → AppConfigFactory-BPp02HMv.js} +82 -5
- package/dist/{AppConfigFactory-CqKWXqOm.js.map → AppConfigFactory-BPp02HMv.js.map} +1 -1
- package/dist/{AppConfigFactory-CK28UPK0.d.ts → AppConfigFactory-Dq7ttwQ_.d.ts} +6001 -144
- package/dist/{AppContainerFactory-CcSGFNLW.js → AppContainerFactory-D9je1sSV.js} +2604 -155
- package/dist/AppContainerFactory-D9je1sSV.js.map +1 -0
- package/dist/{CodemationAppContext-KqDoeHqN.d.ts → CodemationAppContext-P7P-xZhQ.d.ts} +2 -2
- package/dist/{CodemationAuthoring.types-BP6Inucu.d.ts → CodemationAuthoring.types-OMYu7vKP.d.ts} +3 -3
- package/dist/{CodemationConfigNormalizer-DIAE0VHw.d.ts → CodemationConfigNormalizer-BCtBrJDe.d.ts} +2 -2
- package/dist/{CodemationConsumerConfigLoader-nj9kTmfJ.d.ts → CodemationConsumerConfigLoader-evvw4b_a.d.ts} +2 -2
- package/dist/CodemationPluginListMerger-PSTtEQjC.d.ts +674 -0
- package/dist/{CredentialServices-BFQD_VN1.d.ts → CredentialServices-0Hk8RFY1.d.ts} +3 -3
- package/dist/{CredentialServices-BPKUF8Xs.js → CredentialServices-BNBMFOPt.js} +6 -1
- package/dist/CredentialServices-BNBMFOPt.js.map +1 -0
- package/dist/{PublicFrontendBootstrapFactory-DTA1iDo0.d.ts → PublicFrontendBootstrapFactory-D0_ds7nS.d.ts} +2 -2
- package/dist/authoring.d.ts +3 -3
- 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-Dd6BrWyH.d.ts → index-CeS2saCe.d.ts} +105 -2
- package/dist/index.d.ts +12 -11
- package/dist/index.js +5 -5
- package/dist/nextServer.d.ts +8 -58
- package/dist/nextServer.js +6 -100
- package/dist/{persistenceServer-DKbFDxoS.js → persistenceServer-CA0_q0D7.js} +2 -2
- package/dist/{persistenceServer-DKbFDxoS.js.map → persistenceServer-CA0_q0D7.js.map} +1 -1
- package/dist/{persistenceServer-Y-u7lV7f.d.ts → persistenceServer-CJeu1STC.d.ts} +2 -2
- package/dist/persistenceServer.d.ts +5 -5
- package/dist/persistenceServer.js +2 -2
- package/dist/{server-CFpgKuVE.js → server-C_ZIEOTY.js} +4 -4
- package/dist/{server-CFpgKuVE.js.map → server-C_ZIEOTY.js.map} +1 -1
- package/dist/{server-axppTMgo.d.ts → server-Clvg5x1w.d.ts} +11 -5
- package/dist/server.d.ts +8 -8
- package/dist/server.js +5 -5
- package/package.json +6 -5
- package/prisma/migrations/20260414120000_telemetry_foundation/migration.sql +112 -0
- package/prisma/migrations/20260414153000_telemetry_retention_metrics_refactor/migration.sql +239 -0
- package/prisma/migrations.sqlite/20260414120000_telemetry_foundation/migration.sql +103 -0
- package/prisma/migrations.sqlite/20260414153000_telemetry_retention_metrics_refactor/migration.sql +540 -0
- package/prisma/schema.postgresql.prisma +100 -1
- package/prisma/schema.sqlite.prisma +100 -1
- package/scripts/generate-prisma-clients.mjs +89 -1
- package/src/application/contracts/TelemetryDashboardContracts.ts +113 -0
- package/src/application/contracts/TelemetryRunTraceContracts.ts +13 -0
- package/src/application/cost/FrameworkCostCatalogEntries.ts +126 -0
- package/src/application/queries/GetTelemetryDashboardDimensionsQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardDimensionsQueryHandler.ts +20 -0
- package/src/application/queries/GetTelemetryDashboardRunsQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardRunsQueryHandler.ts +20 -0
- package/src/application/queries/GetTelemetryDashboardSummaryQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardSummaryQueryHandler.ts +29 -0
- package/src/application/queries/GetTelemetryDashboardTimeseriesQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardTimeseriesQueryHandler.ts +36 -0
- package/src/application/queries/GetTelemetryRunTraceQuery.ts +8 -0
- package/src/application/queries/GetTelemetryRunTraceQueryHandler.ts +20 -0
- package/src/application/queries/WorkflowQueryHandlers.ts +5 -0
- package/src/application/runs/WorkflowRunRetentionPruneScheduler.ts +71 -26
- package/src/application/telemetry/CompositeTelemetryExporter.ts +13 -0
- package/src/application/telemetry/LazyExecutionTelemetryFactory.ts +21 -0
- package/src/application/telemetry/NoOpTelemetryExporter.ts +7 -0
- package/src/application/telemetry/OtelExecutionTelemetry.types.ts +41 -0
- package/src/application/telemetry/OtelExecutionTelemetryFactory.ts +56 -0
- package/src/application/telemetry/OtelIdentityFactory.ts +41 -0
- package/src/application/telemetry/RunEventBusTelemetryReporter.ts +188 -0
- package/src/application/telemetry/StoredExecutionTelemetry.ts +56 -0
- package/src/application/telemetry/StoredNodeExecutionTelemetry.ts +35 -0
- package/src/application/telemetry/StoredTelemetrySpanScope.ts +188 -0
- package/src/application/telemetry/TelemetryEnricherChain.ts +85 -0
- package/src/application/telemetry/TelemetryPrivacyPolicy.ts +19 -0
- package/src/application/telemetry/TelemetryQueryService.ts +815 -0
- package/src/application/telemetry/TelemetryRetentionTimestampFactory.ts +40 -0
- package/src/applicationTokens.ts +18 -0
- package/src/bootstrap/AppContainerFactory.ts +124 -1
- package/src/bootstrap/AppContainerLifecycle.ts +8 -0
- package/src/bootstrap/runtime/FrontendRuntime.ts +8 -0
- package/src/bootstrap/runtime/WorkerRuntime.ts +8 -0
- package/src/domain/runs/WorkflowRunRepository.ts +3 -1
- package/src/domain/telemetry/TelemetryContracts.ts +197 -0
- package/src/infrastructure/persistence/InMemoryRunTraceContextRepository.ts +56 -0
- package/src/infrastructure/persistence/InMemoryTelemetryArtifactStore.ts +56 -0
- package/src/infrastructure/persistence/InMemoryTelemetryMetricPointStore.ts +97 -0
- package/src/infrastructure/persistence/InMemoryTelemetrySpanStore.ts +113 -0
- package/src/infrastructure/persistence/InMemoryWorkflowRunRepository.ts +4 -2
- package/src/infrastructure/persistence/PrismaRunTraceContextRepository.ts +92 -0
- package/src/infrastructure/persistence/PrismaTelemetryArtifactStore.ts +125 -0
- package/src/infrastructure/persistence/PrismaTelemetryMetricPointStore.ts +134 -0
- package/src/infrastructure/persistence/PrismaTelemetrySpanStore.ts +166 -0
- package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +20 -7
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/edge.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index-browser.js +81 -0
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index.d.ts +6488 -102
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/package.json +1 -1
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/schema.prisma +100 -0
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/edge.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index-browser.js +81 -0
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index.d.ts +6476 -98
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/package.json +1 -1
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/schema.prisma +100 -0
- package/src/presentation/http/ApiPaths.ts +22 -0
- package/src/presentation/http/hono/registrars/TelemetryHonoApiRouteRegistrar.ts +19 -0
- package/src/presentation/http/routeHandlers/TelemetryDashboardRequestError.ts +1 -0
- package/src/presentation/http/routeHandlers/TelemetryHttpRouteHandler.ts +181 -0
- package/dist/AppContainerFactory-CcSGFNLW.js.map +0 -1
- package/dist/CodemationPluginListMerger-QvUa2SIt.d.ts +0 -357
- package/dist/CredentialServices-BPKUF8Xs.js.map +0 -1
- package/dist/nextServer.js.map +0 -1
|
@@ -27,7 +27,6 @@ model Run {
|
|
|
27
27
|
workItems RunWorkItem[]
|
|
28
28
|
executionInstances ExecutionInstance[]
|
|
29
29
|
slotProjection RunSlotProjection?
|
|
30
|
-
|
|
31
30
|
@@index([workflowId, startedAt])
|
|
32
31
|
@@index([workflowId, status, finishedAt])
|
|
33
32
|
}
|
|
@@ -140,6 +139,106 @@ model TriggerSetupState {
|
|
|
140
139
|
@@id([workflowId, nodeId])
|
|
141
140
|
}
|
|
142
141
|
|
|
142
|
+
model RunTraceContext {
|
|
143
|
+
runId String @id @map("run_id")
|
|
144
|
+
workflowId String @map("workflow_id")
|
|
145
|
+
traceId String @unique @map("trace_id")
|
|
146
|
+
rootSpanId String @map("root_span_id")
|
|
147
|
+
serviceName String? @map("service_name")
|
|
148
|
+
createdAt String @map("created_at")
|
|
149
|
+
expiresAt String? @map("expires_at")
|
|
150
|
+
|
|
151
|
+
@@index([workflowId, createdAt])
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
model TelemetrySpan {
|
|
155
|
+
telemetrySpanId String @id @map("telemetry_span_id")
|
|
156
|
+
traceId String @map("trace_id")
|
|
157
|
+
spanId String @map("span_id")
|
|
158
|
+
parentSpanId String? @map("parent_span_id")
|
|
159
|
+
runId String @map("run_id")
|
|
160
|
+
workflowId String @map("workflow_id")
|
|
161
|
+
nodeId String? @map("node_id")
|
|
162
|
+
activationId String? @map("activation_id")
|
|
163
|
+
connectionInvocationId String? @map("connection_invocation_id")
|
|
164
|
+
name String
|
|
165
|
+
kind String
|
|
166
|
+
status String?
|
|
167
|
+
statusMessage String? @map("status_message")
|
|
168
|
+
startTime String? @map("start_time")
|
|
169
|
+
endTime String? @map("end_time")
|
|
170
|
+
workflowFolder String? @map("workflow_folder")
|
|
171
|
+
nodeType String? @map("node_type")
|
|
172
|
+
nodeRole String? @map("node_role")
|
|
173
|
+
modelName String? @map("model_name")
|
|
174
|
+
attributesJson String? @map("attributes_json")
|
|
175
|
+
eventsJson String? @map("events_json")
|
|
176
|
+
retentionExpiresAt String? @map("retention_expires_at")
|
|
177
|
+
updatedAt String @map("updated_at")
|
|
178
|
+
|
|
179
|
+
@@unique([traceId, spanId])
|
|
180
|
+
@@index([traceId, startTime])
|
|
181
|
+
@@index([workflowId, endTime])
|
|
182
|
+
@@index([workflowId, status, endTime])
|
|
183
|
+
@@index([runId, endTime])
|
|
184
|
+
@@index([modelName, endTime])
|
|
185
|
+
@@index([connectionInvocationId])
|
|
186
|
+
@@index([retentionExpiresAt])
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
model TelemetryArtifact {
|
|
190
|
+
artifactId String @id @map("artifact_id")
|
|
191
|
+
traceId String @map("trace_id")
|
|
192
|
+
spanId String @map("span_id")
|
|
193
|
+
runId String @map("run_id")
|
|
194
|
+
workflowId String @map("workflow_id")
|
|
195
|
+
nodeId String? @map("node_id")
|
|
196
|
+
activationId String? @map("activation_id")
|
|
197
|
+
kind String
|
|
198
|
+
contentType String @map("content_type")
|
|
199
|
+
previewText String? @map("preview_text")
|
|
200
|
+
previewJson String? @map("preview_json")
|
|
201
|
+
payloadText String? @map("payload_text")
|
|
202
|
+
payloadJson String? @map("payload_json")
|
|
203
|
+
bytes Int?
|
|
204
|
+
truncated Boolean?
|
|
205
|
+
createdAt String @map("created_at")
|
|
206
|
+
expiresAt String? @map("expires_at")
|
|
207
|
+
retentionExpiresAt String? @map("retention_expires_at")
|
|
208
|
+
|
|
209
|
+
@@index([traceId, createdAt])
|
|
210
|
+
@@index([spanId, createdAt])
|
|
211
|
+
@@index([runId, createdAt])
|
|
212
|
+
@@index([retentionExpiresAt])
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
model TelemetryMetricPoint {
|
|
216
|
+
metricPointId String @id @map("metric_point_id")
|
|
217
|
+
traceId String? @map("trace_id")
|
|
218
|
+
spanId String? @map("span_id")
|
|
219
|
+
runId String? @map("run_id")
|
|
220
|
+
workflowId String @map("workflow_id")
|
|
221
|
+
nodeId String? @map("node_id")
|
|
222
|
+
activationId String? @map("activation_id")
|
|
223
|
+
metricName String @map("metric_name")
|
|
224
|
+
value Float
|
|
225
|
+
unit String?
|
|
226
|
+
observedAt String @map("observed_at")
|
|
227
|
+
workflowFolder String? @map("workflow_folder")
|
|
228
|
+
nodeType String? @map("node_type")
|
|
229
|
+
nodeRole String? @map("node_role")
|
|
230
|
+
modelName String? @map("model_name")
|
|
231
|
+
dimensionsJson String? @map("dimensions_json")
|
|
232
|
+
retentionExpiresAt String? @map("retention_expires_at")
|
|
233
|
+
|
|
234
|
+
@@index([workflowId, observedAt])
|
|
235
|
+
@@index([workflowId, metricName, observedAt])
|
|
236
|
+
@@index([runId, observedAt])
|
|
237
|
+
@@index([traceId, observedAt])
|
|
238
|
+
@@index([modelName, observedAt])
|
|
239
|
+
@@index([retentionExpiresAt])
|
|
240
|
+
}
|
|
241
|
+
|
|
143
242
|
model CredentialInstance {
|
|
144
243
|
instanceId String @id @map("instance_id")
|
|
145
244
|
typeId String @map("type_id")
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
2
7
|
|
|
3
8
|
class PrismaClientGenerator {
|
|
4
9
|
static providers = ["postgresql", "sqlite"];
|
|
10
|
+
static prismaCliEntrypoint = require.resolve("prisma/build/index.js");
|
|
11
|
+
static minimumNodeVersion = { major: 20, minor: 19 };
|
|
12
|
+
static reexecMarker = "CODEMATION_PRISMA_GENERATE_REEXEC";
|
|
5
13
|
|
|
6
14
|
static run() {
|
|
15
|
+
this.reexecUnderSupportedNodeWhenNeeded();
|
|
7
16
|
for (const provider of this.providers) {
|
|
8
|
-
const result = spawnSync(
|
|
17
|
+
const result = spawnSync(process.execPath, [this.prismaCliEntrypoint, "generate"], {
|
|
9
18
|
cwd: import.meta.dirname.replace(/\/scripts$/, ""),
|
|
10
19
|
env: {
|
|
11
20
|
...process.env,
|
|
@@ -19,6 +28,85 @@ class PrismaClientGenerator {
|
|
|
19
28
|
}
|
|
20
29
|
}
|
|
21
30
|
}
|
|
31
|
+
|
|
32
|
+
static reexecUnderSupportedNodeWhenNeeded() {
|
|
33
|
+
if (process.env[this.reexecMarker] === "1" || this.isSupportedNode(process.versions.node)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const supportedNodeBinary = this.resolveSupportedNodeBinary();
|
|
37
|
+
if (!supportedNodeBinary) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Prisma generation requires Node >= ${this.minimumNodeVersion.major}.${this.minimumNodeVersion.minor}. ` +
|
|
40
|
+
`Current process is ${process.versions.node} and no supported Node binary could be resolved.`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const result = spawnSync(supportedNodeBinary, [new URL(import.meta.url).pathname], {
|
|
44
|
+
cwd: import.meta.dirname.replace(/\/scripts$/, ""),
|
|
45
|
+
env: {
|
|
46
|
+
...process.env,
|
|
47
|
+
[this.reexecMarker]: "1",
|
|
48
|
+
},
|
|
49
|
+
stdio: "inherit",
|
|
50
|
+
shell: false,
|
|
51
|
+
});
|
|
52
|
+
process.exit(result.status ?? 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static resolveSupportedNodeBinary() {
|
|
56
|
+
const pnpmPath = this.resolvePnpmPath();
|
|
57
|
+
if (!pnpmPath) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const pnpmDir = path.dirname(pnpmPath);
|
|
61
|
+
const candidates = [path.join(pnpmDir, "node"), path.resolve(pnpmDir, "../../../../bin/node")];
|
|
62
|
+
for (const candidate of candidates) {
|
|
63
|
+
if (!existsSync(candidate)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const versionResult = spawnSync(candidate, ["-p", "process.versions.node"], {
|
|
67
|
+
env: process.env,
|
|
68
|
+
encoding: "utf8",
|
|
69
|
+
shell: false,
|
|
70
|
+
});
|
|
71
|
+
if (versionResult.status === 0 && this.isSupportedNode(versionResult.stdout.trim())) {
|
|
72
|
+
return candidate;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static resolvePnpmPath() {
|
|
79
|
+
const npmExecPath = process.env.npm_execpath?.trim();
|
|
80
|
+
if (npmExecPath) {
|
|
81
|
+
return realpathSync(npmExecPath);
|
|
82
|
+
}
|
|
83
|
+
const result = spawnSync("bash", ["-lc", 'realpath "$(command -v pnpm)"'], {
|
|
84
|
+
env: process.env,
|
|
85
|
+
encoding: "utf8",
|
|
86
|
+
shell: false,
|
|
87
|
+
});
|
|
88
|
+
if (result.status !== 0) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
const pnpmPath = result.stdout.trim();
|
|
92
|
+
return pnpmPath.length > 0 ? pnpmPath : undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static isSupportedNode(version) {
|
|
96
|
+
const [majorText = "0", minorText = "0"] = version.split(".");
|
|
97
|
+
const major = Number(majorText);
|
|
98
|
+
const minor = Number(minorText);
|
|
99
|
+
if (!Number.isInteger(major) || !Number.isInteger(minor)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (major > this.minimumNodeVersion.major) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (major < this.minimumNodeVersion.major) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return minor >= this.minimumNodeVersion.minor;
|
|
109
|
+
}
|
|
22
110
|
}
|
|
23
111
|
|
|
24
112
|
PrismaClientGenerator.run();
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { TelemetrySpanStatus } from "../../domain/telemetry/TelemetryContracts";
|
|
2
|
+
|
|
3
|
+
export type TelemetryDashboardBucketIntervalDto = "minute_5" | "minute_15" | "hour" | "day" | "week";
|
|
4
|
+
export type TelemetryDashboardRunOriginDto = "triggered" | "manual";
|
|
5
|
+
|
|
6
|
+
export interface TelemetryDashboardFiltersDto {
|
|
7
|
+
readonly workflowIds?: ReadonlyArray<string>;
|
|
8
|
+
readonly statuses?: ReadonlyArray<TelemetrySpanStatus>;
|
|
9
|
+
readonly runOrigins?: ReadonlyArray<TelemetryDashboardRunOriginDto>;
|
|
10
|
+
readonly modelNames?: ReadonlyArray<string>;
|
|
11
|
+
readonly startTimeGte?: string;
|
|
12
|
+
readonly endTimeLte?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TelemetryDashboardRunAggregateDto {
|
|
16
|
+
readonly totalRuns: number;
|
|
17
|
+
readonly completedRuns: number;
|
|
18
|
+
readonly failedRuns: number;
|
|
19
|
+
readonly runningRuns: number;
|
|
20
|
+
readonly averageDurationMs: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TelemetryDashboardAiAggregateDto {
|
|
24
|
+
readonly inputTokens: number;
|
|
25
|
+
readonly outputTokens: number;
|
|
26
|
+
readonly totalTokens: number;
|
|
27
|
+
readonly cachedInputTokens: number;
|
|
28
|
+
readonly reasoningTokens: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface TelemetryDashboardCostKeyTotalDto {
|
|
32
|
+
readonly costKey: string;
|
|
33
|
+
readonly estimatedCostMinor: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TelemetryDashboardCostCurrencyTotalDto {
|
|
37
|
+
readonly currency: string;
|
|
38
|
+
readonly currencyScale: number;
|
|
39
|
+
readonly estimatedCostMinor: number;
|
|
40
|
+
readonly averageCostPerRunMinor: number;
|
|
41
|
+
readonly costKeys: ReadonlyArray<TelemetryDashboardCostKeyTotalDto>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface TelemetryDashboardCostAggregateDto {
|
|
45
|
+
readonly currencies: ReadonlyArray<TelemetryDashboardCostCurrencyTotalDto>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface TelemetryDashboardSummaryDto {
|
|
49
|
+
readonly runs: TelemetryDashboardRunAggregateDto;
|
|
50
|
+
readonly ai: TelemetryDashboardAiAggregateDto;
|
|
51
|
+
readonly costs: TelemetryDashboardCostAggregateDto;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface TelemetryDashboardBucketCostDto {
|
|
55
|
+
readonly currency: string;
|
|
56
|
+
readonly currencyScale: number;
|
|
57
|
+
readonly estimatedCostMinor: number;
|
|
58
|
+
readonly component?: string;
|
|
59
|
+
readonly costKey?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface TelemetryDashboardTimeseriesBucketDto {
|
|
63
|
+
readonly bucketStartIso: string;
|
|
64
|
+
readonly bucketEndIso: string;
|
|
65
|
+
readonly totalRuns: number;
|
|
66
|
+
readonly completedRuns: number;
|
|
67
|
+
readonly failedRuns: number;
|
|
68
|
+
readonly runningRuns: number;
|
|
69
|
+
readonly averageDurationMs: number;
|
|
70
|
+
readonly inputTokens: number;
|
|
71
|
+
readonly outputTokens: number;
|
|
72
|
+
readonly totalTokens: number;
|
|
73
|
+
readonly cachedInputTokens: number;
|
|
74
|
+
readonly reasoningTokens: number;
|
|
75
|
+
readonly costs: ReadonlyArray<TelemetryDashboardBucketCostDto>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface TelemetryDashboardTimeseriesDto {
|
|
79
|
+
readonly interval: TelemetryDashboardBucketIntervalDto;
|
|
80
|
+
readonly buckets: ReadonlyArray<TelemetryDashboardTimeseriesBucketDto>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface TelemetryDashboardDimensionsDto {
|
|
84
|
+
readonly modelNames: ReadonlyArray<string>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface TelemetryDashboardTimeseriesRequestDto {
|
|
88
|
+
readonly filters: TelemetryDashboardFiltersDto;
|
|
89
|
+
readonly interval: TelemetryDashboardBucketIntervalDto;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface TelemetryDashboardRunsRequestDto {
|
|
93
|
+
readonly filters: TelemetryDashboardFiltersDto;
|
|
94
|
+
readonly page: number;
|
|
95
|
+
readonly pageSize: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface TelemetryDashboardRunListItemDto {
|
|
99
|
+
readonly runId: string;
|
|
100
|
+
readonly workflowId: string;
|
|
101
|
+
readonly status: TelemetrySpanStatus;
|
|
102
|
+
readonly origin: TelemetryDashboardRunOriginDto;
|
|
103
|
+
readonly startedAt: string;
|
|
104
|
+
readonly finishedAt?: string;
|
|
105
|
+
readonly costs?: ReadonlyArray<TelemetryDashboardBucketCostDto>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface TelemetryDashboardRunsDto {
|
|
109
|
+
readonly items: ReadonlyArray<TelemetryDashboardRunListItemDto>;
|
|
110
|
+
readonly totalCount: number;
|
|
111
|
+
readonly page: number;
|
|
112
|
+
readonly pageSize: number;
|
|
113
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TelemetryArtifactRecord,
|
|
3
|
+
TelemetryMetricPointRecord,
|
|
4
|
+
TelemetrySpanRecord,
|
|
5
|
+
} from "../../domain/telemetry/TelemetryContracts";
|
|
6
|
+
|
|
7
|
+
export interface TelemetryRunTraceViewDto {
|
|
8
|
+
readonly traceId: string;
|
|
9
|
+
readonly runId: string;
|
|
10
|
+
readonly spans: ReadonlyArray<TelemetrySpanRecord>;
|
|
11
|
+
readonly artifacts: ReadonlyArray<TelemetryArtifactRecord>;
|
|
12
|
+
readonly metricPoints: ReadonlyArray<TelemetryMetricPointRecord>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { CostCatalogEntry } from "@codemation/core";
|
|
2
|
+
|
|
3
|
+
const currencyScale = 1_000_000_000;
|
|
4
|
+
|
|
5
|
+
export const FrameworkCostCatalogEntries: ReadonlyArray<CostCatalogEntry> = [
|
|
6
|
+
{
|
|
7
|
+
component: "chat",
|
|
8
|
+
provider: "openai",
|
|
9
|
+
operation: "completion.input",
|
|
10
|
+
pricingKey: "demo-gpt",
|
|
11
|
+
usageUnit: "input_tokens",
|
|
12
|
+
currency: "USD",
|
|
13
|
+
currencyScale,
|
|
14
|
+
pricePerUnitMinor: 1_000,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
component: "chat",
|
|
18
|
+
provider: "openai",
|
|
19
|
+
operation: "completion.output",
|
|
20
|
+
pricingKey: "demo-gpt",
|
|
21
|
+
usageUnit: "output_tokens",
|
|
22
|
+
currency: "USD",
|
|
23
|
+
currencyScale,
|
|
24
|
+
pricePerUnitMinor: 2_000,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
component: "chat",
|
|
28
|
+
provider: "openai",
|
|
29
|
+
operation: "completion.input",
|
|
30
|
+
pricingKey: "gpt-4o-mini",
|
|
31
|
+
usageUnit: "input_tokens",
|
|
32
|
+
currency: "USD",
|
|
33
|
+
currencyScale,
|
|
34
|
+
pricePerUnitMinor: 150,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
component: "chat",
|
|
38
|
+
provider: "openai",
|
|
39
|
+
operation: "completion.output",
|
|
40
|
+
pricingKey: "gpt-4o-mini",
|
|
41
|
+
usageUnit: "output_tokens",
|
|
42
|
+
currency: "USD",
|
|
43
|
+
currencyScale,
|
|
44
|
+
pricePerUnitMinor: 600,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
component: "chat",
|
|
48
|
+
provider: "openai",
|
|
49
|
+
operation: "completion.input",
|
|
50
|
+
pricingKey: "gpt-4.1-nano",
|
|
51
|
+
usageUnit: "input_tokens",
|
|
52
|
+
currency: "USD",
|
|
53
|
+
currencyScale,
|
|
54
|
+
pricePerUnitMinor: 100,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
component: "chat",
|
|
58
|
+
provider: "openai",
|
|
59
|
+
operation: "completion.output",
|
|
60
|
+
pricingKey: "gpt-4.1-nano",
|
|
61
|
+
usageUnit: "output_tokens",
|
|
62
|
+
currency: "USD",
|
|
63
|
+
currencyScale,
|
|
64
|
+
pricePerUnitMinor: 400,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
component: "chat",
|
|
68
|
+
provider: "openai",
|
|
69
|
+
operation: "completion.input",
|
|
70
|
+
pricingKey: "gpt-4.1-mini",
|
|
71
|
+
usageUnit: "input_tokens",
|
|
72
|
+
currency: "USD",
|
|
73
|
+
currencyScale,
|
|
74
|
+
pricePerUnitMinor: 400,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
component: "chat",
|
|
78
|
+
provider: "openai",
|
|
79
|
+
operation: "completion.output",
|
|
80
|
+
pricingKey: "gpt-4.1-mini",
|
|
81
|
+
usageUnit: "output_tokens",
|
|
82
|
+
currency: "USD",
|
|
83
|
+
currencyScale,
|
|
84
|
+
pricePerUnitMinor: 1_600,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
component: "chat",
|
|
88
|
+
provider: "openai",
|
|
89
|
+
operation: "completion.input",
|
|
90
|
+
pricingKey: "gpt-4.1",
|
|
91
|
+
usageUnit: "input_tokens",
|
|
92
|
+
currency: "USD",
|
|
93
|
+
currencyScale,
|
|
94
|
+
pricePerUnitMinor: 2_000,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
component: "chat",
|
|
98
|
+
provider: "openai",
|
|
99
|
+
operation: "completion.output",
|
|
100
|
+
pricingKey: "gpt-4.1",
|
|
101
|
+
usageUnit: "output_tokens",
|
|
102
|
+
currency: "USD",
|
|
103
|
+
currencyScale,
|
|
104
|
+
pricePerUnitMinor: 8_000,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
component: "rag",
|
|
108
|
+
provider: "openai",
|
|
109
|
+
operation: "embed_documents",
|
|
110
|
+
pricingKey: "text-embedding-3-small",
|
|
111
|
+
usageUnit: "input_tokens",
|
|
112
|
+
currency: "USD",
|
|
113
|
+
currencyScale,
|
|
114
|
+
pricePerUnitMinor: 20,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
component: "ocr",
|
|
118
|
+
provider: "azure_document_intelligence",
|
|
119
|
+
operation: "analyze_document",
|
|
120
|
+
pricingKey: "prebuilt_read",
|
|
121
|
+
usageUnit: "pages",
|
|
122
|
+
currency: "EUR",
|
|
123
|
+
currencyScale,
|
|
124
|
+
pricePerUnitMinor: 15_000_000,
|
|
125
|
+
},
|
|
126
|
+
] as const;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TelemetryDashboardDimensionsDto,
|
|
3
|
+
TelemetryDashboardFiltersDto,
|
|
4
|
+
} from "../contracts/TelemetryDashboardContracts";
|
|
5
|
+
import { Query } from "../bus/Query";
|
|
6
|
+
|
|
7
|
+
export class GetTelemetryDashboardDimensionsQuery extends Query<TelemetryDashboardDimensionsDto> {
|
|
8
|
+
constructor(public readonly filters: TelemetryDashboardFiltersDto) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { inject } from "@codemation/core";
|
|
2
|
+
import type { TelemetryDashboardDimensionsDto } from "../contracts/TelemetryDashboardContracts";
|
|
3
|
+
import { HandlesQuery } from "../../infrastructure/di/HandlesQueryRegistry";
|
|
4
|
+
import { QueryHandler } from "../bus/QueryHandler";
|
|
5
|
+
import { TelemetryQueryService } from "../telemetry/TelemetryQueryService";
|
|
6
|
+
import { GetTelemetryDashboardDimensionsQuery } from "./GetTelemetryDashboardDimensionsQuery";
|
|
7
|
+
|
|
8
|
+
@HandlesQuery.for(GetTelemetryDashboardDimensionsQuery)
|
|
9
|
+
export class GetTelemetryDashboardDimensionsQueryHandler extends QueryHandler<
|
|
10
|
+
GetTelemetryDashboardDimensionsQuery,
|
|
11
|
+
TelemetryDashboardDimensionsDto
|
|
12
|
+
> {
|
|
13
|
+
constructor(@inject(TelemetryQueryService) private readonly telemetryQueryService: TelemetryQueryService) {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async execute(query: GetTelemetryDashboardDimensionsQuery): Promise<TelemetryDashboardDimensionsDto> {
|
|
18
|
+
return await this.telemetryQueryService.listModelNames(query.filters);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TelemetryDashboardRunsDto,
|
|
3
|
+
TelemetryDashboardRunsRequestDto,
|
|
4
|
+
} from "../contracts/TelemetryDashboardContracts";
|
|
5
|
+
import { Query } from "../bus/Query";
|
|
6
|
+
|
|
7
|
+
export class GetTelemetryDashboardRunsQuery extends Query<TelemetryDashboardRunsDto> {
|
|
8
|
+
constructor(public readonly request: TelemetryDashboardRunsRequestDto) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { inject } from "@codemation/core";
|
|
2
|
+
import type { TelemetryDashboardRunsDto } from "../contracts/TelemetryDashboardContracts";
|
|
3
|
+
import { HandlesQuery } from "../../infrastructure/di/HandlesQueryRegistry";
|
|
4
|
+
import { QueryHandler } from "../bus/QueryHandler";
|
|
5
|
+
import { TelemetryQueryService } from "../telemetry/TelemetryQueryService";
|
|
6
|
+
import { GetTelemetryDashboardRunsQuery } from "./GetTelemetryDashboardRunsQuery";
|
|
7
|
+
|
|
8
|
+
@HandlesQuery.for(GetTelemetryDashboardRunsQuery)
|
|
9
|
+
export class GetTelemetryDashboardRunsQueryHandler extends QueryHandler<
|
|
10
|
+
GetTelemetryDashboardRunsQuery,
|
|
11
|
+
TelemetryDashboardRunsDto
|
|
12
|
+
> {
|
|
13
|
+
constructor(@inject(TelemetryQueryService) private readonly telemetryQueryService: TelemetryQueryService) {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async execute(query: GetTelemetryDashboardRunsQuery): Promise<TelemetryDashboardRunsDto> {
|
|
18
|
+
return await this.telemetryQueryService.listRuns(query.request);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TelemetryDashboardFiltersDto,
|
|
3
|
+
TelemetryDashboardSummaryDto,
|
|
4
|
+
} from "../contracts/TelemetryDashboardContracts";
|
|
5
|
+
import { Query } from "../bus/Query";
|
|
6
|
+
|
|
7
|
+
export class GetTelemetryDashboardSummaryQuery extends Query<TelemetryDashboardSummaryDto> {
|
|
8
|
+
constructor(public readonly filters: TelemetryDashboardFiltersDto) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { inject } from "@codemation/core";
|
|
2
|
+
import type { TelemetryDashboardSummaryDto } from "../contracts/TelemetryDashboardContracts";
|
|
3
|
+
import { HandlesQuery } from "../../infrastructure/di/HandlesQueryRegistry";
|
|
4
|
+
import { QueryHandler } from "../bus/QueryHandler";
|
|
5
|
+
import { TelemetryQueryService } from "../telemetry/TelemetryQueryService";
|
|
6
|
+
import { GetTelemetryDashboardSummaryQuery } from "./GetTelemetryDashboardSummaryQuery";
|
|
7
|
+
|
|
8
|
+
@HandlesQuery.for(GetTelemetryDashboardSummaryQuery)
|
|
9
|
+
export class GetTelemetryDashboardSummaryQueryHandler extends QueryHandler<
|
|
10
|
+
GetTelemetryDashboardSummaryQuery,
|
|
11
|
+
TelemetryDashboardSummaryDto
|
|
12
|
+
> {
|
|
13
|
+
constructor(@inject(TelemetryQueryService) private readonly telemetryQueryService: TelemetryQueryService) {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async execute(query: GetTelemetryDashboardSummaryQuery): Promise<TelemetryDashboardSummaryDto> {
|
|
18
|
+
const [runs, ai, costs] = await Promise.all([
|
|
19
|
+
this.telemetryQueryService.summarizeRuns(query.filters),
|
|
20
|
+
this.telemetryQueryService.summarizeAiUsage(query.filters),
|
|
21
|
+
this.telemetryQueryService.summarizeCosts(query.filters),
|
|
22
|
+
]);
|
|
23
|
+
return {
|
|
24
|
+
runs,
|
|
25
|
+
ai,
|
|
26
|
+
costs,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TelemetryDashboardTimeseriesDto,
|
|
3
|
+
TelemetryDashboardTimeseriesRequestDto,
|
|
4
|
+
} from "../contracts/TelemetryDashboardContracts";
|
|
5
|
+
import { Query } from "../bus/Query";
|
|
6
|
+
|
|
7
|
+
export class GetTelemetryDashboardTimeseriesQuery extends Query<TelemetryDashboardTimeseriesDto> {
|
|
8
|
+
constructor(public readonly request: TelemetryDashboardTimeseriesRequestDto) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { inject } from "@codemation/core";
|
|
2
|
+
import type { TelemetryDashboardTimeseriesDto } from "../contracts/TelemetryDashboardContracts";
|
|
3
|
+
import { HandlesQuery } from "../../infrastructure/di/HandlesQueryRegistry";
|
|
4
|
+
import { QueryHandler } from "../bus/QueryHandler";
|
|
5
|
+
import { TelemetryQueryService } from "../telemetry/TelemetryQueryService";
|
|
6
|
+
import { GetTelemetryDashboardTimeseriesQuery } from "./GetTelemetryDashboardTimeseriesQuery";
|
|
7
|
+
|
|
8
|
+
@HandlesQuery.for(GetTelemetryDashboardTimeseriesQuery)
|
|
9
|
+
export class GetTelemetryDashboardTimeseriesQueryHandler extends QueryHandler<
|
|
10
|
+
GetTelemetryDashboardTimeseriesQuery,
|
|
11
|
+
TelemetryDashboardTimeseriesDto
|
|
12
|
+
> {
|
|
13
|
+
constructor(@inject(TelemetryQueryService) private readonly telemetryQueryService: TelemetryQueryService) {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async execute(query: GetTelemetryDashboardTimeseriesQuery): Promise<TelemetryDashboardTimeseriesDto> {
|
|
18
|
+
const [runSeries, aiSeries, costSeries] = await Promise.all([
|
|
19
|
+
this.telemetryQueryService.summarizeRunsTimeseries(query.request.filters, query.request.interval),
|
|
20
|
+
this.telemetryQueryService.summarizeAiUsageTimeseries(query.request.filters, query.request.interval),
|
|
21
|
+
this.telemetryQueryService.summarizeCostsTimeseries(query.request.filters, query.request.interval),
|
|
22
|
+
]);
|
|
23
|
+
return {
|
|
24
|
+
interval: query.request.interval,
|
|
25
|
+
buckets: runSeries.buckets.map((runBucket, index) => ({
|
|
26
|
+
...runBucket,
|
|
27
|
+
inputTokens: aiSeries.buckets[index]?.inputTokens ?? 0,
|
|
28
|
+
outputTokens: aiSeries.buckets[index]?.outputTokens ?? 0,
|
|
29
|
+
totalTokens: aiSeries.buckets[index]?.totalTokens ?? 0,
|
|
30
|
+
cachedInputTokens: aiSeries.buckets[index]?.cachedInputTokens ?? 0,
|
|
31
|
+
reasoningTokens: aiSeries.buckets[index]?.reasoningTokens ?? 0,
|
|
32
|
+
costs: costSeries.buckets[index]?.costs ?? [],
|
|
33
|
+
})),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TelemetryRunTraceViewDto } from "../contracts/TelemetryRunTraceContracts";
|
|
2
|
+
import { Query } from "../bus/Query";
|
|
3
|
+
|
|
4
|
+
export class GetTelemetryRunTraceQuery extends Query<TelemetryRunTraceViewDto> {
|
|
5
|
+
constructor(public readonly runId: string) {
|
|
6
|
+
super();
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { inject } from "@codemation/core";
|
|
2
|
+
import type { TelemetryRunTraceViewDto } from "../contracts/TelemetryRunTraceContracts";
|
|
3
|
+
import { HandlesQuery } from "../../infrastructure/di/HandlesQueryRegistry";
|
|
4
|
+
import { QueryHandler } from "../bus/QueryHandler";
|
|
5
|
+
import { TelemetryQueryService } from "../telemetry/TelemetryQueryService";
|
|
6
|
+
import { GetTelemetryRunTraceQuery } from "./GetTelemetryRunTraceQuery";
|
|
7
|
+
|
|
8
|
+
@HandlesQuery.for(GetTelemetryRunTraceQuery)
|
|
9
|
+
export class GetTelemetryRunTraceQueryHandler extends QueryHandler<
|
|
10
|
+
GetTelemetryRunTraceQuery,
|
|
11
|
+
TelemetryRunTraceViewDto
|
|
12
|
+
> {
|
|
13
|
+
constructor(@inject(TelemetryQueryService) private readonly telemetryQueryService: TelemetryQueryService) {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async execute(query: GetTelemetryRunTraceQuery): Promise<TelemetryRunTraceViewDto> {
|
|
18
|
+
return await this.telemetryQueryService.loadRunTrace(query.runId);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export { GetRunBinaryAttachmentQueryHandler } from "./GetRunBinaryAttachmentQueryHandler";
|
|
2
2
|
export { GetRunStateQueryHandler } from "./GetRunStateQueryHandler";
|
|
3
|
+
export { GetTelemetryDashboardDimensionsQueryHandler } from "./GetTelemetryDashboardDimensionsQueryHandler";
|
|
4
|
+
export { GetTelemetryDashboardRunsQueryHandler } from "./GetTelemetryDashboardRunsQueryHandler";
|
|
5
|
+
export { GetTelemetryRunTraceQueryHandler } from "./GetTelemetryRunTraceQueryHandler";
|
|
6
|
+
export { GetTelemetryDashboardSummaryQueryHandler } from "./GetTelemetryDashboardSummaryQueryHandler";
|
|
7
|
+
export { GetTelemetryDashboardTimeseriesQueryHandler } from "./GetTelemetryDashboardTimeseriesQueryHandler";
|
|
3
8
|
export { GetWorkflowRunDetailQueryHandler } from "./GetWorkflowRunDetailQueryHandler";
|
|
4
9
|
export { GetWorkflowDebuggerOverlayQueryHandler } from "./GetWorkflowDebuggerOverlayQueryHandler";
|
|
5
10
|
export { GetWorkflowDetailQueryHandler } from "./GetWorkflowDetailQueryHandler";
|