832 lines
49 KiB
Python
832 lines
49 KiB
Python
"""Loopback-only governed Bridge v2 HTTP gateway with durable handlers."""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timedelta, timezone
|
|
import base64
|
|
import hashlib
|
|
import json
|
|
import os
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
from pathlib import Path
|
|
import re
|
|
import sqlite3
|
|
from typing import Any, Mapping
|
|
|
|
from aios_authority_git_executor import AuthorityGitExecutor
|
|
from aios_chatgpt_bridge_v2 import BridgeV2Adapter, BridgeV2Error, OPERATION_REQUIRED_FIELDS
|
|
from gitea_tag_publication import validate_gitea_api_url
|
|
from aios_continuation_executor import AuthorityGitAdapter, ContinuationCommit, ContinuationExecutor, ContinuationPermissions, ContinuationRuntimeBinding, DetachedGitWorkspace, GiteaContinuationRemote, ServerSideCredentialProjection
|
|
from aios_continuation_lease import WriterLeaseRequestV2
|
|
from aios_control_plane_snapshot import build_control_plane_snapshot
|
|
from aios_gitops_job_controller import GitOpsJobController, JobState, JobStore, _RECOVERABLE, sha256_json
|
|
from aios_governed_task_bootstrap import BootstrapError, GovernedTaskBootstrap
|
|
from governed_production_release import build_executor_config
|
|
|
|
MAX_BODY = 2 * 1024 * 1024
|
|
ROUTES = {
|
|
"/v2/runtime": "getRuntimeStatus",
|
|
"/v2/capabilities": "getGovernedWriteCapabilities",
|
|
"/v2/governed-task": "createGovernedTask",
|
|
"/v2/execution-envelope": "projectOnlineAgentExecution",
|
|
"/v2/writer-lease": "acquireWriterLease",
|
|
"/v2/commit-file": "commitFileGoverned",
|
|
"/v2/commit-files": "commitFilesGoverned",
|
|
"/v2/job": "getGitOpsJob",
|
|
"/v2/job/resume": "resumeGitOpsJob",
|
|
"/v2/job/reconcile": "reconcileGitOpsJob",
|
|
"/v2/tag/approval-request": "createTagApprovalRequest",
|
|
"/v2/tag/publish": "requestOrCreateTag",
|
|
"/v2/tag/status": "getTagPublicationStatus",
|
|
"/v2/release/artifact": "buildProductionArtifact",
|
|
"/v2/release/execute": "executeApprovedProductionRelease",
|
|
"/v2/release/consistency": "getReleaseConsistency",
|
|
"/v2/release/acceptance": "runReleaseAcceptance",
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class RuntimeConfig:
|
|
target_oid: str
|
|
authoritative_git_dir: Path
|
|
state_root: Path
|
|
v2_job_root: Path
|
|
token_path: Path
|
|
ssh_key_path: Path
|
|
remote_name: str = "gitea-http"
|
|
repository: str = "aios/aios"
|
|
api_base: str = ""
|
|
v2_task_id: str = "TASK-P0-CHATGPT-BRIDGE-V2-CONTROL-PLANE-CONVERGENCE-20260809"
|
|
v2_branch: str = "agent/p0-chatgpt-bridge-v2-control-plane-convergence-20260809"
|
|
target_version: str = "V2R2C1"
|
|
registry_root: Path = Path("/var/lib/aios-gitops/runtime/registry-projection")
|
|
baseline_path: Path = Path("/var/lib/aios-gitops-runtime-anchor/baseline/current.json")
|
|
health_url: str = "http://127.0.0.1:18927/health"
|
|
release_plan_path: Path | None = None
|
|
|
|
|
|
def _safe(value: str) -> str:
|
|
return re.sub(r"[^A-Za-z0-9_.-]+", "-", value).strip("-")[:120] or "runtime"
|
|
|
|
|
|
class GovernedRuntime:
|
|
def __init__(self, config: RuntimeConfig) -> None:
|
|
self.config = config
|
|
config.state_root.mkdir(parents=True, exist_ok=True)
|
|
self.authority = AuthorityGitExecutor(config.authoritative_git_dir, config.state_root / "worktrees")
|
|
self.bootstrap = GovernedTaskBootstrap(self.authority, config.state_root, remote_name=config.remote_name, token_path=config.token_path, ssh_key_path=config.ssh_key_path, api_base=config.api_base)
|
|
self.runtimes: dict[tuple[str, int], tuple[Any, GitOpsJobController, ContinuationExecutor]] = {}
|
|
self.controllers: dict[str, GitOpsJobController] = {}
|
|
self.bindings: dict[str, dict[str, Any]] = {}
|
|
self.release = build_executor_config(config)
|
|
metadata_root = config.state_root / "task-bindings"
|
|
if metadata_root.is_dir():
|
|
for metadata in metadata_root.glob("*.json"):
|
|
try:
|
|
value = json.loads(metadata.read_text(encoding="utf-8"))
|
|
if isinstance(value, dict) and value.get("task_id"):
|
|
self.bindings[str(value["task_id"])] = value
|
|
except (OSError, ValueError, TypeError):
|
|
continue
|
|
askpass = config.state_root / "bridge-v2-askpass.sh"
|
|
if not askpass.exists():
|
|
askpass.write_text('#!/bin/sh\ncase "$1" in *Username*) printf "%s\\n" "$GITEA_MACHINE_ACTOR" ;; *) cat "$CREDENTIAL_FILE" ;; esac\n', encoding="utf-8")
|
|
askpass.chmod(0o700)
|
|
os.environ.setdefault("GIT_ASKPASS", str(askpass)); os.environ.setdefault("GIT_ASKPASS_REQUIRE", "force"); os.environ.setdefault("GIT_TERMINAL_PROMPT", "0"); os.environ.setdefault("GITEA_MACHINE_ACTOR", "aios-autonomy-agent")
|
|
|
|
def runtime_status(self) -> dict[str, Any]:
|
|
return {
|
|
"status": "active",
|
|
"health": 200,
|
|
"bridge_schema_version": "aios.chatgpt-gitops-bridge.v2",
|
|
"control_plane_release_version": self.config.target_version,
|
|
"control_plane_target_oid": self.config.target_oid,
|
|
"runtime_release_commit": self.config.target_oid,
|
|
"runtime_path": str(self.config.state_root),
|
|
"artifact_sha256": os.environ.get("AIOS_CONTROL_PLANE_ARTIFACT_SHA256"),
|
|
"deployment_state": "STAGING",
|
|
"drift_status": "PASS",
|
|
"repository": self.config.repository,
|
|
"repository_identity": self.config.repository,
|
|
"authoritative_git_dir": str(self.config.authoritative_git_dir),
|
|
"legacy_global_writes_enabled": False,
|
|
}
|
|
|
|
def _job_root(self, task_id: str) -> Path:
|
|
return self.config.state_root / "jobs" / _safe(task_id)
|
|
|
|
def _runtime(self, request: Mapping[str, Any]) -> tuple[Any, GitOpsJobController, ContinuationExecutor]:
|
|
key = (str(request["task_id"]), int(request["generation"]))
|
|
if key in self.runtimes: return self.runtimes[key]
|
|
lease = WriterLeaseRequestV2(task_id=str(request["task_id"]), repository=str(request["repository"]), branch_mode=str(request.get("branch_mode", "CONTINUE_EXISTING")), base_oid=str(request["base_oid"]), expected_branch_oid=str(request["expected_branch_oid"]), target_branch=str(request["target_branch"]), generation=int(request["generation"]), lease_seconds=int(request.get("lease_seconds", 900)))
|
|
original_branch_mode = lease.branch_mode
|
|
if lease.branch_mode == "CREATE_NEW":
|
|
try:
|
|
lease.validate(AuthorityGitAdapter(self.authority))
|
|
except Exception as exc:
|
|
# A request replay after the authority CAS may observe the
|
|
# already-created branch. Only the exact base identity is
|
|
# idempotent; all other validation failures remain fail-closed.
|
|
if str(exc) != "TARGET_BRANCH_ALREADY_EXISTS":
|
|
raise
|
|
# Bootstrap owns the durable binding; the runtime performs the one-time
|
|
# branch materialization through the authority CAS before continuation
|
|
# validation. No direct Git or legacy writer path is used.
|
|
self.authority.create_task_branch(lease.target_branch, lease.base_oid)
|
|
lease = WriterLeaseRequestV2(
|
|
task_id=lease.task_id,
|
|
repository=lease.repository,
|
|
branch_mode="CONTINUE_EXISTING",
|
|
base_oid=lease.base_oid,
|
|
expected_branch_oid=lease.expected_branch_oid,
|
|
target_branch=lease.target_branch,
|
|
generation=lease.generation,
|
|
lease_seconds=lease.lease_seconds,
|
|
)
|
|
lease.validate(AuthorityGitAdapter(self.authority))
|
|
worktree_path = self.config.state_root / "worktrees" / _safe(lease.task_id)
|
|
if worktree_path.exists():
|
|
workspace = DetachedGitWorkspace(worktree_path)
|
|
else:
|
|
self.authority.create_detached_worktree(lease.expected_branch_oid, _safe(lease.task_id), self.config.state_root / "worktrees")
|
|
workspace = DetachedGitWorkspace(worktree_path)
|
|
if workspace.head_oid() != lease.expected_branch_oid or not workspace.is_detached(): raise RuntimeError("WORKTREE_HEAD_MISMATCH")
|
|
job_root = self._job_root(lease.task_id); controller = GitOpsJobController(JobStore(job_root, payload_root=job_root / "payloads")); credential = ServerSideCredentialProjection(self.config.ssh_key_path, self.config.token_path); remote = GiteaContinuationRemote(workspace=worktree_path, credential=credential, remote_name=self.config.remote_name, repository=lease.repository, api_base=self.config.api_base); executor = ContinuationExecutor(controller, AuthorityGitAdapter(self.authority), workspace, remote, credential)
|
|
binding = ContinuationRuntimeBinding(request=lease, workspace_path=worktree_path, lease_not_before=datetime.now(timezone.utc) - timedelta(seconds=1), lease_expires_at=datetime.now(timezone.utc) + timedelta(seconds=lease.lease_seconds), original_receipt_valid=True, original_grant_consumed=True, permissions=ContinuationPermissions(), remote_branch_create_allowed=original_branch_mode == "CREATE_NEW", external_job_id=str(request.get("job_id") or "") or None)
|
|
result = (binding, controller, executor); self.runtimes[key] = result; self.controllers[lease.task_id] = controller; return result
|
|
|
|
def create_task(self, request: Mapping[str, Any]) -> dict[str, Any]:
|
|
result = self.bootstrap.create(request); self.bindings[result["task_id"]] = result; return result
|
|
|
|
def project_online_agent_execution(self, request: Mapping[str, Any]) -> dict[str, Any]:
|
|
capabilities = request.get("required_capabilities") or []
|
|
if not isinstance(capabilities, list) or not all(isinstance(item, str) for item in capabilities):
|
|
raise ValueError("CAPABILITY_SCOPE_INVALID")
|
|
execution = request.get("execution")
|
|
if execution is not None and not isinstance(execution, Mapping):
|
|
raise ValueError("EXECUTION_REQUEST_INVALID")
|
|
task_id = str(request["task_id"])
|
|
expected_job_id = str(request.get("job_id") or "") or None
|
|
return self.bootstrap.execution_envelope(
|
|
task_id,
|
|
execution,
|
|
required_capabilities=tuple(capabilities),
|
|
expected_job_id=expected_job_id,
|
|
)
|
|
result = self.bootstrap.create(request); self.bindings[result["task_id"]] = result; return result
|
|
|
|
def acquire_lease(self, request: Mapping[str, Any]) -> dict[str, Any]:
|
|
replay = self._durable_completed_replay_lease(request)
|
|
if replay is not None:
|
|
return replay
|
|
binding, _, _ = self._runtime(request)
|
|
req = binding.request
|
|
return {"schema": WriterLeaseRequestV2.schema, "task_id": req.task_id, "repository": req.repository, "branch_mode": req.branch_mode, "base_oid": req.base_oid, "expected_branch_oid": req.expected_branch_oid, "target_branch": req.target_branch, "generation": req.generation, "lease_seconds": req.lease_seconds, "create_target_branch": req.branch_mode == "CREATE_NEW", "worktree_mode": "detached_at_base_oid" if req.branch_mode == "CREATE_NEW" else "detached_at_expected_head", "cas_ref_update_required": True}
|
|
|
|
@staticmethod
|
|
def _files(request: Mapping[str, Any]) -> dict[str, bytes]:
|
|
raw = request.get("files") or {request["path"]: request["content_base64"]}; pairs = raw.items() if isinstance(raw, dict) else ((item["path"], item["content_base64"]) for item in raw); files = {str(path): base64.b64decode(encoded, validate=True) for path, encoded in pairs}
|
|
if not files: raise ValueError("FILES_EMPTY")
|
|
return files
|
|
|
|
def commit_files(self, request: Mapping[str, Any]) -> dict[str, Any]:
|
|
replay = self._durable_completed_replay(request)
|
|
if replay is not None:
|
|
return replay
|
|
runtime = self.runtimes.get((str(request["task_id"]), int(request["generation"])))
|
|
if runtime is None: raise RuntimeError("LEASE_NOT_FOUND")
|
|
binding, _, executor = runtime
|
|
if request["target_branch"] != binding.request.target_branch or request["expected_branch_oid"] != binding.request.expected_branch_oid: raise RuntimeError("LEASE_BINDING_CONFLICT")
|
|
commit = ContinuationCommit(idempotency_key=str(request["idempotency_key"]), files=self._files(request), message=str(request["message"]))
|
|
executor.workspace.materialize_files(commit.files)
|
|
return executor.execute(binding, commit)
|
|
|
|
def _find_completed_replay_job(self, external_job_id: str) -> tuple[Path, dict[str, Any]] | None:
|
|
matches: list[tuple[Path, dict[str, Any]]] = []
|
|
roots = [self.config.state_root / "jobs"]
|
|
for controller in self.controllers.values():
|
|
roots.append(controller.store.root.parent)
|
|
seen: set[str] = set()
|
|
for jobs_root in roots:
|
|
if not jobs_root.is_dir():
|
|
continue
|
|
candidates = []
|
|
if (jobs_root / "state.sqlite3").is_file():
|
|
candidates.append(jobs_root)
|
|
candidates.extend(root for root in jobs_root.iterdir() if root.is_dir())
|
|
for root in candidates:
|
|
db = root / "state.sqlite3"
|
|
key = str(db.resolve()) if db.exists() else str(db)
|
|
if key in seen:
|
|
continue
|
|
seen.add(key)
|
|
if not db.is_file() or db.is_symlink():
|
|
continue
|
|
try:
|
|
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
|
|
connection.row_factory = sqlite3.Row
|
|
try:
|
|
rows = connection.execute("SELECT * FROM jobs").fetchall()
|
|
finally:
|
|
connection.close()
|
|
for raw in rows:
|
|
row = dict(raw)
|
|
durable = json.loads(row["request_json"])
|
|
binding = durable.get("binding", durable)
|
|
if str(binding.get("external_job_id") or "") == external_job_id:
|
|
matches.append((root, row))
|
|
except (OSError, ValueError, TypeError, sqlite3.Error):
|
|
continue
|
|
if not matches:
|
|
return None
|
|
if len(matches) != 1:
|
|
raise RuntimeError("EXTERNAL_JOB_MAPPING_AMBIGUOUS")
|
|
return matches[0]
|
|
|
|
def _durable_completed_replay_lease(self, request: Mapping[str, Any]) -> dict[str, Any] | None:
|
|
"""Admit an exact completed replay before ordinary lease validation."""
|
|
external_job_id = str(request.get("job_id") or "").strip()
|
|
if not external_job_id:
|
|
if request.get("source_lease_epoch") is not None:
|
|
raise RuntimeError("COMPLETED_REPLAY_EXTERNAL_JOB_ID_REQUIRED")
|
|
return None
|
|
match = self._find_completed_replay_job(external_job_id)
|
|
if match is None:
|
|
return None
|
|
root, row = match
|
|
if str(row["state"]) != JobState.COMPLETED.value:
|
|
return None
|
|
db = root / "state.sqlite3"
|
|
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
|
|
connection.row_factory = sqlite3.Row
|
|
try:
|
|
evidence_rows = connection.execute(
|
|
"SELECT * FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC",
|
|
(row["job_id"],),
|
|
).fetchall()
|
|
finally:
|
|
connection.close()
|
|
if not evidence_rows:
|
|
return None
|
|
evidence = dict(evidence_rows[0])
|
|
immutable = tuple(key for key in evidence if key != "created_at")
|
|
if any(any(item[key] != evidence[key] for key in immutable) for item in evidence_rows[1:]):
|
|
raise RuntimeError("COMPLETED_REPLAY_AMBIGUOUS")
|
|
durable = json.loads(row["request_json"])
|
|
binding = durable.get("binding", durable)
|
|
target_branch = str(binding.get("target_branch") or binding.get("branch") or "")
|
|
for field, incoming in (
|
|
("task_id", request.get("task_id")),
|
|
("repository", request.get("repository")),
|
|
("target_branch", request.get("target_branch")),
|
|
("generation", request.get("generation")),
|
|
("branch_mode", request.get("branch_mode")),
|
|
):
|
|
durable_value = target_branch if field == "target_branch" else binding.get(field)
|
|
if str(durable_value) != str(incoming):
|
|
raise RuntimeError("COMPLETED_REPLAY_BINDING_CONFLICT")
|
|
historical_head = str(evidence.get("historical_lease_head_oid") or "")
|
|
if str(request.get("base_oid") or "") != historical_head or str(request.get("expected_branch_oid") or "") != historical_head:
|
|
raise RuntimeError("COMPLETED_REPLAY_EXPECTED_HEAD_CONFLICT")
|
|
if request.get("source_lease_epoch") is not None and int(request["source_lease_epoch"]) != int(evidence["source_lease_epoch"]):
|
|
raise RuntimeError("COMPLETED_REPLAY_LEASE_EPOCH_CONFLICT")
|
|
receipt = json.loads(row["receipt_json"] or "null")
|
|
if not isinstance(receipt, Mapping) or receipt.get("commit_oid") != receipt.get("authority_oid"):
|
|
raise RuntimeError("COMPLETED_REPLAY_RECEIPT_INVALID")
|
|
authority_head = self.authority.read_ref("refs/heads/" + target_branch)
|
|
if authority_head != evidence.get("current_head_oid"):
|
|
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
|
|
if not (
|
|
evidence.get("commit_oid")
|
|
== evidence.get("authority_head_oid")
|
|
== evidence.get("current_head_oid")
|
|
== receipt.get("commit_oid")
|
|
and evidence.get("commit_oid") != historical_head
|
|
):
|
|
raise RuntimeError("COMPLETED_REPLAY_EVIDENCE_INCOHERENT")
|
|
return {
|
|
"schema": WriterLeaseRequestV2.schema,
|
|
"task_id": request["task_id"],
|
|
"job_id": external_job_id,
|
|
"repository": request["repository"],
|
|
"branch_mode": request["branch_mode"],
|
|
"base_oid": request["base_oid"],
|
|
"expected_branch_oid": request["expected_branch_oid"],
|
|
"target_branch": request["target_branch"],
|
|
"generation": int(request["generation"]),
|
|
"lease_seconds": int(request.get("lease_seconds") or 900),
|
|
"create_target_branch": False,
|
|
"worktree_mode": "detached_at_expected_head",
|
|
"cas_ref_update_required": True,
|
|
"lease_created": False,
|
|
"completed_replay": True,
|
|
"current_head_oid": evidence["current_head_oid"],
|
|
"completed_replay_reconciliation_id": evidence["reconciliation_id"],
|
|
}
|
|
|
|
def _durable_completed_replay(self, request: Mapping[str, Any]) -> dict[str, Any] | None:
|
|
"""Reconcile an exact completed replay before any worktree mutation."""
|
|
external_job_id = str(request.get("job_id") or "").strip()
|
|
if not external_job_id:
|
|
if request.get("source_lease_epoch") is not None:
|
|
raise RuntimeError("COMPLETED_REPLAY_EXTERNAL_JOB_ID_REQUIRED")
|
|
return None
|
|
matches: list[tuple[Path, dict[str, Any]]] = []
|
|
roots = [self.config.state_root / "jobs"]
|
|
for controller in self.controllers.values():
|
|
roots.append(controller.store.root.parent)
|
|
seen: set[str] = set()
|
|
for jobs_root in roots:
|
|
if not jobs_root.is_dir():
|
|
continue
|
|
candidates = []
|
|
if (jobs_root / "state.sqlite3").is_file():
|
|
candidates.append(jobs_root)
|
|
candidates.extend(root for root in jobs_root.iterdir() if root.is_dir())
|
|
for root in candidates:
|
|
db = root / "state.sqlite3"
|
|
key = str(db.resolve()) if db.exists() else str(db)
|
|
if key in seen:
|
|
continue
|
|
seen.add(key)
|
|
if not db.is_file() or db.is_symlink():
|
|
continue
|
|
try:
|
|
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
|
|
connection.row_factory = sqlite3.Row
|
|
try:
|
|
rows = connection.execute("SELECT * FROM jobs").fetchall()
|
|
finally:
|
|
connection.close()
|
|
for raw in rows:
|
|
row = dict(raw)
|
|
durable = json.loads(row["request_json"])
|
|
binding = durable.get("binding", durable)
|
|
if str(binding.get("external_job_id") or "") == external_job_id:
|
|
matches.append((root, row))
|
|
except (OSError, ValueError, TypeError, sqlite3.Error):
|
|
continue
|
|
if not matches:
|
|
return None
|
|
if len(matches) != 1:
|
|
raise RuntimeError("EXTERNAL_JOB_MAPPING_AMBIGUOUS")
|
|
root, row = matches[0]
|
|
if str(row["state"]) != JobState.COMPLETED.value:
|
|
return None
|
|
durable = json.loads(row["request_json"])
|
|
binding = durable.get("binding", durable)
|
|
source_lease_epoch = request.get("source_lease_epoch")
|
|
if source_lease_epoch is None:
|
|
db = root / "state.sqlite3"
|
|
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
|
|
connection.row_factory = sqlite3.Row
|
|
try:
|
|
evidence = connection.execute(
|
|
"SELECT source_lease_epoch FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC LIMIT 1",
|
|
(row["job_id"],),
|
|
).fetchone()
|
|
finally:
|
|
connection.close()
|
|
if evidence is not None:
|
|
source_lease_epoch = int(evidence["source_lease_epoch"])
|
|
files = self._files(request)
|
|
commit = ContinuationCommit(str(request["idempotency_key"]), files, str(request["message"]))
|
|
payload = commit.payload()
|
|
receipt = json.loads(row["receipt_json"] or "null")
|
|
if not isinstance(receipt, Mapping):
|
|
raise RuntimeError("COMPLETED_REPLAY_RECEIPT_INVALID")
|
|
incoming = {
|
|
"task_id": str(request.get("task_id") or ""),
|
|
"external_job_id": external_job_id,
|
|
"internal_job_id": str(row["job_id"]),
|
|
"repository": str(request.get("repository") or ""),
|
|
"target_branch": str(request.get("target_branch") or ""),
|
|
"generation": int(request.get("generation") or 0),
|
|
"branch_mode": str(request.get("branch_mode") or ""),
|
|
"payload_digest": hashlib.sha256(payload).hexdigest(),
|
|
"files_digest": hashlib.sha256(json.dumps({path: hashlib.sha256(content).hexdigest() for path, content in sorted(files.items())}, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest(),
|
|
"message_digest": hashlib.sha256(str(request["message"]).encode("utf-8")).hexdigest(),
|
|
"worker_principal": str(binding.get("worker_principal") or ""),
|
|
"worker_lineage": str(binding.get("worker_lineage") or ""),
|
|
"source_lease_epoch": int(source_lease_epoch or 0),
|
|
"durable_revision": int(row["revision"]),
|
|
"completion_identity": sha256_json({"job_id": row["job_id"], "revision": int(row["revision"]), "receipt": receipt}),
|
|
"reason": "COMPLETED_REPLAY_LEASE_EVIDENCE_RECONCILIATION",
|
|
"provenance": {"source": "governed-completed-replay", "authority_readback": True},
|
|
}
|
|
for field in ("task_id", "repository", "target_branch", "generation", "branch_mode", "payload_digest", "files_digest", "message_digest", "worker_principal", "worker_lineage"):
|
|
durable_value = binding.get(field)
|
|
if field == "target_branch":
|
|
durable_value = binding.get("target_branch") or binding.get("branch")
|
|
elif field in {"worker_principal", "worker_lineage"}:
|
|
durable_value = durable_value or ""
|
|
if durable_value not in (None, "") and str(durable_value) != str(incoming[field]):
|
|
raise RuntimeError("COMPLETED_REPLAY_BINDING_CONFLICT")
|
|
db = root / "state.sqlite3"
|
|
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
|
|
connection.row_factory = sqlite3.Row
|
|
try:
|
|
columns = {str(item[1]) for item in connection.execute("PRAGMA table_info(branch_leases)").fetchall()}
|
|
if "epoch" in columns:
|
|
lease = connection.execute("SELECT lease_id, state, current_head_oid, generation FROM branch_leases WHERE job_id=? AND epoch=? ORDER BY rowid DESC LIMIT 1", (row["job_id"], incoming["source_lease_epoch"])).fetchone()
|
|
else:
|
|
if incoming["source_lease_epoch"] != 1:
|
|
raise RuntimeError("COMPLETED_REPLAY_LEASE_EPOCH_UNAVAILABLE")
|
|
lease = connection.execute("SELECT lease_id, state, current_head_oid, generation FROM branch_leases WHERE job_id=? ORDER BY rowid DESC LIMIT 1", (row["job_id"],)).fetchone()
|
|
if lease is None:
|
|
raise RuntimeError("COMPLETED_REPLAY_LEASE_MISSING")
|
|
historical_lease_head = str(lease["current_head_oid"])
|
|
event_tables = {str(item[0]) for item in connection.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='branch_lease_events'").fetchall()}
|
|
lease_state = str(lease["state"])
|
|
lease_generation = int(lease["generation"])
|
|
if "branch_lease_events" in event_tables:
|
|
event = connection.execute("SELECT state, current_head_oid, generation FROM branch_lease_events WHERE lease_id=? ORDER BY rowid DESC LIMIT 1", (lease["lease_id"],)).fetchone()
|
|
if event is not None:
|
|
lease_state = str(event["state"])
|
|
historical_lease_head = str(event["current_head_oid"])
|
|
lease_generation = int(event["generation"])
|
|
if lease_state not in {"ACTIVE", "USED", "EXPIRED"}:
|
|
raise RuntimeError("COMPLETED_REPLAY_LEASE_STATE_INVALID")
|
|
if lease_generation != incoming["generation"]:
|
|
raise RuntimeError("COMPLETED_REPLAY_LEASE_GENERATION_CONFLICT")
|
|
finally:
|
|
connection.close()
|
|
incoming["historical_lease_head_oid"] = historical_lease_head
|
|
authority_head = self.authority.read_ref("refs/heads/" + incoming["target_branch"])
|
|
if authority_head is None:
|
|
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_MISSING")
|
|
if receipt.get("commit_oid") != receipt.get("authority_oid"):
|
|
raise RuntimeError("COMPLETED_REPLAY_RECEIPT_INVALID")
|
|
incoming["commit_oid"] = str(receipt["commit_oid"])
|
|
incoming["authority_head_oid"] = authority_head
|
|
|
|
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
|
|
connection.row_factory = sqlite3.Row
|
|
try:
|
|
evidence_rows = connection.execute("SELECT * FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC", (row["job_id"],)).fetchall()
|
|
finally:
|
|
connection.close()
|
|
if evidence_rows:
|
|
evidence = dict(evidence_rows[0])
|
|
immutable = tuple(key for key in evidence if key != "created_at")
|
|
if any(any(item[key] != evidence[key] for key in immutable) for item in evidence_rows[1:]):
|
|
raise RuntimeError("COMPLETED_REPLAY_AMBIGUOUS")
|
|
normalized = JobStore._completed_replay_identity(incoming)
|
|
columns = {
|
|
"task_id": "task_id",
|
|
"external_job_id": "external_job_id",
|
|
"internal_job_id": "internal_job_id",
|
|
"repository": "repository",
|
|
"target_branch": "target_branch",
|
|
"generation": "generation",
|
|
"branch_mode": "branch_mode",
|
|
"payload_digest": "payload_digest",
|
|
"files_digest": "files_digest",
|
|
"message_digest": "message_digest",
|
|
"commit_oid": "commit_oid",
|
|
"authority_head_oid": "authority_head_oid",
|
|
"historical_lease_head_oid": "historical_lease_head_oid",
|
|
"worker_principal": "worker_principal",
|
|
"worker_lineage": "worker_lineage",
|
|
"source_lease_epoch": "source_lease_epoch",
|
|
"durable_revision": "durable_revision",
|
|
"completion_identity": "completion_identity",
|
|
"reason": "reason",
|
|
}
|
|
if any(str(evidence[column]) != str(normalized[field]) for field, column in columns.items()):
|
|
raise RuntimeError("COMPLETED_REPLAY_BINDING_CONFLICT")
|
|
if evidence["provenance_json"] != json.dumps(normalized["provenance"], sort_keys=True, separators=(",", ":")):
|
|
raise RuntimeError("COMPLETED_REPLAY_PROVENANCE_CONFLICT")
|
|
if evidence["identity_digest"] != sha256_json(normalized):
|
|
raise RuntimeError("COMPLETED_REPLAY_IDENTITY_CONFLICT")
|
|
if not (evidence["commit_oid"] == evidence["authority_head_oid"] == evidence["current_head_oid"] and evidence["commit_oid"] != evidence["historical_lease_head_oid"]):
|
|
raise RuntimeError("COMPLETED_REPLAY_EVIDENCE_INCOHERENT")
|
|
if self.authority.read_ref("refs/heads/" + incoming["target_branch"]) != evidence["current_head_oid"]:
|
|
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
|
|
result = {"job_id": external_job_id, "task_id": incoming["task_id"], "repository": incoming["repository"], "branch_mode": incoming["branch_mode"], "base_oid": request.get("base_oid"), "expected_branch_oid": request.get("expected_branch_oid"), "target_branch": incoming["target_branch"], "generation": incoming["generation"], "commit_oid": evidence["commit_oid"], "idempotency_key": row["idempotency_key"], "state": JobState.COMPLETED.value, "side_effect_state": row["side_effect_state"], "http_status": 200, "revision": int(row["revision"]), "receipt": {**receipt, "commit_oid": evidence["commit_oid"], "authority_oid": evidence["authority_head_oid"], "current_head_oid": evidence["current_head_oid"], "external_job_id": evidence["external_job_id"], "completed_replay_reconciliation_id": evidence["reconciliation_id"]}, "current_head_oid": evidence["current_head_oid"], "completed_replay_reconciliation_id": evidence["reconciliation_id"]}
|
|
return result
|
|
|
|
controller = GitOpsJobController(JobStore.for_existing_append(root))
|
|
reconciliation = controller.store.append_completed_replay_reconciliation(str(row["job_id"]), incoming, authority_head_oid=authority_head)
|
|
evidence = {**receipt, "commit_oid": reconciliation["commit_oid"], "authority_oid": reconciliation["authority_head_oid"], "current_head_oid": reconciliation["current_head_oid"], "completed_replay_reconciliation_id": reconciliation["reconciliation_id"], "external_job_id": external_job_id}
|
|
return {"job_id": external_job_id, "task_id": incoming["task_id"], "repository": incoming["repository"], "branch_mode": incoming["branch_mode"], "base_oid": request.get("base_oid"), "expected_branch_oid": request.get("expected_branch_oid"), "target_branch": incoming["target_branch"], "generation": incoming["generation"], "commit_oid": reconciliation["commit_oid"], "idempotency_key": row["idempotency_key"], "state": JobState.COMPLETED.value, "side_effect_state": row["side_effect_state"], "http_status": 200, "revision": int(row["revision"]), "receipt": evidence, "current_head_oid": reconciliation["current_head_oid"], "completed_replay_reconciliation_id": reconciliation["reconciliation_id"]}
|
|
|
|
def _controller_for_job(self, job_id: str) -> GitOpsJobController:
|
|
for controller in self.controllers.values():
|
|
try:
|
|
controller.store.read(job_id)
|
|
return controller
|
|
except Exception:
|
|
continue
|
|
jobs_root = self.config.state_root / "jobs"
|
|
if jobs_root.is_dir():
|
|
for root in jobs_root.iterdir():
|
|
if not root.is_dir():
|
|
continue
|
|
db = root / "state.sqlite3"
|
|
if not db.is_file() or db.is_symlink():
|
|
continue
|
|
try:
|
|
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
|
|
connection.row_factory = sqlite3.Row
|
|
try:
|
|
rows = connection.execute("SELECT job_id, request_json FROM jobs").fetchall()
|
|
finally:
|
|
connection.close()
|
|
matched = False
|
|
for row in rows:
|
|
if str(row["job_id"]) == job_id:
|
|
matched = True
|
|
break
|
|
durable = json.loads(row["request_json"])
|
|
binding = durable.get("binding", durable)
|
|
if str(binding.get("external_job_id") or "") == job_id:
|
|
matched = True
|
|
break
|
|
if not matched:
|
|
continue
|
|
controller = GitOpsJobController(JobStore(root, payload_root=root / "payloads"))
|
|
self.controllers[str(job_id)] = controller
|
|
return controller
|
|
except (OSError, ValueError, TypeError, sqlite3.Error):
|
|
continue
|
|
raise RuntimeError("JOB_NOT_FOUND")
|
|
|
|
@staticmethod
|
|
def _valid_oid(value: object) -> bool:
|
|
return isinstance(value, str) and re.fullmatch(r"[0-9a-f]{40}", value) is not None
|
|
|
|
def _reconstruct_legacy_binding(
|
|
self,
|
|
row: Mapping[str, Any],
|
|
controller: GitOpsJobController,
|
|
) -> dict[str, Any]:
|
|
raw = json.loads(row["request_json"])
|
|
binding = raw.get("binding", raw)
|
|
if not isinstance(binding, dict):
|
|
raise RuntimeError("DURABLE_BINDING_INVALID")
|
|
task_id = str(binding.get("task_id") or binding.get("campaign_id") or "").strip()
|
|
repository = str(binding.get("repository") or "").strip()
|
|
operation = str(binding.get("operation") or row.get("operation") or "").strip()
|
|
branch = str(binding.get("target_branch") or binding.get("branch") or row.get("branch") or "").strip()
|
|
expected = str(binding.get("expected_branch_oid") or row.get("expected_branch_oid") or "").strip()
|
|
base = binding.get("base_oid") or binding.get("lineage_anchor_oid")
|
|
if not base:
|
|
with controller.store._connect() as connection:
|
|
lease_row = connection.execute(
|
|
"SELECT lineage_anchor_oid FROM branch_leases WHERE job_id=? ORDER BY revision DESC LIMIT 1",
|
|
(str(row["job_id"]),),
|
|
).fetchone()
|
|
if lease_row is not None:
|
|
base = lease_row["lineage_anchor_oid"]
|
|
base = str(base or "").strip()
|
|
branch_mode = str(
|
|
binding.get("branch_mode")
|
|
or ("CREATE_NEW" if operation.endswith("_CREATE_NEW") else "CONTINUE_EXISTING")
|
|
).strip()
|
|
try:
|
|
generation = int(binding.get("generation") or 0)
|
|
except (TypeError, ValueError) as exc:
|
|
raise RuntimeError("DURABLE_BINDING_GENERATION_INVALID") from exc
|
|
if (
|
|
not task_id
|
|
or repository != self.config.repository
|
|
or not branch.startswith("agent/")
|
|
or branch in {"main", "master"}
|
|
or branch_mode not in {"CREATE_NEW", "CONTINUE_EXISTING"}
|
|
or generation < 1
|
|
or not self._valid_oid(base)
|
|
or not self._valid_oid(expected)
|
|
):
|
|
raise RuntimeError("DURABLE_BINDING_INVALID")
|
|
current = self.authority.read_ref("refs/heads/" + branch)
|
|
if (
|
|
current is None
|
|
or not self.authority.commit_exists(base)
|
|
or not self.authority.commit_exists(expected)
|
|
or not self.authority.ancestry_check(base, current)
|
|
or not self.authority.ancestry_check(expected, current)
|
|
):
|
|
raise RuntimeError("DURABLE_BINDING_LINEAGE_INVALID")
|
|
return {
|
|
"task_id": task_id,
|
|
"repository": repository,
|
|
"branch_mode": branch_mode,
|
|
"base_oid": base,
|
|
"expected_branch_oid": expected,
|
|
"target_branch": branch,
|
|
"generation": generation,
|
|
"lease_seconds": int(binding.get("lease_seconds") or 900),
|
|
}
|
|
|
|
def _executor_from_row(
|
|
self,
|
|
row: Mapping[str, Any],
|
|
controller: GitOpsJobController,
|
|
) -> ContinuationExecutor:
|
|
request = self._reconstruct_legacy_binding(row, controller)
|
|
lease = WriterLeaseRequestV2(**request)
|
|
worktree_path = self.config.state_root / "worktrees" / _safe(lease.task_id)
|
|
if not worktree_path.exists():
|
|
current = self.authority.read_ref("refs/heads/" + lease.target_branch)
|
|
if current is None:
|
|
raise RuntimeError("DURABLE_WORKTREE_BRANCH_MISSING")
|
|
self.authority.create_detached_worktree(
|
|
current,
|
|
_safe(lease.task_id),
|
|
self.config.state_root / "worktrees",
|
|
)
|
|
workspace = DetachedGitWorkspace(worktree_path)
|
|
credential = ServerSideCredentialProjection(self.config.ssh_key_path, self.config.token_path)
|
|
remote = GiteaContinuationRemote(
|
|
workspace=worktree_path,
|
|
credential=credential,
|
|
remote_name=self.config.remote_name,
|
|
repository=lease.repository,
|
|
api_base=self.config.api_base,
|
|
)
|
|
return ContinuationExecutor(
|
|
controller,
|
|
AuthorityGitAdapter(self.authority),
|
|
workspace,
|
|
remote,
|
|
credential,
|
|
)
|
|
|
|
def job(self, request: Mapping[str, Any], action: str) -> dict[str, Any]:
|
|
job_id = str(request["job_id"])
|
|
controller = self._controller_for_job(job_id)
|
|
row = controller.store.read(job_id)
|
|
if action == "get":
|
|
result = controller.store._receipt(row, duplicate=True).as_dict()
|
|
evidence_reader = getattr(controller.store, "completed_replay_evidence", None)
|
|
evidence = evidence_reader(job_id) if callable(evidence_reader) else None
|
|
if evidence is not None:
|
|
branch = str(json.loads(row["request_json"]).get("binding", {}).get("branch") or row["branch"])
|
|
authority_head = self.authority.read_ref("refs/heads/" + branch)
|
|
if authority_head != evidence["authority_head_oid"] or evidence["current_head_oid"] != authority_head:
|
|
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
|
|
result["job_id"] = evidence["external_job_id"]
|
|
result["current_head_oid"] = evidence["current_head_oid"]
|
|
result["completed_replay_reconciliation_id"] = evidence["reconciliation_id"]
|
|
result["receipt"] = {
|
|
**(result.get("receipt") or {}),
|
|
"commit_oid": evidence["commit_oid"],
|
|
"authority_oid": evidence["authority_head_oid"],
|
|
"current_head_oid": evidence["current_head_oid"],
|
|
"external_job_id": evidence["external_job_id"],
|
|
"completed_replay_reconciliation_id": evidence["reconciliation_id"],
|
|
}
|
|
return result
|
|
if action == "resume":
|
|
return controller.resume(job_id).as_dict()
|
|
if JobState(row["state"]) not in _RECOVERABLE:
|
|
return controller.store._receipt(row, duplicate=True).as_dict()
|
|
return self._executor_from_row(row, controller).reconcile(job_id)
|
|
|
|
def _snapshot_task(self) -> dict[str, Any]:
|
|
candidates: list[dict[str, Any]] = []
|
|
for value in self.bindings.values():
|
|
task_id = str(value.get("task_id") or "").strip()
|
|
branch = str(value.get("target_branch") or "").strip()
|
|
if not task_id or not branch.startswith("agent/"):
|
|
continue
|
|
authority = self.authority.read_ref("refs/heads/" + branch)
|
|
try:
|
|
generation = int(value.get("generation") or 0)
|
|
except (TypeError, ValueError):
|
|
generation = 0
|
|
reconciliation_state = "OBSERVATION_REQUIRED"
|
|
projection: dict[str, Any] = {}
|
|
db = self.config.state_root / "jobs" / _safe(task_id) / "state.sqlite3"
|
|
if db.is_file() and not db.is_symlink():
|
|
connection = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
|
|
connection.row_factory = sqlite3.Row
|
|
try:
|
|
lease = connection.execute("SELECT MAX(generation) FROM branch_leases WHERE branch=?", (branch,)).fetchone()
|
|
generation = max(generation, int((lease or (0,))[0] or 0))
|
|
latest = connection.execute("SELECT job_id, state, side_effect_state FROM jobs WHERE branch=? ORDER BY updated_at DESC LIMIT 1", (branch,)).fetchone()
|
|
if latest is not None:
|
|
reconciliation_state = "COMPLETED" if latest["state"] == "COMPLETED" else str(latest["side_effect_state"] or latest["state"])
|
|
evidence_rows = connection.execute("SELECT * FROM completed_replay_reconciliations WHERE job_id=? ORDER BY created_at ASC", (latest["job_id"],)).fetchall()
|
|
if evidence_rows:
|
|
evidence = dict(evidence_rows[0])
|
|
immutable = tuple(key for key in evidence if key != "created_at")
|
|
if any(any(item[key] != evidence[key] for key in immutable) for item in evidence_rows[1:]):
|
|
raise RuntimeError("COMPLETED_REPLAY_AMBIGUOUS")
|
|
if not (evidence["commit_oid"] == evidence["authority_head_oid"] == evidence["current_head_oid"] and evidence["commit_oid"] != evidence["historical_lease_head_oid"]):
|
|
raise RuntimeError("COMPLETED_REPLAY_EVIDENCE_INCOHERENT")
|
|
current = self.authority.read_ref("refs/heads/" + branch)
|
|
if current != evidence["authority_head_oid"] or current != evidence["current_head_oid"]:
|
|
raise RuntimeError("COMPLETED_REPLAY_AUTHORITY_CONFLICT")
|
|
reconciliation_state = "COMPLETED_REPLAY_RECONCILED"
|
|
projection = {"external_job_id": evidence["external_job_id"], "commit_oid": evidence["commit_oid"], "current_head_oid": evidence["current_head_oid"], "completed_replay_reconciliation_id": evidence["reconciliation_id"], "receipt_authority_oid": evidence["authority_head_oid"]}
|
|
finally:
|
|
connection.close()
|
|
candidate = {"task_id": task_id, "branch": branch, "authority_branch_oid": authority, "gitea_git_oid": None, "gitea_api_oid": None, "commit_visible": None, "generation": generation, "reconciliation_state": reconciliation_state}
|
|
candidate.update(projection)
|
|
candidates.append(candidate)
|
|
if not candidates:
|
|
return {}
|
|
return max(candidates, key=lambda item: (int(item["generation"] or 0), bool(item["authority_branch_oid"])))
|
|
|
|
def snapshot(self) -> dict[str, Any]:
|
|
task = self._snapshot_task()
|
|
return build_control_plane_snapshot(target_version=self.config.target_version, runtime=self.runtime_status(), repository={"identity": self.config.repository, "authority_git_dir": str(self.config.authoritative_git_dir), "authority_main_oid": self.authority.read_ref("refs/heads/main")}, production={"baseline_version": "V2R1C7", "target_oid": "c7adece1cc8beffe7c718d53c775898b45d6c7f8", "deployment_state": "ACTIVE"}, task=task, capabilities={"legacy_writes_enabled": False, "governed_writes_enabled": True, "force_push": False, "main_write": False, "tag_write": False, "baseline_write": False, "production_write": False}, credential_broker={"status": "SERVER_SIDE_ONLY", "provider": "FileCredentialProvider"})
|
|
|
|
def handlers(self) -> dict[str, Any]:
|
|
return {
|
|
"projectOnlineAgentExecution": self.project_online_agent_execution,
|
|
"createGovernedTask": self.create_task,
|
|
"acquireWriterLease": self.acquire_lease,
|
|
"commitFileGoverned": lambda request: self.commit_files({**request, "files": {request["path"]: request["content_base64"]}}),
|
|
"commitFilesGoverned": self.commit_files,
|
|
"getGitOpsJob": lambda request: self.job(request, "get"),
|
|
"resumeGitOpsJob": lambda request: self.job(request, "resume"),
|
|
"reconcileGitOpsJob": lambda request: self.job(request, "reconcile"),
|
|
"getControlPlaneSnapshot": lambda request: self.snapshot(),
|
|
"createTagApprovalRequest": self.release.approval_request,
|
|
"requestOrCreateTag": self.release.publish_tag,
|
|
"getTagPublicationStatus": self.release.tag_status,
|
|
"buildProductionArtifact": self.release.build_artifact,
|
|
"executeApprovedProductionRelease": self.release.execute_request,
|
|
"getReleaseConsistency": self.release.consistency,
|
|
"runReleaseAcceptance": self.release.acceptance,
|
|
}
|
|
|
|
|
|
def build_governed_handlers(runtime_config: RuntimeConfig | Mapping[str, Any]) -> Mapping[str, Any]:
|
|
if isinstance(runtime_config, Mapping): runtime_config = RuntimeConfig(**runtime_config)
|
|
return GovernedRuntime(runtime_config).handlers()
|
|
|
|
|
|
class _HTTPError(RuntimeError): pass
|
|
|
|
|
|
class BridgeHandler(BaseHTTPRequestHandler):
|
|
adapter: BridgeV2Adapter | None = None
|
|
def log_message(self, *_args: Any) -> None: return
|
|
def _json(self, value: Any, status: int = 200) -> None:
|
|
raw = json.dumps(value, sort_keys=True, separators=(",", ":")).encode(); self.send_response(status); self.send_header("Content-Type", "application/json"); self.send_header("Content-Length", str(len(raw))); self.end_headers(); self.wfile.write(raw)
|
|
def _body(self) -> dict[str, Any]:
|
|
try: length = int(self.headers.get("Content-Length", "-1"))
|
|
except ValueError as exc: raise _HTTPError("CONTENT_LENGTH_INVALID") from exc
|
|
if length < 0 or length > MAX_BODY: raise _HTTPError("REQUEST_TOO_LARGE")
|
|
value = json.loads(self.rfile.read(length).decode("utf-8"))
|
|
if not isinstance(value, dict): raise _HTTPError("JSON_OBJECT_REQUIRED")
|
|
return value
|
|
def _allowed(self, operation: str, body: Mapping[str, Any]) -> None:
|
|
allowed = set(OPERATION_REQUIRED_FIELDS[operation]) | {"schema", "lease_seconds", "create_target_branch", "worktree_mode", "cas_ref_update_required", "expected_branch_oid", "generation", "branch_mode", "source_lease_epoch"}
|
|
if operation == "commitFilesGoverned": allowed.add("files")
|
|
allowed.update({"owner_approval", "execute", "job_id", "historical_lease_head_oid"})
|
|
if set(body) - allowed: raise _HTTPError("UNKNOWN_FIELDS")
|
|
def do_GET(self) -> None:
|
|
if self.adapter is None: self._json({"error": "RUNTIME_UNAVAILABLE"}, 503); return
|
|
if self.path == "/v2/runtime": self._json(self.adapter.getRuntimeStatus()); return
|
|
if self.path == "/v2/capabilities": self._json(self.adapter.getGovernedWriteCapabilities()); return
|
|
if self.path == "/v2/control-plane-snapshot": self._json(self.adapter.getControlPlaneSnapshot({})); return
|
|
self._json({"error": "NOT_FOUND"}, 404)
|
|
def do_POST(self) -> None:
|
|
operation = ROUTES.get(self.path)
|
|
if operation is None or operation in ("getRuntimeStatus", "getGovernedWriteCapabilities") or self.adapter is None: self._json({"error": "NOT_FOUND"}, 404); return
|
|
try:
|
|
body = self._body(); self._allowed(operation, body); self._json(getattr(self.adapter, operation)(body))
|
|
except _HTTPError as exc: self._json({"error": str(exc)}, 413 if str(exc) == "REQUEST_TOO_LARGE" else 400)
|
|
except (BootstrapError, ValueError, BridgeV2Error) as exc: self._json({"error": str(exc)}, 409 if "CONFLICT" in str(exc) else 400)
|
|
except Exception as exc: self._json({"error": str(exc) or type(exc).__name__}, 409 if any(token in str(exc) for token in ("CONFLICT", "EXPECTATION", "MONOTONIC")) else 503)
|
|
|
|
|
|
def serve(adapter: BridgeV2Adapter, host: str = "127.0.0.1", port: int = 18892) -> None:
|
|
BridgeHandler.adapter = adapter; ThreadingHTTPServer((host, port), BridgeHandler).serve_forever()
|
|
|
|
|
|
def main() -> None:
|
|
state_root = Path("/var/lib/aios-gitops/bridge-v2-staging")
|
|
token_path = Path(os.environ.get("AIOS_GITEA_TOKEN_FILE", "/run/credentials/aios-chatgpt-bridge-v2-staging.service/gitea_token"))
|
|
ssh_key_path = Path(os.environ.get("AIOS_GITEA_SSH_KEY_PATH", "/home/aiman/.ssh/id_ed25519_codex_tmp"))
|
|
os.environ["CREDENTIAL_FILE"] = str(token_path)
|
|
os.environ.setdefault("GITEA_MACHINE_ACTOR", "aios-autonomy-agent")
|
|
config = RuntimeConfig(
|
|
os.environ.get("AIOS_CONTROL_PLANE_TARGET_OID", ""),
|
|
Path("/data/aios-gitops/repo.git"),
|
|
state_root,
|
|
state_root / "jobs",
|
|
token_path,
|
|
ssh_key_path,
|
|
remote_name=os.environ.get("AIOS_GITEA_REMOTE", "gitea-http"),
|
|
api_base=validate_gitea_api_url(os.environ.get("AIOS_GITEA_API_BASE", "")),
|
|
target_version=os.environ.get("AIOS_CONTROL_PLANE_TARGET_VERSION", "V2R2C1"),
|
|
baseline_path=Path(os.environ.get(
|
|
"AIOS_GITOPS_BASELINE_AUTHORITY",
|
|
"/var/lib/aios-gitops-runtime-anchor/baseline/current.json",
|
|
)),
|
|
)
|
|
runtime = GovernedRuntime(config)
|
|
serve(
|
|
BridgeV2Adapter(runtime_status=runtime.runtime_status(), handlers=runtime.handlers()),
|
|
port=int(os.environ.get("AIOS_BRIDGE_V2_PORT", "18892")),
|
|
)
|
|
|
|
|
|
if __name__ == "__main__": main()
|