diff --git a/npm/packages/ruvbot/Dockerfile b/npm/packages/ruvbot/Dockerfile index d6d21d57..d60f982c 100644 --- a/npm/packages/ruvbot/Dockerfile +++ b/npm/packages/ruvbot/Dockerfile @@ -41,6 +41,9 @@ COPY src/ ./src/ # Build TypeScript RUN npm run build +# Copy static files to dist +RUN mkdir -p dist/api/public && cp -r src/api/public/* dist/api/public/ 2>/dev/null || true + # ----------------------------------------------------------------------------- # Stage 3: Production Runner # ----------------------------------------------------------------------------- diff --git a/npm/packages/ruvbot/README.md b/npm/packages/ruvbot/README.md index 8348c146..59aef2bc 100644 --- a/npm/packages/ruvbot/README.md +++ b/npm/packages/ruvbot/README.md @@ -17,6 +17,8 @@ - [Requirements](#requirements) - [Quick Start](#quick-start) - [Configuration](#configuration) +- [Channel Integrations](#channel-integrations) +- [Template Library](#template-library) - [API Usage](#api-usage) - [Security](#security-architecture-6-layers---why-this-matters) - [LLM Providers](#llm-providers---gemini-25-default) @@ -86,29 +88,49 @@ RuvBot is a next-generation personal AI assistant powered by RuVector's WASM vec ## Quick Start -### Install via curl +### Install via curl (Recommended) ```bash -curl -fsSL https://get.ruvector.dev/ruvbot | bash +# Basic install +curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/npm/packages/ruvbot/scripts/install.sh | bash + +# Install with interactive wizard +RUVBOT_WIZARD=true curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/npm/packages/ruvbot/scripts/install.sh | bash + +# Install specific version +RUVBOT_VERSION=0.1.3 curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/npm/packages/ruvbot/scripts/install.sh | bash + +# Install and deploy to Cloud Run +RUVBOT_DEPLOY=cloudrun curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/npm/packages/ruvbot/scripts/install.sh | bash + +# Install with Slack channel dependencies +RUVBOT_CHANNEL=slack curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/npm/packages/ruvbot/scripts/install.sh | bash ``` -Or with custom settings: +### Install Options (Environment Variables) -```bash -RUVBOT_VERSION=0.1.0 \ -RUVBOT_INSTALL_DIR=/opt/ruvbot \ -curl -fsSL https://get.ruvector.dev/ruvbot | bash -``` +| Variable | Description | Default | +|----------|-------------|---------| +| `RUVBOT_VERSION` | Specific version to install | `latest` | +| `RUVBOT_GLOBAL` | Install globally | `true` | +| `RUVBOT_INIT` | Initialize project after install | `false` | +| `RUVBOT_CHANNEL` | Install channel deps: `slack`, `discord`, `telegram`, `all` | - | +| `RUVBOT_DEPLOY` | Deploy to: `cloudrun`, `docker`, `k8s` | - | +| `RUVBOT_WIZARD` | Run interactive setup wizard | `false` | ### Install via npm/npx ```bash -# Run directly -npx @ruvector/ruvbot start +# Run directly (no install) +npx ruvbot start -# Or install globally -npm install -g @ruvector/ruvbot +# Install globally +npm install -g ruvbot ruvbot start + +# Install locally +npm install ruvbot +npx ruvbot start ``` ## Configuration @@ -177,6 +199,11 @@ ruvbot init # Start the bot server ruvbot start [--port 3000] [--debug] +# Start with a specific channel +ruvbot start --channel slack +ruvbot start --channel discord +ruvbot start --channel telegram + # Check status ruvbot status @@ -184,6 +211,17 @@ ruvbot status ruvbot skills list ruvbot skills add +# Channel setup help +ruvbot channels list +ruvbot channels setup slack +ruvbot channels setup discord +ruvbot channels setup telegram + +# Template library +ruvbot templates list +ruvbot templates info +ruvbot deploy + # Run diagnostics ruvbot doctor @@ -191,6 +229,246 @@ ruvbot doctor ruvbot config --show ``` +## Channel Integrations + +RuvBot supports multiple messaging platforms. Use `ruvbot channels setup ` for interactive setup guides. + +### Slack Integration + +**Step 1: Create a Slack App** +1. Go to https://api.slack.com/apps +2. Click "Create New App" → "From Scratch" +3. Name your app and select your workspace + +**Step 2: Configure Bot Permissions** +Navigate to **OAuth & Permissions** and add these Bot Token Scopes: +- `app_mentions:read` - Receive @mentions +- `chat:write` - Send messages +- `channels:history` - Read channel messages +- `im:history` - Read direct messages +- `reactions:write` - Add reactions +- `files:read` - Access shared files + +**Step 3: Enable Socket Mode** +1. Go to **Socket Mode** → Enable +2. Create an App-Level Token with `connections:write` scope +3. Save the `xapp-...` token + +**Step 4: Install & Get Tokens** +1. Go to **Install App** → Install to Workspace +2. Copy the Bot User OAuth Token (`xoxb-...`) +3. Copy the Signing Secret from **Basic Information** + +**Step 5: Configure Environment** +```bash +export SLACK_BOT_TOKEN="xoxb-your-bot-token" +export SLACK_SIGNING_SECRET="your-signing-secret" +export SLACK_APP_TOKEN="xapp-your-app-token" + +# Start with Slack +ruvbot start --channel slack +``` + +**Step 6: Enable Events (Optional)** +For real-time events without Socket Mode: +1. Go to **Event Subscriptions** → Enable +2. Add Request URL: `https://your-ruvbot.run.app/slack/events` +3. Subscribe to bot events: `message.channels`, `message.im`, `app_mention` + +--- + +### Discord Integration + +**Step 1: Create a Discord Application** +1. Go to https://discord.com/developers/applications +2. Click "New Application" and name it + +**Step 2: Create a Bot** +1. Go to **Bot** section → Add Bot +2. Enable Privileged Gateway Intents: + - ✅ Message Content Intent + - ✅ Server Members Intent +3. Copy the Bot Token (click "Reset Token" if needed) + +**Step 3: Get Application IDs** +1. Copy **Application ID** from General Information +2. Right-click your server → Copy Server ID (for guild-specific commands) + +**Step 4: Invite Bot to Server** +1. Go to **OAuth2** → **URL Generator** +2. Select scopes: `bot`, `applications.commands` +3. Select permissions: `Send Messages`, `Read Message History`, `Add Reactions`, `Use Slash Commands` +4. Open the generated URL to invite the bot + +**Step 5: Configure Environment** +```bash +export DISCORD_TOKEN="your-bot-token" +export DISCORD_CLIENT_ID="your-application-id" +export DISCORD_GUILD_ID="your-server-id" # Optional, for testing + +# Start with Discord +ruvbot start --channel discord +``` + +--- + +### Telegram Integration + +**Step 1: Create a Bot with BotFather** +1. Open Telegram and search for `@BotFather` +2. Send `/newbot` command +3. Follow prompts to name your bot +4. Copy the HTTP API token (format: `123456789:ABC-DEF...`) + +**Step 2: Configure Environment** +```bash +export TELEGRAM_BOT_TOKEN="your-bot-token" + +# Start with Telegram +ruvbot start --channel telegram +``` + +**Step 3: Test Your Bot** +1. Search for your bot by username in Telegram +2. Start a chat and send `/start` +3. Send messages to interact with RuvBot + +**Production: Webhook Mode** +For production deployments (Cloud Run, etc.), use webhook mode: +```bash +export TELEGRAM_BOT_TOKEN="your-bot-token" +export TELEGRAM_WEBHOOK_URL="https://your-ruvbot.run.app/telegram/webhook" +``` + +--- + +### Multi-Channel Configuration + +Run RuvBot with multiple channels simultaneously: + +```json +{ + "name": "my-ruvbot", + "channels": { + "slack": { + "enabled": true, + "token": "${SLACK_BOT_TOKEN}", + "signingSecret": "${SLACK_SIGNING_SECRET}", + "appToken": "${SLACK_APP_TOKEN}" + }, + "discord": { + "enabled": true, + "token": "${DISCORD_TOKEN}", + "clientId": "${DISCORD_CLIENT_ID}" + }, + "telegram": { + "enabled": true, + "token": "${TELEGRAM_BOT_TOKEN}" + } + } +} +``` + +Install optional dependencies: +```bash +npm install @slack/bolt @slack/web-api discord.js telegraf +``` + +--- + +### Cloud Run Channel Setup + +For Google Cloud Run deployments: + +```bash +# Slack +gcloud run services update ruvbot --set-env-vars="\ +SLACK_BOT_TOKEN=xoxb-...,\ +SLACK_SIGNING_SECRET=...,\ +SLACK_APP_TOKEN=xapp-..." + +# Discord +gcloud run services update ruvbot --set-env-vars="\ +DISCORD_TOKEN=...,\ +DISCORD_CLIENT_ID=...,\ +DISCORD_GUILD_ID=..." + +# Telegram (webhook mode recommended) +gcloud run services update ruvbot --set-env-vars="\ +TELEGRAM_BOT_TOKEN=...,\ +TELEGRAM_WEBHOOK_URL=https://ruvbot-xxx.run.app/telegram/webhook" +``` + +## Template Library + +RuvBot includes pre-built templates for deploying long-running agent patterns. + +### List Available Templates + +```bash +ruvbot templates list +ruvbot templates list --category advanced +``` + +### Available Templates + +| Category | Template | Description | +|----------|----------|-------------| +| **Practical** | `code-reviewer` | Automated code review with security scanning | +| | `doc-generator` | Auto-generate project documentation | +| | `test-generator` | Generate comprehensive test suites (TDD) | +| **Intermediate** | `feature-swarm` | Parallel feature development with coordinated agents | +| | `refactor-squad` | Coordinated codebase refactoring | +| | `ci-cd-pipeline` | Automated build, test, and deployment | +| **Advanced** | `self-learning-bot` | AI that improves from interactions | +| | `research-swarm` | Distributed research across sources | +| | `performance-optimizer` | Continuous performance monitoring | +| **Exotic** | `byzantine-validator` | Byzantine fault-tolerant validation (33% fault tolerance) | +| | `hive-mind` | Emergent collective intelligence with queen coordination | +| | `multi-repo-coordinator` | Cross-repository change coordination | +| | `adversarial-tester` | Red team vs blue team security testing | + +### Deploy a Template + +```bash +# View template details +ruvbot templates info hive-mind + +# Deploy a template +ruvbot deploy code-reviewer --repo ./my-project + +# Deploy with options +ruvbot deploy feature-swarm --name "auth-feature" --model google/gemini-2.0-flash-001 + +# Dry run (preview without executing) +ruvbot deploy hive-mind --dry-run +``` + +### Template Examples + +```bash +# Code review with security scanning +ruvbot deploy code-reviewer --repo ./my-project + +# Documentation generation +ruvbot deploy doc-generator --output ./docs + +# Test suite generation with 80% coverage target +ruvbot deploy test-generator --coverage 80 + +# Feature development with coordinated swarm +ruvbot deploy feature-swarm --feature "Add user authentication" + +# Research across multiple sources +ruvbot deploy research-swarm --topic "vector databases" + +# Hive-mind for complex objectives +ruvbot deploy hive-mind --objective "Build complete REST API" + +# Byzantine validation for critical operations +ruvbot deploy byzantine-validator --quorum 4 +``` + ## API Usage ### REST API Endpoints @@ -503,7 +781,20 @@ const results = await memory.search('find important info', { }); ``` -## Docker +## Deployment + +RuvBot supports multiple deployment options from local development to enterprise cloud. + +### Quick Deploy Options + +| Method | Best For | Command | +|--------|----------|---------| +| **npx** | Quick testing | `npx ruvbot start` | +| **Docker** | Containerized | `docker run -p 3000:3000 ruvector/ruvbot` | +| **Cloud Run** | Serverless | `ruvbot deploy cloudrun` | +| **Kubernetes** | Enterprise | `kubectl apply -f k8s/` | + +### Docker ```yaml # docker-compose.yml @@ -514,13 +805,41 @@ services: ports: - "3000:3000" environment: - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} - SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN} + - SLACK_SIGNING_SECRET=${SLACK_SIGNING_SECRET} + - SLACK_APP_TOKEN=${SLACK_APP_TOKEN} + - DISCORD_TOKEN=${DISCORD_TOKEN} + - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN} volumes: - ./data:/app/data - ./skills:/app/skills ``` +### Docker with Webhooks + +```yaml +# docker-compose.yml with webhook configuration +version: '3.8' +services: + ruvbot: + image: ruvector/ruvbot:latest + ports: + - "3000:3000" + environment: + - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} + # Outbound webhooks + - WEBHOOK_URL=https://your-service.com/callback + - WEBHOOK_SECRET=your-shared-secret + # Inbound webhook auth + - WEBHOOK_AUTH_TOKEN=your-auth-token + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 +``` + ## Google Cloud Deployment RuvBot includes cost-optimized Google Cloud Platform deployment (~$15-20/month for low traffic). @@ -566,6 +885,236 @@ terraform apply \ See [ADR-013: GCP Deployment](docs/adr/ADR-013-gcp-deployment.md) for architecture details. +### gcloud CLI Integration + +RuvBot includes native gcloud CLI integration for Cloud Run deployments. + +**Prerequisites:** +```bash +# Install gcloud CLI +curl https://sdk.cloud.google.com | bash + +# Authenticate +gcloud auth login + +# Set project +gcloud config set project YOUR_PROJECT_ID +``` + +**Deploy with CLI:** +```bash +# Interactive deployment wizard +ruvbot deploy-cloud wizard + +# Deploy to Cloud Run +ruvbot deploy-cloud cloudrun --project my-project --region us-central1 + +# Deploy with environment file +ruvbot deploy-cloud cloudrun --env-file .env + +# Deploy to Docker +ruvbot deploy-cloud docker --port 3000 + +# Deploy to Kubernetes +ruvbot deploy-cloud k8s --namespace production --replicas 3 + +# Check deployment status +ruvbot deploy-cloud status +``` + +**CLI Options:** + +| Command | Options | Description | +|---------|---------|-------------| +| `cloudrun` | `--project`, `--region`, `--service`, `--memory`, `--min-instances`, `--max-instances`, `--env-file` | Deploy to Cloud Run | +| `docker` | `--name`, `--port`, `--detach`, `--env-file` | Deploy with Docker Compose | +| `k8s` | `--namespace`, `--replicas`, `--env-file` | Deploy to Kubernetes | +| `wizard` | - | Interactive deployment wizard | +| `status` | `--platform` | Check deployment status | + +### Deploy with Channel Webhooks (Cloud Run) + +Complete Cloud Run deployment with all channel webhooks: + +```bash +# Build and deploy +gcloud run deploy ruvbot \ + --source . \ + --platform managed \ + --region us-central1 \ + --allow-unauthenticated \ + --set-env-vars="\ +OPENROUTER_API_KEY=${OPENROUTER_API_KEY},\ +DEFAULT_MODEL=google/gemini-2.0-flash-001,\ +SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN},\ +SLACK_SIGNING_SECRET=${SLACK_SIGNING_SECRET},\ +TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN},\ +TELEGRAM_WEBHOOK_URL=https://ruvbot-xxx.run.app/telegram/webhook,\ +DISCORD_TOKEN=${DISCORD_TOKEN},\ +DISCORD_CLIENT_ID=${DISCORD_CLIENT_ID}" +``` + +### Kubernetes Deployment + +```yaml +# k8s/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ruvbot +spec: + replicas: 2 + selector: + matchLabels: + app: ruvbot + template: + metadata: + labels: + app: ruvbot + spec: + containers: + - name: ruvbot + image: ruvector/ruvbot:latest + ports: + - containerPort: 3000 + envFrom: + - secretRef: + name: ruvbot-secrets + livenessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 10 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /ready + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 10 +--- +apiVersion: v1 +kind: Service +metadata: + name: ruvbot +spec: + selector: + app: ruvbot + ports: + - port: 80 + targetPort: 3000 + type: LoadBalancer +``` + +## Webhook Configuration + +### Inbound Webhooks + +RuvBot exposes webhook endpoints for receiving messages from external services. + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/webhook/message` | POST | Receive chat messages | +| `/webhook/event` | POST | Receive system events | +| `/slack/events` | POST | Slack Events API | +| `/telegram/webhook` | POST | Telegram webhook updates | +| `/api/sessions/:id/chat` | POST | Direct chat endpoint | + +### Webhook Security + +```bash +# Set webhook authentication +export WEBHOOK_SECRET="your-shared-secret" +export WEBHOOK_AUTH_TOKEN="bearer-token-for-inbound" +``` + +Inbound requests are validated using: +- **X-Webhook-Secret** header for HMAC signature verification +- **Authorization** header with Bearer token +- Request body signature validation + +### Outbound Webhooks + +Configure RuvBot to send responses and events to your services: + +```json +{ + "webhooks": { + "outbound": { + "url": "https://your-service.com/ruvbot-callback", + "secret": "shared-secret-for-signing", + "events": ["message", "agent.spawn", "session.create", "error"], + "retries": 3, + "timeout": 30000, + "headers": { + "X-Custom-Header": "value" + } + } + } +} +``` + +### Webhook Event Types + +| Event | Payload | Description | +|-------|---------|-------------| +| `message` | `{sessionId, role, content, timestamp}` | New message in session | +| `message.response` | `{sessionId, content, model, tokens}` | Bot response generated | +| `agent.spawn` | `{agentId, type, name}` | Agent created | +| `agent.stop` | `{agentId, reason}` | Agent terminated | +| `session.create` | `{sessionId, agentId, userId}` | Session started | +| `session.end` | `{sessionId, messageCount, duration}` | Session ended | +| `memory.store` | `{key, namespace, size}` | Memory stored | +| `security.threat` | `{type, severity, blocked}` | Threat detected | +| `error` | `{code, message, context}` | Error occurred | + +### Example: Custom Integration + +```typescript +// Your webhook receiver +app.post('/ruvbot-callback', (req, res) => { + // Verify signature + const signature = req.headers['x-webhook-signature']; + const isValid = verifyHmac(req.body, signature, WEBHOOK_SECRET); + + if (!isValid) { + return res.status(401).send('Invalid signature'); + } + + const { event, data } = req.body; + + switch (event) { + case 'message.response': + // Forward to your chat system + sendToChat(data.sessionId, data.content); + break; + case 'security.threat': + // Alert security team + alertSecurity(data); + break; + } + + res.status(200).send('OK'); +}); +``` + +### CLI Webhook Commands + +```bash +# List configured webhooks +ruvbot webhooks list + +# Test a webhook endpoint +ruvbot webhooks test https://your-service.com/callback + +# Test with custom payload +ruvbot webhooks test https://your-service.com/callback \ + --payload '{"test": true, "message": "Hello"}' + +# Show channel-specific setup +ruvbot channels setup webhook +``` + ## LLM Providers - Gemini 2.5 Default RuvBot supports 12+ models with **Gemini 2.5 Pro as the recommended default** for optimal cost/performance. diff --git a/npm/packages/ruvbot/docs/adr/ADR-015-chat-ui.md b/npm/packages/ruvbot/docs/adr/ADR-015-chat-ui.md new file mode 100644 index 00000000..51b6e3ce --- /dev/null +++ b/npm/packages/ruvbot/docs/adr/ADR-015-chat-ui.md @@ -0,0 +1,192 @@ +# ADR-015: Chat UI Architecture + +## Status + +Accepted + +## Date + +2026-01-28 + +## Context + +RuvBot provides a powerful REST API for chat interactions, but lacks a user-facing web interface. When users visit the root URL of a deployed RuvBot instance (e.g., on Cloud Run), they receive a 404 error instead of a usable chat interface. + +### Requirements + +1. Provide a modern, responsive chat UI out of the box +2. Support dark mode (default) and light mode themes +3. Work with the existing REST API endpoints +4. No build step required - serve static files directly +5. Support streaming responses for real-time AI interaction +6. Mobile-friendly design +7. Model selection capability +8. Integration with CLI and npm package + +### Alternatives Considered + +| Option | Pros | Cons | +|--------|------|------| +| **assistant-ui** | Industry leader, 200k+ downloads, Y Combinator backed | Requires React build, adds complexity | +| **Vercel AI Elements** | Official Vercel components, AI SDK integration | Requires Next.js | +| **shadcn-chatbot-kit** | Beautiful components, shadcn design system | Requires React build | +| **Embedded HTML/CSS/JS** | No build step, portable, fast deployment | Less features, custom implementation | + +## Decision + +Implement a **lightweight embedded chat UI** using vanilla HTML, CSS, and JavaScript that: + +1. Is served directly from the existing HTTP server +2. Requires no build step or additional dependencies +3. Provides a modern, accessible interface +4. Supports dark mode by default +5. Includes basic markdown rendering +6. Works seamlessly with the existing REST API + +### Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ RuvBot Server │ +├─────────────────────────────────────────────────────────────────┤ +│ GET / → Chat UI (index.html) │ +│ GET /health → Health check │ +│ GET /api/models → Available models │ +│ POST /api/sessions → Create session │ +│ POST /api/sessions/:id/chat → Chat endpoint │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### File Structure + +``` +src/ +├── api/ +│ └── public/ +│ └── index.html # Chat UI (single file) +├── server.ts # Updated to serve static files +└── ... +``` + +### Features + +1. **Theme Support**: Dark mode default, light mode toggle +2. **Model Selection**: Dropdown for available models +3. **Responsive Design**: Mobile-first approach +4. **Accessibility**: ARIA labels, keyboard navigation +5. **Markdown Rendering**: Code blocks, lists, links +6. **Error Handling**: User-friendly error messages +7. **Session Management**: Automatic session creation +8. **Real-time Updates**: Typing indicators + +### CSS Design System + +```css +:root { + --bg-primary: #0a0a0f; /* Dark background */ + --bg-secondary: #12121a; /* Card background */ + --text-primary: #f0f0f5; /* Main text */ + --accent: #6366f1; /* Indigo accent */ + --radius: 12px; /* Border radius */ +} +``` + +### API Integration + +The UI integrates with existing endpoints: + +```javascript +// Create session +POST /api/sessions { agentId: 'default-agent' } + +// Send message +POST /api/sessions/:id/chat { message: '...', model: '...' } +``` + +## Consequences + +### Positive + +1. **Zero Configuration**: Works out of the box +2. **Fast Deployment**: No build step required +3. **Portable**: Single HTML file, easy to customize +4. **Lightweight**: ~25KB uncompressed +5. **Framework Agnostic**: No React/Vue/Svelte dependency +6. **Cloud Run Compatible**: Works with existing deployment + +### Negative + +1. **Limited Features**: No streaming UI (yet), basic markdown +2. **Manual Updates**: No component library updates +3. **Custom Code**: Maintenance responsibility + +### Neutral + +1. Future option to add assistant-ui or similar for advanced features +2. Can be replaced with any frontend framework later + +## Implementation + +### Server Changes (server.ts) + +```typescript +// Serve static files +function getChatUIPath(): string { + const possiblePaths = [ + join(__dirname, 'api', 'public', 'index.html'), + // ... fallback paths + ]; + // Find first existing path +} + +// Add root route +{ method: 'GET', pattern: /^\/$/, handler: handleRoot } +``` + +### CLI Integration + +```bash +# View chat UI URL after deployment +ruvbot deploy-cloud cloudrun +# Output: URL: https://ruvbot-xxx.run.app + +# Open chat UI +ruvbot open # Opens browser to chat UI +``` + +### npm Package + +The chat UI is bundled with the npm package: + +```json +{ + "files": [ + "dist", + "bin", + "scripts", + "src/api/public" + ] +} +``` + +## Future Enhancements + +1. **Streaming Responses**: SSE/WebSocket for real-time streaming +2. **File Uploads**: Image and document support +3. **Voice Input**: Speech-to-text integration +4. **assistant-ui Migration**: Full-featured React UI option +5. **Themes**: Additional theme presets +6. **Plugins**: Extensible UI components + +## References + +- [assistant-ui](https://github.com/assistant-ui/assistant-ui) - Industry-leading chat UI library +- [Vercel AI SDK](https://ai-sdk.dev/) - AI SDK with streaming support +- [shadcn/ui](https://ui.shadcn.com/) - Design system inspiration +- [ADR-013: GCP Deployment](./ADR-013-gcp-deployment.md) - Cloud Run deployment + +## Changelog + +| Date | Change | +|------|--------| +| 2026-01-28 | Initial version - embedded chat UI | diff --git a/npm/packages/ruvbot/package.json b/npm/packages/ruvbot/package.json index 63f4fce2..9b58a585 100644 --- a/npm/packages/ruvbot/package.json +++ b/npm/packages/ruvbot/package.json @@ -1,6 +1,6 @@ { "name": "ruvbot", - "version": "0.1.1", + "version": "0.1.6", "description": "Enterprise-grade self-learning AI assistant with military-strength security, 150x faster vector search, and 12+ LLM models", "main": "dist/index.js", "module": "dist/esm/index.js", @@ -133,6 +133,8 @@ "files": [ "dist", "bin", + "scripts", + "src/api/public", ".env.example", "README.md" ] diff --git a/npm/packages/ruvbot/scripts/install.sh b/npm/packages/ruvbot/scripts/install.sh index ac142be6..7b77a44d 100755 --- a/npm/packages/ruvbot/scripts/install.sh +++ b/npm/packages/ruvbot/scripts/install.sh @@ -1,7 +1,37 @@ #!/bin/bash - -# RuvBot Installation Script -# Usage: curl -fsSL https://get.ruvector.dev/ruvbot | bash +# +# RuvBot Installer +# +# Usage: +# curl -fsSL https://get.ruvector.dev/ruvbot | bash +# curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/npm/packages/ruvbot/scripts/install.sh | bash +# +# Options (via environment variables): +# RUVBOT_VERSION - Specific version to install (default: latest) +# RUVBOT_GLOBAL - Install globally (default: true) +# RUVBOT_INIT - Run init after install (default: false) +# RUVBOT_CHANNEL - Configure channel: slack, discord, telegram +# RUVBOT_DEPLOY - Deploy target: local, docker, cloudrun, k8s +# RUVBOT_WIZARD - Run interactive wizard (default: false) +# +# Examples: +# # Basic install +# curl -fsSL https://get.ruvector.dev/ruvbot | bash +# +# # Install specific version +# RUVBOT_VERSION=0.1.3 curl -fsSL https://get.ruvector.dev/ruvbot | bash +# +# # Install and initialize +# RUVBOT_INIT=true curl -fsSL https://get.ruvector.dev/ruvbot | bash +# +# # Install with Slack configuration +# RUVBOT_CHANNEL=slack curl -fsSL https://get.ruvector.dev/ruvbot | bash +# +# # Install and deploy to Cloud Run +# RUVBOT_DEPLOY=cloudrun curl -fsSL https://get.ruvector.dev/ruvbot | bash +# +# # Run full interactive wizard +# RUVBOT_WIZARD=true curl -fsSL https://get.ruvector.dev/ruvbot | bash set -e @@ -10,91 +40,699 @@ RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' +CYAN='\033[0;36m' +MAGENTA='\033[0;35m' NC='\033[0m' # No Color +BOLD='\033[1m' +DIM='\033[2m' -echo -e "${BLUE}" -echo " ____ ____ _ " -echo " | _ \ _ ___ _| __ ) ___ | |_ " -echo " | |_) | | | \ \ / / _ \ / _ \| __|" -echo " | _ <| |_| |\ V /| |_) | (_) | |_ " -echo " |_| \_\\\\__,_| \_/ |____/ \___/ \__|" -echo -e "${NC}" -echo "Self-learning AI Assistant" -echo "" +# Configuration +RUVBOT_VERSION="${RUVBOT_VERSION:-latest}" +RUVBOT_GLOBAL="${RUVBOT_GLOBAL:-true}" +RUVBOT_INIT="${RUVBOT_INIT:-false}" +RUVBOT_CHANNEL="${RUVBOT_CHANNEL:-}" +RUVBOT_DEPLOY="${RUVBOT_DEPLOY:-}" +RUVBOT_WIZARD="${RUVBOT_WIZARD:-false}" -# Check Node.js version -check_node() { - if ! command -v node &> /dev/null; then - echo -e "${RED}Error: Node.js is not installed.${NC}" - echo "Please install Node.js 18 or later: https://nodejs.org" - exit 1 - fi +# Feature flags +GCLOUD_AVAILABLE=false +DOCKER_AVAILABLE=false +KUBECTL_AVAILABLE=false - NODE_VERSION=$(node -v | sed 's/v//' | cut -d. -f1) - if [ "$NODE_VERSION" -lt 18 ]; then - echo -e "${RED}Error: Node.js 18+ required. Found v$(node -v)${NC}" - exit 1 - fi - - echo -e "${GREEN}[x] Node.js $(node -v)${NC}" +# Banner +print_banner() { + echo -e "${CYAN}" + echo ' ____ ____ _ ' + echo ' | _ \ _ ___ _| __ ) ___ | |_ ' + echo ' | |_) | | | \ \ / / _ \ / _ \| __|' + echo ' | _ <| |_| |\ V /| |_) | (_) | |_ ' + echo ' |_| \_\\__,_| \_/ |____/ \___/ \__|' + echo -e "${NC}" + echo -e "${BOLD}Enterprise-Grade Self-Learning AI Assistant${NC}" + echo -e "${DIM}Military-strength security • 150x faster search • 12+ LLM models${NC}" + echo "" } -# Check npm version -check_npm() { - if ! command -v npm &> /dev/null; then - echo -e "${RED}Error: npm is not installed.${NC}" - exit 1 - fi +# Logging functions +info() { echo -e "${BLUE}ℹ${NC} $1"; } +success() { echo -e "${GREEN}✓${NC} $1"; } +warn() { echo -e "${YELLOW}⚠${NC} $1"; } +error() { echo -e "${RED}✗${NC} $1"; exit 1; } +step() { echo -e "\n${MAGENTA}▸${NC} ${BOLD}$1${NC}"; } - echo -e "${GREEN}[x] npm $(npm -v)${NC}" +# Check dependencies +check_dependencies() { + step "Checking dependencies" + + # Check Node.js + if ! command -v node &> /dev/null; then + error "Node.js is required but not installed. Install from https://nodejs.org" + fi + + NODE_VERSION=$(node -v | cut -d 'v' -f 2 | cut -d '.' -f 1) + if [ "$NODE_VERSION" -lt 18 ]; then + error "Node.js 18+ is required. Current: $(node -v)" + fi + success "Node.js $(node -v)" + + # Check npm + if ! command -v npm &> /dev/null; then + error "npm is required but not installed" + fi + success "npm $(npm -v)" + + # Check optional: gcloud + if command -v gcloud &> /dev/null; then + success "gcloud CLI $(gcloud --version 2>/dev/null | head -1 | awk '{print $4}')" + GCLOUD_AVAILABLE=true + else + echo -e "${DIM} ○ gcloud CLI not found (optional for Cloud Run)${NC}" + fi + + # Check optional: docker + if command -v docker &> /dev/null; then + success "Docker $(docker --version | awk '{print $3}' | tr -d ',')" + DOCKER_AVAILABLE=true + else + echo -e "${DIM} ○ Docker not found (optional for containerization)${NC}" + fi + + # Check optional: kubectl + if command -v kubectl &> /dev/null; then + success "kubectl $(kubectl version --client -o json 2>/dev/null | grep -o '"gitVersion": "[^"]*"' | cut -d'"' -f4)" + KUBECTL_AVAILABLE=true + else + echo -e "${DIM} ○ kubectl not found (optional for Kubernetes)${NC}" + fi } -# Install RuvBot globally +# Install RuvBot install_ruvbot() { - echo "" - echo -e "${BLUE}Installing @ruvector/ruvbot...${NC}" + step "Installing RuvBot" - npm install -g @ruvector/ruvbot + PACKAGE="ruvbot" + if [ "$RUVBOT_VERSION" != "latest" ]; then + PACKAGE="ruvbot@$RUVBOT_VERSION" + info "Installing version $RUVBOT_VERSION" + fi - if [ $? -eq 0 ]; then - echo -e "${GREEN}[x] RuvBot installed successfully!${NC}" - else - echo -e "${RED}Installation failed. Try running with sudo:${NC}" - echo "sudo npm install -g @ruvector/ruvbot" - exit 1 - fi + if [ "$RUVBOT_GLOBAL" = "true" ]; then + npm install -g "$PACKAGE" 2>/dev/null || sudo npm install -g "$PACKAGE" + success "RuvBot installed globally" + else + npm install "$PACKAGE" + success "RuvBot installed locally" + fi + + # Verify installation + if command -v ruvbot &> /dev/null; then + INSTALLED_VERSION=$(ruvbot --version 2>/dev/null || echo "unknown") + success "RuvBot $INSTALLED_VERSION is ready" + else + success "RuvBot installed (use 'npx ruvbot' to run)" + fi } -# Show next steps -show_next_steps() { - echo "" - echo -e "${GREEN}Installation complete!${NC}" - echo "" - echo "Next steps:" - echo "" - echo " 1. Initialize a new project:" - echo -e " ${YELLOW}ruvbot init${NC}" - echo "" - echo " 2. Configure your environment:" - echo -e " ${YELLOW}ruvbot config${NC}" - echo "" - echo " 3. Start the bot:" - echo -e " ${YELLOW}ruvbot start${NC}" - echo "" - echo "Documentation: https://github.com/ruvnet/ruvector" - echo "" +# Install optional dependencies for channels +install_channel_deps() { + local channel=$1 + step "Installing $channel dependencies" + + case "$channel" in + slack) + npm install @slack/bolt @slack/web-api 2>/dev/null + success "Slack SDK installed (@slack/bolt, @slack/web-api)" + ;; + discord) + npm install discord.js 2>/dev/null + success "Discord.js installed" + ;; + telegram) + npm install telegraf 2>/dev/null + success "Telegraf installed" + ;; + all) + npm install @slack/bolt @slack/web-api discord.js telegraf 2>/dev/null + success "All channel dependencies installed" + ;; + esac } -# Main installation flow +# Initialize project +init_project() { + step "Initializing RuvBot project" + + if [ "$RUVBOT_GLOBAL" = "true" ]; then + ruvbot init --yes + else + npx ruvbot init --yes + fi + + success "Project initialized" +} + +# Configure channel interactively +configure_channel() { + local channel=$1 + + step "Configuring $channel" + + case "$channel" in + slack) + echo "" + echo " To set up Slack, you'll need credentials from:" + echo -e " ${CYAN}https://api.slack.com/apps${NC}" + echo "" + read -p " SLACK_BOT_TOKEN (xoxb-...): " SLACK_BOT_TOKEN + read -p " SLACK_SIGNING_SECRET: " SLACK_SIGNING_SECRET + read -p " SLACK_APP_TOKEN (xapp-...): " SLACK_APP_TOKEN + + { + echo "SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN" + echo "SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET" + echo "SLACK_APP_TOKEN=$SLACK_APP_TOKEN" + } >> .env + + success "Slack configuration saved to .env" + ;; + + discord) + echo "" + echo " To set up Discord, you'll need credentials from:" + echo -e " ${CYAN}https://discord.com/developers/applications${NC}" + echo "" + read -p " DISCORD_TOKEN: " DISCORD_TOKEN + read -p " DISCORD_CLIENT_ID: " DISCORD_CLIENT_ID + read -p " DISCORD_GUILD_ID (optional): " DISCORD_GUILD_ID + + { + echo "DISCORD_TOKEN=$DISCORD_TOKEN" + echo "DISCORD_CLIENT_ID=$DISCORD_CLIENT_ID" + [ -n "$DISCORD_GUILD_ID" ] && echo "DISCORD_GUILD_ID=$DISCORD_GUILD_ID" + } >> .env + + success "Discord configuration saved to .env" + ;; + + telegram) + echo "" + echo " To set up Telegram, get a token from:" + echo -e " ${CYAN}@BotFather${NC} on Telegram" + echo "" + read -p " TELEGRAM_BOT_TOKEN: " TELEGRAM_BOT_TOKEN + + echo "TELEGRAM_BOT_TOKEN=$TELEGRAM_BOT_TOKEN" >> .env + + success "Telegram configuration saved to .env" + ;; + esac +} + +# Deploy to Cloud Run +deploy_cloudrun() { + step "Deploying to Google Cloud Run" + + if [ "$GCLOUD_AVAILABLE" != "true" ]; then + error "gcloud CLI is required. Install from https://cloud.google.com/sdk" + fi + + # Check authentication + if ! gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null | head -1; then + warn "Not authenticated with gcloud" + info "Running 'gcloud auth login'..." + gcloud auth login + fi + + # Get project + CURRENT_PROJECT=$(gcloud config get-value project 2>/dev/null || echo "") + echo "" + read -p " GCP Project ID [$CURRENT_PROJECT]: " PROJECT_ID + PROJECT_ID="${PROJECT_ID:-$CURRENT_PROJECT}" + + if [ -z "$PROJECT_ID" ]; then + error "Project ID is required" + fi + + gcloud config set project "$PROJECT_ID" 2>/dev/null + + # Get region + read -p " Region [us-central1]: " REGION + REGION="${REGION:-us-central1}" + + # Get service name + read -p " Service name [ruvbot]: " SERVICE_NAME + SERVICE_NAME="${SERVICE_NAME:-ruvbot}" + + # Get API key + echo "" + echo " LLM Provider:" + echo " 1. OpenRouter (recommended - Gemini, Claude, GPT)" + echo " 2. Anthropic (Claude only)" + read -p " Choose [1]: " PROVIDER_CHOICE + PROVIDER_CHOICE="${PROVIDER_CHOICE:-1}" + + if [ "$PROVIDER_CHOICE" = "1" ]; then + read -p " OPENROUTER_API_KEY: " API_KEY + ENV_VARS="OPENROUTER_API_KEY=$API_KEY,DEFAULT_MODEL=google/gemini-2.0-flash-001" + else + read -p " ANTHROPIC_API_KEY: " API_KEY + ENV_VARS="ANTHROPIC_API_KEY=$API_KEY" + fi + + # Channel configuration + echo "" + read -p " Configure Slack? [y/N]: " SETUP_SLACK + if [[ "$SETUP_SLACK" =~ ^[Yy]$ ]]; then + read -p " SLACK_BOT_TOKEN: " SLACK_BOT_TOKEN + read -p " SLACK_SIGNING_SECRET: " SLACK_SIGNING_SECRET + ENV_VARS="$ENV_VARS,SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN,SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET" + fi + + read -p " Configure Telegram? [y/N]: " SETUP_TELEGRAM + if [[ "$SETUP_TELEGRAM" =~ ^[Yy]$ ]]; then + read -p " TELEGRAM_BOT_TOKEN: " TELEGRAM_BOT_TOKEN + ENV_VARS="$ENV_VARS,TELEGRAM_BOT_TOKEN=$TELEGRAM_BOT_TOKEN" + fi + + # Enable required APIs + info "Enabling required GCP APIs..." + gcloud services enable run.googleapis.com containerregistry.googleapis.com cloudbuild.googleapis.com 2>/dev/null + + # Create Dockerfile if it doesn't exist + if [ ! -f "Dockerfile" ]; then + info "Creating Dockerfile..." + cat > Dockerfile << 'DOCKERFILE' +FROM node:20-slim + +WORKDIR /app + +# Install curl for health checks +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* + +# Install ruvbot +RUN npm install -g ruvbot + +# Create directories +RUN mkdir -p /app/data /app/plugins /app/skills + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:${PORT:-8080}/health || exit 1 + +# Start command +CMD ["ruvbot", "start", "--port", "8080"] +DOCKERFILE + success "Dockerfile created" + fi + + # Deploy + info "Deploying to Cloud Run (this may take a few minutes)..." + gcloud run deploy "$SERVICE_NAME" \ + --source . \ + --platform managed \ + --region "$REGION" \ + --allow-unauthenticated \ + --port 8080 \ + --memory 512Mi \ + --min-instances 0 \ + --max-instances 10 \ + --set-env-vars="$ENV_VARS" \ + --quiet + + # Get URL + SERVICE_URL=$(gcloud run services describe "$SERVICE_NAME" --region "$REGION" --format='value(status.url)') + + echo "" + echo -e "${GREEN}═══════════════════════════════════════${NC}" + echo -e "${BOLD}🚀 RuvBot deployed successfully!${NC}" + echo -e "${GREEN}═══════════════════════════════════════${NC}" + echo "" + echo -e " URL: ${CYAN}$SERVICE_URL${NC}" + echo -e " Health: ${CYAN}$SERVICE_URL/health${NC}" + echo -e " API: ${CYAN}$SERVICE_URL/api/status${NC}" + echo -e " Models: ${CYAN}$SERVICE_URL/api/models${NC}" + echo "" + echo " Quick test:" + echo -e " ${DIM}curl $SERVICE_URL/health${NC}" + echo "" + + # Set Telegram webhook if configured + if [ -n "$TELEGRAM_BOT_TOKEN" ]; then + WEBHOOK_URL="$SERVICE_URL/telegram/webhook" + info "Setting Telegram webhook..." + curl -s "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook?url=$WEBHOOK_URL" > /dev/null + success "Telegram webhook: $WEBHOOK_URL" + fi +} + +# Deploy to Docker +deploy_docker() { + step "Deploying with Docker" + + if [ "$DOCKER_AVAILABLE" != "true" ]; then + error "Docker is required. Install from https://docker.com" + fi + + # Get configuration + read -p " Container name [ruvbot]: " CONTAINER_NAME + CONTAINER_NAME="${CONTAINER_NAME:-ruvbot}" + + read -p " Port [3000]: " PORT + PORT="${PORT:-3000}" + + # Create docker-compose.yml + info "Creating docker-compose.yml..." + cat > docker-compose.yml << COMPOSE +version: '3.8' +services: + ruvbot: + image: node:20-slim + container_name: $CONTAINER_NAME + working_dir: /app + command: sh -c "npm install -g ruvbot && ruvbot start --port 3000" + ports: + - "$PORT:3000" + environment: + - OPENROUTER_API_KEY=\${OPENROUTER_API_KEY} + - ANTHROPIC_API_KEY=\${ANTHROPIC_API_KEY} + - SLACK_BOT_TOKEN=\${SLACK_BOT_TOKEN} + - SLACK_SIGNING_SECRET=\${SLACK_SIGNING_SECRET} + - SLACK_APP_TOKEN=\${SLACK_APP_TOKEN} + - DISCORD_TOKEN=\${DISCORD_TOKEN} + - DISCORD_CLIENT_ID=\${DISCORD_CLIENT_ID} + - TELEGRAM_BOT_TOKEN=\${TELEGRAM_BOT_TOKEN} + volumes: + - ./data:/app/data + - ./plugins:/app/plugins + - ./skills:/app/skills + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + restart: unless-stopped +COMPOSE + success "docker-compose.yml created" + + # Start containers + read -p " Start containers now? [Y/n]: " START_NOW + START_NOW="${START_NOW:-Y}" + + if [[ "$START_NOW" =~ ^[Yy]$ ]]; then + info "Starting Docker containers..." + docker-compose up -d + + echo "" + echo -e "${GREEN}═══════════════════════════════════════${NC}" + echo -e "${BOLD}🚀 RuvBot is running!${NC}" + echo -e "${GREEN}═══════════════════════════════════════${NC}" + echo "" + echo -e " URL: ${CYAN}http://localhost:$PORT${NC}" + echo -e " Health: ${CYAN}http://localhost:$PORT/health${NC}" + echo -e " Logs: ${DIM}docker-compose logs -f${NC}" + echo -e " Stop: ${DIM}docker-compose down${NC}" + echo "" + fi +} + +# Deploy to Kubernetes +deploy_k8s() { + step "Deploying to Kubernetes" + + if [ "$KUBECTL_AVAILABLE" != "true" ]; then + error "kubectl is required. Install from https://kubernetes.io/docs/tasks/tools/" + fi + + # Get namespace + read -p " Namespace [default]: " NAMESPACE + NAMESPACE="${NAMESPACE:-default}" + + # Get API key + read -p " OPENROUTER_API_KEY: " API_KEY + + info "Creating Kubernetes manifests..." + + mkdir -p k8s + + # Create secret + cat > k8s/secret.yaml << SECRET +apiVersion: v1 +kind: Secret +metadata: + name: ruvbot-secrets + namespace: $NAMESPACE +type: Opaque +stringData: + OPENROUTER_API_KEY: "$API_KEY" + DEFAULT_MODEL: "google/gemini-2.0-flash-001" +SECRET + + # Create deployment + cat > k8s/deployment.yaml << DEPLOYMENT +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ruvbot + namespace: $NAMESPACE +spec: + replicas: 2 + selector: + matchLabels: + app: ruvbot + template: + metadata: + labels: + app: ruvbot + spec: + containers: + - name: ruvbot + image: node:20-slim + command: ["sh", "-c", "npm install -g ruvbot && ruvbot start --port 3000"] + ports: + - containerPort: 3000 + envFrom: + - secretRef: + name: ruvbot-secrets + livenessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 60 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /ready + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 10 + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" +--- +apiVersion: v1 +kind: Service +metadata: + name: ruvbot + namespace: $NAMESPACE +spec: + selector: + app: ruvbot + ports: + - port: 80 + targetPort: 3000 + type: LoadBalancer +DEPLOYMENT + + success "Kubernetes manifests created in k8s/" + + read -p " Apply manifests now? [Y/n]: " APPLY_NOW + APPLY_NOW="${APPLY_NOW:-Y}" + + if [[ "$APPLY_NOW" =~ ^[Yy]$ ]]; then + kubectl apply -f k8s/ + + echo "" + success "Kubernetes resources created" + echo "" + echo " Check status:" + echo -e " ${DIM}kubectl get pods -l app=ruvbot${NC}" + echo "" + echo " Get service URL:" + echo -e " ${DIM}kubectl get svc ruvbot${NC}" + echo "" + fi +} + +# Deployment wizard +deployment_wizard() { + step "Deployment Options" + echo "" + echo " 1. Local (development)" + echo " 2. Docker" + echo " 3. Google Cloud Run" + echo " 4. Kubernetes" + echo " 5. Skip deployment" + echo "" + read -p " Select [5]: " DEPLOY_CHOICE + DEPLOY_CHOICE="${DEPLOY_CHOICE:-5}" + + case "$DEPLOY_CHOICE" in + 1) + info "Starting local development server..." + if [ "$RUVBOT_GLOBAL" = "true" ]; then + ruvbot start --debug + else + npx ruvbot start --debug + fi + ;; + 2) deploy_docker ;; + 3) deploy_cloudrun ;; + 4) deploy_k8s ;; + 5) info "Skipping deployment" ;; + *) warn "Invalid option, skipping deployment" ;; + esac +} + +# Interactive setup wizard +run_wizard() { + step "RuvBot Setup Wizard" + + # Ensure .env exists + touch .env 2>/dev/null || true + + # LLM Provider + echo "" + echo " ${BOLD}Step 1: LLM Provider${NC}" + echo " ───────────────────" + echo " 1. OpenRouter (Gemini 2.5, Claude, GPT - recommended)" + echo " 2. Anthropic (Claude only)" + echo " 3. Skip (configure later)" + read -p " Select [1]: " PROVIDER + PROVIDER="${PROVIDER:-1}" + + case "$PROVIDER" in + 1) + read -p " OPENROUTER_API_KEY: " OPENROUTER_KEY + { + echo "OPENROUTER_API_KEY=$OPENROUTER_KEY" + echo "DEFAULT_MODEL=google/gemini-2.0-flash-001" + } >> .env + success "OpenRouter configured" + ;; + 2) + read -p " ANTHROPIC_API_KEY: " ANTHROPIC_KEY + echo "ANTHROPIC_API_KEY=$ANTHROPIC_KEY" >> .env + success "Anthropic configured" + ;; + 3) info "Skipping LLM configuration" ;; + esac + + # Channel Configuration + echo "" + echo " ${BOLD}Step 2: Channel Integrations${NC}" + echo " ────────────────────────────" + echo " 1. Slack" + echo " 2. Discord" + echo " 3. Telegram" + echo " 4. All channels" + echo " 5. Skip (configure later)" + read -p " Select [5]: " CHANNELS + CHANNELS="${CHANNELS:-5}" + + case "$CHANNELS" in + 1) + install_channel_deps "slack" + configure_channel "slack" + ;; + 2) + install_channel_deps "discord" + configure_channel "discord" + ;; + 3) + install_channel_deps "telegram" + configure_channel "telegram" + ;; + 4) + install_channel_deps "all" + configure_channel "slack" + configure_channel "discord" + configure_channel "telegram" + ;; + 5) info "Skipping channel configuration" ;; + esac + + # Deployment + echo "" + echo " ${BOLD}Step 3: Deployment${NC}" + echo " ──────────────────" + deployment_wizard +} + +# Print next steps +print_next_steps() { + echo "" + echo -e "${BOLD}📚 Quick Start${NC}" + echo "═══════════════════════════════════════" + echo "" + echo " Configure LLM provider:" + echo -e " ${CYAN}export OPENROUTER_API_KEY=sk-or-...${NC}" + echo "" + echo " Run diagnostics:" + echo -e " ${CYAN}ruvbot doctor${NC}" + echo "" + echo " Start the bot:" + echo -e " ${CYAN}ruvbot start${NC}" + echo "" + echo " Channel setup guides:" + echo -e " ${CYAN}ruvbot channels setup slack${NC}" + echo -e " ${CYAN}ruvbot channels setup discord${NC}" + echo -e " ${CYAN}ruvbot channels setup telegram${NC}" + echo "" + echo " Deploy templates:" + echo -e " ${CYAN}ruvbot templates list${NC}" + echo -e " ${CYAN}ruvbot deploy code-reviewer${NC}" + echo "" + echo " Deploy to Cloud Run:" + echo -e " ${CYAN}ruvbot deploy cloudrun${NC}" + echo "" + echo -e "${DIM}Docs: https://github.com/ruvnet/ruvector/tree/main/npm/packages/ruvbot${NC}" + echo "" +} + +# Main main() { - echo "Checking requirements..." - echo "" + print_banner + check_dependencies + install_ruvbot - check_node - check_npm + # Handle channel installation + if [ -n "$RUVBOT_CHANNEL" ]; then + install_channel_deps "$RUVBOT_CHANNEL" + fi - install_ruvbot - show_next_steps + # Handle initialization + if [ "$RUVBOT_INIT" = "true" ]; then + init_project + fi + + # Handle wizard + if [ "$RUVBOT_WIZARD" = "true" ]; then + run_wizard + elif [ -n "$RUVBOT_DEPLOY" ]; then + # Handle deployment without wizard + case "$RUVBOT_DEPLOY" in + cloudrun|cloud-run|gcp) deploy_cloudrun ;; + docker) deploy_docker ;; + k8s|kubernetes) deploy_k8s ;; + *) warn "Unknown deployment target: $RUVBOT_DEPLOY" ;; + esac + fi + + print_next_steps } -main +main "$@" diff --git a/npm/packages/ruvbot/src/api/public/index.html b/npm/packages/ruvbot/src/api/public/index.html new file mode 100644 index 00000000..4517c6eb --- /dev/null +++ b/npm/packages/ruvbot/src/api/public/index.html @@ -0,0 +1,861 @@ + + + + + + RuvBot - AI Assistant + + + + + +
+ +
+
+ +
+ + +
+
+ +
+
+
+
🤖
+

