hobots 4.1.0 → 4.2.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 +31 -0
- package/dist/index.cjs +355 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -3
- package/dist/index.d.ts +44 -3
- package/dist/index.js +355 -8
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +39 -1
- package/dist/protocol.d.ts +39 -1
- package/docs/api-reference.md +2 -0
- package/docs/protocol.md +10 -0
- package/docs/runs.md +30 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { RunAttachmentContentType, EventType, EnvironmentInput, MachineSnapshot, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress } from './protocol.cjs';
|
|
2
|
-
export { Breadcrumb, BreadcrumbLevel, Envelope, EnvelopeItem, Environment, EventUser, ExceptionData, InstanceInfo, InstanceMode, ItemColumn, ItemsDeclaration, MachineProcess, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunCreateRequest, RunCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.cjs';
|
|
1
|
+
import { RunAttachmentContentType, EventType, EnvironmentInput, MachineSnapshot, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, FileRef, RunItemStatus, RunProgress } from './protocol.cjs';
|
|
2
|
+
export { Breadcrumb, BreadcrumbLevel, Envelope, EnvelopeItem, Environment, EventUser, ExceptionData, FileDownloadResponse, FileStorage, InstanceInfo, InstanceMode, ItemColumn, ItemsDeclaration, MachineProcess, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunCreateRequest, RunCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.cjs';
|
|
3
3
|
|
|
4
4
|
/** Espera entre tentativas: ms fixo ou função do número da tentativa que acabou de falhar (1-based). */
|
|
5
5
|
type RetryDelay = number | ((attempt: number) => number);
|
|
@@ -355,6 +355,38 @@ declare class Transaction {
|
|
|
355
355
|
finish(): void;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
interface FileDownloadOptions {
|
|
359
|
+
/**
|
|
360
|
+
* Destino da cópia: uma pasta existente (ou terminada em `/`) recebe o arquivo com o nome
|
|
361
|
+
* original; qualquer outro caminho é o arquivo final. Omitido: pasta temporária da
|
|
362
|
+
* execução, apagada quando ela termina.
|
|
363
|
+
*/
|
|
364
|
+
dest?: string;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Arquivos dos campos de upload do formulário (`hobots.files`). O valor do campo nos
|
|
368
|
+
* params é um `FileRef` (ou uma lista deles); `download` traz o arquivo para o disco
|
|
369
|
+
* desta máquina — do S3 (enviado pela Central) ou da pasta do Studio (mesmo computador)
|
|
370
|
+
* — e o consome na origem: cada arquivo é baixado uma vez.
|
|
371
|
+
*/
|
|
372
|
+
interface RunFiles {
|
|
373
|
+
/** Todos os arquivos dos params (campos de topo, valor único ou lista), na ordem dos campos. */
|
|
374
|
+
list(): FileRef[];
|
|
375
|
+
/** O arquivo do campo (campo único), a lista (campo "vários arquivos") ou undefined. */
|
|
376
|
+
get(field: string): FileRef | FileRef[] | undefined;
|
|
377
|
+
/**
|
|
378
|
+
* Baixa um arquivo e resolve com o caminho local. Aceita o `FileRef` ou o nome de um
|
|
379
|
+
* campo de arquivo único (para campo com vários arquivos, use `downloadAll`). Só depois
|
|
380
|
+
* da cópia completa e conferida o arquivo é consumido na origem — uma falha não perde
|
|
381
|
+
* nada e pode ser tentada de novo. Respeita o cancelamento da execução.
|
|
382
|
+
*/
|
|
383
|
+
download(refOrField: FileRef | string, options?: FileDownloadOptions): Promise<string>;
|
|
384
|
+
/** Baixa todos os arquivos do campo (único ou lista), em ordem. `dest` aqui é uma pasta. */
|
|
385
|
+
downloadAll(field: string, options?: FileDownloadOptions): Promise<string[]>;
|
|
386
|
+
/** Conteúdo do arquivo em memória — baixa, lê e apaga a cópia temporária. */
|
|
387
|
+
read(refOrField: FileRef | string): Promise<Buffer>;
|
|
388
|
+
}
|
|
389
|
+
|
|
358
390
|
/**
|
|
359
391
|
* `hobots.log`: cada método aceita vários argumentos, como `console.log` — cada um vira
|
|
360
392
|
* texto (`safeString`) e são unidos por espaço. Nunca lança, nem com a run cancelada.
|
|
@@ -495,6 +527,12 @@ interface RunContext {
|
|
|
495
527
|
readonly items: RunItems;
|
|
496
528
|
/** Progresso declarado (barra + ETA no app) — opcional, independente de `items` */
|
|
497
529
|
readonly progress: RunProgressReporter;
|
|
530
|
+
/**
|
|
531
|
+
* Arquivos dos campos de upload do formulário: `list()`, `get(campo)`, `download(refOuCampo)`
|
|
532
|
+
* (caminho local), `downloadAll(campo)` e `read(refOuCampo)` (conteúdo em memória).
|
|
533
|
+
* Cada arquivo é consumido na origem ao ser baixado — baixe uma vez e reaproveite a cópia.
|
|
534
|
+
*/
|
|
535
|
+
readonly files: RunFiles;
|
|
498
536
|
/**
|
|
499
537
|
* Transaction de performance desta execução — já correlacionada à run e finalizada
|
|
500
538
|
* automaticamente ao fim do handler. Use `transaction.startChild(op, description)` para
|
|
@@ -547,6 +585,8 @@ interface Hobots extends RunContext {
|
|
|
547
585
|
* `step.setError(err, undefined, { image: png })`. Puro: nenhum I/O até o evento ser registrado.
|
|
548
586
|
*/
|
|
549
587
|
image(input: AttachInput, options?: ImageOptions): ImageAttachment;
|
|
588
|
+
/** O valor tem a forma de um arquivo de campo de upload (`FileRef`) — puro, sem I/O. */
|
|
589
|
+
isFileRef(value: unknown): value is FileRef;
|
|
550
590
|
}
|
|
551
591
|
declare global {
|
|
552
592
|
var hobots: Hobots;
|
|
@@ -558,5 +598,6 @@ declare const close: (timeoutMs?: number) => Promise<boolean>;
|
|
|
558
598
|
declare const captureException: (error: unknown, hint?: CaptureHint) => string | undefined;
|
|
559
599
|
declare const retry: <T>(fn: (attempt: number) => T | Promise<T>, options?: RetryOptions) => Promise<T>;
|
|
560
600
|
declare const image: (input: AttachInput, options?: ImageOptions) => ImageAttachment;
|
|
601
|
+
declare const isFileRef: (value: unknown) => value is FileRef;
|
|
561
602
|
|
|
562
|
-
export { type AttachInput, type CaptureAttachment, type CaptureHint, EnvironmentInput, EventPayload, EventType, HeartbeatPayload, type Hobots, type HobotsOptions, ImageAttachment, type ImageInput, type ImageOptions, type ItemColumnInput, type ItemsOptions, MachineSnapshot, type RegisterOptions, type RetryDelay, type RetryOptions, RunAttachmentContentType, type RunContext, type RunHandler, type RunItemHandle, type RunItemImageOptions, type RunItemOptions, RunItemStatus, type RunItems, type RunItemsEachHandler, type RunItemsEachOptions, type RunItemsEachResult, RunLogLevel, type RunLogger, type RunOptions, RunProgress, type RunProgressReporter, type StartOptions, Step, StepData, type StepOutcomeOptions, StepStatus, Transaction, TransactionPayload, captureException, close, hobots as default, hobots, image, register, retry, start };
|
|
603
|
+
export { type AttachInput, type CaptureAttachment, type CaptureHint, EnvironmentInput, EventPayload, EventType, type FileDownloadOptions, FileRef, HeartbeatPayload, type Hobots, type HobotsOptions, ImageAttachment, type ImageInput, type ImageOptions, type ItemColumnInput, type ItemsOptions, MachineSnapshot, type RegisterOptions, type RetryDelay, type RetryOptions, RunAttachmentContentType, type RunContext, type RunFiles, type RunHandler, type RunItemHandle, type RunItemImageOptions, type RunItemOptions, RunItemStatus, type RunItems, type RunItemsEachHandler, type RunItemsEachOptions, type RunItemsEachResult, RunLogLevel, type RunLogger, type RunOptions, RunProgress, type RunProgressReporter, type StartOptions, Step, StepData, type StepOutcomeOptions, StepStatus, Transaction, TransactionPayload, captureException, close, hobots as default, hobots, image, isFileRef, register, retry, start };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { RunAttachmentContentType, EventType, EnvironmentInput, MachineSnapshot, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress } from './protocol.js';
|
|
2
|
-
export { Breadcrumb, BreadcrumbLevel, Envelope, EnvelopeItem, Environment, EventUser, ExceptionData, InstanceInfo, InstanceMode, ItemColumn, ItemsDeclaration, MachineProcess, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunCreateRequest, RunCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.js';
|
|
1
|
+
import { RunAttachmentContentType, EventType, EnvironmentInput, MachineSnapshot, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, FileRef, RunItemStatus, RunProgress } from './protocol.js';
|
|
2
|
+
export { Breadcrumb, BreadcrumbLevel, Envelope, EnvelopeItem, Environment, EventUser, ExceptionData, FileDownloadResponse, FileStorage, InstanceInfo, InstanceMode, ItemColumn, ItemsDeclaration, MachineProcess, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunCreateRequest, RunCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.js';
|
|
3
3
|
|
|
4
4
|
/** Espera entre tentativas: ms fixo ou função do número da tentativa que acabou de falhar (1-based). */
|
|
5
5
|
type RetryDelay = number | ((attempt: number) => number);
|
|
@@ -355,6 +355,38 @@ declare class Transaction {
|
|
|
355
355
|
finish(): void;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
interface FileDownloadOptions {
|
|
359
|
+
/**
|
|
360
|
+
* Destino da cópia: uma pasta existente (ou terminada em `/`) recebe o arquivo com o nome
|
|
361
|
+
* original; qualquer outro caminho é o arquivo final. Omitido: pasta temporária da
|
|
362
|
+
* execução, apagada quando ela termina.
|
|
363
|
+
*/
|
|
364
|
+
dest?: string;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Arquivos dos campos de upload do formulário (`hobots.files`). O valor do campo nos
|
|
368
|
+
* params é um `FileRef` (ou uma lista deles); `download` traz o arquivo para o disco
|
|
369
|
+
* desta máquina — do S3 (enviado pela Central) ou da pasta do Studio (mesmo computador)
|
|
370
|
+
* — e o consome na origem: cada arquivo é baixado uma vez.
|
|
371
|
+
*/
|
|
372
|
+
interface RunFiles {
|
|
373
|
+
/** Todos os arquivos dos params (campos de topo, valor único ou lista), na ordem dos campos. */
|
|
374
|
+
list(): FileRef[];
|
|
375
|
+
/** O arquivo do campo (campo único), a lista (campo "vários arquivos") ou undefined. */
|
|
376
|
+
get(field: string): FileRef | FileRef[] | undefined;
|
|
377
|
+
/**
|
|
378
|
+
* Baixa um arquivo e resolve com o caminho local. Aceita o `FileRef` ou o nome de um
|
|
379
|
+
* campo de arquivo único (para campo com vários arquivos, use `downloadAll`). Só depois
|
|
380
|
+
* da cópia completa e conferida o arquivo é consumido na origem — uma falha não perde
|
|
381
|
+
* nada e pode ser tentada de novo. Respeita o cancelamento da execução.
|
|
382
|
+
*/
|
|
383
|
+
download(refOrField: FileRef | string, options?: FileDownloadOptions): Promise<string>;
|
|
384
|
+
/** Baixa todos os arquivos do campo (único ou lista), em ordem. `dest` aqui é uma pasta. */
|
|
385
|
+
downloadAll(field: string, options?: FileDownloadOptions): Promise<string[]>;
|
|
386
|
+
/** Conteúdo do arquivo em memória — baixa, lê e apaga a cópia temporária. */
|
|
387
|
+
read(refOrField: FileRef | string): Promise<Buffer>;
|
|
388
|
+
}
|
|
389
|
+
|
|
358
390
|
/**
|
|
359
391
|
* `hobots.log`: cada método aceita vários argumentos, como `console.log` — cada um vira
|
|
360
392
|
* texto (`safeString`) e são unidos por espaço. Nunca lança, nem com a run cancelada.
|
|
@@ -495,6 +527,12 @@ interface RunContext {
|
|
|
495
527
|
readonly items: RunItems;
|
|
496
528
|
/** Progresso declarado (barra + ETA no app) — opcional, independente de `items` */
|
|
497
529
|
readonly progress: RunProgressReporter;
|
|
530
|
+
/**
|
|
531
|
+
* Arquivos dos campos de upload do formulário: `list()`, `get(campo)`, `download(refOuCampo)`
|
|
532
|
+
* (caminho local), `downloadAll(campo)` e `read(refOuCampo)` (conteúdo em memória).
|
|
533
|
+
* Cada arquivo é consumido na origem ao ser baixado — baixe uma vez e reaproveite a cópia.
|
|
534
|
+
*/
|
|
535
|
+
readonly files: RunFiles;
|
|
498
536
|
/**
|
|
499
537
|
* Transaction de performance desta execução — já correlacionada à run e finalizada
|
|
500
538
|
* automaticamente ao fim do handler. Use `transaction.startChild(op, description)` para
|
|
@@ -547,6 +585,8 @@ interface Hobots extends RunContext {
|
|
|
547
585
|
* `step.setError(err, undefined, { image: png })`. Puro: nenhum I/O até o evento ser registrado.
|
|
548
586
|
*/
|
|
549
587
|
image(input: AttachInput, options?: ImageOptions): ImageAttachment;
|
|
588
|
+
/** O valor tem a forma de um arquivo de campo de upload (`FileRef`) — puro, sem I/O. */
|
|
589
|
+
isFileRef(value: unknown): value is FileRef;
|
|
550
590
|
}
|
|
551
591
|
declare global {
|
|
552
592
|
var hobots: Hobots;
|
|
@@ -558,5 +598,6 @@ declare const close: (timeoutMs?: number) => Promise<boolean>;
|
|
|
558
598
|
declare const captureException: (error: unknown, hint?: CaptureHint) => string | undefined;
|
|
559
599
|
declare const retry: <T>(fn: (attempt: number) => T | Promise<T>, options?: RetryOptions) => Promise<T>;
|
|
560
600
|
declare const image: (input: AttachInput, options?: ImageOptions) => ImageAttachment;
|
|
601
|
+
declare const isFileRef: (value: unknown) => value is FileRef;
|
|
561
602
|
|
|
562
|
-
export { type AttachInput, type CaptureAttachment, type CaptureHint, EnvironmentInput, EventPayload, EventType, HeartbeatPayload, type Hobots, type HobotsOptions, ImageAttachment, type ImageInput, type ImageOptions, type ItemColumnInput, type ItemsOptions, MachineSnapshot, type RegisterOptions, type RetryDelay, type RetryOptions, RunAttachmentContentType, type RunContext, type RunHandler, type RunItemHandle, type RunItemImageOptions, type RunItemOptions, RunItemStatus, type RunItems, type RunItemsEachHandler, type RunItemsEachOptions, type RunItemsEachResult, RunLogLevel, type RunLogger, type RunOptions, RunProgress, type RunProgressReporter, type StartOptions, Step, StepData, type StepOutcomeOptions, StepStatus, Transaction, TransactionPayload, captureException, close, hobots as default, hobots, image, register, retry, start };
|
|
603
|
+
export { type AttachInput, type CaptureAttachment, type CaptureHint, EnvironmentInput, EventPayload, EventType, type FileDownloadOptions, FileRef, HeartbeatPayload, type Hobots, type HobotsOptions, ImageAttachment, type ImageInput, type ImageOptions, type ItemColumnInput, type ItemsOptions, MachineSnapshot, type RegisterOptions, type RetryDelay, type RetryOptions, RunAttachmentContentType, type RunContext, type RunFiles, type RunHandler, type RunItemHandle, type RunItemImageOptions, type RunItemOptions, RunItemStatus, type RunItems, type RunItemsEachHandler, type RunItemsEachOptions, type RunItemsEachResult, RunLogLevel, type RunLogger, type RunOptions, RunProgress, type RunProgressReporter, type StartOptions, Step, StepData, type StepOutcomeOptions, StepStatus, Transaction, TransactionPayload, captureException, close, hobots as default, hobots, image, isFileRef, register, retry, start };
|
package/dist/index.js
CHANGED
|
@@ -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.
|
|
60
|
+
var SDK_VERSION = true ? "4.2.0" : "0.0.0-dev";
|
|
61
61
|
function isNode() {
|
|
62
62
|
return typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
63
63
|
}
|
|
@@ -139,13 +139,13 @@ async function fetchWithRetry(send, options) {
|
|
|
139
139
|
} catch (error) {
|
|
140
140
|
retriable = error;
|
|
141
141
|
}
|
|
142
|
+
const failure = retriable instanceof Error ? retriable : new Error(String(retriable));
|
|
142
143
|
const base = delays[attempt];
|
|
143
|
-
if (base === void 0)
|
|
144
|
-
throw retriable instanceof Error ? retriable : new Error(String(retriable));
|
|
145
|
-
}
|
|
144
|
+
if (base === void 0 || options.signal?.aborted) throw failure;
|
|
146
145
|
const delay = serverDelay ?? jitter(base);
|
|
147
146
|
options.logger.warn(`${options.label}: falha (${String(retriable)}), retry em ${delay}ms`);
|
|
148
|
-
await sleep(delay);
|
|
147
|
+
await (options.signal ? abortableSleep(delay, options.signal) : sleep(delay));
|
|
148
|
+
if (options.signal?.aborted) throw failure;
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
var MAX_RETRY_AFTER_MS = 6e4;
|
|
@@ -831,6 +831,7 @@ var ATTACHMENT_TERMINAL_STATUSES = [429, 501, 503];
|
|
|
831
831
|
var ITEMS_TERMINAL_STATUSES = [429];
|
|
832
832
|
var UPLOAD_TIMEOUT_MS = 3e4;
|
|
833
833
|
var UPLOAD_RETRY_DELAYS_MS = [1e3, 4e3];
|
|
834
|
+
var FILE_RETRY_DELAYS_MS = [1e3, 4e3];
|
|
834
835
|
var TasksApiError = class extends Error {
|
|
835
836
|
constructor(message, status) {
|
|
836
837
|
super(message);
|
|
@@ -1018,7 +1019,13 @@ var TasksApi = class {
|
|
|
1018
1019
|
signal: timeoutSignal
|
|
1019
1020
|
})
|
|
1020
1021
|
),
|
|
1021
|
-
{
|
|
1022
|
+
{
|
|
1023
|
+
logger: this.logger,
|
|
1024
|
+
label: `anexo da run ${runId}`,
|
|
1025
|
+
noRetryStatuses: ATTACHMENT_TERMINAL_STATUSES,
|
|
1026
|
+
// run abortada (timeout, 409, kill): o create em voo rejeita por abort e não entra na ladder
|
|
1027
|
+
signal
|
|
1028
|
+
}
|
|
1022
1029
|
);
|
|
1023
1030
|
if (!response.ok) {
|
|
1024
1031
|
throw new TasksApiError(`cria\xE7\xE3o de anexo rejeitada (HTTP ${response.status})`, response.status);
|
|
@@ -1052,7 +1059,7 @@ var TasksApi = class {
|
|
|
1052
1059
|
signal: timeoutSignal
|
|
1053
1060
|
})
|
|
1054
1061
|
),
|
|
1055
|
-
{ delays: UPLOAD_RETRY_DELAYS_MS, logger: this.logger, label: "upload de anexo" }
|
|
1062
|
+
{ delays: UPLOAD_RETRY_DELAYS_MS, logger: this.logger, label: "upload de anexo", signal }
|
|
1056
1063
|
);
|
|
1057
1064
|
if (!response.ok) {
|
|
1058
1065
|
throw new TasksApiError(`upload de anexo rejeitado (HTTP ${response.status})`, response.status);
|
|
@@ -1074,6 +1081,74 @@ var TasksApi = class {
|
|
|
1074
1081
|
throw new TasksApiError(`confirma\xE7\xE3o de anexo rejeitada (HTTP ${response.status})`, response.status);
|
|
1075
1082
|
}
|
|
1076
1083
|
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Pede a URL pré-assinada de GET de um arquivo do formulário. 404 (não existe nesta run)
|
|
1086
|
+
* e 410 (já baixado/expirado) viram `TasksApiError` com o status — quem chama traduz.
|
|
1087
|
+
*/
|
|
1088
|
+
async requestFileDownload(runId, fileId, signal) {
|
|
1089
|
+
const response = await fetchWithRetry(
|
|
1090
|
+
() => withRequestTimeout(
|
|
1091
|
+
UPDATE_TIMEOUT_MS,
|
|
1092
|
+
signal,
|
|
1093
|
+
(timeoutSignal) => fetch(`${this.baseUrl}/${encodeURIComponent(runId)}/files/${encodeURIComponent(fileId)}/download`, {
|
|
1094
|
+
redirect: "error",
|
|
1095
|
+
method: "POST",
|
|
1096
|
+
headers: this.headers(),
|
|
1097
|
+
signal: timeoutSignal
|
|
1098
|
+
})
|
|
1099
|
+
),
|
|
1100
|
+
{ delays: FILE_RETRY_DELAYS_MS, logger: this.logger, label: `download do arquivo ${fileId}`, signal }
|
|
1101
|
+
);
|
|
1102
|
+
if (!response.ok) {
|
|
1103
|
+
throw new TasksApiError(`download de arquivo rejeitado (HTTP ${response.status})`, response.status);
|
|
1104
|
+
}
|
|
1105
|
+
const body = await response.json().catch(() => null);
|
|
1106
|
+
if (typeof body?.url !== "string") {
|
|
1107
|
+
throw new TasksApiError("resposta inv\xE1lida do download de arquivo (url ausente)", 0);
|
|
1108
|
+
}
|
|
1109
|
+
return {
|
|
1110
|
+
url: body.url,
|
|
1111
|
+
expires_in: typeof body.expires_in === "number" ? body.expires_in : 0,
|
|
1112
|
+
name: typeof body.name === "string" ? body.name : "",
|
|
1113
|
+
size: typeof body.size === "number" ? body.size : -1,
|
|
1114
|
+
content_type: typeof body.content_type === "string" ? body.content_type : ""
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* GET do binário direto no S3 — deliberadamente sem `authorization` (a autorização
|
|
1119
|
+
* está na assinatura da URL; mandar o client secret para fora da API seria vazamento).
|
|
1120
|
+
* Devolve a Response com o corpo ainda não lido: quem chama faz o streaming para o disco.
|
|
1121
|
+
*/
|
|
1122
|
+
async fetchFile(url, signal) {
|
|
1123
|
+
const response = await fetchWithRetry(() => fetch(url, { redirect: "error", method: "GET", signal }), {
|
|
1124
|
+
delays: FILE_RETRY_DELAYS_MS,
|
|
1125
|
+
logger: this.logger,
|
|
1126
|
+
label: "download de arquivo",
|
|
1127
|
+
signal
|
|
1128
|
+
});
|
|
1129
|
+
if (!response.ok) {
|
|
1130
|
+
throw new TasksApiError(`download de arquivo falhou (HTTP ${response.status})`, response.status);
|
|
1131
|
+
}
|
|
1132
|
+
return response;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Confirma o download: o servidor apaga o objeto do S3 na hora. Idempotente (204);
|
|
1136
|
+
* 404/410 também significam "não há mais nada a apagar" e contam como confirmado.
|
|
1137
|
+
*/
|
|
1138
|
+
async confirmFileDownload(runId, fileId) {
|
|
1139
|
+
const response = await fetchWithRetry(
|
|
1140
|
+
() => fetch(`${this.baseUrl}/${encodeURIComponent(runId)}/files/${encodeURIComponent(fileId)}`, {
|
|
1141
|
+
redirect: "error",
|
|
1142
|
+
method: "DELETE",
|
|
1143
|
+
headers: this.headers(),
|
|
1144
|
+
signal: AbortSignal.timeout(UPDATE_TIMEOUT_MS)
|
|
1145
|
+
}),
|
|
1146
|
+
{ delays: FILE_RETRY_DELAYS_MS, logger: this.logger, label: `confirma\xE7\xE3o do arquivo ${fileId}` }
|
|
1147
|
+
);
|
|
1148
|
+
if (!response.ok && response.status !== 404 && response.status !== 410) {
|
|
1149
|
+
throw new TasksApiError(`confirma\xE7\xE3o de download rejeitada (HTTP ${response.status})`, response.status);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1077
1152
|
};
|
|
1078
1153
|
|
|
1079
1154
|
// src/tasks/backoff.ts
|
|
@@ -1208,6 +1283,255 @@ async function eachItem(list, handler, options, sink) {
|
|
|
1208
1283
|
return result;
|
|
1209
1284
|
}
|
|
1210
1285
|
|
|
1286
|
+
// src/tasks/files.ts
|
|
1287
|
+
var LOCAL_FILES_DIR_ENV = "HOBOTS_LOCAL_FILES_DIR";
|
|
1288
|
+
var FILE_DOWNLOAD_IDLE_TIMEOUT_MS = 6e4;
|
|
1289
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1290
|
+
function isFileRef(value) {
|
|
1291
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1292
|
+
const ref = value;
|
|
1293
|
+
return typeof ref.id === "string" && typeof ref.name === "string" && typeof ref.size === "number" && typeof ref.contentType === "string" && (ref.storage === "s3" || ref.storage === "local");
|
|
1294
|
+
}
|
|
1295
|
+
function fileField(params, field) {
|
|
1296
|
+
if (!Object.prototype.hasOwnProperty.call(params, field)) return void 0;
|
|
1297
|
+
const value = params[field];
|
|
1298
|
+
if (isFileRef(value)) return value;
|
|
1299
|
+
if (Array.isArray(value) && value.every(isFileRef)) return value;
|
|
1300
|
+
return void 0;
|
|
1301
|
+
}
|
|
1302
|
+
function collectFileRefs(params) {
|
|
1303
|
+
const refs = [];
|
|
1304
|
+
for (const field of Object.keys(params)) {
|
|
1305
|
+
const value = fileField(params, field);
|
|
1306
|
+
if (value !== void 0) refs.push(...Array.isArray(value) ? value : [value]);
|
|
1307
|
+
}
|
|
1308
|
+
return refs;
|
|
1309
|
+
}
|
|
1310
|
+
function safeFileName(name, fallback) {
|
|
1311
|
+
const cleaned = name.replace(/^.*[\\/]/, "").replace(/[\u0000-\u001f<>:"|?*]/g, "_").trim();
|
|
1312
|
+
return cleaned === "" || cleaned === "." || cleaned === ".." ? fallback : cleaned;
|
|
1313
|
+
}
|
|
1314
|
+
function isInsideDir(root, candidate, sep) {
|
|
1315
|
+
const base = root.endsWith(sep) ? root : root + sep;
|
|
1316
|
+
return candidate.startsWith(base) && candidate.length > base.length;
|
|
1317
|
+
}
|
|
1318
|
+
function unavailableLocal(ref) {
|
|
1319
|
+
return new Error(`arquivo "${ref.name}" enviado pelo Studio de outro computador ou j\xE1 consumido`);
|
|
1320
|
+
}
|
|
1321
|
+
function errorCode(error) {
|
|
1322
|
+
return error?.code;
|
|
1323
|
+
}
|
|
1324
|
+
var RunFileStore = class {
|
|
1325
|
+
constructor(deps) {
|
|
1326
|
+
this.deps = deps;
|
|
1327
|
+
this.files = {
|
|
1328
|
+
list: () => collectFileRefs(deps.params),
|
|
1329
|
+
get: (field) => fileField(deps.params, field),
|
|
1330
|
+
// async: erro de uso (campo sem arquivo, valor inválido) chega como Promise rejeitada, nunca síncrono
|
|
1331
|
+
download: async (refOrField, options) => this.download(this.single(refOrField), options?.dest),
|
|
1332
|
+
downloadAll: async (field, options) => this.downloadAll(field, options?.dest),
|
|
1333
|
+
read: async (refOrField) => this.read(this.single(refOrField))
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
deps;
|
|
1337
|
+
files;
|
|
1338
|
+
tempDir;
|
|
1339
|
+
/** Apaga a pasta temporária da execução (cópias que o bot não levou para `dest`). Nunca lança. */
|
|
1340
|
+
async cleanup() {
|
|
1341
|
+
const pending = this.tempDir;
|
|
1342
|
+
this.tempDir = void 0;
|
|
1343
|
+
if (!pending) return;
|
|
1344
|
+
try {
|
|
1345
|
+
const { rm } = await import("fs/promises");
|
|
1346
|
+
await rm(await pending, { recursive: true, force: true });
|
|
1347
|
+
} catch {
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
field(field) {
|
|
1351
|
+
const value = fileField(this.deps.params, field);
|
|
1352
|
+
if (value === void 0) throw new Error(`campo "${field}" n\xE3o tem arquivo nos params`);
|
|
1353
|
+
return value;
|
|
1354
|
+
}
|
|
1355
|
+
single(refOrField) {
|
|
1356
|
+
if (typeof refOrField === "string") {
|
|
1357
|
+
const value = this.field(refOrField);
|
|
1358
|
+
if (Array.isArray(value)) {
|
|
1359
|
+
throw new Error(`campo "${refOrField}" tem v\xE1rios arquivos \u2014 use downloadAll("${refOrField}") ou passe um item da lista`);
|
|
1360
|
+
}
|
|
1361
|
+
return value;
|
|
1362
|
+
}
|
|
1363
|
+
if (!isFileRef(refOrField)) throw new TypeError("esperado um FileRef ou o nome de um campo de arquivo");
|
|
1364
|
+
return refOrField;
|
|
1365
|
+
}
|
|
1366
|
+
async downloadAll(field, dest) {
|
|
1367
|
+
const value = this.field(field);
|
|
1368
|
+
const refs = Array.isArray(value) ? value : [value];
|
|
1369
|
+
const paths = [];
|
|
1370
|
+
for (const ref of refs) paths.push(await this.download(ref, dest, true));
|
|
1371
|
+
return paths;
|
|
1372
|
+
}
|
|
1373
|
+
async read(ref) {
|
|
1374
|
+
const { readFile, rm } = await import("fs/promises");
|
|
1375
|
+
const { dirname } = await import("path");
|
|
1376
|
+
const path = await this.download(ref, void 0);
|
|
1377
|
+
try {
|
|
1378
|
+
return await readFile(path);
|
|
1379
|
+
} finally {
|
|
1380
|
+
await rm(dirname(path), { recursive: true, force: true });
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
async download(ref, dest, destIsDir = false) {
|
|
1384
|
+
this.checkCanceled();
|
|
1385
|
+
const target = await this.resolveTarget(ref, dest, destIsDir);
|
|
1386
|
+
if (ref.storage === "local") await this.copyLocal(ref, target);
|
|
1387
|
+
else await this.downloadS3(ref, target);
|
|
1388
|
+
return target;
|
|
1389
|
+
}
|
|
1390
|
+
checkCanceled() {
|
|
1391
|
+
if (this.deps.signal.aborted) throw runCanceledError();
|
|
1392
|
+
}
|
|
1393
|
+
runTempDir() {
|
|
1394
|
+
this.tempDir ??= (async () => {
|
|
1395
|
+
const { mkdtemp } = await import("fs/promises");
|
|
1396
|
+
const { tmpdir } = await import("os");
|
|
1397
|
+
const { join } = await import("path");
|
|
1398
|
+
return mkdtemp(join(tmpdir(), "hobots-files-"));
|
|
1399
|
+
})();
|
|
1400
|
+
return this.tempDir;
|
|
1401
|
+
}
|
|
1402
|
+
async resolveTarget(ref, dest, destIsDir) {
|
|
1403
|
+
const { mkdir, mkdtemp, stat } = await import("fs/promises");
|
|
1404
|
+
const { dirname, join, resolve: resolve2 } = await import("path");
|
|
1405
|
+
const name = safeFileName(ref.name, "arquivo");
|
|
1406
|
+
if (dest === void 0) {
|
|
1407
|
+
return join(await mkdtemp(join(await this.runTempDir(), "f-")), name);
|
|
1408
|
+
}
|
|
1409
|
+
const isDir = destIsDir || dest.endsWith("/") || dest.endsWith("\\") || (await stat(dest).catch(() => void 0))?.isDirectory() === true;
|
|
1410
|
+
if (isDir) {
|
|
1411
|
+
await mkdir(dest, { recursive: true });
|
|
1412
|
+
return resolve2(dest, name);
|
|
1413
|
+
}
|
|
1414
|
+
await mkdir(dirname(resolve2(dest)), { recursive: true });
|
|
1415
|
+
return resolve2(dest);
|
|
1416
|
+
}
|
|
1417
|
+
/** storage 's3': URL pré-assinada → GET em streaming → confere o tamanho → só então DELETE. */
|
|
1418
|
+
async downloadS3(ref, target) {
|
|
1419
|
+
const { api, runId, signal } = this.deps;
|
|
1420
|
+
let expected;
|
|
1421
|
+
let url;
|
|
1422
|
+
try {
|
|
1423
|
+
const info = await api.requestFileDownload(runId, ref.id, signal);
|
|
1424
|
+
expected = info.size >= 0 ? info.size : ref.size;
|
|
1425
|
+
url = info.url;
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
throw this.translate(error, ref);
|
|
1428
|
+
}
|
|
1429
|
+
const { open, rename, rm } = await import("fs/promises");
|
|
1430
|
+
const part = `${target}.part`;
|
|
1431
|
+
const idleTimeoutMs = this.deps.idleTimeoutMs ?? FILE_DOWNLOAD_IDLE_TIMEOUT_MS;
|
|
1432
|
+
const controller = new AbortController();
|
|
1433
|
+
let timedOut = false;
|
|
1434
|
+
let timer;
|
|
1435
|
+
const arm = () => {
|
|
1436
|
+
clearTimeout(timer);
|
|
1437
|
+
timer = setTimeout(() => {
|
|
1438
|
+
timedOut = true;
|
|
1439
|
+
controller.abort();
|
|
1440
|
+
}, idleTimeoutMs);
|
|
1441
|
+
};
|
|
1442
|
+
const onCancel = () => controller.abort();
|
|
1443
|
+
signal.addEventListener("abort", onCancel, { once: true });
|
|
1444
|
+
try {
|
|
1445
|
+
arm();
|
|
1446
|
+
const response = await api.fetchFile(url, controller.signal);
|
|
1447
|
+
const handle = await open(part, "w");
|
|
1448
|
+
let written = 0;
|
|
1449
|
+
try {
|
|
1450
|
+
const reader = response.body?.getReader();
|
|
1451
|
+
const stopReading = () => void reader?.cancel().catch(() => void 0);
|
|
1452
|
+
controller.signal.addEventListener("abort", stopReading, { once: true });
|
|
1453
|
+
for (; ; ) {
|
|
1454
|
+
const chunk = reader ? await reader.read() : { done: true, value: void 0 };
|
|
1455
|
+
if (chunk.done) break;
|
|
1456
|
+
arm();
|
|
1457
|
+
written += chunk.value.byteLength;
|
|
1458
|
+
if (written > expected) {
|
|
1459
|
+
stopReading();
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
await handle.write(chunk.value);
|
|
1463
|
+
}
|
|
1464
|
+
} finally {
|
|
1465
|
+
await handle.close();
|
|
1466
|
+
}
|
|
1467
|
+
if (written !== expected) {
|
|
1468
|
+
throw new Error(`tamanho divergente no download de "${ref.name}": recebidos ${written} bytes, esperados ${expected}`);
|
|
1469
|
+
}
|
|
1470
|
+
await rename(part, target);
|
|
1471
|
+
} catch (error) {
|
|
1472
|
+
await rm(part, { force: true });
|
|
1473
|
+
if (signal.aborted) throw runCanceledError();
|
|
1474
|
+
if (timedOut) {
|
|
1475
|
+
throw new Error(`download de "${ref.name}" parado h\xE1 ${idleTimeoutMs / 1e3}s \u2014 tente de novo`);
|
|
1476
|
+
}
|
|
1477
|
+
throw this.translate(error, ref);
|
|
1478
|
+
} finally {
|
|
1479
|
+
clearTimeout(timer);
|
|
1480
|
+
signal.removeEventListener("abort", onCancel);
|
|
1481
|
+
}
|
|
1482
|
+
try {
|
|
1483
|
+
await api.confirmFileDownload(runId, ref.id);
|
|
1484
|
+
} catch (error) {
|
|
1485
|
+
this.deps.warn(`arquivo "${ref.name}" baixado, mas a confirma\xE7\xE3o ao servidor falhou: ${String(error)}`);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
/** storage 'local': copia o único arquivo de `$HOBOTS_LOCAL_FILES_DIR/<id>/` e apaga a pasta de origem. */
|
|
1489
|
+
async copyLocal(ref, target) {
|
|
1490
|
+
const base = process.env[LOCAL_FILES_DIR_ENV];
|
|
1491
|
+
if (!base) throw unavailableLocal(ref);
|
|
1492
|
+
const { copyFile, readdir, rename, rm, stat } = await import("fs/promises");
|
|
1493
|
+
const { join, resolve: resolve2, sep } = await import("path");
|
|
1494
|
+
const root = resolve2(base);
|
|
1495
|
+
const folder = resolve2(root, ref.id);
|
|
1496
|
+
if (!isInsideDir(root, folder, sep) || !UUID_RE.test(ref.id)) {
|
|
1497
|
+
throw new Error(`id de arquivo local inv\xE1lido: ${JSON.stringify(ref.id)}`);
|
|
1498
|
+
}
|
|
1499
|
+
let names;
|
|
1500
|
+
try {
|
|
1501
|
+
names = (await readdir(folder, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name);
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
if (errorCode(error) === "ENOENT" || errorCode(error) === "ENOTDIR") throw unavailableLocal(ref);
|
|
1504
|
+
throw error;
|
|
1505
|
+
}
|
|
1506
|
+
if (names.length === 0) throw unavailableLocal(ref);
|
|
1507
|
+
if (names.length > 1) throw new Error(`pasta do arquivo "${ref.name}" tem ${names.length} arquivos \u2014 esperado 1`);
|
|
1508
|
+
this.checkCanceled();
|
|
1509
|
+
const part = `${target}.part`;
|
|
1510
|
+
try {
|
|
1511
|
+
await copyFile(join(folder, names[0]), part);
|
|
1512
|
+
const { size } = await stat(part);
|
|
1513
|
+
if (size !== ref.size) {
|
|
1514
|
+
throw new Error(`tamanho divergente na c\xF3pia de "${ref.name}": copiados ${size} bytes, esperados ${ref.size}`);
|
|
1515
|
+
}
|
|
1516
|
+
await rename(part, target);
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
await rm(part, { force: true });
|
|
1519
|
+
throw error;
|
|
1520
|
+
}
|
|
1521
|
+
await rm(folder, { recursive: true, force: true });
|
|
1522
|
+
}
|
|
1523
|
+
translate(error, ref) {
|
|
1524
|
+
if (this.deps.signal.aborted) return runCanceledError();
|
|
1525
|
+
if (error instanceof TasksApiError && error.status === 410) {
|
|
1526
|
+
return new TasksApiError(`arquivo "${ref.name}" j\xE1 baixado ou expirado`, 410);
|
|
1527
|
+
}
|
|
1528
|
+
if (error instanceof TasksApiError && error.status === 404) {
|
|
1529
|
+
return new TasksApiError(`arquivo "${ref.name}" n\xE3o encontrado nesta execu\xE7\xE3o`, 404);
|
|
1530
|
+
}
|
|
1531
|
+
return error;
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
|
|
1211
1535
|
// src/retry.ts
|
|
1212
1536
|
var DEFAULT_RETRIES = 3;
|
|
1213
1537
|
var DEFAULT_DELAY_MS = 1e3;
|
|
@@ -1446,12 +1770,14 @@ var RunReporter = class {
|
|
|
1446
1770
|
);
|
|
1447
1771
|
const signal = this.abortController.signal;
|
|
1448
1772
|
const sleep2 = (ms) => cancelableSleep(ms, signal);
|
|
1773
|
+
this.fileStore = new RunFileStore({ api, runId, params, signal, warn: (message) => this.log("warning", message) });
|
|
1449
1774
|
this.context = {
|
|
1450
1775
|
runId,
|
|
1451
1776
|
params,
|
|
1452
1777
|
log,
|
|
1453
1778
|
items,
|
|
1454
1779
|
progress,
|
|
1780
|
+
files: this.fileStore.files,
|
|
1455
1781
|
transaction: tx,
|
|
1456
1782
|
flush: (timeoutMs) => this.flush(timeoutMs),
|
|
1457
1783
|
signal,
|
|
@@ -1506,6 +1832,12 @@ var RunReporter = class {
|
|
|
1506
1832
|
progressDirty = false;
|
|
1507
1833
|
progressTimer;
|
|
1508
1834
|
progressWarned = false;
|
|
1835
|
+
/** Arquivos do formulário desta run (`ctx.files`) — a pasta temporária é apagada no fim. */
|
|
1836
|
+
fileStore;
|
|
1837
|
+
/** Apaga as cópias de arquivo que ficaram na pasta temporária da run. Nunca lança. */
|
|
1838
|
+
cleanupFiles() {
|
|
1839
|
+
return this.fileStore.cleanup();
|
|
1840
|
+
}
|
|
1509
1841
|
get isDead() {
|
|
1510
1842
|
return this.dead;
|
|
1511
1843
|
}
|
|
@@ -2368,6 +2700,7 @@ async function executeRun(input) {
|
|
|
2368
2700
|
}
|
|
2369
2701
|
await reporter.finalize();
|
|
2370
2702
|
} finally {
|
|
2703
|
+
await reporter.cleanupFiles();
|
|
2371
2704
|
tracker.remove(reporter);
|
|
2372
2705
|
}
|
|
2373
2706
|
}
|
|
@@ -2823,7 +3156,9 @@ function resolveRunOptions(options, mode, logger) {
|
|
|
2823
3156
|
}
|
|
2824
3157
|
function instanceInfo(rt) {
|
|
2825
3158
|
return {
|
|
3159
|
+
/* v8 ignore start -- defensivo: o boot define o instanceId antes de qualquer poll/criação de run */
|
|
2826
3160
|
instance_id: rt.instanceId ?? "unknown",
|
|
3161
|
+
/* v8 ignore stop */
|
|
2827
3162
|
version: SDK_VERSION,
|
|
2828
3163
|
host: hostname(),
|
|
2829
3164
|
// ambiente da instância: junto do heartbeat, é como o servidor sabe onde o robô roda
|
|
@@ -3000,6 +3335,14 @@ var fallbackItems = {
|
|
|
3000
3335
|
each: (list, handler, options = {}) => eachItem(list, handler, options, fallbackEachSink)
|
|
3001
3336
|
};
|
|
3002
3337
|
var fallbackProgress = { total: noop, advance: noop, set: noop, current: 0, snapshot: null };
|
|
3338
|
+
var outsideRunFiles = () => Promise.reject(new Error("hobots.files s\xF3 baixa arquivos dentro de uma execu\xE7\xE3o (handler do register()/start())"));
|
|
3339
|
+
var fallbackFiles = {
|
|
3340
|
+
list: () => [],
|
|
3341
|
+
get: () => void 0,
|
|
3342
|
+
download: outsideRunFiles,
|
|
3343
|
+
downloadAll: outsideRunFiles,
|
|
3344
|
+
read: outsideRunFiles
|
|
3345
|
+
};
|
|
3003
3346
|
var neverAborts = new AbortController().signal;
|
|
3004
3347
|
var detachedTransaction;
|
|
3005
3348
|
function resolve(member) {
|
|
@@ -3021,6 +3364,7 @@ var hobots = Object.defineProperties({}, {
|
|
|
3021
3364
|
log: getter("log", () => fallbackLog),
|
|
3022
3365
|
items: getter("items", () => fallbackItems),
|
|
3023
3366
|
progress: getter("progress", () => fallbackProgress),
|
|
3367
|
+
files: getter("files", () => fallbackFiles),
|
|
3024
3368
|
transaction: getter("transaction", () => detachedTransaction ??= new Transaction(void 0, "hobots.detached")),
|
|
3025
3369
|
signal: getter("signal", () => neverAborts),
|
|
3026
3370
|
throwIfCanceled: method(() => resolve("throwIfCanceled")?.throwIfCanceled()),
|
|
@@ -3041,7 +3385,8 @@ var hobots = Object.defineProperties({}, {
|
|
|
3041
3385
|
}),
|
|
3042
3386
|
close: method((timeoutMs) => close(timeoutMs)),
|
|
3043
3387
|
captureException: method((error, hint) => captureException(error, hint)),
|
|
3044
|
-
image: method(image)
|
|
3388
|
+
image: method(image),
|
|
3389
|
+
isFileRef: method(isFileRef)
|
|
3045
3390
|
});
|
|
3046
3391
|
function installGlobalHobots() {
|
|
3047
3392
|
if (globalThis.hobots === hobots) return;
|
|
@@ -3053,6 +3398,7 @@ var close2 = hobots.close;
|
|
|
3053
3398
|
var captureException2 = hobots.captureException;
|
|
3054
3399
|
var retry = hobots.retry;
|
|
3055
3400
|
var image2 = hobots.image;
|
|
3401
|
+
var isFileRef2 = hobots.isFileRef;
|
|
3056
3402
|
|
|
3057
3403
|
// src/index.ts
|
|
3058
3404
|
var src_default = hobots;
|
|
@@ -3062,6 +3408,7 @@ export {
|
|
|
3062
3408
|
src_default as default,
|
|
3063
3409
|
hobots,
|
|
3064
3410
|
image2 as image,
|
|
3411
|
+
isFileRef2 as isFileRef,
|
|
3065
3412
|
register2 as register,
|
|
3066
3413
|
retry,
|
|
3067
3414
|
start2 as start
|