@tonyclaw/llm-inspector 1.16.4 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/CompareDrawer-C4fie5g5.js +1 -0
  3. package/.output/public/assets/ReplayDialog-Dme5uOR9.js +1 -0
  4. package/.output/public/assets/RequestAnatomy-ChBLDNFH.js +1 -0
  5. package/.output/public/assets/ResponseView-wGeqBzVU.js +1 -0
  6. package/.output/public/assets/StreamingChunkSequence-zeJZQLqT.js +1 -0
  7. package/.output/public/assets/index-DoGvsnbA.css +1 -0
  8. package/.output/public/assets/index-DpbutOvo.js +101 -0
  9. package/.output/public/assets/json-viewer-BV-WUszW.js +14 -0
  10. package/.output/public/assets/{main-DbWwVQFh.js → main-DRu10KNQ.js} +1 -1
  11. package/.output/server/_libs/lucide-react.mjs +105 -85
  12. package/.output/server/_ssr/CompareDrawer-C4-CQL5w.mjs +1040 -0
  13. package/.output/server/_ssr/ReplayDialog-BTb1Bam8.mjs +321 -0
  14. package/.output/server/_ssr/RequestAnatomy-CZFV1IvL.mjs +351 -0
  15. package/.output/server/_ssr/ResponseView-CTZekh65.mjs +601 -0
  16. package/.output/server/_ssr/StreamingChunkSequence-C38Ynabd.mjs +301 -0
  17. package/.output/server/_ssr/{index-C-z-fZtq.mjs → index-Cnu-QzAy.mjs} +1141 -2443
  18. package/.output/server/_ssr/index.mjs +2 -2
  19. package/.output/server/_ssr/json-viewer-DROqpjS9.mjs +510 -0
  20. package/.output/server/_ssr/{router-CNM9Kbi0.mjs → router-pP4GCTQx.mjs} +42 -18
  21. package/.output/server/{_tanstack-start-manifest_v-BWfLeIsC.mjs → _tanstack-start-manifest_v-CphS4rZd.mjs} +1 -1
  22. package/.output/server/index.mjs +69 -27
  23. package/package.json +1 -1
  24. package/src/components/OnboardingBanner.tsx +2 -2
  25. package/src/components/ProxyViewer.tsx +44 -27
  26. package/src/components/ProxyViewerContainer.tsx +5 -25
  27. package/src/components/providers/SettingsDialog.tsx +52 -1
  28. package/src/components/proxy-viewer/ConversationGroup.tsx +5 -1
  29. package/src/components/proxy-viewer/ConversationHeader.tsx +4 -1
  30. package/src/components/proxy-viewer/LogEntry.tsx +217 -181
  31. package/src/components/proxy-viewer/LogEntryHeader.tsx +181 -40
  32. package/src/components/proxy-viewer/ThreadConnector.tsx +17 -2
  33. package/src/components/proxy-viewer/TurnGroup.tsx +124 -72
  34. package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +98 -0
  35. package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +196 -0
  36. package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +53 -0
  37. package/src/components/proxy-viewer/anatomy/types.ts +39 -0
  38. package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +114 -0
  39. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +3 -23
  40. package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +21 -0
  41. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +5 -3
  42. package/src/components/proxy-viewer/lazy.ts +37 -0
  43. package/src/components/proxy-viewer/log-formats/anthropic.ts +146 -0
  44. package/src/components/proxy-viewer/log-formats/openai.ts +127 -0
  45. package/src/components/proxy-viewer/log-formats/types.ts +7 -0
  46. package/src/components/proxy-viewer/log-formats/unknown.ts +4 -0
  47. package/src/components/proxy-viewer/logEntryVisibility.ts +39 -0
  48. package/src/components/proxy-viewer/useKeyboardNavigation.ts +190 -0
  49. package/src/components/proxy-viewer/viewerState.ts +8 -0
  50. package/src/components/ui/crab-variants.tsx +11 -0
  51. package/src/components/ui/json-expansion-button.tsx +56 -0
  52. package/src/components/ui/json-viewer-bulk.ts +97 -0
  53. package/src/components/ui/json-viewer.tsx +58 -183
  54. package/src/lib/runtimeConfig.ts +9 -0
  55. package/src/lib/useOnboarding.ts +7 -1
  56. package/src/lib/useStripConfig.ts +33 -2
  57. package/src/lib/utils.ts +2 -3
  58. package/src/proxy/config.ts +17 -7
  59. package/src/routes/api/config.ts +7 -0
  60. package/src/routes/api/logs.stream.ts +26 -16
  61. package/.output/public/assets/index-DRRCmu5p.css +0 -1
  62. package/.output/public/assets/index-X7CHS7fS.js +0 -107
