Pular para o conteúdo
miblo

SDK

Apps do Miblo: declarar, configurações, supervisor e sandbox

Transforme um programa em app do Miblo: declare, o esquema de configurações, settings.get, request e onChange, a rotação, o supervisor e a sandbox dos apps da comunidade.

O que é um app

Um app é um programa que diz ao Miblo o nome dele e as configurações de que precisa. A pessoa preenche essas configurações no app Miblo (aba Apps) ou no terminal, nunca num arquivo ou numa variável de ambiente, e o Miblo cuida de rodar o programa. Os apps oficiais (miblo-apps) usam exatamente as mesmas rotas que você vai usar: não há porta especial.

Um app é feito de três peças:

  1. declarar o app (nome, categoria, promessa, configurações, uma prévia);

  2. ler as configurações e reagir quando a pessoa muda alguma;

  3. desenhar a tela com screen.card() ou screen.layer() (A tela App).

app()

const app = miblo.app({ id: 'clima', tool: 'clima' });
  • id: o id do app, letras minúsculas, números e -, até 48 caracteres. Use o mesmo do miblo apps add --id.

  • tool: o nome do seu programa (as regras do tool de uma sessão). As configurações de um app pertencem ao programa que o declarou, por esse nome: outro programa recebe not_your_app. Um app instalado da loja pertence ao nome do pacote.

declare()

await app.declare({
  name: { pt: 'Clima', en: 'Weather' },
  category: 'daily',
  promise: { pt: 'Vai chover?', en: 'Will it rain?' },
  settings: [
    { key: 'city', label: { pt: 'Cidade', en: 'City' }, type: 'text', required: true, max: 40 },
    { key: 'units', label: { pt: 'Unidade', en: 'Units' }, type: 'select', options: ['°C', '°F'], default: '°C' },
    { key: 'peek', label: { pt: 'Avisar antes da chuva', en: 'Heads-up before rain' }, type: 'toggle', default: true },
  ],
  preview: { v: 1, title: 'Clima', icon: 'cloud', items: [{ t: 'big', value: '24°', label: 'Recife' }] },
});
CampoRegras
nameUm texto ou { pt, en }, até 24 caracteres. Obrigatório.
settingsO esquema das configurações (abaixo), até 12.
categoryUm nome curto para a aba Apps. A loja usa computer, money, daily, news-dev, sport, delights e other.
promiseUma linha, { pt, en } ou texto, até 100 caracteres: a pergunta que o app responde.
previewUm card com dados de exemplo, mostrado na aba Apps antes de o app rodar.

Devolve { ok, id }. Declarar de novo substitui o esquema e mantém cada valor salvo que continua válido nele: pode declarar a cada início do programa. No máximo 32 apps por computador (too_many_apps).

O esquema das configurações

Cada configuração é { key, label, type, default?, required? } mais os campos do tipo.

typeCampos extrasValor
textmax (1 a 200, padrão 100)Um texto de uma linha
numbermin?, max?Um número
selectoptions: 1 a 20 opções (até 40 caracteres cada, sem repetir)Uma das opções
toggle(nenhum; padrão false)true ou false
url(nenhum)Um endereço http:// ou https:// de até 512 caracteres
time(nenhum)Um horário HH:MM (24 h); aceita 22:00, 7:30 ou 22h30 e guarda sempre HH:MM. O app Miblo mostra um seletor de horário
connectprovider, clientId, scopes, clientSecret?, help?A conta conectada (veja Conectar contas)
  • key: começa com uma letra minúscula, depois letras, números e _, até 32 caracteres, sem repetir.

  • label: um texto ou { pt, en }, até 40 caracteres.

  • required: true faz o app aparecer como "precisa de configuração" enquanto não houver valor.

  • default: precisa ser um valor válido do próprio tipo.

  • group: "advanced" dobra a configuração sob "Avançado" no app Miblo, para as que quase ninguém muda.

  • placeholder (text, number, url, time): um exemplo do que escrever, texto ou { pt, en } de até 40 caracteres, mostrado no campo vazio. Onde existe uma lista, declare um select (carregue a lista da fonte e guarde por 7 dias); texto livre só onde nenhuma lista cabe, e então com o exemplo.

  • seed: um valor que o seu programa traz das configurações de uma versão anterior (um texto antigo repartido em configurações novas). O Miblo salva uma vez, só onde a pessoa ainda não tem valor, nunca guarda no esquema e ignora um que não seja válido. Um texto salvo cujas partes são todas opções de um select de várias escolhas que o substituiu passa sozinho.

  • Nada de segredos. Não existe tipo "senha", e uma chave ou um rótulo que pareça um segredo (token, password, senha, api_key, secret, chave, cookie, pin...) é recusado. Os apps leem fontes públicas, sem conta.

