"""Actor directory API — AP0.7 + AP1.7c Service Tokens.""" from __future__ import annotations from typing import Literal, Optional from capabilities import require_capability from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel, Field from services import actor_service_tokens from services import actors as actor_service from tenant_context import TenantContext router = APIRouter(prefix="/api/actors", tags=["actors"]) class ServiceTokenCreateRequest(BaseModel): label: str = Field(min_length=1, max_length=255) capabilities: list[str] = Field(default_factory=list) expires_in_days: Optional[int] = Field(default=365, ge=1, le=730) @router.get("") def list_actors( actor_type: Optional[Literal["human", "agent", "working_group", "external_system"]] = None, include_inactive: bool = Query(default=False), q: Optional[str] = Query(default=None, max_length=100), ctx: TenantContext = Depends(require_capability("kairo.actor.read")), ): try: return actor_service.list_actors( tenant_id=ctx.tenant_id, actor_type=actor_type, include_inactive=include_inactive, q=q, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc @router.get("/{actor_id}") def get_actor( actor_id: str, ctx: TenantContext = Depends(require_capability("kairo.actor.read")), ): item = actor_service.get_actor(tenant_id=ctx.tenant_id, actor_id=actor_id) if not item: raise HTTPException(status_code=404, detail="Actor nicht gefunden") return item @router.get("/{actor_id}/service-tokens") def list_actor_service_tokens( actor_id: str, include_revoked: bool = Query(default=False), ctx: TenantContext = Depends(require_capability("kairo.actor.manage")), ): actor = actor_service.get_actor(tenant_id=ctx.tenant_id, actor_id=actor_id) if not actor: raise HTTPException(status_code=404, detail="Actor nicht gefunden") return actor_service_tokens.list_service_tokens_for_actor( tenant_id=ctx.tenant_id, actor_id=actor_id, include_revoked=include_revoked, ) @router.post("/{actor_id}/service-tokens", status_code=201) def create_actor_service_token( actor_id: str, body: ServiceTokenCreateRequest, ctx: TenantContext = Depends(require_capability("kairo.actor.manage")), ): try: return actor_service_tokens.create_service_token( tenant_id=ctx.tenant_id, actor_id=actor_id, label=body.label, created_by_user_id=ctx.user_id, capabilities=body.capabilities, expires_in_days=body.expires_in_days, ) except ValueError as exc: detail = str(exc) if detail == "Actor nicht gefunden": raise HTTPException(status_code=404, detail=detail) from exc raise HTTPException(status_code=400, detail=detail) from exc @router.delete("/service-tokens/{token_id}", status_code=204) def revoke_actor_service_token( token_id: str, ctx: TenantContext = Depends(require_capability("kairo.actor.manage")), ): if not actor_service_tokens.revoke_service_token( tenant_id=ctx.tenant_id, token_id=token_id, user_id=ctx.user_id, ): raise HTTPException(status_code=404, detail="Token nicht gefunden")