Documents Skill Management in CLOUD_DEPLOYMENT.md (cloud-skill store, per-host entitlement, incremental sync, local authoring/override, inventory report, skills:admin scope) and applies ruff check/format to all touched modules. All tasks complete; full non-integration suite green (593 passed) and openspec validate --strict passes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ Two routers:
|
||||
heartbeat/planner-decision) endpoints an agent uses to pull incremental
|
||||
skill deltas and report its local-skill inventory.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
@@ -16,7 +17,12 @@ from uuid import uuid4
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query, Request, status
|
||||
|
||||
from cloud.auth import AuthProvider, HostAuthorizationError, Principal, SKILLS_ADMIN_SCOPE
|
||||
from cloud.auth import (
|
||||
AuthProvider,
|
||||
HostAuthorizationError,
|
||||
Principal,
|
||||
SKILLS_ADMIN_SCOPE,
|
||||
)
|
||||
from cloud.observability import current_correlation_id
|
||||
from cloud.skills import (
|
||||
CloudSkillConflictError,
|
||||
@@ -86,7 +92,9 @@ def create_skill_management_router(
|
||||
authorize(request)
|
||||
skill = service.get_skill(skill_id)
|
||||
if skill is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Skill not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Skill not found"
|
||||
)
|
||||
return _skill_response(skill)
|
||||
|
||||
@router.post(
|
||||
@@ -112,9 +120,13 @@ def create_skill_management_router(
|
||||
now=utc_now(),
|
||||
)
|
||||
except CloudSkillValidationError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=str(exc)) from exc
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=str(exc)
|
||||
) from exc
|
||||
except CloudSkillConflictError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(exc)) from exc
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT, detail=str(exc)
|
||||
) from exc
|
||||
_audit(repository, principal, skill.id, "cloud_skill_create")
|
||||
return _skill_response(skill)
|
||||
|
||||
@@ -139,9 +151,13 @@ def create_skill_management_router(
|
||||
now=utc_now(),
|
||||
)
|
||||
except CloudSkillValidationError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Skill not found") from exc
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Skill not found"
|
||||
) from exc
|
||||
except CloudSkillConflictError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(exc)) from exc
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT, detail=str(exc)
|
||||
) from exc
|
||||
_audit(repository, principal, skill.id, "cloud_skill_update")
|
||||
return _skill_response(skill)
|
||||
|
||||
@@ -152,14 +168,18 @@ def create_skill_management_router(
|
||||
try:
|
||||
service.delete_skill(skill_id)
|
||||
except KeyError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Skill not found") from exc
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Skill not found"
|
||||
) from exc
|
||||
_audit(repository, principal, skill_id, "cloud_skill_delete")
|
||||
|
||||
@router.get(
|
||||
"/skills/{skill_id}/entitlements",
|
||||
response_model=CloudSkillEntitlementListResponse,
|
||||
)
|
||||
def list_entitlements(skill_id: str, request: Request) -> CloudSkillEntitlementListResponse:
|
||||
def list_entitlements(
|
||||
skill_id: str, request: Request
|
||||
) -> CloudSkillEntitlementListResponse:
|
||||
authorize(request)
|
||||
return CloudSkillEntitlementListResponse(
|
||||
skill_id=skill_id,
|
||||
@@ -176,7 +196,9 @@ def create_skill_management_router(
|
||||
try:
|
||||
service.grant_entitlement(skill_id, host_id, now=utc_now())
|
||||
except CloudSkillValidationError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)
|
||||
) from exc
|
||||
_audit(repository, principal, skill_id, "cloud_skill_entitlement_grant")
|
||||
|
||||
@router.delete(
|
||||
@@ -193,7 +215,9 @@ def create_skill_management_router(
|
||||
"/hosts/{host_id}/skill-inventory",
|
||||
response_model=HostSkillInventoryResponse,
|
||||
)
|
||||
def get_host_inventory(host_id: str, request: Request) -> HostSkillInventoryResponse:
|
||||
def get_host_inventory(
|
||||
host_id: str, request: Request
|
||||
) -> HostSkillInventoryResponse:
|
||||
authorize(request)
|
||||
entry = service.get_host_inventory(host_id)
|
||||
if entry is None:
|
||||
@@ -231,7 +255,9 @@ def create_skill_host_router(
|
||||
try:
|
||||
principal.require_host(host_id)
|
||||
except HostAuthorizationError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(exc)) from exc
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN, detail=str(exc)
|
||||
) from exc
|
||||
|
||||
@router.get("/hosts/{host_id}/skills/sync", response_model=CloudSkillSyncResponse)
|
||||
def sync_skills(
|
||||
|
||||
Reference in New Issue
Block a user