CI & headless
BEEM_TOKEN, --json output and stable exit codes.
Every cloud command works without a terminal session, which makes "the deck lives in git, merging publishes it" a three-line workflow.
Auth
Set BEEM_TOKEN. It always outranks the credentials file, so a runner
image's leftover state can never shadow the token you injected. To get a
token: run beem login on any machine, copy the token field from the
credentials file it prints, and store that as a CI secret. Revoke it any
time in the studio's account panel.
--json
whoami, doctor, push, pull, publish, unpublish, logout and
mcp status accept --json: success prints exactly one JSON object on
stdout; failure leaves stdout empty and prints
{"error":{"code","message","hint"}} on stderr. (login stays interactive
by design — headless auth is what BEEM_TOKEN is for.)
Exit codes
Stable and safe to branch on:
| Code | Meaning |
|---|---|
0 | success |
1 | error |
2 | not signed in — token missing, revoked or expired |
3 | paid capability required |
4 | the cloud copy moved — pull, or push --force |
5 | the publish build failed |
Publish on merge (GitHub Actions)
name: Publish slides
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Push and publish
env:
BEEM_TOKEN: ${{ secrets.BEEM_TOKEN }}
run: |
npx --yes --package=@beem-slide/cli beem push slides/my-deck --force --json
npx --yes --package=@beem-slide/cli beem publish slides/my-deck --jsonTwo things make this loop stable:
- Run
beem pushonce from your machine first and commit.beem/cloud.json— that mapping is how CI updates the same deck instead of creating a new one per run. --forcemakes git the source of truth: CI overwrites edits made in the studio (the overwritten version stays recoverable in the deck's history). Drop it to instead fail the job with exit code4when the cloud copy has diverged.