Commands Reference
All development scripts and commands for working on Beai.
Development
# Run all dev servers (turbo)
pnpm dev
# Run only the web app
pnpm --filter @workspace/web dev
# Run only the worker
pnpm --filter @workspace/worker devBuilding
# Build all packages
pnpm build
# Build only the web app
pnpm --filter @workspace/web build
# Type check every package
pnpm turbo check-typesTesting
# Run the unit tests across every package
pnpm test
# Run the tests for scripts/, which sits outside the workspace
pnpm test:scriptsStorybook stories double as browser tests. They run in Vitest's browser mode, so they need a browser installed before the first run:
pnpm --filter @workspace/web exec playwright install chromium
pnpm --filter @workspace/web test:storybookThe analytics stats are checked against a real Postgres. These tests migrate the
database in TEST_DATABASE_URL and write to it, so they refuse to run unless it
points at a local database whose name ends in _test:
createdb elmo_test
TEST_DATABASE_URL=postgres://postgres:postgres@localhost:5432/elmo_test \
pnpm --filter @workspace/web test:integrationEnd-to-End Tests
.github/workflows/e2e.yaml is the source of truth for E2E orchestration — it
stands up one Dockerized container per deployment mode, seeds them, and runs the
suites in order. Replay the whole thing locally with
act:
pnpm test:e2e-localThe scripts below each run one mode's suite against a stack that workflow has already brought up, which is what you want when iterating on a single spec:
# Playwright specs
pnpm test:e2e
pnpm test:e2e:cloud
pnpm test:e2e:whitelabel
pnpm test:e2e:demo
# Bruno API suites
pnpm test:api
pnpm test:api:cloud
pnpm test:api:demoCode Quality
# Check formatting, lint and import order — the same check CI runs
pnpm lint
# Fix everything Biome can fix on its own
pnpm lint:fix
# Format code with Biome
pnpm format
# Find unused exports and dependencies
pnpm knip
# Lint the hand-written OpenAPI document
pnpm lint:spec
# Check every dependency's license against the MIT-compatible allow-list
pnpm license-checkAlways run pnpm format before committing to ensure consistent code style across the codebase.
Database
drizzle.config.ts and the migrations live in packages/lib, so Drizzle runs
against that package:
# Generate a new migration after schema changes
pnpm --filter @workspace/lib exec drizzle-kit generate
# Run pending migrations
pnpm --filter @workspace/lib exec drizzle-kit migrateApply schema changes with drizzle-kit migrate only. See
Architecture for why drizzle-kit push is unsafe against a deployment.
Component Stories
# Start the Storybook dev server on port 6006
pnpm --filter @workspace/web storybook
# Build the static Storybook
pnpm --filter @workspace/web storybook:buildCLI Development
# Run CLI directly from source
pnpm elmo <command>
# Build and link CLI globally for testing
pnpm cli:link
# Unlink global CLI
pnpm cli:unlinkDocker
# Build Docker images locally
elmo compose build
# Build without cache
elmo compose build --no-cache
# Build only web or worker
elmo compose build web
elmo compose build worker
# Run other docker compose commands
elmo compose up -d
elmo compose ps
elmo compose logs -f web
elmo compose downVersioning
# Create a changeset (do this before every PR)
pnpm changeset
# Apply changesets and bump versions (maintainers only)
pnpm version-packagesTurborepo
The monorepo uses Turborepo for parallel task execution and caching. Most commands are defined in turbo.json and can be run from the repo root via pnpm <script>.
To run a command for a specific package:
pnpm --filter @workspace/<package-name> <command>Query fan-out access
Query fan-out is an organization add-on, disabled by default in every deployment mode, including WorkOS/local deployments. Platform admins follow the same flag; there is no admin bypass. Everyone in the organization shares its access.
After applying the database migrations through your normal deployment process,
run this command from the repository root. It loads .env; an existing
DATABASE_URL environment variable takes precedence. Check that it names the
intended database before changing customer access.
# Enable for an agency that has purchased the add-on, or your development org
pnpm set-query-fanout --org default --enabled true
# Disable to see the clinic experience, or revoke the add-on
pnpm set-query-fanout --org default --enabled false--org accepts an organization ID or URL slug. The command creates its settings
row if needed and preserves billing settings. It changes only
organization_settings.query_fanout_enabled; it does not charge the customer.
Pricing and checkout must be agreed before launching paid access.
Reload the app after a toggle to refresh navigation. Server checks use the current flag on each request. When disabled, the fan-out page and data endpoints are blocked, Web Queries are hidden in prompt history and responses, and raw provider payloads are withheld because they can contain embedded searches. Answer text, mentions, and citations remain available. Collection and stored history continue, so enabling the add-on also makes existing query history available.
LLM settings access
The LLMs settings page is disabled by default for every organization, including organizations created on a new Google sign-in. Platform admins follow the same flag. Clients cannot enable it themselves.
From the repository root, use the backend command to grant access only to an organization that should manage its platform choices:
pnpm set-llm-settings --org default --enabled true
pnpm set-llm-settings --org default --enabled falseLike set-query-fanout, this accepts an organization ID or URL slug and loads
.env. It updates only organization_settings.llm_settings_enabled, preserving
query fan-out access and billing settings. Reload the app after toggling.
When disabled, the sidebar link is hidden, direct page access is blocked, and model-picker reads and saves are rejected server-side. Onboarding skips platform selection and uses the configured defaults; manually supplied platform choices at brand creation are also rejected. Existing tracking keeps running unchanged.
Was this page helpful?