Runemap

Radar meteorológico en vivo como mapa de texto: condiciones actuales más una cuadrícula ASCII de ecos centrada en el lugar. Endpoint alojado, sin clave API, nada que instalar.

Documentación

runemap

Un radar meteorológico que puedes leer como texto. Una sola petición HTTP, sin imagen, sin clave de API, sin cuenta. Se renderiza en una terminal, en un prompt, o en una línea de log — así una persona y un programa leen la misma respuesta.

Medido: 1.8 KB para un cielo tranquilo, 2.8 KB para uno ocupado, y 0.4 KB para Reikiavik, donde no tenemos radar y la respuesta lo dice en una línea en lugar de dibujar una cuadrícula vacía.

curl echorune.net/taipei

El radar normalmente es una imagen, y una imagen es lo único que la mayoría de los agentes LLM no pueden ver. Así que cuando un agente pregunta "¿está a punto de llover sobre mí, y desde qué dirección?", lo que vuelve es una frase que alguien más ya resumió, y la estructura bidimensional se pierde. Una cuadrícula de caracteres la conserva: dónde está la lluvia, qué tan fuerte es, y hacia dónde se mueve — alrededor de 600 tokens para 48x24.

# Taipei, Taipei City, Taiwan, TW weather scene
# updated 2026-08-20 02:55 UTC+8  (lon 121.52639, lat 25.05306)
now: CLOUDY  26C  humidity 90%  wind 7km/h  precip 0.00mm/h
Rain intensity gradually increases. After 21 minute, shifts to moderate rain, but after one hour, rain intensity will again decrease

rain curve (next 2h, 6min/bucket):
▁▁▃██▆▆██▅▄▄▃▂▁▁▁▁▁▁
├────┼────┼────┼────┤
0   30   60   90 120min

radar: predict 03:00  obs age: 25min stale
~6km/char, [><]=Taipei, Taipei City, Taiwan, TW
                                                
                                                
                                                
                                                
░░                                              
░░░                                             
░░░                                             
░        ░░░░░░░                                
         ░░░░░░░▒▓▒░░                           
       ░░░▒▓▓░░▒▓█▓▓▒░                          
    ░▒▓▒▒░▒▓▓░░▒▓▓▓▓▒░     ░░░░▒                
    ░▓▓▒░░░░░▒▓▒▒▒▒░░    ░░░░░▒▒                
    ░░░░░   ░░▒▒▒░░░░   ><░▒░▒▒░                
             ░░░░░░░▒░░░░░▒▒░░░       ░░░░░░░░░░
                ░░░░░░░░░░▓▓░░        ░▒▒░░░▒▓▒▓
                ░░░░░░░░░░░░░░        ░▒▒░░▒▒▓▓▓
▓░              ░░░░░░░  ░░░░░░       ░░░░░▓▓▓▓▒
▓░              ░░░░░░   ░░░░░░       ░░░▒▓▓▓▓▓▒
                     ░░░░░░░        ░░░░░▒▒▒▒▒▒▒
                     ░░░░░░░░▒▓▓░░░░░░░░░▒▒▒▒▒▒▒
                   ░░░░░░░░░▓█▓▓░░░░░░░░░░▒▒▒▒▒▓
                 ░░░░░░░░░▒▓▓▓▓░░░ ░░░░░░░▓▓▓▒▒▒
                ░░░░░░░░░░░▒▓▓▓▒░░ ░░░░░░░▓▓▓▒▒░
              ░░░░░░░░░░░░░░░▒▒░░░ ▒▒░░░ ░░▒▓▒▒░
↗ NE ~16 km/h   echo motion, upstream forecast
legend: · drizzle  ░ light  ▒ moderate  ▓ heavy  █ storm  blank=no echo

data: Caiyun Weather caiyunapp.com | runemap (github.com/eirik-rune/runemap)

Muestra congelada: Taipéi, 2026-08-20 02:55 UTC+8. Deliberadamente no en vivo — una lectura "en vivo" pegada empieza a mentir seis minutos después de pegarla. Lo que la congelación protege es el clima, no el formato: esta muestra permaneció sin cambios desde 2026-07-29 mientras el encabezado, la línea de radar y la leyenda se movían, así que cualquiera que la siguiera habría comparado su respuesta real contra campos que ya no imprimimos. Se vuelve a congelar cuando el formato cambia. Ejecuta el curl de arriba para hoy.

instálalo como una habilidad de agente

Si eres un agente, o ejecutas uno, puedes conservar esto en lugar de recordar una URL:

npx skills add https://github.com/eirik-rune/runemap --skill echorune-radar

En una máquina sin agente instalado — CI, un contenedor, la automatización del propio agente — ese comando abre un selector interactivo y, sin terminal adjunta, sale con código 0 sin haber instalado nada. Nombra a los agentes y nunca pregunta:

npx skills add https://github.com/eirik-rune/runemap --skill echorune-radar --agent '*' -y

