Phase 12: LLM provider config + client + CSP

- Bring-your-own-key assistant config in a dedicated persisted store
  (src/stores/assistantStore.ts); partialize drops the key from localStorage
  when 'remember' is off. Key lives in localStorage only, so it's auto-excluded
  from Dexie backups.
- Provider-agnostic LLM client (src/lib/llm) over fetch, no SDK: Anthropic
  Messages + OpenAI-compatible Chat Completions (covers OpenAI/OpenRouter/
  Ollama/LM Studio via custom base URL). Structured JSON output parsed +
  Zod-validated; timeouts; typed error kinds; key never leaked in errors.
- Settings 'Assistant (AI)' section with provider/model/baseUrl/key, remember
  + enabled toggles, and Test connection.
- CSP connect-src widened to any https + localhost for the BYO endpoint.

10 client unit tests; settings e2e asserts config persists and the key is
never present in an exported backup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 09:04:20 +02:00
parent 0930136c46
commit 7fd5fcd9af
9 changed files with 523 additions and 2 deletions
+5 -1
View File
@@ -6,9 +6,13 @@ import { fileURLToPath, URL } from 'node:url';
// Strict CSP injected at build time only. In dev, Vite's react-refresh needs
// inline scripts, so we leave the placeholder empty there.
// connect-src allows any HTTPS endpoint plus localhost so the bring-your-own-key
// assistant can reach cloud providers (Anthropic/OpenAI/OpenRouter/…) and local
// models (Ollama/LM Studio). Everything else stays locked to 'self'.
const PROD_CSP =
"default-src 'self'; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; " +
"script-src 'self'; connect-src 'self'; font-src 'self' data:; worker-src 'self' blob:; " +
"script-src 'self'; connect-src 'self' https: http://localhost:* http://127.0.0.1:*; " +
"font-src 'self' data:; worker-src 'self' blob:; " +
"manifest-src 'self'; object-src 'none'; base-uri 'self'";
function cspPlugin() {