Skip to content

Console commands

Audience: backend, devops
Status: implemented (CAP + Orchestrator shell)
Owns: backend
Depends on: CI/CD, System architecture, Backend stack ADR

CAP and Orchestrator expose a console CLI for one-off deploy tasks and manual VPS repairs. The pattern mirrors the existing seed runners (src/seed/cli.tsdist/seed/cli.js) but stays separate — seeds populate reference/demo data; console commands run idempotent backfills and repairs.

Trigger Where Notes
Deploy activate VPS activate.sh After prisma migrate deploy, before optional seed and before the current symlink flip
Manual SSH Release dir on VPS Same compiled entrypoint as activate
Local dev pnpm console … Runs via tsx src/console/cli.ts against local .env

Commands are not invoked from HTTP app boot (main.ts / PM2). Running on every process restart would race in cluster mode and repeat work unpredictably.

flowchart LR
Migrate["prisma migrate deploy"]
DeployCmd["node dist/console/cli.js --deploy"]
Seed["node dist/seed/cli.js optional"]
Symlink["symlink current + PM2"]
Migrate --> DeployCmd --> Seed --> Symlink

Implementation: monorepo infra/deploy/activate.sh. If dist/console/cli.js is missing (older artifact), activate logs a skip and continues.

Failure policy: any command error or non-zero exit fails the release — same as migrate and seed. The current symlink is not flipped and PM2 is not reloaded.

Each Prisma app that ships console code owns a thin duplicate under src/console/ (no shared @elimi/console package yet):

File Purpose
types.ts Command, CommandContext, exit codes
deploy-commands.ts Checked-in deploy list (DEPLOY_COMMANDS const array)
resolve-deploy-commands.ts Merge file list + process.env.DEPLOY_COMMANDS (comma-separated, trimmed, deduped)
kernel.ts Registry, argv parsing, Prisma lifecycle
cli.ts Entry: node dist/console/cli.js
commands/*.command.ts Domain commands (CAP only today)

Compiled output is included in the release tarball via existing tsc + pack.sh — no pack changes required.

Terminal window
node dist/console/cli.js # usage + list
node dist/console/cli.js list # registered commands
node dist/console/cli.js <name> [--dry-run]
node dist/console/cli.js --deploy [--dry-run]
  • --deploy runs the merged deploy list in order.
  • Empty merged list → no-op, exit 0 (Orchestrator default until commands are added).
  • --dry-run is supported per command where implemented; deploy passes the flag through.

Local equivalent:

Terminal window
pnpm --filter @elimi/cap console list
pnpm --filter @elimi/cap console repair:organisation-centres --dry-run
pnpm --filter @elimi/orchestrator console list

Two sources, merged and deduped:

  1. Checked in: apps/<app>/src/console/deploy-commands.ts
  2. Optional env: DEPLOY_COMMANDS=repair:organisation-centres,custom:once in shared/.env

Use the file for release-tagged backfills the team reviews in PR. Use env for environment-specific overrides without rebuilding (still merged with the file list).

Command Purpose
repair:organisation-centres Provision Centre + CentreStaff (SUPER_ADMIN) for completed organisation onboarding rows that never received founder staff

Logic:

  1. Find OnboardingRecord where persona = CENTRE, variant = organisation, status = COMPLETED, and no CentreStaff with role = SUPER_ADMIN for that capUserId.
  2. Per user (transaction): call provisionCentreFromOrganisationOnboarding — same helper used on live org submit and re-submit repair in onboarding service.
  3. Per-user errors (e.g. registration number collision) log and rethrow — fail-release.

Deploy list for the organisation-centres backfill release: deploy-commands.ts includes repair:organisation-centres. Clear to [] in a follow-up PR after staging/production verification.

Alternative before deploy: affected users can re-submit via POST /onboarding/centre/submit (repair path in onboarding service).

Orchestrator ships the same console framework. DEPLOY_COMMANDS stays empty by default (activate --deploy is a no-op until you add deploy entries).

Manual: mail:test — send a one-off probe via the configured EMAIL_PROVIDER / EMAIL_FROM (same adapters as auth OTP mail).

Terminal window
pnpm --filter @elimi/orchestrator console list
pnpm --filter @elimi/orchestrator console mail:test --to=you@yahoo.com --dry-run
pnpm --filter @elimi/orchestrator console mail:test --to=you@yahoo.com

With EMAIL_PROVIDER=console, send logs locally (no network). With smtp / resend, the provider must accept the message — that does not guarantee Yahoo inbox delivery (check spam + provider logs). Not on --deploy.

HTTP (temporary): POST /v1/dev/mail-test with header X-Mail-Test-Key — requires MAIL_TEST_HTTP_ENABLED=true and MAIL_TEST_HTTP_KEY in env. Disabled → 404. Set MAIL_TEST_HTTP_ENABLED=false when finished.

Terminal window
cd ~/elimi-ecosystem/cap_staging/current
set -a && source ../shared/.env && set +a
node dist/console/cli.js list
node dist/console/cli.js repair:organisation-centres --dry-run
node dist/console/cli.js --deploy

Optional env in shared/.env:

# Merged with deploy-commands.ts (deduped)
DEPLOY_COMMANDS=repair:organisation-centres
Seeds Console
Purpose Reference data, demo orgs, platform admin bootstrap One-off repairs and backfills
Trigger RUN_SEEDS=true on activate Always --deploy step when CLI exists
Entry dist/seed/cli.js dist/console/cli.js
Local pnpm prisma:seed / per-module seed scripts pnpm console …

Runbook detail: monorepo infra/deploy/README.md, apps/cap/README.md, apps/orchestrator/README.md.

  1. Add src/console/commands/<name>.command.ts implementing Command.
  2. Register in kernel.ts commands array.
  3. Add unit tests under test/unit/ (mock Prisma).
  4. For deploy-time execution: add the name to deploy-commands.ts or set DEPLOY_COMMANDS on the VPS — prefer clearing one-off entries after the backfill ships.
  5. Document behaviour here and in the relevant product/backend module page if user-visible.
  • Moving seed runners under console seed …
  • Shared @elimi/console npm package
  • Running commands on HTTP boot
  • LMS / WorkMasters console (add when those apps ship Prisma + activate)