feat: API proxy routes for the desktop app (stream A: A1-A4, A6)#3
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:ANTHROPIC_API_KEY,ELEVENLABS_API_KEY,ELEVENLABS_VOICE_IDvalidated insrc/env.js; keys live inflexithon-app/.env.local(gitignored)./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./api/tts: proxy to ElevenLabs; voice pinned server-side via env.scripts/api-smoke.shexercises 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..env.exampleupdated; ElevenLabs free-tier gotcha recorded (see below).Verified
scripts/api-smoke.shgreen against localhost with real keys:/api/chatstreamed a correct vision response;/api/ttsreturned ~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.localand.env.examplenow use George (JBFqnCBsd6RMkjVDRZzb); pick any premade voice.What's left (A5 — not in this PR)
-b https://<prod>.vercel.app, record the prod URL. Needs Vercel account access — notevercel.jsonwas removed and the CI deploy step was dropped, so this goes via dashboard git integration orvercelCLI.localhost:3000and do the first full voice → pointer → speech loop.Made with Cursor