Ir al contenido

CLI

Esta página está transcrita de la ayuda del propio binario, no escrita de memoria: cada bloque de ayuda es una copia de lo que imprime cms <comando> -h.

Los comandos con prefijo operan sobre un subsistema: db: es el ciclo del esquema —de tu cms.config.ts a las tablas de D1, y de vuelta si te arrepientes—, settings: los ajustes guardados en KV, secret: el secreto con el que se firman las sesiones, y resources: los recursos de Cloudflare contra los que el proyecto está declarado. Los sueltos —init, deploy, update y doctor— no llevan prefijo precisamente porque no operan sobre ningún subsistema: operan sobre el proyecto entero, y son el ciclo de vida de una instalación: crearla, subirla, actualizarla y diagnosticarla.

Ventana de terminal
pnpm cms --help
Usage: cms [options] [command]
Genera el schema, los tipos y las migraciones de Kevin CMS
Options:
-V, --version Muestra la versión del CMS
-h, --help Muestra esta ayuda
Commands:
db:generate [options] Escribe .cms/ desde cms.config.ts y avisa
si hay deriva
db:migrate [options] Ejecuta db:generate y después drizzle-kit
generate
db:apply [options] Aplica las migraciones pendientes a D1, en
local salvo --remote
db:pop [options] Borra la última migración generada
deploy [options] Prepara el repositorio, aplica las
migraciones remotas y abre el dashboard
doctor [options] Diagnostica el cableado del proyecto y dice
cómo arreglar cada fallo
secret:set [options] [nombre] Genera el secreto de sesión y lo guarda en
.dev.vars, o en el Worker con --remote
resources:create [options] Crea la D1, el bucket de R2 y los dos KV
que falten, y wrangler escribe los ids
resources:list [options] Dice qué binding tiene id y cuál sigue
siendo solo local
init [options] [dir] Crea un proyecto Astro en Cloudflare con
Kevin CMS ya cableado
update [options] Actualiza el CMS, enseña lo que rompe y
alinea las peers de la versión destino
settings:reset-site-url [options] Quita el siteUrl guardado en KV para
recuperar el acceso al panel

db:generate lleva el prefijo aunque no toque la base de datos: un solo grupo se lee mejor que un grupo más una excepción suelta.

Lee tu configuración y escribe .cms/schema.ts y .cms/types.d.ts.

Usage: cms db:generate [options]
Escribe .cms/ desde cms.config.ts y avisa si hay deriva
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
--watch Regenera cada vez que cambia el config
--check Convierte la deriva en un fallo y sale con código 1
-h, --help Muestra esta ayuda

Una ejecución normal:

✔ Cargando config
✔ Emitiendo .cms/
3 colecciones desde /mi-sitio/cms.config.ts
/mi-sitio/.cms/schema.ts
/mi-sitio/.cms/types.d.ts

Con globals declarados la línea los cuenta aparte —3 colecciones y 1 globals—, y aun así schema.ts sale igual: un global vive en KV y no genera tabla.

No pregunta nada, así que sin terminal se comporta exactamente igual que con ella.

Se queda escuchando y regenera cada vez que guardas el config. Útil si trabajas sin astro dev; con él no hace falta, porque la integración ya lo vigila.

No genera migraciones. Nunca.

Para integración continua. Igual que sin flag, pero si hay deriva sale con código 1 en vez de limitarse a avisar:

El schema ha cambiado desde la última migración:
+ posts.views (integer)
Ejecuta `cms db:migrate` para generar la migración.

Compara contra los snapshot.json de migrations/, que están commiteados, así que funciona sobre un clon limpio. Sin ninguna migración todavía no hay nada que comparar y pasa.

Ventana de terminal
pnpm cms db:generate --config ./config/cms.config.ts

Sin este flag busca en el directorio actual, en este orden: cms.config.ts, cms.config.js, cms.config.mjs. Si no encuentra ninguno, el error te dice los tres nombres que buscó y dónde miró.

.cms/ se escribe junto al config, no en el directorio desde el que lanzas el comando, para que los tipos queden donde su tsconfig.json los va a buscar.

Todos los comandos de esta página aceptan --config con este mismo significado, y por eso no se repite en cada sección: lo que resuelve no es solo dónde está el config, sino cuál es la carpeta del proyecto sobre la que van a trabajar.

