Files
aios/tools/integrate_projection_recovery.py
T

138 lines
4.1 KiB
Python
Executable File

#!/usr/bin/env python3
from __future__ import annotations
import argparse
from pathlib import Path
import sys
IMPORT_LINE = (
"from aios_agent_task_projection_recovery_http import "
"get_projection as get_canonical_projection, "
"recover_from_json as recover_canonical_projection\n"
)
IMPORT_ANCHOR = '''from aios_online_agent_execution import (
GovernedExecutionBinding,
validate_execution_envelope,
)
'''
GET_OLD = ''' if self.path == "/health":
self._json(
{"status": "ok", "service": "aios-agent-task-initial-execution-projection"},
200,
)
else:
self._json({"error": "NOT_FOUND"}, 404)
'''
GET_NEW = ''' if self.path == "/health":
self._json(
{"status": "ok", "service": "aios-agent-task-initial-execution-projection"},
200,
)
else:
canonical_match = re.fullmatch(
r"/v1/tasks/([^/]+)/canonical-projection", self.path
)
if canonical_match is None:
self._json({"error": "NOT_FOUND"}, 404)
return
if not self._authorized():
self._json({"error": "UNAUTHORIZED"}, 401)
return
if self.store is None:
self._json({"error": "RUNTIME_UNAVAILABLE"}, 503)
return
status, result = get_canonical_projection(
self.store.db_path, canonical_match.group(1)
)
self._json(result, status)
'''
POST_MATCH_ANCHOR = ''' initial_match = re.fullmatch(
r"/v1/tasks/([^/]+)/initial-execution-projection", self.path
)
'''
POST_MATCH_REPLACEMENT = POST_MATCH_ANCHOR + ''' canonical_match = re.fullmatch(
r"/v1/tasks/([^/]+)/canonical-projection-recovery", self.path
)
'''
POST_VALIDATION_OLD = ''' if initial_match is None and recovery_match is None:
self._json({"error": "NOT_FOUND"}, 404)
return
'''
POST_VALIDATION_NEW = ''' if (
initial_match is None
and recovery_match is None
and canonical_match is None
):
self._json({"error": "NOT_FOUND"}, 404)
return
'''
VALUE_HANDLER_OLD = ''' if initial_match is not None:
request = InitialExecutionProjectionRequest.from_mapping(value)
'''
VALUE_HANDLER_NEW = ''' if canonical_match is not None:
status, result = recover_canonical_projection(
self.store.db_path, canonical_match.group(1), value
)
self._json(result, status)
return
if initial_match is not None:
request = InitialExecutionProjectionRequest.from_mapping(value)
'''
class PatchError(RuntimeError):
pass
def replace_once(text: str, old: str, new: str, label: str) -> str:
count = text.count(old)
if count != 1:
raise PatchError(f"{label}_ANCHOR_COUNT_{count}")
return text.replace(old, new, 1)
def apply(path: Path) -> bool:
text = path.read_text(encoding="utf-8")
if "canonical-projection-recovery" in text and IMPORT_LINE.strip() in text:
return False
text = replace_once(text, IMPORT_ANCHOR, IMPORT_ANCHOR + IMPORT_LINE, "IMPORT")
text = replace_once(text, GET_OLD, GET_NEW, "GET")
text = replace_once(
text, POST_MATCH_ANCHOR, POST_MATCH_REPLACEMENT, "POST_MATCH"
)
text = replace_once(
text, POST_VALIDATION_OLD, POST_VALIDATION_NEW, "POST_VALIDATION"
)
text = replace_once(
text, VALUE_HANDLER_OLD, VALUE_HANDLER_NEW, "VALUE_HANDLER"
)
path.write_text(text, encoding="utf-8")
return True
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("path", type=Path)
args = parser.parse_args()
try:
changed = apply(args.path)
except (OSError, PatchError) as exc:
print(f"ERROR:{exc}", file=sys.stderr)
return 2
print("CHANGED" if changed else "ALREADY_APPLIED")
return 0
if __name__ == "__main__":
raise SystemExit(main())