Commit graph

417 commits

Author SHA1 Message Date
8436f75d87 synops-ai prompt: direkte LLM-kall via LiteLLM (oppgave 28.1)
Legger til `synops-ai prompt`-subkommando for enkel prompt-inn/tekst-ut
bruk av LLM via LiteLLM gateway. Eksisterende script-generering
flyttes til `synops-ai script`-subkommando.

Prompt-modus:
- --prompt (påkrevd): bruker-prompt
- --model (valgfri): modellalias, slås opp fra ai_job_routing hvis utelatt
- --system (valgfri): systemprompt
- --job-type: for modelloppslag og logging (default: simple_prompt)
- --temperature: LLM-temperatur (default: 0.7)
- Logger tokenbruk i ai_usage_log

Seed: simple_prompt → sidelinja/rutine i ai_job_routing.
2026-03-18 19:51:44 +00:00
b89f307fa2 Starter oppgave 28.1 2026-03-18 19:45:28 +00:00
8f7d2d7fe8 MindMap-trait for samlingsnoder (oppgave 27.3)
Gjør mindmap til et offisielt trait som kan velges ved opprettelse
av samlingsnoder. Frontend-komponenten (27.1) og BlockShell-panelet
(27.2) var allerede på plass — dette kobler traiten inn i backend-
validering og pakke-definisjoner.

Endringer:
- Lagt til "mindmap" i VALID_TRAITS (intentions.rs)
- Validering av konfig: default_depth 1-3, layout radial/tree
- Inkludert mindmap i Podcaststudio- og Wiki-pakker (traits.ts)
- Oppdatert trait-katalog i docs/primitiver/traits.md
2026-03-18 19:44:30 +00:00
49d8599056 Starter oppgave 27.3 2026-03-18 19:40:27 +00:00
6c5cdf817d MindMap BlockShell-panel i arbeidsflaten (oppgave 27.2)
Legger til MindMap-støtte i workspace-siden slik at tankekartet
kan brukes som et fritt plassert BlockShell-panel med fullskjerm,
resize og drag-handle — på linje med alle andre trait-paneler.

Samlingssiden hadde allerede full MindMap-integrasjon fra 27.1.
Workspace-siden manglet import, knownTraits-oppføring og
rendering i både mobil- og desktop-modus.
2026-03-18 19:38:49 +00:00
1144933ac0 Starter oppgave 27.2 2026-03-18 19:35:27 +00:00
e6b55543b5 MindMap Svelte-komponent med radial/tree-layout (oppgave 27.1)
Ny MindMapTrait-komponent som viser noder og edges fra WebSocket-stores
i radial eller hierarkisk tree-layout via d3-hierarchy.

- Bygger trestruktur fra rotnode med konfigurerbar dybde (1-3 hopp)
- Radial layout (trigonometrisk) og tree layout (hierarkisk) med toggle
- Pan/zoom via d3-zoom, auto-fit ved oppstart
- Klikk node = ny rot (med tilbake-historikk), dobbeltklikk = åpne
- Edge-type-filtrering og visuell stil (farge, stiplet linje)
- Privacy-markering på private noder
- Responsivt: forenklet toolbar på mobil
- Registrert som 'mindmap' trait i katalog og workspace-typer
- Koblet inn i collection-side (desktop BlockShell + mobil tabs)
2026-03-18 19:32:17 +00:00
461c3bfb79 Starter oppgave 27.1 2026-03-18 19:26:14 +00:00
752548d01b Gjør fase 27-30 uavhengige av epost-fasen (26)
26.2 er blokkert av SMTP-credentials. Fase 27 (tankekart),
28 (CLI+AI-ruting), 29 (universell input) og 30 (podcast)
avhenger nå av fase 25, ikke 26. Task-runneren kan fortsette.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 19:25:56 +00:00
b096434ff6 synops-mail CLI + msmtp-oppsett (oppgave 26.2, venter SMTP-credentials)
Implementerer synops-mail --send --to <epost> --subject <emne> CLI-verktøy
for utgående epost via msmtp. Alt er ferdig og testet strukturelt:

