- Admin users can choose to install skills to public (built-in) or custom category via UI radio selector in InstallSkillDialog - Backend: SkillStorage.ainstall_skill_from_archive accepts target_category param; non-admin 403 on public install - Backend: AgentStore/SkillStore add count_owned(user_id); UserRepository/LocalAuthProvider add list_users() - Backend: GET /api/v1/auth/users endpoint (admin-only) returns all users with skill_count and agent_count - Frontend: admin-only "用户管理" nav item in sidebar; /page/workspace/admin/users route with user table - i18n: added sidebar.users and adminUsers section in zh-CN/en-US Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
"""Abstract persistence API for custom agents."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
from typing import Any
|
|
|
|
|
|
class AgentStore(ABC):
|
|
@abstractmethod
|
|
async def list_visible(self, user_id: str) -> list[dict[str, Any]]:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def get_visible(self, agent_id: str, user_id: str) -> dict[str, Any] | None:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def get_any(self, agent_id: str) -> dict[str, Any] | None:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def get_owned(self, agent_id: str, user_id: str) -> dict[str, Any] | None:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def create(self, data: dict[str, Any]) -> dict[str, Any]:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def ensure_builtin(self, data: dict[str, Any]) -> dict[str, Any]:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def update(self, agent_id: str, user_id: str, data: dict[str, Any]) -> dict[str, Any] | None:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def update_builtin(self, agent_id: str, data: dict[str, Any]) -> dict[str, Any] | None:
|
|
"""Update a built-in agent (user_id IS NULL).
|
|
|
|
Caller is responsible for authorization — this method does NOT check
|
|
any user identity and returns ``None`` if the target is not a built-in.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def delete(self, agent_id: str, user_id: str) -> bool:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def delete_builtin(self, agent_id: str) -> bool:
|
|
"""Delete a built-in agent (user_id IS NULL).
|
|
|
|
Caller is responsible for authorization — this method does NOT check
|
|
any user identity and returns ``False`` if the target is not a built-in.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
async def count_owned(self, user_id: str) -> int:
|
|
"""Return the number of agents owned by ``user_id`` (user_id IS NOT NULL)."""
|
|
raise NotImplementedError
|