@isaacwasserman/remora 0.1.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.
Files changed (73) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +99 -0
  3. package/dist/compiler/index.d.ts +8 -0
  4. package/dist/compiler/index.d.ts.map +1 -0
  5. package/dist/compiler/passes/apply-best-practices.d.ts +19 -0
  6. package/dist/compiler/passes/apply-best-practices.d.ts.map +1 -0
  7. package/dist/compiler/passes/build-graph.d.ts +7 -0
  8. package/dist/compiler/passes/build-graph.d.ts.map +1 -0
  9. package/dist/compiler/passes/generate-constrained-tool-schemas.d.ts +4 -0
  10. package/dist/compiler/passes/generate-constrained-tool-schemas.d.ts.map +1 -0
  11. package/dist/compiler/passes/validate-control-flow.d.ts +4 -0
  12. package/dist/compiler/passes/validate-control-flow.d.ts.map +1 -0
  13. package/dist/compiler/passes/validate-foreach-target.d.ts +6 -0
  14. package/dist/compiler/passes/validate-foreach-target.d.ts.map +1 -0
  15. package/dist/compiler/passes/validate-jmespath.d.ts +4 -0
  16. package/dist/compiler/passes/validate-jmespath.d.ts.map +1 -0
  17. package/dist/compiler/passes/validate-references.d.ts +4 -0
  18. package/dist/compiler/passes/validate-references.d.ts.map +1 -0
  19. package/dist/compiler/passes/validate-tools.d.ts +4 -0
  20. package/dist/compiler/passes/validate-tools.d.ts.map +1 -0
  21. package/dist/compiler/types.d.ts +49 -0
  22. package/dist/compiler/types.d.ts.map +1 -0
  23. package/dist/compiler/utils/graph.d.ts +35 -0
  24. package/dist/compiler/utils/graph.d.ts.map +1 -0
  25. package/dist/compiler/utils/jmespath-helpers.d.ts +34 -0
  26. package/dist/compiler/utils/jmespath-helpers.d.ts.map +1 -0
  27. package/dist/executor/errors.d.ts +32 -0
  28. package/dist/executor/errors.d.ts.map +1 -0
  29. package/dist/executor/index.d.ts +20 -0
  30. package/dist/executor/index.d.ts.map +1 -0
  31. package/dist/generator/index.d.ts +24 -0
  32. package/dist/generator/index.d.ts.map +1 -0
  33. package/dist/generator/prompt.d.ts +6 -0
  34. package/dist/generator/prompt.d.ts.map +1 -0
  35. package/dist/lib.d.ts +11 -0
  36. package/dist/lib.d.ts.map +1 -0
  37. package/dist/lib.js +1973 -0
  38. package/dist/lib.js.map +25 -0
  39. package/dist/types.d.ts +234 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/viewer/edges/workflow-edge.d.ts +3 -0
  42. package/dist/viewer/edges/workflow-edge.d.ts.map +1 -0
  43. package/dist/viewer/graph-layout.d.ts +13 -0
  44. package/dist/viewer/graph-layout.d.ts.map +1 -0
  45. package/dist/viewer/index.d.ts +5 -0
  46. package/dist/viewer/index.d.ts.map +1 -0
  47. package/dist/viewer/index.js +1554 -0
  48. package/dist/viewer/index.js.map +23 -0
  49. package/dist/viewer/nodes/base-node.d.ts +17 -0
  50. package/dist/viewer/nodes/base-node.d.ts.map +1 -0
  51. package/dist/viewer/nodes/end-node.d.ts +3 -0
  52. package/dist/viewer/nodes/end-node.d.ts.map +1 -0
  53. package/dist/viewer/nodes/extract-data-node.d.ts +3 -0
  54. package/dist/viewer/nodes/extract-data-node.d.ts.map +1 -0
  55. package/dist/viewer/nodes/for-each-node.d.ts +3 -0
  56. package/dist/viewer/nodes/for-each-node.d.ts.map +1 -0
  57. package/dist/viewer/nodes/group-header-node.d.ts +3 -0
  58. package/dist/viewer/nodes/group-header-node.d.ts.map +1 -0
  59. package/dist/viewer/nodes/llm-prompt-node.d.ts +3 -0
  60. package/dist/viewer/nodes/llm-prompt-node.d.ts.map +1 -0
  61. package/dist/viewer/nodes/start-node.d.ts +3 -0
  62. package/dist/viewer/nodes/start-node.d.ts.map +1 -0
  63. package/dist/viewer/nodes/start-step-node.d.ts +3 -0
  64. package/dist/viewer/nodes/start-step-node.d.ts.map +1 -0
  65. package/dist/viewer/nodes/switch-case-node.d.ts +3 -0
  66. package/dist/viewer/nodes/switch-case-node.d.ts.map +1 -0
  67. package/dist/viewer/nodes/tool-call-node.d.ts +3 -0
  68. package/dist/viewer/nodes/tool-call-node.d.ts.map +1 -0
  69. package/dist/viewer/panels/step-detail-panel.d.ts +10 -0
  70. package/dist/viewer/panels/step-detail-panel.d.ts.map +1 -0
  71. package/dist/viewer/workflow-viewer.d.ts +9 -0
  72. package/dist/viewer/workflow-viewer.d.ts.map +1 -0
  73. package/package.json +78 -0
