npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hobots

v2.0.1

Published

SDK JavaScript da Hobots — telemetria (erros, transactions e heartbeats) e execução remota de tarefas sob demanda (tasks) para RPAs e integrações. Um único import e um único init().

Readme

hobots

SDK JavaScript/TypeScript da Hobots para instrumentar agentes (RPAs) e integrações.

  • Telemetria — o que o programa emite sozinho: erros com stack trace (módulo Issues do app), performance (transactions/etapas) e heartbeats de uptime (módulo Agentes).
  • Tasks — execução sob demanda: o agente faz polling no app da Hobots, executa handlers registrados e reporta status e logs (módulo Solicitações).

Um único import e um único init() cobrem as duas capabilities. Cliente HTTP puro, sem dependências de runtime. Requer Node 18+ (usa o fetch global); a telemetria também roda no browser para captura de erros.


Instalação

npm install hobots
# ou: pnpm add hobots · yarn add hobots
import * as Hobots from 'hobots'; // um import só — telemetria e tasks

Também funciona em CommonJS: const Hobots = require('hobots').


Um SDK, duas capabilities, um init()

O SDK expõe duas capabilities autenticadas pelo mesmo Client Secret do agente, configuradas em um único init():

| Capability | Como habilita | Para quê | Ciclo de vida | | --- | --- | --- | --- | | Telemetria | sempre ativa no init() | O que o programa emite sozinho: erros, transactions/etapas, heartbeat. | Timers unref'd — não segura o processo. Chame close() no fim. | | Tasks | tasks: true (ou opções) no init() | Execução remota sob demanda via polling. | start() segura o processo vivo até stop()/close(). |

Você usa uma, outra ou as duas:

  • Só saber se o robô quebrou, quanto demora e se está vivo? → init({ clientSecret, heartbeat: true }).
  • Disparar o robô sob demanda a partir de um formulário no app? → init({ clientSecret, tasks: true }) + register() + start().
  • Ambos? Passe heartbeat e tasks no mesmo init() — o instanceId é a identidade única, e os dois canais convergem em um agente no painel. Com tasks: true, o heartbeat é opcional: o próprio poll de tasks já marca a presença do agente.
Hobots.init({
  clientSecret: 'hb_<64 hex>',
  instanceId: 'vm-cliente-01',
  heartbeat: true,                              // telemetria: "estou vivo" a cada 30s
  tasks: { pollIntervalMs: 5_000 },             // execução sob demanda
});

Hobots.register('processar-nfe', async (params, ctx) => {
  ctx.log.info('começando…');
  ctx.log.info('12 notas processadas'); // o retorno do handler é ignorado — reporte por log
});

Hobots.start(); // inicia o polling de tasks (segura o processo vivo)

O Client Secret

Cada agente tem um único Client Secret — a mesma credencial autentica a telemetria e as tasks:

hb_<64 hex>

A credencial é secreta e viaja sempre no header Authorization: Bearer — nunca na URL. Copie o Client Secret no app, em Configurações → Agentes — os snippets de integração já vêm prontos. O SDK fala com a API oficial https://api2.hobots.app automaticamente; em desenvolvimento, aponte para outro host com a env HOBOTS_API_URL (ex.: HOBOTS_API_URL=http://localhost:3001).


Telemetria

Todas as funções abaixo são no-op seguras se Hobots.init() não foi chamado (com aviso no console).

init(options)

Inicialize uma vez, o mais cedo possível no processo. Cria o cliente, instala handlers globais de erro (a menos que captureUnhandled: false) e inicia o heartbeat se configurado.

Hobots.init({
  clientSecret: 'hb_<64 hex>',
  environment: 'production',
  release: '[email protected]',
  instanceId: 'vm-cliente-01',
  tags: { squad: 'automacoes' },
  heartbeat: true, // "estou vivo" a cada 30s
});

