hobots 2.3.0 → 3.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/README.md +112 -369
- package/dist/index.cjs +1233 -852
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +223 -205
- package/dist/index.d.ts +223 -205
- package/dist/index.js +1233 -852
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +43 -4
- package/dist/protocol.d.ts +43 -4
- package/docs/README.md +16 -33
- package/docs/api-reference.md +74 -213
- package/docs/concepts.md +60 -389
- package/docs/getting-started.md +38 -84
- package/docs/protocol.md +29 -19
- package/docs/tasks.md +92 -251
- package/package.json +2 -2
- package/docs/telemetry.md +0 -162
package/docs/api-reference.md
CHANGED
|
@@ -4,247 +4,108 @@
|
|
|
4
4
|
|
|
5
5
|
| Import | Conteúdo |
|
|
6
6
|
| --- | --- |
|
|
7
|
-
| `hobots` |
|
|
7
|
+
| `hobots` | `hobots` (default e named), `register`, `start`, `close`, `captureException`, `retry` e os tipos. |
|
|
8
8
|
| `hobots/protocol` | Tipos do wire format (telemetria e tasks). |
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
ESM e CommonJS (`const { hobots } = require('hobots')`), com `.d.ts`. Depois da primeira task, `globalThis.hobots` também existe (tipado via `declare global`).
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Funções do processo
|
|
13
13
|
|
|
14
|
-
| Função | Assinatura |
|
|
14
|
+
| Função | Assinatura | Notas |
|
|
15
15
|
| --- | --- | --- |
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `setTag` / `setTags` | `setTag(key, value)` · `setTags(tags)` | — |
|
|
22
|
-
| `setUser` | `setUser(user: EventUser \| null): void` | — |
|
|
23
|
-
| `setContext` | `setContext(name: string, ctx: Record<string,unknown> \| null): void` | — |
|
|
24
|
-
| `setExtra` | `setExtra(key: string, value: unknown): void` | — |
|
|
25
|
-
| `withScope` | `withScope<T>(cb: (scope: Scope) => T): T` | retorno do callback |
|
|
26
|
-
| `startTransaction` | `startTransaction(name: string, op?: string): Transaction` | transaction (dentro de um handler de task, a própria `ctx.tx`) |
|
|
27
|
-
| `flush` | `flush(timeoutMs = 5_000): Promise<boolean>` | enviou tudo dentro do prazo? |
|
|
28
|
-
| `close` | `close(timeoutMs = 5_000): Promise<boolean>` | para o polling de tasks (aguarda runs) + heartbeat + flush final do transport |
|
|
29
|
-
|
|
30
|
-
### O que cada função faz
|
|
31
|
-
|
|
32
|
-
Os conceitos por trás dessas chamadas estão em [Conceitos](./concepts.md); aqui está o que cada uma **faz** e o que **devolve**.
|
|
33
|
-
|
|
34
|
-
- **`init(options)`** — cria o cliente, instala os handlers globais de erro (a menos que `captureUnhandled: false`), inicia o heartbeat se configurado e cria o agente de tasks se `tasks` foi passado. Chamar de novo **substitui** o cliente e o agente (os handlers registrados com `register` são preservados; o [scope](./concepts.md#scope) **não** é limpo). Chame uma vez, o mais cedo possível.
|
|
35
|
-
- **`getClient()`** — devolve o cliente atual ou `undefined` se o `init()` nunca rodou. Útil para checar se o SDK está ativo. A config resolvida (URL base e endpoints) é acessível via `getClient()?.config` (veja abaixo).
|
|
36
|
-
- **`captureException(error, hint?)`** — monta um [evento](./concepts.md#evento) a partir da exceção (tipo, mensagem, stack trace), mescla o scope e enfileira. O `type` é `error` a menos que você passe outro no `hint`.
|
|
37
|
-
- **`captureMessage(message, type = 'warning', hint?)`** — mesma coisa, sem exceção: um evento só com texto. Bom para marcos (`'success'`) e avisos que não são erro.
|
|
38
|
-
> **Atenção:** as duas devolvem um `event_id` **mesmo quando o evento foi descartado** por [amostragem](./concepts.md#amostragem-samplerate-e-tracessamplerate) ou por [`beforeSend`](./concepts.md#beforesend). O retorno não prova que algo foi enviado — só é `undefined` quando o `init()` não foi chamado.
|
|
39
|
-
- **`addBreadcrumb(crumb)`** — adiciona uma migalha à trilha do [scope](./concepts.md#scope) global. Todos os campos são opcionais menos o `timestamp`, que o SDK preenche. A lista **não** é limpa ao enviar um evento; ela só descarta as mais antigas ao estourar `maxBreadcrumbs`. Conceito: [Breadcrumb](./concepts.md#breadcrumb).
|
|
40
|
-
- **`setTag(key, value)` / `setTags(tags)`** — grava [tags](./concepts.md#tag) no scope global; valem para todos os eventos seguintes. Só `string` → `string`. Precedência: `init.tags` → scope → evento → `hint`.
|
|
41
|
-
- **`setUser(user | null)`** — define quem está operando (`id`, `username`, `email`). `null` remove.
|
|
42
|
-
- **`setContext(name, ctx | null)`** — define um [context](./concepts.md#context) nomeado. Passar `null` **apaga o grupo inteiro**. Os grupos `runtime` e `os` são injetados pelo SDK em todo evento.
|
|
43
|
-
- **`setExtra(key, value)`** — anexa um dado livre, sem estrutura e sem índice. Conceito e critério de escolha em [Extra](./concepts.md#extra).
|
|
44
|
-
- **`withScope(cb)`** — roda o callback com um **clone** do scope e restaura o original no fim; devolve o que o callback devolver. É **síncrono** — um `await` dentro do callback não isola de código concorrente. Conceito: [withScope](./concepts.md#withscope).
|
|
45
|
-
- **`startTransaction(name, op?)`** — abre uma [transaction](./concepts.md#transaction) de performance. A decisão de [amostragem](./concepts.md#amostragem-samplerate-e-tracessamplerate) acontece **aqui**, não no `finish()`. Nada é enviado até o `finish()`. **Dentro de um handler de task**, devolve a [transaction da própria run](./concepts.md#transaction-de-run-ctxtx) (`ctx.tx`) em vez de criar uma segunda — cada execução tem exatamente uma transaction; fora de um handler, cria uma transaction nova, sem `run_id`.
|
|
46
|
-
- **`flush(timeoutMs = 5_000)`** → `Promise<boolean>` — empurra a fila do transport e aguarda os envios em andamento. `true` = tudo saiu no prazo; `false` = o SDK parou de esperar (os envios em voo seguem tentando). O SDK **continua utilizável** depois. Conceito: [Fila, flush e close](./concepts.md#fila-flush-e-close).
|
|
47
|
-
- **`close(timeoutMs = 5_000)`** → `Promise<boolean>` — encerra tudo na ordem agente → heartbeat → flush final. O `timeoutMs` é gasto **duas vezes** (espera das runs, depois flush). Depois do `close()` o transport está fechado e todo item novo é descartado com aviso no console; não há `reopen()`. Conceito: [Fila, flush e close](./concepts.md#fila-flush-e-close).
|
|
48
|
-
|
|
49
|
-
Classes exportadas: `Client`, `Scope`, `Transaction`, `Step`. A resolução da credencial nos endpoints é interna ao `init()` (constante `DEFAULT_BASE_URL = 'https://api2.hobots.app'`, exportada; em desenvolvimento a env `HOBOTS_API_URL` sobrescreve a URL base). A config resolvida fica acessível em `getClient()?.config`:
|
|
16
|
+
| `hobots.register` | `<P>(task: string, handler: TaskHandler<P>, options?: RegisterOptions) => void` | Task sob demanda. Síncrona: valida slug/handler/secret e lança na hora. O boot roda num microtask — chamadas seguidas entram no primeiro poll. Slug repetido lança. |
|
|
17
|
+
| `hobots.start` | `(task: string, handler: TaskHandler, options?: StartOptions) => void` | Task sempre ligada: abre a própria execução e roda o handler uma vez. |
|
|
18
|
+
| `hobots.close` | `(timeoutMs = 30_000) => Promise<boolean>` | Para o polling, espera as execuções em voo (até `timeoutMs`; as restantes viram `AgentTerminated`), para o heartbeat e faz o flush final. `false` se estourou. Um `register`/`start` depois inicia um runtime novo. |
|
|
19
|
+
| `hobots.captureException` | `(error: unknown, hint?: CaptureHint) => string \| undefined` | Issue no módulo Issues; devolve o `event_id`. Antes de qualquer task: descarta com aviso. |
|
|
20
|
+
| `hobots.retry` | `<T>(fn: (attempt: number) => T \| Promise<T>, options?: RetryOptions) => Promise<T>` | async-retry; dentro de uma execução usa o signal dela. |
|
|
50
21
|
|
|
51
|
-
|
|
52
|
-
getClient()?.config; // {
|
|
53
|
-
// key: string; — Client Secret do agente (vai no Authorization: Bearer, nunca na URL)
|
|
54
|
-
// baseUrl: string; — DEFAULT_BASE_URL, ou HOBOTS_API_URL em desenvolvimento
|
|
55
|
-
// ingestEndpoint: string; — <baseUrl>/v1/ingest
|
|
56
|
-
// tasksBaseUrl: string; — <baseUrl>/v1/tasks
|
|
57
|
-
// }
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Métodos de `Transaction` / `Step` / `Scope`
|
|
61
|
-
|
|
62
|
-
Todos encadeáveis, exceto `finish()` e `toJSON()`.
|
|
63
|
-
|
|
64
|
-
- **`Transaction`** — `startChild(op?, description?) → Step` abre uma [step](./concepts.md#step) direta; `setTag(k, v)` marca a transaction (tags de scope **não** são herdadas); `setStatus(s)` (default `'ok'`); `setName(n)` renomeia antes do envio; `finish()` fecha as etapas abertas, monta o payload e envia — é **idempotente** e não envia nada se a transaction não foi amostrada.
|
|
65
|
-
- **`Step`** — `startChild(op?, description?) → Step` aninha uma filha (profundidade arbitrária); `setStatus(s)` (default `'ok'`); `setData(k, v)` anexa dados só àquela step; `finish()` marca o fim — **opcional**, porque o `finish()` da transaction fecha o que ficou aberto; `toJSON()` serializa no formato do wire.
|
|
66
|
-
- **`Scope`** — `setTag`/`setTags`/`setUser`/`setContext`/`setExtra`/`setExtras`/`setType`/`setFingerprint`/`addBreadcrumb`/`clearBreadcrumbs`/`clone`. Alcançável via [`withScope`](./concepts.md#withscope). `setContext(name, null)` apaga o grupo; `setUser(null)` remove o usuário.
|
|
67
|
-
> **Atenção:** `setType` **não tem efeito** em `captureException` (que já grava `error`) nem em `captureMessage` (que já grava `warning`) — os dois definem o tipo do evento antes de o scope ser consultado. Use o `hint`.
|
|
22
|
+
## Contexto da execução (`RunContext` — os membros de `hobots` e o `ctx` do handler)
|
|
68
23
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- **`start()`** — começa o polling. **Mantém o processo vivo** até `stop()`/`close()` — o timer não é `unref`'d, ao contrário dos da telemetria. Lança se o `init()` não configurou `tasks` ou se nenhuma task foi registrada. Também instala os handlers de saída (SIGINT/SIGTERM/erro fatal) que reportam as runs ativas como `AgentTerminated`, a menos que `installExitHandlers: false`.
|
|
85
|
-
- **`stop(timeoutMs = 30_000)`** → `Promise<boolean>` — encerra o polling e aguarda as [runs](./concepts.md#run) ativas terminarem. Para **só as tasks**: a telemetria (heartbeat, captura de erros) continua funcionando. Devolve `false` se estourou o timeout — as runs que ainda estavam rodando são reportadas como `AgentTerminated`. Para encerrar tudo, use [`close()`](./concepts.md#fila-flush-e-close).
|
|
86
|
-
|
|
87
|
-
No poll, o agente se identifica com `InstanceInfo` (`hobots/protocol`): o campo `instance_id` (o `instanceId` do `init()`) é a identidade canônica do agente — a mesma do heartbeat de telemetria, então os dois canais convergem em um agente no painel.
|
|
88
|
-
|
|
89
|
-
---
|
|
24
|
+
| Membro | Tipo | Notas |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| `runId`, `task` | `string` | Identidade da execução (`''` fora de uma execução) |
|
|
27
|
+
| `params` | `Record<string, unknown>` | Params da solicitação; `{}` no modo start |
|
|
28
|
+
| `log` | `RunLogger` — `debug/info/warn/error/success(message)` | Nunca lança |
|
|
29
|
+
| `items` | `RunItems` — `add(item)`, `succeeded(payload?, opts?)`, `failed(message, payload?, opts?)`, `occurrence(message, payload?, opts?)`; `opts.id` = identidade de negócio | Ponto de parada cooperativo |
|
|
30
|
+
| `progress` | `RunProgressReporter` — `total(n)`, `advance(count = 1)`, `set(current, total?)`, `current`, `snapshot` | `total/advance/set` são pontos de parada |
|
|
31
|
+
| `transaction` | `Transaction` — `startChild(op?, description?)`, `setTag`, `setStatus`, `setName`; `Step` — `startChild`, `setStatus('ok' \| 'warning' \| 'error')`, `setData(k, v)`, `finish()` | Finalizada pelo SDK; `startChild` é ponto de parada |
|
|
32
|
+
| `signal` | `AbortSignal` | Cancelamento (app, timeout, shutdown) |
|
|
33
|
+
| `throwIfCanceled()` | `() => void` | Lança `RunCanceled` se o signal disparou |
|
|
34
|
+
| `sleep(ms)` | `(ms: number) => Promise<void>` | Rejeita com `RunCanceled` no cancelamento |
|
|
35
|
+
| `attach(image, options?)` | `(image: AttachInput, options?: AttachOptions) => Promise<AttachResult>` | `AttachInput = Uint8Array \| ArrayBuffer \| string` (caminho); `AttachOptions = { caption?, level?, message?, contentType? }`; `AttachResult = { id, ok, reason? }`. Nunca lança |
|
|
36
|
+
| `retry(fn, options?)` | como `hobots.retry` | |
|
|
37
|
+
|
|
38
|
+
Fora de uma execução, `hobots.*` cai em fallbacks seguros com aviso único por membro (log ecoa no terminal; items/progress no-op; `signal` nunca aborta; `attach` resolve `{ ok: false }`).
|
|
90
39
|
|
|
91
40
|
## Tabelas de opções
|
|
92
41
|
|
|
93
|
-
###
|
|
42
|
+
### `HobotsOptions` (processo — a primeira chamada define; divergências avisam e são ignoradas)
|
|
94
43
|
|
|
95
44
|
| Opção | Tipo | Default | Descrição |
|
|
96
45
|
| --- | --- | --- | --- |
|
|
97
|
-
| `clientSecret` | `string` |
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `tags` | `Record<string,string>` | — | tags
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
| `tasks` | `boolean \| TasksOptions` | `false` | habilita a execução sob demanda (`true` usa defaults) |
|
|
109
|
-
| `beforeSend` | `(event) => event \| null` | — | edita/descarta evento (retorne `null` para descartar) |
|
|
110
|
-
| `debug` | `boolean` | `false` | loga atividade do SDK |
|
|
111
|
-
| `flushIntervalMs` | `number` | `2000` | intervalo do flush automático do transport |
|
|
112
|
-
| `maxBatchSize` | `number` | `10` | itens por envelope antes do flush imediato |
|
|
113
|
-
|
|
114
|
-
O teto da fila do transport (200 itens; ao encher, o **mais antigo** é descartado com aviso) é interno — **não** é opção do `init()`. Conceitos por trás dessas opções: [amostragem](./concepts.md#amostragem-samplerate-e-tracessamplerate), [breadcrumbs](./concepts.md#breadcrumb), [instanceId](./concepts.md#instanceid), [release e environment](./concepts.md#release-e-environment), [beforeSend](./concepts.md#beforesend).
|
|
115
|
-
|
|
116
|
-
### HeartbeatOptions
|
|
46
|
+
| `clientSecret` | `string` | env `HOBOTS_CLIENT_SECRET` | `hb_<64 hex>`; ausente/inválido lança citando a env (sem ecoar o valor) |
|
|
47
|
+
| `instanceId` | `string` | env `HOBOTS_INSTANCE_ID` → `os.hostname()` | identidade estável da instância |
|
|
48
|
+
| `environment` | `'development' \| 'testing' \| 'staging' \| 'production' \| string` | `'production'` | vai em eventos, transactions e na instância; filtra alertas |
|
|
49
|
+
| `release` | `string` | — | versão do programa |
|
|
50
|
+
| `tags` | `Record<string, string>` | — | tags de todos os eventos e transactions |
|
|
51
|
+
| `debug` | `boolean` | `false` | loga a atividade do SDK |
|
|
52
|
+
| `machineMetrics` | `boolean` | `true` | CPU/memória/disco no heartbeat/poll e em `contexts.machine` |
|
|
53
|
+
| `captureUnhandled` | `boolean` | `true` | handlers globais de erro (issues) |
|
|
54
|
+
| `installExitHandlers` | `boolean` | `true` | avançado — SIGINT/SIGTERM/uncaughtException reportam `AgentTerminated` |
|
|
55
|
+
|
|
56
|
+
### `TaskOptions extends HobotsOptions` (por task)
|
|
117
57
|
|
|
118
58
|
| Opção | Tipo | Default | Descrição |
|
|
119
59
|
| --- | --- | --- | --- |
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
### CaptureHint
|
|
127
|
-
|
|
128
|
-
| Campo | Tipo | Descrição |
|
|
129
|
-
| --- | --- | --- |
|
|
130
|
-
| `type` | `EventType` | tipo do evento |
|
|
131
|
-
| `tags` | `Record<string,string>` | tags só deste evento |
|
|
132
|
-
| `extra` | `Record<string,unknown>` | dados extras |
|
|
133
|
-
| `fingerprint` | `string[]` | agrupamento manual |
|
|
134
|
-
| `attachment` | `AttachInput \| CaptureAttachment` | print enviado junto da captura — dentro de uma execução de task, vira anexo da linha de log da run que espelha o evento; fora de uma execução é descartado com aviso. Guia: [Anexos](./tasks.md#anexos-screenshots) |
|
|
135
|
-
|
|
136
|
-
### CaptureAttachment
|
|
60
|
+
| `retries` | `number` | `1` | tentativas extras do handler; `0` desliga |
|
|
61
|
+
| `retryDelayMs` | `number \| (attempt) => number` | `5_000` | `attempt` 1-based (a que falhou) |
|
|
62
|
+
| `timeoutMs` | `number` | — | por tentativa; terminal (`TaskTimeout`) |
|
|
63
|
+
| `logFlushIntervalMs` | `number` | `2_000` | avançado |
|
|
64
|
+
| `cancelCheckIntervalMs` | `number` | `5_000` (1_000..15_000) | avançado — PATCH vazio que detecta cancelamento e é o sinal de vida da execução |
|
|
65
|
+
| `cancelGraceMs` | `number` | `10_000` | avançado — graça após o signal antes de liberar o slot |
|
|
137
66
|
|
|
138
|
-
`
|
|
67
|
+
### `RegisterOptions extends TaskOptions`
|
|
139
68
|
|
|
140
|
-
|
|
|
69
|
+
| Opção | Tipo | Default | Descrição |
|
|
141
70
|
| --- | --- | --- | --- |
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `contentType` | `'image/png' \| 'image/jpeg' \| 'image/webp'` | detectado pelos *magic bytes* | só usado quando a detecção falha; a detecção sempre tem precedência |
|
|
71
|
+
| `concurrency` | `number` | `1` | execuções simultâneas desta task (1..5) |
|
|
72
|
+
| `pollIntervalMs` | `number` | `2_000` (mín. 1_000) | intervalo entre polls sem trabalho; vale o do primeiro `register()` |
|
|
145
73
|
|
|
146
|
-
|
|
74
|
+
`StartOptions = TaskOptions`.
|
|
147
75
|
|
|
148
|
-
|
|
76
|
+
### `RetryOptions`
|
|
149
77
|
|
|
150
|
-
|
|
78
|
+
| Opção | Tipo | Default |
|
|
79
|
+
| --- | --- | --- |
|
|
80
|
+
| `retries` | `number` | `3` |
|
|
81
|
+
| `delayMs` | `number \| (attempt) => number` | `1_000` |
|
|
82
|
+
| `shouldRetry` | `(error) => boolean` | tudo, exceto `RunCanceled`/`TaskTimeout`/`AbortError` (nunca re-tentados) |
|
|
151
83
|
|
|
152
|
-
|
|
84
|
+
### `CaptureHint`
|
|
153
85
|
|
|
154
|
-
|
|
|
155
|
-
| --- | --- | --- |
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `logFlushIntervalMs` | `number` | `2000` | intervalo do envio incremental de logs |
|
|
160
|
-
| `cancelCheckIntervalMs` | `number` | `5000` | heartbeat de cancelamento por run (clamp 1000..15000) — também é o sinal de vida da run (servidor marca `AgentTerminated` após ~60s sem reporte) |
|
|
161
|
-
| `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 |
|
|
86
|
+
| Campo | Tipo | Descrição |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| `type` | `'fatal' \| 'error' \| 'warning' \| 'success'` | default `'error'` |
|
|
89
|
+
| `tags`, `extra`, `fingerprint` | | metadados do evento (`fingerprint` sobrescreve o agrupamento) |
|
|
90
|
+
| `attachment` | `AttachInput \| { image, caption?, contentType? }` | print anexado à linha de log da execução atual |
|
|
162
91
|
|
|
163
|
-
### TaskHandler
|
|
92
|
+
### `TaskHandler`
|
|
164
93
|
|
|
165
94
|
```ts
|
|
166
|
-
|
|
167
|
-
type TaskHandler<P = Record<string, unknown>> =
|
|
168
|
-
(params: P, ctx: RunContext) => void | Promise<void>;
|
|
169
|
-
|
|
170
|
-
interface RunContext {
|
|
171
|
-
runId: string;
|
|
172
|
-
task: string;
|
|
173
|
-
log: RunLogger; // ctx.log.debug/info/warn/error/success(message)
|
|
174
|
-
items: RunItems; // ctx.items.add/succeeded/failed/occurrence — a entrega do bot
|
|
175
|
-
progress: RunProgressReporter; // ctx.progress.total/advance/set — barra + tempo restante (opcional)
|
|
176
|
-
tx: Transaction; // transaction desta run — correlacionada e finalizada pelo SDK
|
|
177
|
-
signal: AbortSignal;
|
|
178
|
-
throwIfCanceled(): void; // lança RunCanceled se o signal já disparou
|
|
179
|
-
sleep(ms: number): Promise<void>; // espera cancelável — rejeita com RunCanceled no abort
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
interface RunItems {
|
|
183
|
-
add(item: { status: RunItemStatus; id?: string; message?: string; payload?: Record<string, unknown> }): void;
|
|
184
|
-
succeeded(payload?: Record<string, unknown>, options?: { id?: string }): void;
|
|
185
|
-
failed(message: string, payload?: Record<string, unknown>, options?: { id?: string }): void;
|
|
186
|
-
occurrence(message: string, payload?: Record<string, unknown>, options?: { id?: string }): void;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
interface RunProgressReporter {
|
|
190
|
-
total(total: number): void; // declara/ajusta o total de itens (inteiro >= 1; idempotente)
|
|
191
|
-
advance(count?: number): void; // incrementa os processados (padrão 1; clampa em total)
|
|
192
|
-
set(current: number, total?: number): void; // valor absoluto
|
|
193
|
-
readonly current: number;
|
|
194
|
-
readonly snapshot: RunProgress | null; // { current, total } ou null sem total declarado
|
|
195
|
-
}
|
|
95
|
+
type TaskHandler<P = Record<string, unknown>> = (params: P, ctx: RunContext) => void | Promise<void>;
|
|
196
96
|
```
|
|
197
97
|
|
|
198
|
-
- **`runId` / `task`** — identificam a execução; úteis para correlacionar com os seus próprios logs.
|
|
199
|
-
- **`log.<level>(message)`** — níveis `debug`, `info`, `warn`, `error` e `success`; enfileira uma linha de log enviada ao app em PATCHes incrementais, e **também imprime no terminal local**. Conceito: [Run](./concepts.md#run).
|
|
200
|
-
- **`items.<status>(…)`** — registra um item processado/gerado pela execução (síncrono, bufferizado, lotes de até 500). `id` opcional = idempotência dentro da run. Guia: [Itens de execução](./tasks.md#itens-de-execução-ctxitems).
|
|
201
|
-
- **`progress.total(n)` / `progress.advance()` / `progress.set(c, t?)`** — progresso declarado da run (itens processados / total), opcional e independente de `items`; o app mostra barra, percentual e tempo restante. Síncrono; o snapshot pega carona nos PATCHes de log e sai no máximo 1×/s por conta própria. Guia: [Progresso da execução](./tasks.md#progresso-da-execução-ctxprogress).
|
|
202
|
-
- **`tx`** — a [transaction de performance da run](./concepts.md#transaction-de-run-ctxtx). Use `ctx.tx.startChild(op, description)` para medir as etapas; **não** chame `finish()` nela. `startTransaction()` dentro do handler devolve essa mesma transaction.
|
|
203
|
-
- **`signal`** — `AbortSignal` que dispara em cancelamento no app, timeout ou shutdown.
|
|
204
|
-
- **`throwIfCanceled()`** — o ponto de parada de uma linha: lança `RunCanceled` se o `signal` já disparou.
|
|
205
|
-
- **`sleep(ms)`** — espera que respeita o `signal` (rejeita com `RunCanceled` em vez de dormir até o fim). Use no lugar do seu `sleep` em esperas longas.
|
|
206
|
-
|
|
207
|
-
Para anexar um print a uma linha de log da run, use o campo `attachment` do [`CaptureHint`](#capturehint) em `captureException`/`captureMessage` dentro do handler. Guia: [Anexos](./tasks.md#anexos-screenshots).
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
98
|
## Tempos e intervalos
|
|
212
99
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
|
218
|
-
|
|
|
219
|
-
|
|
|
220
|
-
|
|
|
221
|
-
|
|
|
222
|
-
|
|
|
223
|
-
|
|
|
224
|
-
| `close(
|
|
225
|
-
| ladder de retry | `1s, 4s, 16s` | — (fixo) | 3 tentativas por envelope; depois ele é descartado. 4xx (exceto 429) não tem retry |
|
|
226
|
-
| heartbeat | `30000` ms | `init({ heartbeat: { intervalMs } })` | bate **imediatamente** no `init()` e depois a cada intervalo; o valor vai no payload e define quanto silêncio o servidor tolera antes de marcar offline |
|
|
227
|
-
|
|
228
|
-
### Tasks — polling e runs
|
|
229
|
-
|
|
230
|
-
| Tempo | Default | Onde configura | O que acontece |
|
|
231
|
-
| --- | --- | --- | --- |
|
|
232
|
-
| poll sem trabalho | `5000` ms (mín. `1000`) | `tasks: { pollIntervalMs }` | é só o **teto da espera quando não há nada**: com trabalho na fila o próximo poll é imediato ([drain mode](./concepts.md#drain-mode)) |
|
|
233
|
-
| flush de logs da run | `2000` ms | `tasks: { logFlushIntervalMs }` | ou na hora, ao acumular 20 linhas |
|
|
234
|
-
| flush de itens da run | `2000` ms | — (acompanha `logFlushIntervalMs`) | ou na hora, ao acumular 100 itens; POSTs de até 500 |
|
|
235
|
-
| espera pelos itens no fim da run | `15000` ms | — (fixo) | antes do PATCH terminal, drena o buffer de itens — os totalizadores ficam completos quando a run termina |
|
|
236
|
-
| validade da URL de upload de anexo | `900` s | — (servidor) | tempo para o PUT no S3 depois que a API presigna |
|
|
237
|
-
| espera pelos anexos no fim da run | `15000` ms | — (fixo) | antes do PATCH terminal, aguarda os uploads pendentes; no estouro a run termina e o app mostra "imagem não enviada" |
|
|
238
|
-
| uploads de anexo simultâneos | `3` | — (fixo) | por execução; os demais ficam na fila |
|
|
239
|
-
| retry do upload de anexo | `1s, 4s` | — (fixo) | ladder curta: o binário é grande e a URL presignada tem prazo |
|
|
240
|
-
| heartbeat de cancelamento | `5000` ms (clamp `1000`..`15000`) | `tasks: { cancelCheckIntervalMs }` | detecta cancelamento feito no app **e** é o sinal de vida da run (veja a nota 2) |
|
|
241
|
-
| timeout por run | sem timeout | `tasks: { taskTimeoutMs }` | no estouro dispara `ctx.signal`, libera o slot de concorrência e reporta `TaskTimeout` — **não mata** o handler |
|
|
242
|
-
| `stop(timeoutMs)` | `30000` ms | argumento | espera as runs ativas; no estouro elas são reportadas como `AgentTerminated` |
|
|
243
|
-
| retry do poll | `1s, 4s, 16s` | — (fixo) | erros de rede/5xx |
|
|
244
|
-
| retry com chave rejeitada (401) | `30000` ms | — (fixo) | segue tentando devagar, com log throttled |
|
|
245
|
-
| PATCH do caminho de saída | `~2500` ms | — (fixo) | melhor esforço, sem retry, ao morrer com run ativa |
|
|
246
|
-
| sweep do servidor | `~60000` ms | — (servidor) | run `running` sem reporte por esse tempo vira `failed`/`AgentTerminated`, sem re-entrega |
|
|
247
|
-
|
|
248
|
-
**Nota 1 — `close(timeoutMs)` gasta o tempo duas vezes.** Ele espera as runs de tasks (`timeoutMs`) e **depois** faz o flush final (`timeoutMs`), em sequência. Pior caso ≈ `2 × timeoutMs`; `close()` sem argumento pode levar até ~35s (30s aguardando runs + 5s de flush). Se o seu SIGTERM tem prazo curto (Kubernetes, systemd), dimensione por aí.
|
|
249
|
-
|
|
250
|
-
**Nota 2 — o teto de 15s do `cancelCheckIntervalMs` existe por causa do sweep de 60s.** Esse heartbeat é o que mantém o `last_report_at` da run atualizado; o clamp garante que o intervalo nunca chegue perto do prazo em que o servidor declara a run morta.
|
|
100
|
+
| O quê | Valor |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| Heartbeat | 30 s (±10 % de jitter), sempre ligado, timer ref'd (segura o processo) |
|
|
103
|
+
| Flush da telemetria | 2 s ou 10 itens; envelopes de até 50 itens; fila de 200 |
|
|
104
|
+
| Retry HTTP (ingest, PATCH, itens, anexos) | 1 s / 4 s / 16 s com jitter; 4xx ≠ 429 é definitivo |
|
|
105
|
+
| Poll sem trabalho | `pollIntervalMs` (2 s); com trabalho, imediato (drain) |
|
|
106
|
+
| Poll/criação de run com erro | 1 s / 4 s / 16 s; 401 → 30 s com erro logado na 1ª e a cada 10 falhas |
|
|
107
|
+
| Logs da execução | flush a cada `logFlushIntervalMs` (2 s) ou 20 linhas; chunks de 100; 1000 por execução no servidor |
|
|
108
|
+
| Cancel-watch / sinal de vida | `cancelCheckIntervalMs` (5 s); o servidor marca `AgentTerminated` após ~120 s sem PATCH |
|
|
109
|
+
| Retry do handler | `retries` 1, `retryDelayMs` 5 s |
|
|
110
|
+
| Drenos antes do terminal | anexos 15 s, itens 15 s |
|
|
111
|
+
| `close()` | 30 s para as execuções em voo; exit path reporta em ≤ 2,5 s |
|