5deee8c65c
- Add ToolDefinitionManifest model with base image versioning - Add manifest compiler: Dockerfile + Compose generation from JSON manifests - Add permission fixer: post-start chown/chmod for mount policies - Add tool definition CRUD API with live compile preview endpoint - Integrate manifest-based startup flow in start_instance - Add Alembic migration with data conversion for pi-agent - Add 48 unit tests for manifest compiler, permission fixer, docker service - Keep backward compatibility with legacy dockerfile_template/compose_template Migration: applied successfully. Pi-agent converted to manifest. Quality gates: pytest (146 passed, 4 pre-existing unrelated failures)
68 lines
2.4 KiB
Python
68 lines
2.4 KiB
Python
"""Tool Definition Manifest model."""
|
|
|
|
import uuid
|
|
from typing import TYPE_CHECKING
|
|
|
|
from sqlalchemy import Boolean, ForeignKey, JSON, String, Text
|
|
from sqlalchemy import Uuid as UUID
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from src.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin
|
|
|
|
if TYPE_CHECKING:
|
|
from src.models.user import User
|
|
|
|
|
|
class ToolDefinitionManifest(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
|
"""A declarative manifest that compiles to Dockerfile + Compose.
|
|
|
|
Can be either:
|
|
- A base definition (is_base=True) with a FROM image and common packages
|
|
- A tool definition (is_base=False) that references a base + adds specifics
|
|
"""
|
|
|
|
__tablename__ = "tool_definition_manifests"
|
|
|
|
name: Mapped[str] = mapped_column(String(64), unique=True, nullable=False)
|
|
display_name: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
category: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
|
interface_type: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
|
|
# Base: either a direct image or a reference to another manifest
|
|
base_image: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
|
base_definition_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
UUID(),
|
|
ForeignKey("tool_definition_manifests.id"),
|
|
nullable=True,
|
|
)
|
|
base_version: Mapped[str] = mapped_column(
|
|
String(32), nullable=False, default="latest"
|
|
)
|
|
|
|
# The full manifest JSON
|
|
manifest: Mapped[dict] = mapped_column(JSON, nullable=False)
|
|
|
|
# Caches for quick inspection
|
|
dockerfile_cache: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
compose_cache: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
# Versioning
|
|
version: Mapped[str] = mapped_column(String(32), nullable=False, default="v1")
|
|
is_base: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
|
|
created_by_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
UUID(),
|
|
ForeignKey("users.id"),
|
|
nullable=True,
|
|
)
|
|
|
|
# Relationships
|
|
created_by: Mapped["User | None"] = relationship(
|
|
foreign_keys=[created_by_id],
|
|
)
|
|
base_definition: Mapped["ToolDefinitionManifest | None"] = relationship(
|
|
remote_side="ToolDefinitionManifest.id",
|
|
foreign_keys=[base_definition_id],
|
|
)
|