@@ -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-BWfLeIsC.mjs");
201
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-CphS4rZd.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-CNM9Kbi0.mjs").then((n) => n.e);
770
+ const routerEntry = await import("./router-pP4GCTQx.mjs").then((n) => n.e);
771
771
  const startEntry = await import("./start-HYkvq4Ni.mjs");
772
772
  return { startEntry, routerEntry };
773
773
  }
@@ -0,0 +1,510 @@
1
+ import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
+ import { q as parseJsonText, c as cn, k as TooltipProvider, l as Tooltip, m as TooltipTrigger, n as TooltipContent } from "./index-Cnu-QzAy.mjs";
3
+ import "./router-pP4GCTQx.mjs";
4
+ import "../_libs/modelcontextprotocol__server.mjs";
5
+ import "../_libs/jszip.mjs";
6
+ import { C as Check, c as Copy, a as ChevronDown, f as ChevronRight, r as ChevronsDown } from "../_libs/lucide-react.mjs";
7
+ import { M as Markdown } from "../_libs/react-markdown.mjs";
8
+ import "../_libs/swr.mjs";
9
+ import "../_libs/use-sync-external-store.mjs";
10
+ import "../_libs/dequal.mjs";
11
+ import "../_libs/clsx.mjs";
12
+ import "../_libs/tailwind-merge.mjs";
13
+ import "../_libs/class-variance-authority.mjs";
14
+ import "../_libs/radix-ui__react-dialog.mjs";
15
+ import "../_libs/radix-ui__primitive.mjs";
16
+ import "../_libs/radix-ui__react-compose-refs.mjs";
17
+ import "../_libs/radix-ui__react-context.mjs";
18
+ import "../_libs/radix-ui__react-id.mjs";
19
+ import "../_libs/@radix-ui/react-use-layout-effect+[...].mjs";
20
+ import "../_libs/@radix-ui/react-use-controllable-state+[...].mjs";
21
+ import "../_libs/@radix-ui/react-dismissable-layer+[...].mjs";
22
+ import "../_libs/radix-ui__react-primitive.mjs";
23
+ import "../_libs/react-dom.mjs";
24
+ import "util";
25
+ import "async_hooks";
26
+ import "stream";
27
+ import "crypto";
28
+ import "../_libs/radix-ui__react-slot.mjs";
29
+ import "../_libs/@radix-ui/react-use-callback-ref+[...].mjs";
30
+ import "../_libs/@radix-ui/react-use-escape-keydown+[...].mjs";
31
+ import "../_libs/radix-ui__react-focus-scope.mjs";
32
+ import "../_libs/radix-ui__react-portal.mjs";
33
+ import "../_libs/radix-ui__react-presence.mjs";
34
+ import "../_libs/radix-ui__react-focus-guards.mjs";
35
+ import "../_libs/react-remove-scroll.mjs";
36
+ import "tslib";
37
+ import "../_libs/react-remove-scroll-bar.mjs";
38
+ import "../_libs/react-style-singleton.mjs";
39
+ import "../_libs/get-nonce.mjs";
40
+ import "../_libs/use-sidecar.mjs";
41
+ import "../_libs/use-callback-ref.mjs";
42
+ import "../_libs/aria-hidden.mjs";
43
+ import "../_libs/diff.mjs";
44
+ import "../_libs/tanstack__react-virtual.mjs";
45
+ import "../_libs/tanstack__virtual-core.mjs";
46
+ import "../_libs/radix-ui__react-select.mjs";
47
+ import "../_libs/radix-ui__number.mjs";
48
+ import "../_libs/radix-ui__react-collection.mjs";
49
+ import "../_libs/radix-ui__react-direction.mjs";
50
+ import "../_libs/radix-ui__react-popper.mjs";
51
+ import "../_libs/floating-ui__react-dom.mjs";
52
+ import "../_libs/floating-ui__dom.mjs";
53
+ import "../_libs/floating-ui__core.mjs";
54
+ import "../_libs/floating-ui__utils.mjs";
55
+ import "../_libs/radix-ui__react-arrow.mjs";
56
+ import "../_libs/radix-ui__react-use-size.mjs";
57
+ import "../_libs/radix-ui__react-use-previous.mjs";
58
+ import "../_libs/@radix-ui/react-visually-hidden+[...].mjs";
59
+ import "../_libs/zod.mjs";
60
+ import "../_libs/radix-ui__react-tabs.mjs";
61
+ import "../_libs/radix-ui__react-roving-focus.mjs";
62
+ import "../_libs/radix-ui__react-tooltip.mjs";
63
+ import "../_libs/tanstack__react-router.mjs";
64
+ import "../_libs/tiny-warning.mjs";
65
+ import "../_libs/tanstack__router-core.mjs";
66
+ import "../_libs/cookie-es.mjs";
67
+ import "../_libs/tanstack__history.mjs";
68
+ import "../_libs/tiny-invariant.mjs";
69
+ import "../_libs/seroval.mjs";
70
+ import "../_libs/seroval-plugins.mjs";
71
+ import "node:stream/web";
72
+ import "node:stream";
73
+ import "../_libs/isbot.mjs";
74
+ import "node:fs";
75
+ import "node:fs/promises";
76
+ import "node:buffer";
77
+ import "node:path";
78
+ import "../_libs/conf.mjs";
79
+ import "node:util";
80
+ import "node:process";
81
+ import "node:crypto";
82
+ import "node:assert";
83
+ import "../_libs/dot-prop.mjs";
84
+ import "../_libs/env-paths.mjs";
85
+ import "node:os";
86
+ import "../_libs/atomically.mjs";
87
+ import "../_libs/stubborn-fs.mjs";
88
+ import "../_libs/stubborn-utils.mjs";
89
+ import "../_libs/when-exit.mjs";
90
+ import "../_libs/ajv.mjs";
91
+ import "../_libs/fast-deep-equal.mjs";
92
+ import "../_libs/json-schema-traverse.mjs";
93
+ import "../_libs/fast-uri.mjs";
94
+ import "../_libs/ajv-formats.mjs";
95
+ import "../_libs/debounce-fn.mjs";
96
+ import "../_libs/mimic-function.mjs";
97
+ import "../_libs/semver.mjs";
98
+ import "../_libs/uint8array-extras.mjs";
99
+ import "node:child_process";
100
+ import "../_libs/readable-stream.mjs";
101
+ import "events";
102
+ import "node:string_decoder";
103
+ import "../_libs/process-nextick-args.mjs";
104
+ import "../_libs/isarray.mjs";
105
+ import "../_libs/safe-buffer.mjs";
106
+ import "buffer";
107
+ import "../_libs/core-util-is.mjs";
108
+ import "../_libs/inherits.mjs";
109
+ import "../_libs/util-deprecate.mjs";
110
+ import "../_libs/lie.mjs";
111
+ import "../_libs/immediate.mjs";
112
+ import "../_libs/setimmediate.mjs";
113
+ import "../_libs/pako.mjs";
114
+ import "../_libs/devlop.mjs";
115
+ import "../_libs/unified.mjs";
116
+ import "../_libs/bail.mjs";
117
+ import "../_libs/extend.mjs";
118
+ import "../_libs/is-plain-obj.mjs";
119
+ import "../_libs/trough.mjs";
120
+ import "../_libs/vfile.mjs";
121
+ import "../_libs/vfile-message.mjs";
122
+ import "../_libs/unist-util-stringify-position.mjs";
123
+ import "node:url";
124
+ import "../_libs/remark-parse.mjs";
125
+ import "../_libs/mdast-util-from-markdown.mjs";
126
+ import "../_libs/micromark-util-decode-numeric-character-reference+[...].mjs";
127
+ import "../_libs/micromark-util-decode-string.mjs";
128
+ import "../_libs/decode-named-character-reference+[...].mjs";
129
+ import "../_libs/character-entities.mjs";
130
+ import "../_libs/micromark-util-normalize-identifier+[...].mjs";
131
+ import "../_libs/micromark.mjs";
132
+ import "../_libs/micromark-util-combine-extensions+[...].mjs";
133
+ import "../_libs/micromark-util-chunked.mjs";
134
+ import "../_libs/micromark-factory-space.mjs";
135
+ import "../_libs/micromark-util-character.mjs";
136
+ import "../_libs/micromark-core-commonmark.mjs";
137
+ import "../_libs/micromark-util-classify-character+[...].mjs";
138
+ import "../_libs/micromark-util-resolve-all.mjs";
139
+ import "../_libs/micromark-util-subtokenize.mjs";
140
+ import "../_libs/micromark-factory-destination.mjs";
141
+ import "../_libs/micromark-factory-label.mjs";
142
+ import "../_libs/micromark-factory-title.mjs";
143
+ import "../_libs/micromark-factory-whitespace.mjs";
144
+ import "../_libs/micromark-util-html-tag-name.mjs";
145
+ import "../_libs/mdast-util-to-string.mjs";
146
+ import "../_libs/remark-rehype.mjs";
147
+ import "../_libs/mdast-util-to-hast.mjs";
148
+ import "../_libs/ungap__structured-clone.mjs";
149
+ import "../_libs/micromark-util-sanitize-uri.mjs";
150
+ import "../_libs/unist-util-position.mjs";
151
+ import "../_libs/trim-lines.mjs";
152
+ import "../_libs/unist-util-visit.mjs";
153
+ import "../_libs/unist-util-visit-parents.mjs";
154
+ import "../_libs/unist-util-is.mjs";
155
+ import "../_libs/hast-util-to-jsx-runtime.mjs";
156
+ import "../_libs/comma-separated-tokens.mjs";
157
+ import "../_libs/property-information.mjs";
158
+ import "../_libs/space-separated-tokens.mjs";
159
+ import "../_libs/style-to-js.mjs";
160
+ import "../_libs/style-to-object.mjs";
161
+ import "../_libs/inline-style-parser.mjs";
162
+ import "../_libs/hast-util-whitespace.mjs";
163
+ import "../_libs/estree-util-is-identifier-name.mjs";
164
+ import "../_libs/html-url-attributes.mjs";
165
+ function classifyValue(value) {
166
+ if (value === null) return "null";
167
+ if (Array.isArray(value)) return "array";
168
+ switch (typeof value) {
169
+ case "string":
170
+ return "string";
171
+ case "number":
172
+ return "number";
173
+ case "boolean":
174
+ return "boolean";
175
+ case "object":
176
+ return "object";
177
+ case "bigint":
178
+ case "symbol":
179
+ case "undefined":
180
+ case "function":
181
+ return "object";
182
+ }
183
+ }
184
+ function isExpandable(value) {
185
+ return value !== null && (Array.isArray(value) || typeof value === "object");
186
+ }
187
+ function getEntries(value) {
188
+ if (Array.isArray(value)) {
189
+ return value.map((item, index) => [String(index), item]);
190
+ }
191
+ if (typeof value === "object" && value !== null) {
192
+ return Object.entries(value);
193
+ }
194
+ return [];
195
+ }
196
+ const STRING_TRUNCATE_LIMIT = 120;
197
+ function StringValue({ text }) {
198
+ const [expanded, setExpanded] = reactExports.useState(false);
199
+ const isLong = text.length > STRING_TRUNCATE_LIMIT;
200
+ if (!isLong) {
201
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 break-all", children: [
202
+ '"',
203
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text }) }),
204
+ '"'
205
+ ] });
206
+ }
207
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 break-all", children: [
208
+ '"',
209
+ expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(
210
+ "span",
211
+ {
212
+ className: "cursor-pointer prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300",
213
+ onClick: (e) => {
214
+ e.stopPropagation();
215
+ setExpanded(false);
216
+ },
217
+ onKeyDown: (e) => {
218
+ if (e.key === "Enter" || e.key === " ") {
219
+ e.stopPropagation();
220
+ setExpanded(false);
221
+ }
222
+ },
223
+ role: "button",
224
+ tabIndex: 0,
225
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text })
226
+ }
227
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { delayDuration: 300, children: [
228
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
229
+ TooltipTrigger,
230
+ {
231
+ onClick: (e) => {
232
+ e.stopPropagation();
233
+ setExpanded(true);
234
+ },
235
+ className: "text-left cursor-pointer",
236
+ children: [
237
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: text.slice(0, STRING_TRUNCATE_LIMIT) }),
238
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400/50", children: "…" })
239
+ ]
240
+ }
241
+ ),
242
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
243
+ TooltipContent,
244
+ {
245
+ side: "bottom",
246
+ className: "max-w-md text-xs p-2 break-words whitespace-pre-wrap",
247
+ children: [
248
+ text.slice(0, 500),
249
+ text.length > 500 ? "…" : ""
250
+ ]
251
+ }
252
+ )
253
+ ] }),
254
+ '"'
255
+ ] });
256
+ }
257
+ function PrimitiveValue({ value }) {
258
+ if (value === null) {
259
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-rose-400 italic", children: "null" });
260
+ }
261
+ switch (typeof value) {
262
+ case "string":
263
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(StringValue, { text: value });
264
+ case "number":
265
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-400", children: value });
266
+ case "boolean":
267
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-400", children: value ? "true" : "false" });
268
+ case "object":
269
+ case "bigint":
270
+ case "symbol":
271
+ case "undefined":
272
+ case "function":
273
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: JSON.stringify(value) });
274
+ }
275
+ }
276
+ function CopyValueButton({ value }) {
277
+ const [copied, setCopied] = reactExports.useState(false);
278
+ function handleCopy(e) {
279
+ e.stopPropagation();
280
+ void window.navigator.clipboard.writeText(JSON.stringify(value, null, 2)).then(() => {
281
+ setCopied(true);
282
+ setTimeout(() => {
283
+ setCopied(false);
284
+ }, 2e3);
285
+ });
286
+ }
287
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
288
+ "button",
289
+ {
290
+ type: "button",
291
+ onClick: handleCopy,
292
+ className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground transition-colors",
293
+ title: "Copy JSON",
294
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" })
295
+ }
296
+ );
297
+ }
298
+ function CopyButton({ value }) {
299
+ const [copied, setCopied] = reactExports.useState(false);
300
+ function handleCopy(e) {
301
+ e.stopPropagation();
302
+ void window.navigator.clipboard.writeText(JSON.stringify(value, null, 2)).then(() => {
303
+ setCopied(true);
304
+ setTimeout(() => {
305
+ setCopied(false);
306
+ }, 2e3);
307
+ });
308
+ }
309
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
310
+ "button",
311
+ {
312
+ type: "button",
313
+ onClick: handleCopy,
314
+ className: "opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0",
315
+ title: "Copy to clipboard",
316
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3 text-muted-foreground" })
317
+ }
318
+ );
319
+ }
320
+ function ExpandCollapseButton({
321
+ onClick
322
+ }) {
323
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
324
+ "button",
325
+ {
326
+ type: "button",
327
+ onClick,
328
+ className: "opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0",
329
+ title: "Expand all descendants",
330
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3.5 text-muted-foreground" })
331
+ }
332
+ );
333
+ }
334
+ const JsonNode = reactExports.memo(function JsonNode2({
335
+ name,
336
+ value,
337
+ level,
338
+ defaultExpandDepth,
339
+ isArrayItem,
340
+ path,
341
+ expandTargetPath,
342
+ anatomyPaths
343
+ }) {
344
+ const isAncestorOfTarget = reactExports.useMemo(() => {
345
+ if (expandTargetPath === null) return false;
346
+ if (path === "") return expandTargetPath.length > 0;
347
+ return expandTargetPath === path || expandTargetPath.startsWith(`${path}/`);
348
+ }, [expandTargetPath, path]);
349
+ const initialExpanded = level < defaultExpandDepth || isAncestorOfTarget && isExpandable(value);
350
+ const [expanded, setExpanded] = reactExports.useState(initialExpanded);
351
+ reactExports.useEffect(() => {
352
+ if (isAncestorOfTarget && isExpandable(value) && !expanded) {
353
+ setExpanded(true);
354
+ }
355
+ }, [expandTargetPath]);
356
+ const [childResetKey, setChildResetKey] = reactExports.useState(0);
357
+ const [childDepthOverride, setChildDepthOverride] = reactExports.useState(null);
358
+ const expandable = isExpandable(value);
359
+ const fullyExpanded = childDepthOverride === Number.POSITIVE_INFINITY;
360
+ const entries = reactExports.useMemo(() => getEntries(value), [value]);
361
+ const hasExpandableChild = reactExports.useMemo(
362
+ () => entries.some(([, child]) => isExpandable(child)),
363
+ [entries]
364
+ );
365
+ const dataType = classifyValue(value);
366
+ const openBracket = dataType === "array" ? "[" : "{";
367
+ const closeBracket = dataType === "array" ? "]" : "}";
368
+ const hasAnatomyPath = anatomyPaths !== null && anatomyPaths.has(path);
369
+ function expandAllDeep() {
370
+ setExpanded(true);
371
+ setChildDepthOverride(Number.POSITIVE_INFINITY);
372
+ setChildResetKey((k) => k + 1);
373
+ }
374
+ function collapseToDefault() {
375
+ setExpanded(false);
376
+ setChildDepthOverride(0);
377
+ setChildResetKey((k) => k + 1);
378
+ }
379
+ function handleRowToggle() {
380
+ if (fullyExpanded) {
381
+ collapseToDefault();
382
+ } else {
383
+ setExpanded(!expanded);
384
+ }
385
+ }
386
+ function handleExpandAll(e) {
387
+ e.stopPropagation();
388
+ expandAllDeep();
389
+ }
390
+ const effectiveChildDepth = childDepthOverride ?? defaultExpandDepth;
391
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn(level > 0 && "border-l border-border/50 ml-2"), children: [
392
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
393
+ "div",
394
+ {
395
+ className: cn(
396
+ "flex items-start gap-1 py-0.5 px-1 -ml-1 rounded-sm group/row",
397
+ expandable && "cursor-pointer hover:bg-muted/50"
398
+ ),
399
+ "data-anatomy-path": hasAnatomyPath ? path : void 0,
400
+ onClick: expandable ? handleRowToggle : void 0,
401
+ onKeyDown: expandable ? (e) => {
402
+ if (e.key === "Enter" || e.key === " ") {
403
+ e.preventDefault();
404
+ handleRowToggle();
405
+ }
406
+ } : void 0,
407
+ onDoubleClick: expandable && hasExpandableChild ? () => expandAllDeep() : void 0,
408
+ role: expandable ? "button" : void 0,
409
+ tabIndex: expandable ? 0 : void 0,
410
+ children: [
411
+ expandable ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 h-5 flex items-center justify-center shrink-0", children: expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 shrink-0" }),
412
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("shrink-0", isArrayItem ? "text-muted-foreground" : "text-cyan-400"), children: isArrayItem ? name : `"${name}"` }),
413
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground shrink-0", children: expandable ? "" : ":" }),
414
+ expandable ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground", children: [
415
+ openBracket,
416
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground/60 text-xs", children: [
417
+ " ",
418
+ entries.length,
419
+ " ",
420
+ entries.length === 1 ? "item" : "items",
421
+ " ",
422
+ closeBracket
423
+ ] })
424
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(PrimitiveValue, { value }) }),
425
+ expandable && hasExpandableChild && !fullyExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandCollapseButton, { onClick: handleExpandAll }),
426
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CopyButton, { value })
427
+ ]
428
+ }
429
+ ),
430
+ expandable && expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pl-4", children: [
431
+ entries.map(([key, childValue]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
432
+ JsonNode2,
433
+ {
434
+ name: key,
435
+ value: childValue,
436
+ level: level + 1,
437
+ defaultExpandDepth: effectiveChildDepth,
438
+ isArrayItem: dataType === "array",
439
+ path: path === "" ? `/${key}` : `${path}/${key}`,
440
+ expandTargetPath,
441
+ anatomyPaths
442
+ },
443
+ key
444
+ )),
445
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground py-0.5 px-1", children: closeBracket })
446
+ ] }, childResetKey)
447
+ ] });
448
+ });
449
+ function JsonViewer({
450
+ data,
451
+ defaultExpandDepth = 0,
452
+ className,
453
+ showCopy = false,
454
+ bulkDepth: controlledBulkDepth,
455
+ bulkRevision: controlledBulkRevision,
456
+ anatomyPaths = null,
457
+ expandToPath = null
458
+ }) {
459
+ const expandable = isExpandable(data);
460
+ const entries = reactExports.useMemo(() => getEntries(data), [data]);
461
+ const bulkDepth = controlledBulkDepth ?? defaultExpandDepth;
462
+ const bulkRevision = controlledBulkRevision ?? 0;
463
+ if (!expandable) {
464
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("font-mono text-xs leading-relaxed", className), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
465
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PrimitiveValue, { value: data }),
466
+ showCopy && /* @__PURE__ */ jsxRuntimeExports.jsx(CopyValueButton, { value: data })
467
+ ] }) }) });
468
+ }
469
+ const dataType = classifyValue(data);
470
+ const isArray = dataType === "array";
471
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("font-mono text-xs leading-relaxed", className), children: [
472
+ showCopy && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-2 flex items-center justify-end gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyValueButton, { value: data }) }),
473
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: entries.map(([key, childValue]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
474
+ JsonNode,
475
+ {
476
+ name: key,
477
+ value: childValue,
478
+ level: 0,
479
+ defaultExpandDepth: bulkDepth,
480
+ isArrayItem: isArray,
481
+ path: `/${key}`,
482
+ expandTargetPath: expandToPath,
483
+ anatomyPaths
484
+ },
485
+ key
486
+ )) }, bulkRevision)
487
+ ] }) });
488
+ }
489
+ const JsonViewerFromString = reactExports.memo(function JsonViewerFromString2({
490
+ text,
491
+ defaultExpandDepth = 0,
492
+ className
493
+ }) {
494
+ const parsed = reactExports.useMemo(() => parseJsonText(text), [text]);
495
+ if (parsed.kind === "json") {
496
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
497
+ JsonViewer,
498
+ {
499
+ data: parsed.data,
500
+ defaultExpandDepth,
501
+ className
502
+ }
503
+ );
504
+ }
505
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: cn("font-mono text-xs whitespace-pre-wrap break-words", className), children: text });
506
+ });
507
+ export {
508
+ JsonViewer,
509
+ JsonViewerFromString
510
+ };
@@ -46,7 +46,7 @@ import "../_libs/debounce-fn.mjs";
46
46
  import "../_libs/mimic-function.mjs";
