@qredence/fleet 0.5.11 → 0.6.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 (57) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/web/client/assets/{artifacts-panel-CQmFEF_H.js → artifacts-panel-CJ75gQub.js} +1 -1
  3. package/dist/web/client/assets/chat-Ci2cEQrR.js +52 -0
  4. package/dist/web/client/assets/{chat-command-palette-CmHTS3rs.js → chat-command-palette-CkISHfKE.js} +1 -1
  5. package/dist/web/client/assets/{command-DPfiaYUL.js → command-_WjnervL.js} +1 -1
  6. package/dist/web/client/assets/fleet-message-queue-BCno4XP-.js +1 -0
  7. package/dist/web/client/assets/{horizontal-resize-qK6eRhIG.js → horizontal-resize-CwoDojST.js} +1 -1
  8. package/dist/web/client/assets/{index-C1zOjrMK.js → index-DJ1fFbQu.js} +2 -2
  9. package/dist/web/client/assets/input-group-Tb5TYbzi.js +1 -0
  10. package/dist/web/client/assets/{model-selector-list-DsKTuAgS.js → model-selector-list-Ch05B2nZ.js} +1 -1
  11. package/dist/web/client/assets/{openui-renderer-J3dhPpL4.js → openui-renderer-Bf60nxNi.js} +5 -5
  12. package/dist/web/client/assets/pencil-DcUwsKLn.js +1 -0
  13. package/dist/web/client/assets/{provider-label-Dpr7djG9.js → provider-label-DDLRnZyC.js} +1 -1
  14. package/dist/web/client/assets/{right-panel-registry-DewpBWHM.js → right-panel-registry-Dp8yX-oL.js} +2 -2
  15. package/dist/web/client/assets/routes-DpgMJZ5n.js +16 -0
  16. package/dist/web/client/assets/settings-dialog-YJkvt5EB.js +2 -0
  17. package/dist/web/client/assets/{subagent-chat-panel-DkVUumrP.js → subagent-chat-panel-Yujplxpb.js} +1 -1
  18. package/dist/web/client/assets/{subagent-transcript-D8wmQxXe.js → subagent-transcript-CnI-mqN0.js} +1 -1
  19. package/dist/web/client/assets/{subagents-panel-BMp2Ego0.js → subagents-panel-CjXEdimj.js} +1 -1
  20. package/dist/web/client/assets/{virtualized-turn-list-DMJENRY0.js → virtualized-turn-list--Vynre4D.js} +2 -2
  21. package/dist/web/client/assets/{workspace-panel-G6yMpKJ8.js → workspace-panel-CPPIznT9.js} +1 -1
  22. package/dist/web/client/bundle-budget.json +6 -6
  23. package/dist/web/server/{_tanstack-start-manifest_v-po4woX6g-AUGCOEZ2.js → _tanstack-start-manifest_v-DS_CSIFQ-WB3VTKF3.js} +10 -9
  24. package/dist/web/server/{artifacts-panel-8GGUCtOd-X5TNKTY2.js → artifacts-panel-CaKrX2XC-R37I4BHF.js} +5 -5
  25. package/dist/web/server/{chat-command-palette-Dgl5tqWn-72LLEWUD.js → chat-command-palette-7gbqEnm9-6XICQZN7.js} +4 -4
  26. package/dist/web/server/{chunk-WILCIZYU.js → chunk-BBBS5AS7.js} +2 -2
  27. package/dist/web/server/{chunk-O4OTWWPF.js → chunk-CNXJN5HN.js} +4 -4
  28. package/dist/web/server/{chunk-XKKXXG6N.js → chunk-DI7XRK2H.js} +2 -2
  29. package/dist/web/server/{chunk-BXWAUTTH.js → chunk-DZ2IC3FH.js} +2 -2
  30. package/dist/web/server/{chunk-7MDZVLIY.js → chunk-EAE2QXRK.js} +1 -1
  31. package/dist/web/server/{chunk-JUROEEWM.js → chunk-EH4BZ6QM.js} +82 -4
  32. package/dist/web/server/{chunk-ABBWBBSS.js → chunk-KELEYPVK.js} +4 -4
  33. package/dist/web/server/{chunk-RNOJACWQ.js → chunk-LXJD6IGW.js} +2 -2
  34. package/dist/web/server/chunk-M5RSPNYN.js +22 -0
  35. package/dist/web/server/{chunk-P7H2OPKF.js → chunk-PIOFZUOU.js} +9 -1
  36. package/dist/web/server/chunk-PTCKOR23.js +39 -0
  37. package/dist/web/server/{chunk-MIW4LJTV.js → chunk-TWYHHG5N.js} +1 -9
  38. package/dist/web/server/{chunk-WKV2Z4KO.js → chunk-WHBVQJ4K.js} +2 -2
  39. package/dist/web/server/{chunk-GGT2ZKDB.js → chunk-YNUWKMNQ.js} +11 -4
  40. package/dist/web/server/fleet-message-queue-BS-cZ56r-GLZXUKDF.js +152 -0
  41. package/dist/web/server/{model-selector-list-OYnzNbyu-HBGCNF2L.js → model-selector-list-jBTZc2Mu-OXJOS5NA.js} +3 -3
  42. package/dist/web/server/{openui-renderer-Cj7kfqyL-EKJ6JTTQ.js → openui-renderer-Biyyjhjn-SJOEKPTJ.js} +4 -4
  43. package/dist/web/server/{router-Ci1wxI7I-E2AYMNON.js → router-DWALoX_M-I5CBL7FQ.js} +1228 -459
  44. package/dist/web/server/{routes-CRpm9-Sa-IYK6MUJO.js → routes-BBVirYfL-5WXMOT7M.js} +223 -49
  45. package/dist/web/server/server.js +2 -2
  46. package/dist/web/server/{settings-dialog-BvkgNflv-GPJ6COQO.js → settings-dialog-DndPbM-v-RFNXXEAN.js} +890 -567
  47. package/dist/web/server/{subagent-chat-panel-CMuvW9Sd-M5TEEGQW.js → subagent-chat-panel-C0RpnXav-J7VYO7QY.js} +3 -3
  48. package/dist/web/server/{subagents-panel-DgfB6_Yy-4INEPE3Y.js → subagents-panel-jDqIn9Nh-YEV6QLUY.js} +3 -3
  49. package/dist/web/server/{workspace-panel-bpwj-Veu-Q2CEJMN4.js → workspace-panel-CX_Vo6As-EWO6AMPI.js} +3 -3
  50. package/package.json +1 -1
  51. package/dist/web/client/assets/chat-yh02zgA7.js +0 -52
  52. package/dist/web/client/assets/fleet-message-queue-DUz6Hfrf.js +0 -1
  53. package/dist/web/client/assets/input-group-BFDsCVS9.js +0 -1
  54. package/dist/web/client/assets/routes-vNkVToAF.js +0 -16
  55. package/dist/web/client/assets/settings-dialog-B-v4me3Y.js +0 -1
  56. package/dist/web/server/chunk-QK4CHLVV.js +0 -17
  57. package/dist/web/server/fleet-message-queue-j0DCU5Xy-5HKBHMPV.js +0 -75
@@ -12,7 +12,6 @@ import {
12
12
  HIT_AREA_EXPAND_CLASS,
13
13
  HIT_AREA_EXPAND_DENSE_CLASS,
14
14
  PROVIDER_METADATA,
15
- Pencil,
16
15
  ProviderBrandIcon,
17
16
  Settings,
18
17
  Spinner,
@@ -25,7 +24,7 @@ import {
25
24
  useHoverInteractionSharedState,
26
25
  useHoverReferenceInteraction,
27
26
  usePopupHandleStore
28
- } from "./chunk-MIW4LJTV.js";
27
+ } from "./chunk-TWYHHG5N.js";
29
28
  import {
30
29
  UI_PREFERENCES_KEY,
31
30
  isModelPatternEnabled,
@@ -36,6 +35,9 @@ import {
36
35
  import {
37
36
  MessageSquare
38
37
  } from "./chunk-GL6HWDJB.js";
38
+ import {
39
+ Pencil
40
+ } from "./chunk-M5RSPNYN.js";
39
41
  import {
40
42
  Keyboard,
41
43
  Monitor,
@@ -80,8 +82,9 @@ import {
80
82
  InputGroup,
81
83
  InputGroupAddon,
82
84
  InputGroupButton,
83
- InputGroupInput
84
- } from "./chunk-P7H2OPKF.js";
85
+ InputGroupInput,
86
+ Textarea
87
+ } from "./chunk-PIOFZUOU.js";
85
88
  import {
86
89
  Search
87
90
  } from "./chunk-JIULUYHT.js";
@@ -210,7 +213,9 @@ import {
210
213
  useRenderElement,
211
214
  useStableCallback
212
215
  } from "./chunk-C5X6G26O.js";
213
- import "./chunk-AHTO6GDS.js";
216
+ import {
217
+ Plug
218
+ } from "./chunk-AHTO6GDS.js";
214
219
  import "./chunk-EAJTWMK5.js";
215
220
  import "./chunk-HFQZLPU2.js";
216
221
  import {
@@ -223,7 +228,7 @@ import "./chunk-JDCCC7A7.js";
223
228
  import "./chunk-L547YHC4.js";
224
229
  import "./chunk-MLKGABMK.js";
225
230
 
226
- // web/app/dist/server/assets/settings-dialog-BvkgNflv.js
231
+ // web/app/dist/server/assets/settings-dialog-DndPbM-v.js
227
232
  import * as React$1 from "react";
228
233
  import { useCallback as useCallback2, useEffect as useEffect2, useId, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
229
234
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -2284,6 +2289,44 @@ function PersonalizationSection({ onThemePreferenceChange, themePreference }) {
2284
2289
  })]
2285
2290
  });
2286
2291
  }
