@tonyclaw/agent-inspector 2.0.17 → 2.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/.output/cli.js +149 -40
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-DDg7Cs4N.js → CompareDrawer-D-90PIPw.js} +1 -1
  4. package/.output/public/assets/ProxyViewerContainer-CxRDWacg.js +114 -0
  5. package/.output/public/assets/ReplayDialog-DzlLYoTO.js +1 -0
  6. package/.output/public/assets/{RequestAnatomy-DR8OICOJ.js → RequestAnatomy-0hbyf6oa.js} +1 -1
  7. package/.output/public/assets/ResponseView-CKe0tNqr.js +1 -0
  8. package/.output/public/assets/StreamingChunkSequence-enhfdeNh.js +1 -0
  9. package/.output/public/assets/_sessionId-CJ-SusES.js +1 -0
  10. package/.output/public/assets/index-BbFsmQRC.js +1 -0
  11. package/.output/public/assets/index-BfBgrkgU.css +1 -0
  12. package/.output/public/assets/{main-bwZlEXw2.js → main-o9-hg8g1.js} +2 -2
  13. package/.output/server/_libs/lucide-react.mjs +141 -118
  14. package/.output/server/_libs/tanstack__react-virtual.mjs +13 -2
  15. package/.output/server/_libs/tanstack__virtual-core.mjs +24 -1
  16. package/.output/server/{_sessionId-BEuJJhqT.mjs → _sessionId-F8lm6PIb.mjs} +3 -2
  17. package/.output/server/_ssr/{CompareDrawer-yTO93GMz.mjs → CompareDrawer-Cf1yfwfd.mjs} +4 -3
  18. package/.output/server/_ssr/{ProxyViewerContainer-C8qCkHGB.mjs → ProxyViewerContainer-Bmu9867L.mjs} +349 -74
  19. package/.output/server/_ssr/{ReplayDialog-DRF9PG7Z.mjs → ReplayDialog-DxZfLBOe.mjs} +199 -17
  20. package/.output/server/_ssr/{RequestAnatomy-DBcuV_jV.mjs → RequestAnatomy-CbdERGZd.mjs} +4 -3
  21. package/.output/server/_ssr/{ResponseView-JJm78HZT.mjs → ResponseView-CvLd6qxI.mjs} +4 -3
  22. package/.output/server/_ssr/{StreamingChunkSequence-DyGKjBYx.mjs → StreamingChunkSequence-B_lkRB6o.mjs} +4 -3
  23. package/.output/server/_ssr/{index-T7JG28t6.mjs → index-ejrSItfz.mjs} +3 -2
  24. package/.output/server/_ssr/index.mjs +2 -2
  25. package/.output/server/_ssr/{router-BJob8RN3.mjs → router-SbCnbDc2.mjs} +896 -240
  26. package/.output/server/{_tanstack-start-manifest_v-CuiFi3XC.mjs → _tanstack-start-manifest_v-yx8ftqWz.mjs} +1 -1
  27. package/.output/server/index.mjs +60 -60
  28. package/README.md +26 -109
  29. package/package.json +2 -1
  30. package/src/cli/doctor.ts +160 -8
  31. package/src/cli/templates/skill-onboard.ts +2 -20
  32. package/src/components/OnboardingBanner.tsx +13 -13
  33. package/src/components/ProxyViewer.tsx +55 -38
  34. package/src/components/ProxyViewerContainer.tsx +5 -0
  35. package/src/components/providers/SettingsDialog.tsx +113 -2
  36. package/src/components/proxy-viewer/ConversationGroup.tsx +20 -3
  37. package/src/components/proxy-viewer/ConversationGroupList.tsx +144 -0
  38. package/src/components/proxy-viewer/ConversationHeader.tsx +0 -2
  39. package/src/components/proxy-viewer/ReplayDialog.tsx +200 -13
  40. package/src/components/proxy-viewer/index.ts +1 -1
  41. package/src/knowledge/openclawClient.ts +0 -8
  42. package/src/knowledge/types.ts +0 -2
  43. package/src/lib/export-logs.ts +72 -5
  44. package/src/lib/providerTestContract.ts +0 -1
  45. package/src/lib/useProviders.ts +1 -1
  46. package/src/mcp/server.ts +14 -6
  47. package/src/mcp/toolHandlers.ts +21 -4
  48. package/src/proxy/chunkStorage.ts +2 -4
  49. package/src/proxy/dataDir.ts +37 -15
  50. package/src/proxy/formats/anthropic/index.ts +0 -1
  51. package/src/proxy/formats/anthropic/schemas.ts +0 -2
  52. package/src/proxy/handler.ts +41 -29
  53. package/src/proxy/logFinalizer.ts +93 -11
  54. package/src/proxy/logger.ts +35 -10
  55. package/src/proxy/providers.ts +12 -5
  56. package/src/proxy/rawStreamCapture.ts +85 -0
  57. package/src/proxy/schemas.ts +1 -51
  58. package/src/proxy/sessionRuntime.ts +0 -9
  59. package/src/proxy/socketTracker.ts +15 -8
  60. package/src/proxy/store.ts +414 -13
  61. package/src/routes/api/logs.$id.replay.ts +66 -1
  62. package/src/routes/api/logs.stream.ts +11 -3
  63. package/src/routes/api/logs.ts +51 -17
  64. package/src/routes/api/providers.$providerId.ts +10 -3
  65. package/src/routes/api/providers.ts +6 -3
  66. package/.output/public/assets/ProxyViewerContainer-DbbK1y7N.js +0 -114
  67. package/.output/public/assets/ReplayDialog-C1zWNkoz.js +0 -1
  68. package/.output/public/assets/ResponseView-Dh9iSj6h.js +0 -1
  69. package/.output/public/assets/StreamingChunkSequence-B_xElyH3.js +0 -1
  70. package/.output/public/assets/_sessionId-gv1a_NNu.js +0 -1
  71. package/.output/public/assets/index-D_nZj9Vt.css +0 -1
  72. package/.output/public/assets/index-u-LwDaeH.js +0 -1
  73. package/src/components/ui/json-expansion-button.tsx +0 -56
