147 Commits

Author SHA1 Message Date
itsrubberduck
b6a492bb49 hotkey support 2026-06-20 03:17:53 +02:00
itsrubberduck
2d1b9eab75 feat(bridge): map GPS position + true heading into telemetry
Bridge now sends latitude_deg/longitude_deg/heading_deg; map them to
PLANE_LATITUDE/PLANE_LONGITUDE/PLANE_HEADING_DEGREES_TRUE so position and
course land in the telemetry store and surface in /api/bridge/live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 13:36:30 +02:00
itsrubberduck
4a145844d9 feat(pm): auto-tune radio from SimBridge telemetry via ?token
When /pm is opened with ?token=<bridge-token>, poll /api/bridge/live for
fresh telemetry. While the bridge keeps posting, mirror the sim's COM1
active frequency into the radio (only on actual sim change, so manual/flow
tuning isn't clobbered) and show a "Bridge connected" badge in the HUD.

Telemetry now carries COM_ACTIVE_FREQUENCY/COM_STANDBY_FREQUENCY from the
bridge's com_active_frequency/com_standby_frequency fields.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:30:23 +02:00
itsrubberduck
ccab0e7116 feat(pm): add bug-report button with screenshot annotation and admin panel
- New BugReport MongoDB model (comment, contact, userId, screenshot, pmState, status)
- POST /api/bug-reports — authenticated submit; emails emanuel@faktorxmensch.com on receipt
- GET/PATCH /api/admin/bug-reports + /[id] — admin list, detail with screenshot, status toggle
- /pm: "Bug" button in HUD captures viewport screenshot (html2canvas), shows annotation
  canvas where testers can draw arrows; submits comment + contact + state snapshot
- /admin: new "Bug Reports" tab with open-count badge, screenshot expand, "Erledigt" toggle,
  and "In /pm öffnen" link that restores captured engine state via ?restoreBugReport=<id>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:05:25 +02:00
leubeem
592ec5912c feat(stt): seed Whisper prompt with expected readback + per-field debug UI
Whisper prompt seeding (per request):
- ptt.post.ts builds the prompt as generic ICAO bias + this state's expected
  readback appended LAST (survives the 224-token truncation), in both raw token
  form and spoken ICAO form via new radioSpeech.speakToken().
- pm.vue passes the expected phrase + active variable values; classroom.vue
  passes the lesson's expected field values.

Per-field readback debug:
- sttMatch.matchTranscriptionToFields returns fields[] (matched/missing + which
  view matched) plus normalized/denormalized transcription views.
- useRadioBackend types readback_report on the transmit response.
- pm.vue renders a "Readback check" panel in the right log rail; classroom.vue
  renders per-field rows under the STT panel.

Radio-pronunciation fixes (radioSpeech.ts):
- callsign expander handles multi-letter suffixes (DLH6RK -> Lufthansa six Romeo
  Kilo).
- toRadioSpeech now expands airports (EDDC -> Echo Delta Delta Charlie).
- bare altitudes >=1000 in a clearance context are spoken ("climb initially
  5000" -> "climb initially five thousand feet"); speeds/headings untouched.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 14:12:54 +02:00
leubeem
0154c6d624 fix(security): mandatory cron secret + reject placeholder JWT secrets (SEC-07, OPS-02, SEC-09)
SEC-07 — committed secrets:
- Replace real-looking defaults in .env.example (JWT_SECRET/JWT_REFRESH_SECRET
  "changeme", MANUAL_INVITE_PASSWORD "pm.local@zghl.de") with CHANGE_ME
  placeholders, and drop the personal DOME_LIGHT_WEBHOOK_URL default.
- Add a Nitro startup plugin (server/plugins/validate-secrets.ts) that refuses
  to boot in production when JWT_SECRET is unset, looks like a placeholder, or
  is shorter than 32 chars (warns only in development).

OPS-02 / SEC-09 — cron endpoints:
- requireCronSecret now fails closed: when no CRON_SECRET/KPI_CRON_SECRET is
  configured the endpoint returns 503 instead of being publicly callable
  (previously it allowed the request with a warning). Both cron routes already
  call the guard. Prefer the x-cron-secret header over the loggable ?secret=
  query param; document CRON_SECRET in .env.example.

Operational note: production deployments must now set JWT_SECRET (>=32 chars)
and CRON_SECRET, or the server won't start / crons return 503.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 11:07:27 +02:00
leubeem
58f9bfad4c ci: make typecheck a real blocking gate; bump actions to Node 24 majors
The previous `vue-tsc --noEmit` step was a no-op: the root tsconfig uses
`files: []` with project references, so without `--build` it checks zero files
and always passes. Switch to `vue-tsc --build` (new `yarn typecheck` script)
and make the job blocking.

Fix the one error this surfaced: UsageEventDocument extended mongoose.Document,
whose `model` method collides with the `model: string` field. Use the
recommended pattern — a plain attrs interface passed to the Schema/Model
generics (hydrated docs still expose Document methods). Typecheck is now clean.

Bump actions/checkout@v5 and actions/setup-node@v5 to silence the Node.js 20
runtime deprecation (forced to Node 24 from 2026-06-16).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:28:27 +02:00
leubeem
72a5bef375 feat(atc): bias Whisper STT toward aviation phraseology
Replace the generic one-line Whisper prompt with a vocabulary-rich bias prompt
(phonetic alphabet, aviation number words, common phraseology, and the airline
telephony names pulled live from DEFAULT_AIRLINE_TELEPHONY) and set
temperature: 0. Aviation R/T is a constrained domain, so seeding the expected
vocabulary improves transcription of callsigns, runways, and readbacks — the
upstream input to the decision engine. Stays within the ~224-token prompt cap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:47:22 +02:00
leubeem
f8fdd8bc79 feat(server): per-user AI usage tracking, cost alerting, and endpoint hardening
Usage tracking:
- new UsageEvent collection records every STT/TTS/LLM call per user with
  provider, model, volume (audio seconds, characters, tokens) and an
  estimated USD cost; self-hosted providers (Speaches/Piper) and cache
  hits record at $0
- pricing table for whisper-1, tts-1, gpt-5-nano & co. in server/utils/usage.ts
- weekly KPI mail gains an "AI-Nutzung & Kosten" section: weekly and
  rolling 30-day cost, per-kind breakdown, top 5 users by cost
- quota alert mail when rolling 30-day cost exceeds USAGE_ALERT_USD
  (default $5), at most once per calendar month (UsageAlertDelivery)

Hardening:
- /api/atc/say now requires an authenticated session (middleware
  exemption removed); useFlightLabAudio sends the bearer token
- /api/service/tools/latency requires auth (was a public LLM endpoint)
- per-user rate limits: PTT 20/min, say 60/min, latency 5/min
- cron endpoints (waitlist-drip, weekly-kpi-report) require a shared
  secret via ?secret= or x-cron-secret (CRON_SECRET, falls back to
  KPI_CRON_SECRET); allowed with a warning while unset so existing
  deployments keep working
- PTT records the actual transcribed audio duration for billing accuracy

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:17:03 +02:00
leubeem
c48f5fe40e feat(atc): accept preNormalized flag on /api/atc/say
Clients that already ran the full radiotelephony normalizer (see
normalizeATCText) pass preNormalized: true so the server skips
normalizeATC — double-normalizing corrupts the text, e.g. expandAirports
spells the city name "MAIN" letter-by-letter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:12:53 +02:00
itsrubberduck
f894eb4928 feat(atis): inline METAR expansion, airport name lookup, acronym fix
ATIS text often arrives in raw METAR form (e.g.
"METAR EDDF 281050Z AUTO 02008KT 320V070 CAVOK 24/02 Q1025 NOSIG"),
which TTS reads as letter-by-letter spelling. The normalizer now expands
the full WMO Code Form FM 15-XV vocabulary inline: DDHHMMZ date stamps,
compressed wind (with gusts, VRB, calm), wind variability ranges, RVR
(R25L/1500N), wind shear, slash-form temp/dewpoint, Q/A pressure,
NSC/SKC/CLR/NCD/VV cloud codes, weather phenomena (with intensity and
descriptors), recent-weather RE prefix, BECMG/TEMPO/FM/TL/AT trend
codes, and strips RMK remarks. Plus ATIS/METAR/SPECI get lowercased
so TTS pronounces them as words (pilots SPELL ILS/QNH/VOR so those
stay uppercase).

Airport ICAO codes are substituted with their OpenAIP name when the
frequencies endpoint returns one. New `airportName` field added to
the FrequencyResponse for that. Adds 7 test cases covering the user-
reported EDDF sample plus calm/VRB/gust winds, RVR, weather codes,
cloud specials, trend codes, and RMK stripping.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:45:15 +02:00
itsrubberduck
8d4bfe9684 feat(atis): clock-locked ATIS loop tied to tuned frequency
Real ATIS broadcasts run continuously; tuning in mid-broadcast should
drop the pilot into the current spoken position, then loop. The frontend
now generates the full announcement once via TTS, plays it as a looping
HTMLAudioElement, and seeks to ((Date.now() - lastUpdated) / duration)
on metadata-load so all clients tuned to the same ATIS hear it phase-
synced. The loop starts/stops automatically with frequency tuning and
restarts on info-letter change. say.post.ts now caches tag=atis like
tag=flightlab to avoid re-synthesizing identical announcements.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 14:43:48 +02:00
leubeem
e7ac12e218 fix: correct OpenAIP v2 airport frequency parsing (3 bugs)
The frequency panel was always empty when using OpenAIP because of three
compounding bugs in frequencies.get.ts:

1. Wrong query parameter — ?icao=EDDF performs an unfiltered full-text
   search and returns all 46 000+ airports paginated; EDDF wasn't even
   on the first page.  The correct parameter is ?search=EDDF, which
   returns exactly the one matching airport.

2. Wrong ICAO field name — the code checked airport.icao but the real
   field in the v2 API response is icaoCode.  Even on a correctly
   filtered response the match would always fail.

3. Wrong frequency field names and numeric types — each frequency item
   exposes the MHz value in a value field (not frequency / frequencyMHz),
   and the service type is a numeric code (5=Delivery, 9=Ground,
   14=Tower, 15=ATIS) rather than a string.  Added OPENAIP_TYPE_MAP to
   translate these numeric codes to the internal DEL/GND/TWR/ATIS codes
   the rest of the pipeline expects.

With these fixes EDDF now returns all 8 frequencies (Delivery 122.035,
Ground 121.805, three Tower variants, two ATIS) which are then stored in
delivery_freq / ground_freq / tower_freq / atis_freq and used for both
the frequency overview panel and the per-state frequency validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 18:16:32 +02:00
leubeem
2069663f01 feat: wire Python backend session to PTT, add pm.vue debug logger
- Pass  as top-level field in PTT requests so Whisper STT
  results are linked to the correct Python backend session
- Add namespaced  helper in pm.vue (info/warn/error/debug/group)
  controlled by localStorage PM_DEBUG flag; logs transmit/response
  cycles, TTS calls, flag/variable syncs, and fallback warnings
- Log backend session creation context (flow, start state, vars, flags)
  in startMonitoring
- Fix typo in text input hint: STT fails not PTT fails

and

fix: sync backend variables to frontend after each transmission

The ATC say template was rendered using the frontend engine's local
variable defaults (squawk '1234', hardcoded SID, etc.) instead of
the authoritative values from the Python backend session. This caused
the spoken clearance and the readback prompt to show different squawk
codes.

- After each backend transmission response, sync all response.variables
  into vars.value (same pattern already used for flags)
- Prefer controller_say_rendered (pre-rendered by backend) over the raw
  template for TTS scheduling, eliminating any remaining dependency on
  local variable state for the ATC speech text
2026-05-20 16:44:01 +02:00
leubeem
1e31c7b2e7 Cleanup old unused code and add id sessionId to /api/atc/ptt 2026-05-20 14:13:26 +02:00
itsrubberduck
f38b47acbd Wochenreport 2026-05-06 17:38:36 +02:00
itsrubberduck
896ee4c3df simbrief gefixt 2026-05-06 15:43:05 +02:00
itsrubberduck
bbcd9dca54 first version looks ok 2026-04-25 20:29:09 +02:00
itsrubberduck
b7df1e86f2 feat: warn when classroom speech server is unavailable 2026-04-23 09:21:23 +02:00
itsrubberduck
b83ccf2c7c engine thr mehr respektieren 2026-02-20 23:55:45 +01:00
itsrubberduck
c4c841a2c1 feat(ws): add stick-input WebSocket message handler and client support
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:30:26 +01:00
itsrubberduck
390c38d8bd Bridge dome light mode mapping and flightlab seat belt flow 2026-02-18 22:43:56 +01:00
itsrubberduck
7971aeace5 add the lidl dome light temporarily 2026-02-18 21:49:03 +01:00
itsrubberduck
c605705a38 change default settings 2026-02-18 18:05:12 +01:00
itsrubberduck
04f2b6473e besser error message 2026-02-17 19:10:50 +01:00
itsrubberduck
73b3d19e33 personal waitlist link 2026-02-17 19:04:52 +01:00
itsrubberduck
f88fced5b1 typescript 2026-02-17 18:19:55 +01:00
itsrubberduck
8f45c3397d fix typescript errors and update dependencies 2026-02-17 18:13:04 +01:00
itsrubberduck
d34fba5cba add server log 2026-02-17 15:31:32 +01:00
itsrubberduck
c4f9237916 feat(bridge): add unlink flow for linked tokens 2026-02-16 16:39:18 +01:00
itsrubberduck
e1130734c1 fix: add CORS middleware for bridge endpoints 2026-02-16 16:21:30 +01:00
itsrubberduck
3d5a18df6a use 6 digit codes for bridge 2026-02-16 15:47:12 +01:00
itsrubberduck
08808b73b7 Fix bridge telemetry boolean coercion for auto-advance conditions
SimConnect sends boolean fields (on_ground, parking_brake, etc.) as 0/1
numbers. The condition evaluator uses strict equality (===), so
0 === false was returning false even when the condition was semantically
met. Cast boolean telemetry fields with !! to ensure proper type matching.
Also reduce solo-mode telemetry polling from 500ms to 2000ms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:26:50 +01:00
itsrubberduck
dc36a4b63f Connect bridge telemetry to FlightLab with auto-advance triggers
Map raw bridge fields (ias_kt, on_ground, etc.) to FlightLabTelemetryState
format, add direct telemetry polling endpoint for solo mode, and show
sim condition panel in sidebar regardless of auto-advance toggle state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:16:42 +01:00
itsrubberduck
76471c4bd4 Unify bridge auth header and add live telemetry panel 2026-02-15 16:04:07 +01:00
itsrubberduck
df21f4242d Add roadmap sim-control idea and update classroom news date 2026-02-15 15:41:02 +01:00
itsrubberduck
5a25e212d7 fix(classroom): audio speed slider now actually changes playback speed with pitch correction
- Fix client: playbackRate was set to 1 for non-native-speed providers (Speaches/Piper),
  making the speed slider ineffective in the main Pizzicato audio path
- Fix server: pass speed parameter to Speaches TTS API
- Add pitch-preserving playback via MediaElementSourceNode when rate != 1,
  routing through the same Web Audio effects chain (radio filters, distortion, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 01:01:02 +01:00
itsrubberduck
77ecd49334 feat(flightlab): sidebar, progress bars, skip speech, SimBridge telemetry & auth
- Add collapsible sidebar with phase stepper (jump between phases)
- Add SimBridge conditions panel in sidebar (live values, progress bars, targets)
- Add global progress bar (top edge, glowing) + phase-local TTS progress bar
- Add skip button to skip TTS speech while ATC is speaking
- Add skipSpeech() to audio composable (stops current Pizzicato sound)
- Wire up bridge data.post.ts with user auth (JWT) + example payload
- Add server-side telemetry store with pub/sub for Bridge→WS relay
- Extend WS handler with subscribe-telemetry message + userId tracking
- Extend sync composable with subscribeTelemetry() + onTelemetry() callback
- Add require-auth middleware to all flightlab pages
- Fix instructor station ECONNREFUSED via import.meta.client guard
- Add animations: phase transitions, button lists, fade-scale, check-pop, pulse

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:14:33 +01:00
itsrubberduck
a915af4398 dont log users id on transmission 2026-02-13 18:44:34 +01:00
itsrubberduck
fad56ee28d feat(flightlab): add WebSocket handler for instructor-participant session sync
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:45:18 +01:00
itsrubberduck
d90b494334 fix pm 2026-02-13 08:50:02 +01:00
Remi
93fed93d41 Add unsubscribe endpoint and email footers 2025-11-20 23:29:44 +01:00
Remi
040783f679 Style feedback drip email like invites 2025-11-20 21:26:55 +01:00
itsrubberduck
6e04501c4d add todo for najan 2025-10-19 21:03:52 +02:00
Remi
3dee67b3a5 Add bidirectional airport geocode and name-aware taxi routing 2025-10-19 19:17:18 +02:00
Remi
cc7e4927e4 Remove explicit type hints from airport geocode lookup 2025-10-19 19:07:23 +02:00
Remi
9d43c69539 Infer airport geocode types from query text 2025-10-19 19:00:32 +02:00
Remi
c8e35a140e Add airport geocode endpoint 2025-10-19 18:56:48 +02:00
Remi
aa50e345a7 Populate user notes from waitlist entry 2025-10-19 17:35:30 +02:00
Remi
77c7325c59 Add button-controlled roadmap item 2025-10-19 08:43:17 +02:00