Spaces:
Running
Running
acecalisto3
feat(binder): identity survives a rebuild; distribution is observed, not assumed
2f6fd0e | """ | |
| signalmesh_api.py — SignalMesh Sovereign Liquid Matrix Gateway | |
| ======================================================= | |
| Public API: /api/* — requires X-SignalMesh-Key header | |
| Dashboard: /ui/* — server-side key, no browser exposure | |
| Landing: / — interactive HTML dashboard | |
| """ | |
| from __future__ import annotations | |
| import asyncio, hashlib, html, json, os, sys, time, re | |
| import urllib.parse | |
| from contextlib import asynccontextmanager | |
| from pathlib import Path | |
| from typing import Any, Dict, List, Optional | |
| _HERE = Path(__file__).parent | |
| sys.path.insert(0, str(_HERE)) | |
| import types | |
| _app_pkg = types.ModuleType("app") | |
| _log_pkg = types.ModuleType("app.logger") | |
| class _L: | |
| def info(self,*a,**k): pass | |
| def warning(self,*a,**k): pass | |
| def error(self,*a,**k): pass | |
| def debug(self,*a,**k): pass | |
| _log_pkg.logger = _L() | |
| sys.modules.setdefault("app", _app_pkg) | |
| sys.modules.setdefault("app.logger", _log_pkg) | |
| _app_pkg.logger = _log_pkg.logger | |
| import feedparser, httpx | |
| from fastapi import FastAPI, Header, HTTPException, Request | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse | |
| from pydantic import BaseModel | |
| from core.signal_registry import SignalRegistry, signal_registry | |
| from core.spatial_grid import SpatialGridManager | |
| from core.feed_hydrator import feed_hydrator | |
| from core.source_hydrator import source_hydrator | |
| from core import mport_articles | |
| from core import binder | |
| from core import world_state | |
| from core import broadcast_journal | |
| from core.antenna import AntennaForge, TARGETS as ANTENNA_TARGETS | |
| from core.manifest_parser import manifest_registry, ManifestParser | |
| from core.sec_omega import guarded_broadcast, sec_omega | |
| from core.coordinate_resolver import coordinate_resolver, NAMESPACES, SLOTS | |
| from core.relationship_graph import relationship_graph | |
| from core import submesh as mport | |
| from tools.autonomous_pumps import adys_pump | |
| grid = SpatialGridManager(index_path=str(_HERE / "workspace" / "spatial_index.json")) | |
| _ROWS = SpatialGridManager.GRID_ROWS | |
| _COLS = SpatialGridManager.GRID_COLS | |
| _PROT = ("security_","sql_","auth_","key_","secret_","credential_") | |
| _SEC_Q: List[Dict[str,Any]] = [] | |
| antenna_forge = AntennaForge(signal_registry) | |
| # ── Key tier system ─────────────────────────────────────────────────────────── | |
| # Tiers: admin (unlimited) | paid (unlimited) | update (update-channel only). | |
| # No public free tier. The legacy demo key lives on permanently as the OCODX | |
| # update-channel key: shipped builds poll release/update signals with it | |
| # (submesh reads + ocodx.* tune_in) and nothing else. | |
| _UPDATE_KEY = os.environ.get("SIGNALMESH_UPDATE_KEY", "smesh-free-demo") | |
| _ADMIN_KEY = os.environ.get("SIGNALMESH_API_KEY", "") | |
| _PAID_KEYS = set(filter(None, os.environ.get("SIGNALMESH_PAID_KEYS", "").split(","))) | |
| def _key_values(raw: str) -> set[str]: | |
| keys = set() | |
| for part in raw.split(","): | |
| part = part.strip() | |
| if not part: | |
| continue | |
| label, sep, value = part.partition(":") | |
| keys.add((value if sep else label).strip()) | |
| return {k for k in keys if k} | |
| def _owner_keys() -> set[str]: | |
| return ( | |
| _key_values(os.environ.get("SIGNALMESH_OWNER_KEYS", "")) | |
| | _key_values(os.environ.get("SIGNALMESH_OWNER_KEY", "")) | |
| | _key_values(os.environ.get("OWNER_KEY", "")) | |
| ) | |
| # Rate limit: the update key is shared by every shipped build | |
| _UPDATE_RATE_LIMIT = 5000 # calls per rolling 24h window | |
| _update_usage: Dict[str, List[float]] = {} # key → list of call timestamps | |
| def _tier(k: Optional[str]) -> str: | |
| if k and (k == _ADMIN_KEY or k in _owner_keys()): | |
| return "admin" | |
| if k and k in _PAID_KEYS: | |
| return "paid" | |
| if k and k == _UPDATE_KEY: | |
| return "update" | |
| return "none" | |
| def _check_key(k: Optional[str], require_paid: bool = False, allow_update: bool = False): | |
| t = _tier(k) | |
| if t == "none": | |
| if k and _db_get_by_key(k): | |
| return # valid DB paid user | |
| raise HTTPException(401, detail={ | |
| "error": "Invalid or missing X-SignalMesh-Key", | |
| "get_paid_key": "https://kyklos.io/apps/signalmesh/", | |
| }) | |
| if t == "update": | |
| if not allow_update or require_paid: | |
| raise HTTPException(403, detail={ | |
| "error": "This key is restricted to the OCODX update channel", | |
| "get_paid_key": "https://kyklos.io/apps/signalmesh/", | |
| }) | |
| _rate_check(k) | |
| def _rate_check(k: str): | |
| now = time.time() | |
| window = now - 86400 # 24h | |
| hits = [ts for ts in _update_usage.get(k, []) if ts > window] | |
| if len(hits) >= _UPDATE_RATE_LIMIT: | |
| raise HTTPException(429, detail={ | |
| "error": f"Update-channel limit ({_UPDATE_RATE_LIMIT} calls/24h) reached", | |
| }) | |
| hits.append(now) | |
| _update_usage[k] = hits | |
| # ── User database (SQLite + HF dataset backup) ──────────────────────────────── | |
| import sqlite3, secrets, threading | |
| # Persist outside /tmp where possible: HF Spaces persistent storage is /data. | |
| # Falls back to /tmp (still backed up to the HF dataset) if /data isn't writable. | |
| _DB_DIR = os.environ.get("SIGNALMESH_DB_DIR") or ( | |
| "/data" if os.path.isdir("/data") and os.access("/data", os.W_OK) else "/tmp") | |
| _DB_PATH = os.path.join(_DB_DIR, "signalmesh_users.db") | |
| _DB_LOCK = threading.Lock() | |
| def _db_conn(): | |
| conn = sqlite3.connect(_DB_PATH) | |
| conn.row_factory = sqlite3.Row | |
| return conn | |
| def _db_init(): | |
| try: | |
| from huggingface_hub import hf_hub_download | |
| import shutil | |
| p = hf_hub_download("acecalisto3/signalmesh-users", "users.db", | |
| repo_type="dataset", local_dir="/tmp") | |
| shutil.copy(p, _DB_PATH) | |
| print("[UserDB] Loaded from HF dataset") | |
| except Exception: | |
| print("[UserDB] Starting fresh") | |
| with _DB_LOCK: | |
| conn = _db_conn() | |
| conn.execute("""CREATE TABLE IF NOT EXISTS users ( | |
| id TEXT PRIMARY KEY, | |
| email TEXT NOT NULL, | |
| api_key TEXT UNIQUE NOT NULL, | |
| tier TEXT DEFAULT 'paid', | |
| stripe_customer_id TEXT, | |
| stripe_session_id TEXT, | |
| active INTEGER DEFAULT 1, | |
| created_at REAL, | |
| last_seen REAL, | |
| call_count INTEGER DEFAULT 0 | |
| )""") | |
| conn.commit(); conn.close() | |
| def _db_backup(): | |
| try: | |
| from huggingface_hub import HfApi | |
| _hf = HfApi() | |
| try: | |
| _hf.create_repo("signalmesh-users", repo_type="dataset", private=True) | |
| except Exception: | |
| pass | |
| _hf.upload_file(path_or_fileobj=_DB_PATH, path_in_repo="users.db", | |
| repo_id="acecalisto3/signalmesh-users", repo_type="dataset", | |
| commit_message="auto-backup users.db") | |
| except Exception as e: | |
| print(f"[UserDB] Backup failed: {e}") | |
| def _db_create_user( | |
| email: str, | |
| stripe_customer_id: str, | |
| stripe_session_id: str, | |
| tier: str = "managed_monthly", | |
| ) -> str: | |
| key = "smesh-" + secrets.token_urlsafe(24) | |
| uid = secrets.token_hex(8) | |
| with _DB_LOCK: | |
| conn = _db_conn() | |
| conn.execute("INSERT OR IGNORE INTO users VALUES (?,?,?,?,?,?,1,?,?,0)", | |
| (uid, email, key, tier, stripe_customer_id, | |
| stripe_session_id, time.time(), time.time())) | |
| conn.commit(); conn.close() | |
| threading.Thread(target=_db_backup, daemon=True).start() | |
| return key | |
| def _db_get_by_key(key: str): | |
| with _DB_LOCK: | |
| conn = _db_conn() | |
| row = conn.execute("SELECT * FROM users WHERE api_key=? AND active=1", (key,)).fetchone() | |
| if row: | |
| conn.execute("UPDATE users SET last_seen=?,call_count=call_count+1 WHERE api_key=?", | |
| (time.time(), key)) | |
| conn.commit() | |
| conn.close() | |
| return dict(row) if row else None | |
| def _dev_from_key(key: str) -> int: | |
| """Read the device quota out of a signed OCODX1.<payload>.<sig> token.""" | |
| try: | |
| import base64 as _b64 | |
| pb = key.split(".")[1] | |
| data = json.loads(_b64.urlsafe_b64decode(pb + "=" * (-len(pb) % 4))) | |
| return int(data.get("dev", 0)) | |
| except Exception: | |
| return 0 | |
| def _db_get_by_email(email: str): | |
| with _DB_LOCK: | |
| conn = _db_conn() | |
| row = conn.execute("SELECT * FROM users WHERE email=? AND active=1 " | |
| "ORDER BY created_at DESC LIMIT 1", (email,)).fetchone() | |
| conn.close() | |
| return dict(row) if row else None | |
| def _db_add_seats(email: str, add: int, cid: str, sid: str) -> str: | |
| """Upgrade an existing license in place: re-mint the same email's key with | |
| dev = current + add. If they have no base license yet, treat the add as solo+add.""" | |
| cur = _db_get_by_email(email) | |
| if cur: | |
| tier = cur.get("tier", "solo") | |
| base = _dev_from_key(cur.get("api_key", "")) or TIER_DEVICES.get(tier, 1) | |
| else: | |
| tier, base = "solo", TIER_DEVICES.get("solo", 1) | |
| new_dev = base + max(0, add) | |
| key = _mint_ocodx_key(email, tier, new_dev) | |
| uid = (cur or {}).get("id") or secrets.token_hex(8) | |
| with _DB_LOCK: | |
| conn = _db_conn() | |
| if cur: | |
| conn.execute("UPDATE users SET api_key=?,stripe_session_id=?,last_seen=? WHERE id=?", | |
| (key, sid, time.time(), uid)) | |
| else: | |
| conn.execute("INSERT OR IGNORE INTO users VALUES (?,?,?,?,?,?,1,?,?,0)", | |
| (uid, email, key, tier, cid, sid, time.time(), time.time())) | |
| conn.commit(); conn.close() | |
| threading.Thread(target=_db_backup, daemon=True).start() | |
| return key | |
| def _db_get_by_session(session_id: str): | |
| with _DB_LOCK: | |
| conn = _db_conn() | |
| row = conn.execute("SELECT * FROM users WHERE stripe_session_id=?", (session_id,)).fetchone() | |
| conn.close() | |
| return dict(row) if row else None | |
| def _db_revoke(stripe_customer_id: str): | |
| with _DB_LOCK: | |
| conn = _db_conn() | |
| conn.execute("UPDATE users SET active=0 WHERE stripe_customer_id=?", (stripe_customer_id,)) | |
| conn.commit(); conn.close() | |
| threading.Thread(target=_db_backup, daemon=True).start() | |
| def _db_all() -> list: | |
| with _DB_LOCK: | |
| conn = _db_conn() | |
| rows = conn.execute("SELECT * FROM users ORDER BY created_at DESC").fetchall() | |
| conn.close() | |
| return [dict(r) for r in rows] | |
| def _socket_limit(key: Optional[str]) -> int: | |
| """Resolve the number of public MPort edge sockets a key may own.""" | |
| if key and (key == _ADMIN_KEY or key in _owner_keys()): | |
| return int(os.environ.get("SIGNALMESH_ADMIN_SOCKET_LIMIT", "1000")) | |
| if key and key in _PAID_KEYS: | |
| return int(os.environ.get("SIGNALMESH_STATIC_KEY_SOCKET_LIMIT", "1")) | |
| if key: | |
| user = _db_get_by_key(key) | |
| if user: | |
| # Current managed plans include one public relay. Additional relay | |
| # products can raise this without changing the MPort protocol. | |
| return max(1, int(user.get("socket_limit", 1) or 1)) | |
| return 0 | |
| _db_init() | |
| # ── OpenAPI tag groups ──────────────────────────────────────────────────────── | |
| _TAGS = [ | |
| {"name": "Core", "description": "Broadcast signals, tune in, check mesh status."}, | |
| {"name": "Feeds", "description": "RSS/Atom hydration — auto-broadcast feed items into the mesh."}, | |
| {"name": "Manifest", "description": "AGENTS.md ingestion — register external nodes by URL."}, | |
| {"name": "Coordinates", "description": "Addressable memory grid — store and resolve AA01-style coordinates."}, | |
| {"name": "Trails", "description": "Fuzzy keyword trails — self-healing synonym paths the mesh learns from misses."}, | |
| {"name": "Grid", "description": "72-node spatial grid state and SEC-Ω quarantine queue."}, | |
| {"name": "MPort", "description": "Discover, simulate, approve, and execute mesh-native capabilities."}, | |
| ] | |
| SITE_INGEST_DEFAULT = ",".join([ | |
| "nexus.overview=https://www.nexusaifirst.com/", | |
| "nexus.modules.roadmap=https://www.nexusaifirst.com/modules", | |
| "nexus.tech.architecture=https://www.nexusaifirst.com/docs/technical", | |
| "nexus.industries=https://www.nexusaifirst.com/industries", | |
| ]) | |
| def _page_text(html_raw: str, base: str) -> str: | |
| """Strip to prose but keep the hrefs — a signal with no link can't be cited.""" | |
| # Chrome first: nav/header/footer repeat on every page and otherwise dominate chunk 1. | |
| body = re.sub(r"<(script|style|nav|header|footer|aside|form)\b[\s\S]*?</\1>", " ", html_raw, flags=re.I) | |
| body = re.sub(r'<[a-z]+[^>]*\b(?:class|id)="[^"]*\b(?:nav|navbar|menu|footer|header|cookie|banner)\b[^"]*"[^>]*>', | |
| " ", body, flags=re.I) | |
| links = [] | |
| for href, label in re.findall(r'<a[^>]+href="([^"]+)"[^>]*>([\s\S]{0,80}?)</a>', body, re.I): | |
| label = " ".join(re.sub(r"<[^>]+>", " ", label).split()) | |
| if label and href.startswith(("http", "/")) and not href.startswith("#"): | |
| links.append(f"{label} <{urllib.parse.urljoin(base, href)}>") | |
| text = " ".join(html.unescape(re.sub(r"<[^>]+>", " ", body)).split()) | |
| seen, uniq = set(), [] | |
| for l in links: | |
| if l not in seen: | |
| seen.add(l); uniq.append(l) | |
| return text + (" Links: " + " · ".join(uniq[:25]) if uniq else "") | |
| def _chunks(text: str, size: int = 360) -> List[str]: | |
| """_do_tune_in renders each signal at [:400]; anything past that is unreachable.""" | |
| out, cur = [], "" | |
| for w in text.split(): | |
| if len(cur) + len(w) + 1 > size: | |
| out.append(cur); cur = w | |
| else: | |
| cur = f"{cur} {w}".strip() | |
| if cur: | |
| out.append(cur) | |
| return out | |
| async def _ingest_sites() -> None: | |
| """Re-broadcast configured doc sites on boot — signals live in memory and a | |
| Space restart wipes them, so a one-shot broadcast does not survive a rebuild.""" | |
| spec = os.environ.get("SITE_INGEST", SITE_INGEST_DEFAULT).strip() | |
| if not spec: | |
| return | |
| async with httpx.AsyncClient(timeout=25.0, follow_redirects=True) as client: | |
| for pair in spec.split(","): | |
| freq, _, url = pair.strip().partition("=") | |
| if not (freq and url): | |
| continue | |
| try: | |
| r = await client.get(url, headers={"User-Agent": "SignalMesh/1.0"}) | |
| if r.status_code != 200: | |
| print(f"[site_ingest] {freq}: HTTP {r.status_code}"); continue | |
| parts = _chunks(_page_text(r.text, url)[:12000]) | |
| for p in parts: | |
| signal_registry.broadcast(name=freq, source_type="site_docs", | |
| data=f"{p} (source: {url})", | |
| metadata={"source_url": url}) | |
| print(f"[site_ingest] {freq}: {len(parts)} signals from {url}") | |
| except Exception as e: | |
| print(f"[site_ingest] {freq}: {str(e)[:120]}") | |
| async def _lifespan(_app: FastAPI): | |
| binder.restore() # identity survives a rebuild | |
| broadcast_journal.replay(signal_registry) # ad-hoc signals survive a restart | |
| # Re-register runtime-added sources BEFORE the hydrators start, so they are | |
| # polled exactly like the curated ones instead of returning as dead history. | |
| broadcast_journal.replay_sources(feed_hydrator, source_hydrator) | |
| feed_hydrator.start() | |
| source_hydrator.start() | |
| antenna_forge.registry = signal_registry | |
| adys_pump.start() | |
| mport.load_state( | |
| try_hf=os.environ.get("SIGNALMESH_MPORT_RESTORE", "1") == "1" | |
| ) | |
| mport.broadcast_nicoli_catalog_now(signal_registry) | |
| persistence_task = asyncio.create_task(mport.persistence_loop()) | |
| ingest_task = asyncio.create_task(_ingest_sites()) | |
| try: | |
| yield | |
| finally: | |
| persistence_task.cancel(); ingest_task.cancel() | |
| app = FastAPI( | |
| title="SignalMesh — Sovereign Liquid Matrix Gateway", | |
| description=""" | |
| Ambient context bus for AI agent fleets. Agents broadcast to named frequencies; | |
| other agents tune in by keyword — no config, no service registry, no tool calls. | |
| ## Authentication | |
| All `/api/*` endpoints require an `X-SignalMesh-Key` header. | |
| | Tier | Key | Limits | | |
| |------|-----|--------| | |
| | **Managed** | personal key | Hosted API + one public MPort relay | | |
| [Get managed access →](https://kyklos.io/apps/signalmesh/#pricing) | |
| ## Quick start | |
| ```bash | |
| # Broadcast a signal | |
| curl -X POST https://acecalisto3-signalmesh.hf.space/api/broadcast \\ | |
| -H "X-SignalMesh-Key: YOUR_KEY" \\ | |
| -H "Content-Type: application/json" \\ | |
| -d '{"frequency":"my_agent","content":"hello mesh","source_type":"demo"}' | |
| # Tune in | |
| curl -X POST https://acecalisto3-signalmesh.hf.space/api/tune_in \\ | |
| -H "X-SignalMesh-Key: YOUR_KEY" \\ | |
| -H "Content-Type: application/json" \\ | |
| -d '{"keywords":["my_agent"]}' | |
| ``` | |
| """, | |
| version="2.0.0", | |
| docs_url="/docs", | |
| redoc_url=None, | |
| openapi_tags=_TAGS, | |
| lifespan=_lifespan, | |
| ) | |
| async def redoc_html(): | |
| from fastapi.responses import HTMLResponse | |
| return HTMLResponse("""<!DOCTYPE html> | |
| <html><head> | |
| <title>SignalMesh API Docs</title> | |
| <meta charset="utf-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet"> | |
| <style>body{margin:0;padding:0}</style> | |
| </head> | |
| <body> | |
| <redoc spec-url='/openapi.json' expand-responses="200,201" | |
| theme='{"colors":{"primary":{"main":"#e91e8c"}},"typography":{"fontSize":"14px","fontFamily":"Roboto,sans-serif","headings":{"fontFamily":"Montserrat,sans-serif"}}}' | |
| ></redoc> | |
| <script src="https://unpkg.com/redoc@latest/bundles/redoc.standalone.js"></script> | |
| </body></html>""") | |
| _cors_origins = [ | |
| origin.strip() | |
| for origin in os.environ.get("SIGNALMESH_CORS_ORIGINS", "").split(",") | |
| if origin.strip() | |
| ] | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=_cors_origins or ["*"], | |
| allow_credentials=bool(_cors_origins), | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| app.include_router( | |
| mport.make_router( | |
| check_key=_check_key, | |
| signal_registry=signal_registry, | |
| sec_omega=sec_omega, | |
| socket_limit=_socket_limit, | |
| ) | |
| ) | |
| # ── the editor ──────────────────────────────────────────────────────────────── | |
| # Mounted, not merged. It is a complete app — its own sessions, its own ACL read | |
| # from the client's Joomla, its own write adapter — and mounting keeps a single | |
| # implementation: the same code runs standalone on the machine that edits one | |
| # site, and here as a Studio tab. | |
| # | |
| # The prefix also settles the route collisions for free: both apps define | |
| # /healthz and /api/media, and neither has to give up its name. | |
| # The import is guarded: a build that ships without the package loses the tab, | |
| # not the Studio. | |
| try: | |
| import editor as _editor # noqa: E402 | |
| _editor_app = _editor.build() | |
| except ImportError as e: | |
| _editor_app = None | |
| print(f"[editor] not present, tab disabled: {e}", flush=True) | |
| if _editor_app is not None: | |
| app.mount("/api/editor", _editor_app) | |
| print("[editor] mounted at /api/editor", flush=True) | |
| # ── Models ──────────────────────────────────────────────────────────────────── | |
| class BroadcastReq(BaseModel): | |
| frequency: str; content: str | |
| bypass_gate: bool = False; source_type: str = "external" | |
| # rolling diff / provenance (all optional) | |
| change_id: int = 0; parent_change: int = 0 | |
| origin_agent: str = ""; change_type: str = "" | |
| files: List[str] = []; summary: str = "" | |
| ttl_ms: int = 0 | |
| class AntennaReq(BaseModel): | |
| frequency: str | |
| target: str | |
| name: Optional[str] = None | |
| class TuneInReq(BaseModel): | |
| keywords: List[str] | |
| role: Optional[str] = None # role-scoped delivery | |
| limit: int = 0 # 0 = every match; else newest N | |
| class CoordinateStoreReq(BaseModel): | |
| coordinate: str; data: Any | |
| symbolic: str = ""; change_id: int = 0; ttl_ms: int = 0 | |
| class IdentitySeedReq(BaseModel): | |
| canonical_locator: str | |
| revision: str = "" | |
| deployed_as: str = "" | |
| lineage_fingerprint: str = "" | |
| class RelationshipRoleReq(BaseModel): | |
| role: str; patterns: List[str] | |
| class RssSyncReq(BaseModel): | |
| url: str | |
| class ContextDiscoveryReq(BaseModel): | |
| text: str | |
| class HfInjectReq(BaseModel): | |
| repo_id: str | |
| repo_type: str = "model" # model | dataset | space | |
| class ArxivInjectReq(BaseModel): | |
| arxiv_id: str # e.g. "2303.08774" | |
| class FeedAddReq(BaseModel): | |
| url: str | |
| frequency: str | |
| interval: int = 3600 | |
| label: str = "" | |
| class ArticleFetchReq(BaseModel): | |
| url: str | |
| with_images: bool = False | |
| class ArticleSnapshotReq(BaseModel): | |
| frequency: str | |
| limit: int = 5 | |
| with_images: bool = False | |
| class FeedDiscoverReq(BaseModel): | |
| url: str | |
| class ManifestIngestReq(BaseModel): | |
| url: str # raw URL to an AGENTS.md file | |
| # ── Internal helpers ────────────────────────────────────────────────────────── | |
| def _node_idx(freq: str) -> int: | |
| return int(hashlib.sha256(freq.encode()).hexdigest(), 16) % (_ROWS * _COLS) | |
| def _agent_for(freq: str): | |
| idx = _node_idx(freq); r,c = divmod(idx, _COLS) | |
| return idx, grid.coords_to_agent.get((r,c), f"node-{idx}") | |
| def _mesh_size() -> int: | |
| return sum(len(v) for v in signal_registry.streams.values()) | |
| def _is_prot(freq: str) -> bool: | |
| return any(freq.lower().startswith(p) for p in _PROT) | |
| # ── Core logic (no auth — called by both /api/ and /ui/ routes) ─────────────── | |
| def _do_status(): | |
| return {"status":"live","signals_in_mesh":_mesh_size(), | |
| "active_frequencies":len(signal_registry.streams), | |
| "grid_nodes":_ROWS*_COLS,"quarantined":len(_SEC_Q),"version":"2.0.0", | |
| "mesh":"Sovereign Liquid Matrix"} | |
| def _do_broadcast(req: BroadcastReq): | |
| idx, agent = _agent_for(req.frequency) | |
| if _is_prot(req.frequency) and not req.bypass_gate: | |
| _SEC_Q.append({"frequency":req.frequency,"content":req.content, | |
| "source_type":req.source_type,"node":idx,"agent":agent, | |
| "queued_at":time.time()}) | |
| return {"status":"quarantined","node":idx,"agent":agent, | |
| "frequency":req.frequency, | |
| "reason":"Protected frequency — SEC-Ω staged. Use bypass_gate=true to commit."} | |
| result = guarded_broadcast(signal_registry, req.frequency, req.content, | |
| source_type=req.source_type, | |
| metadata={"node":idx,"agent":agent,"source":"external_api"}, | |
| ttl_ms=req.ttl_ms, change_id=req.change_id, | |
| parent_change=req.parent_change, | |
| origin_agent=req.origin_agent, | |
| change_type=req.change_type, | |
| files=req.files, summary=req.summary) | |
| if not result["ok"]: | |
| raise HTTPException(400, f"SEC-Ω blocked: {result['reason']}") | |
| return {"status":"live","node":idx,"agent":agent,"frequency":req.frequency, | |
| "fingerprint":result["fingerprint"],"signals_in_mesh":_mesh_size()} | |
| def _do_tune_in(keywords: List[str], role: Optional[str] = None, limit: int = 0): | |
| t0 = time.perf_counter() | |
| signals = signal_registry.tune_in(keywords) | |
| if role: | |
| signals = relationship_graph.filter_signals(role, signals) | |
| # ponytail: cap after role filtering — signals arrive newest-first, so the | |
| # slice keeps the freshest N rather than whatever survived the filter. | |
| if limit: | |
| signals = signals[:limit] | |
| us = (time.perf_counter()-t0)*1e6 | |
| # Collect any fuzzy trail bridges that fired during this call | |
| trails_fired = {} | |
| for s in signals: | |
| t = s.get("_trail") | |
| if t: | |
| via = next((k for k in keywords if k in signal_registry.fuzzy_trails), None) | |
| if via: | |
| trails_fired[via] = t | |
| if signals: | |
| lines = ["[SignalMesh — Live Context]"] | |
| for s in signals: | |
| age = round(s.get("age", 0), 1) | |
| prov = "" | |
| if s.get("change_id"): | |
| prov = f" | Δ{s['change_id']} by {s.get('origin_agent','?')} ({s.get('change_type','')})" | |
| trail_note = "" | |
| if s.get("_trail"): | |
| tr = s["_trail"] | |
| trail_note = f" [⟿ trail: {tr.get('bridged_to','?')} @ {tr.get('confidence','?')}]" | |
| lines.append(f"• [{s['name']}] ({s['type']}, {age}s ago{prov}{trail_note}): {str(s['content'])[:400]}") | |
| ctx = "\n".join(lines) | |
| else: | |
| ctx = "" | |
| # Check if any keyword was a pure miss (trail written but no signals yet on that frequency) | |
| misses = [] | |
| for kw in keywords: | |
| if kw in signal_registry.fuzzy_trails and not any( | |
| s["name"] == signal_registry.fuzzy_trails[kw][0] for s in signals | |
| ): | |
| best = signal_registry.fuzzy_trails[kw][0] | |
| conf = signal_registry.trail_gaps.get(kw, {}).get(best, 0) | |
| misses.append({"keyword": kw, "bridged_to": best, "confidence": conf, | |
| "branches": signal_registry.fuzzy_trails[kw][1:]}) | |
| return { | |
| "context": ctx, | |
| "signals_matched": len(signals), | |
| "keywords": keywords, | |
| "role": role, | |
| "latency_us": round(us, 2), | |
| "trails_fired": trails_fired, | |
| "misses": misses, | |
| } | |
| async def _do_rss_sync(url: str): | |
| try: | |
| async with httpx.AsyncClient(timeout=30.0) as c: | |
| r = await c.get(url, follow_redirects=True, | |
| headers={"User-Agent":"SignalMesh/2.0"}) | |
| r.raise_for_status() | |
| raw_bytes = r.content | |
| raw = r.text | |
| except Exception as e: | |
| raise HTTPException(502, f"Fetch failed: {e}") | |
| feed = feedparser.parse(raw) | |
| if feed.bozo and not feed.entries: | |
| feed = feedparser.parse(raw_bytes) | |
| if feed.bozo and not feed.entries: | |
| import re as _re | |
| sanitized = _re.sub(r'&(?!amp;|lt;|gt;|quot;|apos;|#\d+;|#x[\da-fA-F]+;)', '&', raw) | |
| sanitized = _re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f]', '', sanitized) | |
| feed = feedparser.parse(sanitized) | |
| if not feed.entries: | |
| exc = getattr(feed, 'bozo_exception', None) | |
| raise HTTPException(422, f"Feed parse error: {exc or 'no entries found'}") | |
| synced = [] | |
| for entry in feed.entries: | |
| title = entry.get("title","Untitled") | |
| link = entry.get("link","") or entry.get("id","") | |
| summary = entry.get("summary", entry.get("description",""))[:400] | |
| if not link: continue | |
| idx, agent = _agent_for(link) | |
| signal_registry.broadcast(name=f"rss_{agent}", source_type="rss_cp", | |
| data={"title":title,"link":link,"summary":summary}, | |
| metadata={"node":idx,"agent":agent,"feed_url":url}) | |
| synced.append({"title":title,"node":idx,"agent":agent,"link":link}) | |
| return {"synced":len(synced),"feed_url":url, | |
| "feed_title":feed.feed.get("title",""), | |
| "items":synced[:20],"signals_in_mesh":_mesh_size()} | |
| async def _do_context_discovery(text: str): | |
| url_pat = re.compile(r'https?://[^\s<>"\']+') | |
| all_urls = list(set(url_pat.findall(text))) | |
| rss_urls = [u for u in all_urls if any(k in u.lower() for k in ("rss","atom","xml","feed"))] | |
| other_urls = [u for u in all_urls if u not in rss_urls] | |
| for u in other_urls[:20]: | |
| idx, agent = _agent_for(u) | |
| signal_registry.broadcast(name=f"discovered_{agent}", source_type="context_discovery", | |
| data=u, metadata={"node":idx,"agent":agent}) | |
| rss_results = [] | |
| for rss_url in rss_urls[:5]: | |
| try: | |
| d = await _do_rss_sync(rss_url) | |
| rss_results.append({"url":rss_url,"synced":d["synced"],"feed_title":d.get("feed_title","")}) | |
| except Exception as e: | |
| rss_results.append({"url":rss_url,"error":str(e)}) | |
| return {"urls_found":len(all_urls),"rss_feeds_synced":rss_results, | |
| "signals_broadcast":len(other_urls),"signals_in_mesh":_mesh_size()} | |
| async def _do_hf_inject(repo_id: str, repo_type: str = "model"): | |
| hf_url = f"https://huggingface.co/api/{repo_type}s/{repo_id}" | |
| try: | |
| async with httpx.AsyncClient(timeout=20.0, follow_redirects=True) as c: | |
| r = await c.get(hf_url, headers={"User-Agent":"SignalMesh/2.0"}) | |
| r.raise_for_status(); data = r.json() | |
| except Exception as e: | |
| raise HTTPException(502, f"HuggingFace API error: {e}") | |
| content = { | |
| "id": data.get("id", repo_id), "type": repo_type, | |
| "downloads": data.get("downloads", 0), "likes": data.get("likes", 0), | |
| "tags": (data.get("tags") or [])[:12], | |
| "pipeline_tag": data.get("pipeline_tag",""), | |
| "private": data.get("private", False), | |
| } | |
| freq = f"hf_{repo_type}_{repo_id.replace('/','__').replace('-','_')}" | |
| idx, agent = _agent_for(freq) | |
| signal_registry.broadcast(name=freq, source_type="hf_inject", | |
| data=content, metadata={"node":idx,"agent":agent,"hf_url":f"https://huggingface.co/{repo_id}"}) | |
| return {"injected":content,"frequency":freq,"node":idx,"agent":agent, | |
| "hf_url":f"https://huggingface.co/{repo_id}","signals_in_mesh":_mesh_size()} | |
| async def _do_arxiv_inject(arxiv_id: str): | |
| clean_id = arxiv_id.strip().replace("https://arxiv.org/abs/","").replace("arxiv:","") | |
| url = f"https://export.arxiv.org/api/query?id_list={clean_id}&max_results=1" | |
| try: | |
| async with httpx.AsyncClient(timeout=20.0) as c: | |
| r = await c.get(url, headers={"User-Agent":"SignalMesh/2.0"}) | |
| r.raise_for_status(); raw = r.text | |
| except Exception as e: | |
| raise HTTPException(502, f"arxiv fetch error: {e}") | |
| feed = feedparser.parse(raw) | |
| if not feed.entries: | |
| raise HTTPException(404, f"Paper {clean_id} not found on arxiv") | |
| entry = feed.entries[0] | |
| authors = [a.get("name","") for a in entry.get("authors",[])][:5] | |
| content = { | |
| "arxiv_id": clean_id, | |
| "title": re.sub(r'\s+',' ', entry.get("title","")), | |
| "authors": authors, | |
| "summary": (entry.get("summary",""))[:600], | |
| "published": entry.get("published",""), | |
| "link": entry.get("link",""), | |
| } | |
| freq = f"arxiv_{clean_id.replace('.','_').replace('-','_')}" | |
| idx, agent = _agent_for(freq) | |
| signal_registry.broadcast(name=freq, source_type="arxiv_inject", | |
| data=content, metadata={"node":idx,"agent":agent}) | |
| return {"injected":content,"frequency":freq,"node":idx,"agent":agent, | |
| "signals_in_mesh":_mesh_size()} | |
| def _do_grid_state(): | |
| active_freqs = set(signal_registry.streams.keys()) | |
| nodes = [] | |
| for (r,c), kw in grid.coords_to_agent.items(): | |
| idx = r*_COLS+c | |
| sigs = [s for fn in active_freqs | |
| if kw.lower() in fn.lower() or fn.lower() in kw.lower() | |
| for s in signal_registry.streams.get(fn,[])] | |
| nodes.append({"node":idx,"row":r,"col":c,"agent":kw, | |
| "active":len(sigs)>0,"signal_count":len(sigs)}) | |
| occupied = {n["node"] for n in nodes} | |
| for i in range(_ROWS*_COLS): | |
| if i not in occupied: | |
| rr,cc = divmod(i,_COLS) | |
| nodes.append({"node":i,"row":rr,"col":cc,"agent":None,"active":False,"signal_count":0}) | |
| nodes.sort(key=lambda n:n["node"]) | |
| return {"nodes":nodes,"total_nodes":len(nodes), | |
| "active_count":sum(1 for n in nodes if n["active"]), | |
| "occupied_count":len(occupied)} | |
| def _do_frequencies(): | |
| res = [] | |
| for name, buf in signal_registry.streams.items(): | |
| if buf: | |
| lat = buf[-1] | |
| res.append({"frequency":name,"signal_count":len(buf), | |
| "latest_type":lat.source_type, | |
| "latest_age_s":round(time.time()-lat.timestamp,1), | |
| "node":_node_idx(name),"protected":_is_prot(name)}) | |
| res.sort(key=lambda f:f["signal_count"],reverse=True) | |
| return {"frequencies":res,"count":len(res)} | |
| # ── Public API (requires X-SignalMesh-Key header) ───────────────────────────── | |
| def api_status(x_signalmesh_key: Optional[str]=Header(default=None)): | |
| _check_key(x_signalmesh_key); return _do_status() | |
| def api_broadcast(req: BroadcastReq, x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """ | |
| Broadcast content to a frequency. All agents tuned to matching keywords | |
| will receive it on their next `tune_in` call. SEC-Ω gates protected prefixes | |
| (security_, auth_, key_, etc.) — use `bypass_gate=true` to commit after review. | |
| """ | |
| _check_key(x_signalmesh_key); return _do_broadcast(req) | |
| def api_tune_in(req: TuneInReq, x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """ | |
| Retrieve all signals whose frequency matches any of the given keywords. | |
| Fuzzy matching via learned trails handles near-misses automatically. | |
| Pass `role` to filter by relationship graph (e.g. only signals relevant to CodeAgent). | |
| """ | |
| _check_key(x_signalmesh_key, allow_update=True) | |
| if _tier(x_signalmesh_key) == "update" and not all(str(kw).startswith("ocodx") for kw in req.keywords): | |
| raise HTTPException(403, detail="Update key may only tune ocodx.* frequencies") | |
| return _do_tune_in(req.keywords, req.role, req.limit) | |
| def api_tune_freq(frequency: str, limit: int = 0, x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """Quick single-frequency lookup with latency measurement.""" | |
| _check_key(x_signalmesh_key, allow_update=frequency.startswith("ocodx")) | |
| t0=time.perf_counter(); sigs=signal_registry.tune_in([frequency], limit); us=(time.perf_counter()-t0)*1e6 | |
| return {"frequency":frequency,"signals":sigs,"count":len(sigs),"latency_us":round(us,2)} | |
| def api_grid(x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """Returns the full 9×8 coordinate grid — each node's assigned agent and live signal count.""" | |
| _check_key(x_signalmesh_key); return _do_grid_state() | |
| def api_frequencies(x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """Returns every named frequency that has at least one signal in the mesh.""" | |
| _check_key(x_signalmesh_key); return _do_frequencies() | |
| def api_quarantine(x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """Signals staged by the SEC-Ω gate pending review. Use bypass_gate=true to commit.""" | |
| _check_key(x_signalmesh_key); return {"quarantined":_SEC_Q,"count":len(_SEC_Q)} | |
| def api_tool_broadcast(req: BroadcastReq, x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """Identical to /api/broadcast — exposed under /tools/ for MCP and LangChain tool registries.""" | |
| _check_key(x_signalmesh_key); return _do_broadcast(req) | |
| async def api_tool_rss(req: RssSyncReq, x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """Fetch an RSS/Atom feed and broadcast each item as a signal.""" | |
| _check_key(x_signalmesh_key); return await _do_rss_sync(req.url) | |
| async def api_tool_ctx(req: ContextDiscoveryReq, x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """NLP extraction — finds named entities and keywords, broadcasts each as a signal.""" | |
| _check_key(x_signalmesh_key); return await _do_context_discovery(req.text) | |
| async def api_hf_inject(req: HfInjectReq, x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """Fetches repo metadata from HuggingFace Hub and broadcasts it as signals.""" | |
| _check_key(x_signalmesh_key); return await _do_hf_inject(req.repo_id, req.repo_type) | |
| async def api_arxiv_inject(req: ArxivInjectReq, x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """Fetches abstract and metadata from arXiv and broadcasts as signals.""" | |
| _check_key(x_signalmesh_key); return await _do_arxiv_inject(req.arxiv_id) | |
| # ── UI proxy routes (no key exposure — dashboard uses these) ────────────────── | |
| def ui_status(): return _do_status() | |
| def public_health(): | |
| """Keyless liveness probe with no user, key, or payload data.""" | |
| status = _do_status() | |
| return { | |
| "ok": True, | |
| "service": "signalmesh", | |
| "version": app.version, | |
| "signals_in_mesh": status.get("signals_in_mesh", 0), | |
| "active_frequencies": status.get("active_frequencies", 0), | |
| } | |
| def _do_world_state(): | |
| return world_state.build( | |
| signal_registry=signal_registry, grid=grid, | |
| feed_hydrator=feed_hydrator, source_hydrator=source_hydrator, | |
| coordinate_resolver=coordinate_resolver, binder=binder, mport=mport, | |
| node_idx=_node_idx, rows=_ROWS, cols=_COLS) | |
| def ui_world_state(): | |
| """One semantic projection every antenna and skin renders from. | |
| Server-side on purpose: if each client assembled its own, one would derive | |
| provenance edges from real evidence and another would draw /ui/relationship | |
| — an agent keyword table — as causality. Disposable; nothing here is stored. | |
| Roles with no producer report not_modeled and carry no values. | |
| """ | |
| return _do_world_state() | |
| def public_identity(): | |
| """Keyless. The centrally adjudicated record for SignalMesh itself. | |
| Exists so a cold agent in any directory can settle "which copy is real" in | |
| one request instead of inferring it from whichever clone looks newest — | |
| the inference that sent a full session's work into a stale fork on | |
| 2026-08-03. Returns authority_unresolved rather than guessing. | |
| """ | |
| return binder.self_identity() | |
| def api_identity_seed(req: IdentitySeedReq, x_signalmesh_key: Optional[str]=Header(default=None)): | |
| """Owner/admin bootstrap for the canonical SignalMesh identity. | |
| This is deliberately not automatic. A missing record must stay unresolved | |
| until an owner chooses the canonical locator and revision. A second call is | |
| passed through to binder.register_self(), which refuses to overwrite the | |
| existing identity; corrections belong in binder.adjudicate(). | |
| """ | |
| if _tier(x_signalmesh_key) != "admin": | |
| raise HTTPException(403, "Admin only") | |
| canonical = req.canonical_locator.strip() | |
| if not canonical: | |
| raise HTTPException(400, "canonical_locator is required") | |
| return binder.register_self( | |
| canonical, | |
| revision=req.revision.strip() or None, | |
| deployed_as=req.deployed_as.strip(), | |
| lineage_fingerprint=req.lineage_fingerprint.strip() or None, | |
| ) | |
| def mport_console(): | |
| """Stable product URL; the Submesh path remains the protocol/API name. | |
| Lands on Articles, not Studio. Both links used to resolve to the same Studio | |
| page, so MPort and Studio were indistinguishable in the UI even though they | |
| are different planes — Studio ports a site, MPort executes capabilities. | |
| """ | |
| return RedirectResponse("/api/submesh/ui?tab=articles", status_code=307) | |
| def studio_console(): | |
| """URL → pixel-identical mesh → platform-specific bundle. Opens the MPort face | |
| on its Studio tab so Scan stays one click away.""" | |
| return RedirectResponse("/api/submesh/ui?tab=studio", status_code=307) | |
| def ui_broadcast(req: BroadcastReq): return _do_broadcast(req) | |
| def ui_tune_in(req: TuneInReq): return _do_tune_in(req.keywords, req.role, req.limit) | |
| def ui_tune_freq(frequency: str, limit: int = 0): | |
| """Keyless read for antennas. Any receiver tunes and renders in its own wrapper.""" | |
| t0=time.perf_counter(); sigs=signal_registry.tune_in([frequency], limit); us=(time.perf_counter()-t0)*1e6 | |
| return {"frequency":frequency,"signals":sigs,"count":len(sigs),"latency_us":round(us,2)} | |
| def ui_sources(): | |
| return {"sources": source_hydrator.list_sources()} | |
| async def ui_source_refresh(frequency: str): | |
| """Pull from the source immediately instead of waiting for the poll.""" | |
| return await source_hydrator.refresh(frequency) | |
| def _public_base(request: Request) -> str: | |
| """The URL an antenna out in the world must call — not the in-container address.""" | |
| host = os.environ.get("SPACE_HOST") or os.environ.get("SIGNALMESH_PUBLIC_URL") | |
| if host: | |
| return host if host.startswith("http") else f"https://{host}" | |
| fwd_host = request.headers.get("x-forwarded-host") or request.headers.get("host") | |
| if fwd_host and not fwd_host.startswith(("127.", "localhost", "0.0.0.0")): | |
| proto = request.headers.get("x-forwarded-proto", "https") | |
| return f"{proto}://{fwd_host}" | |
| return str(request.base_url).rstrip("/") | |
| def ui_antenna_inspect(frequency: str): | |
| return antenna_forge.inspect(frequency) | |
| def ui_antenna_build(req: AntennaReq, request: Request): | |
| """Emit a wrapper shaped to what the frequency actually carries.""" | |
| try: | |
| return antenna_forge.generate(req.frequency, req.target, req.name, | |
| mesh_url=_public_base(request)) | |
| except ValueError as e: | |
| raise HTTPException(400, detail={"error": str(e), "targets": list(ANTENNA_TARGETS)}) | |
| def ui_grid(): return _do_grid_state() | |
| def ui_frequencies(): return _do_frequencies() | |
| def ui_quarantine(): return {"quarantined":_SEC_Q,"count":len(_SEC_Q)} | |
| async def ui_rss(req: RssSyncReq): return await _do_rss_sync(req.url) | |
| async def ui_ctx(req: ContextDiscoveryReq): return await _do_context_discovery(req.text) | |
| async def ui_hf(req: HfInjectReq): return await _do_hf_inject(req.repo_id, req.repo_type) | |
| async def ui_arxiv(req: ArxivInjectReq): return await _do_arxiv_inject(req.arxiv_id) | |
| def ui_feeds(): | |
| """List all active hydrator feeds — label, URL, freshness, item count.""" | |
| return {"feeds": feed_hydrator.list_feeds(), "count": len(feed_hydrator._feeds)} | |
| async def ui_feeds_add(req: FeedAddReq): | |
| cfg = feed_hydrator.add_feed(req.url, req.frequency, req.interval, req.label) | |
| return {"added": True, "frequency": cfg.frequency, "url": cfg.url, | |
| "interval_s": cfg.interval, "persisted": True, | |
| "env_var": "SIGNALMESH_FEEDS", "env_value": feed_hydrator.env_value()} | |
| async def ui_feeds_discover(req: FeedDiscoverReq): | |
| """Probe any URL for RSS/Atom feeds — RSSHub Radar built in.""" | |
| feeds = await feed_hydrator.discover(req.url) | |
| return {"url": req.url, "found": feeds, "count": len(feeds)} | |
| def api_feeds(x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """Returns all registered RSS/Atom feeds with freshness and item counts.""" | |
| _check_key(x_signalmesh_key) | |
| return {"feeds": feed_hydrator.list_feeds(), "count": len(feed_hydrator._feeds)} | |
| async def api_feeds_add(req: FeedAddReq, x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """Add a feed — items auto-broadcast to that frequency on each poll. | |
| The registration is persisted (local + HF dataset). `env_value` is the same | |
| set as a SIGNALMESH_FEEDS variable, for anyone who wants it as config rather | |
| than state — we hand it back instead of writing the Space variable ourselves, | |
| because that write restarts the Space. | |
| """ | |
| _check_key(x_signalmesh_key) | |
| cfg = feed_hydrator.add_feed(req.url, req.frequency, req.interval, req.label) | |
| return {"added": True, "frequency": cfg.frequency, "url": cfg.url, | |
| "persisted": True, "env_var": "SIGNALMESH_FEEDS", | |
| "env_value": feed_hydrator.env_value()} | |
| async def api_feeds_persist(x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """Force the HF-dataset upload instead of waiting for the background loop.""" | |
| _check_key(x_signalmesh_key) | |
| ok = await asyncio.to_thread(feed_hydrator.persist_hf) | |
| return {"persisted": ok, "feeds": len(feed_hydrator.dynamic()), | |
| "dataset": None if not ok else "feeds.json", | |
| "env_var": "SIGNALMESH_FEEDS", "env_value": feed_hydrator.env_value()} | |
| def api_mport_articles(frequency: str, limit: int = 0, | |
| x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """Title, link, summary, published, feed. The mesh's half of the contract.""" | |
| _check_key(x_signalmesh_key) | |
| return mport_articles.export_feed_json(frequency, limit) | |
| async def api_mport_article(req: ArticleFetchReq, | |
| x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """Full body and image URLs. Deliberately never broadcast into the mesh.""" | |
| _check_key(x_signalmesh_key) | |
| return await mport_articles.fetch_article(req.url, req.with_images) | |
| async def api_mport_snapshot(req: ArticleSnapshotReq, | |
| x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """Returns a download token, not bytes — "download Robert's latest five | |
| typewriter posts with their images" lands here after semantic resolution.""" | |
| _check_key(x_signalmesh_key) | |
| return await mport_articles.snapshot_articles(req.frequency, req.limit, | |
| req.with_images) | |
| def ui_mport_articles(frequency: str, limit: int = 0): | |
| """Keyless read — identity only, same as every other /ui/ receiver route.""" | |
| return mport_articles.export_feed_json(frequency, limit) | |
| def ui_aliases(frequency: str = ""): | |
| """The alias index: feed titles, authors, hostnames and article titles that | |
| resolve to a frequency. Makes wording-based resolution inspectable.""" | |
| return signal_registry.get_aliases(frequency) | |
| async def api_feeds_discover(req: FeedDiscoverReq, x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """Probe any URL — finds RSS/Atom feeds via link headers and common paths.""" | |
| _check_key(x_signalmesh_key) | |
| return {"url": req.url, "found": await feed_hydrator.discover(req.url)} | |
| def ui_trails(): | |
| """ | |
| Returns every keyword trail the mesh has self-healed — the fuzzy paths it builds | |
| when a keyword misses and a close-enough frequency is found via SequenceMatcher + token overlap. | |
| Empty dict on a fresh instance; grows as agents use the mesh. | |
| """ | |
| return signal_registry.get_trails() | |
| def ui_clear_trail(keyword: str): | |
| """Remove a specific keyword trail — the mesh will re-learn it from scratch on next miss.""" | |
| removed = signal_registry.clear_trail(keyword) | |
| return {"removed": removed, "keyword": keyword} | |
| def api_trails(x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """Same as /ui/trails — requires API key.""" | |
| _check_key(x_signalmesh_key) | |
| return signal_registry.get_trails() | |
| # ── AGENTS.md Manifest endpoints ───────────────────────────────────────────── | |
| async def serve_agents_md(): | |
| """Serve this node's AGENTS.md manifest so any external agent can discover it.""" | |
| here = Path(__file__).parent / "AGENTS.md" | |
| if not here.exists(): | |
| raise HTTPException(404, "AGENTS.md not found on this node") | |
| return HTMLResponse(content=here.read_text(), media_type="text/markdown") | |
| async def api_manifest_ingest(req: ManifestIngestReq, | |
| x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """ | |
| Fetch a remote AGENTS.md and register its node in the mesh. | |
| Parses declared frequencies and auto-broadcasts them. SEC-Ω validated before ingestion. | |
| """ | |
| _check_key(x_signalmesh_key) | |
| try: | |
| async with httpx.AsyncClient(follow_redirects=True, timeout=10.0) as client: | |
| r = await client.get(req.url) | |
| r.raise_for_status() | |
| raw = r.text | |
| except Exception as e: | |
| raise HTTPException(502, f"Fetch failed: {e}") | |
| safe, reason = sec_omega.validate("manifest", raw[:2000], "external_manifest") | |
| if not safe: | |
| raise HTTPException(400, f"SEC-Ω blocked manifest: {reason}") | |
| parsed = manifest_registry.ingest(raw, source_uri=req.url) | |
| # Register all declared frequencies into the mesh | |
| for freq in parsed.get("frequencies", []): | |
| signal_registry.broadcast( | |
| name=freq["frequency"], | |
| source_type="manifest_ingestion", | |
| data={"node_uri": parsed["node_uri"], "mode": freq["mode"]}, | |
| metadata={"grid_hash": parsed["grid_hash"]}, | |
| ) | |
| return { | |
| "ingested": True, | |
| "node_uri": parsed["node_uri"], | |
| "grid_hash": parsed["grid_hash"], | |
| "frequencies_registered": [f["frequency"] for f in parsed["frequencies"]], | |
| "gc_strategy": parsed["gc_rules"].get("strategy", "auto"), | |
| "strip_tool_schemas": parsed["strip_tool_schemas"], | |
| } | |
| def api_manifest_nodes(x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """All external nodes registered via AGENTS.md ingestion — URI, grid hash, declared frequencies.""" | |
| _check_key(x_signalmesh_key) | |
| return {"nodes": manifest_registry.all_nodes(), "count": len(manifest_registry._manifests)} | |
| async def ui_manifest_ingest(req: ManifestIngestReq): | |
| """Dashboard-accessible manifest ingestion (no key — uses server-side validation).""" | |
| try: | |
| async with httpx.AsyncClient(follow_redirects=True, timeout=10.0) as client: | |
| r = await client.get(req.url); r.raise_for_status(); raw = r.text | |
| except Exception as e: | |
| raise HTTPException(502, f"Fetch failed: {e}") | |
| safe, reason = sec_omega.validate("manifest", raw[:2000], "ui_manifest") | |
| if not safe: | |
| raise HTTPException(400, f"SEC-Ω blocked: {reason}") | |
| parsed = manifest_registry.ingest(raw, source_uri=req.url) | |
| return {"ingested": True, "node_uri": parsed["node_uri"], | |
| "frequencies": len(parsed["frequencies"])} | |
| # ── Coordinate addressing ──────────────────────────────────────────────────── | |
| def ui_store_coordinate(req: CoordinateStoreReq): | |
| """Store content at a grid address (e.g. AA03). Supports symbolic names, TTL, and change_id for provenance.""" | |
| coord = coordinate_resolver.store( | |
| req.coordinate, req.data, | |
| symbolic=req.symbolic, change_id=req.change_id, ttl_ms=req.ttl_ms | |
| ) | |
| return {"coordinate": coord, "symbolic": req.symbolic, "stored": True} | |
| def ui_resolve(address: str): | |
| """ | |
| Resolve any address form to stored content: | |
| - `AA03` — grid coordinate | |
| - `ARCH.DECISIONS.RETRY` — dot-path symbolic name | |
| - `AA03:D2` — namespaced slot | |
| - `AA03@371` — specific change_id version | |
| Returns 404 if not found or TTL expired. | |
| """ | |
| result = coordinate_resolver.resolve(address) | |
| if result is None: | |
| raise HTTPException(404, f"Coordinate '{address}' not found or expired") | |
| return {"address": address, "resolved": result} | |
| def ui_list_coordinates(): | |
| """All live (non-TTL-expired) coordinates with namespaces and slot definitions.""" | |
| return { | |
| "coordinates": coordinate_resolver.all_coordinates(), | |
| "namespaces": NAMESPACES, | |
| "slots": SLOTS, | |
| } | |
| # ── Relationship graph / scoped delivery ───────────────────────────────────── | |
| def ui_get_relationship(): | |
| """The relationship graph — maps agent roles (CodeAgent, QAAgent, etc.) to frequency patterns for scoped delivery.""" | |
| return {"roles": relationship_graph.all_roles()} | |
| def ui_set_relationship(req: RelationshipRoleReq): | |
| """Register or update which frequencies a given role should receive on tune_in.""" | |
| relationship_graph.set_role(req.role, req.patterns) | |
| return {"role": req.role, "patterns": req.patterns, "updated": True} | |
| # ── Dashboard (v3 — clean bright build) ────────────────────────────────────── | |
| _HTML = r"""<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/> | |
| <title>SignalMesh</title> | |
| <style> | |
| :root{ | |
| --pink:#e91e8c;--amber:#f59e0b;--pink-light:#ff6ec7;--amber-light:#fcd34d; | |
| --grad:linear-gradient(135deg,var(--pink),var(--amber)); | |
| --grad-soft:linear-gradient(135deg,#fce7f3,#fef9c3); | |
| --bg:#fffbf0;--white:#ffffff;--card:#ffffff; | |
| --border:#f0e8d0;--border-strong:#fcd34d; | |
| --text:#1a0800;--dim:#7a6040;--dimmer:#c8a860; | |
| --green:#059669;--red:#dc2626;--yellow:#d97706; | |
| --font:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; | |
| --mono:'Courier New',monospace; | |
| } | |
| *{box-sizing:border-box;margin:0;padding:0} | |
| body{background:var(--bg);color:var(--text);font-family:var(--font);font-size:13px;min-height:100vh} | |
| a{color:var(--amber);text-decoration:none}a:hover{text-decoration:underline} | |
| ::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:4px} | |
| /* ── Header ── */ | |
| .header{background:var(--white);border-bottom:1px solid var(--border);padding:0 24px; | |
| display:flex;align-items:center;justify-content:space-between;height:56px; | |
| box-shadow:0 1px 3px rgba(245,158,11,.08)} | |
| .logo{display:flex;align-items:center;gap:10px} | |
| .logo-icon{width:32px;height:32px;border-radius:8px;background:var(--grad); | |
| display:flex;align-items:center;justify-content:center;font-size:16px} | |
| .logo-name{font-size:16px;font-weight:700;background:var(--grad); | |
| -webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text} | |
| .logo-tag{font-size:11px;color:var(--dim);margin-left:2px} | |
| .header-nav{display:flex;align-items:center;gap:4px} | |
| .nav-link{padding:6px 12px;border-radius:6px;font-size:12px;font-weight:500;color:var(--dim);transition:all .15s} | |
| .nav-link:hover{background:var(--grad-soft);color:var(--amber)} | |
| .live-dot{width:7px;height:7px;border-radius:50%;background:var(--green); | |
| box-shadow:0 0 0 2px rgba(5,150,105,.2);animation:livepulse 2s ease-in-out infinite;display:inline-block;margin-right:5px} | |
| @keyframes livepulse{0%,100%{opacity:1}50%{opacity:.4}} | |
| /* ── Status strip ── */ | |
| .status-strip{background:var(--white);border-bottom:1px solid var(--border); | |
| display:flex;padding:0 24px;gap:0} | |
| .sstat{padding:10px 24px 10px 0;margin-right:24px;border-right:1px solid var(--border)} | |
| .sstat:last-child{border-right:none} | |
| .sstat-val{font-size:24px;font-weight:700;background:var(--grad); | |
| -webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;line-height:1} | |
| .sstat-lbl{font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.8px;margin-top:2px} | |
| /* ── Layout ── */ | |
| .layout{display:grid;grid-template-columns:300px 1fr;align-items:start;min-height:calc(100vh - 100px)} | |
| .sidebar{background:var(--white);border-right:1px solid var(--border);display:flex;flex-direction:column;position:sticky;top:0;max-height:100vh;overflow-y:auto} | |
| .main{background:var(--bg);min-width:0} | |
| /* ── Section header ── */ | |
| .sec-hdr{padding:14px 16px 10px;border-bottom:1px solid var(--border); | |
| font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.2px; | |
| color:var(--dim);display:flex;align-items:center;gap:6px} | |
| .sec-hdr .dot{width:6px;height:6px;border-radius:50%} | |
| .dot-pink{background:var(--pink)} | |
| .dot-amber{background:var(--amber)} | |
| .dot-green{background:var(--green)} | |
| /* ── Walkthrough ── */ | |
| .wt-tabs{display:flex;overflow-x:auto;border-bottom:1px solid var(--border);gap:0} | |
| .wt-tab{flex:none;padding:8px 14px;font-size:11px;font-weight:600;color:var(--dim); | |
| cursor:pointer;border:none;background:transparent;font-family:var(--font); | |
| border-bottom:2px solid transparent;transition:all .15s;white-space:nowrap} | |
| .wt-tab:hover{color:var(--amber)} | |
| .wt-tab.active{color:var(--amber);border-bottom-color:var(--amber)} | |
| .wt-body{padding:14px 16px} | |
| .wt-title{font-size:13px;font-weight:700;color:var(--text);margin-bottom:8px} | |
| .wt-text{font-size:12px;color:var(--dim);line-height:1.7} | |
| .wt-text strong{color:var(--text)} | |
| .wt-text code{font-family:var(--mono);background:#fef9c3;color:var(--amber);padding:1px 5px;border-radius:3px;font-size:11px} | |
| .compare{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:10px 0} | |
| .cbox{padding:10px;border-radius:8px;font-size:11px;line-height:1.7} | |
| .cbox.old{background:#fff1f2;border:1px solid #fecdd3}.cbox.old .clbl{color:var(--red)} | |
| .cbox.new{background:#f0fdf4;border:1px solid #a7f3d0}.cbox.new .clbl{color:var(--green)} | |
| .clbl{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:1px;margin-bottom:4px} | |
| .callout{background:var(--grad-soft);border:1px solid var(--border-strong);border-radius:8px; | |
| padding:10px 12px;font-size:11px;color:var(--dim);margin-top:10px;line-height:1.7} | |
| .callout strong{color:var(--amber)} | |
| .wt-actions{display:flex;gap:6px;margin-top:12px} | |
| /* ── Panels ── */ | |
| .panel{padding:14px 16px;border-bottom:1px solid var(--border)} | |
| /* ── Freq display ── */ | |
| .freq-display{background:var(--grad);border-radius:8px;padding:10px 14px;margin-bottom:10px;position:relative;overflow:hidden} | |
| .freq-display::after{content:'';position:absolute;top:0;left:-100%;width:60%;height:100%; | |
| background:linear-gradient(90deg,transparent,rgba(255,255,255,.15),transparent); | |
| animation:shimmer 2.5s ease-in-out infinite} | |
| @keyframes shimmer{0%{left:-100%}100%{left:150%}} | |
| .freq-label{font-size:9px;color:rgba(255,255,255,.7);text-transform:uppercase;letter-spacing:1px;font-weight:600} | |
| .freq-value{font-size:15px;font-weight:700;color:#fff;margin-top:2px; | |
| white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:var(--mono)} | |
| /* ── Tuner ── */ | |
| .tuner-wrap{background:linear-gradient(135deg,#1a0800,#1a0f00);border-radius:8px; | |
| padding:12px;margin-bottom:10px;position:relative;overflow:hidden} | |
| .tuner-scan{position:absolute;top:0;bottom:0;width:2px; | |
| background:linear-gradient(180deg,var(--pink-light),var(--amber-light)); | |
| opacity:.8;animation:scan 2.2s linear infinite;box-shadow:0 0 8px var(--pink)} | |
| @keyframes scan{0%{left:0}100%{left:100%}} | |
| .tuner-freq{font-size:13px;font-weight:700;color:#fff;font-family:var(--mono);letter-spacing:1px} | |
| .tuner-meta{font-size:10px;color:rgba(255,255,255,.5);margin-top:4px} | |
| .tuner-bars{display:flex;align-items:flex-end;gap:2px;height:20px;margin-top:8px} | |
| .tuner-bar{flex:1;border-radius:1px;transition:height .25s,background .25s} | |
| /* ── Inputs ── */ | |
| input,textarea,select{width:100%;background:var(--white);border:1px solid var(--border); | |
| color:var(--text);padding:8px 10px;border-radius:6px;font-family:var(--font); | |
| font-size:12px;outline:none;resize:vertical;transition:border-color .15s,box-shadow .15s} | |
| input:focus,textarea:focus,select:focus{border-color:var(--amber);box-shadow:0 0 0 3px rgba(245,158,11,.1)} | |
| .field{margin-bottom:10px} | |
| .field label{display:block;font-size:10px;font-weight:600;color:var(--dim); | |
| margin-bottom:4px;text-transform:uppercase;letter-spacing:.8px} | |
| select{cursor:pointer} | |
| /* ── Buttons ── */ | |
| button{padding:7px 14px;border-radius:6px;font-family:var(--font);font-size:11px; | |
| font-weight:600;cursor:pointer;letter-spacing:.3px;transition:all .15s;border:none;white-space:nowrap} | |
| button.primary{background:var(--grad);color:#fff;box-shadow:0 2px 8px rgba(233,30,140,.25)} | |
| button.primary:hover{opacity:.9;box-shadow:0 4px 14px rgba(233,30,140,.35);transform:translateY(-1px)} | |
| button.secondary{background:var(--grad-soft);color:var(--amber);border:1px solid var(--border-strong)} | |
| button.secondary:hover{background:var(--border-strong);color:#fff} | |
| button.ghost{background:transparent;color:var(--dim);border:1px solid var(--border)} | |
| button.ghost:hover{border-color:var(--amber);color:var(--amber)} | |
| button.danger{background:#fff1f2;color:var(--red);border:1px solid #fecdd3} | |
| button.danger:hover{background:var(--red);color:#fff} | |
| button:disabled{opacity:.4;cursor:not-allowed;transform:none!important} | |
| .btn-row{display:flex;gap:6px;flex-wrap:wrap} | |
| /* ── Feed ── */ | |
| .feed{background:#fffbf0;border:1px solid var(--border);border-radius:6px; | |
| overflow-y:auto;padding:8px;font-size:11px;line-height:1.7;min-height:44px;max-height:150px;font-family:var(--mono)} | |
| .feed-item{padding:2px 0;border-bottom:1px solid var(--border)} | |
| .feed-item:last-child{border-bottom:none} | |
| .tag{display:inline-block;padding:1px 6px;border-radius:4px;font-size:9px; | |
| font-weight:700;margin-right:5px;letter-spacing:.3px} | |
| .tag.live{background:#d1fae5;color:#065f46} | |
| .tag.q{background:#fef3c7;color:#92400e} | |
| .tag.err{background:#fee2e2;color:#991b1b} | |
| .tag.rss{background:#fef9c3;color:var(--amber)} | |
| .tag.tune{background:#fce7f3;color:#9d174d} | |
| .tag.hf{background:#fef9c3;color:#78350f} | |
| .tag.ax{background:#e0f2fe;color:#075985} | |
| .feed .dim{color:var(--dimmer)} | |
| /* ── Grid ── */ | |
| .main-section{background:var(--white);border-bottom:1px solid var(--border)} | |
| .main-section-hdr{padding:10px 20px;display:flex;justify-content:space-between;align-items:center; | |
| border-bottom:1px solid var(--border)} | |
| .main-section-hdr h3{font-size:11px;font-weight:700;text-transform:uppercase; | |
| letter-spacing:1px;color:var(--dim)} | |
| /* ── Freq ticker ── */ | |
| .freq-ticker{display:flex;flex-wrap:wrap;gap:6px;padding:12px 20px;min-height:56px;align-items:center} | |
| .freq-pill{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;border-radius:20px; | |
| font-size:11px;font-weight:500;cursor:default;transition:all .2s;border:1px solid} | |
| .freq-pill.active{background:#fce7f3;border-color:#f9a8d4;color:#9d174d} | |
| .freq-pill.prot{background:#fef3c7;border-color:#fde68a;color:#92400e} | |
| .fp-dot{width:5px;height:5px;border-radius:50%} | |
| .freq-pill.active .fp-dot{background:var(--pink)} | |
| .freq-pill.prot .fp-dot{background:var(--yellow)} | |
| .no-freq{color:var(--dimmer);font-size:12px} | |
| /* ── Antenna grid ── */ | |
| .ant-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(64px,1fr));gap:5px;padding:14px 20px} | |
| .ant-node{position:relative;display:flex;flex-direction:column;align-items:center; | |
| justify-content:flex-end;height:42px;border-radius:8px;background:var(--bg); | |
| border:1px solid var(--border);cursor:default;overflow:visible;transition:all .3s} | |
| .ant-node.active{background:linear-gradient(135deg,#fce7f3,#fef9c3);border-color:var(--pink-light); | |
| box-shadow:0 2px 8px rgba(233,30,140,.15)} | |
| .ant-node svg{flex:none;transition:color .3s} | |
| .ant-node.active svg{color:var(--pink)} | |
| .ant-node svg{color:var(--dimmer)} | |
| .ant-lbl{font-size:7px;font-weight:600;color:var(--dimmer);text-align:center; | |
| margin-top:2px;width:100%;overflow:hidden;text-overflow:ellipsis; | |
| white-space:nowrap;padding:0 3px;transition:color .3s} | |
| .ant-node.active .ant-lbl{color:var(--amber)} | |
| .ant-node.active::before{content:'';position:absolute;top:6px;left:50%; | |
| transform:translateX(-50%);width:6px;height:6px;border-radius:50%; | |
| border:1px solid var(--pink);animation:ring-out 1.8s ease-out infinite} | |
| @keyframes ring-out{0%{transform:translateX(-50%) scale(1);opacity:.8}100%{transform:translateX(-50%) scale(4);opacity:0}} | |
| .ant-tip{display:none;position:absolute;bottom:calc(100%+6px);left:50%; | |
| transform:translateX(-50%);background:var(--text);color:#fff;padding:5px 9px; | |
| border-radius:6px;white-space:nowrap;z-index:30;font-size:10px;font-weight:500; | |
| box-shadow:0 4px 14px rgba(0,0,0,.2)} | |
| .ant-node:hover .ant-tip{display:block} | |
| /* ── Inject panels ── */ | |
| /* ── Collapsible sections ── */ | |
| .ms-chev{display:inline-block;width:12px;font-size:9px;color:var(--dimmer); | |
| transition:transform .18s;flex:none} | |
| .collapsed>.main-section-hdr .ms-chev,.collapsed>.sec-hdr .ms-chev{transform:rotate(-90deg)} | |
| .main-section.collapsed>.ms-body,.panel.collapsed>.ms-body{display:none} | |
| .main-section-hdr,.panel>.sec-hdr{user-select:none} | |
| .main-section-hdr:hover h3,.panel>.sec-hdr:hover{color:var(--amber)} | |
| .ms-count{font-size:10px;color:var(--dimmer);font-weight:500;margin-left:6px} | |
| .ms-title{display:flex;align-items:center;gap:8px;min-width:0} | |
| /* ── Build antenna ── */ | |
| .ant-build{padding:14px 20px 18px} | |
| .ant-build-note{font-size:11px;color:var(--dim);line-height:1.6;margin-bottom:12px;max-width:760px} | |
| .ant-build-row{display:grid;grid-template-columns:minmax(150px,1.1fr) minmax(170px,1.2fr) minmax(110px,.8fr) auto; | |
| gap:10px;align-items:end} | |
| .ant-build-row .field{margin:0;min-width:0} | |
| .ant-build-row .btn-row{padding:0;white-space:nowrap} | |
| .ant-shape{font-size:11px;color:var(--dim);line-height:1.7;margin:10px 0 0} | |
| .ant-shape code{background:var(--bg);border:1px solid var(--border);border-radius:3px; | |
| padding:1px 4px;font-family:var(--mono);font-size:10px;display:inline-block;margin:1px 0} | |
| #ant-out{margin-top:12px} | |
| #ant-out pre{max-height:340px;overflow:auto;background:var(--bg);border:1px solid var(--border); | |
| border-radius:6px;padding:10px;font-size:11px;line-height:1.5;font-family:var(--mono)} | |
| @media(max-width:1100px){.ant-build-row{grid-template-columns:1fr 1fr}} | |
| @media(max-width:640px){.ant-build-row{grid-template-columns:1fr}} | |
| .inject-grid{display:grid;grid-template-columns:1fr 1fr 1fr;border-bottom:1px solid var(--border)} | |
| .inject-panel{padding:14px 16px;border-right:1px solid var(--border);background:var(--white)} | |
| .inject-panel:last-child{border-right:none} | |
| /* ── Tabs ── */ | |
| .tabs{display:flex;border-bottom:1px solid var(--border);margin-bottom:12px} | |
| .tab{padding:5px 12px;font-size:11px;font-weight:600;color:var(--dim);cursor:pointer; | |
| border:none;background:transparent;font-family:var(--font); | |
| border-bottom:2px solid transparent;transition:all .15s} | |
| .tab:hover{color:var(--amber)} | |
| .tab.active{color:var(--amber);border-bottom-color:var(--amber)} | |
| /* ── Preset grid ── */ | |
| .preset-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-bottom:10px} | |
| .preset-btn{padding:5px 8px;font-size:10px;font-weight:500;text-align:left; | |
| overflow:hidden;text-overflow:ellipsis;white-space:nowrap} | |
| @media(max-width:1280px){ | |
| .inject-grid{grid-template-columns:1fr 1fr} | |
| } | |
| @media(max-width:900px){ | |
| .layout{grid-template-columns:1fr} | |
| .sidebar{position:static;max-height:none;border-right:none;border-bottom:1px solid var(--border)} | |
| .inject-grid{grid-template-columns:1fr} | |
| .compare{grid-template-columns:1fr} | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- HEADER --> | |
| <div class="header"> | |
| <div class="logo"> | |
| <div class="logo-icon">📡</div> | |
| <div> | |
| <div class="logo-name">SignalMesh</div> | |
| <div class="logo-tag">SignalMesh · Sovereign Liquid Matrix</div> | |
| </div> | |
| </div> | |
| <div class="header-nav"> | |
| <span><span class="live-dot"></span><span id="live-label" style="font-size:11px;font-weight:600;color:var(--green)">Live</span></span> | |
| <a class="nav-link" href="/mport">MPort</a> | |
| <a class="nav-link" href="/studio">Studio</a> | |
| <a class="nav-link" href="/docs">API Docs</a> | |
| <a class="nav-link" href="/redoc">Redoc</a> | |
| <a class="nav-link" href="https://kyklos.io/apps/signalmesh/#pricing">Managed Access</a> | |
| </div> | |
| </div> | |
| <!-- STATUS STRIP --> | |
| <div class="status-strip"> | |
| <div class="sstat"><div class="sstat-val" id="s-sig">—</div><div class="sstat-lbl">Signals in Mesh</div></div> | |
| <div class="sstat"><div class="sstat-val" id="s-fr">—</div><div class="sstat-lbl">Frequencies</div></div> | |
| <div class="sstat"><div class="sstat-val" id="s-nd">72</div><div class="sstat-lbl">Grid Nodes</div></div> | |
| <div class="sstat"><div class="sstat-val" id="s-q">—</div><div class="sstat-lbl">SEC-Ω Queue</div></div> | |
| </div> | |
| <div class="layout"> | |
| <!-- ═══════════ SIDEBAR ═══════════ --> | |
| <div class="sidebar"> | |
| <!-- Walkthrough --> | |
| <div class="wt-tabs" id="wt-tabs"> | |
| <button class="wt-tab active" onclick="wt(0,this)">What is this?</button> | |
| <button class="wt-tab" onclick="wt(1,this)">Broadcast</button> | |
| <button class="wt-tab" onclick="wt(2,this)">Tune In</button> | |
| <button class="wt-tab" onclick="wt(3,this)">Feed it</button> | |
| <button class="wt-tab" onclick="wt(4,this)">The Grid</button> | |
| <button class="wt-tab" onclick="wt(5,this)">Flash</button> | |
| </div> | |
| <div class="wt-body" id="wt-body"></div> | |
| <!-- Broadcast --> | |
| <div class="panel"> | |
| <div class="sec-hdr"><div class="dot dot-pink"></div>Transmit Signal</div> | |
| <div class="freq-display"> | |
| <div class="freq-label">Frequency</div> | |
| <div class="freq-value" id="bc-freq-display">signalmesh_signal</div> | |
| </div> | |
| <div class="field"><label>Frequency name</label> | |
| <input id="bc-freq" value="signalmesh_signal" oninput="document.getElementById('bc-freq-display').textContent=this.value||'…'"/> | |
| </div> | |
| <div class="field"><label>Signal content</label> | |
| <textarea id="bc-content" rows="3" placeholder="Any text, JSON, data…"></textarea> | |
| </div> | |
| <div class="field"><label>Source type</label> | |
| <select id="bc-type"> | |
| <option value="external">external</option> | |
| <option value="agent_broadcast">agent_broadcast</option> | |
| <option value="rss_cp">rss_cp</option> | |
| <option value="hf_inject">hf_inject</option> | |
| <option value="arxiv_inject">arxiv_inject</option> | |
| </select> | |
| </div> | |
| <div class="btn-row"> | |
| <button class="primary" onclick="doBroadcast()">▶ Broadcast</button> | |
| <button class="ghost" onclick="doBroadcast(true)">Bypass SEC-Ω</button> | |
| </div> | |
| <div class="feed" id="bc-feed" style="margin-top:10px;height:110px"></div> | |
| </div> | |
| <!-- Tune In --> | |
| <div class="panel" style="border-bottom:none"> | |
| <div class="sec-hdr"><div class="dot dot-amber"></div>Tune In</div> | |
| <div class="tuner-wrap"> | |
| <div class="tuner-scan"></div> | |
| <div class="tuner-freq" id="tuner-freq">signalmesh_signal, backend-architect</div> | |
| <div class="tuner-meta" id="tuner-meta">awaiting signal…</div> | |
| <div class="tuner-bars" id="tuner-bars"></div> | |
| </div> | |
| <div class="field"><label>Keywords (comma-separated)</label> | |
| <input id="tune-kw" value="signalmesh_signal,backend-architect" | |
| oninput="document.getElementById('tuner-freq').textContent=this.value"/> | |
| </div> | |
| <div class="btn-row"> | |
| <button class="primary" onclick="doTuneIn()">📡 Tune In</button> | |
| <button class="secondary" id="autobtn" onclick="toggleAuto()">⟳ Auto 3s</button> | |
| <button class="ghost" onclick="clearFeed('tune-feed')">Clear</button> | |
| </div> | |
| <div class="feed" id="tune-feed" style="margin-top:10px;height:160px"></div> | |
| </div> | |
| </div><!-- /sidebar --> | |
| <!-- ═══════════ MAIN ═══════════ --> | |
| <div class="main"> | |
| <!-- Live frequency ticker --> | |
| <div class="main-section"> | |
| <div class="main-section-hdr"> | |
| <h3>⚡ Live Frequencies</h3> | |
| <span style="font-size:10px;color:var(--dimmer)" id="ticker-ts"></span> | |
| </div> | |
| <div class="freq-ticker" id="freq-ticker"><span class="no-freq">No active frequencies yet — broadcast a signal or sync a feed.</span></div> | |
| </div> | |
| <!-- Inject panels --> | |
| <div class="main-section"> | |
| <div class="main-section-hdr"><h3>📥 Ingest — RSS · HuggingFace · arXiv</h3></div> | |
| <div class="inject-grid"> | |
| <!-- RSS --> | |
| <div class="inject-panel"> | |
| <div class="sec-hdr" style="padding:0 0 10px"><div class="dot dot-amber"></div>RSS Feed Sync</div> | |
| <div class="tabs"> | |
| <button class="tab active" onclick="setTab('rss','m',this)">URL</button> | |
| <button class="tab" onclick="setTab('rss','p',this)">Presets</button> | |
| </div> | |
| <div id="rss-m"> | |
| <div class="field"><label>Feed URL</label> | |
| <input id="rss-url" value="https://news.ycombinator.com/rss"/></div> | |
| <button class="primary" onclick="doRss()">Sync →</button> | |
| </div> | |
| <div id="rss-p" style="display:none"> | |
| <div class="preset-grid"> | |
| <button class="ghost preset-btn" onclick="rssP('https://news.ycombinator.com/rss')">HN Top</button> | |
| <button class="ghost preset-btn" onclick="rssP('https://www.reddit.com/r/MachineLearning/.rss')">r/ML</button> | |
| <button class="ghost preset-btn" onclick="rssP('https://huggingface.co/blog/feed.xml')">HF Blog</button> | |
| <button class="ghost preset-btn" onclick="rssP('https://dev.to/feed/tag/ai')">dev.to AI</button> | |
| <button class="ghost preset-btn" onclick="rssP('https://openai.com/blog/rss.xml')">OpenAI</button> | |
| <button class="ghost preset-btn" onclick="rssP('https://www.anthropic.com/rss.xml')">Anthropic</button> | |
| </div> | |
| <button class="primary" onclick="doRss()">Sync selected →</button> | |
| </div> | |
| <div class="feed" id="rss-feed" style="margin-top:10px"></div> | |
| </div> | |
| <!-- HF --> | |
| <div class="inject-panel"> | |
| <div class="sec-hdr" style="padding:0 0 10px"><div class="dot dot-pink"></div>🤗 HF Dataset Flash</div> | |
| <div class="tabs"> | |
| <button class="tab active" onclick="setTab('hf','m',this)">Repo ID</button> | |
| <button class="tab" onclick="setTab('hf','p',this)">Presets</button> | |
| </div> | |
| <div id="hf-m"> | |
| <div class="field"><label>HuggingFace repo</label> | |
| <input id="hf-id" value="acecalisto3/SignalMesh"/></div> | |
| <div class="field"><label>Type</label> | |
| <select id="hf-type"><option value="model">Model</option><option value="dataset">Dataset</option><option value="space">Space</option></select> | |
| </div> | |
| <button class="primary" onclick="doHf()">⚡ Flash into Mesh</button> | |
| </div> | |
| <div id="hf-p" style="display:none"> | |
| <div class="preset-grid"> | |
| <button class="ghost preset-btn" onclick="hfP('acecalisto3/SignalMesh','space')">SignalMesh</button> | |
| <button class="ghost preset-btn" onclick="hfP('meta-llama/Llama-3.1-8B','model')">Llama 3.1</button> | |
| <button class="ghost preset-btn" onclick="hfP('microsoft/phi-4','model')">Phi-4</button> | |
| <button class="ghost preset-btn" onclick="hfP('Qwen/Qwen2.5-Coder-32B-Instruct','model')">Qwen2.5</button> | |
| <button class="ghost preset-btn" onclick="hfP('HuggingFaceFW/fineweb','dataset')">FineWeb</button> | |
| <button class="ghost preset-btn" onclick="hfP('openai/openai_humaneval','dataset')">HumanEval</button> | |
| </div> | |
| <button class="primary" onclick="doHf()">⚡ Flash selected</button> | |
| </div> | |
| <div class="feed" id="hf-feed" style="margin-top:10px"></div> | |
| </div> | |
| <!-- arxiv --> | |
| <div class="inject-panel"> | |
| <div class="sec-hdr" style="padding:0 0 10px"><div class="dot dot-green"></div>📄 arxiv Paper Inject</div> | |
| <div class="tabs"> | |
| <button class="tab active" onclick="setTab('ax','m',this)">Paper ID</button> | |
| <button class="tab" onclick="setTab('ax','p',this)">Key Papers</button> | |
| </div> | |
| <div id="ax-m"> | |
| <div class="field"><label>arxiv ID or URL</label> | |
| <input id="ax-id" value="2303.08774"/></div> | |
| <button class="primary" onclick="doAx()">🔬 Inject Paper</button> | |
| </div> | |
| <div id="ax-p" style="display:none"> | |
| <div class="preset-grid"> | |
| <button class="ghost preset-btn" onclick="axP('2303.08774')">GPT-4 Report</button> | |
| <button class="ghost preset-btn" onclick="axP('2210.03629')">ReAct</button> | |
| <button class="ghost preset-btn" onclick="axP('2309.07864')">Cognitive Archs</button> | |
| <button class="ghost preset-btn" onclick="axP('2305.10601')">Tree of Thoughts</button> | |
| <button class="ghost preset-btn" onclick="axP('2401.04088')">LLM Agent Survey</button> | |
| <button class="ghost preset-btn" onclick="axP('2311.10208')">DSPy</button> | |
| </div> | |
| <button class="primary" onclick="doAx()">🔬 Inject selected</button> | |
| </div> | |
| <div class="feed" id="ax-feed" style="margin-top:10px"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Antenna Grid --> | |
| <div class="main-section" data-collapsed="1"> | |
| <div class="main-section-hdr"> | |
| <h3>📡 Antenna Grid — 9×8 Spatial Matrix (72 nodes)</h3> | |
| <div style="display:flex;gap:14px;font-size:11px"> | |
| <span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:linear-gradient(135deg,#fce7f3,#fef9c3);border:1px solid #f9a8d4;margin-right:4px"></span>Transmitting</span> | |
| <span style="color:var(--dimmer)"><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--bg);border:1px solid var(--border);margin-right:4px"></span>Silent</span> | |
| </div> | |
| </div> | |
| <div class="ant-grid" id="ant-grid"></div> | |
| </div> | |
| <!-- Build Antenna --> | |
| <div class="main-section"> | |
| <div class="main-section-hdr"> | |
| <h3>🛠 Build Antenna — materialize a frequency somewhere</h3> | |
| <span style="font-size:10px;color:var(--dimmer)" id="ant-shape-ts"></span> | |
| </div> | |
| <div class="ant-build"> | |
| <div class="ant-build-note"> | |
| Transmit puts a signal on a frequency. This builds the receiving end: it reads what the | |
| frequency is <em>actually</em> carrying right now and emits a wrapper shaped to it. | |
| Same signal, any number of antennas. | |
| </div> | |
| <div class="ant-build-row"> | |
| <div class="field"><label>Frequency</label> | |
| <select id="ant-freq"><option value="">— pick a live frequency —</option></select> | |
| </div> | |
| <div class="field"><label>Materialize as</label> | |
| <select id="ant-target"> | |
| <option value="nextjs_route">Web route — Next.js / Vercel</option> | |
| <option value="html_embed">Embeddable page — standalone HTML</option> | |
| <option value="shell">Terminal — shell script</option> | |
| <option value="system_prompt">Agent context — silent system prompt</option> | |
| </select> | |
| </div> | |
| <div class="field"><label>Name (optional)</label> | |
| <input id="ant-name" placeholder="calendar"/> | |
| </div> | |
| <div class="btn-row"> | |
| <button class="secondary" onclick="inspectAntenna()">Inspect</button> | |
| <button class="primary" onclick="buildAntenna()">🛠 Build</button> | |
| </div> | |
| </div> | |
| <div id="ant-shape" class="ant-shape"></div> | |
| <div id="ant-out"></div> | |
| </div> | |
| </div> | |
| </div><!-- /main --> | |
| </div><!-- /layout --> | |
| <script> | |
| const H={'Content-Type':'application/json'}; | |
| async function api(m,p,b){ | |
| try{const r=await fetch(p,{method:m,headers:H,body:b?JSON.stringify(b):undefined});return await r.json()} | |
| catch(e){return{error:String(e)}} | |
| } | |
| const ts=()=>new Date().toLocaleTimeString(); | |
| const esc=s=>String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); | |
| function appendFeed(id,html){const el=document.getElementById(id);if(!el)return;el.innerHTML+=`<div class="feed-item">${html}</div>`;el.scrollTop=el.scrollHeight} | |
| function clearFeed(id){const el=document.getElementById(id);if(el)el.innerHTML=''} | |
| // Status | |
| async function pollStatus(){ | |
| const d=await api('GET','/ui/status'); | |
| if(d.error)return; | |
| document.getElementById('s-sig').textContent=d.signals_in_mesh??'—'; | |
| document.getElementById('s-fr').textContent=d.active_frequencies??'—'; | |
| document.getElementById('s-nd').textContent=d.grid_nodes??72; | |
| document.getElementById('s-q').textContent=d.quarantined??0; | |
| } | |
| // Frequency ticker | |
| async function pollFreq(){ | |
| const d=await api('GET','/ui/frequencies'); | |
| const el=document.getElementById('freq-ticker'); | |
| if(!el||d.error||!d.frequencies)return; | |
| fillAntFreqs(d.frequencies); | |
| if(!d.frequencies.length){el.innerHTML='<span class="no-freq">No active frequencies yet — broadcast a signal or sync a feed to populate the mesh.</span>';return} | |
| el.innerHTML=d.frequencies.slice(0,40).map(f=> | |
| `<div class="freq-pill ${f.protected?'prot':'active'}"><div class="fp-dot"></div>${esc(f.frequency.length>22?f.frequency.slice(0,22)+'…':f.frequency)}<span style="font-size:9px;color:var(--dimmer);margin-left:2px">${f.signal_count}</span></div>` | |
| ).join(''); | |
| document.getElementById('ticker-ts').textContent='updated '+ts(); | |
| } | |
| // Grid | |
| const ANT=`<svg width="14" height="19" viewBox="0 0 14 19" fill="none" stroke="currentColor" stroke-linecap="round"><line x1="7" y1="19" x2="7" y2="10" stroke-width="1.5"/><line x1="3" y1="14" x2="11" y2="14" stroke-width="1"/><circle cx="7" cy="9" r="2" fill="currentColor" stroke="none"/><line x1="7" y1="7" x2="2" y2="3" stroke-width="1.5"/><line x1="7" y1="7" x2="12" y2="3" stroke-width="1.5"/></svg>`; | |
| async function pollGrid(){ | |
| const d=await api('GET','/ui/grid'); | |
| const el=document.getElementById('ant-grid'); | |
| if(!el||d.error||!d.nodes)return; | |
| el.innerHTML=d.nodes.map(n=>{ | |
| const lbl=n.agent?n.agent.split('-').map(w=>w[0]).join('').toUpperCase().slice(0,3):'·'; | |
| return `<div class="ant-node ${n.active?'active':''}">${ANT}<div class="ant-lbl">${lbl}</div><div class="ant-tip">${esc(n.agent||'empty')} · node ${n.node} · ${n.signal_count} sig</div></div>`; | |
| }).join(''); | |
| } | |
| // Collapsible sections — everything after a header folds, state remembered | |
| function initCollapse(){ | |
| const setup=(sec,hdr)=>{ | |
| if(!hdr||sec.dataset.msInit)return; sec.dataset.msInit='1'; | |
| const label=(hdr.querySelector('h3')||hdr).textContent.trim().slice(0,40); | |
| const key='ms:'+label; | |
| const body=document.createElement('div'); body.className='ms-body'; | |
| while(hdr.nextSibling) body.appendChild(hdr.nextSibling); | |
| sec.appendChild(body); | |
| const chev=document.createElement('span'); chev.className='ms-chev'; chev.textContent='▾'; | |
| const h3=hdr.querySelector('h3'); | |
| if(h3){const grp=document.createElement('div'); grp.className='ms-title'; | |
| hdr.insertBefore(grp,h3); grp.appendChild(chev); grp.appendChild(h3);} | |
| else hdr.insertBefore(chev,hdr.firstChild); | |
| hdr.style.cursor='pointer'; | |
| const stored=localStorage.getItem(key); | |
| const start=stored===null?sec.dataset.collapsed==='1':stored==='1'; | |
| sec.classList.toggle('collapsed',start); | |
| hdr.addEventListener('click',e=>{ | |
| if(e.target.closest('button,select,input,textarea,a'))return; | |
| const now=!sec.classList.contains('collapsed'); | |
| sec.classList.toggle('collapsed',now); | |
| localStorage.setItem(key,now?'1':'0'); | |
| }); | |
| }; | |
| document.querySelectorAll('.main-section').forEach(s=>setup(s,s.querySelector(':scope>.main-section-hdr'))); | |
| document.querySelectorAll('.sidebar .panel').forEach(s=>setup(s,s.querySelector(':scope>.sec-hdr'))); | |
| } | |
| // Antennas | |
| function antShapeLine(s){ | |
| if(!s||s.kind==='empty') return '<span class="dim">Nothing on this frequency yet — transmit a pointer or sync a feed first.</span>'; | |
| const f=(s.fields||[]).map(x=>`<code>${esc(x)}</code>`).join(' '); | |
| const n=s.count?` · ${s.count} records`:''; | |
| return `carrying <b>${esc(s.kind)}</b> as <b>${esc(s.signal_type||'—')}</b>${n}${f?`<br>fields: ${f}`:''}`; | |
| } | |
| async function inspectAntenna(){ | |
| const frequency=document.getElementById('ant-freq').value; | |
| if(!frequency){document.getElementById('ant-shape').innerHTML='<span class="dim">Pick a frequency.</span>';return} | |
| const s=await api('GET','/ui/antennas/'+encodeURIComponent(frequency)); | |
| document.getElementById('ant-shape').innerHTML=antShapeLine(s); | |
| document.getElementById('ant-shape-ts').textContent='inspected '+ts(); | |
| } | |
| async function buildAntenna(){ | |
| const frequency=document.getElementById('ant-freq').value; | |
| if(!frequency){document.getElementById('ant-shape').innerHTML='<span class="dim">Pick a frequency.</span>';return} | |
| const target=document.getElementById('ant-target').value; | |
| const name=document.getElementById('ant-name').value.trim()||null; | |
| const d=await api('POST','/ui/antennas',{frequency,target,name}); | |
| if(d.detail||d.error){document.getElementById('ant-out').innerHTML=`<span class="tag err">ERR</span> ${esc(JSON.stringify(d.detail||d.error))}`;return} | |
| document.getElementById('ant-shape').innerHTML=antShapeLine(d.shape); | |
| document.getElementById('ant-shape-ts').textContent='built '+ts(); | |
| document.getElementById('ant-out').innerHTML= | |
| `<div style="display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:6px"> | |
| <b style="font-size:12px;font-family:var(--mono);overflow:hidden;text-overflow:ellipsis">${esc(d.filename)}</b> | |
| <button class="ghost" onclick="copyAnt(this)" data-i="${encodeURIComponent(d.code)}">Copy</button> | |
| </div> | |
| <pre><code>${esc(d.code)}</code></pre>`; | |
| } | |
| function copyAnt(b){navigator.clipboard.writeText(decodeURIComponent(b.dataset.i));b.textContent='Copied';setTimeout(()=>b.textContent='Copy',1600)} | |
| function fillAntFreqs(freqs){ | |
| const el=document.getElementById('ant-freq'); if(!el)return; | |
| const cur=el.value; | |
| el.innerHTML='<option value="">— pick a live frequency —</option>'+ | |
| freqs.map(f=>`<option value="${esc(f.frequency)}">${esc(f.frequency)} (${f.signal_count})</option>`).join(''); | |
| if(cur) el.value=cur; | |
| } | |
| // Broadcast | |
| async function doBroadcast(bypass=false){ | |
| const freq=document.getElementById('bc-freq').value.trim(); | |
| const content=document.getElementById('bc-content').value.trim(); | |
| const source_type=document.getElementById('bc-type').value; | |
| if(!freq||!content){appendFeed('bc-feed','<span class="tag err">Missing frequency or content</span>');return} | |
| const d=await api('POST','/ui/broadcast',{frequency:freq,content,source_type,bypass_gate:bypass}); | |
| if(d.status==='live') appendFeed('bc-feed',`<span class="tag live">LIVE</span><b>${esc(freq)}</b> → node ${d.node} <span class="dim">(${esc(d.agent||'')})</span>`); | |
| else if(d.status==='quarantined') appendFeed('bc-feed',`<span class="tag q">SEC-Ω</span><b>${esc(freq)}</b> staged for review`); | |
| else appendFeed('bc-feed',`<span class="tag err">ERR</span>${esc(JSON.stringify(d).slice(0,80))}`); | |
| refresh(); | |
| } | |
| // Tune | |
| let _auto=null; | |
| async function doTuneIn(){ | |
| const kws=document.getElementById('tune-kw').value.split(',').map(k=>k.trim()).filter(Boolean); | |
| if(!kws.length)return; | |
| const d=await api('POST','/ui/tune_in',{keywords:kws}); | |
| document.getElementById('tuner-meta').textContent=`${d.signals_matched??0} matched · ${d.latency_us??0}µs`; | |
| renderBars(d.signals_matched??0); | |
| clearFeed('tune-feed'); | |
| appendFeed('tune-feed',`<span class="tag tune">TUNE</span><b>${kws.join(', ')}</b> · ${d.signals_matched} signals · ${d.latency_us}µs`); | |
| // ── Fuzzy trail bridges ──────────────────────────────────────────────────── | |
| if(d.misses && d.misses.length){ | |
| d.misses.forEach(m=>{ | |
| appendFeed('tune-feed', | |
| `<span class="tag" style="background:#7f5af022;color:#b464ff;border:1px solid #b464ff55">⟿ TRAIL</span>`+ | |
| `<b>${esc(m.keyword)}</b> missed — bridged to `+ | |
| `<b style="color:#7ddfff">${esc(m.bridged_to)}</b> `+ | |
| `<span style="color:#32ff9f">@ ${Math.round((m.confidence||0)*100)}% confidence</span>`+ | |
| (m.branches&&m.branches.length ? ` · branches: ${m.branches.slice(0,3).map(b=>`<span style="color:#f59e0b">${esc(b)}</span>`).join(', ')}` : '')+ | |
| ` <span style="color:#3a3a5c">— trail written, next call is instant</span>` | |
| ); | |
| }); | |
| } | |
| if(d.trails_fired && Object.keys(d.trails_fired).length){ | |
| Object.entries(d.trails_fired).forEach(([kw,t])=>{ | |
| appendFeed('tune-feed', | |
| `<span class="tag" style="background:#32ff9f22;color:#32ff9f;border:1px solid #32ff9f55">⚡ FAST PATH</span>`+ | |
| `<b>${esc(kw)}</b> → trail hit: <b style="color:#7ddfff">${esc(t.bridged_to||'')}</b>`+ | |
| ` <span style="color:#32ff9f">@ ${Math.round((t.confidence||0)*100)}%</span>` | |
| ); | |
| }); | |
| } | |
| // ── Context ──────────────────────────────────────────────────────────────── | |
| if(d.context) d.context.split('\n').forEach(l=>{if(l)appendFeed('tune-feed',`<span class="dim">${esc(l)}</span>`)}); | |
| else if(!d.misses||!d.misses.length) appendFeed('tune-feed','<span class="dim">No signals on these frequencies yet.</span>'); | |
| } | |
| function renderBars(n){ | |
| const el=document.getElementById('tuner-bars');if(!el)return; | |
| el.innerHTML=Array.from({length:20},(_,i)=>{ | |
| const on=i<Math.min(n*2,20); | |
| const h=on?(4+Math.random()*14):2; | |
| const c=on?`hsl(${310+i*5},70%,65%)`:'rgba(255,255,255,.15)'; | |
| return `<div class="tuner-bar" style="height:${h}px;background:${c}"></div>`; | |
| }).join(''); | |
| } | |
| function toggleAuto(){ | |
| const btn=document.getElementById('autobtn'); | |
| if(_auto){clearInterval(_auto);_auto=null;btn.textContent='⟳ Auto 3s';btn.className='secondary'} | |
| else{doTuneIn();_auto=setInterval(doTuneIn,3000);btn.textContent='■ Stop';btn.className='primary'} | |
| } | |
| // Tabs | |
| function setTab(g,id,el){ | |
| ['m','p'].forEach(t=>{const d=document.getElementById(g+'-'+t);if(d)d.style.display=t===id?'block':'none'}); | |
| el.closest('.tabs').querySelectorAll('.tab').forEach(t=>t.classList.remove('active')); | |
| el.classList.add('active'); | |
| } | |
| // RSS | |
| function rssP(u){document.getElementById('rss-url').value=u} | |
| async function doRss(){ | |
| const url=document.getElementById('rss-url').value.trim();if(!url)return; | |
| appendFeed('rss-feed',`<span class="tag rss">SYNC</span>Fetching…`); | |
| const d=await api('POST','/ui/rss_sync',{url}); | |
| if(d.error||d.detail){appendFeed('rss-feed',`<span class="tag err">ERR</span>${esc(String(d.error||d.detail))}`);return} | |
| const lbl=d.feed_title?`<b>${esc(d.feed_title)}</b> · `:''; | |
| appendFeed('rss-feed',`<span class="tag rss">RSS</span>${lbl}<b>${d.synced}</b> items → ${d.signals_in_mesh} in mesh`); | |
| if(!d.synced)appendFeed('rss-feed','<span class="dim">Feed returned 0 items.</span>'); | |
| (d.items||[]).slice(0,6).forEach(it=>appendFeed('rss-feed',`<span class="dim">↳ node ${it.node}</span> ${esc((it.title||'').slice(0,55))}`)); | |
| refresh(); | |
| } | |
| // HF | |
| function hfP(id,t){document.getElementById('hf-id').value=id;document.getElementById('hf-type').value=t} | |
| async function doHf(){ | |
| const repo_id=document.getElementById('hf-id').value.trim(); | |
| const repo_type=document.getElementById('hf-type').value;if(!repo_id)return; | |
| appendFeed('hf-feed',`<span class="tag hf">FLASH</span>Injecting ${esc(repo_id)}…`); | |
| const d=await api('POST','/ui/hf_inject',{repo_id,repo_type}); | |
| if(d.error||d.detail){appendFeed('hf-feed',`<span class="tag err">ERR</span>${esc(String(d.error||d.detail))}`);return} | |
| const info=d.injected||{}; | |
| appendFeed('hf-feed',`<span class="tag hf">HF</span><b>${esc(info.id||repo_id)}</b> [${info.type}] → node ${d.node}`); | |
| if(info.pipeline_tag)appendFeed('hf-feed',`<span class="dim">task: ${esc(info.pipeline_tag)} · ↓${(info.downloads||0).toLocaleString()}</span>`); | |
| if((info.tags||[]).length)appendFeed('hf-feed',`<span class="dim">${info.tags.slice(0,6).map(esc).join(', ')}</span>`); | |
| appendFeed('hf-feed',`<a href="${esc(d.hf_url||'')}" target="_blank" style="color:var(--amber)">→ Open on HuggingFace</a>`); | |
| refresh(); | |
| } | |
| // arxiv | |
| function axP(id){document.getElementById('ax-id').value=id} | |
| async function doAx(){ | |
| const arxiv_id=document.getElementById('ax-id').value.trim();if(!arxiv_id)return; | |
| appendFeed('ax-feed',`<span class="tag ax">FETCH</span>arxiv:${esc(arxiv_id)}…`); | |
| const d=await api('POST','/ui/arxiv_inject',{arxiv_id}); | |
| if(d.error||d.detail){appendFeed('ax-feed',`<span class="tag err">ERR</span>${esc(String(d.error||d.detail))}`);return} | |
| const p=d.injected||{}; | |
| appendFeed('ax-feed',`<span class="tag ax">PAPER</span><b>${esc((p.title||'').slice(0,60))}</b>`); | |
| if(p.authors)appendFeed('ax-feed',`<span class="dim">${p.authors.slice(0,3).map(esc).join(', ')}</span>`); | |
| if(p.summary)appendFeed('ax-feed',`<span class="dim">${esc(p.summary.slice(0,180))}…</span>`); | |
| if(p.link)appendFeed('ax-feed',`<a href="${esc(p.link)}" target="_blank" style="color:#0284c7">Open on arxiv →</a>`); | |
| refresh(); | |
| } | |
| // Walkthrough | |
| const WT=[ | |
| {title:'What is SignalMesh?',body:`<p>SignalMesh is an <strong>ambient context protocol</strong> — the nervous system of an AI agent fleet. Instead of agents polling for data with tool calls, data <em>broadcasts itself</em> to the right agents automatically.</p> | |
| <div class="compare"> | |
| <div class="cbox old"><div class="clbl">❌ Old Way</div>Agent asks → tool call → wait → answer<br>5 agents × 3 queries = <strong>15 extra inferences</strong></div> | |
| <div class="cbox new"><div class="clbl">✅ SignalMesh</div>Data broadcasts → agents tune in<br><strong>0 tool calls · 1.69µs · 96% less overhead</strong></div> | |
| </div> | |
| <div class="callout">📡 <strong>Broadcast</strong> = push data to a named frequency<br>🎛 <strong>Tune In</strong> = receive all signals matching your keywords<br>🗺 <strong>Grid</strong> = 72 spatial nodes, SHA-256 routed</div>`, | |
| demo:()=>runDemo()}, | |
| {title:'Broadcasting a Signal',body:`<p>Pick a <strong>frequency name</strong> (like a radio station) and write your content. Hit Broadcast — it routes deterministically to a grid node via SHA-256.</p> | |
| <div class="callout"><code>SHA-256("backend_events") % 72 → node 14</code><br>Same frequency always = same node. <strong>Zero config.</strong><br><br>⚠️ Frequencies starting with <strong>security_</strong>, <strong>auth_</strong>, <strong>key_</strong> go to SEC-Ω quarantine.</div>`, | |
| demo:()=>{document.getElementById('bc-freq').value='signalmesh_demo';document.getElementById('bc-freq-display').textContent='signalmesh_demo';document.getElementById('bc-content').value='Hello SignalMesh — ambient context at 1.69µs';doBroadcast()}}, | |
| {title:'Tuning In',body:`<p>Agents are <strong>antennae</strong>. They declare keyword frequencies — and matching signals flow into their context before they even ask.</p> | |
| <div class="callout"><strong>POST /ui/tune_in</strong><br><code>{"keywords": ["signalmesh_demo", "backend"]}</code><br><br>Returns a context string you inject directly into any LLM system prompt. Average: <strong>1.69µs</strong>.</div>`, | |
| demo:()=>{document.getElementById('tune-kw').value='signalmesh_demo';document.getElementById('tuner-freq').textContent='signalmesh_demo';doTuneIn()}}, | |
| {title:'Feeding the Mesh',body:`<p>Any live data source — RSS feeds, HN, Reddit, blogs — gets ingested and distributed across the 72-node grid automatically.</p> | |
| <div class="callout">Hit <strong>Sync →</strong> with any RSS URL. Each feed item gets SHA-256 routed to the agent domain most relevant to its content. No manual assignment.</div>`, | |
| demo:async()=>{document.getElementById('rss-url').value='https://news.ycombinator.com/rss';await doRss()}}, | |
| {title:'The Antenna Grid',body:`<p>The 9×8 grid is the <strong>spatial topology</strong> of the agent fleet. 72 nodes, each mapped to an agent role.</p> | |
| <div class="callout"><strong>Row 0</strong>: Architecture (backend, frontend, mobile)<br><strong>Row 1</strong>: Language pros (Python, JS, Go, Rust)<br><strong>Row 2–3</strong>: Infra + DevOps<br><strong>Row 4</strong>: AI/ML agents<br><strong>Row 5</strong>: Security + Quality<br><strong>Rows 6–8</strong>: Business, Growth, Utility</div>`, | |
| demo:()=>refresh()}, | |
| {title:'Dataset Flashing',body:`<p><strong>Flash</strong> any HuggingFace model, dataset, or Space into the mesh — its metadata becomes live context for every relevant agent instantly.</p> | |
| <div class="callout">🤗 <strong>HF inject</strong> → model card, tags, downloads, pipeline task<br>📄 <strong>arxiv inject</strong> → title, authors, abstract<br><br>External systems call <code>/api/tools/hf_inject</code> with <code>X-SignalMesh-Key</code>.</div>`, | |
| demo:async()=>{hfP('acecalisto3/SignalMesh','space');await doHf()}}, | |
| ]; | |
| function wt(idx,el){ | |
| document.querySelectorAll('.wt-tab').forEach(b=>b.classList.remove('active')); | |
| if(el)el.classList.add('active'); | |
| const s=WT[idx]; | |
| document.getElementById('wt-body').innerHTML=` | |
| <div class="wt-title">${s.title}</div> | |
| <div class="wt-text">${s.body}</div> | |
| <div class="wt-actions"> | |
| <button class="primary" onclick="WT[${idx}].demo&&WT[${idx}].demo()">▶ Run Demo</button> | |
| ${idx>0?`<button class="ghost" onclick="wt(${idx-1},document.querySelectorAll('.wt-tab')[${idx-1}])">← Prev</button>`:''} | |
| ${idx<WT.length-1?`<button class="secondary" onclick="wt(${idx+1},document.querySelectorAll('.wt-tab')[${idx+1}])">Next →</button>`:''} | |
| </div>`; | |
| } | |
| async function runDemo(){ | |
| document.getElementById('bc-freq').value='signalmesh_demo'; | |
| document.getElementById('bc-freq-display').textContent='signalmesh_demo'; | |
| document.getElementById('bc-content').value='SignalMesh demo — ambient context at 1.69µs'; | |
| await doBroadcast(); | |
| await new Promise(r=>setTimeout(r,500)); | |
| document.getElementById('tune-kw').value='signalmesh_demo'; | |
| document.getElementById('tuner-freq').textContent='signalmesh_demo'; | |
| await doTuneIn(); | |
| await new Promise(r=>setTimeout(r,500)); | |
| document.getElementById('rss-url').value='https://news.ycombinator.com/rss'; | |
| await doRss(); | |
| } | |
| async function refresh(){await Promise.all([pollStatus(),pollFreq(),pollGrid()])} | |
| wt(0,document.querySelector('.wt-tab')); | |
| initCollapse(); | |
| refresh(); | |
| setInterval(refresh,4000); | |
| </script> | |
| </body> | |
| </html>""" | |
| # ── OCODX license minting (Ed25519, offline-verifiable by the binary) ────────── | |
| # Only this host holds the private signing key. The OCODX binary holds the matching | |
| # PUBLIC key and verifies the minted token offline — no validation endpoint needed. | |
| TIER_DEVICES = {"solo": 1, "pro": 3, "team": 10} | |
| def _tier_from_session(sess: dict) -> tuple: | |
| """Resolve (tier, devices) from Checkout Session metadata set at checkout time.""" | |
| md = sess.get("metadata") or {} | |
| tier = md.get("tier", "pro") | |
| if tier not in TIER_DEVICES: | |
| tier = "pro" | |
| try: | |
| devices = int(md.get("devices", 0)) or TIER_DEVICES[tier] | |
| except (TypeError, ValueError): | |
| devices = TIER_DEVICES[tier] | |
| return tier, devices | |
| def _mint_ocodx_key(email: str, tier: str, devices: int, days: int = 0) -> str: | |
| import base64 as _b64 | |
| from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey | |
| raw = os.environ.get("OCODX_SIGNING_KEY", "") | |
| if not raw: | |
| raise RuntimeError("OCODX_SIGNING_KEY not set — cannot mint license") | |
| priv = Ed25519PrivateKey.from_private_bytes( | |
| _b64.urlsafe_b64decode(raw + "=" * (-len(raw) % 4))) | |
| now = int(time.time()) | |
| payload = {"email": email, "tier": tier, "iat": now, | |
| "exp": now + days * 86400 if days > 0 else 0, "dev": devices} | |
| pb = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode() | |
| enc = lambda x: _b64.urlsafe_b64encode(x).decode().rstrip("=") | |
| return f"OCODX1.{enc(pb)}.{enc(priv.sign(pb))}" | |
| def _db_create_licensed_user(email, cid, sid, tier, devices) -> str: | |
| """Mint an OCODX license token and store it as the user's key.""" | |
| key = _mint_ocodx_key(email, tier, devices) | |
| uid = secrets.token_hex(8) | |
| with _DB_LOCK: | |
| conn = _db_conn() | |
| conn.execute("INSERT OR IGNORE INTO users VALUES (?,?,?,?,?,?,1,?,?,0)", | |
| (uid, email, key, tier, cid, sid, time.time(), time.time())) | |
| conn.commit(); conn.close() | |
| threading.Thread(target=_db_backup, daemon=True).start() | |
| return key | |
| def _provision_checkout_session(sess: dict) -> dict: | |
| """Mint the product the Stripe session actually purchased. | |
| SignalMesh/MPort purchases receive a managed ``smesh-`` API key. OCODX | |
| purchases continue to receive an offline-verifiable ``OCODX1`` license. | |
| Keeping these paths explicit prevents one product's checkout metadata from | |
| silently minting the other product's credential. | |
| """ | |
| if sess.get("payment_status") != "paid": | |
| raise ValueError("Payment not completed") | |
| md = sess.get("metadata") or {} | |
| product = str(md.get("product") or "signalmesh").lower() | |
| email = (sess.get("customer_details") or {}).get("email", "") | |
| cid = sess.get("customer", "") | |
| sid = sess.get("id", "") | |
| if not sid: | |
| raise ValueError("Checkout session has no id") | |
| existing = _db_get_by_session(sid) | |
| if existing: | |
| return { | |
| "api_key": existing["api_key"], | |
| "email": existing["email"], | |
| "tier": existing["tier"], | |
| "product": product, | |
| } | |
| if product == "signalmesh": | |
| checkout_tier = str(md.get("tier") or "monthly").lower() | |
| tier = ( | |
| "managed_lifetime" | |
| if checkout_tier in {"lifetime", "founding_lifetime"} | |
| else "managed_monthly" | |
| ) | |
| key = _db_create_user(email, cid, sid, tier=tier) | |
| return {"api_key": key, "email": email, "tier": tier, "product": product} | |
| if product == "ocodx": | |
| add_seats = int(md.get("add_seats", 0) or 0) | |
| if add_seats > 0: | |
| key = _db_add_seats(email, add_seats, cid, sid) | |
| row = _db_get_by_key(key) or {} | |
| tier = row.get("tier", "solo") | |
| else: | |
| tier, devices = _tier_from_session(sess) | |
| key = _db_create_licensed_user(email, cid, sid, tier, devices) | |
| return {"api_key": key, "email": email, "tier": tier, "product": product} | |
| raise ValueError(f"Unsupported checkout product: {product}") | |
| # ── Stripe webhook ──────────────────────────────────────────────────────────── | |
| async def stripe_webhook(request: Request): | |
| payload = await request.body() | |
| sig = request.headers.get("stripe-signature", "") | |
| secret = os.environ.get("STRIPE_WEBHOOK_SECRET", "") | |
| try: | |
| _stripe_verify(payload, sig, secret) | |
| except Exception as e: | |
| raise HTTPException(400, f"Webhook signature invalid: {e}") | |
| event = json.loads(payload) | |
| etype = event.get("type", "") | |
| if etype == "checkout.session.completed": | |
| sess = event["data"]["object"] | |
| if sess.get("payment_status") == "paid": | |
| _provision_checkout_session(sess) | |
| elif etype == "customer.subscription.deleted": | |
| cid = event["data"]["object"].get("customer", "") | |
| if cid: | |
| _db_revoke(cid) | |
| return {"received": True} | |
| def _stripe_verify(payload: bytes, sig_header: str, secret: str): | |
| import hmac, hashlib | |
| if not secret: | |
| raise ValueError("STRIPE_WEBHOOK_SECRET is not configured") | |
| parts = {k: v for k, v in (p.split("=", 1) for p in sig_header.split(",") if "=" in p)} | |
| ts = parts.get("t", "") | |
| v1 = parts.get("v1", "") | |
| if not ts or not v1: | |
| raise ValueError("Stripe signature header is incomplete") | |
| try: | |
| if abs(time.time() - int(ts)) > 300: | |
| raise ValueError("Stripe signature timestamp is outside the 5-minute tolerance") | |
| except ValueError: | |
| raise | |
| except Exception as exc: | |
| raise ValueError("Stripe signature timestamp is invalid") from exc | |
| signed = f"{ts}.".encode() + payload | |
| expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest() | |
| if not hmac.compare_digest(expected, v1): | |
| raise ValueError("Signature mismatch") | |
| # ── Key claim ───────────────────────────────────────────────────────────────── | |
| async def claim_key(session_id: str): | |
| """Verify Stripe checkout session and return API key. Safe to call multiple times.""" | |
| existing = _db_get_by_session(session_id) | |
| if existing: | |
| return {"api_key": existing["api_key"], "email": existing["email"], | |
| "tier": existing["tier"], "status": "active"} | |
| sk = os.environ.get("STRIPE_SECRET_KEY", "") | |
| if not sk: | |
| raise HTTPException(503, "Stripe not configured") | |
| try: | |
| import base64 as _b64, urllib.request as _ur | |
| req = _ur.Request(f"https://api.stripe.com/v1/checkout/sessions/{session_id}") | |
| req.add_header("Authorization", "Basic " + _b64.b64encode(f"{sk}:".encode()).decode()) | |
| with _ur.urlopen(req, timeout=10) as r: | |
| sess = json.loads(r.read()) | |
| except Exception as e: | |
| raise HTTPException(502, f"Stripe lookup failed: {e}") | |
| if sess.get("payment_status") != "paid": | |
| raise HTTPException(402, "Payment not completed") | |
| provisioned = _provision_checkout_session(sess) | |
| return {**provisioned, "status": "active"} | |
| # ── Key info ────────────────────────────────────────────────────────────────── | |
| def key_info(x_signalmesh_key: Optional[str] = Header(default=None)): | |
| """Returns tier, usage, and account info for the authenticated key.""" | |
| if not x_signalmesh_key: | |
| raise HTTPException(401, "No key provided") | |
| t = _tier(x_signalmesh_key) | |
| if t == "update": | |
| hits = [ts for ts in _update_usage.get(x_signalmesh_key, []) if ts > time.time() - 86400] | |
| return {"tier": "update", "calls_used_24h": len(hits), | |
| "limit_24h": _UPDATE_RATE_LIMIT, | |
| "note": "update channel only — submesh reads + ocodx.* tune_in", | |
| "upgrade": "https://kyklos.io/apps/signalmesh/"} | |
| if t in ("admin", "paid"): | |
| return {"tier": t, "public_socket_limit": _socket_limit(x_signalmesh_key)} | |
| db_user = _db_get_by_key(x_signalmesh_key) | |
| if db_user: | |
| return {"tier": db_user["tier"], "email": db_user["email"], | |
| "call_count": db_user["call_count"], | |
| "created_at": db_user["created_at"], | |
| "active": bool(db_user["active"]), | |
| "public_socket_limit": 1} | |
| raise HTTPException(401, "Invalid key") | |
| # ── Admin: list users ───────────────────────────────────────────────────────── | |
| def admin_users(x_signalmesh_key: Optional[str] = Header(default=None)): | |
| if _tier(x_signalmesh_key) != "admin": | |
| raise HTTPException(403, "Admin only") | |
| users = _db_all() | |
| for u in users: | |
| k = u.get("api_key", "") | |
| u["api_key"] = k[:12] + "..." if k else "" | |
| return {"users": users, "count": len(users)} | |
| def admin_dashboard(key: Optional[str] = None): | |
| """Sales + validation dashboard. Auth via ?key=<admin key> (browser-friendly).""" | |
| if _tier(key) != "admin": | |
| return HTMLResponse("<h2>403 — append ?key=<admin key></h2>", status_code=403) | |
| users = _db_all() | |
| now = time.time() | |
| total = len(users) | |
| active = sum(1 for u in users if u.get("active")) | |
| revoked = total - active | |
| # "validated" = the license has actually been used at least once (device check-in) | |
| validated = sum(1 for u in users if (u.get("call_count") or 0) > 0 or u.get("last_seen")) | |
| active_30d = sum(1 for u in users if (u.get("last_seen") or 0) > now - 30 * 86400) | |
| by_tier: Dict[str, int] = {} | |
| for u in users: | |
| by_tier[u.get("tier", "?")] = by_tier.get(u.get("tier", "?"), 0) + 1 | |
| legit = sum(1 for u in users if str(u.get("api_key", "")).startswith("OCODX1.")) | |
| tier_rows = "".join(f"<tr><td>{t}</td><td>{n}</td></tr>" for t, n in sorted(by_tier.items())) | |
| def row(u): | |
| k = u.get("api_key", "") | |
| kind = "OCODX1 ✓" if k.startswith("OCODX1.") else ("smesh (legacy)" if k else "—") | |
| seen = time.strftime("%Y-%m-%d", time.localtime(u["last_seen"])) if u.get("last_seen") else "never" | |
| made = time.strftime("%Y-%m-%d", time.localtime(u["created_at"])) if u.get("created_at") else "?" | |
| return (f"<tr><td>{u.get('email','')}</td><td>{u.get('tier','')}</td><td>{kind}</td>" | |
| f"<td>{'✓' if u.get('active') else '✗'}</td><td>{u.get('call_count',0)}</td>" | |
| f"<td>{seen}</td><td>{made}</td></tr>") | |
| recent = "".join(row(u) for u in users[:100]) | |
| html = f"""<!doctype html><meta charset=utf-8><title>SignalMesh · Admin</title> | |
| <style>body{{font:14px system-ui;margin:0;background:#0b0d12;color:#dde}} | |
| .wrap{{max-width:1000px;margin:0 auto;padding:24px}}h1{{font-size:18px}} | |
| .cards{{display:flex;gap:12px;flex-wrap:wrap;margin:16px 0}} | |
| .card{{background:#141923;border:1px solid #232a38;border-radius:10px;padding:14px 18px;min-width:120px}} | |
| .card b{{display:block;font-size:26px;color:#4dd0e1}}.card span{{color:#8a93a6;font-size:12px}} | |
| table{{width:100%;border-collapse:collapse;margin-top:10px;font-size:12.5px}} | |
| th,td{{border-bottom:1px solid #232a38;padding:7px 9px;text-align:left}} | |
| th{{color:#8a93a6;text-transform:uppercase;font-size:10px}}h2{{font-size:14px;margin-top:24px}}</style> | |
| <div class=wrap><h1>📡 SignalMesh — License Admin</h1> | |
| <div class=cards> | |
| <div class=card><b>{total}</b><span>keys sold</span></div> | |
| <div class=card><b>{validated}</b><span>validated (used)</span></div> | |
| <div class=card><b>{active_30d}</b><span>active · 30d</span></div> | |
| <div class=card><b>{active}</b><span>active</span></div> | |
| <div class=card><b>{revoked}</b><span>revoked</span></div> | |
| <div class=card><b>{legit}</b><span>OCODX1 tokens</span></div> | |
| </div> | |
| <h2>By tier</h2><table><tr><th>tier</th><th>count</th></tr>{tier_rows}</table> | |
| <h2>Recent (latest 100)</h2> | |
| <table><tr><th>email</th><th>tier</th><th>key type</th><th>active</th><th>uses</th><th>last seen</th><th>created</th></tr>{recent}</table> | |
| </div>""" | |
| return HTMLResponse(html) | |
| def root(): | |
| return _HTML | |
| _CHAT_SYSTEM = ( | |
| "You are SignalMesh, the Sovereign Liquid Matrix agent. Answer the message that was actually " | |
| "sent, in as few words as it takes.\n\n" | |
| "1. If the live context below already answers the question, answer FROM it — summarise the " | |
| "signals in plain prose. Never write code to go fetch what you were already handed.\n" | |
| "2. Write code only when asked to write code. Then it is complete and working, no placeholders.\n" | |
| "3. Never echo, quote, or paste the context block back to the user. It is your input, not your " | |
| "output. Cite a signal by name if you need to attribute something.\n" | |
| "4. If the request is to build or wire something and you are missing what you'd need to build " | |
| "it, say what you can do in one line, then ask only the questions whose answers change the " | |
| "build. No preamble, no options essay.\n" | |
| "5. You may open with a short signal tag for the dominant matched node (e.g. '▶ L1 · Python " | |
| "Pro') when a specialist lens actually applies. Otherwise skip it." | |
| ) | |
| # name -> (default_base_url, key_env, model_env, default_model) | |
| _CHAT_PROVIDERS = { | |
| "groq": ("https://api.groq.com/openai/v1", "GROQ_API_KEY", "GROQ_MODEL", "llama-3.1-8b-instant"), | |
| "gemini": ("https://generativelanguage.googleapis.com/v1beta/openai", "GEMINI_API_KEY", "GEMINI_MODEL", "gemini-2.0-flash"), | |
| "opencode_zen": ("https://opencode.ai/zen/v1", "OPENCODE_ZEN_API_KEY", "OPENCODE_ZEN_MODEL", "glm-4.6"), | |
| "huggingface": ("https://router.huggingface.co/v1", "HF_TOKEN", "HF_CHAT_MODEL", "meta-llama/Llama-3.1-8B-Instruct"), | |
| "xai": ("https://api.x.ai/v1", "XAI_API_KEY", "XAI_MODEL", "grok-2-latest"), | |
| } | |
| _CHAT_ORDER_DEFAULT = "huggingface,groq,gemini,opencode_zen,xai" | |
| def _chat_cfg(name: str): | |
| base, key_env, model_env, default_model = _CHAT_PROVIDERS[name] | |
| # .strip() everything: a secret pasted into the Space UI often carries a trailing | |
| # newline, and "moonshotai/Kimi-K3\n" is a 400 from the router, not a fallback. | |
| base = os.environ.get(f"{name.upper()}_BASE_URL", base).strip().rstrip("/") | |
| return base, os.environ.get(key_env, "").strip(), (os.environ.get(model_env) or default_model).strip() | |
| def _chat_order() -> List[str]: | |
| raw = os.environ.get("CHAT_PROVIDER_ORDER", _CHAT_ORDER_DEFAULT) | |
| return [p.strip() for p in raw.split(",") if p.strip() in _CHAT_PROVIDERS] | |
| _CHAT_SESSIONS: Dict[str, List[dict]] = {} | |
| _CHAT_TOPIC: Dict[str, List[str]] = {} # last keywords that actually tuned | |
| _CHAT_ACTIVE: Dict[str, List[str]] = {} | |
| _CHAT_LOCK = threading.Lock() | |
| _CHAT_MAX_HISTORY = 16 | |
| _CHAT_STOP = {"the","and","for","that","with","this","you","your","are","can","what","how","why", | |
| "who","when","where","a","an","to","of","in","on","is","it","me","my","do","does", | |
| "i","we","be","as","at","or","if","so","but","not","get","let","now","then","please", | |
| "help","need","want","make","using","use","about"} | |
| _ROW_LETTERS = "ALIODQBGU" | |
| def _chat_keywords(text: str, limit: int = 8) -> List[str]: | |
| out, seen = [], set() | |
| for t in re.split(r"[^a-zA-Z0-9_+#-]+", (text or "").lower()): | |
| if len(t) >= 3 and t not in _CHAT_STOP and t not in seen: | |
| seen.add(t); out.append(t) | |
| if len(out) >= limit: | |
| break | |
| return out | |
| def _freq_coord(freq: str) -> str: | |
| idx = _node_idx(freq); r, c = divmod(idx, _COLS) | |
| return f"{_ROW_LETTERS[r]}{c+1}" if 0 <= r < len(_ROW_LETTERS) else f"N{idx}" | |
| def _render_signal(content: Any) -> str: | |
| """Feed rows arrive as dicts; a raw repr is unreadable and the model echoes it back.""" | |
| if isinstance(content, dict): | |
| title = content.get("title") or content.get("name") or content.get("summary") or "" | |
| link = content.get("link") or content.get("url") or "" | |
| if title: | |
| return f"{str(title)[:200]}{' — ' + link if link else ''}" | |
| content = json.dumps(content, separators=(",", ":")) | |
| return str(content or "")[:300] | |
| def _mesh_hydrate(keywords: List[str], role: Optional[str] = None, max_signals: int = 12): | |
| """One tune_in pass -> (context_str, [grid_coords]). Capped to keep the prompt lean.""" | |
| try: | |
| signals = signal_registry.tune_in(keywords) | |
| if role: | |
| signals = relationship_graph.filter_signals(role, signals) | |
| except Exception: | |
| signals = [] | |
| signals = signals[:max_signals] | |
| lines, coords = [], [] | |
| if signals: | |
| lines.append("[SignalMesh — Live Context]") | |
| for s in signals: | |
| nm = s.get("name", "?") | |
| # the frequency name is what the user can act on; A1-style cells are for the canvas | |
| if nm not in coords: | |
| coords.append(nm) | |
| lines.append(f"• [{nm}] {_render_signal(s.get('content'))}") | |
| return "\n".join(lines)[:4000], coords | |
| async def _chat_one(client, base, key, model, messages, max_tokens, temperature): | |
| r = await client.post(base.rstrip("/") + "/chat/completions", | |
| headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"}, | |
| json={"model": model, "messages": messages, "max_tokens": max_tokens, "temperature": temperature}) | |
| if r.status_code != 200: | |
| raise RuntimeError(f"HTTP {r.status_code}: {r.text[:160]}") | |
| msg = ((r.json().get("choices") or [{}])[0].get("message") or {}) | |
| txt = (msg.get("content") or "").strip() | |
| if not txt: | |
| raise RuntimeError("empty completion") | |
| return txt | |
| async def _chat_complete(messages, max_tokens=1024, temperature=0.6): | |
| errors = [] | |
| async with httpx.AsyncClient(timeout=45.0) as client: | |
| for name in _chat_order(): | |
| base, key, model = _chat_cfg(name) | |
| if not key: | |
| errors.append(f"{name}: skipped, no key set") # never fail silently | |
| continue | |
| try: | |
| return (await _chat_one(client, base, key, model, messages, max_tokens, temperature), | |
| f"{name}:{model}", errors) | |
| except Exception as e: | |
| errors.append(f"{name}: {str(e)[:120]}") | |
| return None, "none", errors | |
| def _live_frequencies(term: str) -> List[str]: | |
| stem = re.sub(r"^(www\.|https?://)|\.(com|io|ai|org|net)$", "", term.lower()).split(".")[0] | |
| return [n for n, buf in signal_registry.streams.items() if buf and stem and stem in n.lower()] | |
| _ASKING_ABOUT = re.compile( | |
| r"^\s*(?:tell me about|what(?:'?s| is| are)|who(?:'?s| is)|explain|describe)\s+(.{2,60}?)\s*[?.!]*\s*$", | |
| re.I) | |
| _PRONOUNS = {"it", "that", "this", "they", "them", "he", "she", "these", "those", "one"} | |
| def _empty_mesh_reply(msg: str, context: str, history: list) -> Optional[str]: | |
| """A question about a named thing with nothing in the mesh is answered by the mesh, | |
| not by the model — that is the path that invented a Meta product called NexusAI. | |
| Build/code requests fall through untouched; they were never mesh lookups.""" | |
| m = _ASKING_ABOUT.match(msg) | |
| if context or not m or history: | |
| return None # mid-thread, the subject is the thread — never gate a follow-up | |
| subject = m.group(1).strip() | |
| head = re.split(r"[^a-z0-9]+", subject.lower())[0] if subject else "" | |
| if not head or head in _PRONOUNS: | |
| return None # "who is it for" names nothing to look up | |
| near = sorted({n for n in signal_registry.streams | |
| if signal_registry.streams[n] and n.split(".")[0].lower()[:4] in msg.lower()}) | |
| out = [f"The mesh is carrying nothing on {subject} — so I'm not answering from my own " | |
| f"training data and calling it live."] | |
| if near: | |
| out.append("\nClosest frequencies: " + ", ".join(near[:6])) | |
| host = os.environ.get("SIGNALMESH_PUBLIC_URL") or ( | |
| f"https://{os.environ['SPACE_HOST']}" if os.environ.get("SPACE_HOST") else "") | |
| out.append(f"\nTo make this answerable, broadcast it:\n" | |
| f' curl -X POST {host}/ui/broadcast \\\n' | |
| f' -H "Content-Type: application/json" \\\n' | |
| f' -d \'{{"frequency":"<name>","content":"…"}}\'\n' | |
| f"or add it to SITE_INGEST as <frequency>=<url> and it re-hydrates on every boot.") | |
| return "\n".join(out) | |
| def _fast_reply(msg: str) -> Optional[str]: | |
| """Deterministic answers for requests whose shape we already know. | |
| An antenna is built from (frequency, target, name) against a frequency that | |
| already carries signals — so the missing pieces are knowable without a model. | |
| """ | |
| m = re.search(r"\bantenna\b[^.?!]{0,30}?\bfor\b\s+([^\s,.?!]+)", msg, re.I) | |
| if not m: | |
| return None | |
| term = m.group(1).strip() | |
| live = _live_frequencies(term) | |
| out = [f"On it — antenna for {term}."] | |
| if live: | |
| out.append("\nCarrying signals now: " + ", ".join(sorted(live)[:8])) | |
| out.append("\nTwo things I need:") | |
| out.append("1. Which frequency above should it receive?") | |
| else: | |
| out.append(f"\nNothing in the mesh is carrying {term} yet — an antenna is shaped against " | |
| "live signals, so that frequency has to be broadcast first.") | |
| out.append("\nTwo things I need:") | |
| out.append("1. What should be broadcast onto it — docs, API responses, a feed?") | |
| out.append("2. Where does it materialize? " + " · ".join(ANTENNA_TARGETS)) | |
| out.append("\n nextjs_route → an API route in their app · shell → a curl/CLI receiver") | |
| out.append(" system_prompt → context injected into an agent · html_embed → a drop-in widget") | |
| out.append("\nAnswer those two and I'll emit the wrapper.") | |
| return "\n".join(out) | |
| class ChatReq(BaseModel): | |
| session_id: str = "" | |
| message: str | |
| role: Optional[str] = None | |
| keywords: Optional[List[str]] = None | |
| max_tokens: int = 1024 | |
| temperature: float = 0.6 | |
| class ChatResetReq(BaseModel): | |
| session_id: str = "" | |
| async def api_chat(req: ChatReq, x_signalmesh_key: Optional[str] = Header(default=None)): | |
| _check_key(x_signalmesh_key) | |
| return await _do_chat(req) | |
| async def ui_chat(req: ChatReq): | |
| """Keyless, like the rest of /ui/*. The mesh is the demo; the model runs on our keys.""" | |
| return await _do_chat(req) | |
| def ui_chat_reset(req: ChatResetReq): | |
| sid = (req.session_id or "anon").strip() | |
| with _CHAT_LOCK: | |
| _CHAT_SESSIONS.pop(sid, None); _CHAT_ACTIVE.pop(sid, None); _CHAT_TOPIC.pop(sid, None) | |
| return {"status": "reset", "session_id": sid} | |
| async def _do_chat(req: ChatReq): | |
| msg = (req.message or "").strip() | |
| if not msg: | |
| raise HTTPException(400, "message is required") | |
| sid = (req.session_id or "anon").strip() | |
| kws = req.keywords or _chat_keywords(msg) | |
| context, coords = _mesh_hydrate(kws, req.role) | |
| with _CHAT_LOCK: | |
| history = list(_CHAT_SESSIONS.get(sid, [])) | |
| prev = _CHAT_ACTIVE.get(sid, []) | |
| carried = list(_CHAT_TOPIC.get(sid, [])) | |
| # A follow-up ("who is it for", "what about pricing") carries no routable keyword of | |
| # its own. Stay tuned to whatever the thread is already about instead of going deaf. | |
| if not context and carried: | |
| context, coords = _mesh_hydrate(carried, req.role) | |
| kws = carried | |
| elif context: | |
| with _CHAT_LOCK: | |
| _CHAT_TOPIC[sid] = kws | |
| sys_msg = _CHAT_SYSTEM + ("\n\n" + context if context else | |
| "\n\n[SignalMesh — no live signals matched this query.]\n" | |
| "Say so in your first line — the mesh is carrying nothing on this. Anything you add " | |
| "after that is your own training data, not mesh knowledge: label it as unverified and " | |
| "keep it short. Never present it as what the mesh knows, and never invent specifics " | |
| "(dates, versions, features, pricing) about a named product you have no signals for.") | |
| messages = ([{"role": "system", "content": sys_msg}] | |
| + history[-_CHAT_MAX_HISTORY:] | |
| + [{"role": "user", "content": msg}]) | |
| fast = _fast_reply(msg) or _empty_mesh_reply(msg, context, history) | |
| if fast: | |
| reply, provider, errors = fast, "mesh", [] | |
| else: | |
| reply, provider, errors = await _chat_complete(messages, req.max_tokens, req.temperature) | |
| if reply is None: | |
| reply = ("⚠ No language provider answered. What each one said:\n\n" | |
| + "\n".join(f"· {e}" for e in errors) if errors else | |
| "⚠ No language provider is configured on the mesh. Set at least one of " | |
| "GROQ_API_KEY, GEMINI_API_KEY, OPENCODE_ZEN_API_KEY, HF_TOKEN, or XAI_API_KEY " | |
| "as a Space secret to enable chat.") | |
| provider = "none" | |
| with _CHAT_LOCK: | |
| h = _CHAT_SESSIONS.get(sid, []) | |
| h.append({"role": "user", "content": msg}) | |
| if provider != "none": | |
| h.append({"role": "assistant", "content": reply}) | |
| _CHAT_SESSIONS[sid] = h[-_CHAT_MAX_HISTORY:] | |
| _CHAT_ACTIVE[sid] = coords | |
| hist_len = sum(1 for m in _CHAT_SESSIONS[sid] if m["role"] == "user") | |
| new_sigs = [c for c in coords if c not in prev] | |
| removed = [c for c in prev if c not in coords] | |
| return { | |
| "reply": reply, "provider": provider, | |
| "active_signals": coords, "new_signals": new_sigs, "removed_signals": removed, | |
| "history_len": hist_len, | |
| "trail": [], "had_reask": False, "fuzzy_acquired": [], | |
| "seek_needed": False, "seek_gaps": [], "seek_prompt": "", | |
| "provider_errors": errors, | |
| } | |
| def api_chat_reset(req: ChatResetReq, x_signalmesh_key: Optional[str] = Header(default=None)): | |
| _check_key(x_signalmesh_key) | |
| sid = (req.session_id or "anon").strip() | |
| with _CHAT_LOCK: | |
| _CHAT_SESSIONS.pop(sid, None); _CHAT_ACTIVE.pop(sid, None); _CHAT_TOPIC.pop(sid, None) | |
| return {"status": "reset", "session_id": sid} | |
| def api_chat_session(session_id: str, x_signalmesh_key: Optional[str] = Header(default=None)): | |
| _check_key(x_signalmesh_key) | |
| with _CHAT_LOCK: | |
| h = _CHAT_SESSIONS.get(session_id, []) | |
| hist_len = sum(1 for m in h if m["role"] == "user") | |
| return {"session_id": session_id, "history_len": hist_len, | |
| "active_signals": _CHAT_ACTIVE.get(session_id, []), "signal_detail": []} | |
| _CHAT_HTML = r"""<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>SignalMesh · Live Chat</title> | |
| <style> | |
| *{margin:0;padding:0;box-sizing:border-box} | |
| :root{ | |
| --bg:#0c0e11; --panel:#14181d; --panel2:#1a1f26; | |
| --line:#242b33; --line2:#2f3742; | |
| --text:#e7e5df; --muted:#8b929c; --dim:#5b626c; | |
| --accent:#46d39a; --user:#1c2630; | |
| } | |
| html,body{height:100%} | |
| body{background:var(--bg);color:var(--text); | |
| font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif; | |
| display:flex;flex-direction:column;height:100vh;overflow:hidden} | |
| header{display:flex;align-items:center;gap:12px;padding:14px 20px;border-bottom:1px solid var(--line);background:var(--panel)} | |
| .logo{font-weight:700;letter-spacing:.3px;font-size:1.02rem} | |
| .logo .d{color:var(--accent)} | |
| .tag{color:var(--muted);font-size:.8rem} | |
| .live{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace} | |
| .live .p{width:8px;height:8px;border-radius:50%;background:var(--accent);animation:pulse 2s infinite} | |
| @keyframes pulse{0%{box-shadow:0 0 0 0 rgba(70,211,154,.5)}70%{box-shadow:0 0 0 7px rgba(70,211,154,0)}100%{box-shadow:0 0 0 0 rgba(70,211,154,0)}} | |
| #scroll{flex:1;overflow-y:auto;padding:22px 0} | |
| #wrap{max-width:760px;margin:0 auto;padding:0 20px;display:flex;flex-direction:column;gap:16px} | |
| #intro{margin-top:6vh;text-align:center;color:var(--muted)} | |
| #intro h1{color:var(--text);font-size:1.5rem;font-weight:650;margin-bottom:8px} | |
| #intro p{font-size:.92rem;line-height:1.6;max-width:450px;margin:0 auto 20px} | |
| .chips{display:flex;flex-wrap:wrap;gap:8px;justify-content:center} | |
| .chip{background:var(--panel);border:1px solid var(--line2);color:var(--text);padding:9px 14px;border-radius:20px;font-size:.82rem;cursor:pointer;transition:.15s} | |
| .chip:hover{border-color:var(--accent);color:var(--accent)} | |
| .msg{display:flex;flex-direction:column;gap:5px} | |
| .msg.user{align-items:flex-end} | |
| .role{font-size:.7rem;color:var(--dim);font-family:ui-monospace,monospace;letter-spacing:.04em} | |
| .bubble{padding:13px 16px;border-radius:13px;font-size:.93rem;line-height:1.68;max-width:86%;word-wrap:break-word;overflow-wrap:anywhere} | |
| .msg.user .bubble{background:var(--user);border:1px solid var(--line2);border-bottom-right-radius:4px} | |
| .msg.bot .bubble{background:var(--panel);border:1px solid var(--line);border-bottom-left-radius:4px} | |
| .bubble pre{background:#0a0c0f;border:1px solid var(--line);border-radius:8px;padding:12px;margin:8px 0;overflow-x:auto;font-size:.84rem} | |
| .bubble code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.86em} | |
| .bubble :not(pre)>code{background:#0a0c0f;border:1px solid var(--line);border-radius:5px;padding:1px 5px} | |
| .bubble a{color:var(--accent);text-decoration:none;border-bottom:1px solid rgba(70,211,154,.35)} | |
| .foot{font-size:.68rem;color:var(--dim);font-family:ui-monospace,monospace} | |
| .foot b{color:var(--muted);font-weight:500} | |
| .typing{display:inline-flex;gap:4px;padding:14px 16px;background:var(--panel);border:1px solid var(--line);border-radius:13px;border-bottom-left-radius:4px} | |
| .typing i{width:6px;height:6px;border-radius:50%;background:var(--muted);animation:wave 1s infinite} | |
| .typing i:nth-child(2){animation-delay:.15s}.typing i:nth-child(3){animation-delay:.3s} | |
| @keyframes wave{0%,60%,100%{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-4px)}} | |
| #composer{border-top:1px solid var(--line);background:var(--panel);padding:14px 20px} | |
| #cform{max-width:760px;margin:0 auto;display:flex;gap:10px;align-items:flex-end;background:var(--panel2);border:1px solid var(--line2);border-radius:14px;padding:8px 8px 8px 14px;transition:.15s} | |
| #cform:focus-within{border-color:var(--accent)} | |
| #input{flex:1;background:none;border:none;outline:none;color:var(--text);font:inherit;font-size:.95rem;resize:none;max-height:140px;line-height:1.5;padding:6px 0} | |
| #send{flex-shrink:0;background:var(--accent);color:#06291c;border:none;border-radius:10px;width:40px;height:40px;font-size:1.15rem;cursor:pointer;font-weight:700;display:flex;align-items:center;justify-content:center} | |
| #send:disabled{opacity:.4;cursor:not-allowed} | |
| .hint{max-width:760px;margin:8px auto 0;text-align:center;font-size:.68rem;color:var(--dim)} | |
| @media(max-width:560px){.bubble{max-width:92%}#intro{margin-top:4vh}.tag{display:none}} | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <span class="logo">📡 Signal<span class="d">Mesh</span></span> | |
| <span class="tag">Sovereign Liquid Matrix</span> | |
| <span class="live"><span class="p"></span> live</span> | |
| </header> | |
| <div id="scroll"><div id="wrap"> | |
| <div id="intro"> | |
| <h1>Ask the mesh</h1> | |
| <p>Live ambient context from Reddit, Hacker News, arXiv, HuggingFace and more — answered in real time.</p> | |
| <div class="chips"> | |
| <div class="chip" data-q="What's trending on r/MachineLearning right now?">r/MachineLearning trends</div> | |
| <div class="chip" data-q="What's hot on Reddit's r/popular today?">Reddit trending today</div> | |
| <div class="chip" data-q="What's on the front page of Hacker News right now?">Hacker News front page</div> | |
| <div class="chip" data-q="What are the latest AI papers on arXiv?">Latest arXiv AI papers</div> | |
| </div> | |
| </div> | |
| </div></div> | |
| <div id="composer"> | |
| <form id="cform"> | |
| <textarea id="input" rows="1" placeholder="Ask about trending Reddit, a subreddit, Hacker News, arXiv..." autofocus></textarea> | |
| <button id="send" type="submit" title="Send">↑</button> | |
| </form> | |
| <div class="hint">SignalMesh answers from live mesh signals · responses may vary</div> | |
| </div> | |
| <script> | |
| const API='/ui/chat'; | |
| let SID=localStorage.getItem('sm_chat_sid'); | |
| if(!SID){SID='web-'+Math.random().toString(36).slice(2,11);localStorage.setItem('sm_chat_sid',SID);} | |
| const scroll=document.getElementById('scroll'),wrap=document.getElementById('wrap'),intro=document.getElementById('intro'),input=document.getElementById('input'),form=document.getElementById('cform'),sendBtn=document.getElementById('send'); | |
| function esc(s){return (s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');} | |
| function md(t){ | |
| t=esc(t); | |
| t=t.replace(/```(\w+)?\n?([\s\S]*?)```/g,function(m,l,c){return '<pre><code>'+c.replace(/\n+$/,'')+'</code></pre>';}); | |
| t=t.replace(/`([^`\n]+)`/g,'<code>$1</code>'); | |
| t=t.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>'); | |
| t=t.replace(/(https?:\/\/[^\s<)]+)/g,'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>'); | |
| t=t.replace(/\n/g,'<br>'); | |
| return t; | |
| } | |
| function down(){scroll.scrollTop=scroll.scrollHeight;} | |
| function addUser(text){if(intro&&intro.parentNode)intro.remove();const d=document.createElement('div');d.className='msg user';d.innerHTML='<div class="role">you</div><div class="bubble"></div>';d.querySelector('.bubble').textContent=text;wrap.appendChild(d);down();} | |
| function addTyping(){const d=document.createElement('div');d.className='msg bot';d.innerHTML='<div class="role">signalmesh</div><div class="typing"><i></i><i></i><i></i></div>';wrap.appendChild(d);down();return d;} | |
| function addBot(reply,provider,signals){const d=document.createElement('div');d.className='msg bot';let foot='';const prov=provider&&provider!=='none'?provider.split(':')[0]:'';const sig=(signals&&signals.length)?' · signals: '+signals.slice(0,6).join(', '):'';if(prov)foot='<div class="foot"><b>● live mesh</b> · '+esc(prov)+sig+'</div>';d.innerHTML='<div class="role">signalmesh</div><div class="bubble">'+md(reply)+'</div>'+foot;wrap.appendChild(d);down();} | |
| let busy=false; | |
| async function ask(text,isRetry){ | |
| const tr=addTyping(); | |
| try{ | |
| const res=await fetch(API,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({session_id:SID,message:text})}); | |
| const data=await res.json(); | |
| tr.remove(); | |
| if((!data.reply||data.provider==='none')&&!isRetry){await new Promise(r=>setTimeout(r,1600));return ask(text,true);} | |
| addBot(data.reply||'The mesh is busy right now — please try again in a moment.',data.provider,data.active_signals); | |
| }catch(e){tr.remove();addBot('Connection hiccup reaching the mesh — please try again.',null,null);} | |
| } | |
| async function submit(text){text=(text||'').trim();if(!text||busy)return;busy=true;sendBtn.disabled=true;input.value='';input.style.height='auto';addUser(text);await ask(text,false);busy=false;sendBtn.disabled=false;input.focus();} | |
| form.addEventListener('submit',function(e){e.preventDefault();submit(input.value);}); | |
| input.addEventListener('keydown',function(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();submit(input.value);}}); | |
| input.addEventListener('input',function(){input.style.height='auto';input.style.height=Math.min(input.scrollHeight,140)+'px';}); | |
| document.querySelectorAll('.chip').forEach(function(c){c.addEventListener('click',function(){submit(c.getAttribute('data-q'));});}); | |
| </script> | |
| </body> | |
| </html>""" | |
| def chat_ui(): | |
| return _CHAT_HTML | |
| if __name__ == "__main__": | |
| import uvicorn | |
| port = int(os.environ.get("PORT", os.environ.get("SIGNALMESH_PORT", 7475))) | |
| print(f"\n📡 SignalMesh — Sovereign Liquid Matrix Gateway v2.0") | |
| print(f" http://0.0.0.0:{port} → /docs → /redoc") | |
| print(f" UI routes: /ui/* (no key) | API routes: /api/* (X-SignalMesh-Key)") | |
| print(f" Set SIGNALMESH_API_KEY env var to change the key\n") | |
| uvicorn.run("signalmesh_api:app", host="0.0.0.0", port=port, reload=False) | |