diff --git a/backend/services/oracle_service.py b/backend/services/oracle_service.py index 5667920..9027740 100644 --- a/backend/services/oracle_service.py +++ b/backend/services/oracle_service.py @@ -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", ""), } diff --git a/desktop-shell/tauri-skeleton/scripts/build-backend-runtime.cjs b/desktop-shell/tauri-skeleton/scripts/build-backend-runtime.cjs index d30ad2e..ccb18a9 100644 --- a/desktop-shell/tauri-skeleton/scripts/build-backend-runtime.cjs +++ b/desktop-shell/tauri-skeleton/scripts/build-backend-runtime.cjs @@ -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}`); diff --git a/desktop-shell/tauri-skeleton/src-tauri/src/backend_runtime.rs b/desktop-shell/tauri-skeleton/src-tauri/src/backend_runtime.rs index 76e383f..0244437 100644 --- a/desktop-shell/tauri-skeleton/src-tauri/src/backend_runtime.rs +++ b/desktop-shell/tauri-skeleton/src-tauri/src/backend_runtime.rs @@ -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 { + 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() diff --git a/frontend/src/components/InfonetTerminal/InfonetShell.tsx b/frontend/src/components/InfonetTerminal/InfonetShell.tsx index ac8629a..e2557d8 100644 --- a/frontend/src/components/InfonetTerminal/InfonetShell.tsx +++ b/frontend/src/components/InfonetTerminal/InfonetShell.tsx @@ -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: }, { name: 'PROFILE', icon: }, { name: 'MESSAGES', icon: }, - { name: 'EXIT', icon: }, ]; interface CommandHistory { diff --git a/frontend/src/components/InfonetTerminal/NetworkStats.tsx b/frontend/src/components/InfonetTerminal/NetworkStats.tsx index 9c36a37..0d6a3eb 100644 --- a/frontend/src/components/InfonetTerminal/NetworkStats.tsx +++ b/frontend/src/components/InfonetTerminal/NetworkStats.tsx @@ -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(), }); diff --git a/frontend/src/components/InfonetTerminal/index.tsx b/frontend/src/components/InfonetTerminal/index.tsx index f86e1aa..f2d3ca6 100644 --- a/frontend/src/components/InfonetTerminal/index.tsx +++ b/frontend/src/components/InfonetTerminal/index.tsx @@ -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({
- + )}
"{pred.title}" @@ -5760,10 +5808,16 @@ const MaplibreViewer = ({
{pred.polymarket_pct != null && ( -
+
+ + )} {pred.kalshi_pct != null && (
@@ -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 ↗ ) : }