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/dist/index.d.cts CHANGED
@@ -340,6 +340,18 @@ declare class Transaction {
340
340
  setTag(key: string, value: string): this;
341
341
  setStatus(status: string): this;
342
342
  setName(name: string): this;
343
+ /**
344
+ * Envia já as steps raiz finalizadas (e todas as descendentes, fechando as que
345
+ * ficaram abertas) como transactions parciais da mesma run (`op` hobots.run.flush,
346
+ * `event_id` novo, até 200 steps cada) e as tira da memória. Steps raiz ainda abertas
347
+ * ficam para o próximo flush ou para o `finish()`. É assim que um agente sempre
348
+ * ligado (`start()`), cuja run não termina, entrega as etapas enquanto processa.
349
+ * Devolve quantas steps foram enviadas.
350
+ */
351
+ flush(): number;
352
+ /** Entrega já (com teto) o que está na fila do transport — as transactions parciais inclusive. */
353
+ drain(timeoutMs: number): Promise<void>;
354
+ private payload;
343
355
  finish(): void;
344
356
  }
345
357
 
@@ -489,6 +501,14 @@ interface RunContext {
489
501
  * medir as etapas (steps); elas aparecem como waterfall na aba de performance da execução.
490
502
  */
491
503
  readonly transaction: Transaction;
504
+ /**
505
+ * Entrega já o que a execução fez: as steps raiz finalizadas viram transactions parciais
506
+ * (waterfall da Central sem esperar a run terminar) e itens, logs e progresso pendentes
507
+ * saem sem esperar o throttle. Quem decide quando chamar é o bot — num agente sempre
508
+ * ligado (`start()`), ao fim de cada rodada que processou itens. Aguarda a entrega com
509
+ * teto (`timeoutMs`, default 15s) e nunca lança.
510
+ */
511
+ flush(timeoutMs?: number): Promise<void>;
492
512
  /** Dispara em cancelamento (run cancelada no app), timeout ou shutdown — cancelamento cooperativo */
493
513
  readonly signal: AbortSignal;
494
514
  /**
package/dist/index.d.ts CHANGED
@@ -340,6 +340,18 @@ declare class Transaction {
340
340
  setTag(key: string, value: string): this;
341
341
  setStatus(status: string): this;
342
342
  setName(name: string): this;
343
+ /**
344
+ * Envia já as steps raiz finalizadas (e todas as descendentes, fechando as que
345
+ * ficaram abertas) como transactions parciais da mesma run (`op` hobots.run.flush,
346
+ * `event_id` novo, até 200 steps cada) e as tira da memória. Steps raiz ainda abertas
347
+ * ficam para o próximo flush ou para o `finish()`. É assim que um agente sempre
348
+ * ligado (`start()`), cuja run não termina, entrega as etapas enquanto processa.
349
+ * Devolve quantas steps foram enviadas.
350
+ */
351
+ flush(): number;
352
+ /** Entrega já (com teto) o que está na fila do transport — as transactions parciais inclusive. */
353
+ drain(timeoutMs: number): Promise<void>;
354
+ private payload;
343
355
  finish(): void;
344
356
  }
345
357
 
