@getcatalystiq/agent-plane-ui 0.1.11 → 0.1.13

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/dist/index.d.cts CHANGED
@@ -103,6 +103,18 @@ interface AgentPlaneClient {
103
103
  get(marketplaceId: string): Promise<unknown>;
104
104
  listPlugins(marketplaceId: string): Promise<unknown[]>;
105
105
  getPlugin(marketplaceId: string, pluginName: string): Promise<unknown>;
106
+ getPluginFiles(marketplaceId: string, pluginName: string): Promise<unknown>;
107
+ savePluginFiles(marketplaceId: string, pluginName: string, data: {
108
+ skills: {
109
+ path: string;
110
+ content: string;
111
+ }[];
112
+ agents: {
113
+ path: string;
114
+ content: string;
115
+ }[];
116
+ mcpJson: string | null;
117
+ }): Promise<unknown>;
106
118
  create(params: Record<string, unknown>): Promise<unknown>;
107
119
  delete(marketplaceId: string): Promise<void>;
108
120
  updateToken(marketplaceId: string, params: Record<string, unknown>): Promise<unknown>;
package/dist/index.d.ts CHANGED
@@ -103,6 +103,18 @@ interface AgentPlaneClient {
103
103
  get(marketplaceId: string): Promise<unknown>;
104
104
  listPlugins(marketplaceId: string): Promise<unknown[]>;
105
105
  getPlugin(marketplaceId: string, pluginName: string): Promise<unknown>;
106
+ getPluginFiles(marketplaceId: string, pluginName: string): Promise<unknown>;
107
+ savePluginFiles(marketplaceId: string, pluginName: string, data: {
108
+ skills: {
109
+ path: string;
110
+ content: string;
111
+ }[];
112
+ agents: {
113
+ path: string;
114
+ content: string;
115
+ }[];
116
+ mcpJson: string | null;
117
+ }): Promise<unknown>;
106
118
  create(params: Record<string, unknown>): Promise<unknown>;
107
119
  delete(marketplaceId: string): Promise<void>;
108
120
  updateToken(marketplaceId: string, params: Record<string, unknown>): Promise<unknown>;
package/dist/index.js CHANGED
@@ -1,202 +1,13 @@
1
- import * as React3 from 'react';
2
- import React3__default, { createContext, lazy, useRef, useMemo, useContext, useState, useCallback, useEffect, Suspense } from 'react';
1
+ import { cn, Card, CardHeader, CardTitle, CardContent, Button, Badge, useNavigation, useApi, Skeleton, useAgentPlaneClient, Input, supportsClaudeRunner, buttonVariants } from './chunk-HMB7OKXO.js';
2
+ export { AgentPlaneProvider, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Skeleton, badgeVariants, buttonVariants, cn, useAgentPlaneClient, useApi, useAuthError, useNavigation } from './chunk-HMB7OKXO.js';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
- import useSWR, { useSWRConfig } from 'swr';
5
- import { clsx } from 'clsx';
6
- import { twMerge } from 'tailwind-merge';
7
- import { cva } from 'class-variance-authority';
4
+ import * as React3 from 'react';
5
+ import React3__default, { lazy, useState, useCallback, useMemo, useRef, useEffect, Suspense } from 'react';
6
+ import { useSWRConfig } from 'swr';
8
7
  import ReactMarkdown from 'react-markdown';
9
8
  import { Command } from 'cmdk';
10
9
  import * as Popover from '@radix-ui/react-popover';
11
10
 
12
- // src/provider.tsx
13
- var ClientContext = createContext(null);
14
- var NavigationContext = createContext(
15
- null
16
- );
17
- function DefaultLink({ href, children, className }) {
18
- return /* @__PURE__ */ jsx("a", { href, className, children });
19
- }
20
- function AgentPlaneProvider({
21
- client,
22
- onNavigate,
23
- LinkComponent = DefaultLink,
24
- onAuthError,
25
- basePath = "",
26
- children
27
- }) {
28
- const clientRef = useRef(client);
29
- clientRef.current = client;
30
- const onAuthErrorRef = useRef(onAuthError);
31
- onAuthErrorRef.current = onAuthError;
32
- const clientValue = useMemo(
33
- () => ({
34
- // Expose a stable object whose `.client` always points to the latest ref.
35
- get client() {
36
- return clientRef.current;
37
- },
38
- get onAuthError() {
39
- return onAuthErrorRef.current;
40
- }
41
- }),
42
- // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally stable
43
- []
44
- );
45
- const navigationValue = useMemo(
46
- () => ({
47
- onNavigate,
48
- LinkComponent,
49
- basePath
50
- }),
51
- [onNavigate, LinkComponent, basePath]
52
- );
53
- return /* @__PURE__ */ jsx(ClientContext.Provider, { value: clientValue, children: /* @__PURE__ */ jsx(NavigationContext.Provider, { value: navigationValue, children }) });
54
- }
55
- function useAgentPlaneClient() {
56
- const ctx = useContext(ClientContext);
57
- if (!ctx) {
58
- throw new Error(
59
- "useAgentPlaneClient must be used within an <AgentPlaneProvider>"
60
- );
61
- }
62
- return ctx.client;
63
- }
64
- function useAuthError() {
65
- const ctx = useContext(ClientContext);
66
- if (!ctx) {
67
- throw new Error(
68
- "useAuthError must be used within an <AgentPlaneProvider>"
69
- );
70
- }
71
- return ctx.onAuthError;
72
- }
73
- function useNavigation() {
74
- const ctx = useContext(NavigationContext);
75
- if (!ctx) {
76
- throw new Error(
77
- "useNavigation must be used within an <AgentPlaneProvider>"
78
- );
79
- }
80
- return ctx;
81
- }
82
- function useApi(key, fetcher, options) {
83
- const client = useAgentPlaneClient();
84
- const onAuthError = useAuthError();
85
- return useSWR(
86
- key,
87
- () => fetcher(client),
88
- {
89
- revalidateOnFocus: false,
90
- errorRetryCount: 3,
91
- onError: (err) => {
92
- if (onAuthError && err && typeof err === "object" && "status" in err && err.status === 401) {
93
- onAuthError(err instanceof Error ? err : new Error(String(err)));
94
- }
95
- },
96
- ...options
97
- }
98
- );
99
- }
100
- function cn(...inputs) {
101
- return twMerge(clsx(inputs));
102
- }
103
- function supportsClaudeRunner(model) {
104
- return !model.includes("/") || model.startsWith("anthropic/");
105
- }
106
- var buttonVariants = cva(
107
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
108
- {
109
- variants: {
110
- variant: {
111
- default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
112
- destructive: "bg-destructive text-white shadow-sm hover:bg-destructive/90",
113
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
114
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
115
- ghost: "hover:bg-accent hover:text-accent-foreground",
116
- link: "text-primary underline-offset-4 hover:underline"
117
- },
118
- size: {
119
- default: "h-9 px-4 py-2",
120
- sm: "h-8 rounded-md px-3 text-xs",
121
- lg: "h-10 rounded-md px-8",
122
- icon: "h-9 w-9"
123
- }
124
- },
125
- defaultVariants: {
126
- variant: "default",
127
- size: "default"
128
- }
129
- }
130
- );
131
- var Button = React3.forwardRef(
132
- ({ className, variant, size, ...props }, ref) => {
133
- return /* @__PURE__ */ jsx(
134
- "button",
135
- {
136
- className: cn(buttonVariants({ variant, size, className })),
137
- ref,
138
- ...props
139
- }
140
- );
141
- }
142
- );
143
- Button.displayName = "Button";
144
- var Card = React3.forwardRef(
145
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("rounded-xl border bg-card text-card-foreground shadow", className), ...props })
146
- );
147
- Card.displayName = "Card";
148
- var CardHeader = React3.forwardRef(
149
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })
150
- );
151
- CardHeader.displayName = "CardHeader";
152
- var CardTitle = React3.forwardRef(
153
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("font-semibold leading-none tracking-tight", className), ...props })
154
- );
155
- CardTitle.displayName = "CardTitle";
156
- var CardDescription = React3.forwardRef(
157
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("text-sm text-muted-foreground", className), ...props })
158
- );
159
- CardDescription.displayName = "CardDescription";
160
- var CardContent = React3.forwardRef(
161
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props })
162
- );
163
- CardContent.displayName = "CardContent";
164
- var badgeVariants = cva(
165
- "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
166
- {
167
- variants: {
168
- variant: {
169
- default: "border-transparent bg-primary text-primary-foreground shadow",
170
- secondary: "border-transparent bg-secondary text-secondary-foreground",
171
- destructive: "border-transparent bg-destructive text-white shadow",
172
- outline: "text-foreground"
173
- }
174
- },
175
- defaultVariants: {
176
- variant: "default"
177
- }
178
- }
179
- );
180
- function Badge({ className, variant, ...props }) {
181
- return /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
182
- }
183
- var Input = React3.forwardRef(
184
- ({ className, type, ...props }, ref) => {
185
- return /* @__PURE__ */ jsx(
186
- "input",
187
- {
188
- type,
189
- className: cn(
190
- "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
191
- className
192
- ),
193
- ref,
194
- ...props
195
- }
196
- );
197
- }
198
- );
199
- Input.displayName = "Input";
200
11
  function Select({ className = "", ...props }) {
201
12
  return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
202
13
  /* @__PURE__ */ jsx(
@@ -265,9 +76,6 @@ function DetailPageHeader({ backHref, backLabel, title, actions, badge, subtitle
265
76
  subtitle && /* @__PURE__ */ jsx("div", { className: "mt-1", children: subtitle })
266
77
  ] });
267
78
  }
