Operación de MarkeUP: comandos de Hermes y troubleshooting diario
Los comandos de gestión del vault con Hermes Agent, los scripts principales del proyecto, el troubleshooting más habitual y el flujo típico de publicación diaria.
Navegando en el blogver navegación en todos los blogs
Orden de lectura recomendado: doc-readme → doc-guia → doc-arquitectura → doc-buscador → doc-operacion → doc-publicar → doc-auditoria
Hermes Agent es el asistente IA que gestiona el vault. Corre sobre Ollama (modelo qwen2.5-coder:7b-64k). El sistema de reglas y skills vive en src/content/hermes/.
# Iniciar Hermes (requiere Ollama corriendo)
hermes chat
# Estado del sistema
hermes status
| Comando | Skill usado | Resultado |
|---|---|---|
Crea un post sobre [tema] en [categoría] | crear-post.md | Post nuevo con frontmatter 22 campos + wikilinks |
Amplía [slug] con [tema] | ampliar-post.md | Sección nueva en nota existente |
Revisa los wikilinks de [slug] | generar-wikilinks.md | Escanea y enriquece enlaces `[[slug |
Audita el vault | auditar-vault.md | Informe de salud con huérfanos y recomendaciones |
Revisa la documentación | documentar.md | Verifica que los 5 docs reflejen la realidad |
¿Qué posts debería crear? | — | Consulta memory/backlog.md (22 ideas priorizadas) |
| Error | Causa | Solución |
|---|---|---|
| Hermes no responde | Ollama no está corriendo | ollama serve o brew services start ollama |
model not found | Modelo no descargado | ollama pull qwen2.5-coder:7b-64k |
Error Connection refused | Ollama en otro puerto | Verificar ~/.hermes/config.yaml: base_url: http://127.0.0.1:11434/v1 |
| Hermes lento | Modelo pesado para el hardware | Cerrar otras apps. ollama ps para ver uso |
npm run dev # Servidor local en localhost:4321 con hot reload
npm run dev -- --host # Servidor accesible desde otros dispositivos en la red
npm run dev:tunnel # Dev server en la LAN + túnel HTTPS: devuelve los dos enlaces
npm run preview # Previsualiza el build de producción localmente
npm test # Ejecuta tests unitarios (Vitest, 6 tests)
npm run test:watch # Tests en modo watch (re-ejecuta al guardar)
npm run dev:tunnel es la vía cómoda para probar en el móvil o enseñar el sitio desde otra red:
levanta el dev server en 0.0.0.0 y un túnel HTTPS hacia el mismo puerto, y devuelve dos enlaces
(el de la WiFi y el público). Se para con npm run dev:tunnel:stop, se consulta con npm run dev:tunnel:status (exit 0 = sigue sirviendo, y sirve como gate de un /goal) y se diagnostica con
npm run dev:tunnel:setup. Guía de uso en llano: Dev tunnel; manual del repo:
docs/dev-tunnel.md.
npm run build # Build completo: images + astro + pagefind
npm run build:astro # Solo build de Astro (HTML estático)
npm run build:search # Regenerar el índice Pagefind (solo artículos, --force-language es)
El build completo genera ~380 páginas HTML en dist/ y, de forma declarativa:
/llms.txt— lista de páginas y posts para consumidores LLM (se regenera en cada build)./api/posts.json— manifiesto ligero con slug/título/descripción/metadatos/minutos (usado por rich previews y agentes)./faq,/privacy,/terms,preview.jsyrich.cssse generan/complían automáticamente en su ruta pública.
Los rich tooltips y rich cards leen /api/posts.json en tiempo de ejecución (fetch perezoso en el navegador). No requieren servidor extra.
npm run build:astro
node scripts/mjs/a11y-check.mjs # auditoría básica de accesibilidad en dist/
npm run validate:share # OG + Twitter Cards + URLs de compartir en dist/
python3 -m json.tool dist/api/posts.json >/dev/null # valida el manifiesto JSON
npm run images # Procesa imágenes: crop 1920×1080 + variantes responsivas + WebP
npm run images:watch # Modo vigilancia continua
npm run images -- --force # Reprocesa todas (ignora caché)
npm run compress-audio # Procesa pendientes/incorrectos
npm run compress-audio:watch # Modo vigilancia
npm run compress-audio:force # Recomprime todos
npm run media:fetch # Descarga los pósteres de los embeds (public/img/media/)
npm run films:fetch # Fichas de película desde TMDB (necesita TMDB_TOKEN)
npm run validate:media # Gate de medios embebidos: bloques, archivos y CSP
npm run content:cache # Invalida la caché de render de Astro (inputs externos)
compress-audio.js es idempotente: solo convierte lo que falta o lo que no
cumple los valores objetivo (≤ 24 MB, 24-120 kbps, ≤ 2 canales). Un MP3 que
supera el límite o que quedó con bitrate degradado se detecta y se rehace; los
que ya están bien se omiten sin reescribirse.
Límite real de Cloudflare Pages: 25 MiB por archivo. El default es 24 MB por
margen. Si un audio no cabe ni en el escalón más bajo (24k mono), el script lo
avisa con las opciones disponibles (trocear, subir --max-size, o alojar
fuera). Cloudflare Pages no soporta range requests, así que el reproductor
descarga el archivo completo antes de sonar: conviene mantener los MP3 pequeños.
Seguridad de los originales. El archivo fuente (.m4a, .wav…) se borra
solo después de que la salida haya pasado la validación. Si la conversión
falla o el resultado no es válido, el original y el MP3 anterior quedan
intactos. Usa --keep-originals para conservarlos siempre.
Salida con código 1 si algún audio no cumple (útil en CI).
node scripts/js/compress-audio.js --dry-run # Informa sin escribir
node scripts/js/compress-audio.js --bitrate 96k # Bitrate objetivo (ABR)
node scripts/js/compress-audio.js --max-size 24 # Límite en MB
node scripts/js/compress-audio.js --keep-originals # No borra los originales
node scripts/js/compress-audio.js --help # Ayuda
Un --max-size o --bitrate inválido aborta con error, no se ignora: un
valor no numérico produciría NaN y desactivaría en silencio todas las
comprobaciones de tamaño, dando por buenos audios que romperían el despliegue.
Los reproductores ya no se escriben a mano: un audio o un vídeo es un bloque
cercado (audio, video, embed, book, film) que el plugin remark-media
convierte en HTML, con su ficha de título, pie y descarga. npm run validate:media
es el gate: comprueba que el archivo exista, que viva en una carpeta pública
permitida, que no pase de 25 MiB, que el frame-src de la CSP cuadre con los
proveedores admitidos y que no haya más de cuatro embeds de terceros por nota. La
sintaxis de cada bloque, con sus opciones y sus límites, está en la
guía de medios embebidos.
validate:media cubre también el HTML escrito a mano: cualquier <audio> o <source>
crudo que aparezca en una nota se comprueba igual (existe el archivo, carpeta permitida,
límite de 25 MiB). Antes había un validate:audio solo para eso; se retiró el 2026-10-03
porque era la misma comprobación repetida. Sin ella, un reproductor crudo roto no da
error visible: el control aparece, se pulsa play y no suena nada, que es justo lo que
delata el gate.
Los scripts están organizados en scripts/ por extensión, para localizarlos rápido:
scripts/
├── sh/ ← Bash (lib/common.sh = utilidades compartidas)
├── py/ ← Python
├── js/ ← JavaScript
└── mjs/ ← ES Modules (.mjs)
| Script | Propósito |
|---|---|
js/process-images.js | Crop 1920×1080 + 3 tamaños WebP (1920/1280/640, calidad 82) |
js/sync-images.js | Limpiador de imágenes huérfanas (public/img/). Dry-run por defecto; --apply borra; --include-sources incluye fuentes |
mjs/og-cards.mjs | Genera tarjetas Open Graph 1200×630 (PNG) por post |
mjs/validate-share.mjs | Audita OG + Twitter Cards + URLs absolutas sobre dist/ (bloqueante CI) |
mjs/validate-slugs.mjs | Detecta colisiones de slug/alias entre posts (bloqueante CI) |
mjs/validate-wikilinks.mjs | Valida sintaxis y estilo de los wikilinks |
mjs/validate-icons.mjs | Verifica que el mapa de iconos coincida con los tipos |
mjs/a11y-check.mjs | Accesibilidad estática sobre dist/ (h1, labels, alt, noopener…) |
js/compress-audio.js | Compresión ffmpeg (límite 25MiB Cloudflare) |
mjs/validate-media.mjs | Gate de medios embebidos: bloques embed/audio/video/book/film, archivos locales, CSP ↔ proveedores (bloqueante) |
mjs/fetch-media.mjs | Descarga los pósteres de los embeds a public/img/media/ (media:fetch) |
mjs/fetch-films.mjs | Puebla la caché de fichas de película desde TMDB (films:fetch; necesita TMDB_TOKEN) |
mjs/content-cache.mjs | Invalida la caché de render de Astro cuando cambia un input externo a las notas (content:cache) |
js/export-blog.js | Exportar posts por categoría |
js/dev-search.js | Índice Pagefind + copia del grafo de búsqueda para desarrollo (npm run dev:search) |
sh/hermes-connect.sh | Nuevo — Arranca Hermes + su gateway de Telegram |
sh/telegram-connect.sh | Terminal del Mac por Telegram (npm run term): bot propio, init, check, install, logs, policy |
py/telegram-terminal.py | El bot de la terminal: ejecuta shell en el Mac desde el móvil, con política de seguridad |
py/enrich-wikilinks.py | Enriquece wikilinks (npm run wikilinks:enrich) |
py/subset-fonts.py | Subset de fuentes a WOFF2 (npm run fonts:subset) |
sh/publish.sh | Publica: valida, commit y push |
sh/auto-commit.sh | Sincronización automática (imágenes → commit → push) |
sh/syncthing-connect.sh | Conecta y gestiona Syncthing |
npm run test # tests unitarios (vitest)
npm run validate:slugs # slugs/aliases únicos
npm run validate:share # OG + Twitter Cards + URLs de compartir
npm run test:a11y # accesibilidad sobre dist/
npm run sync:images # imágenes huérfanas (dry-run)
npm run prettier # formatea el código
npm run lint # eslint
La generación automática de artículos ya no la hace un script suelto: la hace
Hermes Agent con el atajo /markeup-post (contexto, frontmatter, cuerpo,
enlaces, medios, imagen, SEO y validación). Así el resultado pasa por el mismo pipeline validado
que el resto del vault.
npm run hermes # abre Hermes (o usa el bot de Telegram)
Y le pides en lenguaje natural:
crea un post sobre <tema> en Máquina
amplía <slug> con <tema>
revisa los wikilinks de <slug>
| Parámetro | Valor | Nota |
|---|---|---|
model | qwen2.5-coder:7b-64k | Según ~/.hermes/config.yaml |
num_ctx | configurable | Un contexto alto consume mucha RAM |
temperature | 0.7 | — |
Hermes también puede escribir en el vault desde Telegram, usando las mismas skills. Ver la sección 6.1.
- Escribir/editar en Obsidian o pedírselo a Hermes
- Syncthing sincroniza al Mac
- Si hay imágenes nuevas:
npm run images - Verificar:
npm run build(debe dar 0 errores) - Publicar:
npm run publish -- -m "tipo(alcance): descripción"— valida (tests + slugs), construye, hace commit y sube a GitHub en un paso. Añade--dry-runpara simular sin cambios,--no-verifypara saltar validaciones. - GitHub Actions valida → Cloudflare Pages despliega automáticamente
Publicar desde Hermes (CLI o Telegram): existe la skill
markeup-deploy(y el atajo/markeup-publish, que además audita y corrige), que encapsula el push a GitHub y el despliegue a Cloudflare con validación previa y verificación posterior. Basta pedirle «haz push y deploy».
El script
scripts/sh/publish.shsustituye a los antiguosdeploy.shycommit-local.sh. No usawranglerporque Cloudflare Pages ya construye al recibir el push.
Los scripts de integración tienen nombres descriptivos: cada uno gestiona una conexión externa (los dos bots de Telegram y Syncthing) y todos sus comandos tienen un atajo npm equivalente.
Telegram tiene dos consumidores distintos, cada uno con su bot y su token (Telegram admite un solo proceso por token):
| Bot | Quién lo atiende | Para qué |
|---|---|---|
| El del agente | Gateway de Hermes (esta sección) | Hablar con el agente: crear posts, auditar el vault, usar sus skills |
| El de la terminal | Bot propio (npm run term, § 6.2) | Ejecutar comandos de shell en el Mac desde el móvil y leer la salida |
Al escribir al bot del agente hablas con Hermes, con acceso a todas sus skills
(markeup-content, markeup-audit, markeup-wikilinks…), memoria y
herramientas.
npm run hermes # arrancar (atajo)
npm run hermes -- check # diagnóstico
npm run hermes -- stop
npm run hermes -- restart
npm run hermes -- status
npm run hermes -- logs
npm run hermes -- setup # reconfigurar Telegram (solo si cambias de bot)
| Comando | Qué hace |
|---|---|
| (ninguno) | Arranca Hermes + Telegram |
start | Arranca el gateway y repara el servicio si está obsoleto |
check | Diagnóstico: binario, token, skills, conflictos y vault |
stop / restart | Ciclo de vida |
status | Gateway, Telegram y arranque automático |
logs | tail -f del log del gateway |
setup | Asistente de Hermes para reconfigurar plataformas |
No pide tokens. La configuración vive una sola vez en
~/.hermes/.env:
| Variable | Contenido |
|---|---|
TELEGRAM_BOT_TOKEN | Token del bot (de @BotFather) |
TELEGRAM_ALLOWED_USERS | IDs de Telegram autorizados |
⚠️ Telegram solo admite un proceso por bot. Si arrancas otro bot con el mismo token, el gateway se cae con
Conflict: terminated by other getUpdates. Por eso el token ya no está en el.envdel repo: la fuente única es~/.hermes/.env. El comandocheckdetecta este conflicto.
Reparación automática del servicio. Hermes instala un servicio launchd
(ai.hermes.gateway) que guarda la ruta absoluta de su intérprete. Si el
proyecto se mueve, esa ruta deja de existir y el servicio falla. start detecta
el caso y reinstala el servicio por su cuenta (hermes gateway install --force --start-now --start-on-login).
Contexto del vault. Para que Hermes conozca las reglas del repositorio
(categorías válidas, Regla de Oro de wikilinks, comandos de validación), existe
AGENTS.md en la raíz. Hermes lo carga automáticamente al trabajar aquí.
El bot de Hermes no ejecuta shell: te conecta con el agente. Para trabajar en el Mac desde el móvil (lanzar comandos, validar, mirar el log, comprobar el disco) hay un bot propio, con su token, que hace long-polling contra la API de Telegram: sin webhooks, sin puertos abiertos y sin depender de la nube.
npm run term -- init # crea el archivo de claves (chmod 600)
npm run term -- check --deep # token, API, política y selftest
npm run term -- install --keep-awake # servicio launchd (arranca al iniciar sesión)
npm run term -- status # ¿corriendo? ¿modo? ¿estado?
npm run term -- logs 200 # log del bot
| Dónde | Qué |
|---|---|
Claves (fuera del repo, permisos 600) | ~/.config/markeup/telegram-terminal.env |
| Bot (solo librería estándar de Python) | scripts/py/telegram-terminal.py |
Gestor (init, check, install, logs, policy) | scripts/sh/telegram-connect.sh |
| Estado y log | ~/.local/state/markeup/telegram-term/ |
En el chat, el texto libre se ejecuta en la shell y los atajos atienden el resto:
/status, /id, /cd, /pwd, /mode, /policy, /cancel y /acciones
(botones rápidos). Lo peligroso pide botón ✅/🚫; además hay denylist dura,
autorización por from.id (solo chats privados), timeout con grupo de procesos
propio, redacción de secretos en la salida y un solo comando en vuelo. sudo
no funciona (no hay TTY) y los comandos interactivos se cierran.
⚠️ El token de la terminal no puede ser el de Hermes: Telegram admite un solo
consumidor por token y el otro proceso recibe 409 Conflict. npm run term -- check compara los dos y avisa. El token se valida al pegarlo (init): el
fallo típico es pegarlo dos veces en el prompt silencioso, que Telegram contesta
con un 404 Not Found que parece un problema de red.
El manual completo (uso diario, referencia de comandos y variables, seguridad, mantenimiento y problemas típicos) vive en
docs/telegram-terminal.md, y la ayuda del día a día está en el propio chat (/help) y ennpm run term -- --help.
Syncthing sincroniza src/content/posts/ entre el Mac y Android (Obsidian)
de forma peer-to-peer.
npm run syncthing # detecta y arranca
npm run syncthing -- check # diagnóstico
npm run syncthing -- id # Device ID de este Mac
npm run syncthing -- folder # carpeta a compartir + pasos
npm run syncthing -- open # interfaz web (localhost:8384)
npm run syncthing -- install # instala con Homebrew (pregunta antes)
npm run syncthing -- install-service
| Comando | Qué hace |
|---|---|
| (ninguno) | Detecta el binario y arranca |
check | Diagnóstico (binario, config, proceso, servicio, interfaz, carpeta) |
id | Device ID del Mac — necesario para emparejar con Android |
folder | Ruta exacta a compartir + pasos guiados en Mac y Android |
open | Abre la interfaz web |
install | brew install syncthing (siempre pregunta antes) |
install-service / uninstall-service | Arranque automático |
El script detecta la instalación automáticamente, en este orden:
PATH → /Applications/[[syncthing-solucion-segura-abierta|Syncthing]].app → /opt/homebrew/bin → /usr/local/bin.
Así funciona tanto si Syncthing viene de Homebrew como si es la app de macOS.
Para saber si Syncthing está realmente operativo, el script no se fía de que el
proceso exista: consulta la API REST (127.0.0.1:8384). Es la única señal de
que la interfaz responde de verdad.
| Estado | Significado |
|---|---|
OPERATIVO | El proceso vive y la interfaz responde |
ARRANCANDO | Hay proceso pero la API aún no contesta |
DETENIDO | Sin proceso |
Esto evita el falso positivo de comprobar solo el proceso, donde el script podía decir “ya está corriendo” mientras la interfaz no respondía.
Al arrancar, prueba las vías en orden hasta que una funcione:
brew services— si es una instalación de Homebrewopen -a [[syncthing-solucion-segura-abierta|Syncthing]]— si es la app de macOS (la GUI)- Daemon en segundo plano — binario suelto, con PID y log propios
La app de macOS puede tardar 1-2 minutos en levantar la interfaz (arranque de la GUI + generación de claves + indexado inicial). El script espera hasta 120 s y te avisa de que es normal.
Nunca instala nada sin permiso:
installpide confirmación explícita y el resto de comandos solo leen/arrancan/paran procesos existentes.
Los scripts devuelven código ≠ 0 cuando algo falla, para que el error sea
detectable desde otros scripts o desde CI. Antes un fallo terminaba en 0 y
pasaba desapercibido.
Las skills son flujos que Hermes carga automáticamente según lo que le pidas. El canon vive en
~/.hermes/skills/markeup/<nombre>/SKILL.md —una carpeta por skill, enlazada desde
src/content/hermes/skills— y son 14 skills atómicas más 3 atajos, con su índice
navegable en ~/.hermes/skills/INDEX.md.
Los atajos (bundles, en ~/.hermes/skill-bundles/) son la forma corta de pedir un flujo
entero: cargan varias skills de golpe.
| Atajo | Para qué | Ejemplo de petición |
|---|---|---|
/markeup-post | Crear una nota completa | «crea un post sobre X en Máquina» |
/markeup-publish | Auditar, corregir y publicar | «audita el vault y publica» |
/markeup-verify | Pasada de gates, en solo lectura | «¿cómo está el vault?» |
Qué sabe hacer cada una de las catorce, con qué frase se pide y su ficha pública: la guía
Hablar con Hermes: qué pedir y cómo. El manual técnico del sistema —canon,
gate y las dos proyecciones— es docs/skills-system.md del repo.
Añadir o editar una skill: crea o edita ~/.hermes/skills/markeup/markeup-<x>/SKILL.md con el
frontmatter del canon (name igual que la carpeta, description de 60 caracteres y con punto,
tags y related_skills que resuelvan) y documéntala en ~/.hermes/skills/INDEX.md, en
markeup/DESCRIPTION.md y en docs/skills-system.md. Ciérralo con npm run skills:check (canon,
comandos reales, referencias y copia de Cline) y regenera las copias con npm run skills:cline y
npm run skills:publish.
Encapsula la publicación (es el último paso del atajo /markeup-publish). Hace el push a main y
verifica que Cloudflare haya desplegado, comparando el hash del CSS servido en
producción.
npm run publish -- -m "feat(posts): añade artículo sobre X"
npm run publish -- --dry-run -m "prueba" # simulación
npm run publish -- --check-deploy # solo diagnosticar
⚠️ Un push a
maindespliega a producción. La skill exige confirmación explícita del usuario y prohíbe--no-verifyopush --forcepor iniciativa propia.
| Error | Causa | Solución |
|---|---|---|
ResourceExhausted: Worker local total request limit reached | El modelo del proveedor (NVIDIA NIM) está saturado | Cambiar a modelo local: hermes config set model.default qwen2.5-coder:7b-64k |
| El agente responde cosas genéricas tipo «no tengo herramientas para eso» | Fallback tras agotar reintentos: el modelo no respondió y emite un texto por defecto | Revisar ~/.hermes/logs/errors.log; comprobar el modelo activo |
El agente ignora el modelo de config.yaml | La sesión quedó anclada a otro modelo al crearse | hermes sessions delete <id> --yes y reiniciar el gateway |
| Respuestas de 200-300 s | Modelo remoto saturado o demasiado grande | Usar un modelo local; verificar con curl la latencia real |
The token ... was rejected by the server | Token revocado o incorrecto en ~/.hermes/.env | Genera uno nuevo con @BotFather y ejecuta npm run hermes -- setup |
Conflict: terminated by other getUpdates | Dos procesos usando el mismo bot | npm run hermes -- check (detecta el conflicto) y para el otro bot |
| El gateway no arranca y el servicio falla | Ruta obsoleta en el plist de launchd | npm run hermes (lo repara automáticamente) |
stop parece fallar pero el gateway sí para | KeepAlive de launchd tarda en soltar el proceso | Corregido: el script ahora espera y distingue ambos casos |
| Syncthing no encontrado | No está instalado | npm run syncthing -- install |
Dice DETENIDO y no arranca | App de macOS sin daemon activo | npm run syncthing — reinicia la app automáticamente |
Estado ARRANCANDO mucho tiempo | La app está indexando o generando claves | Espera 1-2 min; comprueba con npm run syncthing -- status |
| La interfaz web no responde | Proceso vivo pero API caída | npm run syncthing -- stop y luego npm run syncthing |
| No sincroniza con Android | Device ID o Folder ID distintos | npm run syncthing -- folder (revisa el Folder ID) |
Cuando el agente responde algo raro, lo primero es saber qué modelo está usando de verdad:
hermes config get model.default # el configurado
hermes config get model.provider
# ¿Qué modelo usó cada sesión? (la sesión puede tener otro anclado)
python3 -c "
import sqlite3
c = sqlite3.connect('$HOME/.hermes/state.db')
for r in c.execute('SELECT session_id, model, billing_provider FROM session_model_usage ORDER BY last_seen DESC LIMIT 5'):
print(r)
"
# Errores del proveedor
tail -20 ~/.hermes/logs/errors.log
⚠️ Lección aprendida: una respuesta genérica del tipo «no tengo herramientas para eso» no significa que falte la skill. Suele ser el texto de fallback que Hermes emite cuando el proveedor del modelo falla tras agotar los reintentos. Se reconoce porque menciona herramientas que no existen en la instalación real.
| Error | Causa probable | Solución |
|---|---|---|
ZodError en frontmatter | Campo inválido o faltante | Revisar schema en content.config.ts |
Cannot find module | Dependencia no instalada | npm ci |
ENOENT en imagen | Imagen referenciada no existe | Verificar public/img/ |
| Error en wikilink | Slug inexistente | Corregir enlace o crear el post destino |
Un [[slug]] sale como huérfano pese a existir | El slug difiere (mayúsculas/caracteres) | Normalizar slug en frontmatter del destino y npm run build |
| Los tooltips/rich cards no aparecen | preview.js no cargado o manifiesto antiguo | Subir public/preview.js, limpiar caché, revisar Network («manifest 200 JSON») |
| El tooltip se crea en el DOM pero no se ve (queda arriba/fuera) | .lp-pop posicionado fuera del viewport | Desde el fix, .lp-pop usa position: fixed y place() se ancla a coordenadas de viewport (compatible con Lenis) con recorte final: la tarjeta nunca se sale del viewport. Verificar que el CSS compilado tenga .lp-pop{position:fixed} |
| El tooltip se cierra al ir del enlace al pop | Cierre por pointerout al cruzar el hueco | Cubierto por removePopSoon() + elementFromPoint en preview.js. No hay que tocar nada |
| Síntoma | Causa probable | Solución |
|---|---|---|
WhatsApp/LinkedIn comparten /posts/slug (sin dominio) | El componente recibió una URL relativa | Usar URL absoluta; buildShareLinks() lanza si no lo es. Ejecutar npm run validate:share |
| El preview en X/Facebook no muestra imagen grande | og:image no absoluta, o ratio ≠ 1.91:1 | npm run validate:share; regenerar imagen con npm run og (1200×630) |
| El autor no aparece como atribución en X | Falta twitter:site/twitter:creator o no es @usuario | Revisar TWITTER_HANDLE en BaseLayout.astro; el validador exige formato @usuario |
| Preview antiguo tras publicar | Caché del crawler | Re-scrape manual (Facebook Debugger / X Card Validator) |
Diagnóstico de compartir (2026-11-09): los botones propagaban una ruta relativa (
/posts/<slug>) a los endpoints de cada red, por lo que WhatsApp compartía literalmente/posts/caretas-mascarassin dominio (enlace roto) y LinkedIn/Facebook ignoraban la URL. Fix: la URL se construye absoluta en[...slug].astro(${siteOrigin}/posts/${slug}), se normaliza enSocialShare.astrocontraAstro.sitey se valida enbuildShareLinks()(lanza si no es absoluta). Verificado endist/: los 5 endpoints llevanhttps%3A%2F%2Fmarkeup.pages.dev%2Fposts%2F.... Añadidonpm run validate:sharepara detectarlo en CI.
Rich previews: los tooltips y las rich cards (
![[post-slug]]) necesitan quedist/api/posts.jsonexista (lo generabuild:astro). Si prueba ennpm run devy ves.rich-card-waitsin hidratar, asegúrate de que/api/posts.jsonresponde 200 en el navegador (pestaña Network). El script hace fetch perezoso, así que el primer tooltip puede tardar ~150 ms en mostrarse.
Diagnóstico de tooltips (2026-09-10): el fallo «no van los rich tooltips» era de posicionamiento:
.lp-poperaposition: absoluteañadido a<body>pero colocado con coordenadas de viewport (getBoundingClientRect). Con Lenis (smooth scroll) activo, el pop se dibujaba entopnegativo y quedaba invisible aunque existiese en el DOM. Fix:.lp-pop→position: fixed,place()convisualViewport,findHit()sin abortar el ascenso al topar con un<a>no-preview, y cierre diferido con comprobación deelementFromPointpara permitir el «puente» enlace→tarjeta.Reescritura de la animación (2026-09-24 ·
verify:preview): la coreografía anterior animabatopyheightde la caja conbackdrop-filterencima, así que ir fino era imposible. La nueva («la tarjeta se materializa y su foto se abre») anima solotransform,opacityyclip-path, mide el tamaño una sola vez, renuncia albackdrop-filter, reutiliza la misma tarjeta al cambiar de enlace (vuela al enlace nuevo y re-revela su contenido) y sueltawill-changeal terminar. Medido en Chrome real: p50 17 ms / p95 18 ms, sin picos.
curl http://localhost:11434/api/tags # ¿Responde?
ollama list # Modelos instalados
ollama ps # Modelos en ejecución
sysctl vm.swapusage # Swap en uso (si es alto, falta RAM)
La redacción con IA tarda 1-3 minutos: es normal. Deberías recibir el progreso paso a paso en el chat.
Si no llega ningún mensaje intermedio y todo se queda en «Procesando…», era
un bug ya corregido: el callback de progreso se invocaba desde un hilo secundario
con asyncio.create_task, que no lo adjuntaba al bucle de eventos y nunca se
ejecutaba (avisaba con “coroutine was never awaited”). La forma correcta es
asyncio.run_coroutine_threadsafe.
| Variable | Valor por defecto | Por qué |
|---|---|---|
OLLAMA_NUM_CTX | 8192 | Contexto. Un valor alto multiplica la RAM de la KV cache |
OLLAMA_NUM_PREDICT | 3000 | Tope de tokens a generar; evita esperas eternas |
OLLAMA_TIMEOUT | 420 | 7 min de margen antes de abortar |
OLLAMA_MODEL | qwen2.5-coder:7b-64k | Modelo usado |
En un equipo con 16 GB de RAM, un contexto de 65536 tokens empuja al sistema a swap (se midieron ~2,5 GB en uso) y la generación se vuelve lentísima. Si tienes margen de memoria, puedes subirlo:
OLLAMA_NUM_CTX=32768 npm run hermes
Medición de referencia: con
num_ctx=8192el modelo cargado ocupa 5,0 GB de VRAM y la generación completa tarda ~2 minutos. Con 65536 subía a 6,4 GB y se entraba en swap.
| Servicio | Función | Estado |
|---|---|---|
com.markeup.process-images | Procesa imágenes al detectar cambios | ✅ Activo |
ai.markeup.autopublish | Autopublica los cambios de src/content/posts/ (debounce 30 s · rate limit 120 s) | Apagado desde el 2026-09-24 · reactivar con npm run autopublish -- enable |
ai.hermes.gateway | Hermes + Telegram al iniciar sesión | Opcional (npm run hermes) |
com.markeup.syncthing | Syncthing al iniciar sesión | Opcional (npm run syncthing -- install-service) |
com.user.watchexec | Auto-commit al detectar cambios | ❌ Descargado |
launchctl load ~/Library/LaunchAgents/com.markeup.process-images.plist
launchctl unload ~/Library/LaunchAgents/com.markeup.process-images.plist
launchctl list | grep markeup
| Variable | Uso | Dónde se configura |
|---|---|---|
ADMIN_PASSWORD | Hash de contraseña del panel admin | GitHub Secrets |
JWT_SECRET | Clave para firmar tokens JWT | GitHub Secrets |
BOT_TOKEN | Token del bot de Telegram | .env local (nunca en git) |
PUBLIC_CF_ANALYTICS_TOKEN | Beacon de analítica Cloudflare | .env / Cloudflare Pages |
MARKEUP_VAULT_ROOT | Ruta alternativa del vault (opcional) | Entorno |
| Documento | Contenido |
|---|---|
| doc-readme-MarkeUp | Visión general y stack |
| doc-guia-MarkeUp | Instalación y configuración |
| doc-arquitectura-MarkeUp | Arquitectura y componentes |
| Medios embebidos | Medios embebidos: bloques, azúcar de Obsidian y gate |
| doc-auditoria-MarkeUp | Auditoría y deuda técnica |
1 PROCESOS
├── 1.1 PROCESOS OLLAMA (iniciar, cambiar modelo, estado, detener)
├── 1.2 PROCESOS TELEGRAM (iniciar/detener bot)
├── 1.3 PROCESOS HERMES (lanzar, reanudar, cambiar modelo)
└── 1.4 ARRANCAR Ollama + Telegram
2 VAULT → 2.1 ANÁLISIS DEL VAULT (auditar, huérfanos, stats, docs...)
3 CONTENIDO DEL VAULT (crear, generar artículo, ampliar, wikilinks...)
4 BUILD & ASSETS
├── 4.1 PROCESAR IMÁGENES Y AUDIOS
├── 4.2 BUILD (completo, preview, pagefind)
├── 4.3 DEV SERVER (local, network)
└── 4.4 NPM (8 comandos útiles)
5 GIT
├── 5.1 Ramas (creación y cambio)
├── 5.2 Ramas (limpieza y fusión)
├── 5.3 Estado y preparación
├── 5.4 Commits y deshacer
├── 5.5 Stash
└── 5.6 Remoto
6 CLOUDFLARE
├── 6.1 Autenticación y proyectos
└── 6.2 Despliegues (preview, producción, rama)
7 MANTENIMIENTO (renombrar, mover, limpiar, exportar, docs)
8 STATUS (posts, git, build, Ollama, assets)
| Comando | Acción | Equivalente en menú |
|---|---|---|
npm run tools | Menú interactivo completo | — |
npm run tools:crear | Crear post directamente (también --borrador) | 3.1 |
npm run tools:crear:borrador | Crear borrador/esqueleto sin IA | 3.1 |
npm run tools:auditar | Auditar vault directamente | 2.1.1 |
npm run tools:stats | Dashboard stats | 2.1.3 |
npm run tools:index | Regenerar vault-index.md | 7 |
npm run tools:tagfix | Normalizar tags automáticamente | 7 |
npm run tools:buscar | Buscar texto en el vault | 6 |
npm run tools:backlinks | Ver backlinks de una nota | 3.4 |
npm run tools:relacionadas | Notas relacionadas | 3.5 |
npm run tools:huerfanos | Listar huérfanos prioritarios | 2 |
npm run tools:hermes | Iniciar Hermes Agent | 1.3.1 |
npm run tools:ollama | Estado Ollama (JSON) | 1.1.3 |
npm run hermes | Arrancar Hermes + Telegram | 1.2.1 |
npm run syncthing | Conectar/gestionar Syncthing | — |
npm run syncthing | Conectar/gestionar Syncthing | — |
Para el diseño del sistema, consulta la arquitectura; para el contexto general, el README del proyecto.
Véase también: la arquitectura y la visión general para entender el sistema completo.
Enlaces: README y instalación.
Para profundizar en este ámbito, Dýnamis Kínēsis y Enérgeia desarrolla la tríada aristotélica de potencia, movimiento y acto: la arquitectura metafísica del devenir y su lectura en el cuerpo, la.
doc-operacion-MarkeUp
Explora las ramas del artículo
