@qredence/fleet 0.5.10 → 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 (115) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/web/client/assets/agent-activity-CwMDU7aP.js +1 -0
  3. package/dist/web/client/assets/artifacts-panel-CJ75gQub.js +1 -0
  4. package/dist/web/client/assets/chat-Ci2cEQrR.js +52 -0
  5. package/dist/web/client/assets/{chat-command-palette-DfYSPPXI.js → chat-command-palette-CkISHfKE.js} +1 -1
  6. package/dist/web/client/assets/{citations-BQ_pr_9_.js → citations-C_tPOJE_.js} +1 -1
  7. package/dist/web/client/assets/{command-DPfiaYUL.js → command-_WjnervL.js} +1 -1
  8. package/dist/web/client/assets/fleet-message-queue-BCno4XP-.js +1 -0
  9. package/dist/web/client/assets/{fleet-pi-tool-renderer-AJoMbXBa.js → fleet-pi-tool-renderer-BEDIO3al.js} +4 -4
  10. package/dist/web/client/assets/fleet-reasoning-panel-CbkO7u-v.js +1 -0
  11. package/dist/web/client/assets/{fleet-subagent-list-BnRnrWKS.js → fleet-subagent-list-iKUtLJKZ.js} +1 -1
  12. package/dist/web/client/assets/fleet-tool-timeline-C8heL7ae.js +1 -0
  13. package/dist/web/client/assets/{fork-picker-dialog-CkcSekBl.js → fork-picker-dialog-ByBOGVsa.js} +1 -1
  14. package/dist/web/client/assets/globals-B_I1Eq1u.css +2 -0
  15. package/dist/web/client/assets/horizontal-resize-CwoDojST.js +1 -0
  16. package/dist/web/client/assets/{index-DlnHLDpx.js → index-DJ1fFbQu.js} +3 -3
  17. package/dist/web/client/assets/input-group-Tb5TYbzi.js +1 -0
  18. package/dist/web/client/assets/{ipython-tool-BrMePqD-.js → ipython-tool-ldJ9F1Cw.js} +1 -1
  19. package/dist/web/client/assets/model-selector-list-Ch05B2nZ.js +1 -0
  20. package/dist/web/client/assets/{openui-renderer-DwYFJdbe.js → openui-renderer-Bf60nxNi.js} +4 -4
  21. package/dist/web/client/assets/pencil-DcUwsKLn.js +1 -0
  22. package/dist/web/client/assets/prompt-input-qPIXZ-Rb.js +1 -0
  23. package/dist/web/client/assets/prompt-suggestions-Cc--lSNR.js +1 -0
  24. package/dist/web/client/assets/{provider-label-BCJh04J5.js → provider-label-DDLRnZyC.js} +1 -1
  25. package/dist/web/client/assets/repl-panel-DsPX04Xr.js +1 -0
  26. package/dist/web/client/assets/{resources-panel-Bo8dfUdm.js → resources-panel-D867-goy.js} +1 -1
  27. package/dist/web/client/assets/right-panel-registry-Dp8yX-oL.js +2 -0
  28. package/dist/web/client/assets/routes-DpgMJZ5n.js +16 -0
  29. package/dist/web/client/assets/safe-storage-s25GrfpH.js +1 -0
  30. package/dist/web/client/assets/session-insights-panel-YnaXvTpi.js +1 -0
  31. package/dist/web/client/assets/settings-dialog-YJkvt5EB.js +2 -0
  32. package/dist/web/client/assets/skeleton-loaders-C9vHwAYV.js +1 -0
  33. package/dist/web/client/assets/{subagent-chat-panel-DseoO31p.js → subagent-chat-panel-Yujplxpb.js} +1 -1
  34. package/dist/web/client/assets/subagent-transcript-CnI-mqN0.js +1 -0
  35. package/dist/web/client/assets/subagents-panel-CjXEdimj.js +1 -0
  36. package/dist/web/client/assets/{surfaces-cW-gp56p.js → surfaces-DI-U-_2a.js} +1 -1
  37. package/dist/web/client/assets/tool-result-DfnPN0FQ.js +4 -0
  38. package/dist/web/client/assets/ui-preferences-CImQGZm_.js +1 -0
  39. package/dist/web/client/assets/virtualized-turn-list--Vynre4D.js +6 -0
  40. package/dist/web/client/assets/{workspace-panel-DI1W3ce9.js → workspace-panel-CPPIznT9.js} +1 -1
  41. package/dist/web/client/bundle-budget.json +6 -6
  42. package/dist/web/server/{_tanstack-start-manifest_v-Cdx6hy2i-I7A5YJRU.js → _tanstack-start-manifest_v-DS_CSIFQ-WB3VTKF3.js} +15 -13
  43. package/dist/web/server/{agent-activity-B-JMvecd-SLE3GGY2.js → agent-activity-CS2rbF_C-A3R26CYH.js} +2 -2
  44. package/dist/web/server/{artifacts-panel-DLMK1O8P-42T45JMA.js → artifacts-panel-CaKrX2XC-R37I4BHF.js} +12 -12
  45. package/dist/web/server/{chat-command-palette-CdYwoTMe-4LBAZF7K.js → chat-command-palette-7gbqEnm9-6XICQZN7.js} +4 -4
  46. package/dist/web/server/{chunk-EBCYJSBR.js → chunk-7SD2ZSEA.js} +3 -3
  47. package/dist/web/server/{chunk-6AW2XFTZ.js → chunk-BBBS5AS7.js} +194 -10
  48. package/dist/web/server/{chunk-EOVMHQ2H.js → chunk-CNXJN5HN.js} +7 -7
  49. package/dist/web/server/{chunk-XKKXXG6N.js → chunk-DI7XRK2H.js} +2 -2
  50. package/dist/web/server/{chunk-KNJUSMBT.js → chunk-DZ2IC3FH.js} +2 -2
  51. package/dist/web/server/{chunk-K5ESTT4A.js → chunk-EAE2QXRK.js} +11 -3
  52. package/dist/web/server/{chunk-VHBFU4TG.js → chunk-EH4BZ6QM.js} +123 -16
  53. package/dist/web/server/{chunk-54II5QXQ.js → chunk-ETTDO4UO.js} +6 -3
  54. package/dist/web/server/{chunk-EHNIN245.js → chunk-GETSIVNU.js} +5 -5
  55. package/dist/web/server/{chunk-X2R5YPQZ.js → chunk-IJPCV6DH.js} +3 -2
  56. package/dist/web/server/{chunk-AJJ2S5FF.js → chunk-JQDLJ7KI.js} +20 -20
  57. package/dist/web/server/{chunk-M564UIEY.js → chunk-KELEYPVK.js} +8 -8
  58. package/dist/web/server/{chunk-OB5OSYJK.js → chunk-LXJD6IGW.js} +53 -29
  59. package/dist/web/server/chunk-M5RSPNYN.js +22 -0
  60. package/dist/web/server/{chunk-2TRF62K5.js → chunk-N6NOTURM.js} +3 -3
  61. package/dist/web/server/{chunk-5XX7RKJ6.js → chunk-NXJ32VFR.js} +5 -5
  62. package/dist/web/server/{chunk-P7H2OPKF.js → chunk-PIOFZUOU.js} +9 -1
  63. package/dist/web/server/chunk-PTCKOR23.js +39 -0
  64. package/dist/web/server/chunk-SZJUXVWG.js +40 -0
  65. package/dist/web/server/{chunk-TCH3U26B.js → chunk-TWYHHG5N.js} +2 -10
  66. package/dist/web/server/{chunk-FED63ZHU.js → chunk-WHBVQJ4K.js} +11 -6
  67. package/dist/web/server/{chunk-ANL4B775.js → chunk-XLNCWZMZ.js} +2 -2
  68. package/dist/web/server/{chunk-GM32R7JW.js → chunk-YKVGQBZA.js} +10 -8
  69. package/dist/web/server/{chunk-NRDG2ISJ.js → chunk-YNUWKMNQ.js} +59 -52
  70. package/dist/web/server/{chunk-3PIUR75R.js → chunk-YXXRP3NS.js} +9 -9
  71. package/dist/web/server/fleet-message-queue-BS-cZ56r-GLZXUKDF.js +152 -0
  72. package/dist/web/server/{fleet-pi-tool-renderer-RIeVlM9J-D6MISCQC.js → fleet-pi-tool-renderer-umKunT3K-O4GBH35V.js} +5 -5
  73. package/dist/web/server/{fleet-reasoning-panel-D_K48UOT-GN2PA6KT.js → fleet-reasoning-panel-BxC2COxi-GPLMQSTT.js} +9 -9
  74. package/dist/web/server/{fleet-subagent-list-5luKtpds-3OTVFHJQ.js → fleet-subagent-list-CpSYzCIq-JDH2LHSS.js} +1 -1
  75. package/dist/web/server/{fleet-tool-timeline-DwRqQgOj-HUFLGMDV.js → fleet-tool-timeline-IB36_PB2-XJENTO6I.js} +1 -1
  76. package/dist/web/server/{fork-picker-dialog-B9yGc8Au-IL63F2DN.js → fork-picker-dialog-DlINz9UQ-UWIXFDJX.js} +3 -3
  77. package/dist/web/server/{model-selector-list-cb2-ewoA-KIVU6LD3.js → model-selector-list-jBTZc2Mu-OXJOS5NA.js} +7 -7
  78. package/dist/web/server/{openui-renderer-Czy5Vo2L-BLXPJD65.js → openui-renderer-Biyyjhjn-SJOEKPTJ.js} +4 -4
  79. package/dist/web/server/{prompt-suggestions-BOmHi4-J-C563CHFU.js → prompt-suggestions-D4AEEY7K-RZSP7PWV.js} +3 -3
  80. package/dist/web/server/{repl-panel-yyxhwe4c-7U6HXYPS.js → repl-panel-D5KsoJik-BQKO53JO.js} +9 -9
  81. package/dist/web/server/{resources-panel-j4fAIi9g-5C267IXV.js → resources-panel-CE5pD4vy-V5FXBTYC.js} +2 -2
  82. package/dist/web/server/{router-CgSWrAYc-5UBYSZTJ.js → router-DWALoX_M-I5CBL7FQ.js} +1461 -515
  83. package/dist/web/server/{routes-CP_BBhD0-BAWQYGKY.js → routes-BBVirYfL-5WXMOT7M.js} +908 -566
  84. package/dist/web/server/server.js +2 -2
  85. package/dist/web/server/{session-insights-panel-BHZyKDTo-LRMAMA3O.js → session-insights-panel-CUhdgvRt-VB2HUCQP.js} +4 -4
  86. package/dist/web/server/{settings-dialog-CxHe8nym-KKXDPTIW.js → settings-dialog-DndPbM-v-RFNXXEAN.js} +902 -575
  87. package/dist/web/server/{subagent-chat-panel-DlSsM59d-DBW7DI3T.js → subagent-chat-panel-C0RpnXav-J7VYO7QY.js} +11 -11
  88. package/dist/web/server/{subagents-panel-Txzk8oDb-NRKLASYN.js → subagents-panel-jDqIn9Nh-YEV6QLUY.js} +13 -13
  89. package/dist/web/server/{workspace-panel-BOs-58Cv-SWQBMZSB.js → workspace-panel-CX_Vo6As-EWO6AMPI.js} +5 -4
  90. package/package.json +1 -1
  91. package/dist/web/client/assets/agent-activity-CDcDNVC6.js +0 -1
  92. package/dist/web/client/assets/artifacts-panel-CObI-DMt.js +0 -1
  93. package/dist/web/client/assets/chat-C8XDWWXz.js +0 -52
  94. package/dist/web/client/assets/fleet-message-queue-zWM5-y18.js +0 -1
  95. package/dist/web/client/assets/fleet-reasoning-panel-MtbSdoCv.js +0 -1
  96. package/dist/web/client/assets/fleet-tool-timeline-COzPrwr8.js +0 -1
  97. package/dist/web/client/assets/generative-text-renderer-CDKHv-v_.js +0 -6
  98. package/dist/web/client/assets/globals-DisQPFtV.css +0 -2
  99. package/dist/web/client/assets/horizontal-resize-Bl3AyBJg.js +0 -1
  100. package/dist/web/client/assets/input-group-BFDsCVS9.js +0 -1
  101. package/dist/web/client/assets/model-selector-list-DQaPFUYt.js +0 -1
  102. package/dist/web/client/assets/prompt-input-DZ4cho_e.js +0 -1
  103. package/dist/web/client/assets/prompt-suggestions-CXM6knMK.js +0 -1
  104. package/dist/web/client/assets/repl-panel-BDQ5MEgV.js +0 -1
  105. package/dist/web/client/assets/right-panel-registry-VVRPktV3.js +0 -2
  106. package/dist/web/client/assets/routes-DAikUNzO.js +0 -16
  107. package/dist/web/client/assets/session-insights-panel-CQdT90Op.js +0 -1
  108. package/dist/web/client/assets/settings-dialog-DS64KmwC.js +0 -1
  109. package/dist/web/client/assets/skeleton-loaders-DjGv9LLA.js +0 -1
  110. package/dist/web/client/assets/subagent-transcript-cxVKYnh9.js +0 -1
  111. package/dist/web/client/assets/subagents-panel-CHC96I68.js +0 -1
  112. package/dist/web/client/assets/tool-result-Baj3CdL_.js +0 -4
  113. package/dist/web/client/assets/ui-preferences-DUEFuJFj.js +0 -1
  114. package/dist/web/server/chunk-QK4CHLVV.js +0 -17
  115. package/dist/web/server/fleet-message-queue-BxycG26e-2XLOTCLP.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,17 +24,20 @@ import {
25
24
  useHoverInteractionSharedState,
26
25
  useHoverReferenceInteraction,
27
26
  usePopupHandleStore
28
- } from "./chunk-TCH3U26B.js";
27
+ } from "./chunk-TWYHHG5N.js";
29
28
  import {
30
29
  UI_PREFERENCES_KEY,
31
30
  isModelPatternEnabled,
32
31
  modelMatchesPattern,
33
32
  modelPattern,
34
33
  readUiPreferences
35
- } from "./chunk-54II5QXQ.js";
34
+ } from "./chunk-ETTDO4UO.js";
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,
@@ -73,12 +75,16 @@ import {
73
75
  import {
74
76
  HardDrive
75
77
  } from "./chunk-D7ELZVTI.js";
78
+ import {
79
+ writeStoredValue
80
+ } from "./chunk-SZJUXVWG.js";
76
81
  import {
77
82
  InputGroup,
78
83
  InputGroupAddon,
79
84
  InputGroupButton,
80
- InputGroupInput
81
- } from "./chunk-P7H2OPKF.js";
85
+ InputGroupInput,
86
+ Textarea
87
+ } from "./chunk-PIOFZUOU.js";
82
88
  import {
83
89
  Search
84
90
  } from "./chunk-JIULUYHT.js";
