@tonyclaw/agent-inspector 2.0.33 → 2.0.35
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 +19 -1
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-ClTJxdoU.js → CompareDrawer-Cin0_f71.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer--Pes01q2.js → ProxyViewerContainer-BVlcRirf.js} +9 -9
- package/.output/public/assets/{ReplayDialog-iuzfh85b.js → ReplayDialog-C6nOMhjx.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-FZnnP9Gx.js → RequestAnatomy-DenWbdqV.js} +1 -1
- package/.output/public/assets/{ResponseView-MZ6ZWvbL.js → ResponseView-BsalZiZ5.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-B8mZMdzs.js → StreamingChunkSequence-Cla2X13P.js} +1 -1
- package/.output/public/assets/_sessionId-DkhHqlzB.js +1 -0
- package/.output/public/assets/index-AiQY20d7.js +1 -0
- package/.output/public/assets/index-BfGJEb-2.css +1 -0
- package/.output/public/assets/{main-B797Hnge.js → main-eD8hEOHT.js} +2 -2
- package/.output/server/_libs/ajv.mjs +827 -6191
- package/.output/server/_libs/cookie-es.mjs +7 -21
- package/.output/server/_libs/h3-v2.mjs +18 -7
- package/.output/server/_libs/json-schema-traverse.mjs +1 -90
- package/.output/server/_libs/seroval-plugins.mjs +5 -5
- package/.output/server/_libs/seroval.mjs +606 -596
- package/.output/server/_libs/srvx.mjs +10 -5
- package/.output/server/_libs/tanstack__history.mjs +55 -38
- package/.output/server/_libs/tanstack__router-core.mjs +4366 -4075
- package/.output/server/{_sessionId-DD05qd8d.mjs → _sessionId-URSu2XsA.mjs} +4 -4
- package/.output/server/_ssr/{CompareDrawer-oGOG5-kQ.mjs → CompareDrawer-BbBPKdVc.mjs} +4 -4
- package/.output/server/_ssr/{ProxyViewerContainer-Bbl5tqT1.mjs → ProxyViewerContainer-DtQxtOpi.mjs} +15 -7
- package/.output/server/_ssr/{ReplayDialog-BegfwWIy.mjs → ReplayDialog-BU20cMGd.mjs} +5 -5
- package/.output/server/_ssr/{RequestAnatomy-BW_X2DyZ.mjs → RequestAnatomy-a5ySAAG1.mjs} +4 -4
- package/.output/server/_ssr/{ResponseView-Bx_uJ4RS.mjs → ResponseView-CA_QM1fM.mjs} +4 -4
- package/.output/server/_ssr/{StreamingChunkSequence-CQp5QQnB.mjs → StreamingChunkSequence-DJjITqM7.mjs} +4 -4
- package/.output/server/_ssr/{index-ODSKeDD8.mjs → index-DIFzLUAV.mjs} +4 -4
- package/.output/server/_ssr/index.mjs +20 -11
- package/.output/server/_ssr/{router-QcI4GOvo.mjs → router-aVeuM_1d.mjs} +5193 -5269
- package/.output/server/{_tanstack-start-manifest_v-dEujKJ3-.mjs → _tanstack-start-manifest_v-BNlafGbl.mjs} +1 -1
- package/.output/server/index.mjs +65 -65
- package/README.md +28 -0
- package/package.json +6 -2
- package/src/cli/templates/codex-skill-onboard.ts +19 -1
- package/src/components/providers/SettingsDialog.tsx +10 -0
- package/src/lib/groupContract.ts +0 -2
- package/src/lib/runContract.ts +0 -3
- package/src/mcp/server.ts +101 -164
- package/src/mcp/toolHandlers.ts +80 -177
- package/src/routes/api/groups.$groupId.evidence.ts +14 -33
- package/src/routes/api/groups.$groupId.sessions.ts +9 -9
- package/src/routes/api/groups.$groupId.ts +19 -13
- package/src/routes/api/groups.ts +3 -3
- package/src/routes/api/runs.$runId.evidence.ts +14 -21
- package/src/routes/api/runs.$runId.ts +9 -9
- package/src/routes/api/runs.ts +9 -5
- package/src/services/groups.ts +110 -0
- package/src/services/result.ts +17 -0
- package/src/services/runs.ts +76 -0
- package/.output/public/assets/_sessionId-Bc26Rfxs.js +0 -1
- package/.output/public/assets/index-C6AiRPsJ.js +0 -1
- package/.output/public/assets/index-C_8PFAm2.css +0 -1
package/src/mcp/toolHandlers.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Tool handler implementations for the MCP server. These are split out from
|
|
3
|
+
* `server.ts` so the wire-level MCP registration stays thin.
|
|
4
|
+
*
|
|
5
|
+
* Log/provider/knowledge handlers still use an injected `callApi` while those
|
|
6
|
+
* domains are being migrated. Run/group/evidence handlers call the shared
|
|
7
|
+
* service layer directly so MCP and REST API are peers instead of MCP looping
|
|
8
|
+
* back through `/api/*`.
|
|
7
9
|
*
|
|
8
10
|
* Each impl returns a `CallToolResult`-shaped object: either
|
|
9
11
|
* `{ content: [{ type: "text", text: string }] }` on success or
|
|
@@ -15,27 +17,34 @@
|
|
|
15
17
|
|
|
16
18
|
import { Buffer } from "node:buffer";
|
|
17
19
|
import { z } from "zod";
|
|
20
|
+
import { getCurrentPort } from "../lib/serverPort";
|
|
18
21
|
import { LoopbackTimeoutError, type CallApiOptions } from "./loopback";
|
|
19
22
|
import { extractLastUserMessagePreview, extractResponsePreview } from "./previewExtractor";
|
|
20
23
|
import {
|
|
21
|
-
DeleteInspectorGroupResponseSchema,
|
|
22
|
-
GroupEvidenceExportResultSchema,
|
|
23
|
-
InspectorGroupSchema,
|
|
24
|
-
InspectorGroupsListResponseSchema,
|
|
25
24
|
type AddInspectorGroupSessionInput,
|
|
26
25
|
type CreateInspectorGroupInput,
|
|
27
26
|
type DeleteInspectorGroupOptions,
|
|
28
27
|
type UpdateInspectorGroupInput,
|
|
29
28
|
} from "../lib/groupContract";
|
|
30
|
-
import {
|
|
31
|
-
EvidenceExportResultSchema,
|
|
32
|
-
InspectorRunSchema,
|
|
33
|
-
RecentFailuresResponseSchema,
|
|
34
|
-
type CreateInspectorRunInput,
|
|
35
|
-
type UpdateInspectorRunInput,
|
|
36
|
-
} from "../lib/runContract";
|
|
29
|
+
import { type CreateInspectorRunInput, type UpdateInspectorRunInput } from "../lib/runContract";
|
|
37
30
|
import { SessionInfoSchema } from "../lib/sessionInfoContract";
|
|
38
31
|
import { CapturedLogSchema, type CapturedLog } from "../proxy/schemas";
|
|
32
|
+
import {
|
|
33
|
+
addInspectorGroupSession,
|
|
34
|
+
createInspectorGroup,
|
|
35
|
+
deleteInspectorGroup,
|
|
36
|
+
exportInspectorGroupEvidence,
|
|
37
|
+
getInspectorGroup,
|
|
38
|
+
listInspectorGroups,
|
|
39
|
+
updateInspectorGroup,
|
|
40
|
+
} from "../services/groups";
|
|
41
|
+
import {
|
|
42
|
+
createInspectorRun,
|
|
43
|
+
exportInspectorRunEvidence,
|
|
44
|
+
getInspectorRun,
|
|
45
|
+
listInspectorRecentFailures,
|
|
46
|
+
updateInspectorRun,
|
|
47
|
+
} from "../services/runs";
|
|
39
48
|
|
|
40
49
|
/** Minimal callApi contract: same shape as the real `callApi` in loopback.ts. */
|
|
41
50
|
export type CallApiFn = (path: string, options?: CallApiOptions) => Promise<Response>;
|
|
@@ -81,6 +90,10 @@ function toolError(message: string): ToolResult {
|
|
|
81
90
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
82
91
|
}
|
|
83
92
|
|
|
93
|
+
function inspectorBaseUrl(): string {
|
|
94
|
+
return `http://127.0.0.1:${String(getCurrentPort())}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
84
97
|
function textByteLength(value: string | null): number | null {
|
|
85
98
|
if (value === null) return null;
|
|
86
99
|
return Buffer.byteLength(value, "utf8");
|
|
@@ -288,73 +301,38 @@ export async function getSessionImpl(
|
|
|
288
301
|
return textJson(parsed.data);
|
|
289
302
|
}
|
|
290
303
|
|
|
291
|
-
export
|
|
292
|
-
|
|
293
|
-
args: CreateInspectorRunInput,
|
|
294
|
-
): Promise<ToolResult> {
|
|
295
|
-
const res = await callApi("/api/runs", {
|
|
296
|
-
method: "POST",
|
|
297
|
-
headers: { "content-type": "application/json" },
|
|
298
|
-
body: JSON.stringify(args ?? {}),
|
|
299
|
-
});
|
|
300
|
-
if (!res.ok) return toolError(`POST /api/runs returned ${res.status}`);
|
|
301
|
-
const rawBody: unknown = await res.json();
|
|
302
|
-
const parsed = InspectorRunSchema.safeParse(rawBody);
|
|
303
|
-
if (!parsed.success) return toolError("POST /api/runs returned an unparseable run");
|
|
304
|
-
return textJson(parsed.data);
|
|
304
|
+
export function createRunImpl(args: CreateInspectorRunInput): ToolResult {
|
|
305
|
+
return textJson(createInspectorRun(args));
|
|
305
306
|
}
|
|
306
307
|
|
|
307
308
|
export type GetRunArgs = {
|
|
308
309
|
runId: string;
|
|
309
310
|
};
|
|
310
311
|
|
|
311
|
-
export
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const rawBody: unknown = await res.json();
|
|
316
|
-
const parsed = InspectorRunSchema.safeParse(rawBody);
|
|
317
|
-
if (!parsed.success) return toolError("GET /api/runs/{runId} returned an unparseable run");
|
|
318
|
-
return textJson(parsed.data);
|
|
312
|
+
export function getRunImpl(args: GetRunArgs): ToolResult {
|
|
313
|
+
const run = getInspectorRun(args.runId);
|
|
314
|
+
if (!run.ok) return toolError(run.message);
|
|
315
|
+
return textJson(run.value);
|
|
319
316
|
}
|
|
320
317
|
|
|
321
318
|
export type UpdateRunArgs = {
|
|
322
319
|
runId: string;
|
|
323
320
|
} & UpdateInspectorRunInput;
|
|
324
321
|
|
|
325
|
-
export
|
|
322
|
+
export function updateRunImpl(args: UpdateRunArgs): ToolResult {
|
|
326
323
|
const { runId, ...patch } = args;
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
headers: { "content-type": "application/json" },
|
|
331
|
-
body: JSON.stringify(patch),
|
|
332
|
-
});
|
|
333
|
-
if (!res.ok) return toolError(`PATCH ${path} returned ${res.status}`);
|
|
334
|
-
const rawBody: unknown = await res.json();
|
|
335
|
-
const parsed = InspectorRunSchema.safeParse(rawBody);
|
|
336
|
-
if (!parsed.success) return toolError("PATCH /api/runs/{runId} returned an unparseable run");
|
|
337
|
-
return textJson(parsed.data);
|
|
324
|
+
const run = updateInspectorRun(runId, patch);
|
|
325
|
+
if (!run.ok) return toolError(run.message);
|
|
326
|
+
return textJson(run.value);
|
|
338
327
|
}
|
|
339
328
|
|
|
340
329
|
export type GetRecentFailuresArgs = {
|
|
341
330
|
limit?: number;
|
|
342
331
|
};
|
|
343
332
|
|
|
344
|
-
export
|
|
345
|
-
callApi: CallApiFn,
|
|
346
|
-
args: GetRecentFailuresArgs,
|
|
347
|
-
): Promise<ToolResult> {
|
|
333
|
+
export function getRecentFailuresImpl(args: GetRecentFailuresArgs): ToolResult {
|
|
348
334
|
const limit = args.limit ?? 5;
|
|
349
|
-
|
|
350
|
-
const res = await callApi(`/api/runs?${params.toString()}`);
|
|
351
|
-
if (!res.ok) return toolError(`GET /api/runs?failures=1 returned ${res.status}`);
|
|
352
|
-
const rawBody: unknown = await res.json();
|
|
353
|
-
const parsed = RecentFailuresResponseSchema.safeParse(rawBody);
|
|
354
|
-
if (!parsed.success) {
|
|
355
|
-
return toolError("GET /api/runs?failures=1 returned an unparseable failure list");
|
|
356
|
-
}
|
|
357
|
-
return textJson(parsed.data);
|
|
335
|
+
return textJson(listInspectorRecentFailures(limit));
|
|
358
336
|
}
|
|
359
337
|
|
|
360
338
|
export type ExportEvidenceArgs = {
|
|
@@ -363,133 +341,67 @@ export type ExportEvidenceArgs = {
|
|
|
363
341
|
latestLogLimit?: number;
|
|
364
342
|
};
|
|
365
343
|
|
|
366
|
-
export async function exportEvidenceImpl(
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const path = `/api/runs/${encodeURIComponent(args.runId)}/evidence`;
|
|
371
|
-
const res = await callApi(path, {
|
|
372
|
-
method: "POST",
|
|
373
|
-
headers: { "content-type": "application/json" },
|
|
374
|
-
body: JSON.stringify({
|
|
344
|
+
export async function exportEvidenceImpl(args: ExportEvidenceArgs): Promise<ToolResult> {
|
|
345
|
+
const exported = await exportInspectorRunEvidence(
|
|
346
|
+
args.runId,
|
|
347
|
+
{
|
|
375
348
|
includeHistory: args.includeHistory,
|
|
376
349
|
latestLogLimit: args.latestLogLimit,
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (!parsed.success) {
|
|
383
|
-
return toolError("POST /api/runs/{runId}/evidence returned an unparseable evidence result");
|
|
384
|
-
}
|
|
385
|
-
return textJson(parsed.data);
|
|
350
|
+
},
|
|
351
|
+
inspectorBaseUrl(),
|
|
352
|
+
);
|
|
353
|
+
if (!exported.ok) return toolError(exported.message);
|
|
354
|
+
return textJson(exported.value);
|
|
386
355
|
}
|
|
387
356
|
|
|
388
|
-
export
|
|
389
|
-
|
|
390
|
-
if (!res.ok) return toolError(`GET /api/groups returned ${res.status}`);
|
|
391
|
-
const rawBody: unknown = await res.json();
|
|
392
|
-
const parsed = InspectorGroupsListResponseSchema.safeParse(rawBody);
|
|
393
|
-
if (!parsed.success) return toolError("GET /api/groups returned an unparseable group list");
|
|
394
|
-
return textJson(parsed.data);
|
|
357
|
+
export function listGroupsImpl(): ToolResult {
|
|
358
|
+
return textJson(listInspectorGroups());
|
|
395
359
|
}
|
|
396
360
|
|
|
397
|
-
export
|
|
398
|
-
|
|
399
|
-
args: CreateInspectorGroupInput,
|
|
400
|
-
): Promise<ToolResult> {
|
|
401
|
-
const res = await callApi("/api/groups", {
|
|
402
|
-
method: "POST",
|
|
403
|
-
headers: { "content-type": "application/json" },
|
|
404
|
-
body: JSON.stringify(args ?? {}),
|
|
405
|
-
});
|
|
406
|
-
if (!res.ok) return toolError(`POST /api/groups returned ${res.status}`);
|
|
407
|
-
const rawBody: unknown = await res.json();
|
|
408
|
-
const parsed = InspectorGroupSchema.safeParse(rawBody);
|
|
409
|
-
if (!parsed.success) return toolError("POST /api/groups returned an unparseable group");
|
|
410
|
-
return textJson(parsed.data);
|
|
361
|
+
export function createGroupImpl(args: CreateInspectorGroupInput): ToolResult {
|
|
362
|
+
return textJson(createInspectorGroup(args));
|
|
411
363
|
}
|
|
412
364
|
|
|
413
365
|
export type GetGroupArgs = {
|
|
414
366
|
groupId: string;
|
|
415
367
|
};
|
|
416
368
|
|
|
417
|
-
export
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const rawBody: unknown = await res.json();
|
|
422
|
-
const parsed = InspectorGroupSchema.safeParse(rawBody);
|
|
423
|
-
if (!parsed.success) return toolError("GET /api/groups/{groupId} returned an unparseable group");
|
|
424
|
-
return textJson(parsed.data);
|
|
369
|
+
export function getGroupImpl(args: GetGroupArgs): ToolResult {
|
|
370
|
+
const group = getInspectorGroup(args.groupId);
|
|
371
|
+
if (!group.ok) return toolError(group.message);
|
|
372
|
+
return textJson(group.value);
|
|
425
373
|
}
|
|
426
374
|
|
|
427
375
|
export type UpdateGroupArgs = {
|
|
428
376
|
groupId: string;
|
|
429
377
|
} & UpdateInspectorGroupInput;
|
|
430
378
|
|
|
431
|
-
export
|
|
432
|
-
callApi: CallApiFn,
|
|
433
|
-
args: UpdateGroupArgs,
|
|
434
|
-
): Promise<ToolResult> {
|
|
379
|
+
export function updateGroupImpl(args: UpdateGroupArgs): ToolResult {
|
|
435
380
|
const { groupId, ...patch } = args;
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
headers: { "content-type": "application/json" },
|
|
440
|
-
body: JSON.stringify(patch),
|
|
441
|
-
});
|
|
442
|
-
if (!res.ok) return toolError(`PATCH ${path} returned ${res.status}`);
|
|
443
|
-
const rawBody: unknown = await res.json();
|
|
444
|
-
const parsed = InspectorGroupSchema.safeParse(rawBody);
|
|
445
|
-
if (!parsed.success) {
|
|
446
|
-
return toolError("PATCH /api/groups/{groupId} returned an unparseable group");
|
|
447
|
-
}
|
|
448
|
-
return textJson(parsed.data);
|
|
381
|
+
const group = updateInspectorGroup(groupId, patch);
|
|
382
|
+
if (!group.ok) return toolError(group.message);
|
|
383
|
+
return textJson(group.value);
|
|
449
384
|
}
|
|
450
385
|
|
|
451
386
|
export type DeleteGroupArgs = {
|
|
452
387
|
groupId: string;
|
|
453
388
|
} & DeleteInspectorGroupOptions;
|
|
454
389
|
|
|
455
|
-
export
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
)
|
|
459
|
-
const query = args.deleteEvidence === true ? "?deleteEvidence=1" : "";
|
|
460
|
-
const path = `/api/groups/${encodeURIComponent(args.groupId)}${query}`;
|
|
461
|
-
const res = await callApi(path, { method: "DELETE" });
|
|
462
|
-
if (!res.ok) return toolError(`DELETE ${path} returned ${res.status}`);
|
|
463
|
-
const rawBody: unknown = await res.json();
|
|
464
|
-
const parsed = DeleteInspectorGroupResponseSchema.safeParse(rawBody);
|
|
465
|
-
if (!parsed.success) {
|
|
466
|
-
return toolError("DELETE /api/groups/{groupId} returned an unparseable delete result");
|
|
467
|
-
}
|
|
468
|
-
return textJson(parsed.data);
|
|
390
|
+
export function deleteGroupImpl(args: DeleteGroupArgs): ToolResult {
|
|
391
|
+
const deleted = deleteInspectorGroup(args.groupId, { deleteEvidence: args.deleteEvidence });
|
|
392
|
+
if (!deleted.ok) return toolError(deleted.message);
|
|
393
|
+
return textJson(deleted.value);
|
|
469
394
|
}
|
|
470
395
|
|
|
471
396
|
export type AddGroupSessionArgs = {
|
|
472
397
|
groupId: string;
|
|
473
398
|
} & AddInspectorGroupSessionInput;
|
|
474
399
|
|
|
475
|
-
export
|
|
476
|
-
callApi: CallApiFn,
|
|
477
|
-
args: AddGroupSessionArgs,
|
|
478
|
-
): Promise<ToolResult> {
|
|
400
|
+
export function addGroupSessionImpl(args: AddGroupSessionArgs): ToolResult {
|
|
479
401
|
const { groupId, ...session } = args;
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
headers: { "content-type": "application/json" },
|
|
484
|
-
body: JSON.stringify(session),
|
|
485
|
-
});
|
|
486
|
-
if (!res.ok) return toolError(`POST ${path} returned ${res.status}`);
|
|
487
|
-
const rawBody: unknown = await res.json();
|
|
488
|
-
const parsed = InspectorGroupSchema.safeParse(rawBody);
|
|
489
|
-
if (!parsed.success) {
|
|
490
|
-
return toolError("POST /api/groups/{groupId}/sessions returned an unparseable group");
|
|
491
|
-
}
|
|
492
|
-
return textJson(parsed.data);
|
|
402
|
+
const group = addInspectorGroupSession(groupId, session);
|
|
403
|
+
if (!group.ok) return toolError(group.message);
|
|
404
|
+
return textJson(group.value);
|
|
493
405
|
}
|
|
494
406
|
|
|
495
407
|
export type ExportGroupEvidenceArgs = {
|
|
@@ -498,26 +410,17 @@ export type ExportGroupEvidenceArgs = {
|
|
|
498
410
|
latestLogLimit?: number;
|
|
499
411
|
};
|
|
500
412
|
|
|
501
|
-
export async function exportGroupEvidenceImpl(
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const path = `/api/groups/${encodeURIComponent(args.groupId)}/evidence`;
|
|
506
|
-
const res = await callApi(path, {
|
|
507
|
-
method: "POST",
|
|
508
|
-
headers: { "content-type": "application/json" },
|
|
509
|
-
body: JSON.stringify({
|
|
413
|
+
export async function exportGroupEvidenceImpl(args: ExportGroupEvidenceArgs): Promise<ToolResult> {
|
|
414
|
+
const exported = await exportInspectorGroupEvidence(
|
|
415
|
+
args.groupId,
|
|
416
|
+
{
|
|
510
417
|
includeHistory: args.includeHistory,
|
|
511
418
|
latestLogLimit: args.latestLogLimit,
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (!parsed.success) {
|
|
518
|
-
return toolError("POST /api/groups/{groupId}/evidence returned an unparseable evidence result");
|
|
519
|
-
}
|
|
520
|
-
return textJson(parsed.data);
|
|
419
|
+
},
|
|
420
|
+
inspectorBaseUrl(),
|
|
421
|
+
);
|
|
422
|
+
if (!exported.ok) return toolError(exported.message);
|
|
423
|
+
return textJson(exported.value);
|
|
521
424
|
}
|
|
522
425
|
|
|
523
426
|
export async function listModelsImpl(callApi: CallApiFn): Promise<ToolResult> {
|
|
@@ -724,7 +627,7 @@ export async function getProjectContextImpl(
|
|
|
724
627
|
* CallToolResult (instead of a JSON-RPC protocol error, which would abort
|
|
725
628
|
* the whole session). Loopback timeouts get a clear message.
|
|
726
629
|
*/
|
|
727
|
-
export async function safeCall(fn: () => Promise<ToolResult>): Promise<ToolResult> {
|
|
630
|
+
export async function safeCall(fn: () => ToolResult | Promise<ToolResult>): Promise<ToolResult> {
|
|
728
631
|
try {
|
|
729
632
|
return await fn();
|
|
730
633
|
} catch (err) {
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { GroupEvidenceExportOptionsSchema } from "../../lib/groupContract";
|
|
3
|
-
import {
|
|
4
|
-
exportGroupEvidence,
|
|
5
|
-
readGroupEvidenceDocument,
|
|
6
|
-
readGroupEvidenceMarkdown,
|
|
7
|
-
} from "../../proxy/groupEvidenceExporter";
|
|
8
|
-
import { getGroup } from "../../proxy/groupStore";
|
|
3
|
+
import { exportInspectorGroupEvidence, readInspectorGroupEvidence } from "../../services/groups";
|
|
9
4
|
|
|
10
5
|
type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
|
|
11
6
|
|
|
@@ -23,35 +18,14 @@ export const Route = createFileRoute("/api/groups/$groupId/evidence")({
|
|
|
23
18
|
server: {
|
|
24
19
|
handlers: {
|
|
25
20
|
GET: ({ params }: { params: { groupId: string } }) => {
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
return Response.json({ error:
|
|
21
|
+
const evidence = readInspectorGroupEvidence(params.groupId);
|
|
22
|
+
if (!evidence.ok) {
|
|
23
|
+
return Response.json({ error: evidence.message }, { status: evidence.status });
|
|
29
24
|
}
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
if (group.evidence === null || markdown === null) {
|
|
33
|
-
return Response.json(
|
|
34
|
-
{ error: "Evidence has not been exported for this group" },
|
|
35
|
-
{ status: 404 },
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const document = readGroupEvidenceDocument(group);
|
|
40
|
-
|
|
41
|
-
return Response.json({
|
|
42
|
-
group,
|
|
43
|
-
evidence: group.evidence,
|
|
44
|
-
markdown,
|
|
45
|
-
summary: document?.summary ?? null,
|
|
46
|
-
report: document?.jenkinsReport ?? null,
|
|
47
|
-
});
|
|
26
|
+
return Response.json(evidence.value);
|
|
48
27
|
},
|
|
49
28
|
POST: async ({ params, request }: { params: { groupId: string }; request: Request }) => {
|
|
50
|
-
const group = getGroup(params.groupId);
|
|
51
|
-
if (group === null) {
|
|
52
|
-
return Response.json({ error: "Group not found" }, { status: 404 });
|
|
53
|
-
}
|
|
54
|
-
|
|
55
29
|
const body = await readJsonBody(request);
|
|
56
30
|
if (!body.ok) {
|
|
57
31
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
@@ -65,9 +39,16 @@ export const Route = createFileRoute("/api/groups/$groupId/evidence")({
|
|
|
65
39
|
);
|
|
66
40
|
}
|
|
67
41
|
|
|
68
|
-
|
|
69
|
-
|
|
42
|
+
const exported = await exportInspectorGroupEvidence(
|
|
43
|
+
params.groupId,
|
|
44
|
+
parsed.data,
|
|
45
|
+
new URL(request.url).origin,
|
|
70
46
|
);
|
|
47
|
+
if (!exported.ok) {
|
|
48
|
+
return Response.json({ error: exported.message }, { status: exported.status });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return Response.json(exported.value);
|
|
71
52
|
},
|
|
72
53
|
},
|
|
73
54
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { AddInspectorGroupSessionInputSchema } from "../../lib/groupContract";
|
|
3
|
-
import {
|
|
3
|
+
import { addInspectorGroupSession, readInspectorGroupSessions } from "../../services/groups";
|
|
4
4
|
|
|
5
5
|
type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
|
|
6
6
|
|
|
@@ -18,11 +18,11 @@ export const Route = createFileRoute("/api/groups/$groupId/sessions")({
|
|
|
18
18
|
server: {
|
|
19
19
|
handlers: {
|
|
20
20
|
GET: ({ params }: { params: { groupId: string } }) => {
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
return Response.json({ error:
|
|
21
|
+
const sessions = readInspectorGroupSessions(params.groupId);
|
|
22
|
+
if (!sessions.ok) {
|
|
23
|
+
return Response.json({ error: sessions.message }, { status: sessions.status });
|
|
24
24
|
}
|
|
25
|
-
return Response.json(
|
|
25
|
+
return Response.json(sessions.value);
|
|
26
26
|
},
|
|
27
27
|
POST: async ({ params, request }: { params: { groupId: string }; request: Request }) => {
|
|
28
28
|
const body = await readJsonBody(request);
|
|
@@ -38,12 +38,12 @@ export const Route = createFileRoute("/api/groups/$groupId/sessions")({
|
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const updated =
|
|
42
|
-
if (updated
|
|
43
|
-
return Response.json({ error:
|
|
41
|
+
const updated = addInspectorGroupSession(params.groupId, parsed.data);
|
|
42
|
+
if (!updated.ok) {
|
|
43
|
+
return Response.json({ error: updated.message }, { status: updated.status });
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
return Response.json(updated);
|
|
46
|
+
return Response.json(updated.value);
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
49
|
},
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { UpdateInspectorGroupInputSchema } from "../../lib/groupContract";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
deleteInspectorGroup,
|
|
5
|
+
getInspectorGroup,
|
|
6
|
+
updateInspectorGroup,
|
|
7
|
+
} from "../../services/groups";
|
|
4
8
|
|
|
5
9
|
type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
|
|
6
10
|
type DeleteEvidenceFlagResult = { ok: true; value: boolean | undefined } | { ok: false };
|
|
@@ -39,11 +43,11 @@ export const Route = createFileRoute("/api/groups/$groupId")({
|
|
|
39
43
|
server: {
|
|
40
44
|
handlers: {
|
|
41
45
|
GET: ({ params }: { params: { groupId: string } }) => {
|
|
42
|
-
const group =
|
|
43
|
-
if (group
|
|
44
|
-
return Response.json({ error:
|
|
46
|
+
const group = getInspectorGroup(params.groupId);
|
|
47
|
+
if (!group.ok) {
|
|
48
|
+
return Response.json({ error: group.message }, { status: group.status });
|
|
45
49
|
}
|
|
46
|
-
return Response.json(group);
|
|
50
|
+
return Response.json(group.value);
|
|
47
51
|
},
|
|
48
52
|
PATCH: async ({ params, request }: { params: { groupId: string }; request: Request }) => {
|
|
49
53
|
const body = await readJsonBody(request);
|
|
@@ -59,12 +63,12 @@ export const Route = createFileRoute("/api/groups/$groupId")({
|
|
|
59
63
|
);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
const updated =
|
|
63
|
-
if (updated
|
|
64
|
-
return Response.json({ error:
|
|
66
|
+
const updated = updateInspectorGroup(params.groupId, parsed.data);
|
|
67
|
+
if (!updated.ok) {
|
|
68
|
+
return Response.json({ error: updated.message }, { status: updated.status });
|
|
65
69
|
}
|
|
66
70
|
|
|
67
|
-
return Response.json(updated);
|
|
71
|
+
return Response.json(updated.value);
|
|
68
72
|
},
|
|
69
73
|
DELETE: ({ params, request }: { params: { groupId: string }; request: Request }) => {
|
|
70
74
|
const deleteEvidence = readDeleteEvidenceFlag(request);
|
|
@@ -75,12 +79,14 @@ export const Route = createFileRoute("/api/groups/$groupId")({
|
|
|
75
79
|
);
|
|
76
80
|
}
|
|
77
81
|
|
|
78
|
-
const deleted =
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
const deleted = deleteInspectorGroup(params.groupId, {
|
|
83
|
+
deleteEvidence: deleteEvidence.value,
|
|
84
|
+
});
|
|
85
|
+
if (!deleted.ok) {
|
|
86
|
+
return Response.json({ error: deleted.message }, { status: deleted.status });
|
|
81
87
|
}
|
|
82
88
|
|
|
83
|
-
return Response.json(deleted);
|
|
89
|
+
return Response.json(deleted.value);
|
|
84
90
|
},
|
|
85
91
|
},
|
|
86
92
|
},
|
package/src/routes/api/groups.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { CreateInspectorGroupInputSchema } from "../../lib/groupContract";
|
|
3
|
-
import {
|
|
3
|
+
import { createInspectorGroup, listInspectorGroups } from "../../services/groups";
|
|
4
4
|
|
|
5
5
|
type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
|
|
6
6
|
|
|
@@ -17,7 +17,7 @@ async function readJsonBody(request: Request): Promise<JsonBodyResult> {
|
|
|
17
17
|
export const Route = createFileRoute("/api/groups")({
|
|
18
18
|
server: {
|
|
19
19
|
handlers: {
|
|
20
|
-
GET: () => Response.json(
|
|
20
|
+
GET: () => Response.json(listInspectorGroups()),
|
|
21
21
|
POST: async ({ request }: { request: Request }) => {
|
|
22
22
|
const body = await readJsonBody(request);
|
|
23
23
|
if (!body.ok) {
|
|
@@ -32,7 +32,7 @@ export const Route = createFileRoute("/api/groups")({
|
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
return Response.json(
|
|
35
|
+
return Response.json(createInspectorGroup(parsed.data), { status: 201 });
|
|
36
36
|
},
|
|
37
37
|
},
|
|
38
38
|
},
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { EvidenceExportOptionsSchema } from "../../lib/runContract";
|
|
3
|
-
import {
|
|
4
|
-
import { getRun } from "../../proxy/runStore";
|
|
3
|
+
import { exportInspectorRunEvidence, readInspectorRunEvidence } from "../../services/runs";
|
|
5
4
|
|
|
6
5
|
type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
|
|
7
6
|
|
|
@@ -19,27 +18,14 @@ export const Route = createFileRoute("/api/runs/$runId/evidence")({
|
|
|
19
18
|
server: {
|
|
20
19
|
handlers: {
|
|
21
20
|
GET: ({ params }: { params: { runId: string } }) => {
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
24
|
-
return Response.json({ error:
|
|
21
|
+
const evidence = readInspectorRunEvidence(params.runId);
|
|
22
|
+
if (!evidence.ok) {
|
|
23
|
+
return Response.json({ error: evidence.message }, { status: evidence.status });
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
if (run.evidence === null || markdown === null) {
|
|
29
|
-
return Response.json(
|
|
30
|
-
{ error: "Evidence has not been exported for this run" },
|
|
31
|
-
{ status: 404 },
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return Response.json({ run, evidence: run.evidence, markdown });
|
|
26
|
+
return Response.json(evidence.value);
|
|
36
27
|
},
|
|
37
28
|
POST: async ({ params, request }: { params: { runId: string }; request: Request }) => {
|
|
38
|
-
const run = getRun(params.runId);
|
|
39
|
-
if (run === null) {
|
|
40
|
-
return Response.json({ error: "Run not found" }, { status: 404 });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
29
|
const body = await readJsonBody(request);
|
|
44
30
|
if (!body.ok) {
|
|
45
31
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
@@ -53,9 +39,16 @@ export const Route = createFileRoute("/api/runs/$runId/evidence")({
|
|
|
53
39
|
);
|
|
54
40
|
}
|
|
55
41
|
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
const exported = await exportInspectorRunEvidence(
|
|
43
|
+
params.runId,
|
|
44
|
+
parsed.data,
|
|
45
|
+
new URL(request.url).origin,
|
|
58
46
|
);
|
|
47
|
+
if (!exported.ok) {
|
|
48
|
+
return Response.json({ error: exported.message }, { status: exported.status });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return Response.json(exported.value);
|
|
59
52
|
},
|
|
60
53
|
},
|
|
61
54
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { UpdateInspectorRunInputSchema } from "../../lib/runContract";
|
|
3
|
-
import {
|
|
3
|
+
import { getInspectorRun, updateInspectorRun } from "../../services/runs";
|
|
4
4
|
|
|
5
5
|
type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
|
|
6
6
|
|
|
@@ -18,11 +18,11 @@ export const Route = createFileRoute("/api/runs/$runId")({
|
|
|
18
18
|
server: {
|
|
19
19
|
handlers: {
|
|
20
20
|
GET: ({ params }: { params: { runId: string } }) => {
|
|
21
|
-
const run =
|
|
22
|
-
if (run
|
|
23
|
-
return Response.json({ error:
|
|
21
|
+
const run = getInspectorRun(params.runId);
|
|
22
|
+
if (!run.ok) {
|
|
23
|
+
return Response.json({ error: run.message }, { status: run.status });
|
|
24
24
|
}
|
|
25
|
-
return Response.json(run);
|
|
25
|
+
return Response.json(run.value);
|
|
26
26
|
},
|
|
27
27
|
PATCH: async ({ params, request }: { params: { runId: string }; request: Request }) => {
|
|
28
28
|
const body = await readJsonBody(request);
|
|
@@ -38,12 +38,12 @@ export const Route = createFileRoute("/api/runs/$runId")({
|
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const updated =
|
|
42
|
-
if (updated
|
|
43
|
-
return Response.json({ error:
|
|
41
|
+
const updated = updateInspectorRun(params.runId, parsed.data);
|
|
42
|
+
if (!updated.ok) {
|
|
43
|
+
return Response.json({ error: updated.message }, { status: updated.status });
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
return Response.json(updated);
|
|
46
|
+
return Response.json(updated.value);
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
49
|
},
|