Saltar a contenido

Anatomía de un plugin

Estructura canónica de un plugin de dominio. La plantilla vive en plugins/_template/ (repo cortex-plugin-template).

Árbol de archivos

plugins/{id}/
├── pyproject.toml              # entry point cortex.plugins
├── cortex_plugin_{id}/
│   ├── __init__.py             # reexporta create_plugin + hooks
│   ├── plugin.py               # SPI: descriptor, api_router, resource_paths
│   ├── resources.py            # register_resources (CUS)
│   ├── store.py                # persistencia o fixtures
│   ├── validators.py           # opcional
│   ├── mcp_tools.py            # opcional
│   ├── configuration/
│   │   └── manifest.json       # settings por panel (opcional)
│   ├── fixtures/               # datos de desarrollo (opcional)
│   └── ui/                     # JSON legacy si no usas ResourceBuilder
│       ├── manifest.json
│       └── dashboards/
├── alembic/                    # migraciones si persiste en PG
├── tests/
│   └── test_*.py
└── docs/                       # documentación de dominio del plugin
    ├── README.md
    ├── api.md
    └── dependencies.md

Piezas y responsabilidades

Pieza Responsabilidad Hook / API
pyproject.toml Nombre del paquete, dependencias, entry point cortex.plugins
plugin.py REST bajo /api/v1/{namespace} api_router(), create_plugin()
resources.py Pantallas declarativas (list/create/edit) register_resources
store.py Acceso a datos (PG, fixtures, memoria) Llamado desde plugin.py
configuration/manifest.json Ajustes por panel Cargado en bootstrap; hook register_configuration opcional
tests/ Contrato HTTP y registro CUS create_test_app(enabled_plugins=...)
docs/ Comportamiento de dominio Fuera de cortex-docs

__init__.py — contrato de exportación

El loader importa el módulo raíz del paquete. Reexporta todo lo que el framework debe invocar:

from cortex_plugin_template.plugin import create_plugin
from cortex_plugin_template.resources import register_resources

__all__ = ["create_plugin", "register_resources"]

Si un hook no está en __all__ ni es atributo del módulo importado, el loader no lo ejecuta.

plugin.py — capa HTTP

Responsabilidades:

  • Definir PluginDescriptor con id, versión y dependencias.
  • Montar rutas REST con prefijo coherente (/api/v1/{id}/...).
  • Delegar persistencia a store.py u otro servicio.
  • Devolver segmentos en resource_paths() para validación CUS.

Referencia mínima: plugins/_template/cortex_plugin_template/plugin.py. Ejemplo CRUD completo (sandbox): plugins/helloworld/cortex_plugin_helloworld/resources.py — ver API interna del framework. Ejemplo CRUD completo (sandbox): plugins/helloworld/cortex_plugin_helloworld/resources.py — ver API interna del framework.

resources.py — capa UI declarativa

Registra un módulo en un namespace de panel:

register_resource(registry, "operations", "template", configure)
#                  ^panel_id      ^module_id

O con registro directo (como en el template):

registry.register_module("operations", "template", manifest, dashboards)

El panel_id y module_id determinan rutas UI (/operations/template/...) y endpoints de manifest (GET /api/v1/panels/operations/modules/template/manifest).

docs/ — documentación del plugin

Toda regla de negocio, tabla de endpoints detallada, eventos y runbooks viven aquí. El handbook de plataforma solo enlaza.

Estructura sugerida: ver Documentación framework vs plugins.

Workspace uv

En el monorepo cortex-hive, cada plugin es submódulo bajo plugins/ y miembro del workspace en el pyproject.toml raíz. Sin git submodule update --init, el discovery no encuentra el paquete.

Flujo git: Repositorios y submódulos.

Checklist antes del primer commit

  • [ ] Entry point cortex.plugins apunta a create_plugin
  • [ ] Hooks reexportados en __init__.py
  • [ ] list_path en CUS coincide con ruta en api_router()
  • [ ] docs/README.md con propósito y dependencias
  • [ ] Tests con create_test_app

Ver Checklist CI y TDD.

Siguiente paso

Integración con el framework — cómo el loader ensambla tu plugin en la API y el shell React.