@eidentic/cli 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.
- package/LICENSE +201 -0
- package/README.md +54 -0
- package/dist/chunk-QGM4M3NI.js +37 -0
- package/dist/dist-DDLWLV6O.js +8044 -0
- package/dist/index.js +1127 -0
- package/package.json +70 -0
- package/templates/components/chat.tsx +226 -0
- package/templates/components/run-status.tsx +273 -0
- package/templates/components/workflow-trace.tsx +189 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Copied by `eidentic add component` — yours to edit.
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { useWorkflowRun } from "@eidentic/react";
|
|
6
|
+
import type { StepTrace } from "@eidentic/react";
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Props
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
export interface WorkflowTraceProps {
|
|
13
|
+
/** Workflow run ID to display, or null to show empty state. */
|
|
14
|
+
id: string | null;
|
|
15
|
+
/** Base URL of the Eidentic server. Defaults to same-origin (""). */
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
/** Class name applied to the outermost container. */
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Step row
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
function StatusBadge({ status }: { status: "ok" | "error" }) {
|
|
26
|
+
if (status === "ok") {
|
|
27
|
+
return (
|
|
28
|
+
<span
|
|
29
|
+
className="inline-flex items-center rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700 ring-1 ring-inset ring-emerald-200"
|
|
30
|
+
aria-label="Step passed"
|
|
31
|
+
>
|
|
32
|
+
ok
|
|
33
|
+
</span>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return (
|
|
37
|
+
<span
|
|
38
|
+
className="inline-flex items-center rounded-full bg-red-50 px-2 py-0.5 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-200"
|
|
39
|
+
aria-label="Step errored"
|
|
40
|
+
>
|
|
41
|
+
error
|
|
42
|
+
</span>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function StepRow({ step }: { step: StepTrace }) {
|
|
47
|
+
const indent = step.path.length;
|
|
48
|
+
// Each level of nesting adds 16px of left padding.
|
|
49
|
+
const paddingLeft = `${indent * 16 + 8}px`;
|
|
50
|
+
|
|
51
|
+
const durationLabel =
|
|
52
|
+
step.durationMs >= 1000
|
|
53
|
+
? `${(step.durationMs / 1000).toFixed(2)}s`
|
|
54
|
+
: `${step.durationMs}ms`;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<li
|
|
58
|
+
className="border-b border-zinc-100 last:border-0"
|
|
59
|
+
aria-label={`Step ${step.name}: ${step.status}`}
|
|
60
|
+
>
|
|
61
|
+
<div
|
|
62
|
+
className="flex items-start gap-3 py-2 pr-4"
|
|
63
|
+
style={{ paddingLeft }}
|
|
64
|
+
>
|
|
65
|
+
{/* Tree connector */}
|
|
66
|
+
{indent > 0 && (
|
|
67
|
+
<span className="mt-1.5 h-2 w-2 shrink-0 rounded-full border border-zinc-300" aria-hidden="true" />
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
|
+
{/* Name */}
|
|
71
|
+
<span className="flex-1 truncate font-mono text-sm text-zinc-700">
|
|
72
|
+
{step.name}
|
|
73
|
+
</span>
|
|
74
|
+
|
|
75
|
+
{/* Duration */}
|
|
76
|
+
<span className="shrink-0 text-xs text-zinc-400">{durationLabel}</span>
|
|
77
|
+
|
|
78
|
+
{/* Badge */}
|
|
79
|
+
<StatusBadge status={step.status} />
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{/* Inline error */}
|
|
83
|
+
{step.status === "error" && step.error && (
|
|
84
|
+
<p
|
|
85
|
+
className="px-3 pb-2 text-xs text-red-600"
|
|
86
|
+
style={{ paddingLeft: `calc(${paddingLeft} + 20px)` }}
|
|
87
|
+
role="alert"
|
|
88
|
+
>
|
|
89
|
+
{step.error}
|
|
90
|
+
</p>
|
|
91
|
+
)}
|
|
92
|
+
</li>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// WorkflowTrace
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Renders the step trace of a workflow run as an indented tree.
|
|
102
|
+
*
|
|
103
|
+
* Usage:
|
|
104
|
+
* <WorkflowTrace id={runId} baseUrl="http://localhost:3000" />
|
|
105
|
+
*/
|
|
106
|
+
export function WorkflowTrace({ id, baseUrl = "", className = "" }: WorkflowTraceProps) {
|
|
107
|
+
const { run, trace, loading, error } = useWorkflowRun(id, { baseUrl });
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div
|
|
111
|
+
className={`rounded-2xl border border-zinc-200 bg-white shadow-sm ${className}`}
|
|
112
|
+
role="region"
|
|
113
|
+
aria-label="Workflow trace"
|
|
114
|
+
aria-busy={loading}
|
|
115
|
+
>
|
|
116
|
+
{/* Header */}
|
|
117
|
+
<div className="flex items-center gap-3 border-b border-zinc-100 px-4 py-3">
|
|
118
|
+
<span className="text-sm font-medium text-zinc-800">
|
|
119
|
+
{run ? run.name : "Workflow trace"}
|
|
120
|
+
</span>
|
|
121
|
+
{run && (
|
|
122
|
+
<StatusBadge status={run.status} />
|
|
123
|
+
)}
|
|
124
|
+
{loading && (
|
|
125
|
+
<span className="ml-auto text-xs text-zinc-400" aria-live="polite">
|
|
126
|
+
Loading…
|
|
127
|
+
</span>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{/* Body */}
|
|
132
|
+
<div className="p-2">
|
|
133
|
+
{/* Error fetching */}
|
|
134
|
+
{error && (
|
|
135
|
+
<div
|
|
136
|
+
role="alert"
|
|
137
|
+
className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700"
|
|
138
|
+
>
|
|
139
|
+
{error}
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
|
|
143
|
+
{/* Empty / no id */}
|
|
144
|
+
{!id && !loading && !error && (
|
|
145
|
+
<p className="py-6 text-center text-sm text-zinc-400">
|
|
146
|
+
No workflow run selected.
|
|
147
|
+
</p>
|
|
148
|
+
)}
|
|
149
|
+
|
|
150
|
+
{/* Loading placeholder */}
|
|
151
|
+
{id && loading && trace.length === 0 && (
|
|
152
|
+
<ul aria-label="Loading steps" className="space-y-1 px-2 py-2">
|
|
153
|
+
{[1, 2, 3].map((n) => (
|
|
154
|
+
<li key={n} className="flex items-center gap-3 rounded-lg bg-zinc-50 px-3 py-2">
|
|
155
|
+
<span className="h-3 flex-1 animate-pulse rounded bg-zinc-200" />
|
|
156
|
+
<span className="h-3 w-12 animate-pulse rounded bg-zinc-200" />
|
|
157
|
+
</li>
|
|
158
|
+
))}
|
|
159
|
+
</ul>
|
|
160
|
+
)}
|
|
161
|
+
|
|
162
|
+
{/* Step list */}
|
|
163
|
+
{trace.length > 0 && (
|
|
164
|
+
<ul aria-label="Workflow steps">
|
|
165
|
+
{trace.map((step, i) => (
|
|
166
|
+
<StepRow key={`${step.name}-${i}`} step={step} />
|
|
167
|
+
))}
|
|
168
|
+
</ul>
|
|
169
|
+
)}
|
|
170
|
+
|
|
171
|
+
{/* Loaded but empty trace */}
|
|
172
|
+
{id && !loading && !error && trace.length === 0 && run && (
|
|
173
|
+
<p className="py-6 text-center text-sm text-zinc-400">No steps recorded.</p>
|
|
174
|
+
)}
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
{/* Footer: total duration */}
|
|
178
|
+
{run && (
|
|
179
|
+
<div className="border-t border-zinc-100 px-4 py-2 text-xs text-zinc-400">
|
|
180
|
+
{run.stepCount} step{run.stepCount !== 1 ? "s" : ""} ·{" "}
|
|
181
|
+
{run.durationMs >= 1000
|
|
182
|
+
? `${(run.durationMs / 1000).toFixed(2)}s`
|
|
183
|
+
: `${run.durationMs}ms`}{" "}
|
|
184
|
+
total
|
|
185
|
+
</div>
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
}
|