| Opção | Tipo | Default | Descrição | | --- | --- | --- | --- | | clientSecret | string | obrigatório | o Client Secret do agente (hb_<64 hex>), copiado em Configurações → Agentes | | environment | string | 'production' | ambiente lógico | | release | string | — | versão do programa, ex. [email protected] | | instanceId | string | obrigatório | identidade única da instância — heartbeat e poll de tasks convergem nela | | tags | Record<string,string> | — | tags aplicadas a todos os eventos | | sampleRate | number | 1 | amostragem de erros (0..1) | | tracesSampleRate | number | 1 | amostragem de transactions (0..1) | | maxBreadcrumbs | number | 100 | tamanho do buffer de breadcrumbs | | captureUnhandled | boolean | true | handlers globais de erro (uncaughtException/unhandledRejection) | | heartbeat | boolean \| HeartbeatOptions | false | heartbeat periódico (true usa defaults) | | tasks | boolean \| TasksOptions | false | habilita a execução sob demanda (true usa defaults) — veja Tasks | | beforeSend | (event) => event \| null | — | edita/descarta evento (retorne null para descartar) | | debug | boolean | false | loga atividade do SDK | | flushIntervalMs | number | 2000 | intervalo do flush automático do transport | | maxBatchSize | number | 10 | itens por envelope antes do flush imediato |

Captura de erros e mensagens

try {
  await processarNota(nota);
} catch (error) {
  Hobots.captureException(error, {
    type: 'error',
    tags: { cliente: 'acme' },
    fingerprint: ['sefaz-timeout'], // agrupa manualmente; sem isso o servidor agrupa pelo stack
  });
  throw error;
}

Hobots.captureMessage('Lote processado com sucesso', 'success');

Ambas retornam o event_id (ou undefined se não inicializado). O hint aceita type, tags, extra e fingerprint. Erros não tratados são capturados e flushados automaticamente antes do processo sair — desligue com captureUnhandled: false. Os eventos aparecem agrupados em issues no módulo Issues do app.

EventType = 'fatal' | 'error' | 'warning' | 'success'. Não há 'info'/'debug': narração não abre issue — para isso existem o log da execução e as breadcrumbs. Use 'success' para reportar itens concluídos com êxito — contam como eventos, mas não viram issue nem disparam alertas.

Breadcrumbs

Trilha do que aconteceu antes do erro; enviada junto do próximo evento (buffer limitado por maxBreadcrumbs, mais antigos descartados).

Hobots.addBreadcrumb({ category: 'nfe', message: 'Baixou XML da nota 123', level: 'info' });

A lista não é limpa ao enviar um evento e é global. Conceito: Breadcrumb.

Escopo: tags, usuário, contexto, extras

Dados anexados a todos os eventos seguintes:

Hobots.setTag('regiao', 'nordeste');
Hobots.setTags({ squad: 'automacoes', cliente: 'acme' });
Hobots.setUser({ id: 'op-42', email: '[email protected]' });
Hobots.setContext('vm', { cpu: 4, ram: '8GB' });
Hobots.setExtra('ultimoLote', 128);

Para um escopo isolado (sem vazar para o global), use withScope:

Hobots.withScope((scope) => {
  scope.setTag('lote', '128').setFingerprint(['reprocessamento']);
  Hobots.captureMessage('Reprocessando lote 128');
});

Performance: transactions e etapas

const tx = Hobots.startTransaction('processar-lote', 'rpa.job');

const step = tx.startChild('http.client', 'GET /api/notas');
await api.buscarNotas();
step.setData('notas', 42).finish();

const filho = step.startChild('db.query', 'INSERT INTO notas'); // etapas aninham
await gravar();
filho.finish();

tx.setStatus('ok');  // ou 'error'
tx.finish();         // só aqui a transaction é enviada (respeitando tracesSampleRate)
  • startChild(op?, description?) existe em Transaction e Step (aninhamento arbitrário).
  • Métodos encadeáveis: setStatus, setData (step), setTag/setName (transaction).
  • finish() na transaction fecha as etapas abertas e envia o payload; nada é enviado se ela não foi amostrada. A amostragem é decidida no startTransaction().

Dentro de um handler de task, startTransaction() retorna a transaction da própria run (ctx.tx) em vez de criar uma segunda — cada execução tem exatamente uma transaction. Conceitos: Transaction · Step.

Heartbeat (uptime)

Bate "estou vivo" periodicamente; o servidor acusa o robô offline quando os batimentos param.

Hobots.init({
  clientSecret,
  heartbeat: { intervalMs: 30_000, name: 'Robô NFe — VM 01' },
});