Usage: cms db:migrate [options]
Ejecuta db:generate y después drizzle-kit generate
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
-h, --help Muestra esta ayuda

Regenera .cms/ y después llama a drizzle-kit, que escribe el SQL en migrations/. Si la migración va a ser destructiva —recrear una tabla, borrar una columna— pide confirmación antes.

Ahí se para. Aplicarla es db:apply, y es un comando aparte a propósito.

drizzle-kit lo instalas tú, con la versión que el CMS declara como peer (pnpm add -D drizzle-kit@1.0.0-rc.4). La CLI lo lanza como subproceso y te pasa su salida y su código tal cual, así que lo que ves es drizzle-kit hablando, sin intermediarios. Si no está instalado, el error te da el comando exacto en vez de un ENOENT.

Sin terminal, la confirmación de una migración destructiva se da por aceptada y el SQL se genera igual. Sigue siendo seguro —escribe un fichero, no lo aplica—, pero es una razón más para leer el SQL antes de db:apply.

Usage: cms db:apply [options]
Aplica las migraciones pendientes a D1, en local salvo --remote
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
--db <binding> Binding de la base D1, en vez del primero de wrangler.jsonc
--remote Aplica en producción en vez de en local
-h, --help Muestra esta ayuda
Ventana de terminal
pnpm cms db:apply # local
pnpm cms db:apply --remote # producción, tras confirmar
Aplicando en local sobre el binding DB
🚣 8 commands executed successfully.

Aplica lo que ya haya en migrations/. No genera nada: si encuentra deriva sin migrar, te lo dice antes de aplicar y sigue con lo que hay. Encadenar generar y aplicar en un solo comando es justo lo que no quieres que ocurra sin querer.

No hace falta que le digas cuál es. Lee tu wrangler.jsonc —o wrangler.json, o wrangler.toml— y usa el binding de la primera D1 que encuentre, que es el mismo nombre con el que tu código habla con ella (env.DB).

Con varias bases declaradas usa la primera y te avisa de cuál eligió. --db <binding> manda sobre todo eso:

Ventana de terminal
pnpm cms db:apply --db ANALYTICS
Usage: cms db:pop [options]
Borra la última migración generada
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
--db <binding> Binding de la base D1, en vez del primero de wrangler.jsonc
-h, --help Muestra esta ayuda

Deshace la última migración generada: borra su directorio con el migration.sql y el snapshot.json. Para el caso que pasa de verdad — generas, lees el SQL, no te convence, y lo quieres fuera antes de que lo vea nadie más.

Borrada la migración 20260730051522_smooth_exiles
La línea base de la deriva vuelve a la migración anterior.

Después de un pop, db:generate vuelve a avisarte de la deriva que esa migración cubría, porque la comparación pasa a hacerse contra la migración anterior. El ciclo queda como si nunca la hubieras generado.

Sólo mira la base local. Comprobar remoto metería una llamada de red en cada pop, y quien haya aplicado en producción lo que ahora quiere tirar tiene un problema más gordo que este aviso.

Sin ninguna migración que borrar, te lo dice y sale con 0.

Usage: cms deploy [options]
Prepara el repositorio, aplica las migraciones remotas y abre el dashboard
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el
directorio actual
--repo <owner/nombre> Nombre del repositorio a crear, en vez del name de
wrangler.jsonc
--public Crea el repositorio público en vez de privado
--no-migrate No aplica las migraciones remotas antes de empujar
--no-open No abre el dashboard en el navegador, solo imprime la
URL
--yes No pregunta: acepta las confirmaciones de este comando
-h, --help Muestra esta ayuda

Deja el proyecto listo para que lo despliegue Workers Builds, en este orden: el diagnóstico de cms doctor como puerta, los ids de los bindings, el .npmrc, gh y git en el PATH con sesión de GitHub iniciada, las migraciones remotas, el repositorio en GitHub y el primer push, y al final el dashboard abierto con los cuatro clics que quedan escritos en la terminal.

Esas cuatro comprobaciones van antes de la primera migración remota, así que en una máquina sin gh el comando para sin haber tocado nada.

