@tonyclaw/llm-inspector 1.14.2 → 1.14.4

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.
@@ -1,5 +1,5 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, a as parseRequest, s as stripClaudeCodeBillingHeader, R as RuntimeConfigSchema, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, b as createFailedProviderTestResults, d as ProviderConfigSchema, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-BoeSXWHG.mjs";
2
+ import { C as CapturedLogSchema, a as parseRequest, s as stripClaudeCodeBillingHeader, R as RuntimeConfigSchema, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, b as createFailedProviderTestResults, d as ProviderConfigSchema, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-Bl3OCdGC.mjs";
3
3
  import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
4
4
  import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
5
5
  import { J as JSZip } from "../_libs/jszip.mjs";
@@ -10,9 +10,9 @@ import { d as diffLines, a as diffJson } from "../_libs/diff.mjs";
10
10
  import { R as Root, T as Trigger$1, C as Content, a as Close, b as Title, P as Portal$1, O as Overlay } from "../_libs/radix-ui__react-dialog.mjs";
11
11
  import { R as Root2, T as Trigger, I as Icon, V as Value, P as Portal, C as Content2, a as Viewport, b as Item, c as ItemIndicator, d as ItemText, S as ScrollUpButton, e as ScrollDownButton } from "../_libs/radix-ui__react-select.mjs";
12
12
  import "../_libs/modelcontextprotocol__server.mjs";
13
- import { D as Download, L as LayoutGrid, a as List, G as GitCompareArrows, X, S as Settings, C as ChevronDown, b as Check, R as RotateCcw, U as Upload, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, f as ChevronRight, g as Clock, M as MessageSquare, Z as Zap, h as LoaderCircle, W as Wrench, i as Globe, j as User, F as FileTerminal, k as Radio, l as Rows3, m as Columns2, n as Minus, o as Pencil, E as Equal, p as EyeOff, q as Eye, r as ExternalLink, s as RotateCw, T as Trash2, A as ArrowUp, t as ArrowDown, u as TriangleAlert, v as CircleCheckBig, w as CircleStop, x as CircleQuestionMark, y as Server, z as Gauge, B as Lock, H as Wifi, I as WifiOff, J as ChevronsUp, K as ChevronsDown, N as Brain, O as Terminal } from "../_libs/lucide-react.mjs";
13
+ import { D as Download, L as LayoutGrid, a as List, G as GitCompareArrows, X, S as Settings, C as ChevronDown, b as Check, R as RotateCcw, U as Upload, c as Scan, P as Plus, d as Copy, e as CircleAlert, f as ChevronUp, g as ChevronRight, h as Clock, M as MessageSquare, Z as Zap, i as LoaderCircle, W as Wrench, j as Globe, k as User, F as FileTerminal, l as Radio, m as Rows3, n as Columns2, o as Minus, p as Pencil, E as Equal, q as EyeOff, r as Eye, s as ExternalLink, t as RotateCw, T as Trash2, A as ArrowUp, u as ArrowDown, v as TriangleAlert, w as CircleCheckBig, x as CircleStop, y as CircleQuestionMark, z as Server, B as Gauge, H as Lock, I as Wifi, J as WifiOff, K as ChevronsUp, N as ChevronsDown, O as Brain, Q as Terminal } from "../_libs/lucide-react.mjs";
14
14
  import { M as Markdown } from "../_libs/react-markdown.mjs";
15
- import { a as array, b as string, u as union, d as object, l as literal, n as number, c as boolean } from "../_libs/zod.mjs";
15
+ import { a as array, b as string, u as union, d as object, l as literal, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
16
16
  import { R as Root2$1, L as List$1, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
17
17
  import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
18
18
  import { P as Provider, R as Root3, T as Trigger$3, a as Portal$2, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
@@ -276,7 +276,7 @@ async function exportLogsAsZip(logs) {
276
276
  document.body.removeChild(anchor);
277
277
  URL.revokeObjectURL(url);
278
278
  }
279
- const version = "1.14.2";
279
+ const version = "1.14.4";
280
280
  const packageJson = {
281
281
  version
282
282
  };
@@ -2861,6 +2861,210 @@ function SelectScrollDownButton({
2861
2861
  }
2862
2862
  );
2863
2863
  }
2864
+ const ExternalProviderSchema = object({
2865
+ name: string(),
2866
+ apiKey: string(),
2867
+ format: _enum(["anthropic", "openai"]),
2868
+ anthropicBaseUrl: string(),
2869
+ openaiBaseUrl: string(),
2870
+ models: array(string()),
2871
+ sourceTool: _enum(["claude-code", "opencode"]),
2872
+ alreadyExists: boolean()
2873
+ });
2874
+ const ScanResponseSchema = object({
2875
+ providers: array(ExternalProviderSchema),
2876
+ warnings: array(string()).optional()
2877
+ });
2878
+ const ImportResponseSchema$1 = object({
2879
+ success: boolean().optional(),
2880
+ imported: number().optional(),
2881
+ message: string().optional(),
2882
+ errors: array(string()).optional()
2883
+ });
2884
+ function ImportWizardDialog({
2885
+ open,
2886
+ onOpenChange,
2887
+ onImportComplete
2888
+ }) {
2889
+ const [scanning, setScanning] = reactExports.useState(false);
2890
+ const [scanError, setScanError] = reactExports.useState(null);
2891
+ const [providers, setProviders] = reactExports.useState([]);
2892
+ const [warnings, setWarnings] = reactExports.useState([]);
2893
+ const [selected, setSelected] = reactExports.useState(/* @__PURE__ */ new Set());
2894
+ const [importing, setImporting] = reactExports.useState(false);
2895
+ const [importResult, setImportResult] = reactExports.useState(null);
2896
+ const [importError, setImportError] = reactExports.useState(null);
2897
+ const scan = reactExports.useCallback(() => {
2898
+ setScanning(true);
2899
+ setScanError(null);
2900
+ setWarnings([]);
2901
+ setProviders([]);
2902
+ setSelected(/* @__PURE__ */ new Set());
2903
+ setImportResult(null);
2904
+ setImportError(null);
2905
+ fetch("/api/providers/scan").then((res) => {
2906
+ if (!res.ok) {
2907
+ return res.text().then((text) => {
2908
+ setScanError(`Scan failed (${res.status}): ${text}`);
2909
+ });
2910
+ }
2911
+ return res.json().then((data) => {
2912
+ const parsed = ScanResponseSchema.safeParse(data);
2913
+ if (!parsed.success) {
2914
+ setScanError(`Invalid response: ${parsed.error.message}`);
2915
+ return;
2916
+ }
2917
+ setProviders(parsed.data.providers);
2918
+ setWarnings(parsed.data.warnings ?? []);
2919
+ const indices = /* @__PURE__ */ new Set();
2920
+ parsed.data.providers.forEach((p, i) => {
2921
+ if (!p.alreadyExists) indices.add(i);
2922
+ });
2923
+ setSelected(indices);
2924
+ });
2925
+ }).catch((err) => {
2926
+ setScanError(err instanceof Error ? err.message : String(err));
2927
+ }).finally(() => {
2928
+ setScanning(false);
2929
+ });
2930
+ }, []);
2931
+ reactExports.useEffect(() => {
2932
+ if (open) {
2933
+ scan();
2934
+ }
2935
+ }, [open, scan]);
2936
+ const toggleProvider = reactExports.useCallback((index) => {
2937
+ setSelected((prev) => {
2938
+ const next = new Set(prev);
2939
+ if (next.has(index)) {
2940
+ next.delete(index);
2941
+ } else {
2942
+ next.add(index);
2943
+ }
2944
+ return next;
2945
+ });
2946
+ }, []);
2947
+ const importSelected = reactExports.useCallback(() => {
2948
+ const toImport = providers.filter((_, i) => selected.has(i));
2949
+ if (toImport.length === 0) return;
2950
+ setImporting(true);
2951
+ setImportError(null);
2952
+ setImportResult(null);
2953
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2954
+ const providersPayload = toImport.map((p) => ({
2955
+ id: window.crypto.randomUUID(),
2956
+ name: p.name,
2957
+ apiKey: p.apiKey,
2958
+ format: p.format,
2959
+ anthropicBaseUrl: p.anthropicBaseUrl,
2960
+ openaiBaseUrl: p.openaiBaseUrl,
2961
+ models: p.models,
2962
+ createdAt: now,
2963
+ updatedAt: now
2964
+ }));
2965
+ fetch("/api/providers/import", {
2966
+ method: "POST",
2967
+ headers: { "Content-Type": "application/json" },
2968
+ body: JSON.stringify({ providers: providersPayload })
2969
+ }).then((res) => res.json()).then((data) => {
2970
+ const parsed = ImportResponseSchema$1.safeParse(data);
2971
+ if (!parsed.success) {
2972
+ setImportError(`Invalid response: ${parsed.error.message}`);
2973
+ return;
2974
+ }
2975
+ const result = parsed.data;
2976
+ if (result.errors !== void 0 && result.errors.length > 0 && result.message !== void 0) {
2977
+ setImportResult(result.message);
2978
+ } else {
2979
+ setImportResult(`Imported ${result.imported ?? toImport.length} provider(s)`);
2980
+ }
2981
+ onImportComplete();
2982
+ }).catch((err) => {
2983
+ setImportError(err instanceof Error ? err.message : String(err));
2984
+ }).finally(() => {
2985
+ setImporting(false);
2986
+ });
2987
+ }, [providers, selected, onImportComplete]);
2988
+ const hasSelectable = providers.some((p) => !p.alreadyExists);
2989
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-w-xl max-h-[80vh] overflow-hidden flex flex-col", children: [
2990
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogHeader, { children: [
2991
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: "Import from External Tools" }),
2992
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Detect provider configurations from Claude Code and OpenCode." })
2993
+ ] }),
2994
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto space-y-3", children: [
2995
+ scanning && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center py-8 gap-2 text-muted-foreground", children: [
2996
+ /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-4 animate-spin" }),
2997
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm", children: "Scanning for external providers..." })
2998
+ ] }),
2999
+ scanError !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-destructive text-sm py-4", children: [
3000
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "size-4" }),
3001
+ scanError
3002
+ ] }),
3003
+ !scanning && scanError === null && providers.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm text-muted-foreground py-8 text-center", children: [
3004
+ "No external provider configurations found.",
3005
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
3006
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "Supported tools: Claude Code (~/.claude/settings.json), OpenCode (~/.config/opencode/opencode.json)" })
3007
+ ] }),
3008
+ !scanning && providers.map((p, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3009
+ "label",
3010
+ {
3011
+ className: "flex items-start gap-3 p-3 border rounded-md cursor-pointer hover:bg-muted/50 has-[:disabled]:opacity-50 has-[:disabled]:cursor-not-allowed",
3012
+ children: [
3013
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3014
+ "input",
3015
+ {
3016
+ type: "checkbox",
3017
+ checked: selected.has(i),
3018
+ disabled: p.alreadyExists || importing,
3019
+ onChange: () => toggleProvider(i),
3020
+ className: "mt-0.5 size-4"
3021
+ }
3022
+ ),
3023
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
3024
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
3025
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium truncate", children: p.name }),
3026
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "secondary", className: "text-[10px] px-1.5 py-0", children: p.format }),
3027
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "text-[10px] px-1.5 py-0", children: p.sourceTool }),
3028
+ p.alreadyExists && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground", children: "Already added" })
3029
+ ] }),
3030
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground mt-1 truncate", children: [
3031
+ p.models.slice(0, 4).join(", "),
3032
+ p.models.length > 4 ? ` +${p.models.length - 4} more` : ""
3033
+ ] }),
3034
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground mt-0.5 truncate", children: p.format === "anthropic" ? p.anthropicBaseUrl : p.openaiBaseUrl }),
3035
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground mt-0.5 font-mono", children: p.apiKey.length > 8 ? `${p.apiKey.slice(0, 4)}••••${p.apiKey.slice(-4)}` : "••••" })
3036
+ ] })
3037
+ ]
3038
+ },
3039
+ `${p.sourceTool}-${p.name}`
3040
+ )),
3041
+ warnings.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground space-y-1 border-t pt-2", children: warnings.map((w, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
3042
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "size-3" }),
3043
+ w
3044
+ ] }, i)) }),
3045
+ importResult !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-green-500 border-t pt-2", children: importResult }),
3046
+ importError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-destructive border-t pt-2", children: importError })
3047
+ ] }),
3048
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pt-3 border-t", children: [
3049
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "outline", size: "sm", onClick: scan, disabled: scanning, children: [
3050
+ /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: `size-3 mr-1 ${scanning ? "animate-spin" : ""}` }),
3051
+ "Rescan"
3052
+ ] }),
3053
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3054
+ Button,
3055
+ {
3056
+ size: "sm",
3057
+ onClick: importSelected,
3058
+ disabled: !hasSelectable || selected.size === 0 || importing,
3059
+ children: [
3060
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3 mr-1" }),
3061
+ importing ? "Importing..." : `Import Selected (${selected.size})`
3062
+ ]
3063
+ }
3064
+ )
3065
+ ] })
3066
+ ] }) });
3067
+ }
2864
3068
  function maskApiKey(apiKey) {
2865
3069
  if (apiKey.length <= 8) return "••••••••";
2866
3070
  return apiKey.slice(0, 4) + "••••••••" + apiKey.slice(-4);
@@ -3162,6 +3366,7 @@ function ProviderCard({
3162
3366
  size: "sm",
3163
3367
  onClick: () => onEdit(provider),
3164
3368
  className: "text-xs h-7 gap-1",
3369
+ disabled: isTesting ?? false,
3165
3370
  children: [
3166
3371
  /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
3167
3372
  "Edit"
@@ -3175,6 +3380,7 @@ function ProviderCard({
3175
3380
  size: "sm",
3176
3381
  onClick: () => onDelete(provider.id),
3177
3382
  className: "text-xs h-7 gap-1 text-destructive hover:text-destructive",
3383
+ disabled: isTesting ?? false,
3178
3384
  children: [
3179
3385
  /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3" }),
3180
3386
  "Delete"
@@ -3213,7 +3419,7 @@ const MINIMAX_MODELS = [
3213
3419
  ];
3214
3420
  const ALIBABA_MODELS = ["glm-5", "glm-5.1", "qwen3.6-plus", "qwen3.7-max"];
3215
3421
  function ProviderForm({ provider, onSubmit, onCancel }) {
3216
- const [name, setName] = reactExports.useState(provider?.name ?? "Provider Name");
3422
+ const [name, setName] = reactExports.useState(provider?.name ?? "");
3217
3423
  const [apiKey, setApiKey] = reactExports.useState(provider?.apiKey ?? "");
3218
3424
  const [showApiKey, setShowApiKey] = reactExports.useState(false);
3219
3425
  const [copied, setCopied] = reactExports.useState(false);
@@ -3228,6 +3434,21 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3228
3434
  const [source, setSource] = reactExports.useState(provider?.source);
3229
3435
  const [errors, setErrors] = reactExports.useState({});
3230
3436
  const [isSubmitting, setIsSubmitting] = reactExports.useState(false);
3437
+ const [openModelDropdown, setOpenModelDropdown] = reactExports.useState(null);
3438
+ const modelRowRefs = reactExports.useRef([]);
3439
+ reactExports.useEffect(() => {
3440
+ if (openModelDropdown === null) return;
3441
+ const index = openModelDropdown;
3442
+ function handleClick(e) {
3443
+ if (!(e.target instanceof Node)) return;
3444
+ const ref = modelRowRefs.current[index];
3445
+ if (ref !== null && ref !== void 0 && !ref.contains(e.target)) {
3446
+ setOpenModelDropdown(null);
3447
+ }
3448
+ }
3449
+ document.addEventListener("mousedown", handleClick);
3450
+ return () => document.removeEventListener("mousedown", handleClick);
3451
+ }, [openModelDropdown]);
3231
3452
  const [manualAnthropicUrlOverride, setManualAnthropicUrlOverride] = reactExports.useState(false);
3232
3453
  const [manualOpenaiUrlOverride, setManualOpenaiUrlOverride] = reactExports.useState(false);
3233
3454
  const isMiniMax = name.toLowerCase().includes("minimax");
@@ -3335,7 +3556,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3335
3556
  type: "text",
3336
3557
  value: name,
3337
3558
  onChange: (e) => setName(e.target.value),
3338
- placeholder: "Model Name",
3559
+ placeholder: "Provider Name",
3339
3560
  className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
3340
3561
  }
3341
3562
  ),
@@ -3410,54 +3631,93 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3410
3631
  "Models ",
3411
3632
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
3412
3633
  ] }),
3413
- (isMiniMax || isAlibaba) && /* @__PURE__ */ jsxRuntimeExports.jsx("datalist", { id: "model-suggestions", children: (isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: opt }, opt)) }),
3414
- models.map((m, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
3415
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3416
- "input",
3417
- {
3418
- type: "text",
3419
- value: m,
3420
- onChange: (e) => {
3421
- setModels((prev) => {
3422
- const next = [...prev];
3423
- next[i] = e.target.value;
3424
- return next;
3425
- });
3426
- },
3427
- placeholder: isMiniMax || isAlibaba ? "Type or select a model..." : "Model name",
3428
- list: isMiniMax || isAlibaba ? "model-suggestions" : void 0,
3429
- className: "flex-1 rounded-md border border-input bg-background px-4 py-3 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
3430
- }
3431
- ),
3432
- models.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
3433
- "button",
3434
- {
3435
- type: "button",
3436
- onClick: () => setModels((prev) => prev.filter((_, idx) => idx !== i)),
3437
- className: "text-muted-foreground hover:text-destructive transition-colors p-1 shrink-0",
3438
- "aria-label": "Remove model",
3439
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
3440
- "svg",
3634
+ models.map((m, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3635
+ "div",
3636
+ {
3637
+ ref: (el) => {
3638
+ modelRowRefs.current[i] = el;
3639
+ },
3640
+ className: "flex items-center gap-2",
3641
+ children: [
3642
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [
3643
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3644
+ "input",
3645
+ {
3646
+ type: "text",
3647
+ value: m,
3648
+ onChange: (e) => {
3649
+ setModels((prev) => {
3650
+ const next = [...prev];
3651
+ next[i] = e.target.value;
3652
+ return next;
3653
+ });
3654
+ },
3655
+ placeholder: isMiniMax || isAlibaba ? "Type or select a model..." : "Model name",
3656
+ className: "w-full rounded-md border border-input bg-background px-4 py-3 pr-8 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
3657
+ }
3658
+ ),
3659
+ (isMiniMax || isAlibaba) && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3660
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3661
+ "button",
3662
+ {
3663
+ type: "button",
3664
+ onClick: () => setOpenModelDropdown(openModelDropdown === i ? null : i),
3665
+ className: "absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors p-1 z-10",
3666
+ "aria-label": "Show model suggestions",
3667
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" })
3668
+ }
3669
+ ),
3670
+ openModelDropdown === i && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-0 right-0 top-full mt-1 z-50 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto", children: (isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3671
+ "button",
3672
+ {
3673
+ type: "button",
3674
+ onClick: () => {
3675
+ setModels((prev) => {
3676
+ const next = [...prev];
3677
+ next[i] = opt;
3678
+ return next;
3679
+ });
3680
+ setOpenModelDropdown(null);
3681
+ },
3682
+ className: "w-full text-left px-3 py-2 text-sm hover:bg-muted transition-colors",
3683
+ children: opt
3684
+ },
3685
+ opt
3686
+ )) })
3687
+ ] })
3688
+ ] }),
3689
+ models.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
3690
+ "button",
3441
3691
  {
3442
- xmlns: "http://www.w3.org/2000/svg",
3443
- width: "16",
3444
- height: "16",
3445
- viewBox: "0 0 24 24",
3446
- fill: "none",
3447
- stroke: "currentColor",
3448
- strokeWidth: "2",
3449
- strokeLinecap: "round",
3450
- strokeLinejoin: "round",
3451
- children: [
3452
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 6h18" }),
3453
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
3454
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
3455
- ]
3692
+ type: "button",
3693
+ onClick: () => setModels((prev) => prev.filter((_, idx) => idx !== i)),
3694
+ className: "text-muted-foreground hover:text-destructive transition-colors p-1 shrink-0",
3695
+ "aria-label": "Remove model",
3696
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
3697
+ "svg",
3698
+ {
3699
+ xmlns: "http://www.w3.org/2000/svg",
3700
+ width: "16",
3701
+ height: "16",
3702
+ viewBox: "0 0 24 24",
3703
+ fill: "none",
3704
+ stroke: "currentColor",
3705
+ strokeWidth: "2",
3706
+ strokeLinecap: "round",
3707
+ strokeLinejoin: "round",
3708
+ children: [
3709
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 6h18" }),
3710
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
3711
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
3712
+ ]
3713
+ }
3714
+ )
3456
3715
  }