268
- function Skeleton({ className, ...props }) {
269
- return /* @__PURE__ */ jsx("div", { className: cn("animate-pulse rounded-md bg-muted/50", className), ...props });
270
- }
271
79
  function MetricCard({ label, children, className }) {
272
80
  return /* @__PURE__ */ jsxs(Card, { className, children: [
273
81
  /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium text-muted-foreground", children: label }) }),
@@ -1499,7 +1307,7 @@ function PluginMarketplaceDetailPage({ marketplaceId, initialData, initialPlugin
1499
1307
  !plugins ? /* @__PURE__ */ jsx(Skeleton, { className: "h-48 rounded-lg" }) : plugins.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No plugins found in this marketplace." }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: plugins.map((plugin) => /* @__PURE__ */ jsx(
1500
1308
  LinkComponent,
1501
1309
  {
1502
- href: `${basePath}/plugin-marketplaces/${marketplaceId}/${plugin.name}`,
1310
+ href: `${basePath}/plugin-marketplaces/${marketplaceId}/plugins/${plugin.name}`,
1503
1311
  children: /* @__PURE__ */ jsxs(Card, { className: "hover:border-primary/50 transition-colors cursor-pointer h-full", children: [
1504
1312
  /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium flex items-center justify-between", children: [
1505
1313
  plugin.displayName,
@@ -1523,46 +1331,248 @@ function PluginMarketplaceDetailPage({ marketplaceId, initialData, initialPlugin
1523
1331
  ] })
1524
1332
  ] });
1525
1333
  }
1526
- function AgentsTab({ agents }) {
1334
+ function AgentsTab({
1335
+ agents,
1336
+ onSelectAgent,
1337
+ selectedFilename
1338
+ }) {
1527
1339
  if (agents.length === 0) {
1528
1340
  return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4", children: "No agents defined in this plugin." });
1529
1341
  }
1530
- return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: agents.map((agent) => /* @__PURE__ */ jsxs(Card, { children: [
1531
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: agent.name }) }),
1532
- /* @__PURE__ */ jsxs(CardContent, { children: [
1533
- agent.description ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: agent.description }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground italic", children: "No description" }),
1534
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground/60 mt-2 font-mono", children: agent.filename })
1535
- ] })
1536
- ] }, agent.filename)) });
1342
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: agents.map((agent) => /* @__PURE__ */ jsxs(
1343
+ Card,
1344
+ {
1345
+ className: `cursor-pointer transition-colors hover:border-primary/50 ${selectedFilename === agent.filename ? "border-primary ring-1 ring-primary/30" : ""}`,
1346
+ onClick: () => onSelectAgent(agent.filename),
1347
+ children: [
1348
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: agent.name }) }),
1349
+ /* @__PURE__ */ jsxs(CardContent, { children: [
1350
+ agent.description ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: agent.description }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground italic", children: "No description" }),
1351
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground/60 mt-2 font-mono", children: agent.filename })
1352
+ ] })
1353
+ ]
1354
+ },
1355
+ agent.filename
1356
+ )) });
1537
1357
  }
