mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-05-08 02:16:41 +02:00
fix: polish v0.9.7 micro update
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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">
|
||||
"{pred.title}"
|
||||
@@ -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
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -635,6 +635,8 @@ export interface NewsArticle {
|
||||
kalshi_pct: number | null;
|
||||
consensus_pct: number | null;
|
||||
match_score: number;
|
||||
slug?: string;
|
||||
kalshi_ticker?: string;
|
||||
} | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
Reference in New Issue
Block a user