hobots 1.8.1 → 1.9.1
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 +35 -0
- package/dist/index.cjs +307 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -4
- package/dist/index.d.ts +33 -4
- package/dist/index.js +307 -7
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +41 -1
- package/dist/protocol.d.ts +41 -1
- package/docs/api-reference.md +21 -1
- package/docs/concepts.md +17 -0
- package/docs/getting-started.md +1 -0
- package/docs/protocol.md +60 -3
- package/docs/tasks.md +75 -0
- package/package.json +1 -1
package/dist/protocol.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Tipos do protocolo do Hobots (SDK → servidor).\n *\n * Este arquivo é a fonte de verdade do wire format e só pode reexportar tipos:\n * o app valida os payloads com zod e trava o drift via `satisfies z.ZodType<...>`\n * contra estes tipos (importados de `hobots/protocol`).\n */\n\n// Wire format de tasks (agente ↔ servidor) — reexportado aqui para o app\n// consumir tudo de `hobots/protocol`.\nexport type {\n RunStatus,\n RunUpdateStatus,\n RunLogLevel,\n RunLogEntry,\n AgentInfo,\n PollRequest,\n RunDescriptor,\n PollResponse,\n RunError,\n RunUpdateRequest,\n RunUpdateResponse,\n} from './tasks/protocol';\n\nexport type Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'success';\n\n/** Frames em ordem innermost-first (mesma ordem impressa pelo V8). */\nexport interface StackFrame {\n function?: string;\n filename?: string;\n lineno?: number;\n colno?: number;\n in_app: boolean;\n}\n\nexport interface Breadcrumb {\n /** ISO 8601 */\n timestamp: string;\n type?: string;\n category?: string;\n message?: string;\n level?: Severity;\n data?: Record<string, unknown>;\n}\n\nexport interface ExceptionData {\n type: string;\n value: string;\n stacktrace?: { frames: StackFrame[] };\n}\n\nexport interface EventUser {\n id?: string;\n username?: string;\n email?: string;\n}\n\nexport interface EventPayload {\n /** uuid v4 sem hífens (32 hex), gerado pelo SDK */\n event_id: string;\n /** ISO 8601 */\n timestamp: string;\n level: Severity;\n message?: string;\n exception?: ExceptionData;\n /** Override de agrupamento; quando ausente o servidor calcula pelo stack trace */\n fingerprint?: string[];\n breadcrumbs?: Breadcrumb[];\n tags?: Record<string, string>;\n user?: EventUser;\n contexts?: Record<string, Record<string, unknown>>;\n extra?: Record<string, unknown>;\n release?: string;\n environment?: string;\n}\n\n/** Status de uma step (etapa). `warning` = terminou, mas merece atenção (não é falha). */\nexport type StepStatus = 'ok' | 'warning' | 'error' | 'cancelled';\n\nexport interface StepData {\n /** 16 hex */\n step_id: string;\n parent_step_id?: string;\n op?: string;\n description?: string;\n status?: StepStatus;\n /** ISO 8601 */\n start_timestamp: string;\n /** ISO 8601 (fim da step) */\n timestamp: string;\n data?: Record<string, unknown>;\n}\n\nexport interface TransactionPayload {\n event_id: string;\n name: string;\n op?: string;\n status: string;\n start_timestamp: string;\n timestamp: string;\n steps: StepData[];\n tags?: Record<string, string>;\n release?: string;\n environment?: string;\n /** Correlaciona esta transaction a uma execução de task (ctx.tx dentro de um task handler) */\n run_id?: string;\n}\n\nexport interface HeartbeatPayload {\n instance_id: string;\n name?: string;\n interval_ms: number;\n /** ISO 8601 */\n timestamp: string;\n metadata?: Record<string, unknown>;\n release?: string;\n}\n\nexport type EnvelopeItem =\n | { type: 'event'; payload: EventPayload }\n | { type: 'transaction'; payload: TransactionPayload }\n | { type: 'heartbeat'; payload: HeartbeatPayload };\n\nexport interface Envelope {\n sdk: { name: string; version: string };\n /** ISO 8601 */\n sent_at: string;\n items: EnvelopeItem[];\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Tipos do protocolo do Hobots (SDK → servidor).\n *\n * Este arquivo é a fonte de verdade do wire format e só pode reexportar tipos:\n * o app valida os payloads com zod e trava o drift via `satisfies z.ZodType<...>`\n * contra estes tipos (importados de `hobots/protocol`).\n */\n\n// Wire format de tasks (agente ↔ servidor) — reexportado aqui para o app\n// consumir tudo de `hobots/protocol`.\nexport type {\n RunStatus,\n RunUpdateStatus,\n RunLogLevel,\n RunLogEntry,\n AgentInfo,\n PollRequest,\n RunDescriptor,\n PollResponse,\n RunError,\n RunUpdateRequest,\n RunUpdateResponse,\n RunAttachmentContentType,\n RunAttachmentCreateRequest,\n RunAttachmentCreateResponse,\n RunAttachmentConfirmRequest,\n RunAttachmentConfirmResponse,\n} from './tasks/protocol';\n\nexport type Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'success';\n\n/** Frames em ordem innermost-first (mesma ordem impressa pelo V8). */\nexport interface StackFrame {\n function?: string;\n filename?: string;\n lineno?: number;\n colno?: number;\n in_app: boolean;\n}\n\nexport interface Breadcrumb {\n /** ISO 8601 */\n timestamp: string;\n type?: string;\n category?: string;\n message?: string;\n level?: Severity;\n data?: Record<string, unknown>;\n}\n\nexport interface ExceptionData {\n type: string;\n value: string;\n stacktrace?: { frames: StackFrame[] };\n}\n\nexport interface EventUser {\n id?: string;\n username?: string;\n email?: string;\n}\n\nexport interface EventPayload {\n /** uuid v4 sem hífens (32 hex), gerado pelo SDK */\n event_id: string;\n /** ISO 8601 */\n timestamp: string;\n level: Severity;\n message?: string;\n exception?: ExceptionData;\n /** Override de agrupamento; quando ausente o servidor calcula pelo stack trace */\n fingerprint?: string[];\n breadcrumbs?: Breadcrumb[];\n tags?: Record<string, string>;\n user?: EventUser;\n contexts?: Record<string, Record<string, unknown>>;\n extra?: Record<string, unknown>;\n release?: string;\n environment?: string;\n}\n\n/** Status de uma step (etapa). `warning` = terminou, mas merece atenção (não é falha). */\nexport type StepStatus = 'ok' | 'warning' | 'error' | 'cancelled';\n\nexport interface StepData {\n /** 16 hex */\n step_id: string;\n parent_step_id?: string;\n op?: string;\n description?: string;\n status?: StepStatus;\n /** ISO 8601 */\n start_timestamp: string;\n /** ISO 8601 (fim da step) */\n timestamp: string;\n data?: Record<string, unknown>;\n}\n\nexport interface TransactionPayload {\n event_id: string;\n name: string;\n op?: string;\n status: string;\n start_timestamp: string;\n timestamp: string;\n steps: StepData[];\n tags?: Record<string, string>;\n release?: string;\n environment?: string;\n /** Correlaciona esta transaction a uma execução de task (ctx.tx dentro de um task handler) */\n run_id?: string;\n}\n\nexport interface HeartbeatPayload {\n instance_id: string;\n name?: string;\n interval_ms: number;\n /** ISO 8601 */\n timestamp: string;\n metadata?: Record<string, unknown>;\n release?: string;\n}\n\nexport type EnvelopeItem =\n | { type: 'event'; payload: EventPayload }\n | { type: 'transaction'; payload: TransactionPayload }\n | { type: 'heartbeat'; payload: HeartbeatPayload };\n\nexport interface Envelope {\n sdk: { name: string; version: string };\n /** ISO 8601 */\n sent_at: string;\n items: EnvelopeItem[];\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
package/dist/protocol.d.cts
CHANGED
|
@@ -15,6 +15,12 @@ interface RunLogEntry {
|
|
|
15
15
|
ts: string;
|
|
16
16
|
level: RunLogLevel;
|
|
17
17
|
message: string;
|
|
18
|
+
/**
|
|
19
|
+
* Ids dos anexos (imagens) desta linha — gerados pelo agente (32 hex) e criados via
|
|
20
|
+
* `POST /v1/tasks/runs/{id}/attachments`. O upload roda em segundo plano: a linha pode
|
|
21
|
+
* chegar ao servidor antes do binário estar no S3.
|
|
22
|
+
*/
|
|
23
|
+
attachments?: string[];
|
|
18
24
|
}
|
|
19
25
|
interface AgentInfo {
|
|
20
26
|
/** Identidade canônica do agente — correlaciona com o heartbeat de telemetria */
|
|
@@ -68,6 +74,40 @@ interface RunUpdateRequest {
|
|
|
68
74
|
interface RunUpdateResponse {
|
|
69
75
|
ok: true;
|
|
70
76
|
}
|
|
77
|
+
/** Formatos aceitos para anexo. O S3 valida o content-type contra a assinatura. */
|
|
78
|
+
type RunAttachmentContentType = 'image/png' | 'image/jpeg' | 'image/webp';
|
|
79
|
+
/** POST /v1/tasks/runs/{id}/attachments */
|
|
80
|
+
interface RunAttachmentCreateRequest {
|
|
81
|
+
/** Id do anexo gerado pelo agente (32 hex) — já referenciado na linha de log */
|
|
82
|
+
id: string;
|
|
83
|
+
content_type: RunAttachmentContentType;
|
|
84
|
+
/** Tamanho exato do binário; o PUT presignado é assinado com este Content-Length */
|
|
85
|
+
size_bytes: number;
|
|
86
|
+
caption?: string;
|
|
87
|
+
/** ISO 8601 — ts da linha de log que referencia o anexo */
|
|
88
|
+
log_ts?: string;
|
|
89
|
+
width?: number;
|
|
90
|
+
height?: number;
|
|
91
|
+
}
|
|
92
|
+
interface RunAttachmentCreateResponse {
|
|
93
|
+
id: string;
|
|
94
|
+
/** URL presignada de PUT direto no S3 (não envie Authorization nesta requisição) */
|
|
95
|
+
upload_url: string;
|
|
96
|
+
/** Headers obrigatórios no PUT — fazem parte da assinatura */
|
|
97
|
+
headers: Record<string, string>;
|
|
98
|
+
/** Validade da URL, em segundos */
|
|
99
|
+
expires_in: number;
|
|
100
|
+
}
|
|
101
|
+
/** PATCH /v1/tasks/runs/{id}/attachments/{attachmentId} */
|
|
102
|
+
interface RunAttachmentConfirmRequest {
|
|
103
|
+
status: 'uploaded' | 'failed';
|
|
104
|
+
size_bytes?: number;
|
|
105
|
+
width?: number;
|
|
106
|
+
height?: number;
|
|
107
|
+
}
|
|
108
|
+
interface RunAttachmentConfirmResponse {
|
|
109
|
+
ok: true;
|
|
110
|
+
}
|
|
71
111
|
|
|
72
112
|
/**
|
|
73
113
|
* Tipos do protocolo do Hobots (SDK → servidor).
|
|
@@ -183,4 +223,4 @@ interface Envelope {
|
|
|
183
223
|
items: EnvelopeItem[];
|
|
184
224
|
}
|
|
185
225
|
|
|
186
|
-
export type { AgentInfo, Breadcrumb, Envelope, EnvelopeItem, EventPayload, EventUser, ExceptionData, HeartbeatPayload, PollRequest, PollResponse, RunDescriptor, RunError, RunLogEntry, RunLogLevel, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, Severity, StackFrame, StepData, StepStatus, TransactionPayload };
|
|
226
|
+
export type { AgentInfo, Breadcrumb, Envelope, EnvelopeItem, EventPayload, EventUser, ExceptionData, HeartbeatPayload, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentContentType, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunLogEntry, RunLogLevel, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, Severity, StackFrame, StepData, StepStatus, TransactionPayload };
|
package/dist/protocol.d.ts
CHANGED
|
@@ -15,6 +15,12 @@ interface RunLogEntry {
|
|
|
15
15
|
ts: string;
|
|
16
16
|
level: RunLogLevel;
|
|
17
17
|
message: string;
|
|
18
|
+
/**
|
|
19
|
+
* Ids dos anexos (imagens) desta linha — gerados pelo agente (32 hex) e criados via
|
|
20
|
+
* `POST /v1/tasks/runs/{id}/attachments`. O upload roda em segundo plano: a linha pode
|
|
21
|
+
* chegar ao servidor antes do binário estar no S3.
|
|
22
|
+
*/
|
|
23
|
+
attachments?: string[];
|
|
18
24
|
}
|
|
19
25
|
interface AgentInfo {
|
|
20
26
|
/** Identidade canônica do agente — correlaciona com o heartbeat de telemetria */
|
|
@@ -68,6 +74,40 @@ interface RunUpdateRequest {
|
|
|
68
74
|
interface RunUpdateResponse {
|
|
69
75
|
ok: true;
|
|
70
76
|
}
|
|
77
|
+
/** Formatos aceitos para anexo. O S3 valida o content-type contra a assinatura. */
|
|
78
|
+
type RunAttachmentContentType = 'image/png' | 'image/jpeg' | 'image/webp';
|
|
79
|
+
/** POST /v1/tasks/runs/{id}/attachments */
|
|
80
|
+
interface RunAttachmentCreateRequest {
|
|
81
|
+
/** Id do anexo gerado pelo agente (32 hex) — já referenciado na linha de log */
|
|
82
|
+
id: string;
|
|
83
|
+
content_type: RunAttachmentContentType;
|
|
84
|
+
/** Tamanho exato do binário; o PUT presignado é assinado com este Content-Length */
|
|
85
|
+
size_bytes: number;
|
|
86
|
+
caption?: string;
|
|
87
|
+
/** ISO 8601 — ts da linha de log que referencia o anexo */
|
|
88
|
+
log_ts?: string;
|
|
89
|
+
width?: number;
|
|
90
|
+
height?: number;
|
|
91
|
+
}
|
|
92
|
+
interface RunAttachmentCreateResponse {
|
|
93
|
+
id: string;
|
|
94
|
+
/** URL presignada de PUT direto no S3 (não envie Authorization nesta requisição) */
|
|
95
|
+
upload_url: string;
|
|
96
|
+
/** Headers obrigatórios no PUT — fazem parte da assinatura */
|
|
97
|
+
headers: Record<string, string>;
|
|
98
|
+
/** Validade da URL, em segundos */
|
|
99
|
+
expires_in: number;
|
|
100
|
+
}
|
|
101
|
+
/** PATCH /v1/tasks/runs/{id}/attachments/{attachmentId} */
|
|
102
|
+
interface RunAttachmentConfirmRequest {
|
|
103
|
+
status: 'uploaded' | 'failed';
|
|
104
|
+
size_bytes?: number;
|
|
105
|
+
width?: number;
|
|
106
|
+
height?: number;
|
|
107
|
+
}
|
|
108
|
+
interface RunAttachmentConfirmResponse {
|
|
109
|
+
ok: true;
|
|
110
|
+
}
|
|
71
111
|
|
|
72
112
|
/**
|
|
73
113
|
* Tipos do protocolo do Hobots (SDK → servidor).
|
|
@@ -183,4 +223,4 @@ interface Envelope {
|
|
|
183
223
|
items: EnvelopeItem[];
|
|
184
224
|
}
|
|
185
225
|
|
|
186
|
-
export type { AgentInfo, Breadcrumb, Envelope, EnvelopeItem, EventPayload, EventUser, ExceptionData, HeartbeatPayload, PollRequest, PollResponse, RunDescriptor, RunError, RunLogEntry, RunLogLevel, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, Severity, StackFrame, StepData, StepStatus, TransactionPayload };
|
|
226
|
+
export type { AgentInfo, Breadcrumb, Envelope, EnvelopeItem, EventPayload, EventUser, ExceptionData, HeartbeatPayload, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentContentType, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunLogEntry, RunLogLevel, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, Severity, StackFrame, StepData, StepStatus, TransactionPayload };
|
package/docs/api-reference.md
CHANGED
|
@@ -128,6 +128,19 @@ O teto da fila do transport (200 itens; ao encher, o **mais antigo** é descarta
|
|
|
128
128
|
| `tags` | `Record<string,string>` | tags só deste evento |
|
|
129
129
|
| `extra` | `Record<string,unknown>` | dados extras |
|
|
130
130
|
| `fingerprint` | `string[]` | agrupamento manual |
|
|
131
|
+
| `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) |
|
|
132
|
+
|
|
133
|
+
### CaptureAttachment
|
|
134
|
+
|
|
135
|
+
`AttachInput = Uint8Array | ArrayBuffer | string` (bytes ou um caminho de arquivo, só em Node). Para legenda ou content-type, passe o objeto:
|
|
136
|
+
|
|
137
|
+
| Campo | Tipo | Default | O que faz |
|
|
138
|
+
| --- | --- | --- | --- |
|
|
139
|
+
| `image` | `AttachInput` | — | a imagem em si |
|
|
140
|
+
| `caption` | `string` | — | legenda exibida sob a imagem ampliada; truncada em 200 caracteres e passada pelo scrub de segredos |
|
|
141
|
+
| `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 |
|
|
142
|
+
|
|
143
|
+
O texto da linha de log é a mensagem capturada; o nível vem do `level` da captura (`'fatal'` vira `'error'`). O upload roda em segundo plano e **nunca bloqueia nem faz a captura lançar**. Limites (todos aplicados também no servidor): PNG/JPEG/WEBP, **5 MB** por imagem, **20** anexos por execução, **4** por linha de log.
|
|
131
144
|
|
|
132
145
|
`Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'success'`. O que cada nível significa (e por que `'success'` existe) em [Level](./concepts.md#level-severity).
|
|
133
146
|
|
|
@@ -169,6 +182,8 @@ interface RunContext {
|
|
|
169
182
|
- **`throwIfCanceled()`** — o ponto de parada de uma linha: lança `RunCanceled` se o `signal` já disparou.
|
|
170
183
|
- **`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.
|
|
171
184
|
|
|
185
|
+
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).
|
|
186
|
+
|
|
172
187
|
---
|
|
173
188
|
|
|
174
189
|
## Tempos e intervalos
|
|
@@ -180,7 +195,8 @@ Todos os prazos do SDK num lugar só. A coluna **Onde configura** diz se o valor
|
|
|
180
195
|
| Tempo | Default | Onde configura | O que acontece |
|
|
181
196
|
| --- | --- | --- | --- |
|
|
182
197
|
| flush automático | `2000` ms | `init({ flushIntervalMs })` | de quanto em quanto o transport esvazia a fila; menor = mais requests, telemetria mais fresca |
|
|
183
|
-
|
|
|
198
|
+
| gatilho de flush por lote | `10` itens | `init({ maxBatchSize })` | ao atingir, envia **na hora** sem esperar o intervalo; é só o gatilho, não o tamanho do envelope |
|
|
199
|
+
| teto de itens por envelope | `50` itens | — (servidor) | limite aceito pelo `/v1/ingest`; a fila é fatiada em quantos envelopes forem necessários, um POST por lote |
|
|
184
200
|
| teto da fila | `200` itens | — (fixo) | fila cheia descarta o item **mais antigo**, com aviso no console |
|
|
185
201
|
| `flush(timeoutMs)` | `5000` ms | argumento | quanto espera os envios em voo antes de devolver `false` |
|
|
186
202
|
| `close(timeoutMs)` | `5000` ms | argumento | idem, no flush final (veja a nota 1 abaixo) |
|
|
@@ -193,6 +209,10 @@ Todos os prazos do SDK num lugar só. A coluna **Onde configura** diz se o valor
|
|
|
193
209
|
| --- | --- | --- | --- |
|
|
194
210
|
| 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)) |
|
|
195
211
|
| flush de logs da run | `2000` ms | `tasks: { logFlushIntervalMs }` | ou na hora, ao acumular 20 linhas |
|
|
212
|
+
| validade da URL de upload de anexo | `900` s | — (servidor) | tempo para o PUT no S3 depois que a API presigna |
|
|
213
|
+
| 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" |
|
|
214
|
+
| uploads de anexo simultâneos | `3` | — (fixo) | por execução; os demais ficam na fila |
|
|
215
|
+
| retry do upload de anexo | `1s, 4s` | — (fixo) | ladder curta: o binário é grande e a URL presignada tem prazo |
|
|
196
216
|
| 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) |
|
|
197
217
|
| 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 |
|
|
198
218
|
| `stop(timeoutMs)` | `30000` ms | argumento | espera as runs ativas; no estouro elas são reportadas como `AgentTerminated` |
|
package/docs/concepts.md
CHANGED
|
@@ -365,6 +365,23 @@ O agente só transiciona para `running`, `succeeded` e `failed`. `pending` e `cl
|
|
|
365
365
|
|
|
366
366
|
Cada run carrega os **`params`** (o JSON validado do formulário) e chega ao seu código como o primeiro argumento do handler, junto de um [`RunContext`](./api-reference.md#taskhandler--runcontext) com logs, sinal de cancelamento e a [transaction](#transaction-de-run-ctxtx).
|
|
367
367
|
|
|
368
|
+
## Anexo
|
|
369
|
+
|
|
370
|
+
Um **anexo** é uma imagem — na prática, um print da tela — vinculada a uma linha de log de uma [run](#run). Serve para responder "como estava a tela quando isso aconteceu?", pergunta que texto de log sozinho não responde. No app, aparece como miniatura logo abaixo da linha de log correspondente, na aba "logs do agente" da execução.
|
|
371
|
+
|
|
372
|
+
Quem captura é o seu código; o SDK só recebe a imagem pronta, via o campo [`attachment`](./tasks.md#anexos-screenshots) de `captureException`/`captureMessage`.
|
|
373
|
+
|
|
374
|
+
**O binário não passa pela API do Hobots.** O agente pede uma URL presignada, sobe direto no S3 e depois confirma. Isso mantém a API livre de tráfego pesado e permite imagens bem maiores do que o limite de corpo de um PATCH. O ciclo de vida de um anexo é:
|
|
375
|
+
|
|
376
|
+
```
|
|
377
|
+
pending → uploaded
|
|
378
|
+
↘ failed
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
O `pending` existe porque a linha de log é registrada **antes** do upload terminar — é isso que impede o print de atrasar o log ou bagunçar a ordem. Enquanto o anexo está `pending`, o app mostra a miniatura como "enviando imagem…".
|
|
382
|
+
|
|
383
|
+
Anexos herdam o mesmo gate de visibilidade dos logs: solicitantes (papel `requester`) só os veem quando o projeto está com "mostrar logs para solicitantes" ligado.
|
|
384
|
+
|
|
368
385
|
## Claim
|
|
369
386
|
|
|
370
387
|
O **claim** é o ato de um agente reivindicar uma run para si. Importa porque resolve a pergunta óbvia de qualquer sistema de fila: **o que impede dois agentes de pegarem a mesma run?**
|
package/docs/getting-started.md
CHANGED
|
@@ -88,6 +88,7 @@ Hobots.init({
|
|
|
88
88
|
|
|
89
89
|
Hobots.register('processar-nfe', async (params, ctx) => {
|
|
90
90
|
ctx.log.info(`processando lote ${params.lote}…`);
|
|
91
|
+
// Hobots.captureMessage('portal após o login', 'info', { attachment: await page.screenshot() }); // print junto do log
|
|
91
92
|
ctx.log.info('12 notas processadas'); // o retorno do handler é ignorado — reporte por log
|
|
92
93
|
});
|
|
93
94
|
Hobots.start(); // mantém o processo vivo até stop()/close()
|
package/docs/protocol.md
CHANGED
|
@@ -16,6 +16,7 @@ hb_<64 hex>
|
|
|
16
16
|
| --- | --- | --- |
|
|
17
17
|
| Telemetria (ingestão) | `POST <baseUrl>/v1/ingest` | `Authorization: Bearer <clientSecret>` |
|
|
18
18
|
| Tasks (execução) | `<baseUrl>/v1/tasks/*` | `Authorization: Bearer <clientSecret>` |
|
|
19
|
+
| Anexos (binário) | URL presignada do S3 (PUT) | assinatura na própria URL — **sem** header `Authorization` |
|
|
19
20
|
|
|
20
21
|
A resolução da credencial nos endpoints é interna ao `init()` (em `src/config.ts`, com a constante exportada `DEFAULT_BASE_URL = 'https://api2.hobots.app'`); o resultado fica acessível em `getClient()?.config` → `{ key, baseUrl, ingestEndpoint, tasksBaseUrl }`. Em desenvolvimento a env `HOBOTS_API_URL` sobrescreve a URL base — apenas `http(s)`, e `http` para host fora de loopback gera aviso porque o secret trafega sem TLS. A credencial é **secreta** (formato `hb_` + 64 hex — chaves de outro formato são rejeitadas no `init()`) e viaja **só no header** `Authorization: Bearer` — nunca na URL da requisição.
|
|
21
22
|
|
|
@@ -39,6 +40,8 @@ Tipos de item: `event`, `transaction`, `heartbeat` (definidos em `hobots/protoco
|
|
|
39
40
|
|
|
40
41
|
Um `transaction` pode carregar `run_id`: é assim que a [transaction de uma run](./concepts.md#transaction-de-run-ctxtx) (`ctx.tx`) é correlacionada à execução, e o que permite o app montar o waterfall de performance dentro da solicitação. Transactions abertas com `startTransaction()` fora de uma run não têm esse campo.
|
|
41
42
|
|
|
43
|
+
`items` aceita de 1 a **50** itens por envelope — acima disso a API responde `422`. O `maxBatchSize` é só o gatilho do flush: quando a fila tem mais que esse teto (burst de captures, ou fila acumulada durante uma janela de rede ruim), o transport a fatia em lotes de 50 e faz um POST por lote.
|
|
44
|
+
|
|
42
45
|
**Flush & retries:** o transport flusha por `flushIntervalMs` ou ao atingir `maxBatchSize`. Ladder de retry de 3 tentativas; respostas 4xx (exceto 429) são descartadas sem retry; a fila tem teto fixo e descarta os itens **mais antigos** ao encher. Todos os valores em [Tempos e intervalos](./api-reference.md#tempos-e-intervalos).
|
|
43
46
|
|
|
44
47
|
## Tasks — polling e reporte
|
|
@@ -76,12 +79,17 @@ Corpo `RunUpdateRequest`:
|
|
|
76
79
|
```jsonc
|
|
77
80
|
{
|
|
78
81
|
"status": "running", // 'running' | 'succeeded' | 'failed'
|
|
79
|
-
"logs": [
|
|
80
|
-
|
|
82
|
+
"logs": [
|
|
83
|
+
{ "ts": "…", "level": "info", "message": "…" }, // delta; o servidor concatena
|
|
84
|
+
{ "ts": "…", "level": "info", "message": "portal após o login",
|
|
85
|
+
"attachments": ["<32 hex>"] } // opcional; ids de anexo (máx. 4)
|
|
86
|
+
],
|
|
87
|
+
"error": { "type": "Error", "message": "…", "stack": "…" } // só com 'failed'
|
|
81
88
|
}
|
|
82
89
|
```
|
|
83
90
|
|
|
84
|
-
- `status: 'running'` pode repetir para enviar **logs incrementais** (throttle por `logFlushIntervalMs`, flush ao acumular 20, chunk de 100, teto de
|
|
91
|
+
- `status: 'running'` pode repetir para enviar **logs incrementais** (throttle por `logFlushIntervalMs`, flush ao acumular 20, chunk de 100, teto de 1000 logs/run, mensagem cap 10k).
|
|
92
|
+
- `attachments` é **opcional** e aditivo: SDKs anteriores à 1.9 simplesmente não enviam o campo, e uma API anterior ao recurso descarta o campo na validação (objeto não-strict) sem rejeitar o PATCH. Os ids são gerados pelo agente **antes** do upload, o que permite enviar a linha de log sem esperar o binário subir.
|
|
85
93
|
- Ordem garantida `running → logs → terminal` (PATCHes serializados numa cadeia de promise).
|
|
86
94
|
- Retry `[1s, 4s, 16s]`; 4xx exceto 429 é terminal (**409** = run cancelada / transição inválida → marca a run como morta e dispara `ctx.signal`).
|
|
87
95
|
- Todo PATCH atualiza `last_report_at` no servidor (relógio do servidor) — é o **sinal de vida da run**. O heartbeat de cancelamento (PATCH `running` vazio a cada `cancelCheckIntervalMs`, máx 15s) garante reporte contínuo; um sweep server-side marca como `failed` com erro `AgentTerminated` ("execução encerrada inesperadamente") toda run `running` sem reporte há **60s**, sem re-entrega. No exit path (sinal/erro fatal com runs ativas), o SDK envia um único PATCH `failed`/`AgentTerminated` com timeout curto (~2s), sem retry — o sweep é o backstop.
|
|
@@ -89,6 +97,55 @@ Corpo `RunUpdateRequest`:
|
|
|
89
97
|
|
|
90
98
|
Ciclo de vida completo da run no servidor (`RunStatus`): `pending → claimed → running → succeeded | failed | canceled`. O agente só transiciona para `running`/`succeeded`/`failed` (`RunUpdateStatus`).
|
|
91
99
|
|
|
100
|
+
### `POST /v1/tasks/runs/{id}/attachments`
|
|
101
|
+
|
|
102
|
+
Registra um anexo de imagem e devolve uma **URL presignada de PUT**. O binário nunca passa pela API: o agente sobe direto no S3.
|
|
103
|
+
|
|
104
|
+
Corpo `RunAttachmentCreateRequest`:
|
|
105
|
+
|
|
106
|
+
```jsonc
|
|
107
|
+
{
|
|
108
|
+
"id": "<32 hex>", // gerado pelo agente, já referenciado na linha de log
|
|
109
|
+
"content_type": "image/png", // 'image/png' | 'image/jpeg' | 'image/webp'
|
|
110
|
+
"size_bytes": 184320, // exato — entra na assinatura da URL
|
|
111
|
+
"caption": "portal após o login", // opcional, máx. 500
|
|
112
|
+
"log_ts": "…", // opcional, ISO 8601 — ts da linha de log
|
|
113
|
+
"width": 1920, "height": 1080 // opcionais
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Resposta `200` `RunAttachmentCreateResponse`:
|
|
118
|
+
|
|
119
|
+
```jsonc
|
|
120
|
+
{
|
|
121
|
+
"id": "<32 hex>",
|
|
122
|
+
"upload_url": "https://…", // PUT direto no S3, SEM Authorization
|
|
123
|
+
"headers": { "content-type": "image/png", "content-length": "184320" },
|
|
124
|
+
"expires_in": 900
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
O PUT no S3 precisa mandar exatamente esses headers — eles fazem parte da assinatura. `content-length` é assinado de propósito: sem isso a URL aceitaria um upload de qualquer tamanho.
|
|
129
|
+
|
|
130
|
+
| Status | Quando |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| `401` | chave do agente inválida ou inativa |
|
|
133
|
+
| `404` | execução inexistente ou de outro projeto |
|
|
134
|
+
| `409` | id de anexo já registrado |
|
|
135
|
+
| `413` | corpo acima de 8 KB (é só metadado) |
|
|
136
|
+
| `422` | payload inválido |
|
|
137
|
+
| `429` | limite de 20 anexos por execução atingido |
|
|
138
|
+
| `503` | anexos não habilitados nesta instalação (sem bucket S3 configurado) |
|
|
139
|
+
| `502` | falha ao presignar — o registro é desfeito |
|
|
140
|
+
|
|
141
|
+
### `PATCH /v1/tasks/runs/{id}/attachments/{attachmentId}`
|
|
142
|
+
|
|
143
|
+
Confirma o upload. Corpo `RunAttachmentConfirmRequest`: `{ "status": "uploaded" | "failed", "size_bytes"?, "width"?, "height"? }`; resposta `{ "ok": true }`, ou `404` se o anexo não existe no projeto da chave.
|
|
144
|
+
|
|
145
|
+
É **conforto de UX**, não correção: enquanto não confirmado, o app mostra a miniatura como "enviando imagem…". O agente engole falhas desta chamada.
|
|
146
|
+
|
|
147
|
+
Limites de anexo (aplicados no SDK **e** no servidor): PNG/JPEG/WEBP, 5 MB por imagem, 20 por execução, 4 por linha de log.
|
|
148
|
+
|
|
92
149
|
## Onde vive o claim atômico
|
|
93
150
|
|
|
94
151
|
O `POST /v1/tasks/poll` no app chama `claimPendingRuns()` em
|
package/docs/tasks.md
CHANGED
|
@@ -68,6 +68,81 @@ Você pode tipar os params: `Hobots.register<{ lote: number }>(...)`.
|
|
|
68
68
|
|
|
69
69
|
> **Dados sensíveis:** o que você escreve em `ctx.log` e as mensagens/stack de erros lançados **são enviados ao servidor** e ficam visíveis na execução no app. Não inclua senhas, tokens, documentos, dados pessoais ou qualquer informação sensível em logs e mensagens de erro.
|
|
70
70
|
|
|
71
|
+
## Anexos (screenshots)
|
|
72
|
+
|
|
73
|
+
Texto de log conta o que o robô fez; um print conta **como estava a tela**. Os métodos de captura — `Hobots.captureException(error, hint)` e `Hobots.captureMessage(message, level, hint)` — 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, e aparece como miniatura logo abaixo dela na aba "logs do agente" da execução — clicável para ampliar.
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
Hobots.register('processar-nfe', async (params, ctx) => {
|
|
77
|
+
await entrar();
|
|
78
|
+
Hobots.captureMessage('portal após o login', 'info', {
|
|
79
|
+
attachment: await page.screenshot(),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
await emitir(params);
|
|
84
|
+
} catch (erro) {
|
|
85
|
+
// numa falha é justamente quando o print importa
|
|
86
|
+
Hobots.captureException(erro, {
|
|
87
|
+
attachment: { image: await page.screenshot(), caption: 'tela no momento do erro' },
|
|
88
|
+
});
|
|
89
|
+
throw erro;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**O SDK não tira o print.** Ele recebe a imagem pronta — de onde você quiser: `page.screenshot()` do Playwright/Puppeteer, uma biblioteca de captura de desktop, um arquivo que o próprio sistema gerou. Isso mantém o SDK sem dependências nativas e funcionando em qualquer ambiente.
|
|
95
|
+
|
|
96
|
+
### O que aceita
|
|
97
|
+
|
|
98
|
+
O `attachment` pode ser a imagem direto, ou um objeto com metadados:
|
|
99
|
+
|
|
100
|
+
| Entrada | Exemplo |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| bytes | `Uint8Array`, `ArrayBuffer` ou `Buffer` — `await page.screenshot()` |
|
|
103
|
+
| caminho de arquivo | `'C:\\prints\\tela.png'` (só em Node) |
|
|
104
|
+
| objeto | `{ image, caption?, contentType? }` |
|
|
105
|
+
|
|
106
|
+
| Campo do objeto | Tipo | Default | O que faz |
|
|
107
|
+
| --- | --- | --- | --- |
|
|
108
|
+
| `image` | bytes ou caminho | — | a imagem em si |
|
|
109
|
+
| `caption` | `string` | — | legenda curta exibida sob a imagem ampliada (máx. 200 caracteres) |
|
|
110
|
+
| `contentType` | `'image/png' \| 'image/jpeg' \| 'image/webp'` | detectado | só é usado quando os *magic bytes* não bastam |
|
|
111
|
+
|
|
112
|
+
O texto da linha de log é a própria mensagem capturada (`message` do `captureMessage`, ou `Nome: mensagem` do erro no `captureException`); o nível vem do `level` da captura (`'fatal'` vira `'error'` no log da run).
|
|
113
|
+
|
|
114
|
+
### Limites
|
|
115
|
+
|
|
116
|
+
| Limite | Valor |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| formatos | PNG, JPEG e WEBP |
|
|
119
|
+
| tamanho por imagem | 5 MB |
|
|
120
|
+
| anexos por execução | 20 |
|
|
121
|
+
| anexos por linha de log | 4 |
|
|
122
|
+
| uploads simultâneos | 3 (o resto fica na fila) |
|
|
123
|
+
|
|
124
|
+
### Como funciona (e por que não trava a run)
|
|
125
|
+
|
|
126
|
+
A linha de log é registrada **na hora**, com o id do anexo; o upload roda em segundo plano. O binário vai direto para o S3 por uma URL presignada — não passa pela API do Hobots. Enquanto o upload não termina, o app mostra a miniatura como "enviando imagem…".
|
|
127
|
+
|
|
128
|
+
O anexo **nunca faz a captura lançar**. Todo insucesso vira um aviso no log da run:
|
|
129
|
+
|
|
130
|
+
| Motivo | Quando |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| `too-large` | acima de 5 MB |
|
|
133
|
+
| `quota` | passou de 20 anexos na execução |
|
|
134
|
+
| `unsupported-format` | não é PNG/JPEG/WEBP |
|
|
135
|
+
| `read-failed` | o caminho de arquivo não pôde ser lido |
|
|
136
|
+
| `unsupported` | a instalação do Hobots não tem anexos habilitados |
|
|
137
|
+
| `upload-failed` | falha de rede depois dos retries |
|
|
138
|
+
| `canceled` | a run foi cancelada ou encerrada durante o upload |
|
|
139
|
+
|
|
140
|
+
O upload é sempre fire-and-forget — a captura retorna na hora. Antes de reportar o desfecho da run, o SDK aguarda os uploads pendentes por até 15 s, para a execução não terminar com miniaturas em "enviando…".
|
|
141
|
+
|
|
142
|
+
> **Fora de uma execução:** `attachment` só funciona dentro de um handler de task — fora dele não existe log de run para pendurar a imagem, e o SDK descarta o anexo com um aviso no console (o evento em si é capturado normalmente).
|
|
143
|
+
|
|
144
|
+
> **Dados sensíveis:** o print é enviado ao servidor **exatamente como está** — o SDK não borra nem recorta nada. Uma tela de portal costuma conter CPF, nome, saldo, número de documento. Capture apenas o necessário e evite prints de telas com dados de terceiros. Solicitantes (papel `requester`) só veem os anexos quando o projeto está com "mostrar logs para solicitantes" ligado, o mesmo gate dos logs.
|
|
145
|
+
|
|
71
146
|
## Performance da run (`ctx.tx`)
|
|
72
147
|
|
|
73
148
|
O SDK já abre uma [transaction](./concepts.md#transaction-de-run-ctxtx) para cada run e entrega em `ctx.tx`. Ela é correlacionada à execução, recebe o status conforme o desfecho (`ok`, `error` ou `cancelled`) e é **finalizada automaticamente** quando o handler termina. Você não cria e não finaliza — só marca as etapas:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hobots",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "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().",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|