@@ -0,0 +1,1554 @@
1
+ // src/viewer/graph-layout.ts
2
+ import dagre from "@dagrejs/dagre";
3
+ var NODE_WIDTH = 300;
4
+ var NODE_HEIGHT = 180;
5
+ var END_NODE_SIZE = 60;
6
+ var START_NODE_SIZE = 60;
7
+ var GROUP_HEADER_WIDTH = 280;
8
+ var GROUP_HEADER_HEIGHT = 80;
9
+ var GROUP_PADDING = 30;
10
+ var GROUP_HEADER = 40;
11
+ var START_NODE_ID = "__start__";
12
+ function getOrThrow(map, key) {
13
+ const val = map.get(key);
14
+ if (val === undefined)
15
+ throw new Error(`Missing map key: ${String(key)}`);
16
+ return val;
17
+ }
18
+ function stepNodeType(step) {
19
+ switch (step.type) {
20
+ case "tool-call":
21
+ return "toolCall";
22
+ case "llm-prompt":
23
+ return "llmPrompt";
24
+ case "extract-data":
25
+ return "extractData";
26
+ case "switch-case":
27
+ return "switchCase";
28
+ case "for-each":
29
+ return "forEach";
30
+ case "start":
31
+ return "startStep";
32
+ case "end":
33
+ return "end";
34
+ }
35
+ }
36
+ function renderExpression(expr) {
37
+ if (expr.type === "literal")
38
+ return JSON.stringify(expr.value);
39
+ return expr.expression;
40
+ }
41
+ function nodeSize(step) {
42
+ return step.type === "end" ? { w: END_NODE_SIZE, h: END_NODE_SIZE } : { w: NODE_WIDTH, h: NODE_HEIGHT };
43
+ }
44
+ function collectChildSteps(step, stepMap) {
45
+ const children = new Set;
46
+ const continuation = step.nextStepId;
47
+ const seeds = [];
48
+ if (step.type === "for-each") {
49
+ seeds.push(step.params.loopBodyStepId);
50
+ } else if (step.type === "switch-case") {
51
+ for (const c of step.params.cases) {
52
+ seeds.push(c.branchBodyStepId);
53
+ }
54
+ } else {
55
+ return children;
56
+ }
57
+ const queue = [...seeds];
58
+ while (queue.length > 0) {
59
+ const id = queue.shift();
60
+ if (id === undefined)
61
+ continue;
62
+ if (children.has(id) || id === continuation)
63
+ continue;
64
+ const s = stepMap.get(id);
65
+ if (!s)
66
+ continue;
67
+ children.add(id);
68
+ if (s.nextStepId)
69
+ queue.push(s.nextStepId);
70
+ if (s.type === "switch-case") {
71
+ for (const c of s.params.cases)
72
+ queue.push(c.branchBodyStepId);
73
+ }
74
+ if (s.type === "for-each") {
75
+ queue.push(s.params.loopBodyStepId);
76
+ }
77
+ }
78
+ return children;
79
+ }
80
+ function buildParentMap(workflow, stepMap) {
81
+ const parentMap = new Map;
82
+ const allGroupChildren = new Map;
83
+ for (const step of workflow.steps) {
84
+ if (step.type === "for-each" || step.type === "switch-case") {
85
+ allGroupChildren.set(step.id, collectChildSteps(step, stepMap));
86
+ }
87
+ }
88
+ for (const [groupId, children] of allGroupChildren) {
89
+ for (const childId of children) {
90
+ const currentParent = parentMap.get(childId);
91
+ if (!currentParent) {
92
+ parentMap.set(childId, groupId);
93
+ } else {
94
+ const currentParentChildren = allGroupChildren.get(currentParent);
95
+ if (currentParentChildren?.has(groupId)) {
96
+ parentMap.set(childId, groupId);
97
+ }
98
+ }
99
+ }
100
+ }
101
+ return parentMap;
102
+ }
103
+ function getDirectChildren(groupId, parentMap) {
104
+ const direct = new Set;
105
+ for (const [childId, pid] of parentMap) {
106
+ if (pid === groupId)
107
+ direct.add(childId);
108
+ }
109
+ return direct;
110
+ }
111
+ function groupProcessingOrder(groupIds, parentMap) {
112
+ const order = [];
113
+ const visited = new Set;
114
+ function visit(id) {
115
+ if (visited.has(id))
116
+ return;
117
+ visited.add(id);
118
+ for (const [childId, pid] of parentMap) {
119
+ if (pid === id && groupIds.has(childId)) {
120
+ visit(childId);
121
+ }
122
+ }
123
+ order.push(id);
124
+ }
125
+ for (const id of groupIds) {
126
+ visit(id);
127
+ }
128
+ return order;
129
+ }
130
+ function groupHeaderId(groupId) {
131
+ return `__header__${groupId}`;
132
+ }
133
+ function getNodeDimensions(nodeId, groupIds, computedSizes, stepMap) {
134
+ if (groupIds.has(nodeId)) {
135
+ return getOrThrow(computedSizes, nodeId);
136
+ }
137
+ return nodeSize(getOrThrow(stepMap, nodeId));
138
+ }
139
+ function buildLayout(workflow, diagnostics = []) {
140
+ const diagnosticsByStep = new Map;
141
+ for (const d of diagnostics) {
142
+ if (d.location.stepId) {
143
+ const existing = diagnosticsByStep.get(d.location.stepId) ?? [];
144
+ existing.push(d);
145
+ diagnosticsByStep.set(d.location.stepId, existing);
146
+ }
147
+ }
148
+ const stepMap = new Map;
149
+ for (const step of workflow.steps) {
150
+ stepMap.set(step.id, step);
151
+ }
152
+ const parentMap = buildParentMap(workflow, stepMap);
153
+ const groupIds = new Set;
154
+ for (const step of workflow.steps) {
155
+ if (step.type === "for-each" || step.type === "switch-case") {
156
+ const hasChildren = [...parentMap.values()].some((pid) => pid === step.id);
157
+ if (hasChildren)
158
+ groupIds.add(step.id);
159
+ }
160
+ }
161
+ const processOrder = groupProcessingOrder(groupIds, parentMap);
162
+ const computedSizes = new Map;
163
+ const groupChildPositions = new Map;
164
+ for (const groupId of processOrder) {
165
+ const groupStep = getOrThrow(stepMap, groupId);
166
+ const directChildren = getDirectChildren(groupId, parentMap);
167
+ if (directChildren.size === 0) {
168
+ groupIds.delete(groupId);
169
+ continue;
170
+ }
171
+ const subG = new dagre.graphlib.Graph;
172
+ subG.setGraph({ rankdir: "TB", ranksep: 60, nodesep: 40 });
173
+ subG.setDefaultEdgeLabel(() => ({}));
174
+ const headerId = groupHeaderId(groupId);
175
+ subG.setNode(headerId, {
176
+ width: GROUP_HEADER_WIDTH,
177
+ height: GROUP_HEADER_HEIGHT
178
+ });
179
+ for (const childId of directChildren) {
180
+ const { w, h } = getNodeDimensions(childId, groupIds, computedSizes, stepMap);
181
+ subG.setNode(childId, { width: w, height: h });
182
+ }
183
+ if (groupStep.type === "switch-case") {
184
+ for (const c of groupStep.params.cases) {
185
+ if (directChildren.has(c.branchBodyStepId)) {
186
+ subG.setEdge(headerId, c.branchBodyStepId);
187
+ }
188
+ }
189
+ } else if (groupStep.type === "for-each") {
190
+ if (directChildren.has(groupStep.params.loopBodyStepId)) {
191
+ subG.setEdge(headerId, groupStep.params.loopBodyStepId);
192
+ }
193
+ }
194
+ for (const childId of directChildren) {
195
+ const step = getOrThrow(stepMap, childId);
196
+ if (step.nextStepId && directChildren.has(step.nextStepId)) {
197
+ subG.setEdge(childId, step.nextStepId);
198
+ }
199
+ if (step.type === "switch-case") {
200
+ for (const c of step.params.cases) {
201
+ if (directChildren.has(c.branchBodyStepId)) {
202
+ subG.setEdge(childId, c.branchBodyStepId);
203
+ }
204
+ }
205
+ }
206
+ if (step.type === "for-each") {
207
+ if (directChildren.has(step.params.loopBodyStepId)) {
208
+ subG.setEdge(childId, step.params.loopBodyStepId);
209
+ }
210
+ }
211
+ }
212
+ dagre.layout(subG);
213
+ let minX = Number.POSITIVE_INFINITY;
214
+ let minY = Number.POSITIVE_INFINITY;
215
+ let maxX = Number.NEGATIVE_INFINITY;
216
+ let maxY = Number.NEGATIVE_INFINITY;
217
+ const allSubNodes = new Set(directChildren);
218
+ allSubNodes.add(headerId);
219
+ const rawPositions = new Map;
220
+ for (const nodeId of allSubNodes) {
221
+ const pos = subG.node(nodeId);
222
+ let w;
223
+ let h;
224
+ if (nodeId === headerId) {
225
+ w = GROUP_HEADER_WIDTH;
226
+ h = GROUP_HEADER_HEIGHT;
227
+ } else {
228
+ const dims = getNodeDimensions(nodeId, groupIds, computedSizes, stepMap);
229
+ w = dims.w;
230
+ h = dims.h;
231
+ }
232
+ const x = pos.x - w / 2;
233
+ const y = pos.y - h / 2;
234
+ rawPositions.set(nodeId, { x, y });
235
+ minX = Math.min(minX, x);
236
+ minY = Math.min(minY, y);
237
+ maxX = Math.max(maxX, x + w);
238
+ maxY = Math.max(maxY, y + h);
239
+ }
240
+ const positions = new Map;
241
+ for (const [nodeId, pos] of rawPositions) {
242
+ positions.set(nodeId, {
243
+ x: pos.x - minX + GROUP_PADDING,
244
+ y: pos.y - minY + GROUP_HEADER
245
+ });
246
+ }
247
+ groupChildPositions.set(groupId, positions);
248
+ const contentWidth = maxX - minX;
249
+ const contentHeight = maxY - minY;
250
+ computedSizes.set(groupId, {
251
+ w: contentWidth + GROUP_PADDING * 2,
252
+ h: contentHeight + GROUP_HEADER + GROUP_PADDING
253
+ });
254
+ }
255
+ const topG = new dagre.graphlib.Graph;
256
+ topG.setGraph({ rankdir: "TB", ranksep: 80, nodesep: 60 });
257
+ topG.setDefaultEdgeLabel(() => ({}));
258
+ topG.setNode(START_NODE_ID, {
259
+ width: START_NODE_SIZE,
260
+ height: START_NODE_SIZE
261
+ });
262
+ const topLevelStepIds = [];
263
+ for (const step of workflow.steps) {
264
+ if (!parentMap.has(step.id)) {
265
+ topLevelStepIds.push(step.id);
266
+ const { w, h } = getNodeDimensions(step.id, groupIds, computedSizes, stepMap);
267
+ topG.setNode(step.id, { width: w, height: h });
268
+ }
269
+ }
270
+ topG.setEdge(START_NODE_ID, workflow.initialStepId);
271
+ const topLevelSet = new Set(topLevelStepIds);
272
+ for (const stepId of topLevelStepIds) {
273
+ const step = getOrThrow(stepMap, stepId);
274
+ if (step.nextStepId && topLevelSet.has(step.nextStepId)) {
275
+ topG.setEdge(stepId, step.nextStepId);
276
+ }
277
+ if (step.type === "switch-case") {
278
+ for (const c of step.params.cases) {
279
+ if (topLevelSet.has(c.branchBodyStepId)) {
280
+ topG.setEdge(stepId, c.branchBodyStepId);
281
+ }
282
+ }
283
+ }
284
+ if (step.type === "for-each") {
285
+ if (topLevelSet.has(step.params.loopBodyStepId)) {
286
+ topG.setEdge(stepId, step.params.loopBodyStepId);
287
+ }
288
+ }
289
+ }
290
+ dagre.layout(topG);
291
+ const topLevelPositions = new Map;
292
+ const startPos = topG.node(START_NODE_ID);
293
+ topLevelPositions.set(START_NODE_ID, {
294
+ x: startPos.x - START_NODE_SIZE / 2,
295
+ y: startPos.y - START_NODE_SIZE / 2
296
+ });
297
+ for (const stepId of topLevelStepIds) {
298
+ const pos = topG.node(stepId);
299
+ const { w, h } = getNodeDimensions(stepId, groupIds, computedSizes, stepMap);
300
+ topLevelPositions.set(stepId, {
301
+ x: pos.x - w / 2,
302
+ y: pos.y - h / 2
303
+ });
304
+ }
305
+ const nodes = [];
306
+ const startNodePos = getOrThrow(topLevelPositions, START_NODE_ID);
307
+ nodes.push({
308
+ id: START_NODE_ID,
309
+ type: "start",
310
+ position: startNodePos,
311
+ data: {},
312
+ selectable: false
313
+ });
314
+ function addNodesForContext(nodeIds, getPosition, parentId) {
315
+ const groups = [];
316
+ const headers = [];
317
+ const nonGroups = [];
318
+ for (const id of nodeIds) {
319
+ if (groupIds.has(id))
320
+ groups.push(id);
321
+ else if (id.startsWith("__header__"))
322
+ headers.push(id);
323
+ else
324
+ nonGroups.push(id);
325
+ }
326
+ for (const id of groups) {
327
+ const step = getOrThrow(stepMap, id);
328
+ const pos = getPosition(id);
329
+ const size = getOrThrow(computedSizes, id);
330
+ nodes.push({
331
+ id,
332
+ type: stepNodeType(step),
333
+ position: pos,
334
+ data: {
335
+ step,
336
+ diagnostics: diagnosticsByStep.get(id) ?? [],
337
+ isGroup: true,
338
+ groupWidth: size.w,
339
+ groupHeight: size.h,
340
+ hasSourceEdge: !!step.nextStepId
341
+ },
342
+ style: { width: size.w, height: size.h },
343
+ ...parentId ? { parentId, extent: "parent" } : {}
344
+ });
345
+ const childPositions = getOrThrow(groupChildPositions, id);
346
+ addNodesForContext(childPositions.keys(), (childId) => getOrThrow(childPositions, childId), id);
347
+ }
348
+ for (const id of headers) {
349
+ const pos = getPosition(id);
350
+ const gid = id.replace("__header__", "");
351
+ const step = getOrThrow(stepMap, gid);
352
+ if (step.type === "switch-case") {
353
+ nodes.push({
354
+ id,
355
+ type: "groupHeader",
356
+ position: pos,
357
+ data: {
358
+ variant: "switch",
359
+ description: step.description,
360
+ expression: renderExpression(step.params.switchOn)
361
+ },
362
+ ...parentId ? { parentId, extent: "parent" } : {}
363
+ });
364
+ } else if (step.type === "for-each") {
365
+ nodes.push({
366
+ id,
367
+ type: "groupHeader",
368
+ position: pos,
369
+ data: {
370
+ variant: "loop",
371
+ description: step.description,
372
+ target: renderExpression(step.params.target),
373
+ itemName: step.params.itemName
374
+ },
375
+ ...parentId ? { parentId, extent: "parent" } : {}
376
+ });
377
+ }
378
+ }
379
+ for (const id of nonGroups) {
380
+ const step = getOrThrow(stepMap, id);
381
+ const pos = getPosition(id);
382
+ nodes.push({
383
+ id,
384
+ type: stepNodeType(step),
385
+ position: pos,
386
+ data: {
387
+ step,
388
+ diagnostics: diagnosticsByStep.get(id) ?? [],
389
+ hasSourceEdge: !!step.nextStepId
390
+ },
391
+ ...parentId ? { parentId, extent: "parent" } : {}
392
+ });
393
+ }
394
+ }
395
+ addNodesForContext(topLevelStepIds, (id) => getOrThrow(topLevelPositions, id));
396
+ const edges = [];
397
+ edges.push({
398
+ id: `${START_NODE_ID}->${workflow.initialStepId}`,
399
+ source: START_NODE_ID,
400
+ target: workflow.initialStepId,
401
+ type: "workflow",
402
+ data: { edgeKind: "sequential" }
403
+ });
404
+ for (const step of workflow.steps) {
405
+ if (step.type === "switch-case") {
406
+ if (groupIds.has(step.id)) {
407
+ const headerId = groupHeaderId(step.id);
408
+ for (const c of step.params.cases) {
409
+ const label = c.value.type === "default" ? "default" : renderExpression(c.value);
410
+ edges.push({
411
+ id: `${headerId}->${c.branchBodyStepId}`,
412
+ source: headerId,
413
+ target: c.branchBodyStepId,
414
+ label,
415
+ type: "workflow",
416
+ data: { edgeKind: "branch" }
417
+ });
418
+ }
419
+ }
420
+ if (step.nextStepId) {
421
+ edges.push({
422
+ id: `${step.id}->${step.nextStepId}`,
423
+ source: step.id,
424
+ target: step.nextStepId,
425
+ type: "workflow",
426
+ data: { edgeKind: "sequential" }
427
+ });
428
+ }
429
+ } else if (step.type === "for-each") {
430
+ if (groupIds.has(step.id)) {
431
+ const headerId = groupHeaderId(step.id);
432
+ edges.push({
433
+ id: `${headerId}->${step.params.loopBodyStepId}`,
434
+ source: headerId,
435
+ target: step.params.loopBodyStepId,
436
+ type: "workflow",
437
+ data: { edgeKind: "sequential" }
438
+ });
439
+ }
440
+ if (step.nextStepId) {
441
+ edges.push({
442
+ id: `${step.id}->${step.nextStepId}`,
443
+ source: step.id,
444
+ target: step.nextStepId,
445
+ type: "workflow",
446
+ data: { edgeKind: "sequential" }
447
+ });
448
+ }
449
+ } else if (step.nextStepId) {
450
+ edges.push({
451
+ id: `${step.id}->${step.nextStepId}`,
452
+ source: step.id,
453
+ target: step.nextStepId,
454
+ type: "workflow",
455
+ data: { edgeKind: "sequential" }
456
+ });
457
+ }
458
+ }
459
+ return { nodes, edges };
460
+ }
461
+ // src/viewer/workflow-viewer.tsx
462
+ import {
463
+ Background,
464
+ Controls,
465
+ MiniMap,
466
+ ReactFlow,
467
+ useEdgesState,
468
+ useNodesState
469
+ } from "@xyflow/react";
470
+ import { useCallback, useEffect, useMemo, useState } from "react";
471
+
472
+ // src/viewer/edges/workflow-edge.tsx
473
+ import {
474
+ BaseEdge,
475
+ EdgeLabelRenderer,
476
+ getBezierPath
477
+ } from "@xyflow/react";
478
+ import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
479
+ function WorkflowEdge({
480
+ id,
481
+ sourceX,
482
+ sourceY,
483
+ targetX,
484
+ targetY,
485
+ sourcePosition,
486
+ targetPosition,
487
+ label,
488
+ data,
489
+ markerEnd,
490
+ style
491
+ }) {
492
+ const edgeKind = data?.edgeKind ?? "sequential";
493
+ const isContinuation = edgeKind === "continuation";
494
+ const [edgePath, labelX, labelY] = getBezierPath({
495
+ sourceX,
496
+ sourceY,
497
+ sourcePosition,
498
+ targetX,
499
+ targetY,
500
+ targetPosition
501
+ });
502
+ return /* @__PURE__ */ jsxDEV(Fragment, {
503
+ children: [
504
+ /* @__PURE__ */ jsxDEV(BaseEdge, {
505
+ id,
506
+ path: edgePath,
507
+ markerEnd,
508
+ style: {
509
+ ...style,
510
+ strokeDasharray: isContinuation ? "6 3" : undefined,
511
+ stroke: isContinuation ? "#9ca3af" : "#6b7280",
512
+ strokeWidth: 1.5
513
+ }
514
+ }, undefined, false, undefined, this),
515
+ label && /* @__PURE__ */ jsxDEV(EdgeLabelRenderer, {
516
+ children: /* @__PURE__ */ jsxDEV("div", {
517
+ style: {
518
+ position: "absolute",
519
+ transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
520
+ pointerEvents: "all",
521
+ zIndex: 10
522
+ },
523
+ className: "bg-white px-1.5 py-0.5 rounded text-[10px] text-gray-700 font-medium border border-gray-200 shadow whitespace-nowrap",
524
+ children: label
525
+ }, undefined, false, undefined, this)
526
+ }, undefined, false, undefined, this)
527
+ ]
528
+ }, undefined, true, undefined, this);
529
+ }
530
+
531
+ // src/viewer/nodes/end-node.tsx
532
+ import { Handle, Position } from "@xyflow/react";
533
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
534
+ function EndNode({ data, selected }) {
535
+ const { diagnostics } = data;
536
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
537
+ let ringClass = "";
538
+ if (hasErrors)
539
+ ringClass = "ring-2 ring-red-500";
540
+ else if (selected)
541
+ ringClass = "ring-2 ring-blue-400";
542
+ return /* @__PURE__ */ jsxDEV2("div", {
543
+ className: `bg-gray-100 rounded-full w-[60px] h-[60px] flex items-center justify-center shadow-sm border border-gray-300 ${ringClass}`,
544
+ children: [
545
+ /* @__PURE__ */ jsxDEV2(Handle, {
546
+ type: "target",
547
+ position: Position.Top,
548
+ className: "!bg-gray-400 !w-2 !h-2"
549
+ }, undefined, false, undefined, this),
550
+ /* @__PURE__ */ jsxDEV2("span", {
551
+ className: "text-xs font-medium text-gray-500",
552
+ children: "End"
553
+ }, undefined, false, undefined, this)
554
+ ]
555
+ }, undefined, true, undefined, this);
556
+ }
557
+
558
+ // src/viewer/nodes/base-node.tsx
559
+ import { Handle as Handle2, Position as Position2 } from "@xyflow/react";
560
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
561
+ function BaseNode({
562
+ id,
563
+ name,
564
+ typeLabel,
565
+ typeLabelColor,
566
+ accent,
567
+ description,
568
+ diagnostics,
569
+ children,
570
+ selected,
571
+ hasSourceEdge = true
572
+ }) {
573
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
574
+ const hasWarnings = !hasErrors && diagnostics.some((d) => d.severity === "warning");
575
+ let ringClass = "";
576
+ if (hasErrors)
577
+ ringClass = "ring-2 ring-red-500";
578
+ else if (hasWarnings)
579
+ ringClass = "ring-2 ring-amber-400";
580
+ else if (selected)
581
+ ringClass = "ring-2 ring-blue-400";
582
+ return /* @__PURE__ */ jsxDEV3("div", {
583
+ className: `bg-white rounded-lg shadow-md border-l-4 w-[300px] ${ringClass}`,
584
+ style: { borderLeftColor: accent },
585
+ children: [
586
+ /* @__PURE__ */ jsxDEV3(Handle2, {
587
+ type: "target",
588
+ position: Position2.Top,
589
+ className: "!bg-gray-400 !w-2 !h-2"
590
+ }, undefined, false, undefined, this),
591
+ /* @__PURE__ */ jsxDEV3("div", {
592
+ className: "px-3 py-2.5",
593
+ children: [
594
+ /* @__PURE__ */ jsxDEV3("div", {
595
+ className: "flex items-center justify-between gap-2",
596
+ children: [
597
+ /* @__PURE__ */ jsxDEV3("div", {
598
+ className: "flex items-center gap-2 min-w-0",
599
+ children: [
600
+ /* @__PURE__ */ jsxDEV3("span", {
601
+ className: `text-[10px] font-semibold uppercase tracking-wide shrink-0 ${typeLabelColor}`,
602
+ children: typeLabel
603
+ }, undefined, false, undefined, this),
604
+ /* @__PURE__ */ jsxDEV3("div", {
605
+ className: "font-medium text-sm text-gray-900 truncate",
606
+ children: name
607
+ }, undefined, false, undefined, this)
608
+ ]
609
+ }, undefined, true, undefined, this),
610
+ (hasErrors || hasWarnings) && /* @__PURE__ */ jsxDEV3("span", {
611
+ className: `text-xs px-1.5 py-0.5 rounded-full font-medium shrink-0 ${hasErrors ? "bg-red-100 text-red-700" : "bg-amber-100 text-amber-700"}`,
612
+ children: diagnostics.length
613
+ }, undefined, false, undefined, this)
614
+ ]
615
+ }, undefined, true, undefined, this),
616
+ /* @__PURE__ */ jsxDEV3("div", {
617
+ className: "text-[11px] text-gray-400 font-mono",
618
+ children: id
619
+ }, undefined, false, undefined, this),
620
+ /* @__PURE__ */ jsxDEV3("div", {
621
+ className: "text-[11px] text-gray-500 mt-1",
622
+ children: description
623
+ }, undefined, false, undefined, this),
624
+ children && /* @__PURE__ */ jsxDEV3("div", {
625
+ className: "mt-2 border-t border-gray-100 pt-2",
626
+ children
627
+ }, undefined, false, undefined, this)
628
+ ]
629
+ }, undefined, true, undefined, this),
630
+ hasSourceEdge && /* @__PURE__ */ jsxDEV3(Handle2, {
631
+ type: "source",
632
+ position: Position2.Bottom,
633
+ className: "!bg-gray-400 !w-2 !h-2"
634
+ }, undefined, false, undefined, this)
635
+ ]
636
+ }, undefined, true, undefined, this);
637
+ }
638
+
639
+ // src/viewer/nodes/extract-data-node.tsx
640
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
641
+ function renderExpr(expr) {
642
+ if (expr.type === "literal")
643
+ return JSON.stringify(expr.value);
644
+ return expr.expression;
645
+ }
646
+ function ExtractDataNode({ data, selected }) {
647
+ const { step, diagnostics, hasSourceEdge } = data;
648
+ if (step.type !== "extract-data")
649
+ return null;
650
+ const outputFormat = step.params.outputFormat;
651
+ const outputKeys = outputFormat?.properties ? Object.keys(outputFormat.properties) : [];
652
+ return /* @__PURE__ */ jsxDEV4(BaseNode, {
653
+ id: step.id,
654
+ name: step.name,
655
+ typeLabel: "Extract",
656
+ typeLabelColor: "text-purple-500",
657
+ accent: "#a855f7",
658
+ description: step.description,
659
+ diagnostics,
660
+ selected,
661
+ hasSourceEdge,
662
+ children: [
663
+ /* @__PURE__ */ jsxDEV4("div", {
664
+ className: "flex gap-1.5 text-[11px]",
665
+ children: [
666
+ /* @__PURE__ */ jsxDEV4("span", {
667
+ className: "text-gray-400 shrink-0",
668
+ children: "source:"
669
+ }, undefined, false, undefined, this),
670
+ /* @__PURE__ */ jsxDEV4("span", {
671
+ className: "font-mono text-gray-600 truncate",
672
+ children: renderExpr(step.params.sourceData)
673
+ }, undefined, false, undefined, this)
674
+ ]
675
+ }, undefined, true, undefined, this),
676
+ outputKeys.length > 0 && /* @__PURE__ */ jsxDEV4("div", {
677
+ className: "mt-1 flex gap-1.5 text-[11px]",
678
+ children: [
679
+ /* @__PURE__ */ jsxDEV4("span", {
680
+ className: "text-gray-400 shrink-0",
681
+ children: "output:"
682
+ }, undefined, false, undefined, this),
683
+ /* @__PURE__ */ jsxDEV4("span", {
684
+ className: "font-mono text-gray-500",
685
+ children: outputKeys.join(", ")
686
+ }, undefined, false, undefined, this)
687
+ ]
688
+ }, undefined, true, undefined, this)
689
+ ]
690
+ }, undefined, true, undefined, this);
691
+ }
692
+
693
+ // src/viewer/nodes/for-each-node.tsx
694
+ import { Handle as Handle3, Position as Position3 } from "@xyflow/react";
695
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
696
+ function renderExpr2(expr) {
697
+ if (expr.type === "literal")
698
+ return JSON.stringify(expr.value);
699
+ return expr.expression;
700
+ }
701
+ function ForEachNode({ data, selected }) {
702
+ const { step, diagnostics, isGroup, groupWidth, groupHeight, hasSourceEdge } = data;
703
+ if (step.type !== "for-each")
704
+ return null;
705
+ if (isGroup) {
706
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
707
+ const hasWarnings = !hasErrors && diagnostics.some((d) => d.severity === "warning");
708
+ let ringClass = "";
709
+ if (hasErrors)
710
+ ringClass = "ring-2 ring-red-500";
711
+ else if (hasWarnings)
712
+ ringClass = "ring-2 ring-amber-400";
713
+ else if (selected)
714
+ ringClass = "ring-2 ring-emerald-400";
715
+ return /* @__PURE__ */ jsxDEV5("div", {
716
+ className: `rounded-xl border-2 border-dashed border-emerald-300 bg-emerald-50/30 ${ringClass}`,
717
+ style: { width: groupWidth, height: groupHeight },
718
+ children: [
719
+ /* @__PURE__ */ jsxDEV5(Handle3, {
720
+ type: "target",
721
+ position: Position3.Top,
722
+ className: "!bg-emerald-500 !w-2.5 !h-2.5"
723
+ }, undefined, false, undefined, this),
724
+ /* @__PURE__ */ jsxDEV5("div", {
725
+ className: "px-3 py-2 flex items-center gap-2",
726
+ children: [
727
+ /* @__PURE__ */ jsxDEV5("span", {
728
+ className: "text-[10px] font-semibold uppercase tracking-wide text-emerald-500",
729
+ children: "Loop"
730
+ }, undefined, false, undefined, this),
731
+ /* @__PURE__ */ jsxDEV5("span", {
732
+ className: "text-sm font-medium text-gray-800 truncate",
733
+ children: step.name
734
+ }, undefined, false, undefined, this)
735
+ ]
736
+ }, undefined, true, undefined, this),
737
+ hasSourceEdge && /* @__PURE__ */ jsxDEV5(Handle3, {
738
+ type: "source",
739
+ position: Position3.Bottom,
740
+ className: "!bg-emerald-500 !w-2.5 !h-2.5"
741
+ }, undefined, false, undefined, this)
742
+ ]
743
+ }, undefined, true, undefined, this);
744
+ }
745
+ return /* @__PURE__ */ jsxDEV5(BaseNode, {
746
+ id: step.id,
747
+ name: step.name,
748
+ typeLabel: "ForEach",
749
+ typeLabelColor: "text-emerald-500",
750
+ accent: "#10b981",
751
+ description: step.description,
752
+ diagnostics,
753
+ selected,
754
+ hasSourceEdge,
755
+ children: [
756
+ /* @__PURE__ */ jsxDEV5("div", {
757
+ className: "flex gap-1.5 text-[11px]",
758
+ children: [
759
+ /* @__PURE__ */ jsxDEV5("span", {
760
+ className: "text-gray-400 shrink-0",
761
+ children: "target:"
762
+ }, undefined, false, undefined, this),
763
+ /* @__PURE__ */ jsxDEV5("span", {
764
+ className: "font-mono text-gray-600 truncate",
765
+ children: renderExpr2(step.params.target)
766
+ }, undefined, false, undefined, this)
767
+ ]
768
+ }, undefined, true, undefined, this),
769
+ /* @__PURE__ */ jsxDEV5("div", {
770
+ className: "mt-0.5 flex gap-1.5 text-[11px]",
771
+ children: [
772
+ /* @__PURE__ */ jsxDEV5("span", {
773
+ className: "text-gray-400 shrink-0",
774
+ children: "as:"
775
+ }, undefined, false, undefined, this),
776
+ /* @__PURE__ */ jsxDEV5("span", {
777
+ className: "font-mono text-gray-600",
778
+ children: step.params.itemName
779
+ }, undefined, false, undefined, this)
780
+ ]
781
+ }, undefined, true, undefined, this)
782
+ ]
783
+ }, undefined, true, undefined, this);
784
+ }
785
+
786
+ // src/viewer/nodes/group-header-node.tsx
787
+ import { Handle as Handle4, Position as Position4 } from "@xyflow/react";
788
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
789
+ var styles = {
790
+ switch: {
791
+ bg: "bg-amber-50",
792
+ border: "border-amber-300",
793
+ label: "text-amber-600",
794
+ mono: "text-amber-800",
795
+ desc: "text-amber-700/70",
796
+ handle: "!bg-amber-500",
797
+ ring: "ring-amber-400"
798
+ },
799
+ loop: {
800
+ bg: "bg-emerald-50",
801
+ border: "border-emerald-300",
802
+ label: "text-emerald-600",
803
+ mono: "text-emerald-800",
804
+ desc: "text-emerald-700/70",
805
+ handle: "!bg-emerald-500",
806
+ ring: "ring-emerald-400"
807
+ }
808
+ };
809
+ function GroupHeaderNode({ data, selected }) {
810
+ const { variant, description, expression, target, itemName } = data;
811
+ const s = styles[variant];
812
+ return /* @__PURE__ */ jsxDEV6("div", {
813
+ className: `${s.bg} border-2 ${s.border} rounded-lg w-[280px] shadow-sm ${selected ? `ring-2 ${s.ring}` : ""}`,
814
+ children: [
815
+ /* @__PURE__ */ jsxDEV6("div", {
816
+ className: "px-3 py-2",
817
+ children: [
818
+ variant === "switch" && /* @__PURE__ */ jsxDEV6("div", {
819
+ className: "flex items-baseline gap-1.5",
820
+ children: [
821
+ /* @__PURE__ */ jsxDEV6("span", {
822
+ className: `text-[10px] font-bold uppercase tracking-wide ${s.label} shrink-0`,
823
+ children: "switch"
824
+ }, undefined, false, undefined, this),
825
+ /* @__PURE__ */ jsxDEV6("span", {
826
+ className: `text-[11px] ${s.label}`,
827
+ children: "on"
828
+ }, undefined, false, undefined, this),
829
+ /* @__PURE__ */ jsxDEV6("span", {
830
+ className: `text-xs font-mono font-medium ${s.mono} truncate`,
831
+ children: expression
832
+ }, undefined, false, undefined, this)
833
+ ]
834
+ }, undefined, true, undefined, this),
835
+ variant === "loop" && /* @__PURE__ */ jsxDEV6("div", {
836
+ className: "flex items-baseline gap-1.5 flex-wrap",
837
+ children: [
838
+ /* @__PURE__ */ jsxDEV6("span", {
839
+ className: `text-[10px] font-bold uppercase tracking-wide ${s.label} shrink-0`,
840
+ children: "foreach"
841
+ }, undefined, false, undefined, this),
842
+ /* @__PURE__ */ jsxDEV6("span", {
843
+ className: `text-xs font-mono font-medium ${s.mono}`,
844
+ children: itemName
845
+ }, undefined, false, undefined, this),
846
+ /* @__PURE__ */ jsxDEV6("span", {
847
+ className: `text-[11px] ${s.label}`,
848
+ children: "in"
849
+ }, undefined, false, undefined, this),
850
+ /* @__PURE__ */ jsxDEV6("span", {
851
+ className: `text-xs font-mono font-medium ${s.mono} truncate`,
852
+ children: target
853
+ }, undefined, false, undefined, this)
854
+ ]
855
+ }, undefined, true, undefined, this),
856
+ description && /* @__PURE__ */ jsxDEV6("div", {
857
+ className: `text-[11px] ${s.desc} mt-0.5 line-clamp-1`,
858
+ children: description
859
+ }, undefined, false, undefined, this)
860
+ ]
861
+ }, undefined, true, undefined, this),
862
+ /* @__PURE__ */ jsxDEV6(Handle4, {
863
+ type: "source",
864
+ position: Position4.Bottom,
865
+ className: `${s.handle} !w-2 !h-2`
866
+ }, undefined, false, undefined, this)
867
+ ]
868
+ }, undefined, true, undefined, this);
869
+ }
870
+
871
+ // src/viewer/nodes/llm-prompt-node.tsx
872
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
873
+ function LlmPromptNode({ data, selected }) {
874
+ const { step, diagnostics, hasSourceEdge } = data;
875
+ if (step.type !== "llm-prompt")
876
+ return null;
877
+ const outputFormat = step.params.outputFormat;
878
+ const outputKeys = outputFormat?.properties ? Object.keys(outputFormat.properties) : [];
879
+ return /* @__PURE__ */ jsxDEV7(BaseNode, {
880
+ id: step.id,
881
+ name: step.name,
882
+ typeLabel: "LLM",
883
+ typeLabelColor: "text-violet-500",
884
+ accent: "#8b5cf6",
885
+ description: step.description,
886
+ diagnostics,
887
+ selected,
888
+ hasSourceEdge,
889
+ children: [
890
+ /* @__PURE__ */ jsxDEV7("div", {
891
+ className: "text-[11px] text-gray-500 italic line-clamp-3 bg-gray-50 rounded p-1.5 font-mono",
892
+ children: step.params.prompt
893
+ }, undefined, false, undefined, this),
894
+ outputKeys.length > 0 && /* @__PURE__ */ jsxDEV7("div", {
895
+ className: "mt-1.5 text-[11px] text-gray-400",
896
+ children: [
897
+ /* @__PURE__ */ jsxDEV7("span", {
898
+ className: "text-gray-400",
899
+ children: "output: "
900
+ }, undefined, false, undefined, this),
901
+ /* @__PURE__ */ jsxDEV7("span", {
902
+ className: "font-mono text-gray-500",
903
+ children: outputKeys.join(", ")
904
+ }, undefined, false, undefined, this)
905
+ ]
906
+ }, undefined, true, undefined, this)
907
+ ]
908
+ }, undefined, true, undefined, this);
909
+ }
910
+
911
+ // src/viewer/nodes/start-node.tsx
912
+ import { Handle as Handle5, Position as Position5 } from "@xyflow/react";
913
+ import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
914
+ function StartNode({ selected }) {
915
+ return /* @__PURE__ */ jsxDEV8("div", {
916
+ className: `bg-gray-100 rounded-full w-[60px] h-[60px] flex items-center justify-center shadow-sm border border-gray-300 ${selected ? "ring-2 ring-blue-400" : ""}`,
917
+ children: [
918
+ /* @__PURE__ */ jsxDEV8("span", {
919
+ className: "text-xs font-medium text-gray-500",
920
+ children: "Start"
921
+ }, undefined, false, undefined, this),
922
+ /* @__PURE__ */ jsxDEV8(Handle5, {
923
+ type: "source",
924
+ position: Position5.Bottom,
925
+ className: "!bg-gray-400 !w-2 !h-2"
926
+ }, undefined, false, undefined, this)
927
+ ]
928
+ }, undefined, true, undefined, this);
929
+ }
930
+
931
+ // src/viewer/nodes/start-step-node.tsx
932
+ import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
933
+ function StartStepNode({ data, selected }) {
934
+ const { step, diagnostics, hasSourceEdge } = data;
935
+ if (step.type !== "start")
936
+ return null;
937
+ const schemaStr = JSON.stringify(step.params.inputSchema, null, 2);
938
+ return /* @__PURE__ */ jsxDEV9(BaseNode, {
939
+ id: step.id,
940
+ name: step.name,
941
+ typeLabel: "Start",
942
+ typeLabelColor: "text-green-500",
943
+ accent: "#22c55e",
944
+ description: step.description,
945
+ diagnostics,
946
+ selected,
947
+ hasSourceEdge,
948
+ children: [
949
+ /* @__PURE__ */ jsxDEV9("div", {
950
+ className: "text-xs text-gray-600",
951
+ children: /* @__PURE__ */ jsxDEV9("span", {
952
+ className: "text-gray-400",
953
+ children: "Input Schema:"
954
+ }, undefined, false, undefined, this)
955
+ }, undefined, false, undefined, this),
956
+ /* @__PURE__ */ jsxDEV9("pre", {
957
+ className: "text-[11px] font-mono text-gray-600 truncate max-h-[60px] overflow-hidden",
958
+ children: schemaStr === "{}" ? "(no inputs)" : schemaStr
959
+ }, undefined, false, undefined, this)
960
+ ]
961
+ }, undefined, true, undefined, this);
962
+ }
963
+
964
+ // src/viewer/nodes/switch-case-node.tsx
965
+ import { Handle as Handle6, Position as Position6 } from "@xyflow/react";
966
+ import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
967
+ function renderExpr3(expr) {
968
+ if (expr.type === "literal")
969
+ return JSON.stringify(expr.value);
970
+ return expr.expression;
971
+ }
972
+ function SwitchCaseNode({ data, selected }) {
973
+ const { step, diagnostics, isGroup, groupWidth, groupHeight, hasSourceEdge } = data;
974
+ if (step.type !== "switch-case")
975
+ return null;
976
+ if (isGroup) {
977
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
978
+ const hasWarnings = !hasErrors && diagnostics.some((d) => d.severity === "warning");
979
+ let ringClass = "";
980
+ if (hasErrors)
981
+ ringClass = "ring-2 ring-red-500";
982
+ else if (hasWarnings)
983
+ ringClass = "ring-2 ring-amber-400";
984
+ else if (selected)
985
+ ringClass = "ring-2 ring-amber-400";
986
+ return /* @__PURE__ */ jsxDEV10("div", {
987
+ className: `rounded-xl border-2 border-dashed border-amber-300 bg-amber-50/30 ${ringClass}`,
988
+ style: { width: groupWidth, height: groupHeight },
989
+ children: [
990
+ /* @__PURE__ */ jsxDEV10(Handle6, {
991
+ type: "target",
992
+ position: Position6.Top,
993
+ className: "!bg-amber-500 !w-2.5 !h-2.5"
994
+ }, undefined, false, undefined, this),
995
+ /* @__PURE__ */ jsxDEV10("div", {
996
+ className: "px-3 py-2 flex items-center gap-2",
997
+ children: [
998
+ /* @__PURE__ */ jsxDEV10("span", {
999
+ className: "text-[10px] font-semibold uppercase tracking-wide text-amber-500",
1000
+ children: "Branch"
1001
+ }, undefined, false, undefined, this),
1002
+ /* @__PURE__ */ jsxDEV10("span", {
1003
+ className: "text-sm font-medium text-gray-800 truncate",
1004
+ children: step.name
1005
+ }, undefined, false, undefined, this)
1006
+ ]
1007
+ }, undefined, true, undefined, this),
1008
+ hasSourceEdge && /* @__PURE__ */ jsxDEV10(Handle6, {
1009
+ type: "source",
1010
+ position: Position6.Bottom,
1011
+ className: "!bg-amber-500 !w-2.5 !h-2.5"
1012
+ }, undefined, false, undefined, this)
1013
+ ]
1014
+ }, undefined, true, undefined, this);
1015
+ }
1016
+ return /* @__PURE__ */ jsxDEV10(BaseNode, {
1017
+ id: step.id,
1018
+ name: step.name,
1019
+ typeLabel: "Switch",
1020
+ typeLabelColor: "text-amber-500",
1021
+ accent: "#f59e0b",
1022
+ description: step.description,
1023
+ diagnostics,
1024
+ selected,
1025
+ hasSourceEdge,
1026
+ children: [
1027
+ /* @__PURE__ */ jsxDEV10("div", {
1028
+ className: "flex gap-1.5 text-[11px]",
1029
+ children: [
1030
+ /* @__PURE__ */ jsxDEV10("span", {
1031
+ className: "text-gray-400 shrink-0",
1032
+ children: "on:"
1033
+ }, undefined, false, undefined, this),
1034
+ /* @__PURE__ */ jsxDEV10("span", {
1035
+ className: "font-mono text-gray-600 truncate",
1036
+ children: renderExpr3(step.params.switchOn)
1037
+ }, undefined, false, undefined, this)
1038
+ ]
1039
+ }, undefined, true, undefined, this),
1040
+ /* @__PURE__ */ jsxDEV10("div", {
1041
+ className: "mt-1.5 space-y-0.5",
1042
+ children: step.params.cases.map((c) => /* @__PURE__ */ jsxDEV10("div", {
1043
+ className: "flex items-center gap-1.5 text-[11px]",
1044
+ children: [
1045
+ /* @__PURE__ */ jsxDEV10("span", {
1046
+ className: "font-mono text-gray-500",
1047
+ children: c.value.type === "default" ? "default" : renderExpr3(c.value)
1048
+ }, undefined, false, undefined, this),
1049
+ /* @__PURE__ */ jsxDEV10("span", {
1050
+ className: "text-gray-300",
1051
+ children: "→"
1052
+ }, undefined, false, undefined, this),
1053
+ /* @__PURE__ */ jsxDEV10("span", {
1054
+ className: "font-mono text-gray-600",
1055
+ children: c.branchBodyStepId
1056
+ }, undefined, false, undefined, this)
1057
+ ]
1058
+ }, c.branchBodyStepId, true, undefined, this))
1059
+ }, undefined, false, undefined, this)
1060
+ ]
1061
+ }, undefined, true, undefined, this);
1062
+ }
1063
+
1064
+ // src/viewer/nodes/tool-call-node.tsx
1065
+ import { jsxDEV as jsxDEV11 } from "react/jsx-dev-runtime";
1066
+ function renderExpr4(expr) {
1067
+ if (expr.type === "literal")
1068
+ return JSON.stringify(expr.value);
1069
+ return expr.expression;
1070
+ }
1071
+ function ToolCallNode({ data, selected }) {
1072
+ const { step, diagnostics, hasSourceEdge } = data;
1073
+ if (step.type !== "tool-call")
1074
+ return null;
1075
+ const entries = Object.entries(step.params.toolInput);
1076
+ return /* @__PURE__ */ jsxDEV11(BaseNode, {
1077
+ id: step.id,
1078
+ name: step.name,
1079
+ typeLabel: "Tool",
1080
+ typeLabelColor: "text-blue-500",
1081
+ accent: "#3b82f6",
1082
+ description: step.description,
1083
+ diagnostics,
1084
+ selected,
1085
+ hasSourceEdge,
1086
+ children: [
1087
+ /* @__PURE__ */ jsxDEV11("div", {
1088
+ className: "text-xs font-mono text-gray-700 font-medium",
1089
+ children: step.params.toolName
1090
+ }, undefined, false, undefined, this),
1091
+ entries.length > 0 && /* @__PURE__ */ jsxDEV11("div", {
1092
+ className: "mt-1.5 space-y-0.5",
1093
+ children: entries.map(([key, val]) => /* @__PURE__ */ jsxDEV11("div", {
1094
+ className: "flex gap-1.5 text-[11px]",
1095
+ children: [
1096
+ /* @__PURE__ */ jsxDEV11("span", {
1097
+ className: "text-gray-400 shrink-0",
1098
+ children: [
1099
+ key,
1100
+ ":"
1101
+ ]
1102
+ }, undefined, true, undefined, this),
1103
+ /* @__PURE__ */ jsxDEV11("span", {
1104
+ className: "font-mono text-gray-600 truncate",
1105
+ children: renderExpr4(val)
1106
+ }, undefined, false, undefined, this)
1107
+ ]
1108
+ }, key, true, undefined, this))
1109
+ }, undefined, false, undefined, this)
1110
+ ]
1111
+ }, undefined, true, undefined, this);
1112
+ }
1113
+
1114
+ // src/viewer/panels/step-detail-panel.tsx
1115
+ import { jsxDEV as jsxDEV12 } from "react/jsx-dev-runtime";
1116
+ function renderExpression2(expr) {
1117
+ if (expr.type === "literal")
1118
+ return JSON.stringify(expr.value);
1119
+ return expr.expression;
1120
+ }
1121
+ function TypeBadge({ type }) {
1122
+ const colors = {
1123
+ "tool-call": "bg-blue-100 text-blue-700",
1124
+ "llm-prompt": "bg-violet-100 text-violet-700",
1125
+ "extract-data": "bg-purple-100 text-purple-700",
1126
+ "switch-case": "bg-amber-100 text-amber-700",
1127
+ "for-each": "bg-emerald-100 text-emerald-700",
1128
+ start: "bg-green-100 text-green-700",
1129
+ end: "bg-gray-100 text-gray-600"
1130
+ };
1131
+ return /* @__PURE__ */ jsxDEV12("span", {
1132
+ className: `text-[11px] font-medium px-2 py-0.5 rounded-full ${colors[type] ?? "bg-gray-100 text-gray-600"}`,
1133
+ children: type
1134
+ }, undefined, false, undefined, this);
1135
+ }
1136
+ function StepParams({ step }) {
1137
+ switch (step.type) {
1138
+ case "tool-call":
1139
+ return /* @__PURE__ */ jsxDEV12("div", {
1140
+ className: "space-y-2",
1141
+ children: [
1142
+ /* @__PURE__ */ jsxDEV12("div", {
1143
+ children: [
1144
+ /* @__PURE__ */ jsxDEV12(Label, {
1145
+ children: "Tool"
1146
+ }, undefined, false, undefined, this),
1147
+ /* @__PURE__ */ jsxDEV12(Code, {
1148
+ children: step.params.toolName
1149
+ }, undefined, false, undefined, this)
1150
+ ]
1151
+ }, undefined, true, undefined, this),
1152
+ Object.keys(step.params.toolInput).length > 0 && /* @__PURE__ */ jsxDEV12("div", {
1153
+ children: [
1154
+ /* @__PURE__ */ jsxDEV12(Label, {
1155
+ children: "Inputs"
1156
+ }, undefined, false, undefined, this),
1157
+ /* @__PURE__ */ jsxDEV12("div", {
1158
+ className: "space-y-1",
1159
+ children: Object.entries(step.params.toolInput).map(([key, val]) => /* @__PURE__ */ jsxDEV12("div", {
1160
+ className: "flex gap-2 text-xs",
1161
+ children: [
1162
+ /* @__PURE__ */ jsxDEV12("span", {
1163
+ className: "font-mono text-gray-500",
1164
+ children: [
1165
+ key,
1166
+ ":"
1167
+ ]
1168
+ }, undefined, true, undefined, this),
1169
+ /* @__PURE__ */ jsxDEV12("span", {
1170
+ className: "font-mono text-gray-700",
1171
+ children: renderExpression2(val)
1172
+ }, undefined, false, undefined, this)
1173
+ ]
1174
+ }, key, true, undefined, this))
1175
+ }, undefined, false, undefined, this)
1176
+ ]
1177
+ }, undefined, true, undefined, this)
1178
+ ]
1179
+ }, undefined, true, undefined, this);
1180
+ case "llm-prompt":
1181
+ return /* @__PURE__ */ jsxDEV12("div", {
1182
+ className: "space-y-2",
1183
+ children: [
1184
+ /* @__PURE__ */ jsxDEV12("div", {
1185
+ children: [
1186
+ /* @__PURE__ */ jsxDEV12(Label, {
1187
+ children: "Prompt"
1188
+ }, undefined, false, undefined, this),
1189
+ /* @__PURE__ */ jsxDEV12("pre", {
1190
+ className: "text-xs text-gray-700 bg-gray-50 rounded p-2 whitespace-pre-wrap font-mono",
1191
+ children: step.params.prompt
1192
+ }, undefined, false, undefined, this)
1193
+ ]
1194
+ }, undefined, true, undefined, this),
1195
+ /* @__PURE__ */ jsxDEV12("div", {
1196
+ children: [
1197
+ /* @__PURE__ */ jsxDEV12(Label, {
1198
+ children: "Output Format"
1199
+ }, undefined, false, undefined, this),
1200
+ /* @__PURE__ */ jsxDEV12(Code, {
1201
+ children: JSON.stringify(step.params.outputFormat, null, 2)
1202
+ }, undefined, false, undefined, this)
1203
+ ]
1204
+ }, undefined, true, undefined, this)
1205
+ ]
1206
+ }, undefined, true, undefined, this);
1207
+ case "extract-data":
1208
+ return /* @__PURE__ */ jsxDEV12("div", {
1209
+ className: "space-y-2",
1210
+ children: [
1211
+ /* @__PURE__ */ jsxDEV12("div", {
1212
+ children: [
1213
+ /* @__PURE__ */ jsxDEV12(Label, {
1214
+ children: "Source"
1215
+ }, undefined, false, undefined, this),
1216
+ /* @__PURE__ */ jsxDEV12(Code, {
1217
+ children: renderExpression2(step.params.sourceData)
1218
+ }, undefined, false, undefined, this)
1219
+ ]
1220
+ }, undefined, true, undefined, this),
1221
+ /* @__PURE__ */ jsxDEV12("div", {
1222
+ children: [
1223
+ /* @__PURE__ */ jsxDEV12(Label, {
1224
+ children: "Output Format"
1225
+ }, undefined, false, undefined, this),
1226
+ /* @__PURE__ */ jsxDEV12(Code, {
1227
+ children: JSON.stringify(step.params.outputFormat, null, 2)
1228
+ }, undefined, false, undefined, this)
1229
+ ]
1230
+ }, undefined, true, undefined, this)
1231
+ ]
1232
+ }, undefined, true, undefined, this);
1233
+ case "switch-case":
1234
+ return /* @__PURE__ */ jsxDEV12("div", {
1235
+ className: "space-y-2",
1236
+ children: [
1237
+ /* @__PURE__ */ jsxDEV12("div", {
1238
+ children: [
1239
+ /* @__PURE__ */ jsxDEV12(Label, {
1240
+ children: "Switch On"
1241
+ }, undefined, false, undefined, this),
1242
+ /* @__PURE__ */ jsxDEV12(Code, {
1243
+ children: renderExpression2(step.params.switchOn)
1244
+ }, undefined, false, undefined, this)
1245
+ ]
1246
+ }, undefined, true, undefined, this),
1247
+ /* @__PURE__ */ jsxDEV12("div", {
1248
+ children: [
1249
+ /* @__PURE__ */ jsxDEV12(Label, {
1250
+ children: "Cases"
1251
+ }, undefined, false, undefined, this),
1252
+ /* @__PURE__ */ jsxDEV12("div", {
1253
+ className: "space-y-1",
1254
+ children: step.params.cases.map((c) => /* @__PURE__ */ jsxDEV12("div", {
1255
+ className: "text-xs flex gap-2",
1256
+ children: [
1257
+ /* @__PURE__ */ jsxDEV12("span", {
1258
+ className: "font-mono text-gray-500",
1259
+ children: c.value.type === "default" ? "default" : renderExpression2(c.value)
1260
+ }, undefined, false, undefined, this),
1261
+ /* @__PURE__ */ jsxDEV12("span", {
1262
+ className: "text-gray-400",
1263
+ children: "→"
1264
+ }, undefined, false, undefined, this),
1265
+ /* @__PURE__ */ jsxDEV12("span", {
1266
+ className: "font-mono text-gray-700",
1267
+ children: c.branchBodyStepId
1268
+ }, undefined, false, undefined, this)
1269
+ ]
1270
+ }, c.branchBodyStepId, true, undefined, this))
1271
+ }, undefined, false, undefined, this)
1272
+ ]
1273
+ }, undefined, true, undefined, this)
1274
+ ]
1275
+ }, undefined, true, undefined, this);
1276
+ case "for-each":
1277
+ return /* @__PURE__ */ jsxDEV12("div", {
1278
+ className: "space-y-2",
1279
+ children: [
1280
+ /* @__PURE__ */ jsxDEV12("div", {
1281
+ children: [
1282
+ /* @__PURE__ */ jsxDEV12(Label, {
1283
+ children: "Target"
1284
+ }, undefined, false, undefined, this),
1285
+ /* @__PURE__ */ jsxDEV12(Code, {
1286
+ children: renderExpression2(step.params.target)
1287
+ }, undefined, false, undefined, this)
1288
+ ]
1289
+ }, undefined, true, undefined, this),
1290
+ /* @__PURE__ */ jsxDEV12("div", {
1291
+ children: [
1292
+ /* @__PURE__ */ jsxDEV12(Label, {
1293
+ children: "Item Variable"
1294
+ }, undefined, false, undefined, this),
1295
+ /* @__PURE__ */ jsxDEV12(Code, {
1296
+ children: step.params.itemName
1297
+ }, undefined, false, undefined, this)
1298
+ ]
1299
+ }, undefined, true, undefined, this),
1300
+ /* @__PURE__ */ jsxDEV12("div", {
1301
+ children: [
1302
+ /* @__PURE__ */ jsxDEV12(Label, {
1303
+ children: "Loop Body"
1304
+ }, undefined, false, undefined, this),
1305
+ /* @__PURE__ */ jsxDEV12(Code, {
1306
+ children: step.params.loopBodyStepId
1307
+ }, undefined, false, undefined, this)
1308
+ ]
1309
+ }, undefined, true, undefined, this)
1310
+ ]
1311
+ }, undefined, true, undefined, this);
1312
+ case "start":
1313
+ return /* @__PURE__ */ jsxDEV12("div", {
1314
+ className: "space-y-2",
1315
+ children: /* @__PURE__ */ jsxDEV12("div", {
1316
+ children: [
1317
+ /* @__PURE__ */ jsxDEV12(Label, {
1318
+ children: "Input Schema"
1319
+ }, undefined, false, undefined, this),
1320
+ /* @__PURE__ */ jsxDEV12(Code, {
1321
+ children: JSON.stringify(step.params.inputSchema, null, 2)
1322
+ }, undefined, false, undefined, this)
1323
+ ]
1324
+ }, undefined, true, undefined, this)
1325
+ }, undefined, false, undefined, this);
1326
+ case "end":
1327
+ return null;
1328
+ }
1329
+ }
1330
+ function Label({ children }) {
1331
+ return /* @__PURE__ */ jsxDEV12("div", {
1332
+ className: "text-[11px] font-medium text-gray-400 uppercase tracking-wide mb-0.5",
1333
+ children
1334
+ }, undefined, false, undefined, this);
1335
+ }
1336
+ function Code({ children }) {
1337
+ return /* @__PURE__ */ jsxDEV12("pre", {
1338
+ className: "text-xs text-gray-700 bg-gray-50 rounded p-2 whitespace-pre-wrap font-mono overflow-auto max-h-[200px]",
1339
+ children
1340
+ }, undefined, false, undefined, this);
1341
+ }
1342
+ function StepDetailPanel({
1343
+ step,
1344
+ diagnostics,
1345
+ onClose
1346
+ }) {
1347
+ return /* @__PURE__ */ jsxDEV12("div", {
1348
+ className: "w-[340px] bg-white border-l border-gray-200 h-full overflow-y-auto shadow-lg",
1349
+ children: [
1350
+ /* @__PURE__ */ jsxDEV12("div", {
1351
+ className: "sticky top-0 bg-white border-b border-gray-200 px-4 py-3 flex items-center justify-between",
1352
+ children: [
1353
+ /* @__PURE__ */ jsxDEV12("div", {
1354
+ className: "flex items-center gap-2",
1355
+ children: [
1356
+ /* @__PURE__ */ jsxDEV12(TypeBadge, {
1357
+ type: step.type
1358
+ }, undefined, false, undefined, this),
1359
+ /* @__PURE__ */ jsxDEV12("span", {
1360
+ className: "font-medium text-sm text-gray-900 truncate",
1361
+ children: step.name
1362
+ }, undefined, false, undefined, this)
1363
+ ]
1364
+ }, undefined, true, undefined, this),
1365
+ /* @__PURE__ */ jsxDEV12("button", {
1366
+ type: "button",
1367
+ onClick: onClose,
1368
+ className: "text-gray-400 hover:text-gray-600 text-lg leading-none",
1369
+ children: "×"
1370
+ }, undefined, false, undefined, this)
1371
+ ]
1372
+ }, undefined, true, undefined, this),
1373
+ /* @__PURE__ */ jsxDEV12("div", {
1374
+ className: "px-4 py-3 space-y-4",
1375
+ children: [
1376
+ /* @__PURE__ */ jsxDEV12("div", {
1377
+ children: [
1378
+ /* @__PURE__ */ jsxDEV12(Label, {
1379
+ children: "Step ID"
1380
+ }, undefined, false, undefined, this),
1381
+ /* @__PURE__ */ jsxDEV12("div", {
1382
+ className: "text-xs font-mono text-gray-600",
1383
+ children: step.id
1384
+ }, undefined, false, undefined, this)
1385
+ ]
1386
+ }, undefined, true, undefined, this),
1387
+ /* @__PURE__ */ jsxDEV12("div", {
1388
+ children: [
1389
+ /* @__PURE__ */ jsxDEV12(Label, {
1390
+ children: "Description"
1391
+ }, undefined, false, undefined, this),
1392
+ /* @__PURE__ */ jsxDEV12("div", {
1393
+ className: "text-xs text-gray-600",
1394
+ children: step.description
1395
+ }, undefined, false, undefined, this)
1396
+ ]
1397
+ }, undefined, true, undefined, this),
1398
+ step.nextStepId && /* @__PURE__ */ jsxDEV12("div", {
1399
+ children: [
1400
+ /* @__PURE__ */ jsxDEV12(Label, {
1401
+ children: "Next Step"
1402
+ }, undefined, false, undefined, this),
1403
+ /* @__PURE__ */ jsxDEV12("div", {
1404
+ className: "text-xs font-mono text-gray-600",
1405
+ children: step.nextStepId
1406
+ }, undefined, false, undefined, this)
1407
+ ]
1408
+ }, undefined, true, undefined, this),
1409
+ /* @__PURE__ */ jsxDEV12("div", {
1410
+ className: "border-t border-gray-100 pt-3",
1411
+ children: [
1412
+ /* @__PURE__ */ jsxDEV12(Label, {
1413
+ children: "Parameters"
1414
+ }, undefined, false, undefined, this),
1415
+ /* @__PURE__ */ jsxDEV12("div", {
1416
+ className: "mt-1",
1417
+ children: /* @__PURE__ */ jsxDEV12(StepParams, {
1418
+ step
1419
+ }, undefined, false, undefined, this)
1420
+ }, undefined, false, undefined, this)
1421
+ ]
1422
+ }, undefined, true, undefined, this),
1423
+ diagnostics.length > 0 && /* @__PURE__ */ jsxDEV12("div", {
1424
+ className: "border-t border-gray-100 pt-3",
1425
+ children: [
1426
+ /* @__PURE__ */ jsxDEV12(Label, {
1427
+ children: "Diagnostics"
1428
+ }, undefined, false, undefined, this),
1429
+ /* @__PURE__ */ jsxDEV12("div", {
1430
+ className: "space-y-2 mt-1",
1431
+ children: diagnostics.map((d) => /* @__PURE__ */ jsxDEV12("div", {
1432
+ className: `text-xs p-2 rounded ${d.severity === "error" ? "bg-red-50 text-red-700 border border-red-200" : "bg-amber-50 text-amber-700 border border-amber-200"}`,
1433
+ children: [
1434
+ /* @__PURE__ */ jsxDEV12("div", {
1435
+ className: "font-medium font-mono",
1436
+ children: d.code
1437
+ }, undefined, false, undefined, this),
1438
+ /* @__PURE__ */ jsxDEV12("div", {
1439
+ className: "mt-0.5",
1440
+ children: d.message
1441
+ }, undefined, false, undefined, this)
1442
+ ]
1443
+ }, `${d.code}-${d.message}`, true, undefined, this))
1444
+ }, undefined, false, undefined, this)
1445
+ ]
1446
+ }, undefined, true, undefined, this)
1447
+ ]
1448
+ }, undefined, true, undefined, this)
1449
+ ]
1450
+ }, undefined, true, undefined, this);
1451
+ }
1452
+
1453
+ // src/viewer/workflow-viewer.tsx
1454
+ import { jsxDEV as jsxDEV13 } from "react/jsx-dev-runtime";
1455
+ var nodeTypes = {
1456
+ toolCall: ToolCallNode,
1457
+ llmPrompt: LlmPromptNode,
1458
+ extractData: ExtractDataNode,
1459
+ switchCase: SwitchCaseNode,
1460
+ groupHeader: GroupHeaderNode,
1461
+ forEach: ForEachNode,
1462
+ end: EndNode,
1463
+ start: StartNode,
1464
+ startStep: StartStepNode
1465
+ };
1466
+ var edgeTypes = {
1467
+ workflow: WorkflowEdge
1468
+ };
1469
+ var EMPTY_DIAGNOSTICS = [];
1470
+ function WorkflowViewer({
1471
+ workflow,
1472
+ diagnostics = EMPTY_DIAGNOSTICS,
1473
+ onStepSelect
1474
+ }) {
1475
+ const layout = useMemo(() => buildLayout(workflow, diagnostics), [workflow, diagnostics]);
1476
+ const [nodes, setNodes, onNodesChange] = useNodesState(layout.nodes);
1477
+ const [edges, setEdges, onEdgesChange] = useEdgesState(layout.edges);
1478
+ const [selectedStep, setSelectedStep] = useState(null);
1479
+ const [selectedDiagnostics, setSelectedDiagnostics] = useState(EMPTY_DIAGNOSTICS);
1480
+ useEffect(() => {
1481
+ setNodes(layout.nodes);
1482
+ setEdges(layout.edges);
1483
+ setSelectedStep(null);
1484
+ }, [layout, setNodes, setEdges]);
1485
+ const onNodeClick = useCallback((_, node) => {
1486
+ const data = node.data;
1487
+ if (!data.step)
1488
+ return;
1489
+ setSelectedStep(data.step);
1490
+ setSelectedDiagnostics(data.diagnostics);
1491
+ onStepSelect?.(data.step.id);
1492
+ }, [onStepSelect]);
1493
+ const onPaneClick = useCallback(() => {
1494
+ setSelectedStep(null);
1495
+ setSelectedDiagnostics([]);
1496
+ onStepSelect?.(null);
1497
+ }, [onStepSelect]);
1498
+ return /* @__PURE__ */ jsxDEV13("div", {
1499
+ className: "flex h-full w-full",
1500
+ children: [
1501
+ /* @__PURE__ */ jsxDEV13("div", {
1502
+ className: "flex-1 relative",
1503
+ children: /* @__PURE__ */ jsxDEV13(ReactFlow, {
1504
+ nodes,
1505
+ edges,
1506
+ onNodesChange,
1507
+ onEdgesChange,
1508
+ onNodeClick,
1509
+ onPaneClick,
1510
+ nodeTypes,
1511
+ edgeTypes,
1512
+ fitView: true,
1513
+ fitViewOptions: { padding: 0.2 },
1514
+ nodesDraggable: false,
1515
+ defaultEdgeOptions: {
1516
+ type: "workflow"
1517
+ },
1518
+ proOptions: { hideAttribution: true },
1519
+ children: [
1520
+ /* @__PURE__ */ jsxDEV13(Background, {
1521
+ color: "#e5e7eb",
1522
+ gap: 16
1523
+ }, undefined, false, undefined, this),
1524
+ /* @__PURE__ */ jsxDEV13(Controls, {
1525
+ showInteractive: false
1526
+ }, undefined, false, undefined, this),
1527
+ /* @__PURE__ */ jsxDEV13(MiniMap, {
1528
+ nodeStrokeWidth: 2,
1529
+ pannable: true,
1530
+ zoomable: true,
1531
+ style: { border: "1px solid #e5e7eb" },
1532
+ nodeColor: "rgba(0, 0, 0, 0.1)"
1533
+ }, undefined, false, undefined, this)
1534
+ ]
1535
+ }, undefined, true, undefined, this)
1536
+ }, undefined, false, undefined, this),
1537
+ selectedStep && /* @__PURE__ */ jsxDEV13(StepDetailPanel, {
1538
+ step: selectedStep,
1539
+ diagnostics: selectedDiagnostics,
1540
+ onClose: () => {
1541
+ setSelectedStep(null);
1542
+ setSelectedDiagnostics([]);
1543
+ onStepSelect?.(null);
1544
+ }
1545
+ }, undefined, false, undefined, this)
1546
+ ]
1547
+ }, undefined, true, undefined, this);
1548
+ }
1549
+ export {
1550
+ buildLayout,
1551
+ WorkflowViewer
1552
+ };
1553
+
1554
+ //# debugId=B74E025EA0D7FD5564756E2164756E21