@@ -489,6 +501,14 @@ interface RunContext {
489
501
  * medir as etapas (steps); elas aparecem como waterfall na aba de performance da execução.
490
502
  */
491
503
  readonly transaction: Transaction;
504
+ /**
505
+ * Entrega já o que a execução fez: as steps raiz finalizadas viram transactions parciais
506
+ * (waterfall da Central sem esperar a run terminar) e itens, logs e progresso pendentes
507
+ * saem sem esperar o throttle. Quem decide quando chamar é o bot — num agente sempre
508
+ * ligado (`start()`), ao fim de cada rodada que processou itens. Aguarda a entrega com
509
+ * teto (`timeoutMs`, default 15s) e nunca lança.
510
+ */
511
+ flush(timeoutMs?: number): Promise<void>;
492
512
  /** Dispara em cancelamento (run cancelada no app), timeout ou shutdown — cancelamento cooperativo */
493
513
  readonly signal: AbortSignal;
494
514
  /**
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  WIRE_LIMITS
3
- } from "./chunk-5WIIACWC.js";
3
+ } from "./chunk-HKCDMUA3.js";
4
4
 
5
5
  // src/stacktrace.ts
6
6
  var MAX_FRAMES = 50;
@@ -57,7 +57,7 @@ function scrubSecrets(text) {
57
57
 
58
58
  // src/utils.ts
59
59
  var SDK_NAME = "hobots";
60
- var SDK_VERSION = true ? "4.0.0" : "0.0.0-dev";
60
+ var SDK_VERSION = true ? "4.1.0" : "0.0.0-dev";
61
61
  function isNode() {
62
62
  return typeof process !== "undefined" && typeof process.versions?.node === "string";
63
63
  }
@@ -1348,7 +1348,6 @@ var MAX_CANCEL_CHECK_INTERVAL_MS = 15e3;
1348
1348
  var MAX_LOG_MESSAGE_CHARS = WIRE_LIMITS.MAX_LOG_MESSAGE_CHARS;
1349
1349
  var MAX_LOGS_PER_PATCH = WIRE_LIMITS.MAX_LOGS_PER_PATCH;
1350
1350
  var FLUSH_AT_BUFFERED = 20;
1351
- var MAX_ITEMS_PER_RUN = WIRE_LIMITS.MAX_ITEMS_PER_RUN;
1352
1351
  var MAX_ITEMS_PER_REQUEST = WIRE_LIMITS.MAX_ITEMS_PER_REQUEST;
1353
1352
  var MAX_ITEM_MESSAGE_CHARS = WIRE_LIMITS.MAX_ITEM_MESSAGE_CHARS;
1354
1353
  var MAX_ITEM_PAYLOAD_BYTES = WIRE_LIMITS.MAX_ITEM_PAYLOAD_BYTES;
@@ -1370,6 +1369,7 @@ var RunReporter = class {
1370
1369
  this.api = api;
1371
1370
  this.runId = runId;
1372
1371
  this.logger = logger;
1372
+ this.tx = tx;
1373
1373
  this.flushIntervalMs = flushIntervalMs ?? DEFAULT_LOG_FLUSH_INTERVAL_MS;
1374
1374
  this.cancelCheckIntervalMs = Math.min(
1375
1375
  MAX_CANCEL_CHECK_INTERVAL_MS,
@@ -1453,6 +1453,7 @@ var RunReporter = class {
1453
1453
  items,
1454
1454
  progress,
1455
1455
  transaction: tx,
1456
+ flush: (timeoutMs) => this.flush(timeoutMs),
1456
1457
  signal,
1457
1458
  throwIfCanceled: checkCanceled,
1458
1459
  sleep: sleep2,
@@ -1462,6 +1463,7 @@ var RunReporter = class {
1462
1463
  api;
1463
1464
  runId;
1464
1465
  logger;
1466
+ tx;
1465
1467
  context;
1466
1468
  buffer = [];
1467
1469
  dead = false;
@@ -1479,7 +1481,6 @@ var RunReporter = class {
1479
1481
  itemsChain = Promise.resolve();
1480
1482
  itemsTimer;
1481
1483
  itemSeq = 0;
1482
- itemCount = 0;
1483
1484
  /** Servidor sem o recurso (404) — para de tentar (e de avisar) nesta run. */
1484
1485
  itemsUnsupported = false;
1485
1486
  /** Cap de itens por run atingido (client-side ou 429) — degrada com um aviso só. */
@@ -1602,6 +1603,21 @@ var RunReporter = class {
1602
1603
  this.enqueue({ status: "running", logs: logs.slice(i, i + MAX_LOGS_PER_PATCH) });
1603
1604
  }
1604
1605
  }