@@ -207,7 +213,9 @@ import {
207
213
  useRenderElement,
208
214
  useStableCallback
209
215
  } from "./chunk-C5X6G26O.js";
210
- import "./chunk-AHTO6GDS.js";
216
+ import {
217
+ Plug
218
+ } from "./chunk-AHTO6GDS.js";
211
219
  import "./chunk-EAJTWMK5.js";
212
220
  import "./chunk-HFQZLPU2.js";
213
221
  import {
@@ -220,7 +228,7 @@ import "./chunk-JDCCC7A7.js";
220
228
  import "./chunk-L547YHC4.js";
221
229
  import "./chunk-MLKGABMK.js";
222
230
 
223
- // web/app/dist/server/assets/settings-dialog-CxHe8nym.js
231
+ // web/app/dist/server/assets/settings-dialog-DndPbM-v.js
224
232
  import * as React$1 from "react";
225
233
  import { useCallback as useCallback2, useEffect as useEffect2, useId, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
226
234
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -2281,6 +2289,44 @@ function PersonalizationSection({ onThemePreferenceChange, themePreference }) {
2281
2289
  })]
2282
2290
  });
2283
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
+ }
2284
2330
  function SettingsPane({ actions, children, description, title }) {
2285
2331
  return /* @__PURE__ */ jsxs("div", {
2286
2332
  className: "flex flex-col gap-4",
@@ -2328,393 +2374,11 @@ function SettingsCommitActions({ dirty, disabled: disabled2, onRevert, onSave, s
2328
2374
  }) : /* @__PURE__ */ jsx(Save, { "data-icon": "inline-start" }), "Commit"]
2329
2375
  })] });