2292
+ function RowSurface({ className, interactive, padding, tone, ...props }) {
2293
+ return /* @__PURE__ */ jsx("div", {
2294
+ className: cn(fleetPiRowSurface({
2295
+ interactive,
2296
+ padding,
2297
+ tone
2298
+ }), className),
2299
+ ...props
2300
+ });
2301
+ }
2302
+ function ItemRow({ icon, title, subtitle, trailing, className, interactive = true, tone = "default" }) {
2303
+ return /* @__PURE__ */ jsxs(RowSurface, {
2304
+ tone,
2305
+ padding: "md",
2306
+ interactive,
2307
+ className: cn("items-center gap-3", className),
2308
+ children: [
2309
+ icon ? /* @__PURE__ */ jsx("div", {
2310
+ className: "flex size-8 shrink-0 items-center justify-center rounded-md border border-border/40 bg-background/60 text-foreground/70",
2311
+ children: icon
2312
+ }) : null,
2313
+ /* @__PURE__ */ jsxs("div", {
2314
+ className: "min-w-0 flex-1",
2315
+ children: [/* @__PURE__ */ jsx("div", {
2316
+ className: "truncate text-sm font-medium text-foreground",
2317
+ children: title
2318
+ }), subtitle ? /* @__PURE__ */ jsx("div", {
2319
+ className: "mt-0.5 truncate text-xs text-muted-foreground",
2320
+ children: subtitle
2321
+ }) : null]
2322
+ }),
2323
+ trailing ? /* @__PURE__ */ jsx("div", {
2324
+ className: "flex shrink-0 items-center gap-1.5",
2325
+ children: trailing
2326
+ }) : null
2327
+ ]
2328
+ });
2329
+ }
2287
2330
  function SettingsPane({ actions, children, description, title }) {
2288
2331
  return /* @__PURE__ */ jsxs("div", {
2289
2332
  className: "flex flex-col gap-4",
@@ -2331,393 +2374,11 @@ function SettingsCommitActions({ dirty, disabled: disabled2, onRevert, onSave, s
2331
2374
  }) : /* @__PURE__ */ jsx(Save, { "data-icon": "inline-start" }), "Commit"]
2332
2375
  })] });
2333
2376
  }
