@tonyclaw/agent-inspector 3.0.9 → 3.0.11
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.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-CJKL4LMm.js → CompareDrawer-gb4FHIDP.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-yqasBIbY.js +106 -0
- package/.output/public/assets/{ReplayDialog-BYanl-a_.js → ReplayDialog-uXfMIorn.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-DvPz1jqE.js → RequestAnatomy-NqGFn7lk.js} +1 -1
- package/.output/public/assets/{ResponseView-sZnxp9GN.js → ResponseView-BWSWdjPl.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-G-tiYiBd.js → StreamingChunkSequence-qeCLglzr.js} +1 -1
- package/.output/public/assets/{_sessionId-_hDcsmXq.js → _sessionId-COT-xg31.js} +1 -1
- package/.output/public/assets/index-BIgsQvaF.css +1 -0
- package/.output/public/assets/{index-SuOSQb2_.js → index-CKVD6xzC.js} +1 -1
- package/.output/public/assets/index-swjqFLcG.js +1 -0
- package/.output/public/assets/{json-viewer-yQdhgjaw.js → json-viewer-ux5usy0f.js} +1 -1
- package/.output/public/assets/{main-CgjcDxjR.js → main-BM5Sp2nO.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +2 -2
- package/.output/server/{_sessionId-BGISf2En.mjs → _sessionId-8D0eTdRJ.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-s-UF9gW3.mjs → CompareDrawer-Hcs0aQWl.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-C0Sbz9ED.mjs → ProxyViewerContainer-D2CkbvcS.mjs} +185 -246
- package/.output/server/_ssr/{ReplayDialog-DvS8gvJr.mjs → ReplayDialog-O2R9y9xD.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-BwhWglJL.mjs → RequestAnatomy-DkvrjK-3.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-wBVreqQf.mjs → ResponseView-CPcMs3Um.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-Dr6eg-MJ.mjs → StreamingChunkSequence-FlBdqDP3.mjs} +2 -2
- package/.output/server/_ssr/{index-B98Wia1X.mjs → index-ChG0L62e.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-D7L8y0FH.mjs → json-viewer-Bb1g2nec.mjs} +2 -2
- package/.output/server/_ssr/{router-g9mo0nWT.mjs → router-840N-dk9.mjs} +21 -12
- package/.output/server/{_tanstack-start-manifest_v-CbGc-o50.mjs → _tanstack-start-manifest_v-Qi0D1Tl0.mjs} +1 -1
- package/.output/server/index.mjs +70 -70
- package/package.json +1 -1
- package/src/components/ProxyViewerContainer.tsx +10 -2
- package/src/components/providers/ProviderCard.tsx +98 -136
- package/src/components/providers/ProvidersPanel.tsx +61 -106
- package/src/components/providers/SettingsDialog.tsx +17 -22
- package/src/components/proxy-viewer/LogEntryHeader.tsx +17 -43
- package/src/components/ui/tooltip.tsx +1 -1
- package/src/routes/api/logs.ts +14 -5
- package/.output/public/assets/ProxyViewerContainer-DBUy29O-.js +0 -106
- package/.output/public/assets/index-DAPAWsAd.js +0 -1
- package/.output/public/assets/index-DspnLqUW.css +0 -1
|
@@ -31,13 +31,6 @@ import {
|
|
|
31
31
|
providerHasContextMetadata,
|
|
32
32
|
} from "../../lib/providerModelMetadata";
|
|
33
33
|
import { formatContextWindowTokens } from "../../lib/utils";
|
|
34
|
-
import {
|
|
35
|
-
PATH_V1_CHAT_COMPLETIONS,
|
|
36
|
-
PATH_V1_MESSAGES,
|
|
37
|
-
PATH_V1_RESPONSES,
|
|
38
|
-
previewUpstreamUrl,
|
|
39
|
-
type UpstreamUrlDiagnostic,
|
|
40
|
-
} from "../../lib/upstreamUrl";
|
|
41
34
|
import type {
|
|
42
35
|
ProviderFormatTestResults,
|
|
43
36
|
ProviderTestErrorType as ErrorType,
|
|
@@ -225,51 +218,16 @@ function ProviderFormatTestStatus({
|
|
|
225
218
|
);
|
|
226
219
|
}
|
|
227
220
|
|
|
228
|
-
function
|
|
229
|
-
diagnostic,
|
|
230
|
-
}: {
|
|
231
|
-
diagnostic: UpstreamUrlDiagnostic;
|
|
232
|
-
}): JSX.Element {
|
|
233
|
-
return (
|
|
234
|
-
<div className={diagnostic.severity === "warning" ? "text-amber-200" : "text-muted-foreground"}>
|
|
235
|
-
{diagnostic.message}
|
|
236
|
-
</div>
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function ProviderEndpointSummary({
|
|
221
|
+
function ProviderFormatStatusBlock({
|
|
241
222
|
label,
|
|
242
|
-
baseUrl,
|
|
243
|
-
endpoint,
|
|
244
|
-
providerName,
|
|
245
223
|
children,
|
|
246
224
|
}: {
|
|
247
225
|
label: string;
|
|
248
|
-
baseUrl: string;
|
|
249
|
-
endpoint: string;
|
|
250
|
-
providerName: string;
|
|
251
226
|
children?: ReactNode;
|
|
252
227
|
}): JSX.Element {
|
|
253
|
-
const preview = previewUpstreamUrl(baseUrl, endpoint, { providerName });
|
|
254
|
-
|
|
255
228
|
return (
|
|
256
|
-
<div className="
|
|
257
|
-
<div className="
|
|
258
|
-
<span className="font-medium text-muted-foreground">{label}</span>
|
|
259
|
-
<code className="min-w-0 break-all font-mono text-foreground/80">{baseUrl}</code>
|
|
260
|
-
<span className="text-[10px] uppercase text-muted-foreground/80">Final</span>
|
|
261
|
-
<code className="min-w-0 break-all font-mono text-foreground">{preview.finalUrl}</code>
|
|
262
|
-
</div>
|
|
263
|
-
{preview.diagnostics.length > 0 && (
|
|
264
|
-
<div className="space-y-1 text-xs">
|
|
265
|
-
{preview.diagnostics.map((diagnostic) => (
|
|
266
|
-
<EndpointDiagnosticLine
|
|
267
|
-
key={`${diagnostic.severity}:${diagnostic.message}`}
|
|
268
|
-
diagnostic={diagnostic}
|
|
269
|
-
/>
|
|
270
|
-
))}
|
|
271
|
-
</div>
|
|
272
|
-
)}
|
|
229
|
+
<div className="min-w-0 space-y-2 rounded-md border border-border/60 bg-muted/15 p-2">
|
|
230
|
+
<div className="text-xs font-medium text-foreground/80">{label}</div>
|
|
273
231
|
{children}
|
|
274
232
|
</div>
|
|
275
233
|
);
|
|
@@ -293,9 +251,38 @@ function providerTestTargets(provider: ProviderConfig): string[] {
|
|
|
293
251
|
return targets;
|
|
294
252
|
}
|
|
295
253
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
254
|
+
type CapabilityChipTone = "active" | "quiet" | "response";
|
|
255
|
+
|
|
256
|
+
function CapabilityChip({
|
|
257
|
+
label,
|
|
258
|
+
enabled,
|
|
259
|
+
tone,
|
|
260
|
+
}: {
|
|
261
|
+
label: string;
|
|
262
|
+
enabled: boolean;
|
|
263
|
+
tone?: CapabilityChipTone;
|
|
264
|
+
}): JSX.Element {
|
|
265
|
+
const resolvedTone: CapabilityChipTone = tone ?? (enabled ? "active" : "quiet");
|
|
266
|
+
const className =
|
|
267
|
+
resolvedTone === "response" && enabled
|
|
268
|
+
? "border-sky-300/30 bg-sky-300/[0.09] text-sky-100"
|
|
269
|
+
: enabled
|
|
270
|
+
? "border-border/70 bg-muted/25 text-foreground/80"
|
|
271
|
+
: "border-border/50 bg-transparent text-muted-foreground/70";
|
|
272
|
+
return (
|
|
273
|
+
<span className={`inline-flex items-center gap-1 rounded border px-1.5 py-0.5 ${className}`}>
|
|
274
|
+
{enabled ? <CheckCircle className="size-3" /> : <Minus className="size-3" />}
|
|
275
|
+
{label}
|
|
276
|
+
</span>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function ProviderCapabilitySummary({ provider }: { provider: ProviderConfig }): JSX.Element {
|
|
281
|
+
const hasAnthropic = providerHasEndpoint(provider.anthropicBaseUrl);
|
|
282
|
+
const hasChat = providerHasEndpoint(provider.openaiBaseUrl);
|
|
283
|
+
const hasResponses = providerHasEndpoint(provider.openaiResponsesBaseUrl);
|
|
284
|
+
|
|
285
|
+
if (!hasAnthropic && !hasChat && !hasResponses) {
|
|
299
286
|
return (
|
|
300
287
|
<div className="text-xs text-muted-foreground">
|
|
301
288
|
Test requires at least one configured endpoint.
|
|
@@ -305,16 +292,15 @@ function ProviderTestCoverage({ provider }: { provider: ProviderConfig }): JSX.E
|
|
|
305
292
|
|
|
306
293
|
return (
|
|
307
294
|
<div className="flex min-w-0 flex-wrap items-center gap-1.5 text-xs text-muted-foreground">
|
|
308
|
-
<span>
|
|
309
|
-
{
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
<span>non-stream + stream</span>
|
|
295
|
+
<span>Capabilities</span>
|
|
296
|
+
{hasAnthropic && <CapabilityChip label="Anthropic" enabled={true} />}
|
|
297
|
+
{hasChat && <CapabilityChip label="OpenAI Chat" enabled={true} />}
|
|
298
|
+
<CapabilityChip
|
|
299
|
+
label={hasResponses ? "OpenAI Responses" : "No OpenAI Responses"}
|
|
300
|
+
enabled={hasResponses}
|
|
301
|
+
tone="response"
|
|
302
|
+
/>
|
|
303
|
+
<span>tested non-stream + stream</span>
|
|
318
304
|
</div>
|
|
319
305
|
);
|
|
320
306
|
}
|
|
@@ -570,23 +556,14 @@ function TestStatus({ result }: { result: ProviderTestState }): JSX.Element {
|
|
|
570
556
|
displayTiming.push(timingParts[i]);
|
|
571
557
|
}
|
|
572
558
|
return (
|
|
573
|
-
<
|
|
574
|
-
<
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
)}
|
|
582
|
-
{timingParts.length > 0 && (
|
|
583
|
-
<span className="hidden text-muted-foreground lg:inline">({displayTiming})</span>
|
|
584
|
-
)}
|
|
585
|
-
</div>
|
|
586
|
-
</TooltipTrigger>
|
|
587
|
-
<TooltipContent>Connection test passed</TooltipContent>
|
|
588
|
-
</Tooltip>
|
|
589
|
-
</TooltipProvider>
|
|
559
|
+
<div className="flex shrink-0 items-center gap-1 text-xs text-cyan-100">
|
|
560
|
+
<CheckCircle className="size-3 text-cyan-300" />
|
|
561
|
+
<span>Connected</span>
|
|
562
|
+
{tokenParts.length > 0 && <span className="text-muted-foreground">({displayTokens})</span>}
|
|
563
|
+
{timingParts.length > 0 && (
|
|
564
|
+
<span className="hidden text-muted-foreground lg:inline">({displayTiming})</span>
|
|
565
|
+
)}
|
|
566
|
+
</div>
|
|
590
567
|
);
|
|
591
568
|
}
|
|
592
569
|
|
|
@@ -599,14 +576,7 @@ function TestStatus({ result }: { result: ProviderTestState }): JSX.Element {
|
|
|
599
576
|
<div className="flex flex-col gap-1 shrink-0">
|
|
600
577
|
<div className="flex items-center gap-1 text-xs text-red-600 max-w-[200px]">
|
|
601
578
|
{getErrorIcon(errorType)}
|
|
602
|
-
<
|
|
603
|
-
<Tooltip>
|
|
604
|
-
<TooltipTrigger asChild>
|
|
605
|
-
<span className="truncate">{errorMessage}</span>
|
|
606
|
-
</TooltipTrigger>
|
|
607
|
-
<TooltipContent>Connection test failed</TooltipContent>
|
|
608
|
-
</Tooltip>
|
|
609
|
-
</TooltipProvider>
|
|
579
|
+
<span className="truncate">{errorMessage}</span>
|
|
610
580
|
{hasDetails && (
|
|
611
581
|
<button
|
|
612
582
|
type="button"
|
|
@@ -703,27 +673,13 @@ export function ProviderCard({
|
|
|
703
673
|
const testedModelsLabel = modelResultsLabel(testResults);
|
|
704
674
|
const providerSourceBadge =
|
|
705
675
|
provider.source === "company" ? (
|
|
706
|
-
<
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
<span className="shrink-0 rounded border border-cyan-300/20 bg-cyan-300/[0.08] px-1.5 py-0.5 text-xs text-cyan-100">
|
|
710
|
-
Company
|
|
711
|
-
</span>
|
|
712
|
-
</TooltipTrigger>
|
|
713
|
-
<TooltipContent>Company-provided API key</TooltipContent>
|
|
714
|
-
</Tooltip>
|
|
715
|
-
</TooltipProvider>
|
|
676
|
+
<span className="shrink-0 rounded border border-cyan-300/20 bg-cyan-300/[0.08] px-1.5 py-0.5 text-xs text-cyan-100">
|
|
677
|
+
Company
|
|
678
|
+
</span>
|
|
716
679
|
) : provider.source === "personal" ? (
|
|
717
|
-
<
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
<span className="shrink-0 rounded border border-violet-300/20 bg-violet-300/[0.08] px-1.5 py-0.5 text-xs text-violet-100">
|
|
721
|
-
Personal
|
|
722
|
-
</span>
|
|
723
|
-
</TooltipTrigger>
|
|
724
|
-
<TooltipContent>Your personal API key</TooltipContent>
|
|
725
|
-
</Tooltip>
|
|
726
|
-
</TooltipProvider>
|
|
680
|
+
<span className="shrink-0 rounded border border-violet-300/20 bg-violet-300/[0.08] px-1.5 py-0.5 text-xs text-violet-100">
|
|
681
|
+
Personal
|
|
682
|
+
</span>
|
|
727
683
|
) : null;
|
|
728
684
|
|
|
729
685
|
return (
|
|
@@ -762,7 +718,7 @@ export function ProviderCard({
|
|
|
762
718
|
</div>
|
|
763
719
|
|
|
764
720
|
<div className="flex flex-col gap-2 text-xs text-muted-foreground sm:flex-row sm:items-center sm:justify-between">
|
|
765
|
-
<
|
|
721
|
+
<ProviderCapabilitySummary provider={provider} />
|
|
766
722
|
<div className="flex flex-wrap items-center gap-2">
|
|
767
723
|
<span>Key configured</span>
|
|
768
724
|
<span>{provider.authHeader === "x-api-key" ? "x-api-key" : "Bearer"}</span>
|
|
@@ -838,43 +794,49 @@ export function ProviderCard({
|
|
|
838
794
|
</div>
|
|
839
795
|
)}
|
|
840
796
|
|
|
841
|
-
{providerHasEndpoint(provider.anthropicBaseUrl)
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
797
|
+
{(providerHasEndpoint(provider.anthropicBaseUrl) ||
|
|
798
|
+
providerHasEndpoint(provider.openaiBaseUrl) ||
|
|
799
|
+
providerHasEndpoint(provider.openaiResponsesBaseUrl)) && (
|
|
800
|
+
<div className="grid gap-2 lg:grid-cols-3">
|
|
801
|
+
{providerHasEndpoint(provider.anthropicBaseUrl) && (
|
|
802
|
+
<ProviderFormatStatusBlock label="Anthropic">
|
|
803
|
+
{testResults !== undefined && (
|
|
804
|
+
<ProviderFormatTestStatus results={testResults.anthropic} />
|
|
805
|
+
)}
|
|
806
|
+
{testResults === undefined && (
|
|
807
|
+
<div className="text-xs text-muted-foreground">Not tested yet</div>
|
|
808
|
+
)}
|
|
809
|
+
</ProviderFormatStatusBlock>
|
|
850
810
|
)}
|
|
851
|
-
</ProviderEndpointSummary>
|
|
852
|
-
)}
|
|
853
811
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
812
|
+
{providerHasEndpoint(provider.openaiBaseUrl) && (
|
|
813
|
+
<ProviderFormatStatusBlock label="OpenAI Chat">
|
|
814
|
+
{testResults !== undefined && (
|
|
815
|
+
<ProviderFormatTestStatus results={testResults.openaiChat} />
|
|
816
|
+
)}
|
|
817
|
+
{testResults === undefined && (
|
|
818
|
+
<div className="text-xs text-muted-foreground">Not tested yet</div>
|
|
819
|
+
)}
|
|
820
|
+
</ProviderFormatStatusBlock>
|
|
863
821
|
)}
|
|
864
|
-
</ProviderEndpointSummary>
|
|
865
|
-
)}
|
|
866
822
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
823
|
+
{providerHasEndpoint(provider.openaiResponsesBaseUrl) && (
|
|
824
|
+
<ProviderFormatStatusBlock label="OpenAI Responses">
|
|
825
|
+
{testResults !== undefined && (
|
|
826
|
+
<ProviderFormatTestStatus results={testResults.openaiResponses} />
|
|
827
|
+
)}
|
|
828
|
+
{testResults === undefined && (
|
|
829
|
+
<div className="text-xs text-muted-foreground">Not tested yet</div>
|
|
830
|
+
)}
|
|
831
|
+
</ProviderFormatStatusBlock>
|
|
832
|
+
)}
|
|
833
|
+
|
|
834
|
+
{!providerHasEndpoint(provider.openaiResponsesBaseUrl) && (
|
|
835
|
+
<div className="min-w-0 rounded-md border border-border/50 bg-transparent p-2 text-xs text-muted-foreground/75">
|
|
836
|
+
OpenAI Responses not configured
|
|
837
|
+
</div>
|
|
876
838
|
)}
|
|
877
|
-
</
|
|
839
|
+
</div>
|
|
878
840
|
)}
|
|
879
841
|
|
|
880
842
|
{testResults?.models !== undefined && Object.keys(testResults.models).length > 0 && (
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type JSX, useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { Button } from "../ui/button";
|
|
4
|
-
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
|
|
5
4
|
import { Plus, AlertCircle, Copy, Check, Download, Upload, Scan } from "lucide-react";
|
|
6
5
|
import { ImportWizardDialog } from "./ImportWizardDialog";
|
|
7
6
|
import { ConfirmDialog } from "../ui/confirm-dialog";
|
|
@@ -535,7 +534,7 @@ export function ProvidersPanel({
|
|
|
535
534
|
if (showForm || editingProvider) {
|
|
536
535
|
return (
|
|
537
536
|
<div className="space-y-4">
|
|
538
|
-
<div className="
|
|
537
|
+
<div className="sticky top-0 z-10 flex items-center justify-between gap-3 rounded-[8px] bg-background/95 px-1 py-1 backdrop-blur">
|
|
539
538
|
<h3 className="text-lg font-medium">
|
|
540
539
|
{editingProvider ? "Edit Provider" : "Add New Provider"}
|
|
541
540
|
</h3>
|
|
@@ -554,41 +553,27 @@ export function ProvidersPanel({
|
|
|
554
553
|
|
|
555
554
|
return (
|
|
556
555
|
<div className="space-y-4">
|
|
557
|
-
<div className="
|
|
556
|
+
<div className="sticky top-0 z-10 flex items-center justify-between gap-3 rounded-[8px] bg-background/95 px-1 py-1 backdrop-blur">
|
|
558
557
|
<h3 className="text-lg font-medium">Providers</h3>
|
|
559
558
|
<div className="flex items-center gap-2">
|
|
560
|
-
<
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
<TooltipTrigger asChild>
|
|
579
|
-
<Button
|
|
580
|
-
variant="outline"
|
|
581
|
-
size="sm"
|
|
582
|
-
onClick={handleImportClick}
|
|
583
|
-
className="gap-1 hover:bg-muted"
|
|
584
|
-
>
|
|
585
|
-
<Upload className="size-3" />
|
|
586
|
-
Import
|
|
587
|
-
</Button>
|
|
588
|
-
</TooltipTrigger>
|
|
589
|
-
<TooltipContent>Import providers from an exported JSON file</TooltipContent>
|
|
590
|
-
</Tooltip>
|
|
591
|
-
</TooltipProvider>
|
|
559
|
+
<Button
|
|
560
|
+
variant="outline"
|
|
561
|
+
size="sm"
|
|
562
|
+
onClick={() => handleExport(false)}
|
|
563
|
+
className="gap-1 hover:bg-muted"
|
|
564
|
+
>
|
|
565
|
+
<Download className="size-3" />
|
|
566
|
+
Export
|
|
567
|
+
</Button>
|
|
568
|
+
<Button
|
|
569
|
+
variant="outline"
|
|
570
|
+
size="sm"
|
|
571
|
+
onClick={handleImportClick}
|
|
572
|
+
className="gap-1 hover:bg-muted"
|
|
573
|
+
>
|
|
574
|
+
<Upload className="size-3" />
|
|
575
|
+
Import
|
|
576
|
+
</Button>
|
|
592
577
|
<input
|
|
593
578
|
type="file"
|
|
594
579
|
ref={fileInputRef}
|
|
@@ -596,24 +581,15 @@ export function ProvidersPanel({
|
|
|
596
581
|
onChange={handleFileChange}
|
|
597
582
|
style={{ display: "none" }}
|
|
598
583
|
/>
|
|
599
|
-
<
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
<Scan className="size-3" />
|
|
609
|
-
Scan
|
|
610
|
-
</Button>
|
|
611
|
-
</TooltipTrigger>
|
|
612
|
-
<TooltipContent>
|
|
613
|
-
Detect and import provider configs from Claude Code and OpenCode
|
|
614
|
-
</TooltipContent>
|
|
615
|
-
</Tooltip>
|
|
616
|
-
</TooltipProvider>
|
|
584
|
+
<Button
|
|
585
|
+
variant="outline"
|
|
586
|
+
size="sm"
|
|
587
|
+
onClick={() => setShowImportWizard(true)}
|
|
588
|
+
className="gap-1"
|
|
589
|
+
>
|
|
590
|
+
<Scan className="size-3" />
|
|
591
|
+
Scan
|
|
592
|
+
</Button>
|
|
617
593
|
<Button onClick={() => setShowForm(true)} size="sm" className="gap-1">
|
|
618
594
|
<Plus className="size-4" />
|
|
619
595
|
Add Provider
|
|
@@ -622,38 +598,29 @@ export function ProvidersPanel({
|
|
|
622
598
|
</div>
|
|
623
599
|
|
|
624
600
|
{configPath !== null && (
|
|
625
|
-
<div className="
|
|
601
|
+
<div className="flex items-center gap-2 rounded-[8px] bg-muted/15 px-3 py-2 text-xs text-muted-foreground">
|
|
626
602
|
<span className="shrink-0">Config:</span>
|
|
627
603
|
<span className="font-mono truncate" title={configPath}>
|
|
628
604
|
{configPath}
|
|
629
605
|
</span>
|
|
630
|
-
<
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
<Check className="size-3 text-cyan-300" />
|
|
649
|
-
) : (
|
|
650
|
-
<Copy className="size-3" />
|
|
651
|
-
)}
|
|
652
|
-
</button>
|
|
653
|
-
</TooltipTrigger>
|
|
654
|
-
<TooltipContent>Copy config file path to clipboard</TooltipContent>
|
|
655
|
-
</Tooltip>
|
|
656
|
-
</TooltipProvider>
|
|
606
|
+
<button
|
|
607
|
+
type="button"
|
|
608
|
+
onClick={() => {
|
|
609
|
+
void copyTextToClipboard(configPath).then((success) => {
|
|
610
|
+
if (!success) return;
|
|
611
|
+
setConfigPathCopied(true);
|
|
612
|
+
setTimeout(() => setConfigPathCopied(false), 2000);
|
|
613
|
+
});
|
|
614
|
+
}}
|
|
615
|
+
className="ml-auto shrink-0 text-muted-foreground transition-colors hover:text-foreground"
|
|
616
|
+
aria-label={configPathCopied ? "Copied config file path" : "Copy config file path"}
|
|
617
|
+
>
|
|
618
|
+
{configPathCopied ? (
|
|
619
|
+
<Check className="size-3 text-cyan-300" />
|
|
620
|
+
) : (
|
|
621
|
+
<Copy className="size-3" />
|
|
622
|
+
)}
|
|
623
|
+
</button>
|
|
657
624
|
</div>
|
|
658
625
|
)}
|
|
659
626
|
|
|
@@ -674,32 +641,20 @@ export function ProvidersPanel({
|
|
|
674
641
|
</div>
|
|
675
642
|
) : (
|
|
676
643
|
<div className="space-y-3">
|
|
677
|
-
<div className="
|
|
644
|
+
<div className="inline-flex w-fit gap-1 rounded-[8px] bg-muted/20 p-1">
|
|
678
645
|
{(["all", "personal", "company"] as const).map((tab) => (
|
|
679
|
-
<
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
{tab === "all" ? "All" : tab === "personal" ? "Personal" : "Company"}
|
|
692
|
-
</button>
|
|
693
|
-
</TooltipTrigger>
|
|
694
|
-
<TooltipContent>
|
|
695
|
-
{tab === "all"
|
|
696
|
-
? "Show all providers"
|
|
697
|
-
: tab === "personal"
|
|
698
|
-
? "Providers you configured yourself"
|
|
699
|
-
: "Providers set by your organization"}
|
|
700
|
-
</TooltipContent>
|
|
701
|
-
</Tooltip>
|
|
702
|
-
</TooltipProvider>
|
|
646
|
+
<button
|
|
647
|
+
key={tab}
|
|
648
|
+
type="button"
|
|
649
|
+
onClick={() => setSourceFilter(tab)}
|
|
650
|
+
className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${
|
|
651
|
+
sourceFilter === tab
|
|
652
|
+
? "bg-background text-foreground shadow-sm"
|
|
653
|
+
: "text-muted-foreground hover:text-foreground"
|
|
654
|
+
}`}
|
|
655
|
+
>
|
|
656
|
+
{tab === "all" ? "All" : tab === "personal" ? "Personal" : "Company"}
|
|
657
|
+
</button>
|
|
703
658
|
))}
|
|
704
659
|
</div>
|
|
705
660
|
<div ref={listScrollRef} className="space-y-3">
|