1538
- function SkillsTab({ skills }) {
1358
+ function SkillsTab({
1359
+ skills,
1360
+ onSelectSkill,
1361
+ selectedSkill
1362
+ }) {
1539
1363
  if (skills.length === 0) {
1540
1364
  return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4", children: "No skills defined in this plugin." });
1541
1365
  }
1542
- return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: skills.map((skill) => /* @__PURE__ */ jsxs(Card, { children: [
1543
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: skill }) }),
1544
- /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground font-mono", children: [
1545
- "skills/",
1546
- skill,
1547
- "/"
1548
- ] }) })
1549
- ] }, skill)) });
1366
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: skills.map((skill) => /* @__PURE__ */ jsxs(
1367
+ Card,
1368
+ {
1369
+ className: `cursor-pointer transition-colors hover:border-primary/50 ${selectedSkill === skill ? "border-primary ring-1 ring-primary/30" : ""}`,
1370
+ onClick: () => onSelectSkill(skill),
1371
+ children: [
1372
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: skill }) }),
1373
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground font-mono", children: [
1374
+ "skills/",
1375
+ skill,
1376
+ "/"
1377
+ ] }) })
1378
+ ]
1379
+ },
1380
+ skill
1381
+ )) });
1550
1382
  }
1551
- function ConnectorsTab({ hasMcpJson }) {
1383
+ function ConnectorsTab({
1384
+ hasMcpJson,
1385
+ mcpJsonContent,
1386
+ onSelectConnector,
1387
+ selected
1388
+ }) {
1552
1389
  if (!hasMcpJson) {
1553
1390
  return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4", children: "No connectors defined in this plugin." });
1554
1391
  }
1555
- return /* @__PURE__ */ jsxs(Card, { children: [
1556
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: ".mcp.json" }) }),
1557
- /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "This plugin includes an MCP connector configuration that will be suggested to agents using it." }) })
1392
+ return /* @__PURE__ */ jsxs(
1393
+ Card,
1394
+ {
1395
+ className: `cursor-pointer transition-colors hover:border-primary/50 ${selected ? "border-primary ring-1 ring-primary/30" : ""}`,
1396
+ onClick: onSelectConnector,
1397
+ children: [
1398
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: ".mcp.json" }) }),
1399
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "This plugin includes an MCP connector configuration that will be suggested to agents using it." }) })
1400
+ ]
1401
+ }
1402
+ );
1403
+ }
1404
+ function FileEditorInline({
1405
+ filePath,
1406
+ content,
1407
+ onChange,
1408
+ onSave,
1409
+ onClose,
1410
+ saving,
1411
+ saveError,
1412
+ saveSuccess,
1413
+ readOnly
1414
+ }) {
1415
+ return /* @__PURE__ */ jsxs("div", { className: "mt-4 border border-border rounded-md overflow-hidden", children: [
1416
+ /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 bg-muted/50 border-b border-border flex items-center justify-between", children: [
1417
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground font-mono", children: filePath }),
1418
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1419
+ saveError && /* @__PURE__ */ jsx("span", { className: "text-xs text-destructive", children: saveError }),
1420
+ saveSuccess && /* @__PURE__ */ jsx("span", { className: "text-xs text-green-500", children: saveSuccess }),
1421
+ !readOnly && /* @__PURE__ */ jsx(Button, { size: "sm", onClick: onSave, disabled: saving, className: "h-7 text-xs", children: saving ? "Saving..." : "Save" }),
1422
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: onClose, className: "h-7 text-xs", children: "Close" })
1423
+ ] })
1424
+ ] }),
1425
+ /* @__PURE__ */ jsx(
1426
+ "textarea",
1427
+ {
1428
+ value: content,
1429
+ onChange: (e) => onChange(e.target.value),
1430
+ readOnly,
1431
+ className: "w-full min-h-[400px] p-3 bg-background text-foreground text-sm font-mono resize-y focus:outline-none",
1432
+ spellCheck: false
1433
+ }
1434
+ )
1558
1435
  ] });
