@getcatalystiq/agent-plane-ui 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/README.md ADDED
@@ -0,0 +1,394 @@
1
+ # @getcatalystiq/agent-plane-ui
2
+
3
+ Embeddable React component library for [AgentPlane](https://github.com/getcatalystiq/agent-plane). Drop full-featured agent management pages into any React app — agents, runs, MCP servers, plugins, settings, and dashboards.
4
+
5
+ Built on the [`@getcatalystiq/agent-plane`](https://www.npmjs.com/package/@getcatalystiq/agent-plane) SDK for data fetching and [SWR](https://swr.vercel.app/) for caching and revalidation.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @getcatalystiq/agent-plane-ui @getcatalystiq/agent-plane swr
11
+ ```
12
+
13
+ Peer dependencies:
14
+ - `react` ^18 or ^19
15
+ - `react-dom` ^18 or ^19
16
+ - `@getcatalystiq/agent-plane` (SDK)
17
+ - `swr` ^2
18
+
19
+ Optional peer dependencies (only needed if you use the corresponding entry points):
20
+ - `recharts` ^2 — required for `@getcatalystiq/agent-plane-ui/charts`
21
+ - `react-markdown` ^9 — required for transcript rendering in `RunDetailPage`
22
+
23
+ ## Quick Start
24
+
25
+ ```tsx
26
+ import { AgentPlane } from "@getcatalystiq/agent-plane";
27
+ import { AgentPlaneProvider, AgentListPage } from "@getcatalystiq/agent-plane-ui";
28
+
29
+ const client = new AgentPlane({
30
+ baseUrl: "https://your-agentplane.vercel.app",
31
+ apiKey: "ap_browser_...", // browser token — see "Browser Token Authentication" below
32
+ });
33
+
34
+ function App() {
35
+ return (
36
+ <AgentPlaneProvider
37
+ client={client}
38
+ onNavigate={(path) => window.location.assign(path)}
39
+ >
40
+ <AgentListPage />
41
+ </AgentPlaneProvider>
42
+ );
43
+ }
44
+ ```
45
+
46
+ ## Provider Configuration
47
+
48
+ Wrap your app (or the AgentPlane section) with `<AgentPlaneProvider>`:
49
+
50
+ ```tsx
51
+ <AgentPlaneProvider
52
+ client={client}
53
+ onNavigate={handleNavigate}
54
+ LinkComponent={MyLink}
55
+ onAuthError={handleAuthError}
56
+ basePath="/admin/agentplane"
57
+ >
58
+ {children}
59
+ </AgentPlaneProvider>
60
+ ```
61
+
62
+ ### Props
63
+
64
+ | Prop | Type | Required | Description |
65
+ |------|------|----------|-------------|
66
+ | `client` | `AgentPlaneClient` | Yes | An instance of the `AgentPlane` SDK client. |
67
+ | `onNavigate` | `(path: string) => void` | Yes | Called when a component needs to navigate. The `path` is relative (e.g. `/agents/ag_123`). |
68
+ | `LinkComponent` | `React.ComponentType<LinkComponentProps>` | No | Custom link component for in-app navigation. Defaults to `<a>`. |
69
+ | `onAuthError` | `(error: Error) => void` | No | Called on 401/403 responses. Use this to refresh browser tokens or redirect to login. |
70
+ | `basePath` | `string` | No | Path prefix prepended to all navigation paths. For example, if your AgentPlane pages live at `/admin/agentplane/agents`, set `basePath` to `"/admin/agentplane"`. Defaults to `""`. |
71
+
72
+ ### LinkComponentProps
73
+
74
+ ```ts
75
+ interface LinkComponentProps {
76
+ href: string;
77
+ children: React.ReactNode;
78
+ className?: string;
79
+ }
80
+ ```
81
+
82
+ ## Navigation Setup
83
+
84
+ ### React Router
85
+
86
+ ```tsx
87
+ import { useNavigate, Link } from "react-router-dom";
88
+
89
+ function AgentPlaneLayout() {
90
+ const navigate = useNavigate();
91
+
92
+ return (
93
+ <AgentPlaneProvider
94
+ client={client}
95
+ onNavigate={(path) => navigate(path)}
96
+ LinkComponent={({ href, children, className }) => (
97
+ <Link to={href} className={className}>{children}</Link>
98
+ )}
99
+ basePath="/agentplane"
100
+ >
101
+ <Outlet />
102
+ </AgentPlaneProvider>
103
+ );
104
+ }
105
+ ```
106
+
107
+ ### Next.js App Router
108
+
109
+ ```tsx
110
+ "use client";
111
+
112
+ import { useRouter } from "next/navigation";
113
+ import NextLink from "next/link";
114
+
115
+ function AgentPlaneLayout({ children }: { children: React.ReactNode }) {
116
+ const router = useRouter();
117
+
118
+ return (
119
+ <AgentPlaneProvider
120
+ client={client}
121
+ onNavigate={(path) => router.push(path)}
122
+ LinkComponent={({ href, children, className }) => (
123
+ <NextLink href={href} className={className}>{children}</NextLink>
124
+ )}
125
+ basePath="/admin/agentplane"
126
+ >
127
+ {children}
128
+ </AgentPlaneProvider>
129
+ );
130
+ }
131
+ ```
132
+
133
+ ## Theming
134
+
135
+ Components use CSS custom properties (variables) for theming. Define them on your root element or a wrapper `<div>`. The library ships with no default styles — you provide the theme.
136
+
137
+ ### CSS Variable Contract
138
+
139
+ ```css
140
+ :root {
141
+ /* Base colors — used for page backgrounds and primary text */
142
+ --background: 0 0% 100%; /* page background (hsl channels) */
143
+ --foreground: 0 0% 3.9%; /* primary text */
144
+
145
+ /* Card surfaces */
146
+ --card: 0 0% 100%; /* card background */
147
+ --card-foreground: 0 0% 3.9%; /* card text */
148
+
149
+ /* Primary — buttons, links, active states */
150
+ --primary: 0 0% 9%;
151
+ --primary-foreground: 0 0% 98%;
152
+
153
+ /* Secondary — secondary buttons, subtle backgrounds */
154
+ --secondary: 0 0% 96.1%;
155
+ --secondary-foreground: 0 0% 9%;
156
+
157
+ /* Muted — disabled states, subtle text, placeholders */
158
+ --muted: 0 0% 96.1%;
159
+ --muted-foreground: 0 0% 45.1%;
160
+
161
+ /* Accent — hover states, highlights */
162
+ --accent: 0 0% 96.1%;
163
+ --accent-foreground: 0 0% 9%;
164
+
165
+ /* Destructive — error states, delete buttons */
166
+ --destructive: 0 84.2% 60.2%;
167
+ --destructive-foreground: 0 0% 98%;
168
+
169
+ /* Borders, inputs, focus rings */
170
+ --border: 0 0% 89.8%;
171
+ --input: 0 0% 89.8%;
172
+ --ring: 0 0% 3.9%;
173
+
174
+ /* Border radius */
175
+ --radius: 0.5rem;
176
+ }
177
+ ```
178
+
179
+ ### Dark Mode
180
+
181
+ Apply the `.dark` class to a parent element to switch to dark mode:
182
+
183
+ ```css
184
+ .dark {
185
+ --background: 0 0% 3.9%;
186
+ --foreground: 0 0% 98%;
187
+ --card: 0 0% 7%;
188
+ --card-foreground: 0 0% 98%;
189
+ --primary: 0 0% 98%;
190
+ --primary-foreground: 0 0% 9%;
191
+ --secondary: 0 0% 14.9%;
192
+ --secondary-foreground: 0 0% 98%;
193
+ --muted: 0 0% 14.9%;
194
+ --muted-foreground: 0 0% 63.9%;
195
+ --accent: 0 0% 14.9%;
196
+ --accent-foreground: 0 0% 98%;
197
+ --destructive: 0 62.8% 30.6%;
198
+ --destructive-foreground: 0 0% 98%;
199
+ --border: 0 0% 14.9%;
200
+ --input: 0 0% 14.9%;
201
+ --ring: 0 0% 83.1%;
202
+ }
203
+ ```
204
+
205
+ ```tsx
206
+ <div className="dark">
207
+ <AgentPlaneProvider {...props}>
208
+ <AgentListPage />
209
+ </AgentPlaneProvider>
210
+ </div>
211
+ ```
212
+
213
+ ## Available Components
214
+
215
+ ### Page Components
216
+
217
+ These are full-page components that handle data fetching, loading states, error handling, and mutations internally.
218
+
219
+ | Component | Description |
220
+ |-----------|-------------|
221
+ | `DashboardPage` | Overview dashboard with stat cards and run/cost charts. |
222
+ | `AgentListPage` | Paginated agent list with search, create, and delete. |
223
+ | `AgentDetailPage` | Agent detail view with tabbed sections (edit, connectors, skills, plugins, schedule, runs, A2A). |
224
+ | `RunListPage` | Paginated run list with status filtering and source badges. |
225
+ | `RunDetailPage` | Run detail with full transcript viewer, cancel button, and metadata. |
226
+ | `McpServerListPage` | MCP server management (list, create, delete). |
227
+ | `PluginMarketplaceListPage` | Plugin marketplace list. |
228
+ | `PluginMarketplaceDetailPage` | Marketplace detail with plugin browser and file editor. |
229
+ | `SettingsPage` | Tenant settings (API keys, budget, timezone). |
230
+
231
+ ### Agent Sub-Components
232
+
233
+ These are used internally by `AgentDetailPage` but are also exported for custom layouts:
234
+
235
+ | Component | Description |
236
+ |-----------|-------------|
237
+ | `AgentEditForm` | Agent configuration form (name, model, tools, permissions). |
238
+ | `AgentConnectorsManager` | Composio and MCP connector management. |
239
+ | `AgentSkillManager` | Skill CRUD for an agent. |
240
+ | `AgentPluginManager` | Plugin installation/removal. |
241
+ | `AgentScheduleForm` | Schedule configuration (cron, timezone). |
242
+ | `AgentRuns` | Run history for a specific agent. |
243
+ | `AgentA2aInfo` | A2A protocol info (endpoint URLs, Agent Card preview). |
244
+
245
+ ### Charts (Separate Entry Point)
246
+
247
+ Import from `@getcatalystiq/agent-plane-ui/charts` to keep Recharts (~50KB) out of your main bundle:
248
+
249
+ ```tsx
250
+ import { RunCharts } from "@getcatalystiq/agent-plane-ui/charts";
251
+
252
+ <RunCharts data={dailyStats} />
253
+ ```
254
+
255
+ | Component | Description |
256
+ |-----------|-------------|
257
+ | `RunCharts` | Line charts for runs/day and cost/day per agent. Accepts `DailyAgentStat[]`. |
258
+
259
+ ### Editor (Separate Entry Point)
260
+
261
+ Import from `@getcatalystiq/agent-plane-ui/editor` for CodeMirror-based components. This keeps CodeMirror (~120KB) out of the core bundle. Currently a placeholder for future phases.
262
+
263
+ ### UI Primitives
264
+
265
+ Low-level building blocks are also exported for custom pages:
266
+
267
+ `Button`, `Card`, `Badge`, `Input`, `Select`, `Textarea`, `FormField`, `FormError`, `SectionHeader`, `DetailPageHeader`, `Skeleton`, `MetricCard`, `AdminTable`, `PaginationBar`, `Tabs`, `Dialog`, `ConfirmDialog`, `CopyButton`, `RunStatusBadge`, `RunSourceBadge`, `LocalDate`, `ModelSelector`, `ToolkitMultiselect`
268
+
269
+ ### Hooks
270
+
271
+ | Hook | Description |
272
+ |------|-------------|
273
+ | `useAgentPlaneClient()` | Returns the SDK client from the nearest provider. |
274
+ | `useAuthError()` | Returns the `onAuthError` callback (if set). |
275
+ | `useNavigation()` | Returns `{ onNavigate, LinkComponent, basePath }` from the nearest provider. |
276
+ | `useApi(key, fetcher, options?)` | SWR wrapper that injects the SDK client into the fetcher. Pass `null` as key to skip fetching. |
277
+
278
+ ```tsx
279
+ import { useApi } from "@getcatalystiq/agent-plane-ui";
280
+
281
+ function MyComponent() {
282
+ const { data, error, isLoading } = useApi(
283
+ "agents",
284
+ (client) => client.agents.list(),
285
+ );
286
+
287
+ if (isLoading) return <Skeleton />;
288
+ if (error) return <div>Error: {error.message}</div>;
289
+ return <pre>{JSON.stringify(data, null, 2)}</pre>;
290
+ }
291
+ ```
292
+
293
+ ## Browser Token Authentication
294
+
295
+ Raw API keys (`ap_live_...`) must never be exposed in client-side code. Use short-lived browser tokens instead.
296
+
297
+ ### Server-Side: Mint a Token
298
+
299
+ ```ts
300
+ // In your API route or server component
301
+ import { AgentPlane } from "@getcatalystiq/agent-plane";
302
+
303
+ const serverClient = new AgentPlane({
304
+ baseUrl: "https://your-agentplane.vercel.app",
305
+ apiKey: process.env.AGENT_PLANE_API_KEY!, // secret, server-only
306
+ });
307
+
308
+ const { token, expires_at } = await serverClient.keys.createBrowserToken({
309
+ scopes: ["agents:read", "agents:write", "runs:read", "runs:write"],
310
+ ttl_seconds: 3600, // optional, defaults to 1 hour
311
+ });
312
+ ```
313
+
314
+ ### Client-Side: Use the Token
315
+
316
+ ```tsx
317
+ "use client";
318
+
319
+ import { AgentPlane } from "@getcatalystiq/agent-plane";
320
+ import { AgentPlaneProvider, AgentListPage } from "@getcatalystiq/agent-plane-ui";
321
+ import { useState, useEffect } from "react";
322
+
323
+ function AgentPlaneApp() {
324
+ const [client, setClient] = useState<AgentPlane | null>(null);
325
+
326
+ useEffect(() => {
327
+ fetch("/api/agentplane-token")
328
+ .then((r) => r.json())
329
+ .then(({ token }) => {
330
+ setClient(
331
+ new AgentPlane({
332
+ baseUrl: "https://your-agentplane.vercel.app",
333
+ apiKey: token,
334
+ }),
335
+ );
336
+ });
337
+ }, []);
338
+
339
+ if (!client) return <div>Loading...</div>;
340
+
341
+ return (
342
+ <AgentPlaneProvider
343
+ client={client}
344
+ onNavigate={(path) => window.location.assign(path)}
345
+ onAuthError={(error) => {
346
+ // Token expired — refresh it
347
+ console.error("Auth error:", error);
348
+ window.location.reload();
349
+ }}
350
+ >
351
+ <AgentListPage />
352
+ </AgentPlaneProvider>
353
+ );
354
+ }
355
+ ```
356
+
357
+ ### Token Refresh Pattern
358
+
359
+ For long-lived sessions, refresh the token before it expires:
360
+
361
+ ```tsx
362
+ function useAgentPlaneToken() {
363
+ const [token, setToken] = useState<string | null>(null);
364
+
365
+ const refresh = useCallback(async () => {
366
+ const res = await fetch("/api/agentplane-token");
367
+ const { token } = await res.json();
368
+ setToken(token);
369
+ }, []);
370
+
371
+ useEffect(() => {
372
+ refresh();
373
+ // Refresh every 50 minutes (token lasts 60)
374
+ const interval = setInterval(refresh, 50 * 60 * 1000);
375
+ return () => clearInterval(interval);
376
+ }, [refresh]);
377
+
378
+ return { token, refresh };
379
+ }
380
+ ```
381
+
382
+ ## Bundle Size
383
+
384
+ The library uses three separate entry points to minimize bundle size:
385
+
386
+ | Entry Point | Approx. Size | Includes |
387
+ |---|---|---|
388
+ | `@getcatalystiq/agent-plane-ui` | ~55KB min | All pages, UI primitives, hooks |
389
+ | `@getcatalystiq/agent-plane-ui/charts` | ~4KB min (+Recharts peer) | `RunCharts` |
390
+ | `@getcatalystiq/agent-plane-ui/editor` | placeholder | CodeMirror editor (future) |
391
+
392
+ ## License
393
+
394
+ MIT
@@ -0,0 +1,129 @@
1
+ 'use strict';
2
+
3
+ var recharts = require('recharts');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/components/pages/run-charts.tsx
7
+ var COLORS = [
8
+ "#6366f1",
9
+ "#f59e0b",
10
+ "#10b981",
11
+ "#ef4444",
12
+ "#3b82f6",
13
+ "#ec4899",
14
+ "#14b8a6",
15
+ "#f97316",
16
+ "#8b5cf6",
17
+ "#84cc16"
18
+ ];
19
+ function AgentLineChart({
20
+ title,
21
+ data,
22
+ agents,
23
+ valueFormatter,
24
+ yLabel
25
+ }) {
26
+ if (data.length === 0) {
27
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-border p-6", children: [
28
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold mb-4", children: title }),
29
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground text-center py-8", children: "No data" })
30
+ ] });
31
+ }
32
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-border p-4", children: [
33
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold mb-4", children: title }),
34
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: 220, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data, margin: { top: 4, right: 16, left: 0, bottom: 0 }, children: [
35
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }),
36
+ /* @__PURE__ */ jsxRuntime.jsx(
37
+ recharts.XAxis,
38
+ {
39
+ dataKey: "date",
40
+ tick: { fontSize: 11, fill: "hsl(var(--muted-foreground))" },
41
+ tickLine: false,
42
+ axisLine: false
43
+ }
44
+ ),
45
+ /* @__PURE__ */ jsxRuntime.jsx(
46
+ recharts.YAxis,
47
+ {
48
+ tick: { fontSize: 11, fill: "hsl(var(--muted-foreground))" },
49
+ tickLine: false,
50
+ axisLine: false,
51
+ ...yLabel ? { label: { value: yLabel, angle: -90, position: "insideLeft", fontSize: 11 } } : {},
52
+ tickFormatter: valueFormatter,
53
+ width: 50
54
+ }
55
+ ),
56
+ /* @__PURE__ */ jsxRuntime.jsx(
57
+ recharts.Tooltip,
58
+ {
59
+ contentStyle: {
60
+ backgroundColor: "hsl(var(--card))",
61
+ border: "1px solid hsl(var(--border))",
62
+ borderRadius: "6px",
63
+ fontSize: 12
64
+ },
65
+ formatter: (value, name) => [valueFormatter(Number(value)), String(name)]
66
+ }
67
+ ),
68
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { wrapperStyle: { fontSize: 12 } }),
69
+ agents.map((agent, i) => /* @__PURE__ */ jsxRuntime.jsx(
70
+ recharts.Line,
71
+ {
72
+ type: "monotone",
73
+ dataKey: agent,
74
+ stroke: COLORS[i % COLORS.length] ?? "#6366f1",
75
+ strokeWidth: 2,
76
+ dot: false,
77
+ activeDot: { r: 4 }
78
+ },
79
+ agent
80
+ ))
81
+ ] }) })
82
+ ] });
83
+ }
84
+ function buildChartData(rows, valueKey) {
85
+ const dateSet = /* @__PURE__ */ new Set();
86
+ const agentSet = /* @__PURE__ */ new Set();
87
+ for (const r of rows) {
88
+ dateSet.add(r.date);
89
+ agentSet.add(r.agent_name);
90
+ }
91
+ const dates = Array.from(dateSet).sort();
92
+ const agents = Array.from(agentSet).sort();
93
+ const lookup = /* @__PURE__ */ new Map();
94
+ for (const r of rows) lookup.set(`${r.date}|${r.agent_name}`, r[valueKey]);
95
+ const data = dates.map((date) => {
96
+ const row = { date };
97
+ for (const agent of agents) {
98
+ row[agent] = lookup.get(`${date}|${agent}`) ?? 0;
99
+ }
100
+ return row;
101
+ });
102
+ return { data, agents };
103
+ }
104
+ function RunCharts({ stats }) {
105
+ const { data: runData, agents: runAgents } = buildChartData(stats, "run_count");
106
+ const { data: costData, agents: costAgents } = buildChartData(stats, "cost_usd");
107
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
108
+ /* @__PURE__ */ jsxRuntime.jsx(
109
+ AgentLineChart,
110
+ {
111
+ title: "Runs per day",
112
+ data: runData,
113
+ agents: runAgents,
114
+ valueFormatter: (v) => String(Math.round(v))
115
+ }
116
+ ),
117
+ /* @__PURE__ */ jsxRuntime.jsx(
118
+ AgentLineChart,
119
+ {
120
+ title: "Cost per day (USD)",
121
+ data: costData,
122
+ agents: costAgents,
123
+ valueFormatter: (v) => `$${v.toFixed(2)}`
124
+ }
125
+ )
126
+ ] });
127
+ }
128
+
129
+ exports.RunCharts = RunCharts;
@@ -0,0 +1,13 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface DailyAgentStat {
4
+ date: string;
5
+ agent_name: string;
6
+ run_count: number;
7
+ cost_usd: number;
8
+ }
9
+ declare function RunCharts({ stats }: {
10
+ stats: DailyAgentStat[];
11
+ }): react_jsx_runtime.JSX.Element;
12
+
13
+ export { type DailyAgentStat, RunCharts };
@@ -0,0 +1,13 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface DailyAgentStat {
4
+ date: string;
5
+ agent_name: string;
6
+ run_count: number;
7
+ cost_usd: number;
8
+ }
9
+ declare function RunCharts({ stats }: {
10
+ stats: DailyAgentStat[];
11
+ }): react_jsx_runtime.JSX.Element;
12
+
13
+ export { type DailyAgentStat, RunCharts };
package/dist/charts.js ADDED
@@ -0,0 +1,127 @@
1
+ import { ResponsiveContainer, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Line } from 'recharts';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ // src/components/pages/run-charts.tsx
5
+ var COLORS = [
6
+ "#6366f1",
7
+ "#f59e0b",
8
+ "#10b981",
9
+ "#ef4444",
10
+ "#3b82f6",
11
+ "#ec4899",
12
+ "#14b8a6",
13
+ "#f97316",
14
+ "#8b5cf6",
15
+ "#84cc16"
16
+ ];
17
+ function AgentLineChart({
18
+ title,
19
+ data,
20
+ agents,
21
+ valueFormatter,
22
+ yLabel
23
+ }) {
24
+ if (data.length === 0) {
25
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border p-6", children: [
26
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold mb-4", children: title }),
27
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground text-center py-8", children: "No data" })
28
+ ] });
29
+ }
30
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border p-4", children: [
31
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold mb-4", children: title }),
32
+ /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: 220, children: /* @__PURE__ */ jsxs(LineChart, { data, margin: { top: 4, right: 16, left: 0, bottom: 0 }, children: [
33
+ /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }),
34
+ /* @__PURE__ */ jsx(
35
+ XAxis,
36
+ {
37
+ dataKey: "date",
38
+ tick: { fontSize: 11, fill: "hsl(var(--muted-foreground))" },
39
+ tickLine: false,
40
+ axisLine: false
41
+ }
42
+ ),
43
+ /* @__PURE__ */ jsx(
44
+ YAxis,
45
+ {
46
+ tick: { fontSize: 11, fill: "hsl(var(--muted-foreground))" },
47
+ tickLine: false,
48
+ axisLine: false,
49
+ ...yLabel ? { label: { value: yLabel, angle: -90, position: "insideLeft", fontSize: 11 } } : {},
50
+ tickFormatter: valueFormatter,
51
+ width: 50
52
+ }
53
+ ),
54
+ /* @__PURE__ */ jsx(
55
+ Tooltip,
56
+ {
57
+ contentStyle: {
58
+ backgroundColor: "hsl(var(--card))",
59
+ border: "1px solid hsl(var(--border))",
60
+ borderRadius: "6px",
61
+ fontSize: 12
62
+ },
63
+ formatter: (value, name) => [valueFormatter(Number(value)), String(name)]
64
+ }
65
+ ),
66
+ /* @__PURE__ */ jsx(Legend, { wrapperStyle: { fontSize: 12 } }),
67
+ agents.map((agent, i) => /* @__PURE__ */ jsx(
68
+ Line,
69
+ {
70
+ type: "monotone",
71
+ dataKey: agent,
72
+ stroke: COLORS[i % COLORS.length] ?? "#6366f1",
73
+ strokeWidth: 2,
74
+ dot: false,
75
+ activeDot: { r: 4 }
76
+ },
77
+ agent
78
+ ))
79
+ ] }) })
80
+ ] });
81
+ }
82
+ function buildChartData(rows, valueKey) {
83
+ const dateSet = /* @__PURE__ */ new Set();
84
+ const agentSet = /* @__PURE__ */ new Set();
85
+ for (const r of rows) {
86
+ dateSet.add(r.date);
87
+ agentSet.add(r.agent_name);
88
+ }
89
+ const dates = Array.from(dateSet).sort();
90
+ const agents = Array.from(agentSet).sort();
91
+ const lookup = /* @__PURE__ */ new Map();
92
+ for (const r of rows) lookup.set(`${r.date}|${r.agent_name}`, r[valueKey]);
93
+ const data = dates.map((date) => {
94
+ const row = { date };
95
+ for (const agent of agents) {
96
+ row[agent] = lookup.get(`${date}|${agent}`) ?? 0;
97
+ }
98
+ return row;
99
+ });
100
+ return { data, agents };
101
+ }
102
+ function RunCharts({ stats }) {
103
+ const { data: runData, agents: runAgents } = buildChartData(stats, "run_count");
104
+ const { data: costData, agents: costAgents } = buildChartData(stats, "cost_usd");
105
+ return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
106
+ /* @__PURE__ */ jsx(
107
+ AgentLineChart,
108
+ {
109
+ title: "Runs per day",
110
+ data: runData,
111
+ agents: runAgents,
112
+ valueFormatter: (v) => String(Math.round(v))
113
+ }
114
+ ),
115
+ /* @__PURE__ */ jsx(
116
+ AgentLineChart,
117
+ {
118
+ title: "Cost per day (USD)",
119
+ data: costData,
120
+ agents: costAgents,
121
+ valueFormatter: (v) => `$${v.toFixed(2)}`
122
+ }
123
+ )
124
+ ] });
125
+ }
126
+
127
+ export { RunCharts };
@@ -0,0 +1,2 @@
1
+ 'use strict';
2
+
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/editor.js ADDED
@@ -0,0 +1 @@
1
+