add moveing of tabs and groups, multi browser support, auto complite into terminal, extract html and adding testing

This commit is contained in:
2026-04-09 01:41:01 +02:00
parent 0cb2f1cb3f
commit ab4ba97886
19 changed files with 1069 additions and 57 deletions
+71 -15
View File
@@ -5,6 +5,10 @@ Native Messaging Host for browser-cli.
Chrome launches this process when the extension calls connectNative().
It relays messages between the Chrome extension (via stdin/stdout using the
Native Messaging protocol) and the CLI (via a Unix domain socket).
Multi-browser support: the extension sends a "hello" message on startup
with a profile alias. The host uses that alias to create a unique socket
path and registers it in a shared registry file.
"""
import json
import os
@@ -16,7 +20,10 @@ import threading
import uuid
from pathlib import Path
SOCKET_PATH = "/tmp/browser-cli.sock"
REGISTRY_PATH = Path("/tmp/browser-cli-registry.json")
DEFAULT_ALIAS = "default"
SOCKET_PATH: str = "" # set after hello handshake
PENDING: dict[str, queue.Queue] = {}
PENDING_LOCK = threading.Lock()
@@ -40,16 +47,48 @@ def write_native_message(stream, msg: dict) -> None:
stream.flush()
# --- Registry helpers ---
def _registry_add(alias: str, sock_path: str) -> None:
try:
reg = json.loads(REGISTRY_PATH.read_text()) if REGISTRY_PATH.exists() else {}
reg[alias] = sock_path
REGISTRY_PATH.write_text(json.dumps(reg))
except Exception:
pass
def _registry_remove(alias: str) -> None:
try:
if not REGISTRY_PATH.exists():
return
reg = json.loads(REGISTRY_PATH.read_text())
reg.pop(alias, None)
REGISTRY_PATH.write_text(json.dumps(reg))
except Exception:
pass
def _socket_path_for(alias: str) -> str:
safe = alias.replace(" ", "_").replace("/", "_")
return f"/tmp/browser-cli-{safe}.sock"
# --- Thread A: read messages from extension (stdin) ---
def stdin_reader():
def stdin_reader(alias: str):
stdin = sys.stdin.buffer
while True:
msg = read_native_message(stdin)
if msg is None:
# Extension disconnected — clean up socket and exit
_cleanup()
# Extension disconnected — clean up and exit
_cleanup(alias)
os._exit(0)
# Profile alias handshake
if msg.get("type") == "hello":
continue # already handled during startup
msg_id = msg.get("id")
if msg_id:
with PENDING_LOCK:
@@ -60,13 +99,13 @@ def stdin_reader():
# --- Thread B: accept CLI socket connections ---
def socket_server():
path = Path(SOCKET_PATH)
def socket_server(sock_path: str):
path = Path(sock_path)
if path.exists():
path.unlink()
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind(SOCKET_PATH)
sock.bind(sock_path)
sock.listen(16)
while True:
@@ -92,10 +131,8 @@ def handle_cli_connection(conn: socket.socket) -> None:
with PENDING_LOCK:
PENDING[msg_id] = response_queue
# Forward command to extension via stdout
write_native_message(sys.stdout.buffer, cmd)
# Wait for extension's response (30 s timeout)
try:
result = response_queue.get(timeout=30)
except queue.Empty:
@@ -139,20 +176,39 @@ def _recv_exact(conn: socket.socket, n: int) -> bytes | None:
return buf
def _cleanup():
def _cleanup(alias: str):
try:
Path(SOCKET_PATH).unlink(missing_ok=True)
Path(_socket_path_for(alias)).unlink(missing_ok=True)
except Exception:
pass
_registry_remove(alias)
def main():
# Start socket server thread
t = threading.Thread(target=socket_server, daemon=True)
stdin = sys.stdin.buffer
# Wait for the hello handshake to learn the profile alias
first_msg = read_native_message(stdin)
if first_msg and first_msg.get("type") == "hello":
alias = first_msg.get("alias") or DEFAULT_ALIAS
else:
# No hello — fall back to default, re-queue message if it was a command
alias = DEFAULT_ALIAS
if first_msg:
msg_id = first_msg.get("id")
if msg_id:
q: queue.Queue = queue.Queue()
with PENDING_LOCK:
PENDING[msg_id] = q
write_native_message(sys.stdout.buffer, first_msg)
sock_path = _socket_path_for(alias)
_registry_add(alias, sock_path)
t = threading.Thread(target=socket_server, args=(sock_path,), daemon=True)
t.start()
# Read extension messages on main thread (blocks until extension disconnects)
stdin_reader()
stdin_reader(alias)
if __name__ == "__main__":