# Getting started

## Instalação

```bash
pnpm add hobots
```

Sem dependências de runtime. Requer **Node 18+** (usa o `fetch` global).

## Conceitos em 2 minutos

| Conceito | Em uma frase |
| --- | --- |
| [Agente](./concepts.md#agente) | O seu robô — um processo, uma coisa que ele faz, iniciado com `register` (sob demanda) ou `start` (sempre ligado) |
| [Execução (run)](./concepts.md#execução-run) | Uma rodada do agente, com logs, itens, progresso e resultado — um trabalho no app |
| [Item](./concepts.md#item) | Uma unidade de trabalho entregue pela execução (nota baixada, boleto emitido) |
| [Heartbeat](./concepts.md#heartbeat) | Batimento "estou vivo" — sempre ligado; o servidor acusa offline quando para |
| [instanceId](./concepts.md#instanceid) | A identidade da instância do robô no painel |
| [Issue](./concepts.md#evento-e-issue) | Erros agrupados — `hobots.captureException` e erros não tratados |

## Credenciais

Cada agente tem **um Client Secret** (`hb_<64 hex>`), copiado em **Configurações → Agentes**. O SDK lê do ambiente:

```bash
export HOBOTS_CLIENT_SECRET=hb_<64 hex>
export HOBOTS_INSTANCE_ID=vm-cliente-01   # opcional — sem ela, o hostname
```

Também dá para passar `clientSecret`/`instanceId` nas opções de `register()`/`start()` (o snippet do app já vem assim). Em desenvolvimento, `HOBOTS_API_URL=http://localhost:3001` aponta para outra API; em produção é sempre `https://api2.hobots.app`.

## Primeiro agente sob demanda (`register`)

1. No app, abra o agente em **Configurações → Agentes** e monte o **formulário do agente** (os campos que cada solicitação vai carregar).
2. No robô:

```js
import hobots from 'hobots';

hobots.register(async params => {
  hobots.log.info(`lote ${params.lote}`);
  for (const nota of params.notas) {
    await baixar(nota);
    hobots.items.succeeded({ numero_nota: nota }, { id: nota });
  }
  hobots.log.success('concluído');
});

process.on('SIGINT', () => void hobots.close(15_000).then(() => process.exit(0)));
```

3. Rode o robô: ele aparece online em **Instâncias** (modo "sob demanda") e fica aguardando. Crie uma solicitação pelo formulário — a execução aparece em **Trabalhos** com logs em tempo real.

## Primeiro agente sempre ligado (`start`)

```js
import hobots from 'hobots';

hobots.start(async () => {
  hobots.log.info('monitor iniciado');
  while (true) {
    await hobots.sleep(60_000); // cancelável — para se a execução for cancelada no app
    for (const arquivo of await novosArquivos()) hobots.items.succeeded({ arquivo });
  }
});
```

O robô abre a própria execução ao iniciar e ela aparece em **Trabalhos** com origem "agente". Quando o handler retorna (ou é cancelado), a execução fecha; o processo continua vivo pelo heartbeat até `hobots.close()`.

> **Uma chamada por processo.** `register()` ou `start()`, nunca os dois — a segunda chamada lança. Um robô que faz duas coisas são dois agentes (dois processos, dois Client Secrets).

## Encerramento

`hobots.close(timeoutMs?)` para o polling, espera as execuções em voo, para o heartbeat e faz o flush final. Sem handler próprio de `SIGINT`/`SIGTERM`, o SDK reporta as execuções ativas como `AgentTerminated` antes do processo morrer.

Próximos passos: [Execuções](./runs.md) para itens, progresso, anexos, cancelamento e retry; [Referência de API](./api-reference.md) para todas as opções.