@@ -1,6 +1,6 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { z } from "zod";
3
- import { getLogById } from "../../proxy/store";
3
+ import { addTestLogEntry, getLogById } from "../../proxy/store";
4
4
  import { findProviderByModel } from "../../proxy/providers";
5
5
  import { registry } from "../../proxy/formats";
6
6
  import { formatForPath } from "../../proxy/formats";
@@ -39,6 +39,7 @@ type ReplayResponse = {
39
39
  outputTokens?: number;
40
40
  elapsedMs?: number;
41
41
  streaming?: boolean;
42
+ replayLogId?: number;
42
43
  };
43
44
 
44
45
  export const Route = createFileRoute("/api/logs/$id/replay")({
@@ -161,6 +162,37 @@ export const Route = createFileRoute("/api/logs/$id/replay")({
161
162
  const mockLog: typeof log = { ...log };
162
163
  const responseText = formatHandler.extractStream(fullResponse, mockLog, model, false);
163
164
  const tokens = formatHandler.extractTokens(responseText);
165
+ const savedReplayLog = await addTestLogEntry({
166
+ timestamp: new Date().toISOString(),
167
+ method: log.method,
168
+ path: log.path,
169
+ model,
170
+ sessionId: log.sessionId,
171
+ rawRequestBody: modifiedBody,
172
+ responseStatus: upstreamRes.status,
173
+ responseText,
174
+ inputTokens: tokens.inputTokens ?? null,
175
+ outputTokens: tokens.outputTokens ?? null,
176
+ cacheCreationInputTokens: tokens.cacheCreationInputTokens ?? null,
177
+ cacheReadInputTokens: tokens.cacheReadInputTokens ?? null,
178
+ elapsedMs,
179
+ streaming: true,
180
+ userAgent: log.userAgent,
181
+ origin: log.origin,
182
+ rawHeaders: log.rawHeaders,
183
+ headers: log.headers,
184
+ apiFormat: log.apiFormat,
185
+ isTest: true,
186
+ replayOfLogId: log.id,
187
+ providerName: log.providerName,
188
+ clientPort: log.clientPort,
189
+ clientPid: log.clientPid,
190
+ clientCwd: log.clientCwd,
191
+ clientProjectFolder: log.clientProjectFolder,
192
+ streamingChunks: undefined,
193
+ streamingChunksPath: null,
194
+ error: null,
195
+ });
164
196
 
165
197
  return Response.json({
166
198
  success: true,
@@ -170,10 +202,42 @@ export const Route = createFileRoute("/api/logs/$id/replay")({
170
202
  outputTokens: tokens.outputTokens ?? undefined,
171
203
  elapsedMs,
172
204
  streaming: true,
205
+ replayLogId: savedReplayLog.id,
173
206
  } satisfies ReplayResponse);
174
207
  } else {
175
208
  const responseText = await upstreamRes.text();
176
209
  const tokens = formatHandler.extractTokens(responseText);
210
+ const savedReplayLog = await addTestLogEntry({
211
+ timestamp: new Date().toISOString(),
212
+ method: log.method,
213
+ path: log.path,
214
+ model,
215
+ sessionId: log.sessionId,
216
+ rawRequestBody: modifiedBody,
217
+ responseStatus: upstreamRes.status,
218
+ responseText,
219
+ inputTokens: tokens.inputTokens ?? null,
220
+ outputTokens: tokens.outputTokens ?? null,
221
+ cacheCreationInputTokens: tokens.cacheCreationInputTokens ?? null,
222
+ cacheReadInputTokens: tokens.cacheReadInputTokens ?? null,
223
+ elapsedMs,
224
+ streaming: false,
225
+ userAgent: log.userAgent,
226
+ origin: log.origin,
227
+ rawHeaders: log.rawHeaders,
228
+ headers: log.headers,
229
+ apiFormat: log.apiFormat,
230
+ isTest: true,
231
+ replayOfLogId: log.id,
232
+ providerName: log.providerName,
233
+ clientPort: log.clientPort,
234
+ clientPid: log.clientPid,
235
+ clientCwd: log.clientCwd,
236
+ clientProjectFolder: log.clientProjectFolder,
237
+ streamingChunks: undefined,
238
+ streamingChunksPath: null,
239
+ error: null,
240
+ });
177
241
 
178
242
  return Response.json({
179
243
  success: true,
@@ -183,6 +247,7 @@ export const Route = createFileRoute("/api/logs/$id/replay")({
183
247
  outputTokens: tokens.outputTokens ?? undefined,
184
248
  elapsedMs,
185
249
  streaming: false,
250
+ replayLogId: savedReplayLog.id,
186
251
  } satisfies ReplayResponse);
187
252
  }
188
253
  },
@@ -1,7 +1,9 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { onLogUpdate, getFilteredLogs, getLogSessionId } from "../../proxy/store";
2
+ import { onLogUpdate, getLogSessionId, listLogsPage } from "../../proxy/store";
3
3
  import { type CapturedLog } from "../../proxy/schemas";
4
4
 
5
+ const INITIAL_STREAM_LOG_LIMIT = 500;
6
+
5
7
  export const Route = createFileRoute("/api/logs/stream")({
6
8
  server: {
7
9
  handlers: {
@@ -49,10 +51,16 @@ export const Route = createFileRoute("/api/logs/stream")({
49
51
  request.signal.addEventListener("abort", cleanup, { once: true });
50
52
 
51
53
  const stream = new ReadableStream<Uint8Array>({
52
- start(controller) {
54
+ async start(controller) {
53
55
  controllerRef = controller;
54
56
  // Send initial batch of logs immediately
55
- const logs = getFilteredLogs(sessionId, model);
57
+ const result = await listLogsPage({
58
+ sessionId,
59
+ model,
60
+ offset: 0,
61
+ limit: INITIAL_STREAM_LOG_LIMIT,
62
+ });
63
+ const logs = result.logs;
56
64
  const initData = `data: ${JSON.stringify({ type: "init", logs })}\n\n`;
57
65
  controller.enqueue(new TextEncoder().encode(initData));
58
66
  },
@@ -1,6 +1,12 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { z } from "zod";
3
- import { clearAllLogs, clearLogsByIds, getFilteredLogs } from "../../proxy/store";
3
+ import {
4
+ clearPersistedLogStorage,
5
+ clearPersistedLogsByIds,
6
+ getLogStorageStats,
7
+ getReplayLogsForSource,
8
+ listLogsPage,
9
+ } from "../../proxy/store";
4
10
 
5
11
  const DeleteBodySchema = z
6
12
  .object({
@@ -8,25 +14,43 @@ const DeleteBodySchema = z
8
14
  })
9
15
  .optional();
10
16
 
17
+ function parseNonNegativeInt(value: string | null, fallback: number): number {
18
+ if (value === null) return fallback;
19
+ const parsed = Number(value);
20
+ if (!Number.isInteger(parsed) || parsed < 0) return fallback;
21
+ return parsed;
22
+ }
23
+
24
+ function parsePositiveInt(value: string | null, fallback: number): number {
25
+ if (value === null) return fallback;
26
+ const parsed = Number(value);
27
+ if (!Number.isInteger(parsed) || parsed < 1) return fallback;
28
+ return parsed;
29
+ }
30
+
11
31
  export const Route = createFileRoute("/api/logs")({
12
32
  server: {
13
33
  handlers: {
14
- GET: ({ request }: { request: Request }) => {
34
+ GET: async ({ request }: { request: Request }) => {
15
35
  const url = new URL(request.url);
36
+ if (url.searchParams.get("stats") === "1") {
37
+ return Response.json(getLogStorageStats());
38
+ }
39
+ const replayOf = url.searchParams.get("replayOf");
40
+ if (replayOf !== null) {
41
+ const id = Number(replayOf);
42
+ if (!Number.isInteger(id) || id <= 0) {
43
+ return Response.json({ error: "Invalid replayOf log ID" }, { status: 400 });
44
+ }
45
+ const logs = getReplayLogsForSource(id);
46
+ return Response.json({ logs, total: logs.length, offset: 0, limit: logs.length });
47
+ }
16
48
  const sessionId = url.searchParams.get("sessionId") ?? undefined;
17
49
  const model = url.searchParams.get("model") ?? undefined;
18
- const offset = Number(url.searchParams.get("offset") ?? 0);
19
- const limit = Number(url.searchParams.get("limit") ?? 50);
50
+ const offset = parseNonNegativeInt(url.searchParams.get("offset"), 0);
51
+ const limit = parsePositiveInt(url.searchParams.get("limit"), 50);
20
52
 
21
- const allLogs = getFilteredLogs(sessionId, model);
22
- const paginatedLogs = allLogs.slice(offset, offset + limit);
23
-
24
- return Response.json({
25
- logs: paginatedLogs,
26
- total: allLogs.length,
27
- offset,
28
- limit,
29
- });
53
+ return Response.json(await listLogsPage({ sessionId, model, offset, limit }));
30
54
  },
31
55
  DELETE: async ({ request }: { request: Request }) => {
32
56
  let body: z.infer<typeof DeleteBodySchema> = undefined;
@@ -48,11 +72,21 @@ export const Route = createFileRoute("/api/logs")({
48
72
  }
49
73
 
50
74
  if (body?.ids !== undefined && body.ids.length > 0) {
51
- const result = clearLogsByIds(body.ids);
52
- return Response.json({ success: true, cleared: result.cleared });
75
+ const result = await clearPersistedLogsByIds(body.ids);
76
+ return Response.json({
77
+ success: true,
78
+ cleared: result.cleared,
79
+ logFilesRewritten: result.logFilesRewritten,
80
+ chunkFilesDeleted: result.chunkFilesDeleted,
81
+ });
53
82
  }
54
- const result = clearAllLogs();
55
- return Response.json({ success: true, cleared: result.cleared });
83
+ const result = await clearPersistedLogStorage();
84
+ return Response.json({
85
+ success: true,
86
+ cleared: result.cleared,
87
+ logFilesDeleted: result.logFilesDeleted,
88
+ chunkFilesDeleted: result.chunkFilesDeleted,
89
+ });
56
90
  },
57
91
  },
58
92
  },
@@ -1,6 +1,11 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { z } from "zod";
3
- import { getProviders, updateProvider, deleteProvider } from "../../proxy/providers";
3
+ import {
4
+ getProviders,
5
+ updateProvider,
6
+ deleteProvider,
7
+ redactProvider,
8
+ } from "../../proxy/providers";
4
9
  import { ProviderModelMetadataSchema } from "../../lib/providerContract";
5
10
 
6
11
  const ProviderUpdateSchema = z.object({
@@ -22,13 +27,15 @@ const ProviderUpdateSchema = z.object({
22
27
  export const Route = createFileRoute("/api/providers/$providerId")({
23
28
  server: {
24
29
  handlers: {
25
- GET: ({ params }: { params: { providerId: string } }) => {
30
+ GET: ({ params, request }: { params: { providerId: string }; request: Request }) => {
31
+ const url = new URL(request.url);
32
+ const includeSecrets = url.searchParams.get("includeSecrets") === "1";
26
33
  const providers = getProviders();
27
34
  const provider = providers.find((p) => p.id === params.providerId);
28
35
  if (!provider) {
29
36
  return Response.json({ error: "Provider not found" }, { status: 404 });
30
37
  }
31
- return Response.json(provider);
38
+ return Response.json(includeSecrets ? provider : redactProvider(provider));
32
39
  },
33
40
  PUT: async ({ params, request }: { params: { providerId: string }; request: Request }) => {
34
41
  const parsed = ProviderUpdateSchema.safeParse(await request.json());
@@ -1,6 +1,6 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { z } from "zod";
3
- import { getProviders, addProvider } from "../../proxy/providers";
3
+ import { getProviders, addProvider, redactProviders } from "../../proxy/providers";
4
4
  import { ProviderModelMetadataSchema } from "../../lib/providerContract";
5
5
 
6
6
  const ProviderInputSchema = z.object({
@@ -21,8 +21,11 @@ const ProviderInputSchema = z.object({
21
21
  export const Route = createFileRoute("/api/providers")({
22
22
  server: {
23
23
  handlers: {
24
- GET: () => {
25
- return Response.json(getProviders());
24
+ GET: ({ request }: { request: Request }) => {
25
+ const url = new URL(request.url);
26
+ const includeSecrets = url.searchParams.get("includeSecrets") === "1";
27
+ const providers = getProviders();
28
+ return Response.json(includeSecrets ? providers : redactProviders(providers));
26
29
  },
27
30
  POST: async ({ request }: { request: Request }) => {
28
31
  const parsed = ProviderInputSchema.safeParse(await request.json());