1559
1436
  }
1560
1437
  function PluginDetailPage({ marketplaceId, pluginName }) {
1561
1438
  const { LinkComponent, basePath } = useNavigation();
1439
+ const client = useAgentPlaneClient();
1562
1440
  const { data: plugin, error, isLoading } = useApi(
1563
1441
  `marketplace-${marketplaceId}-plugin-${pluginName}`,
1564
1442
  (c) => c.pluginMarketplaces.getPlugin(marketplaceId, pluginName)
1565
1443
  );
1444
+ const [editorState, setEditorState] = useState(null);
1445
+ const [pluginFiles, setPluginFiles] = useState(null);
1446
+ const [filesLoading, setFilesLoading] = useState(false);
1447
+ const [filesError, setFilesError] = useState("");
1448
+ const [editedContent, setEditedContent] = useState(/* @__PURE__ */ new Map());
1449
+ const [saving, setSaving] = useState(false);
1450
+ const [saveError, setSaveError] = useState("");
1451
+ const [saveSuccess, setSaveSuccess] = useState("");
1452
+ const fetchFiles = useCallback(async () => {
1453
+ if (pluginFiles) return pluginFiles;
1454
+ setFilesLoading(true);
1455
+ setFilesError("");
1456
+ try {
1457
+ const files = await client.pluginMarketplaces.getPluginFiles(marketplaceId, pluginName);
1458
+ setPluginFiles(files);
1459
+ return files;
1460
+ } catch (err) {
1461
+ setFilesError(err instanceof Error ? err.message : "Failed to load files");
1462
+ return null;
1463
+ } finally {
1464
+ setFilesLoading(false);
1465
+ }
1466
+ }, [client, marketplaceId, pluginName, pluginFiles]);
1467
+ const handleSelectAgent = useCallback(async (filename) => {
1468
+ if (editorState?.type === "agent" && editorState.identifier === filename) {
1469
+ setEditorState(null);
1470
+ return;
1471
+ }
1472
+ setSaveError("");
1473
+ setSaveSuccess("");
1474
+ const files = await fetchFiles();
1475
+ if (files) {
1476
+ setEditorState({ type: "agent", identifier: filename });
1477
+ }
1478
+ }, [editorState, fetchFiles]);
1479
+ const handleSelectSkill = useCallback(async (skill) => {
1480
+ if (editorState?.type === "skill" && editorState.identifier === skill) {
1481
+ setEditorState(null);
1482
+ return;
1483
+ }
1484
+ setSaveError("");
1485
+ setSaveSuccess("");
1486
+ const files = await fetchFiles();
1487
+ if (files) {
1488
+ setEditorState({ type: "skill", identifier: skill });
1489
+ }
1490
+ }, [editorState, fetchFiles]);
1491
+ const handleSelectConnector = useCallback(async () => {
1492
+ if (editorState?.type === "connector") {
1493
+ setEditorState(null);
1494
+ return;
1495
+ }
1496
+ setSaveError("");
1497
+ setSaveSuccess("");
1498
+ const files = await fetchFiles();
1499
+ if (files) {
1500
+ setEditorState({ type: "connector", identifier: ".mcp.json" });
1501
+ }
1502
+ }, [editorState, fetchFiles]);
1503
+ function getEditorFile() {
1504
+ if (!editorState || !pluginFiles) return null;
1505
+ if (editorState.type === "agent") {
1506
+ const file = pluginFiles.agents.find((f) => f.path === editorState.identifier);
1507
+ if (!file) return null;
1508
+ const editedKey = `agents/${file.path}`;
1509
+ return {
1510
+ path: `agents/${file.path}`,
1511
+ content: editedContent.has(editedKey) ? editedContent.get(editedKey) : file.content
1512
+ };
1513
+ }
1514
+ if (editorState.type === "skill") {
1515
+ const skillFiles = pluginFiles.skills.filter((f) => f.path.startsWith(editorState.identifier + "/") || f.path === editorState.identifier);
1516
+ const file = skillFiles.length > 0 ? skillFiles[0] : null;
1517
+ if (!file) return null;
1518
+ const editedKey = `skills/${file.path}`;
1519
+ return {
1520
+ path: `skills/${file.path}`,
1521
+ content: editedContent.has(editedKey) ? editedContent.get(editedKey) : file.content
1522
+ };
1523
+ }
1524
+ if (editorState.type === "connector") {
1525
+ const editedKey = ".mcp.json";
1526
+ return {
1527
+ path: ".mcp.json",
1528
+ content: editedContent.has(editedKey) ? editedContent.get(editedKey) : pluginFiles.mcpJson ?? ""
1529
+ };
1530
+ }
1531
+ return null;
1532
+ }
1533
+ function handleContentChange(content) {
1534
+ const file = getEditorFile();
1535
+ if (!file) return;
1536
+ setEditedContent((prev) => new Map(prev).set(file.path, content));
1537
+ }
1538
+ async function handleSave() {
1539
+ if (!pluginFiles) return;
1540
+ setSaving(true);
1541
+ setSaveError("");
1542
+ setSaveSuccess("");
1543
+ const updatedSkills = pluginFiles.skills.map((f) => {
1544
+ const key = `skills/${f.path}`;
1545
+ return editedContent.has(key) ? { ...f, content: editedContent.get(key) } : f;
1546
+ });
1547
+ const updatedAgents = pluginFiles.agents.map((f) => {
1548
+ const key = `agents/${f.path}`;
1549
+ return editedContent.has(key) ? { ...f, content: editedContent.get(key) } : f;
1550
+ });
1551
+ const updatedMcpJson = editedContent.has(".mcp.json") ? editedContent.get(".mcp.json") : pluginFiles.mcpJson;
1552
+ try {
1553
+ const result = await client.pluginMarketplaces.savePluginFiles(
1554
+ marketplaceId,
1555
+ pluginName,
1556
+ {
1557
+ skills: updatedSkills,
1558
+ agents: updatedAgents,
1559
+ mcpJson: updatedMcpJson || null
1560
+ }
1561
+ );
1562
+ setPluginFiles({
1563
+ ...pluginFiles,
1564
+ skills: updatedSkills,
1565
+ agents: updatedAgents,
1566
+ mcpJson: updatedMcpJson ?? null
1567
+ });
1568
+ setEditedContent(/* @__PURE__ */ new Map());
1569
+ setSaveSuccess(`Saved (commit ${result.commitSha.slice(0, 7)})`);
1570
+ } catch (err) {
1571
+ setSaveError(err instanceof Error ? err.message : "Failed to save");
1572
+ } finally {
1573
+ setSaving(false);
1574
+ }
1575
+ }
1566
1576
  if (error) {
1567
1577
  return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
1568
1578
  "Failed to load plugin: ",
@@ -1576,18 +1586,90 @@ function PluginDetailPage({ marketplaceId, pluginName }) {
1576
1586
  /* @__PURE__ */ jsx(Skeleton, { className: "h-64 rounded-lg" })
1577
1587
  ] });