- tools/synops-mail: Rust CLI som bygger RFC 5322-melding og sender via msmtp
- /srv/synops/config/msmtp/msmtprc: msmtp-konfig mot Hetzner-relay (587/STARTTLS)
- Installert til /usr/local/bin/synops-mail

Blokkert av: mangler SMTP-relay brukernavn/passord. Hetzner-relay
(mail.your-server.de) krever autentisering, port 25 er blokkert utgående.
Trenger credentials i msmtprc for å fullføre.
2026-03-18 19:12:14 +00:00
abf8b526c3 Starter oppgave 26.2 2026-03-18 19:05:24 +00:00
25fc1a1b59 Username i auth_identities: Authentik-synk ved login (oppgave 26.1)
Legger til username-kolonne i auth_identities med UNIQUE constraint.
Ved innlogging sender SvelteKit preferred_username fra Authentik til
maskinrommet POST /auth/sync, som oppdaterer kolonnen. Grunnlaget
for epost-ruting i fase 26: vegard@synops.no → username-oppslag.
2026-03-18 19:04:57 +00:00
6b81569581 Starter oppgave 26.1 2026-03-18 19:00:29 +00:00
8af4265b6e synops-clip orkestrering-støtte: cli_tool-registrering + clip_url jobb/API (oppgave 25.4)
Gjør synops-clip tilgjengelig i orkestreringer ved å:

1. Registrere synops-clip som cli_tool-node (migration 026) med norske
   aliases (clip, klipp, hent artikkel) og args_hints for script-kompilatoren.
   Orkestreringer kan nå skrive "1. clip fra event (lagre node, bruker)"
   som kompileres til "synops-clip --url {event.url} --write --created-by ...".

2. Legge til clip_url som jobbtype i jobbkøen (clip.rs) — spawner
   synops-clip med riktige env-variabler (DATABASE_URL, AI_GATEWAY_URL, etc).

3. Legge til POST /intentions/clip_url API-endepunkt slik at frontend
   og andre klienter kan trigge URL-klipping direkte.

4. Utvide trigger-konteksten med event.url og event.created_by slik at
   orkestreringer som reagerer på URL-deling kan videresende URL til
   synops-clip via variabel-substitusjon.
2026-03-18 18:55:11 +00:00
8f02dbabc4 Starter oppgave 25.4 2026-03-18 18:45:27 +00:00
f22465d72b @bot URL-klipping i chat: synops-clip-integrasjon (oppgave 25.3)
Når en bruker limer inn en URL i chatten, gjenkjenner synops-respond
URL-en automatisk, kaller synops-clip --write for å hente, parse og
oppsummere artikkelen, og inkluderer resultatet i prompten slik at
Claude kan presentere oppsummeringen naturlig.

Ved betalingsmur: Claude informerer brukeren og ber om innlimt innhold.
Maks 3 URL-er per melding, 60s timeout per klipp.

Endringer:
- synops-respond: URL-deteksjon (regex), synops-clip-kall, prompt-kontekst
- maskinrommet/agent.rs: videresend env-variabler for synops-clip
- maskinrommet-env.sh: SYNOPS_CLIP_SCRIPTS env-variabel
- docs/infra/claude_agent.md: dokumentert URL-klipping-flyten
2026-03-18 18:41:33 +00:00
16ee209283 Starter oppgave 25.3 2026-03-18 18:35:25 +00:00
c0fecf2f4e synops-clip --write: node-opprettelse med AI-oppsummering (oppgave 25.2)
Legger til --write-modus som oppretter:
- content-node med artikkelinnhold og metadata.source_url
- tagged-edge "clipped" (self-tag)
- AI-oppsummering via LiteLLM (integrert i node-innhold)
- mentions-edges til gjenkjente entiteter i kunnskapsgrafen

AI-analysen er robust: feiler den, opprettes noden uten oppsummering.
Gjenbruker eksisterende topic-noder der navnene matcher (case-insensitive).
Nye entiteter opprettes som topic-noder med entity_type i metadata.
Ressursforbruk logges til resource_usage_log.

