mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[Haik]: ckpt, fixed nine router inter dependency issues with local and prod sharing ports. Also centralized all port handling to a ports.config.json in the root dir
This commit is contained in:
@@ -55,31 +55,44 @@ function CallbackContent() {
|
||||
}
|
||||
|
||||
// Method 4: Direct exchange via OpenSwarm backend (works even when postMessage fails)
|
||||
// Fetch pending OAuth data from OpenSwarm, then call 9Router's exchange endpoint
|
||||
// The backend port is dynamic (8325 dev, 8324+ prod), so try known ports and
|
||||
// use whichever has a matching pending state entry.
|
||||
if (code && state) {
|
||||
(async () => {
|
||||
try {
|
||||
const pendingRes = await fetch(`http://localhost:8324/api/subscriptions/pending/${encodeURIComponent(state)}`);
|
||||
if (pendingRes.ok) {
|
||||
const pending = await pendingRes.json();
|
||||
if (pending.provider && pending.code_verifier) {
|
||||
const exchangeRes = await fetch(`/api/oauth/${pending.provider}/exchange`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
code,
|
||||
redirectUri: pending.redirect_uri,
|
||||
codeVerifier: pending.code_verifier,
|
||||
state,
|
||||
}),
|
||||
});
|
||||
if (exchangeRes.ok) {
|
||||
console.log("Direct exchange successful");
|
||||
}
|
||||
const portsToTry = [8325, 8324];
|
||||
let pending = null;
|
||||
for (const port of portsToTry) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`http://localhost:${port}/api/subscriptions/pending/${encodeURIComponent(state)}`,
|
||||
{ signal: AbortSignal.timeout(1000) },
|
||||
);
|
||||
if (res.ok) {
|
||||
pending = await res.json();
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
/* port not reachable, try next */
|
||||
}
|
||||
}
|
||||
if (pending?.provider && pending?.code_verifier) {
|
||||
try {
|
||||
const exchangeRes = await fetch(`/api/oauth/${pending.provider}/exchange`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
code,
|
||||
redirectUri: pending.redirect_uri,
|
||||
codeVerifier: pending.code_verifier,
|
||||
state,
|
||||
}),
|
||||
});
|
||||
if (exchangeRes.ok) {
|
||||
console.log("Direct exchange successful");
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Direct exchange failed:", e);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Direct exchange fallback failed:", e);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
+9
-9
@@ -52,7 +52,7 @@ For other integrations, edit `backend/.env`:
|
||||
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `BACKEND_PORT` | Backend server port (default: `8324`) |
|
||||
| `BACKEND_PORT` | Backend server port (default: `8325` for dev; prod uses `8324`) |
|
||||
| `GOOGLE_OAUTH_CLIENT_ID` | Google Workspace integration (Gmail, Calendar, Drive) |
|
||||
| `GOOGLE_OAUTH_CLIENT_SECRET` | Google Workspace integration |
|
||||
| `APPLE_ID` | macOS code signing & notarization (release builds only) |
|
||||
@@ -70,14 +70,14 @@ For other integrations, edit `backend/.env`:
|
||||
bash run/local.sh
|
||||
```
|
||||
|
||||
This starts the backend (port 8324), frontend (port 3000), and Electron shell together. The script handles virtual environments and dependency installation automatically.
|
||||
This starts the backend (port 8325), frontend (port 3000), and Electron shell together. The script handles virtual environments and dependency installation automatically.
|
||||
|
||||
### Option B: Run services individually
|
||||
|
||||
**Backend** (in one terminal):
|
||||
|
||||
```bash
|
||||
bash backend/run.sh # API at http://localhost:8324 — docs at /docs
|
||||
bash backend/run.sh # API at http://localhost:8325 — docs at /docs
|
||||
```
|
||||
|
||||
**Frontend** (in another terminal):
|
||||
@@ -94,7 +94,7 @@ bash frontend/run.sh # App at http://localhost:3000
|
||||
cd backend
|
||||
source .venv/bin/activate
|
||||
cd ..
|
||||
python -m uvicorn backend.main:app --host 0.0.0.0 --port 8324 --reload --reload-dir backend
|
||||
python -m uvicorn backend.main:app --host 0.0.0.0 --port 8325 --reload --reload-dir backend
|
||||
```
|
||||
|
||||
**Terminal 2 — Frontend dev server:**
|
||||
@@ -113,8 +113,8 @@ Once everything is running:
|
||||
| Service | URL |
|
||||
|---------|-----|
|
||||
| **Frontend (UI)** | [http://localhost:3000](http://localhost:3000) |
|
||||
| **Backend API** | [http://localhost:8324](http://localhost:8324) |
|
||||
| **API Docs (Swagger)** | [http://localhost:8324/docs](http://localhost:8324/docs) |
|
||||
| **Backend API** | [http://localhost:8325](http://localhost:8325) |
|
||||
| **API Docs (Swagger)** | [http://localhost:8325/docs](http://localhost:8325/docs) |
|
||||
|
||||
---
|
||||
|
||||
@@ -144,7 +144,7 @@ To use Google Calendar, Gmail, Drive, and other Google tools from your agents, y
|
||||
- Add your Google account as a test user (required while the app is in "Testing" status)
|
||||
4. Back on the credentials page, create an **OAuth client ID**:
|
||||
- Application type: **Web application**
|
||||
- Authorized redirect URIs: `http://localhost:8324/api/tools/oauth/callback`
|
||||
- Authorized redirect URIs: `http://localhost:8325/api/tools/oauth/callback`
|
||||
5. Copy the **Client ID** and **Client Secret**
|
||||
|
||||
### c. Add credentials to your `.env`
|
||||
@@ -239,11 +239,11 @@ Please open an issue first for larger changes so we can discuss the approach.
|
||||
Make sure you're running from the **project root** (not from `backend/`):
|
||||
```bash
|
||||
cd openswarm
|
||||
python -m uvicorn backend.main:app --host 0.0.0.0 --port 8324 --reload
|
||||
python -m uvicorn backend.main:app --host 0.0.0.0 --port 8325 --reload
|
||||
```
|
||||
|
||||
### Frontend proxy errors / API calls failing
|
||||
The frontend dev server proxies `/api` requests to `http://localhost:8324`. Make sure the backend is running first.
|
||||
The frontend dev server proxies `/api` requests to `http://localhost:8325`. Make sure the backend is running first.
|
||||
|
||||
### Mock mode vs real mode
|
||||
If you see mock responses, either:
|
||||
|
||||
@@ -89,7 +89,7 @@ cd openswarm
|
||||
bash run/local.sh
|
||||
```
|
||||
|
||||
This starts the backend (port 8324), frontend (port 3000), and Electron shell together. Once running, set your Anthropic API key in the in-app Settings page.
|
||||
This starts the backend (port 8325), frontend (port 3000), and Electron shell together. Once running, set your Anthropic API key in the in-app Settings page.
|
||||
|
||||
See the **[Contributing Guide](CONTRIBUTING.md)** for detailed setup options, environment configuration, Google Workspace integration, and troubleshooting.
|
||||
|
||||
@@ -101,7 +101,7 @@ See the **[Contributing Guide](CONTRIBUTING.md)** for detailed setup options, en
|
||||
Electron Shell (desktop wrapper, auto-updater)
|
||||
├─────────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ Frontend (React/TypeScript :3000) Backend (FastAPI :8324) │
|
||||
│ Frontend (React/TypeScript :3000) Backend (FastAPI :8325) │
|
||||
│ ┌───────────────────────────────┐ ┌───────────────────────┐ │
|
||||
│ │ Spatial Dashboard Canvas │◄────►│ REST API (/api/*) │ │
|
||||
│ │ Agent Chat (streaming) │ │ WebSocket (/ws/*) │ │
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# =============================================================================
|
||||
# Backend Server
|
||||
# =============================================================================
|
||||
BACKEND_PORT=8324
|
||||
# Dev default is 8325 (avoids collision with prod's 8324).
|
||||
# Override via OPENSWARM_PORT env var or ports.config.json.
|
||||
BACKEND_PORT=8325
|
||||
|
||||
GOOGLE_OAUTH_CLIENT_ID=your-google-oauth-client-id.apps.googleusercontent.com
|
||||
GOOGLE_OAUTH_CLIENT_SECRET=your-google-oauth-client-secret
|
||||
|
||||
@@ -26,6 +26,7 @@ from backend.apps.tools_lib.tools_lib import (
|
||||
load_builtin_permissions,
|
||||
)
|
||||
from backend.apps.common.mcp_utils import sanitize_server_name as _sanitize_server_name
|
||||
from backend.ports import BACKEND_DEV_PORT, NINE_ROUTER_PORT
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -71,7 +72,7 @@ async def build_agent_options(
|
||||
|
||||
if not _browser_all_denied:
|
||||
browser_agent_server_path = os.path.join(os.path.dirname(__file__), "browser_agent_mcp_server.py")
|
||||
backend_port = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
backend_port = os.environ.get("OPENSWARM_PORT", str(BACKEND_DEV_PORT))
|
||||
pre_selected_bids = get_pre_selected_browser_ids(session.dashboard_id)
|
||||
mcp_servers["openswarm-browser-agent"] = {
|
||||
"command": sys.executable,
|
||||
@@ -91,7 +92,7 @@ async def build_agent_options(
|
||||
|
||||
if not _invoke_all_denied:
|
||||
invoke_agent_server_path = os.path.join(os.path.dirname(__file__), "invoke_agent_mcp_server.py")
|
||||
backend_port = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
backend_port = os.environ.get("OPENSWARM_PORT", str(BACKEND_DEV_PORT))
|
||||
mcp_servers["openswarm-invoke-agent"] = {
|
||||
"command": sys.executable,
|
||||
"args": [invoke_agent_server_path],
|
||||
@@ -128,7 +129,7 @@ async def build_agent_options(
|
||||
elif _9r_running():
|
||||
options_kwargs["env"] = {
|
||||
"ANTHROPIC_API_KEY": "9router",
|
||||
"ANTHROPIC_BASE_URL": "http://localhost:20128",
|
||||
"ANTHROPIC_BASE_URL": f"http://localhost:{NINE_ROUTER_PORT}",
|
||||
}
|
||||
options_kwargs["extra_args"] = {"bare": None}
|
||||
logger.info("[MCP-DEBUG] Using 9Router (bare mode)")
|
||||
|
||||
@@ -22,7 +22,7 @@ except ImportError:
|
||||
|
||||
from browser_agent_mcp_schemas import TOOLS # noqa: E402 (sibling script import)
|
||||
|
||||
BACKEND_PORT = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
BACKEND_PORT = os.environ.get("OPENSWARM_PORT", "8325")
|
||||
BACKEND_URL = f"http://127.0.0.1:{BACKEND_PORT}/api/agents/browser-agent/run"
|
||||
MODEL = os.environ.get("OPENSWARM_AGENT_MODEL", "sonnet")
|
||||
DASHBOARD_ID = os.environ.get("OPENSWARM_DASHBOARD_ID", "")
|
||||
|
||||
@@ -23,7 +23,7 @@ except ImportError:
|
||||
|
||||
from browser_mcp_schemas import TOOLS # noqa: E402 (sibling script import)
|
||||
|
||||
BACKEND_PORT = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
BACKEND_PORT = os.environ.get("OPENSWARM_PORT", "8325")
|
||||
BACKEND_URL = f"http://127.0.0.1:{BACKEND_PORT}/api/agents/browser/command"
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import os
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
BACKEND_PORT = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
BACKEND_PORT = os.environ.get("OPENSWARM_PORT", "8325")
|
||||
BACKEND_URL = f"http://127.0.0.1:{BACKEND_PORT}/api/agents/invoke-agent/run"
|
||||
PARENT_SESSION_ID = os.environ.get("OPENSWARM_PARENT_SESSION_ID", "")
|
||||
DASHBOARD_ID = os.environ.get("OPENSWARM_DASHBOARD_ID", "")
|
||||
|
||||
@@ -17,7 +17,7 @@ import httpx
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
NINE_ROUTER_PORT = 20128
|
||||
from backend.ports import NINE_ROUTER_PORT
|
||||
NINE_ROUTER_URL = f"http://localhost:{NINE_ROUTER_PORT}"
|
||||
NINE_ROUTER_API = f"{NINE_ROUTER_URL}/api"
|
||||
NINE_ROUTER_V1 = f"{NINE_ROUTER_URL}/v1"
|
||||
|
||||
@@ -19,7 +19,8 @@ def _check_9router() -> bool:
|
||||
"""Check if 9Router is running locally."""
|
||||
try:
|
||||
import httpx
|
||||
r = httpx.get("http://localhost:20128/v1/models", timeout=2.0)
|
||||
from backend.ports import NINE_ROUTER_PORT
|
||||
r = httpx.get(f"http://localhost:{NINE_ROUTER_PORT}/v1/models", timeout=2.0)
|
||||
return r.status_code == 200
|
||||
except Exception:
|
||||
return False
|
||||
@@ -146,9 +147,10 @@ def get_anthropic_client(settings: AppSettings) -> anthropic.AsyncAnthropic:
|
||||
|
||||
# Fall back to 9Router subscription (free for users with Claude/ChatGPT/Gemini subscriptions)
|
||||
if _check_9router():
|
||||
from backend.ports import NINE_ROUTER_PORT
|
||||
return anthropic.AsyncAnthropic(
|
||||
api_key="9router",
|
||||
base_url="http://localhost:20128",
|
||||
base_url=f"http://localhost:{NINE_ROUTER_PORT}",
|
||||
)
|
||||
|
||||
raise ValueError("No AI provider configured. Set an API key or connect a subscription.")
|
||||
|
||||
@@ -13,6 +13,8 @@ from typing import Any, Optional
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import httpx
|
||||
|
||||
from backend.ports import BACKEND_DEV_PORT
|
||||
from fastapi import HTTPException, Query
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
@@ -43,7 +45,7 @@ async def oauth_callback(code: str = Query(...), state: str = Query("")):
|
||||
|
||||
client_id = os.environ.get(provider.client_id_env, "")
|
||||
client_secret = os.environ.get(provider.client_secret_env, "")
|
||||
_port = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
_port = os.environ.get("OPENSWARM_PORT", str(BACKEND_DEV_PORT))
|
||||
redirect_uri = f"http://localhost:{_port}/api/tools/oauth/callback"
|
||||
|
||||
token_data: dict[str, str] = {
|
||||
@@ -166,7 +168,7 @@ async def oauth_start(tool_id: str):
|
||||
if not client_id:
|
||||
raise HTTPException(status_code=400, detail=f"{provider.client_id_env} not set in backend .env")
|
||||
|
||||
_port = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
_port = os.environ.get("OPENSWARM_PORT", str(BACKEND_DEV_PORT))
|
||||
redirect_uri = f"http://localhost:{_port}/api/tools/oauth/callback"
|
||||
provider_key = tool.oauth_provider or "google"
|
||||
state = f"{provider_key}:{tool_id}"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
|
||||
from backend.ports import BACKEND_DEV_PORT
|
||||
from fastapi import FastAPI, APIRouter
|
||||
# import debug
|
||||
from uuid import uuid4
|
||||
@@ -32,7 +33,7 @@ class MainApp:
|
||||
for sub_app in sub_apps:
|
||||
# debug(sub_app.name)
|
||||
await stack.enter_async_context(sub_app.lifespan())
|
||||
_port = os.environ.get("OPENSWARM_PORT", "8324")
|
||||
_port = os.environ.get("OPENSWARM_PORT", str(BACKEND_DEV_PORT))
|
||||
print(f"\nCheck out the API docs at: http://127.0.0.1:{_port}/docs\n")
|
||||
yield
|
||||
|
||||
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from backend.ports import get_backend_port
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from backend.config.Apps import MainApp
|
||||
@@ -68,7 +70,7 @@ if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
parser = argparse.ArgumentParser(description="OpenSwarm backend server")
|
||||
parser.add_argument("--port", type=int, default=int(os.environ.get("OPENSWARM_PORT", "8324")))
|
||||
parser.add_argument("--port", type=int, default=get_backend_port())
|
||||
parser.add_argument("--host", default=os.environ.get("OPENSWARM_HOST", "127.0.0.1"))
|
||||
parser.add_argument("--reload", action="store_true", default=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
"""Centralized port configuration.
|
||||
|
||||
Reads from ports.config.json at the project root so every part of the
|
||||
Python backend uses the same port numbers without hardcoding them.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
_config_path = os.path.join(os.path.dirname(__file__), "..", "ports.config.json")
|
||||
with open(_config_path) as _f:
|
||||
_cfg = json.load(_f)
|
||||
|
||||
BACKEND_DEV_PORT: int = _cfg["backend"]["dev"]
|
||||
BACKEND_PROD_PORT_START: int = _cfg["backend"]["prod"]["start"]
|
||||
BACKEND_PROD_PORT_END: int = _cfg["backend"]["prod"]["end"]
|
||||
FRONTEND_DEV_PORT: int = _cfg["frontend"]["dev"]
|
||||
NINE_ROUTER_PORT: int = _cfg["nineRouter"]
|
||||
|
||||
|
||||
def get_backend_port() -> int:
|
||||
"""Return the active backend port (env override or dev default)."""
|
||||
return int(os.environ.get("OPENSWARM_PORT", str(BACKEND_DEV_PORT)))
|
||||
+5
-2
@@ -68,9 +68,12 @@ if [[ $? -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Read dev port from ports.config.json ---
|
||||
BACKEND_PORT=$(python3 -c "import json; print(json.load(open('$PROJECT_ROOT_ABSPATH/ports.config.json'))['backend']['dev'])")
|
||||
|
||||
# --- Start the backend server ---
|
||||
echo "Starting backend server on http://0.0.0.0:8324 ..."
|
||||
echo "Starting backend server on http://0.0.0.0:${BACKEND_PORT} ..."
|
||||
cd "$PROJECT_ROOT_ABSPATH"
|
||||
python3 -m uvicorn backend.main:app --host 0.0.0.0 --port 8324 --reload \
|
||||
python3 -m uvicorn backend.main:app --host 0.0.0.0 --port "$BACKEND_PORT" --reload \
|
||||
--reload-dir "$BACKEND_DIR_ABSPATH" \
|
||||
--reload-exclude '*.pyc'
|
||||
+11
-2
@@ -8,6 +8,15 @@ const fs = require('fs');
|
||||
const getPort = require('get-port');
|
||||
const http = require('http');
|
||||
|
||||
const portsConfig = JSON.parse(
|
||||
fs.readFileSync(
|
||||
app.isPackaged
|
||||
? path.join(process.resourcesPath, 'ports.config.json')
|
||||
: path.join(__dirname, '..', 'ports.config.json'),
|
||||
'utf8',
|
||||
)
|
||||
);
|
||||
|
||||
app.commandLine.appendSwitch('disable-features', 'HardwareMediaKeyHandling');
|
||||
app.commandLine.appendSwitch('ignore-gpu-blocklist');
|
||||
app.commandLine.appendSwitch('enable-gpu-rasterization');
|
||||
@@ -139,7 +148,7 @@ function waitForBackend(port, timeoutMs = 60000) {
|
||||
}
|
||||
|
||||
async function startBackend() {
|
||||
backendPort = await getPort({ port: getPort.makeRange(8324, 8424) });
|
||||
backendPort = await getPort({ port: getPort.makeRange(portsConfig.backend.prod.start, portsConfig.backend.prod.end) });
|
||||
|
||||
const pythonPath = getPythonPath();
|
||||
const backendDir = getResourcePath('backend');
|
||||
@@ -368,7 +377,7 @@ app.whenReady().then(async () => {
|
||||
|
||||
try {
|
||||
if (isDev) {
|
||||
backendPort = parseInt(process.env.OPENSWARM_PORT || '8324', 10);
|
||||
backendPort = parseInt(process.env.OPENSWARM_PORT || String(portsConfig.backend.dev), 10);
|
||||
console.log(`Dev mode: using existing backend on port ${backendPort}`);
|
||||
} else {
|
||||
await startBackend();
|
||||
|
||||
@@ -59,6 +59,10 @@
|
||||
]
|
||||
},
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "build-staging/ports.config.json",
|
||||
"to": "ports.config.json"
|
||||
},
|
||||
{
|
||||
"from": "build-staging/frontend",
|
||||
"to": "frontend",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const port = (window as any).__OPENSWARM_PORT__ || 8324;
|
||||
const port = (window as any).__OPENSWARM_PORT__ || 8325;
|
||||
const host = window.location.hostname || 'localhost';
|
||||
|
||||
export const API_BASE = `http://${host}:${port}/api`;
|
||||
|
||||
@@ -2,6 +2,8 @@ const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
const portsConfig = require('../ports.config.json');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
const isDevelopment = argv.mode === 'development';
|
||||
|
||||
@@ -79,13 +81,13 @@ module.exports = (env, argv) => {
|
||||
devServer: {
|
||||
static: { directory: path.join(__dirname, 'public') },
|
||||
compress: true,
|
||||
port: 3000,
|
||||
port: portsConfig.frontend.dev,
|
||||
hot: true,
|
||||
open: false,
|
||||
historyApiFallback: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8324',
|
||||
target: `http://localhost:${portsConfig.backend.dev}`,
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"backend": {
|
||||
"dev": 8325,
|
||||
"prod": { "start": 8326, "end": 8424 }
|
||||
},
|
||||
"frontend": {
|
||||
"dev": 3000
|
||||
},
|
||||
"nineRouter": 20128
|
||||
}
|
||||
+10
-6
@@ -85,6 +85,10 @@ if [ ! -f "$UV_BIN_DIR/uvx" ]; then
|
||||
rm -rf /tmp/uv-*-apple-darwin
|
||||
fi
|
||||
|
||||
# --- Read ports from config ---
|
||||
BACKEND_PORT=$(python3 -c "import json; print(json.load(open('$PROJECT_ROOT/ports.config.json'))['backend']['dev'])")
|
||||
FRONTEND_PORT=$(python3 -c "import json; print(json.load(open('$PROJECT_ROOT/ports.config.json'))['frontend']['dev'])")
|
||||
|
||||
# --- Start backend ---
|
||||
echo -e "${BLUE}${BOLD}[backend]${RESET} Starting backend server..."
|
||||
bash "$PROJECT_ROOT/backend/run.sh" > >(
|
||||
@@ -95,11 +99,11 @@ bash "$PROJECT_ROOT/backend/run.sh" > >(
|
||||
BACKEND_PID=$!
|
||||
|
||||
# --- Wait for backend to become healthy ---
|
||||
echo -e "${YELLOW}${BOLD}Waiting for backend (http://localhost:8324) to be ready...${RESET}"
|
||||
echo -e "${YELLOW}${BOLD}Waiting for backend (http://localhost:${BACKEND_PORT}) to be ready...${RESET}"
|
||||
MAX_WAIT=120
|
||||
elapsed=0
|
||||
while (( elapsed < MAX_WAIT )); do
|
||||
if curl -s -o /dev/null --connect-timeout 1 http://localhost:8324/ 2>/dev/null; then
|
||||
if curl -s -o /dev/null --connect-timeout 1 "http://localhost:${BACKEND_PORT}/" 2>/dev/null; then
|
||||
echo -e "${GREEN}${BOLD}Backend is ready!${RESET}"
|
||||
break
|
||||
fi
|
||||
@@ -126,11 +130,11 @@ bash "$PROJECT_ROOT/frontend/run.sh" > >(
|
||||
FRONTEND_PID=$!
|
||||
|
||||
# --- Wait for frontend dev server to become available ---
|
||||
echo -e "${YELLOW}${BOLD}Waiting for frontend (http://localhost:3000) to be ready...${RESET}"
|
||||
echo -e "${YELLOW}${BOLD}Waiting for frontend (http://localhost:${FRONTEND_PORT}) to be ready...${RESET}"
|
||||
FRONTEND_MAX_WAIT=60
|
||||
frontend_elapsed=0
|
||||
while (( frontend_elapsed < FRONTEND_MAX_WAIT )); do
|
||||
if curl -s -o /dev/null --connect-timeout 1 http://localhost:3000/ 2>/dev/null; then
|
||||
if curl -s -o /dev/null --connect-timeout 1 "http://localhost:${FRONTEND_PORT}/" 2>/dev/null; then
|
||||
echo -e "${GREEN}${BOLD}Frontend is ready!${RESET}"
|
||||
break
|
||||
fi
|
||||
@@ -175,8 +179,8 @@ ELECTRON_PID=$!
|
||||
|
||||
echo ""
|
||||
echo -e "${BOLD}All services are running. Press Ctrl+C to stop.${RESET}"
|
||||
echo -e " Backend: ${BLUE}http://localhost:8324${RESET}"
|
||||
echo -e " Frontend: ${GREEN}http://localhost:3000${RESET}"
|
||||
echo -e " Backend: ${BLUE}http://localhost:${BACKEND_PORT}${RESET}"
|
||||
echo -e " Frontend: ${GREEN}http://localhost:${FRONTEND_PORT}${RESET}"
|
||||
echo -e " Electron: ${MAGENTA}dev shell (pid $ELECTRON_PID)${RESET}"
|
||||
echo ""
|
||||
|
||||
|
||||
@@ -118,6 +118,8 @@ STAGING_DIR="$PROJECT_ROOT/electron/build-staging"
|
||||
rm -rf "$STAGING_DIR"
|
||||
mkdir -p "$STAGING_DIR"
|
||||
|
||||
cp "$PROJECT_ROOT/ports.config.json" "$STAGING_DIR/ports.config.json"
|
||||
|
||||
rsync -a \
|
||||
--exclude='__pycache__' --exclude='**/__pycache__' \
|
||||
--exclude='*.pyc' --exclude='.venv' \
|
||||
|
||||
Reference in New Issue
Block a user