- Implemented Agent-based Data Collection Nodes

- Added More Dark Theming throughout Borealis
- Added API Data Collector Node
- Added Image Viewer Node
- Added Agent Deployment Script (Powershell)
This commit is contained in:
Nicole Rappe 2025-04-04 03:23:01 -06:00
parent dca79b8556
commit a75c472c98
14 changed files with 693 additions and 222 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
.vs/
Borealis-Workflow-Automation-Tool/
Borealis-Workflow-Automation-Tool/
Borealis-API-Collector-Agent/

View File

@ -0,0 +1,205 @@
import sys
import uuid
import time
import json
import base64
import threading
import requests
from io import BytesIO
import socket
from PyQt5 import QtCore, QtGui, QtWidgets
from PIL import ImageGrab
# ---------------- Configuration ----------------
SERVER_URL = "http://localhost:5000"
CHECKIN_ENDPOINT = f"{SERVER_URL}/api/agent/checkin"
CONFIG_ENDPOINT = f"{SERVER_URL}/api/agent/config"
DATA_POST_ENDPOINT = f"{SERVER_URL}/api/agent/data"
HOSTNAME = socket.gethostname().lower()
RANDOM_SUFFIX = uuid.uuid4().hex[:8]
AGENT_ID = f"{HOSTNAME}-agent-{RANDOM_SUFFIX}"
CONFIG_POLL_INTERVAL = 5
# ---------------- State ----------------
app_instance = None
region_widget = None
capture_thread_started = False
current_interval = 1000
config_ready = threading.Event()
overlay_visible = True
# ---------------- Signal Bridge ----------------
class RegionLauncher(QtCore.QObject):
trigger = QtCore.pyqtSignal(int, int, int, int)
def __init__(self):
super().__init__()
self.trigger.connect(self.handle)
def handle(self, x, y, w, h):
launch_region(x, y, w, h)
region_launcher = None
# ---------------- Agent Networking ----------------
def check_in():
try:
requests.post(CHECKIN_ENDPOINT, json={"agent_id": AGENT_ID, "hostname": HOSTNAME})
print(f"[INFO] Agent ID: {AGENT_ID}")
except Exception as e:
print(f"[ERROR] Check-in failed: {e}")
def poll_for_config():
try:
res = requests.get(CONFIG_ENDPOINT, params={"agent_id": AGENT_ID})
if res.status_code == 200:
return res.json()
except Exception as e:
print(f"[ERROR] Config polling failed: {e}")
return None
def send_image_data(image):
try:
buffer = BytesIO()
image.save(buffer, format="PNG")
encoded = base64.b64encode(buffer.getvalue()).decode("utf-8")
response = requests.post(DATA_POST_ENDPOINT, json={
"agent_id": AGENT_ID,
"type": "screenshot",
"image_base64": encoded
})
if response.status_code != 200:
print(f"[ERROR] Screenshot POST failed: {response.status_code} - {response.text}")
except Exception as e:
print(f"[ERROR] Failed to send image: {e}")
# ---------------- Region Overlay ----------------
class ScreenshotRegion(QtWidgets.QWidget):
def __init__(self, x=100, y=100, w=300, h=200):
super().__init__()
self.setGeometry(x, y, w, h)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.drag_offset = None
self.resizing = False
self.resize_handle_size = 12
self.setVisible(True)
self.label = QtWidgets.QLabel(self)
self.label.setText(AGENT_ID)
self.label.setStyleSheet("color: lime; background: transparent; font-size: 10px;")
self.label.move(8, 4)
self.setMouseTracking(True)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
# Transparent fill
painter.setBrush(QtCore.Qt.transparent)
painter.setPen(QtGui.QPen(QtGui.QColor(0, 255, 0), 2))
painter.drawRect(self.rect())
# Resize Handle Visual (Bottom-Right)
handle_rect = QtCore.QRect(
self.width() - self.resize_handle_size,
self.height() - self.resize_handle_size,
self.resize_handle_size,
self.resize_handle_size
)
painter.fillRect(handle_rect, QtGui.QColor(0, 255, 0))
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
if (event.pos().x() > self.width() - self.resize_handle_size and
event.pos().y() > self.height() - self.resize_handle_size):
self.resizing = True
else:
self.drag_offset = event.globalPos() - self.frameGeometry().topLeft()
def mouseMoveEvent(self, event):
if self.resizing:
new_width = max(event.pos().x(), 100)
new_height = max(event.pos().y(), 80)
self.resize(new_width, new_height)
elif event.buttons() & QtCore.Qt.LeftButton and self.drag_offset:
self.move(event.globalPos() - self.drag_offset)
def mouseReleaseEvent(self, event):
self.resizing = False
self.drag_offset = None
def get_geometry(self):
geo = self.geometry()
return geo.x(), geo.y(), geo.width(), geo.height()
# ---------------- Threads ----------------
def capture_loop():
global current_interval
print("[INFO] Screenshot capture loop started")
config_ready.wait()
while region_widget is None:
print("[WAIT] Waiting for region widget to initialize...")
time.sleep(0.2)
print(f"[INFO] Agent Capturing Region: x:{region_widget.x()} y:{region_widget.y()} w:{region_widget.width()} h:{region_widget.height()}")
while True:
if overlay_visible:
x, y, w, h = region_widget.get_geometry()
try:
img = ImageGrab.grab(bbox=(x, y, x + w, y + h))
send_image_data(img)
except Exception as e:
print(f"[ERROR] Screenshot error: {e}")
time.sleep(current_interval / 1000)
def config_loop():
global region_widget, capture_thread_started, current_interval, overlay_visible
check_in()
while True:
config = poll_for_config()
if config and config.get("task") == "screenshot":
print("[PROVISIONING] Agent Provisioning Command Issued by Borealis")
x = config.get("x", 100)
y = config.get("y", 100)
w = config.get("w", 300)
h = config.get("h", 200)
current_interval = config.get("interval", 1000)
overlay_visible = config.get("visible", True)
print(f"[PROVISIONING] Agent Configured as \"Screenshot\" Collector w/ Polling Rate of <{current_interval/1000:.1f}s>")
if not region_widget:
region_launcher.trigger.emit(x, y, w, h)
elif region_widget:
region_widget.setVisible(overlay_visible)
if not capture_thread_started:
threading.Thread(target=capture_loop, daemon=True).start()
capture_thread_started = True
config_ready.set()
time.sleep(CONFIG_POLL_INTERVAL)
def launch_region(x, y, w, h):
global region_widget
if region_widget:
return
print(f"[INFO] Agent Starting...")
region_widget = ScreenshotRegion(x, y, w, h)
region_widget.show()
# ---------------- Main ----------------
if __name__ == "__main__":
app_instance = QtWidgets.QApplication(sys.argv)
region_launcher = RegionLauncher()
threading.Thread(target=config_loop, daemon=True).start()
sys.exit(app_instance.exec_())

