| |
| """FSI_FELON Mesh Repo — P2P Git-like module |
| |
| Commands: |
| mesh init <name> Initialize a repo |
| mesh publish <name> Archive and announce to mesh |
| mesh clone <node>/<r> Clone a remote repo |
| mesh pull <node>/<r> Pull latest version |
| mesh push <name> Push new version (alias for publish) |
| mesh search <query> Search repos across mesh |
| mesh fork <node>/<r> Fork a repo |
| mesh log <name> Show version history |
| """ |
| import os, sys, json, time, hashlib, tarfile, shutil, threading |
| from http.server import HTTPServer, BaseHTTPRequestHandler |
|
|
| MESH_DB = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mesh_db.json") |
| NODE_ID = None |
|
|
| def _set_node_id(nid): |
| global NODE_ID |
| NODE_ID = nid |
|
|
| def _load_db(): |
| if os.path.exists(MESH_DB): |
| with open(MESH_DB) as f: |
| return json.load(f) |
| return {"shared": [], "peers": [], "repos": {}} |
|
|
| def _save_db(db): |
| with open(MESH_DB, "w") as f: |
| json.dump(db, f, indent=2) |
|
|
| class MeshRepo: |
| def __init__(self, workspace=None): |
| if workspace is None: |
| workspace = os.path.join(os.path.dirname(os.path.abspath(__file__)), "workspace") |
| self.workspace = workspace |
| os.makedirs(workspace, exist_ok=True) |
| self._peer_server = None |
|
|
| def init(self, name): |
| path = os.path.join(self.workspace, name) |
| if os.path.exists(os.path.join(path, ".mesh", "manifest.json")): |
| return {"error": f"Repo '{name}' already exists"} |
| os.makedirs(path, exist_ok=True) |
| mesh_dir = os.path.join(path, ".mesh") |
| os.makedirs(os.path.join(mesh_dir, "versions"), exist_ok=True) |
| manifest = { |
| "name": name, |
| "description": "", |
| "version": 0, |
| "node_id": NODE_ID or "unknown", |
| "created": int(time.time()), |
| "updated": int(time.time()), |
| "tags": [], |
| "license": "MIT", |
| "origin": None, |
| "versions": [], |
| } |
| with open(os.path.join(mesh_dir, "manifest.json"), "w") as f: |
| json.dump(manifest, f, indent=2) |
| db = _load_db() |
| db.setdefault("repos", {})[name] = {"path": path, "node_id": NODE_ID or "local", "manifest": manifest} |
| _save_db(db) |
| return {"success": True, "name": name, "path": path} |
|
|
| def publish(self, name): |
| path = os.path.join(self.workspace, name) |
| mf_path = os.path.join(path, ".mesh", "manifest.json") |
| if not os.path.exists(mf_path): |
| return {"error": f"Repo '{name}' not initialized. Run 'mesh init {name}' first"} |
| manifest = json.load(open(mf_path)) |
| version = manifest["version"] + 1 |
| archive_name = f"v{version}.tar.gz" |
| archive_path = os.path.join(path, ".mesh", "versions", archive_name) |
| with tarfile.open(archive_path, "w:gz") as tar: |
| for root, dirs, files in os.walk(path): |
| parts = root.replace(path, "").split(os.sep) |
| if ".mesh" in parts: |
| dirs[:] = [] |
| continue |
| for f in files: |
| fp = os.path.join(root, f) |
| tar.add(fp, arcname=os.path.relpath(fp, path)) |
| manifest["version"] = version |
| manifest["updated"] = int(time.time()) |
| manifest["versions"].append({ |
| "version": version, |
| "hash": hashlib.sha256(open(archive_path, "rb").read()).hexdigest()[:16], |
| "timestamp": int(time.time()), |
| "size": os.path.getsize(archive_path), |
| }) |
| with open(mf_path, "w") as f: |
| json.dump(manifest, f, indent=2) |
| db = _load_db() |
| db.setdefault("repos", {})[name] = {"path": path, "node_id": NODE_ID or "local", "manifest": manifest} |
| _save_db(db) |
| return {"success": True, "name": name, "version": version, "archive": archive_path, "size": os.path.getsize(archive_path)} |
|
|
| def clone(self, source): |
| parts = source.split("/") |
| if len(parts) != 2: |
| return {"error": "Format: node_id/repo_name"} |
| node_id, repo_name = parts |
| db = _load_db() |
| repo = db.get("repos", {}).get(repo_name) |
| if repo and repo["node_id"] == node_id: |
| dst = os.path.join(self.workspace, repo_name) |
| if os.path.exists(dst): |
| return {"error": f"'{repo_name}' already exists in workspace"} |
| shutil.copytree(repo["path"], dst) |
| manifest = json.load(open(os.path.join(dst, ".mesh", "manifest.json"))) |
| return {"success": True, "name": repo_name, "version": manifest["version"], "path": dst} |
| return {"error": f"Repo '{repo_name}' not found on node '{node_id}'"} |
|
|
| def pull(self, source): |
| parts = source.split("/") |
| if len(parts) != 2: |
| return {"error": "Format: node_id/repo_name"} |
| node_id, repo_name = parts |
| path = os.path.join(self.workspace, repo_name) |
| if not os.path.exists(os.path.join(path, ".mesh", "manifest.json")): |
| return {"error": f"'{repo_name}' not found locally. Clone first."} |
| db = _load_db() |
| remote = db.get("repos", {}).get(repo_name) |
| if not remote: |
| return {"error": f"Remote repo '{repo_name}' not found on mesh"} |
| local_mf = json.load(open(os.path.join(path, ".mesh", "manifest.json"))) |
| remote_mf = remote["manifest"] |
| if remote_mf["version"] <= local_mf["version"]: |
| return {"success": True, "message": "Already up to date", "version": local_mf["version"]} |
| |
| for v in remote_mf["versions"]: |
| if v["version"] > local_mf["version"]: |
| src = os.path.join(remote["path"], ".mesh", "versions", f"v{v['version']}.tar.gz") |
| dst_dir = os.path.join(path, ".mesh", "versions") |
| os.makedirs(dst_dir, exist_ok=True) |
| shutil.copy(src, os.path.join(dst_dir, f"v{v['version']}.tar.gz")) |
| with tarfile.open(src, "r:gz") as tar: |
| tar.extractall(path) |
| |
| local_mf["version"] = remote_mf["version"] |
| local_mf["updated"] = int(time.time()) |
| local_mf["versions"] = remote_mf["versions"] |
| with open(os.path.join(path, ".mesh", "manifest.json"), "w") as f: |
| json.dump(local_mf, f, indent=2) |
| return {"success": True, "name": repo_name, "version": remote_mf["version"]} |
|
|
| def push(self, name): |
| return self.publish(name) |
|
|
| def search(self, query): |
| db = _load_db() |
| repos = db.get("repos", {}) |
| q = query.lower() |
| results = [] |
| for rname, rdata in repos.items(): |
| m = rdata.get("manifest", {}) |
| score = 0 |
| if q in rname.lower(): |
| score += 10 |
| if q in m.get("description", "").lower(): |
| score += 5 |
| for tag in m.get("tags", []): |
| if q in tag.lower(): |
| score += 3 |
| if score > 0: |
| results.append({ |
| "name": rname, |
| "node_id": rdata.get("node_id", ""), |
| "description": m.get("description", ""), |
| "version": m.get("version", 1), |
| "tags": m.get("tags", []), |
| "score": score, |
| }) |
| results.sort(key=lambda x: x["score"], reverse=True) |
| return {"results": results, "count": len(results)} |
|
|
| def fork(self, source): |
| result = self.clone(source) |
| if result.get("success"): |
| path = os.path.join(self.workspace, result["name"]) |
| mf = json.load(open(os.path.join(path, ".mesh", "manifest.json"))) |
| mf["origin"] = source |
| with open(os.path.join(path, ".mesh", "manifest.json"), "w") as f: |
| json.dump(mf, f, indent=2) |
| result["origin"] = source |
| return result |
|
|
| def log(self, name): |
| path = os.path.join(self.workspace, name, ".mesh", "manifest.json") |
| if not os.path.exists(path): |
| return {"error": f"Repo '{name}' not found"} |
| manifest = json.load(open(path)) |
| return { |
| "name": name, |
| "version_count": len(manifest.get("versions", [])), |
| "versions": manifest.get("versions", []), |
| "created": manifest.get("created"), |
| "updated": manifest.get("updated"), |
| "node_id": manifest.get("node_id"), |
| "origin": manifest.get("origin"), |
| } |
|
|
| def serve(self, port=9091): |
| if self._peer_server: |
| return {"port": port, "status": "already running"} |
| class _Handler(BaseHTTPRequestHandler): |
| repo = self |
| def do_GET(self): |
| |
| |
| parts = self.path.strip("/").split("/") |
| if len(parts) >= 2 and parts[0] == "repo": |
| name = parts[1] |
| path = os.path.join(self.repo.workspace, name, ".mesh", "manifest.json") |
| if os.path.exists(path): |
| if len(parts) == 2 or (len(parts) == 3 and parts[2] == "manifest"): |
| self.send_response(200) |
| self.send_header("Content-Type", "application/json") |
| self.end_headers() |
| self.wfile.write(open(path, "rb").read()) |
| return |
| elif len(parts) == 4 and parts[2] == "download": |
| vname = parts[3] |
| apath = os.path.join(self.repo.workspace, name, ".mesh", "versions", vname) |
| if os.path.exists(apath): |
| self.send_response(200) |
| self.send_header("Content-Type", "application/gzip") |
| self.end_headers() |
| self.wfile.write(open(apath, "rb").read()) |
| return |
| self.send_response(404) |
| self.end_headers() |
| def log_message(self, *a): |
| pass |
| try: |
| self._peer_server = HTTPServer(("0.0.0.0", port), _Handler) |
| t = threading.Thread(target=self._peer_server.serve_forever, daemon=True) |
| t.start() |
| return {"port": port, "status": "listening"} |
| except Exception as e: |
| return {"error": str(e)} |
|
|