Simplify Athena operator architecture

This commit is contained in:
Mikei386
2026-08-25 21:53:12 +02:00
parent c25e57af57
commit 0a640e76ea
22 changed files with 577 additions and 1522 deletions
+51 -27
View File
@@ -29,9 +29,9 @@ from pathlib import Path
from typing import Any
VERSION = "2.3.4"
VERSION = "3.0.0"
STACK = Path(os.environ.get("ATHENA_OPERATOR_STACK", "/opt/mike-ai/stack")).resolve()
REPOSITORY = Path(os.environ.get("ATHENA_OPERATOR_REPOSITORY", "/data/mike-ai-operator/repository")).resolve()
REPOSITORY = Path(os.environ.get("ATHENA_OPERATOR_REPOSITORY", str(STACK))).resolve()
STATE = Path(os.environ.get("ATHENA_OPERATOR_STATE", "/data/mike-ai-operator/state")).resolve()
SOCKET = Path(os.environ.get("ATHENA_OPERATOR_SOCKET", "/run/mike-ai-operator/operator.sock"))
MODELS = Path(os.environ.get("ATHENA_OPERATOR_MODELS", "/data/models")).resolve()
@@ -145,6 +145,7 @@ def safe_relative(value: str) -> Path:
def source_file(root: Path, relative: Path) -> Path:
root = root.resolve(strict=False)
candidate = (root / relative).resolve(strict=False)
candidate.relative_to(root)
return candidate
@@ -250,15 +251,10 @@ def audit(event: str, **fields: Any) -> None:
def ensure_repository() -> None:
if not (REPOSITORY / ".git").is_dir():
bundle = Path("/data/mike-ai-recovery-kit/source.git.bundle")
if not bundle.is_file():
raise RuntimeError("operator repository missing and no recovery Git bundle is available")
REPOSITORY.parent.mkdir(parents=True, exist_ok=True)
run(["git", "clone", str(bundle), str(REPOSITORY)], cwd=Path("/data"), check=True)
current_file = STACK / ".mike-ai-source-commit"
current = current_file.read_text().strip() if current_file.is_file() else ""
if re.fullmatch(r"[0-9a-f]{40}", current):
run(["git", "switch", "-C", "main", current], cwd=REPOSITORY, check=True)
raise RuntimeError(
f"canonical Git checkout is missing at {REPOSITORY}; "
"restore /opt/mike-ai/stack with the documented recovery script"
)
remote = os.environ.get("ATHENA_OPERATOR_GIT_REMOTE", "").strip()
if remote:
run(["git", "remote", "set-url", "origin", remote], cwd=REPOSITORY, check=True)
@@ -271,12 +267,13 @@ def inspect(subject: str, arguments: dict[str, Any]) -> dict[str, Any]:
if subject == "overview":
return {
"version": VERSION,
"source_commit": (STACK / ".mike-ai-source-commit").read_text().strip(),
"source_commit": run(["git", "rev-parse", "HEAD"], cwd=REPOSITORY, check=True)["output"].strip(),
"git": run(["git", "status", "--short", "--branch"], cwd=REPOSITORY),
"containers": run(["docker", "ps", "--format", "{{.Names}}\t{{.Status}}\t{{.Image}}"]),
"storage": run(["df", "-h", "/", "/data", str(MODELS)]),
"gpus": run(["nvidia-smi", "--query-gpu=name,memory.total,memory.used,utilization.gpu", "--format=csv,noheader"]),
"boundary": "Athena AI-platform operator; no arbitrary shell, shutdown, reboot, SSH/network/firewall/kernel/driver/partition operations",
"layout": {"worktree": str(REPOSITORY), "runtime_stack": str(STACK), "single_worktree": REPOSITORY == STACK},
"boundary": "No shutdown, reboot or Athena SSH/network/firewall/kernel/partition/mount changes",
}
if subject == "git_status":
return {"status": run(["git", "status", "--short", "--branch"], cwd=REPOSITORY), "diff": run(["git", "diff", "--stat"], cwd=REPOSITORY)}
@@ -301,12 +298,18 @@ def read_source(arguments: dict[str, Any]) -> dict[str, Any]:
relative = safe_relative(str(arguments.get("path", "")))
target = source_file(REPOSITORY, relative)
if not target.is_file():
raise FileNotFoundError("source file not found")
return {"ok": False, "error": "source file not found", "path": str(relative), "retry": False}
text = target.read_text(encoding="utf-8", errors="replace")
start = max(1, int(arguments.get("start_line", 1)))
count = min(1000, max(1, int(arguments.get("line_count", 300))))
count = min(200, max(1, int(arguments.get("line_count", 80))))
lines = text.splitlines()
return {"path": str(relative), "sha256": sha(target.read_bytes()), "start_line": start, "content": "\n".join(lines[start - 1:start - 1 + count]), "total_lines": len(lines)}
selected = lines[start - 1:start - 1 + count]
return {
"ok": True, "path": str(relative), "sha256": sha(target.read_bytes()),
"start_line": start, "end_line": start + len(selected) - 1 if selected else start - 1,
"content": "\n".join(selected), "total_lines": len(lines),
"truncated": start - 1 + len(selected) < len(lines),
}
def search_source(arguments: dict[str, Any]) -> dict[str, Any]:
@@ -315,12 +318,10 @@ def search_source(arguments: dict[str, Any]) -> dict[str, Any]:
if not query or len(query) > 200 or any(x in query for x in ("\x00", "\n", "\r")):
raise ValueError("invalid query")
if shutil.which("rg"):
result = run(["rg", "-n", "--hidden", "--glob", "!.git/**", "--", query, "."], cwd=REPOSITORY, timeout=20)
return {"query": query, "matches": result["output"], "exit_code": result["exit_code"], "engine": "rg"}
try:
pattern = re.compile(query)
except re.error as exc:
raise ValueError(f"invalid search expression: {exc}") from exc
result = run(["rg", "-n", "-F", "-m", "20", "--hidden", "--glob", "!.git/**", "--", query, "."], cwd=REPOSITORY, timeout=20)
lines = result["output"].splitlines()[:20]
return {"ok": True, "query": query, "matches": lines, "count": len(lines), "truncated": len(lines) == 20, "engine": "rg"}
pattern = re.compile(re.escape(query))
matches: list[str] = []
for path in sorted(REPOSITORY.rglob("*")):
if not path.is_file() or ".git" in path.parts or path.stat().st_size > MAX_FILE_BYTES:
@@ -333,9 +334,9 @@ def search_source(arguments: dict[str, Any]) -> dict[str, Any]:
for number, line in enumerate(lines, 1):
if pattern.search(line):
matches.append(f"{relative}:{number}:{line}")
if len(matches) >= 200:
return {"query": query, "matches": "\n".join(matches), "exit_code": 0, "engine": "python", "truncated": True}
return {"query": query, "matches": "\n".join(matches), "exit_code": 0 if matches else 1, "engine": "python", "truncated": False}
if len(matches) >= 20:
return {"ok": True, "query": query, "matches": matches, "count": len(matches), "engine": "python", "truncated": True}
return {"ok": True, "query": query, "matches": matches, "count": len(matches), "engine": "python", "truncated": False}
def staged_file(item: dict[str, Any]) -> dict[str, Any]:
@@ -564,7 +565,7 @@ def prepare(arguments: dict[str, Any]) -> dict[str, Any]:
def sync_file(relative: Path, content: str, backup_root: Path) -> None:
for root in (REPOSITORY, STACK):
for root in dict.fromkeys((REPOSITORY, STACK)):
target = source_file(root, relative)
target_mode = stat.S_IMODE(target.stat().st_mode) if target.exists() else 0o644
if target.exists():
@@ -610,7 +611,7 @@ def apply_files(files: list[dict[str, Any]], backup: Path) -> list[str]:
def restore_files(files: list[dict[str, Any]], backup: Path) -> None:
for item in files:
relative = safe_relative(item["path"])
for root in (REPOSITORY, STACK):
for root in dict.fromkeys((REPOSITORY, STACK)):
target = source_file(root, relative)
saved = backup / root.name / relative
if saved.is_file():
@@ -789,6 +790,28 @@ def execute(arguments: dict[str, Any]) -> dict[str, Any]:
raise
def change(arguments: dict[str, Any]) -> dict[str, Any]:
"""Apply one user-requested operation without a second ticket ceremony."""
ensure_repository()
operation = str(arguments.get("operation", ""))
payload, preview = normalise_operation(operation, arguments.get("payload") or {})
change_id = uuid.uuid4().hex
audit("change_started", change=change_id, operation=operation)
try:
result = execute_operation(change_id, operation, payload)
except Exception:
audit("change_failed", change=change_id, operation=operation)
raise
audit("change_completed", change=change_id, operation=operation)
return {
"change_id": change_id,
"operation": operation,
"summary": compact(preview),
"result": result,
"instruction": "Verify the focused service behavior before declaring completion.",
}
def job(arguments: dict[str, Any]) -> dict[str, Any]:
job_id = str(arguments.get("job_id", ""))
if not re.fullmatch(r"[0-9a-f]{32}", job_id):
@@ -806,6 +829,7 @@ def dispatch(request: dict[str, Any]) -> dict[str, Any]:
if action == "read_source": return read_source(arguments)
if action == "search_source": return search_source(arguments)
if action == "terminal": return terminal(arguments)
if action == "change": return change(arguments)
if action == "prepare": return prepare(arguments)
if action == "execute": return execute(arguments)
if action == "job": return job(arguments)