2334
- function isFieldInvalid(value, attemptedSave, required = true) {
2335
- return attemptedSave && required && value.trim().length === 0;
2336
- }
2337
- function SecretCredentialField({ attemptedSave, label, onChange, onToggleVisibility, placeholder, required = true, showPassword, value }) {
2338
- const controlId = useId();
2339
- const invalid = isFieldInvalid(value, attemptedSave, required);
2340
- return /* @__PURE__ */ jsxs(Field, {
2341
- "data-invalid": invalid || void 0,
2342
- children: [/* @__PURE__ */ jsx(FieldLabel, {
2343
- htmlFor: controlId,
2344
- children: label
2345
- }), /* @__PURE__ */ jsxs(InputGroup, { children: [
2346
- /* @__PURE__ */ jsx(InputGroupAddon, {
2347
- align: "inline-start",
2348
- children: /* @__PURE__ */ jsx(Lock, {})
2349
- }),
2350
- /* @__PURE__ */ jsx(InputGroupInput, {
2351
- id: controlId,
2352
- type: showPassword ? "text" : "password",
2353
- placeholder,
2354
- value,
2355
- onChange: (event) => onChange(event.target.value),
2356
- "aria-label": label,
2357
- "aria-invalid": invalid || void 0
2358
- }),
2359
- /* @__PURE__ */ jsx(InputGroupAddon, {
2360
- align: "inline-end",
2361
- children: /* @__PURE__ */ jsx(InputGroupButton, {
2362
- size: "icon-xs",
2363
- className: cn(HIT_AREA_EXPAND_CLASS, "transition-[background-color,transform] duration-150 active:scale-[0.96]"),
2364
- "aria-label": showPassword ? "Hide value" : "Show value",
2365
- onClick: onToggleVisibility,
2366
- children: showPassword ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {})
2367
- })
2368
- })
2369
- ] })]
2370
- });
2371
- }
2372
- function TextCredentialField({ attemptedSave, icon: Icon, inputType = "text", label, onChange, placeholder, required = true, value }) {
2373
- const controlId = useId();
2374
- const invalid = isFieldInvalid(value, attemptedSave, required);
2375
- return /* @__PURE__ */ jsxs(Field, {
2376
- "data-invalid": invalid || void 0,
2377
- children: [/* @__PURE__ */ jsx(FieldLabel, {
2378
- htmlFor: controlId,
2379
- children: label
2380
- }), /* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(InputGroupAddon, {
2381
- align: "inline-start",
2382
- children: /* @__PURE__ */ jsx(Icon, {})
2383
- }), /* @__PURE__ */ jsx(InputGroupInput, {
2384
- id: controlId,
2385
- type: inputType,
2386
- placeholder,
2387
- value,
2388
- onChange: (event) => onChange(event.target.value),
2389
- "aria-label": label,
2390
- "aria-invalid": invalid || void 0
2391
- })] })]
2392
- });
2393
- }
2394
- var OPENAI_CHAT_BASE_URL_PLACEHOLDER = "https://opencode.ai/zen/v1";
2395
- var OPENAI_CHAT_MODEL_PLACEHOLDER = "qwen35-122b-a10b";
2396
- var OPENAI_CHAT_NAME_PLACEHOLDER = "e.g. OpenCode Zen, Nebius, Groq\u2026";
2397
- var CUSTOM_MODELS_PLACEHOLDER = "model-a, model-b (comma-separated)";
2398
- var CUSTOM_PROVIDER_API_OPTIONS = [
2399
- {
2400
- value: "openai-completions",
2401
- label: "OpenAI Chat Completions"
2402
- },
2403
- {
2404
- value: "openai-responses",
2405
- label: "OpenAI Responses"
2406
- },
2407
- {
2408
- value: "anthropic-messages",
2409
- label: "Anthropic Messages"
2410
- },
2411
- {
2412
- value: "google-genai",
2413
- label: "Google Generative AI"
2414
- }
2415
- ];
2416
- function ProviderCredentialFields({ attemptedSave, apiKey, api, baseUrl, modelId, models, displayName, isCustom, onApiKeyChange, onApiChange, onBaseUrlChange, onModelIdChange, onModelsChange, onDisplayNameChange, onTogglePassword, openAiChat, placeholder, showPassword }) {
2417
- const showApiSelector = isCustom && onApiChange;
2418
- const showMultiModels = isCustom && onModelsChange;
2419
- return /* @__PURE__ */ jsxs(FieldGroup, {
2420
- className: "gap-2",
2421
- children: [
2422
- (openAiChat || isCustom) && onDisplayNameChange ? /* @__PURE__ */ jsx(TextCredentialField, {
2423
- attemptedSave,
2424
- icon: Tag,
2425
- label: "Provider name",
2426
- placeholder: OPENAI_CHAT_NAME_PLACEHOLDER,
2427
- value: displayName ?? "",
2428
- onChange: onDisplayNameChange
2429
- }) : null,
2430
- showApiSelector ? /* @__PURE__ */ jsx(ApiSelector, {
2431
- value: api ?? "openai-completions",
2432
- onChange: onApiChange
2433
- }) : null,
2434
- /* @__PURE__ */ jsx(SecretCredentialField, {
2435
- attemptedSave,
2436
- label: "API key",
2437
- placeholder,
2438
- value: apiKey,
2439
- showPassword,
2440
- onChange: onApiKeyChange,
2441
- onToggleVisibility: onTogglePassword
2442
- }),
2443
- openAiChat || isCustom ? /* @__PURE__ */ jsx(TextCredentialField, {
2444
- attemptedSave,
2445
- icon: Globe,
2446
- inputType: "url",
2447
- label: "Base URL",
2448
- placeholder: OPENAI_CHAT_BASE_URL_PLACEHOLDER,
2449
- value: baseUrl,
2450
- onChange: onBaseUrlChange
2451
- }) : null,
2452
- showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
2453
- attemptedSave,
2454
- icon: Layers,
2455
- label: "Model ids",
2456
- placeholder: CUSTOM_MODELS_PLACEHOLDER,
2457
- value: models ?? "",
2458
- onChange: onModelsChange
2459
- }) : null,
2460
- openAiChat && !showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
2461
- attemptedSave,
2462
- icon: Box,
2463
- label: "Model name",
2464
- placeholder: OPENAI_CHAT_MODEL_PLACEHOLDER,
2465
- value: modelId,
2466
- onChange: onModelIdChange
2467
- }) : null
2468
- ]
2469
- });
2470
- }
2471
- function ApiSelector({ value, onChange }) {
2472
- return /* @__PURE__ */ jsxs("div", {
2473
- className: "flex flex-col gap-1",
2474
- children: [/* @__PURE__ */ jsx("span", {
2475
- className: "text-xs font-medium text-muted-foreground",
2476
- children: "API family"
2477
- }), /* @__PURE__ */ jsx(Select, {
2478
- "aria-label": "API family",
2479
- value,
2480
- options: CUSTOM_PROVIDER_API_OPTIONS,
2481
- placeholder: "Select API family",
2482
- onValueChange: (next) => onChange(next)
2483
- })]
2484
- });
2485
- }
2486
- function isOAuthProvider(provider) {
2487
- return provider.authType === "oauth";
2488
- }
2489
- function supportsOAuth(provider) {
2490
- return provider.supportsOAuth === true;
2491
- }
2492
- var CUSTOM_PROVIDER_PICKER_ROW = {
2493
- id: "custom",
2494
- name: "Custom provider",
2495
- isConfigured: false,
2496
- envVarName: "CUSTOM_PROVIDER_API_KEY",
2497
- displayName: "Custom provider"
2498
- };
2499
- function parseCustomProviderModels(raw) {
2500
- return raw.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
2501
- }
2502
- function useProviderCredentialsController({ onRemoveProvider, onUpdateProvider, providers }) {
2503
- const [editingProvider, setEditingProvider] = useState2(null);
2504
- const [editingIsNewOccInstance, setEditingIsNewOccInstance] = useState2(false);
2505
- const [apiKey, setApiKey] = useState2("");
2506
- const [baseUrl, setBaseUrl] = useState2("");
2507
- const [modelId, setModelId] = useState2("");
2508
- const [models, setModels] = useState2("");
2509
- const [api, setApi] = useState2("openai-completions");
2510
- const [displayName, setDisplayName] = useState2("");
2511
- const [showPassword, setShowPassword] = useState2(false);
2512
- const [attemptedSave, setAttemptedSave] = useState2(false);
2513
- const [searchQuery, setSearchQuery] = useState2("");
2514
- const [addPickerOpen, setAddPickerOpen] = useState2(false);
2515
- const [addPickerQuery, setAddPickerQuery] = useState2("");
2516
- const [confirmRemoveProvider, setConfirmRemoveProvider] = useState2(null);
2517
- const credentialProviders = useMemo2(() => {
2518
- return [...providers.filter((provider) => provider.id !== CUSTOM_PROVIDER_PICKER_ROW.id), CUSTOM_PROVIDER_PICKER_ROW];
2519
- }, [providers]);
2520
- const activeProviders = useMemo2(() => credentialProviders.filter((provider) => provider.isConfigured), [credentialProviders]);
2521
- const unconfiguredProviders = useMemo2(() => credentialProviders.filter((provider) => !provider.isConfigured || provider.id === "openai-chat-completions" && provider.providerFamily === void 0), [credentialProviders]);
2522
- const filteredActiveProviders = useMemo2(() => {
2523
- const query = searchQuery.trim().toLowerCase();
2524
- if (!query) return activeProviders;
2525
- return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
2526
- }, [activeProviders, searchQuery]);
2527
- const filteredPickerAvailable = useMemo2(() => {
2528
- const query = addPickerQuery.trim().toLowerCase();
2529
- return unconfiguredProviders.filter((provider) => providerMatchesQuery(provider, query));
2530
- }, [addPickerQuery, unconfiguredProviders]);
2531
- const filteredPickerConfigured = useMemo2(() => {
2532
- const query = addPickerQuery.trim().toLowerCase();
2533
- return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
2534
- }, [activeProviders, addPickerQuery]);
2535
- const pickerHasResults = filteredPickerAvailable.length > 0 || filteredPickerConfigured.length > 0;
2536
- const editingUnconfiguredProvider = editingProvider && !activeProviders.some((provider) => provider.id === editingProvider) ? credentialProviders.find((entry) => entry.id === editingProvider) ?? null : null;
2537
- const resetEditor = () => {
2538
- setApiKey("");
2539
- setBaseUrl("");
2540
- setModelId("");
2541
- setModels("");
2542
- setApi("openai-completions");
2543
- setDisplayName("");
2544
- setShowPassword(false);
2545
- setAttemptedSave(false);
2546
- };
2547
- const closeEditor = () => {
2548
- setEditingProvider(null);
2549
- setEditingIsNewOccInstance(false);
2550
- resetEditor();
2551
- };
2552
- const openEditor = (providerId, opts) => {
2553
- setAddPickerOpen(false);
2554
- setAddPickerQuery("");
2555
- setEditingProvider(providerId);
2556
- setEditingIsNewOccInstance(opts?.newOccInstance === true);
2557
- resetEditor();
2558
- const existing = providers.find((provider) => provider.id === providerId);
2559
- setBaseUrl(existing?.baseUrl ?? "");
2560
- setDisplayName(existing?.displayName ?? (isCustomProviderId(providerId) ? existing?.name ?? "" : ""));
2561
- setApi(existing?.api ?? "openai-completions");
2562
- setModels((existing?.modelIds ?? []).join(", "));
2563
- setModelId(existing?.modelIds?.[0] ?? "");
2564
- };
2565
- const openAddPicker = () => {
2566
- closeEditor();
2567
- setAddPickerQuery("");
2568
- setAddPickerOpen(true);
2569
- };
2570
- const closeAddPicker = () => {
2571
- setAddPickerOpen(false);
2572
- setAddPickerQuery("");
2573
- };
2574
- const selectProviderFromPicker = (providerId) => {
2575
- openEditor(providerId, { newOccInstance: isOccProviderId(providerId) });
2576
- };
2577
- const handleSave = async (providerId) => {
2578
- if (!onUpdateProvider) return;
2579
- setAttemptedSave(true);
2580
- const isCustomEditor = providerId === "custom" || isCustomProviderId(providerId);
2581
- const openAiChat = isOccProviderId(providerId);
2582
- if (isCustomEditor) {
2583
- const modelList = parseCustomProviderModels(models);
2584
- if (!apiKey.trim() || !baseUrl.trim() || !displayName.trim()) return;
2585
- if (modelList.length === 0) return;
2586
- try {
2587
- await onUpdateProvider({
2588
- providerId,
2589
- apiKey: apiKey.trim(),
2590
- baseUrl: baseUrl.trim(),
2591
- displayName: displayName.trim(),
2592
- api,
2593
- models: modelList,
2594
- ...providerId === "custom" ? { createOccInstance: true } : {}
2595
- });
2596
- notify.success("Provider credentials applied to your active sessions.");
2597
- closeEditor();
2598
- closeAddPicker();
2599
- } catch (error) {
2600
- notify.error(error instanceof Error ? error.message : "Failed to update provider");
2601
- }
2602
- return;
2603
- }
2604
- const isNamedOcc = openAiChat && isNamedOccInstanceId(providerId);
2605
- const isOccLike = openAiChat && (isNamedOcc || editingIsNewOccInstance);
2606
- if (!apiKey.trim()) return;
2607
- if (openAiChat && (!baseUrl.trim() || !modelId.trim())) return;
2608
- if (isOccLike && !displayName.trim()) return;
2609
- try {
2610
- await onUpdateProvider({
2611
- providerId,
2612
- apiKey: apiKey.trim(),
2613
- ...openAiChat ? {
2614
- baseUrl: baseUrl.trim(),
2615
- modelId: modelId.trim(),
2616
- ...isOccLike ? { displayName: displayName.trim() } : {},
2617
- ...editingIsNewOccInstance ? { createOccInstance: true } : {}
2618
- } : {}
2619
- });
2620
- notify.success("Provider credentials applied to your active sessions.");
2621
- closeEditor();
2622
- closeAddPicker();
2623
- } catch (error) {
2624
- notify.error(error instanceof Error ? error.message : "Failed to update provider");
2625
- }
2626
- };
2627
- const handleRemove = async (provider) => {
2628
- if (!onRemoveProvider) return;
2629
- try {
2630
- await onRemoveProvider({ providerId: provider.id });
2631
- if (editingProvider === provider.id) closeEditor();
2632
- notify.success(`${provider.name} removed from your configured providers.`);
2633
- setConfirmRemoveProvider(null);
2634
- } catch (error) {
2635
- notify.error(error instanceof Error ? error.message : "Failed to remove provider");
2636
- }
2637
- };
2638
- return {
2639
- activeProviders,
2640
- addPickerOpen,
2641
- addPickerQuery,
2642
- api,
2643
- apiKey,
2644
- attemptedSave,
2645
- baseUrl,
2646
- canSave: useMemo2(() => {
2647
- if (apiKey.trim().length === 0) return false;
2648
- if (!editingProvider) return true;
2649
- if (editingProvider === "custom" || isCustomProviderId(editingProvider)) {
2650
- if (baseUrl.trim().length === 0) return false;
2651
- if (parseCustomProviderModels(models).length === 0) return false;
2652
- return displayName.trim().length > 0;
2653
- }
2654
- if (!isOccProviderId(editingProvider)) return true;
2655
- if (baseUrl.trim().length === 0 || modelId.trim().length === 0) return false;
2656
- if (isNamedOccInstanceId(editingProvider) || editingIsNewOccInstance) return displayName.trim().length > 0;
2657
- return true;
2658
- }, [
2659
- apiKey,
2660
- baseUrl,
2661
- modelId,
2662
- models,
2663
- displayName,
2664
- editingProvider,
2665
- editingIsNewOccInstance
2666
- ]),
2667
- closeAddPicker,
2668
- closeEditor,
2669
- confirmRemoveProvider,
2670
- credentialProviders,
2671
- displayName,
2672
- editingIsNewOccInstance,
2673
- editingProvider,
2674
- editingUnconfiguredProvider,
2675
- filteredActiveProviders,
2676
- filteredPickerAvailable,
2677
- filteredPickerConfigured,
2678
- handleRemove,
2679
- handleSave,
2680
- modelId,
2681
- models,
2682
- openAddPicker,
2683
- openEditor,
2684
- pickerHasResults,
2685
- resetEditor,
2686
- searchQuery,
2687
- selectProviderFromPicker,
2688
- setAddPickerOpen,
2689
- setAddPickerQuery,
2690
- setApi,
2691
- setApiKey,
2692
- setAttemptedSave,
2693
- setBaseUrl,
2694
- setConfirmRemoveProvider,
2695
- setDisplayName,
2696
- setEditingIsNewOccInstance,
2697
- setEditingProvider,
2698
- setModelId,
2699
- setModels,
2700
- setSearchQuery,
2701
- setShowPassword,
2702
- showPassword,
2703
- unconfiguredProviders
2704
- };
2705
- }
2706
- function providerMatchesQuery(provider, query) {
2707
- if (!query) return true;
2708
- const keywords = supportsOAuth(provider) ? isOAuthProvider(provider) ? "oauth sign-in login" : "api key oauth sign-in login" : provider.id === "custom" || isCustomProviderId(provider.id) ? "custom provider endpoint openai anthropic google compatible" : isOccProviderId(provider.id) ? "api key base url model name chat completions" : "";
2709
- return [
2710
- provider.name,
2711
- provider.envVarName,
2712
- provider.id,
2713
- keywords
2714
- ].join(" ").toLowerCase().includes(query);
2715
- }
2716
- var OAUTH_POLL_MS = 1500;
2717
- var STALE_LOGIN_ERROR = "Unknown or expired OAuth login";
2718
- function delay(ms) {
2719
- return new Promise((resolve) => {
2720
- setTimeout(resolve, ms);
2377
+ var OAUTH_POLL_MS = 1500;
2378
+ var STALE_LOGIN_ERROR = "Unknown or expired OAuth login";
2379
+ function delay(ms) {
2380
+ return new Promise((resolve) => {
2381
+ setTimeout(resolve, ms);
2721
2382
  });
2722
2383
  }
2723
2384
  function useOAuthLoginFlow({ onConfigured, onOAuthLogin, providerId }) {
@@ -2887,141 +2548,819 @@ function useOAuthLoginFlow({ onConfigured, onOAuthLogin, providerId }) {
2887
2548
  login?.status
2888
2549
  ]);
2889
2550
  return {
2890
- busy,
2891
- cancel,
2892
- canStart: Boolean(onOAuthLogin),
2893
- login,
2894
- promptAnswer,
2895
- setPromptAnswer,
2896
- start,
2897
- submitPrompt
2551
+ busy,
2552
+ cancel,
2553
+ canStart: Boolean(onOAuthLogin),
2554
+ login,
2555
+ promptAnswer,
2556
+ setPromptAnswer,
2557
+ start,
2558
+ submitPrompt
2559
+ };
2560
+ }
2561
+ function ProviderOAuthSignIn({ onConfigured, onOAuthLogin, provider }) {
2562
+ const { busy, cancel, canStart, login, promptAnswer, setPromptAnswer, start, submitPrompt } = useOAuthLoginFlow({
2563
+ onConfigured,
2564
+ onOAuthLogin,
2565
+ providerId: provider.id
2566
+ });
2567
+ const waiting = login?.status === "waiting";
2568
+ const error = login?.status === "error" ? login.error : void 0;
2569
+ const authUrl = waiting ? login?.authUrl : void 0;
2570
+ const userCode = waiting ? login?.userCode : void 0;
2571
+ const instructions = waiting ? login?.instructions : void 0;
2572
+ const prompt = waiting ? login?.prompt : void 0;
2573
+ const oauthOnly = provider.authType === "oauth";
2574
+ return /* @__PURE__ */ jsxs("div", {
2575
+ className: "flex flex-col gap-3",
2576
+ children: [provider.isConfigured && oauthOnly && !waiting ? /* @__PURE__ */ jsxs("div", {
2577
+ className: "flex items-center gap-2 text-sm",
2578
+ children: [/* @__PURE__ */ jsx(Check, { className: "size-3.5 text-muted-foreground" }), /* @__PURE__ */ jsx("span", { children: "Connected" })]
2579
+ }) : null, waiting ? /* @__PURE__ */ jsxs("div", {
2580
+ className: "flex flex-col gap-2",
2581
+ children: [
2582
+ /* @__PURE__ */ jsxs("p", {
2583
+ className: "text-sm",
2584
+ children: [
2585
+ "Sign in with ",
2586
+ provider.name,
2587
+ " to store OAuth credentials for this account."
2588
+ ]
2589
+ }),
2590
+ userCode ? /* @__PURE__ */ jsxs("p", {
2591
+ className: "text-sm",
2592
+ children: [
2593
+ "Enter this code:",
2594
+ " ",
2595
+ /* @__PURE__ */ jsx("span", {
2596
+ className: "font-mono text-foreground",
2597
+ children: userCode
2598
+ })
2599
+ ]
2600
+ }) : null,
2601
+ instructions && !userCode ? /* @__PURE__ */ jsx("p", {
2602
+ className: "text-xs text-pretty text-muted-foreground",
2603
+ children: instructions
2604
+ }) : null,
2605
+ authUrl ? /* @__PURE__ */ jsxs(Button, {
2606
+ type: "button",
2607
+ size: "sm",
2608
+ onClick: () => {
2609
+ window.open(authUrl, "_blank", "noopener,noreferrer");
2610
+ },
2611
+ children: [/* @__PURE__ */ jsx(ExternalLink, { "data-icon": "inline-start" }), "Open sign-in page"]
2612
+ }) : prompt ? null : /* @__PURE__ */ jsxs("div", {
2613
+ className: "flex items-center gap-2 text-xs text-muted-foreground",
2614
+ children: [/* @__PURE__ */ jsx(Spinner, { className: "size-3" }), "Waiting for sign-in\u2026"]
2615
+ }),
2616
+ prompt ? /* @__PURE__ */ jsxs("div", {
2617
+ className: "flex flex-col gap-2",
2618
+ children: [
2619
+ /* @__PURE__ */ jsx("p", {
2620
+ className: "text-xs text-pretty text-muted-foreground",
2621
+ children: prompt.message
2622
+ }),
2623
+ /* @__PURE__ */ jsx(Input, {
2624
+ value: promptAnswer,
2625
+ placeholder: prompt.placeholder,
2626
+ onChange: (event) => setPromptAnswer(event.target.value),
2627
+ "aria-label": prompt.message
2628
+ }),
2629
+ /* @__PURE__ */ jsx("div", {
2630
+ className: "flex justify-end",
2631
+ children: /* @__PURE__ */ jsxs(Button, {
2632
+ type: "button",
2633
+ size: "sm",
2634
+ disabled: busy || !prompt.allowEmpty && !promptAnswer.trim(),
2635
+ onClick: () => {
2636
+ submitPrompt();
2637
+ },
2638
+ children: [busy ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Continue"]
2639
+ })
2640
+ })
2641
+ ]
2642
+ }) : null,
2643
+ /* @__PURE__ */ jsx("div", {
2644
+ className: "flex justify-end",
2645
+ children: /* @__PURE__ */ jsx(Button, {
2646
+ type: "button",
2647
+ variant: "ghost",
2648
+ size: "sm",
2649
+ disabled: busy,
2650
+ onClick: () => {
2651
+ cancel();
2652
+ },
2653
+ children: "Cancel sign-in"
2654
+ })
2655
+ })
2656
+ ]
2657
+ }) : /* @__PURE__ */ jsxs("div", {
2658
+ className: "flex flex-col gap-2",
2659
+ children: [error ? /* @__PURE__ */ jsx(Alert, {
2660
+ className: "px-3 py-2",
2661
+ children: /* @__PURE__ */ jsx(AlertDescription, {
2662
+ className: "text-xs text-pretty",
2663
+ children: error
2664
+ })
2665
+ }) : /* @__PURE__ */ jsx("p", {
2666
+ className: "text-xs text-pretty text-muted-foreground",
2667
+ children: oauthOnly && provider.isConfigured ? `Sign in again with ${provider.name} to refresh OAuth credentials.` : oauthOnly ? `Sign in with ${provider.name}. No API key is stored; credentials are saved to auth.json.` : `Sign in with ${provider.name} to use OAuth credentials; API-key editing remains available above.`
2668
+ }), /* @__PURE__ */ jsx("div", {
2669
+ className: "flex justify-end",
2670
+ children: /* @__PURE__ */ jsxs(Button, {
2671
+ type: "button",
2672
+ size: "sm",
2673
+ disabled: busy || !canStart,
2674
+ onClick: () => {
2675
+ start();
2676
+ },
2677
+ children: [
2678
+ busy ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null,
2679
+ "Sign in with ",
2680
+ provider.name
2681
+ ]
2682
+ })
2683
+ })]
2684
+ })]
2685
+ });
2686
+ }
2687
+ function statusLabel(connection) {
2688
+ switch (connection.status) {
2689
+ case "connected":
2690
+ return "Connected";
2691
+ case "anonymous":
2692
+ return "Available without sign-in";
2693
+ case "needs_auth":
2694
+ return "Sign-in required";
2695
+ case "disabled":
2696
+ return "Disabled";
2697
+ default:
2698
+ return connection.status;
2699
+ }
2700
+ }
2701
+ function connectionSubtitle(connection) {
2702
+ const details = connection.transport === "http" ? connection.url : connection.commandPreview;
2703
+ return [
2704
+ connection.source === "builtin" ? "Built-in" : "User",
2705
+ connection.transport === "http" ? "HTTP" : "stdio",
2706
+ statusLabel(connection),
2707
+ details
2708
+ ].filter((part) => Boolean(part)).join(" \xB7 ");
2709
+ }
2710
+ function McpConnectionsSection({ connections, isLoading, isPending, onOAuth, onRemove, onUpsert }) {
2711
+ const [adding, setAdding] = useState2(false);
2712
+ const [transport, setTransport] = useState2("http");
2713
+ const [name, setName] = useState2("");
2714
+ const [url, setUrl] = useState2("");
2715
+ const [oauth, setOauth] = useState2(true);
2716
+ const [bearerTokenEnvVar, setBearerTokenEnvVar] = useState2("");
2717
+ const [command, setCommand] = useState2("");
2718
+ const [args, setArgs] = useState2("");
2719
+ const [cwd, setCwd] = useState2("");
2720
+ const [envLines, setEnvLines] = useState2("");
2721
+ const [confirmRemove, setConfirmRemove] = useState2(null);
2722
+ const resetAddForm = () => {
2723
+ setName("");
2724
+ setUrl("");
2725
+ setOauth(true);
2726
+ setBearerTokenEnvVar("");
2727
+ setCommand("");
2728
+ setArgs("");
2729
+ setCwd("");
2730
+ setEnvLines("");
2731
+ setTransport("http");
2732
+ setAdding(false);
2733
+ };
2734
+ const handleAdd = async () => {
2735
+ if (!onUpsert) return;
2736
+ const trimmedName = name.trim();
2737
+ try {
2738
+ if (transport === "http") await onUpsert({
2739
+ name: trimmedName,
2740
+ transport: "http",
2741
+ url: url.trim(),
2742
+ ...oauth ? { oauth: true } : {},
2743
+ ...bearerTokenEnvVar.trim() ? { bearerTokenEnvVar: bearerTokenEnvVar.trim() } : {},
2744
+ force: true
2745
+ });
2746
+ else {
2747
+ const parsedArgs = args.split(/\s+/).map((part) => part.trim()).filter(Boolean);
2748
+ const env = envLines.split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
2749
+ const equals = line.indexOf("=");
2750
+ if (equals <= 0) return [];
2751
+ const child = line.slice(0, equals).trim();
2752
+ const source = line.slice(equals + 1).trim();
2753
+ if (!child || !source) return [];
2754
+ return [{
2755
+ child,
2756
+ source
2757
+ }];
2758
+ });
2759
+ await onUpsert({
2760
+ name: trimmedName,
2761
+ transport: "stdio",
2762
+ command: command.trim(),
2763
+ ...parsedArgs.length > 0 ? { args: parsedArgs } : {},
2764
+ ...cwd.trim() ? { cwd: cwd.trim() } : {},
2765
+ ...env.length > 0 ? { env } : {},
2766
+ force: true
2767
+ });
2768
+ }
2769
+ notify.success("MCP connection saved");
2770
+ resetAddForm();
2771
+ } catch (error) {
2772
+ notify.error(error instanceof Error ? error.message : "Could not save MCP connection");
2773
+ }
2774
+ };
2775
+ const handleRemove = async (connectionName) => {
2776
+ if (!onRemove) return;
2777
+ try {
2778
+ await onRemove({ name: connectionName });
2779
+ notify.success(`Removed ${connectionName}`);
2780
+ setConfirmRemove(null);
2781
+ } catch (error) {
2782
+ notify.error(error instanceof Error ? error.message : "Could not remove MCP connection");
2783
+ }
2784
+ };
2785
+ const handleLogout = async (connectionName) => {
2786
+ if (!onOAuth) return;
2787
+ try {
2788
+ await onOAuth({
2789
+ name: connectionName,
2790
+ action: "logout"
2791
+ });
2792
+ notify.success(`Signed out of ${connectionName}`);
2793
+ } catch (error) {
2794
+ notify.error(error instanceof Error ? error.message : "Could not sign out");
2795
+ }
2796
+ };
2797
+ const oauthAdapter = useMemo2(() => {
2798
+ if (!onOAuth) return void 0;
2799
+ return (connection) => async (request) => {
2800
+ const starting = !request.loginId && !request.cancel;
2801
+ return await onOAuth({
2802
+ name: connection.name,
2803
+ action: starting && connection.status === "connected" ? "reconnect" : "login",
2804
+ loginId: request.loginId,
2805
+ promptAnswer: request.promptAnswer,
2806
+ cancel: request.cancel
2807
+ });
2808
+ };
2809
+ }, [onOAuth]);
2810
+ return /* @__PURE__ */ jsxs(SettingsPane, {
2811
+ title: "MCP",
2812
+ description: "Connect built-in and user MCP servers. Tokens stay on the server; this list only shows status and environment-variable names.",
2813
+ actions: /* @__PURE__ */ jsxs(Button, {
2814
+ type: "button",
2815
+ size: "sm",
2816
+ variant: "outline",
2817
+ disabled: isPending || adding,
2818
+ onClick: () => setAdding(true),
2819
+ children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add server"]
2820
+ }),
2821
+ children: [
2822
+ isLoading ? /* @__PURE__ */ jsxs("div", {
2823
+ className: "flex items-center gap-2 text-sm text-muted-foreground",
2824
+ children: [/* @__PURE__ */ jsx(Spinner, { className: "size-3.5" }), "Loading MCP connections\u2026"]
2825
+ }) : null,
2826
+ adding ? /* @__PURE__ */ jsxs("div", {
2827
+ className: "flex flex-col gap-3 rounded-xl border p-4",
2828
+ children: [
2829
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Name" }), /* @__PURE__ */ jsx(Input, {
2830
+ value: name,
2831
+ onChange: (event) => setName(event.target.value),
2832
+ "aria-label": "MCP server name"
2833
+ })] }),
2834
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Transport" }), /* @__PURE__ */ jsx(Select, {
2835
+ value: transport,
2836
+ onValueChange: (value) => setTransport(value),
2837
+ options: [{
2838
+ label: "HTTP",
2839
+ value: "http"
2840
+ }, {
2841
+ label: "stdio",
2842
+ value: "stdio"
2843
+ }]
2844
+ })] }),
2845
+ transport === "http" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2846
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "URL" }), /* @__PURE__ */ jsx(Input, {
2847
+ value: url,
2848
+ onChange: (event) => setUrl(event.target.value),
2849
+ "aria-label": "MCP server URL"
2850
+ })] }),
2851
+ /* @__PURE__ */ jsxs(Field, {
2852
+ orientation: "horizontal",
2853
+ className: "items-center justify-between",
2854
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "OAuth" }), /* @__PURE__ */ jsx(FieldDescription, { children: "Use the browser sign-in flow for this server." })] }), /* @__PURE__ */ jsx(Switch, {
2855
+ checked: oauth,
2856
+ onCheckedChange: (checked) => setOauth(checked),
2857
+ "aria-label": "Use OAuth"
2858
+ })]
2859
+ }),
2860
+ !oauth ? /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Bearer token environment variable" }), /* @__PURE__ */ jsx(Input, {
2861
+ value: bearerTokenEnvVar,
2862
+ onChange: (event) => setBearerTokenEnvVar(event.target.value),
2863
+ "aria-label": "Bearer token environment variable",
2864
+ placeholder: "MCP_BEARER_TOKEN"
2865
+ })] }) : null
2866
+ ] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
2867
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Command" }), /* @__PURE__ */ jsx(Input, {
2868
+ value: command,
2869
+ onChange: (event) => setCommand(event.target.value),
2870
+ "aria-label": "MCP command"
2871
+ })] }),
2872
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Arguments" }), /* @__PURE__ */ jsx(Input, {
2873
+ value: args,
2874
+ onChange: (event) => setArgs(event.target.value),
2875
+ "aria-label": "MCP command arguments"
2876
+ })] }),
2877
+ /* @__PURE__ */ jsxs(Field, { children: [
2878
+ /* @__PURE__ */ jsx(FieldLabel, { children: "Working directory" }),
2879
+ /* @__PURE__ */ jsx(Input, {
2880
+ value: cwd,
2881
+ onChange: (event) => setCwd(event.target.value),
2882
+ "aria-label": "MCP working directory"
2883
+ }),
2884
+ /* @__PURE__ */ jsx(FieldDescription, { children: "Must stay inside the current workspace." })
2885
+ ] }),
2886
+ /* @__PURE__ */ jsxs(Field, { children: [
2887
+ /* @__PURE__ */ jsx(FieldLabel, { children: "Environment mappings" }),
2888
+ /* @__PURE__ */ jsx(Textarea, {
2889
+ value: envLines,
2890
+ onChange: (event) => setEnvLines(event.target.value),
2891
+ "aria-label": "MCP environment mappings",
2892
+ placeholder: "CHILD=SOURCE"
2893
+ }),
2894
+ /* @__PURE__ */ jsx(FieldDescription, { children: "One CHILD=SOURCE pair per line. Only variable names are stored." })
2895
+ ] })
2896
+ ] }),
2897
+ /* @__PURE__ */ jsxs("div", {
2898
+ className: "flex justify-end gap-2",
2899
+ children: [/* @__PURE__ */ jsx(Button, {
2900
+ type: "button",
2901
+ size: "sm",
2902
+ variant: "ghost",
2903
+ onClick: resetAddForm,
2904
+ children: "Cancel"
2905
+ }), /* @__PURE__ */ jsxs(Button, {
2906
+ type: "button",
2907
+ size: "sm",
2908
+ disabled: isPending || !name.trim(),
2909
+ onClick: () => void handleAdd(),
2910
+ children: [isPending ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Save"]
2911
+ })]
2912
+ })
2913
+ ]
2914
+ }) : null,
2915
+ !isLoading && connections.length === 0 && !adding ? /* @__PURE__ */ jsx("p", {
2916
+ className: "text-sm text-muted-foreground",
2917
+ children: "No MCP connections yet."
2918
+ }) : null,
2919
+ /* @__PURE__ */ jsx("div", {
2920
+ className: "flex flex-col gap-2",
2921
+ children: connections.map((connection) => /* @__PURE__ */ jsxs("div", {
2922
+ className: "flex flex-col gap-2",
2923
+ children: [
2924
+ /* @__PURE__ */ jsx(ItemRow, {
2925
+ icon: /* @__PURE__ */ jsx(Plug, { className: "size-4" }),
2926
+ title: connection.label,
2927
+ subtitle: connectionSubtitle(connection),
2928
+ trailing: /* @__PURE__ */ jsxs("div", {
2929
+ className: "flex items-center gap-1.5",
2930
+ children: [connection.usesOAuth && connection.status !== "needs_auth" ? /* @__PURE__ */ jsx(Button, {
2931
+ type: "button",
2932
+ size: "sm",
2933
+ variant: "ghost",
2934
+ disabled: isPending || !onOAuth,
2935
+ onClick: () => {
2936
+ handleLogout(connection.name);
2937
+ },
2938
+ children: "Sign out"
2939
+ }) : null, connection.source === "user" ? confirmRemove === connection.name ? /* @__PURE__ */ jsx(Button, {
2940
+ type: "button",
2941
+ size: "sm",
2942
+ variant: "destructive",
2943
+ disabled: isPending,
2944
+ onClick: () => {
2945
+ handleRemove(connection.name);
2946
+ },
2947
+ children: "Confirm remove"
2948
+ }) : /* @__PURE__ */ jsx(Button, {
2949
+ type: "button",
2950
+ size: "sm",
2951
+ variant: "ghost",
2952
+ disabled: isPending || !onRemove,
2953
+ "aria-label": `Remove ${connection.name}`,
2954
+ onClick: () => setConfirmRemove(connection.name),
2955
+ children: /* @__PURE__ */ jsx(Trash2, { className: "size-3.5" })
2956
+ }) : null]
2957
+ })
2958
+ }),
2959
+ connection.error ? /* @__PURE__ */ jsx(Alert, {
2960
+ className: "px-3 py-2",
2961
+ children: /* @__PURE__ */ jsx(AlertDescription, {
2962
+ className: "text-xs text-pretty",
2963
+ children: connection.error
2964
+ })
2965
+ }) : null,
2966
+ connection.usesOAuth && oauthAdapter ? /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
2967
+ provider: {
2968
+ id: connection.name,
2969
+ name: connection.label,
2970
+ isConfigured: connection.status === "connected",
2971
+ envVarName: "",
2972
+ authType: "oauth",
2973
+ supportsOAuth: true
2974
+ },
2975
+ onOAuthLogin: oauthAdapter(connection)
2976
+ }) : null
2977
+ ]
2978
+ }, `${connection.source}:${connection.name}`))
2979
+ })
2980
+ ]
2981
+ });
2982
+ }
2983
+ function isFieldInvalid(value, attemptedSave, required = true) {
2984
+ return attemptedSave && required && value.trim().length === 0;
2985
+ }
2986
+ function SecretCredentialField({ attemptedSave, label, onChange, onToggleVisibility, placeholder, required = true, showPassword, value }) {
2987
+ const controlId = useId();
2988
+ const invalid = isFieldInvalid(value, attemptedSave, required);
2989
+ return /* @__PURE__ */ jsxs(Field, {
2990
+ "data-invalid": invalid || void 0,
2991
+ children: [/* @__PURE__ */ jsx(FieldLabel, {
2992
+ htmlFor: controlId,
2993
+ children: label
2994
+ }), /* @__PURE__ */ jsxs(InputGroup, { children: [
2995
+ /* @__PURE__ */ jsx(InputGroupAddon, {
2996
+ align: "inline-start",
2997
+ children: /* @__PURE__ */ jsx(Lock, {})
2998
+ }),
2999
+ /* @__PURE__ */ jsx(InputGroupInput, {
3000
+ id: controlId,
3001
+ type: showPassword ? "text" : "password",
3002
+ placeholder,
3003
+ value,
3004
+ onChange: (event) => onChange(event.target.value),
3005
+ "aria-label": label,
3006
+ "aria-invalid": invalid || void 0
3007
+ }),
3008
+ /* @__PURE__ */ jsx(InputGroupAddon, {
3009
+ align: "inline-end",
3010
+ children: /* @__PURE__ */ jsx(InputGroupButton, {
3011
+ size: "icon-xs",
3012
+ className: cn(HIT_AREA_EXPAND_CLASS, "transition-[background-color,transform] duration-150 active:scale-[0.96]"),
3013
+ "aria-label": showPassword ? "Hide value" : "Show value",
3014
+ onClick: onToggleVisibility,
3015
+ children: showPassword ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {})
3016
+ })
3017
+ })
3018
+ ] })]
3019
+ });
3020
+ }
3021
+ function TextCredentialField({ attemptedSave, icon: Icon, inputType = "text", label, onChange, placeholder, required = true, value }) {
3022
+ const controlId = useId();
3023
+ const invalid = isFieldInvalid(value, attemptedSave, required);
3024
+ return /* @__PURE__ */ jsxs(Field, {
3025
+ "data-invalid": invalid || void 0,
3026
+ children: [/* @__PURE__ */ jsx(FieldLabel, {
3027
+ htmlFor: controlId,
3028
+ children: label
3029
+ }), /* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(InputGroupAddon, {
3030
+ align: "inline-start",
3031
+ children: /* @__PURE__ */ jsx(Icon, {})
3032
+ }), /* @__PURE__ */ jsx(InputGroupInput, {
3033
+ id: controlId,
3034
+ type: inputType,
3035
+ placeholder,
3036
+ value,
3037
+ onChange: (event) => onChange(event.target.value),
3038
+ "aria-label": label,
3039
+ "aria-invalid": invalid || void 0
3040
+ })] })]
3041
+ });
3042
+ }
3043
+ var OPENAI_CHAT_BASE_URL_PLACEHOLDER = "https://opencode.ai/zen/v1";
3044
+ var OPENAI_CHAT_MODEL_PLACEHOLDER = "qwen35-122b-a10b";
3045
+ var OPENAI_CHAT_NAME_PLACEHOLDER = "e.g. OpenCode Zen, Nebius, Groq\u2026";
3046
+ var CUSTOM_MODELS_PLACEHOLDER = "model-a, model-b (comma-separated)";
3047
+ var CUSTOM_PROVIDER_API_OPTIONS = [
3048
+ {
3049
+ value: "openai-completions",
3050
+ label: "OpenAI Chat Completions"
3051
+ },
3052
+ {
3053
+ value: "openai-responses",
3054
+ label: "OpenAI Responses"
3055
+ },
3056
+ {
3057
+ value: "anthropic-messages",
3058
+ label: "Anthropic Messages"
3059
+ },
3060
+ {
3061
+ value: "google-genai",
3062
+ label: "Google Generative AI"
3063
+ }
3064
+ ];
3065
+ function ProviderCredentialFields({ attemptedSave, apiKey, api, baseUrl, modelId, models, displayName, isCustom, onApiKeyChange, onApiChange, onBaseUrlChange, onModelIdChange, onModelsChange, onDisplayNameChange, onTogglePassword, openAiChat, placeholder, showPassword }) {
3066
+ const showApiSelector = isCustom && onApiChange;
3067
+ const showMultiModels = isCustom && onModelsChange;
3068
+ return /* @__PURE__ */ jsxs(FieldGroup, {
3069
+ className: "gap-2",
3070
+ children: [
3071
+ (openAiChat || isCustom) && onDisplayNameChange ? /* @__PURE__ */ jsx(TextCredentialField, {
3072
+ attemptedSave,
3073
+ icon: Tag,
3074
+ label: "Provider name",
3075
+ placeholder: OPENAI_CHAT_NAME_PLACEHOLDER,
3076
+ value: displayName ?? "",
3077
+ onChange: onDisplayNameChange
3078
+ }) : null,
3079
+ showApiSelector ? /* @__PURE__ */ jsx(ApiSelector, {
3080
+ value: api ?? "openai-completions",
3081
+ onChange: onApiChange
3082
+ }) : null,
3083
+ /* @__PURE__ */ jsx(SecretCredentialField, {
3084
+ attemptedSave,
3085
+ label: "API key",
3086
+ placeholder,
3087
+ value: apiKey,
3088
+ showPassword,
3089
+ onChange: onApiKeyChange,
3090
+ onToggleVisibility: onTogglePassword
3091
+ }),
3092
+ openAiChat || isCustom ? /* @__PURE__ */ jsx(TextCredentialField, {
3093
+ attemptedSave,
3094
+ icon: Globe,
3095
+ inputType: "url",
3096
+ label: "Base URL",
3097
+ placeholder: OPENAI_CHAT_BASE_URL_PLACEHOLDER,
3098
+ value: baseUrl,
3099
+ onChange: onBaseUrlChange
3100
+ }) : null,
3101
+ showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
3102
+ attemptedSave,
3103
+ icon: Layers,
3104
+ label: "Model ids",
3105
+ placeholder: CUSTOM_MODELS_PLACEHOLDER,
3106
+ value: models ?? "",
3107
+ onChange: onModelsChange
3108
+ }) : null,
3109
+ openAiChat && !showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
3110
+ attemptedSave,
3111
+ icon: Box,
3112
+ label: "Model name",
3113
+ placeholder: OPENAI_CHAT_MODEL_PLACEHOLDER,
3114
+ value: modelId,
3115
+ onChange: onModelIdChange
3116
+ }) : null
3117
+ ]
3118
+ });
3119
+ }
3120
+ function ApiSelector({ value, onChange }) {
3121
+ return /* @__PURE__ */ jsxs("div", {
3122
+ className: "flex flex-col gap-1",
3123
+ children: [/* @__PURE__ */ jsx("span", {
3124
+ className: "text-xs font-medium text-muted-foreground",
3125
+ children: "API family"
3126
+ }), /* @__PURE__ */ jsx(Select, {
3127
+ "aria-label": "API family",
3128
+ value,
3129
+ options: CUSTOM_PROVIDER_API_OPTIONS,
3130
+ placeholder: "Select API family",
3131
+ onValueChange: (next) => onChange(next)
3132
+ })]
3133
+ });
3134
+ }
3135
+ function isOAuthProvider(provider) {
3136
+ return provider.authType === "oauth";
3137
+ }
3138
+ function supportsOAuth(provider) {
3139
+ return provider.supportsOAuth === true;
3140
+ }
3141
+ var CUSTOM_PROVIDER_PICKER_ROW = {
3142
+ id: "custom",
3143
+ name: "Custom provider",
3144
+ isConfigured: false,
3145
+ envVarName: "CUSTOM_PROVIDER_API_KEY",
3146
+ displayName: "Custom provider"
3147
+ };
3148
+ function parseCustomProviderModels(raw) {
3149
+ return raw.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
3150
+ }
3151
+ function useProviderCredentialsController({ onRemoveProvider, onUpdateProvider, providers }) {
3152
+ const [editingProvider, setEditingProvider] = useState2(null);
3153
+ const [editingIsNewOccInstance, setEditingIsNewOccInstance] = useState2(false);
3154
+ const [apiKey, setApiKey] = useState2("");
3155
+ const [baseUrl, setBaseUrl] = useState2("");
3156
+ const [modelId, setModelId] = useState2("");
3157
+ const [models, setModels] = useState2("");
3158
+ const [api, setApi] = useState2("openai-completions");
3159
+ const [displayName, setDisplayName] = useState2("");
3160
+ const [showPassword, setShowPassword] = useState2(false);
3161
+ const [attemptedSave, setAttemptedSave] = useState2(false);
3162
+ const [searchQuery, setSearchQuery] = useState2("");
3163
+ const [addPickerOpen, setAddPickerOpen] = useState2(false);
3164
+ const [addPickerQuery, setAddPickerQuery] = useState2("");
3165
+ const [confirmRemoveProvider, setConfirmRemoveProvider] = useState2(null);
3166
+ const credentialProviders = useMemo2(() => {
3167
+ return [...providers.filter((provider) => provider.id !== CUSTOM_PROVIDER_PICKER_ROW.id), CUSTOM_PROVIDER_PICKER_ROW];
3168
+ }, [providers]);
3169
+ const activeProviders = useMemo2(() => credentialProviders.filter((provider) => provider.isConfigured), [credentialProviders]);
3170
+ const unconfiguredProviders = useMemo2(() => credentialProviders.filter((provider) => !provider.isConfigured || provider.id === "openai-chat-completions" && provider.providerFamily === void 0), [credentialProviders]);
3171
+ const filteredActiveProviders = useMemo2(() => {
3172
+ const query = searchQuery.trim().toLowerCase();
3173
+ if (!query) return activeProviders;
3174
+ return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
3175
+ }, [activeProviders, searchQuery]);
3176
+ const filteredPickerAvailable = useMemo2(() => {
3177
+ const query = addPickerQuery.trim().toLowerCase();
3178
+ return unconfiguredProviders.filter((provider) => providerMatchesQuery(provider, query));
3179
+ }, [addPickerQuery, unconfiguredProviders]);
3180
+ const filteredPickerConfigured = useMemo2(() => {
3181
+ const query = addPickerQuery.trim().toLowerCase();
3182
+ return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
3183
+ }, [activeProviders, addPickerQuery]);
3184
+ const pickerHasResults = filteredPickerAvailable.length > 0 || filteredPickerConfigured.length > 0;
3185
+ const editingUnconfiguredProvider = editingProvider && !activeProviders.some((provider) => provider.id === editingProvider) ? credentialProviders.find((entry) => entry.id === editingProvider) ?? null : null;
3186
+ const resetEditor = () => {
3187
+ setApiKey("");
3188
+ setBaseUrl("");
3189
+ setModelId("");
3190
+ setModels("");
3191
+ setApi("openai-completions");
3192
+ setDisplayName("");
3193
+ setShowPassword(false);
3194
+ setAttemptedSave(false);
3195
+ };
3196
+ const closeEditor = () => {
3197
+ setEditingProvider(null);
3198
+ setEditingIsNewOccInstance(false);
3199
+ resetEditor();
3200
+ };
3201
+ const openEditor = (providerId, opts) => {
3202
+ setAddPickerOpen(false);
3203
+ setAddPickerQuery("");
3204
+ setEditingProvider(providerId);
3205
+ setEditingIsNewOccInstance(opts?.newOccInstance === true);
3206
+ resetEditor();
3207
+ const existing = providers.find((provider) => provider.id === providerId);
3208
+ setBaseUrl(existing?.baseUrl ?? "");
3209
+ setDisplayName(existing?.displayName ?? (isCustomProviderId(providerId) ? existing?.name ?? "" : ""));
3210
+ setApi(existing?.api ?? "openai-completions");
3211
+ setModels((existing?.modelIds ?? []).join(", "));
3212
+ setModelId(existing?.modelIds?.[0] ?? "");
3213
+ };
3214
+ const openAddPicker = () => {
3215
+ closeEditor();
3216
+ setAddPickerQuery("");
3217
+ setAddPickerOpen(true);
3218
+ };
3219
+ const closeAddPicker = () => {
3220
+ setAddPickerOpen(false);
3221
+ setAddPickerQuery("");
3222
+ };
3223
+ const selectProviderFromPicker = (providerId) => {
3224
+ openEditor(providerId, { newOccInstance: isOccProviderId(providerId) });
3225
+ };
3226
+ const handleSave = async (providerId) => {
3227
+ if (!onUpdateProvider) return;
3228
+ setAttemptedSave(true);
3229
+ const isCustomEditor = providerId === "custom" || isCustomProviderId(providerId);
3230
+ const openAiChat = isOccProviderId(providerId);
3231
+ if (isCustomEditor) {
3232
+ const modelList = parseCustomProviderModels(models);
3233
+ if (!apiKey.trim() || !baseUrl.trim() || !displayName.trim()) return;
3234
+ if (modelList.length === 0) return;
3235
+ try {
3236
+ await onUpdateProvider({
3237
+ providerId,
3238
+ apiKey: apiKey.trim(),
3239
+ baseUrl: baseUrl.trim(),
3240
+ displayName: displayName.trim(),
3241
+ api,
3242
+ models: modelList,
3243
+ ...providerId === "custom" ? { createOccInstance: true } : {}
3244
+ });
3245
+ notify.success("Provider credentials applied to your active sessions.");
3246
+ closeEditor();
3247
+ closeAddPicker();
3248
+ } catch (error) {
3249
+ notify.error(error instanceof Error ? error.message : "Failed to update provider");
3250
+ }
3251
+ return;
3252
+ }
3253
+ const isNamedOcc = openAiChat && isNamedOccInstanceId(providerId);
3254
+ const isOccLike = openAiChat && (isNamedOcc || editingIsNewOccInstance);
3255
+ if (!apiKey.trim()) return;
3256
+ if (openAiChat && (!baseUrl.trim() || !modelId.trim())) return;
3257
+ if (isOccLike && !displayName.trim()) return;
3258
+ try {
3259
+ await onUpdateProvider({
3260
+ providerId,
3261
+ apiKey: apiKey.trim(),
3262
+ ...openAiChat ? {
3263
+ baseUrl: baseUrl.trim(),
3264
+ modelId: modelId.trim(),
3265
+ ...isOccLike ? { displayName: displayName.trim() } : {},
3266
+ ...editingIsNewOccInstance ? { createOccInstance: true } : {}
3267
+ } : {}
3268
+ });
3269
+ notify.success("Provider credentials applied to your active sessions.");
3270
+ closeEditor();
3271
+ closeAddPicker();
3272
+ } catch (error) {
3273
+ notify.error(error instanceof Error ? error.message : "Failed to update provider");
3274
+ }
3275
+ };
3276
+ const handleRemove = async (provider) => {
3277
+ if (!onRemoveProvider) return;
3278
+ try {
3279
+ await onRemoveProvider({ providerId: provider.id });
3280
+ if (editingProvider === provider.id) closeEditor();
3281
+ notify.success(`${provider.name} removed from your configured providers.`);
3282
+ setConfirmRemoveProvider(null);
3283
+ } catch (error) {
3284
+ notify.error(error instanceof Error ? error.message : "Failed to remove provider");
3285
+ }
3286
+ };
3287
+ return {
3288
+ activeProviders,
3289
+ addPickerOpen,
3290
+ addPickerQuery,
3291
+ api,
3292
+ apiKey,
3293
+ attemptedSave,
3294
+ baseUrl,
3295
+ canSave: useMemo2(() => {
3296
+ if (apiKey.trim().length === 0) return false;
3297
+ if (!editingProvider) return true;
3298
+ if (editingProvider === "custom" || isCustomProviderId(editingProvider)) {
3299
+ if (baseUrl.trim().length === 0) return false;
3300
+ if (parseCustomProviderModels(models).length === 0) return false;
3301
+ return displayName.trim().length > 0;
3302
+ }
3303
+ if (!isOccProviderId(editingProvider)) return true;
3304
+ if (baseUrl.trim().length === 0 || modelId.trim().length === 0) return false;
3305
+ if (isNamedOccInstanceId(editingProvider) || editingIsNewOccInstance) return displayName.trim().length > 0;
3306
+ return true;
3307
+ }, [
3308
+ apiKey,
3309
+ baseUrl,
3310
+ modelId,
3311
+ models,
3312
+ displayName,
3313
+ editingProvider,
3314
+ editingIsNewOccInstance
3315
+ ]),
3316
+ closeAddPicker,
3317
+ closeEditor,
3318
+ confirmRemoveProvider,
3319
+ credentialProviders,
3320
+ displayName,
3321
+ editingIsNewOccInstance,
3322
+ editingProvider,
3323
+ editingUnconfiguredProvider,
3324
+ filteredActiveProviders,
3325
+ filteredPickerAvailable,
3326
+ filteredPickerConfigured,
3327
+ handleRemove,
3328
+ handleSave,
3329
+ modelId,
3330
+ models,
3331
+ openAddPicker,
3332
+ openEditor,
3333
+ pickerHasResults,
3334
+ resetEditor,
3335
+ searchQuery,
3336
+ selectProviderFromPicker,
3337
+ setAddPickerOpen,
3338
+ setAddPickerQuery,
3339
+ setApi,
3340
+ setApiKey,
3341
+ setAttemptedSave,
3342
+ setBaseUrl,
3343
+ setConfirmRemoveProvider,
3344
+ setDisplayName,
3345
+ setEditingIsNewOccInstance,
3346
+ setEditingProvider,
3347
+ setModelId,
3348
+ setModels,
3349
+ setSearchQuery,
3350
+ setShowPassword,
3351
+ showPassword,
3352
+ unconfiguredProviders
2898
3353
  };
2899
3354
  }