Ler as configurações

settings.get()

const { values, missing, rev } = await app.settings.get();
  • values: os padrões combinados com o que a pessoa salvou; uma configuração sem nenhum dos dois é null.

  • missing: as configurações obrigatórias ainda sem valor.

  • rev: muda quando os valores mudam.

settings.request()

Pede à pessoa agora, com um motivo de até 80 caracteres.

if (missing.length) await app.settings.request(missing, 'Para mostrar a previsão');

O que acontece: o app Miblo mostra um cartão de configuração com o formulário e o motivo, o celular diz "Configure o app Clima no computador", e a tela do app no Miblo mostra "Configure no app Miblo" no lugar do card. O pedido fica valendo até os valores mudarem. Devolve { ok, needsSetup }. O motivo não pode começar como um aviso do Miblo.

settings.onChange() e settings.wait()

const stop = app.settings.onChange((values) => redraw(values));   // a pessoa salvou: redesenhe já
// ...
stop();
  • onChange(cb, { onError, retryMs }) chama cb(values, answer) a cada mudança (não para os valores atuais) e devolve stop(). Erros vão para onError e ele continua tentando a cada retryMs (padrão 5000). Em Python: on_change(cb, on_error=None, retry=5.0), com cb(values).

  • wait(rev?) é a peça de baixo: espera os valores mudarem a partir de rev, no máximo 20 s, e devolve { values, missing, rev }. No máximo 16 esperas ao mesmo tempo (busy).

Os valores ficam neste computador (<dados do Miblo>/apps/<id>.json, só o seu usuário lê). Pelo terminal: miblo apps config <id> chave=valor.

Quando a tela do app aparece

  • O rodízio: com vários apps, a tela App mostra um por vez, miblo apps rotation <segundos> cada (padrão 15; 0: só o fixado).

  • Quando: miblo apps when idle (padrão: só quando nenhuma sessão de IA está rodando, esperando ou precisando de você há --idle-after segundos, 60 por padrão, de 10 a 3600), rotation (sempre no rodízio) ou manual (só fixado ou num peek). A pessoa escolhe; um app não muda isso.

  • Fixar e peek continuam valendo em qualquer escolha. Os avisos do Miblo sempre vêm primeiro.

O supervisor

miblo apps add "<comando>" registra o seu programa e miblo apps enable <id> o mantém rodando: o Miblo o inicia com o token, o reinicia quando termina e guarda a saída (miblo apps logs <id>). O passo a passo está em Instalação.

ComandoO que faz
miblo apps list [--json]Todos os apps, ligados ou não, e quem precisa de configuração
miblo apps enable / disable <id>Liga ou desliga
miblo apps show <id>Estado, reinícios, comando ou hosts
miblo apps logs <id>A saída do programa (e o que a sandbox bloqueou)
miblo apps config <id> [chave=valor ...]Mostra ou muda as configurações
miblo apps rotation [segundos]O tempo de cada tela no rodízio
miblo apps when [rotation|idle|manual]Quando os apps aparecem
miblo apps install <id>Instala um app da loja (assinado)
miblo apps remove <id>Desliga, tira a tela e apaga as configurações
miblo screen status / clearMostra ou limpa a tela App

Conectar contas

Com a próxima versão do Miblo, um app pode ler dados da própria conta Google ou Microsoft da pessoa (o app oficial Agenda lê a agenda assim) sem nunca ver uma senha ou um refresh token: quem faz o login é a ponte do Miblo, com OAuth 2.0 (Authorization Code + PKCE, como um app de desktop), e o seu programa recebe só tokens de acesso de curta duração.

