@tonyclaw/agent-inspector 3.1.19 → 3.1.21
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/backend/nitro.json +1 -1
- package/.output/cli.js +140 -20
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-0qlvqhVh.mjs → router-Be6BLuut.mjs} +2454 -1135
- package/.output/server/_tanstack-start-manifest_v-ClTaMkEj.mjs +4 -0
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-BOk9hd-Z.js → CompareDrawer-CGk2HFI1.js} +1 -1
- package/.output/ui/assets/{InspectorPet-DhHDBpk7.js → InspectorPet-CDM7SdOT.js} +1 -1
- package/.output/ui/assets/ProxyViewerContainer-CaWD78tw.js +60 -0
- package/.output/ui/assets/{ReplayDialog-DJhKtuNB.js → ReplayDialog-DqaeV1He.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-hP0tsqDC.js → RequestAnatomy-BtHl9P0k.js} +1 -1
- package/.output/ui/assets/{ResponseView-BSiUkwCb.js → ResponseView-C6t1vpxs.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-CzQqwv4X.js → StreamingChunkSequence-R4mgd3GQ.js} +1 -1
- package/.output/ui/assets/{_sessionId-BU-i6h0V.js → _sessionId-CL3hdLlQ.js} +1 -1
- package/.output/ui/assets/{_sessionId-C2Pq4Jb0.js → _sessionId-HS666S72.js} +1 -1
- package/.output/ui/assets/index-B-Qod-aA.css +1 -0
- package/.output/ui/assets/{index-D8C-t-2U.js → index-Bd9jPL4n.js} +1 -1
- package/.output/ui/assets/{index-zHpVF0jk.js → index-C4NHrclw.js} +1 -1
- package/.output/ui/assets/{index-UUnRwbOJ.js → index-UF1PJEcq.js} +1 -1
- package/.output/ui/assets/{index-B7NeIBDf.js → index-tX38BNV2.js} +2 -2
- package/.output/ui/assets/{json-viewer-DOzS5rUT.js → json-viewer-KSgzN_Ov.js} +1 -1
- package/.output/ui/assets/{jszip.min-CCCY7qNk.js → jszip.min-BiMDxghN.js} +1 -1
- package/.output/ui/index.html +2 -2
- package/.output/workers/logFinalizer.worker.js +261 -0
- package/.output/workers/sessionWorkerEntry.js +261 -0
- package/package.json +13 -6
- package/src/backend/routes/api/logs.$id.replay.ts +6 -1
- package/src/backend/routes/api/logs.stream.ts +7 -0
- package/src/backend/routes/api/providers.$providerId.model-metadata.ts +15 -4
- package/src/backend/routes/api/storage.ts +56 -0
- package/src/backend/routes/metrics.ts +14 -0
- package/src/cli/doctor.ts +135 -2
- package/src/cli.ts +7 -4
- package/src/components/ProxyViewerContainer.tsx +41 -24
- package/src/components/providers/SettingsDialog.tsx +261 -70
- package/src/components/proxy-viewer/LogEntry.tsx +19 -5
- package/src/components/proxy-viewer/bodyHydration.ts +67 -0
- package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +78 -0
- package/src/contracts/index.ts +2 -0
- package/src/contracts/log.ts +5 -0
- package/src/knowledge/openclawClient.ts +11 -2
- package/src/knowledge/openclawGatewayClient.ts +6 -1
- package/src/lib/resourceLimits.ts +191 -0
- package/src/lib/safeFetch.ts +428 -20
- package/src/lib/stopReason.ts +66 -39
- package/src/proxy/ecosystemTasks.ts +20 -0
- package/src/proxy/handler.ts +17 -4
- package/src/proxy/identityProxy.ts +36 -16
- package/src/proxy/logIndex.ts +57 -6
- package/src/proxy/logger.ts +4 -0
- package/src/proxy/rawStreamCapture.ts +16 -1
- package/src/proxy/runtimeHealth.ts +4 -2
- package/src/proxy/runtimeMetrics.ts +149 -0
- package/src/proxy/schemas.ts +2 -0
- package/src/proxy/sessionRuntime.ts +38 -0
- package/src/proxy/sqliteLogIndex.ts +78 -2
- package/src/proxy/storageLifecycle.ts +432 -0
- package/src/proxy/store.ts +69 -13
- package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +0 -4
- package/.output/ui/assets/ProxyViewerContainer-BRYf987G.js +0 -59
- package/.output/ui/assets/index-IBEDJoV_.css +0 -1
package/src/cli/doctor.ts
CHANGED
|
@@ -35,6 +35,7 @@ export type DoctorOptions = {
|
|
|
35
35
|
chromeExtension: boolean;
|
|
36
36
|
fix: boolean;
|
|
37
37
|
json: boolean;
|
|
38
|
+
supportBundle: boolean;
|
|
38
39
|
verbose: boolean;
|
|
39
40
|
help: boolean;
|
|
40
41
|
};
|
|
@@ -48,6 +49,12 @@ export type HealthProbe = {
|
|
|
48
49
|
status: number | null;
|
|
49
50
|
};
|
|
50
51
|
|
|
52
|
+
export type ReadinessProbe = {
|
|
53
|
+
ok: boolean;
|
|
54
|
+
status: number | null;
|
|
55
|
+
body: unknown;
|
|
56
|
+
};
|
|
57
|
+
|
|
51
58
|
export type DoctorDeps = {
|
|
52
59
|
env: Readonly<Record<string, string | undefined>>;
|
|
53
60
|
platform: typeof process.platform;
|
|
@@ -57,17 +64,35 @@ export type DoctorDeps = {
|
|
|
57
64
|
listDir: (path: string) => readonly string[] | null;
|
|
58
65
|
stat: (path: string) => { isDirectory: boolean; size: number } | null;
|
|
59
66
|
fetchHealth: (port: number, basePath: string) => Promise<HealthProbe>;
|
|
67
|
+
fetchReadiness: (port: number, basePath: string) => Promise<ReadinessProbe>;
|
|
60
68
|
isPortOpen: (port: number) => Promise<boolean>;
|
|
61
69
|
};
|
|
62
70
|
|
|
63
71
|
export type DoctorReport = {
|
|
64
72
|
checks: readonly DoctorCheck[];
|
|
73
|
+
readiness: ReadinessProbe | null;
|
|
65
74
|
passCount: number;
|
|
66
75
|
warnCount: number;
|
|
67
76
|
failCount: number;
|
|
68
77
|
exitCode: number;
|
|
69
78
|
};
|
|
70
79
|
|
|
80
|
+
export type DoctorSupportBundle = {
|
|
81
|
+
schema: "agent-inspector.support-bundle/v1";
|
|
82
|
+
createdAt: string;
|
|
83
|
+
report: DoctorReport;
|
|
84
|
+
environment: {
|
|
85
|
+
platform: typeof process.platform;
|
|
86
|
+
cwd: string;
|
|
87
|
+
basePath: string;
|
|
88
|
+
port: number;
|
|
89
|
+
hasControlToken: boolean;
|
|
90
|
+
hasProxyToken: boolean;
|
|
91
|
+
hasProviderJsonOverride: boolean;
|
|
92
|
+
};
|
|
93
|
+
storage: readonly DoctorCheck[];
|
|
94
|
+
};
|
|
95
|
+
|
|
71
96
|
function check(name: string, severity: DoctorSeverity, message: string, hint = ""): DoctorCheck {
|
|
72
97
|
return {
|
|
73
98
|
name,
|
|
@@ -96,6 +121,7 @@ export function parseDoctorArgs(
|
|
|
96
121
|
chromeExtension: false,
|
|
97
122
|
fix: false,
|
|
98
123
|
json: false,
|
|
124
|
+
supportBundle: false,
|
|
99
125
|
verbose: false,
|
|
100
126
|
help: false,
|
|
101
127
|
};
|
|
@@ -146,6 +172,10 @@ export function parseDoctorArgs(
|
|
|
146
172
|
case "--json":
|
|
147
173
|
options.json = true;
|
|
148
174
|
break;
|
|
175
|
+
case "--support-bundle":
|
|
176
|
+
options.supportBundle = true;
|
|
177
|
+
options.json = true;
|
|
178
|
+
break;
|
|
149
179
|
case "--verbose":
|
|
150
180
|
options.verbose = true;
|
|
151
181
|
break;
|
|
@@ -316,6 +346,42 @@ async function checkProxyHealth(port: number, deps: DoctorDeps): Promise<DoctorC
|
|
|
316
346
|
);
|
|
317
347
|
}
|
|
318
348
|
|
|
349
|
+
function readinessChecks(readiness: ReadinessProbe): readonly DoctorCheck[] {
|
|
350
|
+
if (!readiness.ok) {
|
|
351
|
+
const suffix = readiness.status === null ? "" : ` (HTTP ${readiness.status})`;
|
|
352
|
+
return [
|
|
353
|
+
check(
|
|
354
|
+
"Readiness",
|
|
355
|
+
"warn",
|
|
356
|
+
`Readiness probe is unavailable${suffix}.`,
|
|
357
|
+
"Use /readyz locally or run with --verbose for storage and worker hints.",
|
|
358
|
+
),
|
|
359
|
+
];
|
|
360
|
+
}
|
|
361
|
+
if (!isObject(readiness.body)) {
|
|
362
|
+
return [check("Readiness", "warn", "Readiness response was not an object.")];
|
|
363
|
+
}
|
|
364
|
+
const status = readiness.body["status"];
|
|
365
|
+
const checks = readiness.body["checks"];
|
|
366
|
+
if (typeof status !== "string" || !isObject(checks)) {
|
|
367
|
+
return [check("Readiness", "warn", "Readiness response did not include status/checks.")];
|
|
368
|
+
}
|
|
369
|
+
const degraded = Object.entries(checks)
|
|
370
|
+
.filter(([, value]) => value !== "ok")
|
|
371
|
+
.map(([name, value]) => `${name}=${String(value)}`);
|
|
372
|
+
if (degraded.length === 0) {
|
|
373
|
+
return [check("Readiness", "pass", `Runtime readiness is ${status}.`)];
|
|
374
|
+
}
|
|
375
|
+
return [
|
|
376
|
+
check(
|
|
377
|
+
"Readiness",
|
|
378
|
+
status === "unavailable" ? "fail" : "warn",
|
|
379
|
+
`Runtime readiness is ${status}: ${degraded.join(", ")}.`,
|
|
380
|
+
"Inspect /readyz and /metrics for safe aggregate runtime details.",
|
|
381
|
+
),
|
|
382
|
+
];
|
|
383
|
+
}
|
|
384
|
+
|
|
319
385
|
function findProjectRoot(startDir: string, deps: Pick<DoctorDeps, "exists">): string {
|
|
320
386
|
let current = resolve(startDir);
|
|
321
387
|
for (let i = 0; i < 6; i++) {
|
|
@@ -549,8 +615,11 @@ export async function buildDoctorReport(
|
|
|
549
615
|
deps: DoctorDeps = createDoctorDeps(),
|
|
550
616
|
): Promise<DoctorReport> {
|
|
551
617
|
const rootDir = findProjectRoot(deps.cwd, deps);
|
|
618
|
+
const basePath = getConfiguredBasePath(deps.env);
|
|
619
|
+
const readiness = await deps.fetchReadiness(options.port, basePath);
|
|
552
620
|
const coreChecks = [
|
|
553
621
|
await checkProxyHealth(options.port, deps),
|
|
622
|
+
...readinessChecks(readiness),
|
|
554
623
|
checkProviderConfig(options, deps),
|
|
555
624
|
checkPackage(rootDir, deps),
|
|
556
625
|
];
|
|
@@ -567,6 +636,7 @@ export async function buildDoctorReport(
|
|
|
567
636
|
|
|
568
637
|
return {
|
|
569
638
|
checks,
|
|
639
|
+
readiness,
|
|
570
640
|
passCount,
|
|
571
641
|
warnCount,
|
|
572
642
|
failCount,
|
|
@@ -574,6 +644,31 @@ export async function buildDoctorReport(
|
|
|
574
644
|
};
|
|
575
645
|
}
|
|
576
646
|
|
|
647
|
+
export function buildDoctorSupportBundle(
|
|
648
|
+
options: DoctorOptions,
|
|
649
|
+
deps: DoctorDeps,
|
|
650
|
+
report: DoctorReport,
|
|
651
|
+
): DoctorSupportBundle {
|
|
652
|
+
return {
|
|
653
|
+
schema: "agent-inspector.support-bundle/v1",
|
|
654
|
+
createdAt: new Date().toISOString(),
|
|
655
|
+
report,
|
|
656
|
+
environment: {
|
|
657
|
+
platform: deps.platform,
|
|
658
|
+
cwd: deps.cwd,
|
|
659
|
+
basePath: getConfiguredBasePath(deps.env),
|
|
660
|
+
port: options.port,
|
|
661
|
+
hasControlToken:
|
|
662
|
+
deps.env["AGENT_INSPECTOR_CONTROL_TOKEN"] !== undefined ||
|
|
663
|
+
deps.env["AGENT_INSPECTOR_API_TOKEN"] !== undefined,
|
|
664
|
+
hasProxyToken: deps.env["AGENT_INSPECTOR_PROXY_TOKEN"] !== undefined,
|
|
665
|
+
hasProviderJsonOverride:
|
|
666
|
+
options.providersJson !== null || deps.env["AGENT_INSPECTOR_PROVIDERS_JSON"] !== undefined,
|
|
667
|
+
},
|
|
668
|
+
storage: verboseChecks(options, deps).filter((item) => item.name.includes("storage")),
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
577
672
|
export function formatDoctorReport(report: DoctorReport): string {
|
|
578
673
|
const lines = ["agent-inspector doctor", ""];
|
|
579
674
|
for (const item of report.checks) {
|
|
@@ -593,17 +688,22 @@ export function formatDoctorJson(report: DoctorReport): string {
|
|
|
593
688
|
return `${JSON.stringify(report, null, 2)}\n`;
|
|
594
689
|
}
|
|
595
690
|
|
|
691
|
+
export function formatDoctorSupportBundle(bundle: DoctorSupportBundle): string {
|
|
692
|
+
return `${JSON.stringify(bundle, null, 2)}\n`;
|
|
693
|
+
}
|
|
694
|
+
|
|
596
695
|
export function doctorHelp(): string {
|
|
597
696
|
return [
|
|
598
697
|
"agent-inspector doctor",
|
|
599
698
|
"",
|
|
600
699
|
"Usage:",
|
|
601
|
-
" agent-inspector doctor [--port <port>] [--config-dir <dir>] [--providers <json>] [--fix] [--json] [--verbose] [--chrome-extension]",
|
|
700
|
+
" agent-inspector doctor [--port <port>] [--config-dir <dir>] [--providers <json>] [--fix] [--json] [--support-bundle] [--verbose] [--chrome-extension]",
|
|
602
701
|
"",
|
|
603
702
|
"Checks local backend health, provider config presence, and package metadata.",
|
|
604
703
|
"The backend check probes the local health endpoint and does not require a composed Web UI.",
|
|
605
704
|
"Pass --fix to run safe local repairs before checks, including legacy data-dir migration.",
|
|
606
705
|
"Pass --json to emit a machine-readable report for CI, scripts, and Jenkins.",
|
|
706
|
+
"Pass --support-bundle to emit a redacted JSON bundle with readiness causes and storage summary.",
|
|
607
707
|
"Pass --verbose to include local paths, capture mode, and log storage checks.",
|
|
608
708
|
"Pass --chrome-extension to also check Chrome companion source and packaged artifacts.",
|
|
609
709
|
"",
|
|
@@ -629,6 +729,31 @@ async function fetchHealth(port: number, basePath: string): Promise<HealthProbe>
|
|
|
629
729
|
}
|
|
630
730
|
}
|
|
631
731
|
|
|
732
|
+
async function fetchReadiness(port: number, basePath: string): Promise<ReadinessProbe> {
|
|
733
|
+
const controller = new AbortController();
|
|
734
|
+
const timeout = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
735
|
+
try {
|
|
736
|
+
const response = await fetch(
|
|
737
|
+
appendBasePathToOrigin(`http://127.0.0.1:${port}`, "/readyz", basePath),
|
|
738
|
+
{
|
|
739
|
+
cache: "no-store",
|
|
740
|
+
signal: controller.signal,
|
|
741
|
+
},
|
|
742
|
+
);
|
|
743
|
+
let body: unknown = null;
|
|
744
|
+
try {
|
|
745
|
+
body = await response.json();
|
|
746
|
+
} catch {
|
|
747
|
+
body = null;
|
|
748
|
+
}
|
|
749
|
+
return { ok: response.ok, status: response.status, body };
|
|
750
|
+
} catch {
|
|
751
|
+
return { ok: false, status: null, body: null };
|
|
752
|
+
} finally {
|
|
753
|
+
clearTimeout(timeout);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
632
757
|
function isPortOpen(port: number): Promise<boolean> {
|
|
633
758
|
return new Promise((resolveOpen) => {
|
|
634
759
|
const socket = createConnection({ host: "127.0.0.1", port });
|
|
@@ -680,6 +805,7 @@ export function createDoctorDeps(): DoctorDeps {
|
|
|
680
805
|
listDir,
|
|
681
806
|
stat,
|
|
682
807
|
fetchHealth,
|
|
808
|
+
fetchReadiness,
|
|
683
809
|
isPortOpen,
|
|
684
810
|
};
|
|
685
811
|
}
|
|
@@ -698,6 +824,13 @@ export async function runDoctor(argv: readonly string[]): Promise<number> {
|
|
|
698
824
|
migrateLegacyDataDirs();
|
|
699
825
|
}
|
|
700
826
|
const report = await buildDoctorReport(parsed.options);
|
|
701
|
-
|
|
827
|
+
if (parsed.options.supportBundle) {
|
|
828
|
+
const bundle = buildDoctorSupportBundle(parsed.options, createDoctorDeps(), report);
|
|
829
|
+
process.stdout.write(formatDoctorSupportBundle(bundle));
|
|
830
|
+
} else {
|
|
831
|
+
process.stdout.write(
|
|
832
|
+
parsed.options.json ? formatDoctorJson(report) : formatDoctorReport(report),
|
|
833
|
+
);
|
|
834
|
+
}
|
|
702
835
|
return report.exitCode;
|
|
703
836
|
}
|
package/src/cli.ts
CHANGED
|
@@ -1137,18 +1137,21 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
1137
1137
|
const backgroundSupervisorLogs = runningInBackgroundSupervisor
|
|
1138
1138
|
? createBackgroundSupervisorLogs(serverEnv)
|
|
1139
1139
|
: null;
|
|
1140
|
+
const shouldPipeBackgroundRuntimeLogs = backgroundSupervisorLogs !== null;
|
|
1140
1141
|
const serverProcess = spawn(serverCommand.command, serverCommand.args, {
|
|
1141
1142
|
stdio:
|
|
1142
1143
|
managedLaunch === null
|
|
1143
|
-
? background
|
|
1144
|
+
? background && !shouldPipeBackgroundRuntimeLogs
|
|
1144
1145
|
? ["ignore", "ignore", "ignore"]
|
|
1145
1146
|
: ["ignore", "pipe", "pipe"]
|
|
1146
1147
|
: backend === "typescript"
|
|
1147
|
-
? background
|
|
1148
|
+
? background && !shouldPipeBackgroundRuntimeLogs
|
|
1148
1149
|
? ["ignore", "ignore", "ignore", "ipc"]
|
|
1149
1150
|
: ["ignore", "pipe", "pipe", "ipc"]
|
|
1150
1151
|
: background
|
|
1151
|
-
?
|
|
1152
|
+
? shouldPipeBackgroundRuntimeLogs
|
|
1153
|
+
? ["ignore", "pipe", "pipe"]
|
|
1154
|
+
: ["ignore", "ignore", "ignore"]
|
|
1152
1155
|
: ["ignore", "pipe", "pipe"],
|
|
1153
1156
|
detached: background,
|
|
1154
1157
|
env: serverEnv,
|
|
@@ -1158,7 +1161,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
1158
1161
|
console.error(`Failed to start Agent Inspector runtime: ${err.message}`);
|
|
1159
1162
|
});
|
|
1160
1163
|
const serverExit = waitForProcessExit(serverProcess);
|
|
1161
|
-
if (!background) {
|
|
1164
|
+
if (!background || backgroundSupervisorLogs !== null) {
|
|
1162
1165
|
const outputContext = {
|
|
1163
1166
|
enableIdentityProxy,
|
|
1164
1167
|
publicPort: port,
|
|
@@ -18,7 +18,9 @@ import {
|
|
|
18
18
|
import type { SessionLoadProgress } from "./proxy-viewer/SessionLoadProgressBar";
|
|
19
19
|
import { browserPrefersReducedMotion, preferredScrollBehavior } from "./proxy-viewer/accessibility";
|
|
20
20
|
import {
|
|
21
|
+
buildLogIndexById,
|
|
21
22
|
filterLogs,
|
|
23
|
+
mergeLogsIncrementally,
|
|
22
24
|
mergeLogsById,
|
|
23
25
|
mergeSessionIds,
|
|
24
26
|
} from "./proxy-viewer/proxyViewerContainerLogic";
|
|
@@ -133,6 +135,7 @@ const SESSION_LOGS_FETCH_TIMEOUT_MS = 20_000;
|
|
|
133
135
|
const SESSION_MEMBERSHIP_FETCH_TIMEOUT_MS = 10_000;
|
|
134
136
|
const SSE_RECONNECT_DELAY_MS = 3_000;
|
|
135
137
|
const SSE_STALE_AFTER_MS = 10_000;
|
|
138
|
+
const LIVE_RECENT_LOG_LIMIT = 2_000;
|
|
136
139
|
|
|
137
140
|
function buildSessionLogsPageUrl(
|
|
138
141
|
sessionId: string,
|
|
@@ -163,15 +166,6 @@ function buildSessionLogsPageUrl(
|
|
|
163
166
|
return `/api/logs?${params.toString()}`;
|
|
164
167
|
}
|
|
165
168
|
|
|
166
|
-
function buildLogIndex(logs: readonly CapturedLog[]): Map<number, number> {
|
|
167
|
-
const idx = new Map<number, number>();
|
|
168
|
-
for (let i = 0; i < logs.length; i++) {
|
|
169
|
-
const log = logs[i];
|
|
170
|
-
if (log !== undefined) idx.set(log.id, i);
|
|
171
|
-
}
|
|
172
|
-
return idx;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
169
|
function fetchSessionLogsPage(
|
|
176
170
|
sessionId: string,
|
|
177
171
|
request: SessionPageRequest,
|
|
@@ -275,6 +269,14 @@ export function ProxyViewerContainer({
|
|
|
275
269
|
// Debounce buffer for SSE updates
|
|
276
270
|
const pendingUpdatesRef = useRef<CapturedLog[]>([]);
|
|
277
271
|
const flushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
272
|
+
const logMergeOptions = useCallback(
|
|
273
|
+
() => ({
|
|
274
|
+
recentLimit: LIVE_RECENT_LOG_LIMIT,
|
|
275
|
+
retainSessionId:
|
|
276
|
+
initialSessionId ?? (selectedSession === "__all__" ? undefined : selectedSession),
|
|
277
|
+
}),
|
|
278
|
+
[initialSessionId, selectedSession],
|
|
279
|
+
);
|
|
278
280
|
const showErrorToast = useCallback(
|
|
279
281
|
(message: string) => {
|
|
280
282
|
showToast({ message, tone: "error" });
|
|
@@ -359,12 +361,17 @@ export function ProxyViewerContainer({
|
|
|
359
361
|
|
|
360
362
|
startLogTransition(() => {
|
|
361
363
|
setAllLogs((prev) => {
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
364
|
+
const merged = mergeLogsIncrementally(
|
|
365
|
+
prev,
|
|
366
|
+
logIndexRef.current,
|
|
367
|
+
updates,
|
|
368
|
+
logMergeOptions(),
|
|
369
|
+
);
|
|
370
|
+
logIndexRef.current = merged.index;
|
|
371
|
+
return merged.logs;
|
|
365
372
|
});
|
|
366
373
|
});
|
|
367
|
-
}, [startLogTransition]);
|
|
374
|
+
}, [logMergeOptions, startLogTransition]);
|
|
368
375
|
|
|
369
376
|
const scheduleUpdate = useCallback(
|
|
370
377
|
(log: CapturedLog) => {
|
|
@@ -425,9 +432,14 @@ export function ProxyViewerContainer({
|
|
|
425
432
|
pendingUpdatesRef.current = [];
|
|
426
433
|
|
|
427
434
|
setAllLogs((prev) => {
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
435
|
+
const merged = mergeLogsIncrementally(
|
|
436
|
+
prev,
|
|
437
|
+
logIndexRef.current,
|
|
438
|
+
update.logs,
|
|
439
|
+
logMergeOptions(),
|
|
440
|
+
);
|
|
441
|
+
logIndexRef.current = merged.index;
|
|
442
|
+
return merged.logs;
|
|
431
443
|
});
|
|
432
444
|
setKnownSessions((prev) => mergeSessionIds(prev, extractSessions(update.logs)));
|
|
433
445
|
if (staleTimeoutRef.current !== null) {
|
|
@@ -523,7 +535,7 @@ export function ProxyViewerContainer({
|
|
|
523
535
|
.then((page) => {
|
|
524
536
|
if (sessionPageRequestIdRef.current !== requestId) return;
|
|
525
537
|
const nextLogs = page.logs;
|
|
526
|
-
logIndexRef.current =
|
|
538
|
+
logIndexRef.current = buildLogIndexById(nextLogs);
|
|
527
539
|
setAllLogs(nextLogs);
|
|
528
540
|
setLogPage(page);
|
|
529
541
|
setSelectedSession(initialSessionId);
|
|
@@ -592,9 +604,14 @@ export function ProxyViewerContainer({
|
|
|
592
604
|
|
|
593
605
|
if (sessionPageRequestIdRef.current !== requestId) return;
|
|
594
606
|
setAllLogs((prev) => {
|
|
595
|
-
const
|
|
596
|
-
|
|
597
|
-
|
|
607
|
+
const merged = mergeLogsIncrementally(
|
|
608
|
+
prev,
|
|
609
|
+
logIndexRef.current,
|
|
610
|
+
collected,
|
|
611
|
+
logMergeOptions(),
|
|
612
|
+
);
|
|
613
|
+
logIndexRef.current = merged.index;
|
|
614
|
+
return merged.logs;
|
|
598
615
|
});
|
|
599
616
|
setKnownSessions((prev) => mergeSessionIds(prev, [sessionId]));
|
|
600
617
|
setSessionPageLoading(false);
|
|
@@ -796,7 +813,7 @@ export function ProxyViewerContainer({
|
|
|
796
813
|
const removed = prev.filter((log) => idSet.has(log.id));
|
|
797
814
|
const remaining = prev.filter((log) => !idSet.has(log.id));
|
|
798
815
|
setKnownSessions((known) => mergeSessionIds(known, extractSessions(removed)));
|
|
799
|
-
logIndexRef.current =
|
|
816
|
+
logIndexRef.current = buildLogIndexById(remaining);
|
|
800
817
|
return remaining;
|
|
801
818
|
});
|
|
802
819
|
showToast({ message: "Cleared this group from the current view.", tone: "success" });
|
|
@@ -856,7 +873,7 @@ export function ProxyViewerContainer({
|
|
|
856
873
|
if (sessionPageRequestIdRef.current !== deleteRequestId) return;
|
|
857
874
|
setAllLogs((prev) => {
|
|
858
875
|
const remaining = prev.filter((log) => log.sessionId !== sessionId);
|
|
859
|
-
logIndexRef.current =
|
|
876
|
+
logIndexRef.current = buildLogIndexById(remaining);
|
|
860
877
|
return remaining;
|
|
861
878
|
});
|
|
862
879
|
setLogPage((prev) =>
|
|
@@ -886,7 +903,7 @@ export function ProxyViewerContainer({
|
|
|
886
903
|
if (sessionPageRequestIdRef.current !== deleteRequestId) return;
|
|
887
904
|
setAllLogs((prev) => {
|
|
888
905
|
const remaining = prev.filter((log) => log.sessionId !== sessionId);
|
|
889
|
-
logIndexRef.current =
|
|
906
|
+
logIndexRef.current = buildLogIndexById(remaining);
|
|
890
907
|
return remaining;
|
|
891
908
|
});
|
|
892
909
|
setLogPage((prev) =>
|
|
@@ -949,7 +966,7 @@ export function ProxyViewerContainer({
|
|
|
949
966
|
prev.filter((log) => !importedIds.has(log.id)),
|
|
950
967
|
result.logs,
|
|
951
968
|
);
|
|
952
|
-
logIndexRef.current =
|
|
969
|
+
logIndexRef.current = buildLogIndexById(next);
|
|
953
970
|
return next;
|
|
954
971
|
});
|
|
955
972
|
setSelectedSession(result.sessionId);
|