View File

@ -0,0 +1,3 @@
requests
PyQt5
Pillow

View File

@ -451,7 +451,7 @@ export default function App() {
}}
sx={{ color: "#58a6ff", borderColor: "#58a6ff", fontSize: "0.75rem", textTransform: "none", px: 1.5 }}
>
Update Rate
Apply Rate
</Button>
</Box>
</Box>

View File

@ -61,4 +61,18 @@
background: #58a6ff;
width: 10px;
height: 10px;
}
}
/* Global dark form inputs */
input, select, button {
background-color: #2a2a2a;
color: #ccc;
border: 1px solid #444;
font-size: 10px;
}
/* Label / Dark Text styling */
label {
color: #aaa;
font-size: 10px;
}

View File

@ -0,0 +1,177 @@
import React, { useEffect, useState } from "react";
import { Handle, Position, useReactFlow } from "reactflow";
const APINode = ({ id, data }) => {
const { setNodes } = useReactFlow();
const [agents, setAgents] = useState([]);
const [selectedAgent, setSelectedAgent] = useState(data.agent_id || "");
const [selectedType, setSelectedType] = useState(data.data_type || "screenshot");
const [imageData, setImageData] = useState("");
const [intervalMs, setIntervalMs] = useState(data.interval || 1000);
const [paused, setPaused] = useState(false);
const [overlayVisible, setOverlayVisible] = useState(true);
// Refresh agents every 5s
useEffect(() => {
const fetchAgents = () => fetch("/api/agents").then(res => res.json()).then(setAgents);
fetchAgents();
const interval = setInterval(fetchAgents, 5000);
return () => clearInterval(interval);
}, []);
// Pull image if agent provisioned
useEffect(() => {
if (!selectedAgent || paused) return;
const interval = setInterval(() => {
fetch(`/api/agent/image?agent_id=${selectedAgent}`)
.then(res => res.json())
.then(json => {
if (json.image_base64) {
setImageData(json.image_base64);
window.BorealisValueBus = window.BorealisValueBus || {};
window.BorealisValueBus[id] = json.image_base64;
}
})
.catch(() => { });
}, intervalMs);
return () => clearInterval(interval);
}, [selectedAgent, id, paused, intervalMs]);
const provisionAgent = () => {
if (!selectedAgent) return;
fetch("/api/agent/provision", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
agent_id: selectedAgent,
x: 250,
y: 100,
w: 300,
h: 200,
interval: intervalMs,
visible: overlayVisible,
task: selectedType
})
}).then(() => {
setNodes(nds =>
nds.map(n => n.id === id
? {
...n,
data: {
...n.data,
agent_id: selectedAgent,
data_type: selectedType,
interval: intervalMs
}
}
: n
)
);
});
};
const resetAgent = () => {
if (!selectedAgent) return;
fetch("/api/agent/reset", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ agent_id: selectedAgent })
}).then(() => {
setSelectedAgent("");
});
};
const toggleOverlay = () => {
const newVisibility = !overlayVisible;
setOverlayVisible(newVisibility);
if (selectedAgent) {
fetch("/api/agent/overlay_visibility", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ agent_id: selectedAgent, visible: newVisibility })
});
}
};
return (
<div className="borealis-node">
<Handle type="source" position={Position.Right} className="borealis-handle" />
<div className="borealis-node-header">API Data Collector</div>
<div className="borealis-node-content">
<label style={{ fontSize: "10px" }}>Agent:</label>
<select
value={selectedAgent}
onChange={(e) => setSelectedAgent(e.target.value)}
style={{ width: "100%", fontSize: "9px", marginBottom: "6px" }}
>
<option value="">-- Select --</option>
{Object.entries(agents).map(([id, info]) => (
<option key={id} value={id} disabled={info.status === "provisioned"}>
{id} {info.status === "provisioned" ? "(Adopted)" : ""}
</option>
))}
</select>
<label style={{ fontSize: "10px" }}>Data Type:</label>
<select
value={selectedType}
onChange={(e) => setSelectedType(e.target.value)}
style={{ width: "100%", fontSize: "9px", marginBottom: "6px" }}
>
<option value="screenshot">Screenshot Region</option>
</select>
<label style={{ fontSize: "10px" }}>Update Rate (ms):</label>
<input
type="number"
min="100"
step="100"
value={intervalMs}
onChange={(e) => setIntervalMs(Number(e.target.value))}
style={{ width: "100%", fontSize: "9px", marginBottom: "6px" }}
/>
<div style={{ marginBottom: "6px" }}>
<label style={{ fontSize: "10px" }}>
<input
type="checkbox"
checked={paused}
onChange={() => setPaused(!paused)}
style={{ marginRight: "4px" }}
/>
Pause Data Collection
</label>
</div>
<div style={{ display: "flex", gap: "4px", flexWrap: "wrap" }}>
<button
style={{ flex: 1, fontSize: "9px" }}
onClick={provisionAgent}
>
Provision
</button>
<button
style={{ flex: 1, fontSize: "9px" }}
onClick={resetAgent}
>
Reset Agent
</button>
<button
style={{ flex: 1, fontSize: "9px" }}
onClick={toggleOverlay}
>
{overlayVisible ? "Hide Overlay" : "Show Overlay"}
</button>
</div>
</div>
</div>
);
};
export default {
type: "API_Data_Collector",
label: "API Data Collector",
description: "Connects to a remote agent via API and collects data such as screenshots, OCR results, and more.",
content: "Publishes agent-collected data into the workflow ValueBus.",
component: APINode
};