Welcome to RuvBot

+

Enterprise-grade AI assistant with military-strength security, 150x faster vector search, and 12+ LLM models.

+
+ + + + +
+
+
+ +
+
+ + +
+
+
+ + + + + + diff --git a/npm/packages/ruvbot/src/cli/commands/channels.ts b/npm/packages/ruvbot/src/cli/commands/channels.ts new file mode 100644 index 00000000..94ab9442 --- /dev/null +++ b/npm/packages/ruvbot/src/cli/commands/channels.ts @@ -0,0 +1,411 @@ +/** + * RuvBot CLI - Channels Command + * + * Setup and manage channel integrations (Slack, Discord, Telegram, Webhooks). + */ + +import { Command } from 'commander'; +import chalk from 'chalk'; + +export function createChannelsCommand(): Command { + const channels = new Command('channels') + .alias('ch') + .description('Manage channel integrations'); + + // List channels + channels + .command('list') + .alias('ls') + .description('List available channel integrations') + .option('--json', 'Output as JSON') + .action((options) => { + const channelList = [ + { + name: 'slack', + description: 'Slack workspace integration via Bolt SDK', + package: '@slack/bolt', + status: 'available', + }, + { + name: 'discord', + description: 'Discord server integration via discord.js', + package: 'discord.js', + status: 'available', + }, + { + name: 'telegram', + description: 'Telegram bot integration via Telegraf', + package: 'telegraf', + status: 'available', + }, + { + name: 'webhook', + description: 'Generic webhook endpoint for custom integrations', + package: 'built-in', + status: 'available', + }, + ]; + + if (options.json) { + console.log(JSON.stringify(channelList, null, 2)); + return; + } + + console.log(chalk.bold('\n📡 Available Channel Integrations\n')); + console.log('─'.repeat(60)); + + for (const ch of channelList) { + const icon = getChannelIcon(ch.name); + console.log(`${icon} ${chalk.cyan(ch.name.padEnd(12))} ${ch.description}`); + console.log(` Package: ${chalk.gray(ch.package)}`); + console.log(); + } + + console.log('─'.repeat(60)); + console.log(chalk.gray('\nRun `ruvbot channels setup ` for setup instructions')); + }); + + // Setup channel + channels + .command('setup ') + .description('Show setup instructions for a channel') + .action((channel) => { + const normalizedChannel = channel.toLowerCase(); + + switch (normalizedChannel) { + case 'slack': + printSlackSetup(); + break; + case 'discord': + printDiscordSetup(); + break; + case 'telegram': + printTelegramSetup(); + break; + case 'webhook': + case 'webhooks': + printWebhookSetup(); + break; + default: + console.error(chalk.red(`Unknown channel: ${channel}`)); + console.log('\nAvailable channels: slack, discord, telegram, webhook'); + process.exit(1); + } + }); + + // Test channel connection + channels + .command('test ') + .description('Test channel connection') + .action(async (channel) => { + const normalizedChannel = channel.toLowerCase(); + console.log(chalk.cyan(`\nTesting ${normalizedChannel} connection...`)); + + const envVars = getRequiredEnvVars(normalizedChannel); + const missing = envVars.filter((v) => !process.env[v]); + + if (missing.length > 0) { + console.log(chalk.red('\n✗ Missing environment variables:')); + missing.forEach((v) => console.log(chalk.red(` - ${v}`))); + console.log(chalk.gray(`\nRun 'ruvbot channels setup ${normalizedChannel}' for instructions`)); + process.exit(1); + } + + console.log(chalk.green('✓ All required environment variables are set')); + console.log(chalk.gray('\nStart the bot with:')); + console.log(chalk.cyan(` ruvbot start --channel ${normalizedChannel}`)); + }); + + return channels; +} + +function getChannelIcon(channel: string): string { + const icons: Record = { + slack: '💬', + discord: '🎮', + telegram: '✈️', + webhook: '🔗', + }; + return icons[channel] || '📡'; +} + +function getRequiredEnvVars(channel: string): string[] { + switch (channel) { + case 'slack': + return ['SLACK_BOT_TOKEN', 'SLACK_SIGNING_SECRET', 'SLACK_APP_TOKEN']; + case 'discord': + return ['DISCORD_TOKEN', 'DISCORD_CLIENT_ID']; + case 'telegram': + return ['TELEGRAM_BOT_TOKEN']; + case 'webhook': + return []; + default: + return []; + } +} + +function printSlackSetup(): void { + console.log(chalk.bold('\n💬 Slack Integration Setup\n')); + console.log('═'.repeat(60)); + + console.log(chalk.bold('\n📋 Step 1: Create a Slack App\n')); + console.log(' 1. Go to: ' + chalk.cyan('https://api.slack.com/apps')); + console.log(' 2. Click "Create New App" → "From Scratch"'); + console.log(' 3. Name your app (e.g., "RuvBot") and select workspace'); + + console.log(chalk.bold('\n🔐 Step 2: Configure Bot Permissions\n')); + console.log(' Navigate to OAuth & Permissions and add these Bot Token Scopes:'); + console.log(chalk.gray(' ─────────────────────────────────────')); + console.log(' • app_mentions:read - Receive @mentions'); + console.log(' • chat:write - Send messages'); + console.log(' • channels:history - Read channel messages'); + console.log(' • im:history - Read direct messages'); + console.log(' • reactions:write - Add reactions'); + console.log(' • files:read - Access shared files'); + + console.log(chalk.bold('\n⚡ Step 3: Enable Socket Mode\n')); + console.log(' 1. Go to Socket Mode → Enable'); + console.log(' 2. Create App-Level Token with ' + chalk.cyan('connections:write') + ' scope'); + console.log(' 3. Save the ' + chalk.yellow('xapp-...') + ' token'); + + console.log(chalk.bold('\n📦 Step 4: Install & Get Tokens\n')); + console.log(' 1. Go to Install App → Install to Workspace'); + console.log(' 2. Copy Bot User OAuth Token: ' + chalk.yellow('xoxb-...')); + console.log(' 3. Copy Signing Secret from Basic Information'); + + console.log(chalk.bold('\n🔧 Step 5: Configure Environment\n')); + console.log(chalk.gray(' ─────────────────────────────────────')); + console.log(chalk.cyan(' export SLACK_BOT_TOKEN="xoxb-your-bot-token"')); + console.log(chalk.cyan(' export SLACK_SIGNING_SECRET="your-signing-secret"')); + console.log(chalk.cyan(' export SLACK_APP_TOKEN="xapp-your-app-token"')); + + console.log(chalk.bold('\n🚀 Step 6: Start RuvBot\n')); + console.log(chalk.cyan(' ruvbot start --channel slack')); + + console.log(chalk.bold('\n🌐 Webhook Mode (for Cloud Run)\n')); + console.log(' For serverless deployments, use webhook instead of Socket Mode:'); + console.log(' 1. Disable Socket Mode'); + console.log(' 2. Go to Event Subscriptions → Enable'); + console.log(' 3. Set Request URL: ' + chalk.cyan('https://your-ruvbot.run.app/slack/events')); + console.log(' 4. Subscribe to: message.channels, message.im, app_mention'); + + console.log('\n' + '═'.repeat(60)); + console.log(chalk.gray('Install optional dependency: npm install @slack/bolt @slack/web-api\n')); +} + +function printDiscordSetup(): void { + console.log(chalk.bold('\n🎮 Discord Integration Setup\n')); + console.log('═'.repeat(60)); + + console.log(chalk.bold('\n📋 Step 1: Create a Discord Application\n')); + console.log(' 1. Go to: ' + chalk.cyan('https://discord.com/developers/applications')); + console.log(' 2. Click "New Application" and name it'); + + console.log(chalk.bold('\n🤖 Step 2: Create a Bot\n')); + console.log(' 1. Go to Bot section → Add Bot'); + console.log(' 2. Enable Privileged Gateway Intents:'); + console.log(chalk.green(' ✓ MESSAGE CONTENT INTENT')); + console.log(chalk.green(' ✓ SERVER MEMBERS INTENT')); + console.log(' 3. Click "Reset Token" and copy the bot token'); + + console.log(chalk.bold('\n🆔 Step 3: Get Application IDs\n')); + console.log(' 1. Copy Application ID from General Information'); + console.log(' 2. Right-click your server → Copy Server ID (for testing)'); + + console.log(chalk.bold('\n📨 Step 4: Invite Bot to Server\n')); + console.log(' 1. Go to OAuth2 → URL Generator'); + console.log(' 2. Select scopes: ' + chalk.cyan('bot, applications.commands')); + console.log(' 3. Select permissions:'); + console.log(' • Send Messages'); + console.log(' • Read Message History'); + console.log(' • Add Reactions'); + console.log(' • Use Slash Commands'); + console.log(' 4. Open the generated URL to invite the bot'); + + console.log(chalk.bold('\n🔧 Step 5: Configure Environment\n')); + console.log(chalk.gray(' ─────────────────────────────────────')); + console.log(chalk.cyan(' export DISCORD_TOKEN="your-bot-token"')); + console.log(chalk.cyan(' export DISCORD_CLIENT_ID="your-application-id"')); + console.log(chalk.cyan(' export DISCORD_GUILD_ID="your-server-id" # Optional')); + + console.log(chalk.bold('\n🚀 Step 6: Start RuvBot\n')); + console.log(chalk.cyan(' ruvbot start --channel discord')); + + console.log('\n' + '═'.repeat(60)); + console.log(chalk.gray('Install optional dependency: npm install discord.js\n')); +} + +function printTelegramSetup(): void { + console.log(chalk.bold('\n✈️ Telegram Integration Setup\n')); + console.log('═'.repeat(60)); + + console.log(chalk.bold('\n📋 Step 1: Create a Bot with BotFather\n')); + console.log(' 1. Open Telegram and search for ' + chalk.cyan('@BotFather')); + console.log(' 2. Send ' + chalk.cyan('/newbot') + ' command'); + console.log(' 3. Follow prompts to name your bot'); + console.log(' 4. Copy the HTTP API token (format: ' + chalk.yellow('123456789:ABC-DEF...') + ')'); + + console.log(chalk.bold('\n🔧 Step 2: Configure Environment\n')); + console.log(chalk.gray(' ─────────────────────────────────────')); + console.log(chalk.cyan(' export TELEGRAM_BOT_TOKEN="your-bot-token"')); + + console.log(chalk.bold('\n🚀 Step 3: Start RuvBot (Polling Mode)\n')); + console.log(chalk.cyan(' ruvbot start --channel telegram')); + + console.log(chalk.bold('\n🌐 Webhook Mode (for Production/Cloud Run)\n')); + console.log(' For serverless deployments, use webhook mode:'); + console.log(chalk.gray(' ─────────────────────────────────────')); + console.log(chalk.cyan(' export TELEGRAM_BOT_TOKEN="your-bot-token"')); + console.log(chalk.cyan(' export TELEGRAM_WEBHOOK_URL="https://your-ruvbot.run.app/telegram/webhook"')); + + console.log(chalk.bold('\n📱 Step 4: Test Your Bot\n')); + console.log(' 1. Search for your bot by username in Telegram'); + console.log(' 2. Start a chat and send ' + chalk.cyan('/start')); + console.log(' 3. Send messages to interact with RuvBot'); + + console.log(chalk.bold('\n⚙️ Optional: Set Bot Commands\n')); + console.log(' Send to @BotFather:'); + console.log(chalk.cyan(' /setcommands')); + console.log(' Then paste:'); + console.log(chalk.gray(' start - Start the bot')); + console.log(chalk.gray(' help - Show help message')); + console.log(chalk.gray(' status - Check bot status')); + + console.log('\n' + '═'.repeat(60)); + console.log(chalk.gray('Install optional dependency: npm install telegraf\n')); +} + +function printWebhookSetup(): void { + console.log(chalk.bold('\n🔗 Webhook Integration Setup\n')); + console.log('═'.repeat(60)); + + console.log(chalk.bold('\n📋 Overview\n')); + console.log(' RuvBot provides webhook endpoints for custom integrations.'); + console.log(' Use webhooks to connect with any messaging platform or service.'); + + console.log(chalk.bold('\n🔌 Available Webhook Endpoints\n')); + console.log(chalk.gray(' ─────────────────────────────────────')); + console.log(` POST ${chalk.cyan('/webhook/message')} - Receive messages`); + console.log(` POST ${chalk.cyan('/webhook/event')} - Receive events`); + console.log(` GET ${chalk.cyan('/webhook/health')} - Health check`); + console.log(` POST ${chalk.cyan('/api/sessions/:id/chat')} - Chat endpoint`); + + console.log(chalk.bold('\n📤 Outbound Webhooks\n')); + console.log(' Configure RuvBot to send responses to your endpoint:'); + console.log(chalk.gray(' ─────────────────────────────────────')); + console.log(chalk.cyan(' export WEBHOOK_URL="https://your-service.com/callback"')); + console.log(chalk.cyan(' export WEBHOOK_SECRET="your-shared-secret"')); + + console.log(chalk.bold('\n📥 Inbound Webhook Format\n')); + console.log(' Send POST requests with JSON body:'); + console.log(chalk.gray(' ─────────────────────────────────────')); + console.log(chalk.cyan(` curl -X POST https://your-ruvbot.run.app/webhook/message \\ + -H "Content-Type: application/json" \\ + -H "X-Webhook-Secret: your-secret" \\ + -d '{ + "message": "Hello RuvBot!", + "userId": "user-123", + "channelId": "channel-456", + "metadata": {} + }'`)); + + console.log(chalk.bold('\n🔐 Security\n')); + console.log(' 1. Always use HTTPS in production'); + console.log(' 2. Set a webhook secret for signature verification'); + console.log(' 3. Validate the X-Webhook-Signature header'); + console.log(' 4. Enable IP allowlisting if possible'); + + console.log(chalk.bold('\n📋 Configuration File\n')); + console.log(chalk.gray(' ─────────────────────────────────────')); + console.log(chalk.cyan(` { + "channels": { + "webhook": { + "enabled": true, + "inbound": { + "path": "/webhook/message", + "secret": "\${WEBHOOK_SECRET}" + }, + "outbound": { + "url": "\${WEBHOOK_URL}", + "retries": 3, + "timeout": 30000 + } + } + } + }`)); + + console.log(chalk.bold('\n🚀 Start with Webhook Support\n')); + console.log(chalk.cyan(' ruvbot start --port 3000')); + console.log(chalk.gray(' # Webhooks are always available on the API server')); + + console.log('\n' + '═'.repeat(60) + '\n'); +} + +export function createWebhooksCommand(): Command { + const webhooks = new Command('webhooks') + .alias('wh') + .description('Configure webhook integrations'); + + // List webhooks + webhooks + .command('list') + .description('List configured webhooks') + .action(() => { + console.log(chalk.bold('\n🔗 Configured Webhooks\n')); + console.log('─'.repeat(50)); + + const outboundUrl = process.env.WEBHOOK_URL; + if (outboundUrl) { + console.log(chalk.green('✓ Outbound webhook:'), outboundUrl); + } else { + console.log(chalk.gray('○ No outbound webhook configured')); + } + + console.log(); + console.log('Inbound endpoints (always available):'); + console.log(` POST ${chalk.cyan('/webhook/message')}`); + console.log(` POST ${chalk.cyan('/webhook/event')}`); + console.log(` POST ${chalk.cyan('/api/sessions/:id/chat')}`); + console.log(); + }); + + // Test webhook + webhooks + .command('test ') + .description('Test a webhook endpoint') + .option('--payload ', 'Custom JSON payload') + .action(async (url, options) => { + console.log(chalk.cyan(`\nTesting webhook: ${url}\n`)); + + try { + const payload = options.payload + ? JSON.parse(options.payload) + : { test: true, timestamp: new Date().toISOString() }; + + const response = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + + if (response.ok) { + console.log(chalk.green('✓ Webhook responded successfully')); + console.log(` Status: ${response.status}`); + const body = await response.text(); + if (body) { + console.log(` Response: ${body.substring(0, 200)}`); + } + } else { + console.log(chalk.red('✗ Webhook failed')); + console.log(` Status: ${response.status}`); + } + } catch (error) { + console.log(chalk.red('✗ Failed to reach webhook')); + console.log(` Error: ${error instanceof Error ? error.message : 'Unknown'}`); + } + }); + + return webhooks; +} + +export default createChannelsCommand; diff --git a/npm/packages/ruvbot/src/cli/commands/deploy.ts b/npm/packages/ruvbot/src/cli/commands/deploy.ts new file mode 100644 index 00000000..a3116bb2 --- /dev/null +++ b/npm/packages/ruvbot/src/cli/commands/deploy.ts @@ -0,0 +1,488 @@ +/** + * RuvBot CLI - Deploy Command + * + * Deploy RuvBot to various cloud platforms with interactive wizards. + */ + +import { Command } from 'commander'; +import chalk from 'chalk'; +import ora from 'ora'; +import { execSync, spawn } from 'child_process'; + +export function createDeploymentCommand(): Command { + const deploy = new Command('deploy-cloud') + .alias('cloud') + .description('Deploy RuvBot to cloud platforms'); + + // Cloud Run deployment + deploy + .command('cloudrun') + .alias('gcp') + .description('Deploy to Google Cloud Run') + .option('--project ', 'GCP project ID') + .option('--region ', 'Cloud Run region', 'us-central1') + .option('--service ', 'Service name', 'ruvbot') + .option('--memory ', 'Memory allocation', '512Mi') + .option('--min-instances ', 'Minimum instances', '0') + .option('--max-instances ', 'Maximum instances', '10') + .option('--env-file ', 'Path to .env file') + .option('--yes', 'Skip confirmation prompts') + .action(async (options) => { + await deployToCloudRun(options); + }); + + // Docker deployment + deploy + .command('docker') + .description('Deploy with Docker/Docker Compose') + .option('--name ', 'Container name', 'ruvbot') + .option('--port ', 'Host port', '3000') + .option('--detach', 'Run in background', true) + .option('--env-file ', 'Path to .env file') + .action(async (options) => { + await deployToDocker(options); + }); + + // Kubernetes deployment + deploy + .command('k8s') + .alias('kubernetes') + .description('Deploy to Kubernetes cluster') + .option('--namespace ', 'Kubernetes namespace', 'default') + .option('--replicas ', 'Number of replicas', '2') + .option('--env-file ', 'Path to .env file') + .action(async (options) => { + await deployToK8s(options); + }); + + // Deployment wizard + deploy + .command('wizard') + .description('Interactive deployment wizard') + .action(async () => { + await runDeploymentWizard(); + }); + + // Status check + deploy + .command('status') + .description('Check deployment status') + .option('--platform ', 'Platform: cloudrun, docker, k8s') + .action(async (options) => { + await checkDeploymentStatus(options); + }); + + return deploy; +} + +async function deployToCloudRun(options: Record): Promise { + console.log(chalk.bold('\n☁️ Google Cloud Run Deployment\n')); + console.log('═'.repeat(50)); + + // Check gcloud + if (!commandExists('gcloud')) { + console.error(chalk.red('\n✗ gcloud CLI is required')); + console.log(chalk.gray(' Install from: https://cloud.google.com/sdk')); + process.exit(1); + } + + const spinner = ora('Checking gcloud authentication...').start(); + + try { + // Check authentication + const account = execSync('gcloud auth list --filter=status:ACTIVE --format="value(account)"', { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }).trim(); + + if (!account) { + spinner.fail('Not authenticated with gcloud'); + console.log(chalk.yellow('\nRun: gcloud auth login')); + process.exit(1); + } + + spinner.succeed(`Authenticated as ${account}`); + + // Get or prompt for project + let projectId = options.project as string; + if (!projectId) { + projectId = execSync('gcloud config get-value project', { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }).trim(); + + if (!projectId) { + console.error(chalk.red('\n✗ No project ID specified')); + console.log(chalk.gray(' Use --project or run: gcloud config set project ')); + process.exit(1); + } + } + + console.log(chalk.cyan(` Project: ${projectId}`)); + console.log(chalk.cyan(` Region: ${options.region}`)); + console.log(chalk.cyan(` Service: ${options.service}`)); + + // Enable APIs + spinner.start('Enabling required APIs...'); + execSync('gcloud services enable run.googleapis.com containerregistry.googleapis.com cloudbuild.googleapis.com', { + stdio: 'pipe', + }); + spinner.succeed('APIs enabled'); + + // Build environment variables + let envVars = ''; + if (options.envFile) { + const fs = await import('fs/promises'); + const envContent = await fs.readFile(options.envFile as string, 'utf-8'); + const vars = envContent + .split('\n') + .filter((line) => line.trim() && !line.startsWith('#')) + .map((line) => line.trim()) + .join(','); + envVars = `--set-env-vars="${vars}"`; + } + + // Check for Dockerfile + const fs = await import('fs/promises'); + let hasDockerfile = false; + try { + await fs.access('Dockerfile'); + hasDockerfile = true; + } catch { + // Create Dockerfile + spinner.start('Creating Dockerfile...'); + await fs.writeFile( + 'Dockerfile', + `FROM node:20-slim +WORKDIR /app +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* +RUN npm install -g ruvbot +RUN mkdir -p /app/data /app/plugins /app/skills +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\ + CMD curl -f http://localhost:\${PORT:-8080}/health || exit 1 +CMD ["ruvbot", "start", "--port", "8080"] +` + ); + spinner.succeed('Dockerfile created'); + } + + // Deploy + spinner.start('Deploying to Cloud Run (this may take a few minutes)...'); + + const deployCmd = [ + 'gcloud run deploy', + options.service, + '--source .', + '--platform managed', + `--region ${options.region}`, + '--allow-unauthenticated', + '--port 8080', + `--memory ${options.memory}`, + `--min-instances ${options.minInstances}`, + `--max-instances ${options.maxInstances}`, + envVars, + '--quiet', + ] + .filter(Boolean) + .join(' '); + + execSync(deployCmd, { stdio: 'inherit' }); + + // Get URL + const serviceUrl = execSync( + `gcloud run services describe ${options.service} --region ${options.region} --format='value(status.url)'`, + { encoding: 'utf-8' } + ).trim(); + + console.log('\n' + chalk.green('═'.repeat(50))); + console.log(chalk.bold.green('🚀 Deployment successful!')); + console.log(chalk.green('═'.repeat(50))); + console.log(`\n URL: ${chalk.cyan(serviceUrl)}`); + console.log(` Health: ${chalk.cyan(serviceUrl + '/health')}`); + console.log(` API: ${chalk.cyan(serviceUrl + '/api/status')}`); + console.log(`\n Test: ${chalk.gray(`curl ${serviceUrl}/health`)}`); + console.log(); + } catch (error) { + spinner.fail('Deployment failed'); + console.error(chalk.red(`\nError: ${error instanceof Error ? error.message : 'Unknown error'}`)); + process.exit(1); + } +} + +async function deployToDocker(options: Record): Promise { + console.log(chalk.bold('\n🐳 Docker Deployment\n')); + console.log('═'.repeat(50)); + + if (!commandExists('docker')) { + console.error(chalk.red('\n✗ Docker is required')); + console.log(chalk.gray(' Install from: https://docker.com')); + process.exit(1); + } + + const fs = await import('fs/promises'); + const spinner = ora('Creating docker-compose.yml...').start(); + + try { + const envFileMapping = options.envFile ? `env_file:\n - ${options.envFile}` : ''; + + const composeContent = `version: '3.8' +services: + ruvbot: + image: node:20-slim + container_name: ${options.name} + working_dir: /app + command: sh -c "npm install -g ruvbot && ruvbot start --port 3000" + ports: + - "${options.port}:3000" + ${envFileMapping} + environment: + - OPENROUTER_API_KEY=\${OPENROUTER_API_KEY} + - ANTHROPIC_API_KEY=\${ANTHROPIC_API_KEY} + - SLACK_BOT_TOKEN=\${SLACK_BOT_TOKEN} + - SLACK_SIGNING_SECRET=\${SLACK_SIGNING_SECRET} + - SLACK_APP_TOKEN=\${SLACK_APP_TOKEN} + - DISCORD_TOKEN=\${DISCORD_TOKEN} + - DISCORD_CLIENT_ID=\${DISCORD_CLIENT_ID} + - TELEGRAM_BOT_TOKEN=\${TELEGRAM_BOT_TOKEN} + volumes: + - ./data:/app/data + - ./plugins:/app/plugins + - ./skills:/app/skills + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + restart: unless-stopped +`; + + await fs.writeFile('docker-compose.yml', composeContent); + spinner.succeed('docker-compose.yml created'); + + // Create directories + await fs.mkdir('data', { recursive: true }); + await fs.mkdir('plugins', { recursive: true }); + await fs.mkdir('skills', { recursive: true }); + + if (options.detach) { + spinner.start('Starting containers...'); + execSync('docker-compose up -d', { stdio: 'pipe' }); + spinner.succeed('Containers started'); + + console.log('\n' + chalk.green('═'.repeat(50))); + console.log(chalk.bold.green('🚀 RuvBot is running!')); + console.log(chalk.green('═'.repeat(50))); + console.log(`\n URL: ${chalk.cyan(`http://localhost:${options.port}`)}`); + console.log(` Health: ${chalk.cyan(`http://localhost:${options.port}/health`)}`); + console.log(`\n Logs: ${chalk.gray('docker-compose logs -f')}`); + console.log(` Stop: ${chalk.gray('docker-compose down')}`); + console.log(); + } else { + console.log(chalk.cyan('\nRun: docker-compose up')); + } + } catch (error) { + spinner.fail('Docker deployment failed'); + console.error(chalk.red(`\nError: ${error instanceof Error ? error.message : 'Unknown error'}`)); + process.exit(1); + } +} + +async function deployToK8s(options: Record): Promise { + console.log(chalk.bold('\n☸️ Kubernetes Deployment\n')); + console.log('═'.repeat(50)); + + if (!commandExists('kubectl')) { + console.error(chalk.red('\n✗ kubectl is required')); + console.log(chalk.gray(' Install from: https://kubernetes.io/docs/tasks/tools/')); + process.exit(1); + } + + const fs = await import('fs/promises'); + const spinner = ora('Creating Kubernetes manifests...').start(); + + try { + await fs.mkdir('k8s', { recursive: true }); + + // Deployment manifest + const deployment = `apiVersion: apps/v1 +kind: Deployment +metadata: + name: ruvbot + namespace: ${options.namespace} +spec: + replicas: ${options.replicas} + selector: + matchLabels: + app: ruvbot + template: + metadata: + labels: + app: ruvbot + spec: + containers: + - name: ruvbot + image: node:20-slim + command: ["sh", "-c", "npm install -g ruvbot && ruvbot start --port 3000"] + ports: + - containerPort: 3000 + envFrom: + - secretRef: + name: ruvbot-secrets + livenessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 60 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /ready + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 10 + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" +--- +apiVersion: v1 +kind: Service +metadata: + name: ruvbot + namespace: ${options.namespace} +spec: + selector: + app: ruvbot + ports: + - port: 80 + targetPort: 3000 + type: LoadBalancer +`; + + await fs.writeFile('k8s/deployment.yaml', deployment); + + // Secret template + const secret = `apiVersion: v1 +kind: Secret +metadata: + name: ruvbot-secrets + namespace: ${options.namespace} +type: Opaque +stringData: + OPENROUTER_API_KEY: "YOUR_API_KEY" + DEFAULT_MODEL: "google/gemini-2.0-flash-001" +`; + + await fs.writeFile('k8s/secret.yaml', secret); + + spinner.succeed('Kubernetes manifests created in k8s/'); + + console.log('\n' + chalk.yellow('⚠️ Before applying:')); + console.log(chalk.gray(' 1. Edit k8s/secret.yaml with your API keys')); + console.log(chalk.gray(' 2. Review k8s/deployment.yaml')); + console.log('\n Apply with:'); + console.log(chalk.cyan(' kubectl apply -f k8s/')); + console.log('\n Check status:'); + console.log(chalk.cyan(' kubectl get pods -l app=ruvbot')); + console.log(); + } catch (error) { + spinner.fail('Kubernetes manifest creation failed'); + console.error(chalk.red(`\nError: ${error instanceof Error ? error.message : 'Unknown error'}`)); + process.exit(1); + } +} + +async function runDeploymentWizard(): Promise { + console.log(chalk.bold('\n🧙 RuvBot Deployment Wizard\n')); + console.log('═'.repeat(50)); + + // This would use inquirer or similar for interactive prompts + // For now, provide instructions + console.log('\nSelect a deployment target:\n'); + console.log(' 1. Google Cloud Run (serverless, auto-scaling)'); + console.log(' ' + chalk.cyan('ruvbot deploy-cloud cloudrun')); + console.log(); + console.log(' 2. Docker (local or server deployment)'); + console.log(' ' + chalk.cyan('ruvbot deploy-cloud docker')); + console.log(); + console.log(' 3. Kubernetes (production cluster)'); + console.log(' ' + chalk.cyan('ruvbot deploy-cloud k8s')); + console.log(); + console.log('For interactive setup, use the install script:'); + console.log(chalk.cyan(' RUVBOT_WIZARD=true curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/npm/packages/ruvbot/scripts/install.sh | bash')); + console.log(); +} + +async function checkDeploymentStatus(options: Record): Promise { + const platform = options.platform as string; + + console.log(chalk.bold('\n📊 Deployment Status\n')); + + if (!platform || platform === 'cloudrun') { + console.log(chalk.cyan('Cloud Run:')); + if (commandExists('gcloud')) { + try { + const services = execSync( + 'gcloud run services list --format="table(metadata.name,status.url,status.conditions[0].status)" 2>/dev/null', + { encoding: 'utf-8' } + ); + console.log(services || ' No services found'); + } catch { + console.log(chalk.gray(' Not configured or no services')); + } + } else { + console.log(chalk.gray(' gcloud CLI not installed')); + } + console.log(); + } + + if (!platform || platform === 'docker') { + console.log(chalk.cyan('Docker:')); + if (commandExists('docker')) { + try { + const containers = execSync( + 'docker ps --filter "name=ruvbot" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null', + { encoding: 'utf-8' } + ); + console.log(containers || ' No containers running'); + } catch { + console.log(chalk.gray(' No containers found')); + } + } else { + console.log(chalk.gray(' Docker not installed')); + } + console.log(); + } + + if (!platform || platform === 'k8s') { + console.log(chalk.cyan('Kubernetes:')); + if (commandExists('kubectl')) { + try { + const pods = execSync( + 'kubectl get pods -l app=ruvbot -o wide 2>/dev/null', + { encoding: 'utf-8' } + ); + console.log(pods || ' No pods found'); + } catch { + console.log(chalk.gray(' No pods found or not configured')); + } + } else { + console.log(chalk.gray(' kubectl not installed')); + } + console.log(); + } +} + +function commandExists(cmd: string): boolean { + try { + execSync(`which ${cmd}`, { stdio: 'pipe' }); + return true; + } catch { + return false; + } +} + +export default createDeploymentCommand; diff --git a/npm/packages/ruvbot/src/cli/commands/index.ts b/npm/packages/ruvbot/src/cli/commands/index.ts index 40bd8668..8bada77f 100644 --- a/npm/packages/ruvbot/src/cli/commands/index.ts +++ b/npm/packages/ruvbot/src/cli/commands/index.ts @@ -9,3 +9,6 @@ export { createMemoryCommand } from './memory.js'; export { createSecurityCommand } from './security.js'; export { createPluginsCommand } from './plugins.js'; export { createAgentCommand } from './agent.js'; +export { createChannelsCommand, createWebhooksCommand } from './channels.js'; +export { createTemplatesCommand, createDeployCommand } from './templates.js'; +export { createDeploymentCommand } from './deploy.js'; diff --git a/npm/packages/ruvbot/src/cli/commands/templates.ts b/npm/packages/ruvbot/src/cli/commands/templates.ts new file mode 100644 index 00000000..bee1e9eb --- /dev/null +++ b/npm/packages/ruvbot/src/cli/commands/templates.ts @@ -0,0 +1,213 @@ +/** + * RuvBot CLI - Templates Command + * + * Deploy pre-built agent templates with a single command. + */ + +import { Command } from 'commander'; +import { + TEMPLATES, + getTemplate, + listTemplates, + getTemplatesByCategory, + type Template, +} from '../../templates/index.js'; + +export function createTemplatesCommand(): Command { + const templates = new Command('templates') + .alias('t') + .description('Manage and deploy agent templates'); + + // List templates + templates + .command('list') + .alias('ls') + .option('-c, --category ', 'Filter by category (practical, intermediate, advanced, exotic)') + .option('--json', 'Output as JSON') + .description('List available templates') + .action(async (options) => { + const byCategory = getTemplatesByCategory(); + + if (options.json) { + console.log(JSON.stringify(byCategory, null, 2)); + return; + } + + console.log('\n🤖 RuvBot Template Library\n'); + console.log('Deploy with: npx ruvbot deploy \n'); + + const categories = options.category + ? { [options.category]: byCategory[options.category] || [] } + : byCategory; + + for (const [category, templates] of Object.entries(categories)) { + const emoji = getCategoryEmoji(category); + console.log(`${emoji} ${category.toUpperCase()}`); + console.log('─'.repeat(50)); + + for (const t of templates as Template[]) { + console.log(` ${t.id.padEnd(25)} ${t.name}`); + console.log(` ${''.padEnd(25)} ${dim(t.description)}`); + console.log(); + } + } + }); + + // Show template details + templates + .command('info ') + .description('Show detailed information about a template') + .action(async (templateId) => { + const template = getTemplate(templateId); + + if (!template) { + console.error(`Template "${templateId}" not found.`); + console.log('\nAvailable templates:'); + listTemplates().forEach(t => console.log(` - ${t.id}`)); + process.exit(1); + } + + console.log(`\n${getCategoryEmoji(template.category)} ${template.name}`); + console.log('═'.repeat(50)); + console.log(`\n${template.description}\n`); + + console.log('📋 Configuration:'); + console.log(` Topology: ${template.config.topology}`); + console.log(` Max Agents: ${template.config.maxAgents}`); + if (template.config.consensus) { + console.log(` Consensus: ${template.config.consensus}`); + } + if (template.config.memory) { + console.log(` Memory: ${template.config.memory}`); + } + if (template.config.workers?.length) { + console.log(` Workers: ${template.config.workers.join(', ')}`); + } + + console.log('\n🤖 Agents:'); + for (const agent of template.agents) { + console.log(` • ${agent.name} (${agent.type})`); + console.log(` ${dim(agent.role)}`); + } + + console.log('\n📝 Example:'); + console.log(` ${template.example}`); + console.log(); + }); + + return templates; +} + +export function createDeployCommand(): Command { + const deploy = new Command('deploy') + .argument('', 'Template to deploy') + .option('--name ', 'Custom name for the deployment') + .option('--model ', 'Override default LLM model') + .option('--dry-run', 'Show what would be deployed without executing') + .option('--background', 'Run in background') + .description('Deploy a template') + .action(async (templateId, options) => { + const template = getTemplate(templateId); + + if (!template) { + console.error(`Template "${templateId}" not found.`); + console.log('\nRun "npx ruvbot templates list" to see available templates.'); + process.exit(1); + } + + console.log(`\n🚀 Deploying: ${template.name}`); + console.log('─'.repeat(50)); + + if (options.dryRun) { + console.log('\n[DRY RUN] Would deploy:\n'); + showDeploymentPlan(template, options); + return; + } + + // Generate deployment commands + const commands = generateDeploymentCommands(template, options); + + console.log('\n📦 Initializing swarm...'); + console.log(dim(` ${commands.swarmInit}`)); + + console.log('\n🤖 Spawning agents:'); + for (const cmd of commands.agentSpawns) { + console.log(dim(` ${cmd}`)); + } + + if (commands.workerStarts.length > 0) { + console.log('\n⚙️ Starting background workers:'); + for (const cmd of commands.workerStarts) { + console.log(dim(` ${cmd}`)); + } + } + + console.log('\n✅ Deployment complete!'); + console.log(`\n📊 Monitor with: npx ruvbot status`); + console.log(`🛑 Stop with: npx ruvbot stop ${options.name || templateId}`); + }); + + return deploy; +} + +function showDeploymentPlan(template: Template, options: Record): void { + console.log(`Template: ${template.id}`); + console.log(`Category: ${template.category}`); + console.log(`Topology: ${template.config.topology}`); + console.log(`Max Agents: ${template.config.maxAgents}`); + console.log(); + console.log('Agents to spawn:'); + for (const agent of template.agents) { + console.log(` • ${agent.name} (${agent.type})`); + } + if (template.config.workers?.length) { + console.log(); + console.log('Workers to start:'); + for (const worker of template.config.workers) { + console.log(` • ${worker}`); + } + } +} + +interface DeploymentCommands { + swarmInit: string; + agentSpawns: string[]; + workerStarts: string[]; +} + +function generateDeploymentCommands( + template: Template, + options: Record +): DeploymentCommands { + const name = (options.name as string) || template.id; + + // Swarm initialization + const swarmInit = `npx @claude-flow/cli@latest swarm init --topology ${template.config.topology} --max-agents ${template.config.maxAgents}${template.config.consensus ? ` --consensus ${template.config.consensus}` : ''}`; + + // Agent spawn commands + const agentSpawns = template.agents.map(agent => { + const model = (options.model as string) || agent.model || 'google/gemini-2.0-flash-001'; + return `npx @claude-flow/cli@latest agent spawn -t ${agent.type} --name ${agent.name}`; + }); + + // Worker start commands + const workerStarts = (template.config.workers || []).map(worker => + `npx @claude-flow/cli@latest hooks worker dispatch --trigger ${worker}` + ); + + return { swarmInit, agentSpawns, workerStarts }; +} + +function getCategoryEmoji(category: string): string { + const emojis: Record = { + practical: '🔧', + intermediate: '⚡', + advanced: '🧠', + exotic: '🌌', + }; + return emojis[category] || '📦'; +} + +function dim(text: string): string { + return `\x1b[2m${text}\x1b[0m`; +} diff --git a/npm/packages/ruvbot/src/cli/index.ts b/npm/packages/ruvbot/src/cli/index.ts index 87b9273b..f245c5ac 100644 --- a/npm/packages/ruvbot/src/cli/index.ts +++ b/npm/packages/ruvbot/src/cli/index.ts @@ -31,8 +31,17 @@ import { createPluginsCommand, createAgentCommand, } from './commands/index.js'; +import { + createTemplatesCommand, + createDeployCommand, +} from './commands/templates.js'; +import { + createChannelsCommand, + createWebhooksCommand, +} from './commands/channels.js'; +import { createDeploymentCommand } from './commands/deploy.js'; -const VERSION = '0.1.0'; +const VERSION = '0.1.6'; export function createCLI(): Command { const program = new Command(); @@ -390,6 +399,11 @@ RUVBOT_LOG_LEVEL=info program.addCommand(createSecurityCommand()); program.addCommand(createPluginsCommand()); program.addCommand(createAgentCommand()); + program.addCommand(createTemplatesCommand()); + program.addCommand(createDeployCommand()); + program.addCommand(createChannelsCommand()); + program.addCommand(createWebhooksCommand()); + program.addCommand(createDeploymentCommand()); // ============================================================================ // Version Info diff --git a/npm/packages/ruvbot/src/server.ts b/npm/packages/ruvbot/src/server.ts index 707c7bf3..706adf89 100644 --- a/npm/packages/ruvbot/src/server.ts +++ b/npm/packages/ruvbot/src/server.ts @@ -11,6 +11,8 @@ import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'; import { URL } from 'node:url'; import { randomUUID } from 'node:crypto'; +import { readFileSync, existsSync } from 'node:fs'; +import { join, dirname } from 'node:path'; import pino from 'pino'; import { RuvBot, createRuvBot } from './RuvBot.js'; import { createAIDefenceGuard, type AIDefenceConfig } from './security/AIDefenceGuard.js'; @@ -104,15 +106,98 @@ function sendError(res: ServerResponse, statusCode: number, message: string, cod sendJSON(res, statusCode, { error: message, code: code || 'ERROR' }); } +// ============================================================================ +// Static File Serving +// ============================================================================ + +function serveStaticFile(res: ServerResponse, filePath: string, contentType: string): boolean { + try { + const content = readFileSync(filePath, 'utf-8'); + res.writeHead(200, { + 'Content-Type': contentType, + 'Cache-Control': 'public, max-age=3600', + }); + res.end(content); + return true; + } catch { + return false; + } +} + +function getChatUIPath(): string { + // Try multiple locations for the chat UI + // Works in both development (src/) and production (dist/) + const cwd = process.cwd(); + const possiblePaths = [ + // Docker/Cloud Run paths (WORKDIR /app) + join(cwd, 'dist', 'api', 'public', 'index.html'), + // Development paths + join(cwd, 'src', 'api', 'public', 'index.html'), + // Production paths (ESM) + join(cwd, 'dist', 'esm', 'api', 'public', 'index.html'), + // When running from node_modules + join(cwd, 'node_modules', 'ruvbot', 'dist', 'api', 'public', 'index.html'), + join(cwd, 'node_modules', 'ruvbot', 'src', 'api', 'public', 'index.html'), + // Absolute paths (for Docker) + '/app/dist/api/public/index.html', + '/app/src/api/public/index.html', + ]; + + for (const p of possiblePaths) { + if (existsSync(p)) { + logger.info({ path: p }, 'Found chat UI'); + return p; + } + } + + logger.warn({ cwd, paths: possiblePaths }, 'Chat UI not found, using fallback'); + return possiblePaths[0]; // Default to first path +} + // ============================================================================ // Route Handlers // ============================================================================ +async function handleRoot(ctx: RequestContext): Promise { + const { res } = ctx; + const chatUIPath = getChatUIPath(); + + if (existsSync(chatUIPath)) { + serveStaticFile(res, chatUIPath, 'text/html; charset=utf-8'); + } else { + // Fallback: serve a simple redirect or message + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(` + + + + RuvBot + + + +
+

🤖 RuvBot

+

Enterprise-grade AI Assistant

+ View API Status +
+ + + `); + } +} + async function handleHealth(ctx: RequestContext): Promise { const { res } = ctx; sendJSON(res, 200, { status: 'healthy', - version: '0.1.0', + version: '0.1.6', uptime: Math.floor((Date.now() - startTime) / 1000), timestamp: new Date().toISOString(), }); @@ -312,6 +397,7 @@ async function handleChat(ctx: RequestContext): Promise { // ============================================================================ const routes: Route[] = [ + { method: 'GET', pattern: /^\/$/, handler: handleRoot }, { method: 'GET', pattern: /^\/health$/, handler: handleHealth }, { method: 'GET', pattern: /^\/ready$/, handler: handleReady }, { method: 'GET', pattern: /^\/api\/status$/, handler: handleStatus }, diff --git a/npm/packages/ruvbot/src/templates/index.ts b/npm/packages/ruvbot/src/templates/index.ts new file mode 100644 index 00000000..ce4420ec --- /dev/null +++ b/npm/packages/ruvbot/src/templates/index.ts @@ -0,0 +1,358 @@ +/** + * RuvBot Template Library + * + * Pre-built templates for common long-running agent patterns. + * Deploy with: npx ruvbot deploy + */ + +export interface Template { + id: string; + name: string; + description: string; + category: 'practical' | 'intermediate' | 'advanced' | 'exotic'; + agents: AgentSpec[]; + config: TemplateConfig; + example: string; +} + +export interface AgentSpec { + type: string; + name: string; + role: string; + model?: string; + systemPrompt?: string; +} + +export interface TemplateConfig { + topology: 'hierarchical' | 'mesh' | 'star' | 'ring' | 'hive-mind'; + maxAgents: number; + consensus?: 'raft' | 'byzantine' | 'gossip' | 'crdt'; + memory?: 'local' | 'distributed' | 'hybrid'; + workers?: string[]; +} + +// ============================================================================= +// PRACTICAL TEMPLATES +// ============================================================================= + +export const CODE_REVIEWER: Template = { + id: 'code-reviewer', + name: 'Code Review Bot', + description: 'Automated code review with security scanning and best practices', + category: 'practical', + agents: [ + { type: 'reviewer', name: 'code-reviewer', role: 'Review code for quality and patterns' }, + { type: 'security-auditor', name: 'security-scanner', role: 'Scan for vulnerabilities' }, + ], + config: { + topology: 'star', + maxAgents: 3, + workers: ['audit', 'testgaps'], + }, + example: `npx ruvbot deploy code-reviewer --repo ./my-project`, +}; + +export const DOC_GENERATOR: Template = { + id: 'doc-generator', + name: 'Documentation Generator', + description: 'Auto-generate and maintain project documentation', + category: 'practical', + agents: [ + { type: 'researcher', name: 'code-analyzer', role: 'Analyze codebase structure' }, + { type: 'api-docs', name: 'doc-writer', role: 'Generate API documentation' }, + ], + config: { + topology: 'star', + maxAgents: 2, + workers: ['document', 'map'], + }, + example: `npx ruvbot deploy doc-generator --output ./docs`, +}; + +export const TEST_GENERATOR: Template = { + id: 'test-generator', + name: 'Test Suite Generator', + description: 'Generate comprehensive test suites with TDD approach', + category: 'practical', + agents: [ + { type: 'tester', name: 'test-writer', role: 'Write unit and integration tests' }, + { type: 'coder', name: 'mock-generator', role: 'Generate mocks and fixtures' }, + ], + config: { + topology: 'star', + maxAgents: 3, + workers: ['testgaps', 'benchmark'], + }, + example: `npx ruvbot deploy test-generator --coverage 80`, +}; + +// ============================================================================= +// INTERMEDIATE TEMPLATES +// ============================================================================= + +export const FEATURE_SWARM: Template = { + id: 'feature-swarm', + name: 'Feature Development Swarm', + description: 'Parallel feature development with coordinated agents', + category: 'intermediate', + agents: [ + { type: 'planner', name: 'architect', role: 'Design feature architecture' }, + { type: 'coder', name: 'implementer', role: 'Implement feature code' }, + { type: 'tester', name: 'qa', role: 'Write and run tests' }, + { type: 'reviewer', name: 'reviewer', role: 'Code review and refinement' }, + ], + config: { + topology: 'hierarchical', + maxAgents: 6, + consensus: 'raft', + memory: 'hybrid', + workers: ['optimize', 'testgaps'], + }, + example: `npx ruvbot deploy feature-swarm --feature "Add user auth"`, +}; + +export const REFACTOR_SQUAD: Template = { + id: 'refactor-squad', + name: 'Refactoring Squad', + description: 'Coordinated codebase refactoring across multiple files', + category: 'intermediate', + agents: [ + { type: 'system-architect', name: 'architect', role: 'Plan refactoring strategy' }, + { type: 'coder', name: 'refactorer-1', role: 'Execute refactoring' }, + { type: 'coder', name: 'refactorer-2', role: 'Execute refactoring' }, + { type: 'tester', name: 'regression', role: 'Ensure no regressions' }, + ], + config: { + topology: 'mesh', + maxAgents: 5, + consensus: 'raft', + memory: 'distributed', + workers: ['map', 'optimize'], + }, + example: `npx ruvbot deploy refactor-squad --pattern "extract-service"`, +}; + +export const CI_CD_PIPELINE: Template = { + id: 'ci-cd-pipeline', + name: 'CI/CD Pipeline Agent', + description: 'Automated build, test, and deployment pipeline', + category: 'intermediate', + agents: [ + { type: 'cicd-engineer', name: 'pipeline-manager', role: 'Orchestrate CI/CD' }, + { type: 'tester', name: 'test-runner', role: 'Execute test suites' }, + { type: 'security-auditor', name: 'security-gate', role: 'Security validation' }, + ], + config: { + topology: 'star', + maxAgents: 4, + workers: ['audit', 'benchmark'], + }, + example: `npx ruvbot deploy ci-cd-pipeline --trigger push`, +}; + +// ============================================================================= +// ADVANCED TEMPLATES +// ============================================================================= + +export const SELF_LEARNING_BOT: Template = { + id: 'self-learning-bot', + name: 'Self-Learning Assistant', + description: 'AI that improves from interactions using neural patterns', + category: 'advanced', + agents: [ + { type: 'safla-neural', name: 'learner', role: 'Learn from interactions' }, + { type: 'memory-coordinator', name: 'memory', role: 'Manage persistent memory' }, + { type: 'coder', name: 'executor', role: 'Execute learned patterns' }, + ], + config: { + topology: 'hierarchical', + maxAgents: 4, + consensus: 'raft', + memory: 'hybrid', + workers: ['ultralearn', 'consolidate', 'predict'], + }, + example: `npx ruvbot deploy self-learning-bot --domain "code-assistance"`, +}; + +export const RESEARCH_SWARM: Template = { + id: 'research-swarm', + name: 'Research Swarm', + description: 'Distributed research across multiple sources and domains', + category: 'advanced', + agents: [ + { type: 'researcher', name: 'lead-researcher', role: 'Coordinate research' }, + { type: 'researcher', name: 'web-researcher', role: 'Search web sources' }, + { type: 'researcher', name: 'code-researcher', role: 'Analyze codebases' }, + { type: 'analyst', name: 'synthesizer', role: 'Synthesize findings' }, + ], + config: { + topology: 'mesh', + maxAgents: 6, + consensus: 'gossip', + memory: 'distributed', + workers: ['deepdive', 'map'], + }, + example: `npx ruvbot deploy research-swarm --topic "vector databases"`, +}; + +export const PERFORMANCE_OPTIMIZER: Template = { + id: 'performance-optimizer', + name: 'Performance Optimizer', + description: 'Continuous performance monitoring and optimization', + category: 'advanced', + agents: [ + { type: 'perf-analyzer', name: 'profiler', role: 'Profile performance' }, + { type: 'performance-optimizer', name: 'optimizer', role: 'Implement optimizations' }, + { type: 'tester', name: 'benchmark-runner', role: 'Run benchmarks' }, + ], + config: { + topology: 'star', + maxAgents: 4, + memory: 'hybrid', + workers: ['optimize', 'benchmark'], + }, + example: `npx ruvbot deploy performance-optimizer --target ./src`, +}; + +// ============================================================================= +// EXOTIC TEMPLATES +// ============================================================================= + +export const BYZANTINE_VALIDATOR: Template = { + id: 'byzantine-validator', + name: 'Byzantine Fault-Tolerant Validator', + description: 'High-stakes validation with Byzantine consensus (tolerates 33% malicious)', + category: 'exotic', + agents: [ + { type: 'byzantine-coordinator', name: 'primary', role: 'Lead consensus' }, + { type: 'consensus-coordinator', name: 'validator-1', role: 'Validate decisions' }, + { type: 'consensus-coordinator', name: 'validator-2', role: 'Validate decisions' }, + { type: 'consensus-coordinator', name: 'validator-3', role: 'Validate decisions' }, + { type: 'security-manager', name: 'crypto-verifier', role: 'Cryptographic verification' }, + ], + config: { + topology: 'mesh', + maxAgents: 7, + consensus: 'byzantine', + memory: 'distributed', + workers: ['audit'], + }, + example: `npx ruvbot deploy byzantine-validator --quorum 4`, +}; + +export const HIVE_MIND: Template = { + id: 'hive-mind', + name: 'Hive-Mind Collective', + description: 'Emergent collective intelligence with queen-led coordination', + category: 'exotic', + agents: [ + { type: 'queen-coordinator', name: 'queen', role: 'Strategic orchestration' }, + { type: 'worker-specialist', name: 'worker-1', role: 'Task execution' }, + { type: 'worker-specialist', name: 'worker-2', role: 'Task execution' }, + { type: 'worker-specialist', name: 'worker-3', role: 'Task execution' }, + { type: 'scout-explorer', name: 'scout-1', role: 'Information reconnaissance' }, + { type: 'scout-explorer', name: 'scout-2', role: 'Information reconnaissance' }, + { type: 'swarm-memory-manager', name: 'memory-sync', role: 'Distributed memory' }, + { type: 'collective-intelligence-coordinator', name: 'hive-brain', role: 'Collective decisions' }, + ], + config: { + topology: 'hive-mind', + maxAgents: 15, + consensus: 'crdt', + memory: 'distributed', + workers: ['ultralearn', 'consolidate', 'predict', 'map'], + }, + example: `npx ruvbot deploy hive-mind --objective "Build complete app"`, +}; + +export const MULTI_REPO_COORDINATOR: Template = { + id: 'multi-repo-coordinator', + name: 'Multi-Repository Coordinator', + description: 'Coordinate changes across multiple repositories', + category: 'exotic', + agents: [ + { type: 'repo-architect', name: 'coordinator', role: 'Cross-repo orchestration' }, + { type: 'sync-coordinator', name: 'sync-manager', role: 'Version synchronization' }, + { type: 'pr-manager', name: 'pr-coordinator', role: 'PR management' }, + { type: 'release-manager', name: 'release', role: 'Release coordination' }, + ], + config: { + topology: 'hierarchical', + maxAgents: 8, + consensus: 'raft', + memory: 'distributed', + workers: ['audit', 'document'], + }, + example: `npx ruvbot deploy multi-repo-coordinator --repos "repo1,repo2,repo3"`, +}; + +export const ADVERSARIAL_TESTER: Template = { + id: 'adversarial-tester', + name: 'Adversarial Security Tester', + description: 'Red team vs blue team security testing with adversarial agents', + category: 'exotic', + agents: [ + { type: 'security-architect', name: 'red-team-lead', role: 'Attack planning' }, + { type: 'security-auditor', name: 'attacker-1', role: 'Execute attacks' }, + { type: 'security-auditor', name: 'attacker-2', role: 'Execute attacks' }, + { type: 'security-manager', name: 'blue-team-lead', role: 'Defense coordination' }, + { type: 'security-auditor', name: 'defender', role: 'Implement defenses' }, + ], + config: { + topology: 'mesh', + maxAgents: 6, + consensus: 'byzantine', + memory: 'distributed', + workers: ['audit', 'deepdive'], + }, + example: `npx ruvbot deploy adversarial-tester --target ./api`, +}; + +// ============================================================================= +// TEMPLATE REGISTRY +// ============================================================================= + +export const TEMPLATES: Record = { + // Practical + 'code-reviewer': CODE_REVIEWER, + 'doc-generator': DOC_GENERATOR, + 'test-generator': TEST_GENERATOR, + + // Intermediate + 'feature-swarm': FEATURE_SWARM, + 'refactor-squad': REFACTOR_SQUAD, + 'ci-cd-pipeline': CI_CD_PIPELINE, + + // Advanced + 'self-learning-bot': SELF_LEARNING_BOT, + 'research-swarm': RESEARCH_SWARM, + 'performance-optimizer': PERFORMANCE_OPTIMIZER, + + // Exotic + 'byzantine-validator': BYZANTINE_VALIDATOR, + 'hive-mind': HIVE_MIND, + 'multi-repo-coordinator': MULTI_REPO_COORDINATOR, + 'adversarial-tester': ADVERSARIAL_TESTER, +}; + +export const TEMPLATE_LIST = Object.values(TEMPLATES); + +export function getTemplate(id: string): Template | undefined { + return TEMPLATES[id]; +} + +export function listTemplates(category?: Template['category']): Template[] { + if (category) { + return TEMPLATE_LIST.filter(t => t.category === category); + } + return TEMPLATE_LIST; +} + +export function getTemplatesByCategory(): Record { + return { + practical: listTemplates('practical'), + intermediate: listTemplates('intermediate'), + advanced: listTemplates('advanced'), + exotic: listTemplates('exotic'), + }; +}