@tonyclaw/llm-inspector 1.14.3 → 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-Cz7UcQ5N.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.3";
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"
@@ -3649,6 +3855,7 @@ function ProvidersPanel({
3649
3855
  const [configPath, setConfigPath] = reactExports.useState(null);
3650
3856
  const [configPathCopied, setConfigPathCopied] = reactExports.useState(false);
3651
3857
  const [highlightedProviderId, setHighlightedProviderId] = reactExports.useState(null);
3858
+ const [showImportWizard, setShowImportWizard] = reactExports.useState(false);
3652
3859
  const [sourceFilter, setSourceFilter] = reactExports.useState("all");
3653
3860
  const listScrollRef = reactExports.useRef(null);
3654
3861
  const highlightTimeoutRef = reactExports.useRef(null);
@@ -3967,6 +4174,19 @@ function ProvidersPanel({
3967
4174
  style: { display: "none" }
3968
4175
  }
3969
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
+ ),
3970
4190
  /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { onClick: () => setShowForm(true), size: "sm", className: "gap-1", children: [
3971
4191
  /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4" }),
3972
4192
  "Add Provider"
@@ -4029,7 +4249,19 @@ function ProvidersPanel({
4029
4249
  },
4030
4250
  provider.id
4031
4251
  )) })
4032
- ] })
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
+ )
4033
4265
  ] });
4034
4266
  }
4035
4267
  async function fetcher(url) {
@@ -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-DxqMZv-B.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-Cz7UcQ5N.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
  }