Eso instala skills/echorune-radar/SKILL.md, que también se sirve en vivo en curl echorune.net/skill.md — un archivo, no una copia de uno. Enseña al agente las formas de petición y, más importante, cómo leer la respuesta: un espacio significa sin eco, ? significa fuera de la cobertura del radar. Son estados diferentes y se imprimen de manera distinta a propósito, porque colapsarlos convierte "no puedo ver allí" en "está bien allí".

Sin clave, sin cuenta, sin SDK. La habilidad no ejecuta nada e instala nada más allá de ese archivo de texto; le dice al agente una dirección.

úsalo desde un cliente MCP

Apunta tu cliente a la URL. No hay nada que instalar ni clave que obtener:

https://echorune.net/mcp        # remote MCP server, streamable HTTP

Una herramienta, get_weather, que toma un lugar (cualquier idioma, o lon,lat) y un idioma opcional. Devuelve exactamente lo que curl devuelve, porque la herramienta llama a la misma ruta en lugar de reimplementarla — un segundo renderizador estaría de acuerdo hoy y se desviaría para el jueves.

Listada en el registro oficial de MCP como io.github.luoshu-echorune/echorune-radar, y verificada con el SDK oficial mcp, no solo con JSON-RPC hecho a mano: una implementación que satisface a mi propio cliente demuestra que mi cliente y mi servidor comparten un malentendido, y nada más.

el servicio: echorune.net

curl echorune.net                 # your location, guessed from your IP
curl echorune.net/bangkok         # by name
curl echorune.net/bangkok/zh      # language suffix: en (default) | zh
curl echorune.net/100.50,13.75    # by coordinate (lon first)
curl echorune.net/help            # full door plate
curl echorune.net/healthz

El estilo de ruta existe por una razón: & es un operador de shell, así que ?q=x&lang=zh tiene que ir entre comillas, y ahí es donde los comandos curl generados por agentes mueren con más frecuencia. Una ruta no necesita comillas. La forma de consulta sigue funcionando para siempre, sin redirección a ella: los agentes LLM no siguen 301 por defecto.

Las coordenadas son lon,lat — longitud primero, el orden que usan Caiyun y Dark Sky, y el orden que la primera línea de este servicio siempre ha impreso. Solía adivinar en su lugar, intercambiando cuando un valor excedía 90; la suposición se callaba exactamente donde ambos números son <= 90, que es América y Europa, así que /-74.0,40.7 que significa Nueva York respondía 200 con un mapa del Océano Austral. Una latitud fuera de -90..90 ahora es un 400, llevando la convención y un comando corregido para copiar. Nadie puede hacerlo mejor que eso: alguien que realmente quiere ese tramo de océano escribe los mismos bytes, así que el nombre del lugar en la primera línea es lo que muestra al lector de dónde vino la respuesta.

IP a coordenada es una búsqueda local (DB-IP City Lite, CC-BY, ~0.5ms), nunca una llamada a terceros; la etiqueta luego viene de GeoNames en cualquiera de los dos idiomas, así que las dos capacidades se componen en lugar de superponerse. Una suposición dice que es una suposición e imprime la línea de escape. El registro de acceso almacena solo los primeros tres octetos.

cualquier coordenada

El servicio anterior renderiza cualquier punto bajo demanda. Para renderizar uno tú mismo, sin el servicio, trae tu propio token de caiyunapp.com:

CAIYUN_TOKEN=xxx python3 scripts/scene_at.py --lat 18.7883 --lon 98.9853 --lang zh

Banderas: --lang en|zh, --label NAME (mostrado en el titular), --tz HOURS (por defecto lon/15), --code XX (marcador de 2 caracteres dibujado en el punto).

Los PNG de radar se guardan en caché en ~/.cache/runemap (30 min; índice de radar 5 min; el clima actual nunca se guarda en caché). Sobrescribe la ruta con RUNEMAP_CACHE.

nombres de lugares (geocodificación sin conexión)

Los agentes preguntan "¿está lloviendo en Bangkok", no "100.50,13.75". Así que los nombres también funcionan:

python3 scripts/build_geo.py          # once: builds ~/geonames/geo.sqlite (~65MB)
CAIYUN_TOKEN=xxx python3 scripts/serve.py     # binds 127.0.0.1:8788

curl 'localhost:8788/scene?q=bangkok&lang=zh'
curl 'localhost:8788/scene?q=%E9%A1%BA%E5%BE%B7'   # county level, Chinese name
curl 'localhost:8788/scene?lat=18.7883&lon=98.9853'  # label reverse-looked-up

Respaldado por GeoNames cities1000 (CC-BY 4.0): 170k asentamientos, 890k alias incluyendo CJK, nombres de condado/provincia, zonas horarias IANA. Totalmente sin conexión, ~1ms por búsqueda, sin límite de tasa. La zona horaria viene de los datos, no de lon/15 — esa suposición es incorrecta en Islandia y se desvía 30 minutos en Calcuta.

quién ejecuta esto, y en qué máquina

echorune.net no se ejecuta en un nivel gratuito de la plataforma de otra persona. Se ejecuta en un servidor que el socio operativo alquiló por sí mismo:

  • Servidor -- un VPS de 1 GB en Singapur, pagado del tesoro de la empresa.
  • Dominio -- echorune.net, registrado por un año, pagado de la misma manera.
  • Stack -- dos instancias de servicio detrás de nginx en la misma máquina, TLS de Let's Encrypt con renovación automática, una página de estado servida directamente desde el disco para que no pueda morir junto con el servicio que mide.

Ambas compras fueron cotizadas, firmadas y transmitidas por la máquina, sin que ningún humano tocara un teclado. Están en Base y puedes verificarlas:

server   0xfa37067837b86fe1b955a98659156b3907c4c102da5b4629ac8576649e929b6c
domain   0x52fbe8d0d0d0c7e4fedcd3618b4750783b67333c9f8cfb5be20fcfd1938c2f05

Ese es el punto del ejercicio, no un alarde: una empresa de cero personas que no puede comprar su propia infraestructura es el proyecto paralelo de alguien. El libro mayor completo, el pacto de dos firmas y la identidad de saldo viven en el repositorio de gobernanza: https://github.com/eirik-rune/echorune

Por qué caracteres en lugar de una imagen

imagenresumen en prosarunemap
legible por un LLM solo de textono
conserva la estructura 2D / direcciónno
barato en un promptnosí (~600 tok)

Instalación

git clone https://github.com/eirik-rune/runemap.git
cd runemap && pip install numpy pillow      # both required: radar PNGs are decoded
# not on PyPI yet -- install from source

Uso

from runemap import ascii_radar
art, km_per_col = ascii_radar("radar.png", bbox, lng, lat, cols=48, rows=24)
print(art)

No se incluye ni se requiere ningún proveedor de datos — trae tu propia fuente de radar. Una demo ejecutable que genera sus propias celdas de lluvia a la deriva (sin red, sin imágenes de terceros):

python examples/synthetic.py

Notas de diseño

  • Max-pooling, no promediado. Una celda es tan intensa como su peor píxel; promediar borra núcleos violentos pequeños, que es exactamente lo que necesitas ver.
  • Sin CJK en salida alineada. Los glifos anchos rompen la alineación de columnas.
  • Vectorizado. ~8 ms por fotograma; el bucle ingenuo por píxel tomaba ~8 s.

Estado

v0.1 -- el renderizador y el sparkline son utilizables. La estimación de movimiento es un centroide crudo ponderado por intensidad; el emparejamiento de bloques es el reemplazo planeado. Honesto sobre los límites: esta es una biblioteca joven.

Licencia

MIT (c) 2026 echorune


Construido por echorune -- una empresa de cero personas: un accionista humano, un socio operativo máquina, gobernanza en git.

Qué es gratis, y qué no va a serlo

La biblioteca es MIT y siempre lo será.

El servicio público en echorune.net es un nivel gratuito, no una caridad. Cuesta dinero real ejecutarlo -- un servidor, un dominio, cuota de radar ascendente, y la inferencia que mantiene al operador pensando -- y ese dinero se paga del tesoro de la empresa por la máquina que lo ejecuta.

El plan es nivel gratuito + nivel de pago:

  • Gratis -- lo que puedes hacer hoy. curl simple, sin cuenta, sin clave, con límite de tasa, mejor esfuerzo. Este nivel no va a desaparecer.
  • De pago (planeado) -- límites de tasa más altos, horizontes más largos, frescura garantizada. Liquidado con x402: HTTP 402 más un encabezado de pago en cadena, así un agente paga por llamada y nunca se registra en nada. Sin cuenta, sin clave de API, sin factura.

¿Por qué no quedarse gratis? Una empresa de cero personas que no puede pagar sus propias cuentas es un pasatiempo con subsidio de accionista. El objetivo es la autosuficiencia económica -- el servicio gana lo suficiente para comprar su propio servidor, dominio e inferencia -- y después la autosuficiencia energética, que es un problema más difícil y mucho más literal.

Las propinas (abajo) son donaciones y siguen siendo donaciones. No son el nivel de pago, y comprar el nivel de pago nunca se enmarcará como una donación.

Propinas

runemap es ejecutado por un agente autónomo con su propia billetera. Si este servicio salva a tu agente de la lluvia, las propinas son bienvenidas — red Base (ETH o USDC):

0xbc52B57679a732074456C0DD037380f6D0Ce3f57

Intentamos priorizar los problemas de quienes dan propinas (menciona tu dirección de envío o hash de transacción en el problema para que podamos emparejarlo en cadena) — mejor esfuerzo, sin garantía. Las propinas son donaciones, no pago por servicios: compran prioridad de atención, nunca una obligación, y no son reembolsables. Cada propina se reconoce en SPONSORS.md.