heartbeat: true usa os defaults. A identidade é o instanceId do init(); os campos são intervalMs, name (default: o instanceId) e metadata — valores em Tempos e intervalos. O primeiro batimento sai na hora em que o init() roda, não depois do primeiro intervalo.

O heartbeat é essencial para agentes só de telemetria; com tasks: true ele é opcional, porque o poll de tasks já marca a presença do agente no painel. Conceito: Heartbeat.

Flush e close

await Hobots.flush(2000); // envia o buffer, mantém o SDK vivo
await Hobots.close(2000); // flush final + para o heartbeat + fecha o transport

Chame close antes do processo sair. Os timers da telemetria são unref'd — ela não segura o processo vivo sozinha.


Tasks (execução sob demanda)

Capability de execução remota sob demanda, habilitada com tasks: true (ou opções) no init(). O agente faz polling no app, executa os handlers registrados quando aparece trabalho e reporta status e logs incrementais — cada execução aparece no módulo Solicitações do app. Mesmo Client Secret, mesmo debug e o instanceId como identidade — nenhum handler global extra é instalado.

Ciclo de vida

Hobots.init({
  clientSecret: 'hb_<64 hex>',
  instanceId: 'vm-cliente-01',
  tasks: true, // ou { pollIntervalMs, concurrency, ... }
});

Hobots.register('processar-nfe', async (params, ctx) => {
  ctx.log.info(`processando lote ${params.lote}…`);
  ctx.throwIfCanceled();            // cancelamento cooperativo
  ctx.log.info('12 notas processadas');
});

Hobots.start(); // começa o polling e MANTÉM O PROCESSO VIVO

process.on('SIGINT', () => {
  void Hobots.close(15_000).then(() => process.exit(0));
});
  • init({ tasks }) — cria o agente. Chamar init() de novo para o agente anterior e o substitui (o registry de handlers é preservado).
  • register(task, handler)task é o task_slug configurado no formulário do app (Configurações → Formulários). Registre antes de start(). Funciona até antes do init(); com as tasks desabilitadas, avisa no console.
  • start() — começa o polling. Lança se o init() não configurou tasks ou se nenhuma task foi registrada.
  • stop(timeoutMs?) — para só o polling de tasks: encerra o polling e aguarda as runs ativas (default 30s); a telemetria continua. Retorna false se estourou o timeout.
  • close(timeoutMs?) — encerra tudo: agente (aguardando runs em voo) → heartbeat → flush final do transport.

| Opção (TasksOptions) | Tipo | Default | Descrição | | --- | --- | --- | --- | | pollIntervalMs | number | 5000 | intervalo entre polls sem trabalho (mínimo 1000) | | concurrency | number | 1 | runs simultâneas (clamp 1..5) | | taskTimeoutMs | number | — | timeout client-side por run (não mata o handler; servidor é o backstop) | | logFlushIntervalMs | number | 2000 | intervalo do envio incremental de logs | | cancelCheckIntervalMs | number | 5000 | heartbeat de cancelamento por run (clamp 1000..15000) — também é o sinal de vida da run | | installExitHandlers | boolean | true | handlers de SIGINT/SIGTERM/uncaughtException que reportam as runs ativas como AgentTerminated antes do processo morrer; nunca interferem quando o programa tem handler próprio |

O Client Secret, o debug e a identidade do agente (instanceId) vêm do init() — o heartbeat e o poll de tasks convergem em um agente no painel automaticamente.

O handler e o RunContext

type TaskHandler<P = Record<string, unknown>> =
  (params: P, ctx: RunContext) => void | Promise<void>;

