@tonyclaw/llm-inspector 1.18.2 → 1.19.1

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 (42) hide show
  1. package/.output/cli.js +903 -139
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-C-4ypEWs.js → CompareDrawer-DtERUdIt.js} +1 -1
  4. package/.output/public/assets/ProxyViewerContainer-DfxRK7Nt.js +101 -0
  5. package/.output/public/assets/{ReplayDialog-CyBKOgba.js → ReplayDialog-VMsGnJSI.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-C0IrVQ3q.js → RequestAnatomy-Cx_vluvK.js} +1 -1
  7. package/.output/public/assets/{ResponseView-MogToC4i.js → ResponseView-5F8Ms5z4.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-ClhUhT-s.js → StreamingChunkSequence-CKDCWfu9.js} +1 -1
  9. package/.output/public/assets/_sessionId-C-aKd1Ky.js +1 -0
  10. package/.output/public/assets/index-B8ttyigz.js +1 -0
  11. package/.output/public/assets/index-DeJyypsp.css +1 -0
  12. package/.output/public/assets/{json-viewer-BicGakI5.js → json-viewer-CztuZ9cT.js} +2 -2
  13. package/.output/public/assets/{main-Be2qqUUW.js → main-CR9IJlz1.js} +2 -2
  14. package/.output/server/_libs/lucide-react.mjs +93 -72
  15. package/.output/server/{_sessionId-DhKJIdQC.mjs → _sessionId-DvWQaDEm.mjs} +2 -2
  16. package/.output/server/_ssr/{CompareDrawer-BGUgukJ8.mjs → CompareDrawer-C5FsxSDS.mjs} +4 -4
  17. package/.output/server/_ssr/{ProxyViewerContainer--3K3o3Sm.mjs → ProxyViewerContainer-v0cvR8f5.mjs} +354 -343
  18. package/.output/server/_ssr/{ReplayDialog-Bo86xZI4.mjs → ReplayDialog-C3KOv9OW.mjs} +4 -4
  19. package/.output/server/_ssr/{RequestAnatomy-jRU5qgwB.mjs → RequestAnatomy-BYRe33eG.mjs} +3 -3
  20. package/.output/server/_ssr/{ResponseView-DdO_-79a.mjs → ResponseView-va7yQDeL.mjs} +4 -4
  21. package/.output/server/_ssr/{StreamingChunkSequence-BigLwhh4.mjs → StreamingChunkSequence-BJlI-gWl.mjs} +3 -3
  22. package/.output/server/_ssr/{index-BHG6vOnr.mjs → index-CS0fA2GT.mjs} +2 -2
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-B4c_WjXD.mjs → json-viewer-Dg8rqrxL.mjs} +9 -5
  25. package/.output/server/_ssr/{router-DVixpJO-.mjs → router-D_Boe9Bu.mjs} +3 -3
  26. package/.output/server/{_tanstack-start-manifest_v-BbvWUF4v.mjs → _tanstack-start-manifest_v-KFXyNRGC.mjs} +1 -1
  27. package/.output/server/index.mjs +65 -65
  28. package/package.json +2 -1
  29. package/src/cli/detect-tools.ts +146 -0
  30. package/src/cli/onboard.ts +229 -0
  31. package/src/cli/templates/command-onboard.ts +17 -0
  32. package/src/cli/templates/skill-onboard.ts +458 -0
  33. package/src/cli.ts +193 -163
  34. package/src/components/ProxyViewer.tsx +153 -142
  35. package/src/components/proxy-viewer/LogEntry.tsx +136 -157
  36. package/src/components/proxy-viewer/LogEntryHeader.tsx +147 -66
  37. package/src/components/proxy-viewer/useCopyFeedback.ts +36 -0
  38. package/src/components/ui/json-viewer.tsx +12 -0
  39. package/.output/public/assets/ProxyViewerContainer-WRenRpeh.js +0 -101
  40. package/.output/public/assets/_sessionId-BO47oA3Z.js +0 -1
  41. package/.output/public/assets/index-BRvz6-L6.css +0 -1
  42. package/.output/public/assets/index-Btw8ec7-.js +0 -1
