skillguard/lib/api-zod/src/generated/api.ts

627 lines
22 KiB
TypeScript
Raw Normal View History

2026-05-28 23:37:31 +00:00
/**
* Generated by orval v8.9.1 🍺
2026-05-28 23:37:31 +00:00
* Do not edit manually.
* Api
* API specification
* OpenAPI spec version: 0.1.0
*/
import * as zod from 'zod';
2026-05-28 23:37:31 +00:00
/**
* Returns server health status
* @summary Health check
*/
export const HealthCheckResponse = zod.object({
"status": zod.string()
})
/**
* Returns whether the caller is signed in and whether their email is on the admin allowlist. Always 200; never requires auth.
* @summary Current authentication and admin status
*/
export const GetMeResponse = zod.object({
"authenticated": zod.boolean(),
"isAdmin": zod.boolean(),
"email": zod.string().nullish().describe('The signed-in user\'s primary email, when available')
})
/**
* Aggregated statistics across all scans.
* @summary Dashboard summary
*/
export const GetDashboardResponse = zod.object({
"totalScans": zod.number(),
"avgRiskScore": zod.number(),
"verdictCounts": zod.object({
"pass": zod.number(),
"review": zod.number(),
"block": zod.number()
}),
"severityTotals": zod.object({
"critical": zod.number(),
"high": zod.number(),
"medium": zod.number(),
"low": zod.number(),
"info": zod.number()
}),
"axisTotals": zod.object({
"security": zod.number(),
"privacy": zod.number()
}),
"recentScans": zod.array(zod.object({
"id": zod.number(),
"name": zod.string(),
KI-generierte Skill-Beschreibung im Bericht Adds an AI-generated, factual German description ("Was macht dieser Skill?") to scans and shows it in the report. Changes: - DB: new nullable `description` column on scansTable (lib/db schema; pushed via drizzle-kit). - AI: new `generateSkillDescription()` in aiAnalysis.ts — reuses provider selection, token redaction, system prompt and JSON extraction; expects {"description": "..."}, returns null and never throws on failure. - Engine: scanEngine now generates the description independently of the AI findings rules — only a provider+token are required, so it works even when AI findings rules are disabled. Description failures do not break the scan. EngineResult gains aiDescription. (Provider/token error precedence unchanged for findings.) - Prompt: new admin-editable "description" prompt (Beschreibungs-Anweisung) seeded via onConflictDoNothing, consistent with system/analysis prompts. - Persist/serialize: description written on scan insert and returned in serializeScan (list + detail responses). - API spec: added nullable `description` to the Scan schema in openapi.yaml; regenerated zod + react-query clients via orval codegen. - Report UI: new "Was macht dieser Skill?" card in the report header (hidden when empty) and a matching section in the PDF/print export. Notes / deviations: - Old scans are not backfilled (per task scope); their description stays null and the section is hidden. - Description is requested as JSON ({"description": ...}) to stay compatible with the existing "JSON only" system prompt. - Verified: full typecheck passes, both workflows run, new prompt seeded, scans API returns description. Replit-Task-Id: 40c4457b-54d1-4283-a336-478620c3afa8
2026-06-10 21:13:51 +00:00
"description": zod.string().nullish().describe('AI-generated summary of the skill\'s purpose (null when no AI description is available)'),
"source": zod.enum(['zip', 'file', 'text']),
"status": zod.enum(['completed', 'failed']),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"fileCount": zod.number(),
"aiUsed": zod.boolean(),
"aiError": zod.string().nullish(),
"findingCounts": zod.object({
"critical": zod.number(),
"high": zod.number(),
"medium": zod.number(),
"low": zod.number(),
"info": zod.number(),
"security": zod.number(),
"privacy": zod.number(),
"total": zod.number()
}),
Add Skill-Fingerprint database & report comparison Each scan gets a deterministic overall fingerprint (SHA-256 over sorted path+fileHash pairs) plus per-file SHA-256 hashes and stored text content (binary: hash+size only). On upload the skill is always re-scanned and classified vs prior scans as new / identical / modified, with a per-fingerprint check counter, a "most similar known skill" link, and a file-level diff view. Deviations from the plan: - Relation matching keys off shared file *paths* (Jaccard over paths, tie-break on hashes), not hash-Jaccard alone, which is always 0 for single-file edits (text paste = one SKILL.md) and would mis-class every edited single-file skill as "new". Similarity is content-aware: identical files = 1.0, changed text files use line-level LCS ratio, added/removed/changed-binary = 0. - parseText no longer uses the display name as the file path (fixed "SKILL.md") so identical pastes with different names are "identical", not "modified". Backend: skillFingerprint.ts, lineDiff.ts (+lineSimilarity), skillParser.ts (per-file hash+isBinary), routes/scans.ts (computeRelation, content similarity, checkCount, comparedScan, GET /scans/:id/compare/:otherId). DB: scans fingerprint/relation/similarity/comparedScanId (+index), scan_files hash/content. API spec + orval codegen regenerated. UI: fingerprint card + compare link on report, relation badges in history, new /vergleich/:id/:otherId page with side-by-side summaries and expandable line diff. German UI, no emojis. Verified end-to-end against the running API and screenshotted both UI pages; test data cleaned up afterward. Code-review fix: relation classification no longer relies on path-Jaccard (every text paste shares path SKILL.md, so unrelated pastes were falsely linked as "modified"). computeRelation now selects the candidate by content-aware similarity and only returns "modified" when similarity >= 40 or a file is byte-identical; otherwise "new". Updated OpenAPI similarity description; removed now-unused jaccard import. Replit-Task-Id: 79a8e472-6635-493c-8995-3233ba7df75c
2026-06-10 19:34:46 +00:00
"fingerprint": zod.string().describe('Deterministic hash over all files (path + per-file hash)'),
"relation": zod.union([zod.literal('new'),zod.literal('identical'),zod.literal('modified'),zod.literal(null)]).nullable().describe('Relation to previously stored skills'),
"similarity": zod.number().nullable().describe('Content-aware similarity (0-100) to the compared skill (identical files count fully, changed text files use line-level similarity)'),
"comparedScanId": zod.number().nullable().describe('The scan this one was compared against, if any'),
"hidden": zod.boolean().describe('Whether an admin has hidden this scan from the public catalog'),
"createdAt": zod.string()
})),
"topRules": zod.array(zod.object({
"ruleId": zod.string(),
"title": zod.string(),
"axis": zod.enum(['security', 'privacy']),
"count": zod.number()
}))
})
/**
* @summary List scan history
*/
export const ListScansResponseItem = zod.object({
"id": zod.number(),
"name": zod.string(),
KI-generierte Skill-Beschreibung im Bericht Adds an AI-generated, factual German description ("Was macht dieser Skill?") to scans and shows it in the report. Changes: - DB: new nullable `description` column on scansTable (lib/db schema; pushed via drizzle-kit). - AI: new `generateSkillDescription()` in aiAnalysis.ts — reuses provider selection, token redaction, system prompt and JSON extraction; expects {"description": "..."}, returns null and never throws on failure. - Engine: scanEngine now generates the description independently of the AI findings rules — only a provider+token are required, so it works even when AI findings rules are disabled. Description failures do not break the scan. EngineResult gains aiDescription. (Provider/token error precedence unchanged for findings.) - Prompt: new admin-editable "description" prompt (Beschreibungs-Anweisung) seeded via onConflictDoNothing, consistent with system/analysis prompts. - Persist/serialize: description written on scan insert and returned in serializeScan (list + detail responses). - API spec: added nullable `description` to the Scan schema in openapi.yaml; regenerated zod + react-query clients via orval codegen. - Report UI: new "Was macht dieser Skill?" card in the report header (hidden when empty) and a matching section in the PDF/print export. Notes / deviations: - Old scans are not backfilled (per task scope); their description stays null and the section is hidden. - Description is requested as JSON ({"description": ...}) to stay compatible with the existing "JSON only" system prompt. - Verified: full typecheck passes, both workflows run, new prompt seeded, scans API returns description. Replit-Task-Id: 40c4457b-54d1-4283-a336-478620c3afa8
2026-06-10 21:13:51 +00:00
"description": zod.string().nullish().describe('AI-generated summary of the skill\'s purpose (null when no AI description is available)'),
"source": zod.enum(['zip', 'file', 'text']),
"status": zod.enum(['completed', 'failed']),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"fileCount": zod.number(),
"aiUsed": zod.boolean(),
"aiError": zod.string().nullish(),
"findingCounts": zod.object({
"critical": zod.number(),
"high": zod.number(),
"medium": zod.number(),
"low": zod.number(),
"info": zod.number(),
"security": zod.number(),
"privacy": zod.number(),
"total": zod.number()
}),
Add Skill-Fingerprint database & report comparison Each scan gets a deterministic overall fingerprint (SHA-256 over sorted path+fileHash pairs) plus per-file SHA-256 hashes and stored text content (binary: hash+size only). On upload the skill is always re-scanned and classified vs prior scans as new / identical / modified, with a per-fingerprint check counter, a "most similar known skill" link, and a file-level diff view. Deviations from the plan: - Relation matching keys off shared file *paths* (Jaccard over paths, tie-break on hashes), not hash-Jaccard alone, which is always 0 for single-file edits (text paste = one SKILL.md) and would mis-class every edited single-file skill as "new". Similarity is content-aware: identical files = 1.0, changed text files use line-level LCS ratio, added/removed/changed-binary = 0. - parseText no longer uses the display name as the file path (fixed "SKILL.md") so identical pastes with different names are "identical", not "modified". Backend: skillFingerprint.ts, lineDiff.ts (+lineSimilarity), skillParser.ts (per-file hash+isBinary), routes/scans.ts (computeRelation, content similarity, checkCount, comparedScan, GET /scans/:id/compare/:otherId). DB: scans fingerprint/relation/similarity/comparedScanId (+index), scan_files hash/content. API spec + orval codegen regenerated. UI: fingerprint card + compare link on report, relation badges in history, new /vergleich/:id/:otherId page with side-by-side summaries and expandable line diff. German UI, no emojis. Verified end-to-end against the running API and screenshotted both UI pages; test data cleaned up afterward. Code-review fix: relation classification no longer relies on path-Jaccard (every text paste shares path SKILL.md, so unrelated pastes were falsely linked as "modified"). computeRelation now selects the candidate by content-aware similarity and only returns "modified" when similarity >= 40 or a file is byte-identical; otherwise "new". Updated OpenAPI similarity description; removed now-unused jaccard import. Replit-Task-Id: 79a8e472-6635-493c-8995-3233ba7df75c
2026-06-10 19:34:46 +00:00
"fingerprint": zod.string().describe('Deterministic hash over all files (path + per-file hash)'),
"relation": zod.union([zod.literal('new'),zod.literal('identical'),zod.literal('modified'),zod.literal(null)]).nullable().describe('Relation to previously stored skills'),
"similarity": zod.number().nullable().describe('Content-aware similarity (0-100) to the compared skill (identical files count fully, changed text files use line-level similarity)'),
"comparedScanId": zod.number().nullable().describe('The scan this one was compared against, if any'),
"hidden": zod.boolean().describe('Whether an admin has hidden this scan from the public catalog'),
"createdAt": zod.string()
})
export const ListScansResponse = zod.array(ListScansResponseItem)
/**
* Accepts a skill as a base64 ZIP archive, a single base64 file, or pasted text, runs the static rule engine and (optionally) the configured AI analysis, and returns the completed report.
* @summary Upload a skill and run an audit
*/
export const CreateScanBody = zod.object({
"name": zod.string().nullish().describe('Optional display name for the scan'),
"source": zod.enum(['zip', 'file', 'text']),
"useAi": zod.boolean().describe('Whether to also run the configured AI analysis'),
"contentBase64": zod.string().nullish().describe('Base64 content for source=zip or source=file'),
"filename": zod.string().nullish().describe('Original filename for source=file or source=zip'),
"text": zod.string().nullish().describe('Raw skill text for source=text')
})
Add Skill-Fingerprint database & report comparison Each scan gets a deterministic overall fingerprint (SHA-256 over sorted path+fileHash pairs) plus per-file SHA-256 hashes and stored text content (binary: hash+size only). On upload the skill is always re-scanned and classified vs prior scans as new / identical / modified, with a per-fingerprint check counter, a "most similar known skill" link, and a file-level diff view. Deviations from the plan: - Relation matching keys off shared file *paths* (Jaccard over paths, tie-break on hashes), not hash-Jaccard alone, which is always 0 for single-file edits (text paste = one SKILL.md) and would mis-class every edited single-file skill as "new". Similarity is content-aware: identical files = 1.0, changed text files use line-level LCS ratio, added/removed/changed-binary = 0. - parseText no longer uses the display name as the file path (fixed "SKILL.md") so identical pastes with different names are "identical", not "modified". Backend: skillFingerprint.ts, lineDiff.ts (+lineSimilarity), skillParser.ts (per-file hash+isBinary), routes/scans.ts (computeRelation, content similarity, checkCount, comparedScan, GET /scans/:id/compare/:otherId). DB: scans fingerprint/relation/similarity/comparedScanId (+index), scan_files hash/content. API spec + orval codegen regenerated. UI: fingerprint card + compare link on report, relation badges in history, new /vergleich/:id/:otherId page with side-by-side summaries and expandable line diff. German UI, no emojis. Verified end-to-end against the running API and screenshotted both UI pages; test data cleaned up afterward. Code-review fix: relation classification no longer relies on path-Jaccard (every text paste shares path SKILL.md, so unrelated pastes were falsely linked as "modified"). computeRelation now selects the candidate by content-aware similarity and only returns "modified" when similarity >= 40 or a file is byte-identical; otherwise "new". Updated OpenAPI similarity description; removed now-unused jaccard import. Replit-Task-Id: 79a8e472-6635-493c-8995-3233ba7df75c
2026-06-10 19:34:46 +00:00
/**
* Returns a file-level diff between the current scan (id) and a previously stored scan (otherId), including line-by-line diffs for modified text files.
* @summary Compare two scans on the file level
*/
export const CompareScansParams = zod.object({
"id": zod.coerce.number(),
"otherId": zod.coerce.number()
})
export const CompareScansResponse = zod.object({
"current": zod.object({
"id": zod.number(),
"name": zod.string(),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"fileCount": zod.number(),
"fingerprint": zod.string(),
"createdAt": zod.string()
}),
"previous": zod.object({
"id": zod.number(),
"name": zod.string(),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"fileCount": zod.number(),
"fingerprint": zod.string(),
"createdAt": zod.string()
}),
"files": zod.array(zod.object({
"path": zod.string(),
"status": zod.enum(['unchanged', 'modified', 'added', 'removed']),
"previousHash": zod.string().nullable(),
"currentHash": zod.string().nullable(),
"previousSize": zod.number().nullable(),
"currentSize": zod.number().nullable(),
"previousHasContent": zod.boolean().nullable(),
"currentHasContent": zod.boolean().nullable(),
"lineDiff": zod.union([zod.array(zod.object({
"type": zod.enum(['context', 'add', 'remove']),
"text": zod.string(),
"previousLine": zod.number().nullable(),
"currentLine": zod.number().nullable()
})),zod.null()])
}))
})
Add skill version timeline (fingerprint lineage) Task #14: show a full version timeline for each skill family, not just the single most-similar prior scan. What changed: - OpenAPI spec (lib/api-spec/openapi.yaml): new GET /scans/{id}/lineage (operationId getScanLineage) returning an array of ScanLineageEntry (id, name, verdict, riskScore, relation, similarity, comparedScanId, fingerprint, createdAt). Regenerated api-zod + api-client-react via codegen. - API (artifacts/api-server/src/routes/scans.ts): new lineage endpoint. Builds an undirected graph over all scans linked by the comparedScanId chain AND identical (non-empty) fingerprints, then BFS-walks the connected component containing the requested scan and returns it newest-first. Works purely from existing data, no re-scanning. 404 for unknown ids. - UI (artifacts/skillguard/src/pages/scan-report.tsx): new VersionTimeline card rendering the family as a vertical timeline; each entry shows verdict, relation badge, similarity, risk score and date. The viewed scan is marked "Aktuell angezeigt"; every other entry links to the existing comparison view /vergleich/{viewedId}/{entryId}. Card hidden when the family has <=1 member. Notes: - Lineage = connected component, so any member returns the full family. - Verified end-to-end locally (created new/modified/identical chain, checked lineage ordering + 404, confirmed timeline + compare links in the UI), then deleted the test scans. Replit-Task-Id: c7f87ce6-59d8-4396-b16b-f20846f42f0b
2026-06-10 19:47:39 +00:00
/**
* Returns every scan in the same fingerprint lineage as the given scan (linked by an identical fingerprint or by the comparedScanId chain), newest first, so the full version history of a skill can be shown on a timeline without re-scanning.
* @summary Get the version timeline for a skill family
*/
export const GetScanLineageParams = zod.object({
"id": zod.coerce.number()
})
export const GetScanLineageResponseItem = zod.object({
"id": zod.number(),
"name": zod.string(),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"relation": zod.union([zod.literal('new'),zod.literal('identical'),zod.literal('modified'),zod.literal(null)]).nullable().describe('Relation of this version to the one it was compared against'),
"similarity": zod.number().nullable().describe('Content-aware similarity (0-100) to its compared version'),
"comparedScanId": zod.number().nullable().describe('The prior version this scan was compared against, if any'),
"fingerprint": zod.string(),
"createdAt": zod.string()
})
export const GetScanLineageResponse = zod.array(GetScanLineageResponseItem)
/**
* @summary Get a scan report with findings
*/
export const GetScanParams = zod.object({
"id": zod.coerce.number()
})
export const GetScanResponse = zod.object({
"id": zod.number(),
"name": zod.string(),
KI-generierte Skill-Beschreibung im Bericht Adds an AI-generated, factual German description ("Was macht dieser Skill?") to scans and shows it in the report. Changes: - DB: new nullable `description` column on scansTable (lib/db schema; pushed via drizzle-kit). - AI: new `generateSkillDescription()` in aiAnalysis.ts — reuses provider selection, token redaction, system prompt and JSON extraction; expects {"description": "..."}, returns null and never throws on failure. - Engine: scanEngine now generates the description independently of the AI findings rules — only a provider+token are required, so it works even when AI findings rules are disabled. Description failures do not break the scan. EngineResult gains aiDescription. (Provider/token error precedence unchanged for findings.) - Prompt: new admin-editable "description" prompt (Beschreibungs-Anweisung) seeded via onConflictDoNothing, consistent with system/analysis prompts. - Persist/serialize: description written on scan insert and returned in serializeScan (list + detail responses). - API spec: added nullable `description` to the Scan schema in openapi.yaml; regenerated zod + react-query clients via orval codegen. - Report UI: new "Was macht dieser Skill?" card in the report header (hidden when empty) and a matching section in the PDF/print export. Notes / deviations: - Old scans are not backfilled (per task scope); their description stays null and the section is hidden. - Description is requested as JSON ({"description": ...}) to stay compatible with the existing "JSON only" system prompt. - Verified: full typecheck passes, both workflows run, new prompt seeded, scans API returns description. Replit-Task-Id: 40c4457b-54d1-4283-a336-478620c3afa8
2026-06-10 21:13:51 +00:00
"description": zod.string().nullish().describe('AI-generated summary of the skill\'s purpose (null when no AI description is available)'),
"source": zod.enum(['zip', 'file', 'text']),
"status": zod.enum(['completed', 'failed']),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"fileCount": zod.number(),
"aiUsed": zod.boolean(),
"aiError": zod.string().nullish(),
"findingCounts": zod.object({
"critical": zod.number(),
"high": zod.number(),
"medium": zod.number(),
"low": zod.number(),
"info": zod.number(),
"security": zod.number(),
"privacy": zod.number(),
"total": zod.number()
}),
Add Skill-Fingerprint database & report comparison Each scan gets a deterministic overall fingerprint (SHA-256 over sorted path+fileHash pairs) plus per-file SHA-256 hashes and stored text content (binary: hash+size only). On upload the skill is always re-scanned and classified vs prior scans as new / identical / modified, with a per-fingerprint check counter, a "most similar known skill" link, and a file-level diff view. Deviations from the plan: - Relation matching keys off shared file *paths* (Jaccard over paths, tie-break on hashes), not hash-Jaccard alone, which is always 0 for single-file edits (text paste = one SKILL.md) and would mis-class every edited single-file skill as "new". Similarity is content-aware: identical files = 1.0, changed text files use line-level LCS ratio, added/removed/changed-binary = 0. - parseText no longer uses the display name as the file path (fixed "SKILL.md") so identical pastes with different names are "identical", not "modified". Backend: skillFingerprint.ts, lineDiff.ts (+lineSimilarity), skillParser.ts (per-file hash+isBinary), routes/scans.ts (computeRelation, content similarity, checkCount, comparedScan, GET /scans/:id/compare/:otherId). DB: scans fingerprint/relation/similarity/comparedScanId (+index), scan_files hash/content. API spec + orval codegen regenerated. UI: fingerprint card + compare link on report, relation badges in history, new /vergleich/:id/:otherId page with side-by-side summaries and expandable line diff. German UI, no emojis. Verified end-to-end against the running API and screenshotted both UI pages; test data cleaned up afterward. Code-review fix: relation classification no longer relies on path-Jaccard (every text paste shares path SKILL.md, so unrelated pastes were falsely linked as "modified"). computeRelation now selects the candidate by content-aware similarity and only returns "modified" when similarity >= 40 or a file is byte-identical; otherwise "new". Updated OpenAPI similarity description; removed now-unused jaccard import. Replit-Task-Id: 79a8e472-6635-493c-8995-3233ba7df75c
2026-06-10 19:34:46 +00:00
"fingerprint": zod.string().describe('Deterministic hash over all files (path + per-file hash)'),
"relation": zod.union([zod.literal('new'),zod.literal('identical'),zod.literal('modified'),zod.literal(null)]).nullable().describe('Relation to previously stored skills'),
"similarity": zod.number().nullable().describe('Content-aware similarity (0-100) to the compared skill (identical files count fully, changed text files use line-level similarity)'),
"comparedScanId": zod.number().nullable().describe('The scan this one was compared against, if any'),
"hidden": zod.boolean().describe('Whether an admin has hidden this scan from the public catalog'),
"createdAt": zod.string()
}).and(zod.object({
"files": zod.array(zod.object({
"path": zod.string(),
"kind": zod.enum(['instruction', 'script', 'resource']),
"language": zod.string().nullish(),
Add Skill-Fingerprint database & report comparison Each scan gets a deterministic overall fingerprint (SHA-256 over sorted path+fileHash pairs) plus per-file SHA-256 hashes and stored text content (binary: hash+size only). On upload the skill is always re-scanned and classified vs prior scans as new / identical / modified, with a per-fingerprint check counter, a "most similar known skill" link, and a file-level diff view. Deviations from the plan: - Relation matching keys off shared file *paths* (Jaccard over paths, tie-break on hashes), not hash-Jaccard alone, which is always 0 for single-file edits (text paste = one SKILL.md) and would mis-class every edited single-file skill as "new". Similarity is content-aware: identical files = 1.0, changed text files use line-level LCS ratio, added/removed/changed-binary = 0. - parseText no longer uses the display name as the file path (fixed "SKILL.md") so identical pastes with different names are "identical", not "modified". Backend: skillFingerprint.ts, lineDiff.ts (+lineSimilarity), skillParser.ts (per-file hash+isBinary), routes/scans.ts (computeRelation, content similarity, checkCount, comparedScan, GET /scans/:id/compare/:otherId). DB: scans fingerprint/relation/similarity/comparedScanId (+index), scan_files hash/content. API spec + orval codegen regenerated. UI: fingerprint card + compare link on report, relation badges in history, new /vergleich/:id/:otherId page with side-by-side summaries and expandable line diff. German UI, no emojis. Verified end-to-end against the running API and screenshotted both UI pages; test data cleaned up afterward. Code-review fix: relation classification no longer relies on path-Jaccard (every text paste shares path SKILL.md, so unrelated pastes were falsely linked as "modified"). computeRelation now selects the candidate by content-aware similarity and only returns "modified" when similarity >= 40 or a file is byte-identical; otherwise "new". Updated OpenAPI similarity description; removed now-unused jaccard import. Replit-Task-Id: 79a8e472-6635-493c-8995-3233ba7df75c
2026-06-10 19:34:46 +00:00
"size": zod.number(),
"hash": zod.string().describe('SHA-256 hash of the file content'),
"hasContent": zod.boolean().describe('Whether the text content was stored (false for binary files)'),
"content": zod.string().nullish().describe('The stored text content of the file, or null for binary files')
})),
"findings": zod.array(zod.object({
"id": zod.number(),
"ruleId": zod.string(),
"axis": zod.enum(['security', 'privacy']),
"severity": zod.enum(['critical', 'high', 'medium', 'low', 'info']),
"title": zod.string(),
"description": zod.string(),
"remediation": zod.string().nullish(),
"file": zod.string().nullish(),
"line": zod.number().nullish(),
"snippet": zod.string().nullish(),
"detectedBy": zod.enum(['static', 'ai'])
})),
"checkpoints": zod.array(zod.object({
"id": zod.string(),
"label": zod.string(),
"category": zod.string(),
"axis": zod.union([zod.literal('security'),zod.literal('privacy'),zod.literal(null)]).nullish(),
"severity": zod.union([zod.literal('critical'),zod.literal('high'),zod.literal('medium'),zod.literal('low'),zod.literal('info'),zod.literal(null)]).nullish(),
"status": zod.enum(['pass', 'flagged', 'skipped', 'error']),
"findingCount": zod.number(),
"scoreDelta": zod.number(),
"detectedBy": zod.union([zod.literal('static'),zod.literal('ai'),zod.literal(null)]).nullish()
Add Skill-Fingerprint database & report comparison Each scan gets a deterministic overall fingerprint (SHA-256 over sorted path+fileHash pairs) plus per-file SHA-256 hashes and stored text content (binary: hash+size only). On upload the skill is always re-scanned and classified vs prior scans as new / identical / modified, with a per-fingerprint check counter, a "most similar known skill" link, and a file-level diff view. Deviations from the plan: - Relation matching keys off shared file *paths* (Jaccard over paths, tie-break on hashes), not hash-Jaccard alone, which is always 0 for single-file edits (text paste = one SKILL.md) and would mis-class every edited single-file skill as "new". Similarity is content-aware: identical files = 1.0, changed text files use line-level LCS ratio, added/removed/changed-binary = 0. - parseText no longer uses the display name as the file path (fixed "SKILL.md") so identical pastes with different names are "identical", not "modified". Backend: skillFingerprint.ts, lineDiff.ts (+lineSimilarity), skillParser.ts (per-file hash+isBinary), routes/scans.ts (computeRelation, content similarity, checkCount, comparedScan, GET /scans/:id/compare/:otherId). DB: scans fingerprint/relation/similarity/comparedScanId (+index), scan_files hash/content. API spec + orval codegen regenerated. UI: fingerprint card + compare link on report, relation badges in history, new /vergleich/:id/:otherId page with side-by-side summaries and expandable line diff. German UI, no emojis. Verified end-to-end against the running API and screenshotted both UI pages; test data cleaned up afterward. Code-review fix: relation classification no longer relies on path-Jaccard (every text paste shares path SKILL.md, so unrelated pastes were falsely linked as "modified"). computeRelation now selects the candidate by content-aware similarity and only returns "modified" when similarity >= 40 or a file is byte-identical; otherwise "new". Updated OpenAPI similarity description; removed now-unused jaccard import. Replit-Task-Id: 79a8e472-6635-493c-8995-3233ba7df75c
2026-06-10 19:34:46 +00:00
}).describe('A single inspection step (Prüfschritt) with its partial assessment (Teilbewertung).')),
"checkCount": zod.number().describe('How often a skill with this exact fingerprint was scanned'),
"comparedScan": zod.union([zod.object({
"id": zod.number(),
"name": zod.string(),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"createdAt": zod.string()
}),zod.null()])
}))
/**
* Admin-only. Toggles whether a scan appears in the public catalog.
* @summary Moderate a scan (hide or unhide from the public catalog)
*/
export const ModerateScanParams = zod.object({
"id": zod.coerce.number()
})
export const ModerateScanBody = zod.object({
"hidden": zod.boolean().describe('Whether to hide the scan from the public catalog')
})
export const ModerateScanResponse = zod.object({
"id": zod.number(),
"name": zod.string(),
"description": zod.string().nullish().describe('AI-generated summary of the skill\'s purpose (null when no AI description is available)'),
"source": zod.enum(['zip', 'file', 'text']),
"status": zod.enum(['completed', 'failed']),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"fileCount": zod.number(),
"aiUsed": zod.boolean(),
"aiError": zod.string().nullish(),
"findingCounts": zod.object({
"critical": zod.number(),
"high": zod.number(),
"medium": zod.number(),
"low": zod.number(),
"info": zod.number(),
"security": zod.number(),
"privacy": zod.number(),
"total": zod.number()
}),
"fingerprint": zod.string().describe('Deterministic hash over all files (path + per-file hash)'),
"relation": zod.union([zod.literal('new'),zod.literal('identical'),zod.literal('modified'),zod.literal(null)]).nullable().describe('Relation to previously stored skills'),
"similarity": zod.number().nullable().describe('Content-aware similarity (0-100) to the compared skill (identical files count fully, changed text files use line-level similarity)'),
"comparedScanId": zod.number().nullable().describe('The scan this one was compared against, if any'),
"hidden": zod.boolean().describe('Whether an admin has hidden this scan from the public catalog'),
"createdAt": zod.string()
})
/**
* @summary Delete a scan report
*/
export const DeleteScanParams = zod.object({
"id": zod.coerce.number()
})
Add on-demand AI description generation for existing scans Task #24: Older scans created before description generation existed showed an empty "Was macht dieser Skill?" section. Users can now trigger description generation for any existing scan from the report. Changes: - OpenAPI: added POST /scans/{id}/description (operationId generateScanDescription) returning ScanDetail (200), ApiError (404 not found, 422 cannot generate). Regenerated api-zod and api-client-react via codegen. - api-server (routes/scans.ts): new route loads the scan, its stored files, the enabled provider and prompts, reconstructs ParsedFile[] from scan_files (binary files -> empty content/isBinary), calls existing generateSkillDescription(), persists description and returns full ScanDetail. Clean 422 errors when no provider / no token / generation yields nothing; the scan is never mutated on failure. - skillguard (scan-report.tsx): the description card now always renders; when no description exists it shows a "Beschreibung erzeugen" button wired to the new mutation, with loading state, toast feedback, and query cache update on success. Incidental fix: the dev/test database was missing the `scans.description` column (schema drift from the earlier description task). Ran drizzle-kit push to sync; this unblocked 5 previously failing api-server tests. All 59 tests now pass and full typecheck is green. Rebase: one conflict in scan-report.tsx import line — main added the `ShieldAlert` icon (new KI-disclaimer Alert), this branch added `Loader2`. Resolved by keeping both icons; the rest of the file (disclaimer Alert + new description card) merged cleanly. No semantic divergence. Replit-Task-Id: 0610af4f-aa62-434e-abcd-d742081b6459
2026-06-11 01:25:35 +00:00
/**
* @summary Generate the AI description for an existing scan
*/
export const GenerateScanDescriptionParams = zod.object({
"id": zod.coerce.number()
})
export const GenerateScanDescriptionResponse = zod.object({
"id": zod.number(),
"name": zod.string(),
"description": zod.string().nullish().describe('AI-generated summary of the skill\'s purpose (null when no AI description is available)'),
"source": zod.enum(['zip', 'file', 'text']),
"status": zod.enum(['completed', 'failed']),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"fileCount": zod.number(),
"aiUsed": zod.boolean(),
"aiError": zod.string().nullish(),
"findingCounts": zod.object({
"critical": zod.number(),
"high": zod.number(),
"medium": zod.number(),
"low": zod.number(),
"info": zod.number(),
"security": zod.number(),
"privacy": zod.number(),
"total": zod.number()
}),
"fingerprint": zod.string().describe('Deterministic hash over all files (path + per-file hash)'),
"relation": zod.union([zod.literal('new'),zod.literal('identical'),zod.literal('modified'),zod.literal(null)]).nullable().describe('Relation to previously stored skills'),
"similarity": zod.number().nullable().describe('Content-aware similarity (0-100) to the compared skill (identical files count fully, changed text files use line-level similarity)'),
"comparedScanId": zod.number().nullable().describe('The scan this one was compared against, if any'),
"hidden": zod.boolean().describe('Whether an admin has hidden this scan from the public catalog'),
Add on-demand AI description generation for existing scans Task #24: Older scans created before description generation existed showed an empty "Was macht dieser Skill?" section. Users can now trigger description generation for any existing scan from the report. Changes: - OpenAPI: added POST /scans/{id}/description (operationId generateScanDescription) returning ScanDetail (200), ApiError (404 not found, 422 cannot generate). Regenerated api-zod and api-client-react via codegen. - api-server (routes/scans.ts): new route loads the scan, its stored files, the enabled provider and prompts, reconstructs ParsedFile[] from scan_files (binary files -> empty content/isBinary), calls existing generateSkillDescription(), persists description and returns full ScanDetail. Clean 422 errors when no provider / no token / generation yields nothing; the scan is never mutated on failure. - skillguard (scan-report.tsx): the description card now always renders; when no description exists it shows a "Beschreibung erzeugen" button wired to the new mutation, with loading state, toast feedback, and query cache update on success. Incidental fix: the dev/test database was missing the `scans.description` column (schema drift from the earlier description task). Ran drizzle-kit push to sync; this unblocked 5 previously failing api-server tests. All 59 tests now pass and full typecheck is green. Rebase: one conflict in scan-report.tsx import line — main added the `ShieldAlert` icon (new KI-disclaimer Alert), this branch added `Loader2`. Resolved by keeping both icons; the rest of the file (disclaimer Alert + new description card) merged cleanly. No semantic divergence. Replit-Task-Id: 0610af4f-aa62-434e-abcd-d742081b6459
2026-06-11 01:25:35 +00:00
"createdAt": zod.string()
}).and(zod.object({
"files": zod.array(zod.object({
"path": zod.string(),
"kind": zod.enum(['instruction', 'script', 'resource']),
"language": zod.string().nullish(),
"size": zod.number(),
"hash": zod.string().describe('SHA-256 hash of the file content'),
"hasContent": zod.boolean().describe('Whether the text content was stored (false for binary files)'),
"content": zod.string().nullish().describe('The stored text content of the file, or null for binary files')
Add on-demand AI description generation for existing scans Task #24: Older scans created before description generation existed showed an empty "Was macht dieser Skill?" section. Users can now trigger description generation for any existing scan from the report. Changes: - OpenAPI: added POST /scans/{id}/description (operationId generateScanDescription) returning ScanDetail (200), ApiError (404 not found, 422 cannot generate). Regenerated api-zod and api-client-react via codegen. - api-server (routes/scans.ts): new route loads the scan, its stored files, the enabled provider and prompts, reconstructs ParsedFile[] from scan_files (binary files -> empty content/isBinary), calls existing generateSkillDescription(), persists description and returns full ScanDetail. Clean 422 errors when no provider / no token / generation yields nothing; the scan is never mutated on failure. - skillguard (scan-report.tsx): the description card now always renders; when no description exists it shows a "Beschreibung erzeugen" button wired to the new mutation, with loading state, toast feedback, and query cache update on success. Incidental fix: the dev/test database was missing the `scans.description` column (schema drift from the earlier description task). Ran drizzle-kit push to sync; this unblocked 5 previously failing api-server tests. All 59 tests now pass and full typecheck is green. Rebase: one conflict in scan-report.tsx import line — main added the `ShieldAlert` icon (new KI-disclaimer Alert), this branch added `Loader2`. Resolved by keeping both icons; the rest of the file (disclaimer Alert + new description card) merged cleanly. No semantic divergence. Replit-Task-Id: 0610af4f-aa62-434e-abcd-d742081b6459
2026-06-11 01:25:35 +00:00
})),
"findings": zod.array(zod.object({
"id": zod.number(),
"ruleId": zod.string(),
"axis": zod.enum(['security', 'privacy']),
"severity": zod.enum(['critical', 'high', 'medium', 'low', 'info']),
"title": zod.string(),
"description": zod.string(),
"remediation": zod.string().nullish(),
"file": zod.string().nullish(),
"line": zod.number().nullish(),
"snippet": zod.string().nullish(),
"detectedBy": zod.enum(['static', 'ai'])
})),
"checkpoints": zod.array(zod.object({
"id": zod.string(),
"label": zod.string(),
"category": zod.string(),
"axis": zod.union([zod.literal('security'),zod.literal('privacy'),zod.literal(null)]).nullish(),
"severity": zod.union([zod.literal('critical'),zod.literal('high'),zod.literal('medium'),zod.literal('low'),zod.literal('info'),zod.literal(null)]).nullish(),
"status": zod.enum(['pass', 'flagged', 'skipped', 'error']),
"findingCount": zod.number(),
"scoreDelta": zod.number(),
"detectedBy": zod.union([zod.literal('static'),zod.literal('ai'),zod.literal(null)]).nullish()
}).describe('A single inspection step (Prüfschritt) with its partial assessment (Teilbewertung).')),
"checkCount": zod.number().describe('How often a skill with this exact fingerprint was scanned'),
"comparedScan": zod.union([zod.object({
"id": zod.number(),
"name": zod.string(),
"verdict": zod.enum(['pass', 'review', 'block']),
"riskScore": zod.number(),
"createdAt": zod.string()
}),zod.null()])
}))
/**
* @summary List configured AI providers
*/
export const ListProvidersResponseItem = zod.object({
"id": zod.number(),
"name": zod.string(),
"apiType": zod.enum(['openai', 'anthropic', 'custom']),
"baseUrl": zod.string(),
"model": zod.string(),
"enabled": zod.boolean(),
"hasToken": zod.boolean(),
"tokenPreview": zod.string().describe('Masked preview of the stored token (e.g. \"sk-...abcd\")'),
"createdAt": zod.string()
})
export const ListProvidersResponse = zod.array(ListProvidersResponseItem)
/**
* @summary Create an AI provider
*/
export const CreateProviderBody = zod.object({
"name": zod.string().min(1),
"apiType": zod.enum(['openai', 'anthropic', 'custom']),
"baseUrl": zod.string().min(1),
"model": zod.string().min(1),
"apiToken": zod.string().optional(),
"enabled": zod.boolean().optional()
})
/**
* @summary Update an AI provider
*/
export const UpdateProviderParams = zod.object({
"id": zod.coerce.number()
})
export const UpdateProviderBody = zod.object({
"name": zod.string().min(1).optional(),
"apiType": zod.enum(['openai', 'anthropic', 'custom']).optional(),
"baseUrl": zod.string().min(1).optional(),
"model": zod.string().min(1).optional(),
"apiToken": zod.string().optional().describe('Provide to replace the stored token; omit to keep existing'),
"enabled": zod.boolean().optional()
})
export const UpdateProviderResponse = zod.object({
"id": zod.number(),
"name": zod.string(),
"apiType": zod.enum(['openai', 'anthropic', 'custom']),
"baseUrl": zod.string(),
"model": zod.string(),
"enabled": zod.boolean(),
"hasToken": zod.boolean(),
"tokenPreview": zod.string().describe('Masked preview of the stored token (e.g. \"sk-...abcd\")'),
"createdAt": zod.string()
})
/**
* @summary Delete an AI provider
*/
export const DeleteProviderParams = zod.object({
"id": zod.coerce.number()
})
/**
* @summary Test the connection to an AI provider
*/
export const TestProviderParams = zod.object({
"id": zod.coerce.number()
})
export const TestProviderResponse = zod.object({
"ok": zod.boolean(),
"message": zod.string().nullish()
})
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
/**
* @summary Test a provider connection with ad-hoc configuration
*/
export const TestProviderConnectionBody = zod.object({
"apiType": zod.enum(['openai', 'anthropic', 'custom']),
"baseUrl": zod.string().min(1),
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
"model": zod.string().optional().describe('Optional model to exercise with a full request; when omitted the test verifies credentials via the models endpoint instead'),
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
"apiToken": zod.string().optional().describe('Token to use for the test; omit or leave empty to fall back to the stored token of providerId'),
"providerId": zod.number().optional().describe('When apiToken is empty, fall back to this saved provider\'s stored token')
})
export const TestProviderConnectionResponse = zod.object({
"ok": zod.boolean(),
"message": zod.string().nullish()
})
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
/**
* Queries the provider's models endpoint with the supplied ad-hoc configuration (or the stored token of providerId when the token is omitted) and returns the discovered model IDs.
* @summary List the available models for a provider configuration
*/
export const ListProviderModelsBody = zod.object({
"apiType": zod.enum(['openai', 'anthropic', 'custom']),
"baseUrl": zod.string().min(1),
"apiToken": zod.string().optional().describe('Token to use for the request; omit or leave empty to fall back to the stored token of providerId'),
"providerId": zod.number().optional().describe('When apiToken is empty, fall back to this saved provider\'s stored token')
})
export const ListProviderModelsResponse = zod.object({
"ok": zod.boolean(),
"models": zod.array(zod.string()),
"message": zod.string().nullish()
})
/**
* @summary List configurable AI prompts
*/
export const ListPromptsResponseItem = zod.object({
"id": zod.number(),
"key": zod.string(),
"name": zod.string(),
"content": zod.string(),
"updatedAt": zod.string()
})
export const ListPromptsResponse = zod.array(ListPromptsResponseItem)
/**
* @summary Update an AI prompt
*/
export const UpdatePromptParams = zod.object({
"id": zod.coerce.number()
})
export const UpdatePromptBody = zod.object({
"name": zod.string().min(1).optional(),
"content": zod.string().min(1).optional()
})
export const UpdatePromptResponse = zod.object({
"id": zod.number(),
"key": zod.string(),
"name": zod.string(),
"content": zod.string(),
"updatedAt": zod.string()
})
/**
* @summary List the static rule catalog
*/
export const ListRulesResponseItem = zod.object({
"id": zod.number(),
"ruleId": zod.string(),
"axis": zod.enum(['security', 'privacy']),
"category": zod.string(),
"title": zod.string(),
"description": zod.string(),
"severity": zod.enum(['critical', 'high', 'medium', 'low', 'info']),
"detectionType": zod.enum(['regex', 'heuristic', 'ai']),
"enabled": zod.boolean()
})
export const ListRulesResponse = zod.array(ListRulesResponseItem)
/**
* @summary Update a rule's severity or enabled state
*/
export const UpdateRuleParams = zod.object({
"id": zod.coerce.number()
})
export const UpdateRuleBody = zod.object({
"severity": zod.enum(['critical', 'high', 'medium', 'low', 'info']).optional(),
"enabled": zod.boolean().optional()
})
export const UpdateRuleResponse = zod.object({
"id": zod.number(),
"ruleId": zod.string(),
"axis": zod.enum(['security', 'privacy']),
"category": zod.string(),
"title": zod.string(),
"description": zod.string(),
"severity": zod.enum(['critical', 'high', 'medium', 'low', 'info']),
"detectionType": zod.enum(['regex', 'heuristic', 'ai']),
"enabled": zod.boolean()
})