Files
2026-07-13 12:55:23 +08:00

118 lines
3.6 KiB
Python

"""Fetch, cache, and merge the CLI-Anything registries (harness + public)."""
import json
import time
from pathlib import Path
import requests
REGISTRY_URL = "https://hkuds.github.io/CLI-Anything/registry.json"
PUBLIC_REGISTRY_URL = "https://hkuds.github.io/CLI-Anything/public_registry.json"
CACHE_DIR = Path.home() / ".cli-hub"
CACHE_FILE = CACHE_DIR / "registry_cache.json"
PUBLIC_CACHE_FILE = CACHE_DIR / "public_registry_cache.json"
CACHE_TTL = 3600 # 1 hour
def _ensure_cache_dir():
CACHE_DIR.mkdir(parents=True, exist_ok=True)
def _load_cached_data(cache_file):
"""Return cached registry data if the cache file is valid."""
if not cache_file.exists():
return None
try:
cached = json.loads(cache_file.read_text())
return cached["data"]
except (json.JSONDecodeError, KeyError):
return None
def _fetch_json(url, cache_file, force_refresh=False):
"""Fetch a JSON URL with local file caching."""
_ensure_cache_dir()
if not force_refresh and cache_file.exists():
try:
cached = json.loads(cache_file.read_text())
if time.time() - cached.get("_cached_at", 0) < CACHE_TTL:
return cached["data"]
except (json.JSONDecodeError, KeyError):
pass
try:
resp = requests.get(url, timeout=15)
resp.raise_for_status()
data = resp.json()
except (requests.RequestException, ValueError):
cached_data = _load_cached_data(cache_file)
if cached_data is not None:
return cached_data
raise
cache_payload = {"_cached_at": time.time(), "data": data}
cache_file.write_text(json.dumps(cache_payload, indent=2))
return data
def fetch_registry(force_refresh=False):
"""Fetch the harness registry.json."""
return _fetch_json(REGISTRY_URL, CACHE_FILE, force_refresh)
def fetch_public_registry(force_refresh=False):
"""Fetch the public CLI registry. Returns None on failure."""
try:
return _fetch_json(PUBLIC_REGISTRY_URL, PUBLIC_CACHE_FILE, force_refresh)
except Exception:
return None
def fetch_all_clis(force_refresh=False):
"""Fetch and merge both registries. Each CLI is tagged with _source."""
registry = fetch_registry(force_refresh)
all_clis = []
for cli in registry["clis"]:
entry = dict(cli)
entry["_source"] = "harness"
all_clis.append(entry)
public = fetch_public_registry(force_refresh)
if public:
for cli in public["clis"]:
entry = dict(cli)
entry["_source"] = "public"
all_clis.append(entry)
return all_clis
def get_cli(name, force_refresh=False):
"""Look up a CLI entry by name (case-insensitive) across both registries."""
name_lower = name.lower()
for cli in fetch_all_clis(force_refresh):
if cli["name"].lower() == name_lower:
return cli
return None
def search_clis(query, force_refresh=False):
"""Search CLIs by name, description, or category across both registries."""
query_lower = query.lower()
results = []
for cli in fetch_all_clis(force_refresh):
if (query_lower in cli["name"].lower()
or query_lower in cli["description"].lower()
or query_lower in cli.get("category", "").lower()
or query_lower in cli.get("display_name", "").lower()):
results.append(cli)
return results
def list_categories(force_refresh=False):
"""Return sorted list of unique categories across both registries."""
return sorted(set(cli.get("category", "uncategorized") for cli in fetch_all_clis(force_refresh)))