#!/usr/bin/env python3 """lean-ctx native messaging bridge for Chrome. One-shot mode: reads a single JSON message from Chrome via stdin (length-prefixed), processes it, returns the result, then exits. """ import json import struct import subprocess import sys import os import traceback LOG_PATH = os.path.expanduser("~/.lean-ctx/bridge-debug.log") def log(msg): try: os.makedirs(os.path.dirname(LOG_PATH), exist_ok=True) with open(LOG_PATH, "a") as f: f.write(msg + "\n") except Exception: pass def find_lean_ctx(): for candidate in [ os.path.expanduser("~/.cargo/bin/lean-ctx"), "/usr/local/bin/lean-ctx", "/opt/homebrew/bin/lean-ctx", ]: if os.path.isfile(candidate) and os.access(candidate, os.X_OK): return candidate import shutil return shutil.which("lean-ctx") def read_message(): raw = sys.stdin.buffer.read(4) log(f"header bytes: {len(raw)}") if len(raw) < 4: return None length = struct.unpack("I", raw)[0] log(f"message length: {length}") if length > 1024 * 1024: return None data = sys.stdin.buffer.read(length) log(f"body bytes: {len(data)}") if len(data) < length: return None return json.loads(data.decode("utf-8")) def send_message(obj): encoded = json.dumps(obj, ensure_ascii=False).encode("utf-8") sys.stdout.buffer.write(struct.pack("I", len(encoded))) sys.stdout.buffer.write(encoded) sys.stdout.buffer.flush() log(f"sent: {len(encoded)} bytes") def compress(text, binary_path): try: env = os.environ.copy() env.pop("LEAN_CTX_ACTIVE", None) env.pop("LEAN_CTX_DISABLED", None) env["NO_COLOR"] = "1" result = subprocess.run( [binary_path, "-c", "cat"], input=text, capture_output=True, text=True, timeout=10, env=env, ) compressed = result.stdout.strip() if result.returncode == 0 else text except (subprocess.TimeoutExpired, FileNotFoundError) as e: log(f"compress error: {e}") compressed = text input_tokens = len(text) // 4 output_tokens = len(compressed) // 4 savings = ((input_tokens - output_tokens) / max(input_tokens, 1)) * 100 return { "compressed": compressed, "inputTokens": input_tokens, "outputTokens": output_tokens, "savings": round(savings, 1), } def main(): log("--- bridge started ---") try: msg = read_message() if msg is None: log("no input received") send_message({"error": "no input"}) return binary = find_lean_ctx() action = msg.get("action", "") log(f"action: {action}, binary: {binary}") if action == "ping": send_message({"status": "ok", "binary": binary or "not found"}) elif action == "compress": text = msg.get("text", "") if binary and text: send_message(compress(text, binary)) else: send_message({"error": "lean-ctx not found or empty text"}) else: send_message({"error": f"unknown action: {action}"}) except Exception as e: log(f"EXCEPTION: {traceback.format_exc()}") try: send_message({"error": str(e)}) except Exception: pass if __name__ == "__main__": main()