2330
2376
  }
2331
- function isFieldInvalid(value, attemptedSave, required = true) {
2332
- return attemptedSave && required && value.trim().length === 0;
2333
- }
2334
- function SecretCredentialField({ attemptedSave, label, onChange, onToggleVisibility, placeholder, required = true, showPassword, value }) {
2335
- const controlId = useId();
2336
- const invalid = isFieldInvalid(value, attemptedSave, required);
2337
- return /* @__PURE__ */ jsxs(Field, {
2338
- "data-invalid": invalid || void 0,
2339
- children: [/* @__PURE__ */ jsx(FieldLabel, {
2340
- htmlFor: controlId,
2341
- children: label
2342
- }), /* @__PURE__ */ jsxs(InputGroup, { children: [
2343
- /* @__PURE__ */ jsx(InputGroupAddon, {
2344
- align: "inline-start",
2345
- children: /* @__PURE__ */ jsx(Lock, {})
2346
- }),
2347
- /* @__PURE__ */ jsx(InputGroupInput, {
2348
- id: controlId,
2349
- type: showPassword ? "text" : "password",
2350
- placeholder,
2351
- value,
2352
- onChange: (event) => onChange(event.target.value),
2353
- "aria-label": label,
2354
- "aria-invalid": invalid || void 0
2355
- }),
2356
- /* @__PURE__ */ jsx(InputGroupAddon, {
2357
- align: "inline-end",
2358
- children: /* @__PURE__ */ jsx(InputGroupButton, {
2359
- size: "icon-xs",
2360
- className: cn(HIT_AREA_EXPAND_CLASS, "transition-[background-color,transform] duration-150 active:scale-[0.96]"),
2361
- "aria-label": showPassword ? "Hide value" : "Show value",
2362
- onClick: onToggleVisibility,
2363
- children: showPassword ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {})
2364
- })
2365
- })
2366
- ] })]
2367
- });
2368
- }
2369
- function TextCredentialField({ attemptedSave, icon: Icon, inputType = "text", label, onChange, placeholder, required = true, value }) {
2370
- const controlId = useId();
2371
- const invalid = isFieldInvalid(value, attemptedSave, required);
2372
- return /* @__PURE__ */ jsxs(Field, {
2373
- "data-invalid": invalid || void 0,
2374
- children: [/* @__PURE__ */ jsx(FieldLabel, {
2375
- htmlFor: controlId,
2376
- children: label
2377
- }), /* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(InputGroupAddon, {
2378
- align: "inline-start",
2379
- children: /* @__PURE__ */ jsx(Icon, {})
2380
- }), /* @__PURE__ */ jsx(InputGroupInput, {
2381
- id: controlId,
2382
- type: inputType,
2383
- placeholder,
2384
- value,
2385
- onChange: (event) => onChange(event.target.value),
2386
- "aria-label": label,
2387
- "aria-invalid": invalid || void 0
2388
- })] })]
2389
- });
2390
- }
2391
- var OPENAI_CHAT_BASE_URL_PLACEHOLDER = "https://opencode.ai/zen/v1";
2392
- var OPENAI_CHAT_MODEL_PLACEHOLDER = "qwen35-122b-a10b";
2393
- var OPENAI_CHAT_NAME_PLACEHOLDER = "e.g. OpenCode Zen, Nebius, Groq\u2026";
2394
- var CUSTOM_MODELS_PLACEHOLDER = "model-a, model-b (comma-separated)";
2395
- var CUSTOM_PROVIDER_API_OPTIONS = [
2396
- {
2397
- value: "openai-completions",
2398
- label: "OpenAI Chat Completions"
2399
- },
2400
- {
2401
- value: "openai-responses",
2402
- label: "OpenAI Responses"
2403
- },
2404
- {
2405
- value: "anthropic-messages",
2406
- label: "Anthropic Messages"
2407
- },
2408
- {
2409
- value: "google-genai",
2410
- label: "Google Generative AI"
2411
- }
2412
- ];
2413
- function ProviderCredentialFields({ attemptedSave, apiKey, api, baseUrl, modelId, models, displayName, isCustom, onApiKeyChange, onApiChange, onBaseUrlChange, onModelIdChange, onModelsChange, onDisplayNameChange, onTogglePassword, openAiChat, placeholder, showPassword }) {
2414
- const showApiSelector = isCustom && onApiChange;
2415
- const showMultiModels = isCustom && onModelsChange;
2416
- return /* @__PURE__ */ jsxs(FieldGroup, {
2417
- className: "gap-2",
2418
- children: [
2419
- (openAiChat || isCustom) && onDisplayNameChange ? /* @__PURE__ */ jsx(TextCredentialField, {
2420
- attemptedSave,
2421
- icon: Tag,
2422
- label: "Provider name",
2423
- placeholder: OPENAI_CHAT_NAME_PLACEHOLDER,
2424
- value: displayName ?? "",
2425
- onChange: onDisplayNameChange
2426
- }) : null,
2427
- showApiSelector ? /* @__PURE__ */ jsx(ApiSelector, {
2428
- value: api ?? "openai-completions",
2429
- onChange: onApiChange
2430
- }) : null,
2431
- /* @__PURE__ */ jsx(SecretCredentialField, {
2432
- attemptedSave,
2433
- label: "API key",
2434
- placeholder,
2435
- value: apiKey,
2436
- showPassword,
2437
- onChange: onApiKeyChange,
2438
- onToggleVisibility: onTogglePassword
2439
- }),
2440
- openAiChat || isCustom ? /* @__PURE__ */ jsx(TextCredentialField, {
2441
- attemptedSave,
2442
- icon: Globe,
2443
- inputType: "url",
2444
- label: "Base URL",
2445
- placeholder: OPENAI_CHAT_BASE_URL_PLACEHOLDER,
2446
- value: baseUrl,
2447
- onChange: onBaseUrlChange
2448
- }) : null,
2449
- showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
2450
- attemptedSave,
2451
- icon: Layers,
2452
- label: "Model ids",
2453
- placeholder: CUSTOM_MODELS_PLACEHOLDER,
2454
- value: models ?? "",
2455
- onChange: onModelsChange
2456
- }) : null,
2457
- openAiChat && !showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
2458
- attemptedSave,
2459
- icon: Box,
2460
- label: "Model name",
2461
- placeholder: OPENAI_CHAT_MODEL_PLACEHOLDER,
2462
- value: modelId,
2463
- onChange: onModelIdChange
2464
- }) : null
2465
- ]
2466
- });
2467
- }
2468
- function ApiSelector({ value, onChange }) {
2469
- return /* @__PURE__ */ jsxs("div", {
2470
- className: "flex flex-col gap-1",
2471
- children: [/* @__PURE__ */ jsx("span", {
2472
- className: "text-xs font-medium text-muted-foreground",
2473
- children: "API family"
2474
- }), /* @__PURE__ */ jsx(Select, {
2475
- "aria-label": "API family",
2476
- value,
2477
- options: CUSTOM_PROVIDER_API_OPTIONS,
2478
- placeholder: "Select API family",
2479
- onValueChange: (next) => onChange(next)
2480
- })]
2481
- });
2482
- }
2483
- function isOAuthProvider(provider) {
2484
- return provider.authType === "oauth";
2485
- }
2486
- function supportsOAuth(provider) {
2487
- return provider.supportsOAuth === true;
2488
- }
2489
- var CUSTOM_PROVIDER_PICKER_ROW = {
2490
- id: "custom",
2491
- name: "Custom provider",
2492
- isConfigured: false,
2493
- envVarName: "CUSTOM_PROVIDER_API_KEY",
2494
- displayName: "Custom provider"
2495
- };
2496
- function parseCustomProviderModels(raw) {
2497
- return raw.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
2498
- }
2499
- function useProviderCredentialsController({ onRemoveProvider, onUpdateProvider, providers }) {
2500
- const [editingProvider, setEditingProvider] = useState2(null);
2501
- const [editingIsNewOccInstance, setEditingIsNewOccInstance] = useState2(false);
2502
- const [apiKey, setApiKey] = useState2("");
2503
- const [baseUrl, setBaseUrl] = useState2("");
2504
- const [modelId, setModelId] = useState2("");
2505
- const [models, setModels] = useState2("");
2506
- const [api, setApi] = useState2("openai-completions");
2507
- const [displayName, setDisplayName] = useState2("");
2508
- const [showPassword, setShowPassword] = useState2(false);
2509
- const [attemptedSave, setAttemptedSave] = useState2(false);
2510
- const [searchQuery, setSearchQuery] = useState2("");
2511
- const [addPickerOpen, setAddPickerOpen] = useState2(false);
2512
- const [addPickerQuery, setAddPickerQuery] = useState2("");
2513
- const [confirmRemoveProvider, setConfirmRemoveProvider] = useState2(null);
2514
- const credentialProviders = useMemo2(() => {
2515
- return [...providers.filter((provider) => provider.id !== CUSTOM_PROVIDER_PICKER_ROW.id), CUSTOM_PROVIDER_PICKER_ROW];
2516
- }, [providers]);
2517
- const activeProviders = useMemo2(() => credentialProviders.filter((provider) => provider.isConfigured), [credentialProviders]);
2518
- const unconfiguredProviders = useMemo2(() => credentialProviders.filter((provider) => !provider.isConfigured || provider.id === "openai-chat-completions" && provider.providerFamily === void 0), [credentialProviders]);
2519
- const filteredActiveProviders = useMemo2(() => {
2520
- const query = searchQuery.trim().toLowerCase();
2521
- if (!query) return activeProviders;
2522
- return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
2523
- }, [activeProviders, searchQuery]);
2524
- const filteredPickerAvailable = useMemo2(() => {
2525
- const query = addPickerQuery.trim().toLowerCase();
2526
- return unconfiguredProviders.filter((provider) => providerMatchesQuery(provider, query));
2527
- }, [addPickerQuery, unconfiguredProviders]);
2528
- const filteredPickerConfigured = useMemo2(() => {
2529
- const query = addPickerQuery.trim().toLowerCase();
2530
- return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
2531
- }, [activeProviders, addPickerQuery]);
2532
- const pickerHasResults = filteredPickerAvailable.length > 0 || filteredPickerConfigured.length > 0;
2533
- const editingUnconfiguredProvider = editingProvider && !activeProviders.some((provider) => provider.id === editingProvider) ? credentialProviders.find((entry) => entry.id === editingProvider) ?? null : null;
2534
- const resetEditor = () => {
2535
- setApiKey("");
2536
- setBaseUrl("");
2537
- setModelId("");
2538
- setModels("");
2539
- setApi("openai-completions");
2540
- setDisplayName("");
2541
- setShowPassword(false);
2542
- setAttemptedSave(false);
2543
- };
2544
- const closeEditor = () => {
2545
- setEditingProvider(null);
2546
- setEditingIsNewOccInstance(false);
2547
- resetEditor();
2548
- };
2549
- const openEditor = (providerId, opts) => {
2550
- setAddPickerOpen(false);
2551
- setAddPickerQuery("");
2552
- setEditingProvider(providerId);
2553
- setEditingIsNewOccInstance(opts?.newOccInstance === true);
2554
- resetEditor();
2555
- const existing = providers.find((provider) => provider.id === providerId);
2556
- setBaseUrl(existing?.baseUrl ?? "");
2557
- setDisplayName(existing?.displayName ?? (isCustomProviderId(providerId) ? existing?.name ?? "" : ""));
2558
- setApi(existing?.api ?? "openai-completions");
2559
- setModels((existing?.modelIds ?? []).join(", "));
2560
- setModelId(existing?.modelIds?.[0] ?? "");
2561
- };
2562
- const openAddPicker = () => {
2563
- closeEditor();
2564
- setAddPickerQuery("");
2565
- setAddPickerOpen(true);
2566
- };
2567
- const closeAddPicker = () => {
2568
- setAddPickerOpen(false);
2569
- setAddPickerQuery("");
2570
- };
2571
- const selectProviderFromPicker = (providerId) => {
2572
- openEditor(providerId, { newOccInstance: isOccProviderId(providerId) });
2573
- };
2574
- const handleSave = async (providerId) => {
2575
- if (!onUpdateProvider) return;
2576
- setAttemptedSave(true);
2577
- const isCustomEditor = providerId === "custom" || isCustomProviderId(providerId);
2578
- const openAiChat = isOccProviderId(providerId);
2579
- if (isCustomEditor) {
2580
- const modelList = parseCustomProviderModels(models);
2581
- if (!apiKey.trim() || !baseUrl.trim() || !displayName.trim()) return;
2582
- if (modelList.length === 0) return;
2583
- try {
2584
- await onUpdateProvider({
2585
- providerId,
2586
- apiKey: apiKey.trim(),
2587
- baseUrl: baseUrl.trim(),
2588
- displayName: displayName.trim(),
2589
- api,
2590
- models: modelList,
2591
- ...providerId === "custom" ? { createOccInstance: true } : {}
2592
- });
2593
- notify.success("Provider credentials applied to your active sessions.");
2594
- closeEditor();
2595
- closeAddPicker();
2596
- } catch (error) {
2597
- notify.error(error instanceof Error ? error.message : "Failed to update provider");
2598
- }
2599
- return;
2600
- }
2601
- const isNamedOcc = openAiChat && isNamedOccInstanceId(providerId);
2602
- const isOccLike = openAiChat && (isNamedOcc || editingIsNewOccInstance);
2603
- if (!apiKey.trim()) return;
2604
- if (openAiChat && (!baseUrl.trim() || !modelId.trim())) return;
2605
- if (isOccLike && !displayName.trim()) return;
2606
- try {
2607
- await onUpdateProvider({
2608
- providerId,
2609
- apiKey: apiKey.trim(),
2610
- ...openAiChat ? {
2611
- baseUrl: baseUrl.trim(),
2612
- modelId: modelId.trim(),
2613
- ...isOccLike ? { displayName: displayName.trim() } : {},
2614
- ...editingIsNewOccInstance ? { createOccInstance: true } : {}
2615
- } : {}
2616
- });
2617
- notify.success("Provider credentials applied to your active sessions.");
2618
- closeEditor();
2619
- closeAddPicker();
2620
- } catch (error) {
2621
- notify.error(error instanceof Error ? error.message : "Failed to update provider");
2622
- }
2623
- };
2624
- const handleRemove = async (provider) => {
2625
- if (!onRemoveProvider) return;
2626
- try {
2627
- await onRemoveProvider({ providerId: provider.id });
2628
- if (editingProvider === provider.id) closeEditor();
2629
- notify.success(`${provider.name} removed from your configured providers.`);
2630
- setConfirmRemoveProvider(null);
2631
- } catch (error) {
2632
- notify.error(error instanceof Error ? error.message : "Failed to remove provider");
2633
- }
2634
- };
2635
- return {
2636
- activeProviders,
2637
- addPickerOpen,
2638
- addPickerQuery,
2639
- api,
2640
- apiKey,
2641
- attemptedSave,
2642
- baseUrl,
2643
- canSave: useMemo2(() => {
2644
- if (apiKey.trim().length === 0) return false;
2645
- if (!editingProvider) return true;
2646
- if (editingProvider === "custom" || isCustomProviderId(editingProvider)) {
2647
- if (baseUrl.trim().length === 0) return false;
2648
- if (parseCustomProviderModels(models).length === 0) return false;
2649
- return displayName.trim().length > 0;
2650
- }
2651
- if (!isOccProviderId(editingProvider)) return true;
2652
- if (baseUrl.trim().length === 0 || modelId.trim().length === 0) return false;
2653
- if (isNamedOccInstanceId(editingProvider) || editingIsNewOccInstance) return displayName.trim().length > 0;
2654
- return true;
2655
- }, [
2656
- apiKey,
2657
- baseUrl,
2658
- modelId,
2659
- models,
2660
- displayName,
2661
- editingProvider,
2662
- editingIsNewOccInstance
2663
- ]),
2664
- closeAddPicker,
2665
- closeEditor,
2666
- confirmRemoveProvider,
2667
- credentialProviders,
2668
- displayName,
2669
- editingIsNewOccInstance,
2670
- editingProvider,
2671
- editingUnconfiguredProvider,
2672
- filteredActiveProviders,
2673
- filteredPickerAvailable,
2674
- filteredPickerConfigured,
2675
- handleRemove,
2676
- handleSave,
2677
- modelId,
2678
- models,
2679
- openAddPicker,
2680
- openEditor,
2681
- pickerHasResults,
2682
- resetEditor,
2683
- searchQuery,
2684
- selectProviderFromPicker,
2685
- setAddPickerOpen,
2686
- setAddPickerQuery,
2687
- setApi,
2688
- setApiKey,
2689
- setAttemptedSave,
2690
- setBaseUrl,
2691
- setConfirmRemoveProvider,
2692
- setDisplayName,
2693
- setEditingIsNewOccInstance,
2694
- setEditingProvider,
2695
- setModelId,
2696
- setModels,
2697
- setSearchQuery,
2698
- setShowPassword,
2699
- showPassword,
2700
- unconfiguredProviders
2701
- };
2702
- }
2703
- function providerMatchesQuery(provider, query) {
2704
- if (!query) return true;
2705
- 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" : "";
2706
- return [
2707
- provider.name,
2708
- provider.envVarName,
2709
- provider.id,
2710
- keywords
2711
- ].join(" ").toLowerCase().includes(query);
2712
- }
2713
- var OAUTH_POLL_MS = 1500;
2714
- var STALE_LOGIN_ERROR = "Unknown or expired OAuth login";
2715
- function delay(ms) {
2716
- return new Promise((resolve) => {
2717
- 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);
2718
2382
  });
