La CLI de cmssy
init genera el cableado en una app existente, add block crea y registra un bloque, link conecta la app a un workspace y demuestra que funciona.
Tres comandos, cada uno para un momento que antes era trabajo manual: cablear una app nueva, añadir un bloque y conectar con un workspace.
cmssy init
cmssy nunca crea el andamiaje de tu app. El framework es un adaptador, nunca el cimiento: creas la app con la CLI de Next.js e init le añade el cableado de cmssy.
npx create-next-app@latest my-site
cd my-site
npx @cmssy/cli initLa CLI apunta a Next.js con App Router y necesita Node 18.18+. Astro, Remix y React Router los soporta el propio SDK: esos los cableas a mano, no con init.
Es idempotente: un archivo que ya existe se omite y se informa como omitido, nunca se sobrescribe. Ejecútala dos veces y la segunda no cambia nada.
Qué escribe
cmssy.config.ts
proxy.ts
next.config.mjs
env.example
cmssy/blocks.ts registro de bloques
cmssy/editor.tsx puente del editor
blocks/hero/block.ts
blocks/hero/Hero.tsx
blocks/hero/Hero.module.css
app/[[...path]]/page.tsx ruta catch-all
app/api/draft/route.ts vista previaFíjate en lo que no está: ni app/sitemap.ts, ni app/robots.ts, ni ruta /cmssy-edit, ni layout raíz, ni layout editable.
No falta: es deliberado. Esos archivos codifican decisiones sobre tu app -qué URL expones, cómo es tu chrome, cómo formas un sitemap- y una plantilla que las adivinara sería errónea para la mayoría de proyectos. init te da una página que renderiza y una ruta de borrador; el resto lo añades según lo necesites, siguiendo rutas y páginas, layouts y SEO.
Flags: --dir <ruta> apunta a una app fuera del directorio de trabajo, --force sobrescribe el cableado existente.
cmssy add block
Cada bloque después del hero generado suponía copiar archivos a mano y acordarse de editar el registro.
cmssy add block pricing-table
cmssy add block faq-list --dir ../my-siteTodos los nombres se derivan del argumento en kebab-case: pricing-table da tipo pricing-table, etiqueta Pricing Table, componente PricingTable y export pricingTableBlock.
Escribe los archivos del bloque y lo registra en cmssy/blocks.ts: añade el import y lo agrega al array blocks, preservando tu formato y las entradas existentes.
Se niega a tocar cualquier cosa ambigua: un nombre inválido, un bloque ya registrado, archivos existentes, o un registro sin array export const blocks = [...]. Cada caso es un fallo ruidoso con el paso manual detallado, nunca una escritura parcial silenciosa.
El bloque generado empieza con un heading obligatorio y un text opcional. Edita las props y el marcado, reinicia el servidor de desarrollo, y el editor recoge el tipo nuevo del handshake del manifiesto.
cmssy link
Conectar una app a un workspace suponía copiar cinco valores entre el panel y .env.local, y el editor seguía muerto hasta que todos estaban bien.
npx @cmssy/cli link
cmssy link --token cs_... --workspace acme/shop --preview-url https://shop.example.comSe autentica con un token de API (desde --token o CMSSY_API_TOKEN; .env.local y .env se leen primero, sin sobrescribir variables del shell), elige un workspace, lee el secreto de borrador, escribe CMSSY_ORG_SLUG, CMSSY_WORKSPACE_SLUG y CMSSY_DRAFT_SECRET en .env.local -fusionando, así que las líneas y comentarios existentes sobreviven- y luego ejecuta el preflight.
Leer el secreto de borrador requiere el permiso PAGES_EDIT. Su ausencia se informa exactamente como eso.
La URL de vista previa es compartida: localhost se rechaza
--preview-url fija el origen donde el editor enmarca tu app para todos en el workspace. Un valor localhost se rechaza a propósito: apuntar la vista previa compartida a tu máquina rompería el editor de cada compañero.
Para desarrollo local, activa el modo dev en el editor de cmssy e introduce ahí tu host local. Ese destino es por usuario y no toca nada compartido.
Las comprobaciones
- Workspace alcanzable:
public.siteConfigresponde. Distingue slugs erróneos, problemas de red y un workspace por encima de su límite de entrega. - Secreto de borrador: el backend confirma que el secreto escrito coincide. En una plataforma sin ese campo informa
?y continúa. - Enlace profundo al editor: siempre se imprime.
- Enlace de vista previa: se imprime si el workspace declara una URL de vista previa. Salir:
/api/draft?disable=1.
Hay tres estados, no dos: ✓ verificado, ✗ roto con el arreglo en la línea siguiente y código de salida 1, y ? no se pudo verificar, que nunca bloquea. Ese tercer estado existe porque "desconocido" y "roto" son cosas distintas, y una herramienta que los junta o grita sin motivo o esconde un fallo real.
El preflight también es una API
Cada comprobación es una función pura en @cmssy/core, expuesta bajo una subruta para que el utillaje de desarrollo nunca entre en tu bundle de producción:
import {
checkWorkspaceReachable,
checkDraftSecret,
checkPreviewUrl,
checkFrameAncestors,
buildEditorUrl,
} from "@cmssy/core/preflight";Cada una devuelve { status: "ok" | "fail" | "unknown", message, fix? }. Las dos primeras hablan con la API de entrega; el resto es lógica pura de cadenas. Ninguna importa un framework ni un módulo de Node, así que corren en cualquier sitio.
Cuando el cableado está roto
En desarrollo, las mismas comprobaciones guardan la ruta de edición. Una petición del editor que no supere la verificación no da 404: el adaptador renderiza una página de diagnóstico dentro del iframe del editor, una línea por comprobación.
Muestra el slug del workspace y qué comprobación falló, nunca el valor de un secreto.
Siguientes pasos
- Instalación: qué genera
init, explicado. - Desarrollo de bloques: qué hacer con un bloque generado.
- Vista previa: los secretos que escribe
link.