1578
1588
  }
1589
+ const readOnly = pluginFiles ? !pluginFiles.isOwned : true;
1590
+ const editorFile = getEditorFile();
1579
1591
  const tabs = [
1580
1592
  {
1581
1593
  label: `Agents (${plugin.agents.length})`,
1582
- content: /* @__PURE__ */ jsx(AgentsTab, { agents: plugin.agents })
1594
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
1595
+ /* @__PURE__ */ jsx(
1596
+ AgentsTab,
1597
+ {
1598
+ agents: plugin.agents,
1599
+ onSelectAgent: handleSelectAgent,
1600
+ selectedFilename: editorState?.type === "agent" ? editorState.identifier : null
1601
+ }
1602
+ ),
1603
+ editorState?.type === "agent" && editorFile && (filesLoading ? /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[400px] rounded-md" }) }) : filesError ? /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-destructive", children: filesError }) : /* @__PURE__ */ jsx(
1604
+ FileEditorInline,
1605
+ {
1606
+ filePath: editorFile.path,
1607
+ content: editorFile.content,
1608
+ onChange: handleContentChange,
1609
+ onSave: handleSave,
1610
+ onClose: () => setEditorState(null),
1611
+ saving,
1612
+ saveError,
1613
+ saveSuccess,
1614
+ readOnly
1615
+ }
1616
+ ))
1617
+ ] })
1583
1618
  },