2900
- function ProviderOAuthSignIn({ onConfigured, onOAuthLogin, provider }) {
2901
- const { busy, cancel, canStart, login, promptAnswer, setPromptAnswer, start, submitPrompt } = useOAuthLoginFlow({
2902
- onConfigured,
2903
- onOAuthLogin,
2904
- providerId: provider.id
2905
- });
2906
- const waiting = login?.status === "waiting";
2907
- const error = login?.status === "error" ? login.error : void 0;
2908
- const authUrl = waiting ? login?.authUrl : void 0;
2909
- const userCode = waiting ? login?.userCode : void 0;
2910
- const instructions = waiting ? login?.instructions : void 0;
2911
- const prompt = waiting ? login?.prompt : void 0;
2912
- const oauthOnly = provider.authType === "oauth";
2913
- return /* @__PURE__ */ jsxs("div", {
2914
- className: "flex flex-col gap-3",
2915
- children: [provider.isConfigured && oauthOnly && !waiting ? /* @__PURE__ */ jsxs("div", {
2916
- className: "flex items-center gap-2 text-sm",
2917
- children: [/* @__PURE__ */ jsx(Check, { className: "size-3.5 text-muted-foreground" }), /* @__PURE__ */ jsx("span", { children: "Connected" })]
2918
- }) : null, waiting ? /* @__PURE__ */ jsxs("div", {
2919
- className: "flex flex-col gap-2",
2920
- children: [
2921
- /* @__PURE__ */ jsxs("p", {
2922
- className: "text-sm",
2923
- children: [
2924
- "Sign in with ",
2925
- provider.name,
2926
- " to store OAuth credentials for this account."
2927
- ]
2928
- }),
2929
- userCode ? /* @__PURE__ */ jsxs("p", {
2930
- className: "text-sm",
2931
- children: [
2932
- "Enter this code:",
2933
- " ",
2934
- /* @__PURE__ */ jsx("span", {
2935
- className: "font-mono text-foreground",
2936
- children: userCode
2937
- })
2938
- ]
2939
- }) : null,
2940
- instructions && !userCode ? /* @__PURE__ */ jsx("p", {
2941
- className: "text-xs text-pretty text-muted-foreground",
2942
- children: instructions
2943
- }) : null,
2944
- authUrl ? /* @__PURE__ */ jsxs(Button, {
2945
- type: "button",
2946
- size: "sm",
2947
- onClick: () => {
2948
- window.open(authUrl, "_blank", "noopener,noreferrer");
2949
- },
2950
- children: [/* @__PURE__ */ jsx(ExternalLink, { "data-icon": "inline-start" }), "Open sign-in page"]
2951
- }) : prompt ? null : /* @__PURE__ */ jsxs("div", {
2952
- className: "flex items-center gap-2 text-xs text-muted-foreground",
2953
- children: [/* @__PURE__ */ jsx(Spinner, { className: "size-3" }), "Waiting for sign-in\u2026"]
2954
- }),
2955
- prompt ? /* @__PURE__ */ jsxs("div", {
2956
- className: "flex flex-col gap-2",
2957
- children: [
2958
- /* @__PURE__ */ jsx("p", {
2959
- className: "text-xs text-pretty text-muted-foreground",
2960
- children: prompt.message
2961
- }),
2962
- /* @__PURE__ */ jsx(Input, {
2963
- value: promptAnswer,
2964
- placeholder: prompt.placeholder,
2965
- onChange: (event) => setPromptAnswer(event.target.value),
2966
- "aria-label": prompt.message
2967
- }),
2968
- /* @__PURE__ */ jsx("div", {
2969
- className: "flex justify-end",
2970
- children: /* @__PURE__ */ jsxs(Button, {
2971
- type: "button",
2972
- size: "sm",
2973
- disabled: busy || !prompt.allowEmpty && !promptAnswer.trim(),
2974
- onClick: () => {
2975
- submitPrompt();
2976
- },
2977
- children: [busy ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Continue"]
2978
- })
2979
- })
2980
- ]
2981
- }) : null,
2982
- /* @__PURE__ */ jsx("div", {
2983
- className: "flex justify-end",
2984
- children: /* @__PURE__ */ jsx(Button, {
2985
- type: "button",
2986
- variant: "ghost",
2987
- size: "sm",
2988
- disabled: busy,
2989
- onClick: () => {
2990
- cancel();
2991
- },
2992
- children: "Cancel sign-in"
2993
- })
2994
- })
2995
- ]
2996
- }) : /* @__PURE__ */ jsxs("div", {
2997
- className: "flex flex-col gap-2",
2998
- children: [error ? /* @__PURE__ */ jsx(Alert, {
2999
- className: "px-3 py-2",
3000
- children: /* @__PURE__ */ jsx(AlertDescription, {
3001
- className: "text-xs text-pretty",
3002
- children: error
3003
- })
3004
- }) : /* @__PURE__ */ jsx("p", {
3005
- className: "text-xs text-pretty text-muted-foreground",
3006
- children: oauthOnly && provider.isConfigured ? `Sign in again with ${provider.name} to refresh OAuth credentials.` : oauthOnly ? `Sign in with ${provider.name}. No API key is stored; credentials are saved to auth.json.` : `Sign in with ${provider.name} to use OAuth credentials; API-key editing remains available above.`
3007
- }), /* @__PURE__ */ jsx("div", {
3008
- className: "flex justify-end",
3009
- children: /* @__PURE__ */ jsxs(Button, {
3010
- type: "button",
3011
- size: "sm",
3012
- disabled: busy || !canStart,
3013
- onClick: () => {
3014
- start();
3015
- },
3016
- children: [
3017
- busy ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null,
3018
- "Sign in with ",
3019
- provider.name
3020
- ]
3021
- })
3022
- })]
3023
- })]
3024
- });
3355
+ function providerMatchesQuery(provider, query) {
3356
+ if (!query) return true;
3357
+ const keywords = supportsOAuth(provider) ? isOAuthProvider(provider) ? "oauth sign-in login" : "api key oauth sign-in login" : provider.id === "custom" || isCustomProviderId(provider.id) ? "custom provider endpoint openai anthropic google compatible" : isOccProviderId(provider.id) ? "api key base url model name chat completions" : "";
3358
+ return [
3359
+ provider.name,
3360
+ provider.envVarName,
3361
+ provider.id,
3362
+ keywords
3363
+ ].join(" ").toLowerCase().includes(query);
3025
3364
  }
3026
3365
  function AddProviderPickerPanel({ addPickerQuery, credentialProvidersLength, filteredPickerAvailable, filteredPickerConfigured, pickerHasResults, onClose, onQueryChange, onSelect }) {
3027
3366
  return /* @__PURE__ */ jsxs("div", {
@@ -3251,44 +3590,6 @@ function ProviderPickerRow({ configured, onSelect, provider }) {
3251
3590
  })]
3252
3591
  });
3253
3592
  }
