Skip to content

Scripts & maintenance CLIs

All scripts run under Bun. Two conventions apply everywhere:

  • Destructive CLIs are dry-run by default and only act with --execute (dev:refresh, queue:admin, backfill-cfdi-encryption, enrich-viewports). There are no interactive prompts; each prints an env banner before acting.
  • Env loading depends on cwd: root scripts/* rely on Bun auto-loading .env.local from the repo root; package-local scripts are registered with --env-file=../../.env.local in their package.json, so always run them via bun run <script-name> from the package directory.

bun run dev:refresh (scripts/dev-refresh.ts) resets the entire dev environment in one command:

OrderStepWhat happens
1streamFull sweep — hard-deletes ALL GetStream messaging channels, then all users (app is dev-dedicated)
2knockFull sweep — deletes ALL Knock users in the environment (workflows/layouts untouched)
3stripeDeletes ONLY customers referenced by the local DB — the test account is shared with staging, so there is no sweep mode by design; products/prices are never touched
4facturapiCancels test CFDIs (motive 02), then deletes customers referenced by the local DB
5storageDeletes the R2 app-upload prefixes (properties/, avatars/, agent-avatars/, teams/, floor_plans/) — never search-seeds/, the Meili restore artifacts
6dbRuns bun run db:reset in packages/database as a subprocess (drop schema public + migrate)
7valkeyFLUSHDB on db0 (cache + psearch:*), then obliterates the six suffixed BullMQ queues on db1 (never FLUSHDB there)
8meiliDeletes the locations / service_areas / agents indexes; the worker’s ensureSearchIndexes() restores them at boot

Flags: --execute (required to act), --only <csv>, --skip-<step>, --allow-remote-db (needed on the Dokploy dev stack — Postgres/Valkey/Meili hosts aren’t localhost), --allow-unsuffixed, --keep-going.

Guards: refuses under NODE_ENV=production, with a non-sk_test_ Stripe or Knock key, an sk_live_ FacturAPI key, or a data-store host that is not provably local without the flag. Note the FacturAPI check is a denylist (sk_live_), not the allowlist Stripe and Knock get — a key in neither format passes. A DATABASE_URL / VALKEY_URL / MEILISEARCH_HOST that is unset is skipped, but one that is set and unparsable (or scheme-less, which parses to an empty host) is treated as non-local and blocked. Because Stream keys and bucket names carry no dev/prod marker, --execute additionally requires the sweep-target pins in .env.local: DEV_REFRESH_STREAM_KEY must match PUBLIC_GETSTREAM_API_KEY and DEV_REFRESH_BUCKET must match AWS_S3_BUCKET_NAME — a production env copied into .env.local fails the match instead of being swept. External ids are collected before the schema drop, and recorded external failures abort before the DB is touched — unless --keep-going, and excepting CFDI cancellation failures, which are warnings by design (a stamped CFDI that refuses cancellation can’t be fixed by re-running) and so do not block the drop. An orphan-risk guard refuses to drop the DB while Stripe/FacturAPI are unconfigured but the DB still references their objects — skipping those steps silently would strand the external records forever.

After an execute run: restart the worker (obliterate removed the repeatable schedulers; boot re-registers them and reseeds Meilisearch). There is no seed script — everything starts empty.

CommandPurpose
bun run db:resetDrop schema public, rerun the ordered Kysely migration chain (scripts/db-reset.ts). Postgres only — pair with dev:refresh for the full environment
bun run db:migrateRun pending Kysely migrations to latest (scripts/migrate.ts)
bun run db:codegenRegenerate src/generated/db.ts from the live DB (kysely-codegen)
bun run scripts/check-enum-array-parser.tsVerify the "UserRole"[] enum-array node-pg parser fix (roles must decode as string[])
CommandPurpose
bun --env-file=../../.env.local run queue:admin <cmd>Operational queue CLI — counts, inspect, retry-failed, promote, DLQ replay. See Incident guides
bun run backfill:searchEnqueue sync-agent / sync-team for all public agents + teams → rebuilds the Meili agents index
bun run backfill:propertiesEnqueue sync-property for every ACTIVE property → rebuilds psearch:*
bun run backfill:main-imagesOne-off repair: promote a cover image for properties with photos but no isMain row
bun run search:schemaProperty-search schema migration command (--dry-run / --enqueue / --verify)

Backfills only enqueue — a running worker does the work.

CommandPurpose
bun run sync:locationsWipe + rebuild the Meili locations index from the SEPOMEX XML (not in git — slow path; prefer the S3 seed restore)
bun run sync:service-areasSame wipe-and-rebuild for service_areas
bun run export:search-seedExport locations + service_areas to gzipped NDJSON seeds in R2 (search-seeds/) — what ensureSearchIndexes() restores from
bun --env-file=../../.env.local run scripts/enrich-viewports.tsRe-geocode _geo + viewport bounds via Google Geocoding (--execute gated)
CommandPurpose
bun run scripts/sync-native-i18n.tsFan the shared i18n/{user,agent}/{en,es}.json catalogs out to the four native apps
bun design-tokens/export-native-tokens.mjsRegenerate Tokens.swift / Tokens.kt from design-tokens/tokens.cjs — commit the output
bun run packages/contract/scripts/codegen-models.tsRegenerate the native Swift/Kotlin DTOs from the @repo/contract Zod schemas
bun run scripts/contract-fixtures/generate-fixtures.tsCapture real tRPC response envelopes into committed golden fixtures for the native apps
cd packages/contract && bun run fixtures:validateParse each committed fixture through its authoritative Zod schema — fails on contract drift
CommandPurpose
bun run scripts/check-job-data-casts.tsEnforce that BullMQ processors Zod-parse job.data instead of type-asserting (FSR-019/086)
bun run scripts/native-core-drift/check.tsDetect unported Core/ fixes across the four copy-and-adapt native apps
bun run docs:checkBuild the docs site, then run docs-stale-check.ts (stale references) + docs-link-check.ts (broken relative links)
bun run scripts/generate-email-previews.tsRegenerate the static email-templates/ previews from the real email builders — commit the output
CommandPurpose
packages/api/scripts/parity-*.ts (14 scripts)Behavior + atomicity tests for the Kysely-migrated services — run against a scratch DB (they write and clean up): DATABASE_URL=<scratch> bun run scripts/parity-leads.ts
bun run packages/api/scripts/backfill-cfdi-encryption.tsEncrypt pre-FSR-014 plaintext fiscal PII rows in place (--execute gated)
bun run packages/server-services/scripts/backfill-inquiry-chat-channels.tsOne-off backfill of Inquiry.chatChannelId from derived GetStream channel ids (migration 0012)
bun run scripts/firebase-distribute.tsBuild + upload a signed native app artifact to Firebase App Distribution. See Release playbooks