Commit Graph
476 Commits
Author SHA1 Message Date
Said Turksever 91d8bba4a3 Merge branch 'main' of https://github.com/bilawalsidhu/gods-eye-view into pr/mapillary-layer
# Conflicts:
#	CHANGELOG.md
#	README.md
2026-10-05 13:47:54 +01:00
Huanyi Xie 1c3748e3bd fix(satellites): recover TLE parsing after incomplete sets 2026-10-05 13:02:20 +03:00
Niccolò Saggese e2ce7fa38e Merge remote-tracking branch 'upstream/main' into fix/363-geocode-proxy
# Conflicts:
#	SECURITY.md
#	server/providers/places/google.js
2026-10-05 09:26:08 +02:00
H4ldenandClaude Opus 5.5 10d10eab6b fix: address review on the Statens vegvesen CCTV pack
- Lower the Vegvesen default cap to 900, still above the ~850 working
  cameras, so the per-pack defaults stay closer to the catalog default.
- Name the flat ground-elevation prior as a known limitation: the feed
  is 2D, and mountain-pass cameras can sit below terrain where neither
  the point-height prior nor the ground snap resolves.
- README: ~3,900 public cameras.
- Use one HLS figure (~135 working) in CHANGELOG and DATA_SOURCES.
- DATA_SOURCES: list the frame and HLS hosts alongside the catalog host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 05:47:50 +02:00
manjunath22466 0343b4495e fix the remaining voice review gaps
keep analyst counts, speech, cards, cancellation, pointer capture, tool schema boundaries, and session lifetime consistent
add the exact Alps fallback and focused regression coverage
2026-10-05 04:37:28 +05:30
H4ldenandClaude Opus 5.5 d43478a547 feat: add Statens vegvesen (Norway) road cameras as a CCTV pack
Norway's ~850 working road cameras load from one keyless GeoJSON request
to the agency's OGC API view of its DATEX 3.1 CCTV table, so no DATEX
account is needed. Faulty cameras are dropped, every camera sits under a
"Norway" category, and the ~135 that publish HLS play as live video
through the existing in-memory relay with the still as fallback. Frames
and manifests are pinned to each camera's own path on the agency hosts.

CCTV_VEGVESEN_ENABLED=0 disables the pack, CCTV_VEGVESEN_MAX_SOURCES
caps it (nearest the largest cities first) and CCTV_VEGVESEN_VIDEO=0
keeps stills only. Attributed under NLOD 2.0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 22:23:16 +02:00
Bilawal Sidhu e7cb9b02b5 Merge main into feat/voice-core
Keeps the voice-core prompt rules and adds main's world-query and
vessels-vs-visuals rules; the tool catalog wraps the deixis-aware runner.

Also fixes two things the branch had before the merge:
- Live voice could not start: OpenAI rejects track_entity's top-level
  anyOf. Model-facing tools now omit top-level combinators; the runner
  and benchmark still require a query or a referent.
- The Contacts-centred analyst answer dropped the viewport coverage note.
2026-10-03 23:30:02 -07:00
Michael JohnstonandClaude Opus 5.5 44a2083d7b Merge current upstream and resolve PR 581 conflicts
b4067f9 moved the OpenSky states/all fetch into fetchOpenSkyStates, with a
10-second limit per attempt and one retry. Keep that and drop this branch's
20-second exchange deadline; the 8 MiB body cap now reads inside the helper
under each attempt's signal. The CHANGELOG bullet moves under [Unreleased],
and CURRENT-STATE describes the per-attempt OpenSky deadline.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 02:00:20 -04:00
Said TurkseverandClaude Opus 5.5 5a7ce5785e Merge upstream/main (v0.2.1) into pr/mapillary-layer
Brings in 123 commits from main, including the v0.2.0 and v0.2.1 releases
(MCP server and tool catalog, server security hardening, build hygiene).