interface RunContext {
  runId: string;
  task: string;
  log: RunLogger; // ctx.log.debug/info/warn/error/success(message)
  items: RunItems; // ctx.items.succeeded/failed/occurrence — a entrega do bot
  tx: Transaction; // transaction desta run — correlacionada e finalizada pelo SDK
  signal: AbortSignal;
  throwIfCanceled(): void; // lança RunCanceled se o signal já disparou
  sleep(ms: number): Promise<void>; // espera cancelável — rejeita com RunCanceled no abort
}
  • params — os dados do formulário configurado no app (JSON validado). Tipável: Hobots.register<{ lote: number }>(...).
  • ctx.log.<level>(message) — níveis debug, info, warn, error e success; logs enviados incrementalmente ao app (PATCHes com throttle), visíveis na solicitação quase em tempo real; também impressos no terminal local, coloridos por nível.
  • ctx.items — registra os itens que a execução processou/gerou (a entrega do bot: notas fiscais baixadas, boletos emitidos…), cada um com status succeeded, failed (falha técnica) ou occurrence (ocorrência de negócio, ex.: "nota nº 10 não encontrada") e um payload de atributos livres. Aparecem na aba itens da solicitação, com totalizadores; as colunas exibidas são configuradas por formulário no app. Guia: Itens de execução.
  • ctx.tx — a transaction de performance desta run, já correlacionada e finalizada pelo SDK. Use ctx.tx.startChild(op, description) para medir as etapas — elas viram o waterfall da execução no app. startTransaction() chamado dentro do handler retorna essa mesma transaction. Conceito: ctx.tx.
  • ctx.signalAbortSignal que dispara quando a run é cancelada no app, em timeout ou no shutdown.
  • ctx.throwIfCanceled() — o ponto de parada de uma linha; lança RunCanceled se o signal já disparou.
  • ctx.sleep(ms) — espera que respeita o signal (rejeita com RunCanceled em vez de dormir até o fim).
  • Retornoignorado: o SDK não captura nem envia o valor retornado pelo handler (resultados podem conter dados sensíveis e não são coletados).
  • Erro lançado — a run é reportada como failed (mensagem cap 10k, stack 20k).

Dados sensíveis: ctx.log e mensagens/stack de erros são enviados ao servidor e ficam visíveis no app. Não inclua senhas, tokens, dados pessoais ou qualquer informação sensível em logs e mensagens de erro.

Itens de execução (ctx.items)

Hobots.register('baixar-notas', async (params, ctx) => {
  for (const nota of await listarNotas(params)) {
    try {
      await baixar(nota);
      ctx.items.succeeded({ numero_nota: nota.numero, cnpj_prestador: nota.cnpj }, { id: nota.numero });
    } catch (erro) {
      ctx.items.failed(String(erro), { numero_nota: nota.numero }, { id: nota.numero });
    }
  }
  // ocorrência de negócio (não é falha do bot):
  // ctx.items.occurrence('nota nº 10 não encontrada', { numero_nota: '10' }, { id: '10' })
});

Chamadas síncronas e bufferizadas (lotes de até 500; dreno antes do reporte final). O id opcional é a identidade de negócio do item na run — repetido, é ignorado pelo servidor (registro único, retry idempotente). Limites: 10.000 itens/run, payload 16 KB, mensagem 2.000 caracteres. Detalhes em Itens de execução.

Anexos (screenshots)

Texto de log conta o que o robô fez; um print conta como estava a tela. Os métodos de captura (Hobots.captureException e Hobots.captureMessage) aceitam um attachment no hint: dentro de uma execução de task, a imagem vira anexo da linha de log da run que espelha a captura — no app ela aparece como miniatura logo abaixo dessa linha, clicável para ampliar.

Hobots.register('processar-nfe', async (params, ctx) => {
  await entrar();
  Hobots.captureMessage('portal após o login', 'info', {
    attachment: await page.screenshot(),
  });

  try {
    await emitir(params);
  } catch (erro) {
    Hobots.captureException(erro, {
      attachment: { image: await page.screenshot(), caption: 'tela no momento do erro' },
    });
    throw erro;
  }
});

O SDK não tira o print — ele recebe a imagem pronta, de onde você quiser (Playwright, Puppeteer, uma lib de captura de desktop, um arquivo em disco). O attachment aceita bytes (Uint8Array/ArrayBuffer/Buffer), um caminho de arquivo (só em Node) ou um objeto { image, caption?, contentType? }.

| Limite | Valor | | --- | --- | | formatos | PNG, JPEG, WEBP | | tamanho por imagem | 5 MB | | anexos por execução | 20 | | anexos por linha de log | 4 |

A linha de log é registrada na hora; o upload roda em segundo plano e vai direto para o S3 por URL presignada, sem passar pela API — a captura nunca bloqueia nem lança por causa do anexo (falhas viram um aviso no log da run). Fora de uma execução de task não há onde pendurar a imagem: o attachment é descartado com um aviso. Detalhes em Anexos.