Nye CLI-flagg: --write, --created-by <uuid>
Payload-JSON utvides med write + created_by for jobbkø-integrasjon.
2026-03-18 18:33:07 +00:00
0ebe3b0421 Starter oppgave 25.2 2026-03-18 18:25:35 +00:00
5dead5dac9 synops-clip: CLI for web article extraction (oppgave 25.1)
Nytt verktøy som henter og parser webartikler til ren tekst + metadata.
Bruker Mozilla Readability (via Node.js) for artikkelekstraksjon, med
Playwright som fallback for JS-rendrede sider.

Arkitektur:
- Rust CLI (clap, reqwest) håndterer HTTP-henting, paywall-deteksjon, JSON-output
- Node.js-hjelpeskript (readability.mjs) bruker @mozilla/readability + jsdom
- Playwright-script (playwright.mjs) for headless browser-fallback
- Støtter --payload-json for maskinrommet/jobbkø-integrasjon

Paywall-deteksjon basert på:
- Kort innhold (<200 tegn)
- Norske/engelske paywall-fraser i innholdet
- CSS-klasser/HTML-attributter (piano, schibsted, amedia, etc.)
- Schema.org isAccessibleForFree meta-tagg

Output: JSON med title, author, date, content, url, paywall, excerpt, source
2026-03-18 18:24:22 +00:00
b447c8272f Starter oppgave 25.1 2026-03-18 18:15:26 +00:00
1f21f90f76 Seed-orkestreringer og flerords-verbstøtte (oppgave 24.9)
Fem standard-orkestreringer opprettet som seed-data:
- Podcast-pipeline (transkriber → oppsummer → RSS)
- Publiseringsflyt (render → indeks → RSS)
- AI-beriking (foreslå koblinger ved nytt innhold)
- Planlagt publisering (render ved tidspunkt)
- Podcast TTS (kaskade fra pipeline → les opp oppsummering)

Podcast-pipeline → TTS demonstrerer kaskade via triggers-edge.

Script-kompilatoren utvidet med flerords-verbstøtte: aliaser som
"generer feed", "les opp", "foreslå koblinger" matcher nå korrekt
selv om parseren splitter ved første mellomrom. Prøver verb + N
første ord av objekt opptil 3 ord.
2026-03-18 18:11:02 +00:00
88945b0d64 Starter oppgave 24.9 2026-03-18 18:00:33 +00:00
b94576cded Legg til fase 30: komplett podcast-hosting uten ekstern avhengighet
8 oppgaver: iTunes/Podcasting 2.0 RSS-tags, nedlastingsstatistikk
(IAB-kompatibel), embed-spiller, import fra eksisterende podcast
med prøveimport-flyt (importer → test → re-importer nye → 301),
og feed-redirect for å flytte bort.

Feature-spec: docs/features/podcast_hosting.md
Ingen castopod — podcasten er noder med riktige edges og en feed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:59:59 +00:00
44327df370 Kaskade: triggers-edge mellom orkestreringer (oppgave 24.8)
Etter vellykket pipeline-utførelse sjekker handle_orchestrate() om
orkestreringen har utgående triggers-edges til andre orchestration-noder.
Hvert gyldig mål enqueues som ny orchestrate-jobb med kaskade-kontekst.

Syklusdeteksjon via cascade_chain i jobb-payload:
- Sporer alle orchestration-IDer allerede utført i kjeden
- Blokkerer target som allerede finnes i kjeden (direkte + indirekte syklus)
- Dybdegrense på 10 ledd (MAX_CASCADE_DEPTH)
- Blokkerte kaskader logges i orchestration_log med status=skipped

Nedstrøms orkestreringer mottar:
- trigger_event: "cascade"
- upstream_orchestration_id i trigger_context
- {event.upstream_orchestration_id} tilgjengelig i script

Kaskade-feil er ikke-fatale — selve orkestreringen rapporteres som suksess.

8 nye enhetstester for syklusdeteksjon og dybdegrense.
2026-03-18 17:57:26 +00:00
d435f6ab33 Starter oppgave 24.8 2026-03-18 17:50:27 +00:00
a7e2bd6b60 Legg til fase 29: universell input — alle modaliteter blir noder
12 oppgaver: skjermklipp, RSS/feed-abonnement, webhook (universell
ekstern input med templates), video-opptak, geolokasjon, håndskrift/
tegning, kalender-import (ICS + CalDAV).

