2026-04-01 23:54:14 -07:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
API_DIR="$ROOT/@applications/api"
|
|
|
|
|
WEB_DIR="$ROOT/@applications/web"
|
|
|
|
|
AI_DIR="$HOME/Code/@applications/@ai/services/ai-core"
|
|
|
|
|
|
|
|
|
|
# shellcheck source=/dev/null
|
|
|
|
|
source "${HOME}/Code/@packages/@ts/@cli/bash-templates/lib/colors.sh"
|
|
|
|
|
C=$CYAN G=$GREEN Y=$YELLOW M=$MAGENTA B=$BOLD R=$NC D=$DIM
|
|
|
|
|
|
|
|
|
|
cmd_help() {
|
|
|
|
|
echo -e "${B}companion${R} — unified project runner\n"
|
|
|
|
|
echo -e "${C}${B}Dev Workflow${R}"
|
|
|
|
|
echo -e " ${G}dev${R} Start API + web ${D}pnpm dev${R}"
|
|
|
|
|
echo -e " ${G}dev:all${R} Start @ai + API + web ${D}all 3 services${R}"
|
|
|
|
|
echo -e " ${G}dev:ai${R} Start @ai service only ${D}nest start --watch${R}"
|
|
|
|
|
echo -e " ${G}dev:api${R} Start API only ${D}nest start --watch${R}"
|
|
|
|
|
echo -e " ${G}dev:web${R} Start web only ${D}vite${R}"
|
|
|
|
|
echo -e " ${G}build${R} Build all packages ${D}pnpm -r build${R}"
|
|
|
|
|
echo -e " ${G}typecheck${R} Type-check everything ${D}pnpm -r typecheck${R}"
|
|
|
|
|
echo -e " ${G}lint${R} Lint all packages ${D}pnpm -r lint${R}"
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${C}${B}Testing${R}"
|
|
|
|
|
echo -e " ${G}test${R} Unit tests ${D}pnpm -r test${R}"
|
|
|
|
|
echo -e " ${G}test:e2e${R} Playwright E2E tests ${D}playwright test${R}"
|
2026-04-08 21:29:50 -07:00
|
|
|
echo -e " ${G}e2e:docker${R} Docker E2E stack ${D}docker compose e2e${R}"
|
2026-04-01 23:54:14 -07:00
|
|
|
echo ""
|
|
|
|
|
echo -e "${C}${B}Docker${R}"
|
|
|
|
|
echo -e " ${G}docker${R} Start Postgres + Redis ${D}docker compose up -d${R}"
|
|
|
|
|
echo -e " ${G}docker:stop${R} Stop Postgres + Redis ${D}docker compose down${R}"
|
|
|
|
|
echo -e " ${G}docker:status${R} Show container status ${D}docker compose ps${R}"
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${C}${B}Database${R}"
|
|
|
|
|
echo -e " ${G}db:migrate${R} Run dev migrations ${D}typeorm migration:run${R}"
|
|
|
|
|
echo -e " ${G}db:generate${R} ${Y}<name>${R} Generate migration ${D}typeorm migration:generate${R}"
|
|
|
|
|
echo -e " ${G}db:reset${R} Drop + recreate dev database ${D}dropdb + createdb${R}"
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${M}Usage:${R} ./run <command> [args...]"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "${1:-help}" in
|
|
|
|
|
|
|
|
|
|
# Dev workflow
|
|
|
|
|
dev)
|
|
|
|
|
echo -e "${C}Starting companion-api + companion-web...${R}"
|
|
|
|
|
cd "$ROOT" && pnpm dev
|
|
|
|
|
;;
|
|
|
|
|
dev:all)
|
|
|
|
|
echo -e "${C}Starting @ai + companion-api + companion-web...${R}"
|
|
|
|
|
# Start docker infra if not running
|
|
|
|
|
if ! docker ps --filter "name=lilith-companion-postgres" --format '{{.Status}}' | grep -q "Up"; then
|
|
|
|
|
echo -e "${Y}Starting docker infra...${R}"
|
|
|
|
|
cd "$ROOT/@deployments" && docker compose up -d
|
|
|
|
|
sleep 3
|
|
|
|
|
fi
|
|
|
|
|
if ! docker ps --filter "name=lilith-ai-postgres" --format '{{.Status}}' | grep -q "Up"; then
|
|
|
|
|
echo -e "${Y}Starting @ai docker infra...${R}"
|
|
|
|
|
cd "$HOME/Code/@applications/@ai" && docker compose up -d
|
|
|
|
|
sleep 3
|
|
|
|
|
fi
|
|
|
|
|
# Start all 3 services
|
|
|
|
|
trap 'kill 0' EXIT
|
|
|
|
|
(cd "$AI_DIR" && pnpm start:dev) &
|
|
|
|
|
sleep 3
|
|
|
|
|
(cd "$API_DIR" && pnpm dev) &
|
|
|
|
|
(cd "$WEB_DIR" && pnpm dev) &
|
|
|
|
|
echo -e "${G}All services starting...${R}"
|
|
|
|
|
echo -e " @ai: http://localhost:3790"
|
|
|
|
|
echo -e " companion-api: http://localhost:3850"
|
|
|
|
|
echo -e " companion-web: http://localhost:5850"
|
|
|
|
|
echo -e "${D}Press Ctrl+C to stop all${R}"
|
|
|
|
|
wait
|
|
|
|
|
;;
|
|
|
|
|
dev:ai)
|
|
|
|
|
echo -e "${C}Starting @ai service...${R}"
|
|
|
|
|
cd "$AI_DIR" && pnpm start:dev
|
|
|
|
|
;;
|
|
|
|
|
dev:api)
|
|
|
|
|
echo -e "${C}Starting companion-api...${R}"
|
|
|
|
|
cd "$API_DIR" && pnpm dev
|
|
|
|
|
;;
|
|
|
|
|
dev:web)
|
|
|
|
|
echo -e "${C}Starting companion-web...${R}"
|
|
|
|
|
cd "$WEB_DIR" && pnpm dev
|
|
|
|
|
;;
|
|
|
|
|
build)
|
|
|
|
|
echo -e "${C}Building all packages...${R}"
|
|
|
|
|
cd "$ROOT" && pnpm build
|
|
|
|
|
;;
|
|
|
|
|
typecheck)
|
|
|
|
|
echo -e "${C}Type-checking...${R}"
|
|
|
|
|
cd "$ROOT" && pnpm typecheck
|
|
|
|
|
;;
|
|
|
|
|
lint)
|
|
|
|
|
echo -e "${C}Linting...${R}"
|
|
|
|
|
cd "$ROOT" && pnpm lint
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
# Testing
|
|
|
|
|
test)
|
|
|
|
|
echo -e "${C}Running unit tests...${R}"
|
|
|
|
|
cd "$ROOT" && pnpm test
|
|
|
|
|
;;
|
|
|
|
|
test:e2e)
|
|
|
|
|
shift
|
|
|
|
|
echo -e "${C}Running E2E tests...${R}"
|
|
|
|
|
cd "$ROOT/@tooling/e2e" && npx playwright test "$@"
|
|
|
|
|
;;
|
2026-04-08 21:29:50 -07:00
|
|
|
e2e:docker)
|
|
|
|
|
echo -e "${C}Running E2E tests in Docker...${R}"
|
|
|
|
|
cd "$ROOT" && docker compose \
|
|
|
|
|
-f "@tooling/e2e/docker-compose.e2e.yml" \
|
|
|
|
|
up --build --abort-on-container-exit --exit-code-from e2e-tests
|
|
|
|
|
;;
|
2026-04-01 23:54:14 -07:00
|
|
|
|
|
|
|
|
# Docker (local containers)
|
|
|
|
|
docker)
|
|
|
|
|
echo -e "${C}Starting Postgres + Redis...${R}"
|
|
|
|
|
cd "$ROOT/@deployments" && docker compose up -d
|
|
|
|
|
;;
|
|
|
|
|
docker:stop)
|
|
|
|
|
echo -e "${C}Stopping containers...${R}"
|
|
|
|
|
cd "$ROOT/@deployments" && docker compose down
|
|
|
|
|
;;
|
|
|
|
|
docker:status)
|
|
|
|
|
cd "$ROOT/@deployments" && docker compose ps
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
# Database
|
|
|
|
|
db:migrate)
|
|
|
|
|
echo -e "${C}Running database migrations...${R}"
|
|
|
|
|
cd "$API_DIR"
|
|
|
|
|
if [ -f "$ROOT/.env" ]; then
|
|
|
|
|
set -a; source "$ROOT/.env"; set +a
|
|
|
|
|
fi
|
|
|
|
|
pnpm migration:run
|
|
|
|
|
;;
|
|
|
|
|
db:generate)
|
|
|
|
|
if [[ -z "${2:-}" ]]; then
|
|
|
|
|
echo -e "${Y}Usage:${R} ./run db:generate <migration-name>"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
cd "$API_DIR"
|
|
|
|
|
if [ -f "$ROOT/.env" ]; then
|
|
|
|
|
set -a; source "$ROOT/.env"; set +a
|
|
|
|
|
fi
|
|
|
|
|
pnpm migration:generate "src/migrations/$2"
|
|
|
|
|
;;
|
|
|
|
|
db:reset)
|
|
|
|
|
if [ -f "$ROOT/.env" ]; then
|
|
|
|
|
set -a; source "$ROOT/.env"; set +a
|
|
|
|
|
fi
|
|
|
|
|
DB="${DATABASE_NAME:-companion}"
|
|
|
|
|
if [[ "$DB" == *prod* ]]; then
|
|
|
|
|
echo -e "${Y}Refusing to reset production database: ${DB}${R}"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
echo -e "${Y}Dropping and recreating dev database: ${DB}${R}"
|
|
|
|
|
PGPASSWORD="$DATABASE_PASSWORD" psql \
|
|
|
|
|
-h "${DATABASE_HOST:-localhost}" \
|
|
|
|
|
-p "${DATABASE_PORT:-26397}" \
|
|
|
|
|
-U "${DATABASE_USER:-lilith}" \
|
|
|
|
|
-d postgres \
|
|
|
|
|
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$DB' AND pid <> pg_backend_pid();" \
|
|
|
|
|
> /dev/null 2>&1
|
|
|
|
|
PGPASSWORD="$DATABASE_PASSWORD" dropdb \
|
|
|
|
|
-h "${DATABASE_HOST:-localhost}" \
|
|
|
|
|
-p "${DATABASE_PORT:-26397}" \
|
|
|
|
|
-U "${DATABASE_USER:-lilith}" \
|
|
|
|
|
--if-exists "$DB"
|
|
|
|
|
PGPASSWORD="$DATABASE_PASSWORD" createdb \
|
|
|
|
|
-h "${DATABASE_HOST:-localhost}" \
|
|
|
|
|
-p "${DATABASE_PORT:-26397}" \
|
|
|
|
|
-U "${DATABASE_USER:-lilith}" \
|
|
|
|
|
"$DB"
|
|
|
|
|
PGPASSWORD="$DATABASE_PASSWORD" psql \
|
|
|
|
|
-h "${DATABASE_HOST:-localhost}" \
|
|
|
|
|
-p "${DATABASE_PORT:-26397}" \
|
|
|
|
|
-U "${DATABASE_USER:-lilith}" \
|
|
|
|
|
"$DB" \
|
|
|
|
|
-c 'CREATE EXTENSION IF NOT EXISTS pgcrypto;' \
|
|
|
|
|
> /dev/null
|
|
|
|
|
echo -e "${G}Database ${DB} reset.${R}"
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
# Help
|
|
|
|
|
help|--help|-h) cmd_help ;;
|
|
|
|
|
|
|
|
|
|
*)
|
|
|
|
|
echo -e "${Y}Unknown command: $1${R}"
|
|
|
|
|
cmd_help
|
|
|
|
|
exit 1
|
|
|
|
|
;;
|
|
|
|
|
esac
|