View File

@ -0,0 +1,60 @@
import React, { useEffect, useState } from "react";
import { Handle, Position, useReactFlow } from "reactflow";
const ImageViewerNode = ({ id, data }) => {
const { getEdges } = useReactFlow();
const [imageBase64, setImageBase64] = useState("");
const [selectedType, setSelectedType] = useState("base64");
// Watch upstream value
useEffect(() => {
const interval = setInterval(() => {
const edges = getEdges();
const inputEdge = edges.find(e => e.target === id);
if (inputEdge) {
const sourceId = inputEdge.source;
const valueBus = window.BorealisValueBus || {};
const value = valueBus[sourceId];
if (typeof value === "string") {
setImageBase64(value);
}
}
}, 1000);
return () => clearInterval(interval);
}, [id, getEdges]);
return (
<div className="borealis-node">
<Handle type="target" position={Position.Left} className="borealis-handle" />
<div className="borealis-node-header">Image Viewer</div>
<div className="borealis-node-content">
<label style={{ fontSize: "10px" }}>Data Type:</label>
<select
value={selectedType}
onChange={(e) => setSelectedType(e.target.value)}
style={{ width: "100%", fontSize: "9px", marginBottom: "6px" }}
>
<option value="base64">Base64 Encoded Image</option>
</select>
{imageBase64 ? (
<img
src={`data:image/png;base64,${imageBase64}`}
alt="Live"
style={{ width: "100%", border: "1px solid #333", marginTop: "6px" }}
/>
) : (
<div style={{ fontSize: "9px", color: "#888" }}>Waiting for image...</div>
)}
</div>
</div>
);
};
export default {
type: "Image_Viewer",
label: "Image Viewer",
description: "Displays base64 image pulled from ValueBus of upstream node.",
content: "Visual preview of base64 image",
component: ImageViewerNode
};