3254
- function RowSurface({ className, interactive, padding, tone, ...props }) {
3255
- return /* @__PURE__ */ jsx("div", {
3256
- className: cn(fleetPiRowSurface({
3257
- interactive,
3258
- padding,
3259
- tone
3260
- }), className),
3261
- ...props
3262
- });
3263
- }
3264
- function ItemRow({ icon, title, subtitle, trailing, className, interactive = true, tone = "default" }) {
3265
- return /* @__PURE__ */ jsxs(RowSurface, {
3266
- tone,
3267
- padding: "md",
3268
- interactive,
3269
- className: cn("items-center gap-3", className),
3270
- children: [
3271
- icon ? /* @__PURE__ */ jsx("div", {
3272
- className: "flex size-8 shrink-0 items-center justify-center rounded-md border border-border/40 bg-background/60 text-foreground/70",
3273
- children: icon
3274
- }) : null,
3275
- /* @__PURE__ */ jsxs("div", {
3276
- className: "min-w-0 flex-1",
3277
- children: [/* @__PURE__ */ jsx("div", {
3278
- className: "truncate text-sm font-medium text-foreground",
3279
- children: title
3280
- }), subtitle ? /* @__PURE__ */ jsx("div", {
3281
- className: "mt-0.5 truncate text-xs text-muted-foreground",
3282
- children: subtitle
3283
- }) : null]
3284
- }),
3285
- trailing ? /* @__PURE__ */ jsx("div", {
3286
- className: "flex shrink-0 items-center gap-1.5",
3287
- children: trailing
3288
- }) : null
3289
- ]
3290
- });
3291
- }
3292
3593
  function ActiveProviderList({ providers, editingProvider, form, operation, actions, onEdit, onCancelEdit, onSave, onRemove, onOAuthLogin, onConfigured }) {
3293
3594
  const { api, apiKey, baseUrl, modelId, models, displayName, showPassword, attemptedSave, canSave } = form;
3294
3595
  const { isPending, canRemove } = operation;
@@ -4679,6 +4980,14 @@ var SETTINGS_SECTION_REGISTRY = {
4679
4980
  icon: MessageSquare,
4680
4981
  group: "workspace"
4681
4982
  },
4983
+ mcp: {
4984
+ id: "mcp",
4985
+ order: 25,
4986
+ title: "MCP",
4987
+ ariaLabel: "MCP connection settings",
4988
+ icon: Plug,
4989
+ group: "workspace"
4990
+ },
4682
4991
  sandbox: {
4683
4992
  id: "sandbox",
4684
4993
  order: 30,
@@ -4768,7 +5077,7 @@ function PreferenceRow({ children, description, label }) {
4768
5077
  });
4769
5078
  }