1584
1619
  {
1585
1620
  label: `Skills (${plugin.skills.length})`,
1586
- content: /* @__PURE__ */ jsx(SkillsTab, { skills: plugin.skills })
1621
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
1622
+ /* @__PURE__ */ jsx(
1623
+ SkillsTab,
1624
+ {
1625
+ skills: plugin.skills,
1626
+ onSelectSkill: handleSelectSkill,
1627
+ selectedSkill: editorState?.type === "skill" ? editorState.identifier : null
1628
+ }
1629
+ ),
1630
+ editorState?.type === "skill" && editorFile && (filesLoading ? /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[400px] rounded-md" }) }) : filesError ? /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-destructive", children: filesError }) : /* @__PURE__ */ jsx(
1631
+ FileEditorInline,
1632
+ {
1633
+ filePath: editorFile.path,
1634
+ content: editorFile.content,
1635
+ onChange: handleContentChange,
1636
+ onSave: handleSave,
1637
+ onClose: () => setEditorState(null),
1638
+ saving,
1639
+ saveError,
1640
+ saveSuccess,
1641
+ readOnly
1642
+ }
1643
+ ))
1644
+ ] })
1587
1645
  },
1588
1646
  {
1589
1647
  label: `Connectors (${plugin.hasMcpJson ? 1 : 0})`,
1590
- content: /* @__PURE__ */ jsx(ConnectorsTab, { hasMcpJson: plugin.hasMcpJson })
1648
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
1649
+ /* @__PURE__ */ jsx(
1650
+ ConnectorsTab,
1651
+ {
1652
+ hasMcpJson: plugin.hasMcpJson,
1653
+ mcpJsonContent: pluginFiles?.mcpJson ?? null,
1654
+ onSelectConnector: handleSelectConnector,
1655
+ selected: editorState?.type === "connector"
1656
+ }
1657
+ ),
1658
+ editorState?.type === "connector" && editorFile && (filesLoading ? /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[400px] rounded-md" }) }) : filesError ? /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-destructive", children: filesError }) : /* @__PURE__ */ jsx(
1659
+ FileEditorInline,
1660
+ {
1661
+ filePath: editorFile.path,
1662
+ content: editorFile.content,
1663
+ onChange: handleContentChange,
1664
+ onSave: handleSave,
1665
+ onClose: () => setEditorState(null),
1666
+ saving,
1667
+ saveError,
1668
+ saveSuccess,
1669
+ readOnly
1670
+ }
1671
+ ))
1672
+ ] })
1591
1673
  }
