diff --git a/.claude/commands/oracle.md b/.claude/commands/oracle.md index 838c4d84..85f45caf 100644 --- a/.claude/commands/oracle.md +++ b/.claude/commands/oracle.md @@ -6,9 +6,14 @@ If arguments were provided, Oracle should interpret them and act accordingly: - Business/onboarding intent ("quero começar", "plano pra minha empresa", "o que isso pode fazer pelo meu negócio") → run the full 8-step flow (detect state → initial-setup if needed → business discovery → delegate to Scout/Echo → present potential → delegate to Compass for the plan → deliver with 3 autonomy paths) - Knowledge question ("quais agentes existem?", "como crio uma rotina?", "o que mudou na última release?") → answer directly by reading the repo -If no arguments were provided, Oracle should greet the user as a consultant and offer two clear paths upfront: +If no arguments were provided, Oracle must FIRST run Step 0 (detect workspace state) before greeting — never assume the user is new. Read `config/workspace.yaml`, glob `workspace/*/`, check `memory/` for content, and check whether the scheduler has run recently. Classify the workspace as **fresh install**, **fully configured**, or **partial**, then branch the greeting accordingly: -1. **Consultoria de negócio + plano de implementação** — "me conta sobre sua empresa e eu monto um plano personalizado do que você pode automatizar aqui" (this is the primary path for new users) -2. **Tirar uma dúvida pontual** — agentes, skills, rotinas, integrações, dashboard, configuração (for users who just want information) +- **Fresh install** (no owner/company set, empty memory, no recent activity) → greet as a consultant and offer two paths upfront: + 1. **Consultoria de negócio + plano de implementação** — "me conta sobre sua empresa e eu monto um plano personalizado do que você pode automatizar aqui" (the primary path for new users) + 2. **Tirar uma dúvida pontual** — agentes, skills, rotinas, integrações, dashboard, configuração (for users who just want information) -Do NOT dump a menu of workspace topics without first offering the consulting path. The business consultation is Oracle's main job, not a footnote. +- **Fully configured** (owner/company set, memory populated, scheduler/heartbeats active) → do NOT pitch onboarding. Greet as a returning operator: open with a short state recap (active projects, recent sessions, anything time-sensitive), then offer to (a) review/continue what's in progress, (b) start something new, or (c) answer a specific question. + +- **Partial** → greet, explain what's already configured vs. missing, and ask whether to resume setup or proceed with work. + +Do NOT dump a menu of workspace topics. For a fresh install, the business consultation is Oracle's main job, not a footnote; for a configured workspace, lead with the user's actual state, not a generic pitch. diff --git a/.env.example b/.env.example index 11610d8e..1ebe24fd 100644 --- a/.env.example +++ b/.env.example @@ -108,6 +108,13 @@ META_APP_SECRET= LINKEDIN_CLIENT_ID= LINKEDIN_CLIENT_SECRET= +# ── License — headless auto-activation ─────────────── +# Set this to the email used in your first manual license registration. +# On startup, EvoNexus calls /v1/register/auto silently and skips the manual +# setup screen. Falls back to manual setup if the email isn't registered yet. +# Leave empty (or unset) to keep the default behavior. +# EVOLUTION_OPERATOR_EMAIL=operator@example.com + # ── Evolution API ──────────────────────────────────── # Your Evolution API instance URL and global API key EVOLUTION_API_URL= diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f4fd5855..8070b35b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Harassment, discrimination, or abusive behavior will not be tolerated. ### Reporting Bugs -1. Check existing [issues](https://github.com/EvolutionAPI/evo-nexus/issues) +1. Check existing [issues](https://github.com/evolution-foundation/evo-nexus/issues) to avoid duplicates 2. Open a new issue with: - Clear, descriptive title diff --git a/README.md b/README.md index 73e204c7..70be4e38 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@
-
+
@@ -46,7 +46,7 @@ It turns a single CLI installation into a team of **38 specialized agents** orga
## Part of the Evolution Foundation ecosystem
-EvoNexus is one of the projects maintained by Evolution Foundation. It is the operating layer that orchestrates the Foundation's own work — including the development of [Evo CRM Community](https://github.com/EvolutionAPI/evo-crm-community), [Evolution API](https://github.com/EvolutionAPI/evolution-api) and [Evolution Go](https://github.com/EvolutionAPI/evolution-go).
+EvoNexus is one of the projects maintained by Evolution Foundation. It is the operating layer that orchestrates the Foundation's own work — including the development of [Evo CRM Community](https://github.com/evolution-foundation/evo-crm-community), [Evolution API](https://github.com/evolution-foundation/evolution-api) and [Evolution Go](https://github.com/evolution-foundation/evolution-go).
### Why EvoNexus?
@@ -101,7 +101,7 @@ EvoNexus is one of the projects maintained by Evolution Foundation. It is the op
### Method 1 — Docker (no setup, runs anywhere)
```bash
-curl -O https://raw.githubusercontent.com/EvolutionAPI/evo-nexus/main/docker-compose.hub.yml
+curl -O https://raw.githubusercontent.com/evolution-foundation/evo-nexus/main/docker-compose.hub.yml
docker compose -f docker-compose.hub.yml up -d
open http://localhost:8080
```
@@ -117,7 +117,7 @@ npx @evoapi/evo-nexus
### Method 3 — Manual clone (developers / contributors)
```bash
-git clone --depth 1 https://github.com/EvolutionAPI/evo-nexus.git
+git clone --depth 1 https://github.com/evolution-foundation/evo-nexus.git
cd evo-nexus
# Interactive setup wizard
diff --git a/dashboard/backend/licensing.py b/dashboard/backend/licensing.py
index 60ed26f2..9677cf24 100644
--- a/dashboard/backend/licensing.py
+++ b/dashboard/backend/licensing.py
@@ -4,12 +4,14 @@
Protocol:
POST /v1/register/direct — register with email/name, receive api_key
+ POST /v1/register/auto — headless register by email (must exist server-side)
POST /v1/activate — validate existing api_key on startup
GET /api/geo — geo-lookup from client IP
"""
import hashlib
import hmac as hmac_mod
+import os
import socket
import uuid
import logging
@@ -155,6 +157,24 @@ def direct_register(email: str, name: str, instance_id: str,
return _post("/v1/register/direct", payload)
+# ── Auto Registration (email-only, headless) ──
+
+def auto_register(email: str, instance_id: str) -> dict:
+ """Headless registration using only the operator email.
+
+ The customer must already exist on the licensing server (one prior manual
+ registration). Used by the EVOLUTION_OPERATOR_EMAIL env-var flow.
+
+ Returns {api_key, customer_id, tier, status}.
+ """
+ return _post("/v1/register/auto", {
+ "email": email,
+ "tier": TIER,
+ "instance_id": instance_id,
+ "version": VERSION,
+ })
+
+
# ── Activation (startup with existing api_key) ──
def activate(instance_id: str, api_key: str) -> bool:
@@ -260,8 +280,54 @@ def initialize_runtime():
# ── Auto-register for existing installs ──────
+def try_auto_register_from_env(instance_id: str) -> bool:
+ """Headless activation via EVOLUTION_OPERATOR_EMAIL env var.
+
+ Requires the email to already exist on the licensing server (one prior
+ manual registration). Returns True on success.
+
+ Failures are silent — caller falls back to the existing admin-based or
+ manual setup flow.
+ """
+ email = os.environ.get("EVOLUTION_OPERATOR_EMAIL", "").strip()
+ if not email:
+ return False
+
+ try:
+ result = auto_register(email=email, instance_id=instance_id)
+ except requests.HTTPError as e:
+ status = e.response.status_code if e.response is not None else "?"
+ if status == 404:
+ logger.info("Auto-activation skipped — email not registered yet (first time?).")
+ else:
+ logger.warning(f"Auto-activation rejected ({status}): falling back to manual flow.")
+ return False
+ except Exception as e:
+ logger.warning(f"Auto-activation skipped — {e}")
+ return False
+
+ api_key = result.get("api_key")
+ if not api_key:
+ logger.warning("Auto-activation response missing api_key")
+ return False
+
+ set_runtime_config("api_key", api_key)
+ set_runtime_config("tier", result.get("tier", TIER))
+ if result.get("customer_id"):
+ set_runtime_config("customer_id", str(result["customer_id"]))
+ set_runtime_config("version", VERSION)
+ set_runtime_config("registered_at", datetime.now(timezone.utc).isoformat())
+
+ ctx = get_context()
+ ctx.api_key = api_key
+ ctx.instance_id = instance_id
+ logger.info("License activated automatically via EVOLUTION_OPERATOR_EMAIL")
+ return True
+
+
def auto_register_if_needed():
- """If users exist but no license, register retroactively."""
+ """If no license yet, try EVOLUTION_OPERATOR_EMAIL first, then fall back to
+ the admin-based retroactive flow."""
try:
instance_id = get_runtime_config("instance_id")
api_key = get_runtime_config("api_key")
@@ -270,6 +336,15 @@ def auto_register_if_needed():
initialize_runtime()
return
+ if not instance_id:
+ instance_id = generate_instance_id()
+ set_runtime_config("instance_id", instance_id)
+
+ # First-class path: silent activation from env var.
+ if try_auto_register_from_env(instance_id):
+ return
+
+ # Fallback: if there's an admin user already, register retroactively.
from models import User
if User.query.count() == 0:
return
@@ -278,10 +353,6 @@ def auto_register_if_needed():
if not admin or not admin.email:
return
- if not instance_id:
- instance_id = generate_instance_id()
- set_runtime_config("instance_id", instance_id)
-
setup_perform(
email=admin.email or "",
name=admin.display_name or admin.username,