Feature-spec: docs/features/universell_input.md
Prinsipp: modaliteten er transport, noden er det som lever videre.
Alt fra screenshot til GitHub-webhook ender som noder i grafen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:48:40 +00:00
45916a6f1d Fullfør oppgave 24.7: oppdater tasks.md og tools/README.md
Markerer 24.7 som ferdig. Legger synops-ai i verktøy-tabellen.
2026-03-18 17:48:11 +00:00
91a73329a4 Starter oppgave 24.7 2026-03-18 17:35:28 +00:00
376bf7ee62 Orchestration UI: editor med tre visninger, sanntids kompilering, testkjøring (oppgave 24.6)
Backend:
- POST /intentions/compile_script — kompilerer script, returnerer diagnostikk + kompilert resultat
- POST /intentions/test_orchestration — trigger manuell testkjøring via jobbkø
- GET /query/orchestration_log — henter kjørehistorikk for en orkestrering
- "orchestration" lagt til som gyldig trait for samlingsnoder

Frontend:
- OrchestrationTrait.svelte — BlockShell-panel med:
  - Tre tabber: Enkel (editor), Teknisk (kompilert CLI), Kompilert (JSON)
  - Sanntids kompilering med 500ms debounce og diagnostikk-visning
  - Trigger-velger (6 event-typer) og executor-velger (script/bot/dream)
  - "Test kjøring"-knapp (lagrer + oppretter testjobb i køen)
  - Kjørehistorikk-panel med steg-status, varighet, feilmeldinger
  - Responsiv: container queries + mobile viewport fallback
- Registrert i collection-page (mobil + desktop), workspace/types.ts
- API-funksjoner: compileScript, testOrchestration, fetchOrchestrationLog
2026-03-18 17:30:52 +00:00
984f5e2f75 Starter oppgave 24.6 2026-03-18 17:20:26 +00:00
059c776bf4 Script-executor: vaktmesteren eksekverer kompilerte pipelines (oppgave 24.5)
Ny modul script_executor.rs som tar en kompilert pipeline fra
script_compiler og kjører stegene sekvensielt:

- Substituerer {event.*}-variabler fra trigger-kontekst
- Spawner hvert CLI-verktøy som subprosess via generisk dispatch
- VED_FEIL-håndtering: steg-fallback → global fallback → stopp
- Spesialhåndtering av work_item (oppretter oppgave-node i grafen)
- Logger hvert steg i ny orchestration_log-tabell

handle_orchestrate i jobs.rs utvides: kompilerer + utfører i
samme jobb (var tidligere kun kompilering).

Migration 023: orchestration_log-tabell med indekser for
effektiv spørring per orkestrering og per jobb.
2026-03-18 17:18:10 +00:00
e5ef7145ac Starter oppgave 24.5 2026-03-18 17:10:28 +00:00
47299435ab cli_tool alias-metadata: seed 14 verktøy med aliases og args_hints (oppgave 24.4)
Alle eksisterende CLI-verktøy har nå cli_tool-noder i PG med:
- aliases: norske verb for script-kompilatoren ("transkriber", "oppsummer", etc.)
- args_hints: mapping fra menneskelige argumenter til CLI-flagg
  ("stor modell" → "--model large", "lydfilen" → "--cas-hash {event.cas_hash}")

Muliggjør at script-kompilatoren (24.3) kan slå opp verktøy fra PG
og kompilere menneskelig scriptspråk til faktiske CLI-kall.
2026-03-18 17:09:53 +00:00
b328ebfb3a Starter oppgave 24.4 2026-03-18 17:05:32 +00:00
cc23e26802 Script-kompilator for orkestreringer (oppgave 24.3)
Parser menneskelig scriptspråk og kompilerer til tekniske CLI-kall.
"transkriber lydfilen (stor modell)" → "synops-transcribe --cas-hash {event.cas_hash} --model large"

Kompilatoren:
- Parser nummererte steg, ved_feil-fallbacks, og globale feilhåndterere
- Matcher verb mot cli_tool-noders aliases (case-insensitive)
- Mapper argumenter i parentes via args_hints
- Validerer variabelreferanser ({event.*}) mot kjent liste
- Fuzzy-matching med Levenshtein-avstand for forslag ved feil
- Rust-stil kompileringsrapport med ✓/✗ per linje

