fix: polish v0.9.7 micro update

This commit is contained in:
BigBodyCobain
2026-05-02 02:13:36 -06:00
parent 3a8db7f9cd
commit d515aba450
11 changed files with 247 additions and 55 deletions
+2
View File
@@ -104,6 +104,8 @@ def _match_prediction_markets(title: str, markets: list[dict]) -> dict | None:
"kalshi_pct": best_match.get("kalshi_pct"),
"consensus_pct": best_match.get("consensus_pct"),
"match_score": round(best_score, 2),
"slug": best_match.get("slug", ""),
"kalshi_ticker": best_match.get("kalshi_ticker", ""),
}
@@ -2,11 +2,13 @@
const fs = require('node:fs');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const scriptDir = __dirname;
const tauriDir = path.resolve(scriptDir, '..');
const repoRoot = path.resolve(tauriDir, '..', '..');
const backendDir = path.join(repoRoot, 'backend');
const privacyCoreDir = path.join(repoRoot, 'privacy-core');
const outputDir = path.join(tauriDir, 'src-tauri', 'backend-runtime');
const venvMarkerPath = path.join(backendDir, '.venv-dir');
const releaseAttestationPath = path.join(backendDir, 'data', 'release_attestation.json');
@@ -77,15 +79,58 @@ function ensureRuntimePrereqs() {
}
}
function privacyCoreArtifactName() {
if (process.platform === 'win32') return 'privacy_core.dll';
if (process.platform === 'darwin') return 'libprivacy_core.dylib';
return 'libprivacy_core.so';
}
function privacyCoreArtifactPath() {
return path.join(privacyCoreDir, 'target', 'release', privacyCoreArtifactName());
}
function ensurePrivacyCoreArtifact() {
const artifact = privacyCoreArtifactPath();
if (fs.existsSync(artifact)) {
return artifact;
}
console.log('privacy-core release library missing; building it for desktop packaging...');
const result = spawnSync(
'cargo',
['build', '--release', '--manifest-path', path.join(privacyCoreDir, 'Cargo.toml')],
{
cwd: repoRoot,
env: process.env,
stdio: 'inherit',
},
);
if (result.error || result.status !== 0) {
throw new Error(
'Failed to build privacy-core release library. Install Rust/Cargo and rerun the desktop build.',
);
}
if (!fs.existsSync(artifact)) {
throw new Error(`privacy-core build completed but artifact is missing: ${artifact}`);
}
return artifact;
}
function stageBackendRuntime() {
fs.rmSync(outputDir, { recursive: true, force: true });
fs.cpSync(backendDir, outputDir, {
recursive: true,
filter: shouldCopy,
});
stagePrivacyCoreArtifact();
stageReleaseAttestation();
}
function stagePrivacyCoreArtifact() {
const artifact = ensurePrivacyCoreArtifact();
const stagedPath = path.join(outputDir, path.basename(artifact));
fs.copyFileSync(artifact, stagedPath);
}
function stageReleaseAttestation() {
if (!fs.existsSync(releaseAttestationPath)) {
console.warn(`backend-runtime staged without release attestation: ${releaseAttestationPath}`);
@@ -91,7 +91,8 @@ pub async fn ensure_and_start_managed_backend(
.open(&stderr_log)
.map_err(|e| format!("managed_backend_stderr_log_failed:{e}"))?;
let mut child = Command::new(&python_bin)
let mut command = Command::new(&python_bin);
command
.current_dir(&runtime_root)
.arg("-m")
.arg("uvicorn")
@@ -103,7 +104,13 @@ pub async fn ensure_and_start_managed_backend(
.arg("--timeout-keep-alive")
.arg("120")
.env("PYTHONUNBUFFERED", "1")
.env("SB_DATA_DIR", data_dir.as_os_str())
.env("SB_DATA_DIR", data_dir.as_os_str());
if let Some(privacy_core_lib) = bundled_privacy_core_lib(&runtime_root) {
command.env("PRIVACY_CORE_LIB", privacy_core_lib.as_os_str());
}
let mut child = command
.stdout(Stdio::from(stdout))
.stderr(Stdio::from(stderr))
.spawn()
@@ -191,6 +198,18 @@ fn sync_release_attestation(bundled_root: &Path, install_root: &Path) -> Result<
Ok(())
}
fn bundled_privacy_core_lib(runtime_root: &Path) -> Option<PathBuf> {
let file_name = if cfg!(target_os = "windows") {
"privacy_core.dll"
} else if cfg!(target_os = "macos") {
"libprivacy_core.dylib"
} else {
"libprivacy_core.so"
};
let candidate = runtime_root.join(file_name);
candidate.exists().then_some(candidate)
}
fn release_attestation_path(root: &Path) -> PathBuf {
RELEASE_ATTESTATION_RELATIVE_PATH
.iter()
@@ -1,7 +1,7 @@
'use client';
import React, { useState, useEffect, useRef, useMemo } from 'react';
import { Terminal, Radio, Globe, Key, LogOut, Activity, Vote, User, ArrowRightLeft, Briefcase, Mail, Brain, GitBranch, Cpu, KeyRound } from 'lucide-react';
import { Terminal, Radio, Globe, Key, Activity, Vote, User, ArrowRightLeft, Briefcase, Mail, Brain, GitBranch, Cpu, KeyRound } from 'lucide-react';
import { getNodeIdentity, getWormholeIdentityDescriptor } from '@/mesh/meshIdentity';
import {
activateWormholeGatePersona,
@@ -128,7 +128,6 @@ const SECTIONS = [
{ name: 'EXCHANGE', icon: <ArrowRightLeft size={14} className="mr-2" /> },
{ name: 'PROFILE', icon: <User size={14} className="mr-2" /> },
{ name: 'MESSAGES', icon: <Mail size={14} className="mr-2" /> },
{ name: 'EXIT', icon: <LogOut size={14} className="mr-2" /> },
];
interface CommandHistory {
@@ -32,12 +32,22 @@ export default function NetworkStats() {
fetchInfonetNodeStatusSnapshot(true).catch(() => null),
]);
if (!alive) return;
const knownNodes = Number(infonet?.known_nodes || 0);
const syncPeerCount = Number(infonet?.bootstrap?.sync_peer_count || 0);
const defaultSyncPeerCount = Number(infonet?.bootstrap?.default_sync_peer_count || 0);
const lastPeerUrl = String(infonet?.sync_runtime?.last_peer_url || '').trim();
const visibleInfonetNodes = Math.max(
knownNodes,
syncPeerCount,
defaultSyncPeerCount,
lastPeerUrl ? 1 : 0,
);
setStats({
meshtastic: Number(channelsRes?.total_live || channelsRes?.total_nodes || meshRes?.signal_counts?.meshtastic || 0),
aprs: Number(meshRes?.signal_counts?.aprs || 0),
infonetNodes: Number(infonet?.known_nodes || 0),
infonetNodes: visibleInfonetNodes,
infonetEvents: Number(infonet?.total_events || 0),
syncPeers: Number(infonet?.bootstrap?.sync_peer_count || 0),
syncPeers: syncPeerCount,
nodeEnabled: Boolean(infonet?.node_enabled),
syncOutcome: String(infonet?.sync_runtime?.last_outcome || 'offline').toLowerCase(),
});
@@ -2,7 +2,7 @@
import React, { useEffect } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { X, Minus } from 'lucide-react';
import { X } from 'lucide-react';
import InfonetShell from './InfonetShell';
interface InfonetTerminalProps {
@@ -55,13 +55,6 @@ export default function InfonetTerminal({
</span>
</div>
<div className="flex items-center gap-1">
<button
onClick={onClose}
className="p-1 text-gray-600 hover:text-gray-300 transition-colors"
title="Minimize"
>
<Minus size={14} />
</button>
<button
onClick={onClose}
className="p-1 text-gray-600 hover:text-red-400 transition-colors"
+69 -15
View File
@@ -16,7 +16,7 @@ import 'maplibre-gl/dist/maplibre-gl.css';
import { computeNightPolygon } from '@/utils/solarTerminator';
import { darkStyle, lightStyle } from '@/components/map/styles/mapStyles';
import maplibregl from 'maplibre-gl';
import { AlertTriangle, Radio, Activity, Play, Satellite } from 'lucide-react';
import { AlertTriangle, Radio, Activity, Play, Satellite, ExternalLink, Info } from 'lucide-react';
import WikiImage from '@/components/WikiImage';
import FishingDestinationRoute from '@/components/map/FishingDestinationRoute';
import { useTheme } from '@/lib/ThemeContext';
@@ -293,6 +293,15 @@ function probeRasterTile(url: string): Promise<boolean> {
});
}
function buildPolymarketUrl(prediction: { slug?: string; title?: string } | null | undefined): string {
const slug = String(prediction?.slug || '').trim();
if (slug) return `https://polymarket.com/event/${encodeURIComponent(slug)}`;
const title = String(prediction?.title || '').trim();
return title
? `https://polymarket.com/search?query=${encodeURIComponent(title)}`
: 'https://polymarket.com/markets';
}
const MaplibreViewer = ({
activeLayers,
activeFilters,
@@ -5712,29 +5721,56 @@ const MaplibreViewer = ({
<div className="px-5 pb-3">
<div className="grid grid-cols-3 gap-2">
{/* Oracle Score */}
<div className={`border rounded p-3 text-center ${oTierBg || 'bg-black/40 border-cyan-800/30'}`}>
<div className="text-[9px] text-[var(--text-muted)] tracking-[0.15em] mb-1.5">ORACLE SCORE</div>
<label className={`border rounded p-3 text-center transition-colors hover:border-white/40 cursor-pointer ${oTierBg || 'bg-black/40 border-cyan-800/30'}`}>
<input type="checkbox" className="peer sr-only" aria-label="Explain Oracle Score" />
<div className="flex items-center justify-center gap-1 text-[9px] text-[var(--text-muted)] tracking-[0.15em] mb-1.5">
<span>ORACLE SCORE</span>
<Info size={10} />
</div>
<div className={`text-[28px] font-bold leading-none ${oTierColor || 'text-gray-500'}`}>
{oScore != null ? oScore.toFixed(1) : '—'}
</div>
{oTier && <div className={`text-[10px] font-bold ${oTierColor} mt-1`}>{oTier}</div>}
</div>
<div className="hidden peer-checked:block mt-2 border-t border-white/10 pt-2 text-left text-[10px] leading-relaxed text-cyan-100">
<div className="text-cyan-400 font-bold tracking-[0.16em] mb-1">SCALE</div>
<p>0-10 weighted signal score combining alert risk and source confidence.</p>
<p className="mt-1 text-[var(--text-muted)]">0-3 low, 4-5 moderate, 6-7 elevated, 8-10 critical.</p>
</div>
</label>
{/* Sentiment */}
<div className={`border rounded p-3 text-center ${sentBg || 'bg-black/40 border-cyan-800/30'}`}>
<div className="text-[9px] text-[var(--text-muted)] tracking-[0.15em] mb-1.5">SENTIMENT</div>
<label className={`border rounded p-3 text-center transition-colors hover:border-white/40 cursor-pointer ${sentBg || 'bg-black/40 border-cyan-800/30'}`}>
<input type="checkbox" className="peer sr-only" aria-label="Explain Sentiment" />
<div className="flex items-center justify-center gap-1 text-[9px] text-[var(--text-muted)] tracking-[0.15em] mb-1.5">
<span>SENTIMENT</span>
<Info size={10} />
</div>
<div className={`text-[28px] font-bold leading-none ${sentColor || 'text-gray-500'}`}>
{sent != null ? <>{sentArrow} {sent > 0 ? '+' : ''}{sent.toFixed(2)}</> : '—'}
</div>
{sentLabel && <div className={`text-[10px] font-bold ${sentColor} mt-1`}>{sentLabel}</div>}
</div>
<div className="hidden peer-checked:block mt-2 border-t border-white/10 pt-2 text-left text-[10px] leading-relaxed text-cyan-100">
<div className="text-cyan-400 font-bold tracking-[0.16em] mb-1">SCALE</div>
<p>-1.00 to +1.00 headline tone. Negative reads more adverse; positive reads more constructive.</p>
<p className="mt-1 text-[var(--text-muted)]">Below -0.10 negative, -0.10 to +0.10 neutral, above +0.10 positive. It measures tone, not truth.</p>
</div>
</label>
{/* Threat Level */}
<div className={`border rounded p-3 text-center ${rs >= 8 ? 'bg-red-500/10 border-red-500/30' : rs >= 6 ? 'bg-orange-500/10 border-orange-500/30' : rs >= 4 ? 'bg-yellow-500/10 border-yellow-500/30' : 'bg-green-500/10 border-green-500/30'}`}>
<div className="text-[9px] text-[var(--text-muted)] tracking-[0.15em] mb-1.5">RISK LEVEL</div>
<label className={`border rounded p-3 text-center transition-colors hover:border-white/40 cursor-pointer ${rs >= 8 ? 'bg-red-500/10 border-red-500/30' : rs >= 6 ? 'bg-orange-500/10 border-orange-500/30' : rs >= 4 ? 'bg-yellow-500/10 border-yellow-500/30' : 'bg-green-500/10 border-green-500/30'}`}>
<input type="checkbox" className="peer sr-only" aria-label="Explain Risk Level" />
<div className="flex items-center justify-center gap-1 text-[9px] text-[var(--text-muted)] tracking-[0.15em] mb-1.5">
<span>RISK LEVEL</span>
<Info size={10} />
</div>
<div className={`text-[28px] font-bold leading-none ${threatColor}`}>{rs}/10</div>
<div className={`text-[10px] font-bold ${threatColor} mt-1`}>
{rs >= 9 ? 'CRITICAL' : rs >= 7 ? 'HIGH' : rs >= 4 ? 'MEDIUM' : 'LOW'}
</div>
</div>
<div className="hidden peer-checked:block mt-2 border-t border-white/10 pt-2 text-left text-[10px] leading-relaxed text-cyan-100">
<div className="text-cyan-400 font-bold tracking-[0.16em] mb-1">SCALE</div>
<p>0-10 operational severity estimate based on source, topic, keywords, corroboration, and alert context.</p>
<p className="mt-1 text-[var(--text-muted)]">0-3 low, 4-6 medium, 7-8 high, 9-10 critical.</p>
</div>
</label>
</div>
</div>
@@ -5742,8 +5778,20 @@ const MaplibreViewer = ({
{pred && pred.consensus_pct != null && (
<div className="px-5 pb-3">
<div className="bg-purple-950/30 border border-purple-500/40 rounded p-4">
<div className="text-[10px] text-purple-400 tracking-[0.2em] font-bold mb-2">
PREDICTION MARKET ANALYSIS
<div className="flex items-center justify-between gap-3 mb-2">
<div className="text-[10px] text-purple-400 tracking-[0.2em] font-bold">
PREDICTION MARKET ANALYSIS
</div>
{pred.polymarket_pct != null && (
<button
type="button"
onClick={() => window.open(buildPolymarketUrl(pred), '_blank', 'noopener,noreferrer')}
className="inline-flex items-center gap-1 text-[10px] text-purple-200 hover:text-white border border-purple-500/30 hover:border-purple-300/70 px-2 py-1 rounded transition-colors"
title="Open this market on Polymarket"
>
POLYMARKET <ExternalLink size={10} />
</button>
)}
</div>
<div className="text-[14px] text-purple-200 font-bold leading-snug mb-3">
&quot;{pred.title}&quot;
@@ -5760,10 +5808,16 @@ const MaplibreViewer = ({
</div>
<div className="flex gap-6 text-[11px]">
{pred.polymarket_pct != null && (
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => window.open(buildPolymarketUrl(pred), '_blank', 'noopener,noreferrer')}
className="flex items-center gap-2 hover:text-white transition-colors"
title="Open this market on Polymarket"
>
<span className="text-purple-400/70">Polymarket</span>
<span className="text-white font-bold text-[13px]">{pred.polymarket_pct}%</span>
</div>
<ExternalLink size={10} className="text-purple-400/70" />
</button>
)}
{pred.kalshi_pct != null && (
<div className="flex items-center gap-2">
@@ -5834,7 +5888,7 @@ const MaplibreViewer = ({
onClick={() => window.open(item.link, '_blank', 'noopener,noreferrer')}
className={`${threatColor} hover:text-white text-[12px] font-bold underline underline-offset-2 cursor-pointer`}
>
VIEW FULL REPORT
GO TO ARTICLE
</button>
) : <span />}
<button
+33 -25
View File
@@ -4,7 +4,9 @@ import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { X, ExternalLink, Key, Shield, Radar, Globe, Satellite, Ship, Radio } from 'lucide-react';
const STORAGE_KEY = 'shadowbroker_onboarding_complete';
const CURRENT_ONBOARDING_VERSION = '0.9.7';
const STORAGE_KEY = `shadowbroker_onboarding_complete_v${CURRENT_ONBOARDING_VERSION}`;
const LEGACY_STORAGE_KEY = 'shadowbroker_onboarding_complete';
const API_GUIDES = [
{
@@ -62,11 +64,13 @@ const OnboardingModal = React.memo(function OnboardingModal({
const handleDismiss = () => {
localStorage.setItem(STORAGE_KEY, 'true');
localStorage.setItem(LEGACY_STORAGE_KEY, 'true');
onClose();
};
const handleOpenSettings = () => {
localStorage.setItem(STORAGE_KEY, 'true');
localStorage.setItem(LEGACY_STORAGE_KEY, 'true');
onClose();
onOpenSettings();
};
@@ -123,7 +127,7 @@ const OnboardingModal = React.memo(function OnboardingModal({
{/* Step Indicators */}
<div className="flex gap-2 px-6 pt-4">
{['Welcome', 'API Keys', 'Free Sources'].map((label, i) => (
{['API Keys', 'Trust Modes', 'Free Sources'].map((label, i) => (
<button
key={label}
onClick={() => setStep(i)}
@@ -140,36 +144,23 @@ const OnboardingModal = React.memo(function OnboardingModal({
{/* Content */}
<div className="flex-1 overflow-y-auto styled-scrollbar p-6">
{step === 0 && (
{step === 1 && (
<div className="space-y-4">
<div className="text-center py-4">
<div className="text-lg font-bold tracking-[0.3em] text-[var(--text-primary)] font-mono mb-2">
S H A D O W <span className="text-cyan-400">B R O K E R</span>
T R U S T <span className="text-cyan-400">M O D E S</span>
</div>
<p className="text-[11px] text-[var(--text-secondary)] font-mono leading-relaxed max-w-md mx-auto">
<p className="hidden">
Real-time OSINT dashboard aggregating 12+ live intelligence sources. Flights,
ships, satellites, earthquakes, conflicts, and more all on one map.
</p>
<p className="text-[11px] text-[var(--text-secondary)] font-mono leading-relaxed max-w-md mx-auto">
These modes explain what lane the network is using. Set up the API keys first,
then use this screen to understand public mesh versus private Wormhole paths.
</p>
</div>
<div className="bg-yellow-950/20 border border-yellow-500/20 p-4">
<div className="flex items-start gap-2">
<Key size={14} className="text-yellow-500 mt-0.5 flex-shrink-0" />
<div>
<p className="text-[11px] text-yellow-400 font-mono font-bold mb-1">
API Keys Required
</p>
<p className="text-sm text-[var(--text-secondary)] font-mono leading-relaxed">
Two API keys are needed for full functionality:{' '}
<span className="text-cyan-400">OpenSky Network</span> (flights) and{' '}
<span className="text-blue-400">AIS Stream</span> (ships). Both are free.
Without them, some panels will show no data.
</p>
</div>
</div>
</div>
<div className="bg-green-950/20 border border-green-500/20 p-4">
<div className="hidden">
<div className="flex items-start gap-2">
<Globe size={14} className="text-green-500 mt-0.5 flex-shrink-0" />
<div>
@@ -216,8 +207,24 @@ const OnboardingModal = React.memo(function OnboardingModal({
</div>
)}
{step === 1 && (
{step === 0 && (
<div className="space-y-4">
<div className="bg-yellow-950/20 border border-yellow-500/20 p-4">
<div className="flex items-start gap-2">
<Key size={14} className="text-yellow-500 mt-0.5 flex-shrink-0" />
<div>
<p className="text-[11px] text-yellow-400 font-mono font-bold mb-1">
START HERE
</p>
<p className="text-sm text-[var(--text-secondary)] font-mono leading-relaxed">
OpenSky Network and AIS Stream are the free keys that make ShadowBroker
useful immediately: live aircraft and vessel tracking. Add these first;
trust modes and the no-key sources can come next.
</p>
</div>
</div>
</div>
{API_GUIDES.map((api) => (
<div
key={api.name}
@@ -272,7 +279,8 @@ const OnboardingModal = React.memo(function OnboardingModal({
<div className="space-y-3">
<p className="text-sm text-[var(--text-secondary)] font-mono mb-3">
These data sources are completely free and require no API keys. They activate
automatically on launch.
automatically on launch, while OpenSky and AIS Stream unlock the richer live
aviation and maritime experience.
</p>
<div className="grid grid-cols-2 gap-2">
{FREE_SOURCES.map((src) => (
+2
View File
@@ -635,6 +635,8 @@ export interface NewsArticle {
kalshi_pct: number | null;
consensus_pct: number | null;
match_score: number;
slug?: string;
kalshi_ticker?: string;
} | null;
}
+35
View File
@@ -84,6 +84,9 @@ for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":8000 "') do (
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":3000 "') do (
taskkill /F /PID %%a >nul 2>&1
)
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":8787 "') do (
taskkill /F /PID %%a >nul 2>&1
)
:: Brief pause to let OS release the ports
timeout /t 1 /nobreak >nul
@@ -99,6 +102,11 @@ if %errorlevel% equ 0 (
echo [!] WARNING: Port 3000 is still occupied! Waiting 3s for OS cleanup...
timeout /t 3 /nobreak >nul
)
netstat -ano | findstr ":8787 " | findstr "LISTENING" >nul 2>&1
if %errorlevel% equ 0 (
echo [!] WARNING: Port 8787 is still occupied! Waiting 3s for OS cleanup...
timeout /t 3 /nobreak >nul
)
echo [*] Ports clear.
:: ────────────────────────────────────────────────────────────────────
@@ -225,6 +233,33 @@ if not exist "node_modules\ws" (
call npm ci --omit=dev --silent
)
echo [*] Backend Node.js dependencies OK.
echo.
echo [*] Checking privacy-core shared library...
set "PRIVACY_CORE_DLL=%ROOT%\privacy-core\target\release\privacy_core.dll"
if not exist "%PRIVACY_CORE_DLL%" (
where cargo >nul 2>&1
if errorlevel 1 (
echo [!] WARNING: privacy-core DLL is missing and Rust/Cargo is not installed.
echo [!] Infonet private lanes and gates need this library.
echo [!] Install Rust from https://rustup.rs/ and run:
echo [!] cargo build --release --manifest-path "%ROOT%\privacy-core\Cargo.toml"
echo.
) else (
echo [*] Building privacy-core release DLL...
cd /d "%ROOT%"
cargo build --release --manifest-path "%ROOT%\privacy-core\Cargo.toml"
if errorlevel 1 (
echo [!] ERROR: privacy-core build failed. Infonet private lanes need this DLL.
echo.
pause
exit /b 1
)
cd /d "%ROOT%\backend"
)
)
if exist "%PRIVACY_CORE_DLL%" echo [*] privacy-core DLL OK.
cd /d "%ROOT%"
echo.
+26 -1
View File
@@ -64,7 +64,7 @@ echo ""
echo "[*] Clearing zombie processes..."
# Kill anything listening on ports 8000 or 3000
for PORT in 8000 3000; do
for PORT in 8000 3000 8787; do
if command -v lsof &> /dev/null; then
PIDS=$(lsof -ti :$PORT 2>/dev/null)
elif command -v ss &> /dev/null; then
@@ -82,6 +82,7 @@ done
# Kill orphaned uvicorn and ais_proxy processes
pkill -9 -f "uvicorn.*main:app" 2>/dev/null
pkill -9 -f "ais_proxy" 2>/dev/null
pkill -9 -f "wormhole_server.py" 2>/dev/null
# Brief pause for OS to release ports
sleep 1
@@ -192,6 +193,30 @@ if [ ! -d "node_modules/ws" ]; then
fi
echo "[*] Backend Node.js dependencies OK."
echo ""
echo "[*] Checking privacy-core shared library..."
PRIVACY_CORE_SO="$SCRIPT_DIR/privacy-core/target/release/libprivacy_core.so"
PRIVACY_CORE_DYLIB="$SCRIPT_DIR/privacy-core/target/release/libprivacy_core.dylib"
if [ ! -f "$PRIVACY_CORE_SO" ] && [ ! -f "$PRIVACY_CORE_DYLIB" ]; then
if command -v cargo >/dev/null 2>&1; then
echo "[*] Building privacy-core release library..."
cargo build --release --manifest-path "$SCRIPT_DIR/privacy-core/Cargo.toml"
if [ $? -ne 0 ]; then
echo "[!] ERROR: privacy-core build failed. Infonet private lanes need this library."
exit 1
fi
else
echo "[!] WARNING: privacy-core shared library is missing and Rust/Cargo is not installed."
echo "[!] Infonet private lanes and gates need this library."
echo "[!] Install Rust from https://rustup.rs/ and run:"
echo "[!] cargo build --release --manifest-path \"$SCRIPT_DIR/privacy-core/Cargo.toml\""
echo ""
fi
fi
if [ -f "$PRIVACY_CORE_SO" ] || [ -f "$PRIVACY_CORE_DYLIB" ]; then
echo "[*] privacy-core shared library OK."
fi
cd "$SCRIPT_DIR"
echo ""