hobots 2.2.0 → 2.4.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 +14 -2
- package/dist/index.cjs +216 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -6
- package/dist/index.d.ts +57 -6
- package/dist/index.js +216 -46
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +85 -3
- package/dist/protocol.d.ts +85 -3
- package/docs/api-reference.md +4 -1
- package/docs/concepts.md +5 -1
- package/docs/protocol.md +23 -0
- package/docs/telemetry.md +11 -0
- package/package.json +5 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Breadcrumb, EventUser, EventType, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress, RunAttachmentContentType } from './protocol.cjs';
|
|
2
|
-
export { BreadcrumbLevel, Envelope, EnvelopeItem, ExceptionData, InstanceInfo, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.cjs';
|
|
1
|
+
import { Breadcrumb, EventUser, EventType, MachineSnapshot, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress, RunAttachmentContentType, EnvironmentInput } from './protocol.cjs';
|
|
2
|
+
export { BreadcrumbLevel, Envelope, EnvelopeItem, ExceptionData, InstanceInfo, MachineProcess, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.cjs';
|
|
3
3
|
|
|
4
4
|
/** Aceita tudo que o handler pode ter em mãos: bytes ou um caminho de arquivo (só Node). */
|
|
5
5
|
type AttachInput = Uint8Array | ArrayBuffer | string;
|
|
@@ -47,15 +47,46 @@ interface Logger {
|
|
|
47
47
|
error: (...args: unknown[]) => void;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Coleta CPU/memória/disco da máquina e do processo — só em Node (no browser é no-op).
|
|
52
|
+
*
|
|
53
|
+
* Os módulos `node:*` entram por import dinâmico (o bundle é `platform: neutral`), e
|
|
54
|
+
* `sample()` é síncrono e barato: `os.cpus()` + `statfsSync` + `process.memoryUsage()`.
|
|
55
|
+
* Nunca lança — telemetria de recurso não pode derrubar o bot nem o capture de um erro.
|
|
56
|
+
*/
|
|
57
|
+
declare class MachineSampler {
|
|
58
|
+
private readonly instanceId;
|
|
59
|
+
private readonly logger;
|
|
60
|
+
private os;
|
|
61
|
+
private fs;
|
|
62
|
+
private ready;
|
|
63
|
+
private prevCpu;
|
|
64
|
+
private prevProcessCpu;
|
|
65
|
+
private prevAt;
|
|
66
|
+
private lastCpuPercent;
|
|
67
|
+
private lastProcessCpuPercent;
|
|
68
|
+
constructor(instanceId: string, logger: Logger);
|
|
69
|
+
/** Carrega os módulos e tira a leitura-base de CPU (a primeira amostra ainda sai com cpu_percent null). */
|
|
70
|
+
start(): Promise<void>;
|
|
71
|
+
sample(): MachineSnapshot | undefined;
|
|
72
|
+
private cpuTotals;
|
|
73
|
+
/** Média desde a leitura anterior; leituras com menos de 1s de janela reutilizam a última média. */
|
|
74
|
+
private updateCpu;
|
|
75
|
+
private diskUsage;
|
|
76
|
+
}
|
|
77
|
+
|
|
50
78
|
declare class Client {
|
|
51
79
|
#private;
|
|
52
80
|
readonly logger: Logger;
|
|
53
81
|
/** Eco de lifecycle (Transaction.emit) e afins só imprimem no terminal com debug ligado. */
|
|
54
82
|
readonly debug: boolean;
|
|
55
83
|
private readonly transport;
|
|
84
|
+
private machine;
|
|
56
85
|
constructor(options: InitOptions, config?: ResolvedConfig);
|
|
57
86
|
get options(): InitOptions;
|
|
58
87
|
get config(): ResolvedConfig;
|
|
88
|
+
/** Sampler de recursos da máquina — cada evento passa a levar `contexts.machine`. */
|
|
89
|
+
attachMachineSampler(sampler: MachineSampler | undefined): void;
|
|
59
90
|
/** Serialização explícita nunca inclui o secret. */
|
|
60
91
|
toJSON(): Record<string, unknown>;
|
|
61
92
|
captureException(error: unknown, hint: CaptureHint | undefined, scope: Scope): string;
|
|
@@ -64,6 +95,12 @@ declare class Client {
|
|
|
64
95
|
private prepareEvent;
|
|
65
96
|
sendTransaction(payload: TransactionPayload): void;
|
|
66
97
|
sendHeartbeat(payload: HeartbeatPayload): void;
|
|
98
|
+
/**
|
|
99
|
+
* Ambiente do item: o dele, senão o do init(), senão produção. Centralizado
|
|
100
|
+
* porque evento, transaction e heartbeat precisam responder igual — o servidor
|
|
101
|
+
* normaliza apelidos legados, então aqui basta repassar.
|
|
102
|
+
*/
|
|
103
|
+
private resolveEnvironment;
|
|
67
104
|
flush(timeoutMs?: number): Promise<boolean>;
|
|
68
105
|
close(timeoutMs?: number): Promise<boolean>;
|
|
69
106
|
}
|
|
@@ -131,7 +168,7 @@ declare class Transaction {
|
|
|
131
168
|
|
|
132
169
|
/** Opções de tasks dentro de `init({ tasks: ... })`. Credencial (clientSecret), identidade (instanceId) e debug vêm do init. */
|
|
133
170
|
interface TasksOptions {
|
|
134
|
-
/** Intervalo entre polls sem trabalho. Default:
|
|
171
|
+
/** Intervalo entre polls sem trabalho. Default: 2_000 (mínimo 1_000) */
|
|
135
172
|
pollIntervalMs?: number;
|
|
136
173
|
/** Runs simultâneas (1..5). Default: 1 (sequencial) */
|
|
137
174
|
concurrency?: number;
|
|
@@ -254,8 +291,16 @@ interface HeartbeatOptions {
|
|
|
254
291
|
interface InitOptions {
|
|
255
292
|
/** O Client Secret do agente (hb_…) — a mesma credencial para telemetria e tasks. Copie em Configurações → Agentes. */
|
|
256
293
|
clientSecret: string;
|
|
257
|
-
/**
|
|
258
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Ambiente lógico onde este robô roda: 'development', 'testing', 'staging'
|
|
296
|
+
* (homologação) ou 'production'. Vai em todo evento e transaction E na
|
|
297
|
+
* identidade da instância — é por ele que as regras de alerta decidem se
|
|
298
|
+
* notificam. Default: 'production'.
|
|
299
|
+
*
|
|
300
|
+
* Um valor livre herdado continua valendo — o servidor normaliza apelidos
|
|
301
|
+
* como 'prod' e 'qa'.
|
|
302
|
+
*/
|
|
303
|
+
environment?: EnvironmentInput;
|
|
259
304
|
/** Versão do programa, ex.: 'robo-nfe@1.4.2' */
|
|
260
305
|
release?: string;
|
|
261
306
|
/** Identificador único e estável desta instância — a identidade do agente no painel (heartbeat e poll de tasks convergem nele) */
|
|
@@ -270,6 +315,12 @@ interface InitOptions {
|
|
|
270
315
|
maxBreadcrumbs?: number;
|
|
271
316
|
/** Registra handlers globais de erro (uncaughtException etc.). Default: true */
|
|
272
317
|
captureUnhandled?: boolean;
|
|
318
|
+
/**
|
|
319
|
+
* Coleta CPU, memória, disco e recursos do processo: vai em cada heartbeat/poll (série da
|
|
320
|
+
* instância no app) e como `contexts.machine` em cada evento (como estava a máquina na hora
|
|
321
|
+
* do erro). Só em Node; sem custo de timer — pega carona no heartbeat. Default: true
|
|
322
|
+
*/
|
|
323
|
+
machineMetrics?: boolean;
|
|
273
324
|
/** Habilita heartbeat periódico (true usa defaults) */
|
|
274
325
|
heartbeat?: boolean | HeartbeatOptions;
|
|
275
326
|
/**
|
|
@@ -356,4 +407,4 @@ declare function close(timeoutMs?: number): Promise<boolean>;
|
|
|
356
407
|
|
|
357
408
|
declare function scrubSecrets(text: string): string;
|
|
358
409
|
|
|
359
|
-
export { type AttachInput, Breadcrumb, type BreadcrumbInput, type CaptureAttachment, type CaptureHint, Client, DEFAULT_BASE_URL, EventPayload, EventType, EventUser, type HeartbeatOptions, HeartbeatPayload, type InitOptions, RunAttachmentContentType, type RunContext, type RunItemOptions, RunItemStatus, type RunItems, RunLogLevel, RunProgress, type RunProgressReporter, Scope, Step, StepData, type TaskHandler, type TasksOptions, Transaction, TransactionPayload, addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, register, scrubSecrets, setContext, setExtra, setTag, setTags, setUser, start, startTransaction, stop, withScope };
|
|
410
|
+
export { type AttachInput, Breadcrumb, type BreadcrumbInput, type CaptureAttachment, type CaptureHint, Client, DEFAULT_BASE_URL, EventPayload, EventType, EventUser, type HeartbeatOptions, HeartbeatPayload, type InitOptions, MachineSnapshot, RunAttachmentContentType, type RunContext, type RunItemOptions, RunItemStatus, type RunItems, RunLogLevel, RunProgress, type RunProgressReporter, Scope, Step, StepData, type TaskHandler, type TasksOptions, Transaction, TransactionPayload, addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, register, scrubSecrets, setContext, setExtra, setTag, setTags, setUser, start, startTransaction, stop, withScope };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Breadcrumb, EventUser, EventType, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress, RunAttachmentContentType } from './protocol.js';
|
|
2
|
-
export { BreadcrumbLevel, Envelope, EnvelopeItem, ExceptionData, InstanceInfo, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.js';
|
|
1
|
+
import { Breadcrumb, EventUser, EventType, MachineSnapshot, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress, RunAttachmentContentType, EnvironmentInput } from './protocol.js';
|
|
2
|
+
export { BreadcrumbLevel, Envelope, EnvelopeItem, ExceptionData, InstanceInfo, MachineProcess, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.js';
|
|
3
3
|
|
|
4
4
|
/** Aceita tudo que o handler pode ter em mãos: bytes ou um caminho de arquivo (só Node). */
|
|
5
5
|
type AttachInput = Uint8Array | ArrayBuffer | string;
|
|
@@ -47,15 +47,46 @@ interface Logger {
|
|
|
47
47
|
error: (...args: unknown[]) => void;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Coleta CPU/memória/disco da máquina e do processo — só em Node (no browser é no-op).
|
|
52
|
+
*
|
|
53
|
+
* Os módulos `node:*` entram por import dinâmico (o bundle é `platform: neutral`), e
|
|
54
|
+
* `sample()` é síncrono e barato: `os.cpus()` + `statfsSync` + `process.memoryUsage()`.
|
|
55
|
+
* Nunca lança — telemetria de recurso não pode derrubar o bot nem o capture de um erro.
|
|
56
|
+
*/
|
|
57
|
+
declare class MachineSampler {
|
|
58
|
+
private readonly instanceId;
|
|
59
|
+
private readonly logger;
|
|
60
|
+
private os;
|
|
61
|
+
private fs;
|
|
62
|
+
private ready;
|
|
63
|
+
private prevCpu;
|
|
64
|
+
private prevProcessCpu;
|
|
65
|
+
private prevAt;
|
|
66
|
+
private lastCpuPercent;
|
|
67
|
+
private lastProcessCpuPercent;
|
|
68
|
+
constructor(instanceId: string, logger: Logger);
|
|
69
|
+
/** Carrega os módulos e tira a leitura-base de CPU (a primeira amostra ainda sai com cpu_percent null). */
|
|
70
|
+
start(): Promise<void>;
|
|
71
|
+
sample(): MachineSnapshot | undefined;
|
|
72
|
+
private cpuTotals;
|
|
73
|
+
/** Média desde a leitura anterior; leituras com menos de 1s de janela reutilizam a última média. */
|
|
74
|
+
private updateCpu;
|
|
75
|
+
private diskUsage;
|
|
76
|
+
}
|
|
77
|
+
|
|
50
78
|
declare class Client {
|
|
51
79
|
#private;
|
|
52
80
|
readonly logger: Logger;
|
|
53
81
|
/** Eco de lifecycle (Transaction.emit) e afins só imprimem no terminal com debug ligado. */
|
|
54
82
|
readonly debug: boolean;
|
|
55
83
|
private readonly transport;
|
|
84
|
+
private machine;
|
|
56
85
|
constructor(options: InitOptions, config?: ResolvedConfig);
|
|
57
86
|
get options(): InitOptions;
|
|
58
87
|
get config(): ResolvedConfig;
|
|
88
|
+
/** Sampler de recursos da máquina — cada evento passa a levar `contexts.machine`. */
|
|
89
|
+
attachMachineSampler(sampler: MachineSampler | undefined): void;
|
|
59
90
|
/** Serialização explícita nunca inclui o secret. */
|
|
60
91
|
toJSON(): Record<string, unknown>;
|
|
61
92
|
captureException(error: unknown, hint: CaptureHint | undefined, scope: Scope): string;
|
|
@@ -64,6 +95,12 @@ declare class Client {
|
|
|
64
95
|
private prepareEvent;
|
|
65
96
|
sendTransaction(payload: TransactionPayload): void;
|
|
66
97
|
sendHeartbeat(payload: HeartbeatPayload): void;
|
|
98
|
+
/**
|
|
99
|
+
* Ambiente do item: o dele, senão o do init(), senão produção. Centralizado
|
|
100
|
+
* porque evento, transaction e heartbeat precisam responder igual — o servidor
|
|
101
|
+
* normaliza apelidos legados, então aqui basta repassar.
|
|
102
|
+
*/
|
|
103
|
+
private resolveEnvironment;
|
|
67
104
|
flush(timeoutMs?: number): Promise<boolean>;
|
|
68
105
|
close(timeoutMs?: number): Promise<boolean>;
|
|
69
106
|
}
|
|
@@ -131,7 +168,7 @@ declare class Transaction {
|
|
|
131
168
|
|
|
132
169
|
/** Opções de tasks dentro de `init({ tasks: ... })`. Credencial (clientSecret), identidade (instanceId) e debug vêm do init. */
|
|
133
170
|
interface TasksOptions {
|
|
134
|
-
/** Intervalo entre polls sem trabalho. Default:
|
|
171
|
+
/** Intervalo entre polls sem trabalho. Default: 2_000 (mínimo 1_000) */
|
|
135
172
|
pollIntervalMs?: number;
|
|
136
173
|
/** Runs simultâneas (1..5). Default: 1 (sequencial) */
|
|
137
174
|
concurrency?: number;
|
|
@@ -254,8 +291,16 @@ interface HeartbeatOptions {
|
|
|
254
291
|
interface InitOptions {
|
|
255
292
|
/** O Client Secret do agente (hb_…) — a mesma credencial para telemetria e tasks. Copie em Configurações → Agentes. */
|
|
256
293
|
clientSecret: string;
|
|
257
|
-
/**
|
|
258
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Ambiente lógico onde este robô roda: 'development', 'testing', 'staging'
|
|
296
|
+
* (homologação) ou 'production'. Vai em todo evento e transaction E na
|
|
297
|
+
* identidade da instância — é por ele que as regras de alerta decidem se
|
|
298
|
+
* notificam. Default: 'production'.
|
|
299
|
+
*
|
|
300
|
+
* Um valor livre herdado continua valendo — o servidor normaliza apelidos
|
|
301
|
+
* como 'prod' e 'qa'.
|
|
302
|
+
*/
|
|
303
|
+
environment?: EnvironmentInput;
|
|
259
304
|
/** Versão do programa, ex.: 'robo-nfe@1.4.2' */
|
|
260
305
|
release?: string;
|
|
261
306
|
/** Identificador único e estável desta instância — a identidade do agente no painel (heartbeat e poll de tasks convergem nele) */
|
|
@@ -270,6 +315,12 @@ interface InitOptions {
|
|
|
270
315
|
maxBreadcrumbs?: number;
|
|
271
316
|
/** Registra handlers globais de erro (uncaughtException etc.). Default: true */
|
|
272
317
|
captureUnhandled?: boolean;
|
|
318
|
+
/**
|
|
319
|
+
* Coleta CPU, memória, disco e recursos do processo: vai em cada heartbeat/poll (série da
|
|
320
|
+
* instância no app) e como `contexts.machine` em cada evento (como estava a máquina na hora
|
|
321
|
+
* do erro). Só em Node; sem custo de timer — pega carona no heartbeat. Default: true
|
|
322
|
+
*/
|
|
323
|
+
machineMetrics?: boolean;
|
|
273
324
|
/** Habilita heartbeat periódico (true usa defaults) */
|
|
274
325
|
heartbeat?: boolean | HeartbeatOptions;
|
|
275
326
|
/**
|
|
@@ -356,4 +407,4 @@ declare function close(timeoutMs?: number): Promise<boolean>;
|
|
|
356
407
|
|
|
357
408
|
declare function scrubSecrets(text: string): string;
|
|
358
409
|
|
|
359
|
-
export { type AttachInput, Breadcrumb, type BreadcrumbInput, type CaptureAttachment, type CaptureHint, Client, DEFAULT_BASE_URL, EventPayload, EventType, EventUser, type HeartbeatOptions, HeartbeatPayload, type InitOptions, RunAttachmentContentType, type RunContext, type RunItemOptions, RunItemStatus, type RunItems, RunLogLevel, RunProgress, type RunProgressReporter, Scope, Step, StepData, type TaskHandler, type TasksOptions, Transaction, TransactionPayload, addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, register, scrubSecrets, setContext, setExtra, setTag, setTags, setUser, start, startTransaction, stop, withScope };
|
|
410
|
+
export { type AttachInput, Breadcrumb, type BreadcrumbInput, type CaptureAttachment, type CaptureHint, Client, DEFAULT_BASE_URL, EventPayload, EventType, EventUser, type HeartbeatOptions, HeartbeatPayload, type InitOptions, MachineSnapshot, RunAttachmentContentType, type RunContext, type RunItemOptions, RunItemStatus, type RunItems, RunLogLevel, RunProgress, type RunProgressReporter, Scope, Step, StepData, type TaskHandler, type TasksOptions, Transaction, TransactionPayload, addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, register, scrubSecrets, setContext, setExtra, setTag, setTags, setUser, start, startTransaction, stop, withScope };
|
package/dist/index.js
CHANGED
|
@@ -103,7 +103,7 @@ function scrubSecrets(text) {
|
|
|
103
103
|
|
|
104
104
|
// src/utils.ts
|
|
105
105
|
var SDK_NAME = "hobots";
|
|
106
|
-
var SDK_VERSION = true ? "2.
|
|
106
|
+
var SDK_VERSION = true ? "2.4.0" : "0.0.0-dev";
|
|
107
107
|
function isNode() {
|
|
108
108
|
return typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
109
109
|
}
|
|
@@ -396,6 +396,7 @@ var Client = class {
|
|
|
396
396
|
#options;
|
|
397
397
|
#config;
|
|
398
398
|
transport;
|
|
399
|
+
machine;
|
|
399
400
|
constructor(options, config = resolveConfig(options)) {
|
|
400
401
|
this.#options = options;
|
|
401
402
|
this.debug = !!options.debug;
|
|
@@ -415,6 +416,10 @@ var Client = class {
|
|
|
415
416
|
get config() {
|
|
416
417
|
return this.#config;
|
|
417
418
|
}
|
|
419
|
+
/** Sampler de recursos da máquina — cada evento passa a levar `contexts.machine`. */
|
|
420
|
+
attachMachineSampler(sampler) {
|
|
421
|
+
this.machine = sampler;
|
|
422
|
+
}
|
|
418
423
|
/** Serialização explícita nunca inclui o secret. */
|
|
419
424
|
toJSON() {
|
|
420
425
|
return { sdk: SDK_NAME, version: SDK_VERSION, baseUrl: this.#config.baseUrl };
|
|
@@ -446,15 +451,17 @@ var Client = class {
|
|
|
446
451
|
prepareEvent(event, hint, scope2) {
|
|
447
452
|
const tags = { ...this.options.tags, ...scope2.tags, ...event.tags, ...hint?.tags };
|
|
448
453
|
const extra = { ...scope2.extra, ...event.extra, ...hint?.extra };
|
|
454
|
+
const sampled = this.machine?.sample();
|
|
455
|
+
const machine = sampled ? { machine: sampled } : {};
|
|
449
456
|
return {
|
|
450
457
|
...event,
|
|
451
458
|
type: hint?.type ?? event.type ?? scope2.type ?? "error",
|
|
452
459
|
release: event.release ?? this.options.release,
|
|
453
|
-
environment:
|
|
460
|
+
environment: this.resolveEnvironment(event.environment),
|
|
454
461
|
tags: Object.keys(tags).length > 0 ? tags : void 0,
|
|
455
462
|
extra: Object.keys(extra).length > 0 ? extra : void 0,
|
|
456
463
|
user: event.user ?? scope2.user,
|
|
457
|
-
contexts: { ...runtimeContexts(), ...scope2.contexts, ...event.contexts },
|
|
464
|
+
contexts: { ...runtimeContexts(), ...machine, ...scope2.contexts, ...event.contexts },
|
|
458
465
|
breadcrumbs: scope2.breadcrumbs.length > 0 ? [...scope2.breadcrumbs] : event.breadcrumbs,
|
|
459
466
|
fingerprint: hint?.fingerprint ?? event.fingerprint ?? scope2.fingerprint
|
|
460
467
|
};
|
|
@@ -465,7 +472,7 @@ var Client = class {
|
|
|
465
472
|
payload: {
|
|
466
473
|
...payload,
|
|
467
474
|
release: payload.release ?? this.options.release,
|
|
468
|
-
environment:
|
|
475
|
+
environment: this.resolveEnvironment(payload.environment),
|
|
469
476
|
tags: { ...this.options.tags, ...payload.tags }
|
|
470
477
|
}
|
|
471
478
|
});
|
|
@@ -473,9 +480,22 @@ var Client = class {
|
|
|
473
480
|
sendHeartbeat(payload) {
|
|
474
481
|
this.transport.enqueue({
|
|
475
482
|
type: "heartbeat",
|
|
476
|
-
payload: {
|
|
483
|
+
payload: {
|
|
484
|
+
...payload,
|
|
485
|
+
release: payload.release ?? this.options.release,
|
|
486
|
+
// o ambiente viaja no beat: é assim que a instância se classifica no app
|
|
487
|
+
environment: this.resolveEnvironment(payload.environment)
|
|
488
|
+
}
|
|
477
489
|
});
|
|
478
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* Ambiente do item: o dele, senão o do init(), senão produção. Centralizado
|
|
493
|
+
* porque evento, transaction e heartbeat precisam responder igual — o servidor
|
|
494
|
+
* normaliza apelidos legados, então aqui basta repassar.
|
|
495
|
+
*/
|
|
496
|
+
resolveEnvironment(value) {
|
|
497
|
+
return value ?? this.options.environment ?? "production";
|
|
498
|
+
}
|
|
479
499
|
flush(timeoutMs) {
|
|
480
500
|
return this.transport.flush(timeoutMs);
|
|
481
501
|
}
|
|
@@ -489,8 +509,9 @@ var DEFAULT_INTERVAL_MS = 3e4;
|
|
|
489
509
|
var MIN_REPORTED_INTERVAL_MS = 1e3;
|
|
490
510
|
var MAX_REPORTED_INTERVAL_MS = 9e5;
|
|
491
511
|
var Heartbeat = class {
|
|
492
|
-
constructor(client2, options = {}, instanceId) {
|
|
512
|
+
constructor(client2, options = {}, instanceId, sampler) {
|
|
493
513
|
this.client = client2;
|
|
514
|
+
this.sampler = sampler;
|
|
494
515
|
this.intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
495
516
|
this.instanceId = instanceId;
|
|
496
517
|
this.name = options.name ?? instanceId;
|
|
@@ -529,7 +550,9 @@ var Heartbeat = class {
|
|
|
529
550
|
name: this.name,
|
|
530
551
|
interval_ms: Math.min(Math.max(this.intervalMs, MIN_REPORTED_INTERVAL_MS), MAX_REPORTED_INTERVAL_MS),
|
|
531
552
|
timestamp: nowISO(),
|
|
532
|
-
metadata: this.metadata
|
|
553
|
+
metadata: this.metadata,
|
|
554
|
+
// recursos da máquina a cada beat — a série da instância no app
|
|
555
|
+
machine: this.sampler?.sample()
|
|
533
556
|
});
|
|
534
557
|
}
|
|
535
558
|
};
|
|
@@ -560,50 +583,184 @@ function hasInstalledGlobalHandlers() {
|
|
|
560
583
|
return installed;
|
|
561
584
|
}
|
|
562
585
|
function installGlobalHandlers(client2, getScope) {
|
|
563
|
-
if (installed) return;
|
|
586
|
+
if (installed) return () => void 0;
|
|
564
587
|
installed = true;
|
|
565
588
|
if (isNode()) {
|
|
566
|
-
|
|
567
|
-
"uncaughtException",
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
})
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
);
|
|
586
|
-
|
|
587
|
-
"
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return;
|
|
589
|
+
const onUncaught = markInternal((error) => {
|
|
590
|
+
client2.captureException(error, { type: "fatal", tags: { mechanism: "uncaughtException" } }, getScope());
|
|
591
|
+
void client2.flush(2e3).then(async () => {
|
|
592
|
+
if (externalListenerCount("uncaughtException") === 0) {
|
|
593
|
+
await awaitEmergencyTasks(EMERGENCY_REPORT_CAP_MS);
|
|
594
|
+
console.error(error);
|
|
595
|
+
process.exit(1);
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
const onRejection = markInternal((reason) => {
|
|
600
|
+
client2.captureException(reason, { type: "error", tags: { mechanism: "unhandledRejection" } }, getScope());
|
|
601
|
+
void client2.flush(2e3);
|
|
602
|
+
});
|
|
603
|
+
const onBeforeExit = markInternal(() => {
|
|
604
|
+
void client2.flush(2e3);
|
|
605
|
+
});
|
|
606
|
+
process.on("uncaughtException", onUncaught);
|
|
607
|
+
process.on("unhandledRejection", onRejection);
|
|
608
|
+
process.on("beforeExit", onBeforeExit);
|
|
609
|
+
return () => {
|
|
610
|
+
process.off("uncaughtException", onUncaught);
|
|
611
|
+
process.off("unhandledRejection", onRejection);
|
|
612
|
+
process.off("beforeExit", onBeforeExit);
|
|
613
|
+
installed = false;
|
|
614
|
+
};
|
|
593
615
|
}
|
|
594
616
|
if (typeof window !== "undefined") {
|
|
595
|
-
|
|
617
|
+
const onError = (event) => {
|
|
596
618
|
client2.captureException(event.error ?? event.message, { tags: { mechanism: "onerror" } }, getScope());
|
|
597
|
-
}
|
|
598
|
-
|
|
619
|
+
};
|
|
620
|
+
const onRejection = (event) => {
|
|
599
621
|
client2.captureException(event.reason, { tags: { mechanism: "onunhandledrejection" } }, getScope());
|
|
600
|
-
}
|
|
601
|
-
|
|
622
|
+
};
|
|
623
|
+
const onPageHide = () => {
|
|
602
624
|
void client2.flush(1e3);
|
|
603
|
-
}
|
|
625
|
+
};
|
|
626
|
+
window.addEventListener("error", onError);
|
|
627
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
628
|
+
window.addEventListener("pagehide", onPageHide);
|
|
629
|
+
return () => {
|
|
630
|
+
window.removeEventListener("error", onError);
|
|
631
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
632
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
633
|
+
installed = false;
|
|
634
|
+
};
|
|
604
635
|
}
|
|
636
|
+
return () => {
|
|
637
|
+
installed = false;
|
|
638
|
+
};
|
|
605
639
|
}
|
|
606
640
|
|
|
641
|
+
// src/machine.ts
|
|
642
|
+
var MIN_CPU_WINDOW_MS = 1e3;
|
|
643
|
+
var round1 = (value) => Math.round(value * 10) / 10;
|
|
644
|
+
var clampPercent = (value) => Math.min(100, Math.max(0, value));
|
|
645
|
+
var MachineSampler = class {
|
|
646
|
+
constructor(instanceId, logger) {
|
|
647
|
+
this.instanceId = instanceId;
|
|
648
|
+
this.logger = logger;
|
|
649
|
+
}
|
|
650
|
+
os;
|
|
651
|
+
fs;
|
|
652
|
+
ready = false;
|
|
653
|
+
prevCpu;
|
|
654
|
+
prevProcessCpu;
|
|
655
|
+
prevAt = 0;
|
|
656
|
+
lastCpuPercent = null;
|
|
657
|
+
lastProcessCpuPercent = null;
|
|
658
|
+
/** Carrega os módulos e tira a leitura-base de CPU (a primeira amostra ainda sai com cpu_percent null). */
|
|
659
|
+
async start() {
|
|
660
|
+
if (!isNode()) return;
|
|
661
|
+
try {
|
|
662
|
+
this.os = await import("os");
|
|
663
|
+
this.fs = await import("fs");
|
|
664
|
+
this.prevCpu = this.cpuTotals();
|
|
665
|
+
this.prevProcessCpu = process.cpuUsage();
|
|
666
|
+
this.prevAt = Date.now();
|
|
667
|
+
this.ready = true;
|
|
668
|
+
} catch (error) {
|
|
669
|
+
this.logger.debug("m\xE9tricas de m\xE1quina indispon\xEDveis neste runtime", error);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
sample() {
|
|
673
|
+
if (!this.ready || !this.os) return void 0;
|
|
674
|
+
try {
|
|
675
|
+
const os = this.os;
|
|
676
|
+
const cpuCount = Math.max(1, os.cpus().length);
|
|
677
|
+
this.updateCpu(cpuCount);
|
|
678
|
+
const snapshot = {
|
|
679
|
+
sampled_at: nowISO(),
|
|
680
|
+
instance_id: this.instanceId,
|
|
681
|
+
hostname: os.hostname(),
|
|
682
|
+
os: process.platform,
|
|
683
|
+
arch: process.arch,
|
|
684
|
+
cpu_count: cpuCount,
|
|
685
|
+
cpu_percent: this.lastCpuPercent,
|
|
686
|
+
process: {
|
|
687
|
+
pid: process.pid,
|
|
688
|
+
uptime_s: Math.round(process.uptime()),
|
|
689
|
+
cpu_percent: this.lastProcessCpuPercent
|
|
690
|
+
},
|
|
691
|
+
source: "stdlib"
|
|
692
|
+
};
|
|
693
|
+
if (process.platform !== "win32") {
|
|
694
|
+
const load = os.loadavg()[0];
|
|
695
|
+
if (typeof load === "number" && Number.isFinite(load)) snapshot.load_1 = round1(load);
|
|
696
|
+
}
|
|
697
|
+
const memTotal = os.totalmem();
|
|
698
|
+
const memFree = os.freemem();
|
|
699
|
+
if (memTotal > 0) {
|
|
700
|
+
snapshot.mem_total_bytes = memTotal;
|
|
701
|
+
snapshot.mem_available_bytes = memFree;
|
|
702
|
+
snapshot.mem_percent = round1(clampPercent((memTotal - memFree) / memTotal * 100));
|
|
703
|
+
}
|
|
704
|
+
const disk = this.diskUsage();
|
|
705
|
+
if (disk) Object.assign(snapshot, disk);
|
|
706
|
+
const mem = process.memoryUsage();
|
|
707
|
+
snapshot.process.rss_bytes = mem.rss;
|
|
708
|
+
snapshot.process.heap_used_bytes = mem.heapUsed;
|
|
709
|
+
return snapshot;
|
|
710
|
+
} catch (error) {
|
|
711
|
+
this.logger.debug("falha ao coletar m\xE9tricas de m\xE1quina", error);
|
|
712
|
+
return void 0;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
cpuTotals() {
|
|
716
|
+
let busy = 0;
|
|
717
|
+
let total = 0;
|
|
718
|
+
for (const cpu of this.os.cpus()) {
|
|
719
|
+
const t = cpu.times;
|
|
720
|
+
const cpuBusy = t.user + t.nice + t.sys + t.irq;
|
|
721
|
+
busy += cpuBusy;
|
|
722
|
+
total += cpuBusy + t.idle;
|
|
723
|
+
}
|
|
724
|
+
return { busy, total };
|
|
725
|
+
}
|
|
726
|
+
/** Média desde a leitura anterior; leituras com menos de 1s de janela reutilizam a última média. */
|
|
727
|
+
updateCpu(cpuCount) {
|
|
728
|
+
const now = Date.now();
|
|
729
|
+
const elapsedMs = now - this.prevAt;
|
|
730
|
+
if (elapsedMs < MIN_CPU_WINDOW_MS) return;
|
|
731
|
+
const cpu = this.cpuTotals();
|
|
732
|
+
if (this.prevCpu) {
|
|
733
|
+
const total = cpu.total - this.prevCpu.total;
|
|
734
|
+
if (total > 0) this.lastCpuPercent = round1(clampPercent((cpu.busy - this.prevCpu.busy) / total * 100));
|
|
735
|
+
}
|
|
736
|
+
this.prevCpu = cpu;
|
|
737
|
+
const usage = process.cpuUsage(this.prevProcessCpu);
|
|
738
|
+
const usedMicros = usage.user + usage.system;
|
|
739
|
+
this.lastProcessCpuPercent = round1(clampPercent(usedMicros / (elapsedMs * 1e3 * cpuCount) * 100));
|
|
740
|
+
this.prevProcessCpu = process.cpuUsage();
|
|
741
|
+
this.prevAt = now;
|
|
742
|
+
}
|
|
743
|
+
diskUsage() {
|
|
744
|
+
const statfs = this.fs?.statfsSync;
|
|
745
|
+
if (typeof statfs !== "function") return void 0;
|
|
746
|
+
try {
|
|
747
|
+
const path = process.cwd();
|
|
748
|
+
const stat = statfs(path);
|
|
749
|
+
const total = stat.bsize * stat.blocks;
|
|
750
|
+
const free = stat.bsize * stat.bavail;
|
|
751
|
+
if (total <= 0) return void 0;
|
|
752
|
+
return {
|
|
753
|
+
disk_path: path,
|
|
754
|
+
disk_total_bytes: total,
|
|
755
|
+
disk_free_bytes: free,
|
|
756
|
+
disk_percent: round1(clampPercent((total - free) / total * 100))
|
|
757
|
+
};
|
|
758
|
+
} catch {
|
|
759
|
+
return void 0;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
|
|
607
764
|
// src/scope.ts
|
|
608
765
|
var Scope = class _Scope {
|
|
609
766
|
tags = {};
|
|
@@ -1787,7 +1944,7 @@ function installShutdownHandlers(agent2) {
|
|
|
1787
1944
|
}
|
|
1788
1945
|
|
|
1789
1946
|
// src/tasks/agent.ts
|
|
1790
|
-
var DEFAULT_POLL_INTERVAL_MS =
|
|
1947
|
+
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
1791
1948
|
var MIN_POLL_INTERVAL_MS = 1e3;
|
|
1792
1949
|
var MAX_CONCURRENCY = 5;
|
|
1793
1950
|
var POLL_RETRY_DELAYS_MS = [1e3, 4e3, 16e3];
|
|
@@ -1918,11 +2075,17 @@ var TaskAgent = class {
|
|
|
1918
2075
|
instance_id: this.instanceId,
|
|
1919
2076
|
version: SDK_VERSION,
|
|
1920
2077
|
host: hostname(),
|
|
2078
|
+
// ambiente da instância: em agentes poll-only (sem heartbeat) este é o
|
|
2079
|
+
// único canal por onde o servidor descobre onde o robô está rodando
|
|
2080
|
+
environment: this.options.environment,
|
|
1921
2081
|
tasks: [...this.registry.keys()],
|
|
1922
2082
|
// o servidor calcula a tolerância de offline (3×) a partir disto em
|
|
1923
2083
|
// agentes poll-only — sem o campo, um poll lento flaparia offline/online.
|
|
1924
2084
|
// Clampado à faixa aceita pelo schema do poll (1s..15min).
|
|
1925
|
-
poll_interval_ms: Math.min(Math.max(this.pollIntervalMs, 1e3), 9e5)
|
|
2085
|
+
poll_interval_ms: Math.min(Math.max(this.pollIntervalMs, 1e3), 9e5),
|
|
2086
|
+
// recursos da máquina — alimenta a série da instância em agentes sem heartbeat
|
|
2087
|
+
// (o servidor grava no máx. 1 ponto/30s por instância)
|
|
2088
|
+
machine: this.options.machineSampler?.sample()
|
|
1926
2089
|
},
|
|
1927
2090
|
max: Math.min(MAX_CONCURRENCY, this.concurrency - this.activeRuns.size),
|
|
1928
2091
|
tasks: [...this.registry.keys()]
|
|
@@ -2070,7 +2233,9 @@ var DEFAULT_MAX_BREADCRUMBS = 100;
|
|
|
2070
2233
|
var client;
|
|
2071
2234
|
var scope = new Scope();
|
|
2072
2235
|
var heartbeat;
|
|
2236
|
+
var machineSampler;
|
|
2073
2237
|
var agent;
|
|
2238
|
+
var uninstallGlobalHandlers;
|
|
2074
2239
|
var registry = /* @__PURE__ */ new Map();
|
|
2075
2240
|
var warnedTasksDisabled = false;
|
|
2076
2241
|
function init(options) {
|
|
@@ -2083,21 +2248,26 @@ function init(options) {
|
|
|
2083
2248
|
void agent.stop(0);
|
|
2084
2249
|
agent = void 0;
|
|
2085
2250
|
}
|
|
2251
|
+
uninstallGlobalHandlers?.();
|
|
2252
|
+
uninstallGlobalHandlers = void 0;
|
|
2086
2253
|
warnedTasksDisabled = false;
|
|
2087
2254
|
const config = resolveConfig(options);
|
|
2088
2255
|
client = new Client(options, config);
|
|
2256
|
+
machineSampler = options.machineMetrics === false ? void 0 : new MachineSampler(options.instanceId, client.logger);
|
|
2257
|
+
void machineSampler?.start();
|
|
2258
|
+
client.attachMachineSampler(machineSampler);
|
|
2089
2259
|
if (options.captureUnhandled !== false) {
|
|
2090
|
-
installGlobalHandlers(client, () => scope);
|
|
2260
|
+
uninstallGlobalHandlers = installGlobalHandlers(client, () => scope);
|
|
2091
2261
|
}
|
|
2092
2262
|
if (options.heartbeat) {
|
|
2093
2263
|
const heartbeatOptions = options.heartbeat === true ? {} : options.heartbeat;
|
|
2094
|
-
heartbeat = new Heartbeat(client, heartbeatOptions, options.instanceId);
|
|
2264
|
+
heartbeat = new Heartbeat(client, heartbeatOptions, options.instanceId, machineSampler);
|
|
2095
2265
|
heartbeat.start();
|
|
2096
2266
|
}
|
|
2097
2267
|
if (options.tasks) {
|
|
2098
2268
|
const tasksOptions = options.tasks === true ? {} : options.tasks;
|
|
2099
2269
|
agent = new TaskAgent(
|
|
2100
|
-
{ ...tasksOptions, config, instanceId: options.instanceId, debug: options.debug, client },
|
|
2270
|
+
{ ...tasksOptions, config, instanceId: options.instanceId, environment: options.environment, debug: options.debug, client, machineSampler },
|
|
2101
2271
|
registry
|
|
2102
2272
|
);
|
|
2103
2273
|
}
|