forked from retoor/devplacepy
feat: add audit log tables, indexes, and CLI/content recording hooks
This commit is contained in:
@@ -67,6 +67,36 @@ def _is_destructive_command(arguments: dict[str, Any]) -> bool:
|
||||
return bool(DESTRUCTIVE_COMMAND.search(str(arguments.get("command", ""))))
|
||||
|
||||
|
||||
_DEVII_MECHANIC_EVENTS = {
|
||||
"update_behavior": "devii.behavior.update",
|
||||
"tool_create": "devii.tool.create",
|
||||
"tool_update": "devii.tool.update",
|
||||
"tool_delete": "devii.tool.delete",
|
||||
"create_task": "devii.task.create",
|
||||
"update_task": "devii.task.update",
|
||||
"delete_task": "devii.task.delete",
|
||||
"reflect": "devii.lesson.reflect",
|
||||
"forget_lessons": "devii.lesson.forget",
|
||||
"customize_set_css": "devii.customization.css.set",
|
||||
"customize_set_js": "devii.customization.js.set",
|
||||
"customize_reset": "devii.customization.reset",
|
||||
}
|
||||
|
||||
_DEVII_CONTAINER_EVENTS = {
|
||||
"container_create_instance": "container.instance.create",
|
||||
"container_exec": "container.instance.exec",
|
||||
"container_schedule": "container.schedule.create",
|
||||
}
|
||||
|
||||
|
||||
def _safe_args(arguments: dict[str, Any]) -> dict[str, Any]:
|
||||
out: dict[str, Any] = {}
|
||||
for key, value in arguments.items():
|
||||
text = str(value)
|
||||
out[key] = text if len(text) <= 200 else text[:200] + "..."
|
||||
return out
|
||||
|
||||
|
||||
def confirmation_error(name: str, arguments: dict[str, Any]) -> ToolInputError | None:
|
||||
if _is_confirmed(arguments):
|
||||
return None
|
||||
@@ -148,6 +178,8 @@ class Dispatcher:
|
||||
self._avatar = avatar
|
||||
self._browser = browser
|
||||
self._is_admin = is_admin
|
||||
self._owner_kind = owner_kind
|
||||
self._owner_id = owner_id
|
||||
self._fetch = FetchController(settings)
|
||||
self._docs = DocsController(settings)
|
||||
self._cost = CostController(quota_provider=quota_provider)
|
||||
@@ -219,6 +251,7 @@ class Dispatcher:
|
||||
logger.info("Resource cache hit for %s (%s)", name, resource_key)
|
||||
return cached
|
||||
result = await self._run(action, arguments)
|
||||
self._audit_mechanic(action, arguments)
|
||||
if action.handler == "chunks":
|
||||
return result
|
||||
return wrap_if_large(
|
||||
@@ -231,6 +264,30 @@ class Dispatcher:
|
||||
logger.exception("Dispatch %s crashed", name)
|
||||
return unexpected_result(exc)
|
||||
|
||||
def _audit_mechanic(self, action: Action, arguments: dict[str, Any]) -> None:
|
||||
from devplacepy.services.audit import record as audit
|
||||
|
||||
name = action.name
|
||||
event_key = _DEVII_MECHANIC_EVENTS.get(name)
|
||||
if event_key is None and action.handler == "container":
|
||||
if name == "container_instance_action":
|
||||
event_key = f"container.instance.{arguments.get('action', 'action')}"
|
||||
else:
|
||||
event_key = _DEVII_CONTAINER_EVENTS.get(name)
|
||||
if event_key is None:
|
||||
return
|
||||
actor_kind = "user" if self._owner_kind == "user" else self._owner_kind
|
||||
audit.record_system(
|
||||
event_key,
|
||||
actor_kind=actor_kind,
|
||||
actor_uid=self._owner_id if self._owner_kind == "user" else None,
|
||||
actor_role="admin" if self._is_admin else (actor_kind if actor_kind != "user" else "member"),
|
||||
origin="devii",
|
||||
via_agent=1,
|
||||
summary=f"Devii {name} for {self._owner_id or self._owner_kind}",
|
||||
metadata={"tool": name, "args": _safe_args(arguments)},
|
||||
)
|
||||
|
||||
async def _run(self, action: Action, arguments: dict[str, Any]) -> str:
|
||||
if action.handler == "status":
|
||||
return json.dumps(
|
||||
|
||||
Reference in New Issue
Block a user