1592
1674
  ];
1593
1675
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
@@ -1605,7 +1687,8 @@ function PluginDetailPage({ marketplaceId, pluginName }) {
1605
1687
  plugin.version && /* @__PURE__ */ jsxs(Badge, { variant: "outline", children: [
1606
1688
  "v",
1607
1689
  plugin.version
1608
- ] })
1690
+ ] }),
1691
+ pluginFiles && (pluginFiles.isOwned ? /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: "Editable" }) : /* @__PURE__ */ jsx(Badge, { variant: "outline", children: "Read-only" }))
1609
1692
  ] }),
1610
1693
  plugin.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: plugin.description })
1611
1694
  ] }),
@@ -4288,4 +4371,4 @@ function ScheduleCard({
4288
4371
  ] });
4289
4372
  }
4290
4373
 
4291
- export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, AgentPlaneProvider, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, ConfirmDialog, CopyButton, DashboardPage, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, Input, LocalDate, McpServerListPage, MetricCard, ModelSelector, PaginationBar, PluginDetailPage, PluginMarketplaceDetailPage, PluginMarketplaceListPage, RunDetailPage, RunListPage, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, Skeleton, Tabs, Textarea, Th, ToolkitMultiselect, TranscriptViewer, badgeVariants, buttonVariants, cn, parsePaginationParams, useAgentPlaneClient, useApi, useAuthError, useNavigation };
4374
+ export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, ConfirmDialog, CopyButton, DashboardPage, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, LocalDate, McpServerListPage, MetricCard, ModelSelector, PaginationBar, PluginDetailPage, PluginMarketplaceDetailPage, PluginMarketplaceListPage, RunDetailPage, RunListPage, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, Tabs, Textarea, Th, ToolkitMultiselect, TranscriptViewer, parsePaginationParams };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getcatalystiq/agent-plane-ui",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Embeddable React component library for AgentPlane",
5
5
  "type": "module",
6
6
  "exports": {
@@ -55,7 +55,7 @@
55
55
  "@codemirror/lang-json": "^6.0.0",
56
56
  "@codemirror/lang-markdown": "^6.0.0",
57
57
  "@codemirror/theme-one-dark": "^6.0.0",
58
- "@getcatalystiq/agent-plane": "^0.5.9",
58
+ "@getcatalystiq/agent-plane": "^0.5.10",
59
59
  "@uiw/react-codemirror": "^4.0.0",
60
60
  "react": "^18.0.0 || ^19.0.0",
61
61
  "react-dom": "^18.0.0 || ^19.0.0",