@tonyclaw/agent-inspector 2.0.26 → 2.0.28
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 +169 -13
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-DBTFzxG5.js → CompareDrawer-CicCP3Hb.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer-ivZk8MgE.js → ProxyViewerContainer-BccuA6p5.js} +4 -4
- package/.output/public/assets/{ReplayDialog-syW2hDNE.js → ReplayDialog-Bc8q3ujm.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-BkuRtY9o.js → RequestAnatomy-Coyy3zcH.js} +1 -1
- package/.output/public/assets/{ResponseView-BwyvEvoE.js → ResponseView-BWJzgh0c.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-BY4MbPKg.js → StreamingChunkSequence-BSC3uuE5.js} +1 -1
- package/.output/public/assets/_sessionId-9rEF0uSE.js +1 -0
- package/.output/public/assets/index-B9_VaAWl.js +1 -0
- package/.output/public/assets/{main-DKRDRBdd.js → main-Brj0Gn91.js} +2 -2
- package/.output/server/_libs/modelcontextprotocol__server.mjs +228 -0
- package/.output/server/{_sessionId-VDd4N_1B.mjs → _sessionId-DDdyKVG-.mjs} +3 -3
- package/.output/server/_ssr/{CompareDrawer-BQZlxsOC.mjs → CompareDrawer-XSExe2ml.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-njY2oQCc.mjs → ProxyViewerContainer-B5pIiBZN.mjs} +7 -7
- package/.output/server/_ssr/{ReplayDialog-B8lkdAIh.mjs → ReplayDialog-BOAu0ric.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-CwOTfdwS.mjs → RequestAnatomy-C8Q5lozD.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-3Iz_mZpd.mjs → ResponseView-a_iJfPUF.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-hmJcQNW5.mjs → StreamingChunkSequence-YMKC0j8U.mjs} +2 -2
- package/.output/server/_ssr/{index-UuTKM4aC.mjs → index-CkDwBPzI.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-C7InHrxE.mjs → router-DXOXdFFM.mjs} +1816 -151
- package/.output/server/_tanstack-start-manifest_v-DPf3uJys.mjs +4 -0
- package/.output/server/index.mjs +64 -64
- package/README.md +55 -1
- package/package.json +1 -1
- package/src/cli/networkHints.ts +150 -0
- package/src/cli.ts +72 -13
- package/src/lib/runContract.ts +162 -0
- package/src/mcp/server.ts +554 -2
- package/src/mcp/toolHandlers.ts +154 -0
- package/src/proxy/evidenceAnalysis.ts +522 -0
- package/src/proxy/evidenceExporter.ts +215 -0
- package/src/proxy/logSearch.ts +118 -0
- package/src/proxy/runFailures.ts +100 -0
- package/src/proxy/runStore.ts +159 -0
- package/src/routes/api/logs.ts +16 -0
- package/src/routes/api/runs.$runId.evidence.ts +62 -0
- package/src/routes/api/runs.$runId.ts +50 -0
- package/src/routes/api/runs.ts +58 -0
- package/.output/public/assets/_sessionId-XlPUgIIb.js +0 -1
- package/.output/public/assets/index-By11a28-.js +0 -1
- package/.output/server/_tanstack-start-manifest_v-B6yfnMHA.mjs +0 -4
package/.output/cli.js
CHANGED
|
@@ -2116,6 +2116,117 @@ import { createConnection as createConnection2 } from "node:net";
|
|
|
2116
2116
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2117
2117
|
import { dirname as dirname2, join as join5, resolve as resolvePath } from "node:path";
|
|
2118
2118
|
import { existsSync as existsSync5 } from "node:fs";
|
|
2119
|
+
|
|
2120
|
+
// src/cli/networkHints.ts
|
|
2121
|
+
import { networkInterfaces } from "node:os";
|
|
2122
|
+
function localUrlForPort(port) {
|
|
2123
|
+
return `http://localhost:${String(port)}`;
|
|
2124
|
+
}
|
|
2125
|
+
function trimHost(host) {
|
|
2126
|
+
const trimmed = host?.trim();
|
|
2127
|
+
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
2128
|
+
}
|
|
2129
|
+
function isWildcardHost(host) {
|
|
2130
|
+
const trimmed = trimHost(host);
|
|
2131
|
+
return trimmed === void 0 || trimmed === "0.0.0.0" || trimmed === "::" || trimmed === "[::]";
|
|
2132
|
+
}
|
|
2133
|
+
function isLoopbackHost(host) {
|
|
2134
|
+
const trimmed = trimHost(host);
|
|
2135
|
+
if (trimmed === void 0) return false;
|
|
2136
|
+
return trimmed === "localhost" || trimmed === "::1" || trimmed === "[::1]" || trimmed.startsWith("127.");
|
|
2137
|
+
}
|
|
2138
|
+
function bracketIpv6(host) {
|
|
2139
|
+
if (!host.includes(":")) return host;
|
|
2140
|
+
if (host.startsWith("[") && host.endsWith("]")) return host;
|
|
2141
|
+
return `[${host}]`;
|
|
2142
|
+
}
|
|
2143
|
+
function urlForHost(port, host) {
|
|
2144
|
+
const trimmed = trimHost(host);
|
|
2145
|
+
if (trimmed === void 0 || isWildcardHost(trimmed)) return localUrlForPort(port);
|
|
2146
|
+
return `http://${bracketIpv6(trimmed)}:${String(port)}`;
|
|
2147
|
+
}
|
|
2148
|
+
function probeHostForBindHost(host) {
|
|
2149
|
+
const trimmed = trimHost(host);
|
|
2150
|
+
if (trimmed === void 0 || isWildcardHost(trimmed)) return "127.0.0.1";
|
|
2151
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) return trimmed.slice(1, -1);
|
|
2152
|
+
return trimmed;
|
|
2153
|
+
}
|
|
2154
|
+
function isValidBindHost(host) {
|
|
2155
|
+
const trimmed = host.trim();
|
|
2156
|
+
if (trimmed.length === 0) return false;
|
|
2157
|
+
if (/^https?:\/\//i.test(trimmed)) return false;
|
|
2158
|
+
if (/[\s/\\]/.test(trimmed)) return false;
|
|
2159
|
+
return true;
|
|
2160
|
+
}
|
|
2161
|
+
function isUsableAddress(address) {
|
|
2162
|
+
return address !== "0.0.0.0" && !address.startsWith("169.254.");
|
|
2163
|
+
}
|
|
2164
|
+
function is172Private(address) {
|
|
2165
|
+
const parts = address.split(".");
|
|
2166
|
+
const second = Number(parts[1] ?? "");
|
|
2167
|
+
return parts[0] === "172" && Number.isInteger(second) && second >= 16 && second <= 31;
|
|
2168
|
+
}
|
|
2169
|
+
function addressScore(address) {
|
|
2170
|
+
if (address.startsWith("192.168.")) return 0;
|
|
2171
|
+
if (address.startsWith("10.")) return 1;
|
|
2172
|
+
if (is172Private(address)) return 2;
|
|
2173
|
+
return 3;
|
|
2174
|
+
}
|
|
2175
|
+
function collectNetworkUrls(port, interfaces = networkInterfaces()) {
|
|
2176
|
+
const addresses = /* @__PURE__ */ new Set();
|
|
2177
|
+
for (const entries of Object.values(interfaces)) {
|
|
2178
|
+
if (entries === void 0) continue;
|
|
2179
|
+
for (const entry of entries) {
|
|
2180
|
+
if (entry.family === "IPv4" && !entry.internal && isUsableAddress(entry.address)) {
|
|
2181
|
+
addresses.add(entry.address);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
return [...addresses].sort((left, right) => {
|
|
2186
|
+
const score = addressScore(left) - addressScore(right);
|
|
2187
|
+
return score !== 0 ? score : left.localeCompare(right);
|
|
2188
|
+
}).slice(0, 4).map((address) => `http://${address}:${String(port)}`);
|
|
2189
|
+
}
|
|
2190
|
+
function networkUrlsForBindHost(port, host, interfaces = networkInterfaces()) {
|
|
2191
|
+
if (isLoopbackHost(host)) return [];
|
|
2192
|
+
if (isWildcardHost(host)) return collectNetworkUrls(port, interfaces);
|
|
2193
|
+
return [urlForHost(port, host)];
|
|
2194
|
+
}
|
|
2195
|
+
function formatAccessHintLines(port, networkUrls, host = void 0) {
|
|
2196
|
+
const localUrl = localUrlForPort(port);
|
|
2197
|
+
const hostLabel = isWildcardHost(host) ? "all interfaces (0.0.0.0)" : trimHost(host) ?? "";
|
|
2198
|
+
const lines = ["Access URLs:", ` Bind host: ${hostLabel}`];
|
|
2199
|
+
if (isLoopbackHost(host) || isWildcardHost(host)) {
|
|
2200
|
+
lines.push(` Local UI: ${localUrl}`);
|
|
2201
|
+
lines.push(` Local proxy: ${localUrl}/proxy`);
|
|
2202
|
+
lines.push(` Local MCP: ${localUrl}/api/mcp`);
|
|
2203
|
+
} else {
|
|
2204
|
+
const boundUrl = urlForHost(port, host);
|
|
2205
|
+
lines.push(` Bound UI: ${boundUrl}`);
|
|
2206
|
+
lines.push(` Bound proxy: ${boundUrl}/proxy`);
|
|
2207
|
+
lines.push(` Bound MCP: ${boundUrl}/api/mcp`);
|
|
2208
|
+
lines.push(" Localhost: may not work because the server is bound to a specific host");
|
|
2209
|
+
}
|
|
2210
|
+
const firstNetworkUrl = networkUrls[0];
|
|
2211
|
+
if (isLoopbackHost(host)) {
|
|
2212
|
+
lines.push(" Network: disabled by loopback bind host");
|
|
2213
|
+
} else if (firstNetworkUrl === void 0) {
|
|
2214
|
+
lines.push(" Network: no non-loopback IPv4 address detected");
|
|
2215
|
+
} else {
|
|
2216
|
+
lines.push(` Network UI: ${firstNetworkUrl}`);
|
|
2217
|
+
lines.push(` Network proxy: ${firstNetworkUrl}/proxy`);
|
|
2218
|
+
const moreUrls = networkUrls.slice(1);
|
|
2219
|
+
if (moreUrls.length > 0) {
|
|
2220
|
+
lines.push(` Other IPs: ${moreUrls.join(", ")}`);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
lines.push(
|
|
2224
|
+
" Container note: if the coding agent is in another container or machine, replace localhost with a reachable host/IP and verify the port is reachable."
|
|
2225
|
+
);
|
|
2226
|
+
return lines;
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
// src/cli.ts
|
|
2119
2230
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2120
2231
|
var __dirname2 = dirname2(__filename2);
|
|
2121
2232
|
var DEFAULT_PORT3 = 25947;
|
|
@@ -2135,11 +2246,11 @@ if (subcommand === "doctor") {
|
|
|
2135
2246
|
process.exit(code);
|
|
2136
2247
|
}
|
|
2137
2248
|
await runStart(process.argv.slice(2));
|
|
2138
|
-
async function isInspectorHealthy(port) {
|
|
2249
|
+
async function isInspectorHealthy(port, host) {
|
|
2139
2250
|
const controller = new AbortController();
|
|
2140
2251
|
const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
|
|
2141
2252
|
try {
|
|
2142
|
-
const response = await fetch(
|
|
2253
|
+
const response = await fetch(`${urlForHost(port, probeHostForBindHost(host))}/api/health`, {
|
|
2143
2254
|
cache: "no-store",
|
|
2144
2255
|
signal: controller.signal
|
|
2145
2256
|
});
|
|
@@ -2150,11 +2261,11 @@ async function isInspectorHealthy(port) {
|
|
|
2150
2261
|
clearTimeout(timeout);
|
|
2151
2262
|
}
|
|
2152
2263
|
}
|
|
2153
|
-
async function getRunningCaptureMode(port) {
|
|
2264
|
+
async function getRunningCaptureMode(port, host) {
|
|
2154
2265
|
const controller = new AbortController();
|
|
2155
2266
|
const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
|
|
2156
2267
|
try {
|
|
2157
|
-
const response = await fetch(
|
|
2268
|
+
const response = await fetch(`${urlForHost(port, probeHostForBindHost(host))}/api/config`, {
|
|
2158
2269
|
cache: "no-store",
|
|
2159
2270
|
signal: controller.signal
|
|
2160
2271
|
});
|
|
@@ -2167,9 +2278,9 @@ async function getRunningCaptureMode(port) {
|
|
|
2167
2278
|
clearTimeout(timeout);
|
|
2168
2279
|
}
|
|
2169
2280
|
}
|
|
2170
|
-
function isPortAcceptingConnections(port) {
|
|
2281
|
+
function isPortAcceptingConnections(port, host) {
|
|
2171
2282
|
return new Promise((resolve2) => {
|
|
2172
|
-
const socket = createConnection2({ host:
|
|
2283
|
+
const socket = createConnection2({ host: probeHostForBindHost(host), port });
|
|
2173
2284
|
const finish = (value) => {
|
|
2174
2285
|
socket.removeAllListeners();
|
|
2175
2286
|
socket.destroy();
|
|
@@ -2186,10 +2297,10 @@ function sleep(ms) {
|
|
|
2186
2297
|
setTimeout(resolve2, ms);
|
|
2187
2298
|
});
|
|
2188
2299
|
}
|
|
2189
|
-
async function waitForInspectorHealthy(port, timeoutMs) {
|
|
2300
|
+
async function waitForInspectorHealthy(port, timeoutMs, host) {
|
|
2190
2301
|
const start = Date.now();
|
|
2191
2302
|
while (Date.now() - start < timeoutMs) {
|
|
2192
|
-
if (await isInspectorHealthy(port)) return true;
|
|
2303
|
+
if (await isInspectorHealthy(port, host)) return true;
|
|
2193
2304
|
await sleep(250);
|
|
2194
2305
|
}
|
|
2195
2306
|
return false;
|
|
@@ -2251,11 +2362,19 @@ function readCaptureMode(raw) {
|
|
|
2251
2362
|
const value = descriptor?.value;
|
|
2252
2363
|
return typeof value === "string" ? parseCaptureMode(value) : null;
|
|
2253
2364
|
}
|
|
2365
|
+
function printAccessHints(port, host) {
|
|
2366
|
+
const networkUrls = networkUrlsForBindHost(port, host);
|
|
2367
|
+
for (const line of formatAccessHintLines(port, networkUrls, host)) {
|
|
2368
|
+
console.log(line);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2254
2371
|
async function runStart(args) {
|
|
2255
2372
|
const envPort = process.env["PORT"];
|
|
2256
2373
|
const portDefault = envPort !== void 0 ? Number(envPort) : DEFAULT_PORT3;
|
|
2374
|
+
const envHost = process.env["NITRO_HOST"] ?? process.env["HOST"];
|
|
2257
2375
|
const envMode = process.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? process.env["AGENT_INSPECTOR_MODE"];
|
|
2258
2376
|
let port = portDefault;
|
|
2377
|
+
let host = envHost !== void 0 && envHost.trim().length > 0 ? envHost.trim() : void 0;
|
|
2259
2378
|
let open = true;
|
|
2260
2379
|
let openWasSpecified = false;
|
|
2261
2380
|
let background = false;
|
|
@@ -2274,8 +2393,23 @@ async function runStart(args) {
|
|
|
2274
2393
|
captureMode = parsedMode;
|
|
2275
2394
|
captureModeWasSpecified = true;
|
|
2276
2395
|
}
|
|
2396
|
+
if (host !== void 0 && !isValidBindHost(host)) {
|
|
2397
|
+
console.error(`Invalid host: ${host}. Use an IP or hostname without protocol/path.`);
|
|
2398
|
+
process.exitCode = 1;
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2277
2401
|
for (let i = 0; i < args.length; i++) {
|
|
2278
2402
|
const arg = args[i] ?? "";
|
|
2403
|
+
if (arg.startsWith("--host=")) {
|
|
2404
|
+
const value = arg.slice(arg.indexOf("=") + 1).trim();
|
|
2405
|
+
if (!isValidBindHost(value)) {
|
|
2406
|
+
console.error(`Invalid host: ${value}. Use an IP or hostname without protocol/path.`);
|
|
2407
|
+
process.exitCode = 1;
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
host = value;
|
|
2411
|
+
continue;
|
|
2412
|
+
}
|
|
2279
2413
|
if (arg.startsWith("--mode=") || arg.startsWith("--capture-mode=")) {
|
|
2280
2414
|
const value = arg.slice(arg.indexOf("=") + 1);
|
|
2281
2415
|
const parsedMode = parseCaptureMode(value);
|
|
@@ -2294,6 +2428,18 @@ async function runStart(args) {
|
|
|
2294
2428
|
port = Number(args[i + 1]);
|
|
2295
2429
|
i++;
|
|
2296
2430
|
break;
|
|
2431
|
+
case "--host":
|
|
2432
|
+
case "-H": {
|
|
2433
|
+
const value = args[i + 1]?.trim() ?? "";
|
|
2434
|
+
if (!isValidBindHost(value)) {
|
|
2435
|
+
console.error(`Invalid host: ${value}. Use an IP or hostname without protocol/path.`);
|
|
2436
|
+
process.exitCode = 1;
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
host = value;
|
|
2440
|
+
i++;
|
|
2441
|
+
break;
|
|
2442
|
+
}
|
|
2297
2443
|
case "--no-open":
|
|
2298
2444
|
open = false;
|
|
2299
2445
|
openWasSpecified = true;
|
|
@@ -2398,11 +2544,12 @@ async function runStart(args) {
|
|
|
2398
2544
|
}
|
|
2399
2545
|
}
|
|
2400
2546
|
process.env["PORT"] = String(port);
|
|
2401
|
-
const url =
|
|
2402
|
-
if (!forceRestart && await isInspectorHealthy(port)) {
|
|
2547
|
+
const url = urlForHost(port, host);
|
|
2548
|
+
if (!forceRestart && await isInspectorHealthy(port, host)) {
|
|
2403
2549
|
console.log(`agent-inspector is already running at ${url}`);
|
|
2550
|
+
printAccessHints(port, host);
|
|
2404
2551
|
if (captureModeWasSpecified) {
|
|
2405
|
-
const runningMode = await getRunningCaptureMode(port);
|
|
2552
|
+
const runningMode = await getRunningCaptureMode(port, host);
|
|
2406
2553
|
if (runningMode !== null && runningMode !== captureMode) {
|
|
2407
2554
|
console.log(`Existing instance capture mode is ${runningMode}; requested ${captureMode}.`);
|
|
2408
2555
|
console.log(`Use --force-restart to restart with ${captureMode} mode.`);
|
|
@@ -2414,7 +2561,7 @@ async function runStart(args) {
|
|
|
2414
2561
|
}
|
|
2415
2562
|
return;
|
|
2416
2563
|
}
|
|
2417
|
-
if (!forceRestart && await isPortAcceptingConnections(port)) {
|
|
2564
|
+
if (!forceRestart && await isPortAcceptingConnections(port, host)) {
|
|
2418
2565
|
console.error(`Port ${port} is already in use, but it is not a healthy agent-inspector.`);
|
|
2419
2566
|
console.error(`Stop that process, choose --port <n>, or re-run with --force-restart.`);
|
|
2420
2567
|
process.exitCode = 1;
|
|
@@ -2426,6 +2573,7 @@ async function runStart(args) {
|
|
|
2426
2573
|
console.log(`Server running at ${url}`);
|
|
2427
2574
|
console.log(` Capture mode: ${captureMode}`);
|
|
2428
2575
|
console.log(` Proxy: ${url}/proxy`);
|
|
2576
|
+
printAccessHints(port, host);
|
|
2429
2577
|
console.log(``);
|
|
2430
2578
|
console.log(`Route AI coding tools through the proxy:`);
|
|
2431
2579
|
console.log(` Claude Code: ANTHROPIC_BASE_URL=${url}/proxy claude`);
|
|
@@ -2434,6 +2582,9 @@ async function runStart(args) {
|
|
|
2434
2582
|
console.log(
|
|
2435
2583
|
` Direct HTTP: curl ${url}/proxy/v1/messages -d '{"model":"...","messages":[...]}'`
|
|
2436
2584
|
);
|
|
2585
|
+
console.log(
|
|
2586
|
+
` Remote/container tools: use the Network proxy URL above when shown; otherwise restart with --host 0.0.0.0 or a reachable IP.`
|
|
2587
|
+
);
|
|
2437
2588
|
console.log(``);
|
|
2438
2589
|
console.log(`Routing environment variables:`);
|
|
2439
2590
|
console.log(` ROUTES JSON map of model prefix -> upstream URL`);
|
|
@@ -2463,6 +2614,10 @@ async function runStart(args) {
|
|
|
2463
2614
|
if (providersJson !== void 0) {
|
|
2464
2615
|
serverEnv["AGENT_INSPECTOR_PROVIDERS_JSON"] = providersJson;
|
|
2465
2616
|
}
|
|
2617
|
+
if (host !== void 0) {
|
|
2618
|
+
serverEnv["HOST"] = host;
|
|
2619
|
+
serverEnv["NITRO_HOST"] = host;
|
|
2620
|
+
}
|
|
2466
2621
|
serverEnv["AGENT_INSPECTOR_CAPTURE_MODE"] = captureMode;
|
|
2467
2622
|
const serverProcess = spawn(serverCommand.command, serverCommand.args, {
|
|
2468
2623
|
stdio: background ? ["ignore", "ignore", "ignore"] : "inherit",
|
|
@@ -2472,8 +2627,9 @@ async function runStart(args) {
|
|
|
2472
2627
|
});
|
|
2473
2628
|
if (background) {
|
|
2474
2629
|
serverProcess.unref();
|
|
2475
|
-
if (await waitForInspectorHealthy(port, 5e3)) {
|
|
2630
|
+
if (await waitForInspectorHealthy(port, 5e3, host)) {
|
|
2476
2631
|
console.log(`agent-inspector background server is ready at ${url}`);
|
|
2632
|
+
printAccessHints(port, host);
|
|
2477
2633
|
return;
|
|
2478
2634
|
}
|
|
2479
2635
|
console.error(`agent-inspector background server did not become ready at ${url}.`);
|
package/.output/nitro.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as h,j as t}from"./main-DKRDRBdd.js";import{c as X,g as O,r as P,a as q,X as Y,b as x,B as Z,f as $,R as ee,C as te,M as _,d as J,e as M,h as V,J as N,i as re,j as ne}from"./ProxyViewerContainer-ivZk8MgE.js";const se=[["line",{x1:"5",x2:"19",y1:"9",y2:"9",key:"1nwqeh"}],["line",{x1:"5",x2:"19",y1:"15",y2:"15",key:"g8yjpy"}]],ae=X("equal",se),oe="";function j(e){if(e.length===0)return oe;let r="";for(let n=0;n<e.length;n++){const s=e[n];s!==void 0&&(typeof s=="number"?r+=`[${s}]`:n===0?r+=s:r+=`.${s}`)}return r}function de(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(de(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 ie(e,r){const n=[];return R([],e,r,n),n}function R(e,r,n,s){const d=j(e);if(E(r,n)){s.push({kind:"equal",path:d,value:r});return}if(r.kind!==n.kind){s.push({kind:"changed",path:d,left:r,right:n});return}if(r.kind==="primitive"&&n.kind==="primitive"){s.push({kind:"changed",path:d,left:r,right:n});return}if(r.kind==="object"&&n.kind==="object"){const o=Object.keys(r.value),a=Object.keys(n.value),m=new Set(a);for(const i of o){const f=r.value[i];if(f!==void 0)if(!m.has(i))s.push({kind:"removed",path:j([...e,i]),value:f});else{const p=n.value[i];if(p===void 0)continue;R([...e,i],f,p,s)}}for(const i of a){if(o.includes(i))continue;const f=n.value[i];f!==void 0&&s.push({kind:"added",path:j([...e,i]),value:f})}return}if(r.kind==="array"&&n.kind==="array"){const o=Math.min(r.value.length,n.value.length);for(let a=0;a<o;a++){const m=r.value[a],i=n.value[a];m===void 0||i===void 0||R([...e,a],m,i,s)}for(let a=o;a<n.value.length;a++){const m=n.value[a];m!==void 0&&s.push({kind:"added",path:j([...e,a]),value:m})}for(let a=o;a<r.value.length;a++){const m=r.value[a];m!==void 0&&s.push({kind:"removed",path:j([...e,a]),value:m})}}}function E(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 s=e.value[n],d=r.value[n];if(s===void 0||d===void 0||!E(s,d))return!1}return!0}if(e.kind==="object"&&r.kind==="object"){const n=Object.keys(e.value),s=Object.keys(r.value);if(n.length!==s.length)return!1;for(const d of n){const o=e.value[d],a=r.value[d];if(o===void 0||a===void 0||!E(o,a))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 w(e,r=2){return JSON.stringify(S(e),null,r)}function S(e){switch(e.kind){case"primitive":return e.value;case"array":return e.value.map(S);case"object":{const r={};for(const[n,s]of Object.entries(e.value))r[n]=S(s);return 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 L(e){return e.kind==="equal"&&(e.value.kind==="object"||e.value.kind==="array")}function le(e){const r=[];let n=0;for(;n<e.length;){const s=e[n];if(s!==void 0&&L(s)){const d=K(s.path);let o=n+1;for(;o<e.length;){const a=e[o];if(a===void 0||!L(a)||K(a.path)!==d)break;o++}if(o-n>1){const a=[];for(let m=n;m<o;m++){const i=e[m];i!==void 0&&i.kind==="equal"&&a.push(i)}r.push({kind:"equal-run",ops:a}),n=o;continue}}s!==void 0&&r.push({kind:"single",op:s}),n++}return r}const B={added:{icon:J,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:_,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:ae,accent:"text-muted-foreground/70",bg:"bg-muted/20 hover:bg-muted/30",border:"border-l-muted-foreground/20",label:"EQUAL"}};function ce({ops:e,expanded:r,onToggle:n}){const s=e[0],d=e[e.length-1];if(s===void 0||d===void 0)return t.jsx("div",{className:"text-muted-foreground/40 text-xs",children:"—"});const o=s.path,a=d.path,m=e.length===1?o:`${o} … ${a}`,i=s.value.kind==="array"?`${e.length} equal arrays`:s.value.kind==="object"?`${e.length} equal objects`:"equal",f=B.equal;return t.jsxs("div",{className:x("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(V,{className:x("size-3 transition-transform shrink-0",r&&"rotate-90")}),t.jsx(f.icon,{className:x("size-3 shrink-0",f.accent)}),t.jsx("span",{className:"font-mono truncate flex-1",title:`${o} … ${a}`,children:m}),t.jsx("span",{className:x("text-[10px] uppercase tracking-wider shrink-0",f.accent),children:f.label}),t.jsxs("span",{className:"text-muted-foreground/60 shrink-0",children:["(",i,")"]})]}),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(N,{text:w(p.value),defaultExpandDepth:0})]},p.path))})]})}function ue({op:e,idx:r,copiedPath:n,onCopyPath:s,expanded:d,onToggle:o}){const a=B[e.kind],m=a.icon,i=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:x("border-l-4 rounded-sm px-3 py-2 my-0.5 transition-colors",a.border,a.bg),children:[t.jsxs("button",{type:"button",onClick:o,disabled:!i,className:x("w-full flex items-center gap-2 text-xs text-left rounded-sm",i?"cursor-pointer":"cursor-default"),"aria-expanded":i?d:void 0,"aria-label":i?d?`Collapse ${e.path||"root"}`:`Expand ${e.path||"root"}`:void 0,children:[i?t.jsx(V,{className:x("size-3 shrink-0 transition-transform",a.accent,d&&"rotate-90")}):t.jsx("span",{className:"size-3 shrink-0","aria-hidden":"true"}),t.jsx(m,{className:x("size-3.5 shrink-0",a.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:x("text-[9px] font-bold uppercase tracking-wider shrink-0 px-1.5 py-0.5 rounded",a.accent,e.kind==="equal"?"bg-muted/40":"bg-background/60"),children:a.label}),e.path!==""&&t.jsx("span",{role:"button",tabIndex:0,onClick:b=>{b.stopPropagation(),s(e.path)},onKeyDown:b=>{(b.key==="Enter"||b.key===" ")&&(b.stopPropagation(),b.preventDefault(),s(e.path))},className:x("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(re,{className:"size-3"}):t.jsx(ne,{className:"size-3"})})]}),f.map((b,y)=>t.jsx("div",{className:x("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&&i?"2000px":"0"},"aria-hidden":!d,children:d&&i&&e.kind!=="equal"?t.jsx(me,{op:e}):null})]})}function me({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(N,{text:w(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(N,{text:w(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(N,{text:w(e.right),defaultExpandDepth:0})]})]})}function xe({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:x("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(_,{className:"size-3"}),e.removed," removed"]}),t.jsxs("button",{type:"button",onClick:()=>r("added"),disabled:e.added===0,className:x("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(J,{className:"size-3"}),e.added," added"]}),t.jsxs("button",{type:"button",onClick:()=>r("changed"),disabled:e.changed===0,className:x("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 fe({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:x("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(ee,{className:"size-3"}),"Unified"]}),t.jsxs("button",{type:"button",onClick:()=>r("split"),"aria-pressed":e==="split",className:x("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(te,{className:"size-3"}),"Split"]})]})}function A({log:e,side:r}){const n=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(Z,{variant:"outline",className:x("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",children:["#",e.id]}),e.model!==null&&t.jsx("span",{className:"font-mono text-muted-foreground truncate",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 +",$(e.cacheCreationInputTokens)]}),e.cacheReadInputTokens!==null&&e.cacheReadInputTokens>0&&t.jsxs("span",{className:"text-purple-400",children:["KV Cache ~",$(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:n,children:["session: ",n]})]})}function ve({left:e,right:r,onClose:n}){const s=h.useMemo(()=>{const l=O(P(e)).analyzeRequest(e.rawRequestBody),c=O(P(r)).analyzeRequest(r.rawRequestBody),u=D(l.comparisonValue),g=D(c.comparisonValue);return ie(u,g)},[e.apiFormat,e.path,e.rawRequestBody,r.apiFormat,r.path,r.rawRequestBody]),d=h.useMemo(()=>le(s),[s]),o=h.useMemo(()=>{let l=0,c=0,u=0;for(const g of d)if(g.kind==="single")switch(g.op.kind){case"added":l++;break;case"removed":c++;break;case"changed":u++;break}return{added:l,removed:c,changed:u}},[d]),[a,m]=h.useState(new Set),i=l=>{m(c=>{const u=new Set(c);return u.has(l)?u.delete(l):u.add(l),u})},[f,p]=h.useState(new Set),b=l=>{p(c=>{const u=new Set(c);return u.has(l)?u.delete(l):u.add(l),u})};h.useEffect(()=>{p(new Set)},[e.id,r.id]);const[y,F]=h.useState("unified"),T=h.useRef(null),[U,z]=h.useState(null),k=h.useRef(null),H=l=>{window.navigator.clipboard.writeText(l).then(()=>{z(l),k.current!==null&&clearTimeout(k.current),k.current=setTimeout(()=>z(null),1500)})};h.useEffect(()=>()=>{k.current!==null&&clearTimeout(k.current)},[]);const G=l=>{const c=d.findIndex(I=>I.kind==="single"&&I.op.kind===l);if(c===-1)return;const u=T.current;if(u===null)return;const g=u.querySelector(`[data-diff-idx="${c}"]`);g!==null&&g.scrollIntoView({behavior:"smooth",block:"center"})};h.useEffect(()=>{const l=u=>{u.key==="Escape"&&n()};document.addEventListener("keydown",l);const c=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",l),document.body.style.overflow=c}},[n]);const Q=q(e)===q(r),W=s.length===1&&s[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:n,"aria-label":"Close compare drawer",className:"absolute inset-0 bg-black/40 cursor-default",tabIndex:-1}),t.jsxs("div",{className:x("relative bg-background border-l border-border shadow-xl","w-full md:w-[70vw] max-w-[1100px] flex flex-col h-full"),onClick:l=>l.stopPropagation(),onKeyDown:l=>l.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(A,{log:e,side:"left"}),t.jsx(A,{log:r,side:"right"})]}),t.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[t.jsx(fe,{mode:y,onChange:F}),t.jsx("button",{type:"button",onClick:n,"aria-label":"Close",className:"p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted cursor-pointer",children:t.jsx(Y,{className:"size-4"})})]})]}),!Q&&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."}),W?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(xe,{counts:o,onJumpTo:G}),t.jsx("div",{ref:T,className:"flex-1 min-h-0 overflow-y-auto",children:y==="unified"?t.jsx("div",{className:"px-3 py-2 space-y-0.5",children:d.map((l,c)=>{if(l.kind==="equal-run")return t.jsx(ce,{ops:l.ops,expanded:a.has(c),onToggle:()=>i(c)},`r${c}`);const u=l.op;return t.jsx(ue,{op:u,idx:c,copiedPath:U,onCopyPath:H,expanded:f.has(c),onToggle:()=>b(c)},`o${c}`)})}):t.jsx(pe,{grouped:d,left:e,right:r})})]})]})]})}function pe({grouped:e,left:r,right:n}){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",{children:["Left (Log #",r.id,")"]}),t.jsxs("span",{children:["Right (Log #",n.id,")"]})]}),e.map((s,d)=>{if(s.kind==="equal-run")return t.jsxs("div",{className:"col-span-3 px-2 py-1 text-xs text-muted-foreground/60",children:[s.ops.length," equal siblings collapsed — switch to Unified to expand"]},d);const o=s.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)})]},d):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)]})]},d):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)]})]},d):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)})]})]},d)})]})}export{ve as CompareDrawer};
|
|
1
|
+
import{r as h,j as t}from"./main-Brj0Gn91.js";import{c as X,g as O,r as P,a as q,X as Y,b as x,B as Z,f as $,R as ee,C as te,M as _,d as J,e as M,h as V,J as N,i as re,j as ne}from"./ProxyViewerContainer-BccuA6p5.js";const se=[["line",{x1:"5",x2:"19",y1:"9",y2:"9",key:"1nwqeh"}],["line",{x1:"5",x2:"19",y1:"15",y2:"15",key:"g8yjpy"}]],ae=X("equal",se),oe="";function j(e){if(e.length===0)return oe;let r="";for(let n=0;n<e.length;n++){const s=e[n];s!==void 0&&(typeof s=="number"?r+=`[${s}]`:n===0?r+=s:r+=`.${s}`)}return r}function de(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(de(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 ie(e,r){const n=[];return R([],e,r,n),n}function R(e,r,n,s){const d=j(e);if(E(r,n)){s.push({kind:"equal",path:d,value:r});return}if(r.kind!==n.kind){s.push({kind:"changed",path:d,left:r,right:n});return}if(r.kind==="primitive"&&n.kind==="primitive"){s.push({kind:"changed",path:d,left:r,right:n});return}if(r.kind==="object"&&n.kind==="object"){const o=Object.keys(r.value),a=Object.keys(n.value),m=new Set(a);for(const i of o){const f=r.value[i];if(f!==void 0)if(!m.has(i))s.push({kind:"removed",path:j([...e,i]),value:f});else{const p=n.value[i];if(p===void 0)continue;R([...e,i],f,p,s)}}for(const i of a){if(o.includes(i))continue;const f=n.value[i];f!==void 0&&s.push({kind:"added",path:j([...e,i]),value:f})}return}if(r.kind==="array"&&n.kind==="array"){const o=Math.min(r.value.length,n.value.length);for(let a=0;a<o;a++){const m=r.value[a],i=n.value[a];m===void 0||i===void 0||R([...e,a],m,i,s)}for(let a=o;a<n.value.length;a++){const m=n.value[a];m!==void 0&&s.push({kind:"added",path:j([...e,a]),value:m})}for(let a=o;a<r.value.length;a++){const m=r.value[a];m!==void 0&&s.push({kind:"removed",path:j([...e,a]),value:m})}}}function E(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 s=e.value[n],d=r.value[n];if(s===void 0||d===void 0||!E(s,d))return!1}return!0}if(e.kind==="object"&&r.kind==="object"){const n=Object.keys(e.value),s=Object.keys(r.value);if(n.length!==s.length)return!1;for(const d of n){const o=e.value[d],a=r.value[d];if(o===void 0||a===void 0||!E(o,a))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 w(e,r=2){return JSON.stringify(S(e),null,r)}function S(e){switch(e.kind){case"primitive":return e.value;case"array":return e.value.map(S);case"object":{const r={};for(const[n,s]of Object.entries(e.value))r[n]=S(s);return 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 L(e){return e.kind==="equal"&&(e.value.kind==="object"||e.value.kind==="array")}function le(e){const r=[];let n=0;for(;n<e.length;){const s=e[n];if(s!==void 0&&L(s)){const d=K(s.path);let o=n+1;for(;o<e.length;){const a=e[o];if(a===void 0||!L(a)||K(a.path)!==d)break;o++}if(o-n>1){const a=[];for(let m=n;m<o;m++){const i=e[m];i!==void 0&&i.kind==="equal"&&a.push(i)}r.push({kind:"equal-run",ops:a}),n=o;continue}}s!==void 0&&r.push({kind:"single",op:s}),n++}return r}const B={added:{icon:J,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:_,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:ae,accent:"text-muted-foreground/70",bg:"bg-muted/20 hover:bg-muted/30",border:"border-l-muted-foreground/20",label:"EQUAL"}};function ce({ops:e,expanded:r,onToggle:n}){const s=e[0],d=e[e.length-1];if(s===void 0||d===void 0)return t.jsx("div",{className:"text-muted-foreground/40 text-xs",children:"—"});const o=s.path,a=d.path,m=e.length===1?o:`${o} … ${a}`,i=s.value.kind==="array"?`${e.length} equal arrays`:s.value.kind==="object"?`${e.length} equal objects`:"equal",f=B.equal;return t.jsxs("div",{className:x("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(V,{className:x("size-3 transition-transform shrink-0",r&&"rotate-90")}),t.jsx(f.icon,{className:x("size-3 shrink-0",f.accent)}),t.jsx("span",{className:"font-mono truncate flex-1",title:`${o} … ${a}`,children:m}),t.jsx("span",{className:x("text-[10px] uppercase tracking-wider shrink-0",f.accent),children:f.label}),t.jsxs("span",{className:"text-muted-foreground/60 shrink-0",children:["(",i,")"]})]}),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(N,{text:w(p.value),defaultExpandDepth:0})]},p.path))})]})}function ue({op:e,idx:r,copiedPath:n,onCopyPath:s,expanded:d,onToggle:o}){const a=B[e.kind],m=a.icon,i=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:x("border-l-4 rounded-sm px-3 py-2 my-0.5 transition-colors",a.border,a.bg),children:[t.jsxs("button",{type:"button",onClick:o,disabled:!i,className:x("w-full flex items-center gap-2 text-xs text-left rounded-sm",i?"cursor-pointer":"cursor-default"),"aria-expanded":i?d:void 0,"aria-label":i?d?`Collapse ${e.path||"root"}`:`Expand ${e.path||"root"}`:void 0,children:[i?t.jsx(V,{className:x("size-3 shrink-0 transition-transform",a.accent,d&&"rotate-90")}):t.jsx("span",{className:"size-3 shrink-0","aria-hidden":"true"}),t.jsx(m,{className:x("size-3.5 shrink-0",a.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:x("text-[9px] font-bold uppercase tracking-wider shrink-0 px-1.5 py-0.5 rounded",a.accent,e.kind==="equal"?"bg-muted/40":"bg-background/60"),children:a.label}),e.path!==""&&t.jsx("span",{role:"button",tabIndex:0,onClick:b=>{b.stopPropagation(),s(e.path)},onKeyDown:b=>{(b.key==="Enter"||b.key===" ")&&(b.stopPropagation(),b.preventDefault(),s(e.path))},className:x("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(re,{className:"size-3"}):t.jsx(ne,{className:"size-3"})})]}),f.map((b,y)=>t.jsx("div",{className:x("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&&i?"2000px":"0"},"aria-hidden":!d,children:d&&i&&e.kind!=="equal"?t.jsx(me,{op:e}):null})]})}function me({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(N,{text:w(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(N,{text:w(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(N,{text:w(e.right),defaultExpandDepth:0})]})]})}function xe({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:x("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(_,{className:"size-3"}),e.removed," removed"]}),t.jsxs("button",{type:"button",onClick:()=>r("added"),disabled:e.added===0,className:x("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(J,{className:"size-3"}),e.added," added"]}),t.jsxs("button",{type:"button",onClick:()=>r("changed"),disabled:e.changed===0,className:x("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 fe({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:x("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(ee,{className:"size-3"}),"Unified"]}),t.jsxs("button",{type:"button",onClick:()=>r("split"),"aria-pressed":e==="split",className:x("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(te,{className:"size-3"}),"Split"]})]})}function A({log:e,side:r}){const n=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(Z,{variant:"outline",className:x("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",children:["#",e.id]}),e.model!==null&&t.jsx("span",{className:"font-mono text-muted-foreground truncate",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 +",$(e.cacheCreationInputTokens)]}),e.cacheReadInputTokens!==null&&e.cacheReadInputTokens>0&&t.jsxs("span",{className:"text-purple-400",children:["KV Cache ~",$(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:n,children:["session: ",n]})]})}function ve({left:e,right:r,onClose:n}){const s=h.useMemo(()=>{const l=O(P(e)).analyzeRequest(e.rawRequestBody),c=O(P(r)).analyzeRequest(r.rawRequestBody),u=D(l.comparisonValue),g=D(c.comparisonValue);return ie(u,g)},[e.apiFormat,e.path,e.rawRequestBody,r.apiFormat,r.path,r.rawRequestBody]),d=h.useMemo(()=>le(s),[s]),o=h.useMemo(()=>{let l=0,c=0,u=0;for(const g of d)if(g.kind==="single")switch(g.op.kind){case"added":l++;break;case"removed":c++;break;case"changed":u++;break}return{added:l,removed:c,changed:u}},[d]),[a,m]=h.useState(new Set),i=l=>{m(c=>{const u=new Set(c);return u.has(l)?u.delete(l):u.add(l),u})},[f,p]=h.useState(new Set),b=l=>{p(c=>{const u=new Set(c);return u.has(l)?u.delete(l):u.add(l),u})};h.useEffect(()=>{p(new Set)},[e.id,r.id]);const[y,F]=h.useState("unified"),T=h.useRef(null),[U,z]=h.useState(null),k=h.useRef(null),H=l=>{window.navigator.clipboard.writeText(l).then(()=>{z(l),k.current!==null&&clearTimeout(k.current),k.current=setTimeout(()=>z(null),1500)})};h.useEffect(()=>()=>{k.current!==null&&clearTimeout(k.current)},[]);const G=l=>{const c=d.findIndex(I=>I.kind==="single"&&I.op.kind===l);if(c===-1)return;const u=T.current;if(u===null)return;const g=u.querySelector(`[data-diff-idx="${c}"]`);g!==null&&g.scrollIntoView({behavior:"smooth",block:"center"})};h.useEffect(()=>{const l=u=>{u.key==="Escape"&&n()};document.addEventListener("keydown",l);const c=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",l),document.body.style.overflow=c}},[n]);const Q=q(e)===q(r),W=s.length===1&&s[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:n,"aria-label":"Close compare drawer",className:"absolute inset-0 bg-black/40 cursor-default",tabIndex:-1}),t.jsxs("div",{className:x("relative bg-background border-l border-border shadow-xl","w-full md:w-[70vw] max-w-[1100px] flex flex-col h-full"),onClick:l=>l.stopPropagation(),onKeyDown:l=>l.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(A,{log:e,side:"left"}),t.jsx(A,{log:r,side:"right"})]}),t.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[t.jsx(fe,{mode:y,onChange:F}),t.jsx("button",{type:"button",onClick:n,"aria-label":"Close",className:"p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted cursor-pointer",children:t.jsx(Y,{className:"size-4"})})]})]}),!Q&&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."}),W?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(xe,{counts:o,onJumpTo:G}),t.jsx("div",{ref:T,className:"flex-1 min-h-0 overflow-y-auto",children:y==="unified"?t.jsx("div",{className:"px-3 py-2 space-y-0.5",children:d.map((l,c)=>{if(l.kind==="equal-run")return t.jsx(ce,{ops:l.ops,expanded:a.has(c),onToggle:()=>i(c)},`r${c}`);const u=l.op;return t.jsx(ue,{op:u,idx:c,copiedPath:U,onCopyPath:H,expanded:f.has(c),onToggle:()=>b(c)},`o${c}`)})}):t.jsx(pe,{grouped:d,left:e,right:r})})]})]})]})}function pe({grouped:e,left:r,right:n}){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",{children:["Left (Log #",r.id,")"]}),t.jsxs("span",{children:["Right (Log #",n.id,")"]})]}),e.map((s,d)=>{if(s.kind==="equal-run")return t.jsxs("div",{className:"col-span-3 px-2 py-1 text-xs text-muted-foreground/60",children:[s.ops.length," equal siblings collapsed — switch to Unified to expand"]},d);const o=s.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)})]},d):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)]})]},d):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)]})]},d):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)})]})]},d)})]})}export{ve as CompareDrawer};
|