Conflicts:
- package.json: kept main's script order and added this PR's
  qa:panel-resize and qa:street-level; exports keep both sides (main's
  tools/view entries and this PR's street-level and Mapillary entries).
- src/tooling/viteBuild.test.mjs: kept this PR's Mapillary-token define
  assertion and main's new wildcard-host allowedHosts assertions.
- CHANGELOG.md: main now has release sections; this PR's entries go under
  [Unreleased].

Checks after the merge: doctor, format:check, check:boundaries,
layer-token:check (28 published, 1 new: street-level 0), npm test (5726
pass), build, the Windows job's focused tests (70 pass), qa:panel-resize,
and the keyless street-level gate. The new CSP allows Mapillary (https:
connect/img, blob workers). The keyed gate stops at coverage locally
because the dev .env Mapillary token is invalid (upstream 403), not because
of the merge.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-03 13:29:32 +01:00
Sameh Khamis 80fc8f0e1c chore(release): prepare v0.2.1
Date the changes since v0.2.0 as 0.2.1, move the live CCTV video entry
into the 0.2.0 section where it shipped, and align the package metadata.
2026-10-02 22:19:04 -07:00
Sameh KhamisandClaude Opus 5.5 b33cae4b74 Merge main into #873
Resolve the CHANGELOG and CURRENT-STATE conflicts: the PR's entries go under
Unreleased, after the 0.2.0 release.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 21:18:15 -07:00
Sameh KhamisandClaude Opus 5.5 9c87fabf88 build: ship each bundled data pack once
loadBundledJson's callers passed a literal JSON import as a Node fallback,
which made the production build emit a JavaScript copy of every pack beside
the JSON the browser fetches. The loader now reads file: URLs itself under
Node (through process.getBuiltinModule, synchronously, as promptly as the
cached import it replaces), and the callers pass only the URL. On a Node too
old to provide getBuiltinModule it says that Node 24.14 or newer is needed.
dist/ drops from 54 MB to 42 MB. A test keeps app code from importing a pack
as a module.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 21:08:11 -07:00
Sameh KhamisandClaude Opus 5.5 9b4ccbff62 fix(server): gate Google text search against cross-site requests
/api/google/text-search spends the same Google quota as nearby-places but
was not behind the cross-site gate. It now refuses cross-site requests
before calling Google, and a test covers both paid Google routes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 20:16:39 -07:00
Sameh KhamisandClaude Opus 5.5 e6f1dd70a1 fix(server): apply the Host check before the app's own routes
Vite installs its Host check after the middleware plugins add in
configureServer, so the provider and /api routes answered any Host,
including a DNS-rebinding name. hostCheckPlugin runs first on the dev and
preview servers and applies Vite's rule with the same allowed hosts: IP
addresses, localhost and *.localhost, the configured server host, and the
listed names. A test drives a real Vite server with an unlisted Host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 20:15:54 -07:00
Sameh KhamisandClaude Opus 5.5 81513f9aae fix(mcp): refuse panel requests to routes reached through a dot suffix
The server routes a mounted path when a dot follows it, so /mcp.json reached
the MCP endpoint while panel_request refused only /mcp and /mcp/...; the same
held for the provider-settings, credential and model routes. The refusals now
match a dot after the route as well. A regression test drives panel_request
against a real Vite server.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 20:10:47 -07:00
Sameh KhamisandClaude Opus 5.5 6cdd6c8af9 refactor(server): share one proxy-header check across the local gates
localRequestGate.mjs owns the reverse-proxy header list and hasProxySignals;
Provider Settings, the cross-site gate and /mcp all use it instead of two
copies of the same nine names. The changelog credits the cross-site gate.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-02 20:02:48 -07:00
bbf4976f5e fix(server): keep debug-log timestamps server-owned and send nosniff
The voice debug log writes the server's timestamp after the client's record,
so a record can no longer replace loggedAt. The dev and preview servers also
send X-Content-Type-Options: nosniff with their shared browser headers.

From the findings in #710.

Co-authored-by: Sunil <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-02 20:00:11 -07:00
98b8c6c581 fix(server): keep the Host-header check when binding to all interfaces
Binding HOST=0.0.0.0 no longer sets Vite's allowedHosts to true. The dev and
preview servers accept IP addresses, localhost and the LAN hostnames listed
in GEV_ALLOWED_HOSTS; suffix and wildcard entries are ignored. The
configuration factory takes the resolved list as an input, so build/vite.js
still reads no environment.

Ported from #97 to the current build/vite.js layout. Fixes #21.

Co-authored-by: Sameh Khamis <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-02 19:59:02 -07:00
bb74071dc2 fix(server): throttle the cost-bearing proxies by default
The OpenAI endpoints (/api/realtime/token, /api/openai/hud-summary) now
allow 30 requests per minute per client IP and the Google Places endpoints
120, the caps the Pinokio build already ships. GEV_RATELIMIT_OPENAI_PER_MIN
and GEV_RATELIMIT_GOOGLE_PER_MIN still override them and exactly 0 disables
them; an unreadable value falls back to the default rather than to
unlimited.

Salvaged from #683 onto current main.

Co-authored-by: Sameh Khamis <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-02 19:57:29 -07:00
Sameh KhamisandClaude Opus 5.5 42faed3aea docs: describe the MCP server and panel in the security guide
SECURITY.md gains a section on what /mcp accepts, the panel's request key,
the browser keys the panel build carries and embed-mode framing. TOOLS.md
now describes what panel_request may request, and the changelog records
the recent MCP and embed changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-02 19:47:37 -07:00
Sameh Khamis b86239aa6d chore(release): prepare v0.2.0
Group the changes since v0.1.1 under a dated 0.2.0 section and align the
package metadata.
2026-10-02 18:47:29 -07:00
Sameh Khamis 9bc031c753 docs: describe God's Eye View in conversations in the changelog and current state 2026-10-02 18:29:42 -07:00
rileygramlich 8887ca47eb feat(air-quality): add the Air quality layer to the Weather group
Reworks #568 to the shape asked for in review.

Generalized from "AQHI" to a provider-backed Air quality layer, with ECCC's
AQHI as the first registered provider. Readings are carried as
{ provider, scale, value, band } rather than a bare number, because
air-quality indices are not interchangeable: AQHI runs 1-10+ on health risk,
the US AQI runs 0-500 on concentration breakpoints, and Europe's EAQI is a
five-step category. Naming the index a value belongs to is what lets another
network be added without inventing a shared numeric meaning. Coverage is
stated as "Canada only" on the provider, the layer row and the card.

Adds the WEATHER panel readout (readout: true) carrying the band legend and
the worst readings currently in view, with getRowControls/setRowControlsListener
following the wind and cyclone cards. An unresolvable view rectangle degrades
to the worst readings overall rather than to a blank card, and a rectangle
crossing the antimeridian is handled.

update() now takes the earthquakes layer's abort-and-guard shape: it aborts any
in-flight request, passes the signal through to the source, and refuses to
touch the scene when the request was superseded, aborted, or the layer was
disabled while it was outstanding. disable() and destroy() abort too. Tests
cover the late-response, superseded-refresh and disabled-update paths.

Rebased on main: the source is registered in src/sources/reference.js, and the
allocation profile was re-measured against the current worker — median 3,900
B/frame, max 4,114, 85.7 B/candidate, inside the shared 154 ceiling, budgeted
at 5,200 for 26% headroom. The earlier figures no longer held.

Freshness is now judged on the observation time rather than on when the last
response arrived. lastUpdate only says the feed answered; a station that keeps
returning a reading from four hours ago would otherwise be narrated as live by
the HUD and the voice provenance envelope. getStats() reports observedAt and
stale, so layerFeedState() calls the layer stale past two missed hourly
publications, and degraded when a refresh fails over readings that are still
good.

The share-link token is 0, which is what layer-token:next allocates. Taking it
moves the ledger fixtures that had hard-coded 0 as the free digit and 3 as the
one after; those shift to 3 and 4. The pinned equality between the reservation
ledger and LEGACY_LAYER_STATE_TOKENS also had to become "the closed legacy set
plus air-quality", since that set is documented as never growing. Happy to
re-pin this differently if you would rather the fixtures derive the provisional
digits from nextLayerStateToken() so the next layer does not move them again.

The layer and its source are registered as ./layers/air-quality and
./layers/air-quality/source, each with its own boundary group, following the
perimeters pair. check:boundaries requires the package exports and the group
exports to be the same set, so a layer that ships a source needs both.
2026-10-02 15:12:37 -06:00
manjunath22466 095981eee7 fix(voice): preserve analyst result and tracking contracts
keep contacts counts, rows, scope and provenance on one retained snapshot

report capped cohorts as lower bounds

separate sibling tool cancellation from newer user turns

align track_entity validation across runtime and benchmark paths
2026-10-02 11:15:31 +05:30
Michael Johnston d866f9b01c Merge current upstream and resolve PR 581 conflicts 2026-10-01 21:36:44 -04:00
Said TurkseverandClaude Opus 5.5 514788303d feat(street-level): refine ground casting with the Google 3D surface
The raycast challenge on #768 showed bare-earth casting misplacing roads
that are not at grade: lines floated over freeways in trenches and steep
streets (rendered line pixels floating: Austin 8.5%, SF 27.1%, LA 43.8%),
because a ~111 m terrain grid cannot see a 30 m-wide cut or a slope between
its corners.

meshSampler.js samples the rendered Google 3D surface (scene.sampleHeight,
overlays excluded) once per ~11 m cell within 900 m of the camera, nearest
first, in idle time within a 6 ms budget; misses are retried, not latched.
groundCast.refineHeights blends it in, per point along a line:
- mesh up to 3 m above bare earth is the road: follow it, 1 m above;
- higher mesh (canopy, deck, roof): carry the road's offset from bare earth
  across from the road samples on both sides (up to 8 points);
- otherwise bare earth + 2 m, as before.
Lines, cones and the marker use it; tiles touched by new samples are
redrawn one per idle slice with the no-blink swap.

Rendered line pixels on the surface / floating, before -> after:
Austin 90.0 -> 95.3% / 8.5 -> 0.7%; SF 69.0 -> 86.4% / 27.1 -> 9.1%;
LA 52.3 -> 91.0% / 43.8 -> 4.0%; Innsbruck 95.0 -> 95.7% / 0.2 -> 0.2%;
Sacramento 80.7 -> 79.3% / 3.8 -> 2.7%; buried lines stay under 1%.
Cones floating in SF 44 -> 5%, buried at LA 59 -> 28%. Main-thread long
tasks added during refinement: at most 134 ms (Austin, densest tile).
Roads on elevated decks are still drawn at ground level under the deck.

Unit tests cover the blend (trench, slope, canopy bridge, deck, bad probe),
the finer cast with meshAt, and the sampler (once per cell, nearest first,
overlays excluded, far cells skipped, misses not latched, idle when off).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 01:43:16 +01:00
Sameh Khamis 1a03e7483d feat: share the tool catalog with voice 2026-10-01 13:52:36 -07:00
Sameh Khamis 469b1e8d89 feat: serve the MCP server at /mcp on the local servers
Mount the local MCP server at /mcp on the development and preview
servers for clients that connect by URL. The route accepts only
requests from this machine that name a loopback host and, when an
Origin is sent, come from a loopback origin, and drains oversized
bodies so the 413 response is delivered. The provider-order regression
test now pins the route between the providers and the API fallback.
2026-10-01 12:47:44 -07:00
Sameh Khamis 9d12da0a79 feat: add a traffic flow tool
Add get_traffic_flow over the traffic source's TomTom flow tiles. It
reports average speed as a share of free-flow speed, congested and
closed road length, and a breakdown by road category, stepping down a
zoom level when an area needs more tiles than the source reads at once.
2026-10-01 12:36:00 -07:00
Sameh Khamis e6cbcc31ad feat: add ship tools
Add vessels_in_area, find_vessel and get_vessel_track over the live
vessel source, and a ships section in situation_brief. Without an AIS
key the source's own message reaches clients as an unavailable feed.
2026-10-01 12:32:09 -07:00
Sameh Khamis 0a74e36ce8 feat: add weather, hazard, terrain and situation tools
Add get_weather, get_regional_brief, get_cyclones, get_fire_perimeters,
get_terrain_height, find_military_installations and get_map_features
over the application request services and the layers' sources.

situation_brief combines weather, earthquakes, fires, aircraft and
cyclones for an area by reusing those tools, marking sections that are
unavailable instead of failing. get_hud_caption returns the app's
heads-up display caption for the same overview.
2026-10-01 12:28:34 -07:00
Sameh Khamis 8c97fed92d feat: add bike-share and transit tools
Add get_bike_share over the public GBFS systems and get_transit_vehicles
over the GTFS-Realtime feeds. Each finds the systems or feeds whose
coverage reaches the area, reads up to three, and reports any that were
unavailable instead of failing the whole answer. Transit results carry
each feed's attribution and license.
2026-10-01 12:22:13 -07:00
Sameh Khamis e680a59bf5 feat: add place search and routing tools
Add search_places and places_nearby over the app's Google Places routes,
and plan_route over its OpenStreetMap routing. The place search service
reports when no search key is configured, so an unconfigured server is
not mistaken for an empty result, and rate limits become retry_later.
Route endpoints accept a place name or coordinates.
2026-10-01 12:16:01 -07:00
Sameh Khamis 22c3fe40b0 feat: add aviation and satellite tools
Add aircraft_in_area, find_aircraft, get_aircraft_track and
get_aircraft_info over the live aircraft sources, with an optional
military feed, and next_satellite_pass and satellites_overhead over the
satellite catalog and the existing pass predictor.

Source failures from the live contract now become tool errors with the
matching code, so a rate limit reaches clients as retry_later with a
wait time. Tool schemas may use pattern.
2026-10-01 12:05:50 -07:00
Sameh Khamis c3dfa8e853 feat: add a tool catalog and local MCP server
Define tools that answer questions from the app's data once, independent
of the surface that exposes them. A tool declares its arguments and the
services it reads; applications compose tools with their own services and
optional call interceptors. Queries reuse the layers' portable source
factories, so they run wherever an application routes /api requests.

The first queries are get_earthquakes, get_active_fires and
get_recent_launches, with a shared area argument (place name, bounding
box, or point and radius) and capped lists.

gods-eye-view/tools/mcp adapts a catalog to the Model Context Protocol
with a small JSON-RPC handler and a stateless HTTP transport, adding no
dependencies. npm run mcp serves Core's tools over stdio to local clients
such as Claude Code, reading from a running app. Both exports are
portable and checked by check:boundaries; the app does not import them.
2026-10-01 11:51:59 -07:00
Manjunath 1dbc8a4452 fix(ui): preserve Cyber and Cockpit visual state 2026-10-01 17:00:10 +05:30
bassem chagra 031df60014 Merge remote-tracking branch 'origin/main' into feat/king-county-camera-pack
# Conflicts:
#	CHANGELOG.md
#	docs/CURRENT-STATE.md
2026-10-01 12:20:32 +02:00
Manjunath e03bf008f3 fix(ui): harden Cyber panel layout and preset restoration 2026-10-01 01:50:31 +05:30
Niccolò Saggese 5083a74413 Merge remote-tracking branch 'upstream/main' into fix/363-geocode-proxy
# Conflicts:
#	CHANGELOG.md
2026-09-29 19:16:15 +02:00
Said Turksever c4514784fc Merge branch 'main' into pr/mapillary-layer 2026-09-29 15:44:25 +01:00
ulihhhh 3bae92fce7 Merge upstream/main into feature/catalonia-cctv-cameras
# Conflicts:
#	docs/CURRENT-STATE.md
2026-09-29 10:08:59 +02:00
Bilawal Sidhu a19897ad15 feat(voice): voice core — honest layer reach, spoken summary with on-screen card, narration, point-and-ask
- Voice layer manifest (src/voice/layerManifest.js) generates the layer enums,
  aliases and analyst field hints; a test fails when a catalog layer has no entry.
- analyst_query validates fields, operators, units, values and scopes, counts up
  to 250,000 loaded records per layer, reports complete/partial/unanswered, tells
  LAYER_OFF / NOT_READY / FEED_UNAVAILABLE apart from zero, refuses mismatched
  follow-ups and never lets a superseded query replace follow-up memory. Seven
  more layers expose analyst records.
- Tool results carry a code-built say line and a display card; the voice card in
  the mic control shows captions, plan steps and the result
  (src/voice/resultDisplay.js is the one adapter). Slow tools narrate their
  current step out of band; holding Space while the assistant talks cuts it off.
  Turn spans go to the debug log; caption transcription is metered; the log
  omits what was said unless GEV_VOICE_LOG_CONTENT=1.
- Point and ask: the Space-down pointer snapshot resolves "this", "that one" and
  "around here" through 'pointer' targets, and "the second one" through
  referent:n from the last numbered list.
- Prompt states each policy once (instructions + tools 48.5k -> 46.7k chars).
- Voice benchmark harness (scripts/qa-voice-bench.mjs, scripts/voice-bench/)
  shares its phrase table and grading with scripts/qa-voice-routing.mjs.
2026-09-28 21:19:23 -05:00
f4fcf267c6 fix(osm): stop using public Overpass by default (#648) (#742)
Traffic roads come from OpenFreeMap vector tiles, or TomTom flow tiles with a
key (Hybrid by default). Military areas use OpenFreeMap polygons with a bundled
worldwide name index; wide views show named points. ALPR reads an hourly
OpenStreetMap extract. Cockpit place context and country, state and US county
outlines use bundled Natural Earth and US Census data. Overpass is used only
when an operator configures their own endpoint.

Also: camera arrivals no longer land below ground, keyless terrain retries on
429, bounded relation geometry for configured Overpass, one shared
OpenStreetMap credit, and separate tile and API transports for embedders.

Co-authored-by: theGaryLarson <[email protected]>
Co-authored-by: Milan Khanal <[email protected]>
2026-09-28 19:07:57 -05:00
Said TurkseverandClaude Opus 5.5 0f2cac2ca2 feat(street-level): one colour per imagery source
As more sources arrive, each one reads the same everywhere it appears: its
PROVIDERS chip, coverage lines, overview points, image cones and a single
legend swatch. The palette lives in one place, PROVIDER_COLORS in
src/layers/streetLevel/policy.js: Mapillary green (#05cb63), Panoramax
purple, Google Street View blue.

Mapillary drops its age dimming and magenta panoramas: lines and overview
points are Mapillary green, cones are green too and 360° images keep their
ring shape; the 360°/FLAT filter still separates them, and the selected
sequence stays GEV cyan.

The provider contract loses `legend`: `colors.coverage` is the source's one
colour, and the core composes the legend as one swatch per active source
(its name, its colour) followed by Selected. The chip group takes an
optional per-chip colour as a `--chip-color` CSS variable, written only
when it changes; Street Level chips show it as a swatch dot and, while on,
as the chip's border, text and a faint fill.

The feature gate checks the legend has one swatch per source plus Selected
and that the Mapillary chip carries #05cb63.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 00:03:00 +01:00
Said TurkseverandClaude Opus 5.5 ecb16b2fe2 Merge upstream/main into pr/mapillary-layer
Brings in #744 (share-link token ledger). package.json: keep the branch's
sorted scripts and add layer-token:next and layer-token:check.

Street Level's token moves from the provisional `3` to `0`: following
CONTRIBUTING, the provisional entry was removed and
`npm run layer-token:next -- street-level` assigned `0` (free digits in
order). It is added to the registry and to
src/data/layerStateTokenReservations.json, and
`npm run layer-token:check -- --base-ref upstream/main` passes (28
published, 1 new). No link with the provisional token was ever published.

Pinned token tests move with the ledger as CONTRIBUTING asks: 29
reservations, next free digit `3`, and the competing-PR and retired-digit
scenarios shift up one free digit (0->3, 3->4) so they still test the same
rules. The README share examples and the free-digit notes in
CONTRIBUTING and CURRENT-STATE now read `0`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 21:49:46 +01:00
Said TurkseverandClaude Opus 5.5 d3d3898dbb feat(street-level): place coverage on the bare earth on Google 3D
Draped ground polylines and clamped billboards land on the top of the
Google Photorealistic 3D mesh, so on a street under trees or an overpass
the coverage seemed to float at roof height.

At street zoom on Google 3D (camera below 1,400 m, back above 1,800 m)
the core switches to a `terrain` surface mode. groundCast.js samples the
existing terrain service on the ground-floor grid (0.001 deg), interpolates
bilinearly and adds 2 m; Mapillary lines become plain polylines at those
heights, and cones and the position marker stand on the street, so the
mesh hides what is behind buildings and trees. A tile draws draped first
and swaps to its cast lines once the heights arrive, keeping the old lines
until the new primitives are ready. Lookups are once per tile and aborted
when the tile is dropped; a geoid fallback counts as unknown and leaves
lines draped. Other map stacks and higher views are unchanged.

Providers get getSurface() and groundCaster in their context and an
optional setSurface(mode); getUIState() exposes `surface`. The QA gate
checks draped on Esri and terrain on Google 3D.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 21:16:54 +01:00
bassem chagra 3273fb2431 Merge branch 'main' into feat/king-county-camera-pack 2026-09-28 10:19:25 +02:00
bassem chagra 5dfe3f0525 docs(cctv): add the King County pack to CHANGELOG, CURRENT-STATE, README and .env.example
CONTRIBUTING requires runtime-behavior changes to update docs/CURRENT-STATE.md
and CHANGELOG.md in the same PR, and new env controls belong in .env.example
with the other CCTV pack controls. The README and CURRENT-STATE table diffs
are column realignment from the widened source cell.
2026-09-27 12:23:23 +00:00
ulihhhhandClaude Opus 5.5 320a1d6bfd fix(cctv): harden the Catalonia catalog fetch and frame-link allowlist
Address review feedback on #595:

- Reject frame links carrying userinfo (user:pass@) in
  normalizeCataloniaImageUrl, so catalog data cannot inject credentials
  into the server-side frame request on an allowlisted host.
- Fetch cameres.xml over https (verified: same 200 body, no redirect),
  refuse redirects with the unread body released, and read the body
  through readResponseTextCapped with a 1 MiB CATALONIA_MAX_CATALOG_BYTES
  cap instead of an unbounded resp.text().
- Add regression tests for userinfo links on every allowed host, the
  refused redirect, and declared and streamed oversize bodies.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 12:01:17 +02:00
Michael JohnstonandClaude Opus 5 5f8a275487 fix(providers): bound the remaining upstream body reads
Route the six proxied fetches that still buffered upstream bodies without
a limit through the shared capped readers in src/sources/httpBody.js:
FIRMS CSV, CelesTrak TLE groups, adsb.lol /v2/mil (both the OK and
relayed-status paths), TomTom flow tiles, OpenSky states/all and adsbdb
enrichment lookups. Each cap is a bound sized well above the measured
payload, not a budget.

readResponseBytesCapped gains the same optional abort signal as the text
reader, with the same cancel-on-abort plumbing and reader release, so the
TomTom tile path can share the route deadline instead of reading
arrayBuffer() unbounded. Where a route already had a deadline, the same
signal now covers the body read as well as the headers. Two polls had no
deadline at all: the adsb.lol military poll gains a 12-second one and the
OpenSky states/all exchange, including its Basic-auth retry, a 20-second
one; both miss into their existing STALE paths.

FIRMS map-key status, the OpenSky token exchange and CCTV media are left
as they are.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-26 14:40:08 -04:00