Integrert i jobbkøen: orchestrate-jobb kompilerer scriptet og
lagrer pipeline i metadata. Utførelse kommer i oppgave 24.5.

12 unit-tester dekker parser, kompilator, feilhåndtering og fuzzy-matching.
2026-03-18 17:03:47 +00:00
021ee46023 Starter oppgave 24.3 2026-03-18 16:55:28 +00:00
26f03ef21d Trigger-evaluering i portvokteren (oppgave 24.2)
Ved node/edge-events fra PG LISTEN/NOTIFY evaluerer portvokteren nå
om noen orchestration-noder matcher triggeren. Implementert som non-blocking
async task som ikke blokkerer WebSocket-flyten.

Ny modul orchestration_trigger.rs:
- Mapper NOTIFY-events til trigger-typer (node.created, edge.created)
- Effektiv lookup via funksjonell B-tree-indeks på metadata->trigger->event
- Evaluerer observes-edges (eksplisitt) vs conditions (implisitt)
- Betingelser: node_kind, edge_type, has_trait, has_tag (AND-logikk)
- Legger matchende orkestreringer i jobbkøen som "orchestrate"-jobb

Ny migration 021: indeks for trigger-event lookup på orchestration-noder.
Jobbkø-dispatcher håndterer "orchestrate" med placeholder (24.3 implementerer utførelse).

Verifisert: content-node trigrer matching orchestration, communication-node hoppes over.
2026-03-18 16:53:59 +00:00
b759808831 Orkestrering: menneskelig scriptspråk med kompilator til CLI-kall
To lag av samme script:
- Menneskelig: "transkriber lydfilen (stor modell)"
- Teknisk: "synops-transcribe --cas-hash {event.cas_hash} --model large"

Kompilator matcher verb mot cli_tool-aliases, argumenter mot
args_hints, variabler fra trigger-kontekst. Rust-stil feilmeldinger.
Tre visninger i editoren (Enkel/Teknisk/Kompilert).

Oppgaver restrukturert: kompilator, alias-metadata, executor,
UI med tre visninger, AI-assistert oppretting, kaskade, seed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:52:21 +00:00
42a45cd613 Starter oppgave 24.2 2026-03-18 16:46:14 +00:00
6dabdcf8ec Legg til orchestration-validering i maskinrommet (oppgave 24.1)
Ny node_kind 'orchestration' med strukturert metadata-validering:
- trigger.event valideres mot kjent liste (node.created, edge.created,
  communication.ended, node.published, scheduled.due, manual)
- trigger.conditions må være objekt hvis satt
- executor valideres mot script/bot/dream
- intelligence og effort valideres som heltall 1-3
- compiled valideres som boolean
- pipeline valideres som array

Valideringen kjører i både create_node og update_node, identisk
mønster som validate_collection_traits og validate_ai_preset_metadata.

Ref: docs/concepts/orkestrering.md
2026-03-18 16:43:11 +00:00
6543c1fedd Starter oppgave 24.1 2026-03-18 16:35:24 +00:00
e8a1a80652 Valider fase 22: STDB-migrering fullført, ingen rester i aktiv kode
Validering av fase 22 (SpacetimeDB-migrering) bekrefter:

1. WebSocket-sanntid fungerer:
   - maskinrommet lytter på PG NOTIFY-kanaler (node_changed, edge_changed,
     access_changed, mixer_channel_changed)
   - Enrichment av events med fulle rader fra PG
   - Broadcast via tokio::broadcast til WebSocket-klienter
   - Tilgangskontroll filtrerer events per bruker
   - Frontend kobler til /ws med JWT, mottar initial_sync + inkrementelle events

2. PG LISTEN/NOTIFY-triggere verifisert i database:
   - 4 notify-funksjoner: notify_node_change, notify_edge_change,
     notify_access_change, notify_mixer_channel_change
   - 4 triggere: nodes_notify, edges_notify, node_access_notify,
     mixer_channels_notify