1606
+ /**
1607
+ * `hobots.flush()`: o bot decide quando entregar o que já fez — steps raiz finalizadas
1608
+ * viram transactions parciais da run, e itens, logs e progresso pendentes saem já, sem
1609
+ * esperar o throttle. Aguarda a entrega com teto. Nunca lança (pode ir num `finally`).
1610
+ */
1611
+ async flush(timeoutMs = ITEMS_DRAIN_MS) {
1612
+ this.tx.flush();
1613
+ this.flushItems();
1614
+ if (this.progressDirty) this.flushProgress();
1615
+ else this.flushLogs();
1616
+ await Promise.race([
1617
+ Promise.all([this.itemsChain, this.chain, this.tx.drain(timeoutMs)]).catch(() => void 0),
1618
+ new Promise((resolve2) => unrefTimer(setTimeout(resolve2, timeoutMs)))
1619
+ ]);
1620
+ }
1605
1621
  /** Primeiro PATCH (claimed → running). Retorna false se a run já morreu (cancelada). */
1606
1622
  async reportRunning() {
1607
1623
  this.enqueue({ status: "running" });
@@ -1712,14 +1728,6 @@ var RunReporter = class {
1712
1728
  */
1713
1729
  addItem(status, message, payload, id, image3) {
1714
1730
  if (this.itemsClosed || this.itemsUnsupported) return;
1715
- if (this.itemCount >= MAX_ITEMS_PER_RUN) {
1716
- if (!this.itemsQuotaExceeded) {
1717
- this.itemsQuotaExceeded = true;
1718
- this.log("warning", `itens descartados: limite de ${MAX_ITEMS_PER_RUN} por execu\xE7\xE3o atingido`);
1719
- }
1720
- return;
1721
- }
1722
- this.itemCount++;
1723
1731
  let scrubbedPayload;
1724
1732
  if (payload) {
1725
1733
  scrubbedPayload = scrubPayloadValue(payload, 0);
@@ -2014,6 +2022,7 @@ function currentRunReporter() {
2014
2022
  }
2015
2023
 
2016
2024
  // src/transaction.ts
2025
+ var FLUSH_TRANSACTION_OP = "hobots.run.flush";
2017
2026
  var MAX_STEP_MESSAGE_CHARS = 2e3;
2018
2027
  function stepMessage(text) {
2019
2028
  return truncate(scrubSecrets(text), MAX_STEP_MESSAGE_CHARS);
@@ -2235,6 +2244,60 @@ var Transaction = class {
2235
2244
  this.name = name;
2236
2245
  return this;
2237
2246
  }
2247
+ /**
2248
+ * Envia já as steps raiz finalizadas (e todas as descendentes, fechando as que
2249
+ * ficaram abertas) como transactions parciais da mesma run (`op` hobots.run.flush,
2250
+ * `event_id` novo, até 200 steps cada) e as tira da memória. Steps raiz ainda abertas
2251
+ * ficam para o próximo flush ou para o `finish()`. É assim que um agente sempre
2252
+ * ligado (`start()`), cuja run não termina, entrega as etapas enquanto processa.
2253
+ * Devolve quantas steps foram enviadas.
2254
+ */
2255
+ flush() {
2256
+ if (this.finished) return 0;
2257
+ const ready = /* @__PURE__ */ new Set();
2258
+ for (const step of this.steps) {
2259
+ if (step.parentStepId === this.stepId && step.endTimestamp) ready.add(step.stepId);
2260
+ }
2261
+ if (ready.size === 0) return 0;
2262
+ const batch = [];
2263
+ const remaining = [];
2264
+ for (const step of this.steps) {
2265
+ if (ready.has(step.stepId) || ready.has(step.parentStepId)) {
2266
+ ready.add(step.stepId);
2267
+ batch.push(step);
2268
+ } else {
2269
+ remaining.push(step);
2270
+ }
2271
+ }
2272
+ for (const step of batch) step.finish();
2273
+ this.steps.splice(0, this.steps.length, ...remaining);
2274
+ if (this.client) {
2275
+ for (let i = 0; i < batch.length; i += MAX_STEPS) {
2276
+ const chunk = batch.slice(i, i + MAX_STEPS);
2277
+ const startTimestamp = chunk.reduce((min, step) => step.startTimestamp < min ? step.startTimestamp : min, chunk[0].startTimestamp);
2278
+ const status = chunk.some((step) => step.status === "error") ? "error" : "ok";
2279
+ this.client.sendTransaction(this.payload(uuid4(), FLUSH_TRANSACTION_OP, status, startTimestamp, nowISO(), chunk));
2280
+ }
2281
+ }
2282
+ return batch.length;
2283
+ }
2284
+ /** Entrega já (com teto) o que está na fila do transport — as transactions parciais inclusive. */
2285
+ async drain(timeoutMs) {
2286
+ await this.client?.flush(timeoutMs);
2287
+ }
2288
+ payload(eventId, op, status, startTimestamp, timestamp, steps) {
2289
+ return {
2290
+ event_id: eventId,
2291
+ name: this.name,
2292
+ op,
2293
+ status,
2294
+ start_timestamp: startTimestamp,
2295
+ timestamp,
2296
+ steps: steps.map((step) => step.toJSON()),
2297
+ tags: Object.keys(this.tags).length > 0 ? { ...this.tags } : void 0,
2298
+ run_id: this.runId
2299
+ };
2300
+ }
2238
2301
  finish() {
2239
2302
  if (this.finished) return;
2240
2303
  this.finished = true;
@@ -2249,18 +2312,15 @@ var Transaction = class {
2249
2312
  this.status
2250
2313
  );
2251
2314
  if (!this.client) return;
2252
- const payload = {
2253
- event_id: this.eventId,
2254
- name: this.name,
2255
- op: this.op,
2256
- status: this.status,
2257
- start_timestamp: this.startTimestamp,
2258
- timestamp: endTimestamp,
2259
- steps: this.steps.map((step) => step.toJSON()),
2260
- tags: Object.keys(this.tags).length > 0 ? this.tags : void 0,
2261
- run_id: this.runId
2262
- };
2263
- this.client.sendTransaction(payload);
2315
+ const steps = this.steps;
2316
+ let first = 0;
2317
+ while (steps.length - first > MAX_STEPS) {
2318
+ const chunk = steps.slice(first, first + MAX_STEPS);
2319
+ const status = chunk.some((step) => step.status === "error") ? "error" : "ok";
2320
+ this.client.sendTransaction(this.payload(uuid4(), FLUSH_TRANSACTION_OP, status, chunk[0].startTimestamp, endTimestamp, chunk));
2321
+ first += MAX_STEPS;
2322
+ }
2323
+ this.client.sendTransaction(this.payload(this.eventId, this.op, this.status, this.startTimestamp, endTimestamp, steps.slice(first)));
2264
2324
  }
2265
2325
  };
2266
2326
 
@@ -2965,6 +3025,7 @@ var hobots = Object.defineProperties({}, {
2965
3025
  signal: getter("signal", () => neverAborts),
2966
3026
  throwIfCanceled: method(() => resolve("throwIfCanceled")?.throwIfCanceled()),
2967
3027
  sleep: method((ms) => resolve("sleep")?.sleep(ms) ?? sleep(ms)),
3028
+ flush: method((timeoutMs) => resolve("flush")?.flush(timeoutMs) ?? Promise.resolve()),
2968
3029
  retry: method((fn, options) => {
2969
3030
  const reporter = currentReporter();
2970
3031
  return reporter ? reporter.context.retry(fn, options) : retryWithBackoff(fn, options);