await app.declare({ name: 'Agenda', settings: [
  { key: 'google', label: { pt: 'Conectar Google', en: 'Connect Google' }, type: 'connect', provider: 'google',
    clientId: '<seu client id de desktop>.apps.googleusercontent.com', clientSecret: '<o secret de app instalado do Google>',
    scopes: ['https://www.googleapis.com/auth/calendar.readonly'],
    help: { pt: 'Os eventos aparecem no Miblo em até 30 s', en: 'Events show on Miblo within 30 s' } },
] });
const { url } = await app.connect.start('google');            // o endereço de login (o app Miblo tem o botão)
const { accessToken, expiresAt, account } = await app.connect.token('google');
await fetch('https://www.googleapis.com/calendar/v3/users/me/calendarList', { headers: { authorization: `Bearer ${accessToken}` } });
await app.connect.off('google');                              // desconecta
  • A configuração connect diz onde entrar: provider (google ou microsoft, uma configuração para cada), clientId (o seu cliente OAuth: um cliente "Desktop app" do Google ou um cliente público do Azure), scopes (1 a 8), o clientSecret do Google quando houver (o Google dá um aos clientes de desktop e documenta que não é confidencial; nunca para a Microsoft) e uma linha help opcional (80 caracteres, embaixo do botão). Nunca é obrigatória nem tem padrão; o valor é a conta conectada (o e-mail), '' até lá, e só a ponte escreve nele. Por isso settings.wait() acorda o programa assim que a pessoa conecta ou desconecta.

  • connect.start(provider) devolve { provider, url }, o endereço de login. A ponte espera um único retorno em até 5 minutos numa porta local própria, confere o state, troca o código com o verificador PKCE e confere o id_token. O navegador mostra "Conectado como ...". No máximo 4 logins ao mesmo tempo (busy).

  • connect.token(provider) devolve { provider, accessToken, expiresAt, account }, um token com pelo menos um minuto de vida, renovado quando preciso. not_connected até a pessoa conectar (e de novo se o provedor revogar o acesso); unavailable com retryAfter quando o provedor não responde: tente depois, continua conectado.

  • connect.off(provider) esquece a conta neste computador (miblo apps remove também).

  • Quem pode: só o programa que declarou o app (not_your_app), como as configurações.

  • Onde ficam os tokens: o refresh token fica cifrado (AES-256-GCM, com uma chave derivada da chave da ponte, presa ao app e ao provedor) em <dados do Miblo>/apps/<id>/connect-<provider>.json; os tokens de acesso ficam só na memória da ponte. Nada vai para o miblo.ai.

  • Para a pessoa: a aba Apps do app Miblo mostra o botão "Conectar Google" e depois "Conectado como ... · Desconectar". No terminal: miblo apps connect <id> google e miblo apps disconnect <id> google.

A sandbox dos apps da comunidade

Um app da loja pode trazer código escrito por outra pessoa. Ele só chega a um computador depois da análise automática, da revisão por IA e da aprovação humana (Publicar na loja), e roda numa sandbox:

  • Arquivos: lê só o próprio pacote, a própria pasta de estado e o runtime do Miblo; escreve só na pasta de estado. Usa o modelo de permissões do Node (--permission).

  • Sem processos nem código nativo: nada de processos filhos, worker threads, addons nativos ou WASI.

  • Módulos bloqueados: http, https, http2, net, tls, dgram, dns, child_process, cluster, worker_threads, vm, module, wasi, inspector, repl, v8, trace_events e sqlite. O app importa só arquivos do próprio pacote e os outros módulos internos do Node; código que ele grava na pasta de estado não pode ser importado.

  • Rede só pelo fetch do Miblo, e só para os hosts declarados em network: https na porta padrão, redirecionamentos seguidos com a mesma regra, 10 s por chamada, 256 KB por resposta, 30 chamadas por minuto. WebSocket, EventSource e XMLHttpRequest não existem.

  • O token do SDK sai do ambiente antes de o app carregar; só o cliente o guarda.

  • Tudo que é bloqueado vira uma linha blocked: ... em miblo apps logs <id>, mesmo que o app trate o erro.

  • A loja tem a palavra final: a cada início e a cada hora o pacote é conferido de novo; um app que a loja deixou de servir ou escondeu é desligado.

  • Precisa do Node 22.15+, 23.5+ ou 24+. Num Node mais antigo, o app não roda (estado unsupported, com o motivo no log).

O que a sandbox não garante: a rede é contida pelo fetch substituído e pelo controle de módulos, não pelo sistema operacional, e um app pode gastar CPU e memória e mandar aos hosts dele o que o Node dá a qualquer programa (hora, idioma, o nome do computador). Por isso a revisão humana vem antes de cada versão.