4770
5079
  function useSettingsForm() {
4771
- const { isLoadingProviders, isUpdatingProvider, modelCatalog, onDiscoverModels, onOAuthLogin, onRemoveProvider, onThemePreferenceChange, onUpdateProvider, providers = [], saveSettings, settings, settingsLoading, themePreference } = useSettingsActionsContext();
5080
+ const { isLoadingMcp, isLoadingProviders, isUpdatingMcp, isUpdatingProvider, mcpConnections = [], modelCatalog, onDiscoverModels, onMcpOAuth, onOAuthLogin, onRemoveMcp, onRemoveProvider, onThemePreferenceChange, onUpdateMcp, onUpdateProvider, providers = [], saveSettings, settings, settingsLoading, themePreference } = useSettingsActionsContext();
4772
5081
  const { models, resources } = useChatPanelDataContext();
4773
5082
  const [draft, setDraft] = useState2(null);
4774
5083
  const [savingSection, setSavingSection] = useState2(null);
@@ -4832,11 +5141,17 @@ function useSettingsForm() {
4832
5141
  return "close";
4833
5142
  };
4834
5143
  return {
5144
+ isLoadingMcp,
4835
5145
  isLoadingProviders,
5146
+ isUpdatingMcp,
4836
5147
  isUpdatingProvider,
4837
- onThemePreferenceChange,
5148
+ mcpConnections,
5149
+ onMcpOAuth,
4838
5150
  onOAuthLogin,
5151
+ onRemoveMcp,
4839
5152
  onRemoveProvider,
5153
+ onThemePreferenceChange,
5154
+ onUpdateMcp,
4840
5155
  onUpdateProvider,
4841
5156
  providers,
4842
5157
  resources,
@@ -4907,7 +5222,7 @@ function SettingsDialogSession({ initialTab, onOpenChange, open }) {
4907
5222
  });
4908
5223
  }