47
47
  import "../_libs/semver.mjs";
48
48
  import "../_libs/uint8array-extras.mjs";
49
- const appCss = "/assets/index-DRRCmu5p.css";
49
+ const appCss = "/assets/index-DoGvsnbA.css";
50
50
  const Route$k = createRootRoute({
51
51
  head: () => ({
52
52
  meta: [
@@ -70,7 +70,7 @@ function RootDocument({ children }) {
70
70
  ] })
71
71
  ] });
72
72
  }
73
- const $$splitComponentImporter = () => import("./index-C-z-fZtq.mjs");
73
+ const $$splitComponentImporter = () => import("./index-Cnu-QzAy.mjs").then((n) => n.t);
74
74
  const Route$j = createFileRoute("/")({
75
75
  component: lazyRouteComponent($$splitComponentImporter, "component")
76
76
  });
@@ -2158,9 +2158,12 @@ async function getClientInfo(request) {
2158
2158
  inflight.set(port, promise);
2159
2159
  return promise;
2160
2160
  }
2161
+ const DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS = 10;
2162
+ const MAX_SLOW_RESPONSE_THRESHOLD_SECONDS = 600;
2161
2163
  const RuntimeConfigSchema = object({
2162
2164
  stripClaudeCodeBillingHeader: boolean(),
2163
- hasSeenOnboarding: boolean().default(false)
2165
+ hasSeenOnboarding: boolean().default(false),
2166
+ slowResponseThresholdSeconds: number().int().min(0).max(MAX_SLOW_RESPONSE_THRESHOLD_SECONDS).default(DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS)
2164
2167
  });
