diff --git a/TEST_PLAN.md b/TEST_PLAN.md new file mode 100644 index 0000000..aa4bd15 --- /dev/null +++ b/TEST_PLAN.md @@ -0,0 +1,1084 @@ +# LeoCRM — Vollständiger Test-Plan + +> **Erstellt:** 2026-08-17 +> **Codebasis:** ~230.000 Zeilen +> **Ziel:** Jede Komponente testen, jeden Fehler finden, nichts überspringen + +--- + +## Was getestet werden muss + +| Schicht | Was | Anzahl | Zeilen | +|---|---|---|---| +| Backend API | Endpoints | 224 | 75.797 | +| Backend Services | Service-Module | 42 | (in app/) | +| Backend Models | SQLAlchemy-Models | 40 | (in app/) | +| Backend Core | Core-Module | 38 | (in app/) | +| Plugins | Built-in Plugins | 25 | (in app/) | +| Migrationen | Alembic-Versionen | 121 | 9.177 | +| Scripts | Python-Scripts | 20 | 4.601 | +| Frontend Seiten | React-Pages | 62 | (in src/) | +| Frontend Komponenten | React-Components | 154 | (in src/) | +| Frontend Stores | Zustand-Stores | 12 | (in src/) | +| Frontend Hooks | Custom Hooks | 11 | (in src/) | +| Frontend API | API-Clients | 48 | (in src/) | +| Existierende Tests | Backend-Tests | 86 | 40.214 | +| Existierende Tests | Frontend-Tests | 79 | 7.369 | +| Existierende Tests | E2E-Tests | 7 | 1.175 | +| Docs | Dokumentation | 18 | 12.484 | +| Config | Docker/Deploy | — | 1.076 | +| **Gesamt** | | **~1.000 Komponenten** | **~230.000** | + +--- + +## Phase 1: Backend API — Alle 224 Endpoints testen + +### 1.1 Endpoint-Discovery +- OpenAPI-Spec parsen (`/openapi.json`) +- Alle Endpoints mit Method, Path, Auth-Requirement extrahieren +- Erwartete Status-Codes pro Endpoint ableiten + +### 1.2 Admin-Test (als is_system_admin=True) +- Login → Session-Cookie holen +- Alle 224 Endpoints aufrufen (GET/POST/PUT/DELETE) +- Für GET: Echte Daten abrufen, 200/404/403 erwartet, **nicht 500** +- Für POST/PUT/DELETE: Mit minimalen Payloads, 201/200/400/403/404 erwartet, **nicht 500** +- Alle 500er = Server-Crash → finden und fixen +- Alle 422er = Schema-Mismatch → finden und fixen + +### 1.3 Non-Admin-Test (als normaler User) +- Neuen User erstellen ohne is_system_admin +- Alle 224 Endpoints aufrufen +- 403 erwartet für Admin-Only Endpoints +- 200 erwartet für User-Endpunkte +- 500er = Permission-Resolver-Crash → finden und fixen + +### 1.4 Unauthenticated-Test (ohne Login) +- Alle 224 Endpoints aufrufen +- 401 erwartet für geschützte Endpoints +- 200 erwartet für öffentliche Endpoints (health, login) +- 500er = Auth-Middleware-Crash → finden und fixen + +### 1.5 Plugin-Endpoint-Test +- Alle Plugin-Routes identifizieren (aus Registry) +- Pro Plugin: Alle Routes aufrufen +- Plugin-spezifische Errors finden + +**Aufwand: ~6h** +**Findet: Alle Backend-Crashes, Import-Fehler, Query-Fehler, Schema-Mismatches** + +--- + +## Phase 2: Frontend — Alle 62 Seiten testen + +### 2.1 Route-Discovery +- Router-Config aus `routes/index.tsx` parsen +- Alle 62 Routes mit Path, Permission-Requirement extrahieren + +### 2.2 Admin-Route-Test (als Admin eingeloggt) +- Login über Browser +- Alle 62 Routes nacheinander laden +- Pro Route prüfen: + - `#root` nicht leer (keine weiße Seite) + - Keine Weiterleitung zu `/kein-zugriff` + - Keine JS-Console-Errors + - Keine unhandled Promise rejections + - API-Calls erfolgreich (keine 500er in Network-Tab) + +### 2.3 Non-Admin-Route-Test +- Als normaler User einloggen +- Alle 62 Routes laden +- Permission-geschützte Routes → Weiterleitung zu `/kein-zugriff` erwartet +- Nicht-geschützte Routes → Seite lädt + +### 2.4 Component-Test +- Alle 154 Komponenten identifizieren +- Pro Komponente: Wird sie auf mindestens einer Route gerendert? +- Komponenten die nirgends gerendert werden = tot oder fehlende Integration + +### 2.5 Store-Test +- Alle 12 Stores prüfen: + - Wird der Store initialisiert? + - Werden die Actions aufgerufen? + - Stimmt der State nach API-Calls? + +### 2.6 Hook-Test +- Alle 11 Hooks prüfen: + - Wird der Hook aufgerufen? + - Macht er die erwarteten API-Calls? + - Verarbeitet er die Response korrekt? + +**Aufwand: ~5h** +**Findet: Alle weißen Seiten, Router-Context-Fehler, fehlende Provider, kaputte Hooks, tote Komponenten** + +--- + +## Phase 3: Plugin-System — Alle 25 Plugins testen + +### 3.1 Plugin-Discovery +- Alle 25 Plugins aus Registry holen +- Pro Plugin: Manifest, Routes, Hooks, Services identifizieren + +### 3.2 Plugin-Manifest-Test +- Pro Plugin: Manifest laden über API +- Prüfen: menu_items, page_routes, settings_pages, detail_tabs vorhanden? +- Frontend: Werden Plugin-Menü-Items in Sidebar angezeigt? + +### 3.3 Plugin-Route-Test +- Pro Plugin: Alle Plugin-Routes laden +- Prüfen: Seite rendert, keine 500er, keine weiße Seite + +### 3.4 Plugin-Hook-Test +- Pro Plugin: Alle registrierten Hooks identifizieren +- Trigger auslösen (z.B. contact.after_create) +- Prüfen: Hook wird ausgeführt, kein Crash + +### 3.5 Plugin-Settings-Test +- Pro Plugin: Settings-Seite laden +- Prüfen: Seite rendert, Settings können gespeichert werden + +### 3.6 Plugin-Activation-Test +- Pro Plugin: Deaktivieren → Aktivieren +- Prüfen: Kein Crash, Hooks korrekt registriert/deregistriert + +**Aufwand: ~4h** +**Findet: Alle Plugin-Verkabelungs-Fehler, fehlende Manifeste, kaputte Hooks** + +--- + +## Phase 4: DB-Schema — Alle 126 Tabellen testen + +### 4.1 Schema-vs-Model-Test +- Alle 40 SQLAlchemy-Models laden +- Pro Model: Tabelle existiert in DB? +- Pro Model: Alle Spalten in DB vorhanden? +- Pro Model: Spalten-Typen stimmen überein? +- Pro Model: FKs vorhanden und korrekt? + +### 4.2 RLS-Test +- Pro Tabelle mit tenant_id: RLS-Policy vorhanden? +- Pro Tabelle: Cross-Tenant-Zugriff blockiert? +- Pro Tabelle: Admin-Bypass funktioniert? + +### 4.3 Migration-Integrität +- Alembic current = head? +- Alle 121 Migration-Hashes gültig? +- Downgrade-Test: Letzte Migration zurücknehmen, wieder anwenden + +### 4.4 Seed-Test +- seed_admin.py ausführen → User erstellt? +- seed_default_workspace() → Workspace + Module erstellt? +- Plugin-Schema-Sync → Alle Plugin-Tabellen vorhanden? + +**Aufwand: ~3h** +**Findet: Alle Schema-Drifts, fehlende Tabellen, RLS-Lücken** + +--- + +## Phase 5: Permission-System — Vollständige Matrix + +### 5.1 Permission-Definitionen +- Alle definierten Permissions aus `permissions.py` holen +- Alle Plugin-Permissions aus Manifesten holen +- Prüfen: Frontend kennt alle Backend-Permissions? + +### 5.2 Permission-Matrix +- Admin (is_system_admin=True): Alle 224 Endpoints → 200/201/204 erwartet +- Non-Admin mit Rolle "admin" (*:* Permission): Alle Endpoints → 200/201/204 +- Non-Admin ohne Rolle: Geschützte Endpoints → 403 +- Guest: Nur Guest-Endpoints → 200, Rest → 403 + +### 5.3 Frontend-Permission-Checks +- Alle PermissionRoute-Komponenten identifizieren +- Pro Route: Mit Admin → Seite lädt +- Pro Route: Ohne Permission → Weiterleitung zu /kein-zugriff + +### 5.4 Field-Level-Permissions +- Pro Entity: Field-Permissions testen +- Admin: Alle Felder sichtbar +- Non-Admin: Nur erlaubte Felder sichtbar + +**Aufwand: ~3h** +**Findet: Alle RBAC-Verkabelungs-Fehler, fehlende Permission-Checks, Field-Level-Bugs** + +--- + +## Phase 6: Existierende Tests — Laufen sie überhaupt? + +### 6.1 Backend-Tests (86 Dateien, 40.214 Zeilen) +- `pytest tests/ -v --tb=short` ausführen +- Alle Failures dokumentieren +- Prüfen: Sind die Tests korrekt oder ist der Code kaputt? +- Mocks/Fixtures prüfen: Werden Permissions weg-gemockt? + +### 6.2 Frontend-Tests (79 Dateien, 7.369 Zeilen) +- `npx vitest run` ausführen +- Alle Failures dokumentieren +- Prüfen: Test-Setup korrekt? + +### 6.3 E2E-Tests (7 Dateien, 1.175 Zeilen) +- `npx playwright test` ausführen +- Alle Failures dokumentieren +- Prüfen: Test-Environment korrekt? + +**Aufwand: ~2h** +**Findet: Welche Tests wirklich laufen, welche kaputt sind, welche wertlos (gemockt)** + +--- + +## Phase 7: Integration — Frontend ↔ Backend Contracts + +### 7.1 API-Response vs Frontend-Erwartung +- Pro Frontend-API-Call: Welche Felder erwartet das Frontend? +- Pro Backend-Endpoint: Welche Felder liefert er? +- Mismatches finden (z.B. is_system_admin fehlt in Login-Response) + +### 7.2 WebSocket-Test +- Comm WebSocket: Connect, Subscribe, Message send/receive +- AI UI Control WebSocket: Connect, Command send/receive + +### 7.3 File-Upload-Test +- DMS: Datei hochladen, herunterladen, löschen +- Mail: Attachment hochladen +- Avatar: Bild hochladen + +### 7.4 Search-Test +- Global Search: Query senden, Ergebnisse prüfen +- Pro Search-Provider: Funktioniert er? + +**Aufwand: ~3h** +**Findet: Alle Contract-Mismatches, WebSocket-Bugs, Upload-Bugs, Search-Bugs** + +--- + +## Phase 8: Deployment & Config + +### 8.1 Docker-Build-Test +- `docker compose build` — funktioniert? +- `docker compose up` — starten alle Services? + +### 8.2 prestart.sh-Test +- Alembic migration: Läuft durch? +- DB role passwords: Werden gesetzt? +- sync_plugin_schema: Läuft? +- seed_admin: Läuft und erstellt User + Workspace? + +### 8.3 Health-Check-Test +- `/api/v1/health` → 200, alle Checks up? +- Worker healthcheck → Redis ping? + +### 8.4 Config-Validation +- Alle ENV-Variablen gesetzt? +- SECRET_KEY nicht default? +- Production-Checks in config.py + +**Aufwand: ~2h** +**Findet: Deploy-Bugs, fehlende ENV-Vars, Seed-Fehler** + +--- + +## Phase 9: Scripts (20 Stück, 4.601 Zeilen) + +### 9.1 Script-Ausführung +- Pro Script: `python3 scripts/.py --help` — läuft? +- Kritische Scripts testen: + - backup.py: Backup erstellen + - restore.py: Restore testen + - sync_plugin_schema.py: Schema sync + - seed_admin.py: Admin seed + - check_migration_hashes.py: Hash validation + - check_indexes.py: Index check + - check_cross_plugin_imports.py: Import check + +**Aufwand: ~1h** +**Findet: Kaputte Scripts, fehlende Dependencies** + +--- + +## Phase 10: Docs-Accuracy (18 Dateien, 12.484 Zeilen) + +### 10.1 API-Docs vs Code +- `docs/api-documentation.md` vs OpenAPI-Spec — stimmen die Endpoints? + +### 10.2 Deploy-Guide vs Realität +- `docs/deploy-guide.md` — stimmen die Befehle? + +### 10.3 Test-Strategy vs Realität +- `docs/test-strategy.md` — wird sie befolgt? + +**Aufwand: ~1h** +**Findet: Veraltete/irreführende Dokumentation** + +--- + +## Zusammenfassung + +| Phase | Was | Aufwand | Findet | +|---|---|---|---| +| 1 | Backend API (224 Endpoints) | 6h | Alle Server-Crashes | +| 2 | Frontend (62 Seiten) | 5h | Alle weißen Seiten | +| 3 | Plugins (25 Stück) | 4h | Alle Plugin-Verkabelungs-Fehler | +| 4 | DB-Schema (126 Tabellen) | 3h | Alle Schema-Drifts | +| 5 | Permissions (Matrix) | 3h | Alle RBAC-Fehler | +| 6 | Existierende Tests (172 Stück) | 2h | Welche Tests wirklich laufen | +| 7 | Integration (Contracts) | 3h | Alle Frontend↔Backend-Mismatches | +| 8 | Deployment & Config | 2h | Alle Deploy-Bugs | +| 9 | Scripts (20 Stück) | 1h | Kaputte Scripts | +| 10 | Docs (18 Dateien) | 1h | Veraltete Docs | +| **Gesamt** | **Alles** | **~30h** | **Vollständige Abdeckung** | + +--- + +## Ausführung + +### Reihenfolge +1. Phase 6 zuerst — wissen welche Tests existieren und laufen +2. Phase 1 — alle API-Crashes finden +3. Phase 2 — alle weißen Seiten finden +4. Phase 4 — DB-Schema verifizieren +5. Phase 5 — Permission-System verifizieren +6. Phase 3 — Plugins testen +7. Phase 7 — Integration testen +8. Phase 8 — Deployment testen +9. Phase 9 — Scripts testen +10. Phase 10 — Docs prüfen + +### Nach jeder Phase +- Alle gefundenen Fehler dokumentieren +- Fehler fixen +- Phase nochmal laufen lassen +- Erst weiter wenn 0 Fehler + +### Automatisierung +- Phase 1: Python-Script das OpenAPI-Spec parst und alle Endpoints testet +- Phase 2: Playwright-Script das alle Routes lädt +- Phase 3: Python-Script das Plugin-Registry ausliest und testet +- Phase 4: Python-Script das Models vs DB-Schema vergleicht +- Phase 5: Python-Script das Permission-Matrix testet +- Alle Scripts in `scripts/test_*.py` speichern +- CI-Pipeline kann sie automatisch ausführen + +--- + +## Ergebnis + +Nach Abschluss dieses Plans: +- Jedes der 224 Endpoints wurde aufgerufen +- Jede der 62 Seiten wurde geladen +- Jedes der 25 Plugins wurde getestet +- Jede der 126 Tabellen wurde verifiziert +- Jede Permission-Kombination wurde getestet +- Jeder existierende Test wurde ausgeführt +- Jeder Frontend↔Backend-Contract wurde geprüft +- Jedes Script wurde ausgeführt +- Jede Doku wurde gegen Code geprüft + +**Das ist nicht läppisch. Das ist vollständige Abdeckung der Basis-Funktionalität.** + +--- + +## Phase 11: Edge Cases & Input Validation + +### 11.1 Leere/Null-Daten +- Alle GET-Listen mit leerer DB → 200, `{items: [], total: 0}` +- Alle POST mit leeren Pflichtfeldern → 422 +- Alle POST mit null-Werten für optionale Felder → 200/201 +- Filter/Search mit leerem Query-String → 200, leere Ergebnisse + +### 11.2 Ungültige Inputs +- Ungültige UUIDs → 422 (nicht 500) +- Ungültige Datumsformate → 422 +- Ungültige Email-Format → 422 +- SQL-Sonderzeichen in Search (`'; DROP TABLE--`) → 200, keine Injection +- Unicode/Emoji in allen Text-Feldern → 200/201 +- Sehr lange Strings (>10.000 Zeichen) → 200/201 oder 422 mit klarer Meldung +- Negative Zahlen für IDs/Counts → 422 + +### 11.3 Große Datenmengen +- 10.000 Kontakte erstellen → Performance <5s +- Paginierung: page=1&page_size=1 vs page=10000&page_size=100 → korrekte Ergebnisse +- Bulk-Import mit 5.000 Zeilen → Erfolg oder klarer Fehler +- Datei-Upload 50MB (Limit) → Erfolg +- Datei-Upload 51MB → 413 + +### 11.4 Pydantic-Schema-Validation +- Alle Schemas mit falschen Typen testen (String für Integer-Feld, etc.) +- Alle Schemas mit fehlenden Pflichtfeldern +- Alle Schemas mit zusätzlichen unbekannten Feldern (sollten ignoriert oder 422) +- Nested Objects mit falschen Strukturen + +**Aufwand: ~4h** +**Findet: Alle Input-Validation-Lücken, SQL-Injection-Vektoren, Performance-Bottlenecks** + +--- + +## Phase 12: Security & Penetration + +### 12.1 SQL-Injection +- Alle GET-Parameter mit SQL-Injection-Patterns testen +- Alle POST/PUT-Body-Felder mit SQL-Injection-Patterns +- Search-Queries mit Injection-Patterns +- Raw-SQL-Queries im Code identifizieren und prüfen + +### 12.2 XSS +- Alle Text-Eingabefelder mit `` testen +- Prüfen ob Output escaped wird (Frontend + Backend) +- Markdown-Rendering mit XSS-Payloads +- HTML-E-Mail-Anzeige mit XSS-Payloads +- DOMPurify funktioniert in allen Render-Pfaden? + +### 12.3 CSRF +- Alle POST/PUT/DELETE ohne CSRF-Token → 403 +- Alle POST/PUT/DELETE mit falschem CSRF-Token → 403 +- CSRF-Token-Rotation nach Login + +### 12.4 Path-Traversal +- File-Download mit `../../../etc/passwd` → 404/403 +- DMS-Dateipfade mit Traversal-Patterns +- Attachment-Pfade mit Traversal + +### 12.5 RLS-Bypass +- User A kann nicht auf Tenant B Daten zugreifen +- User A kann nicht auf User B's private Daten zugreifen +- Admin-Bypass funktioniert nur mit is_system_admin=True +- RLS-Policies für alle 126 Tabellen mit tenant_id prüfen + +### 12.6 Session-Security +- Session-Cookie: HttpOnly, Secure, SameSite +- Session-Fixation: Session-ID ändert sich nach Login +- Session-Expiry nach TTL +- Concurrent-Session-Limit (falls konfiguriert) +- Logout invalidiert Session in Redis + +### 12.7 File-Upload-Security +- MIME-Type-Validation: `.py` als `image/jpeg` → abgelehnt +- File-Size-Limits enforced +- Filename-Sanitization (keine `../`, keine Null-Bytes) +- Virus-Scan (falls konfiguriert) + +### 12.8 API-Token-Security +- Token-Erstellung, -Validierung, -Revocation +- Token-Scopes enforced +- Token-Expiry + +**Aufwand: ~6h** +**Findet: Alle Security-Lücken, Injection-Vektoren, Auth-Bypasses** + +--- + +## Phase 13: Data Integrity & Audit + +### 13.1 Soft-Delete +- Alle Entities: DELETE → `deleted_at` gesetzt, nicht aus DB entfernt +- Alle Listen: Gelöschte Entities nicht sichtbar +- `?gdpr=true` → Hard-Delete funktioniert +- Restore aus Papierkorb funktioniert + +### 13.2 Audit-Log +- Pro Mutation (Create/Update/Delete): Audit-Log-Eintrag erstellt? +- Audit-Log enthält: user_id, action, entity_type, entity_id, changes +- Audit-Log ist immutable (kein Update/Delete möglich) + +### 13.3 Entity-History +- Pro Update: History-Eintrag mit diff erstellt? +- Undo funktioniert (Restore einer vorherigen Version) +- History zeigt korrekte Feld-Änderungen + +### 13.4 FK-Constraints +- Contact löschen → Person wird gelöscht/NULL gesetzt (je nach Config) +- Tenant löschen → Alle Tenant-Daten gelöscht (Cascade) +- Role löschen → UserTenant.role_id wird NULL gesetzt + +### 13.5 Outbox-Reliability +- Mutation → Outbox-Eintrag erstellt +- Worker verarbeitet Outbox-Eintrag +- Bei Fehler: Retry-Logic funktioniert +- Dead-Letter-Queue für fehlgeschlagene Events + +**Aufwand: ~3h** +**Findet: Alle Data-Integrity-Verletzungen, fehlende Audit-Logs, kaputte FKs** + +--- + +## Phase 14: Concurrent & Race Conditions + +### 14.1 Gleichzeitige Edits +- Zwei User editieren gleichen Kontakt gleichzeitig → Optimistic Locking oder Last-Write-Wins +- Zwei User erstellen Kontakt mit gleicher Email → Unique-Constraint oder Dedup + +### 14.2 WebSocket bei mehreren Clients +- Zwei Browser-Tabs gleicher User → Messages synchron? +- Two Users in gleicher Conversation → Real-time Updates? +- WebSocket Reconnect nach Verbindungsabbruch + +### 14.3 Worker-Concurrency +- Mehrere Worker-Instanzen → Cron-Jobs nicht doppelt ausgeführt (Redis-Lock) +- Gleichzeitige Background-Jobs → Keine Deadlocks + +### 14.4 Session-Concurrency +- Login von zwei Geräten → Beide Sessions gültig? +- Logout auf Gerät A → Gerät B noch eingeloggt? + +**Aufwand: ~3h** +**Findet: Alle Race Conditions, Deadlock-Gefahren, WebSocket-Sync-Issues** + +--- + +## Phase 15: Error Recovery & Resilience + +### 15.1 DB-Verbindungsabbruch +- DB kurzzeitig nicht erreichbar → API gibt 503, nicht 500 +- DB wieder da → API erholt sich automatisch +- Circuit-Breaker öffnet/schließt korrekt + +### 15.2 Redis-Ausfall +- Redis nicht erreichbar → Sessions funktionieren nicht (klare Fehlermeldung) +- Redis wieder da → Alles normal +- Worker: Redis-Ausfall → Retry, nicht Crash + +### 15.3 Worker-Crash +- Worker stirbt mittendrin → Job wird retryt +- Job schlägt 3x fehl → Dead-Letter oder klarer Fehler + +### 15.4 LLM-Timeout/Error +- LLM-Provider nicht erreichbar → Timeout nach konfigurierten Sekunden +- LLM gibt Fehler → KI-Chat zeigt Fehlermeldung, nicht weiße Seite +- Cost-Tracking funktioniert auch bei Fehlern + +### 15.5 External Service-Ausfall +- SMTP nicht erreichbar → Password-Reset-Email schlägt fehl mit klarer Meldung +- IMAP nicht erreichbar → Mail-Sync schlägt fehl, retryt später +- Forgejo/GitHub Webhook-Empfänger nicht erreichbar → Retry-Logic + +**Aufwand: ~3h** +**Findet: Alle unhandled Errors, fehlende Circuit-Breaker, Crash-bei-Ausfall-Szenarien** + +--- + +## Phase 16: Performance & Scalability + +### 16.1 Response-Zeiten +- Alle 224 Endpoints: Response-Zeit <200ms (mit DB-Daten) +- Listen-Endpoints mit 1.000 Einträgen: <500ms +- Search-Queries: <1s + +### 16.2 N+1 Queries +- Kontakte-Liste lädt nicht pro Kontakt eine separate Query für Companies/Persons +- Plugin-Manifeste: Nicht pro Plugin eine separate Query +- Audit-Log: Nicht pro Eintrag eine separate User-Query + +### 16.3 Memory-Leaks +- Längerer Last-Test (100 Requests/min für 10min) → Memory stabil? +- Frontend: Seitenwechsel 50x → Memory stabil? + +### 16.4 DB-Query-Performance +- EXPLAIN ANALYZE für kritische Queries (Contacts, Mails, Files, Search) +- Fehlende Indexe identifizieren +- Slow-Query-Log aktivieren und auswerten + +### 16.5 Frontend-Bundle-Size +- Bundle-Analyse: Chunks >500kB identifizieren +- Lazy-Loading funktioniert für alle 62 Seiten +- Code-Splitting effektiv? + +**Aufwand: ~3h** +**Findet: Alle Performance-Bottlenecks, N+1 Queries, Memory-Leaks, fehlende Indexe** + +--- + +## Phase 17: Cross-Plugin & Plugin-Lifecycle + +### 17.1 Plugin-Dependencies +- Plugin A deaktivieren während Plugin B von A abhängt → Fehlermeldung +- Plugin A aktivieren bevor Dependency B aktiv ist → Fehlermeldung +- Dependency-Graph korrekt? + +### 17.2 Hook-Ketten +- Contact erstellen → ContactsPlugin Hook → HistoryPlugin Hook → SearchPlugin Hook → AIPlugin Hook +- Alle Hooks werden in korrekter Reihenfolge ausgeführt +- Ein Hook crasht → Andere Hooks laufen trotzdem + +### 17.3 Plugin-Activation/Deactivation +- Alle 25 Plugins: Aktivieren → Hooks registriert, Tabellen erstellt +- Alle 25 Plugins: Deaktivieren → Hooks deregistriert, Tabellen bleiben +- Re-Aktivieren → Hooks wieder registriert, keine Duplikate + +### 17.4 Plugin-Isolation +- Plugin A kann nicht Plugin B's Hooks löschen (clear_actions Bug war P0-3) +- Plugin A kann nicht auf Plugin B's private Tabellen zugreifen + +**Aufwand: ~3h** +**Findet: Alle Plugin-Dependency-Issues, Hook-Ketten-Fehler, Isolation-Verletzungen** + +--- + +## Phase 18: State Management & Frontend Consistency + +### 18.1 Store-Consistency nach API-Fehlern +- API gibt 500 → Store bleibt konsistent (kein halber State) +- API gibt 403 → UI zeigt korrekte Fehlermeldung +- Network-Error → UI zeigt Offline-Banner, Store bleibt konsistent + +### 18.2 State nach Page-Refresh +- Alle 12 Stores: Persistierter State korrekt geladen? +- authStore: User, isAuthenticated, is_system_admin nach Refresh korrekt? +- workspaceStore: Active Workspace nach Refresh korrekt? +- themeStore: Theme nach Refresh korrekt? + +### 18.3 TanStack Query Cache +- Cache invalidation nach Mutations (Create/Update/Delete) +- staleTime korrekt konfiguriert? +- refetchOnWindowFocus disabled (konfiguriert) +- Query-Keys eindeutig? + +### 18.4 Optimistic Updates +- Contact-Edit: UI aktualisiert sofort, rollback bei API-Fehler +- Mail-Send: UI zeigt sofort, rollback bei Fehler + +**Aufwand: ~2h** +**Findet: Alle State-Inconsistencies, Cache-Bugs, fehlende Rollbacks** + +--- + +## Phase 19: Accessibility (a11y) + +### 19.1 ARIA-Attributes +- Alle interaktiven Elemente haben aria-label +- Alle Buttons haben aria-label oder sichtbaren Text +- Alle Form-Inputs haben label +- Navigation hat aria-label + +### 19.2 Keyboard-Navigation +- Tab-Reihenfolge logisch (top-to-bottom, left-to-right) +- Enter auf Button löst Klick aus +- Escape schließt Modals/Dialogs +- Tab-Trapping in Modals + +### 19.3 Touch-Targets +- Alle interaktiven Elemente ≥44px (min-h-touch class) +- Mobile: Keine zu kleinen Buttons + +### 19.4 Screen-Reader +- Skip-to-content Link funktioniert +- Heading-Hierarchie korrekt (h1 → h2 → h3) +- Status-Messages haben role=status oder aria-live + +### 19.5 Color-Contrast +- Alle Text/Background-Kombinationen ≥4.5:1 (WCAG AA) +- Dark-Mode: Contrast korrekt? + +**Aufwand: ~2h** +**Findet: Alle a11y-Verstöße, fehlende ARIA, Keyboard-Traps** + +--- + +## Phase 20: i18n (Internationalisierung) + +### 20.1 Translation-Completeness +- Deutsch: Alle t()-Keys vorhanden? Keine hardcoded Strings? +- Englisch: Alle t()-Keys vorhanden? Keine hardcoded Strings? +- Fallback: Fehlender Key → Fallback auf Deutsch? + +### 20.2 Dynamic-Values +- Interpolation: `{{count}}`, `{{name}}` funktionieren in allen Sprachen +- Plural-Forms korrekt? + +### 20.3 Date/Number-Formatting +- Datumsformat korrekt pro Locale (de: dd.MM.yyyy, en: MM/dd/yyyy) +- Zahlenformat korrekt (de: 1.234,56, en: 1,234.56) + +**Aufwand: ~1h** +**Findet: Fehlende Übersetzungen, hardcoded Strings, Formatierungs-Fehler** + +--- + +## Phase 21: Backup & Restore + +### 21.1 Full-Backup +- `scripts/backup.py` → pg_dump + files → Backup erstellt +- Backup-Datei gültig (kann restored werden) +- Backup-Retention funktioniert + +### 21.2 Full-Restore +- `scripts/restore.py` → DB + Files restored +- Alle Daten nach Restore korrekt +- RLS-Policies nach Restore intakt + +### 21.3 Partial-Restore +- Nur DB restore → funktioniert +- Nur Files restore → funktioniert +- Restore auf andere DB → funktioniert + +### 21.4 Backup-Schedule +- Cron-Job für regelmäßige Backups konfiguriert? +- Backup-Fehler → Notification + +**Aufwand: ~2h** +**Findet: Backup/Restore-Bugs, fehlende Schedules** + +--- + +## Phase 22: Migration Safety + +### 22.1 Rollback-Test +- Letzte Migration zurücknehmen (`alembic downgrade -1`) +- Wieder anwenden (`alembic upgrade head`) +- Daten nach Roundtrip intakt? + +### 22.2 Hash-Validierung +- `scripts/check_migration_hashes.py` → alle Hashes gültig? +- Manuell geänderte Migrationen erkannt? + +### 22.3 Fehlgeschlagene Migration +- Migration simuliert fehlschlagen → DB konsistent? +- Recovery: `alembic stamp` vs `alembic upgrade` + +### 22.4 Migration-Order +- Alle 121 Migrationen in korrekter Reihenfolge +- Keine zirkulären Dependencies +- Down-Revisions korrekt + +**Aufwand: ~2h** +**Findet: Migration-Order-Fehler, kaputte Rollbacks, Hash-Mismatches** + +--- + +## Phase 23: Rate Limiting & Circuit Breaker + +### 23.1 Rate-Limiting +- Login: 5 Versuche → Rate-Limit aktiviert +- API: 100 Requests/min → Rate-Limit aktiviert +- Rate-Limit-Reset nach Window + +### 23.2 Circuit-Breaker +- DB: 5 Fehler in 30s → Circuit öffnet +- Circuit offen → API gibt 503, nicht 500 +- Nach Cooldown → Circuit schließt, Requests gehen durch + +### 23.3 Retry-Logic +- DB-Retry: 3 Versuche mit Backoff +- LLM-Retry: Konfigurierte Versuche +- Worker-Retry: Failed-Job wird retryt + +**Aufwand: ~1h** +**Findet: Fehlende Rate-Limits, kaputte Circuit-Breaker, fehlende Retries** + +--- + +## Phase 24: Webhooks & External Integration + +### 24.1 Webhook-Delivery +- Webhook registriert → Event ausgelöst → Delivery an externe URL +- Externe URL nicht erreichbar → Retry-Logic +- Webhook-Signatur korrekt + +### 24.2 Forgejo/GitHub-Integration +- Forgejo-Error-Reporter: Fehler werden gemeldet +- GitHub-Actions: CI-Pipeline funktioniert + +### 24.3 MCP-Server/Client +- MCP-Server: Tools verfügbar, ausführbar +- MCP-Client: Server-Config, Connection, Tool-Call + +**Aufwand: ~2h** +**Findet: Webhook-Delivery-Fehler, fehlende Retries, kaputte Integrationen** + +--- + +## Phase 25: Search & AI + +### 25.1 Unified-Search +- Global-Search: Query → Ergebnisse aus allen Providern +- Pro Provider (13 Stück): Funktioniert er? +- Reindexing: `reindex_all` → alle Entities neu indiziert +- pgvector: Embeddings korrekt, HNSW-Index funktioniert + +### 25.2 AI-Assistant +- Chat-Session erstellen → Message senden → Antwort erhalten +- Streaming: Token-weise Ausgabe funktioniert +- Cost-Tracking: Kosten pro Request erfasst +- Error-Handling: LLM-Fehler → klare Meldung + +### 25.3 AI-Proactive +- Context-Tracking: Seitenwechsel → Context aktualisiert +- Suggestion-Generation: Vorschläge basierend auf Context + +### 25.4 AI-UI-Control +- WebSocket: Command senden → UI führt Aktion aus +- Navigation, Filter, Modal, Tab, Settings Commands + +### 25.5 Automation & Agents +- Automation erstellen → Trigger auslösen → Action ausführen +- Agent erstellen → Execute → Subtasks → Result +- Dry-Run: Automation ohne echte Ausführung +- Cron-Jobs: Scheduler-Tick funktioniert + +**Aufwand: ~3h** +**Findet: Alle Search/AI/Automation-Verkabelungs-Fehler** + +--- + +## Phase 26: Communication & Collaboration + +### 26.1 Comm-WebSocket +- Connect → Subscribe → Message senden → Message empfangen +- Typing-Indicators +- Read-Receipts +- Message-Blocks (Markdown, HTML, Audio, Video, File, Action-Card, Contact-Card, MiniApp) +- Reconnect nach Verbindungsabbruch + +### 26.2 Conversations +- Create, List, Archive, Pin, Mute +- Direct-Chat zwischen Usern +- System-Channel (Notifications) +- AI-Channel (Assistant) + +### 26.3 Message-Editing +- Message editieren → Edit-History gespeichert +- Message löschen → Soft-Delete +- Reactions auf Messages + +**Aufwand: ~2h** +**Findet: Alle Comm-Verkabelungs-Fehler, WebSocket-Bugs, Message-Rendering-Issues** + +--- + +## Phase 27: Workspace & Tenant + +### 27.1 Workspace-Operations +- Workspace erstellen, bearbeiten, löschen +- Module zuweisen, Sichtbarkeit togglen +- User zuweisen, Rollen vergeben +- Workspace-Switching: Context ändert sich korrekt + +### 27.2 Tenant-Operations +- Tenant erstellen (Multi-Tenant) +- User zu Tenant einladen +- Tenant-Switching: Daten isoliert +- Cross-Tenant-Access: Blockiert durch RLS + +### 27.3 Workspace-Module-Visibility +- Pro Module: Sichtbar in Sidebar? +- Module ohne Permission → ausgeblendet +- Admin sieht alle Module + +**Aufwand: ~2h** +**Findet: Alle Workspace/Tenant-Isolation-Fehler** + +--- + +## Phase 28: Browser Compatibility + +### 28.1 Chrome (aktuell) +- Alle 62 Seiten laden +- Alle Features funktionieren + +### 28.2 Firefox (aktuell) +- Alle 62 Seiten laden +- CSS-Rendering korrekt + +### 28.3 Safari (aktuell) +- Alle 62 Seiten laden +- Datei-Upload funktioniert +- WebSocket funktioniert + +### 28.4 Mobile (Chrome Android / Safari iOS) +- Responsive-Layout korrekt +- Touch-Targets ≥44px +- Mobile-Sidebar (Off-Canvas) funktioniert +- Virtual Keyboard verdeckt nicht Inputs + +**Aufwand: ~2h** +**Findet: Browser-spezifische Bugs, Mobile-Issues** + +--- + +## Phase 29: File Storage & DMS + +### 29.1 File-Operations +- Upload (einzelne Datei, mehrere Dateien, Drag-Drop) +- Download (einzelne Datei, Bulk-Download) +- Rename, Move, Copy +- Delete (Soft-Delete, Hard-Delete mit GDPR) +- Restore aus Papierkorb + +### 29.2 Folder-Operations +- Create, Rename, Move, Delete +- Nested-Folders (5+ Ebenen) +- Folder-Permissions (User, Group) + +### 29.3 Share-Links +- Share-Link erstellen mit Password +- Share-Link mit Expiry-Date +- Public-Access über Share-Link +- Share-Link revoken + +### 29.4 Storage-Limits +- Max-File-Size enforced +- Storage-Quota (falls konfiguriert) +- Cleanup verwaister Dateien + +**Aufwand: ~2h** +**Findet: Alle DMS/File-Storage-Bugs** + +--- + +## Phase 30: Mail System + +### 30.1 Mail-Accounts +- IMAP-Account hinzufügen, testen, synchronisieren +- SMTP-Account konfigurieren +- Shared vs Personal Accounts +- Account löschen → Mails bleiben oder werden gelöscht + +### 30.2 Mail-Operations +- Mail empfangen, lesen, als gelesen/ungelesen markieren +- Mail senden (mit/ohne Attachments) +- Reply, Reply-All, Forward +- Flag, Move, Delete, Restore +- Search in Mails + +### 30.3 Mail-Rules +- Rule erstellen (Condition → Action) +- Rule ausführen auf neue Mails +- Rule löschen + +### 30.4 Mail-Labels +- Label erstellen, zuweisen, entfernen +- Filter nach Labels + +### 30.5 Mail-Signatures +- Signature erstellen, als Default setzen +- Signature in neuer Mail eingefügt + +### 30.6 Vacation-Responder +- Aktivieren, konfigurieren, deaktivieren +- Auto-Response wird gesendet + +### 30.7 PGP-Verschlüsselung +- Key importieren +- Mail verschlüsseln/entschlüsseln +- Contact-PGP-Key speichern + +**Aufwand: ~3h** +**Findet: Alle Mail-System-Bugs** + +--- + +## Vollständige Zusammenfassung (alle 30 Phasen) + +| Phase | Was | Aufwand | +|---|---|---| +| 1 | Backend API (224 Endpoints) | 6h | +| 2 | Frontend (62 Seiten, 154 Komponenten, 12 Stores, 11 Hooks) | 5h | +| 3 | Plugins (25 Stück) | 4h | +| 4 | DB-Schema (126 Tabellen, 40 Models) | 3h | +| 5 | Permissions (Matrix, Field-Level) | 3h | +| 6 | Existierende Tests (172 Stück) | 2h | +| 7 | Integration (Contracts, WebSocket, Upload, Search) | 3h | +| 8 | Deployment & Config | 2h | +| 9 | Scripts (20 Stück) | 1h | +| 10 | Docs (18 Dateien) | 1h | +| 11 | Edge Cases & Input Validation | 4h | +| 12 | Security & Penetration | 6h | +| 13 | Data Integrity & Audit | 3h | +| 14 | Concurrent & Race Conditions | 3h | +| 15 | Error Recovery & Resilience | 3h | +| 16 | Performance & Scalability | 3h | +| 17 | Cross-Plugin & Lifecycle | 3h | +| 18 | State Management & Consistency | 2h | +| 19 | Accessibility | 2h | +| 20 | i18n | 1h | +| 21 | Backup & Restore | 2h | +| 22 | Migration Safety | 2h | +| 23 | Rate Limiting & Circuit Breaker | 1h | +| 24 | Webhooks & External Integration | 2h | +| 25 | Search & AI & Automation | 3h | +| 26 | Communication & Collaboration | 2h | +| 27 | Workspace & Tenant | 2h | +| 28 | Browser Compatibility | 2h | +| 29 | File Storage & DMS | 2h | +| 30 | Mail System | 3h | +| **Gesamt** | **Vollständige Abdeckung** | **~80h** | + +--- + +## Ausführung + +### Reihenfolge (angepasst) +1. Phase 6 — Existierende Tests ausführen +2. Phase 1 — Alle 224 API-Endpoints +3. Phase 2 — Alle 62 Frontend-Seiten +4. Phase 4 — DB-Schema verifizieren +5. Phase 5 — Permission-Matrix +6. Phase 12 — Security (früh, weil kritisch) +7. Phase 3 — Plugins testen +8. Phase 7 — Integration +9. Phase 11 — Edge Cases +10. Phase 13 — Data Integrity +11. Phase 15 — Error Recovery +12. Phase 16 — Performance +13. Phase 17 — Cross-Plugin +14. Phase 18 — State Management +15. Phase 14 — Concurrent +16. Phase 8 — Deployment +17. Phase 21 — Backup/Restore +18. Phase 22 — Migration Safety +19. Phase 23 — Rate Limiting +20. Phase 24 — Webhooks +21. Phase 25 — Search & AI +22. Phase 26 — Communication +23. Phase 27 — Workspace & Tenant +24. Phase 29 — File Storage & DMS +25. Phase 30 — Mail System +26. Phase 19 — Accessibility +27. Phase 20 — i18n +28. Phase 28 — Browser Compatibility +29. Phase 9 — Scripts +30. Phase 10 — Docs + +### Nach jeder Phase +- Alle gefundenen Fehler in `docs/test-results/phase-XX.md` dokumentieren +- Fehler fixen +- Phase nochmal laufen lassen +- Erst weiter wenn 0 Fehler + +### Automatisierung +- Phase 1: `scripts/test_all_endpoints.py` — OpenAPI-Spec parsen, alle Endpoints testen +- Phase 2: `scripts/test_all_routes.py` — Playwright, alle Routes laden +- Phase 3: `scripts/test_all_plugins.py` — Plugin-Registry, Manifeste, Routes +- Phase 4: `scripts/test_schema_vs_models.py` — Models vs DB-Schema +- Phase 5: `scripts/test_permission_matrix.py` — Permission-Matrix +- Phase 11: `scripts/test_edge_cases.py` — Edge-Case-Payloads +- Phase 12: `scripts/test_security.py` — Security-Scans +- Phase 16: `scripts/test_performance.py` — Performance-Benchmarks +- Alle Scripts in `scripts/test_*.py` speichern +- CI-Pipeline kann sie automatisch ausführen + +--- + +## Ergebnis nach Abschluss + +- Jedes der 224 Endpoints wurde mit gültigen/ungültigen/leeren/bösartigen Inputs getestet +- Jede der 62 Seiten wurde in Chrome geladen und auf JS-Errors geprüft +- Jedes der 25 Plugins wurde aktiviert/deaktiviert und getestet +- Jede der 126 Tabellen wurde gegen Models validiert +- Jede Permission-Kombination (Admin/Non-Admin/Guest/Unauth) wurde getestet +- Jeder der 172 existierenden Tests wurde ausgeführt +- Jeder Frontend↔Backend-Contract wurde geprüft +- Jedes Script wurde ausgeführt +- Jede Doku wurde gegen Code geprüft +- Security: SQL-Injection, XSS, CSRF, Path-Traversal, RLS-Bypass, Session getestet +- Performance: Response-Zeiten, N+1 Queries, Memory-Leaks getestet +- Resilience: DB/Redis/Worker/LLM-Ausfall getestet +- Accessibility: ARIA, Keyboard, Touch-Targets, Contrast getestet +- i18n: Alle Übersetzungen, alle Sprachen getestet +- Backup/Restore: Vollständiger Cycle getestet +- Migration: Rollback, Hash-Validierung getestet +- Browser: Chrome, Firefox, Safari, Mobile getestet +- Mail: Kompletter Mail-Cycle getestet +- DMS: Kompletter File-Cycle getestet +- Communication: WebSocket, Messages, Blocks getestet +- AI: Chat, Streaming, Cost-Tracking, Automation, Agents getestet + +**30 Phasen. ~80 Stunden. Vollständige Abdeckung. Nichts übersprungen.** diff --git a/app/plugins/builtins/automation/agent_comm.py b/app/plugins/builtins/automation/agent_comm.py index da02976..fdd6722 100644 --- a/app/plugins/builtins/automation/agent_comm.py +++ b/app/plugins/builtins/automation/agent_comm.py @@ -55,7 +55,7 @@ async def send_agent_message( # 2. Create a kommunikation message in a dedicated agent room try: - from app.plugins.builtins.kommunikation.contracts import Message, Room + from app.plugins.builtins.kommunikation.contracts import CommConversation as Room, CommMessage as Message # Find or create the agent-to-agent room room_name = f"agent:{from_agent_id}:{target_agent.id}" diff --git a/app/plugins/builtins/automation/agent_runner.py b/app/plugins/builtins/automation/agent_runner.py index 9798fa2..06d5b52 100644 --- a/app/plugins/builtins/automation/agent_runner.py +++ b/app/plugins/builtins/automation/agent_runner.py @@ -112,8 +112,15 @@ async def run_agent( logger.warning("Failed to collect contacts for proactive context") try: - from app.core.cache import get_cached_mail_summary - context_data["recent_mails"] = get_cached_mail_summary(agent.tenant_id) or [] + from app.services.mail_service import MailService + mail_svc = MailService(db) + recent_mails = await mail_svc.list_mails( + tenant_id=agent.tenant_id, + user_id=None, + page=1, + page_size=5, + ) + context_data["recent_mails"] = recent_mails.get("items", []) except Exception: logger.warning("Failed to collect mails for proactive context") diff --git a/app/routes/entity_permissions.py b/app/routes/entity_permissions.py index 60b0f34..9ddcfe9 100644 --- a/app/routes/entity_permissions.py +++ b/app/routes/entity_permissions.py @@ -102,7 +102,7 @@ async def _check_entity_ownership( from sqlalchemy import select - from app.models.entity_permission import ENTITY_MODELS + from app.services.entity_permission_service import ENTITY_MODELS model_info = ENTITY_MODELS.get(entity_type) if model_info is None: diff --git a/scripts/test_suite/marathon.py b/scripts/test_suite/marathon.py new file mode 100644 index 0000000..f7bdc81 --- /dev/null +++ b/scripts/test_suite/marathon.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +"""Test Marathon — Runs all trace scripts and test scripts, combines results. + +Usage: python3 scripts/test_suite/marathon.py +""" +from __future__ import annotations + +import json +import subprocess +import sys +import time +from datetime import datetime +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent.parent +SUITE_DIR = Path(__file__).resolve().parent + +# All trace scripts (static analysis — find verkabelungsfehler) +TRACE_SCRIPTS = [ + ("trace_api_contracts", "Frontend↔Backend API Contracts"), + ("trace_hooks", "Hook Registrations vs Triggers"), + ("trace_functions", "Dead Functions (defined but never called)"), + ("trace_stores", "Unused Store Actions/State"), + ("trace_contracts", "Contract Attribute Mismatches"), + ("trace_plugins", "Plugin→Manifest→Frontend Verkabelung"), + ("trace_imports", "Broken/Missing Imports"), +] + +# All test scripts (runtime — find crashes and errors) +# These will be added as they are built +TEST_SCRIPTS = [ + # ("test_all_endpoints", "All 224 API Endpoints"), + # ("test_all_routes", "All 62 Frontend Routes"), + # ("test_schema_vs_models", "DB Schema vs Models"), + # ("test_permission_matrix", "Permission Matrix"), +] + + +def run_script(name: str, description: str) -> dict: + """Run a single script and capture results.""" + script_path = SUITE_DIR / f"{name}.py" + if not script_path.exists(): + return {"name": name, "description": description, "status": "SKIP", "reason": "Script not found", "issues": 0} + + print(f"\n{'─' * 70}") + print(f"RUNNING: {name} — {description}") + print(f"{'─' * 70}") + + start = time.time() + try: + result = subprocess.run( + [sys.executable, str(script_path)], + capture_output=True, + text=True, + timeout=300, + cwd=str(ROOT), + ) + elapsed = time.time() - start + + # Print stdout + if result.stdout: + print(result.stdout) + if result.stderr: + print(f"STDERR: {result.stderr[:500]}") + + # Try to read results JSON + results_file = SUITE_DIR / f"results_{name}.json" + issues_count = 0 + if results_file.exists(): + try: + with open(results_file) as f: + data = json.load(f) + if "summary" in data: + issues_count = data["summary"].get("total_issues", 0) + elif "issues" in data: + issues_count = len(data["issues"]) + elif "mismatches" in data: + issues_count = len(data["mismatches"]) + elif "broken" in data: + issues_count = len(data["broken"]) + elif "critical_dead" in data: + issues_count = len(data["critical_dead"]) + elif "orphan_registrations" in data: + issues_count = len(data.get("orphan_registrations", [])) + len(data.get("orphan_triggers", [])) + elif "unused" in data: + issues_count = sum(len(v) for v in data["unused"].values()) if isinstance(data["unused"], dict) else 0 + except Exception: + pass + + return { + "name": name, + "description": description, + "status": "PASS" if result.returncode == 0 else "FAIL", + "exit_code": result.returncode, + "elapsed": round(elapsed, 2), + "issues": issues_count, + } + except subprocess.TimeoutExpired: + elapsed = time.time() - start + return {"name": name, "description": description, "status": "TIMEOUT", "elapsed": round(elapsed, 2), "issues": 0} + except Exception as e: + elapsed = time.time() - start + return {"name": name, "description": description, "status": "ERROR", "error": str(e), "elapsed": round(elapsed, 2), "issues": 0} + + +def main(): + print("╔" + "═" * 70 + "╗") + print("║" + " LEOCRM TEST MARATHON — Vollständige System-Audit".center(70) + "║") + print("║" + f" Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}".center(70) + "║") + print("╚" + "═" * 70 + "╝") + + all_results = [] + total_start = time.time() + + # Phase 1: Trace Scripts (static analysis) + print("\n" + "=" * 70) + print("PHASE 1: TRACE SCRIPTS — Statische Verkabelungs-Analyse") + print("=" * 70) + + for name, desc in TRACE_SCRIPTS: + result = run_script(name, desc) + all_results.append(result) + + # Phase 2: Test Scripts (runtime) + if TEST_SCRIPTS: + print("\n" + "=" * 70) + print("PHASE 2: TEST SCRIPTS — Runtime Tests") + print("=" * 70) + + for name, desc in TEST_SCRIPTS: + result = run_script(name, desc) + all_results.append(result) + + # Summary + total_elapsed = time.time() - total_start + print("\n" + "╔" + "═" * 70 + "╗") + print("║" + " MARATHON SUMMARY".center(70) + "║") + print("╠" + "═" * 70 + "╣") + + total_issues = 0 + passed = 0 + failed = 0 + skipped = 0 + + for r in all_results: + status_icon = {"PASS": "✅", "FAIL": "❌", "SKIP": "⏭️", "TIMEOUT": "⏰", "ERROR": "💥"}.get(r["status"], "?") + issues = r.get("issues", 0) + total_issues += issues + if r["status"] == "PASS": + passed += 1 + elif r["status"] in ("FAIL", "ERROR", "TIMEOUT"): + failed += 1 + else: + skipped += 1 + elapsed = r.get("elapsed", 0) + line = f" {status_icon} {r['name']:<30} {issues:>5} issues {elapsed:>6}s" + print("║" + line.ljust(70) + "║") + + print("╠" + "═" * 70 + "╣") + summary_line = f" Total: {len(all_results)} scripts | ✅ {passed} passed | ❌ {failed} failed | ⏭️ {skipped} skipped" + print("║" + summary_line.ljust(70) + "║") + issues_line = f" Total issues found: {total_issues}" + print("║" + issues_line.ljust(70) + "║") + time_line = f" Total time: {round(total_elapsed, 2)}s" + print("║" + time_line.ljust(70) + "║") + print("╚" + "═" * 70 + "╝") + + # Write combined results + combined_file = SUITE_DIR / "marathon_results.json" + with open(combined_file, "w") as f: + json.dump({ + "timestamp": datetime.now().isoformat(), + "total_time": round(total_elapsed, 2), + "total_scripts": len(all_results), + "total_issues": total_issues, + "passed": passed, + "failed": failed, + "skipped": skipped, + "results": all_results, + }, f, indent=2) + print(f"\nCombined results: {combined_file}") + + return total_issues + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/test_suite/marathon_results.json b/scripts/test_suite/marathon_results.json new file mode 100644 index 0000000..01ffc8f --- /dev/null +++ b/scripts/test_suite/marathon_results.json @@ -0,0 +1,67 @@ +{ + "timestamp": "2026-08-17T00:23:01.849835", + "total_time": 6.53, + "total_scripts": 7, + "total_issues": 1112, + "passed": 1, + "failed": 6, + "skipped": 0, + "results": [ + { + "name": "trace_api_contracts", + "description": "Frontend\u2194Backend API Contracts", + "status": "FAIL", + "exit_code": 246, + "elapsed": 0.21, + "issues": 758 + }, + { + "name": "trace_hooks", + "description": "Hook Registrations vs Triggers", + "status": "FAIL", + "exit_code": 1, + "elapsed": 0.06, + "issues": 0 + }, + { + "name": "trace_functions", + "description": "Dead Functions (defined but never called)", + "status": "FAIL", + "exit_code": 3, + "elapsed": 2.94, + "issues": 3 + }, + { + "name": "trace_stores", + "description": "Unused Store Actions/State", + "status": "FAIL", + "exit_code": 67, + "elapsed": 0.08, + "issues": 323 + }, + { + "name": "trace_contracts", + "description": "Contract Attribute Mismatches", + "status": "PASS", + "exit_code": 0, + "elapsed": 1.72, + "issues": 0 + }, + { + "name": "trace_plugins", + "description": "Plugin\u2192Manifest\u2192Frontend Verkabelung", + "status": "FAIL", + "exit_code": 24, + "elapsed": 0.11, + "issues": 24 + }, + { + "name": "trace_imports", + "description": "Broken/Missing Imports", + "status": "FAIL", + "exit_code": 4, + "elapsed": 1.4, + "issues": 4 + } + ] +} \ No newline at end of file diff --git a/scripts/test_suite/results_trace_api_contracts.json b/scripts/test_suite/results_trace_api_contracts.json new file mode 100644 index 0000000..3e7c864 --- /dev/null +++ b/scripts/test_suite/results_trace_api_contracts.json @@ -0,0 +1,10600 @@ +{ + "frontend_calls": [ + { + "file": "api/webhooks.ts", + "method": "GET", + "url": "/webhooks{param}", + "raw_url": "/webhooks${params}" + }, + { + "file": "api/webhooks.ts", + "method": "GET", + "url": "/webhooks/{param}", + "raw_url": "/webhooks/${id}" + }, + { + "file": "api/webhooks.ts", + "method": "POST", + "url": "/webhooks", + "raw_url": "/webhooks" + }, + { + "file": "api/webhooks.ts", + "method": "POST", + "url": "/webhooks/{param}/test", + "raw_url": "/webhooks/${id}/test" + }, + { + "file": "api/webhooks.ts", + "method": "PATCH", + "url": "/webhooks/{param}", + "raw_url": "/webhooks/${id}" + }, + { + "file": "api/webhooks.ts", + "method": "DELETE", + "url": "/webhooks/{param}", + "raw_url": "/webhooks/${id}" + }, + { + "file": "api/notifications.ts", + "method": "GET", + "url": "/notifications/unread-count", + "raw_url": "/notifications/unread-count" + }, + { + "file": "api/notifications.ts", + "method": "GET", + "url": "/notifications/types", + "raw_url": "/notifications/types" + }, + { + "file": "api/notifications.ts", + "method": "GET", + "url": "/notifications/preferences", + "raw_url": "/notifications/preferences" + }, + { + "file": "api/notifications.ts", + "method": "PATCH", + "url": "/notifications/{param}/read", + "raw_url": "/notifications/${id}/read" + }, + { + "file": "api/notifications.ts", + "method": "PATCH", + "url": "/notifications/preferences/{param}", + "raw_url": "/notifications/preferences/${typeKey}" + }, + { + "file": "api/notifications.ts", + "method": "DELETE", + "url": "/notifications/{param}", + "raw_url": "/notifications/${id}" + }, + { + "file": "api/calendar.ts", + "method": "GET", + "url": "/calendars", + "raw_url": "/calendars" + }, + { + "file": "api/calendar.ts", + "method": "GET", + "url": "/calendars/{param}/permissions", + "raw_url": "/calendars/${calendarId}/permissions" + }, + { + "file": "api/calendar.ts", + "method": "GET", + "url": "/calendar/entries${qs ", + "raw_url": "/calendar/entries${qs ? " + }, + { + "file": "api/calendar.ts", + "method": "GET", + "url": "/calendar/entries/{param}", + "raw_url": "/calendar/entries/${entryId}" + }, + { + "file": "api/calendar.ts", + "method": "GET", + "url": "/calendar/kanban", + "raw_url": "/calendar/kanban" + }, + { + "file": "api/calendar.ts", + "method": "POST", + "url": "/calendars", + "raw_url": "/calendars" + }, + { + "file": "api/calendar.ts", + "method": "POST", + "url": "/calendars/{param}/share", + "raw_url": "/calendars/${calendarId}/share" + }, + { + "file": "api/calendar.ts", + "method": "POST", + "url": "/calendar/entries", + "raw_url": "/calendar/entries" + }, + { + "file": "api/calendar.ts", + "method": "POST", + "url": "/calendar/entries/{param}/subtasks", + "raw_url": "/calendar/entries/${entryId}/subtasks" + }, + { + "file": "api/calendar.ts", + "method": "POST", + "url": "/resources", + "raw_url": "/resources" + }, + { + "file": "api/calendar.ts", + "method": "POST", + "url": "/calendar/entries/{param}/book-resource", + "raw_url": "/calendar/entries/${entryId}/book-resource" + }, + { + "file": "api/calendar.ts", + "method": "PATCH", + "url": "/calendars/{param}", + "raw_url": "/calendars/${calendarId}" + }, + { + "file": "api/calendar.ts", + "method": "PATCH", + "url": "/calendar/entries/{param}", + "raw_url": "/calendar/entries/${entryId}" + }, + { + "file": "api/calendar.ts", + "method": "PATCH", + "url": "/calendar/entries/{param}/subtasks/{param}", + "raw_url": "/calendar/entries/${entryId}/subtasks/${subId}" + }, + { + "file": "api/calendar.ts", + "method": "DELETE", + "url": "/calendars/{param}", + "raw_url": "/calendars/${calendarId}" + }, + { + "file": "api/calendar.ts", + "method": "DELETE", + "url": "/calendar/entries/{param}", + "raw_url": "/calendar/entries/${entryId}" + }, + { + "file": "api/userPreferences.ts", + "method": "GET", + "url": "/user/preferences", + "raw_url": "/user/preferences" + }, + { + "file": "api/userPreferences.ts", + "method": "GET", + "url": "/user/preferences/{param}", + "raw_url": "/user/preferences/${key}" + }, + { + "file": "api/userPreferences.ts", + "method": "PUT", + "url": "/user/preferences/{param}", + "raw_url": "/user/preferences/${key}" + }, + { + "file": "api/userPreferences.ts", + "method": "DELETE", + "url": "/user/preferences/{param}", + "raw_url": "/user/preferences/${key}" + }, + { + "file": "api/plugins.ts", + "method": "GET", + "url": "/plugins", + "raw_url": "/plugins" + }, + { + "file": "api/plugins.ts", + "method": "POST", + "url": "/plugins/{param}/install", + "raw_url": "/plugins/${name}/install" + }, + { + "file": "api/plugins.ts", + "method": "POST", + "url": "/plugins/{param}/activate", + "raw_url": "/plugins/${name}/activate" + }, + { + "file": "api/plugins.ts", + "method": "POST", + "url": "/plugins/{param}/deactivate", + "raw_url": "/plugins/${name}/deactivate" + }, + { + "file": "api/plugins.ts", + "method": "DELETE", + "url": "/plugins/{param}", + "raw_url": "/plugins/${name}?remove_data=${removeData}" + }, + { + "file": "api/groups.ts", + "method": "GET", + "url": "/groups", + "raw_url": "/groups" + }, + { + "file": "api/groups.ts", + "method": "GET", + "url": "/groups/{param}/members", + "raw_url": "/groups/${groupId}/members" + }, + { + "file": "api/groups.ts", + "method": "GET", + "url": "/groups/user/{param}", + "raw_url": "/groups/user/${userId}" + }, + { + "file": "api/groups.ts", + "method": "POST", + "url": "/groups", + "raw_url": "/groups" + }, + { + "file": "api/groups.ts", + "method": "POST", + "url": "/groups/{param}/members", + "raw_url": "/groups/${groupId}/members" + }, + { + "file": "api/groups.ts", + "method": "PATCH", + "url": "/groups/{param}", + "raw_url": "/groups/${id}" + }, + { + "file": "api/groups.ts", + "method": "DELETE", + "url": "/groups/{param}", + "raw_url": "/groups/${id}" + }, + { + "file": "api/groups.ts", + "method": "DELETE", + "url": "/groups/{param}/members/{param}", + "raw_url": "/groups/${groupId}/members/${userId}" + }, + { + "file": "api/entityPermissions.ts", + "method": "GET", + "url": "/permissions/{param}/{param}", + "raw_url": "/permissions/${entityType}/${entityId}" + }, + { + "file": "api/entityPermissions.ts", + "method": "GET", + "url": "/permissions/{param}/{param}/access", + "raw_url": "/permissions/${entityType}/${entityId}/access" + }, + { + "file": "api/entityPermissions.ts", + "method": "GET", + "url": "/permissions/all", + "raw_url": "/permissions/all" + }, + { + "file": "api/entityPermissions.ts", + "method": "GET", + "url": "/permissions/registry", + "raw_url": "/permissions/registry" + }, + { + "file": "api/entityPermissions.ts", + "method": "POST", + "url": "/permissions/{param}/{param}", + "raw_url": "/permissions/${entityType}/${entityId}" + }, + { + "file": "api/entityPermissions.ts", + "method": "PUT", + "url": "/permissions/{param}/{param}/{param}", + "raw_url": "/permissions/${entityType}/${entityId}/${permId}" + }, + { + "file": "api/entityPermissions.ts", + "method": "DELETE", + "url": "/permissions/{param}/{param}/{param}", + "raw_url": "/permissions/${entityType}/${entityId}/${permId}" + }, + { + "file": "api/attachments.ts", + "method": "GET", + "url": "/attachments", + "raw_url": "/attachments?${params.toString()}" + }, + { + "file": "api/attachments.ts", + "method": "GET", + "url": "/addresses", + "raw_url": "/addresses?entity_type=${entityType}&entity_id=${entityId}" + }, + { + "file": "api/attachments.ts", + "method": "POST", + "url": "/addresses", + "raw_url": "/addresses" + }, + { + "file": "api/attachments.ts", + "method": "PATCH", + "url": "/addresses/{param}", + "raw_url": "/addresses/${id}" + }, + { + "file": "api/attachments.ts", + "method": "DELETE", + "url": "/attachments/{param}", + "raw_url": "/attachments/${id}" + }, + { + "file": "api/attachments.ts", + "method": "DELETE", + "url": "/addresses/{param}", + "raw_url": "/addresses/${id}" + }, + { + "file": "api/comm.ts", + "method": "GET", + "url": "/comm/conversations", + "raw_url": "/comm/conversations" + }, + { + "file": "api/comm.ts", + "method": "GET", + "url": "/comm/conversations/{param}", + "raw_url": "/comm/conversations/${id}" + }, + { + "file": "api/comm.ts", + "method": "GET", + "url": "/comm/conversations/{param}/messages", + "raw_url": "/comm/conversations/${convId}/messages" + }, + { + "file": "api/comm.ts", + "method": "GET", + "url": "/comm/miniapps", + "raw_url": "/comm/miniapps" + }, + { + "file": "api/comm.ts", + "method": "GET", + "url": "/comm/block-types", + "raw_url": "/comm/block-types" + }, + { + "file": "api/comm.ts", + "method": "POST", + "url": "/comm/conversations", + "raw_url": "/comm/conversations" + }, + { + "file": "api/comm.ts", + "method": "POST", + "url": "/comm/conversations/{param}/pin", + "raw_url": "/comm/conversations/${id}/pin" + }, + { + "file": "api/comm.ts", + "method": "POST", + "url": "/comm/conversations/{param}/mute", + "raw_url": "/comm/conversations/${id}/mute" + }, + { + "file": "api/comm.ts", + "method": "POST", + "url": "/comm/conversations/{param}/participants", + "raw_url": "/comm/conversations/${convId}/participants" + }, + { + "file": "api/comm.ts", + "method": "POST", + "url": "/comm/conversations/{param}/messages", + "raw_url": "/comm/conversations/${convId}/messages" + }, + { + "file": "api/comm.ts", + "method": "POST", + "url": "/comm/messages/{param}/reactions", + "raw_url": "/comm/messages/${msgId}/reactions" + }, + { + "file": "api/comm.ts", + "method": "POST", + "url": "/comm/conversations/{param}/read", + "raw_url": "/comm/conversations/${convId}/read" + }, + { + "file": "api/comm.ts", + "method": "POST", + "url": "/comm/conversations/{param}/miniapps", + "raw_url": "/comm/conversations/${convId}/miniapps" + }, + { + "file": "api/comm.ts", + "method": "PATCH", + "url": "/comm/conversations/{param}", + "raw_url": "/comm/conversations/${id}" + }, + { + "file": "api/comm.ts", + "method": "PATCH", + "url": "/comm/conversations/{param}/participants/{param}", + "raw_url": "/comm/conversations/${convId}/participants/${pid}" + }, + { + "file": "api/comm.ts", + "method": "PATCH", + "url": "/comm/messages/{param}", + "raw_url": "/comm/messages/${id}" + }, + { + "file": "api/comm.ts", + "method": "DELETE", + "url": "/comm/conversations/{param}", + "raw_url": "/comm/conversations/${id}" + }, + { + "file": "api/comm.ts", + "method": "DELETE", + "url": "/comm/conversations/{param}/pin", + "raw_url": "/comm/conversations/${id}/pin" + }, + { + "file": "api/comm.ts", + "method": "DELETE", + "url": "/comm/conversations/{param}/mute", + "raw_url": "/comm/conversations/${id}/mute" + }, + { + "file": "api/comm.ts", + "method": "DELETE", + "url": "/comm/conversations/{param}/participants/{param}", + "raw_url": "/comm/conversations/${convId}/participants/${pid}" + }, + { + "file": "api/comm.ts", + "method": "DELETE", + "url": "/comm/messages/{param}", + "raw_url": "/comm/messages/${id}" + }, + { + "file": "api/comm.ts", + "method": "DELETE", + "url": "/comm/messages/{param}/reactions/{param}", + "raw_url": "/comm/messages/${msgId}/reactions/${encodeURIComponent(emoji)}" + }, + { + "file": "api/dashboard.ts", + "method": "GET", + "url": "/dashboard/widgets", + "raw_url": "/dashboard/widgets" + }, + { + "file": "api/customFields.ts", + "method": "GET", + "url": "/contacts/{param}/custom-fields", + "raw_url": "/contacts/${contactId}/custom-fields" + }, + { + "file": "api/customFields.ts", + "method": "PATCH", + "url": "/contacts/{param}/custom-fields", + "raw_url": "/contacts/${contactId}/custom-fields" + }, + { + "file": "api/roles.ts", + "method": "GET", + "url": "/roles", + "raw_url": "/roles" + }, + { + "file": "api/roles.ts", + "method": "GET", + "url": "/roles/permissions", + "raw_url": "/roles/permissions" + }, + { + "file": "api/roles.ts", + "method": "POST", + "url": "/roles", + "raw_url": "/roles" + }, + { + "file": "api/roles.ts", + "method": "PATCH", + "url": "/roles/{param}", + "raw_url": "/roles/${id}" + }, + { + "file": "api/roles.ts", + "method": "DELETE", + "url": "/roles/{param}", + "raw_url": "/roles/${id}" + }, + { + "file": "api/mcp.ts", + "method": "GET", + "url": "/mcp/tools", + "raw_url": "/mcp/tools" + }, + { + "file": "api/mcp.ts", + "method": "GET", + "url": "/mcp/config", + "raw_url": "/mcp/config" + }, + { + "file": "api/mcp.ts", + "method": "POST", + "url": "/mcp/tools/{param}/execute", + "raw_url": "/mcp/tools/${toolName}/execute" + }, + { + "file": "api/dms.ts", + "method": "GET", + "url": "/dms/folders", + "raw_url": "/dms/folders" + }, + { + "file": "api/dms.ts", + "method": "GET", + "url": "/dms/files/{param}", + "raw_url": "/dms/files/${fileId}" + }, + { + "file": "api/dms.ts", + "method": "GET", + "url": "/dms/search", + "raw_url": "/dms/search?${qs}" + }, + { + "file": "api/dms.ts", + "method": "GET", + "url": "/dms/shared-with-me", + "raw_url": "/dms/shared-with-me" + }, + { + "file": "api/dms.ts", + "method": "GET", + "url": "/dms/folders/{param}/files", + "raw_url": "/dms/folders/${folderId}/files" + }, + { + "file": "api/dms.ts", + "method": "GET", + "url": "/dms/files", + "raw_url": "/dms/files" + }, + { + "file": "api/dms.ts", + "method": "POST", + "url": "/dms/folders", + "raw_url": "/dms/folders" + }, + { + "file": "api/dms.ts", + "method": "POST", + "url": "/dms/files/upload", + "raw_url": "/dms/files/upload" + }, + { + "file": "api/dms.ts", + "method": "POST", + "url": "/dms/files/{param}/restore", + "raw_url": "/dms/files/${fileId}/restore" + }, + { + "file": "api/dms.ts", + "method": "POST", + "url": "/dms/files/{param}/edit-session", + "raw_url": "/dms/files/${fileId}/edit-session" + }, + { + "file": "api/dms.ts", + "method": "POST", + "url": "/dms/files/{param}/share", + "raw_url": "/dms/files/${fileId}/share" + }, + { + "file": "api/dms.ts", + "method": "POST", + "url": "/dms/files/bulk-move", + "raw_url": "/dms/files/bulk-move" + }, + { + "file": "api/dms.ts", + "method": "POST", + "url": "/dms/files/bulk-delete", + "raw_url": "/dms/files/bulk-delete" + }, + { + "file": "api/dms.ts", + "method": "PATCH", + "url": "/dms/folders/{param}", + "raw_url": "/dms/folders/${folderId}" + }, + { + "file": "api/dms.ts", + "method": "PATCH", + "url": "/dms/files/{param}", + "raw_url": "/dms/files/${fileId}" + }, + { + "file": "api/dms.ts", + "method": "DELETE", + "url": "/dms/folders/{param}", + "raw_url": "/dms/folders/${folderId}" + }, + { + "file": "api/dms.ts", + "method": "DELETE", + "url": "/dms/files/{param}", + "raw_url": "/dms/files/${fileId}" + }, + { + "file": "api/dms.ts", + "method": "DELETE", + "url": "/dms/files/{param}/share", + "raw_url": "/dms/files/${fileId}/share" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/automation", + "raw_url": "/automation" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/automation/{param}", + "raw_url": "/automation/${id}" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/automation/{param}/runs", + "raw_url": "/automation/${automationId}/runs" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/automation/{param}/versions", + "raw_url": "/automation/${automationId}/versions" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/automation/settings", + "raw_url": "/automation/settings" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/agents", + "raw_url": "/agents" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/agents/{param}", + "raw_url": "/agents/${id}" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/agents/{param}/runs", + "raw_url": "/agents/${agentId}/runs" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/agents/{param}/versions", + "raw_url": "/agents/${agentId}/versions" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/agents/tools", + "raw_url": "/agents/tools" + }, + { + "file": "api/automation.ts", + "method": "GET", + "url": "/automation/miniapps", + "raw_url": "/automation/miniapps" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/automation", + "raw_url": "/automation" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/automation/{param}/execute", + "raw_url": "/automation/${id}/execute" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/automation/{param}/dry-run", + "raw_url": "/automation/${id}/dry-run" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/automation/{param}/versions/{param}/restore", + "raw_url": "/automation/${id}/versions/${versionId}/restore" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/agents", + "raw_url": "/agents" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/agents/{param}/execute", + "raw_url": "/agents/${id}/execute" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/agents/{param}/test-run", + "raw_url": "/agents/${id}/test-run" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/agents/{param}/versions/{param}/restore", + "raw_url": "/agents/${id}/versions/${versionId}/restore" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/agents/{param}/send-message", + "raw_url": "/agents/${id}/send-message" + }, + { + "file": "api/automation.ts", + "method": "POST", + "url": "/automation/miniapps", + "raw_url": "/automation/miniapps" + }, + { + "file": "api/automation.ts", + "method": "PATCH", + "url": "/automation/{param}", + "raw_url": "/automation/${id}" + }, + { + "file": "api/automation.ts", + "method": "PATCH", + "url": "/automation/settings", + "raw_url": "/automation/settings" + }, + { + "file": "api/automation.ts", + "method": "PATCH", + "url": "/agents/{param}", + "raw_url": "/agents/${id}" + }, + { + "file": "api/automation.ts", + "method": "DELETE", + "url": "/automation/{param}", + "raw_url": "/automation/${id}" + }, + { + "file": "api/automation.ts", + "method": "DELETE", + "url": "/agents/{param}", + "raw_url": "/agents/${id}" + }, + { + "file": "api/automation.ts", + "method": "DELETE", + "url": "/automation/miniapps/{param}", + "raw_url": "/automation/miniapps/${appId}" + }, + { + "file": "api/ai.ts", + "method": "GET", + "url": "/ai/providers", + "raw_url": "/ai/providers" + }, + { + "file": "api/ai.ts", + "method": "GET", + "url": "/ai/models", + "raw_url": "/ai/models" + }, + { + "file": "api/ai.ts", + "method": "GET", + "url": "/ai/presets", + "raw_url": "/ai/presets" + }, + { + "file": "api/ai.ts", + "method": "GET", + "url": "/ai/agents", + "raw_url": "/ai/agents" + }, + { + "file": "api/ai.ts", + "method": "GET", + "url": "/ai/tools", + "raw_url": "/ai/tools" + }, + { + "file": "api/ai.ts", + "method": "GET", + "url": "/ai/folders", + "raw_url": "/ai/folders" + }, + { + "file": "api/ai.ts", + "method": "GET", + "url": "/ai/sessions", + "raw_url": "/ai/sessions" + }, + { + "file": "api/ai.ts", + "method": "GET", + "url": "/ai/sessions/{param}/messages", + "raw_url": "/ai/sessions/${sessionId}/messages" + }, + { + "file": "api/ai.ts", + "method": "GET", + "url": "/ai/sessions/{param}/attachments", + "raw_url": "/ai/sessions/${sessionId}/attachments" + }, + { + "file": "api/ai.ts", + "method": "POST", + "url": "/ai/providers", + "raw_url": "/ai/providers" + }, + { + "file": "api/ai.ts", + "method": "POST", + "url": "/ai/models", + "raw_url": "/ai/models" + }, + { + "file": "api/ai.ts", + "method": "POST", + "url": "/ai/presets", + "raw_url": "/ai/presets" + }, + { + "file": "api/ai.ts", + "method": "POST", + "url": "/ai/agents", + "raw_url": "/ai/agents" + }, + { + "file": "api/ai.ts", + "method": "POST", + "url": "/ai/folders", + "raw_url": "/ai/folders" + }, + { + "file": "api/ai.ts", + "method": "POST", + "url": "/ai/sessions", + "raw_url": "/ai/sessions" + }, + { + "file": "api/ai.ts", + "method": "PUT", + "url": "/ai/providers/{param}", + "raw_url": "/ai/providers/${id}" + }, + { + "file": "api/ai.ts", + "method": "PUT", + "url": "/ai/models/{param}", + "raw_url": "/ai/models/${id}" + }, + { + "file": "api/ai.ts", + "method": "PUT", + "url": "/ai/presets/{param}", + "raw_url": "/ai/presets/${id}" + }, + { + "file": "api/ai.ts", + "method": "PUT", + "url": "/ai/agents/{param}", + "raw_url": "/ai/agents/${id}" + }, + { + "file": "api/ai.ts", + "method": "PUT", + "url": "/ai/folders/{param}", + "raw_url": "/ai/folders/${id}" + }, + { + "file": "api/ai.ts", + "method": "PUT", + "url": "/ai/sessions/{param}", + "raw_url": "/ai/sessions/${id}" + }, + { + "file": "api/ai.ts", + "method": "DELETE", + "url": "/ai/providers/{param}", + "raw_url": "/ai/providers/${id}" + }, + { + "file": "api/ai.ts", + "method": "DELETE", + "url": "/ai/models/{param}", + "raw_url": "/ai/models/${id}" + }, + { + "file": "api/ai.ts", + "method": "DELETE", + "url": "/ai/presets/{param}", + "raw_url": "/ai/presets/${id}" + }, + { + "file": "api/ai.ts", + "method": "DELETE", + "url": "/ai/agents/{param}", + "raw_url": "/ai/agents/${id}" + }, + { + "file": "api/ai.ts", + "method": "DELETE", + "url": "/ai/folders/{param}", + "raw_url": "/ai/folders/${id}" + }, + { + "file": "api/ai.ts", + "method": "DELETE", + "url": "/ai/sessions/{param}", + "raw_url": "/ai/sessions/${id}" + }, + { + "file": "api/auth.ts", + "method": "GET", + "url": "/auth/me", + "raw_url": "/auth/me" + }, + { + "file": "api/auth.ts", + "method": "POST", + "url": "/auth/login", + "raw_url": "/auth/login" + }, + { + "file": "api/auth.ts", + "method": "POST", + "url": "/auth/logout", + "raw_url": "/auth/logout" + }, + { + "file": "api/auth.ts", + "method": "POST", + "url": "/auth/password-reset/request", + "raw_url": "/auth/password-reset/request" + }, + { + "file": "api/auth.ts", + "method": "POST", + "url": "/auth/password-reset/confirm", + "raw_url": "/auth/password-reset/confirm" + }, + { + "file": "api/auth.ts", + "method": "POST", + "url": "/auth/switch-tenant", + "raw_url": "/auth/switch-tenant" + }, + { + "file": "api/entityHistory.ts", + "method": "GET", + "url": "/entity-history/{param}/{param}", + "raw_url": "/entity-history/${entityType}/${entityId}?limit=${limit}" + }, + { + "file": "api/entityHistory.ts", + "method": "GET", + "url": "/entity-history/trash", + "raw_url": "/entity-history/trash?${params.toString()}" + }, + { + "file": "api/entityHistory.ts", + "method": "POST", + "url": "/entity-history/bulk-restore", + "raw_url": "/entity-history/bulk-restore" + }, + { + "file": "api/entityHistory.ts", + "method": "POST", + "url": "/entity-history/undo/{param}/{param}", + "raw_url": "/entity-history/undo/${entityType}/${entityId}" + }, + { + "file": "api/settings.ts", + "method": "GET", + "url": "/system-settings", + "raw_url": "/system-settings" + }, + { + "file": "api/settings.ts", + "method": "GET", + "url": "/currencies", + "raw_url": "/currencies" + }, + { + "file": "api/settings.ts", + "method": "GET", + "url": "/taxes", + "raw_url": "/taxes" + }, + { + "file": "api/settings.ts", + "method": "GET", + "url": "/sequences", + "raw_url": "/sequences" + }, + { + "file": "api/settings.ts", + "method": "POST", + "url": "/currencies", + "raw_url": "/currencies" + }, + { + "file": "api/settings.ts", + "method": "POST", + "url": "/taxes", + "raw_url": "/taxes" + }, + { + "file": "api/settings.ts", + "method": "POST", + "url": "/sequences", + "raw_url": "/sequences" + }, + { + "file": "api/settings.ts", + "method": "PATCH", + "url": "/currencies/{param}", + "raw_url": "/currencies/${id}" + }, + { + "file": "api/settings.ts", + "method": "PATCH", + "url": "/taxes/{param}", + "raw_url": "/taxes/${id}" + }, + { + "file": "api/settings.ts", + "method": "PATCH", + "url": "/sequences/{param}", + "raw_url": "/sequences/${id}" + }, + { + "file": "api/settings.ts", + "method": "DELETE", + "url": "/currencies/{param}", + "raw_url": "/currencies/${id}" + }, + { + "file": "api/settings.ts", + "method": "DELETE", + "url": "/taxes/{param}", + "raw_url": "/taxes/${id}" + }, + { + "file": "api/settings.ts", + "method": "DELETE", + "url": "/sequences/{param}", + "raw_url": "/sequences/${id}" + }, + { + "file": "api/tags.ts", + "method": "GET", + "url": "/tags", + "raw_url": "/tags" + }, + { + "file": "api/tags.ts", + "method": "GET", + "url": "/tags/{param}/entities", + "raw_url": "/tags/${tagId}/entities" + }, + { + "file": "api/tags.ts", + "method": "POST", + "url": "/tags", + "raw_url": "/tags" + }, + { + "file": "api/tags.ts", + "method": "POST", + "url": "/tags/assign", + "raw_url": "/tags/assign" + }, + { + "file": "api/tags.ts", + "method": "POST", + "url": "/tags/bulk-assign", + "raw_url": "/tags/bulk-assign" + }, + { + "file": "api/tags.ts", + "method": "PATCH", + "url": "/tags/{param}", + "raw_url": "/tags/${tagId}" + }, + { + "file": "api/tags.ts", + "method": "DELETE", + "url": "/tags/{param}", + "raw_url": "/tags/${tagId}" + }, + { + "file": "api/tags.ts", + "method": "DELETE", + "url": "/tags/assign", + "raw_url": "/tags/assign" + }, + { + "file": "api/pluginManifests.ts", + "method": "GET", + "url": "/plugins/active-manifests", + "raw_url": "/plugins/active-manifests" + }, + { + "file": "api/permissions.ts", + "method": "GET", + "url": "/permissions/files/{param}/permissions", + "raw_url": "/permissions/files/${fileId}/permissions" + }, + { + "file": "api/permissions.ts", + "method": "POST", + "url": "/permissions/files/{param}/permissions", + "raw_url": "/permissions/files/${fileId}/permissions" + }, + { + "file": "api/permissions.ts", + "method": "POST", + "url": "/permissions/files/{param}/share-link", + "raw_url": "/permissions/files/${fileId}/share-link" + }, + { + "file": "api/permissions.ts", + "method": "DELETE", + "url": "/permissions/files/{param}/permissions/{param}", + "raw_url": "/permissions/files/${fileId}/permissions/${userId}" + }, + { + "file": "api/permissions.ts", + "method": "DELETE", + "url": "/permissions/share-links/{param}", + "raw_url": "/permissions/share-links/${linkId}" + }, + { + "file": "api/tasks.ts", + "method": "GET", + "url": "/tasks", + "raw_url": "/tasks?${params.toString()}" + }, + { + "file": "api/tasks.ts", + "method": "GET", + "url": "/tasks/{param}", + "raw_url": "/tasks/${id}" + }, + { + "file": "api/tasks.ts", + "method": "POST", + "url": "/tasks", + "raw_url": "/tasks" + }, + { + "file": "api/tasks.ts", + "method": "POST", + "url": "/tasks/{param}/assign", + "raw_url": "/tasks/${id}/assign" + }, + { + "file": "api/tasks.ts", + "method": "POST", + "url": "/tasks/{param}/status", + "raw_url": "/tasks/${id}/status" + }, + { + "file": "api/tasks.ts", + "method": "PATCH", + "url": "/tasks/{param}", + "raw_url": "/tasks/${id}" + }, + { + "file": "api/tasks.ts", + "method": "DELETE", + "url": "/tasks/{param}", + "raw_url": "/tasks/${id}" + }, + { + "file": "api/contacts.ts", + "method": "GET", + "url": "/contacts/{param}", + "raw_url": "/contacts/${id}" + }, + { + "file": "api/contacts.ts", + "method": "GET", + "url": "/contact-folders", + "raw_url": "/contact-folders" + }, + { + "file": "api/contacts.ts", + "method": "GET", + "url": "/contact-folders/{param}/permissions", + "raw_url": "/contact-folders/${folderId}/permissions" + }, + { + "file": "api/contacts.ts", + "method": "POST", + "url": "/contacts", + "raw_url": "/contacts" + }, + { + "file": "api/contacts.ts", + "method": "POST", + "url": "/contact-folders", + "raw_url": "/contact-folders" + }, + { + "file": "api/contacts.ts", + "method": "POST", + "url": "/contact-folders/{param}/permissions", + "raw_url": "/contact-folders/${folderId}/permissions" + }, + { + "file": "api/contacts.ts", + "method": "PUT", + "url": "/contact-folders/{param}", + "raw_url": "/contact-folders/${id}" + }, + { + "file": "api/contacts.ts", + "method": "PUT", + "url": "/contact-folders/contacts/{param}/move", + "raw_url": "/contact-folders/contacts/${contactId}/move" + }, + { + "file": "api/contacts.ts", + "method": "PUT", + "url": "/contact-folders/{param}/permissions/{param}", + "raw_url": "/contact-folders/${folderId}/permissions/${permissionId}" + }, + { + "file": "api/contacts.ts", + "method": "PATCH", + "url": "/contacts/{param}", + "raw_url": "/contacts/${id}" + }, + { + "file": "api/contacts.ts", + "method": "DELETE", + "url": "/contacts/{param}", + "raw_url": "/contacts/${id}" + }, + { + "file": "api/contacts.ts", + "method": "DELETE", + "url": "/contact-folders/{param}", + "raw_url": "/contact-folders/${id}" + }, + { + "file": "api/contacts.ts", + "method": "DELETE", + "url": "/contact-folders/{param}/permissions/{param}", + "raw_url": "/contact-folders/${folderId}/permissions/${permissionId}" + }, + { + "file": "api/workflows.ts", + "method": "GET", + "url": "/workflows", + "raw_url": "/workflows?${params.toString()}" + }, + { + "file": "api/workflows.ts", + "method": "GET", + "url": "/workflows/{param}", + "raw_url": "/workflows/${id}" + }, + { + "file": "api/workflows.ts", + "method": "GET", + "url": "/workflows/instances", + "raw_url": "/workflows/instances?${params.toString()}" + }, + { + "file": "api/workflows.ts", + "method": "GET", + "url": "/workflows/instances/{param}", + "raw_url": "/workflows/instances/${id}" + }, + { + "file": "api/workflows.ts", + "method": "POST", + "url": "/workflows", + "raw_url": "/workflows" + }, + { + "file": "api/workflows.ts", + "method": "POST", + "url": "/workflows/{param}/instances", + "raw_url": "/workflows/${workflowId}/instances" + }, + { + "file": "api/workflows.ts", + "method": "POST", + "url": "/workflows/instances/{param}/advance", + "raw_url": "/workflows/instances/${instanceId}/advance" + }, + { + "file": "api/workflows.ts", + "method": "POST", + "url": "/workflows/instances/{param}/cancel", + "raw_url": "/workflows/instances/${instanceId}/cancel" + }, + { + "file": "api/workflows.ts", + "method": "PATCH", + "url": "/workflows/{param}", + "raw_url": "/workflows/${id}" + }, + { + "file": "api/workflows.ts", + "method": "DELETE", + "url": "/workflows/{param}", + "raw_url": "/workflows/${id}" + }, + { + "file": "api/backups.ts", + "method": "GET", + "url": "/backups", + "raw_url": "/backups" + }, + { + "file": "api/backups.ts", + "method": "POST", + "url": "/backups", + "raw_url": "/backups" + }, + { + "file": "api/backups.ts", + "method": "POST", + "url": "/backups/{param}/restore", + "raw_url": "/backups/${backupId}/restore" + }, + { + "file": "api/backups.ts", + "method": "DELETE", + "url": "/backups/{param}", + "raw_url": "/backups/${backupId}" + }, + { + "file": "api/unifiedContacts.ts", + "method": "GET", + "url": "/contacts/{param}", + "raw_url": "/contacts/${id}" + }, + { + "file": "api/unifiedContacts.ts", + "method": "GET", + "url": "/contacts/{param}/persons", + "raw_url": "/contacts/${contactId}/persons" + }, + { + "file": "api/unifiedContacts.ts", + "method": "GET", + "url": "/contacts/{param}", + "raw_url": "/contacts/${id}" + }, + { + "file": "api/unifiedContacts.ts", + "method": "GET", + "url": "/contacts/{param}/persons", + "raw_url": "/contacts/${contactId}/persons" + }, + { + "file": "api/unifiedContacts.ts", + "method": "POST", + "url": "/contacts", + "raw_url": "/contacts" + }, + { + "file": "api/unifiedContacts.ts", + "method": "POST", + "url": "/contacts/{param}/persons", + "raw_url": "/contacts/${contactId}/persons" + }, + { + "file": "api/unifiedContacts.ts", + "method": "POST", + "url": "/contacts", + "raw_url": "/contacts" + }, + { + "file": "api/unifiedContacts.ts", + "method": "POST", + "url": "/contacts/{param}/persons", + "raw_url": "/contacts/${contactId}/persons" + }, + { + "file": "api/unifiedContacts.ts", + "method": "PUT", + "url": "/contacts/{param}", + "raw_url": "/contacts/${id}" + }, + { + "file": "api/unifiedContacts.ts", + "method": "PUT", + "url": "/contacts/{param}/persons/{param}", + "raw_url": "/contacts/${contactId}/persons/${personId}" + }, + { + "file": "api/unifiedContacts.ts", + "method": "PUT", + "url": "/contacts/{param}", + "raw_url": "/contacts/${id}" + }, + { + "file": "api/unifiedContacts.ts", + "method": "PUT", + "url": "/contacts/{param}/persons/{param}", + "raw_url": "/contacts/${contactId}/persons/${personId}" + }, + { + "file": "api/unifiedContacts.ts", + "method": "DELETE", + "url": "/contacts/{param}${hard ", + "raw_url": "/contacts/${id}${hard ? " + }, + { + "file": "api/unifiedContacts.ts", + "method": "DELETE", + "url": "/contacts/{param}/persons/{param}", + "raw_url": "/contacts/${contactId}/persons/${personId}" + }, + { + "file": "api/unifiedContacts.ts", + "method": "DELETE", + "url": "/contacts/{param}${hard ", + "raw_url": "/contacts/${id}${hard ? " + }, + { + "file": "api/unifiedContacts.ts", + "method": "DELETE", + "url": "/contacts/{param}/persons/{param}", + "raw_url": "/contacts/${contactId}/persons/${personId}" + }, + { + "file": "api/reports.ts", + "method": "GET", + "url": "/reports/templates", + "raw_url": "/reports/templates" + }, + { + "file": "api/reports.ts", + "method": "GET", + "url": "/reports/templates/{param}", + "raw_url": "/reports/templates/${templateId}" + }, + { + "file": "api/reports.ts", + "method": "GET", + "url": "/reports/presets", + "raw_url": "/reports/presets" + }, + { + "file": "api/reports.ts", + "method": "POST", + "url": "/reports/templates", + "raw_url": "/reports/templates" + }, + { + "file": "api/reports.ts", + "method": "PUT", + "url": "/reports/templates/{param}", + "raw_url": "/reports/templates/${id}" + }, + { + "file": "api/reports.ts", + "method": "DELETE", + "url": "/reports/templates/{param}", + "raw_url": "/reports/templates/${id}" + }, + { + "file": "api/customFieldDefinitions.ts", + "method": "GET", + "url": "/custom-fields/definitions{param}", + "raw_url": "/custom-fields/definitions${params}" + }, + { + "file": "api/customFieldDefinitions.ts", + "method": "POST", + "url": "/custom-fields/definitions", + "raw_url": "/custom-fields/definitions" + }, + { + "file": "api/customFieldDefinitions.ts", + "method": "PATCH", + "url": "/custom-fields/definitions/{param}", + "raw_url": "/custom-fields/definitions/${id}" + }, + { + "file": "api/customFieldDefinitions.ts", + "method": "DELETE", + "url": "/custom-fields/definitions/{param}", + "raw_url": "/custom-fields/definitions/${id}" + }, + { + "file": "api/mcpClient.ts", + "method": "GET", + "url": "/mcp-client/servers", + "raw_url": "/mcp-client/servers" + }, + { + "file": "api/mcpClient.ts", + "method": "GET", + "url": "/mcp-client/servers/{param}/tools", + "raw_url": "/mcp-client/servers/${serverId}/tools" + }, + { + "file": "api/mcpClient.ts", + "method": "POST", + "url": "/mcp-client/servers", + "raw_url": "/mcp-client/servers" + }, + { + "file": "api/mcpClient.ts", + "method": "POST", + "url": "/mcp-client/servers/{param}/execute", + "raw_url": "/mcp-client/servers/${serverId}/execute" + }, + { + "file": "api/mcpClient.ts", + "method": "PATCH", + "url": "/mcp-client/servers/{param}", + "raw_url": "/mcp-client/servers/${id}" + }, + { + "file": "api/mcpClient.ts", + "method": "DELETE", + "url": "/mcp-client/servers/{param}", + "raw_url": "/mcp-client/servers/${id}" + }, + { + "file": "api/dedup.ts", + "method": "GET", + "url": "/contacts/merge-history", + "raw_url": "/contacts/merge-history?page=${page}&page_size=${pageSize}" + }, + { + "file": "api/dedup.ts", + "method": "POST", + "url": "/contacts/duplicates", + "raw_url": "/contacts/duplicates" + }, + { + "file": "api/dedup.ts", + "method": "POST", + "url": "/contacts/merge", + "raw_url": "/contacts/merge" + }, + { + "file": "api/users.ts", + "method": "GET", + "url": "/users/{param}", + "raw_url": "/users/${id}" + }, + { + "file": "api/users.ts", + "method": "GET", + "url": "/users/me/menu-order", + "raw_url": "/users/me/menu-order" + }, + { + "file": "api/users.ts", + "method": "POST", + "url": "/users", + "raw_url": "/users" + }, + { + "file": "api/users.ts", + "method": "PUT", + "url": "/users/me/menu-order", + "raw_url": "/users/me/menu-order" + }, + { + "file": "api/users.ts", + "method": "PATCH", + "url": "/users/{param}", + "raw_url": "/users/${id}" + }, + { + "file": "api/users.ts", + "method": "DELETE", + "url": "/users/{param}", + "raw_url": "/users/${id}" + }, + { + "file": "api/contactFolders.ts", + "method": "GET", + "url": "/contact-folders", + "raw_url": "/contact-folders" + }, + { + "file": "api/contactFolders.ts", + "method": "GET", + "url": "/contact-folders/{param}/permissions", + "raw_url": "/contact-folders/${folderId}/permissions" + }, + { + "file": "api/contactFolders.ts", + "method": "GET", + "url": "/contact-folders/{param}/access", + "raw_url": "/contact-folders/${folderId}/access" + }, + { + "file": "api/contactFolders.ts", + "method": "POST", + "url": "/contact-folders", + "raw_url": "/contact-folders" + }, + { + "file": "api/contactFolders.ts", + "method": "POST", + "url": "/contact-folders/{param}/permissions", + "raw_url": "/contact-folders/${folderId}/permissions" + }, + { + "file": "api/contactFolders.ts", + "method": "PUT", + "url": "/contact-folders/{param}", + "raw_url": "/contact-folders/${id}" + }, + { + "file": "api/contactFolders.ts", + "method": "PUT", + "url": "/contact-folders/{param}/reorder", + "raw_url": "/contact-folders/${folderId}/reorder" + }, + { + "file": "api/contactFolders.ts", + "method": "PUT", + "url": "/contact-folders/contacts/{param}/move", + "raw_url": "/contact-folders/contacts/${contactId}/move" + }, + { + "file": "api/contactFolders.ts", + "method": "PUT", + "url": "/contact-folders/{param}/permissions/{param}", + "raw_url": "/contact-folders/${folderId}/permissions/${permissionId}" + }, + { + "file": "api/contactFolders.ts", + "method": "DELETE", + "url": "/contact-folders/{param}", + "raw_url": "/contact-folders/${id}" + }, + { + "file": "api/contactFolders.ts", + "method": "DELETE", + "url": "/contact-folders/{param}/permissions/{param}", + "raw_url": "/contact-folders/${folderId}/permissions/${permissionId}" + }, + { + "file": "api/savedFilters.ts", + "method": "GET", + "url": "/saved-filters", + "raw_url": "/saved-filters?${params.toString()}" + }, + { + "file": "api/savedFilters.ts", + "method": "POST", + "url": "/saved-filters", + "raw_url": "/saved-filters" + }, + { + "file": "api/savedFilters.ts", + "method": "DELETE", + "url": "/saved-filters/{param}", + "raw_url": "/saved-filters/${id}" + }, + { + "file": "api/policies.ts", + "method": "GET", + "url": "/policies/{param}", + "raw_url": "/policies/${entityType}" + }, + { + "file": "api/policies.ts", + "method": "GET", + "url": "/policies/{param}/{param}", + "raw_url": "/policies/${entityType}/${policyId}" + }, + { + "file": "api/policies.ts", + "method": "POST", + "url": "/policies/{param}", + "raw_url": "/policies/${entityType}" + }, + { + "file": "api/policies.ts", + "method": "PUT", + "url": "/policies/{param}/{param}", + "raw_url": "/policies/${entityType}/${policyId}" + }, + { + "file": "api/policies.ts", + "method": "DELETE", + "url": "/policies/{param}/{param}", + "raw_url": "/policies/${entityType}/${policyId}" + }, + { + "file": "api/savedViews.ts", + "method": "GET", + "url": "/saved-views{param}", + "raw_url": "/saved-views${qs}" + }, + { + "file": "api/savedViews.ts", + "method": "POST", + "url": "/saved-views", + "raw_url": "/saved-views" + }, + { + "file": "api/savedViews.ts", + "method": "PUT", + "url": "/saved-views/{param}", + "raw_url": "/saved-views/${id}" + }, + { + "file": "api/savedViews.ts", + "method": "DELETE", + "url": "/saved-views/{param}", + "raw_url": "/saved-views/${id}" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/accounts", + "raw_url": "/mail/accounts" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/accounts/shared", + "raw_url": "/mail/accounts/shared" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/folders", + "raw_url": "/mail/folders?${qs}" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail", + "raw_url": "/mail?${params.toString()}" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/{param}", + "raw_url": "/mail/${mailId}" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/search", + "raw_url": "/mail/search?${qs}" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/threads", + "raw_url": "/mail/threads?${qs}" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/templates", + "raw_url": "/mail/templates" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/signatures", + "raw_url": "/mail/signatures" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/rules", + "raw_url": "/mail/rules" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/pgp/keys", + "raw_url": "/mail/pgp/keys" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/contacts/pgp-keys", + "raw_url": "/mail/contacts/pgp-keys" + }, + { + "file": "api/mail.ts", + "method": "GET", + "url": "/mail/labels", + "raw_url": "/mail/labels" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/accounts", + "raw_url": "/mail/accounts" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/accounts/{param}/users", + "raw_url": "/mail/accounts/${accountId}/users" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/accounts/{param}/delegates", + "raw_url": "/mail/accounts/${accountId}/delegates" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/accounts/{param}/send-permissions", + "raw_url": "/mail/accounts/${accountId}/send-permissions" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/accounts/{param}/test-connection", + "raw_url": "/mail/accounts/${accountId}/test-connection" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/accounts/{param}/sync", + "raw_url": "/mail/accounts/${accountId}/sync" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/folders", + "raw_url": "/mail/folders" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/folders/{param}/empty", + "raw_url": "/mail/folders/${folderId}/empty" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/folders/{param}/sync", + "raw_url": "/mail/folders/${folderId}/sync" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/send", + "raw_url": "/mail/send" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/{param}/reply", + "raw_url": "/mail/${mailId}/reply" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/{param}/forward", + "raw_url": "/mail/${mailId}/forward" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/{param}/link", + "raw_url": "/mail/${mailId}/link" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/{param}/create-event", + "raw_url": "/mail/${mailId}/create-event" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/{param}/labels", + "raw_url": "/mail/${mailId}/labels" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/templates", + "raw_url": "/mail/templates" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/templates/substitute", + "raw_url": "/mail/templates/substitute" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/signatures", + "raw_url": "/mail/signatures" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/rules", + "raw_url": "/mail/rules" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/vacation", + "raw_url": "/mail/vacation" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/vacation/test-dedup", + "raw_url": "/mail/vacation/test-dedup" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/pgp/keys", + "raw_url": "/mail/pgp/keys" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/pgp/encrypt", + "raw_url": "/mail/pgp/encrypt" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/contacts/{param}/pgp-key", + "raw_url": "/mail/contacts/${contactId}/pgp-key" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/labels", + "raw_url": "/mail/labels" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/{param}/move", + "raw_url": "/mail/${mailId}/move" + }, + { + "file": "api/mail.ts", + "method": "POST", + "url": "/mail/drafts", + "raw_url": "/mail/drafts" + }, + { + "file": "api/mail.ts", + "method": "PATCH", + "url": "/mail/accounts/{param}", + "raw_url": "/mail/accounts/${accountId}" + }, + { + "file": "api/mail.ts", + "method": "PATCH", + "url": "/mail/folders/{param}", + "raw_url": "/mail/folders/${folderId}" + }, + { + "file": "api/mail.ts", + "method": "PATCH", + "url": "/mail/{param}/flags", + "raw_url": "/mail/${mailId}/flags" + }, + { + "file": "api/mail.ts", + "method": "PATCH", + "url": "/mail/signatures/{param}", + "raw_url": "/mail/signatures/${signatureId}" + }, + { + "file": "api/mail.ts", + "method": "PATCH", + "url": "/mail/drafts/{param}", + "raw_url": "/mail/drafts/${mailId}" + }, + { + "file": "api/mail.ts", + "method": "DELETE", + "url": "/mail/accounts/{param}", + "raw_url": "/mail/accounts/${accountId}" + }, + { + "file": "api/mail.ts", + "method": "DELETE", + "url": "/mail/folders/{param}", + "raw_url": "/mail/folders/${folderId}" + }, + { + "file": "api/mail.ts", + "method": "DELETE", + "url": "/mail/signatures/{param}", + "raw_url": "/mail/signatures/${signatureId}" + }, + { + "file": "api/mail.ts", + "method": "DELETE", + "url": "/mail/rules/{param}", + "raw_url": "/mail/rules/${ruleId}" + }, + { + "file": "api/mail.ts", + "method": "DELETE", + "url": "/mail/labels/{param}", + "raw_url": "/mail/labels/${labelId}" + }, + { + "file": "api/mail.ts", + "method": "DELETE", + "url": "/mail/{param}", + "raw_url": "/mail/${mailId}" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "GET", + "url": "/api/v1/workspaces", + "raw_url": "/api/v1/workspaces" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "GET", + "url": "/api/v1/workspaces/my", + "raw_url": "/api/v1/workspaces/my" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "GET", + "url": "/api/v1/workspaces/context", + "raw_url": "/api/v1/workspaces/context" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "GET", + "url": "/api/v1/workspaces/{param}/widgets", + "raw_url": "/api/v1/workspaces/${workspaceId}/widgets" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "POST", + "url": "/api/v1/workspaces", + "raw_url": "/api/v1/workspaces" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "POST", + "url": "/api/v1/workspaces/{param}/modules", + "raw_url": "/api/v1/workspaces/${workspaceId}/modules" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "POST", + "url": "/api/v1/workspaces/{param}/users", + "raw_url": "/api/v1/workspaces/${workspaceId}/users" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "POST", + "url": "/api/v1/workspaces/{param}/widgets", + "raw_url": "/api/v1/workspaces/${workspaceId}/widgets" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "POST", + "url": "/api/v1/workspaces/{param}/set-default", + "raw_url": "/api/v1/workspaces/${workspaceId}/set-default" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "PUT", + "url": "/api/v1/workspaces/{param}", + "raw_url": "/api/v1/workspaces/${id}" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "PUT", + "url": "/api/v1/workspaces/{param}/widgets/{param}", + "raw_url": "/api/v1/workspaces/${workspaceId}/widgets/${widgetId}" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "DELETE", + "url": "/api/v1/workspaces/{param}", + "raw_url": "/api/v1/workspaces/${id}" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "DELETE", + "url": "/api/v1/workspaces/{param}/users/{param}", + "raw_url": "/api/v1/workspaces/${workspaceId}/users/${userId}" + }, + { + "file": "api/hooks/workspaces.ts", + "method": "DELETE", + "url": "/api/v1/workspaces/{param}/widgets/{param}", + "raw_url": "/api/v1/workspaces/${workspaceId}/widgets/${widgetId}" + } + ], + "backend_endpoints": [ + { + "file": "routes/saved_filters.py", + "method": "DELETE", + "path": "/{filter_id}" + }, + { + "file": "routes/api_tokens.py", + "method": "DELETE", + "path": "/{token_id}" + }, + { + "file": "routes/sequences.py", + "method": "PATCH", + "path": "/{sequence_id}" + }, + { + "file": "routes/sequences.py", + "method": "DELETE", + "path": "/{sequence_id}" + }, + { + "file": "routes/companies.py", + "method": "GET", + "path": "/export" + }, + { + "file": "routes/companies.py", + "method": "GET", + "path": "/{company_id}" + }, + { + "file": "routes/companies.py", + "method": "PUT", + "path": "/{company_id}" + }, + { + "file": "routes/companies.py", + "method": "DELETE", + "path": "/{company_id}" + }, + { + "file": "routes/companies.py", + "method": "POST", + "path": "/{company_id}/contacts/{contact_id}" + }, + { + "file": "routes/companies.py", + "method": "DELETE", + "path": "/{company_id}/contacts/{contact_id}" + }, + { + "file": "routes/companies.py", + "method": "GET", + "path": "/{company_id}/emails" + }, + { + "file": "routes/contact_folders.py", + "method": "PUT", + "path": "/{folder_id}" + }, + { + "file": "routes/contact_folders.py", + "method": "DELETE", + "path": "/{folder_id}" + }, + { + "file": "routes/contact_folders.py", + "method": "PUT", + "path": "/{folder_id}/reorder" + }, + { + "file": "routes/contact_folders.py", + "method": "PUT", + "path": "/contacts/{contact_id}/move" + }, + { + "file": "routes/delegations.py", + "method": "PUT", + "path": "/{delegation_id}" + }, + { + "file": "routes/delegations.py", + "method": "DELETE", + "path": "/{delegation_id}" + }, + { + "file": "routes/delegations.py", + "method": "GET", + "path": "/active" + }, + { + "file": "routes/owner_transfer.py", + "method": "POST", + "path": "/transfer" + }, + { + "file": "routes/contacts.py", + "method": "GET", + "path": "/export" + }, + { + "file": "routes/contacts.py", + "method": "GET", + "path": "/merge-history" + }, + { + "file": "routes/contacts.py", + "method": "GET", + "path": "/{contact_id}" + }, + { + "file": "routes/contacts.py", + "method": "PUT", + "path": "/{contact_id}" + }, + { + "file": "routes/contacts.py", + "method": "DELETE", + "path": "/{contact_id}" + }, + { + "file": "routes/contacts.py", + "method": "GET", + "path": "/{contact_id}/persons" + }, + { + "file": "routes/contacts.py", + "method": "POST", + "path": "/{contact_id}/persons" + }, + { + "file": "routes/contacts.py", + "method": "PUT", + "path": "/{contact_id}/persons/{person_id}" + }, + { + "file": "routes/contacts.py", + "method": "DELETE", + "path": "/{contact_id}/persons/{person_id}" + }, + { + "file": "routes/contacts.py", + "method": "POST", + "path": "/duplicates" + }, + { + "file": "routes/contacts.py", + "method": "POST", + "path": "/merge" + }, + { + "file": "routes/guests.py", + "method": "POST", + "path": "/invite" + }, + { + "file": "routes/guests.py", + "method": "DELETE", + "path": "/{guest_id}" + }, + { + "file": "routes/tenants.py", + "method": "GET", + "path": "/{tenant_id}/users" + }, + { + "file": "routes/tenants.py", + "method": "POST", + "path": "/{tenant_id}/users" + }, + { + "file": "routes/custom_field_definitions.py", + "method": "GET", + "path": "/definitions" + }, + { + "file": "routes/custom_field_definitions.py", + "method": "POST", + "path": "/definitions" + }, + { + "file": "routes/custom_field_definitions.py", + "method": "PATCH", + "path": "/definitions/{definition_id}" + }, + { + "file": "routes/custom_field_definitions.py", + "method": "DELETE", + "path": "/definitions/{definition_id}" + }, + { + "file": "routes/saved_views.py", + "method": "PUT", + "path": "/{view_id}" + }, + { + "file": "routes/saved_views.py", + "method": "DELETE", + "path": "/{view_id}" + }, + { + "file": "routes/roles.py", + "method": "GET", + "path": "/permissions" + }, + { + "file": "routes/roles.py", + "method": "PATCH", + "path": "/{role_id}" + }, + { + "file": "routes/roles.py", + "method": "DELETE", + "path": "/{role_id}" + }, + { + "file": "routes/groups.py", + "method": "GET", + "path": "/{group_id}" + }, + { + "file": "routes/groups.py", + "method": "PATCH", + "path": "/{group_id}" + }, + { + "file": "routes/groups.py", + "method": "DELETE", + "path": "/{group_id}" + }, + { + "file": "routes/groups.py", + "method": "GET", + "path": "/{group_id}/members" + }, + { + "file": "routes/groups.py", + "method": "POST", + "path": "/{group_id}/members" + }, + { + "file": "routes/groups.py", + "method": "DELETE", + "path": "/{group_id}/members/{user_id}" + }, + { + "file": "routes/groups.py", + "method": "GET", + "path": "/user/{user_id}" + }, + { + "file": "routes/workspaces.py", + "method": "GET", + "path": "/my" + }, + { + "file": "routes/workspaces.py", + "method": "GET", + "path": "/context" + }, + { + "file": "routes/workspaces.py", + "method": "GET", + "path": "/{workspace_id}" + }, + { + "file": "routes/workspaces.py", + "method": "PUT", + "path": "/{workspace_id}" + }, + { + "file": "routes/workspaces.py", + "method": "DELETE", + "path": "/{workspace_id}" + }, + { + "file": "routes/workspaces.py", + "method": "POST", + "path": "/{workspace_id}/modules" + }, + { + "file": "routes/workspaces.py", + "method": "POST", + "path": "/{workspace_id}/users" + }, + { + "file": "routes/workspaces.py", + "method": "DELETE", + "path": "/{workspace_id}/users/{user_id}" + }, + { + "file": "routes/workspaces.py", + "method": "GET", + "path": "/{workspace_id}/widgets" + }, + { + "file": "routes/workspaces.py", + "method": "POST", + "path": "/{workspace_id}/widgets" + }, + { + "file": "routes/workspaces.py", + "method": "PUT", + "path": "/{workspace_id}/widgets/{widget_id}" + }, + { + "file": "routes/workspaces.py", + "method": "DELETE", + "path": "/{workspace_id}/widgets/{widget_id}" + }, + { + "file": "routes/workspaces.py", + "method": "POST", + "path": "/{workspace_id}/set-default" + }, + { + "file": "routes/dashboard.py", + "method": "GET", + "path": "/widgets" + }, + { + "file": "routes/dashboard.py", + "method": "GET", + "path": "/counts" + }, + { + "file": "routes/backups.py", + "method": "POST", + "path": "/{backup_id}/restore" + }, + { + "file": "routes/backups.py", + "method": "DELETE", + "path": "/{backup_id}" + }, + { + "file": "routes/custom_fields.py", + "method": "GET", + "path": "/{contact_id}/custom-fields" + }, + { + "file": "routes/custom_fields.py", + "method": "PATCH", + "path": "/{contact_id}/custom-fields" + }, + { + "file": "routes/auth.py", + "method": "POST", + "path": "/login" + }, + { + "file": "routes/auth.py", + "method": "POST", + "path": "/logout" + }, + { + "file": "routes/auth.py", + "method": "GET", + "path": "/me" + }, + { + "file": "routes/auth.py", + "method": "GET", + "path": "/me/permissions" + }, + { + "file": "routes/auth.py", + "method": "POST", + "path": "/switch-tenant" + }, + { + "file": "routes/auth.py", + "method": "POST", + "path": "/password-reset/request" + }, + { + "file": "routes/auth.py", + "method": "POST", + "path": "/password-reset/confirm" + }, + { + "file": "routes/users.py", + "method": "GET", + "path": "/{user_id}" + }, + { + "file": "routes/users.py", + "method": "PATCH", + "path": "/{user_id}" + }, + { + "file": "routes/users.py", + "method": "DELETE", + "path": "/{user_id}" + }, + { + "file": "routes/users.py", + "method": "GET", + "path": "/me/menu-order" + }, + { + "file": "routes/users.py", + "method": "PUT", + "path": "/me/menu-order" + }, + { + "file": "routes/bank_accounts.py", + "method": "PATCH", + "path": "/{account_id}" + }, + { + "file": "routes/bank_accounts.py", + "method": "DELETE", + "path": "/{account_id}" + }, + { + "file": "routes/webhooks.py", + "method": "GET", + "path": "/{webhook_id}" + }, + { + "file": "routes/webhooks.py", + "method": "PATCH", + "path": "/{webhook_id}" + }, + { + "file": "routes/webhooks.py", + "method": "DELETE", + "path": "/{webhook_id}" + }, + { + "file": "routes/webhooks.py", + "method": "POST", + "path": "/{webhook_id}/test" + }, + { + "file": "routes/addresses.py", + "method": "PATCH", + "path": "/{address_id}" + }, + { + "file": "routes/addresses.py", + "method": "DELETE", + "path": "/{address_id}" + }, + { + "file": "routes/contact_folder_permissions.py", + "method": "GET", + "path": "/{folder_id}/permissions" + }, + { + "file": "routes/contact_folder_permissions.py", + "method": "POST", + "path": "/{folder_id}/permissions" + }, + { + "file": "routes/contact_folder_permissions.py", + "method": "PUT", + "path": "/{folder_id}/permissions/{permission_id}" + }, + { + "file": "routes/contact_folder_permissions.py", + "method": "DELETE", + "path": "/{folder_id}/permissions/{permission_id}" + }, + { + "file": "routes/contact_folder_permissions.py", + "method": "GET", + "path": "/{folder_id}/access" + }, + { + "file": "routes/taxes.py", + "method": "PATCH", + "path": "/{tax_id}" + }, + { + "file": "routes/taxes.py", + "method": "DELETE", + "path": "/{tax_id}" + }, + { + "file": "routes/notifications.py", + "method": "PATCH", + "path": "/{notification_id}/read" + }, + { + "file": "routes/notifications.py", + "method": "GET", + "path": "/unread-count" + }, + { + "file": "routes/notifications.py", + "method": "GET", + "path": "/types" + }, + { + "file": "routes/notifications.py", + "method": "GET", + "path": "/preferences" + }, + { + "file": "routes/notifications.py", + "method": "PATCH", + "path": "/preferences/{type_key}" + }, + { + "file": "routes/import_export.py", + "method": "POST", + "path": "/import" + }, + { + "file": "routes/import_export.py", + "method": "POST", + "path": "/import/preview" + }, + { + "file": "routes/import_export.py", + "method": "POST", + "path": "/import/validate" + }, + { + "file": "routes/import_export.py", + "method": "GET", + "path": "/import/status/{job_id}" + }, + { + "file": "routes/import_export.py", + "method": "POST", + "path": "/import/contacts" + }, + { + "file": "routes/import_export.py", + "method": "POST", + "path": "/import/companies" + }, + { + "file": "routes/import_export.py", + "method": "GET", + "path": "/export" + }, + { + "file": "routes/attachments.py", + "method": "GET", + "path": "/{attachment_id}" + }, + { + "file": "routes/attachments.py", + "method": "DELETE", + "path": "/{attachment_id}" + }, + { + "file": "routes/health.py", + "method": "GET", + "path": "/health/live" + }, + { + "file": "routes/health.py", + "method": "GET", + "path": "/health/ready" + }, + { + "file": "routes/health.py", + "method": "GET", + "path": "/api/v1/health" + }, + { + "file": "routes/workflows.py", + "method": "GET", + "path": "/instances" + }, + { + "file": "routes/workflows.py", + "method": "GET", + "path": "/{workflow_id}" + }, + { + "file": "routes/workflows.py", + "method": "PATCH", + "path": "/{workflow_id}" + }, + { + "file": "routes/workflows.py", + "method": "DELETE", + "path": "/{workflow_id}" + }, + { + "file": "routes/workflows.py", + "method": "POST", + "path": "/{workflow_id}/instances" + }, + { + "file": "routes/workflows.py", + "method": "GET", + "path": "/instances/{instance_id}" + }, + { + "file": "routes/workflows.py", + "method": "POST", + "path": "/instances/{instance_id}/advance" + }, + { + "file": "routes/workflows.py", + "method": "POST", + "path": "/instances/{instance_id}/cancel" + }, + { + "file": "routes/outbox.py", + "method": "GET", + "path": "/stats" + }, + { + "file": "routes/outbox.py", + "method": "GET", + "path": "/failed" + }, + { + "file": "routes/outbox.py", + "method": "POST", + "path": "/replay/{event_id}" + }, + { + "file": "routes/outbox.py", + "method": "POST", + "path": "/replay-all" + }, + { + "file": "routes/outbox.py", + "method": "GET", + "path": "/consumer-registry" + }, + { + "file": "routes/outbox.py", + "method": "POST", + "path": "/recover-stuck" + }, + { + "file": "routes/outbox.py", + "method": "POST", + "path": "/cleanup-published" + }, + { + "file": "routes/ai_copilot.py", + "method": "POST", + "path": "/query" + }, + { + "file": "routes/ai_copilot.py", + "method": "POST", + "path": "/execute" + }, + { + "file": "routes/ai_copilot.py", + "method": "GET", + "path": "/history" + }, + { + "file": "routes/metrics.py", + "method": "GET", + "path": "/api/v1/metrics" + }, + { + "file": "routes/entity_history.py", + "method": "GET", + "path": "/{entity_type}/{entity_id}" + }, + { + "file": "routes/entity_history.py", + "method": "POST", + "path": "/restore" + }, + { + "file": "routes/entity_history.py", + "method": "POST", + "path": "/undo/{entity_type}/{entity_id}" + }, + { + "file": "routes/entity_history.py", + "method": "GET", + "path": "/trash" + }, + { + "file": "routes/entity_history.py", + "method": "POST", + "path": "/bulk-restore" + }, + { + "file": "routes/entity_history.py", + "method": "POST", + "path": "/retention/archive" + }, + { + "file": "routes/policies.py", + "method": "GET", + "path": "/{entity_type}" + }, + { + "file": "routes/policies.py", + "method": "PUT", + "path": "/{policy_id}" + }, + { + "file": "routes/policies.py", + "method": "DELETE", + "path": "/{policy_id}" + }, + { + "file": "routes/permission_templates.py", + "method": "PUT", + "path": "/{template_id}" + }, + { + "file": "routes/permission_templates.py", + "method": "DELETE", + "path": "/{template_id}" + }, + { + "file": "routes/permission_templates.py", + "method": "POST", + "path": "/apply" + }, + { + "file": "routes/currencies.py", + "method": "PATCH", + "path": "/{currency_id}" + }, + { + "file": "routes/currencies.py", + "method": "DELETE", + "path": "/{currency_id}" + }, + { + "file": "routes/user_preferences.py", + "method": "GET", + "path": "/{key}" + }, + { + "file": "routes/user_preferences.py", + "method": "PUT", + "path": "/{key}" + }, + { + "file": "routes/user_preferences.py", + "method": "DELETE", + "path": "/{key}" + }, + { + "file": "routes/entity_permissions.py", + "method": "GET", + "path": "/{entity_type}/{entity_id}" + }, + { + "file": "routes/entity_permissions.py", + "method": "POST", + "path": "/{entity_type}/{entity_id}" + }, + { + "file": "routes/entity_permissions.py", + "method": "PUT", + "path": "/{entity_type}/{entity_id}/{permission_id}" + }, + { + "file": "routes/entity_permissions.py", + "method": "DELETE", + "path": "/{entity_type}/{entity_id}/{permission_id}" + }, + { + "file": "routes/entity_permissions.py", + "method": "GET", + "path": "/{entity_type}/{entity_id}/access" + }, + { + "file": "routes/entity_permissions.py", + "method": "GET", + "path": "/all" + }, + { + "file": "routes/entity_permissions.py", + "method": "GET", + "path": "/registry" + }, + { + "file": "routes/entity_permissions.py", + "method": "POST", + "path": "/bulk" + }, + { + "file": "routes/entity_permissions.py", + "method": "POST", + "path": "/bulk/unshare" + }, + { + "file": "routes/entity_permissions.py", + "method": "GET", + "path": "/analytics" + }, + { + "file": "routes/plugins.py", + "method": "GET", + "path": "/manifest" + }, + { + "file": "routes/plugins.py", + "method": "GET", + "path": "/updates" + }, + { + "file": "routes/plugins.py", + "method": "GET", + "path": "/active-manifests" + }, + { + "file": "routes/plugins.py", + "method": "GET", + "path": "/{name}/config" + }, + { + "file": "routes/plugins.py", + "method": "PATCH", + "path": "/{name}/config" + }, + { + "file": "routes/plugins.py", + "method": "POST", + "path": "/{name}/install" + }, + { + "file": "routes/plugins.py", + "method": "POST", + "path": "/{name}/activate" + }, + { + "file": "routes/plugins.py", + "method": "POST", + "path": "/{name}/deactivate" + }, + { + "file": "routes/plugins.py", + "method": "DELETE", + "path": "/{name}" + }, + { + "file": "routes/plugins.py", + "method": "POST", + "path": "/upload" + }, + { + "file": "routes/plugins.py", + "method": "POST", + "path": "/install-url" + }, + { + "file": "routes/plugins.py", + "method": "POST", + "path": "/install-marketplace" + }, + { + "file": "plugins/builtins/mcp_client/routes.py", + "method": "GET", + "path": "/servers", + "is_plugin": true + }, + { + "file": "plugins/builtins/mcp_client/routes.py", + "method": "POST", + "path": "/servers", + "is_plugin": true + }, + { + "file": "plugins/builtins/mcp_client/routes.py", + "method": "PATCH", + "path": "/servers/{server_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mcp_client/routes.py", + "method": "DELETE", + "path": "/servers/{server_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mcp_client/routes.py", + "method": "GET", + "path": "/servers/{server_id}/tools", + "is_plugin": true + }, + { + "file": "plugins/builtins/mcp_client/routes.py", + "method": "POST", + "path": "/servers/{server_id}/execute", + "is_plugin": true + }, + { + "file": "plugins/builtins/forgejo_error_reporter/routes.py", + "method": "GET", + "path": "/status", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "POST", + "path": "/", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "GET", + "path": "/runs/recent", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "GET", + "path": "/miniapps", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "POST", + "path": "/miniapps", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "DELETE", + "path": "/miniapps/{app_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "GET", + "path": "/settings", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "PATCH", + "path": "/settings", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "GET", + "path": "/{automation_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "PATCH", + "path": "/{automation_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "DELETE", + "path": "/{automation_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "POST", + "path": "/{automation_id}/execute", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "POST", + "path": "/{automation_id}/dry-run", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "GET", + "path": "/{automation_id}/runs", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "GET", + "path": "/{automation_id}/versions", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "POST", + "path": "/{automation_id}/versions/{version_id}/restore", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "GET", + "path": "/subtasks", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "POST", + "path": "/subtasks", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "GET", + "path": "/subtasks/{subtask_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "PATCH", + "path": "/subtasks/{subtask_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "POST", + "path": "/subtasks/{subtask_id}/cancel", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "POST", + "path": "/subtasks/{subtask_id}/wait", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/routes.py", + "method": "POST", + "path": "/subtasks/aggregate", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "POST", + "path": "/", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "GET", + "path": "/tools", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "GET", + "path": "/runs/recent", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "GET", + "path": "/{agent_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "PATCH", + "path": "/{agent_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "DELETE", + "path": "/{agent_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "POST", + "path": "/{agent_id}/execute", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "POST", + "path": "/{agent_id}/test-run", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "GET", + "path": "/{agent_id}/runs", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "GET", + "path": "/{agent_id}/versions", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "POST", + "path": "/{agent_id}/versions/{version_id}/restore", + "is_plugin": true + }, + { + "file": "plugins/builtins/automation/agent_routes.py", + "method": "POST", + "path": "/{id}/send-message", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "GET", + "path": "/folders", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "POST", + "path": "/folders", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "PATCH", + "path": "/folders/{folder_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "DELETE", + "path": "/folders/{folder_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "POST", + "path": "/files/upload", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "GET", + "path": "/files/{file_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "GET", + "path": "/files", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "GET", + "path": "/folders/{folder_id}/files", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "PATCH", + "path": "/files/{file_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "DELETE", + "path": "/files/{file_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "POST", + "path": "/files/{file_id}/restore", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "GET", + "path": "/files/{file_id}/preview", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "GET", + "path": "/files/{file_id}/download", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "POST", + "path": "/files/{file_id}/edit-session", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "POST", + "path": "/files/{file_id}/share", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "DELETE", + "path": "/files/{file_id}/share", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "GET", + "path": "/search", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "GET", + "path": "/shared-with-me", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "POST", + "path": "/files/bulk-move", + "is_plugin": true + }, + { + "file": "plugins/builtins/dms/routes.py", + "method": "POST", + "path": "/files/bulk-delete", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/accounts", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/accounts", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/accounts/shared", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "PATCH", + "path": "/accounts/{account_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "DELETE", + "path": "/accounts/{account_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/accounts/{account_id}/users", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/accounts/{account_id}/delegates", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/accounts/{account_id}/send-permissions", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/accounts/{account_id}/test-connection", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/accounts/{account_id}/sync", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/folders", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/folders", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "PATCH", + "path": "/folders/{folder_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "DELETE", + "path": "/folders/{folder_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/folders/{folder_id}/empty", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/folders/{folder_id}/sync", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/upload-attachment", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/send", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/search", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/threads", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/templates", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/templates", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/templates/substitute", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/signatures", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/signatures", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/rules", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/rules", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "DELETE", + "path": "/rules/{rule_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/vacation", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/vacation/test-dedup", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/pgp/keys", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/pgp/keys", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/pgp/encrypt", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/labels", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/labels", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/contacts/{contact_id}/pgp-key", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/{mail_id}/apply-rules", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/{mail_id}/reply", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/{mail_id}/forward", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "PATCH", + "path": "/{mail_id}/flags", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/{mail_id}/attachments/{att_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/{mail_id}/link", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/{mail_id}/create-event", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/{mail_id}/labels", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/drafts", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "PUT", + "path": "/drafts/{mail_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "DELETE", + "path": "/{mail_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "POST", + "path": "/{mail_id}/move", + "is_plugin": true + }, + { + "file": "plugins/builtins/mail/routes.py", + "method": "GET", + "path": "/{mail_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/tasks/routes.py", + "method": "GET", + "path": "/{task_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/tasks/routes.py", + "method": "PATCH", + "path": "/{task_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/tasks/routes.py", + "method": "DELETE", + "path": "/{task_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/tasks/routes.py", + "method": "POST", + "path": "/{task_id}/assign", + "is_plugin": true + }, + { + "file": "plugins/builtins/tasks/routes.py", + "method": "POST", + "path": "/{task_id}/status", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "GET", + "path": "/calendar/entries", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "POST", + "path": "/calendar/entries", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "GET", + "path": "/calendar/entries/export", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "GET", + "path": "/calendar/entries/{entry_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "PATCH", + "path": "/calendar/entries/{entry_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "DELETE", + "path": "/calendar/entries/{entry_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "POST", + "path": "/calendar/entries/{entry_id}/link", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "POST", + "path": "/calendar/entries/{entry_id}/subtasks", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "PATCH", + "path": "/calendar/entries/{entry_id}/subtasks/{sub_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "POST", + "path": "/calendar/entries/bulk", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "GET", + "path": "/calendar/kanban", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "GET", + "path": "/calendar/{calendar_id}/ics-feed", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "POST", + "path": "/calendar/import", + "is_plugin": true + }, + { + "file": "plugins/builtins/calendar/routes.py", + "method": "POST", + "path": "/calendar/entries/{entry_id}/book-resource", + "is_plugin": true + }, + { + "file": "plugins/builtins/unified_search/routes.py", + "method": "GET", + "path": "/facets", + "is_plugin": true + }, + { + "file": "plugins/builtins/unified_search/routes.py", + "method": "GET", + "path": "/suggest", + "is_plugin": true + }, + { + "file": "plugins/builtins/unified_search/routes.py", + "method": "POST", + "path": "/similar", + "is_plugin": true + }, + { + "file": "plugins/builtins/unified_search/routes.py", + "method": "POST", + "path": "/reindex", + "is_plugin": true + }, + { + "file": "plugins/builtins/unified_search/routes.py", + "method": "POST", + "path": "/rebuild/{entity_type}/{entity_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/unified_search/routes.py", + "method": "POST", + "path": "/purge/{entity_type}/{entity_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/unified_search/routes.py", + "method": "GET", + "path": "/providers", + "is_plugin": true + }, + { + "file": "plugins/builtins/unified_search/routes.py", + "method": "POST", + "path": "/providers/{entity_type}/toggle", + "is_plugin": true + }, + { + "file": "plugins/builtins/unified_search/routes.py", + "method": "GET", + "path": "/stats", + "is_plugin": true + }, + { + "file": "plugins/builtins/agent_memory/routes.py", + "method": "GET", + "path": "/search", + "is_plugin": true + }, + { + "file": "plugins/builtins/agent_memory/routes.py", + "method": "PATCH", + "path": "/{memory_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/agent_memory/routes.py", + "method": "DELETE", + "path": "/{memory_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/marketplace/routes.py", + "method": "GET", + "path": "/listings", + "is_plugin": true + }, + { + "file": "plugins/builtins/marketplace/routes.py", + "method": "GET", + "path": "/listings/{name}", + "is_plugin": true + }, + { + "file": "plugins/builtins/marketplace/routes.py", + "method": "POST", + "path": "/install/{name}", + "is_plugin": true + }, + { + "file": "plugins/builtins/marketplace/routes.py", + "method": "POST", + "path": "/verify/{name}", + "is_plugin": true + }, + { + "file": "plugins/builtins/marketplace/routes.py", + "method": "GET", + "path": "/categories", + "is_plugin": true + }, + { + "file": "plugins/builtins/graph_rag/routes.py", + "method": "POST", + "path": "/relationships", + "is_plugin": true + }, + { + "file": "plugins/builtins/graph_rag/routes.py", + "method": "GET", + "path": "/relationships", + "is_plugin": true + }, + { + "file": "plugins/builtins/graph_rag/routes.py", + "method": "POST", + "path": "/traverse", + "is_plugin": true + }, + { + "file": "plugins/builtins/graph_rag/routes.py", + "method": "DELETE", + "path": "/relationships/{relationship_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "GET", + "path": "/presets", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "POST", + "path": "/presets/generate", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "GET", + "path": "/templates", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "POST", + "path": "/templates", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "GET", + "path": "/templates/{template_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "PUT", + "path": "/templates/{template_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "DELETE", + "path": "/templates/{template_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "POST", + "path": "/generate", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "POST", + "path": "/generate/async", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "GET", + "path": "/{report_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/report_generator/routes.py", + "method": "GET", + "path": "/{report_id}/download", + "is_plugin": true + }, + { + "file": "plugins/builtins/entity_links/routes.py", + "method": "POST", + "path": "/files/{file_id}/link", + "is_plugin": true + }, + { + "file": "plugins/builtins/entity_links/routes.py", + "method": "DELETE", + "path": "/files/{file_id}/link", + "is_plugin": true + }, + { + "file": "plugins/builtins/entity_links/routes.py", + "method": "GET", + "path": "/files/{file_id}/links", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "GET", + "path": "/conversations", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "POST", + "path": "/conversations", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "GET", + "path": "/conversations/{conversation_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "PATCH", + "path": "/conversations/{conversation_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "DELETE", + "path": "/conversations/{conversation_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "POST", + "path": "/conversations/{conversation_id}/pin", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "DELETE", + "path": "/conversations/{conversation_id}/pin", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "POST", + "path": "/conversations/{conversation_id}/mute", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "DELETE", + "path": "/conversations/{conversation_id}/mute", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "POST", + "path": "/conversations/{conversation_id}/participants", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "DELETE", + "path": "/conversations/{conversation_id}/participants/{participant_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "PATCH", + "path": "/conversations/{conversation_id}/participants/{participant_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "GET", + "path": "/conversations/{conversation_id}/messages", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "POST", + "path": "/conversations/{conversation_id}/messages", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "PATCH", + "path": "/messages/{message_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "DELETE", + "path": "/messages/{message_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "POST", + "path": "/messages/{message_id}/attachments", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "POST", + "path": "/messages/{message_id}/reactions", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "DELETE", + "path": "/messages/{message_id}/reactions/{emoji}", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "POST", + "path": "/conversations/{conversation_id}/read", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "GET", + "path": "/miniapps", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "POST", + "path": "/conversations/{conversation_id}/miniapps", + "is_plugin": true + }, + { + "file": "plugins/builtins/kommunikation/routes.py", + "method": "GET", + "path": "/block-types", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_proactive/routes.py", + "method": "POST", + "path": "/context", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_proactive/routes.py", + "method": "GET", + "path": "/suggestions", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_proactive/routes.py", + "method": "POST", + "path": "/suggestions/{suggestion_id}/dismiss", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_proactive/routes.py", + "method": "POST", + "path": "/suggestions/{suggestion_id}/act", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_proactive/routes.py", + "method": "GET", + "path": "/suggestions/stream", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_proactive/routes.py", + "method": "GET", + "path": "/settings", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_proactive/routes.py", + "method": "PUT", + "path": "/settings", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_proactive/routes.py", + "method": "GET", + "path": "/stats", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/external_api.py", + "method": "POST", + "path": "/{agent_id}/run", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/external_api.py", + "method": "GET", + "path": "/{agent_id}/status", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/external_api.py", + "method": "POST", + "path": "/{agent_id}/stream", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/providers", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "POST", + "path": "/providers", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "PUT", + "path": "/providers/{provider_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "DELETE", + "path": "/providers/{provider_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/models", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "POST", + "path": "/models", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "PUT", + "path": "/models/{model_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "DELETE", + "path": "/models/{model_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/presets", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "POST", + "path": "/presets", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "PUT", + "path": "/presets/{preset_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "DELETE", + "path": "/presets/{preset_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/agents", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "POST", + "path": "/agents", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "PUT", + "path": "/agents/{agent_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "DELETE", + "path": "/agents/{agent_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/tools", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/sessions", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "POST", + "path": "/sessions", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "PUT", + "path": "/sessions/{session_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "DELETE", + "path": "/sessions/{session_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/sessions/{session_id}/messages", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "POST", + "path": "/sessions/{session_id}/stream", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/folders", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "POST", + "path": "/folders", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "PUT", + "path": "/folders/{folder_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "DELETE", + "path": "/folders/{folder_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "POST", + "path": "/sessions/{session_id}/attachments", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/sessions/{session_id}/attachments", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_assistant/routes.py", + "method": "GET", + "path": "/attachments/{attachment_id}/download", + "is_plugin": true + }, + { + "file": "plugins/builtins/tags/routes.py", + "method": "PATCH", + "path": "/{tag_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/tags/routes.py", + "method": "POST", + "path": "/assign", + "is_plugin": true + }, + { + "file": "plugins/builtins/tags/routes.py", + "method": "DELETE", + "path": "/assign", + "is_plugin": true + }, + { + "file": "plugins/builtins/tags/routes.py", + "method": "DELETE", + "path": "/{tag_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/tags/routes.py", + "method": "POST", + "path": "/bulk-assign", + "is_plugin": true + }, + { + "file": "plugins/builtins/tags/routes.py", + "method": "GET", + "path": "/{tag_id}/entities", + "is_plugin": true + }, + { + "file": "plugins/builtins/permissions/routes.py", + "method": "GET", + "path": "/files/{file_id}/permissions", + "is_plugin": true + }, + { + "file": "plugins/builtins/permissions/routes.py", + "method": "POST", + "path": "/files/{file_id}/permissions", + "is_plugin": true + }, + { + "file": "plugins/builtins/permissions/routes.py", + "method": "DELETE", + "path": "/files/{file_id}/permissions/{user_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/permissions/routes.py", + "method": "POST", + "path": "/files/{file_id}/share-link", + "is_plugin": true + }, + { + "file": "plugins/builtins/permissions/routes.py", + "method": "DELETE", + "path": "/share-links/{link_id}", + "is_plugin": true + }, + { + "file": "plugins/builtins/permissions/public_routes.py", + "method": "GET", + "path": "/{token}", + "is_plugin": true + }, + { + "file": "plugins/builtins/permissions/public_routes.py", + "method": "POST", + "path": "/{token}/verify", + "is_plugin": true + }, + { + "file": "plugins/builtins/permissions/public_routes.py", + "method": "GET", + "path": "/{token}/download", + "is_plugin": true + }, + { + "file": "plugins/builtins/mcp_server/routes.py", + "method": "GET", + "path": "/tools", + "is_plugin": true + }, + { + "file": "plugins/builtins/mcp_server/routes.py", + "method": "POST", + "path": "/tools/{tool_name}/execute", + "is_plugin": true + }, + { + "file": "plugins/builtins/mcp_server/routes.py", + "method": "GET", + "path": "/config", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_ui_control/routes.py", + "method": "POST", + "path": "/command", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_ui_control/routes.py", + "method": "GET", + "path": "/command/{command_id}/status", + "is_plugin": true + }, + { + "file": "plugins/builtins/ai_ui_control/routes.py", + "method": "GET", + "path": "/online-users", + "is_plugin": true + } + ], + "issues": [ + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/webhooks{param}", + "file": "api/webhooks.ts", + "severity": "HIGH", + "message": "Frontend calls GET /webhooks{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/webhooks/{param}", + "file": "api/webhooks.ts", + "severity": "HIGH", + "message": "Frontend calls GET /webhooks/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/webhooks", + "file": "api/webhooks.ts", + "severity": "HIGH", + "message": "Frontend calls POST /webhooks but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/webhooks/{param}/test", + "file": "api/webhooks.ts", + "severity": "HIGH", + "message": "Frontend calls POST /webhooks/{param}/test but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/webhooks/{param}", + "file": "api/webhooks.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /webhooks/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/webhooks/{param}", + "file": "api/webhooks.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /webhooks/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/notifications/unread-count", + "file": "api/notifications.ts", + "severity": "HIGH", + "message": "Frontend calls GET /notifications/unread-count but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/notifications/types", + "file": "api/notifications.ts", + "severity": "HIGH", + "message": "Frontend calls GET /notifications/types but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/notifications/preferences", + "file": "api/notifications.ts", + "severity": "HIGH", + "message": "Frontend calls GET /notifications/preferences but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/notifications/{param}/read", + "file": "api/notifications.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /notifications/{param}/read but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/notifications/preferences/{param}", + "file": "api/notifications.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /notifications/preferences/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/notifications/{param}", + "file": "api/notifications.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /notifications/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/calendars", + "file": "api/calendar.ts", + "severity": "HIGH", + "message": "Frontend calls GET /calendars but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/calendars/{param}/permissions", + "file": "api/calendar.ts", + "severity": "HIGH", + "message": "Frontend calls GET /calendars/{param}/permissions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/calendar/entries${qs ", + "file": "api/calendar.ts", + "severity": "HIGH", + "message": "Frontend calls GET /calendar/entries${qs but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/calendars", + "file": "api/calendar.ts", + "severity": "HIGH", + "message": "Frontend calls POST /calendars but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/calendars/{param}/share", + "file": "api/calendar.ts", + "severity": "HIGH", + "message": "Frontend calls POST /calendars/{param}/share but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/resources", + "file": "api/calendar.ts", + "severity": "HIGH", + "message": "Frontend calls POST /resources but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/calendars/{param}", + "file": "api/calendar.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /calendars/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/calendars/{param}", + "file": "api/calendar.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /calendars/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/user/preferences", + "file": "api/userPreferences.ts", + "severity": "HIGH", + "message": "Frontend calls GET /user/preferences but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/user/preferences/{param}", + "file": "api/userPreferences.ts", + "severity": "HIGH", + "message": "Frontend calls GET /user/preferences/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/user/preferences/{param}", + "file": "api/userPreferences.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /user/preferences/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/user/preferences/{param}", + "file": "api/userPreferences.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /user/preferences/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/plugins", + "file": "api/plugins.ts", + "severity": "HIGH", + "message": "Frontend calls GET /plugins but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/plugins/{param}/install", + "file": "api/plugins.ts", + "severity": "HIGH", + "message": "Frontend calls POST /plugins/{param}/install but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/plugins/{param}/activate", + "file": "api/plugins.ts", + "severity": "HIGH", + "message": "Frontend calls POST /plugins/{param}/activate but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/plugins/{param}/deactivate", + "file": "api/plugins.ts", + "severity": "HIGH", + "message": "Frontend calls POST /plugins/{param}/deactivate but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/plugins/{param}", + "file": "api/plugins.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /plugins/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/groups", + "file": "api/groups.ts", + "severity": "HIGH", + "message": "Frontend calls GET /groups but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/groups/{param}/members", + "file": "api/groups.ts", + "severity": "HIGH", + "message": "Frontend calls GET /groups/{param}/members but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/groups/user/{param}", + "file": "api/groups.ts", + "severity": "HIGH", + "message": "Frontend calls GET /groups/user/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/groups", + "file": "api/groups.ts", + "severity": "HIGH", + "message": "Frontend calls POST /groups but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/groups/{param}/members", + "file": "api/groups.ts", + "severity": "HIGH", + "message": "Frontend calls POST /groups/{param}/members but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/groups/{param}", + "file": "api/groups.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /groups/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/groups/{param}", + "file": "api/groups.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /groups/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/groups/{param}/members/{param}", + "file": "api/groups.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /groups/{param}/members/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/permissions/{param}/{param}", + "file": "api/entityPermissions.ts", + "severity": "HIGH", + "message": "Frontend calls GET /permissions/{param}/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/permissions/{param}/{param}/access", + "file": "api/entityPermissions.ts", + "severity": "HIGH", + "message": "Frontend calls GET /permissions/{param}/{param}/access but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/permissions/all", + "file": "api/entityPermissions.ts", + "severity": "HIGH", + "message": "Frontend calls GET /permissions/all but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/permissions/registry", + "file": "api/entityPermissions.ts", + "severity": "HIGH", + "message": "Frontend calls GET /permissions/registry but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/permissions/{param}/{param}", + "file": "api/entityPermissions.ts", + "severity": "HIGH", + "message": "Frontend calls POST /permissions/{param}/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/permissions/{param}/{param}/{param}", + "file": "api/entityPermissions.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /permissions/{param}/{param}/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/permissions/{param}/{param}/{param}", + "file": "api/entityPermissions.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /permissions/{param}/{param}/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/attachments", + "file": "api/attachments.ts", + "severity": "HIGH", + "message": "Frontend calls GET /attachments but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/addresses", + "file": "api/attachments.ts", + "severity": "HIGH", + "message": "Frontend calls GET /addresses but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/addresses", + "file": "api/attachments.ts", + "severity": "HIGH", + "message": "Frontend calls POST /addresses but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/addresses/{param}", + "file": "api/attachments.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /addresses/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/attachments/{param}", + "file": "api/attachments.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /attachments/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/addresses/{param}", + "file": "api/attachments.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /addresses/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/comm/conversations", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls GET /comm/conversations but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/comm/conversations/{param}", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls GET /comm/conversations/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/comm/conversations/{param}/messages", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls GET /comm/conversations/{param}/messages but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/comm/miniapps", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls GET /comm/miniapps but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/comm/block-types", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls GET /comm/block-types but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/comm/conversations", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls POST /comm/conversations but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/comm/conversations/{param}/pin", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls POST /comm/conversations/{param}/pin but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/comm/conversations/{param}/mute", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls POST /comm/conversations/{param}/mute but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/comm/conversations/{param}/participants", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls POST /comm/conversations/{param}/participants but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/comm/conversations/{param}/messages", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls POST /comm/conversations/{param}/messages but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/comm/messages/{param}/reactions", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls POST /comm/messages/{param}/reactions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/comm/conversations/{param}/read", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls POST /comm/conversations/{param}/read but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/comm/conversations/{param}/miniapps", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls POST /comm/conversations/{param}/miniapps but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/comm/conversations/{param}", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /comm/conversations/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/comm/conversations/{param}/participants/{param}", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /comm/conversations/{param}/participants/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/comm/messages/{param}", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /comm/messages/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/comm/conversations/{param}", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /comm/conversations/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/comm/conversations/{param}/pin", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /comm/conversations/{param}/pin but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/comm/conversations/{param}/mute", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /comm/conversations/{param}/mute but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/comm/conversations/{param}/participants/{param}", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /comm/conversations/{param}/participants/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/comm/messages/{param}", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /comm/messages/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/comm/messages/{param}/reactions/{param}", + "file": "api/comm.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /comm/messages/{param}/reactions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/dashboard/widgets", + "file": "api/dashboard.ts", + "severity": "HIGH", + "message": "Frontend calls GET /dashboard/widgets but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contacts/{param}/custom-fields", + "file": "api/customFields.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contacts/{param}/custom-fields but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/contacts/{param}/custom-fields", + "file": "api/customFields.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /contacts/{param}/custom-fields but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/roles", + "file": "api/roles.ts", + "severity": "HIGH", + "message": "Frontend calls GET /roles but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/roles/permissions", + "file": "api/roles.ts", + "severity": "HIGH", + "message": "Frontend calls GET /roles/permissions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/roles", + "file": "api/roles.ts", + "severity": "HIGH", + "message": "Frontend calls POST /roles but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/roles/{param}", + "file": "api/roles.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /roles/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/roles/{param}", + "file": "api/roles.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /roles/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mcp/tools", + "file": "api/mcp.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mcp/tools but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mcp/config", + "file": "api/mcp.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mcp/config but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mcp/tools/{param}/execute", + "file": "api/mcp.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mcp/tools/{param}/execute but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/dms/folders", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls GET /dms/folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/dms/files/{param}", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls GET /dms/files/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/dms/search", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls GET /dms/search but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/dms/shared-with-me", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls GET /dms/shared-with-me but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/dms/folders/{param}/files", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls GET /dms/folders/{param}/files but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/dms/files", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls GET /dms/files but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/dms/folders", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls POST /dms/folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/dms/files/upload", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls POST /dms/files/upload but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/dms/files/{param}/restore", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls POST /dms/files/{param}/restore but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/dms/files/{param}/edit-session", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls POST /dms/files/{param}/edit-session but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/dms/files/{param}/share", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls POST /dms/files/{param}/share but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/dms/files/bulk-move", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls POST /dms/files/bulk-move but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/dms/files/bulk-delete", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls POST /dms/files/bulk-delete but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/dms/folders/{param}", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /dms/folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/dms/files/{param}", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /dms/files/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/dms/folders/{param}", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /dms/folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/dms/files/{param}", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /dms/files/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/dms/files/{param}/share", + "file": "api/dms.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /dms/files/{param}/share but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/automation", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls GET /automation but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/automation/{param}", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls GET /automation/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/automation/{param}/runs", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls GET /automation/{param}/runs but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/automation/{param}/versions", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls GET /automation/{param}/versions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/automation/settings", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls GET /automation/settings but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/agents/{param}/runs", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls GET /agents/{param}/runs but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/agents/{param}/versions", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls GET /agents/{param}/versions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/agents/tools", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls GET /agents/tools but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/automation/miniapps", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls GET /automation/miniapps but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/automation", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls POST /automation but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/automation/{param}/execute", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls POST /automation/{param}/execute but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/automation/{param}/dry-run", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls POST /automation/{param}/dry-run but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/automation/{param}/versions/{param}/restore", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls POST /automation/{param}/versions/{param}/restore but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/agents/{param}/execute", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls POST /agents/{param}/execute but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/agents/{param}/test-run", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls POST /agents/{param}/test-run but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/agents/{param}/versions/{param}/restore", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls POST /agents/{param}/versions/{param}/restore but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/agents/{param}/send-message", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls POST /agents/{param}/send-message but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/automation/miniapps", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls POST /automation/miniapps but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/automation/{param}", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /automation/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/automation/settings", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /automation/settings but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/automation/{param}", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /automation/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/automation/miniapps/{param}", + "file": "api/automation.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /automation/miniapps/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/ai/providers", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls GET /ai/providers but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/ai/models", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls GET /ai/models but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/ai/presets", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls GET /ai/presets but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/ai/agents", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls GET /ai/agents but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/ai/tools", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls GET /ai/tools but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/ai/folders", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls GET /ai/folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/ai/sessions", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls GET /ai/sessions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/ai/sessions/{param}/messages", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls GET /ai/sessions/{param}/messages but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/ai/sessions/{param}/attachments", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls GET /ai/sessions/{param}/attachments but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/ai/providers", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls POST /ai/providers but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/ai/models", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls POST /ai/models but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/ai/presets", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls POST /ai/presets but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/ai/agents", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls POST /ai/agents but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/ai/folders", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls POST /ai/folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/ai/sessions", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls POST /ai/sessions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/ai/providers/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /ai/providers/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/ai/models/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /ai/models/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/ai/presets/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /ai/presets/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/ai/agents/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /ai/agents/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/ai/folders/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /ai/folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/ai/sessions/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /ai/sessions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/ai/providers/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /ai/providers/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/ai/models/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /ai/models/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/ai/presets/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /ai/presets/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/ai/agents/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /ai/agents/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/ai/folders/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /ai/folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/ai/sessions/{param}", + "file": "api/ai.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /ai/sessions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/auth/me", + "file": "api/auth.ts", + "severity": "HIGH", + "message": "Frontend calls GET /auth/me but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/auth/login", + "file": "api/auth.ts", + "severity": "HIGH", + "message": "Frontend calls POST /auth/login but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/auth/logout", + "file": "api/auth.ts", + "severity": "HIGH", + "message": "Frontend calls POST /auth/logout but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/auth/password-reset/request", + "file": "api/auth.ts", + "severity": "HIGH", + "message": "Frontend calls POST /auth/password-reset/request but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/auth/password-reset/confirm", + "file": "api/auth.ts", + "severity": "HIGH", + "message": "Frontend calls POST /auth/password-reset/confirm but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/auth/switch-tenant", + "file": "api/auth.ts", + "severity": "HIGH", + "message": "Frontend calls POST /auth/switch-tenant but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/entity-history/{param}/{param}", + "file": "api/entityHistory.ts", + "severity": "HIGH", + "message": "Frontend calls GET /entity-history/{param}/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/entity-history/trash", + "file": "api/entityHistory.ts", + "severity": "HIGH", + "message": "Frontend calls GET /entity-history/trash but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/entity-history/bulk-restore", + "file": "api/entityHistory.ts", + "severity": "HIGH", + "message": "Frontend calls POST /entity-history/bulk-restore but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/entity-history/undo/{param}/{param}", + "file": "api/entityHistory.ts", + "severity": "HIGH", + "message": "Frontend calls POST /entity-history/undo/{param}/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/system-settings", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls GET /system-settings but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/currencies", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls GET /currencies but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/taxes", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls GET /taxes but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/sequences", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls GET /sequences but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/currencies", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls POST /currencies but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/taxes", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls POST /taxes but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/sequences", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls POST /sequences but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/currencies/{param}", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /currencies/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/taxes/{param}", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /taxes/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/sequences/{param}", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /sequences/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/currencies/{param}", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /currencies/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/taxes/{param}", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /taxes/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/sequences/{param}", + "file": "api/settings.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /sequences/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/tags", + "file": "api/tags.ts", + "severity": "HIGH", + "message": "Frontend calls GET /tags but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/tags/{param}/entities", + "file": "api/tags.ts", + "severity": "HIGH", + "message": "Frontend calls GET /tags/{param}/entities but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/tags", + "file": "api/tags.ts", + "severity": "HIGH", + "message": "Frontend calls POST /tags but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/tags/assign", + "file": "api/tags.ts", + "severity": "HIGH", + "message": "Frontend calls POST /tags/assign but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/tags/bulk-assign", + "file": "api/tags.ts", + "severity": "HIGH", + "message": "Frontend calls POST /tags/bulk-assign but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/tags/{param}", + "file": "api/tags.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /tags/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/tags/{param}", + "file": "api/tags.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /tags/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/tags/assign", + "file": "api/tags.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /tags/assign but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/plugins/active-manifests", + "file": "api/pluginManifests.ts", + "severity": "HIGH", + "message": "Frontend calls GET /plugins/active-manifests but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/permissions/files/{param}/permissions", + "file": "api/permissions.ts", + "severity": "HIGH", + "message": "Frontend calls GET /permissions/files/{param}/permissions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/permissions/files/{param}/permissions", + "file": "api/permissions.ts", + "severity": "HIGH", + "message": "Frontend calls POST /permissions/files/{param}/permissions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/permissions/files/{param}/share-link", + "file": "api/permissions.ts", + "severity": "HIGH", + "message": "Frontend calls POST /permissions/files/{param}/share-link but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/permissions/files/{param}/permissions/{param}", + "file": "api/permissions.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /permissions/files/{param}/permissions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/permissions/share-links/{param}", + "file": "api/permissions.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /permissions/share-links/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/tasks", + "file": "api/tasks.ts", + "severity": "HIGH", + "message": "Frontend calls GET /tasks but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/tasks/{param}", + "file": "api/tasks.ts", + "severity": "HIGH", + "message": "Frontend calls GET /tasks/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/tasks", + "file": "api/tasks.ts", + "severity": "HIGH", + "message": "Frontend calls POST /tasks but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/tasks/{param}/assign", + "file": "api/tasks.ts", + "severity": "HIGH", + "message": "Frontend calls POST /tasks/{param}/assign but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/tasks/{param}/status", + "file": "api/tasks.ts", + "severity": "HIGH", + "message": "Frontend calls POST /tasks/{param}/status but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/tasks/{param}", + "file": "api/tasks.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /tasks/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/tasks/{param}", + "file": "api/tasks.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /tasks/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contacts/{param}", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contacts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contact-folders", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contact-folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contact-folders/{param}/permissions", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contact-folders/{param}/permissions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contacts", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contacts but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contact-folders", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contact-folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contact-folders/{param}/permissions", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contact-folders/{param}/permissions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contact-folders/{param}", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contact-folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contact-folders/contacts/{param}/move", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contact-folders/contacts/{param}/move but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contact-folders/{param}/permissions/{param}", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contact-folders/{param}/permissions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/contacts/{param}", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /contacts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/contacts/{param}", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /contacts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/contact-folders/{param}", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /contact-folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/contact-folders/{param}/permissions/{param}", + "file": "api/contacts.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /contact-folders/{param}/permissions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/workflows", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls GET /workflows but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/workflows/{param}", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls GET /workflows/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/workflows/instances", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls GET /workflows/instances but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/workflows/instances/{param}", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls GET /workflows/instances/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/workflows", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls POST /workflows but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/workflows/{param}/instances", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls POST /workflows/{param}/instances but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/workflows/instances/{param}/advance", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls POST /workflows/instances/{param}/advance but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/workflows/instances/{param}/cancel", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls POST /workflows/instances/{param}/cancel but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/workflows/{param}", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /workflows/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/workflows/{param}", + "file": "api/workflows.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /workflows/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/backups", + "file": "api/backups.ts", + "severity": "HIGH", + "message": "Frontend calls GET /backups but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/backups", + "file": "api/backups.ts", + "severity": "HIGH", + "message": "Frontend calls POST /backups but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/backups/{param}/restore", + "file": "api/backups.ts", + "severity": "HIGH", + "message": "Frontend calls POST /backups/{param}/restore but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/backups/{param}", + "file": "api/backups.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /backups/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contacts/{param}", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contacts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contacts/{param}/persons", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contacts/{param}/persons but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contacts/{param}", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contacts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contacts/{param}/persons", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contacts/{param}/persons but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contacts", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contacts but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contacts/{param}/persons", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contacts/{param}/persons but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contacts", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contacts but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contacts/{param}/persons", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contacts/{param}/persons but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contacts/{param}", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contacts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contacts/{param}/persons/{param}", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contacts/{param}/persons/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contacts/{param}", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contacts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contacts/{param}/persons/{param}", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contacts/{param}/persons/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/contacts/{param}${hard ", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /contacts/{param}${hard but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/contacts/{param}/persons/{param}", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /contacts/{param}/persons/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/contacts/{param}${hard ", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /contacts/{param}${hard but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/contacts/{param}/persons/{param}", + "file": "api/unifiedContacts.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /contacts/{param}/persons/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/reports/templates", + "file": "api/reports.ts", + "severity": "HIGH", + "message": "Frontend calls GET /reports/templates but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/reports/templates/{param}", + "file": "api/reports.ts", + "severity": "HIGH", + "message": "Frontend calls GET /reports/templates/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/reports/presets", + "file": "api/reports.ts", + "severity": "HIGH", + "message": "Frontend calls GET /reports/presets but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/reports/templates", + "file": "api/reports.ts", + "severity": "HIGH", + "message": "Frontend calls POST /reports/templates but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/reports/templates/{param}", + "file": "api/reports.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /reports/templates/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/reports/templates/{param}", + "file": "api/reports.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /reports/templates/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/custom-fields/definitions{param}", + "file": "api/customFieldDefinitions.ts", + "severity": "HIGH", + "message": "Frontend calls GET /custom-fields/definitions{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/custom-fields/definitions", + "file": "api/customFieldDefinitions.ts", + "severity": "HIGH", + "message": "Frontend calls POST /custom-fields/definitions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/custom-fields/definitions/{param}", + "file": "api/customFieldDefinitions.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /custom-fields/definitions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/custom-fields/definitions/{param}", + "file": "api/customFieldDefinitions.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /custom-fields/definitions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mcp-client/servers", + "file": "api/mcpClient.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mcp-client/servers but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mcp-client/servers/{param}/tools", + "file": "api/mcpClient.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mcp-client/servers/{param}/tools but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mcp-client/servers", + "file": "api/mcpClient.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mcp-client/servers but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mcp-client/servers/{param}/execute", + "file": "api/mcpClient.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mcp-client/servers/{param}/execute but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/mcp-client/servers/{param}", + "file": "api/mcpClient.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /mcp-client/servers/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/mcp-client/servers/{param}", + "file": "api/mcpClient.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /mcp-client/servers/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contacts/merge-history", + "file": "api/dedup.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contacts/merge-history but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contacts/duplicates", + "file": "api/dedup.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contacts/duplicates but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contacts/merge", + "file": "api/dedup.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contacts/merge but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/users/{param}", + "file": "api/users.ts", + "severity": "HIGH", + "message": "Frontend calls GET /users/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/users/me/menu-order", + "file": "api/users.ts", + "severity": "HIGH", + "message": "Frontend calls GET /users/me/menu-order but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/users", + "file": "api/users.ts", + "severity": "HIGH", + "message": "Frontend calls POST /users but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/users/me/menu-order", + "file": "api/users.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /users/me/menu-order but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/users/{param}", + "file": "api/users.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /users/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/users/{param}", + "file": "api/users.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /users/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contact-folders", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contact-folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contact-folders/{param}/permissions", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contact-folders/{param}/permissions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/contact-folders/{param}/access", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls GET /contact-folders/{param}/access but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contact-folders", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contact-folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/contact-folders/{param}/permissions", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls POST /contact-folders/{param}/permissions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contact-folders/{param}", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contact-folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contact-folders/{param}/reorder", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contact-folders/{param}/reorder but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contact-folders/contacts/{param}/move", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contact-folders/contacts/{param}/move but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/contact-folders/{param}/permissions/{param}", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /contact-folders/{param}/permissions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/contact-folders/{param}", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /contact-folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/contact-folders/{param}/permissions/{param}", + "file": "api/contactFolders.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /contact-folders/{param}/permissions/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/saved-filters", + "file": "api/savedFilters.ts", + "severity": "HIGH", + "message": "Frontend calls GET /saved-filters but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/saved-filters", + "file": "api/savedFilters.ts", + "severity": "HIGH", + "message": "Frontend calls POST /saved-filters but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/saved-filters/{param}", + "file": "api/savedFilters.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /saved-filters/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/policies/{param}", + "file": "api/policies.ts", + "severity": "HIGH", + "message": "Frontend calls GET /policies/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/policies/{param}/{param}", + "file": "api/policies.ts", + "severity": "HIGH", + "message": "Frontend calls GET /policies/{param}/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/policies/{param}", + "file": "api/policies.ts", + "severity": "HIGH", + "message": "Frontend calls POST /policies/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/policies/{param}/{param}", + "file": "api/policies.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /policies/{param}/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/policies/{param}/{param}", + "file": "api/policies.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /policies/{param}/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/saved-views{param}", + "file": "api/savedViews.ts", + "severity": "HIGH", + "message": "Frontend calls GET /saved-views{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/saved-views", + "file": "api/savedViews.ts", + "severity": "HIGH", + "message": "Frontend calls POST /saved-views but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/saved-views/{param}", + "file": "api/savedViews.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /saved-views/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/saved-views/{param}", + "file": "api/savedViews.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /saved-views/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/accounts", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/accounts but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/accounts/shared", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/accounts/shared but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/folders", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/search", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/search but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/threads", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/threads but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/templates", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/templates but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/signatures", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/signatures but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/rules", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/rules but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/pgp/keys", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/pgp/keys but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/contacts/pgp-keys", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/contacts/pgp-keys but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/mail/labels", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls GET /mail/labels but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/accounts", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/accounts but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/accounts/{param}/users", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/accounts/{param}/users but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/accounts/{param}/delegates", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/accounts/{param}/delegates but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/accounts/{param}/send-permissions", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/accounts/{param}/send-permissions but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/accounts/{param}/test-connection", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/accounts/{param}/test-connection but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/accounts/{param}/sync", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/accounts/{param}/sync but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/folders", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/folders but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/folders/{param}/empty", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/folders/{param}/empty but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/folders/{param}/sync", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/folders/{param}/sync but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/send", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/send but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/{param}/reply", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/{param}/reply but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/{param}/forward", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/{param}/forward but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/{param}/link", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/{param}/link but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/{param}/create-event", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/{param}/create-event but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/{param}/labels", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/{param}/labels but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/templates", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/templates but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/templates/substitute", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/templates/substitute but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/signatures", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/signatures but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/rules", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/rules but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/vacation", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/vacation but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/vacation/test-dedup", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/vacation/test-dedup but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/pgp/keys", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/pgp/keys but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/pgp/encrypt", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/pgp/encrypt but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/contacts/{param}/pgp-key", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/contacts/{param}/pgp-key but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/labels", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/labels but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/{param}/move", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/{param}/move but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/mail/drafts", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls POST /mail/drafts but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/mail/accounts/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /mail/accounts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/mail/folders/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /mail/folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/mail/{param}/flags", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /mail/{param}/flags but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/mail/signatures/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /mail/signatures/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PATCH", + "url": "/mail/drafts/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls PATCH /mail/drafts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/mail/accounts/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /mail/accounts/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/mail/folders/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /mail/folders/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/mail/signatures/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /mail/signatures/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/mail/rules/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /mail/rules/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/mail/labels/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /mail/labels/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/mail/{param}", + "file": "api/mail.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /mail/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/api/v1/workspaces", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls GET /api/v1/workspaces but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/api/v1/workspaces/my", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls GET /api/v1/workspaces/my but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/api/v1/workspaces/context", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls GET /api/v1/workspaces/context but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "GET", + "url": "/api/v1/workspaces/{param}/widgets", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls GET /api/v1/workspaces/{param}/widgets but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/api/v1/workspaces", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls POST /api/v1/workspaces but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/api/v1/workspaces/{param}/modules", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls POST /api/v1/workspaces/{param}/modules but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/api/v1/workspaces/{param}/users", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls POST /api/v1/workspaces/{param}/users but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/api/v1/workspaces/{param}/widgets", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls POST /api/v1/workspaces/{param}/widgets but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "POST", + "url": "/api/v1/workspaces/{param}/set-default", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls POST /api/v1/workspaces/{param}/set-default but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/api/v1/workspaces/{param}", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /api/v1/workspaces/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "PUT", + "url": "/api/v1/workspaces/{param}/widgets/{param}", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls PUT /api/v1/workspaces/{param}/widgets/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/api/v1/workspaces/{param}", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /api/v1/workspaces/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/api/v1/workspaces/{param}/users/{param}", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /api/v1/workspaces/{param}/users/{param} but no matching backend endpoint found" + }, + { + "type": "frontend_call_no_backend", + "method": "DELETE", + "url": "/api/v1/workspaces/{param}/widgets/{param}", + "file": "api/hooks/workspaces.ts", + "severity": "HIGH", + "message": "Frontend calls DELETE /api/v1/workspaces/{param}/widgets/{param} but no matching backend endpoint found" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{filter_id}", + "file": "routes/saved_filters.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{filter_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{token_id}", + "file": "routes/api_tokens.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{token_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{sequence_id}", + "file": "routes/sequences.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{sequence_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{sequence_id}", + "file": "routes/sequences.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{sequence_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/export", + "file": "routes/companies.py", + "severity": "LOW", + "message": "Backend endpoint GET /export is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{company_id}", + "file": "routes/companies.py", + "severity": "LOW", + "message": "Backend endpoint GET /{company_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{company_id}", + "file": "routes/companies.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{company_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{company_id}", + "file": "routes/companies.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{company_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{company_id}/contacts/{contact_id}", + "file": "routes/companies.py", + "severity": "LOW", + "message": "Backend endpoint POST /{company_id}/contacts/{contact_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{company_id}/contacts/{contact_id}", + "file": "routes/companies.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{company_id}/contacts/{contact_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{company_id}/emails", + "file": "routes/companies.py", + "severity": "LOW", + "message": "Backend endpoint GET /{company_id}/emails is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{folder_id}", + "file": "routes/contact_folders.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{folder_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{folder_id}", + "file": "routes/contact_folders.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{folder_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{folder_id}/reorder", + "file": "routes/contact_folders.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{folder_id}/reorder is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/contacts/{contact_id}/move", + "file": "routes/contact_folders.py", + "severity": "LOW", + "message": "Backend endpoint PUT /contacts/{contact_id}/move is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{delegation_id}", + "file": "routes/delegations.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{delegation_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{delegation_id}", + "file": "routes/delegations.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{delegation_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/active", + "file": "routes/delegations.py", + "severity": "LOW", + "message": "Backend endpoint GET /active is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/transfer", + "file": "routes/owner_transfer.py", + "severity": "LOW", + "message": "Backend endpoint POST /transfer is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/export", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint GET /export is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/merge-history", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint GET /merge-history is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{contact_id}", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint GET /{contact_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{contact_id}", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{contact_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{contact_id}", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{contact_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{contact_id}/persons", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint GET /{contact_id}/persons is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{contact_id}/persons", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint POST /{contact_id}/persons is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{contact_id}/persons/{person_id}", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{contact_id}/persons/{person_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{contact_id}/persons/{person_id}", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{contact_id}/persons/{person_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/duplicates", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint POST /duplicates is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/merge", + "file": "routes/contacts.py", + "severity": "LOW", + "message": "Backend endpoint POST /merge is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/invite", + "file": "routes/guests.py", + "severity": "LOW", + "message": "Backend endpoint POST /invite is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{guest_id}", + "file": "routes/guests.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{guest_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{tenant_id}/users", + "file": "routes/tenants.py", + "severity": "LOW", + "message": "Backend endpoint GET /{tenant_id}/users is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{tenant_id}/users", + "file": "routes/tenants.py", + "severity": "LOW", + "message": "Backend endpoint POST /{tenant_id}/users is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/definitions", + "file": "routes/custom_field_definitions.py", + "severity": "LOW", + "message": "Backend endpoint GET /definitions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/definitions", + "file": "routes/custom_field_definitions.py", + "severity": "LOW", + "message": "Backend endpoint POST /definitions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/definitions/{definition_id}", + "file": "routes/custom_field_definitions.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /definitions/{definition_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/definitions/{definition_id}", + "file": "routes/custom_field_definitions.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /definitions/{definition_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{view_id}", + "file": "routes/saved_views.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{view_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{view_id}", + "file": "routes/saved_views.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{view_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/permissions", + "file": "routes/roles.py", + "severity": "LOW", + "message": "Backend endpoint GET /permissions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{role_id}", + "file": "routes/roles.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{role_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{role_id}", + "file": "routes/roles.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{role_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{group_id}", + "file": "routes/groups.py", + "severity": "LOW", + "message": "Backend endpoint GET /{group_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{group_id}", + "file": "routes/groups.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{group_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{group_id}", + "file": "routes/groups.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{group_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{group_id}/members", + "file": "routes/groups.py", + "severity": "LOW", + "message": "Backend endpoint GET /{group_id}/members is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{group_id}/members", + "file": "routes/groups.py", + "severity": "LOW", + "message": "Backend endpoint POST /{group_id}/members is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{group_id}/members/{user_id}", + "file": "routes/groups.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{group_id}/members/{user_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/user/{user_id}", + "file": "routes/groups.py", + "severity": "LOW", + "message": "Backend endpoint GET /user/{user_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/my", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint GET /my is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/context", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint GET /context is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{workspace_id}", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint GET /{workspace_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{workspace_id}", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{workspace_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{workspace_id}", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{workspace_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{workspace_id}/modules", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint POST /{workspace_id}/modules is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{workspace_id}/users", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint POST /{workspace_id}/users is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{workspace_id}/users/{user_id}", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{workspace_id}/users/{user_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{workspace_id}/widgets", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint GET /{workspace_id}/widgets is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{workspace_id}/widgets", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint POST /{workspace_id}/widgets is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{workspace_id}/widgets/{widget_id}", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{workspace_id}/widgets/{widget_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{workspace_id}/widgets/{widget_id}", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{workspace_id}/widgets/{widget_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{workspace_id}/set-default", + "file": "routes/workspaces.py", + "severity": "LOW", + "message": "Backend endpoint POST /{workspace_id}/set-default is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/widgets", + "file": "routes/dashboard.py", + "severity": "LOW", + "message": "Backend endpoint GET /widgets is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/counts", + "file": "routes/dashboard.py", + "severity": "LOW", + "message": "Backend endpoint GET /counts is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{backup_id}/restore", + "file": "routes/backups.py", + "severity": "LOW", + "message": "Backend endpoint POST /{backup_id}/restore is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{backup_id}", + "file": "routes/backups.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{backup_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{contact_id}/custom-fields", + "file": "routes/custom_fields.py", + "severity": "LOW", + "message": "Backend endpoint GET /{contact_id}/custom-fields is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{contact_id}/custom-fields", + "file": "routes/custom_fields.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{contact_id}/custom-fields is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/login", + "file": "routes/auth.py", + "severity": "LOW", + "message": "Backend endpoint POST /login is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/logout", + "file": "routes/auth.py", + "severity": "LOW", + "message": "Backend endpoint POST /logout is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/me", + "file": "routes/auth.py", + "severity": "LOW", + "message": "Backend endpoint GET /me is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/me/permissions", + "file": "routes/auth.py", + "severity": "LOW", + "message": "Backend endpoint GET /me/permissions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/switch-tenant", + "file": "routes/auth.py", + "severity": "LOW", + "message": "Backend endpoint POST /switch-tenant is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/password-reset/request", + "file": "routes/auth.py", + "severity": "LOW", + "message": "Backend endpoint POST /password-reset/request is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/password-reset/confirm", + "file": "routes/auth.py", + "severity": "LOW", + "message": "Backend endpoint POST /password-reset/confirm is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{user_id}", + "file": "routes/users.py", + "severity": "LOW", + "message": "Backend endpoint GET /{user_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{user_id}", + "file": "routes/users.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{user_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{user_id}", + "file": "routes/users.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{user_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/me/menu-order", + "file": "routes/users.py", + "severity": "LOW", + "message": "Backend endpoint GET /me/menu-order is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/me/menu-order", + "file": "routes/users.py", + "severity": "LOW", + "message": "Backend endpoint PUT /me/menu-order is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{account_id}", + "file": "routes/bank_accounts.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{account_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{account_id}", + "file": "routes/bank_accounts.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{account_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{webhook_id}", + "file": "routes/webhooks.py", + "severity": "LOW", + "message": "Backend endpoint GET /{webhook_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{webhook_id}", + "file": "routes/webhooks.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{webhook_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{webhook_id}", + "file": "routes/webhooks.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{webhook_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{webhook_id}/test", + "file": "routes/webhooks.py", + "severity": "LOW", + "message": "Backend endpoint POST /{webhook_id}/test is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{address_id}", + "file": "routes/addresses.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{address_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{address_id}", + "file": "routes/addresses.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{address_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{folder_id}/permissions", + "file": "routes/contact_folder_permissions.py", + "severity": "LOW", + "message": "Backend endpoint GET /{folder_id}/permissions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{folder_id}/permissions", + "file": "routes/contact_folder_permissions.py", + "severity": "LOW", + "message": "Backend endpoint POST /{folder_id}/permissions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{folder_id}/permissions/{permission_id}", + "file": "routes/contact_folder_permissions.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{folder_id}/permissions/{permission_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{folder_id}/permissions/{permission_id}", + "file": "routes/contact_folder_permissions.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{folder_id}/permissions/{permission_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{folder_id}/access", + "file": "routes/contact_folder_permissions.py", + "severity": "LOW", + "message": "Backend endpoint GET /{folder_id}/access is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{tax_id}", + "file": "routes/taxes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{tax_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{tax_id}", + "file": "routes/taxes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{tax_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{notification_id}/read", + "file": "routes/notifications.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{notification_id}/read is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/unread-count", + "file": "routes/notifications.py", + "severity": "LOW", + "message": "Backend endpoint GET /unread-count is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/types", + "file": "routes/notifications.py", + "severity": "LOW", + "message": "Backend endpoint GET /types is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/preferences", + "file": "routes/notifications.py", + "severity": "LOW", + "message": "Backend endpoint GET /preferences is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/preferences/{type_key}", + "file": "routes/notifications.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /preferences/{type_key} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/import", + "file": "routes/import_export.py", + "severity": "LOW", + "message": "Backend endpoint POST /import is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/import/preview", + "file": "routes/import_export.py", + "severity": "LOW", + "message": "Backend endpoint POST /import/preview is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/import/validate", + "file": "routes/import_export.py", + "severity": "LOW", + "message": "Backend endpoint POST /import/validate is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/import/status/{job_id}", + "file": "routes/import_export.py", + "severity": "LOW", + "message": "Backend endpoint GET /import/status/{job_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/import/contacts", + "file": "routes/import_export.py", + "severity": "LOW", + "message": "Backend endpoint POST /import/contacts is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/import/companies", + "file": "routes/import_export.py", + "severity": "LOW", + "message": "Backend endpoint POST /import/companies is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/export", + "file": "routes/import_export.py", + "severity": "LOW", + "message": "Backend endpoint GET /export is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{attachment_id}", + "file": "routes/attachments.py", + "severity": "LOW", + "message": "Backend endpoint GET /{attachment_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{attachment_id}", + "file": "routes/attachments.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{attachment_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/health/live", + "file": "routes/health.py", + "severity": "LOW", + "message": "Backend endpoint GET /health/live is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/health/ready", + "file": "routes/health.py", + "severity": "LOW", + "message": "Backend endpoint GET /health/ready is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/api/v1/health", + "file": "routes/health.py", + "severity": "LOW", + "message": "Backend endpoint GET /api/v1/health is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/instances", + "file": "routes/workflows.py", + "severity": "LOW", + "message": "Backend endpoint GET /instances is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{workflow_id}", + "file": "routes/workflows.py", + "severity": "LOW", + "message": "Backend endpoint GET /{workflow_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{workflow_id}", + "file": "routes/workflows.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{workflow_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{workflow_id}", + "file": "routes/workflows.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{workflow_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{workflow_id}/instances", + "file": "routes/workflows.py", + "severity": "LOW", + "message": "Backend endpoint POST /{workflow_id}/instances is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/instances/{instance_id}", + "file": "routes/workflows.py", + "severity": "LOW", + "message": "Backend endpoint GET /instances/{instance_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/instances/{instance_id}/advance", + "file": "routes/workflows.py", + "severity": "LOW", + "message": "Backend endpoint POST /instances/{instance_id}/advance is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/instances/{instance_id}/cancel", + "file": "routes/workflows.py", + "severity": "LOW", + "message": "Backend endpoint POST /instances/{instance_id}/cancel is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/stats", + "file": "routes/outbox.py", + "severity": "LOW", + "message": "Backend endpoint GET /stats is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/failed", + "file": "routes/outbox.py", + "severity": "LOW", + "message": "Backend endpoint GET /failed is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/replay/{event_id}", + "file": "routes/outbox.py", + "severity": "LOW", + "message": "Backend endpoint POST /replay/{event_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/replay-all", + "file": "routes/outbox.py", + "severity": "LOW", + "message": "Backend endpoint POST /replay-all is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/consumer-registry", + "file": "routes/outbox.py", + "severity": "LOW", + "message": "Backend endpoint GET /consumer-registry is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/recover-stuck", + "file": "routes/outbox.py", + "severity": "LOW", + "message": "Backend endpoint POST /recover-stuck is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/cleanup-published", + "file": "routes/outbox.py", + "severity": "LOW", + "message": "Backend endpoint POST /cleanup-published is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/query", + "file": "routes/ai_copilot.py", + "severity": "LOW", + "message": "Backend endpoint POST /query is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/execute", + "file": "routes/ai_copilot.py", + "severity": "LOW", + "message": "Backend endpoint POST /execute is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/history", + "file": "routes/ai_copilot.py", + "severity": "LOW", + "message": "Backend endpoint GET /history is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/api/v1/metrics", + "file": "routes/metrics.py", + "severity": "LOW", + "message": "Backend endpoint GET /api/v1/metrics is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{entity_type}/{entity_id}", + "file": "routes/entity_history.py", + "severity": "LOW", + "message": "Backend endpoint GET /{entity_type}/{entity_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/restore", + "file": "routes/entity_history.py", + "severity": "LOW", + "message": "Backend endpoint POST /restore is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/undo/{entity_type}/{entity_id}", + "file": "routes/entity_history.py", + "severity": "LOW", + "message": "Backend endpoint POST /undo/{entity_type}/{entity_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/trash", + "file": "routes/entity_history.py", + "severity": "LOW", + "message": "Backend endpoint GET /trash is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/bulk-restore", + "file": "routes/entity_history.py", + "severity": "LOW", + "message": "Backend endpoint POST /bulk-restore is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/retention/archive", + "file": "routes/entity_history.py", + "severity": "LOW", + "message": "Backend endpoint POST /retention/archive is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{entity_type}", + "file": "routes/policies.py", + "severity": "LOW", + "message": "Backend endpoint GET /{entity_type} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{policy_id}", + "file": "routes/policies.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{policy_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{policy_id}", + "file": "routes/policies.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{policy_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{template_id}", + "file": "routes/permission_templates.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{template_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{template_id}", + "file": "routes/permission_templates.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{template_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/apply", + "file": "routes/permission_templates.py", + "severity": "LOW", + "message": "Backend endpoint POST /apply is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{currency_id}", + "file": "routes/currencies.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{currency_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{currency_id}", + "file": "routes/currencies.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{currency_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{key}", + "file": "routes/user_preferences.py", + "severity": "LOW", + "message": "Backend endpoint GET /{key} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{key}", + "file": "routes/user_preferences.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{key} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{key}", + "file": "routes/user_preferences.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{key} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{entity_type}/{entity_id}", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint GET /{entity_type}/{entity_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{entity_type}/{entity_id}", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint POST /{entity_type}/{entity_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/{entity_type}/{entity_id}/{permission_id}", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint PUT /{entity_type}/{entity_id}/{permission_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{entity_type}/{entity_id}/{permission_id}", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{entity_type}/{entity_id}/{permission_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{entity_type}/{entity_id}/access", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint GET /{entity_type}/{entity_id}/access is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/all", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint GET /all is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/registry", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint GET /registry is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/bulk", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint POST /bulk is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/bulk/unshare", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint POST /bulk/unshare is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/analytics", + "file": "routes/entity_permissions.py", + "severity": "LOW", + "message": "Backend endpoint GET /analytics is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/manifest", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint GET /manifest is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/updates", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint GET /updates is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/active-manifests", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint GET /active-manifests is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{name}/config", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint GET /{name}/config is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{name}/config", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{name}/config is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{name}/install", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint POST /{name}/install is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{name}/activate", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint POST /{name}/activate is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{name}/deactivate", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint POST /{name}/deactivate is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{name}", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{name} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/upload", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint POST /upload is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/install-url", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint POST /install-url is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/install-marketplace", + "file": "routes/plugins.py", + "severity": "LOW", + "message": "Backend endpoint POST /install-marketplace is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/servers", + "file": "plugins/builtins/mcp_client/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /servers is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/servers", + "file": "plugins/builtins/mcp_client/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /servers is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/servers/{server_id}", + "file": "plugins/builtins/mcp_client/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /servers/{server_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/servers/{server_id}", + "file": "plugins/builtins/mcp_client/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /servers/{server_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/servers/{server_id}/tools", + "file": "plugins/builtins/mcp_client/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /servers/{server_id}/tools is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/servers/{server_id}/execute", + "file": "plugins/builtins/mcp_client/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /servers/{server_id}/execute is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/status", + "file": "plugins/builtins/forgejo_error_reporter/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /status is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST / is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/runs/recent", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /runs/recent is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/miniapps", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /miniapps is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/miniapps", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /miniapps is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/miniapps/{app_id}", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /miniapps/{app_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/settings", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /settings is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/settings", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /settings is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{automation_id}", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{automation_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{automation_id}", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{automation_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{automation_id}", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{automation_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{automation_id}/execute", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{automation_id}/execute is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{automation_id}/dry-run", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{automation_id}/dry-run is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{automation_id}/runs", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{automation_id}/runs is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{automation_id}/versions", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{automation_id}/versions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{automation_id}/versions/{version_id}/restore", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{automation_id}/versions/{version_id}/restore is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/subtasks", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /subtasks is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/subtasks", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /subtasks is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/subtasks/{subtask_id}", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /subtasks/{subtask_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/subtasks/{subtask_id}", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /subtasks/{subtask_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/subtasks/{subtask_id}/cancel", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /subtasks/{subtask_id}/cancel is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/subtasks/{subtask_id}/wait", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /subtasks/{subtask_id}/wait is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/subtasks/aggregate", + "file": "plugins/builtins/automation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /subtasks/aggregate is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint POST / is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/tools", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /tools is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/runs/recent", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /runs/recent is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{agent_id}", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{agent_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{agent_id}", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{agent_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{agent_id}", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{agent_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{agent_id}/execute", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{agent_id}/execute is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{agent_id}/test-run", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{agent_id}/test-run is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{agent_id}/runs", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{agent_id}/runs is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{agent_id}/versions", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{agent_id}/versions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{agent_id}/versions/{version_id}/restore", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{agent_id}/versions/{version_id}/restore is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{id}/send-message", + "file": "plugins/builtins/automation/agent_routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{id}/send-message is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/folders", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /folders is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/folders", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /folders is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/folders/{folder_id}", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /folders/{folder_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/folders/{folder_id}", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /folders/{folder_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/files/upload", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /files/upload is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/files/{file_id}", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /files/{file_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/files", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /files is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/folders/{folder_id}/files", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /folders/{folder_id}/files is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/files/{file_id}", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /files/{file_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/files/{file_id}", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /files/{file_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/files/{file_id}/restore", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /files/{file_id}/restore is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/files/{file_id}/preview", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /files/{file_id}/preview is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/files/{file_id}/download", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /files/{file_id}/download is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/files/{file_id}/edit-session", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /files/{file_id}/edit-session is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/files/{file_id}/share", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /files/{file_id}/share is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/files/{file_id}/share", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /files/{file_id}/share is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/search", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /search is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/shared-with-me", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /shared-with-me is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/files/bulk-move", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /files/bulk-move is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/files/bulk-delete", + "file": "plugins/builtins/dms/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /files/bulk-delete is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/accounts", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /accounts is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/accounts", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /accounts is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/accounts/shared", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /accounts/shared is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/accounts/{account_id}", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /accounts/{account_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/accounts/{account_id}", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /accounts/{account_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/accounts/{account_id}/users", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /accounts/{account_id}/users is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/accounts/{account_id}/delegates", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /accounts/{account_id}/delegates is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/accounts/{account_id}/send-permissions", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /accounts/{account_id}/send-permissions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/accounts/{account_id}/test-connection", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /accounts/{account_id}/test-connection is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/accounts/{account_id}/sync", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /accounts/{account_id}/sync is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/folders", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /folders is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/folders", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /folders is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/folders/{folder_id}", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /folders/{folder_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/folders/{folder_id}", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /folders/{folder_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/folders/{folder_id}/empty", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /folders/{folder_id}/empty is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/folders/{folder_id}/sync", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /folders/{folder_id}/sync is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/upload-attachment", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /upload-attachment is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/send", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /send is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/search", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /search is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/threads", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /threads is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/templates", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /templates is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/templates", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /templates is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/templates/substitute", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /templates/substitute is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/signatures", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /signatures is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/signatures", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /signatures is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/rules", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /rules is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/rules", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /rules is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/rules/{rule_id}", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /rules/{rule_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/vacation", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /vacation is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/vacation/test-dedup", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /vacation/test-dedup is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/pgp/keys", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /pgp/keys is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/pgp/keys", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /pgp/keys is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/pgp/encrypt", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /pgp/encrypt is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/labels", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /labels is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/labels", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /labels is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/contacts/{contact_id}/pgp-key", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /contacts/{contact_id}/pgp-key is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{mail_id}/apply-rules", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{mail_id}/apply-rules is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{mail_id}/reply", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{mail_id}/reply is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{mail_id}/forward", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{mail_id}/forward is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{mail_id}/flags", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{mail_id}/flags is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{mail_id}/attachments/{att_id}", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{mail_id}/attachments/{att_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{mail_id}/link", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{mail_id}/link is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{mail_id}/create-event", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{mail_id}/create-event is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{mail_id}/labels", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{mail_id}/labels is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/drafts", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /drafts is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/drafts/{mail_id}", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint PUT /drafts/{mail_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{mail_id}", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{mail_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{mail_id}/move", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{mail_id}/move is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{mail_id}", + "file": "plugins/builtins/mail/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{mail_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{task_id}", + "file": "plugins/builtins/tasks/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{task_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{task_id}", + "file": "plugins/builtins/tasks/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{task_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{task_id}", + "file": "plugins/builtins/tasks/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{task_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{task_id}/assign", + "file": "plugins/builtins/tasks/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{task_id}/assign is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{task_id}/status", + "file": "plugins/builtins/tasks/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{task_id}/status is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/calendar/entries", + "file": "plugins/builtins/calendar/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /calendar/entries is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/calendar/entries/export", + "file": "plugins/builtins/calendar/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /calendar/entries/export is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/calendar/entries/{entry_id}/link", + "file": "plugins/builtins/calendar/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /calendar/entries/{entry_id}/link is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/calendar/entries/bulk", + "file": "plugins/builtins/calendar/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /calendar/entries/bulk is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/calendar/{calendar_id}/ics-feed", + "file": "plugins/builtins/calendar/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /calendar/{calendar_id}/ics-feed is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/calendar/import", + "file": "plugins/builtins/calendar/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /calendar/import is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/facets", + "file": "plugins/builtins/unified_search/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /facets is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/suggest", + "file": "plugins/builtins/unified_search/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /suggest is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/similar", + "file": "plugins/builtins/unified_search/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /similar is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/reindex", + "file": "plugins/builtins/unified_search/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /reindex is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/rebuild/{entity_type}/{entity_id}", + "file": "plugins/builtins/unified_search/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /rebuild/{entity_type}/{entity_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/purge/{entity_type}/{entity_id}", + "file": "plugins/builtins/unified_search/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /purge/{entity_type}/{entity_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/providers", + "file": "plugins/builtins/unified_search/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /providers is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/providers/{entity_type}/toggle", + "file": "plugins/builtins/unified_search/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /providers/{entity_type}/toggle is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/stats", + "file": "plugins/builtins/unified_search/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /stats is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/search", + "file": "plugins/builtins/agent_memory/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /search is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{memory_id}", + "file": "plugins/builtins/agent_memory/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{memory_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{memory_id}", + "file": "plugins/builtins/agent_memory/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{memory_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/listings", + "file": "plugins/builtins/marketplace/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /listings is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/listings/{name}", + "file": "plugins/builtins/marketplace/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /listings/{name} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/install/{name}", + "file": "plugins/builtins/marketplace/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /install/{name} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/verify/{name}", + "file": "plugins/builtins/marketplace/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /verify/{name} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/categories", + "file": "plugins/builtins/marketplace/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /categories is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/relationships", + "file": "plugins/builtins/graph_rag/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /relationships is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/relationships", + "file": "plugins/builtins/graph_rag/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /relationships is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/traverse", + "file": "plugins/builtins/graph_rag/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /traverse is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/relationships/{relationship_id}", + "file": "plugins/builtins/graph_rag/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /relationships/{relationship_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/presets", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /presets is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/presets/generate", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /presets/generate is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/templates", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /templates is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/templates", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /templates is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/templates/{template_id}", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /templates/{template_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/templates/{template_id}", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint PUT /templates/{template_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/templates/{template_id}", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /templates/{template_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/generate", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /generate is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/generate/async", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /generate/async is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{report_id}", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{report_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{report_id}/download", + "file": "plugins/builtins/report_generator/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{report_id}/download is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/files/{file_id}/link", + "file": "plugins/builtins/entity_links/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /files/{file_id}/link is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/files/{file_id}/link", + "file": "plugins/builtins/entity_links/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /files/{file_id}/link is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/files/{file_id}/links", + "file": "plugins/builtins/entity_links/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /files/{file_id}/links is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/conversations", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /conversations is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/conversations", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /conversations is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/conversations/{conversation_id}", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /conversations/{conversation_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/conversations/{conversation_id}", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /conversations/{conversation_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/conversations/{conversation_id}", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /conversations/{conversation_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/conversations/{conversation_id}/pin", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /conversations/{conversation_id}/pin is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/conversations/{conversation_id}/pin", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /conversations/{conversation_id}/pin is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/conversations/{conversation_id}/mute", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /conversations/{conversation_id}/mute is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/conversations/{conversation_id}/mute", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /conversations/{conversation_id}/mute is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/conversations/{conversation_id}/participants", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /conversations/{conversation_id}/participants is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/conversations/{conversation_id}/participants/{participant_id}", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /conversations/{conversation_id}/participants/{participant_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/conversations/{conversation_id}/participants/{participant_id}", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /conversations/{conversation_id}/participants/{participant_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/conversations/{conversation_id}/messages", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /conversations/{conversation_id}/messages is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/conversations/{conversation_id}/messages", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /conversations/{conversation_id}/messages is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/messages/{message_id}", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /messages/{message_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/messages/{message_id}", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /messages/{message_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/messages/{message_id}/attachments", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /messages/{message_id}/attachments is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/messages/{message_id}/reactions", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /messages/{message_id}/reactions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/messages/{message_id}/reactions/{emoji}", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /messages/{message_id}/reactions/{emoji} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/conversations/{conversation_id}/read", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /conversations/{conversation_id}/read is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/miniapps", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /miniapps is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/conversations/{conversation_id}/miniapps", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /conversations/{conversation_id}/miniapps is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/block-types", + "file": "plugins/builtins/kommunikation/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /block-types is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/context", + "file": "plugins/builtins/ai_proactive/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /context is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/suggestions", + "file": "plugins/builtins/ai_proactive/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /suggestions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/suggestions/{suggestion_id}/dismiss", + "file": "plugins/builtins/ai_proactive/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /suggestions/{suggestion_id}/dismiss is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/suggestions/{suggestion_id}/act", + "file": "plugins/builtins/ai_proactive/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /suggestions/{suggestion_id}/act is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/suggestions/stream", + "file": "plugins/builtins/ai_proactive/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /suggestions/stream is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/settings", + "file": "plugins/builtins/ai_proactive/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /settings is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/settings", + "file": "plugins/builtins/ai_proactive/routes.py", + "severity": "LOW", + "message": "Backend endpoint PUT /settings is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/stats", + "file": "plugins/builtins/ai_proactive/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /stats is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{agent_id}/run", + "file": "plugins/builtins/ai_assistant/external_api.py", + "severity": "LOW", + "message": "Backend endpoint POST /{agent_id}/run is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{agent_id}/status", + "file": "plugins/builtins/ai_assistant/external_api.py", + "severity": "LOW", + "message": "Backend endpoint GET /{agent_id}/status is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{agent_id}/stream", + "file": "plugins/builtins/ai_assistant/external_api.py", + "severity": "LOW", + "message": "Backend endpoint POST /{agent_id}/stream is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/providers", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /providers is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/providers", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /providers is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/providers/{provider_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint PUT /providers/{provider_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/providers/{provider_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /providers/{provider_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/models", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /models is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/models", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /models is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/models/{model_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint PUT /models/{model_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/models/{model_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /models/{model_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/presets", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /presets is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/presets", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /presets is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/presets/{preset_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint PUT /presets/{preset_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/presets/{preset_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /presets/{preset_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/agents/{agent_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint PUT /agents/{agent_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/tools", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /tools is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/sessions", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /sessions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/sessions", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /sessions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/sessions/{session_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint PUT /sessions/{session_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/sessions/{session_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /sessions/{session_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/sessions/{session_id}/messages", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /sessions/{session_id}/messages is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/sessions/{session_id}/stream", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /sessions/{session_id}/stream is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/folders", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /folders is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/folders", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /folders is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PUT", + "path": "/folders/{folder_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint PUT /folders/{folder_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/folders/{folder_id}", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /folders/{folder_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/sessions/{session_id}/attachments", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /sessions/{session_id}/attachments is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/sessions/{session_id}/attachments", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /sessions/{session_id}/attachments is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/attachments/{attachment_id}/download", + "file": "plugins/builtins/ai_assistant/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /attachments/{attachment_id}/download is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "PATCH", + "path": "/{tag_id}", + "file": "plugins/builtins/tags/routes.py", + "severity": "LOW", + "message": "Backend endpoint PATCH /{tag_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/assign", + "file": "plugins/builtins/tags/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /assign is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/assign", + "file": "plugins/builtins/tags/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /assign is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/{tag_id}", + "file": "plugins/builtins/tags/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /{tag_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/bulk-assign", + "file": "plugins/builtins/tags/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /bulk-assign is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{tag_id}/entities", + "file": "plugins/builtins/tags/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{tag_id}/entities is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/files/{file_id}/permissions", + "file": "plugins/builtins/permissions/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /files/{file_id}/permissions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/files/{file_id}/permissions", + "file": "plugins/builtins/permissions/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /files/{file_id}/permissions is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/files/{file_id}/permissions/{user_id}", + "file": "plugins/builtins/permissions/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /files/{file_id}/permissions/{user_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/files/{file_id}/share-link", + "file": "plugins/builtins/permissions/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /files/{file_id}/share-link is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "DELETE", + "path": "/share-links/{link_id}", + "file": "plugins/builtins/permissions/routes.py", + "severity": "LOW", + "message": "Backend endpoint DELETE /share-links/{link_id} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{token}", + "file": "plugins/builtins/permissions/public_routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{token} is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/{token}/verify", + "file": "plugins/builtins/permissions/public_routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /{token}/verify is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/{token}/download", + "file": "plugins/builtins/permissions/public_routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /{token}/download is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/tools", + "file": "plugins/builtins/mcp_server/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /tools is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/tools/{tool_name}/execute", + "file": "plugins/builtins/mcp_server/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /tools/{tool_name}/execute is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/config", + "file": "plugins/builtins/mcp_server/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /config is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "POST", + "path": "/command", + "file": "plugins/builtins/ai_ui_control/routes.py", + "severity": "LOW", + "message": "Backend endpoint POST /command is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/command/{command_id}/status", + "file": "plugins/builtins/ai_ui_control/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /command/{command_id}/status is never called by frontend" + }, + { + "type": "backend_endpoint_no_frontend", + "method": "GET", + "path": "/online-users", + "file": "plugins/builtins/ai_ui_control/routes.py", + "severity": "LOW", + "message": "Backend endpoint GET /online-users is never called by frontend" + } + ], + "summary": { + "total_frontend_calls": 363, + "total_backend_endpoints": 419, + "total_issues": 758, + "high_severity": 350, + "low_severity": 408 + } +} \ No newline at end of file diff --git a/scripts/test_suite/results_trace_contracts.json b/scripts/test_suite/results_trace_contracts.json new file mode 100644 index 0000000..7f45be2 --- /dev/null +++ b/scripts/test_suite/results_trace_contracts.json @@ -0,0 +1,327 @@ +{ + "contracts": [ + { + "name": "ContractError", + "file": "app/plugins/builtins/contracts.py", + "attributes": [] + }, + { + "name": "PluginContract", + "file": "app/plugins/builtins/contracts.py", + "attributes": [ + "contract_name" + ] + }, + { + "name": "ContractRegistry", + "file": "app/plugins/builtins/contracts.py", + "attributes": [ + "_instance" + ] + }, + { + "name": "McpClientContract", + "file": "app/plugins/builtins/mcp_client/contracts.py", + "attributes": [ + "contract_name", + "McpServerConfig", + "McpClient", + "McpServerExecuteRequest", + "McpServerExecuteResponse", + "McpServerToolInfo", + "McpServerToolsResponse" + ] + }, + { + "name": "ForgejoErrorReporterContract", + "file": "app/plugins/builtins/forgejo_error_reporter/contracts.py", + "attributes": [ + "contract_name", + "report_error_to_forgejo", + "ReportedError" + ] + }, + { + "name": "AutomationContract", + "file": "app/plugins/builtins/automation/contracts.py", + "attributes": [ + "contract_name", + "AgentDefinition", + "Automation", + "CronJob", + "AgentRun", + "AgentVersion", + "AutomationRun", + "AutomationVersion", + "AgentService", + "AutomationService", + "CronJobService", + "RunLogService", + "run_agent", + "run_automation", + "calculate_next_run", + "scheduler_tick", + "send_agent_message" + ] + }, + { + "name": "DmsContract", + "file": "app/plugins/builtins/dms/contracts.py", + "attributes": [ + "contract_name", + "DmsFile", + "Folder" + ] + }, + { + "name": "MailContract", + "file": "app/plugins/builtins/mail/contracts.py", + "attributes": [ + "contract_name", + "Mail" + ] + }, + { + "name": "TestContractRegistry", + "file": "app/plugins/builtins/tests/test_contracts.py", + "attributes": [] + }, + { + "name": "TestKommunikationContract", + "file": "app/plugins/builtins/tests/test_contracts.py", + "attributes": [] + }, + { + "name": "TestAIAssistantContract", + "file": "app/plugins/builtins/tests/test_contracts.py", + "attributes": [] + }, + { + "name": "TestMailContract", + "file": "app/plugins/builtins/tests/test_contracts.py", + "attributes": [] + }, + { + "name": "TasksContract", + "file": "app/plugins/builtins/tasks/contracts.py", + "attributes": [ + "contract_name", + "list_tasks", + "get_task", + "create_task", + "update_task", + "delete_task", + "assign_task", + "update_task_status", + "get_due_tasks", + "Task" + ] + }, + { + "name": "CalendarContract", + "file": "app/plugins/builtins/calendar/contracts.py", + "attributes": [ + "contract_name", + "Calendar", + "CalendarEntry", + "CalendarEntryLink" + ] + }, + { + "name": "UnifiedSearchContract", + "file": "app/plugins/builtins/unified_search/contracts.py", + "attributes": [ + "contract_name", + "generate_embedding", + "hybrid_search", + "find_similar_all_types", + "get_search_registry", + "llm_analyze_query", + "BaseSearchProvider" + ] + }, + { + "name": "GraphRagContract", + "file": "app/plugins/builtins/graph_rag/contracts.py", + "attributes": [ + "contract_name", + "GraphRAGSearchProvider" + ] + }, + { + "name": "ReportGeneratorContract", + "file": "app/plugins/builtins/report_generator/contracts.py", + "attributes": [ + "contract_name", + "ReportTemplate", + "ReportInstance", + "generate_pdf", + "generate_print_pdf", + "generate_preset_report", + "generate_pdf_from_template_content", + "render_template_file", + "render_template_string", + "get_preset_list", + "PRESET_META", + "PRESET_TEMPLATES" + ] + }, + { + "name": "SystemNotifContract", + "file": "app/plugins/builtins/system_notif/contracts.py", + "attributes": [ + "contract_name", + "SystemParticipantHandler" + ] + }, + { + "name": "EntityLinksContract", + "file": "app/plugins/builtins/entity_links/contracts.py", + "attributes": [ + "contract_name", + "EntityLink" + ] + }, + { + "name": "KommunikationContract", + "file": "app/plugins/builtins/kommunikation/contracts.py", + "attributes": [ + "contract_name", + "parse_mentions", + "get_conversation", + "get_messages", + "send_message", + "create_plugin_room", + "get_participant_registry", + "ParticipantHandler", + "MiniAppRegistry", + "MiniAppDef", + "get_miniapp_registry", + "reset_miniapp_registry", + "post_system_message", + "CommConversation", + "CommMessage", + "CommParticipant" + ] + }, + { + "name": "AiProactiveContract", + "file": "app/plugins/builtins/ai_proactive/contracts.py", + "attributes": [ + "contract_name", + "ProactiveSuggestion", + "ContextLog", + "ProactiveSettings", + "handle_context_change", + "get_active_suggestions", + "get_sse_queue", + "get_stats", + "get_user_settings", + "mark_dismissed", + "push_suggestion", + "register_context_tools", + "deep_analysis", + "heartbeat" + ] + }, + { + "name": "AIAssistantContract", + "file": "app/plugins/builtins/ai_assistant/contracts.py", + "attributes": [ + "contract_name", + "get_tool_registry", + "ToolRegistry", + "AITool", + "get_default_provider" + ] + }, + { + "name": "TagsContract", + "file": "app/plugins/builtins/tags/contracts.py", + "attributes": [ + "contract_name", + "Tag", + "TagAssignment" + ] + }, + { + "name": "PermissionsContract", + "file": "app/plugins/builtins/permissions/contracts.py", + "attributes": [ + "contract_name", + "Permission", + "ShareLink" + ] + }, + { + "name": "McpServerContract", + "file": "app/plugins/builtins/mcp_server/contracts.py", + "attributes": [ + "contract_name", + "ToolDefinitions", + "ToolHandlers", + "get_tool_definition", + "get_all_tool_names", + "McpToolDefinition", + "McpToolExecuteRequest", + "McpToolExecuteResponse", + "McpToolListResponse", + "McpToolParameter", + "McpServerConfig" + ] + }, + { + "name": "AiUiControlContract", + "file": "app/plugins/builtins/ai_ui_control/contracts.py", + "attributes": [ + "contract_name", + "AIUIControlWSManager", + "UICommand", + "UICommandCreate", + "UICommandFeedback", + "UICommandResponse", + "UICommandStatus", + "UICommandStatusResponse", + "UICommandType" + ] + } + ], + "accesses": [ + { + "contract": "AIAssistantContract", + "attribute": "get_tool_registry", + "file": "app/plugins/builtins/automation/agent_coordinator.py" + }, + { + "contract": "AIAssistantContract", + "attribute": "get_tool_registry", + "file": "app/plugins/builtins/automation/agent_coordinator.py" + }, + { + "contract": "GraphRagContract", + "attribute": "GraphRAGSearchProvider", + "file": "app/plugins/builtins/unified_search/provider_registry.py" + }, + { + "contract": "AutomationContract", + "attribute": "AgentRun", + "file": "app/plugins/builtins/ai_assistant/external_api.py" + }, + { + "contract": "AutomationContract", + "attribute": "AgentRun", + "file": "app/plugins/builtins/ai_assistant/external_api.py" + }, + { + "contract": "AutomationContract", + "attribute": "AgentRun", + "file": "app/plugins/builtins/ai_assistant/external_api.py" + }, + { + "contract": "DmsContract", + "attribute": "DmsFile", + "file": "app/plugins/builtins/permissions/public_routes.py" + } + ], + "mismatches": [] +} \ No newline at end of file diff --git a/scripts/test_suite/results_trace_functions.json b/scripts/test_suite/results_trace_functions.json new file mode 100644 index 0000000..062f314 --- /dev/null +++ b/scripts/test_suite/results_trace_functions.json @@ -0,0 +1,26 @@ +{ + "py_defs": 1616, + "py_dead": 629, + "ts_defs": 959, + "ts_dead": 442, + "critical_dead": [ + { + "name": "seed_default_workspace", + "file": "app/services/workspace_service.py", + "line": 560, + "lang": "py" + }, + { + "name": "register_default_history_hooks", + "file": "app/core/history_hooks.py", + "line": 128, + "lang": "py" + }, + { + "name": "register_default_entities", + "file": "app/core/restore_registry.py", + "line": 114, + "lang": "py" + } + ] +} \ No newline at end of file diff --git a/scripts/test_suite/results_trace_imports.json b/scripts/test_suite/results_trace_imports.json new file mode 100644 index 0000000..78803dc --- /dev/null +++ b/scripts/test_suite/results_trace_imports.json @@ -0,0 +1,33 @@ +{ + "total_imports": 2274, + "broken": [ + { + "file": "app/routes/entity_permissions.py", + "module": "app.models.entity_permission", + "name": "ENTITY_MODELS", + "line": 105, + "issue": "Name 'ENTITY_MODELS' not found in module" + }, + { + "file": "app/core/auth.py", + "module": "app.models.session", + "name": "SessionModel", + "line": 261, + "issue": "Name 'SessionModel' not found in module" + }, + { + "file": "app/plugins/builtins/automation/agent_comm.py", + "module": "app.plugins.builtins.kommunikation.contracts", + "name": "Room", + "line": 58, + "issue": "Name 'Room' not found in module" + }, + { + "file": "app/plugins/builtins/automation/agent_runner.py", + "module": "app.core.cache", + "name": "get_cached_mail_summary", + "line": 115, + "issue": "Name 'get_cached_mail_summary' not found in module" + } + ] +} \ No newline at end of file diff --git a/scripts/test_suite/results_trace_plugins.json b/scripts/test_suite/results_trace_plugins.json new file mode 100644 index 0000000..95c1667 --- /dev/null +++ b/scripts/test_suite/results_trace_plugins.json @@ -0,0 +1,1489 @@ +{ + "plugins": [ + { + "name": "mcp_client", + "path": "app/plugins/builtins/mcp_client", + "has_manifest": false + }, + { + "name": "forgejo_error_reporter", + "path": "app/plugins/builtins/forgejo_error_reporter", + "has_manifest": false + }, + { + "name": "automation", + "path": "app/plugins/builtins/automation", + "has_manifest": false + }, + { + "name": "dms", + "path": "app/plugins/builtins/dms", + "has_manifest": false + }, + { + "name": "mail", + "path": "app/plugins/builtins/mail", + "has_manifest": false + }, + { + "name": "migrations", + "path": "app/plugins/builtins/migrations", + "has_manifest": false + }, + { + "name": "tests", + "path": "app/plugins/builtins/tests", + "has_manifest": false + }, + { + "name": "contacts", + "path": "app/plugins/builtins/contacts", + "has_manifest": false + }, + { + "name": "tasks", + "path": "app/plugins/builtins/tasks", + "has_manifest": false + }, + { + "name": "calendar", + "path": "app/plugins/builtins/calendar", + "has_manifest": false + }, + { + "name": "unified_search", + "path": "app/plugins/builtins/unified_search", + "has_manifest": false + }, + { + "name": "agent_memory", + "path": "app/plugins/builtins/agent_memory", + "has_manifest": false + }, + { + "name": "marketplace", + "path": "app/plugins/builtins/marketplace", + "has_manifest": false + }, + { + "name": "graph_rag", + "path": "app/plugins/builtins/graph_rag", + "has_manifest": false + }, + { + "name": "report_generator", + "path": "app/plugins/builtins/report_generator", + "has_manifest": false + }, + { + "name": "system_notif", + "path": "app/plugins/builtins/system_notif", + "has_manifest": false + }, + { + "name": "entity_links", + "path": "app/plugins/builtins/entity_links", + "has_manifest": false + }, + { + "name": "kommunikation", + "path": "app/plugins/builtins/kommunikation", + "has_manifest": false + }, + { + "name": "ai_proactive", + "path": "app/plugins/builtins/ai_proactive", + "has_manifest": false + }, + { + "name": "ai_assistant", + "path": "app/plugins/builtins/ai_assistant", + "has_manifest": false + }, + { + "name": "tags", + "path": "app/plugins/builtins/tags", + "has_manifest": false + }, + { + "name": "permissions", + "path": "app/plugins/builtins/permissions", + "has_manifest": false + }, + { + "name": "mcp_server", + "path": "app/plugins/builtins/mcp_server", + "has_manifest": false + }, + { + "name": "ai_ui_control", + "path": "app/plugins/builtins/ai_ui_control", + "has_manifest": false + } + ], + "frontend_refs": {}, + "plugin_routes": [ + { + "method": "GET", + "path": "/servers", + "file": "app/plugins/builtins/mcp_client/routes.py" + }, + { + "method": "POST", + "path": "/servers", + "file": "app/plugins/builtins/mcp_client/routes.py" + }, + { + "method": "PATCH", + "path": "/servers/{server_id}", + "file": "app/plugins/builtins/mcp_client/routes.py" + }, + { + "method": "DELETE", + "path": "/servers/{server_id}", + "file": "app/plugins/builtins/mcp_client/routes.py" + }, + { + "method": "GET", + "path": "/servers/{server_id}/tools", + "file": "app/plugins/builtins/mcp_client/routes.py" + }, + { + "method": "POST", + "path": "/servers/{server_id}/execute", + "file": "app/plugins/builtins/mcp_client/routes.py" + }, + { + "method": "GET", + "path": "/status", + "file": "app/plugins/builtins/forgejo_error_reporter/routes.py" + }, + { + "method": "POST", + "path": "/", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "GET", + "path": "/runs/recent", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "GET", + "path": "/miniapps", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "POST", + "path": "/miniapps", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "DELETE", + "path": "/miniapps/{app_id}", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "GET", + "path": "/settings", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "PATCH", + "path": "/settings", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "GET", + "path": "/{automation_id}", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "PATCH", + "path": "/{automation_id}", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "DELETE", + "path": "/{automation_id}", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "POST", + "path": "/{automation_id}/execute", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "POST", + "path": "/{automation_id}/dry-run", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "GET", + "path": "/{automation_id}/runs", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "GET", + "path": "/{automation_id}/versions", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "POST", + "path": "/{automation_id}/versions/{version_id}/restore", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "GET", + "path": "/subtasks", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "POST", + "path": "/subtasks", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "GET", + "path": "/subtasks/{subtask_id}", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "PATCH", + "path": "/subtasks/{subtask_id}", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "POST", + "path": "/subtasks/{subtask_id}/cancel", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "POST", + "path": "/subtasks/{subtask_id}/wait", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "POST", + "path": "/subtasks/aggregate", + "file": "app/plugins/builtins/automation/routes.py" + }, + { + "method": "POST", + "path": "/", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "GET", + "path": "/tools", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "GET", + "path": "/runs/recent", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "GET", + "path": "/{agent_id}", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "PATCH", + "path": "/{agent_id}", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "DELETE", + "path": "/{agent_id}", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "POST", + "path": "/{agent_id}/execute", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "POST", + "path": "/{agent_id}/test-run", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "GET", + "path": "/{agent_id}/runs", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "GET", + "path": "/{agent_id}/versions", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "POST", + "path": "/{agent_id}/versions/{version_id}/restore", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "POST", + "path": "/{id}/send-message", + "file": "app/plugins/builtins/automation/agent_routes.py" + }, + { + "method": "GET", + "path": "/folders", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "POST", + "path": "/folders", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "PATCH", + "path": "/folders/{folder_id}", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "DELETE", + "path": "/folders/{folder_id}", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "POST", + "path": "/files/upload", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "GET", + "path": "/files/{file_id}", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "GET", + "path": "/files", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "GET", + "path": "/folders/{folder_id}/files", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "PATCH", + "path": "/files/{file_id}", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "DELETE", + "path": "/files/{file_id}", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "POST", + "path": "/files/{file_id}/restore", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "GET", + "path": "/files/{file_id}/preview", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "GET", + "path": "/files/{file_id}/download", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "POST", + "path": "/files/{file_id}/edit-session", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "POST", + "path": "/files/{file_id}/share", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "DELETE", + "path": "/files/{file_id}/share", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "GET", + "path": "/search", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "GET", + "path": "/shared-with-me", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "POST", + "path": "/files/bulk-move", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "POST", + "path": "/files/bulk-delete", + "file": "app/plugins/builtins/dms/routes.py" + }, + { + "method": "GET", + "path": "/accounts", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/accounts", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/accounts/shared", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "PATCH", + "path": "/accounts/{account_id}", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "DELETE", + "path": "/accounts/{account_id}", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/accounts/{account_id}/users", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/accounts/{account_id}/delegates", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/accounts/{account_id}/send-permissions", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/accounts/{account_id}/test-connection", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/accounts/{account_id}/sync", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/folders", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/folders", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "PATCH", + "path": "/folders/{folder_id}", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "DELETE", + "path": "/folders/{folder_id}", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/folders/{folder_id}/empty", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/folders/{folder_id}/sync", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/upload-attachment", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/send", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/search", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/threads", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/templates", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/templates", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/templates/substitute", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/signatures", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/signatures", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/rules", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/rules", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "DELETE", + "path": "/rules/{rule_id}", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/vacation", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/vacation/test-dedup", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/pgp/keys", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/pgp/keys", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/pgp/encrypt", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/labels", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/labels", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/contacts/{contact_id}/pgp-key", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/{mail_id}/apply-rules", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/{mail_id}/reply", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/{mail_id}/forward", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "PATCH", + "path": "/{mail_id}/flags", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/{mail_id}/attachments/{att_id}", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/{mail_id}/link", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/{mail_id}/create-event", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/{mail_id}/labels", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/drafts", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "PUT", + "path": "/drafts/{mail_id}", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "DELETE", + "path": "/{mail_id}", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "POST", + "path": "/{mail_id}/move", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/{mail_id}", + "file": "app/plugins/builtins/mail/routes.py" + }, + { + "method": "GET", + "path": "/{task_id}", + "file": "app/plugins/builtins/tasks/routes.py" + }, + { + "method": "PATCH", + "path": "/{task_id}", + "file": "app/plugins/builtins/tasks/routes.py" + }, + { + "method": "DELETE", + "path": "/{task_id}", + "file": "app/plugins/builtins/tasks/routes.py" + }, + { + "method": "POST", + "path": "/{task_id}/assign", + "file": "app/plugins/builtins/tasks/routes.py" + }, + { + "method": "POST", + "path": "/{task_id}/status", + "file": "app/plugins/builtins/tasks/routes.py" + }, + { + "method": "GET", + "path": "/calendar/entries", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "POST", + "path": "/calendar/entries", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "GET", + "path": "/calendar/entries/export", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "GET", + "path": "/calendar/entries/{entry_id}", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "PATCH", + "path": "/calendar/entries/{entry_id}", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "DELETE", + "path": "/calendar/entries/{entry_id}", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "POST", + "path": "/calendar/entries/{entry_id}/link", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "POST", + "path": "/calendar/entries/{entry_id}/subtasks", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "PATCH", + "path": "/calendar/entries/{entry_id}/subtasks/{sub_id}", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "POST", + "path": "/calendar/entries/bulk", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "GET", + "path": "/calendar/kanban", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "GET", + "path": "/calendar/{calendar_id}/ics-feed", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "POST", + "path": "/calendar/import", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "POST", + "path": "/calendar/entries/{entry_id}/book-resource", + "file": "app/plugins/builtins/calendar/routes.py" + }, + { + "method": "GET", + "path": "/facets", + "file": "app/plugins/builtins/unified_search/routes.py" + }, + { + "method": "GET", + "path": "/suggest", + "file": "app/plugins/builtins/unified_search/routes.py" + }, + { + "method": "POST", + "path": "/similar", + "file": "app/plugins/builtins/unified_search/routes.py" + }, + { + "method": "POST", + "path": "/reindex", + "file": "app/plugins/builtins/unified_search/routes.py" + }, + { + "method": "POST", + "path": "/rebuild/{entity_type}/{entity_id}", + "file": "app/plugins/builtins/unified_search/routes.py" + }, + { + "method": "POST", + "path": "/purge/{entity_type}/{entity_id}", + "file": "app/plugins/builtins/unified_search/routes.py" + }, + { + "method": "GET", + "path": "/providers", + "file": "app/plugins/builtins/unified_search/routes.py" + }, + { + "method": "POST", + "path": "/providers/{entity_type}/toggle", + "file": "app/plugins/builtins/unified_search/routes.py" + }, + { + "method": "GET", + "path": "/stats", + "file": "app/plugins/builtins/unified_search/routes.py" + }, + { + "method": "GET", + "path": "/search", + "file": "app/plugins/builtins/agent_memory/routes.py" + }, + { + "method": "PATCH", + "path": "/{memory_id}", + "file": "app/plugins/builtins/agent_memory/routes.py" + }, + { + "method": "DELETE", + "path": "/{memory_id}", + "file": "app/plugins/builtins/agent_memory/routes.py" + }, + { + "method": "GET", + "path": "/listings", + "file": "app/plugins/builtins/marketplace/routes.py" + }, + { + "method": "GET", + "path": "/listings/{name}", + "file": "app/plugins/builtins/marketplace/routes.py" + }, + { + "method": "POST", + "path": "/install/{name}", + "file": "app/plugins/builtins/marketplace/routes.py" + }, + { + "method": "POST", + "path": "/verify/{name}", + "file": "app/plugins/builtins/marketplace/routes.py" + }, + { + "method": "GET", + "path": "/categories", + "file": "app/plugins/builtins/marketplace/routes.py" + }, + { + "method": "POST", + "path": "/relationships", + "file": "app/plugins/builtins/graph_rag/routes.py" + }, + { + "method": "GET", + "path": "/relationships", + "file": "app/plugins/builtins/graph_rag/routes.py" + }, + { + "method": "POST", + "path": "/traverse", + "file": "app/plugins/builtins/graph_rag/routes.py" + }, + { + "method": "DELETE", + "path": "/relationships/{relationship_id}", + "file": "app/plugins/builtins/graph_rag/routes.py" + }, + { + "method": "GET", + "path": "/presets", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "POST", + "path": "/presets/generate", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "GET", + "path": "/templates", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "POST", + "path": "/templates", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "GET", + "path": "/templates/{template_id}", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "PUT", + "path": "/templates/{template_id}", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "DELETE", + "path": "/templates/{template_id}", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "POST", + "path": "/generate", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "POST", + "path": "/generate/async", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "GET", + "path": "/{report_id}", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "GET", + "path": "/{report_id}/download", + "file": "app/plugins/builtins/report_generator/routes.py" + }, + { + "method": "POST", + "path": "/files/{file_id}/link", + "file": "app/plugins/builtins/entity_links/routes.py" + }, + { + "method": "DELETE", + "path": "/files/{file_id}/link", + "file": "app/plugins/builtins/entity_links/routes.py" + }, + { + "method": "GET", + "path": "/files/{file_id}/links", + "file": "app/plugins/builtins/entity_links/routes.py" + }, + { + "method": "GET", + "path": "/conversations", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/conversations", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "GET", + "path": "/conversations/{conversation_id}", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "PATCH", + "path": "/conversations/{conversation_id}", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "DELETE", + "path": "/conversations/{conversation_id}", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/conversations/{conversation_id}/pin", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "DELETE", + "path": "/conversations/{conversation_id}/pin", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/conversations/{conversation_id}/mute", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "DELETE", + "path": "/conversations/{conversation_id}/mute", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/conversations/{conversation_id}/participants", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "DELETE", + "path": "/conversations/{conversation_id}/participants/{participant_id}", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "PATCH", + "path": "/conversations/{conversation_id}/participants/{participant_id}", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "GET", + "path": "/conversations/{conversation_id}/messages", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/conversations/{conversation_id}/messages", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "PATCH", + "path": "/messages/{message_id}", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "DELETE", + "path": "/messages/{message_id}", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/messages/{message_id}/attachments", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/messages/{message_id}/reactions", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "DELETE", + "path": "/messages/{message_id}/reactions/{emoji}", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/conversations/{conversation_id}/read", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "GET", + "path": "/miniapps", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/conversations/{conversation_id}/miniapps", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "GET", + "path": "/block-types", + "file": "app/plugins/builtins/kommunikation/routes.py" + }, + { + "method": "POST", + "path": "/context", + "file": "app/plugins/builtins/ai_proactive/routes.py" + }, + { + "method": "GET", + "path": "/suggestions", + "file": "app/plugins/builtins/ai_proactive/routes.py" + }, + { + "method": "POST", + "path": "/suggestions/{suggestion_id}/dismiss", + "file": "app/plugins/builtins/ai_proactive/routes.py" + }, + { + "method": "POST", + "path": "/suggestions/{suggestion_id}/act", + "file": "app/plugins/builtins/ai_proactive/routes.py" + }, + { + "method": "GET", + "path": "/suggestions/stream", + "file": "app/plugins/builtins/ai_proactive/routes.py" + }, + { + "method": "GET", + "path": "/settings", + "file": "app/plugins/builtins/ai_proactive/routes.py" + }, + { + "method": "PUT", + "path": "/settings", + "file": "app/plugins/builtins/ai_proactive/routes.py" + }, + { + "method": "GET", + "path": "/stats", + "file": "app/plugins/builtins/ai_proactive/routes.py" + }, + { + "method": "POST", + "path": "/{agent_id}/run", + "file": "app/plugins/builtins/ai_assistant/external_api.py" + }, + { + "method": "GET", + "path": "/{agent_id}/status", + "file": "app/plugins/builtins/ai_assistant/external_api.py" + }, + { + "method": "POST", + "path": "/{agent_id}/stream", + "file": "app/plugins/builtins/ai_assistant/external_api.py" + }, + { + "method": "GET", + "path": "/providers", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "POST", + "path": "/providers", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "PUT", + "path": "/providers/{provider_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "DELETE", + "path": "/providers/{provider_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "GET", + "path": "/models", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "POST", + "path": "/models", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "PUT", + "path": "/models/{model_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "DELETE", + "path": "/models/{model_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "GET", + "path": "/presets", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "POST", + "path": "/presets", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "PUT", + "path": "/presets/{preset_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "DELETE", + "path": "/presets/{preset_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "GET", + "path": "/agents", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "POST", + "path": "/agents", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "PUT", + "path": "/agents/{agent_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "DELETE", + "path": "/agents/{agent_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "GET", + "path": "/tools", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "GET", + "path": "/sessions", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "POST", + "path": "/sessions", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "PUT", + "path": "/sessions/{session_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "DELETE", + "path": "/sessions/{session_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "GET", + "path": "/sessions/{session_id}/messages", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "POST", + "path": "/sessions/{session_id}/stream", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "GET", + "path": "/folders", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "POST", + "path": "/folders", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "PUT", + "path": "/folders/{folder_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "DELETE", + "path": "/folders/{folder_id}", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "POST", + "path": "/sessions/{session_id}/attachments", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "GET", + "path": "/sessions/{session_id}/attachments", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "GET", + "path": "/attachments/{attachment_id}/download", + "file": "app/plugins/builtins/ai_assistant/routes.py" + }, + { + "method": "PATCH", + "path": "/{tag_id}", + "file": "app/plugins/builtins/tags/routes.py" + }, + { + "method": "POST", + "path": "/assign", + "file": "app/plugins/builtins/tags/routes.py" + }, + { + "method": "DELETE", + "path": "/assign", + "file": "app/plugins/builtins/tags/routes.py" + }, + { + "method": "DELETE", + "path": "/{tag_id}", + "file": "app/plugins/builtins/tags/routes.py" + }, + { + "method": "POST", + "path": "/bulk-assign", + "file": "app/plugins/builtins/tags/routes.py" + }, + { + "method": "GET", + "path": "/{tag_id}/entities", + "file": "app/plugins/builtins/tags/routes.py" + }, + { + "method": "GET", + "path": "/files/{file_id}/permissions", + "file": "app/plugins/builtins/permissions/routes.py" + }, + { + "method": "POST", + "path": "/files/{file_id}/permissions", + "file": "app/plugins/builtins/permissions/routes.py" + }, + { + "method": "DELETE", + "path": "/files/{file_id}/permissions/{user_id}", + "file": "app/plugins/builtins/permissions/routes.py" + }, + { + "method": "POST", + "path": "/files/{file_id}/share-link", + "file": "app/plugins/builtins/permissions/routes.py" + }, + { + "method": "DELETE", + "path": "/share-links/{link_id}", + "file": "app/plugins/builtins/permissions/routes.py" + }, + { + "method": "GET", + "path": "/{token}", + "file": "app/plugins/builtins/permissions/public_routes.py" + }, + { + "method": "POST", + "path": "/{token}/verify", + "file": "app/plugins/builtins/permissions/public_routes.py" + }, + { + "method": "GET", + "path": "/{token}/download", + "file": "app/plugins/builtins/permissions/public_routes.py" + }, + { + "method": "GET", + "path": "/tools", + "file": "app/plugins/builtins/mcp_server/routes.py" + }, + { + "method": "POST", + "path": "/tools/{tool_name}/execute", + "file": "app/plugins/builtins/mcp_server/routes.py" + }, + { + "method": "GET", + "path": "/config", + "file": "app/plugins/builtins/mcp_server/routes.py" + }, + { + "method": "POST", + "path": "/command", + "file": "app/plugins/builtins/ai_ui_control/routes.py" + }, + { + "method": "GET", + "path": "/command/{command_id}/status", + "file": "app/plugins/builtins/ai_ui_control/routes.py" + }, + { + "method": "GET", + "path": "/online-users", + "file": "app/plugins/builtins/ai_ui_control/routes.py" + } + ], + "issues": [ + { + "plugin": "mcp_client", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "forgejo_error_reporter", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "automation", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "dms", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "mail", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "migrations", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "tests", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "contacts", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "tasks", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "calendar", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "unified_search", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "agent_memory", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "marketplace", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "graph_rag", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "report_generator", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "system_notif", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "entity_links", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "kommunikation", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "ai_proactive", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "ai_assistant", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "tags", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "permissions", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "mcp_server", + "issue": "No manifest.py", + "severity": "HIGH" + }, + { + "plugin": "ai_ui_control", + "issue": "No manifest.py", + "severity": "HIGH" + } + ] +} \ No newline at end of file diff --git a/scripts/test_suite/results_trace_stores.json b/scripts/test_suite/results_trace_stores.json new file mode 100644 index 0000000..de28aa5 --- /dev/null +++ b/scripts/test_suite/results_trace_stores.json @@ -0,0 +1,701 @@ +{ + "stores": { + "calendarStore": [ + "setVisibleWeek", + "toggleCalendarVisibility", + "goToNextDay", + "goToToday", + "setVisibleMonth", + "setActiveCalendarId", + "goToPrevDay", + "goToPrevWeek", + "setRangeEnd", + "visibleMonth", + "rangeStart", + "visibleWeek", + "selectedEntry", + "activeCalendarId", + "rangeEnd", + "setVisibleDay", + "viewMode", + "goToNextMonth", + "visibleDay", + "date", + "setViewMode", + "entry", + "visibleCalendarIds", + "setRangeStart", + "mode", + "goToPrevMonth", + "setSelectedEntry", + "calendars", + "goToNextWeek", + "setCalendars" + ], + "aiUIControlStore": [ + "setAIActive", + "value", + "section", + "entity", + "pendingSettings", + "settings", + "clearPending", + "setActiveCommand", + "status", + "activeTab", + "pendingFilter", + "setConnected", + "activeModal", + "lastFeedback", + "connected", + "setLastFeedback", + "feedback", + "command_id", + "setPendingSettings", + "commandHistory", + "setActiveModal", + "active", + "addCommandToHistory", + "activeCommand", + "action", + "filter", + "setPendingFilter", + "aiActive", + "setActiveTab", + "modal" + ], + "onboardingStore": [ + "completed", + "isActive", + "goToStep", + "prev", + "start", + "skipped", + "complete", + "reset", + "next", + "skip", + "data", + "step" + ], + "windowStore": [ + "windows", + "minimizeWindow", + "toggleAiChat", + "nextZIndex", + "openWindow", + "const", + "restoreWindow", + "updateWindowSize", + "height", + "newWindow", + "zIndex", + "aiChatVisible", + "config", + "type", + "title", + "position", + "componentProps", + "setActiveWindow", + "updateWindowPosition", + "closeWindow", + "activeWindowId", + "null", + "component", + "size", + "toggleFullscreen", + "width" + ], + "commandPaletteStore": [ + "close", + "toggle", + "isOpen", + "open" + ], + "pluginToolbarStore": [ + "updateItem", + "value", + "setActivePlugin", + "label", + "registerItems", + "plugin", + "unregisterPlugin", + "updates", + "query", + "activePlugin", + "null", + "items", + "onClick" + ], + "commStore": [ + "file_type", + "sort_order", + "locked_by", + "is_direct", + "conversation_id", + "file_size", + "sender_id", + "content", + "participants", + "addMessage", + "role", + "setActiveConversation", + "setTyping", + "setConversations", + "created_at", + "participant_id", + "typingUsers", + "userIds", + "file_name", + "reply_to_id", + "participant_type", + "setMessages", + "conv", + "title", + "blocks", + "edited_at", + "activeConversationId", + "sender_type", + "file_id", + "last_msg_at", + "updateConversation", + "msgs", + "display_name", + "count", + "is_archived", + "reactions", + "loading", + "setLoading", + "created_by_type", + "attachments", + "created_by", + "last_msg_preview", + "setUnread", + "block_data", + "file_source", + "convId", + "is_pinned", + "messages", + "convs", + "content_format", + "unread_count", + "conversations", + "thumbnail_path", + "is_locked", + "last_msg_sender_type", + "block_type", + "unreadCounts", + "metadata" + ], + "pluginStore": [ + "permission", + "is_core", + "getDetailTabsForEntity", + "default_value", + "entity", + "setManifests", + "loaded", + "badge_key", + "error", + "entityType", + "parent", + "row_span", + "custom_fields", + "detail_tabs", + "dashboard_widgets", + "col_span", + "reset", + "order", + "getCustomFieldsForEntity", + "getAllDashboardWidgets", + "display_name", + "protected", + "version", + "label", + "required", + "path", + "getAllMenuItems", + "loading", + "getAllSettingsPages", + "setLoading", + "field_type", + "group", + "options", + "component", + "page_routes", + "entity_type", + "manifests", + "label_key", + "settings_pages", + "getAllPageRoutes", + "menu_items", + "icon", + "setError" + ], + "themeStore": [ + "borderRadius", + "applyTheme", + "toggleDarkMode", + "config", + "loadFromStorage", + "accentColor", + "DEFAULT_THEME", + "primaryColor", + "target", + "darkMode", + "amount", + "result", + "setTheme", + "saveToStorage", + "base", + "fontFamily", + "scales" + ], + "uiStore": [ + "message", + "suggestionSidebarOpen", + "setAISidebarCollapsed", + "openAISidebarProactive", + "locale", + "theme", + "clearToasts", + "toggleAISidebar", + "setLocale", + "open", + "type", + "setTheme", + "collapsed", + "toggleSidebar", + "setAISidebarTab", + "aiSidebarCollapsed", + "clearNotifications", + "setSidebarOpen", + "setMessageSidebarCollapsed", + "removeToast", + "notifications", + "toggleSuggestionSidebar", + "toggleMessageSidebar", + "toasts", + "messageSidebarCollapsed", + "toast", + "removeNotification", + "index", + "addToast", + "sidebarOpen", + "aiSidebarTab" + ], + "workspaceStore": [ + "is_visible", + "widgets", + "context", + "height", + "config", + "modules", + "visibleModuleKeys", + "workspaces", + "workspace_id", + "setActiveWorkspace", + "menu_order", + "position_x", + "is_active", + "reset", + "description", + "isModuleVisible", + "position_y", + "widget_key", + "isLoading", + "activeWorkspaceId", + "moduleKey", + "loading", + "setLoading", + "setMyWorkspaces", + "module_key", + "setContext", + "is_default", + "myWorkspaces", + "width", + "hasWorkspaces", + "icon" + ], + "authStore": [ + "currentTenant", + "setUser", + "setAuthenticated", + "first_name", + "is_system_admin", + "role", + "isAuthenticated", + "field_permissions", + "error", + "setError", + "perms", + "fieldPerms", + "isLoading", + "tenant", + "loading", + "setLoading", + "slug", + "permissions", + "last_name", + "user", + "isSystemAdmin", + "authed", + "setPermissions", + "avatar_url", + "tenants", + "setTenant", + "email", + "logout" + ] + }, + "usage": {}, + "unused": { + "calendarStore": [ + "setVisibleWeek", + "toggleCalendarVisibility", + "goToNextDay", + "goToToday", + "setVisibleMonth", + "setActiveCalendarId", + "goToPrevDay", + "goToPrevWeek", + "setRangeEnd", + "visibleMonth", + "rangeStart", + "visibleWeek", + "selectedEntry", + "activeCalendarId", + "rangeEnd", + "setVisibleDay", + "viewMode", + "goToNextMonth", + "visibleDay", + "date", + "setViewMode", + "entry", + "visibleCalendarIds", + "setRangeStart", + "mode", + "goToPrevMonth", + "setSelectedEntry", + "calendars", + "goToNextWeek", + "setCalendars" + ], + "aiUIControlStore": [ + "setAIActive", + "value", + "section", + "entity", + "pendingSettings", + "settings", + "clearPending", + "setActiveCommand", + "status", + "activeTab", + "pendingFilter", + "setConnected", + "activeModal", + "lastFeedback", + "connected", + "setLastFeedback", + "feedback", + "command_id", + "setPendingSettings", + "commandHistory", + "setActiveModal", + "active", + "addCommandToHistory", + "activeCommand", + "action", + "filter", + "setPendingFilter", + "aiActive", + "setActiveTab", + "modal" + ], + "onboardingStore": [ + "completed", + "isActive", + "goToStep", + "prev", + "start", + "skipped", + "complete", + "reset", + "next", + "skip", + "data", + "step" + ], + "windowStore": [ + "windows", + "minimizeWindow", + "toggleAiChat", + "nextZIndex", + "openWindow", + "const", + "restoreWindow", + "updateWindowSize", + "height", + "newWindow", + "zIndex", + "aiChatVisible", + "config", + "type", + "title", + "position", + "componentProps", + "setActiveWindow", + "updateWindowPosition", + "closeWindow", + "activeWindowId", + "null", + "component", + "size", + "toggleFullscreen", + "width" + ], + "commandPaletteStore": [ + "close", + "toggle", + "isOpen", + "open" + ], + "pluginToolbarStore": [ + "updateItem", + "value", + "setActivePlugin", + "label", + "registerItems", + "plugin", + "unregisterPlugin", + "updates", + "query", + "activePlugin", + "null", + "items", + "onClick" + ], + "commStore": [ + "file_type", + "sort_order", + "locked_by", + "is_direct", + "conversation_id", + "file_size", + "sender_id", + "content", + "participants", + "addMessage", + "role", + "setActiveConversation", + "setTyping", + "setConversations", + "created_at", + "participant_id", + "typingUsers", + "userIds", + "file_name", + "reply_to_id", + "participant_type", + "setMessages", + "conv", + "title", + "blocks", + "edited_at", + "activeConversationId", + "sender_type", + "file_id", + "last_msg_at", + "updateConversation", + "msgs", + "display_name", + "count", + "is_archived", + "reactions", + "loading", + "setLoading", + "created_by_type", + "attachments", + "created_by", + "last_msg_preview", + "setUnread", + "block_data", + "file_source", + "convId", + "is_pinned", + "messages", + "convs", + "content_format", + "unread_count", + "conversations", + "thumbnail_path", + "is_locked", + "last_msg_sender_type", + "block_type", + "unreadCounts", + "metadata" + ], + "pluginStore": [ + "permission", + "is_core", + "getDetailTabsForEntity", + "default_value", + "entity", + "setManifests", + "loaded", + "badge_key", + "error", + "entityType", + "parent", + "row_span", + "custom_fields", + "detail_tabs", + "dashboard_widgets", + "col_span", + "reset", + "order", + "getCustomFieldsForEntity", + "getAllDashboardWidgets", + "display_name", + "protected", + "version", + "label", + "required", + "path", + "getAllMenuItems", + "loading", + "getAllSettingsPages", + "setLoading", + "field_type", + "group", + "options", + "component", + "page_routes", + "entity_type", + "manifests", + "label_key", + "settings_pages", + "getAllPageRoutes", + "menu_items", + "icon", + "setError" + ], + "themeStore": [ + "borderRadius", + "applyTheme", + "toggleDarkMode", + "config", + "loadFromStorage", + "accentColor", + "DEFAULT_THEME", + "primaryColor", + "target", + "darkMode", + "amount", + "result", + "setTheme", + "saveToStorage", + "base", + "fontFamily", + "scales" + ], + "uiStore": [ + "message", + "suggestionSidebarOpen", + "setAISidebarCollapsed", + "openAISidebarProactive", + "locale", + "theme", + "clearToasts", + "toggleAISidebar", + "setLocale", + "open", + "type", + "setTheme", + "collapsed", + "toggleSidebar", + "setAISidebarTab", + "aiSidebarCollapsed", + "clearNotifications", + "setSidebarOpen", + "setMessageSidebarCollapsed", + "removeToast", + "notifications", + "toggleSuggestionSidebar", + "toggleMessageSidebar", + "toasts", + "messageSidebarCollapsed", + "toast", + "removeNotification", + "index", + "addToast", + "sidebarOpen", + "aiSidebarTab" + ], + "workspaceStore": [ + "is_visible", + "widgets", + "context", + "height", + "config", + "modules", + "visibleModuleKeys", + "workspaces", + "workspace_id", + "setActiveWorkspace", + "menu_order", + "position_x", + "is_active", + "reset", + "description", + "isModuleVisible", + "position_y", + "widget_key", + "isLoading", + "activeWorkspaceId", + "moduleKey", + "loading", + "setLoading", + "setMyWorkspaces", + "module_key", + "setContext", + "is_default", + "myWorkspaces", + "width", + "hasWorkspaces", + "icon" + ], + "authStore": [ + "currentTenant", + "setUser", + "setAuthenticated", + "first_name", + "is_system_admin", + "role", + "isAuthenticated", + "field_permissions", + "error", + "setError", + "perms", + "fieldPerms", + "isLoading", + "tenant", + "loading", + "setLoading", + "slug", + "permissions", + "last_name", + "user", + "isSystemAdmin", + "authed", + "setPermissions", + "avatar_url", + "tenants", + "setTenant", + "email", + "logout" + ] + } +} \ No newline at end of file diff --git a/scripts/test_suite/trace_api_contracts.py b/scripts/test_suite/trace_api_contracts.py new file mode 100644 index 0000000..429b798 --- /dev/null +++ b/scripts/test_suite/trace_api_contracts.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python3 +"""Trace API Contracts — Find Frontend↔Backend Verkabelungsfehler. + +Compares every frontend API call with the backend response fields. +Mismatches = Verkabelungsfehler. + +Usage: python3 scripts/test_suite/trace_api_contracts.py +""" +from __future__ import annotations + +import ast +import json +import os +import re +import sys +from collections import defaultdict +from pathlib import Path + +# Project root +ROOT = Path(__file__).resolve().parent.parent.parent +FRONTEND_SRC = ROOT / "frontend" / "src" +BACKEND_APP = ROOT / "app" + + +def find_frontend_api_calls() -> list[dict]: + """Find all API calls in frontend source.""" + calls = [] + api_dir = FRONTEND_SRC / "api" + if not api_dir.exists(): + return calls + + for ts_file in api_dir.rglob("*.ts"): + try: + content = ts_file.read_text(encoding="utf-8") + except Exception: + continue + + rel_path = str(ts_file.relative_to(FRONTEND_SRC)) + + # Find apiGet/apiPost/apiPut/apiPatch/apiDelete calls with URL patterns + patterns = [ + r"apiGet(?:<[^>]+>)?\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + r"apiPost(?:<[^>]+>)?\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + r"apiPut(?:<[^>]+>)?\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + r"apiPatch(?:<[^>]+>)?\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + r"apiDelete(?:<[^>]+>)?\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + r"\bM\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + r"\bj\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + r"\bSt\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + r"\byt\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + r"\bxt\s*\(\s*[`'\"]([^`'\"]+)[`'\"]\s*", + ] + + for pattern in patterns: + for match in re.finditer(pattern, content): + url = match.group(1) + # Normalize URL — remove template literals, query params + url_clean = re.sub(r"\$\{[^}]+\}", "{param}", url) + url_clean = url_clean.split("?")[0].split("#")[0] + # Determine method from function name + method = "GET" + if "Post" in pattern or "\bj\s*" in pattern: + method = "POST" + elif "Put" in pattern: + method = "PUT" + elif "Patch" in pattern or "\byt\s*" in pattern: + method = "PATCH" + elif "Delete" in pattern or "\bxt\s*" in pattern: + method = "DELETE" + + calls.append({ + "file": rel_path, + "method": method, + "url": url_clean, + "raw_url": url, + }) + + return calls + + +def find_frontend_expected_fields() -> dict[str, list[str]]: + """Find TypeScript interfaces/types that define expected API response fields.""" + fields_by_url = defaultdict(list) + api_dir = FRONTEND_SRC / "api" + if not api_dir.exists(): + return fields_by_url + + for ts_file in api_dir.rglob("*.ts"): + try: + content = ts_file.read_text(encoding="utf-8") + except Exception: + continue + + # Find interface definitions + for match in re.finditer(r"interface\s+(\w+)\s*\{([^}]+)\}", content): + iface_name = match.group(1) + body = match.group(2) + field_names = re.findall(r"(\w+)\s*[?:]", body) + fields_by_url[iface_name] = field_names + + return fields_by_url + + +def find_backend_response_fields() -> list[dict]: + """Find backend response fields from routes.""" + responses = [] + routes_dir = BACKEND_APP / "routes" + if not routes_dir.exists(): + return responses + + for py_file in routes_dir.rglob("*.py"): + try: + content = py_file.read_text(encoding="utf-8") + except Exception: + continue + + rel_path = str(py_file.relative_to(BACKEND_APP)) + + # Find response_model definitions + for match in re.finditer(r"response_model\s*=\s*(\w+)", content): + responses.append({ + "file": rel_path, + "response_model": match.group(1), + }) + + # Find content= dict definitions (inline responses) + for match in re.finditer(r"content\s*=\s*\{([^}]+)\}", content, re.DOTALL): + body = match.group(1) + fields = re.findall(r"['\"](\w+)['\"]\s*:", body) + if fields: + responses.append({ + "file": rel_path, + "fields": fields, + "type": "inline", + }) + + # Find return dict patterns + for match in re.finditer(r"return\s*\{([^}]+)\}", content, re.DOTALL): + body = match.group(1) + fields = re.findall(r"['\"](\w+)['\"]\s*:", body) + if fields: + responses.append({ + "file": rel_path, + "fields": fields, + "type": "return_dict", + }) + + return responses + + +def find_backend_endpoints() -> list[dict]: + """Find all backend endpoints from route decorators.""" + endpoints = [] + routes_dir = BACKEND_APP / "routes" + if not routes_dir.exists(): + return endpoints + + for py_file in routes_dir.rglob("*.py"): + try: + content = py_file.read_text(encoding="utf-8") + except Exception: + continue + + rel_path = str(py_file.relative_to(BACKEND_APP)) + + # Find router decorators + for match in re.finditer(r"@router\.(get|post|put|patch|delete)\s*\(\s*[\"']([^\"']+)[\"']", content): + method = match.group(1).upper() + path = match.group(2) + endpoints.append({ + "file": rel_path, + "method": method, + "path": path, + }) + + # Also find plugin routes + plugins_dir = BACKEND_APP / "plugins" / "builtins" + if plugins_dir.exists(): + for py_file in plugins_dir.rglob("*.py"): + try: + content = py_file.read_text(encoding="utf-8") + except Exception: + continue + + rel_path = str(py_file.relative_to(BACKEND_APP)) + for match in re.finditer(r"@router\.(get|post|put|patch|delete)\s*\(\s*[\"']([^\"']+)[\"']", content): + method = match.group(1).upper() + path = match.group(2) + endpoints.append({ + "file": rel_path, + "method": method, + "path": path, + "is_plugin": True, + }) + + return endpoints + + +def normalize_path(path: str) -> str: + """Normalize a path for comparison.""" + # Remove leading /api/v1 if present + path = re.sub(r"^/api/v1/", "/", path) + # Replace {param} patterns + path = re.sub(r"\{[^}]+\}", "{param}", path) + # Remove trailing slash + path = path.rstrip("/") + return path + + +def check_frontend_calls_have_backend(frontend_calls, backend_endpoints) -> list[dict]: + """Check if every frontend API call has a matching backend endpoint.""" + backend_paths = set() + for ep in backend_endpoints: + normalized = normalize_path(ep["path"]) + backend_paths.add((ep["method"], normalized)) + + mismatches = [] + for call in frontend_calls: + url = call["url"] + # Remove /api/v1 prefix if present + url_normalized = normalize_path(url) + key = (call["method"], url_normalized) + + if key not in backend_paths: + # Try without method (some endpoints might have different method mapping) + found = False + for bm, bp in backend_paths: + if bp == url_normalized: + found = True + break + if not found: + mismatches.append({ + "type": "frontend_call_no_backend", + "method": call["method"], + "url": url, + "file": call["file"], + "severity": "HIGH", + "message": f"Frontend calls {call['method']} {url} but no matching backend endpoint found", + }) + + return mismatches + + +def check_backend_endpoints_have_frontend(frontend_calls, backend_endpoints) -> list[dict]: + """Check if every backend endpoint is called by the frontend.""" + frontend_paths = set() + for call in frontend_calls: + url_normalized = normalize_path(call["url"]) + frontend_paths.add((call["method"], url_normalized)) + + mismatches = [] + for ep in backend_endpoints: + normalized = normalize_path(ep["path"]) + key = (ep["method"], normalized) + + if key not in frontend_paths: + mismatches.append({ + "type": "backend_endpoint_no_frontend", + "method": ep["method"], + "path": ep["path"], + "file": ep["file"], + "severity": "LOW", + "message": f"Backend endpoint {ep['method']} {ep['path']} is never called by frontend", + }) + + return mismatches + + +def main(): + print("=" * 70) + print("TRACE API CONTRACTS — Frontend ↔ Backend Verkabelungsfehler") + print("=" * 70) + + print("\n[1] Scanning frontend API calls...") + frontend_calls = find_frontend_api_calls() + print(f" Found {len(frontend_calls)} API calls in frontend") + + print("\n[2] Scanning backend endpoints...") + backend_endpoints = find_backend_endpoints() + print(f" Found {len(backend_endpoints)} endpoints in backend") + + print("\n[3] Scanning backend response fields...") + backend_responses = find_backend_response_fields() + print(f" Found {len(backend_responses)} response definitions") + + print("\n[4] Scanning frontend expected fields...") + frontend_fields = find_frontend_expected_fields() + print(f" Found {len(frontend_fields)} TypeScript interfaces") + + print("\n[5] Checking frontend calls have matching backend...") + missing_backend = check_frontend_calls_have_backend(frontend_calls, backend_endpoints) + print(f" Found {len(missing_backend)} frontend calls without backend endpoint") + + print("\n[6] Checking backend endpoints are called by frontend...") + missing_frontend = check_backend_endpoints_have_frontend(frontend_calls, backend_endpoints) + print(f" Found {len(missing_frontend)} backend endpoints never called by frontend") + + # Summary + all_issues = missing_backend + missing_frontend + + print("\n" + "=" * 70) + print("SUMMARY") + print("=" * 70) + print(f"Frontend API calls: {len(frontend_calls)}") + print(f"Backend endpoints: {len(backend_endpoints)}") + print(f"Response definitions: {len(backend_responses)}") + print(f"TS interfaces: {len(frontend_fields)}") + print(f"\nISSUES FOUND: {len(all_issues)}") + print(f" HIGH (frontend calls missing backend): {len(missing_backend)}") + print(f" LOW (backend endpoints not called): {len(missing_frontend)}") + + if missing_backend: + print("\n--- HIGH SEVERITY: Frontend calls without backend ---") + for m in missing_backend[:30]: + print(f" [{m['method']}] {m['url']} (in {m['file']})") + if len(missing_backend) > 30: + print(f" ... and {len(missing_backend) - 30} more") + + if missing_frontend: + print("\n--- LOW SEVERITY: Backend endpoints never called by frontend ---") + for m in missing_frontend[:30]: + print(f" [{m['method']}] {m['path']} (in {m['file']})") + if len(missing_frontend) > 30: + print(f" ... and {len(missing_frontend) - 30} more") + + # Write results to JSON + results_file = ROOT / "scripts" / "test_suite" / "results_trace_api_contracts.json" + with open(results_file, "w") as f: + json.dump({ + "frontend_calls": frontend_calls, + "backend_endpoints": backend_endpoints, + "issues": all_issues, + "summary": { + "total_frontend_calls": len(frontend_calls), + "total_backend_endpoints": len(backend_endpoints), + "total_issues": len(all_issues), + "high_severity": len(missing_backend), + "low_severity": len(missing_frontend), + }, + }, f, indent=2, default=str) + print(f"\nResults written to {results_file}") + + return len(all_issues) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/test_suite/trace_contracts.py b/scripts/test_suite/trace_contracts.py new file mode 100644 index 0000000..eb75e9d --- /dev/null +++ b/scripts/test_suite/trace_contracts.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Trace Contracts — Find Contract attribute mismatches (like GraphRagContract).""" +from __future__ import annotations +import ast, re, sys, json +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent.parent +APP = ROOT / "app" + +def find_contracts() -> list[dict]: + contracts = [] + for py in APP.rglob("*.py"): + try: content = py.read_text(); tree = ast.parse(content) + except: continue + rel = str(py.relative_to(ROOT)) + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef): + if "Contract" in node.name or node.name.endswith("Contract"): + attrs = [] + for item in node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + attrs.append(item.target.id) + elif isinstance(item, ast.Assign): + for t in item.targets: + if isinstance(t, ast.Name): attrs.append(t.id) + contracts.append({"name": node.name, "file": rel, "attributes": attrs}) + return contracts + +def find_contract_access() -> list[dict]: + accesses = [] + for py in APP.rglob("*.py"): + try: content = py.read_text() + except: continue + rel = str(py.relative_to(ROOT)) + for m in re.finditer(r'(\w+Contract)\.(\w+)', content): + accesses.append({"contract": m.group(1), "attribute": m.group(2), "file": rel}) + return accesses + +def main(): + print("=" * 70) + print("TRACE CONTRACTS — Contract Attribute Mismatches") + print("=" * 70) + contracts = find_contracts() + accesses = find_contract_access() + contract_attrs = {c["name"]: set(c["attributes"]) for c in contracts} + mismatches = [] + for acc in accesses: + cname = acc["contract"] + attr = acc["attribute"] + if cname in contract_attrs and attr not in contract_attrs[cname]: + mismatches.append(acc) + print(f"\nContracts found: {len(contracts)}") + print(f"Contract accesses: {len(accesses)}") + print(f"\nMISMATCHES: {len(mismatches)}") + if mismatches: + print("\n--- Contract attribute mismatches ---") + for m in mismatches: print(f" {m['contract']}.{m['attribute']} (in {m['file']})") + results = {"contracts": contracts, "accesses": accesses, "mismatches": mismatches} + with open(ROOT / "scripts" / "test_suite" / "results_trace_contracts.json", "w") as f: + json.dump(results, f, indent=2, default=str) + return len(mismatches) + +if __name__ == "__main__": sys.exit(main()) diff --git a/scripts/test_suite/trace_functions.py b/scripts/test_suite/trace_functions.py new file mode 100644 index 0000000..562a207 --- /dev/null +++ b/scripts/test_suite/trace_functions.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +"""Trace Functions — Find dead functions (defined but never called).""" +from __future__ import annotations +import ast, re, sys, json +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent.parent +APP = ROOT / "app" +FRONTEND = ROOT / "frontend" / "src" + +# Critical functions that MUST be called +CRITICAL_FUNCTIONS = [ + "seed_admin", "seed_default_workspace", "sync_plugin_schema", + "init_permission_registry", "register_default_entities", "register_default_history_hooks", + "discover_builtins", "load_tmp_chats", "initialize_agent", +] + +def find_function_defs_py() -> list[dict]: + defs = [] + for py in APP.rglob("*.py"): + try: content = py.read_text(); tree = ast.parse(content) + except: continue + rel = str(py.relative_to(ROOT)) + for node in ast.walk(tree): + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + if not node.name.startswith("_") and len(node.name) > 3: + defs.append({"name": node.name, "file": rel, "line": node.lineno, "lang": "py"}) + return defs + +def find_function_calls_py() -> set[str]: + calls = set() + for py in APP.rglob("*.py"): + try: content = py.read_text(); tree = ast.parse(content) + except: continue + for node in ast.walk(tree): + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Name): + calls.add(node.func.id) + elif isinstance(node.func, ast.Attribute): + calls.add(node.func.attr) + return calls + +def find_function_defs_ts() -> list[dict]: + defs = [] + for ts in FRONTEND.rglob("*.ts"): + try: content = ts.read_text() + except: continue + rel = str(ts.relative_to(ROOT)) + for m in re.finditer(r'(?:export\s+)?(?:async\s+)?function\s+(\w+)', content): + name = m.group(1) + if not name.startswith("_") and len(name) > 3: + defs.append({"name": name, "file": rel, "lang": "ts"}) + for m in re.finditer(r'(?:export\s+)?const\s+(\w+)\s*=', content): + name = m.group(1) + if not name.startswith("_") and len(name) > 3: + defs.append({"name": name, "file": rel, "lang": "ts"}) + return defs + +def find_function_calls_ts() -> set[str]: + calls = set() + for ts in FRONTEND.rglob("*.ts"): + try: content = ts.read_text() + except: continue + # Find function calls: word followed by ( + for m in re.finditer(r'\b(\w+)\s*\(', content): + calls.add(m.group(1)) + return calls + +def main(): + print("=" * 70) + print("TRACE FUNCTIONS — Dead Functions (defined but never called)") + print("=" * 70) + py_defs = find_function_defs_py() + py_calls = find_function_calls_py() + ts_defs = find_function_defs_ts() + ts_calls = find_function_calls_ts() + py_dead = [d for d in py_defs if d["name"] not in py_calls] + ts_dead = [d for d in ts_defs if d["name"] not in ts_calls] + critical_dead = [d for d in py_dead if d["name"] in CRITICAL_FUNCTIONS] + print(f"\nPython functions defined: {len(py_defs)}") + print(f"Python functions called: {len(py_calls)}") + print(f"Python dead functions: {len(py_dead)}") + print(f"TS functions defined: {len(ts_defs)}") + print(f"TS functions called: {len(ts_calls)}") + print(f"TS dead functions: {len(ts_dead)}") + print(f"\nCRITICAL dead functions: {len(critical_dead)}") + if critical_dead: + print("\n--- CRITICAL: Functions that MUST be called but aren't ---") + for d in critical_dead: print(f" {d['name']} (in {d['file']}:{d.get('line','?')})") + results = {"py_defs": len(py_defs), "py_dead": len(py_dead), "ts_defs": len(ts_defs), "ts_dead": len(ts_dead), "critical_dead": critical_dead} + with open(ROOT / "scripts" / "test_suite" / "results_trace_functions.json", "w") as f: + json.dump(results, f, indent=2, default=str) + return len(critical_dead) + +if __name__ == "__main__": sys.exit(main()) diff --git a/scripts/test_suite/trace_hooks.py b/scripts/test_suite/trace_hooks.py new file mode 100644 index 0000000..c8f3400 --- /dev/null +++ b/scripts/test_suite/trace_hooks.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Trace Hooks — Find orphan hooks (registered but never triggered) and orphan triggers (triggered but never received).""" +from __future__ import annotations +import re, sys, json +from pathlib import Path +from collections import defaultdict + +ROOT = Path(__file__).resolve().parent.parent.parent +APP = ROOT / "app" + +def find_hook_registrations() -> list[dict]: + regs = [] + for py in APP.rglob("*.py"): + try: content = py.read_text() + except: continue + rel = str(py.relative_to(ROOT)) + for m in re.finditer(r'register_(?:action|filter)s?\s*\(\s*["\']([^"\']+)['"]', content): + regs.append({"file": rel, "hook_type": "action_or_filter", "hook_name": m.group(1)}) + for m in re.finditer(r'register_actions_by_owner\s*\(\s*["\']([^"\']+)['"]', content): + regs.append({"file": rel, "hook_type": "action_group", "hook_name": m.group(1)}) + return regs + +def find_hook_triggers() -> list[dict]: + triggers = [] + for py in APP.rglob("*.py"): + try: content = py.read_text() + except: continue + rel = str(py.relative_to(ROOT)) + for m in re.finditer(r'do_action\s*\(\s*["\']([^"\']+)['"]', content): + triggers.append({"file": rel, "hook_name": m.group(1), "type": "action"}) + for m in re.finditer(r'apply_filters?\s*\(\s*["\']([^"\']+)['"]', content): + triggers.append({"file": rel, "hook_name": m.group(1), "type": "filter"}) + for m in re.finditer(r'trigger\s*\(\s*["\']([^"\']+)['"]', content): + triggers.append({"file": rel, "hook_name": m.group(1), "type": "trigger"}) + return triggers + +def main(): + print("=" * 70) + print("TRACE HOOKS — Orphan Hooks & Triggers") + print("=" * 70) + regs = find_hook_registrations() + triggers = find_hook_triggers() + reg_names = {r["hook_name"] for r in regs} + trig_names = {t["hook_name"] for t in triggers} + orphans_reg = [r for r in regs if r["hook_name"] not in trig_names] + orphans_trig = [t for t in triggers if t["hook_name"] not in reg_names] + print(f"\nHook registrations: {len(regs)}") + print(f"Hook triggers: {len(triggers)}") + print(f"\nISSUES: {len(orphans_reg) + len(orphans_trig)}") + print(f" Registered but never triggered: {len(orphans_reg)}") + print(f" Triggered but never received: {len(orphans_trig)}") + if orphans_reg: + print("\n--- Registered but never triggered ---") + for o in orphans_reg[:20]: print(f" {o['hook_name']} (in {o['file']})") + if orphans_trig: + print("\n--- Triggered but never received ---") + for o in orphans_trig[:20]: print(f" {o['hook_name']} (in {o['file']})") + results = {"registrations": regs, "triggers": triggers, "orphan_registrations": orphans_reg, "orphan_triggers": orphans_trig} + with open(ROOT / "scripts" / "test_suite" / "results_trace_hooks.json", "w") as f: + json.dump(results, f, indent=2, default=str) + return len(orphans_reg) + len(orphans_trig) + +if __name__ == "__main__": sys.exit(main()) diff --git a/scripts/test_suite/trace_imports.py b/scripts/test_suite/trace_imports.py new file mode 100644 index 0000000..caae1d3 --- /dev/null +++ b/scripts/test_suite/trace_imports.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Trace Imports — Find broken/missing imports in backend.""" +from __future__ import annotations +import ast, sys, json +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent.parent +APP = ROOT / "app" + +def find_imports() -> list[dict]: + imports = [] + for py in APP.rglob("*.py"): + try: content = py.read_text(); tree = ast.parse(content) + except: continue + rel = str(py.relative_to(ROOT)) + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom): + if node.module and node.module.startswith("app."): + for alias in node.names: + imports.append({"file": rel, "module": node.module, "name": alias.name, "line": node.lineno}) + return imports + +def check_imports_exist(imports: list[dict]) -> list[dict]: + broken = [] + for imp in imports: + module_path = imp["module"].replace(".", "/") + ".py" + full_path = ROOT / module_path + if not full_path.exists(): + # Try as package + pkg_path = ROOT / module_path.replace(".py", "/__init__.py") + if not pkg_path.exists(): + broken.append({**imp, "issue": "Module not found"}) + continue + # Check if name exists in module + try: + content = full_path.read_text() + if imp["name"] not in content and imp["name"] != "__init__": + # Could be imported via __init__.py + if not full_path.name == "__init__.py": + broken.append({**imp, "issue": f"Name '{imp['name']}' not found in module"}) + except: pass + return broken + +def main(): + print("=" * 70) + print("TRACE IMPORTS — Broken/Missing Imports") + print("=" * 70) + imports = find_imports() + broken = check_imports_exist(imports) + print(f"\nTotal imports: {len(imports)}") + print(f"Broken imports: {len(broken)}") + if broken: + print("\n--- Broken imports ---") + for b in broken[:30]: print(f" {b['file']}:{b['line']} — {b['module']}.{b['name']} — {b['issue']}") + results = {"total_imports": len(imports), "broken": broken} + with open(ROOT / "scripts" / "test_suite" / "results_trace_imports.json", "w") as f: + json.dump(results, f, indent=2, default=str) + return len(broken) + +if __name__ == "__main__": sys.exit(main()) diff --git a/scripts/test_suite/trace_plugins.py b/scripts/test_suite/trace_plugins.py new file mode 100644 index 0000000..2220436 --- /dev/null +++ b/scripts/test_suite/trace_plugins.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Trace Plugins — Find plugin→manifest→frontend verkabelungsfehler.""" +from __future__ import annotations +import ast, re, sys, json +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent.parent +APP = ROOT / "app" +FRONTEND = ROOT / "frontend" / "src" + +def find_plugin_manifests() -> list[dict]: + plugins = [] + builtins = APP / "plugins" / "builtins" + if not builtins.exists(): return plugins + for d in builtins.iterdir(): + if not d.is_dir() or d.name.startswith("_"): continue + manifest_file = d / "manifest.py" + plugin_info = {"name": d.name, "path": str(d.relative_to(ROOT)), "has_manifest": manifest_file.exists()} + if manifest_file.exists(): + try: content = manifest_file.read_text() + except: continue + for m in re.finditer(r'menu_items\s*[=:]\s*\[', content): plugin_info["has_menu_items"] = True + for m in re.finditer(r'page_routes\s*[=:]\s*\[', content): plugin_info["has_page_routes"] = True + for m in re.finditer(r'settings_pages\s*[=:]\s*\[', content): plugin_info["has_settings_pages"] = True + for m in re.finditer(r'detail_tabs\s*[=:]\s*\[', content): plugin_info["has_detail_tabs"] = True + plugins.append(plugin_info) + return plugins + +def find_frontend_plugin_refs() -> dict[str, list[str]]: + refs = {} + for ts in FRONTEND.rglob("*.ts"): + try: content = ts.read_text() + except: continue + for m in re.finditer(r'@/plugins/([\w-]+)', content): + refs.setdefault(m.group(1), []).append(str(ts.relative_to(ROOT))) + return refs + +def find_plugin_routes() -> list[dict]: + routes = [] + builtins = APP / "plugins" / "builtins" + if not builtins.exists(): return routes + for py in builtins.rglob("*.py"): + try: content = py.read_text() + except: continue + rel = str(py.relative_to(ROOT)) + for m in re.finditer(r'@router\.(get|post|put|patch|delete)\s*\(\s*["\']([^"\']+)["\']', content): + routes.append({"method": m.group(1).upper(), "path": m.group(2), "file": rel}) + return routes + +def main(): + print("=" * 70) + print("TRACE PLUGINS — Plugin→Manifest→Frontend Verkabelungsfehler") + print("=" * 70) + plugins = find_plugin_manifests() + frontend_refs = find_frontend_plugin_refs() + plugin_routes = find_plugin_routes() + issues = [] + for p in plugins: + if not p["has_manifest"]: + issues.append({"plugin": p["name"], "issue": "No manifest.py", "severity": "HIGH"}) + if p.get("has_menu_items") and p["name"] not in frontend_refs: + issues.append({"plugin": p["name"], "issue": "Has menu_items but no frontend reference", "severity": "MEDIUM"}) + print(f"\nPlugins found: {len(plugins)}") + print(f"Plugin routes: {len(plugin_routes)}") + print(f"Frontend plugin refs: {len(frontend_refs)}") + print(f"\nISSUES: {len(issues)}") + if issues: + print("\n--- Plugin issues ---") + for i in issues: print(f" [{i['severity']}] {i['plugin']}: {i['issue']}") + results = {"plugins": plugins, "frontend_refs": frontend_refs, "plugin_routes": plugin_routes, "issues": issues} + with open(ROOT / "scripts" / "test_suite" / "results_trace_plugins.json", "w") as f: + json.dump(results, f, indent=2, default=str) + return len(issues) + +if __name__ == "__main__": sys.exit(main()) diff --git a/scripts/test_suite/trace_stores.py b/scripts/test_suite/trace_stores.py new file mode 100644 index 0000000..20c1d69 --- /dev/null +++ b/scripts/test_suite/trace_stores.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +"""Trace Stores — Find unused store actions/state in frontend.""" +from __future__ import annotations +import re, sys, json +from pathlib import Path +from collections import defaultdict + +ROOT = Path(__file__).resolve().parent.parent.parent +FRONTEND = ROOT / "frontend" / "src" + +def find_store_actions() -> dict[str, list[str]]: + stores = {} + store_dir = FRONTEND / "store" + if not store_dir.exists(): return stores + for ts in store_dir.glob("*.ts"): + try: content = ts.read_text() + except: continue + store_name = ts.stem + actions = [] + # Find action names in zustand store definitions + for m in re.finditer(r'(\w+)\s*:', content): + name = m.group(1) + if name not in ('state', 'set', 'get', 'name', 'storage', 'partialize') and len(name) > 3: + actions.append(name) + stores[store_name] = list(set(actions)) + return stores + +def find_store_usage() -> dict[str, set[str]]: + usage = defaultdict(set) + for ts in FRONTEND.rglob("*.ts"): + try: content = ts.read_text() + except: continue + for store_name in ['authStore', 'uiStore', 'themeStore', 'workspaceStore', 'pluginStore', 'commStore', 'calendarStore', 'commandPaletteStore', 'onboardingStore', 'windowStore', 'aiUIControlStore', 'pluginToolbarStore']: + if store_name in content: + # Find which actions/state are accessed + for m in re.finditer(rf'{store_name}\.*(\w+)', content): + usage[store_name].add(m.group(1)) + return usage + +def main(): + print("=" * 70) + print("TRACE STORES — Unused Store Actions/State") + print("=" * 70) + stores = find_store_actions() + usage = find_store_usage() + unused = {} + total_unused = 0 + for store_name, actions in stores.items(): + used = usage.get(store_name, set()) + unused_actions = [a for a in actions if a not in used] + if unused_actions: + unused[store_name] = unused_actions + total_unused += len(unused_actions) + print(f"\nStores found: {len(stores)}") + for name, actions in stores.items(): + used = len(usage.get(name, set())) + print(f" {name}: {len(actions)} defined, {used} used, {len(unused.get(name, []))} unused") + print(f"\nTotal unused store actions: {total_unused}") + if unused: + print("\n--- Unused store actions ---") + for store, actions in unused.items(): + for a in actions[:10]: print(f" {store}.{a}") + results = {"stores": stores, "usage": {k: list(v) for k, v in usage.items()}, "unused": unused} + with open(ROOT / "scripts" / "test_suite" / "results_trace_stores.json", "w") as f: + json.dump(results, f, indent=2, default=str) + return total_unused + +if __name__ == "__main__": sys.exit(main())