@tonyclaw/agent-inspector 2.0.30 → 2.0.32
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/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-BDzfRSt1.js → CompareDrawer-Dgi0hhg5.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-4dG0nLRp.js +115 -0
- package/.output/public/assets/{ReplayDialog-p-5TKg75.js → ReplayDialog-BL6BwDuI.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-G0dV8-D_.js → RequestAnatomy-DZMmiIiD.js} +1 -1
- package/.output/public/assets/{ResponseView-D2wE6lD-.js → ResponseView-DJLOiTYq.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-D8JRK-j_.js → StreamingChunkSequence--SxZ5cxc.js} +1 -1
- package/.output/public/assets/_sessionId-C4z6gu9i.js +1 -0
- package/.output/public/assets/index-Drpb2qR5.js +1 -0
- package/.output/public/assets/index-fUUi8iQF.css +1 -0
- package/.output/public/assets/{main-XHHZsOBK.js → main-CbRIHhJ5.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +160 -135
- package/.output/server/{_sessionId-CK9QEhkB.mjs → _sessionId-DNfumR8F.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-BJVPFMIx.mjs → CompareDrawer-CzgjhnUa.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-DhbmJ2zj.mjs → ProxyViewerContainer-BqOcZ7en.mjs} +737 -17
- package/.output/server/_ssr/{ReplayDialog-DXRj95Gj.mjs → ReplayDialog-DmEknIL8.mjs} +4 -4
- package/.output/server/_ssr/{RequestAnatomy-CrYJTXdJ.mjs → RequestAnatomy-DNoiMudY.mjs} +3 -3
- package/.output/server/_ssr/{ResponseView-4ESqSITr.mjs → ResponseView-C_cRrleT.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-Czdko5n_.mjs → StreamingChunkSequence-DHZ9Q_dL.mjs} +3 -3
- package/.output/server/_ssr/{index-bei0I7qL.mjs → index-coYS9XyS.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-sQh4Gk8J.mjs → router-DUb5lKw1.mjs} +174 -40
- package/.output/server/{_tanstack-start-manifest_v-C0fdSWnA.mjs → _tanstack-start-manifest_v-xcG1o8Vt.mjs} +1 -1
- package/.output/server/index.mjs +60 -60
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +9 -2
- package/src/components/groups/GroupsDialog.tsx +945 -0
- package/src/lib/groupContract.ts +24 -2
- package/src/lib/runContract.ts +2 -0
- package/src/lib/useGroupEvidence.ts +33 -0
- package/src/lib/useGroups.ts +28 -0
- package/src/mcp/server.ts +20 -1
- package/src/mcp/toolHandlers.ts +22 -0
- package/src/proxy/groupEvidenceExporter.ts +13 -0
- package/src/proxy/groupStore.ts +52 -2
- package/src/routes/api/groups.$groupId.evidence.ts +14 -2
- package/src/routes/api/groups.$groupId.ts +38 -1
- package/.output/public/assets/ProxyViewerContainer-NtOD8Ex_.js +0 -115
- package/.output/public/assets/_sessionId-zimzVrJE.js +0 -1
- package/.output/public/assets/index-Cv5vPgpr.js +0 -1
- package/.output/public/assets/index-CwlHPmgL.css +0 -1
package/src/lib/groupContract.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { JsonValueSchema } from "../proxy/schemas";
|
|
3
|
-
import { InspectorRunSchema, InspectorRunStatusSchema
|
|
3
|
+
import { EvidenceReportSchema, InspectorRunSchema, InspectorRunStatusSchema } from "./runContract";
|
|
4
4
|
import { SessionInfoSchema, SessionTokenUsageSchema } from "./sessionInfoContract";
|
|
5
5
|
|
|
6
6
|
export const InspectorGroupStatusSchema = z.enum([
|
|
@@ -92,6 +92,17 @@ export const GroupEvidenceExportOptionsSchema = z
|
|
|
92
92
|
})
|
|
93
93
|
.optional();
|
|
94
94
|
|
|
95
|
+
export const DeleteInspectorGroupOptionsSchema = z.object({
|
|
96
|
+
deleteEvidence: z.boolean().optional(),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export const DeleteInspectorGroupResponseSchema = z.object({
|
|
100
|
+
groupId: z.string(),
|
|
101
|
+
deleted: z.boolean(),
|
|
102
|
+
evidenceDeleted: z.boolean(),
|
|
103
|
+
evidencePath: z.string().nullable(),
|
|
104
|
+
});
|
|
105
|
+
|
|
95
106
|
export const InspectorGroupExportMemberSchema = z.object({
|
|
96
107
|
member: InspectorGroupMemberSchema,
|
|
97
108
|
run: InspectorRunSchema.nullable(),
|
|
@@ -112,12 +123,20 @@ export const InspectorGroupEvidenceSummarySchema = z.object({
|
|
|
112
123
|
runningMembers: z.number().int().nonnegative(),
|
|
113
124
|
});
|
|
114
125
|
|
|
126
|
+
export const GroupEvidenceReadResponseSchema = z.object({
|
|
127
|
+
group: InspectorGroupSchema,
|
|
128
|
+
evidence: InspectorGroupEvidenceSchema,
|
|
129
|
+
markdown: z.string(),
|
|
130
|
+
summary: InspectorGroupEvidenceSummarySchema.nullable(),
|
|
131
|
+
report: EvidenceReportSchema.nullable(),
|
|
132
|
+
});
|
|
133
|
+
|
|
115
134
|
export const GroupEvidenceExportResultSchema = z.object({
|
|
116
135
|
group: InspectorGroupSchema,
|
|
117
136
|
evidence: InspectorGroupEvidenceSchema,
|
|
118
137
|
members: z.array(InspectorGroupExportMemberSchema),
|
|
119
138
|
summary: InspectorGroupEvidenceSummarySchema,
|
|
120
|
-
jenkinsReport:
|
|
139
|
+
jenkinsReport: EvidenceReportSchema,
|
|
121
140
|
});
|
|
122
141
|
|
|
123
142
|
export const InspectorGroupsListResponseSchema = z.object({
|
|
@@ -133,7 +152,10 @@ export type CreateInspectorGroupInput = z.infer<typeof CreateInspectorGroupInput
|
|
|
133
152
|
export type UpdateInspectorGroupInput = z.infer<typeof UpdateInspectorGroupInputSchema>;
|
|
134
153
|
export type AddInspectorGroupSessionInput = z.infer<typeof AddInspectorGroupSessionInputSchema>;
|
|
135
154
|
export type GroupEvidenceExportOptions = z.infer<typeof GroupEvidenceExportOptionsSchema>;
|
|
155
|
+
export type DeleteInspectorGroupOptions = z.infer<typeof DeleteInspectorGroupOptionsSchema>;
|
|
156
|
+
export type DeleteInspectorGroupResponse = z.infer<typeof DeleteInspectorGroupResponseSchema>;
|
|
136
157
|
export type InspectorGroupExportMember = z.infer<typeof InspectorGroupExportMemberSchema>;
|
|
137
158
|
export type InspectorGroupEvidenceSummary = z.infer<typeof InspectorGroupEvidenceSummarySchema>;
|
|
159
|
+
export type GroupEvidenceReadResponse = z.infer<typeof GroupEvidenceReadResponseSchema>;
|
|
138
160
|
export type GroupEvidenceExportResult = z.infer<typeof GroupEvidenceExportResultSchema>;
|
|
139
161
|
export type InspectorGroupsListResponse = z.infer<typeof InspectorGroupsListResponseSchema>;
|
package/src/lib/runContract.ts
CHANGED
|
@@ -71,6 +71,7 @@ export const JenkinsReportSchema = z.object({
|
|
|
71
71
|
summary: z.string(),
|
|
72
72
|
markdown: z.string(),
|
|
73
73
|
});
|
|
74
|
+
export const EvidenceReportSchema = JenkinsReportSchema;
|
|
74
75
|
|
|
75
76
|
export const InspectorRunSchema = z.object({
|
|
76
77
|
id: z.string(),
|
|
@@ -153,6 +154,7 @@ export type RunTimelineEvent = z.infer<typeof RunTimelineEventSchema>;
|
|
|
153
154
|
export type FailureCategory = z.infer<typeof FailureCategorySchema>;
|
|
154
155
|
export type FailureClassification = z.infer<typeof FailureClassificationSchema>;
|
|
155
156
|
export type JenkinsReport = z.infer<typeof JenkinsReportSchema>;
|
|
157
|
+
export type EvidenceReport = JenkinsReport;
|
|
156
158
|
export type InspectorRun = z.infer<typeof InspectorRunSchema>;
|
|
157
159
|
export type CreateInspectorRunInput = z.infer<typeof CreateInspectorRunInputSchema>;
|
|
158
160
|
export type UpdateInspectorRunInput = z.infer<typeof UpdateInspectorRunInputSchema>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import useSWR, { type SWRResponse } from "swr";
|
|
2
|
+
import {
|
|
3
|
+
GroupEvidenceReadResponseSchema,
|
|
4
|
+
type GroupEvidenceReadResponse,
|
|
5
|
+
type InspectorGroup,
|
|
6
|
+
} from "./groupContract";
|
|
7
|
+
import { fetchJson } from "./apiClient";
|
|
8
|
+
|
|
9
|
+
type UseGroupEvidenceResult = {
|
|
10
|
+
evidenceResponse: GroupEvidenceReadResponse | null;
|
|
11
|
+
isLoading: boolean;
|
|
12
|
+
error: Error | undefined;
|
|
13
|
+
mutate: SWRResponse<GroupEvidenceReadResponse, Error>["mutate"];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function evidenceKey(group: InspectorGroup | null): string | null {
|
|
17
|
+
if (group === null || group.evidence === null) return null;
|
|
18
|
+
return `/api/groups/${encodeURIComponent(group.id)}/evidence`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useGroupEvidence(group: InspectorGroup | null): UseGroupEvidenceResult {
|
|
22
|
+
const response: SWRResponse<GroupEvidenceReadResponse, Error> = useSWR<
|
|
23
|
+
GroupEvidenceReadResponse,
|
|
24
|
+
Error
|
|
25
|
+
>(evidenceKey(group), (url: string) => fetchJson(url, GroupEvidenceReadResponseSchema));
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
evidenceResponse: response.data ?? null,
|
|
29
|
+
isLoading: response.isLoading,
|
|
30
|
+
error: response.error,
|
|
31
|
+
mutate: response.mutate,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import useSWR, { type SWRResponse } from "swr";
|
|
2
|
+
import {
|
|
3
|
+
InspectorGroupsListResponseSchema,
|
|
4
|
+
type InspectorGroup,
|
|
5
|
+
type InspectorGroupsListResponse,
|
|
6
|
+
} from "./groupContract";
|
|
7
|
+
import { fetchJson } from "./apiClient";
|
|
8
|
+
|
|
9
|
+
type UseGroupsResult = {
|
|
10
|
+
groups: InspectorGroup[];
|
|
11
|
+
isLoading: boolean;
|
|
12
|
+
error: Error | undefined;
|
|
13
|
+
mutate: SWRResponse<InspectorGroupsListResponse, Error>["mutate"];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function useGroups(): UseGroupsResult {
|
|
17
|
+
const response: SWRResponse<InspectorGroupsListResponse, Error> = useSWR<
|
|
18
|
+
InspectorGroupsListResponse,
|
|
19
|
+
Error
|
|
20
|
+
>("/api/groups", (url: string) => fetchJson(url, InspectorGroupsListResponseSchema));
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
groups: response.data?.groups ?? [],
|
|
24
|
+
isLoading: response.isLoading,
|
|
25
|
+
error: response.error,
|
|
26
|
+
mutate: response.mutate,
|
|
27
|
+
};
|
|
28
|
+
}
|
package/src/mcp/server.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { z } from "zod";
|
|
19
|
+
import packageJson from "../../package.json";
|
|
19
20
|
import {
|
|
20
21
|
type GetPromptResult,
|
|
21
22
|
type ListResourcesResult,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
} from "@modelcontextprotocol/server";
|
|
28
29
|
import {
|
|
29
30
|
AddInspectorGroupSessionInputSchema,
|
|
31
|
+
DeleteInspectorGroupOptionsSchema,
|
|
30
32
|
GroupEvidenceExportOptionsSchema,
|
|
31
33
|
InspectorGroupSchema,
|
|
32
34
|
InspectorGroupStatusSchema,
|
|
@@ -47,6 +49,7 @@ import {
|
|
|
47
49
|
createGroupImpl,
|
|
48
50
|
createRunImpl,
|
|
49
51
|
createSessionKnowledgeImpl,
|
|
52
|
+
deleteGroupImpl,
|
|
50
53
|
exportEvidenceImpl,
|
|
51
54
|
exportGroupEvidenceImpl,
|
|
52
55
|
getGroupImpl,
|
|
@@ -80,12 +83,14 @@ import {
|
|
|
80
83
|
|
|
81
84
|
type ServerPair = { server: McpServer; transport: WebStandardStreamableHTTPServerTransport };
|
|
82
85
|
|
|
86
|
+
export const MCP_SERVER_VERSION = packageJson.version;
|
|
87
|
+
|
|
83
88
|
let initialized: ServerPair | null = null;
|
|
84
89
|
let initPromise: Promise<ServerPair> | null = null;
|
|
85
90
|
|
|
86
91
|
function buildServer(): ServerPair {
|
|
87
92
|
const server = new McpServer(
|
|
88
|
-
{ name: "agent-inspector", version:
|
|
93
|
+
{ name: "agent-inspector", version: MCP_SERVER_VERSION },
|
|
89
94
|
{ capabilities: { tools: {}, resources: {}, prompts: {} } },
|
|
90
95
|
);
|
|
91
96
|
registerTools(server);
|
|
@@ -658,6 +663,19 @@ function registerTools(server: McpServer): void {
|
|
|
658
663
|
(args) => safeCall(() => updateGroupImpl(callApi, args)),
|
|
659
664
|
);
|
|
660
665
|
|
|
666
|
+
server.registerTool(
|
|
667
|
+
"inspector_delete_group",
|
|
668
|
+
{
|
|
669
|
+
title: "Delete an Inspector group",
|
|
670
|
+
description:
|
|
671
|
+
"Deletes one declared Inspector group from <dataDir>/groups.json. By default this keeps exported evidence files; set deleteEvidence=true only when the local evidence pack should also be removed.",
|
|
672
|
+
inputSchema: DeleteInspectorGroupOptionsSchema.extend({
|
|
673
|
+
groupId: z.string().min(1).describe("The Inspector group id."),
|
|
674
|
+
}),
|
|
675
|
+
},
|
|
676
|
+
(args) => safeCall(() => deleteGroupImpl(callApi, args)),
|
|
677
|
+
);
|
|
678
|
+
|
|
661
679
|
server.registerTool(
|
|
662
680
|
"inspector_add_group_session",
|
|
663
681
|
{
|
|
@@ -1221,6 +1239,7 @@ export const TOOL_NAMES = [
|
|
|
1221
1239
|
"inspector_create_group",
|
|
1222
1240
|
"inspector_get_group",
|
|
1223
1241
|
"inspector_update_group",
|
|
1242
|
+
"inspector_delete_group",
|
|
1224
1243
|
"inspector_add_group_session",
|
|
1225
1244
|
"inspector_export_group_evidence",
|
|
1226
1245
|
"inspector_list_models",
|
package/src/mcp/toolHandlers.ts
CHANGED
|
@@ -18,11 +18,13 @@ import { z } from "zod";
|
|
|
18
18
|
import { LoopbackTimeoutError, type CallApiOptions } from "./loopback";
|
|
19
19
|
import { extractLastUserMessagePreview, extractResponsePreview } from "./previewExtractor";
|
|
20
20
|
import {
|
|
21
|
+
DeleteInspectorGroupResponseSchema,
|
|
21
22
|
GroupEvidenceExportResultSchema,
|
|
22
23
|
InspectorGroupSchema,
|
|
23
24
|
InspectorGroupsListResponseSchema,
|
|
24
25
|
type AddInspectorGroupSessionInput,
|
|
25
26
|
type CreateInspectorGroupInput,
|
|
27
|
+
type DeleteInspectorGroupOptions,
|
|
26
28
|
type UpdateInspectorGroupInput,
|
|
27
29
|
} from "../lib/groupContract";
|
|
28
30
|
import {
|
|
@@ -446,6 +448,26 @@ export async function updateGroupImpl(
|
|
|
446
448
|
return textJson(parsed.data);
|
|
447
449
|
}
|
|
448
450
|
|
|
451
|
+
export type DeleteGroupArgs = {
|
|
452
|
+
groupId: string;
|
|
453
|
+
} & DeleteInspectorGroupOptions;
|
|
454
|
+
|
|
455
|
+
export async function deleteGroupImpl(
|
|
456
|
+
callApi: CallApiFn,
|
|
457
|
+
args: DeleteGroupArgs,
|
|
458
|
+
): Promise<ToolResult> {
|
|
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);
|
|
469
|
+
}
|
|
470
|
+
|
|
449
471
|
export type AddGroupSessionArgs = {
|
|
450
472
|
groupId: string;
|
|
451
473
|
} & AddInspectorGroupSessionInput;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import {
|
|
4
|
+
GroupEvidenceExportResultSchema,
|
|
4
5
|
type GroupEvidenceExportOptions,
|
|
5
6
|
type GroupEvidenceExportResult,
|
|
6
7
|
type InspectorGroup,
|
|
@@ -339,3 +340,15 @@ export function readGroupEvidenceMarkdown(group: InspectorGroup): string | null
|
|
|
339
340
|
return null;
|
|
340
341
|
}
|
|
341
342
|
}
|
|
343
|
+
|
|
344
|
+
export function readGroupEvidenceDocument(group: InspectorGroup): GroupEvidenceExportResult | null {
|
|
345
|
+
const path = group.evidence?.jsonPath;
|
|
346
|
+
if (path === undefined || !existsSync(path)) return null;
|
|
347
|
+
try {
|
|
348
|
+
const raw: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
349
|
+
const parsed = GroupEvidenceExportResultSchema.safeParse(raw);
|
|
350
|
+
return parsed.success ? parsed.data : null;
|
|
351
|
+
} catch {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
}
|
package/src/proxy/groupStore.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import {
|
|
6
6
|
AddInspectorGroupSessionInputSchema,
|
|
7
7
|
CreateInspectorGroupInputSchema,
|
|
8
|
+
DeleteInspectorGroupOptionsSchema,
|
|
8
9
|
InspectorGroupSchema,
|
|
9
10
|
UpdateInspectorGroupInputSchema,
|
|
10
11
|
type AddInspectorGroupSessionInput,
|
|
11
12
|
type CreateInspectorGroupInput,
|
|
13
|
+
type DeleteInspectorGroupOptions,
|
|
14
|
+
type DeleteInspectorGroupResponse,
|
|
12
15
|
type InspectorGroup,
|
|
13
16
|
type InspectorGroupEvidence,
|
|
14
17
|
type InspectorGroupMember,
|
|
@@ -88,6 +91,33 @@ function sortedGroups(groups: readonly InspectorGroup[]): InspectorGroup[] {
|
|
|
88
91
|
return [...groups].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
89
92
|
}
|
|
90
93
|
|
|
94
|
+
function isPathInside(parentPath: string, childPath: string): boolean {
|
|
95
|
+
const relativePath = relative(parentPath, childPath);
|
|
96
|
+
return (
|
|
97
|
+
relativePath === "" ||
|
|
98
|
+
(relativePath.length > 0 && !relativePath.startsWith("..") && !isAbsolute(relativePath))
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function evidenceDirectoryFor(group: InspectorGroup): string | null {
|
|
103
|
+
if (group.evidence === null) return null;
|
|
104
|
+
|
|
105
|
+
const root = resolve(getDataDir(), "evidence", "groups");
|
|
106
|
+
const evidenceDir = resolve(dirname(group.evidence.jsonPath));
|
|
107
|
+
return isPathInside(root, evidenceDir) ? evidenceDir : null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function removeEvidenceDirectory(evidencePath: string | null, deleteEvidence: boolean): boolean {
|
|
111
|
+
if (!deleteEvidence || evidencePath === null || !existsSync(evidencePath)) return false;
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
rmSync(evidencePath, { recursive: true, force: true });
|
|
115
|
+
return !existsSync(evidencePath);
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
91
121
|
export function listGroups(): InspectorGroup[] {
|
|
92
122
|
return sortedGroups(readGroups());
|
|
93
123
|
}
|
|
@@ -153,6 +183,26 @@ export function updateGroup(
|
|
|
153
183
|
return updatedGroup;
|
|
154
184
|
}
|
|
155
185
|
|
|
186
|
+
export function deleteGroup(
|
|
187
|
+
groupId: string,
|
|
188
|
+
options: DeleteInspectorGroupOptions = {},
|
|
189
|
+
): DeleteInspectorGroupResponse | null {
|
|
190
|
+
const parsed = DeleteInspectorGroupOptionsSchema.parse(options);
|
|
191
|
+
const groups = readGroups();
|
|
192
|
+
const group = groups.find((candidate) => candidate.id === groupId);
|
|
193
|
+
if (group === undefined) return null;
|
|
194
|
+
|
|
195
|
+
const evidencePath = evidenceDirectoryFor(group);
|
|
196
|
+
writeGroups(groups.filter((candidate) => candidate.id !== groupId));
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
groupId: group.id,
|
|
200
|
+
deleted: true,
|
|
201
|
+
evidenceDeleted: removeEvidenceDirectory(evidencePath, parsed.deleteEvidence === true),
|
|
202
|
+
evidencePath,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
156
206
|
function matchesExistingMember(
|
|
157
207
|
member: InspectorGroupMember,
|
|
158
208
|
memberId: string,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { GroupEvidenceExportOptionsSchema } from "../../lib/groupContract";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
exportGroupEvidence,
|
|
5
|
+
readGroupEvidenceDocument,
|
|
6
|
+
readGroupEvidenceMarkdown,
|
|
7
|
+
} from "../../proxy/groupEvidenceExporter";
|
|
4
8
|
import { getGroup } from "../../proxy/groupStore";
|
|
5
9
|
|
|
6
10
|
type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
|
|
@@ -32,7 +36,15 @@ export const Route = createFileRoute("/api/groups/$groupId/evidence")({
|
|
|
32
36
|
);
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
|
|
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
|
+
});
|
|
36
48
|
},
|
|
37
49
|
POST: async ({ params, request }: { params: { groupId: string }; request: Request }) => {
|
|
38
50
|
const group = getGroup(params.groupId);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { UpdateInspectorGroupInputSchema } from "../../lib/groupContract";
|
|
3
|
-
import { getGroup, updateGroup } from "../../proxy/groupStore";
|
|
3
|
+
import { deleteGroup, getGroup, updateGroup } from "../../proxy/groupStore";
|
|
4
4
|
|
|
5
5
|
type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
|
|
6
|
+
type DeleteEvidenceFlagResult = { ok: true; value: boolean | undefined } | { ok: false };
|
|
6
7
|
|
|
7
8
|
async function readJsonBody(request: Request): Promise<JsonBodyResult> {
|
|
8
9
|
try {
|
|
@@ -14,6 +15,26 @@ async function readJsonBody(request: Request): Promise<JsonBodyResult> {
|
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
function readDeleteEvidenceFlag(request: Request): DeleteEvidenceFlagResult {
|
|
19
|
+
try {
|
|
20
|
+
const value = new URL(request.url).searchParams.get("deleteEvidence");
|
|
21
|
+
switch (value) {
|
|
22
|
+
case null:
|
|
23
|
+
return { ok: true, value: undefined };
|
|
24
|
+
case "1":
|
|
25
|
+
case "true":
|
|
26
|
+
return { ok: true, value: true };
|
|
27
|
+
case "0":
|
|
28
|
+
case "false":
|
|
29
|
+
return { ok: true, value: false };
|
|
30
|
+
default:
|
|
31
|
+
return { ok: false };
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
return { ok: false };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
17
38
|
export const Route = createFileRoute("/api/groups/$groupId")({
|
|
18
39
|
server: {
|
|
19
40
|
handlers: {
|
|
@@ -45,6 +66,22 @@ export const Route = createFileRoute("/api/groups/$groupId")({
|
|
|
45
66
|
|
|
46
67
|
return Response.json(updated);
|
|
47
68
|
},
|
|
69
|
+
DELETE: ({ params, request }: { params: { groupId: string }; request: Request }) => {
|
|
70
|
+
const deleteEvidence = readDeleteEvidenceFlag(request);
|
|
71
|
+
if (!deleteEvidence.ok) {
|
|
72
|
+
return Response.json(
|
|
73
|
+
{ error: "deleteEvidence must be one of true, false, 1, or 0" },
|
|
74
|
+
{ status: 400 },
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const deleted = deleteGroup(params.groupId, { deleteEvidence: deleteEvidence.value });
|
|
79
|
+
if (deleted === null) {
|
|
80
|
+
return Response.json({ error: "Group not found" }, { status: 404 });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return Response.json(deleted);
|
|
84
|
+
},
|
|
48
85
|
},
|
|
49
86
|
},
|
|
50
87
|
});
|