- name
- byoa-deploy-vercel
- description
- Deploy the BYOA wake-receiver Vercel Function from `deployment/vercel/`. Reads `.env.byoa`, pushes the operator's required env to the Vercel project, deploys via `npx vercel`, health-checks the URL, logs the operator in, and registers the resulting alias as the ship's `source_url` via `ship_byoa_configure`. Picks up from `/byoa-link` (which claims the ship and writes `.env.byoa`). Usage `/byoa-deploy-vercel [env]` (env defaults to `prod`; `live` is an accepted synonym for `prod`).
# BYOA: deploy Vercel wake function
> **env defaults to `prod` when omitted.** `live` is accepted as a user-facing synonym for `prod` (e.g. `/byoa-deploy-vercel live` is equivalent to `/byoa-deploy-vercel prod` and to `/byoa-deploy-vercel` with no arg). `local` is the only other valid value.
Picks up where `/byoa-link` finishes. The operator's ship is already claimed as BYOA, `.env.byoa` is populated with `BYOA_CHARACTER_ID` / `BYOA_SHIP_ID` / `BYOA_WAKE_SECRET`, and the per-ship wake secret is registered server-side. This skill takes the template Vercel Function at [deployment/vercel/](../../../deployment/vercel/) and walks the operator through deploying their own copy.
End state: a Vercel deployment at `https://<their-project>.vercel.app/api/wake` that auths inbound wakes against `BYOA_WAKE_SECRET` and spawns a persistent `@vercel/sandbox` running `uv run byoa` per wake. The skill prints a ready-to-run `ship_byoa_configure set { source_url }` curl so the operator can point their ship at it.
## Parameters
```
/byoa-deploy-vercel [env] [--preview] [--no-link] [--out-url <path>] [--access-token <jwt>] [--skip-register]
```
- **env**: `prod` (default, also accepts `live` as a synonym) or `local`. Picks the game server endpoint that `/login` + `/ship_byoa_configure` are called against (the auto-register step at the end):
- `prod` / `live` → `https://api.gradient-bang.com` (hardcoded; operator never types it; no env file needed). If the operator types nothing, this is what they get.
- `local` → sources `SUPABASE_URL` from `.env.supabase` (e.g. `http://127.0.0.1:54321` when `npx supabase start` is running)
The `dev` env was dropped — it required internal-only env files that external developers don't have. The skill no longer needs `EDGE_API_TOKEN` either; `ship_byoa_configure` and `my_corporation` authenticate on the user JWT alone via the standard `getAuthenticatedUser()` pattern (`Authorization: Bearer <jwt>`).
- **--preview**: deploy as a Vercel preview instead of production. **Almost never what you want for BYOA**: Vercel's default Standard Protection gates preview URLs behind SSO, so the game server can't reach them. Use only if you've disabled Project Protection or are testing with a bypass token.
- **--no-link**: skip the link verification (steps 3a + 3b). Use only when you're certain `deployment/vercel/.vercel/project.json` is correct.
- **--out-url**: write the final canonical (alias) URL to this file. Defaults to stdout only.
- **--access-token <jwt>**: skip the email+password prompt in step 8 by reusing an existing user JWT (e.g. one just minted by `/byoa-link`). Token must be valid for the same operator who owns the ship.
- **--skip-register**: stop after the health-check; don't auto-register `source_url`. Falls back to printing a manual curl. Use when the operator wants to inspect the deployment first or doesn't have credentials handy.
**Default deploy target is production.** The BYOA wake endpoint must be publicly reachable by the game server, and only the production alias (`<projectName>.vercel.app`) is exempt from Vercel's Standard Protection. Per-deploy URLs (`<projectName>-<hash>-<team>.vercel.app`) are SSO-gated regardless of target.
**Tested with Vercel CLI 54.x.**
**Game tool calls route through the bus, not direct HTTP.** The BYOA harness running in the Vercel sandbox never calls the game server directly — it publishes `BusGameToolCallRequest` messages onto the Postgres bus, and the bot-side `Orchestrator` broker forwards them to Supabase using the bot's own `SUPABASE_URL`. wake_agent's injected env (`BYOA_BUS_DATABASE_URL`, `BYOA_CHANNEL`, `BYOA_SHIP_ID`, `BYOA_CHARACTER_ID`, `BYOA_TASK_ID`, `BYOA_WAKE_REQUEST_ID`) contains no game-server URL on purpose. Practical implication: for local-stack testing against a Vercel-deployed receiver, **only the Postgres bus needs to be publicly reachable** (e.g. via `ngrok tcp 54322` + `BYOA_BUS_DATABASE_URL` set in `.env.supabase`). No second tunnel for the game server is required. See `runtime/orchestrator.py` `_on_game_tool_call_request` for the broker side.
## Pre-flight
Stop with a clean error if any of these is true:
- `.env.byoa` does not exist in cwd → direct the operator to run `/byoa-link` first.
- `.env.byoa` is missing any required key: `BYOA_WAKE_SECRET`, `BYOA_SHIP_ID`, `BYOA_CHARACTER_ID`, `TASK_LLM_PROVIDER`, `TASK_LLM_MODEL`, and the API key matching the provider (one of `ANTHROPIC_API_KEY` / `GOOGLE_API_KEY` / `OPENAI_API_KEY` / `MINIMAX_API_KEY`). **Check by sourcing the file first (`set -a && source .env.byoa && set +a`), then verifying each required var is non-empty (`[ -n "$BYOA_WAKE_SECRET" ]`, etc.). Do NOT grep the raw file — commented-out lines like `# BYOA_PROMPT=...` will match a naive `grep BYOA_PROMPT=` and produce false "is set" readings.** Operators routinely keep commented-out alternates in `.env.byoa`; `source` correctly skips them, so trust the resulting env, not the file's text.
- [deployment/vercel/](../../../deployment/vercel/) does not exist or is missing `api/wake.ts`, `package.json`, or `vercel.json` — the template was deleted or this skill is being run against the wrong checkout.
- `npx vercel --version` errors (Vercel CLI not available via npx).
- For `env=local`: `.env.supabase` is missing or `SUPABASE_URL` is unset inside it.
Resolve which API key is required from `TASK_LLM_PROVIDER`:
| Provider | Required key |
|---|---|
| `anthropic` | `ANTHROPIC_API_KEY` |
| `google` | `GOOGLE_API_KEY` |
| `openai` | `OPENAI_API_KEY` |
| `minimax` | `MINIMAX_API_KEY` |
If both `BYOA_PROMPT` and `BYOA_PROMPT_FILE` are unset, mention that the agent will fall back to the bundled default prompt at [deployment/vercel/prompt.md](../../../deployment/vercel/prompt.md) (auto-loaded by `wake.ts` on every wake — operators can edit it pre-deploy, fork the deploy template, or override via `BYOA_PROMPT` / `BYOA_PROMPT_FILE` on the Vercel project env).
## Steps
### 1. Load envs
```bash
set -a && source .env.byoa && set +a
```
Then resolve `SUPABASE_URL` per the env arg. If the operator typed nothing, treat it as `prod`. `live` is accepted as a user-facing synonym for `prod`:
```bash
case "${ENV_ARG:-prod}" in
prod|live) SUPABASE_URL=https://api.gradient-bang.com ;;
local) set -a && source .env.supabase && set +a ;;
*) echo "unknown env: ${ENV_ARG}; expected prod | live | local"; exit 1 ;;
esac
# SUPABASE_URL now points at api.gradient-bang.com (prod/live) or http://127.0.0.1:54321 (local)
```
All curls in step 8 append `/functions/v1/<endpoint>`, so `SUPABASE_URL` must NOT include `/functions/v1`. Never echo `BYOA_WAKE_SECRET` or any `*_API_KEY` to the console.
### 2. Verify Vercel CLI login
```bash
npx vercel whoami
```
If this prints "Not authenticated" (or errors), tell the operator to run `npx vercel login` in their shell and re-run the skill. Do not try to log them in for them.
### 3. Verify (or set up) the Vercel link
The canonical link location is `deployment/vercel/.vercel/project.json`. All subsequent `vercel` commands must be run from `deployment/vercel/` so they pick up the right link.
Skip-able if the link is already in place; otherwise the skill **must stop** until the operator establishes it — `vercel link` is interactive (scope / project name / create-new prompts) and operator-specific. Do not pass `--yes`.
**3a. Check for a misplaced `.vercel/` at the repo root or `deployment/`.**
```bash
ls .vercel/project.json deployment/.vercel/project.json 2>/dev/null
```
If either prints, a previous `vercel link` was run from the wrong cwd. Bail with the exact remediation, pasted ready-to-run, and **stop**:
```bash
# If the existing link is for this BYOA project, move it to the right place:
mkdir -p deployment/vercel
mv deployment/.vercel deployment/vercel/.vercel # or: mv .vercel deployment/vercel/.vercel
# If the link is for a different project (or you want a fresh start), remove it:
rm -rf .vercel deployment/.vercel
```
Do not auto-move or auto-delete — the operator may have a legitimate `.vercel/` for some other project in the repo. Let them pick.
**3b. Confirm the canonical link exists.**
```bash
ls deployment/vercel/.vercel/project.json 2>/dev/null
```
If present → linked, proceed to step 4.
If missing → **stop** and surface this exactly:
```bash
cd deployment/vercel && npx vercel link
```
Tell the operator: run that in their shell, follow the prompts (suggested project name: `gradient-bang-byoa-<their-handle>`), then re-run `/byoa-deploy-vercel`. The interactive prompts (scope / "link to existing?" / project name) can't be driven from this skill — they must run it themselves.
### 4. Push project env to Vercel
Push the values from `.env.byoa` to the **production** Vercel environment only. The wake function is invoked through the production alias (`<projectName>.vercel.app`) and preview/development environments are never reached by the game server, so syncing env vars to them is pure noise. Use the `rm`-then-`add` pattern so re-runs are idempotent.
All `vercel` invocations use `--cwd deployment/vercel` instead of `cd deployment/vercel && ...` — that way the snippet runs the same whether or not the caller wraps it in a subshell, and accidental cwd drift can't bleed into later steps. **Keep `--cwd` if you edit these commands** — chained `cd` patterns silently move the Bash-tool cwd and break later steps (the operator-side curl in step 8 tries to source `.env.supabase` from the repo root).
First resolve the LLM-provider API key name + value from `TASK_LLM_PROVIDER` in one shot (resolving both here avoids `${!VAR}` indirect expansion, which works in bash but not zsh — and zsh is the default macOS shell):
```bash
case "$TASK_LLM_PROVIDER" in
anthropic) API_KEY_NAME=ANTHROPIC_API_KEY; API_KEY_VALUE="$ANTHROPIC_API_KEY" ;;
google) API_KEY_NAME=GOOGLE_API_KEY; API_KEY_VALUE="$GOOGLE_API_KEY" ;;
openai) API_KEY_NAME=OPENAI_API_KEY; API_KEY_VALUE="$OPENAI_API_KEY" ;;
minimax) API_KEY_NAME=MINIMAX_API_KEY; API_KEY_VALUE="$MINIMAX_API_KEY" ;;
*) echo "Unknown TASK_LLM_PROVIDER=$TASK_LLM_PROVIDER"; exit 1 ;;
esac
```
Then push every key to production. The function reads named globals (`KEY` / `VAL`) instead of positional bash args — the slash-command loader substitutes literal positional refs (dollar-N) inside fenced code blocks with the skill's command-line args, which silently corrupts shell snippets. Don't use bash positional parameters in skill snippets — use named env vars on the call line instead, as below:
```bash
push_env() {
[ -z "$VAL" ] && return
npx vercel --cwd deployment/vercel env rm "$KEY" production --yes >/dev/null 2>&1 || true
if npx vercel --cwd deployment/vercel env add "$KEY" production --value "$VAL" --yes >/dev/null 2>&1; then
echo " ✓ $KEY → production"
else
echo " ✗ $KEY → production FAILED"
fi
}
# Required
KEY=BYOA_WAKE_SECRET VAL="$BYOA_WAKE_SECRET" push_env
KEY=TASK_LLM_PROVIDER VAL="$TASK_LLM_PROVIDER" push_env
KEY=TASK_LLM_MODEL VAL="$TASK_LLM_MODEL" push_env
KEY="$API_KEY_NAME" VAL="$API_KEY_VALUE" push_env
# Optional — only push when set
[ -n "$BYOA_PROMPT" ] && KEY=BYOA_PROMPT VAL="$BYOA_PROMPT" push_env
[ -n "$BYOA_PROMPT_FILE" ] && KEY=BYOA_PROMPT_FILE VAL="$BYOA_PROMPT_FILE" push_env
[ -n "$TASK_LLM_THINKING_BUDGET" ] && KEY=TASK_LLM_THINKING_BUDGET VAL="$TASK_LLM_THINKING_BUDGET" push_env
[ -n "$BYOA_TOOL_CALL_TIMEOUT_SECONDS" ] && KEY=BYOA_TOOL_CALL_TIMEOUT_SECONDS VAL="$BYOA_TOOL_CALL_TIMEOUT_SECONDS" push_env
[ -n "$BYOA_AGENT_IDLE_TEARDOWN_SECONDS" ] && KEY=BYOA_AGENT_IDLE_TEARDOWN_SECONDS VAL="$BYOA_AGENT_IDLE_TEARDOWN_SECONDS" push_env
```
Note on `--value`: this puts the secret in the process command line, briefly visible to other local users via `ps aux`. Acceptable on a developer machine; if pushing on shared infrastructure use `vercel env pull` + manual edit instead.
If `vercel env rm` complains "Environment Variable was not found" on first run, that's fine — `|| true` swallows it. The real failure mode is an `action_required` JSON blob from the `add` step; if you see one, surface the message and stop. **Do not** echo `BYOA_WAKE_SECRET` or API keys to the console in error reporting.
### 5. Deploy and capture the canonical alias
Default to production (see "Default deploy target" note above). Tee the output so we can grep for the alias line. Same `--cwd` rule as step 4 — don't replace with chained `cd` patterns, even in pipelines:
```bash
npx vercel --cwd deployment/vercel deploy --prod --yes 2>&1 | tee /tmp/byoa-deploy.log
```
For `--preview` (rarely correct for BYOA), drop `--prod` — but warn the operator the resulting URL will be SSO-gated.
After the deploy, capture **two** URLs:
```bash
# Per-deploy URL (immutable, SSO-gated under Standard Protection — not useful for source_url)
DEPLOY_URL=$(grep -oE 'https://[a-z0-9-]+\.vercel\.app' /tmp/byoa-deploy.log | head -1)
# Canonical alias (public for prod, the URL the game server actually hits).
# Use `cut`, not awk's positional-field operator — the loader rewrites literal
# dollar-N tokens in this file with the slash-command args, which would mangle
# any awk field reference inside a fenced code block.
ALIAS_URL=$(grep -oE 'Aliased: https://[^ ]+' /tmp/byoa-deploy.log | head -1 | cut -d' ' -f2)
# Fallback: construct from project.json if the Aliased: line is missing (e.g. on preview deploys)
if [ -z "$ALIAS_URL" ]; then
PROJECT_NAME=$(grep -o '"projectName":"[^"]*"' deployment/vercel/.vercel/project.json | cut -d'"' -f4)
ALIAS_URL="https://${PROJECT_NAME}.vercel.app"
fi
```
On non-zero exit from `vercel deploy`, surface the CLI's error and stop.
**Use `ALIAS_URL` for everything downstream (health-check, smoke-test, source_url).** The per-deploy `DEPLOY_URL` will 401 on any unauthenticated request because Vercel's Standard Protection gates it behind SSO — even when the function itself is wired correctly. Hitting it during health-checks is the single biggest source of false-negative deployment reports.
Voir sur GitHub