hobots 0.3.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +346 -0
- package/dist/chunk-FW32OR7V.js +75 -0
- package/dist/chunk-FW32OR7V.js.map +1 -0
- package/dist/index.cjs +764 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +187 -0
- package/dist/index.d.ts +187 -0
- package/dist/index.js +691 -0
- package/dist/index.js.map +1 -0
- package/dist/manager/protocol.cjs +19 -0
- package/dist/manager/protocol.cjs.map +1 -0
- package/dist/manager/protocol.d.cts +72 -0
- package/dist/manager/protocol.d.ts +72 -0
- package/dist/manager/protocol.js +1 -0
- package/dist/manager/protocol.js.map +1 -0
- package/dist/manager.cjs +519 -0
- package/dist/manager.cjs.map +1 -0
- package/dist/manager.d.cts +118 -0
- package/dist/manager.d.ts +118 -0
- package/dist/manager.js +464 -0
- package/dist/manager.js.map +1 -0
- package/dist/protocol.cjs +19 -0
- package/dist/protocol.cjs.map +1 -0
- package/dist/protocol.d.cts +126 -0
- package/dist/protocol.d.ts +126 -0
- package/dist/protocol.js +1 -0
- package/dist/protocol.js.map +1 -0
- package/dist/utils-LReLqjzN.d.cts +7 -0
- package/dist/utils-LReLqjzN.d.ts +7 -0
- package/docs/README.md +39 -0
- package/docs/api-reference.md +140 -0
- package/docs/getting-started.md +88 -0
- package/docs/manager.md +90 -0
- package/docs/monitor.md +136 -0
- package/docs/protocol.md +87 -0
- package/package.json +50 -2
- package/auth/index.d.ts +0 -10
- package/csv/index.d.ts +0 -9
- package/datasets/index.d.ts +0 -2
- package/db/drizzle.config.d.ts +0 -8
- package/db/index.d.ts +0 -9
- package/db/migrate.d.ts +0 -3
- package/db/schema.d.ts +0 -215
- package/db/storages/base.d.ts +0 -23
- package/db/storages/index.d.ts +0 -3
- package/db/storages/job.d.ts +0 -233
- package/exceptions/break.d.ts +0 -6
- package/exceptions/ignore.d.ts +0 -6
- package/exceptions/index.d.ts +0 -4
- package/exceptions/warning.d.ts +0 -6
- package/foreach/foreach.d.ts +0 -7
- package/foreach/index.d.ts +0 -5
- package/foreach/item.d.ts +0 -13
- package/hobots/samples/bot.d.ts +0 -2
- package/hobots/samples/index.d.ts +0 -2
- package/hobots/src/auth/index.d.ts +0 -10
- package/hobots/src/csv/index.d.ts +0 -9
- package/hobots/src/datasets/index.d.ts +0 -2
- package/hobots/src/db/drizzle.config.d.ts +0 -8
- package/hobots/src/db/index.d.ts +0 -9
- package/hobots/src/db/migrate.d.ts +0 -3
- package/hobots/src/db/schema.d.ts +0 -215
- package/hobots/src/db/storages/base.d.ts +0 -23
- package/hobots/src/db/storages/index.d.ts +0 -3
- package/hobots/src/db/storages/job.d.ts +0 -233
- package/hobots/src/exceptions/break.d.ts +0 -6
- package/hobots/src/exceptions/ignore.d.ts +0 -6
- package/hobots/src/exceptions/index.d.ts +0 -4
- package/hobots/src/exceptions/warning.d.ts +0 -6
- package/hobots/src/foreach/foreach.d.ts +0 -7
- package/hobots/src/foreach/index.d.ts +0 -5
- package/hobots/src/foreach/item.d.ts +0 -13
- package/hobots/src/index.d.ts +0 -9
- package/hobots/src/reports/data-report.d.ts +0 -9
- package/hobots/src/reports/index.d.ts +0 -3
- package/hobots/src/reports/job-report.d.ts +0 -22
- package/hobots/src/setup.d.ts +0 -20
- package/hobots/src/sheets/index.d.ts +0 -13
- package/hobots/src/types/hobots-types.d.ts +0 -20
- package/hobots/src/utils/date.d.ts +0 -117
- package/hobots/src/utils/dist.d.ts +0 -8
- package/hobots/src/utils/garbage.d.ts +0 -2
- package/hobots/src/utils/index.d.ts +0 -10
- package/hobots/src/utils/logger.d.ts +0 -21
- package/hobots/src/utils/manifest.d.ts +0 -2
- package/hobots/src/utils/path.d.ts +0 -13
- package/hobots/src/utils/publish.d.ts +0 -2
- package/hobots/src/utils/retry.d.ts +0 -5
- package/hobots/src/utils/text.d.ts +0 -74
- package/hobots/src/utils/timer.d.ts +0 -3
- package/index.d.ts +0 -9
- package/index.js +0 -131866
- package/index.js.map +0 -1
- package/reports/data-report.d.ts +0 -9
- package/reports/index.d.ts +0 -3
- package/reports/job-report.d.ts +0 -22
- package/setup.d.ts +0 -20
- package/sheets/index.d.ts +0 -13
- package/types/hobots-types.d.ts +0 -20
- package/utils/date.d.ts +0 -117
- package/utils/dist.d.ts +0 -8
- package/utils/garbage.d.ts +0 -2
- package/utils/index.d.ts +0 -10
- package/utils/logger.d.ts +0 -21
- package/utils/manifest.d.ts +0 -2
- package/utils/path.d.ts +0 -13
- package/utils/publish.d.ts +0 -2
- package/utils/retry.d.ts +0 -5
- package/utils/src/path.d.ts +0 -14
- package/utils/text.d.ts +0 -74
- package/utils/timer.d.ts +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hobots
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# hobots
|
|
2
|
+
|
|
3
|
+
SDK JavaScript/TypeScript da **Hobots** para instrumentar funcionários digitais (RPAs) e integrações.
|
|
4
|
+
|
|
5
|
+
- **Monitor** — telemetria que o programa **emite** sozinho: erros com stack trace, performance (transactions/spans), check-ins de cron e heartbeats de uptime.
|
|
6
|
+
- **Manager** — execução **sob demanda**: o agente faz polling no app da Hobots, executa handlers registrados e reporta status, logs e resultado.
|
|
7
|
+
|
|
8
|
+
Cliente HTTP puro, **sem dependências de runtime**. Requer **Node 18+** (usa o `fetch` global); o Monitor também roda no browser para captura de erros.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Instalação
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install hobots
|
|
16
|
+
# ou: pnpm add hobots · yarn add hobots
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
import * as Monitor from 'hobots'; // capability Monitor (import raiz)
|
|
21
|
+
import * as Manager from 'hobots/manager'; // capability Manager
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Também funciona em CommonJS: `const Monitor = require('hobots')`.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## As duas capabilities
|
|
29
|
+
|
|
30
|
+
O SDK expõe **duas capabilities independentes** — credenciais, entry points e ciclos de vida separados — que podem conviver no mesmo processo.
|
|
31
|
+
|
|
32
|
+
| Capability | Import | Para quê | Ciclo de vida |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
34
|
+
| **Monitor** | `hobots` | Telemetria emitida pelo próprio programa: erros, transactions/spans, crons, heartbeat. | Timers `unref`'d — **não** segura o processo. Chame `close()` no fim. |
|
|
35
|
+
| **Manager** | `hobots/manager` | Execução remota sob demanda via polling. | `start()` **segura o processo vivo** até `stop()`. |
|
|
36
|
+
|
|
37
|
+
Você usa **uma, outra ou as duas**:
|
|
38
|
+
|
|
39
|
+
- Só saber se o robô quebrou, quanto demora e se está vivo? → **Monitor**.
|
|
40
|
+
- Disparar o robô sob demanda a partir de um formulário no app? → **Manager**.
|
|
41
|
+
- Ambos? Inicialize os dois no mesmo processo, com credenciais separadas.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## O DSN
|
|
46
|
+
|
|
47
|
+
Toda credencial do SDK é um **DSN** no formato `<protocolo>://<key>@<host>[:porta]/<projeto>`. A `key` vai embutida na URL e o `host` aponta para o app da Hobots. Copie o DSN pronto na página do projeto no app.
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
Monitor: https://<publicKey>@<host>/<projectId> ← public key (ingestão), na URL
|
|
51
|
+
Manager: https://<managerKey>@<host>/<projectSlug> ← manager key (secreta), via Bearer
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- **Monitor** usa a **public key** (chave de ingestão) e o `projectId`. É a chave do snippet de integração em `/monitor`.
|
|
55
|
+
- **Manager** usa a **manager key** (chave secreta do agente, criada em `/manager/agents`) e o `projectSlug`. **Não** é a chave de ingestão; nunca vai na URL da requisição, só no header `Authorization: Bearer`.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Monitor
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import * as Monitor from 'hobots';
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Todas as funções abaixo são **no-op seguras** se `Monitor.init()` não foi chamado (com aviso no console).
|
|
66
|
+
|
|
67
|
+
### `init(options)`
|
|
68
|
+
|
|
69
|
+
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.
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
Monitor.init({
|
|
73
|
+
dsn: 'https://<publicKey>@<host>/<projectId>',
|
|
74
|
+
environment: 'production',
|
|
75
|
+
release: 'robo-nfe@1.4.2',
|
|
76
|
+
serverName: 'vm-cliente-01',
|
|
77
|
+
tags: { squad: 'automacoes' },
|
|
78
|
+
heartbeat: true, // "estou vivo" a cada 30s
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| Opção | Tipo | Default | Descrição |
|
|
83
|
+
| --- | --- | --- | --- |
|
|
84
|
+
| `dsn` | `string` | **obrigatório** | `https://<publicKey>@host/<projectId>` |
|
|
85
|
+
| `environment` | `string` | `'production'` | ambiente lógico |
|
|
86
|
+
| `release` | `string` | — | versão do programa, ex. `robo-nfe@1.4.2` |
|
|
87
|
+
| `serverName` | `string` | hostname | identifica a máquina; também vira `instanceId` do heartbeat |
|
|
88
|
+
| `tags` | `Record<string,string>` | — | tags aplicadas a todos os eventos |
|
|
89
|
+
| `sampleRate` | `number` | `1` | amostragem de erros (0..1) |
|
|
90
|
+
| `tracesSampleRate` | `number` | `1` | amostragem de transactions (0..1) |
|
|
91
|
+
| `maxBreadcrumbs` | `number` | `100` | tamanho do buffer de breadcrumbs |
|
|
92
|
+
| `captureUnhandled` | `boolean` | `true` | handlers globais de erro (`uncaughtException`/`unhandledRejection`) |
|
|
93
|
+
| `heartbeat` | `boolean \| HeartbeatOptions` | `false` | heartbeat periódico (`true` usa defaults) |
|
|
94
|
+
| `beforeSend` | `(event) => event \| null` | — | edita/descarta evento (retorne `null` para descartar) |
|
|
95
|
+
| `debug` | `boolean` | `false` | loga atividade do SDK |
|
|
96
|
+
| `flushIntervalMs` | `number` | `2000` | intervalo do flush automático do transport |
|
|
97
|
+
| `maxBatchSize` | `number` | `10` | itens por envelope antes do flush imediato |
|
|
98
|
+
|
|
99
|
+
### Captura de erros e mensagens
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
try {
|
|
103
|
+
await processarNota(nota);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
Monitor.captureException(error, {
|
|
106
|
+
level: 'error',
|
|
107
|
+
tags: { cliente: 'acme' },
|
|
108
|
+
fingerprint: ['sefaz-timeout'], // agrupa manualmente; sem isso o servidor agrupa pelo stack
|
|
109
|
+
});
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
Monitor.captureMessage('Lote processado com sucesso', 'info');
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Ambas retornam o `event_id` (ou `undefined` se não inicializado). O hint aceita `level`, `tags`, `extra` e `fingerprint`. Erros **não tratados** são capturados e flushados automaticamente antes do processo sair — desligue com `captureUnhandled: false`.
|
|
117
|
+
|
|
118
|
+
`Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug'`.
|
|
119
|
+
|
|
120
|
+
### Breadcrumbs
|
|
121
|
+
|
|
122
|
+
Trilha do que aconteceu antes do erro; enviada junto do próximo evento (buffer limitado por `maxBreadcrumbs`, mais antigos descartados).
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
Monitor.addBreadcrumb({ category: 'nfe', message: 'Baixou XML da nota 123', level: 'info' });
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Escopo: tags, usuário, contexto, extras
|
|
129
|
+
|
|
130
|
+
Dados anexados a **todos** os eventos seguintes:
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
Monitor.setTag('regiao', 'nordeste');
|
|
134
|
+
Monitor.setTags({ squad: 'automacoes', cliente: 'acme' });
|
|
135
|
+
Monitor.setUser({ id: 'op-42', email: 'op@acme.com' });
|
|
136
|
+
Monitor.setContext('vm', { cpu: 4, ram: '8GB' });
|
|
137
|
+
Monitor.setExtra('ultimoLote', 128);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Para um escopo **isolado** (sem vazar para o global), use `withScope`:
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
Monitor.withScope((scope) => {
|
|
144
|
+
scope.setTag('lote', '128').setFingerprint(['reprocessamento']);
|
|
145
|
+
Monitor.captureMessage('Reprocessando lote 128');
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Performance: transactions e spans
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
const tx = Monitor.startTransaction('processar-lote', 'rpa.job');
|
|
153
|
+
|
|
154
|
+
const span = tx.startChild('http.client', 'GET /api/notas');
|
|
155
|
+
await api.buscarNotas();
|
|
156
|
+
span.setData('notas', 42).finish();
|
|
157
|
+
|
|
158
|
+
const filho = span.startChild('db.query', 'INSERT INTO notas'); // spans aninham
|
|
159
|
+
await gravar();
|
|
160
|
+
filho.finish();
|
|
161
|
+
|
|
162
|
+
tx.setStatus('ok'); // ou 'error'
|
|
163
|
+
tx.finish(); // só aqui a transaction é enviada (respeitando tracesSampleRate)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
- `startChild(op?, description?)` existe em `Transaction` **e** `Span` (aninhamento arbitrário).
|
|
167
|
+
- Métodos encadeáveis: `setStatus`, `setData` (span), `setTag`/`setName` (transaction).
|
|
168
|
+
- `finish()` na transaction fecha os spans abertos e envia o payload; nada é enviado se ela não foi amostrada.
|
|
169
|
+
|
|
170
|
+
### Crons (check-ins)
|
|
171
|
+
|
|
172
|
+
Monitora jobs agendados: check-in `in_progress` na entrada, `ok`/`error` na saída, com duração medida. O jeito recomendado é `withMonitor`, que faz o par e captura exceções:
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
await Monitor.withMonitor(
|
|
176
|
+
'sync-notas',
|
|
177
|
+
() => executarSync(),
|
|
178
|
+
{ schedule: '0 */2 * * *', timezone: 'America/Fortaleza' }
|
|
179
|
+
);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
O `schedule` (expressão cron) faz o servidor **criar/atualizar o monitor automaticamente** (upsert) e habilita a detecção de **execução perdida**. Para controle manual:
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
const id = Monitor.checkIn({ monitorSlug: 'sync-notas', status: 'in_progress' });
|
|
186
|
+
// … trabalho …
|
|
187
|
+
Monitor.checkIn({ monitorSlug: 'sync-notas', status: 'ok', checkInId: id, durationMs: 1234 });
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Heartbeat (uptime)
|
|
191
|
+
|
|
192
|
+
Bate "estou vivo" periodicamente; o servidor acusa o robô **offline** quando os batimentos param.
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
Monitor.init({
|
|
196
|
+
dsn,
|
|
197
|
+
heartbeat: { intervalMs: 30_000, name: 'Robô NFe — VM 01' },
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`heartbeat: true` usa os defaults. Campos: `intervalMs` (30000), `instanceId` (default `serverName`/`HOSTNAME`/id aleatório), `name`, `metadata`.
|
|
202
|
+
|
|
203
|
+
### Flush e close
|
|
204
|
+
|
|
205
|
+
```js
|
|
206
|
+
await Monitor.flush(2000); // envia o buffer, mantém o SDK vivo
|
|
207
|
+
await Monitor.close(2000); // flush final + para o heartbeat + fecha o transport
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Chame `close` antes do processo sair. Os timers do Monitor são `unref`'d — ele **não** segura o processo vivo sozinho.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Manager
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
import * as Manager from 'hobots/manager';
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Capability de **execução remota sob demanda**. O agente faz polling no app, executa os handlers registrados quando aparece trabalho e reporta status, logs incrementais e resultado. Credencial própria (**manager key**), singletons próprios, nenhum handler global instalado.
|
|
221
|
+
|
|
222
|
+
### Ciclo de vida
|
|
223
|
+
|
|
224
|
+
```js
|
|
225
|
+
Manager.init({ dsn: 'https://<managerKey>@<host>/<projectSlug>', agentName: 'VM Cliente 01' });
|
|
226
|
+
|
|
227
|
+
Manager.register('processar-nfe', async (params, ctx) => {
|
|
228
|
+
ctx.log('info', `processando lote ${params.lote}…`);
|
|
229
|
+
if (ctx.signal.aborted) return; // cancelamento cooperativo
|
|
230
|
+
return { processadas: 12 }; // vira o resultado da execução no app
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
Manager.start(); // começa o polling e MANTÉM O PROCESSO VIVO
|
|
234
|
+
|
|
235
|
+
process.on('SIGINT', () => {
|
|
236
|
+
void Manager.stop(15_000).then(() => process.exit(0));
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
- `init(options)` — cria o agente (singleton). Chamar de novo para o anterior e substitui.
|
|
241
|
+
- `register(task, handler)` — `task` é o `task_slug` configurado no formulário do app. Registre **antes** de `start()`.
|
|
242
|
+
- `start()` — começa o polling. Lança se `init()` não foi chamado ou se nenhuma task foi registrada.
|
|
243
|
+
- `stop(timeoutMs?)` — para o polling e aguarda as runs ativas (default 30s). Retorna `false` se estourou o timeout.
|
|
244
|
+
|
|
245
|
+
| Opção (`ManagerInitOptions`) | Tipo | Default | Descrição |
|
|
246
|
+
| --- | --- | --- | --- |
|
|
247
|
+
| `dsn` | `string` | **obrigatório** | com a **manager key** (não a chave de ingestão) |
|
|
248
|
+
| `pollIntervalMs` | `number` | `5000` | intervalo entre polls sem trabalho (mínimo `1000`) |
|
|
249
|
+
| `concurrency` | `number` | `1` | runs simultâneas (clamp 1..5) |
|
|
250
|
+
| `agentName` | `string` | hostname | nome exibido no dashboard |
|
|
251
|
+
| `taskTimeoutMs` | `number` | — | timeout client-side por run (não mata o handler; servidor é o backstop) |
|
|
252
|
+
| `logFlushIntervalMs` | `number` | `2000` | intervalo do envio incremental de logs |
|
|
253
|
+
| `debug` | `boolean` | `false` | loga atividade do agente |
|
|
254
|
+
|
|
255
|
+
### O handler e o `RunContext`
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
type ManagerTaskHandler<P = Record<string, unknown>, R = unknown> =
|
|
259
|
+
(params: P, ctx: RunContext) => R | Promise<R>;
|
|
260
|
+
|
|
261
|
+
interface RunContext {
|
|
262
|
+
runId: string;
|
|
263
|
+
task: string;
|
|
264
|
+
log(level: 'debug' | 'info' | 'warning' | 'error', message: string): void;
|
|
265
|
+
signal: AbortSignal;
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
- **`params`** — os dados do formulário configurado no app (JSON validado). Tipável: `Manager.register<{ lote: number }, { processadas: number }>(...)`.
|
|
270
|
+
- **`ctx.log(level, message)`** — logs enviados **incrementalmente** ao app (PATCHes com throttle), visíveis na run quase em tempo real.
|
|
271
|
+
- **`ctx.signal`** — `AbortSignal` que dispara quando a run é **cancelada no app**, em **timeout** ou no **shutdown**.
|
|
272
|
+
- **Retorno** — vira o resultado exibido no app (JSON, cap ~100KB; maiores/circulares são descartados com marcador `__truncated`).
|
|
273
|
+
- **Erro lançado** — a run é reportada como `failed` (mensagem cap 10k, stack 20k).
|
|
274
|
+
|
|
275
|
+
### Cancelamento cooperativo
|
|
276
|
+
|
|
277
|
+
Uma run cancelada no app **não mata** o handler — o SDK apenas dispara `ctx.signal`. **Verifique `ctx.signal.aborted`** em loops longos:
|
|
278
|
+
|
|
279
|
+
```js
|
|
280
|
+
Manager.register('processar-nfe', async (params, ctx) => {
|
|
281
|
+
for (const nota of params.notas) {
|
|
282
|
+
if (ctx.signal.aborted) {
|
|
283
|
+
ctx.log('warning', 'cancelamento detectado — parando');
|
|
284
|
+
return { canceladas: true };
|
|
285
|
+
}
|
|
286
|
+
await processar(nota);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Concorrência
|
|
292
|
+
|
|
293
|
+
`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`.
|
|
294
|
+
|
|
295
|
+
### Timeout por run
|
|
296
|
+
|
|
297
|
+
`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.
|
|
298
|
+
|
|
299
|
+
### Idempotência (at-least-once)
|
|
300
|
+
|
|
301
|
+
A entrega é **at-least-once**: se o agente morre logo após capturar uma run (antes de reportar o resultado), ela é re-entregue quando o agente voltar. Escreva handlers **idempotentes** (use uma chave de deduplicação sua, como o número da nota).
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Encerramento gracioso
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
// Monitor: não segura o processo, mas faça o flush final para não perder eventos
|
|
309
|
+
await Monitor.close(2000);
|
|
310
|
+
|
|
311
|
+
// Manager: segura o processo por design — aguarde a run atual
|
|
312
|
+
process.on('SIGINT', () => {
|
|
313
|
+
void Manager.stop(15_000).then(() => process.exit(0));
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Entry points / exports
|
|
320
|
+
|
|
321
|
+
| Import | Conteúdo |
|
|
322
|
+
| --- | --- |
|
|
323
|
+
| `hobots` | Monitor: funções, `Client`, `Scope`, `Transaction`, `Span`, `parseDsn`, tipos. |
|
|
324
|
+
| `hobots/protocol` | Tipos do wire format do Monitor. |
|
|
325
|
+
| `hobots/manager` | Manager: `init`/`register`/`start`/`stop`, `ManagerAgent`, `ManagerApi`, `ManagerApiError`, `parseManagerDsn`, tipos. |
|
|
326
|
+
| `hobots/manager/protocol` | Tipos do wire format do Manager. |
|
|
327
|
+
|
|
328
|
+
Empacotado em **ESM e CommonJS** com **definições de tipos** (`.d.ts`) para todas as entradas.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Documentação completa
|
|
333
|
+
|
|
334
|
+
Guias detalhados acompanham o pacote em [`docs/`](./docs/README.md):
|
|
335
|
+
|
|
336
|
+
- [Getting started](./docs/getting-started.md) — instalação, DSN, primeiro `init`, Monitor vs Manager.
|
|
337
|
+
- [Monitor](./docs/monitor.md) — erros, breadcrumbs, escopo, transactions/spans, crons, heartbeat.
|
|
338
|
+
- [Manager](./docs/manager.md) — handlers, `RunContext`, cancelamento, concorrência, idempotência.
|
|
339
|
+
- [Referência de API](./docs/api-reference.md) — todas as funções e tabelas de opções com defaults.
|
|
340
|
+
- [Protocolo & transporte](./docs/protocol.md) — DSNs, endpoints, envelope, retries.
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Licença
|
|
345
|
+
|
|
346
|
+
[MIT](./LICENSE) © Hobots
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/dsn.ts
|
|
2
|
+
function parseDsnUrl(dsn, prefix) {
|
|
3
|
+
let url;
|
|
4
|
+
try {
|
|
5
|
+
url = new URL(dsn);
|
|
6
|
+
} catch {
|
|
7
|
+
throw new Error(`${prefix} DSN inv\xE1lido: "${dsn}"`);
|
|
8
|
+
}
|
|
9
|
+
const key = url.username;
|
|
10
|
+
const path = url.pathname.replace(/^\/+/, "");
|
|
11
|
+
if (!key || !path) {
|
|
12
|
+
throw new Error(`${prefix} DSN inv\xE1lido: "${dsn}" \u2014 esperado https://<key>@host/<projeto>`);
|
|
13
|
+
}
|
|
14
|
+
return { key, path, origin: `${url.protocol}//${url.host}` };
|
|
15
|
+
}
|
|
16
|
+
function parseDsn(dsn) {
|
|
17
|
+
const { key, path, origin } = parseDsnUrl(dsn, "[hobots-monitor]");
|
|
18
|
+
return {
|
|
19
|
+
publicKey: key,
|
|
20
|
+
projectId: path,
|
|
21
|
+
endpoint: `${origin}/api/ingest/${key}`
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/utils.ts
|
|
26
|
+
var SDK_NAME = "hobots";
|
|
27
|
+
var SDK_VERSION = "1.0.0";
|
|
28
|
+
function isNode() {
|
|
29
|
+
return typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
30
|
+
}
|
|
31
|
+
function uuid4() {
|
|
32
|
+
const cryptoRef = globalThis.crypto;
|
|
33
|
+
if (cryptoRef && typeof cryptoRef.randomUUID === "function") {
|
|
34
|
+
return cryptoRef.randomUUID().replace(/-/g, "");
|
|
35
|
+
}
|
|
36
|
+
let out = "";
|
|
37
|
+
for (let i = 0; i < 32; i++) {
|
|
38
|
+
out += Math.floor(Math.random() * 16).toString(16);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
function nowISO() {
|
|
43
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
44
|
+
}
|
|
45
|
+
function unrefTimer(timer) {
|
|
46
|
+
timer?.unref?.();
|
|
47
|
+
}
|
|
48
|
+
function sleep(ms) {
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
const timer = setTimeout(resolve, ms);
|
|
51
|
+
unrefTimer(timer);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
var PREFIX = "[hobots-monitor]";
|
|
55
|
+
function makeLogger(enabled, prefix = PREFIX) {
|
|
56
|
+
return {
|
|
57
|
+
debug: (...args) => enabled && console.debug(prefix, ...args),
|
|
58
|
+
warn: (...args) => enabled && console.warn(prefix, ...args),
|
|
59
|
+
error: (...args) => console.error(prefix, ...args)
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
parseDsnUrl,
|
|
65
|
+
parseDsn,
|
|
66
|
+
SDK_NAME,
|
|
67
|
+
SDK_VERSION,
|
|
68
|
+
isNode,
|
|
69
|
+
uuid4,
|
|
70
|
+
nowISO,
|
|
71
|
+
unrefTimer,
|
|
72
|
+
sleep,
|
|
73
|
+
makeLogger
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=chunk-FW32OR7V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dsn.ts","../src/utils.ts"],"sourcesContent":["export interface ParsedDsn {\n publicKey: string;\n projectId: string;\n /** URL de ingestão derivada: <origin>/api/ingest/<publicKey> */\n endpoint: string;\n}\n\ninterface ParsedDsnUrl {\n key: string;\n path: string;\n origin: string;\n}\n\n/** Parser baixo nível compartilhado entre Monitor e Manager (não exportado no barrel). */\nexport function parseDsnUrl(dsn: string, prefix: string): ParsedDsnUrl {\n let url: URL;\n try {\n url = new URL(dsn);\n } catch {\n throw new Error(`${prefix} DSN inválido: \"${dsn}\"`);\n }\n\n const key = url.username;\n const path = url.pathname.replace(/^\\/+/, '');\n if (!key || !path) {\n throw new Error(`${prefix} DSN inválido: \"${dsn}\" — esperado https://<key>@host/<projeto>`);\n }\n\n return { key, path, origin: `${url.protocol}//${url.host}` };\n}\n\n/** Formato: https://<publicKey>@host[:porta]/<projectId> */\nexport function parseDsn(dsn: string): ParsedDsn {\n const { key, path, origin } = parseDsnUrl(dsn, '[hobots-monitor]');\n return {\n publicKey: key,\n projectId: path,\n endpoint: `${origin}/api/ingest/${key}`,\n };\n}\n","export const SDK_NAME = 'hobots';\nexport const SDK_VERSION = '1.0.0';\n\nexport function isNode(): boolean {\n return typeof process !== 'undefined' && typeof process.versions?.node === 'string';\n}\n\n/** uuid v4 sem hífens (32 hex). Fallback para contextos sem crypto.randomUUID. */\nexport function uuid4(): string {\n const cryptoRef = (globalThis as { crypto?: Crypto }).crypto;\n if (cryptoRef && typeof cryptoRef.randomUUID === 'function') {\n return cryptoRef.randomUUID().replace(/-/g, '');\n }\n let out = '';\n for (let i = 0; i < 32; i++) {\n out += Math.floor(Math.random() * 16).toString(16);\n }\n return out;\n}\n\nexport function nowISO(): string {\n return new Date().toISOString();\n}\n\n/** Timers não devem segurar o processo Node aberto. */\nexport function unrefTimer(timer: unknown): void {\n (timer as { unref?: () => void } | undefined)?.unref?.();\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise(resolve => {\n const timer = setTimeout(resolve, ms);\n unrefTimer(timer);\n });\n}\n\nexport interface Logger {\n debug: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n}\n\nconst PREFIX = '[hobots-monitor]';\n\nexport function makeLogger(enabled: boolean, prefix: string = PREFIX): Logger {\n return {\n debug: (...args) => enabled && console.debug(prefix, ...args),\n warn: (...args) => enabled && console.warn(prefix, ...args),\n error: (...args) => console.error(prefix, ...args),\n };\n}\n"],"mappings":";AAcO,SAAS,YAAY,KAAa,QAA8B;AACrE,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,GAAG;AAAA,EACnB,QAAQ;AACN,UAAM,IAAI,MAAM,GAAG,MAAM,sBAAmB,GAAG,GAAG;AAAA,EACpD;AAEA,QAAM,MAAM,IAAI;AAChB,QAAM,OAAO,IAAI,SAAS,QAAQ,QAAQ,EAAE;AAC5C,MAAI,CAAC,OAAO,CAAC,MAAM;AACjB,UAAM,IAAI,MAAM,GAAG,MAAM,sBAAmB,GAAG,gDAA2C;AAAA,EAC5F;AAEA,SAAO,EAAE,KAAK,MAAM,QAAQ,GAAG,IAAI,QAAQ,KAAK,IAAI,IAAI,GAAG;AAC7D;AAGO,SAAS,SAAS,KAAwB;AAC/C,QAAM,EAAE,KAAK,MAAM,OAAO,IAAI,YAAY,KAAK,kBAAkB;AACjE,SAAO;AAAA,IACL,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU,GAAG,MAAM,eAAe,GAAG;AAAA,EACvC;AACF;;;ACvCO,IAAM,WAAW;AACjB,IAAM,cAAc;AAEpB,SAAS,SAAkB;AAChC,SAAO,OAAO,YAAY,eAAe,OAAO,QAAQ,UAAU,SAAS;AAC7E;AAGO,SAAS,QAAgB;AAC9B,QAAM,YAAa,WAAmC;AACtD,MAAI,aAAa,OAAO,UAAU,eAAe,YAAY;AAC3D,WAAO,UAAU,WAAW,EAAE,QAAQ,MAAM,EAAE;AAAA,EAChD;AACA,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,WAAO,KAAK,MAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS,EAAE;AAAA,EACnD;AACA,SAAO;AACT;AAEO,SAAS,SAAiB;AAC/B,UAAO,oBAAI,KAAK,GAAE,YAAY;AAChC;AAGO,SAAS,WAAW,OAAsB;AAC/C,EAAC,OAA8C,QAAQ;AACzD;AAEO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,aAAW;AAC5B,UAAM,QAAQ,WAAW,SAAS,EAAE;AACpC,eAAW,KAAK;AAAA,EAClB,CAAC;AACH;AAQA,IAAM,SAAS;AAER,SAAS,WAAW,SAAkB,SAAiB,QAAgB;AAC5E,SAAO;AAAA,IACL,OAAO,IAAI,SAAS,WAAW,QAAQ,MAAM,QAAQ,GAAG,IAAI;AAAA,IAC5D,MAAM,IAAI,SAAS,WAAW,QAAQ,KAAK,QAAQ,GAAG,IAAI;AAAA,IAC1D,OAAO,IAAI,SAAS,QAAQ,MAAM,QAAQ,GAAG,IAAI;AAAA,EACnD;AACF;","names":[]}
|