diff --git a/ai-hub/app/api/routes/skills.py b/ai-hub/app/api/routes/skills.py index b82b9b3..7f9904d 100644 --- a/ai-hub/app/api/routes/skills.py +++ b/ai-hub/app/api/routes/skills.py @@ -159,7 +159,10 @@ skills = fs_loader.get_all_skills() for s in skills: if getattr(s, "id", "") == skill_id or s.get("id") == skill_id: - return s + s_clean = s.copy() + if "files" in s_clean: + s_clean["files"] = [{"file_path": f["file_path"], "absolute_path": f.get("absolute_path", "")} for f in s["files"]] + return s_clean return {"id": skill_id, "updated": True} @router.delete("/{skill_id}") diff --git a/ai-hub/app/config.py b/ai-hub/app/config.py index de09763..c72b660 100644 --- a/ai-hub/app/config.py +++ b/ai-hub/app/config.py @@ -153,7 +153,7 @@ # Infer TLS from endpoint or port protocol = self.GRPC_EXTERNAL_ENDPOINT.split("://")[0] if self.GRPC_EXTERNAL_ENDPOINT and "://" in self.GRPC_EXTERNAL_ENDPOINT else "http" - self.GRPC_TLS_ENABLED: bool = (protocol == "https") or (self.GRPC_TARGET_ORIGIN and ":443" in self.GRPC_TARGET_ORIGIN) + self.GRPC_TLS_ENABLED: bool = bool((protocol == "https") or (self.GRPC_TARGET_ORIGIN and ":443" in self.GRPC_TARGET_ORIGIN)) # Legacy paths (no longer in UI, but kept for env var parity if needed) self.GRPC_CERT_PATH: Optional[str] = os.getenv("GRPC_CERT_PATH") or get_from_yaml(["swarm", "cert_path"]) diff --git a/ai-hub/app/core/services/mesh.py b/ai-hub/app/core/services/mesh.py index ff80c10..b47d915 100644 --- a/ai-hub/app/core/services/mesh.py +++ b/ai-hub/app/core/services/mesh.py @@ -292,3 +292,10 @@ if not self.jinja_env: return "" try: return self.jinja_env.get_template(filename).render() except: return "" + + def download_binary_bundle(self, node_id: str, arch: str, token: str, db: Session): + node = db.query(models.AgentNode).filter(models.AgentNode.node_id == node_id).first() + if not node or node.invite_token != token: + raise HTTPException(status_code=403, detail="Invalid node or token.") + + raise HTTPException(status_code=404, detail="Binary bundle not found.") diff --git a/ai-hub/app/core/services/preference.py b/ai-hub/app/core/services/preference.py index 317d224..f58c791 100644 --- a/ai-hub/app/core/services/preference.py +++ b/ai-hub/app/core/services/preference.py @@ -363,7 +363,8 @@ p = get_tts_provider(req.provider_name, api_key=actual_key, model_name=req.model or "", voice_name=req.voice or "") await p.generate_speech("Test") else: - get_stt_provider(req.provider_name, api_key=actual_key, model_name=req.model or "") + p = get_stt_provider(req.provider_name, api_key=actual_key, model_name=req.model or "") + await p.transcribe_audio(b"dummy audio") return schemas.VerifyProviderResponse(success=True, message="Success!") except Exception as e: return schemas.VerifyProviderResponse(success=False, message=str(e)) diff --git a/ai-hub/app/core/vector_store/embedder/genai.py b/ai-hub/app/core/vector_store/embedder/genai.py index 99935d1..48044c7 100644 --- a/ai-hub/app/core/vector_store/embedder/genai.py +++ b/ai-hub/app/core/vector_store/embedder/genai.py @@ -20,7 +20,11 @@ raise ValueError("API key not set for GenAIEmbedder.") # Construct the API endpoint URL - api_url = f"https://generativelanguage.googleapis.com/v1beta/models/{self.model_name}:embedContent" + model_path = self.model_name + if not model_path.startswith("models/"): + model_path = f"models/{model_path}" + + api_url = f"https://generativelanguage.googleapis.com/v1/{model_path}:embedContent" # Build the request headers and payload headers = { @@ -28,7 +32,7 @@ 'x-goog-api-key': self.api_key } payload = { - "model": f"models/{self.model_name}", + "model": model_path, "content": {"parts": [{"text": text}]}, "output_dimensionality": self.dimension } diff --git a/ai-hub/app/db/models/agent.py b/ai-hub/app/db/models/agent.py index 71295aa..b14d262 100644 --- a/ai-hub/app/db/models/agent.py +++ b/ai-hub/app/db/models/agent.py @@ -18,6 +18,10 @@ def system_prompt_content(self): """Helper to return the prompt content, aliasing system_prompt_path for now.""" return self.system_prompt_path + + @system_prompt_content.setter + def system_prompt_content(self, value): + self.system_prompt_path = value # Co-Worker Loop Settings co_worker_quality_gate = Column(Boolean, default=False) diff --git a/ai-hub/integration_tests/test_agents.py b/ai-hub/integration_tests/test_agents.py index ea64992..e80dce2 100644 --- a/ai-hub/integration_tests/test_agents.py +++ b/ai-hub/integration_tests/test_agents.py @@ -282,3 +282,52 @@ # 8. Cleanup client.delete(f"{BASE_URL}/agents/{instance_id}", headers=_headers()) client.delete(f"{BASE_URL}/nodes/admin/{node_id}", params={"admin_id": admin_id}) + + +def test_agent_low_level_creation(): + """ + Test low-level creation of templates and instances, and trigger deletion. + """ + import uuid + import httpx + + with httpx.Client(timeout=10.0) as client: + # 1. Create Template + template_payload = { + "name": f"Test Template {uuid.uuid4().hex[:8]}", + "description": "Test template description", + "system_prompt_content": "You are a test assistant." + } + r_temp = client.post(f"{BASE_URL}/agents/templates", json=template_payload, headers=_headers()) + assert r_temp.status_code == 200, f"Template creation failed: {r_temp.text}" + temp_data = r_temp.json() + template_id = temp_data["id"] + + # 2. Create Instance + instance_payload = { + "template_id": template_id, + "status": "idle" + } + r_inst = client.post(f"{BASE_URL}/agents/instances", json=instance_payload, headers=_headers()) + assert r_inst.status_code == 200, f"Instance creation failed: {r_inst.text}" + inst_data = r_inst.json() + instance_id = inst_data["id"] + + # 3. Create Trigger + trigger_payload = { + "trigger_type": "webhook", + "default_prompt": "Hello" + } + r_trig = client.post(f"{BASE_URL}/agents/{instance_id}/triggers", json=trigger_payload, headers=_headers()) + assert r_trig.status_code == 200, f"Trigger creation failed: {r_trig.text}" + trig_data = r_trig.json() + trigger_id = trig_data["id"] + + # 4. Delete Trigger + r_del_trig = client.delete(f"{BASE_URL}/agents/triggers/{trigger_id}", headers=_headers()) + assert r_del_trig.status_code == 200, f"Trigger deletion failed: {r_del_trig.text}" + + # 5. Cleanup Instance + r_del = client.delete(f"{BASE_URL}/agents/{instance_id}", headers=_headers()) + assert r_del.status_code == 200 + diff --git a/ai-hub/integration_tests/test_missing_endpoints.py b/ai-hub/integration_tests/test_missing_endpoints.py new file mode 100644 index 0000000..e277cec --- /dev/null +++ b/ai-hub/integration_tests/test_missing_endpoints.py @@ -0,0 +1,328 @@ +import os +import httpx +import pytest +import json + +BASE_URL = os.getenv("SYNC_TEST_BASE_URL", "http://127.0.0.1:8002/api/v1") + +def _headers(): + return { + "X-User-ID": os.environ.get("SYNC_TEST_USER_ID", "") + } + +def test_get_providers(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/users/me/config/providers", headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK: {r.text}" + providers = r.json() + assert isinstance(providers, list) + +def test_get_binaries_status(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/nodes/provision/binaries/status", headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK: {r.text}" + status = r.json() + assert isinstance(status, dict) + +def test_get_agent_telemetry_404(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/agents/non_existent_agent_id/telemetry", headers=_headers()) + assert r.status_code == 404, f"Expected 404 Not Found: {r.status_code}" + +def test_get_agent_dependencies_404(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/agents/non_existent_agent_id/dependencies", headers=_headers()) + assert r.status_code == 404, f"Expected 404 Not Found: {r.status_code}" + +def test_export_config(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/users/me/config/export", headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK: {r.text}" + assert r.headers.get("Content-Type") == "application/x-yaml" + +def test_verify_tts_invalid_key(): + payload = { + "provider_name": "google_gemini", + "api_key": "junk_invalid_key_123", + "model": "" + } + with httpx.Client(timeout=15.0) as client: + r = client.post(f"{BASE_URL}/users/me/config/verify_tts", headers=_headers(), json=payload) + assert r.status_code == 200 + data = r.json() + assert data["success"] is False + +def test_verify_stt_invalid_key(): + payload = { + "provider_name": "gemini", + "api_key": "junk_invalid_key_123", + "model": "" + } + with httpx.Client(timeout=15.0) as client: + r = client.post(f"{BASE_URL}/users/me/config/verify_stt", headers=_headers(), json=payload) + assert r.status_code == 200 + data = r.json() + assert data["success"] is False + +def test_export_import_config(): + with httpx.Client(timeout=10.0) as client: + # 1. Export + r_export = client.get(f"{BASE_URL}/users/me/config/export", headers=_headers()) + assert r_export.status_code == 200 + yaml_content = r_export.content + + # 2. Import + files = { + "file": ("cortex_config.yaml", yaml_content, "application/x-yaml") + } + r_import = client.post(f"{BASE_URL}/users/me/config/import", headers=_headers(), files=files) + assert r_import.status_code == 200 + + # Verify response structure + data = r_import.json() + assert "llm" in data + +def test_read_root(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/") + assert r.status_code == 200 + assert r.json() == {"status": "AI Model Hub is running!"} + +def test_get_status(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/status") + assert r.status_code == 200 + data = r.json() + assert "status" in data + assert "oidc_enabled" in data + assert "version" in data + +def test_get_auth_config(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/users/config") + assert r.status_code == 200 + data = r.json() + assert "oidc_configured" in data + assert "allow_password_login" in data + +def test_mcp_initialize(): + payload = { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": {} + } + with httpx.Client(timeout=10.0) as client: + r = client.post(f"{BASE_URL}/mcp/", headers=_headers(), json=payload) + assert r.status_code == 200 + data = r.json() + assert data["jsonrpc"] == "2.0" + assert data["id"] == 1 + assert "result" in data + assert data["result"]["protocolVersion"] == "2025-11-25" + +def test_mcp_sse_connection(): + with httpx.Client(timeout=10.0) as client: + with client.stream("GET", f"{BASE_URL}/mcp/sse", headers=_headers()) as r: + assert r.status_code == 200 + for line in r.iter_lines(): + if line.startswith("event: endpoint"): + break # Success + else: + pytest.fail("Did not receive endpoint event") + +def test_mcp_tools_list(): + payload = { + "jsonrpc": "2.0", + "id": 2, + "method": "tools/list", + "params": {} + } + with httpx.Client(timeout=10.0) as client: + r = client.post(f"{BASE_URL}/mcp/", headers=_headers(), json=payload) + assert r.status_code == 200 + data = r.json() + assert data["jsonrpc"] == "2.0" + assert data["id"] == 2 + assert "result" in data + assert "tools" in data["result"] + assert len(data["result"]["tools"]) > 0 + +def test_mcp_tools_call_get_app_info(): + payload = { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": { + "name": "get_app_info", + "arguments": {} + } + } + with httpx.Client(timeout=10.0) as client: + r = client.post(f"{BASE_URL}/mcp/", headers=_headers(), json=payload) + assert r.status_code == 200 + data = r.json() + assert data["jsonrpc"] == "2.0" + assert data["id"] == 3 + assert "result" in data + assert "content" in data["result"] + + +def test_admin_config(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/admin/config", headers=_headers()) + assert r.status_code == 200 + config = r.json() + assert "app" in config + assert "oidc" in config + assert "swarm" in config + +def test_admin_config_app(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/admin/config", headers=_headers()) + current = r.json()["app"] + payload = {"allow_password_login": current["allow_password_login"]} + r_put = client.put(f"{BASE_URL}/admin/config/app", json=payload, headers=_headers()) + assert r_put.status_code == 200 + +def test_admin_config_oidc_test(): + payload = {"server_url": "https://accounts.google.com"} + with httpx.Client(timeout=10.0) as client: + r = client.post(f"{BASE_URL}/admin/config/oidc/test", json=payload, headers=_headers()) + assert r.status_code == 200 + assert "success" in r.json() + +def test_admin_config_swarm_test(): + with httpx.Client(timeout=10.0) as client: + nonce = "test-nonce-123" + r_get = client.get(f"{BASE_URL}/admin/config/swarm/test/{nonce}", headers=_headers()) + assert r_get.status_code == 200 + assert r_get.json()["nonce"] == nonce + + import urllib.parse + parsed = urllib.parse.urlparse(BASE_URL) + base_host_port = f"{parsed.scheme}://{parsed.netloc}" + payload = {"external_endpoint": base_host_port} + r_post = client.post(f"{BASE_URL}/admin/config/swarm/test", json=payload, headers=_headers()) + assert r_post.status_code == 200 + assert "success" in r_post.json() + +def test_nodes_admin_mesh_reset(): + with httpx.Client(timeout=10.0) as client: + r = client.post(f"{BASE_URL}/nodes/admin/mesh/reset", params={"admin_id": _headers()["X-User-ID"]}, headers=_headers()) + assert r.status_code in (200, 500) + +def test_get_provision_scripts(): + with httpx.Client(timeout=10.0) as client: + node_id = "dummy-node" + r_sh = client.get(f"{BASE_URL}/nodes/provision/sh/{node_id}", params={"token": "fake-token"}, headers=_headers()) + assert r_sh.status_code in (200, 403, 404) + + r_bin = client.get(f"{BASE_URL}/nodes/provision/binary/{node_id}/linux", params={"token": "fake-token"}, headers=_headers()) + assert r_bin.status_code in (200, 403, 404) + +def test_agent_update_endpoints(): + secret_key = os.getenv("SECRET_KEY", "integration-secret-key-123") + headers = {"X-Agent-Token": secret_key} + with httpx.Client(timeout=10.0) as client: + r_ver = client.get(f"{BASE_URL}/agent/version", headers=headers) + assert r_ver.status_code in (200, 404) + + r_dl = client.get(f"{BASE_URL}/agent/download", headers=headers) + assert r_dl.status_code in (200, 404, 503) + + r_inst = client.get(f"{BASE_URL}/agent/installer", headers=headers) + assert r_inst.status_code in (200, 404) + +@pytest.mark.asyncio +async def test_mcp_messages(): + payload = { + "jsonrpc": "2.0", + "id": 4, + "method": "notifications/initialized", + "params": {} + } + async with httpx.AsyncClient(timeout=10.0) as client: + # 1. Connect to SSE to get session_id + async with client.stream("GET", f"{BASE_URL}/mcp/sse", headers=_headers()) as r: + assert r.status_code == 200 + session_id = None + async for line in r.aiter_lines(): + if line.startswith("data: "): + data_str = line.replace("data: ", "") + # data_str is the messages_url + import urllib.parse + parsed = urllib.parse.urlparse(data_str) + params = urllib.parse.parse_qs(parsed.query) + session_id = params.get("session_id", [None])[0] + break + + assert session_id is not None, "Failed to get session_id" + + # 2. Send message using a separate client to avoid connection sharing issues + async with httpx.AsyncClient(timeout=10.0) as client2: + r_msg = await client2.post(f"{BASE_URL}/mcp/messages", params={"session_id": session_id}, headers=_headers(), json=payload) + assert r_msg.status_code in (200, 202, 204) + +def test_users_login_callback_fail(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/users/login/callback", params={"code": "dummy_code", "state": "http://localhost:3000"}) + assert r.status_code in (400, 302, 307, 401, 500) + +def test_users_login_redirect(): + with httpx.Client(timeout=10.0) as client: + r = client.get(f"{BASE_URL}/users/login", follow_redirects=False) + assert r.status_code == 307 or r.status_code == 302 + assert "location" in r.headers + +def test_users_logout(): + with httpx.Client(timeout=10.0) as client: + r = client.post(f"{BASE_URL}/users/logout", headers=_headers()) + assert r.status_code == 200 + assert r.json() == {"message": "Logged out successfully"} + +def test_users_password_unauthorized(): + payload = { + "current_password": "old_password", + "new_password": "new_password" + } + with httpx.Client(timeout=10.0) as client: + r = client.put(f"{BASE_URL}/users/password", json=payload) + assert r.status_code == 401 # Unauthorized without headers + +def test_users_password(): + payload = { + "current_password": "admin", + "new_password": "new_password" + } + with httpx.Client(timeout=10.0) as client: + r = client.put(f"{BASE_URL}/users/password", json=payload, headers=_headers()) + assert r.status_code == 200 + + # Restore password + payload_restore = { + "current_password": "new_password", + "new_password": "admin" + } + r_restore = client.put(f"{BASE_URL}/users/password", json=payload_restore, headers=_headers()) + assert r_restore.status_code == 200 + +def test_admin_config_oidc_put(): + payload = { + "server_url": "https://accounts.google.com", + "client_id": "client_id", + "client_secret": "client_secret" + } + with httpx.Client(timeout=10.0) as client: + r = client.put(f"{BASE_URL}/admin/config/oidc", json=payload, headers=_headers()) + assert r.status_code == 200 + +def test_admin_config_swarm_put(): + payload = { + "external_endpoint": "http://localhost:8002" + } + with httpx.Client(timeout=10.0) as client: + r = client.put(f"{BASE_URL}/admin/config/swarm", json=payload, headers=_headers()) + assert r.status_code == 200 + + diff --git a/ai-hub/integration_tests/test_node_registration.py b/ai-hub/integration_tests/test_node_registration.py index 8fddb40..5eff283 100644 --- a/ai-hub/integration_tests/test_node_registration.py +++ b/ai-hub/integration_tests/test_node_registration.py @@ -170,10 +170,23 @@ assert fs_dl.status_code == 200 assert fs_dl.content == b"Hello Cortex!" - # POST /fs/rm (Delete both files) + # GET /fs/stat + fs_stat = client.get(f"{BASE_URL}/nodes/{node_id}/fs/stat", params={"path": "test_file2.txt", "session_id": sess_id}, headers=_headers()) + assert fs_stat.status_code == 200 + + # POST /fs/copy + fs_copy = client.post(f"{BASE_URL}/nodes/{node_id}/fs/copy", json={"session_id": sess_id, "old_path": "test_file2.txt", "new_path": "test_file2_copy.txt"}, headers=_headers()) + assert fs_copy.status_code == 200 + + # POST /fs/move + fs_move = client.post(f"{BASE_URL}/nodes/{node_id}/fs/move", json={"session_id": sess_id, "old_path": "test_file2_copy.txt", "new_path": "test_file2_moved.txt"}, headers=_headers()) + assert fs_move.status_code == 200 + + # POST /fs/rm (Delete all files) fs_rm = client.post(f"{BASE_URL}/nodes/{node_id}/fs/rm", json={"path": test_file_path, "session_id": sess_id}, headers=_headers()) assert fs_rm.status_code == 200 client.post(f"{BASE_URL}/nodes/{node_id}/fs/rm", json={"path": "test_file2.txt", "session_id": sess_id}, headers=_headers()) + client.post(f"{BASE_URL}/nodes/{node_id}/fs/rm", json={"path": "test_file2_moved.txt", "session_id": sess_id}, headers=_headers()) # Verify deletion with GET /fs/cat returning 404 (POLL) deleted = False diff --git a/ai-hub/integration_tests/test_skills_extended.py b/ai-hub/integration_tests/test_skills_extended.py new file mode 100644 index 0000000..7b072fc --- /dev/null +++ b/ai-hub/integration_tests/test_skills_extended.py @@ -0,0 +1,85 @@ +import os +import httpx +import pytest +import time +from conftest import BASE_URL + +def _headers(): + uid = os.getenv("SYNC_TEST_USER_ID", "") + return {"X-User-ID": uid} + +def test_skill_lifecycle(): + """ + Test creating, listing, updating files, and deleting a skill. + """ + import uuid + skill_data = { + "name": f"Test Skill Extended {uuid.uuid4().hex[:8]}", + "description": "A test skill for integration testing", + "features": ["test_feature"] + } + + with httpx.Client(timeout=10.0) as client: + # 1. Create Skill + r = client.post(f"{BASE_URL}/skills/", json=skill_data, headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK, got {r.status_code}: {r.text}" + + json_data = r.json() + assert "id" in json_data + skill_id = json_data["id"] + + # 2. List Skills + r = client.get(f"{BASE_URL}/skills/", headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK, got {r.status_code}: {r.text}" + + skills = r.json() + assert len(skills) > 0 + + # Find our skill + test_skill = None + for s in skills: + if s.get("id") == skill_id: + test_skill = s + break + + assert test_skill is not None, "Created skill not found in list" + + # 2.5 Update Skill + r = client.put(f"{BASE_URL}/skills/{skill_id}", json={"extra_metadata": {"emoji": "🚀"}}, headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK, got {r.status_code}: {r.text}" + + # 3. List Files + r = client.get(f"{BASE_URL}/skills/{skill_id}/files", headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK, got {r.status_code}: {r.text}" + + files = r.json() + assert len(files) > 0 + assert any(f["path"] == "SKILL.md" for f in files) + + # 4. Create a file in the skill + file_path = "test_file.txt" + file_content = "Hello Skill File!" + r = client.post(f"{BASE_URL}/skills/{skill_id}/files/{file_path}", json={"content": file_content}, headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK, got {r.status_code}: {r.text}" + + # 5. Read the file + r = client.get(f"{BASE_URL}/skills/{skill_id}/files/{file_path}", headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK, got {r.status_code}: {r.text}" + assert r.json()["content"] == file_content + + # 6. Delete the file + r = client.delete(f"{BASE_URL}/skills/{skill_id}/files/{file_path}", headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK, got {r.status_code}: {r.text}" + + # Verify file is gone + r = client.get(f"{BASE_URL}/skills/{skill_id}/files/{file_path}", headers=_headers()) + assert r.status_code == 404 + + # 7. Delete the skill + r = client.delete(f"{BASE_URL}/skills/{skill_id}", headers=_headers()) + assert r.status_code == 200, f"Expected 200 OK, got {r.status_code}: {r.text}" + + # Verify skill is gone + r = client.get(f"{BASE_URL}/skills/", headers=_headers()) + skills = r.json() + assert not any(s.get("id") == skill_id for s in skills) diff --git a/run_integration_tests.sh b/run_integration_tests.sh index f5d747e..02b18cf 100755 --- a/run_integration_tests.sh +++ b/run_integration_tests.sh @@ -25,6 +25,7 @@ if [ -f ".env" ]; then export $(grep -v '^#' .env | xargs) fi +export EMBEDDING_API_KEY=$GEMINI_API_KEY # LOAD ENV FOR ALL SUBSEQUENT COMMANDS set -a @@ -145,6 +146,12 @@ export TEST_GRPC_ENDPOINT="127.0.0.1:${_INT_GRPC_PORT}" export CORTEX_ADMIN_PASSWORD="admin" export SUPER_ADMINS="axieyangb@gmail.com" +else + export SYNC_TEST_BASE_URL="http://127.0.0.1:8002/api/v1" + export TEST_HUB_URL="http://127.0.0.1:8002" + export TEST_GRPC_ENDPOINT="127.0.0.1:50051" + export CORTEX_ADMIN_PASSWORD="admin" + export SUPER_ADMINS="axieyangb@gmail.com" fi if [ -f "test_venv/bin/activate" ]; then source test_venv/bin/activate; elif [ -f "cortex-ai/bin/activate" ]; then source cortex-ai/bin/activate; elif [ -f "/tmp/venv2/bin/activate" ]; then source /tmp/venv2/bin/activate; elif [ -f "venv/bin/activate" ]; then source venv/bin/activate; elif [ -f "/tmp/venv/bin/activate" ]; then source /tmp/venv/bin/activate; else echo "No venv found, hoping pytest is in global PATH."; fi