3457
3716
  )
3458
- }
3459
- )
3460
- ] }, i)),
3717
+ ]
3718
+ },
3719
+ i
3720
+ )),
3461
3721
  /* @__PURE__ */ jsxRuntimeExports.jsx(
3462
3722
  "button",
3463
3723
  {
@@ -3595,11 +3855,21 @@ function ProvidersPanel({
3595
3855
  const [configPath, setConfigPath] = reactExports.useState(null);
3596
3856
  const [configPathCopied, setConfigPathCopied] = reactExports.useState(false);
3597
3857
  const [highlightedProviderId, setHighlightedProviderId] = reactExports.useState(null);
3858
+ const [showImportWizard, setShowImportWizard] = reactExports.useState(false);
3598
3859
  const [sourceFilter, setSourceFilter] = reactExports.useState("all");
3599
3860
  const listScrollRef = reactExports.useRef(null);
3600
3861
  const highlightTimeoutRef = reactExports.useRef(null);
3601
3862
  const providers = externalProviders ?? [];
3602
- const filteredProviders = sourceFilter === "all" ? providers : providers.filter((p) => p.source === sourceFilter);
3863
+ const filteredProviders = reactExports.useMemo(() => {
3864
+ const filtered = sourceFilter === "all" ? providers : providers.filter((p) => p.source === sourceFilter);
3865
+ if (sourceFilter === "all") {
3866
+ return [...filtered].sort((a, b) => {
3867
+ const order = { personal: 0, company: 1 };
3868
+ return (order[a.source ?? ""] ?? 2) - (order[b.source ?? ""] ?? 2);
3869
+ });
3870
+ }
3871
+ return filtered;
3872
+ }, [providers, sourceFilter]);
3603
3873
  const testResults = externalTestResults ?? internalTestResults;
3604
3874
  const testingProviders = externalTestingProviders ?? internalTestingProviders;
3605
3875
  const testingTimeLeft = externalTestingTimeLeft ?? internalTestingTimeLeft;
@@ -3850,7 +4120,7 @@ function ProvidersPanel({
3850
4120
  }
3851
4121
  if (showForm || editingProvider) {
3852
4122
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
3853
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium", children: editingProvider ? "Edit Provider" : "Add New Provider" }) }),
4123
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between sticky top-0 bg-background z-10 pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium", children: editingProvider ? "Edit Provider" : "Add New Provider" }) }),
3854
4124
  /* @__PURE__ */ jsxRuntimeExports.jsx(
3855
4125
  ProviderForm,
3856
4126
  {
@@ -3904,6 +4174,19 @@ function ProvidersPanel({
3904
4174
  style: { display: "none" }
3905
4175
  }
3906
4176
  ),
4177
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
4178
+ Button,
4179
+ {
4180
+ variant: "outline",
4181
+ size: "sm",
4182
+ onClick: () => setShowImportWizard(true),
4183
+ className: "gap-1",
4184
+ children: [
4185
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Scan, { className: "size-3" }),
4186
+ "Scan"
4187
+ ]
4188
+ }
4189
+ ),
3907
4190
  /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { onClick: () => setShowForm(true), size: "sm", className: "gap-1", children: [
3908
4191
  /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4" }),
3909
4192
  "Add Provider"
@@ -3966,7 +4249,19 @@ function ProvidersPanel({
3966
4249
  },
3967
4250
  provider.id
3968
4251
  )) })
3969
- ] })
4252
+ ] }),
4253
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
4254
+ ImportWizardDialog,
4255
+ {
4256
+ open: showImportWizard,
4257
+ onOpenChange: setShowImportWizard,
4258
+ onImportComplete: () => {
4259
+ if (onProvidersMutate !== void 0) {
4260
+ void onProvidersMutate();
4261
+ }
4262
+ }
4263
+ }
4264
+ )
3970
4265
  ] });
