Skip to content

Unify on vx serve: drop CF cloud + otel-bridge package, SPA over HTTP#143

Merged
Exelord merged 4 commits into
mainfrom
claude/unified-insights-api
Jun 21, 2026
Merged

Unify on vx serve: drop CF cloud + otel-bridge package, SPA over HTTP#143
Exelord merged 4 commits into
mainfrom
claude/unified-insights-api

Conversation

@Exelord

@Exelord Exelord commented Jun 21, 2026

Copy link
Copy Markdown
Member

Summary

Owner directive (2026-06-21): "vx cloud is exactly the same as vx serve. We don't need a separate stack for it. It can run in Docker just fine. Cf is no longer a requirement. Simplify and unify architecture where possible only what we really need. Also instead of Otel bridge we could natively talk in that. No custom format and standard."

This PR collapses the platform layer onto one backend (vx serve) and one OTel path (native, no bridge package).

What changed

One backend. Delete apps/cloud/ (the entire CF Workers project — wrangler.toml, R2/D1/DO/Queue/KV bindings, HMAC wrapper, queue→D1 consumer). vx serve is the cloud — same Bun binary, same bun:sqlite cache, same JSON shape — and it runs in Docker for hosted use.

OTel inline. Delete packages/otel-bridge/. OTel emit moves into core as src/orchestrator/otel-emit.ts, opting in via OTEL_EXPORTER_OTLP_ENDPOINT + three optional @opentelemetry/* peer deps. Missing peers / missing env var = silent skip; core's 19-dep tree stays untouched.

New /v1/* HTTP surface on vx serve.

  • GET /v1/runs, /v1/invocations, /v1/runs/:id
  • GET /v1/cache/stats, /v1/history
  • GET /v1/explain/:taskId, /v1/why/:runId/:taskId
  • GET /v1/events (SSE alias of /events)
  • Every response: Access-Control-Allow-Origin: * + 204 OPTIONS preflight (hosted SPA must reach localhost from a foreign origin)
  • New src/orchestrator/insights-queries.ts: pure SQL over bun:sqlite backing all of the above

SPA rewritten as a thin HTTP client. apps/insights/ drops DuckDB-WASM (no more 30 MB payload, no more ATTACH/registerFileBuffer dance). New api.ts is typed HTTP over /v1/*. Shell gains a connection picker (URL chip + status dot, localStorage-persisted, defaults from VITE_DEFAULT_ORIGIN at dev time). Same UI for local OR hosted; pick your backend at runtime.

vx insights simplified. Boot vx serve + the Vite dev server, pass the server's origin through as VITE_DEFAULT_ORIGIN. Static cache.db server gone.

Docs

  • DELETE guides/vx-cloud.md → REPLACE with guides/self-hosting.md (Docker compose + Caddy auth/TLS recipe + browser-localhost Secure Context exception)
  • REWRITE guides/otel-bridge.md as "OpenTelemetry — native"
  • REWRITE guides/insights.md around the connection picker UX + /v1/* surface
  • docs/design/vx-cloud-2026-06.md marked superseded with a header note
  • README + introduction + landing-page card updated
  • Append a unification step to docs/progress/implementation-log-2026-06.md

Test plan

  • bun src/bin.ts run ci — full gate green
  • 923 pass / 17 skip / 0 fail (was 919/17). Added 4 serve HTTP-API tests + 10 insights-queries tests; removed 3 obsolete static-server tests
  • CORS preflight + permissive headers verified end-to-end
  • /version advertises workspace root + RPC capabilities
  • 404 returned for unknown run id

Net

  • Commit 1: −1646/+896 LOC across 25 files (delete cloud + bridge, inline OTel, new insights-queries)
  • Commit 2: +239/−19 (serve /v1/* + CORS + tests)
  • Commit 3: −603/+422 across 13 files (SPA refactor)
  • Commit 4: docs

One stack everywhere; the SPA is a thin client; vx serve is the only backend.


Generated by Claude Code

claude added 4 commits June 21, 2026 17:32
One stack: vx serve runs locally OR in Docker for hosted use; no
separate CF Workers project. The hosted SPA connects to either a
local server (file://-equivalent local cloud) or a remote one — same
HTTP surface either way.

OpenTelemetry emit moves into core as src/orchestrator/otel-emit.ts —
no bridge package, no custom wire format. Users still opt in via
OTEL_EXPORTER_OTLP_ENDPOINT + installing the three @opentelemetry/*
peer deps; missing deps silently skip.

New src/orchestrator/insights-queries.ts: pure SQL over a bun:sqlite
Database (listRuns, getRun, getCacheStats, getHistory, explainCacheKey,
whyDidThisRerun). Backs the upcoming vx serve /v1/* routes.

- delete apps/cloud/ (entire CF Workers project)
- delete packages/otel-bridge/
- drop "packages/*" from root workspaces
- inline OTel emit into core (attachOtelEmit)
- new insights-queries module + 10 tests
The hosted SPA needs to read cache.db via HTTP — not direct ATTACH
or DuckDB-WASM tricks. vx serve now opens the workspace cache once
and exposes:

  GET /v1/runs?project=&task=&runId=&limit=
  GET /v1/invocations?limit=
  GET /v1/runs/:runId
  GET /v1/cache/stats
  GET /v1/history?project=&task=&limit=
  GET /v1/explain/:taskId
  GET /v1/why/:runId/:taskId
  GET /v1/events            (SSE alias of /events)

Every response carries `Access-Control-Allow-Origin: *` and OPTIONS
preflights answer 204 — the hosted SPA must be able to reach a
localhost vx serve from a foreign origin. The surface is read-only
+ the WS run-submission already authenticated by being on the same
machine, so wide-open CORS is safe.

/version now also reports the workspace root.

4 new tests covering the API, 404 behavior, CORS preflight, and the
extended /version payload.
The insights SPA is now a thin client over the unified vx serve API:

- delete apps/insights/src/duckdb.ts (no more 30MB WASM payload, no
  more ATTACH/registerFileBuffer dance)
- rewrite api.ts as a typed HTTP client over /v1/* with a connection
  store (localStorage-persisted origin, defaults from
  VITE_DEFAULT_ORIGIN at dev time)
- Shell gains a connection picker (URL input + status dot, footer
  shows the connected workspace + vx version)
- Overview now reads /v1/invocations + /v1/cache/stats; RunDetail
  uses /v1/runs/:id; Flamegraph adapts to the camelCase shape
- listRuns SQL now returns wallclockStartNs/EndNs as decimal strings
  so flamegraphs work over HTTP

vx insights drops the static cache.db server entirely: it boots
vx serve (same backend everywhere) and the Vite dev server with
VITE_DEFAULT_ORIGIN pointing at the server's origin. One stack.

Drop @duckdb/duckdb-wasm dep + the obsolete insights-static test.
- delete guides/vx-cloud.md (CF deployment); replace with
  guides/self-hosting.md (Docker compose + Caddy auth/TLS recipe +
  the browser-localhost gotcha documented)
- rewrite guides/otel-bridge.md to "OpenTelemetry — native"; no
  more @vzn/vx-otel-bridge — three optional @opentelemetry/* peers
- rewrite guides/insights.md around the connection picker UX +
  /v1/* HTTP surface
- mark docs/design/vx-cloud-2026-06.md as superseded with a header
  note pointing at the unification
- update sidebar (drop vx-cloud, add self-hosting)
- update landing-page card ("Cloudflare-template" → "Self-hostable,
  Docker-ready")
- update introduction + wire-protocol stray references
- update README: vx insights now talks HTTP, OTel is native, self-
  hosted cloud row replaces CF-template row
- append unification step to the 2026-06 implementation log
@Exelord Exelord merged commit e8f4245 into main Jun 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants