SDK
Sesiones, alertas y estado: la Status API de Miblo
Cada método de la Status API de Miblo: session, needsYou, done, say, focus, timer y snapshot, con parámetros, respuestas, errores y un ejemplo completo.
El cliente
Todo empieza con un MibloStatus. Guarda el token y el puerto y firma cada solicitud; no abre ninguna conexión hasta que se usa por primera vez.
import { MibloStatus } from '@miblo/status';
const miblo = new MibloStatus({ token, port, timeoutMs }); // all optionalfrom miblo_status import MibloStatus
miblo = MibloStatus(token=None, port=None, timeout=3.0) # all optional| Parámetro | Por defecto | Qué es |
|---|---|---|
token | MIBLO_TOKEN | El token de miblo sdk token. Sin él, el constructor lanza no_token de inmediato. |
port | MIBLO_PORT, si no 47821 | El puerto del puente. La dirección siempre es 127.0.0.1. |
timeoutMs / timeout | 3000 ms / 3.0 s | Cuánto esperar cada respuesta. |
hello()
Verifica el token y la versión de Miblo sin cambiar nada: { api, version } (por ejemplo { api: 2, version: "1.37.0" }: api es la API del SDK que habla el puente). Útil para comprobar la configuración cuando tu programa arranca.
const { api, version } = await miblo.hello();info = miblo.hello() # {"api": 2, "version": "1.37.0"}Cómo se firma cada solicitud
Tú no haces nada de esto, lo hacen los paquetes: antes de cada solicitud el cliente pide un desafío (GET /sdk/v1/hello con un nonce aleatorio), verifica que el puente demostró conocer el token y que las versiones coinciden (upgrade, bridge_outdated), firma la solicitud con HMAC-SHA256 (método, ruta y el SHA-256 del cuerpo) y, a la vuelta, verifica la firma de la respuesta. Un programa que toma el puerto de Miblo mientras Miblo está caído no puede demostrar el token, y el cliente no le envía nada. El paso a paso está en la referencia.
Sesiones
Una sesión es tu programa en la lista de Miblo, junto a las sesiones de las herramientas de IA.
session()
const job = await miblo.session({ tool, title, cwd, state, detail, pid });job = miblo.session(tool, title=None, cwd=None, state="working", detail=None, pid=-1)| Campo | Obligatorio | Reglas |
|---|---|---|
tool | sí | El nombre de tu programa, de 1 a 20 caracteres, en una línea. No puede ser el nombre de una herramienta de Claude Code (Bash, Edit, Read, Task, Agent...) ni empezar con _. |
title | no | El nombre de la sesión en el Miblo Desk (hasta 64 caracteres; el Miblo Desk muestra unos 20). Sin él, lo nombra la carpeta de cwd; sin ninguno de los dos, tool. Los nombres repetidos reciben " 2", " 3". |
cwd | no | Una carpeta; su nombre pasa a ser el título cuando no hay title. |
state | no | working (por defecto), needs_you, idle o done. |
detail | no | Una línea corta (hasta 64 caracteres; si es más larga, se corta). Es la actividad mientras trabaja y el motivo mientras te necesita. |
pid | no | El proceso cuya salida termina la sesión. Por defecto: tu propio programa. null (TS) o None (Python): ninguno, y la sesión expira tras 12 h sin una actualización (2 h una vez en reposo o terminada). |
Devuelve una Session con id (sdk- + 32 caracteres hexadecimales, hecho por el puente) y state. Después solo se aceptan ids sdk-, así que un programa nunca puede cambiar ni terminar la sesión de una herramienta de IA.
Cambiar el estado
| TypeScript | Python | Estado | En el Miblo Desk |
|---|---|---|---|
job.working(detail?) | job.working(detail=None) | working | La línea dice tool · detail. |
job.needsYou(reason?) | job.needs_you(reason=None) | needs_you | La alerta de pregunta, una vez por cambio; cuenta en "te necesita". |
job.idle(detail?) | job.idle(detail=None) | idle | En reposo, sin alerta. |
job.done(detail?) | job.done(detail=None) | done | La alerta de terminado, una vez por cambio. |
job.set(state, detail?) | job.set(state, detail=None) | cualquiera | Lo mismo, eligiendo el estado. |
job.end() | job.end() | (desaparece) | Saca la sesión de Miblo: { id, ended: true }. |
Cada llamada devuelve { id, state }. Los modos reunión y discreto ocultan el detail, como hacen con toda sesión. Las sesiones sobreviven a una actualización del puente, pero no a un reinicio del computador.
Mensajes, enfoque y temporizador
Las mismas acciones que miblo say, miblo focus y miblo timer, con los límites propios del Miblo Desk.
| TypeScript | Python | Reglas |
|---|---|---|
say(text, { minutes }) | say(text, minutes=None) | Una línea, hasta 40 caracteres (47 bytes UTF-8), de 1 a 480 minutos (por defecto 30). No puede empezar como los avisos propios de Miblo ("Approved:", "Phone:", "Miblo:", "Allow?"... en inglés y portugués). |
sayOff() | say_off() | Quita el mensaje. |
focus({ focusMin, breakMin, rounds }) | focus(focus_min=None, break_min=None, rounds=None) | Enfoque de 5 a 120 min, descanso de 1 a 60, de 1 a 12 rondas. |
focusStop() | focus_stop() | Detiene la sesión de enfoque. |
timer(minutes) | timer(minutes) | De 1 a 180 minutos. |
timerStop() | timer_stop() | Detiene el temporizador. |
Cada una devuelve { ok, delivered, gadgets }: delivered cuenta los dispositivos que lo recibieron, y gadgets lista cada dispositivo emparejado como { id, name, ok, error? }, donde error es offline, unpaired, unsupported, rejected o busy (un Miblo Desk con un firmware anterior a 1.30.0 no se maneja y aparece como offline: actualízalo). Sin ningún Miblo emparejado, delivered es 0, que no es un error. Nada de esto se convierte en una notificación de escritorio.
Snapshot
snapshot() lee quién está trabajando y quién te necesita, sin cambiar nada.
const { sessions, gadgets } = await miblo.snapshot();
const waiting = sessions.filter((s) => s.state === 'needs_you');snap = miblo.snapshot()
waiting = [s for s in snap["sessions"] if s["state"] == "needs_you"]sessions:[{ id, name, state, tool, sdk, since }]. La sesión de una herramienta de IA aparece con un id de 8 caracteres y el nombre de la herramienta entool("Claude Code","Codex"...);sdkestruepara las sesiones de programas;sincees cuándo entró en su estado (segundos epoch).gadgets:[{ id, name, online }].Nunca lleva un comando, un archivo, un prompt, un costo, un límite ni nada de Miblo+. Lecturas: 5 por segundo, ráfagas de 10.
Ejemplo completo
Un vigilante de builds: ejecuta el comando, muestra su progreso, te avisa cuando falla y pone un mensaje cuando pasa.
// MIBLO_TOKEN=$(miblo sdk token) node build-watch.mjs
import { spawn } from 'node:child_process';
import { MibloStatus, MibloError } from '@miblo/status';
const miblo = new MibloStatus();
const job = await miblo.session({ tool: 'build', title: 'Site build', detail: 'npm run build' });
const code = await new Promise((resolve) => {
spawn('npm', ['run', 'build'], { stdio: 'inherit' }).on('exit', resolve);
});
try {
if (code === 0) {
await job.done('passed');
await miblo.say('Build green', { minutes: 10 });
} else {
await job.needsYou(`failed (exit ${code})`);
}
} catch (e) {
if (e instanceof MibloError && e.code === 'rate_limited') console.log(`try again in ${e.retryAfter} s`);
else throw e;
}# MIBLO_TOKEN=$(miblo sdk token) python3 build_watch.py
import subprocess
from miblo_status import MibloStatus, MibloError
miblo = MibloStatus()
job = miblo.session("build", title="Site build", detail="npm run build")
code = subprocess.call(["npm", "run", "build"])
try:
if code == 0:
job.done("passed")
miblo.say("Build green", minutes=10)
else:
job.needs_you(f"failed (exit {code})")
except MibloError as e:
if e.code == "rate_limited":
print(f"try again in {e.retry_after} s")
else:
raiseErrores
Cada error es un MibloError con:
code: estable, nunca se renombra (lista abajo);status: el estado HTTP, o0cuando no se llegó al puente;field(enbad_request): el campo con el problema, comotoolodetail;retryAfter(TS) /retry_after(Python) enrate_limited: cuántos segundos esperar.
code | Estado | Cuándo |
|---|---|---|
bad_request | 400 | Un campo fuera de rango o del tipo equivocado (ver field) |
bad_json | 400 | El cuerpo no es JSON |
sdk_off | 401 | No hay token en este computador |
not_authorised | 401 | Token incorrecto o rotado |
unknown_session | 404 | La sesión terminó, expiró o no es una sesión del SDK |
not_found | 404 | No existe esa ruta (con call()) |
method_not_allowed | 405 | El método equivocado para la ruta (con call()) |
too_many_sessions | 409 | Hay 8 sesiones del SDK activas |
too_large | 413 | Cuerpo de más de 4096 bytes (el cliente lo rechaza antes de enviarlo) |
upgrade | 426 | Este SDK es más antiguo de lo que Miblo acepta: instala el SDK 2.x |
rate_limited | 429 | Se superó un límite; espera retryAfter segundos |
internal | 500 | Un bug en Miblo: avísanos, por favor |
busy | 503 | El puente está ocupado con el mismo tipo de solicitud; espera retryAfter segundos |
Del lado del cliente: no_token, bridge_unavailable, timeout, unverified_answer y bridge_outdated (ver Instalación). Las páginas de la pantalla, las apps y las alertas listan sus propios códigos.
Llamadas de bajo nivel
call(method, path, body?, opts?) hace una solicitud firmada a cualquier ruta /sdk/v1/* y devuelve el JSON de la respuesta (lanza MibloError ante un error). Un Uint8Array (TS) o bytes (Python) va como application/octet-stream. Úsalo para rutas nuevas antes de que el paquete tenga un método para ellas. Opciones: { tool, timeoutMs } (TS) / tool=, timeout= (Python); tool es el nombre de tu programa, que las rutas de pantalla y de apps necesitan.
const screen = await miblo.call('GET', '/sdk/v1/screen', undefined, { tool: 'build' });screen = miblo.call("GET", "/sdk/v1/screen", tool="build")