Skip to content

feat: API proxy routes for the desktop app (stream A: A1-A4, A6)#3

Merged
gitjehan merged 15 commits into
mainfrom
feat/api-proxy
Jul 3, 2026
Merged

feat: API proxy routes for the desktop app (stream A: A1-A4, A6)#3
gitjehan merged 15 commits into
mainfrom
feat/api-proxy

Conversation

@gitjehan

@gitjehan gitjehan commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Stream A from docs/TASKS.md — the Next.js backend the desktop app talks to, so no API key ever ships in the app binary. Six commits:

  1. A1 — env schema: ANTHROPIC_API_KEY, ELEVENLABS_API_KEY, ELEVENLABS_VOICE_ID validated in src/env.js; keys live in flexithon-app/.env.local (gitignored).
  2. A2 — /api/chat: verbatim streaming passthrough to Anthropic Messages API. The Mac app speaks Anthropic's wire format directly, so the SSE stream pipes back untouched. Includes a load-bearing middleware fix: both API routes are exempted from Clerk auth — the desktop app has no browser session, and the default matcher was 307-redirecting API calls to the sign-in page.
  3. A3 — /api/tts: proxy to ElevenLabs; voice pinned server-side via env.
  4. A4 — smoke test / prompt harness: scripts/api-smoke.sh exercises both routes end to end (streaming transcript + audio bytes) with flags for base URL, image, question, system prompt, and model — this is also the prompt-iteration harness for streams E and F.
  5. A6 — structured logging: upstream status, time-to-first-byte, payload size, truncated error bodies; real status codes pass through instead of a blanket 500.
  6. Docs: .env.example updated; ElevenLabs free-tier gotcha recorded (see below).

Verified

  • scripts/api-smoke.sh green against localhost with real keys: /api/chat streamed a correct vision response; /api/tts returned ~34KB of playable MP3.
  • npm run check (lint + typecheck) passes clean.

Gotcha worth knowing

Free-tier ElevenLabs keys can only use premade voices via the API — community-library voices return 402. .env.local and .env.example now use George (JBFqnCBsd6RMkjVDRZzb); pick any premade voice.

What's left (A5 — not in this PR)

  • Vercel deploy: set the three env vars in the Vercel project, deploy, re-run the smoke test with -b https://<prod>.vercel.app, record the prod URL. Needs Vercel account access — note vercel.json was removed and the CI deploy step was dropped, so this goes via dashboard git integration or vercel CLI.
  • After merge: B6 smoke test — run the desktop app against localhost:3000 and do the first full voice → pointer → speech loop.

Made with Cursor

gitjehan and others added 15 commits July 4, 2026 09:42
ANTHROPIC_API_KEY, ELEVENLABS_API_KEY and ELEVENLABS_VOICE_ID are now
validated at build time so a missing key fails loudly instead of
breaking silently at runtime. Keys live in flexithon-app/.env.local
(gitignored) per docs/API_KEYS.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
Verbatim passthrough: forwards the desktop app's Messages API body,
attaches the key server-side, and pipes the SSE stream back untouched
so the app's existing streaming parser works as-is. Marked public in
the Clerk middleware - the desktop app has no browser session, and the
route was otherwise caught by the /(api|trpc) matcher and would 401.

Co-authored-by: Cursor <cursoragent@cursor.com>
Forwards the app's { text, model_id, voice_settings } body and streams
audio/mpeg back. Voice ID is pinned server-side via env so all installs
share the same voice; key attaches server-side like /api/chat.

Co-authored-by: Cursor <cursoragent@cursor.com>
scripts/api-smoke.sh exercises /api/chat (streaming, with a built-in or
supplied image) and /api/tts (audio bytes written to /tmp). Flags for
base URL, image, question, system prompt and model make it the harness
for step-mode and scam-guardian prompt iteration without the app.
Verified green against localhost with real keys.

Co-authored-by: Cursor <cursoragent@cursor.com>
Both routes now log upstream status, time-to-first-byte and (for chat)
payload size on success, and truncated upstream error bodies on
failure, so Vercel logs tell us immediately whether a demo hiccup is
our proxy, the provider, or the app. Upstream status codes pass
through instead of a blanket 500.

Co-authored-by: Cursor <cursoragent@cursor.com>
.env.example now includes the three AI proxy vars with a working
premade voice ID. API_KEYS.md warns that free-tier ElevenLabs keys can
only use premade voices via the API (library voices return 402 - hit
this in testing). TASKS.md: A1-A4 + A6 done, A5 (Vercel deploy)
highlighted as remaining.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
npm install on macOS dropped the @emnapi entries that the Linux CI
runner needs, so `npm ci` failed the lockfile sync check on the quality
job. Regenerated with --package-lock-only to capture the full
cross-platform dependency tree.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gitjehan gitjehan merged commit be9212b into main Jul 3, 2026
2 of 3 checks passed
@gitjehan gitjehan deleted the feat/api-proxy branch July 3, 2026 23:47
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.

1 participant