@truefoundry/trueforge-ui 0.0.0 → 0.1.0-rc.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +340 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +17 -0
  4. package/README.md +640 -3
  5. package/dist/ConnectorSettings-CLBEWGQJ.js +898 -0
  6. package/dist/ConnectorSettings-CLBEWGQJ.js.map +1 -0
  7. package/dist/DockLayout-WRF7EQLD.js +35 -0
  8. package/dist/DockLayout-WRF7EQLD.js.map +1 -0
  9. package/dist/DrawerLayout-X2UXYTOS.js +156 -0
  10. package/dist/DrawerLayout-X2UXYTOS.js.map +1 -0
  11. package/dist/ModelSettings-IJ55QJQO.js +1082 -0
  12. package/dist/ModelSettings-IJ55QJQO.js.map +1 -0
  13. package/dist/OpenUiFenceBlock-SLLZSTMJ.js +9 -0
  14. package/dist/OpenUiFenceBlock-SLLZSTMJ.js.map +1 -0
  15. package/dist/SandboxSettings-S5XGZWOL.js +505 -0
  16. package/dist/SandboxSettings-S5XGZWOL.js.map +1 -0
  17. package/dist/SettingsBuilder-64Y33NP3.js +128 -0
  18. package/dist/SettingsBuilder-64Y33NP3.js.map +1 -0
  19. package/dist/SidebarLayout-M7HEOZVK.js +236 -0
  20. package/dist/SidebarLayout-M7HEOZVK.js.map +1 -0
  21. package/dist/SkillSettings-MXK4ZEI7.js +389 -0
  22. package/dist/SkillSettings-MXK4ZEI7.js.map +1 -0
  23. package/dist/WidgetLayout-7MDOKLHU.js +101 -0
  24. package/dist/WidgetLayout-7MDOKLHU.js.map +1 -0
  25. package/dist/assistant-ui.d.ts +1 -0
  26. package/dist/assistant-ui.js +9 -0
  27. package/dist/assistant-ui.js.map +1 -0
  28. package/dist/chunk-42GF723P.js +24 -0
  29. package/dist/chunk-42GF723P.js.map +1 -0
  30. package/dist/chunk-CFLVPV5M.js +29 -0
  31. package/dist/chunk-CFLVPV5M.js.map +1 -0
  32. package/dist/chunk-M6MHLVSO.js +8 -0
  33. package/dist/chunk-M6MHLVSO.js.map +1 -0
  34. package/dist/chunk-NBMP2XNL.js +89 -0
  35. package/dist/chunk-NBMP2XNL.js.map +1 -0
  36. package/dist/chunk-UBOFEI77.js +567 -0
  37. package/dist/chunk-UBOFEI77.js.map +1 -0
  38. package/dist/chunk-UHGSHDBM.js +633 -0
  39. package/dist/chunk-UHGSHDBM.js.map +1 -0
  40. package/dist/chunk-UXZOL5GO.js +2088 -0
  41. package/dist/chunk-UXZOL5GO.js.map +1 -0
  42. package/dist/chunk-WFC3W4C3.js +73 -0
  43. package/dist/chunk-WFC3W4C3.js.map +1 -0
  44. package/dist/chunk-XSHXH6EI.js +6800 -0
  45. package/dist/chunk-XSHXH6EI.js.map +1 -0
  46. package/dist/createTrueFoundryServer-B72bU-J7.d.ts +28 -0
  47. package/dist/index.d.ts +1569 -0
  48. package/dist/index.js +781 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/plugins/trueforge-agent-server-adapter/index.d.ts +479 -0
  51. package/dist/plugins/trueforge-agent-server-adapter/index.js +830 -0
  52. package/dist/plugins/trueforge-agent-server-adapter/index.js.map +1 -0
  53. package/dist/styles.css +2 -0
  54. package/package.json +129 -6
  55. package/index.js +0 -1
