shango/shango-cli/shango_cli.py

191 lines
7.1 KiB
Python

#!/usr/bin/env python3
"""
shango_cli.py — Interface unifiée Shango
"""
import argparse, json, subprocess, sys
from pathlib import Path
SHANGO_DIR = Path("/root/.shango")
VERSION = "0.1.0-shango"
def cmd_status():
"""État du mesh Shango + Tailscale."""
print("═══ SHANGO MESH STATUS ═══")
print(f"Version: {VERSION}")
# Tailscale
try:
out = subprocess.check_output(["tailscale", "status"], text=True, timeout=5)
lines = out.strip().split("\n")
print(f"Tailscale peers: {len(lines)}")
for l in lines[:3]:
print(f" {l}")
if len(lines) > 3:
print(f" ... +{len(lines)-3} autres")
except:
print("Tailscale: NON CONNECTE")
# Shango state
state_file = SHANGO_DIR / "mesh_state.json"
if state_file.exists():
state = json.load(open(state_file))
print(f"Shango node: {state.get('node_id', 'unknown')}")
print(f"Health: {state.get('health', 0):.0%}")
print(f"Peers connus: {state.get('peers_count', 0)}")
else:
print("Shango: pas encore initialisé. Lance: shango-cli singularize")
# Services Ivoire
services = {
"hermes": "singularite_workspace:8080",
"erp": "odoo_web:8069",
"ai": "ollama:11434",
"chat": "open-webui:8080",
"status": "uptime-kuma:3001",
"git": "gitea:3000",
}
print("\nServices Ivoire:")
for name, addr in services.items():
url = f"https://{name}.ivoire-monade.shop"
print(f" {name:10}{url}")
def cmd_heal():
"""Auto-réparation Shango."""
sys.path.insert(0, "/root/shango/shango-daemon")
from shango_daemon import ShangoNode
node = ShangoNode()
result = node.heal()
print(json.dumps(result, indent=2))
def cmd_singularize():
"""Singularise les identités."""
sys.path.insert(0, "/root/shango/shango-daemon")
from shango_daemon import ShangoNode
node = ShangoNode()
result = node.singularize(["hermes", "erp", "ai", "status"])
print(json.dumps(result, indent=2))
print("\n[SINGULARIZE] Hermes + Odoo + Tailscale + ivoire-monade.shop unifiés.")
def cmd_mcp_scan(path: str):
"""Scan un projet pour générer un serveur MCP."""
sys.path.insert(0, "/root/shango/mcp-builder")
from mcp_builder import main as mcp_main
sys.argv = ["mcp-builder", path]
mcp_main()
def cmd_glm_check():
"""Vérifie les déps GLM training."""
sys.path.insert(0, "/root/shango/glm-trainer")
from glm_trainer import check_deps
check_deps()
def cmd_maieutic(subcmd: str = "ask", qid: str = None, choice: int = None, text: str = ""):
"""Moteur maieutique Socratique."""
sys.path.insert(0, "/root/shango/shango-maieutic")
from shango_maieutic import MaieuticEngine
engine = MaieuticEngine()
if subcmd == "ask":
result = engine.ask()
if result["status"] == "question":
print(f"\n[{result['dimension'].upper()}] {result['context']}")
print(f"\n? {result['question']}")
for i, opt in enumerate(result["options"]):
print(f" [{i}] {opt}")
print(f"\nOdù possibles: {', '.join(result['odu_map'])}")
print(f"\n{result['progress']}")
print(f"\nPour répondre: shango maieutic answer --qid {result['qid']} --choice <0-3>")
else:
print(result["message"])
elif subcmd == "answer":
if not qid or choice is None:
print("Usage: shango maieutic answer --qid <ID> --choice <0-3> [--text 'libre']")
return
result = engine.answer(qid, choice, text)
print(json.dumps(result, indent=2))
elif subcmd == "profile":
print(engine.get_profile_summary())
def cmd_forge(args):
"""Forge multi-runtime wrapper."""
sys.path.insert(0, "/root/shango/ivoire-forge")
from shango_forge import detect_runtime, build_docker, run_container, gitea_push, RUNTIMES
from pathlib import Path
src = Path(args.dir)
if args.subcmd == "detect":
rt = detect_runtime(src)
print(json.dumps({"runtime": rt, "source": str(src)}, indent=2))
elif args.subcmd == "build":
rt = args.runtime or detect_runtime(src)
if not rt:
print("Runtime non detecte. Utilise --runtime.")
return
tag = build_docker(src, rt, args.tag)
print(json.dumps({"image": tag, "runtime": rt}))
elif args.subcmd == "run":
ports = {}
for p in args.port: h, c = p.split(":"); ports[h] = c
envs = {}
for e in args.env: k, v = e.split("=", 1); envs[k] = v
cid = run_container(args.tag, args.name, ports, envs)
print(json.dumps({"container_id": cid, "name": args.name}))
elif args.subcmd == "gitea-push":
if not args.repo:
print("--repo obligatoire")
return
gitea_push(src, args.repo)
elif args.subcmd == "status":
import shutil
for name, meta in RUNTIMES.items():
ok = shutil.which(meta["cmd"]) is not None
print(f" {name:12} {'OK' if ok else 'ABSENT'} ({meta['docker']})")
def main():
parser = argparse.ArgumentParser(prog="shango", description="Shango Mesh CLI")
sub = parser.add_subparsers(dest="command")
sub.add_parser("status", help="État du mesh")
sub.add_parser("heal", help="Auto-réparer")
sub.add_parser("singularize", help="Unifier identités")
p_mcp = sub.add_parser("mcp-scan", help="Générer serveur MCP depuis code")
p_mcp.add_argument("path", nargs="?", default=".")
sub.add_parser("glm-check", help="Vérifier deps GLM")
p_mai = sub.add_parser("maieutic", help="Pose une question maïeutique")
p_mai.add_argument("subcmd", nargs="?", default="ask", choices=["ask", "answer", "profile"])
p_mai.add_argument("--qid", default=None)
p_mai.add_argument("--choice", type=int, default=None)
p_mai.add_argument("--text", default="")
p_forge = sub.add_parser("forge", help="Forge multi-runtime: build/run/detect/push")
p_forge.add_argument("subcmd", nargs="?", default="status", choices=["detect","build","run","gitea-push","status"])
p_forge.add_argument("--dir", default=".", help="Répertoire source")
p_forge.add_argument("--tag", default="shango/forge:latest")
p_forge.add_argument("--runtime", choices=["go","rust","node","python3","web3","pytorch","react"])
p_forge.add_argument("--name", default="forge-app")
p_forge.add_argument("--port", action="append", default=[], help="host:container")
p_forge.add_argument("--env", action="append", default=[], help="KEY=VAL")
p_forge.add_argument("--repo", default="")
args = parser.parse_args()
if args.command == "status":
cmd_status()
elif args.command == "heal":
cmd_heal()
elif args.command == "singularize":
cmd_singularize()
elif args.command == "mcp-scan":
cmd_mcp_scan(args.path)
elif args.command == "glm-check":
cmd_glm_check()
elif args.command == "maieutic":
cmd_maieutic(args.subcmd, args.qid, args.choice, args.text)
elif args.command == "forge":
cmd_forge(args)
else:
parser.print_help()
if __name__ == "__main__":
main()