@tonyclaw/agent-inspector 3.0.18 → 3.0.20
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 +1257 -506
- package/.output/nitro.json +3 -3
- package/.output/public/assets/CompareDrawer-CV7rBBty.js +1 -0
- package/.output/public/assets/ProxyViewerContainer-CqRfBWJ3.js +127 -0
- package/.output/public/assets/ReplayDialog-yDeF-z9y.js +1 -0
- package/.output/public/assets/RequestAnatomy-BXxKNdgk.js +1 -0
- package/.output/public/assets/ResponseView-BIYNgUNB.js +3 -0
- package/.output/public/assets/StreamingChunkSequence-C1faiChu.js +1 -0
- package/.output/public/assets/_sessionId-B34PrfCQ.js +1 -0
- package/.output/public/assets/index-B3YOj8-_.css +1 -0
- package/.output/public/assets/index-BLwqIxup.js +12 -0
- package/.output/public/assets/{index-ClQ7Vi3J.js → index-DOspyaW6.js} +1 -1
- package/.output/public/assets/index-DT476lno.js +1 -0
- package/.output/public/assets/json-viewer-C1S08J_2.js +1 -0
- package/.output/server/_chunks/ssr-renderer.mjs +18 -2
- package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +165 -31
- package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +1 -1
- package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +2 -2
- package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +28 -1
- package/.output/server/_libs/mistralai__mistralai.mjs +1 -1
- package/.output/server/_libs/modelcontextprotocol__server.mjs +11571 -8127
- package/.output/server/_libs/radix-ui__primitive.mjs +1 -1
- package/.output/server/_libs/radix-ui__react-collapsible.mjs +1 -1
- package/.output/server/_libs/radix-ui__react-collection.mjs +8 -8
- package/.output/server/_libs/radix-ui__react-compose-refs.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-context.mjs +4 -18
- package/.output/server/_libs/radix-ui__react-dialog.mjs +41 -88
- package/.output/server/_libs/radix-ui__react-focus-guards.mjs +15 -5
- package/.output/server/_libs/radix-ui__react-focus-scope.mjs +6 -2
- package/.output/server/_libs/radix-ui__react-id.mjs +3 -3
- package/.output/server/_libs/radix-ui__react-popper.mjs +62 -8
- package/.output/server/_libs/radix-ui__react-portal.mjs +2 -2
- package/.output/server/_libs/radix-ui__react-presence.mjs +50 -5
- package/.output/server/_libs/radix-ui__react-roving-focus.mjs +15 -4
- package/.output/server/_libs/radix-ui__react-scroll-area.mjs +30 -20
- package/.output/server/_libs/radix-ui__react-select.mjs +167 -123
- package/.output/server/_libs/radix-ui__react-slot.mjs +74 -41
- package/.output/server/_libs/radix-ui__react-tabs.mjs +6 -1
- package/.output/server/_libs/radix-ui__react-tooltip.mjs +6 -3
- package/.output/server/_libs/react-dom.mjs +13 -15
- package/.output/server/_libs/react.mjs +45 -45
- package/.output/server/_libs/srvx.mjs +409 -53
- package/.output/server/_libs/swr.mjs +10 -2
- package/.output/server/_libs/tailwind-merge.mjs +286 -41
- package/.output/server/_libs/tanstack__history.mjs +9 -13
- package/.output/server/_libs/tanstack__react-router.mjs +788 -1092
- package/.output/server/_libs/tanstack__react-store.mjs +1 -1
- package/.output/server/_libs/tanstack__react-virtual.mjs +82 -7
- package/.output/server/_libs/tanstack__router-core.mjs +1841 -2060
- package/.output/server/_libs/tanstack__virtual-core.mjs +116 -45
- package/.output/server/_libs/use-sync-external-store.mjs +1 -1
- package/.output/server/_libs/zod-to-json-schema.mjs +1 -1
- package/.output/server/_libs/zod.mjs +5085 -200
- package/.output/server/{_sessionId-Bi3ZIrqT.mjs → _sessionId-CmoBbtKT.mjs} +30 -32
- package/.output/server/_ssr/{CompareDrawer-BYZS_hCR.mjs → CompareDrawer-BsXnVK4e.mjs} +29 -31
- package/.output/server/_ssr/{ProxyViewerContainer-BE4umfVs.mjs → ProxyViewerContainer-D-_7pyfx.mjs} +54 -52
- package/.output/server/_ssr/{ReplayDialog-BlID2ZH4.mjs → ReplayDialog-deDQA2M5.mjs} +30 -32
- package/.output/server/_ssr/{RequestAnatomy-CGJ2lkzT.mjs → RequestAnatomy-CX-cCRdX.mjs} +29 -31
- package/.output/server/_ssr/{ResponseView-DDjcGf7M.mjs → ResponseView-D0LJR1qj.mjs} +29 -31
- package/.output/server/_ssr/{StreamingChunkSequence-G7B8M5G2.mjs → StreamingChunkSequence-CJRECetE.mjs} +29 -31
- package/.output/server/_ssr/empty-plugin-adapters-BFgPZ6_d.mjs +6 -0
- package/.output/server/_ssr/{index-ClQ9FvRJ.mjs → index-CYWOXS4d.mjs} +31 -33
- package/.output/server/_ssr/index.mjs +1102 -784
- package/.output/server/_ssr/{json-viewer-CFbpkiBJ.mjs → json-viewer-CCneYiCn.mjs} +29 -31
- package/.output/server/_ssr/{router-Cq8LEvhE.mjs → router-D-PAN8IK.mjs} +182 -67
- package/.output/server/_tanstack-start-manifest_v-CVZ9yh4R.mjs +4 -0
- package/.output/server/index.mjs +74 -92
- package/README.md +4 -4
- package/package.json +17 -8
- package/src/cli/onboard.ts +1 -1
- package/src/cli.ts +2 -2
- package/src/components/clients/ClientLogo.tsx +5 -1
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +1 -0
- package/src/components/proxy-viewer/ResponseView.tsx +1 -0
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +1 -0
- package/src/components/proxy-viewer/TurnGroup.tsx +1 -0
- package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +1 -0
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +4 -0
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +1 -0
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +2 -0
- package/src/components/ui/dialog.tsx +1 -1
- package/src/components/ui/json-viewer.tsx +2 -0
- package/src/components/ui/select.tsx +1 -1
- package/src/contracts/json.ts +7 -2
- package/src/lib/providerContract.ts +25 -5
- package/src/mcp/server.ts +12 -3
- package/src/mcp/toolHandlers.ts +6 -1
- package/src/proxy/handler.ts +17 -7
- package/src/proxy/logFinalizer.ts +76 -8
- package/src/proxy/logIndex.ts +16 -32
- package/src/proxy/providers.ts +22 -1
- package/src/proxy/sessionProcess.ts +15 -1
- package/src/proxy/upstream.ts +4 -2
- package/src/router.tsx +9 -0
- package/.output/public/assets/CompareDrawer-Daj03-RL.js +0 -1
- package/.output/public/assets/ProxyViewerContainer-BLCRQtU_.js +0 -106
- package/.output/public/assets/ReplayDialog-5FR5crdo.js +0 -1
- package/.output/public/assets/RequestAnatomy-DF0hHdQY.js +0 -1
- package/.output/public/assets/ResponseView-BwkMSsUa.js +0 -3
- package/.output/public/assets/StreamingChunkSequence-CKeUd58x.js +0 -1
- package/.output/public/assets/_sessionId-CThgXa41.js +0 -1
- package/.output/public/assets/index-BHXN1Fsx.css +0 -1
- package/.output/public/assets/index-gm3A0idN.js +0 -1
- package/.output/public/assets/json-viewer-BA-W-PZq.js +0 -1
- package/.output/public/assets/main-BnRC1932.js +0 -18
- package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +0 -17
- package/.output/server/_libs/cfworker__json-schema.mjs +0 -1
- package/.output/server/_libs/tiny-invariant.mjs +0 -12
- package/.output/server/_libs/tiny-warning.mjs +0 -5
- package/.output/server/_tanstack-start-manifest_v-p7J0QCMh.mjs +0 -4
package/src/proxy/handler.ts
CHANGED
|
@@ -197,14 +197,24 @@ function handleStreamingResponse(
|
|
|
197
197
|
return new Response("No response body", { status: STATUS_BAD_GATEWAY });
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
scheduleStreamingFinalization("stream-abort");
|
|
206
|
-
}
|
|
200
|
+
// pipeTo returns a promise that rejects when either side errors. Without
|
|
201
|
+
// this catch, an upstream connection drop skips flush() and leaves the log
|
|
202
|
+
// permanently un-finalized with a leaked temp file.
|
|
203
|
+
void upstreamRes.body.pipeTo(transform.writable).catch(() => {
|
|
204
|
+
scheduleStreamingFinalization("streaming");
|
|
207
205
|
});
|
|
206
|
+
const loggedStream = transform.readable;
|
|
207
|
+
|
|
208
|
+
if (req.signal === undefined) {
|
|
209
|
+
logger.warn("[handler] req.signal is undefined; abort detection disabled for this request");
|
|
210
|
+
} else {
|
|
211
|
+
req.signal.addEventListener("abort", () => {
|
|
212
|
+
if (log.responseText === null) {
|
|
213
|
+
logger.info(`[handler] Streaming client aborted: ${log.method} ${log.path}`);
|
|
214
|
+
scheduleStreamingFinalization("stream-abort");
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
208
218
|
|
|
209
219
|
const responseHeaders = new Headers(upstreamRes.headers);
|
|
210
220
|
responseHeaders.delete(HEADER_CONTENT_ENCODING);
|
|
@@ -10,6 +10,7 @@ import { getSessionProcess, isSessionProcessAvailable } from "./sessionProcess";
|
|
|
10
10
|
import { finalizeLogUpdate } from "./store";
|
|
11
11
|
import { computeTokensPerSecond } from "./streamTiming";
|
|
12
12
|
import { analyzeToolSchemaWarnings } from "./toolSchemaWarnings";
|
|
13
|
+
import { isPlainRecord, safeGetOwnProperty } from "../lib/objectUtils";
|
|
13
14
|
|
|
14
15
|
type BaseFinalizeLogJob = {
|
|
15
16
|
log: CapturedLog;
|
|
@@ -151,6 +152,11 @@ function applyOptionalStreamingTiming(log: CapturedLog, job: FinalizeLogJob): vo
|
|
|
151
152
|
totalStreamMs: job.totalStreamMs,
|
|
152
153
|
});
|
|
153
154
|
return;
|
|
155
|
+
default: {
|
|
156
|
+
const _exhaustive: never = job;
|
|
157
|
+
logger.error(`[logFinalizer] Unhandled job type: ${JSON.stringify(_exhaustive)}`);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
154
160
|
}
|
|
155
161
|
}
|
|
156
162
|
|
|
@@ -241,6 +247,11 @@ function cleanupPathForRawStream(rawStream: string | RawStreamSource): string |
|
|
|
241
247
|
return null;
|
|
242
248
|
case "file":
|
|
243
249
|
return rawStream.path;
|
|
250
|
+
default: {
|
|
251
|
+
const _exhaustive: never = rawStream;
|
|
252
|
+
logger.error(`[logFinalizer] Unhandled raw stream type: ${JSON.stringify(_exhaustive)}`);
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
244
255
|
}
|
|
245
256
|
}
|
|
246
257
|
|
|
@@ -263,6 +274,11 @@ function rawStreamByteLength(rawStream: string | RawStreamSource): number | null
|
|
|
263
274
|
} catch {
|
|
264
275
|
return null;
|
|
265
276
|
}
|
|
277
|
+
default: {
|
|
278
|
+
const _exhaustive: never = rawStream;
|
|
279
|
+
logger.error(`[logFinalizer] Unhandled raw stream type: ${JSON.stringify(_exhaustive)}`);
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
266
282
|
}
|
|
267
283
|
}
|
|
268
284
|
|
|
@@ -304,6 +320,11 @@ function readRawStream(rawStream: string | RawStreamSource): {
|
|
|
304
320
|
rawStreamText: readFileSync(rawStream.path, "utf-8"),
|
|
305
321
|
cleanupRawStreamPath: rawStream.path,
|
|
306
322
|
};
|
|
323
|
+
default: {
|
|
324
|
+
const _exhaustive: never = rawStream;
|
|
325
|
+
logger.error(`[logFinalizer] Unhandled raw stream type: ${JSON.stringify(_exhaustive)}`);
|
|
326
|
+
return { rawStreamText: "", cleanupRawStreamPath: null };
|
|
327
|
+
}
|
|
307
328
|
}
|
|
308
329
|
}
|
|
309
330
|
|
|
@@ -423,6 +444,7 @@ function finalizeStreamAbort(job: FinalizeStreamAbortLogJob, log: CapturedLog):
|
|
|
423
444
|
|
|
424
445
|
export function buildFinalizeLogResult(job: FinalizeLogJob): FinalizeLogResult {
|
|
425
446
|
const log = cloneLog(job.log);
|
|
447
|
+
const upstreamUrl = job.upstreamUrl;
|
|
426
448
|
|
|
427
449
|
switch (job.type) {
|
|
428
450
|
case "non-streaming":
|
|
@@ -431,6 +453,16 @@ export function buildFinalizeLogResult(job: FinalizeLogJob): FinalizeLogResult {
|
|
|
431
453
|
return finalizeStreaming(job, log);
|
|
432
454
|
case "stream-abort":
|
|
433
455
|
return finalizeStreamAbort(job, log);
|
|
456
|
+
default: {
|
|
457
|
+
const _exhaustive: never = job;
|
|
458
|
+
logger.error(`[logFinalizer] Unhandled job type: ${JSON.stringify(_exhaustive)}`);
|
|
459
|
+
return {
|
|
460
|
+
log,
|
|
461
|
+
upstreamUrl,
|
|
462
|
+
error: "Unhandled job type",
|
|
463
|
+
cleanupRawStreamPath: null,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
434
466
|
}
|
|
435
467
|
}
|
|
436
468
|
|
|
@@ -504,12 +536,25 @@ function resolveBuildPromise(job: FinalizeLogJob): Promise<FinalizeLogResult> {
|
|
|
504
536
|
return executeBuildInWorker(job);
|
|
505
537
|
case "inline":
|
|
506
538
|
return Promise.resolve(buildFinalizeLogResult(job));
|
|
539
|
+
default: {
|
|
540
|
+
const _exhaustive: never = RUNTIME;
|
|
541
|
+
logger.error(`[logFinalizer] Unhandled runtime: ${JSON.stringify(_exhaustive)}`);
|
|
542
|
+
return Promise.resolve({
|
|
543
|
+
log: job.log,
|
|
544
|
+
upstreamUrl: job.upstreamUrl,
|
|
545
|
+
error: "Unhandled runtime",
|
|
546
|
+
cleanupRawStreamPath: null,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
507
549
|
}
|
|
508
550
|
}
|
|
509
551
|
|
|
510
552
|
const WORKER_COUNT = Math.max(1, Number(process.env["FINALIZER_WORKER_COUNT"]) || 4);
|
|
511
553
|
let _workers: Worker[] | null = null;
|
|
512
|
-
const _pending = new Map<
|
|
554
|
+
const _pending = new Map<
|
|
555
|
+
string,
|
|
556
|
+
{ resolve: (result: FinalizeLogResult) => void; reject: (err: Error) => void }
|
|
557
|
+
>();
|
|
513
558
|
let _nextWorker = 0;
|
|
514
559
|
let _jobSeq = 0;
|
|
515
560
|
|
|
@@ -518,11 +563,24 @@ function getWorkers(): Worker[] {
|
|
|
518
563
|
_workers = [];
|
|
519
564
|
for (let i = 0; i < WORKER_COUNT; i++) {
|
|
520
565
|
const w = new Worker(new URL("./logFinalizer.worker.ts", import.meta.url));
|
|
521
|
-
w.on("message", (
|
|
522
|
-
|
|
523
|
-
|
|
566
|
+
w.on("message", (raw: unknown) => {
|
|
567
|
+
if (!isPlainRecord(raw)) {
|
|
568
|
+
logger.error("[logFinalizer] Received malformed IPC message from worker, discarding");
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const id = safeGetOwnProperty(raw, "id");
|
|
572
|
+
const result = safeGetOwnProperty(raw, "result");
|
|
573
|
+
if (typeof id !== "string" || !isPlainRecord(result)) {
|
|
574
|
+
logger.error("[logFinalizer] Received malformed IPC message from worker, discarding");
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
// Runtime shape validated above; the cast bridges unknown → FinalizeLogResult.
|
|
578
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
579
|
+
const msg = { id, result: result as FinalizeLogResult };
|
|
580
|
+
const entry = _pending.get(msg.id);
|
|
581
|
+
if (entry !== undefined) {
|
|
524
582
|
_pending.delete(msg.id);
|
|
525
|
-
resolve(msg.result);
|
|
583
|
+
entry.resolve(msg.result);
|
|
526
584
|
}
|
|
527
585
|
});
|
|
528
586
|
w.on("error", (err) => {
|
|
@@ -530,6 +588,13 @@ function getWorkers(): Worker[] {
|
|
|
530
588
|
"[logFinalizer] Worker error:",
|
|
531
589
|
err instanceof Error ? err.message : String(err),
|
|
532
590
|
);
|
|
591
|
+
// Reject all pending jobs for this worker so callers don't hang forever
|
|
592
|
+
for (const [id, entry] of _pending) {
|
|
593
|
+
_pending.delete(id);
|
|
594
|
+
entry.reject(
|
|
595
|
+
new Error(`Worker crashed: ${err instanceof Error ? err.message : String(err)}`),
|
|
596
|
+
);
|
|
597
|
+
}
|
|
533
598
|
});
|
|
534
599
|
_workers.push(w);
|
|
535
600
|
}
|
|
@@ -537,15 +602,18 @@ function getWorkers(): Worker[] {
|
|
|
537
602
|
}
|
|
538
603
|
|
|
539
604
|
export function executeBuildInWorker(job: FinalizeLogJob): Promise<FinalizeLogResult> {
|
|
540
|
-
return new Promise((resolve) => {
|
|
605
|
+
return new Promise((resolve, reject) => {
|
|
541
606
|
const list = getWorkers();
|
|
542
607
|
// getWorkers() always returns at least 1 worker; the array index is safe
|
|
543
608
|
const idx = _nextWorker % list.length;
|
|
544
609
|
_nextWorker++;
|
|
545
610
|
const w = list[idx];
|
|
546
|
-
if (w === undefined)
|
|
611
|
+
if (w === undefined) {
|
|
612
|
+
reject(new Error("No workers available"));
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
547
615
|
const id = String(++_jobSeq);
|
|
548
|
-
_pending.set(id, resolve);
|
|
616
|
+
_pending.set(id, { resolve, reject });
|
|
549
617
|
w.postMessage({ id, job });
|
|
550
618
|
});
|
|
551
619
|
}
|
package/src/proxy/logIndex.ts
CHANGED
|
@@ -468,38 +468,24 @@ export async function rebuildIndex(): Promise<LogIndex> {
|
|
|
468
468
|
return newIndex;
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
-
//
|
|
472
|
-
//
|
|
473
|
-
|
|
474
|
-
let
|
|
475
|
-
|
|
476
|
-
async function
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
idGenerationPromise = new Promise<void>((resolve) => {
|
|
486
|
-
releaseLock = resolve;
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
function releaseLockFn(): void {
|
|
492
|
-
if (releaseLock) {
|
|
493
|
-
const resolve = releaseLock;
|
|
494
|
-
releaseLock = null;
|
|
495
|
-
idGenerationPromise = null;
|
|
496
|
-
resolve();
|
|
497
|
-
}
|
|
471
|
+
// Promise-chaining queue for atomic ID generation. Each caller chains on the
|
|
472
|
+
// previous promise, guaranteeing mutual exclusion without the broken
|
|
473
|
+
// releaseLock pattern that allowed concurrent entry when 3+ callers waited.
|
|
474
|
+
let idGenerationChain: Promise<void> = Promise.resolve();
|
|
475
|
+
|
|
476
|
+
async function withIdLock<T>(fn: () => Promise<T>): Promise<T> {
|
|
477
|
+
const run = idGenerationChain.then(fn);
|
|
478
|
+
// Keep the chain alive regardless of success/failure so the next caller
|
|
479
|
+
// never sees a rejected antecedent.
|
|
480
|
+
idGenerationChain = run.then(
|
|
481
|
+
() => {},
|
|
482
|
+
() => {},
|
|
483
|
+
);
|
|
484
|
+
return run;
|
|
498
485
|
}
|
|
499
486
|
|
|
500
487
|
export async function getNextLogId(): Promise<number> {
|
|
501
|
-
|
|
502
|
-
try {
|
|
488
|
+
return withIdLock(async () => {
|
|
503
489
|
const index = await loadIndex();
|
|
504
490
|
const sqliteMaxId = await getSqliteLogIndexMaxId();
|
|
505
491
|
const nextId = Math.max(index.maxId, sqliteMaxId ?? 0) + 1;
|
|
@@ -507,9 +493,7 @@ export async function getNextLogId(): Promise<number> {
|
|
|
507
493
|
// Synchronously update the index in memory (disk write is deferred via batching)
|
|
508
494
|
cachedIndex = index;
|
|
509
495
|
return nextId;
|
|
510
|
-
}
|
|
511
|
-
releaseLockFn();
|
|
512
|
-
}
|
|
496
|
+
});
|
|
513
497
|
}
|
|
514
498
|
|
|
515
499
|
export function getCurrentLogFile(): string {
|
package/src/proxy/providers.ts
CHANGED
|
@@ -379,7 +379,28 @@ if (providersJson !== undefined) {
|
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
export function getProviders(): ProviderConfig[] {
|
|
382
|
-
|
|
382
|
+
const raw = store.get("providers", []);
|
|
383
|
+
// Validate the on-disk store entry-by-entry so a single corrupted record
|
|
384
|
+
// cannot wipe out the entire provider list. The whole-store safeParse was
|
|
385
|
+
// too strict — once any legacy/in-flight provider failed validation the
|
|
386
|
+
// whole list was discarded, which broke `updateProvider` and routing.
|
|
387
|
+
const valid: ProviderConfig[] = [];
|
|
388
|
+
for (const candidate of raw) {
|
|
389
|
+
const entry = ProviderConfigSchema.safeParse(candidate);
|
|
390
|
+
if (entry.success) {
|
|
391
|
+
valid.push(entry.data);
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
let candidateName = "<unknown>";
|
|
395
|
+
if (typeof candidate === "object" && candidate !== null) {
|
|
396
|
+
const nameResult = z.object({ name: z.string() }).safeParse(candidate);
|
|
397
|
+
if (nameResult.success) candidateName = nameResult.data.name;
|
|
398
|
+
}
|
|
399
|
+
logger.warn(
|
|
400
|
+
`[providers] Skipping invalid on-disk provider "${candidateName}": ${entry.error.message}`,
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
return valid;
|
|
383
404
|
}
|
|
384
405
|
|
|
385
406
|
export function getProvider(id: string): ProviderConfig | undefined {
|
|
@@ -3,6 +3,7 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { logger } from "./logger";
|
|
5
5
|
import type { FinalizeLogJob, FinalizeLogResult } from "./logFinalizer";
|
|
6
|
+
import { isPlainRecord, safeGetOwnProperty } from "../lib/objectUtils";
|
|
6
7
|
|
|
7
8
|
const IDLE_TIMEOUT_MS = Number(process.env["SESSION_PROCESS_IDLE_MS"]) || 5 * 60 * 1000; // 5 min default
|
|
8
9
|
const MAX_RESTARTS = 3;
|
|
@@ -53,7 +54,20 @@ export class SessionProcess {
|
|
|
53
54
|
|
|
54
55
|
this.restartCount += 1;
|
|
55
56
|
|
|
56
|
-
this.child.on("message", (
|
|
57
|
+
this.child.on("message", (raw: unknown) => {
|
|
58
|
+
if (!isPlainRecord(raw)) {
|
|
59
|
+
logger.error("[sessionProcess] Received malformed IPC message from worker, discarding");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const id = safeGetOwnProperty(raw, "id");
|
|
63
|
+
const result = safeGetOwnProperty(raw, "result");
|
|
64
|
+
if (typeof id !== "string" || !isPlainRecord(result)) {
|
|
65
|
+
logger.error("[sessionProcess] Received malformed IPC message from worker, discarding");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Runtime shape validated above; the cast bridges unknown → FinalizeLogResult.
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
70
|
+
const msg = { id, result: result as FinalizeLogResult };
|
|
57
71
|
const pending = this.pending.get(msg.id);
|
|
58
72
|
if (pending !== undefined) {
|
|
59
73
|
this.pending.delete(msg.id);
|
package/src/proxy/upstream.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ProviderConfig } from "./providers";
|
|
2
|
+
import { logger } from "./logger";
|
|
2
3
|
import {
|
|
3
4
|
AUTH_HEADER_X_API_KEY,
|
|
4
5
|
DEFAULT_OPENAI_UPSTREAM,
|
|
@@ -77,8 +78,9 @@ export function setUpstreamHost(headers: Headers, upstreamBase: string): void {
|
|
|
77
78
|
try {
|
|
78
79
|
headers.set(HEADER_HOST, new URL(upstreamBase).host);
|
|
79
80
|
} catch {
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
logger.warn(
|
|
82
|
+
`[upstream] Invalid provider URL "${upstreamBase}" — falling back to api.anthropic.com for Host header`,
|
|
83
|
+
);
|
|
82
84
|
headers.set(HEADER_HOST, "api.anthropic.com");
|
|
83
85
|
}
|
|
84
86
|
}
|
package/src/router.tsx
CHANGED
|
@@ -13,6 +13,15 @@ export function getRouter() {
|
|
|
13
13
|
function attachServerSsrStoreCompatibility(
|
|
14
14
|
router: Readonly<{ state: Readonly<{ matches: unknown }> }>,
|
|
15
15
|
): void {
|
|
16
|
+
// TanStack >= 1.168 refactored `state` from an own data property into a
|
|
17
|
+
// prototype getter backed by lazily-created `stores` (`this.stores.__store`).
|
|
18
|
+
// Installing a `stores` value here would shadow that lazy init
|
|
19
|
+
// (`if (!this.stores && this.latestLocation) { this.stores = … }`) and break
|
|
20
|
+
// `router.state`. When `state` is already a prototype accessor the new store
|
|
21
|
+
// architecture handles SSR natively and this shim is obsolete.
|
|
22
|
+
const stateDesc = Object.getOwnPropertyDescriptor(router, "state");
|
|
23
|
+
if (stateDesc === undefined) return;
|
|
24
|
+
|
|
16
25
|
const existingStores = Object.getOwnPropertyDescriptor(router, "stores");
|
|
17
26
|
if (existingStores !== undefined) return;
|
|
18
27
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as h,j as t}from"./main-BnRC1932.js";import{c as Z,g as $,r as L,a as q,X as ee,b as m,B as te,f as P,R as re,C as ne,M as V,d as _,e as M,h as B,i as se,j as ae,k as oe,L as ie}from"./ProxyViewerContainer-BLCRQtU_.js";const de=[["line",{x1:"5",x2:"19",y1:"9",y2:"9",key:"1nwqeh"}],["line",{x1:"5",x2:"19",y1:"15",y2:"15",key:"g8yjpy"}]],le=Z("equal",de),ce="";function j(e){if(e.length===0)return ce;let r="";for(let n=0;n<e.length;n++){const a=e[n];a!==void 0&&(typeof a=="number"?r+=`[${a}]`:n===0?r+=a:r+=`.${a}`)}return r}function ue(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function D(e){if(typeof e=="string")try{return C(JSON.parse(e))}catch{return{kind:"primitive",value:e}}return C(e)}function C(e){if(e===null)return{kind:"primitive",value:null};if(typeof e=="string")return{kind:"primitive",value:e};if(typeof e=="number")return{kind:"primitive",value:e};if(typeof e=="boolean")return{kind:"primitive",value:e};if(Array.isArray(e))return{kind:"array",value:e.map(r=>C(r))};if(ue(e)){const r={};for(const n of Object.keys(e).sort())r[n]=C(e[n]);return{kind:"object",value:r}}return{kind:"primitive",value:null}}function xe(e,r){const n=[];return R([],e,r,n),n}function R(e,r,n,a){const d=j(e);if(S(r,n)){a.push({kind:"equal",path:d,value:r});return}if(r.kind!==n.kind){a.push({kind:"changed",path:d,left:r,right:n});return}if(r.kind==="primitive"&&n.kind==="primitive"){a.push({kind:"changed",path:d,left:r,right:n});return}if(r.kind==="object"&&n.kind==="object"){const i=Object.keys(r.value),s=Object.keys(n.value),o=new Set(s);for(const l of i){const f=r.value[l];if(f!==void 0)if(!o.has(l))a.push({kind:"removed",path:j([...e,l]),value:f});else{const p=n.value[l];if(p===void 0)continue;R([...e,l],f,p,a)}}for(const l of s){if(i.includes(l))continue;const f=n.value[l];f!==void 0&&a.push({kind:"added",path:j([...e,l]),value:f})}return}if(r.kind==="array"&&n.kind==="array"){const i=Math.min(r.value.length,n.value.length);for(let s=0;s<i;s++){const o=r.value[s],l=n.value[s];o===void 0||l===void 0||R([...e,s],o,l,a)}for(let s=i;s<n.value.length;s++){const o=n.value[s];o!==void 0&&a.push({kind:"added",path:j([...e,s]),value:o})}for(let s=i;s<r.value.length;s++){const o=r.value[s];o!==void 0&&a.push({kind:"removed",path:j([...e,s]),value:o})}}}function S(e,r){if(e.kind!==r.kind)return!1;if(e.kind==="primitive"&&r.kind==="primitive")return e.value===r.value;if(e.kind==="array"&&r.kind==="array"){if(e.value.length!==r.value.length)return!1;for(let n=0;n<e.value.length;n++){const a=e.value[n],d=r.value[n];if(a===void 0||d===void 0||!S(a,d))return!1}return!0}if(e.kind==="object"&&r.kind==="object"){const n=Object.keys(e.value),a=Object.keys(r.value);if(n.length!==a.length)return!1;for(const d of n){const i=e.value[d],s=r.value[d];if(i===void 0||s===void 0||!S(i,s))return!1}return!0}return!1}function v(e,r=80){let n;switch(e.kind){case"primitive":n=e.value===null?"null":JSON.stringify(e.value);break;case"array":n=`[… ${e.value.length} items]`;break;case"object":n=`{… ${Object.keys(e.value).length} keys}`;break}return n.length>r&&(n=`${n.slice(0,r-1)}…`),n}function N(e,r=2){return JSON.stringify(E(e),null,r)}function E(e){switch(e.kind){case"primitive":return e.value;case"array":return e.value.map(E);case"object":{const r={};for(const[n,a]of Object.entries(e.value))r[n]=E(a);return r}}}function w({text:e,defaultExpandDepth:r}){return t.jsx(h.Suspense,{fallback:t.jsx("div",{className:"text-xs text-muted-foreground",children:"Loading JSON..."}),children:t.jsx(ie,{text:e,defaultExpandDepth:r})})}function K(e){if(e==="")return"";for(let r=e.length-1;r>=0;r--){const n=e[r];if(n==="."||n==="[")return e.substring(0,r)}return""}function A(e){return e.kind==="equal"&&(e.value.kind==="object"||e.value.kind==="array")}function me(e){const r=[];let n=0;for(;n<e.length;){const a=e[n];if(a!==void 0&&A(a)){const d=K(a.path);let i=n+1;for(;i<e.length;){const s=e[i];if(s===void 0||!A(s)||K(s.path)!==d)break;i++}if(i-n>1){const s=[];for(let o=n;o<i;o++){const l=e[o];l!==void 0&&l.kind==="equal"&&s.push(l)}r.push({kind:"equal-run",ops:s}),n=i;continue}}a!==void 0&&r.push({kind:"single",op:a}),n++}return r}const F={added:{icon:_,accent:"text-emerald-600 dark:text-emerald-400",bg:"bg-emerald-500/5 hover:bg-emerald-500/10",border:"border-l-emerald-500",label:"ADDED"},removed:{icon:V,accent:"text-rose-600 dark:text-rose-400",bg:"bg-rose-500/5 hover:bg-rose-500/10",border:"border-l-rose-500",label:"REMOVED"},changed:{icon:M,accent:"text-amber-600 dark:text-amber-400",bg:"bg-amber-500/5 hover:bg-amber-500/10",border:"border-l-amber-500",label:"CHANGED"},equal:{icon:le,accent:"text-muted-foreground/70",bg:"bg-muted/20 hover:bg-muted/30",border:"border-l-muted-foreground/20",label:"EQUAL"}};function fe({ops:e,expanded:r,onToggle:n}){const a=e[0],d=e[e.length-1];if(a===void 0||d===void 0)return t.jsx("div",{className:"text-muted-foreground/40 text-xs",children:"—"});const i=a.path,s=d.path,o=e.length===1?i:`${i} … ${s}`,l=a.value.kind==="array"?`${e.length} equal arrays`:a.value.kind==="object"?`${e.length} equal objects`:"equal",f=F.equal;return t.jsxs("div",{className:m("border-l-4 rounded-sm",f.border,f.bg),children:[t.jsxs("button",{type:"button",onClick:n,className:"w-full text-left flex items-center gap-2 px-3 py-1.5 text-xs text-muted-foreground cursor-pointer",children:[t.jsx(B,{className:m("size-3 transition-transform shrink-0",r&&"rotate-90")}),t.jsx(f.icon,{className:m("size-3 shrink-0",f.accent)}),t.jsx("span",{className:"font-mono truncate flex-1",title:`${i} … ${s}`,children:o}),t.jsx("span",{className:m("text-[10px] uppercase tracking-wider shrink-0",f.accent),children:f.label}),t.jsxs("span",{className:"text-muted-foreground/60 shrink-0",children:["(",l,")"]})]}),r&&t.jsx("div",{className:"ml-5 mt-1 mb-2 space-y-2 pr-2",children:e.map(p=>t.jsxs("div",{className:"border border-border/50 rounded p-2 bg-muted/20",children:[t.jsx("div",{className:"font-mono text-xs text-muted-foreground mb-1",children:p.path}),t.jsx(w,{text:N(p.value),defaultExpandDepth:0})]},p.path))})]})}function pe({op:e,idx:r,copiedPath:n,onCopyPath:a,expanded:d,onToggle:i}){const s=F[e.kind],o=s.icon,l=e.kind==="added"||e.kind==="removed"?e.value.kind==="object"||e.value.kind==="array":e.kind==="changed"?e.left.kind==="object"||e.left.kind==="array"||e.right.kind==="object"||e.right.kind==="array":!1,f=e.kind==="changed"?[{text:v(e.left,400),tone:"text-rose-700 dark:text-rose-300 line-through"},{text:v(e.right,400),tone:"text-emerald-700 dark:text-emerald-300"}]:e.kind==="removed"?[{text:v(e.value,400),tone:"text-rose-700 dark:text-rose-300 line-through"}]:e.kind==="added"?[{text:v(e.value,400),tone:"text-emerald-700 dark:text-emerald-300"}]:[{text:v(e.value,400),tone:"text-muted-foreground"}],p=n===e.path&&e.path!=="";return t.jsxs("div",{"data-diff-idx":r,"data-diff-kind":e.kind,className:m("border-l-4 rounded-sm px-3 py-2 my-0.5 transition-colors",s.border,s.bg),children:[t.jsxs("button",{type:"button",onClick:i,disabled:!l,className:m("w-full flex items-center gap-2 text-xs text-left rounded-sm",l?"cursor-pointer":"cursor-default"),"aria-expanded":l?d:void 0,"aria-label":l?d?`Collapse ${e.path||"root"}`:`Expand ${e.path||"root"}`:void 0,children:[l?t.jsx(B,{className:m("size-3 shrink-0 transition-transform",s.accent,d&&"rotate-90")}):t.jsx("span",{className:"size-3 shrink-0","aria-hidden":"true"}),t.jsx(o,{className:m("size-3.5 shrink-0",s.accent),strokeWidth:2.5}),t.jsx("span",{className:"font-mono truncate flex-1 min-w-0",title:e.path||"(root)",children:e.path===""?"(root)":e.path}),t.jsx("span",{className:m("text-[9px] font-bold uppercase tracking-wider shrink-0 px-1.5 py-0.5 rounded",s.accent,e.kind==="equal"?"bg-muted/40":"bg-background/60"),children:s.label}),e.path!==""&&t.jsx("span",{role:"button",tabIndex:0,onClick:b=>{b.stopPropagation(),a(e.path)},onKeyDown:b=>{(b.key==="Enter"||b.key===" ")&&(b.stopPropagation(),b.preventDefault(),a(e.path))},className:m("shrink-0 p-1 rounded transition-colors cursor-pointer inline-flex items-center justify-center",p?"text-emerald-500":"text-muted-foreground/50 hover:text-foreground hover:bg-muted"),"aria-label":p?"Copied":"Copy",title:p?"Copied!":"Copy",children:p?t.jsx(se,{className:"size-3"}):t.jsx(ae,{className:"size-3"})})]}),f.map((b,y)=>t.jsx("div",{className:m("font-mono text-xs mt-1 break-all pl-5",b.tone),children:b.text},y)),t.jsx("div",{className:"overflow-hidden transition-all duration-200",style:{maxHeight:d&&l?"2000px":"0"},"aria-hidden":!d,children:d&&l&&e.kind!=="equal"?t.jsx(he,{op:e}):null})]})}function he({op:e}){if(e.kind==="added"||e.kind==="removed")return t.jsx("div",{className:"pl-5 mt-2 border border-border/50 rounded p-2 bg-muted/20",children:t.jsx(w,{text:N(e.value),defaultExpandDepth:0})});const r=e.left.kind==="object"||e.left.kind==="array",n=e.right.kind==="object"||e.right.kind==="array";return!r&&!n?t.jsx("div",{className:"pl-5 mt-2 text-xs text-muted-foreground/70 italic",children:"Primitive values are shown inline above."}):t.jsxs("div",{className:"pl-5 mt-2 grid grid-cols-1 md:grid-cols-2 gap-2",children:[t.jsxs("div",{className:"border border-rose-500/30 rounded p-2 bg-rose-500/5",children:[t.jsx("div",{className:"text-[10px] uppercase tracking-wider text-rose-500 mb-1",children:"Old"}),t.jsx(w,{text:N(e.left),defaultExpandDepth:0})]}),t.jsxs("div",{className:"border border-emerald-500/30 rounded p-2 bg-emerald-500/5",children:[t.jsx("div",{className:"text-[10px] uppercase tracking-wider text-emerald-500 mb-1",children:"New"}),t.jsx(w,{text:N(e.right),defaultExpandDepth:0})]})]})}function be({counts:e,onJumpTo:r}){const n=e.added+e.removed+e.changed;return t.jsxs("div",{className:"px-4 py-2 border-b border-border bg-muted/20 flex items-center gap-2 text-xs flex-wrap",children:[t.jsxs("span",{className:"text-muted-foreground font-medium",children:[n," ",n===1?"change":"changes"]}),t.jsxs("button",{type:"button",onClick:()=>r("removed"),disabled:e.removed===0,className:m("inline-flex items-center gap-1 px-2 py-0.5 rounded-full border cursor-pointer transition-colors",e.removed>0?"border-rose-500/40 text-rose-600 dark:text-rose-400 bg-rose-500/10 hover:bg-rose-500/20":"border-border text-muted-foreground/40 cursor-not-allowed"),title:e.removed>0?"Jump to first removed":"No removals",children:[t.jsx(V,{className:"size-3"}),e.removed," removed"]}),t.jsxs("button",{type:"button",onClick:()=>r("added"),disabled:e.added===0,className:m("inline-flex items-center gap-1 px-2 py-0.5 rounded-full border cursor-pointer transition-colors",e.added>0?"border-emerald-500/40 text-emerald-600 dark:text-emerald-400 bg-emerald-500/10 hover:bg-emerald-500/20":"border-border text-muted-foreground/40 cursor-not-allowed"),title:e.added>0?"Jump to first added":"No additions",children:[t.jsx(_,{className:"size-3"}),e.added," added"]}),t.jsxs("button",{type:"button",onClick:()=>r("changed"),disabled:e.changed===0,className:m("inline-flex items-center gap-1 px-2 py-0.5 rounded-full border cursor-pointer transition-colors",e.changed>0?"border-amber-500/40 text-amber-600 dark:text-amber-400 bg-amber-500/10 hover:bg-amber-500/20":"border-border text-muted-foreground/40 cursor-not-allowed"),title:e.changed>0?"Jump to first changed":"No changes",children:[t.jsx(M,{className:"size-3"}),e.changed," changed"]})]})}function ve({mode:e,onChange:r}){return t.jsxs("div",{className:"inline-flex rounded-md border border-border overflow-hidden",children:[t.jsxs("button",{type:"button",onClick:()=>r("unified"),"aria-pressed":e==="unified",className:m("flex items-center gap-1 px-2 py-1 text-xs transition-colors cursor-pointer",e==="unified"?"bg-muted text-foreground":"hover:bg-muted/50 text-muted-foreground"),title:"Unified view (single column, emphasized diffs)",children:[t.jsx(re,{className:"size-3"}),"Unified"]}),t.jsxs("button",{type:"button",onClick:()=>r("split"),"aria-pressed":e==="split",className:m("flex items-center gap-1 px-2 py-1 text-xs transition-colors border-l border-border cursor-pointer",e==="split"?"bg-muted text-foreground":"hover:bg-muted/50 text-muted-foreground"),title:"Split view (path | left | right)",children:[t.jsx(ne,{className:"size-3"}),"Split"]})]})}function J({log:e,side:r,displayNumber:n}){const a=q(e);return t.jsxs("div",{className:"flex-1 min-w-0 space-y-1 text-xs",children:[t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx(te,{variant:"outline",className:m("text-[10px] px-1.5 py-0 h-5 font-mono shrink-0",r==="left"?"border-rose-500/40 text-rose-400":"border-emerald-500/40 text-emerald-400"),children:r==="left"?"← Left":"Right →"}),t.jsxs("span",{className:"font-mono text-blue-400/80",title:`Log ID ${String(e.id)}`,children:["#",n]}),e.model!==null&&t.jsx("span",{className:"font-mono leading-snug text-muted-foreground",children:e.model})]}),t.jsxs("div",{className:"flex items-center gap-3 text-muted-foreground font-mono",children:[e.cacheCreationInputTokens!==null&&e.cacheCreationInputTokens>0&&t.jsxs("span",{className:"text-emerald-400",children:["KV Cache +",P(e.cacheCreationInputTokens)]}),e.cacheReadInputTokens!==null&&e.cacheReadInputTokens>0&&t.jsxs("span",{className:"text-purple-400",children:["KV Cache ~",P(e.cacheReadInputTokens)]}),t.jsx("span",{className:"truncate",title:e.timestamp,children:e.timestamp})]}),t.jsxs("div",{className:"text-muted-foreground/70 font-mono truncate",title:a,children:["session: ",a]})]})}function ye({left:e,right:r,leftDisplayNumber:n,rightDisplayNumber:a,onClose:d}){const i=h.useMemo(()=>{const c=$(L(e)).analyzeRequest(e.rawRequestBody),u=$(L(r)).analyzeRequest(r.rawRequestBody),x=D(c.comparisonValue),g=D(u.comparisonValue);return xe(x,g)},[e.apiFormat,e.path,e.rawRequestBody,r.apiFormat,r.path,r.rawRequestBody]),s=h.useMemo(()=>me(i),[i]),o=h.useMemo(()=>{let c=0,u=0,x=0;for(const g of s)if(g.kind==="single")switch(g.op.kind){case"added":c++;break;case"removed":u++;break;case"changed":x++;break}return{added:c,removed:u,changed:x}},[s]),[l,f]=h.useState(new Set),p=c=>{f(u=>{const x=new Set(u);return x.has(c)?x.delete(c):x.add(c),x})},[b,y]=h.useState(new Set),U=c=>{y(u=>{const x=new Set(u);return x.has(c)?x.delete(c):x.add(c),x})};h.useEffect(()=>{y(new Set)},[e.id,r.id]);const[T,H]=h.useState("unified"),I=h.useRef(null),[G,z]=h.useState(null),k=h.useRef(null),Q=c=>{oe(c).then(u=>{u&&(z(c),k.current!==null&&clearTimeout(k.current),k.current=setTimeout(()=>z(null),1500))})};h.useEffect(()=>()=>{k.current!==null&&clearTimeout(k.current)},[]);const W=c=>{const u=s.findIndex(O=>O.kind==="single"&&O.op.kind===c);if(u===-1)return;const x=I.current;if(x===null)return;const g=x.querySelector(`[data-diff-idx="${u}"]`);g!==null&&g.scrollIntoView({behavior:"smooth",block:"center"})};h.useEffect(()=>{const c=x=>{x.key==="Escape"&&d()};document.addEventListener("keydown",c);const u=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",c),document.body.style.overflow=u}},[d]);const X=q(e)===q(r),Y=i.length===1&&i[0]?.kind==="equal";return t.jsxs("div",{className:"fixed inset-0 z-50 flex justify-end",role:"dialog","aria-modal":"true","aria-label":"Compare two log requests",children:[t.jsx("button",{type:"button",onClick:d,"aria-label":"Close compare drawer",className:"absolute inset-0 bg-black/40 cursor-default",tabIndex:-1}),t.jsxs("div",{className:m("relative bg-background border-l border-border shadow-xl","w-full md:w-[70vw] max-w-[1100px] flex flex-col h-full"),onClick:c=>c.stopPropagation(),onKeyDown:c=>c.stopPropagation(),children:[t.jsxs("div",{className:"flex items-start gap-4 px-4 py-3 border-b border-border",children:[t.jsxs("div",{className:"flex-1 flex gap-4 min-w-0",children:[t.jsx(J,{log:e,side:"left",displayNumber:n}),t.jsx(J,{log:r,side:"right",displayNumber:a})]}),t.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[t.jsx(ve,{mode:T,onChange:H}),t.jsx("button",{type:"button",onClick:d,"aria-label":"Close",className:"p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted cursor-pointer",children:t.jsx(ee,{className:"size-4"})})]})]}),!X&&t.jsx("div",{className:"px-4 py-1.5 text-xs text-amber-400 bg-amber-500/10 border-b border-border",children:"Heads up: the two selected logs are from different sessions."}),Y?t.jsx("div",{className:"flex-1 min-h-0 overflow-y-auto flex items-center justify-center text-muted-foreground text-sm",children:"The two Request payloads are identical."}):t.jsxs(t.Fragment,{children:[t.jsx(be,{counts:o,onJumpTo:W}),t.jsx("div",{ref:I,className:"flex-1 min-h-0 overflow-y-auto",children:T==="unified"?t.jsx("div",{className:"px-3 py-2 space-y-0.5",children:s.map((c,u)=>{if(c.kind==="equal-run")return t.jsx(fe,{ops:c.ops,expanded:l.has(u),onToggle:()=>p(u)},`r${u}`);const x=c.op;return t.jsx(pe,{op:x,idx:u,copiedPath:G,onCopyPath:Q,expanded:b.has(u),onToggle:()=>U(u)},`o${u}`)})}):t.jsx(ge,{grouped:s,left:e,right:r,leftDisplayNumber:n,rightDisplayNumber:a})})]})]})]})}function ge({grouped:e,left:r,right:n,leftDisplayNumber:a,rightDisplayNumber:d}){return t.jsxs("div",{className:"grid grid-cols-[200px_1fr_1fr] gap-x-2 gap-y-0.5 px-3 py-2 text-xs",children:[t.jsxs("div",{className:"grid grid-cols-[200px_1fr_1fr] gap-x-2 col-span-3 pb-2 mb-2 border-b border-border text-[10px] uppercase tracking-wider text-muted-foreground",children:[t.jsx("span",{children:"Path"}),t.jsxs("span",{title:`Log ID ${String(r.id)}`,children:["Left (Log #",a,")"]}),t.jsxs("span",{title:`Log ID ${String(n.id)}`,children:["Right (Log #",d,")"]})]}),e.map((i,s)=>{if(i.kind==="equal-run")return t.jsxs("div",{className:"col-span-3 px-2 py-1 text-xs text-muted-foreground/60",children:[i.ops.length," equal siblings collapsed — switch to Unified to expand"]},s);const o=i.op;return o.kind==="equal"?t.jsxs("div",{className:"col-span-3 grid grid-cols-[200px_1fr_1fr] gap-x-2 px-2 py-0.5 text-muted-foreground",children:[t.jsx("span",{className:"font-mono text-xs truncate",title:o.path,children:o.path}),t.jsx("span",{className:"font-mono text-xs break-all opacity-60",children:v(o.value,200)}),t.jsx("span",{className:"font-mono text-xs break-all opacity-60",children:v(o.value,200)})]},s):o.kind==="added"?t.jsxs("div",{className:"col-span-3 px-2 py-1 rounded text-xs border-l-2 border-l-emerald-400/70 bg-emerald-500/5",children:[t.jsx("div",{className:"font-mono text-xs text-muted-foreground mb-0.5",children:o.path}),t.jsxs("div",{className:"font-mono break-all text-emerald-300/90",children:["+ ",v(o.value,400)]})]},s):o.kind==="removed"?t.jsxs("div",{className:"col-span-3 px-2 py-1 rounded text-xs border-l-2 border-l-rose-400/70 bg-rose-500/5",children:[t.jsx("div",{className:"font-mono text-xs text-muted-foreground mb-0.5",children:o.path}),t.jsxs("div",{className:"font-mono break-all text-rose-300/90 line-through",children:["− ",v(o.value,400)]})]},s):t.jsxs("div",{className:"col-span-3 px-2 py-1 rounded text-xs border-l-2 border-l-amber-400/70 bg-amber-500/5",children:[t.jsx("div",{className:"font-mono text-xs text-muted-foreground mb-1",children:o.path}),t.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[t.jsx("div",{className:"font-mono text-rose-300/90 break-all line-through",children:v(o.left,400)}),t.jsx("div",{className:"font-mono text-emerald-300/90 break-all",children:v(o.right,400)})]})]},s)})]})}export{ye as CompareDrawer};
|