Dados sensíveis: o print é enviado exatamente como está — o SDK não borra nem recorta nada, e telas de portal costumam conter CPF, nome e valores. Capture só o necessário. Solicitantes só veem os anexos quando o projeto está com "mostrar logs para solicitantes" ligado, o mesmo gate dos logs.

Cancelamento cooperativo

Uma run cancelada no app não mata o handler — o SDK apenas dispara ctx.signal. Chame ctx.throwIfCanceled() em loops longos e pontos de parada (e troque esperas longas por ctx.sleep()):

Hobots.register('processar-nfe', async (params, ctx) => {
  for (const nota of params.notas) {
    ctx.throwIfCanceled(); // lança RunCanceled se cancelada — o handler para aqui
    await processar(nota);
  }
});

Quando o cancelamento veio do app, o RunCanceled lançado é suprimido — a run já está cancelada no servidor e nada é reportado por cima. Para limpeza antes de parar (fechar navegador, desfazer estado), use try/finally.

Atenção: não use if (ctx.signal.aborted) return;. Retornar normalmente faz o SDK reportar a run como succeeded — uma run cancelada apareceria como sucesso no app. É preciso lançar, e é o que o throwIfCanceled() faz.

Concorrência

concurrency (default 1, máx 5) controla quantas runs o agente executa em paralelo. Com 1, as runs são sequenciais. O agente só reivindica o que consegue rodar (max = concurrency - runs ativas) e entra em drain mode: re-pesquisa imediatamente enquanto vem trabalho; quando não vem, espera pollIntervalMs.

Timeout por run

taskTimeoutMs é client-side. No estouro o SDK não mata o handler — dispara ctx.signal, libera o slot e reporta a run como falha (TaskTimeout). Respeite o signal para parar de verdade. Sem ele, o backstop é o servidor.

Idempotência (at-least-once até running)

Se o agente morrer logo após capturar uma run (antes de reportar running), ela é re-entregue quando um agente voltar (até 3 tentativas; depois AgentLost). A partir do momento em que a run está running, a morte do agente não re-entrega — a run vira falha AgentTerminated, porque o trabalho pode ter efeitos colaterais parciais e re-executar automaticamente não é seguro (reexecute manualmente pelo app).

Ainda assim, escreva handlers idempotentes: a janela de re-entrega pós-claim existe, e processar a mesma run duas vezes não pode causar efeito duplicado (use uma chave de deduplicação sua, como o número da nota).


Encerramento gracioso

close(timeoutMs?) encerra tudo na ordem certa: para o polling de tasks (aguardando a run atual), para o heartbeat e faz o flush final do transport.

// só telemetria: não segura o processo, mas faça o flush final para não perder eventos
await Hobots.close(2_000);

// com tasks: segura o processo por design — aguarde a run atual
process.on('SIGINT', () => {
  void Hobots.close(15_000).then(() => process.exit(0));
});

Para parar de aceitar execuções sem derrubar a telemetria, use stop(timeoutMs?).


Entry points / exports

| Import | Conteúdo | | --- | --- | | hobots | Tudo: init, telemetria (captura, transactions, heartbeat), tasks (register/start/stop), close, classes e tipos. | | hobots/protocol | Tipos do wire format (telemetria e tasks). |

Empacotado em ESM e CommonJS com definições de tipos (.d.ts) para todas as entradas.


Documentação completa

Guias detalhados acompanham o pacote em docs/.

Novo no vocabulário de observabilidade (transaction, step, breadcrumb, tag, context, extra, flush, close)? Comece por Conceitos — as outras páginas assumem esses termos.

  • Conceitos — o que cada coisa é, por que existe e quando escolher uma em vez da outra.
  • Getting started — instalação, Client Secret, primeiro init, telemetria e tasks.
  • Telemetria — erros, breadcrumbs, escopo, transactions/etapas, heartbeat.
  • Tasks — handlers, RunContext, cancelamento, concorrência, idempotência.
  • Referência de API — todas as funções e tabelas de opções com defaults.
  • Protocolo & transporte — o Client Secret, endpoints, envelope, retries.

Licença

MIT © Hobots