mirror of
https://github.com/myronblair/jarvis
synced 2026-07-29 09:12:36 -05:00
Compare commits
8 Commits
90e4ded7c9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| af03a2f2d8 | |||
| dfc92a6791 | |||
| 05522edb1d | |||
| 435af8ccc9 | |||
| a9ea75db98 | |||
| 651455cb47 | |||
| 874f6e8c5c | |||
| 42a82c40cb |
@@ -18,13 +18,13 @@ define(chr(39)+'CLAUDE_MODEL'.chr(39), chr(39)+'claude-sonnet-4-6'.chr(39))
|
||||
define(chr(39)+'CLAUDE_MAX_TOKENS'.chr(39), 1024);
|
||||
|
||||
define(chr(39)+'GROQ_API_KEY'.chr(39), chr(39)+'gsk_...'.chr(39));
|
||||
define(chr(39)+'GROQ_MODEL_SEARCH'.chr(39), chr(39)+'groq/compound-mini'.chr(39));
|
||||
define(chr(39)+'GROQ_MODEL_SEARCH'.chr(39), chr(39)+'compound-beta-mini'.chr(39));
|
||||
define(chr(39)+'GROQ_MODEL_GENERAL'.chr(39), chr(39)+'llama-3.3-70b-versatile'.chr(39));
|
||||
define(chr(39)+'GROQ_TIMEOUT'.chr(39), 30);
|
||||
|
||||
define(chr(39)+'OLLAMA_HOST'.chr(39), chr(39)+'http://10.48.200.95:11434'.chr(39));
|
||||
define(chr(39)+'OLLAMA_MODEL_PRIMARY'.chr(39), chr(39)+'llama3.2:1b'.chr(39));
|
||||
define(chr(39)+'OLLAMA_MODEL_HEAVY'.chr(39), chr(39)+'llama3.1:70b'.chr(39));
|
||||
define(chr(39)+'OLLAMA_HOST'.chr(39), chr(39)+'http://10.48.200.210:11434'.chr(39));
|
||||
define(chr(39)+'OLLAMA_MODEL_PRIMARY'.chr(39), chr(39)+'llama3.1:8b'.chr(39));
|
||||
define(chr(39)+'OLLAMA_MODEL_HEAVY'.chr(39), chr(39)+'llama3.1:8b'.chr(39));
|
||||
define(chr(39)+'OLLAMA_TIMEOUT'.chr(39), 90);
|
||||
|
||||
define(chr(39)+'LOCAL_SUBNET'.chr(39), chr(39)+'10.48.200'.chr(39));
|
||||
|
||||
@@ -184,7 +184,7 @@ function collect_all(): array {
|
||||
"jarvis" => "http://127.0.0.1",
|
||||
'tomsjavajive' => 'https://tomsjavajive.com',
|
||||
'epictravelexp'=> 'https://epictravelexpeditions.com',
|
||||
'parkersling' => 'https://parkerslingshotrentals.com',
|
||||
'parkerslingshotrentals' => 'https://parkerslingshotrentals.com',
|
||||
'orbishosting' => 'https://orbishosting.com',
|
||||
'orbisportal' => 'https://orbis.orbishosting.com',
|
||||
'tomtomgames' => 'https://tomtomgames.com',
|
||||
|
||||
@@ -441,4 +441,41 @@ CREATE TABLE `users` (
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
--
|
||||
-- Table structure for table `guardian_config`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `guardian_config` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`key_name` varchar(64) NOT NULL,
|
||||
`value` varchar(255) NOT NULL DEFAULT '',
|
||||
`updated_at` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_key` (`key_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
--
|
||||
-- Table structure for table `guardian_events`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `guardian_events` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`event_type` varchar(64) NOT NULL,
|
||||
`severity` enum('info','warning','critical') NOT NULL DEFAULT 'info',
|
||||
`agent_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`hostname` varchar(128) NOT NULL DEFAULT '',
|
||||
`metric` varchar(64) NOT NULL DEFAULT '',
|
||||
`value` float NOT NULL DEFAULT 0,
|
||||
`threshold` float NOT NULL DEFAULT 0,
|
||||
`message` text NOT NULL,
|
||||
`ai_analysis` text NOT NULL DEFAULT '',
|
||||
`acknowledged` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_severity` (`severity`),
|
||||
KEY `idx_ack` (`acknowledged`),
|
||||
KEY `idx_created` (`created_at`),
|
||||
KEY `idx_agent` (`agent_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Dump completed on 2026-06-29 23:15:44
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=JARVIS Arc Reactor
|
||||
After=network-online.target mysql.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/jarvis-arc
|
||||
ExecStart=/opt/jarvis-arc/venv/bin/python3 /opt/jarvis-arc/reactor.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
User=root
|
||||
StandardOutput=append:/home/jarvis.orbishosting.com/logs/arc_reactor.log
|
||||
StandardError=append:/home/jarvis.orbishosting.com/logs/arc_reactor.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+35
-4
@@ -80,11 +80,42 @@ while IFS= read -r path; do
|
||||
systemctl reload lsws 2>/dev/null || systemctl restart lsws 2>/dev/null
|
||||
log "OLS reloaded for JARVIS deploy"
|
||||
|
||||
# Sync reactor.py to runtime location if it changed
|
||||
if echo "$CHANGED" | grep -q 'deploy/reactor.py'; then
|
||||
# Patch live config.php with correct Ollama host/model and Groq search model
|
||||
CONFIG="$path/api/config.php"
|
||||
if [ -f "$CONFIG" ]; then
|
||||
sed -i "s|http://10\.48\.200\.95:11434|http://10.48.200.210:11434|g" "$CONFIG"
|
||||
sed -i "s|'llama3\.2:1b'|'llama3.1:8b'|g" "$CONFIG"
|
||||
sed -i "s|\"llama3\.2:1b\"|\"llama3.1:8b\"|g" "$CONFIG"
|
||||
sed -i "s|'llama3\.1:70b'|'llama3.1:8b'|g" "$CONFIG"
|
||||
sed -i "s|\"llama3\.1:70b\"|\"llama3.1:8b\"|g" "$CONFIG"
|
||||
sed -i "s|'groq/compound-mini'|'compound-beta-mini'|g;s|\"groq/compound-mini\"|\"compound-beta-mini\"|g" "$CONFIG"
|
||||
log "Patched config.php: Ollama IP→.210, model→llama3.1:8b, Groq search→compound-beta-mini"
|
||||
fi
|
||||
|
||||
# Self-install the deploy script on every run
|
||||
cp "$path/deploy/jarvis-deploy.sh" /usr/local/bin/jarvis-deploy.sh 2>/dev/null && chmod +x /usr/local/bin/jarvis-deploy.sh
|
||||
|
||||
# Sync reactor.py + service file to runtime location if they changed
|
||||
if echo "$CHANGED" | grep -q 'deploy/reactor.py\|deploy/jarvis-arc.service\|deploy/requirements.txt'; then
|
||||
mkdir -p /opt/jarvis-arc /home/jarvis.orbishosting.com/logs
|
||||
cp "$path/deploy/reactor.py" /opt/jarvis-arc/reactor.py
|
||||
systemctl restart jarvis-arc
|
||||
log "Arc Reactor updated and restarted (reactor.py changed)"
|
||||
cp "$path/deploy/requirements.txt" /opt/jarvis-arc/requirements.txt 2>/dev/null
|
||||
# Bootstrap venv if it doesn't exist
|
||||
if [ ! -f /opt/jarvis-arc/venv/bin/activate ]; then
|
||||
log "Arc Reactor venv missing — creating and installing packages"
|
||||
python3 -m venv /opt/jarvis-arc/venv
|
||||
/opt/jarvis-arc/venv/bin/pip install -q -r /opt/jarvis-arc/requirements.txt
|
||||
log "Arc Reactor venv ready"
|
||||
fi
|
||||
if echo "$CHANGED" | grep -q 'deploy/jarvis-arc.service'; then
|
||||
cp "$path/deploy/jarvis-arc.service" /etc/systemd/system/jarvis-arc.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable jarvis-arc
|
||||
log "Arc Reactor service file installed and enabled"
|
||||
fi
|
||||
systemctl restart jarvis-arc 2>/dev/null || \
|
||||
bash -c "pkill -f reactor.py 2>/dev/null; sleep 1; cd /opt/jarvis-arc && source venv/bin/activate && nohup python3 reactor.py >> /home/jarvis.orbishosting.com/logs/arc_reactor.log 2>&1 &"
|
||||
log "Arc Reactor updated and restarted"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
+108
-52
@@ -49,8 +49,8 @@ CLAUDE_API_KEY = "sk-ant-api03-JL6vjFeyEfajQmaTOmsT6AfLLPs2icrIAvvJ0hdi4DuMi0155
|
||||
CLAUDE_MODEL = "claude-sonnet-4-6"
|
||||
GROQ_API_KEY = "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"
|
||||
GROQ_MODEL = "llama-3.3-70b-versatile"
|
||||
OLLAMA_HOST = "http://10.48.200.95:11434"
|
||||
OLLAMA_MODEL = "llama3.2:1b"
|
||||
OLLAMA_HOST = "http://10.48.200.210:11434"
|
||||
OLLAMA_MODEL = "llama3.1:8b"
|
||||
|
||||
GMAIL_USER = "myronblair@gmail.com"
|
||||
GMAIL_PASS = "demsvdylwweacbcx"
|
||||
@@ -175,6 +175,16 @@ async def _ollama_call(messages: list, system: str = "") -> str:
|
||||
data = await resp.json()
|
||||
return data.get("response", "")
|
||||
|
||||
async def _ollama_vision_call(image_b64: str, prompt: str) -> str:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
f"{OLLAMA_HOST}/api/generate",
|
||||
json={"model": "llava:7b", "prompt": prompt, "images": [image_b64], "stream": False},
|
||||
timeout=aiohttp.ClientTimeout(total=120),
|
||||
) as resp:
|
||||
data = await resp.json()
|
||||
return data.get("response", "").strip()
|
||||
|
||||
async def handle_llm(payload: dict) -> dict:
|
||||
message = payload.get("message", "")
|
||||
system = payload.get("system", "You are JARVIS, an Iron Man-style AI assistant.")
|
||||
@@ -658,38 +668,44 @@ async def handle_screenshot(payload: dict) -> dict:
|
||||
height = result.get("height", 0)
|
||||
file_size = result.get("file_size", 0)
|
||||
|
||||
# Run Claude vision analysis if we have an image
|
||||
# Run vision analysis if we have an image — llava first, Claude fallback
|
||||
analysis = ""
|
||||
provider_used = ""
|
||||
if do_analyze and image_b64:
|
||||
try:
|
||||
import anthropic
|
||||
client = anthropic.AsyncAnthropic(api_key=CLAUDE_API_KEY)
|
||||
msg = await client.messages.create(
|
||||
model="claude-opus-4-8-20251101",
|
||||
max_tokens=1024,
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "image", "source": {"type": "base64",
|
||||
"media_type": "image/png", "data": image_b64}},
|
||||
{"type": "text", "text": analyze_prompt},
|
||||
],
|
||||
}],
|
||||
)
|
||||
analysis = msg.content[0].text if msg.content else ""
|
||||
provider_used = "claude"
|
||||
log.info(f"[VISION] Claude analysis complete ({len(analysis)} chars)")
|
||||
analysis = await _ollama_vision_call(image_b64, analyze_prompt)
|
||||
provider_used = "ollama:llava"
|
||||
log.info(f"[VISION] llava analysis complete ({len(analysis)} chars)")
|
||||
except Exception as e:
|
||||
log.warning(f"[VISION] Claude vision failed: {e}")
|
||||
analysis = f"Vision analysis unavailable: {e}"
|
||||
log.warning(f"[VISION] llava failed: {e} — falling back to Claude")
|
||||
try:
|
||||
import anthropic
|
||||
client = anthropic.AsyncAnthropic(api_key=CLAUDE_API_KEY)
|
||||
msg = await client.messages.create(
|
||||
model="claude-opus-4-8-20251101",
|
||||
max_tokens=1024,
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "image", "source": {"type": "base64",
|
||||
"media_type": "image/png", "data": image_b64}},
|
||||
{"type": "text", "text": analyze_prompt},
|
||||
],
|
||||
}],
|
||||
)
|
||||
analysis = msg.content[0].text if msg.content else ""
|
||||
provider_used = "claude"
|
||||
log.info(f"[VISION] Claude fallback analysis complete ({len(analysis)} chars)")
|
||||
except Exception as e2:
|
||||
log.warning(f"[VISION] Claude fallback also failed: {e2}")
|
||||
analysis = f"Vision analysis unavailable: {e2}"
|
||||
elif do_analyze and not image_b64 and result.get("snapshot_type") == "text":
|
||||
# Text-only sysinfo snapshot — summarize with LLM
|
||||
# Text-only sysinfo snapshot — summarize with Ollama
|
||||
try:
|
||||
snap_text = json.dumps(result, indent=2)[:3000]
|
||||
prompt = f"Summarize this server system snapshot for JARVIS. Highlight any concerns:\n\n{snap_text}"
|
||||
analysis = await llm_call([{"role": "user", "content": prompt}], "groq")
|
||||
provider_used = "groq"
|
||||
analysis = await llm_call([{"role": "user", "content": prompt}], "ollama")
|
||||
provider_used = "ollama"
|
||||
except Exception as e:
|
||||
analysis = f"Analysis unavailable: {e}"
|
||||
|
||||
@@ -742,32 +758,47 @@ async def handle_vision(payload: dict) -> dict:
|
||||
if not image_b64:
|
||||
raise ValueError("No image data provided")
|
||||
|
||||
log.info(f"[VISION] Analysis: screenshot_id={screenshot_id} agent={hostname}")
|
||||
log.info(f"[VISION] Analysis: screenshot_id={screenshot_id} agent={hostname} provider={provider}")
|
||||
|
||||
try:
|
||||
import anthropic
|
||||
client = anthropic.AsyncAnthropic(api_key=CLAUDE_API_KEY)
|
||||
msg = await client.messages.create(
|
||||
model="claude-opus-4-8-20251101",
|
||||
max_tokens=2048,
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "image", "source": {"type": "base64",
|
||||
"media_type": "image/png", "data": image_b64}},
|
||||
{"type": "text", "text": prompt},
|
||||
],
|
||||
}],
|
||||
)
|
||||
analysis = msg.content[0].text if msg.content else ""
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Vision analysis failed: {e}")
|
||||
analysis = ""
|
||||
provider_used = provider
|
||||
|
||||
if provider == "ollama" or provider == "llava":
|
||||
analysis = await _ollama_vision_call(image_b64, prompt)
|
||||
provider_used = "ollama:llava"
|
||||
else:
|
||||
# Default: llava first, Claude fallback
|
||||
try:
|
||||
analysis = await _ollama_vision_call(image_b64, prompt)
|
||||
provider_used = "ollama:llava"
|
||||
log.info(f"[VISION] llava analysis complete ({len(analysis)} chars)")
|
||||
except Exception as e:
|
||||
log.warning(f"[VISION] llava failed: {e} — falling back to Claude")
|
||||
try:
|
||||
import anthropic
|
||||
client = anthropic.AsyncAnthropic(api_key=CLAUDE_API_KEY)
|
||||
msg = await client.messages.create(
|
||||
model="claude-opus-4-8-20251101",
|
||||
max_tokens=2048,
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "image", "source": {"type": "base64",
|
||||
"media_type": "image/png", "data": image_b64}},
|
||||
{"type": "text", "text": prompt},
|
||||
],
|
||||
}],
|
||||
)
|
||||
analysis = msg.content[0].text if msg.content else ""
|
||||
provider_used = "claude"
|
||||
except Exception as e2:
|
||||
raise RuntimeError(f"Vision analysis failed (llava: {e}, claude: {e2})")
|
||||
|
||||
# Update stored screenshot if we have an ID
|
||||
if screenshot_id:
|
||||
await db_execute(
|
||||
"UPDATE agent_screenshots SET vision_analysis=%s, vision_provider=%s WHERE id=%s",
|
||||
(analysis, "claude", int(screenshot_id))
|
||||
(analysis, provider_used, int(screenshot_id))
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -775,7 +806,7 @@ async def handle_vision(payload: dict) -> dict:
|
||||
"screenshot_id": screenshot_id,
|
||||
"prompt": prompt,
|
||||
"analysis": analysis,
|
||||
"provider": "claude",
|
||||
"provider": provider_used,
|
||||
}
|
||||
|
||||
|
||||
@@ -1022,7 +1053,7 @@ async def guardian_loop() -> None:
|
||||
"for Myron. Be direct about severity and what action to take. "
|
||||
"No markdown, no headers."
|
||||
)
|
||||
ai_msg = await llm_call([{"role": "user", "content": ai_prompt}], "groq")
|
||||
ai_msg = await llm_call([{"role": "user", "content": ai_prompt}], "ollama")
|
||||
# Update the most recent guardian event with AI analysis
|
||||
await db_execute(
|
||||
"""UPDATE guardian_events SET ai_analysis=%s
|
||||
@@ -1051,7 +1082,7 @@ async def _guardian_inject_chat(message: str) -> None:
|
||||
"""Write a proactive JARVIS message into the conversations table so the HUD picks it up."""
|
||||
try:
|
||||
await db_execute(
|
||||
"""INSERT INTO conversations (session_id, role, message, created_at)
|
||||
"""INSERT INTO conversations (session_id, role, content, created_at)
|
||||
VALUES ('guardian', 'assistant', %s, NOW())""",
|
||||
(message,)
|
||||
)
|
||||
@@ -1065,7 +1096,7 @@ async def handle_sitrep(payload: dict) -> dict:
|
||||
payload: { detail: brief|full, provider: groq }
|
||||
"""
|
||||
detail = payload.get("detail", "full")
|
||||
provider = payload.get("provider", "groq")
|
||||
provider = payload.get("provider", "ollama")
|
||||
|
||||
log.info(f"[GUARDIAN] SITREP requested (detail={detail})")
|
||||
|
||||
@@ -1806,7 +1837,7 @@ Keep the tone confident and action-oriented. Format with clear sections. Under 4
|
||||
|
||||
# Store in conversations so HUD can surface it
|
||||
await db_execute(
|
||||
"INSERT INTO conversations (session_id, role, message, created_at) VALUES ('guardian','assistant',%s,NOW())",
|
||||
"INSERT INTO conversations (session_id, role, content, created_at) VALUES ('guardian','assistant',%s,NOW())",
|
||||
(review_text,)
|
||||
)
|
||||
|
||||
@@ -2209,6 +2240,31 @@ async def lifespan(app: FastAPI):
|
||||
log.info(f"◈ JARVIS Arc Reactor v{VERSION} starting on {HOST}:{PORT}")
|
||||
await get_pool()
|
||||
await db_execute("UPDATE arc_status SET started_at=NOW(), last_heartbeat=NOW(), version=%s WHERE id=1", (VERSION,))
|
||||
await db_execute("""CREATE TABLE IF NOT EXISTS guardian_config (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
key_name VARCHAR(64) NOT NULL,
|
||||
value VARCHAR(255) NOT NULL DEFAULT '',
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_key (key_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""")
|
||||
await db_execute("""CREATE TABLE IF NOT EXISTS guardian_events (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
event_type VARCHAR(64) NOT NULL,
|
||||
severity ENUM('info','warning','critical') NOT NULL DEFAULT 'info',
|
||||
agent_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
hostname VARCHAR(128) NOT NULL DEFAULT '',
|
||||
metric VARCHAR(64) NOT NULL DEFAULT '',
|
||||
value FLOAT NOT NULL DEFAULT 0,
|
||||
threshold FLOAT NOT NULL DEFAULT 0,
|
||||
message TEXT NOT NULL,
|
||||
ai_analysis TEXT NOT NULL DEFAULT '',
|
||||
acknowledged TINYINT(1) NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
KEY idx_severity (severity),
|
||||
KEY idx_ack (acknowledged),
|
||||
KEY idx_created (created_at),
|
||||
KEY idx_agent (agent_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""")
|
||||
asyncio.create_task(job_poller())
|
||||
asyncio.create_task(heartbeat_loop())
|
||||
asyncio.create_task(guardian_loop())
|
||||
@@ -2728,13 +2784,13 @@ async def guardian_chat_events(since: str = ""):
|
||||
"""Return proactive guardian messages injected into conversations."""
|
||||
if since:
|
||||
rows = await db_fetchall(
|
||||
"SELECT id, message, created_at FROM conversations "
|
||||
"SELECT id, content AS message, created_at FROM conversations "
|
||||
"WHERE session_id='guardian' AND created_at > %s ORDER BY created_at ASC",
|
||||
(since,)
|
||||
)
|
||||
else:
|
||||
rows = await db_fetchall(
|
||||
"SELECT id, message, created_at FROM conversations "
|
||||
"SELECT id, content AS message, created_at FROM conversations "
|
||||
"WHERE session_id='guardian' ORDER BY created_at DESC LIMIT 10"
|
||||
)
|
||||
return rows or []
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
fastapi
|
||||
uvicorn[standard]
|
||||
aiomysql
|
||||
aiohttp
|
||||
anthropic
|
||||
trafilatura
|
||||
@@ -550,8 +550,22 @@ if ($action) {
|
||||
j(['ok'=>true,'msg'=>ucwords(str_replace('_',' ',$wId)).' triggered']);
|
||||
} else { bad('Unknown cron worker'); }
|
||||
} elseif ($wType === 'daemon' && $wId === 'arc_reactor' && $wAction === 'restart') {
|
||||
shell_exec('pkill -f reactor.py 2>/dev/null; sleep 1; cd /opt/jarvis-arc && source venv/bin/activate && nohup python3 reactor.py >> /home/jarvis.orbishosting.com/logs/arc_reactor.log 2>&1 &');
|
||||
shell_exec('bash -c "mkdir -p /home/jarvis.orbishosting.com/logs; systemctl restart jarvis-arc 2>/dev/null || (pkill -f reactor.py 2>/dev/null; sleep 1; cd /opt/jarvis-arc && source venv/bin/activate && nohup python3 reactor.py >> /home/jarvis.orbishosting.com/logs/arc_reactor.log 2>&1 &)"');
|
||||
j(['ok'=>true,'msg'=>'Arc Reactor restarting']);
|
||||
} elseif ($wType === 'daemon' && $wId === 'arc_reactor' && $wAction === 'setup') {
|
||||
$setupLog = '/home/jarvis.orbishosting.com/logs/arc_reactor.log';
|
||||
$cmd = 'bash -c "'.
|
||||
'mkdir -p /opt/jarvis-arc /home/jarvis.orbishosting.com/logs && '.
|
||||
'cp /var/www/jarvis/deploy/reactor.py /opt/jarvis-arc/reactor.py && '.
|
||||
'cp /var/www/jarvis/deploy/requirements.txt /opt/jarvis-arc/requirements.txt && '.
|
||||
'if [ ! -d /opt/jarvis-arc/venv ]; then python3 -m venv /opt/jarvis-arc/venv; fi && '.
|
||||
'/opt/jarvis-arc/venv/bin/pip install -q -r /opt/jarvis-arc/requirements.txt && '.
|
||||
'pkill -f reactor.py 2>/dev/null; sleep 1 && '.
|
||||
'cd /opt/jarvis-arc && source venv/bin/activate && '.
|
||||
'nohup python3 reactor.py >> '.$setupLog.' 2>&1 &'.
|
||||
'" >> '.$setupLog.' 2>&1';
|
||||
shell_exec($cmd);
|
||||
j(['ok'=>true,'msg'=>'Arc Reactor setup started — check log in ~30s then restart']);
|
||||
} else { bad('Invalid worker action'); }
|
||||
break;
|
||||
case 'arc_status':
|
||||
@@ -2332,7 +2346,10 @@ async function loadWorkers() {
|
||||
<td class="ts">jarvis-do :7474</td>
|
||||
<td>${rdot}${ron?'<span style="color:var(--green)">ONLINE</span>':'<span style="color:var(--red)">OFFLINE</span>'}</td>
|
||||
<td class="ts">${rinfo}</td>
|
||||
<td><button onclick="workerAction('daemon','arc_reactor','restart')" style="${wBtn('red')}">↻ RESTART</button></td>
|
||||
<td style="display:flex;gap:4px">
|
||||
<button onclick="workerAction('daemon','arc_reactor','restart')" style="${wBtn('red')}">↻ RESTART</button>
|
||||
<button onclick="workerAction('daemon','arc_reactor','setup')" style="${wBtn('orange')}">⚙ SETUP</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
async function loadDashboard() {
|
||||
|
||||
+4
-15
@@ -13,8 +13,8 @@ if (!defined('WEBHOOK_SECRET')) {
|
||||
echo json_encode(['error' => 'Webhook not configured']);
|
||||
exit;
|
||||
}
|
||||
define('DEPLOY_QUEUE', '/tmp/jarvis-deploy-queue.txt');
|
||||
define('DEPLOY_LOG', '/var/www/jarvis/logs/deploy.log');
|
||||
define('DEPLOY_QUEUE', '/tmp/jarvis-deploy-queue.txt');
|
||||
define('DEPLOY_LOG', '/var/www/jarvis/logs/deploy.log');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
@@ -50,20 +50,9 @@ if (!isset($repoMap[$repo])) {
|
||||
}
|
||||
|
||||
$path = $repoMap[$repo];
|
||||
|
||||
// NovaCPX lives on a private VM — the VM polls GitHub every minute via cron
|
||||
// This webhook receipt confirms GitHub delivered the push notification
|
||||
if ($path === '__NOVACPX_VM__') {
|
||||
$commit = $data['after'] ?? 'HEAD';
|
||||
$msg = "[" . date('Y-m-d H:i:s') . "] NovaCPX push by $pusher (commit: $commit) — VM will deploy within 1 min";
|
||||
file_put_contents(DEPLOY_LOG, $msg . "\n", FILE_APPEND | LOCK_EX);
|
||||
echo json_encode(['ok' => true, 'queued' => 'novacpx', 'commit' => $commit]);
|
||||
exit;
|
||||
}
|
||||
$ts = date('Y-m-d H:i:s');
|
||||
|
||||
file_put_contents(DEPLOY_QUEUE, $path . "\n", FILE_APPEND | LOCK_EX);
|
||||
|
||||
$msg = "[" . date('Y-m-d H:i:s') . "] Queued deploy: $repo by $pusher -> $path";
|
||||
file_put_contents(DEPLOY_LOG, $msg . "\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents(DEPLOY_LOG, "[$ts] Queued deploy: $repo by $pusher -> $path\n", FILE_APPEND | LOCK_EX);
|
||||
|
||||
echo json_encode(['ok' => true, 'queued' => $repo, 'path' => $path]);
|
||||
|
||||
Reference in New Issue
Block a user