@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 +394 -0
- package/dist/charts.cjs +129 -0
- package/dist/charts.d.cts +13 -0
- package/dist/charts.d.ts +13 -0
- package/dist/charts.js +127 -0
- package/dist/editor.cjs +2 -0
- package/dist/editor.d.cts +2 -0
- package/dist/editor.d.ts +2 -0
- package/dist/editor.js +1 -0
- package/dist/index.cjs +3936 -0
- package/dist/index.d.cts +635 -0
- package/dist/index.d.ts +635 -0
- package/dist/index.js +3848 -0
- package/package.json +99 -0
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
|
package/dist/charts.cjs
ADDED
|
@@ -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 };
|
package/dist/charts.d.ts
ADDED
|
@@ -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 };
|
package/dist/editor.cjs
ADDED
package/dist/editor.d.ts
ADDED
package/dist/editor.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|