3. Ingen STDB-rester i aktiv kode/konfig:
   - maskinrommet/src/: rent
   - Cargo.toml: ingen spacetimedb-avhengigheter
   - docker-compose.yml: ingen spacetimedb-tjeneste
   - Caddyfile: ingen spacetimedb-proxy
   - Eneste funn: frontend/src/lib/spacetime/ katalognavn —
     omdøpt til frontend/src/lib/realtime/ (32 filer oppdatert)
   - Historiske referanser i docs/arkiv og scripts/synops.md er OK
2026-03-18 16:31:16 +00:00
0dc014f4ab Legg til fase 28: synops-ai med admin-rutingskontroll + manglende CLI
7 oppgaver:
- synops-ai: lettvekts LLM-kall via LiteLLM (ikke claude -p)
- AI-rutingskontroll i admin: modell per kontekst, endres uten redeploy
- Kostnadstak per bruker/samling med budsjett-sjekk
- synops-notify, synops-validate, synops-backup, synops-health

Nøkkelprinsipp: admin styrer hvilken modell som brukes til hva.
synops-ai uten --model bruker ai_job_routing-tabellen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:27:04 +00:00
7a7eba5264 Starter oppgave 23.11 2026-03-18 16:25:24 +00:00
f4cb834ad4 Valider fase 21: alle 15 CLI-verktøy bestått
Validering:
- Alle 15 synops-* verktøy kompilerer uten feil
- --help fungerer for alle verktøy med norske beskrivelser
- Feilhåndtering: exit(1) + stderr for alle feiltilfeller
- JSON-output validert (prune, render, node --format json)
- Markdown-output validert (search, context, node --format md)
- XML-output validert (rss)
- synops-common brukes av 14/15 verktøy (db, cas, logging)
- synops-tasks bruker ikke synops-common (som dokumentert)

Funn fikset:
- Installerte manglende binaries i /usr/local/bin: synops-tts,
  synops-audio, synops-render (maskinrommet dispatcher fant dem ikke)

Notat: --payload-json (generisk dispatch) er spesifisert i
docs/retninger/unix_filosofi.md men ikke implementert — verktøyene
bruker individuelle CLI-args, og maskinrommet mapper payload→args
i dedikerte dispatcher-moduler. Fungerer korrekt som det er.
2026-03-18 16:21:33 +00:00
5b3cc1c400 Legg til fase 27: tankekart — radial grafvisning
3 oppgaver: MindMap Svelte-komponent med radial layout,
BlockShell-panel, og mindmap-trait for samlinger.
Feature-spec i docs/features/tankekart.md.

Ren frontend-visning av eksisterende grafdata — ingen ny backend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:19:34 +00:00
74ad7d7011 Legg til fase 26: epost — send/motta via synops.no
7 oppgaver: username i auth_identities, msmtp utgående,
MX-records, Postfix mottak, synops-mail CLI med "Kjære
vaktmester"-filter, domene-alias (alle domener → samme bruker),
utgående varsler.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:15:26 +00:00
0d1c79cd85 Starter oppgave 23.10 2026-03-18 16:10:53 +00:00
15dd23b873 Valider fase 19–20: arbeidsflaten + universell overføring bestått
Fikser funnet under validering:
- Gjør collection-prop valgfri i alle trait-komponenter slik at de
  fungerer i personlig arbeidsflate uten collection-kontekst
- Legger til null-guards for collection.id i alle derived-blokker
  og funksjoner som oppretter edges
- Fjerner microsSinceUnixEpoch-rester fra STDB-migrasjonen —
  createdAt er nå et tall (Unix µs), ikke et objekt
- Retter saveTimeout-lekkasje i collection-sida: timer ryddes nå
  ved navigasjon mellom samlinger
- Fikser TypeScript-feil i editorial (number vs string, uoppnåelig
  'scheduled'-sammenligning), studio (bigint vs number),
  RecordingTrait ($state-generics)
- Typefeil redusert fra 55 → 4 (gjenværende er pre-eksisterende
  i mixer.ts/livekit.ts, ikke fase 19-20)

Validert: Canvas pan/zoom, BlockShell, layout-persistering,
snarveier, transfer service, alle panelreworks. Frontend bygger OK.
2026-03-18 16:03:17 +00:00