hobots 4.0.0 → 4.1.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 +2 -1
- package/dist/{chunk-5WIIACWC.js → chunk-HKCDMUA3.js} +1 -2
- package/dist/chunk-HKCDMUA3.js.map +1 -0
- package/dist/index.cjs +84 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +85 -24
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs +0 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +0 -2
- package/dist/protocol.d.ts +0 -2
- package/dist/protocol.js +1 -1
- package/docs/api-reference.md +2 -1
- package/docs/protocol.md +2 -2
- package/docs/runs.md +30 -2
- package/package.json +1 -1
- package/dist/chunk-5WIIACWC.js.map +0 -1
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ A opção passada em `register()`/`start()` tem precedência sobre a env — út
|
|
|
91
91
|
|
|
92
92
|
**Um processo = um agente = uma chamada.** A segunda chamada a `register()`/`start()` lança (`o agente já foi iniciado`). Um robô que precisa monitorar uma pasta *e* atender solicitações são dois agentes — dois processos, cada um com o seu Client Secret.
|
|
93
93
|
|
|
94
|
-
> **Modo start e o limite de logs:** o servidor guarda no máximo **1000 linhas de log por execução**. Num loop que roda por dias, logue o que importa (ciclos com novidade, avisos, erros) e registre o trabalho em `hobots.items` — itens não têm
|
|
94
|
+
> **Modo start e o limite de logs:** o servidor guarda no máximo **1000 linhas de log por execução**. Num loop que roda por dias, logue o que importa (ciclos com novidade, avisos, erros) e registre o trabalho em `hobots.items` — itens não têm teto por execução (só o armazenamento do plano limita). As steps de uma run que não termina só chegam à Central com `hobots.flush()` — chame-o ao fim de cada rodada que processou itens.
|
|
95
95
|
|
|
96
96
|
---
|
|
97
97
|
|
|
@@ -103,6 +103,7 @@ A opção passada em `register()`/`start()` tem precedência sobre a env — út
|
|
|
103
103
|
| `hobots.items.succeeded(payload?, { id, image }?)` · `.failed(msg, payload?, …)` · `.occurrence(msg, payload?, …)` · `.add({...})` | **A entrega do robô**: cada item processado, com status e atributos (viram colunas no app — declare-as com a opção `items: { columns: [...] }` do `register`/`start`). `image` pendura um print no item. Bufferizado, em lotes. |
|
|
104
104
|
| `hobots.items.each(lista, async (el, item) => …, { id, payload }?)` | **Loop com o contrato garantido**: cada elemento vira step + item; exceção no handler = item `failed` com a mensagem e o loop **segue** até o fim; progresso automático. Só cancelamento/timeout interrompem. |
|
|
105
105
|
| `hobots.progress.total(n)` · `.advance(k = 1)` · `.set(atual, total?)` · `.current` · `.snapshot` | Barra, percentual e tempo restante na execução. Sem `total()` nada é enviado. |
|
|
106
|
+
| `hobots.flush(timeoutMs?)` | **Entrega já** o que a execução fez: as steps raiz finalizadas viram transactions parciais (waterfall na Central sem esperar a run terminar) e itens, logs e progresso pendentes saem sem esperar o throttle. O bot decide quando — num agente `start()`, ao fim de cada rodada que processou itens. Aguarda com teto (15 s) e nunca lança. |
|
|
106
107
|
| `hobots.transaction.startChild(op, descrição)` | Uma **step** no waterfall de performance da execução. A transaction da execução (`run`) já existe e é finalizada pelo SDK. `step.setStatus(status, msg?, { image })` / `setError(err, msg?, { image })` penduram um print na step. |
|
|
107
108
|
| `hobots.image(bytes \| caminho, { caption?, contentType? })` | Embrulha um **print** (PNG/JPEG/WEBP, até 5 MB, 20 por execução) para ir junto de um log, item ou step. Puro: o upload roda em segundo plano quando o evento é registrado; nunca lança nem bloqueia. |
|
|
108
109
|
| `hobots.captureException(erro, hint?)` | Abre uma **issue** (módulo Issues). `hint.attachment` anexa um print à linha de log correspondente. |
|
|
@@ -9,7 +9,6 @@ var protocol_limits_default = {
|
|
|
9
9
|
MAX_LOGS_PER_PATCH: 100,
|
|
10
10
|
MAX_ERROR_MESSAGE_CHARS: 1e4,
|
|
11
11
|
MAX_ERROR_STACK_CHARS: 2e4,
|
|
12
|
-
MAX_ITEMS_PER_RUN: 1e4,
|
|
13
12
|
MAX_ITEMS_PER_REQUEST: 500,
|
|
14
13
|
MAX_ITEM_MESSAGE_CHARS: 2e3,
|
|
15
14
|
MAX_ITEM_PAYLOAD_BYTES: 16384,
|
|
@@ -28,4 +27,4 @@ var WIRE_LIMITS = protocol_limits_default;
|
|
|
28
27
|
export {
|
|
29
28
|
WIRE_LIMITS
|
|
30
29
|
};
|
|
31
|
-
//# sourceMappingURL=chunk-
|
|
30
|
+
//# sourceMappingURL=chunk-HKCDMUA3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../protocol-limits.json","../src/wire-limits.ts"],"sourcesContent":["{\n \"MAX_ITEMS_PER_ENVELOPE\": 50,\n \"HEARTBEAT_DEFAULT_INTERVAL_MS\": 30000,\n \"HEARTBEAT_MIN_INTERVAL_MS\": 1000,\n \"HEARTBEAT_MAX_INTERVAL_MS\": 900000,\n \"MAX_CLAIM_PER_POLL\": 5,\n \"MAX_LOG_MESSAGE_CHARS\": 10000,\n \"MAX_LOGS_PER_PATCH\": 100,\n \"MAX_ERROR_MESSAGE_CHARS\": 10000,\n \"MAX_ERROR_STACK_CHARS\": 20000,\n \"MAX_ITEMS_PER_REQUEST\": 500,\n \"MAX_ITEM_MESSAGE_CHARS\": 2000,\n \"MAX_ITEM_PAYLOAD_BYTES\": 16384,\n \"MAX_ITEM_COLUMNS\": 12,\n \"MAX_ITEM_COLUMN_LABEL_CHARS\": 60,\n \"MAX_ATTACHMENT_BYTES\": 5242880,\n \"MAX_ATTACHMENTS_PER_RUN\": 20,\n \"MAX_ATTACHMENTS_PER_LOG_ENTRY\": 4,\n \"MAX_CAPTION_CHARS\": 200,\n \"MAX_IMAGE_DIMENSION_PX\": 20000\n}\n","import limits from '../protocol-limits.json';\n\n/**\n * Limites do wire SDK ↔ servidor — fonte ÚNICA (protocol-limits.json): o SDK\n * Node lê daqui, a @hobots/api importa de `hobots/protocol` e o SDK Python\n * confere a paridade num teste que lê o mesmo JSON. Mudar um limite = mudar o\n * JSON (e, no Python, a constante espelhada — o teste acusa).\n */\nexport const WIRE_LIMITS = limits as Readonly<typeof limits>;\nexport type WireLimits = typeof WIRE_LIMITS;\n"],"mappings":";AAAA;AAAA,EACE,wBAA0B;AAAA,EAC1B,+BAAiC;AAAA,EACjC,2BAA6B;AAAA,EAC7B,2BAA6B;AAAA,EAC7B,oBAAsB;AAAA,EACtB,uBAAyB;AAAA,EACzB,oBAAsB;AAAA,EACtB,yBAA2B;AAAA,EAC3B,uBAAyB;AAAA,EACzB,uBAAyB;AAAA,EACzB,wBAA0B;AAAA,EAC1B,wBAA0B;AAAA,EAC1B,kBAAoB;AAAA,EACpB,6BAA+B;AAAA,EAC/B,sBAAwB;AAAA,EACxB,yBAA2B;AAAA,EAC3B,+BAAiC;AAAA,EACjC,mBAAqB;AAAA,EACrB,wBAA0B;AAC5B;;;ACZO,IAAM,cAAc;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -96,7 +96,7 @@ function scrubSecrets(text) {
|
|
|
96
96
|
|
|
97
97
|
// src/utils.ts
|
|
98
98
|
var SDK_NAME = "hobots";
|
|
99
|
-
var SDK_VERSION = true ? "4.
|
|
99
|
+
var SDK_VERSION = true ? "4.1.0" : "0.0.0-dev";
|
|
100
100
|
function isNode() {
|
|
101
101
|
return typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
102
102
|
}
|
|
@@ -281,7 +281,6 @@ var protocol_limits_default = {
|
|
|
281
281
|
MAX_LOGS_PER_PATCH: 100,
|
|
282
282
|
MAX_ERROR_MESSAGE_CHARS: 1e4,
|
|
283
283
|
MAX_ERROR_STACK_CHARS: 2e4,
|
|
284
|
-
MAX_ITEMS_PER_RUN: 1e4,
|
|
285
284
|
MAX_ITEMS_PER_REQUEST: 500,
|
|
286
285
|
MAX_ITEM_MESSAGE_CHARS: 2e3,
|
|
287
286
|
MAX_ITEM_PAYLOAD_BYTES: 16384,
|
|
@@ -1414,7 +1413,6 @@ var MAX_CANCEL_CHECK_INTERVAL_MS = 15e3;
|
|
|
1414
1413
|
var MAX_LOG_MESSAGE_CHARS = WIRE_LIMITS.MAX_LOG_MESSAGE_CHARS;
|
|
1415
1414
|
var MAX_LOGS_PER_PATCH = WIRE_LIMITS.MAX_LOGS_PER_PATCH;
|
|
1416
1415
|
var FLUSH_AT_BUFFERED = 20;
|
|
1417
|
-
var MAX_ITEMS_PER_RUN = WIRE_LIMITS.MAX_ITEMS_PER_RUN;
|
|
1418
1416
|
var MAX_ITEMS_PER_REQUEST = WIRE_LIMITS.MAX_ITEMS_PER_REQUEST;
|
|
1419
1417
|
var MAX_ITEM_MESSAGE_CHARS = WIRE_LIMITS.MAX_ITEM_MESSAGE_CHARS;
|
|
1420
1418
|
var MAX_ITEM_PAYLOAD_BYTES = WIRE_LIMITS.MAX_ITEM_PAYLOAD_BYTES;
|
|
@@ -1436,6 +1434,7 @@ var RunReporter = class {
|
|
|
1436
1434
|
this.api = api;
|
|
1437
1435
|
this.runId = runId;
|
|
1438
1436
|
this.logger = logger;
|
|
1437
|
+
this.tx = tx;
|
|
1439
1438
|
this.flushIntervalMs = flushIntervalMs ?? DEFAULT_LOG_FLUSH_INTERVAL_MS;
|
|
1440
1439
|
this.cancelCheckIntervalMs = Math.min(
|
|
1441
1440
|
MAX_CANCEL_CHECK_INTERVAL_MS,
|
|
@@ -1519,6 +1518,7 @@ var RunReporter = class {
|
|
|
1519
1518
|
items,
|
|
1520
1519
|
progress,
|
|
1521
1520
|
transaction: tx,
|
|
1521
|
+
flush: (timeoutMs) => this.flush(timeoutMs),
|
|
1522
1522
|
signal,
|
|
1523
1523
|
throwIfCanceled: checkCanceled,
|
|
1524
1524
|
sleep: sleep2,
|
|
@@ -1528,6 +1528,7 @@ var RunReporter = class {
|
|
|
1528
1528
|
api;
|
|
1529
1529
|
runId;
|
|
1530
1530
|
logger;
|
|
1531
|
+
tx;
|
|
1531
1532
|
context;
|
|
1532
1533
|
buffer = [];
|
|
1533
1534
|
dead = false;
|
|
@@ -1545,7 +1546,6 @@ var RunReporter = class {
|
|
|
1545
1546
|
itemsChain = Promise.resolve();
|
|
1546
1547
|
itemsTimer;
|
|
1547
1548
|
itemSeq = 0;
|
|
1548
|
-
itemCount = 0;
|
|
1549
1549
|
/** Servidor sem o recurso (404) — para de tentar (e de avisar) nesta run. */
|
|
1550
1550
|
itemsUnsupported = false;
|
|
1551
1551
|
/** Cap de itens por run atingido (client-side ou 429) — degrada com um aviso só. */
|
|
@@ -1668,6 +1668,21 @@ var RunReporter = class {
|
|
|
1668
1668
|
this.enqueue({ status: "running", logs: logs.slice(i, i + MAX_LOGS_PER_PATCH) });
|
|
1669
1669
|
}
|
|
1670
1670
|
}
|
|
1671
|
+
/**
|
|
1672
|
+
* `hobots.flush()`: o bot decide quando entregar o que já fez — steps raiz finalizadas
|
|
1673
|
+
* viram transactions parciais da run, e itens, logs e progresso pendentes saem já, sem
|
|
1674
|
+
* esperar o throttle. Aguarda a entrega com teto. Nunca lança (pode ir num `finally`).
|
|
1675
|
+
*/
|
|
1676
|
+
async flush(timeoutMs = ITEMS_DRAIN_MS) {
|
|
1677
|
+
this.tx.flush();
|
|
1678
|
+
this.flushItems();
|
|
1679
|
+
if (this.progressDirty) this.flushProgress();
|
|
1680
|
+
else this.flushLogs();
|
|
1681
|
+
await Promise.race([
|
|
1682
|
+
Promise.all([this.itemsChain, this.chain, this.tx.drain(timeoutMs)]).catch(() => void 0),
|
|
1683
|
+
new Promise((resolve2) => unrefTimer(setTimeout(resolve2, timeoutMs)))
|
|
1684
|
+
]);
|
|
1685
|
+
}
|
|
1671
1686
|
/** Primeiro PATCH (claimed → running). Retorna false se a run já morreu (cancelada). */
|
|
1672
1687
|
async reportRunning() {
|
|
1673
1688
|
this.enqueue({ status: "running" });
|
|
@@ -1778,14 +1793,6 @@ var RunReporter = class {
|
|
|
1778
1793
|
*/
|
|
1779
1794
|
addItem(status, message, payload, id, image3) {
|
|
1780
1795
|
if (this.itemsClosed || this.itemsUnsupported) return;
|
|
1781
|
-
if (this.itemCount >= MAX_ITEMS_PER_RUN) {
|
|
1782
|
-
if (!this.itemsQuotaExceeded) {
|
|
1783
|
-
this.itemsQuotaExceeded = true;
|
|
1784
|
-
this.log("warning", `itens descartados: limite de ${MAX_ITEMS_PER_RUN} por execu\xE7\xE3o atingido`);
|
|
1785
|
-
}
|
|
1786
|
-
return;
|
|
1787
|
-
}
|
|
1788
|
-
this.itemCount++;
|
|
1789
1796
|
let scrubbedPayload;
|
|
1790
1797
|
if (payload) {
|
|
1791
1798
|
scrubbedPayload = scrubPayloadValue(payload, 0);
|
|
@@ -2080,6 +2087,7 @@ function currentRunReporter() {
|
|
|
2080
2087
|
}
|
|
2081
2088
|
|
|
2082
2089
|
// src/transaction.ts
|
|
2090
|
+
var FLUSH_TRANSACTION_OP = "hobots.run.flush";
|
|
2083
2091
|
var MAX_STEP_MESSAGE_CHARS = 2e3;
|
|
2084
2092
|
function stepMessage(text) {
|
|
2085
2093
|
return truncate(scrubSecrets(text), MAX_STEP_MESSAGE_CHARS);
|
|
@@ -2301,6 +2309,60 @@ var Transaction = class {
|
|
|
2301
2309
|
this.name = name;
|
|
2302
2310
|
return this;
|
|
2303
2311
|
}
|
|
2312
|
+
/**
|
|
2313
|
+
* Envia já as steps raiz finalizadas (e todas as descendentes, fechando as que
|
|
2314
|
+
* ficaram abertas) como transactions parciais da mesma run (`op` hobots.run.flush,
|
|
2315
|
+
* `event_id` novo, até 200 steps cada) e as tira da memória. Steps raiz ainda abertas
|
|
2316
|
+
* ficam para o próximo flush ou para o `finish()`. É assim que um agente sempre
|
|
2317
|
+
* ligado (`start()`), cuja run não termina, entrega as etapas enquanto processa.
|
|
2318
|
+
* Devolve quantas steps foram enviadas.
|
|
2319
|
+
*/
|
|
2320
|
+
flush() {
|
|
2321
|
+
if (this.finished) return 0;
|
|
2322
|
+
const ready = /* @__PURE__ */ new Set();
|
|
2323
|
+
for (const step of this.steps) {
|
|
2324
|
+
if (step.parentStepId === this.stepId && step.endTimestamp) ready.add(step.stepId);
|
|
2325
|
+
}
|
|
2326
|
+
if (ready.size === 0) return 0;
|
|
2327
|
+
const batch = [];
|
|
2328
|
+
const remaining = [];
|
|
2329
|
+
for (const step of this.steps) {
|
|
2330
|
+
if (ready.has(step.stepId) || ready.has(step.parentStepId)) {
|
|
2331
|
+
ready.add(step.stepId);
|
|
2332
|
+
batch.push(step);
|
|
2333
|
+
} else {
|
|
2334
|
+
remaining.push(step);
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
for (const step of batch) step.finish();
|
|
2338
|
+
this.steps.splice(0, this.steps.length, ...remaining);
|
|
2339
|
+
if (this.client) {
|
|
2340
|
+
for (let i = 0; i < batch.length; i += MAX_STEPS) {
|
|
2341
|
+
const chunk = batch.slice(i, i + MAX_STEPS);
|
|
2342
|
+
const startTimestamp = chunk.reduce((min, step) => step.startTimestamp < min ? step.startTimestamp : min, chunk[0].startTimestamp);
|
|
2343
|
+
const status = chunk.some((step) => step.status === "error") ? "error" : "ok";
|
|
2344
|
+
this.client.sendTransaction(this.payload(uuid4(), FLUSH_TRANSACTION_OP, status, startTimestamp, nowISO(), chunk));
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
return batch.length;
|
|
2348
|
+
}
|
|
2349
|
+
/** Entrega já (com teto) o que está na fila do transport — as transactions parciais inclusive. */
|
|
2350
|
+
async drain(timeoutMs) {
|
|
2351
|
+
await this.client?.flush(timeoutMs);
|
|
2352
|
+
}
|
|
2353
|
+
payload(eventId, op, status, startTimestamp, timestamp, steps) {
|
|
2354
|
+
return {
|
|
2355
|
+
event_id: eventId,
|
|
2356
|
+
name: this.name,
|
|
2357
|
+
op,
|
|
2358
|
+
status,
|
|
2359
|
+
start_timestamp: startTimestamp,
|
|
2360
|
+
timestamp,
|
|
2361
|
+
steps: steps.map((step) => step.toJSON()),
|
|
2362
|
+
tags: Object.keys(this.tags).length > 0 ? { ...this.tags } : void 0,
|
|
2363
|
+
run_id: this.runId
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2304
2366
|
finish() {
|
|
2305
2367
|
if (this.finished) return;
|
|
2306
2368
|
this.finished = true;
|
|
@@ -2315,18 +2377,15 @@ var Transaction = class {
|
|
|
2315
2377
|
this.status
|
|
2316
2378
|
);
|
|
2317
2379
|
if (!this.client) return;
|
|
2318
|
-
const
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
status
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
run_id: this.runId
|
|
2328
|
-
};
|
|
2329
|
-
this.client.sendTransaction(payload);
|
|
2380
|
+
const steps = this.steps;
|
|
2381
|
+
let first = 0;
|
|
2382
|
+
while (steps.length - first > MAX_STEPS) {
|
|
2383
|
+
const chunk = steps.slice(first, first + MAX_STEPS);
|
|
2384
|
+
const status = chunk.some((step) => step.status === "error") ? "error" : "ok";
|
|
2385
|
+
this.client.sendTransaction(this.payload(uuid4(), FLUSH_TRANSACTION_OP, status, chunk[0].startTimestamp, endTimestamp, chunk));
|
|
2386
|
+
first += MAX_STEPS;
|
|
2387
|
+
}
|
|
2388
|
+
this.client.sendTransaction(this.payload(this.eventId, this.op, this.status, this.startTimestamp, endTimestamp, steps.slice(first)));
|
|
2330
2389
|
}
|
|
2331
2390
|
};
|
|
2332
2391
|
|
|
@@ -3031,6 +3090,7 @@ var hobots = Object.defineProperties({}, {
|
|
|
3031
3090
|
signal: getter("signal", () => neverAborts),
|
|
3032
3091
|
throwIfCanceled: method(() => resolve("throwIfCanceled")?.throwIfCanceled()),
|
|
3033
3092
|
sleep: method((ms) => resolve("sleep")?.sleep(ms) ?? sleep(ms)),
|
|
3093
|
+
flush: method((timeoutMs) => resolve("flush")?.flush(timeoutMs) ?? Promise.resolve()),
|
|
3034
3094
|
retry: method((fn, options) => {
|
|
3035
3095
|
const reporter = currentReporter();
|
|
3036
3096
|
return reporter ? reporter.context.retry(fn, options) : retryWithBackoff(fn, options);
|