diff --git a/.env.example b/.env.example index efa5089d2..252609411 100644 --- a/.env.example +++ b/.env.example @@ -56,8 +56,14 @@ GOOGLE_MAPS_SERVER_API_KEY= # CLIENT-EXPOSED β€” use a public assets:read token with URL restrictions. CESIUM_ION_TOKEN= -# Optional: OpenAI Realtime voice control. Do not prefix with VITE_. +# Optional: OpenAI Realtime voice control. API key is the default cloud auth. +# Provider Settings can instead opt voice into the signed-in local +# ChatGPT/Codex OAuth session; the browser still receives only a short-lived +# Realtime client secret. Do not prefix with VITE_. OPENAI_API_KEY= +# Optional: forbid API-key voice sessions. Select ChatGPT OAuth in Provider +# Settings; missing or expired Codex sign-in then fails closed. +# GEV_PREFER_CODEX_OAUTH=true OPENAI_REALTIME_MODEL=gpt-realtime-2 # Cheaper voice tier, used when the GEV MIC panel's MINI toggle is on. # Model ids and prices are external facts that drift β€” if OpenAI moves this id, diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b1cb05cc..54380bdc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,37 @@ # Changelog +- Treat explicitly current, complete empty vessel coverage as a successful update, + clearing obsolete contacts while retaining stale or incomplete snapshots. + +- Custom Street Level provider switches survive share links and stored state + using URL-safe stable provider IDs and a shared boolean-switch codec; existing + Mapillary links keep their meaning. Explicit provider toggles reach durable + state without adopting unrelated live switch changes. + +- Directions and Recent Imagery accept replaceable acquisition sources, including + route transport, imagery catalog, thumbnails, tile templates and attribution. + Explicitly removing either source leaves its layer unavailable; standalone + defaults preserve the existing providers. + +- Catalog source availability no longer modifies layer objects; unavailable + tooltips use layer display names. + +- Street Level with no providers uses the shared unavailable state and refuses + activation before initialization, while keeping its layer identity visible. + +- Application source configuration accepts replacements and explicit removal. + Missing catalog feeds leave their layers unavailable without blocking startup + or making fallback requests; malformed supplied feeds remain configuration errors. + +- Street Level providers are optional in application composition. Omitting a + Mapillary source no longer prevents startup; callers can inject a different + provider list or an empty list without changing layer identities or share links. + +- Vector tile sources take their allowed tile origin from the configured + `tileJsonUrl`, so repointing only that URL at a mirror loads tiles from the + mirror or fails visibly instead of silently using OpenFreeMap. Thanks to + [daikaginza](https://github.com/daikaginza) (#935). + - MCP setup examples use the app's default port, `4173`. Thanks to [Huanyi Xie](https://github.com/xiehuanyi) (#934) and [shirunjies8-png](https://github.com/shirunjies8-png) (#959) for the same fix. @@ -69,7 +101,20 @@ Maps JavaScript API) and click a street, or press OPEN STREET VIEW, to open Google's own panorama in the panel, with its arrows, the address and month in the caption, a link - to Google Maps and FOLLOW. Share links carry it as `g`. + to Google Maps and FOLLOW. Share links carry it in the provider switches + (`r.google-1`). + +- Add an experimental ChatGPT/Codex OAuth option for cloud voice in Provider + Settings. API-key voice remains the default. Local sign-in can start from + the auth button and reports completion, failure, timeout, or expired + credentials. OAuth credentials stay on the server; local routes require + loopback access and same-site requests. OAuth sessions retain usage data + and show unknown cost; caption transcriptions are counted rather than + priced into the API-key cap. The auth button keeps keyboard focus after a + mode change, and pending sign-in does not discard text in the key fields. + Thanks to [Shayan Khan](https://github.com/devv-shayan) for #621 and + [TheSmokeDev](https://github.com/TheSmokeDev) for the credential handling + from #653. - Stdio servers from one install share a panel key so a page read from one process can make requests through another. Concurrent malformed-key repairs diff --git a/README.md b/README.md index 5f7150780..420953f1f 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,13 @@ _Why cockpit mode exists: you're riding a real aircraft over real terrain β€” an ## πŸŽ™οΈ Talk to It -> Voice needs an **OpenAI key**. Without one the entire app still runs β€” the mic button just reports voice is unavailable. The same key drives the **AI HUD summary**: a terse, five-word intelligence-style readout of the current view that regenerates as you move. +> Voice can use an **OpenAI API key** or, on the same machine, an opt-in signed-in **ChatGPT/Codex OAuth** session. The API-key path remains the default. Selecting ChatGPT OAuth in Provider Settings reuses an existing local sign-in or starts the Codex browser sign-in flow and selects OAuth when it completes. The OpenAI key still drives the **AI HUD summary**: a terse, five-word intelligence-style readout of the current view that regenerates as you move. + +The OAuth option is experimental. OpenAI documents ChatGPT plan usage for the [Responses API](https://developers.openai.com/siwc/token-sharing-open-source/models-and-inference); a supported third-party Realtime contract for reusing Codex tokens has not been confirmed. A successful token mint does not establish billing or support terms. + +GEV reads Codex's file credentials but does not write or refresh them. It checks token shape and expiry, and reports expired sign-in as requiring re-authentication. The login button runs `codex login`, permits one pending attempt, and reports exit failures or a two-minute timeout. `CODEX_HOME` must match the directory that Codex uses. If `CODEX_AUTH_JSON` points elsewhere, sign in separately or fix that mismatch before using the button. Codex credential storage must use `auth.json`; Keychain-only and in-memory storage are not readable by GEV. On Windows, the login button may not be able to start an npm-installed Codex; run `codex login` yourself, then select OAuth. See [Codex credential storage](https://learn.chatgpt.com/docs/auth#credential-storage). + +For a live voice check, run `node scripts/qa-voice-auth.mjs http://127.0.0.1:4189 oauth microphone.wav` against your local dev server. Use a WAV with three seconds of leading silence followed by a short spoken reply request. Replace `oauth` with `api-key` to check the API-key path. The check verifies audio input, a completed spoken reply, usage, resource cleanup, and reconnect. It saves screenshots and credential-free results under `.gev-logs/voice-auth/`. Click **GEV MIC**, grant the microphone, and just talk. This is more than a voice-controlled remote: @@ -243,7 +249,7 @@ Click **GEV MIC**, grant the microphone, and just talk. This is more than a voic - **🎯 Entity Q&A.** Click any plane, ship, or datacenter and ask _"what's this?"_ It answers using the object's live telemetry. - **πŸ‘οΈ Visual grounding.** At street level, it reads a viewport screenshot to identify legible signage and building names, and is instructed never to hallucinate labels. - **🎬 Cinematic framing.** _"Show me the planes overhead"_ pulls the camera back, angles it, and frames the live traffic like a director. -- **πŸ”’ Honest and secure.** The agent only confirms actions that succeeded. Your `OPENAI_API_KEY` never touches the browser; the client only gets a short-lived session token. +- **πŸ”’ Honest and secure.** The agent only confirms actions that succeeded. Your `OPENAI_API_KEY` never touches the browser; the client only gets a short-lived session token. If you choose **USE CHATGPT OAUTH** in Provider Settings, the local server uses that sign-in only to mint the same kind of short-lived Realtime credential, and refuses the OAuth path for non-loopback clients. Thirty tools, four jobs β€” the commands below come straight from the product's voice test suite and tool playbook: @@ -340,7 +346,7 @@ _The Space Missions layer replaying a Falcon 9 ascent β€” labeled `RECONSTRUCTED **Use.** Turn on **Street Level** under DATA LAYERS β†’ Cameras, or with the pill in the STREET LEVEL panel header. Each provider has a chip in its own colour (Mapillary green). The **IMAGERY** (ALL / 360Β° / FLAT) and **SINCE** filters apply to every provider. Click a coverage line for its image cones and a cone to open the photo. On the **Google 3D** map (Google's terms keep Street View off other maps, where it does not appear), light **STREET VIEW** and click a street from below 3 km, or press **OPEN STREET VIEW** for the view centre, to open Google's panorama there; step along its arrows. Above the photo: **EXPAND**, **FIT / FILL** (Mapillary), and **FOLLOW** (the globe camera follows the photo's view; Google 3D map only). The caption links to the image on the provider's site, and the globe credits each provider that is on ("Β© Mapillary contributors, CC BY-SA 4.0", "Street View imagery Β© Google"). -Drag the panel header to float the panel, and resize it from any edge. Double-click the header or collapse it to put it back in the rail. Share links carry the provider switches and filters (`0.m.0` Mapillary off, `0.g.1` Street View on, `0.p.p` panoramas only, `0.s.365` the last year). To add a provider, implement the contract in `src/layers/streetLevel/registry.js` and register it in `src/app/layers/streetLevel.js`. +Drag the panel header to float the panel, and resize it from any edge. Double-click the header or collapse it to put it back in the rail. Share links carry the provider switches and filters (`0.m.0` Mapillary off, `0.r.google-1` Street View on, `0.p.p` panoramas only, `0.s.365` the last year). To add a provider, implement the contract in `src/layers/streetLevel/registry.js` and register it in `src/app/layers/streetLevel.js`. ## πŸŽ–οΈ Field Missions @@ -429,7 +435,7 @@ Seven keys. Five have a free tier, and the two πŸ”΄ ones are metered: | --- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 🟑 | **Cesium ion** | πŸ—ΊοΈ Google Photorealistic 3D, world terrain, and additional ion-hosted imagery stacks. The free Community plan is for eligible individual, personal/non-commercial use and has quotas | [cesium.com/ion](https://cesium.com/ion) β€” use a public `assets:read` token and check current [pricing/eligibility](https://cesium.com/platform/cesium-ion/pricing/) | | πŸ”΄ | **Google Maps** | Direct Google Photorealistic 3D, Google place search and Street View ([Map Tiles API](https://developers.google.com/maps/documentation/tile); Street View also needs the Maps JavaScript API) | [Google Cloud Console](https://console.cloud.google.com/) β€” URL-restrict it | -| πŸ”΄ | **OpenAI** | πŸŽ™οΈ The voice experience + AI HUD summary. The mini model works; the standard model is noticeably smarter. Want Gemini or another provider behind the mic? PRs welcome | [platform.openai.com](https://platform.openai.com) β€” metered, see costs below | +| πŸ”΄ | **OpenAI** | πŸŽ™οΈ Voice + AI HUD summary through an API key. Provider Settings also offers **USE CHATGPT OAUTH** for cloud voice without removing the API-key option | [platform.openai.com](https://platform.openai.com) β€” API-key usage is metered, see costs below | | 🟑 | **AISStream** | 🚒 Live global ships | [aisstream.io](https://aisstream.io) β€” free signup | | 🟑 | **NASA FIRMS** | πŸ”₯ Live active fires | [firms.modaps.eosdis.nasa.gov](https://firms.modaps.eosdis.nasa.gov/api/map_key/) β€” free | | 🟑 | **TomTom** | 🚦 Live flow speeds and congestion colors for the simulated traffic layer | [my.tomtom.com](https://my.tomtom.com/keys) β€” free tier available | @@ -508,7 +514,7 @@ Honest numbers, roughly, as of mid-2026 β€” always check the provider pricing pa | **🟒 Most layers** | **$0, no signup.** OpenSky anon, USGS, CelesTrak, adsb.lol, city CCTV, Radio Browser, GBFS, Launch Library 2, transit feeds, OSRM routing, NOAA and ECMWF weather, OpenStreetMap ALPR mapping, bundled datasets. | | **🟑 The free-key tier** | **$0 with a signup.** AISStream, FIRMS, TomTom, OpenSky, plus Cesium ion for eligible personal/non-commercial use. Provider quotas and eligibility still apply. | | **πŸ—ΊοΈ Google 3D tiles** | **Free through an eligible Cesium ion Community account within its quota; metered through a direct Google key.** Use the direct route for Google place search or commercial deployment, verify current provider terms, and set budget alerts where billing is enabled. | -| **πŸ”΄ OpenAI voice** | **The one that costs real money β€” so the app meters it for you.** Realtime audio runs a few cents per active minute; an evening of heavy use is single-digit dollars. A live session-spend readout sits next to the mic, with an STD/MINI model toggle, a $2 warning, and a **$5 hard cap that ends the session**. The voice context window is kept deliberately short too. | +| **πŸ”΄ OpenAI voice** | API-key sessions keep the live spend readout, STD/MINI model toggle, $2 warning, and **$5 hard cap**. ChatGPT OAuth is an explicit choice in Provider Settings. Its usage is tracked without an API-dollar estimate or spend cap. The voice context window stays deliberately short. | Google's direct 3D route is surprisingly generous: the first 1,000 Photorealistic 3D Tiles sessions each month are currently free, and one root request supports diff --git a/SECURITY.md b/SECURITY.md index 15ad0bc16..664a3e6d5 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -18,6 +18,7 @@ The golden rule: **secret-bearing API keys stay on the server side.** The dev/pr | Key | Where it lives | How the browser uses it | |-----|----------------|--------------------------| | `OPENAI_API_KEY` | Server only | Browser fetches a short-lived **ephemeral** Realtime session token from `/api/realtime/token`; the real key never ships | +| ChatGPT sign-in through Codex (optional, experimental) | Codex's own `auth.json` (`CODEX_HOME`, default `~/.codex`); GEV reads it server-side and never writes or refreshes it | Only after **USE CHATGPT OAUTH** is selected in Provider Settings: the server mints the same ephemeral Realtime token with the ChatGPT access token, for loopback clients only | | `AISSTREAM_API_KEY` | Server only | Server holds the AISStream websocket; browser polls the same-origin `/api/vessels` cache | | OpenSky OAuth (`OPENSKY_CLIENT_ID/SECRET`) | Server only | Server mints + refreshes the token behind `/api/flights` | | `GOOGLE_MAPS_SERVER_API_KEY` (optional, #33) | Server only | Server calls Places (`/api/google/nearby-places`, `/api/google/text-search`) and the Street View fallback with this key; falls back to `GOOGLE_MAPS_API_KEY` when unset | @@ -76,6 +77,8 @@ The data proxies under `server/providers/` are written so the browser cannot tur - **Full Content-Security-Policy.** The dev and preview servers send a real CSP (`script-src 'self' 'unsafe-eval' blob:` plus the YouTube, Facebook and X scripts the Nepal event's embedded media loads and the Maps JavaScript API that Street View runs on, with a matching `frame-src` for their players and posts: no inline script and no other script origin; `blob:` covers the object URLs Cesium's bundled workers bootstrap through in the built app), alongside the existing `X-Frame-Options: DENY` and `frame-ancestors 'none'`. `'unsafe-eval'` stays because Knockout, bundled inside Cesium's widgets package, resolves the global object with `eval` at load time and the globe does not initialize without it; styles permit `'unsafe-inline'` and Google Fonts because Vite's dev client injects `