@tonyclaw/agent-inspector 3.0.7 → 3.0.9
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/cli.js +245 -171
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-C0Kbsm-Q.js → CompareDrawer-CJKL4LMm.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-DBUy29O-.js +106 -0
- package/.output/public/assets/ReplayDialog-BYanl-a_.js +1 -0
- package/.output/public/assets/{RequestAnatomy-fa0JXy5p.js → RequestAnatomy-DvPz1jqE.js} +1 -1
- package/.output/public/assets/ResponseView-sZnxp9GN.js +3 -0
- package/.output/public/assets/StreamingChunkSequence-G-tiYiBd.js +1 -0
- package/.output/public/assets/{_sessionId-D5fYH2C0.js → _sessionId-_hDcsmXq.js} +1 -1
- package/.output/public/assets/agent-inspector-58K1_MsC.ico +0 -0
- package/.output/public/assets/index-DAPAWsAd.js +1 -0
- package/.output/public/assets/index-DspnLqUW.css +1 -0
- package/.output/public/assets/{index-BEsqvywM.js → index-SuOSQb2_.js} +1 -1
- package/.output/public/assets/{json-viewer-B0QRbMSf.js → json-viewer-yQdhgjaw.js} +1 -1
- package/.output/public/assets/{main-CuKZD7n0.js → main-CgjcDxjR.js} +2 -2
- package/.output/server/_libs/radix-ui__react-dialog.mjs +2 -2
- package/.output/server/{_sessionId-DdBTK3qm.mjs → _sessionId-BGISf2En.mjs} +3 -3
- package/.output/server/_ssr/{CompareDrawer-m82cVZ73.mjs → CompareDrawer-s-UF9gW3.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-7geuf78f.mjs → ProxyViewerContainer-C0Sbz9ED.mjs} +1033 -530
- package/.output/server/_ssr/{ReplayDialog-B8RlWLLH.mjs → ReplayDialog-DvS8gvJr.mjs} +58 -23
- package/.output/server/_ssr/{RequestAnatomy-BE7pmwpv.mjs → RequestAnatomy-BwhWglJL.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-D-sDsdQr.mjs → ResponseView-wBVreqQf.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-DU_hThJr.mjs → StreamingChunkSequence-Dr6eg-MJ.mjs} +30 -20
- package/.output/server/_ssr/{index-D0sV8UbE.mjs → index-B98Wia1X.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-CaDh1zrw.mjs → json-viewer-D7L8y0FH.mjs} +2 -2
- package/.output/server/_ssr/{router-Zh2iZ_u0.mjs → router-g9mo0nWT.mjs} +459 -196
- package/.output/server/_tanstack-start-manifest_v-CbGc-o50.mjs +4 -0
- package/.output/server/index.mjs +76 -69
- package/package.json +1 -1
- package/src/assets/IDE/opencode.svg +1 -1
- package/src/assets/IDE/xiaomimimo.svg +1 -1
- package/src/cli/detect-tools.ts +2 -27
- package/src/cli/templates/codex-skill-onboard.ts +1 -1
- package/src/cli/templates/skill-onboard.ts +2 -3
- package/src/cli.ts +9 -86
- package/src/components/ProxyViewer.tsx +249 -37
- package/src/components/ProxyViewerContainer.tsx +167 -22
- package/src/components/clients/ClientLogo.tsx +38 -3
- package/src/components/ecosystem/AgentLabDialog.tsx +204 -3
- package/src/components/groups/GroupsDialog.tsx +28 -40
- package/src/components/providers/ImportWizardDialog.tsx +51 -101
- package/src/components/providers/ProvidersPanel.tsx +82 -68
- package/src/components/providers/SettingsDialog.tsx +11 -12
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +15 -23
- package/src/components/proxy-viewer/ReplayDialog.tsx +65 -22
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +33 -20
- package/src/components/proxy-viewer/TurnGroup.tsx +0 -16
- package/src/components/proxy-viewer/log-formats/anthropic.ts +5 -5
- package/src/components/proxy-viewer/log-formats/openai.ts +7 -10
- package/src/components/proxy-viewer/requestTools.ts +5 -2
- package/src/lib/apiClient.ts +43 -10
- package/src/lib/ecosystemContract.ts +32 -0
- package/src/lib/export-logs.ts +37 -3
- package/src/proxy/ecosystemRegistry.ts +244 -0
- package/src/proxy/ecosystemTasks.ts +153 -0
- package/src/proxy/platformCommands.ts +212 -0
- package/src/proxy/socketTracker.ts +84 -143
- package/src/routes/api/ecosystem.packages.$packageId.help.ts +16 -0
- package/src/routes/api/ecosystem.packages.$packageId.install.ts +16 -0
- package/src/routes/api/ecosystem.packages.$packageId.runner-presets.ts +19 -0
- package/src/routes/api/ecosystem.packages.$packageId.upgrade.ts +16 -0
- package/src/routes/api/ecosystem.packages.ts +1 -248
- package/src/routes/api/ecosystem.tasks.$taskId.ts +16 -0
- package/src/routes/api/ecosystem.tasks.ts +12 -0
- package/src/routes/api/providers.$providerId.test.log.ts +28 -24
- package/.output/public/assets/ProxyViewerContainer-p-rpYyiF.js +0 -106
- package/.output/public/assets/ReplayDialog-CM3dhP8v.js +0 -1
- package/.output/public/assets/ResponseView-Bq6sC7Ay.js +0 -3
- package/.output/public/assets/StreamingChunkSequence-CGOun3Df.js +0 -1
- package/.output/public/assets/index-C29Olfzt.js +0 -1
- package/.output/public/assets/index-DgANE0r3.css +0 -1
- package/.output/server/_tanstack-start-manifest_v-LCE-2AaF.mjs +0 -4
|
@@ -52,6 +52,8 @@ import { groupLogsByConversation, type ConversationGroupData } from "./proxy-vie
|
|
|
52
52
|
import { CrabLogo } from "./ui/crab-logo";
|
|
53
53
|
import { crabVariants } from "./ui/crab-variants";
|
|
54
54
|
import { McpLogo } from "./ui/mcp-logo";
|
|
55
|
+
import { Button } from "./ui/button";
|
|
56
|
+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog";
|
|
55
57
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
|
|
56
58
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
|
|
57
59
|
import type { TransientToastInput } from "./ui/transient-toast";
|
|
@@ -149,6 +151,7 @@ export type SessionMembershipEvidence = {
|
|
|
149
151
|
|
|
150
152
|
export type LogPaginationControls = {
|
|
151
153
|
isLoading: boolean;
|
|
154
|
+
loadProgress?: SessionLoadProgress;
|
|
152
155
|
total: number | null;
|
|
153
156
|
pageSize: number;
|
|
154
157
|
hasOlder: boolean;
|
|
@@ -161,6 +164,14 @@ export type LogPaginationControls = {
|
|
|
161
164
|
onNewest: () => void;
|
|
162
165
|
};
|
|
163
166
|
|
|
167
|
+
export type SessionLoadProgress = {
|
|
168
|
+
label: string;
|
|
169
|
+
elapsedMs: number;
|
|
170
|
+
timeoutMs: number;
|
|
171
|
+
loadedLogs: number;
|
|
172
|
+
loadedPages: number;
|
|
173
|
+
};
|
|
174
|
+
|
|
164
175
|
function formatTimeRange(logs: CapturedLog[], timeDisplayFormat: TimeDisplayFormat): string | null {
|
|
165
176
|
const first = logs[0];
|
|
166
177
|
const last = logs[logs.length - 1];
|
|
@@ -1003,6 +1014,44 @@ function LogPaginationButton({
|
|
|
1003
1014
|
);
|
|
1004
1015
|
}
|
|
1005
1016
|
|
|
1017
|
+
function formatDurationSeconds(ms: number): string {
|
|
1018
|
+
return `${Math.max(0, Math.ceil(ms / 1000)).toString()}s`;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function SessionLoadProgressBar({ progress }: { progress: SessionLoadProgress }): JSX.Element {
|
|
1022
|
+
const elapsedRatio = Math.min(1, progress.elapsedMs / progress.timeoutMs);
|
|
1023
|
+
const percent = Math.max(4, Math.round(elapsedRatio * 100));
|
|
1024
|
+
const remainingMs = Math.max(0, progress.timeoutMs - progress.elapsedMs);
|
|
1025
|
+
const nearTimeout = elapsedRatio >= 0.75;
|
|
1026
|
+
const details = [
|
|
1027
|
+
progress.loadedLogs > 0 ? `${progress.loadedLogs.toString()} logs` : null,
|
|
1028
|
+
progress.loadedPages > 0
|
|
1029
|
+
? `${progress.loadedPages.toString()} page${progress.loadedPages === 1 ? "" : "s"}`
|
|
1030
|
+
: null,
|
|
1031
|
+
`${formatDurationSeconds(remainingMs)} before timeout`,
|
|
1032
|
+
].filter((item): item is string => item !== null);
|
|
1033
|
+
|
|
1034
|
+
return (
|
|
1035
|
+
<div className="min-w-0 space-y-1 px-1 pb-1">
|
|
1036
|
+
<div className="flex min-w-0 items-center justify-between gap-3 font-mono text-[10px] text-muted-foreground">
|
|
1037
|
+
<span className="min-w-0 truncate">{progress.label}</span>
|
|
1038
|
+
<span className={cn("shrink-0 tabular-nums", nearTimeout && "text-amber-300")}>
|
|
1039
|
+
{details.join(" / ")}
|
|
1040
|
+
</span>
|
|
1041
|
+
</div>
|
|
1042
|
+
<div className="h-1.5 overflow-hidden rounded-full bg-white/[0.06]">
|
|
1043
|
+
<div
|
|
1044
|
+
className={cn(
|
|
1045
|
+
"h-full rounded-full transition-all duration-300",
|
|
1046
|
+
nearTimeout ? "bg-amber-300/80" : "bg-cyan-300/70",
|
|
1047
|
+
)}
|
|
1048
|
+
style={{ width: `${percent.toString()}%` }}
|
|
1049
|
+
/>
|
|
1050
|
+
</div>
|
|
1051
|
+
</div>
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1006
1055
|
function LogPaginationBar({
|
|
1007
1056
|
logs,
|
|
1008
1057
|
pagination,
|
|
@@ -1027,6 +1076,9 @@ function LogPaginationBar({
|
|
|
1027
1076
|
<div className="w-full min-w-0 px-1 font-mono text-[11px] text-muted-foreground sm:flex-1">
|
|
1028
1077
|
{formatPaginationStatus({ logs, pagination })}
|
|
1029
1078
|
{pagination.isLoading && logs.length > 0 && <span className="ml-2">Loading...</span>}
|
|
1079
|
+
{pagination.loadProgress !== undefined && (
|
|
1080
|
+
<SessionLoadProgressBar progress={pagination.loadProgress} />
|
|
1081
|
+
)}
|
|
1030
1082
|
</div>
|
|
1031
1083
|
<div className="flex w-full items-center gap-1 overflow-x-auto sm:w-auto">
|
|
1032
1084
|
<LogPaginationButton
|
|
@@ -1167,26 +1219,6 @@ function CopyableCommand({ command }: { command: string }): JSX.Element {
|
|
|
1167
1219
|
);
|
|
1168
1220
|
}
|
|
1169
1221
|
|
|
1170
|
-
function McpReadyBadge(): JSX.Element {
|
|
1171
|
-
return (
|
|
1172
|
-
<TooltipProvider>
|
|
1173
|
-
<Tooltip>
|
|
1174
|
-
<TooltipTrigger asChild>
|
|
1175
|
-
<span className="bg-cyan-300/[0.055] shadow-[inset_0_1px_0_rgba(255,255,255,0.05)] inline-flex h-7 items-center gap-2 rounded-md px-2.5 font-mono text-[11px] font-medium text-cyan-200">
|
|
1176
|
-
<span className="size-1.5 rounded-full bg-cyan-200 shadow-[0_0_8px_rgba(125,211,252,0.75)]" />
|
|
1177
|
-
MCP Ready
|
|
1178
|
-
<span className="hidden text-cyan-200/70 sm:inline">/api/mcp</span>
|
|
1179
|
-
</span>
|
|
1180
|
-
</TooltipTrigger>
|
|
1181
|
-
<TooltipContent sideOffset={8} className="max-w-[320px] text-left leading-relaxed">
|
|
1182
|
-
Coding agents can inspect logs, replay requests, test providers, and debug sessions
|
|
1183
|
-
through MCP at /api/mcp.
|
|
1184
|
-
</TooltipContent>
|
|
1185
|
-
</Tooltip>
|
|
1186
|
-
</TooltipProvider>
|
|
1187
|
-
);
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
1222
|
function SidebarPanel({
|
|
1191
1223
|
label,
|
|
1192
1224
|
children,
|
|
@@ -1249,7 +1281,7 @@ function CrabFamily({
|
|
|
1249
1281
|
entrancePhase: "hidden" | "playing" | "done";
|
|
1250
1282
|
}): JSX.Element {
|
|
1251
1283
|
if (compact) {
|
|
1252
|
-
return <CrabLogo className="size-
|
|
1284
|
+
return <CrabLogo className="size-14 text-amber-500" />;
|
|
1253
1285
|
}
|
|
1254
1286
|
|
|
1255
1287
|
return (
|
|
@@ -1295,7 +1327,7 @@ function BrandHeader({
|
|
|
1295
1327
|
return (
|
|
1296
1328
|
<div className="bg-white/[0.035] shadow-[inset_0_1px_0_rgba(255,255,255,0.045),0_16px_44px_rgba(0,0,0,0.18)] rounded-[8px] p-3">
|
|
1297
1329
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
1298
|
-
<div className="flex min-w-fit items-start gap-
|
|
1330
|
+
<div className="flex min-w-fit items-start gap-3">
|
|
1299
1331
|
<CrabFamily compact={true} entrancePhase={crabEntrancePhase} />
|
|
1300
1332
|
<div className="min-w-fit">
|
|
1301
1333
|
<div className="flex min-w-fit flex-wrap items-baseline gap-x-2 gap-y-0.5">
|
|
@@ -1316,9 +1348,6 @@ function BrandHeader({
|
|
|
1316
1348
|
<SettingsDialog />
|
|
1317
1349
|
</div>
|
|
1318
1350
|
</div>
|
|
1319
|
-
<div className="mt-2 hidden sm:block">
|
|
1320
|
-
<McpReadyBadge />
|
|
1321
|
-
</div>
|
|
1322
1351
|
</div>
|
|
1323
1352
|
);
|
|
1324
1353
|
}
|
|
@@ -1341,7 +1370,6 @@ function BrandHeader({
|
|
|
1341
1370
|
<span className="max-w-full text-sm font-semibold text-muted-foreground">
|
|
1342
1371
|
Local First. Evidence First.
|
|
1343
1372
|
</span>
|
|
1344
|
-
<McpReadyBadge />
|
|
1345
1373
|
</h1>
|
|
1346
1374
|
<div className="flex flex-1 justify-end gap-1">
|
|
1347
1375
|
<AlertsDialog />
|
|
@@ -1669,6 +1697,8 @@ export type ProxyViewerProps = {
|
|
|
1669
1697
|
onSessionChange: (session: string) => void;
|
|
1670
1698
|
onModelChange: (model: string) => void;
|
|
1671
1699
|
onClearAll: () => void;
|
|
1700
|
+
/** Delete visible logs from persisted storage when the user explicitly confirms it. */
|
|
1701
|
+
onDeleteCurrentView?: (ids: number[]) => Promise<void>;
|
|
1672
1702
|
/** Reload the selected/pinned session from stored logs without requiring a page refresh. */
|
|
1673
1703
|
onReloadSession?: () => void;
|
|
1674
1704
|
/** Clear only the logs whose ids are passed. Called by the per-group
|
|
@@ -1689,6 +1719,8 @@ export type ProxyViewerProps = {
|
|
|
1689
1719
|
isLoading?: boolean;
|
|
1690
1720
|
/** Cursor controls for history pages. Undefined keeps the live stream UI compact. */
|
|
1691
1721
|
pagination?: LogPaginationControls;
|
|
1722
|
+
/** Active session log loading progress, shown near load/reload controls. */
|
|
1723
|
+
sessionLoadProgress?: SessionLoadProgress;
|
|
1692
1724
|
/** Group/run member metadata attached to the pinned session id. */
|
|
1693
1725
|
sessionMemberships?: SessionMembershipEvidence[];
|
|
1694
1726
|
/** Hide the session filter dropdown. Used on `/session/$id` routes where
|
|
@@ -1708,6 +1740,7 @@ export function ProxyViewer({
|
|
|
1708
1740
|
onSessionChange,
|
|
1709
1741
|
onModelChange,
|
|
1710
1742
|
onClearAll,
|
|
1743
|
+
onDeleteCurrentView,
|
|
1711
1744
|
onReloadSession,
|
|
1712
1745
|
onClearGroup,
|
|
1713
1746
|
onNotify,
|
|
@@ -1719,6 +1752,7 @@ export function ProxyViewer({
|
|
|
1719
1752
|
timeDisplayFormat,
|
|
1720
1753
|
isLoading = false,
|
|
1721
1754
|
pagination,
|
|
1755
|
+
sessionLoadProgress,
|
|
1722
1756
|
sessionMemberships = [],
|
|
1723
1757
|
hideSessionFilter = false,
|
|
1724
1758
|
pinnedSessionId,
|
|
@@ -1730,6 +1764,9 @@ export function ProxyViewer({
|
|
|
1730
1764
|
const [importing, setImporting] = useState(false);
|
|
1731
1765
|
const [importStatus, setImportStatus] = useState<string | null>(null);
|
|
1732
1766
|
const [importError, setImportError] = useState<string | null>(null);
|
|
1767
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
1768
|
+
const [deleteStoredData, setDeleteStoredData] = useState(false);
|
|
1769
|
+
const [deletingCurrentView, setDeletingCurrentView] = useState(false);
|
|
1733
1770
|
const [comparePair, setComparePair] = useState<[CapturedLog, CapturedLog] | null>(null);
|
|
1734
1771
|
const [crabEntrancePhase, setCrabEntrancePhase] = useState<"hidden" | "playing" | "done">(
|
|
1735
1772
|
"hidden",
|
|
@@ -1737,8 +1774,27 @@ export function ProxyViewer({
|
|
|
1737
1774
|
const logListRef = useRef<HTMLDivElement>(null);
|
|
1738
1775
|
const logListWrapperRef = useRef<HTMLDivElement>(null);
|
|
1739
1776
|
const importInputRef = useRef<HTMLInputElement>(null);
|
|
1777
|
+
const exportAbortRef = useRef<AbortController | null>(null);
|
|
1778
|
+
const exportRequestIdRef = useRef(0);
|
|
1740
1779
|
useKeyboardNavigation(logListRef, logListWrapperRef, { pageWide: true });
|
|
1741
1780
|
|
|
1781
|
+
const cancelTransientTasks = useCallback(() => {
|
|
1782
|
+
exportRequestIdRef.current += 1;
|
|
1783
|
+
exportAbortRef.current?.abort();
|
|
1784
|
+
exportAbortRef.current = null;
|
|
1785
|
+
setExporting(null);
|
|
1786
|
+
setExportError(null);
|
|
1787
|
+
}, []);
|
|
1788
|
+
|
|
1789
|
+
useEffect(
|
|
1790
|
+
() => () => {
|
|
1791
|
+
exportRequestIdRef.current += 1;
|
|
1792
|
+
exportAbortRef.current?.abort();
|
|
1793
|
+
exportAbortRef.current = null;
|
|
1794
|
+
},
|
|
1795
|
+
[],
|
|
1796
|
+
);
|
|
1797
|
+
|
|
1742
1798
|
useEffect(() => {
|
|
1743
1799
|
const perCrabDuration = 400;
|
|
1744
1800
|
const startDelay = 50;
|
|
@@ -1762,11 +1818,18 @@ export function ProxyViewer({
|
|
|
1762
1818
|
|
|
1763
1819
|
const handleExport = useCallback(
|
|
1764
1820
|
async (mode: ExportMode) => {
|
|
1821
|
+
exportAbortRef.current?.abort();
|
|
1822
|
+
const controller = new AbortController();
|
|
1823
|
+
exportAbortRef.current = controller;
|
|
1824
|
+
const requestId = exportRequestIdRef.current + 1;
|
|
1825
|
+
exportRequestIdRef.current = requestId;
|
|
1765
1826
|
setExporting(mode);
|
|
1766
1827
|
setExportError(null);
|
|
1767
1828
|
try {
|
|
1768
|
-
const result = await exportLogsAsZip(logs, mode);
|
|
1829
|
+
const result = await exportLogsAsZip(logs, mode, { signal: controller.signal });
|
|
1830
|
+
if (exportRequestIdRef.current !== requestId) return;
|
|
1769
1831
|
if (!result.ok) {
|
|
1832
|
+
if (result.canceled === true) return;
|
|
1770
1833
|
if (onNotify !== undefined) {
|
|
1771
1834
|
onNotify({ message: result.message, tone: "error" });
|
|
1772
1835
|
} else {
|
|
@@ -1774,7 +1837,10 @@ export function ProxyViewer({
|
|
|
1774
1837
|
}
|
|
1775
1838
|
}
|
|
1776
1839
|
} finally {
|
|
1777
|
-
|
|
1840
|
+
if (exportRequestIdRef.current === requestId) {
|
|
1841
|
+
exportAbortRef.current = null;
|
|
1842
|
+
setExporting(null);
|
|
1843
|
+
}
|
|
1778
1844
|
}
|
|
1779
1845
|
},
|
|
1780
1846
|
[logs, onNotify],
|
|
@@ -1819,8 +1885,61 @@ export function ProxyViewer({
|
|
|
1819
1885
|
// Close the compare drawer when the user changes the session or model
|
|
1820
1886
|
// filter, since the predecessor relationship may no longer be meaningful.
|
|
1821
1887
|
useEffect(() => {
|
|
1888
|
+
cancelTransientTasks();
|
|
1889
|
+
setComparePair(null);
|
|
1890
|
+
}, [cancelTransientTasks, selectedSession, selectedModel]);
|
|
1891
|
+
|
|
1892
|
+
const handleClearCurrentView = useCallback(() => {
|
|
1893
|
+
cancelTransientTasks();
|
|
1894
|
+
setImportStatus(null);
|
|
1895
|
+
setImportError(null);
|
|
1822
1896
|
setComparePair(null);
|
|
1823
|
-
|
|
1897
|
+
onClearAll();
|
|
1898
|
+
}, [cancelTransientTasks, onClearAll]);
|
|
1899
|
+
|
|
1900
|
+
const handleRequestDeleteCurrentView = useCallback(() => {
|
|
1901
|
+
setDeleteStoredData(false);
|
|
1902
|
+
setDeleteDialogOpen(true);
|
|
1903
|
+
}, []);
|
|
1904
|
+
|
|
1905
|
+
const handleConfirmDeleteCurrentView = useCallback(() => {
|
|
1906
|
+
cancelTransientTasks();
|
|
1907
|
+
setImportStatus(null);
|
|
1908
|
+
setImportError(null);
|
|
1909
|
+
setComparePair(null);
|
|
1910
|
+
|
|
1911
|
+
if (!deleteStoredData || onDeleteCurrentView === undefined) {
|
|
1912
|
+
setDeleteDialogOpen(false);
|
|
1913
|
+
onClearAll();
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
setDeletingCurrentView(true);
|
|
1918
|
+
void onDeleteCurrentView(logs.map((log) => log.id))
|
|
1919
|
+
.then(() => {
|
|
1920
|
+
setDeleteDialogOpen(false);
|
|
1921
|
+
})
|
|
1922
|
+
.catch((err: unknown) => {
|
|
1923
|
+
const message = err instanceof Error ? err.message : "Failed to delete stored logs";
|
|
1924
|
+
if (onNotify !== undefined) {
|
|
1925
|
+
onNotify({ message, tone: "error" });
|
|
1926
|
+
} else {
|
|
1927
|
+
setImportError(message);
|
|
1928
|
+
}
|
|
1929
|
+
})
|
|
1930
|
+
.finally(() => {
|
|
1931
|
+
setDeletingCurrentView(false);
|
|
1932
|
+
});
|
|
1933
|
+
}, [cancelTransientTasks, deleteStoredData, logs, onClearAll, onDeleteCurrentView, onNotify]);
|
|
1934
|
+
|
|
1935
|
+
const handleReloadCurrentSession = useCallback(() => {
|
|
1936
|
+
if (onReloadSession === undefined) return;
|
|
1937
|
+
cancelTransientTasks();
|
|
1938
|
+
setImportStatus(null);
|
|
1939
|
+
setImportError(null);
|
|
1940
|
+
setComparePair(null);
|
|
1941
|
+
onReloadSession();
|
|
1942
|
+
}, [cancelTransientTasks, onReloadSession]);
|
|
1824
1943
|
|
|
1825
1944
|
const closeCompare = useCallback(() => {
|
|
1826
1945
|
setComparePair(null);
|
|
@@ -1927,7 +2046,7 @@ export function ProxyViewer({
|
|
|
1927
2046
|
{onReloadSession !== undefined && (
|
|
1928
2047
|
<button
|
|
1929
2048
|
type="button"
|
|
1930
|
-
onClick={
|
|
2049
|
+
onClick={handleReloadCurrentSession}
|
|
1931
2050
|
disabled={isLoading}
|
|
1932
2051
|
className="bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
1933
2052
|
title="Load stored logs for this session"
|
|
@@ -1938,18 +2057,37 @@ export function ProxyViewer({
|
|
|
1938
2057
|
)}
|
|
1939
2058
|
<button
|
|
1940
2059
|
type="button"
|
|
1941
|
-
onClick={
|
|
2060
|
+
onClick={handleClearCurrentView}
|
|
1942
2061
|
disabled={!canClearCurrentView}
|
|
1943
2062
|
className={cn(
|
|
1944
2063
|
"bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
|
1945
|
-
onReloadSession === undefined &&
|
|
2064
|
+
onReloadSession === undefined &&
|
|
2065
|
+
onDeleteCurrentView === undefined &&
|
|
2066
|
+
"col-span-2",
|
|
1946
2067
|
)}
|
|
1947
2068
|
title="Clear the current view. Stored logs remain reloadable."
|
|
1948
2069
|
>
|
|
1949
2070
|
<Trash2 className="size-3.5" />
|
|
1950
|
-
<span>Clear
|
|
2071
|
+
<span>Clear</span>
|
|
1951
2072
|
</button>
|
|
2073
|
+
{onDeleteCurrentView !== undefined && (
|
|
2074
|
+
<button
|
|
2075
|
+
type="button"
|
|
2076
|
+
onClick={handleRequestDeleteCurrentView}
|
|
2077
|
+
disabled={!canClearCurrentView || deletingCurrentView}
|
|
2078
|
+
className="bg-red-500/[0.07] hover:bg-red-500/[0.12] inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs text-red-200 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.14)] transition-colors hover:text-red-100 disabled:cursor-not-allowed disabled:opacity-50"
|
|
2079
|
+
title="Choose whether to delete stored data for the current view."
|
|
2080
|
+
>
|
|
2081
|
+
<Trash2 className="size-3.5" />
|
|
2082
|
+
<span>Delete</span>
|
|
2083
|
+
</button>
|
|
2084
|
+
)}
|
|
1952
2085
|
</div>
|
|
2086
|
+
{sessionLoadProgress !== undefined && (
|
|
2087
|
+
<div className="mt-2 rounded-md bg-black/20 px-2 py-2 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.045)]">
|
|
2088
|
+
<SessionLoadProgressBar progress={sessionLoadProgress} />
|
|
2089
|
+
</div>
|
|
2090
|
+
)}
|
|
1953
2091
|
</SidebarPanel>
|
|
1954
2092
|
|
|
1955
2093
|
<SidebarPanel label="Workspace">
|
|
@@ -2128,7 +2266,7 @@ export function ProxyViewer({
|
|
|
2128
2266
|
{onReloadSession !== undefined && (
|
|
2129
2267
|
<button
|
|
2130
2268
|
type="button"
|
|
2131
|
-
onClick={
|
|
2269
|
+
onClick={handleReloadCurrentSession}
|
|
2132
2270
|
disabled={isLoading}
|
|
2133
2271
|
className="bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
2134
2272
|
title="Reload this session from stored logs"
|
|
@@ -2139,15 +2277,32 @@ export function ProxyViewer({
|
|
|
2139
2277
|
)}
|
|
2140
2278
|
<button
|
|
2141
2279
|
type="button"
|
|
2142
|
-
onClick={
|
|
2280
|
+
onClick={handleClearCurrentView}
|
|
2143
2281
|
disabled={!canClearCurrentView}
|
|
2144
2282
|
className="bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
2145
2283
|
title="Clear the current view. Stored logs remain reloadable."
|
|
2146
2284
|
>
|
|
2147
2285
|
<Trash2 className="size-3.5" />
|
|
2148
|
-
<span>Clear
|
|
2286
|
+
<span>Clear</span>
|
|
2149
2287
|
</button>
|
|
2288
|
+
{onDeleteCurrentView !== undefined && (
|
|
2289
|
+
<button
|
|
2290
|
+
type="button"
|
|
2291
|
+
onClick={handleRequestDeleteCurrentView}
|
|
2292
|
+
disabled={!canClearCurrentView || deletingCurrentView}
|
|
2293
|
+
className="bg-red-500/[0.07] hover:bg-red-500/[0.12] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-red-200 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.14)] transition-colors hover:text-red-100 disabled:cursor-not-allowed disabled:opacity-50"
|
|
2294
|
+
title="Choose whether to delete stored data for the current view."
|
|
2295
|
+
>
|
|
2296
|
+
<Trash2 className="size-3.5" />
|
|
2297
|
+
<span>Delete</span>
|
|
2298
|
+
</button>
|
|
2299
|
+
)}
|
|
2150
2300
|
</div>
|
|
2301
|
+
{pagination === undefined && sessionLoadProgress !== undefined && (
|
|
2302
|
+
<div className="mt-2 rounded-md bg-black/20 px-2 py-2 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.045)]">
|
|
2303
|
+
<SessionLoadProgressBar progress={sessionLoadProgress} />
|
|
2304
|
+
</div>
|
|
2305
|
+
)}
|
|
2151
2306
|
<LogPaginationBar logs={logs} pagination={pagination} embedded={true} />
|
|
2152
2307
|
{onNotify === undefined && exportError !== null && (
|
|
2153
2308
|
<div className="mt-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive">
|
|
@@ -2178,6 +2333,11 @@ export function ProxyViewer({
|
|
|
2178
2333
|
<p className="inline-block max-w-full rounded bg-muted px-3 py-1 font-mono text-xs break-all">
|
|
2179
2334
|
{truncateSessionId(selectedSession)}
|
|
2180
2335
|
</p>
|
|
2336
|
+
{sessionLoadProgress !== undefined && (
|
|
2337
|
+
<div className="mx-auto max-w-md rounded-md bg-black/20 px-2 py-2 text-left shadow-[inset_0_0_0_1px_rgba(255,255,255,0.045)]">
|
|
2338
|
+
<SessionLoadProgressBar progress={sessionLoadProgress} />
|
|
2339
|
+
</div>
|
|
2340
|
+
)}
|
|
2181
2341
|
{sessionMemberships.length > 0 && (
|
|
2182
2342
|
<div className="text-left">
|
|
2183
2343
|
<SessionMembershipPanel memberships={sessionMemberships} />
|
|
@@ -2190,7 +2350,7 @@ export function ProxyViewer({
|
|
|
2190
2350
|
{onReloadSession !== undefined && (
|
|
2191
2351
|
<button
|
|
2192
2352
|
type="button"
|
|
2193
|
-
onClick={
|
|
2353
|
+
onClick={handleReloadCurrentSession}
|
|
2194
2354
|
className="bg-white/[0.045] hover:bg-white/[0.075] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground"
|
|
2195
2355
|
>
|
|
2196
2356
|
<RefreshCw className="size-3.5" />
|
|
@@ -2268,6 +2428,58 @@ export function ProxyViewer({
|
|
|
2268
2428
|
</main>
|
|
2269
2429
|
</div>
|
|
2270
2430
|
|
|
2431
|
+
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
|
2432
|
+
<DialogContent className="border-red-500/20 bg-zinc-950 text-zinc-100 sm:max-w-md">
|
|
2433
|
+
<DialogHeader>
|
|
2434
|
+
<DialogTitle>Delete current logs?</DialogTitle>
|
|
2435
|
+
<DialogDescription>
|
|
2436
|
+
Choose whether this only clears the UI or also deletes the stored log data for the
|
|
2437
|
+
current view.
|
|
2438
|
+
</DialogDescription>
|
|
2439
|
+
</DialogHeader>
|
|
2440
|
+
<label className="flex items-start gap-3 rounded-md bg-red-500/[0.08] px-3 py-3 text-sm text-red-100 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.16)]">
|
|
2441
|
+
<input
|
|
2442
|
+
type="checkbox"
|
|
2443
|
+
className="mt-0.5 size-4 shrink-0"
|
|
2444
|
+
checked={deleteStoredData}
|
|
2445
|
+
disabled={deletingCurrentView}
|
|
2446
|
+
onChange={(event) => setDeleteStoredData(event.currentTarget.checked)}
|
|
2447
|
+
/>
|
|
2448
|
+
<span className="min-w-0">
|
|
2449
|
+
<span className="block font-medium">Also delete stored data</span>
|
|
2450
|
+
<span className="mt-1 block text-xs text-red-200/80">
|
|
2451
|
+
Removes persisted log rows, streaming chunks, and session archive entries for these{" "}
|
|
2452
|
+
{logs.length.toString()} visible request{logs.length === 1 ? "" : "s"}.
|
|
2453
|
+
</span>
|
|
2454
|
+
</span>
|
|
2455
|
+
</label>
|
|
2456
|
+
<div className="flex justify-end gap-2">
|
|
2457
|
+
<Button
|
|
2458
|
+
type="button"
|
|
2459
|
+
variant="outline"
|
|
2460
|
+
size="sm"
|
|
2461
|
+
disabled={deletingCurrentView}
|
|
2462
|
+
onClick={() => setDeleteDialogOpen(false)}
|
|
2463
|
+
>
|
|
2464
|
+
Cancel
|
|
2465
|
+
</Button>
|
|
2466
|
+
<Button
|
|
2467
|
+
type="button"
|
|
2468
|
+
variant={deleteStoredData ? "destructive" : "secondary"}
|
|
2469
|
+
size="sm"
|
|
2470
|
+
disabled={deletingCurrentView}
|
|
2471
|
+
onClick={handleConfirmDeleteCurrentView}
|
|
2472
|
+
>
|
|
2473
|
+
{deletingCurrentView
|
|
2474
|
+
? "Deleting..."
|
|
2475
|
+
: deleteStoredData
|
|
2476
|
+
? "Delete data"
|
|
2477
|
+
: "Clear only"}
|
|
2478
|
+
</Button>
|
|
2479
|
+
</div>
|
|
2480
|
+
</DialogContent>
|
|
2481
|
+
</Dialog>
|
|
2482
|
+
|
|
2271
2483
|
{/* Compare drawer: sibling of the log list, not a route change. */}
|
|
2272
2484
|
{comparePair !== null && (
|
|
2273
2485
|
<Suspense fallback={null}>
|