@@ -0,0 +1,898 @@
1
+ "use client";
2
+ import {
3
+ Button,
4
+ CatalogLogo,
5
+ CenteredModal,
6
+ SearchInput_default,
7
+ auiInputClass,
8
+ getErrorMessage,
9
+ useMCPAuth,
10
+ useToasterOptional
11
+ } from "./chunk-XSHXH6EI.js";
12
+ import {
13
+ Icon,
14
+ useCatalogServer
15
+ } from "./chunk-UHGSHDBM.js";
16
+ import "./chunk-42GF723P.js";
17
+ import {
18
+ cn
19
+ } from "./chunk-UBOFEI77.js";
20
+
21
+ // src/containers/SettingsBuilder/ConnectorSettings.tsx
22
+ import { useCallback, useEffect as useEffect2, useMemo, useState as useState3 } from "react";
23
+
24
+ // src/containers/SettingsBuilder/AddMcpServerForm.tsx
25
+ import { useState } from "react";
26
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
27
+ var AUTH_OPTIONS = [
28
+ { value: "dcr", label: "OAuth" },
29
+ { value: "none", label: "None" },
30
+ { value: "header", label: "API Key" }
31
+ ];
32
+ var inputClassName = auiInputClass("h-11 shadow-sm");
33
+ var RequiredMark = () => /* @__PURE__ */ jsx("span", { className: "ml-0.5 text-failure-bg", "aria-hidden": true, children: "*" });
34
+ var AddMcpServerForm = ({ open, onOpenChange, onAdd, busy = false, error }) => {
35
+ const [name, setName] = useState("");
36
+ const [url, setUrl] = useState("");
37
+ const [authType, setAuthType] = useState("dcr");
38
+ const [apiKey, setApiKey] = useState("");
39
+ const [headerName, setHeaderName] = useState("");
40
+ const resetForm = () => {
41
+ setName("");
42
+ setUrl("");
43
+ setAuthType("dcr");
44
+ setApiKey("");
45
+ setHeaderName("");
46
+ };
47
+ const handleOpenChange = (nextOpen) => {
48
+ if (!nextOpen) resetForm();
49
+ onOpenChange(nextOpen);
50
+ };
51
+ const isValid = !!name.trim() && !!url.trim() && (authType !== "header" || !!apiKey.trim());
52
+ const handleSubmit = async (event) => {
53
+ event.preventDefault();
54
+ if (!isValid || busy) return;
55
+ const auth = authType === "header" ? {
56
+ type: "header",
57
+ apiKey: apiKey.trim(),
58
+ ...headerName.trim() ? { headerName: headerName.trim() } : {}
59
+ } : { type: authType };
60
+ try {
61
+ await onAdd({
62
+ name: name.trim(),
63
+ url: url.trim(),
64
+ auth
65
+ });
66
+ resetForm();
67
+ onOpenChange(false);
68
+ } catch {
69
+ }
70
+ };
71
+ return /* @__PURE__ */ jsx(
72
+ CenteredModal,
73
+ {
74
+ open,
75
+ onOpenChange: handleOpenChange,
76
+ title: "Add MCP server",
77
+ description: "Point at a remote MCP endpoint. It then behaves like any other connector.",
78
+ contentSized: true,
79
+ headerIcon: /* @__PURE__ */ jsx(
80
+ "span",
81
+ {
82
+ className: "flex size-11 shrink-0 items-center justify-center rounded-lg border border-border bg-secondary-bg text-text-primary",
83
+ "aria-hidden": true,
84
+ children: /* @__PURE__ */ jsx(Icon, { name: "mcp-server", className: "size-6" })
85
+ }
86
+ ),
87
+ children: /* @__PURE__ */ jsxs("form", { className: "flex flex-col overflow-y-auto p-5 md:p-6", onSubmit: (e) => void handleSubmit(e), children: [
88
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
89
+ /* @__PURE__ */ jsxs("div", { children: [
90
+ /* @__PURE__ */ jsxs("label", { htmlFor: "mcp-server-name", className: "mb-1.5 block text-sm font-medium text-text-primary", children: [
91
+ "Name",
92
+ /* @__PURE__ */ jsx(RequiredMark, {})
93
+ ] }),
94
+ /* @__PURE__ */ jsx(
95
+ "input",
96
+ {
97
+ id: "mcp-server-name",
98
+ type: "text",
99
+ required: true,
100
+ value: name,
101
+ onChange: (event) => {
102
+ setName(event.target.value);
103
+ },
104
+ placeholder: "analytics-postgres-mcp",
105
+ autoFocus: true,
106
+ className: inputClassName
107
+ }
108
+ )
109
+ ] }),
110
+ /* @__PURE__ */ jsxs("div", { children: [
111
+ /* @__PURE__ */ jsxs("label", { htmlFor: "mcp-server-url", className: "mb-1.5 block text-sm font-medium text-text-primary", children: [
112
+ "URL",
113
+ /* @__PURE__ */ jsx(RequiredMark, {})
114
+ ] }),
115
+ /* @__PURE__ */ jsx(
116
+ "input",
117
+ {
118
+ id: "mcp-server-url",
119
+ type: "url",
120
+ required: true,
121
+ value: url,
122
+ onChange: (event) => {
123
+ setUrl(event.target.value);
124
+ },
125
+ placeholder: "https://mcp.example.com/mcp",
126
+ className: inputClassName
127
+ }
128
+ )
129
+ ] }),
130
+ /* @__PURE__ */ jsxs("fieldset", { children: [
131
+ /* @__PURE__ */ jsxs("legend", { className: "mb-1.5 text-sm font-medium text-text-primary", children: [
132
+ "Auth type",
133
+ /* @__PURE__ */ jsx(RequiredMark, {})
134
+ ] }),
135
+ /* @__PURE__ */ jsx("div", { className: "flex w-full flex-row rounded-md border border-border bg-secondary-bg/40 p-1", children: AUTH_OPTIONS.map((option) => /* @__PURE__ */ jsxs(
136
+ "label",
137
+ {
138
+ className: `flex h-8 flex-1 cursor-pointer items-center justify-center rounded-sm text-sm font-medium transition-colors ${authType === option.value ? "bg-dropdown-selected-item-bg text-dropdown-selected-item-text shadow-sm" : "text-text-secondary hover:text-text-primary"}`,
139
+ children: [
140
+ /* @__PURE__ */ jsx(
141
+ "input",
142
+ {
143
+ type: "radio",
144
+ name: "mcp-auth-type",
145
+ value: option.value,
146
+ checked: authType === option.value,
147
+ onChange: () => {
148
+ setAuthType(option.value);
149
+ },
150
+ className: "sr-only"
151
+ }
152
+ ),
153
+ option.label
154
+ ]
155
+ },
156
+ option.value
157
+ )) })
158
+ ] }),
159
+ authType === "dcr" ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 bg-secondary-bg/40 p-2 rounded-md", children: [
160
+ /* @__PURE__ */ jsx(Icon, { name: "info", className: "size-3.5 mt-1 text-text-secondary" }),
161
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-text-secondary leading-5.5", children: "Sign-in only \u2014 the server must support dynamic client registration. Manual OAuth (client ID / secret) isn't supported yet." })
162
+ ] }) : null,
163
+ authType === "header" ? /* @__PURE__ */ jsxs(Fragment, { children: [
164
+ /* @__PURE__ */ jsxs("div", { children: [
165
+ /* @__PURE__ */ jsxs("label", { htmlFor: "mcp-server-api-key", className: "mb-1.5 block text-sm font-medium text-text-primary", children: [
166
+ "API key",
167
+ /* @__PURE__ */ jsx(RequiredMark, {})
168
+ ] }),
169
+ /* @__PURE__ */ jsx(
170
+ "input",
171
+ {
172
+ id: "mcp-server-api-key",
173
+ type: "password",
174
+ required: true,
175
+ value: apiKey,
176
+ onChange: (event) => {
177
+ setApiKey(event.target.value);
178
+ },
179
+ placeholder: "Paste the server token",
180
+ className: inputClassName
181
+ }
182
+ )
183
+ ] }),
184
+ /* @__PURE__ */ jsxs("div", { children: [
185
+ /* @__PURE__ */ jsxs("label", { htmlFor: "mcp-server-header-name", className: "mb-1.5 block text-sm font-medium text-text-primary", children: [
186
+ "Header name ",
187
+ /* @__PURE__ */ jsx("span", { className: "font-normal text-text-secondary", children: "(optional)" })
188
+ ] }),
189
+ /* @__PURE__ */ jsx(
190
+ "input",
191
+ {
192
+ id: "mcp-server-header-name",
193
+ type: "text",
194
+ value: headerName,
195
+ onChange: (event) => {
196
+ setHeaderName(event.target.value);
197
+ },
198
+ placeholder: "Authorization",
199
+ className: inputClassName
200
+ }
201
+ )
202
+ ] })
203
+ ] }) : null
204
+ ] }),
205
+ /* @__PURE__ */ jsxs("div", { className: "mt-6 space-y-3", children: [
206
+ error ? /* @__PURE__ */ jsx("p", { className: "text-failure-bg text-sm", children: error }) : null,
207
+ /* @__PURE__ */ jsx(Button, { type: "submit", size: "lg", disabled: !isValid || busy, className: "w-full shrink-0", children: "Add" })
208
+ ] })
209
+ ] })
210
+ }
211
+ );
212
+ };
213
+ var AddMcpServerForm_default = AddMcpServerForm;
214
+
215
+ // src/containers/SettingsBuilder/authTypeLabels.ts
216
+ var AUTH_TYPE_LABELS = {
217
+ dcr: "OAuth2",
218
+ header: "API Key",
219
+ none: "No Auth"
220
+ };
221
+
222
+ // src/containers/SettingsBuilder/ConnectorDetails.tsx
223
+ import { useEffect, useLayoutEffect, useRef, useState as useState2 } from "react";
224
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
225
+ function ExpandableDescription({ text }) {
226
+ const contentRef = useRef(null);
227
+ const [expanded, setExpanded] = useState2(false);
228
+ const [canExpand, setCanExpand] = useState2(false);
229
+ const [maxHeight, setMaxHeight] = useState2(16);
230
+ useLayoutEffect(() => {
231
+ setExpanded(false);
232
+ setMaxHeight(16);
233
+ }, [text]);
234
+ useLayoutEffect(() => {
235
+ const el = contentRef.current;
236
+ if (!el) return;
237
+ if (!expanded) {
238
+ setMaxHeight(16);
239
+ setCanExpand(el.scrollWidth > el.clientWidth + 1);
240
+ return;
241
+ }
242
+ setMaxHeight(16);
243
+ const frame = requestAnimationFrame(() => {
244
+ setMaxHeight(el.scrollHeight);
245
+ });
246
+ return () => cancelAnimationFrame(frame);
247
+ }, [text, expanded]);
248
+ if (!text) return null;
249
+ return /* @__PURE__ */ jsxs2("div", { className: "relative min-w-0", children: [
250
+ /* @__PURE__ */ jsxs2(
251
+ "p",
252
+ {
253
+ ref: contentRef,
254
+ style: { maxHeight },
255
+ className: cn(
256
+ "mt-0.5 overflow-hidden font-mono text-xs leading-4 text-text-secondary transition-[max-height] duration-300 ease-in-out",
257
+ !expanded && "whitespace-nowrap",
258
+ !expanded && canExpand && "pr-[5.75rem]"
259
+ ),
260
+ children: [
261
+ text,
262
+ expanded && canExpand ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
263
+ " ",
264
+ /* @__PURE__ */ jsx2(
265
+ "button",
266
+ {
267
+ type: "button",
268
+ className: "cursor-pointer font-mono text-xs font-medium text-text-primary/80 hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus-ring",
269
+ "aria-expanded": true,
270
+ onClick: () => setExpanded(false),
271
+ children: "Show less"
272
+ }
273
+ )
274
+ ] }) : null
275
+ ]
276
+ }
277
+ ),
278
+ !expanded && canExpand ? /* @__PURE__ */ jsxs2(
279
+ "button",
280
+ {
281
+ type: "button",
282
+ className: "absolute top-0.5 right-0 cursor-pointer bg-card-bg pl-1.5 font-mono text-xs font-medium leading-4 text-text-primary/80 hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus-ring",
283
+ "aria-expanded": false,
284
+ onClick: () => setExpanded(true),
285
+ children: [
286
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": true, className: "text-text-secondary", children: "\u2026" }),
287
+ /* @__PURE__ */ jsx2("span", { className: "ml-1", children: "Read more" })
288
+ ]
289
+ }
290
+ ) : null
291
+ ] });
292
+ }
293
+ var ConnectorDetails = ({
294
+ connector,
295
+ onBack,
296
+ onConnectorRefreshed,
297
+ onDisconnect,
298
+ busy = false
299
+ }) => {
300
+ const { connectorCatalog } = useCatalogServer();
301
+ const { handleAuthorize, isOAuthLoading } = useMCPAuth();
302
+ const [tools, setTools] = useState2([]);
303
+ const [toolsLoading, setToolsLoading] = useState2(true);
304
+ const [toolsError, setToolsError] = useState2(null);
305
+ const [refreshingAfterAuth, setRefreshingAfterAuth] = useState2(false);
306
+ useEffect(() => {
307
+ let cancelled = false;
308
+ setTools([]);
309
+ setToolsError(null);
310
+ setToolsLoading(true);
311
+ void connectorCatalog.getToolsByConnectorId({ id: connector.id }).then((result) => {
312
+ if (!cancelled) setTools(result);
313
+ }).catch((err) => {
314
+ if (!cancelled) {
315
+ setToolsError(getErrorMessage(err, "Failed to load connector tools"));
316
+ }
317
+ }).finally(() => {
318
+ if (!cancelled) setToolsLoading(false);
319
+ });
320
+ return () => {
321
+ cancelled = true;
322
+ };
323
+ }, [connector.authenticated, connector.id, connectorCatalog]);
324
+ const refreshAfterAuthentication = async () => {
325
+ setRefreshingAfterAuth(true);
326
+ setToolsError(null);
327
+ try {
328
+ const refreshedConnector = await connectorCatalog.getConnector({ id: connector.id });
329
+ onConnectorRefreshed(refreshedConnector);
330
+ } catch (err) {
331
+ setToolsError(getErrorMessage(err, "Failed to refresh connector details"));
332
+ } finally {
333
+ setRefreshingAfterAuth(false);
334
+ }
335
+ };
336
+ const connecting = isOAuthLoading || refreshingAfterAuth;
337
+ return /* @__PURE__ */ jsxs2("div", { className: "flex h-full flex-col overflow-hidden", children: [
338
+ /* @__PURE__ */ jsxs2(
339
+ "button",
340
+ {
341
+ type: "button",
342
+ onClick: onBack,
343
+ className: "mb-3 inline-flex w-fit items-center gap-1 rounded-md py-1 pr-2 text-sm text-text-secondary hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus-ring",
344
+ children: [
345
+ /* @__PURE__ */ jsx2(Icon, { name: "chevron-left", className: "size-3.5" }),
346
+ "Connectors"
347
+ ]
348
+ }
349
+ ),
350
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col overflow-y-auto pb-1", children: [
351
+ /* @__PURE__ */ jsxs2("header", { className: "flex items-start gap-3", children: [
352
+ /* @__PURE__ */ jsx2(
353
+ "span",
354
+ {
355
+ className: "flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-border bg-secondary-bg",
356
+ "aria-hidden": true,
357
+ children: /* @__PURE__ */ jsx2(Icon, { name: "mcp-server", className: "size-5 text-text-primary" })
358
+ }
359
+ ),
360
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
361
+ /* @__PURE__ */ jsx2("h4", { className: "text-lg font-semibold text-text-primary", children: connector.name }),
362
+ /* @__PURE__ */ jsxs2("div", { className: "mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-sm", children: [
363
+ /* @__PURE__ */ jsxs2(
364
+ "span",
365
+ {
366
+ className: cn(
367
+ "flex items-center gap-1.5 text-xs font-medium",
368
+ connector.authenticated ? "text-success-bg" : "text-text-primary"
369
+ ),
370
+ children: [
371
+ /* @__PURE__ */ jsx2(
372
+ "span",
373
+ {
374
+ className: cn(
375
+ "h-1.5 w-1.5 rounded-full",
376
+ connector.authenticated ? "bg-success-bg" : "bg-primary-button-bg"
377
+ )
378
+ }
379
+ ),
380
+ connector.authenticated ? "Connected" : "Not authenticated"
381
+ ]
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsxs2("span", { className: "text-text-secondary", children: [
385
+ "\xB7 ",
386
+ connector.description
387
+ ] })
388
+ ] }),
389
+ /* @__PURE__ */ jsx2("span", { className: "mt-2 inline-flex items-center rounded-full border border-border bg-secondary-bg/40 px-2 py-0.5 text-xs font-medium text-text-secondary", children: AUTH_TYPE_LABELS[connector.auth.type] ?? AUTH_TYPE_LABELS.none })
390
+ ] }),
391
+ connector.auth.type === "dcr" && !connector.authenticated ? /* @__PURE__ */ jsx2(
392
+ Button,
393
+ {
394
+ variant: "outline",
395
+ size: "sm",
396
+ type: "button",
397
+ disabled: busy || connecting,
398
+ onClick: () => {
399
+ void handleAuthorize(connector.id, (isSuccess) => {
400
+ if (isSuccess) void refreshAfterAuthentication();
401
+ });
402
+ },
403
+ children: connecting ? "Connecting\u2026" : "Connect"
404
+ }
405
+ ) : connector.auth.type === "dcr" && !connector.requiresAuth ? /* @__PURE__ */ jsx2(Button, { variant: "outline", size: "sm", type: "button", disabled: busy, onClick: onDisconnect, children: "Disconnect" }) : null
406
+ ] }),
407
+ /* @__PURE__ */ jsx2("section", { className: "mt-6", "aria-labelledby": "connector-tools-heading", children: toolsLoading ? /* @__PURE__ */ jsx2("div", { className: "rounded-xl border border-dashed border-border p-6 text-center text-sm text-text-secondary", children: "Loading tools\u2026" }) : toolsError ? /* @__PURE__ */ jsxs2("div", { className: "rounded-xl border border-failure-bg/30 bg-failure-bg/10 p-6 text-center text-sm text-failure-bg", children: [
408
+ toolsError,
409
+ connector.auth.type === "dcr" && !connector.authenticated && /* @__PURE__ */ jsx2("div", { className: "font-bold mt-1", children: "Click Connect and authenticate successfully to view tools." })
410
+ ] }) : tools.length > 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
411
+ /* @__PURE__ */ jsxs2("h5", { id: "connector-tools-heading", className: "mb-2 text-[0.8125rem] font-semibold text-text-primary", children: [
412
+ "Tools ",
413
+ /* @__PURE__ */ jsxs2("span", { className: "font-normal text-text-secondary", children: [
414
+ "\xB7 ",
415
+ tools.length
416
+ ] })
417
+ ] }),
418
+ /* @__PURE__ */ jsx2("div", { className: "overflow-hidden rounded-xl border border-border bg-card-bg", children: tools.map((tool) => /* @__PURE__ */ jsx2(
419
+ "article",
420
+ {
421
+ className: "flex min-h-12 items-start gap-3 border-b border-border px-3 py-2 last:border-b-0",
422
+ children: /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
423
+ /* @__PURE__ */ jsx2("h6", { className: "text-sm font-medium text-text-primary", children: tool.name }),
424
+ /* @__PURE__ */ jsx2(ExpandableDescription, { text: tool.description })
425
+ ] })
426
+ },
427
+ tool.id
428
+ )) })
429
+ ] }) : /* @__PURE__ */ jsx2("div", { className: "rounded-xl border border-dashed border-border p-6 text-center text-sm text-text-secondary", children: "No tools reported for this connector." }) })
430
+ ] })
431
+ ] });
432
+ };
433
+ var ConnectorDetails_default = ConnectorDetails;
434
+
435
+ // src/containers/SettingsBuilder/ConnectorSettings.tsx
436
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
437
+ var ConnectorSettings = () => {
438
+ const { connectorCatalog } = useCatalogServer();
439
+ const toaster = useToasterOptional();
440
+ const [query, setQuery] = useState3("");
441
+ const [connectors, setConnectors] = useState3({
442
+ ordered: [],
443
+ authStatusById: /* @__PURE__ */ new Map()
444
+ });
445
+ const [loading, setLoading] = useState3(true);
446
+ const [error, setError] = useState3(null);
447
+ const [formError, setFormError] = useState3(null);
448
+ const [busy, setBusy] = useState3(false);
449
+ const [catalog, setCatalog] = useState3([]);
450
+ const [addMcpServerFormOpen, setAddMcpServerFormOpen] = useState3(false);
451
+ const [connectorAwaitingKey, setConnectorAwaitingKey] = useState3(null);
452
+ const [selectedConnector, setSelectedConnector] = useState3(null);
453
+ const [apiKey, setApiKey] = useState3("");
454
+ const connectorIconMap = useMemo(() => {
455
+ return (catalog ?? []).reduce(
456
+ (acc, entry) => {
457
+ acc[entry.name] = entry.logo ?? "";
458
+ return acc;
459
+ },
460
+ {}
461
+ );
462
+ }, [catalog]);
463
+ const refresh = useCallback(async () => {
464
+ setLoading(true);
465
+ setError(null);
466
+ try {
467
+ const [listed, available] = await Promise.all([
468
+ connectorCatalog.listConnectors(),
469
+ connectorCatalog.getConnectorCatalog()
470
+ ]);
471
+ setCatalog(available);
472
+ const configured = listed ?? [];
473
+ const seenIds = new Set(configured.map((connector) => connector.id));
474
+ const ordered = [
475
+ ...configured.map((connector) => ({ connector, isConfigured: true })),
476
+ ...available.filter((connector) => !seenIds.has(connector.id)).map((connector) => ({ connector, isConfigured: false }))
477
+ ];
478
+ setConnectors({
479
+ ordered,
480
+ authStatusById: new Map(
481
+ ordered.map((item) => [item.connector.id, item.isConfigured ? item.connector.authenticated : false])
482
+ )
483
+ });
484
+ setSelectedConnector((current) => current ? listed.find((item) => item.id === current.id) ?? null : null);
485
+ } catch (err) {
486
+ setError(getErrorMessage(err, "Failed to load connectors"));
487
+ } finally {
488
+ setLoading(false);
489
+ }
490
+ }, [connectorCatalog]);
491
+ useEffect2(() => {
492
+ void refresh();
493
+ }, [refresh]);
494
+ const normalizedQuery = query.trim().toLowerCase();
495
+ const matchingConnectors = useMemo(() => {
496
+ if (!normalizedQuery) return connectors.ordered;
497
+ return connectors.ordered.filter(({ connector }) => {
498
+ return connector.name.toLowerCase().includes(normalizedQuery) || (connector.description?.toLowerCase().includes(normalizedQuery) ?? false) || connector.url.toLowerCase().includes(normalizedQuery);
499
+ });
500
+ }, [connectors.ordered, normalizedQuery]);
501
+ const matchingConnected = useMemo(() => {
502
+ const result = [];
503
+ for (const item of matchingConnectors) {
504
+ if (item.isConfigured) {
505
+ const authenticated = connectors.authStatusById.get(item.connector.id) ?? item.connector.authenticated;
506
+ result.push(
507
+ authenticated === item.connector.authenticated ? item.connector : { ...item.connector, authenticated }
508
+ );
509
+ }
510
+ }
511
+ return result;
512
+ }, [connectors.authStatusById, matchingConnectors]);
513
+ const availableConnectors = useMemo(() => {
514
+ const result = [];
515
+ for (const item of matchingConnectors) {
516
+ if (!item.isConfigured) result.push(item.connector);
517
+ }
518
+ return result;
519
+ }, [matchingConnectors]);
520
+ const runMutation = async (fn, setMutationError = setError) => {
521
+ setBusy(true);
522
+ setError(null);
523
+ try {
524
+ await fn();
525
+ await refresh();
526
+ } catch (err) {
527
+ setMutationError(getErrorMessage(err, "Request failed"));
528
+ throw err;
529
+ } finally {
530
+ setBusy(false);
531
+ }
532
+ };
533
+ const closeApiKeyModal = () => {
534
+ setConnectorAwaitingKey(null);
535
+ setApiKey("");
536
+ setFormError(null);
537
+ };
538
+ const createFromCatalog = async (entry, authOverride) => {
539
+ const auth = authOverride ?? (entry.auth.type === "header" ? {
540
+ type: "header",
541
+ ...entry.auth.headerName ? { headerName: entry.auth.headerName } : {}
542
+ } : entry.auth.type === "dcr" ? { type: "dcr" } : { type: "none" });
543
+ await connectorCatalog.createConnector({
544
+ name: entry.name,
545
+ url: entry.url,
546
+ auth
547
+ });
548
+ };
549
+ const handleConnect = (entry) => {
550
+ if (entry.auth.type === "header") {
551
+ setApiKey("");
552
+ setFormError(null);
553
+ setConnectorAwaitingKey(entry);
554
+ return;
555
+ }
556
+ void runMutation(async () => {
557
+ await createFromCatalog(entry);
558
+ }).catch(() => {
559
+ });
560
+ };
561
+ const handleApiKeySubmit = (event) => {
562
+ event.preventDefault();
563
+ if (!connectorAwaitingKey || !apiKey.trim()) return;
564
+ const entry = connectorAwaitingKey;
565
+ setFormError(null);
566
+ void runMutation(async () => {
567
+ const auth = {
568
+ type: "header",
569
+ apiKey: apiKey.trim(),
570
+ ...entry.auth.type === "header" && entry.auth.headerName ? { headerName: entry.auth.headerName } : {}
571
+ };
572
+ const existing = connectors.ordered.find(({ connector }) => connector.id === entry.id);
573
+ const existingConnector = existing?.isConfigured ? existing.connector : void 0;
574
+ if (existingConnector) {
575
+ await connectorCatalog.updateConnector({
576
+ id: existingConnector.id,
577
+ name: existingConnector.name,
578
+ url: existingConnector.url,
579
+ auth
580
+ });
581
+ } else {
582
+ await createFromCatalog(entry, auth);
583
+ }
584
+ closeApiKeyModal();
585
+ setTimeout(() => {
586
+ toaster?.showSuccess({ title: `${entry.name} ${existingConnector ? "updated" : "connected"}` });
587
+ }, 100);
588
+ }, setFormError).catch(() => {
589
+ });
590
+ };
591
+ const handleAddMcpServer = async (draft) => {
592
+ setFormError(null);
593
+ await runMutation(async () => {
594
+ await connectorCatalog.createConnector({
595
+ name: draft.name,
596
+ url: draft.url,
597
+ auth: draft.auth
598
+ });
599
+ }, setFormError);
600
+ setTimeout(() => {
601
+ toaster?.showSuccess({ title: `${draft.name} added` });
602
+ }, 0);
603
+ };
604
+ const handleDisconnect = (connector) => {
605
+ void runMutation(async () => {
606
+ await connectorCatalog.disconnectConnector({ id: connector.id });
607
+ setSelectedConnector(null);
608
+ }).catch(() => {
609
+ });
610
+ };
611
+ const handleConnectorRefreshed = (refreshedConnector) => {
612
+ setSelectedConnector((current) => current?.id === refreshedConnector.id ? refreshedConnector : current);
613
+ setConnectors((current) => {
614
+ const authStatusById = new Map(current.authStatusById);
615
+ authStatusById.set(refreshedConnector.id, refreshedConnector.authenticated);
616
+ return {
617
+ ordered: current.ordered.map(
618
+ (item) => item.isConfigured && item.connector.id === refreshedConnector.id ? { connector: refreshedConnector, isConfigured: true } : item
619
+ ),
620
+ authStatusById
621
+ };
622
+ });
623
+ };
624
+ const renderConnector = (connector, isConnected) => {
625
+ const logoSrc = connectorIconMap[connector.name];
626
+ const content = /* @__PURE__ */ jsxs3(Fragment3, { children: [
627
+ /* @__PURE__ */ jsx3(
628
+ "span",
629
+ {
630
+ className: "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-border bg-secondary-bg",
631
+ "aria-hidden": true,
632
+ children: logoSrc ? /* @__PURE__ */ jsx3(CatalogLogo, { src: logoSrc, alt: connector.name, className: "size-4.5" }) : /* @__PURE__ */ jsx3(Icon, { name: "mcp-server", className: "size-4.5 text-text-primary" })
633
+ }
634
+ ),
635
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
636
+ /* @__PURE__ */ jsx3("h5", { className: "truncate text-sm font-medium text-text-primary", children: connector.name }),
637
+ /* @__PURE__ */ jsx3("p", { className: "mt-0.5 line-clamp-2 text-sm text-text-secondary sm:truncate", children: connector.description })
638
+ ] })
639
+ ] });
640
+ const rowClassName = "flex min-h-16 w-full items-center gap-3 border-b border-border p-3 text-left last:border-b-0";
641
+ if (isConnected) {
642
+ return /* @__PURE__ */ jsxs3(
643
+ "article",
644
+ {
645
+ className: `${rowClassName} cursor-pointer transition-colors hover:bg-ghost-button-hover/60`,
646
+ onClick: () => {
647
+ setSelectedConnector(connector);
648
+ },
649
+ children: [
650
+ /* @__PURE__ */ jsx3("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: content }),
651
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
652
+ /* @__PURE__ */ jsx3("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxs3(
653
+ "span",
654
+ {
655
+ className: cn(
656
+ "flex items-center gap-1.5 rounded-full border border-border bg-secondary-bg/40 px-2 py-0.5 text-xs font-medium",
657
+ connector.authenticated ? "text-success-bg" : "text-text-primary"
658
+ ),
659
+ children: [
660
+ /* @__PURE__ */ jsx3(
661
+ "span",
662
+ {
663
+ className: cn(
664
+ "h-1.5 w-1.5 rounded-full",
665
+ connector.authenticated ? "bg-success-bg" : "bg-primary-button-bg"
666
+ )
667
+ }
668
+ ),
669
+ connector.authenticated ? "Connected" : "Added"
670
+ ]
671
+ }
672
+ ) }),
673
+ connector.auth.type === "header" ? /* @__PURE__ */ jsxs3(
674
+ Button,
675
+ {
676
+ variant: "secondary",
677
+ size: "sm",
678
+ type: "button",
679
+ disabled: busy,
680
+ onClick: (event) => {
681
+ event.stopPropagation();
682
+ setApiKey("");
683
+ setConnectorAwaitingKey(connector);
684
+ },
685
+ children: [
686
+ /* @__PURE__ */ jsx3(Icon, { name: "wrench", className: "size-3" }),
687
+ "Replace Key"
688
+ ]
689
+ }
690
+ ) : null,
691
+ /* @__PURE__ */ jsx3(Icon, { name: "chevron-right", className: "size-4" })
692
+ ] })
693
+ ]
694
+ },
695
+ connector.id
696
+ );
697
+ }
698
+ return /* @__PURE__ */ jsx3("article", { className: rowClassName, children: content }, connector.id);
699
+ };
700
+ const renderCatalogEntry = (entry) => {
701
+ const logoSrc = connectorIconMap[entry.name];
702
+ return /* @__PURE__ */ jsxs3(
703
+ "article",
704
+ {
705
+ className: "flex min-h-16 w-full items-center gap-3 border-b border-border p-3 text-left last:border-b-0",
706
+ children: [
707
+ /* @__PURE__ */ jsx3(
708
+ "span",
709
+ {
710
+ className: "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-border bg-secondary-bg",
711
+ "aria-hidden": true,
712
+ children: logoSrc ? /* @__PURE__ */ jsx3(CatalogLogo, { src: logoSrc, alt: entry.name, className: "size-4.5" }) : /* @__PURE__ */ jsx3(Icon, { name: "mcp-server", className: "size-4.5 text-text-primary" })
713
+ }
714
+ ),
715
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
716
+ /* @__PURE__ */ jsx3("h5", { className: "truncate text-sm font-medium text-text-primary", children: entry.name }),
717
+ /* @__PURE__ */ jsx3("p", { className: "mt-0.5 line-clamp-2 text-sm text-text-secondary sm:truncate", children: entry.description ?? entry.url })
718
+ ] }),
719
+ /* @__PURE__ */ jsxs3("div", { className: "flex shrink-0 items-center gap-2", children: [
720
+ /* @__PURE__ */ jsx3("span", { className: "hidden text-xs text-text-secondary sm:inline", children: AUTH_TYPE_LABELS[entry.auth.type] ?? AUTH_TYPE_LABELS.none }),
721
+ /* @__PURE__ */ jsx3(
722
+ Button,
723
+ {
724
+ variant: "secondary",
725
+ size: "sm",
726
+ type: "button",
727
+ disabled: busy,
728
+ onClick: () => {
729
+ handleConnect(entry);
730
+ },
731
+ children: entry.auth.type === "dcr" ? "Add" : "Connect"
732
+ }
733
+ )
734
+ ] })
735
+ ]
736
+ },
737
+ entry.id
738
+ );
739
+ };
740
+ const isReplacingKey = useMemo(() => {
741
+ return connectorAwaitingKey !== null && connectors.ordered.some(
742
+ (item) => item.isConfigured && item.connector.id === connectorAwaitingKey.id
743
+ );
744
+ }, [connectorAwaitingKey, connectors.ordered]);
745
+ if (selectedConnector) {
746
+ return /* @__PURE__ */ jsx3(
747
+ ConnectorDetails_default,
748
+ {
749
+ connector: selectedConnector,
750
+ busy,
751
+ onBack: () => {
752
+ setSelectedConnector(null);
753
+ },
754
+ onConnectorRefreshed: handleConnectorRefreshed,
755
+ onDisconnect: () => {
756
+ handleDisconnect(selectedConnector);
757
+ }
758
+ }
759
+ );
760
+ }
761
+ const hasMatches = matchingConnected.length > 0 || availableConnectors.length > 0;
762
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
763
+ /* @__PURE__ */ jsx3("h3", { className: "text-xl font-semibold tracking-tight text-text-primary", children: "Connectors" }),
764
+ /* @__PURE__ */ jsx3("p", { className: "mt-1 text-sm text-text-secondary", children: "Connect tools your agents can use." }),
765
+ error ? /* @__PURE__ */ jsx3("p", { className: "mt-3 rounded-md border border-failure-bg/30 bg-failure-bg/10 px-3 py-2 text-sm text-failure-bg", children: error }) : null,
766
+ /* @__PURE__ */ jsx3("div", { className: "mt-4 flex-1 overflow-y-hidden", children: /* @__PURE__ */ jsxs3("div", { className: "flex h-full flex-col gap-3", children: [
767
+ /* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [
768
+ /* @__PURE__ */ jsx3("div", { className: "flex-1", children: /* @__PURE__ */ jsx3(SearchInput_default, { query, setQuery, placeholder: "Search connectors" }) }),
769
+ /* @__PURE__ */ jsxs3(
770
+ Button,
771
+ {
772
+ variant: "secondary",
773
+ type: "button",
774
+ onClick: () => {
775
+ setFormError(null);
776
+ setAddMcpServerFormOpen(true);
777
+ },
778
+ children: [
779
+ /* @__PURE__ */ jsx3(Icon, { name: "plus", size: "1rem", className: "mr-1" }),
780
+ "Add MCP Server"
781
+ ]
782
+ }
783
+ )
784
+ ] }),
785
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-1 flex-col gap-5 overflow-y-auto pb-1", children: [
786
+ loading ? /* @__PURE__ */ jsx3("p", { className: "py-8 text-center text-sm text-text-secondary", children: "Loading\u2026" }) : null,
787
+ !loading && matchingConnected.length > 0 ? /* @__PURE__ */ jsxs3("section", { "aria-labelledby": "connected-connectors-heading", children: [
788
+ /* @__PURE__ */ jsxs3(
789
+ "h4",
790
+ {
791
+ id: "connected-connectors-heading",
792
+ className: "mb-2 text-[0.8125rem] font-semibold uppercase text-text-secondary",
793
+ children: [
794
+ "Configured \xB7 ",
795
+ matchingConnected.length
796
+ ]
797
+ }
798
+ ),
799
+ /* @__PURE__ */ jsx3("div", { className: "overflow-hidden rounded-xl border border-border bg-card-bg", children: matchingConnected.map((connector) => renderConnector(connector, true)) })
800
+ ] }) : null,
801
+ !loading && availableConnectors.length > 0 ? /* @__PURE__ */ jsxs3("section", { "aria-labelledby": "available-connectors-heading", children: [
802
+ /* @__PURE__ */ jsxs3(
803
+ "h4",
804
+ {
805
+ id: "available-connectors-heading",
806
+ className: "mb-2 text-[0.8125rem] font-semibold uppercase text-text-secondary",
807
+ children: [
808
+ "Available \xB7 ",
809
+ availableConnectors.length
810
+ ]
811
+ }
812
+ ),
813
+ /* @__PURE__ */ jsx3("div", { className: "overflow-hidden rounded-xl border border-border bg-card-bg", children: availableConnectors.map((entry) => renderCatalogEntry(entry)) })
814
+ ] }) : null,
815
+ !loading && !hasMatches ? /* @__PURE__ */ jsx3("div", { className: "flex flex-1 items-center justify-center rounded-xl border border-dashed border-border p-8 text-center text-sm text-text-secondary", children: query.trim() ? `No connectors match \u201C${query.trim()}\u201D.` : "No connectors yet." }) : null
816
+ ] }),
817
+ /* @__PURE__ */ jsx3(
818
+ CenteredModal,
819
+ {
820
+ open: connectorAwaitingKey !== null,
821
+ onOpenChange: (open) => {
822
+ if (!open) closeApiKeyModal();
823
+ },
824
+ title: `${isReplacingKey ? "Replace key for" : "Connect"} ${connectorAwaitingKey?.name ?? "connector"}`,
825
+ description: connectorAwaitingKey?.url,
826
+ contentSized: true,
827
+ className: "md:max-w-xl",
828
+ children: /* @__PURE__ */ jsxs3("form", { onSubmit: handleApiKeySubmit, children: [
829
+ /* @__PURE__ */ jsxs3("div", { className: "space-y-5 px-5 py-5", children: [
830
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3", children: [
831
+ /* @__PURE__ */ jsx3(
832
+ "span",
833
+ {
834
+ className: "flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-xl border border-border bg-secondary-bg",
835
+ "aria-hidden": true,
836
+ children: /* @__PURE__ */ jsx3(Icon, { name: "mcp-server", className: "size-6 text-text-primary" })
837
+ }
838
+ ),
839
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
840
+ /* @__PURE__ */ jsx3("p", { className: "font-medium text-text-primary", children: connectorAwaitingKey?.name }),
841
+ /* @__PURE__ */ jsx3("p", { className: "truncate text-sm text-text-secondary", children: connectorAwaitingKey?.description })
842
+ ] })
843
+ ] }),
844
+ /* @__PURE__ */ jsxs3("div", { children: [
845
+ /* @__PURE__ */ jsx3(
846
+ "label",
847
+ {
848
+ htmlFor: "connector-api-key",
849
+ className: "mb-2 block text-xs font-semibold uppercase tracking-wide text-text-secondary",
850
+ children: "API key / token"
851
+ }
852
+ ),
853
+ /* @__PURE__ */ jsx3(
854
+ "input",
855
+ {
856
+ id: "connector-api-key",
857
+ type: "password",
858
+ value: apiKey,
859
+ onChange: (event) => {
860
+ setApiKey(event.target.value);
861
+ },
862
+ placeholder: `Paste the token from ${connectorAwaitingKey?.name ?? "the provider"}`,
863
+ autoFocus: true,
864
+ required: true,
865
+ className: auiInputClass("h-11")
866
+ }
867
+ )
868
+ ] }),
869
+ formError ? /* @__PURE__ */ jsx3("p", { className: "text-failure-bg text-sm", children: formError }) : null
870
+ ] }),
871
+ /* @__PURE__ */ jsxs3("footer", { className: "flex justify-end gap-2 border-t border-border px-5 py-4", children: [
872
+ /* @__PURE__ */ jsx3(Button, { variant: "ghost", type: "button", onClick: closeApiKeyModal, disabled: busy, children: "Cancel" }),
873
+ /* @__PURE__ */ jsx3(Button, { type: "submit", disabled: !apiKey.trim() || busy, children: isReplacingKey ? "Replace Key" : "Connect" })
874
+ ] })
875
+ ] })
876
+ }
877
+ ),
878
+ /* @__PURE__ */ jsx3(
879
+ AddMcpServerForm_default,
880
+ {
881
+ open: addMcpServerFormOpen,
882
+ onOpenChange: (open) => {
883
+ setAddMcpServerFormOpen(open);
884
+ if (!open) setFormError(null);
885
+ },
886
+ onAdd: handleAddMcpServer,
887
+ busy,
888
+ error: formError
889
+ }
890
+ )
891
+ ] }) })
892
+ ] });
893
+ };
894
+ var ConnectorSettings_default = ConnectorSettings;
895
+ export {
896
+ ConnectorSettings_default as default
897
+ };
898
+ //# sourceMappingURL=ConnectorSettings-CLBEWGQJ.js.map