@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.
Files changed (41) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-BDzfRSt1.js → CompareDrawer-Dgi0hhg5.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-4dG0nLRp.js +115 -0
  4. package/.output/public/assets/{ReplayDialog-p-5TKg75.js → ReplayDialog-BL6BwDuI.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-G0dV8-D_.js → RequestAnatomy-DZMmiIiD.js} +1 -1
  6. package/.output/public/assets/{ResponseView-D2wE6lD-.js → ResponseView-DJLOiTYq.js} +1 -1
  7. package/.output/public/assets/{StreamingChunkSequence-D8JRK-j_.js → StreamingChunkSequence--SxZ5cxc.js} +1 -1
  8. package/.output/public/assets/_sessionId-C4z6gu9i.js +1 -0
  9. package/.output/public/assets/index-Drpb2qR5.js +1 -0
  10. package/.output/public/assets/index-fUUi8iQF.css +1 -0
  11. package/.output/public/assets/{main-XHHZsOBK.js → main-CbRIHhJ5.js} +2 -2
  12. package/.output/server/_libs/lucide-react.mjs +160 -135
  13. package/.output/server/{_sessionId-CK9QEhkB.mjs → _sessionId-DNfumR8F.mjs} +2 -2
  14. package/.output/server/_ssr/{CompareDrawer-BJVPFMIx.mjs → CompareDrawer-CzgjhnUa.mjs} +3 -3
  15. package/.output/server/_ssr/{ProxyViewerContainer-DhbmJ2zj.mjs → ProxyViewerContainer-BqOcZ7en.mjs} +737 -17
  16. package/.output/server/_ssr/{ReplayDialog-DXRj95Gj.mjs → ReplayDialog-DmEknIL8.mjs} +4 -4
  17. package/.output/server/_ssr/{RequestAnatomy-CrYJTXdJ.mjs → RequestAnatomy-DNoiMudY.mjs} +3 -3
  18. package/.output/server/_ssr/{ResponseView-4ESqSITr.mjs → ResponseView-C_cRrleT.mjs} +3 -3
  19. package/.output/server/_ssr/{StreamingChunkSequence-Czdko5n_.mjs → StreamingChunkSequence-DHZ9Q_dL.mjs} +3 -3
  20. package/.output/server/_ssr/{index-bei0I7qL.mjs → index-coYS9XyS.mjs} +2 -2
  21. package/.output/server/_ssr/index.mjs +2 -2
  22. package/.output/server/_ssr/{router-sQh4Gk8J.mjs → router-DUb5lKw1.mjs} +174 -40
  23. package/.output/server/{_tanstack-start-manifest_v-C0fdSWnA.mjs → _tanstack-start-manifest_v-xcG1o8Vt.mjs} +1 -1
  24. package/.output/server/index.mjs +60 -60
  25. package/package.json +1 -1
  26. package/src/components/ProxyViewer.tsx +9 -2
  27. package/src/components/groups/GroupsDialog.tsx +945 -0
  28. package/src/lib/groupContract.ts +24 -2
  29. package/src/lib/runContract.ts +2 -0
  30. package/src/lib/useGroupEvidence.ts +33 -0
  31. package/src/lib/useGroups.ts +28 -0
  32. package/src/mcp/server.ts +20 -1
  33. package/src/mcp/toolHandlers.ts +22 -0
  34. package/src/proxy/groupEvidenceExporter.ts +13 -0
  35. package/src/proxy/groupStore.ts +52 -2
  36. package/src/routes/api/groups.$groupId.evidence.ts +14 -2
  37. package/src/routes/api/groups.$groupId.ts +38 -1
  38. package/.output/public/assets/ProxyViewerContainer-NtOD8Ex_.js +0 -115
  39. package/.output/public/assets/_sessionId-zimzVrJE.js +0 -1
  40. package/.output/public/assets/index-Cv5vPgpr.js +0 -1
  41. package/.output/public/assets/index-CwlHPmgL.css +0 -1
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { JsonValueSchema } from "../proxy/schemas";
3
- import { InspectorRunSchema, InspectorRunStatusSchema, JenkinsReportSchema } from "./runContract";
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: JenkinsReportSchema,
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>;
@@ -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: "1.0.0" },
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",
@@ -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
+ }
@@ -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 { exportGroupEvidence, readGroupEvidenceMarkdown } from "../../proxy/groupEvidenceExporter";
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
- return Response.json({ group, evidence: group.evidence, markdown });
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
  });