diff --git a/.gitignore b/.gitignore index 931e1b0..7f47838 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,9 @@ dump.rdb # Temp dirs tmp +# Dev script logs +.dev-logs/ + ### PYTHON # Byte-compiled / optimized / DLL files diff --git a/CLAUDE.md b/CLAUDE.md index 91ceecd..f07a062 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,7 +20,33 @@ The platform integrates with the separate [agentex-python SDK](https://github.co - Node.js (for frontend) - uv (Python package manager) -### Quick Start (3-Terminal Setup) +### Quick Start (Recommended) + +One command does everything (auto-installs prerequisites if missing): + +```bash +./dev.sh # Installs deps + starts backend + frontend +``` + +> Make sure Docker Desktop or Rancher Desktop is running first. + +Other commands: +```bash +./dev.sh stop # Stop all services +./dev.sh status # Check service status +./dev.sh logs # View all logs +./dev.sh restart # Restart all services +``` + +**Then in a separate terminal - Agent Development:** +```bash +agentex init # Create a new agent +cd your-agent-name/ +uv venv && source .venv/bin/activate && uv sync +agentex agents run --manifest manifest.yaml +``` + +### Manual Setup (Alternative - 3 Terminals) **Terminal 1 - Backend:** ```bash @@ -37,9 +63,9 @@ npm run dev # Starts Next.js dev server **Terminal 3 - Agent Development:** ```bash -export ENVIRONMENT=development agentex init # Create a new agent cd your-agent-name/ +uv venv && source .venv/bin/activate && uv sync agentex agents run --manifest manifest.yaml ``` diff --git a/README.md b/README.md index 035acd0..06ef783 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,47 @@ To do this, you just need to spin up the [Agentex Server](https://github.com/sca > Each agent also ships with a `dev.ipynb` notebook for those uninterested in a UI, but more on that later +### Quick Start (Recommended) + +Just run one command: + +```bash +./dev.sh +``` + +That's it. This will automatically: +- Install Homebrew, uv, Node.js, and agentex-sdk if missing (macOS) +- Install all backend and frontend dependencies +- Start all Docker services (Postgres, Redis, MongoDB, Temporal) +- Start the backend API and frontend dev server +- Wait for everything to be healthy + +> **Note:** Make sure Docker Desktop or Rancher Desktop is running before you start. + +Once ready: +| Service | URL | +|---------|-----| +| Frontend UI | http://localhost:3000 | +| Backend API | http://localhost:5003 | +| Swagger Docs | http://localhost:5003/swagger | +| Temporal UI | http://localhost:8080 | + +**Other commands:** +```bash +./dev.sh stop # Stop all services +./dev.sh status # Check service status +./dev.sh logs # View all logs +./dev.sh restart # Restart all services +``` + +Then skip ahead to [Create Your First Agent](#create-your-first-agent). + +--- + +### Manual Setup (Alternative) + +If you prefer to run services in separate terminals for more control, follow the steps below. + ### Terminal 1 - Agentex Server First, open up a terminal. Then run the following commands. diff --git a/agentex-ui/package-lock.json b/agentex-ui/package-lock.json index 7532c33..383977a 100644 --- a/agentex-ui/package-lock.json +++ b/agentex-ui/package-lock.json @@ -179,7 +179,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -603,7 +602,6 @@ "version": "6.38.6", "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz", "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", - "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -695,7 +693,6 @@ "url": "https://opencollective.com/csstools" } ], - "peer": true, "engines": { "node": ">=18" }, @@ -737,7 +734,6 @@ "url": "https://opencollective.com/csstools" } ], - "peer": true, "engines": { "node": ">=18" } @@ -3523,6 +3519,7 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, + "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -3606,7 +3603,8 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -3964,7 +3962,6 @@ "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -3974,7 +3971,6 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "devOptional": true, - "peer": true, "peerDependencies": { "@types/react": "^19.0.0" } @@ -4051,7 +4047,6 @@ "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.39.0", "@typescript-eslint/types": "8.39.0", @@ -4747,7 +4742,6 @@ "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.0.6.tgz", "integrity": "sha512-1ekpBsYNUm0Xv/0YsTvoSRmiRkmzz9Pma7qQ3Ui76sg2gwp2/ewSWqx4W/HfaN5dF0E8iBbidFo1wGaeqXYIrQ==", "dev": true, - "peer": true, "dependencies": { "@vitest/utils": "4.0.6", "fflate": "^0.8.2", @@ -4781,7 +4775,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4833,6 +4826,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "peer": true, "engines": { "node": ">=8" } @@ -5178,7 +5172,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -5342,7 +5335,6 @@ "version": "11.0.3", "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", - "peer": true, "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", @@ -5553,7 +5545,6 @@ "version": "3.33.1", "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", - "peer": true, "engines": { "node": ">=0.10" } @@ -5928,7 +5919,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "peer": true, "engines": { "node": ">=12" } @@ -6240,7 +6230,8 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/dompurify": { "version": "3.3.0", @@ -6541,7 +6532,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -6629,7 +6619,6 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", "dev": true, - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -6722,7 +6711,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7398,7 +7386,6 @@ "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.0.10.tgz", "integrity": "sha512-6umCCHcjQrhP5oXhrHQQvLB0bwb1UzHAHdsXy+FjtKoYjUhmNZsQL8NivwM1vDvNEChJabVrUYxUnp/ZdYmy2g==", "dev": true, - "peer": true, "dependencies": { "@types/node": "^20.0.0", "@types/whatwg-mimetype": "^3.0.2", @@ -8917,6 +8904,7 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -10495,7 +10483,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -10601,6 +10588,7 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -10615,6 +10603,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "peer": true, "engines": { "node": ">=10" }, @@ -10626,7 +10615,8 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "dev": true, + "peer": true }, "node_modules/prop-types": { "version": "15.8.1", @@ -10696,7 +10686,6 @@ "version": "19.1.1", "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -10705,7 +10694,6 @@ "version": "19.1.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", - "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -10717,7 +10705,6 @@ "version": "7.62.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", "integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -11903,7 +11890,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "peer": true, "engines": { "node": ">=12" }, @@ -12137,7 +12123,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12452,7 +12437,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -12544,7 +12528,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "peer": true, "engines": { "node": ">=12" }, @@ -12557,7 +12540,6 @@ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.6.tgz", "integrity": "sha512-gR7INfiVRwnEOkCk47faros/9McCZMp5LM+OMNWGLaDBSvJxIzwjgNFufkuePBNaesGRnLmNfW+ddbUJRZn0nQ==", "dev": true, - "peer": true, "dependencies": { "@vitest/expect": "4.0.6", "@vitest/mocker": "4.0.6", diff --git a/dev.sh b/dev.sh new file mode 100755 index 0000000..a8a1079 --- /dev/null +++ b/dev.sh @@ -0,0 +1,446 @@ +#!/bin/bash +# +# Agentex Development Script +# Starts all services (backend + frontend) with a single command +# +# Usage: +# ./dev.sh Start all services +# ./dev.sh setup Install all prerequisites (macOS) +# ./dev.sh stop Stop all services +# ./dev.sh logs Show logs +# ./dev.sh status Check status of services +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOG_DIR="$SCRIPT_DIR/.dev-logs" +BACKEND_PID_FILE="$LOG_DIR/backend.pid" +FRONTEND_PID_FILE="$LOG_DIR/frontend.pid" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# ============================================================================= +# SETUP / INSTALLATION +# ============================================================================= + +install_prerequisites() { + echo "" + echo "========================================" + echo " Agentex Development Setup " + echo "========================================" + echo "" + + # Check OS + if [[ "$OSTYPE" != "darwin"* ]]; then + log_warn "This setup script is optimized for macOS." + log_warn "For Linux, please install manually: Python 3.12+, uv, Docker, Node.js" + exit 1 + fi + + # Step 1: Install Homebrew if not present + log_info "Checking Homebrew..." + if ! command -v brew &> /dev/null; then + log_info "Installing Homebrew..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + # Add brew to PATH for Apple Silicon + if [[ -f "/opt/homebrew/bin/brew" ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + fi + log_success "Homebrew installed" + else + log_success "Homebrew already installed" + fi + + # Step 2: Install Python 3.12+ if needed + log_info "Checking Python version..." + local python_version="" + if command -v python3 &> /dev/null; then + python_version=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') + fi + + if [[ -z "$python_version" ]] || [[ "$(echo "$python_version < 3.12" | bc -l)" == "1" ]]; then + log_info "Installing Python 3.12..." + brew install python@3.12 + log_success "Python 3.12 installed" + else + log_success "Python $python_version already installed" + fi + + # Step 3: Install uv + log_info "Checking uv..." + if ! command -v uv &> /dev/null; then + log_info "Installing uv (fast Python package manager)..." + curl -LsSf https://astral.sh/uv/install.sh | sh + # Source the env to get uv in PATH + source "$HOME/.local/bin/env" 2>/dev/null || true + export PATH="$HOME/.local/bin:$PATH" + log_success "uv installed" + else + log_success "uv already installed" + fi + + # Step 4: Check Docker daemon is running (works with Docker Desktop or Rancher) + log_info "Checking Docker..." + if ! docker info &> /dev/null; then + log_error "Docker daemon is not running. Please start Docker Desktop or Rancher Desktop." + exit 1 + fi + log_success "Docker is running" + + # Step 5: Install Node.js + log_info "Checking Node.js..." + if ! command -v node &> /dev/null; then + log_info "Installing Node.js..." + brew install node + log_success "Node.js installed" + else + log_success "Node.js already installed ($(node --version))" + fi + + # Step 6: Stop local Redis if running + log_info "Checking for local Redis..." + if brew services list 2>/dev/null | grep -q "redis.*started"; then + log_info "Stopping local Redis (conflicts with Docker Redis)..." + brew services stop redis + log_success "Local Redis stopped" + elif lsof -i :6379 &> /dev/null; then + log_warn "Something is using port 6379. You may need to stop it manually." + else + log_success "No conflicting Redis found" + fi + + # Step 7: Install agentex-sdk CLI + log_info "Checking agentex CLI..." + if ! command -v agentex &> /dev/null; then + log_info "Installing agentex-sdk..." + uv tool install agentex-sdk + log_success "agentex-sdk installed" + else + log_success "agentex CLI already installed" + fi + + # Step 8: Install backend dependencies + log_info "Installing backend dependencies..." + cd "$SCRIPT_DIR/agentex" + if [ ! -d ".venv" ]; then + uv venv + fi + uv sync --group dev + log_success "Backend dependencies installed" + cd "$SCRIPT_DIR" + + # Step 9: Install frontend dependencies + log_info "Installing frontend dependencies..." + cd "$SCRIPT_DIR/agentex-ui" + npm install + log_success "Frontend dependencies installed" + cd "$SCRIPT_DIR" + + echo "" + echo "========================================" + log_success "Setup complete!" + echo "========================================" + echo "" + echo "You can now run:" + echo " ./dev.sh # Start the development environment" + echo "" + echo "Once running, create your first agent:" + echo " agentex init # Create a new agent" + echo " cd " + echo " uv venv && source .venv/bin/activate && uv sync" + echo " agentex agents run --manifest manifest.yaml" + echo "" +} + +# ============================================================================= +# PREREQUISITES CHECK (auto-installs if missing) +# ============================================================================= + +ensure_prerequisites() { + log_info "Checking prerequisites..." + + local needs_install=false + + # Check what's missing (things we can auto-install) + if ! command -v brew &> /dev/null && [[ "$OSTYPE" == "darwin"* ]]; then + needs_install=true + fi + + if ! command -v uv &> /dev/null; then + needs_install=true + fi + + if ! command -v node &> /dev/null; then + needs_install=true + fi + + if ! command -v agentex &> /dev/null; then + needs_install=true + fi + + # Docker must be running (can't auto-install - user chooses Docker Desktop or Rancher) + if ! docker info &> /dev/null; then + log_error "Docker daemon is not running. Please start Docker Desktop or Rancher Desktop." + exit 1 + fi + + if [ "$needs_install" = true ]; then + log_info "Some prerequisites missing - installing automatically..." + install_prerequisites + else + log_success "All prerequisites found" + fi +} + +check_redis_conflict() { + # Check if local redis is running on port 6379 + if lsof -i :6379 &> /dev/null; then + log_warn "Port 6379 is in use. This may conflict with Docker Redis." + log_warn "If you have local Redis running, stop it with: brew services stop redis" + fi +} + +setup_log_dir() { + mkdir -p "$LOG_DIR" +} + +start_backend() { + log_info "Starting backend services..." + + cd "$SCRIPT_DIR/agentex" + + # Delegate to Makefile (handles deps + docker compose) + make dev > "$LOG_DIR/backend.log" 2>&1 & + local pid=$! + echo $pid > "$BACKEND_PID_FILE" + + log_success "Backend starting (PID: $pid)" + log_info "Backend logs: tail -f $LOG_DIR/backend.log" + + cd "$SCRIPT_DIR" +} + +start_frontend() { + log_info "Starting frontend..." + + cd "$SCRIPT_DIR/agentex-ui" + + # Delegate to Makefile (handles deps + dev server) + make dev > "$LOG_DIR/frontend.log" 2>&1 & + local pid=$! + echo $pid > "$FRONTEND_PID_FILE" + + log_success "Frontend starting (PID: $pid)" + log_info "Frontend logs: tail -f $LOG_DIR/frontend.log" + + cd "$SCRIPT_DIR" +} + +wait_for_backend() { + log_info "Waiting for backend to be healthy..." + + local max_attempts=90 + local attempt=0 + + while [ $attempt -lt $max_attempts ]; do + # Check if swagger endpoint responds (indicates FastAPI is ready) + if curl -s http://localhost:5003/openapi.json > /dev/null 2>&1; then + log_success "Backend is healthy!" + return 0 + fi + + attempt=$((attempt + 1)) + echo -n "." + sleep 2 + done + + echo "" + log_warn "Backend health check timed out. It may still be starting up." + log_info "Check logs with: ./dev.sh logs" +} + +stop_services() { + log_info "Stopping all services..." + + # Stop frontend + if [ -f "$FRONTEND_PID_FILE" ]; then + local frontend_pid=$(cat "$FRONTEND_PID_FILE") + if kill -0 "$frontend_pid" 2>/dev/null; then + kill "$frontend_pid" 2>/dev/null || true + log_success "Frontend stopped" + fi + rm -f "$FRONTEND_PID_FILE" + fi + + # Stop backend (docker compose) + cd "$SCRIPT_DIR/agentex" + docker compose down 2>/dev/null || true + log_success "Backend stopped" + + if [ -f "$BACKEND_PID_FILE" ]; then + rm -f "$BACKEND_PID_FILE" + fi + + cd "$SCRIPT_DIR" + + log_success "All services stopped" +} + +show_logs() { + local service="${1:-all}" + + case "$service" in + backend) + tail -f "$LOG_DIR/backend.log" + ;; + frontend) + tail -f "$LOG_DIR/frontend.log" + ;; + all|*) + log_info "Showing all logs (Ctrl+C to exit)" + tail -f "$LOG_DIR/backend.log" "$LOG_DIR/frontend.log" + ;; + esac +} + +show_status() { + echo "" + echo "=== Agentex Development Status ===" + echo "" + + # Check backend + echo -n "Backend (Docker): " + if docker compose -f "$SCRIPT_DIR/agentex/docker-compose.yml" ps 2>/dev/null | grep -q "Up"; then + echo -e "${GREEN}Running${NC}" + else + echo -e "${RED}Stopped${NC}" + fi + + # Check frontend + echo -n "Frontend (Next.js): " + if [ -f "$FRONTEND_PID_FILE" ] && kill -0 "$(cat "$FRONTEND_PID_FILE")" 2>/dev/null; then + echo -e "${GREEN}Running${NC}" + else + echo -e "${RED}Stopped${NC}" + fi + + echo "" + echo "=== URLs ===" + echo "Frontend: http://localhost:3000" + echo "Backend API: http://localhost:5003" + echo "Swagger: http://localhost:5003/swagger" + echo "Temporal UI: http://localhost:8080" + echo "" +} + +start_all() { + ensure_prerequisites + check_redis_conflict + setup_log_dir + + echo "" + echo "========================================" + echo " Starting Agentex Development Env " + echo "========================================" + echo "" + + start_backend + start_frontend + + echo "" + log_info "Services are starting up..." + echo "" + echo "=== URLs (will be ready shortly) ===" + echo "Frontend: http://localhost:3000" + echo "Backend API: http://localhost:5003" + echo "Swagger: http://localhost:5003/swagger" + echo "Temporal UI: http://localhost:8080" + echo "" + echo "=== Commands ===" + echo "./dev.sh logs - View all logs" + echo "./dev.sh logs backend - View backend logs" + echo "./dev.sh logs frontend - View frontend logs" + echo "./dev.sh status - Check service status" + echo "./dev.sh stop - Stop all services" + echo "" + + # Wait for backend to be healthy + wait_for_backend + + echo "" + log_success "Development environment is ready!" + log_info "Open http://localhost:3000 to access the UI" + echo "" + log_info "To create an agent, in a new terminal run:" + echo " agentex init" + echo " cd " + echo " uv venv && source .venv/bin/activate && uv sync" + echo " agentex agents run --manifest manifest.yaml" + echo "" +} + +# Main command router +case "${1:-start}" in + start|"") + start_all + ;; + setup) + install_prerequisites + ;; + stop) + stop_services + ;; + logs) + show_logs "$2" + ;; + status) + show_status + ;; + restart) + stop_services + sleep 2 + start_all + ;; + help|--help|-h) + echo "Agentex Development Script" + echo "" + echo "Usage: $0 [command]" + echo "" + echo "Commands:" + echo " (none) Start everything (auto-installs prerequisites if needed)" + echo " setup Just install prerequisites without starting" + echo " stop Stop all services" + echo " restart Restart all services" + echo " logs Show logs (logs backend|frontend|all)" + echo " status Check service status" + echo "" + ;; + *) + echo "Unknown command: $1" + echo "Run './dev.sh help' for usage" + exit 1 + ;; +esac