2165
2168
  function getConfigFilePath() {
2166
2169
  return join(getDataDir(), "config.json");
@@ -2190,9 +2193,17 @@ function resolveInitialConfig() {
2190
2193
  }
2191
2194
  }
2192
2195
  if (process.env["LLM_INSPECTOR_STRIP_CLAUDE_CODE_BILLING_HEADER"] === "1") {
2193
- return { stripClaudeCodeBillingHeader: true, hasSeenOnboarding: false };
2196
+ return {
2197
+ stripClaudeCodeBillingHeader: true,
2198
+ hasSeenOnboarding: false,
2199
+ slowResponseThresholdSeconds: DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS
2200
+ };
2194
2201
  }
2195
- return { stripClaudeCodeBillingHeader: false, hasSeenOnboarding: false };
2202
+ return {
2203
+ stripClaudeCodeBillingHeader: false,
2204
+ hasSeenOnboarding: false,
2205
+ slowResponseThresholdSeconds: DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS
2206
+ };
2196
2207
  }
2197
2208
  function getConfig() {
2198
2209
  return currentConfig;
@@ -3279,7 +3290,8 @@ const Route$c = createFileRoute("/api/health")({
3279
3290
  });
3280
3291
  const RuntimeConfigPatchSchema = object({
3281
3292
  stripClaudeCodeBillingHeader: boolean().optional(),
3282
- hasSeenOnboarding: boolean().optional()
3293
+ hasSeenOnboarding: boolean().optional(),
3294
+ slowResponseThresholdSeconds: number().int().min(0).max(MAX_SLOW_RESPONSE_THRESHOLD_SECONDS).optional()
3283
3295
  }).strict().refine((v) => Object.keys(v).length > 0, {
3284
3296
  message: "At least one field must be provided"
3285
3297
  });
@@ -3817,16 +3829,9 @@ const Route$6 = createFileRoute("/api/logs/stream")({
3817
3829
  const sessionId = url.searchParams.get("sessionId") ?? void 0;
3818
3830
  const model = url.searchParams.get("model") ?? void 0;
3819
3831
  let controllerRef = null;
3820
- const heartbeat = setInterval(() => {
3821
- if (controllerRef) {
3822
- try {
3823
- controllerRef.enqueue(new TextEncoder().encode(": heartbeat\n\n"));
3824
- } catch {
3825
- }
3826
- }
3827
- }, 3e4);
3832
+ let cleanedUp = false;
3828
3833
  const unsubscribe = onLogUpdate((log) => {
3829
- if (!controllerRef) return;
3834
+ if (controllerRef === null) return;
3830
3835
  if (sessionId !== void 0 && log.sessionId !== sessionId) return;
3831
3836
  if (model !== void 0 && log.model !== model) return;
3832
3837
  try {
@@ -3835,8 +3840,27 @@ const Route$6 = createFileRoute("/api/logs/stream")({
3835
3840
  `;
3836
3841
  controllerRef.enqueue(new TextEncoder().encode(data));
3837
3842
  } catch {
3843
+ cleanup();
3838
3844
  }
3839
3845
  });
3846
+ const cleanup = () => {
3847
+ if (cleanedUp) return;
3848
+ cleanedUp = true;
3849
+ clearInterval(heartbeat);
3850
+ unsubscribe();
3851
+ controllerRef = null;
3852
+ request.signal.removeEventListener("abort", cleanup);
3853
+ };
3854
+ const heartbeat = setInterval(() => {
3855
+ if (controllerRef !== null) {
3856
+ try {
3857
+ controllerRef.enqueue(new TextEncoder().encode(": heartbeat\n\n"));
3858
+ } catch {
3859
+ cleanup();
3860
+ }
3861
+ }
3862
+ }, 3e4);
3863
+ request.signal.addEventListener("abort", cleanup, { once: true });
3840
3864
  const stream = new ReadableStream({
3841
3865
  start(controller) {
3842
3866
  controllerRef = controller;
@@ -3847,9 +3871,7 @@ const Route$6 = createFileRoute("/api/logs/stream")({
3847
3871
  controller.enqueue(new TextEncoder().encode(initData));
3848
3872
  },
3849
3873
  cancel() {
3850
- clearInterval(heartbeat);
3851
- unsubscribe();
3852
- controllerRef = null;
3874
+ cleanup();
3853
3875
  }
3854
3876
  });
3855
3877
  return new Response(stream, {
@@ -5006,6 +5028,8 @@ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
5006
5028
  export {
5007
5029
  AnthropicResponseSchema$1 as A,
5008
5030
  CapturedLogSchema as C,
5031
+ DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS as D,
5032
+ MAX_SLOW_RESPONSE_THRESHOLD_SECONDS as M,
5009
5033
  OpenAIRequestSchema as O,
5010
5034
  ProviderTestResultsSchema as P,
5011
5035
  RuntimeConfigSchema as R,