3971
4266
  }
3972
4267
  async function fetcher(url) {
@@ -4042,7 +4337,7 @@ function SettingsDialog() {
4042
4337
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "providers", children: "Providers" }),
4043
4338
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "proxy", children: "Proxy" })
4044
4339
  ] }),
4045
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 overflow-y-auto flex-1", children: [
4340
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 overflow-y-auto flex-1 pr-3", children: [
4046
4341
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "providers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4047
4342
  ProvidersPanel,
4048
4343
  {
@@ -198,7 +198,7 @@ function getResponse() {
198
198
  return event.res;
199
199
  }
200
200
  async function getStartManifest(matchedRoutes) {
201
- const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-B6idtbmL.mjs");
201
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-Oekf1osO.mjs");
202
202
  const startManifest = tsrStartManifest();
203
203
  const rootRoute = startManifest.routes[rootRouteId] = startManifest.routes[rootRouteId] || {};
204
204
  rootRoute.assets = rootRoute.assets || [];
@@ -767,7 +767,7 @@ let entriesPromise;
767
767
  let baseManifestPromise;
768
768
  let cachedFinalManifestPromise;
769
769
  async function loadEntries() {
770
- const routerEntry = await import("./router-BoeSXWHG.mjs").then((n) => n.r);
770
+ const routerEntry = await import("./router-Bl3OCdGC.mjs").then((n) => n.r);
771
771
  const startEntry = await import("./start-HYkvq4Ni.mjs");
772
772
  return { startEntry, routerEntry };
773
773
  }