skillguard/lib/api-spec/openapi.yaml

1093 lines
27 KiB
YAML
Raw Normal View History

2026-05-28 23:37:31 +00:00
openapi: 3.1.0
info:
# Do not change the title, if the title changes, the import paths will be broken
title: Api
version: 0.1.0
description: API specification
servers:
- url: /api
description: Base API path
tags:
- name: health
description: Health operations
- name: scans
description: Skill scans and audit reports
- name: providers
description: Configurable external AI providers
- name: prompts
description: Configurable AI analysis prompts
- name: rules
description: Static rule catalog configuration
- name: dashboard
description: Dashboard summaries
2026-05-28 23:37:31 +00:00
paths:
/healthz:
get:
operationId: healthCheck
tags: [health]
summary: Health check
description: Returns server health status
responses:
"200":
description: Healthy
content:
application/json:
schema:
$ref: "#/components/schemas/HealthStatus"
/dashboard:
get:
operationId: getDashboard
tags: [dashboard]
summary: Dashboard summary
description: Aggregated statistics across all scans.
responses:
"200":
description: Dashboard summary
content:
application/json:
schema:
$ref: "#/components/schemas/DashboardSummary"
/scans:
get:
operationId: listScans
tags: [scans]
summary: List scan history
responses:
"200":
description: List of scans (most recent first)
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Scan"
post:
operationId: createScan
tags: [scans]
summary: Upload a skill and run an audit
description: >-
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.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SkillScanInput"
responses:
"201":
description: Completed scan report
content:
application/json:
schema:
$ref: "#/components/schemas/ScanDetail"
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/ApiError"
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
/scans/{id}/compare/{otherId}:
get:
operationId: compareScans
tags: [scans]
summary: Compare two scans on the file level
description: >-
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.
parameters:
- name: id
in: path
required: true
schema:
type: integer
- name: otherId
in: path
required: true
schema:
type: integer
responses:
"200":
description: File-level comparison
content:
application/json:
schema:
$ref: "#/components/schemas/ScanComparison"
"404":
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/ApiError"
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
/scans/{id}/lineage:
get:
operationId: getScanLineage
tags: [scans]
summary: Get the version timeline for a skill family
description: >-
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.
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Version timeline (most recent first)
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ScanLineageEntry"
"404":
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/ApiError"
/scans/{id}:
get:
operationId: getScan
tags: [scans]
summary: Get a scan report with findings
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Scan report
content:
application/json:
schema:
$ref: "#/components/schemas/ScanDetail"
"404":
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/ApiError"
delete:
operationId: deleteScan
tags: [scans]
summary: Delete a scan report
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"204":
description: Deleted
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
/scans/{id}/description:
post:
operationId: generateScanDescription
tags: [scans]
summary: Generate the AI description for an existing scan
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Scan report with the newly generated description
content:
application/json:
schema:
$ref: "#/components/schemas/ScanDetail"
"404":
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/ApiError"
"422":
description: Description could not be generated
content:
application/json:
schema:
$ref: "#/components/schemas/ApiError"
/providers:
get:
operationId: listProviders
tags: [providers]
summary: List configured AI providers
responses:
"200":
description: List of providers (tokens masked)
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/AiProvider"
post:
operationId: createProvider
tags: [providers]
summary: Create an AI provider
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/AiProviderInput"
responses:
"201":
description: Created provider
content:
application/json:
schema:
$ref: "#/components/schemas/AiProvider"
/providers/{id}:
patch:
operationId: updateProvider
tags: [providers]
summary: Update an AI provider
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/AiProviderUpdate"
responses:
"200":
description: Updated provider
content:
application/json:
schema:
$ref: "#/components/schemas/AiProvider"
delete:
operationId: deleteProvider
tags: [providers]
summary: Delete an AI provider
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"204":
description: Deleted
/providers/{id}/test:
post:
operationId: testProvider
tags: [providers]
summary: Test the connection to an AI provider
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Test result
content:
application/json:
schema:
$ref: "#/components/schemas/ProviderTestResult"
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
/providers/test-connection:
post:
operationId: testProviderConnection
tags: [providers]
summary: Test a provider connection with ad-hoc configuration
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ProviderTestConnectionInput"
responses:
"200":
description: Test result
content:
application/json:
schema:
$ref: "#/components/schemas/ProviderTestResult"
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
/providers/list-models:
post:
operationId: listProviderModels
tags: [providers]
summary: List the available models for a provider configuration
description: >-
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.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ProviderListModelsInput"
responses:
"200":
description: Discovered models
content:
application/json:
schema:
$ref: "#/components/schemas/ProviderModelsResult"
/prompts:
get:
operationId: listPrompts
tags: [prompts]
summary: List configurable AI prompts
responses:
"200":
description: List of prompts
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Prompt"
/prompts/{id}:
patch:
operationId: updatePrompt
tags: [prompts]
summary: Update an AI prompt
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PromptUpdate"
responses:
"200":
description: Updated prompt
content:
application/json:
schema:
$ref: "#/components/schemas/Prompt"
/rules:
get:
operationId: listRules
tags: [rules]
summary: List the static rule catalog
responses:
"200":
description: List of rules
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Rule"
/rules/{id}:
patch:
operationId: updateRule
tags: [rules]
summary: Update a rule's severity or enabled state
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RuleUpdate"
responses:
"200":
description: Updated rule
content:
application/json:
schema:
$ref: "#/components/schemas/Rule"
2026-05-28 23:37:31 +00:00
components:
schemas:
HealthStatus:
type: object
properties:
status:
type: string
required:
- status
ApiError:
type: object
required: [error]
properties:
error:
type: string
SkillScanInput:
type: object
required: [source, useAi]
properties:
name:
type: ["string", "null"]
description: Optional display name for the scan
source:
type: string
enum: [zip, file, text]
useAi:
type: boolean
description: Whether to also run the configured AI analysis
contentBase64:
type: ["string", "null"]
description: Base64 content for source=zip or source=file
filename:
type: ["string", "null"]
description: Original filename for source=file or source=zip
text:
type: ["string", "null"]
description: Raw skill text for source=text
Scan:
type: object
required:
- id
- name
- source
- status
- verdict
- riskScore
- fileCount
- aiUsed
- findingCounts
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
- relation
- similarity
- comparedScanId
- createdAt
properties:
id:
type: integer
name:
type: 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:
type: ["string", "null"]
description: AI-generated summary of the skill's purpose (null when no AI description is available)
source:
type: string
enum: [zip, file, text]
status:
type: string
enum: [completed, failed]
verdict:
type: string
enum: [pass, review, block]
riskScore:
type: integer
fileCount:
type: integer
aiUsed:
type: boolean
aiError:
type: ["string", "null"]
findingCounts:
$ref: "#/components/schemas/FindingCounts"
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:
type: string
description: Deterministic hash over all files (path + per-file hash)
relation:
type: ["string", "null"]
enum: [new, identical, modified, null]
description: Relation to previously stored skills
similarity:
type: ["integer", "null"]
description: Content-aware similarity (0-100) to the compared skill (identical files count fully, changed text files use line-level similarity)
comparedScanId:
type: ["integer", "null"]
description: The scan this one was compared against, if any
createdAt:
type: string
FindingCounts:
type: object
required: [critical, high, medium, low, info, security, privacy, total]
properties:
critical:
type: integer
high:
type: integer
medium:
type: integer
low:
type: integer
info:
type: integer
security:
type: integer
privacy:
type: integer
total:
type: integer
ScanCheckpoint:
type: object
description: >-
A single inspection step (Prüfschritt) with its partial assessment
(Teilbewertung).
required: [id, label, category, status, findingCount, scoreDelta]
properties:
id:
type: string
label:
type: string
category:
type: string
axis:
type: ["string", "null"]
enum: [security, privacy, null]
severity:
type: ["string", "null"]
enum: [critical, high, medium, low, info, null]
status:
type: string
enum: [pass, flagged, skipped, error]
findingCount:
type: integer
scoreDelta:
type: integer
detectedBy:
type: ["string", "null"]
enum: [static, ai, null]
ScanFile:
type: object
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
required: [path, kind, size, hash, hasContent]
properties:
path:
type: string
kind:
type: string
enum: [instruction, script, resource]
language:
type: ["string", "null"]
size:
type: integer
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
hash:
type: string
description: SHA-256 hash of the file content
hasContent:
type: boolean
description: Whether the text content was stored (false for binary files)
Finding:
type: object
required:
- id
- ruleId
- axis
- severity
- title
- description
- detectedBy
properties:
id:
type: integer
ruleId:
type: string
axis:
type: string
enum: [security, privacy]
severity:
type: string
enum: [critical, high, medium, low, info]
title:
type: string
description:
type: string
remediation:
type: ["string", "null"]
file:
type: ["string", "null"]
line:
type: ["integer", "null"]
snippet:
type: ["string", "null"]
detectedBy:
type: string
enum: [static, ai]
ScanDetail:
allOf:
- $ref: "#/components/schemas/Scan"
- type: object
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
required: [files, findings, checkpoints, checkCount, comparedScan]
properties:
files:
type: array
items:
$ref: "#/components/schemas/ScanFile"
findings:
type: array
items:
$ref: "#/components/schemas/Finding"
checkpoints:
type: array
items:
$ref: "#/components/schemas/ScanCheckpoint"
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
checkCount:
type: integer
description: How often a skill with this exact fingerprint was scanned
comparedScan:
oneOf:
- $ref: "#/components/schemas/ComparedScan"
- type: "null"
ComparedScan:
type: object
required: [id, name, verdict, riskScore, createdAt]
properties:
id:
type: integer
name:
type: string
verdict:
type: string
enum: [pass, review, block]
riskScore:
type: integer
createdAt:
type: string
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
ScanLineageEntry:
type: object
required:
- id
- name
- verdict
- riskScore
- relation
- similarity
- comparedScanId
- fingerprint
- createdAt
properties:
id:
type: integer
name:
type: string
verdict:
type: string
enum: [pass, review, block]
riskScore:
type: integer
relation:
type: ["string", "null"]
enum: [new, identical, modified, null]
description: Relation of this version to the one it was compared against
similarity:
type: ["integer", "null"]
description: Content-aware similarity (0-100) to its compared version
comparedScanId:
type: ["integer", "null"]
description: The prior version this scan was compared against, if any
fingerprint:
type: string
createdAt:
type: string
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
ScanComparisonSide:
type: object
required: [id, name, verdict, riskScore, fileCount, fingerprint, createdAt]
properties:
id:
type: integer
name:
type: string
verdict:
type: string
enum: [pass, review, block]
riskScore:
type: integer
fileCount:
type: integer
fingerprint:
type: string
createdAt:
type: string
DiffLine:
type: object
required: [type, text, previousLine, currentLine]
properties:
type:
type: string
enum: [context, add, remove]
text:
type: string
previousLine:
type: ["integer", "null"]
currentLine:
type: ["integer", "null"]
ScanFileDiff:
type: object
required:
- path
- status
- previousHash
- currentHash
- previousSize
- currentSize
- previousHasContent
- currentHasContent
- lineDiff
properties:
path:
type: string
status:
type: string
enum: [unchanged, modified, added, removed]
previousHash:
type: ["string", "null"]
currentHash:
type: ["string", "null"]
previousSize:
type: ["integer", "null"]
currentSize:
type: ["integer", "null"]
previousHasContent:
type: ["boolean", "null"]
currentHasContent:
type: ["boolean", "null"]
lineDiff:
oneOf:
- type: array
items:
$ref: "#/components/schemas/DiffLine"
- type: "null"
ScanComparison:
type: object
required: [current, previous, files]
properties:
current:
$ref: "#/components/schemas/ScanComparisonSide"
previous:
$ref: "#/components/schemas/ScanComparisonSide"
files:
type: array
items:
$ref: "#/components/schemas/ScanFileDiff"
AiProvider:
type: object
required:
- id
- name
- apiType
- baseUrl
- model
- enabled
- hasToken
- tokenPreview
- createdAt
properties:
id:
type: integer
name:
type: string
apiType:
type: string
enum: [openai, anthropic, custom]
baseUrl:
type: string
model:
type: string
enabled:
type: boolean
hasToken:
type: boolean
tokenPreview:
type: string
description: Masked preview of the stored token (e.g. "sk-...abcd")
createdAt:
type: string
AiProviderInput:
type: object
required: [name, apiType, baseUrl, model]
properties:
name:
type: string
minLength: 1
apiType:
type: string
enum: [openai, anthropic, custom]
baseUrl:
type: string
minLength: 1
model:
type: string
minLength: 1
apiToken:
type: string
enabled:
type: boolean
AiProviderUpdate:
type: object
properties:
name:
type: string
minLength: 1
apiType:
type: string
enum: [openai, anthropic, custom]
baseUrl:
type: string
minLength: 1
model:
type: string
minLength: 1
apiToken:
type: string
description: Provide to replace the stored token; omit to keep existing
enabled:
type: boolean
ProviderTestResult:
type: object
required: [ok]
properties:
ok:
type: boolean
message:
type: ["string", "null"]
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
ProviderTestConnectionInput:
type: object
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
required: [apiType, baseUrl]
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
properties:
apiType:
type: string
enum: [openai, anthropic, custom]
baseUrl:
type: string
minLength: 1
model:
type: string
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
description: 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:
type: string
description: Token to use for the test; omit or leave empty to fall back to the stored token of providerId
providerId:
type: integer
description: When apiToken is empty, fall back to this saved provider's stored token
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
ProviderListModelsInput:
type: object
required: [apiType, baseUrl]
properties:
apiType:
type: string
enum: [openai, anthropic, custom]
baseUrl:
type: string
minLength: 1
apiToken:
type: string
description: Token to use for the request; omit or leave empty to fall back to the stored token of providerId
providerId:
type: integer
description: When apiToken is empty, fall back to this saved provider's stored token
ProviderModelsResult:
type: object
required: [ok, models]
properties:
ok:
type: boolean
models:
type: array
items:
type: string
message:
type: ["string", "null"]
Prompt:
type: object
required: [id, key, name, content, updatedAt]
properties:
id:
type: integer
key:
type: string
name:
type: string
content:
type: string
updatedAt:
type: string
PromptUpdate:
type: object
properties:
name:
type: string
minLength: 1
content:
type: string
minLength: 1
Rule:
type: object
required:
- id
- ruleId
- axis
- category
- title
- description
- severity
- detectionType
- enabled
properties:
id:
type: integer
ruleId:
type: string
axis:
type: string
enum: [security, privacy]
category:
type: string
title:
type: string
description:
type: string
severity:
type: string
enum: [critical, high, medium, low, info]
detectionType:
type: string
enum: [regex, heuristic, ai]
enabled:
type: boolean
RuleUpdate:
type: object
properties:
severity:
type: string
enum: [critical, high, medium, low, info]
enabled:
type: boolean
DashboardSummary:
type: object
required:
- totalScans
- avgRiskScore
- verdictCounts
- severityTotals
- axisTotals
- recentScans
- topRules
properties:
totalScans:
type: integer
avgRiskScore:
type: integer
verdictCounts:
$ref: "#/components/schemas/VerdictCounts"
severityTotals:
$ref: "#/components/schemas/SeverityTotals"
axisTotals:
$ref: "#/components/schemas/AxisTotals"
recentScans:
type: array
items:
$ref: "#/components/schemas/Scan"
topRules:
type: array
items:
$ref: "#/components/schemas/RuleStat"
VerdictCounts:
type: object
required: [pass, review, block]
properties:
pass:
type: integer
review:
type: integer
block:
type: integer
SeverityTotals:
type: object
required: [critical, high, medium, low, info]
properties:
critical:
type: integer
high:
type: integer
medium:
type: integer
low:
type: integer
info:
type: integer
AxisTotals:
type: object
required: [security, privacy]
properties:
security:
type: integer
privacy:
type: integer
RuleStat:
type: object
required: [ruleId, title, axis, count]
properties:
ruleId:
type: string
title:
type: string
axis:
type: string
enum: [security, privacy]
count:
type: integer