@@ -303,165 +303,176 @@ export function ProxyViewer({
303
303
 
304
304
  return (
305
305
  <div className="max-w-[1400px] xl:max-w-[1600px] 2xl:max-w-[1800px] mx-auto px-6 pb-6">
306
- {/* Brand row */}
307
- <div className="grid grid-cols-[1fr_auto_1fr] items-start gap-3 pt-6 pb-8">
308
- <div />
309
- <h1 className="flex min-w-0 flex-col items-center gap-2 text-center">
310
- <span className="flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap">
311
- {/* Crab family — hover to animate together */}
312
- <span className="flex shrink-0 items-end gap-1 group cursor-default" aria-hidden="true">
313
- <CrabLogo className="size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" />
314
- <span className="hidden items-end gap-0.5 sm:flex">
315
- {crabVariants.map((Crab, i) => {
316
- const color = [
317
- "text-amber-500",
318
- "text-rose-500",
319
- "text-sky-500",
320
- "text-emerald-500",
321
- "text-violet-500",
322
- "text-orange-500",
323
- "text-cyan-500",
324
- "text-pink-500",
325
- "text-lime-500",
326
- "text-blue-500",
327
- "text-yellow-500",
328
- "text-fuchsia-500",
329
- ][i];
330
- const entranceClass =
331
- crabEntrancePhase === "hidden"
332
- ? "opacity-0 scale-0"
333
- : crabEntrancePhase === "playing"
334
- ? "animate-crab-piano-pop"
335
- : "";
336
- return (
337
- <Crab
338
- key={i}
339
- className={`size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`}
340
- style={{
341
- transitionDelay: `${i * 50}ms`,
342
- ...(crabEntrancePhase === "playing"
343
- ? { animationDelay: `${i * 400}ms` }
344
- : {}),
345
- }}
346
- />
347
- );
348
- })}
306
+ {/* Sticky chrome — brand row, optional pinned-session context, and the
307
+ filter/control bar stay visible while the user scrolls through the
308
+ log list. `z-30` sits above the per-conversation sticky header
309
+ (`z-10`) but below modal-level overlays (dialogs, dropdowns,
310
+ tooltips `z-50`). Background is fully opaque so logs don't bleed
311
+ through. */}
312
+ <div className="sticky top-0 z-30 bg-background pt-6">
313
+ {/* Brand row */}
314
+ <div className="grid grid-cols-[1fr_auto_1fr] items-start gap-3 pb-8">
315
+ <div />
316
+ <h1 className="flex min-w-0 flex-col items-center gap-2 text-center">
317
+ <span className="flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap">
318
+ {/* Crab family — hover to animate together */}
319
+ <span
320
+ className="flex shrink-0 items-end gap-1 group cursor-default"
321
+ aria-hidden="true"
322
+ >
323
+ <CrabLogo className="size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" />
324
+ <span className="hidden items-end gap-0.5 sm:flex">
325
+ {crabVariants.map((Crab, i) => {
326
+ const color = [
327
+ "text-amber-500",
328
+ "text-rose-500",
329
+ "text-sky-500",
330
+ "text-emerald-500",
331
+ "text-violet-500",
332
+ "text-orange-500",
333
+ "text-cyan-500",
334
+ "text-pink-500",
335
+ "text-lime-500",
336
+ "text-blue-500",
337
+ "text-yellow-500",
338
+ "text-fuchsia-500",
339
+ ][i];
340
+ const entranceClass =
341
+ crabEntrancePhase === "hidden"
342
+ ? "opacity-0 scale-0"
343
+ : crabEntrancePhase === "playing"
344
+ ? "animate-crab-piano-pop"
345
+ : "";
346
+ return (
347
+ <Crab
348
+ key={i}
349
+ className={`size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`}
350
+ style={{
351
+ transitionDelay: `${i * 50}ms`,
352
+ ...(crabEntrancePhase === "playing"
353
+ ? { animationDelay: `${i * 400}ms` }
354
+ : {}),
355
+ }}
356
+ />
357
+ );
358
+ })}
359
+ </span>
349
360
  </span>
350
- </span>
351
- <span className="flex min-w-0 items-baseline gap-2 pl-1">
352
- <span className="truncate text-lg font-bold">LLM Inspector</span>
353
- <span className="shrink-0 font-mono text-xs font-semibold text-muted-foreground">
354
- v{packageJson.version}
361
+ <span className="flex min-w-0 items-baseline gap-2 pl-1">
362
+ <span className="truncate text-lg font-bold">LLM Inspector</span>
363
+ <span className="shrink-0 font-mono text-xs font-semibold text-muted-foreground">
364
+ v{packageJson.version}
365
+ </span>
355
366
  </span>
367
+ <Plus className="size-4 shrink-0 text-muted-foreground/70" aria-hidden="true" />
368
+ <McpLogo className="size-10 shrink-0" />
356
369
  </span>
357
- <Plus className="size-4 shrink-0 text-muted-foreground/70" aria-hidden="true" />
358
- <McpLogo className="size-10 shrink-0" />
359
- </span>
360
- <McpReadyBadge />
361
- </h1>
362
- <div className="justify-self-end">
363
- <SettingsDialog />
370
+ <McpReadyBadge />
371
+ </h1>
372
+ <div className="justify-self-end">
373
+ <SettingsDialog />
374
+ </div>
364
375
  </div>
365
- </div>
366
376
 
367
- {pinnedSessionId !== undefined && (
368
- <SessionContextBar
369
- sessionId={pinnedSessionId}
370
- logs={logs}
371
- totalIn={totalIn}
372
- totalOut={totalOut}
373
- />
374
- )}
377
+ {pinnedSessionId !== undefined && (
378
+ <SessionContextBar
379
+ sessionId={pinnedSessionId}
380
+ logs={logs}
381
+ totalIn={totalIn}
382
+ totalOut={totalOut}
383
+ />
384
+ )}
375
385
 
376
- {/* Controls + Filters */}
377
- <div className="flex items-center gap-3 mb-4">
378
- {!hideSessionFilter && (
379
- <Select value={selectedSession} onValueChange={onSessionChange}>
380
- <SelectTrigger className="flex-1 max-w-[350px] text-xs">
381
- <SelectValue placeholder="All sessions" />
386
+ {/* Controls + Filters */}
387
+ <div className="flex items-center gap-3 mb-4">
388
+ {!hideSessionFilter && (
389
+ <Select value={selectedSession} onValueChange={onSessionChange}>
390
+ <SelectTrigger className="flex-1 max-w-[350px] text-xs">
391
+ <SelectValue placeholder="All sessions" />
392
+ </SelectTrigger>
393
+ <SelectContent>
394
+ <SelectItem value="__all__">All sessions</SelectItem>
395
+ {sessions.map((s) => (
396
+ <SelectItem key={s} value={s}>
397
+ {truncateSessionId(s)}
398
+ </SelectItem>
399
+ ))}
400
+ </SelectContent>
401
+ </Select>
402
+ )}
403
+ <Select value={selectedModel} onValueChange={onModelChange}>
404
+ <SelectTrigger className="flex-1 max-w-[250px] text-xs">
405
+ <SelectValue placeholder="All models" />
382
406
  </SelectTrigger>
383
407
  <SelectContent>
384
- <SelectItem value="__all__">All sessions</SelectItem>
385
- {sessions.map((s) => (
386
- <SelectItem key={s} value={s}>
387
- {truncateSessionId(s)}
408
+ <SelectItem value="__all__">All models</SelectItem>
409
+ {models.map((m) => (
410
+ <SelectItem key={m} value={m}>
411
+ {m}
388
412
  </SelectItem>
389
413
  ))}
390
414
  </SelectContent>
391
415
  </Select>
392
- )}
393
- <Select value={selectedModel} onValueChange={onModelChange}>
394
- <SelectTrigger className="flex-1 max-w-[250px] text-xs">
395
- <SelectValue placeholder="All models" />
396
- </SelectTrigger>
397
- <SelectContent>
398
- <SelectItem value="__all__">All models</SelectItem>
399
- {models.map((m) => (
400
- <SelectItem key={m} value={m}>
401
- {m}
402
- </SelectItem>
403
- ))}
404
- </SelectContent>
405
- </Select>
406
- <div className="flex items-center border border-border rounded-md overflow-hidden">
407
- <button
408
- type="button"
409
- onClick={() => onViewModeChange("simple")}
410
- className={`h-8 px-3 cursor-pointer transition-colors text-xs ${
411
- viewMode === "simple"
412
- ? "bg-muted text-foreground"
413
- : "text-muted-foreground hover:bg-muted/50"
414
- }`}
415
- >
416
- Simple
417
- </button>
416
+ <div className="flex items-center border border-border rounded-md overflow-hidden">
417
+ <button
418
+ type="button"
419
+ onClick={() => onViewModeChange("simple")}
420
+ className={`h-8 px-3 cursor-pointer transition-colors text-xs ${
421
+ viewMode === "simple"
422
+ ? "bg-muted text-foreground"
423
+ : "text-muted-foreground hover:bg-muted/50"
424
+ }`}
425
+ >
426
+ Simple
427
+ </button>
428
+ <button
429
+ type="button"
430
+ onClick={() => onViewModeChange("full")}
431
+ className={`h-8 px-3 cursor-pointer transition-colors text-xs ${
432
+ viewMode === "full"
433
+ ? "bg-muted text-foreground"
434
+ : "text-muted-foreground hover:bg-muted/50"
435
+ }`}
436
+ >
437
+ Full
438
+ </button>
439
+ </div>
440
+ <div className="flex-1" />
441
+ <span className="text-muted-foreground text-xs font-mono">
442
+ {logs.length} request{logs.length !== 1 ? "s" : ""}
443
+ {totalIn > 0 || totalOut > 0
444
+ ? ` · ${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out`
445
+ : ""}
446
+ </span>
447
+ {logs.length > 0 && (
448
+ <button
449
+ type="button"
450
+ onClick={() => {
451
+ void handleExport();
452
+ }}
453
+ disabled={exporting}
454
+ className="h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5 rounded-md hover:bg-muted"
455
+ title="Export all logs as JSON ZIP"
456
+ >
457
+ {exporting ? (
458
+ <span>Exporting...</span>
459
+ ) : (
460
+ <>
461
+ <Download className="size-3.5" />
462
+ <span>Export</span>
463
+ </>
464
+ )}
465
+ </button>
466
+ )}
418
467
  <button
419
468
  type="button"
420
- onClick={() => onViewModeChange("full")}
421
- className={`h-8 px-3 cursor-pointer transition-colors text-xs ${
422
- viewMode === "full"
423
- ? "bg-muted text-foreground"
424
- : "text-muted-foreground hover:bg-muted/50"
425
- }`}
469
+ onClick={onClearAll}
470
+ className="h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer rounded-md hover:bg-muted"
471
+ title="Clear all logs"
426
472
  >
427
- Full
473
+ Clear
428
474
  </button>
429
475
  </div>
430
- <div className="flex-1" />
431
- <span className="text-muted-foreground text-xs font-mono">
432
- {logs.length} request{logs.length !== 1 ? "s" : ""}
433
- {totalIn > 0 || totalOut > 0
434
- ? ` · ${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out`
435
- : ""}
436
- </span>
437
- {logs.length > 0 && (
438
- <button
439
- type="button"
440
- onClick={() => {
441
- void handleExport();
442
- }}
443
- disabled={exporting}
444
- className="h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5 rounded-md hover:bg-muted"
445
- title="Export all logs as JSON ZIP"
446
- >
447
- {exporting ? (
448
- <span>Exporting...</span>
449
- ) : (
450
- <>
451
- <Download className="size-3.5" />
452
- <span>Export</span>
453
- </>
454
- )}
455
- </button>
456
- )}
457
- <button
458
- type="button"
459
- onClick={onClearAll}
460
- className="h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer rounded-md hover:bg-muted"
461
- title="Clear all logs"
462
- >
463
- Clear
464
- </button>
465
476
  </div>
466
477
 
467
478
  {/* Log list */}