2719
2383
  }
2720
2384
  function useOAuthLoginFlow({ onConfigured, onOAuthLogin, providerId }) {
@@ -2884,141 +2548,819 @@ function useOAuthLoginFlow({ onConfigured, onOAuthLogin, providerId }) {
2884
2548
  login?.status
2885
2549
  ]);
2886
2550
  return {
2887
- busy,
2888
- cancel,
2889
- canStart: Boolean(onOAuthLogin),
2890
- login,
2891
- promptAnswer,
2892
- setPromptAnswer,
2893
- start,
2894
- 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
2895
3353
  };
2896
3354
  }
2897
- function ProviderOAuthSignIn({ onConfigured, onOAuthLogin, provider }) {
2898
- const { busy, cancel, canStart, login, promptAnswer, setPromptAnswer, start, submitPrompt } = useOAuthLoginFlow({
2899
- onConfigured,
2900
- onOAuthLogin,
2901
- providerId: provider.id
2902
- });
2903
- const waiting = login?.status === "waiting";
2904
- const error = login?.status === "error" ? login.error : void 0;
2905
- const authUrl = waiting ? login?.authUrl : void 0;
2906
- const userCode = waiting ? login?.userCode : void 0;
2907
- const instructions = waiting ? login?.instructions : void 0;
2908
- const prompt = waiting ? login?.prompt : void 0;
2909
- const oauthOnly = provider.authType === "oauth";
2910
- return /* @__PURE__ */ jsxs("div", {
2911
- className: "flex flex-col gap-3",
2912
- children: [provider.isConfigured && oauthOnly && !waiting ? /* @__PURE__ */ jsxs("div", {
2913
- className: "flex items-center gap-2 text-sm",
2914
- children: [/* @__PURE__ */ jsx(Check, { className: "size-3.5 text-muted-foreground" }), /* @__PURE__ */ jsx("span", { children: "Connected" })]
2915
- }) : null, waiting ? /* @__PURE__ */ jsxs("div", {
2916
- className: "flex flex-col gap-2",
2917
- children: [
2918
- /* @__PURE__ */ jsxs("p", {
2919
- className: "text-sm",
2920
- children: [
2921
- "Sign in with ",
2922
- provider.name,
2923
- " to store OAuth credentials for this account."
2924
- ]
2925
- }),
2926
- userCode ? /* @__PURE__ */ jsxs("p", {
2927
- className: "text-sm",
2928
- children: [
2929
- "Enter this code:",
2930
- " ",
2931
- /* @__PURE__ */ jsx("span", {
2932
- className: "font-mono text-foreground",
2933
- children: userCode
2934
- })
2935
- ]
2936
- }) : null,
2937
- instructions && !userCode ? /* @__PURE__ */ jsx("p", {
2938
- className: "text-xs text-pretty text-muted-foreground",
2939
- children: instructions
2940
- }) : null,
2941
- authUrl ? /* @__PURE__ */ jsxs(Button, {
2942
- type: "button",
2943
- size: "sm",
2944
- onClick: () => {
2945
- window.open(authUrl, "_blank", "noopener,noreferrer");
2946
- },
2947
- children: [/* @__PURE__ */ jsx(ExternalLink, { "data-icon": "inline-start" }), "Open sign-in page"]
2948
- }) : prompt ? null : /* @__PURE__ */ jsxs("div", {
2949
- className: "flex items-center gap-2 text-xs text-muted-foreground",
2950
- children: [/* @__PURE__ */ jsx(Spinner, { className: "size-3" }), "Waiting for sign-in\u2026"]
2951
- }),
2952
- prompt ? /* @__PURE__ */ jsxs("div", {
2953
- className: "flex flex-col gap-2",
2954
- children: [
2955
- /* @__PURE__ */ jsx("p", {
2956
- className: "text-xs text-pretty text-muted-foreground",
2957
- children: prompt.message
2958
- }),
2959
- /* @__PURE__ */ jsx(Input, {
2960
- value: promptAnswer,
2961
- placeholder: prompt.placeholder,
2962
- onChange: (event) => setPromptAnswer(event.target.value),
2963
- "aria-label": prompt.message
2964
- }),
2965
- /* @__PURE__ */ jsx("div", {
2966
- className: "flex justify-end",
2967
- children: /* @__PURE__ */ jsxs(Button, {
2968
- type: "button",
2969
- size: "sm",
2970
- disabled: busy || !prompt.allowEmpty && !promptAnswer.trim(),
2971
- onClick: () => {
2972
- submitPrompt();
2973
- },
2974
- children: [busy ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Continue"]
2975
- })
2976
- })
2977
- ]
2978
- }) : null,
2979
- /* @__PURE__ */ jsx("div", {
2980
- className: "flex justify-end",
2981
- children: /* @__PURE__ */ jsx(Button, {
2982
- type: "button",
2983
- variant: "ghost",
2984
- size: "sm",
2985
- disabled: busy,
2986
- onClick: () => {
2987
- cancel();
2988
- },
2989
- children: "Cancel sign-in"
2990
- })
2991
- })
2992
- ]
2993
- }) : /* @__PURE__ */ jsxs("div", {
2994
- className: "flex flex-col gap-2",
2995
- children: [error ? /* @__PURE__ */ jsx(Alert, {
2996
- className: "px-3 py-2",
2997
- children: /* @__PURE__ */ jsx(AlertDescription, {
2998
- className: "text-xs text-pretty",
2999
- children: error
3000
- })
3001
- }) : /* @__PURE__ */ jsx("p", {
3002
- className: "text-xs text-pretty text-muted-foreground",
3003
- 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.`
3004
- }), /* @__PURE__ */ jsx("div", {
3005
- className: "flex justify-end",
3006
- children: /* @__PURE__ */ jsxs(Button, {
3007
- type: "button",
3008
- size: "sm",
3009
- disabled: busy || !canStart,
3010
- onClick: () => {
3011
- start();
3012
- },
3013
- children: [
3014
- busy ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null,
3015
- "Sign in with ",
3016
- provider.name
3017
- ]
3018
- })
3019
- })]
3020
- })]
3021
- });
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);
3022
3364
  }
3023
3365
  function AddProviderPickerPanel({ addPickerQuery, credentialProvidersLength, filteredPickerAvailable, filteredPickerConfigured, pickerHasResults, onClose, onQueryChange, onSelect }) {
3024
3366
  return /* @__PURE__ */ jsxs("div", {
@@ -3071,7 +3413,7 @@ function AddProviderPickerPanel({ addPickerQuery, credentialProvidersLength, fil
3071
3413
  }) : null, filteredPickerConfigured.length > 0 ? /* @__PURE__ */ jsxs("div", {
3072
3414
  className: "flex flex-col gap-1",
3073
3415
  children: [filteredPickerAvailable.length > 0 ? /* @__PURE__ */ jsx("p", {
3074
- className: "px-2 pt-1 text-[11px] font-medium tracking-wide text-muted-foreground uppercase",
3416
+ className: "px-2 pt-1 text-[0.6875rem] font-medium tracking-wide text-muted-foreground uppercase",
3075
3417
  children: "Already configured"
3076
3418
  }) : null, filteredPickerConfigured.map((provider) => /* @__PURE__ */ jsx(ProviderPickerRow, {
3077
3419
  provider,
@@ -3248,44 +3590,6 @@ function ProviderPickerRow({ configured, onSelect, provider }) {
3248
3590
  })]
3249
3591
  });
3250
3592
  }
3251
- function RowSurface({ className, interactive, padding, tone, ...props }) {
3252
- return /* @__PURE__ */ jsx("div", {
3253
- className: cn(fleetPiRowSurface({
3254
- interactive,
3255
- padding,
3256
- tone
3257
- }), className),
3258
- ...props
3259
- });
3260
- }
3261
- function ItemRow({ icon, title, subtitle, trailing, className, interactive = true, tone = "default" }) {
3262
- return /* @__PURE__ */ jsxs(RowSurface, {
3263
- tone,
3264
- padding: "md",
3265
- interactive,
3266
- className: cn("items-center gap-3", className),
3267
- children: [
3268
- icon ? /* @__PURE__ */ jsx("div", {
3269
- className: "flex size-8 shrink-0 items-center justify-center rounded-md border border-border/40 bg-background/60 text-foreground/70",
3270
- children: icon
3271
- }) : null,
3272
- /* @__PURE__ */ jsxs("div", {
3273
- className: "min-w-0 flex-1",
3274
- children: [/* @__PURE__ */ jsx("div", {
3275
- className: "truncate text-sm font-medium text-foreground",
3276
- children: title
3277
- }), subtitle ? /* @__PURE__ */ jsx("div", {
3278
- className: "mt-0.5 truncate text-xs text-muted-foreground",
3279
- children: subtitle
3280
- }) : null]
3281
- }),
3282
- trailing ? /* @__PURE__ */ jsx("div", {
3283
- className: "flex shrink-0 items-center gap-1.5",
3284
- children: trailing
3285
- }) : null
3286
- ]
3287
- });
3288
- }
3289
3593
  function ActiveProviderList({ providers, editingProvider, form, operation, actions, onEdit, onCancelEdit, onSave, onRemove, onOAuthLogin, onConfigured }) {
3290
3594
  const { api, apiKey, baseUrl, modelId, models, displayName, showPassword, attemptedSave, canSave } = form;
3291
3595
  const { isPending, canRemove } = operation;
@@ -3569,7 +3873,7 @@ function searchableModelText(model) {
3569
3873
  model.id
3570
3874
  ].join(" ").toLowerCase();
3571
3875
  }
3572
- function AddModelsDialog({ configuredProviderIds, discoverableProviderIds, discoveringProviderId, enabledModels, modelOptions, onAddModels, onDiscoverProvider, onOpenChange, providerLabel = formatProviderLabel }) {
3876
+ function AddModelsDialog({ configuredProviderIds, discoverableProviderIds, discoveringProviderId, enabledModels, modelOptions, onAddModels, onDiscoverProvider, onOpenChange, open, providerLabel = formatProviderLabel }) {
3573
3877
  const [filter, setFilter] = useState2("");
3574
3878
  const [selectedKeys, setSelectedKeys] = useState2(/* @__PURE__ */ new Set());
3575
3879
  const normalizedFilter = filter.trim().toLowerCase();
@@ -3621,7 +3925,7 @@ function AddModelsDialog({ configuredProviderIds, discoverableProviderIds, disco
3621
3925
  onOpenChange(false);
3622
3926
  };
3623
3927
  return /* @__PURE__ */ jsx(Dialog, {
3624
- open: true,
3928
+ open,
3625
3929
  onOpenChange,
3626
3930
  children: /* @__PURE__ */ jsxs(DialogContent, {
3627
3931
  className: "max-w-lg",
@@ -3682,7 +3986,7 @@ function AddModelsDialog({ configuredProviderIds, discoverableProviderIds, disco
3682
3986
  children: groupedCandidates.map(([provider, models]) => /* @__PURE__ */ jsxs("div", {
3683
3987
  className: "flex flex-col gap-1",
3684
3988
  children: [/* @__PURE__ */ jsxs("div", {
3685
- className: "px-2 text-[11px] font-medium tracking-wide text-muted-foreground uppercase",
3989
+ className: "px-2 text-[0.6875rem] font-medium tracking-wide text-muted-foreground uppercase",
3686
3990
  children: [providerLabel(provider), (activeCountByProvider.get(provider) ?? 0) > 0 ? /* @__PURE__ */ jsxs("span", {
3687
3991
  className: "ml-1 font-normal normal-case tabular-nums",
3688
3992
  children: [
@@ -3924,7 +4228,8 @@ function ModelDefaultsSection({ draft, discoveringProviderId, modelDirty, modelF
3924
4228
  }, model.id))]
3925
4229
  }, provider))
3926
4230
  }),
3927
- addOpen ? /* @__PURE__ */ jsx(AddModelsDialog, {
4231
+ /* @__PURE__ */ jsx(AddModelsDialog, {
4232
+ open: addOpen,
3928
4233
  configuredProviderIds,
3929
4234
  discoverableProviderIds,
3930
4235
  discoveringProviderId,
@@ -3934,7 +4239,7 @@ function ModelDefaultsSection({ draft, discoveringProviderId, modelDirty, modelF
3934
4239
  onDiscoverProvider,
3935
4240
  onOpenChange: setAddOpen,
3936
4241
  providerLabel
3937
- }) : null
4242
+ })
3938
4243
  ]
3939
4244
  });
3940
4245
  }
@@ -4675,6 +4980,14 @@ var SETTINGS_SECTION_REGISTRY = {
4675
4980
  icon: MessageSquare,
4676
4981
  group: "workspace"
4677
4982
  },
4983
+ mcp: {
4984
+ id: "mcp",
4985
+ order: 25,
4986
+ title: "MCP",
4987
+ ariaLabel: "MCP connection settings",
4988
+ icon: Plug,
4989
+ group: "workspace"
4990
+ },
4678
4991
  sandbox: {
4679
4992
  id: "sandbox",
4680
4993
  order: 30,
@@ -4764,7 +5077,7 @@ function PreferenceRow({ children, description, label }) {
4764
5077
  });
4765
5078
  }
4766
5079
  function useSettingsForm() {
4767
- 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();
4768
5081
  const { models, resources } = useChatPanelDataContext();
4769
5082
  const [draft, setDraft] = useState2(null);
4770
5083
  const [savingSection, setSavingSection] = useState2(null);
@@ -4828,11 +5141,17 @@ function useSettingsForm() {
4828
5141
  return "close";
4829
5142
  };
4830
5143
  return {
5144
+ isLoadingMcp,
4831
5145
  isLoadingProviders,
5146
+ isUpdatingMcp,
4832
5147
  isUpdatingProvider,
4833
- onThemePreferenceChange,
5148
+ mcpConnections,
5149
+ onMcpOAuth,
4834
5150
  onOAuthLogin,
5151
+ onRemoveMcp,
4835
5152
  onRemoveProvider,
5153
+ onThemePreferenceChange,
5154
+ onUpdateMcp,
4836
5155
  onUpdateProvider,
4837
5156
  providers,
4838
5157
  resources,
@@ -4903,7 +5222,7 @@ function SettingsDialogSession({ initialTab, onOpenChange, open }) {
4903
5222
  });
4904
5223
  }
4905
5224
  function SettingsDialogPaneContent({ activeTab, form, preferences, updatePreference }) {
4906
- 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;
4907
5226
  const resourcesPane = (scope) => /* @__PURE__ */ jsx(ResourcesSection, {
4908
5227
  scope,
4909
5228
  draft,
@@ -5013,6 +5332,14 @@ function SettingsDialogPaneContent({ activeTab, form, preferences, updatePrefere
5013
5332
  })
5014
5333
  })]
5015
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
+ }),
5016
5343
  sandbox: () => /* @__PURE__ */ jsx(SandboxProviderSection, {
5017
5344
  isLoading: isLoadingProviders ?? false,
5018
5345
  isPending: isUpdatingProvider ?? false,
@@ -5101,7 +5428,7 @@ function SettingsDialogBody({ initialTab, onCloseAttemptRef, onOpenChange }) {
5101
5428
  const [activeTab, setActiveTab] = useState2(() => initialTab ?? "appearance");
5102
5429
  const [preferences, setPreferences] = useState2(readUiPreferences);
5103
5430
  useEffect2(() => {
5104
- window.localStorage.setItem(UI_PREFERENCES_KEY, JSON.stringify(preferences));
5431
+ writeStoredValue(UI_PREFERENCES_KEY, JSON.stringify(preferences));
5105
5432
  document.documentElement.dataset.density = preferences.density;
5106
5433
  document.documentElement.classList.toggle("reduce-motion", preferences.motion === "reduced");
5107
5434
  }, [preferences]);