View File

@ -32,7 +32,8 @@ const BackdropGroupBoxNode = ({ id, data }) => {
}
}, [isEditing]);
const handleTitleClick = () => {
const handleTitleClick = (e) => {
e.stopPropagation();
setIsEditing(true);
};
@ -47,22 +48,32 @@ const BackdropGroupBoxNode = ({ id, data }) => {
};
return (
<div style={{ pointerEvents: "auto", zIndex: -1 }}> {/* Prevent blocking other nodes */}
<div style={{ pointerEvents: "auto" }}>
<ResizableBox
width={200}
height={120}
minConstraints={[120, 80]}
maxConstraints={[600, 600]}
resizeHandles={["se"]}
className="borealis-node"
handle={(h) => (
<span
className={`react-resizable-handle react-resizable-handle-${h}`}
style={{ pointerEvents: "auto" }}
onMouseDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
/>
)}
onClick={(e) => e.stopPropagation()}
style={{
backgroundColor: "rgba(44, 44, 44, 0.5)",
border: "1px solid #3a3a3a",
borderRadius: "4px",
boxShadow: "0 0 5px rgba(88, 166, 255, 0.15)",
overflow: "hidden",
position: "relative"
position: "relative",
zIndex: 0
}}
onClick={(e) => e.stopPropagation()} // prevent drag on resize
>
<div
onClick={handleTitleClick}
@ -82,6 +93,8 @@ const BackdropGroupBoxNode = ({ id, data }) => {
value={title}
onChange={handleTitleChange}
onBlur={handleBlur}
onClick={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
style={{
fontSize: "10px",
padding: "2px",

View File

@ -1,7 +1,10 @@
from flask import Flask, send_from_directory
from flask import Flask, request, jsonify, send_from_directory
import time
import os
# Determine the absolute path for the React build folder
# ---------------------------------------------
# React Frontend Hosting Configuration
# ---------------------------------------------
build_folder = os.path.join(os.getcwd(), "web-interface", "build")
if not os.path.exists(build_folder):
print("WARNING: web-interface build folder not found. Please build your React app.")
@ -9,12 +12,103 @@ if not os.path.exists(build_folder):
app = Flask(__name__, static_folder=build_folder, static_url_path="/")
@app.route("/")
def serve_frontend():
"""Serve the React app."""
def serve_index():
index_path = os.path.join(build_folder, "index.html")
if os.path.exists(index_path):
return send_from_directory(app.static_folder, "index.html")
return send_from_directory(build_folder, "index.html")
return "<h1>Borealis React App Code Not Found</h1><p>Please re-deploy Borealis Workflow Automation Tool</p>", 404
# Wildcard route to serve React for sub-routes (e.g., /workflow)
@app.route("/<path:path>")
def serve_react_app(path):
full_path = os.path.join(build_folder, path)
if os.path.exists(full_path):
return send_from_directory(build_folder, path)
return send_from_directory(build_folder, "index.html")
# ---------------------------------------------
# Borealis Agent API Endpoints
# ---------------------------------------------
registered_agents = {}
agent_configurations = {}
latest_images = {}
@app.route("/api/agent/checkin", methods=["POST"])
def agent_checkin():
data = request.json
agent_id = data.get("agent_id")
hostname = data.get("hostname", "unknown")
registered_agents[agent_id] = {
"agent_id": agent_id,
"hostname": hostname,
"last_seen": time.time(),
"status": "orphaned" if agent_id not in agent_configurations else "provisioned"
}
return jsonify({"status": "ok"})
@app.route("/api/agent/reset", methods=["POST"])
def reset_agent():
agent_id = request.json.get("agent_id")
if agent_id in agents:
agents[agent_id]["status"] = "orphaned"
agents[agent_id]["config"] = None
latest_images.pop(agent_id, None)
return jsonify({"status": "reset"}), 200
return jsonify({"error": "Agent not found"}), 404
@app.route("/api/agent/provision", methods=["POST"])
def provision_agent():
data = request.json
agent_id = data.get("agent_id")
config = {
"task": "screenshot",
"x": data.get("x", 100),
"y": data.get("y", 100),
"w": data.get("w", 300),
"h": data.get("h", 200),
"interval": data.get("interval", 1000)
}
agent_configurations[agent_id] = config
if agent_id in registered_agents:
registered_agents[agent_id]["status"] = "provisioned"
return jsonify({"status": "provisioned"})
@app.route("/api/agent/config")
def get_agent_config():
agent_id = request.args.get("agent_id")
config = agent_configurations.get(agent_id)
return jsonify(config or {})
@app.route("/api/agent/data", methods=["POST"])
def agent_data():
data = request.json
agent_id = data.get("agent_id")
image = data.get("image_base64")
if not agent_id or not image:
return jsonify({"error": "Missing data"}), 400
latest_images[agent_id] = {
"image_base64": image,
"timestamp": time.time()
}
return jsonify({"status": "received"})
@app.route("/api/agent/image")
def get_latest_image():
agent_id = request.args.get("agent_id")
entry = latest_images.get(agent_id)
if entry:
return jsonify(entry)
return jsonify({"error": "No image"}), 404
@app.route("/api/agents")
def get_agents():
return jsonify(registered_agents)
# ---------------------------------------------
# Server Start
# ---------------------------------------------
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=False)

View File

@ -0,0 +1,86 @@
# Launch-API-Collector-Agent.ps1
# Run this script with:
# Set-ExecutionPolicy Unrestricted -Scope Process; .\Launch-API-Collector-Agent.ps1
# ---------------------- Initialization & Visuals ----------------------
$symbols = @{
Success = [char]0x2705
Running = [char]0x23F3
Fail = [char]0x274C
Info = [char]0x2139
}
function Write-ProgressStep {
param (
[string]$Message,
[string]$Status = $symbols["Info"]
)
Write-Host "`r$Status $Message... " -NoNewline
}
function Run-Step {
param (
[string]$Message,
[scriptblock]$Script
)
Write-ProgressStep -Message $Message -Status "$($symbols.Running)"
try {
& $Script
if ($LASTEXITCODE -eq 0 -or $?) {
Write-Host "`r$($symbols.Success) $Message "
} else {
throw "Non-zero exit code"
}
} catch {
Write-Host "`r$($symbols.Fail) $Message - Failed: $_ " -ForegroundColor Red
exit 1
}
}
Clear-Host
Write-Host "Deploying Borealis API Collector Agent..." -ForegroundColor Green
Write-Host "===================================================================================="
# ---------------------- Path Definitions ----------------------
$venvFolder = "Borealis-API-Collector-Agent"
$agentSourcePath = "Data\Agent\api-collector-agent.py"
$agentRequirements = "Data\Agent\requirements.txt"
$agentDestinationFolder = "$venvFolder\Agent"
$agentDestinationFile = "$agentDestinationFolder\api-collector-agent.py"
# ---------------------- Create Python Virtual Environment & Copy Agent ----------------------
Run-Step "Create Virtual Python Environment for Collector Agent" {
if (!(Test-Path "$venvFolder\Scripts\Activate")) {
python -m venv $venvFolder | Out-Null
}
# Copy Agent Script
if (Test-Path $agentSourcePath) {
if (Test-Path $agentDestinationFolder) {
Remove-Item -Recurse -Force $agentDestinationFolder | Out-Null
}
New-Item -Path $agentDestinationFolder -ItemType Directory -Force | Out-Null
Copy-Item -Path $agentSourcePath -Destination $agentDestinationFile -Force
} else {
Write-Host "`r$($symbols.Info) Warning: Agent script not found at '$agentSourcePath', skipping copy." -ForegroundColor Yellow
}
. "$venvFolder\Scripts\Activate"
}
# ---------------------- Install Python Dependencies ----------------------
Run-Step "Install Python Dependencies for Collector Agent" {
if (Test-Path $agentRequirements) {
pip install -q -r $agentRequirements 2>&1 | Out-Null
} else {
Write-Host "`r$($symbols.Info) Agent-specific requirements.txt not found at '$agentRequirements', skipping Python packages." -ForegroundColor Yellow
}
}
# ---------------------- Launch Agent ----------------------
Push-Location $venvFolder
Write-Host "`nLaunching Borealis API Collector Agent..." -ForegroundColor Green
Write-Host "===================================================================================="
Write-Host "$($symbols.Running) Starting Agent..." -NoNewline
python "Agent\api-collector-agent.py"
Pop-Location

View File

@ -54,15 +54,12 @@ $dataDestination = "$venvFolder\Borealis"
$customUIPath = "$dataSource\WebUI"
$webUIDestination = "$venvFolder\web-interface"
# ---------------------- Create Python Virtual Environment ----------------------
Run-Step "Create Virtual Python Environment" {
# ---------------------- Create Python Virtual Environment & Prepare Borealis Files ----------------------
Run-Step "Create Borealis Virtual Python Environment" {
if (!(Test-Path "$venvFolder\Scripts\Activate")) {
python -m venv $venvFolder | Out-Null
}
}
# ---------------------- Copy Server Data ----------------------
Run-Step "Copy Borealis Server Data into Virtual Python Environment" {
# ---------------------- Copy Server Data ----------------------
if (Test-Path $dataSource) {
if (Test-Path $dataDestination) {
Remove-Item -Recurse -Force $dataDestination | Out-Null
@ -72,31 +69,24 @@ Run-Step "Copy Borealis Server Data into Virtual Python Environment" {
} else {
Write-Host "`r$($symbols.Info) Warning: Data folder not found, skipping copy." -ForegroundColor Yellow
}
}
# ---------------------- React UI Deployment ----------------------
Run-Step "Create a new ReactJS App in $webUIDestination" {
# ---------------------- React UI Deployment ----------------------
if (-not (Test-Path $webUIDestination)) {
npx create-react-app $webUIDestination | Out-Null
}
}
Run-Step "Overwrite ReactJS App Files with Borealis ReactJS Files" {
if (Test-Path $customUIPath) {
Copy-Item -Path "$customUIPath\*" -Destination $webUIDestination -Recurse -Force
} else {
Write-Host "`r$($symbols.Info) No custom UI found, using default React app." -ForegroundColor Yellow
}
}
Run-Step "Remove Existing ReactJS Build Folder (If Exists)" {
# Remove Pre-Existing ReactJS Build Folder (If one Exists)
if (Test-Path "$webUIDestination\build") {
Remove-Item -Path "$webUIDestination\build" -Recurse -Force
}
}
# ---------------------- Activate Python Virtual Environment ----------------------
Run-Step "Activate Virtual Python Environment" {
# ---------------------- Activate Python Virtual Environment ----------------------
. "$venvFolder\Scripts\Activate"
}
@ -110,39 +100,32 @@ Run-Step "Install Python Dependencies into Virtual Python Environment" {
}
# ---------------------- Build React App ----------------------
Run-Step "ReactJS App: Install NPM" {
Run-Step "ReactJS Web Frontend: Install Necessary NPM Packages" {
$packageJsonPath = Join-Path $webUIDestination "package.json"
if (Test-Path $packageJsonPath) {
Push-Location $webUIDestination
$env:npm_config_loglevel = "silent"
# Install NPM
npm install --silent --no-fund --audit=false 2>&1 | Out-Null
# Install React Resizable
npm install --silent react-resizable --no-fund --audit=false | Out-Null
# Install React Flow
npm install --silent reactflow --no-fund --audit=false | Out-Null
# Install Material UI Libraries
npm install --silent @mui/material @mui/icons-material @emotion/react @emotion/styled --no-fund --audit=false 2>&1 | Out-Null
Pop-Location
}
}
Run-Step "ReactJS App: Install React Resizable" {
Run-Step "ReactJS Web Frontend: Build App" {
Push-Location $webUIDestination
npm install react-resizable --no-fund --audit=false | Out-Null
Pop-Location
}
Run-Step "ReactJS App: Install React Flow" {
Push-Location $webUIDestination
npm install reactflow --no-fund --audit=false | Out-Null
Pop-Location
}
Run-Step "ReactJS App: Install Material UI Libraries" {
Push-Location $webUIDestination
$env:npm_config_loglevel = "silent" # Force NPM to be completely silent
npm install --silent @mui/material @mui/icons-material @emotion/react @emotion/styled --no-fund --audit=false 2>&1 | Out-Null
Pop-Location
}
Run-Step "ReactJS App: Building App" {
Push-Location $webUIDestination
#npm run build | Out-Null
npm run build
#npm run build | Out-Null # Suppress Compilation Output
npm run build # Enabled during Development
Pop-Location
}
@ -150,7 +133,6 @@ Run-Step "ReactJS App: Building App" {
Push-Location $venvFolder
Write-Host "`nLaunching Borealis..." -ForegroundColor Green
Write-Host "===================================================================================="
Write-Host "$($symbols.Running) Starting the Python Flask server..." -NoNewline
Write-Host "$($symbols.Running) Starting Python Flask Server..." -NoNewline
python "Borealis\server.py"
Write-Host "`r$($symbols.Success) Borealis Launched Successfully!"
Pop-Location

View File

@ -1,80 +0,0 @@
{
"nodes": [
{
"id": "node-1743304886846",
"type": "custom",
"position": {
"x": 214.63333129882812,
"y": 146.66666666666666
},
"data": {
"label": "Custom Node",
"content": "Placeholder"
},
"width": 160,
"height": 72
},
{
"id": "node-1743304888268",
"type": "custom",
"position": {
"x": 621.2999979654948,
"y": 276.6666666666667
},
"data": {
"label": "Custom Node",
"content": "Placeholder"
},
"width": 160,
"height": 72
},
{
"id": "node-1743304891251",
"type": "custom",
"position": {
"x": 814.6333312988281,
"y": 65.33333333333334
},
"data": {
"label": "Custom Node",
"content": "Placeholder"
},
"width": 160,
"height": 72,
"selected": true,
"positionAbsolute": {
"x": 814.6333312988281,
"y": 65.33333333333334
},
"dragging": false
}
],
"edges": [
{
"source": "node-1743304886846",
"sourceHandle": null,
"target": "node-1743304888268",
"targetHandle": null,
"type": "smoothstep",
"animated": true,
"style": {
"strokeDasharray": "6 3",
"stroke": "#58a6ff"
},
"id": "reactflow__edge-node-1743304886846-node-1743304888268"
},
{
"source": "node-1743304886846",
"sourceHandle": null,
"target": "node-1743304891251",
"targetHandle": null,
"type": "smoothstep",
"animated": true,
"style": {
"strokeDasharray": "6 3",
"stroke": "#58a6ff"
},
"id": "reactflow__edge-node-1743304886846-node-1743304891251"
}
]
}

View File

@ -1,86 +0,0 @@
{
"nodes": [
{
"id": "node-1743545354663",
"type": "dataNode",
"position": {
"x": 177.26666259765625,
"y": 122.66666666666667
},
"data": {
"label": "Data Node",
"content": "Placeholder Node"
},
"width": 160,
"height": 63
},
{
"id": "node-1743545357071",
"type": "dataNode",
"position": {
"x": 506.59999593098956,
"y": 232
},
"data": {
"label": "Data Node",
"content": "Placeholder Node"
},
"width": 160,
"height": 63,
"selected": true,
"positionAbsolute": {
"x": 506.59999593098956,
"y": 232
},
"dragging": false
},
{
"id": "node-1743545361694",
"type": "ExportToCSVNode",
"position": {
"x": 187.2666625976562,
"y": 316.66666666666663
},
"data": {
"label": "Export to CSV",
"content": "Placeholder Node"
},
"width": 160,
"height": 63,
"selected": false,
"positionAbsolute": {
"x": 187.2666625976562,
"y": 316.66666666666663
},
"dragging": false
}
],
"edges": [
{
"source": "node-1743545354663",
"sourceHandle": null,
"target": "node-1743545357071",
"targetHandle": null,
"type": "smoothstep",
"animated": true,
"style": {
"strokeDasharray": "6 3",
"stroke": "#58a6ff"
},
"id": "reactflow__edge-node-1743545354663-node-1743545357071"
},
{
"source": "node-1743545354663",
"sourceHandle": null,
"target": "node-1743545361694",
"targetHandle": null,
"type": "smoothstep",
"animated": true,
"style": {
"strokeDasharray": "6 3",
"stroke": "#58a6ff"
},
"id": "reactflow__edge-node-1743545354663-node-1743545361694"
}
]
}

View File

@ -3,7 +3,7 @@ torch --index-url https://download.pytorch.org/whl/cu121
torchvision --index-url https://download.pytorch.org/whl/cu121
torchaudio --index-url https://download.pytorch.org/whl/cu121
# Flask for API handling
# Flask for API Hhandling
Flask
requests
@ -13,9 +13,11 @@ qtpy
OdenGraphQt
PyQt5
# Computer Vision & OCR dependencies
# Computer Vision & OCR Dependencies
numpy # Numerical operations
opencv-python # Computer vision processing
pytesseract # OCR engine
easyocr # Deep-learning-based OCR
Pillow # Image processing
Pillow # Image processing
# API Collector Agent Dependencies