Skip to content

Add aai dev/share/deploy and make starter templates deployable beyond Vercel#51

Merged
alexkroman merged 41 commits into
mainfrom
feat/aai-dev
Jun 9, 2026
Merged

Add aai dev/share/deploy and make starter templates deployable beyond Vercel#51
alexkroman merged 41 commits into
mainfrom
feat/aai-dev

Conversation

@alexkroman

Copy link
Copy Markdown
Collaborator

Summary

New commands

  • aai dev — boot a scaffolded template's Procfile web process locally (live reload + browser open). Boots the same command hosts run, so every aai dev smoke-tests the deploy artifact.
  • aai share — same boot, exposed on a public *.trycloudflare.com URL via a cloudflared quick tunnel (cloudflared added to the Homebrew formula).
  • aai deploy — confirm-gated (y/N, --yes) deploy to Vercel (default, --prod), Railway (railway up, then railway domain for the URL), or Fly (fly launch). Requires the target CLI with an install hint; no formula deps for Railway/Fly.

Templates made portable beyond Vercel

  • Serve the frontend from static/ instead of the Vercel-reserved public/ (which Vercel drops from the Python lambda → FUNCTION_INVOCATION_FAILED). Regression guard added.
  • Run uvicorn via python -m uvicorn in the Procfile (fixes the Railway/Nixpacks venv console-script shebang crash).
  • Ship a Dockerfile (+ .dockerignore so .env isn't baked in, EXPOSE 8080) for Fly / Railway / Render-Docker / Cloudflare Containers.
  • Return a clear error when ASSEMBLYAI_API_KEY is unset instead of a cryptic Bearer/SDK failure.

Supporting

  • Shared boot helpers extracted to aai_cli/init/{devserver,procfile,tunnel}.py; runner.run_server/spawn.
  • scripts/docker_build_check.sh builds all three template images (verified locally).
  • All work passes ./scripts/check.sh (100% patch coverage, mutation gate, build + twine).

Note

This branch also carries in-progress work from a parallel session interleaved with the above — aai onboard --non-interactive and aai api design docs. Worth confirming/splitting before merge if you want a tightly-scoped PR.

🤖 Generated with Claude Code

alexkroman-assembly and others added 30 commits June 9, 2026 10:57
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each starter (audio-transcription, live-captions, voice-agent) now ships a
Procfile (`web: uvicorn api.index:app --host 0.0.0.0 --port ${PORT:-3000}`)
and runtime.txt (python-3.12), so a `git push` deploys to Render, Railway,
Heroku, and Cloud Run buildpacks with no typed start command. Vercel ignores
both. READMEs document the "Deploy elsewhere" path.

The init template contract gate (run by scripts/check.sh) now boots each
template's Procfile command for real and asserts it serves GET / with 200,
plus validates the runtime pin. Static mirrors added to the pytest contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Run the guided setup without prompts; defaults on when an agent/CI is
detected. Promotes output._is_agentic() to public is_agentic() now that
it's used cross-module (pyright strict rejects private cross-module use).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Curl-style authenticated passthrough to the AssemblyAI REST and LLM-gateway
APIs, driven by bundled OpenAPI specs. Bundled-only (no live fetch),
multi-spec/multi-host resolution via environments.active(), spec-driven auth,
keyring-only key, full Vercel-parity surface (passthrough + list + picker +
--show-code).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dev_command

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The contract gate boots the real Procfile but only curls GET /; the
install test just imports the module. Neither touches the /api/*
surface, so a route that imports fine but raises on every request
slips through. Add an in-process TestClient test that drives each
route for both outcomes — outbound call succeeds (assert the documented
200 body) and outbound call raises (assert the graceful 502 {"detail"}).
Covers all three template api/index.py files to 100%.

Also strip ASSEMBLYAI_BASE_URL in isolate_env so template-import
behavior doesn't vary with the dev's environment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…prompt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ploys don't crash

public/ is Vercel's reserved CDN output dir and is omitted from the Python lambda,
so StaticFiles(directory=public/static) raised at import -> FUNCTION_INVOCATION_FAILED.
Move assets to a bundled static/ dir FastAPI owns; add a contract test forbidding public/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ys don't crash on the venv console-script shebang

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
alexkroman-assembly and others added 8 commits June 9, 2026 14:17
…to surface the URL

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…to 'fly launch' when fly.toml is missing

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…der/Cloudflare-Containers build a working image

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…images

Scaffolds each template and runs docker build to prove the shipped Dockerfile
works (deps install, COPY layout, CMD). Self-skips without Docker; shellcheck-linted
by check.sh. Verified all 3 images build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he fly.toml preflight

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t matches the app

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ex checks type-narrow

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t instead of a cryptic Bearer/SDK failure

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
return probe.getsockname()[1]


def _terminate(proc: subprocess.Popen[str]) -> str:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading and returning raw subprocess stdout in _terminate can expose secrets or user-controlled output; avoid returning/embedding full process output or sanitize/truncate it before including in messages.

Details

✨ AI Reasoning
​The new helper reads the child process' stdout and returns it verbatim. Returning raw process output and later interpolating it into failure messages can expose secrets or other sensitive, user-controlled content emitted by that process. This was introduced in the PR (new _terminate function) and increases the risk of logging sensitive runtime output from spawned template processes.

🔧 How do I fix it?
Keep sensitive data such as emails, passwords, and tokens out of logs. When logging values tied to a user, prefer a safe identifier like a user ID over the raw input, and strip line breaks from any user-provided text you do log.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

alexkroman and others added 3 commits June 9, 2026 15:28
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- test_deploy_help_lists_flags: CI renders --help with ANSI color, which inserts
  escape codes mid-flag (--<ESC>...-fly), breaking the substring match. Strip ANSI
  before asserting.
- test_init_template_stream: _load_app booted keyless, so the new missing-API-key
  guard returned 500 instead of 502/200. Inject a dummy key (preserving a pre-set
  one), matching the other template suites. Was masked locally by the repo .env.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alexkroman alexkroman merged commit efbd2a1 into main Jun 9, 2026
11 checks passed
@alexkroman alexkroman deleted the feat/aai-dev branch June 9, 2026 23:12
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