Initial curated ITWorx_MCP_Hub source from 368fba691ea8d23403b4d56b53b5ec670d7c5020
This commit is contained in:
commit
cb5be898ea
663 files changed
+82674
No files matched your search
@@ -0,0 +1,5 @@
|
||||
"""Typed control-plane domain primitives."""
|
||||
|
||||
from .domain import Scope
|
||||
|
||||
__all__ = ["Scope"]
|
||||
@@ -0,0 +1,303 @@
|
||||
"""Pydantic domain contracts for the Hub control plane.
|
||||
|
||||
These models deliberately contain references to credentials, never credential
|
||||
values. Persistence rows use the same field names so repositories can validate
|
||||
their output through ``from_attributes`` without leaking ORM objects upward.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, ClassVar
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
from .errors import MissingProjectScope
|
||||
|
||||
|
||||
class DomainModel(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid", from_attributes=True, use_enum_values=True)
|
||||
_datetime_fields: ClassVar[frozenset[str]] = frozenset()
|
||||
|
||||
@field_validator("*", mode="before")
|
||||
@classmethod
|
||||
def require_aware_datetime(cls, value: Any) -> Any:
|
||||
if isinstance(value, datetime) and value.tzinfo is None:
|
||||
raise ValueError("timestamps must include timezone information")
|
||||
return value
|
||||
|
||||
|
||||
class Scope(DomainModel):
|
||||
"""Mandatory authorization scope passed to every repository boundary."""
|
||||
|
||||
workspace_id: UUID
|
||||
project_id: UUID | None = None
|
||||
|
||||
@classmethod
|
||||
def workspace(cls, workspace_id: UUID) -> Scope:
|
||||
return cls(workspace_id=workspace_id)
|
||||
|
||||
@classmethod
|
||||
def project(cls, workspace_id: UUID, project_id: UUID) -> Scope:
|
||||
return cls(workspace_id=workspace_id, project_id=project_id)
|
||||
|
||||
def require_project(self) -> UUID:
|
||||
if self.project_id is None:
|
||||
raise MissingProjectScope("project scope is required")
|
||||
return self.project_id
|
||||
|
||||
|
||||
class Timestamped(DomainModel):
|
||||
id: UUID = Field(default_factory=uuid4)
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
revision: int = Field(default=1, ge=1)
|
||||
|
||||
|
||||
class Workspace(Timestamped):
|
||||
slug: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,62}$")
|
||||
name: str = Field(min_length=1, max_length=160)
|
||||
status: str = Field(default="active", min_length=1, max_length=32)
|
||||
|
||||
|
||||
class Project(Timestamped):
|
||||
workspace_id: UUID
|
||||
slug: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,62}$")
|
||||
name: str = Field(min_length=1, max_length=160)
|
||||
description: str | None = Field(default=None, max_length=1000)
|
||||
status: str = Field(default="active", max_length=32)
|
||||
repository_provider: str | None = Field(default=None, max_length=32)
|
||||
repository_owner: str | None = Field(default=None, max_length=120)
|
||||
repository_name: str | None = Field(default=None, max_length=120)
|
||||
classification: str = Field(default="internal", max_length=32)
|
||||
owner_metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class Environment(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
name: str = Field(pattern=r"^(dev|test|staging|prod)$")
|
||||
classification: str = Field(max_length=32)
|
||||
base_url: str | None = Field(default=None, max_length=2048)
|
||||
status: str = Field(default="active", max_length=32)
|
||||
|
||||
|
||||
class Connector(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
environment_id: UUID | None = None
|
||||
type: str = Field(min_length=1, max_length=64)
|
||||
slug: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,62}$")
|
||||
endpoint: str | None = Field(default=None, max_length=2048)
|
||||
credential_reference: str | None = Field(default=None, max_length=512)
|
||||
configuration: dict[str, Any] = Field(default_factory=dict)
|
||||
enabled: bool = False
|
||||
health_state: str = Field(default="unconfigured", max_length=32)
|
||||
last_check_at: datetime | None = None
|
||||
latency_ms: int | None = Field(default=None, ge=0)
|
||||
stable_error_code: str | None = Field(default=None, max_length=128)
|
||||
sanitized_error: str | None = Field(default=None, max_length=1000)
|
||||
|
||||
|
||||
class RiskClass(DomainModel):
|
||||
code: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,62}$")
|
||||
description: str = Field(min_length=1, max_length=500)
|
||||
allows_writes: bool = False
|
||||
max_output_bytes: int = Field(default=65536, ge=1024, le=10485760)
|
||||
|
||||
|
||||
class Capability(Timestamped):
|
||||
canonical_name: str = Field(pattern=r"^[a-z0-9][a-z0-9.-]{0,119}$")
|
||||
version: str = Field(min_length=1, max_length=32)
|
||||
description: str = Field(min_length=1, max_length=1000)
|
||||
risk_class: str = Field(min_length=1, max_length=64)
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
class ToolDefinition(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
connector_id: UUID
|
||||
canonical_name: str = Field(pattern=r"^[a-z0-9][a-z0-9.-]{0,119}$")
|
||||
version: str = Field(min_length=1, max_length=32)
|
||||
input_schema: dict[str, Any] = Field(default_factory=dict)
|
||||
output_schema: dict[str, Any] = Field(default_factory=dict)
|
||||
timeout_ms: int = Field(default=10000, ge=100, le=120000)
|
||||
output_limit_bytes: int = Field(default=65536, ge=1024, le=10485760)
|
||||
rate_limit_per_minute: int = Field(default=60, ge=1, le=100000)
|
||||
redaction_profile: str = Field(default="default", max_length=64)
|
||||
lifecycle_status: str = Field(default="active", max_length=32)
|
||||
|
||||
|
||||
class VirtualServer(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
environment_id: UUID
|
||||
slug: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,62}$")
|
||||
gateway_external_id: str | None = Field(default=None, max_length=256)
|
||||
desired_hash: str = Field(min_length=1, max_length=128)
|
||||
enabled: bool = False
|
||||
protocol: str = Field(default="streamable-http", max_length=64)
|
||||
health_state: str = Field(default="unconfigured", max_length=32)
|
||||
metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class VirtualServerCapability(DomainModel):
|
||||
id: UUID = Field(default_factory=uuid4)
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
virtual_server_id: UUID
|
||||
capability_id: UUID
|
||||
desired_state_source: str = Field(default="catalog", max_length=64)
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class Client(Timestamped):
|
||||
workspace_id: UUID
|
||||
subject: str = Field(min_length=1, max_length=256)
|
||||
issuer: str = Field(min_length=1, max_length=512)
|
||||
client_type: str = Field(default="mcp", max_length=32)
|
||||
status: str = Field(default="pending", max_length=32)
|
||||
revoked_at: datetime | None = None
|
||||
last_seen_at: datetime | None = None
|
||||
metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class ClientBinding(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
client_id: UUID
|
||||
virtual_server_id: UUID
|
||||
capability_pack: str | None = Field(default=None, max_length=128)
|
||||
constraints: dict[str, Any] = Field(default_factory=dict)
|
||||
enabled: bool = False
|
||||
|
||||
|
||||
class Policy(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID | None = None
|
||||
slug: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,62}$")
|
||||
priority: int = Field(ge=0, le=100000)
|
||||
description: str = Field(min_length=1, max_length=1000)
|
||||
match: dict[str, Any] = Field(default_factory=dict)
|
||||
conditions: dict[str, Any] = Field(default_factory=dict)
|
||||
decision: str = Field(pattern=r"^(allow|allow_if_conditions|deny|deny_and_alert|enforce)$")
|
||||
terminal: bool = False
|
||||
source: str = Field(default="catalog", max_length=64)
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
class PolicyDecision(DomainModel):
|
||||
id: UUID = Field(default_factory=uuid4)
|
||||
workspace_id: UUID
|
||||
project_id: UUID | None = None
|
||||
policy_id: UUID | None = None
|
||||
decision: str = Field(min_length=1, max_length=32)
|
||||
matched_rule_ids: list[str] = Field(default_factory=list)
|
||||
decision_trace: list[dict[str, Any]] = Field(default_factory=list)
|
||||
reason_code: str | None = Field(default=None, max_length=128)
|
||||
trace_id: str = Field(min_length=1, max_length=128)
|
||||
retention_until: datetime | None = None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class ReconcileRun(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
desired_hash: str = Field(min_length=1, max_length=128)
|
||||
current_hash: str | None = Field(default=None, max_length=128)
|
||||
plan_hash: str | None = Field(default=None, max_length=128)
|
||||
status: str = Field(default="created", max_length=32)
|
||||
planned_changes: list[dict[str, Any]] = Field(default_factory=list)
|
||||
applied_changes: list[dict[str, Any]] = Field(default_factory=list)
|
||||
verification: dict[str, Any] = Field(default_factory=dict)
|
||||
started_at: datetime | None = None
|
||||
completed_at: datetime | None = None
|
||||
|
||||
|
||||
class Operation(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
environment_id: UUID
|
||||
operation_type: str = Field(min_length=1, max_length=64)
|
||||
target_ref: str = Field(min_length=1, max_length=512)
|
||||
plan_hash: str = Field(min_length=1, max_length=128)
|
||||
state: str = Field(default="requested", max_length=32)
|
||||
requested_by: str = Field(min_length=1, max_length=256)
|
||||
expires_at: datetime | None = None
|
||||
verification: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class Approval(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
operation_id: UUID
|
||||
plan_hash: str = Field(min_length=1, max_length=128)
|
||||
state: str = Field(default="pending", max_length=32)
|
||||
actor: str = Field(min_length=1, max_length=256)
|
||||
reason: str | None = Field(default=None, max_length=1000)
|
||||
expires_at: datetime
|
||||
approved_at: datetime | None = None
|
||||
|
||||
|
||||
class AuditEvent(DomainModel):
|
||||
id: UUID = Field(default_factory=uuid4)
|
||||
occurred_at: datetime
|
||||
trace_id: str = Field(min_length=1, max_length=128)
|
||||
workspace_id: UUID
|
||||
project_id: UUID | None = None
|
||||
environment_id: UUID | None = None
|
||||
event_type: str = Field(min_length=1, max_length=128)
|
||||
principal_issuer: str | None = Field(default=None, max_length=512)
|
||||
principal_subject: str | None = Field(default=None, max_length=256)
|
||||
client_id: UUID | None = None
|
||||
virtual_server_id: UUID | None = None
|
||||
tool_name: str | None = Field(default=None, max_length=256)
|
||||
policy_decision: str | None = Field(default=None, max_length=32)
|
||||
matched_rule_ids: list[str] = Field(default_factory=list)
|
||||
policy_trace: list[dict[str, Any]] = Field(default_factory=list)
|
||||
arguments_hash: str | None = Field(default=None, max_length=128)
|
||||
sanitized_arguments: dict[str, Any] = Field(default_factory=dict)
|
||||
duration_ms: int | None = Field(default=None, ge=0)
|
||||
outcome: str = Field(min_length=1, max_length=32)
|
||||
error_code: str | None = Field(default=None, max_length=128)
|
||||
retention_until: datetime | None = None
|
||||
operation_id: UUID | None = None
|
||||
|
||||
|
||||
class KnowledgeSource(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
slug: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,62}$")
|
||||
source_type: str = Field(min_length=1, max_length=64)
|
||||
uri: str = Field(min_length=1, max_length=2048)
|
||||
visibility: str = Field(default="project", max_length=32)
|
||||
state: str = Field(default="registered", max_length=32)
|
||||
configuration: dict[str, Any] = Field(default_factory=dict)
|
||||
last_indexed_at: datetime | None = None
|
||||
|
||||
|
||||
class KnowledgeDocument(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
source_id: UUID
|
||||
external_ref: str = Field(min_length=1, max_length=1024)
|
||||
title: str = Field(min_length=1, max_length=500)
|
||||
content_hash: str = Field(min_length=1, max_length=128)
|
||||
document_version: int = Field(default=1, ge=1)
|
||||
state: str = Field(default="registered", max_length=32)
|
||||
content_bytes: int = Field(default=0, ge=0, le=104857600)
|
||||
metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class KnowledgeChunk(Timestamped):
|
||||
workspace_id: UUID
|
||||
project_id: UUID
|
||||
document_id: UUID
|
||||
chunk_index: int = Field(ge=0)
|
||||
text: str = Field(min_length=1, max_length=200000)
|
||||
content_hash: str = Field(min_length=1, max_length=128)
|
||||
token_count: int = Field(default=0, ge=0)
|
||||
metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Domain errors shared by persistence and application services."""
|
||||
|
||||
|
||||
class DomainError(ValueError):
|
||||
"""Base class for expected domain validation failures."""
|
||||
|
||||
|
||||
class InvalidTransition(DomainError):
|
||||
"""Raised when a state machine transition is not permitted."""
|
||||
|
||||
|
||||
class MissingProjectScope(DomainError):
|
||||
"""Raised when a project-owned repository is created without a project scope."""
|
||||
|
||||
|
||||
class ConcurrentUpdateError(DomainError):
|
||||
"""Raised when an optimistic version check detects a stale write."""
|
||||
|
||||
|
||||
class AuditMutationError(DomainError):
|
||||
"""Raised when ordinary application code attempts to mutate an audit event."""
|
||||
@@ -0,0 +1,75 @@
|
||||
"""Explicit state transition tables for control-plane records."""
|
||||
|
||||
from collections.abc import Mapping
|
||||
|
||||
from .errors import InvalidTransition
|
||||
|
||||
TRANSITIONS: Mapping[str, Mapping[str, frozenset[str]]] = {
|
||||
"connector_health": {
|
||||
"unconfigured": frozenset({"checking", "disabled"}),
|
||||
"checking": frozenset({"healthy", "degraded", "unavailable", "misconfigured", "disabled"}),
|
||||
"healthy": frozenset({"checking", "degraded", "unavailable", "disabled"}),
|
||||
"degraded": frozenset({"checking", "healthy", "unavailable", "misconfigured", "disabled"}),
|
||||
"unavailable": frozenset({"checking", "degraded", "misconfigured", "disabled"}),
|
||||
"misconfigured": frozenset({"checking", "disabled"}),
|
||||
"disabled": frozenset(),
|
||||
},
|
||||
"reconcile": {
|
||||
"created": frozenset({"planning"}),
|
||||
"planning": frozenset({"planned", "invalid"}),
|
||||
"planned": frozenset({"applying", "invalid"}),
|
||||
"applying": frozenset({"verifying", "failed"}),
|
||||
"verifying": frozenset({"succeeded", "failed"}),
|
||||
"succeeded": frozenset(),
|
||||
"invalid": frozenset(),
|
||||
"failed": frozenset(),
|
||||
},
|
||||
"client": {
|
||||
"pending": frozenset({"active", "disabled"}),
|
||||
"active": frozenset({"disabled", "revoked"}),
|
||||
"disabled": frozenset({"active", "revoked"}),
|
||||
"revoked": frozenset(),
|
||||
},
|
||||
"operation": {
|
||||
"requested": frozenset({"planned", "rejected"}),
|
||||
"planned": frozenset({"approval_required", "rejected"}),
|
||||
"approval_required": frozenset({"approved", "rejected"}),
|
||||
"approved": frozenset({"executing", "rejected"}),
|
||||
"executing": frozenset({"verifying", "failed"}),
|
||||
"verifying": frozenset({"succeeded", "failed"}),
|
||||
"failed": frozenset({"rollback_pending"}),
|
||||
"rollback_pending": frozenset({"rolled_back", "rollback_failed"}),
|
||||
"rolled_back": frozenset(),
|
||||
"rollback_failed": frozenset(),
|
||||
"succeeded": frozenset(),
|
||||
"rejected": frozenset(),
|
||||
},
|
||||
"approval": {
|
||||
"pending": frozenset({"approved", "rejected", "expired"}),
|
||||
"approved": frozenset(),
|
||||
"rejected": frozenset(),
|
||||
"expired": frozenset(),
|
||||
},
|
||||
"knowledge": {
|
||||
"registered": frozenset({"queued"}),
|
||||
"queued": frozenset({"indexing", "failed"}),
|
||||
"indexing": frozenset({"current", "failed"}),
|
||||
"current": frozenset({"stale"}),
|
||||
"stale": frozenset({"queued"}),
|
||||
"failed": frozenset({"queued"}),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def transition(machine: str, current: str, target: str) -> str:
|
||||
"""Validate and return a target state for a named state machine."""
|
||||
|
||||
table = TRANSITIONS.get(machine)
|
||||
if table is None:
|
||||
raise InvalidTransition(f"unknown state machine: {machine}")
|
||||
allowed = table.get(current)
|
||||
if allowed is None:
|
||||
raise InvalidTransition(f"unknown {machine} state: {current}")
|
||||
if target not in allowed:
|
||||
raise InvalidTransition(f"invalid {machine} transition: {current} -> {target}")
|
||||
return target
|
||||
Reference in new issue
Block a user