191 lines
7.1 KiB
Python
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()
|