4909
5224
  function SettingsDialogPaneContent({ activeTab, form, preferences, updatePreference }) {
4910
- const { isLoadingProviders, isUpdatingProvider, onThemePreferenceChange, onOAuthLogin, onRemoveProvider, onUpdateProvider, providers, resources, settingsLoading, themePreference, draft, savingSection, packageRows, packageError, modelFilter, setModelFilter, resourceSummary, modelOptions, modelDirty, resourceDirty, revertResourceDraft, revertModelDraft, updateDraft, handlePackageRowsChange, addModels, removeModel, setDefaultModel, discoverProvider, discoveringProviderId, saveSection } = form;
5225
+ const { isLoadingMcp, isLoadingProviders, isUpdatingMcp, isUpdatingProvider, mcpConnections, onMcpOAuth, onOAuthLogin, onRemoveMcp, onRemoveProvider, onThemePreferenceChange, onUpdateMcp, onUpdateProvider, providers, resources, settingsLoading, themePreference, draft, savingSection, packageRows, packageError, modelFilter, setModelFilter, resourceSummary, modelOptions, modelDirty, resourceDirty, revertResourceDraft, revertModelDraft, updateDraft, handlePackageRowsChange, addModels, removeModel, setDefaultModel, discoverProvider, discoveringProviderId, saveSection } = form;
4911
5226
  const resourcesPane = (scope) => /* @__PURE__ */ jsx(ResourcesSection, {
4912
5227
  scope,
4913
5228
  draft,
@@ -5017,6 +5332,14 @@ function SettingsDialogPaneContent({ activeTab, form, preferences, updatePrefere
5017
5332
  })
5018
5333
  })]
5019
5334
  }),
5335
+ mcp: () => /* @__PURE__ */ jsx(McpConnectionsSection, {
5336
+ connections: mcpConnections,
5337
+ isLoading: isLoadingMcp ?? false,
5338
+ isPending: isUpdatingMcp ?? false,
5339
+ onOAuth: onMcpOAuth,
5340
+ onRemove: onRemoveMcp,
5341
+ onUpsert: onUpdateMcp
5342
+ }),
5020
5343
  sandbox: () => /* @__PURE__ */ jsx(SandboxProviderSection, {
5021
5344
  isLoading: isLoadingProviders ?? false,
5022
5345
  isPending: isUpdatingProvider ?? false,