El paso que este comando no puede hacer por ti es conectar el repositorio con Workers Builds: es una GitHub App con OAuth y no hay API ni CLI para ello (workers-sdk#12058). Así que lo escribe en vez de disimularlo.

El nombre del repositorio a crear. Sin él se usa el name del wrangler.jsonc. Si el proyecto ya tiene un origin, se empuja ahí y este flag se ignora avisando.

El repositorio se crea privado salvo que pases esto.

Salta cms db:apply --remote. El aviso sale dos veces —antes de empujar y en el resumen— porque el build de Cloudflare no aplica migraciones: si las saltas aquí, las aplicas tú antes de que ese push despliegue.

No abre el navegador; la URL del dashboard se imprime igual.

Acepta las confirmaciones de este comando: el repositorio que engloba al proyecto, el git add -A y la creación del repositorio en GitHub. No silencia la confirmación de PRODUCCIÓN de db:apply --remote: esa la pone apply, y deploy no la puede tocar.

Al terminar imprime los cuatro clics, el repositorio, el Worker y si las migraciones remotas se aplicaron o se saltaron. Sin terminal, todas sus confirmaciones se dan por aceptadas: un cms deploy en un pipeline crea el repositorio y empuja sin preguntar.

Usage: cms doctor [options]
Diagnostica el cableado del proyecto y dice cómo arreglar cada fallo
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
--fix Arregla lo trivial: las líneas que falten en .gitignore y el
include del tsconfig.json
-h, --help Muestra esta ayuda

Lee el proyecto de disco y dice qué está bien, qué está degradado y qué está roto, con el comando o la edición exacta para salir de cada fallo. No toca la red ni lanza ningún proceso, y por eso init, deploy y update lo usan como puerta antes de su propio trabajo: es la misma función, no una copia.

✔ Node 24.18.0 (se pide >= 24).
✔ drizzle-orm 1.0.0-rc.4 satisface 1.0.0-rc.4.
✔ Binding SESSION declarado en kv_namespaces.
⚠ El id de DB, SESSION, KV parece un marcador de posición, no un id real.
→ Ejecuta `cms resources:create`: wrangler escribe los ids él mismo en "/mi-sitio/wrangler.jsonc". Si ya existen, cópialos del panel de Cloudflare.
✔ nodejs_compat en compatibility_flags.
✔ assets.directory sirve ./dist/client.
✔ El esquema y la última migración coinciden.
31 comprobaciones: 28 bien, 2 avisos, 1 errores.
Arregla los errores de arriba y vuelve a ejecutar `cms doctor`.
  • ok: eso está como tiene que estar.
  • warn: funciona, pero algo está degradado o no se ha podido comprobar. No cambia el código de salida.
  • error: está roto o es inseguro. Un solo error deja el código de salida en 1, y es lo que para el preflight de cms deploy.

Que SESSION falte y que tu config esté en TOML no son el mismo problema: el primero rompe en silencio y pierde datos, el segundo solo limita lo que la CLI puede hacer por ti. Por eso hay tres niveles y no dos.

Cada check tiene un id estable en kebab-case —node, peer-drizzle-orm, ids, migrations-pattern, compat-flags, assets-directory, tsconfig-include, astro-config, npmrc-token, npmrc-ignored, gitignore, dev-vars, drift…—. El de npmrc-token es el que vigila que no haya un token de GitHub escrito literalmente en un .npmrc, que es el fichero que Despliegue manda commitear.

Toca dos cosas y solo dos, y las dos son reversibles de un vistazo:

  • Las líneas que le falten a tu .gitignore (.cms y .dev.vars).
  • La entrada ./.cms/types.d.ts en el include del tsconfig.json.

Sin nada que arreglar lo dice y no escribe nada. Lo que no puede tocar —un tsconfig.json que no parsea, un fichero sin permisos— lo dice en amarillo y te deja la edición a mano.

No pregunta nada, así que sin terminal se comporta igual que con ella.

Usage: cms secret:set [options] [nombre]
Genera el secreto de sesión y lo guarda en .dev.vars, o en el Worker con
--remote
Arguments:
nombre Nombre de la variable, BETTER_AUTH_SECRET por defecto
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
--remote Lo sube al Worker de producción en vez de escribir .dev.vars
--force Rota el secreto que ya existe, invalidando las sesiones
abiertas
--yes No pregunta antes de rotar o de subir a producción
-h, --help Muestra esta ayuda

Genera BETTER_AUTH_SECRET —la única variable de entorno que el CMS necesita— con randomBytes(32) de node:crypto en base64. La misma fuerza que los 32 bytes que antes había que sacar de OpenSSL a mano, sin depender de que haya ese binario en la máquina ni de que nadie copie bien 44 caracteres.

Sin --remote escribe el .dev.vars que hay junto al cms.config.ts, conservando byte a byte todo lo demás —comentarios y claves ajenas incluidos—, y lo crea con modo 0600 si no existía:

Secreto BETTER_AUTH_SECRET escrito en /mi-sitio/.dev.vars
El valor no se imprime a propósito. `.dev.vars` no se commitea: compruébalo antes de subir nada.

Si la clave ya está puesta no la pisa: sale nombrando --force. Una clave presente pero vacía cuenta como ausente y se rellena sin más.

Rota el secreto que ya existe. Rotar invalida todas las sesiones abiertas —hay que volver a entrar en /admin—, que es exactamente lo que quieres si sospechas una filtración. Pide confirmación antes, y solo cuando hay algo que invalidar.

Sube el secreto al Worker de producción con wrangler secret put, y no toca el .dev.vars: local y remoto son dos secretos distintos a propósito, porque una sesión abierta contra wrangler dev no tiene por qué valer contra producción.

El valor viaja por la entrada estándar, nunca en el argv: en la línea de comandos quedaría en la tabla de procesos y en el historial del shell para siempre.

Da por aceptada la confirmación, la de rotar y la de subir a producción.

Sin terminal las confirmaciones se dan por aceptadas: un secret:set --remote en un pipeline sube un secreto nuevo y tira todas las sesiones sin preguntar.

Usage: cms resources:create [options]
Crea la D1, el bucket de R2 y los dos KV que falten, y wrangler escribe los ids
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
--name <prefijo> Nombre de la base y del bucket, en vez del "name" de
wrangler.jsonc
--yes No pregunta antes de crear
-h, --help Muestra esta ayuda

Mira cuáles de los cuatro bindings que el CMS necesita —DB, R2, KV y SESSION— faltan, y crea solo esos, en serie. Es idempotente: lo que ya existe no se crea dos veces.

Un wrangler.toml no se puede tocar: el comando para diciendo que pases el fichero a wrangler.jsonc —el mismo contenido en JSON, con comentarios— en vez de reescribir un formato que no sabe editar.

Si un create choca con un recurso que ya existe —lo habitual en un segundo proyecto de la misma cuenta, donde el título de un KV es único—, la salida de wrangler llega tal cual, debajo aparece el trozo exacto a pegar, y sigue con los demás. Al terminar dice qué quedó sin id y sale con 1.

Cómo se llamarán la base y el bucket. Sin él se usa el name del wrangler.jsonc; si el fichero ya declara un database_name propio, ese se respeta. Los dos namespaces de KV conservan siempre su título (KV y SESSION).

Da por aceptada la confirmación de crear los recursos.

Sin terminal esa confirmación se da por aceptada y los recursos se crean.

Usage: cms resources:list [options]
Dice qué binding tiene id y cuál sigue siendo solo local
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
--remote Añade la salida cruda de wrangler d1/kv/r2 list
-h, --help Muestra esta ayuda
DB local
R2 kevin-cms-demo (declarado, sin comprobar)
KV local
SESSION local
«local» significa que ese recurso todavía no existe en Cloudflare: el proyecto funciona con `wrangler dev` y `cms db:apply`, pero no se puede desplegar. Créalos con `cms resources:create`.
«declarado, sin comprobar» es el bucket de R2: su identidad es su propio nombre, así que el fichero dice cómo se llama, pero no si existe en tu cuenta. Míralo con `cms resources:list --remote`, o ejecuta `cms resources:create`: lo crea si falta y no hace nada si ya está.

local no es un fallo, es un estado: un proyecto cuyos bindings no llevan id corre perfectamente bajo wrangler dev y cms db:apply —el modo local no necesita ids—, solo que todavía no se puede desplegar. Un binding que ni siquiera aparece en el fichero sale como sin declarar.

La fila de R2 no dice lo mismo que las otras tres: lo que enseña no es un id que Cloudflare haya emitido, sino el bucket_name que hay en el wrangler.jsonc —arriba, kevin-cms-demo—, y ese nombre está ahí exista o no el bucket. Por eso sale con el (declarado, sin comprobar) a cuestas: sin --remote este comando no toca la red, y sin red no hay forma de saberlo. Quien lo sabe es tu cuenta:

Ventana de terminal
cms resources:list --remote # la lista de buckets, debajo de la tabla
cms resources:create # lo crea si falta, y no hace nada si ya está

Añade debajo la salida cruda de wrangler d1 list, wrangler kv namespace list y wrangler r2 bucket list, cada una precedida de su orden, para comparar a ojo. Nada de eso se parsea.

Sin --remote este comando no toca la red: lee tu wrangler.jsonc y nada más. No pregunta nada, así que sin terminal se comporta igual.

Usage: cms init [options] [dir]
Crea un proyecto Astro en Cloudflare con Kevin CMS ya cableado
Arguments:
dir Directorio del proyecto, por defecto el actual
Options:
--cloud Crea los recursos en Cloudflare (D1, R2 y los dos KV)
--local No toca tu cuenta de Cloudflare; los recursos se crean
después
--name <nombre> Nombre del Worker y de los recursos, en vez del del
directorio
--pm <gestor> pnpm, npm, yarn o bun, en vez de deducirlo del lockfile
--skip-create No invoca create-cloudflare aunque no haya proyecto Astro
--no-install No instala dependencias
--no-migrate No genera ni aplica migraciones
--yes No pregunta nada (no responde por ti la pregunta del modo)
-h, --help Muestra esta ayuda

Los catorce pasos manuales de la instalación en un comando. Compone las capas que ya existen —resources:create, secret:set, db:migrate y db:apply— y añade lo único que no estaba: invocar create-cloudflare, invocar astro add, y escribir las plantillas.

Es el único comando que se sirve también desde otro paquete: pnpm create @kevolution-co/cms —y npm, yarn y bun create— resuelve @kevolution-co/create-cms, cuyo binario create-cms es este mismo init, con los mismos flags y construido desde la misma fuente. Existe porque pnpm dlx @kevolution-co/cms init no llegaba a arrancar: dlx instala las peers de la librería —astro, wrangler, drizzle-kit— y esas traen esbuild y workerd con build scripts que pnpm ≥ 10 se niega a ejecutar sin aprobación, cosa que en un dlx nadie puede dar. create-cms no declara peers y su árbol no tiene un solo build script. Los demás comandos no lo necesitan: se ejecutan desde el proyecto, y ahí la aprobación de los build scripts sí tiene dónde declararse —el allowBuilds del pnpm-workspace.yaml de la raíz, que create-cloudflare deja escrito—, cosa que en la caché de un dlx no existe.

La única diferencia entre los dos modos es el paso de crear recursos, y nada más: con --cloud se crean la D1 y los dos namespaces de KV en tu cuenta; con --local no se toca tu cuenta de Cloudflare y los subes después con cms resources:create. El bucket de R2 entra en los dos igual que los otros tres: quien dice si existe es tu cuenta, no el fichero — ver el aviso de resources:create.

El nombre del Worker y de los recursos. Sin él se usa el del directorio, y tiene que valer para Cloudflare: minúsculas, dígitos y guiones, sin empezar ni acabar en guion y como mucho 63 caracteres.

pnpm, npm, yarn o bun. Sin él se deduce del lockfile que haya, y pnpm cuando no hay ninguno.

No invoca create-cloudflare aunque el directorio no sea un proyecto Astro. Es lo que se usa para cablear un proyecto Astro que ya existe.

Saltan la instalación de dependencias y el ciclo db:migratedb:apply local. Lo saltado sale en la lista del final.

No pregunta nada… con una excepción, que es la única de toda esta CLI: no responde por ti la pregunta del modo. Ver abajo.

Al terminar imprime lo escrito, lo saltado, los avisos de lo que quedó a medias, y cómo arrancar. Ningún aviso aborta lo que quedaba por hacer, pero no todos cuentan igual para el código de salida, y la diferencia dice de qué tipo es el problema:

  • Un paso que el comando intentó y falló deja el código en 1: los recursos de Cloudflare, el secreto de sesión, las migraciones.
  • Un paso que el comando decidió no tocar por no arriesgarse deja el código en 0: un astro.config.mjs cuya forma no reconoce, el include de tu tsconfig.json, un wrangler.toml, un wrangler.jsonc que no se puede leer como JSON, un assets.directory que apunta a otro sitio. Ahí el aviso trae debajo el bloque exacto a pegar.

Así que un cms init con avisos amarillos puede salir con 0, y de hecho es lo normal sobre un proyecto Astro preexistente. Si lo llamas desde un pipeline o desde un agente, la puerta es cms doctor —que sí sale con 1 cuando algo está en rojo—, no el código de init.

Usage: cms update [options]
Actualiza el CMS, enseña lo que rompe y alinea las peers de la versión destino
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
--to <version> La versión exacta a la que ir, en vez de la última del
registro
--dry-run Enseña el salto y el plan de peers sin tocar nada
--no-regen No regenera .cms/ al terminar
--yes No pregunta: instala y se queda con la versión
-h, --help Muestra esta ayuda

Actualiza @kevolution-co/cms, enseña lo que rompe antes de que te quedes con la versión, y alinea las peers que la versión destino declara. La página con el detalle es Actualizar.

De dónde sale cada dato, que es lo que importa:

  • La versión instalada es la del proyecto, no la del binario que corre: con pnpm dlx @kevolution-co/cms update son distintas.
  • La versión destino y sus peers salen del registro, que es lo único que sabe qué exige una versión que todavía no tienes. Se pregunta con npm view, porque npm viene con Node y lee el mismo .npmrc que autentica contra GitHub Packages.
  • Lo que rompe sale del CHANGELOG.md del paquete ya instalado: el repositorio es privado y un token con solo read:packages no puede leerlo de ninguna otra forma.
@kevolution-co/cms 0.7.0 → 0.8.0
drizzle-orm 1.0.0-rc.4 → 1.0.0-rc.5
drizzle-kit 1.0.0-rc.4 → 1.0.0-rc.5 (-D)

Son dos a propósito, y la segunda llega después de enseñar el CHANGELOG: se instala, se lee lo que rompe, y solo entonces se pregunta si te la quedas. Decir que no restaura el package.json byte a byte —el rango que tenías escrito, ^0.7.0 y no 0.7.0, el orden de las claves y la indentación— y reinstala. El lockfile no se restaura.

Una versión exacta, no un rango ni una etiqueta. Es también la forma de bajar de versión: sin él el comando no baja nunca por su cuenta, porque la latest del registro puede ser más vieja que una precandidata instalada.

Enseña el salto y el plan de peers y no escribe nada: lo único que corre es el npm view, que es de lectura. Es el modo para un pipeline.

No regenera .cms/ al terminar. Sin el flag, se regenera lanzando el binario recién instalado, nunca en este proceso: Node no recarga un módulo ESM porque sus bytes hayan cambiado en disco, y el codegen viejo emitiría un .cms/ que no corresponde a la versión del manifiesto.

Acepta las dos confirmaciones: instala y se queda con la versión.

Las migraciones no las ejecuta este comando. Si el CHANGELOG habla del esquema, lo avisa y te manda al ciclo de siempre: cms db:migrate, leer el SQL, cms db:apply.

Usage: cms settings:reset-site-url [options]
Quita el siteUrl guardado en KV para recuperar el acceso al panel
Options:
--config <ruta> Ruta al cms.config.ts, en vez de buscarlo en el directorio
actual
--kv <binding> Binding del namespace de KV, en vez del llamado "KV" de
wrangler.jsonc
--remote Opera sobre el KV de producción en vez del local
-h, --help Muestra esta ayuda

Borra el siteUrl guardado en KV —el ajuste del sitio del que salen los enlaces de los correos y contra el que better-auth valida—, para recuperar el acceso al panel cuando el valor guardado apunta a una URL a la que no llegas. Sin siteUrl, el CMS vuelve a deducirlo del origen de cada petición.

Limpia los dos sitios donde puede estar: su clave propia, settings:site:siteUrl, y la entrada siteUrl dentro del blob antiguo settings:site —del que solo se quita esa entrada; el resto se escribe de vuelta tal cual—.

Quitando siteUrl en local sobre el binding KV
siteUrl quitado: vuelve a deducirse del origen de cada petición.
Ojo: cada isolate que ya esté sirviendo —incluido un wrangler dev en marcha— conserva el siteUrl viejo en su caché hasta 60 s. El panel vuelve a responder en torno a un minuto, no al instante.

Sin ningún siteUrl guardado lo dice, no escribe nada y sale con 0.

El namespace sobre el que operar. Sin él se usa el que tu wrangler.jsonc llama KV.

Opera sobre el KV de producción. Como db:apply --remote, exige el flag más la confirmación: dos cosas a propósito antes de escribir en un KV vivo. Sin terminal esa confirmación se da por aceptada.

Situación Salida Código
Sin argumentos Ayuda 0
-h, <comando> -h Ayuda 0
Comando desconocido El comando que no existe, y la ayuda 1
db:generate correcto, con o sin deriva Resumen de lo escrito 0
db:generate --check con deriva El listado de la deriva 1
Config no encontrado, o config inválido El error, con el motivo 1
db:migrate con drizzle-kit fallando Lo que imprima drizzle-kit El suyo
db:apply con wrangler fallando Lo que imprima wrangler El suyo
db:apply --remote cancelado en el prompt Aviso de cancelación 0
db:pop correcto, o sin nada que borrar Qué borró, o que no había nada 0
Binding no resoluble y sin --db El error, nombrando los ficheros que buscó 1
doctor con algún check en error El diagnóstico entero, y el recuento 1
doctor sin errores, con o sin avisos El diagnóstico entero, y el recuento 0
init con un error de validación El error, y ningún fichero escrito 1
init con create-cloudflare o la instalación fallando Lo que imprima el proceso El suyo
init con un paso que intentó y falló: recursos, secreto, migraciones El aviso, sin abortar lo que quedaba 1
init con un paso que decidió no tocar: astro.config, tsconfig, un wrangler que no sabe editar El aviso, con el bloque exacto a pegar 0
deploy sin .npmrc, o con el token escrito dentro El error, antes de lanzar ni un proceso 1
deploy con el .npmrc ignorado por git El error, después del git init y de las migraciones remotas 1
deploy con las migraciones remotas fallando El error, sin haber creado ni empujado nada El de wrangler
secret:set --remote con wrangler fallando Lo que imprima wrangler, y qué comprobar El suyo
resources:create dejando algún recurso sin id El estado final, y qué pegar 1
update --dry-run El salto y el plan de peers 0
update cancelado en cualquiera de las dos confirmaciones Qué se restauró 0
update con db:generate fallando después de instalar El aviso: la versión quedó instalada 1
Una confirmación rechazada, en cualquier comando Aviso de cancelación 0

Pedir ayuda no es un error: sin argumentos sale con 0. Equivocarse de comando sí. Cancelar un prompt tampoco: dijiste que no y se te hizo caso.

Eso vale, tal cual, para la confirmación de una migración destructiva en db:migrate, para la de PRODUCCIÓN de db:apply --remote, para la de db:pop sobre una migración ya aplicada, para la de secret:set --remote y para las dos de update — y de esas dos conviene decirlo explícito: un cms update en un pipeline instala y se queda con la versión, porque nadie está ahí para leer el CHANGELOG y decir que no. El modo de CI de ese comando es --dry-run, que no escribe nada.

cms init es la única excepción deliberada. Sin TTY —y también con --yesexige --cloud o --local, y sin ninguno de los dos sale con 1 sin haber escrito ni un fichero:

no puedo preguntarte si crear los recursos en Cloudflare, así que no lo adivino. Vuelve a ejecutarlo con `--cloud` … o con `--local` … No se ha escrito ningún fichero.

El porqué: crear recursos de pago en una cuenta ajena por omisión no es lo mismo que aplicar una migración que el usuario ya generó. Una confirmación asumida es «sigue con lo que te pedí»; aquí no hay nada pedido que seguir, y la única respuesta por omisión posible costaría dinero.

Lo mínimo que merece la pena en un pipeline:

- run: pnpm cms db:generate --check

Falla la build si alguien cambió cms.config.ts y se olvidó de generar la migración, que es el despiste que más caro sale después.