@getcatalystiq/agent-plane-ui 0.1.5 → 0.1.7
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.cjs +451 -120
- package/dist/index.js +451 -120
- package/package.json +1 -1
- package/dist/charts.d.cts +0 -13
- package/dist/charts.d.ts +0 -13
- package/dist/editor.d.cts +0 -2
- package/dist/editor.d.ts +0 -2
- package/dist/index.d.cts +0 -636
- package/dist/index.d.ts +0 -636
package/dist/index.js
CHANGED
|
@@ -1065,6 +1065,7 @@ function CancelRunButton({ runId, onCancelled }) {
|
|
|
1065
1065
|
] }) })
|
|
1066
1066
|
] });
|
|
1067
1067
|
}
|
|
1068
|
+
var emptyForm = { name: "", slug: "", description: "", base_url: "", mcp_endpoint_path: "/mcp" };
|
|
1068
1069
|
function McpServerListPage({ initialData }) {
|
|
1069
1070
|
const { mutate } = useSWRConfig();
|
|
1070
1071
|
const client = useAgentPlaneClient();
|
|
@@ -1073,21 +1074,48 @@ function McpServerListPage({ initialData }) {
|
|
|
1073
1074
|
(c) => c.customConnectors.listServers(),
|
|
1074
1075
|
initialData ? { fallbackData: initialData } : void 0
|
|
1075
1076
|
);
|
|
1076
|
-
const [
|
|
1077
|
-
const [
|
|
1078
|
-
const [
|
|
1077
|
+
const [showCreate, setShowCreate] = useState(false);
|
|
1078
|
+
const [creating, setCreating] = useState(false);
|
|
1079
|
+
const [createForm, setCreateForm] = useState(emptyForm);
|
|
1080
|
+
const [createError, setCreateError] = useState("");
|
|
1081
|
+
const [editTarget, setEditTarget] = useState(null);
|
|
1082
|
+
const [editing, setEditing] = useState(false);
|
|
1083
|
+
const [editForm, setEditForm] = useState({ name: "", description: "" });
|
|
1084
|
+
const [editError, setEditError] = useState("");
|
|
1079
1085
|
const [deleteTarget, setDeleteTarget] = useState(null);
|
|
1080
1086
|
const [deleting, setDeleting] = useState(false);
|
|
1081
1087
|
const [deleteError, setDeleteError] = useState("");
|
|
1082
|
-
async function
|
|
1083
|
-
|
|
1088
|
+
async function handleCreate() {
|
|
1089
|
+
setCreating(true);
|
|
1090
|
+
setCreateError("");
|
|
1084
1091
|
try {
|
|
1085
|
-
await client.customConnectors.createServer(
|
|
1086
|
-
|
|
1087
|
-
|
|
1092
|
+
await client.customConnectors.createServer(createForm);
|
|
1093
|
+
setShowCreate(false);
|
|
1094
|
+
setCreateForm(emptyForm);
|
|
1088
1095
|
mutate("mcp-servers");
|
|
1096
|
+
} catch (err) {
|
|
1097
|
+
setCreateError(err instanceof Error ? err.message : "Failed to create");
|
|
1089
1098
|
} finally {
|
|
1090
|
-
|
|
1099
|
+
setCreating(false);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
function openEdit(server) {
|
|
1103
|
+
setEditTarget(server);
|
|
1104
|
+
setEditForm({ name: server.name, description: server.description });
|
|
1105
|
+
setEditError("");
|
|
1106
|
+
}
|
|
1107
|
+
async function handleEdit() {
|
|
1108
|
+
if (!editTarget) return;
|
|
1109
|
+
setEditing(true);
|
|
1110
|
+
setEditError("");
|
|
1111
|
+
try {
|
|
1112
|
+
await client.customConnectors.updateServer(editTarget.id, editForm);
|
|
1113
|
+
setEditTarget(null);
|
|
1114
|
+
mutate("mcp-servers");
|
|
1115
|
+
} catch (err) {
|
|
1116
|
+
setEditError(err instanceof Error ? err.message : "Failed to update");
|
|
1117
|
+
} finally {
|
|
1118
|
+
setEditing(false);
|
|
1091
1119
|
}
|
|
1092
1120
|
}
|
|
1093
1121
|
async function handleDelete() {
|
|
@@ -1106,7 +1134,7 @@ function McpServerListPage({ initialData }) {
|
|
|
1106
1134
|
}
|
|
1107
1135
|
if (error) {
|
|
1108
1136
|
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
|
|
1109
|
-
"Failed to load
|
|
1137
|
+
"Failed to load connectors: ",
|
|
1110
1138
|
error.message
|
|
1111
1139
|
] }) });
|
|
1112
1140
|
}
|
|
@@ -1114,17 +1142,7 @@ function McpServerListPage({ initialData }) {
|
|
|
1114
1142
|
return /* @__PURE__ */ jsx(Skeleton, { className: "h-96 rounded-lg" });
|
|
1115
1143
|
}
|
|
1116
1144
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
1117
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () =>
|
|
1118
|
-
showAdd && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border p-4 space-y-3", children: [
|
|
1119
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
1120
|
-
/* @__PURE__ */ jsx(Input, { placeholder: "Name", value: newServer.name, onChange: (e) => setNewServer({ ...newServer, name: e.target.value }) }),
|
|
1121
|
-
/* @__PURE__ */ jsx(Input, { placeholder: "Slug", value: newServer.slug, onChange: (e) => setNewServer({ ...newServer, slug: e.target.value }) }),
|
|
1122
|
-
/* @__PURE__ */ jsx(Input, { placeholder: "Description", value: newServer.description, onChange: (e) => setNewServer({ ...newServer, description: e.target.value }) }),
|
|
1123
|
-
/* @__PURE__ */ jsx(Input, { placeholder: "Base URL", value: newServer.base_url, onChange: (e) => setNewServer({ ...newServer, base_url: e.target.value }) }),
|
|
1124
|
-
/* @__PURE__ */ jsx(Input, { placeholder: "MCP Endpoint Path", value: newServer.mcp_endpoint_path, onChange: (e) => setNewServer({ ...newServer, mcp_endpoint_path: e.target.value }) })
|
|
1125
|
-
] }),
|
|
1126
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleAdd, disabled: adding || !newServer.name || !newServer.base_url, children: adding ? "Adding..." : "Add Server" })
|
|
1127
|
-
] }),
|
|
1145
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowCreate(true), children: "+ New Connector" }) }),
|
|
1128
1146
|
/* @__PURE__ */ jsxs(AdminTable, { children: [
|
|
1129
1147
|
/* @__PURE__ */ jsxs(AdminTableHead, { children: [
|
|
1130
1148
|
/* @__PURE__ */ jsx(Th, { children: "Name" }),
|
|
@@ -1138,10 +1156,17 @@ function McpServerListPage({ initialData }) {
|
|
|
1138
1156
|
] }),
|
|
1139
1157
|
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
1140
1158
|
servers.map((s) => /* @__PURE__ */ jsxs(AdminTableRow, { children: [
|
|
1141
|
-
/* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxs(
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1159
|
+
/* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxs(
|
|
1160
|
+
"button",
|
|
1161
|
+
{
|
|
1162
|
+
onClick: () => openEdit(s),
|
|
1163
|
+
className: "flex items-center gap-2 text-left hover:underline cursor-pointer",
|
|
1164
|
+
children: [
|
|
1165
|
+
s.logo_url && /* @__PURE__ */ jsx("img", { src: s.logo_url, alt: "", className: "w-5 h-5 rounded-sm object-contain" }),
|
|
1166
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-primary", children: s.name })
|
|
1167
|
+
]
|
|
1168
|
+
}
|
|
1169
|
+
) }),
|
|
1145
1170
|
/* @__PURE__ */ jsx("td", { className: "p-3 font-mono text-xs text-muted-foreground", children: s.slug }),
|
|
1146
1171
|
/* @__PURE__ */ jsx("td", { className: "p-3 font-mono text-xs text-muted-foreground truncate max-w-xs", title: s.base_url, children: s.base_url }),
|
|
1147
1172
|
/* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsx(Badge, { variant: s.client_id ? "default" : "secondary", children: s.client_id ? "Registered" : "No DCR" }) }),
|
|
@@ -1159,9 +1184,55 @@ function McpServerListPage({ initialData }) {
|
|
|
1159
1184
|
}
|
|
1160
1185
|
) })
|
|
1161
1186
|
] }, s.id)),
|
|
1162
|
-
servers.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { colSpan: 8, children: 'No custom connectors registered. Click "
|
|
1187
|
+
servers.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { colSpan: 8, children: 'No custom connectors registered. Click "+ New Connector" to add one.' })
|
|
1163
1188
|
] })
|
|
1164
1189
|
] }),
|
|
1190
|
+
/* @__PURE__ */ jsx(Dialog, { open: showCreate, onOpenChange: setShowCreate, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
1191
|
+
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "Register Connector" }) }),
|
|
1192
|
+
/* @__PURE__ */ jsxs(DialogBody, { className: "space-y-3", children: [
|
|
1193
|
+
createError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: createError }),
|
|
1194
|
+
/* @__PURE__ */ jsx(FormField, { label: "Name", children: /* @__PURE__ */ jsx(Input, { value: createForm.name, onChange: (e) => setCreateForm({ ...createForm, name: e.target.value }), placeholder: "My MCP Server" }) }),
|
|
1195
|
+
/* @__PURE__ */ jsx(FormField, { label: "Slug", children: /* @__PURE__ */ jsx(Input, { value: createForm.slug, onChange: (e) => setCreateForm({ ...createForm, slug: e.target.value }), placeholder: "my-mcp-server" }) }),
|
|
1196
|
+
/* @__PURE__ */ jsx(FormField, { label: "Description", children: /* @__PURE__ */ jsx(Input, { value: createForm.description, onChange: (e) => setCreateForm({ ...createForm, description: e.target.value }), placeholder: "What this connector does" }) }),
|
|
1197
|
+
/* @__PURE__ */ jsx(FormField, { label: "Base URL", children: /* @__PURE__ */ jsx(Input, { value: createForm.base_url, onChange: (e) => setCreateForm({ ...createForm, base_url: e.target.value }), placeholder: "https://my-server.example.com" }) }),
|
|
1198
|
+
/* @__PURE__ */ jsx(FormField, { label: "MCP Endpoint Path", children: /* @__PURE__ */ jsx(Input, { value: createForm.mcp_endpoint_path, onChange: (e) => setCreateForm({ ...createForm, mcp_endpoint_path: e.target.value }), placeholder: "/mcp" }) })
|
|
1199
|
+
] }),
|
|
1200
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
1201
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setShowCreate(false), children: "Cancel" }),
|
|
1202
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleCreate, disabled: creating || !createForm.name || !createForm.base_url, children: creating ? "Creating..." : "Create" })
|
|
1203
|
+
] })
|
|
1204
|
+
] }) }),
|
|
1205
|
+
/* @__PURE__ */ jsx(Dialog, { open: !!editTarget, onOpenChange: (open) => {
|
|
1206
|
+
if (!open) setEditTarget(null);
|
|
1207
|
+
}, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
1208
|
+
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "Edit Connector" }) }),
|
|
1209
|
+
/* @__PURE__ */ jsxs(DialogBody, { className: "space-y-3", children: [
|
|
1210
|
+
editError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: editError }),
|
|
1211
|
+
/* @__PURE__ */ jsx(FormField, { label: "Name", children: /* @__PURE__ */ jsx(Input, { value: editForm.name, onChange: (e) => setEditForm({ ...editForm, name: e.target.value }) }) }),
|
|
1212
|
+
/* @__PURE__ */ jsx(FormField, { label: "Description", children: /* @__PURE__ */ jsx(Input, { value: editForm.description, onChange: (e) => setEditForm({ ...editForm, description: e.target.value }) }) }),
|
|
1213
|
+
editTarget && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground space-y-1", children: [
|
|
1214
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1215
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Slug:" }),
|
|
1216
|
+
" ",
|
|
1217
|
+
editTarget.slug
|
|
1218
|
+
] }),
|
|
1219
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1220
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Base URL:" }),
|
|
1221
|
+
" ",
|
|
1222
|
+
editTarget.base_url
|
|
1223
|
+
] }),
|
|
1224
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1225
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Endpoint:" }),
|
|
1226
|
+
" ",
|
|
1227
|
+
editTarget.mcp_endpoint_path
|
|
1228
|
+
] })
|
|
1229
|
+
] })
|
|
1230
|
+
] }),
|
|
1231
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
1232
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setEditTarget(null), children: "Cancel" }),
|
|
1233
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleEdit, disabled: editing || !editForm.name, children: editing ? "Saving..." : "Save" })
|
|
1234
|
+
] })
|
|
1235
|
+
] }) }),
|
|
1165
1236
|
/* @__PURE__ */ jsxs(
|
|
1166
1237
|
ConfirmDialog,
|
|
1167
1238
|
{
|
|
@@ -1172,14 +1243,14 @@ function McpServerListPage({ initialData }) {
|
|
|
1172
1243
|
setDeleteError("");
|
|
1173
1244
|
}
|
|
1174
1245
|
},
|
|
1175
|
-
title: "Delete
|
|
1246
|
+
title: "Delete Connector",
|
|
1176
1247
|
confirmLabel: "Delete",
|
|
1177
1248
|
loadingLabel: "Deleting...",
|
|
1178
1249
|
loading: deleting,
|
|
1179
1250
|
error: deleteError,
|
|
1180
1251
|
onConfirm: handleDelete,
|
|
1181
1252
|
children: [
|
|
1182
|
-
"Delete
|
|
1253
|
+
"Delete connector ",
|
|
1183
1254
|
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: deleteTarget?.name }),
|
|
1184
1255
|
"? This cannot be undone."
|
|
1185
1256
|
]
|
|
@@ -3074,128 +3145,388 @@ function AgentConnectorsManager({ agentId, toolkits: initialToolkits, composioAl
|
|
|
3074
3145
|
] });
|
|
3075
3146
|
}
|
|
3076
3147
|
var CodeEditor = lazy(() => import('./code-editor-E7L6Y3LM.js'));
|
|
3077
|
-
function
|
|
3078
|
-
const
|
|
3079
|
-
const
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3148
|
+
function buildTree(files) {
|
|
3149
|
+
const rootFiles = [];
|
|
3150
|
+
const dirMap = /* @__PURE__ */ new Map();
|
|
3151
|
+
function ensureDir(dirPath) {
|
|
3152
|
+
const existing = dirMap.get(dirPath);
|
|
3153
|
+
if (existing) return existing;
|
|
3154
|
+
const parts = dirPath.split("/");
|
|
3155
|
+
const node = {
|
|
3156
|
+
name: parts[parts.length - 1],
|
|
3157
|
+
fullPath: dirPath,
|
|
3158
|
+
children: [],
|
|
3159
|
+
files: []
|
|
3160
|
+
};
|
|
3161
|
+
dirMap.set(dirPath, node);
|
|
3162
|
+
if (parts.length > 1) {
|
|
3163
|
+
const parentPath = parts.slice(0, -1).join("/");
|
|
3164
|
+
const parent = ensureDir(parentPath);
|
|
3165
|
+
if (!parent.children.some((c) => c.fullPath === dirPath)) {
|
|
3166
|
+
parent.children.push(node);
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
return node;
|
|
3170
|
+
}
|
|
3171
|
+
for (const file of files) {
|
|
3172
|
+
const slashIdx = file.path.lastIndexOf("/");
|
|
3173
|
+
if (slashIdx === -1) {
|
|
3174
|
+
rootFiles.push(file);
|
|
3175
|
+
} else {
|
|
3176
|
+
const dirPath = file.path.slice(0, slashIdx);
|
|
3177
|
+
const dir = ensureDir(dirPath);
|
|
3178
|
+
dir.files.push(file);
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
const topLevel = [];
|
|
3182
|
+
for (const node of dirMap.values()) {
|
|
3183
|
+
if (!node.fullPath.includes("/")) {
|
|
3184
|
+
topLevel.push(node);
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
function sortNode(node) {
|
|
3188
|
+
node.children.sort((a, b) => a.name.localeCompare(b.name));
|
|
3189
|
+
node.files.sort((a, b) => a.path.localeCompare(b.path));
|
|
3190
|
+
node.children.forEach(sortNode);
|
|
3191
|
+
}
|
|
3192
|
+
topLevel.forEach(sortNode);
|
|
3193
|
+
topLevel.sort((a, b) => a.name.localeCompare(b.name));
|
|
3194
|
+
rootFiles.sort((a, b) => a.path.localeCompare(b.path));
|
|
3195
|
+
return { rootFiles, rootDirs: topLevel };
|
|
3196
|
+
}
|
|
3197
|
+
function collectAllDirPaths(nodes) {
|
|
3198
|
+
const paths = /* @__PURE__ */ new Set();
|
|
3199
|
+
function walk(node) {
|
|
3200
|
+
paths.add(node.fullPath);
|
|
3201
|
+
node.children.forEach(walk);
|
|
3202
|
+
}
|
|
3203
|
+
nodes.forEach(walk);
|
|
3204
|
+
return paths;
|
|
3205
|
+
}
|
|
3206
|
+
function FileTreeEditor({
|
|
3207
|
+
initialFiles,
|
|
3208
|
+
onSave,
|
|
3209
|
+
onChange,
|
|
3210
|
+
readOnly = false,
|
|
3211
|
+
hideSave = false,
|
|
3212
|
+
title = "Files",
|
|
3213
|
+
saveLabel = "Save",
|
|
3214
|
+
addFolderLabel = "Folder",
|
|
3215
|
+
newFileTemplate = { filename: "SKILL.md", content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n" },
|
|
3216
|
+
savedVersion
|
|
3217
|
+
}) {
|
|
3088
3218
|
const [files, setFiles] = useState(initialFiles);
|
|
3089
|
-
const [selectedPath, setSelectedPath] = useState(
|
|
3219
|
+
const [selectedPath, setSelectedPath] = useState(
|
|
3220
|
+
initialFiles.length > 0 ? initialFiles[0].path : null
|
|
3221
|
+
);
|
|
3090
3222
|
const [saving, setSaving] = useState(false);
|
|
3091
|
-
const [
|
|
3223
|
+
const [expanded, setExpanded] = useState(() => {
|
|
3224
|
+
const { rootDirs } = buildTree(initialFiles);
|
|
3225
|
+
return collectAllDirPaths(rootDirs);
|
|
3226
|
+
});
|
|
3227
|
+
const [showAddFolder, setShowAddFolder] = useState(false);
|
|
3228
|
+
const [newFolderName, setNewFolderName] = useState("");
|
|
3229
|
+
const [addingFileInDir, setAddingFileInDir] = useState(null);
|
|
3092
3230
|
const [newFileName, setNewFileName] = useState("");
|
|
3231
|
+
const [savedSnapshot, setSavedSnapshot] = useState(() => JSON.stringify(initialFiles));
|
|
3232
|
+
useEffect(() => {
|
|
3233
|
+
const snap = JSON.stringify(initialFiles);
|
|
3234
|
+
setSavedSnapshot(snap);
|
|
3235
|
+
setFiles(initialFiles);
|
|
3236
|
+
const { rootDirs } = buildTree(initialFiles);
|
|
3237
|
+
setExpanded(collectAllDirPaths(rootDirs));
|
|
3238
|
+
}, [initialFiles]);
|
|
3239
|
+
useEffect(() => {
|
|
3240
|
+
if (savedVersion !== void 0 && savedVersion > 0) {
|
|
3241
|
+
setSavedSnapshot(JSON.stringify(files));
|
|
3242
|
+
}
|
|
3243
|
+
}, [savedVersion]);
|
|
3244
|
+
const onChangeRef = useRef(onChange);
|
|
3245
|
+
onChangeRef.current = onChange;
|
|
3246
|
+
useEffect(() => {
|
|
3247
|
+
if (onChangeRef.current && JSON.stringify(files) !== savedSnapshot) {
|
|
3248
|
+
onChangeRef.current(files);
|
|
3249
|
+
}
|
|
3250
|
+
}, [files, savedSnapshot]);
|
|
3093
3251
|
const isDirty = useMemo(
|
|
3094
|
-
() => JSON.stringify(files) !==
|
|
3095
|
-
[files,
|
|
3252
|
+
() => JSON.stringify(files) !== savedSnapshot,
|
|
3253
|
+
[files, savedSnapshot]
|
|
3254
|
+
);
|
|
3255
|
+
const tree = useMemo(() => buildTree(files), [files]);
|
|
3256
|
+
const activeFile = useMemo(
|
|
3257
|
+
() => selectedPath ? files.find((f) => f.path === selectedPath) ?? null : null,
|
|
3258
|
+
[files, selectedPath]
|
|
3096
3259
|
);
|
|
3097
|
-
const
|
|
3098
|
-
|
|
3099
|
-
setFiles((prev) => prev.map((f) => f.path ===
|
|
3260
|
+
const handleEditorChange = useCallback((value) => {
|
|
3261
|
+
if (readOnly || !selectedPath) return;
|
|
3262
|
+
setFiles((prev) => prev.map((f) => f.path === selectedPath ? { ...f, content: value } : f));
|
|
3263
|
+
}, [readOnly, selectedPath]);
|
|
3264
|
+
function toggleExpand(dirPath) {
|
|
3265
|
+
setExpanded((prev) => {
|
|
3266
|
+
const next = new Set(prev);
|
|
3267
|
+
if (next.has(dirPath)) next.delete(dirPath);
|
|
3268
|
+
else next.add(dirPath);
|
|
3269
|
+
return next;
|
|
3270
|
+
});
|
|
3271
|
+
}
|
|
3272
|
+
function addFolder() {
|
|
3273
|
+
const name = newFolderName.trim();
|
|
3274
|
+
if (!name) return;
|
|
3275
|
+
const filePath = `${name}/${newFileTemplate.filename}`;
|
|
3276
|
+
if (files.some((f) => f.path === filePath)) return;
|
|
3277
|
+
const content = newFileTemplate.content.replace("# New", `# ${name}`);
|
|
3278
|
+
setFiles((prev) => [...prev, { path: filePath, content }]);
|
|
3279
|
+
setSelectedPath(filePath);
|
|
3280
|
+
setExpanded((prev) => /* @__PURE__ */ new Set([...prev, name]));
|
|
3281
|
+
setNewFolderName("");
|
|
3282
|
+
setShowAddFolder(false);
|
|
3100
3283
|
}
|
|
3101
|
-
function
|
|
3284
|
+
function removeDir(dirPath) {
|
|
3285
|
+
const prefix = dirPath + "/";
|
|
3286
|
+
const affectedFiles = files.filter((f) => f.path.startsWith(prefix));
|
|
3287
|
+
if (affectedFiles.length === 0) return;
|
|
3288
|
+
if (!confirm(`Remove "${dirPath}" and all ${affectedFiles.length} file(s)?`)) return;
|
|
3289
|
+
setFiles((prev) => prev.filter((f) => !f.path.startsWith(prefix)));
|
|
3290
|
+
if (selectedPath && selectedPath.startsWith(prefix)) setSelectedPath(null);
|
|
3291
|
+
}
|
|
3292
|
+
function removeFile(filePath) {
|
|
3293
|
+
const fileName = filePath.split("/").pop() ?? filePath;
|
|
3294
|
+
if (!confirm(`Remove file "${fileName}"?`)) return;
|
|
3295
|
+
setFiles((prev) => prev.filter((f) => f.path !== filePath));
|
|
3296
|
+
if (selectedPath === filePath) setSelectedPath(null);
|
|
3297
|
+
}
|
|
3298
|
+
function addFileInDir(dirPath) {
|
|
3102
3299
|
const name = newFileName.trim();
|
|
3103
|
-
if (!name
|
|
3104
|
-
const
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3300
|
+
if (!name) return;
|
|
3301
|
+
const filePath = dirPath ? `${dirPath}/${name}` : name;
|
|
3302
|
+
if (files.some((f) => f.path === filePath)) return;
|
|
3303
|
+
setFiles((prev) => [...prev, { path: filePath, content: "" }]);
|
|
3304
|
+
setSelectedPath(filePath);
|
|
3108
3305
|
setNewFileName("");
|
|
3306
|
+
setAddingFileInDir(null);
|
|
3109
3307
|
}
|
|
3110
|
-
function
|
|
3111
|
-
setFiles((prev) => prev.filter((f) => f.path !== path));
|
|
3112
|
-
if (selectedPath === path) {
|
|
3113
|
-
setSelectedPath(files.find((f) => f.path !== path)?.path ?? null);
|
|
3114
|
-
}
|
|
3115
|
-
}
|
|
3116
|
-
const handleSave = useCallback(async () => {
|
|
3308
|
+
async function handleSave() {
|
|
3117
3309
|
setSaving(true);
|
|
3118
3310
|
try {
|
|
3119
|
-
|
|
3120
|
-
for (const file of files) {
|
|
3121
|
-
const slashIdx = file.path.lastIndexOf("/");
|
|
3122
|
-
if (slashIdx === -1) {
|
|
3123
|
-
const existing = folderMap.get("(root)") ?? [];
|
|
3124
|
-
existing.push({ path: file.path, content: file.content });
|
|
3125
|
-
folderMap.set("(root)", existing);
|
|
3126
|
-
} else {
|
|
3127
|
-
const folder = file.path.slice(0, slashIdx);
|
|
3128
|
-
const fileName = file.path.slice(slashIdx + 1);
|
|
3129
|
-
const existing = folderMap.get(folder) ?? [];
|
|
3130
|
-
existing.push({ path: fileName, content: file.content });
|
|
3131
|
-
folderMap.set(folder, existing);
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
|
|
3135
|
-
await client.agents.update(agentId, { skills });
|
|
3136
|
-
onSaved?.();
|
|
3311
|
+
await onSave(files);
|
|
3137
3312
|
} finally {
|
|
3138
3313
|
setSaving(false);
|
|
3139
3314
|
}
|
|
3140
|
-
}
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
/* @__PURE__ */
|
|
3145
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleSave, disabled: saving || !isDirty, children: saving ? "Saving..." : "Save Skills" })
|
|
3146
|
-
] }) }),
|
|
3147
|
-
addingFile && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
|
|
3148
|
-
/* @__PURE__ */ jsx(
|
|
3149
|
-
Input,
|
|
3150
|
-
{
|
|
3151
|
-
value: newFileName,
|
|
3152
|
-
onChange: (e) => setNewFileName(e.target.value),
|
|
3153
|
-
placeholder: "folder/SKILL.md",
|
|
3154
|
-
className: "max-w-xs text-sm",
|
|
3155
|
-
onKeyDown: (e) => e.key === "Enter" && addFile()
|
|
3156
|
-
}
|
|
3157
|
-
),
|
|
3158
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: addFile, children: "Add" }),
|
|
3159
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
|
|
3160
|
-
setAddingFile(false);
|
|
3161
|
-
setNewFileName("");
|
|
3162
|
-
}, children: "Cancel" })
|
|
3163
|
-
] }),
|
|
3164
|
-
files.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: 'No skills defined. Click "Add File" to create a skill.' }) : /* @__PURE__ */ jsxs("div", { className: "flex gap-4 min-h-[300px]", children: [
|
|
3165
|
-
/* @__PURE__ */ jsx("div", { className: "w-48 shrink-0 border-r border-border pr-3 space-y-1", children: files.map((f) => /* @__PURE__ */ jsxs(
|
|
3315
|
+
}
|
|
3316
|
+
function renderTreeNode(node, depth) {
|
|
3317
|
+
const isExpanded = expanded.has(node.fullPath);
|
|
3318
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
3319
|
+
/* @__PURE__ */ jsxs(
|
|
3166
3320
|
"div",
|
|
3167
3321
|
{
|
|
3168
|
-
className:
|
|
3169
|
-
|
|
3322
|
+
className: "flex items-center justify-between cursor-pointer hover:bg-muted/50 py-1 pr-2",
|
|
3323
|
+
style: { paddingLeft: `${depth * 16 + 8}px` },
|
|
3324
|
+
onClick: () => toggleExpand(node.fullPath),
|
|
3170
3325
|
children: [
|
|
3171
|
-
/* @__PURE__ */
|
|
3172
|
-
|
|
3326
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium text-xs truncate flex items-center gap-1", children: [
|
|
3327
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: isExpanded ? "\u25BE" : "\u25B8" }),
|
|
3328
|
+
node.name,
|
|
3329
|
+
"/"
|
|
3330
|
+
] }),
|
|
3331
|
+
!readOnly && /* @__PURE__ */ jsx(
|
|
3173
3332
|
"button",
|
|
3174
3333
|
{
|
|
3175
|
-
type: "button",
|
|
3176
3334
|
onClick: (e) => {
|
|
3177
3335
|
e.stopPropagation();
|
|
3178
|
-
|
|
3336
|
+
removeDir(node.fullPath);
|
|
3179
3337
|
},
|
|
3180
|
-
className: "
|
|
3338
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3181
3339
|
children: "\xD7"
|
|
3182
3340
|
}
|
|
3183
3341
|
)
|
|
3184
3342
|
]
|
|
3185
|
-
},
|
|
3186
|
-
f.path
|
|
3187
|
-
)) }),
|
|
3188
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: selectedFile ? /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "h-[300px] animate-pulse bg-muted/50 rounded" }), children: /* @__PURE__ */ jsx(
|
|
3189
|
-
CodeEditor,
|
|
3190
|
-
{
|
|
3191
|
-
value: selectedFile.content,
|
|
3192
|
-
onChange: (val) => updateFileContent(selectedFile.path, val),
|
|
3193
|
-
filename: selectedFile.path
|
|
3194
3343
|
}
|
|
3195
|
-
)
|
|
3344
|
+
),
|
|
3345
|
+
isExpanded && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3346
|
+
node.children.map((child) => renderTreeNode(child, depth + 1)),
|
|
3347
|
+
node.files.map((file) => {
|
|
3348
|
+
const fileName = file.path.split("/").pop() ?? file.path;
|
|
3349
|
+
return /* @__PURE__ */ jsxs(
|
|
3350
|
+
"div",
|
|
3351
|
+
{
|
|
3352
|
+
className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
|
|
3353
|
+
style: { paddingLeft: `${(depth + 1) * 16 + 8}px` },
|
|
3354
|
+
onClick: () => setSelectedPath(file.path),
|
|
3355
|
+
children: [
|
|
3356
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs truncate", children: fileName }),
|
|
3357
|
+
!readOnly && /* @__PURE__ */ jsx(
|
|
3358
|
+
"button",
|
|
3359
|
+
{
|
|
3360
|
+
onClick: (e) => {
|
|
3361
|
+
e.stopPropagation();
|
|
3362
|
+
removeFile(file.path);
|
|
3363
|
+
},
|
|
3364
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3365
|
+
children: "\xD7"
|
|
3366
|
+
}
|
|
3367
|
+
)
|
|
3368
|
+
]
|
|
3369
|
+
},
|
|
3370
|
+
file.path
|
|
3371
|
+
);
|
|
3372
|
+
}),
|
|
3373
|
+
!readOnly && /* @__PURE__ */ jsx("div", { style: { paddingLeft: `${(depth + 1) * 16 + 8}px` }, className: "py-1 pr-2", children: addingFileInDir === node.fullPath ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
3374
|
+
/* @__PURE__ */ jsx(
|
|
3375
|
+
Input,
|
|
3376
|
+
{
|
|
3377
|
+
value: newFileName,
|
|
3378
|
+
onChange: (e) => setNewFileName(e.target.value),
|
|
3379
|
+
placeholder: "file.md",
|
|
3380
|
+
className: "h-6 text-xs",
|
|
3381
|
+
onKeyDown: (e) => e.key === "Enter" && addFileInDir(node.fullPath),
|
|
3382
|
+
autoFocus: true
|
|
3383
|
+
}
|
|
3384
|
+
),
|
|
3385
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => addFileInDir(node.fullPath), size: "sm", className: "h-6 text-xs px-2", children: "+" })
|
|
3386
|
+
] }) : /* @__PURE__ */ jsx(
|
|
3387
|
+
"button",
|
|
3388
|
+
{
|
|
3389
|
+
onClick: () => {
|
|
3390
|
+
setAddingFileInDir(node.fullPath);
|
|
3391
|
+
setNewFileName("");
|
|
3392
|
+
},
|
|
3393
|
+
className: "text-xs text-primary hover:underline",
|
|
3394
|
+
children: "+ File"
|
|
3395
|
+
}
|
|
3396
|
+
) })
|
|
3397
|
+
] })
|
|
3398
|
+
] }, node.fullPath);
|
|
3399
|
+
}
|
|
3400
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
3401
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
3402
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3403
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: title }),
|
|
3404
|
+
isDirty && !readOnly && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
|
|
3405
|
+
readOnly && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
|
|
3406
|
+
] }),
|
|
3407
|
+
!readOnly && !hideSave && /* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
|
|
3408
|
+
] }),
|
|
3409
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4 min-h-[500px]", children: [
|
|
3410
|
+
/* @__PURE__ */ jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
|
|
3411
|
+
/* @__PURE__ */ jsxs("div", { className: "p-2 bg-muted/50 border-b border-border flex items-center justify-between", children: [
|
|
3412
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }),
|
|
3413
|
+
!readOnly && /* @__PURE__ */ jsxs(
|
|
3414
|
+
"button",
|
|
3415
|
+
{
|
|
3416
|
+
onClick: () => setShowAddFolder(!showAddFolder),
|
|
3417
|
+
className: "text-xs text-primary hover:underline",
|
|
3418
|
+
children: [
|
|
3419
|
+
"+ ",
|
|
3420
|
+
addFolderLabel
|
|
3421
|
+
]
|
|
3422
|
+
}
|
|
3423
|
+
)
|
|
3424
|
+
] }),
|
|
3425
|
+
showAddFolder && !readOnly && /* @__PURE__ */ jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
|
|
3426
|
+
/* @__PURE__ */ jsx(
|
|
3427
|
+
Input,
|
|
3428
|
+
{
|
|
3429
|
+
value: newFolderName,
|
|
3430
|
+
onChange: (e) => setNewFolderName(e.target.value),
|
|
3431
|
+
placeholder: "folder-name",
|
|
3432
|
+
className: "h-7 text-xs",
|
|
3433
|
+
onKeyDown: (e) => e.key === "Enter" && addFolder(),
|
|
3434
|
+
autoFocus: true
|
|
3435
|
+
}
|
|
3436
|
+
),
|
|
3437
|
+
/* @__PURE__ */ jsx(Button, { onClick: addFolder, size: "sm", className: "h-7 text-xs px-2", children: "Add" })
|
|
3438
|
+
] }),
|
|
3439
|
+
/* @__PURE__ */ jsxs("div", { className: "text-sm overflow-y-auto", children: [
|
|
3440
|
+
tree.rootFiles.map((file) => /* @__PURE__ */ jsxs(
|
|
3441
|
+
"div",
|
|
3442
|
+
{
|
|
3443
|
+
className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
|
|
3444
|
+
style: { paddingLeft: "8px" },
|
|
3445
|
+
onClick: () => setSelectedPath(file.path),
|
|
3446
|
+
children: [
|
|
3447
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs truncate", children: file.path }),
|
|
3448
|
+
!readOnly && /* @__PURE__ */ jsx(
|
|
3449
|
+
"button",
|
|
3450
|
+
{
|
|
3451
|
+
onClick: (e) => {
|
|
3452
|
+
e.stopPropagation();
|
|
3453
|
+
removeFile(file.path);
|
|
3454
|
+
},
|
|
3455
|
+
className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
|
|
3456
|
+
children: "\xD7"
|
|
3457
|
+
}
|
|
3458
|
+
)
|
|
3459
|
+
]
|
|
3460
|
+
},
|
|
3461
|
+
file.path
|
|
3462
|
+
)),
|
|
3463
|
+
tree.rootDirs.map((node) => renderTreeNode(node, 0)),
|
|
3464
|
+
files.length === 0 && /* @__PURE__ */ jsx("p", { className: "p-3 text-xs text-muted-foreground", children: readOnly ? "No files." : `No ${title.toLowerCase()} yet. Add a ${addFolderLabel.toLowerCase()} to get started.` })
|
|
3465
|
+
] })
|
|
3466
|
+
] }),
|
|
3467
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 border border-border rounded-md overflow-hidden", children: activeFile ? /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col", children: [
|
|
3468
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 bg-muted/50 border-b border-border text-xs text-muted-foreground", children: activeFile.path }),
|
|
3469
|
+
/* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { className: "flex-1 animate-pulse bg-muted/50" }), children: /* @__PURE__ */ jsx(
|
|
3470
|
+
CodeEditor,
|
|
3471
|
+
{
|
|
3472
|
+
value: activeFile.content,
|
|
3473
|
+
onChange: handleEditorChange,
|
|
3474
|
+
filename: activeFile.path
|
|
3475
|
+
}
|
|
3476
|
+
) })
|
|
3477
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "h-full flex items-center justify-center text-muted-foreground text-sm", children: [
|
|
3478
|
+
"Select a file to ",
|
|
3479
|
+
readOnly ? "view" : "edit"
|
|
3480
|
+
] }) })
|
|
3196
3481
|
] })
|
|
3197
3482
|
] });
|
|
3198
3483
|
}
|
|
3484
|
+
function AgentSkillManager({ agentId, initialSkills, onSaved }) {
|
|
3485
|
+
const client = useAgentPlaneClient();
|
|
3486
|
+
const initialFiles = useMemo(
|
|
3487
|
+
() => initialSkills.flatMap(
|
|
3488
|
+
(s) => s.files.map((f) => ({
|
|
3489
|
+
path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
|
|
3490
|
+
content: f.content
|
|
3491
|
+
}))
|
|
3492
|
+
),
|
|
3493
|
+
[initialSkills]
|
|
3494
|
+
);
|
|
3495
|
+
const handleSave = useCallback(async (files) => {
|
|
3496
|
+
const folderMap = /* @__PURE__ */ new Map();
|
|
3497
|
+
for (const file of files) {
|
|
3498
|
+
const slashIdx = file.path.lastIndexOf("/");
|
|
3499
|
+
if (slashIdx === -1) {
|
|
3500
|
+
const existing = folderMap.get("(root)") ?? [];
|
|
3501
|
+
existing.push({ path: file.path, content: file.content });
|
|
3502
|
+
folderMap.set("(root)", existing);
|
|
3503
|
+
} else {
|
|
3504
|
+
const folder = file.path.slice(0, slashIdx);
|
|
3505
|
+
const fileName = file.path.slice(slashIdx + 1);
|
|
3506
|
+
const existing = folderMap.get(folder) ?? [];
|
|
3507
|
+
existing.push({ path: fileName, content: file.content });
|
|
3508
|
+
folderMap.set(folder, existing);
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
|
|
3512
|
+
await client.agents.update(agentId, { skills });
|
|
3513
|
+
onSaved?.();
|
|
3514
|
+
}, [agentId, client, onSaved]);
|
|
3515
|
+
return /* @__PURE__ */ jsx(
|
|
3516
|
+
FileTreeEditor,
|
|
3517
|
+
{
|
|
3518
|
+
initialFiles,
|
|
3519
|
+
onSave: handleSave,
|
|
3520
|
+
title: "Skills",
|
|
3521
|
+
saveLabel: "Save Skills",
|
|
3522
|
+
addFolderLabel: "Skill",
|
|
3523
|
+
newFileTemplate: {
|
|
3524
|
+
filename: "SKILL.md",
|
|
3525
|
+
content: "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n"
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
);
|
|
3529
|
+
}
|
|
3199
3530
|
function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
|
|
3200
3531
|
const client = useAgentPlaneClient();
|
|
3201
3532
|
const [plugins, setPlugins] = useState(initialPlugins);
|